博客:docker制作自己的镜像并上传dockerhub
dockerfile 内容
FROM golang:alpine AS builder | |
LABEL stage=gobuilder | |
ENV CGO_ENABLED 0 | |
ENV GOOS linux | |
ENV GOPROXY https://goproxy.cn,direct | |
WORKDIR /build/zero | |
ADD go.mod . | |
ADD go.sum . | |
RUN go mod download | |
COPY . . | |
COPY service/hello/etc /app/etc | |
RUN go build -ldflags="-s -w" -o /app/hello service/hello/hello.go | |
FROM alpine | |
RUN apk update --no-cache && apk add --no-cache ca-certificates tzdata | |
ENV TZ Asia/Shanghai | |
WORKDIR /app | |
COPY --from=builder /app/hello /app/hello | |
COPY --from=builder /app/etc /app/etc | |
CMD ["./hello", "-f", "etc/hello-api.yaml"] |
dockerfile 详解
当我在/Users/myself/Golang/common_project/src/greet目录下执行 docker build -t hello:v1 -f service/hello/Dockerfile . 命令 | |
-f service/hello/Dockerfile 代表的事dockerfile文件的所在目录,可以是任意目录 | |
. 代表的是当前目录的所有文件,就是执行命令所在的目录下的文件,所以 greet目录下 的所有文件都会打包到 Docker引擎(服务端守护进程) | |
如果没有 . ,那么默认上下文路径就是 Dockerfile 所在的位置。 | |
所以首先会把 greet目录 打包到 守护进程,后面继续执行以下代码 | |
#FROM <image> | |
FROM <image>:<tag> | |
FROM <image>@<digest> | |
tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像 | |
打包依赖阶段使用golang:alpine作为基础镜像 | |
FROM golang:alpine AS builder | |
#维护者信息 | |
MAINTAINER Dong Lei | |
MAINTAINER 2781897595@qq.com | |
MAINTAINER Dong Lei <2781897595@qq.com> | |
#LABEL:用于为镜像添加元数据 | |
LABEL stage=gobuilder | |
#ENV:设置环境变量 | |
ENV CGO_ENABLED 0 | |
ENV GOOS linux | |
ENV GOPROXY https://goproxy.cn,direct | |
#WORKDIR:工作目录,类似于cd命令,进入/build/zero这个目录 | |
WORKDIR /build/zero | |
格式: | |
WORKDIR /path/to/workdir | |
示例: | |
WORKDIR /a (这时工作目录为/a) | |
WORKDIR b (这时工作目录为/a/b) | |
WORKDIR c (这时工作目录为/a/b/c) | |
注: | |
通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用 | |
ADD:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget | |
因为复制的上下问目录是 greet,所以 将项目目录下的 | |
把docekr守护进程下的 go.mod 拷贝到 /build/zero目录下 | |
ADD go.mod . | |
ADD go.sum . | |
RUN go mod download | |
COPY:功能类似ADD,但是是不会自动解压文件,也不能访问网络资源 | |
.是上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。 | |
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。 | |
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。 | |
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢 | |
COPY <源路径>... <目标路径> | |
COPY <src>... <dest> | |
dest: 指得是镜像(image)中的路径 | |
src: 指得是Dockerfile所在的context路径 | |
那么COPY的作用就是:将构建镜像的电脑上的文件或者文件夹拷贝到镜像中去。 | |
我们知道,Dockerfile文件往往是放在github上的某个工程里,当我们checkout工程,然后执行 docker build 命令的时候,我们的电脑和作者的电脑肯定不一样的,所以,明白了这一点,基于这样最基本的常识,我们马上就能明白什么样的src是正确的,什么样的src是错误的: | |
#正确: | |
COPY ./package.json /app/ | |
COPY package.json /usr/src/app/ | |
错误: | |
COPY ../package.json /app 因为上一层目录大家都不一样的 | |
或者 COPY /opt/xxxx /app 有可能连操作系统都不一样,哪里有什么/opt这样的目录 | |
所以,Dockerfile中的COPY中的context(上下文)指得就是 Dockerfile 所在的工程中的目录结构,离开了这个工程的其他目录都是错误的。 | |
顺便说一下:dest 是可以在镜像中的任意位置的,这个无所谓,不用解释大家都明白了吧。 | |
#COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。 | |
对于 COPY 和 ADD 命令来说,如果要把本地的文件拷贝到镜像中,那么本地的文件必须是在上下文目录中的文件。其实这一点很好解释,因为在执行 build 命令时,docker 客户端会把上下文中的所有文件发送给 docker daemon。考虑 docker 客户端和 docker daemon 不在同一台机器上的情况,build 命令只能从上下文中获取文件。如果我们在 Dockerfile 的 COPY 和 ADD 命令中引用了上下文中没有的文件,就会收到类似下面的错误: | |
. 便是当前目录 | |
如果在 Dockerfile 中这么写: | |
COPY ./package.json /app/ | |
这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json。 | |
因此,COPY 这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。 | |
理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 COPY /opt/xxxx /app 不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。 | |
# 把docekr守护进程下的所有文件 复制到 /build/zero 目录下,相当于 zero 就是 greet一样 | |
COPY . . | |
把service/hello/etc 这个文件夹 复制到 容器中 /app/etc | |
因为我是在greet 这个项目下执行的 打包命令 docker build -t hello:v1 -f service/hello/Dockerfile . | |
所以上下文就是 greet,相当于把 greet整个 拷贝到守护进程,所以 | |
COPY service/hello/etc /app/etc | |
指定编译完成后的文件名,可以不设置使用默认的,最后一步要执行该文件名,因为service/hello/hello.go里面是mian函数编译到 /app/hello | |
RUN go build -ldflags="-s -w" -o /app/hello service/hello/hello.go | |
#通过上面的copy和RUN go 使 /app/etc 和 /app/hello 两个数据生成 ,/app/hello是编译后的文件,而不是文件夹 | |
# 运行阶段指定scratch作为基础镜像 | |
FROM alpine | |
#RUN用于在镜像容器中执行命令,其有以下两种命令执行方式: | |
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache | |
RUN apk update --no-cache && apk add --no-cache ca-certificates tzdata | |
ENV TZ Asia/Shanghai | |
# 进去app 目录 | |
WORKDIR /app | |
COPY --from=builder将上一层镜像复制制定目录复制到当前工作目录 上一阶 通过COPY 和 RUN go 生成的 /app/hello 复制到 /app/hello | |
COPY --from=builder /app/hello /app/hello | |
COPY --from=builder /app/etc /app/etc | |
# 执行结果如下 | |
# ls | /|
app bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var | |
# cd app | /|
# ls | /app|
etc hello | |
# | /app|
#CMD:构建容器后调用,也就是在容器启动时才进行调用。 | |
CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。 | |
CMD ["./hello", "-f", "etc/hello-api.yaml"] |
案例1
dockerfile 文件
FROM golang:alpine AS builder | |
LABEL stage=gobuilder | |
ENV CGO_ENABLED 0 | |
ENV GOOS linux | |
ENV GOPROXY https://goproxy.cn,direct | |
WORKDIR /build/zero | |
# 把docekr守护进程下的 go.mod 拷贝到 /build/zero目录下 | |
ADD go.mod . |
启动容器
luwei@luweideMacBook-Pro-2 greet % docker build -t hello:v4 -f service/hello/Dockerfile . | |
[+] Building 81.4s (9/9) FINISHED | |
=> [internal] load build definition from Dockerfile 0.1s | |
=> => transferring dockerfile: 6.13kB 0.0s | |
=> [internal] load .dockerignore 0.1s | |
=> => transferring context: 2B 0.0s | |
=> [internal] load metadata for docker.io/library/golang:alpine 80.4s | |
=> [auth] library/golang:pull token for registry-1.docker.io 0.0s | |
=> [internal] load build context 0.1s | |
=> => transferring context: 27B 0.0s | |
=> [1/3] FROM docker.io/library/golang:alpine@sha256:55da409cc0fe11df63a7d6962fbefd1321fedc305d9969da636876893e289e2d 0.0s | |
=> CACHED [2/3] WORKDIR /build/zero 0.0s | |
=> CACHED [3/3] ADD go.mod . 0.0s | |
=> exporting to image 0.1s | |
=> => exporting layers 0.0s | |
=> => writing image sha256:b1298754a175d735d9e8d767e025c0b4ccf8ee1f65b78bd6af8a091b4f36a1b5 0.0s | |
=> => naming to docker.io/library/hello:v4 0.0s | |
luwei@luweideMacBook-Pro-2 greet % |
查看
luwei@luweideMacBook-Pro-2 greet % docker images | |
REPOSITORY TAG IMAGE ID CREATED SIZE | |
hello v4 b1298754a175 42 hours ago 315MB |
启动容器
可以看到 go.mod被复制到容器 /build/zero 中
luwei@luweideMacBook-Pro-2 greet % docker run -t -i hello:v4 | |
/build/zero # ls | |
go.mod | |
/build/zero # cd go.mod | |
/bin/sh: cd: can't cd to go.mod: Not a directory | |
文件目录
为什么需要 ADD go.sum . 因为其实 go.sun 是在根目录下并不是 g.mod 目录下
案例2
dockerfile 文件
FROM golang:alpine AS builder | |
LABEL stage=gobuilder | |
ENV CGO_ENABLED 0 | |
ENV GOOS linux | |
ENV GOPROXY https://goproxy.cn,direct | |
WORKDIR /build/zero | |
# 把docekr守护进程下的所有文件 复制到 /build/zero 目录下,相当于 zero 就是 greet一样 | |
COPY . . |
打包镜像
docker build -t hello:v4 -f service/hello/Dockerfile .
luwei@luweideMacBook-Pro-2 greet % docker build -t hello:v4 -f service/hello/Dockerfile . | |
[+] Building 88.6s (9/9) FINISHED | |
=> [internal] load build definition from Dockerfile 0.1s | |
=> => transferring dockerfile: 6.13kB 0.0s | |
=> [internal] load .dockerignore 0.0s | |
=> => transferring context: 2B 0.0s | |
=> [internal] load metadata for docker.io/library/golang:alpine 83.7s | |
=> [auth] library/golang:pull token for registry-1.docker.io 0.0s | |
=> [1/3] FROM docker.io/library/golang:alpine@sha256:55da409cc0fe11df63a7d6962fbefd1321fedc305d9969da636876893e289e2d 0.0s | |
=> [internal] load build context 2.9s | |
=> => transferring context: 145.68MB 2.8s | |
=> CACHED [2/3] WORKDIR /build/zero 0.0s | |
=> [3/3] COPY . . 0.5s | |
=> exporting to image 1.0s | |
=> => exporting layers 1.0s | |
=> => writing image sha256:2a71b6880ec710842cd090ffbf44ece12ab49829981aec3a424739c1f208f224 0.0s | |
=> => naming to docker.io/library/hello:v4 0.0s | |
luwei@luweideMacBook-Pro-2 greet % |
查看内容
内容和greet 内容一样的
/build/zero # ls | |
Dockerfile README.en.md README.md default.etcd go.mod go.sum service |
案例3
dockerfile 文件
FROM golang:alpine AS builder | |
LABEL stage=gobuilder | |
ENV CGO_ENABLED 0 | |
ENV GOOS linux | |
ENV GOPROXY https://goproxy.cn,direct | |
WORKDIR /build/zero | |
# 这三个可以不放,前提是你已经把扩展已经同步过去了 | |
#ADD go.mod . | |
#ADD go.sum . | |
#RUN go mod download | |
COPY . . | |
COPY service/hello/etc /app/etc | |
RUN go build -ldflags="-s -w" -o /app/hello service/hello/hello.go | |
FROM alpine | |
RUN apk update --no-cache && apk add --no-cache ca-certificates tzdata | |
ENV TZ Asia/Shanghai | |
WORKDIR /app | |
COPY --from=builder /app/hello /app/hello | |
COPY --from=builder /app/etc /app/etc | |
CMD ["./hello", "-f", "etc/hello-api.yaml"] |
启动容器并进去
luwei@luweideMacBook-Pro-2 greet % docker run -t -i hello:v5 /bin/sh | |
/app # | |
/app # ls | |
etc hello | |
/app # |
请求测试
luwei@luweideMacBook-Pro-2 ~ % curl -i http://localhost:8889/from/you | |
HTTP/1.1 200 OK | |
Content-Type: application/json | |
X-Trace-Id: efd07feabdbedf9e3dd51e2eb6ddcb1b | |
Date: Thu, 25 Nov 2021 04:13:09 GMT | |
Content-Length: 14 | |
{"message":""}% | |
luwei@luweideMacBook-Pro-2 ~ % |