目录

实现思路

需要扩展的类型

核心代码

KubernetesApiGroup

KubernetesKind

KubernetesKindProperties

 KubernetesHandler

效果图:

deploy

patch

更复杂的类型


Spinnaker的clouddriver对kubernetes支持本质是将UI入参转化到代码最终转换成本地kuber命令来实现的,因为其是通过“白名单”的策略来实现的,所以对k8s的支持比较有限,不在白名单内的资源类型是无法被spinnaker执行的。像Istio这么火的云原生技术竟然在spinnaker体系中应用不起来,这是不能容忍的,所以经过对clouddriver代码的研究终于找到了破解之法。

实现思路

既然是“白名单”策略,那我们就按照clouddriver框架仿照其它资源把该实现的接口和方法实现掉好了。

需要扩展的类型

先看下跟spinnaker有关涉及到的istio类型吧。

 其中绿色部分跟部署计算节点相关,这部分spinnaker已经很完美的实现了,我们需要解决的是入口蓝色部分和网格内流量调度黄色部分,因为这些部分是我们自动化发布流程金丝雀部分需要用到的。 

核心代码

spinnaker中对于k8s的支持核心有4个类:KubernetesApiGroup、KubernetesKind、KubernetesKindProperties、KubernetesHandler

KubernetesApiGroup

Spinnaker中支持的k8sAPIGroup的白名单,所以为了支持istio我们需要添加

public static final KubernetesApiGroup NETWORKING_ISTIO_IO = new KubernetesApiGroup("networking.istio.io");

KubernetesKind

Spinnaker中支持的k8s资源类型的白名单,如果manifest的kind不在这个白名单中spinnaker是无法管理的。

  private static KubernetesKind createWithAlias(
      String name, @Nullable String alias, @Nullable KubernetesApiGroup apiGroup) {
    KubernetesKind kind = new KubernetesKind(name, apiGroup);
    aliasMap.put(kind, kind);
    if (alias != null) {
      aliasMap.put(new KubernetesKind(alias, apiGroup), kind);
    }
    return kind;
  }

Kind有3个属性,第一个对应资源类型也就是manifest中的kind;第二个是别名,非必填;第三个是api组,对应前面的KubernetesApiGroup
这里我们需要补充spinnaker中用到的3个跟istio相关的kind:

  public static final KubernetesKind VIRTUAL_SERVICE =
    createWithAlias(
      "virtualService", null, KubernetesApiGroup.NETWORKING_ISTIO_IO);
  public static final KubernetesKind DESTINATION_RULE =
    createWithAlias(
      "destinationRule", null, KubernetesApiGroup.NETWORKING_ISTIO_IO);
  public static final KubernetesKind GATEWAY =
    createWithAlias(
      "gateway", null, KubernetesApiGroup.NETWORKING_ISTIO_IO);

KubernetesKindProperties

类型相关的配置

  private KubernetesKindProperties(
      KubernetesKind kubernetesKind, boolean isNamespaced, boolean hasClusterRelationship) {
    this.kubernetesKind = kubernetesKind;
    this.isNamespaced = isNamespaced;
    this.hasClusterRelationship = hasClusterRelationship;
  }

 kubernetesKind对应前面的KubernetesKind;isNamespaced表示资源是否受命名空间的隔离约束,除开高级的管理资源类型基本都是false;hasClusterRelationship这个没看明道,我看现在的类型都是false。
这里我们需要补充istio相关的kindProperties:

new KubernetesKindProperties(KubernetesKind.GATEWAY, true, false),
new KubernetesKindProperties(KubernetesKind.DESTINATION_RULE, true, false),
new KubernetesKindProperties(KubernetesKind.VIRTUAL_SERVICE, true, false),

 KubernetesHandler

对每种kind的自定义部分,每个kind必须配备一个handler,以KubernetesVirtualServiceHandler为例:

package com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler;

import com.netflix.spinnaker.clouddriver.kubernetes.description.SpinnakerKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest;
import com.netflix.spinnaker.clouddriver.kubernetes.v2.model.Manifest;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

@Component
public class KubernetesVirtualServiceHandler extends KubernetesHandler {
  @Override
  public int deployPriority() {
    return DeployPriority.MOUNTABLE_DATA_PRIORITY.getValue();
  }

  @Nonnull
  @Override
  public KubernetesKind kind() {
    return KubernetesKind.VIRTUAL_SERVICE;
  }

  @Override
  public boolean versioned() {
    return false;
  }

  @Nonnull
  @Override
  public SpinnakerKind spinnakerKind() {
    return SpinnakerKind.CONFIGS;
  }

  @Override
  public Manifest.Status status(KubernetesManifest manifest) {
    return Manifest.Status.defaultStatus();
  }

  @Override
  protected KubernetesV2CachingAgentFactory cachingAgentFactory() {
    return KubernetesCoreCachingAgent::new;
  }
}

deployPriority()定义kind执行时的优先级,因为istio相关的都属于配置类的,所以我们可以参考configMap的优先级。
versioned()定义kind执行时是否有版本的概念,方便历史记录管理和回滚。
spinnakerKind()定义的是在spinnaker中的归类,用于前端deck加载,像pod定义成instance、ingress定义成loadBalancer,istio中如果不做展示可以把资源定义成config,如果前端要看到他们的关联关系就要严格按照spinnaker的类型来定义了。

  INSTANCES("instances"),
  CONFIGS("configs"),
  SERVER_GROUPS("serverGroups"),
  LOAD_BALANCERS("loadBalancers"),
  SECURITY_GROUPS("securityGroups"),
  SERVER_GROUP_MANAGERS("serverGroupManagers"),
  UNCLASSIFIED("unclassified");

 status(KubernetesManifest manifest)和cachingAgentFactory()我们也沿用configMap的,主要用于状态判断和缓存管理。

效果图:

deploy

执行结果

patch

patch时需要注意因为istio是CRD实现的,默认的type(strategic)会报错,所以patch时type要选择merge

 

更复杂的类型

很幸运istio的这些KubernetesKind不需要关心执行结果,像deployment这样的就复杂多了,因为spinnaker不仅需要关心kuber apply命令的结果,还需要去判断有没有达到预期。

private Status status(V1Deployment deployment) {
    V1DeploymentStatus status = deployment.getStatus();
    if (status == null) {
      return Status.noneReported();
    }

    if (!generationMatches(deployment, status)) {
      return Status.defaultStatus().unstable(UnstableReason.OLD_GENERATION.getMessage());
    }

    List<V1DeploymentCondition> conditions =
        Optional.ofNullable(status.getConditions()).orElse(ImmutableList.of());

    Status result = Status.defaultStatus();
    getPausedReason(conditions).ifPresent(result::paused);
    getUnavailableReason(conditions)
        .ifPresent(reason -> result.unstable(reason).unavailable(reason));
    getFailedReason(conditions).ifPresent(result::failed);
    checkReplicaCounts(deployment, status)
        .ifPresent(reason -> result.unstable(reason.getMessage()));
    return result;
  }
  注意checkReplicaCounts()这个地方就是去检查有没有开出理想中的pod数量。

 

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