はじめに
今更ですが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 より引用)
後述しますが、出来上がったクラスタで試しに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
RoleBinding
やClusterRoleBinding
で指定したグループの名前です。今回は勉強用のためコントロールプレーンと同等の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より)
※実際のコマンドは以下の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ファイルを作成します。
[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
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ではどうなっているか調べるのも面白そうです。
これからも勉強します。