AWSの色んなサービスを意図的に使って、サンプルWebアプリを構築してみているのですが、今回は part2 ということで、「EKSでRDSにアクセスするWEB APIサーバを構築」していきたいと思います。

本記事で対応する内容

この記事では、以下をやってみました。

  • API サーバの Docker イメージを Amazon ECR にプッシュする
  • Amazon EKS クラスタを作成する
  • EKS クラスタに Docker コンテナをデプロイする
  • Docker コンテナ(POD)から RDS にアクセスする

作業用 IAM ユーザを作成

IAM ユーザ(sample-admin)を作成します。

  • IAM コンソール > ユーザ > ユーザを追加 から、以下の設定でユーザを作成する
    • ユーザ名: sample-admin
    • プログラムによるアクセス: チェック
    • アクセス許可の設定: なし
    • タグ: なし

最初は何も権限がないですが、後で必要なポリシーをアタッチしていきます。
また、ユーザを作成した際に表示される、aws_access_key_idaws_secret_access_keyは後で使うのでメモおきます。

ECR に Docker イメージをプッシュ

ECR でリポジトリを作成

アプリの Docker イメージをホストするために ECR > リポジトリを作成 から、以下の設定でプライベートリポジトリを作成しました。

  • 可視性設定: プライベート
  • リポジトリ名: sample-api
  • タグのイミュータビリティ: 有効
  • イメージスキャン: 有効
  • KMS 暗号化: 有効
    • 暗号化設定をカスタマイズする: チェックなし

IAM ユーザに ECR 関連の権限を付与

ECRを管理するためのポリシーを作成し、IAM ユーザにアタッチします。

  • IAM コンソール > ポリシー > ポリシーの作成 から、以下の設定でポリシーを作成する
    • アクセス許可
      • サービス: Elastic Container Registry を選択
      • アクション: 全て
      • リソース: arn:aws:ecr:*:xxx:repository/sample-api(先ほど作ったECRのリポジトリ)
    • タグ: なし
    • ポリシー名: manage-ecr-sample-api-policy

Docker イメージの作成

Node.js+Express+TypeScript+MariaDBという構成のシンプルな WEB API サーバで、エンドポイントは以下の二つだけです。

  • POST /comments: コメントを新規登録する
  • GET /comments/:id: 指定されたIDのコメントを取得する

アプリ構築は本筋ではないので、別記事(Node.js with TypeScriptでDBアクセスするWebアプリを作る)にしました。

この API サーバのDockerfileは以下の通りです。

FROM node:14.17.0

WORKDIR /usr/src/app
COPY package.json .
COPY tsconfig.json .
COPY src ./src

RUN npm install
RUN npx tsc

EXPOSE 3000

CMD ["node", "dist/index.js"]

依存関係を解決(npm install)して、TypeScriptをコンパイル(npx tsc)し、Node.jsindex.jsを実行するだけのシンプルな内容です。

このDockerfileをもとに、Docker イメージを作成したいと思います。

# イメージの作成
docker build . -t sample-api:1.0.0

ECR へプッシュ

先ほど作成した、Docker イメージを ECR にプッシュしたいと思います。
手順は 公式ドキュメント に従っています。

まずは、こちら を参考に、ECR レジストリに対して Docker クライアントを認証します。

~/.aws/credentialsに以下のようにプロファイルを追加し、

[sample-admin]
aws_access_key_id = xxxx
aws_secret_access_key = xxxx

以下のコマンドで、ECR レジストリに対して Docker クライアントを認証します。

aws ecr get-login-password --region us-west-2 --profile sample-admin | docker login --username AWS --password-stdin xxxx.dkr.ecr.us-west-2.amazonaws.com

> Login Succeeded

あとは、先ほど作成したイメージにタグをつけて、プッシュするだけです。

# タグを追加
docker tag sample-api:1.0.0 xxxx.dkr.ecr.us-west-2.amazonaws.com/sample-api:1.0.0

# ECRにプッシュ
docker push xxxx.dkr.ecr.us-west-2.amazonaws.com/sample-api:1.0.0

