Discussion on the startup sequence of Docker Compose - springboot actual e-commerce project mall4j

Posted by JeDi58 on Mon, 03 Jan 2022 19:52:47 +0100

springboot e-commerce project mall4j( https://gitee.com/gz-yami/mall4j)

java mall system source code

The following discussion is based on the V2 version of Docker Compose:

Now container choreography is widely used. We are using docker Compose for website deployment. In the process of deployment, Mysql, Redis, JAVA back-end programs and PHP back-end programs will be used, which will involve the start sequence of a program. The general solution is to add dependencies to the Compose file_ On parameter, for example:

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
    # Depending on the database, start the database first and then start the JAVA program
    depends_on:
      - xxl-job-mysql

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

It seems that the above choreography files are OK, but when they are actually executed, after the database is started, because the dependencies_ On parameter, the JAVA program will be started immediately after starting the database. Whether Mysql has been started or not. At this time, it may appear that the JAVA program has been started before the database is started, and attempts to connect to the database, resulting in connection failure and JAVA program error. So, dependencies_ On doesn't really wait for the last program to start and complete. This is also described in the official docker document: https://docs.docker.com/compo...

There is a sentence "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."

Therefore, officials recommend some TCP detection gadgets to detect whether the program port is unblocked. Once unblocked, it means that the program has been started and completed.
Next, let's use the wait for it widget: https://github.com/vishnubob/... To detect whether the database has been started and completed
The dockerfile content of the java program of XXL job admin is as follows:

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
    # Depending on the database, start the database first and then start the JAVA program
    depends_on:
      - xxl-job-mysql
    volumes:
      # Mount the wait for it script to the root directory in the container,
      # Because you can see the app. XML by checking the dockerfile of XXL job admin Jar is also in the root directory, so it is placed in the same directory
      - ./wait-for-it.sh:/wait-for-it.sh
    # Using the command, first execute wait for it, wait for the database to start, and then execute Java - jar app jar
    command: ["/wait-for-it.sh", "xxl-job-mysql:3306", "--", "sh", "-c", "java -jar $JAVA_OPTS /app.jar $PARAMS"]

The complete code is as follows:

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
    # Depending on the database, start the database first and then start the JAVA program
    depends_on:
      - xxl-job-mysql
    volumes:
      # Mount the wait for it script to the root directory in the container,
      # Because you can see the app. XML by checking the dockerfile of XXL job admin Jar is also in the root directory, so it is placed in the same directory
      - ./wait-for-it.sh:/wait-for-it.sh
    # Using the command, first execute wait for it, wait for the database to start, and then execute 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

In addition to the official recommended TCP detection gadget, the official also provides a method, healthcheck, health detection and document connection: https://docs.docker.com/engin...

According to the meaning of the document, we add the healthcheck parameter. The code is as follows:
xxl-job-admin:

    # Add the healthcheck parameter: execute the command 'mysqladmin ping -h localhost'
    # The execution time is set to 10 seconds, the timeout is often set to 20 seconds, and the number of retries is 10. If you can ping, the database startup is completed
    healthcheck:
        test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
        interval: 10s
        timeout: 20s
        retries: 10

xxl-job-mysql:

    depends_on:
      xxl-job-mysql:
        # The java container will not start until the mysql container status is healthy
        condition: service_healthy

The complete code is as follows:

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
    # Add the healthcheck parameter: execute the command 'mysqladmin ping -h localhost'
    # The execution time is set to 10 seconds, the timeout is often set to 20 seconds, and the number of retries is 10. If you can ping, the database startup is completed
    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:
        # The java container will not start until the mysql container is in a healthy state. Note: Condition: service_ Health, obsolete in V3 version of Compose  
        condition: service_healthy

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

So, has the problem been solved so far? However, only part of the problem is solved, because there is a command in the Mysql startup script, which will detect / docker entrypoint initdb D whether there is an initialization sql script in the folder. If so, the sql will be executed at the first startup to initialize the database. The link is as follows: https://github.com/docker-lib... If the initialization script is large, there will be a situation. The 3306 port of mysql can be pinged, but the sql is still executing. The app container thinks it has been started and connected to the database, so, We need to improve the detection method: replace mysqladmin ping -h localhost with / usr/bin/mysql --user=root --password=123456 --execute "SHOW DATABASE;"
The code is as follows:

  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
    # Add healthcheck parameter: execute '/ usr/bin/mysql --user=root --password=123456 --execute "SHOW DATABASES;" command
    # The execution time is set to 10 seconds, the timeout is often set to 20 seconds, and the number of retries is 10. If the database can log in and query, the database startup is completed
    healthcheck:
        # Only when the query command can be executed can the startup be completed
        test: '/usr/bin/mysql --user=root --password=123456 --execute "SHOW DATABASES;"'
        interval: 10s
        timeout: 20s
        retries: 10

Summary:
1. Only dependencies exist_ On must be unreliable
2. When the initialization script is large, ping ing does not necessarily mean that the database initialization is completed
3. The real startup is completed when the database can be logged in and queried

springboot e-commerce project mall4j( https://gitee.com/gz-yami/mall4j)

java mall system source code

Topics: Docker docker compose