故事梗概
Java程序员马意浓在互联网公司维护老旧电商后台系统。
渴望学习新技术的他在工作中无缘Docker和K8s。
他开始自学Vue3并使用SpringBoot3完成了一个前后端分离的Web应用系统,并打算将其用Docker容器化后用K8s上云。
3 挑选工具
马意浓画好架构图后,就开始为这次技能升级之旅挑选工具。
他有一台旧Windows 10笔记本电脑。i5的4核CPU。
原本的内存是8GB,后来在维修中心将其扩展到了20GB。
但跑起程序来,还是感觉慢。
他于是设法搞到了一台新一点的Windows 11迷你主机。i7的14核CPU。内存32GB。
新机器使用起来感觉流畅多了。如图1。
Windows11主机选好后,就需要在上面安装git、jdk、node.js和npm这些工具。
❌比较直接的安装方法,就是分别去这些工具的官网,下载最新的安装包,进行安装。
但马意浓知道,这种方法,只能爽一时。
当遇到一些老旧项目,需要在同一工具新旧多个版本间切换时,就比较麻烦。
比如jdk从2017年之后,版本升级就从过去的几年一次,变成半年一次。
马意浓在工作中所维护的老旧系统,前一阵IT部门好不容易从jdk6升级到jdk8。
他之前那台Windows 10的笔记本电脑,就是从Oracle官网上找到jdk8的下载页面,下载安装包,然后解压并安装的。
既然要技能升级,jdk就要使用2024年主流版本17。
如果还是使用老办法,那就得再从官网下载并安装jdk17。
然后手工在Settings
的Environment Variables
里修改JAVA_HOME
和PATH
环境变量,以便从jdk11切换到jdk17。
要是想再回到老旧项目的jdk8,又得做一通手工环境变量修改。这太麻烦了。
该如何应对上面的场景?马意浓决定先问一下AIGC。
✅AIGC回答:【可以使用包管理器来解决开发工具版本切换的问题。】
【在Windows 11操作系统中,可以用winget来安装不同版本的git。】
【用jabba来安装不同版本的jdk。】
【用nvm for Windows来安装不同版本的node.js和npm】
马意浓知道,编程离不开命令行工具。
他打开Windows 11默认自带的命令行工具PowerShell,并在里面敲入命令$PSVersionTable.PSVersion
。
屏幕显示PowerShell的版本是5.1。
他去网上搜了一下,得知现在的PowerShell的最新版本是7.4.1。
他想,既然PowerShell是微软制作的,那么在Windows 11的Microsoft Store里应该能找到最新版本。
3.1 挑好PowerShell
✅果然,他在Microsoft Store里成功安装了7.4.1版的PowerShell。
他打算用winget来安装最新版的git。
他在PowerShell里敲入命令winget -v
,检查winget的版本号。
屏幕显示winget的版本号是v1.6.3482。
他上网查了一下,winget的稳定版是v1.6.2771。看来电脑中的winget还是比较新的。
3.2 挑好git
✅他又在网上搜到了如何用winget安装git。于是他敲入了命令winget install -e --id Git.Git
。
结果屏幕显示系统中已经有git了,并且版本已经是最新的了。看来Windows 11已经自带git了。
他运行命令git -v
,检查git的版本:
屏幕显示git的版本是2.43.0.windows.1。
他上网查了一下最新的git版本,确实是2.43.0。
3.3 克隆代码
✅有了git,马意浓于是进入到一个保存源代码的文件夹,运行git clone
命令。
他把之前写好的前后端分离的Shopping List Web App源代码,从github的wubin28账号下的名为shopping-list-web-app-2024-for-windows
的代码库,克隆到本地电脑。
(因为众所周知的原因,本系列文章不会包含外部恋街。如你需要可以在留言区留言。)
克隆好代码后,马意浓在PowerShell里,进入项目文件夹shoppling-list-web-app-2024-for-windows。
3.4 git命令别名
✅马意浓进入项目文件夹后,首先在自己账号的home文件夹中,创建.gitconfig
文件,设置了git命令的别名。
这样一来,像git status
这样经常使用且很长的命令,就可以短别名git st
来代替。
使用git的体验一下子好了许多。
3.5 命令行提示显示分支名
马意浓还想让PowerShell命令行提示符,显示目前所check out的git分支名称,以免搞错分支。
✅他上网查了一下,发现安装posh-git工具,就能解决这个问题。
很快,他就安装好了posh-git。命令行提示后面出现了令人安心的当前分支名。
3.6 浏览源文件
✅马意浓运行文件查看命令,简单回顾了这个项目文件夹中3个子文件夹中的文件。
back-end
文件夹存放了后端代码、后端Dockerfile
和其他配置文件。
front-end
文件夹存放了前端代码、前端Dockerfile
和其他配置文件。
通过读Docker书,马意浓了解到,Dockerfile
是一种配置文件,用于把源代码构建为docker image,以便以容器化的方式进行部署。
infrastructure
文件夹存放了运行docker compose和k8s的配置文件docker-compose.yml
。
3.7 用jabba安装jdk 17以便在本地进行后端app构建
马意浓现在需要用jdk构建后端app了。
✅他在github的shyiko的账号下,找到了jabba的安装方法。
安装好后,他运行命令jabba --version
验证一下。
屏幕显示jabba的版本是0.11.2。
他又运行命令jabba ls-remote
,查看jabba可供安装的jdk版本:
✅他运行命令jabba install openjdk@1.17.0
,安装open jdk 17。
屏幕显示正在下载。由于网速较慢,马意浓着实等了一会儿。
等安装完毕后,他运行命令jabba ls
,查看jabba所安装的jdk版本。
屏幕显示已经安装了openjdk@1.17.0。
马意浓又运行命令jabba use openjdk@1.17.0
,将刚刚安装的jdk17设置为当前正在使用的jdk版本。
然后他运行命令jabba current
,验证当前正在使用的jdk确实是版本17。
最后,他运行命令java -version
,以验证jdk17是否正常工作。
屏幕显示jdk版本是openjdk version "17" 2021-09-14。
3.8 用nvm for windows安装node.js和npm以便在本地进行前端app构建
马意浓通过上网搜索了解到,nvm是Node Version Manager的简称。
它是管理多个 Node.js 版本的工具。但最初的 nvm 仅可用于 Mac和Linux,没有Windows版本。
虽然在Windows上可以使用类似的工具,如nvmw或nodist,但它们的架构都依赖于 .bat 文件。
这些.bat文件虽然使用一些技巧来设置或模拟环境变量,但很容易出现问题。
✅马意浓决定使用后来以go语言新开发的nvm for windows,来安装node.js和npm。
他知道这个新工具虽然叫nvm for Windows,但完全不同于最初的nvm。
他在github的coreybutler账号下,找到了nvm-windows的安装方法。
安装完后,他重新打开一个powershell,运行命令nvm version
,验证nvm的版本。
屏幕显示nvm for windows的版本是1.1.12。
✅接着,马意浓运行命令nvm install lts
,安装node.js和npm。
屏幕显示,node.js v20.11.0正在下载。很快,屏幕显示npm v10.2.4已经安装成功。
马意浓根据屏幕提示,运行命令nvm use 20.11.0
,以使用刚刚安装好的node.js和npm。
接着,他又运行命令node -v
,以验证前端工具node.js的版本。
屏幕显示node.js版本是v20.11.0。
他又运行命令npm -v
,以验证前端构建工具npm的版本。
屏幕显示npm版本是10.2.4。
4 接近深洞
开发前后端分离Web应用的主要开发工具都准备好了。
🎯马意浓的下一个目标,就是在本地Gradle和npm的开发环境里,成功运行前后端分离的shopping list web app。
目前,前端app和后端app的源代码都已经克隆下来了。
但要把代码跑起来,需要把postgres数据库和pgadmin管理工具运行起来。
因为购物清单数据都需要存储到数据库中。
而且后端app在使用gradle进行构建时,会运行自动化测试,需要访问数据库。
如果在后端app构建时找不到postgres数据库,那么gradle构建会失败。
✅马意浓觉得现在可以用docker容器来运行数据库及其管理工具,从而迈出容器化的第一步。
4.1 安装docker desktop以用容器方式运行postgres数据库及其管理工具
马意浓之前在工作中,如需要使用数据库及其管理工具,都是从官网下载安装包进行安装。
但就和安装jdk类似,这种安装方法使得将来卸载或升级数据库时,会非常麻烦。
他从资料中了解到,在容器化的时代,如果想使用数据库及其管理工具,完全可以从Docker hub上,下载对应的docker image文件。
之后在本地电脑用简单的一行命令,就能启动相应的容器,来使用数据库及其管理工具。
将来卸载或升级,也是运行一行命令的事儿。方便至极。
之前马意浓还担心,容器里跑数据库,要是关闭或删除容器,那数据不就丢了。
✅后来通过读Nigel Poulton的Doker书,他了解到,可以为数据库容器设置一个位于本地硬盘中的volume,以便保存持久化的数据。
只要不删除这个volume,数据库容器关闭后再启动,仍然能够获取之前的数据。
4.1.1 安装docker desktop
✅马意浓在docker的官网上,找到了docker desktop for Windows的安装方法。
他按图索骥,安装好。再根据提示重启电脑,并用自己的docker hub账号登录docker desktop。
他看到所安装的docker desktop for windows版本是v4.27.1(136059)。
4.1.2 用容器方式运行postgres数据库及其管理工具
马意浓进入项目文件夹,再运行命令进入infrastructure
子文件夹。
✅然后再运行命令docker compose up -d postgres pgadmin
,启动postgres数据库和pgadmin管理工具。
他知道,这个命令会读取infrastructure
文件夹下面的docker-compose.yml
文件。并根据其中有关postgres和pgadmin服务的配置信息,启动这两个服务。
等到屏幕显示两个容器都启动了,他切换到docker desktop界面,看到两个容器都启动后的界面。如图2。
马意浓用鼠标点击docker desktop中pgadmin-1那一行的5050:80
链接,打开了一个浏览器。
数据库管理界面出现在眼前。
他在数据库管理界面中,输入了登录信息。
用户名和密码都是admin@gmail.com
。
他知道这个用户名和密码,是在docker-compose.yml
文件中的pdadmin服务中设置好的。
他用鼠标右击数据库管理界面中的Servers,然后点击Register,点击Server…。
✅这时出现了数据库服务器的Register界面。
他在General里的Name里,填写了数据库服务器名shopping-list-web-app
。
他又点击Connection页签,在Host name/address里,填写了数据库服务器主机名postgres
。
他确认了一下,Port已经填好了5432
,Maintenance database已经填好了postgres
。
他把Username改成了postgres
。把Password设置为postgres
。将Save password设置为允许。
确认一切都填好了,他点击了Save按钮。
刚刚创建的shopping-list-web-app数据库服务器就出现在眼前。
就能在数据库出现问题时查看数据库里的数据,如图3。
4.2 在本地Gradle开发环境启动后端app
数据库已经运行起来了。马意浓开始在本地Gradle开发环境启动后端app。
他按快捷键Ctrl+Shift+3
,打开一个新的PowerShell。然后进入项目文件夹。
之后他进入后端app代码文件夹back-end
。
✅他运行命令jabba use openjdk@1.17.0
,将当前jdk版本设置为17。
然后运行命令.\\gradlew.bat bootRun
启动后端app。
屏幕滚过一些提示信息,最后出现了Started ShoppingListApplication in 2.494 seconds
提示。
马意浓知道,这表示后端app已经成功启动了。
他还想继续验证这一点。于是他打开浏览器,在地址栏输入localhost:8081/swagger-ui.html
。
一个OpenAPI definition页面出现在眼前。
马意浓知道,这是后端app的api接口定义信息。这表示后端app已经启动了。
在这个api接口定义界面,他是可以通过鼠标点击,来访问后端app获取数据的。
马意浓用鼠标点开GET /api/v1/shopping-items
接口,然后点击Try it out按钮。
再点击Execute按钮,这时在下面Response body里,返回了[]
空记录。
他知道,这是正常的。因为现在数据库里还没有任何数据。如图4。
4.3 在本地npm开发环境启动前端app
他又按快捷键打开一个新的PowerShell,进入项目文件夹,然后进入前端app代码文件夹front-end
。
他又运行命令nvm use 20.11.0
,将当前node.js和npm版本设置为20.11.0。
然后运行命令npm install
,以便安装package.json
文件所设置的依赖库。
他等到屏幕显示命令执行完毕后,知道此时依赖库已经安装好了。
他接着运行命令npm run dev
,启动前端app。
很快,他看到屏幕显示Local: <xxxx://localhost:5173/
。
他按住Ctrl
键,然后用鼠标点击屏幕上的5173
链接。
这样就打开了一个Chrome浏览器网页,显示了Shopping List Web App前端页面。
终于,他在本地电脑,用docker容器运行了数据库,并成功启动了后端app和前端app!
马意浓很兴奋。
他在前端页面的Item
输入框中,输入了a banana
,点击Add
按钮。
但所输入的购物项,却没有按照预期出现在下面的列表中。
他预感到一定是出问题了。
他于是在浏览器中,按快捷键Ctrl+Shift+I
,打开Developer tools窗口,想看看出错日志。
他点击Console
页签,一行红色背景的CORS出错信息映入眼帘。
Access to XMLHttpRequest at 'localhost:8081/api/v1/shopping-items' from origin 'localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
他之前在写完后端app和前端app代码后,进行联调时,遇到过这样的CORS问题。
当时他按照网上查到的几个解决方案,这里试试,那里试试,就莫名其妙地就解决了。
但那时光顾着高兴,忘了记笔记了。
在没有笔记的情况下,要想回忆当时是如何解决的,几乎是不可能了。
🔥【未完待续】
⚠️因公众号文章发布后难以改进其中的内容,所以要想获得本文及后续连载内容不断改进的最新版,可以随时在评论区给我留言,我会发你最新版。
🔥后面连载内容大纲先睹为快:
🔥5 历经磨难
5.1 在用本地Gradle/npm开发环境自测时面临前端无法访问后端的CORS问题的挑战
5.2 清理现场
🔥6 夺取宝剑
6.1 在本地docker compose里的软件架构
6.2 免费注册Docker hub账号以便推送docker image为部署k8s做准备
6.3 构建后端docker image并推送到docker hub
6.4 构建前端docker image并推送到docker hub
6.5 在本地docker compose里运行shopping list web app
6.6 清理现场
🔥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云集群上,并能顺利使用后,马意浓把整个容器化和上云之旅,写成系列文章,分享给其他程序员。
😃你能否跟着马意浓一步步做下来?在阅读中有任何疑问,欢迎在留言区留言。我会一一回复。
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!