—— 如果说Java,NodeJS这些跨操作系统的语言帮助我们在开发阶段屏蔽了操作系统,那Docker这样的容器化技术则在部署与运营层面,进一步隔离了操作系统的影响。 考虑容器化技术是非常必要的方式之一。
在持续交付的过程中,我喜欢将服务容器化,包括后端,前端都是。
当然移动端肯定就不在能容器化之列了,这个是额外要考虑的。
本篇,继续从零到一,构建你的持续交付流程,这是第四篇,本系列其它文章是:
- 从零到一,构建你的持续交付流程(一):一个持续交付流程的构思
- 从零到一,构建你的持续交付流程(二):好的工程实践是必要的前提
- 从零到一,构建你的持续交付流程(三):搭建基于Jenkins+Docker的持续交付环境
一)
提醒
持续交付的形态应该与最终项目或产品的部署形态二进制保持一致。如果你最终在生产上的部署形态是rpm包,那持续交付也应该是rpm包。不保持一致的持续交付,则失去了它本来应有的价值。
我更喜欢用Docker将服务容器化,我觉得这样在服务管理上比较方便。
明确点说就是:
- 单个服务时,你可以用单个镜像来管理
- 多个服务时,在单个机器上部署可以考虑docker compose (开发或测试环境)
- 多个服务多个机器部署,则可以考虑docker swam或更重量级的k8s。
而且,对持续集成来说,管理docker服务非常方便,重启,停止或更新
#启动XX服务
docker start XXService
#关闭XX服务
docker stop XXService
如上,服务的管理都是比较方便的。
当然,这并不意味着只能用Docker,如果你是Jar或War,或RPM也好,DEB也好,Systemctl服务也行,都可以。但是切记,持续交付的二进制产物必须与实际生产保持一致,否则价值就大大降低了。
二)
接下来,我将分别简单就三种模式进行阐述:
- 将基于JDK的后端服务容器化管理
- 将基于npm的前端服务容器化管理
- 将一个后端服务+一个前端服务合并起来进行容器化管理
我需要再解释下,这个系列并不是技术教程,不会就涉及到的Docker知识一步一步的解释与说明。后续我会做Docker的专题系列。本系列的目标着重在让大家知道一个持续交付是如何构建起来的。这个过程中涉及到的技术仅做简单解释。
为了便于演示,整个过程,我会用我的myddd starter生成一个后端项目及一个前端项目来配合说明。访问 https://starter.myddd.org 来快速生成项目。

如图所示,请使用myddd starter来快速生成项目,以配合后续的操作。
三)
请在myddd starter中,随便生成一个myddd-spring-boot的项目就好。生成的项目是基于gradle的,带了一些示例代码,也带了单元测试。非常方便我们接下来的演示。
之所以使用myddd-spring-boot,是因为这是Java+Spring Boot,大家熟悉,而且默认数据库是H2内存数据库,方便我们操作。而myddd-vertx则是kotlin+vertx结合,大多数人可能并不熟悉。
#如果需要本地编译,请安装gradle,我当前用的是gradle 7.2
gradle build
尝试gradle build,你可以成功的构建它。
生成与编译项目
比如,我生成了一个名称为test-backend的myddd-spring-boot项目,构建完后,可以在test-backend-bootstrap的build的libs目录下,找到最终运行的Jar。

你可以尝试在本机,使用java来运行
java -jar test-backend-bootstrap-1.0.0-SNAPSHOT.jar
这是一个基于Spring Boot的最终的fat jar。只要你的JDK不低于11,它就能正常运行。
当然,我们的目的不是这样,我们是希望以docker的容器化的模式来管理这个服务。
构建镜像
在项目的根目录下,新建名称为Dockerfile的文件。
1 # syntax=docker/dockerfile:1
2
3 #构建镜像
4 FROM openjdk:11.0.12-jre-slim
5 WORKDIR /app
6 COPY ./test-backend-bootstrap/build/libs/test-backend-bootstrap-1.0.0-SNAPSHOT.jar .
7 CMD ["java","-jar","/app/test-backend-bootstrap-1.0.0-SNAPSHOT.jar"]
在这里中,我仅仅简单解释下
- 行1,申明你用的哪个dockerfile的语法。最新的就是这个了。
- 行4,这个是指我们以OpenJDK 11为基础镜像,因为我们这是一个基于JDK11的Java应用。
- 行5,这个是指在构建过程中,容器中进入的默认目录就是app,后面文件复制啊什么,就会以这个目录为标准来定位文件
- 行6,这个,就是将我们先前生成的jar包,复制到”.“(这有个点,别忽略了),也就是/app这个目录
- 行7,最后,容器启动时,我们执行这个命令,这个相当于“java -jar /app/test-backend-bootstrap-1.0.0-SNAPSHOT.jar”
在项目当前目录下,执行以下构建镜像的命令
docker build -t test-backend:1 .
我们构建一个名为“test-backend”的镜像,tag为1。
第一次可能会下载一些基础镜像,时间会有点久。
然后,运行docker images来查看是否在本地生成成功。
REPOSITORY TAG IMAGE ID CREATED SIZE
test-backend 1 9455edc604ee 4 minutes ago 274MB
如上所示,出现这么个东西,表示你的镜像就生成成功啦 。
运行镜像
docker run --name=test-backend -d -p 8080:8080 test-backend:1
#如果你想查看日志
docker logs -f test-backend
如无意外,你能看到服务启动成功了。
是不是挺简单的?
四)
好,我们再来搞个前端的项目。
我们要学会举一反三,前端也好,后端也好,构建docker镜像服务的原理是一模一样的。
生成前端项目
好,再次访问 https://starter.myddd.org , 生成一个myddd-web项目,这是一个基于typescript+react的项目。
举例说明,你生成了一个名称为test-front的项目。
生成完成后,执行以下命令
# 编译项目
npm install
# 以开发的模式运行它
npm run serve
访问https://127.0.0.1:3000, 你能见到这样的一个默认首页

