概述

我们使用前面《SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(中)》的两个微服务示例,分别是库存微服务和订单微服务,基于Nacos注册中心和配置中心的使用,前面Nacos我们已基于dock-compose方式部署,我们增加配置数据,这里我们暂时也不把数据打包进去,各位可以直接将容器以dokcer export方式导入为镜像,微服务使用订单、库存MySQL数据库暂时也不单独做成镜像,各位可以做成SQL脚本执行导入方式。

整体工程结构

  • docker目录docker compose编排脚本目录

    • bin目录:包含初始化脚本、启动脚本、停止脚本、更新脚本
    • env目录:存在为微服务环境变量
    • yaml目录:存在全局环境脚本变量、微服务docker-compose脚本
  • 库存微服务
    • bin目录:存在微服务启动脚本
    • conf目录:存在启动配置文件和日志配置文件
    • Dockerfile文件
  • 订单微服务
    • bin目录:存在微服务启动脚本
    • conf目录:存在启动配置文件和日志配置文件
    • Dockerfile文件

库存微服务

编写配置文件

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仓库本地先生成镜像,然后再上传最后删除本地的镜像。

部署

env目录

订单微服务环境变量ecom-order-service.env,这里NACOS_CONFIG_SERVER简单先用地址,如果是在单个宿主机或者K8s环境下,并且在同个容器网络内可以直接使用容器名,可不需要Nacos地址配置,这里我们就先用暴露宿主机端口,先重点放在两个微服务容器上。

SPRING_PROFILES_ACTIVE=devNACOS_CONFIG_SERVER=192.168.50.95:8848NACOS_CONFIG_NAMESPACE=a2b1a5b7-d0bc-48e8-ab65-04695e61db01NACOS_CONFIG_GROUP=order-groupJAVA_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=devNACOS_CONFIG_SERVER=192.168.50.95:8848NACOS_CONFIG_NAMESPACE=a2b1a5b7-d0bc-48e8-ab65-04695e61db01NACOS_CONFIG_GROUP=storage-groupJAVA_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: alwaysnetworks:  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: alwaysnetworks:  simple_ecommerce:    external: true

部署脚本

bin目录下我们创建操作脚本,init.sh初始化检查环境、安装docker和docker-compose、

#!/usr/bin/env bashecho "############当前操作系统版本##############"if ! type yum >/dev/null 2>&1; then        echo "【ERROR】目前脚本仅支持CentOS7.X系统"        exit 8else        osVersion=$(echo `cat /etc/redhat-release | sed -r 's/.* ([0-9]+)\..*/\1/'`)        if [[ "$osVersion" != "7" ]]; then             echo "【ERROR】目前脚本仅支持CentOS7.X系统"             exit 8        else             echo '版本校验成功'         fifiecho "############判断是否安装了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 dockercat >> /etc/docker/daemon.json << EOF{  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]}EOF	  systemctl restart dockerelse    echo 'docker 安装完毕';fiecho "############判断是否安装了wget##############"if ! type wget >/dev/null 2>&1; then    echo 'wget 未安装';	  echo '开始安装wget....';	  yum -y install wgetelse    echo 'wget 已安装';fiecho "############判断是否安装了dos2unix##############"if ! type dos2unix >/dev/null 2>&1; then    echo 'dos2unix 未安装';	  echo '开始安装dos2unix....';	  yum -y install dos2unix*else    echo 'dos2unix 已安装';fiecho "############判断是否安装了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 已安装';fiecho '创建simple_ecommerce网络';docker network create simple_ecommerce# 添加执行权限chmod +x ../bin/startup-all.shchmod +x ../bin/shutdown-all.shchmod +x ../bin/update.shchmod +x ../bin/wait-for-it.sh# 修改编码echo "修改编码...."dos2unix startup-all.shdos2unix shutdown-all.shdos2unix update.shdos2unix wait-for-it.shsh 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 bashecho '=====开始安装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 -decho '======================'echo '=====开始运行后台====='echo '======================'#echo '=====开始运行ecom-gateway====='#docker-compose -f ../yaml/ecom-gateway.yml up -decho '=====开始运行ecom-storage-service====='docker-compose -f ../yaml/ecom-storage-service.yml up -decho '=====开始运行ecom-order-service====='docker-compose -f ../yaml/ecom-order-service.yml up -decho '执行完成 日志目录: ./log'echo '======================'echo '=====开始运行前台====='echo '======================'#echo '=====开始运行ecom_vue_web====='#docker-compose -f ../yaml/ecom_vue_web.yml up -decho '======================================================'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

