SpringCloudAlibaba微服務docker容器打包和部署示例實戰


概述

我們使用前面《SpringCloudAlibaba注冊中心與配置中心之利器Nacos實戰與源碼分析(中)》的兩個微服務示例,分別是庫存微服務和訂單微服務,基於Nacos注冊中心和配置中心的使用,前面Nacos我們已基於dock-compose方式部署,我們增加配置數據,這里我們暫時也不把數據打包進去,各位可以直接將容器以dokcer export方式導入為鏡像,微服務使用訂單、庫存MySQL數據庫暫時也不單獨做成鏡像,各位可以做成SQL腳本執行導入方式。

整體工程結構

  • docker目錄docker compose編排腳本目錄
    • bin目錄:包含初始化腳本、啟動腳本、停止腳本、更新腳本
    • env目錄:存在為微服務環境變量
    • yaml目錄:存在全局環境腳本變量、微服務docker-compose腳本
  • 庫存微服務
    • bin目錄:存在微服務啟動腳本
    • conf目錄:存在啟動配置文件和日志配置文件
    • Dockerfile文件
  • 訂單微服務
    • bin目錄:存在微服務啟動腳本
    • conf目錄:存在啟動配置文件和日志配置文件
    • Dockerfile文件

image-20220419112440637

庫存微服務

編寫配置文件

bootstrap.yml

spring:
  application:
    name: ecom-storage-service
  profiles:
    active: ${SPRING_PROFILES_ACTIVE:"dev"}
  main:
    allow-circular-references: true
  cloud:
    nacos:
      # 注冊中心信息放在配置中心上,每個程序一般只配置配置中心的信息
      server-addr: ${NACOS_CONFIG_SERVER:"192.168.50.95:8848"}
      config:
        server-addr: ${spring.cloud.nacos.server-addr}
        file-extension: yaml
        namespace: ${NACOS_CONFIG_NAMESPACE:"a2b1a5b7-d0bc-48e8-ab65-04695e61db01"}
        group: ${NACOS_CONFIG_GROUP:"storage-group"}
        extension-configs:
          - dataId: extension-priority-dev.yaml
            group: extension-group
            refresh: true
          - dataId: commons-dev.yaml
            group: commons-group
            refresh: true
        shared-configs:
          - dataId: shared-priority-dev.yaml
            group: shared-group
            refresh: true
        username: itsx
        password: itxs123
        enabled: true # 默認為true,設置false 來完全關閉 Spring Cloud Nacos Config
        refresh-enabled: true # 默認為true,當變更配置時,應用程序中能夠獲取到最新的值,設置false來關閉動態刷新,我們使用注冊中心場景大部分就是動態感知,因此基本使用默認的

logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="false">
    <!--定義日志文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->
    <springProperty scope="context" name="APP_HOME" source="spring.application.name"/>
    <property name="LOG_HOME" value="${LOG_PATH:-.}" />
    <!-- 控制台輸出設置 -->
    <!-- 彩色日志格式,magenta:洋紅,boldMagenta:粗紅,yan:青色,·⊱══> -->
    <property name="CONSOLE_LOG_PATTERN" value="%boldMagenta([%d{yyyy-MM-dd HH:mm:ss.SSS}]) %cyan([%X{requestId}]) %boldMagenta(%-5level) %blue(%logger{15}) %red([%thread]) %magenta(·⊱══>) %cyan(%msg%n)"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
    <!-- 按天輸出日志設置 -->
    <appender name="DAY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件輸出的文件名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}.%i.log</FileNamePattern>
            <!-- 日志文件保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>             <!-- 設置攔截的對象為INFO級別日志 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了INFO級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到INFO級別日志時,屏蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按天輸出WARN級別日志設置 -->
    <appender name="DAY_WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件輸出的文件名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}_warn.%i.log</FileNamePattern>
            <!-- 日志文件保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>             <!-- 設置攔截的對象為INFO級別日志 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了INFO級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到INFO級別日志時,屏蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按天輸出ERROR級別日志設置 -->
    <appender name="DAY_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件輸出的文件名 -->
            <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}_${APP_HOME}_error.%i.log</FileNamePattern>
            <!-- 日志文件保留天數 -->
            <MaxHistory>7</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>            <!-- 設置攔截的對象為ERROR級別日志 -->
            <onMatch>ACCEPT</onMatch>       <!-- 當遇到了ERROR級別時,啟用改段配置 -->
            <onMismatch>DENY</onMismatch>   <!-- 沒有遇到ERROR級別日志時,屏蔽改段配置 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志輸出級別,OFF level > FATAL > ERROR > WARN > INFO > DEBUG > ALL level -->
    <logger name="com.sand" level="INFO"/>
    <logger name="com.apache.ibatis" level="INFO"/>
    <logger name="java.sql.Statement" level="INFO"/>
    <logger name="java.sql.Connection" level="INFO"/>
    <logger name="java.sql.PreparedStatement" level="INFO"/>
    <logger name="org.springframework" level="WARN"/>
    <logger name="com.baomidou.mybatisplus" level="WARN"/>

    <!-- 開發環境:打印控制台和輸出到文件 -->
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DAY_FILE"/>
            <appender-ref ref="DAY_WARN_FILE"/>
            <appender-ref ref="DAY_ERROR_FILE"/>
        </root>
    </springProfile>

    <!-- 生產環境:打印控制台和輸出到文件 -->
    <springProfile name="pro">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DAY_FILE"/>
            <appender-ref ref="DAY_WARN_FILE"/>
            <appender-ref ref="DAY_ERROR_FILE"/>
        </root>
    </springProfile>