如果需要编译生成包
npm run buildProd
成功后,dist目录下的文件就是最终的静态文件了,一般情况下,我们会把这上放到nginx上去。
制作镜像
同样,我们的目的还是要把这个也做成容器服务。
在项目目录下,创建一个Dockerfile的文件
1 # syntax=docker/dockerfile:1
2
3 FROM nginx:alpine
4 COPY ./dist /usr/share/nginx/html
好吧,这个就更简单了。
- 行3,这个服务是基于nginx
- 行4,我们把dist下的文件都复制到了nginx的默认主目录,也就是/usr/share/nginx/html
下一步,执行以下命令
docker build -t test-front:1 .
同理,我们用docker images来查看
REPOSITORY TAG IMAGE ID CREATED SIZE
test-front 1 813afab9fac1 33 seconds ago 23.5MB
test-backend 1 9455edc604ee 29 minutes ago 274MB
运行镜像
docker run --name=test-front -d -p 80:80 test-front:1
#如果要查看日志
docker logs -f test-front
同理后,访问https://127.0.0.1, 你可以见到刚刚在开发模式下一样的页面了。
五)
是不是挺简单的?
我们已经把后端,前端都做成了镜像服务,并且都能基于docker来方便的管理这些服务。
但是通常一个项目或产品,有前端,后端,数据库,可能还有缓存redis等,如果一个个来管理,非常麻烦。
在单台机器上,我们可以利用docker compose来集中式的管理服务,这个就非常方便了。
docker compose非常适合开发或测试环境,在单台机器上进行部署与管理服务。(如果是生产,则不适合了,生产你需要考虑docker swam或更复杂的k8s等技术了)
创建docker compse文件
先在合适的地方,创建一个test-compose目录(名称无所谓)
在test-compose目录下,新建docker-compose.yml文件。
文件内容如下:
1 version: "3.9"
2
3 services:
4 test-backend:
5 image: test-backend:1
6 container_name: test-backend
7 ports:
8 - "8080:8080"
9 test-front:
10 image: test-front:1
11 container_name: test-front
12 ports:
13 - "80:80"
14 depends_on:
15 - test-backend
简单解释下:
- 行1: docker compose语法版本,最新为3.9
- 行3: 定义服务集,我们这个示例中包括一个后端test-backend以及一个前端test-front服务
- 行4: 定义其中一个服务,名称为test-backend,镜像为test-backend:1,容器名称为 test-backend,开放的端口为8080
- 行9-15: 定义另一个服务test-front
具体请参阅docker compose文档。
服务集的的启动与停止
基于docker compose来管理多个服务,它们的启动与停止就非常方便
#启动服务,-d表示运行在后台
docker-compose up -d
#停止服务
docker-compose stop
就是这么简单。
docker compose非常适合你的开发环境,或者你的测试环境。
提醒
在实际中,测试环境尽量应该是生产环境的mini版本,包括部署方式。因为生产大多不可能是docker compose来部署的,所以测试环境最好也不要使用docker compose。
六)
如上所说,将我们的服务,包括后端+前端用docker来管理,其实原理上非常简单,并不复杂。
虽然这个示例极其简单,但复杂的东西无非是在简单的东西之上再添加。只要你能理解这个过程,后面添加新的复杂的东西,就更轻易能理解了。
好了,我们已经学会怎么让我们的服务容器化了。
上面的过程我们都是手动在操作。这当然不是我们的目的。
上面这些过程,我们完全可以让Jenkins Pipeline来帮我们自动化的执行。这样就并不需要人工反复的执行这些东西了。
下一篇:从零到一,构建你的持续交付流程(四):使用Jenkins Pipeline,让一切自动化与流程化
附):
本篇文章中所涉及到的所有用myddd starter生成的项目,已放在github上,你可以访问 https://starter.myddd.org , 来生成这些项目,也可以使用github上现成的示例。
https://github.com/lingen/continuous_delivery_example.git
同时,这个地址我们在下一篇文章中使用Jenkins会用到
备注: myddd.org是我个人维护的基于DDD理念的全栈式领域驱动开源基础框架,官网是https://myddd.org