文章目录
  1. 1. 大纲
  2. 2. 一. 应用本身的要求
    1. 2.1. 1.1 会话同步
    2. 2.2. 1.2 锁机制
    3. 2.3. 1.3 外部服务基于域名方式
    4. 2.4. 1.4 配置走环境变量
    5. 2.5. 1.5 应用探针
  3. 3. 二. docker打包等
    1. 3.1. 2.1 打包方式
    2. 3.2. 2.1.1 打包
  4. 4. 三. K8S部署相关
    1. 4.1. 3.1 服务编排
    2. 4.2. 3.2 外部服务接入
    3. 4.3. 3.3 Redis集群搭建
    4. 4.4. 3.4 启动顺序的问题

目前将视频会员服务从阿里云ECS服务器迁移到IDC机房的Kubernates环境,在迁移的过程中遇到一些或大或小的问题,因此将解决的方式及一些心得记录下来,以方便大家进行参考。

大纲

    对应用本身的要求 -->无状态
      会话同步
      锁机制
      外部服务基于域名方式
      配置走环境变量
      应用探针*(保证应用的无缝迁移)
    docker打包
    k8s部署相关
      服务编排
      Redis集群的配置

一. 应用本身的要求

由于K8S内部是分布式环境,因此服务本身必须支持分布式环境下的部署要求。

1.1 会话同步

服务需要是无状态的,如果服务自身保存了会话信息,请求转移到其余节点时会因为丢失会话而报错。
因此对于web服务,涉及到会话保持的部分,需要采用外部方式进行会话保持。常见的方式有:会话粘滞,分布式session

【推荐阅读】 session会话保持原理

我的应用采用的是基于Redis的spring session实现方式。

【推荐阅读】 Spring Boot项目利用Redis实现session管理

(文章采用的是单机模式的Redis部署,生产中多采用集群方式,在后面的内容会讲解视频服务采用的哨兵方式)

1.2 锁机制

对于扫库逻辑,应当支持锁机制,多采用分布式锁、数据库乐观锁(版本号、状态机)、悲观锁等的实现方式。保证同一时刻只有单个服务在实际提供服务,其余未获取锁的服务只能自旋。

【推荐阅读】 分布式锁的几种实现方式

【推荐阅读】 乐观锁与悲观锁

1.3 外部服务基于域名方式

由于服务本身已经无状态,因此类似于ip等的硬编码等配置应当剔除,转而使用基于名字的方式进行服务的访问,K8S的DNS服务会对服务和实ip之间做映射,这样我们的服务可以任意伸缩及转义而不需要频繁更改配置。

1.4 配置走环境变量

配置使用环境变量,在容器实际部署的时候由外部环境设值,docker命令为

docker -e xxxx=xxxxx 

这也是围绕着无状态服务的。在k8s中的使用方式为基于Secret,服务生成时会根据对应的key取到保密字典中的value。后续会讲解。

1.5 应用探针

应用本身需要提供HTTP(推荐)方式的服务探针,k8s会依据探针的响应情况决定是否对服务进行负载伸缩。

二. docker打包等

2.1 打包方式

视频会员服务目前采用的方式是线下环境打包,push到仓库,运维在线上拉取镜像进行部署。前提是保证线下测试通过。
后续该方式会更加的流程化,尽量减少人员的介入。

2.1.1 打包

打包之前需要准备

  1. 应用二进制包,springboot项目为jar包,web后台为war等。
    2.Dockerfile
    3.启停脚本

在docker环境下执行打包操作,命令为(以视频会员举例)

打包

docker build -t development1/video-member-api:1.2 .

打标签,描述镜像属于哪个部门哪个业务线

docker tag development1/video-member-api:1.2 172.20.36.229:5000/development1/video-member-api:1.2

将镜像推送到仓库

docker push 172.20.36.229:5000/development1/video-member-api:1.2

删除本地镜像

docker rmi development1/video-member-api:1.2

