使用kubeadm和flannel来构建Kubernetes集群的IPv6环境

起初

这是KMC2023年圣诞倒数日特刊的第一篇文章。
明天将是taisei先生的“种西瓜游戏就可自给自足”的专栏。

本文将构建一个IPv4/IPv6 DualStack环境的Kubernetes集群。
已经在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

这是一个由1台Master和2台Worker组成的系统。
操作系统是Ubuntu 22.04,容器运行时是cri-o,CNI使用的是flannel。
我们假设已完成Ubuntu的安装和网络设置,并可进行名称解析。

预先准备

目标机器:所有机器

执行安装各种包以构建Kubernetes集群的操作1。
一系列的过程被转化为Ansible角色并发布在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转发

目标机器:所有机器

从这里开始是针对IPv6的设置。

开启 IPv6 数据包传输

sudo sysctl -w net.ipv6.conf.all.forwarding=1

创建集群

目标设备:主控机

使用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只能设置一个地址,所以这里指定了IPv4地址。
node-ip指定了每个节点的IP地址。对于IPv4/v6双栈的情况,这里需要指定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

参加集群

两台工作机器

接下来,将对Worker节点执行加入集群的处理操作。

工作者将在一个内核中创建一个名为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

我会给另外一个工人创建同样的文件。

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

法兰绒的设置

目标机器:一台主机

我作为CNI执行flannel的设置步骤3。
在Master节点上获取flannel的配置。

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"
      }
    }

请在init/akatsuki-1.yaml文件中为Network和IPv6Network设置podSubnet。通过设置”EnableIPv6″: true来启用IPv6(默认为false)。

最后,使用法兰绒面料。

kubectl apply -f init/kube-flannel.yml

在执行 kubectl get nodes -o wide 命令后,状态应该显示为 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

在”akatsuki-1″的节点上,我认为它有一个IPv6地址,然而”INTERNAL-IP”只有一个IPv4地址。

Addresses:
  InternalIP:  192.168.20.121
  InternalIP:  240b:250:9800:4a20::121
  Hostname:    akatsuki-1

以上,IPv4/IPv6 DualStack的Kubernetes环境已经建立完成。

创建IPv6服务

下面将介绍如何使用IPv6地址创建网页。

这次我们将使用Helm和flux。
在网页部署中,我们将使用metallb和ingress-nginx。
关键部分是对metallb和ingress-nginx的Service进行设置,所以请根据您自己的环境进行部署。

关于文件结构的详细信息略过不提。
如果想要搭建相同的环境,请Clone https://github.com/segre5458/k8s-ipv6 。

金属负载平衡(MetalLB)

通过metallb进行负载均衡。
由于基本上几乎所有的服务都默认只支持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双栈| Kubernetes。

    • ipFamilyPolicy

SingleStack:IPv4 or IPv6のどちらか
PreferDualStack:DualStack or SingleStackのどちらか
RequireDualStack:DualStack(それ以外は失敗する)

ipFamilies

IPv4/IPv6のどちらか、もしくは両方の指定が可能
IPv4/IPv6のどれが適用されるかは、クラスタの構成とipFamilyPolicyで決定される

IP广告的设置将如下所示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

地址可以同时指定IPv4和IPv6。

入口-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

您无需在ingress-nginx中指定IP地址,因为您可以将service的IPFamilyPolicy和ipFamilies设置为与metallb的service相同。由于metallb的service中有autoAssign: true的设置,因此不需要在ingress-nginx中指定IP地址。

申请结果如下所示。

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 地址。

网络应用程序

最后,将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

主机设置为myapp.marokiki.net。

以下是申請的結果。

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

可以确认某个地址有一个IPv4和一个IPv6地址。

添加唱片

请在DNS服务器中添加刚刚创建的主机名的AAAA记录。

image.png

可以确认通过 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,但使用动态准入控制等技术可能可以更好地解决问题。
我希望将来能在 Kubernetes 中更好地利用 IPv6。

请提供相关文献
资料来源
引用文献

将Kubernetes安装在Raspberry Pi 4上 ↩
具备kubeadm的双栈支持 ↩
配置flannel ↩
配置Metallb ↩
广告
将在 10 秒后关闭
bannerAds