跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 最简单的 Go Dockerfile 编写姿势,没有之一!
未分類
31 12 月 2020

最简单的 Go Dockerfile 编写姿势,没有之一!

最简单的 Go Dockerfile 编写姿势,没有之一!

資深大佬 : kevinwan 10

1. Dockerfile 一些额外注意点

  • 选择最简单的镜像

    比如 alpine,整个镜像 5M 左右

  • 设置镜像时区

    RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai 

2. 多阶段构建

  • 第一阶段构建否则构建出可执行文件,确保构建过程独立于宿主机
  • 第二阶段将第一阶段的输出作为输入,构建出最终的极简镜像

3. 完整 Dockerfile 编写过程

  • 首先安装 goctl 工具

    GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl

  • 在 greet 项目下创建一个 hello 服务

    goctl api new hello

    文件结构如下:

    greet ├── go.mod ├── go.sum └── service     └── hello         ├── Dockerfile         ├── etc         │   └── hello-api.yaml         ├── hello.api         ├── hello.go         └── internal             ├── config             │   └── config.go             ├── handler             │   ├── hellohandler.go             │   └── routes.go             ├── logic             │   └── hellologic.go             ├── svc             │   └── servicecontext.go             └── types                 └── types.go 
  • 在 hello 目录下一键生成 Dockerfile

    goctl docker -go greet.go

    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 RUN apk add --no-cache ca-certificates RUN apk add --no-cache 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"] 
  • 在 greet 目录下 build 镜像

    docker build -t hello:v1 -f service/hello/Dockerfile .

  • 查看镜像

    hello v1 5455f2eaea6b 7 minutes ago 18.1MB

    可以看出镜像大小约为 18M 。

  • 启动服务

    docker run --rm -it -p 8888:8888 hello:v1

  • 测试服务

    $ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14  {"message":""} 

4. 总结

goctl 工具极大简化了 Dockerfile 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。

如果觉得工具有帮助,欢迎 star