</configuration>

制作Docker啟動腳本

docker-startup.sh

#!/bin/bash
set -x
export CUSTOM_SEARCH_NAMES="application,custom"
export CUSTOM_SEARCH_LOCATIONS=${BASE_DIR}/init.d/,file:${BASE_DIR}/conf/

JAVA_OPT="${JAVA_OPT} -Dsimple_ecommerce.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/ecom-storage-service.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --spring.config.name=${CUSTOM_SEARCH_NAMES}"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/logback.xml"
JAVA_OPT="${JAVA_OPT} --logging.file.path=${BASE_DIR}/logs/"
JAVA_OPT="${JAVA_OPT} --spring.config.location=${BASE_DIR}/conf/bootstrap.yml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"

echo "ecom-storage-service is starting, you can docker logs your container"
exec $JAVA ${JAVA_OPT}

制作Dockerfile文件

Dockerfile文件

FROM java:8
MAINTAINER itxs "107734588@qq.com"

ARG ECOM_STORAGE_SERVICE_VERSION=1.0
ARG ECOM_STORAGE_SERVICE_DIR="ecom-storage-service"
ARG ECOM_STORAGE_SERVICE_PACKAGE="ecom-storage-service-$ECOM_STORAGE_SERVICE_VERSION.jar"
ARG ECOM_STORAGE_SERVICE_PROGRAM="ecom-storage-service.jar"

# set environment
ENV BASE_DIR="/home/simple_ecommerce/${ECOM_STORAGE_SERVICE_DIR}" \
    CLASSPATH=".:/home/simple_ecommerce/${ECOM_STORAGE_SERVICE_DIR}/conf:$CLASSPATH" \
    JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64" \
    JAVA="/usr/lib/jvm/java-8-openjdk-amd64/bin/java" \
    JAVA_OPT_EXT="${JAVA_OPT_EXT}" \
    TIME_ZONE="Asia/Shanghai"

WORKDIR $BASE_DIR

ADD ./target/$ECOM_STORAGE_SERVICE_PACKAGE target/$ECOM_STORAGE_SERVICE_PROGRAM
RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone

ADD bin/docker-startup.sh bin/docker-startup.sh
ADD conf/bootstrap.yml conf/bootstrap.yml
ADD conf/logback.xml conf/logback.xml
RUN mkdir -p init.d

# set startup log dir
RUN mkdir -p logs \
        && cd logs \
        && touch start.out \
        && ln -sf /dev/stdout start.out \
        && ln -sf /dev/stderr start.out
RUN chmod +x bin/docker-startup.sh

EXPOSE 4080
ENTRYPOINT ["bin/docker-startup.sh"]

打包配置

