kubeadm搭建集群

准备基本环境

VMware搭建集群:

参数 配置
CPU 2 * 2核
内存 2G
系统 centos7.x-x86_64

三个节点信息如下:

HOSTS IP
master 192.168.117.156
node1 192.168.117.157
node2 192.168.117.158

关闭防火墙:

1
2
$ systemctl stop firewalld
$ systemctl disable firewalld

禁用交换区:

1
2
3
4
5
6
7
8
9
10
11
$ vim /etc/fstab
# /etc/fstab
# Created by anaconda on Fri Nov 12 00:53:22 2021
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=ab32a4a5-53cf-449d-b6b6-64a4e4bdc0b7 /boot xfs defaults 0 0
# /dev/mapper/centos-swap swap swap defaults 0 0
$ reboot

打开Linux内置的桥接的功能:

1
2
3
$ echo "1" > /proc/sys/net/ipv4/ip_forward
$ echo "1" > /proc/sys/net/bridge/bridge-nf-call-iptables
$ echo "1" > /proc/sys/net/bridge/bridge-nf-call-ip6tables

将桥接的IPV4流量传递到iptables的链:

1
2
3
4
5
$ cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
$ sysctl --system

时间同步:

1
2
$ yum install ntpdate -y
$ ntpdate time.windows.com

安装docker

配置docker的yum源:

1
2
3
4
5
6
7
8
$ cat >/etc/yum.repos.d/docker.repo<<EOF
[docker-ce-edge]
name=Docker CE Edge - \$basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/\$basearch/edge
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
EOF

使用yum安装docker:

1
2
$ yum install -y yum-utils
$ yum -y install docker-ce

配置docker的镜像源:

1
2
3
4
5
6
7
$ mkdir p /etc/docker
$ cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["https://lfqo46wx.mirror.aliyuncs.com"]
}
EOF
$ systemctl daemon-reload && systemctl enable docker && systemctl start docker

安装kube相关

配置k8s的yum源:

1
2
3
4
5
6
7
8
9
$ cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

安装kubeadm、kubelet和kubectl:

1
2
$ yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0
$ systemctl enable kubelet

初始化master

192.168.117.156(master)执行:

1
$ kubeadm init --apiserver-advertise-address=192.168.117.156 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.18.0 --service-cidr=10.96.0.0/12  --pod-network-cidr=10.244.0.0/16

出现以下日志说明加入成功:

1
Your Kubernetes control-plane has initialized successfully!

运行成功后,记录日志中的命令:

1
2
3
4
5
$ 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 192.168.117.156:6443 --token wknxoo.4ogvi2jcyb78roa3 \
--discovery-token-ca-cert-hash sha256:99062bc81d7ccdaa3a172bdeaefd3178db8a36c0964c60131ad1110a5dec83fb

添加slave节点

在node1和node2执行命令,加入集群:

1
2
kubeadm join 192.168.117.156:6443 --token blq5e1.69fw04mz5gldiynq \
--discovery-token-ca-cert-hash sha256:ef5b7163cebfd25a2d844befd66767a38f084a9ee249cbc384ad1ec64b961abb

出现以下日志说明节点加入成功:

1
2
3
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

在maser节点执行命令查看节点:

1
2
3
4
5
[root@master download]# kubectl get node
NAME STATUS ROLES AGE VERSION
master NotReady master 71s v1.18.0
node1 NotReady <none> 31s v1.18.0
node2 NotReady <none> 22s v1.18.0

部署CNI网络

上面节点的状态还是NotReady,下面需要部署网络插件,进行联网访问。

在master执行命令:

1
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

等待pod创建完成,然后查看运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master k8s]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-7ff77c879f-62gkm 1/1 Running 0 55m
coredns-7ff77c879f-xtgz5 1/1 Running 0 55m
etcd-master 1/1 Running 0 71m
kube-apiserver-master 1/1 Running 0 71m
kube-controller-manager-master 1/1 Running 0 71m
kube-flannel-ds-h28k2 1/1 Running 0 2m4s
kube-flannel-ds-nw6lm 1/1 Running 0 2m5s
kube-flannel-ds-xt9vc 1/1 Running 0 2m4s
kube-proxy-gtwbl 1/1 Running 0 70m
kube-proxy-v2zw2 1/1 Running 0 71m
kube-proxy-x2cxl 1/1 Running 0 71m
kube-scheduler-master 1/1 Running 0 71m

再次查看节点状态:

1
2
3
4
5
[root@master k8s]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready master 72m v1.18.0
node1 Ready <none> 72m v1.18.0
node2 Ready <none> 71m v1.18.0

