资讯专栏INFORMATION COLUMN

Docker+UPX 构建更小的镜像

oliverhuang / 2381人阅读

摘要:更小的镜像构建此处省略非常漂亮,到现在我们已经看到的已经缩小到这已经非常的小了,换算过来我们的程序只有。是一个免费且开源的可执行程序文件加壳器,支持许多不同操作系统下的可执行文件格式。

无论是在我们的生产环境或测试环境中,一旦涉及到docker pull和push的时候,我们都迫切希望Docker镜像是一个非常小的file,一方面在网络带宽有限的情况下,image的size越小下载所付出的时间代价就越小,另一方面image始终是一个文件,size对存储空间是有一定影响的,看来这是个提高生产力的问题,那么我们如何去构建一个size很小的image呢?

</>复制代码

  1. 本文内容

单阶段构建镜像

多阶段构建镜像

更小的镜像构建

注:多阶段构建是 Docker 17.05 及更高版本提供的功能

查看一下image列表:

</>复制代码

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. golang 1.10.3 d0e7a411e3da 6 weeks ago 794MB
  4. alpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB

通常在我们什么都不考虑的情况下,构建一个golang的应用镜像是非常简单的,只需要pull官方的golang环境,把我们的程序copy进去就可以了,下面我们先创建一个工程的目录如下:

</>复制代码

  1. $ tree -L 2 -C
  2. .
  3. ├── Dockerfile
  4. └── src
  5. └── main.go
  6. 1 directory, 2 files
单阶段构建镜像

Dockerfile:

</>复制代码

  1. FROM golang:1.10.3
  2. WORKDIR /go/src/test
  3. RUN go get github.com/gin-gonic/gin
  4. COPY src src
  5. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go
  6. CMD ["./main"]

Build

</>复制代码

  1. $ docker build -t zev/test:1.0.0 .
  2. Sending build context to Docker daemon 17.41kB
  3. Step 1/6 : FROM golang:1.10.3
  4. ---> d0e7a411e3da
  5. Step 2/6 : WORKDIR /go/src/test
  6. ---> Running in 94d1ede51e17
  7. Removing intermediate container 94d1ede51e17
  8. ---> 2b643ce8b3cf
  9. Step 3/6 : RUN go get github.com/gin-gonic/gin
  10. ---> Running in de5e9adb7c10
  11. Removing intermediate container de5e9adb7c10
  12. ---> ff970f45de1e
  13. Step 4/6 : COPY src src
  14. ---> 6b79fef06e45
  15. Step 5/6 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go
  16. ---> Running in 6d4ef8c0b580
  17. Removing intermediate container 6d4ef8c0b580
  18. ---> 59678a3ab4d8
  19. Step 6/6 : CMD ["./main"]
  20. ---> Running in a5cea54f2ccb
  21. Removing intermediate container a5cea54f2ccb
  22. ---> a253cfcffffd6a
  23. Successfully built a253cfcffffd6a
  24. Successfully tagged zev/test:1.0.0

RUN

