1. 导读

know

  • k8s的优点
    • 高可用,不宕机,自动灾难恢复
    • 灰度更新,不影响业务正常运转 不会一次清除所有的
    • 一键回滚到历史版本
    • 方便的伸缩扩展(应用伸缩,机器加减)、提供负载均衡
    • 有一个完善的生态
  • k8s
    • master
      • 主节点,控制平台,不需要很高性能,不跑任务,通常一个就行了,也可以开多个主节点来提高集群可用度。
    • worker
      • 工作节点,可以是虚拟机或物理计算机,任务都在这里跑,机器性能需要好点;通常都有很多个,可以不断加机器扩大集群;每个工作节点由主节点管理
    • pod
      • 豆荚,K8S 调度、管理的最小单位,一个 Pod 可以包含一个或多个容器,每个 Pod 有自己的虚拟IP。一个工作节点可以有多个pod,主节点会考量负载自动调度 pod 到哪个节点运行。
  • 工作负载分类
    • Deployment
      适合无状态应用,所有pod等价,可替代
    • StatefulSet
      有状态的应用,适合数据库这种类型。
    • DaemonSet
      在每个节点上跑一个 Pod,可以用来做节点监控、节点日志收集等
    • Job & CronJob
      Job 用来表达的是一次性的任务,而 CronJob 会根据其时间规划反复运行

kubectl

  • kubectl 是一个用来跟 K8S 集群进行交互的命令行工具
    1
    2
    3
    brew install kubectl
    # 查看节点。
    kubectl get node

1.1 minikube

1.2 云服务器安装

  • 可视化操作
  • 登录阿里云控制台 产品搜索 Kubernates
  • 选择流量计费测试
  • 环境默认就配置好了

1.3 裸机安装

  • 裸机安装 需要两台机器 自己安装k8s组件
  • 主节点
    • docker
    • kubectl
    • kubeadm 集群初始化工具
  • 工作节点
    • docker
    • kubelet 管理Pod和容器
    • kube-proxy 网络代理

1.3.1 购买服务器

1
2
3
4
5
6
7
// 记录自己的公网和内网地址。 下面的这些服务器已经销毁了 只是举例
175.27.136.106 master
10.206.0.5
119.45.27.53 node1
10.206.0.2
119.45.1.100 node2
10.206.0.7

1.3.2 服务器安装启动依赖

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
# 每个节点分别设置对应主机名
hostnamectl set-hostname master
hostnamectl set-hostname node1
hostnamectl set-hostname node2
# 内网ip
vim /etc/hosts
10.206.0.5 master
10.206.0.2 node1
10.206.0.7 node2
ping node1
# 所有服务器关闭 SELinux 和防火墙
setenforce 0
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
systemctl stop firewalld
systemctl disable firewalld
# 添加k8s安装源
cat <<EOF > kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
mv kubernetes.repo /etc/yum.repos.d/
# docker安装源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 安装所需组件
yum install -y kubelet kubeadm kubectl docker-ce
# 启动 kubelet、docker,并设置开机启动 所有服务器
systemctl enable kubelet
systemctl start kubelet
systemctl enable docker
systemctl start docker

1.3.3 修改配置

  • kubernetes 官方推荐 docker 等使用 systemd 作为 cgroupdriver,否则 kubelet 启动不了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 修改 docker 配置(所有节点)
    cat <<EOF > daemon.json
    {
    "exec-opts": ["native.cgroupdriver=systemd"],
    "registry-mirrors": ["https://ud6340vz.mirror.aliyuncs.com"]
    }
    EOF
    mv daemon.json /etc/docker/
    # 重启生效
    systemctl daemon-reload
    systemctl restart docker

1.3.4 主节点初始化集群

  • kubeadm 初始化集群(仅在主节点跑)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 初始化集群控制台 Control plane
    # 失败了可以用 kubeadm reset 重置
    kubeadm init --image-repository=registry.aliyuncs.com/google_containers

    # 记得把 kubeadm join xxx 保存起来
    # 忘记了重新获取:kubeadm token create --print-join-command

    # 复制授权文件,以便 kubectl 可以有权限访问集群
    # 如果你其他节点需要访问集群,需要从主节点复制这个文件过去其他节点
    mkdir -p $HOME/.kube
    cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    chown $(id -u):$(id -g) $HOME/.kube/config

    # 在其他机器上创建 ~/.kube/config 文件也能通过 kubectl 访问到集群
  • 把工作节点加入集群(只在工作节点跑)

