使用Kubernetes來管理Docker擴展

jopen 9年前發布 | 32K 次閱讀 Docker

來自:http://www.infoq.com/cn/articles/scaling-docker-with-kubernetes

Kubernetes是一款開源的項目,管理Linux容器集群,并可將集群作為一個單一的系統來對待。其可跨多主機來管理和運行Docker容器、提供容器的定位、服務發現以及復制控制。它由Google發起,現在得到了如微軟、紅帽、IBM和Docker等眾多廠商的支持。

Google使用容器技術有著超過十年的歷史,每周要啟動超過2億臺容器。通過Kubernetes,Google分享了他們關于容器的專業經驗,即創建大規模運行容器的開放平臺。

一旦用戶開始使用Docker容器,那么問題就來了,一、如何跨多個Docker主機擴展和啟動容器,且能夠在主機間平衡這些容器。二、還需要高度抽象出API來定義如何從邏輯上組織容器、定義容器池、負載均衡以及關聯性。該項目就是為了解決這兩個問題而生的。

Kubernetes仍然處于早期階段,這也就意味著會有很多新的變化進入到該項目,目前還僅有比較簡單的實例,仍需要加入新的功能,但是它正在一步一步地平穩發展,也得到了很多大公司的支持,前途無量。

Kubernetes概念

Kubernetes的架構被定義為由一個master服務器和多個minons服務器組成。命令行工具連接到master服務器的API端點,其可以管理和編排所有的minons服務器,Docker容器接收來自master服務器的指令并運行容器。

  • Master:Kubernetes API 服務所在,多Master的配置仍在開發中。
  • Minons:每個具有Kubelet服務的Docker主機,Kubelet服務用于接收來自Master的指令,且管理運行容器的主機。
  • Pod:定義了一組綁在一起的容器,可以部署在同一Minons中,例如一個數據庫或者是web服務器。
  • Replication controller:定義了需要運行多少個Pod或者容器。跨多個minons來調度容器。
  • Service:定義了由容器所發布的可被發現的服務/端口,以及外部代理通信。服務會映射端口到外部可訪問的端口,而所映射的端口是跨多個minons的Pod內運行的容器的端口。
  • kubecfg:命令行客戶端,連接到master來管理Kubernetes。

(點擊圖片可放大顯示)

使用Kubernetes來管理Docker擴展

Kubernetes由狀態所定義,而不是進程。當你定義了一個pod時,Kubernetes會設法確保它會一直運行。如果其中的某個容器掛掉 了,Kubernetes會設法再啟動一個新的容器。如果一個復制控制器定義了3份復制,Kubernetes會設法一直運行這3份,根據需要來啟動和停 止容器。

本文中所用的例子應用是Jenkins持續集成服務,Jenkins是典型的通過主從服務來建立分布式的工作任務的例子。Jenkins由jenkins swarm插件來配置,運行一個jenkins主服務和多個jenkins從服務,所有的Jenkins服務都以Docker容器的方式跨多個主機運行。swarm從服務在啟動時連接到Jenkins主服務,然后就可以運行Jenkins任務了。例子中使用的配置文件可以從Github上下載到,而該Docker鏡像可以從casnchez/jenkins-swarm獲取,對于Jenkins主服務來說,可以通過swarm插件來擴展官方Jenkins鏡像。對于jenkins從服務來說,可以從csanchez/jenkins-swarm-slave獲取,它只是在JVM容器中運行了jenkins從服務而已。

創建Kubernetes集群

Kubernetes提供了多個操作系統和云/虛擬化提供商下創建集群的腳本,有Vagrant(用于本地測試)、Google Compute Engine、Azure、Rackspace等。

本文所實踐的例子就是運行在Vagrant之上的本地集群,使用Fedora作為操作系統,遵照的是官方入門指南,測試的Kubernetes版本是0.5.4。取代了默認的3個minion(Docker主機),而是使用了2個minion,兩臺主機就足以展示Kubernetes的能力了,三臺有點浪費。

當你下載了Kubernetes,然后將至解壓后,即可在本地的目錄下運行這些示例了。初學者創建集群僅需要一個命令,即./cluster/kube-up.sh。

$ export KUBERNETES_PROVIDER=vagrant
$ export KUBERNETES_NUM_MINIONS=2
$ ./cluster/kube-up.sh

獲取示例配置文件:

$ git clone https://github.com/carlossg/kubernetes-jenkins.git

集群創建的快慢取決于機器的性能和內部的帶寬。但是其最終完成不能有任何的錯誤,而且它僅需要運行一次。

命令行工具

