一休.com Developers Blog

一休のエンジニア、デザイナー、ディレクターが情報を発信していきます

Amazon EKS でWindowsコンテナを動かす

Amazon EKS Windows Container を使ってみる。

今年の10月に、Amazon EKSがWindows ワーカーノードのサポートを開始しました。

aws.amazon.com

一休では、今年の初めから、既存アプリケーションのEKS移行を行っており、夏には、ほぼすべてのLinux系アプリケーションをEKSへ移行することができました。

user-first.ikyu.co.jp

これを踏まえ、Windows系のウェブアプリケーションもEKSへ移行できないか、技術検証を行っています。具体的な検証ポイントは以下のふたつです。

  1. Amazon EKS で、Linuxコンテナ同様、Windows コンテナが動作するか。
  2. 既存の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-controllerexternal-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のリソースにアクセスするでしょう。 このような場合、kube2iamkiamを使うことでポッド単位でAWSリソースに対するアクセス権限の制御が行えます。 実際、一休では、すでにサービスインしているクラスタではkube2iamを使っています。 しかし、kube2iamは、Deamonsetであり、Windowsノードでは動作しません。したがって、別の方法で、同じことを実現する必要があります。

kube2iamなしでIAM ロールを Kubernetes サービスアカウントに関連付ける

すでにいくつかの記事で紹介されていますが、今年の9月に、EKSがネイティブで IAM ロールを Kubernetes サービスアカウントに関連付ける仕組みを提供し始めました。

aws.amazon.com

dev.classmethod.jp

この仕組みを使えば、kube2iamを使わずに、podのAWSリソースに対するアクセス権限の制御ができそうです。 実際にうまくいくかどうか試してみます。ここでは、aws-alb-ingress-controllerがきちんとapplication load balancerを作成できるかどうか確認してみます。

まず、上述したクラスタ定義を使って、eksctlでクラスタを作ります。すると、AWSコンソールのIAMの画面のIDプロバイダーに、下記のように、OpenID Connectのプロバイダが作成されます。 withOIDC: true を設定したのはこのためです。

f:id:s-tokutake:20191216182151p:plain

また、AWSコンソールで新しく作成されたEKSクラスタの設定を見ると、上述のOpenID ConnectプロバイダのURLが、表示されます。

f:id:s-tokutake:20191216182430p:plain

次に、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 コマンドは使わずに、手動で設定してみます。 といっても設定は簡単です。↓の通り、対象のロールの信頼関係編集ボタンクリックします。

f:id:s-tokutake:20191216182811p:plain

そして、次のようなポリシーを設定します。

{
  "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が作成されています。

f:id:s-tokutake:20191216182936p:plain

これで、windowsノードにデプロイしたPodが外部からのリクエストを受けれるようになりました。

WIndowsノードの課題

これで、Windowsのウェブアプリケーションも、プロダクション環境で、EKSで動かせる、と思いきや、ひとつ大きなハードルがありました。 Datadogが、Windowsノードが含まれたKubernetesクラスタの監視をサポートしていないのです。 Linuxノードだけであれば、Datadogのhelmチャートを入れるだけで、必要なメトリクスをほぼすべて収集できます。 一休は監視/ログ管理/APMをすべてDatadogに集約しているので、WindowsノードだけDatadog以外の方法を採用する、というのは合理的ではありません。 こちらはDatadog側にリクエストを出している状況です。

まとめ

EKSでWIndowsワーカーノードを扱う方法を書きました。クラスタの準備、とアプリケーションの動作確認自体は比較的簡単に行うことができました。 今後は、DatadogのWindows対応を待ちつつ、EKSへの移行を引き金にして、なかなか着手しにくいWindowsウェブアプリケーションのコンテナ化を推進し、必要なリファクタリングを行ったり、細かな技術検証をするフェーズになりそうです。


この記事の筆者について

システム本部CTO室所属の 徳武 です。 サービスの技術基盤の開発運用、開発支援、SREを行なっています。