Kubernetes 在 Namespace 中配置默认的CPU请求与限额

本页展示了如何在命名空间中配置默认的CPU请求与限额。
一个Kubernetes集群能细分为不同的命名空间。如果在一个拥有默认CPU限额的命名空间中创建一个容器,则这个容器不需要指定它自己的CPU限额, 它会被分配这个默认的CPU限额值。Kubernetes在某些条件下才会分配默认的CPU请求值,这个将在本主题的后面解释。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

创建一个命名空间

创建一个命名空间为了使你在本练习中创建的资源与集群的其它部分相隔离。

kubectl create namespace default-cpu-example

创建一个LimitRange和一个Pod

以下是一个LimitRange对象的配置文件。这个配置中指定了一个默认的CPU请求和一个默认的CPU限额。

cpu-defaults.yaml 
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

在这个defaule-cpu-example命名空间中创建这个LimitRange:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults.yaml --namespace=default-cpu-example

现在如果在这个defaule-cpu-example命名空间中创建一个容器,则该容器不需要指定它自己的CPU请求和CPU限额, 该容器会被赋予一个默认的CPU请求值0.5和一个默认的CPU限额值1。

cpu-defaults-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

创建Pod

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults-pod.yaml --namespace=default-cpu-example

查看该Pod的配置:

kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example

输出显示该Pod的容器含有一个CPU请求值500m和一个CPU限额值1。 这些是由LimitRange指定的默认值。

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

如果你指定了一个容器的限额值,但未指定请求值,会发生什么?

以下是一个含有一个容器的Pod的配置文件。该容器指定了一个CPU限额,但未指定请求:

cpu-defaults-pod-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-2
spec:
  containers:
  - name: default-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1"

创建该Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults-pod-2.yaml --namespace=default-cpu-example

查看该Pod的配置:

kubectl get pod cpu-limit-no-request --output=yaml --namespace=default-cpu-example

输出展示该容器的CPU请求值与它的限额值相等。
注意该容器并未被赋予这个默认的CPU请求值0.5。

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "1"

如果你指定了一个容器的请求值,未指定限额值,会发生什么?

以下是含有一个容器的Pod的配置文件。该容器指定了一个CPU请求,但未指定一个限额:

cpu-defaults-pod-3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-3
spec:
  containers:
  - name: default-cpu-demo-3-ctr
    image: nginx
    resources:
      requests:
        cpu: "0.75"

创建该Pod

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults-pod-3.yaml --namespace=default-cpu-example

输出显示该容器的CPU请求值被设置为该容器配置文件中指定的值。该容器的CPU限额设置为1,这是该命名空间的默认CPU的限额值。

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 750m

默认CPU限额和请求的动机

如果你的命名空间含有资源配额, 它是有帮助的对于设置一个CPU限额的默认值。 以下是资源配额对命名空间施加的两个限制:

  • 在命名空间运行的每一个容器必须含有它自己的CPU限额。
  • 在命名空间中所有容器使用的CPU总量不能超出指定的限额。

如果一个容器没有指定它自己的CPU限额,它将被赋予默认的限额值,然后它可以在被配额限制的命名空间中运行。

What’s next

对于集群管理员

对于应用开发者

译者:jianzhangbjz / 原文链接

K8S中文社区微信公众号

Kubernetes 为 Namespace 配置默认的内存请求与限额

本页展示了如何给命名空间配置默认的内存请求与限额。 如果在一个拥有默认内存限额的命名空间中创建一个容器,并且这个容器未指定它自己的内存限额, 它会被分配这个默认的内存限额值。Kubernetes 在某些条件下才会分配默认的内存请求值,这个将在本主题的后面解释。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

集群中的每个节点必须具有至少 300GiB 的内存。

创建命名空间

创建一个命名空间,以便您在本练习中创建的资源与集群的其它部分相隔离。

kubectl create namespace default-mem-example

创建 LimitRange 和 Pod

以下是一个 LimitRange 对象的配置文件。该配置指定了默认的内存请求与默认的内存限额。

memory-defaults.yaml 
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

在 default-mem-example 命名空间中创建 LimitRange:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults.yaml --namespace=default-mem-example

现在如果在这个 default-mem-example 命名空间中创建一个容器,并且该容器未指定它自己的内存请求与内存限额, 该容器会被赋予默认的内存请求值 256MiB 和默认的内存限额值 512MiB。

以下是一个 Pod 的配置文件,它含有一个容器。这个容器没有指定内存请求和限额。

memory-defaults-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults-pod.yaml --namespace=default-mem-example

查看关于该 Pod 的详细信息:

kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example

输出显示该 Pod 的容器的内存请求值是 256MiB, 内存限额值是 512MiB. 这些是由 LimitRange 指定的默认值。

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

删除 Pod:

kubectl delete pod default-mem-demo --namespace=default-mem-example

如果您指定了容器的限额值,但未指定请求值,会发生什么?

以下是含有一个容器的 Pod 的配置文件。该容器指定了内存限额,但未指定请求:

memory-defaults-pod-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: defalt-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults-pod-2.yaml --namespace=default-mem-example

查看关于该 Pod 的详细信息:

kubectl get pod mem-limit-no-request --output=yaml --namespace=default-mem-example

输出显示该容器的内存请求值与它的限额值相等。
注意该容器并未被赋予默认的内存请求值 256MiB。

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

如果您指定了容器的请求值,但未指定限额值,会发生什么?

以下是含有一个容器的 Pod 的配置文件。该容器指定了内存请求,但未指定限额:

memory-defaults-pod-3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

创建该 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults-pod-3.yaml --namespace=default-mem-example

查看该 Pod 的配置信息:

kubectl get pod default-mem-request-no-limit --output=yaml --namespace=default-mem-example

输出显示该容器的内存请求值被设置为该容器配置文件中指定的值。该容器的内存限额设置为 512Mi,这是该命名空间的默认内存限额值。

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

默认内存限额与请求的动机

如果您的命名空间具有资源配额, 它为内存限额设置默认值是有意义的。 以下是资源配额对命名空间施加的两个限制:

  • 在命名空间运行的每一个容器必须有它自己的内存限额。
  • 在命名空间中所有的容器使用的内存总量不能超出指定的限额。

如果一个容器没有指定它自己的内存限额,它将被赋予默认的限额值,然后它才可以在被配额限制的命名空间中运行。

