用Docker重新定義Java虛擬化部署實戰案例

jopen 8年前發布 | 27K 次閱讀 Docker

 

上周希云和大家分享了《用Docker重新定義Java虛擬化部署(基礎篇)》,估計有些小伙伴早已按耐不住著急的心情了吧。今天希云就和大家分享在docker里部署java應用的實戰案例。

Dockerfiles

Dockerfile包含了一系列指令,告訴Docker如何去構建一個鏡像,它指定了鏡像的基點,以及配置鏡像的每個細節。以下是一個Dockerfile示例,是CentOS鏡像的Dockerfile。

代碼清單1. CentOS Dockerfile

sh
FROM scratch
MAINTAINER The CentOS Project <cloud-ops@centos.org> - 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"]

大部分內容是注釋,主要有四句命令:

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說明了這個文件的架構。

用Docker重新定義Java虛擬化部署實戰案例

這個架構未必如你想象中那么簡單,但我們接下來會慢慢學習它,其實它是非常有邏輯的。上邊已經提過所有Dockerfile的根 是<code>scratch</code>,接下來指定的是<code>debian:jessie< /code>鏡像,這個官方鏡像是基于標準鏡像構建的,Docker不需要重復發明輪子,每次都創建一個新鏡像了,只要基于一個穩定的鏡像來繼續構 建新鏡像即可,在這個例子中,<code>debian:jessie</code>是一個官方Debian Linux鏡像,就像上邊的CentOS一樣,它只有三行指令。

代碼清單 2. debian:jessie Dockerfile

sh
FROM scratch
ADD rootfs.tar.xz /
CMD [&quot;/bin/bash&quot;]

在上圖中我們還見到有安裝兩個額外的鏡像,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 &amp;&amp; apt-get install -y --no-install-recommends \
ca-certificates \
curl \
wget \
&amp;&amp; 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 &amp;&amp; apt-get install -y --no-install-recommends \
bzr \
git \
mercurial \
openssh-client \
subversion \
&amp;&amp; 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 &amp;&amp; apt-get install -y unzip &amp;&amp; rm -rf /var/lib/apt/lists/*

RUN echo 'deb http://httpredir.debian.org/debian 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 https://bugs.debian.org/775775

and https://github.com/docker-libr ... 46872

ENV CA_CERTIFICATES_JAVA_VERSION 20140324

RUN set -x \

&amp;&amp; apt-get update \

&amp;&amp; apt-get install -y \

openjdk-8-jdk=&quot;$JAVA_DEBIAN_VERSION&quot; \

ca-certificates-java=&quot;$CA_CERTIFICATES_JAVA_VERSION&quot; \

&amp;&amp; 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 &quot;$CATALINA_HOME&quot;
WORKDIR $CATALINA_HOME

see https://www.apache.org/dist/tomcat/tomcat-8/KEYS

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 https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz

RUN set -x \

&amp;&amp; curl -fSL &quot;$TOMCAT_TGZ_URL&quot; -o tomcat.tar.gz \

&amp;&amp; curl -fSL &quot;$TOMCAT_TGZ_URL.asc&quot; -o tomcat.tar.gz.asc \

&amp;&amp; gpg --verify tomcat.tar.gz.asc \

&amp;&amp; tar -xvf tomcat.tar.gz --strip-components=1 \

&amp;&amp; rm bin/*.bat \

&amp;&amp; rm tomcat.tar.gz*

EXPOSE 8080

CMD [&quot;catalina.sh&quot;, &quot;run&quot;]

嚴格來說,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. 刪除壓縮包文件。
  7. </ol>

    把這些命令寫在同一行,對應Docker來說就是一條命令,最后Docker會把執行的結果緩存起來,Docker有個策略是檢測鏡像何時需要重 建,以及驗證構建過程中的指令是否正確。當一條指令會使鏡像更改,Docker會把每個步的結果緩存起來,Docker能把最上一個正確指令產生的鏡像啟 動起來。

    <code>EXPOSE</code>指令會讓Docker啟動一個容器時暴露指定的端口,正如之前我們啟動時那樣, 我們需要告訴Docker哪個物理端口會被映射到容器上(<code>-p</code>參 數),<code>EXPOSE</code>的作用就是這個定義Docker容器端口。最后Dockerfile使用 catalina.sh腳本啟動Tomcat。

    用Docker重新定義Java虛擬化部署實戰案例

    簡單回顧

    用Dockerfile從頭開始構建Tomcat是一個漫長的過程,我們總結一下目前為止的步驟:

    1. 安裝Debian Linux。
    2. 安裝curl和wget。
    3. 安裝源碼管理工具。
    4. 下載并安裝Java。
    5. 下載并安裝Tomcat。
    6. 暴露Docker實例的8080端口。
    7. 用catalina.sh啟動Tomcat。
    8. </ol>

      現在你應該成為一個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. 描述哪些文件要包含在鏡像中。
      3. </ol>

        清單8是POM.xml中插件的配置,定義了鏡像的構建和運行的配置。

        代碼清單 8. POM 文件的 build 小節, Docker Maven plug-in 配置

        xml
        &lt;build>
        &lt;finalName>helloworld&lt;/finalName>
        &lt;plugins>
        &lt;plugin>
        &lt;groupId>org.jolokia&lt;/groupId>
        &lt;artifactId>docker-maven-plugin&lt;/artifactId>
        &lt;version>0.13.4&lt;/version>
        &lt;configuration>
        &lt;dockerHost>tcp://192.168.99.100:2376&lt;/dockerHost> &lt;certPath>/Users/shaines/.docker/machine/machines/default&lt;/certPath>
        &lt;useColor>true&lt;/useColor>
        &lt;images>
        &lt;image>
        &lt;name>lygado/tomcat-with-my-app:0.1&lt;/name>
        &lt;alias>tomcat&lt;/alias>
        &lt;build>
        &lt;from>tomcat&lt;/from>
        &lt;assembly>
        &lt;mode>dir&lt;/mode
        &lt;basedir>/usr/local/tomcat/webapps&lt;/basedir
        &lt;descriptor>assembly.xml&lt;/descriptor>
        &lt;/assembly>
        &lt;/build>
        &lt;run>
        &lt;ports>
        &lt;port>8080:8080&lt;/port>
        &lt;/ports>
        &lt;/run>
        &lt;/image>
        &lt;/images>
        &lt;/configuration>
        &lt;/plugin>
        &lt;/plugins>
        &lt;/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
        &lt;assembly xmlns=&quot;http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2&quot;
        xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
        xsi:schemaLocation=&quot;http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd&quot;&gt;
        &lt;dependencySets>
        &lt;dependencySet>
        &lt;includes>
        &lt;include>com.geekcap.vmturbo:hello-world-servlet-example&lt;/include>
        &lt;/includes>
        &lt;outputDirectory>.&lt;/outputDirectory>
        &lt;outputFileNameMapping>helloworld.war&lt;/outputFileNameMapping>
        &lt;/dependencySet>
        &lt;/dependencySets>
        &lt;/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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!