使用Dockerfile構建自己的鏡像


Dockerfile是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。使用docker build命令可以根據Dockerfile里面的指令編排來打包定制我們自己的docker鏡像,首先我們來看一個通用的例子,制作自己的nginx鏡像。

1.構建nginx鏡像

# Base image
FROM centos:7

# MAINTAINER
MAINTAINER cbmiao <miaocbin@126.com>

# 將nginx以及pcre源代碼加入鏡像
ADD nginx-1.20.1.tar.gz /usr/local/src/
ADD pcre-8.45.tar.gz /usr/local/src/

# 安裝編譯器
RUN yum install -y gcc gcc-c++ make openssl-devel lsof
RUN useradd -s /sbin/nologin -M nginx

# 指定工作目錄
WORKDIR /usr/local/src/nginx-1.20.1/

# 編譯nginx
RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf

# 設置環境變量
ENV PATH /usr/local/nginx/sbin:$PATH

# 暴露80端口
EXPOSE 80

# 容器默認啟動命令
ENTRYPOINT ["nginx"]

以上是通過Dockerfile來定制我們自己的nginx的Dockerfile,包含了如下指令:

2. Dockerfile基本格式

2.1 FROM指令

FROM指令用於指定基礎鏡像,必須為Dockerfile的第一個指令

# 格式:
# FROM <image>
# FROM <image>:<tag>
# 示例:
FROM mysql:5.7
# 注意:
# tag是可選的,如果不使用tag,則默認會使用latest版本的基礎鏡像

參數解釋:

  • FROM mysql:5.7:第一行必須指定 基礎鏡像信息

2.2 MAINTAINER 指令

用於說明鏡像維護者的信息,名字,郵箱,聯系方式,實例如下

# 格式:
# 	MAINTAINER <name>
# 示例:
	MAINTAINER Michael Miu
    MAINTAINER miaocbin@126.com
    MAINTAINER Michael Miu <miaocbin@126.com>

2.3 COPY|ADD指令

這兩個命令的用法類似,都可以用於添加本地文件到鏡像中(同樣需求下,官方推薦使用 COPY),功能也類似,如:

  • 源路徑可以有多個,相對於執行build的路徑,如果源路徑是一個目錄,則該目錄下面的所有內容都會被加入到容器,但目錄本身不會被加入到容器;
  • 目標路徑:必須是絕對路徑或者相對於WORKDIR的相對路徑;若目標路徑不存在,則會自動創建完整路徑;目標路徑如果是文件夾,則必須已/結尾;
  • 路徑中可以使用通配符

二者區別如下:

  • ADD 的優點:在執行 <源文件> 為 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動復制並解壓到 <目標路徑>。
  • ADD 的缺點:在不解壓的前提下,無法復制 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,可以根據是否需要自動解壓來決定。
  • COPY:指令能夠將構建命令所在的主機本地的文件或目錄,復制到鏡像文件系統。
  • ADD:指令不僅能夠將構建命令所在的主機本地的文件或目錄,而且能夠將遠程URL所對應的文件或目錄,作為資源復制到鏡像文件系統。
    所以,可以認為ADD是增強版的COPY,支持將遠程URL的資源加入到鏡像的文件系統。
# 格式:
# 	ADD|COPY <src>... <dest>
# 示例:
    ADD|COPY tes* /mydir/    # 添加所有以"tes"開頭的文件
    ADD|COPY test mydir/     # 添加 "test" 到 WORKDIR/mydir/
    ADD|COPY test /opt/      # 添加 "test" 到 /opt/
# [--chown=<user>:<group>]:可選參數,用戶改變復制到容器內文件的擁有者和屬組。

說明:

  1. 對於從遠程URL獲取資源的情況,由於ADD指令不支持認證,如果從遠程獲取資源需要認證,則只能使用RUN wget或RUN curl替代。
  2. 如果源路徑的資源發生變化,則該ADD指令將使Docker Cache失效,Dockerfile中后續的所有指令都不能使用緩存。因此盡量將ADD指令放在Dockerfile的后面。
  3. <源路徑>:源文件或者源目錄,這里可以是通配符表達式,其通配符規則要滿足 Go 的 filepath.Match 規則。
  4. <目標路徑>:容器內的指定路徑,該路徑不用事先建好,路徑不存在的話,會自動創建。

2.4 WORKDIR

指定工作目錄,通過WORKDIR設置工作目錄后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行。(WORKDIR 指定的工作目錄,必須是提前創建好的)

# 格式:
# 	WORKDIR /path/to/workdir
# 示例:
    WORKDIR /abc  (這時工作目錄為/abc)

2.5 RUN

