VirtualBox 上の CentOS7 に k8s クラスターを構築
はじめに
CKA 受験に向け k8s の理解を深めるため、VirtualBox 上の CentOS7 3 台で K8s クラスターの構築にチャレンジした際の手順メモです。本当は Raspberry-pi を利用した物理構成で試したかったのですが金欠で断念。
物理構成
筐体は ThinkCentre M725s を利用。スペックは以下の通り。Vagrant は利用しなかった…
項目 | 内容 |
---|---|
CPU | AMD Ryzen 7 PRO 2700 Eight-Core Processor |
Memory | 24GiB |
ホスト OS | Windows 10 Pro |
仮想 S/W | Oracle VM VirtualBox 6.1 |
ゲスト OS 構成
k8s クラスターを構成する仮想マシンには CentOS 7.9 をインストール。CentOS を選定したのは単純に検索でヒットした情報が多かったため。CentOS 8 だと flannel
が動かないといった情報がちょこちょこ確認できたので CentOS 7.9 を利用した。(あとで調べる)
仮想マシンには、NAT アダプター(enp0s3)とホストオンリーアダプター(enp0s8)をアタッチ。各ホスト名、ノードの役割、IP アドレスは以下の通り。
OS の設定
k8s クラスター構築には必須ではないが、各ノードで OS の基本的な設定は実施しておく。
ユーザ作成
root 以外のユーザを作成し、基本的にはこのユーザで作業を行う。wheel グループに所属させ sudo
を許可する。
$ useradd staff -u 10002 -g wheel $ sed -i.org '6 s/^#//' /etc/pam.d/su
タイムゾーンの変更
タイムゾーンが Asia/Tokyo
以外の場合は変更する。
$ cp /etc/localtime /etc/localtime.org $ ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
SSH 設定
PermitRootLogin no
で root ユーザでの ssh 接続を禁止し、UseDNS no
で ssh 接続時に DNS を利用しない。各ノードの作業ユーザ(staff)で鍵交換をしたら、お好みで PasswordAuthentication no
もあわせて設定する。
$ sed -i.org 's/^#\(PermitRootLogin\) yes/\1 no/g; s/^#\(UseDNS\) yes/\1 no/g' /etc/ssh/sshd_config $ sudo systemctl restart sshd
不要サービスの停止
検証環境でリソースが少ないため、使わなそうなサービスは自動起動を disable
にして停止しておく。今回停止したサービスは以下のとおり。
$ systemctl list-unit-files -t service | grep enable $ for i in auditd.service mdmonitor.service postfix.service qemu-guest-agent.service firewalld.service; do sudo systemctl disable "$i"; sudo systemctl stop "$i"; done
Chrony の設定
お好みで時刻動機の設定も実施しておく。chrony
の設定はいまいち慣れない…
$ sudo sed -i.org 's/^server 0\.centos\.pool\.ntp\.org/server ntp\.nict\.jp/; s/^server [0-3]\.centos\.pool\.ntp\.org iburst//' /etc/chrony.conf $ sudo sed -i.org 's/^#\(minsources 2\)/\1/g' /etc/chrony.conf $ sudo systemctl restart chronyd.service $ sudo systemctl enable chronyd.service $ chronyc sources
IPv6 の無効化
必須ではないはずだが、IPv6 も無効化する。
$ sudo echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf $ sudo echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
パッケージのアップデート
ここまで設定したら yum update
をして、一度 OS を再起動する。
$ sudo yum -y update
前準備
k8s クラスターの構成には kubeadm
を利用するが、インストールにはいくつか要件がある。
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
Swap の無効化
kubelet
を正常に動作させるには SWAP を OFF にする。
$ sudo swapoff -a $ sudo vi /etc/fstab # # /etc/fstab # Created by anaconda on Thu Mar 11 17:03:33 2021 # # Accessible filesystems, by reference, are maintained under '/dev/disk' # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # UUID=faedf728-2f18-47ce-9f82-6ac41398c691 / ext4 defaults 1 1 UUID=8cee5a3d-6dee-4faa-9a51-838ccc56c495 /boot ext4 defaults 1 2 ## - SWAP の mount をコメントアウト #UUID=5acd53ca-fe41-4cc9-862e-6d307b697fa5 swap swap defaults 0 0
Bridge Netfilter
iptables
がブリッジを通過するトラフィックを処理させるため、net.bridge.bridge-nf-call-ip6tables
を設定する。(あとで調べる)
$ sudo cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF $ sudo sysctl --system
/etc/hosts
名前解決は hosts ファイルで行うので 192.168.56.0/24
のネットワークとホスト名を記述。
$ sudo cat <<EOF >> /etc/hosts 192.168.56.171 c7-k8s-node01.localdomain c7-k8s-node01 192.168.56.172 c7-k8s-node02.localdomain c7-k8s-node02 192.168.56.173 c7-k8s-node03.localdomain c7-k8s-node03 EOF
SELinux を Permissive に変更
SELinux を Permissive
に変更しておく。
$ sudo sed -i.org 's/\(^SELINUX=\).*/\1permissive/g' /etc/selinux/config $ getenforce
Docker のインストール
コンテナランタイムとして各ノードに Docker
をインストールする。この辺も CentOS 8 だとコンテナエンジンが Podman になっているので面倒そう。
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2 $ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo $ sudo yum install -y docker-ce $ sudo systemctl enable docker
kubeadm、kubelet、kubectl のインストール
各ノードに kubeadm
kubelet
kubectl
をインストールする。
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOF $ yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes $ systemctl enable --now kubelet
各パッケージの役割は以下の通り。
パッケージ | 説明 |
---|---|
kubeadm | k8s クラスターを構成・起動するためのコマンド |
kubelet | クラスター内のすべてのマシンで実行されるコンポーネント。 Pod やコンテナの起動などを行う。 |
kubectl | クラスターにアクセスするためのコマンドラインツール |
k8s クラスターの作成
クラスターを構成する場合は kubeadm
コマンドを利用する。
コントロールプレーンノードの初期化
まずは Master ノードで kubeadm init
コマンドを実行し、コントロールプレーンの初期化を行う。コントロールプレーンノードでは etcd
や APIサーバーが実行される。--apiserver-advertise-address
には、仮想マシンの enp8s
インターフェイスの 192.168.56.0/24
のネットワークを指定する。--pod-network-cidr
はデフォルトの 10.244.0.0/16
を指定。
$ sudo kubeadm init --apiserver-advertise-address=192.168.56.171 --pod-network-cidr=10.244.0.0/16
初期が完了すると、以下のように Worker ノードを追加するためのコマンドが表示される。
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: sudo kubeadm join 192.168.56.171:6443 --token ljtkc2.fcmz3msyoknpy39g \ --discovery-token-ca-cert-hash sha256:5f1069d6d26fb11cae8e6b85b4b569ee11d884be0c455370cb3e8e05b0a161ca
root ユーザ以外で kubectl
コマンドを利用する場合は、以下の設定を行う。
$ mkdir -p $HOME/.kube $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config $ sudo chown $(id -u):$(id -g) $HOME/.kube/config $ kubectl get node NAME STATUS ROLES AGE VERSION c7-k8s-node01.localdomain NotReady control-plane,master 90s v1.20.4
尚、上記の設定を実施しないで kubectl
コマンドを実行すると、以下のようなエラーとなる。
$ kubectl get node The connection to the server localhost:8080 was refused - did you specify the right host or port?
Worker ノードの追加
残り 2台の Worker で kubeadm join
コマンドを実行しクラスターに参加させる。
$ sudo kubeadm join 192.168.56.171:6443 --token ljtkc2.fcmz3msyoknpy39g \ --discovery-token-ca-cert-hash sha256:5f1069d6d26fb11cae8e6b85b4b569ee11d884be0c455370cb3e8e05b0a161ca
ノードが追加されたことを確認。
$ kubectl get node NAME STATUS ROLES AGE VERSION c7-k8s-node01.localdomain Ready control-plane,master 120s v1.20.4 c7-k8s-node02.localdomain Ready <none> 90s v1.20.4 c7-k8s-node03.localdomain Ready <none> 30s v1.20.4
Flannel のインストール
Pod 間で通信するためには Container Network Interface(CNI)ベースのアドオンをインストールする必要がある。ネットワークアドオンをインストールしないと以下の通り Cluster DNS(CoreDNS)が起動しない。
$ kubectl get pod --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-74ff55c5b-kqpxl 0/1 Pending 0 88s kube-system coredns-74ff55c5b-lcncq 0/1 Pending 0 88s kube-system etcd-c7-k8s-node01.localdomain 1/1 Running 0 101s kube-system kube-apiserver-c7-k8s-node01.localdomain 1/1 Running 0 101s kube-system kube-controller-manager-c7-k8s-node01.localdomain 1/1 Running 0 101s kube-system kube-proxy-wsfzp 1/1 Running 0 88s kube-system kube-scheduler-c7-k8s-node01.localdomain 1/1 Running 0 101s
アドオンには Calico
などいくつか選択肢があるようだが、今回は Flannel
をインストールした。Flannel
の導入自体は YAML ファイルで Pod をデプロイするだけだが、VirtualBox の NATアダプターとホストオンリーアダプターを利用した構成の場合、enp3s
の 10.0.2.0/24
のネットワークに Flannel
の Pod がデプロイされてしまう。そのため、Yaml ファイルに --iface=enp0s8
の追記が必要。
kube-flannel.yml
をダウンロード。
$ curl -O https://raw.githubusercontent.com/coreos/flannel/62e44c867a2846fefb68bd5f178daf4da3095ccb/Documentation/kube-flannel.yml
kube-flannel.yml
に --iface=enp0s8
の追記。利用する image だけで良い気もするが…念の為 5箇所全てに追記した。
-- (中略) -- containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.12.0-amd64 command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr - --iface=enp0s8 -- (中略) -- containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.12.0-arm64 command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr - --iface=enp0s8 -- (中略) -- containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.12.0-arm command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr - --iface=enp0s8 -- (中略) -- containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.12.0-ppc64le command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr - --iface=enp0s8 -- (中略) -- containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.12.0-s390x command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr - --iface=enp0s8
coredns-***
や kube-flannel-ds-***
Pod が Running になれば OK。
$ kubectl apply -f kube-flannel.yml $ kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES default curl 0/1 Completed 0 3d2h 10.244.1.5 c7-k8s-node02.localdomain <none> <none> kube-system coredns-74ff55c5b-d92d2 1/1 Running 0 4d 10.244.0.2 c7-k8s-node01.localdomain <none> <none> kube-system coredns-74ff55c5b-qjgm9 1/1 Running 0 4d 10.244.0.3 c7-k8s-node01.localdomain <none> <none> kube-system etcd-c7-k8s-node01.localdomain 1/1 Running 0 4d 192.168.56.171 c7-k8s-node01.localdomain <none> <none> kube-system kube-apiserver-c7-k8s-node01.localdomain 1/1 Running 0 4d 192.168.56.171 c7-k8s-node01.localdomain <none> <none> kube-system kube-controller-manager-c7-k8s-node01.localdomain 1/1 Running 0 4d 192.168.56.171 c7-k8s-node01.localdomain <none> <none> kube-system kube-flannel-ds-amd64-m29tr 1/1 Running 0 3d23h 192.168.56.172 c7-k8s-node02.localdomain <none> <none> kube-system kube-flannel-ds-amd64-s22sh 1/1 Running 0 3d23h 192.168.56.173 c7-k8s-node03.localdomain <none> <none> kube-system kube-flannel-ds-amd64-xwcnx 1/1 Running 0 3d23h 192.168.56.171 c7-k8s-node01.localdomain <none> <none> kube-system kube-proxy-5jznk 1/1 Running 0 4d 192.168.56.171 c7-k8s-node01.localdomain <none> <none> kube-system kube-proxy-bzj4k 1/1 Running 0 3d23h 192.168.56.172 c7-k8s-node02.localdomain <none> <none> kube-system kube-proxy-vvf74 1/1 Running 0 3d23h 192.168.56.173 c7-k8s-node03.localdomain <none> <none> kube-system kube-scheduler-c7-k8s-node01.localdomain 1/1 Running 0 4d 192.168.56.171 c7-k8s-node01.localdomain <none> <none>
metrics-server のインストール
kubectl top
コマンドを利用するために metrics-server
をインストールする。こちらも YAML ファイルで Deployment を作成するだけだが、YAML ファイルの修正が必要。
$ git clone https://github.com/kubernetes-sigs/metrics-server $ vi metrics-server/manifests/base/deployment.yaml
command
に --kubelet-insecure-tls
と --kubelet-preferred-address-types=InternalIP
を追記。
apiVersion: apps/v1 kind: Deployment metadata: name: metrics-server namespace: kube-system spec: strategy: rollingUpdate: maxUnavailable: 0 template: spec: serviceAccountName: metrics-server volumes: # mount in tmp so we can safely use from-scratch images and/or read-only containers - name: tmp-dir emptyDir: {} priorityClassName: system-cluster-critical containers: - name: metrics-server image: gcr.io/k8s-staging-metrics-server/metrics-server:master imagePullPolicy: IfNotPresent command: - /metrics-server - --kubelet-insecure-tls - --kubelet-preferred-address-types=InternalIP args: - --cert-dir=/tmp - --secure-port=4443 - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --kubelet-use-node-status-port - --metric-resolution=15s resources: requests: cpu: 100m memory: 300Mi -- (中略) --
YAML ファイルから Deployment をデプロイすると kubectl top
コマンドが利用できる。
$ kubectl apply -k metrics-server/manifests/base/ $ kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% c7-k8s-node01.localdomain 112m 5% 1555Mi 40% c7-k8s-node02.localdomain 39m 1% 663Mi 17% c7-k8s-node03.localdomain 31m 1% 610Mi 15%
エラーとなる場合は CNI の設定を確認する。例えば、今回の構成だと enp8s
インターフェイスのネットワーク(192.168.56.0/24)に Flannel がデプロイされていないと、ノードのコンテナ間で通信ができないため metric-server はデプロイされるが kubectl top
コマンドは利用できない。
Pod をデプロイ
helloworld Pod をデプロイ
試しに helloworld Pod をデプロイ。kubectl get pod -o wide
コマンドで確認すると Worker
ノード c7-k8s-node03.localdomain
で稼働していることがわかる。
$ kubectl run --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES helloworld 1/1 Running 0 6s 10.244.2.5 c7-k8s-node03.localdomain <none> <none>
ノードを指定して curl image Pod をデプロイ
helloworld Pod と疎通確認をするためにノード c7-k8s-node02.localdomain
に curl image Pod をデプロイする。ノードを指定する場合は YAML ファイルに nodeSelector
の kubernetes.io/hostname
を追記する。
apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: curl name: curl spec: containers: - name: curl image: curlimages/curl:7.68.0 command: - "bin/sh" - "-c" - "sleep 3000" resources: {} dnsPolicy: ClusterFirst restartPolicy: Never nodeSelector: kubernetes.io/hostname: c7-k8s-node02.localdomain
curl image Pod から helloworld Pod(10.244.2.5)にアクセスできれば OK。
$ kubectl apply -f curlimages.yml $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES curl 1/1 Running 0 8s 10.244.1.6 c7-k8s-node02.localdomain <none> <none> helloworld 1/1 Running 0 9m30s 10.244.2.5 c7-k8s-node03.localdomain <none> <none> $ kubectl exec -it curl sh $ curl 10.244.2.5:8080 Hello, world! Version: 1.0.0 Hostname: helloworld
[おまけ]NFS サーバの導入
仮想マシンを 1台追加し k8s クラスタ間で利用する PVC 領域用の NFS サーバを用意する。
- NFS Server IP:192.168.56.174
NFS Server
rpcbind
と nfs-utils
をインストール
$ sudo yum install -y rpcbind nfs-utils $ sudo mkdir -p /var/share/nfs $ sudo echo "/var/share/nfs 192.168.56.0/24(rw,no_root_squash)" > /etc/exports
サービスを起動する
$ sudo systemctl start rpcbind $ sudo systemctl start nfs-lock $ sudo systemctl start nfs-idmap $ sudo systemctl start nfs-server $ sudo systemctl enable rpcbind $ sudo systemctl enable nfs-lock $ sudo systemctl enable nfs-idmap $ sudo systemctl enable nfs-server
NFS Client
k8s クラスターの各ノードから作成した NFS 領域を mount する。
$ sudo mkdir /mnt/nfs $ sudo chown staff:wheel /mnt/nfs $ sudo mount -t nfs 192.168.56.174:/var/share/nfs /mnt/nfs
参考 URL
参考にさせていただいた記事.