庫存微服務pom文件添加docker-maven-plugin

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>simple-ecommerce</artifactId>
        <groupId>cn.itxs</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ecom-storage-service</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>
    <name>ecom-storage-service</name>
    <description>a simple electronic commerce platform demo tutorial for storage service</description>

    <dependencies>
        <dependency>
            <groupId>cn.itxs</groupId>
            <artifactId>ecom-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定該Main Class為全局的唯一入口 -->
                    <mainClass>cn.itxs.ecom.storage.StorageServiceApplication</mainClass>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal><!--可以把依賴的包都打包到生成的Jar包中-->
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.39.1</version>
                <configuration>
                    <authConfig>
                        <!-- registry服務的認證-->
                        <username>admin</username>
                        <password>admin12345</password>
                    </authConfig>
                    <images>
                        <image>
                            <!-- 指定image的名字(包含registry地址)-->
                            <name>simple_ecommerce/${project.name}:${project.version}</name>
                            <!--registry地址,用於推送,拉取鏡像-->
                            <registry>registry.itxs.cn</registry>
                            <!-- 別名為master,不關鍵-->
                            <alias>master</alias>
                            <build>
                                <!-- 指定dockerfile文件的位置-->
                                <dockerFile>${project.basedir}/Dockerfile</dockerFile>
                                <buildOptions>
                                    <!-- 網絡的配置,與宿主主機共端口號-->
                                    <network>host</network>
                                </buildOptions>
                            </build>
                        </image>
                    </images>
                </configuration>

                <executions>
                    <execution>
                        <id>docker-exec</id>
                        <!-- 綁定mvn install階段,當執行mvn install時 就會執行docker build 和docker push-->
                        <phase>install</phase>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

可以看到庫存微服務pom文件添加docker-maven-plugin,mvn install階段,當執行mvn install時 就會執行docker build 和docker push,我們前面也介紹Docker Harbor私有倉庫的部署,可以通過插件直接推送內網的Harbor私有倉庫里。

訂單微服務

編寫配置文件

bootstrap.yml

spring:
  application:
    name: ecom-order-service
  profiles:
    active: dev
  main:
    allow-circular-references: true
  cloud:
    # 負載均衡器緩存
    loadbalancer:
      cache:
        enabled: true
        caffeine:
          spec: initialCapacity=500,expireAfterWrite=5s
    nacos:
      # 注冊中心信息放在配置中心上,每個程序一般只配置配置中心的信息
      server-addr: ${NACOS_CONFIG_SERVER:"192.168.50.95:8848"}
      config:
        server-addr: ${spring.cloud.nacos.server-addr}
        file-extension: yaml
        namespace: ${NACOS_CONFIG_NAMESPACE:"a2b1a5b7-d0bc-48e8-ab65-04695e61db01"}
        group: ${NACOS_CONFIG_GROUP:"order-group"}
        username: itsx
        password: itxs123
        extension-configs:
          - dataId: commons-dev.yaml
            group: commons-group
            refresh: true
        enabled: true # 默認為true,設置false 來完全關閉 Spring Cloud Nacos Config
        refresh-enabled: true # 默認為true,當變更配置時,應用程序中能夠獲取到最新的值,設置false來關閉動態刷新,我們使用注冊中心場景大部分就是動態感知,因此基本使用默認的

制作Docker啟動腳本

docker-startup.sh

#!/bin/bash
set -x
export CUSTOM_SEARCH_NAMES="application,custom"
export CUSTOM_SEARCH_LOCATIONS=${BASE_DIR}/init.d/,file:${BASE_DIR}/conf/

JAVA_OPT="${JAVA_OPT} -Dsimple_ecommerce.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/ecom-order-service.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --spring.config.name=${CUSTOM_SEARCH_NAMES}"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/logback.xml"
JAVA_OPT="${JAVA_OPT} --logging.file.path=${BASE_DIR}/logs/"
JAVA_OPT="${JAVA_OPT} --spring.config.location=${BASE_DIR}/conf/bootstrap.yml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"

echo "ecom-order-service is starting, you can docker logs your container"
exec $JAVA ${JAVA_OPT}

制作Dockerfile文件

Dockerfile文件

FROM java:8
MAINTAINER itxs "107734588@qq.com"

ARG ECOM_ORDER_SERVICE_VERSION=1.0
ARG ECOM_ORDER_SERVICE_DIR="ecom-order-service"
ARG ECOM_ORDER_SERVICE_PACKAGE="ecom-order-service-$ECOM_ORDER_SERVICE_VERSION.jar"
ARG ECOM_ORDER_SERVICE_PROGRAM="ecom-order-service.jar"

# set environment
ENV BASE_DIR="/home/simple_ecommerce/${ECOM_ORDER_SERVICE_DIR}" \
    CLASSPATH=".:/home/simple_ecommerce/${ECOM_ORDER_SERVICE_DIR}/conf:$CLASSPATH" \
    JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64" \
    JAVA="/usr/lib/jvm/java-8-openjdk-amd64/bin/java" \
    JAVA_OPT_EXT="${JAVA_OPT_EXT}" \
    TIME_ZONE="Asia/Shanghai"

