Kubernetes 工作负载-高级设置篇(硬核版)

2020-12-14  本文已影响0人  聪明的奇瑞

OpenKube 为我司研发的一个容器云产品 ----------- The article was written by:GoodGoodStudy(linyuan@deepexi.com

我们再深入浅出的了解一下 Pod,Pod 只是一个逻辑概念,Kubernetes 真正处理的,还是宿主机操作系统上 Linux 容器的 NameSpace 和 Cgroups,而并不存在一个所谓的 Pod 的边界或者隔离环境。Pod 是一组共享了某些资源的容器,Pod 里的所有容器共享的是一个 Network NameSpace,并且可以声明共享同一个 Volume。在 Kubernetes 里,Pod 的实现需要使用一个中间容器,这个容器叫做 Infra 容器(Infra 容器(k8s.gcr.io/pause)占用极少的资源,它的镜像时用汇编语言编写的,永远处于“暂停”状态的容器)。在 Pod 中 Infra 容器永远都是第一个被创建的容器,而其他用户自定义的容器,则通过 join Network NameSpace 的方式,与 Infra 容器关联在一起。对于同一个 Pod 里面的所有用户容器,它们的进出流量都是通过 Infra 容器完成的。凡是调度、网络、存储、以及安全相关的属性,基本上都是 Pod 级别的。

1. 数据卷

Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用程序带来一些问题:

Kubernetes 的数据卷(Volume)便是为了解决上述问题的。数据卷有很多种类型,OpenKube 也为以下几种常用的卷创建提供了支持。

1.1 主机路径(hostPath)

hostPath 允许将节点(宿主机)上的文件系统挂载到 Pod 中的容器里面去,如果 Pod 需要使用节点上的文件,可以使用该类型。需要注意的是,默认调度策略下 Pod 在重建时,可能会调度到集群中的其它节点上,此时因调度的节点不同,所以无法获取"先前节点"上所保存的数据。

下图是在 OpenKube 上使用了 hostPath 卷类型的配置图:

image

它大致等同于以下的 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-pd
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
      readOnly: true
  volumes:
  - name: test-volume
    hostPath:
      # 宿主上目录位置
      path: /data
      # 此字段为可选
      type: Directory

1.2 临时路径(emptyDir)

emptyDir 可以让 Pod 中的所有容器共享同一文件系统,常用于作为临时目录、或缓存使用。它会在 Pod 被分配到节点上时被创建,并跟随 Pod 的生命中后期,即该该 Pod 一直运行,卷就会一直存在,当它被删除时卷也会同时删除。

下图是在 OpenKube 上使用了 emptyDir 卷类型的配置图:

image

它大致等同于以下的 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
            readOnly: true
  volumes:
  - name: cache-volume
    emptyDir: {}

1.3 配置项(configMap)

configMap 卷提供了向 Pod 注入 ConfigMap(配置)数据的方法。 ConfigMap 对象中存储的每条数据都会以一个个文件形式保存在指定的容器挂载路径中,此时你可以在容器中读取这些配置数据。

image

它大致等同于以下的 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-vol-demo
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
          readOnly: true
  volumes:
    - configMap:
        defaultMode: 420
        name: lytest
      name: config-vol

此时便可以在容器中获取 ConfigMap 对象存储的数据。

image

1.4 密钥(secret)

secret 卷用来给 Pod 传递敏感信息(例如密码)。你可以将 Secret 存储在 Kubernetes API 服务器上,然后以文件的形式挂在到 Pod 中,无需直接与 Kubernetes 耦合。

image

它大致等同于以下的 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  name: secret-vol-demo
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - name: secret-vol
          mountPath: /etc/secret
          readOnly: true
  volumes:
    - secret:
        defaultMode: 420
        secretName: ly-secret
      name: secret-vol

1.5 存储声明(PVC)

...待补充

2. 节点选择

节点选择的作用是用来控制 Pod 的调度的,它是通过 Pod 的 spec.nodeSelector 字段来控制的。在 OpenKube 中,它可以是以下三个选项:

自动分配

默认选项,在该模式下将由 Kubernetes 根据各节点的运行时状态自动分配。

指定节点

Pod 的 spec.nodeName 字段属性可以让其仅调度到指定节点。在 OpenKube 中我们在创建工作负载时通过指定节点来进行配置。

image

匹配节点

除了指定调度节点外,Kubernetes 还支持通过配置 spec.nodeSelector 字段来匹配节点的标签,从而决定是否将 Pod 调度到该节点上。在 OpenKube 中我们在创建工作负载时通过匹配节点来进行配置。

image

3. 升级策略

工作负载的 .spec.strategy 字段值用于指定新 Pod 替换旧 Pod 的策略配置。不同的工作负载类型所支持的策略有所不同:

Deployment

image

StatefulSet

image

DaemonSet

image

4. 自动伸缩

自动伸缩(Horizontal Pod Autoscaler)可以根据 CPU 利用率自动扩缩 ReplicationController、 Deployment、ReplicaSet 或 StatefulSet 中的 Pod 数量 。

image

5. 容忍

可以通过 Pod 的 spec.tolerations 字段来添加了容忍,允许其部署在匹配特定污点的节点上。可以使用两种操作符来匹配污点,分别是:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600
tolerations:
- key: "key2"
  operator: "Exists"
  effect: "NoSchedule"
  tolerationSeconds: 3600

等同于下图的配置:

image

在实践中,某些有着特殊硬件的节点需要专用于运行一类有着此类硬件资源需求的 Pod 对象时,例如,那些有着 SSD 或 GPU 的设备,酒应该为其添加污点信息以排除其他的 Pod 对象。

6. 亲和性

待完善

一般情况下我们部署的 Pod 是通过集群自动调度选择某个节点的,默认情况下调度器考虑的是资源足够,并且负载尽量平均。但是有的时候我们需要能够更加细粒度的去控制 Pod 的调度,比如我们希望服务的 Pod 尽量跑在一些带有特定标签的节点上,或希望能某些服务的 Pod 调度到同样的节点上,这就需要用到 Kubernetes 里面的一个概念 --- 亲和性(Affinity)。

亲和性有又分为节点亲和性和应用亲和性两种类型,每种类型又支持两种策略:

6.1 节点亲和性(nodeAffinity)

节点亲和性(nodeAffinity )是通过匹配节点的标签来决定是否调度到该节点**,但相比节点选择( nodeSelector )更加灵活,它可以进行一些简单的逻辑组合,不只是简单的相等匹配。

image

6.2 应用亲和性(podAffinity)/ 应用反亲和性(podAntiAffinity)

节点亲和性是从 Pod 和 Node 的关系角度进行匹配,而应用亲和性(podAffinity )与应用反亲和性(podAntiAffinity )是从 Pod 与 Pod 方面,即应用与应用的角度去考虑。它会基于已在节点上运行 Pod 的标签(LabelSelector)来约束 Pod 可以调度到的节点,并且也是基于命名空间限定的,而不是节点上的标签。

例如,因为服务 A 和服务 B 需要在同一节点上协同工作;或是我们希望系统数据服务 C 和数据服务 D 尽量分开,部署到不同节点上,以免主机或机房出问题时,不会导致应用完全不可用,等等这些场景下就需要用到应用亲和性了。

7. 终止等待时间

可以通过 spec.terminationGracePeriodSeconds 字段来设置终止等待时间,该配置的作用是当集群向 Pod 发送 SIGTERM 信号后,等待结束的时间。当超过该时间后,集群会强制结束该 Pod。因此该时间是 Kubernetes 集群给你程序预留的最后缓冲时间,来处理关闭之前的操作。

image

8. 主机别名

Kubernetes 中不同服务之间可以通过 "service-name" 域名来相互访问,这是通过集群中的 CoreDNS 组件来完成域名解析工作的。但当我们想在 Pod 上增加一些域名解析时,可以通过 Pod 的 spec.hostAliases 字段往 /etc/hosts 文件中添加域名解析(集群需 1.7.x 及以上版本)。

image

它大致等同于以下的 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  hostAliases:
  – ip: "10.1.2.2"
    hostnames:
    – "mc.local"
    – "rabbitmq.local"
  containers:
  – name: cathosts
    image: busybox
...

9. DNS 策略与配置

我们可以通过 Pod 声明中的 spec.dnsPolicy 字段来设置 DNS 策略,目前支持以下类型:

默认 DNS 策略为 ClusterFirst

在 OpenKube 中 DNS 策略设置目前仅支持 ClusterFirst 与 ClusterFirstWithHostNet 两种类型。除了采用策略预设的配置,Pod 的 spec.dnsConfig 字段可以让用户对 Pod 的 DNS 设置进行更多控制,该字段是可选的,它可以与任何 dnsPolicy 设置一起使用。 但是当 Pod 的 dnsPolicy 设置为 "None" 时,必须指定 dnsConfig 字段。

用户可以在 dnsConfig 字段中指定以下属性:

image

上面配置内容等同于于如下 Pod 声明:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "ClusterFirst"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"

10. 宿主机配置

NameSpace 是 Linux 内核用于隔离资源的机制,通过 NameSpace 可以让一些进程只能看到与自己相关的一部分资源,而不受其它进程影响。其实现方式是把一个或多个进程的相关资源指定在同一个 NameSpace 中。

img

容器本质上是把系统中为同一个业务目标服务的相关进程合成一组,放在同一个叫 Namespace 空间中。同一个 Namespace 中的进程能够互相通信,在这个 Namespace 中可以拥有自己独立的主机名、进程 ID 系统、IPC、网络、文件系统、用户等等资源。在某种程度上,实现了一个简单的虚拟:让一个主机上可以同时运行多个互不感知的系统。

image.png

10.1 是否使用主机 IPC

Pod 声明文件的 spec.hostIPC 字段值表示是否使用主机 PID。若选择"是",此时 Pod 中所有的容器会与宿主机处于同一个 IPC NameSpace 空间中,可直接与宿主机进程进行通信。

10.2 是否使用主机 PID

Pod 声明文件的 spec.hostPID 字段值表示是否使用主机 PID。若选择"是",此时 Pod 中所有的容器会与宿主机处于同一个 PID NameSpace 空间中 。当我们在 Pod 中的容器执行 ps -aux 命令时,可以查看到宿主机里正在运行的所有进程。

10.3 是否使用主机网络

Pod 声明文件的 spec.hostNetwork 字段值表示是否使用主机网络。若选择"是",Pod 中所有的容器会直接使用宿主机的网络,即它们与宿主机处于同一个 NET NameSpace。

10.4 是否共享进程命名空间

Pod 声明文件的 spec.shareProcessNamespace 字段值表示是否共享进程命名空间。若选择 "是" 时,Pod 里的每个容器的进程,对于所有容器来说都是可见的,即它们共享了同一个 PID Namespace。

11. 安全上下文

默认情况下,工作负载会以 root 用户运行容器(可通过在容器中执行 ps -aux 命令查看 USER 列信息),但在某些场景下这会带来安全问题,此时我们需要控制容器中运行的用户与权限,避免以超级用户身份运行容器。

安全上下文(Security Context)定义 Pod 或 Container 的特权与访问设置。目前 OpenKube 支持在创建工作负载时对安全上下文进行以下配置。

11.1 以非 root 用户身份运行

默认情况下,是否以非 root 用户身份运行的单选框值为"否",此时 Pod 声明文件中的 spec.securityContext.runAsNonRoot 字段值为 false,表示以 root 身份运行容器。但当我们要以指定用户运行容器时,应勾选"是"(即字段值为 true)。

除此之外,还需要配置 spec.securityContext.runAsUserspec.securityContext.runAsGroup 字段,即分别设置运行容器的用户 ID主用户组 ID。在下图中我们设置以 ID 为 1 的用户运行容器,其用户组 ID 为 2

image

那么如何确认我们所指定的用户与用户组是否生效呢?可以在容器中执行 whoami 命令获取当前用户名,与用户详情配置文件 /etc/passwd 信息进行比较来确认当前用户。而用户组可以通过 group 命令获取当前用户组,与用户组详情文件 /etc/group 信息进行比较。

fsGroup

该配置项为 Pod 声明中的 spec.fsGroup 字段,用于指定卷的附加用户组 ID。

补充用户组

该配置项为 Pod 声明中的 spec.supplementalGroups 字段,一个用户可以加入很多组,但是其中只有一个是主组,而配置项用于指定额外的用户组 ID。

11.2 配置内核参数

sysctl 参数位于 /proc/sys 目录下,它们是系统运行时的内核参数。Kubernetes 支持我们在 Pod 的声明文件中去配置内核参数。这些配置将会影响容器运行时的系统环境,例如设置联网功能,IP 转发以及源路由检查等。

上一篇 下一篇

猜你喜欢

热点阅读