[CoreOS 轉載] CoreOS實踐指南(七):Docker容器管理服務

轉載:http://www.csdn.net/article/2015-02-11/2823925node

 

摘要:當Docker還名不見經傳的時候,CoreOS創始人Alex就預見了這個項目的價值,並將其作爲CoreOS支持的第一套應用程序隔離方案。本文將主要介紹在具體的場景下,如何在CoreOS中恰當地管理Docker容器。linux

注:本文首發於CSDN,轉載請標明出處。git

【編者按】在「漫步雲端:CoreOS實踐指南」系列的前幾篇文章中,ThoughtWorks的軟件工程師林帆主要介紹了CoreOS及其相關組件和使用。說到CoreOS,不得不提Docker。當Docker還名不見經傳的時候,CoreOS創始人Alex就憑着敏銳直覺,預見了這個項目的價值,將Docker作爲了這個系統支持的第一套應用程序隔離方案。本文將主要介紹在具體的場景下,如何在CoreOS中恰當的管理Docker容器。github


 

做者簡介:docker

林帆,生在80後尾巴的IT攻城獅,ThoughtWorks成都辦公室CloudOps小組成員,平時喜歡在業餘時間研究DevOps相關的應用,目前在備考AWS認證和推廣Docker相關技術。數據庫


 

此次的主角終於輪到了大鯨魚Docker。不曉得有多少人是由於Docker認識了CoreOS的,至少它在社區的知名度事實上高於CoreOS項目自己。這篇文章裏不會對Docker作很深刻的講解,而重點放在開始使用Docker所需的基本知識以及在CoreOS中使用Docker託管服務的推薦實踐方法。    安全

    

 

結緣

 

雷教主說,「站在風口上,豬也能飛起來」。Docker正是藉着雲計算的風飛上了天。伴隨着Docker和應用容器的興起,拉動了一批PaaS產品的發展,而CoreOS也借了這股勁兒賺足了人氣,進行得風生水起。同時CoreOS的成熟也在回饋Docker社區,爲社區帶來了例如Etcd、Deis(私有PaaS雲平臺,目前是基於CoreOS構建的)等許多新的活力。bash

提及CoreOS與Docker的淵源,確有一段歷史了。故事大體是這樣開始的,2013年2月,美國的dotCloud公司發佈了一款新型的Linux容器軟件Docker,並創建了一個網站發佈它的首個演示版本(     見Docker第一篇官方博客)。而幾乎同時,2013年3月,美國加州,年輕的帥小夥Alex Polvi正在本身的車庫開始他的         第二次創業。此前,他的首個創業公司Cloudkick賣給了雲計算巨頭Rackspcace(就是OpenStack的東家)。服務器

有了第一桶金的Alex此次準備幹一票大的,他計劃開發一個足以顛覆傳統的服務器系統的Linux發行版。爲了提供可以從任意操做系統版本穩定無縫地升級到最新版系統的能力,Alex急需解決應用程序與操做系統之間的耦合問題。所以,當時還名不見經傳的Docker容器引發了他的注意,憑着敏銳直覺,Alex預見了這個項目的價值,當仁不讓地將Docker作爲了這個系統支持的第一套應用程序隔離方案。不久之後,他們成立了以本身的系統發行版命名的組織:CoreOS。事實證實,採用Docker這個決定,後來很大程度上成就了CoreOS的生態系統。網絡

如今看來,CoreOS已經不是惟一預裝了Docker的操做系統了,但它是第一個,也是目前作得最成功的一個。RedHat和Canonical(Ubuntu的母公司)隨其後也分別推出了本身的預裝Docker的系統發行版,但知悉者寥寥,並無作成氣候。其項目發起時間見下圖(出自成都ThoughtWorks技術雷達分享活動),Atomic和Ubuntu Core Snappy分別是RedHat和Canonical公司推出的預裝Docker的操做系統,目標也都是直指服務器集羣和容器化部署。

應用容器

 

「應用容器」如今對許多人已經並不陌生了。但它在服務器的系統上還不是那麼普及,至少與你手上的智能手機系統相比。至今在服務器系統上流行的安裝軟件方式依然是編譯源代碼、手工的安裝包或各類包管理工具,雖然包管理工具的出現解決了應用軟件安裝、卸載以及自身依賴等諸多問題,卻沒法很好的解決軟件之間的依賴衝突。而早在Docker誕生之前,「沙盒」的概念已經被廣泛使用在Android、iOS等主流的手機系統中了。經過沙盒的隔離,應用軟件將本身全部的依賴與應用自己打包在一塊兒,並經過SDK API提供的可控的方式訪問操做系統,軟件與系統的耦合度大大下降。這樣帶來的直接好處是,軟件之間的依賴衝突獲得了很好的解決,移除一個應用軟件通常只須要很短的幾秒鐘而且完全無痕,軟件訪問系統的安全性也更加可控。