What’s next

对于集群管理员

对于应用开发者

译者:jianzhangbjz / 原文链接

K8S中文社区微信公众号

Kubernetes 为 Namespace 设置最小和最大内存限制

本文展示了如何为 namespace 中运行的容器设置内存的最小和最大值。您可以设置 LimitRange 对象中内存的最小和最大值。如果 Pod 没有符合 LimitRange 施加的限制,那么它就不能在 namespace 中创建。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

集群中的每个节点至少需要 1 GiB 内存。

创建一个 namespace

请创建一个 namespace,这样您在本练习中创建的资源就可以和集群中其余资源相互隔离。

kubectl create namespace constraints-mem-example

创建一个 LimitRange 和一个 Pod

这是 LimitRange 的配置文件:

memory-constraints.yaml 
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

创建 LimitRange:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints.yaml --namespace=constraints-mem-example

查看 LimitRange 的详细信息:

kubectl get limitrange cpu-min-max-demo --namespace=constraints-mem-example --output=yaml

输出显示了符合预期的最小和最大内存限制。但请注意,即使您没有在配置文件中为 LimitRange 指定默认值,它们也会被自动创建。

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

现在,每当在 constraints-mem-example namespace 中创建一个容器时,Kubernetes 都会执行下列步骤:

  • 如果容器没有指定自己的内存请求(request)和限制(limit),系统将会为其分配默认值。
  • 验证容器的内存请求大于等于 500 MiB。
  • 验证容器的内存限制小于等于 1 GiB。

这是一份包含一个容器的 Pod 的配置文件。这个容器的配置清单指定了 600 MiB 的内存请求和 800 MiB 的内存限制。这些配置符合 LimitRange 施加的最小和最大内存限制。

memory-constraints-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod.yaml --namespace=constraints-mem-example

验证 Pod 的容器是否运行正常:

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

查看关于 Pod 的详细信息:

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

输出显示了容器的内存请求为 600 MiB,内存限制为 800 MiB。这符合 LimitRange 施加的限制。

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

删除 Pod:

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

尝试创建一个超过最大内存限制的 Pod

这是一份包含一个容器的 Pod 的配置文件。这个容器的配置清单指定了 800 MiB 的内存请求和 1.5 GiB 的内存限制。

memory-constraints-pod-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

尝试创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

输出显示 Pod 没有能够成功创建,因为容器指定的内存限制值太大:

Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

尝试创建一个不符合最小内存请求的 Pod

这是一份包含一个容器的 Pod 的配置文件。这个容器的配置清单指定了 200 MiB 的内存请求和 800 MiB 的内存限制。

memory-constraints-pod-3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

尝试创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

输出显示 Pod 没有能够成功创建,因为容器指定的内存请求值太小:

Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

创建一个没有指定任何内存请求和限制的 Pod

这是一份包含一个容器的 Pod 的配置文件。这个容器没有指定内存请求,也没有指定内存限制。

memory-constraints-pod-4.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

创建 Pod:

kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

查看关于 Pod 的细节信息:

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

输出显示 Pod 的容器具有 1 GiB 的内存请求和 1 GiB 的内存限制。容器是如何获取这些值的呢?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

因为您的容器没有指定自己的内存请求和限制,它将从 LimitRange 获取 默认的内存请求和限制值

到目前为止,您的容器可能在运行,也可能没有运行。回想起来,有一个先决条件就是节点必须拥有至少 1 GiB 内存。如果每个节点都只有 1 GiB 内存,那么任何一个节点上都没有足够的内存来容纳 1 GiB 的内存请求。如果碰巧使用的节点拥有 2 GiB 内存,那么它可能会有足够的内存来容纳 1 GiB 的内存请求。

删除 Pod:

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

应用最小和最大内存限制

LimitRange 在 namespace 中施加的最小和最大内存限制只有在创建和更新 Pod 时才会被应用。改变 LimitRange 不会对之前创建的 Pod 造成影响。

最小和最大内存限制的动因

作为一个集群管理员,您可能希望为 Pod 能够使用的内存数量施加限制。例如:

  • 集群中每个节点拥有 2 GB 内存。您不希望任何 Pod 请求超过 2 GB 的内存,因为集群中没有节点能支持这个请求。
  • 集群被生产部门和开发部门共享。 您希望生产负载最多使用 8 GB 的内存而将开发负载限制为 512 MB。这种情况下,您可以为生产环境和开发环境创建单独的 namespace,并对每个 namespace 应用内存限制。

清理

删除 namespace:

kubectl delete namespace constraints-mem-example

What’s next

对于集群管理员

对于应用开发者

译者:xiaosuiba / 原文链接

K8S中文社区微信公众号

Kubernetes 示例:使用 Stateful Sets 部署 Cassandra

本教程展示了如何在 Kubernetes 上开发一个云原生的 Cassandra deployment。在这个实例中,Cassandra 使用了一个自定义的 SeedProvider 来发现新加入集群的节点。

在集群环境中部署类似 Cassandra 的有状态(stateful)应用可能是具有挑战性的。StatefulSets 极大的简化了这个过程。请阅读 StatefulSets 获取更多关于此教程中使用的这个特性的信息。

Cassandra Docker

Pod 使用了来自 Google 容器注册表(container registry) 的 gcr.io/google-samples/cassandra:v12 镜像。这个 docker 镜像基于 debian:jessie 并包含 OpenJDK 8。这个镜像包含了来自 Apache Debian 源的标准 Cassandra 安装。您可以通过环境变量来改变插入到 cassandra.yaml 中的值。

ENV VAR DEFAULT VALUE
CASSANDRA_CLUSTER_NAME ‘Test Cluster’
CASSANDRA_NUM_TOKENS 32
CASSANDRA_RPC_ADDRESS 0.0.0.0

Objectives

Before you begin

为了完成本教程,你应该对 Pod、 Service 和 StatefulSet 有基本的了解。此外,你还应该:

注意: 如果你还没有集群,请查阅 快速入门指南

Minikube 附加安装说明

小心: Minikube 默认配置 1024MB 内存和 1 CPU,这在本例中将导致资源不足。

为了避免这些错误,请这样运行 minikube:

minikube start --memory 5120 --cpus=4

创建 Cassandra Headless Service

Kubernetes Service 描述了一个执行相同任务的 Pod 集合。

