容器化入门(七)--k8s高级

文摘   2024-08-18 11:06   英国  


本文内容主要来源于极客时间《Kubernetes 入门实战课》,夹杂了自己在学习过程中的思考和相关记录

1.容器资源配额Resource:回想容器的萨达隔离技术:namespace创建独立的进程空间,chroot实现独立的文件系统,cgroup实现CPU、内存等资源的管控,但是如何管控呢?只要在Pod容器的描述部分添加一个新字段resources即可

apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod-resources

spec:
  containers:
  - image: nginx:alpine
    name: ngx

    resources:
      requests:
        cpu: 10m
        memory: 100Mi
      limits:
        cpu: 20m
        memory: 200Mi

几个注意点:

  • requests:代表申请,这个是告诉k8s在创建Pod时必须要满足的资源
  • limits:是容器使用资源的上限,不能超过设定值,否则可能会被系统强制停止运行
  • 内存的写法和磁盘容量一样,使用KiMiGi
  • CPU的最小使用单位是0.001,对应的单位是m;如果不带任何单位,就代表几个CPU,可以是小数。
  • 如果不写resources字段,代表“既没有上限、也没有下限”,k8s不管CPU和内存是否足够,可以把Pod调度到任意的节点上,后续Pod运营时有额可以无限制的使用CPU
  • 如果Pod申请的资源系统没法满足,该Pod会创建成功但是实际无法被调度运行。

2.容器状态探针Probe:有时候Pod看起来正常,但是不一定能提供服务。如何更深入的探测Pod的状态呢, 就需要探针,k8s定义了三种探针,Kubernetes在启动容器后就会不断地调用探针来检查容器的状态:

  • Startup探针:启动探针,用来检查是否启动成功,适合那些有大量初始化工作要做、启动很慢的应用。如果Startup探针检测失败,就会认为服务没有正常启动,就会尝试反复重启,此时Liveness探针和Readiniss探针不会被启动。
  • Liveness探针:存活探针,用来检查应用是否正常运行、是否还活着,是否存在死锁、死循环等。如果Liveness探针失败,k8s认为容器发生了异常,会重启容器。
  • Readiness探针:就绪探针,用来检查应用是否可以接收流量、是否能对外提供服务。如果Readiness探针失败,k8s认为该服务内部错误、无法正常提供服务,会把容器从Service对象的负载均衡集合中排除,不给他分配流量。

3.探针的使用:

三种探针的配置方式都是一样的, 几个关键字段如下:

  • periodSeconds:探针探测的时间间隔,默认10s探测一次
  • timeoutSeconds:探针探测的超时时间,如果超时就认为探测失败,默认1s
  • successThreshold:连续探测多少次才认为是成功,对于startupProbe和livenessProbe来说只能是1
  • failureThreshold:连续探测失败几次才认为是真正发生了异常,默认3次

而k8s支持3种探测方式,Shell、TCP Socket、HTTP GET, 需要在探针明确配置:

  • exec:执行一个linux命令,如cp、cat等
  • tcpSocket:使用TCP协议连接指定端口
  • httpGet:连接端口并发送HTTP Get请求

4.探针实战:

使用一个nginx的Pod来演示探针的使用。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ngx-conf

data:
  default.conf: |
    server {
      listen 80;
      location = /ready {
        return 200 'I am ready';
      }
    }
---
apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod-probe

spec:
  volumes:
  - name: ngx-conf-vol
    configMap:
      name: ngx-conf

  containers:
  - image: nginx:alpine
    name: ngx
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: /etc/nginx/conf.d
      name: ngx-conf-vol

    startupProbe:
      periodSeconds: 1
      exec:
        command: ["cat", "/var/run/nginx.pid"]

    livenessProbe:
      periodSeconds: 10
      tcpSocket:
        port: 80

    readinessProbe:
      periodSeconds: 5
      httpGet:
        path: /ready
        port: 80
  • 定义了一个cm对象,启动80端口,并定义了/ready路径返回状态码
  • StartupProbe使用shell方式检测ngx存在磁盘上的进程号文件,如果存在就是启动成功
  • LivenessProbe检查端口
  • ReadinessProbe使用HTTP GET方式访问/ready

创建Pod并检查状态:

5.名字空间:

  • 名字空间并不是一个实体对象, 只是一个逻辑上的概念。它把集群切分成一个个彼此独立的区域,然后我们把对象放到这些区域里,就实现了类似容器技术里 namespace 的隔离效果,应用只能在自己的名字空间里分配资源和运行,不会干扰到其他名字空间里的应用。这是k8s面临大规模集群、海量节点时的一种现实考虑,减少资源争抢和冲突的概率。
  • 多团队、多项目共用Kubernates时最好为每一类用户创建出只属于它自己的“工作空间”。如果把 Kubernetes 比做一个大牧场的话,API 对象就是里面的鸡鸭牛羊,而名字空间就是圈养它们的围栏,有了各自合适的活动区域,就能更有效、更安全地利用 Kubernetes。
  • 创建和查看名字空间