コンソールで確認すると、無事イメージがプッシュされていました。

EKS クラスタを作成

IAM ユーザに EKS クラスタを作成する権限を付与

少しややこしいのですが、登場人物は二人です。

  • コンソールから aws eksコマンドを実行する IAM ユーザ
  • EKS サービスにPassRoleで渡すロール

まずは、EKS サービスに渡すロール IAM ロールを作成します。

  • IAM コンソール > ロール > ロールの作成 から、以下の設定でロールを作成する
    • サービス: EKS
    • ユースケース: EKS - Cluster
      • ポリシー
        • AmazonEKSClusterPolicy
        • AmazonEKSVPCResourceController
    • タグ: なし
    • ロール名: manage-sample-eks-cluster-role

次に、IAM ユーザにアタッチするための「EKS クラスタを作成するポリシー」を作成します。
既存のAmazonEKSClusterPolicyというポリシーはeks:CreateClusterなどの必要な権限を含まないため、新規に作成しました。

  • IAM コンソール > ポリシー > ポリシーの作成 から、以下の設定でポリシーを作成する。多少絞ったつもりですが、過剰に付与している部分もありそうです)
    • JSON
      {
      "Version": "2012-10-17",
      "Statement": [
          {
              "Sid": "VisualEditor0",
              "Effect": "Allow",
              "Action": [
                  "iam:GenerateCredentialReport",
                  "iam:GetContextKeysForCustomPolicy",
                  "iam:GetAccountPasswordPolicy",
                  "iam:SimulateCustomPolicy",
                  "iam:GetServiceLastAccessedDetailsWithEntities",
                  "iam:GetAccountAuthorizationDetails",
                  "iam:GetCredentialReport",
                  "iam:GetServiceLastAccessedDetails",
                  "iam:GetOrganizationsAccessReport",
                  "iam:ListAttachedRolePolicies",
                  "iam:CreateServiceLinkedRole",
                  "iam:ListRoles",
                  "iam:GetRole",
                  "ec2:*"
              ],
              "Resource": "*"
          },
          {
              "Sid": "VisualEditor1",
              "Effect": "Allow",
              "Action": [
                  "eks:ListClusters",
                  "eks:DescribeAddonVersions",
                  "eks:CreateCluster"
              ],
              "Resource": "*"
          },
          {
              "Sid": "VisualEditor2",
              "Effect": "Allow",
              "Action": "eks:*",
              "Resource": [
                  "arn:aws:eks:*:xxx:addon/*/*/*",
                  "arn:aws:eks:*:xxx:identityproviderconfig/*/*/*/*",
                  "arn:aws:eks:*:xxx:fargateprofile/*/*/*",
                  "arn:aws:eks:*:xxx:nodegroup/*/*/*",
                  "arn:aws:eks:*:xxx:cluster/*"
              ]
          }
      ]
      }
    • ポリシー名: manage-eks-policy
    • タグ: なし

また、IAM ユーザがPassRoleできるようにするためのポリシーを作成します。

  • IAM コンソール > ポリシー > ポリシーの作成 から、以下の設定でポリシーを作成する
    • JSON
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Sid": "VisualEditor0",
                  "Effect": "Allow",
                  "Action": "iam:PassRole",
                  "Resource": "arn:aws:iam::xxx:role/manage-sample-eks-cluster-role"
              }
          ]
      }
    • タグ: なし
    • ポリシー名: pass-manage-sample-eks-cluster-role-policy

最後に、IAM ユーザ(sample-admin)には、今作った二つのポリシーをアタッチします。

  • manage-eks-policy
  • pass-manage-sample-eks-cluster-role-policy

これで IAM ユーザへの権限付与は完了しました。

public サブネットを二つ作成

一つのサブネットで EKS クラスタを作成しようとすると以下のエラーが発生します。

An error occurred (InvalidParameterException) when calling the CreateCluster operation: Subnets specified must be in at least two different AZs

