純粋にk3sだけを作る。(1IP) 2025年版

投稿者: | 2月 21, 2025

同じK3sでもRancher Desktopだと思いの他メモリを必要としたのとスナップショットストレージの準備に手間がかかるので、1台のUbuntu Serverにネイティブkubernetesと同様の動きができるK3sをインストールしてみる。k3sでは、metallbなどをインストールすると、ロードバランサーなどが使えるのだが、今回は、手順をなるべく少なくするということで極力追加モジュールをインストールしないようにする。さらに無いと困るローカルレジストリを作成し、dockerコマンドをインストールする。ただし、ローカルレジストリは、動けばいいレベルなので、HTTP接続で認証無しの設定。

できるだけGUIでも見れるようにする。

k3sとは、Kubernetes本家のバイナリから比較的に不要なものを取り去ったライトウェイトなもので、シングルバイナリーで動作する。また、デフォルトではetcdでは動いていない。etcdの代わりにsqliteが動いている。インストール自体もVanilla Kubernetesにあるような事前準備がほぼ不要でシングルコマンドラインで環境だけではなくクライアントもインストールされ、すぐに稼働ができる。

 

所要時間は、インターネットへの回線スピードにもよるが 1時間程度でこのドキュメントのタスクが完了する。

k3sを純粋につくるといろいろなものが入ってくる。

  • CNI: funnel
  • Loadbalancer: servicelb (Klipper Service Load Balancer)
  • Ingress: traefik

traefikデフォルトだと80/443しか渡してくれない

Klipper Service Load Balancer ノードのポートが空いていないとEXTERNAL-IPがPendingになってしまう。

 

とにかく作りたいだけと言う人は、以下を実行すると、以下の作業が全て自動で行われる。4CPU 8GB RAM 100GB HDD のUbuntu Server 22.04/24.04 (amd64/arm64)で動作するはず。 

sudo -i
curl -OL https://gist.githubusercontent.com/masezou/65c29fcff3a761335506b908f97228a3/raw/f84aecbb5b3d6dcc6d75f1d80b7e9937f7b822d1/k3s-setup.sh
bash ./k3s-setup.sh

スクリプトが完了したら、一度ログアウトして入り直す。

 

動作環境

k3s自体の要件は以下に記載されている。

https://docs.k3s.io/installation/requirements

 

前提条件

  • Hardware: amd64/arm64 x1
  • OS: Ubuntu Server 22.04.5

クリーンインストールのもの。前提パッケージや事前のOS設定も一切不要。ホスト名とIPアドレスが設定されていればOK

台数は少なくとも1サーバで間に合う。複数台のノードの追加も可能だが、今回は1台で構築する。

  • リソース

注意:メモリは多少ケチってもいいが、CPUは十分にあったほうがいい。

    • CPU :最低 4vCPU 推奨6vCPU以上
    • メモリ:8GB以上
    • HDD:1台。コンテナ関連で50GB以上の空き容量。ただし、ローカルレジストリも兼ねるので、100GBくらいはあったほうがいい。
  • Network: 1NICで1 IPアドレス(DHCPアドレスで構わない。)

構築、検証などの全ての過程でインターネット接続が必須

 

以下作業はrootで行う。

sudo -i

Ubuntu 22.04での念の為の設定

以下のコマンドを実行しておかないと、ファイルのオープン数の制限に到達してしまい、コンテナの展開に失敗することがあるので、念の為実行しておく。

tee /etc/sysctl.d/10-k8s.conf << EOF
fs.inotify.max_user_instances = 1024
fs.inotify.max_user_watches = 1048576
EOF
sysctl -p /etc/sysctl.d/10-k8s.conf
sysctl -a | grep "fs.inotify"

スワップを無効にする

swapoff -a
sed -i '/\/swap\.img\s\+none\s\+swap\s\+sw\s\+0\s\+0/s/^/#/' /etc/fstab

ローカルレジストリの作成

