14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

今更ですがEKSを触り始めました。いざ作ってみると考えることが多かったので、記事にしました。
それと最近IAM Identity Centerユーザーを使い始めたので、AWS CLIを使うためのトークンの発行方法など調べました。

作成するAWSリソース概要

後述しますが、以下に今回作成したtfファイルを配置しています。

Terraformで作る主なAWSリソースは以下の通り

terraform resource.name リソース種類 備考
aws_eks_cluster.cluster EKSクラスタ エンドポイントはプライベートとしています(endpoint_private_access = true,endpoint_public_access = false)。セキュリティグループにて追加設定しています(※1)
aws_eks_node_group.node_group ノードグループ コンテナを動かすノードになります。
aws_instance.kubectl_instance kubectlを実行するEC2 SSH接続に接続します。
aws_ec2_instance_connect_endpoint.eice EC2 Instance Connect Endpoint(EICE) kubectl_instanceにSSH接続するために使います(※2)
aws_iam_role.kubectl_role kubectl_instanceのIAMロール kubectl_instanceに紐づけます。
aws_iam_policy.kubectl_policy kubectl_instanceのIAMポリシー sts:GetCallerIdentityだけkubectl_roleに付けています。(※3)

※1
EKSクラスタのエンドポイントにローカルVPCからアクセスできるよう、追加設定します。プライベートアクセスでは、VPCのプライベートDNSを使って、443ポートを使っています。

resource "aws_security_group" "cluster-sg" {
  name   = "eks-cluster-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr_block]
  }
}

デフォルトで必要なセキュリティグループは作成されるので、上記のセキュリティグループではegressの許可は入れていません。

※2
最近、EICEを知ったので使ってみることにしました。
以下のドキュメントにある通り、AWS CLIからSSHキーを気にせず接続できるようになります。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-methods.html#connect-linux-inst-eic-cli-ssh
Terraform内でEC2に接続するには、60秒の期限付きでSSHキーを登録して接続します。登録するSSHキーはあらかじめ作成しておく必要があります。

※3
いつもコンソールで使っているIAMユーザー権限では強すぎたり、使いにくいので新しく作ることにしました。
以下のドキュメントより、kubectlを実行するためにsts:GetCallerIdentity権限が必要になります。
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/cluster-auth.html より引用)
image.png

後述しますが、出来上がったクラスタで試しにPODを作ってみます。

アクセス制限について確認しておく

作成前にアクセス制限がどうなっているか確認します。

  • EKSクラスタへのアクセス制限
    プライベートアクセスにしているので、同一VPCからアクセスできるようにします。セキュリティグループは以下のようにしています。
方向 アクセス ソース 送信先
インバウンド 全プロトコル、全ポート クラスタセキュリティグループ -
インバウンド HTTPS,443ポート VPCのCIDR(10.0.0.0/16) -
アウトバウンド 全プロトコル、全ポート - 0.0.0.0/0
  • ノードへのアクセス制限
    今回はコストの都合上、パブリックサブネットにノードプールを作っています。
方向 アクセス ソース 送信先
インバウンド 全プロトコル、全ポート クラスタセキュリティグループ -
アウトバウンド 全プロトコル、全ポート - 0.0.0.0/0
  • kubectlを実行するEC2へのアクセス制限
    SSH接続用に許可します。
方向 アクセス ソース 送信先
インバウンド TCP,22ポート ローカルのパブリックIPアドレス -
アウトバウンド 全プロトコル、全ポート - 0.0.0.0/0

デフォルトの制限を使っている部分もありますが、外部からのアクセスは制限しているようです。

kubectlからEKSの接続方法について

今回はEC2にkubectlをインストールしてEKSに接続することがゴールになります。
接続にはkubeconfigファイルが必要であり、AWS CLIで作成しています。

$ aws eks update-kubeconfig --name <クラスタ名>

また、IAMとKubernetesのRBACの認証が必要になります。
以下のドキュメントのようにnamespace「kube-system」にあるConfigMap「aws-auth」を編集する必要があります。
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/add-user-role.html

[ec2-user@ip-10-0-10-109 ~]$ kubectl get cm -n kube-system aws-auth
NAME       DATA   AGE
aws-auth   2      43m

今回は以下のeksctlコマンドで編集します。

eksctl create iamidentitymapping --cluster <クラスタ名> --region <リージョン名> --arn <aws_iam_role.kubectl_roleのARN> --username "admin" --group "system:masters" --no-duplicate-arns

各項目は以下のようにしました。

  • group: system:masters
    RoleBindingClusterRoleBindingで指定したグループの名前です。今回は勉強用のためコントロールプレーンと同等のsystem:mastersを使いました。
  • usernamme: admin
    任意の名前です。
  • arn: IAMロールaws_iam_role.kubectl_roleのARN
    マッピングするIAMロールのARNです。

今回はコンソールで使っているIAMユーザーでは権限が強すぎるため、別IAMロールを用意しています。ただ、aws-auth編集時にはまだマッピングされていないため、IAM Identity CenterユーザーのSSOで発行された以下のトークンを使用しています。

export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."

上記のexportコマンドはコンソール上から確認できます。(Command line or programmatic accessより)

image.png

※実際のコマンドは以下のshellファイルになります。
https://github.com/kohei39san/mystudy-handson/blob/main/scripts/connect-eks-for-remote-ec2.sh

EKS構築の実践

