目录
-
- 前言
- 一、基于fabric8io操作pod
-
- [1.1 yaml创建pod](#1.1 yaml创建pod)
- [1.2 fabric8io创建pod案例](#1.2 fabric8io创建pod案例)
- 二、基于fabric8io创建Service(含Deployment)
-
- [2.1 yaml创建Service和Deployment](#2.1 yaml创建Service和Deployment)
- [2.2 fabric8io创建service案例](#2.2 fabric8io创建service案例)
- 三、基于fabric8io操作Volume
-
- [3.1 yaml配置挂载存储卷](#3.1 yaml配置挂载存储卷)
- [3.2 基于fabric8io实现挂载存储卷](#3.2 基于fabric8io实现挂载存储卷)
前言
一开始了解到Java Api库操作k8s集群,有两个,分别为:
但个人对比使用了两个发现,还是fabric8io更易用 ,用的人多是有道理的,fabric8io和yaml文件十分贴切 ,所以通俗易懂。本文前提是已配置好集群,已经熟悉了kubectl工具常用命令。
首先,需要导入fabric8io依赖核心库,如下:
xml
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>6.3.1</version>
</dependency>
注:本文采用6.3.1版本,截止2023年12月14日,官方最新版本为6.9.2版本,如果你k8s是最新版本,那么可考虑最新版本。
如果你需要看官方api操作文档,可参考:官方使用说明
如果你初学k8s,关于k8s的基本概念和常用操作不熟 ,强推先读 另一个博主的文章 :Kubernetes核心概念及命令使用
一、基于fabric8io操作pod
定义:pod 是包含一个或多个容器的容器组,是 Kubernetes 中创建和管理的最小对象。
1.1 yaml创建pod
如果我们在k8s集群上创建一个pod,常常会编写yaml文件,例如deploy.yaml
:
yaml
apiVersion: v1
kind: Pod
metadata:
name: cm-nginx
labels:
app: cm-nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
编写好后执行:kubectl apply -f deploy.yaml
,就会创建一个pod
1.2 fabric8io创建pod案例
首先,需要在k8s集群master上获取到/root/.kube/config
文件,文件内容大致如下,证书密钥太长,省略,这个文件记录了连接k8s集群的信息,fabric8io操作集群就需要该文件。
注:只要你需要连接集群,就需要基于config文件,下载下来
yaml
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0t..............
server: https://192.168.20.130:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ................
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVk.................
将config文件下载到本地,就可以对pod进行CRUD操作,本文放在resources目录下
java
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
public class PodCrudExample {
public static void main(String[] args) {
String kubeConfigPath = "D:\\IDEAProject\\k8sApi\\src\\main\\resources\\config";
KubernetesClient client;
try {
client = new DefaultKubernetesClient(Config.fromKubeconfig(FileUtils.readFileToString(new File(kubeConfigPath), StandardCharsets.UTF_8)));
// 创建 Pod
// createPod(client);
// 读取 Pod
// readPod(client);
// 更新 Pod
// updatePod(client);
// 删除 Pod
// deletePod(client);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void createPod(KubernetesClient client) {
// 创建 Pod 对象
Pod pod = new PodBuilder()
.withNewMetadata().withName("my-pod-nginx").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("nginx")
.withImage("nginx:latest")
.endContainer()
.endSpec()
.build();
// 在指定的命名空间中创建 Pod
client.pods().inNamespace("default").resource(pod).create();
System.out.println("Pod created successfully.");
}
private static void readPod(KubernetesClient client) {
// 读取 Pod
Pod pod = client.pods().inNamespace("default").withName("my-pod-nginx").get();
System.out.println("Pod read successfully:");
System.out.println(pod);
}
private static void updatePod(KubernetesClient client) {
// k8s禁止直接对容器中镜像进行更新操作,k8s更新本质还是删除原有的,然后根据配置创建新的
// 删除旧的 Pod
client.pods().inNamespace("default").withName("my-pod-nginx").delete();
// 创建新的 Pod 对象
Pod pod = new PodBuilder()
.withNewMetadata().withName("my-pod-tomcat").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("tomcat")
.withImage("tomcat:latest")
.endContainer()
.endSpec()
.build();
// 在指定的命名空间中创建新的 Pod
client.pods().inNamespace("default").resource(pod).create();
System.out.println("Pod updated successfully.");
}
private static void deletePod(KubernetesClient client) {
// 删除 Pod,默认关闭期限30秒,即最多等待30秒
// 这涉及k8s提供的优雅终止机制,允许容器有时间完成必要的清理工作
client.pods().inNamespace("default").withName("my-pod-tomcat").delete();
}
}
上面是创建一个普通的pod,无自愈、容灾等能力,一般我们会用deployment方式创建,deployment创建会在第二节【基于fabric8io创建Service】一并写到,因为Service和Deployment一般是一起使用的
二、基于fabric8io创建Service(含Deployment)
概念 :网络访问 归Service管理,它用于定义一组 Pod 并提供这些 Pod 的稳定访问点,主要用于服务发现和负载均衡。
Service 可以有不同的类型,其中最常见 的两种类型是 ClusterIP
(默认)和 NodePort
。
- 当你创建一个 Service,类型为
ClusterIP
时,K8s 会为该 Service 分配一个集群地址(集群内部使用,外部不可见) NodePort
类型的 Service 具有ClusterIP
的所有特性,同时还会在每个集群节点 上映射一个静态端口(NodePort),这使得外部流量可以通过任何集群节点的 NodePort 访问 Service(外部可访问)
2.1 yaml创建Service和Deployment
(1)先用Deployment创建两个pod副本,deploy_dep.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-dep
name: my-dep
spec:
replicas: 2 # 副本数
selector:
matchLabels:
app: my-dep
template: # 创建pod模板
metadata:
labels:
app: my-dep
spec:
containers:
- image: nginx
name: nginx
(2)创建Service,deploy_svc.yaml
yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: my-dep
name: my-svc
spec:
selector:
app: my-dep # 表示选择只代理具有标签(键值对)app: my-dep的pod
ports:
- port: 8000 # 表示 Service 在集群内部监听的端口
targetPort: 80 # 表示将 Service 接收到的流量转发到 Pod 的 80 端口
type: NodePort
kubectl apply -f xxx.yaml
,先后执行Deployment和Service
如图,访问时会轮巡方式,将接收到的流量转发到对应两个pod的80端口(targetPort)
由于配置的是NodePort,所以在浏览器输入任意节点ip :暴露的端口号,官方规定了NodePort范围在 30000-32767
之间,这里分配的是32385,那么公网通过该端口可以访问到集群,公网的流量都会经过NodePort暴露的32385端口,转发到targetPort,即80端口。
总结:Service就像一个网关,只负责网络流量的分配和转发,比如将流量转发到部署的两个pod中,两个pod中目前部署的都是nginx容器。
2.2 fabric8io创建service案例
java
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.*;
import io.fabric8.kubernetes.client.*;
import io.fabric8.kubernetes.client.Config;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class KubernetesDeployer {
public static void main(String[] args) {
String kubeConfigPath = "D:\\IDEAProject\\k8sApi\\src\\main\\resources\\config";
KubernetesClient client;
try {
client = new DefaultKubernetesClient(Config.fromKubeconfig(FileUtils.readFileToString(new File(kubeConfigPath), StandardCharsets.UTF_8))).inNamespace("default");
// 1、创建 Deployment(2个pod)
Map<String, String> labels = new HashMap();
labels.put("app", "my-dep-fabric8");
Deployment deployment = new DeploymentBuilder()
.withNewMetadata()
.withName("my-dep-fabric8")
.withLabels(labels)
.endMetadata()
.withNewSpec()
.withReplicas(2)
.withNewSelector()
.withMatchLabels(labels)
.endSelector()
.withNewTemplate()
.withNewMetadata()
.withLabels(labels)
.endMetadata()
.withNewSpec()
.withContainers(
new ContainerBuilder()
.withName("mynginx")
.withImage("nginx")
.build()
)
.endSpec()
.endTemplate()
.endSpec()
.build();
client.apps().deployments().inNamespace("default").resource(deployment).create();
// 2、创建 Service
Service service = new ServiceBuilder()
.withNewMetadata()
.withName("my-svc-fabric8")
.withLabels(labels) // 代理具有labels标签的pod
.endMetadata()
.withNewSpec()
.withSelector(labels)
.withPorts(new ServicePortBuilder()
.withPort(8000) // 集群内部监听的端口
.withNewTargetPort(80) // 流量转发的目标端口
.build())
.withType("NodePort")
.endSpec()
.build();
client.services().inNamespace("default").resource(service).create();
System.out.println("Service Create Successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建后,设置了NodePort,故用Ip+NodePort可以公网访问,但是两个nginx默认index.html都是Welcome to nginx,可以用echo "11112222" > index.html
改动一个nginx容器的index.html页面。
注:用kubectl exec -it pod_name -- /bin/bash
进入容器,默认这个页面就在/usr/shared/nginx/html/
下。
三、基于fabric8io操作Volume
概念 :存储卷(Volume)分为临时卷 和持久卷 ,在 Kubernetes 中,Volume
是一种抽象,用于表示容器中可以访问的存储。
PV(PersistentVolume)
持久卷是集群级别的资源,它是集群中的一块持久化存储资源,可以由多个 Pod 共享。PV 可以来自集群中的各种存储后端,比如云存储、网络存储、本地存储等。PV 与实际的存储资源相对应。
PVC(PersistentVolumeClaim)
PersistentVolumeClaim
是对持久化存储资源的声明 。它是一个请求,用于获取持久 Volume
的一部分或全部存储容量。PVC 允许开发者声明对存储资源的需求,而不用关心底层存储是如何实现的。
关系
PersistentVolumeClaim
是对存储资源的声明,而 Volume
是实际的存储资源 。开发者通过 PVC 声明存储需求,并请求系统提供符合这些需求的 PersistentVolume
(PV,实际存储卷)。
PV生命周期状态
Available(可用)、Bound(已绑定)、Released(已释放)、Failed(失败)
总结:即一个负责申明存储,一个是实际存储资源
3.1 yaml配置挂载存储卷
(1)创建一个 PersistentVolume
(PV),类型为 nfs
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-nfs-pv
spec:
capacity:
storage: 300Mi # 允许多个 Pod 以读写多模式访问,i是二进制
volumeMode: Filesystem
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
server: 192.168.20.133 # NFS 服务器地址
path: /nfs/data/shared/ # 共享的目录
(2)创建 PersistentVolumeClaim
(PVC)来请求使用这个 NFS 存储卷
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-nfs-pvc01
labels:
app: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi # Kubernetes 推荐使用二进制单位i,以避免混淆,确保一致性
storageClassName: nfs
(3)创建Pod(可多个)
yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mynginx
image: nginx
volumeMounts:
- name: pvc-html # 这里的 name 对应 volumes 中的 name
mountPath: "/mount/data"
volumes:
- name: pvc-html # 这里的 name 是存储卷的名称
persistentVolumeClaim:
claimName: my-nfs-pvc
注:普通方式直接创建,此处未用Deployment创建pod
一旦 PVC 与 PV 绑定,Pod 只需声明使用该 PVC,而不需要显式绑定到 PV,Pod就能共享pvc对应的pv存储卷资源。
解释 :如上,pv共享目录是/nfs/data/shared/
,pvc和pv绑定后,创建pod容器对应的mountPath为/mount/data
,那么/nfs/data/shared/
中的内容就会挂载到pod定义的路径/mount/data
中,实现多个pod共享存储卷pv中的内容。
为什么两个name要一致?(即例子pvc-html)
答 :volumeMounts
定义了存储卷挂载到容器中的路径,其中name
字段指定了与 volumes
中相应存储卷的名称。这样 K8s 就知道将哪个卷挂载到容器的哪个路径上。如果名字不一致,pod就不知道去找哪个存储卷挂载到对应路径。

3.2 基于fabric8io实现挂载存储卷
java
package com.yx.mount_volume;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
/**
* 挂载存储卷
*/
public class MountVolumeExample {
public static void main(String[] args) {
String kubeConfigPath = "D:\\IDEAProject\\k8sApi\\src\\main\\resources\\config";
KubernetesClient client;
try {
client = new DefaultKubernetesClient(Config.fromKubeconfig(FileUtils.readFileToString(new File(kubeConfigPath), StandardCharsets.UTF_8))).inNamespace("default");
// 1、创建 PersistentVolume
PersistentVolume nfsPv = new PersistentVolumeBuilder()
.withNewMetadata().withName("my-pv-fabric8").endMetadata()
.withNewSpec()
.withCapacity(Collections.singletonMap("storage", new Quantity("100Mi")))
.withAccessModes("ReadWriteMany")
.withPersistentVolumeReclaimPolicy("Retain")
.withStorageClassName("nfs")
.withNfs(new NFSVolumeSourceBuilder()
.withServer("192.168.20.133")
.withPath("/nfs/data/shared/")
.build())
.endSpec()
.build();
client.persistentVolumes().resource(nfsPv).create();
// 2、创建 PersistentVolumeClaim
PersistentVolumeClaim nfsPvc = new PersistentVolumeClaimBuilder()
.withNewMetadata()
.withName("my-pvc-fabric8")
.endMetadata()
.withNewSpec()
.withAccessModes("ReadWriteMany")
.withResources(new ResourceRequirementsBuilder()
.addToRequests("storage", new Quantity("80Mi"))
.build())
.withStorageClassName("nfs")
.endSpec()
.build();
client.persistentVolumeClaims().resource(nfsPvc).create();
// 3、创建 Pod,并挂载 PersistentVolumeClaim
Pod pod = new PodBuilder()
.withNewMetadata().withName("my-pod-fabric8").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("my-container")
.withImage("nginx")
.addNewVolumeMount()
.withName("pvc-fabric8")
.withMountPath("/mount/nginx/html/") // 挂载到的目录
.endVolumeMount()
.endContainer()
.addNewVolume()
.withName("pvc-fabric8")
.withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder()
.withClaimName("my-pvc-fabric8")
.build())
.endVolume()
.endSpec()
.build();
client.pods().inNamespace("default").resource(pod).create();
System.out.println("Pod created successfully.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上为全部内容!有问题可以评论区交流讨论噢!