practice background
I worked day and night and worked overtime to develop the simplest Go Hello world application. Although I just ran and printed it and then quit, my boss also asked me to launch this only application that I could write.
The project structure is as follows:
.
├── go.mod
└── hello.go
hello.go
code is as follows:
package main
func main() {
println("hello world!")
}
Moreover, the boss asked to use docker
to deploy, it seems that we keep up with the trend and be a little taller. . .
first try
After visiting some martial arts friends, I found that I just put the whole process into docker
to compile it. After some thinking, I got the following Dockerfile
:
FROM golang:alpine
WORKDIR /build
COPY hello.go .
RUN go build -o hello hello.go
CMD ["./hello"]
Build the image:
$ docker build -t hello:v1 .
Done, let's get a closer look.
$ docker run -it --rm hello:v1 ls -l /build
total 1260
-rwxr-xr-x 1 root root 1281547 Mar 6 15:54 hello
-rw-r--r-- 1 root root 55 Mar 6 14:59 hello.go
Good guy, the code I finally wrote is also in it. It seems that the code can't be written badly, otherwise the operation and maintenance girl will laugh at me when she peeks. . .
Let's take a look at how big the image is. It is said that pulling the image will be slower if it is large.
$ docker images | grep hello
hello v1 2783ee221014 44 minutes ago 314MB
Wow, there is actually 314MB, does docker build
change to Java
? Not everything is bigger the better. . .
Let's see why it's so big!
Look, before we ran the first command ( WORKDIR
), it was already 300+MB, which is a bit fierce!
Anyway, let's run and see
$ docker run -it --rm hello:v1
hello world!
It's alright, it works anyway~
second attempt
After some tobacco and alcohol, plus the advice of friends, I found that the basic image we used was really too big.
$ docker images | grep golang
golang alpine d026981a7165 2 days ago 313MB
And my friend told me that I can compile the code first, and then copy it in, so I don't need the huge base image, but it's easier said than done, I still put a lot of effort into it, and finally Dockerfile
like this:
FROM alpine
WORKDIR /build
COPY hello .
CMD ["./hello"]
try running
$ docker build -t hello:v2 .
...
=> ERROR [3/3] COPY hello . 0.0s
------
> [3/3] COPY hello .:
------
failed to compute cache key: "/hello" not found: not found
No, hello
can't find it, forgot to compile it first hello.go
, come again~
$ go build -o hello hello.go
Run again docker build -t hello:v2 .
, no problem, try two steps. . .
$ docker run -it --rm hello:v2
standard_init_linux.go:228: exec user process caused: exec format error
fail! Well, the format is wrong, it turns out that our development machine is not linux
ah, come again~
$ GOOS=linux go build -o hello hello.go
Re docker build
finally got it, run down
$ docker run -it --rm hello:v2
hello world!
No problem, let's take a look at the content and size.
$ docker run -it --rm hello:v2 ls -l /build
total 1252
-rwxr-xr-x 1 root root 1281587 Mar 6 16:18 hello
There is only hello
this executable file, no longer have to worry about others despising my code~
$ docker images | grep hello
hello v2 0dd53f016c93 53 seconds ago 6.61MB
hello v1 ac0e37173b85 25 minutes ago 314MB
Wow, 6.61MB, absolutely!
Look, we run the first command ( WORKDIR
) and there is only 5.3MB in front of it, happy!
third attempt
After showing off, someone actually despised me and said that multi-stage construction is popular now, so what is the problem with the second method? After careful consideration, we found that we need to be able to construct the docker
image from the Go
code, which is divided into three steps:
- Native compilation
Go
code, if it involvescgo
cross-platform compilation will be more troublesome - Build the
docker
image with the compiled executable - Write
shell
script ormakefile
and let these steps get by one command
Multi-stage build is to put all this into one Dockerfile
, without source code leakage, without scripting for cross-platform compilation, and obtaining the smallest image.
I love learning and pursue perfection. I finally wrote the following Dockerfile
, one more line is fat, and one less line is thin:
FROM golang:alpine AS builder
WORKDIR /build
ADD go.mod .
COPY . .
RUN go build -o hello hello.go
FROM alpine
WORKDIR /build
COPY --from=builder /build/hello /build/hello
CMD ["./hello"]
The first FROM
starts with building a builder
image, in which the purpose is to compile an executable file hello
, the second From
beginning part is from the first image copy
out of the executable file hello
, and use the smallest possible base image alpine
to ensure the final image As small as possible, as for why not use smaller scratch
, because scratch
there is really nothing, and there is no chance to even take a look at it, and alpine
It's only 5MB, so it won't have much impact on our services.
Let's run it first to verify:
$ docker run -it --rm hello:v3
hello world!
No problem, as expected! See how the size looks like:
$ docker images | grep hello
hello v3 f51e1116be11 8 hours ago 6.61MB
hello v2 0dd53f016c93 8 hours ago 6.61MB
hello v1 ac0e37173b85 8 hours ago 314MB
The size of the image built by the second method is exactly the same. Take a look at the contents of the mirror:
$ docker run -it --rm hello:v3 ls -l /build
total 1252
-rwxr-xr-x 1 root root 1281547 Mar 6 16:32 hello
Also there is only one executable hello
file, perfect!
It is basically the same as the second final image, but we simplified the process and only need one Dockerfile
, just run one command, I don't need to fix those obscure shell
makefile
too.
Divine Skills
So far, the team members feel that it is perfect, and they all give me praise! However, as a person who pursues perfection and likes to be lazy (fishing), I feel that every time I am asked to write such a line that one line is more fat, and one line less is thin Dockerfile
, I still find it annoying. Yes, so I wrote a tool without telling the boss, I will show it~~
# 安装一下先
$ GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest
# 一键编写 Dockerfile
$ goctl docker -go hello.go
Get it! Look at the generated Dockerfile
ha
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"]
Some of them can be understood:
- Disabled by default
cgo
- Enabled
GOPROXY
accelerationgo mod download
- Removed debug info
-ldflags="-s -w"
to reduce image size - Installed
ca-certificates
, so usingTLS
certificate is no problem -
tzdata
installed inbuilder
mirror, and copied only the required time zone in the final mirror - The local time zone is automatically set, so what we see in the log is Beijing time
Let's take a look at the size of the image built with this automatically generated Dockerfile
:
$ 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
Slightly larger because we copied ca-certificates
and tzdata
. Verify it:
Let's see what's in the mirror:
$ docker run -it --rm hello:v4 ls -l /app
total 832
-rwxr-xr-x 1 root root 851968 Mar 7 08:36 hello
It is also only hello
executable file, and the file size has been reduced from 1281KB to 851KB. Take a run and see:
$ docker run -it --rm hello:v4
hello world!
And you can specify the base image as scratch
when generating Dockerfile
---, so the image will be smaller, but you can't log in directly through sh
.
$ goctl docker -base scratch -go hello.go
The size is also really small:
$ docker images | grep hello
hello v5 d084eed88d88 4 seconds ago 1.07MB
hello v4 94ba3ece3071 15 hours ago 6.66MB
hello v3 f51e1116be11 4 days ago 6.61MB
hello v2 0dd53f016c93 4 days ago 6.61MB
hello v1 ac0e37173b85 4 days ago 314MB
Look at what's in the mirror
I compiled the Macbook M1
linux/arm64
image on ---ab5fca42163ebffaae7d5c73d94fff1e---, I guess you usually want to play the image of linux/amd64
, just use the following command:
$ docker build --rm --platform linux/amd64 -t hello:v6 .
Okay, no more entanglement Dockerfile
, I'm going to learn new skills~
project address
https://github.com/zeromicro/go-zero
Feel good? Welcome to tip, tip just light up GitHub
Little Star⭐️
WeChat exchange group
Follow the official account of " Microservice Practice " and click on the exchange group to get the QR code of the community group.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。