KubernetesをDual Stackで動かす

KubernetesのIPv4, IPv6共存(いわゆるDual Stack)は、1.16でalphaバージョン(デフォルト無効)としてリリースされており、 1.18でbeta(デフォルト有効)となる見込みです。

また、CNIのCalicoが3.11からDual Stackに対応しています。

そこで、今回はこの構成でDual Stackを構築してみます。

手順、設定の詳細は、巻末の参照ドキュメントも参照ください。

構成

  • masterノード1台、workerノード2台
  • IPv4, IPv6のIPアドレスが各ノードにアサインされている
  • IPv4, IPv6で名前解決ができる

SWバージョン

  • CentOS 7.7.1908
  • Docker 19.03.5
  • Kubernetes 1.17.2
  • Calico 3.11

Docker 設定

Dockerをインストールし、設定していきます。

/etc/docker/daemon.json を修正する際、IPv6に対応する設定を入れておく必要があります。

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "ipv6": true,
  "fixed-cidr-v6": "fd00:1::/64"
}

fixed-cidr-v6 はkubernetes上では使用されないので、書かなくても大丈夫かもしれません。(ちゃんと確認してない、おい)

Kubernetes デプロイ

kubeadmでデプロイしていきます。kubeadmはこちらに沿ってインストールしておきます。 systemctl enable --now kubelet を実行しているところがありますが、この時点では kubelet はまだ起動できないので、 systemctl enable kubelet がよいです。

kubeadm init の引数に、--config <config file> を指定し、クラスターをデプロイします。 ネットワークが不安定な環境の場合、 kubeadm init の実行前に kubeadm config images pull を実行しておくと、予めデプロイに必要なイメージをpullできます。 kubeadm init の実行後は、 network plugin is not ready: cni config uninitialized でnodeは NotReady となるので、次のCalicoのインストールを継続してきます。

以下がコンフィグファイルのサンプルです。ポイントは、

  • ClusterConfigurationfeatureGatesIPv6DualStack を有効化する
  • ClusterConfigurationnetworking.podSubnet でIPv4、IPv6双方のサブネットを記載する
  • ClusterConfigurationnetworking.serviceSubnet でIPv4、IPv6双方のサブネットを記載する
  • KubeProxyConfigurationipvs モードで起動する (kube-proxyをipvsモードで動かすための準備はこちら)
apiVersion: kubeadm.k8s.io/v1beta2
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: <master node ipv4 address>
  bindPort: 6443
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  name: <master node hostname>
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns:
  type: CoreDNS
etcd:
  local:
    dataDir: /var/lib/etcd
featureGates:
  IPv6DualStack: true
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.17.2
networking:
  dnsDomain: cluster.local
  podSubnet: "10.244.0.0/16,fd00:2::/48"
  serviceSubnet: "10.96.0.0/16,fd00:3::/112"
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

こちらに書かれている設定の仕方とわりと違うような気がしたのがもやっとポイントでした。 ↑のコンフィグはこちらから拝借しています。

Calico インストール

Calicoのマニュフェストをダウンロードし、IPv6に対応するよう修正し、クラスターに適用します

  • ConfigMapの calico-configipam で、IPv4, IPv6を有効化する
          "ipam": {
              "type": "calico-ipam",
              "assign_ipv4": "true",
              "assign_ipv6": "true"
          },
  • DaemonSetの calico-node のコンテナの環境変数で以下のように設定する
            # Auto-detect the BGP IP address.
            - name: IP
              value: "autodetect"
            - name: IP6
              value: "autodetect"

            # The default IPv4 pool to create on startup if none exists. Pod IPs will be
            # chosen from this range. Changing this value after installation will have
            # no effect. This should fall within `--cluster-cidr`.
            - name: CALICO_IPV4POOL_CIDR
              value: "10.244.0.0/16"
            - name: CALICO_IPV6POOL_CIDR
              value: "fd00:2::/48"

            # Enable IPv6 on Kubernetes.
            - name: FELIX_IPV6SUPPORT
              value: "true"

動作確認

Validate IPv4/IPv6 dual-stack に沿って確認をしていくと、 .status.addresses だけ、IPv4のアドレスしか付きませんでした。

最終的に、起動したクラスターで、nginxのアプリケーションを起動した様子です。 (officialのnginxのイメージはIPv6をListenしていなかったので、Listenするイメージを準備しました。。)

[[email protected] ~]# kubectl get all
NAME                           READY   STATUS    RESTARTS   AGE
pod/my-nginx-94c87cdcd-88hzq   1/1     Running   0          60m
pod/my-nginx-94c87cdcd-c6np8   1/1     Running   0          60m
pod/my-nginx-94c87cdcd-xlqfd   1/1     Running   0          60m

NAME                              TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
service/kubernetes                ClusterIP      10.96.0.1      <none>            443/TCP        149m
service/my-nginx-svc-ipv4         ClusterIP      10.96.48.15    192.168.200.145   80/TCP         60m
service/my-nginx-svc-ipv6         ClusterIP      fd00:3::5068   2000:db1::120     80/TCP         60m
service/my-nginx-svc-ipv6-local   LoadBalancer   fd00:3::1ff3   2000:db1::121     80:32405/TCP   58m

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   3/3     3            3           60m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/my-nginx-94c87cdcd   3         3         3       60m

service/my-nginx-svc-ipv4, service/my-nginx-svc-ipv6ExternalIP でIPを割り当てています。 service/my-nginx-svc-ipv6-localMetalLB を使っています。 MetalLBで .spec.isFamily によって IPv4 か IPv6 かを割り当ててほしかったのですが、それはできなそうでした。

ノード上で ipvsadm -ln を実行すると、ipvsモードでのルーティングの状況が確認できます。(ipvsadmコマンドはパッケージマネージャでインストールしておいてください)

デフォルトでは、外部からのアクセスはNATされますが、 Service を作成する際、 externalTrafficPolicy: Local を指定したりなどすると、 外部のIPを維持できます。

その他

参照

このblogの内容は個人の意見に基づくものであり、 所属組織団体の公式見解とは異なる場合があります点、ご了承ください。