もし、レジストリを持っていないのであれば、ローカルレジストリを立てる。設置場所は、同一サーバでも他のサーバでも構わない。今回は、ローカルレジストリを同じホストに立てる。

  • 実態のデータは、/disk/registry
  • Registoryは、http://<このホスト>:5000 

で構築される。

REGDIR=/disk/registry

ETHDEV=$(netplan get | sed 's/^[[:space:]]*//' | grep -A 1 "ethernet" | grep -v ethernet | cut -d ":" -f 1)
LOCALIPADDR=$(ip -f inet -o addr show $ETHDEV | cut -d\ -f 7 | cut -d/ -f 1)
REGISTRYHOST=${LOCALIPADDR}
REGISTRYPORT=5000
REGISTRY=${REGISTRYHOST}:${REGISTRYPORT}
REGISTRYURL=http://${REGISTRY}
mkdir -p ${REGDIR}
ln -s ${REGDIR} /var/lib/docker-registry
ufw allow 5000
apt -y install docker-registry
sed -i -e "s/ htpasswd/# htpasswd/g" /etc/docker/registry/config.yml
sed -i -e "s/ realm/# realm/g" /etc/docker/registry/config.yml
sed -i -e "s/ path/# path/g" /etc/docker/registry/config.yml
cat <<EOF >/etc/cron.hourly/docker-registry-garbage
#!/bin/sh
/usr/bin/docker-registry garbage-collect /etc/docker/registry/config.yml
EOF
chmod +x /etc/cron.hourly/docker-registry-garbage
systemctl restart docker-registry
systemctl enable docker-registry

K3sのインストール

以下のページのトップに記載されているコマンドをインストールする。

https://k3s.io

今回は少しだけオプションを付ける。以下のコマンドでマスター/ワーカーノードのインストールができる。

curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.31 K3S_KUBECONFIG_MODE="644" sh -

確認

以下を実行。

sleep 20
kubectl -n kube-system wait pod -l k8s-app=kube-dns --for condition=Ready --timeout 60s
kubectl -n kube-system wait pod -l k8s-app=metrics-server --for condition=Ready --timeout 60s
kubectl -n kube-system wait pod -l app=local-path-provisioner --for condition=Ready --timeout 60s
kubectl -n kube-system wait pod -l svccontroller.k3s.cattle.io/svcnamespace=kube-system --for condition=Ready --timeout 60s
kubectl -n kube-system wait pod -l app.kubernetes.io/instance=traefik-kube-system --for condition=Ready --timeout 60s
kubectl cluster-info
kubectl config get-contexts
kubectl get node -o wide
kubectl get pod -A
kubectl get svc -A
kubectl get sc

追加設定

Workerのラベルを付ける

kubectl label node `hostname` node-role.kubernetes.io/worker=worker

ローカルレジストリの利用設定 (HTTP接続で認証無しの設定)

cat <<EOF >/etc/rancher/k3s/registries.yaml
mirrors:
  “${REGISTRY}”:
    endpoint:
      – “${REGISTRYURL}”
EOF
systemctl restart k3s
kubectl -n kube-system wait pod -l k8s-app=kube-dns –for condition=Ready –timeout 60s
kubectl -n kube-system wait pod -l k8s-app=metrics-server –for condition=Ready –timeout 60s
kubectl -n kube-system wait pod -l app=local-path-provisioner –for condition=Ready –timeout 60s
kubectl -n kube-system wait pod -l svccontroller.k3s.cattle.io/svcnamespace=kube-system –for condition=Ready –timeout 60s
kubectl -n kube-system wait pod -l app.kubernetes.io/instance=traefik-kube-system –for condition=Ready –timeout 60s

configと補完の設定

mkdir -p ~/.kube
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
PUBLICIP=$(kubectl get nodes -o jsonpath='{.items[*].metadata.annotations.flannel\.alpha\.coreos\.com\/public-ip}')
sed -i -e "s/127.0.0.1/$PUBLICIP/g" ~/.kube/config
chmod 400 ~/.kube/config
apt -y install bash-completion
k3s completion bash > /etc/bash_completion.d/k3s
source /etc/bash_completion.d/k3s
kubectl completion bash > /etc/bash_completion.d/kubectl
source /etc/bash_completion.d/kubectl
crictl completion bash > /etc/bash_completion.d/crictl
source /etc/bash_completion.d/crictl
CONTAINERDVER=1.7.23
curl --retry 10 --retry-delay 3 --retry-connrefused -sS https://raw.githubusercontent.com/containerd/containerd/v${CONTAINERDVER}/contrib/autocomplete/ctr -o /etc/bash_completion.d/ctr
source /etc/bash_completion.d/ctr

helmのインストール

if type "helm" >/dev/null 2>&1; then
  echo -e "\e[32mhelm OK. \e[m"
else
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | tee /etc/apt/keyrings/helm.gpg > /dev/null
apt install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list
apt update
apt -y install helm
fi
helm version
helm completion bash >/etc/bash_completion.d/helm
source /etc/bash_completion.d/helm

ローカルレジストリの操作用にDocker CLIをインストール

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
cat << EOF > /etc/docker/daemon.json
{"insecure-registries": ["${REGISTRY}"]}
EOF
systemctl restart docker

ローカルレジストリのフロントエンド(オプション)

docker run -d -p 18080:80 –name registryfe –restart=always -e ENV_DOCKER_REGISTRY_HOST=${REGISTRYHOST} -e ENV_DOCKER_REGISTRY_PORT=${REGISTRYPORT} ekazakov/docker-registry-frontend:latest

で立ち上げるのだが、コンテナで立ち上げた。

kubectl create ns registoryfe
cat <<EOF | kubectl apply -n registoryfe -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: registryfe
spec:
replicas: 1
selector:
  matchLabels:
    app: registryfe
template:
  metadata:
    labels:
      app: registryfe
  spec:
    containers:
    - name: registryfe
      image: ekazakov/docker-registry-frontend:latest
      env:
      - name: ENV_DOCKER_REGISTRY_HOST
        value: "${REGISTRYHOST}"
      - name: ENV_DOCKER_REGISTRY_PORT
        value: "${REGISTRYPORT}"
      ports:
      - containerPort: 80
EOF
cat <<EOF | kubectl apply -n registoryfe -f -
apiVersion: v1
kind: Service
metadata:
name: registryfe
spec:
selector:
  app: registryfe
ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30000
type: NodePort
EOF

http://<ホスト(ノード) IP>:30000でレジストリの内容をブラウズできる。

External Snapshotterのインストール

SNAPSHOTTERVER=8.2.0
# Apply VolumeSnapshot CRDs
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v${SNAPSHOTTERVER}/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v${SNAPSHOTTERVER}/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v${SNAPSHOTTERVER}/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml

# Create Snapshot Controller
curl --retry 10 --retry-delay 3 --retry-connrefused -sSOL https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v${SNAPSHOTTERVER}/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
sed -i -e "s/namespace: default/namespace: kube-system/g" rbac-snapshot-controller.yaml
kubectl create -f rbac-snapshot-controller.yaml
rm -rf rbac-snapshot-controller.yaml
curl --retry 10 --retry-delay 3 --retry-connrefused -sSOL https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v${SNAPSHOTTERVER}/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
sed -i -e "s/namespace: default/namespace: kube-system/g" setup-snapshot-controller.yaml
kubectl create -f setup-snapshot-controller.yaml
rm setup-snapshot-controller.yaml
kubectl -n kube-system wait pod -l app.kubernetes.io/name=snapshot-controller --for condition=Ready --timeout 60s

Longhornのインストール

事前準備

apt -y install nfs-common jq
systemctl stop multipathd.socket && systemctl disable multipathd.socket
systemctl stop multipathd && systemctl disable multipathd
modprobe iscsi_tcp
modprobe dm_crypt
echo "iscsi_tcp" > /etc/modules-load.d/iscsi_tcp.conf
echo "dm_crypt" > /etc/modules-load.d/dm_crypt.conf
sed -i -e "s/debian/debian.$(hostname)/g" /etc/iscsi/initiatorname.iscsi
systemctl restart iscsid.service

Longhornのインストール

LONGHORNVER=1.8.0
curl -sSfL -o longhornctl https://github.com/longhorn/cli/releases/download/v${LONGHORNVER}/longhornctl-linux-$(dpkg --print-architecture)
chmod +x ./longhornctl
./longhornctl --kube-config /etc/rancher/k3s/k3s.yaml check preflight
helm repo add longhorn https://charts.longhorn.io
helm repo update
PUBLICIP=`kubectl get nodes -o jsonpath='{.items[*].metadata.annotations.flannel\.alpha\.coreos\.com\/public-ip}'`
DNSDOMAINNAME=`cat /etc/hostname`.${PUBLICIP}.sslip.io
LONGHORNVER=1.8.0
helm install longhorn longhorn/longhorn --namespace longhorn-system --set persistence.defaultClassReplicaCount=1 --set defaultSettings.defaultReplicaCount=1 --set defaultSettings.replicaZoneSoftAntiAffinity=true --set longhornUI.replicas=1 --set ingress.enabled=true --set ingress.host=longhorn.${DNSDOMAINNAME} --create-namespace --version $LONGHORNVER --wait

sleep 60
kubectl -n longhorn-system wait pod -l app=csi-attacher --for condition=Ready --timeout 300s
kubectl -n longhorn-system wait pod -l app=csi-provisioner --for condition=Ready --timeout 300s
kubectl -n longhorn-system wait pod -l app=csi-resizer --for condition=Ready --timeout 300s
kubectl -n longhorn-system wait pod -l app=csi-snapshotter --for condition=Ready --timeout 300s
kubectl label node `cat /etc/hostname` topology.kubernetes.io/zone=`cat /etc/hostname`

確認

kubectl -n longhorn-system get pods

ボリュームスナップショットクラスの設定

cat <<EOF | kubectl apply -f -
kind: VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
metadata:
annotations:
  snapshot.storage.kubernetes.io/is-default-class: "true"
name: longhorn-snapshot-vsc
driver: driver.longhorn.io
deletionPolicy: Delete
parameters:
type: snap
EOF
kubectl get volumesnapshotclasses

デフォルトストレージの変更

cp /var/lib/rancher/k3s/server/manifests/local-storage.yaml /var/lib/rancher/k3s/server/manifests/custom-local-storage.yaml
sed -i -e "s/storageclass.kubernetes.io\/is-default-class: \"true\"/storageclass.kubernetes.io\/is-default-class: \"false\"/g" /var/lib/rancher/k3s/server/manifests/custom-local-storage.yaml
sleep 20
kubectl get sc

Longhorn UIのアクセス

ダッシュボードURLを調べる

echo "http://$(kubectl -n longhorn-system get ingress longhorn-ingress -o jsonpath='{.spec.rules[0].host}')"

traefikダッシュボード

# traefik dashboard
# https://github.com/traefik/traefik-helm-chart/blob/v27.0.0/traefik/values.yaml
cat << 'EOF' > /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
  ingressRoute:
    dashboard:
      enabled: true
    healthcheck:
      enabled: true
      entryPoints: ["traefik", "web", "websecure"]
  logs:
    access:
      enabled: true
  service:
    spec:
      externalTrafficPolicy: Local
  ports:
    traefik:
      port: 9000
      expose:
        default: true
    mysql:
      expose: false
      #exposedPort: 3306
      hostPort: 3306
      port: 3306
      protocol: TCP
    pgsql:
      expose: false
      #exposedPort: 5432
      hostPort: 5432
      port: 5432
      protocol: TCP
  providers:
    kubernetesCRD:
      allowCrossNamespace: true
EOF

traefikダッシュボードURLは

http://<ホスト(ノード) IP>:9000/dashboard/

 

これで通りいっぺんの検証ができるはず。

コメントを残す