日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
如何讓Ansible和Docker愉快地在一起

如何讓 Ansible 和 docker 愉快地在一起

作者:Xavier Bruhiere 2015-10-20 16:48:06

云計算 Docker 和它的容器工作流可封裝、共享和部署您的應(yīng)用程序環(huán)境。Ansible 是一個與 Docker 高度兼容的自動化工具,它使用一個強(qiáng)大的接口來管理遠(yuǎn)程服務(wù)器上的容器。在本文中,我將探索為何和如何使用 Ansible 的可擴(kuò)展設(shè)計來將 Docker 和 Ansible 的最佳功能合并在一起。

Docker 之所以如此流行,是因為它創(chuàng)造了一種采用方便的命令行接口 (CLI) 和 HTTP API 工具來封裝、運(yùn)行和維護(hù)容器的獨(dú)特方式。這種簡化降低了此技術(shù)的入門門檻,使得將應(yīng)用程序及其運(yùn)行時環(huán)境封裝到一個簡單 Dockerfile 中的獨(dú)立鏡像中變得可行。Docker 使您能夠開發(fā)更復(fù)雜的項目,但您仍需要配置這些容器。在本文中,我將展示 Ansible 如何通過更清晰的語法帶來配置管理器的特性。您將學(xué)習(xí)如何僅使用已安裝的 Python 和 Docker 構(gòu)建任何堆棧。

在介紹 Ansible 的細(xì)節(jié)之前,我首先將介紹 Ansible 的 分析 中提到的一些要點(diǎn):

  • 盡管容器導(dǎo)致一些新工作流出現(xiàn),但編排和配置工具仍然非?;钴S。
  • Ansible 和 Salt 等新參與者正在挑戰(zhàn)現(xiàn)有的工具,比如 Chef 和 Puppet。
  • 許多與 Docker 有關(guān)聯(lián)的開發(fā)人員也很關(guān)心這些工具。

更確切地講,借助 Docker,您可以在幾秒內(nèi)實(shí)現(xiàn)完全隔離的堆棧環(huán)境,或者在服務(wù)器之間復(fù)制準(zhǔn)確的設(shè)置。但是,Docker 不包含提供端到端體驗的可靠工具,無論是針對開發(fā)還是生產(chǎn)。Docker 團(tuán)隊通過新的集群工具解決了這些不斷演變的挑戰(zhàn),嘗試將 Docker 轉(zhuǎn)變?yōu)橐粋€大規(guī)模運(yùn)行容器的可靠解決方案。然而,Docker 仍然需要您手動硬編碼任務(wù)和重復(fù)常見的設(shè)置。所以,針對容器的編排和配置管理的關(guān)鍵 Docker 流程仍有待解決。在本文中,您將學(xué)習(xí)如何結(jié)合使用 Ansible 和 Docker 來幫助解決這些問題。

DevOps 的興起

在部署到生產(chǎn)中之前,現(xiàn)代應(yīng)用程序通常涉及到一個復(fù)雜的部署管道。最佳實(shí)踐建議在每次小型迭代后盡早地、頻繁地發(fā)布代碼。任務(wù)的手動執(zhí)行無法擴(kuò)展,組織已開始完善介于開發(fā)人員與系統(tǒng)管理員之間的流程,所以 DevOps 就誕生了。從那時起,敏捷團(tuán)隊就開始嘗試強(qiáng)化和自動化測試代碼,以及將其交付給用戶的方式。

通過實(shí)現(xiàn)最新的技術(shù)和方法,公司對其服務(wù)器上的代碼建立了信心。然而,隨著應(yīng)用程序在規(guī)模和復(fù)雜性上不斷增長,開發(fā)人員和系統(tǒng)管理員繼續(xù)面臨著無數(shù)挑戰(zhàn)?,F(xiàn)在比以往更需要為產(chǎn)品提供受支持的社區(qū)驅(qū)動工具。

Ansible 的可擴(kuò)展設(shè)計

