SpringCloud+Docker+Jenkins+GitLab+Maven实现自动化构建与部署实战 野性酷女 2022-09-02 04:04 183阅读 0赞 **点击关注公众号,利用****碎片时间学习****![586a36b0786cf3dcf68c3f2546704faa.png][]** *作者:寅务* *www.jianshu.com/p/bdb0642b7495* ## 1、前言与初衷 ## 本文章会涉及Docker常见命令基础知识点结合不同场景实操一起使用。 本文章会涉及结合工作过程中部署不同环境服务器的项目案例场景为初心进行实际细讲。 本文章主要讲述Docker、Jenkins、GitLab、Git、JDK、SpringBoot、Maven等技术结合实现自动化运维部署(DevOps)应用工程,适合SpringCloud部署。 初衷想法:在学习过程中遇到比较有趣的问题、然而花了点心血和时间去整理,然而进行梳理出来一份文章比较完整有知识体系的DevOps自动化构建与部署工程文章,技术知识内容比较多,而且文章内容较长,然而分了几个章程来讲述 ## 2、什么是DevOps? ## DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合,它是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。 它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运营工作必须紧密合作。 ## 3 涉及软件环境搭建内容 ## * 如何在Centos7安装JDK1.8-u121详解 * 如何在Centos7安装Maven3.6.1详解 * 如何在Centos7安装Git详解 * 如何在CentOS7与Git配置免密码登陆详解 * 如何在Docker安装GitLab详解 * 如何在Docker创建NetWork网络详解 * 如何在Docker安装Registry私服详解 * 如何在Docker安装Jenkins详解 > 见附录 特别说明 ![635cc0bc01cb58a7bd8a7a0d47bf1ee7.png][] 1、如何使用Maven结合Docker把SpringBoot应用编译成可用的镜像进行部署。 2、其中JDK和Maven是传统方式进行安装,由于本人Centos操作系统是有其他软件依赖它们,有时候传统方式安装软件会更好,这里不过多的阐述。有些软件在Docker安装过程与使用过程并没传统方式的简单,比如:Jenkins。 ## 4、需要准备的工作有哪些 ## #### 4.1 工程结构 #### * 打开IDEA或Eclipse新建一个SpringBoot的应用. ![c49a00ebc2eb15585179c192fc6ffe32.png][] ![d7924d9362829d8ba8175c9e58052042.png][] 环境配置特别说明 > 注意事项:其中Gitlab、Registry、Jenkins都安装在node1机器上面,也就是node1作为主机(master),node2作为slave(从机或副机),机器名起有意义或能区分即可,推荐起master和slave,这里就不作过多的阐述,为了避免看文章有疑问,请看清单列表. ![a6d5cde535543d90b21a4a72080950a9.png][] #### 4.2、SpringBoot配置和代码详解 #### ##### 4.2.1工程的pom.xml配置 ##### * SpringBoot和[Docker依赖][Docker]的jar配置 <dependencies> <!-- Springboot依赖的Jar包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Springboot热部署jar--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!--yml配置文件提示插件--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- spring-boot测试jar --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>springboot</finalName> <!-- 一定要声明如下配置 打包xml 到Jar包 --> <!-- <resources> <resource> <directory>src/main/java</directory> 是否替换资源中的属性 <filtering>false</filtering> </resource> </resources> <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <!-- 默认支持jdk1.8编译 --> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <!--docke rmaven编译插件--> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.12</version> <configuration> <dockerDirectory>${project.basedir}</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.flong.SpringbootApplication</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> ##### 4.2.2 no main manifest attribute错误解决 ##### * 配置工程主入口 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.flong.SpringbootApplication</mainClass> </manifest> </archive> </configuration> </plugin> ##### 4.2.3 env环境变量文件 ##### * 用于设置环境动态参数,文件是以.env为格式 JAVA_OPTS_DEFAULT=-Xmx512m ##### 4.2.4 Dockerfile打包工程镜像细讲 ##### * 以开发环境的Dockerfile为例,如果是测试环境则,把所有路径包含springboot\_dev改成springboot\_test FROM frolvlad/alpine-oraclejdk8:slim MAINTAINER jilongliang@sina.com RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN mkdir -p /home/devsoft/springboot_dev WORKDIR /home/devsoft/springboot_dev EXPOSE 7011 ADD ./target/springboot.jar ./ CMD java ${JAVA_OPTS_DEFAULT} -Djava.security.egd=file:/dev/./urandom -jar springboot.jar * 参数说明 ![fad927ad4af141859584fbf43df716ad.png][] * WORKDIR 工作目录说明进入容器此时会有一个.jar是在Dockerfile的ADD添加进去 docker exec -it 容器名称或容器id /bin/sh 或要使用sh和bash要看COMMAND,-it docker exec -it 容器名称或容器id/bin/bash ![a04456838b97e2429bfb34564f7fbe9c.png][] ##### 4.2.5 build.sh文件shell脚本详解 ##### 注意点1:经过测试动态变量的【等号】不能有空格和tab键置位,否则获取不了值,而且在shell脚本代码里面不支持空格格式化,支持tab置位格式化。在终端(ssh软件端)或Jenkins客户端shell命令,『位置变量』的参数以空格隔开。 如:`sh build.sh 192.168.1.235 springboot 0.0.1 7011 /home/jenkins/workspace/springboot_dev` `$IMG_NAME:$IMG_VERSION这个IMG_VERSION`版本(tag)参数不指定默认latest 注意点2:通常情况下Docker是默认执行Dockerfile,但是可以自定义后缀文件进行编译,前提必须要-f(force)强制指定文件进行运行 #!/usr/bin/env bash # 动态变量的【等号】不能有空格和tab键置位,否则获取不了值,而且在shell脚本代码里面不支持空格格式化,支持tab置位格式化。 # 在终端(ssh软件端)或Jenkins客户端shell命令,参数以空格隔开。如:sh build.sh 192.168.1.235 springboot 0.0.1 7011 /home/jenkins/workspace/springboot_dev IMG_SERVER="$1" IMG_NAME="$2" IMG_VERSION="$3" IMG_PORT="$4" RUN_EVN="$5" IMG_PATH="$6" echo "服务地址:$IMG_SERVER" echo "工程镜像名称:$IMG_NAME" echo "工程版本号:$IMG_VERSION" echo "工程端口:$IMG_PORT" echo "服务环境:$RUN_EVN" #私服访问url路径和编译之后镜像文件存放到指定路径固定,不动态参数进行处理传值. REGISTRY_URL="192.168.1.235:5000" IMG_TAR_GZ_PATH="/home/img_tar_gz_path/" # 判断动态参数不为空字符串的时候才执行下面操作 if [ "$IMG_SERVER" != "" ] && [ "$IMG_NAME" != "" ] && [ "$IMG_VERSION" != "" ] && [ "$IMG_PORT" != "" ]; then echo " .......进入删除 Container & Images 操作 ......." # 清理虚悬镜像,释放磁盘空间 #docker images|grep none|awk '{print $3 }'|xargs docker rmi # 获取容器ID CONTAINER_ID=`docker ps -a | grep $IMG_NAME | awk '{ print $1 }'` # 获取镜像ID IMAGE_ID=`docker images | grep $IMG_NAME | awk '{ print $3 }'` # 判断是否存在删除开发容器 if [[ "$CONTAINER_ID" != "" ]]; then docker rm -f $CONTAINER_ID fi # 判断是否存在删除开发镜像 if [[ "$IMAGE_ID" != "" ]]; then docker rmi -f $IMAGE_ID fi # $IMG_NAME:$IMG_VERSION 这个IMG_VERSION版本(tag)参数不指定默认latest,通过不同参数执行不同环境文件 # -f 表示强制指定Dockerfile文件进行编译 echo " .......进入Building & Images 操作 ....... " #方法1、指定不同文件存放默认的Dockerfile,使用-f进行强制编译 #docker build -t $IMG_NAME:$IMG_VERSION -f $IMG_PATH"env/"$RUN_EVN/Dockerfile $IMG_PATH #方法2、跟据不同Dockerfile文件的后缀进行编译不同环境的文件 docker build -t $IMG_NAME:$IMG_VERSION -f $IMG_PATH"env/"Dockerfile_$RUN_EVN $IMG_PATH # 将镜像打一下标签,然后安照标签进行推送到私服里面,标签名就以服务名即可 docker tag $IMG_NAME:$IMG_VERSION $REGISTRY_URL/$IMG_NAME:$IMG_VERSION # 推镜像到私服里面 docker push $REGISTRY_URL/$IMG_NAME:$IMG_VERSION # 判断是否存在文件夹 if [ -d "$IMG_PATH" ];then echo "已经存在:"$IMG_PATH else mkdir -p $IMG_PATH fi # 保存编译之后镜像文件存放到指定路径 docker save $IMG_NAME -o $IMG_TAR_GZ_PATH/$IMG_NAME.tar.gz echo " .......进入Runing操作 ....." docker run -d --network default_network --restart=always --env-file=./.env -e spring.profiles.active=$RUN_EVN --expose=$IMG_PORT --name=$IMG_NAME -p $IMG_PORT:$IMG_PORT $IMG_NAME:$IMG_VERSION echo " .......Build & Run Finish Success~...." else echo " .......Illegal Command Operation ......." fi ##### 4.2.6 Docker (save、load、tag、push,pull)命令使用 ##### * 其中push,pull一个是推,一个是拉,在某种程度下,都是对在私服上面的镜像进行操作 * docker save命令是保存编译的tar.gz或tar压缩文件,语法如: docker save 镜像名 -o 路径/镜像名.tar.gz 或 docker save 镜像名 -o 路径/镜像名.tar docker load 命令是用于导入使用 `docker save` 命令导出的镜像,此命令非常重要,由于有些客户要求项目工程要求部署在内网,此时这个命令在无网络的内网情况下部署项目的时候就体现它重要的地位了.语法 `docker load [OPTIONS]`,在加载的过程有点慢,因为文件有点大,其中显示`Loady Layer [======]`输出信息,证实镜像是分层关系。 docker load -i /home/img_tar_gz_path/springboot.tar.gz ![27fe140084503343ba6bee37c0c1ccfe.png][] * 参数说明 ![21ada0a7b5adc3a38ce81cc299c72bf1.png][] * `docker tag`和`docker push`命令是一起结合使用,先tag后push,每个镜像名和版本是以冒号区分,而docker pull根据情况使用. # 将镜像打一下标签,然后安照标签进行推送到私服里面,标签名就以服务名即可 docker tag 镜像名:版本号 私服路径/镜像名:版本号 # 推镜像到私服里面 docker push私服路径/镜像名:版本号 * 查看镜像 ![e4215fc8f04d9334639740471f8f2844.png][] 浏览器验证docker push推送上私服的镜像 ![9ef368337f139b1e169a1d99cd158cc9.png][] ![7cf61cf0ec9a02187d0091ad77a452a1.png][] ##### 4.2.7 虚悬镜像 ##### * 在docker编译不成功会或者是新版本覆盖旧版本归类为虚悬镜像,生成这个个镜像既没有仓库名,也没有标签,均为 `<none>`。一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的。 ![b4ef91b019f5d902c8c9b55823b0e7c8.png][] ##### 4.2.8 不同环境的配置文件 ##### 配置参数 ![2b63de119a1ab9f816a9bc656da892fa.png][] 不同环境配置参数内容 server: port: 7011 runEvn: '开发环境' server: port: 7011 runEvn: '测试环境' ##### 4.2.9 Controller测试代码 ##### @RestController public class SimpleController { //读取配置动态参数 @Value("${runEvn}") private String runEvn; @GetMapping("/test") public String test() { return "this spring boot " + runEvn +" date long " + System.currentTimeMillis(); } } ## 5、非多台机器免密远程登录&Jenkins部署详解 ## #### 5.1 特别说明 #### * 以开发环境为例子进行说明 * 开发环境部署目标机器是与Jenkins机器同一台机器,一般情况,Jenkins是单独一台机器,这里为了节省自身电脑内存,故放在同一台机器进行演示与学习。 #### 5.2 新建maven工程 #### * 点击Jenkins的新建任务菜单 ![1b83ebef4c11aa6fe53b162653f3d3f8.png][] #### 5.3 参数化构建过程说明 #### ##### 5.3.1添加参数 ##### ![e3538c31198a27119068fb966e8c5e4d.png][] ##### 5.3.2 参数说明以开发环境为案例 ##### ![100c35b61fb58dcadf0bfa8752921f66.png][] ##### 5.3.3 源码管理 ##### ![622a71f1850ac9140ea87d620b995d4b.png][] ##### 5.3.4 Build编译设置 ##### ![43ce6efbff36bb99422cca7b9db0182c.png][] 表示忽略测试单元类进行编译 clean install -U -Dmaven.test.skip=true #### 5.4 SSH Publishers设置 #### * 其中SSH Server Name就是在`http://jenkins地址:端口/jenkins/configure`设置好进行选择 * Transfer Set Source file传输文件的路径,可以使用参数构建的占位符`${serverPath}`获取 * Remote directory远程文件目录,同理也参数构建的占位符`${serverPath}`获取 ![a945f15ec50c22bcf505149cbf9a625b.png][] * SSH Publishers shell脚本`#!/bin/bash`表示告诉终端使用bash解析器进行执行,而且只有第一行bash才有效。 #!/bin/bash # 创建目录 mkdir -p ${serverPath} # 切换目录 cd ${serverPath} # 运行脚本 sh build.sh $server ${appName} ${version} ${port} ${env} ${serverPath} #### 5.5 构建与编译部署项目 #### * 截图的构建参数都是在参数化构建过程配置的参数 ![a9d6de513dedb63191c67878866352b1.png][] #### 5.6 构建&运行&&部署结果 #### * 构建过程 ![94421ddd2d2da7845a0540c640eb5bab.png][] * 部署结果 ![6fb7f86836a6fc9022809c1ce06328e1.png][] ## 5、多台机器免密远程登录&Jenkins部署流程详解 ## #### 6.1 特别说明 #### * 以测试环境为例子进行说明 * 步骤流程几乎一样,唯一是在SSH Publishers 和源码存放路径不一样,测试环境部署目标机器是与Jenkins机器不同一台机器 #### 6.2 新建maven工程 #### * 点击Jenkins的新建任务菜单 ![980d09fef37940b7d99d90d7345b14a8.png][] #### 6.3 参数化构建过程说明 #### ##### 6.3.1添加参数 ##### ![89eab037b36238023292291c750e4772.png][] ##### 6.3.2 参数说明以测试环境为案例 ##### ![a7ad353445d08a1b83fb210b70688943.png][] ##### 6.3.3 源码管理 ##### ![0a2e55259ba2424ce18446ceded19a3b.png][] ##### 6.3.4 Build编译设置 ##### ![24f3529d100a9e389e060819eb415d05.png][] #### 6.4 SSH Publishers设置 #### 1. 其中SSH Server Name就是在`http://jenkins地址:端口/jenkins/configure`设置好进行选择 2. Transfer Set Source file传输文件的路径,可以使用参数构建的占位符`${serverPath}`获取 3. Remote directory远程文件目录,同理也参数构建的占位符`${serverPath}`获取 4. docker\_server1表示与Jenkins部署同一个宿主机,使用脚本有远程机器shell脚本操作免登陆操作。 [![e29aabecc776386243f9a35dba222afd.png][]][e29aabecc776386243f9a35dba222afd.png 1] * docker\_server1 Shell脚本`#!/bin/bash`表示告诉终端使用bash解析器进行执行,而且只有第一行bash才有效。 #!/bin/bash # 打印信息 echo "用户名${userName}" echo "服务器${server}" echo "服务器目录${serverPath}" # 远程创建存放远程上传的代码目录路径 ssh $server mkdir -p ${targetServerPath} # 远程拷贝代码到目标机器指定路径 scp -r ${serverPath}/ ${userName}@${server}:${targetServerPath} * docker\_server2表示要部署那台目标机器,所以它的脚本跟docker\_server1不一样. [![7618a8d36c87e34df820942d2a309cd2.png][]][e29aabecc776386243f9a35dba222afd.png 1] * docker\_server2 Shell脚本 #!/bin/bash # 切换文件目录 cd ${serverPath} # 执行脚本 sh build.sh ${server} ${appName} ${version} ${port} ${env} ${serverPath} #### 6.5 构建与编译部署项目 #### * 截图的构建参数都是在参数化构建过程配置的参数 [![5f88d7e61b358b122ef22cee976cf8fa.png][]][e29aabecc776386243f9a35dba222afd.png 1] #### 6.6 构建&运行&部署结果 #### * 构建过程 [![88aebd1e0181dc29a52f6690d9ca9ab3.png][]][e29aabecc776386243f9a35dba222afd.png 1] * 部署结果 ![00863833766102b58d28ed2065231af8.png][] ## 7、总结&建议&学习 ## #### 7.1总结与建议 #### 1、此文章仅供提供参考学习指引,如需要系统得学习可以根据自身找资料去学习。 2、以上问题都是根据个人实际学习过程中遇到的问题进行一个一个问题进行梳理与总结整理,除了技术问题查很多网上资料通过进行学习之后整理与分享。 3、在学习过程中也遇到很多困难和疑点,如有问题或误点,望各位老司机多多指出或者提出建议。本人会采纳各种好建议和正确方式不断完善现况,人在成长过程中的需要优质的养料。 4、当遇到问题的时候建议多问『谷歌 、必应、stackoverflow、度娘』这些大神。 5、建议看官方手册更权威,由于随着技术的发展与迭代,通常官方的文档更新较快,国内的网站资料更新较慢。 6、计算机是一门『做中学』的学科,不是会了再去做,而是做了才会。多练,常言道熟能生巧。 7、建议学什么技术『先Know how,再Know Why』,意思就说先入门,搞一个HelloWorld,再深究的意思。 8、希望此文章能帮助你更好了解什么(DevOps)是自动化构建镜像与部署,如何在Docker+Jenkins+GitLab+Maven+SpringBoot&SpringCloud自动构建镜像与部署服务应用,整个学习流程与搭建会有点小曲折,并不会那么顺利,也希望你看了此文章或者通过找资料进行亲身经历学习效果会更好。另外,关注Java知音公众号,回复“后端面试”,送你一份面试题宝典! #### 7.2 学习&参考文章 #### * https://docs.docker.com/engine/reference/commandline/docker/ * https://yeasy.gitbooks.io/docker\_practice/ * https://github.com/spotify/docker-maven-plugin * https://dmp.fabric8.io/ * https://github.com/jilongliang/springboot * https://www.cnblogs.com/kakaln/p/7872873.html * https://www.cnblogs.com/lucoo/p/10209892.html ## 附录 ## * www.jianshu.com/p/dfaaedeb6817 * www.jianshu.com/p/299eeef785f0 * www.jianshu.com/p/a22577232977 * www.jianshu.com/p/e4946edce9a4 * www.jianshu.com/p/0bc9b4755082 * www.jianshu.com/p/9784d63eff23 * www.jianshu.com/p/7827e40aaa71 * www.jianshu.com/p/72d05e43a8f3 推荐好文 [强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!][10k_ SpringBoot] [分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!][SpringBoot_Vue] [能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮][SpringBoot]![d75059188d16ed510145da2c7ff5b5de.png][] [586a36b0786cf3dcf68c3f2546704faa.png]: /images/20220829/9e689bdc5bac486b866053bd378bb1fc.png [635cc0bc01cb58a7bd8a7a0d47bf1ee7.png]: /images/20220829/2fa03c5b00ab4fb38d8d32807d8a2c72.png [c49a00ebc2eb15585179c192fc6ffe32.png]: https://img-blog.csdnimg.cn/img_convert/c49a00ebc2eb15585179c192fc6ffe32.png [d7924d9362829d8ba8175c9e58052042.png]: /images/20220829/8e6550a7a704411f8789c69431f9fd18.png [a6d5cde535543d90b21a4a72080950a9.png]: /images/20220829/7a2135f824fc4daea5fe18040af4c7b8.png [Docker]: http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ%3D%3D&chksm=ebd62205dca1ab1395d51c0b80005bfaa9933c66098ba487a3adee2a40d4f8c6a0abce65beee&idx=2&mid=2247490857&scene=21&sn=3208dcfff7904a3c11d5950e39b0fc2f#wechat_redirect [fad927ad4af141859584fbf43df716ad.png]: /images/20220829/a016dee3432742e6b9d8b1f10c1fcb7d.png [a04456838b97e2429bfb34564f7fbe9c.png]: /images/20220829/3cbad53a5cd74b4db3de807f8cbb3894.png [27fe140084503343ba6bee37c0c1ccfe.png]: /images/20220829/7bd9b127076c4bbdaee7288975e2f054.png [21ada0a7b5adc3a38ce81cc299c72bf1.png]: /images/20220829/56626dc064544274812f7ba4f0224632.png [e4215fc8f04d9334639740471f8f2844.png]: /images/20220829/7928fcd572d041819576290f0480efe2.png [9ef368337f139b1e169a1d99cd158cc9.png]: https://img-blog.csdnimg.cn/img_convert/9ef368337f139b1e169a1d99cd158cc9.png [7cf61cf0ec9a02187d0091ad77a452a1.png]: /images/20220829/6cb4d47ce4194a488f77bb9b4ad0f3ec.png [b4ef91b019f5d902c8c9b55823b0e7c8.png]: /images/20220829/ad346920b18e43debdd8e48831541f12.png [2b63de119a1ab9f816a9bc656da892fa.png]: /images/20220829/a1066aa795bb473b8c97c989806301e0.png [1b83ebef4c11aa6fe53b162653f3d3f8.png]: /images/20220829/906efb7d837c40188c0a05366d28d726.png [e3538c31198a27119068fb966e8c5e4d.png]: /images/20220829/bfad253200d7480585413a96431c0527.png [100c35b61fb58dcadf0bfa8752921f66.png]: /images/20220829/64e51647589e4657b5df2829c1acac9a.png [622a71f1850ac9140ea87d620b995d4b.png]: /images/20220829/b569ca0a0f5a4f4f954d581ae293b14e.png [43ce6efbff36bb99422cca7b9db0182c.png]: /images/20220829/7a3953c94e5e402da2cf22d711510dc4.png [a945f15ec50c22bcf505149cbf9a625b.png]: /images/20220829/386e3e176e9a4ecd99b4926632dd6b5c.png [a9d6de513dedb63191c67878866352b1.png]: /images/20220829/2fea5323183d4e3b9b27d73a80576a9b.png [94421ddd2d2da7845a0540c640eb5bab.png]: /images/20220829/4c85dfeeb17e48119a28b7b7583031ac.png [6fb7f86836a6fc9022809c1ce06328e1.png]: /images/20220829/585b04db8c5d46968a858008908234dd.png [980d09fef37940b7d99d90d7345b14a8.png]: /images/20220829/87e325f9067f4307b8bcb92dacfa96d0.png [89eab037b36238023292291c750e4772.png]: /images/20220829/279631dafd454721aea02e1766d2d041.png [a7ad353445d08a1b83fb210b70688943.png]: /images/20220829/46739d5bf6a945d3951fffd5d4abb344.png [0a2e55259ba2424ce18446ceded19a3b.png]: /images/20220829/22c71fc9caf24f5f837e12aec4de25d0.png [24f3529d100a9e389e060819eb415d05.png]: /images/20220829/01ebc1399d2747ecab35a201c8ddfb65.png [e29aabecc776386243f9a35dba222afd.png]: /images/20220829/5acd6ec6ac134ae094b42685efb81599.png [e29aabecc776386243f9a35dba222afd.png 1]: http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ%3D%3D&chksm=ebd5ed45dca26453c0cf91265d669711a4e2b4ea52f3ff4a00b063a9de46d69fcc599f151210&idx=1&mid=2247504489&scene=21&sn=afd92248113146b086652ad7f89c7a7c#wechat_redirect [7618a8d36c87e34df820942d2a309cd2.png]: /images/20220829/f905adfebe5144ef8f8c12f78716488a.png [5f88d7e61b358b122ef22cee976cf8fa.png]: /images/20220829/f30a1c8e1adc48a48d2bcf27780620e0.png [88aebd1e0181dc29a52f6690d9ca9ab3.png]: /images/20220829/d6010f42dcc54944aeb6c0624796ea3d.png [00863833766102b58d28ed2065231af8.png]: /images/20220829/c6310d7b91404e3f894cfc48f6ce7c39.png [10k_ SpringBoot]: http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ%3D%3D&chksm=ebd5c411dca24d07446a4f22772cd12d2cc1143b62eb922a14500ae981cd57fb850ef74ca257&idx=2&mid=2247498557&scene=21&sn=84902d7030e174e152615d8fae3755cc#wechat_redirect [SpringBoot_Vue]: http://mp.weixin.qq.com/s?__biz=MzU2MTI4MjI0MQ%3D%3D&chksm=fc799be6cb0e12f0b58479f8b031bc5ae79efdca1880ff9b19f0a107c3a88c37b9596dc73203&idx=2&mid=2247497288&scene=21&sn=65d7111a19e920df1e9416959594b2f4#wechat_redirect [SpringBoot]: http://mp.weixin.qq.com/s?__biz=MzU2MTI4MjI0MQ%3D%3D&chksm=fc799a9ccb0e138a64a2671f31b8d4be1b3c061a288f7b0d1d4af02a60c45a72231446b91e00&idx=2&mid=2247497522&scene=21&sn=c5b4e596be4ac7d7f293e7e2f0ea0769#wechat_redirect [d75059188d16ed510145da2c7ff5b5de.png]: /images/20220829/b154b0f6af2f4a6a80278bdde986a143.png
还没有评论,来说两句吧...