微服务不同环境到底该如何部署?

你们是怎么部署微服务的?

如何处理不同的环境部署的?

今天给大家带来我们的一些实践。

1 微服务部署的挑战

一句话来说,微服务部署的最大挑战是如何保证不同的环境部署代码的一致性,以及如何区分不同的部署环境。

作为开发,可能我们大多数时间都在开发代码,却忽略了发布的重要性,实际发布才是到用户手里的最后一步,这一步做的不好,那么你的软件价值则不能很好交付到用户手里,作为开发,你自己的体验也不好。

Docker正式因为改变了部署交付的方式,才引起了革命,产生了如此广泛的影响。

所以我们需要重视并做好交付这一步。

2 微服务部署的方式

微服务的部署方式有多种,传统的比如打个Jar包,通过java -jar来启动,在云计算时代,更多的则是通过构建Docker镜像的方式,通过docker run来启动。

这里的一个主要问题是,如何在不同的环境中去部署相应的服务。

对于一般公司来讲,我们会有开发、沙箱、预生产、生产环境,不同的环境,会有不同的配置,当然微服务背景下,我们都会引入配置中心,但不同环境下,还是需要不同的配置中心配置。

那么如何在不同的环境下去部署?

让我们来看看几种解决方案。(说明:以下都是通过构建Docker镜像的方式来部署的方案。)

方案一:不同的环境打不同的镜像

此种方案通过配合maven profile,maven-antrun-plugin插件,在不同的环境下,将不同环境的配置文件覆盖resources目录下的配置最后在打成Jar包,然后通过docker-maven-plugin插件,将应用Jar包构建成Docker镜像,推送到不同环境的Docker镜像仓库。

不同环境的部署,只需要到不同环境的Docker镜像仓库中拉取镜像,然后启动就行了。

关键步骤信息如下:
1)pom.xml中提前设定好不同的profile,不同的profile将使用不同的配置文件进行打包并构建镜像

<!-- 这里以沙箱环境的镜像打包为例 -->
         <profile>
            <id>sand</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-antrun-plugin</artifactId>
                        <version>1.8</version>
                        <executions>
                            <execution>
                                <phase>validate</phase>
                                <configuration>
                                    <target>
                                        <move file="./config/sand/bootstrap.yml"
                                              tofile="./src/main/resources/bootstrap.yml"
                                              overwrite="true"/>
                                        <echo>${revision}</echo>
                                        <replace file="src/main/resources/bootstrap.yml" token="@version@"
                                                 value="${revision}"/>
                                    </target>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>com.spotify</groupId>
                        <artifactId>docker-maven-plugin</artifactId>
                        <version>1.0.0</version>
                        <executions>
                            <execution>
                                <id>build-image</id>
                                <phase>package</phase>
                                <goals>
                                    <goal>build</goal>
                                    <goal>push</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <!-- 镜像名称 -->
                            <imageName>127.0.0.1:5000/${project.artifactId}</imageName>
                            <!-- 指定标签 -->
                            <imageTags>
                                <imageTag>${revision}</imageTag>
                            </imageTags>
                            <dockerHost>http://127.0.0.1:2375</dockerHost>
                        <dockerDirectory>${project.basedir}/src/main/assembly</dockerDirectory>
                            <buildArgs>
                                <APP_NAME>${project.build.finalName}</APP_NAME>
                            </buildArgs>
                            <resources>
                                <resource>
                                    <directory>${project.build.directory}</directory>
                                    <includes>
                                        <include>${project.build.finalName}.jar</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

2)maven打包阶段指定不同的profile

mvn clean deploy -P sand

上述部署方案整体流程如下图所示:

multicentrDeploy1.png

上述方案的问题是,需要提前将所有的配置文件都放在代码仓库中,在环境多的时候不便于维护,特别是在多数据中心的情况下。另外不同的环境实际部署的是不同的镜像,不符合Docker的一次构建,处处部署的思想,当然还需要不同的环境部署不同的镜像仓库,也有一定的资源浪费。

