在Docker裏使用(支持鏡像繼承的)supervisor管理進程(轉)

這篇文章是受 dockboard 之託幫忙翻譯的與 docker 有關的技術文章。譯自 Using Supervisor with Docker to manage processes (supporting image inheritance) ,做者 Quinten Krijger。
  
  在八月份,我寫了一篇關於如何建立 tomcat 鏡像的 blog 。從那之後,docker 又改進了不少,我對 docker 的瞭解也增長了不少。我很高興和你分們享我找到的關於管理 container 進程的好辦法。在讀完這篇文章後,我但願你能善加利用我 github 倉庫 裏的 supervisor 鏡像。
Docker 命令

在以前的文章裏,我提到 Docker(只能)支持運行一個前臺進程。咱們一般習慣使用相似 upstart 這種管理服務來初始化啓動流程,可是 Docker 默認沒有這些服務的支持。剛開始使用 Docker 時會很不習慣,你必須指定你想要運行的進程。這種行爲和虛擬機相比有個優勢,會盡量的保持輕量的 container。你能夠經過 run 命令最後的參數,在啓動 container 時指定進程命令,好比:git

 
docker run ubuntu echo "hello world"

 


另一種方法,你能夠利用 CMD 指令,在 Dockerfile 裏指定 docker run 命令的默認參數。好比,若是你目錄下的 Dockerfile 包含如下內容: github

FROM ubuntu
CMD echo "hello world"

 

再使用下面的指令構造 hello_world_printer 鏡像: web

docker build -t "hello_world_printer" .

使用下面的命令,你能夠獲得和以前 run 命令相同的執行結果。docker

docker run hello_world_printer

要注意,由於你能夠覆蓋掉 CMD 指定的命令行參數,這個只是個運行時的指令。有趣的事情是,在 Linux container 裏,你能夠只調用 upstart 命令而後獲得和普通虛擬機大體相同的行爲。
運行多個命令

運行多個進程是個很正常的想法。好比,一個 ssh 服務(這樣就能登陸到正在運行的 container)和實際的應用。你能夠用下面的方法運行 container:ubuntu

docker run ... /usr/sbin/sshd && run_your_app_in_foreground

 

這在開發時很方便。這樣,當應用進程退出後,由於惟一的前臺程序退出了,container 會自動關閉。固然你可使用 using /usr/bin/sshd -D 保證 container 不會退出,可是這裏真正的問題是,這種使用 run 命令設置初始程序的方式不夠簡潔。並且,隨着你的 container 變複雜,run 命令會愈來愈長。
因此,在運行更復雜的 container 的時候,不少人使用複雜的 bash 腳本。典型的 bash 腳本會執行一個前臺進程,並開啓一個或者多個(renegade)守護進程。與只是用 Docker 命令行的方式相比,這種方法最重要的改進在於,bash 腳本是能夠作版本控制的:啓動腳本在你的 Docker 鏡像裏,新的改動能夠和軟件項目一塊兒分發。不過,使用 bash 腳本管理進程依舊簡陋枯燥,並且容易出錯。緩存


……使用 supervisor

  更好的方法是使用 supervisor 。supervisor 能夠更好的管理進程:使用更加簡潔的代碼管理進程;在崩潰時能夠重啓進程;容許重啓一組進程而且有命令行工具和網頁界面來管理進程。固然,越大的能力要求 越大的責任:大量使用 supervisor 特性的代碼,預示着你應該將整個服務更好的拆分紅多個小的 supervisor 來管理。
我的來說,我喜歡 supervisor 讓我用更清晰的代碼管理啓動的進程。我見過最簡潔的使用例子,是子鏡像擴展出一個進程組。好比,若是你常用 SSH,使用一個 SSH 鏡像做爲基礎鏡像就是很合理的。這種狀況,在全部基於這個鏡像的擴展鏡像上實現啓動 SSH 進程的代碼,形式少就是一種重複代碼。我來給大家展現下我找到的解決這個問題的好辦法。
supervisor 基礎鏡像

  首先,由於我默認使用 supervisor,因此我全部的鏡像都擴展自一個只包含 supervisor 和最新版本 ubuntu 的基礎鏡像。你能夠在 這裏 找到這個 Dockerfile。這個基礎鏡像包括一個配置文件 /etc/supervisor.conf : tomcat

