Dockerfile相关

使用Dockerfile定制镜像

镜像的定制实际上就是每一层所添加的配置文件。

Dockerfile是一个文本文件,其中包含多条指令。每条指令构建一层。

FROM:定制的镜像都是基于FROM的镜像。

其中如果以scratch为基础镜像,意味着不需要任何镜像基础。

RUN

此指令用来执行命令行命令。主要有两种格式:

  • shell格式:RUN 就像直接在命令行中输入的命令一样。
    • 例如:FROM:定制的镜像都是基于FROM的镜像。
  • exec格式:RUN["可执行文件","参数1","参数2"]更像是函数调用的格式
    • 例如: RUN ["./test.php", "dev", "offline"] == RUN ./test.php dev offline

Dockerfile总每一个指令都会在docker上新建立一层。所以针对多条指令可以用&&符号连接命令,这样执行后,只会创建1层镜像。

例如

FROM centos
RUN yum install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

同时每一层构建完成后,一定要清理掉无关文件。避免镜像臃肿

构建镜像

docker build -t nginx:v3 .

镜像构建上下文

docker build命令后有一个 .。表示的是当前目录,实际上是表示的上下文路径

  • docker build工作原理
    • Docker在运行时分为Docker引擎和客户端工具,就是服务端守护进程和客户端工具
    • 本机执行的docker功能都是使用远程调用形式在服务端(Docker引擎)完成
  • 构建镜像时,用户需要指定构建镜像上下文的路径,docker build命令会将路径下的所有内容打包,然后上传给Docker引擎。
    • 超出上下文路径的文件是无法COPY

Dockerfile指令详解

COPY复制文件

  • 格式:

    • COPY [ -- chown=<user>:<group>] <源路径> ... <目标路径>
    • COPY [--chown=:] ["<源路径1>",... "<目标路径>"]
  • <源路径>可以是通配符 例如 COPY hom* /mydir

  • <目标路径>可以是容器内部的绝对路径,也可以是相对于工作目录的相对路径

  • 使用COPY,源文件的各种元数据都会保留。例如权限,变更时间

ADD更高级的复制文件

与COPY基本一致,在COPY的基础上添加了功能

实际上最适合的场景就是自动解压到目标目录

比如<源路径>可以是URL,这种情况下Docker引擎会试图下载这个链接的文件放到<目标路径>

如果权限信息不正确,需要用RUN再次修改权限,所以并不适用,不推荐

  • 如果<源路径>为tar压缩文件,ADD只能会自动解压缩这个压缩文件到<目标路径>

CMD容器启动命令

CMD指令就是用于指定默认的容器主进程的启动命令

在运行时可以指定新的命令来替代镜像设置中的默认命令

与RUN指令运行时间点区别:

  • CMD 在docker run时运行
  • RUN 是在 docker build

运行格式:

  • shell格式:CMD <命令>
  • exec 格式: CMD["可执行文件","参数1","参数2"]

采取 shell 格式,实际的命令会被包装成sh -c 的参数格式进行执行。比如:

CMD  ["sh","-c","echo $HOME"]

这就是可以使用环境变量的原因,因为这些环境变量会被shell进行解析处理。

因为docker不是虚拟机,所i容器中的应用都应该以前台执行,而不是像虚拟机,物理机中用system启动后台服务。

CMD所启动的命令实际都依靠于主进程sh,当sh作为主进程退出了,自然就会令容器退出。

正确的做法时直接执行相关的可执行文件,并且要求以前台形式运行。

CMD ["nginx","-g","deamon off"]

ENTRYPOINT入口点

如果Dockerfile中存在多个ENTRYPOINT指令,仅最后一个生效

ENTRYPOINT的格式和RUN指令格式一样,分为exec格式和shell格式

  • 制定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行器命令,而是将CMD内容作为参数传为ENTRYPOINT
    • 实际执行时将变成:<ENTRYPOINT> "<CMD>"

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

FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://myip.ipip.net" ]

如果此时用docker build -t myip构建镜像,只需要执行docker run myip

但是当希望添加参数比如 docker build -t myip -i会提示可执行文件找不到。

原因:

  • 跟在镜像后的command运行时会替换CMD的默认值。这里的-i会替换原来的CMD,所以自然找不到

  • 如果希望加入-i参数,必须重新完整输入命令docker run myip curl -s http://myip.ipip.net -i

此时用ENTRYPOINT就可以解决这个问题

修改最后一句ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ],此时可以使用 docker run myip -i

场景二:运行前的准备工作

启动容器就是启动主进程,但有时,启动主程序前需要进行准备工作

