架构师教你如何 10 步 Docker 化一个应用

前言java

本文将讲解如何将应用 Docker 化的一些很实用的技巧和准则,推荐一读。python

1、选择基础镜像nginx

每种对应技术几乎都有本身的基础镜像,例如:面试

  • https://hub.docker.com/_/java/
  • https://hub.docker.com/_/python/
  • https://hub.docker.com/_/nginx/

若是不能直接使用这些镜像,咱们就须要从基础操做系统镜像开始安装全部的依赖。docker

网上大多数教程使用的都是以 Ubuntu(例如:Ubuntu:16.04 )做为基础镜像,这并非一个问题,可是我建议优先考虑 Alpine 镜像:vim

  • https://hub.docker.com/_/alpine/

Alpine 是一个很是小的基础镜像(它的容量大约只有 5MB)。缓存

注:在基于 Alpine 的镜像中你没法使用 apt-get 命令。不过你没必要担忧,由于 Alpine 系统有本身的软件包仓库和包管理工具 apk。关于 apk 的具体使用你能够详细参考:「Alpine Linux配置使用技巧」一文。安全

2、安装必要软件包性能优化

这个步骤一般比较琐碎,有一些容易忽略的细节:服务器

  • apt-get update 和 apt-get install 命令应该写在一行(若是使用 Alpine 则对应的是 apk 命令)。这不是一个常见的作法,可是在 Dockerfile 中应该要这么作。不然 apt-get update 命令产出的临时层可能会被缓存,致使构建时没有更新包信息。(具体可参见此文)。
  • 确认是否只安装了实际须要的软件(特别是在生产环境中运行这个容器)。

注:我见过有人在他们的镜像中安装了 vim 和其余开发工具。若是这是必要的,应该针对构建、调试和开发环境建立不一样的 Dockerfile。这不只仅关系到镜像大小,还涉及到安全性、可维护性等等。

3、添加自定义文件

一些优化 Dockerfile 的小提示:

  • 理解 COPY 和 ADD 指令的区别,具体可参考此文。
  • 尽量遵守文件系统惯例来存放文件。例如:针对解释型应用程序(如:Python),使用 /usr/src 目录。
  • 检查添加文件的属性。若是须要可执行权限,没有必要在镜像上新建一个层( 经过 RUN chmod +x … 指令来增长权限)。你只须要在代码仓库的源文件上修正这些属性便可,即便开发平台是 Windows,也能够参照此文给文件增长可执行权限。

4、定义容器运行时的用户权限

  • 容器中的进程默认状况下是以 root 权限运行的。
  • 若是容器中的应用程序须要使用特定的用户或组(/etc/passwd 或 /etc/group)来运行时,能够在容器启动时使用 docker run 命令的 --user 参数来指定其固定的 UID 或 GID。
  • 尽量避免容器中的进程以 root 权限运行。

注:如今很多热门应用程序镜像都须要用特定的用户 ID 来运行(例如:Elastic Search 须要 uid:gid = 1000:1000),请尽可能不要在写出这样的镜像。更多关于容器内运行应用程序的权限说明可参考此文。

5、定义暴露的端口

不要为了暴露特权端口(例如:80)而将容器以 root 权限运行。若是有这样的需求,可让容器暴露一个非特权端口(例如:8080),而后在启动时进行端口映射。

注:低于 1024 的 TCP / IP 端口号就是特权端口,由于不容许普通用户在这些端口上运行服务。

6、定义入口点(entrypoint)

  • 普通方式:直接运行可执行文件。
  • 更好的方式:建立一个 docker-entrypoint.sh 脚本,这样能够经过环境变量来配置容器的入口点。这也是一个很是广泛的作法,可参考下面这些例子:elasticsearch 的 docker-entrypoint.sh 文件 和 postgres 的 docker-entrypoint.sh 文件。

7、定义一种配置方式

每一个应用程序都须要参数化,你基本上能够遵循如下两个原则:

  • 使用应用程序特定的配置文件:该方式须要经过文档来讲明配置文件的格式、字段、放置位置等等(当运行环境比较复杂,例如:应用程序跨越不一样的技术,则不太合适)。
  • 使用操做系统环境变量:简单而有效。这也是 12-factors 推荐的方式。

注:使用环境变量方式并不意味着您须要丢弃配置文件并重构应用程序的配置机制,你只须要经过 envsubst 命令来替换配置文件模板中的值就能够了(这个流程通常须要在 docker-entrypoint.sh 文件中完成,由于这须要在容器进程运行前完成)。例如:在 Nginx 配置中使用环境变量,具体方法可参考此文。

这种方式能够将应用程序的配置文件封装在容器内部。

8、外部化数据

关于数据存储有一条黄金法则:绝对不要将任何持久化数据保存到容器内。

容器的文件系统自己是被设计成临时和短暂的。所以任何由应用程序生成的内容、数据文件和处理结果都应该保存到挂载的卷或者操做系统绑定挂载点上(既:将宿主机操做系统的目录挂载到容器中)。

若是将数据保存到绑定挂载点,对于要绑定到容器的宿主机上的目录,你须要注意如下几点:

  • 在宿主机操做系统上建立非特权用户和组。
  • 全部须要绑定目录的全部者都是该用户。
  • 根据使用场景给受权(仅针对这个特定的用户和组,其余用户无权访问)。
  • 容器也以该用户运行。
  • 容器能够彻底控制这些目录。

9、确保处理好日志

若是这是一个新的应用程序,而且但愿它可以坚持 Docker 约定,就不该该将日志写入任何文件。应用程序应该使用标准输出和标准错误输出日志,这和以前推荐使用环境变量传递参数同样,这也是 12-factors 之一,具体能够参见这里。

Docker 会自动捕捉应用程序的标准输出,并能够经过 docker logs 命令查看。有关于 docker logs 的具体使用你能够参考这里。

可是在一些实际场景下你可能会遇到问题,例如:运行一个简单的 Nginx 容器,至少会有两种不一样的日志文件:

  • HTTP 访问日志(Access Logs)
  • 错误日志(Error Logs)

对于这种按照特定结构输出日志的应用,就不太适合将它们的日志输出到标准输出。这种状况下,你须要按持久化的方式处理这些日志,并确保这些日志文件的能正常的轮转。

10、轮转日志

若是应用程序将日志写到文件,或者会无限追加内容到文件,就须要关注这些文件的轮转(rotation),这对于防止服务器空间耗尽很是有用的。

若是使用绑定挂载,咱们能够依靠宿主机的一些工具来实现文件轮转功能。例如:logrotate,关于 logrotate 的使用你能够参考示例1、示例二。

在此我向你们推荐一个Java高级群 :725633148 里面会分享一些资深架构师录制的视频录像:(有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构、面试资料)等这些成为架构师必备的知识体系 进群立刻免费领取,目前受益良多!

相关文章
相关标签/搜索