概要
AWSのEC2インスタンスで、kubeadmとAnsible Playbookを使ってKubernetesを作ってみたので、手順を本記事にまとめます。
本記事の対象者
- マネージドなKubernetesサービス(EKS,AKS,GKE等)以外でK8sを作ってみたい
- KubernetesをMinikubeやkindではなく、物理マシンでcontrol planeとworker nodeで分けて自力で作ってみたい
- しかしながら自宅にKubernetesの構築を手軽に試せるような物理マシンがない
Kubernetes全般の環境情報
- OS : Ubuntu 22.04
- コンテナランタイム : containerd
- Kubernetesのバージョンは以下
$ kubectl version
Client Version: v1.28.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.5
$ kubelet --version
Kubernetes v1.28.2
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"28", GitVersion:"v1.28.2", GitCommit:"89a4ea3e1e4ddd7f7572286090359983e0387b2f", GitTreeState:"clean", BuildDate:"2023-09-13T09:34:32Z", GoVersion:"go1.20.8", Compiler:"gc", Platform:"linux/amd64"}
Kubernetes構築手順
(1) EC2インスタンスの作成
あくまでお試し用なので、control planeとworker node用のインスタンスをそれぞれ1台作ることにします。
インスタンス情報
AMI
- ID
- ami-07c589821f2b353aa
- name
- amazon/ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20231207
インスタンスタイプ
- control plane
- t3a.small : 2vCPU, 2GiBメモリ
- worker node
- t3.micro : 2vCPU, 1GiBメモリ
kubeadmのインストールにも記載があるように、control planeを構築するEC2インスタンスには2GiBのメモリが必要でした。試しにそれよりもメモリが小さいt3.nano
やt3.micro
でやってみましたが、kubeadm init
の実行途中にメモリ不足とみなされてエラーになってしまうようです。
一方でワーカーノードだと2GiB未満のメモリのマシンでもkubeadm join
に成功しました。(作れはしても、すぐにリソースが枯渇してしまうはずですが...)
(2) セキュリティグループ設定
Kubernetes公式の以下のドキュメントに従って設定します。
本来はcontrol planeとworker nodeとは別々でセキュリティグループルールを設定する必要がありますが、筆者は横着して両方のインスタンスに共通で以下を設定しました(6443は本当はworker nodeでは開ける必要はないです...)
通信の方向 | プロトコル | ポート番号 | 用途 | 通信許可するIP範囲 |
---|---|---|---|---|
inbound | TCP | 6443 | kube-apiserverで利用 | 全てのノード |
inbound | TCP | 10250 | kubeletで利用 | 全てのノード |
inbound | TCP | 30000-32767 | NodePortのServiceリソースで利用 | 全てのノード |
outbound | 全て | 全て | インスタンスから外部にアクセス | 0.0.0.0/0 |
※ 上記はKubernetesのシステムで最低限開ける必要があるポートです。最終的にはCNI(Container Network Interface)のインストールも必要になりますが、使うCNIによって適宜開けるプロトコルとポートの追加が必要です。
(3) Kubernetesの構築
kubeadmのインストールを参考にして、Ansibleでスクリプトを作りました。詳しくは筆者作成の下記GitHubをご覧ください。
こちらのツールを使うにはAnsible Playbookを実行するマシンが別途必要となります。実行するマシンが(ネットワーク的な意味での)存在する場所によって、それに応じたSSHのセキュリティルールの追加やインターネットゲートウェイの作成等が必要となります。
README.mdに従い事前準備ができたら、Ansible Playbookでcontrol planeとworker nodeを構築します。
control planeの構築
ansible-playbook site_control-plane.yml
worker nodeの構築
ansible-playbook site_worker.yml
構築完了後、control planeノードにSSH等で接続すると、以下のようにKubernetesが出来上がっているのを確認できます。今回ケースでは、control planeのホスト名はip-10-0-0-5
です。
(control planeで実行)
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5dd5756b68-r9gdq 0/1 Pending 0 41m
kube-system coredns-5dd5756b68-zhksc 0/1 Pending 0 41m
kube-system etcd-ip-10-0-0-5 1/1 Running 0 42m
kube-system kube-apiserver-ip-10-0-0-5 1/1 Running 0 42m
kube-system kube-controller-manager-ip-10-0-0-5 1/1 Running 0 42m
kube-system kube-proxy-96bcm 1/1 Running 0 41m
kube-system kube-proxy-ghl74 1/1 Running 0 39m
kube-system kube-scheduler-ip-10-0-0-5 1/1 Running 0 42m
(4) CNIのインストール
Kubernetes内のネットワークを設定するためのツールをインストールします。今回はFlannelを使ってみます。(後述しますがCalicoだとうまくいかずに断念しました...)
セキュリティグループにFlannel関連のルールを追加
こちらを参考に、Flannelがノード間で通信を行えるようにするため、以下のルールを追加します。
通信の方向 | プロトコル | ポート番号 | 用途 | 通信許可するIP範囲 |
---|---|---|---|---|
inbound | UDP | 8472 | flannel overlay network - vxlan backend | 全てのノード |
※ Flannelの設定によっては8472ではなく、8285を開ける必要があるかもしれません
Flannelのデプロイ
Flannelのマニフェストkube-flannel.yml
で編集が必要な部分があるため、一旦control planeにマニフェストをダウンロードします。
(control planeで実行)
sudo apt -y install wget
wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
ダウンロードしたkube-flannel.yml
で、PodのCIRDの値をkubeadm init
コマンドで設定したものに変更します。
vi kube-flannel.yml
...
net-conf.json: |
{
"Network": "10.244.0.0/16", # 設定したPodのCIDRに変更
"Backend": {
"Type": "vxlan"
}
}
...
修正したFlannelのマニフェストをapplyします。
$ kubectl apply -f kube-flannel.yml
(実行結果)
namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
Flannelのリソースが新たにデプロイされているのを確認できれば成功です。
$ kubectl get pod -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-c2d6d 1/1 Running 0 37s
kube-flannel-ds-cntpk 1/1 Running 0 37s
以上でEC2インスタンス上にKubernetesを構築できました🙌
構築で行き詰まった点
ほぼ筆者の備忘メモなので読み飛ばしていただいてもいいです。
containerdの設定不備でkube-apiserverが立ち上がらない...
今回はコンテナランタイムでcontainerdを使いましたが、aptでcontainerdをインストールしただけのデフォルトの状態だと、kube-systemのPodが以下のようにリスタートを繰り返してKubernetesが正常稼働しない事態になりました。
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5dd5756b68-8jgt4 0/1 Pending 0 3m25s
kube-system coredns-5dd5756b68-9wq97 0/1 Pending 0 3m25s
kube-system etcd-ip-10-0-0-5 0/1 Running 106 (9s ago) 2m25s
kube-system kube-apiserver-ip-10-0-0-5 0/1 Running 80 (25s ago) 4m40s
kube-system kube-controller-manager-ip-10-0-0-5 0/1 Running 96 (9s ago) 2m43s
kube-system kube-proxy-8bwp7 1/1 Running 3 (3s ago) 3m25s
kube-system kube-scheduler-ip-10-0-0-5 0/1 Running 108 (9s ago) 5m15s
調査と試行錯誤した結果、「今回使ったAMIのデフォルト設定だとsystemdがcgroupドライバーを使うようにする必要があるのに、containerdの設定はそうなっていない」ことがわかりました。そこで以下の対応を行い解決できました。
containerdの設定ファイルを作成
$ sudo su -
# containerd config dump > /etc/containerd/config.toml
# logout
作成した設定ファイルで、SystemdCgroup
の値がfalse
になっているのをtrue
に変更する
sudo vi /etc/containerd/config.toml
以下のように変更
...
SystemdCgroup = true
...
containerdおよびkubeleteのサービスをrestart
sudo systemctl restart containerd
sudo systemctl restart kubelet
参考情報
CNIがCalicoだと異なるノードのPodが通信できない(未解決)
Quickstart for Calico on Kubernetesや筆者記事にしたがってCNIをCalicoで作ろうとしましたが、どうやっても異なるノードのPod同士が通信できなくて断念しました...
試してみたことは以下です。
- Calicoバージョンを以下のように変えてみる
- v3.27.0(現時点の最新), v3.26.4, v3.24.6
- Installationリソースのパラメータ
spec.calicoNetwork.ipPools[].encapsulation
をVXLANCrossSubnet
からIPIP
に変えてみる
怪しいと思っている部分はネットワークのルーティングに関する設定で、以下に具体的に説明します。
既にCalicoの構築に成功しているK8sクラスター上では、vxlan.calico
というネットワークインターフェースがあり、ここを経由するネットワーク経路が以下のように定義されていました。
$ sudo ip route | grep vxlan
10.128.75.0/26 via 10.128.75.1 dev vxlan.calico onlink
...
一列目のCIDR(10.128.75.0/26等)は、PodのCIDRの範囲に含まれています。
このルートがEC2インスタンスで構築した場合だと存在せず、代わりに以下のように ens5
というAWSサブネットのインターフェースに紐づけられています。
$ sudo ip route | grep onlink
192.168.91.64/26 via 10.0.0.5 dev ens5 proto 80 onlink
ちなみにEC2インスタンスのノード上にもvxlan.calico
のインターフェースは作られているのですが、それを使うルートは一つも定義されていません。
ヤマ勘なので間違っている可能性が高いのですが「本当はこのルートはens5
ではなくて、vxlan.calico
に作られるべきなのでは?」というのが現状の考えです。
これに関連しそうな内容をGitHubのissueで見つけました。まだあまりやり取りされていないようなので、今後の解決を期待したいです。