Fullmoon System

KubesprayでKubernetesを構築:Rocky Linux 9・Cilium運用ガイド

EdwardMoon

Kubesprayを使ったKubernetes構築ガイドです。Rocky Linux 9のノードにKubesprayとAnsibleで再現可能なクラスターを構築し、3台のコントロールプレーン、etcdのクォーラム、API VIP、Ciliumネットワークを展開後に検証します。コマンドの実行だけでなく、障害やアップグレードに対応できる構成かを確認します。

旧版で使用したKubernetes 1.31.6は2025年11月にサポートが終了しました。この改訂版は2026年7月時点のKubespray 2.31.0の標準構成であるKubernetes 1.35.4と、同梱のCilium 1.19.3を基準とします。実際の導入前には、選択したKubesprayタグのチェックサムとCiliumのKubernetes互換性マトリクスを併せて確認してください。

KubesprayによるKubernetes構築構成:Ansible、API VIP、3台のコントロールプレーン、3台のワーカー、Cilium、etcdバックアップ
Ansible制御ノードからAPI VIPを経由する構成で、3台のコントロールプレーン・etcdと3台のワーカーを展開し、Ciliumの通信経路と外部バックアップを検証します。

KubesprayによるKubernetes構築のバージョンとサポート範囲

構成要素この記事の基準選定理由と注意点
Rocky Linux9.xの最新パッチ全ノードのマイナーバージョン、カーネル、時刻同期を統一
Kubesprayv2.31.0タグリリースタグとrequirementsを固定して再現性を確保
Kubernetesv1.35.4Kubespray 2.31.0の既定値で、サポート期間中のマイナーバージョン
Ciliumv1.19.3Kubespray 2.31.0の同梱バージョン
containerdKubesprayの既定値2.31.0リリースで検証された組み合わせを使用
etcd3メンバー1台の障害でも過半数による合意を維持
Kubernetesの最新版が、KubesprayとCiliumでも検証された組み合わせとは限りません。2026年7月のKubernetes最新安定版は1.36.2ですが、Kubespray 2.31.0の既定値は1.35.4です。Ciliumのstableドキュメントで保証対象のe2eテスト一覧も確認し、一覧にない組み合わせは運用前に別途検証します。

KubesprayによるKubernetes構築のトポロジー

ホストIPアドレスの例役割
ansible0110.20.0.5Kubesprayの実行、inventoryとartifactの保管
api.k8s.example.com10.20.0.10外部HAProxy/ロードバランサーのVIP:6443
cp01~cp0310.20.0.11~13kube_control_plane + etcd
wk01~wk0310.20.0.21~23kube_node
backup0110.20.0.30暗号化したetcdスナップショットとinventoryのバックアップ

API VIPはHAProxy 1台だけに依存させず、最低2台のプロキシとVRRP、または既存のロードバランサーで構成します。VIPの6443ヘルスチェックは全コントロールプレーンのkube-apiserverを対象とします。ノード、Pod、ServiceのCIDRと、社内ネットワーク、VPN、ストレージネットワークが重複しないよう、先にIPアドレス計画を確定します。

名前解決とポートの確認

getent hosts api.k8s.example.com cp01 cp02 cp03 wk01 wk02 wk03

for host in cp01 cp02 cp03 wk01 wk02 wk03; do
  printf '%-6s ' "$host"
  timeout 3 bash -c "</dev/tcp/${host}/22" && echo SSH_OK || echo SSH_FAIL
done

timeout 3 bash -c '</dev/tcp/api.k8s.example.com/6443'   && echo API_VIP_OK || echo API_VIP_FAIL

KubesprayによるKubernetes構築:Rocky Linux 9ノードの事前確認

Kubesprayがcontainerd、kubelet、カーネルモジュール、sysctlを管理するため、別のインストールスクリプトを先に実行しないでください。まず全ノードのOS、CPU、メモリ、ディスク、cgroup、時刻同期と、既存コンテナーランタイムの残存状況を確認します。

cat /etc/rocky-release
uname -r
timedatectl status
free -h
lsblk -f
findmnt -no FSTYPE,OPTIONS / /var
stat -fc %T /sys/fs/cgroup
swapon --show
rpm -qa | grep -E 'kube(let|adm|ctl)|containerd|docker|cri-o' || true
Kubernetes 1.35はcgroup v2を基本とします。従来のcgroup v1回避オプションに依存せず、Rocky Linux 9のsystemd cgroup v2構成を維持します。SELinuxやfirewalldを無条件に無効化せず、Kubesprayリリース文書の対応方法と組織のネットワークACLを検証してください。