下面的 Service 用于在集群内部的 Cassandra Pod 和客户端之间进行 DNS 查找。

  1. 在下载清单文件的文件夹下启动一个终端窗口。
  2. 使用 cassandra-service.yaml 文件创建一个 Service,用于追踪所有的 Cassandra StatefulSet 节点。
    kubectl create -f cassandra-service.yaml
    
cassandra/cassandra-service.yaml 
apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra

验证(可选)

获取 Cassandra Service。

kubectl get svc cassandra

响应应该像这样:

NAME        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   None         <none>        9042/TCP   45s

如果返回了任何其它消息,这个 service 就没有被成功创建。请查阅 调试 Services,了解常见问题。

使用 StatefulSet 创建 Cassandra 环

上文中的 StatefulSet 清单文件将创建一个由 3 个 pod 组成的 Cassandra 环。

注意: 本例中的 Minikube 使用默认 provisioner。请根据您使用的云服务商更新下面的 StatefulSet。

  1. 如有必要请修改 StatefulSet。
  2. 使用 cassandra-statefulset.yaml 文件创建 Cassandra StatefulSet。
    kubectl create -f cassandra-statefulset.yaml
    
cassandra/cassandra-statefulset.yaml 
apiVersion: "apps/v1beta1"
kind: StatefulSet
metadata:
  name: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v12
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
           cpu: "500m"
           memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "PID=$(pidof java) && kill $PID && while ps -p $PID > /dev/null; do sleep 1; done"]
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: CASSANDRA_AUTO_BOOTSTRAP
            value: "false"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # These volume mounts are persistent. They are like inline claims,
        # but not exactly because the names need to match exactly one of
        # the stateful pod volumes.
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  # do not use these in production until ssd GCEPersistentDisk or other ssd pd
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
      annotations:
        volume.beta.kubernetes.io/storage-class: fast
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

验证 Cassandra StatefulSet

  1. 获取 Cassandra StatefulSet:
    kubectl get statefulset cassandra
    

    响应应该是

    NAME        DESIRED   CURRENT   AGE
    cassandra   3         0         13s
    

    StatefulSet 资源顺序的部署 pod。

  2. 获取 pod, 查看顺序创建的状态:
    kubectl get pods -l="app=cassandra"
    

    响应应该像是

    NAME          READY     STATUS              RESTARTS   AGE
    cassandra-0   1/1       Running             0          1m
    cassandra-1   0/1       ContainerCreating   0          8s
    

    注意: 部署全部三个 pod 可能需要 10 分钟时间。

    一旦所有 pod 都已经部署,相同的命令将返回:

    NAME          READY     STATUS    RESTARTS   AGE
    cassandra-0   1/1       Running   0          10m
    cassandra-1   1/1       Running   0          9m
    cassandra-2   1/1       Running   0          8m
    
  3. 运行 Cassandra nodetool 工具,显示环的状态。
    kubectl exec cassandra-0 -- nodetool status
    

    响应为:

    Datacenter: DC1-K8Demo
    ======================
    Status=Up/Down
    |/ State=Normal/Leaving/Joining/Moving
    --  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
    UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
    UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
    UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
    

修改 Cassandra StatefulSet

使用 kubectl edit修改 Cassandra StatefulSet 的大小。

  1. 运行下面的命令:
    kubectl edit statefulset cassandra
    

    这个命令将在终端中打开一个编辑器。您需要修改 replicas 字段一行。

    注意: 以下示例是 StatefulSet 文件的摘录。

     # Please edit the object below. Lines beginning with a '#' will be ignored,
     # and an empty file will abort the edit. If an error occurs while saving this file will be
     # reopened with the relevant failures.
     #
     apiVersion: apps/v1beta1
     kind: StatefulSet
     metadata:
      creationTimestamp: 2016-08-13T18:40:58Z
      generation: 1
      labels:
        app: cassandra
      name: cassandra
      namespace: default
      resourceVersion: "323"
      selfLink: /apis/apps/v1beta1/namespaces/default/statefulsets/cassandra
      uid: 7a219483-6185-11e6-a910-42010a8a0fc0
     spec:
      replicas: 3
    
  2. 修改副本数量为 4 并保存清单文件。

    这个 StatefulSet 现在包含 4 个 pod。

  3. 获取 Cassandra StatefulSet 来进行验证:
    kubectl get statefulset cassandra
    

    响应应该为:

    NAME        DESIRED   CURRENT   AGE
    cassandra   4         4         36m
    

Cleaning up

删除或缩容 StatefulSet 不会删除与其相关联的 volume。这优先保证了安全性:您的数据比其它所有自动清理的 StatefulSet 资源都更宝贵。

