热门搜索:iPhone 7 iOS 10 iPad Pro
首页 > 职场 > CSDN云计算 > 正文

Kubernetes应用部署策略实践

CSDN云计算 2017-12-06

【摘要】Kubernetes是Google开源的Docker容器集群管理系统,为容器化的应用提供资源调度、部署运行、服务发现、扩容缩容等整一套功能。在应用部署这块,类似于其他系统,Kubernetes也提供了一些控制容器部署的方式,本文主要通过实践的方式介绍一下kubernetes的应用在部署时的部署策略。

几个概念:

Pod:是Kubernetes最基本的部署调度单元,可以包含container,逻辑上表示某种应用的一个实例。比如一个web站点应用由前端、后端及数据库构建而成,这三个组件将运行在各自的容器中,那么我们可以创建包含三个container的pod。

node: 是 Kubernetes的worker节点,通常也称作为Minion node。除了运行一些kubernetes的组件以外(kubelet, kube-proxy等),还承担着运行容器服务的重任。

ReplicationController:是pod的复制抽象,用于解决pod的扩容缩容问题。通常,分布式应用为了性能或高可用性的考虑,需要复制多份资源,并且根据负载情况动态伸缩。通过ReplicationController,我们可以指定一个应用需要几份复制,Kubernetes将为每份复制创建一个pod,并且保证实际运行pod数量总是与该复制数量相等(例如,当前某个pod宕机时,自动创建新的pod来替换)

环境介绍:

为了演示kubernetes应用部署策略,准备了7台机器(1个kubernetes master节点和6个kubernetes worker节点),如下图所示。

Kubernetes应用部署策略实践(图一)

其中:

1) kubernetes master: hchenk8s1(ubuntu 16.04 LTS) 2) etcd: hchenk8s1(可以和kubernetes master不在一个节点上面) 3) worker nodes: hchenk8s2 - hchenk8s7(总共6台机器, 操作系统为ubuntu 16.04 LTS)

目前nodeAffinity支持requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution这两种类型。从字面意思就能看到,类型一的要求要比类型二的苛刻的多,对于类型一来说,更像是上面介绍的nodeSelector的高级版,而对于类型二来说,在创建pod的时候会根据各种调度条件对可调度的机器进行排序,并且不会像类型一那样,因为资源不够或者一些其他原因而创建失败,退而求其次来去选择其他的机器继续创建。

期望结果:

以nginx应用为例,准备一个容器应用部署的kubernetes的deployment文件。

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 1  template:    metadata:      labels:        app: nginx        image: nginx_1_8_1    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:1.8.1        imagePullPolicy: Always        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 1000m            memory: 1024Mi      nodeSelector:        kubernetes.io/hostname: 9.111.254.218

在yaml文件中加入nodeSelector, 其中key和value分别为label的name和value.

首先为集群中的机器添加lable来标示机器的内核版本信息:

root@hchenk8s1:~# kc get nodes --show-labels -l worker=trueNAME            STATUS    AGE       LABELS9.111.254.208   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group1,kernel-version=0310,kubernetes.io/hostname=9.111.254.208,worker=true9.111.254.209   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group1,kernel-version=0404,kubernetes.io/hostname=9.111.254.209,worker=true9.111.254.212   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group2,kubernetes.io/hostname=9.111.254.212,worker=true9.111.254.213   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group2,kubernetes.io/hostname=9.111.254.213,worker=true9.111.254.214   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group3,kubernetes.io/hostname=9.111.254.214,worker=true9.111.254.218   Ready     6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group3,kubernetes.io/hostname=9.111.254.218,worker=true

从上面的输出可以看到,9.111.254.208的kernel-version为0310,9.111.254.209的kernel-version为0404.

准备需要创建服务所需要的yaml文件:

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 1  template:    metadata:      labels:        app: nginx        image: nginx_1_8_1    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:1.8.1        imagePullPolicy: IfNotPresent        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 1000m            memory: 1024Mi      nodeSelector:        storage_type: aufs        application_type: web

从上面的yaml文件可以看到,nodeSelector里面定义了两个条件,分别是storage_type和application_type, 应用只有创建在两个条件同时满足的节点上面。