事實上,Android實現沙盒一樣的基於Linux內核的cgroup和namespace機制用於限制和隔離資源的使用,所使用的技術與Docker一模一樣。這些早在Linux 2.6.x版本就已經加入了的新特性,已經經過了較長時間的檢驗,被證明是可行而且可靠的。

 

當CoreOS 碰見 Docker

這篇文章裏不會專門介紹Docker的使用,而是關注在具體的場景下,如何在CoreOS中恰當的管理Docker容器。瞭解過Docker在CoreOS生態系統中的角色後,下面經過在兩個容器中分別運行NodeJS和MongoDB的例子說明如何在CoreOS中經過Systemd管理服務,並在此基礎上快速瀏覽一些基本的Docker命令。

 

  • 製做服務的Docker鏡像

服務鏡像有一些能夠是現成的標準服務的鏡像,例如MongoDB服務。另外一些則須要通過用戶定製,製做Docker鏡像文件通常能夠經過Dockerfile或現有容器實例生成兩種方法。前者是比較推薦的作法,但需學習Dockerfile的寫法,已經超出了這個系列的範圍。後者相對簡單,但不利於後期的鏡像維護管理,這裏僅僅做爲演示目的,所以採用這種方法。

 

1、拉取基礎鏡像

每個具體的容器其實是運行在虛擬出來的獨立空間裏面的,它被設計成只可以訪問到存在於同一個虛擬空間下面的其餘文件。所以爲了使應用可以使用基本的運行時依賴,還須要將一些Linux的命令和配置文件也打包放到虛擬空間裏,這種打包好的依賴文件集合就是鏡像。

操做 docker 的方式與 systemctl、etcdctl 相似,須要由一個二級命令共同組成一個完整的命令。經過 docker pull 命令能夠指定的網絡地址拉取鏡像到本地(若是指定的是名稱而不是網絡地址,則會在docker官方的鏡像倉庫裏面搜索,好比下面的兩個例子)。

 

 

$ docker pull node:latest ... Status: Downloaded newer image for node:latest $ docker pull mongo:latest ... Status: Downloaded newer image for mongo:latest

鏡像是按照「地址/鏡像名:版本標籤」格式命名的,其中鏡像名是必須的,若是地址部分爲空則默認爲官方倉庫地址。若是版本標籤部分爲空,對於較新的Docker版本(大約1.3.x之後),會僅僅下載標籤爲latest的版本,而較早版本的Docker則會下載指定鏡像的全部版本,經常會所以意外下載許多不須要的鏡像版本。

在一大段輸出之後,若一切順利(事實是,在國內可能不會太順利),本地的Docker已經能夠直接使用這兩個預裝了NodeJS和MongoDB的鏡像了。能夠經過 docker images 命令驗證。

$ docker images REPOSITORY  TAG  IMAGE ID  CREATED  VIRTUAL  SIZE node  latest  61afc26cd88e  3 days ago  696.2 MB mongo   latest  59b3d123f9b8  6 days ago  392.4 MB ...

在國內的一些地區,拉取官方鏡像倉庫的鏡像可能會失敗(或許是大名鼎鼎的某防火牆的功勞)。此時能夠採用國內的第三方開源鏡像倉庫,好比DockerPoolDocker.cn提供的鏡像文件。前者須要配置本地的SSL證書,不然會遇到「Error: Invalid registry endpoint」錯誤,略微麻煩。後者能夠直接使用:

docker pull docker.cn/docker/node:latest docker pull docker.cn/docker/mongo:latest

2、製做定製鏡像

MongoDB能夠直接使用官方的Docker鏡像。而NodeJs的容器還須要些許定製,將應由部署到容器中而後生成新的鏡像。再次說明,製做鏡像的最佳途徑是寫一個Dockerfile,實現基礎設施可視化。如下經過修改現有鏡像的方法通常只用於演示目的。

接下來咱們要分別啓動MongoDB和NodeJs的容器實例,並將MongoDB的端口暴露到NodeJs的容器中。

