• 起源
  • 守护进程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. 解决CentOS下可以ping通ip ping不通域名

    现象:1. ping不通域名,比如 www.qq.com 2. 可以ping通ip,比如 61.135.157.156 分析:1. 查看DNS配置文件 /etc/resolve.conf, 里面的服务 ...

  2. 生鲜水果商城PC手机微信完整版源码2018版(免费)

    采用php+mysql架构,含有PC.手机.微信三端,只需要修改一下数据库配置,并恢复一下数据即可使用,还有微信.支付宝等接口,如有问题请在文章下面留言一下,我看到会协助一下的,下载包里面含有详细的安 ...

  3. Python拼接字符串的7种方法

    1.直接通过+操作: s = 'Python'+','+'你好'+'!'print(s) 打印结果: Python,你好! 2.通过join()方法拼接: 将列表转换成字符串 strlist=['Py ...

  4. Bootstrap栅格系统基本使用

    1.什么是栅格系统: 在Bootstrap中,它提供了一套响应式.移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列.栅格系统用于通过一系列的行(row ...

  5. 213. String Compression【LintCode java】

    Description Implement a method to perform basic string compression using the counts of repeated char ...

  6. 【转】: 探索Lua5.2内部实现:虚拟机指令(1) 概述

    Lua一直把虚拟机执行代码的效率作为一个非常重要的设计目标.而采用什么样的指令系统的对于虚拟机的执行效率来说至关重要. Stack based vs Register based VM 根据指令获取操 ...

  7. DeepLearning - Forard & Backward Propogation

    In the previous post I go through basic 1-layer Neural Network with sigmoid activation function, inc ...

  8. 浅谈PCA

    最近在回顾PCA方面的知识,发现对于之前的很多东西有了新的理解,下面和大家分享下我的一些个人的理解 1.我们为什么要用PCA,它能解决我什么问题? PCA(Principal Component An ...

  9. asp.net mvc5 模式的现象思考

    .net mv5简化了一些应用逻辑,与其说是mvc架构模式,不如说应用.net Entity更好. 现在你只需要去随便创建一个类 相关数据 然后用一个类去继承 DbContext 定义一个 DbSet ...

  10. C语言链接数据库

    一.解释一下函数功能和用法 1.mysql_real_connect 函数原型:MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, co ...