警告: 取决于 storage class 和回收策略(reclaim policy),删除 Persistent Volume Claims 可能导致关联的 volume 也被删除。绝对不要假设在 volume claim 被删除后还能访问数据。

  1. 运行下面的命令,删除 StatefulSet 中所有能内容:
    grace=$(kubectl get po cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
      && kubectl delete statefulset -l app=cassandra \
      && echo "Sleeping $grace" \
      && sleep $grace \
      && kubectl delete pvc -l app=cassandra
    
  2. 运行下面的命令,删除 Cassandra Service。
    kubectl delete service -l app=cassandra
    

本文由xiaosuiba翻译,点击查看原文链接

K8S中文社区微信公众号

Kubernetes StatefulSet基本使用

本教程介绍了如何使用 StatefulSets 来管理应用。演示了如何创建、删除、扩容/缩容和更新 StatefulSets 的 Pods。

Objectives

StatefulSets 旨在与有状态的应用及分布式系统一起使用。然而在 Kubernetes 上管理有状态应用和分布式系统是一个宽泛而复杂的话题。为了演示 StatefulSet 的基本特性,并且不使前后的主题混淆,你将会使用 StatefulSet 部署一个简单的 web 应用。

在阅读本教程后,你将熟悉以下内容:

  • 如何创建 StatefulSet
  • StatefulSet 怎样管理它的 Pods
  • 如何删除 StatefulSet
  • 如何对 StatefulSet 进行扩容/缩容
  • 如何更新一个 StatefulSet 的 Pods

Before you begin

在开始本教程之前,你应该熟悉以下 Kubernetes 的概念:

本教程假设你的集群被配置为动态的提供 PersistentVolumes 。如果没有这样配置,在开始本教程之前,你需要手动准备5个1 GiB的存储卷。

##创建 StatefulSet

作为开始,使用如下示例创建一个 StatefulSet。它和 StatefulSets 概念中的示例相似。它创建了一个 Headless Service nginx 用来发布StatefulSet web 中的 Pod 的 IP 地址。

web.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: gcr.io/google_containers/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

下载上面的例子并保存为文件 web.yaml。

你需要使用两个终端窗口。在第一个终端中,使用 kubectl get 来查看 StatefulSet 的 Pods 的创建情况。

kubectl get pods -w -l app=nginx

在另一个终端中,使用 kubectl create 来创建定义在 web.yaml 中的 Headless Service 和 StatefulSet。

kubectl create -f web.yaml 
service "nginx" created
statefulset "web" created

上面的命令创建了两个 Pod,每个都运行了一个 NGINX web 服务器。获取 nginx Service 和 web StatefulSet 来验证是否成功的创建了它们。

kubectl get service nginx
NAME      CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     None         <none>        80/TCP    12s

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

顺序创建 Pod

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

请注意在 web-0 Pod 处于 Running和Ready 状态后 web-1 Pod 才会被启动。

<!-

Pods in a StatefulSet

–>

StatefulSet 中的 Pod

StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识。

检查 Pod 的顺序索引

获取 StatefulSet 的 Pod。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

如同 StatefulSets 概念中所提到的, StatefulSet 中的 Pod 拥有一个具有黏性的、独一无二的身份标志。这个标志基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。 Pod 的名称的形式为<statefulset name>-<ordinal index>。web StatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0和web-1。

使用稳定的网络身份标识

每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用kubectl exec 在每个 Pod 中执行hostname 。

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

使用 kubectl run 运行一个提供 nslookup 命令的容器,该命令来自于 dnsutils 包。通过对 Pod 的主机名执行 nslookup,你可以检查他们在集群内部的 DNS 地址。

kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh 
nslookup web-0.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

headless service 的 CNAME 指向 SRV 记录(记录每个 Running 和 Ready 状态的 Pod)。SRV 记录指向一个包含 Pod IP 地址的记录表项。

在一个终端中查看 StatefulSet 的 Pod。

kubectl get pod -w -l app=nginx

在另一个终端中使用 kubectl delete 删除 StatefulSet 中所有的 Pod。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

等待 StatefulSet 重启它们,并且两个 Pod 都变成 Running 和 Ready 状态。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

使用 kubectl exec 和 kubectl run 查看 Pod 的主机名和集群内部的 DNS 表项。

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh 
nslookup web-0.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。在本教程中使用的集群中它们就改变了。这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要。

如果你需要查找并连接一个 StatefulSet 的活动成员,你应该查询 Headless Service 的 CNAME。和 CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。

如果你的应用已经实现了用于测试 liveness 和 readiness 的连接逻辑,你可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local)。因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。

写入稳定的存储

获取 web-0 和 web-1 的 PersistentVolumeClaims。

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

StatefulSet 控制器创建了两个 PersistentVolumeClaims,绑定到两个 PersistentVolumes。由于本教程使用的集群配置为动态提供 PersistentVolume,所有的 PersistentVolume 都是自动创建和绑定的。

NGINX web 服务器默认会加载位于 /usr/share/nginx/html/index.html 的 index 文件。StatefulSets spec 中的 volumeMounts 字段保证了 /usr/share/nginx/html 文件夹由一个 PersistentVolume 支持。

将Pod的主机名写入它们的 index.html 文件并验证 NGINX web 服务器使用该主机名提供服务。

for i in 0 1; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
web-0
web-1

请注意,如果你看见上面的 curl 命令返回了 403 Forbidden 的响应,你需要像这样修复使用 volumeMounts (due to a bug when using hostPath volumes)挂载的目录的权限:

for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done

在你重新尝试上面的 curl 命令之前。

在一个终端查看 StatefulSet 的 Pod。

kubectl get pod -w -l app=nginx

在另一个终端删除 StatefulSet 所有的 Pod。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

在第一个终端里检查 kubectl get 命令的输出,等待所有 Pod 变成 Running 和 Ready 状态。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

验证所有 web 服务器在继续使用它们的主机名提供服务。

for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
web-0
web-1

虽然 web-0 和 web-1 被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的 PersistentVolumeClaim 相关联的 PersistentVolume 被重新挂载到了各自的 volumeMount上。不管 web-0 和 web-1 被调度到了哪个节点上,它们的 PersistentVolumes 将会被挂载到合适的挂载点上。

扩容/缩容 StatefulSet

扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新 replicas 字段完成。你可以使用kubectl scale 或者kubectl patch来扩容/缩容一个 StatefulSet。

扩容

在一个终端窗口观察 StatefulSet 的 Pod。

kubectl get pods -w -l app=nginx

在另一个终端窗口使用 kubectl scale 扩展副本数为5。

kubectl scale sts web --replicas=5
statefulset "web" scaled

在第一个 终端中检查 kubectl get 命令的输出,等待增加的3个 Pod 的状态变为 Running 和 Ready。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

StatefulSet 控制器扩展了副本的数量。如同创建 StatefulSet所述,StatefulSet 按序号索引顺序的创建每个 Pod,并且会等待前一个 Pod 变为 Running 和 Ready 才会启动下一个Pod。

缩容

在一个终端观察 StatefulSet 的 Pod。

kubectl get pods -w -l app=nginx

在另一个终端使用 kubectl patch 将 StatefulSet 缩容回三个副本。

kubectl patch sts web -p '{"spec":{"replicas":3}}'
"web" patched

等待 web-4 和 web-3 状态变为 Terminating。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

顺序终止 Pod

控制器会按照与 Pod 序号索引相反的顺序每次删除一个 Pod。在删除下一个 Pod 前会等待上一个被完全关闭。

获取 StatefulSet 的 PersistentVolumeClaims。

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

五个 PersistentVolumeClaims 和五个 PersistentVolumes 仍然存在。查看 Pod 的 稳定存储,我们发现当删除 StatefulSet 的 Pod 时,挂载到 StatefulSet 的 Pod 的 PersistentVolumes不会被删除。当这种删除行为是由 StatefulSe t缩容引起时也是一样的。

更新 StatefulSet

