写点什么

如何使用本地 Docker 更好地开发?我们总结了这八条经验

  • 2022-05-19
  • 本文字数:2705 字

    阅读完需:约 9 分钟

如何使用本地Docker更好地开发?我们总结了这八条经验

如果你像我们一样需要运行许多不同的应用程序,那么将开发环境容器化可以极大地提高工作效率。这里有一些可以优化本地 Docker 环境的技巧。

 

在 Viget,Docker 已经成为本地开发不可或缺的工具。我们的团队构建和维护着大量的应用程序,运行着不同的软件栈和版本,并且能够将开发环境打包,这让不同项目的切换和开发人员快速上手新项目变得非常容易。这并不是说在本地使用 Docker 开发就没有缺点,但它带来的便利远远超过了缺点。

 

随着时间的推移,我们总结出了自己的一套最佳实践,可以有效设置 Docker 开发环境。请注意最后一点(“本地开发”)——如果你是为了部署而创建镜像,那么这些原则中的大多数都不适用。我们的开发环境一般包括(通过 Docker Compose 编配):

 

  • 应用程序(例如 Rails、Django 或 Phoenix);

  • JavaScript 监视器/编译器(例如 webpack-dev-server);

  • 数据库(通常是 PostgreSQL);

  • 其他必要的基础设施(如 Redis、ElasticSearch、Mailhog);

  • 有些应用程序实例偶尔也会做一些其他的事情,而不只是运行开发服务器(比如后台任务)。

 

基于这样的架构,以下是我们试图进行标准化的最佳实践。

 

不要将代码或应用级的依赖项放入镜像中

 

你的主 Dockerfile 文件,也就是运行应用程序所需的文件,应该包含运行应用程序所需的所有软件,但不应该包含应用程序代码本身——当 docker-compose run 命令开始执行时,它们将被挂载到容器中,并在容器和本地机器之间进行同步。

 

另外,区分系统级依赖项(如 ImageMagick)和应用级依赖项(如 Rubygems 和 NPM 包)也很重要——前者应该包含在 Dockerfile 中,后者不应该。将应用级依赖项放到镜像中意味着每次有人添加新依赖项时都必须重新构建镜像,这既耗时又容易出错。相反,我们应该将这些依赖项作为启动脚本的一部分。

 

非必要不使用 Dockerfile

 

基于第一点,你可能会发现根本不需要编写 Dockerfile 文件。如果你的应用程序没有任何特殊的依赖项,可以将 docker-compose.yml 的入口指向官方的 Docker 仓库(如 ruby:2.7.6)。这样做并不常见——大多数应用程序和框架都需要一定数量的镜像基础(例如,Rails 需要 Node),但如果你发现自己的 Dockerfile 只包含一个 FROM 行,你就可以不使用这个文件。

 

只在 docker-compose.yml 中引用一次 Dockerfile

 

如果你将同一个镜像用于多个服务(你应该这么做),只需要在一个服务的定义中提供构建说明,给它起一个名字,然后在其他服务中引用这个名字。举个例子,假设有个 Rails 应用程序使用一个共享的镜像来运行开发服务器和 webpack-dev-server,那么配置可能像这样:

 

services:  rails:    image: appname_rails    build:      context: .      dockerfile: ./.docker-config/rails/Dockerfile    command: ./bin/rails server -p 3000 -b '0.0.0.0'

node: image: appname_rails command: ./bin/webpack-dev-server
复制代码

 

这样,当我们在构建服务(使用 docker-compose)时,镜像就只构建一次。如果我们省略 image:指令同时复制 build:,就会构建完全相同的镜像两次,这样会浪费磁盘空间和有限的时间。

 

在命名卷中缓存依赖项

 

正如第一点所提到的,我们不会将代码依赖项放到镜像中,而是在启动时安装它们。可以想象的是,如果我们每次重启服务时都从头开始安装 gem/pip/yarn 这样的库,速度会非常慢,所以我们使用 Docker 的命名卷来保持缓存。上面的配置可能会变成这样:

 

volumes:  gems:  yarn:  services:  rails:    image: appname_rails    build:      context: .      dockerfile: ./.docker-config/rails/Dockerfile    command: ./bin/rails server -p 3000 -b '0.0.0.0'    volumes:      - .:/app      - gems:/usr/local/bundle      - yarn:/app/node_modules

node: image: appname_rails command: ./bin/webpack-dev-server volumes: - .:/app - yarn:/app/node_modules
复制代码

 

命名卷的挂载点可能因不同的软件栈而异,但原则是差不多的:将编译后的依赖项保存在已命名的卷中,以大幅缩短启动时间。

 

将临时的东西放入命名卷中

 

上一点提到使用命名卷来提高性能,这里有另一个有用的技巧:将保存只读文件的目录放入命名卷中,阻止它们被同步回本地机器(这会带来很大的性能开销),特别是 log 和 tmp 目录,以及应用程序存储上传文件的地方。

 

根据经验,如果一个目录出现在.gitignore 中,那么最好把它放入命名卷中。

 

在 apt-get 更新后进行清理

 

如果在 Dockerfiles 中引用了基于 Debian 的镜像,你就必须运行 apt-get update,然后才能通过 apt-get install 安装依赖项。如果不做一些处理,一堆额外的数据会被放到镜像中,极大增加了镜像的体积。

 

我们的最佳实践是在一个 RUN 命令中执行更新、安装和清理操作:

 

