yikegaya’s blog

yikegayaのブログ

AWS EKSの講座受けてみたが複雑なので内容整理

この間UdemyでMac上にminikubeの環境を構築してkubenetesを学ぶ講座を受けたが、その続きでローカルではなくAWS EKS上で本番運用っぽく動かしてみる講座があったので受講してみた。

米シリコンバレーエンジニア監修!AWS EKS KubernetesハンズオンBest Practices (2020) | Udemy

講座聴きながら一通り動かしてみたけど結構複雑で正直理解しきれてない感がある。。ので、講座についてた資料を見返し理解が曖昧な点を掘り下げながら整理してみる。

コマンドは大体講座の添付資料の通りだけど元はregionがus-west-2だったのでap-northeast-1に変更している。

EKSのセットアップ

前回minikubeの時にも使ったkubectlに加えてEKS操作用のeksctlとAWS CLI(awscli)を使ってターミナルからコマンドでEKS Clusterを立ち上げる。ただこれは学習目的で「本番環境ではTerraformやCloudFormationといったIaCを推奨」とある。

AWSの設定とかCLIのローカルへのインストールを済ませた後以下コマンド実行するとEKSのクラスタが作成される。

eksctl create cluster \
    --name eks-from-eksctl \
    --version 1.16 \
    --region ap-northeast-1 \
    --nodegroup-name workers \
    --node-type t3.medium \
    --nodes 2 \
    --nodes-min 1 \
    --nodes-max 4 \
    --ssh-access \
    --ssh-public-key ~/.ssh/eks_worker_nodes_demo.pem.pub \
    --managed

ここでいうnodeっていうのは前回の講座でも出たけどKubenetesの中のサーバの実態を表す概念みたいなものでAWS上で言うとEC2 or Fagateのホストに当たる。そういえば今回の講座ではworker nodeはEC2を使ったのでFagateの場合はどうするのかは別途調べないとかもな。。

nodeの他はname spaceとかversion指定とかsshの認証周りのオプションつけてる感じ。

このコマンド打った後にAWS管理画面開くとEKSやそのワーカノードのEC2がちゃんと作られていた。

あとクラスタ立ち上げっぱなしだとworker nodeのEC2の料金に加えてEKSを使うための料金も取られるので使わない時はマメにクラスタ落とした。

eksctl delete cluster --name eks-from-eksctl --region us-west-2

Helm Chartについて

Kubenetesは結構な量のyamlファイルの作成が必要なんだけど全部手で書くのは辛いのでHelmというツールで作成する。まあ、Kubenetesのパッケージマネージャだと思っておけば良さそうで調べた感じ現在のデファクトスタンダードみたい。

nginx用のyamlを作りたければこんな感じ。

helm search repo nginx
helm search repo bitnami/nginx
helm install nginx bitnami/nginx

このコマンドでConfigMap、Service、Deploymentとnginx環境構築に必要なリソースのyamlが用意される。 helmはstatausとかhistoryとかいろんな便利コマンドが用意されている。

Kuberenetesダッシュボードについて

Kubenetesクラスタの状態を監視するダッシュボードが紹介されている。 GitHub - kubernetes/dashboard: General-purpose web UI for Kubernetes clusters

kubectlでdashboardのリソースを作る →ダッシュボードにログインするためのトークン(パスワードみたいなもの)をSecretリソースから取得 →kubenetesクラスタへのプロキシを作ってブラウザからlocalhost:8001にアクセスするとクラスタの状態を監視できる。

  • kubectlでdashboardのリソースを作るコマンド
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml

namespace、serviceaccount、service、secret、configmap、role、clusterrole、rolebindingなどができきてたけどぶっちゃけまだそれぞれのリソースの役割を理解できておらず講座でもそこまでそこは深追いはしてないのでここは別途調べないとかな。。

kubectlでsecretを取得→結果がbase64エンコードされてるのでデコードする。これをダッシュボードのログイン画面に貼るとログインできる。

kubectl get secrets  kubernetes-dashboard-token-t9z22 -n kubernetes-dashboard --output yaml
echo "{出てきたパスワード}" | base64 -D
  • プロキシ作成
kubectl proxy

サンプルアプリのデプロイ

「guestbook」と言うPHP+Redisでできたサンプルアプリをkubenetesで動かすためのjsonが公開されているのでそれをEKS上で動かした。

guestbookはkubenetes本体のgithubレポジトリの中で管理されている。

examples/README.md at master · kubernetes/examples · GitHub

web上で公開されているjsonをkubectlで直接読んでクラスタ構築する感じ。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-controller.json

kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-service.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json

kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-service.json

指定しているリソースはservice(Podのラッパー)とReplicationController(レプリカ数指定するやつ)

以下のコマンドで公開したサービスのAWS上のロードバランサを取得できる

kubectl  get svc guestbook

