docker---Dockerfile編寫


  前言:鏡像的定制實際上就是定制每一層所添加的配置文件,如果我們可以把每一層的修改、安裝、構建、操作的命令都寫入一個腳本,然后用這個腳本來構建、定制鏡像,那么鏡像構建透明性的問題、體積的問題就會得到解決,這個腳本就是 Dockerfile; Dockerfile 是一個文本文件,其內包含了一條條的指令,每一條指令構建一層,每一層指令的內容,就是描述該層應該如何構建,然后通過 commit 構成新的鏡像。

 

Dockerfile 參數

FROM

1,FROM:指定基礎鏡像,必須是第一條指令

# 定制 nginx 鏡像的 Dockerfile

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

注: Docker Hub 上有很多高質量的服務類的官方鏡像可以拿來直接使用,比如:nginx 、redis 、mysql 、php 、mongo \ tomcat 等,可以在其中找最符合的一個進行定制

        另外也有一些方便開發、構建、運行各種語言的鏡像,比如:node 、python 、golang 等

        如果沒有找到對應服務的鏡像,官方鏡像中還提供了一些更為基礎的操作系統鏡像,比如:ubuntu 、debian 、fedora 、centos 等,也可以利用這些操作系統提供的軟件庫

RUN

2,RUN :用來執行命令行命令,格式有兩種:

     1, shell 格式: RUN <命令>,就像直接在命令行中輸入的命令一樣

     2, exec 格式:RUN ["可執行文件",“參數1”,“參數2”],更像是函數調用中的格式

warning:每一個RUN命令都會在 docker鏡像中新建一層,所以應該盡量少用 RUN 命令,而且要在RUN 的最后要做必要的清除工作

# 構建層次太多,未做清理工作

FROM debian:stretch

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
error_Dockerfile
# 一層構建,並在最后清理壓縮包等緩存文件
FROM debian:stretch

RUN buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
correct_Dockerfile

Dockerfile 上下文

3, Dockerfile 上下文:

# 構建新的鏡像
# -t :指定鏡像名稱和 tag
# . : 上下文,表示將本路徑下的所有文件打包上傳到 docker daemon,進行定制鏡像

$ docker build -t nginx:v3 .
Context

COPY

4, COPY : 用來從構建上下文目錄中<原路徑>的文件/目錄復制到新一層鏡像內的 <目標路徑>位置,格式有兩種:

    1,shell 格式:COPY [--chown=<user>:<group>] <原路徑>...<目標路徑>

    2,exec 合適:COPY[--chown=<user>:<group>] ["原路徑1",... "<目標路徑>"]

原路徑:可以是多個,甚至可以是通配符

目標路徑:可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定,不需要事先創建,會自動創建)

1 # 利用 通配符 進行復制
2 COPY hom* /mydir/
3 COPY hom?.txt /mydir/
COPY

note : COPY 會將原文件的各種數據都保留,比如 讀、寫、執行權限,可以通過 --chown=<user>:<group> 選項來改變文件的所屬用戶及所屬組。

ADD

5,ADD : 和 COPY 指令的功能,性質基本一致,也可以通過 --chown 改變文件所屬用戶和所屬組,但是在 COPY 的基礎上增加了一些功能:

    1,原路徑為 URL : Docker 會試圖下載這個文件放到 目標路徑去,默認下載后的文件權限為 600,如果想要修改權限或者下載的是壓縮包,需要解壓,則還需要額外的一層 RUN 進行調整,還不如直接用 RUN 指令用 wget 進行下載,處理權限,解壓縮,然后清理無用文件更合理,所以該命令不常用,而且不推薦使用。

    2,原路徑為 tar 壓縮包 : 如果壓縮文件格式為 gzip , bzip2 以及 xz 的情況下,ADD 指令將自動解壓這個壓縮文件到 <目標路徑> 去,只有此種情況適合使用 ADD 指令。

note: ADD 指令可能會使鏡像構建緩存失效,從而可能會令鏡像的構建變的比較緩慢,鏡像構造緩存點擊這里查看

CMD

6,CMD : 和 RUN 指令相似,也是兩種格式:

    1,shell 格式:CMD <命令>

    2,exec 格式 : CMD ["可執行文件",“參數1”,“參數2” ...]

    3,參數格式列表:在指定了 ENTRYPOINT 指令后,用 CMD 指定具體的參數

    CMD 指令用於指定默認的容器主進程的啟動命令的,例如 ubuntu 默認的 CMD 是 bash ,我們也可以在容器運行時指定運行別的命令,如:

