# 第四模块 Kubernetes落地实践(上)

# K8S 概述

# 概述-架构图

分布式系统,两类角色:管理节点和工作节点

# 概述-核心组件

  • ETCD: 分布式高性能键值数据库,存储整个集群的所有元数据

  • APIServer: API服务器,集群资源访问控制入口,提供RESTAPI及安全访问控制

  • Scheduler: 调度器,负责把业务容器调度到最合适的Node节点

  • Controller Manager: 控制器管理,确保集群资源按照期望的方式运行

    • Replication Controller
    • Node Controller
    • ResourceQuota Controller
    • Namespace Controller
    • ServiceAccount Controller
    • Token Controller
    • Service Controller
    • Endpoints Controller
  • kubelet: 运行在每个节点上的主要的"节点代理"

    • Pod 管理
    • 容器健康检查
    • 容器监控
  • kube-proxy: 维护节点中的iptables或者ipvs规则

  • kubectl: 命令行接口,用于对 Kubernetes 集群运行命令

# 组件运行方式

  • 静态Pod的方式:
# 系统初始化配置 /etc/kubernetes/manifests
# etcd、apiserver、controller-manager、kube-scheduler

[root@k8s-master manifests]# kubectl -n kube-system get po
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-59d64cd4d4-fq2tb             1/1     Running   1          74d
coredns-59d64cd4d4-qjrdg             1/1     Running   1          74d
etcd-k8s-master                      1/1     Running   1          74d
kube-apiserver-k8s-master            1/1     Running   5          74d
kube-controller-manager-k8s-master   1/1     Running   3          74d
kube-proxy-9p685                     1/1     Running   1          74d
kube-proxy-pmwhf                     1/1     Running   0          68d
kube-scheduler-k8s-master            1/1     Running   2          74d
  • systemd服务方式:
systemctl status kubelet
  • kubectl: 二进制命令行工具
[root@k8s-master ~]# kubectl -h
[root@k8s-master ~]# kubectl get -h
[root@k8s-master ~]# kubectl create -h
[root@k8s-master ~]# kubectl create namespace -h

# 概述-集群资源

  • 组件是支撑k8s平台的运行的软件;
  • 资源是管理k8s的一种单位,是一种能力的体现;
[root@k8s-master ~]# kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
  • 命名空间 namespace 类似于资源池的概念,一个池子里可以有各种资源类型
[root@k8s-master ~]# kubectl get namespaces
NAME              STATUS   AGE
default           Active   74d
kube-flannel      Active   69d
kube-node-lease   Active   74d
kube-public       Active   74d
kube-system       Active   74d
  • 命名空间 namespace 注意事项

    • NAMESPACED的资源,创建的时需要指定namespace,若不指定,默认会在default命名空间下
    • 相同namespace下的同类资源不可以重名,不同类型的资源可以重名
    • 不同namespace下的同类资源可以重名
    • 通常在项目使用的时候,我们会创建带有业务含义的namespace来做逻辑上的整合

# K8S 工作流程

1. 用户创建yaml编排文件(记录业务应用的名称、镜像地址等),通过调用APIServer执行创建Pod;
2. APIServer收到用户的Pod创建请求,将Pod信息写入到etcd中;
3. 调度器通过list-watch的方式,发现有新的pod数据,但是这个pod还没有绑定到某一个节点中;
4. 调度器通过调度算法,计算出最适合该pod运行的节点,并调用APIServer,把信息更新到etcd中;
5. kubelet同样通过list-watch方式,发现有新的pod调度到本机的节点了,因此调用容器运行时,去根据pod的描述信息,拉取镜像,启动容器,同时生成事件信息;
6. 同时,把容器的信息、事件及状态也通过APIServer写入到etcd中;

