kubernetes(二十)SpringCloud微服务容器化迁移

SpringCloud微服务容器化迁移

运维角度看微服务

单体应用VS 微服务

kubernetes(二十)SpringCloud微服务容器化迁移
详情参考: https://blog.csdn.net/qinaye/article/details/82840625

单体应用的优缺点

  • 优点

    便于共享:单P h = = ] l ;个归档文件包t 9 b h 9含所有功能,便于在团i ! N * 6 /队之间以及不同的部署阶段之间共享。
    易于测试:单体应用一旦部署,所有的服务或特性就都可以使用了,这简化了测试过程,. F 2 u - y 2因为没有额外的依赖,每项测试都可以在部署完成后立刻开始。
    易于部署:只需] / e &将单个归档文件复制到单个目录下。

  • 缺点

    复杂性高:由于是单个归档文件,所以整个项目文件包含的模块非常多,导致模块的边界模糊、依赖关系不清晰、代码的质量参差不齐,混乱的J S V f ] & o ^ h堆在一起,使得整个项目非常复杂。以致每次修改代码,都非常小心,可能添加一个简单的功能,或者修改一个Bug都会L _ G Q o g V带来隐藏的缺陷。
    技术债Q g ^ L F L V l务:随着时间的推移、需求的变更和技术人员的更替,会逐渐形成应用程序的技术债务,并且越积越多。
    扩展能力受限:单体应用只能作为一个整体进行扩展,无法根据? & , ! 7业务模块的需要B S V进行伸缩。
    阻碍技术创新:对4 ] | A g W于单体应用来说,技术是在开发j f I _ 5之前经过慎重评估后选定的,每个团队成员都必须使用相同的开发语言、持久化存储及消息系统。

