Kubernetes 技术分享

Summary: Author: 张亚飞 | Read Time: 8 minute read | Published: 2020-10-12
Filed under Categories: MarkDownTags: Tag,

Kubernetes: 容器调度与编排管理平台

Kubernetes 被设计作为构建组件和工具的生态系统平台,提供了很多的功能,以便更轻松地部署、扩展和管理应用程序,有较强的自动化能力.它可以简化应用程序的工作流,加快开发速度.

传统开发运维常见问题

各服务器部署环境不一致,开发环境测试通过后上线运行报错.系统环境需要单独维护和升级,增加运维的工作量;

微服务架构的优点

  • 单体式架构

各业务使用相同的技术栈,难以快速迭代应用新技术; 通常采用分层架构模式,按不同技术维度分层,例如数据持久化层、业务逻辑层、UI表示层; 对任何功能的修改都要整个系统部署维护,运维成本高;(牵一发而动全身) 系统出现一个问题,整个系统受影响; 系统应用负载高,难以分段水平扩展;

  • 微服务架构

相对于单体式架构,微服务更轻量,它将复杂的应用拆分到不同的独立自治的服务单元,服务与服务之间采用松耦合的形式交互,有很好的扩展性和复用性;

微服务平台

Kubernetes 是谷歌开源的容器调度与编排管理系统,正迅速成为在分布式系统中部署工作负载的事实标准;

主要功能包括:

  • 跨机器和跨地区的集群调度
  • 集群内负载均衡和服务发现
  • 适合灰度发布
  • 基于容器的应用部署、维护和滚动升级
  • 支持弹性伸缩
  • 故障隔离
  • 服务熔断
  • 健康检查

其它容器管理框架SwarmRancher,Rancher定位k8s上层可以管理k8s集群的编排工具


开放接口

Kubernetes 作为云原生应用的的基础调度平台,相当于云原生的操作系统,为了便于系统的扩展,Kubernetes 中开放的以下接口,可以分别对接不同的后端,来实现自己的业务逻辑:

  • CRI(Container Runtime Interface):容器运行时接口,提供计算资源
  • CNI(Container Network Interface):容器网络接口,提供网络资源
  • CSI(Container Storage Interface):容器存储接口,提供存储资源

核心组件

  • kube-apiserver

模块之间的数据交互和通信的枢纽:提供集群管理的 REST API 接口,提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;

  • kube-scheduler

负责资源的调度,它监听 kube-apiserver,按照预定的调度策略将 Pod 调度到相应的机器上;

  • kube-controller-manager

是 Kubernetes 的大脑,它通过 apiserver 监控整个集群的状态,负责维护集群的状态,比如故障检测、自动扩展、滚动更新等,并确保集群处于预期的工作状态;

  • Kubelet

每个 Node 节点上都运行一个 Kubelet 服务进程,在 API Server 上注册所在Node节点的信息,定期向 Master 节点汇报该节点的资源使用情况, 接收并执行 Master 发来的指令,管理 PodPod 中的容器,并监控节点和容器的资源.

  • kube-proxy

每台机器上都运行一个 kube-proxy 服务,它监听 API serverserviceendpoint 的变化情况,并通过 iptables 等来为服务配置负载均衡. 负责为 Service 提供 cluster 内部的服务发现和负载均衡;

  • kube-dns

提供集群中的 dns 服务,可以解析 servicecluster ip,实现服务发现

  • etcd

分部式数据库,保存了整个集群的配置及状态;


常用组件

  • Namespace

通过命名空间,为集群中的部分资源或不同的环境关联鉴权,配置资源限额,有助于不同的项目、团队共享 Kubernetes 集群.

  • Pod

Kubernetes 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器.

  • Deployment

Deployment 通过 ReplicaSet 管理创建 Pod,可以实现指定 Pod 副本数量,滚动更新,扩容 Pod

  • DaemonSet

在每个节点都需要运行的 Pod,例如 filebeat 日志采集服务

  • StatefulSet

有状态服务,部署、扩展、更新、删除都要有顺序,每个 Pod 都有自己存储和状态

  • Service

集群内负载均衡与服务发现

  • Ingress