首先啓動一個MongoDB容器實例,命名爲mongo-ins。啓動容器的命令是 docker run,除了運行配置參數如  --name、--port 等,這個命令的最後兩個參數分別是實例使用的鏡像名字,和實例自己須要運行的命令。有的容器已經配置好了默認的運行程序,此時後面的一個參數能夠省略,好比下面的例子。

參數 -d 表示運行後直接進入後臺,屏幕上回顯的一串輸出是新啓動容器實例的ID。

而後啓動一個NodeJs容器實例,使用官方的node鏡像做爲基礎鏡像,並將它與 mongo-ins 實例創建「鏈接」。這個容器實例命名爲node-app。

$ docker run --name node-app -p 3000 --link mongo-ins:mongo -it node /bin/bash root@e73e7d7836a6:/#  <— 已經進入容器中的Bash>

-it 其實是 -i -t 的簡便寫法,表示啓用交互式模式和啓用顯示終端,這樣咱們能夠進入容器中作一些手工操做。而參數 --link 用來將兩個容器進行關聯,關於Docker Link的用法能夠參考Docker的相關文檔。簡單來講,Link的參數 mongo-ins:mongo 表示將容器 mongo-ins 引入到正在創建的容器鏡像中,並將其稱爲 mongo。這樣作的結果是,在新建的 node-app 容器實例中,可以訪問到兩個全局環境變量: $MONGO_PORT_27017_TCP_ADDR 和 $MONGO_PORT_27017_TCP_PORT,分別是用來訪問 MongoDB 的 IP 地址和端口。

做爲演示,咱們將在容器中部署一個從Github獲取的簡單示例。

$ git clone https://github.com/ijason/NodeJS-Sample-App.git $ cd /NodeJS-Sample-App/EmployeeDB $ sed -i -e "s/27017/process.env.MONGO_PORT_27017_TCP_PORT/" -e "s/'localhost'/process.env.MONGO_PORT_27017_TCP_ADDR/" app.js $ exit

上面的第三條命令將本來容器中指定的 MongoDB 位置改爲了從另外一個容器中暴露的IP地址和端口。至此這個node-app容器已經部署好了一個名爲 Employees 的示例應用,接下來將它生成鏡像並放到集羣的每一個節點上。

3、生成並提交鏡像

爲了在集羣裏對容器中的服務提供橫向擴展能力,須要將定製好的容器在集羣的全部節點共享。

首先須要一個存放共享鏡像的地方,在企業環境可使用私有的鏡像倉庫,但爲了演示簡便起見,咱們直接使用Docker的公共倉庫。首先須要在Docker Hub註冊一個用戶,而後使用 docker login 命令登錄到倉庫服務器。

$ docker login Username: linfan Password: Email: linfan@******.com Login Succeeded

而後咱們須要將本地修改過的容器使用 docker commit 命令生成一個本地的鏡像。注意,因爲以後須要將鏡像提交至Docker Hub,這裏鏡像的名字必須以本身的Docker Hub用戶名做爲前綴,不然在後面的 push 時候會遇到 403 「Access Denied: Not allowed to create Repo at given location」 錯誤。例如名爲 linfan/employees。

$ docker commit node-app linfan/employees a4281aa8baf9aee1173509b30b26b17fd1bb2de62d4d90fa31b86779dd15109b $ docker images REPOSITORY  TAG  IMAGE ID  CREATED  VIRTUAL SIZE linfan/employees  latest  a4281aa8baf9  14 seconds ago  696.2 MB

最後,使用 docker push 命令將這個準備好的鏡像提交到Docker Hub倉庫中。