架构设计的几点思考

  1. 系统各个组件分工明确
    • APIServer是所有请求入口
    • Controller Manager是控制中枢
    • Scheduler主管调度
    • Kubelet负责运行),配合流畅,整个运行机制一气呵成
  2. 除了配置管理和持久化组件ETCD,其他组件并不保存数据,即无状态
  3. 因为组件无状态,组件的升级,重启,故障等并不影响集群最终状态,组件恢复后从中断处继续运行
  4. 各个组件和kube-apiserver之间的数据推送都是通过list-watch机制来实现

# Pod 基本概念

# 最小调度单元 Pod

docker调度的是容器,在k8s集群中,最小的调度单元是Pod(豆荚)

# 为什么引入Pod

  • 平台设计与容器引擎的具体的实现解耦,可兼容Docker、Rkt

  • 多容器共享网络|存储|进程空间,支持的业务场景更加灵活

# Pod 编排文件

  • 前期准备
# 项目地址
https://gitee.com/nining1314/fastapi_demo

# 本地 docker build
docker build . -t fastapi:0.0.1

# 本地测试 docker 运行
docker run -it --rm  -p 80:80  fastapi:0.0.1

# 推到自制hub服务器 192.168.1.106:5000
docker tag fastapi:0.0.1 192.168.1.106:5000/fastapi:0.0.1
docker push 192.168.1.106:5000/fastapi:0.0.1
  • 新增编排文件 fastapi/one-pod/pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: fastapi
  namespace: ni-ning
  labels:
    component: fastapi
spec:
  containers:
  - name: fastapi
    image: 192.168.1.106:5000/fastapi:0.0.1
    ports:
    - containerPort: 80
apiVersion 含义
alpha 进入k8s功能的早期候选版本,可能包含bug,最终不一定进入k8s
beta 已经过测试的版本,最终会进入k8s,但功能、对象定义可能会发生变更
stable 可安全使用的稳定版本
v1 stable 版本之后的首个版本,包含了更多的核心对象
apps/v1 使用最广泛的版本,像Deployment、ReplicaSets都已进入该版本
  • 快速获得资源和版本
[root@k8s-master one-pod]# kubectl explain pod
[root@k8s-master one-pod]# kubectl explain Pod.apiVersion

# Pod 增删改查

  • 创建Pod服务
# 创建namespace, namespace是逻辑上的资源池
[root@k8s-master one-pod]# kubectl create namespace ni-ning

# 使用指定文件创建Pod
[root@k8s-master one-pod]# kubectl create -f pod.yaml

# 查看pod,可以简写po
# 所有的操作都需要指定namespace,如果是在default命名空间下,则可以省略
[root@k8s-master one-pod]# kubectl -n ni-ning get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
fastapi   1/1     Running   0          43s   10.244.0.11   k8s-master   <none>           <none>

# 使用Pod Ip访问服务 80
[root@k8s-master one-pod]# curl 10.244.0.11:80
{"Hello":"World"}

# 进入容器,执行初始化, 不必到对应的主机执行docker exec
[root@k8s-master one-pod]# kubectl -n ni-ning exec -it fastapi -c fastapi -- sh
/data/app/fastapi # python main.py
  • pause:3.4.1 容器
[root@k8s-master one-pod]# docker ps -a | grep 'fastapi'
# 发现有2个容器: fastapi程序以及Infra容器
# Pod内部共享Infra容器的网络空间(Docker网络的container模式),实现内部通信,资源消耗低

pod容器命名: k8s_<container_name>_<pod_name>_<namespace>_<random_string>

  • 查看pod详细信息
# 查看pod调度节点及pod_ip
[root@k8s-master one-pod]# kubectl -n ni-ning get pods -o wide
# 查看完整的yaml
[root@k8s-master one-pod]# kubectl -n ni-ning get po fastapi -o yaml
# 查看pod的明细信息及事件
[root@k8s-master one-pod]# kubectl -n ni-ning describe pod fastapi
  • Troubleshooting and Debugging
# 进入Pod内的容器
$ kubectl -n <namespace> exec <pod_name> -c <container_name> -ti /bin/sh