定义反向代理规则,用来规定 HTTP/S 请求应该被转发到哪个 Service 上,比如根据请求中不同的 Hosturl 路径让请求落到不同的 Service 上,暴露集群内部 Service 服务

  • Job

一次性任务

  • CronJob

定时任务

  • PersistentVolume

持久化存储卷,作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息

  • PersistentVolumeClaim

持久化卷声明

  • StorageClass

使用模板动态创建 PVPVC,适用于有状态服务 StatefulSet

  • Label

Label 是识别 Kubernetes 对象的标签,作为服务资源绑定

其它

  • Secret
  • LoadBalancer
  • ConfigMap

常用命令及操作

可以通过官方提供的工具 kubectl 对集群资源进行管理

kubectl describe namespaces
kubectl get node,pod
kubectl logs pod test-xxx
kubectl create -f xxx.yaml

使用命令创建一个 Deployment

kubectl create deployment --image nginx:1.10 test-web-deployment

也可以使用声明式的配置,以下等价

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-web-deployment
spec:
  selector:
    matchLabels:
      app: test-web-deployment
  replicas: 1
  template:
    metadata:
      labels:
        app: test-web-deployment
    spec:
      containers:
        - name: nginx
          image: nginx:1.10
          ports:
            - containerPort: 80

创建 Service 服务发现

kubectl expose deployment test-web-deployment --port=80 --type=LoadBalancer

以下配置等价

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: LoadBalancer
  ports:
    - name: http
      targetPort: 80
      protocol: TCP
  selector:
    app: test-web-deployment

测试

http HEAD http://ms.iirii.com:30066

调整副本集数量

kubectl scale deployment --replicas 2 test-web-deployment

修改容器镜像

kubectl set image deployment/test-web-deployment nginx=nginx:1.11

删除 Deployment

kubectl delete deployment test-web-deployment

功能演示

  1. 创建节点并加入集群
cargo run tcs create -i t.cs.11
cargo run tcs drop -i t.cs.11

  1. 创建 Deployment 服务容器
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-web-deployment
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: test-web-deployment
  replicas: 10
  template:
    metadata:
      labels:
        app: test-web-deployment
    spec:
      containers:
        - name: test-web
          image: nginx:1.10
          ports:
            - containerPort: 80

  1. 创建服务
apiVersion: v1
kind: Service
metadata:
  name: test-web-svc
spec:
  type: NodePort
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: 80
      nodePort: 30044
  selector:
    app: test-web-deployment

测试

curl -s -I http://ms.iirii.com:30044

  1. 滚动更新

升级 nginx 版本号

kubectl apply -f k8s.run.yaml --record

循环验证

while sleep 0.2;do curl -s -I http://ms.iirii.com:30044 | grep server;done

  1. 回滚

查看历史版本

kubectl rollout history deployment test-web-deployment

执行回滚

kubectl rollout undo deployment test-web-deployment
kubectl rollout undo deployment test-web-deployment --to-revision=1

循环验证

while sleep 0.2;do curl -s -I http://ms.iirii.com:30044 | grep server;done

  1. 水平伸缩 HPA

创建 HorizontalPodAutoscaler 对象或使用 kubectl autoscale 子命令都可以配置 HPA 控制器,以管理工作负载

创建一个服务

  • Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-scale-deployment
spec:
  selector:
    matchLabels:
      app: test-scale-deployment
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  replicas: 1
  template:
    metadata:
      labels:
        app: test-scale-deployment
    spec:
      containers:
        - name: test-scale
          image: registry.ioros.com/coam/test.web:0.0.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 50Mi
  • Service
apiVersion: v1
kind: Service
metadata:
  name: test-scale-svc
spec:
  type: NodePort
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: 80
      nodePort: 30077
  selector:
    app: test-scale-deployment
  • HorizontalPodAutoscaler
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: test-scale-hpa
  namespace: default
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: test-scale-deployment
  targetCPUUtilizationPercentage: 30

监控 Pod 资源占用

watch -n 1 kubectl top pod
watch -n 1 kubectl get hpa

模拟并发请求

while true; do wget -q -O- http://ms.iirii.com:30077; done

  1. 健康检查
  • 执行命令检查
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
  labels:
    test: liveness
