第八课 Kubernetes生产级实践-k8s服务调度编排和Pod深入讲解

第八课 Kubernetes生产级实践-k8s服务调度编排和Pod深入讲解

tags:

  • k8s
  • 慕课网

categories:

  • 健康检查
  • 调度器
  • 污点容忍
  • 部署策略
  • Pod深入
  • 投射数据卷
  • Secret
  • ConfigMap
  • DownwardAPI

第一节 健康检查

1.1 健康检查-通过Command方式

  1. 在没有健康检查时默认:入口程序不退出那么k8s就认为是正常的。容器中pid为1的程序或者
    ENTRYPOINT指定的程序就是入口程序。显然这种健康方式过于简单。
  2. 例子, 健康检查时容器级别的:
    • initialDelaySeconds: 等待容器启动10秒之后再执行健康检查的命令
    • periodSeconds: 每隔10秒检查一次
    • failureThreshold: 失败的门槛,失败2次我认为它失败了 进行容器重启
    • successThreshold: 从错误到正确只需要通过1次
    • timeoutSeconds: 每次执行健康检查命令时 最长等待时间5秒
    spec:
      containers:
      - name: web-demo
        image: 192.168.242.130/k8s/web:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          exec:
            command:
            - /bin/sh
            - -c
            - ps -ef|grep java|grep -v grep
          initialDelaySeconds: 10
          periodSeconds: 10
          failureThreshold: 2
          successThreshold: 1
          timeoutSeconds: 5
  1. 测试一下。
kubectl apply -f web-dev-cmd.yaml
kubectl get pods -n dev
kubectl exec -it  -n dev bash
kubectl exec -it web-demo-79cf5b99c8-gx2s7  -n dev bash
# 查看进程 手动健康检查命令
ps -ef
ps -ef|grep java|grep -v grep
# 查看命令结果
echo $?
# 容器内强制杀掉进程tail -f /usr/local/tomcat/logs/catalina.out
kill -9 13
# 发现容器重启了一次
kubectl get pods -n dev

1.2 健康检查-通过HTTP方式

  1. path: 应用要访问的路径
  2. port: 容器本身的真实端口
  3. 返回200正常,其他返回失败
   spec:
      containers:
      - name: web-demo
        image: 192.168.242.130/k8s/web:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /examples/index.html
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 5
          periodSeconds: 5
          failureThreshold: 1
          successThreshold: 1
          timeoutSeconds: 5
  1. 测试一下
kubectl get pods -n dev
# 发现pod在不断的重启 重启原因是http检测超时
kubectl describe pod web-demo-577b5d88f6-x5fts -n dev
# 上面initialDelaySeconds:10 时间设置长一点呀 要不容器没启动你就检查 能检查到吗
# failureThreshold也要多给两次机会

1.3 健康检查-通过TCP方式

  1. 补充就绪探针, 例子:
    spec:
      containers:
      - name: web-demo
        image: 192.168.242.130/web:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 2
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 2
          successThreshold: 1
          timeoutSeconds: 5
  1. 测试一下。
kubectl create -f web-dev-tcp.yaml
kubectl get pods -n dev -o wide
# readinessProbe 决定下面的available字段 可以提供服务
kubectl get deploy -n dev -o wide
  1. 这里有一些工作中的策略。
    • 如果restart频率比较低,几天几十天一次。不让它重启,保留上下文去解决问题。
    • 如果是稳定复现的问题。去掉livenessProbe,让它一直成功。进入环境当成沙箱环境,先排查问题。

第二节 POD的调度策略

2.1 调度流程

在这里插入图片描述

  1. ApiServer向ETCD数据中心交互。优先级队列用于存储等待调度的优先级列表。Informer用来监听ApiServer的变化。
