使用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记录。
可以确认通过 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。
请提供相关文献
资料来源
引用文献
具备kubeadm的双栈支持 ↩
配置flannel ↩
配置Metallb ↩