専用管理アカウントとSSHホスト鍵の登録

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/kubespray_ed25519

for host in cp01 cp02 cp03 wk01 wk02 wk03; do
  ssh-copy-id -i ~/.ssh/kubespray_ed25519.pub "ansible@${host}"
  ssh-keyscan -H "$host" >> ~/.ssh/known_hosts.new
done

sort -u ~/.ssh/known_hosts.new >> ~/.ssh/known_hosts
rm -f ~/.ssh/known_hosts.new
chmod 0600 ~/.ssh/known_hosts

ssh-keyscanの結果は、信頼できるコンソールや資産管理システムのフィンガープリントと照合してから登録します。StrictHostKeyCheckingを無効にすると、中間者攻撃を検出できません。ansibleアカウントには必要なsudo権限だけを付与し、rootでの直接SSHログインは許可しません。

KubesprayによるKubernetes構築:2.31.0の実行環境を固定

Kubesprayの構築ファイルはmainブランチではなくリリースタグで固定します。Python仮想環境とrequirements.txtを使用すると、システムPythonとAnsibleの依存関係を混在させず、同じ制御環境を再現できます。

sudo dnf install -y git python3.12 python3.12-pip

git clone --branch v2.31.0 --depth 1   https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
git verify-tag v2.31.0 || git show --show-signature --no-patch v2.31.0

python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --requirement requirements.txt

python --version
ansible --version
git describe --tags --always
Gitの署名検証は、信頼するメンテナーの鍵を別経路で確認している場合に意味を持ちます。閉域環境では、リリースアーカイブ、Python wheel、コンテナーイメージのチェックサムと由来を接続環境で検証し、承認済みリポジトリへ搬入します。

KubesprayによるKubernetes構築のinventory作成

inventory/prod/inventory.yml

cp -a inventory/sample inventory/prod
cat > inventory/prod/inventory.yml <<'YAML'
all:
  hosts:
    cp01: {ansible_host: 10.20.0.11, ip: 10.20.0.11, access_ip: 10.20.0.11}
    cp02: {ansible_host: 10.20.0.12, ip: 10.20.0.12, access_ip: 10.20.0.12}
    cp03: {ansible_host: 10.20.0.13, ip: 10.20.0.13, access_ip: 10.20.0.13}
    wk01: {ansible_host: 10.20.0.21, ip: 10.20.0.21, access_ip: 10.20.0.21}
    wk02: {ansible_host: 10.20.0.22, ip: 10.20.0.22, access_ip: 10.20.0.22}
    wk03: {ansible_host: 10.20.0.23, ip: 10.20.0.23, access_ip: 10.20.0.23}
  children:
    kube_control_plane:
      hosts: {cp01: {}, cp02: {}, cp03: {}}
    kube_node:
      hosts: {wk01: {}, wk02: {}, wk03: {}}
    etcd:
      hosts: {cp01: {}, cp02: {}, cp03: {}}
    k8s_cluster:
      children:
        kube_control_plane: {}
        kube_node: {}
    calico_rr:
      hosts: {}
YAML

etcdのメンバー数は奇数にします。コントロールプレーンとワーカーを混在させて定義せず、ansible_hostはSSH接続先、ipとaccess_ipはノード間通信のアドレスとして区別します。NATや複数NICの環境では、通知するアドレスとルーティングを個別に検証します。

次の3ファイルをエディターで開き、該当キーを追加するか既存の値を変更します。各例はファイルに記述するYAMLであり、シェルコマンドではありません。

inventory/prod/group_vars/all/all.ymlの主要設定

ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/kubespray_ed25519

loadbalancer_apiserver:
  address: 10.20.0.10
  port: 6443

kubeconfig_localhost: true
kubectl_localhost: true

Kubernetesとセキュリティの既定値

Kubespray v2.31.0のkube_versioncilium_versionには、接頭辞vを付けない数値のバージョンを指定します。ダウンロードURLやコンテナータグの接頭辞はKubesprayが付けるため、v1.35.4のように入力してはいけません。サンプルYAMLの該当キーを以下の値に変更し、同じキーを後ろに追加しないでください。

kube_version: 1.35.4
container_manager: containerd
kube_network_plugin: cilium

kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
cluster_name: cluster.local

