k8s部署前后分离架构微服务——跨域和缓存问题

前面k8s环境部署的差不多了,现在尝试把自己开发的微服无程序部署到k8s环境中。

首先是打包镜像,前端是用了Ant Design Vue Pro框架开发的,使用cnpm run build 编译,会在工程目录下生成一个dist文件目录,再将此目录和nginx打包为docker镜像,上传到私有harbor仓库中就可以进行k8s的部署了。

1.我这里前端的镜像和要调用api服务不在同一个pod下,就遇到了CORS(跨域资源共享)问题,前端是禁止跳转访问api的服务的,即便在api服务上做了类似下面的cors设置也还是解决不了问题。

复制代码
//go代码 中间件设置
handlers.CORS(
			handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization", "Kt"}), //这里需要添加自定义的头部信息如Kt
			handlers.AllowedMethods([]string{"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE"}),
			handlers.AllowedOrigins([]string{"*"}),
			handlers.AllowCredentials(), // 允许携带凭证(如 cookies)
		)

问了deepseek,我采用了使用nginx代理方案。即在Ant Design Vue Pro框架的工程目录下有deploy/nginx.conf的配置文件,将其配置文件的注释部分打开,设置代理跳转地址,为了方便过滤需要跳转的url,我在.env文件中VUE_APP_API_BASE_URL=/api统一为跳转的url添加了一个/api/ 前缀,这样在nginx代理时,就会将所有有/api/前缀的url拦截,进入代理步骤,当然你的前缀也可以自定义,只要和nginx.conf配置文件中过滤的前缀设置一致就行,如下

复制代码
  location /api/ {
       proxy_pass http://roles-manage.eqim.svc.cluster.local:8002/;  #这是需要跳转的地址
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
   }
}
# 跳转的时候,将/api/前缀自动去除。

这样再重新编译工程,打包前端镜像,部署后,cors跨域问题就可以解决了。

  1. 遇到的第二个问题是缓存问题。之前api服务使用单个redis测试开发的,现在k8s环境中部署的是redis集群,它和单个redis部署的不同点是:在写入和读取时,数据是通过hash分片保存到某一个集群的pod节点上,但为服务的pod此时要访问的redis的pod不一定是存储数据的pod,这样redis集群会发出重定向指令,但是正常使用单个redis测试开发的服务程序是不支持这个重定向的,就会出现,写入的数据获取失败的问题。一开始想通过不修改代码的方式解决,于是选择部署一个redis-cluster-proxy代理服务,应用服务连接代理服务,由代理服务协调redis集群的读写问题。方案是可行的,但我没有搞成功。原因是我没找到现成的redis-cluster-proxy的镜像,是通过下载源码编译自己打包的镜像,不知是什么原因,每次连接代理服务的pod总重启,导致没法使用。

于是我只好退回来选择修改代码的方式来解决缓存问题。

这里也遇到点坑,就是github.com/go-redis/redis/v8 库不支持域名或ip地址的问题,好像只支持类似localhost或主机名的地址方式,不适合在k8s中使用,经过多次尝试,我换了github.com/redis/go-redis/v9库,官网地址

修改代码只需要修改连接redis的代码部分就行,后面的读写操作代码不用修改。代码修改类似下面所示:

复制代码
// go语言代码

//config中的c.Redis.Addr设置:redis-cluster-0.redis-headless.redis.svc.cluster.local:6379,redis-cluster-1.redis-headless.redis.svc.cluster.local:6379,redis-cluster-2.redis-headless.redis.svc.cluster.local:6379




