EKS構築メモ

f:id:cloudfish:20200116173700p:plain:w300
Amazon EKSを触り始めて4ヶ月ほど経ちました。これまでの構築の経験を踏まえてハマった点やこうした方がいいなどの気づいた点や参考になったブログなど、あまりまとまった内容ではありませんがメモとしてまとめておきたいと思います。

VPCのCIDR設計

  EKSのPodのネットワークについては、VPCのネットワークと同じになるネットワーク方式を採用していることから、Podの数だけIPが必要となります。一般的に/24でサブネットを切ることも多いと思いますが、利用可能なIP数は251となるため、システムによってはかなり少ないIPとなりますので、構築するシステムに合わせて余裕を持ったCIDRを設計しておく必要があります。詳細は以下のブログにまとめていますので参照ください。
cloudfish.hatenablog.com

インスタンスタイプの考慮

EC2についても、インスタンスタイプによってEC2にアタッチできるENIの数とENIあたりに割り当て可能なIP数が異なってくるため、どのくらいのPod数が必要になるかある程度見込んだ上で、インスタンスタイプとノード数を決定する必要があります。詳細は上記のブログで説明してます。

EKSクラスタの管理者権限

デフォルトでは、クラスタの管理者権限については、クラスタを作成したIAMユーザー(or ロール)が保持しています。そのため、気づかずに別のユーザーから利用しようとした場合、kubectlコマンドが権限不足でエラーとなります。別のIAMユーザーから利用したい場合はEKSに対してユーザー追加を行う必要がありますので、以下を参照してください。configmapのaws-authを見ても初期ユーザーは登録されていないのですがこれはどこで管理されてるんでしょうね。
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/add-user-role.html

EC2のロールによるPodのアクセス制御

 サービスアカウントを付与しないPodについてはデフォルトでEC2のロールを通してIAMのアクセス権限を取得します。当初はPodごとにアクセス権限を付与できませんでしたが2019/09のアップデートでPodごとにアクセス権限を付与することが可能になりました。
 これは試してみて分かったのですが、PodにはEC2のロールによるアクセス権限かPod(サービスアカウント)に割り当てたアクセス権限のどちらかしか利用できません。ORの関係ではないので注意が必要です。例えばEC2ロールでS3のアクセス権限があり、PodのサービスアカウントでDynamoDBへのアクセスを許可してPodにサービスアカウントを割り当てた場合、S3へのアクセスはできません。
 基本的にはPodごとにアクセス権限を付与することが望ましいと思いますが、そうできない場合は、全ての権限制御をEC2のロールに寄せるなどした方がいいと思います。中途半端に対応すると作業ミスに繋がるので気をつけてください。

Pod(サービスアカウント)ごとのIAMアクセス制御

当初、他のPodと同じアクセス権限が必要だったのでそれ用のポリシーを作ってそれぞれのPodのサービスアカウントに割り当てていました。
以下のように冗長化を避けるためポリシーを共通化しました。
f:id:cloudfish:20200227165756p:plain:w400

当然のことながら、共通化すると変更時にPod_Aの権限のみ変更したいにも関わらず、Pod_Bにも影響が及ぶことになります。
このため、変更の際の影響範囲や変更ポイントを考慮すると、多少冗長にはなりますが、Pod(=ServiceAccount)とポリシーは1対1で作成した方がいいと思いました。
f:id:cloudfish:20200227170919p:plain:w400

dev.classmethod.jp

ワーカーノードの設定変更

EKSのワーカーノードについては、基本的にイミュータブルとなります。そのため、ノードの設定変更については、ノードグループの再作成を行う必要があります。以前、安易に手作業でノードのセキュリティグループを変更したところ、Podの通信が不安定になったことがありました。原因は手作業で変更したことによるものでした。基本的にはワーカーノードの設定変更はノードグループの再作成を行ってください。Podの通信が不安定になった事象の詳細は以下に掲載しています。
cloudfish.hatenablog.com

リソース制限

Podのリソース(CPU、メモリ)制限を行わないと、Podは必要なだけリソースを使用することになります。当初、あまりこのあたりを気にせずに運用していたところ、ワーカーノードが頻繁にNot Readyとなる問題発生しました。直接的な原因はEBSのDiskのReadが高騰したことになりますが、Readが高騰する原因はノードのメモリの使用率が原因でした。(詳細は以下のブログに書いています。)
cloudfish.hatenablog.com

これに対処するにはkube-reservedとsystem-reservedを設定し、kubernetesの動作に必要なリソースやOSの動作に必要となるリソースを予め確保しておくことでPodに割り当てられるリソースを制限します。設定の詳細については以下が参考になります。
qiita.com
kubernetes.io
eksctl.io

eksctlのハマりポイント

cluster.yamlで設定値が小文字だと認識されないしエラーも出ない