# 查看Pod内容器日志,显示标准或者错误输出日志
$ kubectl -n <namespace> logs -f <pod_name> -c <container_name>
  • 更新服务版本
[root@k8s-master one-pod]# kubectl apply -f pod.yaml
  • 删除Pod服务
# 根据文件删除
$ kubectl delete -f pod.yaml

# 根据pod_name删除
$ kubectl -n ni-ning delete pod fastapi

# Pod 数据持久化

删除Pod时,容器内部数据都会丢失,可以进行数据持久化

  • volumes:hostPath 实现方式
apiVersion: v1
kind: Pod
metadata:
  name: fastapi-v2
  namespace: ni-ning
  labels:
    component: fastapi-v2
spec:
  volumes:
    - name: log-data
      hostPath:
        path: /root/fastapi/logs
  containers:
  - name: fastapi
    image: 192.168.1.106:5000/fastapi:0.0.2
    ports:
    - containerPort: 80
    volumeMounts:
      - name: log-data
        mountPath: /data/logs/fastapi
[root@k8s-master one-pod]# kubectl create -f pod-with-volume.yaml
[root@k8s-master one-pod]# kubectl -n ni-ning get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
fastapi      1/1     Running   0          115m   10.244.0.13   k8s-master   <none>           <none>
fastapi-v2   1/1     Running   0          23s    10.244.0.15   k8s-master   <none>           <none>


[root@k8s-master logs]# curl 10.244.0.15
{"Hello":"World"}
[root@k8s-master logs]# cat access.log
10.244.0.1:50582 - "GET / HTTP/1.1" 200

# 删除再创建
[root@k8s-master one-pod]# kubectl delete -f pod-with-volume.yaml
[root@k8s-master one-pod]# kubectl create -f pod-with-volume.yaml
[root@k8s-master one-pod]# kubectl -n ni-ning get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
fastapi      1/1     Running   0          118m   10.244.0.13   k8s-master   <none>           <none>
fastapi-v2   1/1     Running   0          7s     10.244.0.16   k8s-master   <none>           <none>

# 查看旧日志信息仍然存在
[root@k8s-master logs]# curl 10.244.0.16
{"Hello":"World"}
[root@k8s-master logs]# cat access.log
10.244.0.1:50582 - "GET / HTTP/1.1" 200
10.244.0.1:50706 - "GET / HTTP/1.1" 200
  • 使用ceph等分布式存储解决方案

# Pod 健康检查

  • 不设置,默认Pod是健康的
  • 设置时,两种机制
    • LivenessProbe探针 存活性探测: 用于判断容器是否存活,即Pod是否为running状态
    • ReadinessProbe探针 可用性探测:用于判断容器是否正常提供服务,即容器的Ready是否为True
  containers:
  - name: fastapi
    image: 192.168.1.106:5000/fastapi:0.0.3
    livenessProbe:
      httpGet:
        path: /
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10   # 容器启动后第一次执行探测是需要等待多少秒
      periodSeconds: 10         # 执行探测的频率
      timeoutSeconds: 2         # 探测超时时间
    readinessProbe:
      httpGet:
        path:
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10
      timeoutSeconds: 2
      periodSeconds: 10
[root@k8s-master one-pod]# kubectl create -f pod-with-probe.yaml
[root@k8s-master one-pod]# kubectl -n ni-ning get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
fastapi      1/1     Running   0          147m   10.244.0.13   k8s-master   <none>           <none>
fastapi-v2   1/1     Running   0          28m    10.244.0.16   k8s-master   <none>           <none>
fastapi-v3   1/1     Running   0          71s    10.244.0.17   k8s-master   <none>           <none>
  • 三种探测类型
    • exec:通过执行命令来检查服务是否正常,返回值为0则表示容器健康
    • httpGet方式:通过发送http请求检查服务是否正常,返回200-399状态码则表明容器健康
    • tcpSocket:通过容器的IP和Port执行TCP检查,如果能够建立TCP连接,则表明容器健康

# Pod 重启策略

