1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

kubeadm+flannelでkubernetesクラスタのIPv6環境構築

Last updated at Posted at 2023-11-20

はじめに

これは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からスワップの設定を削除します

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

init/akatsuki-1.yaml
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/162001:db8:43:0::/56 、serviceSubnetには 10.97.0.0/162001:db8:43:1::/112 を指定しています。
localAPIEndpointは1つのアドレスしか設定することはできないので、ここはIPv4アドレスを指定しています。
node-ipには各ノードのIPアドレスを指定します。IPv4/v6 DualStackの場合はここでv4とv6のアドレスを指定します。今回は 192.168.20.121240b: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 というファイルを作成します。

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.yamladvertiseAddress に指定したものを設定してください。
tokenとcaCertHashesは先ほど控えていただいた kubeadm join に記載されているものを設定してください。
node-ipにはWorkerのIPアドレスを指定して下さい。

以下のコマンドでクラスタに参加できます。

sudo kubeadm join --config=init/akatsuki-2.yaml

もう1台のWorkerにも同様のファイルを作成します。

init/akatsuki-3.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.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 の設定を行っている箇所があるため、そこを以下のように変更します。

init/kube-flannel.yaml
  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.yamlpodSubnet に指定したものを設定してください。
"EnableIPv6": true とすることでIPv6を有効にします(デフォルトではfalseです)。

最後にflannelをapplyします。

kubectl apply -f init/kube-flannel.yml

kubectl get nodes -o wide の結果、StatusがReadyになってるはずです。

kubectl get nodes -o wide
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アドレスがついていると思います。

kubectl describe nodes/akatsuki-1
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しかサポートしていないため、手動で変更する必要があります。

metallb/service.yaml
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

metallb-ip/ip_pool.yaml
---
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の設定を行います。

ingress-nginx/values.yaml
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した結果は以下の通りになります。

kubectl get service -n ingress-nginx
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アプリをデプロイします。

webapp/ingress.yaml
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した結果は以下の通りになります。

kubectl get ingress -A
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レコードを追加します。

image.png

http://myapp.marokiki.net で公開されていることが確認できます!

curl 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>
nslookup myapp.marokiki.net
名前:    myapp.marokiki.net
Address:  240b:250:9800:4a20:100::1

おわりに

お疲れ様でした!
メインのKubernetesクラスタではIPv6に対応するためにkubeadm resetを何度も行いました......
デフォルトで対応してくれることを願うばかりです。
今回の方法では毎度Serviceを手直しする必要がありますが、Dynamic Admission Control等を使うともう少しうまくできるかもしれません。
今後もKubernetesでIPv6をうまく使っていきたいと思います。

参考文献

  1. Raspberry Pi 4にKubernetesを入れる

  2. Dual-stack support with kubeadm

  3. flannel configuration

  4. Metallb Configuration

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?