異なる AZ に所属するサブネットが必要みたいです。なので、public サブネットを二つ作成したいと思います。
part1 の記事 で VPC と public サブネットを一つ作成済みなので、その続きでもう一つ作成します。

  • VPC コンソール > サブネット > サブネットを作成 から、以下の設定でサブネットを作成する
    • VPC ID: part1 の記事で作った VPC ID(sample-vpc
    • サブネット名: sample-public-subnet2
    • AZ: us-west-2b(一つ目と異なるAZ)
    • IPv4 CIDR ブロック: 10.0.3.0/24(一つ目と異なる範囲)
  • 作成したサブネットを開く > アクション > 自動割り当てIP設定の変更 > パブリック IPv4 アドレスを自動割り当て にチェック

EKS クラスタを作成

前準備がとても大変でしたが、やっと EKS クラスタを作成する準備ができました。
公式ドキュメントには三つの方法が紹介されています。
いずれ、CodePipeline で実行することを考え、CLIが良いのですが、eksctlAWS CLIのどちらを使うかは正直迷います。
ググっていると、もともとサードパーティ製のeksctlがメインだったところに、AWS CLI側が同等の機能を追加提供して追いついてきたという経緯があるようなので、公式が出しているツールであるAWS CLIを使ってみようと思います。

AWS CLIのインストールは、こちら に従って実施します。

クラスタの作成コマンド自体は簡単で、以下の通りです。
--resources-vpc-configで設定しているサブネットは先ほど作成した public サブネットを指定しています。また、セキュリティグループは、part1の記事 で作成したパブリックアクセス用のセキュリティグループで、RDS 用のセキュリティグループに対してアクセスできる権限を持たせてあります。

aws eks create-cluster \
   --region us-west-2 \
   --name sample-eks-cluster \
   --kubernetes-version 1.20 \
   --role-arn arn:aws:iam::xxx:role/manage-sample-eks-cluster-role \
   --resources-vpc-config subnetIds=subnet-xxx,subnet-xxx,securityGroupIds=sg-xxx \
   --profile sample-admin

> {
>     "cluster": {
>         "name": "sample-eks-cluster",
>         "arn": "arn:aws:eks:us-west-2:xxx:cluster/sample-eks-cluster",
>         "createdAt": "2021-07-04T12:20:16.524000+09:00",
>         "version": "1.20",
>         "roleArn": "arn:aws:iam::xxx:role/manage-sample-eks-cluster-role",
>         "resourcesVpcConfig": {
>             "subnetIds": [
>                 "subnet-xxx",
>                 "subnet-xxx"
>             ],
>             "securityGroupIds": [
>                 "sg-xxx"
>             ],
>             "vpcId": "vpc-xxx",
>             "endpointPublicAccess": true,
>             "endpointPrivateAccess": false,
>             "publicAccessCidrs": [
>                 "0.0.0.0/0"
>             ]
>         },
>         "kubernetesNetworkConfig": {
>             "serviceIpv4Cidr": "172.xx.xx.xx/16"
>         },
>         "logging": {
>             "clusterLogging": [
>                 {
>                     "types": [
>                         "api",
>                         "audit",
>                         "authenticator",
>                         "controllerManager",
>                         "scheduler"
>                     ],
>                     "enabled": false
>                 }
>             ]
>         },
>         "status": "CREATING",
>         "certificateAuthority": {},
>         "platformVersion": "eks.1",
>         "tags": {}
>     }
> }

以下のコマンドで確認できます。
10分程度経過すると、無事クラスタが作成されました。

aws eks describe-cluster \
  --query "cluster.status" \
  --name sample-eks-cluster \
  --region us-west-2 \
  --profile sample-admin

> "ACTIVE"

EKS ノードグループを作成

現状、クラスタの中身は空っぽなので、ノードグループを作成したいと思います。

ノード用の IAM ロールを作成

まず、こちらに従って、 IAM ロールを作成します。

  • IAM コンソール > ロール > ロールの作成 から、以下の設定でロールを作成する
    • サービス: EC2
    • ユースケース: EC2
    • ポリシー
      • AmazonEKSWorkerNodePolicy
      • AmazonEC2ContainerRegistryReadOnly
      • AmazonEKS_CNI_Policy
    • タグ: なし
    • ロール名: node-instance-role

次に、IAM ユーザが上記のロールをPassRoleできるようにするための IAM ポリシーを作成します。

  • IAM コンソール > ポリシー > ポリシーの作成 から、以下の設定でポリシーを作成する
    • JSON
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Sid": "VisualEditor0",
                  "Effect": "Allow",
                  "Action": "iam:PassRole",
                  "Resource": "arn:aws:iam::xxx:role/node-instance-role"
              }
          ]
      }
    • タグ: なし
    • ポリシー名: pass-node-instance-role-policy