# 直接進入 bash
$ docker run -it ubuntu

# 修改默認的 CMD
# docker run -it ubuntu cat /etc/os-release

note1: 在指令格式上,一般推薦使用 exec 格式,這類格式在解析時會被解析為 JSON 數組,因此一定要用 雙引號 “ 而不要使用單引號 。

# 如果執行 
CMD echo $HOME

# 實際執行會變更為:
CMD ["sh" "-c" "echo $HOME"]

note2 : 容器的前台執行和后台執行問題

  注:Docker 不是虛擬機,容器中的應用都應該以前台執行,而不能像虛擬機用 systemd 去啟動后台服務,容器內沒有后台服務的概念。例如:

# 錯誤代碼
# 目的:啟動 nginx 在后台以守護進程的形式在運行
CMD service nginx start

# 實際上執行
# sh 為主進程,執行完成進程退出,導致容器也會退出
CMD ["sh" "-c" "service nginx start"]

# 正確做法
# nginx :可執行文件
CMD ["nginx", "-g", "daemon off;"]
后台運行 nginx

ENTRYPOINT

7, ENTRYPOINT:格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式,目的和 CMD 一樣,都是在指定容器啟動程序及參數;當指定了 ENTRYPOINT 后,CMD 的含義就發生了變化,不再是直接的運行其命令,而是將 CMD 的內容作為參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變為: <ENTRYPOINT>"<CMD>"

用處 1 : 讓鏡像變成向命令一樣使用:

# 如果我們需要一個得知自己當前的公網 IP 的鏡像
# Dockerfile 內容:
FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD ["curl", "-s", "https://ip.cn"]

# 構建鏡像
docker build -t myip .

# 查詢 ip 操作
# 不能添加參數,如上面的 -s 參數
docker run myip

# 希望顯示 HTTP 頭信息,需要加上 -i 參數
# 試圖添加參數,會報錯,因為在容器后加入參數,會被解析成 CMD 命令,但 -i 不是任何命令
docker  run myip -i

# 用 ENTRYPOINT 的方式
# Dockerfile 內容:
FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["curl", "-s", "https://ip.cn"]

# 查詢 ip 操作,可加參數
# 此時 CMD 的內容 -i 傳遞給了主進程 curl
docker run myip -i
ENTRYPOINT 的使用

用處 2 : 應用運行前的准備工作:比如數據庫配置,初始化工作,此時可以傳 ENTRYPOINT 一個腳本,然后通過 CMD 指定參數,在腳本最后執行

 1 # allow the container to be started with `--user`
 2 # Dockerfile
 3 FROM alpine:3.4
 4 RUN addgroup -S redis && adduser -S -G redis redis
 5 ...
 6 ENTRYPOINT ["docker-entrypoint.sh"]
 7 EXPOSE 6379
 8 CMD ["redis-server"]
 9 
10 # docker-entrypoint.sh 腳本文件
11 #!/bin/bash
12 if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
13 chown -R redis .
14 exec su-exec redis "$0" "$@"
15 fi
16 exec "$@"
初始化工作

ENV

8, ENV : 用來設置環境變量,格式有兩種:

  1,ENV <key> <value>

  2,ENV <key1>=<value1> <key2>=<value2>...

在設置了環境變量之后,無論是后面的其它指令,如 RUN ,還是運行時的應用,都可以直接使用這里定義的環境變量

# 定義環境變量
ENV VERSION=1.0 DEBUG=ON \
    NAME="Happy Feet"

# 官方 node 鏡像 Dockerfile 中:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
    && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
    && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
    && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1                             && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc 
SHASUMS256.txt \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs            
ENV

ARG

9, ARG : 構建參數,格式:

  1,ARG <參數名>[=<默認值>]

構建參數和 ENV 的效果一樣,都是設置環境變量,所不同的是,ARG 所設置的是構建環境的環境變量,在將來容器運行時是不會存在這些環境變量的。

VOLUME