[supervisord]
nodaemon=true
[include]
files = /etc/supervisor/conf.d/*.conf

 

這個配置讓 supervisor 自己之前臺進程運行,這樣可讓咱們的 container 啓動後持續運行。第二,這個配置將包含全部在 /etc/supervisor/conf.d/ 目錄下的配置文件,啓動任何在這裏定義的程序。
擴展基礎鏡像

  
是的,想法很簡單。全部的子 container 經過將特定的 service.sv.conf 放到特定的目錄的方式,將其本身的服務加入到 supervisor 的管理裏。以後,使用以下命令啓動 container:bash

docker run child_image_name "supervisor -c /etc/supervisor.conf"

 

會自動啓動全部指定的進程。你能夠對鏡像作多層擴展,每層擴展加入一個或者多個服務到配置目錄。在 Docker 裏使用 supervisor 啓動命令代替 upstart 也更有效和有範。
做爲例子,讓咱們看看以前 blog 提到的 Tomcat 工做棧,是如何使用這種改進後的方法的。


網絡

  • 首先,和以前討論的同樣,咱們使用從 ubuntu 擴展而來的 supervisor 基礎鏡像

  •   以後,咱們使用在 supervisor 上安裝了 Java 的 JDK 鏡像 。Java 只是其餘服務使用的庫,因此咱們在這層不指定任何啓動服務。這層要作一些相似設置 JAVA_HOME 環境變量的一般任務

  •   Tomcat 鏡像在工做棧上安裝 Tomcat 並暴露 8080 端口。這層包括一個名字是 Tomcat 的服務,定義在 tomcat.sv.conf :
  • [program:webapp] command=/bin/bash -c "env > /tmp/tomcat.env && cat /etc/default/tomcat7 >> /tmp/tomcat.env && mv /tmp/tomcat.env /etc/default/tomcat7 && service tomcat7 start" redirect_stderr=true

     

    執行 Tomcat 服務的命令並不像我喜歡的那樣簡潔,將其放到一個專門的腳本里會更好。命令先添加了一些環境變量,好比 container 的關聯參數 ,到 /etc/default/tomcat7 ,這樣咱們能夠在以後的配置中使用這些參數,後面的例子會展現這種用法。也許使用相似 etcd 的鍵值存儲會更好,不過這超出了本文的範疇。
    固然,咱們這裏只安裝了默認的文件,沒有真正的網絡應用程序。

  • 你的網絡應用程序應當擴展自 Tomcat 鏡像,並安裝入真正的應用程序。當啓動 supervisor 的時候,會自動啓動 Tomcat。

一個 Tomcat 網絡程序的 Dockerfile 例子

如何安裝實際的網絡應用超出了本文的範疇,不過,做爲結束,我給出了個 Dockerfile 例子,演示如何使用這個工做棧。這個例子徹底基於 Java Tomcat,因此若是你對這個不感興趣,別讀了,玩別的去吧:)
假設,咱們有一個使用 Elasticsearch 的網絡應用:app

FROM quintenk/tomcat:7
# 安裝一些項目的依賴,這些依賴在每次更新時不會改變
# RUN apt-get -y install ...
RUN rm -rf /var/lib/tomcat7/webapps/*
# 將配置加入 /etc/default/tomcat7,好比:
...
RUN echo 'DOCKER_OPTS="-DELASTICSEARCH_SERVER_URL=${ELASTICSEARCH_PORT_9200_TCP_ADDR}"' >> /etc/default/tomcat7
RUN echo 'CATALINA_OPTS="... ${DOCKER_OPTS}"' >> /etc/default/tomcat7
# 加入相似 log4j.properties 的配置文件,並將其 chown root:tomcat7
# 假設項目已經構建好了,並且 ROOT.war 在你構建 Docker 的目錄(包含 Dockerfile 的目錄)。基於緩存的考慮,這個做爲最後的步驟
ADD ROOT.war /var/lib/tomcat7/webapps/
RUN chown root:tomcat7 /var/lib/tomcat7/webapps/ROOT.war
CMD supervisord -c /etc/supervisor.conf

 

In this code, the variables for elasticsearch (a search index), are set because the Supervisor configuration for Tomcat prepends all variables to the /etc/default/tomcat7 file at start-up time. Of course, we would need to start the webapp with a link to the elasticsearch container: e.g. 這段代碼裏,elasticsearch 的相關環境變量(搜索索引)已經被設置了,由於 supervisor 關於 Tomcat 的配置,會在啓動時將全部環境變量添加到 /etc/default/tomcat7。固然,咱們在啓動網絡應用鏡像時須要關聯到 elasticsearch containter,好比:

docker run -link name_of_elasticsearch_instance:elasticsearch -d name_of_webapp_image "supervisor -c /etc/supervisor.conf"

 

你如今的網絡應用能夠去訪問 ELASTICSEARCH_SERVER_URL 路徑了。你能夠在配置文件裏使用這個變量,像這樣:

 
    FROM ubuntu
    CMD echo "hello world"
    0

 


這樣就能夠將配置暴露給你的應用程序。若是你是個 Java 開發者,而且也閱讀了前一篇文章,但願這讓你能開始一段愉快的代碼之旅。

相關文章
相關標籤/搜索