在Docker上创建Go镜像文件并不困难,但建立的文件很大,接近1G,使用起来不太方便。Docker镜像的一个主要难题就是如何优化,创建小的镜像。我们可以用多级构建的方法来创建Docker镜像文件,它也不复杂。但由于使用这种方法时,需要用简版的Linux(Alpine),它带来了一系列的问题。本文讲述如何解决这些问题并成功创建优化的Go镜像文件,优化之后只有14M。

单级构建:

我们用一个Go程序作为例子来展示如何创建Go镜像。下面就是这个程序的目录结构。

file

Go程序的具体内容并不重要,只要能运行就行了。我们重点关注“docker”子目录(“kubernetes”子目录里的文件有别的用途,会在另外的文章中讲解)。它里面有三个文件。“docker-backend.sh”是创建镜像的命令文件,“Dockerfile-k8sdemo-backend”是多级构建文件,“Dockerfile-k8sdemo-backend-full”是单级构建文件,

FROM golang:latest # 从Docker库中获取标准golang镜像 WORKDIR /app # 设置镜像内的当前工作目录 COPY go.mod go.sum ./ # 拷贝Go的包管理文件 RUN go mod download # 下载依赖包中的依赖库 COPY . . #从宿主机拷贝文件到镜像 WORKDIR /app/cmd # 设置新的镜像内的当前工作目录 RUN GOOS=linux go build -o main.exe #编译Go程序,并在生成可执行文件 CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait" # 保持镜像一直运行,容器不被停掉

上面就是“Dockerfile-k8sdemo-backend-full”镜像文件。请阅读文件中的注释以获得解释。

生成镜像容器

cd /home/vagrant/jfeng45/k8sdemo/ docker build -f ./script/kubernetes/backend/docker/Dockerfile-k8sdemo-backend-full -t k8sdemo-backend-full .

运行镜像容器,“--name k8sdemo-backend-full”是给这个容器一个名字(k8sdemo-backend-full),最后的“k8sdemo-backend-full”是镜像的名字

docker run -td --name k8sdemo-backend-full k8sdemo-backend-full

登录镜像容器, 其中“a95c”是容器ID的前四位。

docker exec -it a95c /bin/bash

文件里有一条语句需要特别解释一下“COPY . .”,它把文件从宿主机拷贝到镜像里,在镜像里已经用“WORKDIR”设置了当前工作目录,那么宿主机的“.”(当前目录)是哪个目录呢?它不是Dockerfile文件所在的目录,而是你运行“Docker build”命令时所在的目录。

我们要把整个程序都拷贝到镜像里,那么在运行docker命令时一定是在程序的根目录,也就是“k8sdemo”目录。但是与容器有关的文件都在“script”目录的子目录下,那么当你运行“Docker build”命令时,它是怎么找到Docekrfile的呢?这里有一个重要的概念就是“build cotext”(构建上下文),由它来决定Dockerfile的缺省目录。当你运行“docker build -t k8sdemo-backend .”创建镜像时,它会从“build cotext”的根目录去找Dockerfile文件,缺省值是你运行docker命令的目录。但由于我们的Dockerfile在另外的目录里,因此需要在命令里加一个“-f”选项来指定Dockerfile的位置,命令如下。 其中“-t k8sdemo-backend-full” 是指明镜像名,格式是“name:tag”, 我们这里没有tag,就只有镜像名。

docker build -f ./script/kubernetes/backend/docker/Dockerfile-k8sdemo-backend-full -t k8sdemo-backend-full .

详情请参见Dockerfile reference

这样创建的镜像用的是全版的Linux系统,因此比较大,大概接近1G。如果要想优化,就要用多级构建。

Multi-stage builds(多级构建):

单级构建只有一个“From”语句,而在多级构建中,有多个“From”,每个“From”构成一级。例如,下面的文件有两个“From”,是一个二级构建。每一级都可以根据需要选择适合自己的基础(base)镜像来构造本级镜像。每级镜像完成之后,下一级镜像可选择只保留上一级构建中对自己有用的最终文件,而删除所有的中间产物,这样就大大节省了空间。详情请参见Use multi-stage builds

下面就是多级构建的dockerfile(“Dockerfile-k8sdemo-backend”).

FROM golang:latest as builder # 本级镜像用“builder”标识 # Set the Current Working Directory inside the container WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . WORKDIR /app/cmd # Build the Go app #RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main.exe RUN go build -o main.exe  ######## Start a new stage from scratch ####### FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 # Copy the Pre-built binary file from the previous stage COPY --from=builder /app/cmd/main.exe . #把“/app/cmd/main.exe”文件从“builder”中拷贝到本级的当前目录 # Command to run the executable CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

创建镜像:

cd /home/vagrant/jfeng45/k8sdemo/ docker build -f ./script/kubernetes/backend/docker/Dockerfile-k8sdemo-backend -t k8sdemo-backend .

登录镜像: