关于制作Docker镜像?| Dockerfile快速开始

一、关于制作Docker镜像?🐋

46 | 如何制作Docker镜像?
参考URL: https://time.geekbang.org/column/article/417216
要落地云原生架构,其中的一个核心点是通过容器来部署我们的应用。如果要使用容器来部署应用,那么制作应用的 Docker 镜像就是我们绕不开的关键一步。

构建一个 Docker 镜像,最常用的有两种:

  • 通过docker commit命令,基于一个已存在的容器构建出镜像。
  • 编写 Dockerfile 文件,并使用docker build命令来构建镜像。

上面这两种方法中,镜像构建的底层原理是相同的,都是通过下面 3 个步骤来构建镜像:

docker commit这种镜像构建方式通常用在下面两个场景中:

  • 构建临时的测试镜像;
  • 容器被入侵后,使用docker commit,基于被入侵的容器构建镜像,从而保留现场,方便以后追溯。

除了这两种场景,我不建议你使用docker commit来构建生产现网环境的镜像。

原因如下:

  • 使用docker commit构建的镜像包含了编译构建、安装软件,以及程序运行产生的大量无用文件,这会导致镜像体积很大,非常臃肿。
  • 使用docker commit构建的镜像会丢失掉所有对该镜像的操作历史,无法还原镜像的构建过程,不利于镜像的维护。
  • Dockerfile 的操作流程可以通过docker image history [镜像名称]查询,方便开发者查看变更记录。

在实际开发中,使用Dockerfile来构建是最常用,也最标准的镜像构建方法.

使用 Dockerfile 构建镜像,本质上也是通过镜像创建容器,并在容器中执行相应的指令,然后停止容器,提交存储层的文件变更。和用docker commit构建镜像的方式相比,它有三个好处:

  • Dockerfile 包含了镜像制作的完整操作流程,其他开发者可以通过 Dockerfile 了解并复现制作过程。
  • Dockerfile 中的每一条指令都会创建新的镜像层,这些镜像可以被 Docker Daemnon 缓存。再次制作镜像时,Docker 会尽量复用缓存的镜像层(using cache),而不是重新逐层构建,这样可以节省时间和磁盘空间。
  • 把这一切都放到一个 Dockerfile 里,既没有源码泄漏,又不需要用脚本去跨平台编译,还获得了最小的镜像。

在实际生产环境中,标准的做法是通过 Dockerfile 来构建镜像。使用 Dockerfile 构建镜像,就需要你编写 Dockerfile 文件。

二、Dockerfile快速开始

Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。

  • dockerfile是自定义镜像的一套规则
  • dockerfile由多条指令构成,Dockerfile中的每一条指令都会对应于Docker镜像中的每一层。

docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。

docker build命令会读取Dockerfile的内容,并将Dockerfile的内容发送给 Docker 引擎,最终 Docker 引擎会解析Dockerfile中的每一条指令,构建出需要的镜像。

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。

Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。

FROM [--platform=<platform>] <image> [AS <name>] FROM [--platform=<platform>] <image>[:<tag>] [AS <name>] FROM [--platform=<platform>] <image>[@<digest>] [AS <name>] 
  • FROM:指定基础镜像,必须为第一个命令
  • FROM指令用于指定基础镜像,ARG是唯一可以位于FROM指令之前的指令,一般用于声明基础镜像的版本。
  • –platform选项可用在FROM多平台镜像的情况下指定平台。例如,linux/amd64、lunux/arm64、windows/amd64。
  • AS name表示为构建阶段命令,在后续FROM和COPY –from=name说明中可以使用这个名词,引用此阶段构建的映像。
  • tag或digest值是可选的。如果您省略其中任何一个,构建器默认使用latest标签。如果找不到指定tag,构建起将返回错误。
ARG CODE_VERSION=latest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extras 

LABEL 指令将元数据添加到镜像。LABEL 是键值对。要在 LABEL 值中包含空格,请像在命令行中一样使用引号和反斜杠。

用来指定当前工作目录(或者称为当前目录)

当使用相对目录的情况下,采用上一个WORKDIR指定的目录作为基准

相当与cd 命令,但不同的是指定了WORKDIR后,容器启动时执行的命令会在该目录下执行

COPY [--chown=<user>:<group>] <src>... <dest> COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] 

RUN 主要用于在Image里执行指令,比如安装软件,下载文件等。
COPY指令从复制文件、目录到镜像文件系统的。

编写Dockerfile的时候copy宿主机文件到镜像中。

Dockerfile只允许使用一次CMD命令。使用多个CMD会抵消之前所有的命令,只有最后一个命令生效。一般来说,这是整个Dockerfile脚本的最后一个命令。

FROM ubuntu CMD ["/usr/bin/wc","--help"] 