和Kubernetes交互的命令行工具叫做kubecfg,此腳本位于cluster/kubecfg.sh。

為了檢驗我們剛才創建的兩個minons已經啟動并運行了,只需運行kubecfg list minions命令即可,它的結果會顯示Vagrant的兩臺虛擬機。

$ ./cluster/kubecfg.sh list minions

Minion identifier
----------
10.245.2.2
10.245.2.3

Pod

在Kubernetes的術語中,Jenkins主服務被定義為一個pod。在一個pod中可以指定多個容器,這些容器均部署在同一Docker主機中,在一個pod中的容器的優勢在于可以共享資源,例如存儲,而且使用相同的網絡命名空間和IP地址。默認的卷是空的目錄,類型為emptyDir,它的生存時間就是pod的生命周期,而并非是指定的容器,所以如果一個容器失效了,但是其持久性的存儲仍然在。另外一個卷的類型是hostDir,它是將主機的一個目錄掛載到容器中。

在這個具體的Jenkins示例中,我們所創建的pod是兩個容器,分別是Jenkins主服務和MySQL,前者作為實例,后者作為數據庫。然而我們只需要關心jenkins主服務容器即可。

為了創建一個Jenkins pod,我們運行定義了Jenkins容器pod的kubecfg,使用Docker鏡像csanchez/jenkins-swarm,為了能夠訪問 Jenkins的Web界面和從服務的API,我們將主機的端口8080和50000映射到容器,以及將/var/jenkins_home掛載為卷。讀 者可從Github下載到示例代碼。

Jenkins Web 界面的pod(pod.json)的定義如下:

{
  "id": "jenkins",
  "kind": "Pod",
  "apiVersion": "v1beta1",
  "desiredState": {
    "manifest": {
      "version": "v1beta1",
      "id": "jenkins",
      "containers": [
        {
          "name": "jenkins",
          "image": "csanchez/jenkins-swarm:1.565.3.3",
          "ports": [
            {
              "containerPort": 8080,
              "hostPort": 8080
            },
            {
              "containerPort": 50000,
              "hostPort": 50000
            }
          ],
          "volumeMounts": [
            {
              "name": "jenkins-data",
              "mountPath": "/var/jenkins_home"
            }
          ]
        }
      ],
      "volumes": [
        {
          "name": "jenkins-data",
          "source": {
            "emptyDir": {}
          }
        }
      ]
    }
  },
  "labels": {
    "name": "jenkins"
  }
}

然后使用下面命令來創建:

$ ./cluster/kubecfg.sh -c kubernetes-jenkins/pod.json create pods

Name                Image(s)                           Host                Labels              Status
----------          ----------                         ----------          ----------          ----------
jenkins             csanchez/jenkins-swarm:1.565.3.3           name=jenkins        Pending

這需要等待一段時間,具體時間的長短要視你的網絡而定,因為它會從Docker Hub上下載Docker鏡像到minion,我們可以查看它的狀態,以及在那個minion中啟動了。

$ ./cluster/kubecfg.sh list pods
Name                Image(s)                           Host                    Labels              Status
----------          ----------                         ----------              ----------          ----------
jenkins             csanchez/jenkins-swarm:1.565.3.3   10.0.29.247/10.0.29.247   name=jenkins        Running

如果我們使用SSH登錄到minion中,此minion即是pod被分配到的minion-1或minion-2,我們就可以看到Docker按 照所定義的那樣啟動起來了。其中還包括了用于Kubernetes內部管理的容器(kubernetes/pause和google /cadvisor)。

$ vagrant ssh minion-2 -c "docker ps"

CONTAINER ID        IMAGE                              COMMAND                CREATED             STATUS              PORTS                                              NAMES
7f6825a80c8a        google/cadvisor:0.6.2              "/usr/bin/cadvisor"    3 minutes ago       Up 3 minutes                                                           k8s_cadvisor.b0dae998_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0.default.file_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0_28df406a
5c02249c0b3c        csanchez/jenkins-swarm:1.565.3.3   "/usr/local/bin/jenk   3 minutes ago       Up 3 minutes                                                           k8s_jenkins.f87be3b0_jenkins.default.etcd_901e8027-759b-11e4-bfd0-0800279696e1_bf8db75a
ce51fda15f55        kubernetes/pause:go                "/pause"               10 minutes ago      Up 10 minutes                                                          k8s_net.dbcb7509_0d38f5b2-759c-11e4-bfd0-0800279696e1.default.etcd_0d38fa52-759c-11e4-bfd0-0800279696e1_e4e3a40f
e6f00165d7d3        kubernetes/pause:go                "/pause"               13 minutes ago      Up 13 minutes       0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp   k8s_net.9eb4a781_jenkins.default.etcd_901e8027-759b-11e4-bfd0-0800279696e1_7bd4d24e
7129fa5dccab        kubernetes/pause:go                "/pause"               13 minutes ago      Up 13 minutes       0.0.0.0:4194->8080/tcp                             k8s_net.a0f18f6e_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0.default.file_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0_659a7a52

