1. 程式人生 > >微服務架構 - 解決Docker-Compose服務編排啟動順序問題

微服務架構 - 解決Docker-Compose服務編排啟動順序問題

核心 info 是否 鏡像 try ports ddr links 服務編排

基於Docker Compose進行服務編排時,一定碰到服務啟動順序的問題,例如:B服務啟動之前,A服務要已經啟動並且可以正常對外服務。

這個啟動順序的問題,Docker Compose本身它是無法解決的,即使定義了depends_on或者links,它只能保證該服務依賴這些服務,啟動本服務時會將依賴的服務也啟動,但是啟動順序無法得到保證。

目前本人實驗比較好的方案有兩種:

  • 基於wait-for-it.sh實現,前提條件是本鏡像要支持bash
  • 對於自己構建的鏡像時,讓工程本身帶一個監聽類,用於監聽依賴服務是否啟動,這種方式有侵入性,同時對於第3方的鏡像,不太好實現

1、wait-for-it.sh方案

wait-for-it.sh是GitHub中開源一個腳本,很輕量也很實用,以一個例子說明其的法:

本例子中定義了2個服務,一個mysql服務,一個cs2_serv服務,這個cs2_serv需要等mysql啟動好並做好初始化後才能啟動,要不然cs2_serv服務會由於沒法連接到數據庫而報錯。

version: "3"
services:
  mysql:
    image: mysql:5.6
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=jgyw@123
      - MYSQL_USER=cs2
      - MYSQL_PASS=cs2123
    volumes:
      - ./db/mysql:/var/lib/mysql
      - ./db/init:/docker-entrypoint-initdb.d/

  cs2_serv:
    image: cs2_serv:v1
    ports:
      - "81:81"
    environment:
      - SERV_PORT=81
      - MYSQL_IP=mysql
      - MYSQL_PORT=3306
      - DB_USERNAME=root
      - DB_PASSWORD=jgyw@123
    links:
      - mysql
    volumes:
      - ./wait-for-it.sh:/wait-for-it.sh
    entrypoint: "/wait-for-it.sh -t 0 mysql:3306 -- "
    command:
      - /bin/sh
      - -c
      - |
        sleep 10
        java -Djava.security.egd=file:/dev/./urandom -jar /app.jar

此處最為核心的代碼就是:

    entrypoint: "/wait-for-it.sh -t 0 mysql:3306 -- "
    command:
      - /bin/sh
      - -c
      - |
        sleep 10
        java -Djava.security.egd=file:/dev/./urandom -jar /app.jar

這2個配置的意思是,要等到mysql:3306服務可以用了,才去執行command對應的命令。

同時我在commad命令中再增加等待10s鐘,主要為了完全確保mysql服務啟動完成,還有就是初始化數據庫也完成,最後才去啟動cs2_serv服務。

2、自定義監聽類

這種方式有一定侵入性,但是配置起來會比較方便,在此以Spring Boot為例,寫了一個簡單的監聽類,即:

package com.swnote.cs2.common.listener;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;

/**
 * 依賴服務檢查
 */
public class DependsOnServiceCheckListener implements ApplicationListener<ApplicationStartingEvent> {
    private Logger logger = Logger.getLogger(DependsOnServiceCheckListener.class);

    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        // 獲取環境變量
        Map<String, String> envs = System.getenv();
        
        // 環境變量中DEPENDS_ON值,即是依賴的服務,值的內容格式為:host1:port1,host2:port2
        if (envs.containsKey("DEPENDS_ON")) {
            // 依賴服務是否啟動的標誌
            boolean flag = false;
            
            String val = envs.get("DEPENDS_ON");
            String[] servs = val.split(",");
            
            while (!flag) {
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    logger.warn("Wait depends on Service started...");
                }
                
                for (String serv : servs) {
                    flag = checkServ(serv);
                    if (!flag) {
                        break;
                    }
                }
            }
            
            logger.info("Depends on Service started...");
        }
    }
    
    /**
     * 檢查服務是否啟動
     * 
     * @param serv
     * @return
     */
    private boolean checkServ(String serv) {
        String[] servs = serv.split(":");
        String host = servs[0].trim();
        int port = Integer.parseInt(servs[1].trim());
        
        Socket socket = null;
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress(host, port));
            logger.info(serv + ": Service started...");
            return true;
        } catch (Exception e) {
            logger.warn(serv + ": Service not started...");
            return false;
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

這個監聽類,是將依賴的服務信息放到環境變量DEPENDS_ON中,即是依賴的服務,值的內容格式為:host1:port1,host2:port2,然後每隔5s去測試依賴的服務是否是通的,如果所有依賴的服務都是通的,那麽本服務就可以啟動,否則本服務一直處於等待狀態。

以一個實例說明使用方式,即:

  cs2_web:
    image: cs2_web:v1
    ports:
      - "82:82"
    environment:
      - WEB_PORT=82
      - SERV_DOMAIN=cs2_serv
      - DEPENDS_ON=cs2_serv:81
    links:
      - cs2_serv

這裏定義了一個cs2_web服務,該服務是依賴上面例子中的cs2_serv,但是它配置依賴關系是通過環境變量DEPENDS_ON來配置的。

3、參考資料

https://github.com/vishnubob/wait-for-it

關註我

以你最方便的方式關註我:
微信公眾號:
技術分享圖片

微服務架構 - 解決Docker-Compose服務編排啟動順序問題