Dockerfile
一、结构:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时执行指令
FROM
指明构建的新镜像是来自于哪个基础镜像,例如:
FROM centos:6
MAINTAINER
指明镜像维护着及其联系方式(一般是邮箱地址),例如:
MAINTAINER Edison Zhou <xxx@xxx.com>
不过,MAINTAINER并不推荐使用,更推荐使用LABEL来指定镜像作者,例如:
LABEL maintainer="xxxxx.cn"
RUN
构建镜像时运行的Shell命令,例如:
RUN ["yum", "install", "httpd"]
RUN yum install httpd
CMD
启动容器时执行的Shell命令,例如:
CMD ["-C", "/start.sh"]
CMD ["/usr/sbin/sshd", "-D"]
CMD /usr/sbin/sshd -D
EXPOSE
声明容器运行的服务端口,例如:
EXPOSE 80 443
ENV
设置环境内环境变量,例如:
ENV MYSQL_ROOT_PASSWORD 123456
ENV JAVA_HOME /usr/local/jdk1.8.0_45
ADD
拷贝文件或目录到镜像中,例如:
ADD <src>...<dest>
ADD html.tar.gz /var/www/html
ADD https://xxx.com/html.tar.gz /var/www/html
***PS:***如果是URL或压缩包,会自动下载或自动解压
COPY
拷贝文件或目录到镜像中,用法同ADD,只是不支持自动下载和解压,例如:
COPY ./start.sh /start.sh
ENTRYPOINT
启动容器时执行的Shell命令,同CMD类似,只是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序,例如:
ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'
***PS:***Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效。
VOLUME
指定容器挂载点到宿主机自动生成的目录或其他容器,例如:
VOLUME ["/var/lib/mysql"]
***PS:***一般不会在Dockerfile中用到,更常见的还是在docker run的时候指定-v数据卷。
USER
为RUN、CMD和ENTRYPOINT执行Shell命令指定运行用户,例如:
USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER edisonzhou
WORKDIR
为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,例如:
WORKDIR /data
HEALTHCHECK
告诉Docker如何测试容器以检查它是否仍在工作,即健康检查,例如:
HEALTHCHECK --interval=5m --timeout=3s --retries=3 \
CMD curl -f http:/localhost/ || exit 1
其中,一些选项的说明:
- –interval=DURATION (default: 30s):每隔多长时间探测一次,默认30秒
- – timeout= DURATION (default: 30s):服务响应超时时长,默认30秒
- –start-period= DURATION (default: 0s):服务启动多久后开始探测,默认0秒
- –retries=N (default: 3):认为检测失败几次为宕机,默认3次
一些返回值的说明:
- 0:容器成功是健康的,随时可以使用
- 1:不健康的容器无法正常工作
- 2:保留不使用此退出代码
ARG
在构建镜像时,指定一些参数,例如:
FROM centos:6
ARG user # ARG user=root
USER $user
这时,在docker build时可以带上自定义参数user了,如下所示:
docker build --build-arg user=xxxxx Dockerfile .
二、注意事项:
构建镜像最具挑战性的一点是使镜像体积尽可能的小
使用 .dockerignore 排除构建无关文件
.dockerignore` 语法与 `.gitignore` 语法一致。使用它排除构建无关的文件及目录,如 `node_modules
使用多阶段构建
对于多阶段构建,可以在 Dockerfile 中使用多个 FROM
语句。每个 FROM
指令都可以使用不同的基镜像,并且它们都开始了构建的新阶段,示例如下:
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"]
在这种模式下,第二个FROM会以alpine:latest镜像为基础,开始一个新的构建,其中**–from=0**意为将前一阶段的构建工件复制到此新阶段。Go SDK 和任何中间工件都会被留下,不会保存在最终的镜像中
为构建阶段命名
FROM
指令从 0 开始,也可以通过添加一个 AS <NAME>
到 FROM
指令来命名阶段,示例如下:
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)
阶段和一个精益的生产(production)
阶段 - 使用一个
测试(testing)
阶段,在这个阶段你的应用会被测试数据填充,但是在构建产品时,使用一个使用真实数据的不同阶段。
使用外部镜像作为“阶段”
当使用多阶段构建时,您不受限于从 Dockerfile 中先前创建的阶段进行复制。您可以使用 COPY --from
指令从单独的镜像中进行复制,可以使用本地镜像名称、本地或 Docker 注册表上可用的标签或标签 ID。Docker 客户端会在必要时拉取镜像并从中复制工件。语法是:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
把以前的阶段作为新的阶段
在使用 FROM
指令时,您可以引用前一阶段的内容。例如:
FROM alpine:latest as builder
RUN apk --no-cache add build-base
FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp
FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp
避免安装不必要的包
减小体积,减少构建时间。如前端应用使用 npm install --production
只装生产环境所依赖的包。
一个容器只做一件事
如一个web应用将会包含三个部分,web 服务,数据库与缓存。把他们解耦到多个容器中,方便横向扩展。如果你需要网络通信,则可以将他们至于一个网络下,示例如下:
version: '3'
services:
# 该镜像会暴露出自身的 `header` 信息
whoami:
image: containous/whoami
restart: always
labels:
# 设置Host 为 whoami.docker.localhost 进行域名访问
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
# 使用已存在的 traefik 的 network
networks:
default:
external:
name: traefik_default
减少镜像层数
- 只有
RUN
,COPY
,ADD
会创建层数, 其它指令不会增加镜像的体积 - 尽可能使用多阶段构建
使用以下方法安装依赖
RUN yum install -y node python go
错误的方法安装依赖,这将增加镜像层数
RUN yum install -y node
RUN yum install -y python
RUN yum install -y go
将多行参数排序
便于可读性以及不小心地重复装包
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
充分利用构建缓存
在镜像的构建过程中 docker
会遍历 Dockerfile
文件中的所有指令,顺序执行。对于每一条指令,docker
都会在缓存中查找是否已存在可重用的镜像,否则会创建一个新的镜像
我们可以使用 docker build --no-cache
跳过缓存
-
ADD
和COPY
将会计算文件的checksum
是否改变来决定是否利用缓存 -
RUN
仅仅查看命令字符串是否命中缓存,如RUN apt-get -y update
可能会有问题
如一个 node
应用,可以先拷贝 package.json
进行依赖安装,然后再添加整个目录,可以做到充分利用缓存的目的。
FROM node:10-alpine as builder
WORKDIR /code
ADD package.json /code
# 此步将可以充分利用 node_modules 的缓存
RUN npm install --production
ADD . /code
RUN npm run build
三、常用命令:
docker build
命令用于使用 Dockerfile 创建镜像
docker build [OPTIONS] PATH | URL | -
--build-arg=[] :设置镜像创建时的变量;
--cpu-shares :设置 cpu 使用权重;
--cpu-period :限制 CPU CFS周期;
--cpu-quota :限制 CPU CFS配额;
--cpuset-cpus :指定使用的CPU id;
--cpuset-mems :指定使用的内存 id;
--disable-content-trust :忽略校验,默认开启;
-f :指定要使用的Dockerfile路径;
--force-rm :设置镜像过程中删除中间容器;
--isolation :使用容器隔离技术;
--label=[] :设置镜像使用的元数据;
-m :设置内存最大值;
--memory-swap :设置Swap的最大值为内存+swap,"-1"表示不限swap;
--no-cache :创建镜像的过程不使用缓存;
--pull :尝试去更新镜像的新版本;
--quiet, -q :安静模式,成功后只输出镜像 ID;
--rm :设置镜像成功后删除中间容器;
--shm-size :设置/dev/shm的大小,默认值是64M;
--ulimit :Ulimit配置。
--tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
--network: 默认 default。在构建期间设置RUN指令的网络模式
示例如下,使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1:
docker build -t runoob/ubuntu:v1 .
docker commit :
从容器创建一个新的镜像
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
-a :提交的镜像作者;
-c :使用Dockerfile指令来创建镜像;
-m :提交时的说明文字;
-p :在commit时,将容器暂停。
示例如下:
docker commit -a "runoob.com" -m "my apache" a404c6c174a2 mymysql:v1
**docker tag **
标记本地镜像,将其归入某一仓库
docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
示例如下,将镜像ubuntu:15.10标记为 runoob/ubuntu:v3 镜像:
docker tag ubuntu:15.10 runoob/ubuntu:v3
Docker-compose
结构
docker-compose.yml组成一个project,project里包括多个service,每个service定义了容器运行的镜像(或构建镜像),网络端口,文件挂载,参数,依赖等,每个service可包括同一个镜像的多个容器实例,即 project 包含 service ,service 包含 container ,示例如下:
version:"3.7"
services:
webapp:
build:
context:./dir
dockerfile:Dockerfile-alternate
args:
buildno:1
version
指定 docker-compose.yml 文件的写法格式
services
多个容器集合environment:环境变量配置,可以用数组或字典两种方式
image
指定服务所使用的镜像
image: java
expose
暴露端口,但不映射到宿主机,只被连接的服务访问。
仅可以指定内部端口为参数(一般用来标识镜像使用的端口,方便用ports映射)
expose:
- "3000"
- "8000"
ports
定义宿主机端口和容器端口的映射,可使用宿主机IP+宿主机端口进行访问 宿主机端口:容器端口
ports: # 暴露端口信息 - "宿主机端口:容器暴露端口"
- "8763:8763"
- "8763:8763"
volumes
动态挂载,卷挂载路径,定义宿主机的目录/文件和容器的目录/文件的映射 宿主机路径:容器路径
volumes:
# 只需指定一个路径,让引擎创建一个卷
- /var/lib/mysql
# 指定绝对路径映射
- /opt/data:/var/lib/mysql
# 相对于当前compose文件的相对路径
- ./cache:/tmp/cache
# 用户家目录相对路径
- ~/configs:/etc/configs/:ro
# 命名卷
- datavolume:/var/lib/mysql
environment
添加环境变量,可以使用数组或字典两种形式, 任何布尔值; true,false,yes,no需要用引号括起来,以确保它们不被YML解析器转换为True或False
environment:
RACK_ENV: development
SHOW: 'true'
SESSION_SECRET:
environment:
- RACK_ENV=development
- SHOW=true
- SESSION_SECRET
depend_on
规定service加载顺序,例如数据库服务需要在后台服务前运行
version: '2'
services:
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
extra_hosts
类似于docker里的–add-host参数 配置DNS域名解析(域名和IP的映射)
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
restart
默认值为 no
,即在任何情况下都不会重新启动容器;当值为 always
时,容器总是重新启动;当值为 on-failure
时,当出现 on-failure
报错容器退出时,容器重新启动
restart: "no"
restart: always
restart: on-failure
restart: unless-stopped
privileged: true
开启特权模式
user: root
指定容器运行的用户名
networks/links
指定容器名称进行不同容器间的通信
web:
links:
- db
- db:database
- redis
logging
日志服务
logging:
driver: "gelf"
options:
gelf-address: "udp://graylogserver:12201"
tag: front-tomcat
network_mode
设置网络模式
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
build
置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 路径
注:如果指定build同时也指定image,那么会从build里构建,镜像的名字和tag将取image指定的
logging
日志服务
logging:
driver: "gelf"
options:
gelf-address: "udp://graylogserver:12201"
tag: front-tomcat
network_mode
设置网络模式
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
build
置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 路径
注:如果指定build同时也指定image,那么会从build里构建,镜像的名字和tag将取image指定的
command
覆盖容器启动后默认执行的命令
command: bundle exec thin -p 3000
----------------------------------
command: [bundle,exec,thin,-p,3000]