# nodeName这个字段,在pod刚刚创建时是不存在的只有在调度器调度后,它才会把字段加上去。
kubectl get pod web-demo-79cd8f9f4-bqp72 -n dev -o yaml
  1. 如果每次调度pod就去ApiServer请求信息,性能必然会差。k8s设计了一个Cache, 它从ApiServer取信息保存起来
  2. **预选策略,**初步过滤掉不符合的节点。剩余内存,cpu,端口,volume类型,nodeSeletor必须匹配。
  3. 优选策略,上一步筛选的node进行评分。
  4. 然后pod和Node进行绑定,把绑定信息告诉ApiServer。然后更新pod的那个字段nodeName
  5. 然后指派那个节点的kubelet把服务调度起来

2.2 Scheduler节点调度的配置

  1. affinity亲和性。nodeAffinity,节点亲和性
  2. requiredDuringSchedulingIgnoredDuringExecution:必须满足下面条件才能调度
  3. nodeSelectorTerms: 节点的选择策略。多个nodeSelectorTerms 是或的关系
  4. matchExpressions:如果有多个,是并且的关系
  5. key: 节点的label名字
  6. preferredDuringSchedulingIgnoredDuringExecution: 最好选择那些节点
  7. weight: 1 最好的权重
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: beta.kubernetes.io/arch
                operator: In
                values:
                - amd64
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: disktype
                operator: NotIn
                values:
                - ssd
  1. 运行测试
kubectl apply -f web-dev-node.yaml
kubectl get pods -n dev -o wide
# 因为最好不在ssd标签的节点上 所以选择s1
kubectl get nodes --show-labels

2.3 pod调度的配置

      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-demo
            topologyKey: kubernetes.io/hostname
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - web-demo-node
              topologyKey: kubernetes.io/hostname

  1. pod的亲和性。 podAffinity, pod想和那些pod运行在一起,不想和某些pod运行在一起
  2. 上面例子说:要跟app=web-demo的pod运行在一同一节点
  3. topologyKey 对应节点上label的名字
  4. 更好的话:跟app=web-demo-node的pod运行在同一节点
  5. podAntiAffinity 反亲和性调度
  6. 运行测试
kubectl apply -f web-dev-pod.yaml
kubectl get pods -n dev -o wide

2.4 污点和污点容忍

  1. 给节点打上一个污点
    • 调度效果:NoSchedule 节点不会把pod调度到这
    • 调度效果:PreferNoSchedule 节点最好不把pod调度到这
    • 调度效果:NoExcute 除了不调度之外 调度了的也给你干掉
  2. 如果没有配置污点容忍,pod不会部署到该节点上的。
kubectl taint nodes s1 gpu=true:NoSchedule
# 删除污点
kubectl taint nodes s1 gpu=true:NoSchedule-
  1. 污点容忍例子:
spec:
      containers:
      - name: web-demo-taint
        image: 192.1687.242.130/k8s/web:v1
        ports:
        - containerPort: 8080
      #第一种表达方式,effect可选值:NoSchedule、NoExecute
      tolerations:
      - key: "gpu"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"
      #第二种表达方式
      #tolerations:
      #- key: "key"
      #  operator: "Exists"
      #  effect: "NoSchedule"
  1. tolerations污点容忍,gpu=true。 effect一定要配置上

第三节 部署策略实践

3.1 部署策略

  1. 滚动跟新:Rolling update, 之前修改yaml后重新apply 就是这种方式
  2. 重新创建 : Recreate,先停止旧的服务 在启动新服务
  3. 蓝绿部署: 利用Service的Selector选择不同版本的服务
  4. 金丝雀部署: 通过Ingress,轮询访问不同的服务。之前刷新一下,变一下就是这个
  5. 前面两种是k8s支持的不同的重启策略,后面两种是我们利用了Service的特征结合Deployment完成的部署方式。

3.2 部署实践-重新创建

  1. Recrete 重新创建
strategy:
    type: Recreate
  1. Recrete测试一下
