EKS on Fargateクラスタ構築メモ

2019年12月にGAを発表したEKS on Fargateを使ってみたので、そのメモを残します。

EKS on Fargateとは

はじめにAWSが提供するコンテナ関連のサービスについておさらいします。 サービスが提供するものは、大きく分けて『コントロールプレーン』と『データプレーン』の2つに分類できます。 よく耳にするECSやEKSは、コンテナを管理するコントロールプレーンを提供するサービスです。 他方、コンテナを動かすデータプレーンを提供するサービスとして、EC2とFargateがあります。 EKS on Fargateは、これまでなかったEKSとFargateの組み合わせです。

コントロールプレーン データプレーン
ECS(AWS独自/Serverless) EC2
ECS(AWS独自/Serverless) Fargate(Serverless)
EKS(Kubernetes/Serverless) EC2
EKS(Kubernetes/Serverless) Fargate(Serverless) ←New

EKS on Fargateの利点はサーバ運用無しでk8sを使えることでしょう。

EKS on Fargateの制限事項

サーバ運用から解放される一方で(2019年12月13日現在)いくつかの制限があります。 このため、ログ収集周りの対応に手間がかかります。

  • CLB/NLB未対応
  • GPU未対応
  • Podのリソース上限は4 vCPUと30GBメモリ
  • Podのディスク上限は10GB、Volume Mount向けに4GB
  • Daemonsets、Privileged Container、HostNetwork、HostPortは利用不可
  • 永続ボリュームやファイルシステムで必要とされるステートフルなワークロードは未対応

クラスタの構成

EKS on Fargateが外部からのアクセスを受け付ける公式の手段はALB Ingress Controllerです。 ALB Ingress ControllerはIngressリソースの数だけALBを作成してしまい、その分費用がかかります。 これを回避するため、k8s内部にALBのトラフィックをすべて受け取るTraefikを配置します。 TraefikはIngress Controllerとしてトラフィックを各サービスに振り分けます。

+---------+  +-----------------------------+
|         |  |                             |
|   ALB   |  |             k8s             |
|         |  |                             |
|         |  |  +---------+   +---------+  |
|         |  |  |         |   |         |  |
|         |  |  |         | +-+ Service |  |
|         |  |  |         | | |         |  |
|         |  |  |         | | +---------+  |
|         +-----+ Traefik +-+              |
|         |  |  |         | | +---------+  |
|         |  |  |         | | |         |  |
|         |  |  |         | +-+ Service |  |
|         |  |  |         |   |         |  |
|         |  |  +---------+   +---------+  |
|         |  |                             |
+---------+  +-----------------------------+

EKS on Fargateクラスタの構築

eksctlというCLIを使用して、k8sクラスタを1コマンドで構築します。 eksctlは内部でCloudFormationを用いて必要なリソースを作成します。

brew tap weaveworks/tap
brew install weaveworks/tap/eksctl
eksctl create cluster --name your-cluster-name --version 1.14 --fargate

既存のSubnetに構築する場合は、オプションに--vpc-private-subnets--vpc-public-subnetsを指定します。

参考:https://eksctl.io/

ALB Ingress Controllerの構築

EKS on FargateにおけるPodの公開手段はALBのみです。 ALBはALB Ingress Controllerを利用して構築できます。

構築するにあたり、ALB Ingress ControllerにALBを作成する権限を与える必要があります。 言い換えると、Fargate上のPodに権限を与える必要があります。 これはAWSのIAM RoleとService Accountの関連付けを行うことで実現できます。 以降では実際の導入手順を説明します。

注意点として、AWSの公式ドキュメントは執筆時点でFargateに対応しておらず、ワーカーノード(EC2)に対応した手順が公開されています。そのため、公式ドキュメントに従うとFargateでは動作しません。

Subnetのタグ設定

ALBをどのサブネットに配置すべきかを知らせるため、サブネットにタグを設定します。 プライベートサブネットにはNo.1とNo.2のタグを設定してください。 パブリックサブネットにはNo.1とNo.3のタグを設定してください。

No. Key Value
1 kubernetes.io/cluster/${cluster-name} owned or shared
2 kubernetes.io/role/internal-elb 1
3 kubernetes.io/role/elb 1

参考:https://kubernetes-sigs.github.io/aws-alb-ingress-controller/guide/controller/config/#subnet-auto-discovery

Service AccountとIAMの紐付け

ALB Ingress ControllerがALBを作成するためのIAM Policyを作成します。

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/iam-policy.json
mv -i iam-policy.json alb-ingress-iam-policy.json
aws iam create-policy \
  --policy-name ALBIngressControllerIAMPolicy \
  --policy-document file://alb-ingress-iam-policy.json
policy_arn=$(
  aws iam list-policies \
  | jq -r '.Policies[] | select(.PolicyName == "ALBIngressControllerIAMPolicy") | .Arn'
)

自クラスタでService AccountとIAMを関連付けるためのOIDC Identity Providerを作成します。

eksctl utils associate-iam-oidc-provider \
  --region=ap-northeast-1 \
  --cluster=your-cluster-name \
  --approve

ALB Ingress ControllerのService AccountとRBACを作成します。 マニフェストは公式が公開しているものを使用します。

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/rbac-role.yaml
mv -i rbac-role.yaml alb-ingress-rbac-role.yaml
kubectl apply -f alb-ingress-rbac-role.yaml

