Skip to content

Latest commit

 

History

History
314 lines (269 loc) · 11.8 KB

3.md

File metadata and controls

314 lines (269 loc) · 11.8 KB

容器编排与kubernetes作业管理

Pod

Pod基本概念

  • pod是Kuberetes里的原子调度单位。kubernetes项目的调度器,是统一按照pod而非容器的资源需求来进行计算的

  • pod只是一个逻辑概念

  • pod是一组共享了某些资源的容器

    • Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。
  • pod中infra容器永远都是第一个被创建的容器,其他用户定义的容器则是通过Join Network Namespace的方式与Infra容器关联在一起

  • 对于 Pod 里的容器 A 和容器 B 来说:

    • 它们可以直接使用 localhost 进行通信;
    • 它们看到的网络设备跟 Infra 容器看到的完全一样;
    • 一个 Pod 只有一个 IP 地址,也就是这个 Pod 的 Network Namespace 对应的 IP 地址;
    • 当然,其他的所有网络资源,都是一个 Pod 一份,并且被该 Pod 中的所有容器共享;
    • Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关。
  • 凡是调度、网络、存储、以及安全相关的属性,基本上是Pod级别的

  • pod中几个重要字段的含义和用法

    • NodeSelector 是一个供用户将Pod与Node进行绑定的字段
    • NodeName:一旦 Pod 的这个字段被赋值,Kubernetes 项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。所以,这个字段一般由调度器负责设置,但用户也可以设置它来“骗过”调度器,当然这个做法一般是在测试或者调试的时候才会用到。
    • HostAliases:定义了 Pod 的 hosts 文件(比如 /etc/hosts)里的内容
        apiVersion: v1
        kind: Pod
        ...
        spec:
        hostAliases:
        - ip: "10.1.2.3"
            hostnames:
            - "foo.remote"
            - "bar.remote"
        ...
    
    
        # hosts 文件内容如下
        cat /etc/hosts
        # Kubernetes-managed hosts file.
        127.0.0.1 localhost
        ...
        10.244.135.10 hostaliases-pod
        10.1.2.3 foo.remote
        10.1.2.3 bar.remote
    • 凡是和容器的Linux Namespace相关属性,也一定是Pod级别的

控制器 模型

  • pod看似复杂的API对象,实际上就是对容器的进一步抽象和封装而已
    ######## 控制器部分 ###############
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-deployment
    spec:
        selector:
            matchLabels:
            app: nginx # 确保携带了app=nginx 标签的Pod
        replicas: 2 # 启动的Pod个数
    ######### 被控制的对象 ##############
        template: # Pod 模板
            metadata:
            labels:
                app: nginx # 这个和上面选择器对应
            spec:
            containers:
            - name: nginx
                image: nginx:1.7.9
                ports:
                - containerPort: 80
  • Kubernetes 使用的这个“控制器模式”,跟我们平常所说的“事件驱动”,有什么区别和联系吗?

事件往往是一次性的,如果操作失败比较难处理,但是控制器是循环一直在尝试的,更符合kubernetes申明式API,最终达到与申明一致

作业副本与水平扩展(控制模式Deployment)

举个例子,如果你更新了 Deployment 的 Pod 模板(比如,修改了容器的镜像),那么 Deployment 就需要遵循一种叫作“滚动更新”(rolling update)的方式,来升级现有的容器。

而这个能力的实现,依赖的是 Kubernetes 项目中的一个非常重要的概念(API 对象):ReplicaSet。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
    name: nginx-set
    labels:
        app: nginx
spec:
    replicas: 3
    selector:
        matchLabels:
            app: nginx
    template:
        metadata:
            labels:
                app: nginx
        spec:
            containers:
            - name: nginx
                image: nginx:1.7.9

Deployment 控制器实际操纵的,正是这样的 ReplicaSet 对象,而不是 Pod 对象。

两种的区别

Deployment 实际上一个两层控制器,Deployment 控制 ReplicaSet,ReplicaSet 控制 Pod。ReplicaSet 有版本区分,滚动更新的能力就是基于 ReplicaSet 的版本来实现

StatefulSet 拓扑状态

Stateful把应用状态抽象成

  • 拓扑状态。应用的多个实例之间不是完全对等的关系。这些实例必须按照某些顺序启动。
  • 存储状态 应用的多个实例分别绑定不同的存储数据

Stateful 核心功能 通过某种方式记录这些状态,然后Pod被重新创建时,能够为新Pod恢复这些状态

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx" # 和deployment比多了这这个字段。
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1
        ports:
        - containerPort: 80
          name: web