Networking: Ingress ControllerでPodを外部に公開

kubectl get service guestbook

と打つとレイヤ4のロードバランサが使われていることを確認できる。ただ本当はレイヤ7を使いたい。

helmでレイヤ7のロードバランサをinstall

kubectl create namespace nginx-ingress-controller

helm install nginx-ingress-controller stable/nginx-ingress -n nginx-ingress-controller

これを使うとnginxがレイヤ7のロードバランスをしてくれる。

ただこのやり方は問題があって本番環境で動かすならIstioを別途学んだ方がいいらしい。クラスタ内の認証が面倒でサービス間の認証のたびにサーバに負担がかかるって感じ?↓

NginxやAWS ALB Ingress Controllerは共にSSL terminationに対応しているが、バックエンドのアプリ間のTLSはアプリ次第となっています。つまり 1) アプリ間でTLS certを提供し、2) CPUリソースを消費するSSL handshakesを行う必要があります。

もしIstioを使うと、アプリのコードを一切変更せずに、クラスター内でデフォルトでSSLを可能にし、且つService Mesh機能であるCanaryリリースやFault Injectionなどもできるようになります。

Security: AWS Userの認証 (aws-iam-authenticator) と 認可(RBAC: Role Based Access Control)

AWS上でKubenetesを動かしているわけなのでAWSのIAMで認証されたAWSユーザーがK8sユーザーとして存在するか認可する(ややこしい)

kubectl get configmap aws-auth -n kube-system -o yaml

とコマンドを打つとAWSのユーザがkubenetesのユーザ、ユーザグループにマップされていることを確認できる。

IAMユーザに権限を与える場合(ルートユーザとして追加)Kubenetesのconfigmapに新しいAWS IAM userのARN(Amazonリソースネーム)を追加する。

kubectl edit -n kube-system configmap/aws-auth
mapUsers: |
    - userarn: arn:aws:iam::111122223333:user/eks-viewer   # AWS IAM user
      username: this-aws-iam-user-name-will-have-root-access
      groups:
      - system:masters  # K8s User Group

このAWS IAM userはsystem:mastersというK8sユーザーグループにバインドされている

ClusterRoleBinding

これはkebenetesの世界の話でkubenetesユーザにRoleを割り当てる。リソースの1種なのでyamlで定義してkubectlでapplyできる。

kubectl create clusterrolebinding system:viewer \
    --clusterrole=view \
    --group=system:viewer \
    --dry-run -o yaml > clusterrolebinding_system_viewer.yaml

Monitoring: PrometheusとGrafana

監視ツールの話。Prometheusはサーバインフラ監視ツール、Grafanaはログ・データの可視化ツールでどっちも別にkubenetes専用のものではないがkubenetes環境でも使えるって感じ。

どっちもHelmからinstallできる。Helmって監視ツールもインストールできるのか。。

# まずはnamespaceを作成
kubectl create namespace prometheus

helm install prometheus stable/prometheus \
    --namespace prometheus \
    --set alertmanager.persistentVolume.storageClass="gp2" \
    --set server.persistentVolume.storageClass="gp2"
kubectl create namespace grafana

helm install grafana stable/grafana \
    --namespace grafana \
    --set persistence.storageClassName="gp2" \
    --set persistence.enabled=true \
    --set adminPassword='EKS!sAWSome' \
    --set datasources."datasources\.yaml".apiVersion=1 \
    --set datasources."datasources\.yaml".datasources[0].name=Prometheus \
    --set datasources."datasources\.yaml".datasources[0].type=prometheus \
    --set datasources."datasources\.yaml".datasources[0].url=http://prometheus-server.prometheus.svc.cluster.local \
    --set datasources."datasources\.yaml".datasources[0].access=proxy \
    --set datasources."datasources\.yaml".datasources[0].isDefault=true \
    --set service.type=ClusterIP

(ベストプラクティス) Security: IRSA(IAM Role for Service Account)を使い、PodレベルのAWSへの認可を設定

PodにService Accountを指定してPodにトークンを持たせてそこからAWSapiサーバにつないでPod単位の認証かけられる。その流れざっくり噛み砕くとこんな感じ↓一つ一つは普段触ってるWebアプリの認証の仕組みと変わらないんだけど手順が多くて複雑だな。

Podを起動する時にAWSのAPIサーバにyamlを送る
→APIサーバのwebhookがk8sのService AccountとそのService AccountにIAM Role ARNがあることを確認する
→webhookがService Accountのannotationを元にpodに環境変数を設定
→Service AccountがOIDC(OpenID Connect)経由でAWS IAMから認証
→JWT トークンをOIDCから受け取ってPodの環境変数(AWS_WEB_IDENTITY_TOKEN_FILE)に保存
→以降PodでAWS CLIコマンドを使うと環境変数のトークンを使ってAWS IAM roleをAssumeする。

