天天看點

Docker筆記(五):整一個自己的鏡像

原文位址:http://blog.jboost.cn/2019/07/17/docerk-5.html

擷取鏡像的途徑有兩個,一是從鏡像倉庫擷取,如官方的Docker Hub,二是自定義。上文已經介紹如何從鏡像倉庫擷取鏡像,本文基于一個Springboot項目,來介紹自定義一個鏡像的基本流程。

1. 定制鏡像的本質

我們知道鏡像是分層存儲的,鏡像的建構也是一層一層進行的,一層建構完後,就變為隻讀,在其上再建構下一層。是以定制鏡像,實際上就是定義每一層要幹的事,比如執行某個指令,設定一個環境變量,聲明一個暴露端口等等。然後在建構時,按照各層的定義,一層一層地完成建構,最終形成一個包含這些層的鏡像。

2. Dockerfile檔案

Docker中定義各層要幹的事的檔案叫Dockerfile,它是一個文本檔案,包含了一條條的指令,每一條指令對應一層鏡像,指令的内容就描述了這一層該如何建構。如下示例了一個非常簡單的Dockerfile,

FROM nginx
RUN echo '<h1>Hello jboost!</h1>' > /usr/share/nginx/html/index.html      

我們定制鏡像,必須要以某一個鏡像為基礎,在其上建構自己需要的層,如上示例中,我們是以nginx鏡像為基礎,然後在第二層定制了我們自己的内容——修改index.html的内容為

<h1>Hello jboost!</h1>

,這樣運作容器打開nginx首頁時就不會顯示預設的頁面内容了。 

上面示例中接觸了Dockerfile的兩個指令

  • FROM:FROM指令指定基礎鏡像,每一個定制鏡像必須要有一個基礎鏡像,是以必須要有一條FROM指令,并且是Dockerfile的第一條指令
  • RUN:RUN指令指定需要執行的指令,後面接的指令就像是shell腳本一樣可執行

Dockerfile還提供了許多其它指令,後續我們再集中介紹,本文隻對接觸到的指令做簡單說明。

3. 自定義一個鏡像

這部分以一個Springboot項目為基礎,介紹自定義一個鏡像涉及的基本環節。項目位址為:https://github.com/ronwxy/swagger-register ,該項目是一個Swagger API文檔注冊服務,其它項目可将Swagger API資訊注冊到該服務,進行統一檢視與管理。

3.1 定義Dockerfile檔案

首先,我們在項目的根目錄下建立一個Dockerfile檔案(檔案名就叫Dockerfile),其内容為:

FROM openjdk:8-jdk-alpine
ENV PROFILE=dev
RUN mkdir /app /logs
COPY ./target/swagger-register-1.0.0-SNAPSHOT.jar /app/app.jar
WORKDIR /app
VOLUME /register-data
EXPOSE 11090
CMD ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "app.jar"]      

從上往下依次介紹如下 

  • 第一行:FORM openjdk:8-jdk-alpine, 表示以

    openjdk:8-jdk-alpine

    這個鏡像為基礎鏡像,因為這是一個Springboot項目是以必須要有jdk支援,我們在定制鏡像時,可以找一個最适合的鏡像作為基礎鏡像。
  • 第二行:ENV PROFILE=dev, 定義了一個環境變量,這個環境變量可以在後面被引用
  • 第三行:RUN mkdir /app /logs,通過mkdir指令建立了兩個目錄,用來儲存jar執行檔案及日志
  • 第四行:COPY ./target/swagger-register-1.0.0-SNAPSHOT.jar /app/app.jar 将target目錄下的jar包複制到/app目錄下,并且進行重命名
  • 第五行:WORKDIR /app, 指定工作目錄為/app,後面各層的目前目錄就是指定的工作目錄
  • 第六行:VOLUME /register-data, 定義一個匿名資料卷,前面說過寫操作不要直接在容器内進行,而要改為寫挂載的資料卷目錄,這個定義可在運作容器時通過 -v 來覆寫。
  • 第七行:EXPOSE 11090, 聲明了運作容器時提供的服務端口,也僅僅是個聲明而已,隻是告訴使用的人要映射這個端口,通過 -p 可映射端口。
  • 第八行:CMD [“java”, “-Dspring.profiles.active=${PROFILE}”, “-jar”, “app.jar”], 指定了容器啟動指令,因為是一個Springboot項目,是以就是一個java -jar的執行指令,容器啟動的時候就會執行該指令來運作Springboot服務,這裡引用了第二行定義的環境變量PROFILE

