Node,Pod,Replication Controller,Service等都是k8s中的一种“资源对象”,都可以通过工具kubectl执行增、删、改的管理操作。其配置结果是保存在etcd中。k8s就像是一个自动化的资源控制系统,通过对etcd库中保存的资源期望状态和实际资源状态进行差异对比,来发现、控制和进行“纠错”。


1、k8s集群管理角色——Master

每个k8s集群里需要一个Master节点来负责整个集群的管理和控制,通常要占据一个独立的服务器,从高可用角度考虑则建议使用3台服务器。

Master节点上运行着以下关键进程或服务:

k8s API Server,提供了HTTP Rest接口的关键服务进程,是集群中所有资源增、删、改操作的唯一入口,也是控制集群的唯一入口。

k8s Controller Manager,是集群中所有资源对象的运行指挥中心。

k8s Scheduler,是负责调度Pod资源的进程。

etcd服务,提供集群中所有资源对象配置的持久化。

2、k8s集群管理角色——Node

Node是k8s集群中的工作负载节点,当某个Node当机时,其上的工作负载会被Master自动转移到其他节点上去。

Node节点上运行着以下关键进程或服务:

Kublete,负责Pod对应的容器的创建、启停管理,与Master节点协作,实现集群管理的基本功能。

kube-proxy,是提供k8s的通信与负载均衡功能的重要组件。

Docker Engine,docker引擎。

Node节点可以动态地加入到k8s集群中,在这个过程中kublete找到Master完成信息注册。在加入集群后,kublete进程就定时向Master节点发送操作系统、Docker版本、CPU、内存的统计信息,以及当前有哪些Pods在运行。当某个Node超时未发送以上信息时,会被Master判定为“失联”(Not Ready),随即触发工作负载转移的流程。

查看集群中有哪些Nodes:

[root@gqtest ~]# kubectl get nodes

NAME        STATUS    AGE

127.0.0.1   Ready     3d

查看某个Node的详细信息:

[root@gqtest ~]# kubectl describe node "127.0.0.1"

Name:            127.0.0.1

Role:            

Labels:            beta.kubernetes.io/arch=amd64

            beta.kubernetes.io/os=linux

            kubernetes.io/hostname=127.0.0.1

Taints:            <none>

CreationTimestamp:    Sat, 17 Feb 2018 22:14:43 +0800

Phase:            

Conditions:

  Type            Status    LastHeartbeatTime            LastTransitionTime            Reason                Message

  ----            ------    -----------------            ------------------            ------                -------

  OutOfDisk         False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasSufficientDisk     kubelet has sufficient disk space available

  MemoryPressure     False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasSufficientMemory     kubelet has sufficient memory available

  DiskPressure         False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasNoDiskPressure     kubelet has no disk pressure

  Ready         True     Wed, 21 Feb 2018 10:40:50 +0800     Wed, 21 Feb 2018 10:40:00 +0800     KubeletReady             kubelet is posting ready status

Addresses:        127.0.0.1,127.0.0.1,127.0.0.1

Capacity:

alpha.kubernetes.io/nvidia-gpu:    0

cpu:                    2

memory:                1532144Ki

pods:                    110

Allocatable:

alpha.kubernetes.io/nvidia-gpu:    0

cpu:                    2

memory:                1532144Ki

pods:                    110

System Info:

Machine ID:            3be0a8ad023f4dd0b530ddcaeecf83cd

System UUID:            E351A3F3-7D82-4C97-A174-297F8526DDBD

Boot ID:            d40f85db-90fe-4904-92cc-847b6f136058

Kernel Version:        3.10.0-693.17.1.el7.x86_64

OS Image:            CentOS Linux 7 (Core)

Operating System:        linux

Architecture:            amd64

Container Runtime Version:    docker://1.12.6

Kubelet Version:        v1.5.2

Kube-Proxy Version:        v1.5.2

ExternalID:            127.0.0.1

Non-terminated Pods:        (3 in total)

  Namespace            Name            CPU Requests    CPU Limits    Memory Requests    Memory Limits

  ---------            ----            ------------    ----------    ---------------    -------------

  default            mysql-mn49n        0 (0%)        0 (0%)        0 (0%)        0 (0%)

  default            myweb-k6fp0        0 (0%)        0 (0%)        0 (0%)        0 (0%)

  default            myweb-m9nv9        0 (0%)        0 (0%)        0 (0%)        0 (0%)

Allocated resources:

  (Total limits may be over 100 percent, i.e., overcommitted.

  CPU Requests    CPU Limits    Memory Requests    Memory Limits

  ------------    ----------    ---------------    -------------

  0 (0%)    0 (0%)        0 (0%)        0 (0%)

Events:

  FirstSeen    LastSeen    Count    From            SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----            -------------    --------    ------            -------

  1m        1m        1    {kubelet 127.0.0.1}            Normal        Starting        Starting kubelet.

  1m        1m        1    {kubelet 127.0.0.1}            Warning        ImageGCFailed        unable to find data for container /

  1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasSufficientDisk    Node 127.0.0.1 status is now: NodeHasSufficientDisk

  1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasSufficientMemory    Node 127.0.0.1 status is now: NodeHasSufficientMemory

  1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasNoDiskPressure    Node 127.0.0.1 status is now: NodeHasNoDiskPressure

  1m        1m        1    {kubelet 127.0.0.1}            Warning        Rebooted        Node 127.0.0.1 has been rebooted, boot id: d40f85db-90fe-4904-92cc-847b6f136058

  1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeNotReady        Node 127.0.0.1 status is now: NodeNotReady

  58s        58s        1    {kubelet 127.0.0.1}            Normal        NodeReady        Node 127.0.0.1 status is now: NodeReady

  1m        55s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-k6fp0_default(3ac7a156-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.

  1m        53s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-m9nv9_default(3ac7b4c7-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.

  1m        48s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "mysql-mn49n_default(cee70dc4-140e-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.


3、k8s集群最小资源调度管理单位——Pod

一般情况下,Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。

注:在单个Pod中共同管理多个容器是一个相对高级的用法,应该只有在容器紧密耦合的特殊实例中使用此模式。


Kubernetes中的Pod使用可分两种主要方式:

Pod中运行一个容器。“one-container-per-Pod”模式是Kubernetes最常见的用法。在这种情况下,你可以将Pod视为单个封装的容器,但是Kubernetes是直接管理Pod而不是容器。

Pods中运行多个需要一起工作的容器。Pod可以封装紧密耦合的应用,它们需要由多个容器组成,它们之间能够共享资源,这些容器可以形成一个单一的内部service单位。

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。


Pods提供两种共享资源:网络和存储。

网络,每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。k8s要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这一般是采用虚拟二层网络技术来实现的,如Open vSwitch。

存储,Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。

Pod的几个关键知识点:

很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kuberentes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod。

Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。通常,Controller会用你提供的Pod Template来创建相应的Pod。

Pod都会包含一个特殊的“根容器”,一个或多个业务容器。当根容器“死亡”了时,代表该Pod整个容器组的不可用。

Endpoint,Pod的IP加上Pod里的容器端口(containerPort)称为一个Endpoint,代表着此Pod里的一个服务进程的对外通信地址。

Pod Event事件,可以使用kubectl describe pod xxxx查看一个Pod的详细信息,其中包括了该Pod所发生的每个事件的信息。Event事件信息对于故障排查很有帮助。

Pod的类型:

普通Pod

静态Pod,不受etcd的配置信息管控,直接存放在某个Node上且只在该Node上运行。

Pod的资源限额:

CPU限额,资源限制的单位为cpu Core的数量,该资源是以绝对值计算的。k8s将1/1000的CPU Core的资源定义为可供分配的最小CPU资源单位,称为m。通常一个容器的CPU配额被定义为100~300m,即占用0.1~0.3个CPU Core。因为该配额是一个资源的绝对值,所以无论是在一个只有2 CPU Cores的机器上,还是在一个有48CPU Cores的机器上,100m所代表的CPU使用量都是一样的。

内存限额,内存的资源限制是以节节为计量单位,内存的配额也是按资源的绝对值进行分配的。

k8s中配置资源限额的方法:

Requests设置,一个资源的最小申请量,是系统必须满足的一个用量;

Limits设置,该资源的最大允许使用量,不能被突破,当容器试图突破该参数限制时,可能会被k8s Kii并重启。

一般是结合使用以上两个参数去定义一个资源的配额,如下所示。

spec:

  containers:

  - name: db

    image: mysql

    resources:

      requests:

        memory: "64Mi"

        cpu: "250m"

      limits:

        memory: "128Mi"

        cpu: "500m"


4、Label和Label Selector

Label是一个key/value的键值对,可以附加到各种资源对象上。

一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。

通常是在资源对象定义时确定Label,也支持在对象创建后动态添加或删除。

Label和Lable Selector构成了k8s系统中最核心的一个应用模型,可以对被管理对象进行精细分组,以实现整个集群的高可用。

通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理。一些示例标签如下:

"release" : "stable", "release" : "canary"

"environment" : "dev","environment" : "qa","environment" : "production"

"tier" : "frontend","tier" : "backend","tier" : "cache"

"partition" : "customerA", "partition" : "customerB"

"track" : "daily", "track" : "weekly"

Label Selector可以查询和筛选拥有某些Label的资源对象。

目前支持两种选择器:equality-based(基于平等)和set-based(基于集合)的。前者采用等式的表达式,后者采用集合操作的表达式匹配标签。

标签选择器可以由逗号分隔的多个requirements 组成。在多重需求的情况下,必须满足所有要求,因此逗号分隔符作为AND逻辑运算符。

equality-based示例:

name = redis-slave

env != production

set-based示例:

name in (redis-master, redis-slave)

name not in (frontend)

Label Selector set-based筛选功能仅在以下新出现的管理对象中得到了支持:

Deployment

ReplicaSet

DaemonSet

Job

Label Selector的几个重要使用场景:

kube-controller进程,通过资源对象RC上定义的Label Selector来筛选要监控和管理的Pod副本的数量。

kube-proxy进程,通过Service的Lable Selector来选择对应的Pod,建立起每个Service到对应Pod的请求转发路由表,进而实现Service的智能负载均衡机制。

kube-scheduler进程,通过为某些Node定义特定的Label,然后在Pod定义文件中使用NodeSelector进行筛选,进而实现Pod的定向调度功能。

5、Replication Controller与Replica Set

ReplicationController(简称RC),定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值。通过RC,k8s实现了用户应用集群的高可用性,同时大减少了系统管理员在传统IT环境中需要完成的许多手工运维工作(如主机监控、应用监控和故障恢复等)。

ReplicationController是早期k8s版本中主要使用的一项技术,在较新版本中,RC的功能已经逐渐被功能更强大的ReplicaSet和Deployment的组合所替代。

因为ReplicationController、ReplicaSet和Deployment之间存在着巨大的相似性,所以仍然有了解和掌握RC知识和使用方法的必要。

RC会包含以下几个部分:

Pod期待的副本数;

用于筛选目标Pod的Label Selector;

当Pod的副本数量小于预期时,用于创建新Pod的Pod模板。

修改RC的副本数量可以实现Pod的动态缩放:

$ kubectl scale rc redis-slave --replicas=3


关于删除RC:

删除RC并不会影响通过该RC已经创建好的Pod;

为了删除使用该RC创建的Pods,可以设置replicas为0,然后更新该RC;

使用kubectl delete命令删除RC及其所有pod。

关于ReplicaSet的知识:

由于Replication Controller与k8s代码中的模块Replication Controller同名,所以在k8s v1.2时就升级成了另外一个新的工具Replica Set。二者之间唯一的区别是,Replica Set支持了基于集合的Label Selector功能。

ReplicaSet是支持新的set-based选择器要求的下一代ReplicationController 。它主要用作Deployment协调pod创建、删除和更新。虽然ReplicaSets可以独立使用,但它主要被 Deployments用作pod 机制的创建、删除和更新,建议使用Deployment而不是直接使用ReplicaSets。

ReplicaSet的使用示例(部分):

apiVersion: extensions/v1beta1

kind: ReplicaSet

metadata:

  name: frontend

spec:

  replicas: 3

  selector:

    matchLabels:

      tier: frontend

    matchExpressions:

      - {key: tier, operator: In, values: [frontend]}

  template:

    metadata:

      labels:

        app: guestbook

        tier: frontend

    spec:


RC(Replica Set)的一些特性:

在大多数情况下,我们通过定义一个RC实现Pod的创建过程及副本数量的自动控制。

RC里包括完整的Pod定义模板。

RC通过Label Selector机制实现对Pod副本的自动控制。

通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容功能。

通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能。


6、Deployment

Deployment为Pod和Replica Set(升级版的 Replication Controller)提供声明式更新。

注意:您不该手动管理由 Deployment 创建的 Replica Set,否则您就篡越了 Deployment controller 的职责!


Deployment的典型的用例如下:

创建一个Deployment对象来生成对应的ReplicaSet,并完成Pod副本的创建过程。

检查Deployment的状态来查看部署动作是否完成,Pod副本的数量是否达到预期的值。

更新Deployment以创建新的Pod,通过修改Pod-Template-Spec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。

如果当前状态不稳定,则回滚到一个早先的Deployment版本。

暂停Deployment,以便于一次性修改多个PodTemplateSpec的配置项,然后再恢复Deployment,进行新的发布。

扩容Deployment以满足更高的负载。

查看Deployment 的状态,以此作为发布是否成功的指标。

清除旧的不必要的 ReplicaSets。

(1)创建 Deployment

下面是一个 Deployment 示例,它创建了一个 ReplicaSet 来启动3个 nginx pod。

下载示例文件nginx-deployment.yaml并执行命令:

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  name: nginx-deployment

  labels:

    app: nginx

spec:

  replicas: 1

  selector:

    matchLabels:

      app: nginx

  template:

    metadata:

      labels:

        app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx:1.7.9

        ports:

        - containerPort: 80

执行创建命令:

[root@gqtest ~]# kubectl create -f nginx-deployment.yaml --record

deployment "nginx-deployment" created

注:--record选项可以记录当前命令执行记录,便于以后查看一个deployment revision中执行了哪些命令。

[root@gqtest ~]# kubectl get deployments

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

nginx-deployment   1         1         1            0           2m

看到available数量仍为0,继续查看一下该deployment的详细情况:

[root@gqtest ~]# kubectl describe deployment nginx-deployment

Name:            nginx-deployment

Namespace:        default

CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800

Labels:            app=nginx

Selector:        app=nginx

Replicas:        1 updated | 1 total | 0 available | 1 unavailable

StrategyType:        RollingUpdate

MinReadySeconds:    0

RollingUpdateStrategy:    1 max unavailable, 1 max surge

Conditions:

  Type        Status    Reason

  ----        ------    ------

  Available     True    MinimumReplicasAvailable

OldReplicaSets:    <none>

NewReplicaSet:    nginx-deployment-4087004473 (1/1 replicas created)

Events:

  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----                -------------    --------    ------            -------

  3m        3m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

上面输出信息没有异常,再查看一次该deployment的状态,如下所示,available的实例数量已经是1,说明经过一个创建过程(往往会因为要在线下载docker image镜像而需要等待一段时间),已经成功创建:

[root@gqtest ~]# kubectl get deployments

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

nginx-deployment   1         1         1            1           4m

查看Replica Set信息,可以到到ReplicaSet名称是在Deployment后添加一串数字:

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-4087004473   1         1         1         4m

继续查看pod的状态,如下所示:

[root@gqtest ~]# kubectl get pods

NAME                                READY     STATUS    RESTARTS   AGE

mysql-mn49n                         1/1       Running   1          3d

myweb-k6fp0                         1/1       Running   1          3d

myweb-m9nv9                         1/1       Running   1          3d

nginx-deployment-4087004473-xdxhn   1/1       Running   0          7m


(2)更新Deployment

Deployment的更新(rollout)当且仅当Deployment的pod template中的label发生更新或者镜像发生更改时,才会被触发。像Deployment扩容,不会触发rollout事件。

假如我们现在想要让 nginx pod 使用nginx:1.9.1的镜像来代替原来的nginx:1.7.9的镜像。

[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

deployment "nginx-deployment" image updated


或者可以使用edit命令来编辑 Deployment,修改 .spec.template.spec.containers[0].image ,将nginx:1.7.9 改写成 nginx:1.9.1。

[root@gqtest ~]# kubectl edit deployment/nginx-deployment

查看更新结果:

[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment

Waiting for rollout to finish: 0 of 1 updated replicas are available...

[root@gqtest ~]# kubectl get deployments

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

nginx-deployment   1         1         1            0           18m

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-3599678771   1         1         0         4m

nginx-deployment-4087004473   0         0         0         19m

从上面的输出信息中看到,正在创建一个新的ReplicaSet的过程中,同时已经将旧的缩容到了0个replica。

查看pod状态时,已经看不到旧的pod了:

[root@gqtest ~]# kubectl get pods

NAME                                READY     STATUS    RESTARTS   AGE

mysql-mn49n                         1/1       Running   1          3d

myweb-k6fp0                         1/1       Running   1          3d

myweb-m9nv9                         1/1       Running   1          3d

nginx-deployment-3599678771-chzwr   1/1       Running   0          6m

再次执行查看Deployment更新结果的命令,显示已经成功完成:

[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment

deployment "nginx-deployment" successfully rolled out

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-3599678771   1         1         1         8m

nginx-deployment-4087004473   0         0         0         23m

最后查看一下该Deployment任务的详细信息:

[root@gqtest ~]# kubectl describe deployment nginx-deployment

Name:            nginx-deployment

Namespace:        default

CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800

Labels:            app=nginx

Selector:        app=nginx

Replicas:        1 updated | 1 total | 1 available | 0 unavailable

StrategyType:        RollingUpdate

MinReadySeconds:    0

RollingUpdateStrategy:    1 max unavailable, 1 max surge

Conditions:

  Type        Status    Reason

  ----        ------    ------

  Available     True    MinimumReplicasAvailable

OldReplicaSets:    <none>

NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)

Events:

  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----                -------------    --------    ------            -------

  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

  10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1

  10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0


Rollover(多个rollout并行)的说明:

假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道。


Label selector 更新:

上面的演示是基于image文件发生了更改的条件下的一个Deployment更新的样例。通常不鼓励更新 label selector,虽然技术上可行,但关联影响较多。


(3)回退Deployment

可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。


只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。


我们先故意执行一个有错误的Deployment任务:

[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.99

deployment "nginx-deployment" image updated

我们故障写了一个不存在的镜像文件版本,Deployment rollout任务会补卡住,如下所示:

[root@gqtest ~]# kubectl rollout status deployments nginx-deployment

Waiting for rollout to finish: 0 of 1 updated replicas are available...

可以看到pod的状态处于"ImagePullBackOff"的错误状态:

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-3599678771   0         0         0         23m

nginx-deployment-4087004473   0         0         0         38m

nginx-deployment-538426637    1         1         0         3m

[root@gqtest ~]# kubectl rollout status deployments nginx-deployment

Waiting for rollout to finish: 0 of 1 updated replicas are available...

^C[root@gqtest ~]# kubectl get pods

NAME                               READY     STATUS             RESTARTS   AGE

mysql-mn49n                        1/1       Running            1          3d

myweb-k6fp0                        1/1       Running            1          3d

myweb-m9nv9                        1/1       Running            1          3d

nginx-deployment-538426637-g55n4   0/1       ImagePullBackOff   0          3m

注:Deployment controller会自动停止坏的 rollout,并停止扩容新的 ReplicaSet。

[root@gqtest ~]# kubectl describe deployment nginx-deployment

Name:            nginx-deployment

Namespace:        default

CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800

Labels:            app=nginx

Selector:        app=nginx

Replicas:        1 updated | 1 total | 0 available | 1 unavailable

StrategyType:        RollingUpdate

MinReadySeconds:    0

RollingUpdateStrategy:    1 max unavailable, 1 max surge

Conditions:

  Type        Status    Reason

  ----        ------    ------

  Available     True    MinimumReplicasAvailable

OldReplicaSets:    <none>

NewReplicaSet:    nginx-deployment-538426637 (1/1 replicas created)

Events:

  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----                -------------    --------    ------            -------

  40m        40m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1

  25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0

  5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1

  5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0

我们需要回退到稳定版本的Deployment revision。

先查看一下Deployment的revision记录:

[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment

deployments "nginx-deployment"

REVISION    CHANGE-CAUSE

1        kubectl create -f nginx-deployment.yaml --record

2        kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

3        kubectl set image deployment/nginx-deployment nginx=nginx:1.99

注:因为我们创建 Deployment 的时候使用了--recored参数可以记录命令,我们可以很方便的查看每次 revision 的变化。

查看单个revision 的详细信息:

[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment --revision=2

deployments "nginx-deployment" with revision #2

  Labels:    app=nginx

    pod-template-hash=3599678771

  Annotations:    kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

  Containers:

   nginx:

    Image:    nginx:1.9.1

    Port:    80/TCP

    Volume Mounts:    <none>

    Environment Variables:    <none>

  No volumes.

回退到历史版本

回退到上一个版本:

[root@gqtest ~]# kubectl rollout undo deployment/nginx-deployment

deployment "nginx-deployment" rolled back

回退到一个指定的版本:

# kubectl rollout undo deployment/nginx-deployment --to-revision=2

deployment "nginx-deployment" rolled back


查看下回退结果:

[root@gqtest ~]# kubectl get deployment

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

nginx-deployment   1         1         1            1           46m

查看下回退版本所产生的事件记录:

[root@gqtest ~]# kubectl describe deployment nginx-deployment

Name:            nginx-deployment

Namespace:        default

CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800

Labels:            app=nginx

Selector:        app=nginx

Replicas:        1 updated | 1 total | 1 available | 0 unavailable

StrategyType:        RollingUpdate

MinReadySeconds:    0

RollingUpdateStrategy:    1 max unavailable, 1 max surge

Conditions:

  Type        Status    Reason

  ----        ------    ------

  Available     True    MinimumReplicasAvailable

OldReplicaSets:    <none>

NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)

Events:

  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----                -------------    --------    ------            -------

  46m        46m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

  32m        32m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0

  11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1

  11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0

  32m        2m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1

  2m        2m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2

  2m        2m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0


可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。


(4)Deployment 扩容

使用以下命令将nginx-deployment扩容为3副本:

[root@gqtest ~]# kubectl scale deployment nginx-deployment --replicas 3

deployment "nginx-deployment" scaled

[root@gqtest ~]# kubectl get deployment

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

nginx-deployment   3         3         3            3           51m

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-3599678771   3         3         3         36m

nginx-deployment-4087004473   0         0         0         51m

nginx-deployment-538426637    0         0         0         16m

[root@gqtest ~]# kubectl get pods

NAME                                READY     STATUS    RESTARTS   AGE

mysql-mn49n                         1/1       Running   1          3d

myweb-k6fp0                         1/1       Running   1          3d

myweb-m9nv9                         1/1       Running   1          3d

nginx-deployment-3599678771-hz7l6   1/1       Running   0          7m

nginx-deployment-3599678771-j136m   1/1       Running   0          1m

nginx-deployment-3599678771-kqwpf   1/1       Running   0          1m

查看该Deployment的事件记录:

[root@gqtest ~]# kubectl describe deployment nginx-deployment

Name:            nginx-deployment

Namespace:        default

CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800

Labels:            app=nginx

Selector:        app=nginx

Replicas:        3 updated | 3 total | 3 available | 0 unavailable

StrategyType:        RollingUpdate

MinReadySeconds:    0

RollingUpdateStrategy:    1 max unavailable, 1 max surge

Conditions:

  Type        Status    Reason

  ----        ------    ------

  Available     True    MinimumReplicasAvailable

OldReplicaSets:    <none>

NewReplicaSet:    nginx-deployment-3599678771 (3/3 replicas created)

Events:

  FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message

  ---------    --------    -----    ----                -------------    --------    ------            -------

  52m        52m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

  37m        37m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0

  17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1

  17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0

  37m        7m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1

  7m        7m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2

  7m        7m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0

  1m        1m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 3


注:假如集群中启用了horizontal pod autoscaling,您可以给 Deployment 设置一个 autoscaler,基于当前 Pod的 CPU 利用率选择最少和最多的 Pod 数。


关于Deployment的比例扩容:

RollingUpdate Deployment 支持同时运行一个应用的多个版本。或者 autoscaler 扩 容 RollingUpdate Deployment 的时候,正在中途的 rollout(进行中或者已经暂停的),为了降低风险,Deployment controller 将会平衡已存在的活动中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。这被称为比例扩容。


(5)暂停和恢复Deployment

可以在发出一次或多次更新前暂停一个 Deployment,然后再恢复它。这样您就能多次暂停和恢复 Deployment,在此期间进行一些修复工作,而不会发出不必要的 rollout。

[root@gqtest ~]# kubectl rollout pause deployment/nginx-deployment

deployment "nginx-deployment" paused

[root@gqtest ~]# kubectl set image deploy/nginx-deployment nginx=nginx:1.9.2

deployment "nginx-deployment" image updated

即使执行了上面的镜像更改命令,也没有触发任何Deployment rollout的事件。而且可以继续做更多的修改,例如:

[root@gqtest ~]# kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=256Mi

deployment "nginx-deployment" resource requirements updated

注:-c选项的作用是指定容器唯一标识名

当各种变更都准备妥当后,我们恢复该deployment:

[root@gqtest ~]# kubectl rollout resume deploy nginx-deployment

deployment "nginx-deployment" resumed

查看一下deployment处理结果,使用-w选项(watch)跟踪输出如下所示:

[root@gqtest ~]# kubectl get rs -w

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-27997383     2         2         0         2m

nginx-deployment-3599678771   2         2         2         1h

nginx-deployment-4087004473   0         0         0         1h

nginx-deployment-538426637    0         0         0         59m

NAME                        DESIRED   CURRENT   READY     AGE

nginx-deployment-27997383   2         2         1         4m

nginx-deployment-3599678771   1         2         2         1h

nginx-deployment-3599678771   1         2         2         1h

nginx-deployment-27997383   3         2         1         4m

nginx-deployment-3599678771   1         1         1         1h

nginx-deployment-27997383   3         2         1         4m

nginx-deployment-27997383   3         3         1         4m

nginx-deployment-27997383   3         3         2         5m

nginx-deployment-3599678771   0         1         1         1h

nginx-deployment-3599678771   0         1         1         1h

nginx-deployment-3599678771   0         0         0         1h

可以看到已经成功完成了合并了多次更新内容后的deployment任务:

[root@gqtest ~]# kubectl get rs

NAME                          DESIRED   CURRENT   READY     AGE

nginx-deployment-27997383     3         3         3         13m

nginx-deployment-3599678771   0         0         0         1h

nginx-deployment-4087004473   0         0         0         1h

nginx-deployment-538426637    0         0         0         1h


(6)Deployment 状态

Deployment 在生命周期中有多种状态。在创建一个新的 ReplicaSet 的时候它可以是 progressing 状态, complete 状态,或者 fail to progress 状态。

进行中的 Deployment

Kubernetes 将执行过下列任务之一的 Deployment 标记为 progressing 状态:

Deployment 正在创建新的ReplicaSet过程中。

Deployment 正在扩容一个已有的 ReplicaSet。

Deployment 正在缩容一个已有的 ReplicaSet。

有新的可用的 pod 出现。

可以使用kubectl rollout status命令监控 Deployment 的进度。


完成的 Deployment

Kubernetes 将包括以下特性的 Deployment 标记为 complete 状态:

Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 个数等于或者超过 Deployment 策略中的期望个数。

所有与该 Deployment 相关的replica都被更新到了指定版本,也就说更新完成。

该 Deployment 中没有旧的 Pod 存在。

可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code。

[root@gqtest ~]# kubectl rollout status deploy/nginx-deployment

deployment "nginx-deployment" successfully rolled out

[root@gqtest ~]# echo $?

0


版本记录的清理策略

可以设置 Deployment 中的 .spec.revisionHistoryLimit 项来指定保留多少旧的 ReplicaSet。 余下的将在后台被当作垃圾收集。默认的,所有的 revision 历史就都会被保留。在未来的版本中,将会更改为2。

注意: 将该值设置为0,将导致所有的 Deployment 历史记录都会被清除,该 Deployment 就无法再回退了。


(7)金丝雀 Deployment

如果想要使用 Deployment 对部分用户或服务器发布 release,可以创建多个 Deployment,每个 Deployment 对应一个 release。


(8)编写 Deployment Spec

在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata这些配置项。

Deployment也需要 .spec section.

Pod Template

.spec.template 是 .spec中唯一要求的字段。

.spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。

为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了)和适当的重启策略。

.spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。

Replicas

.spec.replicas 是可以选字段,指定期望的pod数量,默认是1。

Selector

.spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。

如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是 .spec.template.metadata.labels。

在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。

注意: 不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止这么做。如果有多个controller使用了重复的selector,controller们就会互相打架并导致不正确的行为。

策略

.spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"(按比例更新)。"RollingUpdate"是默认值。

Recreate Deployment,.spec.strategy.type==Recreate时,在创建出新的Pod之前会先杀掉所有已存在的Pod。

Rolling Update Deployment,.spec.strategy.type==RollingUpdate时,Deployment使用rolling update 的方式更新Pod 。可以指定maxUnavailable 和 maxSurge 来控制 rolling update 进程。

Max Unavailable,.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。

Max Surge,.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。

Progress Deadline Seconds

.spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。

如果设置该参数,该值必须大于 .spec.minReadySeconds。

Min Ready Seconds

.spec.minReadySeconds是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。

Rollback To

.spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。

Revision

.spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到历史中最老的revision。

Revision History Limit

Deployment revision history存储在它控制的ReplicaSets中。

.spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦删除了旧的RepelicaSet,该Deployment就无法再回退到那个revison了。

如果将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。

Paused

.spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。

Deployment被创建之后默认是非paused。

7、Horizontal Pod Autoscaler

横向自动扩容功能,简称HPA,也是k8s系统中的一种资源对象。在v1.1版本中首次发布,在v1.2版本中升级为稳定版。在v1.6版本之前,仅支持使用CPU负载作为是否扩容的判定条件;自v1.6版本开始提供了根据应用自定义指标进行自动扩容和缩容的功能,不过目前仍为实验性质。


可以通过yaml文件定义一个HPA对象,或者直接使用命令创建一个HPA对象。

yaml文件定义的样例:

apiVersion: autoscaling/v1

kind: HorizontalPodAutoscaler

metadata:

  name: php-apache

  namespace: default

spec:

  maxReplicas: 10

  minReplicas: 1

  scaleTargetRef:

    kink: Deployment

    name: php-apache

  targetCPUUtilizationPercentage: 90

注:当名为php-apache的deployment的Pods副本的CPU使用率超过90%时,会触发自动扩容行为。但扩容或缩容都必须满足的约束条件是Pod的副本数量要在1~10之间。

以上为命令行方式创建一个HPA:

[root@gqtest ~]# kubectl autoscale deployment nginx-deployment --min=1 --max=5 --cpu-percent=80

deployment "nginx-deployment" autoscaled


8、StatefulSet

StatefulSet(有状态系统服务设计)在k8s v1.5中引入,在Kubernetes 1.7中还是beta特性。

现实中很多服务是有状态的,如MySQL集群、kafka集群、ZooKeeper集群等,这些应用集群有以下特点:

每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现和通信;

集群的规模是相对固定的,且不能随意变动;

集群里每个节点都是有状态的,通常会持久化数据到永久存储中;

如果磁盘损坏导致集群里某个节点无法正常运行,则集群功能会部分受损;

StatefulSet是Deployment/RC的一个特殊变种,有如下特性:

StatefulSet里每个Pod都有稳定、唯一的网络标识,可以用来发现集群内其他成员。假设StatefulSet名字叫kafka,那么第1个Pod会命名为kafka-0,第2个Pod叫kafka-1,以此类推。

StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。

StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC实现,删除Pod时默认不会删除与StatefulSet相关的存储卷。

StatefulSet需要与Headless Service配合使用,需要在每个StatefulSet的定义中声明它属于哪个Headless Service。

Headless Service没有Cluster IP,当解析Headless Service的DNS域名时,得到的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上,又为受Headless Service控制的每个Pod实例创建了一个DNS域名,格式为:$(podname).$(headless service name)。

样例:一个3节点的kafka的StatefulSet集群

该应用集群的headless service名称定义为kafka

该应用集群的StatefulSet名字为kafka,则StatefulSet里的3个Pod的名称分别为:kafka-0,kafka-1,kafka-2

该应用集群的StatefulSet中3个Pod的DNS名称应该是:kafka-0.kafka,kafka-1.kafka,kafka-2.kafka

可以在该应用集群的配置文件中直接使用上述DNS名称来代表相应的Pod。


9、Service

(1)Service的概念

k8s的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例。Service与其后端Pod副本集群之间则是通过Label Selector来实现对接的。RC的作用相当于是保证Service的服务能力和服务质量始终处于预期的标准。一个 Service 在 Kubernetes 中是一个 REST 对象。


 Service 定义可以基于 POST 方式,请求 apiserver 创建新的实例。 例如,假定有一组 Pod,它们对外暴露了 9376 端口,同时还被打上 "app=MyApp" 标签。

kind: Service

apiVersion: v1

metadata:

  name: my-service

spec:

  selector:

    app: MyApp

  ports:

    - protocol: TCP

      port: 80

      targetPort: 9376

上述配置将创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 9376,并且具有标签 "app=MyApp" 的 Pod 上。 这个 Service 将被指派一个 IP 地址(通常称为 “Cluster IP”)。 Service 能够将一个接收端口映射到任意的 targetPort。 默认情况下,targetPort 将被设置为与 port 字段相同的值。Kubernetes Service 能够支持 TCP 和 UDP 协议,默认 TCP 协议。


使用以下命令查看Service的yaml定义文件:

# kubectl get svc my-service -o yaml


(2)没有 selector 的 Service

Servcie 抽象了该如何访问 Kubernetes Pod,但也能够抽象其它类型的 backend,例如:

希望在生产环境中使用外部的数据库集群。

希望服务指向另一个 Namespace 中或其它集群中的服务。

正在将工作负载同时转移到 Kubernetes 集群和运行在 Kubernetes 集群之外的 backend。


定义没有 selector 的 Service :

kind: Service

apiVersion: v1

metadata:

  name: my-service

spec:

  ports:

    - protocol: TCP

      port: 80

      targetPort: 9376

由于这个 Service 没有 selector,就不会创建相关的 Endpoints 对象。可以手动将 Service 映射到指定的 Endpoints:

kind: Endpoints

apiVersion: v1

metadata:

  name: my-service

subsets:

  - addresses:

      - ip: 1.2.3.4

    ports:

      - port: 9376

注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。

访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 1.2.3.4:9376)。


ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 相反地,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

kind: Service

apiVersion: v1

metadata:

  name: my-service

  namespace: prod

spec:

  type: ExternalName

  externalName: my.database.example.com

当查询主机 my-service.prod.svc.CLUSTER时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。


(3)VIP 和 Service 代理

运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,它会负责把对Service的请求转发到后端的某个Pod实例上并在内部实现服务的负载均衡与会话保持机制。Service不是共用一个负载均衡器的IP,而是被分配了一个全局唯一的虚拟IP地址,称为Cluster IP。在Service的整个生命周期内,它的Cluster IP不会改变。 kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。在k8s v1.2版本之前默认使用userspace提供vip代理服务,从 Kubernetes v1.2 起,默认是使用 iptables 代理。


iptables 代理模式

这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会创建相关 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会创建 iptables 规则,这个规则会选择一个 backend Pod。默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。

和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 readiness probes。


(4)多端口 Service

很多 Service 需要暴露多个端口。对于这种情况,Kubernetes 支持在 Service 对象中定义多个端口。 当使用多个端口时,必须给出所有的端口的名称,这样 Endpoint 就不会产生歧义,例如:

kind: Service

apiVersion: v1

metadata:

  name: my-service

spec:

    selector:

      app: MyApp

    ports:

      - name: http

        protocol: TCP

        port: 80

        targetPort: 9376

      - name: https

        protocol: TCP

        port: 443

        targetPort: 9377


(5)选择自己的 IP 地址

在 Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。 用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内,这对 API Server 来说是通过一个标识来指定的。 如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法。


(6)服务发现

Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。

环境变量

当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 它同时支持 Docker links兼容 变量、简单的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 变量,这里 Service 的名称需大写,横线被转换成下划线。

举个例子,一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:

REDIS_MASTER_SERVICE_HOST=10.0.0.11

REDIS_MASTER_SERVICE_PORT=6379

REDIS_MASTER_PORT=tcp://10.0.0.11:6379

REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379

REDIS_MASTER_PORT_6379_TCP_PROTO=tcp

REDIS_MASTER_PORT_6379_TCP_PORT=6379

REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

这意味着需要有顺序的要求 —— Pod 想要访问的任何 Service 必须在 Pod 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。

DNS

一个强烈推荐的集群插件 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被启用,那么所有的 Pod 应该能够自动对 Service 进行名称解析。

例如,有一个名称为 "my-service" 的 Service,它在 Kubernetes 集群中名为 "my-ns" 的 Namespace 中,为 "my-service.my-ns" 创建了一条 DNS 记录。 在名称为 "my-ns" 的 Namespace 中的 Pod 应该能够简单地通过名称查询找到 "my-service"。 在另一个 Namespace 中的 Pod 必须限定名称为 "my-service.my-ns"。 这些名称查询的结果是 Cluster IP。

Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 "my-service.my-ns" 的 Service 有一个名为 "http" 的 TCP 端口,可以对 "_http._tcp.my-service.my-ns" 执行 DNS SRV 查询,得到 "http" 的端口号。

Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 更多信息可以查看DNS Pod 和 Service。

Kubernetes 从 1.3 版本起, DNS 是内置的服务,通过插件管理器 集群插件 自动被启动。Kubernetes DNS 在集群中调度 DNS Pod 和 Service ,配置 kubelet 以通知个别容器使用 DNS Service 的 IP 解析 DNS 名字。


(7)Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。

这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。

对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。


配置 Selector

对定义了 selector 的 Headless Service,Endpoint 控制器在 API 中创建了 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod上。

不配置 Selector

对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 


(8)发布服务 —— type类型

对一些应用希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Type 的取值以及行为如下:

ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。

NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。

LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。

ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。


k8s中有3种IP地址:

Node IP: Node节点的IP地址,这是集群中每个节点的物理网卡的IP地址;

Pod IP: Pod的IP地址,这是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络;

Cluster IP:Service 的IP地址,这也是一个虚拟的IP,但它更像是一个“伪造”的IP地址,因为它没有一个实体网络对象,所以无法响应ping命令。它只能结合Service Port组成一个具体的通信服务端口,单独的Cluster IP不具备TCP/IP通信的基础。在k8s集群之内,Node IP网、Pod IP网与Cluster IP网之间的通信采用的是k8s自己设计的一种编程实现的特殊的路由规则,不同于常见的IP路由实现。

10、Volume

(1)Volume概要知识

默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。

Kubernetes Volume具有明确的生命周期,与pod相同。因此,Volume的生命周期比Pod中运行的任何容器要持久,在容器重新启动时能可以保留数据,当然,当Pod被删除不存在时,Volume也将消失。

Kubernetes支持许多类型的Volume,Pod可以同时使用任意类型/数量的Volume。

要使用Volume,pod需要指定Volume的类型和内容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。

Volume的配置样例:

template:

  metadata:

    labels:

      app: app-demo

      tier: frontend

  spec:

    volumes:

    - name: datavol

      emptyDir: {}

    containers:

    - name: tomcat-demo

      image: tomcat

      volumeMounts:

        - mountPath: /mydata-data

          name: datavol

      imagePullPolicy: IfNotPresent      


更多的Volume类型配置样例可以参见:https://github.com/kubernetes/kubernetes/tree/master/examples/volumes


(2)Volume 类型

Kubernetes支持Volume类型有:

emptyDir

hostPath

gcePersistentDisk

awsElasticBlockStore

nfs

iscsi

fc (fibre channel)

flocker

glusterfs

rbd

cephfs

gitRepo

secret

persistentVolumeClaim

downwardAPI

projected

azureFileVolume

azureDisk

vsphereVolume

Quobyte

PortworxVolume

ScaleIO

StorageOS

local

(3)emptyDir

使用emptyDir时,当Pod分配到Node上时,将会创建一个emptyDir Volume,它的内容为空,并且只要Node上的Pod一直运行,Volume就会一直存在。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。emptyDir Volume的存储介质(Disk,SSD等)取决于kubelet根目录(如/var/lib/kubelet)所处文件系统的存储介质。不限制emptyDir或hostPath Volume使用的空间大小,不对容器或Pod的资源隔离。

注:删除容器不影响emptyDir。

emptyDir的一些用途:

临时空间

一个容器需要从另一个容器中获取数据的目录

示例:

apiVersion: v1

kind: Pod

metadata:

  name: test-pd

spec:

  containers:

  - image: gcr.io/google_containers/test-webserver

    name: test-container

    volumeMounts:

    - mountPath: /cache

      name: cache-volume

  volumes:

  - name: cache-volume

    emptyDir: {}


(4)hostPath

hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。

通常有以下两种使用场景:

容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的文件系统进行存储。

需要访问宿主机上Docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。

使用hostPath的注意事项:

在不同的Node上具有相同配置的Pod,可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致。

如果使用了资源配额管理,则k8s无法将hostPath在宿主机上使用的资源纳入管理。

hostPath使用示例:

apiVersion: v1

kind: Pod

metadata:

  name: test-pd

spec:

  containers:

  - image: gcr.io/google_containers/test-webserver

    name: test-container

    volumeMounts:

    - mountPath: /test-pd

      name: test-volume

  volumes:

  - name: test-volume

    hostPath:

      # directory location on host

      path: /data

(5)NFS

Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。Pod被删除时,Volume被卸载,内容被保留。这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。

NFS使用示例(PV):nfs-pv.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

  name: nfs

spec:

  capacity:

    storage: 1Mi

  accessModes:

    - ReadWriteMany

  nfs:

    # FIXME: use the right IP

    server: 10.244.1.4

    path: "/exports"


11、Persistent Volume(PV)

PersistentVolume子系统为用户和管理员提供了一个API,它抽象了如何提供存储以及如何使用它的细节。 为此,我们引入两个新的API资源:PersistentVolume和PersistentVolumeClaim。

PersistentVolume(PV)是管理员设置的群集中的一部分存储。 它就像集群中的一个资源,就像一个节点是集群资源一样。 PV是类似Volumes的插件,但具有独立于Pod的生命周期。 此API对象捕获存储实现的细节,即NFS,iSCSI或特定于云提供程序的存储系统。

PersistentVolumeClaim(PVC)是用户存储的请求。 它与Pod相似。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 PVC声明可以请求特定的存储空间大小和访问模式(例如,可以一次读/写或多次只读)。

PV可以理解成是k8s集群中的某个网络存储中对应的一块存储资源,它与Volume很类似,但有以下区别:

PV只能是网络存储,不属于任何Node,但可以在每个Node上访问;

PV不是定义是Pod上的,而是独立定义的;

PV目前支持的存储类型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC、Flocker、NFS、iSCSI、RBD、CephFS、Cinder、GlusterFS、Vsphere Volume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath(仅支持单机测试使用)。

比较重要的PV的accessModes属性:

ReadWriteOnce: 读写权限,只能被单个Node挂载;

ReadOnlyMany: 只读权限,允许被多个Node挂载;

ReadWriteMany: 读写权限,允许被多个Node挂载;

PV的几种状态:

Available

Bound

Released: 对应的PVC已经删除,但资源还没有被集群回收。

Failed:PV自动回收失败。

PV/PVC使用示例:nfs-pvc.yaml

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

  name: my-nfs

spec:

  accessModes:

    - ReadWriteMany

  resources:

    requests:

      storage: 1Mi

注:PV的示例参见上一段落的NFS使用示例。


然后,在Pod的Volume定义中引用上述PVC即可:

      volumes:

        - name: mypvc

          persistentVolumeClaim:

            claimName: my-nfs


12、Namespace

Kubernetes可以使用Namespaces(命名空间)创建多个虚拟集群。当团队或项目中具有许多用户时,可以考虑使用Namespace来实现多租户的资源隔离。

Namespace为名称提供了一个范围。资源的Names在Namespace中具有唯一性。

Namespace是一种将集群资源划分为多个用途(通过 resource quota)的方法。

在未来的Kubernetes版本中,默认情况下,相同Namespace中的对象将具有相同的访问控制策略。

k8s集群启动后,会创建一个名为"default"的Namespace,如果不特别指明一个资源对象的Namespace属性,则用户创建的资源对象都会使用默认的"default"。

大多数Kubernetes资源(例如pod、services、replication controllers或其他)都在某些Namespace中,但Namespace资源本身并不在Namespace中。而低级别资源(如Node和persistentVolumes)不在任何Namespace中。Events是一个例外:它们可能有也可能没有Namespace,具体取决于Events的对象。


创建Namespace

(1) 命令行直接创建

$ kubectl create namespace new-namespace


(2) 通过文件创建

$ cat my-namespace.yaml

apiVersion: v1

kind: Namespace

metadata:

  name: new-namespace


$ kubectl create -f ./my-namespace.yaml

注意:命名空间名称满足正则表达式[a-z0-9]([-a-z0-9]*[a-z0-9])?,最大长度为63位


删除Namespace


$ kubectl delete namespaces new-namespace

注意:

删除一个namespace会自动删除所有属于该namespace的资源。

default和kube-system命名空间不可删除。

PersistentVolumes是不属于任何namespace的,但PersistentVolumeClaim是属于某个特定namespace的。

Events是否属于namespace取决于产生events的对象。

查看 Namespace

使用以下命令列出群集中的当前的Namespace:

$ kubectl get namespaces

NAME STATUS AGE

default Active 1d

kube-system Active 1d


Kubernetes从两个初始的Namespace开始:

default

kube-system 由Kubernetes系统创建的对象的Namespace

将一个资源对象放入名为"development"的命名空间里:

apiVersion: v1

kind: Pod

metadata:

  name: busybox

  namespace: development

spec:

  containers:

  - image: busybox

    command:

    - sleep

    - "3600"

    name: busybox


设置请求的名称空间

要临时设置Request的Namespace,请使用--namespace 标志。

例如:

$ kubectl --namespace=<insert-namespace-name-here> run nginx --image=nginx

$ kubectl --namespace=<insert-namespace-name-here> get pods

注:如果不加--namespace参数,kubectl get将仅显示属于"default"命名空间的资源对象。


可以使用kubectl命令将创建的Namespace可以永久保存在context中。

$ kubectl config set-context $(kubectl config current-context) --namespace=<insert-namespace-name-here>

# Validate it

$ kubectl config view | grep namespace:


13、Annotation(注解)

Annotation与Label类似,也使用key/value键值对的形式定义。可以使用Kubernetes Annotations将任何非标识metadata附加到对象。客户端(如工具和库)可以检索此metadata。

可以使用Labels或Annotations将元数据附加到Kubernetes对象。标签可用于选择对象并查找满足某些条件的对象集合。相比之下,Annotations不用于标识和选择对象。Annotations中的元数据可以是small 或large,structured 或unstructured,并且可以包括标签不允许使用的字符。


Annotations就如标签一样,也是由key/value组成:

"annotations": {

  "key1" : "value1",

  "key2" : "value2"

}


以下是在Annotations中记录信息的一些例子:

构建、发布的镜像信息,如时间戳,发行ID,git分支,PR编号,镜像hashes和注Registry地址。

一些日志记录、监视、分析或audit repositories。

一些工具信息:例如,名称、版本和构建信息。

用户或工具/系统来源信息,例如来自其他生态系统组件对象的URL。

负责人电话/座机,或一些信息目录。

注意:Annotations不会被Kubernetes直接使用,其主要目的是方便用户阅读查找。

参考1:《Kubernetes权威指南——从Docker到Kubernetes实践全接触》第1章。


参考2:Kubernetes中文社区 | 中文文档



转自:https://blog.csdn.net/watermelonbig/article/details/79346524?utm_source=copy