実際の構築コマンド

AWSでIODCを作成する必要あり(このハンズオンではeksctl create clusterを使ってclusterを作った際に作られているので未実施) 確認コマンドは略

aws iam create-open-id-connect-provider \
          --url $ISSUER_URL \
          --thumbprint-list $ROOT_CA_FINGERPRINT \
          --client-id-list sts.amazonaws.com

OIDC providerをEKSクラスターにリンク

eksctl utils associate-iam-oidc-provider \
            --region=us-west-2 \
            --cluster=eks-from-eksctl \
            --approve

管理画面からIAMを作成してから以下コマンド実行 Service accountを作成 →IAM role ARNをAnnotationに追加

eksctl create iamserviceaccount \
                --name irsa-service-account \
                --namespace default \
                --cluster eks-from-eksctl \
                --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
                --approve \
                --region ap-northeast-1

EC2 instance profileにアクセスが可能なのでブロック

podからEC2のインスタンスメタデータにアクセスできてしまうのでそこも塞がないとダメらしい

以下講義資料のコマンド。ec2に入ってiptablesコマンド打ってファイヤウォール設定するのか。これよく気づくな。

EKSワーカーノードにSSH

eval $(ssh-agent)
ssh-add -k ~/.ssh/
ssh -A ec2-user@192.168.20.213

iptablesコマンドを実行

yum install -y iptables-services
iptables --insert FORWARD 1 --in-interface eni+ --destination 169.254.169.254/32 --jump DROP
iptables-save | tee /etc/sysconfig/iptables 
systemctl enable --now iptables

11. (ベストプラクティス) Scaling: Cluster Autoscaler (CA)

Clusterのスケーリングの話

1.AWS ASGのタグを追加(AWGはAuto Scaling Groupのこと) →AWS管理画面からEC2→Auto Scaling Groupを開いてそこで設定する(クラスタ作成時に勝手に作られているので実際は作成不要。管理画面開くと確認できる)

2.cluster autoscalerへ認可(Authorization)のIAM パミッションをIRSA (Podレベル)かEC2 Instance Profile(ノードレベル)で追加

手順としてはIAMポリシーを管理画面から作成→eksctlでService AccountとIAMロールを作成の流れ

eksctl create iamserviceaccount \
                --name cluster-autoscaler-aws-cluster-autoscaler \
                --namespace kube-system \
                --cluster eks-from-eksctl \
                --attach-policy-arn arn:aws:iam::266981300450:policy/EKSFromEksctlClusterAutoscaler \
                --approve \
                --region ap-northeast-1

3.cluster-autoscalerをHelm ChartとしてEKS clusterにインストール

Region、ServiceAccount名、クラスタ名を記載したyamlを元にしてHelmでcluster-autoscalerをインストールする。これでAuto Scalingの設定が完了する。

awsRegion: ap-northeast-1

rbac:
  create: true # ref: https://github.com/kubernetes/autoscaler/issues/1507
  serviceAccount:
    create: false # because Service account and IAM role already created by `eksctl create iamserviceaccount` s
    name: cluster-autoscaler-aws-cluster-autoscaler

autoDiscovery:
  clusterName: eks-from-eksctl
  enabled: true

(ベストプラクティス) Scaling: Horizontal Pod Autoscaler (HPA、水平オートスケーリング)

Podの数を水平にスケールする

cluster-autoscalerはHelmで入れないといけなかったけどこっちはKubenetesのネイティブで入っているのでインストールは不要。

Metrics Serverは必要(ダッシュボード作る時に入れたやつ)

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml

HPAリソースのyamlを作成してkubectlでapplyすればいいらしい。まあ見たまんまでCPUの状態見て最大数までPodを増やしてくれる。

$ kubectl autoscale deployment test-hpa \
    --min 1 \
    --max 7 \
    --cpu-percent=80 \
    --namespace default \
    --dry-run \
    -o yaml > hpa.yaml

$ kubectl apply -f hpa.yaml
$ kubectl get hpa 

感想

認証とかAutoScalingとかAWSのサービスとKubenetesの機能が複雑につながってる感じがしてなかなか辛そうだな。今回は学習用にコマンド打って一つ一つ設定してったけど、実際に使う時には一度KubenetesのマニフェストファイルとTerraformかCloudFormationを作りきってしまえばインフラの状態コードで把握できるし勝手にスケーリングもしてくれるんで今回みたいにいちいちコマンド打つのとは違った快適な感じになるのかな?

しかし学習コスト高いな。。昔Oracle触ってた時にこれOS学ぶのと同じくらい大変じゃね。。って思ってたけど同じような巨大さを感じる。

この講座だけではまだ本番で使うには不十分でIstioやインフラのコード化の知識やCI/CDも学ばないといけないみたい。しかもまだ開発活発みたいなんで新しいAPIのキャッチアップも必要なんだろうな。