Dockerfile是一個文本文件,其內包含一系列指令(Instruction),每一條指令構建一層,所以每一條指令的內容就是描述該層應當如何構建。
Dockerfile文件示例以下:node
## Dockerfile文件格式 # This dockerfile uses the ubuntu image # VERSION 2 - EDITION 1 # Author: docker_user # Command format: Instruction [arguments / command] .. # 一、第一行必須指定 基礎鏡像信息 FROM centos # 二、維護者信息 MAINTAINER docker_user docker_user@email.com # 三、鏡像操做指令 RUN yum install -y nginx # 四、容器啓動執行指令 CMD /usr/sbin/nginx
Dockerfile分爲四部分:基礎鏡像信息、維護者信息、鏡像操做指令、容器啓動執行指令。第一部分必須指明基礎鏡像名稱;第二部分一般說明維護者信息;第三部分是鏡像操做指令,例如RUN指令,每執行一條RUN 指令,鏡像添加新的一層,並提交;第四部分是CMD指令,指明運行容器時的操做命令。
Dockerfile官方文檔:
https://docs.docker.com/engine/reference/builder/
Dockerfile最佳實踐文檔:
https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
Docker官方鏡像Dockerfile:
https://github.com/docker-library/docspython
FROM用於指定基礎鏡像,所以Dockerfile中FROM是必備指令,而且必須是第一條指令。
Docker Hub中有很是多的高質量的官方鏡像,有直接可用的服務類鏡像,如nginx、redis、mongo、mysql 、httpd、ph、tomcat 等;有方便開發、構建、運行各類語言應用的鏡像,如node、openjdk、 python、ruby、golang等;有基礎的操做系統鏡像,如ubuntu、debian、centos、fedora、alpine等。
Docker存在一個特殊的scratch鏡像,scratch鏡像是虛擬的概念,並不實際存在,表示一個空白的鏡像。
若是以scratch爲基礎鏡像,不以任何鏡像爲基礎,後續所寫的指令將做爲鏡像第一層開始存在。不以任何系統爲基礎,直接將可執行文件複製進鏡像如swarm、coreos/etcd。Linux下靜態編譯的程序並不須要有操做系統提供運行時支持,所需的一切庫都已經在可執行文件裏,所以直接FROM scratch會讓鏡像體積更加小巧。
FROM語法格式爲:mysql
FROM <image> FROM <image>:<tag> FROM <image>:<digest>
FROM限制以下:
A、FROM必須是Dockerfile中第一條非註釋命令
B、在一個Dockerfile文件中建立多個鏡像時,FROM能夠屢次出現。只需在每一個新命令FROM前,記錄提交上次的鏡像ID。
C、tag或digest是可選的,若是不使用tag值時,會使用latest版本的基礎鏡像。linux
在鏡像的構建過程當中執行特定的命令,並生成一箇中間鏡像。格式:ios
#shell格式 RUN <command> #exec格式 RUN ["executable", "param1", "param2"]
RUN命令將在當前image中執行任意合法命令並提交執行結果。命令執行提交後,就會自動執行Dockerfile中的下一個指令。
RUN指令建立的中間鏡像會被緩存,並會在下次構建中使用。若是不想使用緩存鏡像,能夠在構建時指定--no-cache參數,如:docker build --no-cache。nginx
COPY指令語法格式:git
COPY <源路徑>... <目標路徑> COPY ["<源路徑1>",... "<目標路徑>"]
COPY 指令將從構建上下文目錄中<源路徑>的文件/目錄複製到新的一層的鏡像內的<目標路徑>位置。
<源路徑>能夠是多個,甚至能夠是通配符,其通配符規則要知足Go的filepath.Match規則,如:github
COPY hom* /mydir/ COPY hom?.txt /mydir/
<目標路徑>能夠是容器內的絕對路徑,也能夠是相對於工做目錄的相對路徑(工做目錄能夠用WORKDIR指令來指定)。目標路徑不須要事先建立,若是目錄不存在會在複製文件前先行建立缺失目錄。
使用COPY指令,源文件的各類元數據都會保留,好比讀、寫、執行權限、文件變動時間等。golang
ADD指令在COPY基礎上增長了一些功能,好比<源路徑>能夠是一個URL,Docker引擎會試圖去下載URL連接的文件放到<目標路徑>。
在構建鏡像時,複製上下文中的文件到鏡像內,格式:web
ADD <源路徑>... <目標路徑> ADD ["<源路徑>",... "<目標路徑>"]
若是Docker發現文件內容被改變,則後續指令都不會再使用緩存。
ADD指令會將相應文件增長到目標目錄,若是源文件是壓縮文件會進行解壓操做。
CMD用於指定在容器啓動時所要執行的命令。CMD有三種格式:
CMD ["executable","param1","param2"] CMD ["param1","param2"] CMD command param1 param2
默認爲exec格式,使CMD中的參數當作ENTRYPOINT的默認參數,此時ENTRYPOINT應該是exec格式。
若是CMD是/bin/bash,使用docker run -it ubuntu啓動容器時,會直接執行進入bash。docker run -it ubuntu cat /etc/os-release會在啓動容器時輸出系統版本信息。
在CMD指令格式上,推薦使用exec格式,exec格式在解析時會被解析爲JSON數組,所以必須使用雙引號,而不要使用單引號。
若是使用shell格式,CMD命令會被包裝爲sh -c的參數的形式進行執行。好比:
CMD echo $HOME會將其變動爲:CMD ["sh", "-c","echo $HOME"]
exec格式不能使用shell中的環境變量,若是要使用shell中環境變量,須要在exec格式命令中指定使用shell腳本。
ENTRYPOINT指令用於給容器配置一個可執行程序。每次使用鏡像建立容器時,經過ENTRYPOINT指定的程序都會被設置爲默認程序。ENTRYPOINT有兩種形式:
ENTRYPOINT ["executable", "param1", "param2"] ENTRYPOINT command param1 param2
經過docker run執行的命令不會覆蓋ENTRYPOINT,而docker run命令中指定的任何參數,都會被當作參數再次傳遞給ENTRYPOINT。Dockerfile中只容許有一個ENTRYPOINT命令,多指定時會覆蓋前面的設置,而只執行最後的ENTRYPOINT指令。
docker run運行容器時指定的參數都會被傳遞給ENTRYPOINT,且會覆蓋 CMD命令指定的參數。執行docker run <image> -d
時,-d指定的參數將被傳遞給入口點,也能夠經過docker run --entrypoint重寫ENTRYPOINT入口點。ENTRYPOINT ["/usr/bin/nginx"]
ENV指令用於設置環境變量,後續的指令能夠直接使用。
ENV <key> <value> ENV <key1>=<value1> <key2>=<value2>...
ENV示例以下:
ENV VERSION=1.0 DEBUG=on \ NAME="Happy Feet"
ARG指令用於指定傳遞給構建運行時的變量。ARG <name>[=<default value>]
經過ARG指定兩個變量:
ARG site ARG build_user=scorpio
上述指令指定site和build_user兩個變量,其中build_user指定了默認值。使用docker build構建鏡像時,能夠經過--build-arg <varname>=<value>
選項參數來指定或重設置相應變量的值。docker build --build-arg site=www.baidu.com -t baidu/test .
build_user變量使用默認值scorpio。
VOLUME指令用於建立掛載點,即向基於所構建鏡像創始的容器添加捲:VOLUME ["/data"]
一個卷能夠存在於一個或多個容器的指定目錄,該目錄能夠繞過聯合文件系統,並具備如下功能:
A、卷能夠容器間共享和重用
B、容器並不必定要和其它容器共享卷
C、修改卷後會當即生效
D、對卷的修改不會對鏡像產生影響
E、卷會一直存在,直到沒有任何容器在使用它
VOLUME能夠將源代碼、數據或其它內容添加到鏡像中,而不提交到鏡像中,並使多個容器間共享數據。
EXPOSE指令爲構建的鏡像設置監聽端口,使容器在運行時監聽。格式以下:EXPOSE <port> [<port>...]
EXPOSE指令並不會讓容器監聽host的端口,若是須要容器監聽Host端口,須要在docker run時使用 -p、-P 參數來發布容器端口到host的某個端口上。
WORKDIR指令用於在容器內設置一個工做目錄。WORKDIR /path/to/workdir
經過WORKDIR設置工做目錄後,Dockerfile中的後續命令RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都會在工做目錄下執行。
WORKDIR /a WORKDIR b WORKDIR c RUN pwd
pwd最終將會在 /a/b/c 目錄中執行。使用docker run運行容器時,能夠經過-w參數覆蓋構建時所設置的工做目錄。
WORKDIR指令推薦使用絕對路徑。
USER指令用於指定運行鏡像所使用的用戶。USER daemon
使用USER指定用戶時,可使用用戶名、UID 或 GID,或是二者的組合。
USER user USER user:group USER uid USER uid:gid USER user:gid USER uid:group
使用USER指定用戶後,Dockerfile中的後續命令RUN、CMD、ENTRYPOINT 都將使用該用戶。鏡像構建完成後,經過docker run 運行容器時,能夠經過-u參數來覆蓋所指定的用戶。
HEALTHCHECK [OPTIONS] CMD command
經過運行一個容器內部的命令來檢測容器是否健康HEALTHCHECK NONE
關閉任何來自基礎image的健康檢測
options
--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--retries=N (default: 3)
ONBUILD指令用於設置鏡像觸發器。ONBUILD [INSTRUCTION]
當所構建的鏡像被用做其它鏡像的基礎鏡像,鏡像中的觸發器將會被觸發。當鏡像被使用時,可能須要作一些處理:
[...] ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src [...]
LABEL指令用於爲鏡像添加元數據,元數以鍵值對的形式指定。LABEL <key>=<value> <key>=<value> <key>=<value> ...
使用LABEL指定元數據時,一條LABEL指定能夠指定一或多條元數據,指定多條元數據時不一樣元數據之間經過空格分隔。推薦將全部的元數據經過一條LABEL指令指定,以避免生成過多的中間鏡像。 LABEL version="1.0" description="hello world" by="scorpio"
LABEL指定的元數據能夠經過docker inspect查看。
STOPSIGNAL指令用於設置中止容器所要發送的系統調用信號。STOPSIGNAL signal
所使用的信號必須是內核系統調用表中的合法的值,如:SIGKILL。
SHELL指令用於設置執行命令(shell)所使用的的默認shell類型。SHELL ["executable", "parameters"]
SHELL在Windows環境下比較有用,Windows下一般會有cmd和 powershell兩種shell,能夠經過SHELL來指定所使用的shell類型。
docker build命令會根據Dockerfile文件及上下文構建新Docker鏡像。構建上下文是指Dockerfile所在的本地路徑或一個URL(Git倉庫地址)。構建上下文環境會被遞歸處理,因此構建所指定的路徑還包括子目錄,而URL還包括其中指定的子模塊。
構建會在Docker後臺守護進程(daemon)中執行,而不是CLI中。構建前,構建進程會將所有內容(遞歸)發送到守護進程。一般,應該將一個空目錄做爲構建上下文環境,並放入Dockerfile文件。
在構建上下文中使用的Dockerfile文件是一個構建指令文件。爲了提升構建性能,能夠經過.dockerignore文件排除上下文目錄下不須要的文件和目錄。
在Docker構建鏡像的第一步,docker CLI會先在上下文目錄中尋找.dockerignore文件,根據.dockerignore 文件排除上下文目錄中的部分文件和目錄,而後把剩下的文件和目錄傳遞給Docker服務。
Dockerfile文件通常位於構建上下文的根目錄下,也能夠經過-f指定Dockerfile文件的位置:docker build -f /path/to/a/Dockerfile .
構建時,還能夠經過-t參數指定構建成鏡像的倉庫、標籤。若是存在多個倉庫下,或使用多個鏡像標籤,就可使用多個-t參數:docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .
在Docker守護進程執行Dockerfile中的指令前,首先會對Dockerfile進行語法檢查,有語法錯誤時會返回錯誤提示信息。
Dockerfile文件:
FROM ubuntu:14.04 ADD run.sh / VOLUME /data CMD ["./run.sh"]
Dockerfile文件構建的容器以下:
同構鏡像構建是指鏡像構建環境與運行環境兼容。
同構鏡像構建通常要求編譯環境與鏡像所使用的base image是兼容的,好比在Ubuntu 14.04上編譯應用,並將應用打入基於ubuntu系列base image的鏡像。由於應用的編譯環境與其部署運行的環境是兼容的,在Ubuntu 14.04下編譯出來的應用,能夠基本無縫地在基於ubuntu:14.04及後續版本base image鏡像中運行;但在不徹底兼容的base image中,好比CentOS中就可能會運行失敗。
package main import ( "net/http" "log" "fmt" ) func home(w http.ResponseWriter, req *http.Request) { w.Write([]byte("Welcome to this website!\n")) } func main() { http.HandleFunc("/", home) fmt.Println("Webserver start") fmt.Println(" -> listen on port:1111") err := http.ListenAndServe(":1111", nil) if err != nil { log.Fatal("ListenAndServe:", err) } }
編譯:go build -o httpserver httpserver.go
Dockerfile文件:
From ubuntu:14.04 COPY ./httpserver /root/httpserver RUN chmod +x /root/httpserver WORKDIR /root ENTRYPOINT ["/root/httpserver"]
構建httpserver服務鏡像:docker build -t httpserver:latest .
啓動httpserver服務容器:docker run httpserver
基於ubuntu基礎鏡像構建出的應用鏡像太過臃腫,所以有必要基於golang:latest構建本身專用的golang-builder image,Dockerfile.build能夠用於build golang-builder image:
FROM golang:latest WORKDIR /go/src COPY httpserver.go . RUN go build -o httpserver ./httpserver.go
構建golang-builder鏡像:docker build -t golang-builder:latest -f Dockerfile.build .
從golang-builder建立一個容器appsourcedocker create --name appsource golang-builder:latest
從appsource容器中將httpserver拷貝到主機當前目錄docker cp appsource:/go/src/httpserver ./
刪除appsource容器docker rm -f appsource
刪除golang-builder鏡像docker rmi golang-builder:latest
從當前目錄構建出httpserver鏡像docker build -t httpserver:latest .
httpserver鏡像的大小依舊停留在200MB。要想減少httpserver鏡像的大小,必須使用更小的base image,即alpine 。 Alpine image的大小不到4M,再加上應用的size,最終應用鏡像的大小估計能夠縮減到20M如下。
Dockerfile.alpine 文件:
From alpine:latest COPY ./httpserver /root/httpserver RUN chmod +x /root/httpserver WORKDIR /root ENTRYPOINT ["/root/httpserver"]
構建alpine版應用鏡像:docker build -t httpserver-alpine:latest -f Dockerfile.alpine .
啓動httpserver-alpine容器會失敗,由於alpine image並不是ubuntu環境的同構image。
異構鏡像構建是指構建環境與運行環境不兼容。
Go將runtime中的C代碼都用Go重寫,對libc的依賴已經降到最低,但提供了兩個版本的實現:C實現和Go實現。默認狀況下,即在CGO_ENABLED=1狀況下,程序和預編譯的標準庫都採用C實現。所以採用不一樣libc實現的debian系和alpine系天然存在不兼容的狀況。考慮異構鏡像建立首先對Go程序進行靜態構建,而後將靜態構建後的Go應用放入alpine image中。
Dockerfile.build文件以下:
FROM golang:alpine WORKDIR /go/src COPY httpserver.go . RUN go build -o httpserver ./httpserver.go
構建builder鏡像:
docker build -t myrepo/golang-static-builder:latest -f Dockerfile.build . docker create --name appsource golang-static-builder:latest docker cp appsource:/go/src/httpserver ./ docker rm -f appsource docker rmi golang-static-builder:latest docker build -t httpserver-alpine:latest -f Dockerfile.alpine .
運行httpserver服務容器:docker run httpserver-alpine:latest
alpine版golang builder鏡像Dockerfile:
FROM golang:alpine WORKDIR /go/src COPY httpserver.go . RUN go build -o httpserver ./httpserver.go
2017年5月發佈的 Docker 17.05.0-ce 中,Docker官方提供了簡便的多階段構建(multi-stage build)方案。
對於多階段構建,能夠在Dockerfile中使用多個FROM語句。每一個FROM指令可使用不一樣的基礎鏡像,做爲一個構建階段,多條 FROM 就是多階段構建,雖然最後生成的鏡像只能是最後一個階段的結果,但可以將前邊階段中的文件拷貝到後邊的階段中。
多階段構建最大的使用場景是將編譯環境和運行環境分離。
# 編譯階段 FROM golang:1.10.3 COPY server.go /build/ WORKDIR /build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server # 運行階段 FROM scratch # 從編譯階段中拷貝編譯結果到當前鏡像中 COPY --from=0 /build/server / ENTRYPOINT ["/server"]
Dockerfile的COPY指令--from=0參數,從前邊的階段中拷貝文件到當前階段中,多個FROM語句時,0表明第一個階段。除了使用數字,還能夠給階段命名,好比:
# 編譯階段 FROM golang:1.10.3 as builder COPY server.go /build/ WORKDIR /build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server # 運行階段 FROM scratch # 從編譯階段的中拷貝編譯結果到當前鏡像中 COPY --from=builder /build/server / ENTRYPOINT ["/server"]
COPY –from指令從單獨的image中複製,使用本地image名稱,本地或Docker鏡像倉庫中可用的標記或標記ID。
構建映像時,不必定須要構建整個Dockerfile文件的每一個階段,能夠指定目標構建階段。docker build --target builder -t builder:latest .
使用Dockerfile構建,在builder階段中止。
停在特定構建階段很是適合的場景以下:
A、調試特定的構建階段
B、在debug階段,啓用全部調試或工具,而在production階段儘可能精簡
C、在testing階段,應用程序將填充測試數據,但在production階段則使用生產數據
利用多階段構建能夠多個項目的二進制文件構建在一個鏡像中發佈。
from debian as build-essential arg APT_MIRROR workdir /src from build-essential as A copy srcA . run make from build-essential as B copy srcB . run make from alpine copy --from=A binA . copy --from=B binB . cmd ...
HelloDocker.cpp文件以下:
#include <iostream> int main() { std::cout << "Hello, Docker!" << std::endl; return 1; }
docker search gcc
包含多種版本的gcc,包括嵌入式版本的gcc-arm-embedded-docker
docker pull gcc
docker images
Dockerfile文件編寫:
FROM gcc:latest RUN mkdir -p /home/user/docker/src/HelloDocker COPY HelloDocker.cpp /home/user/docker/src/HelloDocker WORKDIR /home/user/docker/src/HelloDocker RUN g++ HelloDocker.cpp -o HelloDocker CMD ["./HelloDocker"]
使用Dockerfile文件建立鏡像:docker build -t hellodocker:v1 .
鏡像查看:docker images
啓動鏡像:docker run -d hellodocker:v1
容器運行狀況查看docker ps
Dockerfile文件編寫:
FROM gcc:latest RUN mkdir -p /home/user/docker/HelloDocker COPY HelloDocker /home/user/docker/HelloDocker WORKDIR /home/user/docker/HelloDocker #RUN g++ HelloDocker.cpp -o HelloDocker CMD ["./HelloDocker"]
構建鏡像:docker build -t hellodocker:v1 .
啓動容器:docker run -d hellodocker:v1