方案二:将所有环境打进一个镜像,通过spring.profiles.active来生效不同的环境

此种方案是将所有环境都打包到一个镜像中,然后再启动的时候通过Spring的spring.profiles.active来生效不同的环境配置,关键步骤如下:

1)将所有环境的配置放到一个配置文件中,或者是放在不同环境的配置文件中(下面是将所有的配置都放到/src/main/resources/bootstrap.yml文件中):

server:
  port: 8888

spring:
  application:
    name: my-app
  profiles:
    # 默认启用dev环境
    active: dev   
# 开发环境
---
spring:
  config:
    activate:
      on-profile: dev
  cloud:
    nacos:
      discovery:
        server-addr: http://localhost:8848
        metadata:
          version: '1.0.0-SNAPSHOT'
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml
        name: ${spring.application.name}
# 沙箱环境
---
spring:
  config:
    activate:
      on-profile: sand
  cloud:
    nacos:
      discovery:
        server-addr: http://sand.nacos:8840
        metadata:
          version: '1.0.0-SNAPSHOT'
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml
        name: ${spring.application.name}
# 生产环境
---
spring:
  config:
    activate:
      on-profile: prod
  cloud:
    nacos:
      discovery:
        server-addr: http://prod.nacos:8848
        metadata:
          version: '1.0.0-SNAPSHOT'
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml
        name: ${spring.application.name}

2)启动的时候,通过启动脚本指定不同的active:

JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=sand"

上述部署方案整体流程如下图所示:

multicentrDeploy2.png

上述方案可以确保不同环境的镜像一致性,符合Docker的构建一次,处处部署的思想,当然问题是需要提前将所有环境都放在项目里,这会导致在环境变化时需要修改代码的问题(代码和配置没有完全分离),也会有安全问题,因为你的生产环境地址也暴露无遗。

方案三:将所有环境抽离出环境变量,在应用启动的时候通过环境变量注入

此种方案利用了Spring通过命令行来指定相关配置的特性,真正实现了代码与配置的完全分离。代码库里不存放任何的环境地址(可以只存放本地开发环境地址),所有的环境地址都通过环境变量的方式注入进去。这样可以真正做到,一套代码,处处运行,即使在新增数据中心,需要重新部署的时候,也不需要修改任何代码,直接通过环境变量指定即可。

关键步骤如下:
1)Docker部署脚本在启动容器时将环境变量注入:

docker run -e SPRING_CLOUD_NACOS_DISCOVERY_SERVER_ADDR="localhost:8840" -d --network=host -v /log/:/log/ ${SERVER_DOCKER}/${SERVICE_NAME}:latest

2)应用启动脚本通过环境变量,获取相应的地址:

# 指定nacos地址
JAVA_OPTS="$JAVA_OPTS -Dspring.cloud.nacos.discovery.server-addr=${SPRING_CLOUD_NACOS_DISCOVERY_SERVER_ADDR}"
# 指定日志文件
JAVA_OPTS="$JAVA_OPTS -DLOGBACK_LOG_HOME=${LOGBACK_LOG_HOME}"
JAVA_OPTS="$JAVA_OPTS -jar ${DEPLOY_DIR}/${SERVER_NAME}.jar"

上述部署方案整体流程如下图所示:

multicentrDeploy3.png

上述方案实现了代码与配置的真正分离,代码库里无需存放任何地址(可以指保留开发环境地址),不同的部署环境只要注入不同的环境配置即可。可以称得上是最佳实践。

写在最后

作为开发,不光光要关心写代码,更要重视发布的重要性。

微服务的发布有多种方式,云计算时代通过Docker镜像去发布是你的最优选择。

要把打包一次,处处运行作为发布目标。

想要实现上述目标,需要做到真正意义上的代码、配置分离,通过Docker环境变量注入,配合应用启动脚本,你可以做到真正意义上的代码、配置分离。

希望今天的文章对大家有所帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容