構建鏡像過程中執行命令(注意:RUN指令創建的中間鏡像會被緩存,並會在下次構建中使用。如果不想使用這些緩存鏡像,可以在構建時指定--no-cache參數,如:docker build --no-cache)

# 格式:
# 	RUN <command>
# 示例:
    RUN yum -y install nginx
    RUN pip install djaong
    RUN mkdir test && rm -rf /var/lib/testfile

2.6 CMD

類似於RUN指令,用於運行程序,但二者運行的時間點不同:

  • CMD:用於指定在容器啟動時所要執行的命令,在docker run 時運行。
  • RUN:RUN用於指定鏡像構建時所要執行的命令,是在 docker build時運行。

作用:為啟動的容器指定默認要運行的程序,也就是在容器啟動時才進行調用。程序運行結束,容器也就結束。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。

# 格式:
#     CMD ["executable","param1","param2"] (執行可執行文件,優先)
#     CMD ["param1","param2"] (設置了ENTRYPOINT,則直接調用ENTRYPOINT添加參數)
#     CMD command param1 param2 (執行shell內部命令)
# 示例:
    CMD ["/usr/bin/wc","--help"]
    CMD ping www.baidu.com

注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最后一個生效。

2.7 ENTRYPOINT

類似於 CMD 指令,但其不會被 docker run 的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT 指令指定的程序。但是, 如果運行 docker run 時使用了 --entrypoint 選項,將覆蓋 CMD 指令指定的程序。

# 格式:
#     ENTRYPOINT ["executable", "param1", "param2"] (可執行文件, 優先)
#     ENTRYPOINT command param1 param2 (shell內部命令)
# 示例:
    ENTRYPOINT ["/usr/bin/wc","--help"]

注意:ENTRYPOINT與CMD非常類似,不同的是通過docker run執行的命令不會覆蓋ENTRYPOINT,而docker run命令中指定的任何參數,都會被當做參數再次傳遞給ENTRYPOINT。

優點:在執行 docker run 的時候可以指定 ENTRYPOINT 運行所需的參數。

注意:Dockerfile中只允許有一個ENTRYPOINT命令,多指定時會覆蓋前面的設置,而只執行最后的ENTRYPOINT指令。

2.8 ENV

設置環境變量,定義了環境變量,那么在后續的指令中,就可以使用這個環境變量。

# 格式:
#     ENV <key> <value>
#     ENV <key>=<value>
# 示例:
    ENV myName John
    ENV myCat=tomcat

2.9 EXPOSE指令

聲明端口,作用如下:

  • 1)幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;
  • 2)在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口;但是,EXPOSE並不會讓容器的端口訪問到主機。要使其可訪問,需要在docker run運行容器時通過-p來發布這些端口,或通過-P參數來發布EXPOSE導出的所有端口。
  • 3)docker ps的時候看到的PORTS字段信息就是這里聲明的信息;
  • 4)需要說明的是,即便這里不寫EXPOSE指令,也不影響鏡像構建和運行,只是會導致其他的使用者不清楚鏡像監聽的端口,使用的時候映射端口沒有那么直觀。
# 格式:
#     EXPOSE <port> [<port>...]
# 示例:
    EXPOSE 80 443
    EXPOSE 8080
    EXPOSE 11211/tcp 11211/udp

2.10 LABEL指令

用來給鏡像添加一些元數據(metadata),以鍵值對的形式

# 語法格式:
# LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 例如:添加鏡像作者
LABEL org.opencontainers.image.authors="runoob"

2.11 VOLUME指令

定義匿名數據卷。在啟動容器時忘記掛載數據卷,會自動掛載到匿名卷。作用:

  • 避免重要的數據,因容器重啟而丟失,這是非常致命的。
  • 避免容器不斷變大。
# 格式:
# 	VOLUME ["<路徑1>", "<路徑2>"...]
# 	VOLUME <路徑>
# 舉例:
VOLUME [/var/lib/mysql]

3. Dockerfile鏡像構建命令

利用Dockerfile構建自己的鏡像命令

docker build . -t ImageName:ImageTag -f Dockerfile
  • . 是上下文路徑
  • 上下文路徑,是指 docker 在構建鏡像,有時候想要使用到本機的文件(比如復制),docker build 命令得知這個路徑后,會將路徑下的所有內容打包。

解析:由於 docker 的運行模式是 C/S。我們本機是 C,docker 引擎是 S。實際的構建過程是在 docker 引擎下完成的,所以這個時候無法用到我們本機的文件。這就需要把我們本機的指定目錄下的文件一起打包提供給 docker 引擎使用。

如果未說明最后一個參數,那么默認上下文路徑就是 Dockerfile 所在的位置。