var (
		rdb           redis.Cmdable
		singleClient  *redis.Client
		clusterClient *redis.ClusterClient
	)
	redis_addr := strings.Split(c.Redis.Addr, ",")
	redis_addr_num := len(redis_addr)
	if redis_addr_num == 0 {
		panic("redis连接失败!!!\n 请检查配置地址信息。")
	}

	if redis_addr_num == 1 {
		singleClient = redis.NewClient(&redis.Options{
			Addr:         redis_addr[0],
			Password:     c.Redis.Passwd,
			DialTimeout:  c.Redis.DialTimeout.AsDuration(),
			WriteTimeout: c.Redis.WriteTimeout.AsDuration(),
			ReadTimeout:  c.Redis.ReadTimeout.AsDuration(),
		})
		err = redisotel.InstrumentTracing(singleClient)
		if err != nil {
			log.Fatal(err)
		}
		rdb = singleClient
	}
	if redis_addr_num > 1 {
		clusterClient = redis.NewClusterClient(&redis.ClusterOptions{
			Addrs:        redis_addr,
			Password:     c.Redis.Passwd,
			DialTimeout:  c.Redis.DialTimeout.AsDuration(),
			WriteTimeout: c.Redis.WriteTimeout.AsDuration(),
			ReadTimeout:  c.Redis.ReadTimeout.AsDuration(),
		})

		//log.Info(fmt.Printf("redis集群地址:%v \n", clusterClient.ClientList(context.Background())))
		//log.Info(fmt.Printf("redis集群信息:%v \n", clusterClient.ClusterInfo(context.Background())))

		// 添加集群健康检查
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		log.Info("执行Redis集群Ping测试...")
		if err := clusterClient.Ping(ctx).Err(); err != nil {
			log.Fatal("Redis集群连接失败: %v", err)
			// 可以在这里降级到单节点模式或抛出更明确的错误
		} else {
			log.Info("Redis集群连接成功")
		}
		err = redisotel.InstrumentTracing(clusterClient)
		if err != nil {
			log.Fatal(err)
		}
		rdb = clusterClient
	}

......


defer func() {
	if singleClient != nil {
			err := singleClient.Close()
			if err != nil {
				log.Error(err)
			}
		}
		if clusterClient != nil {
			err := clusterClient.Close()
			if err != nil {
				log.Error(err)
			}
		}
}()

重新编译打包部署,就可以解决redis集群缓存读写数据不一致问题了。

这里的数据库使用的是greatsql, 在MySQL和MariaDB 迁移过程中,遇到的问题不大,大部分是在创建数据表时,字符集和默认数据格式问题,比如在MariaDB 中 datetime类型 的默认值可以设置为'0000-00-00 00:00:00' ,而在greatsql中较为严格,只支持'1970-01-01 00:00:00' 格式。建议在迁移数据表的时候,将表结构单独部署,使用sql语句一个表一个表的执行,方便查错。迁移过程中一般情况批量导入都会报错。另外数据库工具也非常关键,目前还是没找到太好用的替代Navicat的工具。使用开源的Dbeaver 有时会出现莫明其妙的数据导入后,应用服务读取数据错误的问题。具体原因还没太搞明白,可能和字符集有关,具体还待进一步研究。

相关推荐
比特森林探险记2 小时前
MySQL 架构全景解析
数据库·mysql·架构
稚辉君.MCA_P8_Java3 小时前
DeepSeek Java 单例模式详解
java·spring boot·微服务·单例模式·kubernetes
疯癫的老码农3 小时前
【小白入门docker】创建Spring Boot Hello World应用制作Docker镜像并运行
java·spring boot·分布式·docker·微服务
小刘不想改BUG3 小时前
Docker 部署微服务项目详细步骤
docker·微服务·容器
Liquad Li4 小时前
CometD 长轮询协议及在Salesforce中的应用
架构·salesforce
没有bug.的程序员4 小时前
分布式架构初识:为什么需要分布式
java·分布式·架构·php
郑州光合科技余经理4 小时前
微服务架构:基于Spring Cloud ,构建同城生活服务平台
java·spring cloud·微服务·小程序·架构·uni-app
Aoda4 小时前
企业级项目结构设计的思考与实践 —— 以 PawHaven 为例
前端·架构
野熊佩骑5 小时前
CentOS二进制安装包方式部署K8S集群之系统初始化
运维·docker·微服务·云原生·容器·kubernetes·centos