kubeadm
を利用して、kubernetes clusterをゼロから作成してみます。 kubeadm
とは、kubernetes ClusterのBootstrap Toolとなっています。
Kubeadm is a tool built to provide best-practice "fast paths" for creating Kubernetes clusters. It performs the actions necessary to get a minimum viable, secure cluster up and running in a user friendly way. Kubeadm's scope is limited to the local node filesystem and the Kubernetes API, and it is intended to be a composable building block of higher level tools.
環境情報
作成するkubernetes Cluster構成と、各バージョンは下記となります。
Cluster構成
- Master Node数
- 1台
- Worker Node数
- 1台
- Master Node数
各バージョン
- OS
- Ubuntu 20.04.1 LTS
- kubernetes
- 1.20.0-00
- コンテナ・ランタイム
- Docker CE 5:19.03.11
- CNI
- calico/node:v3.17.1
- OS
環境はGCEを利用しました。
kubeadmのインストール
Cluster内の全ノードに対して、必要な事前設定・確認を実施し、kubeadmをインストールします。下記は公式ドキュメント。
MACアドレスとproduct_uuidの確認
各ノードにて、MACアドレスとproduct_uuidが一意であることを確認します。
$ ip address show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc mq state UP group default qlen 1000 link/ether 42:01:0a:92:00:02 brd ff:ff:ff:ff:ff:ff inet 10.146.0.2/32 scope global dynamic ens4 valid_lft 2607sec preferred_lft 2607sec inet6 fe80::4001:aff:fe92:2/64 scope link valid_lft forever preferred_lft forever $ $ sudo cat /sys/class/dmi/id/product_uuid 759f0cc0-24c3-54dd-22d7-ac580e9e0568
スワップの無効化
スワップを無効化しておきます。確認。
$ free -h | grep -i swap Swap: 0B 0B 0B
br_netfilterモジュールをロードして、iptablesとのフィルタリングを有効化
br_netfileter(bridge network filter)
モジュールの役割を理解するために、Dockerネットワークについておさらいします。以下サイトの説明が、非常に簡潔で分かりやすかったです。
広く使われているコンテナ化技術「Docker」を例として、コンテナネットワークの概要を説明する。コンテナは一般的にLinux OS上で起動する。このホストは「コンテナホスト」と呼ばれ、各コンテナはコンテナホスト内の論理的なネットワークに接続されている。
具体的には、コンテナホスト内に論理ブリッジ(Linuxブリッジ)が存在し、各コンテナは、LinuxのNetwork Namespace機能により隔離され、仮想ネットワークインターフェース(veth)でこのブリッジに接続される。これによって、コンテナ内のプロセスから見ると、自分専用のネットワークインターフェースがあるように見えている。
そして、コンテナホスト自身が持つネットワークインターフェースもこのブリッジに接続されているため、コンテナで起動するサービスを外部公開する場合には、コンテナホストに対するアクセスがLinuxカーネルのiptablesを使ってNATされる。
<コンテナNWの課題と展望>Kubernetes環境のネットワークの基礎を学ぶ
そして、この br_netfileter(bridge network filter)
は、iptables上のルールを論理ブリッジに統合するような働きをするみたいです。
現在の設定を確認。
$ lsmod | grep br_netfilter
モジュールのロード。
$ sudo modprobe br_netfilter
以下コンフィグファイルを設定することで、OS起動時にモジュールをロードし、iptablesとのフィルタリングを有効とするようカーネルパラメーターを設定します。
$ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf br_netfilter EOF $ $ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF
パラメーターを再読み込みします。
$ sudo sysctl --system
確認。
$ lsmod | grep br_netfilter br_netfilter 28672 0 bridge 176128 1 br_netfilter
利用するネットワークポートの確認
kubernets cluster内で、利用するポートを、疎通可能としておきます。利用ポート情報は以下。
Container Runtimeのインストール
コンテナ実行環境として、Docker CEをインストールし、起動させておきます。公式ドキュメントにて言われるまま、作業しています。
$ # (Install Docker CE) $ ## Set up the repository: $ ### Install packages to allow apt to use a repository over HTTPS $ sudo apt-get update && sudo apt-get install -y \ apt-transport-https ca-certificates curl software-properties-common gnupg2 $ $ # Add Docker's official GPG key: $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add - $ $ # Add the Docker apt repository: $ sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" $ $ # Install Docker CE $ sudo apt-get update && sudo apt-get install -y \ containerd.io=1.2.13-2 \ docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) \ docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) $ $ ## Create /etc/docker $ sudo mkdir /etc/docker $ $ # Set up the Docker daemon $ cat <<EOF | sudo tee /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2" } EOF $ $ # Create /etc/systemd/system/docker.service.d $ sudo mkdir -p /etc/systemd/system/docker.service.d $ $ # Restart Docker $ sudo systemctl daemon-reload $ sudo systemctl restart docker $ sudo systemctl enable docker $
正しく起動できていることを確認。
$ sudo systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2021-01-14 16:40:25 UTC; 46s ago TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 4875 (dockerd) Tasks: 10 Memory: 36.0M CGroup: /system.slice/docker.service └─4875 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.090713320Z" level=warning msg="Your kernel> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.090724966Z" level=warning msg="Your kernel> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.090733013Z" level=warning msg="Your kernel> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.091795132Z" level=info msg="Loading contai> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.226503101Z" level=info msg="Default bridge> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.275624298Z" level=info msg="Loading contai> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.298577391Z" level=info msg="Docker daemon"> Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.298661273Z" level=info msg="Daemon has com> Jan 14 16:40:25 kubeadm-master systemd[1]: Started Docker Application Container Engine. Jan 14 16:40:25 kubeadm-master dockerd[4875]: time="2021-01-14T16:40:25.332888590Z" level=info msg="API listen on >
Kubernetes v1.20. 以降にて、コンテナランタイムでDockerを利用することは非推奨となりました。以下、公式のアナウンス資料です。
Don't Panic: Kubernetes and Docker
Kubernetes is deprecating Docker as a container runtime after v1.20.
Docker-produced images will continue to work in your cluster with all runtimes, as they always have.
When Docker runtime support is removed in a future release (currently planned for the 1.22 release in late 2021) of Kubernetes it will no longer be supported and you will need to switch to one of the other compliant container runtimes, like containerd or CRI-O. Just make sure that the runtime you choose supports the docker daemon configurations you currently use (e.g. logging).
とは言うものの、Dockerコンテナイメージは継続して利用できます。Kubernetes Clusterにて利用されるコンテナラインタイムを、containerdやCRI-Oといったランタイムに変更する必要が、将来バージョンであるとの事です。
Kubernetes内のコンテナランタイムの機能としては、コンテナのpullとrunが出来れば良いのに対し、Dockerの機能はtoo muchであり、且つCRI(Container Runtime Interface)にも準拠していないため色々しんどいんだ、みたいなことが書いてありました。
kubeadm, kubelet, kubectlのインストール
kubeadm、kubelet、kubectlをインストールします。下記が各モジュールの説明です。
- kubeadm
- Kubernetes Cluster作成のためのBootstrap Tool
- kubelet
- kubectl
公式ドキュメント通りにインストール。なお、各モジュールのバージョンを揃える必要があります。
$ sudo apt-get update && sudo apt-get install -y apt-transport-https curl $ $ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - $ $ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list deb https://apt.kubernetes.io/ kubernetes-xenial main EOF $ $ sudo apt-get update $ sudo apt-get install -y kubelet=1.20.0-00 kubeadm=1.20.0-00 kubectl=1.20.0-00 $ sudo apt-mark hold kubelet kubeadm kubectl
確認。
$ kubelet --version Kubernetes v1.20.0 $ $ kubeadm version kubeadm version: &version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.0", GitCommit:"af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38", GitTreeState:"clean", BuildDate:"2020-12-08T17:57:36Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"} $ $ kubectl version Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.0", GitCommit:"af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38", GitTreeState:"clean", BuildDate:"2020-12-08T17:59:43Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
kubeletの起動
kubeletプロセスの自動起動設定を入れておきます。
$ sudo systemctl daemon-reload $ sudo systemctl restart kubelet $ sudo systemctl enable kubelet
ステータスを確認すると正しく起動できていないですが、この後kube-apiを作成すると、正しく起動できるようになります。
$ sudo systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled) Drop-In: /etc/systemd/system/kubelet.service.d └─10-kubeadm.conf Active: activating (auto-restart) (Result: exit-code) since Thu 2021-01-14 17:15:09 UTC; 7s ago Docs: https://kubernetes.io/docs/home/ Process: 11120 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS > Main PID: 11120 (code=exited, status=255/EXCEPTION) Jan 14 17:15:09 kubeadm-master systemd[1]: kubelet.service: Failed with result 'exit-code'.
コンテナランタイムでDockerを利用している場合には不要ですが、その他ランタイムを利用している場合、kubeletで利用するcgroup driverを明示的に指定する必要があるそうです。
Configure cgroup driver used by kubelet on control-plane node
kubeadm設定
インストールしたkubeadmを利用して、kubernetes clusterの初期セットアップをしていきます。公式ドキュメントはこちら。
Creating a cluster with kubeadm
kubeadm initの実行
マスターノード上で、Cluster初期化処理を行います。以下を実行します。
$ sudo kubeadm init [init] Using Kubernetes version: v1.20.2 [preflight] Running pre-flight checks ... [addons] Applied essential addon: CoreDNS [addons] Applied essential addon: kube-proxy 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.146.0.2:6443 --token m8sf51.jiip1suec225lyai \ --discovery-token-ca-cert-hash sha256:a70c0361997bbefb6c77f74d9c85d13a6feb830ae52ff1d1b4ebe4c6567e5279
今回の構成ではオプションを何も指定していませんが、作成するClusterの環境により、オプションを付与して実行します。
- オプションたち
-control-plane-endpoint
-pod-network-cidr
- Cluster内のNodeネットワークアドレス帯とPodネットワークアドレス帯は重複不可となる
- 利用する各CNIにより、デフォルトのPodネットワークアドレス帯が定められているため、その情報を基にCIDR情報を設定
- Installing a Pod network add-on
- ただし、現在kubeadmで推奨されているCNIは
Calico
のみのようで、Calicoで利用されるデフォルトPodネットワークアドレス帯は192.168.0.0/16
となる
-cri-socket
- コンテナランタイムでDocker以外を利用する場合、利用するランタイムのUNIX Domain Socketを指定
- Path to Unix domain socket
-apiserver-advertise-address
- kube-apiサーバーとして利用されるIPアドレスを指定
- 通常はデフォルトゲートウェイに関連付けられたネットワークインターフェースのIPを利用するため、それが嫌の場合に明示的に指定
Initializing your control-plane node
kubeconfigの設定
マスターノード上でkubectlを利用できるよう、kubeconfigの設定をいれておきます。サンプルとなるコンフィグファイルを提供してくれるため、そのファイルをHOMEディレクトリ配下にコピーします。
以下の、 kubeadm init
実行時の出力結果のコマンドを実行。
$ mkdir -p $HOME/.kube $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config $ sudo chown $(id -u):$(id -g) $HOME/.kube/config
確認。
$ kubectl cluster-info Kubernetes control plane is running at https://10.146.0.2:6443 KubeDNS is running at https://10.146.0.2:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
CNI(Container Network Interface)の作成
CNI(Container Network Interface)として、 Calico
の設定を実施します。CNIとは、Kubernetes Cluster内で、ノード間のコンテナ通信を管理する働きをします。単一ノード内でのコンテナ通信は、上記で引用したように、仮想的なブリッジを作成し、各コンテナに対し仮想的ネットワークインターフェースを付与することで実現しています。kubernetesでは複数ノードにコンテナが分散しているため、ノードを跨いだコンテナ間通信や、コンテナ間でのIPアドレスを重複制御する必要があり、CNIがその機能を提供してくれています。
まず、現在のノード情報を確認します。STATUSはNotReadyとなっています。
$ kubectl get node NAME STATUS ROLES AGE VERSION kubeadm-master NotReady control-plane,master 7m12s v1.20.0
Calicoを作成するには、公式サイトにて公開されているマニフェストファイルをapplyすることになります。以下、公式の参考マニュアル。
Install Calico networking and network policy for on-premises deployments
実行。利用するPodネットワークアドレスを 192.168.0.0/16
より変更している場合、マニフェストファイル内で定義されているCIDR情報を修正してから実行します。
$ kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
daemonset.apps/calico-node created
serviceaccount/calico-node created
deployment.apps/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
poddisruptionbudget.policy/calico-kube-controllers created
いろいろ作成されますが、結局 calico-node
というDaemonSetを作成している、という訳でしょうか。
再度、現在のノード情報を確認すると、STATUSがReadyとなっています。
$ kubectl get node NAME STATUS ROLES AGE VERSION kubeadm-master Ready control-plane,master 34m v1.20.0
kubeletプロセスを確認すると、正しく起動するようになっています。
$ sudo systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled) Drop-In: /etc/systemd/system/kubelet.service.d └─10-kubeadm.conf Active: active (running) since Fri 2021-01-15 12:51:58 UTC; 41min ago Docs: https://kubernetes.io/docs/home/ Main PID: 4422 (kubelet) Tasks: 15 (limit: 9544) Memory: 44.4M CGroup: /system.slice/kubelet.service └─4422 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc>
マスターノードのtaint削除
デフォルトでは、マスターノード上にはPodをスケジューリングされないようにtaint設定が入っています。
$ kubectl describe node kubeadm-master | grep -i taint Taints: node-role.kubernetes.io/master:NoSchedule
今回の構成では、ワーカーノードも単一ノードとなるため、万が一に備えて、マスターノード上のtaint設定を削除しておきます。
$ kubectl taint nodes --all node-role.kubernetes.io/master-
node/kubeadm-master untainted
確認。
$ kubectl describe node kubeadm-master | grep -i taint Taints: <none>
ワーカーノードの追加
Clusterにワーカーノードを追加します。
ワーカーノードとして追加したいノードに接続して、 kubeadm init
コマンド実行時に表示された以下のコマンドを実行します。
$ sudo kubeadm join 10.146.0.2:6443 --token m8sf51.jiip1suec225lyai \ --discovery-token-ca-cert-hash sha256:a70c0361997bbefb6c77f74d9c85d13a6feb830ae52ff1d1b4ebe4c6567e5279 [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.
ワーカーノード上のkubeletプロセスも、正しく起動するようなっています。
$ sudo systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled) Drop-In: /etc/systemd/system/kubelet.service.d └─10-kubeadm.conf Active: active (running) since Fri 2021-01-15 13:42:34 UTC; 2min 4s ago Docs: https://kubernetes.io/docs/home/ Main PID: 11357 (kubelet) Tasks: 15 (limit: 9544) Memory: 44.7M CGroup: /system.slice/kubelet.service └─11357 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/>
マスターノード上でCluster Nodeの確認。ワーカーノードが参加してくれています。
$ kubectl get node NAME STATUS ROLES AGE VERSION kubeadm-master Ready control-plane,master 53m v1.20.0 kubeadm-worker Ready <none> 3m2s v1.20.0
token情報は、24時間で期限切れとなってしまうらしく、以下コマンドにてtokenの再作成を行えます。
$ sudo kubeadm token create --print-join-command kubeadm join 10.146.0.2:6443 --token ykz2kc.vhrt26ajm4ge854s --discovery-token-ca-cert-hash sha256:a70c0361997bbefb6c77f74d9c85d13a6feb830ae52ff1d1b4ebe4c6567e5279
確認
Kubernets Clusterを作成できました。適当なPodを動かしてみます。
$ kubectl run sample --image=nginx pod/sample created $ $ kubectl get pod NAME READY STATUS RESTARTS AGE sample 1/1 Running 0 10s $
ちゃんと動きますね。