1渴庆、項(xiàng)目介紹
本文章,旨在使用K8S中自身的服務(wù)發(fā)現(xiàn)功能刃滓,不使用其他的服務(wù)發(fā)現(xiàn)組件咧虎,通過(guò) Spring 的 spring-cloud-kubernetes 來(lái)搭建SpringCloud項(xiàng)目计呈。
2、k8s service概述
在介紹Svc之前总寒,首先簡(jiǎn)單說(shuō)明下Kubernetes中Pod概念尉间。
Pod是Kubernetes非常重要的基本概念哲嘲,代表著集群中運(yùn)行的進(jìn)程媳禁,Pod中封裝著應(yīng)用的容器(有情況下是多個(gè)容器)竣稽。
Kubernetes為每個(gè)Pod都分配了唯一的Ip地址,稱為Pod Ip娃弓,一個(gè)Pod里的多個(gè)容器共享Pod Ip地址台丛,這樣客戶端可以通過(guò)Pod Ip+ 容器端口來(lái)訪問(wèn)Pod(Pod Ip+ 容器端口砾肺,又被稱為Endpoint变汪,也是Kubernetes一種資源)。
我們知道Kubernetes在自動(dòng)資源調(diào)度時(shí)实胸,會(huì)頻繁的銷毀與創(chuàng)建番官,這樣就會(huì)導(dǎo)致Pod Ip會(huì)頻繁的變動(dòng)鲤拿,客戶端幾乎不可能以Pod Ip+端口直接訪問(wèn)Pod,這時(shí)候就需要Kubernetes的另一種資源來(lái)實(shí)現(xiàn)生音,這就是SVC缀遍。
SVC服務(wù)是Kubernetes里的核心資源對(duì)象之一,其實(shí)可以理解成我們微服務(wù)架構(gòu)中的一個(gè)微服務(wù)台谊。SVC一旦被創(chuàng)建锅铅,Kubernetes就會(huì)自動(dòng)為它分配一個(gè)可用的Cluster IP减宣,在svc的整個(gè)生命周期內(nèi),Cluster IP不會(huì)發(fā)生改變漆腌。
3贼邓、k8s 服務(wù)發(fā)現(xiàn)簡(jiǎn)介
任何分布式系統(tǒng)都會(huì)涉及“服務(wù)發(fā)現(xiàn)”這個(gè)基礎(chǔ)問(wèn)題,大部分分布式 系統(tǒng)都通過(guò)提供特定的API接口來(lái)實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)功能闷尿,但這樣做會(huì)導(dǎo)致 平臺(tái)的侵入性比較強(qiáng)塑径,也增加了開(kāi)發(fā)、測(cè)試的難度填具。Kubernetes則采用 了直觀樸素的思路去解決這個(gè)棘手的問(wèn)題统舀。
首先,每個(gè)Kubernetes中的Service都有唯一的Cluster IP及唯一的名 稱灌旧,而名稱是由開(kāi)發(fā)者自己定義的,部署時(shí)也沒(méi)必要改變枢泰,所以完全可 以被固定在配置中描融。接下來(lái)的問(wèn)題就是如何通過(guò)Service的名稱找到對(duì)應(yīng) 的Cluster IP。
目前衡蚂, Kubernetes上的大部分應(yīng)用都已經(jīng)采用了DNS這種新興的服務(wù)發(fā)現(xiàn)機(jī)制窿克。
4、項(xiàng)目搭建
下面我們就使用k8s中的原生服務(wù)發(fā)現(xiàn)功能毛甲,不使用其他的注冊(cè)組件年叮,來(lái)搭建在spring-cloud微服務(wù)架構(gòu),從而達(dá)到服務(wù)調(diào)用目的玻募。
現(xiàn)在對(duì)spring-cloud-kubernetes基本原理做簡(jiǎn)單介紹只损。通過(guò)上述描述,我們知道在K8S其實(shí)已經(jīng)維護(hù)了服務(wù)實(shí)例列表,每個(gè)服務(wù)對(duì)應(yīng)的Endpoint也保存在K8S集群etc數(shù)據(jù)庫(kù)中跃惫,所以spring-cloud-kubernetes所做的工作叮叹,就是在服務(wù)調(diào)用時(shí),找到找到服務(wù)的Endpoint爆存,從而完成服務(wù)調(diào)用蛉顽。我們發(fā)現(xiàn)spring-cloud-kubernetes也是通過(guò)實(shí)現(xiàn)DiscoveryClient接口,實(shí)現(xiàn)類KubernetesDiscoveryClient先较,具體源碼這里就不敘述了携冤。
4.1項(xiàng)目目錄:
- abc-gateway 網(wǎng)關(guān)服務(wù)者
- abc-service-provider 服務(wù)提供者
- abc-service-consumer 服務(wù)消費(fèi)者
這個(gè)三個(gè)項(xiàng)目都是非常簡(jiǎn)單的SpringBoot工程,但是都需要引用以下關(guān)鍵jar包闲勺,具體的版本跟隨SpringCloud版本即可曾棕。
<!-- 核心jar包,具體版本可參考官方網(wǎng)站-->
<!--服務(wù)間調(diào)用都是采用feign方式霉翔,所以需要加上相關(guān)jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
</dependency>
4.2項(xiàng)目調(diào)用流程:
4.3關(guān)鍵代碼
1睁蕾、abc-gateway 就是一個(gè)簡(jiǎn)單的springboot工程苞笨,pom文件添加相關(guān)jar包债朵,然后添加以下配置
# 配置文件配置
server:
port: 30000
spring:
application:
name: abc-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
kubernetes:
reload:
enabled: true
mode: polling
period: 5000
logging:
level:
org.springframework.cloud.gateway: debug
org.springframework.cloud.loadbalancer: debug
2、abc-service-provider與abc-service-consumer 也是一個(gè)簡(jiǎn)單的SpringBoot工程瀑凝,與通常的工程沒(méi)有區(qū)別序芦,唯一需要修改的就是在POM文件中,添加上上述jar包粤咪。
/**
* [abc-service-consumer] 啟動(dòng)類
* 與通常的SpringBoot工程沒(méi)有區(qū)別
*/
@Slf4j
@RestController
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class AbcServiceConsumerApplication {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private FeignDemo demo;
/**
* 通過(guò)Feign調(diào)用服務(wù)提供者[abc-service-provider]的接口
*/
@GetMapping("/get")
public String feignDemo(){
return demo.getMessage();
}
/**
* KubernetesDiscoveryClient核心類實(shí)現(xiàn)了DiscoveryClient
* 通過(guò)getServices()方法可以獲取k8s中的服務(wù)實(shí)例
*/
@GetMapping("/abc-service-consumer/index")
public String indexService() {
log.info("消費(fèi)服務(wù):abc-service-consumer");
List<String> services = discoveryClient.getServices();
services.forEach(System.out::println);
return "消費(fèi)服務(wù):abc-service-consumer";
}
public static void main(String[] args) {
SpringApplication.run(AbcServiceConsumerApplication.class, args);
}
}
------------
/**
*[abc-service-consumer]服務(wù)Feign調(diào)用類谚中,通過(guò)Feign調(diào)用服務(wù)提供者abc-service-provider
*/
@FeignClient(value = "abc-service-provider")
public interface FeignDemo {
@GetMapping("/get")
String getMessage();
}
/**
* [abc-service-provider]服務(wù)提供者啟動(dòng)類
*/
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class AbcServiceProviderApplication {
@GetMapping("/get")
public String get() {
return "服務(wù)提供者";
}
public static void main(String[] args) {
SpringApplication.run(AbcServiceProviderApplication.class, args);
}
}
3、K8S 中資源創(chuàng)建模板:
# 注意: 由于我是本地已經(jīng)搭建好K8S環(huán)境與gitlab的CICD,所以該文件中包含多個(gè)腳本變量寥枝。如果使用該文件宪塔,只需要替換成自己項(xiàng)目信息即可。
# PROJECT_NAME:SpringBoot工程的服務(wù)名稱
# REPLACE_IMAGE: Docker鏡像
# PROJECT_PORT: SpringBoot工程的服務(wù)端口
# 定義Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: PROJECT_NAME
labels:
app: PROJECT_NAME
spec:
replicas: 1
template:
metadata:
name: PROJECT_NAME
labels:
app: PROJECT_NAME
spec:
containers:
- name: PROJECT_NAME
image: REPLACE_IMAGE
ports:
- containerPort: PROJECT_PORT
imagePullPolicy: IfNotPresent
# 我使用的是阿里云的公共鏡像倉(cāng)庫(kù)囊拜。更簡(jiǎn)單的方式某筐,是將對(duì)應(yīng)的服務(wù)鏡像COPY到K8S的節(jié)點(diǎn)中。
# (COPY到從節(jié)點(diǎn)冠跷,K8S如果沒(méi)有設(shè)置南誊,是不會(huì)調(diào)度Pod到master節(jié)點(diǎn)上)
imagePullSecrets:
- name: regcred-aliyun
restartPolicy: Always
selector:
matchLabels:
app: PROJECT_NAME
---
# 定義SVC
apiVersion: v1
kind: Service
metadata:
name: PROJECT_NAME
spec:
selector:
app: PROJECT_NAME
ports:
- port: PROJECT_PORT
targetPort: PROJECT_PORT
nodePort: PROJECT_PORT
type: NodePort
5、項(xiàng)目部署流程
1蜜托、構(gòu)建鏡像抄囚。java代碼準(zhǔn)備好之后,使用DockerFile構(gòu)建好3個(gè)服務(wù)鏡像橄务,然后Copy到k8s的node節(jié)點(diǎn)幔托。使用命令docker images
查看鏡像
#如果想Pod能夠調(diào)度到master節(jié)點(diǎn),在master節(jié)點(diǎn)運(yùn)行
kubectl taint node k8s-master node-role.kubernetes.io/master-
#如果要恢復(fù)Master Only狀態(tài)蜂挪,執(zhí)行如下命令:
kubectl taint node k8s-master node-role.kubernetes.io/master=""
2重挑、構(gòu)建K8S服務(wù)迫肖。使用命令kubectl apply -f deployment文件名
啟動(dòng)3個(gè)服務(wù),使用命令kubectl get svc
查看攒驰。我們看到3個(gè)服務(wù)已經(jīng)成功啟動(dòng)蟆湖。
3、訪問(wèn)服務(wù)玻粪。因?yàn)镵8S中的服務(wù)是nodeport類型隅津,可以通過(guò)nodeIP來(lái)進(jìn)行訪問(wèn)。注意劲室,NodeIP與截圖中的Cluster-IP這個(gè)兩個(gè)IP有很大區(qū)別伦仍。樸素的數(shù),NodeIP是真實(shí)存在的IP很洋,是可以Ping通充蓝;而Cluster-IP是K8S獨(dú)有的,是虛擬IP喉磁,是Ping不通的谓苟。具體的大家可查看相關(guān)書(shū)籍。通過(guò)kubectl get node -o wide
可以查看到具體的NodeIP协怒,然后通過(guò)NodeIP來(lái)訪問(wèn)網(wǎng)關(guān)涝焙,從而驗(yàn)證結(jié)果。
4孕暇、驗(yàn)證結(jié)果仑撞。截圖中已經(jīng)打印出,服務(wù)提供者[abc-service-provider]中的信息妖滔。
6隧哮、項(xiàng)目總結(jié)
我們發(fā)現(xiàn)在使用spring-cloud-kubernetes
組件后,不依賴于其他的服務(wù)注冊(cè)組件座舍,可以在K8S集群中正常運(yùn)行沮翔。所以,對(duì)應(yīng)那些在K8S集群中部署的SpringCloud服務(wù)簸州,可以擺脫服務(wù)發(fā)現(xiàn)組件的限制鉴竭。但是對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),在開(kāi)發(fā)過(guò)程中本地的測(cè)試將是一個(gè)問(wèn)題岸浑,因?yàn)槲覀儼l(fā)現(xiàn)搏存,在項(xiàng)目啟動(dòng)過(guò)程中是要依賴K8S環(huán)境的,所以目前的spring-cloud-kubernetes
對(duì)開(kāi)發(fā)來(lái)說(shuō)并不是太友好矢洲,希望后續(xù)版本能解決這個(gè)痛點(diǎn)璧眠。
我們知道K8S的服務(wù)有自身的負(fù)載均衡功能,在使用spring-cloud-kubernetes
后,服務(wù)間調(diào)用的實(shí)質(zhì)是使用的Ribbon,Ribbon也是有負(fù)載均衡功能的责静,那么這兩者有沒(méi)有什么聯(lián)系呢袁滥?待我們下次討論。