Kubernetes 1.7 版本的 StatefulSet 控制器支持自动更新。更新策略由 StatefulSet API Object 的 spec.updateStrategy 字段决定。这个特性能够用来更新一个 StatefulSet 中的 Pod 的 container images, resource requests,以及 limits, labels 和 annotations。

On Delete 策略

OnDelete 更新策略实现了传统(1.7之前)行为,它也是默认的更新策略。当你选择这个更新策略并修改 StatefulSet 的 .spec.template 字段时, StatefulSet 控制器将不会自动的更新Pod。

Patch web StatefulSet 的容器镜像。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.7"}]'
statefulset "web" patched

删除 web-0 Pod。

kubectl delete pod web-0
pod "web-0" deleted

<– Watch the web-0 Pod, and wait for it to transition to Running and Ready. –> 观察 web-0 Pod, 等待它变成 Running 和 Ready。

kubectl get pod web-0 -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          54s
web-0     1/1       Terminating   0         1m
web-0     0/1       Terminating   0         1m
web-0     0/1       Terminating   0         1m
web-0     0/1       Terminating   0         1m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

获取 web StatefulSet 的 Pod 来查看他们的容器镜像。

kubectl get pod -l app=nginx -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
web-0   gcr.io/google_containers/nginx-slim:0.7
web-1   gcr.io/google_containers/nginx-slim:0.8
web-2   gcr.io/google_containers/nginx-slim:0.8

web-0 的镜像已经更新,但 web-1 和 web-2 仍然使用原来的镜像。请删除剩余的 pod 以完成更新操作。

​```shell kubectl delete pod web-1 web-2 pod “web-1” deleted pod “web-2” deleted

观察 StatefulSet 的 Pod,等待它们全部变成 Running 和 Ready。

kubectl get pods -w -l app=nginx NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 8m web-1 1/1 Running 0 4h web-2 1/1 Running 0 23m NAME READY STATUS RESTARTS AGE web-1 1/1 Terminating 0 4h web-1 1/1 Terminating 0 4h web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 0s web-1 0/1 ContainerCreating 0 0s web-2 1/1 Terminating 0 23m web-2 1/1 Terminating 0 23m web-1 1/1 Running 0 4s web-2 0/1 Pending 0 0s web-2 0/1 Pending 0 0s web-2 0/1 ContainerCreating 0 0s web-2 1/1 Running 0 36s

获取 Pod 来查看他们的容器镜像。

```shell
kubectl get pod -l app=nginx -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
web-0   gcr.io/google_containers/nginx-slim:0.7
web-1   gcr.io/google_containers/nginx-slim:0.7
web-2   gcr.io/google_containers/nginx-slim:0.7

现在,StatefulSet 中的 Pod 都已经运行了新的容器镜像。

Rolling Update 策略

RollingUpdate 更新策略会更新一个 StatefulSet 中所有的 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证。

Patch web StatefulSet 来执行 RollingUpdate 更新策略。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}
statefulset "web" patched

在一个终端窗口中 patch web StatefulSet 来再次的改变容器镜像。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]'
statefulset "web" patched

在另一个终端监控 StatefulSet 中的 Pod。

kubectl get po -l app=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod, 使用的是它们当前的版本。已经接收到更新请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。像这样,控制器尝试继续使应用保持健康并在出现间歇性故障时保持更新的一致性。

获取 Pod 来查看他们的容器镜像。

for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
gcr.io/google_containers/nginx-slim:0.8
gcr.io/google_containers/nginx-slim:0.8
gcr.io/google_containers/nginx-slim:0.8

StatefulSet 中的所有 Pod 现在都在运行之前的容器镜像。

小窍门:你还可以使用 kubectl rollout status sts/<name> 来查看 rolling update 的状态。

分段更新

你可以使用 RollingUpdate 更新策略的 partition 参数来分段更新一个 StatefulSet。分段的更新将会使 StatefulSet 中的其余所有 Pod 保持当前版本的同时仅允许改变 StatefulSet 的.spec.template。

Patch web StatefulSet 来对 updateStrategy 字段添加一个分区。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset "web" patched

再次 Patch StatefulSet 来改变容器镜像。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.7"}]'
statefulset "web" patched

删除 StatefulSet 中的 Pod。

kubectl delete po web-2
pod "web-2" deleted

等待 Pod 变成 Running 和 Ready。

kubectl get po -lapp=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

获取 Pod 的容器。

kubectl get po web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.8

请注意,虽然更新策略是 RollingUpdate,StatefulSet 控制器还是会使用原始的容器恢复 Pod。这是因为 Pod 的序号比 updateStrategy 指定的 partition 更小。

灰度扩容

你可以通过减少 上文指定的 partition 来进行灰度扩容,以此来测试你的程序的改动。

Patch StatefulSet 来减少分区。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset "web" patched

等待 web-2 变成 Running 和 Ready。

kubectl get po -lapp=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

获取 Pod 的容器。

kubectl get po web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.7

当你改变 partition 时,StatefulSet 会自动的更新 web-2 Pod,这是因为 Pod 的序号小于或等于 partition。

删除 web-1 Pod。

kubectl delete po web-1
pod "web-1" deleted

等待 web-1 变成 Running 和 Ready。

kubectl get po -lapp=nginx -w
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

获取 web-1 Pod 的容器。

kubectl get po web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.8

web-1 被按照原来的配置恢复,因为 Pod 的序号小于分区。当指定了分区时,如果更新了 StatefulSet 的 .spec.template,则所有序号大于或等于分区的 Pod 都将被更新。如果一个序号小于分区的 Pod 被删除或者终止,它将被按照原来的配置恢复。

分阶段的扩容

你可以使用类似灰度扩容的方法执行一次分阶段的扩容(例如一次线性的、等比的或者指数形式的扩容)。要执行一次分阶段的扩容,你需要设置 partition 为希望控制器暂停更新的序号。

分区当前为2。请将分区设置为0。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset "web" patched

等待 StatefulSet 中的所有 Pod 变成 Running 和 Ready。

kubectl get po -lapp=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

获取 Pod 的容器。

for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
gcr.io/google_containers/nginx-slim:0.7
gcr.io/google_containers/nginx-slim:0.7
gcr.io/google_containers/nginx-slim:0.7

将 partition 改变为 0 以允许StatefulSet控制器继续更新过程。

删除 StatefulSet

StatefulSet 同时支持级联和非级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除。使用级联删除时,StatefulSet 和它的 Pod 都会被删除。

非级联删除

