K8S中部署SpringCloud微服务项目
一、熟悉SpringCloud项目
1.1、主机分配
主机IP | 角色 |
---|---|
192.168.171.11 | k8s-master |
192.168.171.12 | k8s-node1 |
192.168.171.13 | k8s-node2 |
192.168.171.10 | harbor-mysql |
1.2、代码分支详情:
- Dev1 交付代码;
- Dev2 编写Dockerfile构建镜像;
- Dev3 k8s资源编排文件;
- Dev4 微服务链路监控;
- Dev5 新功能测试;
- Master 最终上线;
二、代码编译构建(Maven)
2.1、拉取项目代码
代码分支:
[root@k8s-master ~]# git clone -b dev1 https://github.com/xxxxx/simple-microservice
1、导入db目录下数据库文件到自己的MySQL服务器
2、修改配置环境(xxx-service/src/main/resources/application.yml,active值决定启用环境配置文件)
3、修改连接数据库配置(xxx-service/src/main/resources/application-fat.yml)
4、修改前端页面连接网关地址(portal-service/src/main/resources/static/js/productList.js和orderList.js)
5、服务启动顺序:eureka -> mysql -> product,stock,order -> gateway -> portal
2.3、架构介绍:

- 用户访问==》portal(前端);
- 通过负载均衡调用后端api(通过ingress把网关的service暴露下);
- 后端服务分别注册到Eureka,并由Eureka进行服务发现,注册,心跳等;
2.4、准备编译
编译需要安装jdk , jdk版本要看开发那边使用什么,注意一下。我这里用的1.8.0
# yum install java-1.8.0-openjdk maven -y
# java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)
进行编译:
进入源代码里,我们先进入交付的分支,开发工程师原封不动的把源代码交给我们,然后我们运维进行编译构建,把源代码打成jar包。
[root@k8s-master1 simple-microservice-dev1]# ll
总用量 28
drwxr-xr-x 4 root root 70 7月 28 11:56 basic-common
drwxr-xr-x 2 root root 59 7月 28 11:56 db
drwxr-xr-x 3 root root 32 7月 28 11:56 eureka-service
drwxr-xr-x 3 root root 32 7月 28 11:56 gateway-service
-rw-r--r-- 1 root root 11357 7月 28 11:56 LICENSE
-rw-r--r-- 1 root root 420 7月 28 11:56 lombok.config
drwxr-xr-x 4 root root 71 7月 28 11:56 order-service
-rw-r--r-- 1 root root 5419 7月 28 11:56 pom.xml
drwxr-xr-x 3 root root 32 7月 28 11:56 portal-service
drwxr-xr-x 4 root root 75 7月 28 11:56 product-service
-rw-r--r-- 1 root root 24 7月 28 11:56 README.md
drwxr-xr-x 4 root root 71 7月 28 11:56 stock-service
每个微服务的配置文件均在:
XXX/src/main/resources/application-fat.yml
需要修改的微服务为:stock-service,product-service, order-service中的mysql配置文件;
Maven项目对象模型(POM),可以通过一小段描述信息来观念里项目的构建,报告和文档的项目管理工具软件
mvn clean package -D maven.test.skip=true -P prod
mvn clean package:清除目录中生成的结果,做一个清除,重新打新的包。
-D maven.test.skip: 跳过单元测试,写的测试用例,如果写的有问题,是编译不过去的
-P prod: 使用哪一套配置文件
[root@k8s-master simple-microservice-dev1]# mvn clean package -D maven.test.skip=true
构建完成会多出一个target且根据pom文件生成指定jar包:
# cd product-service/product-service-api/target/
classes/ maven-archiver/ maven-status/ product-service-api.jar
三、构建项目镜像并推送到镜像仓库
构建镜像使用Docker和结合Dockerfile
[root@k8s-master1 product-service-biz]# cat Dockerfile
FROM java:8-jdk-alpine
RUN apk add -U tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/product-service-biz.jar ./
EXPOSE 8010
CMD java -jar /product-service-biz.jar
如果不是https的Harbor需要在docker里面添加信任才能访问到镜像
[root@k8s-master ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["192.168.171.10"]
}
docker build -t product .
[root@k8s-master1 product-service-biz]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
product latest f8d58232cd1b 5 seconds ago 191MB
[root@k8s-master1 product-service-biz]# docker tag product 192.168.171.10/microservice/product:latest
[root@k8s-master ~]# docker login 192.168.171.10
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
推送镜像到Harbor仓库中:
docker push 192.168.171.10/microservice/product:latest
四、推送镜像到harbor
[root@k8s-master1 simple-microservice-dev3]# ll
总用量 28
drwxr-xr-x 4 root root 70 7月 28 11:50 basic-common
drwxr-xr-x 2 root root 59 7月 28 11:50 db
drwxr-xr-x 3 root root 50 7月 28 11:50 eureka-service
drwxr-xr-x 3 root root 50 7月 28 11:50 gateway-service
drwxr-xr-x 2 root root 143 7月 28 11:50 k8s
-rw-r--r-- 1 root root 11357 7月 28 11:50 LICENSE
-rw-r--r-- 1 root root 420 7月 28 11:50 lombok.config
drwxr-xr-x 4 root root 71 7月 28 11:50 order-service
-rw-r--r-- 1 root root 5419 7月 28 11:50 pom.xml
drwxr-xr-x 3 root root 50 7月 28 11:50 portal-service
drwxr-xr-x 4 root root 75 7月 28 11:50 product-service
-rw-r--r-- 1 root root 24 7月 28 11:50 README.md
drwxr-xr-x 4 root root 71 7月 28 11:50 stock-service
具体来看下批量将所有的微服务推送到镜像仓库并在K8S集群中发布:
#!/bin/bash
##创建拉取镜像的认证信息
docker_registry=192.168.171.10
kubectl create secret docker-registry registry-pull-secret --docker-server=$docker_registry --docker-username=admin --docker-password=XXXXXX --docker-email=zhdya@zhdya.cn -n ms
##服务list
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
##编译
cd $work_dir
mvn clean package -Dmaven.test.skip=true
##推送镜像并在K8S中发布(此dev3先取消发布)
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 product-service-biz]# vim Dockerfile
FROM java:8-jdk-alpine
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/product-service-biz.jar ./
EXPOSE 8010
CMD java -jar /product-service-biz.jar

