Kubernetes 基础概念与集群部署
Kubernetes(k8s)是一个容器编排系统,简单而言用来管一堆容器。k8s 的有效部署称为 k8s 集群。网上都用的 CentOS,考虑到它被弃坑,我用 Ubuntu 20 演示。
环境
-
四个 2C2G 阿里云 ECS,放在一个私网。
-
都是 Ubuntu 系统。
基础概念
K8S 集群的工作方式:X Master Node + Y Worker Node. 如果 Master 节点 down 了,可以通过选举或者其他策略选出新的 master.
集群可以分为两部分:Control Plane 和 Nodes. 二者之间通过 API server 交互。直接发生交互的是 api 和 kubelet(相当于 Node 内的组长)。kube-proxy 负责应用间所有的访问,类似计网中的路由器。不同 node 的 kube-proxy 之间会进行通信,串通消息。kube-controller-manager 是一个守护进程,用于调节系统状态。
Node 内部可以运行多个应用,调度信息通过 kubelet 与 master 交换。
而无论是 Master Node 还是 Worker Node,都是在容器中运行。
凌驾于整个集群之上的,则是 kubectl,用于启动整个集群。
部署和安装
题外话
实际工程中很可能不需要我们如此细致地部署,并且公司会提供镜像仓库乃至专线,不容易被 GFW 搞坏心态。另外如果推荐阅读阿里云的教程:自建K8S集群迁移ACK弹性裸金属集群。
总览
使用部署工具安装 Kubernetes | Kubernetes
-
我们需要在所有机器(Node)上安装 kubelet。至少一个节点安装 kubectl。可以用 kubeadm 简化部署.
-
我们挑选一个 node 作为 master,通过 kubeadm init 初始化主节点。之后 kubelet 会把 scheduler, kube-proxy, api-server, etcd, controller-manager 等应用装起来。
-
其它节点通过 kubeadm join 加入集群。kube-proxy 会被 kubelet 安装到从节点。
(其实无所谓打码了,今晚这几个实例就要被销毁。)可以利用 electerm 的批量执行看看能不能 ping 通。
我的四个节点:
1Host "k8s_master"
2 HostName 39.108.232.92
3 # 10.0.0.100
4
5Host "k8s_node2"
6 HostName 120.24.194.30
7 # 10.0.0.98
8
9Host "k8s_node3"
10 HostName 120.24.94.76
11 # 10.0.0.99
12
13Host "k8s_node4"
14 HostName 120.24.173.212
15 # 10.0.0.97
安装 docker
并非必须 docker,详见 容器运行时 | Kubernetes
Install Docker Engine on Ubuntu | Docker Documentation
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io -y
国内备选方案
安装所需的软件
1apt-get update 2apt-get install -y apt-transport-https gnupg-agent software-properties-common
添加阿里云安装源的密钥
1curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
添加阿里云安装源
1add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
安装最新版 docker engine
1apt update 2apt install docker-ce
预备安装 k8s
注意:
-
每台机器至少 2C2G
-
hostname, mac, product_uuid 都要唯一。
- 主机名可以通过
hostnamectl set-hostname NAME
设置
- 主机名可以通过
-
关掉 selinux 大坑。
-
生产环境禁用交换分区。
-
可以通过
free -m
查看。 -
可以通过
swapoff -a
关闭
-
-
一般内网机器直接关闭软件防火墙,但是用 k8s 一般不能关。
lsmod | grep br_netfilter
确保 br_netfilter 启用。手动启动:sudo modprobe br_netfilter
确保 iptables 可以检查桥接流量。
安装 kubeadm、kubelet、kubectl
下面照抄。以 安装 kubeadm | Kubernetes 为准。
-
更新
apt
包索引并安装使用 Kubernetesapt
仓库所需要的包:1apt-get update 2apt-get install -y apt-transport-https ca-certificates curl
-
下载 Google Cloud 公开签名秘钥:
1curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
-
添加 Kubernetes
apt
仓库:1"deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list
-
更新
apt
包索引,安装 kubelet、kubeadm 和 kubectl,并锁定其版本:1sudo apt-get update 2sudo apt-get install -y kubelet kubeadm kubectl 3sudo apt-mark hold kubelet kubeadm kubectl
国内备选方案
Debian 系
添加阿里云安装源
1curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
2cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
3deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
4EOF
添加安装源密钥
1gpg --keyserver keyserver.ubuntu.com --recv-keys BA07F4FB
2gpg --export --armor BA07F4FB | sudo apt-key add -
安装三驾马车
1apt update && \
2apt install -y kubelet kubeadm kubectl && \
3apt-mark hold kubelet kubeadm kubectl
Redhat 系
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg exclude=kubelet kubeadm kubectl EOF sudo yum install -y kubelet-1.20.9 kubeadm-1.20.9 kubectl-1.20.9 --disableexcludes=kubernetes sudo systemctl enable --now kubelet
对所有节点,切换到 systemd 控制
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
修改kubelet:
1mkdir /var/lib/kubelet
2cat > /var/lib/kubelet/config.yaml <<EOF
3apiVersion: kubelet.config.k8s.io/v1beta1
4kind: KubeletConfiguration
5cgroupDriver: systemd
6EOF
重启docker 与 kubelet:
1systemctl daemon-reload
2systemctl restart docker
3systemctl restart kubelet
初始化 Master
kubeadm config print init-defaults > kubeadm-init.yaml
这里面的数据根据情况配置
注意:实际上我用了配置文件反而怎么都不成功。可以试试直接 kubeadm init
失败重置用 kubeadm reset
[reset] FYI: You can look at this config file with ‘kubectl -n kube-system get cm kubeadm-config -o yaml’
1apiVersion: kubeadm.k8s.io/v1beta3
2bootstrapTokens:
3- groups:
4 - system:bootstrappers:kubeadm:default-node-token
5 token: abcdef.0123456789abcdef
6 ttl: 24h0m0s
7 usages:
8 - signing
9 - authentication
10kind: InitConfiguration
11localAPIEndpoint:
12 advertiseAddress: 39.108.232.92
13 bindPort: 6443
14nodeRegistration:
15 criSocket: /var/run/dockershim.sock
16 imagePullPolicy: IfNotPresent
17 name: node # HERE!
18 taints: null
19---
20apiServer:
21 timeoutForControlPlane: 4m0s
22apiVersion: kubeadm.k8s.io/v1beta3
23certificatesDir: /etc/kubernetes/pki
24clusterName: kubernetes
25controllerManager: {}
26dns: {}
27etcd:
28 local:
29 dataDir: /var/lib/etcd
30imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
31kind: ClusterConfiguration
32kubernetesVersion: 1.23.0
33networking:
34 dnsDomain: cluster.local
35 serviceSubnet: 10.0.0.0/12 # HERE!
36scheduler: {}
GFW:
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
完事儿拉取镜像:
kubeadm config images pull --config kubeadm-init.yaml
初始化:
kubeadm init --config kubeadm-init.yaml
重申,实际上我用了配置文件反而怎么都不成功。可以试试直接 kubeadm init
另外,如果总是超时,有可能是 GFW 在作怪。参阅 云服务器如何突破 GFW?SSH 隧道介绍 - Less Bug (less-bug.com),或者 --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers
成功后得到如下 join 命令(随机):
kubeadm join 10.0.0.100:6443 --token hr9m7r.i347n5rk1u144gws \
--discovery-token-ca-cert-hash sha256:d26798c50f3560adfc21516c9337b7e6b0ebbbeced1a4a720f468ed66155ab8c
Your Kubernetes control-plane has initialized successfully! 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 Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.conf # 注意,这里需要一个网络插件 You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 10.0.0.100:6443 --token hr9m7r.i347n5rk1u144gws \ --discovery-token-ca-cert-hash sha256:d26798c50f3560adfc21516c9337b7e6b0ebbbeced1a4a720f468ed66155ab8c
如果 token 忘了,执行
kubeadm token create --print-join-command
重建
Work Node 加入
mkdir /var/lib/kubelet/
cat > /var/lib/kubelet/config.yaml <<EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF
查看集群状态
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodes
NAME STATUS ROLES AGE VERSION
dev-learn001 NotReady control-plane,master 13m v1.23.1
dev-learn002 NotReady <none> 4m47s v1.23.1
dev-learn003 NotReady <none> 3m44s v1.23.1
dev-learn004 NotReady <none> 3m42s v1.23.1
NotReady 是因为网络未就绪,节点间无法互通。可以配置 Calico 网络。
安装网络插件
curl https://docs.projectcalico.org/manifests/calico.yaml -O
kubectl apply -f calico.yaml
网络配置好后就会变成 Ready
kubectl get nodes
NAME STATUS ROLES AGE VERSION
dev-learn001 Ready control-plane,master 24m v1.23.1
dev-learn002 Ready <none> 16m v1.23.1
dev-learn003 Ready <none> 15m v1.23.1
dev-learn004 Ready <none> 15m v1.23.1
k8s 常用命令
#查看集群所有节点
kubectl get nodes
#根据配置文件,给集群创建资源
kubectl apply -f xxxx.yaml
#查看集群部署了哪些应用?
docker ps === kubectl get pods -A
# 运行中的应用在docker里面叫容器,在k8s里面叫Pod
kubectl get pods -A
部署 Dashboard
https://github.com/kubernetes/dashboard
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml
kubectl apply -f recommended.yaml
执行kubectl get pods --all-namespaces
查看 pods 状态。
后续还要创建用户,配置 cert。参阅 https://github.com/kubernetes/dashboard/tree/master/docs
Troubleshott
官方排查例子
Permissions for ‘private-key’ are too open
Windows SSH: Permissions for ‘private-key’ are too open
hostname “node” could not be reached
[WARNING Hostname]: hostname "node" could not be reached
[WARNING Hostname]: hostname "node": lookup node on 127.0.0.53:53: server misbehaving
注意配置 kubeadm-init.yaml 中的主节点名。
the number of available CPUs…
[ERROR NumCPU]: the number of available CPUs 1 is less than the required 2
CPU 至少双核。当然你也可以通过
--ignore-preflight-errors=NumCPU
参数来跳过,毕竟咱测试环境。
如果出错,记得 kubeadm reset
再重试
It seems like the kubelet isn’t running or healthy.、
journalctl -xeu kubelet
看日志。比如我遇到的是 failed to run Kubelet: misconfiguration: kubelet cgroup driver: “cgroupfs” is different from docker cgroup driver: “systemd”
我的解决:
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
修改kubelet:
1mkdir /var/lib/kubelet
2cat > /var/lib/kubelet/config.yaml <<EOF
3apiVersion: kubelet.config.k8s.io/v1beta1
4kind: KubeletConfiguration
5cgroupDriver: systemd
6EOF
重启docker 与 kubelet:
1systemctl daemon-reload
2systemctl restart docker
3systemctl restart kubelet
之后依然会产生错误日志,但 service kubelet status
显示 Running 就行。
Unable to update cni config: No networks found in /etc/cni/net.d
配置网络。上面讲了。
总结
再往后……
接下来需要的是实战。把服务部署到云。可以关注后续文章。
真的需要 k8s?
容器化带来灵活性,但也可能造成性能损失。据说 Hadoop 部署到 k8s 会导致 30% 性能损失。主要原因是容器虚拟网络的开销,可以通过 Host-Only 解决。
其它想法
这次部署花掉了我整整一个下午的时间。而且我发现里面还有大量自己不清楚的内容。所以,很多事情还是得交给专业的运维工程师。运维也并不简单。
参考
Docker与k8s的恩怨情仇(一)—成为PaaS前浪的Cloud Foundry - 葡萄城技术团队 - 博客园 (cnblogs.com)
https://www.yuque.com/leifengyang/oncloud/ghnb83#AGHOX
自建K8S集群迁移ACK弹性裸金属集群-最佳实践-阿里云 (aliyun.com)
序言 · Kubernetes Handbook - Kubernetes 中文指南/云原生应用架构实践手册 · Jimmy Song