kube_api_anonymous_auth: false
remove_anonymous_access: true
kubernetes_audit: true
supplementary_addresses_in_ssl_keys:
  - 10.20.0.10
  - api.k8s.example.com

PodとServiceのCIDRは、ルーター、VPN、データセンターのアドレス範囲と重複させてはいけません。使用中のCIDRを後で変更する作業は、新しいクラスターへの移行に匹敵する規模になる場合があります。API VIPとDNS名はkube-apiserver証明書のSANに含めます。

CiliumのオーバーレイとHubbleの設定

cilium_version: 1.19.3
cilium_tunnel_mode: vxlan
cilium_identity_allocation_mode: crd
cilium_ipam_mode: kubernetes
cilium_cni_exclusive: true

cilium_enable_hubble: true
cilium_hubble_install: true
cilium_hubble_tls_generate: true
cilium_enable_hubble_ui: false
kube-proxy replacementは単なる性能オプションではなく、Serviceのデータパスを変更する設定です。この基本ガイドではkube-proxyを維持します。置き換える場合は、cilium_kube_proxy_replacement、APIのグローバルエンドポイント、DSR・SNAT、ホストファイアウォールの動作を別途設計し、負荷試験と障害試験を行います。

KubesprayによるKubernetes構築前の検証

CIDRの重複とinventoryの構造

ip route
ip -4 address show

grep -R --line-number -E   'kube_(service_addresses|pods_subnet)|loadbalancer_apiserver|kube_version|cilium_version'   inventory/prod/group_vars

ansible-inventory -i inventory/prod/inventory.yml --graph
ansible-inventory -i inventory/prod/inventory.yml --list   > inventory/prod/inventory-expanded.json

SSH・sudo・Pythonの確認

ansible -i inventory/prod/inventory.yml all -m ping
ansible -i inventory/prod/inventory.yml all --become   -m command -a 'id'
ansible -i inventory/prod/inventory.yml all --become   -m shell -a 'python3 --version; stat -fc %T /sys/fs/cgroup; swapon --show'

pingモジュールの成功だけでは不十分です。becomeが非対話で動作し、Pythonインタープリターとcgroup v2の構成が全ノードで一致していることを確認します。Ansible VaultのパスワードファイルとSSH秘密鍵はリポジトリにコミットしないでください。

playbookの構文と変更一覧を確認

ansible-playbook -i inventory/prod/inventory.yml   cluster.yml --syntax-check

git status --short
git diff -- inventory/prod/group_vars inventory/prod/inventory.yml

tar --exclude='credentials' --exclude='artifacts'   -czf "inventory-prod-$(date +%F).tgz" inventory/prod

KubesprayによるKubernetes構築の実行

最初から全ノードに任意のタグを指定して実行せず、検証済みinventoryで公式のcluster.ymlを実行します。失敗した場合は、同じコマンドを再実行する前に最初に失敗したタスクとノードの状態を確認します。Kubesprayは冪等性を目指していますが、外部ロードバランサーとネットワークの状態は別途管理が必要です。

mkdir -p logs

ansible-playbook -i inventory/prod/inventory.yml   cluster.yml --become   2>&1 | tee "logs/cluster-$(date +%F-%H%M%S).log"

test ${PIPESTATUS[0]} -eq 0
teeを使用すると、シェルが最後に返す終了コードがteeの結果になる場合があります。PIPESTATUS[0]でansible-playbookの結果を確認してください。ログにはホスト名、IPアドレス、タスク結果が含まれるため、アクセス権限と保管期間を決め、外部共有前に機密情報を除去します。

KubesprayによるKubernetes構築結果の検証

kubeconfigとコントロールプレーンのエンドポイント

find inventory/prod/artifacts -maxdepth 2 -type f -ls

install -d -m 0700 "$HOME/.kube"
install -m 0600 inventory/prod/artifacts/admin.conf "$HOME/.kube/config"

kubectl config view --minify
kubectl cluster-info
kubectl get --raw='/readyz?verbose'

artifactのファイル名はKubesprayのタグと設定により異なる場合があるため、まずfindの結果を確認します。kubeconfigにはcluster-adminの認証情報が含まれるので、共有ホームディレクトリやCIのartifactにそのまま配置しないでください。運用担当者はOIDCと最小権限のRBACを使用します。

ノード・etcd・システムPodの状態

kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get --raw='/livez?verbose'

ansible -i inventory/prod/inventory.yml etcd --become   -m command -a 'systemctl status etcd --no-pager'

