乐趣区

关于docker:关于Docker-Compose的启动顺序的讨论-springboot实战电商项目mall4j

springboot 实战电商我的项目 mall4j(https://gitee.com/gz-yami/mall4j)

java 商城零碎源码

以下探讨基于 Docker Compose 的 V2 版本:

当初容器编排应用宽泛,大家都在用 docker-compose 进行网站部署,在部署的过程中,会应用到 Mysql,Redis,JAVA 后端程序,PHP 后端程序之类的,会波及到一个程序的启动先后问题,个别的解决方案都是在 Compose 文件中,增加 depends_on 参数,例如:

version: "2.4"
services:
  xxl-job-mysql:
    container_name: xxl-job-mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    image: mysql:5.7
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.2
    ports:
      - 3306:3306
    restart: always
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/initdb:/docker-entrypoint-initdb.d

  xxl-job-admin:
    container_name: xxl-job-admin
    environment:
      - PARAMS=--spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
        --spring.datasource.username=root \
        --spring.datasource.password=123456 \
        --xxl.job.accessToken=
    image: xuxueli/xxl-job-admin:2.3.0
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.3
    ports:
      - 8080:8080
    restart: always
    # 依赖于数据库,先启动数据库再启动 JAVA 程序
    depends_on:
      - xxl-job-mysql

networks:
  xxl-job-network:
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.189.0/24
    name: xxl-job-network

以上编排文件如同看起来没什么问题,但理论执行的时候,在数据库启动后,因为 depends_on 参数,会在启动数据库后马上启动 JAVA 程序。不论 Mysql 是否曾经启动实现。这时候就可能呈现数据库还没启动好,JAVA 程序就曾经启动实现,并尝试去连贯数据库,造成连贯失败,JAVA 程序报错。所以说,depends_on 并没有真正的期待上一个程序启动实现。这个在 docker 官网文档也有阐明:https://docs.docker.com/compo…

外面有一句 ”However, for startup Compose does not wait until a container is“ready”(whatever that means for your particular application) – only until it’s running. There’s a good reason for this.”

所以,官网举荐了一些 TCP 检测的小工具,用于检测程序端口是否畅通,一旦畅通代表程序曾经启动实现。
上面,咱们来应用 wait-for-it 小工具:https://github.com/vishnubob/… 来检测数据库是否曾经启动实现
xxl-job-admin 的 java 程序的 dockerfile 内容如下:

FROM openjdk:8-jre-slim
MAINTAINER xuxueli

ENV PARAMS=""

ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

ADD target/xxl-job-admin-*.jar /app.jar

ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar $PARAMS"]
  xxl-job-admin:
    container_name: xxl-job-admin
    environment:
      - PARAMS=--spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
        --spring.datasource.username=root \
        --spring.datasource.password=123456 \
        --xxl.job.accessToken=
    image: xuxueli/xxl-job-admin:2.3.0
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.3
    ports:
      - 8080:8080
    restart: always
    # 依赖于数据库,先启动数据库再启动 JAVA 程序
    depends_on:
      - xxl-job-mysql
    volumes:
      # 把 wait-for-it 脚本挂载到容器内根目录,# 因为查看 xxl-job-admin 的 dockerfile 得悉 app.jar 也在根目录,所以放在同一个目录下
      - ./wait-for-it.sh:/wait-for-it.sh
    # 应用 command 命令,先执行 wait-for-it 期待数据库启动实现,而后执行 java -jar app.jar
    command: ["/wait-for-it.sh", "xxl-job-mysql:3306", "--", "sh", "-c", "java -jar $JAVA_OPTS /app.jar $PARAMS"]

残缺代码如下:

version: "2.4"
services:
  xxl-job-mysql:
    container_name: xxl-job-mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    image: mysql:5.7
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.2
    ports:
      - 3306:3306
    restart: always
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/initdb:/docker-entrypoint-initdb.d

  xxl-job-admin:
    container_name: xxl-job-admin
    environment:
      - PARAMS=--spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
        --spring.datasource.username=root \
        --spring.datasource.password=123456 \
        --xxl.job.accessToken=
    image: xuxueli/xxl-job-admin:2.3.0
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.3
    ports:
      - 8080:8080
    restart: always
    # 依赖于数据库,先启动数据库再启动 JAVA 程序
    depends_on:
      - xxl-job-mysql
    volumes:
      # 把 wait-for-it 脚本挂载到容器内根目录,# 因为查看 xxl-job-admin 的 dockerfile 得悉 app.jar 也在根目录,所以放在同一个目录下
      - ./wait-for-it.sh:/wait-for-it.sh
    # 应用 command 命令,先执行 wait-for-it 期待数据库启动实现,而后执行 java -jar app.jar
    command: ["/wait-for-it.sh", "xxl-job-mysql:3306", "--", "sh", "-c", "java -jar $JAVA_OPTS /app.jar $PARAMS"]

networks:
  xxl-job-network:
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.189.0/24
    name: xxl-job-network

除了官网举荐的 TCP 检测的小工具以外,官网还提供了一个办法,healthcheck,衰弱检测,文档连贯:https://docs.docker.com/engin…

依据文档意思,咱们增加 healthcheck 参数,代码如下:
xxl-job-admin:

    # 减少 healthcheck 参数:执行 'mysqladmin ping -h localhost' 命令
    # 执行工夫设定为 10 秒,超时时常设定为 20 秒,重试次数为 10 次,如果能 ping 通,阐明数据库启动实现
    healthcheck:
        test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
        interval: 10s
        timeout: 20s
        retries: 10

xxl-job-mysql:

    depends_on:
      xxl-job-mysql:
        # java 容器在 mysql 容器状态为衰弱前不会启动
        condition: service_healthy

残缺代码如下:

version: "2.4"
services:
  xxl-job-mysql:
    container_name: xxl-job-mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    image: mysql:5.7
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.2
    ports:
      - 3306:3306
    restart: always
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/initdb:/docker-entrypoint-initdb.d
    # 减少 healthcheck 参数:执行 'mysqladmin ping -h localhost' 命令
    # 执行工夫设定为 10 秒,超时时常设定为 20 秒,重试次数为 10 次,如果能 ping 通,阐明数据库启动实现
    healthcheck:
        test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
        interval: 10s
        timeout: 20s
        retries: 10

  xxl-job-admin:
    container_name: xxl-job-admin
    environment:
      - PARAMS=--spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
        --spring.datasource.username=root \
        --spring.datasource.password=123456 \
        --xxl.job.accessToken=
    image: xuxueli/xxl-job-admin:2.3.0
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.3
    ports:
      - 8080:8080
    restart: always
    depends_on:
      xxl-job-mysql:
        # java 容器在 mysql 容器状态为衰弱前不会启动,须要留神的是:ondition: service_healthy,在 V3 版本的 Compose 中已废除  
        condition: service_healthy

networks:
  xxl-job-network:
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.189.0/24
    name: xxl-job-network

那么,到此为止,问题是否曾经解决了呢?然而,只是解决了一部分,因为在 Mysql 的启动脚本中,有一段命令,启动时会检测 /docker-entrypoint-initdb.d 文件夹中是否存在初始化 sql 脚本,如果存在,就会在第一次启动时执行 sql,对数据库进行初始化,链接如下:https://github.com/docker-lib… 如果初始化脚本很大,就会呈现一种状况,mysql 的 3306 端口曾经能够 ping 通,但 sql 还在执行中,app 容器就认为他曾经启动实现,从而进行数据库连贯,所以,咱们须要针对检测形式进行改良一下:将 mysqladmin ping -h localhost 替换成 /usr/bin/mysql –user=root –password=123456 –execute “SHOW DATABASE;”
代码如下:

  xxl-job-mysql:
    container_name: xxl-job-mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    image: mysql:5.7
    networks:
      xxl-job-network:
        ipv4_address: 192.168.189.2
    ports:
      - 3306:3306
    restart: always
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/initdb:/docker-entrypoint-initdb.d
    # 减少 healthcheck 参数:执行 '/usr/bin/mysql --user=root --password=123456 --execute"SHOW DATABASES;"' 命令
    # 执行工夫设定为 10 秒,超时时常设定为 20 秒,重试次数为 10 次,如果数据库能登陆并且能够查问时,阐明数据库启动实现
    healthcheck:
        # 只有真正能执行查问命令,才算启动实现
        test: '/usr/bin/mysql --user=root --password=123456 --execute"SHOW DATABASES;"'
        interval: 10s
        timeout: 20s
        retries: 10

总结:
1,只存在 depends_on 时肯定是不牢靠的
2,当初始化脚本比拟大时,能 ping 通不肯定代表数据库初始化实现
3,当数据库能登陆并且能够查问时,才算真正的启动实现

springboot 实战电商我的项目 mall4j(https://gitee.com/gz-yami/mall4j)

java 商城零碎源码

退出移动版