为了测试nodeSelector多条件支持的测试,我们对6个worker分别进行标记:

root@hchenk8s1:~# kubectl label node 9.111.254.208 storage_type=overlay application_type=webnode9.111.254.208" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.209 storage_type=overlay application_type=dbnode9.111.254.209" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.212 storage_type=aufs application_type=webnode9.111.254.212" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.213 storage_type=aufs application_type=dbnode9.111.254.213" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.214 storage_type=devicemapper application_type=webnode9.111.254.214" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.218 storage_type=devicemapper application_type=dbnode9.111.254.218" labeled

标记后, 集群结构如下图所示。 Kubernetes应用部署策略实践(图二)

首先,对集群中的worker node添加label来标识组信息。

root@hchenk8s1:~# kubectl label node 9.111.254.208 group=group1node9.111.254.208" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.209 group=group1node9.111.254.209" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.212 group=group2node9.111.254.212" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.213 group=group2node9.111.254.213" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.214 group=group3node9.111.254.214" labeledroot@hchenk8s1:~# kubectl label node 9.111.254.218 group=group3node9.111.254.218" labeled

标识完后的worker node信息如下图所示。

期望结果:

场景3:

下面准备一个用来测试的yaml文件:

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 4  template:    metadata:      labels:        app: nginx      annotations:        scheduler.alpha.kubernetes.io/affinity: >          {            "nodeAffinity": {              "requiredDuringSchedulingIgnoredDuringExecution": {                "nodeSelectorTerms": [                  {                    "matchExpressions": [                      {                        "key":group",                        "operator":In",                        "values": ["group1",group2"]                      }                    ]                  }                ]              }            }          }    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:latest        imagePullPolicy: IfNotPresent        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 500m            memory: 512Mi

下面开始创建容器服务。

root@hchenk8s1:~# kc create -f nginx_nodeaffinity.yamldeploymentnginx" createdroot@hchenk8s1:~# kc get pods -o wideNAME                     READY     STATUS    RESTARTS   AGE       IP            NODEnginx-3792017226-5cn0s   1/1       Running   0          1m        10.1.36.194   9.111.254.213nginx-3792017226-ljq9h   1/1       Running   0          1m        10.1.56.66    9.111.254.208nginx-3792017226-qfbvs   1/1       Running   0          1m        10.1.64.66    9.111.254.212nginx-3792017226-tdm23   1/1       Running   0          1m        10.1.183.3    9.111.254.209

从测试结果可以看到,4个副本分别部署在了group1和group2上的机器。

下面开始创建应用

root@hchenk8s1:~# kubectl create -f nginx_exist.yamldeploymentnginx" createdroot@hchenk8s1:~# kubectl get pods -o wideNAME                     READY     STATUS    RESTARTS   AGE       IP            NODEnginx-3031338627-7bbfc   1/1       Running   0          7s        10.1.183.17   9.111.254.209nginx-3031338627-cd1jz   1/1       Running   0          7s        10.1.56.80    9.111.254.208nginx-3031338627-wslpb   1/1       Running   0          7s        10.1.183.16   9.111.254.209nginx-3031338627-zgrxn   1/1       Running   0          7s        10.1.56.79    9.111.254.208

从测试结果可以看到,4个副本分别部署在了有network label的机器上面。

下面通过应用部署来测试nodeSelector的多条件选择:

从这个参数就能看出来,这种策略的调度对象是node,也就是上面说的kubernetes的worker,说的更明白一点是,用户在创建应用的时候,可以通过nodeSelector来指定某个、或者某组具有某些属性的worker node来创建这些容器服务。这里既然提到了需要根据worker node的某些属性来创建这些容器服务,那就不得不介绍一下worker node的label.

首先先查看当前kubernetes cluster的worker node的情况。

root@hchenk8s1:~# kubectl get nodesNAME                 STATUS                                    AGE9.111.254.207   Ready,SchedulingDisabled   1d9.111.254.208   Ready                                       1d9.111.254.209   Ready                                       1d9.111.254.212   Ready                                       1d9.111.254.213   Ready                                       1d9.111.254.214   Ready                                       1d9.111.254.218   Ready                                       1d

从输出可以看到目前测试集群中有6台worker node和一个不可调度的master节点。

在kubernetes集群中,kubelet会上报一些机器属性比如hostname, os, arch等信息记录在nodes的label里面。下面先查看一下这些label.

root@hchenk8s1:~# kubectl get nodes --show-labelsNAME            STATUS                     AGE       LABELS9.111.254.207   Ready,SchedulingDisabled   1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2079.111.254.208   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2089.111.254.209   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2099.111.254.212   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2129.111.254.213   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2139.111.254.214   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.2149.111.254.218   Ready                      1d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=9.111.254.218

从输出结果可以看到,每个node都有3个label分别是beta.kubernetes.io/arch,beta.kubernetes.io/os,kubernetes.io/hostname。下面通过hostname作为应用部署的选择策略来部署应用到9.111.254.218机器上面。

集群中的6个worker node分别属于3个不同的组,这里分别命名为group1, group2, group3. 需要部署一个nignx应用,并且有4个副本,要求nignx应用部署在除了group3以外的其他group上面。

下面准备一个nginx的yaml文件,用来创建nginx服务:

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 4  template:    metadata:      labels:        app: nginx      annotations:        scheduler.alpha.kubernetes.io/affinity: >          {            "nodeAffinity": {              "requiredDuringSchedulingIgnoredDuringExecution": {                "nodeSelectorTerms": [                  {                    "matchExpressions": [                      {                        "key":kernel-version",                        "operator":Gt",                        "values": ["0320"]                      }                    ]                  }                ]              }            }          }    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:latest        imagePullPolicy: IfNotPresent        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 200m            memory: 256Mi

上面的文件表示的策略是期望创建服务到kernel-version大于0320的机器上面。

Exists: 和In比较类似,凡是有某个标签的机器都会被选择出来。使用Exists的operator的话,values里面就不能写东西了。

Label: 标签的意思,使用在worker node上面顾名思义就是用来对worker node进行一些标记的。比如说worker node的cpu架构(ppc64, x86, etc)或者分组信息啊什么的。nodeSelector就是通过这些标签来选择应用到底要在哪些机器上去部署。

准备需要创建服务所需要的yaml文件:

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 1  template:    metadata:      labels:        app: nginx        image: nginx_1_8_1    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:1.8.1        imagePullPolicy: IfNotPresent        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 1000m            memory: 1024Mi      nodeSelector:        storage_type: btrfs        application_type: web

下面开始创建容器服务。

root@hchenk8s1:~# kubectl create -f nginx.yamldeploymentnginx" createdroot@hchenk8s1:~# kubectl get deploymentNAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGEnginx     1         1         1            0           58sroot@hchenk8s1:~# kubectl  get pods -o wideNAME                    READY     STATUS    RESTARTS   AGE       IP        NODEnginx-155862529-m5pr1   0/1       Pending   0          1m        <none>root@hchenk8s1:~# kubectl describe pods nginx-155862529-m5pr1Name:       nginx-155862529-m5pr1Namespace:  defaultNode:       /Labels:     app=nginx        pod-template-hash=155862529Status:     PendingIP:Controllers:    ReplicaSet/nginx-155862529Containers:  nginx:    Image:  nginx:latest    Port:   80/TCP    Limits:      cpu:  1      memory:   1Gi    Requests:      cpu:  1      memory:   1Gi    Volume Mounts:      /var/run/secrets/kubernetes.io/serviceaccount from default-token-5rj87 (ro)    Environment Variables:  <none>Conditions:  Type      Status  PodScheduled  FalseVolumes:  default-token-5rj87:    Type:   Secret (a volume populated by a Secret)    SecretName: default-token-5rj87QoS Class:  GuaranteedTolerations:    <none>Events:  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason          Message  --------- --------    -----   ----            -------------   --------    ------          -------  1m        27s     8   {default-scheduler }            Warning     FailedScheduling    pod (nginx-155862529-m5pr1) failed to fit in any nodefit failure summary on nodes : MatchNodeSelector (6)

从输出可以看到,pod创建失败了,原因是没有找到合适的机器去部署。

总结一下:nodeSelector通过label选择机制,提供了比较简单直观的pod部署策略,从一些方面实现了节点的亲和/反亲和的策略。虽然现在仍然存在在kubernetes中,不过相信这个功能会慢慢被接下来要提到的node Affinity和inter-pod affinity取而代之。

2. NodeAffinity

NodeAffinity是kubernetes 1.2的时候集成进来的,概念上类似于上面介绍的nodeSelector, 通过对node label的选择来部署你的pod的。

下面开始创建容器服务。

root@hchenk8s1:~# kubectl create -f nginx.yamldeploymentnginx" createdroot@hchenk8s1:~# kubectl get deploymentNAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGEnginx     1         1         1            1           46sroot@hchenk8s1:~# kubectl  get pods -o wideNAME                     READY     STATUS    RESTARTS   AGE       IP            NODEnginx-2704164239-lr0gj   1/1       Running   0          1m        10.1.58.235   9.111.254.212

从输出可以看到,nginx服务已经选择机器9.111.254.212去部署应用。

上面的例子中用了In的operator,matchExpressions的operator大概有一下几种:

下面设计一个场景还试一下:

完了就开始环境搭建,这里就不演示了,环境搭建部分网上很多。可供参考的比较多。

进入主题

目前,kubernetes提供了3中应用部署策略,下面一一进行介绍:

1. nodeSelector:

nodeSelector是kubernetes提供的最简单的一种应用部署策略,通过一种key=value的方式来部署用户的应用。

nginx web服务部署在节点9.111.254.212上面

期望结果:

期望结果:

创建一个nginx web服务,选择worker node上面,storage_type标记为啊btrfs的节点:

场景2:

步骤:

在这两种类型中,ignoredDuringExecution的意思是在node在运行期间如果label发生了变化,之间通过这些类型部署的pod不会因为node label的变化而去重新部署来满足已经定义好的亲和/反亲和的策略。不过社区计划会针对这些case提供requiredDuringSchedulingRequiredDuringExecution的类型来应对因为node label变化,定义的亲和/反亲和的策略发生变化的问题,当然,pod可能就需要重新部署来适应已经发生的变化。

nginx的应用能部署在group1和group2里的worker node。

场景2:

步骤:

步骤:

场景1:

步骤:

场景1:

Lt: less than的意思,表示凡是某个value小于设定的值的机器则会被选择出来。

下面就开始见证奇迹了。

当然,nodeSelector自身可以支持多个选择条件,当创建应用的时候,nodeSelector里面的条件都满足的机器会被选择出来用来部署pod.

先说说nodeAffinity的类型:

通过kubectl创建应用容器服务。

root@hchenk8s1:~# kubectl  create -f nginx.yamldeploymentnginx" createdroot@hchenk8s1:~# kubectl get deploymentNAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGEnginx     1         1         1            1           9mroot@hchenk8s1:~# kubectl  get pods -o wideNAME                     READY     STATUS    RESTARTS   AGE       IP            NODEnginx-1245594662-sjjp9   1/1       Running   0          1m        10.1.20.130   9.111.254.218

从输出可以看到, nginx的容器服务已经部署到了刚刚指定的机器上面。

集群中的6个worker node,其中的2台有kernel-version标签,用来记录机器的内核版本。通过deployment创建一个nginx应用,并且nginx应用有4个副本,通过nodeAffinity选择内核版本范围来进行应用部署。

集群中的6个worker node,其中的2台标记了network的标签,而其他的4台没有network标签。通过deployment创建一个nginx应用,并且nginx应用有4个副本,通过nodeAffinity选择有network标签的机器进行应用部署。

In: 凡是满足values里面条件的机器都会被选择出来。以上面的例子为例,凡是满足group=group1或者group=group2的机器都会被选择出来。

Kubernetes应用部署策略实践(图三)

期望nginx的应用部署在kerver-version大于0320的机器上面。

创建一个nginx web服务,选择worker node上面,storage_type标记为啊aufs的节点:

步骤:

NotIn: 和In相反,凡是满足values里面条件的机器都会被剔除出去。如果以上面的例子为例,operator换成NotIn, 那么group=group1以及group=group2的机器就会被剔除出去,而group=group3的机器则会被选择出来。

期望结果:

下面我们通过nodeSelector来部署应用,并且应用需要部署在指定的机器上面。

nginx web选择不到合适的机器部署应用。

DoesNotExist: 和Exists相反,凡是不具备某个标签的机器则会被选择出来。和Exists的Operator一样,values里面也不能写东西了。

首先,对集群中的worker node添加label来标识组信息,通过命令可以查看当前集群中的worker node的label信息。

root@hchenk8s1:~# kubectl get nodes --show-labelsNAME            STATUS                     AGE       LABELS9.111.254.208   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group1,kubernetes.io/hostname=9.111.254.208,network=calico9.111.254.209   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group1,kubernetes.io/hostname=9.111.254.209,network=calico9.111.254.212   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group2,kubernetes.io/hostname=9.111.254.2129.111.254.213   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group2,kubernetes.io/hostname=9.111.254.2139.111.254.214   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group3,kubernetes.io/hostname=9.111.254.2149.111.254.218   Ready                      6d        beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,group=group3,kubernetes.io/hostname=9.111.254.218

准备创建nginx应用的deployment

kind: DeploymentapiVersion: extensions/v1beta1metadata:  name: nginxspec:  replicas: 4  template:    metadata:      labels:        app: nginx      annotations:        scheduler.alpha.kubernetes.io/affinity: >          {            "nodeAffinity": {              "requiredDuringSchedulingIgnoredDuringExecution": {                "nodeSelectorTerms": [                  {                    "matchExpressions": [                      {                        "key":network",                        "operator":Exists"                      }                    ]                  }                ]              }            }          }    spec:      hostNetwork: false      containers:      - name: nginx        image: nginx:latest        imagePullPolicy: IfNotPresent        ports:        - protocol: TCP          containerPort: 80        resources:          limits:            cpu: 500m            memory: 512Mi

从上面的yaml文件可以看到,matchExpression里面定义了nodeAffinity的选择条件,从上面的例子可以看到,nginx应用期望能创建在有network label的机器上。

下面2个例子分别看看其他几个operator的使用以及测试结果。

Gt: greater than的意思,表示凡是某个value大于设定的值的机器则会被选择出来。

nginx的应用能部署在有network的标签的机器上面。

下面开始创建;

root@hchenk8s1:~# kubectl create -f nginx_gt.yamldeploymentnginx" createdroot@hchenk8s1:~# kubectl get pods -o wideNAME                     READY     STATUS    RESTARTS   AGE       IP            NODEnginx-4087060041-2x9lw   1/1       Running   0          4s        10.1.183.26   9.111.254.209nginx-4087060041-4x1dd   1/1       Running   0          4s        10.1.183.23   9.111.254.209nginx-4087060041-bgt0z   1/1       Running   0          4s        10.1.183.24   9.111.254.209nginx-4087060041-brgb3   1/1       Running   0          4s        10.1.183.25   9.111.254.209

从测试结果可以看到,4个副本都创建在了kernel-version为0404的机器上。

3. Inter-pod affinity/anti-affinity

Inter-pod affinity/anti-affinity是kubernetes 1.4开始支持的,pod的部署策略不再单单的只是通过node label的选择,而是可以从pod层面通过pod的 label来部署自己的应用,简单点说就是你可以通过inter-pod affinity/anti-affinity来决定自己的容器应用亲近或者远离具有某些label的容器应用。

和nodeAffinity一样,inter-pod affinity/anti-affinity也有requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution两种类型,分别表示”hard”和”soft”两种需求。

篇幅限制,想查看原文,请点击左下角“阅读原文

作者简介:陈晖,供职于西安IBM, 主要从事云计算方面工作,涉及Openstack, Mesos, kubernetes等开源项目,在资源管理 ,资源调度等方面有比较丰富的经验 。

关键字:

特约作者

推荐阅读 ^o^

微信扫一扫
分享到朋友圈

prve

SRWebSocket源码浅析(上)

上一篇

next

大数据计算框架Hadoop, Spark和MPI

下一篇