kubectl create ns test-ns 
kubectl get ns
  • 要想把一个对象放入特定的名字空间,需要在metadata里添加一个namespace字段
apiVersion: v1
kind: Pod
metadata: 
  name: ngx 
  namespace: test-ns
  • 创建对象之后,相关的一些查询资源、查询状态的kubectl指令要指明名字空间
kubectl get pod -n test-ns
  • 非常重要:名字空间中的对象都是从属于名字空间的, 一旦名字空间被删除,里面的对象都会被删除

6.从名字空间的维度限制资源配额:

  • 可以从名字空间的维度,限定资源配额。 即这个空间所有对象使用资源的上限,这里不仅包含CPU和内存,还可以现实各种对象的数量
  • 名字空间的资源配额有个专门的对象,叫做ResourceQuota,简称quota

7.资源配额的YAML描述:

如下创建一个namespace并创建一个资源配额

apiVersion: v1
kind: Namespace
metadata:
  name: dev-ns

---

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-qt
  namespace: dev-ns

spec:
  hard:
    requests.cpu: 10
    requests.memory: 10Gi
    limits.cpu: 10
    limits.memory: 20Gi

    requests.storage: 100Gi
    persistentvolumeclaims: 100

    pods: 100
    configmaps: 100
    secrets: 100
    services: 10

    count/jobs.batch: 1
    count/cronjobs.batch: 1
    count/deployments.apps: 1

其中:

  • quota对象使用非常灵活,可以限制整个名字空间的配额(硬性全局限制,使用spec.hard字段),也可以只限制某些资源(scopeSelector)。
  • CPU和内存的配额,使用limit、request, 跟容器资源限制一样的
  • 可以限制PVC的存储总量和个数
  • 核心资源的个数,直接可以根据名字限制
  • 其他API对象个数,使用count/name.group方式

8.如何使用资源配额:

  • 创建&查询&查询剩余
kubectl apply -f quota-ns.yml
kubectl get quota -n dev-ns
kubectl describe quota -n dev-ns
  • 确认效果:上述的quota对象只允许一个Job对象,我们看看效果:
kubectl create job echo1 -n dev-ns --image=busybox -- echo hello
kubectl create job echo2 -n dev-ns --image=busybox -- echo hello

9.默认资源配额:

  • 为什么需要默认资源配额:当我们给名字空间加上资源配额之后,它会有一个约束,要求所有的Pod都必须用resources声明资源需求,否则就无法创建, 原因是:如果Pod未声明资源配额,代表可以无限制的使用CPU和内存,这个和qouta对象的约束冲突了,因此只能拒绝这样的对象创建:
  • 如果大大小小的每个Pod都去声明资源配额,显然很繁琐。因此需要一个默认值,如果Pod没显式指定、那就用这个默认资源配额,所以LimitRange对象应运而生(简称limit):

  • LimitRange对象的Yaml定义参考如下,其中:spec.limits描述资源限制;type指明要限制的对象,可以是Container、Pod、PVC; default对应容器中的resources.limites,是资源的上限,只使用于Container类型;defaultRequest对应容器中的resources.requests,只使用于Container类型;maxmin指明对象能使用的资源的最大最小值

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: dev-ns

spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: 200m
      memory: 50Mi
    default:
      cpu: 500m
      memory: 100Mi
  - type: Pod
    max:
      cpu: 800m
      memory: 200Mi
  • 创建limit对象并查看
  • 查看效果:默认给未显式指定资源约束的对象加上了资源约束

10.集群的可观测性之Metrics Server:

  • 如何像linux的top命令一样,查询每个Pod的资源占用? 我们需要安装一个Metrics Serve插件,它是一个专门从所有节点kubelet获取指标(metrics)、并把这些信息交给apiserver存储的工具, 示意图如下:
  • 安装方法参考:https://github.com/chronolaw/k8s_study/tree/master/metrics 安装完成之后可以在“kube-system”命名空间看到对应的Pod
  • 安装完成并等待运行一段时间后,就可以使用top命令查看node和pod的资源占用了
kubectl top node
kubectl top pod -n kube-system