kubectl -n kube-system get endpoints kube-dns
kubectl get events -A --sort-by='.lastTimestamp' | tail -n 50

CiliumとHubbleの状態

kubectl -n kube-system rollout status daemonset/cilium --timeout=10m
kubectl -n kube-system get pods -l k8s-app=cilium -o wide

cilium status --wait --wait-duration 10m
cilium connectivity test

kubectl -n kube-system get secret | grep -i hubble
kubectl -n kube-system logs deployment/hubble-relay --tail=100

cilium connectivity testは複数のnamespaceとPodを作成するため、運用方針に沿った検証用namespaceで実行し、終了後に削除します。全ノードのCilium DaemonSet、operator、CoreDNS、Serviceルーティングが正常であることを確認してからワークロードを展開します。

KubesprayによるKubernetes構築:ワークロードとNetworkPolicyの検証

Deployment・Service・DNSのスモークテスト

kubectl create namespace smoke-test
kubectl -n smoke-test create deployment web   --image=registry.k8s.io/e2e-test-images/agnhost:2.53   -- /agnhost netexec --http-port=8080
kubectl -n smoke-test expose deployment web --port=80 --target-port=8080
kubectl -n smoke-test rollout status deployment/web --timeout=5m

kubectl -n smoke-test run client --rm -it --restart=Never   --image=docker.io/library/busybox:1.36.1 --command -- /bin/sh -c 'nslookup web; wget -qO- http://web/'

既定で拒否してから明示的に許可

cat <<'YAML' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-default-deny
  namespace: smoke-test
spec:
  podSelector: {matchLabels: {app: web}}
  policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-allow-client
  namespace: smoke-test
