Amazon EKS Windows Container を使ってみる。
今年の10月に、Amazon EKSがWindows ワーカーノードのサポートを開始しました。
一休では、今年の初めから、既存アプリケーションのEKS移行を行っており、夏には、ほぼすべてのLinux系アプリケーションをEKSへ移行することができました。
これを踏まえ、Windows系のウェブアプリケーションもEKSへ移行できないか、技術検証を行っています。具体的な検証ポイントは以下のふたつです。
- Amazon EKS で、Linuxコンテナ同様、Windows コンテナが動作するか。
- 既存のWindowsのWebアプリケーション(ASP.NETアプリケーションをDockerコンテナ化できるか。
2については、公開されている各種チュートリアルやサンプルなどを参考に、動作させることができました。
この記事では、主に、1.の検証でわかったことや注意点を紹介したいと思います。
この記事は 一休.com Advent Calendar 2019 の20日目の記事です。
Amazon EKS でWindowsのワーカーノードを作ってみる。
まず、eksctlを使って新しいeksクラスタを作成し、Windowsのワーカーノードを作ってみます。 作成のコマンドは以下の通りです。
eksctl create cluster -f cluster.yaml --install-vpc-controllers
--install-vpc-controllers
という引数を渡すことで、Windowsノードグループに必要なリソースを追加します。
cluster.yamlの内容は以下の通りです。
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: dev-win region: ap-northeast-1 version: "1.14" vpc: id: "vpc-123456789" cidr: "10.0.0.0/16" subnets: private: ap-northeast-1a: id: "subnet-xxxxxxxx" cidr: "10.0.144.0/24" ap-northeast-1c: id: "subnet-yyyyyyyy" cidr: "10.0.145.0/24" ap-northeast-1d: id: "subnet-zzzzzzzz" cidr: "10.0.146.0/24" iam: withOIDC: true nodeGroups: - name: ng-control-1 labels: {role: workers } tags: {Stack: development, Site: ikyucom, Role: eks-node, k8s.io/cluster-autoscaler/wincluster: owned, k8s.io/cluster-autoscaler/enabled: "true"} instanceType: t3.medium desiredCapacity: 2 maxSize: 2 ebsOptimized: true privateNetworking: true securityGroups: attachIDs: [sg-xxxxx] withShared: true ssh: allow: true publicKeyPath: xxxx-key - name: ng-win-1 amiFamily: WindowsServer2019FullContainer labels: {role: workers } tags: {Stack: development, Site: ikyucom, Role: eks-node, k8s.io/cluster-autoscaler/wincluster: owned, k8s.io/cluster-autoscaler/enabled: "true"} instanceType: t3.medium desiredCapacity: 3 maxSize: 3 ebsOptimized: true privateNetworking: true securityGroups: attachIDs: [sg-xxxxx] withShared: true ssh: allow: true publicKeyPath: xxxx-key
注目点はふたつです。
withOIDC: true
を設定しています。IAMのアクセス許可をOIDC(OpenID Connect)を使って、Kubernetes サービスアカウントに割り当てるために必要な設定です。詳細は後述します。- ノードグループをふたつ作成しています。LinuxのノードグループとWindowsのノードグループです。これは、一部のシステム系のPodが、Linuxでしか動かないためです。
作成したクラスタにポッドをデプロイする。
システム系のポッドをデプロイする。
一休では、aws-alb-ingress-controllerとexternal-dnsを使ってWebアプリケーションを提供しています。Windowsのコンテナアプリケーションでもこのふたつを使っていきたいです。
ただし、このふたつは、Windowsノードグループ上では動作しません。Linux上で動作させる必要があります。
これを実現するために、node selectorを使います。Linux ノードにはデフォルトで、 kubernetes.io/os: linux
というラベルがついています(Windowsの場合は、kubernetes.io/os: windows
がつきます)。
これを踏まえると、aws-alb-ingress-controllerのyamlは以下の通りになります。
apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/name: alb-ingress-controller name: alb-ingress-controller 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: - --ingress-class=alb - --cluster-name=dev-win - --aws-vpc-id=vpc-123456789 - --aws-region=ap-northeast-1 image: docker.io/amazon/aws-alb-ingress-controller:v1.1.3 ports: - containerPort: 10254 name: health protocol: TCP serviceAccountName: aws-alb-ingress-controller nodeSelector: kubernetes.io/os: linux
external-dns は以下のようになります。
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: external-dns namespace: kube-system spec: strategy: type: Recreate template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: registry.opensource.zalan.do/teapot/external-dns:v0.5.17 args: - --log-level=info - --domain-filter=dev.com - --policy=upsert-only - --provider=aws - --registry=txt - --interval=1m - --source=service - --source=ingress ports: - containerPort: 7979 protocol: TCP livenessProbe: failureThreshold: 3 httpGet: path: /healthz port: 7979 scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 nodeSelector: kubernetes.io/os: linux
このふたつのyamlをapplyすることで、両方ともLinuxノードで動かすことができます。 ※ サービスアカウントの設定については後述します。
Webアプリケーションをデプロイする。
こちらもnode selectorを使って、windowsノードグループにデプロイさせるようにします。 事前にデプロイした、alb-ingress-controllerとexternal-dnsを使って、クラスタ外からアクセスできるウェブアプリケーションとして、デプロイするには以下のyamlになります。
## Deploymentの定義 apiVersion: apps/v1 kind: Deployment metadata: name: test-web-app spec: selector: matchLabels: app: test-web-app replicas: 1 template: metadata: labels: app: test-web-app spec: containers: - name: test-web-app image: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test-web-app:latest ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent livenessProbe: httpGet: port: 80 path: /prob failureThreshold: 5 periodSeconds: 5 readinessProbe: httpGet: port: 80 path: /prob failureThreshold: 5 periodSeconds: 5 dnsConfig: options: - name: ndots value: '1' serviceAccountName: test-web-app nodeSelector: kubernetes.io/os: windows --- ## Serviceの定義 apiVersion: v1 kind: Service metadata: name: test-web-app namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-web-app type: NodePort --- ## Ingressの定義 apiVersion: extensions/v1beta1 kind: Ingress metadata: labels: app: test-web-app name: test-web-app annotations: alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:xxxxxxxxxxxx:certificate/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee alb.ingress.kubernetes.io/healthcheck-path: /health alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/security-groups: security-group-name alb.ingress.kubernetes.io/subnets: subnet-xxxxxxxx,subnet-yyyyyyyy,subnet-zzzzzzzz kubernetes.io/ingress.class: alb external-dns.alpha.kubernetes.io/hostname: testapp.dev.com spec: rules: - http: paths: - path: /* backend: serviceName: test-web-app servicePort: 80
これをapplyすることで、無事、Windowsノードにウェブアプリケーションをポッドを配置できました。
AWSリソースへのアクセス権限はどうするか
ここまでの説明で大事な点を割愛しています。alb-ingress-controllerやexternal-dnsのAWSのリソースへのアクセス権限についてです。また、デプロイするウェブアプリケーションも要件によってはAWSのリソースにアクセスするでしょう。 このような場合、kube2iamやkiamを使うことでポッド単位でAWSリソースに対するアクセス権限の制御が行えます。 実際、一休では、すでにサービスインしているクラスタではkube2iamを使っています。 しかし、kube2iamは、Deamonsetであり、Windowsノードでは動作しません。したがって、別の方法で、同じことを実現する必要があります。
kube2iamなしでIAM ロールを Kubernetes サービスアカウントに関連付ける
すでにいくつかの記事で紹介されていますが、今年の9月に、EKSがネイティブで IAM ロールを Kubernetes サービスアカウントに関連付ける仕組みを提供し始めました。
この仕組みを使えば、kube2iamを使わずに、podのAWSリソースに対するアクセス権限の制御ができそうです。 実際にうまくいくかどうか試してみます。ここでは、aws-alb-ingress-controllerがきちんとapplication load balancerを作成できるかどうか確認してみます。
まず、上述したクラスタ定義を使って、eksctlでクラスタを作ります。すると、AWSコンソールのIAMの画面のIDプロバイダーに、下記のように、OpenID Connectのプロバイダが作成されます。
withOIDC: true
を設定したのはこのためです。
また、AWSコンソールで新しく作成されたEKSクラスタの設定を見ると、上述のOpenID ConnectプロバイダのURLが、表示されます。
次に、alb-ingress-controller用ロールに信頼関係を設定を設定します。
公式ブログのチュートリアル では、eksctl create iamserviceaccount
コマンドを使って、新規にKubernetesサービスアカウントと対応するIAMロールを作成しています。このコマンドを実行するだけで、以下の3つの必要な設定が、一発で完了します。
- IAMロールの新規作成
- Kubernetesサービスアカウントの作成
- KubernetesサービスアカウントがIAMロールを引き受けるようにする信頼ポリシーの設定
一方、わたしたちのケースでは、新規にIAMロールを作るらずに、既存のEKSクラスタで使っているaws-alb-ingress-controller用のロール alb-ingress-controller-role
を使いまわしたいです。
このため、eksctl create iamserviceaccount
コマンドは使わずに、手動で設定してみます。
といっても設定は簡単です。↓の通り、対象のロールの信頼関係編集ボタンクリックします。
そして、次のようなポリシーを設定します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::xxxxxxxxxxxx:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.ap-northeast-1.amazonaws.com/id/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:sub": "system:serviceaccount:kube-system:aws-alb-ingress-controller", "oidc.eks.ap-northeast-1.amazonaws.com/id/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:aud": "sts.amazonaws.com" } } } ] }
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
には、先ほど作成された OpenID Connectプロバイダのarnの識別子が入ります。
そして、Kubernetes側にaws-alb-ingress-controller用のサービスアカウントを作成します。↑のポリシーに書いた通り、サービスアカウント名は、 aws-alb-ingress-controller
で作成します。
yamlは以下の通り。
apiVersion: v1 kind: ServiceAccount metadata: name: aws-alb-ingress-controller namespace: kube-system annotations: eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxxxxxx:role/alb-ingress-controller-role # ロールやロールのバインディングも必要ですが、ここでは割愛します。
annotationsに、 eks.amazonaws.com/role-arn
というキーで、ロールのarnを指定します。
あと、はこのサービスアカウントをapplyします。
AWSコンソールのロードバランサの画面で、クラスタ名のタグで検索してみると、↓の通り、albが作成されています。
これで、windowsノードにデプロイしたPodが外部からのリクエストを受けれるようになりました。
WIndowsノードの課題
これで、Windowsのウェブアプリケーションも、プロダクション環境で、EKSで動かせる、と思いきや、ひとつ大きなハードルがありました。 Datadogが、Windowsノードが含まれたKubernetesクラスタの監視をサポートしていないのです。 Linuxノードだけであれば、Datadogのhelmチャートを入れるだけで、必要なメトリクスをほぼすべて収集できます。 一休は監視/ログ管理/APMをすべてDatadogに集約しているので、WindowsノードだけDatadog以外の方法を採用する、というのは合理的ではありません。 こちらはDatadog側にリクエストを出している状況です。
まとめ
EKSでWIndowsワーカーノードを扱う方法を書きました。クラスタの準備、とアプリケーションの動作確認自体は比較的簡単に行うことができました。 今後は、DatadogのWindows対応を待ちつつ、EKSへの移行を引き金にして、なかなか着手しにくいWindowsウェブアプリケーションのコンテナ化を推進し、必要なリファクタリングを行ったり、細かな技術検証をするフェーズになりそうです。
この記事の筆者について
システム本部CTO室所属の 徳武 です。 サービスの技術基盤の開発運用、開発支援、SREを行なっています。