落とし穴に立て札を立てるが如く

ハマりどころの解決が少しでも早くなることを願って書いていきます

AWS EC2でkubeadmを用いたKubernetesクラスターを作ってみたのでメモ

こちらの記事はcloud.config tech blogにもマルチポストします。

はじめに

Kubernetesって動かすと楽しいじゃないですか。
でも、Kubernetesで遊ぼうと思ったら普段はローカルでRancher Desktopからシングルノードのクラスターをワンボタンで作ったりEKSやAKSでマネージドなクラスターを作ったりするのですが、それではちょっと物足りないこともあります。
例えば最新のバージョンのKubernetesを使ってみようとしたら、前述の方法だとどれもすぐには対応していなくて、最新バージョンに対応するのを待つ必要があります。
そのためいつでも最新のバージョンのKubernetesを試せるように、ついでにマルチノードの構成で立ち上げるようにするためにAWS EC2の中にkubeadmでKubernetesを立ち上げ、クラスターを構成する方法を確立しておこうと試みたので今のところ確立できている方法をメモしておきます。
そのうちもっといい方法が確立できるかもしれないのでその時はその時で別途記事にしようと思います。

今回の構成

今回作成した構成は以下のような形です。
Control Plane Nodeが1台、Worker Nodeの数は2台にしましたが実際はお好みで。
コンテナランタイム: runc
CNI: Cilium

作成したリソース

EC2
- name: kubernetestest-dev-app-controlplane
image: Canonical, Ubuntu, 24.04 LTS, amd64 noble image build on 2024-04-23
instanceType: t3.small
keyPair: 新規作成
VPC: kubernetestest-dev-pvc
サブネット: kubernetes-dev-subnet
NSG: kubernetestest-dev-nsg
パブリックIPの自動割り当て: 有効化
- name: kubernetestest-dev-app-worker
image: Canonical, Ubuntu, 24.04 LTS, amd64 noble image build on 2024-04-23
instanceType: t3.micro
keyPair: 新規作成
VPC: kubernetestest-dev-pvc
サブネット: kubernetes-dev-subnet
NSG: kubernetestest-dev-nsg
パブリックIPの自動割り当て: 有効化

インスタンスサイズは、Control Plane用のインスタンスではメモリとCPUがそれぞれ2GiBと2v必要なためt3.smallとしています。
Worker Node用では最低限で構わないのでt3.microにしています。
また、今回は学習用リソースとして作成しているため、インスタンスにローカルから直接sshするためにパブリックIPの自動割り当てを有効にいています。

VPC
- name: kubernetestest-dev-vpc
IPv4 CIDR: 10.0.0.0/16

Subnet
- name:
VPC: kubernetestest-dev-vpc
IPv4 Subnet CIDR: 10.0.0.0/16

Route Table
- name: kubernetes-dev-rtb
VPC: kubernetestest-dev-vpc
routes:
- to: 10.0.0.0/16
target: local
- to: 0.0.0.0/16
target: kubernetestest-dev-igw

IGW
- name: kubernetestest-dev-igw
VPC: kubernetestest-dev-vpc

NSG
- type: ssh
source: 0.0.0.0/0
- type: customTCP
source: 0.0.0.0/0
port: 6443
- type: customTCP
source: 10.0.0.0/16
port: 10250

NSGではこちらを参考に許可する通信を規定しています。
本当はControl PlaneとWorker Nodeで要件は異なりますが、今回はひとまとめにしています。
ssh用の22番、API Serverへのアクセス用の6443番、Kubernetes Nodeのkubelet間のやりとりのための10250番ポートを開放してあります。
特に10250番はクラスター内部の通信であるため、通信元は同じVPC内部に限定しています。

実行したコマンド

リソースが一通り作成できれば、EC2 Instance Connect等を用いてインスタンスssh接続します。
Control Plane NodeとなるEC2インスタンスでは起動後に以下のコマンドを実行してコンテナランタイム、Kubernetes、CNIをインストールしています。
実行時は先にControl Plane Nodeのコマンドから実行してください。

# Dockerのインストール  
sudo apt-get update  
sudo apt-get install -y apt-transport-https ca-certificates curl  
sudo apt-get update  
sudo apt-get install -y docker.io  
  
# Dockerの開始  
sudo systemctl start docker  
sudo systemctl enable docker  
  
# Kubernetesのインストール  
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg  
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list  
sudo apt-get update  
sudo apt-get install -y kubelet kubeadm kubectl  
sudo apt-mark hold kubelet kubeadm kubectl  
  
# Kubernetesの起動  
sudo kubeadm init --pod-network-cidr=10.0.0.0/16  
  
# kubeconfigのコピー  
mkdir -p $HOME/.kube  
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config  
sudo chown $(id -u):$(id -g) $HOME/.kube/config  
  
# CNIのインストール  
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)  
CLI_ARCH=amd64  
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi  
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}  
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum  
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin  
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}  
cilium install  

Worker NodeとなるEC2インスタンスでは以下のコマンドを実行しています。KubernetesのインストールコマンドまではControl Plane Nodeと同様です。
クラスターに加入するコマンドは、Control Plane Nodeの立ち上げ時に最後のほうでkubeadm join ~~~といった形で表示されるのでそれをそのままWorker Nodeで打ち込んでください。

# Dockerのインストール  
sudo apt-get update  
sudo apt-get install -y apt-transport-https ca-certificates curl  
sudo apt-get update  
sudo apt-get install -y docker.io  
  
# Dockerの開始  
sudo systemctl start docker  
sudo systemctl enable docker  
  
# Kubernetesのインストール  
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg  
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list  
sudo apt-get update  
sudo apt-get install -y kubelet kubeadm kubectl  
sudo apt-mark hold kubelet kubeadm kubectl  
  
# Kubernetesクラスターに加入  
sudo kubeadm join <Control PlaneノードのプライベートIP>:6443 --token <トークン> --discovery-token-ca-cert-hash sha256:<hash値>  

動作確認

各ノードでコマンドを実行し、成功していれば以下のような出力が得られるかと思います。
Control Plane Nodeでkubectl get nodeを実行

どこのノードでもsudo docker infoコマンドを実行すると現在使用しているDockerのバージョンやコンテナランタイムが表示されるかと思います。

Todo

このクラスターではまだメトリクスサーバーがインストールされていません。
そのためkubectl topコマンドやHPAが使えません。
スケーリングの動きを確かめようと思ったらこのあたりの機能が使える必要があるので、また今度インストールも試してみます。

今回動作確認が取れたコンテナランタイムはruncで、しかもそのためにインストールしたパッケージは少し古めのdocker.ioでした。
推奨されるコンテナランタイムはcotainerdかcri-oなのですが、今回は動作確認が取れなかったので今度動作することを確認したいと思います。

また、今回はEC2やネットワーク系のリソース、Kubernetesのインストール等を手動で行っていますが、1コマンドでできたほうが楽なので今度Terraform化を試してみます。

おわりに

今回は勉強がてらEC2でKubernetesクラスターをkubeadmベースで作成してみました。
この方法が確立できていれば後でいろいろ検証するのに役立ちそうに思います。
ただ、今回はそれなりに苦労した割に妥協した点も多くあるので、これからもっと構成を洗練させていければと考えています。
自分と同様にKubernetesクラスターをkubeadmベースで作ってみたいという方の助けになれば幸いです。