</>复制代码

  1. $ docker run -it -p 8080:8080 zev/test:1.0.0
  2. [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
  3. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
  4. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
  5. - using env: export GIN_MODE=release
  6. - using code: gin.SetMode(gin.ReleaseMode)
  7. [GIN-debug] GET /ping --> main.main.func1 (3 handlers)
  8. [GIN-debug] Listening and serving HTTP on :8080

Images

</>复制代码

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. zev/test 1.0.0 a253cfcffffd6a 4 minutes ago 857MB

image的size为857MB,内部包含了整个golang环境,这么大的文件在传输中绝对是个灾难,接下来我们用多阶段构建一个相对比较小的image。

多阶段构建镜像

Dockerfile:

</>复制代码

  1. FROM golang:1.10.3 as builder
  2. WORKDIR /go/src/test
  3. RUN go get github.com/gin-gonic/gin
  4. COPY src src
  5. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go
  6. FROM alpine:3.8
  7. WORKDIR /root
  8. COPY --from=builder /go/src/test/main .
  9. CMD ["./main"]

Build

</>复制代码

  1. $ docker build -t zev/test:1.0.1 .
  2. Sending build context to Docker daemon 17.41kB
  3. Step 1/9 : FROM golang:1.10.3 as builder
  4. ---> d0e7a411e3da
  5. Step 2/9 : WORKDIR /go/src/test
  6. ---> Using cache
  7. ---> 2b643ce8b3cf
  8. Step 3/9 : RUN go get github.com/gin-gonic/gin
  9. ---> Using cache
  10. ---> ff970f45de1e
  11. Step 4/9 : COPY src src
  12. ---> Using cache
  13. ---> 6b79fef06e45
  14. Step 5/9 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go
  15. ---> Using cache
  16. ---> 59678a3ab4d8
  17. Step 6/9 : FROM alpine:3.8
  18. ---> 11cd0b38bc3c
  19. Step 7/9 : WORKDIR /root
  20. ---> Running in 1640c71479d6
  21. Removing intermediate container 1640c71479d6
  22. ---> ec68dc839562
  23. Step 8/9 : COPY --from=builder /go/src/test/main .
  24. ---> 5bb444c91aff
  25. Step 9/9 : CMD ["./main"]
  26. ---> Running in a80305feba6e
  27. Removing intermediate container a80305feba6e
  28. ---> 5923597f59c2
  29. Successfully built 5923597f59c2
  30. Successfully tagged zev/test:1.0.1

RUN

</>复制代码

  1. $ docker run -it -p 8080:8080 zev/test:1.0.1
  2. [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
  3. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
  4. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
  5. - using env: export GIN_MODE=release
  6. - using code: gin.SetMode(gin.ReleaseMode)
  7. [GIN-debug] GET /ping --> main.main.func1 (3 handlers)
  8. [GIN-debug] Listening and serving HTTP on :8080

Images

</>复制代码

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. zev/test 1.0.1 5923597f59c2 2 minutes ago 19.8MB

多阶段构建让images缩小了40倍,19.8M的size无论在测试环境还是生产环境都能很好的工作了,但是这样就结束了吗?
当然不是了,我们的目的是让image变得更小,下面看我们的操作。

更小的镜像构建

Dockerfile:

</>复制代码

  1. FROM golang:1.10.3 as builder
  2. RUN apt-get update && apt-get install -y xz-utils
  3. && rm -rf /var/lib/apt/lists/*
  4. ADD https://github.com/upx/upx/releases/download/v3.95/upx-3.95-amd64_linux.tar.xz /usr/local
  5. RUN xz -d -c /usr/local/upx-3.95-amd64_linux.tar.xz | tar -xOf - upx-3.95-amd64_linux/upx > /bin/upx &&
  6. chmod a+x /bin/upx
  7. WORKDIR /go/src/test
  8. RUN go get github.com/gin-gonic/gin
  9. COPY src src
  10. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go
  11. RUN strip --strip-unneeded main
  12. RUN upx main
  13. FROM alpine:3.8
  14. WORKDIR /root
  15. COPY --from=builder /go/src/test/main .
  16. CMD ["./main"]

Build

</>复制代码

  1. $ docker build -t zev/test:1.0.2 .
  2. Sending build context to Docker daemon 17.92kB
  3. Step 1/14 : FROM golang:1.10.3 as builder
  4. ---> d0e7a411e3da
  5. Step 2/14 : RUN apt-get update && apt-get install -y xz-utils && rm -rf /var/lib/apt/lists/*
  6. ---> Running in 65772cb8fdab
  7. Ign:1 http://deb.debian.org/debian stretch InRelease
  8. Get:2 http://security.debian.org/debian-security stretch/updates InRelease [94.3 kB]
  9. Get:3 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB]
  10. Get:4 http://security.debian.org/debian-security stretch/updates/main amd64 Packages [392 kB]
  11. .....此处省略
  12. Step 10/14 : RUN upx main
  13. ---> Running in d802406ee44a
  14. Ultimate Packer for eXecutables
  15. Copyright (C) 1996 - 2018
  16. UPX 3.95 Markus Oberhumer, Laszlo Molnar & John Reiser Aug 26th 2018
  17. File size Ratio Format Name
  18. -------------------- ------ ----------- -----------
  19. 9848136 -> 2945384 29.91% linux/amd64 main
  20. Packed 1 file.
  21. Removing intermediate container d802406ee44a
  22. ---> 0c29f4b2272d
  23. Step 11/14 : FROM alpine:3.8
  24. ---> 11cd0b38bc3c
  25. Step 12/14 : WORKDIR /root
  26. ---> Using cache
  27. ---> ec68dc839562
  28. Step 13/14 : COPY --from=builder /go/src/test/main .
  29. ---> a2c265cc9aff
  30. Step 14/14 : CMD ["./main"]
  31. ---> Running in 7e350a4620ee
  32. Removing intermediate container 7e350a4620ee
  33. ---> a4d7753c8112
  34. Successfully built a4d7753c8112
  35. Successfully tagged zev/test:1.0.2

RUN

</>复制代码

  1. $ docker run -it -p 8080:8080 zev/test:1.0.2
  2. [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
  3. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
  4. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
  5. - using env: export GIN_MODE=release
  6. - using code: gin.SetMode(gin.ReleaseMode)
  7. [GIN-debug] GET /ping --> main.main.func1 (3 handlers)
  8. [GIN-debug] Listening and serving HTTP on :8080

Images

</>复制代码

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. zev/test 1.0.2 a4d7753c8112 4 minutes ago 7.36MB

OK 非常漂亮,到现在我们已经看到image的size已经缩小到7.36MB,这已经非常的小了,换算过来我们的程序只有2.95M。
我们来一张全景对比看下:

</>复制代码

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. zev/test 1.0.2 a4d7753c8112 6 minutes ago 7.36MB
  4. zev/test 1.0.1 5923597f59c2 About an hour ago 19.8MB
  5. zev/test 1.0.0 a253cfcffffd6a About an hour ago 857MB
  6. golang 1.10.3 d0e7a411e3da 6 weeks ago 794MB
  7. alpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB

那么怎么做到的呢,原理很简单,因为alpine的size已经固定了,能让image变得更小的入手点只能是可执行文件,利用UPX的加壳技术可以压缩main可执行程序,可以把main体积缩小50%-70%。

</>复制代码

  1. Tip:UPX(the Ultimate Packer for eXecutables)是一个免费且开源的可执行程序文件加壳器,支持许多不同操作系统下的可执行文件格式。想了解更多关于UPX的信息可以点击这里 ,也可点击这里.

总结:


好的,到此为止本文首先展示了单阶段构建镜像,并得到了一个~857MB的镜像,然后利用多阶段构建了一个只包含可执行文件的镜像~19.8MB,最后我们利用多阶段+UPX压缩把我们的镜像缩小到了~7.36MB,这样的镜像无论在测试环境还是生产环境,都一定能大大的提高我们的生产力。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/27436.html

相关文章

  • Node.js docker 镜像体积优化实践

    摘要:接下来我们将逐步的减少这个镜像的体积。优化生产环境镜像使用镜像大幅减小镜像体积的最简单和最快的方法是选择一个小得多的基本镜像。使用多阶段构建可以充分利用镜像的缓存,大大减少最终部署到生产环境的时间。 关注作者github每日一道面试题详解 你讨厌部署你的应用程序花费很长时间吗? 对于单个容器来说,超过gb并不是最佳实践。每次部署新版本时都要处理数十亿字节,这对我们来说并不太合适。 本文...

    wenhai.he 评论0 收藏0
  • 创建尽可能小的 Docker 容器

    摘要:我一直在寻找尽可能小的容器入手,并且发现了一个镜像是完美的,真正的完美它简洁,小巧以及快速。所以这可能就是最小的镜像。尽管如此,这样看起来会很复杂并且导致容器很大。 注:本文由 Adriaan de Jonge 编写,本文的原文地址为 Create The Smallest Possible Docker Container 当我们在使用 Docker 的时候,你会很快注意到...

    yankeys 评论0 收藏0
  • 高质量 Node.js 微服务的编写和部署

    摘要:编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。该对象会在之后的时被这时的回调函数会被调用,并输出。微服务部署及集成部署微服务时有一个原则一个容器中只放一个服务,可以使用编 前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app sho...

    szysky 评论0 收藏0
  • 高质量 Node.js 微服务的编写和部署

    摘要:编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。该对象会在之后的时被这时的回调函数会被调用,并输出。微服务部署及集成部署微服务时有一个原则一个容器中只放一个服务,可以使用编 前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app sho...

    Michael_Ding 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<