spec:
  containers:
    - name: liveness
      image: registry.ioros.com/docker.io/busybox
      imagePullPolicy: IfNotPresent
      args:
        - /bin/sh
        - -c
        - echo Exec-Started:$(date '+%d/%m/%Y %H:%M:%S'); touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
      livenessProbe:
        exec:
          command:
            - cat
            - /tmp/healthy
        initialDelaySeconds: 5
        periodSeconds: 5

我们使用存活探针 livenessProbe 每隔 3 秒执行一次上面的 cat /tmp/healthy 命令

  • httpGet 请求检测
apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
  labels:
    test: liveness
spec:
  containers:
    - name: liveness
      image: registry.ioros.com/docker.io/busybox
      imagePullPolicy: IfNotPresent
      args:
        - /bin/sh
        - -c
        - echo Exec-Started:$(date '+%d/%m/%Y %H:%M:%S'); touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
      livenessProbe:
        httpGet:
          host: w.nocs.cn
          path: /pi.php
          port: 443
          scheme: HTTPS
        initialDelaySeconds: 10
        periodSeconds: 3

我们使用存活探针 livenessProbe 每隔 3 秒请求一次 https://w.nocs.cn:443/pi.php


服务网格 Istio

  • 开启自动注入
kubectl label namespace default istio-injection=enabled

准备环境

部署两个版本的应用 flask(v1,v2)

istio/flask.istio.yaml

apiVersion: v1
kind: Service
metadata:
  name: flaskapp
  labels:
    app: flaskapp
spec:
  selector:
    app: flaskapp
  ports:
    - name: http
      port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flaskapp-v1
spec:
  selector:
    matchLabels:
      app: flaskapp
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: flaskapp
        version: v1
      annotations:
        sidecar.istio.io/inject: "true"
    spec:
      containers:
        - name: flaskapp
          image: registry.ioros.com/docker.io/dustise/flaskapp
          imagePullPolicy: IfNotPresent
          env:
            - name: version
              value: v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flaskapp-v2
spec:
  selector:
    matchLabels:
      app: flaskapp
      version: v2
  replicas: 1
  template:
    metadata:
      labels:
        app: flaskapp
        version: v2
      annotations:
        sidecar.istio.io/inject: "true"
    spec:
      containers:
        - name: flaskapp
          image: registry.ioros.com/docker.io/dustise/flaskapp
          imagePullPolicy: IfNotPresent
          env:
            - name: version
              value: v2

部署客户端应用 sleep

istio/sleep.istio.yaml

apiVersion: v1
kind: Service
metadata:
  name: sleep
  labels:
    app: sleep
spec:
  selector:
    app: sleep
  ports:
    - name: ssh
      port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  selector:
    matchLabels:
      app: sleep
  replicas: 1
  template:
    metadata:
      labels:
        app: sleep
      annotations:
        sidecar.istio.io/inject: "true"
    spec:
      containers:
        - name: sleep
          image: registry.ioros.com/docker.io/dustise/sleep
          imagePullPolicy: IfNotPresent

测试,v1,v2交替出现

sleep

for i in `seq 100`;do http --body http://flaskapp/env/version;done

  1. 蓝绿发布

两套环境,蓝色验证,绿色线上

设置目标规则,根据应用 label: version 分别设置2个子集 v1,v2

flaskapp-destinationrule.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: flaskapp
spec:
  host: flaskapp.default.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

默认路由: 定义一个 VirtualService 对象,负责接管 flaskapp 流量,并将请求发送到 v2 的子集

flaskapp-virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: flaskapp
spec:
  hosts:
    - flaskapp.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v1

测试,仅 v1 版本

for i in `seq 100`;do http --body http://flaskapp/env/version;done

  1. 流量拆分与迁移

修改权重比率

flaskapp-virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: flaskapp
spec:
  hosts:
    - flaskapp.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v1
          weight: 70
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v2
          weight: 30

验证

for i in `seq 100`;do http --body http://flaskapp/env/version;done
for i in `seq 100`;do http --body http://flaskapp/env/version;done | awk -F"v1" '{print NF-1}'

  1. 金丝雀部署(灰度发布)

可以添加 match 根据 headerscheme 等信息分流

flaskapp-virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: flaskapp
spec:
  hosts:
    - flaskapp.default.svc.cluster.local
  http:
    - match:
        - headers:
            lab:
              exact: canary
      route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v2
    - route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v1

测试

http --body http://flaskapp.default.svc.cluster.local/env/version
http --body http://flaskapp.default.svc.cluster.local/env/version lab:canary
http --body url=http://flaskapp.default.svc.cluster.local/env/version lab:other

  1. 流量复制

flaskapp-virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: flaskapp
spec:
  hosts:
    - flaskapp.default.svc.cluster.local
  http:
    - match:
        - headers:
            lab:
              exact: canary
      route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v2
      mirror:
        host: flaskapp.default.svc.cluster.local
        subset: v1
    - route:
        - destination:
            host: flaskapp.default.svc.cluster.local
            subset: v1

测试

kube logs flaskapp-v1
kube logs flaskapp-v2
http --body http://flaskapp/env/version

  1. 服务熔断

部署测试环境

httpbin.yaml

apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
    - name: http
      port: 8000
      targetPort: 80
  selector:
    app: httpbin

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  selector:
    matchLabels:
      app: httpbin
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
        - image: registry.ioros.com/docker.io/kennethreitz/httpbin
          imagePullPolicy: IfNotPresent
          name: httpbin
          ports:
            - containerPort: 80

httpbin.virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: httpbin.default.svc.cluster.local

测试

http http://httpbin:8000/get
wrk -c 3 -t 3 http://httpbin:8000/ip

配置熔断规则

httpbin.destinationrule.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin.default.svc.cluster.local
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 1
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1

测试

wrk -c 3 -t 3 http://httpbin.default.svc.cluster.local:8000/ip

其它

Traefik: Ingress Controller 反向代理及负载均衡 RBAC: 鉴权 Prometheus: 监控

结论

重构目的

微服务设计是一个趋势,Kubernetes又是大公司必备的微服务治理Pass平台,后期可以根据具体的业务发展需要进行有益的尝试,梳理精简代码,调整技术架构方案

重构方向

前后的分离,独立部署,采用REST API的方式交互 将一些可复用的业务拆分出来,例如用户帐号,支付渠道,帐号交易 独立的业务也可以拆出来,如不同产品线,网校、直播、点播、BRTC


故障重试

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: httpbin.default.svc.cluster.local
      retries:
        attempts: 3
        retryOn: 5xx

测试

http http://httpbin:8000/status/5000
kubectl logs -f httpbin-77bfc6b755-xvcmx -c istio-proxy

故障注入


超时控制

httpbin.virtualservice.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin.default.svc.cluster.local
  http:
    - route:
        - destination:
            host: httpbin.default.svc.cluster.local

测试

延迟2秒正常,延迟5秒失败

http --body http://httpbin:8000/delay/2
http --body http://httpbin:8000/delay/5

安装 metrics-server

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml

开启 8 线程,每秒并发 500 发起请求

fortio load -a -c 8 -qps 500 -t 60s "http://ms.iirii.com:30077"

微服务的定义和优缺点 Kubernetes面试之Deployment和Statefulset区别 通俗理解Kubernetes中Service、Ingress与Ingress Controller的作用与关系


相关镜像

docker tag kennethreitz/httpbin:latest registry.ioros.com/docker.io/kennethreitz/httpbin:latest
docker image push registry.ioros.com/docker.io/kennethreitz/httpbin:latest
docker tag busybox:latest registry.ioros.com/docker.io/busybox:latest
docker image push registry.ioros.com/docker.io/busybox:latest
docker tag dustise/flaskapp:latest registry.ioros.com/docker.io/dustise/flaskapp:latest
docker image push registry.ioros.com/docker.io/dustise/flaskapp:latest
docker tag dustise/sleep:latest registry.ioros.com/docker.io/dustise/sleep:latest
docker image push registry.ioros.com/docker.io/dustise/sleep:latest
docker tag coam/test.web:0.0.1 registry.ioros.com/coam/test.web:0.0.1
docker image push registry.ioros.com/coam/test.web:0.0.1

Comments

Cor-Ethan, the beverage → www.iirii.com