日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用Docker构建服务(6)

發布時間:2023/12/8 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Docker构建服务(6) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 構建第一個應用

要構建的第一個應用是使用Jekyll框架的自定義網站。我們會構建以下兩個鏡像。

** 一個鏡像安裝了Jekyll及其他用于構建Jekyll網站的必要的軟件包。 ** 一個鏡像通過Apache來讓Jekyll網站工作起來。

我們打算在啟動容器時,通過創建一個新的Jekyll網站來實現自服務。工作流程如下:

** 創建Jekyll基礎鏡像和Apache鏡像(只需要構建一次)。 ** 從Jekyll鏡像創建一個容器,這個容器存放通過卷掛載的網站源代碼。 ** 從Apache鏡像創建一個容器,這個容器利用包含編譯后的網站的卷,并為其服務。 ** 在網站需要更新時,清理并重復上面的步驟。

可以把這個例子看作是創建一個多主機站點最簡單的方法。

1.1 Jekyll基礎鏡像

讓我們開始為第一個鏡像(Jekyll基礎鏡像)創建Dockerfile。我們先創建一個新目錄和一個空的Dockerfile,代碼清單如下:

** 創建Jekyll Dockerfile $ mkdir jekyll $ cd jekyll $ vi Dockerfile

讓我們來看看Dockerfile文件的內容,代碼清單:

** Jekyll Dockerfile FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install ruby ruby-dev make node.js RUN gem install --no-rdoc --no-ri jekyll -v 2.5.3VOLUME /data VOLUME /var/www/html WORKDIR /dataENTRYPOINT [ "jekyll", "build", "--destination=/var/www/html" ]

鏡像基于Ubuntu 14.04,并且安裝了Ruby和用于支持Jekyll包。然后我們使用了VOLUME指令創建了以下兩個卷。

** /data/, 用來存放網站的源代碼。 ** /var/www/html/,用來存放編譯后的Jekyll網站代碼。

然后我們需要將工作目錄設置到/data/,并通過ENTRYPOINT指令指定自動構建的命令,這個命令會將工作目錄/data/中的所有的Jekyll網站代碼構建到/var/www/html目錄中。

1.2 構建Jekyll基礎鏡像

通過這個Dockerfile,可以使用docker build命令構建出可以啟動容器的鏡像,代碼清單:

** 構建Jekyll鏡像 $ sudo docker build -t jamtur01/jekyll .

這樣就構建成了名為jamtur01/jekyll,ID為****的新鏡像。這就是將要使用的新的Jekyll鏡像??梢允褂胐ocker images命令來查看這個新鏡像,代碼清單:

** 查看新的Jekyll基礎鏡像 $ sudo docker images

1.3 Apache 鏡像

接下來,我們來構建第二個鏡像,一個用來架構新網站的Apache服務器。我們先創建一個新目錄和一個空的Dockerfile,代碼清單:

** 創建 Apache Dockerfile $ mkdir apache $ cd apache $ vi Dockerfile

現在讓我們來看看這個Dockerfile的內容,代碼清單:

** Jekyll Apache 的Dockerfile FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install apache2VOLUME [ "/var/www/html" ] WORKDIR /var/www/htmlENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIREXPOSE 80ENTRYPOINT [ "/usr/sbin/apache2" ] CMD ["-D", "FOREGROUND"]

這個鏡像也是基于Ubuntu 14.04的,并安裝了Apache。然后我們使用VOLUME指令創建了一個卷,即/var/www/html/,用來存放編譯后的Jekyll網站。然后將/var/www/html設為工作目錄。

然后我們使用ENV指令設置了一些必要的環境變量,創建了必要的目錄,并且使用EXPOSE公開了80端口。最后指定了ENTRYPOINT和CMD指令組合來在容器啟動時默認運行Apache。

1.4 構建Jekyll Apache鏡像

有了這個Dockerfile, 可以使用docker build命令來構建可以啟動容器的鏡像,代碼清單:

** 構建Jekyll Apache鏡像 $ sudo docker build -t jamtur01/apache . ....

這樣就構建了名為jamtur01/apache,ID為****的新鏡像。這就是要使用的Apache鏡像??梢允褂胐ocker images命令來查看這個新鏡像,代碼清單:

** 查看新的Jekyll Apache鏡像 $ sudo docker images ...

1.5 啟動Jekyll網站

現在有了以下兩個鏡像。

** Jekyll:安裝了Ruby及其它必備軟件包的Jekyll鏡像。 ** Apache:通過Apache Web服務器來讓Jeryll網站工作起來的鏡像。

我們從使用docker run命令來創建一個新的Jekyll容器開始我們的網站。我們將啟動容器,并構建我們的網站。

