- Dockerfile
- 运行命令
- 以下为过程
- 希望 upstart 以后台守护进程形式启动nginx
- 实际解析为 CMD [ “sh”, “-c”, “service nginx start”],主进程为sh,执行命令后即退出
- 正确做法,直接启动 nginx,要求以前台形式允许
- Docker 容器启动时,默认会把容器内部第一个进程,也就是pid=1的程序,作为docker容器是否正在运行的依据,如果 docker 容器pid=1的进程挂了,那么docker容器便会直接退出。
- Docker未执行自定义的CMD之前,nginx的pid是1,执行到CMD之后,nginx就在后台运行,bash或sh脚本的pid变成了1。 故需加上 daemon of(守护进程关闭)
- 场景1:让镜像变成像命令一样使用
- 构建
- 启动 {“ip”: “115.199.98.64”, “country”: “浙江省杭州市”, “city”: “电信”}
- 执行错误,此时即相当于执行 CMD [“-i”]
- https://ip.cn“, “-s”]">替换为 ENTRYPOINT 后, 实际执行:ENTRYPOINT [ “curl”, “-s”, “https://ip.cn“, “-s”]
- 换行、含有空格的值使用引号。还有另一种格式 ENV
- node 镜像示例,NODE_VERSION定以后,可在后续指令中使用,只需改变ENV,即可定制不同版本的node镜像
- 定义/data 目录为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化
- 运行时覆盖
- 2行RUN指令为2层构建,第二行命令重新启动容器,并不会存在 app/world.txt文件
- 需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令
- 定制本镜像 my-node,构建时,ONBUILD 三行不会被执行
- 其他镜像使用,将执行所有命令,即各自复制本工程的代码,完成相应操作。避免每个工程拷贝一份 Dockerfile
- go 示例
- 一次性构建 Dockerfile
- 多阶段构建 Dockerfile
Dockerfile
文本文件,内容为镜像每一层修改、安装、构建、操作的命令,使构建透明化
基础示例:
以下为过程
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
- 镜像构建上下文(Context)- `docker build [选项] <上下文路径/URL/->`,上述命令中 `.` 即将当前目录指定为镜像构建的上下文- 构建时,上下文目录中的文件,被打包上传至 Docker引擎,由服务端完成构建。即后续 Dockerfile 中执行的 `copy` 等操作均使用相对路径写法(因此 `../` 的写法下镜像构建失败,因超出上下文范围,远端无法获取到对应文件)。可使用 `.dockerignore` 文件指定哪些需要被忽略- 其他 `docker build` 用法- 使用 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项目、切换分支、进入相应目录,以此为上下文进行构建- 使用 tar 压缩包: `docker build http://server/context.tar.gz`, docker自动下载并解压- 标准输入输出,示例见 [教程链接](https://yeasy.gitbooks.io/docker_practice/content/image/build.html)<a name="92a348df"></a>#### 指令详解- FROM :指定基础镜像,可指定空包镜像 **scratch**- RUN : 执行命令,支持2种格式:- shell格式:`RUN <命令>`,即等同于编写 shell 命令,但需注意,每一次 `RUN` 的执行都会构建一个分层,因此运行时不需要的东西可通过 `&&` 符号串联命令,避免镜像过于臃肿(UnionFS 有最大层数限制), 也需要注意清理- 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 # 清理缓存等
- COPY :从构建上下文目录中的`源路径` 的文件或谬论复制到新一层镜像的`目标路径`位置- 源路径:可为多个,可使用通配符(正则)- 目标路径:可为容器内绝对路径,也可以相对工作目录`WORKDIR`)的相对路径,不存在会自动创建- `--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/
- ADD : 更高级的文件复制,可支持 `URL`- 下载的文件权限,默认为600,若要改动则需要手动再执行 `RUN` 命令- 若下载为压缩包,`gzip`、`bzip2`、`xz` 格式可自动解压,否则需要 `RUN`- 会领镜像构建缓存失效,使镜像构建缓慢- 支持`--chown=<user>:<group>`- 复制文件尽量使用 `COPY`,语义明确,需要自动解压缩的场景下使用 `ADD`- CMD : 容器启动命令,格式同 `RUN`- shell格式:将被解析为 `sh c`,例如:`CMD echo $HOME` 解析为 `CMD [ "sh", "-c", "echo $HOME" ]`- exec格式:更为推荐,会被解析为JSON数组,需使用双引号- 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;”]
- ENTRYPOINT :指定容器启动及参数,同 `CMD`- 在运行时可替代,需要通过 `docker run` 的参数 `--entrypoint` 来指定- 当指定了 `ENTRYPOINT` 后,`CMD` 的含义就发生了改变,不再是直接的运行其命令,而是将内容作为参数传给 `ENTRYPOINT` 指令- 场景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
- ENV : 设置环境变量- `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
- ARG : 构建参数,同 `ENV`- 所设置的环境变量,在容器运行时不存在。但使用 `docker history`命令可见- Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖- VOLUME : 定义匿名卷- 格式 `VOLUME ["<路径1>", "<路径2>"...]` 或 `VOLUME <路径>`
定义/data 目录为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化
VOLUME /data
运行时覆盖
docker run -d -v mydata:/data xxxx
- EXPOSE : 声明端口- 格式:`EXPOSE <端口1> [<端口2>...]`- Dockerfile中声明,仅用于帮助镜像使用者理解,并不真正在宿主机进行端口映射- `docker run -p <宿主端口>:<容器端口>`, 真正进行映射。若使用`docker run -P` 进行随机映射时,会自动映射 `EXPOSE` 端口- WORKDIR : 指定工作目录,不存在自动新建
2行RUN指令为2层构建,第二行命令重新启动容器,并不会存在 app/world.txt文件
需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令
RUN cd /app RUN echo “hello” > world.txt
- USER : 切换后续命令执行时的身份,需事先存在- HEALTHCHECK :健康检查- `HEALTHCHECK [选项] CMD <命令>` :设置检查容器健康状况的命令- `--interval=<间隔>` :两次健康检查的间隔,默认为 30 秒- `--timeout=<时长>` :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒- `--retries=<次数>` :当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。- `HEALTHCHECK NONE` :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令- 初始状态会为 `starting`,在 `HEALTHCHECK` 指令检查成功后变为 `healthy`,如果连续一定次数失败,则会变为 `unhealthy`- 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
<a name="ae8da901"></a>#### 多阶段构建- 镜像层次少,体积小,部署时间较短
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”] ```