還有,我們一旦拿到了容器的ID,就可以通過如vagrant ssh minion-1 -c "docker logs cec3eab3f4d3"這樣的命令來查看容器的日志了。

我們也可以訪問Jenkins的Web界面,至于要訪問的URL是http://10.245.2.2:8080/還是http://10.0.29.247:8080/,要看用到了那個minion。

服務發現

Kubernetes支持定義服務,一種為容器使用發現和代理請求到合適的pod的方法。下面示例使用了service-http.json文件來 創建一個服務,其將id為jenkins指向了貼有標簽為name=jenkins的pod。而標簽是在如上面pod的定義中所聲明的。且將端口8888 重定向到容器的8080 。

{
  "id": "jenkins",
  "kind": "Service",
  "apiVersion": "v1beta1",
  "port": 8888,
  "containerPort": 8080,
  "selector": {
    "name": "jenkins"
  }
}

使用kubecfg來創建服務:

$ ./cluster/kubecfg.sh -c kubernetes-jenkins/service-http.json create services

Name                Labels              Selector            IP                  Port
----------          ----------          ----------          ----------          ----------
jenkins                                 name=jenkins        10.0.29.247         8888

每個服務都會被分配一個唯一的IP地址,且此IP地址會伴隨服務的整個生命周期。如果我們有多個pod匹配服務的定義,服務就會在所有它們之上提供負載均衡。

服務的另外一個特性是可以為由Kubernetes來運行的容器設置一些環境變量,使其可以連接到該服務容器,這和運行已鏈接的Docker容器有些類似,它可以幫助我們從若干從服務中找到Jenkins主服務。

JENKINS_PORT='tcp://10.0.29.247:8888'
JENKINS_PORT_8080_TCP='tcp://10.0.29.247:8888'
JENKINS_PORT_8080_TCP_ADDR='10.0.29.247'
JENKINS_PORT_8080_TCP_PORT='8888'
JENKINS_PORT_8080_TCP_PROTO='tcp'
JENKINS_SERVICE_PORT='8888'
SERVICE_HOST='10.0.29.247'

在此示例中,我們還需要打開端口50000,Jenkins swarm插件需要用這個端口。我們創建另外一個service-slave.json文件,這樣Kubernetes就可以重定向端口到Jenkins服務容器了。

{
  "id": "jenkins-slave",
  "kind": "Service",
  "apiVersion": "v1beta1",
  "port": 50000,
  "containerPort": 50000,
  "selector": {
    "name": "jenkins"
  }
}

再次使用kubecfg來創建:

$ ./cluster/kubecfg.sh -c kubernetes-jenkins/service-slave.json create services

Name                Labels              Selector            IP                  Port
----------          ----------          ----------          ----------          ----------
jenkins-slave                           name=jenkins        10.0.86.28          50000

列出所有可用的已經定義的服務,包括一些Kubernetes內部的服務:

$ ./cluster/kubecfg.sh list services

Name                Labels              Selector                                  IP                  Port
----------          ----------          ----------                                ----------          ----------
kubernetes-ro                           component=apiserver,provider=kubernetes   10.0.22.155         80
kubernetes                              component=apiserver,provider=kubernetes   10.0.72.49          443
jenkins                                 name=jenkins                              10.0.29.247         8888
jenkins-slave                           name=jenkins                              10.0.86.28          50000

復制控制器

復制控制器允許在多個minion中運行多個pod。Jenkins的從服務以此方式來運行,可以確保一直有一個運行Jenkins任務的從服務池。

創建replication.json,所定義的內容如下:

{
  "id": "jenkins-slave",
  "apiVersion": "v1beta1",
  "kind": "ReplicationController",
  "desiredState": {
    "replicas": 1,
    "replicaSelector": {
      "name": "jenkins-slave"
    },
    "podTemplate": {
      "desiredState": {
        "manifest": {
          "version": "v1beta1",
          "id": "jenkins-slave",
          "containers": [
            {
              "name": "jenkins-slave",
              "image": "csanchez/jenkins-swarm-slave:1.21",
              "command": [
                "sh", "-c", "/usr/local/bin/jenkins-slave.sh 
-master http://$JENKINS_SERVICE_HOST:$JENKINS_SERVICE_PORT -tunnel 
$JENKINS_SLAVE_SERVICE_HOST:$JENKINS_SLAVE_SERVICE_PORT -username 
jenkins -password jenkins -executors 1"
              ]
            }
          ]
        }
      },
      "labels": {
        "name": "jenkins-slave"
      }
    }
  },
  "labels": {
    "name": "jenkins-slave"
  }
}