在此環(huán)境中,Ansible 提供了一個有趣的框架來管理基礎(chǔ)架構(gòu)。您可以獲得服務(wù)器定義的控制權(quán),比如要安裝的包或要復(fù)制的文件,并將該配置擴(kuò)展到數(shù)千個服務(wù)器。Ansible playbook 構(gòu)成了集群的目標(biāo)狀態(tài)的一種安全表示。它的 YAML 語法和龐大的模塊列表生成了任何開發(fā)人員都能快速理解的易讀性配置文件。不同于 Chef 或 Puppet,Ansible 是無代理的,這意味著您要在遠(yuǎn)程主機(jī)上運(yùn)行命令,只需一個 SSH 連接即可。可以看到,Ansible 可輕松地處理 DevOps 復(fù)雜性。

但是,Ansible 是在容器快速興起和它們在云開發(fā)環(huán)境中帶來革命之前設(shè)計的。那么 Ansible 是否仍然有用?微型服務(wù)的范式和復(fù)雜開發(fā)環(huán)境引入了新的需求:

  • 輕量型鏡像。為了容易傳輸或節(jié)省成本,鏡像被剝離到僅剩下最低限度的依賴項。
  • 單一用,單一流程。如果應(yīng)用程序不是非常需要 SSH 守護(hù)進(jìn)程,則無需運(yùn)行它。
  • 短暫性。容器隨時可能死亡、轉(zhuǎn)移和復(fù)活。

在此上下文中,Ansible 的可擴(kuò)展架構(gòu)解決了這些問題。一個 Docker 模塊在較高層面上管理主機(jī)和容器。盡管您可能會爭論哪個編排工具(來自 Google 的 Kubernetes,還是來自 New Relic 的 Centurion)最適合此環(huán)境,但 Docker 模塊執(zhí)行效率很高,這正是我在本文中使用它的原因。但是,您還可以構(gòu)建從其官方 Ansible 鏡像啟動的容器,然后在本地模式下從內(nèi)部運(yùn)行 playbook。盡管此方法非常適合 Packer,而且肯定也適合許多使用情況,但它的缺點(diǎn)通常極為關(guān)鍵。

您被鎖定在一個基礎(chǔ)鏡像中,無法再利用特殊的秘訣或其他堆棧。

最終的工件已安裝 Ansible 和它的依賴項,它們與實(shí)際應(yīng)用程序毫無關(guān)系,這讓工件變得更笨重。

盡管 Ansible 可管理數(shù)千個服務(wù)器,但它只配備(Provision)了一個容器。

此方法將容器視為小型 VM,您可以在其中使用一個特定的解決方案。幸運(yùn)的是,Ansible 擁有模塊化設(shè)計。模塊分散在不同的存儲庫中,而且 Ansible 的大部分功能都可以通過插件進(jìn)行擴(kuò)展。

在下一節(jié)中,您將設(shè)置一個有效的環(huán)境,針對您的需求來調(diào)整 Ansible。

設(shè)置一個 Ansible 環(huán)境

假設(shè)您想要一個很容易部署的工具,它在輕量型容器中配置應(yīng)用程序環(huán)境。由于與這些容器分離,您需要一個安裝了 Ansible 的客戶端,您將使用它來向 Docker 守護(hù)進(jìn)程發(fā)送命令。此設(shè)置如 圖 1 中所示。

圖 1. 使用 Ansible 配備容器所需的組件

您在此配置中必須管理的依賴項,已通過從容器中運(yùn)行 Ansible 來最小化。此架構(gòu)將主機(jī)限制為容器與命令之間的一個通信橋梁。

可通過許多選項在您服務(wù)器上安裝 Docker:

使用 docker-machine 將它安裝在遠(yuǎn)程主機(jī)上。

安裝在本地。順便說一下,您可能不想親自管理一個嚴(yán)格基于容器的基礎(chǔ)架構(gòu);在這種情況下,可以考慮采用外部提供程序。

依賴于外部提供程序。

使用 boot2docker,這是一個在 Windows 和 Mac 上運(yùn)行 Docker 容器的輕量型 Linux 發(fā)行版。

無論選擇何種解決方案,請確保部署了 Docker 1.3 版或更高版本(1.3 版引入了進(jìn)程注入)。您還需要運(yùn)行一個 SSH 服務(wù)器來安全地處理 Ansible 命令。

清單 1 中的命令使用公鑰設(shè)置了一種方便可靠的身份驗證方法。

