ProxmoxでおうちKubernetes環境を作った

お正月なので

使っていないPCにProxmoxを入れて、おうちKubernetes環境を構築しました。

構成

Proxmox上の仮想マシンUbuntu)でKubernetesを動かします。 Kubernetesのノードはコントロールプレーン1台、ワーカーノード2台という構成とします。

仮想マシンのスペック

スペックはコントロールプレーン、ワーカーノード共通で下記のとおり。

  • CPU: 1ソケット2コア
  • メモリ: 4GB
  • ディスク: 32GB

各マシンのホスト名およびIPアドレス

  • Proxmox
    • pve: 192.168.50.10
  • Kubernetesコントロールプレーン
    • k8s-controlplane-01: 192.168.50.11
  • Kubernetesワーカーノード
    • k8s-workernode-01: 192.168.50.12
    • k8s-workernode-02: 192.168.50.13
  • KubernetesのPod用セグメント: 172.16.0.0/12

構築

方針

  • ProxmoxよくわかんないのでひとまずGUIVMを作ります。本当は Terraform とか cloud-init とか Ansible とかでやりたいけどそれはまた今度。
  • Kubernetesもよくわかんないのでおとなしく kubeadm で作ります。
  • セキュリティ面は見なかったことにします。各自いい感じにやってくれ。

構築手順

1. Proxmoxのセットアップ

まずはProxmoxのISOファイルを このへん からダウンロードしてきます。 ダウンロードできたら、そのへんのUSBメモリに書き込めばOK。dd コマンドでやるならだいたいこんな感じ。

$ sudo dd if=./proxmox-ve_8.3-1.iso of=/dev/sdc status=progress bs=1KB

USBメモリができあがったらPCに挿してUSBブートすればインストーラが起動するので、あとはインストーラにしたがって進めていけばOK。

インストールが終わったらPCを再起動するとProxmoxにログインできるようになるので、PCにディスプレイとキーボードを挿したり別の端末からブラウザでWeb UI https://192.168.50.10:8006/ にアクセスしたりしていじります。

なお、このタイミングでProxmoxのサブスクリプション無し版リポジトリの追加&アップデートをしておくとよいです。 左メニューのDatacenter配下のノードを選択して Update > Repositories を開き、Add ボタンから Repository: No-Subscription を追加します。 ついでにEnterpriseっぽい https://enterprise.proxmox.com/debian/ceph-quincyhttps://enterprise.proxmox.com/debian/pve を選択して Disable で無効化し、 Repositories の上の Updates メニューで RefreshUpgrade していけばOKです。

2. Kubernetesノード用のVM構築

ProxmoxのWeb UI https://192.168.50.10:8006/ から作成していきます。 と言っても基本的には画面右上の Create VM ボタンからぽちぽちやっていくだけです。

変えたのは下記項目。その他はデフォルトのまま。

