霞的云原生之旅: 运维工程化

说明

实现自动化部署、监控和故障排除,提高系统的可靠性、稳定性和可维护性,为复杂的微服务架构提供了强大的支持 本文的工程化包括:

  1. CI/CD:使用GitLab、ArgoCD和Jenkins进行CI/CD,实现自动化的构建、测试和部署流程,减少人工干预,提高部署的一致性和可靠性。
  2. 服务治理: 服务注册与发现
  3. 可观测性:使用Jaeger进行链路追踪,将链路信息发送到Otel,同时使用ELK进行日志管理,可以帮助您快速定位和解决问题,保障系统的稳定性和性能。
  4. 容器编排:使用Kubernetes实现应用的自动横向扩展和缩减,根据负载情况动态调整资源,提高系统的灵活性和资源利用率。
  5. 身份验证和流量控制:使用Casdoor和Casbin进行auth,结合Istio作为网关,可以实现对请求的身份验证和授权,同时进行流量控制,保障系统的安全性和稳定性。
  6. 存储与消息队列:使用Harbor对应用容器进行push与pull,使用Kafka作为消息队列,可以实现应用的持久化存储和异步通信,提高系统的可靠性和扩展性。
  7. 负载均衡:结合Nginx、OpenELB和PureLB,可以实现对请求的负载均衡和流量控制,提高系统的性能和可用性。

微服务架构

BFF

全称: Backend for Frontend, 面向前端的后端服务

为什么需要?

  1. 当一个Web服务访问AP时, API会同时请求其它的微服务API, 就需要设计并行编程, 然后进行组装(join), 就需要对这些微服务进行封装, 找一些好用的BFF库或手写一个BFF层, 还需要处理一个局部微服务失败之后如何处理

作用

组装服务, 是前端与后端团队的中间人, 面向业务的团队, 主要用于适配前端业务, 例如首页,需要访问不同的微服务, 把这些数据进行一个组装, 聚合之类的操作

与面向资源的区别

使用场景

BFF是适合面向外网的业务接口设计. 而与面向资源的设计的HTTP Restful有很大不同, HTTP Restful强调单一性不一样, 更适合内网服务使用

非BFF的架构

graph TD Web --> SLB SLB --> 用户微服务 SLB --> 稿件微服务 SLB --> 账号微服务 SLB --> 广告微服务

缺点:

  1. 交付速度慢
  2. 演进慢, 需要每个服务都需要兼容
  3. 客户端到微服务直接通信,强耦合
  4. 需要多次请求, 客户端聚合数据, 工作量巨大, 延迟高
  5. 协议不利于统一, 各部门有差异, 需要端对端来兼容
  6. 统一逻辑无法收敛, 比如安全认证, 限流

单点BFF架构

graph TD Web --> SLB --> BFF subgraph BFF app-interface end BFF --> 稿件微服务 BFF --> 账号微服务 BFF --> 广告微服务

单点BFF缺陷

缺点:

  1. 所有流量都经过一个BFF的app-interface层, 只要app-interface层一挂, 全部都挂了
  2. 流量高峰也会导致集群宕机
  3. 兼容性难, 所有业务都是在一个app-interface层, 与移动端交付更困难

BFF设计

  1. 格式适配(用户在不同网络场景, 2345G/wifi环境下如何加载图片(普清/高清))
  2. 兼容, 针对TV/APP/Web/HD进行不同的兼容性设计, 后端微服务不考虑面向终端的兼容设计, 让后端的微服务可以进行快速迭代演进
  3. 聚合
  4. 裁剪

多点BFF架构

按重要性拆分

例如核心的播放(app-view)拆分一个接口, 一个用户账号拆分一个接口, 杂七杂八的设置都在这个app-interface接口

graph TD Web --> SLB --> BFF subgraph BFF app-interface app-view app-account end app-interface --> 稿件微服务 app-interface --> 账号微服务 app-interface --> 广告微服务 app-view --> 稿件微服务 app-view --> 账号微服务 app-view --> 广告微服务 app-account --> 稿件微服务 app-account --> 账号微服务 app-account --> 广告微服务

多点BFF架构缺陷

对跨横切面的逻辑, 例如安全, 限流, 熔断, 统一认证,日志监控等, 都需要推动这些BFF去升级, 有难度

API Gateway

针对多点BFF架构缺陷, 演进出了API Gateway,把安全, 限流, 熔断, 统一认证这些跨横切面的逻辑都上放到了API Gateway, 由API Gatewa来实现, 把BFF当成了一个基础设施, 实现关注点, 专门对业务进行兼容, 不对安全, 限流, 熔断, 统一认证这些跨横切面的逻辑这些通用逻辑维护