清單 1. 使用公鑰設(shè)置身份驗證的命令

  
 
 
 
  1. # install dependencies 
  2. sudo apt-get install -y openssh-server libssl-dev 
  3. # generate private and public keys 
  4. ssh-keygen -t rsa -f ansible_id_rsa 
  5. # allow future client with this public key to connect to this server 
  6. cat ansible_id_rsa.pub >> ~/.ssh/authorized_keys 
  7. # setup proper permissions 
  8. chmod 0700  ~/.ssh/ 
  9. chmod 0600  ~/.ssh/authorized_keys 
  10. # make sure the daemon is running 
  11. sudo service ssh restart 

配置 SSH 和安全性問題不屬于本文的討論范圍。細(xì)心的讀者可查閱 /etc/ssh/sshd_config 文件,進(jìn)一步了解配置 SSH 的可用選項。

下一步是將公鑰加載到運(yùn)行 Ansible 的客戶端容器上并配備構(gòu)建器容器。使用一個 Dockerfile 來配備構(gòu)建器。參見 清單 2。

清單 2. 配備構(gòu)建器的 Dockerfile

  
 
 
 
  1. FROM python:2.7 
  2.  
  3. # Install Ansible from source (master) 
  4. RUN apt-get -y update && \ 
  5.     apt-get install -y python-httplib2 python-keyczar python-setuptools python-pkg-resources 
  6. git python-pip && \ 
  7.     apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 
  8. RUN pip install paramiko jinja2 PyYAML setuptools pycrypto>=2.6 six \ 
  9.     requests docker-py  # docker inventory plugin 
  10. RUN git clone http://github.com/ansible/ansible.git /opt/ansible && \ 
  11.     cd /opt/ansible && \ 
  12.     git reset --hard fbec8bfb90df1d2e8a0a4df7ac1d9879ca8f4dde && \ 
  13.     git submodule update --init 
  14.  
  15. ENV PATH /opt/ansible/bin:$PATH 
  16. ENV PYTHONPATH $PYTHONPATH:/opt/ansible/lib 
  17. ENV ANSIBLE_LIBRARY /opt/ansible/library 
  18.  
  19. # setup ssh 
  20. RUN mkdir /root/.ssh 
  21. ADD ansible_id_rsa /root/.ssh/id_rsa 
  22. ADD ansible_id_rsa.pub /root/.ssh/id_rsa.pub 
  23.  
  24. # extend Ansible 
  25. # use an inventory directory for multiple inventories support 
  26. RUN mkdir -p /etc/ansible/inventory && \ 
  27.     cp /opt/ansible/plugins/inventory/docker.py /etc/ansible/inventory/ 
  28. ADD ansible.cfg  /etc/ansible/ansible.cfg 
  29. ADD hosts  /etc/ansible/inventory/hosts 

這些指令改編自官方構(gòu)建版本,自動化了一次來自 Ansible 主分支上的提交 fbec8bfb90df1d2e8a0a4df7ac1d9879ca8f4dde 有效安裝。

主機(jī)和 ansible.cfg 配置文件(參見 清單 3 和 清單 4)已封裝。通過使用容器,可以確保您將共享同一個環(huán)境。在本示例中,Dockerfile 安裝了 Python 2.7.10 版和 Ansible 2.0.0。

清單 3. 主機(jī)配置文件

  
 
 
 
  1. # hosts 
  2. # this file is an inventory that Ansible is using to address remote servers.  
  3. Make sure to replace the information with your specific setup and variables  
  4. that you don't want to provide for every command. 
  5.  
  6. [docker] 
  7. # host properties where docker daemon is running 
  8. 192.168.0.12 ansible_ssh_user=xavier 

清單 4. Ansible 配置文件

  
 
 
 
  1. # ansible.cfg 
  2.  
  3. [defaults] 
  4.  
  5. # use the path created from the Dockerfile 
  6. inventory = /etc/ansible/inventory 
  7.  
  8. # not really secure but convenient in non-interactive environment 
  9. host_key_checking = False 
  10. # free you from typing `--private-key` parameter 
  11. priva_key_file = ~/.sh/id_rsa 
  12.  
  13. # tell Ansible where are the plugins to load 
  14. callback_plugins   = /opt/ansible-plugins/callbacks 
  15. connection_plugins = /opt/ansible-plugins/connections 