spec:
  podSelector: {matchLabels: {app: web}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector: {matchLabels: {run: client}}
      ports:
        - {protocol: TCP, port: 8080}
YAML

kubectl -n smoke-test get networkpolicy

ポリシー適用前の成功、既定の拒否による失敗、明示的な許可後の成功をそれぞれ確認します。DNSの送信通信も制限する場合は、kube-dnsサービスの実際のselectorとUDP・TCP 53を確認してください。テスト後はkubectl delete namespace smoke-testでリソースを削除します。

etcdスナップショットと復旧準備

Kubesprayによる構築が完了したら、直ちにetcdスナップショットを自動化します。スナップショットは1メンバーで作成し、クラスター外の暗号化されたストレージへコピーします。inventory、証明書、Kubesprayタグと併せて復旧手順を記録します。

# 既定のhost展開方式のetcdノード1台で実行します。
sudo systemctl cat etcd
sudo grep -E '^ETCD_(LISTEN_CLIENT_URLS|TRUSTED_CA_FILE|CERT_FILE|KEY_FILE)=' /etc/etcd.env

sudo bash -euo pipefail <<'BASH'
backup_dir=/var/backups/etcd
install -d -m 0700 "$backup_dir"
snapshot="$backup_dir/snapshot-$(date +%F-%H%M%S).db"
node_name=$(hostname -s)
ca=/etc/ssl/etcd/ssl/ca.pem
cert="/etc/ssl/etcd/ssl/admin-${node_name}.pem"
key="/etc/ssl/etcd/ssl/admin-${node_name}-key.pem"
for file in "$ca" "$cert" "$key"; do test -r "$file"; done
etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert="$ca" --cert="$cert" --key="$key" snapshot save "$snapshot"
etcdutl snapshot status "$snapshot" --write-out=table
BASH
既定のetcd_deployment_type: hostでは、etcdはsystemdサービスとして動作します。/etc/etcd.envsystemctl cat etcdで実際のエンドポイントと証明書パスを確認し、上記の値を調整してください。inventoryのホスト名とOSのhostnameが異なる場合は、管理者証明書のファイル名も実在するものに修正します。同じクラスターで復旧を試さず、隔離環境でスナップショットのリストア、APIの起動、オブジェクトの整合性まで定期的に検証します。

証明書・監査・運用の確認

sudo kubeadm certs check-expiration
kubectl auth can-i --list
kubectl auth can-i create clusterrolebindings --as=system:anonymous

kubectl get --raw='/metrics' | grep -E   'apiserver_request_total|apiserver_request_duration_seconds' | head

kubectl get nodes -o json | jq -r   '.items[] | [.metadata.name,.status.nodeInfo.kubeletVersion,.status.nodeInfo.containerRuntimeVersion] | @tsv'
  • API VIPとDNSを確認し、コントロールプレーンの1台を停止してもkubectlのリクエストが成功し続けることを確認します。
  • etcdのメンバー、リーダー、DBサイズ、スナップショットの成否をアラートに連携します。
  • ノードのファイルシステム、inode、memory pressure、PID pressure、イメージ用ファイルシステムの容量を監視します。
  • kube-apiserverの監査ログを中央ストレージへ転送し、改ざん防止と保管ポリシーを適用します。
  • 日常利用のアカウントからcluster-adminのkubeconfigを取り除き、OIDC、RBAC、短時間のセッションを使用します。
  • Ciliumのdrop、policy verdict、DNSエラー、Hubble証明書の有効期限を監視します。

KubesprayによるKubernetesのアップグレード

Kubernetesのマイナーバージョンは1段階ずつ更新し、version skewポリシーに従います。まず対象のKubernetesパッチとCiliumバージョンが、移行先Kubesprayタグのチェックサムとサポート一覧に含まれることを確認します。その後、etcdスナップショット、ワークロードのバックアップ、PodDisruptionBudget、余剰リソースを確認します。

git fetch --tags --prune
git tag --sort=-version:refname | head

# 新しいclone/venvへ既存のinventoryをコピーし、差分を確認します。
git diff v2.31.0..'<TARGET_KUBESPRAY_TAG>' --   inventory/sample roles/kubespray_defaults docs

ansible-playbook -i inventory/prod/inventory.yml   upgrade-cluster.yml --become --syntax-check

ansible-playbook -i inventory/prod/inventory.yml   upgrade-cluster.yml --become   2>&1 | tee "logs/upgrade-$(date +%F-%H%M%S).log"

test ${PIPESTATUS[0]} -eq 0

upgrade-cluster.ymlの実行前に、新リリースのurgent upgrade noteを読みます。特にKubespray 2.31では、cgroup v1、サポートを終えたingress-nginx、アーカイブされたKubernetes Dashboard、etcdの前提バージョン、削除された変数に注意してください。コントロールプレーンとワーカーを一斉に再起動しないでください。

よくあるミスと安全な代替策

ミス影響代替策
mainブランチから直接展開依存関係と既定値が随時変化するリリースタグ、requirements、チェックサムを固定
etcdを2メンバーで構成1台の障害で過半数を失う3または5の奇数メンバーで構成
APIエンドポイントが1つだけコントロールプレーンの単一障害点になる複数のLBとVIPヘルスチェックを使用
CIDRの重複Pod・Service・社内ネットワークのルーティングが競合展開前にIPAMとルートを検証
SSHホスト鍵の確認を無効化中間者攻撃を検出できないフィンガープリント検証後にknown_hostsへ登録
cluster-adminのkubeconfigを共有クラスター全体の権限が露出するOIDC、RBAC、短時間のセッションを使用
スナップショットファイルの作成だけで終了復旧できるか未確認のままになる隔離環境で定期的にリストア訓練を実施

KubesprayによるKubernetes構築のチェックリスト

  1. KubesprayタグとPython・Ansibleのrequirementsを固定した。
  2. Kubernetes・Cilium・containerdの組み合わせに関するサポート文書を確認した。
  3. 3台のコントロールプレーン・etcdと、複数のAPIロードバランサーを準備した。
  4. Pod・Service・ノード・VPN・ストレージのCIDRが重複していない。
  5. SSHホスト鍵、sudoの権限範囲、Vaultと秘密鍵の保管を検証した。
  6. ノード・API・CoreDNS・Ciliumの接続性とNetworkPolicyを試験した。
  7. etcdスナップショットを外部に保管し、隔離環境での復旧訓練を完了した。
  8. 証明書の期限、監査ログ、リソース不足、Ciliumのdropを監視している。
  9. アップグレード前にurgent note、version skew、PDB、余剰リソースを確認する。

関連資料

KubesprayによるKubernetes構築のまとめ

KubesprayによるKubernetes構築では、cluster.ymlが一度成功しただけでなく、バージョンの組み合わせ、3ノードの合意、APIエンドポイント、ネットワーク、復旧計画を併せて検証します。リリースタグとinventoryを固定し、匿名アクセスを制限して、Ciliumの接続性、NetworkPolicy、etcdのリストアを実際に試験します。この基準を満たすことで、Rocky Linux 9上の実習を運用可能なKubernetes基盤へ発展させられます。