最终架构图:

graph TD subgraph Web SSR end Web --> CDN subgraph CDN CDN1 CDN2 CDN3 end CDN --> ELB subgraph ELB ELB1 ELB2 ELB3 end ELB --> APIGateway subgraph APIGateway APIGateway1 APIGateway2 APIGateway3 end APIGateway --> BFF subgraph BFF subgraph BFF-app-interface app-interface1 app-interface2 app-interface3 end subgraph BFF-app-view app-view1 app-view2 app-view3 end subgraph BFF-app-account app-account1 app-account2 app-account3 end end

在微服务的网关选型上, 可以选择服务网格lstio

CI

提高交付速度

前置

  1. 对 React 有基本认知,如npm run dev
  2. 对 Golang 有基本认知, 会按照教程按安装,启动 Web 项目
  3. 对 Docker 有基本认知, 会使用基本命令. 如run, exec
  4. 对 Nginx 有基本认知, 会按照教程安装, 配置
  5. 至少准备 2 台 Unix 服务器(虚拟机也可)

准备

本地环境

创建一个 Go Web 项目, 目录结构如下 目录结构: config: 配置文件目录

arduino 复制代码
project
├─ config
└─ main.go

Web

  1. 安装 Node 环境

  2. 安装 PNPM 环境

shell 复制代码
npm install pnpm
  1. 在项目根目录的web目录使用 Vite 安装一个ReactTypescript模板
bash 复制代码
cd project/web
pnpm create-vite . --template react-ts
  1. 初始化git仓库
shell 复制代码
cd project/web
git add .; git commit -m "build: :tada: initial"; git push -u main main --force
  1. 安装项目依赖
shell 复制代码
pnpm i

Golang

  1. 准备 Golang 环境

  2. 初始化git仓库 URI: web 项目的 git 的 URI 地址

shell 复制代码
cd /project
git init
git submodule add <URI> web
  1. 编写代码 main.go
go 复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func main() {
	server := gin.Default()
	server.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Pong")
	})
	if err := server.Run("0.0.0.0:4000"); err != nil {
		log.Fatalln("运行gin服务失败,请检查端口是否被占用", err.Error())
	}
}
  1. 安装依赖

如果该命令失败,则手动创建go.mod文件

shell 复制代码
go mod tidy

安装

前置

Gitlab

  1. 通过Docker安装GitLab
  2. 测试登录 浏览器输入服务器 URL,登录服务器, 默认账号为root, 密码为root ![[Pasted image 20230305192600.png]]

如果忘记,或者密码错误. 进入 Docker 的 Gitlab 镜像中

输入sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password 查看默认密码

  1. 在 Gitlab 新建项目 -> 新建空白项目 ![[Pasted image 20230305193207.png]]
  2. 链接到本地仓库 branch: 你的分支名, 本文为main, URI: 克隆按钮选择一项, 本文使用 SSH 克隆
shell 复制代码
cd project
git remote add <branch> <URI>

GitLab-Runner

进入 Docker user: 用户名, 一般为 root host: 主机 IP, 例如: 192.168.0.152 port: 端口, 默认为22

bash 复制代码
ssh <user>@<host> -p <port>
  1. 通过Docker安装GitLab-Runner,查阅该文章的三级标题注册 Gitlab-Runner部分

  2. 创建一个名为runner1的 Gitlab-Runner

bash 复制代码
mkdir -p /data/gitlab-runner/runner1/config //创建一个保存配置文件的目录

docker run \
-d \
--name runner1 \
--restart always \
-v /data/gitlab-runner/runner1/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
  1. 注册 runner 1. 登录 Gitlab, 默认账号为root,密码为root 2. 找到你的网站的 URL 与 TOKEN(注册令牌) ![[Pasted image 20230305035332.png]]

  2. 注册 CI 通道 参考 /data/gitlab-runner/confi替换为你的gitlab-runner/config配置文件路径

bash 复制代码
docker run \
-it \
-v /data/gitlab-runner/runner1/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest register

部署

编写自动化脚本

从本地 git push 提交触发 git hook 钩子时, gitlab-runner CI 管道监听触发事件, 然后进行处理, 那么就需要编写 gitlab 的 CI 自动化流程 如何编写自动化流程脚本?

  1. 思考以前手动的方式
  2. 自动化与手动有什么不同?

最佳实践是在项目根目录创建.gitlab-ci.yml,也可以起为别的名称作为 gitlab 的 ci

