Dockerfile構建鏡像
構建鏡像
構建鏡像指令:docker build或docker image build
Dockerfile初識:
cat >>Dockerfile<<end
FROM python:2.7-slim
WORKDIR /app
ADD . /app
RUN pip install -r requirements.txt
EXPOSE 80
ENV NAME World
CMD ["python", "app.py"]
end
yum install python2-pip
配置pip國內源
mkdir -p /root/.pip
cat >> /root/.pip/pip.conf<<end
[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple/
end
cat >>requirements.txt<<end
Flask
Redis
end
cat >> app.py<<end
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host=‘0.0.0.0‘, port=80)
end
docker build -t friendlyhello .
上下文的概念:
執行docker build命令時,宿主機上的當前工作目錄稱為構建上下文。默認情況下,假定Dockerfile位於本地當前目錄,但可以使用-f指定其他位置。無論Dockerfile實際存在的位置如何,當前目錄中的所有文件和目錄的遞歸內容都將作為構建上下文發送到Docker守護程序(構建由Dockerd守護程序運行)
Dockerfile詳解
格式:
# Comment
INSTRUCTION arguments
註意事項:
1)、如果一行太長,使用反斜線 \ 續行
2)、在行首使用#符號,表示註釋(註釋中不支持 \續行)
3)、INSTRUCTION不區分大小寫。慣例是讓它們成為大寫的,以便更容易地將它們與參數區分開來
共18個指令
1、FROM #必要指令,通常是第一條(引用變量及多階段構建例外),可有多條(多階段構建)
FROM <image> [AS <name>] #AS <name>為該階段命名
或
FROM <image>[:<tag>] [AS <name>]
或
FROM <image>[@<digest>] [AS <name>]
scratch鏡像:
FROM scratch
ADD hello /
CMD ["/hello"]
說明:
1、可通過添加AS name來為該構建階段命名。該名稱可用於後續FROM和COPY --from=<name|index>指令,以引用此階段構建的鏡像。
2、tag或digest值是可選的。如果省略其中任何一個,則構建器默認采用latest標記
3、FROM指令支持變量,變量在第一個FROM指令之前使用ARG聲明(ARG指令是唯一一個能用在FROM之前的指令,聲明僅在構建鏡像過程中存在的變量)
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
2、MAINTAINER 鏡像維護者信息
MAINTAINER [email protected]
說明:指定該鏡像維護者信息,推薦使用LABEL替代該指令指定鏡像維護者信息
docker inspect <Container ID> | grep -A 6 Labels
3、RUN 執行命令
用來執行命令,有2種格式
格式一:
shell格式: RUN <命令>
如:RUN echo ‘<h1>Hello, Docker!</h1>‘ > /usr/share/nginx/html/index.html
說明:
1、這種格式在shell中運行,會轉換為/bin/sh -c <命令>執行
2、可以使用SHELL命令更改默認的shell,或使用exec形式
3、要使用除“/bin/sh”之外的其他shell,請使用exec形式。例如,RUN ["/bin/bash", "-c", "echo hello"]
4、命令中支持變量擴展
示例:
RUN /bin/bash -c source $HOME/.bashrc; \
echo $HOME‘
格式二:
exec格式: RUN ["executable", "param1", "param2"]
說明:
1、 這種格式不會被轉換
2、 不支持變量擴展
3、 exec形式被解析為JSON數組,因此必須使用雙引號(")來包括單詞而不能使用單引號(‘)
4、 在JSON形式中,必須轉義反斜杠\(windows中作為路徑分隔符經常遇到)
註意:
1、可使用反斜杠\(即續行符)分隔的多行上拆分長或復雜語句,使Dockerfile更具可讀性,可理解性和可維護性
示例:
RUN yum update && yum install -y \
package-bar \
package-baz \
package-foo=1.3.*
2、使用管道
某些RUN命令依賴於使用管道符(|)將一個命令的輸出傳遞到另一個命令的能力,如下例所示:
RUN wget -O - https://some.site | wc -l > /number
Docker使用/bin/sh -c解釋器執行這些命令,解釋器僅評估管道中最後一個操作的退出代碼以確定成功。
在上面的示例中,只要wc -l命令成功,即使wget命令失敗,此構建步驟也會成功並生成新鏡像。
如果希望命令因管道中任何階段的錯誤而失敗,預先使用set -o pipefail設置bash環境變量,確定意外錯誤可防止構建無意中成功。
例如:
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
註:並非所有shell都支持該-o pipefail選項
4、CMD為執行容器提供默認值 #只能有一條,如果列出多個僅最後一條生效
有三種形式:
- CMD ["executable","param1","param2"](exec形式,首選)
- CMD command param1 param2(shell形式)
- CMD ["param1","param2"](必須配合ENTRYPOINT使用,作為其默認參數,單獨使用無效果)
說明:
1)exec形式不支持變量擴展,shell形式支持
2)exec形式被解析為JSON數組,因此必須使用雙引號(")來包括單詞而不能使用單引號(‘)
3)在JSON形式中,必須轉義反斜杠\(windows中經常遇到)
註意:不要混淆RUN和CMD。RUN實際上運行一條命令並提交結果,CMD在構建過程中不執行任何操作,而是作為鏡像的默認指令(即使用該鏡像啟動容器時默認執行的指令)
5、LABEL 添加元數據到鏡像
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL是鍵值對,要在LABEL值中包含空格,請使用(雙)引號或反斜杠轉義,就像在命令行解析中一樣
示例:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \ #續行符
that label-values can span multiple lines."
鏡像可以有多個標簽,可以在一行中指定多個標簽:
兩種示例:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
6、EXPOSE 容器運行時偵聽其服務端口
EXPOSE <port1> [<port2>/<protocol>...] #多個端口之間使用空格間隔
EXPOSE指令通知Docker容器在運行時偵聽指定的網絡端口。
可以指定端口是偵聽TCP還是UDP,如果未指定協議,則默認為TCP
7、ENV 設置環境變量
有兩種形式:
形式一:
ENV <key> <value> #第二個空格後的所有字符串將被視為<value>,包括空格字符
形式二:
ENV <key>=<value> ... # ENV指令將環境變量<key>的值設置為<value>
該環境變量在構建階段所有後續指令的環境中生效,且使用ENV設定的環境變量會在鏡像中一直存在(使用該鏡像啟用的容器中也存在該變量)
示例:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
和
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy #效果相同
ENV指令中可使用變量:
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
示例:
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress &&…
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
註意:
1、可以使用docker inspect查看變量值,使用docker run --env <key>=<value>在構建容器時更改變量的值。
2、Dockerfile中能夠擴展變量的指令:
FROM/ADD/COPY/ENV/EXPOSE/LABEL/STOPSIGNAL/USER/VOLUME/WORKDIR/ONBUILD
8、ARG #設置構建過程中的環境變量
格式:
ARG <參數名>[=<默認值>]
ARG指令可以可選地包括一個默認值:
示例:
FROM busybox
ARG user1=someuser #指定了默認值
ARG buildno=1 #指定了默認值
...
說明:
如果ARG指令具有默認值,並且在構建時沒有傳遞值,將使用默認值
如果構建時傳遞值,將不使用默認值,而是用傳遞的值
註意:
1、效果和ENV一樣,都是設置環境變量,不同的是:ARG設置的環境變量,在將來容器運行時,不再存在,僅在構建鏡像過程中存在
2、可在構建命令docker build使用--build-arg <參數名>[=<值>]選項覆蓋Dockerfile中設定的默認值,參數名必須在Dockerfile中已經使用ARG指令構建,否則會報錯或警告
3、一個ARG指令生效範圍,在其之後且在其構建階段內。若在多階段構建中的每個階段均使用ARG,每個階段都必須包含ARG指令。
示例:多階段構建
FROM busybox #第一階段
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox #第二階段
ARG SETTINGS
RUN ./run/other $SETTINGS
4、Dockerfile可以包括一個或多個ARG指令。
例如,以下是有效的Dockerfile:
FROM busybox
ARG user1
ARG buildno
...
警告:
不要使用構建時變量來傳遞密碼,如github密鑰,用戶憑據等,構建過程中變量值對於任何用戶都是可見的
5、ARG生效範圍
ARG變量定義從Dockerfile中定義的行開始生效,直到其所在構建階段結束(若Dockerfile中有多個構建階段,僅在自己的構建階段中生效)
示例:
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
...
通過以下指令構建:
docker build --build-arg user=what_user .
第2行USER使用some_user作為user變量的值。
第4行USER使用what_user作為user變量的值,且what_user在命令行上傳遞的值。
在通過ARG指令定義之前,對變量的任何使用都會導致空字符串
6、使用ARG變量
可以使用ARG或ENV指令指定在RUN指令中可用的變量。ENV指令定義的環境變量始終覆蓋ARG定義的同名變量。
示例:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER
然後,假設使用此命令構建此映像:
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
在這種情況下,RUN指令使用v1.0.0而不是ARG用戶傳遞的設置:v2.0.1,
9、COPY 復制上下文中的文件或目錄到容器中
COPY有兩種形式:
- COPY [--from=<name|index>] [--chown=<user>:<group>] <src>... <dest>
- COPY [--from=<name|index>] [--chown=<user>:<group>] ["<src>",... "<dest>"](包含空格的路徑使用此形式)
--chown功能僅在用於構建Linux容器
註意:
1、源路徑一定是相對於build指令中指定的上下文的相對路徑
如COPY package.json /usr/src/app/
2、目標路徑可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(也是容器內的路徑,工作目錄可以使用WORKDIR指令來指定)
3、目標路徑不需要事先創建,如果目錄不存在,在復制文件之前會自動創建
4、使用COPY指令時,源文件的各種元數據都會被保留,如讀寫執行權限,文件的相關時間屬性等
5、源路徑可以是多個,甚至可以含有通配符,通配符要符合Go的filepath.Match規則:
COPY hom* /mydir
COPY hom?.txt /mydir
6、如果使用--chown選項,最好使用數字形式的GID和UID(如果使用用戶名和組名,在容器文件系統中不存在/etc/passwd及/etc/group,將會導致構建失敗)
示例:
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/ #不指定組名,使用與用戶名同名的組
COPY --chown=1 files* /somedir/ #GID也為1
COPY --chown=10:11 files* /somedir/
7、(可選)COPY接受一個標誌--from=<name|index>
該選項將用於替代用戶發送先前構建階段的構建上下文
如果先前構建階段使用FROM .. AS <name>,則--from可通過name引用
如果先前構建階段未使用FROM ..
AS <name>,則--from可通過索引值index引用,如果先前構建階段是第一階段,index為0,以此類推
如果找不到具有指定名稱的構建階段,則嘗試使用具有相同名稱的鏡像
COPY遵守以下規則:
- <src>路徑必須位於build構建時的context上下文中;不能COPY ../something /something,因為第一步docker build是將上下文目錄(和子目錄)發送到docker守護程序
- 如果<src>是目錄,則復制目錄中的全部內容,包括文件系統元數據(不復制目錄本身,只復制其內容)
- 如果<src>是任何其他類型的文件,則將其與元數據一起單獨復制。在這種情況下,如果<dest>以尾部斜杠結尾/,則將其視為目錄,<src>並將寫入內容<dest>/base(<src>)。
- 如果<src>直接或由於使用通配符指定了多個資源,則<dest>必須是目錄,並且必須以斜杠結尾/。
- 如果<dest>不以尾部斜杠結束,則將其視為常規文件,並將<src>的內容寫入<dest>文件中。
- 如果<dest>不存在,則會在其路徑中創建所有缺少的目錄。
10、ADD 復制本地文件到上下文中
從<src>中復制文件、目錄或遠程文件URL,並將它們添加到路徑上鏡像的文件系統<dest>中
ADD有兩種形式:
- ADD [--chown=<user>:<group>] <src>...<dest>
- ADD [--chown=<user>:<group>] ["<src>",..."<dest>"](包含空格的路徑需要使用這種形式)
ADD指令的所有使用註意事項及遵守規則與COPY相同
與COPY不同的是:
源路徑:
1、可以是URL,Docker引擎會嘗試下載這個鏈接文件,放入到<dest>中,下載後文件的權限為600,若是想更改文件的權限,還需要增加一層RUN指令
2、如果下載的是壓縮包,還需要增加一層RUN指令進行解壓
3、如果<src>是一個上下文中的tar壓縮文件(gzip、bzip2、xz),ADD指令會自動解壓放入到<dest>中,如果不希望壓縮文件解壓,則使用COPY(這是ADD指令最大的用處)
4、僅在需要自動解壓時,才會選擇使用ADD指令,否則復制文件盡可能使用COPY
5、ADD指令會令鏡像構建緩存失效,從而可能使鏡像構建過程非常緩慢(因此多階段構建時,盡可能不要使用)
11、ENTRYPOINT 入口點 #只能有一條,若有多條僅最後一條生效
ENTRYPOINT有兩種形式:
- ENTRYPOINT ["executable", "param1", "param2"](exec形式,首選)
- ENTRYPOINT command param1 param2(shell形式,最好在command前使用exec指令,以使該進程作為容器的PID 1進程)
exec形式:
docker run <image>的命令行參數將附加在exec形式的ENTRYPOINT的所有元素之後,作為ENTRYPOINT指令的參數,並將覆蓋所有使用CMD指定的元素。
可以使用docker run --entrypoint標誌覆蓋exec形式的ENTRYPOINT指令
shell形式:
shell形式防止任何被CMD或run使用的命令行參數,缺點是ENTRYPOINT將被作為/bin/sh -c的一個子命令,且不傳遞信號。這意味著可執行文件將不是容器中PID為1的進程,並且不會收到Unix信號,因此可執行文件將不會收到來自docker stop <container>的SIGTERM信號。
從命令行工具的鏡像示例:
cat >>Dockerfile<<end
FROM centos
RUN yum install curl -y
CMD ["curl", "-s", "http://ip.cn"]
end
docker build -t myip:1.0 .
docker run --name myip01 myip:1.0
docker run --name myip02 myip:1.0 -i #此時會報錯
原因:執行docker run myip -i 鏡像名稱myip後的手動指定的主進程指令將會替換構建鏡像時CMD指定的主進程,這裏替換後相當於執行-i指令,但實際上沒有該指令,提示executable file not found in $PATH
正確用法:docker run myip curl -s http://ip.cn -i #整體替換Dockerfile中的CMD
使用ENTRYPOINT解決:
使用如下的dockerfile構建鏡像
cat >> Dockerfile<<end
FROM centos
RUN yum install curl -y
ENTRYPOINT ["curl", "-s", "http://ip.cn"]
end
docker build -t myip2:1.0 .
docker run --name myip201 myip2:1.0
docker run --name myip202 myip2:1.0 -i
這種方式很有用,因為鏡像名稱可以兼作二進制文件的引用
ENTRYPOINT指令還可以與輔助腳本結合使用,使其能夠以與上述命令類似的方式運行,即使啟動該工具可能需要多個步驟
例如,Postgres官方鏡像使用以下腳本作為其ENTRYPOINT(這樣在啟動容器時可以傳遞參數給腳本):
#!/bin/bash
set -e #若指令傳回值不等於0,則立即退出shell
if [ "$1" = ‘postgres‘ ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
配置應用為容器中PID為1進程
此腳本使用的exec bash命令,以使最終運行的應用程序成為容器的PID 1,這允許應用程序接收發送到容器的任何Unix信號
幫助程序腳本被復制到容器中並通過ENTRYPOINT容器啟動運行:
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]
該腳本允許用戶以多種方式與Postgres交互。
它可以簡單地啟動Postgres:
$ docker run postgres
或者,它可用於運行Postgres並將參數傳遞給服務器:
$ docker run postgres postgres --help #ENTRYPOINT配合腳本使用的好處
最後,它還可以用來啟動一個完全不同的工具,比如Bash:
$ docker run --rm -it postgres bash
Exec形式的ENTRYPOINT示例
可以使用exec形式ENTRYPOINT設置默認命令和參數,然後使用任一形式CMD設置可更改的其他默認值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
構建鏡像,啟容器
檢查結果:
$ docker exec -it test ps aux
可以使用docker stop優雅地請求top進程關閉
可再次使用docker start啟動
這樣通過docker指令傳遞的信號(傳給容器),實際上就是傳遞給容器中的PID為1的進程
以下Dockerfile顯示使用ENTRYPOINT在前臺運行Apache(即作為容器的PID 1進程):
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
註意:
1、exec形式被解析為JSON數組,這意味著必須使用雙引號(")來引用而不能使用單引號(‘)。
2、與shell形式不同,exec形式不會調用命令shell,因此不能擴展變量。例如,ENTRYPOINT [ "echo", "$HOME" ]不會對變量進行替換$HOME。
3、如果想要shell處理,那麽要麽使用shell形式,要麽直接執行shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
Shell形式的ENTRYPOINT示例
可以為ENTRYPOINT指定一個純字符串指令,將在/bin/sh -c中執行。該形式支持環境變量擴展,並將忽略任何CMD或docker run命令行參數。
為了確保docker stop能夠正確地發出任何信號給長時間運行的ENTRYPOINT可執行文件,需要使用exec指令執行相關指令:
FROM ubuntu
ENTRYPOINT exec top -b
運行此鏡像時,將看到單個PID 1進程:
$ docker run -it --rm --name test top
執行docker stop關閉及docker start啟動容器
如果shell形式中未使用exec執行ENTRYPOINT中的指令:
FROM ubuntu
ENTRYPOINT top -b
CMD -d 5
啟容器:
docker run -it --name test …
從輸出中top看到指定ENTRYPOINT的不是PID 1
如果運行docker stop test,容器將不會幹凈地退出(最終容器也會推出,但是通過將信號發送給sh,而不是發送給應用程序指令top)
CMD和ENTRYPOINT如何相互作用:
- Dockerfile應至少指定一個CMD或ENTRYPOINT命令
- ENTRYPOINT應該在將容器用作可執行文件時使用
- CMD應該用作定義ENTRYPOINT命令的默認參數
- CMD在使用替代參數運行容器時將被覆蓋
下表顯示了針對不同ENTRYPOINT/CMD組合執行的命令:
|
沒有ENTRYPOINT |
ENTRYPOINT exec_entry p1_entry |
ENTRYPOINT [“exec_entry”,“p1_entry”] |
沒有CMD |
錯誤,不允許 |
/bin/sh -c exec_entry p1_entry |
exec_entry p1_entry |
CMD [“exec_cmd”,“p1_cmd”] |
exec_cmd p1_cmd |
/bin/sh -c exec_entry p1_entry |
exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”,“p2_cmd”] |
p1_cmd p2_cmd |
/bin/sh -c exec_entry p1_entry |
exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd |
/bin/sh -c exec_cmd p1_cmd |
/bin/sh -c exec_entry p1_entry |
exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
12、VOLUME
格式:
VOLUME ["/data"]
VOLUME ["/var/log/","/data"] #在docker的卷管理目錄中劃出2塊區域掛載到容器中的這兩個目錄
VOLUME /var/log
VOLUME /var/log /var/db
VOLUME指令創建具有指定名稱的掛載點(即在容器中創建目錄,並將volume掛載到該掛載點,這種方式的volume名稱是隨機生成的),並將其標記為從本機或其他容器保存外部掛載的卷。該值可以是JSON數組,VOLUME ["/var/log/"]或具有多個參數的普通字符串,如VOLUME /var/log或VOLUME /var/log /var/db
示例:
FROM centos
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME ["/myvol", "/data"]
CMD ["/bin/bash"]
docker volume ls
docker volume inspect
ll /var/lib/docker/volumes/
註意:
宿主機目錄在容器運行時聲明:宿主機目錄(mountpoint)本質上是依賴於主機的。這是為了保持鏡像的可移植性,因為不能保證給定的主機目錄在所有主機上都可用。因此,無法從Dockerfile中安裝主機目錄,VOLUME指令不支持指定host-dir參數,必須在創建或運行容器時指定掛載點(-v選項指定)
13、USER
USER <user>[:<group>] 或
USER <UID>[:<GID>]
當構建鏡像時,在Dockerfile文件中有RUN、CMD、ENTRYPOINT指令時,USER指令用於設置執行這些指令的用戶(UID),及可選的用戶組(GID),用戶或組必須事先存在
警告:當用戶沒有主組時,鏡像(或下一個指令)將使用root組運行
14、WORKDIR
WORKDIR /path/to/workdir
WORKDIR指令用於在Dockerfile中設置RUN,CMD,ENTRYPOINT,COPY和ADD指令的工作目錄。如果WORKDIR指定的目錄事先不存在,則會自動被創建。
在Dockerfile中可多次使用WORKDIR指令。如果提供了相對路徑,則它將相對於前一條WORKDIR指令的路徑。
例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最終pwd命令的輸出將是/a/b/c
WORKDIR指令可以解析先前使用ENV設定的環境變量。只能使用Dockerfile文件中顯式設置的環境變量。
例如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
最終pwd命令的輸出Dockerfile將是/path/$DIRNAME
15、ONBUILD
ONBUILD [INSTRUCTION]
當鏡像用作另一個構建的基礎時,該ONBUILD指令向鏡像添加將在稍後執行的觸發指令。觸發器將在下遊構建的上下文中執行,就好像它在下遊Dockerfile中FROM指令之後插入了這裏的INSTRUCTION指令一樣。
任何構建指令都可以註冊為觸發器
16、HEALTHCHECK #只能有一條,如果列出多個,則只有最後一個生效
該HEALTHCHECK指令有兩種形式:
- HEALTHCHECK [OPTIONS] CMD command(通過在容器內運行命令來檢查容器運行狀況)
- HEALTHCHECK NONE(禁用從基礎映像繼承的任何運行狀況檢查)
當容器指定了運行狀況檢查時,除了正常狀態外,它還具有運行狀況:
這個狀態最初是starting
每當健康檢查通過時,它就會變成healthy
經過一定數量的連續失敗後,它就變成了unhealthy
OPTIONS:
- --interval=DURATION(默認值:30s) 檢查時間間隔
- --timeout=DURATION(默認值:30s) 單次檢查超時秒數,若超時視為檢查失敗
- --retries=N(默認值:3) 連續重試N次後,健康檢查均失敗,認為容器為unhealthy
- --start-period=DURATION(默認值:0s) 為需要時間引導的容器提供初始化時間,在此期間探測失敗將不計入最大重試次數。但如果在啟動期間運行狀況檢查成功,則會將容器視為已啟動,並且所有連續失敗將計入最大重試次數
CMD關鍵字後面的命令可以是shell命令(如HEALTHCHECK CMD /bin/check-running)或exec形式
命令的退出狀態指示容器的運行狀況。可能的值是:
- 0:成功 - 容器健康且隨時可用
- 1:不健康 - 容器無法正常工作
- 2:保留 - 不要使用此退出代碼
示例:
要檢查每五分鐘左右網絡服務器能夠在三秒鐘內為網站的主頁面提供服務:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
17、SHELL
SHELL ["executable", "parameters"]
該指令允許覆蓋用於shell形式的命令的默認shell。
Linux上默認shell是["/bin/sh", "-c"]
Windows上默認shell是["cmd", "/S", "/C"]。該SHELL指令必須以JSON格式寫入Dockerfile
Windows上經常使用
18、STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL指令設置將發送到容器的系統調用信號以退出。此信號可以是與內核的系統調用表中的位置匹配的有效無符號數,例如9,或SIGNAME格式的信號名,例如SIGKILL
默認的stop signal是SIGTERM,在docker stop的時候會給容器內PID為1的進程發送這個signal,通過--stop-signal可以設置自己需要的signal,主要的目的是為了讓容器內的應用程序在接收到signal之後可以先做一些事情,實現容器的平滑退出,如果不做任何處理,容器將在一段時間之後強制退出,會造成業務的強制中斷,這個時間默認是10s
Dockerfile說明
Docker鏡像由只讀層組成,每個層都代表一個Dockerfile指令。這些層是堆疊的,每一層都是前一層變化的增量。
Dockerfile:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
每條指令創建一個層:
- FROM從ubuntu:15.04Docker鏡像創建一個層
- COPY從Docker客戶端的當前目錄添加文件
- RUN用你的應用程序構建make
- CMD指定在容器中運行的命令
運行鏡像並生成容器時,可以在基礎層的頂部添加新的可寫層writable layer(“容器層”)。對正在運行的容器所做的所有更改(例如寫入新文件,修改現有文件和刪除文件)都將寫入此可寫容器層。
上下文的概念
執行docker build命令時,宿主機上的當前工作目錄稱為構建上下文。默認情況下,假定Dockerfile位於宿主機本地當前目錄,但可使用選項(-f)指定其他位置。無論Dockerfile實際存在的位置如何,當前目錄中的所有文件和目錄的遞歸內容都將作為構建上下文發送到Docker守護程序(構建由Dockerd守護程序運行,而不是由CLI運行)
Usage: docker build [OPTIONS] PATH | URL | -
從構建上下文(.)中構建映像:
mkdir project && cd project
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .
移動Dockerfile和hello文件到單獨的目錄
構建映像的第二個版本(不依賴於上一個版本的緩存)
使用-f指向Dockerfile並指定構建上下文的目錄:
mkdir -p dockerfiles context
mv Dockerfile dockerfiles && mv hello context
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
Docker 17.05增加了Dockerfile通過stdin使用本地或遠程構建上下文進行管道來構建映像的功能。
在早期版本中,使用來自stdin的Dockerfile構建映像,不支持發送構建上下文
Docker 17.04及更低版本
docker build -t foo - <<EOF
FROM busybox
RUN echo "hello world"
EOF
Docker 17.05及更高版本(本地構建上下文)
docker build -t foo . -f - <<EOF
FROM busybox
RUN echo "hello world"
COPY . /my-copied-files
EOF
Docker 17.05及更高版本(遠程構建上下文)
docker build -t foo https://github.com/thajeztah/pgadmin4-docker.git -f - <<EOF
FROM busybox
COPY LICENSE config_local.py /usr/local/lib/python2.7/site-packages/pgadmin4/
EOF
排除上下文中與構建無關的文件,使用上下文中的.dockerignore文件(每個要排除的文件或目錄獨占一行)
示例.dockerignore文件(支持通配):
# comment
*/temp*
*/*/temp*
temp?
**匹配任意數量目錄(包括零)的特殊通配符字符串
例如,**/*.go將排除.go在所有目錄中找到的以該結尾的所有文件,包括構建上下文的根
以!(感嘆號)開頭的行可用於對排除項進行例外處理
以下是.dockerignore使用此機制的示例文件:
*.md
!README.md
除README.md上下文外,所有以md為擴展名的文件除外(也就是README.md文件不會被排除)
示例:
*.md
README-secret.md
!README*.md
包含所有README文件。中間一行沒有效果,因為!README*.md匹配README-secret.md並且最後。
甚至可以使用該.dockerignore文件來排除Dockerfile和.dockerignore文件
註意:這些文件仍然發送到守護程序,因為它需要它們來完成它的工作。但是ADD和COPY指令不會將它們復制到鏡像中
使用多階段構建
多階段構建(在Docker 17.05或更高版本中)可大幅減小最終鏡像的大小,而不必費力地減少中間層和文件的數量。
由於鏡像是在構建過程的最後階段構建的,因此可以通過利用構建緩存來最小化鏡像層。
#第一階段
Dockerfile:
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
#第二階段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
只需要單個Dockerfile,也不需要單獨的構建腳本。只要運行docker build
$ docker build -t alexellis2/href-counter:latest .
默認情況下,階段未命名,可以通過整數來引用它們,第一條FROM指令從0開始。但是,可以通過as <NAME>在FROM指令中添加一個來命名您的階段。
此示例通過命名階段並使用COPY指令中的名稱來改進前一個示例。這意味著即使Dockerfile中的指令稍後重新排序,COPY也不會中斷。
#第一個構建階段
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
#第二個構建階段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
停在特定的構建階段
構建鏡像時,不一定需要構建整個Dockerfile,包括每個階段。可以指定目標構建階段。
以下命令假定您使用的是前一個Dockerfile但在名為builder的階段停止:
$ docker build --target builder -t alexellis2/href-counter:latest .
一些可能的場景:
- 調試特定的構建階段
- 使用debug階段,該階段啟用了所有調試符號或工具,以及lean production階段
- 使用testing階段,該階段應用程序填充測試數據,但使用實際數據的不同階段構建生產環境
使用外部鏡像作為“stage”
使用多階段構建時,不僅可以從先前在Dockerfile中的構建階段進行復制。還可以使用COPY --from指令從單獨的鏡像進行復制,使用本地鏡像名稱,本地或Docker倉庫中可用的標記或標記ID。
如有必要,Docker客戶端會提取鏡像,並從鏡像中復制工件。語法是:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
Dockerfile構建鏡像