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 Store有很是多的高质量的官方镜像,有直接可用的服务类镜像,如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是可选的,若是不使用这两个值时,会使用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发现文件内容被改变,则后续指令都不会再使用缓存。
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"]
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参数覆盖构建时所设置的工做目录。
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文件的每一个阶段,能够指定目标构建阶段。使用Dockerfile构建,在builder阶段中止:docker build --target builder -t builder:latest .
停在特定构建阶段很是适合的场景以下:
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