故事梗概
Java程序员马意浓在互联网公司维护老旧电商后台系统。
渴望学习新技术的他在工作中无缘Docker和K8s。
他开始自学Vue3并使用SpringBoot3完成了一个前后端分离的Web应用系统,并打算将其用Docker容器化后用K8s上云。
6 夺取宝剑
🔥阅读Nigel Poulton的书,自学完Docker后,马意浓知道,前后端App,各自都要制作成docker image,先部署到本地docker compose里,之后再部署到k8s云集群里。
✅这是因为Docker提供了一个完整打包app所有依赖项的一致的环境,确保应用在开发、测试和生产环境中的行为一致。
这就能实现在测试环境中所测试的image,就是在生产环境所部署的。
这是解决“在我这运行得好好的,怎么在你那不行”的问题的关键。
✅此外,将前端和后端应用容器化,需要使用等同于代码的配置文件。
这种基础设施即代码的实践,能让配置的更改广而告之,配置的执行有据可查。
这是解决“谁改了配置又不告诉大家?”问题的关键。如图1。
图1 Docker能提供完整打包app所有依赖项的一致的环境,以及让配置的更改广而告之,配置的执行有据可查
6.1 前后端App均部署到本地docker compose中的架构图
马意浓稍微修改了之前绘制的前后端App部署到本地Gradle/npm开发环境的架构图。
其实只是将前后端App的部署环境,都改成了docker compose。如图2。
图2 前后端App的部署环境都改成了docker compose
该如何让前后端App,都在本地docker compose里运行起来呢?
马意浓启动了Docker Desktop,并确认已经用自己的docker hub账号登录。
6.2 用gradle构建后端app并生成jar包
马意浓想要让所生成的后端app的docker image,仅包含刚好够运行的jar包,及其依赖项。
这样的docker image就能做到尽可能地小,以节省将其推送到Docker Hub的时间。
他于是决定,先用gradle命令构建后端app并生成jar包,然后再设法将其构建为docker image。
他打开一个Ubuntu终端窗口,进入项目文件夹中的infrastructure子文件夹。
接着运行命令docker compose up postgres pgadmin
,启动了postgres数据库和pgadmin管理工具,为gradle构建做好了准备。
他又新打开一个Ubuntu终端窗口,进入项目文件夹中的back-end子文件夹。
他运行命令sdk use java 17.0.10-tem
,设置好jdk当前版本。
之后,他运行命令./gradlew clean build
,开始构建后端app。
等到屏幕出现了BUILD SUCCESSFUL
字样后,他在文件夹build\\libs
里,找到了刚刚生成的jar包shoppinglist-0.0.1-SNAPSHOT.jar
。
6.3 构建后端app的docker image
马意浓又回到了back-end文件夹。因为他知道,这个文件夹里有运行docker
命令所需的Dockerfile文件。
他打算使用docker buildx build
命令,因为这是对旧的docker build
命令的扩展。
他从资料中发现,相比后者,前者提供了多平台构建、改进的构建缓存机制以及灵活的构建结果输出等新功能。
他输入命令docker buildx build --build-arg JAR_FILE=build/libs/shoppinglist-0.0.1-SNAPSHOT.jar -t <docker-hub-username>/shopping-list-api:v1.1.local-docker-compose .
,来构建后端docker image。
(注:需要把<docker-hub-username>
换成你的docker hub账号名。下同。)
他特别留心命令最后的一个不起眼的小数点没有忘记写。
因为他知道,小数点代表把当前文件夹。
这作为build命令的上下文,以找到所需的jar文件,作为构建资源。
在执行这个命令前,他又仔细检查了参数-t <docker-hub-username>/shopping-list-api:v1.1.docker-compose
。
他知道,-t
指的是,给image加一个tag,用于标识这个image。
如果以冒号:
分界,这个参数的前半段<docker-hub-username>/shopping-list-api
,是这个image将来要推送到的Docker Hub上的镜像库(repository)的名称。
他觉得把后端app的docker image命名为shopping-list-api
,会更加简洁。
而参数的后半段v1.1.local-docker-compose
,是这个image的tag,用来标识image的这次构建的版本号。
他按下回车,执行这个命令。
等这个命令执行完,他运行命令docker image ls
,验证后端app的docker image是否构建成功。
屏幕显示出新构建的image,确实带有<docker-hub-username>/shopping-list-api
的repository,以及v1.1.local-docker-compose
的tag。这表明image构建成功了。
他在Docker Desktop的Images里,也看到了这个新构建的后端app的docker image。如图3。
图3 他在Docker Desktop的Images里,也看到了shopping-list-api
这个新构建的后端app的docker image
6.4 在git代码库打同名的tag以对应刚刚构建的docker image版本
马意浓紧接着,运行命令git tag -a v1.1.local-docker-compose -m "v1.1.local-docker-compose"
,在git库里打了一个与docker image的tag同名的tag。
他知道,随着不断提交,代码库中的代码总是在不断变化。
总有一天,他推送到Docker Hub中的image会有bug。那时若想打开对应的源代码看一下,那该看git代码库中哪一次提交后的代码?
但如果他当时在构建docker image后,立即在git库里打一个同名的tag,那么就好定位代码了。
他又运行命令git push origin v1.1.local-docker-compose
,把这个tag推送到远程git库中。
他接着运行命令git ls-remote --tags origin
,确认这个tag已经成功推送到远程git库中了。
6.5 将后端app的docker image推送到docker hub
因为已经在Docker Desktop里登录Docker Hub了,所以马意浓直接运行命令docker push <docker-hub-username>/shopping-list-api:v1.1.local-docker-compose
,将构建好的image推送到Docker hub。
等命令执行完,没有看到错误信息,他就在浏览器访问自己的Docker hub页面。
他在自己的shopping-list-api
这个repository里,找到了tag是v1.1.local-docker-compose
的image。这表明image推送成功。
下一步,他要构建前端app到docker image,并将其推送到docker hub。
6.6 用Dockerfile里的npm构建前端app的docker image
马意浓知道,构建前端app的docker image的过程,与后端app有些不同。
不同点就是前端app的构建命令npm run build
,已经纳入前端app的Dockerfile文件里了。而不像后端app那样,构建命令在Dockerfile之外。
他运行命令进入前端文件夹front-end。
他又运行命令docker buildx build -t <docker-hub-username>/shopping-list-front-end:v1.1.local-docker-compose .
,来构建前端app的docker image。
等命令执行完,他运行命令docker image ls
,查看新构建的image,确实带有<docker-hub-username>/shopping-list-front-end
的repository,以及v1.1.local-docker-compose
的tag。
这表明image构建成功了。
他在Docker Desktop里的Images里,也看到了这个新构建的前端app的docker image。
按理说,构建完前端app后,也应该在git代码库打同名的tag,以对应刚刚构建的docker image版本。
但他觉得从现在到上次在git库打tag这段时间,没有改代码,而且tag的名称也没变,所以他决定省略这一步。
6.7 将前端app的docker image推送到docker hub
马意浓运行命令docker push <docker-hub-username>/shopping-list-front-end:v1.1.local-docker-compose
,将构建好的image推送到Docker hub。
他访问自己的Docker hub,在自己的shopping-list-front-end
这个repository里,找到了tag是v1.1.local-docker-compose
的image。这表明image推送成功。
6.8 在本地docker compose里运行前后端App
做足了前面的功课,马意浓知道,现在该是享受胜利果实的时候了。
因为在本地docker compose里运行前后端app的命令,超级简单。
他进入项目文件夹,然后运行命令cd infrastructure
,进入这个子文件夹,
他打开docker-compose.yml
文件,仔细确认了shopping-list-api
和shopping-list-front-end
的image
中配置的v1.1.local-docker-compose
这样的tag,确实与之前的命令中的tag一致。
他又检查了一下docker desktop的Containers界面,发现之前为后端app打包而准备的postgres和pgadmin容器还在运行,于是他运行清理现场的命令docker compose down
,将两个容器关闭并删除。
之后他运行命令docker compose up
,来启动docker-compose.yml
文件中配置好的那4个容器。
他特意在这个命令中,没有添加表示detach
的-d
参数,以便观察命令执行中的输出信息是否有错误。
屏幕输出没有任何错误信息,而且最后显示Started ShoppingListApplication
字样。这表示前后端App已经成功运行。
他在docker desktop里,能看到这4个容器都在正常运行。
他于是在里面用鼠标点击shopping-list-front-end-1
那一行右侧的8080:8080
链接,浏览器应声打开,显示了购物清单Web应用的界面。
🔥他在里面添加了一个购物项an apple。这个购物项便顺利地出现在下面的购物清单中!
他兴奋地挥拳庆贺。
至此,前后端分离的Shopping List Web App在本地docker compose里运行成功了。
6.9 清理现场
他又新打开一个Ubuntu终端窗口,进入项目文件夹中的infrastructure子文件夹,然后运行命令docker compose down
。
他打开docker desktop,看到Containers界面已经没有任何容器在运行了。现场清理结束。
😃马意浓很兴奋。现在前后端分离的Web应用的容器化这把宝剑,他已经拿到手里了。
接下来,他要挥剑,向k8s上云的新目标迈进。
💔但他心里隐隐有些不安。
因为对他来说,K8s云集群是个完全陌生的环境。他预感会碰到更大的障碍。
🔥【未完待续】
❤️欲读系列故事的全集内容,可搜用户“程序员吾真本”,找到“2024程序员容器化上云之旅”专栏阅读。
🔥后面连载内容大纲先睹为快:
🔥7 上云之路
7.1 注册Azure k8s service云平台账号
7.2 打开docker desktop kubernetes让kubectl能正常工作
🔥8 复活重生
8.1 在k8s云集群中运行shopping list web app时如何配置前端app在k8s云集群中的对外域名和端口号以解决CORS问题
8.2 在全绽园的帮助下为前端app配置ingress后解决了这个问题
8.3 在k8s云集群中的软件架构
8.4 如何新增k8s的deployment、service和ingress的配置文件,以便使用kubectl命令将ingress和postgres、shopping-list-api和shopping-list-front-end这3个微服务部署到k8s上
8.5 构建后端docker image并推送到docker hub
8.6 构建前端docker image并推送到docker hub
8.7 在k8s云集群上配置postgres、shopping-list-api和shopping-list-front-end三个微服务和ingress并运行
8.8 清理现场
🔥9 取经归来
当最终把前后端分离的web应用成功部署到azure k8s云集群上,并能顺利使用后,马意浓把整个容器化和上云之旅,写成系列文章,分享给其他程序员。
😃你能否跟着马意浓一步步做下来?在阅读中有任何疑问,欢迎在留言区留言。我会一一回复。
❤️如果喜欢本文,那么点赞和留言,并转发给身边有需要的朋友,就是对我的最大支持😃🤝🙏。