#p#

在構(gòu)建 Ansible 容器之前,您必須導(dǎo)出 DOCKER_HOST 環(huán)境變量,因為 Ansible 將使用它連接到遠(yuǎn)程 Docker 守護(hù)進(jìn)程。在使用 HTTP 端點(diǎn)時,需要修改 /etc/default/docker(參見 清單 5)。

清單 5. 修改 /etc/default/docker

  
 
 
 
  1. # make docker to listen on HTTP and default socket 
  2. DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock" 

輸入命令 sudo service docker restart 來重新啟動 Docker 守護(hù)進(jìn)程,以便讓對它的配置文件的更改生效。

以下命令將會構(gòu)建并驗證您用來輸入命令的 Ansible 容器(參見 清單 6)。

清單 6. 構(gòu)建和驗證 Ansible 容器的命令

  
 
 
 
  1. # you need DOCKER_HOST variable to point to a reachable docker daemon 
  2. # pick the method that suits your installation 
  3.  
  4. # for boot2docker users 
  5. eval "$(boot2docker shellinit)" 
  6. # for docker-machine users, provisioning the running VM was named "dev" 
  7. eval "$(docker-machine env dev)" 
  8. # for users running daemon locally 
  9. export DOCKER_HOST=tcp://$(hostname -I | cut -d" " -f1):2375 
  10. # finally users relying on a remote daemon should provide the server's public ip 
  11. export DOCKER_HOST=tcp://1.2.3.4:2375 
  12.  
  13. # build the container from Dockerfile 
  14. docker build -t article/ansible . 
  15.  
  16. # provide server API version, as returned by `docker version | grep -i "server api"` 
  17. # it should be at least greater or equal than 1.8 
  18. export DOCKER_API_VERSION=1.19 
  19.  
  20. # create and enter the workspace 
  21. docker run -it --name builder \ 
  22.     # make docker client available inside 
  23.     -v /usr/bin/docker:/usr/bin/docker \ 
  24.     -v /var/run/docker.sock:/var/run/docker.sock \ 
  25.     # detect local ip 
  26.     -e DOCKER_HOST=$DOCKER_HOST \ 
  27.     -e DEFAULT_DOCKER_API_VERSION=DOCKER_API_VERSION \ 
  28.     -v $PWD:/app -w /app \  # mount the working space 
  29.     article/ansible bash 
  30.  
  31. # challenge the setup 
  32. $ container > ansible docker -m ping 
  33. 192.168.0.12 | SUCCESS => { 
  34.     "invocation": { 
  35.         "module_name": "ping", 
  36.         "module_args": {} 
  37.     }, 
  38.     "changed": false, 
  39.     "ping": "pong" 

目前為止,一切順利。您能夠從容器輸入命令。在下一節(jié)中,將對 Ansible 使用特定于 Docker 的擴(kuò)展。

使用 playbook 和插件擴(kuò)展 Ansible 環(huán)境

實(shí)質(zhì)上,Ansible 通過 playbook 自動化了它的執(zhí)行,這些 playbook 是指定要執(zhí)行的每個任務(wù)和它們的屬性的 YAML 文件(參見清單 7)。

Ansible 還使用了清單 (inventory) 來將用戶提供的主機(jī)映射到基礎(chǔ)架構(gòu)中的具體端點(diǎn)。不同于上一節(jié)中使用的靜態(tài) hosts 文件,Ansible 也支持動態(tài)內(nèi)容。內(nèi)置的列表包含一個 Docker 插件,該插件可查詢 Docker 守護(hù)進(jìn)程并向 Ansible playbook 共享大量信息。

清單 7. 一個 Ansible playbook

  
 
 
 
  1. # provision.yml 
  2.  
  3. - name: debug docker host 
  4.   hosts: docker 
  5.   tasks: 
  6.   - name: debug infrastructure 
  7.     # access container data : print the state 
  8.     debug: var=hostvars["builder"]["docker_state"] 
  9.  
  10. # you can target individual containers by name 
  11. - name: configure the container 
  12.   hosts: builder 
  13.   tasks: 
  14.    - name: run dummy command 
  15.      command: /bin/echo hello world 

清單 8 中的命令查詢 Docker 主機(jī),導(dǎo)入事實(shí),打印一些事實(shí),并使用它們對構(gòu)建器容器執(zhí)行第二個任務(wù)(如 清單 7 中所示)。

清單 8. 查詢 Docker 主機(jī)的命令

  
 
 
 
  1. ansible-playbook provision.yml -i /etc/ansible/inventory 
  2. # ... 
  3. TASK [setup] ******************************************************************** 
  4. fatal: [builder]: FAILED! => {"msg": "ERROR! SSH encountered an unknown error during the 
  5. connection. Re-run the command using -vvvv, which enables SSH debugging 
  6. output to help diagnose the issue", "failed": true} 
  7. # ... 

Ansible 不能連接容器,因為它沒有運(yùn)行 SSH 服務(wù)器。SSH 服務(wù)器是一個要管理的額外進(jìn)程,它與實(shí)際應(yīng)用程序完全無關(guān)。在下一節(jié)中,我們將使用一個連接插件來排除此困難。

連接插件是實(shí)現(xiàn)傳輸命令(比如 SSH 或本地執(zhí)行)的類。Docker 1.3 隨帶了 docker exec,并能夠在容器命名空間內(nèi)運(yùn)行任務(wù)。此外,因為您之前已學(xué)習(xí)如何連接特定的目標(biāo)容器,所以您可以使用此功能來處理 playbook。

像其他插件類型一樣,連接掛鉤(參見 清單 9)繼承一個抽象類,會在您將其放到預(yù)期的目錄(您在配置文件 ansible.cfg 中定義的是 /opt/ansible-plugins/connections)時自動可用。

清單 9. 連接插件

  
 
 
 
  1. # saved as ./connection_plugins/docker.py 
  2.  
  3. import subprocess 
  4. from ansible.plugins.connections import ConnectionBase 
  5.  
  6. class Connection(ConnectionBase): 
  7.  
  8.    @property 
  9.     def transport(self): 
  10.         """ Distinguish connection plugin. """ 
  11.         return 'docker' 
  12.  
  13.    def _connect(self): 
  14.         """ Connect to the container. Nothing to do """ 
  15.         return self 
  16.  
  17.    def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, 
  18.                      executable='/bin/sh', in_data=None, su=None, 
  19.                      su_user=None): 
  20.         """ Run a command within container namespace. """ 
  21.  
  22.     if executable: 
  23.         local_cmd = ["docker", "exec", self._connection_info.remote_addr, executable, '-c', cmd] 
  24.     else: 
  25.         local_cmd = '%s exec "%s" %s' % ("docker", self._connection_info.remote_addr, cmd) 
  26.  
  27.     self._display.vvv("EXEC %s" % (local_cmd), host=self._connection_info.remote_addr) 
  28.     p = subprocess.Popen(local_cmd, 
  29.         shell=isinstance(local_cmd, basestring), 
  30.         stdin=subprocess.PIPE, stdout=subprocess.PIPE, 
  31.         stderr=subprocess.PIPE) 
  32.  
  33.     stdout, stderr = p.communicate() 
  34.     return (p.returncode, '', stdout, stderr) 
  35.  
  36.     def put_file(self, in_path, out_path): 
  37.         """ Transfer a file from local to container """ 
  38.         pass 
  39.  
  40.     def fetch_file(self, in_path, out_path): 
  41.         """ Fetch a file from container to local. """ 
  42.         pass 
  43.  
  44.     def close(self): 
  45.         """ Terminate the connection. Nothing to do for Docker""" 
  46.         pass 

#p#

此代碼掛鉤到 Ansible 模塊中,以便通過一個本地 docker exec 而不是默認(rèn)的 ssh 來運(yùn)行命令。您需要重新排列一些設(shè)置步驟來告訴 Ansible 使用此插件(參見 清單 10)。

清單 10. docker exec 的連接插件

  
 
 
 
  1. # modify the builder Dockerfile to upload the plugin code  
  2. where Ansible is expecting connection plugins 
  3. echo "ADD connection_plugins/docker.py /opt/ansible-plugins/connections/docker.py" >> Dockerfile 
  4.  
  5. # then, you need to explicitly tell which connection hook to use  
  6. when executing playbooks. 
  7. # you can achieve this by inserting the 'connection' property at the top  
  8. of provision tasks in provision.yml 
  9.  
  10. - name: configure the container 
  11.   connection: docker 
  12.   hosts: builder 
  13.  
  14. # you are ready to redeploy the builder container  
  15. # (providing DOCKER_HOST and DOCKER_API_VERSION are still set like before) 
  16.  
  17. # rebuild the image 
  18. docker build -t article/ansible . 
  19.  
  20. # restart the builder environment 
  21. docker run -it --name builder \ 
  22.     # make docker client available inside 
  23.     -v /usr/bin/docker:/usr/bin/docker \ 
  24.     -v /var/run/docker.sock:/var/run/docker.sock \ 
  25.     # detect local ip 
  26.     -e DOCKER_HOST=$DOCKER_HOST \ 
  27.     -e DEFAULT_DOCKER_API_VERSION=DOCKER_API_VERSION \ 
  28.     -v $PWD:/app -w /app \  # mount the working space 
  29.     article/ansible bash 
  30.  
  31. # rerun provisioning from inside 
  32. ansible-playbook -i /etc/ansible/inventory provision.yml 
  33. # ... Hurrah, full green output ... 

目前為止,您在容器中運(yùn)行 Ansible 任務(wù),對容器或主機(jī)沒有太多需求。盡管此實(shí)現(xiàn)滿足了初始需求,但仍有一些不嚴(yán)密的地方需要解決。

前面的代碼在同一個節(jié)點(diǎn)上運(yùn)行任務(wù)。一種更逼真的工作流會啟動一個新基礎(chǔ)鏡像,配置它,最終提交、推送和關(guān)閉得到的工件。得益于 Ansible 中內(nèi)置的 Docker 模塊,這些步驟無需額外的代碼即可實(shí)現(xiàn)(參見 清單 11)。

清單 11. Ansible 中啟動一個新基礎(chǔ)鏡像的 Docker 模塊

  
 
 
 
  1. --- 
  2. - name: initialize provisioning 
  3.   hosts: docker 
  4.  
  5.   - name: start up target container 
  6.     docker: 
  7.       image: python:2.7 
  8.       name: lab 
  9.       pull: missing 
  10.       detach: yes 
  11.       tty: yes 
  12.       command: sleep infinity 
  13.       state: started 
  14.   # dynamically update inventory to make it available down the playbook 
  15.   - name: register new container hostname 
  16.     add_host: name=lab 
  17.  
  18. - name: provision container 
  19.   connection: docker 
  20.   hosts: lab 
  21.   tasks: 
  22.       # ... 
  23.  
  24. - name: finalize build 
  25.   hosts: docker 
  26.   tasks: 
  27.     - name: stop container 
  28.       docker: 
  29.         name: lab 
  30.         image: python:2.7 
  31.         state: stopped 

前面已經(jīng)提到過,自動命名和存儲在成功配備后構(gòu)建的鏡像會很方便。不幸的是,Ansible 中的 Docker 模塊沒有實(shí)現(xiàn)方法來標(biāo)記和推送鏡像。您可以使用簡單的 shell 命令來克服此限制(參見 清單 12)。

清單 12. 命名和存儲鏡像的 shell 命令

  
 
 
 
  1. # name the resulting artifact under a human readable image tag 
  2. docker tag lab article/lab:experimental 
  3.  
  4. # push this image to the official docker hub 
  5. # make sure to replace 'article' by your own Docker Hub login (https://hub.docker.com) 
  6. # (this step is optional and will only make the image available from any docker host.  
  7. You can skip it or even use your own registry) 
  8. docker push article/lab:experimental 

