配置要求
对于 Kubernetes 初学者,在搭建K8S集群时,推荐在阿里云或腾讯云采购如下配置:(您也可以使用自己的虚拟机、私有云等您最容易获得的 Linux 环境)
- 至少2台 2核4G 的服务器
- Cent OS 7.6 / 7.7 / 7.8
安装后软件版本
Kubernetes v1.19.x
calico 3.13.1
nginx-ingress 1.5.5
- Docker 19.03.14
关于二进制安装
kubeadm 是 Kubernetes 官方支持的安装方式,“二进制” 不是。本文档采用 kubernetes.io 官方推荐的 kubeadm 工具安装 kubernetes 集群。
检查CentOS / hostname
1 | # 在 master 节点和 worker 节点都要执行 |
操作系统兼容性
CentOS版本 | 本文档是否兼容 | 备注 |
---|---|---|
7.8 | 是 | 已验证 |
7.7 | 是 | 已验证 |
7.6 | 是 | 已验证 |
7.5 | 否 | 已证实会出现 kubelet 无法启动的问题 |
7.4 | 否 | 已证实会出现 kubelet 无法启动的问题 |
7.3 | 否 | 已证实会出现 kubelet 无法启动的问题 |
7.2 | 否 | 已证实会出现 kubelet 无法启动的问题 |
修改 hostname
如果您需要修改 hostname,可执行如下指令:
1 | 修改 hostname |
检查网络
在所有节点执行命令
1 | [root@demo-master-a-1 ~]$ ip route show |
安装docker及kubelet
使用 root 身份在所有节点执行如下代码,以安装软件:
- docker
- nfs-utils
- kubectl / kubeadm / kubelet
快速安装
请将脚本最后的 1.19.6 替换成您需要的版本号, 脚本中间的 v1.19.x 不要替换
docker hub 镜像请根据自己网络的情况任选一个
- 第四行为腾讯云 docker hub 镜像
- 第六行为DaoCloud docker hub 镜像
- 第八行为华为云 docker hub 镜像
- 第十行为阿里云 docker hub 镜像
1 | 在 master 节点和 worker 节点都要执行 |
手动安装
手动执行以下代码,结果与快速安装相同。请将脚本第79行(已高亮)的 ${1} 替换成您需要的版本号,例如 1.19.6
docker hub 镜像请根据自己网络的情况任选一个
- 第四行为腾讯云 docker hub 镜像
- 第六行为DaoCloud docker hub 镜像
- 第八行为阿里云 docker hub 镜像
1 | 在 master 节点和 worker 节点都要执行 |
可以指定安装docker-ce的版本,用yum list docker-ce –showduplicates|sort -r 查看所有版本
1 | !/bin/bash |
WARNING
如果此时执行 systemctl status kubelet 命令,将得到 kubelet 启动失败的错误提示,请忽略此错误,因为必须完成后续步骤中 kubeadm init 的操作,kubelet 才能正常启动
初始化master节点
关于初始化时用到的环境变量
- APISERVER_NAME 不能是 master 的 hostname
- APISERVER_NAME 必须全为小写字母、数字、小数点,不能包含减号
- POD_SUBNET 所使用的网段不能与 master节点/worker节点 所在的网段重叠。该字段的取值为一个 CIDR 值,如果您对 CIDR 这个概念还不熟悉,请仍然执行 export POD_SUBNET=10.100.0.1/16 命令,不做修改
快速初始化
请将脚本最后的 1.19.6 替换成您需要的版本号, 脚本中间的 v1.19.x 不要替换
1 | 只在 master 节点执行 |
手动初始化
手动执行以下代码,结果与快速初始化相同。请将脚本第21行(已高亮)的 ${1} 替换成您需要的版本号,例如 1.19.6
1 | 只在 master 节点执行 |
1 | !/bin/bash |
感谢 https://github.com/zhangguanzhang/google_containers (opens new window)提供最新的 google_containers 国内镜像
初始化如果出错请看
请确保您的环境符合 安装docker及kubelet 中所有勾选框的要求
请确保您使用 root 用户执行初始化命令
不能下载 kubernetes 的 docker 镜像
安装文档中,默认使用阿里云的 docker 镜像仓库,然而,有时候,该镜像会罢工
如碰到不能下载 docker 镜像的情况,请尝试手工初始化,并修改手工初始化脚本里的第22行(文档中已高亮)为:
1
imageRepository: gcr.azk8s.cn/google-containers
检查环境变量,执行如下命令
1 | echo MASTER_IP=${MASTER_IP} && echo APISERVER_NAME=${APISERVER_NAME} && echo POD_SUBNET=${POD_SUBNET} |
- 请验证如下几点:
- 环境变量 MASTER_IP 的值应该为 master 节点的 内网IP,如果不是,请重新 export
- APISERVER_NAME 不能是 master 的 hostname
- APISERVER_NAME 必须全为小写字母、数字、小数点,不能包含减号
- POD_SUBNET 所使用的网段不能与 master节点/worker节点 所在的网段重叠。该字段的取值为一个 CIDR 值,如果您对 CIDR 这个概念还不熟悉,请仍然执行 export POD_SUBNET=10.100.0.1/16 命令,不做修改
- 重新初始化 master 节点前,请先执行
kubeadm reset -f
操作
ImagePullBackoff / Pending
如果 kubectl get pod -n kube-system -o wide 的输出结果中出现 ImagePullBackoff 或者长时间处于 Pending 的情况,请参考 查看镜像抓取进度
ContainerCreating
如果 kubectl get pod -n kube-system -o wide 的输出结果中某个 Pod 长期处于 ContainerCreating、PodInitializing 或 Init:0/3 的状态,可以尝试:
- 查看该 Pod 的状态,例如:
1
kubectl describe pod kube-flannel-ds-amd64-8l25c -n kube-system
如果输出结果中,最后一行显示的是 Pulling image,请耐心等待,或者参考 查看镜像抓取进度
1 | Normal Pulling 44s kubelet, k8s-worker-02 Pulling image "quay.io/coreos/flannel:v0.12.0-amd64" |
- 将该 Pod 删除,系统会自动重建一个新的 Pod,例如:
1 | kubectl delete pod kube-flannel-ds-amd64-8l25c -n kube-system |
检查master初始化结果
1 | 只在 master 节点执行 |
如果出错请看
ImagePullBackoff / Pending
- 如果 kubectl get pod -n kube-system -o wide的输出结果中出现 ImagePullBackoff 或者长时间处于 Pending 的情况,请参考 查看镜像抓取进度
ContainerCreating
如果kubectl get pod -n kube-system -o wide的输出结果中某个 Pod 长期处于 ContainerCreating、PodInitializing 或 Init:0/3 的状态,可以尝试:
查看该 Pod 的状态,例如:
1
kubectl describe pod kube-flannel-ds-amd64-8l25c -n kube-system
如果输出结果中,最后一行显示的是 Pulling image,请耐心等待,或者参考查看镜像抓取进度
1
Normal Pulling 44s kubelet, k8s-worker-02 Pulling image "quay.io/coreos/flannel:v0.12.0-amd64"
将该 Pod 删除,系统会自动重建一个新的 Pod,例如:
1
kubectl delete pod kube-flannel-ds-amd64-8l25c -n kube-system
初始化worker节点
获得join参数
在master节点上执行
1 | 只在 master 节点执行 |
可获取kubeadm join 命令及参数,如下所示
1 | kubeadm token create 命令的输出 |
有效时间
该 token 的有效时间为 2 个小时,2小时内,您可以使用此 token 初始化任意数量的 worker 节点。
worker节点执行初始化
针对所有的 worker 节点执行
1 | 只在 worker 节点执行 |
检查初始化结果
在master节点上执行1
2 只在 master 节点执行
kubectl get nodes -o wide
输出结果如下所示:1
2
3
4
5[root@demo-master-a-1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
demo-master-a-1 Ready master 5m3s v1.19.x
demo-worker-a-1 Ready <none> 2m26s v1.19.x
demo-worker-a-2 Ready <none> 3m56s v1.19.x
常见错误原因
经常在群里提问为什么 join 不成功的情况大致有这几种:
worker 节点不能访问 apiserver
在worker节点执行以下语句可验证worker节点是否能访问 apiserver
1 | curl -ik https://apiserver.demo:6443 |
如果不能,请在 master 节点上验证
1 | curl -ik https://localhost:6443 |
正常输出结果如下所示:
1 | HTTP/1.1 403 Forbidden |
可能原因
- 如果 master 节点能够访问 apiserver、而 worker 节点不能,则请检查自己的网络设置
- /etc/hosts 是否正确设置?
- 是否有安全组或防火墙的限制?
worker 节点默认网卡
Kubelet使用的 IP 地址与 master 节点可互通(无需 NAT 映射),且没有防火墙、安全组隔离
如果你使用 vmware 或 virtualbox 创建虚拟机用于 K8S 学习,可以尝试 NAT 模式的网络,而不是桥接模式的网络
解决方法
移除worker节点并重试
WARNING
正常情况下,您无需移除 worker 节点,如果添加到集群出错,您可以移除 worker 节点,再重新尝试添加
在准备移除的 worker 节点上执行
1 | 只在 worker 节点执行 |
在 master 节点 demo-master-a-1 上执行
1 | 只在 master 节点执行 |
如果列表中没有您要移除的节点,则忽略下一个步骤
1 | # 只在 master 节点执行 |
TIP
- 将 demo-worker-x-x 替换为要移除的 worker 节点的名字
- worker 节点的名字可以通过在节点 demo-master-a-1 上执行 kubectl get nodes 命令获得
安装Ingress Controller
快速初始化
在master节点上执行1
2 只在 master 节点执行
kubectl apply -f https://kuboard.cn/install-script/v1.19.x/nginx-ingress.yaml
nginx-ingress.yaml内容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168# 如果打算用于生产环境,请参考 https://github.com/nginxinc/kubernetes-ingress/blob/v1.5.5/docs/installation.md 并根据您自己的情况做进一步定制
apiVersion: v1
kind: Namespace
metadata:
name: nginx-ingress
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress
namespace: nginx-ingress
---
apiVersion: v1
kind: Secret
metadata:
name: default-server-secret
namespace: nginx-ingress
type: Opaque
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN2akNDQWFZQ0NRREFPRjl0THNhWFhEQU5CZ2txaGtpRzl3MEJBUXNGQURBaE1SOHdIUVlEVlFRRERCWk8KUjBsT1dFbHVaM0psYzNORGIyNTBjbTlzYkdWeU1CNFhEVEU0TURreE1qRTRNRE16TlZvWERUSXpNRGt4TVRFNApNRE16TlZvd0lURWZNQjBHQTFVRUF3d1dUa2RKVGxoSmJtZHlaWE56UTI5dWRISnZiR3hsY2pDQ0FTSXdEUVlKCktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUwvN2hIUEtFWGRMdjNyaUM3QlBrMTNpWkt5eTlyQ08KR2xZUXYyK2EzUDF0azIrS3YwVGF5aGRCbDRrcnNUcTZzZm8vWUk1Y2Vhbkw4WGM3U1pyQkVRYm9EN2REbWs1Qgo4eDZLS2xHWU5IWlg0Rm5UZ0VPaStlM2ptTFFxRlBSY1kzVnNPazFFeUZBL0JnWlJVbkNHZUtGeERSN0tQdGhyCmtqSXVuektURXUyaDU4Tlp0S21ScUJHdDEwcTNRYzhZT3ExM2FnbmovUWRjc0ZYYTJnMjB1K1lYZDdoZ3krZksKWk4vVUkxQUQ0YzZyM1lma1ZWUmVHd1lxQVp1WXN2V0RKbW1GNWRwdEMzN011cDBPRUxVTExSakZJOTZXNXIwSAo1TmdPc25NWFJNV1hYVlpiNWRxT3R0SmRtS3FhZ25TZ1JQQVpQN2MwQjFQU2FqYzZjNGZRVXpNQ0F3RUFBVEFOCkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWpLb2tRdGRPcEsrTzhibWVPc3lySmdJSXJycVFVY2ZOUitjb0hZVUoKdGhrYnhITFMzR3VBTWI5dm15VExPY2xxeC9aYzJPblEwMEJCLzlTb0swcitFZ1U2UlVrRWtWcitTTFA3NTdUWgozZWI4dmdPdEduMS9ienM3bzNBaS9kclkrcUI5Q2k1S3lPc3FHTG1US2xFaUtOYkcyR1ZyTWxjS0ZYQU80YTY3Cklnc1hzYktNbTQwV1U3cG9mcGltU1ZmaXFSdkV5YmN3N0NYODF6cFErUyt1eHRYK2VBZ3V0NHh3VlI5d2IyVXYKelhuZk9HbWhWNThDd1dIQnNKa0kxNXhaa2VUWXdSN0diaEFMSkZUUkk3dkhvQXprTWIzbjAxQjQyWjNrN3RXNQpJUDFmTlpIOFUvOWxiUHNoT21FRFZkdjF5ZytVRVJxbStGSis2R0oxeFJGcGZnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdi91RWM4b1JkMHUvZXVJTHNFK1RYZUprckxMMnNJNGFWaEMvYjVyYy9XMlRiNHEvClJOcktGMEdYaVN1eE9ycXgrajlnamx4NXFjdnhkenRKbXNFUkJ1Z1B0ME9hVGtIekhvb3FVWmcwZGxmZ1dkT0EKUTZMNTdlT1l0Q29VOUZ4amRXdzZUVVRJVUQ4R0JsRlNjSVo0b1hFTkhzbysyR3VTTWk2Zk1wTVM3YUhudzFtMApxWkdvRWEzWFNyZEJ6eGc2clhkcUNlUDlCMXl3VmRyYURiUzc1aGQzdUdETDU4cGszOVFqVUFQaHpxdmRoK1JWClZGNGJCaW9CbTVpeTlZTW1hWVhsMm0wTGZzeTZuUTRRdFFzdEdNVWozcGJtdlFmazJBNnljeGRFeFpkZFZsdmwKMm82MjBsMllxcHFDZEtCRThCay90elFIVTlKcU56cHpoOUJUTXdJREFRQUJBb0lCQVFDZklHbXowOHhRVmorNwpLZnZJUXQwQ0YzR2MxNld6eDhVNml4MHg4Mm15d1kxUUNlL3BzWE9LZlRxT1h1SENyUlp5TnUvZ2IvUUQ4bUFOCmxOMjRZTWl0TWRJODg5TEZoTkp3QU5OODJDeTczckM5bzVvUDlkazAvYzRIbjAzSkVYNzZ5QjgzQm9rR1FvYksKMjhMNk0rdHUzUmFqNjd6Vmc2d2szaEhrU0pXSzBwV1YrSjdrUkRWYmhDYUZhNk5nMUZNRWxhTlozVDhhUUtyQgpDUDNDeEFTdjYxWTk5TEI4KzNXWVFIK3NYaTVGM01pYVNBZ1BkQUk3WEh1dXFET1lvMU5PL0JoSGt1aVg2QnRtCnorNTZud2pZMy8yUytSRmNBc3JMTnIwMDJZZi9oY0IraVlDNzVWYmcydVd6WTY3TWdOTGQ5VW9RU3BDRkYrVm4KM0cyUnhybnhBb0dCQU40U3M0ZVlPU2huMVpQQjdhTUZsY0k2RHR2S2ErTGZTTXFyY2pOZjJlSEpZNnhubmxKdgpGenpGL2RiVWVTbWxSekR0WkdlcXZXaHFISy9iTjIyeWJhOU1WMDlRQ0JFTk5jNmtWajJTVHpUWkJVbEx4QzYrCk93Z0wyZHhKendWelU0VC84ajdHalRUN05BZVpFS2FvRHFyRG5BYWkyaW5oZU1JVWZHRXFGKzJyQW9HQkFOMVAKK0tZL0lsS3RWRzRKSklQNzBjUis3RmpyeXJpY05iWCtQVzUvOXFHaWxnY2grZ3l4b25BWlBpd2NpeDN3QVpGdwpaZC96ZFB2aTBkWEppc1BSZjRMazg5b2pCUmpiRmRmc2l5UmJYbyt3TFU4NUhRU2NGMnN5aUFPaTVBRHdVU0FkCm45YWFweUNweEFkREtERHdObit3ZFhtaTZ0OHRpSFRkK3RoVDhkaVpBb0dCQUt6Wis1bG9OOTBtYlF4VVh5YUwKMjFSUm9tMGJjcndsTmVCaWNFSmlzaEhYa2xpSVVxZ3hSZklNM2hhUVRUcklKZENFaHFsV01aV0xPb2I2NTNyZgo3aFlMSXM1ZUtka3o0aFRVdnpldm9TMHVXcm9CV2xOVHlGanIrSWhKZnZUc0hpOGdsU3FkbXgySkJhZUFVWUNXCndNdlQ4NmNLclNyNkQrZG8wS05FZzFsL0FvR0FlMkFVdHVFbFNqLzBmRzgrV3hHc1RFV1JqclRNUzRSUjhRWXQKeXdjdFA4aDZxTGxKUTRCWGxQU05rMXZLTmtOUkxIb2pZT2pCQTViYjhibXNVU1BlV09NNENoaFJ4QnlHbmR2eAphYkJDRkFwY0IvbEg4d1R0alVZYlN5T294ZGt5OEp0ek90ajJhS0FiZHd6NlArWDZDODhjZmxYVFo5MWpYL3RMCjF3TmRKS2tDZ1lCbyt0UzB5TzJ2SWFmK2UwSkN5TGhzVDQ5cTN3Zis2QWVqWGx2WDJ1VnRYejN5QTZnbXo5aCsKcDNlK2JMRUxwb3B0WFhNdUFRR0xhUkcrYlNNcjR5dERYbE5ZSndUeThXczNKY3dlSTdqZVp2b0ZpbmNvVlVIMwphdmxoTUVCRGYxSjltSDB5cDBwWUNaS2ROdHNvZEZtQktzVEtQMjJhTmtsVVhCS3gyZzR6cFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
namespace: nginx-ingress
data:
server-names-hash-bucket-size: "1024"
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nginx-ingress
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- update
- create
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- list
- watch
- get
- apiGroups:
- "extensions"
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- k8s.nginx.org
resources:
- virtualservers
- virtualserverroutes
verbs:
- list
- watch
- get
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nginx-ingress
subjects:
- kind: ServiceAccount
name: nginx-ingress
namespace: nginx-ingress
roleRef:
kind: ClusterRole
name: nginx-ingress
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: nginx-ingress
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9113"
spec:
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
spec:
serviceAccountName: nginx-ingress
containers:
- image: nginx/nginx-ingress:1.5.5
name: nginx-ingress
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
- name: prometheus
containerPort: 9113
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
#- -v=3 # Enables extensive logging. Useful for troubleshooting.
#- -report-ingress-status
#- -external-service=nginx-ingress
#- -enable-leader-election
- -enable-prometheus-metrics
#- -enable-custom-resources
卸载ingress congroller
在master节点上执行
1 | #只在 master 节点执行 |
配置域名解析
将域名 *.demo.yourdomain.com 解析到 demo-worker-a-2 的 IP 地址 z.z.z.z (也可以是 demo-worker-a-1 的地址 y.y.y.y)
验证配置
在浏览器访问 a.demo.yourdomain.com,将得到 404 NotFound 错误页面
提示
许多初学者在安装 Ingress Controller 时会碰到问题,请不要灰心,可暂时跳过 安装 Ingress Controller 这个部分,等您学完 www.kuboard.cn 上 Kubernetes 入门 以及 通过互联网访问您的应用程序 这两部分内容后,再来回顾 Ingress Controller 的安装。
也可以参考 Install Nginx Ingress
问题及注意
重启Kubenetes集群后遇到的问题
Kubernetes集群的设计目标是setup-and-run-forever,然而许多学习者使用自己笔记本上的虚拟机安装K8S集群用于学习,这就必然会出现反复重启集群所在虚拟机的情况。本文针对重启后会出现一些的一些令人困惑的问题做了解释。
Worker节点不能启动
Master 节点的 IP 地址变化,导致 worker 节点不能启动。请重装集群,并确保所有节点都有固定内网 IP 地址。许多Pod一直Crash或不能正常访问
1
kubectl get pods --all-namespaces
重启后会发现许多 Pod 不在 Running 状态,此时,请使用如下命令删除这些状态不正常的 Pod。通常,您的 Pod 如果是使用 Deployment、StatefulSet 等控制器创建的,kubernetes 将创建新的 Pod 作为替代,重新启动的 Pod 通常能够正常工作。
1 | kubectl delete pod <pod-name> -n <pod-namespece> |
安装过程中的问题
1 | sysctl -p |
解决办法1
modprobe br_netfilter
查看版本1
kubectl version
QA
怎么查看可安装的docker版本1
yum list docker-ce --showduplicates|sort -r
参考
https://kuboard.cn/install/install-k8s.html#%E6%A3%80%E6%9F%A5-centos-hostname
https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/troubleshooting-kubeadm/