最後に、作成したポリシーを IAM ユーザ(sample-admin)にアタッチします。

キーペアの作成

公式ドキュメントを参考に、SSH キーペアを事前に作成します。

  • EC2 コンソール > キーペア > キーペアを作成 から、以下の設定でキーペアを作成
    • 名前: sample-eks-node-group-key
    • Private key file format: ppm

ノードグループを作成

ノードグループの作成は、以下のコマンドで実行できます。
オプションの意味は公式ドキュメントを見るとわかると思います。

  • POD にセキュリティグループをアタッチできるようにするために、考慮事項に書いてあるインスタンスタイプ(m5.large)を選択
  • EC2 にアクセスするためのec2SshKeyに先ほど作成したキーペアを指定
  • サブネットにはコントロールプレーンに割り当てたサブネットを指定(本当は良くないんだろうけど)
    aws eks create-nodegroup \
    --cluster-name sample-eks-cluster \
    --nodegroup-name sample-eks-node-group \
    --scaling-config minSize=1,maxSize=2,desiredSize=1 \
    --disk-size 20 \
    --subnets subnet-xxx subnet-xxx \
    --instance-types m5.large \
    --ami-type AL2_x86_64 \
    --remote-access ec2SshKey=sample-eks-node-group-key \
    --node-role arn:aws:iam::xxx:role/node-instance-role \
    --region us-west-2 \
    --profile sample-admin
    > {
    >     "nodegroup": {
    >         "nodegroupName": "sample-eks-node-group",
    >         "nodegroupArn": "arn:aws:eks:us-west-2:xxx:nodegroup/sample-eks-cluster/sample-eks-node-group/xxx-xxx-xxx-xxx-xxx",
    >         "clusterName": "sample-eks-cluster",
    >         "version": "1.20",
    >         "releaseVersion": "1.20.4-20210628",
    >         "createdAt": "2021-07-04T20:05:59.695000+09:00",
    >         "modifiedAt": "2021-07-04T20:05:59.695000+09:00",
    >         "status": "CREATING",
    >         "capacityType": "ON_DEMAND",
    >         "scalingConfig": {
    >             "minSize": 1,
    >             "maxSize": 2,
    >             "desiredSize": 1
    >         },
    >         "instanceTypes": [
    >             "m5.large"
    >         ],
    >         "subnets": [
    >             "subnet-xxx",
    >             "subnet-xxx"
    >         ],
    >         "remoteAccess": {
    >             "ec2SshKey": "sample-eks-node-group-key"
    >         },
    >         "amiType": "AL2_x86_64",
    >         "nodeRole": "arn:aws:iam::xxx:role/node-instance-role",
    >         "diskSize": 20,
    >         "health": {
    >             "issues": []
    >         },
    >         "tags": {}
    >     }
    > }

数分待つと、ノードグループが作成されました。

aws eks describe-nodegroup \
  --cluster-name sample-eks-cluster \
  --nodegroup-name sample-eks-node-group \
  --region us-west-2 \
  --profile sample-admin

> {
>     "nodegroup": {
>         "nodegroupName": "sample-eks-node-group",
>         "clusterName": "sample-eks-cluster",
>         "status": "ACTIVE",
>     }
> }

アプリケーションコンテナのデプロイ

コンテナデプロイには、AWS CLIではなくkubectlを利用する必要があるようです。

kubectl をインストール

公式ドキュメントに従って、kubectlをインストールします。

