上週希雲和你們分享了《用Docker從新定義Java虛擬化部署(基礎篇)》,估計有些小夥伴早已按耐不住着急的心情了吧。今天希雲就和你們分享在docker裏部署java應用的實戰案例。java
>>>Dockerfilesgit
Dockerfile包含了一系列指令,告訴Docker如何去構建一個鏡像,它指定了鏡像的基點,以及配置鏡像的每一個細節。如下是一個Dockerfile示例,是CentOS鏡像的Dockerfile。web
代碼清單1. CentOS Dockerfileredis
```sh
FROM scratch
MAINTAINER The CentOS Project - ami_creator
ADD centos-7-20150616_1752-docker.tar.xz /
# Volumes for systemd
# VOLUME ["/run", "/tmp"]
# Environment for systemd
# ENV container=docker
# For systemd usage this changes to /usr/sbin/init
# Keeping it as /bin/bash for compatibility with previous
CMD ["/bin/bash"]
```docker
大部份內容是註釋,主要有四句命令:
1. <code>FROM scratch</code>:全部Dockerfile都要從一個基礎鏡像繼承,在這個例子中,CentOS鏡像是繼承於"scratch"鏡像,這個鏡像是全部鏡像的根。這個配置是固定的,代表了這個是Docker的根鏡像之一。
2. <code>MAINTAINER ...</code>:<code>MAINTAINER</code>指令指明瞭鏡像的全部者,這個例子中全部者是CentOS Project。
3. <code>ADD centos...tar.xz</code>:<code>ADD</code>指令告訴Docker把指定文件上傳到鏡像中,若是文件是壓縮過的,會把它解壓到指定路徑。這個例子中,Docker會上傳一個CentOS操做系統的Gzip包,並解壓到系統的根目錄。
4. <code>CMD ["/bin/bash"]</code>:最後,<code>CMD</code>指令告訴Docker要執行什麼命令,這個例子中,最後會進入Bourne Again Shell (bash)終端。
如今你知道Docker大概是長什麼樣子了,接下來再看看Tomcat官方的Dockerfile,圖2說明了這個文件的架構。apache
這個架構未必如你想象中那麼簡單,但咱們接下來會慢慢學習它,其實它是很是有邏輯的。上邊已經提過全部Dockerfile的根是<code>scratch</code>,接下來指定的是<code>debian:jessie</code>鏡像,這個官方鏡像是基於標準鏡像構建的,Docker不須要重複發明輪子,每次都建立一個新鏡像了,只要基於一個穩定的鏡像來繼續構建新鏡像便可,在這個例子中,<code>debian:jessie</code>是一個官方Debian Linux鏡像,就像上邊的CentOS同樣,它只有三行指令。centos
代碼清單 2. debian:jessie Dockerfile緩存
```sh
FROM scratch
ADD rootfs.tar.xz /
CMD ["/bin/bash"]
```tomcat
在上圖中咱們還見到有安裝兩個額外的鏡像,CURL 和 Source Code Management,鏡像<code>buildpack-deps:jessie-curl</code>的Dockerfile如清單3所示。安全
代碼清單 3. buildpack-deps:jessie-curl Dockerfile
```sh
FROM debian:jessie
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
wget \
&& rm -rf /var/lib/apt/lists/*
```
這個Dockerfile中使用<code>apt-get</code>去安裝<code>curl</code>和<code>wget</code>,使這個鏡像能從其餘服務器下載軟件。<code>RUN</code>指令讓Docker在運行的實例中執行具體的命令,這個例子中,它會更新全部庫(<code>apt-get update</code>),而後執行<code>apt-get install</code>去安裝<code>curl</code>和<code>wget</code>。
<code>buildpack-deps:jessie-scp</code>的Dockerfile如清單4所示.
代碼清單 4. buildpack-deps:jessie-scp Dockerfile
```sh
FROM buildpack-deps:jessie-curl
RUN apt-get update && apt-get install -y --no-install-recommends \
bzr \
git \
mercurial \
openssh-client \
subversion \
&& rm -rf /var/lib/apt/lists/*
```
這個[Dockerfile]會安裝源碼管理工具,例如Git,Mercurial, 和 Subversion。
Java的[Dockerfile會更加複雜些,如清單5所示。
代碼清單 5. Java Dockerfile
```sh
FROM buildpack-deps:jessie-scm
# A few problems with compiling Java from source:
# 1. Oracle. Licensing prevents us from redistributing the official JDK.
# 2. Compiling OpenJDK also requires the JDK to be installed, and it gets
# really hairy.
RUN apt-get update && apt-get install -y unzip && rm -rf /var/lib/apt/lists/*
RUN echo 'deb jessie-backports main' > /etc/apt/sources.list.d/jessie-backports.list
# Default to UTF-8 file.encoding
ENV LANG C.UTF-8
ENV JAVA_VERSION 8u66
ENV JAVA_DEBIAN_VERSION 8u66-b01-1~bpo8+1
# see
# and
ENV CA_CERTIFICATES_JAVA_VERSION 20140324
RUN set -x \
&& apt-get update \
&& apt-get install -y \
openjdk-8-jdk="$JAVA_DEBIAN_VERSION" \
ca-certificates-java="$CA_CERTIFICATES_JAVA_VERSION" \
&& rm -rf /var/lib/apt/lists/*
# see CA_CERTIFICATES_JAVA_VERSION notes above
RUN /var/lib/dpkg/info/ca-certificates-java.postinst configure
# If you're reading this and have any feedback on how this image could be
# improved, please open an issue or a pull request so we can discuss it!
```
簡單來講,這個Dockerfile使用了安全參數去執行<code>apt-get install -y openjdk-8-jdk</code>去下載安裝Java,而ENV指令配置系統的環境變量。
最後,清單6是Tomcat的[Dockerfile。
代碼清單 6. Tomcat Dockerfile
```sh
FROM java:7-jre
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOME
# see
RUN gpg --keyserver pool.sks-keyservers.net --recv-keys \
05AB33110949707C93A279E3D3EFE6B686867BA6 \
07E48665A34DCAFAE522E5E6266191C37C037D42 \
47309207D818FFD8DCD3F83F1931D684307A10A5 \
541FBE7D8F78B25E055DDEE13C370389288584E7 \
61B832AC2F1C5A90F0F9B00A1C506407564C17A3 \
79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED \
9BA44C2621385CB966EBA586F72C284D731FABEE \
A27677289986DB50844682F8ACB77FC2E86E29AC \
A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 \
DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 \
F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE \
F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23
ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.0.26
ENV TOMCAT_TGZ_URL
RUN set -x \
&& curl -fSL "$TOMCAT_TGZ_URL" -o tomcat.tar.gz \
&& curl -fSL "$TOMCAT_TGZ_URL.asc" -o tomcat.tar.gz.asc \
&& gpg --verify tomcat.tar.gz.asc \
&& tar -xvf tomcat.tar.gz --strip-components=1 \
&& rm bin/*.bat \
&& rm tomcat.tar.gz*
EXPOSE 8080
CMD ["catalina.sh", "run"]
```
嚴格來講,Tomcat使用了Java 7的父級Dockerfile(默認的最新Java版本是8)。這個Dockerfile設置了<code>CATALINA_HOME</code>和<code>PATH</code>環境變量,而後用<code>mkdir</code>命令新建了<code>CATALINA_HOME</code>目錄,<code>WORKDIR</code>指令把當前工做路徑更改成<code>CATALINA_HOME</code>,而後<code>RUN</code>指令執行了同一行中一系列的命令:
1. 下載Tomcat壓縮包。
2. 下載文件校驗碼。
3. 驗證下載的文件正確。
4. 解壓Tomcat壓縮包。
5. 刪除全部批處理文件(咱們是在Linux上運行)。
6. 刪除壓縮包文件。
把這些命令寫在同一行,對應Docker來講就是一條命令,最後Docker會把執行的結果緩存起來,Docker有個策略是檢測鏡像什麼時候須要重建,以及驗證構建過程當中的指令是否正確。當一條指令會使鏡像更改,Docker會把每一個步的結果緩存起來,Docker能把最上一個正確指令產生的鏡像啓動起來。
<code>EXPOSE</code>指令會讓Docker啓動一個容器時暴露指定的端口,正如以前咱們啓動時那樣,咱們須要告訴Docker哪一個物理端口會被映射到容器上(<code>-p</code>參數),<code>EXPOSE</code>的做用就是這個定義Docker容器端口。最後Dockerfile使用catalina.sh腳本啓動Tomcat。
>>>簡單回顧
用Dockerfile從頭開始構建Tomcat是一個漫長的過程,咱們總結一下目前爲止的步驟:
1. 安裝Debian Linux。
2. 安裝curl和wget。
3. 安裝源碼管理工具。
4. 下載並安裝Java。
5. 下載並安裝Tomcat。
6. 暴露Docker實例的8080端口。
7. 用catalina.sh啓動Tomcat。
如今你應該成爲一個Dockerfile專家了,下一步咱們將嘗試構建一個自定義Docker鏡像。
>>>部署自定義應用到Docker
由於本篇指南主要關注點是如何在Docker中部署Java應用,而不是應用自己,我會構建一個簡單的Hello World servlet。你能夠從[GitHub]獲取到這個項目,源碼並沒有任何特別,只是一個輸出"Hello World!"的servlet。更加有趣的是相應的[Dockerfile],如清單7所示。
代碼清單 7. Hello World servlet的Dockerfile
```sh
FROM tomcat
ADD deploy /usr/local/tomcat/webapps
```
可能看起來不大同樣,但你應該能理解以上代碼的做用是:
* <code>FROM tomcat</code>指明這個Dockerfile是基於Tomcat鏡像構建。
* <code>ADD deploy </code>告訴Docker把本地文件系統中的"deploy"目錄,複製到Tomcat鏡像中的 /usr/local/tomcat/webapps路徑 。
在本地使用maven命令編譯這個項目:
```sh
mvn clean install
```
這樣將會生成一個war包,target/helloworld.war,把這個文件複製到項目的docker/deploy目錄(你須要先建立好),最後你要使用上邊的Dockerfile構建Docker鏡像,在項目的docker目錄中執行如下命令:
```sh
docker build -t lygado/docker-tomcat .
```
這個命令讓Docker從當前目錄(用點號.表示)構建一個新的鏡像,並用"<code>-t</code>"打上標籤<code>lygado/docker-tomcat</code>,這個例子中,lygado是個人DockerHub用戶名,docker-image是鏡像名稱(你須要替換成你本身的用戶名)。查看是否構建成功你能夠執行如下命令:
```sh
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
lygado/docker-tomcat latest ccb455fabad9 42 seconds ago 849.5 MB
```
最後,你能夠用如下命令加載這個鏡像:
```sh
docker run -d -p 8080:8080 lygado/docker-tomcat
```
這個實例啓動以後 ,你能夠用如下URL訪問(請把URL中的IP替換成你虛擬機的IP):
```sh
http://192.168.99.100:8080/helloworld/hello
```
仍是那樣,你能夠用容器的ID來終止這個實例。
Docker push
一旦你構建並測試過了你的Docker鏡像,你能夠把這個鏡像推送到你DockerHub的帳號中:
```sh
docker push lygado/docker-tomcat
```
這樣,你的鏡像就能被全世界訪問到了,固然,爲了隱私起見,你也能夠推送到私有的Docker倉庫。
下面,咱們將把Docker集成到應用的構建過程,目標是在構建應用完成後,會產出一個包含應用的Docker鏡像。
>>>把Docker集成到Maven構建過程
在前邊的部分,咱們建立了一個自定義的Dockerfile,並把WAR包部署到它裏邊。這樣意味着把WAR包從項目的target目錄,複製到<code>docker/deploy</code>目錄下,而且從命令行中運行docker。這並沒花多少功夫,但若是你須要頻繁的改動並測試代碼,你會發現這個過程很煩瑣。並且,若是你須要在一個CI服務器上構建應用,併產出一個Docker鏡像,那你須要弄明白怎樣把Docker和CI工具整合起來。
如今咱們嘗試一種更有效的方法,使用Maven和Maven Docker插件來構建一個Docker鏡像。
個人用例有這些:
1. 能建立基於Tomcat的Docker鏡像,以用於部署個人應用。
2. 能在測試中自行構建。
3. 能整合到前期集成測試和後期集成測試。
docker-maven-plugin能知足這些需求,並且易於使用和理解。
關於 Maven Docker插件
這個插件自己有良好的[文檔],這裏特別說明一下兩個主要的組件:
1. 在POM.xml中配置Docker鏡像的構建和運行。
2. 描述哪些文件要包含在鏡像中。
清單8是POM.xml中插件的配置,定義了鏡像的構建和運行的配置。
代碼清單 8. POM 文件的 build 小節, Docker Maven plug-in 配置
```xml
<build>
<finalName>helloworld</finalName>
<plugins>
<plugin>
<groupId>org.jolokia</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.13.4</version>
<configuration>
<dockerHost>tcp://192.168.99.100:2376</dockerHost> <certPath>/Users/shaines/.docker/machine/machines/default</certPath>
<useColor>true</useColor>
<images>
<image>
<name>lygado/tomcat-with-my-app:0.1</name>
<alias>tomcat</alias>
<build>
<from>tomcat</from>
<assembly>
<mode>dir</mode
<basedir>/usr/local/tomcat/webapps</basedir
<descriptor>assembly.xml</descriptor>
</assembly>
</build>
<run>
<ports>
<port>8080:8080</port>
</ports>
</run>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
```
正如你所見,這個配置至關簡單,包含了如下元素:
Plug-in定義
<code>groupId</code>, <code>artifactId</code> 和 <code>version</code> 這些信息指定要用哪一個插件。
全局設置
<code>dockerHost</code>和<code>certPath</code>元素,定義了Docker主機的位置,這些配置會用於啓動容器,以及指定Docker證書。Docker證書的路徑在<code>DOCKER_CERT_PATH</code>環境變量中能看到。
鏡像設置
在<code>build</code>元素下的全部<code>image</code>元素都定義在<code>images</code>元素下,每一個<code>image</code>元素都有鏡像相關的配置,與<code>build</code>和<code>run</code>的配置同樣,主要的配置是鏡像的名稱,在這個例子中,是個人DockerHub用戶名(<code>lygado</code>),鏡像的名稱(<code>tomcat-with-my-app</code>)和鏡像的版本號(0.1)。你也能夠用Maven的屬性來定義這些值。
鏡像構建配置
通常構建鏡像時,咱們會使用<code>docker build</code>命令,以及一個Dockerfile來定義構建過程。Maven Docker插件也容許你使用Dockerfile,但在例子中,咱們使用一個運行時生成在內存中的Dockerfile來構建。所以,咱們在<code>from</code>元素中定義父級鏡像,這個例子中是tomcat,而後在<code>assembly</code>中做其餘配置。
使用Maven的<code>maven-assembly-plugin</code>,能夠定義一個項目的輸出內容,指定包含依賴,模塊,文檔,或其餘文件到一個獨立分發的包中。<code>docker-maven-plugin</code>繼承了這個標準,在這個例子中,咱們選擇了<code>dir</code>模式,也就是說定義在<code>src/main/docker/assembly.xml</code>中的文件會被拷貝到Docker鏡像中的basedir中。其餘模式還有<code>tar</code>,<code>tgz</code>和<code>zip</code>。<code>basedir</code>元素中定義了放置文件的路徑,這個例子中是Tomcat的webapps目錄。
最後,<code>descriptor</code>元素指定了<code>assembly</code>文件,這個文件位於<code>basedir</code>中定義的<code>src/main/docker</code>中。以上是一個很簡單的例子,我建議你通讀一下相關文檔,特別地,能夠了解<code>entrypoint</code>和<code>cmd</code>元素,這兩個元素能夠指定啓動Docker鏡像的命令,<code>env</code>元素能夠指定環境變量,<code>runCmds</code>元素相似Dockerfile中的<code>RUN</code>指令,<code>workdir</code>元素能夠指定工做路徑,<code>volumes</code>元素能夠指定要掛載的磁盤卷。簡言之,這個插件實現了全部Dockerfile中所須要的語法,因此前面所用到的Dockerfile指令均可以在這個插件中使用。
鏡像運行配置
啓動Docker鏡像時會用到<code>docker run</code>命令,你能夠傳一些參數給Docker。這個例子中,咱們要用<code>docker run -d -p 8080:8080 lygado/tomcat-with-my-app:0.1</code>這個命令啓動鏡像,因此咱們只須要指定一下端口映射。
run元素可讓咱們指定全部運行時參數,因此咱們指定了把Docker容器中的8080映射到Docker宿主機的8080。另外,還能夠在run這節中指定要掛載的卷(使用<code>volumes</code>),或者要連接起來的容器(使用<code>links</code>)。<code>docker:start</code>在集成測試中很經常使用,在run小節中,咱們可使用wait參數來指定一個時間週期,這樣就能夠等待到某個日誌輸出,或者一個URL可用時,才繼續執行下去,這樣就能夠保證在集成測試開始前鏡像已經運行起來了。
加載依賴
<code>src/main/docker/assembly.xml</code>文件定義了哪些文件須要複製到Docker鏡像中,如清單9所示:
清單 9. assembly.xml
```xml
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<dependencySets>
<dependencySet>
<includes>
<include>com.geekcap.vmturbo:hello-world-servlet-example</include>
</includes>
<outputDirectory>.</outputDirectory>
<outputFileNameMapping>helloworld.war</outputFileNameMapping>
</dependencySet>
</dependencySets>
</assembly>
```
在清單 9 中,咱們能夠看到包含<code>hello-world-servlet-example</code>在內的一個依賴集合,以及複製的目標路徑,<code>outputDirectory</code>這個路徑是相對於前面提到的<code>basedir</code>的,也就是Tomcat的webapps目錄。
這個插件有如下幾個Maven targets:
1. docker:build: 構建鏡像
2. docker:start: 啓動鏡像
3. docker:stop: 中止鏡像
4. docker:push: 把鏡像推送到鏡像倉庫,如DockerHub
5. docker:remove: 本地刪除鏡像
6. docker:logs: 輸出容器日誌
構建鏡像
你能夠從[GitHub]中獲取源碼,而後用下邊的命令構建:
```sh
mvn clean install
```
用如下命令構建Docker鏡像:
```sh
mvn clean package docker:build
```
一旦鏡像構建成功,你能夠在<code>docker images</code>的返回結果中看到:
```sh
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
lygado/tomcat-with-my-app 0.1 1d49e6924d19 16 minutes ago 347.7 MB
```
能夠用如下命令啓動鏡像:
```sh
mvn docker:start
```
如今能夠在<code>docker ps</code>中看到已經啓動了,而後能夠經過如下URL訪問:
```sh
http://192.168.99.100:8080/helloworld/hello
```
最後,能夠用如下命令中止容器:
```sh
mvn docker:stop
```
>>>總結
Docker是一種使進程虛擬化的容器技術,它提供了一系列Docker客戶端命令來調用Docker守護進程。在Linux上,Docker守護進程能夠直接運行於Linux操做系統,可是在Windows和Mac上,須要有一個Linux虛擬機來運行Docker守護進程。Docker鏡像包含了一個輕量級的操做系統,還額外包含了應用運行的依賴庫。Docker鏡像由Dockerfile定義,能夠在Dockerfile中包含一系列配置鏡像的指令。
在這個開源Java項目指南中,我介紹了Docker的基礎,講解了CentOS、Java、Tomcat等鏡像的Dockerfile細節,並演示瞭如何用Tomcat鏡像來構建新的鏡像。最後,咱們使用docker-maven-plugin來把Docker集成到Maven的構建過程當中。經過這樣,使得測試更加簡單了,還能夠把構建過程配置在CI服務器上部署到生產環境。
本文中的示例應用很是簡單,可是涉及的構建步驟一樣能夠用在更復雜的企業級應用中。好好享受Docker帶給咱們的樂趣吧。
感謝您閱讀此文!本週四咱們將繼續分享docker技術文章,請保持關注!
如瞭解更多docker相關知識,請觀看培訓視頻:https://csphere.cn/training!
如須要docker相關產品,請訪問希雲官網首頁:https://csphere.cn!