docker---Dockerfile編寫

  前言:鏡像的定製實際上就是定製每一層所添加的配置文件,若是咱們能夠把每一層的修改、安裝、構建、操做的命令都寫入一個腳本,而後用這個腳原本構建、定製鏡像,那麼鏡像構建透明性的問題、體積的問題就會獲得解決,這個腳本就是 Dockerfile; Dockerfile 是一個文本文件,其內包含了一條條的指令,每一條指令構建一層,每一層指令的內容,就是描述該層應該如何構建,而後經過 commit 構成新的鏡像。php

 

Dockerfile 參數

FROM

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

# 定製 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

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

        若是沒有找到對應服務的鏡像,官方鏡像中還提供了一些更爲基礎的操做系統鏡像,好比:ubuntu 、debian 、fedora 、centos 等,也能夠利用這些操做系統提供的軟件庫mysql

RUN

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

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

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

warning:每個RUN命令都會在 docker鏡像中新建一層,因此應該儘可能少用 RUN 命令,並且要在RUN 的最後要作必要的清除工做github

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

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 上下文:golang

# 構建新的鏡像
# -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 等,而這些指令,在當前鏡像構建時並不會被執行。只有當以之前鏡像爲基礎鏡像,去構建下一級鏡像的時候纔會被執行。

相關文章
相關標籤/搜索