2.8 kubeadm 搭建 #
kubeadm 和 minikube 类似,也是用容器和镜像来封装 Kubernetes 的各种组件,但它的目标不是单机部署,而是要能够轻松地在集群环境里部署 Kubernetes,并且让这个集群接近甚至达到生产级别质量。
kubeadm 具有了和 minikube 一样的易用性,只要很少的几条命令,如 init、join、upgrade、reset 就能够完成 Kubernetes 集群的管理维护工作,让它不仅适用于集群管理员,也适用于开发、测试人员。
2.8.1 准备工作 #
所谓的多节点集群,要求服务器应该有两台或者更多,其实最小可用的 Kubernetes 集群就只有两台主机,一台是 Master 节点,另一台是 Worker 节点。Master 节点需要运行 apiserver、etcd、scheduler、controller-manager 等组件,管理整个集群,Worker 节点只运行业务应用。
因为 Kubernetes 对系统有一些特殊要求,所以要先在 Master 和 Worker 节点上做一些准备,包括改主机名、改 Docker 配置、改网络设置、改交换分区这四步。
第一,由于 Kubernetes 使用主机名来区分集群里的节点,所以每个节点的 hostname 必须不能重名。需要修改 /etc/hostname 这个文件,把它改成容易辨识的名字,比如 Master 节点就叫 master,Worker 节点就叫 worker。
第二,虽然 Kubernetes 目前支持多种容器运行时,但 Docker 还是最方便最易用的一种,所以使用 Docker 作为 Kubernetes 的底层支持,这里可以参考 Docker 官网 安装 Docker Engine。安装完成后需要再对 Docker 的配置做一点修改,在 /etc/docker/daemon.json 里把 cgroup 的驱动程序改成 systemd ,然后重启 Docker 的守护进程,具体的操作如下:
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
在 Docker 中,cgroup 驱动程序是用来管理和限制容器资源的一种机制。默认情况下,Docker 使用 cgroupfs 作为 cgroup 驱动程序。然而,将 Docker 的 cgroup 驱动程序更改为 systemd 可以带来一些好处:
-
与系统一致性:将 Docker 的 cgroup 驱动程序设置为 systemd 可以使 Docker 与系统中的其他进程一致。这样,Docker 容器的资源管理将与其他系统服务使用的 cgroup 驱动程序一致,简化了资源管理和监控。
-
安全性和隔离性:systemd-cgroups 提供了更强大的资源隔离和安全性特性。使用 systemd 作为 cgroup 驱动程序可以更好地利用 systemd 提供的资源控制和隔离功能,进一步增强容器的安全性和资源限制能力。
-
性能改进:systemd-cgroups 在某些情况下可能会提供更好的性能。systemd-cgroups 使用一个单一的 cgroup 作为容器的父级 cgroup,而 cgroupfs 则在每个层级都使用单独的 cgroup。这种改变可能会减少 cgroup 操作的数量,从而提高性能。
需要注意的是,将 Docker 的 cgroup 驱动程序更改为 systemd 需要系统已经安装和配置了 systemd,并且与 Docker 版本兼容。
第三,为了让 Kubernetes 能够检查、转发网络流量,你需要修改 iptables 的配置,启用 br_netfilter 模块:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1 # better than modify /etc/sysctl.conf
EOF
sudo sysctl --system
br_netfilter 是 Linux 内核中的一个模块,用于在 Linux 桥接(bridge)网络中实现网络过滤功能。
Linux 桥接是一种网络技术,用于将多个网络接口连接在一起,形成一个逻辑上的网络。桥接器可以根据 MAC 地址来转发网络数据包,使得连接在不同网桥接口上的设备可以互相通信。
br_netfilter 模块提供了桥接网络中的网络过滤功能,主要用于实现网络层(IP)和传输层(TCP、UDP)的数据包过滤。具体来说,br_netfilter 模块使得可以在 Linux 桥接器上执行以下操作:
-
桥接器上的 IP 数据包过滤:通过在桥接器上启用 iptables 规则,可以对进出桥接器的 IP 数据包进行过滤、修改和重定向操作。这对于实施网络安全策略、防火墙规则和网络地址转换(NAT)等非常有用。
-
桥接器上的传输层数据包过滤:通过在桥接器上启用 ebtables 规则,可以对进出桥接器的传输层(如 TCP、UDP)数据包进行过滤、修改和重定向操作。这使得可以在桥接器级别上执行更细粒度的网络流量控制。
br_netfilter 模块扩展了 Linux 桥接网络的功能,使得可以在桥接器上实现更多的网络过滤和控制策略,提高网络的安全性和可配置性。
第四,你需要修改 /etc/fstab ,关闭 Linux 的 swap 分区,提升 Kubernetes 的性能:
sudo swapoff -a
sudo sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
完成之后,重启一下系统。
2.8.2 安装 kubeadm #
在 Master 节点和 Worker 节点上都要做有安装 kubeadm 这一操作。kubeadm 可以直接从 Google 自己的软件仓库下载安装,但国内的网络不稳定,很难下载成功,需要改用其他的软件源,可以选择国内的某云厂商:
sudo apt install -y apt-transport-https ca-certificates curl
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
sudo apt update
更新了软件仓库之后,可以用 apt install 获取 kubeadm、kubelet 和 kubectl 这三个安装必备工具。apt 默认会下载最新版本,也可以指定版本号,比如 1.23.3:
sudo apt install -y kubeadm=1.23.3-00 kubelet=1.23.3-00 kubectl=1.23.3-00
安装完成之后,可以用 kubeadm version、kubectl version 来验证版本是否正确:
kubeadm version
kubectl version --client
最后可以使用命令 apt-mark hold ,锁定这三个软件的版本,避免意外升级导致版本错误:
sudo apt-mark hold kubeadm kubelet kubectl
2.8.3 下载 Kubernetes 组件镜像 #
kubeadm 把 apiserver、etcd、scheduler 等组件都打包成了镜像,以容器的方式启动 Kubernetes,但这些镜像不是放在 Docker Hub 上,而是放在 Google 自己的镜像仓库网站 gcr.io,在国内的访问很困难。可以采取一些变通措施,提前把镜像下载到本地。使用命令 kubeadm config images list 可以查看安装 Kubernetes 所需的镜像列表,参数 --kubernetes-version
可以指定版本号:
kubeadm config images list --kubernetes-version v1.23.3
k8s.gcr.io/kube-apiserver:v1.23.3
k8s.gcr.io/kube-controller-manager:v1.23.3
k8s.gcr.io/kube-scheduler:v1.23.3
k8s.gcr.io/kube-proxy:v1.23.3
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6
可以从国内的镜像网站下载然后再用 docker tag 改名,shell 脚本如下:
repo=registry.aliyuncs.com/google_containers
for name in `kubeadm config images list --kubernetes-version v1.23.3`; do
src_name=${name#k8s.gcr.io/}
src_name=${src_name#coredns/}
docker pull $repo/$src_name
docker tag $repo/$src_name $name
docker rmi $repo/$src_name
done
2.8.4 安装 Master 节点 #
只需要一个命令 kubeadm init 就可以把组件在 Master 节点上运行起来,不过它还有很多参数用来调整集群的配置,可以使用 -h 查看。常见的有 3 个参数:
-
--pod-network-cidr
,设置集群里 Pod 的 IP 地址段。 -
--apiserver-advertise-address
,设置 apiserver 的 IP 地址,对于多网卡服务器来说很重要(比如 VirtualBox 虚拟机就用了两块网卡),可以指定 apiserver 在哪个网卡上对外提供服务。 -
--kubernetes-version
,指定 Kubernetes 的版本号。下面的这个安装命令里,指定了 Pod 的地址段是10.10.0.0/16
,apiserver 的服务地址是 192.168.10.210(主机的 IP 地址),Kubernetes 的版本号是 1.23.3。
sudo kubeadm init \
--pod-network-cidr=10.10.0.0/16 \
--apiserver-advertise-address=192.168.10.210 \
--kubernetes-version=v1.23.3
kubeadm 安装完成后,会提示出接下来要做的工作:
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
另外还有一个很重要的 kubeadm join 提示,其他节点要加入集群必须要用指令里的 token 和 ca 证书,所以这条命令务必拷贝后保存好:
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.10.210:6443 --token tv9mkx.tw7it9vphe158e74 \
--discovery-token-ca-cert-hash sha256:e8721b8630d5b562e23c010c70559a6d3084f629abad6a2920e87855f8fb96f3
安装 Flannel 网络插件 #
CNI(Container Networking Interface)标准是一个用于容器网络的开放标准和接口规范。它定义了容器运行时(如 Docker、Kubernetes 等)与网络插件之间的通信协议和数据格式,以实现容器网络的配置和管理。
CNI 标准的设计目标是提供一个统一的、可互操作的容器网络接口,使得不同的容器运行时可以使用各种网络插件,并与宿主机上的网络配置进行无缝集成。通过遵循 CNI 标准,容器运行时可以动态地创建、配置和删除容器网络,而不依赖于特定的容器运行时或网络插件实现。
Kubernetes 定义了 CNI 标准,有很多网络插件,可以使用常用的 Flannel。只需要使用如下的 kube-flannel.yml 文件(或者去仓库
https://github.com/flannel-io/flannel/)找相关文档)在 Kubernetes 里部署一下就好了。因为它应用了 Kubernetes 的网段地址,需要修改 net-conf.json 字段,把 Network 改成刚才 kubeadm 的参数 --pod-network-cidr
设置的地址段。比如在这里,就要修改成 10.10.0.0/16 :
apiVersion: v1
kind: Namespace
metadata:
labels:
k8s-app: flannel
pod-security.kubernetes.io/enforce: privileged
name: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: flannel
name: flannel
namespace: kube-flannel
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: flannel
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
- apiGroups:
- networking.k8s.io
resources:
- clustercidrs
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: flannel
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-flannel
---
apiVersion: v1
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.10.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
kind: ConfigMap
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
name: kube-flannel-cfg
namespace: kube-flannel
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
name: kube-flannel-ds
namespace: kube-flannel
spec:
selector:
matchLabels:
app: flannel
k8s-app: flannel
template:
metadata:
labels:
app: flannel
k8s-app: flannel
tier: node
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
containers:
- args:
- --ip-masq
- --kube-subnet-mgr
command:
- /opt/bin/flanneld
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: "5000"
image: docker.io/flannel/flannel:v0.22.0
name: kube-flannel
resources:
requests:
cpu: 100m
memory: 50Mi
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
privileged: false
volumeMounts:
- mountPath: /run/flannel
name: run
- mountPath: /etc/kube-flannel/
name: flannel-cfg
- mountPath: /run/xtables.lock
name: xtables-lock
hostNetwork: true
initContainers:
- args:
- -f
- /flannel
- /opt/cni/bin/flannel
command:
- cp
image: docker.io/flannel/flannel-cni-plugin:v1.1.2
name: install-cni-plugin
volumeMounts:
- mountPath: /opt/cni/bin
name: cni-plugin
- args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
command:
- cp
image: docker.io/flannel/flannel:v0.22.0
name: install-cni
volumeMounts:
- mountPath: /etc/cni/net.d
name: cni
- mountPath: /etc/kube-flannel/
name: flannel-cfg
priorityClassName: system-node-critical
serviceAccountName: flannel
tolerations:
- effect: NoSchedule
operator: Exists
volumes:
- hostPath:
path: /run/flannel
name: run
- hostPath:
path: /opt/cni/bin
name: cni-plugin
- hostPath:
path: /etc/cni/net.d
name: cni
- configMap:
name: kube-flannel-cfg
name: flannel-cfg
- hostPath:
path: /run/xtables.lock
type: FileOrCreate
name: xtables-lock
改好后,可以用 kubectl apply 来安装 Flannel 网络:
kubectl apply -f kube-flannel.yml
安装完成后,如果执行 kubectl get node 能够看到 Master 节点的状态是 Ready,则表明节点网络工作正常了。
2.8.5 安装 Worker 节点 #
如果已经成功安装了 Master 节点,那么 Worker 节点就执行执行之前拷贝的那条 kubeadm join 命令就可以了:
sudo kubeadm join 192.168.10.210:6443 --token tv9mkx.tw7it9vphe158e74 \
--discovery-token-ca-cert-hash sha256:e8721b8630d5b562e23c010c70559a6d3084f629abad6a2920e87855f8fb96f3
kubeadm join 命令里的 token 有时效性,默认是 24 小时,如果失效了可以用 kubeadm token create 创建一个新的 token。
这条命令会连接 Master 节点,然后拉取镜像,安装网络插件,最后把节点加入集群。在这个过程中可能也会遇到拉取镜像的问题,可以按照安装 Master 的方式解决。Worker 节点安装完毕后,执行 kubectl get node ,就会看到两个节点都是 Ready 状态:
在 Master 和 Worker 都安装成功后,可以使用 kubectl run ,运行 Nginx 测试一下:
kubectl run ngx --image=nginx:alpine
kubectl get pod -o wide
2.8.6 Console 节点 #
在生产环境中,在 Kubernetes 集群之外还需要有一台起辅助作用的服务器,也就是 Console 控制台,Console 服务器上只需要安装一个 kubectl,所有对 Kubernetes 集群的管理命令都是从这台主机发出去的,因为出于因为安全的原因,集群里的主机部署好之后应该尽量少直接登录上去操作。
由于 Console 节点的部署只需要安装一个 kubectl,然后复制 config 文件就行,可以直接在 Master 节点上用 scp 远程拷贝,例如:
scp $(which kubectl) root@192.168.10.208:~/
scp ~/.kube/config root@192.168.10.208:~/.kube