CMD有三种形式:

  • CMD [“exec”,“param1”,“param2”]:使用exec执行,推荐方式。
  • CMD command param1 param2:在/bin/sh中执行,可以提供交互操作。
  • CMD [“param1”,“param2”]:提供给ENTRYPOINT的默认参数(极少这样使用)。

EXPOSE指令通知容器在运行时监听某个端口,可以指定TCP或UDP,如果不指定协议,默认为TCP端口。但是为了安全,docker run命令如果没有带上相应的端口映射参数,Docker并不会将端口映射出去。

EXPOSE 80/tcp EXPOSE 80/udp 

指定映射端口方式:

docker run -P:将所有端口发布到主机接口,每个公开端口绑定到主机上的随机端口,端口范围在/proc/sys/net/ipv4/ip_local_port_range定义的临时端口范围内。

docker run -p :显式映射单个端口或端口范围。

ENTRYPOINT 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有其他传入值作为该命令的参数。

一个Dockerfile中只能有一个ENTRYPOINT命令。如果有多条,只有最后一条有效。

无参的方式:
ENTRYPOINT [“/usr/sbin/nginx"]

指定参数的方式:
ENTRYPOINT [“/usr/sbin/nginx”, “-g”, “deamon off"]

docker run 的–entrypoint 标志可以覆盖原Dockerfile中的ENTRYPOINT 指令。

注意理解该命令, 该命令 是指定你每次 docker run启动容器的时候,都会自己执行的一个程序!!!。

  • CMD可以为ENTRYPOINT提供参数,ENTRYPOINT本身也可以包含参数,但是可以把需要变动的参数写到CMD里面,而不需要变动的参数写到ENTRYPOINT里面;

  • ENTRYPOINT使用第二种shell方式会屏蔽掉CMD里面的命令参数和docker run后面加的命令。

  • 在Dockerfile中,ENTRYPOINT指定的参数比运行docker run时指定的参数更靠前。

ENTRYPOINT/CMD最后一条命令为无限运行的命令:
这句话才是使用ENTRYPOINT的精髓。
在Docker Daemon模式下,entrypoint、cmd命令的最后一个命令,一定是要当前容器需要一直运行的,才能防止容器退出。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。换句话说实际执行时,会变成 “”

通过关键字ARG,ENV设置变量

ARG arg1=test ENV env1=production 

注意:

  • 不能通过表达如$(uname -a)进行设置,只能设置为常量
  • ARG设置的变量在构建完成后,就会丢失。即在Docker中无法引用该变量
  • ENV设置的变量在Docker中可以通过如${env1}访问

在RUN中设置变量
在RUN通过arg=someValue中设置变量,以下脚本先获取Debain的系统版本号,并设置到了os_release变量中,在后续的命令中可以通过${os_release}进行引用

RUN os_release="$(cat /etc/os-release | grep VERSION_CODENAME | awk -F '=' '{print $2}')" &&\ echo "deb http://mirrors.aliyun.com/debian/ ${os_release} main non-free contrib\n\ deb http://mirrors.aliyun.com/debian-security ${os_release}/updates main\n\ deb http://mirrors.aliyun.com/debian/ ${os_release}-updates main non-free contrib\n\ deb http://mirrors.aliyun.com/debian/ ${os_release}-backports main non-free contrib\n"\ > /etc/apt/sources.list 

一个RUN命令,相当于新打开一个Shell。所以上一个RUN设置的变量无法在下一个RUN中使用。

因此如果你需要在build期间使用某些变量,那么ARG是最好的选择。如果你是想在运行期间使用,那么ENV是唯一的选择。

ENV主要是定义环境变量,在docker run的时候ENV的配置会加载到容易内部,但ARG的参数在内部是没法看到的。同时也可以通过下面命令更改ENV的默认值:

docker run -e var=yyy 

ARG和ENV 两者结合使用

ARG var ENV var=${var} 

在dockerfile内部可以这样控制命令的参数。

ARG protocal ARG address ARG port ENV protocal=${protocal} \ address=${address} \ port=${port} CMD /usr/bin/lightweightservicediscovery --listen=${PROTOCAL:-ipv4}:${ADDRESS:-0.0.0.0}:${port:-49188} //如果读取环境变量失败再采用后面的默认值。 

这样既可以在build的时候通过docker build –build-arg var=xxx 来传递参数,也可以通过在运行的时候通过docker run -e var=yyy来传递参数。

实战小demo:

docker build --build-arg INSTALL_ZIP=myinstall.zip -t centos-test:v4 . 
ARG INSTALL_ZIP COPY ./${INSTALL_ZIP} /root/ RUN chmod 755 ${INSTALL_ZIP} RUN unzip ${INSTALL_ZIP} 

三、如何制作Docker镜像?

docker build 命令用于使用 Dockerfile 创建镜像。

docker build [OPTIONS] PATH | URL | - 

OPTIONS说明:

  • –build-arg=[] :设置镜像创建时的变量;
  • –cpu-shares :设置 cpu 使用权重;
  • –cpu-period :限制 CPU CFS周期;
  • –cpu-quota :限制 CPU CFS配额;
  • –cpuset-cpus :指定使用的CPU id;
  • –cpuset-mems :指定使用的内存 id;
  • –disable-content-trust :忽略校验,默认开启;
  • -f :指定要使用的Dockerfile路径;
  • –force-rm :设置镜像过程中删除中间容器;
  • –isolation :使用容器隔离技术;
  • –label=[] :设置镜像使用的元数据;
  • -m :设置内存最大值;
  • –memory-swap :设置Swap的最大值为内存+swap,"-1"表示不限swap;
  • –no-cache :创建镜像的过程不使用缓存;
  • –pull :尝试去更新镜像的新版本
  • –quiet, -q :安静模式,成功后只输出镜像 ID;
  • –rm :设置镜像成功后删除中间容器
  • –shm-size :设置/dev/shm的大小,默认值是64M;
  • –ulimit :Ulimit配置。
  • –squash :将 Dockerfile 中所有的操作压缩为一层。
  • –tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
  • –network: 默认 default。在构建期间设置RUN指令的网络模式

使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1。

docker build -t runoob/ubuntu:v1 . 

也可以通过 -f Dockerfile 文件的位置:

$ docker build -f /path/to/a/Dockerfile . 

问题描述:
在参照这docker官网教程学习构建镜像的时候。提示错误:“docker build” requires exactly 1 argument.

问题分析:
原因是因为(少了一个 ‘.’ , ‘.’ 代表当前路径):

docker build --tag=xxx . 

问题描述:
dockerfile中 执行copy 把主机上的文件往容器中copy时,报错file not found in build context or excluded by .dockerignore

问题分析:
我的dockerfile中 宿主机的文件,写的绝对路径。而 dockerfile 不能获取 父目录。

解决方案:
文件放置在当前路径下,dockerfile中 写成 ./文件名 即可(将文件copy到当前目录)。

构建 Go 应用 docker 镜像
https://zhuanlan.zhihu.com/p/476921483

之前我们的演示的是 centos:centos8 基础镜像,查看原文,作者使用了 golang:alpine 镜像,非常小。

原作者的 dockerfile demo:

FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOPROXY https://goproxy.cn,direct RUN apk update --no-cache && apk add --no-cache tzdata WORKDIR /build ADD go.mod . ADD go.sum . RUN go mod download COPY . . RUN go build -ldflags="-s -w" -o /app/hello ./hello.go FROM alpine RUN apk update --no-cache && apk add --no-cache ca-certificates COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello CMD ["./hello"] 
  • 默认禁用了 cgo
  • 启用了 GOPROXY 加速 go mod download
  • 去掉了调试信息 -ldflags=“-s -w” 以减小镜像尺寸
  • 安装了 ca-certificates,这样使用 TLS证书就没问题了
  • tzdata 在 builder 镜像安装,并在最终镜像只拷贝了需要的时区
  • 自动设置了本地时区,这样我们在日志里看到的是北京时间了

Dockerfile 构建出的镜像大小和 v1 centos对比结果如下:

$ docker images | grep hello hello v4 94ba3ece3071 4 hours ago 6.66MB hello v3 f51e1116be11 8 hours ago 6.61MB hello v2 0dd53f016c93 8 hours ago 6.61MB hello v1 ac0e37173b85 9 hours ago 314MB 
docker info | grep "Docker Root Dir" 
root@cka-k8s-master:~/code/mygoapp root@cka-k8s-master:/data/docker buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes root@cka-k8s-master:/data/docker 4.0K runtimes 4.0K swarm 4.0K tmp 4.0K trust 16K plugins 28K volumes 80K network 88K buildkit 4.6M image 8.1M containers 3.1G overlay2 root@cka-k8s-master:/data/docker 

原始镜像的选择?
centos:centos8作为基础镜像,是因为centos:centos8镜像中包含了基本的排障工具,例如vi、cat、curl、mkdir、cp等工具。

FROM centos:centos8 LABEL maintainer="<xxx@yeah.net>" WORKDIR /opt/mygoapp RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone && \ mkdir -p /var/log/mygoapp COPY file-boom /opt/mygoapp/bin/ ENTRYPOINT ["/opt/mygoapp/bin/file-boom"] CMD ["-c", "/etc/mygoapp/file-boom.yaml"] 

原文链接:https://blog.csdn.net/inthat/article/details/124060033?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171836832416800225563159%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171836832416800225563159&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-13-124060033-null-null.nonecase&utm_term=docker%E9%95%9C%E5%83%8F

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享