eksctlを使ってIAMとService Accountの関連付けを行います。 内部で次の処理を行っています。

  1. CloudFormationを用いて、指定したIAM PolicyをアタッチしたIAM Roleを作成
  2. 作成したIAM RoleをService Accountに関連付け
eksctl create iamserviceaccount \
  --name alb-ingress-controller \
  --namespace kube-system \
  --cluster your-cluster-name \
  --attach-policy-arn ${policy_arn} \
  --approve --override-existing-serviceaccounts
kubectl get sa -n kube-system alb-ingress-controller -o jsonpath="{.metadata.annotations['eks\.amazonaws\.com/role-arn']}"

ALB Ingress Controllerのデプロイ

公式のマニフェストをダウンロードします。

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/alb-ingress-controller.yaml

alb-ingress-controller.yamlを編集します。 編集箇所は、ALB Ingress Controllerコンテナの引数であるクラスタ名、VPC ID、およびAWSリージョン名です。

- args:
    - --ingress-class=alb
    - --cluster-name=devCluster
    - --aws-vpc-id=vpc-xxxxxx
    - --aws-region=us-west-1

デプロイします。

kubectl apply -f alb-ingress-controller.yaml
kubectl get po -n kube-system -w

ALB Ingress Controllerのデプロイがうまくいかない場合

ログを確認します。

kubectl logs -n kube-system deployment.apps/alb-ingress-controller

Traefik Ingress Controllerの構築

Traefikはシンプルなリバースプロキシです。 k8sのIngress Controllerとしてデプロイします。

参考:https://docs.traefik.io/

Traefikのデプロイ

筆者が用意したマニフェストをダウンロードして、適用します。

git clone https://github.com/twihike/k8s-manifests.git
cd k8s-manifests/eks-on-fargate/traefik
# 不要なコンテナ定義(node-exporter/fluent-bit)を削除してください
# vi deployment.yml
kubectl apply -f crd.yml
kubectl apply -f clusterrole.yml
kubectl apply -f serviceaccount.yml
kubectl apply -f clusterrolebinding.yml
kubectl apply -f deployment.yml
kubectl apply -f service.yml

動作確認用サンプルアプリのデプロイ

マニフェスト(sample-app.yaml)を作成します。

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  namespace: default
  name: sample-route
spec:
  entryPoints:
    - web
  routes:
    - match: PathPrefix(`/`)
      kind: Rule
      services:
        - name: sample-svc
          port: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: default
  name: sample-svc
spec:
  selector:
    app: sample-nginx
  ports:
    - name: http
      port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deploy
spec:
  selector:
    matchLabels:
      app: sample-nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: sample-nginx
    spec:
      containers:
        - name: sample-nginx
          image: nginx:1.7.9
          ports:
            - containerPort: 80

デプロイします。

kubectl apply -f sample-app.yaml
kubectl get po -w

Traefikの動作確認

kubectl port-forwardを実行して、localhost:8000にアクセスします。

# pod=$(kubectl -n kube-system get po --selector name=traefik -o=jsonpath={.items[0].metadata.name})
kubectl -n kube-system port-forward deployment.apps/traefik 8000:8000

ALBの構築

Ingressリソースを作成すると、ALB Ingress Contollerは指定したServiceに接続するALBを自動作成します。

Ingressの適用

次の内容でalb-ingress.yamlを作成します。 Fargateの場合、target-typeipのみに対応していることに注意してください。

独自ドメインの場合は、追加で次の対応をします。

  • 証明書のARNを指定
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: alb-ing
  namespace: kube-system
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/target-type: ip
    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:123456789012:certificate/12345678-1234-1234-1234-123456789012"
    alb.ingress.kubernetes.io/healthcheck-port: "8080"
    alb.ingress.kubernetes.io/healthcheck-path: /ping
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: traefik
              servicePort: 8008

作成したファイルを適用します。

kubectl apply -f alb-ingress.yaml

独自ドメインの場合は、追加で次の対応をします。

  • Route53にてレコードを設定
  • レコードの宛先はIngress Controllerにより自動作成されたALBのドメインとする

ALBの接続確認

Webブラウザから接続確認をします。 HTTPSとHTTPの両方から接続できることを確認してください。

ALBの接続確認がうまくいかない場合

ログを確認します。

kubectl logs -n kube-system deployment.apps/alb-ingress-controller

クラスタのバージョンアップ

コントロールプレーンをバージョンアップします。 まずはバージョンアップの内容を確認します。

eksctl update cluster --name your-cluster-name

バージョンアップの内容に問題がなければ、適用します。

eksctl update cluster --name your-cluster-name --approve

クラスタのアドオンを更新します。 まずはバージョンアップの内容を確認します。

eksctl utils update-kube-proxy --cluster your-cluster-name
eksctl utils update-aws-node --cluster your-cluster-name
eksctl utils update-coredns --cluster your-cluster-name

バージョンアップの内容に問題がなければ、適用します。

eksctl utils update-kube-proxy --cluster your-cluster-name --approve
eksctl utils update-aws-node --cluster your-cluster-name --approve
eksctl utils update-coredns --cluster your-cluster-name --approve
# Fargateを有効にする
kubectl patch deployment coredns -n kube-system --type json \
  -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'