# 使用场景不多 更新服务过程中 服务会断掉的 或许在资源不太充足的场景下 每个节点只能有一个实例时使用
kubectl apply -f web-recreate.yaml
kubectl delete -f web-recreate.yaml

3.3 部署实践-滚动部署

  1. 滚动部署 Rolling update
    • maxSurge 最大可以超出服务实例数的百分比 比如有四个实例:我们最多可以多启动1个实例
    • maxUnavailable 最大不可用的实例数百分比 有四个实例 必须三个实例可用才行
spec:
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  1. 测试下滚动部署
kubectl apply -f web-rollingupdate.yaml 
kubectl get pods -n dev
# 修改镜像从web到spring-web 滚动更新
kubectl apply -f web-rollingupdate.yaml 
# 暂停更新
kubectl rollout pause deploy web-rollingupdate -n dev
# 开个窗口一直访问服务 可以观察到新版本到旧版本的过程
while sleep 0.2;do curl "http://web-rollingupdate.mooc.com/hello?name=michael";echo "";done
# 继续更新
kubectl rollout resume deploy web-rollingupdate -n dev
# 发现这个版本不适用 回滚到上一个版本 undo 就可以
kubectl rollout undo deploy web-rollingupdate -n dev

3.4 部署实践-蓝绿部署

 template:
    metadata:
      labels:
        app: web-bluegreen
        version: v1.0
  1. 这里多了个version字段。通过这个字段来控制版本。
  2. 蓝绿部署的过程。
# 1. 第一步 部署1.0版本的应用
kubectl apply -f web-bluegreen.yaml
kubectl apply -f bluegreen-service.yaml
# 2. 第二部 部署2.0版本的web-bluegreen.yaml
# 这里修改镜像springboot-web变为web version: v2.0 name: web-bluegreen-v2 这三个地方
kubectl apply -f web-bluegreen.yaml
# 3. 第三部 pod都运行起来后 只需修改bluegreen-service.yaml切换流量.把1.0改成2.0
# 效果没有过渡的痕迹,直接切换流量。使用于新版本线上测试完成后删除旧版本
kubectl apply -f bluegreen-service.yaml
while sleep 0.2;do curl "http://web-bluegreen.mooc.com/hello?name=michael";echo "";done

3.5 部署实践-金丝雀部署

  1. 在蓝绿部署的继承上简单修改Selector就变成了金丝雀部署。
  2. 只要把bluegreen-service.yaml中的version字段去掉即可。
  3. 所有的pod都会被轮训交替访问。同时访问两个或者多个版本。
  4. 比如我新开发个功能,但是我不确定是否好用,可以给一些pod让别人尝试。也是人常说的AB测试。

第四节 Pod的深入学习

4.1 Pod的设计

  1. Pod是k8s最小的调度单位。
  2. 本质上还是容器的隔离,pod是一个逻辑的概念,物理机上没有一个真实的东西叫pod。pod的本质是共享了network,namespace同一个volume。
# docker这种指定方式对容器的启动顺序是有要求的 容器间不是一种对等的关系
docker run --net=xxX --volumes-from=
  1. Pause容器。不需要我们显式声明每个pod都会有。

4.2 Pod的资源学习

  1. volume资源挂载。它的定义是pod层面的。把目录/shared-volume-data挂载到容器的/shared-dubbo和/shared-web中。
apiVersion: v1
kind: Pod
metadata:
  name: pod-volume