大佬有話說 (58)

  • 資深大佬 : yzbythesea

    为什么不在本地编译好 binary 然后传进 docker 里呢?

  • 資深大佬 : Vegetable

    @yzbythesea 可能应上 CI 环境呗,本地能做的只有 git push

  • 資深大佬 : feelinglucky

    @yzbythesea 本地编译的二进制文件再打包到 Docker 镜像中不符合 CI 的规范,同时也不方便交叉编译等情况,所以正确规范的做法是 docker golang 镜像 build 通过后,将产出二进制文件 cp 到第二 stage 的 base 镜像。

    不过主的 Dockerfile 我建议使用 make 等构建工具去构建( golang 镜像自带的),因为如果涉及到更改配置、参数什么的,就又要该 Dockerfile 文件了,会很麻烦而且容易出错。

  • 資深大佬 : lwch

    alpine 镜像里的 glibc 是阉割过的版本,拿来跑 go 程序需要专门编译,而且线上实测过性能下降特别明显,只能拿来做个玩具

  • 主 資深大佬 : kevinwan

    @lwch 有实测数据嘛?我们千万级日活都是用 `alpine` 的,没遇到问题

  • 主 資深大佬 : kevinwan

    @feelinglucky 愿闻其详

  • 資深大佬 : xin053

    Dockerfile 最佳实践中尽量减少层,比如将多个 RUN 合成一个,例如
    RUN apk update –no-cache
    RUN apk add –no-cache ca-certificates
    RUN apk add –no-cache tzdata
    可以写成
    RUN apk update –no-cache && apk add –no-cache ca-certificates tzdata

  • 資深大佬 : knight0zh

    更新真快啊,上周问的功能这周就出了。

  • 主 資深大佬 : kevinwan

    @xin053 对的,这个很快就改掉,

  • 資深大佬 : 12101111

    @lwch alpine 用的是 musl, 根本没有 glibc
    CGO_ENABLED=0 的情况下 go 编译器压根不需要 libc, 性能和什么 libc 没有任何关系, go 的标准库在 Linux 上是直接调 syscall 的

  • 資深大佬 : GopherDaily

    哪来的

  • 主 資深大佬 : kevinwan

    @xin053 已修改,代码已提交

  • 主 資深大佬 : kevinwan

    下一篇文章写个一键生成 k8s 部署文件的

  • 資深大佬 : lyi4ng

    alpine 啊,我记得他的 lfs 是通过 minilibc 和 busybox 构建的,那得要求服务相关代码和脚本高度规范的,就 busybox 这玩意的 shll 规范就能折磨死无数用 bash 写 shell 脚本的人

  • 資深大佬 : feelinglucky

    @kevinwan 如果是一间部署 K8s 的脚本工具集的话,就不要重复造轮子了,兄弟见笑可以参考我原先写的

    https://github.com/mingcheng/deploy-k8s-within-aliyun-mirror

  • 主 資深大佬 : kevinwan

    @feelinglucky 学习下,不过我轮子已造好,只是写个文章介绍下哈

  • 資深大佬 : renzhe8102

    @12101111 zj

  • 資深大佬 : yzbythesea

    @feelinglucky 如果说 CI/CD,我也没见过。都是 builder 的机器在编译,当然你可以把 builder 容器化,但是 builder 的 docker image 是和生产环境不一样的。

  • 資深大佬 : beginor

    把所有的语句都写到一个 install.sh 里面,Dockerfile 一个指令 Run install.sh

  • 主 資深大佬 : kevinwan

    @beginor 这样有何好处?

  • 資深大佬 : kieoo

    @feelinglucky 使用 docker golang build 有个问题, go mod download 等于要重新拉所有的依赖包, 打包时间会变长; 在 builder 机器上可以有缓存, 减少包时间

  • 資深大佬 : cs419

    最简单!! 没有之一!!

    分明是钓鱼呀
    集思广益 让大伙提供优化思路

  • 資深大佬 : joesonw

    不要用 alpine 用 google 的 distroless

  • 資深大佬 : chazyu1996

    这样每次 build 都会去拉 mod 依赖项,建议在本地编译,或者把 GOPATH COPY 进去,不然会很慢

  • 資深大佬 : Lemeng

    集思广益

  • 資深大佬 : dreamusername

    不够简单,应该要尽量减少分层,一个 apk add 你写了 3 行

  • 資深大佬 : wellsc

    @dreamusername 说到点子上了,写 docker file 最需要注意的就是 layer 了

  • 資深大佬 : xuzhzzz

    go build 总要拉私有的包吧,这个权限怎么处理的呢?看到你的 dockerfile 里没有处理相关的
    我的做法是 COPY ~/.netrc,感觉比较蠢,大家都是咋做的

  • 資深大佬 : dayeye2006199

    分享个 google 项目的示例写法:

    —
    # Build the manager binary
    FROM golang:1.13 as builder

    WORKDIR /workspace
    # Copy the Go Modules manifests
    COPY go.mod go.mod
    COPY go.sum go.sum
    # cache deps before building and copying source so that we don’t need to re-download as much
    # and so that source changes don’t invalidate our downloaded layer
    RUN go mod download

    # Copy the go source
    COPY main.go main.go
    COPY api/ api/
    COPY controllers/ controllers/

    # Build
    RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go

    # Use distroless as minimal base image to package the manager binary
    # Refer to https://github.com/GoogleContainerTools/distroless for more details
    FROM gcr.io/distroless/static:nonroot
    WORKDIR /
    COPY –from=builder /workspace/manager .
    USER nonroot:nonroot

    ENTRYPOINT [“/manager”]
    —

  • 資深大佬 : dayeye2006199

    再推荐个 Dockerfile 的 linter,可以找到一些比较有问题的 Dockerfile 写法问题,例如不恰当分层,权限过高,标签不恰当啊之类的。
    https://github.com/hadolint/hadolint

  • 主 資深大佬 : kevinwan

    @dreamusername master 已经改掉了,只是文章不能编辑

  • 主 資深大佬 : kevinwan

    @dayeye2006199 感觉跟我写的版本相差不大,有劳指出差别哈,btw: 层已合并

  • 主 資深大佬 : kevinwan

    @dayeye2006199 谢谢,明天试试

  • 主 資深大佬 : kevinwan

    @cs419 开源的本质就是集思广益,再回馈社会嘛

  • 資深大佬 : beginor

    @lwch alpine 自带的不是 glibc 而是 musl c, 如果不能迁移到 musl 的话,alpine 并没有太大的体积优势。

  • 資深大佬 : beginor

    @kevinwan shell 比 dockerfile 容易调试, 至少可以一行一行的输入进行测试; 而且这样做镜像只有一层, 编译出来的镜像有体积优势

  • 資深大佬 : beginor

    @kieoo golang 还好了, 要是 node 的话, 每次都要执行 npm ci 才真是折磨

  • 資深大佬 : kaka6

    go 现在还用 GO111MODULE 吗,1.4 以后好像升级了,包管理更方便了,一直还没去试

  • 主 資深大佬 : kevinwan

    @beginor 这样吃不上缓存层

  • 主 資深大佬 : kevinwan

    @kaka6 1.14 默认 auto,我记得 1.15 默认 on 了

  • 資深大佬 : feelinglucky

    @kieoo 干净的编译环境是比较重要的,相比之下打包时间变长以及缓存等对比不是很关键

  • 資深大佬 : kiddingU

    多条 run 指令为何不合并成一条

  • 資深大佬 : zunceng

    @kieoo 我碰到过这个问题 后来为了加快速度编译和打包分了两步
    编译在一个编译用的容器内运行 可以把 GOPATH GOROOT 等挂载进去 做增量编译

  • 資深大佬 : vZexc0m

    @kieoo #21 不是每次都会重新拉所有的依赖包,因为先复制的 go.mod 和 go.sum ,这两个文件没有变动的时候会使用缓存。

  • 資深大佬 : lwch

    @kevinwan 没有,我们当初用 docker 的时候还是 go1.10~1.11 左右的时代,监控做的也不全面

  • 資深大佬 : f6x

    @dayeye2006199 的版本更标准. @kevinwan
    build 和 pack 分离.
    然后把 go 环境的 builder 镜像本地缓存, 没有模块变化时达到最快构建速度.

  • 主 資深大佬 : kevinwan

    @kiddingU 合了,只是文章不能改

  • 資深大佬 : fy

    看了第一条:

    选择最简单的镜像
    比如 alpine,整个镜像 5M 左右

    告辞。

  • 資深大佬 : mritd

    把那两个 ADD 指令删掉

  • 主 資深大佬 : kevinwan

    @mritd 这个是优化技巧,吃缓存的

  • 資深大佬 : tozp

    不错,赞一下

  • 資深大佬 : beginor

    @kevinwan 缓存的副作用是导致最终编译出来的镜像体积增大

  • 主 資深大佬 : kevinwan

    @beginor 两阶段构建,不存在变大的

  • 資深大佬 : fuis

    @fy 哈哈,我也是看到第一点就不想看了

  • 主 資深大佬 : kevinwan

    @fuis 支持模板完全可定制,按需自定义

  • 資深大佬 : beginor

    @kevinwan run 语句越多, 镜像越大

  • 主 資深大佬 : kevinwan

    周一我会发一篇文章讲解如何一键生成 K8S 部署文件

  • 主 資深大佬 : kevinwan

    @fy scratch 没有 sh,无法登记去查问题

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具