Dockerfile

  • 文本文件,内容为镜像每一层修改、安装、构建、操作的命令,使构建透明化

  • 基础示例:

    • 新建文件夹并新建文件,名为 Dockerfile,内容如下:

      1. FROM nginx
      2. RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
    • 构建镜像 ```

      运行命令

      docker build -t nginx:v3 .

以下为过程

Sending build context to Docker daemon 2.048kB # 将上下文中内容发送给远端 Step 1/2 : FROM nginx —-> 881bd08c0b08 Step 2/2 : RUN echo ‘

Hello, this is nginx v3!

‘ > /usr/share/nginx/html/index.html —-> Running in 04a647c3c8ce # 创建临时容器,执行 Removing intermediate container 04a647c3c8ce # 删除临时容器 —-> affe11a300bf Successfully built affe11a300bf Successfully tagged nginx:v3

  1. - 镜像构建上下文(Context
  2. - `docker build [选项] <上下文路径/URL/->`,上述命令中 `.` 即将当前目录指定为镜像构建的上下文
  3. - 构建时,上下文目录中的文件,被打包上传至 Docker引擎,由服务端完成构建。即后续 Dockerfile 中执行的 `copy` 等操作均使用相对路径写法(因此 `../` 的写法下镜像构建失败,因超出上下文范围,远端无法获取到对应文件)。可使用 `.dockerignore` 文件指定哪些需要被忽略
  4. - 其他 `docker build` 用法
  5. - 使用 git 仓库地址:`docker build [https://github.com/twang2218/gitlab-ce-zh.git#:11.1](https://github.com/twang2218/gitlab-ce-zh.git#:11.1)`docker自动clone项目、切换分支、进入相应目录,以此为上下文进行构建
  6. - 使用 tar 压缩包: `docker build http://server/context.tar.gz`, docker自动下载并解压
  7. - 标准输入输出,示例见 [教程链接](https://yeasy.gitbooks.io/docker_practice/content/image/build.html)
  8. <a name="92a348df"></a>
  9. #### 指令详解
  10. - FROM :指定基础镜像,可指定空包镜像 **scratch**
  11. - RUN : 执行命令,支持2种格式:
  12. - shell格式:`RUN <命令>`,即等同于编写 shell 命令,但需注意,每一次 `RUN` 的执行都会构建一个分层,因此运行时不需要的东西可通过 `&&` 符号串联命令,避免镜像过于臃肿(UnionFS 有最大层数限制), 也需要注意清理
  13. - exec格式:`RUN ["可执行文件", "参数1", "参数2"]`

FROM debian:stretch

RUN buildDeps=’gcc libc6-dev make wget’ \ # 支持换行符 && apt-get update \ # && 将命令串行 && apt-get install -y $buildDeps \ && wget -O redis.tar.gz “http://download.redis.io/releases/redis-5.0.3.tar.gz“ \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis —strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y —auto-remove $buildDeps # 清理缓存等

  1. - COPY :从构建上下文目录中的`源路径` 的文件或谬论复制到新一层镜像的`目标路径`位置
  2. - 源路径:可为多个,可使用通配符(正则)
  3. - 目标路径:可为容器内绝对路径,也可以相对工作目录`WORKDIR`)的相对路径,不存在会自动创建
  4. - `--chown=<user>:<group>`:改变文件所属用户及所属组

COPY —chown=55:mygroup files /mydir/ COPY —chown=bin files /mydir/ COPY —chown=1 files /mydir/ COPY —chown=10:11 files /mydir/

  1. - ADD : 更高级的文件复制,可支持 `URL`
  2. - 下载的文件权限,默认为600,若要改动则需要手动再执行 `RUN` 命令
  3. - 若下载为压缩包,`gzip``bzip2``xz` 格式可自动解压,否则需要 `RUN`
  4. - 会领镜像构建缓存失效,使镜像构建缓慢
  5. - 支持`--chown=<user>:<group>`
  6. - 复制文件尽量使用 `COPY`,语义明确,需要自动解压缩的场景下使用 `ADD`
  7. - CMD : 容器启动命令,格式同 `RUN`
  8. - shell格式:将被解析为 `sh c`,例如:`CMD echo $HOME` 解析为 `CMD [ "sh", "-c", "echo $HOME" ]`
  9. - exec格式:更为推荐,会被解析为JSON数组,需使用双引号
  10. - Docker区别于虚拟机,对于容器而言,其启动程序就是容器应用进程,主进程退出即容器退出,故容器中的应用均应在前台执行。示例:

希望 upstart 以后台守护进程形式启动nginx

实际解析为 CMD [ “sh”, “-c”, “service nginx start”],主进程为sh,执行命令后即退出

CMD service nginx start

正确做法,直接启动 nginx,要求以前台形式允许

Docker 容器启动时,默认会把容器内部第一个进程,也就是pid=1的程序,作为docker容器是否正在运行的依据,如果 docker 容器pid=1的进程挂了,那么docker容器便会直接退出。

Docker未执行自定义的CMD之前,nginx的pid是1,执行到CMD之后,nginx就在后台运行,bash或sh脚本的pid变成了1。 故需加上 daemon of(守护进程关闭)

CMD [“nginx”, “-g”, “daemon off;”]

  1. - ENTRYPOINT :指定容器启动及参数,同 `CMD`
  2. - 在运行时可替代,需要通过 `docker run` 的参数 `--entrypoint` 来指定
  3. - 当指定了 `ENTRYPOINT` 后,`CMD` 的含义就发生了改变,不再是直接的运行其命令,而是将内容作为参数传给 `ENTRYPOINT` 指令
  4. - 场景2:容器启动前,完成相应准备工作,[Redis官方镜像的例子](https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/entrypoint.html)

场景1:让镜像变成像命令一样使用

FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* CMD [ “curl”, “-s”, “https://ip.cn“ ] # 可替换为 ENTRYPOINT

构建

docker build -t myip .

启动 {“ip”: “115.199.98.64”, “country”: “浙江省杭州市”, “city”: “电信”}

docker run myip

执行错误,此时即相当于执行 CMD [“-i”]

docker run myip -i

替换为 ENTRYPOINT 后, 实际执行:ENTRYPOINT [ “curl”, “-s”, “https://ip.cn“, “-s”]

docker run myip -i

  1. - ENV : 设置环境变量
  2. - `ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD` 指令均支持环境变量

换行、含有空格的值使用引号。还有另一种格式 ENV

ENV VERSION=1.0 DEBUG=on \ NAME=”Happy Feet”

node 镜像示例,NODE_VERSION定以后,可在后续指令中使用,只需改变ENV,即可定制不同版本的node镜像

ENV NODE_VERSION 7.2.0

RUN curl -SLO “https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz“ \ && curl -SLO “https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc“ \ && gpg —batch —decrypt —output SHASUMS256.txt SHASUMS256.txt.asc \ && grep “ node-v$NODE_VERSION-linux-x64.tar.xz\$” SHASUMS256.txt | sha256sum -c - \ && tar -xJf “node-v$NODE_VERSION-linux-x64.tar.xz” -C /usr/local —strip-components=1 \ && rm “node-v$NODE_VERSION-linux-x64.tar.xz” SHASUMS256.txt.asc SHASUMS256.txt \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs

  1. - ARG : 构建参数,同 `ENV`
  2. - 所设置的环境变量,在容器运行时不存在。但使用 `docker history`命令可见
  3. - Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖
  4. - VOLUME : 定义匿名卷
  5. - 格式 `VOLUME ["<路径1>", "<路径2>"...]` `VOLUME <路径>`

定义/data 目录为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化

VOLUME /data

运行时覆盖

docker run -d -v mydata:/data xxxx

  1. - EXPOSE : 声明端口
  2. - 格式:`EXPOSE <端口1> [<端口2>...]`
  3. - Dockerfile中声明,仅用于帮助镜像使用者理解,并不真正在宿主机进行端口映射
  4. - `docker run -p <宿主端口>:<容器端口>`, 真正进行映射。若使用`docker run -P` 进行随机映射时,会自动映射 `EXPOSE` 端口
  5. - WORKDIR : 指定工作目录,不存在自动新建

2行RUN指令为2层构建,第二行命令重新启动容器,并不会存在 app/world.txt文件

需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令

RUN cd /app RUN echo “hello” > world.txt

  1. - USER : 切换后续命令执行时的身份,需事先存在
  2. - HEALTHCHECK :健康检查
  3. - `HEALTHCHECK [选项] CMD <命令>` :设置检查容器健康状况的命令
  4. - `--interval=<间隔>` :两次健康检查的间隔,默认为 30
  5. - `--timeout=<时长>` :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30
  6. - `--retries=<次数>` :当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
  7. - `HEALTHCHECK NONE` :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
  8. - 初始状态会为 `starting`,在 `HEALTHCHECK` 指令检查成功后变为 `healthy`,如果连续一定次数失败,则会变为 `unhealthy`
  9. - ONBUILD : 帮助其他镜像定制本镜像

定制本镜像 my-node,构建时,ONBUILD 三行不会被执行

FROM node:slim RUN mkdir /app WORKDIR /app ONBUILD COPY ./package.json /app ONBUILD RUN [ “npm”, “install” ] ONBUILD COPY . /app/ CMD [ “npm”, “start” ]

其他镜像使用,将执行所有命令,即各自复制本工程的代码,完成相应操作。避免每个工程拷贝一份 Dockerfile

FROM my-node

  1. <a name="ae8da901"></a>
  2. #### 多阶段构建
  3. - 镜像层次少,体积小,部署时间较短

go 示例

package main

import “fmt”

func main(){ fmt.Printf(“Hello World!”); }

一次性构建 Dockerfile

FROM golang:1.9-alpine

RUN apk —no-cache add git ca-certificates

WORKDIR /go/src/github.com/go/helloworld/

COPY app.go .

RUN go get -d -v github.com/go-sql-driver/mysql \ && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \ && cp /go/src/github.com/go/helloworld/app /root

WORKDIR /root/

CMD [“./app”]

多阶段构建 Dockerfile

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 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest as prod

RUN apk —no-cache add ca-certificates

WORKDIR /root/

COPY —from=0 /go/src/github.com/go/helloworld/app .

CMD [“./app”] ```