当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置来进行相应的操作

- Always:当容器进程退出后,由kubelet自动重启该容器,默认值;
- OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器;
- Never:不论容器运行状态如何,kubelet都不会重启该容器。

演示重启策略:

apiVersion: v1
kind: Pod
metadata:
  name: test-restart-policy
spec:
  restartPolicy: Always
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 10

# Pod 拉取策略

spec:
  containers:
  - name: fastapi
    image: 192.168.1.106:5000/fastapi:0.0.1
    imagePullPolicy: IfNotPresent
  • Always,总是拉取镜像,即使本地有镜像也从仓库拉取
  • IfNotPresent ,本地有则使用本地镜像,本地没有则去仓库拉取
  • Never,只使用本地镜像,本地没有则报错

# Pod 资源限制

对于一个Pod来说,资源最基础的2个的指标就是:CPU和内存

  • requests 容器使用的最小资源需求,作为调度判断依据
  • limits 容器能使用资源的最大值
  containers:
  - name: fastapi
    image: 192.168.1.106:5000/fastapi:0.0.1
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: 100Mi
        cpu: 50m
      limits:
        memory: 500Mi
        cpu: 100m
1 CPU =  1000 millicpu(1 Core = 1000m)

# Pod 秘钥信息

k8s提供两类资源 ConfigMap和Secret

具体实现略

# Pod 生命周期

  • Pod的状态如下表所示
状态值 描述
Pending API Server已经创建该Pod,等待调度器调度
ContainerCreating 拉取镜像启动容器中
Running Pod内容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态
Succeeded|Completed Pod内所有容器均已成功执行退出,且不再重启
Failed|Error Pod内所有容器均已退出,但至少有一个容器退出为失败状态
CrashLoopBackOff Pod内有容器启动失败,比如配置文件丢失导致主进程启动失败
Unknown 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致
  • 生命周期示意图

  • 启动和关闭示意

  • 初始化容器
    • 验证业务应用依赖的组件是否均已启动
    • 修改目录的权限
    • 调整系统参数
initContainers:
- command:
  - /sbin/sysctl
  - -w
  - vm.max_map_count=262144
  image: alpine:3.6
  imagePullPolicy: IfNotPresent
  name: elasticsearch-logging-init
  resources: {}
  securityContext:
    privileged: true
- name: fix-permissions
  image: alpine:3.6
  command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
  securityContext:
    privileged: true
  volumeMounts:
  - name: elasticsearch-logging
    mountPath: /usr/share/elasticsearch/data
  • 验证Pod生命周期
apiVersion: v1
kind: Pod
metadata:
  name: pod-lifecycle
  namespace: ni-ning
  labels:
    component: pod-lifecycless
spec:
  initContainers:
  - name: init
    image: busybox
    command: ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing']
    volumeMounts:
    - mountPath: /loap
      name: timing
  containers:
  - name: main
    image: busybox
    command: ['sh', '-c', 'echo $(date +%s): START >> /loap/timing;
sleep 10; echo $(date +%s): END >> /loap/timing;']
    volumeMounts:
    - mountPath: /loap 
      name: timing
    livenessProbe:
      exec:
        command: ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing']
    readinessProbe:
      exec:
        command: ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing']
    lifecycle:
      postStart:
        exec:
          command: ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing']
      preStop:
        exec:
          command: ['sh', '-c', 'echo $(date +%s): PRE-STOP >> /loap/timing']
  volumes:
  - name: timing
    hostPath:
      path: /tmp/loap
  • 创建pod测试
$ kubectl create -f pod-lifecycle.yaml

## 查看demo状态
$ kubectl -n ni-ning get po -o wide -w

## 查看调度节点的/tmp/loap/timing
$ cat /tmp/loap/timing
1585424708: INIT
1585424746: START
1585424746: POST-START
1585424754: READINESS
1585424756: LIVENESS
1585424756: END
上次更新: 11/21/2022, 2:36:58 PM