微服务架构的优缺点

  • 优点

    易于开发和维护:一个微服务只会关注一个特定的业务功能,L @ K所以业务清晰、代码量较少。开发和维护单个微服务相对简单。
    单个微服务启动较快
    局部修改容易部署:单体应用只要有修改,就得重新部署整个O I , $ m z o 9 X应用。微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
    技术栈不受限制:在微服务架构中,可以结合项目业务P [ g {及团队的特点,合理的选择技术栈。
    按需伸缩:可根据需求,实现细粒度的扩展。G m 6

  • 缺点

    运维要求高:更多的服务意味着要投入更多的运维
    分布式固有的复杂性:使用微服务构建的是分布式系统。对于一个分布式系统,系统容错、网络延迟= v j 9 B {、分布式事务等都会带来巨大S | x F l l 4 `的问题。
    接口调整成本高:微服务之间通过接口进行U i = q S :通信。如果修改某一个微服务的A, s h z B UPI,可能所有用到这个接口的微服务都需要进行调整。

微服务的特点

服务组件化

每个服h 0 1 C M 4 J . /务独立开发、部署,有效避免一个服务的修改引起整个系统重新部署。

技术栈灵活h ] 0 I

约定通信方式,使得服务本身功能实现对技术要求不再那么敏感。S | d Q [ G e ~ `

独立部署

每个微服务独立部署,加快部署速) & j ; P , f J度,方便扩展。

扩展性强

每个微服务可以部署多个,并且有负载均衡能力。

独立数C ~ $ & . *

每个l 5 $ t J 5 { {微服务有独立的基本组件,例如数据库、缓存等。

java微服务框架

SpringBoot: 快速开发微服务的框架

SpringCloud: 基于springBoot实现的一套微服务解决方案

DubboM O Y H ^ $ z: 阿里巴巴开源的微服[ z ` $ Z U + F务框架

在k8s平台部署微服务需要考虑的问题

微服务架构图

kubernetes(二十)SpringCloud微服务容器化迁移

对微服务架构的理解

微服务间如何通信?REST API,RPC,MQ
微服务如何发现彼此?注册中心
组件之间怎么个调用关系? • 哪个服务作为整个网站入口?前后端分离
哪些微服务需要对外访问?前端和微服务网关
微服务怎么部署?更新?扩容?
区分有状态应用与无状态应用

为什么要用注册中心

  • 微服务太多面临的问题:
    • 怎么记录一个微服务多个副# b w ; o K y本接口地址?
    • w A ; 1么实现一个微服务多个副本负载均衡?
    • 怎么判断一个微服务副本是否可用?
    • 主流注册中心:Eureka,] ^ : & K B O O oNacos,Consul

kubernetes(二十)SpringCloud微服务容器化迁移

容器交付流程

kubernetes(二十)SpringCloud微服务容器化迁移

kubernetes(二十)SpringCloud微服务容器化迁移

kubernetes(二十)SpringCloud微服务容器化迁移

在K8s部署项目流程

kubernetes(二十)SpringCloud微服务容器化迁移

容器化微服务项T O , H o

具体@ o a ?步骤:
第一步:熟悉SprO ^ r ( & h 6 Wing Clou3 6 $ / H Ld微Q I o服务项目
第二步:源代码编译构建
第三步:构建项目镜像并推送到镜像仓库
第四步:K8s服务编排
第五步:在K8s中部署Eureka集群(注册中心)和MySQL数据库
第六步:部署微服务网关服务
第七步:部署微服务业务程序
第八步:部署微服务前b v H N } L A
第九步:微服务对外发布
第十步:微服务升级与扩容

熟悉Springv x ( z ) + ^ } C Cloud微服务项目


https://github.com/lizhenl. B & tiang/simM ^ } [ =ple-microse@ B P z b O V Ervice

代码分支说明:

• dev1 交付代码

• dev2 增加Dockerfil) 2 G 5 Be

• dev3 增加K8Sx L # % H ^ W 5资源编排

• dev4 增加微服务链路监控

• master 最终上线

源代码编译构建

  • 下载 microservic-code.zip 这个代码包Z L _ 1 e,并解压出dev1分v y b 5
$ unzi4 * I Y ^p simple-microservice-dev1.zip && cd simple-microservice-dev1 
  • 安装java和maven环境

aliy6 / B h nun maven: https://maR ( 0 0ven.aliyun.Y n + g 2 a % 3com/mvn/guide

$ yum install java-1.8.0-o| 2 l G N ~ [penjdk maven
$ vim /etc/maven/settings.xml
<mirrors>
&S h : T  * H  -lt;mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>S % 7 g h 0 - j ;阿里云公共仓库</name>
<url>https://mq v )aven.aliyun.com/repository/public</url>
</mirrorP ! h X 9>
<V y 7 { q  ` I P;/mirrors>
$ mvn clean package -Dmaven.test.sZ & ; L vkip=true    #编8 F G f T _ ! z
  • 构建jdk基础环境(选用)

需要下载好

$? t c e W 6 J _ vim Dockerfile
FROM alpine:e x GlaP N ] ]test
LABEL maintainerB z ! # =="122725501@qq.com"
ADD jdkZ N } 7 - S J ]-8u261-linux-x64.tar.gz /usr/local/
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories &&\
ln -sf /usr/share/zoneinfo/Aj s 0 x U W  J ]sia/Shanghai /g a Q e Q eetc/localtime &a- r + omp;& \
apk update && apk upgrade && apk --no-U C X Q 4 E cache add ca-certificates &&_ $ vamp; \
wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpineI V E J 0 d-pkgs.sgerrand.com/sgerrand.rsa.pub && \q ~ n ; F
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/dowZ d c .nload/2.30-r0/glibc-2.30-r0.apk &&a) B w 6 4mpk C ] C; I # h
apk add glibc-2.30-r0.apk && \
rm -rf *.apk && \
rm -rf /var/cache/apke - & K Y B P r/*
# 设置JAVA变量环境
ENV JAVA_HOME=/usr/local/jdk1.8.0_261
ENV CLASSPATH=$JAVA_HOME/bin~ 2 d h + + t p
ENV PATH=M , 3 T.:$JAVA_HOME/bin1 # Z n { / % ~ c:$PATH
CMD ["java","-version"]
# 指定工作空间
WORKDIR /o^ M b 9 # * 4 ]pt
$ docker build -t jdk-alpine:latest . 

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

kubernetes(二十)SpringCloud微服务容器化迁移

  • 下载 microservic-code.zip 这个代码包,并解压出dev2分支
$ unzip simple-miS , S V d W T 0 NcroP h ] O 6serv^  a Z E $ E  Eice-dev2.zip && cd simple-microservice-dev2
$ mvn clean package -Dmaven.test.skip=true         #编译U c I p
  • 对gateway 制作镜像
$ cd simple-microservice-dev2/gatewa$ o A ) by-service/
$ vim Dockerfile
FROM java:8$ V H S-jdk-alpine
LABEL maintainer 122725501@qq.n ; v % (com
RUN  sed -i 's/dl-cdn.alpinelinux.orgf 7 & B  ` 0/mirrors.aliyun.com/g' /etc/a{ 8 6 X 4 G ] /pk/repositories &&a n R I y iamp;\
apk add -U tzdata && \
rm -rf /var/cache/apk/* &&\
ln -sf /usr/sU a @ & @hare/zoneinfo/Asia/ShanghR W T M E p nai /etc/localtime
COPY ./target/gateway-service.jar ./
EXPOSE 9999
CMD javJ m r F y I O ?a -jar /gateway-service.jaP ! g p 5 Jr
$ docker buiN V Z p c ^ Q 8ld -t gate w i Vway .      #构建镜像
  • 将镜像推送到h= ` t K $ Tarbor2 m % / q $ :仓库

本人之前部署的harbor: 192.168.56.1v P m * ~8 hu| ? ? 0 lb.cropy.cn

$ dos c o ocker login hub.cropy.cn
$ docker tag gateway:latest hub.cropy.cn/demo/gateway:latest

K8s服务编排

kubernetes(二十)SpringCloud微服务容器化迁移

在K8s中部署Eureka集群(注册中心)和MySQL数据库

kubernetes(二十)SpringCloud微服务容器化迁移

Statefu~ d N w X e ` MlSet+He@ : l e Y 0adless DNS名称格式:

<statefulsetName-index>.<service-name> .<namespa* D kceQ 8 N I

name>.svc.cluster.local

Eureka集群节点Pod名称:

http://eureka-0.eureka.ms.svc.cluster.local

http://eureka-1.eureka.ms.svc.cluster.local

http://eureka-2.eureka.ms.svc.cluster.loc

  1. 制作镜像并推送到harbor
$ unzip simple-microservice-dev3.zip && cd simple-microservice-dev3
$ mvn clean package -Dmaven.test.skip=true
$ cd eureka-service && vim Docker! I Dfile
FROM java:8-jdk-alpine
LABEL maintainer 122725501@q[ 1 ^ ^  { Qq.com
RUN  sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories &a^ T  J 9 6mp;&\
apk. 2 B add -T )  y B V | )U tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shangha( k P o Z s V wi /etc/localtime &&\
rm -rf /vaN B 8 R 9 x 0r/cache/apk/*
COPY ./target/eureka-service.jar ./
EXPOSE 8U , J ] * * @888
CMD java -jar -Deureka.insta? 6 * 0 0 e [ P ence.hostname=${MY_| k 1 9 b g H )POD_NAME}.eureka.ms /eureka-service.jar
$ docker buil^ s 8d -t eul p , L o 3 i P 8reka-service .
$ docker tag eureka-service2 ! & ? m e `  s hub.cropy.cn/demo/eurekJ _ ( Q j ( S J a-service
$ docker push hub.cropy.cn/demo/eureka-service
  1. 制作k8s y? 1 7 # 2 ) Yaml文件
$ vim eurake-server.yaml
---
ar E , q {pi5 9 s S @Version: extensions/v1beta1
kind: Ing- l x , . 1ress
metadata:
name: eureka
namespace: ms
spec:
rules:
- host: eur} Z c - C heka.ctnr5 ( V @ G ; * Q _s.com
http:
paths:
- p( } L 4 =ath: /
backend:
serviceName: eureka
servicePog a T w i ; B ]rt: 8888
---
apiVersion: v1
kind: Service
metadata:
naF P 1 _ ( 8 q B Ume: eureka
namespace: ms
spec:
clusterIP: None
p_ s B jorts:
- port: 8888
name: euret r Nka
selector:
project: ms
app: eureka
---
apiVer` B W 6sion: apps/v1
kind: StatefulSet
metadata:
name: eureka
names) o [ Z q space:- S  ^ ms
spec:
replicas: 3
selector:
matchLabels:
project: ms
app: eureka
serviceName: "eureka"
template:
metadata:
labeu Y p = xls:
pro4 7 ! W ? f gject: ms
app: e4 Q Yureka
spec:
imagePullSecrets:
- name: registry-pull-secr@ % y (et
containers:
- name: eureka
containers:
- name: eureka
image: hub.cropy.cn/demo/eureka-service
ports:
- protocol: TCP
containerPort: 8888
env:
- name: MYb g ] / u_POD_NAME
valueFrom:
fiel h * =dReE # X 4 m p 1  Yf:
fielS B o + a q | Z $dPath: metadata.name
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readin= * I - y K SessProbe:
tcpSi k z d s t lo% h _ ~ 8 rcket:
port: 8888
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8888
initialDelaySeconds: 60
periodSeconds: 10
  1. 准备namespace和harbor secret
$ kubectl create namespace ms
$ kubectl cP 4 k % = P 7 Ireate secret docker-registry registry-pull-secret --docker-server=hub.cropy.cn --docker-username=admin --docker-password=Harbor1234% z 1 b | , a5 -n( j b t ms
  1. 构建eurake-serB h j mvice
$ kubectl apply -f eureka.yaml
$ kubectl get pod -n ms
$ kubectl get svc -n ms
$ kubectl get statefulset -n ms
$ ku2 w 8bectl get ingrA f % }ess -n ms
$ kubectl get ep -n ms  
  1. 测试eurZ h v 9 : -ake
$ kubectl get pod -n ingress-nginx -o wm t x j # : Zide
NAME                                       REAO + & 2 p * . xDY   STATUS    R R 6 8ESTARTS   AGV r 7E   IP              NODE        NOMINATED NODE   READINESS GATES
nginx-ingress-controller-766fb9| } n | e @ u jf77-dlf5j   1/1     Running   16         20d   1J ! 492.168.56.13   k8s-node2   <n` X a m _ ] 8 w !one>           <none>
$ kubectl get ingressY 5 ; C R 1 5 P -n ms
NAME     CLASS    HOSTS              Av a 7 k HDDRESS   PORTS   AGE
eureka   &? ! B A zlt;none>   eureka.ctnrs.com             80      17m

绑定192.168.56.13 eureka.ctnrs.com 到宿主机hosts文件

kubernetes(二十)SpringCloud微服务容器化迁移

6 . 基础m* O f r = X 5 Uysql数据库创建

$ helm install java-demo-db --set persistence.storageClass="managed-nfs-storA L x , y w n age" azure/mysql
$ kubectl get* p m secret -V : n : N I-namespace default java-demo-db-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo
RRGynGS53N
mysql -h java-demo-db-mysql -pRRGynGS53N    # 获取访问方式
$ kubectl ) c m t I  { / geB Q ? B c , M N it svc

部署微服务业务程序

  1. 构建镜像p d J(部署业务程序(product、stock、order)
$ cd simple-microservice-dev3 && mv0 8 q X wn clean package -Dmaven.testk @ y b m.skip=true &am( 8 Wp;& cd product-service/
vim Do* . @ *ckerfile
FROM java:8-jdk-alpine
LABEL maintainer 122725501@qq.com
RUN  sed -i 's/dl-cdn.alpinelinux.o] } i 4 E ; Frg/mirrors.aliyun.com/g' /etc: - = * //apk/repositories &; g Q H R e G $ o;&\
apk add -U tzdata && \
rm -rf /var/cache/apk/* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./targI W M S + 0et/producI 0 ,t-service-biz.jar ./
EXPOSE 8010
CMD java -jar /product-service-biz.jar
  1. 修改数据库和注册中心的连接O l l / A ( n 0配置
主要在F q  P各个项目的src/main/resources/application-J T K - p % s adev.yml 配置中
1. m) ^ % $ [syql: jdbc:mys9 = ~  [ H 9 }ql://jae X . : O Z b x rva-demo-j R g f g S cdb-mysql:3306
userg ; 2 J u 4 Kname: root
password: RRGynGS53N
2. eurake
defaultZoM I Q A m D e une: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
  1. 数据库数据导入
 $ cd simple-microservice-dev3/db
$ kubectl run mysql-client -it --image=mysql:. ) . o J E5.7 -- bash
$ root@mysql-client:/# mysql -h java-demo-db-mysql -pRRGynGS53N    # 测试登陆数据库(如不能登陆,请检查网络)
##G A k s H 5 ~  4# 另起终端,准备导入数据
$ cd7 X E a & L simple-microservice-dev3/db
$ kubectl cp order.sql mysql-client:/opt/
$ kubectl+ D T , cpy / S & d produc= } d V wtr w ] _ e e Z X N.sql mysql-client:/opt/
$ kubectl cp stock.sql mysql-client:/opt/
### 回到登陆远程数据库的终端
mysql> source /opk A H W ^ i 9 v Nt/order.sql;
mysql> source /opt/stock.sq. =  !l;
source /opt/product.sqN u a 6 ~ y = - {l;
  1. 重新构建镜像(脚本类的内容请联系QQ: 122725501 获取)
$ c, A T 2 0d microservic-code/simple-microservice-dev3
$ cd k8s && ls
docker_build.` ^ H jsh  eureka.yaml  gateway.yaml  order.yaml  portal.yaml  product.yaml  stock.yaml
$ vim docker_build.sh     #自动构建脚本
#!/bin/bash
docker_registry=hub.cropy.cn
kubectl create secret docker-registry registry-pull-secret -H 2 0 L Q * 1-docker-server=$docker_rE f W ^ d Z | segistry --docker-username=admin --docker-password=Harbor12345 --docker-emaV } & c * ` C dil=admin@12272& } ~ X - s5501.com -n ms
se6 Q P , drvice_lM [ i v pist="eureka-service gateway-service order-servi/ g x + n 5 Jce product-service stock-service portal-s1 d P l rervice"
service_list=${1:-${service_list}}
work_dir=$(dirname $Pd p d 1 q %WD)
current_dir=$C h ~ ~ f c s w /PWD
cd $work_dir
mvn clean package -Dmaven.test.skip=true
for service in $service_list; do
cd $work_dir/$service
if ls |grep biz &>8 p $ r;/dev/null; then
cd ${servicA T c e i Y , oe}-biz
fi
servix - ) Bce=3 e t Q Q${service%-*}
im9 _ 6 = j 4age_name=$doc8 r /ker_registry/microservice/E : & U r J${service}:$(date +! B A%F-%} & * g / L L $H-%M-%S)
docker build -t ${image_name} .
docker push ${image_ne M ` e i * q Xame}
sk b fed -i -r "sj h  + z I ! z y#(image: )(.*)#\1$, b p & M imageQ : 1 i_name#" ${current_dir}/${servic7 z / de}.yaml
kuI 2 f 2bectl apply -f ${current_dir}/${service}.yaml
done
$ ./docker_build.sh     # 自动构建并上传镜像,同时启动服务
$ kubectl ge` : $ 9 u ` m ^t pod -nJ d % Z T n h ms   # 查看构建之后的pod是否正常

微服务对外发布

$ kubectl get ingress -n ms
NAME      CLASS    HOSTS               ADDRESS   PORTS   AGE
eureka    <none>   eureka.ctnrs.c# N - o 0o+ % Cm              80      21m
gateway   <none>   gateway.ctnrs.com             80      16m
portal    <none>   portal.ctnrs.com              80      15m

绑定上述域名到192.168.56.13 的hosts 解析文件,即可U G N X @ 6 Q访问

微服务升级与扩容

微服务升级:对要升级的微服务进行上述步骤打包镜像:版本,替@ & W X F V j , [代运行的镜像

微服务扩容:对Pod扩容副本数

生产环境踩坑经验分享

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

java不能自动发现docker设置的堆内存,这将会导致

JVM资源不稳定,超出limits限制,kM @ D 5 [8s会杀掉该容器!

解决办法:

• 手动指定JVM堆内存大小

• 配置Jy l z / E C . zVM自动识别(1.V | e9| i , + 2 d N m版本+才支持)-

XX:+UnlockExperimentalVMOptions -

XXT L P Z:+UseCGroupMemoryLimitForHeap

滚动更新之健康检查的重W % D要性

滚动更新是默认发布策略,当配置健康检查时,滚动更新会根据Probe状态来决定是否继续更新以及) O )是否允许接入流量,这样在整个滚动更新过程中可抱歉始终会有可用的Pod存在,达到平滑升级。

kubernetes(二十)SpringCloud微服务容器化迁移

滚动更新之流量丢失

kubernetes(二十)SpringCloud微服务容器化迁移
滚动更新触发,Pot i . y t ^ l ~ ;d在删除过程中,有些节点kube-proxy还没来得及同步iptables规则,从而部分流量请求到Terminating的Pod上,导致请求出错。

解决R L w p ; x (办法:配置preStop回调,在容器终止前优雅暂停5秒,给kube-proxy多预留一点时c $ Z l M