1
2
3
4
5
#
kubeadm join 10.206.0.5:6443 --token pcwrhg.993ucguu26f8yczo --discovery-token-ca-cert-hash sha256:74c000111a188dcfa6e5f529514f42bd3a8bbe97aa9d696b30d48eb9946abf34
#
kubectl get node
这时候都是notReady
  • 安装网络插件,否则 nodeNotReady 状态(主节点跑)
    1
    2
    3
    # 很有可能国内网络访问不到这个资源,你可以网上找找国内的源安装
    # flannel
    kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

2. demo

1
2
# 运行一个pod 名字是testapp
kubectl run testapp --image=ccr.ccs.tencentyun.com/k8s-tutorial/test-k8s:v1
  • 执行命令 kubectl apply -f app.yaml
  • pod.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: v1
    kind: Pod
    metadata:
    name: test-pod
    spec:
    # 定义容器,可以多个
    containers:
    - name: test-k8s # 容器名字
    image: ccr.ccs.tencentyun.com/k8s-tutorial/test-k8s:v1 # 镜像

3. kubernetes 常用命令

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
# 部署应用
kubectl apply -f app.yaml
# 查看 deployment
kubectl get deployment
# 查看 pod
kubectl get pod -o wide
# 查看 pod 详情
kubectl describe pod podName
# 查看 log 不断查看日志 -f
kubectl logs podName [-f]
# 进入 Pod 容器终端, -c container-name 可以指定进入哪个容器
# exit 退出
kubectl exec -it podName -- bash
# 伸缩扩展副本
kubectl scale deployment test-k8s --replicas=5
# 把集群内端口映射到节点
kubectl port-forward podName 8090:8080
# 查看历史
kubectl rollout history deployment test-k8s
# 回到上个版本
kubectl rollout undo deployment test-k8s
# 回到指定版本
kubectl rollout undo deployment test-k8s --to-revision=2
# 删除部署
kubectl delete deployment test-k8s
# 查看全部
kubectl get all
# 重新部署
kubectl rollout restart deployment test-k8s
# 命令修改镜像,--record 表示把这个命令记录到操作历史中
kubectl set image deployment test-k8s test-k8s=ccr.ccs.tencentyun.com/k8s-tutorial/test-k8s:v2-with-error --record
# 暂停运行,暂停后,对 deployment 的修改不会立刻生效,恢复后才应用设置
kubectl rollout pause deployment test-k8s
# 恢复
kubectl rollout resume deployment test-k8s
# 输出到文件
kubectl get deployment test-k8s -o yaml >> app2.yaml
# 删除全部资源
kubectl delete all --all

4.service

  • service

    • ClusterIP 默认的,仅在集群内可用
    • NodePort
      • 暴露端口到节点,提供了集群外部访问的入口 端口范围固定 30000 ~ 32767
    • LoadBalancer
      • 需要负载均衡器(通常都需要云服务商提供,裸机可以安装 METALLB 测试)
      • 会额外生成一个 IP 对外服务 k8S 支持的负载均衡器:负载均衡器
  • Service 通过 label 关联对应的 Pod

  • Servcie 生命周期不跟 Pod 绑定,不会因为 Pod 重创改变 IP

  • 提供了负载均衡功能,自动转发流量到不同 Pod

  • 可对集群外部提供访问端口

  • 集群内部可通过服务名字访问

4.1 yaml

  • kubectl get svc 查看服务

  • kubectl describe svc test-k8s 查看服务详情

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    apiVersion: v1
    kind: Service
    metadata:
    name: test-k8s
    spec:
    selector:
    app: test-k8s
    type: ClusterIP
    ports:
    - port: 8080 # 本 Service 的端口
    targetPort: 8080 # 容器端口
    #
  • 服务的默认类型是ClusterIP,只能在集群内部访问,我们可以进入到 Pod 里面访问:

  • 集群外部访问,可以通过端口转发实现(只适合临时测试用)

    • kubectl port-forward service/test-k8s 8888:8080

4.2 对外暴露服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: test-k8s
spec:
selector:
app: test-k8s
# 默认 ClusterIP 集群内可访问,NodePort 节点可访问,LoadBalancer 负载均衡模式(需要负载均衡器才可用)
type: NodePort
ports:
- port: 8080 # 本 Service 的端口
targetPort: 8080 # 容器端口
nodePort: 31000 # 节点端口,范围固定 30000 ~ 32767
# curl http://localhost:31000/hello/easydoc

