07 K8S 微服务容器化迁移


从运维角度看微服务

微服务特点

  1. 服务组件化
    每个服务独立开发、部署,有效避免一个服务的修改引起整个系统重新部署。
    哪个服务出现问题,修改后重新部署,不影响整个业务。
  2. 技术栈灵活
    约定通信方式,使得服务本身功能实现对技术要求不再那么敏感。
    不再采用一种语言开发,通过对接接口互相访问。
  3. 独立部署
    每个微服务独立部署,加快部署速度,方便扩展。
  4. 扩展性强
    每个微服务可以部署多个,并且有负载均衡能力。
  5. 独立数据
    每个微服务有独立的基本组件,例如数据库、缓存等。

微服务不足

  1. 沟通成本
  2. 数据一致性
  3. 运维成本
  4. 内部架构复杂性
  5. 大量服务如何治理
  6. 如何部署
  7. 如何监控
  8. 数据和事务

单体应用 vs 微服务

  1. 将一套业务 按功能拆分 每个服务处理自己的业务
  2. 每个业务都由独立的数据库
  3. 都通过 apigateway 网关 分发到独立的服务 ,路由转发
  4. 如何互相找到对方,通过注册中心,请求会去询问注册中心向哪里转发

单体架构优势

  1. 易于部署
  2. 易于测试

单体架构不足

  1. 代码膨胀,难以维护
  2. 构建、部署成本大
  3. 新人上手难

k8s治理微服务

  1. 产品迭代
  2. 无状态
  3. 有状态很少应用
  4. 把每个微服务作为一个镜像去部署管理、迭代

SOA面向服务

  1. 把每个功能做成api
  2. 每次增加功能服务都要让其他服务去感知
  3. 微服务可以通过 注册中心 感知
  4. SOA可能需要人工干预,细腻度比微服务大一些

Java 微服务框架

  1. Spring Boot
  2. Spring Cloud (Spring生态技术栈,Spring Boot的升级完善,应用最多)
  3. Dubbo (阿里云开源 )

在K8S平台部署微服务考虑的问题

微服务架构图

  1. 用户通过手机或者浏览器访问域名
  2. 访问域名到达前端(前后端分离),访问功能调用对应业务逻辑处理
  3. 访问功能的时候,通过负载均衡到达网关,网关作为微服务的入口,认证过滤降级都在这个层面实现,类似nginx的location
  4. 网关根据用户请求的url匹配,去注册中心去找提供相应的服务
  5. 注册中心保存了所有微服务的信息
  6. 每个微服务的数据都保存在自己的数据库中
  7. 配置中心存储每个微服务的配置数据,集中化管理

微服务架构的理解

  1. 微服务间如何通信?
    • REST API,RPC,MQ
  2. 微服务如何发现彼此?
    • 注册中心
  3. 组件之间怎么个调用关系?
    • 查看商品 -> 订单 -> 库存 -> 支付
    • 链路跟踪 打点 判断一个请求经过哪些环节
  4. 哪个服务作为整个网站入口?
    • 网关 作为 服务端入口
    • 前端 作为 用户访问入口
  5. 哪些微服务需要对外访问?
    • 前端程序调用服务端接口api,前端和网关都需要暴露
  6. 微服务怎么部署?更新?扩容?
    • java 应用 ,微服务jar包,更新jar包,再启动一个jar包服务
  7. 区分有状态应用与无状态应用
    • 网关,微服务都是无状态的,包括注册中心
    • 有状态的都进行持久化,数据库,redis,MQ,分布式存储都按照传统部署

为什么要用注册中心

微服务太多面临的问题

  1. 怎么记录一个微服务多个副本接口地址? 新加入的服务如何让其他业务动态感知到
  2. 怎么实现一个微服务多个副本负载均衡?
  3. 怎么判断一个微服务副本是否可用? SOA的需要监控http,lv做健康检查
  4. 主流注册中心:Eureka,Nacos
1
2
3
# 注册中心主要解决: 每个微服务之间如何交互
# Eureka 去年年中闭源
# Nacos 阿里开源 部署维护基本一样
1
2
3
4
5
# SOA
用户 -> 前端 -> api ->
lv -> DB数据
-> api1
-> api2