作成したクラスタにkubectlでアクセスするための、kubeconfigファイルを作成します。既存の場合は、新しいクラスタ用の定義を追加してくれます。

aws eks --region us-west-2 update-kubeconfig --name sample-eks-cluster --profile sample-admin
> Added new context arn:aws:eks:us-west-2:xxx:cluster/sample-eks-cluster to /Users/xxx/.kube/config

無事、設定できたようです。

kubectl get svc
> NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
> kubernetes   ClusterIP   172.20.0.1   <none>        443/TCP   7m

POD から RDS にアクセス許可

POD(内のコンテナ) から RDS にアクセスできるようにするためには、POD に専用のセキュリティグループをアタッチして、RDS 側のセキュリティグループのインバウンド設定で、POD 側のセキュリティグループからのMYSQL/Aurora: TCP(3306)アクセスを許可する必要があります。

  • EC2 コンソール > セキュリティグループ > RDS 側のセキュリティグループを選択 > インバウンドルール > Edit inbound rules
    • タイプ: MySQL/Aurora(TCP:3306)
    • ソース: POD側のセキュリティグループ
      • partの記事で作成したsample-public-securitygroupを利用することにしました。

POD にセキュリティグループをアタッチするポリシーを設定

こちら を参考に amazon-k8s-cni のバージョンを1.7.7以上にあげます。

# バージョン確認(事前)
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
> amazon-k8s-cni-init:v1.7.5-eksbuild.1
> amazon-k8s-cni:v1.7.5-eksbuild.1
# インストーラダウンロード
curl -o aws-k8s-cni.yaml https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/release-1.7/config/v1.7/aws-k8s-cni.yaml
# 適用
kubectl apply -f aws-k8s-cni.yaml
# バージョン確認(事後)
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
> amazon-k8s-cni-init:v1.7.10
> amazon-k8s-cni:v1.7.10

POD の ENI を有効にします。

kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true

以下のコマンドで、クラスタ作成時に自動作成されたセキュリティグループのIDを取得します。

aws eks describe-cluster \
  --name sample-eks-cluster \
  --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" \
  --output text \
  --region us-west-2 \
  --profile sample-admin
> sg-xxx

api-sg-policy.ymlファイルを作成し、自分で作ったPOD用のセキュリティグループと先ほど取得した自動生成されたセキュリティグループを指定します。

apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
  name: sample-api-sg-policy
spec:
  podSelector:
    matchLabels:
      app: sample-api-pod
  securityGroups:
    groupIds:
      - sg-xxx # 自分でPOD用に作ったセキュリティグループ
      - sg-xxx # クラスタ作成時に自動生成されたセキュリティグループ

最後に、適用します。

kubectl apply -f ./api/api-sg-policy.yml
> securitygrouppolicy.vpcresources.k8s.aws/sample-api-sg-policy created

これで、PODを作成すると、二つのセキュリティグループがアタッチされるようになります。

POD をデプロイ

api-deployment.ymlを作成します。
ポイントは、以下だと思います。

  • template で Pod のテンプレートを定義している
  • Pod は3000ポートを露出する
  • 利用するイメージは ECR から取ってくる
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample-api-deployment
    spec:
    replicas: 1 # お金がかかるので
    selector:
    matchLabels:
      app: sample-api-pod
    template:
    metadata:
      labels:
        app: sample-api-pod
    spec:
      containers:
        - name: sample-api-container
          image: xxx.dkr.ecr.us-west-2.amazonaws.com/sample-api:1.0.0
          env:
            - name: DB_HOST
              value: sample-database.xxx.us-west-2.rds.amazonaws.com
            - name: DB_USER
              value: admin
            - name: DB_PASSWORD
              value: xxx
            - name: DB_DATABASE
              value: sampledb
          ports:
            - protocol: TCP
              containerPort: 3000

上記のファイルを適用します。

kubectl apply -f ./api/api-deployment.yml
> deployment.apps/sample-api-deployment created

POD の状態を確認したいときは、以下を実行します。