11.水平自动伸缩功能HPA:

  • 前面讲过,可以使用kubectl scale手动的增减Deployment部署的Pod数量即说平方向的扩容缩容,如何基于实际的资源占用,完成自动的水平方向伸缩呢。API对象HorizontalPodAutoscaler简称HPA应运而生
  • HPA的YAML定义非常简单,通过maxReplicas、minReplicas设置Pod数量的上下限,targetCPUUtilizationPercentage指明要保持的CPU使用率目标(对于则扩容、少于则缩容),scaleTargetRef类似于selector、指明要控制的对象
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: ngx-hpa

spec:
  maxReplicas: 10
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ngx-hpa-dep
  targetCPUUtilizationPercentage: 5

12.HPA实战:

  • 定义如下的Deployment、svc、hpa对象
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-hpa-dep

spec:
  replicas: 1
  selector:
    matchLabels:
      app: ngx-hpa-dep

  template:
    metadata:
      labels:
        app: ngx-hpa-dep
    spec:
      containers:
      - image: nginx:alpine
        name: nginx
        ports:
        - containerPort: 80

        resources:
          requests:
            cpu: 50m
            memory: 10Mi
          limits:
            cpu: 100m
            memory: 20Mi
---

apiVersion: v1
kind: Service
metadata:
  name: ngx-hpa-svc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: ngx-hpa-dep
    
---

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: ngx-hpa

spec:
  maxReplicas: 10
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ngx-hpa-dep
  targetCPUUtilizationPercentage: 5
  • 创建完毕之后不久,副本数提升至2个
  • 运行一个httpd的测试Pod,使用ab测试工具加压
kubectl run test -it --image=httpd:alpine -- sh
ab -c 10 -t 60 -n 1000000 'http://ngx-hpa-svc/'

此时通过kubectl get hpa产看HPA的状态,可以明显看到扩容缩容的过程

13.更全面的监控系统Prometheus:

  • Prometheus是云原生监控领域的“事实标准”

  • Prometheus核心是Server,一个时序数据库TSDB存储监控数据、Retrieval组件使用拉取Pull的方式从各个组件收集数据、通过HTTP Server把这些数据吐给外界

  • 除了Server以外还有3个重要的组件: Push Gateway用以适配特殊的监控目标,把Pull转化为Push;Alermanager是告警中心,可以设定规则、发现问题就通过邮件等方式告警;Grafana是图形化界面

  • 安装方法参考https://github.com/chronolaw/k8s_study/blob/master/prom/setup.sh

  • 查看对外服务端口
  • 如上所示Prometheus自带的Web界面端口是32316,直接登录,Web界面上有一个查询框,可以使用PromQL来查询指标,生成可视化图表。这个可视化的效果有点弱,但是数据非常灵活,可以用来调试
  • 我们看看Grafana是图形化界面。默认用户名和密码都是admin。它的内部已经预置了很多强大的仪表盘,可以在左侧菜单栏的“Dashboards - Browse”里任意挑选一个(确实强大)

14.再看容器和Pod的网络模型:

  • 容器的网络模型:Docker有三种网络模式,null、host、bridge。最常用的是bridge。 Docker会创建一个名称为docker0的网桥,每个容器都会创建一个虚拟网卡对(veth对),插在网桥上,这样容器之间就可以互联互通
  • k8s网络模型, 简而言之就是IP-per-Pod:集群中每个Pod都会有一个唯一的IP地址,Pod内的所有容器共享这个IP,集群中所有的Pod都属于同一网段,Pod直接可以基于IP访问任意的另外一个Pod

15.CNI:

CNI是对于k8s网络模型的标准,CNI(Container Networking Interface。

根据实现技术大致分为3种:

  • Overlay: 即覆盖,是在真实底层网络之上构建了一个逻辑网络,把原始的Pod网络数据封包、再通过下层网络发送出去、到了目的地再解包。它对底层网络要求低、可适应性强,缺点是有额外的传输成本、性能比较低
  • Route: 利用系统内置的路由功能来实现Pod跨主机通讯,性能高,但是依赖底层网络
  • Underlay: 直接利用底层网络来通讯,所有Pod和宿主机都在一个网络里, 性能最好,但是不够灵活、对底层网络和硬件的依赖性最高

目前业界比较流行的三款插件:

  • Flannel:最早是一种Overlay 模式的网络插件,使用UDP和VXLAN,后来又支持了Route模式。Flannel 简单易用,但是性能不太好
  • Calico:是一种Route模式的插件,使用 BGP 协议(Border Gateway Protocol)来维护路由信息,性能要比 Flannel 好,而且支持多种网络策略,具备数据加密、安全隔离、流量整形等功能。
  • Cilium:同时支持 Overlay 模式和 Route 模式,它的特点是深度使用了 Linux eBPF 技术,在内核层次操作网络数据,所以性能很高,可以灵活实现各种功能。


编程废柴
努力Coding