基于前端React技术栈与Golang后端Kratos技术栈组合的规范化最佳实践(个人认为)的前后端项目
优势:
- 现成的工程化React前端项目
- 与CI/CD(Continuous integration and continuous deployment)的自动化DevOps集成
- 支持Docker与Kubernetes部署
- 集百家所长
前端技术栈使用React作为前端kit, 考量有: React灵活, 前端的生态最丰富
- 桌面库: 采用tauri, 一个rust打造的桌面, 占用极小
- CLI: 采用Vite, 现代和快速
- 语言选择: 使用Golang的基于DDD思想的Kratos作为后端项目的kit, 即可作为单体架构使用, 也可以作为微服务架构使用
- 协议: HTTP/RPC, 支持HTTP与protobuf协议
支持Github
部署, Gitlab CI
部署到Docker或者Kubernetes的shell方式, Gitlab CI
+ Argocd
的部署到Kubernetes部署方式
优缺点:
Github
: 仅支持Github平台, 适合开源项目使用Gitlab CI
: 方便灵活, 仅仅使用Gitlab CI
部署到Docker或者Kubernetes的shell方式, 不方便追踪部署Gitlab CI
+Argocd
: 推荐有Kubernetes集群环境的使用, 有规范化的应用清单, 部署清单, 追踪方便
支持Gitlab, Github的CI, 内置ci脚本, 根据实际需求修改, CI的最佳实践有:
- 保护变量, 利用Gitlab平台提供的变量, 而不是在gitlab.ci.yml中硬编码
- 如果需要进入到远程基础设施(e.g Linux,Windows)时创建一个最小的权限用户, 给它分配合理的目录权限, 文件执行权限等
本项目使用Gitlab + Argo 作为自动化的部署
本仓库的后端项目接口支持Observability可观测性. 使用opentelemetry
库来对gRPC
与HTTP
接口进行采集指标
/日志
的遥测数据
TODO
必须先在根目录进行git的初始化, 因为我在CI/CD流编写了自动获取git仓库地址的sh, 仅在有bash/sh/zsh等unix shell的情况下可以执行
需要的环境分为:
- 前端环境
- 后端环境
- DevOps环境
- node
- pnpm
- go
- protoc
- protoc-gen-go
- Docker: 可选, 如果需要Docker部署的话. 项目包含部署文件, 可直接使用它(Dockerfile 与 docker-compose.yaml)直接部署
- Kubernetes: 可选, 如果需要DevOps部署的话. 需要一个正常运行的Kubernetes集群, 本项目默认使用LoadBalancer类型, 项目包含部署文件, 可直接使用它( Dockerfile 与 docker-compose.yaml)直接部署
- Gitlab: 可选, 可以是Gitlab.com账号或者私有的Gitlab仓库, 用于与Gitlab CI + Argocd的Continuous Integration & Delivery
本项目的示例部署文件Dockerfile
与 docker-compose.yaml
分别在frontend
与backend
目录下, 分别代表前端与后端的部署.
前端项目的部署流程将项目部署到Nginx容器中
后端项目的部署流程将项目编译为二进制文件
部署示例:
需要再Harbor私有容器注册表或者公有的注册表中创建对应的前端/后端的仓库
docker build --progress=plain --no-cache -t myusername/myimage:mytag frontend/ -f frontend/Dockerfile
Kubernetes的CI/CD流
定义变量:
# argocd所在的的命名空间
export ARGOCD_NAMESPACE="argocd"
# 角色名称, 用于管理项目
export ROLE_NAME="lx"
# Kubernetes集群地址
export CLUSTER_SERVER="https://192.168.2.160:6443"
# 前端命名空间, 不需要额外创建命名空间选择default即可
export FRONTEND_NAMESPACE="frontend"
# argocd中的前端项目名, 用于分配团队人员的操作权限
xport FRONTEND_PROJECT_NAME="frontend"
# 前端应用的名称
export FRONTEND_APPLICATION_NAME="react"
# Kubernetes 资源清单在仓库中的路径, 相对于仓库根目录的路径
export FRONTEND_DEPLOY_PATH="frontend/ci"
# 后端端命名空间, 不需要额外创建命名空间选择default即可
export BACKEND_NAMESPACE="backend"
# argocd中的后端项目名, 用于分配团队人员的操作权限
export BACKEND_PROJECT_NAME="backend"
# 后端应用的名称
export BACKEND_APPLICATION_NAME="go"
进入到本仓库的根目录进行生成:
chmod +x kubernetes/argocd/init.sh
./kubernetes/argocd/init.sh
-
初始化git仓库, 不执行husky会报错
git init
-
安装依赖
pnpm i
-
本地运行
pnpm dev
-
运行Web桌面应用
构建桌面应用需要Rust环境
pnpm tauri dev
-
打包
pnpm build
-
构建Web桌面应用
构建桌面应用需要Rust环境
编辑project/frontend/src-tauri/tauri.conf.json
文件的tauri.conf.json > tauri > bundle > identifier的"com.tauri.dev"
值改为:""com.tauri.build""
原来的值:
{
"bundle": {
"identifier": "com.tauri.dev"
}
}
修改后的值:
{
"bundle": {
"identifier": "com.tauri.build"
}
}
运行构建
pnpm tauri build
- 安装依赖
kratos upgrade
- 运行
kratos run
- /: 创建的路径, 例如
service/account
- -r https://xxx.git: 指定的
kratos-layout
的git仓库URL, 例如gitee的kratos-layout
的URL:https://gitee.com/go-kratos/kratos-layout.git
- --nomod: 共用 go.mod, 大仓模式
- -b : 可选, 指定分支, 例如
main
kratos new <path>/<project> [-r <https://xxx.git>] [-b <brach>] [--nomod]
DevOps工程化有详细说明, 这里是简化说明
查看项目根目录的 deploy/frontend/binary
定义以下变量:
- DOMAIN: 域名, example.com, 不需要schema
- NGINX_DIR: 存储nginx的绝对路径
- HTML_DIR: 网页文件目录
- CONF_DIR: 配置文件目录
- SSL_DIR: TLS文件目录
export DOMAIN=""
export NGINX_DIR=""
export HTML_DIR=""
export CONF_DIR=""
export SSL_DIR=""
然后执行:
chmod +x ./deploy/frontend/binary/00-install.sh;./deploy/frontend/binary/00-install.sh
chmod +x ./deploy/frontend/binary/01-create_systemd.sh;./deploy/frontend/binary/01-create_systemd.sh
chmod +x ./deploy/frontend/binary/02-create_user.sh;./deploy/frontend/binary/02-create_user.sh
chmod +x ./deploy/frontend/binary/03-start_nginx.sh;./deploy/frontend/binary/03-start_nginx.sh
Docker的新版已经删除--progress=plain, 如果遇到--progress=plain的错误, 删掉即可
注意事项:
- 请将 /path/to/configs 替换为实际的本地配置文件路径。
- 将 替换为实际的应用程序名称。
- 根据需要调整 replica 数量、Service 类型等配置
最后在安装了Kubernetes的服务器环境上执行:
kubectl apply -f backend/deployment.yaml
编辑
将
image
:web
# 这里需要替换成你使用Docker build
的名字
语法:
docker build [--progress=plain] [--no-cache] -t <user>/<image_name>:<tag> <path>
例: 在当前目录构建所有文件, 镜像名为web
, 不使用Docker
缓存, 显示构建过程的详细信息
- --progress=plain: 以简洁的方式显示构建进度信息
- --no-cache : 不使用缓存
- web : 构建的镜像名
- . : 当前目录所有文件
docker build --progress=plain --no-cache -t web .
选项:
- -f: 指定文件
- -d: 后台执行
- --build: 强制重新构建
docker-compose up \
-f docker-compose.yml \
-d \
--build
选项: -d:以后台模式运行容器。 --name backend:指定容器的名称为 backend。 --restart unless-stopped:定义容器的重启策略为除非手动停止,否则总是重新启动。 -p 80:80: 将容器内部的 80 端口映射到宿主机的 80 端口 -p 443:443:将容器内部的 443 端口映射到宿主机的 443 端口。 mandala/frontend:v1:指定要运行的镜像为 mandala/frontend 的版本 v1。
docker run -d \
--name backend \
--restart unless-stopped \
-p 80:80 \
-p 443:443 \
mandala/frontend:v1
在项目的根目录执行以下脚本
如果项目共用一个 go.mod 模块文件, 能够得到完好支持
Docker build
执行时的上下文环境是依据当前执行的目录为主目录, 并非Dockerfile
的目录, 例如当前目录为/app
,Dockerfile
所在的目录是/app/user/
,执行时是使用/app
作为主目录, 而不是Dockerfile
的目录Docker的新版已经删除--progress=plain, 如果遇到--progress=plain的错误, 删掉即可
语法:
docker build [--progress=plain] [--no-cache] -t <user>/<image_name>:<tag> <path>
- --progress=plain: 构建过程中显示的详细信息的格式
- --no-cache: 不使用缓存
- -t: 标签, 例如 myusername/myimage:v1
- web : 构建的镜像名
- . : 当前目录所有文件 示例:
docker build -f Dockerfile --progress=plain --no-cache -t mandala/backend:v1 .
修改Dockerfile
的ENTRYPOINT
, 把backend
替换为go mod xxx生成的名称, 也就是go build 之后的应用名称, 例如backend
示例:
command: ["/app/backend", "-conf", "/data/conf"]
修改docker-compose.yml
:
- 修改
image
:字段为你的镜像URL, 如果已经打包上传到远程容器注册表, 这里就是镜像的完整URL地址. 如果没有, 那么你需要先使用docker buiuld
进行操作, 然后填写你在docker buiuld
时填写的标签, 例如上一步操作的mandala/backend:v1
进入到docker-compose.yml
所在的目录, 然后运行该应用的容器:
建议使用docker-compose的方式, 这种方便记录你的操作, 可以存留你的操作, 而且也方便其他人员查看你的部署文件
启动并后台运行:
docker-compose up -d
停止
docker-compose down
查看容器
docker-compose ps
查看实时日志
docker-compose logs
docker run -d \
--name backend \
--restart unless-stopped \
-p 30001:30001 \
-p 30002:30002 \
-v $(pwd)/configs:/data/conf \
mandala/backend:v1 /app/backend -conf /data/conf
本项目的CI是分离结构, 前端和后端的部署清单在各自的ci
目录下
在你的服务器下安装, 在我的DevOps工程化文章里面详细写了如何安装, 这里不在赘述
- Gitlab
- Gitlab Runner
- 定义变量
Gitlab -> 项目页 -> 设置 -> CI/CD -> 变量 -> 添加:
- REGISTER_ADDRESS: 注册表的地址, 例如
ccr.ccs.tencentyun.com
- REGISTER_USERNAME: 注册表的用户名
- REGISTER_PASSWORD: 注册表的密码
- REGISTER_REPO: 应用在注册表的空间名, 必须在注册表提前创建, 例如
web
- SSHPASS: 远程服务器的密码, 是你要上传文件的服务器的密码
- 编辑前端/后端项目目录下的
.gitlab-ci.yml
把variables
里面的变量值都改成你实际的变量值
根据你的实际需求, 修改CI文件
frontend/.github/workflows/ci.yaml
根据你的实际需求, 修改CI文件
backend/.github/workflows/ci.yaml
参考文章
参考文章
参考文章