ymal 复制代码
stages:
  - web
  - golang
  - deploy

cache:
  key: edu_system
  paths:
    - ./web/node_modules
    - ./web/client/node_modules
    - ./web/client/dist
    - ./app
    - .cache

job_web:
  stage: web
  image: node:14-alpine
  tags:
    - edu-front-end
  script:
    - npm install pnpm -g
    - cd ./web && pnpm install && cd ./client && pnpm install
    - pnpm eslint:fix && pnpm stylelint:fix && pnpm build
  only:
    - main
  artifacts:
    paths:
      - ./web/client/dist

job_golang:
  stage: golang
  image: golang:alpine # 利用Golang容器进行打包Golang项目
  tags:
    - edu-back-end
  script:
    - mkdir -p .cache
    - export GOPATH="$CI_PROJECT_DIR/.cache"
    - go env -w GOPROXY=https://proxy.golang.com.cn,direct
    - go get
    - go test ./test/
    - go build -o app
    - chmod +x app
  artifacts:
    paths:
      - ./app

job_deploy:
  stage: deploy
  image: rockylinux
  only:
    - main
  tags:
    - edu-front-end
  script:
    - yum makecache # 生成索引缓存索引提供搜索安装速度
    - yum install -y sshpass rsync openssh-clients # 下载`sshpass`,`openssh-clients`用于`SSH`登录服务器
    - tar -czvf ./web/client/distdist.tar.gz --exclude=node_modules ./web/client/* # 进入打包后的`dist`目录,对里面所有文件进行压缩,并排除`node_modules`,压缩后名文件为`dist.tar.gz`
    - export SSHPASS="$PASSWORD" # 设置生产服务器的密码的变量为`SSHPASS`,从gitlab的环境变量获取
    - sshpass -e scp -o stricthostkeychecking=no ./config/dir.sh ./dist.tar.gz root@192.168.0.158:/home/nginx/html/web/ # 发送`dist.tar.gz`文件至生产服务器的`nginx`路径
    - sshpass -e rsync -av -e "ssh -o stricthostkeychecking=no" --exclude="web" --exclude=".git" --exclude=".cache" --exclude=".idea" . root@192.168.0.158:/home/nginx/html/web/temp  # 发送go项目二进制文件与Dockerfile和deploy.sh
    - sshpass -e ssh -o stricthostkeychecking=no root@192.168.0.158 'cd /home/nginx/html/web && bash dir.sh && tar -xzvf ./dist.tar.gz -C /home/nginx/html/web && rm -rf dist.tar.gz' # 进入到生产服务器的`nginx`路径, 对`dist.tar.gz`文件进行解压, 解压完成后删除`dist.tar.gz`文件
    - sshpass -e ssh -o stricthostkeychecking=no root@192.168.0.158 'cd /home/nginx/html/web/temp && bash ./deploy.sh' #部署go项目至Docker镜像

编写 Script

判断服务器是否存在上次的临时目录,如果有, 则删除临时目录. 没有则创建该临时目录存放上传的文件(可优化点 1)

sh 复制代码
#!/bin/bash

# 需要创建的目录
dir=/home/nginx/html/web/temp
# 列出该目录所有文件
res=$(ls -A $dir)
# 检查该目录是否有文件, 没有则创建
if [ -z "$res" ]; then
  mkdir -p /home/nginx/html/web/temp
else
  mkdir -p /home/nginx/html/web/temp
fi

后续的 CI 部署时,判断之前是否存在正在运行的容器, 有则停止运行并删除, 重新生成新容器(可优化点 2) 将 go 二进制文件打包至容器, 对外开放端口与 go server 的端口,运行该容器.

示例: 在项目根目录创建deploy.sh文件

sh 复制代码
#!/bin/bash

# 检测goimage(.gitlab-ci.yml提供的golang打包后的二进制文件的名称)是否已在运行, 如果已在运行则停止该容器
# docker ps 列出Docker容器列表
# grep goimage 搜索docker ps 列出Docker容器列表有没有goimage这个字串
# awk '{print $12}' 从docker ps 列出Docker容器列表的第12列(容器名)
# docker stop goimage 停止运行goimage容器
if [ $(docker ps | grep goimage | awk '{print $12}') ]; then docker stop goimage; fi

# 如果goimage容器存在 则删除该容器
# docker ps -a 列出全部容器
# docker ps -q 静默模式,只列出容器编号
# docker ps -aq 列出全部容器编号
# --filter name=goimage 过滤出容器名称为goimage的容器
# docker rm -f goimage 删除goimage容器
if [ $(docker ps -aq --filter name=goimage) ]; then
  docker rm -f goimage
fi

# docker images 列出所有镜像
# docker rmi -f 强制删除
# docker rmi -f goimage 强制删除goimage镜像
if [ $(docker images | grep goimage | awk '{print $1}') ]; then docker rmi -f goimage; fi

# 在本目录打包一个镜像名字及标签为goimage的二进制文件可运行的Docker镜像
# docker build --tag 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
docker build --tag edu_server .

# docker run 运行
# -d 后台运行容器,并返回容器ID
# -p 映射主机与容器的端口
# --name 容器名称
docker run -d -p 4000:4000 --name edu_server edu_server

# 测试服务器是否响应
curl 192.168.0.158:4000/

编写 Dockerfile

利用一个小型操作系统来作为支架,使 go 二进制文件运行在该操作系统上 示例:

  1. 使用rockylinux:9.1.20230215-minimal操作系统(~40Mb)
  2. 使用4000端口
  3. 复制所有项目文件至容器根目录
  4. 复制go二进制文件至容器根目录下的app文件中
dockerfile 复制代码
# syntax=docker/dockerfile:1

# 从rockylinux镜像运行
FROM rockylinux:9.1.20230215-minimal

# 映射的端口
EXPOSE 4000

### 从宿主机项目目录下所有的文件复制至容器根目录
COPY . .

# 从宿主机./app复制到容器的目录/app
COPY app ./app

# 启动容器时运行app二进制文件
ENTRYPOINT ["/app"]

测试

测试服务器是否响应

shell 复制代码
curl 192.168.0.158:4000/

http 创建api.http

arduino 复制代码
# curl 192.168.0.158
GET 192.168.0.158:4000/

Github CI

链接: github.com/sunmery/tes...

CD

示例: github.com/sunmery/git...

服务治理

Consul

服务端搭建

参考安装与使用Consul

客户端使用

示例: github.com/Mandala-lab...

可观测性

服务端搭建

客户端使用

github.com/sunmery/ope...

容器编排

服务端搭建

参考文章

Kubernetes UI

Kubernetes的可视化资源监控Kuboard

身份验证和流量控制

Kratos 身份验证JWT与Casbin鉴权实践 Golang中的鉴权实践

插件

  1. CSI-NFS: github.com/Mandala-lab...
  2. cert-manager: github.com/Mandala-lab...
  3. metrics-server: github.com/Mandala-lab...
  4. openelb: github.com/Mandala-lab...
  5. shell-autocompletion: github.com/Mandala-lab...

存储与消息队列

搭建

Cache

redis 集群

RDS

juejin.cn/post/731793...

静态文件存储

juejin.cn/post/731419...

镜像存储

juejin.cn/post/731718...

MQ

Docker 部署 kafka服务端与go客户端的使用 segmentio kafka库的使用 confluent kafka库的使用

负载均衡

搭建

Nginx

NGINX二进制编译安装使用QUIC协议

PureLB

juejin.cn/post/732230...

OpenELB

juejin.cn/post/731372...

相关推荐
苦逼IT运维2 天前
正向代理与反向代理:原理、区别以及应用(Nginx 和 Tomcat)
运维·服务器·nginx·云原生·tomcat·devops
Azure DevOps3 天前
Azure DevOps Server:不能指派新增的用户
运维·microsoft·flask·azure·devops
极小狐4 天前
极狐GitLab 签约比博斯特,助力新能源智能底盘企业研发提效
gitlab·devsecops·devops·极狐gitlab·安全合规
山顶望月4 天前
敏捷开发与DevOps的有机结合
运维·devops·敏捷流程
happy_king_zi5 天前
Django-form表单
python·django·devops
子洋5 天前
自动化部署脚本教程:前端项目的自动打包、上传与部署
前端·后端·自动化运维
JackSparrow4146 天前
脚本自动化创建AWS EC2实例+安装ElasticSearch和Kibana+集成OpenTelemetry监控
运维·elasticsearch·自动化·aws·devops·opentelemetry
_.Switch6 天前
Python Web 开发中的DevOps 实践与自动化运维
运维·开发语言·前端·python·架构·serverless·devops
极小狐8 天前
极狐GitLab 17.4 重点功能解读【二】
安全·gitlab·devops·安全合规
洛小豆8 天前
有趣的实战经验分享:自动化测试中如何精确模拟富文本编辑器中的输入与提交
前端·javascript·自动化运维