Docker实践(11) – 将系统拆分为微服务容器

我们已经探讨了如何作为一个整体使用容器(像一个经典的服务器),并解释它可以是一个快速移动系统架构到Docker的好方法。不过在Docker世界中,通常认为最好的做法是尽可能多地分割系统,直到每个容器只运行一个服务,然后通过links连接所有容器。因为这是推荐的Docker方法,你会发现Docker Hub中的大多数容器都是这种方法,理解如何以这种方式构建镜像对于与Docker生态系统进行交互非常重要。
一个容器一个服务的主要原因是通过单一责任原则更容易分离关注点。
如果一个容器只做一项工作,那么将更容易地把容器放到开发,测试和生产的软件开发生命周期中,而不用担心它与其它组件的交互。这使得交付更灵活和软件项目更可扩展。不过它增加了维护开销,所以最好考虑在你的用例中是否值得这样做。

问题

你希望将你的应用程序分解成更易于管理的服务

解决方法

使用Docker将你的应用程序集分解为基于容器的服务

讨论

在Docker社区中,关于如何严格地遵循“一容器一服务”规则的一些争论,其中一部分源于对定义的不同意见 – 是一个单独的进程,还是根据需要把一组进程放到一起完成一项服务?它往往归结为一个声明,给予从头重新设计系统的能力,微服务是最可能的选择。但有时候,实用性与理想主义相克 – 当我们为我们的组织评估Docker时,我们发现自己处于必须走整条路线的位置,以便让Docker尽可能快速和轻松地工作。 让我们来看看在Docker中跑多个进程的一个缺点。首先我们需要展示如何创建一个带有数据库,应用和web服务器的容器。

这些示例是为了阐明目的,所以简化了。尝试直接运行它们不一定能用。

设置简单的PostgreSQL,NodeJS和Nginx应用:

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install postgresql nodejs npm nginx
  3. WORKDIR /opt
  4. COPY . /opt/                             # {*}
  5. RUN service postgresql start && \
  6.     cat db/schema.sql | psql && \
  7.     service postgresql stop
  8. RUN cd app && npm install
  9. RUN cp conf/mysite /etc/nginx/sites-available/ && \
  10.     cd /etc/nginx/sites-enabled && \
  11.     ln -s ../sites-available/mysite

在RUN语句中使用&&有效地确保了几个命令作为一个命令运行。这对于减小你的镜像大小会有用。每个Dockerfile命令都会在上一个层之上创建一个新层。如果你以这种方式 运行软件包更新命令(如apt-get update)和install命令,你可以确保无论何时安装软件包,它们都将来自更新的软件包缓存。

前面的示例是一个概念上简单的Dockerfile,它在容器中安装我们需要的一切,然后设置数据库,应用程序和Web服务器。不过在你想快速重载容器时会有一个问题 – 对存储库中任何文件的任何更改都将从{*}处开始重建所有内容,因为缓存无法重复使用。如果你的一些步骤需要时间比较久(如数据库创建或npm安装),你可能需要一段时间等待容器重建。
解决方案是拆分COPY . /opt指令为三部分,对应数据库,应用程序和Web设置。

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install postgresql nodejs npm nginx
  3. WORKDIR /opt
  4. COPY db /opt/db                                     -+
  5. RUN service postgresql start && \                   |- db setup
  6.     cat db/schema.sql | psql && \ |
  7.     service postgresql stop                         -+
  8. COPY app /opt/app                                   -+
  9. RUN cd app && npm install                            |- app setup
  10. RUN cd app && ./minify_static.sh                    -+
  11. COPY conf /opt/conf                                 -+
  12. RUN cp conf/mysite /etc/nginx/sites-available/ && \  +
  13.     cd /etc/nginx/sites-enabled && \                 |- web setup
  14.     ln -s ../sites-available/mysite                 -+

在上面的代码中,COPY命令分成三个单独的指令。这意味着数据库将不会在每次代码更改时重建,因为在COPY app /opt/app之前缓存可以重用。
不过由于缓存功能相当简单,容器仍然必须在每次对schema.sql进行更改时完全重建 – 解决这个问题的唯一方法是按顺序为三个应用程序分别创建Dockerfile,如下:
Database Dockerfile

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install postgresql
  3. WORKDIR /opt
  4. COPY db /opt/db
  5. RUN service postgresql start && \
  6.     cat db/schema.sql | psql && \
  7.     service postgresql stop

App Dockerfile

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install nodejs npm
  3. WORKDIR /opt
  4. COPY app /opt/app
  5. RUN cd app && npm install
  6. RUN cd app && ./minify_static.sh

Web server Dockerfile

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install nginx
  3. WORKDIR /opt
  4. COPY conf /opt/conf
  5. RUN cp conf/mysite /etc/nginx/sites-available/ && \
  6.     cd /etc/nginx/sites-enabled && \
  7.     ln -s ../sites-available/mysite

不管何时db,app或conf目录中的哪一个更改,仅仅有一个容器需要重建。当你有三个以上的容器或者需要大量时间的设置步骤时这种方法非常有用 – 你可以在每步中添加最少必要的文件以便获得更有用的Dockerfile缓存。在app Dockerfile中的npm install的操作中,只依赖了package.json文件,所以我们可以更改这个Dockerfile以充分利用dockerfile层缓存。

  1. FROM ubuntu:14.04
  2. RUN apt-get update && apt-get install nodejs npm
  3. WORKDIR /opt
  4. COPY app/package.json /opt/app/package.json
  5. RUN cd app && npm install
  6. COPY app /opt/app
  7. RUN cd app && ./minify_static.sh

不过事情没有那么简单,从单个dockerfile文件分割为多个dockerfile,增加了不少重复代码。你可以通过添加另一个dockerfile作为基础镜像来部分解决这个问题。此外,启动你的镜像还有一些复杂的问题 – 除了EXPOSE步骤使适当的端口可用于链接和更改Postgres配置之外,你还需要确保在每次启动时链接容器。 幸运的是,有一个叫做docker-compose(以前的fig)的工具来帮我们完成这件事。

标签:容器Docker 发布于:2019-11-20 21:52:07