三. K8S部署相关

3.1 服务编排

【推荐阅读】 视频会员Gitlab主页

3.2 外部服务接入

外部服务在K8S内部是以一个service的形式部署的,部署方式如下,以数据库为例

video-member-db.yaml

apiVersion: v1
kind: Service
metadata:
  name: video-member-db
  namespace: video-member
spec:
  ports:
  - port: 3306                            -- 集群内监听端口
    protocol: TCP
    targetPort: 3306                        -- 需要继续端口转换的目标端口,指向真实端口
---
apiVersion: v1
kind: Endpoints
metadata:
  name: video-member-db
  namespace: video-member
subsets:
  - addresses:
      - ip: 172.30.61.12                    -- 外部服务的真实ip
    ports:
      - port: 3306                        -- 外部服务端口

3.3 Redis集群搭建

由于视频会员是基于三节点的集群方式部署,需要进行会话同步,采用了基于Redis方式的spring session技术实现,因此需要依赖高可靠的Redis集群方式实现,此处采用的是哨兵模式的Redis集群方式。
由于之前已经有一套现成的Redis服务,因此直接将外部的哨兵集群映射到服务内部,在集群内采用域名方式加载哨兵集群。

spring-application-prod.properties

#########################################################################
#
#     redis配置
#
#########################################################################
spring.redis.database=0
#spring.redis.host=172.30.66.78
spring.redis.port=6379
spring.session.store-type=redis
# name of Redis server  哨兵监听的Redis server的名称
spring.redis.sentinel.master=mymaster
# comma-separated list of host:port pairs  哨兵的配置列表
spring.redis.sentinel.nodes=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
# REDIS-PASSWD
spring.redis.password=${redis_password}

可以看到,集群节点处是以名字方式指向了外部的redis哨兵集群
配置文件如下:

video-member-redis.yaml

apiVersion: v1
kind: Service
metadata:
  name: redis-sentinel-1
  namespace: video-member
spec:
  ports:
  - port: 26379
    protocol: TCP
    targetPort: 26379
---
apiVersion: v1
kind: Endpoints
metadata:
  name: redis-sentinel-1
  namespace: video-member
subsets:
  - addresses:
      - ip: 172.30.61.12
    ports:
      - port: 26379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-sentinel-2
  namespace: video-member
spec:
  ports:
  - port: 26379
    protocol: TCP
    targetPort: 26379
---
apiVersion: v1
kind: Endpoints
metadata:
  name: redis-sentinel-2
  namespace: video-member
subsets:
  - addresses:
      - ip: 172.30.61.12
    ports:
      - port: 26379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-sentinel-3
  namespace: video-member
spec:
  ports:
  - port: 26379
    protocol: TCP
    targetPort: 26379
---
apiVersion: v1
kind: Endpoints
metadata:
  name: redis-sentinel-3
  namespace: video-member
subsets:
  - addresses:
      - ip: 172.30.61.12
    ports:
      - port: 26379

3.4 启动顺序的问题

先加载环境变量secret服务,加载数据库服务,加载外部系统映射服务,外部系统构建完毕之后最后启动主业务系统。
这些操作都可以通过服务编排脚本一键实现。

文章目录
  1. 1. 大纲
  2. 2. 一. 应用本身的要求
    1. 2.1. 1.1 会话同步
    2. 2.2. 1.2 锁机制
    3. 2.3. 1.3 外部服务基于域名方式
    4. 2.4. 1.4 配置走环境变量
    5. 2.5. 1.5 应用探针
  3. 3. 二. docker打包等
    1. 3.1. 2.1 打包方式
    2. 3.2. 2.1.1 打包
  4. 4. 三. K8S部署相关
    1. 4.1. 3.1 服务编排
    2. 4.2. 3.2 外部服务接入
    3. 4.3. 3.3 Redis集群搭建
    4. 4.4. 3.4 启动顺序的问题
Fork me on GitHub