使用Kubernetes V1來管理Docker的擴展
Kubernetes是一款開源的項目,管理Linux容器集群,並可將集群作為一個單一的系統來對待。其可跨多主機來管理和運行Docker容器、提供容器的定位、服務發現以及復制控制。它由Google發起,現在則得到如微軟、紅帽、IBM和Docker等眾多廠商的支持。
Google使用容器技術有著超過十年的歷史,每周要啟動超過2億臺容器。通過Kubernetes,Google分享了他們關於容器的專業經驗,即創建大規模運行容器的開放平臺。
一旦用戶開始使用Docker容器,那麽問題就來了,一、如何大規模並在多個容器主機上啟動它們,且能夠在主機間取得平衡。二、還需要高度抽象出API來定義如何邏輯上組織容器、定義容器池、負載均衡以及相似性。該項目就是為了解決這兩個問題而生的。
Kubernetes概念
從無到有搭建分布式系統:小米的開源實踐
從DevOps到ChatOps,攜程的選擇
阿裏鷹眼:如何從每天萬億次調用中定位問題
第一手實踐:強一致高可用系統設計
同程巡風掃描器:避免心臟滴血、NSA泄露等漏洞
Kubernetes的架構被定義為由一個master服務器和多個minons服務器組成。命令行工具連接到master服務器的API端點,其可以管理和編排所有的minons服務器,Docker容器接收來自master服務器的指令並運行容器。
命名空間:基於它們自身的配額和網絡的集群分區。
節點:每個安裝由Kubelet服務的Docker主機,Kubelet服務用於接收來自Master的指令,且管理運行容器的主機(原來叫做minions)。
Pod:定義了一組綁在一起的容器,可以部署在同一節點,例如一個數據庫或者是web服務器。
Replication controller:定義了需要運行多少個Pod或者容器。跨多個minons來調度容器。
Service:定義了由容器所發布的可被發現的服務/端口,以及外部代理通信。服務會映射端口到外部可訪問的端口,而所映射的端口是跨多個節點的Pod內運行的容器的端口。
kubectl:命令行客戶端,連接到master來管理Kubernetes。
本文中所用的例子應用是Jenkins持續集成服務,Jenkins是典型的通過主從服務來建立分布式的工作任務的例子。Jenkins由jenkins swarm插件來配置,運行一個jenkins主服務和多個jenkins從服務,所有的Jenkins服務都以Docker容器的方式跨多個主機運行。swarm從服務在啟動時連接到Jenkins主服務,然後就可以運行Jenkins任務了。例子中使用的配置文件在從Github上下載到,至於Docker的鏡像,對於Jenkins主服務來說,從casnchez/jenkins-swarm獲取,即加入了swarm插件的官方Jenkins鏡像。對於jenkins從服務來說,從csanchez/jenkins-swarm-slave獲取,此僅是在JVM容器中運行了jenkins從服務而已。
新特性
版本1.0的發布帶來穩定的API,以及管理大型集群的功能,包括DNS、負載均衡、擴展、應用程序級別的健康檢測、服務賬戶、以及使用命名空間來隔離應用。API和命令行現在可以支持遠程執行、跨多個主機的日誌收集、以及資源監控。支持了更多的基於網絡的卷,它們有Google計算引擎的持久磁盤、AWS伸縮塊存儲、NFS、Flocker、GlusterFS。
版本1.1在1.0的基礎上有增加了一些新的特性,包括正常終止pod、支持Docker1.8、自動橫向擴展、以及在Kubernetes集群中使用Pod來調度短時間運行的任務(相對長時間運行的任務)的新的任務對象。
創建Kubernetes集群
Kubernetes提供了多個操作系統和雲/虛擬化提供商下創建集群的腳本,有Vagrant(用於本地測試)、Google Compute Engine、Azure、Rackspace等。 Google容器引擎(GKE)提供了Kubernetes集群即服務的支持。接下來我們會以本地的Vagrant虛擬機和Google容器引擎下來創建一個集群,至於選擇哪個就看讀者本身的喜好了,盡管GKE提供了更多的關於網絡和持久存儲方面的特性。這些例子也同樣可以運行在任何其它的供應商平臺下,當然需要少許的改動,我們也會在相應的章節有所描述。
Vagrant
本文所實踐的例子就是運行在Vagrant之上的本地集群,使用Fedora作為操作系統,遵照的是官方入門指南,測試的Kubernetes版本是1.0.6。取代了默認的3臺節點(Docker主機),而是使用了2個minion,兩臺主機就足夠可以展示Kubernetes的能力了,三臺就有點浪費。
當你下載了Kubernetes,然後將至解壓後,示例即可在本地的目錄下運行。初學者創建集群僅需要一個命令,即./cluster/kube-up.sh
。
$ export KUBERNETES_PROVIDER=vagrant$ export KUBERNETES_NUM_MINIONS=2$ ./cluster/kube-up.sh
集群創建的快慢取決於機器的性能和內部的帶寬。但是其最終完成不能有任何的錯誤,而且它僅需要運行一次。
和Kubernetes交互的命令行工具叫做kubectl。Kubernetes的發布軟件包中包含了一個便利的工具-cluster/kubectl.sh
,但是命令行可以獨立安裝,而且也可以使用由kubeup.sh所創建的配置文件~/.kube/config
。
確保kubectl在環境變量PATH中,因為在接下來的章節中將用到。要檢查集群是啟動並運行的,使用kubectl get nodes
。
$ kubectl get nodes NAME LABELS STATUS10.245.1.3 kubernetes.io/hostname=10.245.1.3 Ready10.245.1.4 kubernetes.io/hostname=10.245.1.4 Ready
google 容器引擎
按照Google雲平臺站點上的步驟註冊Google容器引擎,啟用容器引擎API,安裝gcloud工具,然後使用gcloud init
配置Google雲平臺。
一旦你成功登陸後,首先要更新kubectl工具,設置一些默認值,然後創建Kubernetes集群。確保使用你的正確的Google雲項目ID。
$ gcloud components update kubectl $ gcloud config set project prefab-backbone-109611$ gcloud config set compute/zone us-central1-f$ gcloud container clusters create kubernetes-jenkinsCreating cluster kubernetes-jenkins...done.Created [https://container.googleapis.com/v1/projects/prefab-backbone-109611/zones/us-central1-f/clusters/kubernetes-jenkins].kubeconfig entry generated for kubernetes-jenkins.NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE STATUS kubernetes-jenkins us-central1-f 1.0.6 107.178.220.228 n1-standard-1 RUNNING
設置剛剛創建的集群的默認值,並為kubectl
獲取證書。
$ gcloud config set container/cluster kubernetes-jenkins$ gcloud container clusters get-credentials kubernetes-jenkins
運行kubectl
獲取節點信息,正確的輸出應是集群中有三臺節點。
$ kubectl get nodes NAME LABELS STATUS gke-kubernetes-jenkins-e46fdaa5-node-5gvr kubernetes.io/hostname=gke-kubernetes-jenkins-e46fdaa5-node-5gvr Ready gke-kubernetes-jenkins-e46fdaa5-node-chiv kubernetes.io/hostname=gke-kubernetes-jenkins-e46fdaa5-node-chiv Ready gke-kubernetes-jenkins-e46fdaa5-node-mb7s kubernetes.io/hostname=gke-kubernetes-jenkins-e46fdaa5-node-mb7s Ready
集群信息
在集群啟動後,我們就可以訪問API端點或者是kube-ui
的界面。
$ kubectl cluster-info Kubernetes master is running at https://107.178.220.228KubeDNS is running at https://107.178.220.228/api/v1/proxy/namespaces/kube-system/services/kube-dns KubeUI is running at https://107.178.220.228/api/v1/proxy/namespaces/kube-system/services/kube-ui Heapster is running at https://107.178.220.228/api/v1/proxy/namespaces/kube-system/services/monitoring-heapster
代碼實例
從Github獲取示例配置文件:
$ git clone --branch infoq-kubernetes-1.0 https://github.com/carlossg/kubernetes-jenkins.git
命名空間
命名空間可以跨多個用戶和應用來隔離資源。
$ kubectl get namespaces NAME LABELS STATUS default <none> Active kube-system <none> Active
命名空間可以關聯到資源配額,用來限制所使用的cpu、內存、以及一些各種類型(Pod、服務、復制控制器等)的資源。在下面的例子中,使用了默認的命名空間,但是可以自行創建命名空間來隔離資源,也有一些運行的服務通過kube-system
命名空間提供端點給Kubernetes系統資源。
$ kubectl get endpoints --all-namespacesNAMESPACE NAME ENDPOINTS default jenkins <none>default kubernetes 107.178.220.228:443kube-system kube-dns 10.172.2.3:53,10.172.2.3:53kube-system kube-ui 10.172.0.5:8080kube-system monitoring-heapster 10.172.0.6:8082
Pod
在一個pod中可以指定多個容器,這些容器均部署在同一Docker主機中,在一個pod中的容器的優勢在於可以共享資源,例如存儲卷,而且使用相同的網絡命名空間和IP地址。
在此Jenkins的實例中,我們有一個pod來運行Jenkins主服務,但是這是一個試驗性質的,pod在調度失效、節點失效、缺少資源、或者是節點維護無法存活。假如發生了作為pod運行Jenkins主服務的節點宕掉了,容器永遠不會去重新啟動它。基於此原因,我們將會在下面的例子中將Jenkins主服務以復制控制器的方式運行,基於一個實例。
復制控制器
復制控制器允許跨多個節點運行多個pod,正如上面所提到的,Jenkins主服務會以一個復制控制器來運行到一個存活的節點上。當Jenkins Slaves使用復制控制器來確保一直會有slave的池來運行Jenkins任務。
持久卷
Kubernetes支持多種後端的存儲卷
emptyDir
hostPath
gcePersistentDisk
awsElasticBlockStore
nfs
iscsi
glusterfs
rbd
gitRepo
secret
persistentVolumeClaim
卷在默認情況下均是空的目錄,類型為emptydir
,其生命周期是以pod的生命周期為準的,所以即使是容器宕掉了,持久存儲仍在。其它的卷類型是hostpath
,此是一種在容器內部直接掛在其所在主機服務器上的存儲的一種方式。
secret
的卷是用於哪些較敏感的信息,諸如用於pod的密碼。密文可以保存在Kubernetes API中,pod使用的時候將其掛載為文件,而且會在內存中以tmpfs的形式作備份。
持久卷是實現了用戶所“要求”的持久化的存儲(諸如gcePersistent
或iscsi
卷)而且還無須知道特定雲環境的細節,在pod中使用persistentVolumeClaim
就可以掛載。
在一個典型的Kubernetes安裝中,後端的卷是可用於網絡的,所以數據是可以掛載到任何的節點上的,從而pod可以調度它們。
在Vagrant的試驗環境中,我們使用了一個簡單的hostDir
,這樣做的弊端就是pod從一個節點移到另外一個節點時,數據不會跟著做遷移。
當使用Google容器引擎時,我們需要創建一個GCE的磁盤,這會用於主的Jenkins pod,存放Jenkins的數據。
gcloud compute disks create --size 20GB jenkins-data-disk
Jenkins主復制控制器
為了創建一個Jenkins主pod,我們運行kubectl及其Jenkins容器復制控制器的定義,使用的Docker鏡像是csanchez/jenkins-swarm
,端口是8080和50000,映射到容器,這樣就可以訪問Jenkins的Web用戶界面和從服務的API了,以及掛載卷到路徑/var/jenkins_home
。
livenessProbe
的配置屬性在自我修復一節有所涉及,當然你也可以在GitHub上的例子中找到。
Vagrant環境的定義(jenkins-master-vagrant.yml)如下所示:
apiVersion: "v1" kind: "ReplicationController" metadata: name: "jenkins" labels: name: "jenkins" spec: replicas: 1 template: metadata: name: "jenkins" labels: name: "jenkins" spec: containers: - name: "jenkins" image: "csanchez/jenkins-swarm:1.625.1-for-volumes" ports: - containerPort: 8080 - containerPort: 50000 volumeMounts: - name: "jenkins-data" mountPath: "/var/jenkins_home" livenessProbe: httpGet: path: / port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 volumes: - name: "jenkins-data" hostPath: path: "/home/docker/jenkins"
對於Google容器引擎中的定義,只需將上述中關於卷的定義jenkins-data
替換我我們剛才所創建的GCE磁盤即可:
volumes: - name: "jenkins-data" gcePersistentDisk: pdName: jenkins-data-disk fsType: ext4
創建復制控制器是使用一樣的命令:
$ kubectl create -f jenkins-master-gke.yml
創建完成後,Jenkins復制控制器和Pod會顯示在相應的列表中:
$ kubectl get replicationcontrollers CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS jenkins jenkins csanchez/jenkins-swarm:1.625.1-for-volumes name=jenkins 1$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-gs7h9 0/1 Pending 0 5s
可使用kubectl describe
來獲得更多的細節,諸如查看調度到了哪個節點,或者是部署期間出現的錯誤:
$ kubectl describe pods/jenkins-gs7h9Name: jenkins-gs7h9Namespace: default Image(s): csanchez/jenkins-swarm:1.625.1-for-volumesNode: gke-kubernetes-jenkins-e46fdaa5-node-chiv/10.240.0.3Labels: name=jenkins Status: Running Reason: Message: IP: 10.172.1.32Replication Controllers: jenkins (1/1 replicas created) Containers: jenkins: Image: csanchez/jenkins-swarm:1.625.1-for-volumes Limits: cpu: 100m State: Running Started: Sun, 18 Oct 2015 18:41:52 +0200 Ready: True Restart Count: 0Conditions: Type Status Ready TrueEvents: FirstSeen LastSeen Count From SubobjectPath Reason Message Tue, 13 Oct 2015 20:36:50 +0200 Tue, 13 Oct 2015 20:36:50 +0200 1 {scheduler } scheduled Successfully assigned jenkins to gke-kubernetes-jenkins-e46fdaa5-node-5gvr Tue, 13 Oct 2015 20:36:59 +0200 Tue, 13 Oct 2015 20:36:59 +0200 1 {kubelet gke-kubernetes-jenkins-e46fdaa5-node-5gvr} implicitly required container POD pulled Pod container image "gcr.io/google_containers/pause:0.8.0" already present on machine [...]
一段時間後,它會下載Docker鏡像到節點,我們可以查看它的狀態為Running
:
$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-gs7h9 1/1 Running 0 15s
Jenkins容器日誌可從Kubernetes API來訪問:
$ kubectl logs jenkins-gs7h9 Running from: /usr/share/jenkins/jenkins.war webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")...INFO: Jenkins is fully up and running
服務
Kubernetes可以定義服務,一種讓容器可以彼此發現的方法,代理請求到相應的節點以及做到負載均衡。
每個服務都定義了一些類型字段,這些字段是告訴如何來訪問這些服務的。
ClusterIP:僅用於集群內部的IP,默認;
NodePort:用於集群IP,但是也會拋出集群中每個節點的服務端口(每個節點的端口都一樣);
LoadBlancer: 一些雲服務提供商會提供對外部負載均衡起的支持(目前有GKE和AWS),使用ClusterIP和NodePort,但是可以要求雲服務提供商使用何種負載均衡器來轉發服務。
默認情況下,服務只有ClusterIP
類型,這就意味著只能從集群內部訪問到服務。為了能夠讓外部也可以訪問到服務,或是NodePort
,或是LodaBlancer
,需要打開其中一個。
每個服務都會被分配一個唯一的IP地址,且此IP的生命周期伴隨著服務。如果我們有多個Pod匹配到服務的話,則定義服務的負載均衡到流量到這些Pod。
接下來,我們會創建一個id為jenkins的服務,指配的Pod為標簽是name=jenkins
,正如在jenkins-master復制控制器定義中聲明的,以及轉發鏈接到端口8080、Jenkins Web UI、端口50000,均是Jenkins Swarm插件所需要的。
Vagrant
在Vagrant中運行,我們可以使用kubectl
來創建一個NodePort
類型的服務。
--- apiVersion: "v1" kind: "Service" metadata: name: "jenkins" spec: type: "NodePort" selector: name: "jenkins" ports: - name: "http" port: 8080 protocol: "TCP" - name: "slave" port: 50000 protocol: "TCP"$ kubectl create -f service-vagrant.yml $ kubectl get services/jenkins NAME LABELS SELECTOR IP(S) PORT(S) jenkins <none> name=jenkins 10.247.0.117 8080/TCP 50000/TCP
服務的描述信息會展示節點的那個端口映射到了容器內的8080。
$ kubectl describe services/jenkinsName: jenkinsNamespace: defaultLabels: <none>Selector: name=jenkinsType: NodePortIP: 10.247.0.117Port: http 8080/TCPNodePort: http 32440/TCPEndpoints: 10.246.2.2:8080Port: slave 50000/TCPNodePort: slave 31241/TCPEndpoints: 10.246.2.2:50000Session Affinity: None No events.
我們可以使用我們的Vagrant的任何節點來訪問Jenkins的Web UI,在此例中服務的定義NodePort
為端口32440,所以10.245.1.3:32440或10.245.1.4:32440會轉發以連接到正確的容器。
Goodle容器引擎
在GKE中運行的話,我們可以使用公網IP來創建類生產環境的負載均衡器。
--- apiVersion: "v1" kind: "Service" metadata: name: "jenkins" spec: type: "LoadBalancer" selector: name: "jenkins" ports: - name: "http" port: 80 targetPort: 8080 protocol: "TCP" - name: "slave" port: 50000 protocol: "TCP"$ kubectl create -f service-gke.yml $ kubectl get services/jenkins NAME LABELS SELECTOR IP(S) PORT(S) jenkins <none> name=jenkins 10.175.245.100 80/TCP 50000/TCP
服務的描述信息會展示給我們負載均衡器的IP(在創建的時候可能會稍花費一點時間),此例中的IP是:104.197.19.100
$ kubectl describe services/jenkinsName: jenkinsNamespace: defaultLabels: <none>Selector: name=jenkinsType: LoadBalancerIP: 10.175.245.100LoadBalancer Ingress: 104.197.19.100Port: http 80/TCPNodePort: http 30080/TCPEndpoints: 10.172.1.5:8080Port: slave 50000/TCPNodePort: slave 32081/TCPEndpoints: 10.172.1.5:50000Session Affinity: None No events.
我們也可以使用Google 雲API來獲得關於負載均衡的信息。(在GCE的術語中叫做forwarding-rule
)
$ gcloud compute forwarding-rules listNAME REGION IP_ADDRESS IP_PROTOCOL TARGET afe40deb373e111e5a00442010af0013 us-central1 104.197.19.100 TCP us-central1/targetPools/afe40deb373e111e5a00442010af0013
這是如果我們在瀏覽器中鍵入IP和端口的話,就可以看到Jenkins web界面在運行了。
DNS和服務發現
服務的另外一個特性就是可為通過kubernetes運行的一連串容器設置很多環境變量,從而提供連接到服務容器的能力,類似於Docker容器linked的做法。這樣可以用於從任何的Jenkins從服務發現主的Jenkins服務。
JENKINS_SERVICE_HOST=10.175.245.100JENKINS_PORT=tcp://10.175.245.100:80JENKINS_PORT_80_TCP_ADDR=10.175.245.100JENKINS_SERVICE_PORT=80JENKINS_SERVICE_PORT_SLAVE=50000JENKINS_PORT_50000_TCP_PORT=50000JENKINS_PORT_50000_TCP_ADDR=10.175.245.100JENKINS_SERVICE_PORT_HTTP=80
Kubernetes還支持可選的額外組件SkyDNS,SkyDNS是構建在etcd之上的公告和服務發現的一個分布式服務,可以為服務創建A記錄和SRV紀錄。A記錄的形式是my-svc.my-namespace.svc.cluster.local
,SRV是records _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local
。容器均被配置為使用SkyDNS來解析服務名稱為服務的IP,/etc/resolv/conf
被配置為搜索多個後綴,從而讓名稱解析更加的容易:
nameserver 10.175.240.10nameserver 169.254.169.254nameserver 10.240.0.1search default.svc.cluster.local svc.cluster.local cluster.local c.prefab-backbone-109611.internal. 771200841705.google.internal. google.internal. options ndots:5
舉個例子,對於我們剛剛創建的jenkins服務來說,下面的名稱會被解析:
`jenkins, jenkins.default.svc.cluster.local, A record pointing to 10.175.245.100
_http._tcp.jenkins, _http._tcp.jenkins.default.svc.cluster.local, SRV with value 10 100 80 jenkins.default.svc.cluster.local
_slave._tcp.jenkins, _slave._tcp.jenkins.default.svc.cluster.local, SRV with value 10 100 50000 jenkins.default.svc.cluster.local
Jenkins從服務復制控制器
Jenkins從服務可以以復制控制器來運行從而確保一直會有從服務來運行Jenkins的任務。註意,此步驟可以加深對Kubernetes是如何工作的有所幫助,在這個特殊的Jenkins實例中,使用了Jenkinks Kubernnetes插件是一個更好的方案,此插件可以自動的根據隊列中任務的數量來動態的創建從服務,且可隔離任務的執行。
在jenkins-slaves.yml
中所定義的:
``` --- apiVersion: "v1" kind: "ReplicationController" metadata: name: "jenkins-slave" labels: name: "jenkins-slave" spec: replicas: 1 template: metadata: name: "jenkins-slave" labels: name: "jenkins-slave" spec: containers: - name: "jenkins-slave" image: "csanchez/jenkins-swarm-slave:2.0-net-tools" command: - "/usr/local/bin/jenkins-slave.sh" - "-master" - "http://jenkins:$(JENKINS_SERVICE_PORT_HTTP)" - "-tunnel" - "jenkins:$(JENKINS_SERVICE_PORT_SLAVE)" - "-username" - "jenkins" - "-password" - "jenkins" - "-executors" - "1" livenessProbe: exec: command: - sh - -c - "netstat -tan | grep ESTABLISHED" initialDelaySeconds: 60 timeoutSeconds: 1
在此例中,我們打算讓Jenkis從服務自動的連接到Jenkins主服務,替代了Jenkins所依賴的多播發現。為此我們需要指向swarm插件到在Kubernetes中運行的主機,同時指定http端口(使用-master
)和從服務端口(使用-tunnel
)。
我們使用了來自Jenkins服務的定義的一系列值的組合,由SkyDNS所提供的Jenkins主機名,以及Kubernetes註入的環境變量JENKINS_SERVICE_PORT_HTTP
和JENKINS_SERVICE_PORT_SLAVE
,在服務名稱之後命名,在服務中定義了每個端口(http和從服務)。我們還使用了環境變量JENKINS_SERVICE_HOST
替代了jenkins
主機名。被覆蓋的鏡像命令以此方式來配置容器,重復利用現有的鏡像對於服務發現有好處。它也可以在pod的定義下完成。關於屬性livenessProbe
所覆蓋的內容在下面的自我修復章節中會提及。
使用kubectl
來創建復制:
$ kubectl create -f jenkins-slaves.yml
Pod的列表會展示出新創建的,具體啟動的數量要看在復制控制器中所定義的數量,我們的例子是一個:
$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-gs7h9 1/1 Running 0 2m jenkins-slave-svlwi 0/1 Running 0 4s
第一次運行jenkins-swarm-slave
鏡像到節點會從Docker的倉庫上下載它,然後稍等一會,從服務就會自動連接到Jenkins服務。kubectl logs
可以用於調試任何的啟動問題。
復制控制器可以自動的擴展到想要的數量:
$ kubectl scale replicationcontrollers --replicas=2 jenkins-slave
再次列出pod就可以看到新增加的復制了:
$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-gs7h9 1/1 Running 0 4m jenkins-slave-svlwi 1/1 Running 0 2m jenkins-slave-z3qp8 0/1 Running 0 4s
回滾更新
Kubernetes還支持回滾更新的概念,在某一刻只有一個pod可更新。基於新的jenkins-slaves-v2.yml
配置文件定義了新的復制控制器配置,運行下面的命令,此命令是讓Kubernetes每10秒更新一次pod。
$ kubectl rolling-update jenkins-slave --update-period=10s -f jenkins-slaves-v2.yml Creating jenkins-slave-v2At beginning of loop: jenkins-slave replicas: 1, jenkins-slave-v2 replicas: 1Updating jenkins-slave replicas: 1, jenkins-slave-v2 replicas: 1At end of loop: jenkins-slave replicas: 1, jenkins-slave-v2 replicas: 1At beginning of loop: jenkins-slave replicas: 0, jenkins-slave-v2 replicas: 2Updating jenkins-slave replicas: 0, jenkins-slave-v2 replicas: 2At end of loop: jenkins-slave replicas: 0, jenkins-slave-v2 replicas: 2Update succeeded. Deleting jenkins-slavejenkins-slave-v2$ kubectl get replicationcontrollers CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS jenkins jenkins csanchez/jenkins-swarm:1.625.1-for-volumes name=jenkins 1jenkins-slave-v2 jenkins-slave csanchez/jenkins-swarm-slave:2.0 name=jenkins-slave-v2 2$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-gs7h9 1/1 Running 0 36m jenkins-slave-v2-7jiyn 1/1 Running 0 52s jenkins-slave-v2-9gip7 1/1 Running 0 28s
在此例中,真正並沒有發生什麽,除了復制控制器的名稱,但是其它的任何參數都是可以更改的(docker鏡像、環境等等)。
--- apiVersion: "v1" kind: "ReplicationController" metadata: name: "jenkins-slave-v2" labels: name: "jenkins-slave" spec: replicas: 2 template: metadata: name: "jenkins-slave" labels: name: "jenkins-slave-v2" spec: containers: - name: "jenkins-slave" image: "csanchez/jenkins-swarm-slave:2.0-net-tools" command: - "/usr/local/bin/jenkins-slave.sh" - "-master" - "http://$(JENKINS_SERVICE_HOST):$(JENKINS_SERVICE_PORT_HTTP)" - "-tunnel" - "$(JENKINS_SERVICE_HOST):$(JENKINS_SERVICE_PORT_SLAVE)" - "-username" - "jenkins" - "-password" - "jenkins" - "-executors" - "1" livenessProbe: exec: command: - sh - -c - "netstat -tan | grep ESTABLISHED" initialDelaySeconds: 60 timeoutSeconds: 1
自我修復
使用Kubernetes的一個好處就是自動管理和恢復容器。如果說運行Jenkins服務的容器宕掉了,無論任何的原因,對於實例來講就是一個運行的進程崩潰了,Kubernetes會接到通知,然後在幾秒鐘之後就會再重新創建一個容器。正如我們再前面所提到的,使用復制控制器可以確保即使是節點宕掉了,所定義的復制數量依然會照舊運行。Kubernetes會采取必要的手段來確保在集群中新啟動的pod是在不同的節點上的。
應用程序指定的健康檢查可以使用livenessProbe來創建,允許作HTTP檢查以及在pod中的每一個容器中執行命令。如果失敗的話,容器就會被重啟。
舉例說明,要監控Jenkins是能夠服務於HTTP請求的,將下列代碼添加到Jenkins主容器的定義中:
livenessProbe: httpGet: path: / port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5
我們也可以監控我們的從服務是真的連接到了主服務上了。之所以有必要是因為如果Jenkins主服務重啟到話,會丟失原來所連接到從服務的,而從服務會持續的嘗試連接到原來的主服務,但是會失敗,那麽我們就需要檢查從服務連接到主服務的情況。
livenessProbe: exec: command: - sh - -c - "netstat -tan | grep ESTABLISHED" initialDelaySeconds: 60 timeoutSeconds: 1
Vagrant
我們將Jenkins主服務的pod終止,Kubernetes會在創建一個。
$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-7yuij 1/1 Running 0 8m jenkins-slave-7dtw1 0/1 Running 0 6s jenkins-slave-o3kt2 0/1 Running 0 6s $ kubectl delete pods/jenkins-7yuij pods/jenkins-7yuij $ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-jqz7z 1/1 Running 0 32s jenkins-slave-7dtw1 1/1 Running 0 2m jenkins-slave-o3kt2 1/1 Running 1 2m
Jenkins Web 用戶界面仍然可以由原來的一些地址訪問的到。Kubernetes服務會掌管轉發連接到新的pod。運行Jenkins數據目錄在卷中,我們保證了即使是容器宕掉仍然能夠保持數據,所以我們沒有丟失Jenkins任何的任務或已經創建的數據。而且因為Kubernetes代理了每個節點的服務,從服務會自動的重新連接到主服務,還毋需關心它運行在哪裏!而且即使是如果從服務的容器宕掉的情況亦發生了,系統會自動創建一個新的容器,而且歸功於服務發現的能力,可讓其自動的加入的從服務池。
GKE
一旦發生了節點宕掉這種事情,Kubernetes會自動的重新調度pod到不同的地點。我們使用的是手動的將運行Jenkins主服務的節點關閉。
$ kubectl describe pods/jenkins-gs7h9 | grep Node Node: gke-kubernetes-jenkins-e46fdaa5-node-chiv/10.240.0.3$ gcloud compute instances listgcloud compute instances listNAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS gke-kubernetes-jenkins-e46fdaa5-node-5gvr us-central1-f n1-standard-1 10.240.0.4 104.154.35.119 RUNNING gke-kubernetes-jenkins-e46fdaa5-node-chiv us-central1-f n1-standard-1 10.240.0.3 104.154.82.184 RUNNING gke-kubernetes-jenkins-e46fdaa5-node-mb7s us-central1-f n1-standard-1 10.240.0.2 146.148.81.44 RUNNING $ gcloud compute instances delete gke-kubernetes-jenkins-e46fdaa5-node-chivThe following instances will be deleted. Attached disks configured tobe auto-deleted will be deleted unless they are attached to any other instances. Deleting a disk is irreversible and any data on the disk will be lost. - [gke-kubernetes-jenkins-e46fdaa5-node-chiv] in [us-central1-f]Do you want to continue (Y/n)? Y Deleted [https://www.googleapis.com/compute/v1/projects/prefab-backbone-109611/zones/us-central1-f/instances/gke-kubernetes-jenkins-e46fdaa5-node-chiv].$ gcloud compute instances listNAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS gke-kubernetes-jenkins-e46fdaa5-node-5gvr us-central1-f n1-standard-1 10.240.0.4 104.154.35.119 RUNNING gke-kubernetes-jenkins-e46fdaa5-node-mb7s us-central1-f n1-standard-1 10.240.0.2 146.148.81.44 RUNNING
過一會,Kubernetes知道了運行pod的那個節點宕掉了,它就會在另外一臺節點創建一個新的叫做jenkins-44r3y
的pod。
$ kubectl get pods NAME READY STATUS RESTARTS AGE jenkins-44r3y 1/1 Running 0 21s jenkins-slave-v2-2z71c 1/1 Running 0 21s jenkins-slave-v2-7jiyn 1/1 Running 0 5m $ kubectl describe pods/jenkins-44r3y | grep Node Node: gke-kubernetes-jenkins-e46fdaa5-node-5gvr/10.240.0.4
接著打開瀏覽器,使用原來的IP來訪問,看到的仍然是一樣的Jenkins主服務的用戶界面,因為它會自動的轉發到運行Jenkins主服務的節點。你所創建的任何jenkins任務都會保留,因為數據存放在Google持久磁盤中,而持久磁盤Kubernetes會自動的掛載到新的容器實例。
註意,稍後一段時間,GKE會意識到其中一個節點宕掉了,就會再啟動一個實例,保持我們集群的規模。
$ gcloud compute instances listNAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS gke-kubernetes-jenkins-e46fdaa5-node-5gvr us-central1-f n1-standard-1 10.240.0.4 104.154.35.119 RUNNING gke-kubernetes-jenkins-e46fdaa5-node-chiv us-central1-f n1-standard-1 10.240.0.3 104.154.82.184 RUNNING gke-kubernetes-jenkins-e46fdaa5-node-mb7s us-central1-f n1-standard-1 10.240.0.2 146.148.81.44 RUNNING
刪除
kubectl
提供了刪除復制控制器、pod、和服務定義的命令。
要停止復制控制器,設置復制數為0,這樣就會讓所有相關聯的pod終止。
$ kubectl stop rc/jenkins-slave-v2$ kubectl stop rc/jenkins
刪除服務:
$ kubectl delete services/jenkins
總結
Kubernetes可以跨多個服務器來管理Docker的部署,簡化了長期運行的執行任務,以及分布式的Docker容器。通過抽象基礎設施概念和基於狀態而不是進程工作原理,它提供了集群的簡單定義,包括了很多可用性方面的企業級特性,諸如自我修復能力、集中的日誌、服務發現、動態DNS、網絡隔離、以及資源配額。簡而言之,Kubernetes讓管理Docker集群更加點容易。
本文出自 “一盞燭光” 博客,謝絕轉載!
使用Kubernetes V1來管理Docker的擴展