• 起源
  • 守护进程daemon
  • 从守护进程的角度看Docker Compose
  • Docker的解决方案
  • 思路
  • 代码
  • 结果

起源

Docker Compose提供了一个depends_on参数。

https://docs.docker.com/compose/compose-file/#depends_on

depends_on参数用于描述服务之间的依赖关系,服务依赖将导致如下行为:

  • docker-compose up按照依赖关系的顺序启动服务。
  • docker-compose up SERVICE自动包含SERVICE的依赖关系。

看起来功能强大,不过接下来又说了使用depends_on的注意事项:

  • depends_on在启动服务时并不会等待相关所依赖的服务完成。

也就是说,depends_on的主要功能是自动包含SERVICE的依赖关系, 
而所谓的按照依赖关系的顺序启动服务在实践中几乎毫无意义, 
因为很多被依赖的服务往往启动缓慢,例如数据库。

分析Docker的工作原理会发现, 
管理依赖关系的挑战在于Docker本身并不管理服务的启动的过程, 
一个Docker服务在启动entrypoint或者command进程的时候就开始了, 
一直到这个进程退出后才能被Docker识别服务结束。

Docker服务的生命周期不同于使用服务角度的声明周期, 
从使用服务的角度看,有这么几个关键点

  1. 服务的主进程启动
  2. 服务开始向外提供服务
  3. 服务向外提供服务
  4. 服务终止向外提供服务
  5. 服务的主进程结束
服务的主进程启动服务开始向外提供服务服务向外提供服务服务终止向外提供服务服务的主进程结束
从使用服务的角度看Docker服务的生命周期服务服务客户端客户端主进程启动服务开始向外提供服务请求响应服务终止向外提供服务主进程结束

然后结合Docker服务的生命周期的关键点一起看

  1. entrypoint或者command进程启动
  2. 若干启动辅助进程启动到结束
  3. 服务的主进程启动
  4. 服务开始向外提供服务
  5. 服务向外提供服务
  6. 服务终止向外提供服务
  7. 服务的主进程结束
  8. 若干结束辅助进程启动到结束
  9. entrypoint或者command进程结束
entrypoint或者command进程启动若干启动辅助进程启动到结束服务的主进程启动服务开始向外提供服务服务向外提供服务服务终止向外提供服务服务终止向外提供服务若干结束辅助进程启动到结束entrypoint或者command进程结束
结合Docker的生命周期看服务的生命周期Docker容器Docker容器启动辅助进程启动辅助进程结束辅助进程结束辅助进程服务主进程服务主进程服务工作进程服务工作进程客户端客户端管理端管理端启动启动结束启动启动服务开始向外提供服务请求响应关闭关闭服务终止向外提供服务结束结束启动结束结束

Docker可以识别entrypoint或者command进程的启动和结束, 
但是里面的细节就很难识别了。

守护进程daemon

在启动服务的过程中还有一个容易混淆的地方————守护进程daemon, 
Linux中的服务往往以守护进程daemon的形式出现。

所有守护进程的启动脚本,都放在/etc/init.d目录下面。 
init进程的主要任务,就是逐一运行这些脚本。

Linux中一个守护进程的父进程是init进程, 
因为守护进程真正的父进程在fork出子进程后就先于子进程exit退出了, 
所以守护进程是一个由init继承的孤儿进程。

这种工作方式有两个基本的用途。

  • 用于启动的父进程的运行权限较高, 
    fork子进程的同时可以设置子进程运行于不同的环境,例如不同的用户。
  • 用于启动的父进程在init中是串行的,而且可以依循一定的顺序, 
    fork具体提供服务的子进程后,父进程退出, 
    init运行下一个守护进程的启动的父进程, 
    下一个守护进程的启动的父进程就可以使用前面的守护进程的子进程提供的服务了。 
    这是和本文所讨论的相同的场景。

免责声明:本段并非专门的Linux技术分析,仅用于介绍相关知识。

从守护进程的角度看Docker Compose

既然Docker Compose中按照顺序启动服务的情况和守护进程类似, 
那么能不能使用相同的机制呢?

从技术上没问题,如果所有的服务都采用和守护进程类似的机制,例如fork出子进程, 
或者使用&运行后台进程。总之,只要保证主进程退出, 
就可以被Docker Compose识别了。

这种方式看上去很美,不过就严格限制了服务的灵活性。 
猜测,仅仅是猜测,Docker没有找到尽善尽美的解决方案,所以没有提供这个功能。

Docker的解决方案

Docker没有提供直接的解决方案,不过也并且给出了一篇参考文章:

https://docs.docker.com/compose/startup-order/

这篇文章很神奇,神奇之处不是提出了什么奇思妙想, 
神奇之处在于点踩的人数远远多于点赞的人数, 
今天(2018年4月3日)的数据是172踩53赞。竟然达到了3倍之多! 
这种情况在官方文档中是非常罕见的。

不过我也要赞一下,不是赞这篇文章, 
而是为Docker公司把这么一个数字暴露出来的勇气点赞。

文章内容就不讨论了,思路类似。

思路

既然官方给出的思路是服务之间检测,那么检测端口是一个方法,而且是非侵入式的。 
不过当另一个服务并没有提供端口,例如仅仅是若干操作环节中的一个步骤, 
就不能检查端口了,此时就需要借助其他的方式。 
前置的服务设置一个标志,后续的服务检查这个标志。

从这个角度思考,借助文件系统检查时间戳可能是最简单的方法之一。

  • 借助volume在不同的服务之间共享卷
  • 前置服务完成启动的时候touch一个文件作为标记
  • 后置服务不停的检查这个文件

具体涉及到两个技术细节。

  • 由于服务会重用之前的卷,因此不能仅检查文件是否存在, 
    还需要比对文件的时间戳是否晚于当前服务的启动时间
  • 当前服务的启动时间不能使用uptime,这个是主机的启动时间, 
    而是比对 /proc/1/cmdline 这个文件的时间戳

代码

https://github.com/huzhenghui/Docker-Compose-Startup-Order

version: "3"
services:
  service_1:
    image: ubuntu
    volumes:
      - Docker-Compose-Startup-Order:/Startup-Order
    command: |
      bash -c '
      echo Service 1 Start;
      sleep 10;
      echo Service 1 Up;
      touch /Startup-Order/service_1;'
  service_2:
    image: ubuntu
    volumes:
      - Docker-Compose-Startup-Order:/Startup-Order
    command: |
      bash -c '
      while [[ ! -f /Startup-Order/service_1 || /Startup-Order/service_1 -ot /proc/1/cmdline ]]; do sleep 1; done;
      echo Service 2 Start;
      sleep 10;
      echo Service 2 Up;
      touch /Startup-Order/service_2;'
  service_3:
    image: ubuntu
    volumes:
      - Docker-Compose-Startup-Order:/Startup-Order
    command: |
      bash -c '
      while [[ ! -f /Startup-Order/service_2 || /Startup-Order/service_2 -ot /proc/1/cmdline ]]; do sleep 1; done;
      echo Service 3 Start;
      sleep 10;
      echo Service 3 Up;'
volumes:
  Docker-Compose-Startup-Order:

结果

PS C:\Users\huzh\OneDrive\Docker\Docker-Compose-Startup-Order> docker-compose up
Starting dockercomposestartuporder_service_1_1 ...
Starting dockercomposestartuporder_service_3_1 ...
Starting dockercomposestartuporder_service_3_www.feihuanyule.com1 ... done
Attaching to dockercomposestartuporder_service_1_1, dockercomposestartuporder_service_2_1, dockercomposestartuporder_service_3_1
service_1_1  | Service 1 Start
service_1_1  | Service 1 Up
dockercomposestartuporder_service_1_1 www.feihuanyule.com exited with code 0
service_2_1  | Service 2 Start
service_2_1  | Service 2 Up
dockercomposestartuporder_service_2_1 www.feihuanyule.com exited with code 0
service_3_1  | Service 3 Start
service_3_1  | Service 3 Up
dockercomposestartuporder_service_www.gouyiflb.cn 3_1 exited with code 0
PS C:\Users\huzh\OneDrive\Docker\Docker-Compose-Startup-Order>

