EKSクラスタ内で動作しているpodを外部公開するためのエンドポイントとしてALBを使う場合の構築手順をまとめておく。
ALB Ingress Controllerを使うことで、ingressをapplyしたときに自動でALBを構築してくれるのですごく便利だった。
また、ingressで任意のドメインを指定するとALBのhost-based routingに反映してくれるので、サブドメインを変えて複数のサイト公開も楽にできた。
加えて、ExternalDNSを使うと自分が管理しているドメインのレコード追加を自動でやってくれる。
SSL証明書をACMに用意しておくと、簡単にHTTPS化できたので、HTTPS対応についても最後にまとめておく。
アーキテクチャ
今回使うアプリケーションはDjangoとその前段にリバースプロキシとしてnginxを配置したシンプルなもの。EKSクラスタ内にはその他にALBにルール反映してくれるALB Inress ControllerとRoute 53にレコード反映してくれるExternalDNSのpodが起動している。SSL証明書発行にはACMを利用。
EKSクラスタ構築
公式サイトの方法を参考にクラスタ構築。
eksctl
で構築するのが便利。
eksctl create cluster \
--name test-cluster \
--version 1.14 \
--region ap-northeast-1 \
--nodegroup-name standard-workers \
--node-type t3.medium \
--nodes 2 \
--nodes-min 1 \
--nodes-max 4 \
--managed
(参考)EC2インスタンスのタイプによって付与可能なIPアドレス数が異なるため、podをたくさん起動する場合はworker nodeのインスタンスタイプに注意すること。
IPアドレス数の一覧はこちら
ALB Ingress Controllerのデプロイ
基本は公式サイトの手順通りに実施する。
EKSクラスタのサブネットにタグが付いていることを確認
- VPC 内のすべてのサブネット
kubernetes.io/cluster/
:<cluster-name>
shared
- VPC のパブリックサブネット
kubernetes.io/role/elb
:1
- VPC 内のプライベートサブネット
kubernetes.io/role/internal-elb
:1
IAM ポリシーを作成
IAMポリシーファイルをダウンロード
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/iam-policy.json
IAMポリシーを作成
aws iam create-policy \
--policy-name ALBIngressControllerIAMPolicy \
--policy-document file://iam-policy.json
EKSクラスタのワーカーノードの IAM ロール名を取得
kubectl -n kube-system describe configmap aws-auth
出力結果の<アカウントID>と<ロール名>を控えておく。
Name: aws-auth
Namespace: kube-system
Labels: <none>
Annotations: <none>
Data
====
mapRoles:
----
- groups:
- system:bootstrappers
- system:nodes
rolearn: arn:aws:iam::<アカウントID>:role/<ロール名>
username: system:node:{{EC2PrivateDNSName}}
mapUsers:
----
[]
Events: <none>
EKSクラスタのワーカーノードにIAMポリシーをアタッチ
aws iam attach-role-policy \
--policy-arn arn:aws:iam::<アカウントID>:policy/ALBIngressControllerIAMPolicy \
--role-name <ロール名>
ALB Ingress Controller で使用するサービスアカウント、クラスターロール、クラスターロールバインディングを作成
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/rbac-role.yaml
ALB Ingress Controller をデプロイ
ここは公式サイトとは違う方法でデプロイする。
公式サイトではmanifestをデプロイした後に環境に合わせて編集しているが、今回は最初にデプロイ用manifestをダウンロードし、ファイルを編集した後にデプロイする。
ALB-Ingress-Controllerのmanifestをダウンロード
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/alb-ingress-controller.yaml
Manifestを編集する。以下のパラメータのコメントアウトを外し、書き換える。
spec:
containers:
- args:
- --cluster-name=test-cluster
- --aws-vpc-id=<VPC ID>
- --aws-region=ap-northeast-1
(参考)VPC IDを確認するには以下のコマンドを実行する。
aws ec2 describe-vpcs
デプロイ
kubectl apply -f alb-ingress-controller.yaml
以下のコマンドを実行してエラーが表示されなければデプロイ成功
kubectl logs -n kube-system -f $(kubectl get po -n kube-system | egrep -o 'alb-ingress-controller[A-Za-z0-9-]+')
ExternalDNSのデプロイ
ALBのエンドポイントを自分が管理しているRoute53にailiasとして反映させてくれる ExternalDNS というOSSがあるので使ってみる。
IAM ポリシーを作成
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://external-dns-iam-policy.json
EKSクラスタのワーカーノードにIAMポリシーをアタッチ
aws iam attach-role-policy \
--policy-arn arn:aws:iam::<アカウントID>:policy/ExternalDNSIAMPolicy \
--role-name <ロール名>
ExternalDNS をデプロイ
ExternalDNSのmanifestをダウンロード
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.3/docs/examples/external-dns.yaml
Manifestを編集。example.com
には自分で管理しているFQDNに変更すること。ダウンロードしたファイルではpolicy
オプションがupsert-only
になっているが、これはレコード追加だけすることを表す。今回はingressの内容に合わせて適宜レコード削除もしてほしいのでpolicy
オプションは使わない。
args:
- --domain-filter=example.com
#- --policy=upsert-only # コメントアウトまたは削除
デプロイ
kubectl apply -f external-dns.yaml
確認
kubectl logs -f $(kubectl get po | egrep -o 'external-dns[A-Za-z0-9-]+')
以下のように表示されればOK
time="2019-12-13T14:48:31Z" level=info msg="Created Kubernetes client https://10.100.0.1:443"
app1のmanifestを作成
ここからようやくアプリケーションをデプロイすることができる。
まずはdeploymentとserviceとingressのmanifestを用意する。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app1-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: app1
spec:
containers:
- image: tetsis/simple-nginx-django-app
name: app1
ports:
- containerPort: 8080
env:
- name: APPLICATION_NAME
value: app1
- image: tetsis/simple-nginx-django-proxy
name: app1-proxy
ports:
- name: app1-port
containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: app1-service
spec:
ports:
- port: 80
targetPort: app1-port
protocol: TCP
type: NodePort
selector:
app: app1
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
labels:
app: ingress
spec:
rules:
- host: app1.example.com
http:
paths:
- path: /*
backend:
serviceName: app1-service
servicePort: 80
example.comには自分が所有するドメインを指定する。
app1のデプロイ
kubectl apply -f app1-deployment.yaml
kubectl apply -f app1-service.yaml
kubectl apply -f ingress.yaml
ALB Ingress Controllerで作られたALBを確認
kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
ingress app1.example.com d7f12bb1-default-ingress-e8c7-1478980459.ap-northeast-1.elb.amazonaws.com 80 38s
Route 53にレコードが作られたことを確認
コンソール画面もしくは以下のコマンドでAliasレコードが作成されていることを確認する。
aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/<ホストゾーンID>
ページにアクセスしてみる
ALBの起動とRoute 53のDNSレコード情報が反映されるまで少し待って、ブラウザで「http://app1.example.com」にアクセスし、以下のように表示されればOK。
(example.comは自分のドメインを指定)
This application is "app1".
サブドメイン分割でapp2を追加
さっきはapp1.example.com
というドメインでアプリケーションをデプロイしたが、次はapp2.example.com
ドメインでアプリケーションを追加デプロする。
deploymentとserviceはapp1と同様にmanifestを作成する。
ingressについては 、app1とapp2で同じALBを使うので先程作成したmanifestに追記する。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app2-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: app2
spec:
containers:
- image: tetsis/simple-nginx-django-app
name: app2
ports:
- containerPort: 8080
env:
- name: APPLICATION_NAME
value: app2
- image: tetsis/simple-nginx-django-proxy
name: app2-proxy
ports:
- name: app2-port
containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: app2-service
spec:
ports:
- port: 80
targetPort: app2-port
protocol: TCP
type: NodePort
selector:
app: app2
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
labels:
app: ingress
spec:
rules:
- host: app1.example.com
http:
paths:
- path: /*
backend:
serviceName: app1-service
servicePort: 80
- host: app2.example.com
http:
paths:
- path: /*
backend:
serviceName: app2-service
servicePort: 80
app2のデプロイ
kubectl apply -f app2-deployment.yaml
kubectl apply -f app2-service.yaml
kubectl apply -f ingress.yaml
ページにアクセスしてみる
ブラウザで「http://app2.example.com」にアクセスして以下の文字が表示されればOK。(もちろんドメインは環境に合わせること)
This application is "app2".
HTTPS化
公式ページを参考に、ingress.yamlの annotations
句に一行追加する。
尚、使用するドメインのSSL証明書は事前にACMで発行しておく。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' # 追加
labels:
app: ingress
spec:
rules:
- host: app1.example.com
http:
paths:
- path: /*
backend:
serviceName: app1-service
servicePort: 80
- host: app2.example.com
http:
paths:
- path: /*
backend:
serviceName: app2-service
servicePort: 80
忘れずにapplyする。
kubectl apply -f ingress.yaml
ブラウザで「https://app1.example.com」、 「https://app2.example.com」 にアクセスして、ページが表示されることを確認する。
リソースの削除
EKSクラスタは起動しておくだけでお金がかかるので、検証利用の場合は忘れずにクラスタを削除を実施する。
K8sリソースを削除
kubectl delete -f ingress.yaml
kubectl delete -f app1-service.yaml
kubectl delete -f app2-service.yaml
kubectl delete -f app1-deployment.yaml
kubectl delete -f app2-deployment.yaml
IAMポリシーをデタッチ
aws iam detach-role-policy --policy-arn arn:aws:iam::<アカウントID>:policy/ExternalDNSIAMPolicy --role-name <ロール名>
aws iam detach-role-policy --policy-arn arn:aws:iam::<アカウントID>:policy/ALBIngressControllerIAMPolicy --role-name <ロール名>
IAMポリシーを削除
aws iam delete-policy --policy-arn arn:aws:iam::<アカウントID>:policy/ExternalDNSIAMPolicy
aws iam delete-policy --policy-arn arn:aws:iam::<アカウントID>:policy/ALBIngressControllerIAMPolicy
EKSクラスタを削除
eksctl delete cluster --region=ap-northeast-1 --name=test-cluster