WORKDIR $BASE_DIR

ADD ./target/$ECOM_ORDER_SERVICE_PACKAGE target/$ECOM_ORDER_SERVICE_PROGRAM
RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone

ADD bin/docker-startup.sh bin/docker-startup.sh
ADD conf/bootstrap.yml conf/bootstrap.yml
ADD conf/logback.xml conf/logback.xml
RUN mkdir -p init.d

# set startup log dir
RUN mkdir -p logs \
        && cd logs \
        && touch start.out \
        && ln -sf /dev/stdout start.out \
        && ln -sf /dev/stderr start.out
RUN chmod +x bin/docker-startup.sh

EXPOSE 4070
ENTRYPOINT ["bin/docker-startup.sh"]

打包配置

訂單微服務pom文件添加docker-maven-plugin

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>simple-ecommerce</artifactId>
        <groupId>cn.itxs</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ecom-order-service</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>
    <name>ecom-order-service</name>
    <description>a simple electronic commerce platform demo tutorial for order service</description>

    <dependencies>
        <dependency>
            <groupId>cn.itxs</groupId>
            <artifactId>ecom-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>3.0.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定該Main Class為全局的唯一入口 -->
                    <mainClass>cn.itxs.ecom.order.OrderServiceApplication</mainClass>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal><!--可以把依賴的包都打包到生成的Jar包中-->
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.39.1</version>
                <configuration>
                    <authConfig>
                        <!-- registry服務的認證-->
                        <username>admin</username>
                        <password>admin12345</password>
                    </authConfig>
                    <images>
                        <image>
                            <!-- 指定image的名字(包含registry地址)-->
                            <name>simple_ecommerce/${project.name}:${project.version}</name>
                            <!--registry地址,用於推送,拉取鏡像-->
                            <registry>registry.itxs.cn</registry>
                            <!-- 別名為master,不關鍵-->
                            <alias>master</alias>
                            <build>
                                <!-- 指定dockerfile文件的位置-->
                                <dockerFile>${project.basedir}/Dockerfile</dockerFile>
                                <buildOptions>
                                    <!-- 網絡的配置,與宿主主機共端口號-->
                                    <network>host</network>
                                </buildOptions>
                            </build>
                        </image>
                    </images>
                </configuration>

                <executions>
                    <execution>
                        <id>docker-exec</id>
                        <!-- 綁定mvn install階段,當執行mvn install時 就會執行docker build 和docker push-->
                        <phase>install</phase>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

上面訂單微服務pom文件添加docker-maven-plugin,mvn install階段,當執行mvn install時 就會執行docker build 和docker push,我們前面也介紹Docker Harbor私有倉庫的部署,可以通過插件直接推送內網的Harbor私有倉庫里。

打包

# 由於需要進行docker build 和docker push,打包機器需要安裝docker,直接執行mvn clean install 即可,如果需要單獨mvn clean install# 如果是單獨針對庫存微服務只進行docker build,可以進入庫存微服務目錄mvn clean package docker:bulid

docker build兩個微服務的鏡像文件如下,這個是我單獨docker build沒有push.如果install的話上傳內網Harbor倉庫本地先生成鏡像,然后再上傳最后刪除本地的鏡像。

image-20220419152731637

部署

env目錄

訂單微服務環境變量ecom-order-service.env,這里NACOS_CONFIG_SERVER簡單先用地址,如果是在單個宿主機或者K8s環境下,並且在同個容器網絡內可以直接使用容器名,可不需要Nacos地址配置,這里我們就先用暴露宿主機端口,先重點放在兩個微服務容器上。

SPRING_PROFILES_ACTIVE=dev
NACOS_CONFIG_SERVER=192.168.50.95:8848
NACOS_CONFIG_NAMESPACE=a2b1a5b7-d0bc-48e8-ab65-04695e61db01
NACOS_CONFIG_GROUP=order-group
JAVA_OPT_EXT="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xms1024m -Xmx1024m -Xmn1024m -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=4 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSConcurrentMTEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m"

庫存微服務環境變量ecom-storage-service.env