控制Docker Compose的启动顺序的一个思路的更多相关文章

  1. docker compose 服务启动顺序控制

    概要 docker-compose 可以方便组合多个 docker 容器服务, 但是, 当容器服务之间存在依赖关系时, docker-compose 并不能保证服务的启动顺序. docker-comp ...

  2. asp.net core容器&mysql容器network互联 & docker compose方式编排启动多个容器

    文章简介 asp.net core webapi容器与Mysql容器互联(network方式) docker compose方式编排启动多个容器 asp.net core webapi容器与Mysql ...

  3. Docker Compose 启动mysql,redis,rabbitmq

    这里使用的centos7,首先切换到root. sudo -s 首先去设置下载镜像,否则下载这三个东西要很久,而且可能失败. vim /etc/docker/daemon.json 内容如下: { & ...

  4. Docker学习笔记之常用的 Docker Compose 配置项

    0x00 概述 与 Dockerfile 一样,编写 Docker Compose 的配置文件是掌握和使用好 Docker Compose 的前提.编写 Docker Compose 配置文件,其本质 ...

  5. docker-compose下的java应用启动顺序两部曲之二:实战

    上篇回顾 本文是<docker-compose下的java应用启动顺序两部曲>的终篇,在上一篇<docker-compose下的java应用启动顺序两部曲之一:问题分析>中,我 ...

  6. 5种常见的Docker Compose错误

    在构建一个容器化应用程序时,开发人员需要一种方法来引导他们正在使用的容器去测试其代码.虽然有几种方法可以做到这一点,但 Docker Compose 是最流行的选择之一.它让你可以轻松指定开发期间要引 ...

  7. Docker学习笔记之使用 Docker Compose 管理容器

    0x00 概述 通过之前的介绍,我们已经基本掌握了构建.运行容器的方法,但这还远远不够,由于 Docker 采用轻量级容器的设计,每个容器一般只运行一个软件,而目前绝大多数应用系统都绝不是一个软件所能 ...

  8. docker-compose下的java应用启动顺序两部曲之一:问题分析

    在docker-compose编排多个容器时,需要按实际情况控制各容器的启动顺序,本文是<docker-compose下的java应用启动顺序两部曲>的第一篇,文中会分析启动顺序的重要性, ...

  9. docker和docker compose安装使用、入门进阶案例

    一.前言 现在可谓是容器化的时代,云原生的袭来,导致go的崛起,作为一名java开发,现在慌得一批.作为知识储备,小编也是一直学关于docker的东西,还有一些持续继承jenkins. 提到docke ...

随机推荐

  1. SpringBoot入门(五)——自定义配置

    本文来自网易云社区 大部分比萨店也提供某种形式的自动配置.你可以点荤比萨.素比萨.香辣意大利比萨,或者是自动配置比萨中的极品--至尊比萨.在下单时,你并没有指定具体的辅料,你所点的比萨种类决定了所用的 ...

  2. Python 通过sgmllib模块解析HTML

    """ 对html文本的解析方案-示例:在标签开始的时候检查标签中的attrs属性,解析出所有的参数的href属性值 依赖安装:pip install sgmllib3k ...

  3. Qt-QML-Slider-滑块-Style

    感觉滑块这个东西,可以算是一个基本模块了,在我的项目中也有这个模块,今天我将学一下一下滑块的使用以及美化工作. 想学习滑块,那就要先建立一个滑块,新建工程什么的这里就省略了,不会的可以看我前面的几篇文 ...

  4. python操作字符串内容并重新输出

    今天在做一个函数的作业,题目如下: 编写一个函数实现大写转小写,小写变大写,并且转换为镜像字符串,并且将字符串变为镜像字符串. 例如:’A’变为’Z’,’b’变为’y 示范字符串: ”sdSdsfdA ...

  5. Objective-C NSString基本使用 类方法 self关键字

    NSString基本使用 #import <Foundation/Foundation.h> int main() { //最简单的创建字符串的方式 NSString *str = @&q ...

  6. ObjectMapper的使用

    Jackson ObjectMapper类 ObjectMapper类是Jackson库的主要类它提供一些功能将Java对象转换成JSON结构,反之亦然它使用JsonParser和JsonGenera ...

  7. 标注点(Labeled Point)

    标注点LabeledPoint是一种带有标签(Label/Response)的本地向量,它可以是稠密或者是稀疏的.在MLlib中,标注点在监督学习算法中被使用.由于标签是用双精度浮点型来存储的,故标注 ...

  8. 洛谷 P1781 宇宙总统:sort(string)

    题目描述 地球历公元6036年,全宇宙准备竞选一个最贤能的人当总统,共有n个非凡拔尖的人竞选总统,现在票数已经统计完毕,请你算出谁能够当上总统. 输入输出格式 输入格式: 第一行为一个整数n,代表竞选 ...

  9. 吴恩达深度学习 反向传播(Back Propagation)公式推导技巧

    由于之前看的深度学习的知识都比较零散,补一下吴老师的课程希望能对这块有一个比较完整的认识.课程分为5个部分(粗体部分为已经看过的): 神经网络和深度学习 改善深层神经网络:超参数调试.正则化以及优化 ...

  10. py3.6+anaconda下安装opencv3

    py3.6+anaconda下安装opencv3 首先声明-网上的方法大多数都是有毒的.也不知道给的什么鬼方法都不行. 我说下我的方法.去这个网站https://pypi.tuna.tsinghua. ...