Dockerfile 编写与构建

常用命令

  • FROM: 基础镜像
  • MAINTAINER: 构建者
  • USER: 设置后面指令运行用户
    • 该用户必须事先已经存在
  • COPY <src> <dest>: 拷贝宿主机文件或目录到容器
    • 如果目录不存在则自动创建
    • <dest>需要是绝对路径或者相对于workdir
      • <src>是文件时
        • 如果<dest>/结尾,则视为复制到<dest>目录,否则视为改名为<dest>文件
      • <src>是目录时
        • 只是把<src>中的所有文件复制到<dest>目录下
        • 如果需要把复制目录,需要在<dest>路径后创建该目录
          • 复制./dir目录到容器/test路径下:COPY ./dir /test/dir
    • <src>必须是上下文根目录的相对路径,可以使用通配符
      • 可以指定多个<src>
  • WORKDIR: 指定后面指令的工作目录,也是我们进入容器后的目录
    • 可以多次设置,会覆盖前面的
      • 但是如果是相对路径,它将相对于先前WORKDIR指令的路径
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # 输出/a/b/c
  • VOLUME : 创建容器卷,匿名映射到宿主机
    • 为了保持环境无关性,不能绑定指定宿主机目录
    • 可以在启动时被同名路径覆盖
    • 通过VOLUME挂载目录后,如果尝试对这个volume进行修改,这些修改都不会生效
      • 因为每一层的临时镜像都是通过commit构建的,而commit时不会对挂载的volume进行保存
      • 解决方法
        • 先创建并修改要挂载的目录,此后在挂载时会把已经存在的内容复制到volume
        • 也可以把修改命令放在ENTRYPOINT或者CMD
          • 因为这两个指令是在容器启动时执行的
# 法1
RUN mkdir /data && touch /data/file
RUN chmod +x /data/file
VOLUME /data

# 法2
VOLUME /data
CMD touch /data/file && chmod +x /data/file
  • RUN: 构建镜像过程中执行的操作
    • shell格式RUN echo hello
      • 使用/bin/sh执行命令
      • 实际上是执行了一个shell进程,环境变量会被shell解析
    • exec格式RUN [“echo”, “hello”]
      • 执行时不会引用环境变量
      • exec格式必须使用双引号
        • 因为被当成json解析
      • 使用不同的shell
        • RUN [“bin/bash”, “-c”, “echo hello”]
        • 此时会引用环境变量
    • 如果想要运行一个守护进程,应该以exec格式执行
      • 如果以shell格式执行,shell进程在执行完启动命令后就退出了,容器也就结束了
    • shell格式执行时的init进程(pid=1))是shell,而不是要执行的指令
      • docker stop无法暂停,因为其发送的SIGTERM信号只被init进程接受
  • ENV <k1>=<v1> <k2>=<v2>: 设置环境变量
    • 环境变量被设置完后,后面的命令(例如RUN)可以直接使用
    • 还可以使用外部的环境变量
  • EXPOSE <p1> <p2>:暴露端口
    • 需要运行时使用-P参数
  • HEALTHCHECK [选项] CMD <命令>:健康检查,1.12版本引入
    • 命令返回0成功,1失败
    • 之前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常
      • 如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了
    • 健康检查命令的输出(包括 stdout 以及 stderr)都会被存储于健康状态里,可以用 docker inspect 来查看:
      • docker inspect --format '{{json .State.Health}}' <docker>
    • 例子:每60秒检查一次,3秒无响应则视为失败,失败3次后容器状态变为unhealthy
      • HEALTHCHECK --interval=60s --timeout=3s --retries=3 CMD curl -fs http://localhost/ || exit 1
  • CMD: 默认启动命令
    • 可以在docker run时覆盖
    • 如果存在多个CMD,则只有最后一个CMD将生效
    • 有shell和exec两种格式
  • ENTRYPOINT: 默认启动命令
    • 不可以在docker run时覆盖
    • 如果同时存在ENTRYPOINTCMD命令,则CMD视为ENTRYPOINT 的参数
    • 有shell和exec两种格式

注意点

  • 不常修改的层应该在经常修改的层前面,以利用缓存

  • 层数是有最大限制的,因此最好不要每个shell命令都使用RUN前缀,最好合并为一个

    • 使用&&将各个所需命令串联起来
    • 行尾添加\ 的命令换行方式
  • ADD命令和COPY命令最大的不同在于会自动解压压缩文件以及获取远程文件

    • 更推荐使用RUN wget而不是ADD获取
  • 推荐使用volume共享文件,而不是把文件添加到镜像中

  • 想要通过--link参数连接容器,必须在Dockerfile中暴露端口

  • 容器的每一行命令和上一行命令不是同一个执行环境

    • 因此如果后面的命令对前面的有依赖应该写在同一行
# 正面例子
RUN apt update && apt install -y ssh

# 反面例子
# 容器构建完成后会发现不存在 /app/world.txt文件
RUN cd /app
RUN echo "hello" > world.txt
  • 在这里插入图片描述
    在这里插入图片描述

多阶段构建

  • 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 CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build app .


FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# 避免安全风险
RUN adduser -u 10001 -D runner
USER runner
COPY --from=builder /go/src/github.com/go/helloworld/app .
CMD ["./app"] 

  • 为了提高安全性,应该避免root用户启动
    • 但此时应该提前授予目录权限,否则会permission denied
  • 一个Dockerfile中可以存在多个FROM, 最后一个FROM生成最终镜像
  • 使用as来为某一阶段命名,例如上面的builder
  • 只想构建builder阶段的镜像时,go bulid增加 --target=builder 参数即可
  • COPY --from 不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝
    • 例如COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
      在这里插入图片描述

构建镜像

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 (递归地)复制给守护进程
    • 即使文件没有被使用也会被复制
    • 因此如果context有到大文件的话构建会很慢

常用镜像

busybox

  • 含有许多常用Linux工具,体积很小

alpine

  • 如果想要在该镜像运行go程序,在builder镜像中build时,必须加上CGO_ENABLED=0前缀
  • 推荐使用 Alpine 替代Ubuntu 做为基础镜像环境
    • 镜像下载速度加快,镜像安全性提高,主机之间的切换更方便,占用更少磁盘空间等
  • alpine提供了自己的包管理工具 apk
    • 通过 https://pkgs.alpinelinux.org/packages 网站上查询包信息
    • 如果需要的安装包不在主索引内,但是在测试或社区索引中,那么可以按照以下方法使用这些安装包
  • alpine中没有/bin/bash,只有/bin/sh
echo "http://dl-4.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk --update add --no-cache <package> 
  • alpine中没有curl,只有wget
    • 需要预先使用RUN apk update && apk upgrade && \ apk add --no-cache bash git curl命令安装
  • apk常用命令
apk update #更新最新本地镜像源
apk upgrade #升级软件
apk add [--no-cache] # 添加软件
apk add --upgrade busybox #指定升级部分软件包
apk search #查找所以可用软件包
apk search -v #查找所有可用软件包及其描述内容
apk search -v 'acf*' #通过软件包名称查找软件包
apk search -v -d 'docker' #通过描述文件查找特定的软件包
apk info #列出所有已安装的软件包

ubuntu

  • 当试图直接使用 apt 安装一个软件的时候,会提示 E: Unable to locate package
    • Docker 镜像在制作时为了精简清除了 apt 仓库信息,因此需要先执行 apt update 命令来更新仓库信息

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值