4.3 多端口

  • 必须配置name
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    apiVersion: v1
    kind: Service
    metadata:
    name: test-k8s
    spec:
    selector:
    app: test-k8s
    type: NodePort
    ports:
    - port: 8080 # 本 Service 的端口
    name: test-k8s # 必须配置
    targetPort: 8080 # 容器端口
    nodePort: 31000 # 节点端口,范围固定 30000 ~ 32767
    - port: 8090
    name: test-other
    targetPort: 8090
    nodePort: 32000

5. Deployment

  • Deployment 通过 label 关联起来 Pods

5.1 yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: apps/v1
kind: Deployment
metadata:
# 部署名字
name: test-k8s
spec:
# 要运行的Pod数量
replicas: 2
# 用来查找关联的 Pod,所有标签都匹配才行
selector:
matchLabels:
# 自己定义的 随便写
app: test-k8s
# 定义 Pod 相关数据
template:
metadata:
labels:
app: test-k8s
spec:
# 定义容器,可以多个
containers:
- name: test-k8s # 容器名字
image: ccr.ccs.tencentyun.com/k8s-tutorial/test-k8s:v1 # 镜像

5.2 pod报错

1
2
3
4
5
6
7
8
# 如果你运行 kubectl describe pod/pod-name 发现 Events 中有下面这个错误
networkPlugin cni failed to set up pod "test-k8s-68bb74d654-mc6b9_default" network: open /run/flannel/subnet.env: no such file or directory
# 在每个节点创建文件/run/flannel/subnet.env写入以下内容即可解决
vim /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

5.3 将Pod指定到某个节点运行

  • 将 Pod 分配给节点
  • 计算资源配额
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: v1
    kind: Pod
    metadata:
    name: nginx
    labels:
    env: test
    spec:
    containers:
    - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    nodeSelector:
    disktype: ssd

6. StatefulSet

6.1 MongoDB

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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 3
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongo
image: mongo:4.4
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: mongodb
spec:
selector:
app: mongodb
type: ClusterIP
# HeadLess
clusterIP: None
ports:
- port: 27017
targetPort: 27017

6.2 hostPath

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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongo
image: mongo:4.4
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /data/db # 容器里面的挂载路径
name: mongo-data # 卷名字,必须跟下面定义的名字一致
volumes:
- name: mongo-data # 卷名字
hostPath:
path: /data/mongo-data # 节点上的路径
type: DirectoryOrCreate # 指向一个目录,不存在时自动创建

6.3 Storage Class (SC)

  • 将存储卷划分为不同的种类,例如:SSD,普通磁盘,本地磁盘,按需使用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: slow
    provisioner: kubernetes.io/aws-ebs
    parameters:
    type: io1
    iopsPerGB: "10"
    fsType: ext4

6.4 Persistent Volume (PV)

  • 描述卷的具体信息,例如磁盘大小,访问模式。文档,类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: mongodata
    spec:
    capacity:
    storage: 2Gi
    volumeMode: Filesystem # Filesystem(文件系统) Block(块)
    accessModes:
    - ReadWriteOnce # 卷可以被一个节点以读写方式挂载
    persistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
    path: /root/data
    nodeAffinity:
    required:
    # 通过 hostname 限定在某个节点创建存储卷
    nodeSelectorTerms:
    - matchExpressions:
    - key: kubernetes.io/hostname
    operator: In
    values:
    - node2

6.5 Persistent Volume Claim (PVC)

  • 对存储需求的一个申明,可以理解为一个申请单,系统根据这个申请单去找一个合适的 PV
  • 还可以根据 PVC 自动创建 PV
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mongodata
    spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: "local-storage"
    resources:
    requests:
    storage: 2Gi

6.6 动态卷

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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
image: mongo:5.0
imagePullPolicy: IfNotPresent
name: mongo
volumeMounts:
- mountPath: /data/db
name: mongo-data
volumes:
- name: mongo-data
persistentVolumeClaim:
claimName: mongodata
---
apiVersion: v1
kind: Service
metadata:
name: mongodb
spec:
clusterIP: None
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: mongodb
type: ClusterIP
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodata
spec:
capacity:
storage: 2Gi
volumeMode: Filesystem # Filesystem(文件系统) Block(块)
accessModes:
- ReadWriteOnce # 卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /root/data
nodeAffinity:
required:
# 通过 hostname 限定在某个节点创建存储卷
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-storage"
resources:
requests:
storage: 2Gi