Eureka注册中心

  1. 每启动一个微服务都会被注册到Eureka中
  2. Eureka不单存储信息,还提供负载均衡功能,检查接口是否可用
  3. 请求访问注册中心的服务,响应给请求的客户端,客户端再去请求微服务
  4. 客户端都集成在程序中

项目迁移到K8S平台是怎样的流程

制作镜像

  1. 镜像作为交付物

1
2
3
1. 基础镜像 CentOS
2. 运行环境 基础镜像+ java、php、python
3. 项目环境 基础镜像+运行环境+项目代码

控制器管理Pod

  1. Deployment:无状态部署
  2. StatefulSet:有状态部署
  3. DaemonSet:守护进程部署
  4. Job & CronJob:批处理

暴露应用

  1. 微服务之间都是通过注册中心通信,而不是走service,所以不大多数微服务不需要service
  2. 网关和前端需要ingress暴露

对外发布应用

Pod 数据持久化

容器部署过程中一般有以下三种数据

  1. 启动时需要的初始数据,可以是配置文件 configmap,secret
  2. 启动过程中产生的临时数据,该临时数据需要多个容器间共享
  3. 启动过程中产生的持久化数据

日志与监控

主流方案

  1. Filebeat+ELK
  2. Prometheus+Grafana

传统部署与K8S部署区别

在K8S平台部署 Spring Cloud 微服务项目

熟悉Spring Cloud微服务项目

1
2
# 项目代码地址 
https://github.com/lizhenliang/simple-microservice

  1. portal 域名 让用户访问
  2. portal 访问 ingress 暴露的域名
  3. 域名指向网关

代码编译构建

1
2
# 将代码拉取到部署服务器
git clone https://github.com/lizhenliang/simple-microservice.git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master1 simple-microservice-dev1]# pwd
/opt/microservic-code/simple-microservice-dev1
[root@k8s-master1 simple-microservice-dev1]# ls -l
# 源码目录
drwxr-xr-x 4 root root 4096 Jul 28 11:56 basic-common # 公共依赖
drwxr-xr-x 2 root root 4096 Jul 28 11:56 db # 数据库sql
drwxr-xr-x 3 root root 4096 Jul 28 11:56 eureka-service # 配置中心
drwxr-xr-x 3 root root 4096 Jul 28 11:56 gateway-service # zuul 网关 微服务统一入口认证
-rw-r--r-- 1 root root 11357 Jul 28 11:56 LICENSE
-rw-r--r-- 1 root root 420 Jul 28 11:56 lombok.config
drwxr-xr-x 4 root root 4096 Jul 28 11:56 order-service # order 订单服务
-rw-r--r-- 1 root root 5419 Jul 28 11:56 pom.xml
drwxr-xr-x 3 root root 4096 Jul 28 11:56 portal-service # 前端
drwxr-xr-x 4 root root 4096 Jul 28 11:56 product-service # product 商品服务
-rw-r--r-- 1 root root 24 Jul 28 11:56 README.md
drwxr-xr-x 4 root root 4096 Jul 28 11:56 stock-service # stock 库存服务

查看 gateway 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev1/gateway-service/src/main/resources
[root@k8s-master1 resources]# ls -l
total 12
-rw-r--r-- 1 root root 902 Jul 28 11:56 application-dev.yml
-rw-r--r-- 1 root root 912 Jul 28 11:56 application-fat.yml
-rw-r--r-- 1 root root 95 Jul 28 11:56 application.yml