spec:
  hostNetwork: true
  hostPID: true
  hostAliases:
    - ip: "192.168.242.130"
      hostnames: 
      - "web.mooc.com"
  containers:
  - name: web
    image: 192.168.242.130/k8s/web:v1
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: shared-volume
      mountPath: /shared-web
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo web starting... >> /var/log/message"]
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo web stoping... >> /var/log/message && sleep 3"]
  - name: dubbo
    env:
    - name: DUBBO_PORT
      value: "20881"
    image: 192.168.242.130/k8s/dubbo:v1
    ports:
    - containerPort: 20881
      hostPort: 20881
      protocol: TCP
    volumeMounts:
    - name: shared-volume
      mountPath: /shared-dubbo
  volumes:
  - name: shared-volume
    hostPath:
      path: /shared-volume-data
  1. 测试一下 。POD级别参数
    • hostAliases
    • hostPID
    • hostNetwork
  2. 容器级别参数
    • lifecycle
      • postStart 注意下 容器执行ENTRYPOINT 命令时 同时执行它 并行执行
      • preStop 停止之前做的事 串行执行 它执行完在停止容器。有个超时
kubectl create -f pod-volume.yaml 
kubectl get pods -o wide
# 进入到容器中查看挂载目录 随便创建个文件 touch qnhyn.sh
docker ps | grep volume
docker exec -it 65fdf2ef2b36 bash
# 到挂载目录
docker exec -it 3b886defe598 bash
# 这两个容器的hosts文件一样 它是由pod给我们统一管理的
docker exec -it 65fdf2ef2b36 cat /etc/hosts
docker exec -it 3b886defe598 cat /etc/hosts
# 如何自定义自己的hosts文件 通过如下定义hostAliases
# 是否使用宿主机的网络 hostNetwork: true
# 是否使用宿主机的进程空间: hostPID: true
apiVersion: v1
kind: Pod
metadata:
  name: pod-volume
spec:
  hostNetwork: true
  hostPID: true
  hostAliases:
    - ip: "192.168.242.130"
      hostnames: 
      - "web.mooc.com"

# pod的字段修改和deployment不一样 很多字段不能直接修改 所以应用时先删除再创建
kubectl delete  -f pod-volume.yaml
kubectl create  -f pod-volume.yaml
# 测试一下
docker exec -it 4d1995a2f5b0 cat /etc/hosts
docker exec -it 4d1995a2f5b0 sh
ps -ef # 发现很多进程 我们使用了宿主机的进程空间 
netstat -ntlp # 发现很多监听 我们使用了宿主机的network namespace
tail -f /var/log/message
  1. 容器的状态:
    • Pendding 还没有被调度的的状态。如果长时间这个状态可能资源不足,镜像没下载完等
    • containerCreating 被调度了 等待容器创建
    • Running
    • Succeeded && Failed
    • Ready 通过健康检查
    • CrashLoopBackOff 如果没有通过健康检查的话 等待时间过长则一直处于启动失败中
    • UnKnown 未知状态 kubelet和apiserver通讯的问题

4.3 Pod的投射数据卷ProjectedVolume介绍

  1. 投射数据卷ProjectedVolume: 它是轻量级的volume,通过apiServer投射到pod中的。比如:知道pod需要什么文件,在启动时给你扔过来。
  2. 常用的三种使用方式:
    • Secret
    • ConfigMap
    • DownwardAPI
kubectl get secret
# 查看它的具体定义 发现数据都是base64加密的 type: kubernetes.io/service-account-token 
kubectl get secret default-token-bvh9v -o yaml
# k8s实际上会把serviceaccount自动加入到每一个pod中 随便一个pod进行查看
kubectl get pods pod-volume -o yaml
# 里面有volume.secretName: default-token-bvh9v 挂载到/var/run/secrets/kubernetes.io/serviceaccount 文件夹下 可以到容器中查看

4.4 Secret创建和应用

  1. 创建一个secret Opaque是不透明的意思
apiVersion: v1
kind: Secret
metadata:
  name: dbpass
type: Opaque
data:
  username: aW1vb2M=
  passwd:  aW1vb2MxMjM=
# base64加密 aW1vb2M=
echo -n imooc|base64
# 把它写到etcd中
kubectl apply -f secret.yaml
  1. Pod使用上面的Secret
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
spec:
  containers:
  - name: springboot-web
    image: 192.168.242.130/k8s/springboot-web:v1
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: db-secret
      mountPath: /db-secret
      readOnly: true
  volumes:
  - name: db-secret
    projected:
      sources:
      - secret:
          name: dbpass
  1. 测试用下。可以动态修改容器中的密码