# 一覧確認
kubectl get pods -o wide

# 起動ログ確認
kubectl describe pods

複数の POD を一つにまとめて外部に公開

api-service.ymlを作成し、複数の POD をネットワーク的に一つに取りまとめ、ELBで外部に公開したいと思います。
POD の3000ポートを80ポートで公開するように設定しました。ここ に詳しく書いてあります。

apiVersion: v1
kind: Service
metadata:
  name: sample-api-service
spec:
  type: LoadBalancer
  selector:
    app: sample-api-pod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000

ファイルを適用します。

kubectl apply -f ./api/api-service.yml

以下でエンドポイントを確認します。

# エンドポイント確認
kubectl get service sample-api-service
> NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)        AGE
> sample-api-service   LoadBalancer   172.xx.xx.xx   xxx-xxx.us-west-2.elb.amazonaws.com   80:30613/TCP   7s

# 起動ログ確認
kubectl describe services

動作確認

最後に、公開されたAPIにアクセスして、無事動いてることを確認したいと思います。
先ほど確認したエンドポイントをcURLで叩いてみます。

curl -X POST http://xxx-xxx.us-west-2.elb.amazonaws.com/comments -H "Content-type: application/json" -d '{ "comment" : "new comment" }'
{"id":3,"comment":"new comment"}

curl http://xxx-xxx.us-west-2.elb.amazonaws.com/comments/3
{"id":3,"comment":"new comment"}

無事、APIを叩くことができました。

お掃除

検証目的だった場合はちゃんと最後にノードグループとクラスタを削除しておきましょう。
私は、一週間ほど放置していたら、16.42USDほど請求されてしまいました(涙)

# POD/サービスの削除
kubectl delete -f ./api/api-sg-policy.yml
kubectl delete -f ./api/api-deployment.yml
kubectl delete -f ./api/api-service.yml

# ノードグループの削除
aws eks delete-nodegroup \
  --nodegroup-name sample-eks-node-group \
  --cluster-name sample-eks-cluster \
  --region us-west-2 \
  --profile sample-admin

# クラスターの削除
aws eks delete-cluster \
  --name sample-eks-cluster \
  --region us-west-2 \
  --profile sample-admin

トラブルシューティング

IAM ユーザが IAM ロールにPassRoleできない

  • エラーメッセージ
    error occurred (AccessDeniedException) when calling the CreateCluster operation: User: arn:aws:iam::xxx:user/sample-admin is not authorized to perform: iam:PassRole on resource: arn:aws:iam::xxx:role/sample-eks-role
  • 対処
    • PassRoleするための IAM ポリシーを作成して、IAM ユーザにアタッチ

ノードグループ作成時に自動的に IP アドレスがアサインできない

  • エラーメッセージ
    One or more Amazon EC2 Subnets of [subnet-xxx, subnet-xxx] for node group sample-eks-node-group does not automatically assign public IP addresses to instances launched into it. If you want your instances to be assigned a public IP address, then you need to enable auto-assign public IP address for the subnet. See IP addressing in VPC guide: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-ip-addressing.html#subnet-public-ip
  • 対処
    • こちら の通り対応します
    • VPC コンソール > サブネット > 対象サブネットを選択 > アクション > 自動割り当てIP設定の変更 > パブリック IPv4 アドレスを自動割り当て にチェック

クラスタを作成できない

  • エラーメッセージ
    An error occurred (InvalidParameterException) when calling the CreateCluster operation: The provided role doesn't have the Amazon EKS Managed Policies associated with it.
    Please ensure the following policies [arn:aws:iam::aws:policy/AmazonEKSClusterPolicy] are attached
  • 対処
    • クラスタマネジメント用の IAM ロールにAmazonEKSClusterPolicyポリシーをアタッチする
      • IAM ユーザにアタッチして PassRole すればいいのかなと思ったのですが、だめでした

ノードグループ作成時にNodeCreationFailureエラー

  • エラーメッセージ
    Unhealthy nodes in the kubernetes cluster
  • 対処
    • こちら を参考にワーカーノード用のロール(node-instance-role)にAmazonEKS_CNI_Policyをアタッチ