我們的工具正在成形,但它仍缺少一個必要特性:層緩存。

在使用 Dockerfile 構(gòu)建容器時,通常需要迭代許多次才能完成。為了顯著加快該過程,成功的步驟會被緩存并在后續(xù)運(yùn)行中重用。

要復(fù)制此行為,我們的工具在每次成功完成任務(wù)后提交了容器狀態(tài)。如果發(fā)生構(gòu)建錯誤,該工具會從上次的快照位置重新啟動配備過程。Ansible 承諾實(shí)現(xiàn)冪等的任務(wù),所以以前成功的任務(wù)不會處理兩次。

借助 Ansible,您可以使用回調(diào)插件來掛住任務(wù)事件(參見 清單 13)。這些類應(yīng)實(shí)現(xiàn)了特定的回調(diào),這些回調(diào)在 playbook 生命周期的各個步驟上觸發(fā)。

清單 13. 掛住任務(wù)事件的回調(diào)插件

  
 
 
 
  1. # save as callback_plugins/docker-cache.py 
  2. import hashlib 
  3. import os 
  4. import socket 
  5.  
  6. # Hacky Fix `ImportError: cannot import name display` 
  7. # pylint: disable=unused-import 
  8. import ansible.utils 
  9. import requests 
  10. import docker 
  11.  
  12.  
  13. class DockerDriver(object): 
  14.     """ Provide snapshot feature through 'docker commit'. """ 
  15.  
  16.     def __init__(self, author='ansible'): 
  17.         self._author = author 
  18.         self._hostname = socket.gethostname() 
  19.         try: 
  20.             err = self._connect() 
  21.         except (requests.exceptions.ConnectionError, docker.errors.APIError), error: 
  22.             ansible.utils.warning('Failed to contact docker daemon: {}'.format(error)) 
  23.             # deactivate the plugin on error 
  24.             self.disabled = True 
  25.             return 
  26.  
  27.         self._container = self.target_container() 
  28.         self.disabled = True if self._container is None else False 
  29.  
  30.     def _connect(self): 
  31.         # use the same environment variable as other docker plugins 
  32.         docker_host = os.getenv('DOCKER_HOST', 'unix:///var/run/docker.sock') 
  33.         # default version is current stable docker release (10/07/2015) 
  34.         # if provided, DOCKER_VERSION should match docker server api version 
  35.         docker_server_version = os.getenv('DOCKER_VERSION', '1.19') 
  36.         self._client = docker.Client(base_url=docker_host, 
  37.                                      version=docker_server_version) 
  38.         return self._client.ping() 
  39.  
  40.     def target_container(self): 
  41.         """ Retrieve data on the container you want to provision. """ 
  42.         def _match_container(metadatas): 
  43.             return metadatas['Id'][:len(self._hostname)] == self._hostname 
  44.  
  45.         matchs = filter(_match_container, self._client.containers()) 
  46.         return matchs[0] if len(matchs) == 1 else None 
  47.  
  48.     def snapshot(self, host, task): 
  49.         tag = hashlib.md5(repr(task)).hexdigest() 
  50.         try: 
  51.             feedback = self._client.commit(container=self._container['Id'], 
  52.                                            repository='factory', 
  53.                                            tag=tag, 
  54.                                            author=self._author) 
  55.         except docker.errors.APIError, error: 
  56.             ansible.utils.warning('Failed to commit container: {}'.format(error)) 
  57.             self.disabled = True 
  58.  
  59.  
  60. # pylint: disable=E1101 
  61. class CallbackModule(object): 
  62.     """Emulate docker cache. 
  63.     Commit the current container for each task. 
  64.  
  65.     This plugin makes use of the following environment variables: 
  66.         - DOCKER_HOST (optional): How to reach docker daemon. 
  67.           Default: unix://var/run/docker.sock 
  68.         - DOCKER_VERSION (optional): Docker daemon version. 
  69.           Default: 1.19 
  70.         - DOCKER_AUTHOR (optional): Used when committing image. Default: Ansible 
  71.  
  72.     Requires: 
  73.         - docker-py >= v0.5.3 
  74.  
  75.     Resources: 
  76.         - http://docker-py.readthedocs.org/en/latest/api/ 
  77.     """ 
  78.  
  79.     _current_task = None 
  80.  
  81.     def playbook_on_setup(self): 
  82.         """ initialize client. """ 
  83.       &nb
    當(dāng)前名稱:如何讓Ansible和Docker愉快地在一起
    文章來源:http://www.dlmjj.cn/article/ccsdsoo.html