kubectl apply -f pod-secret.yaml
kubectl get pod -o wide
# 到执行机上
docker ps | grep secret
docker exec -it 6ac784681b7a sh
cd db-secret/
# 这个用户名密码可以用在数据库
cat username
cat passwd
# 如果这个用户名密码错了 直接修改secret.yaml重新应用即可
# pod上会延迟自动更新

4.5 ConfigMap创建和应用

  1. 它和secret的应用和部署非常相似,只是它存储的数据不需要加密。比如一些启动参数,参数的配置等等。
  2. 创建ConfigMap
# 从配置文件game.properties创建ConfigMap
kubectl create configmap web-game --from-file game.properties
# ConfigMap简写cm
kubectl get cm web-game -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-game
spec:
  containers:
  - name: web
    image: 192.168.242.130/k8s/springboot-web:v1
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: game
      mountPath: /etc/config/game
      readOnly: true
  volumes:
  - name: game
    configMap:
      name: web-game
  1. 测试使用下
kubectl apply -f pod-game.yaml
# 节点上
docker ps | grep game
docker exec -it 17b6fadf3f4f sh
cd /etc/config/game/
cat -n game.properties
# 试下是否可以直接修改
kubectl edit cm web-game
# 每隔5秒看下配置 发现很快就修改完了
watch -n 5 cat game.properties
  1. 另外的方式使用。通过yaml定义配置, 通过环境变量的方式使用
apiVersion: v1
kind: ConfigMap
metadata:
  name: configs
data:
  JAVA_OPTS: -Xms1024m
  LOG_LEVEL: DEBUG
  1. 环境变量方式使用。env环境变量
apiVersion: v1
kind: Pod
metadata:
  name: pod-env
spec:
  containers:
  - name: web
    image: 192.168.242.130/k8s/springboot-web:v1
    ports:
    - containerPort: 8080
    env:
      - name: LOG_LEVEL_CONFIG
        valueFrom:
          configMapKeyRef:
            name: configs
            key: LOG_LEVEL
  1. 测试使用下
kubectl apply -f pod-env.yaml 
# 节点上
docker ps | grep env
docker exec -it d0a2c845503f sh
env | grep LOG
  1. 还可以把环境变量应用到我们的启动命令中
apiVersion: v1
kind: Pod
metadata:
  name: pod-cmd
spec:
  containers:
  - name: web
    image: 192.168.242.130/k8s/springboot-web:v1
    command: ["/bin/sh", "-c", "java -jar /springboot-web.jar -DJAVA_OPTS=$(JAVA_OPTS)"]
    ports:
    - containerPort: 8080
    env:
      - name: JAVA_OPTS
        valueFrom:
          configMapKeyRef:
            name: configs
            key: JAVA_OPTS

4.6 DownwardAPI创建和应用

  1. DownwardAPI的作用:是可以让我们在程序中可以取到Pod对象本身的相关信息
apiVersion: v1
kind: Pod
metadata:
  name: pod-downwardapi
  labels:
    app: downwardapi
    type: webapp
spec:
  containers:
  - name: web
    image: 192.168.242.130/k8s/springboot-web:v1
    ports:
    - containerPort: 8080
    volumeMounts:
      - name: podinfo
        mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels
              - path: "name"
                fieldRef:
                  fieldPath: metadata.name
              - path: "namespace"
                fieldRef:
                  fieldPath: metadata.namespace
              - path: "cpu-request"
                resourceFieldRef:
                  containerName: web
                  resource: limits.memory
  1. 测试使用下
kubectl apply -f pod-downwardapi.yaml
docker ps | grep downward
docker exec -it 6e66ce799088  sh
cd /etc/podinfo && ls
cat -n labels
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值