这个字段的作用,就是告诉 StatefulSet 控制器,在执行控制循环(Control Loop)的时候,请使用 nginx 这个 Headless Service 来保证 Pod 的“可解析身份”。

最后总结

StatefulSet 这个控制器的主要作用之一,就是使用 Pod 模板创建 Pod 的时候,对它们进行编号,并且按照编号顺序逐一完成创建工作。而当 StatefulSet 的“控制循环”发现 Pod 的“实际状态”与“期望状态”不一致,需要新建或者删除 Pod 进行“调谐”的时候,它会严格按照这些 Pod 编号的顺序,逐一完成这些操作。

Stateful 存储状态

Kubernetes 中 PVC 和 PV 的设计,实际上类似于“接口”和“实现”的思想。开发者只要知道并会使用“接口”,即:PVC;而运维人员则负责给“接口”绑定具体的实现,即:PV。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi

Pod 删除之后,这个 Pod 对应的 PVC 和 PV,并不会被删除,而这个 Volume 里已经写入的数据,也依然会保存在远程存储服务里(比如,我们在这个例子里用到的 Ceph 服务器)。这个就是关键。。

DaemonSet

特征

  • 这个Pod运行在集群中的每个节点(Node)上
  • 每个节点只有一个这样的pod
  • 当新节点加入集群后,该Pod会自动在新节点创建出来,当节点被删除,对应的Pod也会被回收掉
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: k8s.gcr.io/fluentd-elasticsearch:1.20
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

DaemonSet 跟 Deployment 其实非常相似,只不过是没有 replicas 字段

Job和CronJob

job定义

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: resouer/ubuntu-bc 
        command: ["sh", "-c", "echo 'scale=10000; 4*a(1)' | bc -l "]
      restartPolicy: Never # 失败了会不断尝试创建一个新的Pod
  backoffLimit: 4

CornJob

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

声明式API与Kubernetes

什么是“声明式API”?=> kubectl apply命令

kubectl replace和create都是命令式的,区别是一次只能处理一个写请求。

kubectl apply 一次能处理多个写操作,并且具有Merge能力

在 Kubernetes 项目中,当一个 Pod 或者任何一个 API 对象被提交给 APIServer 之后,总有一些“初始化”性质的工作需要在它们被 Kubernetes 项目正式处理之前进行。比如,自动为所有 Pod 加上某些标签(Labels)。而这个“初始化”操作的实现,借助的是一个叫作 Admission 的功能。它其实是 Kubernetes 项目里一组被称为 Admission Controller 的代码,可以选择性地被编译进 APIServer 中,在 API 对象创建之后会被立刻调用到。但这就意味着,如果你现在想要添加一些自己的规则到 Admission Controller,就会比较困难。因为,这要求重新编译并重启 APIServer。显然,这种使用方法对 Istio 来说,影响太大了。所以,Kubernetes 项目为我们额外提供了一种“热插拔”式的 Admission 机制,它就是 Dynamic Admission Control,也叫作:Initializer。

PATCH API,正是声明式 API 最主要的能力

声明式API

整个 Kubernetes 里的所有 API 对象,实际上就可以用如下的树形结构表示出来:

image

一般来说,扩展api server (或者说添加自定义 resource )有两种方式:

    1. 通过创建CRDs, 主API server可以处理 CRDs 的 REST 请求(CRUD)和持久性存储。简单,不需要其他的编程。更适用于声明式的API,和kubernetes高度集成统一。
    1. API Aggregation, 一个独立的API server。主API server委托此独立的API server处理自定义resource。 需要编程,但能够更加灵活的控制API的行为,更加灵活的自定义存储,以及与API不同版本之间的转换。一般更适用于命令模式,或者复用已经存在REST API代码,不直接支持kubectl 和 k8s UI, 不支持scope resource in a cluster/namespace.

自定义 resource 可以使你方便的存取结构化的resource 数据。但是只有和controller组合在一起才是声明式API。声明式API允许你定义一个期望的状态。controller通过解读结构化的resource数据,获得期望状态,从而不断的调协期望状态和实际状态。

综上,types.go 应该是给controller来理解CRDs的schema用的。只有掌握了resource的schema,才能解释并得到用户创建的resource API object。 而 kubectl create -f resourcedefinition.yaml 或者 自定义API server, 则定义了RESTful API endpoint. 用于接受 REST 请求,改变 resource 的期望状态。

角色权限控制RBAC

Operator工作原理(实际上就是使用了自定义控制器CRD)

Operator 的工作原理,实际上是利用了 Kubernetes 的自定义 API 资源(CRD),来描述我们想要部署的“有状态应用”;然后在自定义控制器里,根据自定义 API 对象的变化,来完成具体的部署和运维工作。