[root@k8s-master1 resources]# cat application.yml
server:
port: 9999 # 微服务端口
spring:
profiles: # 配置文件
active: fat # fat | dev 指定哪个名字使用哪个配置文件 不同环境区分
application:
name: @artifactId@
1
2
3
4
5
# 不同环境区分 
# 微服务都是jar包,如果修改配置文件也不能进去解压修改比较麻烦
# 2种方法
1. java -jar -Dspring.profiles.active=dev xxx.jar
2. 采用配置中心: apollo,disconf(百度) 为所有微服务的配置做集中管理,运维部署服务端,开发继承agent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[root@k8s-master1 resources]# cat application-fat.yml 
spring:
cloud:
gateway:
discovery:
locator:
#开启以服务id去注册中心上获取转发地址
enabled: true
##小写serviceId
lower-case-service-id: true
# gateway 网关 路由规则 根据请求的url转发
routes:
- id: product-service
uri: lb://product-service
filters:
- StripPrefix=1
predicates:
- Path=/product/**

- id: order-service
uri: lb://order-service
filters:
- StripPrefix=1
predicates:
- Path=/order/**

- id: stock-service
uri: lb://stock-service
filters:
- StripPrefix=1
predicates:
- Path=/stock/**
# 指定 eureka
eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:${server.port}/eureka/

查看微服务的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 查看商品微服务的配置文件
[root@k8s-master1 simple-microservice-dev1]# cd product-service/
[root@k8s-master1 product-service]# ls -l
pom.xml
product-service-api # 进一步解耦 实际的api
product-service-biz # 公共依赖 打包在这里

# 查看biz
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev1/product-service/product-service-biz/src/main/resources

# 微服务配置文件
[root@k8s-master1 resources]# vim application.yml

server:
port: 8010
spring:
profiles:
active: fat
application:
name: product-service

# 连接数据库和其他配置
[root@k8s-master1 resources]# cat application-fat.yml
spring:
datasource:
url: jdbc:mysql://192.168.31.70:3306/tb_product?characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

# 连接 eureka
eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:${server.port}/eureka/

# 其他微服务基本一致

查看 eureka 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@k8s-master1 simple-microservice-dev1]# cd eureka-service/src/main/resources/
[root@k8s-master1 resources]# ls -l
total 12
-rw-r--r-- 1 root root 356 Jul 28 11:56 application-dev.yml
-rw-r--r-- 1 root root 337 Jul 28 11:56 application-fat.yml
-rw-r--r-- 1 root root 105 Jul 28 11:56 application.yml

[root@k8s-master1 resources]# vim application.yml

server:
port: 8888
spring:
application:
name: legendshop-basic-eureka
profiles:
active: dev

[root@k8s-master1 resources]# cat application-dev.yml
eureka:
server:
renewal-percent-threshold: 0.9
enable-self-preservation: false
eviction-interval-timer-in-ms: 40000
instance:
hostname: 127.0.0.1
# 重要: 采用 ip 注册 ,默认可能是主机名,但是k8s中pod的主机名是不能通信的,所以需要使用pod的ip注册
prefer-ip-address: true
client:
register-with-eureka: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
fetch-registry: false

完成代码的编译构建

  1. 以上看的都是程序开发的源码,现在查看编译构建状态的代码 dev2
1
2
3
# Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件
# -P 选择要编译的对象
mvn clean package -D maven.test.skip=true -P prod
  1. dev2中有构建脚本 dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@k8s-master1 gateway-service]# cd /opt/microservic-code/simple-microservice-dev2/gateway-service
[root@k8s-master1 gateway-service]# ls -l
total 12
-rw-r--r-- 1 root root 239 Jul 28 11:56 Dockerfile
-rw-r--r-- 1 root root 1094 Jul 28 11:56 pom.xml
drwxr-xr-x 3 root root 4096 Jul 28 11:56 src

# 一般情况开发写,定义如何构建镜像
# jdk镜像 一般也可以自己自己定制,可以尝试docker仓库中的
[root@k8s-master1 gateway-service]# cat Dockerfile
FROM java:8-jdk-alpine
LABEL maintainer lizhenliang/www.ctnrs.com
# 镜像时区
RUN apk add -U tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/gateway-service.jar ./
EXPOSE 9999
CMD java -jar /gateway-service.jar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 单独测试打包 和 整个项目打包
[root@k8s-master1 simple-microservice-dev2]# ls -l
total 60
drwxr-xr-x 4 root root 4096 Jul 28 11:56 basic-common
drwxr-xr-x 2 root root 4096 Jul 28 11:56 db
drwxr-xr-x 3 root root 4096 Jul 28 11:56 eureka-service
drwxr-xr-x 3 root root 4096 Jul 28 11:56 gateway-service
-rw-r--r-- 1 root root 11357 Jul 28 11:56 LICENSE
-rw-r--r-- 1 root root 420 Jul 28 11:56 lombok.config
drwxr-xr-x 4 root root 4096 Jul 28 11:56 order-service
-rw-r--r-- 1 root root 5419 Jul 28 11:56 pom.xml
drwxr-xr-x 3 root root 4096 Jul 28 11:56 portal-service
drwxr-xr-x 4 root root 4096 Jul 28 11:56 product-service
-rw-r--r-- 1 root root 24 Jul 28 11:56 README.md
drwxr-xr-x 4 root root 4096 Jul 28 11:56 stock-service

# 这个路径下的 pom.xml 完成整个项目打包
# 实际的生产环境是 发布哪个微服务 打包哪个
# 老师整个没解决好依赖 所有整个打包
# 需要jdk+maven环境
[root@k8s-master1 simple-microservice-dev2]# yum install java-1.8.0-openjdk maven


# mvn构建打包
[root@k8s-master1 simple-microservice-dev2]# mvn clean package -D maven.test.skip=true

[INFO] simple-microservice ............................... SUCCESS [6.338s]
[INFO] basic-common ...................................... SUCCESS [0.001s]
[INFO] basic-common-core ................................. SUCCESS [1:34.738s]
[INFO] gateway-service ................................... SUCCESS [56.667s]
[INFO] eureka-service .................................... SUCCESS [3.798s]
[INFO] product-service ................................... SUCCESS [0.001s]
[INFO] product-service-api ............................... SUCCESS [0.281s]
[INFO] stock-service ..................................... SUCCESS [0.001s]
[INFO] stock-service-api ................................. SUCCESS [0.249s]
[INFO] product-service-biz ............................... SUCCESS [1.349s]
[INFO] stock-service-biz ................................. SUCCESS [0.329s]
[INFO] order-service ..................................... SUCCESS [0.001s]
[INFO] order-service-api ................................. SUCCESS [0.202s]
[INFO] order-service-biz ................................. SUCCESS [0.643s]
[INFO] basic-common-bom .................................. SUCCESS [0.001s]
[INFO] portal-service .................................... SUCCESS [1.272s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2:46.295s
[INFO] Finished at: Mon Dec 30 15:36:50 CST 2019
[INFO] Final Memory: 74M/383M
[INFO] ------------------------------------------------------------------------

# 项目目录下出现 target 目录
[root@k8s-master1 simple-microservice-dev2]# cd gateway-service/
[root@k8s-master1 gateway-service]# ls
Dockerfile pom.xml src target

构建项目镜像并推送到镜像仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 当前是手动构建 使用脚本循环创建
# 每个微服务都由一个docker 镜像
# 先准备好 harbor 镜像仓库

# 登录镜像仓库
[root@k8s-master1 k8s]# docker login 172.17.70.252

# 查看脚本
[root@k8s-master1 k8s]# vim docker_build.sh

#!/bin/bash
# 镜像仓库地址
docker_registry=172.17.70.252
# kubectl create secret docker-registry registry-pull-secret --docker-server=$docker_registry --docker-username=admin --docker-password=lx@68328153 --docker-email=admin@ctnrs.com -n ms

# 服务列表
service_list="eureka-service gateway-service order-service product-service stock-service portal-service"
service_list=${1:-${service_list}}
work_dir=$(dirname $PWD)
current_dir=$PWD

# mvn 构建
cd $work_dir
mvn clean package -Dmaven.test.skip=true

# 循环每个服务 进入目录 先查看是否有 biz 有的话进入
# 推送镜像 microservice 镜像仓库
for service in $service_list; do
cd $work_dir/$service
if ls |grep biz &>/dev/null; then
cd ${service}-biz
fi
service=${service%-*}
image_name=$docker_registry/microservice/${service}:$(date +%F-%H-%M-%S)
docker build -t ${image_name} .
docker push ${image_name}
# sed -i -r "s#(image: )(.*)#\1$image_name#" ${current_dir}/${service}.yaml
# kubectl apply -f ${current_dir}/${service}.yaml
done

# 执行
[root@k8s-master1 k8s]# sh docker_build.sh

部署Spring Cloud项目

部署环境要求

  1. CoreDNS
  2. Ingress Controller
  3. MySQL
1
2
3
4
5
6
7
8
[root@k8s-master2 tomcat-java-demo]# yum -y install mariadb mariadb-server
[root@k8s-master2 tomcat-java-demo]# systemctl start mariadb
[root@k8s-master2 tomcat-java-demo]# systemctl enable mariadb
[root@k8s-master2 tomcat-java-demo]# mysql_secure_installation

# 授权访问用户
[root@k8s-master2 tomcat-java-demo]# mysql -uroot -p
MariaDB [(none)]> grant all on *.* to 'root'@'%' identified by '123456';

架构图

  1. eureka 有状态部署
  2. gateway portal 需要对外暴露
  3. 微服务通过注册中心,无需service

服务编排

  1. 每个微服务都需要yaml部署文件
  2. 别忘记更换镜像地址

gateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
cd /opt/microservic-code/simple-microservice-dev3/k8s

[root@k8s-master1 k8s]# cat gateway.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gateway
namespace: ms
spec:
rules:
- host: gateway.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: gateway
servicePort: 9999
---
apiVersion: v1
kind: Service
metadata:
name: gateway
namespace: ms
spec:
ports:
- port: 9999
name: gateway
selector:
project: ms
app: gateway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
namespace: ms
spec:
replicas: 2
selector:
matchLabels:
project: ms
app: gateway
template:
metadata:
labels:
project: ms
app: gateway
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: gateway
image: 172.17.70.252/microservice/gateway:2019-12-30-16-00-51
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: 9999
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 9999
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 9999
initialDelaySeconds: 60
periodSeconds: 10

portal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
[root@k8s-master1 k8s]# cat portal.yaml 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: portal
namespace: ms
spec:
rules:
- host: portal.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: portal
servicePort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: portal
namespace: ms
spec:
ports:
- port: 8080
name: portal
selector:
project: ms
app: portal
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: portal
namespace: ms
spec:
replicas: 2
selector:
matchLabels:
project: ms
app: portal
template:
metadata:
labels:
project: ms
app: portal
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: portal
image: 172.17.70.252/microservice/portal:2019-12-30-16-01-04
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: 8080
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 60
periodSeconds: 10

eureka

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# StatefulSet 

[root@k8s-master1 k8s]# cat eureka.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: eureka
namespace: ms
spec:
rules:
- host: eureka.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: eureka
servicePort: 8888
---
apiVersion: v1
kind: Service
metadata:
name: eureka
namespace: ms
spec:
clusterIP: None
ports:
- port: 8888
name: eureka
selector:
project: ms
app: eureka

---

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: eureka
namespace: ms
spec:
replicas: 3
selector:
matchLabels:
project: ms
app: eureka
serviceName: "eureka"
template:
metadata:
labels:
project: ms
app: eureka
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: eureka
image: 172.17.70.252/microservice/eureka:2019-12-30-15-56-33
ports:
- protocol: TCP
containerPort: 8888
# 变量 MY_POD_NAME 是 内置变量 metadata.name = eureka-0(0-2) 由于是 StatefulSet
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 8888
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8888
initialDelaySeconds: 60
periodSeconds: 10
1
2
3
4
5
6
7
8
9
10
11
12
# 用在这里 
[root@k8s-master1 k8s]# cat ../eureka-service/Dockerfile
FROM java:8-jdk-alpine
LABEL maintainer lizhenliang/www.ctnrs.com
RUN apk add -U tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/eureka-service.jar ./
EXPOSE 8888
CMD java -jar -Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms /eureka-service.jar

# 启动的时候指定了 hostname=eureka-0(0-2)
# 实例名 = eureka-0.eureka.ms

order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@k8s-master1 k8s]# cat order.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
name: order
namespace: ms
spec:
replicas: 2
selector:
matchLabels:
project: ms
app: order
template:
metadata:
labels:
project: ms
app: order
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: order
image: 172.17.70.252/microservice/order:2019-12-30-16-00-54
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: 8020
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 8020
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8020
initialDelaySeconds: 60
periodSeconds: 10

product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@k8s-master1 k8s]# cat product.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
name: product
namespace: ms
spec:
replicas: 2
selector:
matchLabels:
project: ms
app: product
template:
metadata:
labels:
project: ms
app: product
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: product
image: 172.17.70.252/microservice/product:2019-12-30-16-00-57
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: 8010
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 8010
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8010
initialDelaySeconds: 60
periodSeconds: 10

stock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@k8s-master1 k8s]# cat stock.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
name: stock
namespace: ms
spec:
replicas: 2
selector:
matchLabels:
project: ms
app: stock
template:
metadata:
labels:
project: ms
app: stock
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: stock
image: 172.17.70.252/microservice/stock:2019-12-30-16-01-01
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: 8030
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: 8030
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8030
initialDelaySeconds: 60
periodSeconds: 10

导入数据库文件到MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@k8s-master1 db]# scp *.sql root@172.17.70.252:/tmp

MariaDB [(none)]> create database tb_order CHARACTER SET utf8mb4;
MariaDB [(none)]> create database tb_stock CHARACTER SET utf8mb4;
MariaDB [(none)]> create database tb_product CHARACTER SET utf8mb4;

MariaDB [(none)]> use tb_order;
MariaDB [tb_order]> source /tmp/order.sql;

MariaDB [tb_order]> use tb_stock;
MariaDB [tb_stock]> source /tmp/stock.sql;

MariaDB [tb_stock]> use tb_product;
MariaDB [tb_product]> source /tmp/product.sql;

修改配置文件

  1. 根据实际配置修改配置文件
  2. eureka的连裤串
  3. mysql连库串 大多在微服务上
  4. 如果有修改 需要重新执行脚本打包镜像,还要修改部署的yaml文件,手动很麻烦
  5. eureka的连库串 = StatefulSet 有状态部署
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cd /opt/microservic-code/simple-microservice-dev3/eureka-service/src/main/resources

[root@k8s-master1 resources]# cat application.yml
server:
port: 8888
spring:
application:
name: eureka-server
profiles:
active: fat

[root@k8s-master1 resources]# cat application-fat.yml
eureka:
server:
renewal-percent-threshold: 0.9
enable-self-preservation: false
eviction-interval-timer-in-ms: 40000
instance:
hostname: 127.0.0.1
prefer-ip-address: false
client:
register-with-eureka: true
serviceUrl:
defaultZone: http://eureka-0.eureka.ms:${server.port}/eureka/,http://eureka-1.eureka.ms:${server.port}/eureka/,http://eureka-2.eureka.ms:${server.port}/eureka/
fetch-registry: true

# 通过有状态部署 得到的 eureka-0 eureka-1 eureka-2 这个信息dev2还是开发人员的地址
# 数据库也要修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# gateway
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev3/gateway-service/src/main/resources
[root@k8s-master1 resources]# cat application-fat.yml
spring:
cloud:
gateway:
discovery:
locator:
#开启以服务id去注册中心上获取转发地址
enabled: true
##小写serviceId
lower-case-service-id: true
routes:
- id: product-service
uri: lb://product-service
filters:
- StripPrefix=1
predicates:
- Path=/product/**

- id: order-service
uri: lb://order-service
filters:
- StripPrefix=1
predicates:
- Path=/order/**

- id: stock-service
uri: lb://stock-service
filters:
- StripPrefix=1
predicates:
- Path=/stock/**

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# portal
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev3/portal-service/src/main/resources

[root@k8s-master1 resources]# cat application-fat.yml
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
register-with-eureka: true
fetch-registry: true

spring:
freemarker:
allow-request-override: false
allow-session-override: false
cache: true
charset: UTF-8
check-template-location: true
content-type: text/html
enabled: true
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: true
prefer-file-system-access: true
suffix: .ftl
template-loader-path: classpath:/templates/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# order
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev3/order-service/order-service-biz/src/main/resources
[root@k8s-master1 resources]# vim application-fat.yml

spring:
datasource:
url: jdbc:mysql://172.17.70.252:3306/tb_order?characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# product
[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev3/product-service/product-service-biz/src/main/resources

[root@k8s-master1 resources]# vim application-fat.yml

spring:
datasource:
url: jdbc:mysql://172.17.70.252:3306/tb_product?characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# stock

[root@k8s-master1 resources]# cd /opt/microservic-code/simple-microservice-dev3/stock-service/stock-service-biz/src/main/resources

[root@k8s-master1 resources]# vim application-fat.yml

spring:
datasource:
url: jdbc:mysql://172.17.70.252:3306/tb_stock?characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
1
2
3
# 1. 有修改执行 重新生成镜像
[root@k8s-master1 k8s]# sh docker_build.sh
# 2. 需要对应所有yaml修改镜像名称

部署资源

创建 命名空间

1
2
3
4
5
6
7
8
9
10
11
[root@k8s-master1 k8s]# kubectl create ns ms
namespace/ms created
[root@k8s-master1 k8s]# kubectl get ns
NAME STATUS AGE
default Active 13d
ingress-nginx Active 13d
kube-node-lease Active 13d
kube-public Active 13d
kube-system Active 13d
kubernetes-dashboard Active 13d
ms Active 4s

创建镜像仓库资源凭证

1
2
3
4
5
6
[root@k8s-master1 k8s]# kubectl create secret docker-registry registry-pull-secret --docker-server=172.17.70.252 --docker-username=admin --docker-password=lx@68328153 --docker-email=admin@ctnrs.com -n ms

[root@k8s-master1 k8s]# kubectl get secret -n ms
NAME TYPE DATA AGE
default-token-jt5r5 kubernetes.io/service-account-token 3 77s
registry-pull-secret kubernetes.io/dockerconfigjson 1 11s

部署 eureka

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 有状态应用部署 会一个一个启动 
# 健康检查 70秒
[root@k8s-master1 k8s]# kubectl apply -f eureka.yaml

[root@k8s-master1 k8s]# kubectl get pods -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 0/1 Running 0 4s

[root@k8s-master1 k8s]# kubectl get pods -n ms -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
eureka-0 1/1 Running 0 3m41s 10.244.0.79 k8s-master1 <none> <none>
eureka-1 1/1 Running 0 2m35s 10.244.1.88 k8s-node1 <none> <none>
eureka-2 1/1 Running 0 82s 10.244.2.123 k8s-node2 <none> <none>

[root@k8s-master1 k8s]# kubectl exec -it eureka-0 sh -n ms
/ # nslookup eureka
nslookup: can't resolve '(null)': Name does not resolve

Name: eureka
Address 1: 10.244.0.79 eureka-0.eureka.ms.svc.cluster.local
Address 2: 10.244.1.88 eureka-1.eureka.ms.svc.cluster.local
Address 3: 10.244.2.123 eureka-2.eureka.ms.svc.cluster.local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 访问管理页面
[root@k8s-master1 k8s]# kubectl get pods,svc,ingress -n ms -o wide

NAME HOSTS ADDRESS PORTS AGE
ingress.extensions/eureka eureka.ctnrs.com 80 13m

# 三个node节点都可以访问
[root@k8s-master1 k8s]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-cc7p6 1/1 Running 5 6d6h
nginx-ingress-controller-gjrb8 1/1 Running 5 6d6h
nginx-ingress-controller-mwqcv 1/1 Running 5 6d6h

# 绑定hosts 访问 管理页面
60.205.217.112 eureka.ctnrs.com

# 刷新会换成另外一个节点地址 ingress -> pod
# unavailable-replicas 有显示 说明集群节点有问题
# 可用的节点 是其他两个节点

部署 gateway

1
2
# 资源不太够 我就部署一个副本
[root@k8s-master1 k8s]# kubectl apply -f gateway.yaml

部署 portal

1
[root@k8s-master1 k8s]# kubectl apply -f portal.yaml

部署所有微服务

1
2
3
4
5
# 如果测试的资源不足 可以用1个副本

[root@k8s-master1 k8s]# kubectl apply -f stock.yaml
[root@k8s-master1 k8s]# kubectl apply -f product.yaml
[root@k8s-master1 k8s]# kubectl apply -f order.yaml

部署的服务 都被注册中心发现

测试访问 portal

1
2
# 加入hosts 
47.240.14.16 eureka.ctnrs.com portal.ctnrs.com gateway.ctnrs.com

程序调用gateway域名的位置

1
2
3
# 都是前端程序调用
# 现有节点可以随意扩容
[root@k8s-master1 simple-microservice-dev3]# grep gateway. -r portal-service/src/main/resources/

在K8S中部署Eureka集群

  1. 相互注册
  2. 提供健康检查,podIP检查,如果不正常就不会将客户端的请求发送给它
  3. 剔除一个eureka,集群也可正常工作
1
2
3
4
# 如果直接对接k8s
# service -- eureka
# ingress -- gateway
# configmap -- 配置中心

生产环境踩坑经验分享

限制了容器资源,还经常被杀死

1
2
3
4
5
6
7
8
9
10
11
12
13
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name

resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
1
2
3
4
5
6
7
8
1. k8s部署java应用 ,docker不能自动发现java设置的内存,jvm限制无用
2. java内存不够会继续申请,k8s的limits 如果超出限制会直接kill 变成OOM状态
3. 所以这个限制必须让java设置,到达内存最大不去申请

4. jdk1.8之前需要
5. 在Dockerfile 启动命令里 加上限制内存参数,对内存回收利用
6. CMD java -jar $JAVA_OPTS /gateway-service.jar
7. jdk1.9以上会自动识别。1.8用的比较多 要手动指定

滚动更新之健康检查重要性

  1. 误判健康检查的失败,需要根据应用启动时间判断
  2. java启动时间较慢 最好设置一分钟或以上
  3. readinessProbe 就绪检查 它检查的是Ready 1/1 准备就绪才会分配流量,否则不会被访问到。微服务不受service控制,所以加不加无所谓
  4. livenessProbe 存活检查 判断pod中的应用是否启动成功,比如判断服务端口,如果不成功会根据拉起策略重建,直到启动成功为止
  5. 具体要看java服务的启动时间来设置
  6. periodSeconds 周期,每间隔10秒来判断一次,可以设置较长
1
2
3
4
5
6
7
8
9
10
readinessProbe:
tcpSocket:
port: 9999
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 9999
initialDelaySeconds: 60
periodSeconds: 10

滚动更新之流量丢失

  1. 运行的服务pod 已经被注册到 注册中心里了
  2. 如果滚动更新 已分配的流量怎么办
  3. 只能为每个pod 增加退出时间,让pod等待退出时间 再去销毁
  4. 滚动更新是突然kill 没有更新提示,kill之后根据退出时间等待销毁
  5. 优雅退出
1
https://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247487699&idx=1&sn=7bf9c62b02e017de5666ab4ade9ef3d1&chksm=9b1c1051ac6b99476bf665bf1e10ac3aca9b0b995c2da944909621660547f2dd382845302aba&mpshare=1&scene=23&srcid=&sharer_sharetime=1577617482898&sharer_shareid=e3aed5c2fdec06d648a99b41c36fe174#rd

配置中心 和 注册中心 和 数据库

1
2
3
1. 配置中心 apollo 安装服务端,配置文件在里面配置好
2. 开发人员 将客户端植入到程序里 实时连接,如果有新的事件触发会更新
3. 配置中心 是有状态的 保存配置文件
1
2
1. eureka 可以部署在pod中,也可以部署在pod之外,只要网络互通即可
2. 网关使用 spring-cloud-zuul
1
2
3
1. 还是建议微服务中的数据存储使用原先的数据库存储,而不是使用有状态部署
2. 使用k8s部署存储会有运维成本,之前怎么维护就怎么维护
3. k8s适用于 无状态应用,有状态的应用不经常变动,放在k8s中意义不大,测试环境倒是可以考虑快速启动

增加pod流程

  1. 新增的pod会先加入 eureka里
  2. 如果有客户端去请求 eureka 查询这个pod所在的后面业务功能IP,eureka健康检查通过新的pod的后就可以响应回去
  3. gateway就作为请求的客户端,根据url查询对应的pod,eureka查询出对应的ip根据负载均衡策略给gateway,gateway再去响应。