kubernetes1.4 基础篇:Learn Kubernetes 1.4 by 6 steps

9月底k8s发行了1.4版,在此版中增加了kubeadm似乎是在向swarm致敬,但是极大方便了k8s的集群搭建。本系列教程将从搭建一个3个节点的k8s的集群开始,用一些简单的动手实例来学习一下k8s相关的基本概念以及k8s的核心用法。

由来

本教程受Kubernetes官方最新更新的文档所触发,之所以没有做单纯的翻译是因为如下几个原因:

  1. Kubernetes官方此教程基于minikube,个人对minikube可能有偏见,觉得像玩具。
  2. Minikube更新较慢,不久前试的仍然只是能模拟kubernetes1.3,kubeadm也出来了,只是用于教程的话完全可以取代。
  3. google的此教程提供了一个交互式的体验窗口,但是本来就不复杂的东西,就不想用它们的交互式的界面,感觉不真实,同时自己搭建可以先看什么就看什么,另外google目前提供的版本仍不是最新的。
  4. Kubernetes入门虽然不复杂,但是一般使用者第一个hello world的时间成本从接触到可用可能还是以天为计算单位,太浪费。但是确实作为对Kubernetes基本概念的理解的入门教程很不错,自己再重新看的时候也能温故而知新。从中糅出这几篇文章分享给大家,希望有所帮助。

Kubernetes基础

此系列教程中会着重于围绕Kubernetes集群编排相关的基本概念展开,同时通过容器化的应用如何在Kubernetes中部署/扩展/更新为主线而展开。我们将会学到:

  • 将容器化的应用部署到集群上
  • 扩展应用部署
  • 更新容器化的应用程序版本
  • 调试容器化的应用程序

Kubernetes是什么