在一个终端窗口查看 StatefulSet 中的 Pod。

kubectl get pods -w -l app=nginx

使用 kubectl delete 删 除StatefulSet。请确保提供了 --cascade=false 参数给命令。这个参数告诉 Kubernetes 只删除 StatefulSet 而不要删除它的任何 Pod。

kubectl delete statefulset web --cascade=false
statefulset "web" deleted

获取 Pod 来检查他们的状态。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

虽然 web 已经被删除了,但所有 Pod 仍然处于 Running 和 Ready 状态。 删除 web-0。

kubectl delete pod web-0
pod "web-0" deleted

获取 StatefulSet 的 Pod。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

由于 web StatefulSet 已经被删除,web-0 没有被重新启动。

在一个终端监控 StatefulSet 的 Pod。

kubectl get pods -w -l app=nginx

在另一个终端里重新创建 StatefulSet。请注意,除非你删除了 nginx Service (你不应该这样做),你将会看到一个错误,提示 Service 已经存在。

kubectl create -f web.yaml 
statefulset "web" created
Error from server (AlreadyExists): error when creating "web.yaml": services "nginx" already exists

请忽略这个错误。它仅表示 kubernetes 进行了一次创建 nginx Headless Service 的尝试,尽管那个 Service 已经存在。

在第一个终端中运行并检查 kubectl get 命令的输出。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

当重新创建 web StatefulSet 时,web-0 被第一个重新启动。由于 web-1 已经处于 Running 和 Ready 状态,当 web-0 变成 Running 和 Ready 时,StatefulSet 会直接接收这个 Pod。由于你重新创建的 StatefulSet 的 replicas 等于2,一旦 web-0 被重新创建并且 web-1 被认为已经处于 Running 和 Ready 状态时,web-2 将会被终止。

让我们再看看被 Pod 的 web 服务器加载的 index.html 的内容。

for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
web-0
web-1

尽管你同时删除了 StatefulSet 和 web-0 Pod,但它仍然使用最初写入 index.html 文件的主机名进行服务。这是因为 StatefulSet 永远不会删除和一个 Pod 相关联的 PersistentVolumes。当你重建这个 StatefulSet 并且重新启动了 web-0 时,它原本的 PersistentVolume 会被重新挂载。

级联删除

在一个终端窗口观察 StatefulSet 里的 Pod。

kubectl get pods -w -l app=nginx

在另一个窗口中再次删除这个 StatefulSet。这次省略 --cascade=false 参数。

kubectl delete statefulset web
statefulset "web" deleted

在第一个终端检查 kubectl get 命令的输出,并等待所有的 Pod 变成 Terminating 状态。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

如同你在缩容一节看到的,Pod 按照和他们序号索引相反的顺序每次终止一个。在终止一个 Pod 前,StatefulSet 控制器会等待 Pod 后继者被完全终止。

请注意,虽然级联删除会删除 StatefulSet 和它的 Pod,但它并不会删除和 StatefulSet 关联 的Headless Service。你必须手动删除 nginx Service。

kubectl delete service nginx
service "nginx" deleted

再一次重新创建 StatefulSet 和 Headless Service。

kubectl create -f web.yaml 
service "nginx" created
statefulset "web" created

当 StatefulSet 所有的 Pod 变成 Running 和 Ready 时,获取它们的 index.html 文件的内容。

for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
web-0
web-1

即使你已经删除了 StatefulSet 和它的全部 Pod,这些 Pod 将会被重新创建并挂载它们的 PersistentVolumes,并且 web-0 和 web-1 将仍然使用它们的主机名提供服务。

最后删除 web StatefulSet 和 nginx service。

kubectl delete service nginx
service "nginx" deleted

kubectl delete statefulset web
statefulset "web" deleted

Pod 管理策略

对于某些分布式系统来说,StatefulSet 的顺序性保证是不必要和/或者不应该的。这些系统仅仅要求唯一性和身份标志。为了解决这个问题,在 Kubernetes 1.7 中我们针对 StatefulSet API Object 引入了 .spec.podManagementPolicy。

OrderedReady Pod 管理策略

OrderedReady pod 管理策略是 StatefulSets 的默认选项。它告诉 StatefulSet 控制器遵循上文展示的顺序性保证。

Parallel Pod 管理策略

Parallel pod 管理策略告诉 StatefulSet 控制器并行的终止所有 Pod,在启动或终止另一个 Pod 前,不必等待这些 Pod 变成 Running 和 Ready 或者完全终止状态。

webp.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: gcr.io/google_containers/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

下载上面的例子并保存为 webp.yaml。

这份清单和你在上文下载的完全一样,只是 web StatefulSet 的 .spec.podManagementPolicy 设置成了 Parallel。

在一个终端窗口查看 StatefulSet 中的 Pod。

kubectl get po -lapp=nginx -w

在另一个终端窗口创建清单中的 StatefulSet 和 Service。

kubectl create -f webp.yaml 
service "nginx" created
statefulset "web" created

查看你在第一个终端中运行的 kubectl get 命令的输出。

kubectl get po -lapp=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

StatefulSet 控制器同时启动了 web-0 和 web-1。

保持第二个终端打开,并在另一个终端窗口中扩容 StatefulSet。

kubectl scale statefulset/web --replicas=4
statefulset "web" scaled

在 kubectl get 命令运行的终端里检查它的输出。

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s

<! The StatefulSet controller launched two new Pods, and it did not wait for the first to become Running and Ready prior to launching the second.

Keep this terminal open, and in another terminal delete the web StatefulSet. –> StatefulSet 控制器启动了两个新的 Pod,而且在启动第二个之前并没有等待第一个变成 Running 和 Ready 状态。

保持这个终端打开,并在另一个终端删除 web StatefulSet。

kubectl delete sts web

在另一个终端里再次检查 kubectl get 命令的输出。

web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

StatefulSet 控制器将并发的删除所有 Pod,在删除一个 Pod 前不会等待它的顺序后继者终止。

关闭 kubectl get 命令运行的终端并删除 nginx Service。

kubectl delete svc nginx

Cleaning up

你需要删除本教程中用到的 PersistentVolumes 的持久化存储媒体。基于你的环境、存储配置和提供方式,按照必须的步骤保证回收所有的存储。

本文由xiaosuiba翻译,点击查看原文链接

K8S中文社区微信公众号

Kubernetes DNS Pod 与 Service 介绍