3.2 配置maven插件

定義好Dockerfile後,為了友善建構鏡像,我們可以借助maven的dockerfile插件

dockerfile-maven-plugin

,在pom.xml的build部分加入配置如下

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- Docker maven plugin -->
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>dockerfile-maven-plugin</artifactId>
            <version>1.4.10</version>
            <configuration>
                <repository>${docker.image.prefix}/${project.artifactId}</repository>
                <buildArgs>
                    <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                </buildArgs>
            </configuration>
        </plugin>
        <!-- Docker maven plugin -->
    </plugins>
</build>      

repository指定了鏡像的名稱,

docker.image.prefix

需要properties部分進行定義,我這裡是

springboot

3.3 建構鏡像

下載下傳源碼:https://github.com/ronwxy/swagger-register.git ,然後在項目的根目錄下執行如下指令(前提是本地已經裝好了docker與maven及jdk)

mvn clean package -Dmaven.test.skip=true dockerfile:build      

該指令首先會執行

mvn clean package -Dmaven.test.skip=true

對項目進行打包,生成./target/swagger-register-1.0.0-SNAPSHOT.jar檔案,然後基于目前目錄下的Dockerfile檔案進行建構,如下圖所示 

由上圖可看出,該鏡像建構分八步(對應Dockerfile的八行指令),每一步生成一個鏡像層,每一層都有唯一的ID。由圖中也可以看出,除了COPY之類的指令外,每一層的建構實際上是先基于上一層啟動一個容器,然後執行該層定義的操作,再移除這個容器來實作的,如第八步中

Step 8/8 : CMD ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "app.jar"]
[INFO] 
[INFO]  ---> Running in f4acd0b53bca
[INFO] Removing intermediate container f4acd0b53bca
[INFO]  ---> a9ee579f2d62      

先啟動一個ID為f4acd0b53bca的容器,在其中執行CMD所定義的指令,然後再移除容器f4acd0b53bca,最後生成ID為a9ee579f2d62的鏡像。 

建構完後,我們就可以在本地鏡像中通過

docker iamges

看到我們定制的鏡像了,如圖

圖中springboot/swagger-register鏡像即為我們剛剛建構好的定制鏡像。

3.4 啟動容器

我們可以通過以下指令來啟動一個剛才定制鏡像的容器

docker run -d --name swagger-register -p 11090:11090 -v /home/jenkins/swagger-register/register-data:/register-data -v /home/jenkins/swagger-register/logs:/logs --restart=always springboot/swagger-register:latest      

其中: 

  • -d 表示以背景程序方式運作
  • –name 指定容器名稱
  • -p 指定端口映射,左邊為主控端端口,右邊為容器服務端口
  • -v 指定資料卷挂載,左邊為主控端目錄,右邊為容器目錄
  • –restart=always 表示在docker啟動時自動啟動該容器

關于容器相關的内容後面詳細介紹,這裡不展開說明了。啟動容器後, 我們就可以浏覽器打開位址 http://主控端ip:11090/doc.html 來通路服務了(打開頁面後内容是空的,因為沒有任何服務注冊Swagger API, 相關内容可參考 swagger api文檔集中化注冊管理)

4. 總結

本文介紹了一個基于Springboot項目的Docker鏡像定制及使用過程,對鏡像的建構過程,及Dockerfile的基本指令以及容器的運作做了基本介紹。後續會對Dockerfile的其它指令及Dockerfile的一些最佳實踐進行更為詳細的介紹,歡迎關注。

我的個人部落格位址:http://blog.jboost.cn

我的微信公衆号:jboost-ksxy (一個不隻有技術幹貨的公衆号,歡迎關注,及時擷取更新内容)

—————————————————————————————————————————————————————