前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >构建更好的Docker镜像的一些技巧

构建更好的Docker镜像的一些技巧

作者头像
御剑
发布2023-11-02 18:32:27
1810
发布2023-11-02 18:32:27
举报
文章被收录于专栏:微言码道微言码道

现在,使用Docker或更复杂的K8S来部署你的服务应该是主流的选择了. 而这个做法的前提是使用把你的程序用docker打包构建成Docker镜像.

在这篇文章中, 我总结了我在构建Docker镜像积累的一些好的实践. 供大家参考与借鉴.

  1. 使用国内源

虽然国内这个情况令我们程序员觉得困扰. 但在国内做开发, 使用国内源基本是每个程序员的必备技能. 从npm国内源, Java Maven仓库国内源, 想要更好更快的编译我们的程序, 不使用国内源是非常浪费时间的行为.

同样,构建Docker镜像时,同样会面临这个问题. 特别是你在构建镜像中, 需要安装Linux的一些服务或软件时, 使用默认的官方源,会显著的让构建时间变得很长.

因此,在国内构建Docker镜像,在Dockerfile文件中,主动加上国内源的设置吧.

以我的一个go服务的构建来说明:

代码语言:javascript
复制
FROM golang:alpine AS build
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN go env -w  GOPROXY=https://goproxy.io,direct
RUN apk update && apk add --no-cache git gcc build-base linux-headers

在这个构建中,我设置了两个国内源:

  • 针对alpine,设置了alpine国内源,加快alpine下安装软件的速度
  • 针对go, 设置了go的国内代理源, 显著加快go依赖的下载速度

如果在国内不使用国内源, 这个镜像的构建时间久的令人难以接受.

  1. 使用官方的镜像做为基础镜像

构建Docker镜像,最开始需要做的是选择一个基础镜像, 比如Java语言,需要JRE或是JDK; Node服务需要一个node环境等.

千万不要自己基于Linux来构建这些语言环境. 而是选择Docker官方提供的基础镜像. Docker提供了非常多的基础镜像, 这些都是久经验证的非常可靠的基础镜像.

在Docker Hub网站上去搜索一下就能找到这些优秀的基础镜像. 凡是Docker提供的基础镜像, 都是非常好认的, 就直接一个名称,没有隶属名. 比如golang这样的就是官方镜像. 而google/cloud-sdk这样的就不是官方镜像,而是Google提供的.

在这里,需要对Java做特别的说明,虽然官方提供了一个OpenJDK的基础镜像,但这个基础镜像已经不再维护了. 我现在用的都是eclipse-temurin这个Java基础镜像, 这也是Docker官方提供的.

  1. 使用统一的定制基础镜像

有时候你可能需要在基础镜像基础之上,添加一些定制的能力或功能. 而这个又是一种通用的需求. 比如你的一个Java微服务,可能要构建几十个镜像,每个镜像都有这些定制的功能.

这时候,为了避免不同服务或不同团队使用不同的基础镜像, 在公司或团队级别,使用统一镜像是更好的做法.

代码语言:javascript
复制
FROM my-company/base-java:17.0.3

比如,构建了一个基于eclipse-temurin的自己的定制基础镜像. 这样,所有需要构建的Java服务都使用这个定制基础镜像是最好的选择.

  1. 考虑更小的基础镜像

在合适的前提下, 你应该考虑使用更小的基础镜像.

比如,你在Linux系统上选择构建一个服务, 那其实你有很多基础Linux镜像可以选择,比如Debian, Ubuntu或是Alpine. 在合适的前提下, 选择Alpine是更好的.

但是, 需要注意的是, 除非它确实不影响你的服务,在其它要素不影响的前提下才这样做, 网上有一种docker镜像干啥都推荐用Alpine的主张,我并不赞同, 因为这里面有其它因素或影响. Alpine用的是***musl***而不是Linux主流的***libc***等,在选择时不能一概而论.