SPRING_PROFILES_ACTIVE=dev
NACOS_CONFIG_SERVER=192.168.50.95:8848
NACOS_CONFIG_NAMESPACE=a2b1a5b7-d0bc-48e8-ab65-04695e61db01
NACOS_CONFIG_GROUP=storage-group
JAVA_OPT_EXT="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xms1024m -Xmx1024m -Xmn1024m -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=4 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSConcurrentMTEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m"

制作Docker-Compose編排文件

我這里做法沒有將多個微服務編排的一個Docker-Compose文件里,而已單獨做一個Docker-Compose,通過shell腳本串聯起來執行,各位也可以直接編寫一個Docker-Compose

全局環境變量.env存放全局參數信息,例如各微服務的版本信息

ECOM_STORAGE_VERSION=1.0ECOM_ORDER_VERSION=1.0

庫存微服務Docker-Compose文件ecom-storage-service.yml,如果是本地build則image去掉registry.itxs.cn/

version: "3"
services:
  ecom-storage-service:
    image: registry.itxs.cn/simple_ecommerce/ecom-storage-service:${ECOM_STORAGE_VERSION}
    container_name: ecom-storage-service
    env_file:
      - ../env/ecom-storage-service.env
    volumes:
      - ../logs/ecom-storage-service/:/home/simple_ecommerce/ecom-storage-service/logs
    ports:
      - "4080:4080"
    networks:
      - simple_ecommerce
    restart: always
networks:
  simple_ecommerce:
    external: true

訂單微服務Docker-Compose文件ecom-order-service.yml

version: "3"
services:
  ecom-order-service:
    image: registry.itxs.cn/simple_ecommerce/ecom-order-service:${ECOM_ORDER_VERSION}
    container_name: ecom-order-service
    env_file:
      - ../env/ecom-order-service.env
    volumes:
      - ../logs/ecom-order-service/:/home/simple_ecommerce/ecom-order-service/logs
    ports:
      - "4070:4070"
    networks:
      - simple_ecommerce
    restart: always
networks:
  simple_ecommerce:
    external: true

部署腳本

bin目錄下我們創建操作腳本,init.sh初始化檢查環境、安裝docker和docker-compose、

#!/usr/bin/env bash

echo "############當前操作系統版本##############"
if ! type yum >/dev/null 2>&1; then
        echo "【ERROR】目前腳本僅支持CentOS7.X系統"
        exit 8
else
        osVersion=$(echo `cat /etc/redhat-release | sed -r 's/.* ([0-9]+)\..*/\1/'`)
        if [[ "$osVersion" != "7" ]]; then
             echo "【ERROR】目前腳本僅支持CentOS7.X系統"
             exit 8
        else
             echo '版本校驗成功' 
        fi
fi

echo "############判斷是否安裝了docker##############"
if ! type docker >/dev/null 2>&1; then
    echo 'docker 未安裝';
	  echo '開始安裝Docker....';
    yum install -y yum-utils
    yum-config-manager \
          --add-repo \
          https://download.docker.com/linux/centos/docker-ce.repo

    #安裝docker核心引擎、命令行客戶端、容器
    yum install docker-ce docker-ce-cli containerd.io
    echo 'docker 安裝完畢';
    #啟動docker
	  echo '配置Docker開啟啟動';
	  systemctl enable docker
	  systemctl start docker

cat >> /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]
}
EOF
	  systemctl restart docker
else
    echo 'docker 安裝完畢';
fi

echo "############判斷是否安裝了wget##############"
if ! type wget >/dev/null 2>&1; then
    echo 'wget 未安裝';
	  echo '開始安裝wget....';
	  yum -y install wget
else
    echo 'wget 已安裝';
fi

echo "############判斷是否安裝了dos2unix##############"
if ! type dos2unix >/dev/null 2>&1; then
    echo 'dos2unix 未安裝';
	  echo '開始安裝dos2unix....';
	  yum -y install dos2unix*
else
    echo 'dos2unix 已安裝';
fi

echo "############判斷是否安裝了docker-compose##############"
if ! type docker-compose >/dev/null 2>&1; then
    echo 'docker-compose 未安裝';
	  echo '開始安裝docker-compose....';
	  wget http://www.itxiaoshen.com:3001/assets/docker-compose
	  chmod +x docker-compose
	  mv docker-compose /usr/local/bin/
	  docker-compose -v
	  echo 'docker-compose安裝完畢....';
else
    echo 'docker-compose 已安裝';
fi

echo '創建simple_ecommerce網絡';
docker network create simple_ecommerce

