「Fargate for EKS」に対応したeksctlコマンドの使い方(初歩編)

eksctlが0.11.0で「Fargate for EKS」に対応しました。使い方を簡単に説明します。
2019.12.10

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

このブログ記事は「Amazon EKS Advent Calendar 2019」の10日目のエントリです

re:Inventで公開された「Fargate for EKS」に合わせて、EKSのコマンドラインツール「eksctl」version 0.11.0 で「Fargate for EKS」に対応しました。

今回は、「Fargate for EKS」に関係する部分を中心にeksctlコマンドの使い方を見ていきたいと思います。

はじめに:「eksctl」のススメ (特に初学者)

現在、EKSの環境を作成して使い始めるには、いくつかの方法があります。

  • マネジメントコンソール
  • AWS CLI
  • eksctl
  • Terraform

この中で、個人的に最も「お手軽」なのは「eksctl」だと思います。

というのも、他の方法だと「IAMロール」や「セキュリティグループ」などEKSに必要な関連リソースを事前に個別に作成する必要がありますが、eksctlを使うと自動的にこれらのリソースを作成してくれるからです。

(もちろん、これらの関連リソースがどのようなものが作成されるのかを知っておくことは重要です。機会があれば、公式ドキュメントを参照したり、eksctlで自動生成されたリソースを確認したりしてみることをお勧めします)

eksctlについては、以前に「入門ブログ」を書きましたので、併せて参考にして頂ければと思います。(Fargate for EKSに対応する前の内容です)

「eksctl」コマンドを使ったAmazon EKS構築入門 | Developers.IO

準備: eksctlおよびその他ツールのインストール

まだeksctlをインストールしていない方は、以下のeksctl公式サイトの「インストール手順」を参照して、インストールしましょう。

https://eksctl.io/introduction/installation/

既にeksctlをインストールしている方は、「Fargate for EKS」に対応したバージョン (0.11.0以降) にアップデートしておいてください。

また、「AWS CLI」「kubectl」などの関連するツールも最新バージョンにしておきましょう。

参考まで、今回のブログは以下の各ツールのバージョンを前提にしています。

$ eksctl version
[i]  version.Info{BuiltAt:"", GitCommit:"", GitTag:"0.11.1"}

$ aws --version
aws-cli/1.16.298 Python/3.6.7 Linux/4.4.0-18362-Microsoft botocore/1.13.34

$ kubectl version --client --short
Client Version: v1.16.3

EKSクラスターを作成してPodを起動してみる (EC2/Fargate)

その1: EC2ワーカーノードのみに対応した従来のクラスターを作成する

「Fargate for EKS」について見ていく前に、まずは従来のEC2ワーカーノードのみに対応したEKSクラスターを作成してみます。

今回は、以下のようなオプションを指定して実行しました。

eksctl create cluster \
  --name eks-example1 \
  --nodegroup-name ng-example \
  --node-type t3.large \
  --nodes 2 \
  --managed

最低限のオプションとして「ノードグループ名」「ノードタイプ」「ノード数」を指定しています。

また、re:Inventの直前にリリースされた「Managed Node Group」に対応させるため --managed も指定しています。
(これを指定しないと、マネジメントコンソールにノードグループが表示されなくなりますので注意してください)

作成されたクラスターを確認する

EKSクラスターが作成されると、以下のようになります。

--managed を付けたことにより、作成したノードグループがマネジメントコンソールに表示されています。

kubectlでNodeの一覧を確認します。

$ kubectl get nodes
NAME                                                STATUS   ROLES    AGE     VERSION
ip-192-168-30-117.ap-northeast-1.compute.internal   Ready    <none>   8m54s   v1.14.7-eks-1861c5
ip-192-168-73-252.ap-northeast-1.compute.internal   Ready    <none>   8m59s   v1.14.7-eks-1861c5

ワーカーノードが2つ作成されていることが確認できます。

Podを起動してみる

では、作成したEKSクラスター上でPodを起動してみます。

以下のマニフェストファイルを用意します。
(nginxコンテナを6つ起動するというシンプルな内容です。コンテナのポート番号などを指定していますが、今回はnginxへのアクセスは行わず、Podを起動させる目的のみで使用します)

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 6
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80

マニフェストを使ってDeploymentを作成します。

$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

作成されたリソースを確認します。

$ kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   6/6     6            6           13s

$ kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-59777878f8   6         6         6       33s

$ kubectl get pods --output wide
NAME                                READY   STATUS    RESTARTS   AGE   IP               NODE                                                NOMINATED NODE   READINESS GATES
nginx-deployment-59777878f8-2n4n9   1/1     Running   0          49s   192.168.70.25    ip-192-168-73-252.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-7wc95   1/1     Running   0          49s   192.168.20.63    ip-192-168-30-117.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-9cl8r   1/1     Running   0          49s   192.168.31.216   ip-192-168-30-117.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-j9cvw   1/1     Running   0          49s   192.168.71.66    ip-192-168-73-252.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-szfcc   1/1     Running   0          49s   192.168.90.80    ip-192-168-73-252.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-x6l6f   1/1     Running   0          49s   192.168.12.59    ip-192-168-30-117.ap-northeast-1.compute.internal   <none>           <none>

DeploymentによってReplicaSetが「レプリカ数: 6」(=マニフェストのreplicasで指定した値) で作成され、ReplicaSetによってPodが6つ起動されました。
Podは2台のNodeに分散して配置されていることが分かります。

その2: Fargateのみに対応したクラスターを作成する

では、いよいよ「Fargate for EKS」に対応したEKSクラスターを作成してみます。

eksctlを使って「Fargate for EKS」を使い始める最も簡単な方法は、eksctl version 0.11.0 で追加された --fargate オプションを指定することです。

eksctl create cluster \
  --name eks-example2 \
  --fargate

--fargate オプションを指定すると、「Fargate for EKS」で必要となる「Fargateプロファイル」が作成されます。

また、--fargate オプションを指定した場合は、ノードグループは作成されません。
暗黙的に --without-nodegroup が指定されていると考えて良いと思います。

作成されたクラスターを確認する

EKSクラスターが作成されると、以下のようになります。

ノードグループが作成されておらず、Fargateプロファイルが作成されていることが分かります。

--fargate オプションの指定により作成されたFargateプロファイルは以下のようになります。

Fargateプロファイルには「Pod実行ロール」「サブネット」などいくつかの要素が含まれますが、最も重要な要素は「Podセレクター」です。

Podセレクターとは、Fargate上で実行させたいPodの条件を指定するための設定です。
具体的には、KubernetesのNamespaceを指定することで、特定のNamespaceに属するPodのみがFargate上で実行されるように制御することができます。

今回作成されたFargateプロファイルには、2つの「Podセレクター」が定義されており、それぞれ「default」「kube-system」というNamespaceが指定されています。
default: Namespaceを明示しなかった場合にデフォルトで選択されるNamespace
kube-system: Kubernetesがシステムで使用するリソースを配置する際に使用されるNamespace

eksctlコマンドでFargateプロファイルの内容を確認すると以下のようになります。

$ eksctl get fargateprofile --cluster eks-example2 --name fp-default --output yaml
- name: fp-default
  podExecutionRoleARN: arn:aws:iam::123456789012:role/eksctl-eks-example2-cluste-FargatePodExecutionRole-ZNKQBZ3EU0Z5
  selectors:
  - namespace: default
  - namespace: kube-system
  subnets:
  - subnet-08f33de655742dcbc
  - subnet-0bd0ddd636af6f0a2
  - subnet-018418fe4c5f53bde

マネジメントコンソールで参照できる情報と同様に、「Pod実行ロール」「Podセレクター」「サブネット」の情報が確認できます。

Podを起動してみる

「その1」の時と同様のマニフェストを使ってDeploymentを作成します。

$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

作成されたリソースを確認します。

$ kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   6/6     6            6           69s

$ kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-59777878f8   6         6         6       88s

$ kubectl get pods --output wide
NAME                                READY   STATUS    RESTARTS   AGE    IP                NODE                                                         NOMINATED NODE   READINESS GATES
nginx-deployment-59777878f8-2v6bs   1/1     Running   0          101s   192.168.140.199   fargate-ip-192-168-140-199.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-65ss9   1/1     Running   0          101s   192.168.153.82    fargate-ip-192-168-153-82.ap-northeast-1.compute.internal    <none>           <none>
nginx-deployment-59777878f8-76n5g   1/1     Running   0          101s   192.168.152.19    fargate-ip-192-168-152-19.ap-northeast-1.compute.internal    <none>           <none>
nginx-deployment-59777878f8-mqlf8   1/1     Running   0          101s   192.168.131.119   fargate-ip-192-168-131-119.ap-northeast-1.compute.internal   <none>           <none>
nginx-deployment-59777878f8-s5sms   1/1     Running   0          101s   192.168.97.193    fargate-ip-192-168-97-193.ap-northeast-1.compute.internal    <none>           <none>
nginx-deployment-59777878f8-z826t   1/1     Running   0          101s   192.168.170.14    fargate-ip-192-168-170-14.ap-northeast-1.compute.internal    <none>           <none>

「その1」の時と同様に、Deployment、ReplicaSetが作成され、Podが6つ起動されています。

Podの「Node」欄を見てみましょう。
「fargate-~」で始まる「ノード」上にPodが配置されていることになっています。
6つのノードは全て異なる名前です。

実は、これがPodの起動要求によって作成された「Fargate」であり、6つのPodを動かすために6つのFargateが作成されていることを示します。

つまり、Fargate for EKSでは、Kubernetes上において1つのFargateは1台のワーカーノードのように見える、ということになります。
(あくまで「のように見える」というだけで、EC2インスタンスベースのワーカーノードと全く同じ物が作成される訳ではありません)

では、ここで、Nodeの一覧を表示してみましょう。

$ kubectl get nodes
NAME                                                         STATUS   ROLES    AGE    VERSION
fargate-ip-192-168-118-124.ap-northeast-1.compute.internal   Ready    <none>   14m    v1.14.8-eks
fargate-ip-192-168-127-142.ap-northeast-1.compute.internal   Ready    <none>   14m    v1.14.8-eks
fargate-ip-192-168-131-119.ap-northeast-1.compute.internal   Ready    <none>   110s   v1.14.8-eks
fargate-ip-192-168-140-199.ap-northeast-1.compute.internal   Ready    <none>   115s   v1.14.8-eks
fargate-ip-192-168-152-19.ap-northeast-1.compute.internal    Ready    <none>   115s   v1.14.8-eks
fargate-ip-192-168-153-82.ap-northeast-1.compute.internal    Ready    <none>   115s   v1.14.8-eks
fargate-ip-192-168-170-14.ap-northeast-1.compute.internal    Ready    <none>   83s    v1.14.8-eks
fargate-ip-192-168-97-193.ap-northeast-1.compute.internal    Ready    <none>   99s    v1.14.8-eks

「Fargate-~」で始まる「ノード」が8つ作成されていることが分かります。

ん? 「8つ」?

さっき「6つのPodを動かすために6つのFargateが作成されている」と書きましたよね。
なのに「8つ」とは一体・・・?

Namespace「kube-system」に作成されるPodの動作

正体を明かすと、「2つ」余分に作成されているFargateは、「CoreDNS」というKubernetesのシステムが使用する特殊なPodを起動するために作成されたものです。

CoreDNSの詳細な説明は割愛しますが、CoreDNSのリソースがどのように作成されたのかは、Namespace「kube-system」のDeployment、ReplicaSet、Podの一覧を表示すると分かります。

$ kubectl get deployments --namespace kube-system
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
coredns   2/2     2            2           102m

$ kubectl get replicasets --namespace kube-system
NAME                 DESIRED   CURRENT   READY   AGE
coredns-699bb99bf8   0         0         0       102m
coredns-6d75bbbf58   2         2         2       98m

$ kubectl get pods --namespace kube-system --output wide
NAME                       READY   STATUS    RESTARTS   AGE   IP                NODE                                                         NOMINATED NODE   READINESS GATES
coredns-6d75bbbf58-8zlrg   1/1     Running   0          98m   192.168.127.142   fargate-ip-192-168-127-142.ap-northeast-1.compute.internal   <none>           <none>
coredns-6d75bbbf58-q2kr9   1/1     Running   0          98m   192.168.118.124   fargate-ip-192-168-118-124.ap-northeast-1.compute.internal   <none>           <none>

Fargateプロファイルの内容を思い出してみましょう。

Podセレクターとして「default」Namespaceともう一つ「kube-system」Namespaceが条件として定義されていたと思います。

このように、Kubernetesのシステムが使用するPodもFargate上で実行させる必要があるため、「kube-system」NamespaceがPodセレクターに定義されていたのです。

Podセレクターに定義されていないNamespaceのPodを起動すると・・・?

では、ここで敢えて、Podセレクターに定義されていないNamespaceを指定して、Podの起動を試みてみます。

まず、適当に「hogehoge」という名前のNamespaceを作成します。

$ kubectl create namespace hogehoge
namespace/hogehoge created

このNamespaceを指定してDeploymentを作成します。
(簡略化のためにマニフェストを使用せずに直接作成します)

$ kubectl create deployment nginx-hogehoge --image nginx --namespace hogehoge
deployment.apps/nginx-hogehoge created

Podが起動されたかどうか見てみましょう。

$ kubectl get deployments --namespace hogehoge
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
nginx-hogehoge   0/1     1            0           4m36s

$ kubectl get replicasets --namespace hogehoge
NAME                       DESIRED   CURRENT   READY   AGE
nginx-hogehoge-7b58884cf   1         1         0       4m43s

$ kubectl get pods --namespace hogehoge
NAME                             READY   STATUS    RESTARTS   AGE
nginx-hogehoge-7b58884cf-6jwlt   0/1     Pending   0          5m

Deploymentを作成して5分近く経過したのにもかかわらず、Podのステータスが「Pending」となっています。

何が起こっているのか、イベントを確認します。

$ kubectl get events --namespace hogehoge
LAST SEEN   TYPE      REASON              OBJECT                                MESSAGE
66s         Warning   FailedScheduling    pod/nginx-hogehoge-7b58884cf-6jwlt    0/8 nodes are available: 8 Insufficient pods.
5m13s       Normal    SuccessfulCreate    replicaset/nginx-hogehoge-7b58884cf   Created pod: nginx-hogehoge-7b58884cf-6jwlt
5m13s       Normal    ScalingReplicaSet   deployment/nginx-hogehoge             Scaled up replica set nginx-hogehoge-7b58884cf to 1

Deployment、ReplicaSetは正常に作成されていますが、Podの起動において「Podの起動に十分なNodeが無い」というようなことを言われています。(ちょっと意訳入ってます?)

ともかく、Podセレクターに定義されていないNamespaceを指定してPodを起動しようとしても、Podが起動されないことが分かりました。

Fargate for EKSを利用する際は、使用するNamespaceを予めFargateプロファイルに登録しておく必要があるということですね。

おまけ: CoreDNSの起動時に起こっていること

さて、上の方で「CoreDNSを起動するために作成されるDeployment、ReplicaSet、Podの一覧」を掲載しましたが、ReplicaSetの表示内容にちょっと気になるところがありました。

$ kubectl get replicasets --namespace kube-system
NAME                 DESIRED   CURRENT   READY   AGE
coredns-699bb99bf8   0         0         0       102m
coredns-6d75bbbf58   2         2         2       98m

1つのDeploymentに対して2つのReplicaSetが作成されていて、しかも、一方のReplicaSetは「DESIRED」「CURRENT」「READY」が全て「0」になっています。

これは、eksctlによるEKSクラスターとFargateプロファイルの作成プロセスに理由があります。

eksctlは、まずEKSクラスター (コントロールプレーン) を作成し、その後にFargateプロファイルを作成します。

一方、CoreDNSはEKSクラスターが作成された直後に、Kubernetesのシステムによって作成されます。

このタイミングでは、まだFargateプロファイルが作成されておらず、またEC2インスタンスベースのワーカーノードも存在しないため、Deployment/ReplicaSetがPodを起動しようとしても配置先のノードが存在しない状態となります。

実際に、このタイミングでPodの一覧を表示すると、以下のような結果になります。

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   coredns-699bb99bf8-9jm7r   0/1     Pending   0          3m42s
kube-system   coredns-699bb99bf8-lct9l   0/1     Pending   0          3m42s

前項の「Podセレクターに定義されていないNamespaceのPodを実行すると・・・?」の時と同じ状況になっているのが分かります。

eksctlは、Fargateプロファイルの作成が完了した後に、この「行き場が無くPodを起動できないDeployment/ReplicaSet」に対して、一度「リフレッシュ」を掛けます。
(Kubernetesの正式な用語では「rollout」と言います)

リフレッシュを掛けられたDeploymentは、Podを配置することができないReplicaSsetに見切りを付け、新たに別のReplicaSetを作成します。
そして、そのReplicaSetが、今度はFargateを使用してPodを無事起動することができた、という訳です。

ReplicaSetが2つ表示されるのは、そういうことなんですね。
(「DESIRE」が「0」になっているというのは「見切りを付けられた」ということなのです)

おわりに

eksctlを使用した「Fargate for EKS」に対応したEKSクラスターの作成を、従来の従来のEC2ワーカーノードのみに対応したEKSクラスターの場合と比較しながら行ってみました。

今回は、作成した「Fargate for EKS」なEKSクラスターの上で単純なPodの起動しか試せませんでしたが、「Service」や「Ingress」など、他のKubernetesリソースと組み合わせた場合についても試してみたいと思います。