備忘録置き場

自分用の備忘録置き場です。やったことを記録していきます。

kubeadmを使用してkubernetesを構築

kubeadmを使用してkubernetes環境を構築してみました。

基本的に以下の公式ドキュメントに従って進めていきます。

kubernetes.io

環境情報

  CPU Mem OS hostname IP
コントロールプレーン(1台) 2vcpu 4GiB ubuntu 20.04 MyVMmaster 10.0.0.5
ワーカーノード(3台) 2vcpu 4GiB ubuntu 20.04 MyVMworker1~3 10.0.0.6~8

VMはAzure Virtual Machineで作成

  • kubeadm:v1.30.0
  • kubectl:v1.30.0
  • Kubernetes:v1.30.0

基盤準備

swapの無効化

swap…メモリが不足した際にディスクのswap領域を使用する機能。
OS側で予期せぬ動き(swapへの読み書き)があると、kubernetesの世界でコンテナのメモリの管理が出来なくなってしまうから無効化するのかな?みたいな理解。

# swap領域の確認
azureuser@MyVMworker1:~$ sudo swapon --show
azureuser@MyVMworker1:~$ cat /etc/fstab
# CLOUD_IMG: This file was created/modified by the Cloud Image build process
UUID=xxx       /        ext4   defaults,discard     0 1
UUID=xxx  /boot/efi       vfat    umask=0077      0 1
/dev/disk/cloud/azure_resource-part1    /mnt    auto    defaults,nofail,x-systemd.requires=cloud-init.service,_netdev,comment=cloudconfig    0       2

→swap領域が存在しないようなのでここはスキップ。

MACアドレスとproduct_uuidの確認

MACアドレスとproduct_uuidがクラスタを構成する全ノードでユニークであることを確認。

ip link
cat /sys/class/dmi/id/product_uuid
ポートの解放

各ノードで使われるポートを解放しないといけない。

対象のポートは以下を確認。

kubernetes.io

今回はNSGファイアウォールでポートの制限等かけているわけではないので、問題無さそう。
ちなみに確認方法は以下。

