大学のICTフェロー業(20年以上続けている無料ボランティアのお兄さん)が完全にお留守で、大学にIPアドレスを返却したが、DNSの設定が残っていると指摘されて、DNSの設定変更をしようとしたら、接続できない。調べてみるとVPNの接続方法がガラリと変わっていた。そこからかぁ。さらにゼミのサーバがhttpsになっていなかったが発覚。というか、誰もhttps使っていない。それはそうだ、教えていない。近々、教えなきゃならない。という背景があって、
Lets Encryptの設定って、IPv6でも使えたんだっけ?
ブログにする必要もなく使えた。ちなみに、DNSの設定を変えるのが面倒なので、ワイルドカードDNSでIPアドレスベースのホスト名を使ったができた。
こういう、ことは、学生を送り出す方としては、最初に習ってしまえば後が楽なので、就職前に教えてしまえと。そういう意味だと、クラウド、コンテナはマストアイテムに。
ちなみに、学生は、主に文系でほぼほぼ女子。しかし、うちら就職氷河期世代と違って全然受け入れてくれる。就職先も結構すごい。ただ、端末持ってきてというとスマホをもってきて、そこからSSHしようとしちゃう時があるけど。
閑話休題
できるはずだろうと思ったができた。ただ、Longhornは使わず、NFS CSIドライバを使うことにした。多分、Longhornを使うには、各コンテナで独立したストレージデバイスが必要になるので面倒かと。Proxmoxでやろうとしたが、Proxmoxのコンテナはcloud-initが使えないので手間がかかる。LXDは、Cloud-initが使えるので便利。(となるとQNAPのコンテナでも作れると思う。)
環境
Ubuntu Server 24.04.2 amd64 1台 (多分arm64でも動く)
LXDのコンテナでk3sのノードを作る。
プライベートレジストリ:1コンテナ
K3sノード:3コンテナ (Master:1 / Worker:2)
LXDサーバは、NFSサーバも兼務させる。
スペックは上記が動作するCPU、メモリ、HDD領域がLXD側に必要なのは言うまでもない。コンテナはうちでの小槌ではないので。
事前準備
物理NICをBridgeにしたものも作成する。理由は、パブリックIPv6を使いたいから。
ちなみに、この環境は、パブリックIPv6が割り振られている環境。
ens33は、各自の環境に書き換える。
また、同時にNFSサーバも設定している。
cat << EOF > /etc/netplan/90-bridge.yaml
network:
version: 2
ethernets:
ens33:
dhcp4: no
bridges:
br0:
interfaces:
- ens33
dhcp-identifier: "Mac"
dhcp4: yes
parameters:
forward-delay: 0
stp: no
EOF
chmod 0600 /etc/netplan/90-bridge.yaml
apt -y install nfs-kernel-server
NFS_SHARE="/disk/nfs_csi/"
mkdir -p ${NFS_SHARE}
chmod 1777 /disk/nfs_csi
ls -ld /disk/nfs_csi
echo "/disk/nfs_csi 192.168.0.0/16(rw,async,no_root_squash,no_subtree_check)" >> /etc/exports
exportfs -ra
reboot
Lxdのインストール
lxdを初期化して使う必要があるが、初めて、lxd/lxcコマンドを使うとsnap経由でLXD一式のインストールが始まる。LXDがサーバプロセスで、LXCはクライアントとして使う。
lxd init
Installing LXD snap, please be patient.
LXDがサーバプロセスで、LXCはクライアントとして使う。
以下が設定。ストレージバックエンドをdirにした。わざわざブロックデバイスにする必要はないと思った次第。ただ、コンテナのファイルとベースOSのファイルシステムが同一なので、何かあった場合にはリスクがあるかもしれない。それを防ぐには、zfsとかbtrfsの方がいいかもしれない。
Would you like to use LXD clustering? (yes/no) [default=no]:
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]:
Name of the storage backend to use (powerflex, zfs, btrfs, ceph, dir, lvm) [default=zfs]: dir
Would you like to connect to a MAAS server? (yes/no) [default=no]:
Would you like to create a new local network bridge? (yes/no) [default=yes]:
What should the new bridge be called? [default=lxdbr0]:
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
Would you like the LXD server to be available over the network? (yes/no) [default=no]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]:
Would you like a YAML “lxd init” preseed to be printed? (yes/no) [default=no]:
ブリッジ接続のプロファイルの作成
lxc profile create bridge
lxc network list
lxc network attach-profile br0 bridge eth0
lxc profile device add bridge root disk path=/ pool=default
lxc profile list
lxc profile show bridge
イメージのプル
Ubuntuのイメージをそのまま使う。OSのセットアップは不要。
lxc image copy ubuntu:24.04 local: --alias noble
k3s用のProfileの作成
ホストのリソース状況によるが、CPUとメモリのスペックは、もう少し大きくしてもいいかもしれない。
lxc profile create k3s
cat << EOF > k3s.cnf
config:
security.nesting: "true"
security.privileged: "true"
limits.cpu: "2"
limits.memory: 4GB
limits.memory.swap: "false"
linux.kernel_modules: overlay,nf_nat,ip_tables,ip6_tables,netlink_diag,br_netfilter,xt_conntrack,nf_conntrack,ip_vs,vxlan
raw.lxc: |
lxc.apparmor.profile = unconfined
lxc.cgroup.devices.allow = a
lxc.mount.auto=proc:rw sys:rw
lxc.cap.drop =
description: Profile settings for a bridged k3s container
devices:
eth0:
name: eth0
nictype: bridged
parent: br0
type: nic
kmsg:
path: /dev/kmsg
source: /dev/kmsg
type: unix-char
root:
path: /
pool: default
type: disk
name: k3s
used_by:
EOF
lxc profile edit k3s < k3s.cnf
プライベートレジストリコンテナの作成
USER_PASSWD="hogehoge"
CONTAINER=k3s-registry
lxc init local:noble ${CONTAINER} --profile=bridge
lxc list
lxc config set ${CONTAINER} user.user-data - <<EOF
#cloud-config
hostname: ${CONTAINER}
timezone: Etc/UTC
locale: en_US.utf8
package_update: true
package_upgrade: true
packages:
- openssh-server
- docker-registry
users:
- name: k3suser
shell: /bin/bash
ssh-authorized-keys:
- $(cat ~/.ssh/id_rsa.pub)
sudo: ALL=(ALL) NOPASSWD:ALL
passwd: "${USER_PASSWD}"
lock_passwd: false
chpasswd:
expire: false
list:
- k3suser:${USER_PASSWD}
ssh_pwauth: true
write_files:
- content: |
alias diff='diff --color=auto'
alias ip='ip -color=auto'
path: /etc/profile.d/Z99-addedcolour.sh
runcmd:
- sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
- systemctl restart ssh
- 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
- systemctl restart docker-registry
- systemctl enable docker-registry
EOF
lxc config show ${CONTAINER}
lxc start ${CONTAINER}
lxc list
各ノード(3ノード分)の作成
ループで回して、完全同一構成をミスなく作る。CONTAINERLISTに、コンテナノード名を必要な数だけ列記すればもっと多くできる。unzipは本来は不要。
USER_PASSWD="hogehoge"
export CONTAINERLIST="k3s-test1 k3s-test2 k3s-test3"
for CONTAINER in $CONTAINERLIST; do
echo "Creating container: $CONTAINER"
lxc init local:noble $CONTAINER --profile=k3s
lxc config set $CONTAINER user.user-data - <<EOF
#cloud-config
hostname: $CONTAINER
timezone: Etc/UTC
locale: en_US.utf8
package_update: true
package_upgrade: true
packages:
- openssh-server
- bash-completion
- nfs-common
- jq
- apt-transport-https
- apparmor-utils
- unzip
users:
- name: k3suser
shell: /bin/bash
ssh-authorized-keys:
- $(cat ~/.ssh/id_rsa.pub)
sudo: ALL=(ALL) NOPASSWD:ALL
passwd: ${USER_PASSWD}
lock_passwd: false
chpasswd:
expire: false
list:
- k3suser:${USER_PASSWD}
ssh_pwauth: true
write_files:
- content: |
alias diff='diff --color=auto'
alias ip='ip -color=auto'
path: /etc/profile.d/Z99-addedcolour.sh
runcmd:
- sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
- systemctl restart ssh
EOF
lxc start $CONTAINER
done
lxc list
これでプライベートレジストリ1ノードとk3s用のノードができた。
K3sの構築
通常のk3sノードの構築方法と同じ。ほんと特殊なことは一切ない。
マスターノードでは以下を実行する。
基本的には、以下と同じ。ただし、Longhornは入れられるかどうかはやったことはない。
https://www.blog.slow-fire.net/2025/02/19/純粋にk3sだけを作る%E3%80%821ip/
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.31 K3S_KUBECONFIG_MODE="644" sh -
LOCALIPADDR=<プライベートレジストリノード>
REGISTRYHOST=${LOCALIPADDR}
REGISTRYPORT=5000
REGISTRY=${REGISTRYHOST}:${REGISTRYPORT}
REGISTRYURL=http://${REGISTRY}
cat <<EOF >/etc/rancher/k3s/registries.yaml
mirrors:
"${REGISTRY}":
endpoint:
- "${REGISTRYURL}"
EOF
systemctl restart k3s
cat /var/lib/rancher/k3s/server/node-token
ノードトークンをメモする。
ワーカーノードでは以下を実行する。また、ワーカーノードにもプライベートレジストリの設定をしないと、プライベートレジストリからイメージが取り出せない。マスターノードと違ってサービス名はk3s-agentなのに注意。
MASTER_IP=<マスターノードIP>
curl -sfL https://get.k3s.io | K3S_URL=https://${MASTER_IP}:6443 K3S_TOKEN=<ノードトークン> sh -
LOCALIPADDR=<プライベートレジストリノード>
REGISTRYHOST=${LOCALIPADDR}
REGISTRYPORT=5000
REGISTRY=${REGISTRYHOST}:${REGISTRYPORT}
REGISTRYURL=http://${REGISTRY}
mkdir -p /etc/rancher/k3s/
cat <<EOF >/etc/rancher/k3s/registries.yaml
mirrors:
"${REGISTRY}":
endpoint:
- "${REGISTRYURL}"
EOF
systemctl restart k3s-agent
マスターノードに戻り
export WORKERLIST="k3s-test2 k3s-test3"
for CONTAINER in $WORKERLIST; do
kubectl label node $CONTAINER node-role.kubernetes.io/worker=worker
done
kubectl get node -o wide
ざっくり書いたがこんな感じ。LXDだからと言って特殊なことはない。
マルチノードなので、ロードバランサーとかは入れ替えたくなるかもしれない。
以前、単体でK3sを動かす方法を書いたが、もう少し深く考慮して検証したい場合は、こっちのほうがいいかもしれない。このサーバのスペックはK3sを1台でネイティブで作った環境と同じスペック。また、k3sノードが必要になれば、LXDのノードを足して、コンテナを足せばいい。K3sは、足元のLXDを意識していないので、また、潰す場合もコンテナを停止して、削除するだけなので、ホストへの汚染も少ない。
それにしても、IPv6を有効にしていると、IPアドレスも個人情報になってしまうので、キャプチャーが面倒だな。