然后我們需要一些我的博客的源代碼。先把示例Jekyll博客復制到$HOME目錄(在這個例子里/home/james)中,代碼清單:

** 創建示例Jekyll博客 $ cd $HOME $ git clone https://github.com/jamtur01/james_blog.git

在這個目錄下可以看到一個啟用了Twitter Bootstrap(http://getbootstrap.com)的最基礎的Jekyll博客。如果你也想使用這個博客,可以修改_config.yml文件和主題,以符合你的要求。

現在在Jekyll容器里使用這個示例數據,代碼清單:

** 創建Jekyll容器 $ sudo docker run -v /home/james/james_blog:/data/ --name james_blog jamtur01/jekyll Configuration file:/data/_config.ymlSource:/dataDestination:/var/www/htmlGenerating...done.

我們啟動了一個叫作james_blog的新容器,把本地的james_blog目錄作為/data/卷掛載到容器里。容器已經拿到網站的源代碼,并將其構建到已編譯的網站,存放到/var/www/html/目錄。

卷是在一個或多個容器中特殊指定的目錄,卷會繞過聯合文件系統,為持久化數據和共享數據提供幾個有用的特性。

** 卷可以在容器間共享和重用。 ** 共享卷時不一定要運行相應的容器。 ** 對卷的修改會直接在卷上反映出來。 ** 更新鏡像時不會包含對卷的修改。 ** 卷會一直存在,直到沒有容器使用它們。

利用卷,可以在不用提交鏡像修改的情況下,向鏡像里加入數據(如源代碼,數據或者其他內容),并且可以在容器間共享這些數據。

卷在Docker宿主機的/var/lib/docker/volumes目錄中??梢酝ㄟ^docker inspect命令來查看某個卷的具體位置,如docker inspect -f "{{ range.Mounts }} {{.}} {{end}}"。

提示:在Docker 1.9中,卷功能已經得到擴展,能通過插件的方式支持第三方存儲系統,如Ceph,Flocker和EMC等。可以在卷插件文檔(http://docs.docker.com/engine/extend/plugins_volume/)和docker volume create命令文檔(https://docs.docker.com/engine/reference/commandline/volume_create/)中獲取更詳細的解釋。

?所以,如果想在另一個容器里使用/var/www/html/卷里編譯好的網站,可以創建一個新的鏈接到這個卷的容器,代碼清單:

** 創建Apache容器 $ sudo docker run -d -P --volumes-from james_blog jamtur01/apache ...

這看上去和典型的docker run很像,只是使用了一個新標志--volume-from。標志--volumes-from把指定容器里的所有卷都加入新創建的容器里。這意味著,Apache容器可以訪問之前創建的james_blog容器里/var/www/html卷中存放的編譯后的Jekyll網站。即便james_blog容器沒有運行,Apache容器也可以訪問這個卷。想想,這只是卷的特性之一。不過,容器本身必須存在。

注意:即使刪除了使用了卷的最后一個容器,卷中的數據也會持久保存。

構建Jekyll網站的最后一步是什么?來查看一下容器把已公開的80端口映射到了哪個端口,代碼清單:

** 解析Apache容器的端口 $ sudo docker port ****容器ID 80 0.0.0.0:49160

現在在Docker宿主機上瀏覽該網站。

1.6 更新Jekyll網站

如果要更新網站的數據,就更有意思了。假設要修改Jekyll網站。我們將通過編輯james_blog/_config.yml文件來修改博客的名字,代碼清單:

** 編輯Jekyll博客 $ vi james_blog/_config.yml并將title域改為James's Dynamic Docker-Driven Blog。

那么如何才能更新博客網站呢?只需要再次使用docker start命令啟動Docker容器即可,代碼清單:

** 再次啟動james_blog容器 $ sudo docker start james_blog ...** 查看james_blog容器的日志 $ sudo docker logs james_blog ...

可以看到,Jekyll編譯過程第二次被運行,并且網站已經被更新。這次更新已經寫入了對應的卷?,F在瀏覽Jekyll網站,就能看到變化了。

由于共享的卷會自動更新,這一切都不需要更新或者重啟Apache容器。這個流程非常簡單,可以將其擴展到更復雜的部署環境。

1.7 備份Jekyll卷

你可能會擔心一不小心刪除卷(盡管能使用已有的步驟輕松重建這個卷)。由于卷的優點之一就是可以掛載到任意容器,因此可以輕松備份它們?,F在創建一個新容器,用來備份/var/www/html卷,代碼清單:

** 備份/var/www/html卷 $ sudo docker run --rm --volumes-from james_blog \ -v $(pwd):/backup ubuntu \ tar cvf /backup/james_blog_backup.tar /var/www/html ...$ ls james_ blog_backup.tar ....

提示:我們還指定了--rm標志,這個標志對于只用一次的容器,或者用完即扔的容器,很有用。這個標志會在容器的進程運行完畢后,自動刪除容器。對于只用一次的容器來說,這是一種很方便的清理方法。

這里我們運行了一個已有的Ubuntu容器,并把james_blog的卷掛載到該容器里。這會在該容器里創建/var/www/html目錄。然后我們使用-v標志把當前目錄(通過$(pwd)命令獲得)掛載到容器的/backup目錄。最后我們的容器運行這一備份命令,代碼清單:

** 備份命令 tar cvf /backup/james_blog_backup.tar /var/www/html這個命令會創建一個名為james_blog_backup.tar的tar文件(該文件包括了/var/www/html目錄里所有內容),然后退出。這個過程創建了卷的備份。

這顯然只是一個最簡單的備份過程。用戶可以擴展這個命令,備份到本地存儲或者云端(如Amazon S3(http://aws.amazon.com/s3/)或者更傳統的類似Amanda(http://www.amanda.org的備份軟件))

提示:這個例子對卷中存儲的數據庫或者其他類似的數據也適用。只要簡單地把卷掛載到新容器,完成備份,然后廢棄這個用于備份的容器就可以了。

1.8 擴展Jekyll示例網站

下面是幾種擴展Jekyll網站的方法。

** 運行多個Apache容器,這些容器都使用來自james_blog容器的卷。在這些Apache容器前面加一個負載均衡器,我們就擁有了一個Web集群。
** 進一步構建一個容器,這個鏡像把用戶提供的源數據復制(如通過git clone)到卷里。再把這個卷掛載到從jamtur01/jekyll鏡像創建的容器。這就是一個可遷移的通用方案,而且不需要宿主機本地包含任何源代碼。
** 在上一個擴展基礎上可以很容易為我們的服務構建一個Web前端,這個服務用于從指定的源自動構建和部署網站。這樣用戶就有一個完全屬于自己的GitHub Pages了。

2. 使用Docker構建一個Java應用服務

現在我們來試一些稍微不同的方法,考慮把Docker作為應用服務器和編輯管道。這次做一個更加"企業化"且用于傳統工作負載的服務:獲取Tomcat服務器上的WAR文件,并運行一個Java應用程序。為了做到這一點,構建一個有兩個步驟的Docker管道。

** 一個鏡像從URL拉取指定的WAR文件并將其保存到卷里。 ** 一個含有Tomcat服務器的鏡像運行這些下載的WAR文件。

2.1 WAR文件的獲取程序

我們從構建一個鏡像開始,這個鏡像會下載WAR文件并將其掛載在卷里,代碼清單:

** 創建獲取程序(fetcher)的Dockerfile $ mkdir fetcher $ cd fetcher $ touch Dockerfile

現在我們來看看這個Dockerfile的內容,代碼清單:

** WAR文件的獲取程序 FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install wgetVOLUME [ "/var/lib/tomcat7/webapps/" ] WORKDIR /var/lib/tomcat7/webapps/ENTRYPOINT [ "wget" ] CMD [ "-?" ]

這個非常簡單的鏡像只做了一件事:容器執行時,使用wget從指定的URL獲取文件并把保存文件保存在/var/lib/tomcat7/webapps/目錄。這個目錄也是一個卷,并且是所有容器的工作目錄。然后我們會把這個卷共享給Tomcat服務器并且運行里面的內容。

最后,如果沒有指定URL,ENTRYPOINT和CMD指令會讓容器運行,在容器不帶URL運行的時候,這兩條指令通過返回wget幫助來做這一點。

現在我們來構建這個鏡像,代碼清單:

** 構建獲取程序的鏡像 $ sudo docker build -t jamtur01/fetcher .

2.2 獲取WAR文件

現在讓我們獲取一個示例文件來啟動新鏡像。從https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/下載Apache Tocat示例應用,代碼清單:

** 獲取WAR文件 $ sudo docker run -t -i --name sample jamtur01/fetcher \ https://tomcat.apache.org/toncat-7.0-doc/appdev/sample/sample.war ...

可以看到,容器通過提供的URL下載了sample.war文件。從輸出結果看不出最終的保存路徑,但是因為設置了容器的工作目錄,sample.war文件最終會保存到/var/lib/tomcat7/webapps/目錄中。

可以在/var/lib/docker目錄找到這個WAR文件。我們先用docker inspect命令查找卷的存儲位置,代碼清單:

** 查看示例里的卷 $ sudo docker inspect -f "{{ range .Mounts }} {{.}} {{ end }}" sample {...}

然后我們可以查看這個目錄,代碼清單:

** 查看卷所在的目錄 $ ls -l /var/lib/docker/volumes/ ...

2.3 Tomcat7 應用服務器

現在我們已經有了一個可以獲取WAR文件的鏡像,并已經將示例WAR文件下載到了容器中。接下來我們構建Tomcat應用服務器的鏡像來運行這個WAR文件,代碼清單:

** 創建Tomcat 7 Dockerfile $ mkdir tomcat7 $ cd tomcat7 $ touch Dockerfile

現在我們來看看這個Dockerfile,代碼清單:

FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install tomcat7 default-jdkENV CATALINA_HOME /usr/share/tomcat7 ENV CATALINA_BASE /var/lib/tomcat7 ENV CATALINA_PID /var/run/tomcat7.pid ENV CATALINA_SH /usr/share/tomcat7/bin/catalina.sh ENV CATALINA_TMPDIR /tmp/tomcat7-tomcat7-tmpRUN mkdir -p $CATALINA_TMPDIRVOLUME [ "/var/lib/tomcat7/webapps" ]EXPOSE 8080ENTRYPOINT [ "/usr/share/tomcat7/bin/catalina.sh", "run" ]

這個鏡像很簡單。我們需要安裝Java JDK和Tomcat服務器。我們首先指定一些啟動Tomcat需要的環境變量,然后我們創建一個臨時目錄,還創建了/var/lib/tomcat7/webapps/卷,公開了Tomcat默認的8080端口,最后使用ENTRYPOINT指令來啟動Tomcat。

現在我們來構建Tomcat 7鏡像,代碼清單:

** 構建Tomcat7鏡像 $ sudo docker build -t jamtur01/tomcat7 .

2.4 運行WAR文件

現在,讓我們創建一個新的Tomcat實例,運行示例應用,代碼清單:

** 創建第一個Tomcat實例 $ sudo docker run --name sample_app --volumes-from sample \-d -P jamtur01/tomcat7

這會創建一個名為sample_app的容器,這個容器會復用sample容器里的卷。這意味著存儲在/var/lib/tomcat7/webapps/卷里的WAR文件會從sample容器掛載到sample_app容器,最終被Tomcat加載并執行。

讓我們在Web瀏覽器里看看這個示例程序。首先,我們必須使用docker port命令找出被公開的端口,代碼清單:

** 查找Tomcat應用的端口 sudo docker port sample_app 8080 0.0.0.0:49154

現在我們來瀏覽這個應用(使用URL和端口,并在最后加上/sample)看看都有什么。應該能看到正在運行的Tomcat應用。

2.5 基于Tomcat應用服務器的構建服務

現在有了自服務Web服務的基礎模塊,讓我們來看看怎么基于這些基礎模塊做擴展。為了做到這一點,我們已經構建好了一個簡單的基于Sinatra的Web應用,這個應用可以通過網頁自動展示Tomcat應用。這個應用叫TProv??梢栽诒緯倬W(http://dockerbook.com/code/6/tomcat/tprov/)或者GitHub(https://github.com/jamtur01/dockerbook-code/tree/master/code/6/tomcat/tprov)找到其源碼。

然后我們使用這個程序來演示如何擴展之前的示例。首先,要保證已經安裝了Ruby,代碼清單如下:TProv應用會直接安裝在Docker宿主機上,因為這個應用會直接和Docker守護進程交互。這也是要裝Ruby的地方。

注意:也可以把TProv應用安裝在Docker容器里。

** 安裝Ruby $ sudo apt-get -qqy install ruby make ruby-dev

然后可以通過Ruby gem安裝這個應用,代碼清單:

** 安裝TProv應用 $ sudo gem install --no-rdoc --no-ri tprov ...

這個命令會安裝TProv應用及相關的支撐gem。

然后可以使用tprov命令來啟動應用,代碼清單:

** 啟動TProv應用 $ sudo tprov ...

這個命令會啟動應用。現在我們可以在Docker宿主機上通過端口4567瀏覽TProv網站。

如我們所見,我們可以指定Tomcat應用的名字和指向Tocat WAR文件的URL。從https://gwt-examples.googlecode.com/files/Calendar.war下載示例日歷應用程序,并將其稱為Calendar。

單擊Submit按鈕下載WAR文件,將其放入卷里,運行Tomcat服務器,加載卷里的WAR文件。可以點擊List instances(展示實例)鏈接來查看實例的運行狀態。

這展示了:
** 容器的ID。
** 容器的內部IP地址。
** 服務映射到的接口和端口。

利用這些信息我們可以通過瀏覽映射的端口來查看應用的運行狀態,還可以使用Delete?(是否刪除)復選框來刪除正在運行的實例。

可以查看TProv應用的源代碼(https://github.com/jamtur01/dockerbook-code/blob/master/code/6/tomcat/tprov/lib/tprov/app.rb),看看程序是如何實現這些功能的。這個應用很簡單,只是通過shell執行docker程序,再捕獲輸出,來運行或者刪除容器。

可以隨意使用TProv代碼,在之上做擴展,或者干脆重新寫一份自己的代碼。本文應用主要用于展示,使用Docker構建一個應用程序部署管道是很容易的事。

警告:TProv應用確實太簡單了,缺少某些錯誤處理和測試。這個應用的開發過程很快:只寫了一個小時,用于展示再構建應用和服務時Docker是一個多么強大的工具。如果你在這個應用里找到了bug(或者想把它寫得更好),可以通過再https://github.com/jamtur01/dockerbook-code提交issue或者PR來告訴我。

3. 多容器的應用棧

在最后一個服務應用得實例中我們把一個使用Express框架的,帶有Redis后端的Node.js應用完全Docker化了。這里要繼續演示如何把之前兩章學到的Docker特性結合起來使用,包括鏈接和卷。

在這個例子中,我們會構建一系列的鏡像來支持部署多容器的應用。

** 一個Node容器,用來服務于Node應用,這個容器會鏈接到。
** 一個Redis主容器,用于保存和集群化應用狀態,這個容器會鏈接到。
** 兩個Redis副本容器,用于集群化應用狀態。
** 一個日志容器,用于捕獲應用日志。

我們的Node應用程序會運行在一個容器中,它后面會有一個配置"主-副本"模式運行在多個容器中的Redis集群。

3.1 Node.js鏡像

先從構建一個安裝了Node.js的鏡像開始,這個鏡像有Express應用和相應的必要的軟件包,代碼如下:

** 創建Node.js Dockerfile $ mkdir nodejs $ cd nodejs $ mkdir -p nodeapp $ cd nodeapp $ wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/6/node/nodejs/nodeapp/package.json $ wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/6/node/nodejs/nodeapp/server.js $ cd .. $ vi Dockerfile

我們已經創建了一個叫nodejs的新目錄,然后創建了子目錄nodeapp來保存應用代碼。然后我們進入這個目錄,并下載了Node.js應用的源代碼。

注意:可以從本書官網(http://docker.com/code/6/node/)或者GitHub倉庫(https://github.com/jamtur01/dockerbook-code/tree/master/code/6/node/)下載Node應用的源代碼。

最后我們回到了nodejs目錄?,F在我們來看看這個Dockerfile的內容,代碼清單:

** Node.js鏡像 FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install nodejs npm RUN ln -s /usr/bin/nodejs /usr/bin/node RUN mkdir -p /var/log/nodeappADD nodeapp /opt/nodeapp/WORKDIR /opt/nodeapp RUN npm installVOLUME [ "/var/log/nodeapp" ]EXPOSE 3000ENTRYPOINT [ "nodejs", "server.js" ]

Node.js鏡像安裝了Node,然后我們用了一個簡單的技巧把二進制文件nodejs鏈接到node,解決了Ubuntu上原有的一些無法向后兼容的問題。

然后我們將nodeapp的源代碼通過ADD指令添加到/opt/nodeapp目錄。這個Node.js應用是一個簡單的Express服務器,包括一個存放應用依賴信息的package.json文件和包含實際應用代碼的server.js文件。

接著我們將工作目錄設置為/opt/nodeapp,并且安裝了Node應用的必要軟件包,還創建了用于存放Node應用日志的卷/var/log/nodeapp。

最后我們公開了3000端口,并使用了ENTRYPOINT指定了運行Node應用的命令nodejs server.js。

現在我們來構建鏡像,代碼清單:

** 構建Node.js鏡像 $ sudo docker build -t jamtur01/nodejs .

3.2 Redis基礎鏡像

現在我們繼續構建第一個Redis鏡像:安裝Redis的基礎鏡像(代碼清單如下:)。然后我們會使用這個鏡像構建Redis主鏡像和副本鏡像。

** 創建Redis基礎鏡像的Dockerfile $ mkdir redis_base $ cd redis_base $ vi Dockerfile

讓我們來看看這個Dockerfile的內容,代碼清單:

** 基礎Redis鏡像 FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get install -yqq software-properties-common python-software-properties RUN add-apt-repository ppa:chris-lea/redis-server RUN apt-get -yqq update RUN apt-get -yqq install redis-server redis-toolsVOLUME [ "/var/lib/redis", "/var/log/redis" ]EXPOSE 6379 CMD []

這個Redis基礎鏡像安裝了最新版本的Redis(從PPA庫安裝,而不是使用Ubuntu自帶的較老的Redis包),指定了兩個VOLUME(/var/lib/redis和/var/log/redis),公開了Redis的默認端口6379。因為不會執行這個鏡像,所以沒有包含ENTRYPOINT或者CMD指令。然后我們將只是基于這個鏡像構建別的鏡像。

現在我們來構建Redis基礎鏡像,代碼清單:

** 構建Redis鏡像 $ sudo docker build -t jamtur01/redis .

3.3 Redis主鏡像

我們繼續第一個Redis鏡像,即Redis主服務器,代碼清單:

** 創建Redis主服務器的Dockerfile $ mkdir redis_primary $ cd redis_primary $ vi Dockerfile

我們來看看這個Dockerfile的內容,代碼清單:

** Redis主鏡像 FROM jamtur01/redis MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-server.log" ]

Redis主鏡像基于之前的jamtur01/redis鏡像,并通過ENTRYPOINT指令指定了Redis服務啟動命令,Redis服務的日志文件保存到/var/log/redis/redis-server.log。

現在我們來看看構建Redis主鏡像,代碼清單如下:

** 構建Redis主鏡像 $ sudo docker build -t jamtur01/redis_primary .

3.4 Redis副本鏡像

為了配合Redis主鏡像,我們會創建Redis副本鏡像,保證為Node.js應用提供Redis服務的冗余度,代碼清單:

** 創建Redis副本鏡像的Dockerfile $ mkdir redis_replica $ cd redis_replica $ touch Dockerfile

現在我們來看看對應的Dockerfile,代碼清單:

** Redis副本鏡像 FROM jamtur01/redis MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-replica.log", "--slaveof redis_primary 6379" ]

Redis副本鏡像也是基于jamtur01/redis構建的,并且通過ENTRYPOINT指令指定了運行Redis服務器的命令,設置了日志文件和slaveof選項。這就把Redis配置為主-副本模式,從這個鏡像構建的任何容器都會將redis_primary主機的Redis作為主服務,鏈接其6379端口,成為其對應的副本服務器。

現在我們來構建Redis副本鏡像,代碼清單:

** 構建Redis副本鏡像 $ sudo docker build -t jamtur01/redis_replica .

3.5 創建Redis后端集群

現在我們已經有了Redis主鏡像和副本鏡像,已經可以構建我們自己的Redis復制環境了。首先我們創建了一個用來運行我們的Express應用程序的網絡,我們稱其為express,代碼清單:

** 創建express網絡 $ sudo docker network create express ...

現在讓我們在這個網絡中運行Redis主容器,代碼清單:

** 運行Redis主容器 $ sudo docker run -d -h redis-primary \ --net express --name redis_primary jamtur01/redis_primary ...

這里使用docker run命令從jamtur01/redis_primary鏡像創建了一個容器。這里使用了一個以前沒有見過的新標志-h,這個標志用來設置容器的主機名。這會覆蓋默認的行為(默認將容器的主機名設置為容器ID)并允許我們指定自己的主機名。使用這個標志可以確保容器使用redis_primary作為主機名,并被本地的DNS服務正確解析。

我們已經指定了--name標志,確保容器的名字是redis_primary,我們還指定了--net標志,確保該容器在express網絡中運行。稍后我們會看到,我們將使用這個網絡來確保容器的連通性。

讓我們使用docker logs命令來查看Redis主容器的運行狀況,代碼清單:

** Redis主容器的日志 $ sudo docker logs redis_primary

什么日志都沒有?這是怎么回事?原來Redis服務會將日志記錄到一個文件而不是記錄標準輸出,所以使用Docker查看不到任何日志。那怎么知道Redis服務器的運行情況呢?為了做到這一點,可以使用之前創建的/var/log/redis卷。現在我們來看看這個卷,讀取一些日志文件的內容,代碼清單:

** 讀取Redis主日志 $ sudo docker run -ti --rm --volumes-from redis_primary \ubuntu cat /var/log/redis/redis-server.log ...

這里以交互方式運行了另一個容器。這個命令指定了--rm標志,它會在進程運行完后自動刪除容器。我們還指定了--volumes-from標志,告訴它執行cat /var/log/redis/redis-server.log來展示日志文件。這種方法利用率卷的優點,可以直接從redis_primary容器掛載/var/log/redis目錄并讀取里面的日志文件。一會兒我們將會看到更多使用這個命令的情況。

查看Redis日志,可以看到一些常規警告,不過一切看上去都沒什么問題。Redis服務器已經準備好從6379端口接收數據了。

那么下一步,我們創建一個Redis副本容器,代碼清單:

** 運行第一個Redis副本容器 $ sudo docker run -d -h redis_replical \--name redis_replical \--net express \jamtur01/redis_replica ...

這里我們運行了另一個容器:這個容器來自jamtur01/redis_replica鏡像。和之前一樣,命令里指定了主機名(通過-h標志)和容器名(通過--name標志)都是redis_replical。我們還使用了--net標志在express網絡中運行Redis副本容器。

提示:在Docker 1.9之前的版本中,不能使用Docker Networking,只能使用Docker鏈接來連接Redis主容器和副本容器。

現在我們來檢查一下這個新容器的日志,代碼清單:

** 讀取Redis副本容器的日志 $ sudo docker run -ti --rm --volumes-from redis_replical \ubuntu cat /var/log/redis/redis-replica.log ....

這里通過交互的方式運行了一個新容器來查詢日志。和之前一樣,我們又使用了--rm標志,它在命令執行完畢后自動刪除容器。我們還指定了--volumes-from標志,掛載了redis_replical容器的所有卷。然后我們指定了ubuntu基礎鏡像,并讓它cat日志文件/var/log/ redis/redis-replica.log。

到這里我們已經成功啟動了redis_primary和redis_replical容器,并讓這兩個容器進行主從復制。

現在我們來加入另一個副本容器redis_replica2,確保萬無一失,代碼清單:

** 運行第二個Redis副本容器 $ sudo docker run -d -h redis_replica2 \--name redis_replica2 \--net express \jamtur01/redis_replica ...

我們來看看新容器的日志,代碼清單:

** 第二個Redis副本容器的日志 $ sudo docker run -ti --rm --volumes-from redis_replica2 ubuntu \cat /var/log/redis/redis-replica.log ...

現在可以確保Redis服務萬無一失了!

3.6 創建Node容器

現在我們已經讓Redis集群運行了,我們可以為啟動Node.js應用啟動一個容器,代碼清單如下:

** 運行Node.js容器 $ sudo docker run -d \--name nodeapp -p 3000:3000 \--net express \jamtur01/nodejs ....

我們來看看新容器的日志,代碼清單:

** nodeapp容器的控制日志 $ sudo docker logs nodeapp Listening on port 3000從這個日志可以看到Node應用程序監聽了3000端口。 ** Node應用的輸出 {"status":"ok" }

這個輸出表明應用正在工作。瀏覽器的會話狀態會先被記錄到Redis主容器redis_primary,然后復制到兩個Redis副本容器redis_replical和redis_replica2。

3.7 捕獲應用日志


現在應用已經可以運行了,需要把這個應用放到生產環境中。在生產環境里需要確??梢圆东@日志并將日志保存到日志服務器。我們將使用Logstash(http://logstash.net/)來完成這件事。我們先來創建一個Logstash鏡像,代碼清單:

** 創建Logstash的Dockerfile $ mkdir logstash $ cd logstash $ touch Dockerfile

現在我們來看看這個Dockerfile的內容,代碼清單:

** Logstash鏡像 FROM ubuntu:14.04 MAINTAINER James Turnbull <james@example.com> ENV REFRESHED_AT 2014-06-01RUN apt-get -yqq update RUN apt-get -yqq install wget RUN wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key and - RUN echo 'deb http://packages.elasticsearch.org/logstash/1.4/debian stable main' > /etc/apt/sources.list.d/logstash.list RUN apt-get -yqq update RUN apt-get -yqq install logstashADD logstash.conf /etc/WORKDIR /opt/logstashENTRYPOINT [ "/bin/logstash" ] CMD [ "--config=/etc/logstash.conf" ]

我們已經創建了鏡像并安裝了Logstash,然后將logstash.conf文件使用ADD指令添加到/etc/目錄?,F在我們來看看Logstash.conf文件的內容,代碼清單:

** Logstash配置文件 input {file {type => "syslog"path => ["/var/log/nodeapp/nodeapp.log", "/var/log/redis/redis-server.log"]} }output {stdout {codec => rubydebug} }

這個Logstash配置很簡單,它監控兩個文件,即/var/log/nodeapp/nodeapp.log和/var/log/redis/redis-server.log。Logstash會一直監視這兩個文件,將其中斷的內容發送給Logstash。配置文件的第二部分是output部分,接受所有Logstash輸入的內容并將其輸出到標準輸出上。現實中,一般會將Logstash配置為輸出到Elasticsearch集群或者其他的目的地,不過這里只使用標準輸出做演示,所以忽略了現實的細節。

注意:如果不太了解Logstash,想要深入學習可以參考作者的書(http://www.logstashbook.com)或者Logstash文檔(http://logstash.net)。

我們指定了工作目錄為/opt/logstash。最后,我們指定了ENTRYPOINT為/bin/logstash,并且指定了CMD為--config=/etc/logstash.conf。這樣容器啟動時會啟動Logstash并加載/etc/logstash.conf配置文件。

現在我們來構建Logstash鏡像,代碼清單:

** 構建Logstash鏡像 $ sudo docker build -t jamtur01/logstash .

構建好鏡像后,可以從這個鏡像啟動一個容器,代碼清單:

** 啟動Logstash容器 $ sudo docker run -d --name logstash \--volumes-from redis_primary \--volumes-from nodeapp \jamtur01/logstash

我們成功地啟動了一個名為logstash的新容器,并指定了兩次--volumes-from標志,分別掛載了redis_primary和nodeapp容器的卷,這樣就可以訪問Redis和Node的日志文件了。任何加到這些日志文件里的內容都會反映在logstash容器的卷里,并傳給Logstash做后續處理。

現在我們使用-f標志來查看logstash容器的日志,代碼清單:

** logstash容器的日志 $ sudo docker logs -f logstash ...

現在再在瀏覽器里刷新Web應用,產生一個新的日志。這樣應該能在logstash容器的輸出中看到這個事件,代碼清單:

** Logstash中的Node事件 {。。。 }

現在Node和Redis容器都將日志輸出到了Logstash。在生產環境中,這些事件會發生到Logstash服務器并存儲到Elasticsearch里。如果要加入新的Redis副本容器或者其他組件,也可以容易地將其日志輸出到日志容器里。

注意:如果需要,也可以通過卷對Redis做備份。

3.8 Node程序棧的小結

現在我們已經演示過了如何使用多個容器組成的應用程序棧,演示了如何使用Docker鏈接來將應用容器連在一起,還演示了如何使用Docker卷來管理應用中的各種數據。這些技術可以很容易地用來構建更加復雜地應用程序和架構。

4. 不使用SSH管理Docker容器

最后,在結束關于使用Docker運行服務的話題之前,了解一些管理Docker鏈接來將應用容器連在一起,還演示了如何使用Docker卷來管理應用中的各種數據。這些技術可以很容易地用來構建更加復雜地應用程序和架構。

傳統上講,通過SSH登入運行環境或者虛擬機里來管理服務。在Docker的世界里,大部分容器都只運行一個進程,所以不能使用這種訪問方法。不過就像之前多次看到的,其實不需要這種訪問:可以使用卷或者鏈接完成大部分同樣的管理操作。比如說,如果服務通過某個網絡接口做管理,就可以在啟動容器時公開這個接口;如果服務通過Unix套接字(socket)來管理,就可以通過卷公開這個套接字。如果需要給容器發送信號,就可以像代碼清單那樣使用docker kill命令發送信號。

** 使用docker kill發送信號 $ sudo docker kill -s <signal> <container>

這個操作會發送指定的信號(如HUP信號)給容器,而不是殺掉容器。

然而,有時候確實需要登入容器。即便如此,也不需要在容器里執行SSH服務或者打開任何不必要的訪問。需要登入容器時,可以使用一個叫nsenter的小工具。

注意:nsenter一般適用于Docker 1.2或者更早的版本呢。docker exec命令是在Docker 1.3中引入的,替換了它的大部分功能。

工具nsenter讓我們可以進入Docker用來構成容器的內核命名空間。從技術上說,這個工具可以進入一個已經存在的命名空間,或者在新的一組命名空間里執行一個進程。簡單來說,使用nsenter可以進入一個已經存在的容器的shell,即便這個容器沒有運行SSH或者任何類似目的的守護進程??梢酝ㄟ^Docker容器安裝nsenter,代碼清單:

** 安裝nsenter $ sudo docker run -v /usr/local/bin:/target jpetazzo/nsenter

這會把nsenter安裝到/usr/loca/bin目錄下,然后立刻就可以使用這個命令。

提示:工具nsenter也可能由所使用的Linux發行版(在util-linux包里)提供。

為了使用nsenter,首先要拿到要進入的容器的進程ID(PID)。可以使用docker inspect命令獲取PID,代碼清單如下:

** 獲取容器的進程ID PID=$(sudo docker inspect --format '{{.State.Pid}}' <container>)

然后就可以進入容器,代碼清單如下:

** 使用nsenter進入容器 $ sudo nsenter --target $PID --mount --uts --ipc --net --pid這會在容器里啟動一個shell,而不需要SSH或者其他類似的守護進程或者進程。

我們還可以將想在容器內執行的命令添加在nsenter命令行的后面,代碼清單:

** 使用nsenter在容器內執行命令 $ sudo nsenter --target $PID --mount --uts --ipc --net --pid ls ...

這會在目標容器內執行ls命令。

總結

以上是生活随笔為你收集整理的使用Docker构建服务(6)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。