定制化创建镜像

214 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

在使用 Docker 的过程中,经常会用到别人已经制作好的镜像。有些时候不符合我们的需求,在这种场景下我们也会构建定制化的镜像,例如在现有镜像中添加功能。

目前,Docker 官方提供了两种构建方案:

1、基于容器创建

2、基于 Dockerfile 创建

这两种方案各有千秋,本文将了解这两种方案。

一、基于容器创建镜像

这种方式最直观明了,在操作上也非常简单,整个过程只用三步:

  • 创建容器
  • 进入容器中修改内容
  • 将容器保存为镜像

下面用 nginx 镜像为例演示:

1、创建目标容器

$ docker run -d --name nginx nginx
09eccb8f8666bdb7657b55d11720ef1c90602e66aa5138c47a59adb5c2300cdb

2、进入容器修改内容

$ docker exec -it nginx bash
root@09eccb8f8666:/#

我们在容器中新建一个文件

root@09eccb8f8666:/# touch test.txt

完成之后退出容器

3、将容器保存为镜像

$ docker commit nginx nginx-new:1.0
sha256:e99efd691e913c53ba21d5dbcd9156cc5591a576cb1c8eb61a75c5e0176767ba

查看镜像列表,可看到新的镜像已经生成

$ docker images | grep nginx
nginx-new                                                                                      1.0                                        e99efd691e91   2 minutes ago   141MB
nginx                                                                                          latest                                     605c77e624dd   6 months ago    141MB

基于容器创建的方式虽然简单直观,但是不被推荐。这种方式需要创建容器然后手工操作,不易被复制,使用者无法了解镜像的整个创建问题,可能会引发其他问题。

二、基于 Dockerfile 创建镜像

Dockerfile 作为文本文件,在文件中包含了镜像的创建指令,用户可以通过它快速创建定制化镜像。

以上述 nginx 镜像制作过程为例

创建一个空目录,在目录中创建 Dockerfile 的文件,并写入如下内容

FROM nginx
RUN touch test.txt

完成后,执行命令

$ docker build -t nginx-new:2.0 .

docker build 命令用于生成镜像,参数 -t 为镜像打 tag,tag 用于标记镜像版本信息,默认 tag 为 latest,最后的 . 表示当前目录为执行目录。

命令执行完成后,查看镜像列表可看到已成功创建。

Dockerfile 可以看做是 docker commit 的代码化过程,能够使得镜像创建流程自动化,配合 CI/CD 流水线使用,这会极大提高效率。

Dockerfile 语法可按照功能简单分为两类:配置指令和操作指令。

配置指令

FROM 指令

格式:FROM

描述:指定基于 image 基础镜像来制作新的镜像,在 Dockerfile 中必须存在。通常为了避免镜像臃肿,占用空间大,在满足需求的前提下,尽量使用精简镜像,如 alpine 镜像,各官方镜像通常都有对应的 alpine 镜像,如 golang:alipine

示例:

FROM golang:alpine

MAINTAINER 指令

格式:MAINTAINER <author_name>

描述:指定镜像的作者信息

示例:

MAINTAINER Alex

USER指令

格式:USER [:]

描述:用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)

示例:

USER root

EXPOSE指令

格式:EXPOSE []

描述:用于声明容器内服务的监控端口,这里只是起到声明作用,并不会映射到宿主机端口。如果要映射宿主机端口,需要在容器启动时使用参数-P会随机映射端口。

示例:

EXPOSE 8088

ENV指令

格式:ENV

描述:指定容器的环境变量,该变量可以被RUN指令使用,并在容器运行后一直存在

示例:

ENV KEY value

ARG指令

格式:ARG [=]

描述:构建参数,与ENV作用一致,但作用域不同。ARG设置的环境变量仅在Dockerfile内有效,也就是说在docker build的过程中有效,构建过程中可以使用`docker build --build-arg <参数名>=<值>来覆盖

ARG ARCH=amd64

VOLUME指令

格式:VOLUME

描述:创建一个数据卷挂载点,一般用来存放需要持久化的数据

示例:

VOLUME ["/data"]

WORKDIR指令

格式:WORKDIR

描述:指定容器的工作目录

示例:

WORKDIR /home

操作指令

RUN指令

格式:RUN

描述:在容器中运行的命令,当执行RUN指令时,会生成一个镜像层来保存结果内容,为了减少镜像体积,通常用&&将多个命令放到单个RUN指令中执行。

示例:

RUN tdnf install sudo tzdata -y >> /dev/null \
    && tdnf clean all 

COPY指令

格式:COPY

描述:复制宿主机文件或目录到镜像中

示例:

COPY test.txt /home

ADD指令

格式:ADD

描述:与COPY类似,但如果src是tar文件会被解压到dest,同时src支持URL资源。

示例:

ADD test.tar /

HEALTHCHECK指令

格式:HEALTHCHECK CMD

描述:用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

示例:

HEALTHCHECK CMD curl --fail -s http://127.0.0.1:8080/api/ping || exit 1

CMD指令

格式:CMD ["executable","param1","param2"]

描述:容器启动后默认运行的命令,每个Dockerfile只能有一条CMD命令,如果有多条则执行最后一条。CMD命令可以在docker run启动容器后被替换。

CMD ["nginx","-g","daemon off;"]

ENTRYPOINT指令

格式:ENTRYPOINT ["executable","param1","param2"]

描述:与CMD指令类似,但ENTRYPOINT在容器启动时无法被替换

示例:

ENTRYPOINT ["nginx","-g","daemon off;"]

三、实践

在理解Dockerfile的相关指令后,我们来将一个应用容器化。

我们以一个Golang程序为例,在main.go文件中写入如下内容:

package main

import "net/http"

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })
    http.ListenAndServe(":8080", nil)
}

编写Dockerfile

FROM golang:1.17.9 as builder

COPY main.go /go/src/

WORKDIR /go/src

ENV GO111MODULE off

RUN CGO_ENABLED=0 go build -o web

FROM alpine:latest

COPY --from=builder /go/src/web web

EXPOSE 8080

CMD ["./web"]

执行命令开始构建

$ docker build -t web-test:v1 .

构建完成,查看构建的镜像

$ docker images 
web-test                                                                                       v1                                         b4986dbf8f6b   About a minute ago   11.7MB

可以通过history命令查看镜像构建过程中执行了哪些操作

$ docker history web-test:v1
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
b4986dbf8f6b   2 minutes ago   /bin/sh -c #(nop)  CMD ["web"]                  0B
2f17c96210ac   2 minutes ago   /bin/sh -c #(nop)  EXPOSE 8080                  0B
9647d0925951   2 minutes ago   /bin/sh -c #(nop) COPY file:37a80aa6fbfae307…   6.07MB
c059bfaa849c   8 months ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      8 months ago    /bin/sh -c #(nop) ADD file:9233f6f2237d79659…   5.59MB

运行容器

$ docker run -p 8080:8080 -d --name web-test web-test:v1

浏览器访问地址,可看到服务已正常运行