关于这个,我过往写过专门的文章,需要了解的可以参阅: 对Docker基础镜像的思考,该不该选择alpine

  1. 使用多平台构建

虽然服务器主流都是X64架构的, 但这并不是完全. ARM架构现在也越来越多的被使用,特别在国内, 统信主流是ARM而不是X64.

在构建你的镜像时,不要只考虑支持X64架构. 而应该考虑支持多平台, 构建一次,支持不同的架构是最佳实践. 事实上, 大多数编程语言都没有说只支持X64, 那你基于这些编程语言构建出来的东西,理论上也不会只依赖特定平台.

Docker的buildx是专门支持多平台的, 而在Docker Hub中,你只要稍等用心都会发现主流的镜像都是支持多平台的.

关于如何基于buildx构建多平台镜像,我写过专门的文章供参阅: Docker多平台镜像构建指引

  1. 利用多阶段构建

有时候,构建Docker镜像有一个很不好的问题,就是一些编译语言的依赖包下载. 比如Java中, 如果你不会多阶段构建,而又在镜像中编译项目的话,那每次都要下载maven或gradle中定义的那些依赖.

这个耗时非常久,而且浪费网络.

而针对这个困境, Docker特别提供了多阶段镜像. 多阶段构建大致就是指把一个Docker镜像构建分为多个阶段. 比如以上面的Java服务为便,利用多阶段构建你可以做成这样

  • 阶段一: 编译项目,这个过程会下载依赖
  • 阶段二: 构建真正的镜像

这样不同阶段的好处在于, 如果你的依赖定义文件没有发生变更的前提下, 阶段一的构建Docker会缓存,意味着下次在再构建时, 阶段一会直接跳过去,使用缓存.

这样就解决了前面的问题.

  1. 善用.dockerignore文件

如果你构建Docker镜像,都从来没有定义,甚至不知道.dockerignore的存在, 那就不应该了.

在构建Docker镜像的过程中, Docker会先将本地的一个目录加载到Context上下文中,你才能COPY等. 但是项目中的很多目录,比如java中的build目录, npm中的node_modules其实并不需要加载到Context中, 因为我们会在构建过程中重新编译生成这些目录或文件.

这时候,你可以利用.dockerignore文件来忽略这些目录. 它的使用方式与.gitignore大致类似.

不要觉得这个无所谓或多此一举, 如果你没有合理的设置, 它会影响镜像的构建时间,更不好的是极大的加大你镜像的大小.

  1. 不要使用root用户

我见过很多程序员或运维人员, 一直使用root用户来部署或运维Linux系统. 这是非常不专业的做法.

这个行为在docker镜像中也是存在的, 很多人构建Docker镜像, 完全没有意识到Docker镜像中也存在用户的概念. 没有对这个做任何处理, 这意味着你就是使用Root用户在运行这个镜像服务.

从安全上来说,这是非常不妥当的.

代码语言:javascript
复制
FROM eclipse-temurin:17.0.6_10-jre
RUN useradd -ms /bin/bash lingen
USER lingen

定义一个用户是非常简单的, 如上代码所示. 只要这样, 这个镜像运行时, 就是以你定义的用户来运行.

当然,在一些复杂的镜像构建中,要考虑用户权限,及后续挂载Host Volume时可能会有权限上的问题. 这一点后续我有时间再单独聊一下.

  1. 扫描你的镜像

镜像做好后,花点时间来分析与扫描一下你的镜像,也是非常有必要的. 在安全上这一步不可少.

大多数人可能都没有这个意识, 但安全非常重要.

而Docker官方其实提供了工具,也就是Docker Scout,专门干这个的. 稍微花点时间学习研究下如何使用这个工具,再利用它来优化与加固你的镜像, 是非常好的做法.

最后

上面这些点就是我在构建镜像时,会特别注意的一些点, 相比过往,Dccker确实方便很多. 善用Docker, 能极大的简化我们服务的部署与运维.

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微言码道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最后
相关产品与服务
容器镜像服务
容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档