此时可以写一个脚本,然后放入ENTRYPOINT中去执行,而这个脚本将会接到的参数(也就是CMD)作为命令,放在脚本最后执行

eg : redis官方镜像

FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD [ "redis-server" ]

其中为了redis服务创建redis用户,并在最后指定entrypoint为docker-entrypoint.sh脚本

#!/bin/sh
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
find . \! -user redis -exec chown redis '{}' +
exec gosu redis "$0" "$@"
fi
exec "$@"

脚本的内容就是根据CDM内容判断,如卡换对应的身份

ENV设置环境变量

两种格式:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2>

这个指令就是设置环境变量,无论是后面的其他指令RUN,还是运行的应用,都可以直接使用这里定义的环境变量

ENV VERSION=1.0 DEBUG=ON \
	NAME="HAPPY TREE"
#后续的命令可以直接使用VERSION
RUN curl -SLO "https://ppp.ok/v$VERSION"

ARG构建参数

ARG <参数名>[=<默认值>]

构建参数和ENV的效果一样,都是设置环境变量。但是,ARG锁构建的环境变量,在将来容器运行时不存在。

构建命令中可以用--build-arg <参数名>=<值> 来覆盖。

  • ARG指令有生效范围,如果在FROM指令之前指定,那么只能用于FROM指令中

VOLUME 定义匿名卷

定义匿名数据券,再启动容器时忘记挂载数据券,会自动挂载到匿名券。

VOLUME ["<路径1>","<路径2>"] VOLUME "<路径1>"

作用:

  • 避免重要的数据,因为容器启动而丢失
  • 避免容器不断变大

为了防止用户忘记将动态文件所保存目录挂载为卷。在Dockerfile中,我们可以实现指定某些目录为匿名卷,这样运行时忘记挂载也可以正常运行

VLOUME /data
docker run -d -v mydata:/data xxxx
#可以通过-v 参数指修改挂载点
#在这行命名命名参数中,就是用了mydata这个命名权挂载到了/data

EXPOSE暴漏端口

格式为EXPOSE<端口1> [<端口2>]

仅仅是声明端口,并不会因为这个声明就开启这个端口的服务。

-p是映射宿主端口和容器端口,就是将容器对端口服务公开给外界访问

WORKDIR指定工作目录

格式为WORKDIR <工作目录路径>

  • 使用WORKDIR指令可以指定工作目录,以后的各层目录都被改成指定目录,如果不存在WORKDIR会帮你创建目录
  • WORKDIR存在相对路径

USER指定当前用户

格式:USER <用户名>[:<用户组>]

  • USER则是改变之后层执行RUNCMDENTRYPOINT这类命令身份
  • USER切换的用户必须是事前建立好的否则无法切换

HEALTHCHECK 健康检查

ONBUILD

Dockerfile多阶段构建

多阶段构建

之前的做法

在Docker 17.05 版本之前,在构建Docker镜像时,通常会采取两种方式

1.全部放入一个Dockerfile

一种方式是将所有的构建过程编在一个Dockerfile中,包含项目机器依赖库的编译、测试、打包等流程,如此可能带来一些问题:

  • 镜像构建层次多,镜像体积较大,部署时间变长
  • 源代码存在泄露的风险

例如:

编写 app.go 文件 // 编写对应dockerfile (其中包含了编译测试打包) // 最后构建镜像

2.分散到多个Dockerfile

另一种方式是事先在一个Dockerfile将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要编写两个Dockerfile和一些编译脚本才能将两个阶段整合

使用多阶段构建

为了解决以上问题,Dockerv17.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 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"]

其他制作镜像的方式

从rootfs压缩包导入

docker import [option] <file>|<URL> - [<depository><:TAG>]]

压缩包可以是本地文件、远程Web文件,甚至是标准输入中得到的。也所报将会在镜像/目录展开,直接作为镜像第一层提交

$ docker import \
http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz \
openvz/ubuntu:16.04

此命令自动下载了tar.gz文件,并作为跟文件系统展开导入,保存为镜像openvz/ubuntu:16.04

导入成功后,可以在docker image ls总看到导入的镜像

Docker镜像导入和导出 docker save和docker load

保存镜像

使用docker save命令可以将镜像保存为归档文件docker save alpine -o filename

若使用gzip压缩:

docker save alpine | gzip > alpine-latest.tar.gz

加载镜像

docker load -i alpine-latest.tar.gz
posted @ 2021-02-07 17:18  NGinko  阅读(115)  评论(0编辑  收藏  举报