kubectl apply でエラー

  • エラーメッセージ
    The Pod "sample-api-pod" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)
  • 対処
    • 一旦、既存の pod を削除する
      kubectl delete pod sample-api-pod

kubectl get svc でエラー

  • エラーメッセージ
    Unable to connect to the server: dial tcp: lookup xxx.gr7.us-west-2.eks.amazonaws.com on xx.xx.xx.xx:xx: no such host
  • 原因
    • クラスタを作り直したのに、kubeconfigを更新し忘れていたため
  • 対処
    • 以下を再実行する
      aws eks update-kubeconfig \
        --region us-west-2 \
        --name sample-eks-cluster \
        --profile sample-admin

アプリのコンテナから RDS が見えない

  • エラーメッセージ
    SqlError: (conn=-1, no: 45012, SQLState: 08S01) Connection timeout: failed to create socket after 10006ms
        at Object.module.exports.createError (/usr/src/app/node_modules/mariadb/lib/misc/errors.js:55:10)
        at Timeout._connectTimeoutReached [as _onTimeout] (/usr/src/app/node_modules/mariadb/lib/connection.js:1139:14)
        at listOnTimeout (internal/timers.js:557:17)
        at processTimers (internal/timers.js:498:7)
  • 原因
    • POD 側のセキュリティグループが、 RDS 側のセキュリティグループにアクセスする権限がないため
  • 対処
    • POD 用のセキュリティグループを作成し、RDS 側のセキュリティグループのインバウンド設定で、そこからのMYSQL/Aurora: TCP(3306)アクセスを許可する
    • POD に対してセキュリティグループをアタッチする。この際、二種類のセキュリティグループをアタッチする必要があったので要注意です。
      • 一種類目:作成したPOD 用のセキュリティグループ
      • 二種類目:クラスタ作成時に自動生成されるセキュリティグループ

POD にセキュリティグループをアタッチしようとするとエラーが出る

  • エラーメッセージ
    Insufficient vpc.amazonaws.com/pod-eni.
  • 原因
    • こちらの考慮事項のいずれかに違反している
  • 対処
    • 元々、クラスタ作成時のインスタンスのタイプにt3.mediumを指定してたがt3系は対象外だったので、m5.largeに変更
    • クラスターを操作するロール(manage-sample-eks-cluster-role)に対して、AmazonEKSVPCResourceControllerポリシーをアタッチする。ノード用のロールではないことに注意

POD 作成時にワーニングが出る

  • ワーニングメッセージ
    sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "xxx" network for pod "sample-api-deployment-xxx-bthc2": networkPlugin cni failed to set up pod "sample-api-deployment-xxx-bthc2_default" network: add cmd: Error received from AddNetwork gRPC call: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connect: connection refused", failed to clean up sandbox container "xxx" network for pod "sample-api-deployment-xxx-bthc2": networkPlugin cni failed to teardown pod "sample-api-deployment-xxx-bthc2_default" network: del cmd: error received from DelNetwork gRPC call: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connect: connection refused"]
  • 原因
    • こちら や こちら に書いてあるように、amazon-k8s-cni のバージョンが古いため
  • 対処
    • amazon-k8s-cni のバージョンを1.7.7以上にあげる(本文に詳細記載)
    • なお、このワーニングが出ていても、PODは起動できており、問題なく動いてたので、あまり気にしなくてもいいかもしれません。

最後に

今回なるべく一つ一つ、自分で設定しながらEKSの環境を構築してみようと思ったのですが、一歩進むたびに次のエラーが発生して非常に大変でした。あと、Consoleで色々やってる部分は、AWS CloudFormation なりでコード化しないと手作業ではやってられないレベルですね。EKS を使いこなしている皆さんはすごいなーと思いました。

正直なところ、インフラに関してはフルマネージドなサービスを活用して、アプリの開発に専念したいので、スケーラブルな環境で Docker コンテナを動かしたいだけなら、ECS + Fragete で十分な気がしました。

Posted in: aws