EKSにて、ALBを利用したpodのロードバランシングを試します。AWSのドキュメントはこちら。
Amazon EKS の ALB Ingress Controller
ALB Ingress Controllerのドキュメントはこちら。
環境
- EKS(Kubernetes) 1.14.9
- ALB Ingress Controller v1.1.5
- ExternalDNS v0.5.18
- eksctl 0.13.0
- kubectl v1.17.1
VPCの設定
ALBを配置したいSubnetに、下記の通りタギングを行います。
サブネット | kubernetes.io/cluster/<k8s-cluster-name> | kubernetes.io/role/elb | kubernetes.io/role/internal-elb |
---|---|---|---|
VPC内全サブネット | shared | ー | ー |
Internet-facing ALBを配置するサブネット | shared | 1 | ー |
Internal-facing ALBを配置するサブネット | shared | ー | 1 |
こんな感じですね。
このタギングをしておくことで、ALB Ingress ContorollerがALBを配置するサブネットを自動検知してくれます。
SSL/TLS証明書の用意
ALBに配置したいSSL/TLS証明書を、ACMにて作成しておきます。
ALBアクセスログ配置用のS3バケットの用意
ALBのアクセスログを格納するS3バケットを作成し、必要なバケットポリシーを設定します。
OpenID Connect ID プロバイダーの作成
KubernetesとAWS間の認証に利用される、IAM OIDC ID プロバイダーをeksctlにて作成します。
$ eksctl utils associate-iam-oidc-provider --cluster <k8s-cluster-name> --region ap-northeast-1 --approve --profile xxx [ℹ] eksctl version 0.13.0 [ℹ] using region ap-northeast-1 [ℹ] will create IAM Open ID Connect provider for cluster "cluster-sample" in "ap-northeast-1" [✔] created IAM Open ID Connect provider for cluster "cluster-sample" in "ap-northeast-1"
eksctl を使用してクラスターの IAM OIDC ID プロバイダーを作成するには
ALB Ingress Controller向けサービスアカウントの作成
ALB Ingress Controllerで利用するサービスアカウントを作成します。
以前は kube2iam
を利用していた部分が、AWS側のUpdateにより、KubernetesのサービスアカウントにIAM Roleを割り当てられるようなっています。
Kubernetes サービスアカウントに対するきめ細やかな IAM ロール割り当ての紹介
まず該当サービスアカウントで利用されるIAMポリシーを作成します。ポリシーファイルが公開されているので、それを用いて作成します。
$ curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/iam-policy.json $ aws iam create-policy \ --policy-name ALBIngressControllerIAMPolicy \ --policy-document file://iam-policy.json \ --profile xxxxxx { "Policy": { "PolicyName": "ALBIngressControllerIAMPolicy", "PolicyId": "ANPAZDDARDEF23WPDYGNE", "Arn": "arn:aws:iam::XXXXXXXXXXXX:policy/ALBIngressControllerIAMPolicy", "Path": "/", "DefaultVersionId": "v1", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2020-02-01T07:45:05Z", "UpdateDate": "2020-02-01T07:45:05Z" } }
続いてkubernetesのサービスアカウントを作成します。サービスアカウント作成用のeksctl向けyamlファイルを作成します。
sa-alb-ingress-controller.yml
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: cluster-sample region: ap-northeast-1 iam: withOIDC: true serviceAccounts: - metadata: name: alb-ingress-controller namespace: kube-system labels: app.kubernetes.io/name: alb-ingress-controller attachPolicyARNs: - "arn:aws:iam::XXXXXXXXXXXX:policy/ALBIngressControllerIAMPolicy"
eksctlを実行します。
$ eksctl create iamserviceaccount -f sa-alb-ingress-controller.yml --approve --profile xxx [ℹ] eksctl version 0.13.0 [ℹ] using region ap-northeast-1 [ℹ] 1 iamserviceaccount (kube-system/alb-ingress-controller) was included (based on the include/exclude rules) [ℹ] 1 task: { 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/alb-ingress-controller", create serviceaccount "kube-system/alb-ingress-controller" } } [ℹ] building iamserviceaccount stack "eksctl-cluster-sample-addon-iamserviceaccount-kube-system-alb-ingress-controller" [ℹ] deploying stack "eksctl-cluster-sample-addon-iamserviceaccount-kube-system-alb-ingress-controller" [ℹ] created serviceaccount "kube-system/alb-ingress-controller"
これにてサービスアカウントが作成されています。
kubectl get serviceaccount alb-ingress-controller -n kube-system -o yaml 結果
apiVersion: v1 kind: ServiceAccount metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxxxxxxxx:role/eksctl-cluster-sample-addon-iamserviceaccoun-Role1-AYTXHAS9IZA creationTimestamp: "2020-02-01T08:35:02Z" labels: app.kubernetes.io/name: alb-ingress-controller name: alb-ingress-controller namespace: kube-system resourceVersion: "5265" selfLink: /api/v1/namespaces/kube-system/serviceaccounts/alb-ingress-controller uid: bd6667e2-44cd-11ea-8640-0aa850b703aa secrets: - name: alb-ingress-controller-token-xhpvn
同時にCloudFormationが実行されており、 role/eksctl-cluster-sample-addon-iamserviceaccoun-RoleX-XXXXXXXXX
というIAM Roleが作成されています。
最後に、作成したサービスアカウント向けの、CluterRoleとCluterRoleBindingを作成します。そのために、下記のkubernetes用マニフェストファイルを用意します。
clusterrole-alb-ingress-controller.yml
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: alb-ingress-controller labels: app.kubernetes.io/name: alb-ingress-controller rules: - apiGroups: - "" - extensions resources: - configmaps - endpoints - events - ingresses - ingresses/status - services verbs: - create - get - list - update - watch - patch - apiGroups: - "" - extensions resources: - nodes - pods - secrets - services - namespaces verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: alb-ingress-controller labels: app.kubernetes.io/name: alb-ingress-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: alb-ingress-controller subjects: - kind: ServiceAccount name: alb-ingress-controller namespace: kube-system
kubectlを実行します。
$ kubectl apply -f clusterrole-alb-ingress-controller.yml
clusterrole.rbac.authorization.k8s.io/alb-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/alb-ingress-controller created
ALB Ingress Controllerの作成
ALB Ingress Controllerにあたるpodを起動します。サンプルとなるマニフェストファイルが公開されているので、まずそれをダウンロードしてきます。
$ curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/alb-ingre ss-controller.yaml
サンプルのマニフェストファイルから、以下のパラメーターをアンコメントアウト・値の修正をします。
パラメーター | 値 |
---|---|
cluster-name | 利用するk8sクラスター名 |
aws-vpc-id | 利用するVPC ID |
aws-region | 利用するリージョン名 |
修正したマニフェストファイルが下記です。
# Application Load Balancer (ALB) Ingress Controller Deployment Manifest. # This manifest details sensible defaults for deploying an ALB Ingress Controller. # GitHub: https://github.com/kubernetes-sigs/aws-alb-ingress-controller apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/name: alb-ingress-controller name: alb-ingress-controller # Namespace the ALB Ingress Controller should run in. Does not impact which # namespaces it's able to resolve ingress resource for. For limiting ingress # namespace scope, see --watch-namespace. namespace: kube-system spec: selector: matchLabels: app.kubernetes.io/name: alb-ingress-controller template: metadata: labels: app.kubernetes.io/name: alb-ingress-controller spec: containers: - name: alb-ingress-controller args: # Limit the namespace where this ALB Ingress Controller deployment will # resolve ingress resources. If left commented, all namespaces are used. # - --watch-namespace=your-k8s-namespace # Setting the ingress-class flag below ensures that only ingress resources with the # annotation kubernetes.io/ingress.class: "alb" are respected by the controller. You may # choose any class you'd like for this controller to respect. - --ingress-class=alb # REQUIRED # Name of your cluster. Used when naming resources created # by the ALB Ingress Controller, providing distinction between # clusters. - --cluster-name=cluster-sample # AWS VPC ID this ingress controller will use to create AWS resources. # If unspecified, it will be discovered from ec2metadata. - --aws-vpc-id=vpc-xxxxxxx # AWS region this ingress controller will operate in. # If unspecified, it will be discovered from ec2metadata. # List of regions: http://docs.aws.amazon.com/general/latest/gr/rande.html#vpc_region - --aws-region=ap-northeast-1 # Enables logging on all outbound requests sent to the AWS API. # If logging is desired, set to true. # - --aws-api-debug # Maximum number of times to retry the aws calls. # defaults to 10. # - --aws-max-retries=10 # env: # AWS key id for authenticating with the AWS API. # This is only here for examples. It's recommended you instead use # a project like kube2iam for granting access. #- name: AWS_ACCESS_KEY_ID # value: KEYVALUE # AWS key secret for authenticating with the AWS API. # This is only here for examples. It's recommended you instead use # a project like kube2iam for granting access. #- name: AWS_SECRET_ACCESS_KEY # value: SECRETVALUE # Repository location of the ALB Ingress Controller. image: docker.io/amazon/aws-alb-ingress-controller:v1.1.4 serviceAccountName: alb-ingress-controller
kubectlを実行します。
$ kubectl apply -f alb-ingress-controller.yaml
deployment.apps/alb-ingress-controller created
ちゃんと動いていることをログから確認します。
$ kubectl get pod -n kube-system -l app.kubernetes.io/name=alb-ingress-controller NAME READY STATUS RESTARTS AGE alb-ingress-controller-75cfcdd576-c2sj5 1/1 Running 0 106m $ $ kubectl logs alb-ingress-controller-75cfcdd576-c2sj5 -n kube-system ------------------------------------------------------------------------------- AWS ALB Ingress controller Release: v1.1.4 Build: git-0db46039 Repository: https://github.com/kubernetes-sigs/aws-alb-ingress-controller.git -------------------------------------------------------------------------------
サンプルのWEBサイトを用意
ALBでロードバランスする先として、NginxのWEBサイトを作成します。Nginxのコンテナ1つを持ったpodを、ReplicaSetで2つ起動するマニフェストファイルを用意します。
deploy-nginx.yml
apiVersion: v1 kind: Namespace metadata: name: sample --- apiVersion: apps/v1 kind: Deployment metadata: name: deployment-sample-nginx namespace: sample labels: app: ingress-test spec: selector: matchLabels: app: sample-nginx replicas: 2 template: metadata: labels: app: sample-nginx spec: containers: - name: nginx-container image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: service-sample-nginx namespace: sample spec: type: NodePort ports: - name: nginx-port protocol: TCP port: 80 targetPort: 80 selector: app: sample-nginx
kubectlを実行します。
$ kubectl apply -f deploy-nginx.yml
namespace/sample created
deployment.apps/deployment-sample-nginx created
service/service-sample-nginx created
確認。
$ kubectl get deployment -n sample -o wide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR deployment-sample-nginx 2/2 2 2 4m52s nginx-container nginx:latest app=sample-nginx $ kubectl get service -n sample -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service-sample-nginx NodePort 172.20.156.90 <none> 80:30296/TCP 5m11s app=sample-nginx
WEBサイトのアクセス時に分かりやすいよう、各pod上のindex.htmlを編集します。
$ kubectl get pod -n sample NAME READY STATUS RESTARTS AGE deployment-sample-nginx-8c56d66c9-4pkqk 1/1 Running 0 5m34s deployment-sample-nginx-8c56d66c9-9c4g6 1/1 Running 0 5m34s $ $ kubectl exec -n sample -it deployment-sample-nginx-8c56d66c9-4pkqk -- cp /etc/hostname /usr/share/nginx/html/index.html $ kubectl exec -n sample -it deployment-sample-nginx-8c56d66c9-9c4g6 -- cp /etc/hostname /usr/share/nginx/html/index.html
実際にWEBサイトが表示されることを確認します。まずNodePortとして利用されているポート番号を確認します。Kubernetesは、30000-32676間いずれかのポートを、NodePortとしてデフォルトで利用します。
$ kubectl get service -n sample NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service-sample-nginx NodePort 172.20.156.90 <none> 80:30296/TCP 6m24s
sshでEKS Worker NodeのEC2インスタンスにアクセスして、WEBページを表示します。
$ ssh -i <秘密鍵ファイル> ec2-user@<worker nodeのIPアドレス> "curl -s http://localhost:30296" deployment-sample-nginx-8c56d66c9-4pkqk
ExternalDNSの設定
Ingressリソースを作成する前に、ExternalDNSの設定を実施します。ExternalDNSは、作成されたALBのエンドポイントを、Route53へカスタムドメインのDNSレコードとして自動登録してくれるものとなります。Kubernetes Incubator Projectの1つとして開発されています。
ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
AWS Route53だけでなく、様々なDNSサービスを利用できます。GitHubに記載されているRoute53向けの手順を参考に作業を進めます。
Setting up ExternalDNS for Services on AWS
まず、ExternalDNSで利用するサービスアカウントを作成します。Route53を操作できるIAMポリシーを定義してポリシーファイルを用意します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets" ], "Resource": [ "arn:aws:route53:::hostedzone/*" ] }, { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:ListResourceRecordSets" ], "Resource": [ "*" ] } ] }
ポリシーファイルを利用して、IAMポリシーを作成します。
$ aws iam create-policy \ --policy-name ExternalDNSIAMPolicy \ --policy-document file://iam-policy-external-dns.json \ --profile xxxxxx { "Policy": { "PolicyName": "ExternalDNSIAMPolicy", "PolicyId": "ANPAZDDARDEFRZSVK722A", "Arn": "arn:aws:iam::xxxxxxxxxxxx:policy/ExternalDNSIAMPolicy", "Path": "/", "DefaultVersionId": "v1", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2020-02-02T09:16:14Z", "UpdateDate": "2020-02-02T09:16:14Z" } }
作成したIAMポリシーを紐付けるサービスアカウントを作成します。eksctl用のyamlファイルを用意します。
sa-external-dns.yml
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: cluster-sample region: ap-northeast-1 iam: withOIDC: true serviceAccounts: - metadata: name: external-dns namespace: kube-system labels: app.kubernetes.io/name: external-dns attachPolicyARNs: - "arn:aws:iam::xxxxxxxxxxxx:policy/ExternalDNSIAMPolicy"
eksctlを実行します。
$ eksctl create iamserviceaccount -f sa-external-dns.yml --approve --profile xxxx [ℹ] eksctl version 0.13.0 [ℹ] using region ap-northeast-1 [ℹ] 1 iamserviceaccount(s) that already exist (kube-system/alb-ingress-controller) will be excluded [ℹ] 1 iamserviceaccount (kube-system/external-dns) was included (based on the include/exclude rules) [ℹ] combined exclude rules: kube-system/alb-ingress-controller [ℹ] no iamserviceaccounts present in the current set were excluded by the filter [!] serviceaccounts that exists in Kubernetes will be excluded, use --override-existing-serviceaccounts to override [ℹ] 1 task: { 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/external-dns", create serviceaccount "kube-system/external-dns" } } [ℹ] building iamserviceaccount stack "eksctl-cluster-sample-addon-iamserviceaccount-kube-system-external-dns" [ℹ] deploying stack "eksctl-cluster-sample-addon-iamserviceaccount-kube-system-external-dns" [ℹ] created serviceaccount "kube-system/external-dns"
サービスアカウントが作成されている事を確認します。
kubectl get serviceaccount external-dns -n kube-system -o yaml結果
apiVersion: v1 kind: ServiceAccount metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxxxxxx:role/eksctl-cluster-sample-addon-iamserviceaccoun-Role1-1UO6V35J8EAVP creationTimestamp: "2020-02-02T09:21:18Z" labels: app.kubernetes.io/name: external-dns name: external-dns namespace: kube-system resourceVersion: "27861" selfLink: /api/v1/namespaces/kube-system/serviceaccounts/external-dns uid: 5e83a074-459d-11ea-b9a5-0ab7d5b9f984 secrets: - name: external-dns-token-bx26v
このサービスアカウントと紐づくClusterRoleとClusterRoleBindingを作成します。必要となるマニフェストファイルを用意します。
clusterrole-external-dns.yml
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns labels: app.kubernetes.io/name: external-dns rules: - apiGroups: - "" - extensions resources: - services - pods - ingresses verbs: - get - watch - list - apiGroups: - "" resources: - nodes verbs: - watch - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-dns labels: app.kubernetes.io/name: external-dns roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dns subjects: - kind: ServiceAccount name: external-dns namespace: kube-system
kubectlを実行します。
$ kubectl apply -f clusterrole-external-dns.yml
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns created
ExternalDNSの処理を実行してくれるpodを作成します。必要となるマニフェストを用意します。
deploy-external-dns.yml
apiVersion: apps/v1 kind: Deployment metadata: name: external-dns namespace: kube-system spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: registry.opensource.zalan.do/teapot/external-dns:latest args: - --source=service - --source=ingress - --domain-filter=goodbyegangster.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones - --provider=aws - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) - --registry=txt - --txt-owner-id=<Route53 HostzoneのID> securityContext: fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
kubectlを実行します。
$ kubectl apply -f deploy-external-dns.yml
deployment.apps/external-dns created
podが作成されていることを確認します。
$ kubectl get deploy external-dns -n kube-system NAME READY UP-TO-DATE AVAILABLE AGE external-dns 1/1 1 1 62s $ $ kubectl get pod -n kube-system -l app=external-dns NAME READY STATUS RESTARTS AGE external-dns-59d8d76b8c-dx8vm 1/1 Running 0 114s $ $ kubectl logs external-dns-59d8d76b8c-dx8vm -n kube-system ... time="2020-02-02T11:56:49Z" level=info msg="Instantiating new Kubernetes client" time="2020-02-02T11:56:49Z" level=info msg="Using inCluster-config based on serviceaccount-token" time="2020-02-02T11:56:49Z" level=info msg="Created Kubernetes client https://172.20.0.1:443" time="2020-02-02T11:56:54Z" level=info msg="All records are already up to date"
サンプルWEBサイト用のIngressリソースの作成
Ingressリソースを作成して、ALBを作成します。以下のIngress用のマニフェストファイルを用意します。
ing-nginx.yml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-nginx namespace: sample annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:XXXXXXXXXX:certificate/ef62a474-e261-4039-a89a-d6955b6a384c alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' alb.ingress.kubernetes.io/load-balancer-attributes: access_logs.s3.enabled=true,access_logs.s3.bucket=awslogs-elb-XXXXXXXXXX,access_logs.s3.prefix=sample-nginx alb.ingress.kubernetes.io/tags: TagKey=TagValue labels: app: ingress-nginx spec: rules: - host: sample.goodbyegangster.com http: paths: - path: /* backend: serviceName: ssl-redirect servicePort: use-annotation - path: /* backend: serviceName: service-sample-nginx servicePort: 80
annotations
で指定する値にて、ALBの各パラメータを細かく設定できます。
下記はHTTPSへのリダイレクトルールを作成する場合のサンプル・マニフェストファイルです。
Redirect Traffic from HTTP to HTTPS
kubectlを実行します。
$ kubectl apply -f ing-nginx.yml
これにてALBが作成されます。暫くしたらALBがActiveとなるので、以下のコマンドでALBのエンドポイント経由でWEBサイトにアクセスしてみます。
$ curl -k -H 'Host:goodbyegangster.com' https://<ALBのエンドポイント名> deployment-sample-nginx-8c56d66c9-4pkqk
なおこの時に、EKS Worker Nodeにアタッチされたセキュリティグループに対して、必要なポリシー情報が自動的に追加されています。
ALBが作成されると同時に、Route53へもレコードが登録されています。ExternalDNSのpodのログを確認して、処理が正しく実行されている事を確認します。
$ kubectl logs external-dns-59d8d76b8c-dx8vm -n kube-system ... time="2020-02-02T11:59:52Z" level=info msg="All records are already up to date" time="2020-02-02T12:00:52Z" level=info msg="Desired change: CREATE sample.goodbyegangster.com A [Id: /hostedzone/xxxxxxxxxxxxxx]" time="2020-02-02T12:00:52Z" level=info msg="Desired change: CREATE sample.goodbyegangster.com TXT [Id: /hostedzone/xxxxxxxxxxxxxx]" time="2020-02-02T12:00:52Z" level=info msg="2 record(s) in zone goodbyegangster.com. [Id: /hostedzone/xxxxxxxxxxxxxx] were successfully updated"
最後に、実際にRoute53に登録されたDNS名でWEBサイトにアクセスしてみます。
$ curl https://sample.goodbyegangster.com/ deployment-sample-nginx-8c56d66c9-4pkqk