已经变成Ready状态。

创建Pod测试

在Kubernetes集群中创建一个nginx的pod,验证是否正常运行:

1
2
3
4
5
6
7
8
# 自动拉取nginx镜像并创建pod
$ kubectl create deployment nginx --image=nginx
# 查看状态
$ kubectl get pod
# 暴露端口
$ kubectl expose deployment nginx --port=80 --type=NodePort
# 查看对外端口
$ kubectl get pod,svc

测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@master k8s]# kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
[root@master k8s]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-f89759699-z744t 0/1 ContainerCreating 0 25s
[root@master k8s]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-f89759699-z744t 1/1 Running 0 62s
[root@master k8s]# kubectl expose deployment nginx --port=80 --type=NodePort
service/nginx exposed
[root@master k8s]# kubectl get pod,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-f89759699-z744t 1/1 Running 0 5m27s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 93m
service/nginx NodePort 10.104.221.51 <none> 80:31900/TCP 107s

可以看到nginx对外开放端口为31900,在node1上测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@node1 k8s]# curl 192.168.117.156:31900
<!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>

说明nginx启动成功。

相关问题

问题1

部署master出现如下错误:

1
2
3
4
5
6
7
8
9
10
Unfortunately, an error has occurred:
timed out waiting for the condition

This error is likely caused by:
- The kubelet is not running
- The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)

If you are on a systemd-powered system, you can try to troubleshoot the error with the following commands:
- 'systemctl status kubelet'
- 'journalctl -xeu kubelet'

但是查看kubelet的状态是running,这个问题比较玄学,网上的解决方法也众说纷纭。

建议执行kubeadm reset,然后重新kubeadm init,重启可以解决的问题就没必要浪费时间。

问题2

加入slave出现如下错误:

1
2
3
4
5
6
[root@node1 ~]# kubeadm join 192.168.117.156:6443 --token blq5e1.69fw04mz5gldiynq     --discovery-token-ca-cert-hash sha256:ef5b7163cebfd25a2d844befd66767a38f084a9ee249cbc384ad1ec64b961abb
W1112 01:13:04.455538 15663 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set.
[preflight] Running pre-flight checks
[WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
error execution phase preflight: couldn't validate the identity of the API Server: Get https://192.168.117.156:6443/api/v1/namespaces/kube-public/configmaps/cluster-info?timeout=10s: x509: certificate has expired or is not yet valid
To see the stack trace of this error execute with --v=5 or higher

默认token有效期为24小时,过期后就不可用,此时需要在master重新生成token:

1
2
3
4
5
[root@master ~]# kubeadm token generate
1arm0f.fvdrfs7knzh9j05q
[root@master ~]# kubeadm token create 1arm0f.fvdrfs7knzh9j05q --print-join-command --ttl=0
W1112 13:35:14.003643 49533 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
kubeadm join 192.168.117.156:6443 --token 1arm0f.fvdrfs7knzh9j05q --discovery-token-ca-cert-hash sha256:ef5b7163cebfd25a2d844befd66767a38f084a9ee249cbc384ad1ec64b961abb

在node节点上执行新的kudeadm join命令即可。

问题3

部署网络插件后,发现flannel的状态为Init:ImagePullBackOff,coredns的状态为Pending

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master download]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-7ff77c879f-fjxjv 0/1 Pending 0 13m
coredns-7ff77c879f-x65x5 0/1 Pending 0 13m
etcd-master 1/1 Running 0 13m
kube-apiserver-master 1/1 Running 0 13m
kube-controller-manager-master 1/1 Running 0 13m
kube-flannel-ds-br57h 0/1 Init:ImagePullBackOff 0 6m1s
kube-flannel-ds-dxt5f 0/1 Init:ImagePullBackOff 0 6m1s
kube-flannel-ds-lv4gx 0/1 Init:ImagePullBackOff 0 6m1s
kube-proxy-gtwbl 1/1 Running 0 12m
kube-proxy-v2zw2 1/1 Running 0 12m
kube-proxy-x2cxl 1/1 Running 0 13m
kube-scheduler-master 1/1 Running 0 13m

这是因为国内访问国外镜像源较慢造成的。

此时,首先我们下载配置文件并移动到指定文件夹:

1
2
$ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
$ mkdir -p /home/khighness/k8s && mv kube-flannel.yml /home/khighness/k8s/

然后编辑文件,修改其中的cni-pluginkube-flannel的image:

1
2
3
4
5
- name: install-cni
image: quay.io/coreos/flannel:v0.13.1-rc2-amd64

