如何Docker化任意一个应用
网上有很多关于如何将应用Docker化的教程,为什么我还要再写一个呢?
\\
我见过的大部分教程都是限定在某种特定技术(例如Java或者Python),可能无法满足读者的需求。同时,这些教程也没有说清楚关于Dev和Ops团队之间建立明确约定所涉及到的所有相关方面(这正是容器化的精髓所在)。
\\
我根据最近的经验总结了以下一些步骤。它是一份细节清单,包含了其他指南中忽略的内容。
\\
声明:这不是一份新手指南。我建议读者先掌握一些如何设置和使用docker的基础知识,并且创建和运行一些容器之后,再来阅读。
\\
让我们开始吧。
\\
一、选择基础镜像
\\
每种对应技术几乎都有自己的基础镜像,例如:
\\
- https://hub.docker.com/_/java/\\t
- https://hub.docker.com/_/python/\\t
- https://hub.docker.com/_/nginx/\
如果不能直接使用这些镜像,我们就需要从基础操作系统镜像开始安装所有的依赖。
\\
外面有很多教程使用的都是Ubuntu(例如 ubuntu:16.04)作为基础镜像,这不能算有问题,但是我建议优先考虑Alpine镜像:
\\
https://hub.docker.com/_/alpine/
\\
它是一个非常小的基础镜像(大约只有5MB)。
\\
注意:在基于Alpine的镜像中无法使用“apt-get”命令,Alpine系统有自己的软件包仓库和包管理工具。详细请参考:
\\
https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management
\\
https://pkgs.alpinelinux.org/packages
\\
二、安装必要软件包
\\
这个步骤通常比较琐碎,有一些容易忽略的细节:
\\
- apt-get update和apt-get install命令应该写在一行(如果使用Alpine则对应的是apk命令)。这不是常见的做法,但是在Dockerfile中应该要这么做,否则“apt-get update”命令产出的临时层可能会被缓存,导致构建时没有更新包信息(参见 https://forums.docker.com/t/dockerfile-run-apt-get-install-all-packages-at-once-or-one-by-one/17191 这个讨论)。\\t
- 确认是否只安装了实际需要的软件(尤其是这个容器会在生产环境中运行)。我看见过有人在他们的镜像中安装了vim和其他开发工具。\
如果有必要,针对构建、调试和开发环境创建不同的Dockerfile。这不仅仅关系到镜像大小,还涉及到安全性、可维护性等等。
\\
三、添加自定义文件
\\
一些优化Dockerfile的小提示:
\\
- 理解COPY和ADD指令的区别,详见:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy。\\t
- (尽可能)遵照文件系统惯例来存放文件:http://www.pathname.com/fhs/。例如针对解释型应用程序(如Python),使用/usr/src目录。\\t
- 检查添加文件的属性。如果需要可执行权限,没有必要在镜像上新建一个层(通过“RUN chmod +x ...”指令),只需要在代码仓库的源文件上修正这些属性即可。即使开发平台是Windows,也可以参照下文给文件增加可执行权限:https://stackoverflow.com/questions/21691202/how-to-create-file-execute-mode-permissions-in-git-on-windows。\
四、定义容器运行时的用户权限
\\
现在可以休息一下,阅读下这篇不错的的文章:Understanding how uid and gid work in Docker containers。
\\
读完这篇文章,我们会了解:
\\
- 仅当应用程序需要访问用户或组数据(/etc/passwd或/etc/group)时,才需要在容器启动时指定固定的用户ID。\\t
- 尽可能避免容器以root权限运行。\
不幸的是,不少热门应用程序镜像需要用特定的用户id来运行(例如Elastic Search需要uid:gid = 1000:1000)。尽量不要在写出这样的镜像……
\\
五、定义暴露的端口
\\
这也是一个微不足道的小操作,但是不要为了暴露特权端口(例如80)而将容器以root权限运行。如果有这样的需求,可以让容器暴露一个非特权端口(例如8080),然后在启动时进行端口映射。
\\
关于特权端口和非特权端口的不同:https://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html。
\\
六、定义入口点(entrypoint)
\\
普通方式:直接运行可执行文件。
\\
更好的方式:创建一个“docker-entrypoint.sh”脚本,可以用来通过环境变量来配置容器的入口点(具体请参照下一节)。
\\
这是一个非常普遍的做法,这里有一些例子:
\\
https://github.com/elastic/elasticsearch-docker/tree/master/build/elasticsearch/bin
\\
https://github.com/docker-library/postgres/tree/de8ba87d50de466a1e05e111927d2bc30c2db36d/10
\\
七、定义一种配置方式
\\
基本上每个应用程序都需要参数化。基本上有两条路可以遵循:
\\
- 使用应用程序特定的配置文件:该方式需要通过文档说明配置文件的格式、字段、放置位置等等(当运行环境比较复杂,例如应用程序跨越不同的技术,则不太合适)。\\t
- 使用(操作系统)环境变量:简单而有效。\
如果读者认为这种方式不够现代,记住这也是12-factors推荐的方式:
\\
https://12factor.net/zh_cn/config
\\
这并不意味着我们可以抛开所有的配置文件,并对应用程序进行重构,去除配置文件机制。只需要通过envsubst命令来替换配置文件模板(这个流程需要在docker-entrypoint.sh文件中完成,因为这需要在运行时完成)。
\\
例如:
\\
https://docs.docker.com/samples/library/nginx/#using-environment-variables-in-nginx-configuration
\\
这种方式可以将应用程序的配置文件封装在容器内部,无须让使用者了解这些细节。
\\
八、外部化数据
\\
关于数据存储有一条黄金法则:绝对不要将任何持久化数据保存到容器内。
\\
容器的文件系统被设计成临时和短暂的。因此任何由应用程序生成的内容、数据文件和处理结果都应该保存到挂载的卷或者操作系统绑定挂载点上(既将宿主机操作系统的目录挂载到容器中)。
\\
对于挂载卷我不太有经验,因此我个人更倾向于将数据保存到绑定挂载点(bind mounts)。这些挂载点一般通过类似Salt Stack这样的配置管理工具仔细的在宿主机上创建。
\\
这里说的“仔细创建”,主要包括下面几个步骤:
\\
- 在宿主机操作系统上创建非特权用户(和组)。\\t
- 所有需要绑定目录的所有者都是该用户。\\t
- 根据使用场景给授权(仅针对这个特定的用户和组,其他用户无权访问)。\\t
- 容器也以该用户运行。\\t
- 此时容器就可以完全控制这些目录。\
九、确保处理好日志
\\
前面关于“持久性数据”没有一个明确的定义,日志在这里就是灰色地带。我们该如何处理它们呢?
\\
如果这是一个新的应用程序,并且希望它能够坚持docker约定,就不应该将日志写入文件。应用程序应该使用标准输出和标准错误输出日志。和之前推荐使用环境变量一样,这也是12-factors之一:
\\
https://12factor.net/zh_cn/logs
\\
Docker会自动捕捉应用程序的标准输出,并可以通过“docker logs”命令查看:
\\
https://docs.docker.com/engine/reference/commandline/logs/
\\
当然还有一些实际场景下会遇到问题。例如运行一个简单的nginx容器,至少会有两种不同的日志文件:
\\
- HTTP访问日志(Access Logs)\\t
- 错误日志(Error Logs)\
对于这种日志按照特定结构输出的应用,可能不太适合将它们的日志输出到标准输出。这个例子中,只需要按照前面一节中说的处理好持久化问题,并确保正确配置文件的轮转。
\\
十、轮转日志和其他仅追加文件
\\
如果应用程序将日志写到文件,或者会无限追加内容到文件,就需要关注这些文件的轮转(rotation),这对于防止服务器空间耗尽非常有用(尤其是GDPR和其他数据安全条例出来之后)。
\\
如果使用绑定挂载,我们可以依靠宿主机的一些工具来实现文件轮转功能,例如logrotate(文档参见https://linux.die.net/man/8/logrotate)。
\\
最近我找到的一个简单且完整的例子:
\\
https://www.aerospike.com/docs/operations/configure/log/logrotate.html
\\
另外一个例子:
\\
https://www.digitalocean.com/community/tutorials/how-to-manage-logfiles-with-logrotate-on-ubuntu-16-04
\\
查看英文原文:How to dockerize any application
\\
感谢张婵对本文的审校。
如何Docker化任意一个应用相关推荐
- Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅
2019独角兽企业重金招聘Python工程师标准>>> 通过 Docker 化一个博客网站来开启我们的 Docker 之旅 这篇文章包含 Docker 的基本概念,以及如何通过创建一 ...
- 游戏研发与运营环境Docker化
在泛娱乐时代,游戏行业特殊的业务特点为技术团队提出了更高的要求,而Docker对游戏研发的运营环境带来了很多好处.发展至今,游戏研发的行业现状是怎么样的?Docker和架构改进之间如何应用?通过Doc ...
- Docker化运维方式
应用迁移需求 应用运维需要考虑的一个重要问题就是迁移, 在不同机器.机房.环境间迁移.迁移的原因有很多, 比如硬件过保(硬件故障), 机房迁移, 应用扩缩容等. 应用迁移的核心需求是: 简单.迁移操作 ...
- 【Docker学习之旅】手把手教你Java应用进行Docker化
手把手教你Java应用进行Docker化 简介:简介:Docker是一个使用Go语言开发的开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的机器上. 一 ...
- scala akka_如何对Scala和Akka HTTP应用程序进行Docker化-简单的方法
scala akka by Miguel Lopez 由Miguel Lopez 如何对Scala和Akka HTTP应用程序进行Docker化-简单的方法 (How to Dockerise a S ...
- Node.js 指南(Docker化Node.js Web应用程序)
Docker化Node.js Web应用程序 此示例的目的是向你展示如何将Node.js应用程序放入Docker容器中,该指南旨在用于开发,而不用于生产部署,本指南还假设你有一个有效的Docker安装 ...
- docker部署python web应用_在 Docker 中运行一个 Python 的 Web 应用
几周前, Elastic Beanstalk声明在AWS云中配置和管理Docker容器.在本文中,我们通过一个简单的注册表单页面应用去理解Docker部署过程,该表单使用Elastic Beansta ...
- python django部署docker_如何Docker化Python Django应用程序
Docker是一个开源项目,为开发人员和系统管理员提供了一个开放平台,可以将应用程序构建.打包为一个轻量级容器,并在任何地方运行.Docker 会在软件容器中自动部署应用程序. Django 是一个用 ...
- docker化你的java应用(下)
前言 在<docker化你的java应用(上)>中,我们已经初步接触了docker的核心概念与思想,本篇博客将对docker进行实践,会介绍一些docker常用的命令,并通过docker化 ...
最新文章
- fiddler设置中文版本_教你下载iOS老版本应用
- 腾讯搜搜退出PC搜索领域:百度搜狗迎来双龙竞争
- android jni 调用java对象_Android NDK开发之Jni调用Java对象
- c++指定在某一线程运行_深入理解Java虚拟机-运行时数据区
- mysql 结果集换页_MySQL 按结果集分页
- 时分多路数字电话基带传输系统
- 入门到精通pl/sql编程(千里之行始于足下)之触发器
- 计算机主板平面图,电脑主板图纸
- 黄梯云,李一军.管理信息系统(第七版)[M].北京:高等教育出版社,2019.8 课后二维码选择题
- IDEA破解方法,永久激活
- php 展示微信图片尺寸,微信公众平台图片最大尺寸
- 人大金仓安装教程(windows)
- Grow up way
- neu1482 2014辽宁省赛Picking Cabbage(状态压缩)
- 基于java web的网上书店系统
- npm ERR! code E404 npm ERR! 404 no such package available : xxx
- ORACLE之ora-01722和ORA-01403的错误测试
- 【无标题】C++错题本
- 联想yoga11装linux,Yoga PC阻止用户安装Linux 联想:微软的锅feilianmeidong
- 北航2022软件工程第三次作业——结对编程(最长英语单词链)
热门文章
- main 函数内的变量是全局变量,还是局部变量?
- java版spring cloud+spring boot+redis社交电子商务平台(八)消息总线(Spring Cloud Bus)
- 一卡通变“一脸通” 人脸识别更新校园考勤方式
- 利用反射机制获取未知类型的枚举的信息
- Nginx加入linux系统服务
- 概述VB.NET正则表达式简化程序代码
- 图文详解安装NetBackup 6.5备份恢复Oracle 10g rac 数据库(修订)
- Pet Shop4解密配置文件
- access和SQL的区别
- 栈堆的emplace和push_C++(STL):19---deque之删除和emplace用法