注意:上下文路徑下不要放無用的文件,因為會一起打包發送給 docker 引擎,如果文件過多會造成過程緩慢。

3.1 執行構建過程

docker build . -t my-nginx:v1.20.1 -f Dockerfile

Sending build context to Docker daemon  3.163MB
Step 1/13 : FROM centos:7
 ---> 8652b9f0cb4c
Step 2/13 : LABEL maintainer="cbmiao <miaocbin@126.com>"
 ---> Using cache
 ---> 16c2487146bc
Step 3/13 : ENV DEBIAN_FRONTEND noninteractive
 ---> Using cache
 ---> 5f3132177398
Step 4/13 : ADD nginx-1.20.1.tar.gz /usr/local/src/
 ---> Using cache
 ---> 116344a28ea2
Step 5/13 : ADD pcre-8.45.tar.gz /usr/local/src
 ---> Using cache
 ---> 23451b87c29b
Step 6/13 : RUN yum install -y wget gcc gcc-c++ make openssl-devel
 ---> Using cache
 ---> dcaa1cbaf4bd
Step 7/13 : RUN useradd -s /sbin/nologin -M nginx
 ---> Using cache
 ---> e0ace7f20ffc
Step 8/13 : WORKDIR /usr/local/src/nginx-1.20.1/
 ---> Using cache
 ---> d437722febe2
Step 9/13 : RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
 ---> Using cache
 ---> 307b6228d110
Step 10/13 : RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
 ---> Using cache
 ---> dc8e0e800987
Step 11/13 : ENV PATH /usr/local/nginx/sbin:$PATH
 ---> Using cache
 ---> e5a1119dced2
Step 12/13 : EXPOSE 80
 ---> Using cache
 ---> 63c669be20e4
Step 13/13 : ENTRYPOINT ["nginx"]
 ---> Using cache
 ---> 391f75b825bd
Successfully built 391f75b825bd
Successfully tagged my-nginx:v1.20.1

3.2 查看構建結果

docker image ls
REPOSITORY                TAG       IMAGE ID       CREATED          SIZE
my-nginx                  v1.20.1   391f75b825bd   17 minutes ago   490MB

3.3 運行鏡像

# 利用我們剛構建的鏡像來啟動容器
docker run -d --name nginx-test my-nginx:v1.20.1
348a73f420181c36abf7a99c958ed76862503e930793331ab819629ea2bdc4a2

# 進入容器查看nginx狀態
docker exec -ti nginx-test bash
[root@348a73f42018 nginx-1.20.1]#

# 確認80端口
[root@348a73f42018 nginx-1.20.1]# lsof -i :80
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx     1 root    6u  IPv4 645923      0t0  TCP *:http (LISTEN)
# 確認nginx進程已經啟動成功
[root@348a73f42018 nginx-1.20.1]# ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.1  0.0  46096  3280 ?        Ss   05:43   0:00 nginx: master process nginx
nginx         7  0.0  0.0  46532  1880 ?        S    05:43   0:00 nginx: worker process
root          8  0.2  0.0  11828  1908 pts/0    Ss   05:43   0:00 bash
root         26  0.0  0.0  51732  1712 pts/0    R+   05:43   0:00 ps aux
# 訪問nginx也正常
[root@348a73f42018 nginx-1.20.1]# curl localhost 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

若想讓我們的nginx向外提供服務,還需要在啟動容器時,將端口映射到主機,然后我們通過主機IP加上映射端口,就可以訪問了

docker run -d --name nginx-test1 -p 80:80 my-nginx:v1.20.1

以上就添加了一個[-p 80:80]參數,將容器內的nginx的80端口,映射到宿主機。然后我們就可以通過宿主機的IP加上80端口訪問剛才啟動的nginx容器里的nginx服務,有關docker啟動容器有不清楚的,可以看我的另外一篇博客:Docker簡介及基本使用

4. Dockerfile構建鏡像的原則

  • 不必要的內容不要放在鏡像中:以免增大鏡像,同時也帶來安全隱患;

  • 減少不必要的層文件

  • 減少網絡傳輸操作:盡量使用本地文件,加快鏡像構建速度,特別是后面我們將利用Jenkins做自動化構建的時候;

  • 可以適當的包含一些調試命令

5. JAR包制作成docker鏡像實例

FROM java:8u211

ENV JAVA_OPTS "\
-Xmx4096m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m"
ENV JAVA_HOME /usr/local/java
ENV PATH ${PATH}:${JAVA_HOME}/bin

COPY target/myweb.jar myweb.jar

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

EXPOSE 8080
CMD java ${JAVA_OPTS} -jar myweb.jar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM