1、说明
在K8s Paas平台搭建SpringCloud微服务框架,进行微服务开发。网关采用Zuul1、服务注册与发现采用Eureka。链路跟踪与日志采集采用Skywalking。Skywalking日志采集是Skywalking8.5.0版本之后的功能,Skywalking日志采集采用服务工程主动推送的方式,与logback集成,可以满足中小型系统的日志采集和分析,数据库可选用elasticsearch,能保证良好的搜索性能。
新建一个k8s命名空间进行实验:
Yaml:
apiVersion: v1
kind: Namespace
metadata:
??? name: sclouds
??? labels:
?????? name: sclouds
设置默认命名空间:
kubectl config set-context $(kubectl config current-context) --namespace=sclouds
2、Eureka容器集群
2.1.StatefulSet、Headless Service
Eureka搭建容器集群,需要依赖k8s的statefulset组件。Eureka-server的每个实例需要相互同步状态。
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现。
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),有序收缩,有序删除(即从N-1到0)。
使用StatefulSet,StatefulSet中每个Pod的DNS就是固定的,格式为
??? statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local
serviceName为Headless Service的名字0..N-1为Pod所在的序号,从0开始到N-1statefulSetName为StatefulSet的名字namespace为服务所在的namespace,Headless Service和StatefulSet必须在相同的namespacecluster.local为Cluster Domain
2.2.Ecureka Springboot镜像
集群由两个ecureka服务组成,服务工程基于Springboot开发,打包成jar包,最后打包成docker镜像在k8s上部署。
Sprinboot版本:2.6.2
SpringCloud版本:2021.0.1
pom.xml设置,引入spring-cloud-starter-netflix-eureka-server:
引入spring-boot-starter-web、spring-cloud-starter-netflix-eureka-server
? ? <dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>
? ? ? ? ? ? <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>
? ? ? ? ? ? <scope>test</scope>
? ? ? ? </dependency>
? ? </dependencies>
? ? <dependencyManagement>
? ? ? ? <dependencies>
? ? ? ? ? ? <dependency>
? ? ? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>
? ? ? ? ? ? ? ? <artifactId>spring-cloud-dependencies</artifactId>
? ? ? ? ? ? ? ? <version>${spring-cloud.version}</version>
? ? ? ? ? ? ? ? <type>pom</type>
? ? ? ? ? ? ? ? <scope>import</scope>
? ? ? ? ? ? </dependency>
? ? ? ? </dependencies>
</dependencyManagement>
application.properties属性设置:
spring.application.name=register-server
server.port=8000
management.server.port=8001
eureka.instance.leaseRenewalIntervalInSeconds=10
eureka.instance.leaseExpirationDurationInSeconds=30
eureka.instance.hostname=${MY_POD_NAME}.register-server
eureka.instance.prefer-ip-address=true
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true
eureka.client.service-url.defaultZone=http://register-server-0.register-server:8000/eureka/,http://register-server-1.register-server:8000/eureka/
eureka.client.service-url.registryFetchIntervalSeconds=10
eureka.client.service-url.disable-delta=true
eureka.server.evictionIntervalTimerInMs=4000
eureka.server.enable-self-preservation=false
Dockfile:
FROM java:8
VOLUME /tmp
ADD eurekaclu-0.0.1-SNAPSHOT.jar /eurekaclu-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/eurekaclu-0.0.1-SNAPSHOT.jar"]
2.3.k8s service服务暴露
使用2.2的镜像部署statefulset。Service关联暴露服务。为了方便访问,设置成nodePort类型,暴露外网端口。
statefulset:
apiVersion: apps/v1
kind: StatefulSet
metadata:
? name: register-server
? labels:
? ? service: register-server
spec:
? replicas: 2
? serviceName: register-server
? selector:
? ? matchLabels:
? ? ? service: register-server
? template:
? ? metadata:
? ? ? labels:
? ? ? ? service: register-server
? ? ? annotations:
? ? ? ? service: register-server
? ? spec:
? ? ?s:
? ? ? ? - name: register-server
? ? ? ? ? image: dw/eurekaclu-2
? ? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ? env:
? ? ? ? ? - name: MY_POD_NAME
? ? ? ? ? ? valueFrom:
? ? ? ? ? ? ? fieldRef:
? ? ? ? ? ? ? ? fieldPath: metadata.name
? ? ? ? ? ports:
? ? ? ? ? ? - name: http
? ? ? ? ? ? ?Port: 8000
? ? ? ? ? ? ? protocol: TCP
? ? ? ? ? readinessProbe:
? ? ? ? ? ? httpGet:
? ? ? ? ? ? ? path: /actuator/health
? ? ? ? ? ? ? port: 8001
? ? ? ? ? ? ? scheme: HTTP
? ? ? ? ? ? failureThreshold: 3
? ? ? ? ? ? initialDelaySeconds: 60
? ? ? ? ? ? periodSeconds: 10
? ? ? ? ? ? successThreshold: 1
? ? ? ? ? ? timeoutSeconds: 10
? ? ? ? ? resources:
? ? ? ? ? ? limits:
? ? ? ? ? ? ? # cpu: 100m
? ? ? ? ? ? ? memory: 1Gi
? ? ? ? ? ? requests:
? ? ? ? ? ? ? ? # cpu: 100m
? ? ? ? ? ? ? memory: 1Gi
? ? ? ? ? volumeMounts:
? ? ? ? ? - mountPath: /Charts
? ? ? ? ? ? name: data
? ? ? volumes:
? ? ? - name: data
? podManagementPolicy: "Parallel"
Service:
apiVersion: v1
kind: Service
metadata:
? name: register-server
? labels:
? ? service: register-server
spec:
? type: NodePort
? ports:
? ? - port: 8000
? ? ? targetPort: 8000
? ? ? protocol: TCP
? ? ? name: http
? ? ? nodePort: 30111
? selector:
? ? service: register-server
依次应用statefulset、service,部署两个statefulset:
这样可以得到两个ecurka-server的集群内url,和2.2节的属性设置对应:
register-server-0.register-server:8000
register-server-1.register-server:8000
导出nodePort,浏览器登录验证:
3.Zuul1容器集群
Zuul1是springcloud早期的服务网关,这里使用旧一点的稳定版本。
3.1.zuul1 springboot
Zuul也要注册到eureka,需要引入eureka-client和netflix-zuul。服务注册ecureka使用上一章搭建的集群,根据k8s服务url规则,ecureka服务的url为http://register-server.sclouds.svc.cluster.local:8000/eureka/,k8s服务进行负载分发到两个stateful实例
Springboot版本:2.1.8
SpringCloud版本:Greenwich.SR2
Pom.xml:
<dependency>
? ? <groupId>org.springframework.cloud</groupId>
? ? <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
? ? <groupId>org.springframework.cloud</groupId>
? ? <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
application.properties:
server.port=7005
spring.application.name=springboot-zuul-server
eureka.client.serviceUrl.defaultZone=http://register-server.sclouds.svc.cluster.local:8000/eureka/
zuul.stripPrefix=false
zuul.ignored-services="*"
zuul.routes.api-a.path=/ribbon/**
zuul.routes.api-a.serviceId=springboot-eureka-client
zuul.routes.api-a.strip-prefix=false
ribbon.eureka.enabled=true
eureka.instance.prefer-ip-address=true
注:这里设置一个路由规则,路由到springboot-eureka-client微服务(下文有例子)
Dockerfile:
FROM java:8
VOLUME /tmp
ADD ecurkazuul-0.0.1-SNAPSHOT.jar /ecurkazuul-0.0.1-SNAPSHOT.jar
ADD skagent /skagent
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/ecurkazuul-0.0.1-SNAPSHOT.jar"]
3.2.k8s service
Zuul集群实例不需要互相同步状态,部署Deployment即可。Service进行服务暴露负载
Yaml:
apiVersion: v1
kind: Service
metadata:
? name: ecurka-zuul
? namespace: sclouds
? labels:
? ? verison: "1.0.0"
? ? env: "test"
spec:
? type: NodePort
? selector:
? ? app: ecurka-zuul
? ? release: master
? ports:
? ? ? - name: http
? ? ? ? port: 7005
? ? ? ? targetPort: 7005
? ? ? ? nodePort: 30112
---
apiVersion: apps/v1
kind: Deployment
metadata:
? name: ecurka-zuul
? namespace: sclouds
? labels:
? ? verison: "1.0.0"
? ? env: "test"
spec:
? replicas: 2
? selector:
? ? matchLabels:
? ? ? app: ecurka-zuul
? ? ? release: master
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: ecurka-zuul
? ? ? ? release: master
? ? spec:
? ? ?s:
? ? ? ? - name: ecurka-zuul
? ? ? ? ? image: dw/ecurka-zuul
? ? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ? ports:
? ? ? ? ? ? - name: http
? ? ? ? ? ? ?Port: 7005
应用service,部署两个pod实例:
登录ecureka,看到有两个zuul服务实例在运行:
4、SkyWalking
这里简单介绍一下Windows单机部署
4.1.软件版本
Skywalking 8.7.0
下载地址:https://skywalking.apache.org/downloads/
Elasticsearch 6.8.23
下载地址:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-8-23
本文选择Elasticsearch 作为skywalking的数据库,skywalking与el的版本需要对应,如果使用Elasticsearch7,需要下载es后缀的skywalking版本。
4.2.elasticsearch
elasticsearch.yml修改:
4.3.Skywalking
分为两个服务,后台信息收集服务oap,前端门户ui(skywalking-webapp)
?OAP后台服务
config:application.yml
?UI门户工程
webapp.yml,如果端口被占用,可修改默认端口
4.4.服务启动
启动Elasticsearch
执行elasticsearch.bat,默认占用9200端口
启动skywalking
执行startup.bat,或者分别执行oapService.bat、webappService.bat,默认占用11800,12800,8080端口。
4.5.Springboot工程接入
Skywalking对于Java工程是非侵入的,通过探针进行接口探测,数据上报。只需要在启动后台服务时,加入启动参数,指定Skywalking agent依赖包以及oap服务Url,idea中可在工程启动配置中配置,有两个属性需要注意,skywalking.agent.service_name是skywalking中显示的服务名称,不同服务需要区分。skywalking.collector.backend_service,是skywalking服务端的地址,要根据实际情况修改。本文中,skywalking没有部署到k8s中,写的是skywalking部署主机的地址。
参考:
-javaagent:J:\tmppro\demo\skagent\skywalking-agent.jar -Dskywalking.agent.service_name=DemoHelloService -Dskywalking.collector.backend_service=192.168.76.54:11800
HTTP调用,Skywalking前端观测
4.6.springboot服务docker镜像打包修改
拷贝skywalking agent依赖到docker镜像打包目录
修改Dockerfile,加入加粗字体部分:
FROM java:8
VOLUME /tmp
ADD ecurkaclient-0.0.1-SNAPSHOT.jar /ecurkaclient-0.0.1-SNAPSHOT.jar
ADD skagent /skagent
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-javaagent:/skagent/skywalking-agent.jar","-Dskywalking.agent.service_name=EurekaClient","-Dskywalking.collector.backend_service=192.168.76.54:11800","-jar","/ecurkaclient-0.0.1-SNAPSHOT.jar"]
5、服务实例
5.1.springboot镜像
Java代码,设置两个url作为服务实验:
package com.example.ecurkaclient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
? ? private static final Logger log = LoggerFactory.getLogger(TestController.class);
? ? @RequestMapping(value = "/hello", method = RequestMethod.GET)
? ? public ResponseX testHello() {
? ? ? ? ResponseX x = new ResponseX();
? ? ? ? x.setMsg("hello-x");
? ? ? ? log.info("######################################\n" + "hello");
? ? ? ? return x;
? ? }
? ? @RequestMapping(value = "/helloworld", method = RequestMethod.GET)
? ? public ResponseX testHellox() {
? ? ? ? ResponseX x = new ResponseX();
? ? ? ? x.setMsg("hello-world-x");
? ? ? ? log.info("######################################\n" + "hello-world");
? ? ? ? return x;
? ? }
}
application.properties:
server.port=7004
spring.application.name=springboot-eureka-client
eureka.client.serviceUrl.defaultZone=http://register-server.sclouds.svc.cluster.local:8000/eureka/
eureka.instance.prefer-ip-address=true
注:spring.application.name和前面zuul网关设置的zuul.routes.api-a.serviceId要对应。Zuul网关根据服务名称进行路由。
Dockerfile打包镜像:
FROM java:8
VOLUME /tmp
ADD ecurkaclient-0.0.1-SNAPSHOT.jar /ecurkaclient-0.0.1-SNAPSHOT.jar
ADD skagent /skagent
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-javaagent:/skagent/skywalking-agent.jar","-Dskywalking.agent.service_name=EurekaClient","-Dskywalking.collector.backend_service=192.168.76.54:11800","-jar","/ecurkaclient-0.0.1-SNAPSHOT.jar"]
5.2.k8s service
Yaml:
apiVersion: v1
kind: Service
metadata:
? name: ecurka-client
? namespace: sclouds
? labels:
? ? verison: "1.0.0"
? ? env: "test"
spec:
? selector:
? ? app: ecurka-client
? ? release: master
? ports:
? ? ? - name: http
? ? ? ? port: 7004
? ? ? ? targetPort: 7004
---
apiVersion: apps/v1
kind: Deployment
metadata:
? name: ecurka-client
? namespace: sclouds
? labels:
? ? verison: "1.0.0"
? ? env: "test"
spec:
? replicas: 2
? selector:
? ? matchLabels:
? ? ? app: ecurka-client
? ? ? release: master
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: ecurka-client
? ? ? ? release: master
? ? spec:
? ? ?s:
? ? ? ? - name: ecurka-client
? ? ? ? ? image: dw/ecurka-client
? ? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ? ports:
? ? ? ? ? ? - name: http
? ? ? ? ? ? ?Port: 7004
6、日志采集(SkyWalking)
logback设置GRPCLogClientAppender,设置日志级别:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
? ? <appender name="msystem-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
? ? ? ? <!-- 日志输出编码 -->
? ? ? ? <encoder>
? ? ? ? ? ? <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
? ? ? ? ? ? <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
? ? ? ? </encoder>
? ? </appender>
? ? <root level="info">
? ? ? ? <appender-ref ref="msystem-log"/>
? ? </root>
</configuration>
完成上述配置,日志就会上传到skywalking
log.info("######################################\n" + "hello-world");