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
#### 2.2、部署须知
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、架构介绍:

mark
  • 用户访问==》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
其中一个service的dockerfile
[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

mark

五、部署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文件实现。

mark

这里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
mark

mark

部署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

查看部署情况: mark

六、总结:

  • 第一步:熟悉Spring Cloud微服务项目
  • 第二步:源代码编译构建
  • 第三步:构建项目镜像并推送到镜像仓库
  • 第四步:在K8S中部署Spring Cloud微服务项目的逻辑架构
  • 第五步: K8S服务编排
  • 第六步:在K8S中部署Eureka集群(注册中心)
  • 第七步:部署微服务网关服务
  • 第八步:部署微服务业务程序
  • 第九步:部署微服务前端
  • 第十步:微服务扩容与发布