ECSで運用しているサービスにEKSを導入し移行する - ECSからEKSへ移行

by

@wapa5pow

前回のEKS構築編に引き続き、ECSからEKSへの移行をどのようにしたか紹介します。今回は、ECSで動いているフロントエンドとバックエンドをEKSに移行します。構成図としては以下です。

eks

EKSのIngressの設定でALBをたてます。ALBからNuxt.js(ポートは3333)でつくられたフロントエンドとGolang(ポートは3000)で作られたバックエンドにリクエストを振り分けます。

フロントエンドとバックエンドの設定

はじめにバックエンドのKubernetes用のファイルを作成します。replicasが1になっているとポッドが1つしか作られませんが、本番運用する際は障害に備えて最低でも3で運用してください。なお、Liveness ProbesとReadiness Probesは設定していないですが本番運用するときは設定したほうがいいので適宜設定してください。

title=backend.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  ports:
  - port: 3000
    targetPort: 3000
    protocol: TCP
    name: backend
  type: NodePort
  selector:
    app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: xxx.dkr.ecr.ap-northeast-1.amazonaws.com/backend:latest
        ports:
        - containerPort: 3000

フロントエンド用のファイルは以下のようになります。

title=frontend.yaml
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  ports:
  - port: 3333
    targetPort: 3333
    protocol: TCP
    name: frontend
  type: NodePort
  selector:
    app: frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: xxx.dkr.ecr.ap-northeast-1.amazonaws.com/frontend:latest
        ports:
        - containerPort: 3333

フロントエンドとバックエンドをKubernetesに反映します。

kubectl apply -f backend.yaml
kubectl apply -f frontend.yaml

デプロイされたか確認します。

kubectl get pod

ALBの設定

Kubernetes内への外部からアクセスをコントロールするものをKubernetesではIngressといいます。Ingressを設定するとロードバランサが作られるのですが、今回はロードバランサとしてALBを採用します。

Ingressを設定する前に権限関係の解決に必要となるkube2iamを設定します。

kube2iamの設定

kube2iamはKubernetesの設定ファイルのアノテーションにAWSのロールを記載することによりポッドがそのロールの権限を実行できるようになります。これを使わない場合AWSの鍵情報を権限が必要なすべてのKubernetes設定ファイルにかかなければならず面倒なのと秘匿情報を設定ファイルにかきたくないのでkube2iamを使います。

kube2iamの設定はREADMEを見ながらやるのがいいかなと思いますが手順を詳しくみていきます。

はじめにkube2iam用のRBACロールをkubectl apply -f kube2iam-rbac.yamlで反映します。

title=kube2iam-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube2iam
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kube2iam
rules:
  - apiGroups: [""]
    resources: ["namespaces","pods"]
    verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kube2iam
subjects:
- kind: ServiceAccount
  name: kube2iam
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: kube2iam
  apiGroup: rbac.authorization.k8s.io

AWSのコンソールでIAMのポリシーを作成します。以下をeks-kube2iamという名前で作成します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "sts:AssumeRole"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

k8s-kube2iamという名前でIAMのロールを作成します。ロールの使用するサービスはEC2を選びます。ロールの使用するポリシーはEC2で先程作ったeks-kube2iamのポリシーをアタッチします。ロールの信頼関係の編集を選び以下でアップデートします。なおarn:aws:iam::xxx:role/worker-nodes-NodeInstanceRole-yyyの部分はEKSのノードのロールを設定してください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::xxx:role/worker-nodes-NodeInstanceRole-yyy"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

kube2iamのDaemonSetを作成しKubernetesにkubectl apply -f kube2iam.yamlで反映します。

title=kube2iam.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube2iam
  namespace: kube-system
  labels:
    app: kube2iam
spec:
  selector:
    matchLabels:
      name: kube2iam
  template:
    metadata:
      labels:
        name: kube2iam
    spec:
      serviceAccountName: kube2iam
      hostNetwork: true
      containers:
        - image: jtblin/kube2iam:latest
          imagePullPolicy: Always
          name: kube2iam
          args:
            - "--app-port=8181"
            - "--auto-discover-base-arn"
            - "--iptables=true"
            - "--host-ip=$(HOST_IP)"
            - "--host-interface=eni+"
            - "--verbose"
          env:
            - name: HOST_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          ports:
            - containerPort: 8181
              hostPort: 8181
              name: http
          securityContext:
            privileged: true

以下で確認してログにエラーが出てなければ大丈夫です。

$ kubectl get pod -n kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
...
kube2iam-kg5gz                          1/1       Running   0          9m
...

$ kubectl logs -n kube-system kube2iam-kg5gz

alb-ingress-controllerの設定

alb-ingress-controllerはIngressの設定を読み取りALBを作ってくれます。

Ingress用のkube2iamの設定をします。ここの手順をみるのがいいかなと思いますが順に説明します。

最初にeks-ingress-policyという名前でiam-policy.jsonのIAMのポリシーを作成します。

次に、k8s-alb-controllerという名前でIAMのロールを作成します。ロールを使用するサービスはEC2を選びます。ロールの使用するポリシーは先程作ったeks-ingress-policyのポリシーをアタッチします。ロールの信頼関係の編集を選び以下でアップデートします。なおarn:aws:iam::xxx:role/worker-nodes-NodeInstanceRole-yyyの部分はEKSのノードのロールを設定してください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::xxx:role/worker-nodes-NodeInstanceRole-yyy"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

RBACの設定をします。以下をkubectl apply -f rbac-alb-ingress-controller.yamlでKubernetesに反映します。

title=rbac-alb-ingress-controller.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app: alb-ingress-controller
  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:
  labels:
    app: alb-ingress-controller
  name: alb-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: alb-ingress-controller
subjects:
  - kind: ServiceAccount
    name: alb-ingress
    namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: alb-ingress-controller
  name: alb-ingress
  namespace: kube-system

最後にalb-ingress-controllerのDeploymentをkubectl apply -f alb-ingress-controller.yamlで反映します。xxxとなっているところは適宜かえてください。

title=alb-ingress-controller.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: alb-ingress-controller
  name: alb-ingress-controller
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: alb-ingress-controller
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      annotations:
        iam.amazonaws.com/role: arn:aws:iam::xxx:role/k8s-alb-controller
      creationTimestamp: null
      labels:
        app: alb-ingress-controller
    spec:
      containers:
        - args:
            - --ingress-class=alb
            - --cluster-name=xxx
            - --aws-vpc-id=xxx
            - --aws-region=ap-northeast-1
            - --aws-api-debug
          image: docker.io/amazon/aws-alb-ingress-controller:v1.1.0
          imagePullPolicy: Always
          name: server
          resources: {}
          terminationMessagePath: /dev/termination-log
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30
      serviceAccountName: alb-ingress
      serviceAccount: alb-ingress

kubectl get pod -n kube-systemalb-ingress-controllerのポッドが動いているのを確認し、ログにエラーがでていなければ大丈夫です。

Ingressの設定

Ingressの設定し、kubectl apply -f ingress.yamlで反映するとALBを自動でつくってくれます。xxxとなっているサブネットや証明書のARN、ホスト名など適宜かえてください。

title=ingress.yaml
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/subnets: xxx, xxx, xxx
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:xxx:certificate/xxx
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
spec:
  rules:
    - host: frontend.example.com
      http:
        paths:
          - path: /*
            backend:
              serviceName: frontend
              servicePort: 3333
    - host: backend.example.jp
      http:
        paths:
          - path: /*
            backend:
              serviceName: backend
              servicePort: 3000
  backend:
    serviceName: frontend
    servicePort: 3333

各種ドメインでアクセスできるのが確認できたら完了です。適宜Route53などの設定をかえドメインの振り分けをEKS側にしてください。

開発環境でベーシック認証を設定している場合

すべてのパスにベーシック認証がかかっている場合ALBからのヘルスチェックが401となりヘルスチェックが失敗してALBが504 Gateway Time-outをかえしてエラーになるのでベーシック認証がかかっているサービスに以下のようにアノテーションを追加します(/healthはヘルスチェック用のパスです)。

apiVersion: v1
kind: Service
metadata:
  ...
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: "/health"
    alb.ingress.kubernetes.io/success-codes: 200,401

Ingress Annotationsにその他のアノテーションもあるのでもっとカスタマイズしたい場合は参考にするといいと思います。

まとめ

ECSで動いていたサービスをEKSに移行しました。Ingressの設定が少しやっかいですが、一度設定するとYAMLファイルでルーティングできるので非常に楽です。KubernetesにすることでYAMLを書くだけでサービスが展開できコードにものこっているので複数のサービスを運営するのも楽になるのでおすすめです。