EKS Anywhereを導入しようとして失敗した話

# EKS Anywhereを導入しようとして失敗した話

4ヶ月ほど前にEKS Anywhereを試した記録です。また時間を見つけてリトライします。メモ書きです。

## EKS Anywhereとは?

[Dellのこのページ](https://www.dell.com/community/%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8-Wiki/Coffee-Break-Amazon-EKS-Anywhere-%E3%81%A3%E3%81%A6%E4%BD%95-Dell-Technologies%E8%A3%BD%E5%93%81%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82%E3%81%AF/ta-p/8259377/jump-to/first-unread-message?ref=lith_popular)の解説がとてもわかりやすいです。我々(顧客)がコントロールプレーンとデータプレーンを管理することになります。

![dell.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242768/57ad66b4-275e-3c26-207d-29afa9724631.png)

じゃあ何が良いのか? DIY([Kubernetes公式のKubeadmによる構築](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)や[KubeEdge](https://kubeedge.io/en/))をなぜ選ばないのか? [AWS公式 Q&A](https://aws.amazon.com/jp/eks/eks-anywhere/faqs/)を参考に説明すると、DIYで導入するとKubenetesバージョンが古くなるとサポートがなくなりますが、Amazon EKSと同じディストリビューションが使用されておりKubernetesバージョンが延長的にサポートされます。「保守コストを下げるためにKubernetesを導入したがKubernetesバージョン管理が必要になり逆に保守工数が増えた」という馬鹿らしい自体を避けることができます。

![スクリーンショット 2023-04-02 12.25.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242768/d55cfbc5-cbc5-c4df-622e-02a5e4862f09.png)

[AWS Black Belt](https://pages.awscloud.com/rs/112-TZM-766/images/202109_AWS_Black_Belt_Containers372_Amazon_EKS_Anywhere.pdf)や[AWS公式Price](https://aws.amazon.com/jp/eks/eks-anywhere/pricing/)によると、Enterpriseサポートを申請すると年間数百万円必要になりますが、特にサポートを申請しなければ基本的に価格は無料です。素晴らしいですね。

![eks-a-price.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242768/4c23115d-f8fa-8b1b-2ba7-e09c17f54335.png)

## やりたいこと

[EKS Anywhere Bare Metal Single Node Support](https://aws.amazon.com/jp/about-aws/whats-new/2022/12/amazon-eks-anywhere-single-node-clusters-bare-metal/) で導入したいと考えています。高可用性以外のKubernetesの恩恵を得たいと考えています。 

特にHelmを使ってデプロイを管理したり、Cloudと同じようにEdgeを管理することが目標です。

- やりたいexample
    - [scsk blog eks-a Sysdig連携例](https://www.scsk.jp/sp/sysdig/blog/container_security/sysdigamazon_eks_anywhere.html)
    - [jfrog.com blog eks-a HelmCharts OTA例](https://jfrog.com/ja/blog/with-aws-eks-anywhere-devops-with-artifactory-can-be-everywhere/)
    - [virtualtech blog eks-a Gitops](https://devops-blog.virtualtech.jp/entry/20220518/1652842173)
    - [virtualtech blog Argo CI/CD](https://devops-blog.virtualtech.jp/entry/20220509/1652081784)
    - [armory.io blog eks-a Complex OTA](https://www.armory.io/blog/amazon-eks-anywhere-armory/)
    - [solo.ioblog eks-a Istio1](https://www.solo.io/blog/amazon-eks-anywhere-and-solo-istio/), [Istio2](https://www.youtube.com/watch?v=-HyQByKUJsg)
    - ...etc

## やったこと

[EKS Anywhere公式ドキュメント](https://anywhere.eks.amazonaws.com/docs/getting-started/) に沿って、導入を試みました。ただ、残念ながらドキュメントは`EKS Anywhere Bare Metal Single Node Support`のベストプラクティスとなる導入手順がstep by stepで書かれておらず、非常に読み解くのが難解でした。

### やったこと1 Administrative Machineの構築

構築は全てAdministrative Machineから実行されます。まずはこのAdministrative Machineを構築することが必要です。以下の図はAdministrative Machineのイメージ図です。([公式ドキュメントのcreating-a-bare-metal-cluster](https://anywhere.eks.amazonaws.com/docs/concepts/clusterworkflow/#creating-a-bare-metal-cluster)から引用。)

![スクリーンショット 2023-04-02 13.00.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242768/58a29cc2-198a-eafa-1a78-2fabaa6d9329.png)

[公式に書かれているAdministrative Machineの要件](https://anywhere.eks.amazonaws.com/docs/getting-started/install/#administrative-machine-prerequisites)では、Mac OS 10.15 / Ubuntu 20.04.2 LTS がサポートされており "Docker Desktop is not supported" と注意書きがあります。私はMacでcolimaを使用しているのでMacをAdministrative Machineとして使用できると思いました。しかし、実際にはできませんでした。EKS Anywhereのソースコードを読んでみると、[Docker Desktopがインストールされていなければエラーにする](https://github.com/aws/eks-anywhere/blob/main/pkg/validations/docker.go#L52)処理が実装されていました。私もこのエラー処理にハマり、実行できせんでした。公式ドキュメントと実際のコードがズレていますね・・。まあ、まだリリースされて間もないから仕方ないか・・と思いつつ、Administrative MachineはUbuntuに切り替えました。Ubuntuでは正常に動きました。Administrative Machineの構築は、[install-eks-anywhere-cli-tools](https://anywhere.eks.amazonaws.com/docs/getting-started/install/#install-eks-anywhere-cli-tools) に書かれている通り、手順通りにcliをインストールするだけです。

### やったこと2 OSイメージのビルド

eks-anywhereでは、対象機を所定のOSで動かさないといけません。やったこと1で構築したAdmin Machineが対象機に対してOSをプロビジョニングします。

ここでもハマりポイントがあります。OSは Bottlerocket, Ubuntu, RHEL の3種類あります。私はubuntuでしか使用できないアプリをネイティブで使用する必要があったため、Ubuntuを利用しようとしました。しかし、なんとBottlerocket以外はdocker imageが公開されておらず、自身でコンテナイメージをビルドする必要があるのです。[公式が"EKS Anywhere does not distribute Ubuntu or RHEL OS images"](https://anywhere.eks.amazonaws.com/docs/reference/artifacts/#ubuntu-or-rhel-os-images-for-bare-metal)と書いています。さらにやっかいなのがビルドに必要なMachine要件が結構厳しいです。[prerequisites](https://anywhere.eks.amazonaws.com/docs/reference/artifacts/#prerequisites)の通り、"Ubuntu 22.04 or later (for Ubuntu images) or RHEL 8.4 or later (for RHEL images) + KVM環境" が必要になります。Ubuntu 22.04, RHEL 8.4の物理マシーンがなかったので、Ubuntu18やCentosでトライしましたが、前述の要件を満たさないとダメなのようで、docker imageのビルドに失敗しました。仕方なく、Bottlerocketを使用することにします。

### やったこと3 Tinkerbellによるプロビジョニング

やったこと1, 2の通り、BottlerocketでEKSを構築する場合に、今回準備した物理Machineは以下の通りです。(Ubuntu or RHELで構築する場合は、さらに1台docker imageビルド用のMachineも必要です・・。)

- 物理Machine1: Administrative Machine (Ubuntu 20.04)
- 物理Machine2: eks-anywhere対象機 (新規OS(Bottlerocket))

eks-anywhereは、物理MachineにOSを入れる作業にて、[TinkerbellのiPXE Boot](https://docs.tinkerbell.org/services/boots/)で行うことを必須にしています。私はTinkerbellの利用経験がなく、さらにPXE Bootもやったことがなかったので、ここで詰みました。[eks-anywhere開発者によるプロビジョニング映像](https://www.youtube.com/live/RmZZ7Gr8kMw?feature=share)を見ながら何度かトライしましたが、結局エラー原因がわかりませんでした。

実施する項目としては[baremetal-getstarted/#steps](https://anywhere.eks.amazonaws.com/docs/getting-started/production-environment/baremetal-getstarted/#steps)に書かれている通り、yamlファイルとcsvファイルにプロビジョニング情報を書いて、以下のようにAdmin Machineから実行するだけです。

```sh
eksctl anywhere create cluster \
   --hardware-csv hardware.csv \
   -f eksa-mgmt-cluster.yaml
```

Single NodeなのでCSVファイルは以下のように1台のみ設定し、

```csvs
hostname,mac,ip_address,netmask,gateway,nameservers,labels,disk
eksa-cp01,5c:60:ba:3b:17:bc,10.18.109.115,255.255.255.0,10.18.109.1,127.0.0.53,type=cp,/dev/sda2
```

yamlファイルは以下のようにBottlerocketでの導入で作成しました。

```yaml
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: Cluster
metadata:
  name: mgmt
spec:
  clusterNetwork:
    cniConfig:
      cilium: {}
    pods:
      cidrBlocks:
      - 192.168.0.0/16
    services:
      cidrBlocks:
      - 10.96.0.0/12
  controlPlaneConfiguration:
    count: 1
    endpoint:
      # https://anywhere.eks.amazonaws.com/docs/reference/clusterspec/baremetal/#controlplaneconfigurationendpointhost-required
      # A unique IP you want to use for the control plane in your EKS Anywhere cluster. Choose an IP in your network range that does not conflict with other machines.
      host: "10.200.1.180"
    machineGroupRef:
      kind: TinkerbellMachineConfig
      name: mgmt-cp
  datacenterRef:
    kind: TinkerbellDatacenterConfig
    name: mgmt
  kubernetesVersion: "1.24"
  managementCluster:
    name: mgmt
  # # https://anywhere.eks.amazonaws.com/docs/reference/baremetal/bare-prereq/
  # The minimum number of physical machines needed to run EKS Anywhere on bare metal is 1.
  # To configure EKS Anywhere to run on a single server, set controlPlaneConfiguration.count to 1,
  # and omit workerNodeGroupConfigurations from your cluster configuration.
  # workerNodeGroupConfigurations:
  # - count: 1
  #   machineGroupRef:
  #     kind: TinkerbellMachineConfig
  #     name: mgmt
  #   name: md-0

---
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: TinkerbellDatacenterConfig
metadata:
  name: mgmt
spec:
  # https://anywhere.eks.amazonaws.com/docs/reference/clusterspec/baremetal/#tinkerbelldatacenterconfig-fields
  # This IP address must be a unique IP in the network range that does not conflict with other IPs
  tinkerbellIP: "10.200.1.190"
  # Validation failed     {"validation": "tinkerbell Provider setup is valid", "error": "please use bottlerocket as osFamily for auto-importing or provide a valid osImageURL", "remediation": ""}
  # osImageURL: "??"
  # hookImagesURLPath: "??"

---
# https://anywhere.eks.amazonaws.com/docs/reference/clusterspec/baremetal/#tinkerbellmachineconfig-fields
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: TinkerbellMachineConfig
metadata:
  name: mgmt-cp
spec:
  hardwareSelector:
    type: cp
  osFamily: bottlerocket
  templateRef: {}
  users:
  - name: ec2-user
    sshAuthorizedKeys:
    - ssh-rsa AAAA(省略)

---
# https://anywhere.eks.amazonaws.com/docs/reference/clusterspec/baremetal/#tinkerbellmachineconfig-fields
# apiVersion: anywhere.eks.amazonaws.com/v1alpha1
# kind: TinkerbellMachineConfig
# metadata:
#   name: mgmt
# spec:
#   hardwareSelector: {}
#   osFamily: bottlerocket
#   templateRef: {}
#   users:
#   - name: ec2-user
#     sshAuthorizedKeys:
#     - ssh-rsa AAAA...

# ---
```

上記の通り実行すると、以下のようにタイムアウトしました。

```sh
$ eksctl anywhere create cluster    --hardware-csv hardware.csv    -f eksa-mgmt-cluster.yaml
Warning: The recommended number of control plane nodes is 3 or 5
Warning: No configurations provided for worker node groups, pods will be scheduled on control-plane nodes
Warning: The recommended number of control plane nodes is 3 or 5
Warning: No configurations provided for worker node groups, pods will be scheduled on control-plane nodes
Performing setup and validations
✅ Tinkerbell Provider setup is valid
✅ Validate certificate for registry mirror
✅ Validate authentication for git provider
✅ Create preflight validations pass
Creating new bootstrap cluster
Provider specific pre-capi-install-setup on bootstrap cluster
Installing cluster-api providers on bootstrap cluster
Provider specific post-setup
Creating new workload cluster
collecting cluster diagnostics
collecting management cluster diagnostics
⏳ Collecting support bundle from cluster, this can take a while {"cluster": "bootstrap-cluster", "bundle": "mgmt/generated/bootstrap-cluster-2023-01-12T17:28:09+09:00-bundle.yaml", "since": 1673501289233050463, "kubeconfig": "mgmt/generated/mgmt.kind.kubeconfig"}
Support bundle archive created  {"path": "support-bundle-2023-01-12T08_28_10.tar.gz"}
Analyzing support bundle        {"bundle": "mgmt/generated/bootstrap-cluster-2023-01-12T17:28:09+09:00-bundle.yaml", "archive": "support-bundle-2023-01-12T08_28_10.tar.gz"}
Analysis output generated       {"path": "mgmt/generated/bootstrap-cluster-2023-01-12T17:29:11+09:00-analysis.yaml"}
collecting workload cluster diagnostics
Error: waiting for control plane to be ready: executing wait: executing wait: error: timed out waiting for the condition on clusters/mgmt
```

[Tinkerbell boots service](https://anywhere.eks.amazonaws.com/docs/reference/baremetal/netbooting-and-tinkerbell/#tinkerbell-boots-service) に `If the process doesn’t get all the information it wants from the DHCP server, it will time out.` と書いてある。DHCP serverが見つからない? そもそも、このTinkerbellが何をしようとしているか理解できないと進まない・・。

[Customize HookOS for EKS Anywhere on Bare Metal](https://anywhere.eks.amazonaws.com/docs/reference/baremetal/bare-custom-hookos/) この辺やれば直る？

```sh
$ docker logs boots
{"level":"info","ts":1673766065.1512206,"caller":"boots/main.go:125","msg":"starting","service":"github.com/tinkerbell/boots","pkg":"main","version":"79b1d9a"}
{"level":"info","ts":1673766065.185178,"caller":"boots/main.go:138","msg":"serving syslog","service":"github.com/tinkerbell/boots","pkg":"main","addr":"10.200.1.121:514"}
{"level":"info","ts":1673766065.1851978,"caller":"boots/main.go:196","msg":"serving iPXE binaries from local HTTP server","service":"github.com/tinkerbell/boots","pkg":"main","addr":"10.200.1.121/ipxe/"}
{"level":"info","ts":1673766065.1853075,"caller":"boots/main.go:216","msg":"serving dhcp","service":"github.com/tinkerbell/boots","pkg":"main","addr":"0.0.0.0:67"}
{"level":"info","ts":1673766065.1854122,"caller":"boots/main.go:223","msg":"serving http","service":"github.com/tinkerbell/boots","pkg":"main","addr":"10.200.1.121:80"}
{"level":"info","ts":1673766065.1859705,"logger":"github.com/tinkerbell/ipxedust","caller":"ipxedust@v0.0.0-20220908192154-99b8049fc267/ipxedust.go:194","msg":"serving iPXE binaries via TFTP","service":"github.com/tinkerbell/boots","addr":"0.0.0.0:69","timeout":5,"singlePortEnabled":true}
{"level":"error","ts":1673766090.6956465,"caller":"boots/dhcp.go:47","msg":"retry dhcp serve: All attempts fail:\n#1: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#2: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#3: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#4: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#5: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#6: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#7: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#8: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#9: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#10: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use","service":"github.com/tinkerbell/boots","pkg":"main","error":"retry dhcp serve: All attempts fail:\n#1: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#2: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#3: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#4: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#5: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#6: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#7: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#8: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#9: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#10: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use","errorVerbose":"All attempts fail:\n#1: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#2: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#3: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#4: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#5: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#6: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#7: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#8: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#9: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\n#10: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use\nretry dhcp serve\nmain.(*BootsDHCPServer).ServeDHCP\n\tgithub.com/tinkerbell/boots/cmd/boots/dhcp.go:47\nruntime.goexit\n\truntime/asm_amd64.s:1571"}
panic: retry dhcp serve: All attempts fail:
#1: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#2: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#3: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#4: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#5: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#6: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#7: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#8: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#9: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use
#10: serving dhcp: listen udp4 0.0.0.0:67: bind: address already in use

goroutine 372 [running]:
github.com/packethost/pkg/log.Logger.Fatal({{0x186c5aa?, 0xc000286a38?}, 0xc000288810?, 0xc00037ce40?}, {0x1ab6320?, 0xc000286a68}, {0x0, 0x0, 0x0})
        github.com/packethost/pkg@v0.0.0-20210325161133-868299771ae0/log/log.go:121 +0xa6
main.(*BootsDHCPServer).ServeDHCP(0xc000210bb0, {0x7ffcaffd7dcb, 0xa}, {0xc00012fbcc, 0x4, 0x4}, {0xc000626c48, 0x12}, {0xc00012fbd0, 0xc})
        github.com/tinkerbell/boots/cmd/boots/dhcp.go:47 +0x293
created by main.main
        github.com/tinkerbell/boots/cmd/boots/main.go:217 +0x192f
```

## 所感

まず一番大きな所感は、__"ちゃんとTinkerbell iPXE Bootを試そう！"__ ということです。OSプロビジョニングのデファクトになりそうな気もしているので・・。eks-anywhereの前に、まずはプロビジョニングをどれだけ効率よくできるか、ということですね。Edge Kubernetes化はその後かなって思いました。

その他、所感をつらつら並べておきます。

- Tinkerbellのみをサポートしているところをみると、OSを入れる作業は、OSSのTinkerbellでiPXE Bootで複数台一気に入れるのが一般的になりつつあるんだなぁと感じた。
- AWSが推奨するOSはbottlerocketであり、ubuntuやrhelは優先度が落ちている。今後ubuntuベースをサポートするかどうかはわからない。
- VMware(vsphere)での動作が優先されている。VMware(vsphere)では、GitOpsとTerraformでの環境の構築/削除/更新がサポートされているが、Bare Metalは共にサポートされていない。VMware -> CloudStack -> Bare Metal の優先度で対応されており、hypervisor + docker engineが主流になってきている？のかなと思った。
- ARMアーキテクチャは未対応。(OpenShift以外のマネージドサービスでEdge KubernetesでARMをサポートしている企業は見たことがない。ニーズがあまりないのかもしれない。OpenShiftもSingleNodeではARMを非対称にしていて優先度を下げている。)
- 今回のBare Metal Single Node Supportは一部の顧客からのリクエストで対応したもので、大口顧客からのリクエストではない。多くの顧客を有している企業はVMware(vsphere)にてKubertenetesでHighly Availableをサポートしているとも言える。
- Bare Metal Single Node Supportは2022/12/21にリリースプレスがあったばかりの機能で、まだまだドキュメントが不足している。Bere Metalのサポート自体も2022/07からなのでBere Metalサポートの実績やドキュメント自体も少ない。

## 引用

- [Official EKS Anywhere Getting Started](https://anywhere.eks.amazonaws.com/docs/getting-started/)
- [rafay.co amazon-eks-and-eks-anywhere-all-you-need-to-know](https://rafay.co/the-kubernetes-current/amazon-eks-and-eks-anywhere-all-you-need-to-know/)
- [AWS Black Belt Online Seminar](https://pages.awscloud.com/rs/112-TZM-766/images/202109_AWS_Black_Belt_Containers372_Amazon_EKS_Anywhere.pdf)
- [Amazon EKS Anywhere の料金](https://aws.amazon.com/jp/eks/eks-anywhere/pricing/)
- [Amazon EKS Anywhere のよくある質問](https://aws.amazon.com/jp/eks/eks-anywhere/faqs/)
- nCloudsによるeks-anywhereテストデプロイ[youtube動画](https://www.youtube.com/watch?v=x-bEStTKqPM)。プロダクションで使わず一旦動作だけみるなら、この動画の通りにするのが簡単だと思います。