介绍

Kubernetes 从 1.3 版本起, DNS 是内置的服务,通过插件管理器 集群插件 自动被启动。

Kubernetes DNS 在集群中调度 DNS Pod 和 Service ,配置 kubelet 以通知个别容器使用 DNS Service 的 IP 解析 DNS 名字。

怎样获取 DNS 名字?

在集群中定义的每个 Service(包括 DNS 服务器自身)都会被指派一个 DNS 名称。默认,一个客户端 Pod 的 DNS 搜索列表将包含该 Pod 自己的 Namespace 和集群默认域。可以通过如下示例进行说明:

假设在 Kubernetes 集群的 Namespace bar 中,定义了一个Service foo。运行在Namespace bar 中的一个 Pod,可以简单地通过 DNS 查询 foo 来找到该 Service。运行在 Namespace quux 中的一个 Pod 可以通过 DNS 查询 foo.bar 找到该 Service。

支持的 DNS 模式

下面各段详细说明支持的记录类型和布局。如果任何其它的布局、名称或查询,碰巧也能够使用,这就需要研究下它们的实现细节,以免后续修改它们又不能使用了。

Service

A 记录

“正常” Service(除了Headless Service)会以 my-svc.my-namespace.svc.cluster.local 这种名字的形式被指派一个 DNS A 记录。这会解析成该 Service 的 Cluster IP。

“Headless” Service(没有Cluster IP)也会以 my-svc.my-namespace.svc.cluster.local 这种名字的形式被指派一个 DNS A 记录。不像正常 Service,它会解析成该 Service 选择的一组 Pod 的 IP。希望客户端能够使用这一组 IP,否则就使用标准的 round-robin 策略从这一组 IP 中进行选择。

SRV 记录

命名端口需要创建 SRV 记录,这些端口是正常 Service或 Headless Services 的一部分。 对每个命名端口,SRV 记录具有 _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local 这种形式。 对普通 Service,这会被解析成端口号和 CNAME:my-svc.my-namespace.svc.cluster.local。 对 Headless Service,这会被解析成多个结果,Service 对应的每个 backend Pod各一个,包含 auto-generated-name.my-svc.my-namespace.svc.cluster.local 这种形式 Pod 的端口号和 CNAME。

后向兼容性

上一版本的 kube-dns 使用 my-svc.my-namespace.cluster.local 这种形式的名字(后续会增加 ‘svc’ 这一级),以后这将不再被支持。

Pod

A 记录

如果启用,Pod 会以 pod-ip-address.my-namespace.pod.cluster.local 这种形式被指派一个 DNS A 记录。

例如,default Namespace 具有 DNS 名字 cluster.local,在该 Namespace 中一个 IP 为 1.2.3.4 的 Pod 将具有一个条目:1-2-3-4.default.pod.cluster.local。

基于 Pod hostname、subdomain 字段的 A 记录和主机名

当前,创建 Pod 后,它的主机名是该 Pod 的 metadata.name 值。

在 v1.2 版本中,用户可以配置 Pod annotation, 通过 pod.beta.kubernetes.io/hostname 来设置 Pod 的主机名。 如果为 Pod 配置了 annotation,会优先使用 Pod 的名称作为主机名。 例如,给定一个 Pod,它具有 annotation pod.beta.kubernetes.io/hostname: my-pod-name,该 Pod 的主机名被设置为 “my-pod-name”。

在 v1.3 版本中,PodSpec 具有 hostname 字段,可以用来指定 Pod 的主机名。这个字段的值优先于 annotation pod.beta.kubernetes.io/hostname。 在 v1.2 版本中引入了 beta 特性,用户可以为 Pod 指定 annotation,其中 pod.beta.kubernetes.io/subdomain 指定了 Pod 的子域名。 最终的域名将是 “ ...svc.”。 举个例子,Pod 的主机名 annotation 设置为 “foo”,子域名 annotation 设置为 “bar”,在 Namespace “my-namespace” 中对应的 FQDN 为 “foo.bar.my-namespace.svc.cluster.local”。

在 v1.3 版本中,PodSpec 具有 subdomain 字段,可以用来指定 Pod 的子域名。这个字段的值优先于 annotation pod.beta.kubernetes.io/subdomain 的值。

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
    - name: foo # Actually, no port is needed.
      port: 1234 
      targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    name: busybox

如果 Headless Service 与 Pod 在同一个 Namespace 中,它们具有相同的子域名,集群的 KubeDNS 服务器也会为该 Pod 的完整合法主机名返回 A 记录。在同一个 Namespace 中,给定一个主机名为 “busybox-1” 的 Pod,子域名设置为 “default-subdomain”,名称为 “default-subdomain” 的 Headless Service ,Pod 将看到自己的 FQDN 为 “busybox-1.default-subdomain.my-namespace.svc.cluster.local”。DNS 会为那个名字提供一个 A 记录,指向该 Pod 的 IP。“busybox1” 和 “busybox2” 这两个 Pod 分别具有它们自己的 A 记录。

在Kubernetes v1.2 版本中,Endpoints 对象也具有 annotation endpoints.beta.kubernetes.io/hostnames-map。它的值是 map[string(IP)][endpoints.HostRecord] 的 JSON 格式,例如: ‘{“10.245.1.6”:{HostName: “my-webserver”}}’。

如果是 Headless Service 的 Endpoints,会以 ...svc. 的格式创建 A 记录。对示例中的 JSON 字符串,如果 `Endpoints` 是为名称为 “bar” 的 Headless Service 而创建的,其中一个 `Endpoints` 的 IP 是 “10.245.1.6”,则会创建一个名称为 “my-webserver.bar.my-namespace.svc.cluster.local” 的 A 记录,该 A 记录查询将返回 “10.245.1.6”。

Endpoints annotation 通常没必要由最终用户指定,但可以被内部的 Service Controller 用来提供上述功能。

在 v1.3 版本中,Endpoints 对象可以为任何 endpoint 指定 hostname 和 IP。hostname 字段优先于通过 endpoints.beta.kubernetes.io/hostnames-map annotation 指定的主机名。

在 v1.3 版本中,下面的 annotation 是过时的:pod.beta.kubernetes.io/hostname、pod.beta.kubernetes.io/subdomain、endpoints.beta.kubernetes.io/hostnames-map。

如何测试它是否可以使用?