10,VOLUME:定義匿名卷,格式為:

  1,VOLUME ["<路徑1>”,”<路徑2>"...]

  2,VOLUME <路徑>

  之前說過,容器運行時應該盡量保持容器存儲層不發生寫操作,對於數據庫類需要保存動態數據的應用,其數據庫文件應該保存在卷中,為了防止運行時用戶忘記將動態文件所保存目錄掛載為卷,在 Dockerfile 中,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。

# /data 目錄會在運行時自動掛載為匿名卷
VOLUME /data

# 運行時也可以覆蓋這個掛載設置
# 用 mydata 這個命名卷掛載到了 /data 這個位置,代替 Dockerfile 中的匿名卷的掛載配置
docker run -d -v mydata:/data xxxx
VALUME 匿名卷

EXPOSE

11,EXPOSE:聲明端口,格式為:

  EXPOSE <端口1> [<端口2>...]

  該條指令是聲明運行時容器提供的服務端口,這只是一個聲明,在運行時並不會因為這個聲明應用就會開啟這個端口的服務。這樣聲明帶來兩個好處:

  1,幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射

  2,在運行時使用隨機端口映射,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口

note: 要將 EXPOSE 和在運行時使用 -p <宿主端口>:<容器端口> 區分開來。-p 是映射宿主端口和容器端口,就是將容器的對應端口服務公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什么端口而已,並不會在宿主進行端口映射。

WORKDIR

12, WORKDIR : 指定工作目錄,格式為:

  WORKDIR <工作目錄路徑>

  該條指令可以來指定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,如果該目錄不存在,則會自動建立。

# 常見錯誤
$ RUN cd /app
$ RUN echo "hello" > word.txt

/×
如果將這個 Dockerfile 進行構建鏡像運行后,會發現根本找不到 /app/word.txt 文件,這是
因為在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不同,是兩個完全不同的容器。
沒一個 RUN 都是啟動一個容器、執行命令、然后提交存儲層文件變更;第一層的執行僅僅是
當前進程的工作目錄變更,一個內存上的變化而已,其結果不會造成任何文件改變;而到了第
二層的時候,啟動的是一個全新的容器,跟第一層的容器更完全沒有關系,自然不可能繼承前一層構建過程中的內存變化。
×/

/×
因此,如果需要改變以后各層的工作目錄的位置,那么應該使用 WORKDIR 指令
×/
常見錯誤

USER

13,USER:指定當前用戶,格式為:

  USER <用戶名>[:<用戶組>]

  該條指令和 WORKDIR 相似,都是改變環境狀態並影響以后的層,WORKDIR 是改變工作目錄, USER 是改變之后層的執行 RUN ,CMD 以及 ENTRYPOINT 這類命令的身份。如果以 root 執行的腳本,在執行期間希望改變身份,比如希望以某個已經建立好的用戶來運行某個服務進程,不要使用 su 或者 sudo ,這些都需要比較麻煩的配置,而且在 TTY 缺失的情況下經常出錯,建議使用 gosu 。

# 建立 redis 用戶,並使用 gosu 換另一個用戶執行命令
RUN groupadd -r redis && useradd -r -g redis redis

# 下載 gosu
RUN wget -O /user/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64"  && chmod +x /usr/local/bin/gosu && gosu nobody true

# 設置 CMD ,並以另外的用戶執行
CMD ["exec", "gosu", "redis", "redis-server"]
USER

HEALTHCHECK

14,HEALTHCHECK:健康檢查,格式為:

  HEALTHCHECK [選項] CMD <命令> :設置檢查容器健康狀況的命令

  HEALTHCHECK  NONE : 如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉其健康檢查指令

options:

  --interval=<間隔> :兩次健康檢查的間隔,默認為 30s;

  --timeout=<時長>: 健康檢查命令運行超時時間,如果超過這個時間,本次健康檢查就被視為失敗,默認 30s;

  --retries=<次數> :  當連續失敗指定次數后,則將容器狀態視為 unhealthy ,默認 3 次;

return value:

  0 : 成功

  1:失敗

  2:保留(不要使用這個值)

ONBUILD

15,ONBUILD:后構建指令,格式為:

  ONBUILD <其它指令>

  ONBUILD 是一個特殊的指令,它后面跟的是其它指令,比如 RUN,COPY 等,而這些指令,在當前鏡像構建時並不會被執行。只有當以以前鏡像為基礎鏡像,去構建下一級鏡像的時候才會被執行。


免責聲明!

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



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