同じ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のインストール
以下のページのトップに記載されているコマンドをインストールする。
今回は少しだけオプションを付ける。以下のコマンドでマスター/ワーカーノードのインストールができる。
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/
これで通りいっぺんの検証ができるはず。