- name: kube-flannel
image: quay.io/coreos/flannel:v0.13.1-rc2-amd64

然后去官方仓库手动下载镜像:https://github.com/coreos/flannel/releases

下载我们指定的版本flanneld-v0.13.1-rc2-amd64.docker

下载成功后将文件通过ftp传输到master节点,然后在master节点将该文件通过scp命令传输到node1和node2即可。

然后在三台机器上都执行:

1
$ docker load < flanneld-v0.13.1-rc2-amd64.docker

最后在master重新执行:

1
$ kubectl apply -f /home/khighness/k8s/kube-flannel.yml

等待一分钟后,查看flannel和coredns的状态为running,即为成功。

二进制方式搭建集群

具体步骤

  1. 准备机器环境
  2. 为etcd和apiserver自签证书
  3. 部署etcd集群
  4. 部署master组件:kube-apiserver、kube-controller-manager、kube-scheduler、etcd
  5. 部署node组件:kubelet、kube-proxy、docker、etcd
  6. 部署集群网络

cfssl工具

1
2
3
4
5
6
7
$ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
$ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
$ chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64
$ mv cfssl_linux-amd64 /usr/local/bin/cfssl
$ mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
$ mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo

kubect常用命令

kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。

语法如下:

1
$ kubectl [command] [type] [name] [flags]

声明式资源管理

语法:

1
$ kubectl apply (-f FILENAME | -k DIRECTORY) [options]
  • -f: yaml或者json格式的资源配置文件
  • -k: kustomization.yaml配置文件的路径

声明式管理,即增加或修改操作都可以使用apply命令完成,取决于配置文件:

  • 配置文件中的资源不存在于集群中,则创建这个资源;
  • 配置文件中的资源已存在于集群中,则更新资源字段。

命令式资源管理

创建资源:

1
2
3
4
# 创建depolyment
$ kubectl create deploymet <deploymet-name> --image=<image-name>
# 创建replication controller
$ kubectl expose rc <image-name> --port=<port> --target-port=<target-port>

更新资源:

1
2
3
4
5
6
# 将yaml中描述的pod对象扩展为n个
$ kubectl scale --replicas=<n> -f <filename>.yaml
# 给pod增加descrption
$ kubectl annotate pods <pod-name> description=<description>
# 给pod增加status
$ kubectl label --overwrite pods <pod-name> status

删除资源:

1
2
3
4
5
6
7
8
9
10
# 删除配置文件对应的资源
$ kubectl delete -f <flename>.yaml
# 根据名称删除pod和service
$ kubectl delete pod, svc <name...>
# 删除所有包含某个label的pod和service
$ kubectl delete pod, svc, -l name=<label-name>
# 删除所有pod
$ kubectl delete pod -all
# 强制删除pod
$ kubectl delete pod <name> --grace-period=0 --force

资源查看

get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 语法
$ kubectl get
[(-o|--output=)](TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]
# 查看当前namespace中的所有service
$ kubectl get services
# 查看集群中所有namespace的pod
$ kubectl get pods --all-namespaces
# 查看详细信息
$ kubectl get pods -o wide
# 查看指定资源名称的信息
$ kubectl get deployment <name>
# 监控资源状态
$ kubectl get deployment <name> --watch
# 查看yaml格式的资源配置
$ kubectl get pod <name> -o yaml
# 查看指定标签的pod
$ kubectl get pod <name> -l app=<app-name>

describe:

1
2
3
4
5
6
# 语法
$ kubectl describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME) [options]
# 查看节点的详细信息
$ kubectl describe nodes <node-name>
# 查看pod的详细信息
$ kubctl describe pods <pod-name>

容器管理

日志查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 语法
$ kubectl logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER] [options]
# 输出一个单容器pod的日志
$ kubectl logs <pod-name>
# 追踪一个单容器pod的日志
$ kubectl logs -f <pod-name>
# 输出该pod上一个退出的容器实例日志
$ kubectl logs <pod-name> -f
# 输出多容器pod中的指定容器的日志,-c指定容器
$ kubectl logs <pod-name> -c <container-name>
# 输出所有包含app-name标签的日志
$ kubectl logs -l app=<app-name>
# 指定时间戳输出日志
kubectl logs my-pod --since-time=2021-11-12T23:11:00Z
# 指定时间段输出日志,单位s/m/h
kubectl logs my-pod --since=1h

执行命令:

1
2
3
4
5
6
# 语法
$ kubectl exec POD [-c CONTAINER] -- COMMAND [args...] [options]
# 对pod执行ping命令
$ kubectl exec <pod-name> ping parak.top
# 进入pod的内部,对于多容器的pod,使用-c指定容器
$ kubectl exec -it <pod-name> bash