创建一个简单的 Pod 作为测试环境

创建 busybox.yaml 文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always

然后,用该文件创建一个 Pod:

kubectl create -f busybox.yaml

等待这个 Pod 变成运行状态

获取它的状态,执行如下命令:

kubectl get pods busybox

可以看到如下内容:

NAME      READY     STATUS    RESTARTS   AGE
busybox   1/1       Running   0          <some-time>

验证 DNS 已经生效

一旦 Pod 处于运行中状态,可以在测试环境中执行如下 nslookup 查询:

kubectl exec -ti busybox -- nslookup kubernetes.default

可以看到类似如下的内容:

Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      kubernetes.default
Address 1: 10.0.0.1

如果看到了,说明 DNS 已经可以正确工作了。

问题排查技巧

如果执行 nslookup 命令失败,检查如下内容:

先检查本地 DNS 配置

查看配置文件 resolv.conf。(关于更多信息,参考下面的 “从 Node 继承 DNS” 和 “已知问题”。)

kubectl exec busybox cat /etc/resolv.conf

按照如下方法(注意搜索路径可能会因为云提供商不同而变化)验证搜索路径和 Name Server 的建立:

search default.svc.cluster.local svc.cluster.local cluster.local google.internal c.gce_project_id.internal
nameserver 10.0.0.10
options ndots:5

快速诊断

出现类似如下指示的错误,说明 kube-dns 插件或相关 Service 存在问题:

$ kubectl exec -ti busybox -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10

nslookup: can't resolve 'kubernetes.default'

或者

$ kubectl exec -ti busybox -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'kubernetes.default'

检查是否 DNS Pod 正在运行

使用 kubectl get pods 命令验证 DNS Pod 正在运行:

kubectl get pods --namespace=kube-system -l k8s-app=kube-dns

应该能够看到类似如下信息:

NAME                                                       READY     STATUS    RESTARTS   AGE
...
kube-dns-v19-ezo1y                                         3/3       Running   0           1h
...

如果看到没有 Pod 运行,或 Pod 失败/结束,DNS 插件不能默认部署到当前的环境,必须手动部署。

检查 DNS Pod 中的错误信息

使用 kubectl logs 命令查看 DNS 后台进程的日志:

kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c kubedns
kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c dnsmasq
kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c healthz

查看是否有任何可疑的日志。在行开头的字母 W、E、F 分别表示 警告、错误、失败。请搜索具有这些日志级别的日志行,通过 Kubernetes 问题 报告意外的错误。

DNS 服务是否运行?

通过使用 kubectl get service 命令,验证 DNS 服务是否运行:

kubectl get svc --namespace=kube-system

应该能够看到:

NAME                    CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
...
kube-dns                10.0.0.10      <none>        53/UDP,53/TCP        1h
...

如果服务已经创建,或在这个例子中默认被创建,但是并没有看到,可以查看 调试 Service 页面 获取更多信息。

kubectl get ep kube-dns --namespace=kube-system

应该能够看到类似如下信息:

NAME       ENDPOINTS                       AGE
kube-dns   10.180.3.17:53,10.180.3.17:53    1h

如果没有看到 Endpoint,查看 调试 Service 文档 中的 Endpoint 段内容。

关于更多 Kubernetes DNS 的示例,参考 Kubernetes GitHub 仓库中 集群 DNS 示例

Kubernetes Federation(多 Zone 支持)

在1.3 发行版本中,为多站点 Kubernetes 安装引入了集群 Federation 支持。这需要对 Kubernetes 集群 DNS 服务器处理 DNS 查询的方式,做出一些微小(后向兼容)改变,从而便利了对联合 Service 的查询(跨多个 Kubernetes 集群)。参考 集群 Federation 管理员指南 获取更多关于集群 Federation 和多站点支持的细节。

工作原理

运行的 Kubernetes DNS Pod 包含 3 个容器 —— kubedns、dnsmasq 和负责健康检查的 healthz。 kubedns 进程监视 Kubernetes master 对 Service 和 Endpoint 操作的变更,并维护一个内存查询结构去处理 DNS 请求。dnsmasq 容器增加了一个 DNS 缓存来改善性能。为执行对 dnsmasq 和 kubedns 的健康检查,healthz 容器提供了一个单独的健康检查 Endpoint。

DNS Pod 通过一个静态 IP 暴露为一个 Service。一旦 IP 被分配,kubelet 会通过 --cluster-dns=10.0.0.10 标志将配置的 DNS 传递给每一个容器。

DNS 名字也需要域名,本地域名是可配置的,在 kubelet 中使用 --cluster-domain=<default local domain> 标志。

Kubernetes 集群 DNS 服务器(根据 SkyDNS 库)支持正向查询(A 记录),Service 查询(SRV 记录)和反向 IP 地址查询(PTR 记录)。

从 Node 继承 DNS

当运行 Pod 时,kubelet 将集群 DNS 服务器和搜索路径追加到 Node 自己的 DNS 设置中。如果 Node 能够在大型环境中解析 DNS 名字,Pod 也应该没问题。参考下面 “已知问题” 中给出的更多说明。

如果不想这样,或者希望 Pod 有一个不同的 DNS 配置,可以使用 kubelet 的 --resolv-conf 标志。设置为 “” 表示 Pod 将不继承自 DNS。设置为一个合法的文件路径,表示 kubelet 将使用这个文件而不是 /etc/resolv.conf 。

已知问题

Kubernetes 安装但并不配置 Node 的 resolv.conf 文件,而是默认使用集群 DNS的,因为那个过程本质上就是和特定的发行版本相关的。最终应该会被实现。

Linux libc 在限制为3个 DNS nameserver 记录和3个 DNS search 记录是不可能卡住的(查看 2005 年的一个 Bug)。Kubernetes 需要使用1个 nameserver 记录和3个 search 记录。这意味着如果本地安装已经使用了3个 nameserver 或使用了3个以上 search,那些设置将会丢失。作为部分解决方法, Node 可以运行 dnsmasq ,它能提供更多 nameserver 条目,但不能运行更多 search 条目。可以使用 kubelet 的 --resolv-conf 标志。

如果使用 3.3 版本的 Alpine 或更早版本作为 base 镜像,由于 Alpine 的一个已知问题,DNS 可能不会正确工作。查看 这里 获取更多信息。

本文由 shirdrn 翻译,原文链接

K8S中文社区微信公众号