podTemplate一節允許和pod的定義進行一樣的配置。在此示例中,我們打算讓Jenkins從服務自動地連接到Jenkins主服務,而 不是利用Jenkins主服務的多播發現。要實現此想法,我們需要執行jenkins-slave.sh命令,指定參數-master,從而實現在 Kubernetes中讓從服務連接到主服務。注意,上述配置文件中,我們使用了Kubernetes所提供的為Jenkins服務定義的環境變量 (JENKINS_SERVICE_HOST和JENKINS_SERVICE_PORT)。此方法中鏡像的命令覆蓋了容器的配置,為的是利用已經下載好 的鏡像。這也同樣可以在pod的定義中去實現。

使用kubecfg來創建副本:

$ ./cluster/kubecfg.sh -c kubernetes-jenkins/replication.json create replicationControllers

Name                Image(s)                            Selector             Replicas
----------          ----------                          ----------           ----------
jenkins-slave       csanchez/jenkins-swarm-slave:1.21   name=jenkins-slave   1

現在執行pod列表,可以看到新的pod已經創建,其數量是由我們剛剛所定義的復制控制器所決定的。

$ ./cluster/kubecfg.sh list pods

Name                                   Image(s)                            Host                    Labels               Status
----------                             ----------                          ----------              ----------           ----------
jenkins                                csanchez/jenkins-swarm:1.565.3.3    10.245.2.3/10.245.2.3   name=jenkins         Running
07651754-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.2/10.245.2.2   name=jenkins-slave   Pending

第一次運行的jenkins-swarm-slave鏡像是minion從Docker倉庫下載下來的,但是一段時間之后(這取決于你的互聯網連 接),Jenkins從服務會自動連接到Jenkins主服務。登錄到Jenkins從服務所在的服務器,docker ps會顯示出運行中的容器和Docker的日志,這能夠幫助你來調試容器啟動中出現的問題。

$ vagrant ssh minion-1 -c "docker ps"

CONTAINER ID        IMAGE                               COMMAND                CREATED              STATUS              PORTS                    NAMES
870665d50f68        csanchez/jenkins-swarm-slave:1.21   "/usr/local/bin/jenk   About a minute ago   Up About a minute                            k8s_jenkins-slave.74f1dda1_07651754-4f88-11e4-b01e-0800279696e1.default.etcd_11cac207-759f-11e4-bfd0-0800279696e1_9495d10e
cc44aa8743f0        kubernetes/pause:go                 "/pause"               About a minute ago   Up About a minute                            k8s_net.dbcb7509_07651754-4f88-11e4-b01e-0800279696e1.default.etcd_11cac207-759f-11e4-bfd0-0800279696e1_4bf086ee
edff0e535a84        google/cadvisor:0.6.2               "/usr/bin/cadvisor"    27 minutes ago       Up 27 minutes                                k8s_cadvisor.b0dae998_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0.default.file_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0_588941b0
b7e23a7b68d0        kubernetes/pause:go                 "/pause"               27 minutes ago       Up 27 minutes       0.0.0.0:4194->8080/tcp   k8s_net.a0f18f6e_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0.default.file_cadvisormanifes12uqn2ohido76855gdecd9roadm7l0_57a2b4de

復制控制器可以自動的調整任何想要的副本數量:

$ ./cluster/kubecfg.sh resize jenkins-slave 2

再次列出pod的話,可以看到每個副本是在哪里運行的。

$ ./cluster/kubecfg.sh list pods
Name                                   Image(s)                            Host                    Labels               Status
----------                             ----------                          ----------              ----------           ----------
07651754-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.2/10.245.2.2   name=jenkins-slave   Running
a22e0d59-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.3/10.245.2.3   name=jenkins-slave   Pending
jenkins                                csanchez/jenkins-swarm:1.565.3.3    10.245.2.3/10.245.2.3   name=jenkins         Running

使用Kubernetes來管理Docker擴展

調度

目前默認的調度是隨機的,但是基于資源的調度很快就將實現了。在寫作本文的時候,基于內存和CPU利用率的調度還有一些沒有修復的問題。還有整合Apache Mesos的調度也在緊鑼密鼓的進行中。Apache Mesos是一款分布式系統的框架,提供了資源管理的API,和跨整個數據中心或云環境的調度。