# 確認対象サーバ:6443ポートでLISTENしておく
azureuser@MyVMmaster:~$ nc -l -k -p 6443
# 別サーバ:確認対象サーバへ接続確認
azureuser@MyVM:~$ nc -v MyVMmaster 6443
Connection to MyVMmaster 6443 port [tcp/*] succeeded!
コンテナランタイムのインストール

今回はコンテナランタイムとしてdockerを使用。

dockerエンジンのインストール手順に従い、全ノードでdockerエンジンのインストールを行う。

※説明は割愛

docs.docker.com

以上で基盤の準備は完了。

kubelet、kubeadm、kubectlのインストール

※全ノードで実施
※実行ユーザはあまり考慮していない

手順

  1. kubernetesリポジトリを利用するために必要なパッケージのインストール
  2. kubernetesリポジトリ用のGPGキーをダウンロード
  3. kubernetesリポジトリの追加
  4. kubelet、kubeadm、kubectlのインストール
# 必要なパッケージのインストール
root@MyVMmaster:~# apt-get install -y apt-transport-https ca-certificates curl gpg
Reading package lists... Done
Building dependency tree
Reading state information... Done
...
Unpacking apt-transport-https (2.0.10) ...
Setting up apt-transport-https (2.0.10) ...

# GPGキーのダウンロード
# /etc/apt/keyringsフォルダはdockerインストール時に作成済
root@MyVMmaster:~# curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
root@MyVMmaster:~# ls /etc/apt/keyrings
docker.asc  kubernetes-apt-keyring.gpg

# kubernetesリポジトリの追加
azureuser@MyVMmaster:~$ 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
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /

# kubelet、kubeadm、kubectlのインストール
azureuser@MyVMmaster:~$ sudo apt-get update
azureuser@MyVMmaster:~$ sudo apt-get install -y kubelet kubeadm kubectl
azureuser@MyVMmaster:~$ sudo apt-mark hold kubelet kubeadm kubectl

# kubeadmバージョン確認
azureuser@MyVMmaster:~$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"30", GitVersion:"v1.30.0", GitCommit:"xxx", GitTreeState:"clean", BuildDate:"2024-04-17T17:34:08Z", GoVersion:"go1.22.2", Compiler:"gc", Platform:"linux/amd64"}

# kubeletステータス確認
azureuser@MyVMmaster:~$ systemctl status kubelet -n0
● kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor pr>
    Drop-In: /usr/lib/systemd/system/kubelet.service.d
             mq10-kubeadm.conf
     Active: inactive (dead)
       Docs: https://kubernetes.io/docs/

# kubectlバージョン確認
azureuser@MyVMmaster:~$ kubectl version --client
Client Version: v1.30.0
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3

kubeadm実行

準備が出来たので、いよいよkubeadmを実行してクラスタを構築していく。

※全ノードで実施

kubeadm init実行

※この段階では実施していないが、kube initコマンドでpodのネットワークcidrの引数指定が必要のため注意。(後述のpodのネットワークプラグインのインストール参照)

root@MyVMmaster:~# kubeadm init
[init] Using Kubernetes version: v1.30.0
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR CRI]: container runtime is not running: output: time="2024-04-29T17:01:17Z" level=fatal msg="validate service connection: validate CRI v1 runtime API for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
, error: exit status 1
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
To see the stack trace of this error execute with --v=5 or higher

エラー発生。

調べると、以下の方法で対応が必要とのこと。

github.com

containerdの設定ファイルを削除して再起動。

root@MyVMmaster:~# rm /etc/containerd/config.toml
root@MyVMmaster:~# systemctl restart containerd

再度kubeadm init実行。

root@MyVMmaster:~# kubeadm init
[init] Using Kubernetes version: v1.30.0
...

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.0.5:6443 --token xxx \
        --discovery-token-ca-cert-hash sha256:xxx

今度は成功。
ここで出力されたtokenを用いて、後ほどworkerノードを追加する形となる。
workerノードのjoin参照)

出力にもある通り、設定ファイルをホームディレクトリに配置する。

root@MyVMmaster:~# mkdir -p $HOME/.kube
root@MyVMmaster:~# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# KUBECONFIG定義
root@MyVMmaster:~# export KUBECONFIG=/etc/kubernetes/admin.conf

podのネットワークプラグインのインストール

pod同士を通信させるために、podネットワークアドオンのインストールが必要。
これが無いと、以下のようにCoreDNSと呼ばれるクラスタ内の名前解決をしてくれるリソースが立ち上がらない。

root@MyVMmaster:~# kubectl get pod -A | grep coredns
kube-system   coredns-7db6d8ff4d-mklkh             0/1     Pending   0             83m
kube-system   coredns-7db6d8ff4d-pddv8             0/1     Pending   0             83m

今回は以下を参考に、Calicoをインストール。

※control-planeのみで実施

docs.tigera.io

# calico公式から引っ張ってきたマニフェストを使用し、kubernetesリソース作成
root@MyVMmaster:~# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/tigera-operator.yaml
namespace/tigera-operator created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
...
clusterrolebinding.rbac.authorization.k8s.io/tigera-operator created
deployment.apps/tigera-operator created
root@MyVMmaster:~# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/custom-resources.yaml
installation.operator.tigera.io/default created
apiserver.operator.tigera.io/default created

# リソース確認
root@MyVMmaster:~# watch kubectl get pods -n calico-system

→calico-systemというnamespaceにpodが立ち上がるはず・・・

Every 2.0s: kubectl get pods -n cali...  MyVMmaster: Mon Apr 29 19:11:53 2024

No resources found in calico-system namespace.

だが、いつまで待っても立ち上がらない。

よく調べると、calicoではデフォルトのpodのネットワークcidrが以下のように192.168.0.0/16となっているため、kubeadm init で指定した引数--pod-network-cidrと同じものに変えないといけないらしい。

# マニフェストをローカルにダウンロード
root@MyVMmaster:~# wget https://projectcalico.docs.tigera.io/manifests/custom-resources.yaml
# カスタムリソース確認
# podのネットワークcidrが192.168.0.0/16になっている
root@MyVMmaster:~# cat custom-resources.yaml
# This section includes base Calico installation configuration.
# For more information, see: https://projectcalico.docs.tigera.io/master/refe  rence/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 192.168.0.0/16
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all()
...

しかし先ほど実行したkubeadm initではそのような引数は指定しておらず・・・
構築したクラスタのpodネットワークcidrがどうなっているか、見方も分からず。

結局kubeadmをアンインストールし、再度ネットワークcidrを設定してインストールし直すことにした。

kubeadm reset
sudo apt-get purge kubeadm kubectl kubelet kubernetes-cni kube*  
sudo apt-get autoremove
sudo rm -rf ~/.kube

※このあたりは「kubernetes アンインストール」で調べて出てきた手順を適当に実行しただけなので、消し忘れの設定等あるかも。

アンインストールが済んだら、kubelet、kubeadm、kubectlのインストールから実施。

その後、今度はpodネットワークcidrを指定してkubeadm init 実行。

kubeadm init \
--pod-network-cidr 192.168.0.0/16

そして再びcalicoをインストールし、確認。

root@MyVMmaster:~# watch kubectl get pods -n calico-system

Every 2.0s: kubectl get pods -n cali...  MyVMmaster: Mon Apr 29 19:51:54 2024

NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-7569b96694-qt7wm   1/1     Running   0          4m23s
calico-node-zz8cv                          1/1     Running   0          4m23s
calico-typha-7bdc59757b-2gdlc              1/1     Running   0          4m23s
csi-node-driver-7cfhw                      2/2     Running   0          4m23s

今度は上手くいった。
corednsのpodも全てRunningになっていることを確認。

root@MyVMmaster:~# kubectl get pod -A | grep coredns
kube-system        coredns-7db6d8ff4d-b4s88                   1/1     Running   0          8m37s
kube-system        coredns-7db6d8ff4d-cn9p9                   1/1     Running   0          8m37s

workerノードのjoin

最後に、workerノードをクラスタにjoinさせる。

※workerノード全てで実施

root@MyVMworker1:~# kubeadm join 10.0.0.5:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx
~kubeadm init時と同様のエラーが出たため対処~
root@MyVMworker1:~# rm /etc/containerd/config.toml
root@MyVMworker1:~# systemctl restart containerd
# join
root@MyVMworker1:~# kubeadm join 10.0.0.5:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx
[preflight] Running pre-flight checks
...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

最後に、control-planeでノードの確認。

root@MyVMmaster:~# kubectl get node
NAME          STATUS   ROLES           AGE     VERSION
myvmmaster    Ready    control-plane   30m     v1.30.0
myvmworker1   Ready    <none>          3m23s   v1.30.0
myvmworker2   Ready    <none>          66s     v1.30.0
myvmworker3   Ready    <none>          18s     v1.30.0

全てのノードが認識されており、ステータスもReadyになっているのでOK!

動作確認

最後に軽くクラスタの動作確認。

# pod作成
root@MyVMmaster:~# kubectl run nginx --image=nginx --port=80
pod/nginx created

# 作成したpodの状態確認
root@MyVMmaster:~# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP              NODE          NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          9s    192.168.235.6   myvmworker2   <none>           <none>

# 作成したpodのserviceのタイプをNodePortにする
root@MyVMmaster:~# kubectl expose pod nginx --type=NodePort
service/nginx exposed

# serviceが作成されたことを確認
root@MyVMmaster:~# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        121m
nginx        NodePort    10.105.194.131   <none>        80:31681/TCP   6m5s

# 疎通確認
root@MyVMmaster:~# curl -I http://10.0.0.7:31681/
HTTP/1.1 200 OK
Server: nginx/1.25.5
Date: Mon, 29 Apr 2024 21:44:32 GMT

まとめ

今まで業務でkubernetesは触ってきましたが、実際に環境を一から作るのは初めてだったので良い勉強になりました。

次はこの一連の流れをansible化してみたいです。