$ docker push linfan/employees The push refers to a repository [linfan/employees] (len: 1) Sending image list ... Pushing tag for rev [5577d6743652] on {https://cdn-registry-1.docker.io/v1/repositories/linfan/employees/tags/latest}

提交完成後,在其餘節點就可使用 docker pull 命令獲取到這個鏡像了。

注意:嚴格來講,將數據庫服務容器經過Docker Link暴露給應用服務容器的方法並不符合分佈式應用的12條準則,由於經過Docker Link鏈接的兩個容器必須運行在同一個物理主機上,數據與應用不能在集羣中分別獨立的部署或橫向擴展。

  • 使用 Fleet 啓動服務容器

1、編寫 Unit 文件

有了相應的服務容器後,在CoreOS中正確啓動服務的方法應該是經過Fleet來管理。經過合理使用 Unit 的 X-Fleet 配置,可以很好的解決容器直接相互依賴的問題。

用 vagrant ssh 進入一個 CoreOS 的 Shell 中,建立如下兩個服務 Unit 文件。

首先是mongo.service

[Unit] Description=General MongoDB Service After=docker.service [Service] TimeoutStartSec=0 ExecStart=/opt/bin/docker-run.sh --name mongo-ins -d mongo ExecStop=/usr/bin/docker stop mongo-ins

而後是employees.service,請注意它的 Unit 和 X-Fleet 段的內容。在Unit段指定了這個服務啓動前必須首先啓動 mongo.service 服務,而在 X-Fleet 段指定了本身須要運行在與 mongo.service 相同的服務節點上。

[Unit] Description=Employee Information Management Service After=docker.service After=mongo.service [Service] TimeoutStartSec=0 ExecStart=/opt/bin/docker-run.sh -p 3000:3000 --link mongo-ins:mongo -d --name node-app node-app node /NodeJS-Sample-App/EmployeeDB/app.js ExecStop=/usr/bin/docker stop mongo-ins [X-Fleet] X-ConditionMachineOf=mongo.service

上面的兩個 Unit 文件都使用到了一個 /opt/bin/docker-run.sh 腳本,用於替代 docker run 命令。這個腳本須要額外建立並放置到 /opt/bin 目錄下面,其做用是檢測是否已經有一個同名的容器在運行了,若是沒有則執行相應的 docker run 命令,不然直接使用 docker start 命令啓動已經存在的容器。其內容以下:

#!/bin/bash PARA="${*}" NAME=$(echo "${PARA}" | grep '\-\-name' | sed 's/.*--name \([^ ]*\).*/\1/g') if [ "${NAME}" == "" ]; then   echo "[ERROR] Must specify a name to the container!";   exit -1; fi EXIST=$(sudo docker ps -a | grep "${NAME}[ ]*$") if [ "${EXIST}" == "" ]; then    sudo docker run ${PARA} else   sudo docker start ${NAME} fi

2、啓動服務

經過 fleetctl 命令啓動服務,具體的用法在系列前面的內容裏面已經介紹過了。

fleetctl start ./mongo.service fleetctl start ./employees.service

這裏爲了簡便直接用了 fleetctl start 命令,更推薦的啓動服務方法請參考系列中關於Fleet的一篇

到這一步,這個部署在容器中的服務已經可使用了。從外部訪問服務器的 3000 端口便可打開下面這個頁面,並向MongoDB服務中的數據庫中添加員工信息了。

 

  • 管理容器運行狀態

最後,再來看一些用於檢測容器運行狀態和平常管理的Docker命令。

1、查看運行日誌

容器經過-d參數進入後臺運行以後,其中服務輸出的日誌內容能夠經過 docker logs 命令查看到。

$ docker logs mongo-ins MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=d9bba1bfc8be ...

 

2、容器實例列表

命令 docker ps 可以列出全部當前正在運行的容器的基本信息。

$ docker ps CONTAINER ID  IMAGE  COMMAND  CREATED   STATUS  PORTS  NAMES d9bba1bfc8be  mongo:2  "/entrypoint.sh"  4 minutes ago  Up 4 minutes  27017/tcp  mongo-ins 22de21d77174  node:0  "/bin/bash"  3 minutes ago  Up 5 minutes  node-app ...

 

 

3、容器實例詳情

使用 docker inspect 命令可以查看到指定一個容器的詳細運行信息。

$ docker inspect mongo-ins { ... }

4、備份和還原容器

簡單的提一下,用來將現有的本地鏡像打包備份和還原的命令是 docker save 和 docker load。也能夠直接將容器實例打包,相關命令是 docker export 和 docker import,注意 import 以後會將備份的數據恢復成一個新的本地鏡像,而不是容器實例。

這兩個命令的使用能夠參考文檔。只額外說明一個問題,既然兩種還原都會將備份的內容還原爲容器,爲何須要兩種還原命令呢?緣由在於使用 save 和 export 生成的打包效果是不太同樣的,簡單說就是 export 生成的備份會丟棄全部的鏡像分層結構,而 save 生成的備份不會。鏡像分層結構有利於減小類似鏡像本地存儲所需的空間,細節可參考這篇文章

以上介紹的這些命令僅僅是Docker強大功能的冰山一角,網絡上已經有許多十分優秀的Docker使用教程,做爲學習Docker和應用容器都是極好的途徑。這裏推薦一個Dockerone翻譯的Docker系列文章

相關文章
相關標籤/搜索