RUN apt-get update && \  apt-get install -y libgirepository1.0-dev libpoppler-glib-dev && \  rm -rf /var/lib/apt/lists/*
复制代码

 

使用 exec 而不是 run

 

如果需要在容器中运行命令,你有两个选项:run 和 exec。前者将启动一个新容器来运行命令,而后者将连接到一个已经在运行中的容器。

 

在大多数情况下,假设在开发应用程序时总是有其他服务在运行,那么 exec(特别是 docker-compose exec)就是你所需要的,因为它运行起来更快,而且不会留下任何奇怪的文件(如果你忘了在 run 中包含--rm 标志,就会发生这种情况)。

 

使用 wait-for-it 协调服务

 

如果使用了之前提到的共享镜像和依赖项命名卷,你可能会遇到这样的问题:一个服务会在另一个服务的入口点脚本执行完毕之前启动,从而导致发生了错误。当出现这种情况时,我们可以引入wait-for-it脚本,它将向一个 Web 地址发起请求,当这个地址返回响应时再执行命令。

 

所以,我们把 docker-compose.yml 修改一下:

 

volumes:  gems:  yarn:  services:  rails:    image: appname_rails    build:      context: .      dockerfile: ./.docker-config/rails/Dockerfile    command: ./bin/rails server -p 3000 -b '0.0.0.0'    volumes:      - .:/app      - gems:/usr/local/bundle      - yarn:/app/node_modules

node: image: appname_rails command: [ "./.docker-config/wait-for-it.sh", "rails:3000", "--timeout=0", "--", "./bin/webpack-dev-server" ] volumes: - .:/app - yarn:/app/node_modules
复制代码

 

这样,在 Rails 开发服务器完全启动并运行之前,webpack-dev-server 是不会启动的。

 

以上就是我们在过去几年中总结的一些 Docker 最佳实践,我们也将努力保持更新这个清单。

 

原文链接:

 

https://www.viget.com/articles/local-docker-best-practices/

 

2022-05-19 21:035971

评论

发布
暂无评论
发现更多内容

华为云Flexus X实例:最高6倍算力提升,满足中小企业多元需求

轶天下事

蓝易云 - linux压缩webfile文件夹 webfile.tar.gz和webfile.tar的区别

百度搜索:蓝易云

Linux 运维 云服务器 压缩 解压

掀起性能巨浪:华为云Flexus X实例定义业务负载多面手

YG科技

华为云Flexus X实例全方位创新,开拓企业数字化发展新边界

YG科技

蓝易云 - 如何选择合适的服务器进行外贸建站?

百度搜索:蓝易云

服务器 云服务器 建站 搭建网站 高防服务器

商品搜索API的未来:1688阿里巴巴引领智能化电商新趋势

代码忍者

API 接口 API 文档 API 测试

手把手教你如何参与开源,详细实用~

XIAOJUSURVEY

GitHub 开源 PR 贡献者 Issue

双倍性能、跃级体验!华为云Flexus X实例助力企业轻松上云

轶天下事

顺应数字化趋势!Flexus X实例助力中小企业开启数字转型“必修课”

轶天下事

中小企业数字化转型难点凸显,华为云Flexus X实例革新上云体验

YG科技

聚焦中小企业实际需求,华为云Flexus X实例推动中小企业数字化转型愿转敢转

轶天下事

顺应数字化浪潮,华为云Flexus X实例助力中小企业破局崛起

轶天下事

低代码实践:题型物料化设计(四)

XIAOJUSURVEY

低代码 schema 源码解读 配置化 设置器

蓝易云 - Linux之autofs自动挂载服务

百度搜索:蓝易云

云计算 Linux 运维 云服务器 autofs

华为云Flexus X实例创新柔性算力技术,助力企业提质、增效、降本

轶天下事

重塑云服务,华为云Flexus X实例破解云服务传统难题

YG科技

数字化转型正当时!华为云Flexus X实例助力中小企业高效便捷上云

轶天下事

蓝易云 - MySQL之MHA高可用配置及故障切换

百度搜索:蓝易云

MySQL 云计算 运维 云服务器 MHA

华为云Flexus X实例全方位为中小企业护航,加速迈进“专精特新”

YG科技

激发云服务效能,华为云Flexus X实例助力破除中小企上云痛点

YG科技

数字转型得力伙伴:华为云Flexus X实例打造云计算应用新标杆

轶天下事

充分发挥数字经济赋能效应,Flexus X实例协助企业高效转型

轶天下事

华为云Flexus X实例创新柔性算力!直击中小企业数字化转型痛点

轶天下事

华为云Flexus X实例依托生态优势,破解中小企业数字化转型困局

YG科技

无缝升级云服务,华为云Flexus X实例带来零门槛极致体验

YG科技

聚焦中小企业上云用云需求,华为云Flexus X实例带来双倍性能、跃级体验

YG科技

蓝易云 - rke2在线部署kubernetes

百度搜索:蓝易云

Linux Kubernetes 运维 k8s RKE2

华为云Flexus X实例,以创新技术打造中小企业高质量发展新引擎

轶天下事

直击中小企业痛点!Flexus X实例让中小企业也可以上好云、用好云

YG科技

性能易用二合一!华为云Flexus X实例加快中小企数字转型

YG科技

华为云Flexus X实例性能出众,降本显著,促进中小企业高质量发展

YG科技

如何使用本地Docker更好地开发?我们总结了这八条经验_语言 & 开发_David Eisinger_InfoQ精选文章