モチベーション
kubeadmでKubernetesクラスタ建ててぇなァ~って思っても、自前Proxmox環境に毎回Ubuntuを建てるのがだるい。
ということで、autoinstallを使ってUbuntu VM作成をKubeadmでコンテナ環境をデプロイする手前まで一気に進めてみる。
VagrantとかTerraformとかは知らん。
TL;DR
-
kubeadmを実行できる直前までのサンプル
#cloud-config autoinstall: version: 1 identity: hostname: autoinstall-test password: (snip) username: thrust2799 realname: 'thrust2799' network: version: 2 ethernets: ens18: dhcp4: false addresses: - 192.168.100.1/24 nameservers: addresses: - 192.168.100.254 search: - mylab.home ens19: dhcp4: true ens20: addresses: - 172.16.1.1/24 storage: config: - type: disk id: disk-0 name: '' match: size: smallest ptable: gpt wipe: superblock preserve: false - type: partition id: partition-0 device: disk-0 number: 1 size: 512M flag: boot preserve: false grub_device: true - type: partition id: partition-1 device: disk-0 number: 2 size: 1G flag: '' preserve: false grub_device: false - type: partition id: partition-2 device: disk-0 number: 3 size: -1 flag: '' preserve: false grub_device: false - type: lvm_volgroup id: lvm_volgroup-0 name: ubuntu-vg devices: - partition-2 preserve: false - type: lvm_partition id: lvm_partition-0 name: ubuntu-lv volgroup: lvm_volgroup-0 size: -1 sipe: superblock preserve: false - type: format id: format-0 volume: partition-0 fstype: fat32 label: ESP - type: format id: format-1 volume: partition-1 fstype: ext4 label: BOOT - type: format id: format-2 volume: lvm_partition-0 fstype: ext4 label: ROOT - type: mount id: mount-0 device: format-0 path: /boot/efi - type: mount id: mount-1 device: format-1 path: /boot - type: mount id: mount-2 device: format-2 path: / swap: size: 0 locale: "en_US.UTF-8" keyboard: layout: "jp" timezone: "Asia/Tokyo" ssh: install-server: true authorized-keys: - ssh-ed25519 (snip) allow-pw: false debconf-selections: | openssh-server openssh-server/permit-root-login boolean false packages: - curl - ca-certificates - gpg - apt-transport-https apt: preserve_source_list: false mirror-selection: primary: - country-mirror geoip: true updates: all shutdown: reboot late-commands: - echo overlay | tee -a /target/etc/modules-load.d/kubernetes.conf > /dev/null - echo br_netfilter | tee -a /target/etc/modules-load.d/kubernetes.conf > /dev/null - echo net.bridge.bridge-nf-call-iptables = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null - echo net.bridge.bridge-nf-call-ip6tables = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null - echo net.ipv4.ip_forward = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null - curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /target/etc/apt/keyrings/docker.asc - curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | gpg --dearmor -o /target/etc/apt/keyrings/kubernetes.gpg - chmod a+r /target/etc/apt/keyrings/docker.asc - echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" | tee /target/etc/apt/sources.list.d/docker.list > /dev/null - echo "deb [signed-by=/etc/apt/keyrings/kubernetes.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb /" | tee /target/etc/apt/sources.list.d/kubernetes.list > /dev/null - curtin in-target --target=/target -- apt update - curtin in-target --target=/target -- apt upgrade -y - curtin in-target --target=/target -- apt install -y containerd.io kubelet kubeadm kubectl - curtin in-target --target=/target -- apt-mark hold kubelet kubeadm kubectl - curtin in-target --target=/target -- containerd config default | tee /target/etc/containerd/config.toml > /dev/null - echo "KUBELET_EXTRA_ARGS=--cgroup-driver=systemd" | tee -a /target/etc/default/kubelet > /dev/null - sed -ie "s/SystemdCgroup = false/SystemdCgroup = true/" /target/etc/containerd/config.toml
-
Ubuntu ServerのインストーラのGrubで起動パラメータをいじる
linux /casper/vmlinuz autoinstall ip=192.168.100.1:::255.255.255.0::ens18:off ds=nocloud-net\;s=http://192.168.100.253/autoinstall-test
autoinstallとは
Ubuntuをインストールするときに使える自動化の仕組みとして「autoinstall」が用意されている。
RedHat系でいうところの「Kickstart」に相当する。
Ubuntu Server 20.04以降、およびUbuntu Desktop 23.04以降でサポートされている。
Cloud-Initの仕組みを利用してインストールデータを与えられるほか、インストールメディアに同梱して読み込むこともできる。
今回インストールメディアは特に加工せず、Cloud-Initの仕組みを利用して進める。
なお、Cloud-InitやYamlの仕様を概ね理解している前提とする。
user-data
を作る
前提
- インストール対象のホスト
- NIC1 / ens18: Cloud-Initが使えるNW、静的にアドレス割当て(
192.168.100.1/24
とする) - NIC2 / ens19: インターネット向けのNW、DHCPでアドレス自動割当て
- NIC3 / ens20: 内部のテキトーNW
- NIC1 / ens18: Cloud-Initが使えるNW、静的にアドレス割当て(
- Cloud-Init用のホスト
- Cloud-Initが使えるNWに接続(
192.168.100.253/24
とする)
- Cloud-Initが使えるNWに接続(
- Gateway
-
192.168.100.254/24
とする
-
- インターネット向けのNW
- OSのアップデートやパッケージ取得などのために用意しておく
大まかな構造
今回作ったuser-data
大体は次の通り。
#cloud-config
からversion: 1
までは固定で冒頭に含める。
autoconfig
は唯一のトップレベルなキー要素となる。
#cloud-config
autoinstall:
version: 1
identity:
# ホスト系の設定
network:
# Netplanの設定
storage:
# インストール先ディスクの設定
locale: # ロケール
keyboard:
# キーボード設定
timezone: # タイムゾーン設定
ssh:
# `identity`で規定するユーザ用のSSH設定
debconf-selections:
# debconf関連
packages:
# 追加パッケージ設定
apt:
# aptリポジトリ設定
updates: # インストール後のアップデート設定
shutdown: # インストール完了後の動作
late-commands:
# その他、インストール完了後に併せて実施するコマンド群
identity
要素
この要素へはホスト系の設定を記載する。
hostname: autoinstall-test
password: (snip)
username: thrust2799
realname: 'thrust2799'
hostname
はインストールするUbuntuのホスト名。
password
とusername
は初期ユーザとして設定する項目で、特にpassword
は平文でなくshadowで投入する必要がある。(セキュア)
realname
はオプションで、初期ユーザの本名を入れたりする。
shadow生成方法はいろいろあるが、今回はOpenSSLを利用する方法で生成する。
$ openssl password -6 -salt=<random string> <password>
network
要素
この要素へはNetplanの設定を記載する。
NetplanやCloud-Init(network-config
)と同じ内容になる。
network:
version: 2
ethernets:
ens18:
dhcp4: false
addresses:
- 192.168.100.1/24
nameservers:
addresses:
- 192.168.100.254
search:
- mylab.home
ens19:
dhcp4: true
ens20:
addresses:
- 172.16.1.1/24
このあたり、本当はあまりよろしくないが自前環境では確実にens18
から始まることが分かっているので、それを前提にProxmox VE側と設定を合わせている。
IF名は基本的にudevで制御されるので、たとえ物理IFだったとしてもPCIスロット番号やポート番号からある程度の予想ができる。また、その他の仮想化環境上のVMでもIF名の法則性が見られることもある。
仮想化環境のバージョンアップやLinuxカーネルのバージョンアップでNICの命名規則が変わることもあるため、本当に設定を固定したいときはMACアドレスを利用してIF名を固定すると良い。
storage
要素
この要素はインストール先ディスクの設定を記載する。
省略した場合はデフォルト動作が実行されるが、複数のディスクがある場合や大容量のディスクをすべて使い切りたい場合などは個別に設定する。
今回は2本のディスクをアタッチしている状態なので、詳細なパーティション構成も含めて設定していく。
なお、おおらかに設定したいときはconfig:
の代わりにlayout:
が使える。
※詳細はリファレンス参照。
長すぎるので折り畳み
storage:
config:
- type: disk
id: disk-0
name: ''
match:
size: smallest
ptable: gpt
wipe: superblock
preserve: false
- type: partition
id: partition-0
device: disk-0
number: 1
size: 512M
flag: boot
preserve: false
grub_device: true
- type: partition
id: partition-1
device: disk-0
number: 2
size: 1G
flag: ''
preserve: false
grub_device: false
- type: partition
id: partition-2
device: disk-0
number: 3
size: -1
flag: ''
preserve: false
grub_device: false
- type: lvm_volgroup
id: lvm_volgroup-0
name: ubuntu-vg
devices:
- partition-2
preserve: false
- type: lvm_partition
id: lvm_partition-0
name: ubuntu-lv
volgroup: lvm_volgroup-0
size: -1
sipe: superblock
preserve: false
- type: format
id: format-0
volume: partition-0
fstype: fat32
label: ESP
- type: format
id: format-1
volume: partition-1
fstype: ext4
label: BOOT
- type: format
id: format-2
volume: lvm_partition-0
fstype: ext4
label: ROOT
- type: mount
id: mount-0
device: format-0
path: /boot/efi
- type: mount
id: mount-1
device: format-1
path: /boot
- type: mount
id: mount-2
device: format-2
path: /
swap:
size: 0
type: disk
:物理ディスク設定
match:
でどのディスクを対象にするか制御できるが、今回は2台のみなので小さいほう(smallest
)を対象にする。
GPTディスクとして使用する。
- type: disk
id: disk-0
name: ''
match:
size: smallest
ptable: gpt
wipe: superblock
preserve: false
type: partition
:パーティション設定
device:
で対象のディスクIDを設定し、パーティション番号とサイズ、その他設定を記載する。
size:
にはfdisk
で使うような補助記号(M
やG
など)が使えるほか、末尾のパーティションのみ-1
で最大容量の割当てができる。
flag:
にはパーティションに与える起動ディスクフラグなどを設定する。空の時は''
を設定する。
- type: partition
id: partition-0
device: disk-0
number: 1
size: 512M
flag: boot
preserve: false
grub_device: true
type: lvm_volgroup
:LVM VG設定
devices:
でVGに含めるパーティションをリスト形式で追加する。
- type: lvm_volgroup
id: lvm_volgroup-0
name: ubuntu-vg
devices:
- partition-2
preserve: false
`type: lvm_partition:LVM LV設定
volgroup:
でLVとして切り出すVGを記載する。
size:
の考え方はパーティション設定と同じ。
- type: lvm_partition
id: lvm_partition-0
name: ubuntu-lv
volgroup: lvm_volgroup-0
size: -1
wipe: superblock
preserve: false
type: format
:ファイルシステム設定
volume:
に設定対象のパーティションを記載する。lvm_partition
も設定可能。
fstype:
にはフォーマット形式のファイスシステム名を記載する。(EFIならfat32
固定)
label:
でラベルを付けることもできる。
- type: format
id: format-0
volume: partition-0
fstype: fat32
label: ESP
`type: mount``:マウントパス設定
device:
でマウント対象のファイルシステムを記載する。
path:
にはマウント先のディレクトリを記載する。
- type: mount
id: mount-0
device: format-0
path: /boot/efi
マウントオプションの指定はないっぽい。特殊なことがしたい場合はlate-command
で/etc/fstab
をいじる方が良さそう?
その他の主要設定
ロケール設定、キーボード設定、タイムゾーン設定、SSH設定を記載している。
SSH設定については、サーバをインストールした上で初期ユーザへのログインに使用する公開鍵を登録しパスワードログインを無効化している。
また、debconf-selections:
でRootユーザのログインを無効している。
locale: "en_US.UTF-8"
keyboard:
layout: "jp"
timezone: "Asia/Tokyo"
ssh:
install-server: true
authorized-keys:
- (snip)
allow-pw: false
debconf-selections: |
openssh-server openssh-server/permit-root-login boolean false
パッケージ関連設定
aptの関連設定と、追加で入れる設定だったり更新の設定だったりを記載する。
追加パッケージについてはpackages:
へリスト形式で指定する。(今回はKubernetesを入れるために必要とされるものを記載している。)
apt:
にはリポジトリ設定を入れているが、正直ここはデフォルトでも良さそう。
プライベートミラーや追加リポジトリがある場合はここに記載するかlate-command
で設定する。
updates:
はインストール後の更新有無を記載する。
security
だとセキュリティパッチのみ、all
だとすべて更新となる。
packages:
- curl
- ca-certificates
- gpg
- apt-transport-https
apt:
preserve_source_list: false
mirror-selection:
primary:
- country-mirror
geoip: true
updates: all
インストール後の挙動について
shutdown:
ですべての処理が完了した後の動作を記載する。
poweroff
にするとシャットダウンして電源を切る動作となる。
デフォルトはreboot
(再起動)。
late-command
はインストール完了後にユーザ指定で実行するコマンドリストを与える。
shutdown: reboot
late-commands:
- echo overlay | tee -a /target/etc/modules-load.d/kubernetes.conf > /dev/null
- echo br_netfilter | tee -a /target/etc/modules-load.d/kubernetes.conf > /dev/null
- echo net.bridge.bridge-nf-call-iptables = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null
- echo net.bridge.bridge-nf-call-ip6tables = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null
- echo net.ipv4.ip_forward = 1 | tee -a /target/etc/sysctl.d/kubernetes.conf > /dev/null
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /target/etc/apt/keyrings/docker.asc
- curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | gpg --dearmor -o /target/etc/apt/keyrings/kubernetes.gpg
- chmod a+r /target/etc/apt/keyrings/docker.asc
- echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable"
| tee /target/etc/apt/sources.list.d/docker.list > /dev/null
- echo "deb [signed-by=/etc/apt/keyrings/kubernetes.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb /"
| tee /target/etc/apt/sources.list.d/kubernetes.list > /dev/null
- curtin in-target --target=/target -- apt update
- curtin in-target --target=/target -- apt upgrade -y
- curtin in-target --target=/target -- apt install -y containerd.io kubelet kubeadm kubectl
- curtin in-target --target=/target -- apt-mark hold kubelet kubeadm kubectl
- curtin in-target --target=/target -- containerd config default | tee /target/etc/containerd/config.toml > /dev/null
- echo "KUBELET_EXTRA_ARGS=--cgroup-driver=systemd" | tee -a /target/etc/default/kubelet > /dev/null
- sed -ie "s/SystemdCgroup = false/SystemdCgroup = true/" /target/etc/containerd/config.toml
late-command
について
インストーラ環境からコマンド実行する内容を記載する。
基本的にインストーラのシェルで実行可能な内容のみが指定できるので、追加パッケージが必要なものはインストールした環境に入って(chrootして)から実行すると良い。
user-data
要素を与えることで、再起動後のCloud-Init動作を制御することができる。
こちらで追加パッケージに関連する動作を入れても良いかもしれない。
インストールした環境でコマンドを動かす際は、動かしたいコマンドの先頭に下記を追加する。
- curtin in-target --target=/target -- <command>
user-data
に問題がないか(最低限)確認する
これを参考にVlidationスクリプトを用意する。
依存パッケージの導入まで完了したら、./scripts/validate-autoinstall-user-data.py
にファイルを渡して問題がないか確認する。
$ ./scripts/validate-autoinstall-user-data.py ~/manifests/autoinstall-test/user-data
Success: The provided autoinstall config validated successfully
ここでの確認は構文チェックや必須条件を満たすかの確認にとどまる。
late-command
に不備があったりnetwork
要素に誤りがあっても検知してくれないので注意する。(2敗)
httpでuser-data
を配信できるようにする
作成したuser-data
ファイルをUbuntuのインストーラからアクセスできるようにする。
いろいろ選択肢はあるけど、今回はDocker ComposeでHTTPサーバを建てて配信する方式を採用した。
用意したファイル
compose.yml
name: nginx-autoinstall
services:
nginx:
build:
context: .
dockerfile: dockerfile
volumes:
- ./manifests:/webroot # `./manifests/<hostname>/user-data`の構造で格納する
- ./nginx.conf:/etc/nginx/conf.d/nginx.conf # Nginxの設定
ports:
- "80:8080"
dockerfile
FROM nginxinc/nginx-unprivileged:stable-alpine # rootless nginx
EXPOSE 8080
USER nginx
nginx.conf
server {
listen 8080;
server_name 192.168.100.253;
location / {
root /webroot;
index index.html;
}
}
Ubuntu ServerのインストーラのGrubで起動パラメータをいじる
Ubuntu ServerのISOからOSをインストールする。
この時、cmdline
としてCloud-Initを動作させるためのパラメータを渡す必要がある。
cmdline
変更方法
まずは通常通り起動し、Grubのメニュー表示を待つ。
↓この画面になったら、Try or Install Ubuntu Server
を選択した状態で「E」キーを押下する。
するとテキストエディタが表示されるので、linux
から始まる行の末尾を編集する。
cmdline
に与える情報
最終的にはこれを与える。
linux /casper/vmlinuz autoinstall ip=192.168.100.1:::255.255.255.0::ens18:off ds=nocloud-net\;s=http://192.168.100.253/autoinstall-test
-
linux /casper/vmlinuz
- 起動するvmlinuzの指定
-
autoinstall
- autoinstallを有効にする
-
ip=192.168.100.1:::255.255.255.0::ens18:off
- ホストのIPアドレス設定する
- ens18に対して静的に
192.168.100.1/24
を割当て
-
ds=nocloud-net\;s=http://192.168.100.253/autoinstall-test/
- Cloud-Initの動作設定
-
nocloud-net
環境での動作 -
http://192.168.100.253/autoinstall-test/
にuser-data
などを取りに行く- 先に設定したnginxコンテナの接続先を指定する
- セミコロン(
;
)はエスケープする
-
- Cloud-Initの動作設定
最後に
これでautoinstallによりUbuntu Server 24.04が自動インストールされる。
再起動が完了すればSSH接続もできるようになるので、煩わしいインストール作業から解放され快適なUbuntu Server構築が実現した。
あとはkubeadmを叩くだけでKubernetesクラスタができる。