前提

  • IAM Identity Centerユーザーが作成済みであること。
    最近、セキュリティ強化のためにIAMユーザーからIAM Identity CenterユーザーのSSOに切り替えました。IAM Identity Centerユーザーで認証を行う前提で進めていきます。
  • WSLでTerraformを実行しています。

AWS CLIのアップデート

Terraformの実行前に、AWS CLI v1→v2にバージョンアップしました。
AWS CLI v1ではEICEやSSOを使えないためです。

以下のようなコマンドでアップデートしました。

sudo yum remove awscli -y
sudo rm -rf /usr/local/aws-cli/
cd /tmp
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli

Terraform実行

Terraformの前にAWS CLIを実行するためのSSOを行うため、以下のconfig,shellファイルを作成します。

~/.aws/config
[profile sso-profile]
sso_start_url = #sso_start_url
sso_region = #sso_region
sso_account_id = #sso_account_id
sso_role_name = #sso_role_name
region = #region
output = json
aws-cli-source.sh
export AWS_PROFILE=sso-profile
aws sso logout
aws sso login
# たまに期限切れのトークンが残っていることがあったのでlogoutして消しています。

SSOします。

$ source aws-cli-source.sh
# 有効期限付きのトークンが発行されます。

では、Terraformを実行します。

$ cd mystudy-handson/tf-manifests/eks/
$ terraform init
$ terraform apply

私の環境ではapplyが終わるまで15分弱かかりました。。。
クラスタとノードプール作成が合計で10分以上かかって長いようでした。(まあクラスタ作るんだしそのくらいはかかるよなーといった感想です)

それではEICEでSSH接続します。キーを気にしなくてよいので少し楽ですね。

aws ec2-instance-connect ssh --instance-id <接続するEC2のID> --connection-type eice

ノードの様子を見てみます。

[ec2-user@ip-10-0-10-109 ~]$ k get node
NAME                                            STATUS   ROLES    AGE   VERSION
ip-10-0-0-101.ap-northeast-1.compute.internal   Ready    <none>   26m   v1.27.7-eks-e71965b
ip-10-0-1-77.ap-northeast-1.compute.internal    Ready    <none>   26m   v1.27.7-eks-e71965b
[ec2-user@ip-10-0-10-109 ~]$ k describe node ip-10-0-0-101.ap-northeast-1.compute.internal
...
Capacity:
  cpu:                1
  ephemeral-storage:  20959212Ki
  hugepages-2Mi:      0
  memory:             975592Ki
  pods:               4
Allocatable:
  cpu:                940m
  ephemeral-storage:  18242267924
  hugepages-2Mi:      0
  memory:             567016Ki
  pods:               4
...

ノードのインスタンスタイプはt2.microで作っているため、1台当たり1CPU,1GBで4PODしか立てられません(うち3台はすでに使われています)。。。

t2.microではノード1台につき、PODがあと1台しか立てられないということですね。
立ててみます。

[ec2-user@ip-10-0-10-109 ~]$ k run nginx --image nginx
pod/nginx created
[ec2-user@ip-10-0-10-109 ~]$ k get po
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          16s
[ec2-user@ip-10-0-10-109 ~]$ k exec nginx -- curl localhost -sI
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Mon, 20 Nov 2023 13:06:31 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 24 Oct 2023 13:46:47 GMT
Connection: keep-alive
ETag: "6537cac7-267"
Accept-Ranges: bytes

できました!

苦労した点

ConfigMap「aws-auth」が作成されるまで時間がかかる

クラスタが作成完了しても、1,2分ConfigMap「aws-auth」が作成されませんでした。何が進行中なのかわからず、仕方ないのでIAMプリンシパル追加前に以下のsleepを設けることにしました。

while [ `echo -n $(eksctl get iamidentitymapping --cluster "${CLUSTER}" --region ${REGION} -o json | jq -r '.[0].username')` = "null" ];do
  echo "waiting aws-auth mapping ..."
  sleep 10
done

全てのアベイラビリティーゾーン(AZ)がEKSに使えるわけではない

私の勉強不足ですが、AZにも種類があるんですね。。。

$ aws ec2 describe-availability-zones --region ap-northeast-1 | jq -r '.AvailabilityZones[].ZoneType' | sort | uniq -c
      3 availability-zone
      2 wavelength-zone

AWS Wavelengthはモバイルネットワークに使われるものということでした。

TerraformではフィルタリングしてEKSで使えるもののみ使うようにしています。

data "aws_availability_zones" "available" {
  filter {
    name   = "zone-type"
    values = ["availability-zone"]
  }
}

EICEのキー登録は60秒で期限切れになってしまう

以下のドキュメントにあるようにEICEで登録したキーは60秒で期限切れになります。
そのため、長い処理は実行が厳しくなっています。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-methods.html#ec2-instance-connect-connecting-aws-cli

ただ、試したところ強制的に切断されるわけではないので、1つのSSHコマンドで完結するようにしました。

ssh -i ${PRIVATE_KEY} ${SSH_USER}@${INSTANCE_IP} /bin/bash -e << 'EOF'
command 1...
command 2...
command 3...
EOF
# 'EOF'とすることで変数展開を防いでいます。結果、SSH接続先で展開されるようになります。

おわりに

今回はEKSを作りました。作っただけなので、これから使っていきたいです。
最近はコンテナセキュリティ周りを学んでいるので、EKSではどうなっているか調べるのも面白そうです。
これからも勉強します。

14
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?