はじめに
これはKMC Advent Calendar 2023の記念すべき1日目の記事です。
明日はtaiseiさんの「スイカゲームくらい自給自足すっか」です。
本記事ではIPv4/IPv6 DualStack環境のKuberneteクラスタを構築します。
構築した環境はGitHubに公開しています。
構成
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
akatsuki-1 Ready control-plane 3h13m v1.28.2 192.168.20.121 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
akatsuki-2 Ready <none> 177m v1.28.2 192.168.20.122 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
akatsuki-3 Ready <none> 177m v1.28.2 192.168.20.123 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
Master1台、Worker2台の計3台構成です。
OSはUbuntu22.04、コンテナランタイムはcri-o、CNIはflannelを使用しています。
Ubuntuのインストール、ネットワークの設定は完了し名前解決可能であることを前提としています。
事前準備
対象マシン: 全マシン
Kubernetesクラスタを構築するための各種パッケージのインストール等を行います1。
一連の処理はAnsibleのRole化してGitHubに公開しています。
Swap無効化
まずスワップを無効にします
sudo swapoff -a
続いて/etc/fstabからスワップの設定を削除します
# /dev/mapper/swap none swap defaults 0 0
cri-oの設定とインストール
cat <<EOF | sudo tee /etc/modules-load.d/crio.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/ /
EOF
cat <<EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:1.27.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.27/xUbuntu_22.04/ /
EOF
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:1.27/xUbuntu_22.04/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
sudo apt update
sudo apt install -y cri-o cri-o-runc
sudo systemctl daemon-reload
sudo systemctl enable crio
sudo systemctl start crio
Kubernetesインストール
sudo apt update && sudo apt install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
cat <<EOF | tee /etc/default/kubelet
KUBELET_EXTRA_ARGS=--container-runtime-endpoint='unix:///var/run/crio/crio.sock'
EOF
IPv6 Forwardingの有効化
対象マシン: 全マシン
ここからがIPv6向けの設定になります。
IPv6 パケット転送を有効にします
sudo sysctl -w net.ipv6.conf.all.forwarding=1
クラスタ作成
対象マシン: Master
Masterでクラスタを作成します2
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
clusterName: akatsuki
networking:
podSubnet: 10.245.0.0/16,2001:db8:43:0::/56
serviceSubnet: 10.97.0.0/16,2001:db8:43:1::/112
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.20.121
bindPort: 6443
nodeRegistration:
kubeletExtraArgs:
node-ip: 192.168.20.121,240b:250:9800:4a20::121
podSabunetには 10.245.0.0/16
と 2001:db8:43:0::/56
、serviceSubnetには 10.97.0.0/16
と 2001:db8:43:1::/112
を指定しています。
localAPIEndpointは1つのアドレスしか設定することはできないので、ここはIPv4アドレスを指定しています。
node-ipには各ノードのIPアドレスを指定します。IPv4/v6 DualStackの場合はここでv4とv6のアドレスを指定します。今回は 192.168.20.121
と 240b:250:9800:4a20::121
を指定しています。
ファイル作成後、クラスタを初期化します。
sudo kubeadm init --config=init/akatsuki-1.yaml
Your Kubernetes control-plane has initialized successfully!
と表示されれば成功です。
その下に表示されるコマンドを実行します。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
また、その下のworkerのjoinコマンドを控えておいてください
kubeadm join 192.168.20.121:6443 --token clvldh.vjjwg16ucnhp94qr \
--discovery-token-ca-cert-hash sha256:a4863cde706cfc580a439f842cc65d5ef112b7b2be31628513a9881cf0d9fe0e
クラスタへの参加
対象マシン: Worker2台
続いてWorker nodeに対してクラスタへの参加の処理を行います。
Workerの内1台で init/akatsuki-2.yaml
というファイルを作成します。
apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
discovery:
bootstrapToken:
apiServerEndpoint: 192.168.20.121:6443
token: "clvldh.vjjwg16ucnhp94qr"
caCertHashes:
- "sha256:a4863cde706cfc580a439f842cc65d5ef112b7b2be31628513a9881cf0d9fe0e"
# change auth info above to match the actual token and CA certificate hash for your cluster
nodeRegistration:
kubeletExtraArgs:
node-ip: 192.168.20.122,240b:250:9800:4a20::122
apiServerEndpoinには init/akatsuki-1.yaml
で advertiseAddress
に指定したものを設定してください。
tokenとcaCertHashesは先ほど控えていただいた kubeadm join
に記載されているものを設定してください。
node-ipにはWorkerのIPアドレスを指定して下さい。
以下のコマンドでクラスタに参加できます。
sudo kubeadm join --config=init/akatsuki-2.yaml
もう1台のWorkerにも同様のファイルを作成します。
apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration
discovery:
bootstrapToken:
apiServerEndpoint: 192.168.20.121:6443
token: "clvldh.vjjwg16ucnhp94qr"
caCertHashes:
- "sha256:a4863cde706cfc580a439f842cc65d5ef112b7b2be31628513a9881cf0d9fe0e"
# change auth info above to match the actual token and CA certificate hash for your cluster
nodeRegistration:
kubeletExtraArgs:
node-ip: 192.168.20.123,240b:250:9800:4a20::123
以下のコマンドでクラスタに参加します。
sudo kubeadm join --config=init/akatsuki-3.yaml
flannelの設定
対象マシン: Master1台
CNIとしてflannelの設定を行います3。
Master上でflannelのconfigを取得します。
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml -O init/kube-flannel.yaml
IPv6を有効にするため一部設定を変更します。
net-conf.json
の設定を行っている箇所があるため、そこを以下のように変更します。
net-conf.json: |
{
"Network": "10.245.0.0/16",
"IPv6Network": "2001:db8:43:0::/56",
"EnableIPv4": true,
"EnableIPv6": true,
"Backend": {
"Type": "vxlan"
}
}
NetworkとIPv6Networkには init/akatsuki-1.yaml
で podSubnet
に指定したものを設定してください。
"EnableIPv6": true
とすることでIPv6を有効にします(デフォルトではfalseです)。
最後にflannelをapplyします。
kubectl apply -f init/kube-flannel.yml
kubectl get nodes -o wide
の結果、StatusがReadyになってるはずです。
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
akatsuki-1 Ready control-plane 3h13m v1.28.2 192.168.20.121 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
akatsuki-2 Ready <none> 177m v1.28.2 192.168.20.122 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
akatsuki-3 Ready <none> 177m v1.28.2 192.168.20.123 <none> Ubuntu 22.04.3 LTS 5.15.0-88-generic cri-o://1.27.1
INTERNAL-IPにはIPv4アドレスしか付いていませんが、 kubectl describe nodes/akatsuki-1
にIPv6アドレスがついていると思います。
Addresses:
InternalIP: 192.168.20.121
InternalIP: 240b:250:9800:4a20::121
Hostname: akatsuki-1
以上でIPv4/IPv6 DualStackのKubernetes環境の構築は完了です。
IPv6のサービスを作成
ここからはIPv6アドレスでWebページを作成する方法を説明します。
今回はHelmとfluxを用います。
Webページのデプロイにはmetallbとingress-nginxを用います。
本質的な部分はmetallbとingress-nginxのServiceの設定になりますので、ご自身の環境にあった方法でDeployしてください。
ファイル構成の詳細については割愛します。
同様の環境を構築したい場合は https://github.com/segre5458/k8s-ipv6 をCloneしてください。
metallb
metallbでLoadBalancingを行います。
基本的にほぼすべてのサービスはデフォルトでIPv4しかサポートしていないため、手動で変更する必要があります。
apiVersion: v1
kind: Service
metadata:
annotations:
meta.helm.sh/release-name: metallb
meta.helm.sh/release-namespace: metallb-system
labels:
app.kubernetes.io/instance: metallb
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: metallb
helm.toolkit.fluxcd.io/name: metallb
helm.toolkit.fluxcd.io/namespace: metallb-system
name: metallb-webhook-service
namespace: metallb-system
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
- IPv6
ipFamilyPolicy: PreferDualStack
ports:
- port: 443
protocol: TCP
targetPort: 9443
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: metallb
app.kubernetes.io/name: metallb
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
重要なのは以下の部分です
ipFamilies:
- IPv4
- IPv6
ipFamilyPolicy: PreferDualStack
ipFamilyPolicyにはSingleStack/PreferDualStack/RequireDualStackを指定することができます。
DualStackにするにはPreferDualStackを指定すれば良いです。
具体的な設定内容は以下のとおりです。
詳しくはOpensourcetechブログやIPv4/IPv6 dual-stack | Kubernetesを参照してください。
- ipFamilyPolicy
- SingleStack:IPv4 or IPv6のどちらか
- PreferDualStack:DualStack or SingleStackのどちらか
- RequireDualStack:DualStack(それ以外は失敗する)
- ipFamilies
- IPv4/IPv6のどちらか、もしくは両方の指定が可能
- IPv4/IPv6のどれが適用されるかは、クラスタの構成とipFamilyPolicyで決定される
IP Advertiseの設定は以下の通りになります4。
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: metallb
namespace: metallb-system
spec:
addresses:
- 192.168.20.231-192.168.20.235
- 240b:250:9800:4a20:100::1-240b:250:9800:4a20:100::5
autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: metallb
namespace: metallb-system
spec:
ipAddressPools:
- metallb
addressにはIPv4とIPv6両方を指定することができます。
ingress-nginx
続いてingressの設定を行います。
controller:
watchIngressWithoutClass: true
extraArgs:
enable-ssl-passthrough: true
service:
enableHttp: true
enableHttps: true
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
ports:
http: 80
https: 443
targetPorts:
http: http
https: https
type: LoadBalancer
serviceのIPFamilyPolicy,ipFamiliesにもmetallbのserviceと同様に設定します。
metallbのserviceに autoAssign: true
の設定が入っているため、ingress-nginxでIPアドレスを指定する必要はありません。
applyした結果は以下の通りになります。
kubernetes-admin@akatsuki kube | segre@akatsuki-1 | 05:03:52
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.97.168.10 192.168.20.231,240b:250:9800:4a20:100::1 80:31865/TCP,443:31116/TCP 48m
ingress-nginx-controller-admission ClusterIP 10.97.76.0 <none> 443/TCP 54m
EXTERNAL-IPにIPv4/v6アドレスが1つずつ付いているのが確認できます。
Webアプリ
最後にWebアプリをデプロイします。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
spec:
ingressClassName: "nginx"
rules:
- host: myapp.marokiki.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
hostは myapp.marokiki.net
にしています。
applyした結果は以下の通りになります。
NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE
webapp myapp nginx myapp.marokiki.net 192.168.20.231,240b:250:9800:4a20:100::1 80 3h4m
ADDRESSにIPv4/v6アドレスが1つずつ付いているのが確認できます。
レコード追加
DNSサーバーに先ほど作成したhost名のAAAAレコードを追加します。
http://myapp.marokiki.net で公開されていることが確認できます!
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
名前: myapp.marokiki.net
Address: 240b:250:9800:4a20:100::1
おわりに
お疲れ様でした!
メインのKubernetesクラスタではIPv6に対応するためにkubeadm resetを何度も行いました......
デフォルトで対応してくれることを願うばかりです。
今回の方法では毎度Serviceを手直しする必要がありますが、Dynamic Admission Control等を使うともう少しうまくできるかもしれません。
今後もKubernetesでIPv6をうまく使っていきたいと思います。