文件传输:

1
2
3
4
5
6
7
8
# 语法
$ kubectl cp <file-spec-src> <file-spec-dest> [options]
# 拷贝宿主机本地文件夹到pod
$ kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir
# 指定namespace的拷贝pod文件到宿主机本地目录
$ kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar
# 对于多容器pod,用-c指定容器名
$ kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <container-name>

集群管理

集群信息查看:

1
2
3
4
5
6
# 查看master和集群服务的地址
$ kubectl cluster-info
# 查看集群详细日志
$ kubectl cluster-info dump
# 查看Kubetnetes集群和客户端版本
$ kubectl version

节点管理:

1
2
3
4
5
6
7
8
9
10
# 标记my-node为unschedulable,禁止pod被调度过来。
# 注意这时现有的pod还会继续运行,不会被驱逐出去。
$ kubectl uncordon my-node
# 与cordon相反,标记my-node为允许调度
$ kubectl drain my-node
# 将my-node的pod平滑切换到其他node,同时标记pod为unschedulable。
kubectl drain my-node --ignore-daemonsets --force --delete-local-data
# --ignore-daemonsets 忽略daemonset部署的pod
# --force 直接删除不由workload对象(Deployment、Job等)管理的pod
# --delete-local-data 直接删除挂载有本地目录(empty-dir方式)的pod

YAML资源清单文件

Kubbernetes集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是把需要对资源对象操作编辑到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
apiVersion: v1             # 指定版本
kind: Pod # 指定创建资源的类型
metadata: # 资源的元数据/属性
name: digango-pod # 资源名称,在同一个namespace中必须唯一
namespace: backend # 设置namespace
labels: # 设置资源的标签
version: v1
annotations: # 设置资源的注解
- name: String

spec:
restartPolicy: Always # 自动重启
nodeSelector: node1 # 选择节点
containers:
- name: django-pod # 容器的名字
image: django:v1.1 # 容器使用的镜像地址
imagePullPolicy: Never # 三个选择Always、Never、IfNotPresent,每次启动时检查和更新(从registery)images的策略,
# Always,每次都检查
# Never,每次都不检查(不管本地是否有)
# IfNotPresent,如果本地有就不检查,如果没有就拉取
command: ['sh'] # 启动容器的运行命令,将覆盖容器中的Entrypoint,对应Dockefile中的ENTRYPOINT
args: ["$(str)"] # 启动容器的命令参数,对应Dockerfile中CMD参数
env: # 指定容器中的环境变量
- name: str # 变量的名字
value: "/etc/run.sh" # 变量的值
resources: # 资源管理
requests: # 容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
cpu: 0.1 # CPU资源(核数),两种方式,浮点数或者是整数+m,0.1=100m,最少值为0.001核(1m)
memory: 32Mi # 内存使用量
limits: # 资源限制
cpu: 0.5
memory: 32Mi
ports:
- containerPort: 8080 # 容器开发对外的端口
name: uwsgi # 名称
protocol: TCP
livenessProbe: # pod内容器健康检查的设置
httpGet: # 通过httpget检查健康,返回200-399之间,则认为容器正常
path: / # URI地址
port: 8080
#host: 127.0.0.1 # 主机地址
scheme: HTTP
initialDelaySeconds: 180 # 表明第一次检测在容器启动后多长时间后开始
timeoutSeconds: 5 # 检测的超时时间
periodSeconds: 15 # 检查间隔时间
lifecycle: # 生命周期管理(钩子)
postStart: # 容器运行之前运行的任务
exec:
command:
- 'sh'
- 'yum upgrade -y'
preStop: # 容器关闭之前运行的任务
exec:
command: ['service httpd stop']
volumeMounts: # 挂载设置
- name: volume # 挂载设备的名字,与volumes[*].name 需要对应
mountPath: /data # 挂载到容器的某个路径下
readOnly: True
volumes: # 定义一组挂载设备
- name: volume # 定义一个挂载设备的名字
#meptyDir: {}
hostPath:
path: /opt # 挂载设备类型为hostPath,路径为宿主机下的/opt

快速创建

(1)使用kubectl create

适用于资源没有部署的情况,直接创建一个YAML配置文件:

1
$ kubectl create deployment web --image=nginx -o yaml --dry-run > nginx.yaml

(2)使用kubectl get

适用于资源已经创建的情况,导出资源的YAML配置文件:

1
kubectl get deploy nginx -o=yaml --export > nginx.yaml