更新时间:2025-04-30 GMT+08:00
使用kubernetes官方Java SDK访问CCI
本节将介绍如何将CCI认证工具cci-iam-authenticator与kubernetes-client/java结合使用以调用API。
安装cci-iam-authenticator
下载安装及设置cci-iam-authenticator。
安装kubernetes-client/java
详情请参考Installation 。
当前CCI服务开放的API对应的Kubernetes版本为1.19,根据Versioning-and-Compatibility,推荐使用的SDK版本为11.0.2及以上。
如果要使用GenericKubernetesClient(参考Code-Examples),则需要9.0.0+以上版本
使用Java SDK
示例已通过以下版本的测试:
- 11.0.2
将以下依赖添加到项目的POM文件中:
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>11.0.2</version>
</dependency>
通过kubeconfig配置文件创建ApiClient
public class CommonCases {
// ...
public static void main(String[] args) throws IOException, ApiException, InterruptedException {
// file path to your KubeConfig
String kubeConfigPath = "<path to kubeconfig>";
// loading the out-of-cluster config, a kubeconfig from file-system
File file = new File(kubeConfigPath);
KubeConfig config = KubeConfig.loadKubeConfig(new FileReader(file));
config.setFile(file);
ApiClient client = buildClient(config).build();
// ...
}
public static ClientBuilder buildClient(KubeConfig config) throws IOException {
final ClientBuilder builder = new ClientBuilder();
String server = config.getServer();
if (!server.contains("://")) {
if (server.contains(":443")) {
server = "https://" + server;
} else {
server = "http://" + server;
}
}
builder.setVerifyingSsl(config.verifySSL());
builder.setBasePath(server);
builder.setAuthentication(new CCIKubeconfigAuthentication(config));
return builder;
}
}
CCIKubeconfigAuthentication.java
package com.huawei.demos;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.util.KubeConfig;
import io.kubernetes.client.util.credentials.Authentication;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.time.Instant;
/**
* Uses a {@link KubeConfig} to configure {@link ApiClient} authentication to the CCI Kubernetes API.
*
* <p> Only try to use AccessTokenAuthentication mechanisms, which is enough for CCI.
*/
public class CCIKubeconfigAuthentication implements Authentication, Interceptor {
private static final Logger log = LoggerFactory.getLogger(CCIKubeconfigAuthentication.class);
private final KubeConfig config;
private String token;
private Instant expiry;
public CCIKubeconfigAuthentication(final KubeConfig config) throws IOException {
this.config = config;
this.expiry = Instant.MIN;
}
private String getToken() {
// get access token every 600 seconds
if (Instant.now().isAfter(this.expiry)) {
log.debug("Token expired, get new one from kubeconfig");
this.token = config.getAccessToken();
if (this.token != null) {
this.expiry = Instant.now().plusSeconds(600);
}
}
return this.token;
}
@Override
public void provide(ApiClient client) {
OkHttpClient httpClient = client.getHttpClient().newBuilder().addInterceptor(this).build();
client.setHttpClient(httpClient);
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request authRequest;
authRequest = request.newBuilder().header("Authorization", "Bearer " + getToken()).build();
return chain.proceed(authRequest);
}
}
访问CCI服务
public class CommonCases {
// ...
private static void createNamespace(CoreV1Api api) throws ApiException {
String enableK8sRbac = "false";
String flavor = "general-computing";
String warmPoolSize = "10";
Map<String, String> labels = new HashMap<>();
labels.put("rbac.authorization.cci.io/enable-k8s-rbac", enableK8sRbac);
Map<String, String> annotations = new HashMap<>();
annotations.put("namespace.kubernetes.io/flavor", flavor);
annotations.put("network.cci.io/warm-pool-size", warmPoolSize);
V1Namespace namespace = new V1Namespace()
.metadata(new V1ObjectMeta().name(NAMESPACE).labels(labels).annotations(annotations));
LOGGER.info("start to create namespace {}", NAMESPACE);
api.createNamespace(namespace, null, null, null);
LOGGER.info("namespace created");
}
private static void createNetwork(GenericKubernetesApi<Network, NetworkList> networkApi) throws ApiException {
String name = NETWORK;
String projectID = "<账号ID,可以在我的凭证获取>";
String domainID = "<项目ID,可以在我的凭证获取>";
String securityGroupID = "<安全组ID,可以在安全组控制台获取>";
String availableZone = "<az名称,例如cn-north-1a、cn-north-4a或cn-east-3a>";
String vpcID = "虚拟私有云的ID,可在VPC控制台获取";
String cidr = "<子网网段,例如192.168.128.0/18>";
String networkID = "<子网的网络ID,可在VPC控制台 > 子网中获取>";
String subnetID = "<子网ID,可在VPC控制台 > 子网获取>";
String networkType = "underlay_neutron";
Map<String, String> annotations = new HashMap<>();
annotations.put("network.alpha.kubernetes.io/default-security-group", securityGroupID);
annotations.put("network.alpha.kubernetes.io/domain-id", domainID);
annotations.put("network.alpha.kubernetes.io/project-id", projectID);
Network network = new Network()
.metadata(new V1ObjectMeta().name(name).namespace(NAMESPACE).annotations(annotations))
.spec(new NetworkSpec()
.availableZone(availableZone)
.cidr(cidr)
.attachedVPC(vpcID)
.networkID(networkID)
.networkType(networkType)
.subnetID(subnetID));
LOGGER.info("start to create network {}/{}", NAMESPACE, name);
networkApi.create(network).throwsApiException();
LOGGER.info("network created");
}
private static void waitNamespaceActive(CoreV1Api api) throws ApiException, InterruptedException {
for (int i = 0; i < 5; i++) {
V1Namespace ns = api.readNamespace(NAMESPACE, null, null, null);
if (ns.getStatus() != null && NAMESPACE_ACTIVE.equals(ns.getStatus().getPhase())) {
return;
}
Thread.sleep(WAIT_ACTIVE_MILLIS);
}
throw new IllegalStateException("namespace not active");
}
private static void waitNetworkActive(GenericKubernetesApi<Network, NetworkList> networkApi) throws ApiException, InterruptedException {
for (int i = 0; i < 5; i++) {
Network network = networkApi.get(NAMESPACE, NETWORK).throwsApiException().getObject();
if (network.getStatus() != null && NETWORK_ACTIVE.equals(network.getStatus().getState())) {
return;
}
Thread.sleep(WAIT_ACTIVE_MILLIS);
}
throw new IllegalStateException("network not active");
}
private static void createDeployment(AppsV1Api api) throws ApiException {
String app = APP;
String cpu = "500m";
String memory = "1024Mi";
String containerName = "container-0";
String image = "library/nginx:stable-alpine-perl";
Map<String, Quantity> limits = new HashMap<>();
limits.put("cpu", Quantity.fromString(cpu));
limits.put("memory", Quantity.fromString(memory));
V1Container container = new V1Container()
.name(containerName)
.image(image)
.resources(new V1ResourceRequirements().limits(limits).requests(limits));
Map<String, String> labels = new HashMap<>();
labels.put("app", app);
V1PodTemplateSpec podTemplateSpec = new V1PodTemplateSpec()
.spec(new V1PodSpec()
.priority(0)
.imagePullSecrets(Collections.singletonList(new V1LocalObjectReference().name("imagepull-secret")))
.containers(Collections.singletonList(container)))
.metadata(new V1ObjectMeta().labels(labels));
V1Deployment deployment = new V1Deployment()
.metadata(new V1ObjectMeta().name(app))
.spec(new V1DeploymentSpec()
.replicas(2)
.selector(new V1LabelSelector().matchLabels(labels))
.template(podTemplateSpec));
LOGGER.info("start to create deployment {}/{}", NAMESPACE, APP);
api.createNamespacedDeployment(NAMESPACE, deployment, null, null, null);
LOGGER.info("deployment created");
}
private static void getDeployment(AppsV1Api api) throws ApiException {
V1Deployment deployment = api.readNamespacedDeployment(APP, NAMESPACE, null, null, null);
LOGGER.info("deployment metadata: {}", deployment.getMetadata());
}
private static void deleteDeployment(AppsV1Api api) throws ApiException {
LOGGER.info("start to delete deployment");
api.deleteNamespacedDeployment(APP, NAMESPACE, null, null, null, null, null, null);
LOGGER.info("deployment deleted");
}
private static void deleteNamespace(CoreV1Api api) throws ApiException {
LOGGER.info("start to delete namespace: {}", NAMESPACE);
api.deleteNamespace(NAMESPACE, null, null, null, null, null, null);
LOGGER.info("namespace deleted");
}
}
FAQ
问:以上示例是否适用于其他版本的kubernetes-client/java?
答:由于以上示例使用了GenericKubernetesClient(参考Code-Examples,需要9.0.0+以上版本),所以不适用9.0.0以下版本SDK。
另外由于不同版本SDK之间存在一定差别,上述示例代码需要做一些细微调整才能适用于不同版本SDK,请自行调试。