# 添加執行權限
chmod +x ../bin/startup-all.sh
chmod +x ../bin/shutdown-all.sh
chmod +x ../bin/update.sh
chmod +x ../bin/wait-for-it.sh

# 修改編碼
echo "修改編碼...."
dos2unix startup-all.sh
dos2unix shutdown-all.sh
dos2unix update.sh
dos2unix wait-for-it.sh

sh startup-all.sh

wait-for-it.sh等待請求腳本

#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available

WAITFORIT_cmdname=${0##*/}

echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }

usage()
{
    cat << USAGE >&2
Usage:
    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
    -h HOST | --host=HOST       Host or IP under test
    -p PORT | --port=PORT       TCP port under test
                                Alternatively, you specify the host and port as host:port
    -s | --strict               Only execute subcommand if the test succeeds
    -q | --quiet                Don't output any status messages
    -t TIMEOUT | --timeout=TIMEOUT
                                Timeout in seconds, zero for no timeout
    -- COMMAND ARGS             Execute command with args after the test finishes
USAGE
    exit 1
}

wait_for()
{
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    else
        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
    fi
    WAITFORIT_start_ts=$(date +%s)
    while :
    do
        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
            WAITFORIT_result=$?
        else
            (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
            WAITFORIT_result=$?
        fi
        if [[ $WAITFORIT_result -eq 0 ]]; then
            WAITFORIT_end_ts=$(date +%s)
            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
            break
        fi
        sleep 1
    done
    return $WAITFORIT_result
}

wait_for_wrapper()
{
    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    else
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    fi
    WAITFORIT_PID=$!
    trap "kill -INT -$WAITFORIT_PID" INT
    wait $WAITFORIT_PID
    WAITFORIT_RESULT=$?
    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    fi
    return $WAITFORIT_RESULT
}

# process arguments
while [[ $# -gt 0 ]]
do
    case "$1" in
        *:* )
        WAITFORIT_hostport=(${1//:/ })
        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
        shift 1
        ;;
        --child)
        WAITFORIT_CHILD=1
        shift 1
        ;;
        -q | --quiet)
        WAITFORIT_QUIET=1
        shift 1
        ;;
        -s | --strict)
        WAITFORIT_STRICT=1
        shift 1
        ;;
        -h)
        WAITFORIT_HOST="$2"
        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
        shift 2
        ;;
        --host=*)
        WAITFORIT_HOST="${1#*=}"
        shift 1
        ;;
        -p)
        WAITFORIT_PORT="$2"
        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
        shift 2
        ;;
        --port=*)
        WAITFORIT_PORT="${1#*=}"
        shift 1
        ;;
        -t)
        WAITFORIT_TIMEOUT="$2"
        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
        shift 2
        ;;
        --timeout=*)
        WAITFORIT_TIMEOUT="${1#*=}"
        shift 1
        ;;
        --)
        shift
        WAITFORIT_CLI=("$@")
        break
        ;;
        --help)
        usage
        ;;
        *)
        echoerr "Unknown argument: $1"
        usage
        ;;
    esac
done

if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
    echoerr "Error: you need to provide a host and port to test."
    usage
fi

WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}

# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)

WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
    WAITFORIT_ISBUSY=1
    # Check if busybox timeout uses -t flag
    # (recent Alpine versions don't support -t anymore)
    if timeout &>/dev/stdout | grep -q -e '-t '; then
        WAITFORIT_BUSYTIMEFLAG="-t"
    fi
else
    WAITFORIT_ISBUSY=0
fi

if [[ $WAITFORIT_CHILD -gt 0 ]]; then
    wait_for
    WAITFORIT_RESULT=$?
    exit $WAITFORIT_RESULT
else
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        wait_for_wrapper
        WAITFORIT_RESULT=$?
    else
        wait_for
        WAITFORIT_RESULT=$?
    fi
fi

if [[ $WAITFORIT_CLI != "" ]]; then
    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
        exit $WAITFORIT_RESULT
    fi
    exec "${WAITFORIT_CLI[@]}"
else
    exit $WAITFORIT_RESULT
fi

容器啟動腳本startup-all.sh,其他只是示例流程,可以一步步完善補充

#!/usr/bin/env bash

echo '=====開始安裝simple_ecommerce系統環境====='

#echo '=====開始運行mysql====='
#docker-compose -f ../yaml/mysql.yml up -d

