创建最小的golang容器镜像
openthings · · 2971 次点击 · · 开始浏览Create the smallest and secured golang docker image based on scratch
When we are building a docker Image, the first idea is using the default official image.
FROM golang FROM nginx FROM openjdk
There is an official Docker image for Go.
$ docker image list golang latest 1c1309ff8e0d 10 days ago 779MB
Ouch 779 MB just for an empty image ... this is crazy ????
There is lightweight Alpine Docker image for Go.
Check the Alpine linux page for more informations.
FROM golang:alpine
This is smaller but too large for a production image with nothing
$ docker image list golang alpine bbab7aea1231 7 weeks ago 269MB
Use Multi-stage builds
Thanks to docker multi-stage builds, we can build our application in a docker alpine image an produce a small image with only a binary in a scratch image.
OK, it’s time to build a smaller image with multi-stage build
Before that we gonna see docker scratch image, only 2MB image. Perfect for our go static binary.
# STEP 1 build executable binary
FROM golang:alpine as builder COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN go build -o /go/bin/hello
# STEP 2 build a small image
# start from scratch FROM scratch
# Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello ENTRYPOINT ["/go/bin/hello"]
Oh cool only 21.2MB with everything i need for my go app.
$ docker image list hello latest bbab7aea1234 3 hours ago 21.2MB
But we can optimize it, by removing debug informations and compile only for linux target and disabling cross compilation.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
Now we have a small image only 13.6MB, almost ready for production
$ docker image list hello latest bbab7aea1234 2hours ago 13.6MB
Let’s build a more secure docker image
Just some reminders :
- Run only one process in a container.
- Never run a process as root in a container.
- Never store data in a container, do it in a volume
- Never store credentials in a container, do it in a volume
- Keep your image up to date
- Verify third-party container repositories
- Use tool like docker-security-scanning
- May the force be with you ????
OK, let’s do that with scratch image.
We have to create a new user on the builder image and copy the /etc/passwd file from the builder to te scratch image. Finally we can use appuser to launch the binary.
# STEP 1 build executable binary
FROM golang:alpine as builder
# Create appuser RUN adduser -D -g '' appuser
COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
# STEP 2 build a small image
# start from scratch FROM scratch
COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
ENTRYPOINT ["/go/bin/hello"]
Always export with port > 1024 as possible
OK, now we have a more secure image. But if we expose our docker with a port < 1024, we need some privileges for that.
OK so let’s expose always our binary with a port > 1024
COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
EXPOSE 9292 ENTRYPOINT ["/go/bin/hello"]
Add SSL ca certificates
Perfect, but to be secure we need to expose our services with SSL isn’t it ?
By default scratch image is not provided with SSL CA certificates. But with multi-step we can provide it.
# STEP 1 build executable binary
FROM golang:alpine as builder
# Install SSL ca certificates RUN apk update && apk add git && apk add ca-certificates
# Create appuser RUN adduser -D -g '' appuser
COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
# STEP 2 build a small image
# start from scratch FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
ENTRYPOINT ["/go/bin/hello"]
If you have any advice about security, please let me know :)
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
Create the smallest and secured golang docker image based on scratch
When we are building a docker Image, the first idea is using the default official image.
FROM golang FROM nginx FROM openjdk
There is an official Docker image for Go.
$ docker image list golang latest 1c1309ff8e0d 10 days ago 779MB
Ouch 779 MB just for an empty image ... this is crazy ????
There is lightweight Alpine Docker image for Go.
Check the Alpine linux page for more informations.
FROM golang:alpine
This is smaller but too large for a production image with nothing
$ docker image list golang alpine bbab7aea1231 7 weeks ago 269MB
Use Multi-stage builds
Thanks to docker multi-stage builds, we can build our application in a docker alpine image an produce a small image with only a binary in a scratch image.
OK, it’s time to build a smaller image with multi-stage build
Before that we gonna see docker scratch image, only 2MB image. Perfect for our go static binary.
# STEP 1 build executable binary
FROM golang:alpine as builder COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN go build -o /go/bin/hello
# STEP 2 build a small image
# start from scratch FROM scratch
# Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello ENTRYPOINT ["/go/bin/hello"]
Oh cool only 21.2MB with everything i need for my go app.
$ docker image list hello latest bbab7aea1234 3 hours ago 21.2MB
But we can optimize it, by removing debug informations and compile only for linux target and disabling cross compilation.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
Now we have a small image only 13.6MB, almost ready for production
$ docker image list hello latest bbab7aea1234 2hours ago 13.6MB
Let’s build a more secure docker image
Just some reminders :
- Run only one process in a container.
- Never run a process as root in a container.
- Never store data in a container, do it in a volume
- Never store credentials in a container, do it in a volume
- Keep your image up to date
- Verify third-party container repositories
- Use tool like docker-security-scanning
- May the force be with you ????
OK, let’s do that with scratch image.
We have to create a new user on the builder image and copy the /etc/passwd file from the builder to te scratch image. Finally we can use appuser to launch the binary.
# STEP 1 build executable binary
FROM golang:alpine as builder
# Create appuser RUN adduser -D -g '' appuser
COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
# STEP 2 build a small image
# start from scratch FROM scratch
COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
ENTRYPOINT ["/go/bin/hello"]
Always export with port > 1024 as possible
OK, now we have a more secure image. But if we expose our docker with a port < 1024, we need some privileges for that.
OK so let’s expose always our binary with a port > 1024
COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
EXPOSE 9292 ENTRYPOINT ["/go/bin/hello"]
Add SSL ca certificates
Perfect, but to be secure we need to expose our services with SSL isn’t it ?
By default scratch image is not provided with SSL CA certificates. But with multi-step we can provide it.
# STEP 1 build executable binary
FROM golang:alpine as builder
# Install SSL ca certificates RUN apk update && apk add git && apk add ca-certificates
# Create appuser RUN adduser -D -g '' appuser
COPY . $GOPATH/src/mypackage/myapp/ WORKDIR $GOPATH/src/mypackage/myapp/
#get dependancies RUN go get -d -v
#build the binary RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/backoffice
# STEP 2 build a small image
# start from scratch FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/passwd /etc/passwd # Copy our static executable COPY --from=builder /go/bin/hello /go/bin/hello USER appuser
ENTRYPOINT ["/go/bin/hello"]
If you have any advice about security, please let me know :)