「执笔人」品高股份 cloud native 资深架构师:继承
随着云计算和容器技术的迅速发展,越来越多的企业选择进行应用容器化转型,以提高应用交付效率和灵活性。在这个转型过程中,制作高效的容器镜像成为关键一环。
那么什么是容器镜像,制作容器镜像需要关注哪些关键要素,怎么做镜像才是最佳实践,本文将对这些内容做概要性的梳理,以帮助对容器镜像不太熟悉的读者,快速构建容器镜像相关的知识体系。
容器镜像的相关概念
什么是容器镜像
容器镜像是一个轻量级、可执行的软件包,包含了运行特定应用程序所需的一切组件,包括操作系统、库、应用程序和配置。它采用分层结构构建,每一层都可以看作是一个文件系统的快照。容器镜像的主要组成部分包括基础镜像、应用程序代码、依赖项和配置文件,除此之外,还包含了一些为运行时准备的配置参数(如匿名卷、环境变量、用户等)。
简单来说,一个镜像最小化,大概包含这些内容:
• 基础镜像:尽量挑选小镜像,规避不需要的内容
• 应用文件:应用的执行程序,例如 java 的 jar 包、war 包
• 环境变量:程序运行所需的环境变量
• 执行命令:容器的启动命令,也可以不填,在执行的时候传入
什么是镜像仓库
镜像仓库,字面意思理解就是存储镜像的仓库,可以用来存储、分发镜像文件。仓库又分为公有镜像仓库和私有镜像仓库。
公有镜像仓库,比如 docker 的仓库 hub.docker.com,或者国内公有云厂商的镜像仓库,例如阿里云 registry.cn-beijing.aliyuncs.com,可以直接注册账号使用。
为了安全性和隐私性,大部分企业会建立自己的私有仓库,比如通过 Harbor 搭建企业级的私有仓库,Harbor 是一款开源的,比较成熟的企业级私有仓库,可支持仓库按项目权限划分,集成企业统一认证,还包括如镜像同步复制、漏洞扫描和权限管理等企业级别的管理功能。如果您要做企业级仓库的选型,那么 Harbor 是首选的解决方案。
但是企业级意味着部署架构比较臃肿,如果您只是需要一个轻量级的镜像托管服务,不希望部署太重的 Harbor,那么 Docker 提供的 Docker Registry 也是一个不错的选择,这同样是一款开源的镜像仓库,运行内存只有几十 MB,但是镜像的托管、分发功能都是齐全的,满足容器应用开发的需求。
获取公开镜像
更多的时候,我们可以使用社区已经制作好的镜像,包含各类开源组件、中间件、数据库等的镜像,这种镜像在上文提到的公开镜像仓库中都能够找到,例如 docker hub ,或者国内阿里云、腾讯云的公开仓库中。
除此之外,还有一类镜像,是托管在国外的 hub 仓库中,并且无法正常访问,例如早期 k8s 的镜像仓库 k8s.gcr.io,这类仓库都需要科学上网才能获取到。
上传到私有仓库
大多数企业内部的环境是无法连接公网的,我们需要将公网的镜像下载到本地,然后打 tag 之后,推送到本地镜像仓库中使用。
容器镜像制作的关键要素
1.选择合适的基础镜像
基础镜像是容器镜像的起点,它提供了操作系统和运行时环境。选择一个稳定、安全且适合应用需求的基础镜像是制作高质量容器镜像的基础。
如果没有特殊要求,那么应该选择一个尽可能小的基础镜像,例如 alpine 镜像,非常的小,但是对应的类库也是最精简的,很多工具都是需要自行安装,与之对应的是操作系统镜像,例如 ubuntu, centos 等,功能齐全,但是镜像大小达到上百 MB,或者类似 busybox 之类的带有一部分调试工具的基础镜像。
有时候镜像有一些要求,比如是否应该带有语言的运行时,例如 openjdk 、python 等,或者一些常见的中间件、数据库,例如 tomcat、nginx 、mysql 等。
选择合适的镜像,可以让我们少做很多工作。
2.容器镜像的架构
与传统部署一样,容器镜像的架构也是要关注的关键要素,常见的例如 arm64 和 amd64,以及其他例如 linux/386 、linux/arm/v7 等相对较少使用的架构。
如果容器可能在多种架构中运行的话,那么多架构镜像构建,就是一个必选项,多架构镜像通过 manifest 机制,将多个镜像关联起来,每当拉镜像的时候,Docker 会根据机器的系统架构自动拉取对应的镜像。参见文档如何制作多架构镜像。
3.优化容器镜像大小
精简容器镜像大小对于提高镜像的传输速度和存储效率至关重要。可以采取一些策略,如删除不必要的文件、合并多个层、使用多阶段构建等来减小镜像的体积。多阶段构建的方法参见:如何进行多阶段构建。
4.镜像的时区设置
大部分镜像,尤其是常见的基础镜像,例如 Alpine,Ubuntu,Debian,CentOS 等,基本上都采用 UTC 时间,默认时区为零时区,而我们主要用的是 CST 时间,北京时间,位于东八区,时区代号:Asia/Shanghai。两个时区相差 8 小时,当我们查看容器的日志时,经常会发现日志的时间与当前时间刚好相差 8 小时,就是这个原因。
调整容器的时区,可以在容器运行时调整,也可以在镜像制作的时候调整,这里只简单介绍一下如何在制作镜像的时候设置时区:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
5.离线镜像的导入导出
企业在使用镜像的时候,经常会遇到一种场景,在开发环境做好的镜像,由于网络不通,无法直接推动到某些环境中,例如客户的私有环境,或者某些隔离性要求很高的生产环境,这时就需要做镜像的离线导入导出了。
镜像的导入导出也属于镜像比较常见的操作了,例如:
docker save <id> > some-image.tardocker load < some-image.tar
除此之外,还可以适用 docker import/export 方法,本篇文章不对这两种导入导出方法做展开讨论。
6.镜像的调试方法
这是一个很重要的点,对于大多数新同学刚开始做镜像的时候,会遇到的坑:镜像制作完成后,无法正常启动。除了根据日志分析,或者反复重新测试以外,我们还有一些常见的调试方法,便于我们对问题进行定位和分析:
• 通过覆盖启动命令,让容器不要启动:我们可以在启动容器时覆盖其 entrypoint 命令,比如设置为--entrypoint=bash ,让主程序不要启动,这样我们就可以进入该容器,手动的测试我们的启动命令
• 通过 cp 命令拷贝文件:除了制作镜像时可以传入文件以外,我们还可以在容器运行态,传入传出文件,用于调试
• 安装调试的类库:有时候你需要一些调试工具,例如 telnet、nslookup、curl、wget 等,确保你的镜像是配置了可用的源,以便我们在需要的时候,能够在容器中安装这些类库,用于调试
容器镜像的最佳实践
容器镜像的制作本身非常简单,编写好 Dockerfile 文件,之后使用 docker build 命令即可完成构建。但是更多的时候,这种命令执行的方式非常繁琐,尤其在敏捷研发过程中,版本更新迭代的速度非常高,我们迫切的希望镜像的制作能够自动化,通过持续集成的能力,将容器镜像制作纳入到 CI 流程中。
业界实现 CI 的工具有很多,例如 Jekins、Tekton 等,而 CI 的流程大概都是这几个环节: 拉取代码、构建程序、构建镜像、推送镜像。其中 CI 工具在做构建镜像的时候,通常都会集成多架构构建的工具,例如:
buildkit: 由 docker 公司开发,目前由社区和 docker 公司合理维护的“含着金钥匙出生”的新一代构建工具,拥有良好的扩展性、极大地提高了构建速度,以及更好的安全性。
Kaniko:由 Google 开发的在 k8s 上做 docker 构建的命令行工具,使用非常简洁,只需要 build 一个二进制工具即可,支持 dockerfile 构建、push、credentail 文件读取。
Img: 是由社区贡献者开发,基于 buildkit 封装的类 docker 化命令行工具,无需 daemon 进程,无需 privilege,可以独立运行的二进制工具,非常小巧易用,而且有着和 buildkit 一样的性能优势。
这几款产品中,buildkit 目前来说相对成熟,对于第一次尝试使用这类“Build without docker daemon”的工具的同学来说,上手比较简单,性能也不错。
总结
容器镜像制作是企业进行应用容器化转型的重要一环。本文从容器镜像的概念和组成开始,介绍了容器镜像制作的关键要素和最佳实践。通过遵循这些理论指导,企业可以更高效、安全地制作容器镜像,为应用容器化转型奠定坚实的基础。随着容器技术的不断发展,容器镜像制作领域还有许多新的挑战和机遇,希望本文能够为读者提供启发和指导,促进企业在容器化转型过程中取得更好的成果。