#echo '=====開始運行nacos====='
#docker-compose -f ../yaml/nacos.yml up -d
#echo '=====nacos正在進行初始化,請等待...====='
#./wait-for-it.sh http://localhost:8848 --timeout=60  -- echo "=====nacos已經准備就緒====="

#echo '=====開始運行rocketmq====='
#docker-compose -f ../yaml/rocketmq.yml up -d

#echo '=====開始運行redis====='
#docker-compose -f ../yaml/redis.yml up -d

#echo '=====開始運行TinyID分布式系統全局ID服務====='
#docker-compose -f ../yaml/tinyid.yml up -d

#echo '=====開始運行ELK====='
#docker-compose -f ../yaml/elk.yml up -d

echo '======================'
echo '=====開始運行后台====='
echo '======================'

#echo '=====開始運行ecom-gateway====='
#docker-compose -f ../yaml/ecom-gateway.yml up -d

echo '=====開始運行ecom-storage-service====='
docker-compose -f ../yaml/ecom-storage-service.yml up -d

echo '=====開始運行ecom-order-service====='
docker-compose -f ../yaml/ecom-order-service.yml up -d

echo '執行完成 日志目錄: ./log'

echo '======================'
echo '=====開始運行前台====='
echo '======================'

#echo '=====開始運行ecom_vue_web====='
#docker-compose -f ../yaml/ecom_vue_web.yml up -d

echo '======================================================'
echo '=====所有服務已經啟動【請檢查是否存在錯誤啟動的】====='
echo '======================================================'

容器關閉腳本shutdown-all.sh

#!/usr/bin/env bash

echo '=====開始結束運行simple_ecommerce系統服務====='

#echo '=====結束運行mysql====='
#docker-compose -f ../yaml/mysql.yml down

#echo '=====結束運行nacos====='
#docker-compose -f ../yaml/nacos.yml down

#echo '=====結束運行rocketmq====='
#docker-compose -f ../yaml/rocketmq.yml down

#echo '=====結束運行redis====='
#docker-compose -f ../yaml/redis.yml down

#echo '=====結束運行TinyID分布式系統全局ID服務====='
#docker-compose -f ../yaml/tinyid.yml down

#echo '=====結束運行ELK====='
#docker-compose -f ../yaml/elk.yml down

echo '=========================='
echo '=====結束后台服務運行====='
echo '=========================='

#echo '=====結束運行ecom-gateway====='
#docker-compose -f ../yaml/ecom-gateway.yml down

echo '=====結束運行ecom-storage-service====='
docker-compose -f ../yaml/ecom-storage-service.yml down

echo '=====結束運行ecom-order-service====='
docker-compose -f ../yaml/ecom-order-service.yml down

echo '=========================='
echo '=====結束前台服務運行====='
echo '=========================='

#echo '=====結束運行ecom_vue_web====='
#docker-compose -f ../yaml/ecom_vue_web.yml down

echo '=============================='
echo '=====所有服務已經結束運行====='
echo '=============================='

更新鏡像腳本update.sh,包含關閉容器、下載新的鏡像、啟動容器

#!/usr/bin/env bash

echo '=====開始更新simple_ecommerce系統鏡像====='

echo '=====開始關閉運行的容器====='
sh shutdown-all.sh

#echo '=====開始更新ecom-gateway====='
#docker pull registry.itxs.cn/simple_ecommerce/ecom-gateway

echo '=====開始更新ecom-storage-service====='
docker pull registry.itxs.cn/simple_ecommerce/ecom-storage-service

echo '=====開始更新ecom-order-service====='
docker pull registry.itxs.cn/simple_ecommerce/ecom-order-service

#echo '=====開始更新cu_vue_web====='
#docker pull registry.itxs.cn/simple_ecommerce/ecom_vue_web

echo '=====刪除docker標簽為none的鏡像====='
docker images | grep none | awk '{print $3}' | xargs docker rmi

echo '=====開始運行的一鍵部署腳本====='
sh startup-all.sh

執行測試

# 進入到bin目錄下,由於我這里本地有鏡像,少了pull流程
sh ./init.sh

image-20220419155812868

查看容器運行情況,容器正常運行

image-20220419160027572

查看nacos服務的注冊信息

image-20220419160237729

訪問訂單接口http://192.168.50.95:4070/create/1001/1001/3 ,返回成功結果

image-20220419160329098

查看訂單表和庫存表的數據都已更新,至此部署完畢

image-20220419160543506

**本人博客網站 **IT小神 www.itxiaoshen.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM