這篇文章是受 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 工做棧,是如何使用這種改進後的方法的。
網絡
[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 網絡程序的 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 開發者,而且也閱讀了前一篇文章,但願這讓你能開始一段愉快的代碼之旅。