五、部署springCloud项目
- 1、服务编排
- 2、在k8s平台部署Erueka
- 3、导入数据库文件到Mysql
- 4、部署网关gateway
- 5、部署业务程序(product、stock、order)
- 6、部署前端(portal)
编译打成jar包,在dev3分支
5.1、创建命名空间
kubectl create ns ms
部署环境要求: - 跨主机网络:使用flannel或者Calico ,需要网络来打通主机之间资源的通信 - CoreDNS: k8s内部的DNS。 ,用于对pod对service做记录的,好让其他的pod做访问。 - Harbor镜像仓库:这个我们已经准备好了,并将项目镜像推送上去了。 - Ingress Controller:同一暴露我们的应用,写yaml文件实现。

这里portal是门户网站-前端,用户访问www.XXX.com的页面,通过域名访问之后,进行的一个页面展示,我们我们通过pod来进行实现,ingress来定义我们的域名,域名定义哪个service,来定义到某个pod上,来影响静态页面,下订单请求交给网关api,采用异步调用,暴露网关,进行来用户访问,ingress也来调用,service来实现pod副本gateway网关,通过一些前端页面的页面功能,同gateway来调用实现,用户点击某个功能gateway拿到这个请求之后,通过路由转发规则,到后端的业务程序,比如商品信息(product)库存,订单,他会根据不同的业务需要来处理,库存服务会根据订单的使用来和内部的调用接口来实现,pod直接调用,需要跨主机网络,怎么找到这个服务,就需要这个注册中心,应用间的互相调用就需要这个注册中心,所有的服务都会放在这里,来进行消息通信,现在比较流行的就是erueka,订单服务都会放入到我们的mysql数据库中的,mysql是部署在外部的,有状态应用,这个部署在k8s中是比较麻烦大的,erueka是部署在k8s集群内的,只需要保证他的id是唯一性就可以了,不需要考虑他的存储。
先来看一个yaml(eureka,需要通过域名暴露服务)
[root@k8s-master1 k8s]# cat eureka.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: eureka
namespace: ms
spec:
rules:
- host: eureka.zhdya.cn
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: 192.168.171.10/microservice/eureka:2020-01-12-13-14-40
ports:
- protocol: TCP
containerPort: 8888
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
如上名为eureka的service,其clusterIP为None,所以它是无头服务。下边为名eureka的StatefulSet,其.spec.serviceName的值为eureka,指明通过上边的eureka无头服务提供DNS解析功能功。
Dockerfile:
[root@k8s-master1 k8s]# cat ../eureka-service/Dockerfile
FROM java:8-jdk-alpine
RUN 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
再来看一个微服务(不需要暴露域名):
[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: 192.168.171.10/microservice/product:2020-01-12-13-14-56
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
创建k8s登录harbor信息认证:
[root@k8s-master1 k8s]# kubectl create secret docker-registry registry-pull-secret --docker-server=192.168.171.10 --docker-username=admin --docker-password=Zhang --docker-email=zhdya@zhdya.cn -n ms
secret/registry-pull-secret created
查看其中一个微服务的配置信息:
[root@k8s-master1 k8s]# cat ../stock-service/stock-service-biz/src/main/resources/application-fat.yml
spring:
datasource:
url: jdbc:mysql://192.168.171.10:3306/tb_stock?characterEncoding=utf-8
username: root
password: XXXXXX
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
部署eureka:
补充说明: StatefulSet的应用特点: - 稳定且有唯一的网络标识符 当节点挂掉,既pod重新调度后其PodName和HostName不变,基于Headless Service来实现。
我们知道kubernetes中的service是定义pod暴露外部访问的一种机制,例如:3个pod,我们可以定义一个service通过标签选择器选到这三个pod,然后让问这个service就可以访问这个pod。
Headless service是Service通过DNS访问的其中一种方式,只要我们访问"mypod.stsname.namespace.svc.cluster.local",我们就会访问到stsname下的mypod。而Service DNS的方式下有两种处理方法: - Normal Service 这里访问"mypod.stsname.namespace.svc.cluster.local"的时候会得到mypod的service的IP,既VIP。 - Headless Service 这里访问"mypod.stsname.namespace.svc.cluster.local"的时候会得到mypod的IP,这里我们可以看到区别是,Headless Service 不需要分配一个VIP,而是通过DNS访问的方式可以解析出带代理的Pod的IP
[root@k8s-master1 k8s]# kubectl apply -f eureka.yaml
ingress.extensions/eureka created
service/eureka created
statefulset.apps/eureka created
##因为是有状态应用,所以是一个一个的启动
[root@k8s-master1 k8s]# kubectl get po -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 5m37s
[root@k8s-master1 k8s]# kubectl get po -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 8m19s
eureka-1 1/1 Running 0 3m7s
eureka-2 1/1 Running 0 108s
[root@k8s-master1 k8s]# kubectl exec -it eureka-1 sh -n ms
/ # nslookup eureka
nslookup: can't resolve '(null)': Name does not resolve
Name: eureka
Address 1: 10.244.36.123 eureka-1.eureka.ms.svc.cluster.local ##通过headless的stateful部署的应用 就算IP变了之后,eureka-1.eureka.ms.svc.cluster.local这个也会自动的绑定新的IP上,且不会变!
Address 2: 10.244.169.176 eureka-2.eureka.ms.svc.cluster.local
Address 3: 10.244.159.171 eureka-0.eureka.ms.svc.cluster.local
StatefulSet+Headless DNS名称格式:
<statefulsetName-index>.<service-name> .<namespacename>.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.local
[root@k8s-master1 k8s]# kubectl get ing -n ms
NAME HOSTS ADDRESS PORTS AGE
eureka eureka.zhdya.cn 80 23m


部署gateway(线上环境务必部署至少要2个副本,测试):
# cat gateway.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gateway
namespace: ms
spec:
rules:
- host: gateway.zhdya.cn
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: 1
selector:
matchLabels:
project: ms
app: gateway
template:
metadata:
labels:
project: ms
app: gateway
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: gateway
image: 192.168.171.10/microservice/gateway:2020-01-12-13-14-44
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
[root@k8s-master1 k8s]# kubectl apply -f gateway.yaml
ingress.extensions/gateway created
service/gateway created
deployment.apps/gateway created
部署portal:
# cat portal.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: portal
namespace: ms
spec:
rules:
- host: portal.zhdya.cn
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: 1
selector:
matchLabels:
project: ms
app: portal
template:
metadata:
labels:
project: ms
app: portal
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: portal
image: 192.168.171.10/microservice/portal:2020-01-12-13-15-06
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
# kubectl apply -f portal.yaml
ingress.extensions/portal created
service/portal created
deployment.apps/portal created
[root@k8s-master1 k8s]# kubectl get po -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 39m
eureka-1 1/1 Running 0 34m
eureka-2 1/1 Running 0 33m
gateway-5b6b78b54c-vjq2l 1/1 Running 0 10m
order-58cc95cf96-4t9pb 1/1 Running 0 2m35s
portal-5574cbd9d6-56tzc 1/1 Running 0 8m53s
product-74bb9d98d-8hz95 1/1 Running 0 2m42s
stock-845f745db5-q868l 1/1 Running 0 2m39s
查看部署情况:
六、总结:
- 第一步:熟悉Spring Cloud微服务项目
- 第二步:源代码编译构建
- 第三步:构建项目镜像并推送到镜像仓库
- 第四步:在K8S中部署Spring Cloud微服务项目的逻辑架构
- 第五步: K8S服务编排
- 第六步:在K8S中部署Eureka集群(注册中心)
- 第七步:部署微服务网关服务
- 第八步:部署微服务业务程序
- 第九步:部署微服务前端
- 第十步:微服务扩容与发布
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!