基本写法
# 说明该镜像以哪个镜像为基础
FROM ubuntu:latest
# 构建者的基本信息
MAINTAINER wwt
# 拷贝文件到指定目录或文件,如果目录不存在则自动创建(目的位置需要为绝对路径或者相对于workdir)
# 如果<src>是目录,则复制目录的整个内容(目录本身不被复制,只是其内容)
# <src>必须是上下文根目录的想退路径,可以使用通配符
# 如果<dest>不以/结尾,则表示把<src>中的内容写入dest文件
# <src>可以指定多个,此时dest必须是目录
COPY <src> <dest>
# 指定下面所有指令的工作目录,也是我们进入容器后的目录
WORKDIR /go/src/
#创建容器卷,匿名映射到宿主机
VOLUME ["/data1","/data2"]
# 在build这个镜像时执行的操作,默认为/bin/sh
RUN apt update && \
apt install git
# 要使用不同的shell,而不是/bin/sh,请使用在所需shell中传递的exec形式
# exec格式必须使用双引号(因为被当成json解析)
RUN [“/bin/bash”,“-c”,“echo hello”]
# 设置环境变量
ENV <key1>=<val1> <key2>=<val2>
# 暴露端口
EXPOSE 22 80
# 健康检查;1.12版本引入
# 每60秒检查一次,3秒无响应则视为失败,失败3次后容器状态变为unhealthy
HEALTHCHECK --interval=60s--timeout=3s --retries=3 \
CMD curl -fs http://localhost/ || exit 1 # 检查命令;0成功,1失败
# 设置后后面指令运行用户;该用户必须事先已经存在
USER wwt
# 默认启动命令,可以在docker run时覆盖;只有最后一个CMD有效
CMD ["ls", "-a", "-l"] # exec格式
CMD ls -a -l # shell格式
- 不常修改的层应该在经常修改的层前面,以利用缓存
- 层数是有最大限制的,因此最好不要每个shell命令都使用RUN前缀,而是合为一个
- 使用
&&
将各个所需命令串联起来 - 行尾添加
\
的命令换行方式
- 使用
ADD
命令和COPY
命令最大的不同在于会自动解压压缩文件以及获取远程文件- 更推荐使用
RUN wget
而不是ADD
获取 - 推荐使用volume共享文件,而不是把文件添加到镜像中
- 更推荐使用
EXPOSE
暴露的端口需要在启动时通过-P
参数随机映射到宿主机端口- 不使用
-P
参数则不会暴露 - 想要通过
--link
参数连接容器,必须在dockerfile中暴露端口
- 不使用
CMD
或者RUN
为exec格式时,不会引用环境变量- 如果想要使用环境变量,应该修改为
CMD ["/bin/sh", "-c", "ls", "-a", "-l"]
- shell格式实际上是执行了一个shell进程,环境变量会被shell解析
- 如果想要使用环境变量,应该修改为
- 如果想要运行一个守护进程,应该以exec格式执行
- 如果以shell格式执行,shell进程在执行完启动命令后就退出了,容器也就结束了
- shell格式执行时的init进程(pid=1))是shell,而不是要执行的指令
docker stop
无法暂停,因为其发送的SIGTERM
信号只被init进程接受
- 如果同时存在
ENTRYPOINT
和CMD
命令,则CMD
作为ENTRYPOINT
的参数 - 在Dockerfile中只能有一个
CMD
或者ENTRYPOINT
- 如果列出多个
CMD
,则只有最后一个CMD
将生效
- 如果列出多个
- 环境变量被设置完后,后面的命令(例如
RUN
)可以直接使用 CMD
,VOLUME
等参数都可以在启动时被覆盖WORKDIR
和ENV
一样,在设置完成后影响后面的命令- 可以多次设置
WORKDIR
,以影响不同的命令 - 如果提供了相对路径,它将相对于先前
WORKDIR
指令的路径
- 可以多次设置
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # 输出/a/b/c
- 容器的每一行命令和上一行命令不是同一个执行环境
- 因此如果后面的命令对前面的有依赖应该写在同一行
- 例如
RUN apt update && apt install -y ssh
# 容器构建完成后会发现不存在 /app/world.txt文件
RUN cd /app
RUN echo "hello" > world.txt
- 通过
VOLUME
挂载目录后,如果尝试对这个volume进行修改,这些修改都不会生效- 因为每一层的临时镜像都是通过commit构建的,而commit时不会对挂载的volume进行保存
- 解决方法
- 先创建并修改要挂载的目录,此后在挂载时会把已经存在的内容复制到volume中
- 也可以把修改命令放在
ENTRYPOINT
或者CMD
中,因为这两个指令是在容器启动时执行的
RUN mkdir /data && touch /data/file
RUN chmod +x /data/file
VOLUME /data
# 或者
VOLUME /data
CMD touch /data/file && chmod +x /data/file
- 1.12版本引入了健康检查选项
HEALTHCHECK [选项] CMD <命令>
- 之前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。
- 健康检查命令的输出(包括 stdout 以及 stderr)都会被存储于健康状态里,可以用 docker inspect 来查看
docker inspect --format '{{json .State.Health}}' <docker>
多阶段构建
- Docker v17.05 开始支持
- 镜像体积更小
FROM golang:1.9-alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN GOARCH=amd64 GOOS=linux go build app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/go/helloworld/app .
CMD ["./app"]
- 一个Dockerfile中可以存在多个
FROM
, 最后一个FROM生成最终镜像 - 使用
as
来为某一阶段命名,例如上面的builder
- 只想构建
builder
阶段的镜像时,go bulid
增加--target=builder
参数即可 COPY --from
不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝- 例如
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
- 例如
通过Dockerfile构建镜像
docker build -t wwt/ubuntu:git .
# 使用Dockerfile.debug在当前目录构建镜像
docker build -f Dockerfile.debug .
# 把/home/me/myapp用作构建上下文的根
docker build -f /home/me/myapp/dockerfiles/debug /home/me/myapp
-t
指定要创建的镜像名- 最后的
.
表示使用当前目录构建上下文(context) - 默认在context的根目录寻找Dockerfile
- 也可以使用
-f
参数指定Dockerfile 的绝对路径
- 也可以使用
- 构建新镜像时,每一层指令都会构建一个临时镜像
- 每个指令都会利用上一层的临时镜像创建出一个临时容器,并在临时容器中执行指令,然后通过commit生成临时镜像
- 如果构建失败,可以利用上一层的临时镜像创建容器,并手动执行接下来的命令来调试错误
- 构建由Docker守护程序运行,而不是由CLI运行。构建过程所做的第一件事是将整个context (递归地) 发送给守护进程
常用镜像
busybox
- 含有许多常用Linux工具,体积很小
alpine
- 推荐使用 Alpine 替代Ubuntu 做为基础镜像环境
- 镜像下载速度加快,镜像安全性提高,主机之间的切换更方便,占用更少磁盘空间等
- alpine提供了自己的包管理工具 apk
- 通过 https://pkgs.alpinelinux.org/packages 网站上查询包信息
- 如果需要的安装包不在主索引内,但是在测试或社区索引中,那么可以按照以下方法使用这些安装包
echo "http://dl-4.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk --update add --no-cache <package>
- apk常用命令
apk update #更新最新本地镜像源
apk upgrade #升级软件
apk add --upgrade busybox #指定升级部分软件包
apk search #查找所以可用软件包
apk search -v #查找所有可用软件包及其描述内容
apk search -v 'acf*' #通过软件包名称查找软件包
apk search -v -d 'docker' #通过描述文件查找特定的软件包
apk info #列出所有已安装的软件包
ubuntu
- 当试图直接使用 apt-get 安装一个软件的时候,会提示
E: Unable to locate package
- Docker 镜像在制作时为了精简清除了 apt 仓库信息,因此需要先执行
apt update
命令来更新仓库信息
- Docker 镜像在制作时为了精简清除了 apt 仓库信息,因此需要先执行