自愈

使用Kubernetes的一大好處就是其能夠自動管理和修復容器。

如果運行Jenkins服務的容器由于某些原因宕機了,比如說進程崩潰了,那么Kubernetes就會得到通知,并會在幾秒鐘之后創建一個新的容器。

$ vagrant ssh minion-2 -c 'docker kill `docker ps | grep csanchez/jenkins-swarm: | sed -e "s/ .*//"`'
51ba3687f4ee


$ ./cluster/kubecfg.sh list pods
Name                                   Image(s)                            Host                    Labels               Status
----------                             ----------                          ----------              ----------           ----------
jenkins                                csanchez/jenkins-swarm:1.565.3.3    10.245.2.3/10.245.2.3   name=jenkins         Failed
07651754-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.2/10.245.2.2   name=jenkins-slave   Running
a22e0d59-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.3/10.245.2.3   name=jenkins-slave   Running

稍后再執行list pod命令,一般不會超過1分鐘,會看到如下內容:

Name                                   Image(s)                            Host                    Labels               Status
----------                             ----------                          ----------              ----------           ----------
jenkins                                csanchez/jenkins-swarm:1.565.3.3    10.245.2.3/10.245.2.3   name=jenkins         Running
07651754-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.2/10.245.2.2   name=jenkins-slave   Running
a22e0d59-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.3/10.245.2.3   name=jenkins-slave   Running

將Jeknkis的數據目錄以卷的形式運行,我們保證了在容器宕機之后仍然能夠保留數據,所以不會丟失任何Jenkins的任務或者是已經創建好的 數據。而且因為Kubernetes在每個minion都代理了服務,Jenkins從服務會自動連接到Jenkins主服務,而根本無須關心它是在哪里 運行的!而且Jenkins從服務容器宕掉,系統也會自動創建新的容器,服務發現會將之自動加入到Jenkins服務池中。

如果發生了更加嚴重的情況,比如minion掛掉了,Kubernetes目前還沒能提供將現有容器重新調度到其它仍在運行的minion中的能力,它僅僅會顯示某個pod失效,如下所示:

$ vagrant halt minion-2
==> minion-2: Attempting graceful shutdown of VM...
$ ./cluster/kubecfg.sh list pods
Name                                   Image(s)                            Host                    Labels               Status
----------                             ----------                          ----------              ----------           ----------
jenkins                                csanchez/jenkins-swarm:1.565.3.3    10.245.2.3/10.245.2.3   name=jenkins         Failed
07651754-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.2/10.245.2.2   name=jenkins-slave   Running
a22e0d59-4f88-11e4-b01e-0800279696e1   csanchez/jenkins-swarm-slave:1.21   10.245.2.3/10.245.2.3   name=jenkins-slave   Failed

清理

kubecfg還提供了一些命令來停止和刪除復制控制器、pod、以及服務等。

要停止復制控制器,設置復制數為0,所有Jenkins從服務的容器都會被終止:

$ ./cluster/kubecfg.sh stop jenkins-slave

刪除它:

$ ./cluster/kubecfg.sh rm jenkins-slave

刪除Jenkins服務Pod,Jenkins主服務的容器會被終止:

$ ./cluster/kubecfg.sh delete pods/jenkins

刪除服務:

$ ./cluster/kubecfg.sh delete services/jenkins
$ ./cluster/kubecfg.sh delete services/jenkins-slave

總結

Kubernetes還是一個尚處于早期的項目,但是極有希望來管理Docker的跨服務器部署以及簡化執行長時間運行和分布式的Docker容 器。通過抽象基礎設施概念,定義狀態而不是進程,它提供了集群的簡單定義,包括脫離管理的自我修復能力。簡而言之,Kubernetes讓Docker的 管理更加的輕松。

關于作者

使用Kubernetes來管理Docker擴展Carlos Sanchez在 自動化、軟件開發質量、QA以及運維等方面有超過10年的經驗,范圍涉及從構建工具、持續集成到部署自動化,DevOps最佳實踐,再到持續交付。他曾經 為財富500強的公司實施過解決方案,在多家美國的創業公司工作過,最近任職的公司叫做MaestroDev,這也是他一手創建的公司。Carlos曾經 在世界各地的各種技術會議上作演講,包括JavaOne、EclipseCON、ApacheCON、JavaZone、Fosdem和 PuppetConf。他非常積極的參與開源,是Apache軟件基金會的成員,同時也是其它一些開源團體的成員,為很多個開源項目做過貢獻,比如 Apache Maven、Fog 和Puppet。

 

查看英文原文:Scaling Docker with Kubernetes

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!