Dockerfile的编写与构建

基本写法

  # 说明该镜像以哪个镜像为基础
  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或者RUNexec格式时,不会引用环境变量
    • 如果想要使用环境变量,应该修改为 CMD ["/bin/sh", "-c", "ls", "-a", "-l"]
    • shell格式实际上是执行了一个shell进程,环境变量会被shell解析
  • 如果想要运行一个守护进程,应该以exec格式执行
    • 如果以shell格式执行,shell进程在执行完启动命令后就退出了,容器也就结束了
    • shell格式执行时的init进程(pid=1))是shell,而不是要执行的指令
    • docker stop无法暂停,因为其发送的SIGTERM信号只被init进程接受
  • 如果同时存在ENTRYPOINTCMD命令,则CMD作为ENTRYPOINT 的参数
  • 在Dockerfile中只能有一个CMD或者ENTRYPOINT
    • 如果列出多个CMD,则只有最后一个CMD将生效
  • 环境变量被设置完后,后面的命令(例如RUN)可以直接使用
  • CMD,VOLUME等参数都可以在启动时被覆盖
  • WORKDIRENV一样,在设置完成后影响后面的命令
    • 可以多次设置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 命令来更新仓库信息

参考

发布了161 篇原创文章 · 获赞 19 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/winter_wu_1998/article/details/103648349