7. ConfigMap & Secret

7.1 ConfigMap

  • ConfigMap,可以方便的配置一些变量
    • 数据库密码 链接地址 …
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: mongo-config
      data:
      mongoHost: mongodb-0.mongodb
      #
      # 应用
      kubectl apply -f configmap.yaml
      # 查看
      kubectl get configmap mongo-config -o yaml

7.2 Secret

  • 一些重要数据,例如密码、TOKEN,我们可以放到 secret
  • 注意,数据要进行 Base64 编码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: Secret
    metadata:
    name: mongo-secret
    # Opaque 用户定义的任意数据,更多类型介绍 https://kubernetes.io/zh/docs/concepts/configuration/secret/#secret-types
    type: Opaque
    data:
    # 数据要 base64。https://tools.fun/base64.html
    mongo-username: bW9uZ291c2Vy
    mongo-password: bW9uZ29wYXNz
    # 应用
    kubectl apply -f secret.yaml
    # 查看
    kubectl get secret mongo-secret -o 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
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: mongodb
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: mongodb
    template:
    metadata:
    labels:
    app: mongodb
    spec:
    containers:
    - name: mongo
    image: mongo:4.4
    # IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
    imagePullPolicy: IfNotPresent
    env:
    - name: MONGO_INITDB_ROOT_USERNAME
    valueFrom:
    secretKeyRef:
    name: mongo-secret
    key: mongo-username
    - name: MONGO_INITDB_ROOT_PASSWORD
    valueFrom:
    secretKeyRef:
    name: mongo-secret
    key: mongo-password
    # Secret 的所有数据定义为容器的环境变量,Secret 中的键名称为 Pod 中的环境变量名称
    # envFrom:
    # - secretRef:
    # name: mongo-secret

7.3 挂载为文件

  • 更适合证书文件
  • 挂载后,会在容器中对应路径生成文件,一个 key 一个文件,内容就是 value
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    apiVersion: v1
    kind: Pod
    metadata:
    name: mypod
    spec:
    containers:
    - name: mypod
    image: redis
    volumeMounts:
    - name: foo
    mountPath: "/etc/foo"
    readOnly: true
    volumes:
    - name: foo
    secret:
    secretName: mysecret

8. Helm & 命名空间

  • Helm类似 npm,pip,docker hub, 可以理解为是一个软件库,可以方便快速的为我们的集群安装一些第三方软件。
  • 使用 Helm 我们可以非常方便的就搭建出来 MongoDB / MySQL 副本集群,YAML 文件别人都给我们写好了,直接使用

8.1 安装

1
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

8.2 安装MongoDB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-mongo bitnami/mongodb

# 指定密码和架构
helm install my-mongo bitnami/mongodb --set architecture="replicaset",auth.rootPassword="mongopass"

# 删除
helm ls
heml delete my-mongo

# 查看密码
kubectl get secret my-mongo-mongodb -o json
kubectl get secret my-mongo-mongodb -o yaml > secret.yaml

# 临时运行一个包含 mongo client 的 debian 系统
kubectl run mongodb-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mongodb:4.4.10-debian-10-r20 --command -- bash

# 进去 mongodb
mongo --host "my-mongo-mongodb" -u root -p mongopass

# 也可以转发集群里的端口到宿主机访问 mongodb
kubectl port-forward svc/my-mongo-mongodb 27017:27018

8.3 命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建命名空间
kubectl create namespace testapp
# 部署应用到指定的命名空间
kubectl apply -f app.yml --namespace testapp
# 查询
kubectl get pod --namespace kube-system

# 可以用 kubens 快速切换 namespace
# 切换命名空间
kubens kube-system
# 回到上个命名空间
kubens -
# 切换集群
kubectx minikube

9. Ingress

  • ngress 为外部访问集群提供了一个 统一 入口,避免了对外暴露集群端口;
    • 功能类似 Nginx,可以根据域名、路径把请求转发到不同的 Service
    • 可以配置 https
  • LoadBalancer 有什么区别?
    • LoadBalancer 需要对外暴露端口,不安全;
    • 无法根据域名、路径转发流量到不同 Service,多个 Service 则需要开多个 LoadBalancer;
    • 功能单一,无法配置 https
  • 云服务会自动配置 可视化配置
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
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-example
spec:
ingressClassName: nginx
rules:
- host: tools.fun
http:
paths:
- path: /easydoc
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /svnbucket
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080

10. 链接

用脚本搭建集群