集群间的同一网段部署很简单,网上教程很多。但是有的时候想学习或者测试之类的种种原因手里没有足够的局域网机器的话,想利用多台异地云服务器搭建k8s集群的话才建议参考本文。
机器准备
- 主节点centos7.8(阿里云ECS)
机器名: k8s-master
公网ip:47.9x.17x.9x - 从节点centos7.9(腾讯云EVM)
机器名: k8s-node1
公网ip:121.4x.9x.7x
设置hostname
-
master节点设置:
hostnamectl set-hostname k8s-master
-
node1节点设置:
hostnamectl set-hostname k8s-node1
修改/etc/hosts文件
将127.0.0.1 和hostname绑定(所有节点都需要)
配置节点间ssh互信
配置ssh互信,那么节点之间就能无密访问,方便日后执行自动化部署
ssh-keygen # 每台机器执行这个命令, 一路回车即可
ssh-keygen -t rsa -C "[email protected]"
代码参数含义:
-t 指定密钥类型,默认是 rsa ,可以省略。
-C 设置注释文字,比如邮箱。
-f 指定密钥文件存储文件名。
ssh-copy-id node # 到master上拷贝公钥到其他节点,这里需要输入 yes和密码
默认的文件名为.ssh/id_rsa.pub
ssh-copy-id -i .ssh/id_rsa.pub 用户名字@192.168.x.xxx
系统参数配置
bash:
# 关闭Selinux
sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
# 永久关闭swap区
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab
echo "vm.swappiness = 0">> /etc/sysctl.conf
# 修改内核参数
cat < /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
EOF
sysctl -p /etc/sysctl.d/k8s.conf
# 完整/etc/sysctl.conf 配置
vm.swappiness = 0
kernel.sysrq = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.neigh.default.gc_stale_time = 120
# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
# 生效/etc/sysctl.conf文件设置
sysctl -p
建立虚拟网卡(所有节点)
将公网ip当做虚拟网卡来对待,以便后续的k8s集群的pod ip采用公网ip来加入集群
# step1 ,注意替换你的公网IP进去
cat > /etc/sysconfig/network-scripts/ifcfg-eth0:1 <
主机端口设置
默认阿里云ECS所有入网方向端口都需要自己手动开启,腾讯云EVM入网方向所有端口都放开了。
-
master端口开放
协议 方向 端口 说明 TCP 入站 6443 Kubernetes API server TCP 入站 2379-2380 etcd server client API TCP 入站 10250 Kubelet API TCP 入站 10251 kube-scheduler TCP 入站 10252 kube-controller-manager UDP 入站 8472 k8s fannel vxlan 默认master是不允许进行非系统的pod部署的,后续k8smaster节点部署起来之后如果执行将master允许pod 部署的话(也就是执行了下面的命令):
kubectl taint node k8s-master node-role.kubernetes.io/master-
那么master节点也需要开放30000-32767的入站方向的tcp端口
-
node节点端口开放
协议 方向 端口 说明 TCP 入站 10250 Kubelet API TCP 入站 30000-32767 k8s NodePort ServicesI
Docker安装或者升级(master节点和所有node节点)
首先查询关于docker的软件包
rpm -qa | grep docker
结果如下:
查到后开始卸载软件
yum remove docker-ce*
成功后查看docker版本看看还在不在
docker version
然后执行脚本开始升级到最新版本(截止发文的时候docker的版本是 20.10.0)
curl -fsSL https://get.docker.com/ | sh
查看版本会发现已经升级到最新版
启动docker
systemctl start docker.service
将docker加入开启自启服务中
systemctl enable docker
最后查看下docker信息
docker info
CentOS7.x系统自带的3.10.x内核存在一些Bug,Docker运行不稳定,建议升级内核
#下载内核源
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
# 安装最新版本内核
yum --enablerepo=elrepo-kernel install -y kernel-lt
# 查看可用内核
cat /boot/grub2/grub.cfg |grep menuentry
# 设置开机从新内核启动
grub2-set-default "CentOS Linux (4.4.230-1.el7.elrepo.x86_64) 7 (Core)"
# 查看内核启动项
grub2-editenv list
# 重启系统使内核生效
reboot
# 查看内核版本是否生效
uname -r
查看是什么系统
lsb_release -a
docker安装成功后
# 设置守护程序
cat > /etc/docker/daemon.json <
registry-mirrors中的镜像地址选择阿里云的,也可以选择别家的
登录进入阿里云镜像服务中心,获取镜像地址
mkdir -p /etc/systemd/system/docker.service.d
重启Docker服务
systemctl daemon-reload
systemctl enable docker
systemctl restart docker
Maste节点部署
当上述步骤完成后,我们依照以下步骤来完成主节点的安装:
-
Kubeadm以及相关工具包的安装(截止发文的时候k8s的版本是v1.20.0)
安装脚本如下所示:
# 配置源
echo '#k8s
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
'>/etc/yum.repos.d/kubernetes.repo
# kubeadm和相关工具包yum -y install kubelet kubeadm kubectl kubernetes-cni
安装成功后的k8s版本为:
重启kubelet
systemctl daemon-reload systemctl enable kubelet
-
批量拉取k8s相关镜像
制作下载镜像的脚本 可按照如下的设置
vim docker.sh#!/bin/bash url=registry.aliyuncs.com/google_containers version=v1.20.0 images=(`kubeadm config images list --kubernetes-version=$version|awk -F '/' '{print $2}'`) for imagename in ${images[@]} ; do docker pull $url/$imagename docker tag $url/$imagename k8s.gcr.io/$imagename docker rmi -f $url/$imagename done
执行docker脚本
./docker.sh
所有组件pull成功后。查看
docker images
结果如下所示:
初始化主节点
默认的kubeadm-config.yml文件可以使用以下命令获取:
kubeadm config print init-defaults > kubeadm-config.yml
# step1 添加配置文件,注意替换下面的IP
cat > kubeadm-config.yml <
# 初始化过程中可能会报错:
[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
解决: echo "1" >/proc/sys/net/bridge/bridge-nf-call-iptables
如果没有上述文件则使用 modprobe br_netfilter 命令
[ERROR Swap]: running with swap on is not supported. Please disable swap [preflight] If you know what you are doing, you can make a check non-fatal with --ignore-preflight-errors=...
解决:
swapoff -a
vim /etc/fstab
# 将下面的这行注释掉
#/dev/mapper/rhel-swap swap swap defaults 0 0
# 初始化如果失败的话,想再重新初始化的话执行下面操作重置:
kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/
rm -rf /var/lib/etcd
# 如果集群开启ipvs的话,也需要将ipvs重置,以确保不受之前的错误配置影响
ipvsadm -C
ps:集群token管理
默认token的有效期为24小时,当过期之后,该token就不可用了,如果后续有nodes节点加入,解决方法如下:重新生成新的token ==> kubeadm token create
# 1.查看当前的token列表
[root@K8S00 ~]# kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
7mjtn4.9kds6sabcouxaugd 23h xxxxx authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token
# 2.重新生成新的token
[root@K8S00 ~]# kubeadm token create
369tcl.oe4punpoj9gaijh7
# 3.再次查看当前的token列表
[root@K8S00 ~]# kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
369tcl.oe4punpoj9gaijh7 23h xxxxx authentication,signing system:bootstrappers:kubeadm:default-node-token
7mjtn4.9kds6sabcouxaugd 23h xxxxx authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token
# 4.获取ca证书sha256编码hash值
[root@K8S00 ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
7ae10591aa593c2c36fb965d58964a84561e9ccd416ffe7432550a0d0b7e4f90
# 5.节点加入集群
[root@k8s-node03 ~]# kubeadm join --token 369tcl.oe4punpoj9gaijh7(新的token) --discovery-token-ca-cert-hash sha256:7ae10591aa593c2c36fb965d58964a84561e9ccd416ffe7432550a0d0b7e4f90(ca证书sha256编码hash值) 47.9x.17x.9x:6443 --ski
至此,master节点部署完毕,现在来查看下pod运行情况
kubectl get pod -n kube-system
# 查看一下集群pod,确认个组件都处于Running 状态
# 注意由于此时k8s的cni组件还没部署,此时的dns日志里面应该会报错,等待cni组件中,所以coredns 暂时还无法正常启动。
使用 kubectl get pod -n kube-system -o wide 命令查看的话可以看到系统pod节点加入的ip地址均为公网ip
# 出于安全考虑,默认配置下Kubernetes不会将Pod调度到Master节点。如果希望将k8s-master也当作Node使用,可以执行如下命令:
kubectl taint node k8s-master node-role.kubernetes.io/master-
其中k8s-master是主机节点hostname如果要恢复Master Only状态,执行如下命令:
kubectl taint node k8s-master node-role.kubernetes.io/master="":NoSchedule
kube-proxy代理方案由iptables改为ipvs
这里的kube-proxy组件默认是使用iptables方案进行启动的,我们来将它改成由ipvs方式启动
1.加载内核模快
cat >> /etc/sysctl.conf << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl -p
lsmod|grep ip_vs
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
yum install ipvsadm ipset -y
2.修改kube-proxy配置
kubectl edit configmap kube-proxy -n kube-system
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs" # 修改此处
nodePortAddresses: null
3.删除所有kube-proxy的pod
kubectl delete pod xxx -n kube-system
4.校验
kubectl logs kube-proxy-xxx -n kube-system 日志出现Using ipvs Proxier即可
5.检查ipvs代理规则
kubectl get svc --all-namespaces
# ipvsadm -ln
#可以看到service对应的很多规则
给集群部署flannel 网络组件
fannel简单易用,无特殊需求采用这种方案,它目前默认采用vxlan模式工作,xvlan采用udp 8472端口和各个节点通信
Flnnel的VXLAN模式有两种:
VXLAN: 原生的VXLAN,即扩展的虚拟LAN
Directrouting:直接路由型
下载fannel文件
wget https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml
# 共修改两个地方,一个是args下,添加
args:
- --public-ip=$(PUBLIC_IP) # 添加此参数,申明公网IP
- --iface=eth0 # 添加此参数,绑定网卡
# 然后是env下
env:
- name: PUBLIC_IP #添加环境变量
valueFrom:
fieldRef:
fieldPath: status.podIP
# 此处将vxlan工作模式改为Directrouting+vxlan结合的方式,它具有如果两个节点在同一网段时使用host-gw通信,如果不在同一网段中,即 当前pod所在节点与目标pod所在节点中间有路由器,就使用VXLAN这种方式,使用叠加网络的优点。
# 需要将configmap中的net-conf.json做调整:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true #添加该属性
}
}
# 最终完整的文件为:
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: '/etc/cni/net.d'
- pathPrefix: '/etc/kube-flannel'
- pathPrefix: '/run/flannel'
readOnlyRootFilesystem: false
# Users and groups
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
# Privilege Escalation
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
# Capabilities
allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
defaultAddCapabilities: []
requiredDropCapabilities: []
# Host namespaces
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
# SELinux
seLinux:
# SELinux is unused in CaaSP
rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
rules:
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
- ''
resources:
- pods
verbs:
- get
- apiGroups:
- ''
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ''
resources:
- nodes/status
verbs:
- patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
priorityClassName: system-node-critical
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni
image: quay.io/coreos/flannel:v0.13.1-rc1
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.13.1-rc1
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
- --public-ip=$(PUBLIC_IP) # 添加此参数,申明公网IP
- --iface=eth0 # 添加此参数,绑定网卡
resources:
requests:
cpu: '100m'
memory: '50Mi'
limits:
cpu: '100m'
memory: '50Mi'
securityContext:
privileged: false
capabilities:
add: ['NET_ADMIN', 'NET_RAW']
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: PUBLIC_IP #添加环境变量
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
执行fannel文件:
kubectl apply -f kube-fannel.yml
执行完后可以在查看k8s集群的pod节点:
kubectl get pod -n kube-system -o wide
可以发现此时fannel和dns都启动成功。
# 刷新coredns报错 Failed to list *v1.Namespace: Get "https://10.1.0.1:443/api/v1/namespaces?limit=500&resourceVersion=0": dial tcp 10.1.0.1:443: connect: no route to host
很有可能是防火墙(iptables)规则错乱或者缓存导致的,可以依次执行以下命令进行解决:
systemctl stop kubelet
systemctl stop docker
iptables --flush
iptables -tnat --flush
systemctl start kubelet
systemctl start docker
至此master节点pod全部部署完毕。
检查集群状态(选做)
kubectl get cs
会发现此时其实 scheduler 和controller manager 健康检查是失败的
- 先检查这两个端口是否启动了
telnet或者ps都可以查看 - 确认kube-scheduler和kube-controller-manager组件配置是否禁用了非安全端口
配置文件路径:
/etc/kubernetes/manifests/kube-scheduler.conf
/etc/kubernetes/manifests/kube-controller-manager.yaml
如controller-manager组件的配置如下:可以去掉--port=0这个设置,
然后重启 sudo systemctl restart kubelet
重新查看 kubectl get cs 应该就解决问题了。
配置k8s集群 命令补全
(仅master)
yum install -y bash-completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
source ~/.bashrc
工作节点部署
安装k8s步骤和主节点一样,直到初始化master节点这里时,在ndoe节点不需要初始化,而是执行join命令加入master节点
执行join命令前需要做调整。
修改kubelet启动参数(重点,所有node节点都要操作)
# 此文件安装kubeadm后就存在了
vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
# 注意,这步很重要,如果不做,节点仍然会使用内网IP注册进集群
# 在末尾添加参数 --node-ip=node节点的公网IP
# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS --node-ip=121.4x.9x.7x
执行刚才master提示的join命令
kubeadm join 47.9x.17x.9x:6443 --token abcdef.0123456789abcdef
--discovery-token-ca-cert-hash sha256:dd008a0ab88d25d7addb121ab11024d6e7c8e44639d2e48e662eb6319cd40b04
待执行完毕再查看集群。
重置k8s集群
在搭建集群时,可能会遇到节点配置失败的情况,此时可以将该节点移出集群,在将节点移出集群时,应该将该节点上正在运行的Pod进行驱离。
例如:
驱离名为"k8s-node-1"的节点上的pod(master上操作)
[root@k8s-master ~]# kubectl drain k8s-node-1 --delete-local-data --force --ignore-daemonsets
删除节点(master上)
[root@k8s-master ~]# kubectl delete node k8s-node-1
重置节点(node上-也就是在被删除的节点上)
[root@k8s-node-1 ~]# kubeadm reset
需要注意:
注1:需要把master也驱离、删除、重置,这里给我坑死了,第一次没有驱离和删除master,最后的结果是查看结果一切正常,但coredns死活不能用,切勿尝试
到此,整个k8s集群部署完毕,接下来需要对集群进行验证,看各个节点是否可用,部署svc服务或者pod时网段是否能互相访问,考虑到本文篇幅已经太长了,后续再进行验证。