タブ 項目名
General Name ホスト名(k8s-controlplane-01, k8s-workernode-01, k8s-workernode-02
OS Use CD/DVD disc image file (iso) > ISO image UbuntuのISOイメージ(事前に左メニュー local > ISO Images からアップロードしておく)
CPU Cores 2
CPU Type host
Memory Memory (MiB) 4096

あとはVMを起動し、 Console からインストールしていけばOK。 これをコントロールプレーン用1台、ワーカーノード用2台の計3台分行います。

3. Kubernetesコントロールプレーンの構築

ProxmoxのコンソールまたはsshでコントロールプレーンのVM k8s-controlplane-01 内から操作していきます。

3-1. インストール前の事前準備

何はなくともまずは諸々アップデート

$ sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get autoremove -y && sudo apt-get autoclean -y

スワップをOFFにします。永続化のため、 /etc/fstab# /swap.img 〜 の行を削除orコメントアウトしておきます。

$ sudo swapoff -a
$ sudo vi /etc/fstab
# /swap.img     none    swap    sw      0       0 # 削除orコメントアウト

cat /proc/swapsスワップの内容が表示されなくなることを確認。

$ cat /proc/swaps
Filename      Type    Size    Used    Priority

カーネルモジュール br_netfilter の有効化と、カーネルパラメータ net.ipv4.ip_forward の設定を行います。

$ echo "br_netfilter" | sudo tee /etc/modules-load.d/k8s.conf
$ echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/k8s.conf
$ sudo sysctl --system

3-2. cri-o, Kubernetesのインストール

だいたい cri-oのREADME の記載に従って進めていきます。

まずはバージョンを環境変数に入れておき…

$ KUBERNETES_VERSION=v1.32
$ CRIO_VERSION=v1.32

cri-o, Kubernetesリポジトリを追加して apt-get でインストール。

$ curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/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:/$KUBERNETES_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/kubernetes.list

$ curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key |
    sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg

$ echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/stable:/$CRIO_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/cri-o.list

$ sudo apt-get update
$ sudo apt-get install -y cri-o kubelet kubeadm kubectl

インストールが完了したら、意図しないバージョンアップを避けるためバージョンを固定しておきます。

$ sudo apt-mark hold cri-o kubelet kubeadm kubectl

cri-o のサービスを立ち上げておき…

$ sudo systemctl start crio.service

念のためVMを再起動。

$ sudo shutdown -r now

3-3. コントロールプレーン構築

起動したら再度VMに入り、いよいよKubernetesのコントロールプレーンを構築していきます。

kubeadm コマンドにPod用ネットワークセグメント 172.16.0.0/12 を渡して実行。

$ sudo kubeadm init --pod-network-cidr=172.16.0.0/12

成功すると下記のようなメッセージが表示されます。最後の kubeadm join コマンドはワーカーノード作成時に使うので、忘れずに控えておきます。

Your Kubernetes control-plane has initialized successfully!

(中略)

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

kubeadm join 192.168.50.11:6443 --token ******.**************** \
        --discovery-token-ca-cert-hash sha256:****************************************************************

3-4. 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 pods とかできるようになります。

3-5. Calicoのインストール

今回はCNIプラグインとしてCalicoを使います。インストールの流れはほぼ Calicoのドキュメント のとおりでOK。

まずはCalicoのマニフェストをダウンロードします。

$ wget https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/tigera-operator.yaml
$ wget https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/custom-resources.yaml

ここで1点注意。custom-resources.yaml にはPodのネットワークセグメントが 192.168.0.0/16 と直接書かれているので、今回のネットワークセグメントに合わせて書き換える必要があります。

$ vi custom-resources.yaml
      # cidr: 192.168.0.0/16 # 削除orコメントアウト
      cidr: 172.16.0.0/12 # 追加

編集が終わったら kubectl create でデプロイします。

$ kubectl create -f tigera-operator.yaml
$ kubectl create -f custom-resources.yaml

しばらく待って、 kubectl get nodesSTATUSReady に、kubectl get pods -A で全Podの STATUSRunning になれば完了です。

4. Kubernetesワーカーノードの構築

ワーカーノードのVM k8s-workernode-01k8s-workernode-02 で操作していきます。

まずは 3. kubernetesコントロールプレーンの構築 の最初から 3-2. cri-o, Kubernetesのインストール まで、コントロールプレーンと同じ手順で進めます。

完了後、3-3. コントロールプレーン構築 の最後で控えた kubeadm join コマンドを実行すればノードがクラスタに追加されます。

$ sudo kubeadm join 192.168.50.11:6443 --token ******.**************** \
        --discovery-token-ca-cert-hash sha256:****************************************************************

コントロールプレーンのVMkubectl get nodes を実行し、ワーカーノードが表示されていればOK。

$ kubectl get nodes
NAME                  STATUS   ROLES           AGE   VERSION
k8s-controlplane-01   Ready    control-plane   16h   v1.32.0
k8s-workernode-01     Ready    <none>          16h   v1.32.0
k8s-workernode-02     Ready    <none>          16h   v1.32.0

試してみる

コントロールプレーン k8s-control-plane-01 でこんな感じのyamlを用意しておき…

# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.27.3
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    nodePort: 30080
    protocol: TCP
  selector:
    app: nginx
# toolbox.yaml
# https://github.com/ser1zw/toolbox-container
apiVersion: v1
kind: Pod
metadata:
  name: toolbox
spec:
  containers:
  - name: toolbox
    image: ser1zw/toolbox:latest

デプロイします。

$ kubectl apply -f nginx.yaml
$ kubectl apply -f toolbox.yaml

nginxに別のPodからcurlでアクセスすると、正常に応答が返ってきます。いい感じ。

$ kubectl exec toolbox -- curl -s nginx:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(中略)
</html>

ついでに上記マニフェストではServiceをNodePortにしているので、ホストマシンと同一ネットワーク上の別端末からノードのIPを指定してcurlを実行しても(ファイヤウォールが開いていれば)応答が返ってきます。

$ curl -s 192.168.50.12:30080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(中略)
</html>

完璧ですね。

まとめ

というわけで、無事おうちKubernetes環境が構築できました。

今回は手動でGUI操作したりコマンド実行したりしましたが、何台もあるとめんどくさいのでIaC化しておきたいところです。

参考