查看容器运行情况,容器正常运行

查看nacos服务的注册信息

访问订单接口http://192.168.50.95:4070/create/1001/1001/3 ,返回成功结果

查看订单表和库存表的数据都已更新,至此部署完毕

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

SpringCloudAlibaba微服务docker容器打包和部署示例实战的更多相关文章

  1. .netcore下的微服务、容器、运维、自动化发布

    原文:.netcore下的微服务.容器.运维.自动化发布 微服务 1.1     基本概念 1.1.1       什么是微服务? 微服务架构是SOA思想某一种具体实现.是一种将单应用程序作为一套小型 ...

  2. 10分钟了解微服务、容器和Kubernetes

    什么是微服务? 什么是微服务?你应该使用微服务吗?微服务与容器和 Kubernetes 有什么关系?如果这些问题在您的日常生活中不断出现,那么这篇文章适合您. 从根本上说,微服务只是一个运行在服务器或 ...

  3. CI Weekly #5 | 微服务架构下的持续部署与交付

    CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...

  4. 微服务 + Docker + Kubernetes 入门实践 目录

    微服务 + Docker + Kubernetes 入门实践: 微服务概念 微服务的一些基本概念 环境准备 Ubuntu & Docker 本文主要讲解在 Ubuntu 上安装和配置 Dock ...

  5. 第二个视频作品《[SpringCloudAlibaba]微服务之注册中心nacos》上线了

    1.场景描述 第二个视频作品出炉了,<[SpringCloudAlibaba]微服务之注册中心nacos>上线了,有需要的朋友可以直接点击链接观看.(如需购买,请通过本文链接购买) 2. ...

  6. DDD/CQRS模式,微服务,容器

    DDD/CQRS模式,微服务,容器 https://docs.microsoft.com/zh-cn/previous-versions/msp-n-p/ee658109(v=pandp.10) We ...

  7. Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战

    Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战 Java生鲜电商平台-  什么是秒杀 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动 比如说京东秒杀,就是一种定时定量秒杀,在规定 ...

  8. Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲

    Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲 Java生鲜电商平台:   微服务是当前非常流行的技术框架,通过服务的小型化.原子化以及分布式架构的弹性伸缩和高可用性, ...

  9. SpringCloud微服务实战——搭建企业级开发框架(三十四):SpringCloud + Docker + k8s实现微服务集群打包部署-Maven打包配置

      SpringCloud微服务包含多个SpringBoot可运行的应用程序,在单应用程序下,版本发布时的打包部署还相对简单,当有多个应用程序的微服务发布部署时,原先的单应用程序部署方式就会显得复杂且 ...

随机推荐

  1. 【推理引擎】ONNXRuntime 的架构设计

    ONNXRuntime,深度学习领域的神经网络模型推理框架,从名字中可以看出它和 ONNX 的关系:以 ONNX 模型作为中间表达(IR)的运行时(Runtime). 本文许多内容翻译于官方文档:ht ...

  2. redis整理:常用命令,雪崩击穿穿透原因及方案,分布式锁实现思路,分布式锁redission(更新中)

    redis个人整理笔记 reids常见数据结构 基本类型 String: 普通key-value Hash: 类似hashMap List: 双向链表 Set: 不可重复 SortedSet: 不可重 ...

  3. SpringBoot中的日志使用:

    SpringBoot中的日志使用(一) 一:日志简介: 常用的日志接口 commons-logging/slf4j 日志框架:log4j/logback/log4j2 日志接口屏蔽了日志框架的底层实现 ...

  4. Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?

    Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询.在 Myba ...

  5. Java 中 sleep 方法和 wait 方法的区别?

    虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件 ...

  6. maven下载出现unknown文件夹

    问题场景 maven下载配置完成后,发现如上图代码包下载失败,本地maven库中出现unknown文件夹,也就是说,maven无法定位下载到上面的代码包. 解决过程 仔细观察发现,所有下载失败的代码包 ...

  7. GC 是什么?为什么要有 GC?

    GC 是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误 的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动 监测对象是否超过作用域从而达到自动回收内存 ...

  8. dp求最长递增子序列并输出

    1 import java.util.ArrayList; 2 import java.util.Arrays; 3 import java.util.List; 4 5 /** 6 * Create ...

  9. flash的TotalFrames显示undefined

    通过js来操作flash的时候,获取到总帧数的是属性.TotalFrames,而不是属性TotalFrames().在asp.net中,js放在最后可以在一定程度上避免当前flash没有加载完,导致获 ...

  10. ImportError: No module named 'Tkinter' [closed]

    跑maskrcnn报错:UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot sh ...