AWS
EC2
kubernetes
ENI
eks

t2.micro 1台でEKSを起動してみた

AWSの無料枠といえばt2.microです。
ということで、EKSをWorkerがt2.micro 1台という構成で構築してみました。1
(2018/06/12) CloudFormationテンプレートのMaxPodsの設定が影響している可能性を考慮していなかったため、追記しました。

結論

EKSのWorkerは、t2.microでも特に問題なく構築でき、Podの起動も可能でした。
ただし、IPアドレス数の制限により、Podは2つ以上起動できませんでした。
IPアドレスが枯渇するとWorkerもPodもスケールできなくなると思われるので、IPアドレス数の制限には注意が必要そうです。
インスタンスタイプごとに、IPアドレス数の制限と、MaxPodsによる制限と、他にも何らかの制限があり、Podの起動数が制限されているようです。
スケールを考えるうえで、Podの起動数の制限には注意が必要そうです。

やったこと

Workerがt2.micro 1台構成なEKSの構築

Workerの構築の手前まで

Amazon EKS の使用開始 に従い、「ステップ 3: Amazon EKS ワーカーノードを起動して設定する」の手前まで構築を進めます。

CloudFormationテンプレートの修正

Amazon EKS の使用開始 に掲載されているテンプレートを、以下のように2箇所書き換えます。

---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS - Node Group'

Parameters:

  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instances
    Type: AWS::EC2::KeyPair::KeyName
    :
    :
    :
  NodeInstanceType:
    Description: EC2 instance type for the node instances
    Type: String
    Default: t2.medium
    AllowedValues:
    # t2.microを追加
    - t2.micro
    - t2.small
    - t2.medium
    - t2.large
    - t2.xlarge
    - t2.2xlarge
    :
    :
    :
      MaxPods: 234
    r4.8xlarge:
      MaxPods: 234
    r4.16xlarge:
      MaxPods: 737
    # t2.microを追加
    t2.micro:
      # MaxPodsは適当に設定
      MaxPods: 4
    t2.small:
      MaxPods: 8
    t2.medium:
      MaxPods: 17
    t2.large:
    :
    :
    :

修正したテンプレートでWorkerを構築

変更したテンプレートを使ってWorkerを構築します。
NodeAutoScalingGroupMaxSizeは1、NodeInstanceTypeはt2.microとします。

eks-worker-stack-parameter.png

ConfigMapの作成

Amazon EKS の使用開始 に記載されている「ワーカーノードをクラスターと結合するには」に従って、ConfigMapを作成します。

Worker構築完了の確認

kubectlでワーカーが1台表示されれば構築完了です。

$ kubectl get nodes
NAME                                           STATUS    ROLES     AGE       VERSION
ip-192-168-157-38.us-west-2.compute.internal   Ready     <none>    1m        v1.10.3

Podの起動

まずはPodを1つ起動してみる

NginxのPodを1つ起動してみます。

$ kubectl run my-nginx --image=nginx:alpine
deployment.apps "my-nginx" created

無事起動しました。

$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/my-nginx-6947c57698-tjt6p   1/1       Running   0          25s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   24m

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   1         1         1            1           25s

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-6947c57698   1         1         1         25s

ポートフォワードして動作確認します。

$ kubectl port-forward deployment.apps/my-nginx 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

ブラウザから http://localhost:8080 に接続すると、画面が表示されました。

nginx.png

t2.microでもPodを起動できることが確認できました。

Podを増やしてみる

Podを2つに増やしてみます。

$ kubectl scale deployment.apps/my-nginx --replicas=2
deployment.apps "my-nginx" scaled

以下の通り、2つ目のPodが起動していません。

$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/my-nginx-6947c57698-5lnxj   0/1       Pending   0          16s
pod/my-nginx-6947c57698-tjt6p   1/1       Running   0          6m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   29m

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   2         2         2            1           6m

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-6947c57698   2         2         1         6m

詳細を確認します。

$ kubectl describe pod/my-nginx-6947c57698-5lnxj
Name:           my-nginx-6947c57698-5lnxj
Namespace:      default
Node:           <none>
Labels:         pod-template-hash=2503713254
                run=my-nginx
Annotations:    <none>
Status:         Pending
IP:
Controlled By:  ReplicaSet/my-nginx-6947c57698
Containers:
  my-nginx:
    Image:        nginx:alpine
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-8xb9r (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  default-token-8xb9r:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-8xb9r
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  19s (x8 over 1m)  default-scheduler  0/1 nodes are available: 1 Insufficient pods.

何かの不足でPodが起動できていないようです。

考察

2つ目のPodが起動できなかった理由

ポッドネットワーキング によると...

Amazon EKS は、Kubernetes の Amazon VPC CNI プラグインを使用した従来の VPC ネットワーキングをサポートしています。この CNI プラグインを使用すると、Kubernetes ポッドは VPC ネットワーク上と同じ IP アドレスをポッド内に持つことができます。この CNI プラグインは、GitHub で管理されているオープンソースのプロジェクトです。
:
:
:
Elastic Network Interface と、Amazon EC2 インスタンスタイプによるセカンダリ IP アドレス制限が適用されます。一般的に、インスタンスが大きいほど、より多くの IP アドレスをサポートできます。詳細については、Linux インスタンス用 Amazon EC2 ユーザーガイドの「各インスタンスタイプのネットワークインターフェイスごとの IP アドレス」を参照してください。

各インスタンスタイプのネットワークインターフェイスごとの IP アドレス を参照すると...

インスタンスタイプ ネットワークインターフェイスの最大数 インターフェイスあたりの IPv4 アドレス インターフェイスあたりの IPv6 アドレス
t2.micro 2 2 2
t2.small 2 4 4
t2.midium 3 6 6

ということで、t2.microは 2 × 2 = 4 つまでしかIPアドレスを持てないようです。

他のNamespaceでどれだけIPアドレスを使っているのか確認すると...

$ kubectl get pod --namespace kube-system -o wide
NAME                       READY     STATUS    RESTARTS   AGE       IP                NODE
aws-node-2tqc2             1/1       Running   0          30m       192.168.157.38    ip-192-168-157-38.us-west-2.compute.internal
kube-dns-7cc87d595-bqdjk   3/3       Running   0          52m       192.168.170.125   ip-192-168-157-38.us-west-2.compute.internal
kube-proxy-zkd2n           1/1       Running   0          30m       192.168.157.38    ip-192-168-157-38.us-west-2.compute.internal

上記のように、kube-systemでIPアドレスを2つ使っています。

インスタンスがそもそも1つIPアドレスを使っているので、各ノードに対して自動的に3個のIPアドレスが消費されてしまうようです。

その結果、合計で4つまでしかIPアドレスを持てないt2.microは、Podを1つしか起動できなかったようです。

t2.smallでも試してみる

t2.smallでも実験してみました。

t2.smallはIPアドレスを 2 × 4 = 8 個持てるはずです。
そのうち3個が消費されてしまうはずなので、5個だけPodが起動して、6個目は起動できないはずです。

実際にPodを6個起動しようとすると...

$ kubectl scale deployment.apps/my-nginx --replicas=6
deployment.apps "my-nginx" scaled
$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/my-nginx-6947c57698-2cq6s   1/1       Running   0          34s
pod/my-nginx-6947c57698-6kdmb   1/1       Running   0          34s
pod/my-nginx-6947c57698-6xnfc   0/1       Pending   0          34s
pod/my-nginx-6947c57698-gl29s   1/1       Running   0          34s
pod/my-nginx-6947c57698-jfk58   1/1       Running   0          34s
pod/my-nginx-6947c57698-z5fh4   1/1       Running   0          34s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   1h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   6         6         6            5           50m

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-6947c57698   6         6         5         50m

たしかに5つまでしか起動できませんでした。

(2018/06/12) 追加調査

t2.mediumでも試してみる

実は、ここまでの確認方法では、t2.micro、t2.smallのPod起動制限がCloudFormationテンプレートで指定したMaxPodsに依存している可能性を排除できていませんでした。
そこで、t2.mediumでも試してみました。

t2.mediumはMaxPodsが17なので、この設定の影響があるならNginxのPodは14個まで起動できるはずです。
ENIの面では、 3 × 6 = 18 なので、こちらの設定が影響するならNginxのPodは15個まで起動できるはずです。

では、Podを16個起動しようとしてみます。

$ kubectl scale deployment.apps/my-nginx --replicas=16
deployment.apps "my-nginx" scaled
$ kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/my-nginx-6947c57698-2dm68   1/1       Running   0          1m
pod/my-nginx-6947c57698-5zdkr   1/1       Running   0          1m
pod/my-nginx-6947c57698-6kx6c   1/1       Running   0          2m
pod/my-nginx-6947c57698-8mt2x   0/1       Pending   0          1m
pod/my-nginx-6947c57698-9lqmk   1/1       Running   0          1m
pod/my-nginx-6947c57698-dwpcz   1/1       Running   0          1m
pod/my-nginx-6947c57698-gxzft   1/1       Running   0          1m
pod/my-nginx-6947c57698-jf94r   1/1       Running   0          1m
pod/my-nginx-6947c57698-jnzd9   1/1       Running   0          1m
pod/my-nginx-6947c57698-k69fp   1/1       Running   0          1m
pod/my-nginx-6947c57698-nntk4   1/1       Running   0          1m
pod/my-nginx-6947c57698-rhdn6   1/1       Running   0          1m
pod/my-nginx-6947c57698-s47mq   1/1       Running   0          1m
pod/my-nginx-6947c57698-thc85   0/1       Pending   0          1m
pod/my-nginx-6947c57698-vkdpb   1/1       Running   0          1m
pod/my-nginx-6947c57698-vwvd6   1/1       Running   0          1m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   14m

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   16        16        16           14          2m

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-6947c57698   16        16        14        2m

念のため詳細を見ておくと...

$ kubectl describe pod/my-nginx-6947c57698-8mt2x
Name:           my-nginx-6947c57698-8mt2x
Namespace:      default
Node:           <none>
Labels:         pod-template-hash=2503713254
                run=my-nginx
Annotations:    <none>
Status:         Pending
IP:
Controlled By:  ReplicaSet/my-nginx-6947c57698
Containers:
  my-nginx:
    Image:        nginx:alpine
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-t664j (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  default-token-t664j:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-t664j
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  48s (x8 over 1m)  default-scheduler  0/1 nodes are available: 1 Insufficient pods.

ということで、Podは14個までしか起動できませんでした。
したがって、Podの起動数の制限はMaxPodsにも依存していたように思えます。

MaxPodsを変更して試してみる

ENIによる制限とMaxPodsによる制限のうち小さいほうに依存している可能性があるので、さらに検証します。

t2.mediumのMaxPodsを19に書き換えます。

      MaxPods: 737
    t2.micro:
      MaxPods: 4
    t2.small:
      MaxPods: 8
    t2.medium:
      # 17から19に書き換える
      MaxPods: 19
    t2.large:
      MaxPods: 35
    t2.xlarge:
      MaxPods: 44
    t2.2xlarge:

これでもMaxPodsが影響するなら 19 - 3 = 16 個のPodが起動するはずです。
ENIが影響するなら 18 - 3 = 15 個のPodが起動するはずです。

$ kubectl get all
NAME                            READY     STATUS              RESTARTS   AGE
pod/my-nginx-6947c57698-2k9kz   1/1       Running             0          6m
pod/my-nginx-6947c57698-4q8j7   0/1       ContainerCreating   0          6m
pod/my-nginx-6947c57698-5n4hn   1/1       Running             0          6m
pod/my-nginx-6947c57698-7ntlc   1/1       Running             0          6m
pod/my-nginx-6947c57698-96p94   1/1       Running             0          6m
pod/my-nginx-6947c57698-bxqfz   0/1       Pending             0          6m
pod/my-nginx-6947c57698-d4rt8   1/1       Running             0          6m
pod/my-nginx-6947c57698-f2bv7   1/1       Running             0          6m
pod/my-nginx-6947c57698-g2krs   0/1       ContainerCreating   0          6m
pod/my-nginx-6947c57698-kw2dq   1/1       Running             0          6m
pod/my-nginx-6947c57698-kzc4k   1/1       Running             0          6m
pod/my-nginx-6947c57698-mrv8m   1/1       Running             0          6m
pod/my-nginx-6947c57698-sq9td   1/1       Running             0          6m
pod/my-nginx-6947c57698-t9kfq   1/1       Running             0          6m
pod/my-nginx-6947c57698-wnvjf   1/1       Running             0          6m
pod/my-nginx-6947c57698-x8lkg   1/1       Running             0          6m
pod/my-nginx-6947c57698-zbkt7   1/1       Running             0          6m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   44m

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   17        17        17           14          31m

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-6947c57698   17        17        14        31m

14個という、予想外の結果になりました。。。

PendingになっているPodの詳細は...

$ kubectl describe pod/my-nginx-6947c57698-bxqfz
Name:           my-nginx-6947c57698-bxqfz
Namespace:      default
Node:           <none>
Labels:         pod-template-hash=2503713254
                run=my-nginx
Annotations:    <none>
Status:         Pending
IP:
Controlled By:  ReplicaSet/my-nginx-6947c57698
Containers:
  my-nginx:
    Image:        nginx:alpine
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-t664j (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  default-token-t664j:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-t664j
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  2m (x26 over 8m)  default-scheduler  0/1 nodes are available: 1 Insufficient pods.

今まで表示されていたのと同様のエラーです。

ContainerCreatingになっているPodの詳細は...

$ kubectl describe pod/my-nginx-6947c57698-4q8j7
Name:           my-nginx-6947c57698-4q8j7
Namespace:      default
Node:           ip-192-168-144-73.us-west-2.compute.internal/192.168.144.73
Start Time:     Tue, 12 Jun 2018 11:25:42 +0900
Labels:         pod-template-hash=2503713254
                run=my-nginx
Annotations:    <none>
Status:         Pending
IP:
Controlled By:  ReplicaSet/my-nginx-6947c57698
Containers:
  my-nginx:
    Container ID:
    Image:          nginx:alpine
    Image ID:
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-t664j (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          False
  PodScheduled   True
Volumes:
  default-token-t664j:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-t664j
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason                  Age                From                                                   Message
  ----     ------                  ----               ----                                                   -------
  Normal   Scheduled               9m                 default-scheduler                                      Successfully assigned my-nginx-6947c57698-4q8j7 to ip-192-168-144-73.us-west-2.compute.internal
  Normal   SuccessfulMountVolume   9m                 kubelet, ip-192-168-144-73.us-west-2.compute.internal  MountVolume.SetUp succeeded for volume "default-token-t664j"
  Warning  FailedCreatePodSandBox  8m (x12 over 9m)   kubelet, ip-192-168-144-73.us-west-2.compute.internal  Failed create pod sandbox: rpc error: code = Unknown desc = NetworkPlugin cni failed to set up pod "my-nginx-6947c57698-4q8j7_default" network: add cmd: failed to assign an IP address to container
  Normal   SandboxChanged          4m (x271 over 9m)  kubelet, ip-192-168-144-73.us-west-2.compute.internal  Pod sandbox changed, it will be killed and re-created.

IPアドレスを与えることに失敗しています。

どうやら、MaxPodsとENIの制限と、他にも何らかの制限があるようです。。。
これ以上は未調査です。

結論 (再掲)

EKSのWorkerは、t2.microでも特に問題なく構築でき、Podの起動も可能でした。
ただし、IPアドレス数の制限により、Podは2つ以上起動できませんでした。
IPアドレスが枯渇するとWorkerもPodもスケールできなくなると思われるので、IPアドレス数の制限には注意が必要そうです。
インスタンスタイプごとに、IPアドレス数の制限と、MaxPodsによる制限と、他にも何らかの制限があり、Podの起動数が制限されているようです。
スケールを考えるうえで、Podの起動数の制限には注意が必要そうです。


  1. GKEやAKSと違い、EKSはMasterノードが有料 ($0.20/hr。t2.xlargeよりも高額) なので、Workerを無料枠に入れても仕方ないというのは別のお話です。