Kubernetes是在整个计算机集群中对应用容器进行编排和执行的一个可以用于生产环境级别的开源平台。(了解更多内容 https://www.kubernetes.org.cn/kubernetes是什么)

Kubernetes能做什么

对于现代的web service,用于期待它应该是24×7的高可用,而开发者则期待能够每天都能对这些应用程序发布几个版本(虽然我没有这么想过)。容器化则能帮助打包应用程序完成这些目标,使得应用程序能够无宕机地平稳快速发布。Kubernetes则能够帮助做到剩下的事情。打完包的容器化应用运行在集群上需要做什么呢:

  • 在哪个节点上执行
  • 什么时候执行
  • 使用那些resouce
  • 如何在这个集群中调整这些resource
  • ……

这些问题都需要进行考虑的,而现在kubernetes的编排和执行功能则为能为你排忧解难。而且Kubernetes不但是可以用于生产级别,而且还积累了google的多年容器化运行的经验,有前人踏坑的可用软件自然是大家竞相追逐的。

基本内容

本系列教程将以容器化的应用如何在Kubernetes集群上进行部署/更新/扩展按照如下六个步骤按序展开。
20161207102903

  1. Step 1. Create a Kubernetes cluster
  2. Step 2. Deploy an app
  3. Step 3. Explore your app
  4. Step 4. Expose your app publicly
  5. Step 5. Scale up your app
  6. Step 6. Update your app

组成要素

Kubernetes集群包含如下两种类型的资源:

类型 作用
Master 协调和操控集群
Node 实际用于运行应用容器的”worker”

集群构成

qq%e6%88%aa%e5%9b%be20161207102912

构成说明

Master的主要职责在于管理集群,协调集群上的所有活动,比如:

  • 编排应用
  • 维护应用状态
  • 扩展应用
  • 更新应用等

Node

Node就是一台在kubernetes集群中担任worker的VM或者物理机。在每个Node上都有一个kubelet,而Kubelet就是一个用于管理node和Master之间的沟通的agent。在node上需要安装docker,或者说用于管理容器操作的工具,因为kubernetes并不绑定docker,我们以前介绍过的rkt在kubernetes中也是支持的。而且明眼人都能看出来,rkt纯粹是google用于制衡docker的,rkt和kubernetes深度融合,但是是否能得到市场的认可这并不是一个纯粹技术的问题。

一个在生产环境中能够大体使用的kubernetes集群至少要有3个node。本来就是node的协调和编排,你就一个node,也非要用kubernetes,虽然可以任性,但是屠龙宝刀只用来裁墙纸多少会有大材小用的唏嘘。

Master

Master则用来管理集群。当你在kubernetes集群上部署应用的时候,你可能会需要Master启动某个应用容器。 Master在集群中协调用于启动此容器的node,而node使用kubernetes API和master进行通信(当然,用户也可以直接使用kubernetes API和集群进行交互)。

Kubernetes集群创建

Kubernetes集群可以被部署到物理机或者虚拟机上。可以使用minikube,但是本文将使用kubeadm方式创建一个3 node + 1 Master的集群。

构成

项番 类别 host名 IP
No.1 Master host31 192.168.32.31
No.2 Node host32 192.168.32.32
No.3 Node host33 192.168.32.33
No.4 Node host34 192.168.32.34

easypack_kubernetes.sh

K8S在1.4出来的时候向全世界宣布2条命令创建集群,在VPN的环境下,的确是这样,因为这些依赖关系1.4会根据需要自动的去pull,但是pull不到google_container的就这样被无比简单的一件事情堵在外面。本着利己利人的出发点,写了个脚本,放到了github上(https://github.com/liumiaocn/easypack/tree/master/k8s),脚本的使用基本上是sh easypack_kubernetes.sh MASTER来创建Master,sh easypack_kubernetes.sh NODE token MASTERIP来join,其他的诸如docker和kubelet/kubectl/kubeadm等的安装,container的事前下载都写进去了,版本全部目前统一为1.4.1的版本,脚本及其简单,无非将google的步骤放到一起而已,可以自行按自己需要进行修改。

创建Master

创建master在kubernetes1.4的版本只需要一条命令,kubeadm init, 但是其前提是能够联上网络,kubeadm会自动地按照需求去pull相应的image的版本,所以省去了翻来覆去的确认各个image等的版本,但是安装过程会慢一些,如果你查看linux的系统日志你就会发现/var/log/messages中只有在安装的时候在本地找不到正确版本的image的时候才回去pull,所以事前把所需要的内容都pull下来是一个很好的主意。

命令:
git clone https://github.com/liumiaocn/easypack
cd easypack/k8s
sh easypack_kubernetes.sh MASTER
>实际执行的时候不需要一定设定本地git,将上面github上面的脚本easypack_kubernetes.sh的内容copy下来在本地vi生成一个即可
安装耗时:10分钟(基本上都是在pull google_container的镜像)
输出参照:
[root@host31 ~]# git clone https://github.com/liumiaocn/easypack
Cloning into 'easypack'...
remote: Counting objects: 67, done.
remote: Compressing objects: 100% (52/52), done.
remote: Total 67 (delta 11), reused 0 (delta 0), pack-reused 15
Unpacking objects: 100% (67/67), done.
[root@host31 ~]# cd easypack/k8s
[root@host31 k8s]# sh easypack_kubernetes.sh MASTER
Wed Nov 9 05:05:53 EST 2016
##INSTALL LOG : /tmp/k8s_install.1456.log
##Step 1: Stop firewall ...

Wed Nov  9 05:05:53 EST 2016
##Step 2: set repository and install kubeadm etc...
  install kubectl in Master...
#######Set docker proxy when needed. If ready, press any to continue...
注意:此处需要回车一下才能继续,因为需要给安装完docker还要设定docker的代理的提供一个手动处理的方式,不通过代理的直接回车即可

Wed Nov  9 05:07:04 EST 2016
##Step 3: pull google containers...
Now begin to pull images from liumiaocn
No.1 : kube-proxy-amd64:v1.4.1 pull begins ...
No.1 : kube-proxy-amd64:v1.4.1 pull ends   ...
No.1 : kube-proxy-amd64:v1.4.1 rename      ...
No.1 : kube-proxy-amd64:v1.4.1 untag       ...

No.2 : kube-discovery-amd64:1.0 pull begins ...
No.2 : kube-discovery-amd64:1.0 pull ends   ...
No.2 : kube-discovery-amd64:1.0 rename      ...
No.2 : kube-discovery-amd64:1.0 untag       ...

No.3 : kube-scheduler-amd64:v1.4.1 pull begins ...
No.3 : kube-scheduler-amd64:v1.4.1 pull ends   ...
No.3 : kube-scheduler-amd64:v1.4.1 rename      ...
No.3 : kube-scheduler-amd64:v1.4.1 untag       ...

No.4 : kube-controller-manager-amd64:v1.4.1 pull begins ...
No.4 : kube-controller-manager-amd64:v1.4.1 pull ends   ...
No.4 : kube-controller-manager-amd64:v1.4.1 rename      ...
No.4 : kube-controller-manager-amd64:v1.4.1 untag       ...

No.5 : kube-apiserver-amd64:v1.4.1 pull begins ...
No.5 : kube-apiserver-amd64:v1.4.1 pull ends   ...
No.5 : kube-apiserver-amd64:v1.4.1 rename      ...
No.5 : kube-apiserver-amd64:v1.4.1 untag       ...

No.6 : pause-amd64:3.0 pull begins ...
No.6 : pause-amd64:3.0 pull ends   ...
No.6 : pause-amd64:3.0 rename      ...
No.6 : pause-amd64:3.0 untag       ...

No.7 : etcd-amd64:2.2.5 pull begins ...
No.7 : etcd-amd64:2.2.5 pull ends   ...
No.7 : etcd-amd64:2.2.5 rename      ...
No.7 : etcd-amd64:2.2.5 untag       ...

No.8 : kubedns-amd64:1.7 pull begins ...
No.8 : kubedns-amd64:1.7 pull ends   ...
No.8 : kubedns-amd64:1.7 rename      ...
No.8 : kubedns-amd64:1.7 untag       ...

No.9 : kube-dnsmasq-amd64:1.3 pull begins ...
No.9 : kube-dnsmasq-amd64:1.3 pull ends   ...
No.9 : kube-dnsmasq-amd64:1.3 rename      ...
No.9 : kube-dnsmasq-amd64:1.3 untag       ...

No.10 : exechealthz-amd64:1.1 pull begins ...
No.10 : exechealthz-amd64:1.1 pull ends   ...
No.10 : exechealthz-amd64:1.1 rename      ...
No.10 : exechealthz-amd64:1.1 untag       ...

No.11 : kubernetes-dashboard-amd64:v1.4.1 pull begins ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 pull ends   ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 rename      ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 untag       ...

All images have been pulled to local as following
gcr.io/google_containers/kube-controller-manager-amd64   v1.4.1              ad7b34f5ecb8        11 days ago         142.4 MB
gcr.io/google_containers/kube-apiserver-amd64            v1.4.1              4a76dd338dfe        4 weeks ago         152.1 MB
gcr.io/google_containers/kube-scheduler-amd64            v1.4.1              f9641959ec72        4 weeks ago         81.67 MB
gcr.io/google_containers/kube-proxy-amd64                v1.4.1              b47199222245        4 weeks ago         202.7 MB
gcr.io/google_containers/kubernetes-dashboard-amd64      v1.4.1              1dda73f463b2        4 weeks ago         86.76 MB
gcr.io/google_containers/kube-discovery-amd64            1.0                 c5e0c9a457fc        6 weeks ago         134.2 MB
gcr.io/google_containers/kubedns-amd64                   1.7                 bec33bc01f03        10 weeks ago        55.06 MB
gcr.io/google_containers/kube-dnsmasq-amd64              1.3                 9a15e39d0db8        4 months ago        5.126 MB
gcr.io/google_containers/pause-amd64                     3.0                 99e59f495ffa        6 months ago        746.9 kB

Wed Nov  9 05:14:18 EST 2016
Wed Nov  9 05:14:18 EST 2016
##Step 4: kubeadm init
Running pre-flight checks
注意:此处被卡的原因在于不符合安装要求,基本上将提示的问题对应即可,一般是/etc/kubernetes下面事前已经有内容了,一些端口已经被占用了等等。
<master/tokens> generated token: "77eddc.77edc19a7ade45d6"
<master/pki> generated Certificate Authority key and certificate:
Issuer: CN=kubernetes | Subject: CN=kubernetes | CA: true
Not before: 2016-11-09 10:14:19 +0000 UTC Not After: 2026-11-07 10:14:19 +0000 UTC
Public: /etc/kubernetes/pki/ca-pub.pem
Private: /etc/kubernetes/pki/ca-key.pem
Cert: /etc/kubernetes/pki/ca.pem
<master/pki> generated API Server key and certificate:
Issuer: CN=kubernetes | Subject: CN=kube-apiserver | CA: false
Not before: 2016-11-09 10:14:19 +0000 UTC Not After: 2017-11-09 10:14:19 +0000 UTC
Alternate Names: [192.168.32.31 10.0.0.1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local]
Public: /etc/kubernetes/pki/apiserver-pub.pem
Private: /etc/kubernetes/pki/apiserver-key.pem
Cert: /etc/kubernetes/pki/apiserver.pem
<master/pki> generated Service Account Signing keys:
Public: /etc/kubernetes/pki/sa-pub.pem
Private: /etc/kubernetes/pki/sa-key.pem
<master/pki> created keys and certificates in "/etc/kubernetes/pki"
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"
<util/kubeconfig> created "/etc/kubernetes/admin.conf"
<master/apiclient> created API client configuration
<master/apiclient> created API client, waiting for the control plane to become ready
注意:此处是容易被卡住的最多的地方,从这里开始基本上是google_container没有下载下来或者是没有下载到正确版本,详细可以参看另外一篇单独讲解使用kubeadm进行安装的文章。
<master/apiclient> all control plane components are healthy after 17.439551 seconds
<master/apiclient> waiting for at least one node to register and become ready
<master/apiclient> first node is ready after 4.004373 seconds
<master/apiclient> attempting a test deployment
<master/apiclient> test deployment succeeded
<master/discovery> created essential addon: kube-discovery, waiting for it to become ready
<master/discovery> kube-discovery is ready after 2.001892 seconds
<master/addons> created essential addon: kube-proxy
<master/addons> created essential addon: kube-dns

Kubernetes master initialised successfully!

You can now join any number of machines by running the following on each node:

kubeadm join --token=77eddc.77edc19a7ade45d6 192.168.32.31
注意:这个一定要注意,token是别的node用来join的时候必须的
Wed Nov  9 05:14:46 EST 2016
##Step 5: taint nodes...
node "host31" tainted
NAME      STATUS    AGE
host31    Ready     5s
注意:Master一般不是作为node来用的使用这种方法,这样可以折衷。
Wed Nov  9 05:14:46 EST 2016
##Step 6: confirm version...
Client Version: version.Info{Major:"1", Minor:"4", GitVersion:"v1.4.1", GitCommit:"33cf7b9acbb2cb7c9c72a10d6636321fb180b159", GitTreeState:"clean", BuildDate:"2016-10-10T18:19:49Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"4", GitVersion:"v1.4.1", GitCommit:"33cf7b9acbb2cb7c9c72a10d6636321fb180b159", GitTreeState:"clean", BuildDate:"2016-10-10T18:13:36Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}
kubeadm version: version.Info{Major:"1", Minor:"5+", GitVersion:"v1.5.0-alpha.1.409+714f816a349e79", GitCommit:"714f816a349e7978bc93b35c67ce7b9851e53a6f", GitTreeState:"clean", BuildDate:"2016-10-17T13:01:29Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}
Wed Nov  9 05:14:47 EST 2016
##Step 7: set weave-kube ...
daemonset "weave-net" created
[root@host31 k8s]#

添加节点

创建完MASTER之后,就可以向这个集群添加节点了,首先添加host32。

命令:sh easypack_kubernetes.sh NODE 77eddc.77edc19a7ade45d6 192.168.32.31
安装耗时:10分钟左右(基本上也是docker pull的时间,但是同时取决于你的网速和dockhub的状况)
安装参照:
[root@host32 k8s]# sh easypack_kubernetes.sh NODE 77eddc.77edc19a7ade45d6 192.168.32.31
Wed Nov 9 05:43:10 EST 2016
##INSTALL LOG : /tmp/k8s_install.10517.log
##Step 1: Stop firewall ...

Wed Nov  9 05:43:10 EST 2016
##Step 2: set repository and install kubeadm etc...
#######Set docker proxy when needed. If ready, press any to continue...

Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.

Wed Nov  9 05:44:04 EST 2016
##Step 3: pull google containers...
Now begin to pull images from liumiaocn
No.1 : kube-proxy-amd64:v1.4.1 pull begins ...
No.1 : kube-proxy-amd64:v1.4.1 pull ends   ...
No.1 : kube-proxy-amd64:v1.4.1 rename      ...
No.1 : kube-proxy-amd64:v1.4.1 untag       ...

No.2 : kube-discovery-amd64:1.0 pull begins ...
No.2 : kube-discovery-amd64:1.0 pull ends   ...
No.2 : kube-discovery-amd64:1.0 rename      ...
No.2 : kube-discovery-amd64:1.0 untag       ...

No.3 : kube-scheduler-amd64:v1.4.1 pull begins ...
No.3 : kube-scheduler-amd64:v1.4.1 pull ends   ...
No.3 : kube-scheduler-amd64:v1.4.1 rename      ...
No.3 : kube-scheduler-amd64:v1.4.1 untag       ...

No.4 : kube-controller-manager-amd64:v1.4.1 pull begins ...
No.4 : kube-controller-manager-amd64:v1.4.1 pull ends   ...
No.4 : kube-controller-manager-amd64:v1.4.1 rename      ...
No.4 : kube-controller-manager-amd64:v1.4.1 untag       ...

No.5 : kube-apiserver-amd64:v1.4.1 pull begins ...
No.5 : kube-apiserver-amd64:v1.4.1 pull ends   ...
No.5 : kube-apiserver-amd64:v1.4.1 rename      ...
No.5 : kube-apiserver-amd64:v1.4.1 untag       ...

No.6 : pause-amd64:3.0 pull begins ...
No.6 : pause-amd64:3.0 pull ends   ...
No.6 : pause-amd64:3.0 rename      ...
No.6 : pause-amd64:3.0 untag       ...

No.7 : etcd-amd64:2.2.5 pull begins ...
No.7 : etcd-amd64:2.2.5 pull ends   ...
No.7 : etcd-amd64:2.2.5 rename      ...
No.7 : etcd-amd64:2.2.5 untag       ...

No.8 : kubedns-amd64:1.7 pull begins ...
No.8 : kubedns-amd64:1.7 pull ends   ...
No.8 : kubedns-amd64:1.7 rename      ...
No.8 : kubedns-amd64:1.7 untag       ...

No.9 : kube-dnsmasq-amd64:1.3 pull begins ...
No.9 : kube-dnsmasq-amd64:1.3 pull ends   ...
No.9 : kube-dnsmasq-amd64:1.3 rename      ...
No.9 : kube-dnsmasq-amd64:1.3 untag       ...

No.10 : exechealthz-amd64:1.1 pull begins ...
No.10 : exechealthz-amd64:1.1 pull ends   ...
No.10 : exechealthz-amd64:1.1 rename      ...
No.10 : exechealthz-amd64:1.1 untag       ...

No.11 : kubernetes-dashboard-amd64:v1.4.1 pull begins ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 pull ends   ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 rename      ...
No.11 : kubernetes-dashboard-amd64:v1.4.1 untag       ...

All images have been pulled to local as following
gcr.io/google_containers/kube-controller-manager-amd64   v1.4.1              ad7b34f5ecb8        11 days ago         142.4 MB
gcr.io/google_containers/pause-amd64                     latest              19047b725e84        3 weeks ago         746.9 kB
gcr.io/google_containers/kube-apiserver-amd64            v1.4.1              4a76dd338dfe        4 weeks ago         152.1 MB
gcr.io/google_containers/kube-scheduler-amd64            v1.4.1              f9641959ec72        4 weeks ago         81.67 MB
gcr.io/google_containers/kube-proxy-amd64                v1.4.1              b47199222245        4 weeks ago         202.7 MB
gcr.io/google_containers/kubernetes-dashboard-amd64      v1.4.1              1dda73f463b2        4 weeks ago         86.76 MB
gcr.io/google_containers/kube-proxy-amd64                v1.4.0              1f6aa6a8c3dc        6 weeks ago         202.8 MB
gcr.io/google_containers/kube-apiserver-amd64            v1.4.0              828dcf2a8776        6 weeks ago         152.3 MB
gcr.io/google_containers/kube-controller-manager-amd64   v1.4.0              b77714f7dc16        6 weeks ago         142.2 MB
gcr.io/google_containers/kube-scheduler-amd64            v1.4.0              9ed70b516ca8        6 weeks ago         81.8 MB
gcr.io/google_containers/kube-discovery-amd64            1.0                 c5e0c9a457fc        6 weeks ago         134.2 MB
gcr.io/google_containers/kubedns-amd64                   1.7                 bec33bc01f03        10 weeks ago        55.06 MB
gcr.io/google_containers/kube-dnsmasq-amd64              1.3                 9a15e39d0db8        4 months ago        5.126 MB
gcr.io/google_containers/pause-amd64                     3.0                 99e59f495ffa        6 months ago        746.9 kB

Wed Nov  9 05:52:49 EST 2016
Wed Nov  9 05:52:49 EST 2016
##Step 4: kubeadm join
Running pre-flight checks
<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://192.168.32.31:9898/cluster-info/v1/?token-id=77eddc"
<node/discovery> cluster info object received, verifying signature using given token
<node/discovery> cluster info signature and contents are valid, will use API endpoints [https://192.168.32.31:6443]
注意:此处有可能被卡,比如此host的时间日期和Master都不一致等会导致被卡,其他被卡被坑的情况可以参照Master或者另外一片文章的整理。
<node/bootstrap> trying to connect to endpoint https://192.168.32.31:6443
<node/bootstrap> detected server version v1.4.1
<node/bootstrap> successfully established connection with endpoint https://192.168.32.31:6443
<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request
<node/csr> received signed certificate from the API server:
Issuer: CN=kubernetes | Subject: CN=system:node:host32 | CA: false
Not before: 2016-11-09 10:49:00 +0000 UTC Not After: 2017-11-09 10:49:00 +0000 UTC
<node/csr> generating kubelet configuration
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.
Wed Nov  9 05:52:55 EST 2016
##Step 5: confirm version...
kubeadm version: version.Info{Major:"1", Minor:"5+", GitVersion:"v1.5.0-alpha.1.409+714f816a349e79", GitCommit:"714f816a349e7978bc93b35c67ce7b9851e53a6f", GitTreeState:"clean", BuildDate:"2016-10-17T13:01:29Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}[root@host32 k8s]#

结果确认

使用kubectl get nodes在Master上即可查看出集群的构成

[root@host31 k8s]# kubectl get nodes
NAME      STATUS    AGE
host31    Ready     42m
host32    Ready     17m
host33    Ready     3m
host34    Ready     5m
[root@host31 k8s]#

什么是Deployment

在Kubernetes中,Deployment是用来负责创建和更新应用程序实例的

为什么要容器化

在Kubernetes中,为了能够被部署,应用程序需要被打包成为其所支持的容器格式(docker/rkt)

Kubernetes Deployment

我们创建了一个具有3个node的kubernetes集群.

 

qq%e6%88%aa%e5%9b%be20161207102927

 

有了这个集群,接下来我们就可以把容器化了的应用部署到其中了。

qq%e6%88%aa%e5%9b%be20161207102933

首先,我们要创建一个kubernetes的Deployment。这个Deployment是用来负责创建和更新我们应用程序实例的。
而一旦这个应用创建好了之后,Kubernetes的Master会协调在集群的哪个node上俩创建应用实例。
而一旦应用实例被创建之后,Kubernetes的Deployment Controller就会持续的监视这些应用实例。
一旦发生机器故障或者其他不可预知的情况导致应用实例停止时,一直在监视的Deployment Controller就立即知道这一情况,然后它就会重新生成新的应用实例
Kubernetes提供了这种在故障发生时的自愈机制,这个机制也是使用Kubernetes提案的时候反复被背书的能力。

创建Deployment

可以使用Kubernetes的命令行接口kubectl,kubectl只安装在Master上,通过使用kubernetes的API与集群进行交互。

确认版本

命令:kubectl version。 其实我们在安装完成的时候如果你还有印象的话就会记得已经用过这条命令了,其结果显示如下,client和Server均为1.4.1版本

[root@host31 ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"4", GitVersion:"v1.4.1", GitCommit:"33cf7b9acbb2cb7c9c72a10d6636321fb180b159", GitTreeState:"clean", BuildDate:"2016-10-10T18:19:49Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"4", GitVersion:"v1.4.1", GitCommit:"33cf7b9acbb2cb7c9c72a10d6636321fb180b159", GitTreeState:"clean", BuildDate:"2016-10-10T18:13:36Z", GoVersion:"go1.6.3", Compiler:"gc", Platform:"linux/amd64"}
[root@host31 ~]#

确认构成

命令:kubectl get nodes。 通过这条命令我们可以确认到此集群的构成以及各组成node的状态是否都是ready

[root@host31 ~]# kubectl get nodes
NAME      STATUS    AGE
host31    Ready     2h
host32    Ready     1h
host33    Ready     1h
host34    Ready     1h
[root@host31 ~]#

创建Deployment

事前准备

可以使用kubectl run的方式创建也可以使用yaml文件+kubectl create的方式进行创建。本次我们采用后者。首先下载一下kubernetes-dashboard.yaml。而这个文件和easypack_kubernetes.sh在同级目录,上篇文章中在git clone取得的时候已经在本地了。

[root@host31 k8s]# pwd
/root/easypack/k8s
[root@host31 k8s]# ls
easypack_kubernetes.sh  kubernetes-dashboard.yaml  README.md
[root@host31 k8s]#

此文件根官方最新文件的不同点仅在于其版本号我们使用的是前面下载下来的1.4.1,现在最新应该已经是1.4.2了。另外还有一点就是imagePullPolicy我们从Always修改成IfNotPresent了。不然,无论如何它都回去pull这个镜像,网络有不允许,基本上kubernetes1.4的安装和使用就只有和这点相关的有些小坑,体验真心不错。

创建

命令: kubectl create -f kubernetes-dashboard.yaml

[root@host31 k8s]# kubectl create -f kubernetes-dashboard.yaml
deployment "kubernetes-dashboard" created
service "kubernetes-dashboard" created
[root@host31 k8s]#

可以看到「deployment “kubernetes-dashboard” created」的提示信息,这表明已经成功创建了一个Deployment,同时还有一个service也被创建出来了,至于service在Kubernetes中扮演什么角色, 后续的文章中会专门讲解,此处不再赘述。

确认Deployment

get deployments

使用kubectl get deployments可以列出当前的Deployment及其他信息

[root@host31 k8s]# kubectl get deployments --namespace=kube-system
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube-discovery         1         1         1            1           2h
kube-dns               1         1         1            1           2h
kubernetes-dashboard   1         1         1            1           3m
[root@host31 k8s]#

确认Dashboard

命令:kubectl describe svc kubernetes-dashboard –namespace=kube-system
使用此条命令可以确认出该service对外暴露出的可以访问的端口,通过此端口我们可以访问Kubernetes的Dashboard UI界面

[root@host31 k8s]# kubectl describe svc kubernetes-dashboard --namespace=kube-system
Name:                   kubernetes-dashboard
Namespace:              kube-system
Labels:                 app=kubernetes-dashboard
Selector:               app=kubernetes-dashboard
Type:                   NodePort
IP:                     10.4.41.47
Port:                   <unset> 80/TCP
NodePort:               <unset> 31276/TCP
Endpoints:              10.36.0.1:9090
Session Affinity:       None
No events.[root@host31 k8s]#
注解:NodePort 31276 为此服务对外暴露的端口号,通过它和IP即可访问Kubernetes1.4的Dashboard了

访问URL:http://192.168.32.31:31276

qq%e6%88%aa%e5%9b%be20161207102946

namespace和node信息,可以清楚地看到其是由4台机器构成的kubernetes集群。

qq%e6%88%aa%e5%9b%be20161207102955

当然号称可以完成cli的90%功能的dashbaord不仅仅限于查看一下构成而已。详细使用方法可以自行探索,此处不再废话。

Pod

我们在前面讲到过node,node就是作为整个集群中的worker,它是和Master这个节点向比较的。而pod是什么呢,我们在上一篇文章中创建了一个dashboard的Deployment,这就是一个容器化了的应用被部署的实例。而Pod是kubernetes的一组应用容器的抽象,为什么要做这层抽象呢,容器间资源的共享。比如容器的Volume,在比如容器的port,如何在多个容器之间共享,docker-compose就是基于这个目的对docker机能的扩展和延伸。pod里面可以是单独的一个container也可以是多个。pod将一组耦合度很高的容器紧密地结合在一起作为一个整体的方式。比如我们在DevOps的Automation的时候很多项目都倒入了Sonarqube,sonarqube使用一个内部缺省的DB的话一个容器就解决问题,但是如果我们希望把数据能单独进行管理比如使用mysql或者mariadb等的话,我们至少可能需要2个容器,这个时候就可以使用一个pod把这两个都塞进去就可以了。

qq%e6%88%aa%e5%9b%be20161207103002

比如上图中的Pod4,我们就可以理解为有多个被容器化了的应用程序的组合,这些应用程序共享一个集群内部的IP10.10.10.4,共享内部的Volume。

Node

了解完pod之后,在来审视一下塞了pod的node吧。pod作为kubernetes上的最小单位,当我们创建一个kubernetes的Deployment的时候,Deployment其实也自然会使用所需的容器创建pod。通过kubernetes的分配,pod会与node建立联系。我们过去通过使用小机作双机双活等技巧在kubernetes面前显得非常无力,无论是从规模还是控制等,在这里死掉一个pod希望其自动恢复之需要调整它的重启策略,node发生故障的时候也可以在其他的node上自动启动该pod,而这些都可以调整。多年之前给客户做的Tuxedo+MC/SG等作的负载均衡和高可用的架构骄傲过几天,而从去年看到象kubernetes这样的神器的出现感到极其的悲伤和无力,又有很多技能和知识变成废纸了。在这里,所有需要做的已经没有什么了,调节一下参数的程度。

qq%e6%88%aa%e5%9b%be20161207103008

使用kubectl故障排查

我们已经使用过kubectl做过好多事情,在接下来的内容中我们将会进一步的使用kubectl来探寻我们已经部署到kubernetes上的应用,而这些将会是在使用kubernetes进行工作的时候故障排查和确认所会使用的最常用的命令。

命令 说明
kubectl get 诸如kubectl get nodes等等列出资源一览
kubectl describe 对kubectl取到的信息进一步对某一resource进行更深入的确认
kubectl logs 确认pod的某个container的log信息
kubectl exec 类似于docker exec,可以在某个container中执行命令等

确认应用配置信息

我们在前面提到过创建Deployment会创建pod,接下来我们会使用kubectl get pods来进行确认

[root@host31 ~]# kubectl get pods --namespace=kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
dummy-2088944543-rp84q                  1/1       Running   0          6h
etcd-host31                             1/1       Running   0          6h
kube-apiserver-host31                   1/1       Running   0          6h
kube-controller-manager-host31          1/1       Running   0          6h
kube-discovery-982812725-t71i9          1/1       Running   0          6h
kube-dns-2247936740-eez6o               3/3       Running   0          6h
kube-proxy-amd64-4jxh6                  1/1       Running   0          5h
kube-proxy-amd64-5ahum                  1/1       Running   0          6h
kube-proxy-amd64-6wql2                  1/1       Running   0          5h
kube-proxy-amd64-qu4bs                  1/1       Running   0          5h
kube-scheduler-host31                   1/1       Running   0          6h
kubernetes-dashboard-3000474083-y1htc   1/1       Running   0          3h
weave-net-7tvvm                         2/2       Running   0          5h
weave-net-q17ey                         2/2       Running   0          5h
weave-net-uktxs                         2/2       Running   0          6h
weave-net-y8zpe                         2/2       Running   0          5h
[root@host31 ~]#

我们清楚地看到kubernetes-dashboard-3000474083-y1htc作为其中一个pod果然被生成了,接下来想进一步察看这个pod的话需要使用kubectl describe pods来进行了。

[root@host31 ~]# kubectl describe pods kubernetes-dashboard-3000474083-y1htc --namespace=kube-system
Name:           kubernetes-dashboard-3000474083-y1htc
Namespace:      kube-system
Node:           host34/192.168.32.34
Start Time:     Wed, 09 Nov 2016 07:37:22 -0500
Labels:         app=kubernetes-dashboard
                pod-template-hash=3000474083
Status:         Running
IP:             10.36.0.1
Controllers:    ReplicaSet/kubernetes-dashboard-3000474083
Containers:
  kubernetes-dashboard:
    Container ID:       docker://f97b36f8de62d911356b20f3e8840c14b7ed11a35443a865ae78d79d439a7667
    Image:              gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.1
    Image ID:           docker://sha256:1dda73f463b239955d4cf94a9cd525ab5306bee0eb53c17534d3282bc50ae7aa
    Port:               9090/TCP
    State:              Running
      Started:          Wed, 09 Nov 2016 07:37:24 -0500
    Ready:              True
    Restart Count:      0
    Liveness:           http-get http://:9090/ delay=30s timeout=30s period=10s #success=1 #failure=3
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-852jy (ro)
    Environment Variables:      <none>
Conditions:
  Type          Status
  Initialized   True
  Ready         True
  PodScheduled  True
Volumes:
  default-token-852jy:
    Type:       Secret (a volume populated by a Secret)
    SecretName: default-token-852jy
QoS Class:      BestEffort
Tolerations:    dedicated=master:Equal:NoSchedule
No events.[root@host31 ~]#

在这里我们看到了很多信息:Port/状态/ContainerID/Namespace等更细的信息。
关于其他kubectl相关的命令我们会在别的文章中展开,此处不再赘述。

Kubernetes Services

Pod在整个集群中有他们自己的惟一IP,但是这些IP是不对Kubernetes外部暴露的,换句话说,外部是无法直接访问它们的.
而且我们还需要考虑在使用的过程中,这些pod有可能会发生很多变化,被停止,被删除,被其他pod替换等,比如被删除之后IP就不存在了,如何访问呢?加之总体的pod的量可能会非常之大,因此我们需要一种方式让pod和应用能够自动的发现彼此.

Kubernetes使用service的概念将pod分组,service是一个逻辑上的抽象层,然后在此基础上进行对外提供访问的接口同时使得负载均衡和服务发现变得可能.

了解Services:https://www.kubernetes.org.cn/kubernetes-services

Kubernetes支持如下LoadBalancer和NodePort两种方式对外提供服务访问

类型 说明
LoadBalancer 此种应用场景一般在于当部署kubernetes到云上得时候为最多。一般云服务商会提供外接的负载均衡器(LoadBalancer), 这种方式需要指定一个公共的IP地址。
NodePort 在所有的node上使用相同的port暴露服务,最为直接的可以理解的结果是使用任何一个Node的IP加此Port的URL即可访问

理解了这些之后,我们再来重新审视一下service在kubernetes中的使用方式. 如下图所示我们有Service A和Service B两个服务. Service A由一个pod组成,而Service B由3个port分别在两个node上所组成,每个pod都有集群内部所提供的唯一IP,但是对外则通过Service层所提供的对外接口的IP. 在前面讲过的Deployment和Service的关系在此也可以看得更加清楚,Service A和Deployment A之间实现互关联的,在使用yaml文件创建kubernetes的Deployment时候为什么同时也创建了一个service,就是希望能够访问dashboard而已。现在再看一下kubernetes-dashboard.yaml的内容你会发现其实那里面就是kind: Deployment和kind: Service的定义而已,自然会两个都创建。

qq%e6%88%aa%e5%9b%be20161207103021

service职责

负载均衡

Service是一组pod在逻辑意义上所组成的抽象层,它能够在这一层上进行整体的协调,负载均衡是其中的一个作用。在service由多个pod组成的时候这个功能变得非常有用。

服务发现

service同时也负责在整个集群之内的服务发现,后面我们会进一步展开相关内容。

Lable

Service在kubernetes中扮演的这样重要的一个角色,Service是一组pod在逻辑意义上所组成的抽象层,它是如何将pod进行分组的,答案就是Lable。

PS:了解Lable https://www.kubernetes.org.cn/kubernetes-labels

Label是key/value的可以设定到pod上的键值对,使用Lable对pod进行分组,而Service使用Lable Selector对其所用到的lable进行选择,这样就将pod进行了分组和设定。
使用lable可以使得这些pod逻辑意义上的组合更具实际意义,更加可读,比如:

  • 环境:production, test, dev
  • 版本:beta, v1.3
  • service/server类型:frontend, backend, database

下面这张图则非常清晰地描述了Lable是在service中尽职尽责的。

qq%e6%88%aa%e5%9b%be20161207103030

sonarqube是在很多项目中都会用到的一个可以完成代码审查等功能的开源平台,lable和service在kubernetes中是如何使用的,接下来我们将会利用kubectl来演示一下如何将sonarqube导入进去。

事前确认

没有任何sonar相关的deployment和service以及pod在运行

[root@host31 ~]# kubectl get pods
[root@host31 ~]# kubectl get deployments
[root@host31 ~]# kubectl get services
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.0.0.1     <none>        443/TCP   11h
[root@host31 ~]#

创建一个Deployment

注意label在此处的使用,–lables指定要设定到其上的Label为app=sonarqube

[root@host31 ~]# kubectl run sonar --replicas=2 --labels="app=sonarqube" --image=docker.io/sonarqube  --port=9000
deployment "sonar" created
[root@host31 ~]#

创建后确认,此时生成了pod和deployment,但是还没有生成service

[root@host31 ~]# kubectl get pods
NAME                     READY     STATUS              RESTARTS   AGE
sonar-2095694103-eqyxe   1/1       Running             0          7s
sonar-2095694103-htvml   0/1       ContainerCreating   0          7s
[root@host31 ~]# kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
sonar     2         2         2            2           14s
[root@host31 ~]# kubectl get services
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.0.0.1     <none>        443/TCP   11h
[root@host31 ~]#

生成service

[root@host31 ~]# kubectl expose deployment/sonar --type="NodePort" --port 9000
service "sonar" exposed
[root@host31 ~]#

生成后确认

[root@host31 ~]# kubectl get services
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
kubernetes   10.0.0.1       <none>        443/TCP    11h
sonar        10.0.186.102   <nodes>       9000/TCP   16s
[root@host31 ~]#
[root@host31 ~]# kubectl describe services
Name:                   kubernetes
Namespace:              default
Labels:                 component=apiserver
                        provider=kubernetes
Selector:               <none>
Type:                   ClusterIP
IP:                     10.0.0.1
Port:                   https   443/TCP
Endpoints:              192.168.32.31:6443
Session Affinity:       ClientIP
No events.

Name:                   sonar
Namespace:              default
Labels:                 app=sonarqube
Selector:               app=sonarqube
Type:                   NodePort
IP:                     10.0.186.102
Port:                   <unset> 9000/TCP
NodePort:               <unset> 30097/TCP
Endpoints:              10.42.0.1:9000,10.44.0.1:9000
Session Affinity:       None
No events.[root@host31 ~]#

使用label确认

[root@host31 ~]# kubectl get pods -l app=sonarqube
NAME                     READY     STATUS    RESTARTS   AGE
sonar-2095694103-eqyxe   1/1       Running   0          3m
sonar-2095694103-htvml   1/1       Running   0          3m
[root@host31 ~]#
[root@host31 ~]#
[root@host31 ~]# kubectl get services -l app=sonarqube
NAME      CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
sonar     10.0.186.102   <nodes>       9000/TCP   3m
[root@host31 ~]#

Sonarqube画面

qq%e6%88%aa%e5%9b%be20161207103043

Dashboard确认

qq%e6%88%aa%e5%9b%be20161207103056

qq%e6%88%aa%e5%9b%be20161207103103

replicas

在上篇文章中创建sonarqube的deployment我们使用了–replicas=2这样一个参数设定, 这样的话就会启动两个pod其中都运行着sonarqube, 比如我们的服务经过设计已无状态的方式对外提供服务的时候, 理论上来说之需要通过调节服务的实例的个数即可满足实际世界中不断增长的需要.

应用扩展

应用扩展之前只是在一个node上运行着一个replica

qq%e6%88%aa%e5%9b%be20161207103109

由于kubernetes的眩技需要,我们需要将其扩展成如下4个
qq%e6%88%aa%e5%9b%be20161207103117

事前确认

sonar这个deployment已经启动了2个pod

[root@host31 ~]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
sonar-2095694103-eqyxe   1/1       Running   0          13h       10.42.0.1   host33
sonar-2095694103-htvml   1/1       Running   0          13h       10.44.0.1   host32
[root@host31 ~]#

扩展至5个

让我们装作这是一个你封装好的服务,它符合微服务的要件,外界需求不断增长的时候我们只需要提供更多的replica的实例运行起来即可。假如从2个扩展至5个可以实现这一需要,在kubernetes中之需要如下一条语句。

[root@host31 ~]# kubectl scale deployments/sonar --replicas=5
deployment "sonar" scaled
[root@host31 ~]#

已经提示sonar已经扩展了,但是实际情况呢,我们来确认:

[root@host31 ~]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
sonar-2095694103-avnz0   1/1       Running   0          2m        10.36.0.2   host34
sonar-2095694103-eqyxe   1/1       Running   0          14h       10.42.0.1   host33
sonar-2095694103-htvml   1/1       Running   0          14h       10.44.0.1   host32
sonar-2095694103-mrspk   1/1       Running   0          2m        10.42.0.2   host33
sonar-2095694103-wp317   1/1       Running   0          2m        10.32.0.3   host31
[root@host31 ~]#

确认了一下之后,果然起了5个,host33这台机器任劳任怨启动了2个,其它的机器一台一个

调节至3个

从5个调节到3个,为什么不直接调整到3个呢,这个是减少,为了眩技。让我们装作不知道swarm等等也都具有极其简单的扩展功能来确认一下这个结果

[root@host31 ~]# kubectl scale deployments/sonar --replicas=3
deployment "sonar" scaled
[root@host31 ~]#
[root@host31 ~]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
sonar-2095694103-avnz0   1/1       Running   0          7m        10.36.0.2   host34
sonar-2095694103-eqyxe   1/1       Running   0          14h       10.42.0.1   host33
sonar-2095694103-htvml   1/1       Running   0          14h       10.44.0.1   host32
[root@host31 ~]#

Self-healing

据说kubernetes会一直监控replica的个数,不够就启动,其所谓的Self-healing。来确认一下:

host32启动的container

[root@host32 ~]# docker ps
CONTAINER ID        IMAGE                                              COMMAND                  CREATED             STATUS              PORTS               NAMES
138cff1deabf        docker.io/sonarqube                                "./bin/run.sh"           14 hours ago        Up 14 hours                             k8s_sonar.37b5347c_sonar-2095694103-htvml_default_5051ad06-a6c4-11e6-89e7-000c293e8b29_0ba07f6c
5ec6cc5d0c36        gcr.io/google_containers/pause-amd64:3.0           "/pause"                 14 hours ago        Up 14 hours                             k8s_POD.d8dbe16c_sonar-2095694103-htvml_default_5051ad06-a6c4-11e6-89e7-000c293e8b29_03c1bbee
a7c9aac7111f        weaveworks/weave-npc:1.8.0                         "/usr/bin/weave-npc"     25 hours ago        Up 25 hours                             k8s_weave-npc.259594a6_weave-net-q17ey_kube-system_d1f9ae63-a668-11e6-89e7-000c293e8b29_aa9ea079
c1d57852e63b        weaveworks/weave-kube:1.8.0                        "/home/weave/launch.s"   25 hours ago        Up 25 hours                             k8s_weave.cafdac57_weave-net-q17ey_kube-system_d1f9ae63-a668-11e6-89e7-000c293e8b29_37443cc1
0d5df3bdd248        gcr.io/google_containers/kube-proxy-amd64:v1.4.1   "/usr/local/bin/kube-"   25 hours ago        Up 25 hours                             k8s_kube-proxy.a7bb6a_kube-proxy-amd64-qu4bs_kube-system_d1f972d2-a668-11e6-89e7-000c293e8b29_d811f1c7
d6ae3b2ee3b6        gcr.io/google_containers/pause-amd64:3.0           "/pause"                 25 hours ago        Up 25 hours                             k8s_POD.d8dbe16c_kube-proxy-amd64-qu4bs_kube-system_d1f972d2-a668-11e6-89e7-000c293e8b29_d369cf01
565c9303e13e        gcr.io/google_containers/pause-amd64:3.0           "/pause"                 25 hours ago        Up 25 hours                             k8s_POD.d8dbe16c_weave-net-q17ey_kube-system_d1f9ae63-a668-11e6-89e7-000c293e8b29_83f80692
[root@host32 ~]#

停掉host32的pod

[root@host32 ~]# docker stop 138cff1deabf
138cff1deabf
[root@host32 ~]#

再次立即确认

再次确认结果,发现host32果然状态是Error

[root@host31 ~]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
sonar-2095694103-avnz0   1/1       Running   0          12m       10.36.0.2   host34
sonar-2095694103-eqyxe   1/1       Running   0          14h       10.42.0.1   host33
sonar-2095694103-htvml   0/1       Error     0          14h       10.44.0.1   host32
[root@host31 ~]#

稍等再确认

已经恢复正常

[root@host31 ~]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
sonar-2095694103-avnz0   1/1       Running   0          12m       10.36.0.2   host34
sonar-2095694103-eqyxe   1/1       Running   0          14h       10.42.0.1   host33
sonar-2095694103-htvml   1/1       Running   1          14h       10.44.0.1   host32
[root@host31 ~]#

应用更新

应用程序开发测试部署完毕之后不是因为着结束,对传统的以开发或者外包为主的项目来说可能是一个终点,但是对于项目的运行维护团队来说这刚刚是开始。很少有项目上线之后不需要做任何变化,因为没有测试出来的bug在理论上依然是存在的,更重要的是这个世界在一直不停的变化,很难出现一个部署上去的应用会一成不变。使用新的容器化的应用替代旧的也是kubernetes部署中需要做的事情。

qq%e6%88%aa%e5%9b%be20161207103126

零服务停止时间

根据咨询公司Forrester的预测,到2020年企业部署更新其系统的平均频度将会达到每年120次以上,这意味开发侧回比现在想更快的推进部署的速度,对于很多传统企业系统来说,系统部署上线频度的加快对系统上线无疑就是一场战役,平均3天必须部署一次也是一个很大的挑战。而最终用户想的非常的单纯,你那个什么系统,不管我什么时候想用,我用的时候它必须好用,不然我就不用你。部署的时候也不能停止服务或者尽可能的减少服务停止时间,这是现在非常单纯的需求。
而这个单纯的需求其实并不是那么单纯,比如在很多系统中都需要有数据库,而到目前为止,关系型数据库仍然占有最大的份额。而在关系型数据库中,Oracle一直占据着半壁江山,而Oracle什么时候推出的Hot Patch的功能,可以自行查询,没几年的事。也就是说有近乎一半的含有数据库的系统,在安装Oracle的补丁的时候需要停下系统。服务无停止也就是说说而已了,所以这也是很多运维人员很辛苦的原因,他们需要半夜/周末/假期等正常人休息的时候去把系统进行更新以维护系统的正常运转。因为目前频度还不是很高,大体没有人觉得这是一个问题,但是如果3天一次部署的话,这个问题就浮出了水面。

Rolling Update

Kubernetes使用Rolling Update解决了不停服务正常更新部署的困境,简单到只要一条语句就解决了这个问题。简单来说,kubernetes会滚动更新,一个一个的替换,从全部是旧的replica到新旧共存再到全部是新的。接下来我们看一下这个过程。

Update之前

更新之前,Service A在3个node上启动了4个pod,每个pod中放的都是相同的容器化的应用
qq%e6%88%aa%e5%9b%be20161207103133

Rolling update

更新的时候,使用新的替换旧的,详细地如下图所示。

qq%e6%88%aa%e5%9b%be20161207103140

接下来更新剩余的

qq%e6%88%aa%e5%9b%be20161207103148

qq%e6%88%aa%e5%9b%be20161207103156

这样整个Rolling update就完成了,而且全程是可以访问的状态。接下来我们来看一下一个模拟的试验。

事前准备

准备镜像

为了演示我们准备如下两个镜像

名称 版本 说明
liumiaocn/nginx v1 nginx原本镜像,没有做任何修改
liumiaocn/nginx v2 只修改了nginx的index.html页面,以便演示

为了提高执行的速度,提前在各个node上pull下来所需要的镜像

[root@host32 ~]# docker pull liumiaocn/nginx:v1; docker pull liumiaocn/nginx:v2
Trying to pull repository docker.io/liumiaocn/nginx ...
v1: Pulling from docker.io/liumiaocn/nginx

Digest: sha256:6010826513c2ad69b121cc7149798b19ed3099d781cbfdccd87ecbe0fb6ba887
Status: Downloaded newer image for docker.io/liumiaocn/nginx:v1
Trying to pull repository docker.io/liumiaocn/nginx ...
v2: Pulling from docker.io/liumiaocn/nginx

386a066cd84a: Already exists
7bdb4b002d7f: Already exists
49b006ddea70: Already exists
39ceba9e1264: Pull complete
Digest: sha256:6b4ee82a23c625540dbfb615d8d3ca9900a9e076f67692abc6537abe38380ade
Status: Downloaded newer image for docker.io/liumiaocn/nginx:v2
[root@host32

运行nginx(v1)

[root@host31 ~]# kubectl run nginx --replicas=4 --labels="app=nginx" --image=docker.io/liumiaocn/nginx:v1 --port=80
deployment "nginx" created
[root@host31 ~]#
[root@host31 ~]# kubectl expose deployment/nginx --type="NodePort" --port 80
service "nginx" exposed
[root@host31 ~]#

确认service

[root@host31 ~]# kubectl get services nginx
NAME      CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
nginx     10.9.251.55   <nodes>       80/TCP    43s
[root@host31 ~]#
[root@host31 ~]# kubectl describe services nginx
Name:                   nginx
Namespace:              default
Labels:                 app=nginx
Selector:               app=nginx
Type:                   NodePort
IP:                     10.9.251.55
Port:                   <unset> 80/TCP
NodePort:               <unset> 31766/TCP
Endpoints:              10.32.0.3:80,10.36.0.3:80,10.42.0.3:80 + 1 more...
Session Affinity:       None
No events.[root@host31 ~]#

确认nginx页面(v1)

qq%e6%88%aa%e5%9b%be20161207103208

Pod详细确认

[root@host31 ~]# kubectl get pods -o wide -l "app=nginx"
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
nginx-1658045568-0iwrn   1/1       Running   0          4m        10.36.0.3   host34
nginx-1658045568-610xo   1/1       Running   0          4m        10.32.0.3   host31
nginx-1658045568-lzwxm   1/1       Running   0          4m        10.42.0.3   host33
nginx-1658045568-m8eu3   1/1       Running   0          4m        10.44.0.2   host32
[root@host31 ~]#

Rolling update

只需要一行语句即可完成所有部署的完成。

[root@host31 ~]# kubectl set image deployments/nginx nginx=liumiaocn/nginx:v2
deployment "nginx" image updated
[root@host31 ~]#

建议:也可以不要事前将所有节点的镜像都pull下来,这样看不到前面所讲的过程了,因为这个过程很快,所以下面的4个pod几乎是同时显示为running状态

Rolling update确认

pod确认

虽然看起来没有什么变化,仔细确认会发现,集群内的IP都换了,原理上面已经阐述,不再赘述。

[root@host31 ~]# kubectl get pods -o wide -l "app=nginx"
NAME                     READY     STATUS    RESTARTS   AGE       IP          NODE
nginx-1948172500-2r66l   1/1       Running   0          17s       10.42.0.3   host33
nginx-1948172500-hqgh5   1/1       Running   0          20s       10.32.0.3   host31
nginx-1948172500-ybxa0   1/1       Running   0          20s       10.44.0.3   host32
nginx-1948172500-ykaew   1/1       Running   0          16s       10.44.0.2   host32
[root@host31 ~]#

确认nginx页面(v2)

qq%e6%88%aa%e5%9b%be20161207103216

从这里也能看出来已经部署完毕了。

总结

至此,kubernetes的基础的特别重要的概念基本上都在这六个步骤中进行了简单的说明。kubernetes确实是一大利器,如何更好地使用它还是值得我们进一步地探索。