cluster.yamlの設定値はキャメルケースとなっていますが、例えば、「volumeSize」を「volumesize」として表記しeksctlを実行した場合、現状ではエラーも出ずに指定した値で設定されないようなので気をつける必要があります。

クラスター作成時にPolicy指定でAmazonEC2ContainerRegistryReadOnlyは不要

AmazonEC2ContainerRegistryReadOnlyは必ず必要となるポリシーのため、eksctl側で付与されているようです。
そのため、作成時に指定すると以下のようなエラーが出ますので気をつけてください。

[✖] AWS::IAM::Role/NodeInstanceRole: CREATE_FAILED – "Property   ManagedPolicyArns contains duplicate values."

パッと見エラー内容だけでは自分が指定したポリシー内で重複しているわけではなったので原因がすぐには分かりませんでしたが、デフォルトで付与されていますので設定は不要になります。

eksctlのノード追加、削除時のエラー

eksctlでノードグループの追加もしくは削除する際に、以下のようなエラーが出る場合があります。

 getting nodegroup stack summaries: failed to find a nodegroup tag (alpha.eksctl.io/nodegroup-name)

これは作成時のeksctlのバージョンが古い場合で、新しいeksctlで作成した場合に起こります。デバッグログで確認してみると分かるのですが、エラー内容としてはノードグループのCloudFormationに指定されたタグが無いためノードグループが取得できずにエラーになっています。どのバージョンからか分かりませんが、付与されるタグは以下のようになっていました。

 旧:eksctl.io/v1alpha2/nodegroup-name
 新:alpha.eksctl.io/nodegroup-name

対処方法について以下の2通りがあります。

作成時点のeksctlのバージョンで作成する。

Macの場合は以下のコマンドで古いeksctlが取得できます。($versionに取得したいバージョンを設定する必要があります。)

curl --silent --location "https://github.com/weaveworks/eksctl/releases/download/$version/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp

今のところ、githubには全てのバージョンが残されているようですが、念の為、クラスターを作ったバージョンのeksctlは残しておいたほうがいいかもしれません。

CloudFormationテンプレートのタグを更新する

「alpha.eksctl.io/nodegroup-name」タグを既存のCloudFormationテンプレートに付与することで対処が可能です。ノードグループが複数ある場合は、全てのCloudFormationテンプレートにタグを付与する必要があります。
タグが正しく設定されていれば、以下のコマンドでノードグループのリストが取得できます。

eksctl get nodegroup --cluster=$CLUSTER_NAME

※ただし、この方法は公式で案内されているわけではないので、テスト環境などでしっかり事前に検証を行ってください。

デプロイ

Kubernetesの便利さはデプロイにあると思います。kubectlコマンドでマニフェストのデプロイも可能ですが、運用面の観点からもツールを使ってデプロイを行った方がいいと思います。ツールはJenkinsX、Spinaker、ArgoCDなど様々なツールがあるので自分に適したツールを選択して利用してください。ちなみに今はArgoCDを使っています。ArgoCDの概要については以下のブログを参照してください、
Argo CDによってGKEでGitOpsをする - Kekeの日記

秘匿情報の管理方法

 デプロイにおいて、DBのパスワードやAPIトークンなどの秘匿情報の管理をどうするかという問題があります。そこで、kubernetesにはsecretという秘匿情報を管理するリソースがあります。ただし、secret自体の定義もマニフェストで管理するとそこに秘匿情報が書かれるのでgitなどにアップしてしまうとセキュリティ上好ましくないことになります。
 より良い方法を模索中ではありますが、現状はAWSのパラメータストアに手動で秘匿情報を登録し、以下のようなコマンドでsecretの登録時にパラメータストアからデータを取得して登録しています。gitにはsecretの登録コマンドをアップすることで秘匿情報を誤ってしてしまうこともありません。

kubectl create -n test secret generic test-secret \
--from-literal=TEST_SECRET=$(aws ssm get-parameter --name test_secret --with-decryption | jq -r .Parameter.Value) \

他にも秘匿情報を管理するためにSealedSecretというツールもありますので参考にしてみてください。
GitOpsでも秘匿情報をバッチリ扱う方法、SealedSecretとは? / How to manage credentials on GitOps - Speaker Deck

※2020/3/5にEKSではKMSをネイティブに使えるようにサポートしました。今後はこれを使う方がいいと思います。
aws.amazon.com

Podが起動しない時のトラブルシューティング

Kubernetesを触り始めた時にPodが立ち上がらなかったりすると、まずどこから調べていいのかよくが分かりませんでした。
そんな時に以下の記事を見つけたのですが、Podのデプロイ時のトラブルシューティングの手順がフローチャートで分かりやすくまとめられていますのでぜひ参考にしてみてください。
learnk8s.io
qiita.com

まとめ

Kubernetesは非常に奥が深いので、まだまだ注意点があると思います。今後運用を続けていく中で気をつける点などEKS特有の問題点などが出てきたら追記していきたいと思います。