2 | Spinnaker orca 如何找到对应的 stage

首先,我们定义的 Stage 都实现了 StageDefinitionBuilder 接口,例如:@Slf4j@Component@CompileStaticclass BakeStage implements StageDefinitionBuilder { public static fina

首先,我们定义的 Stage 都实现了 StageDefinitionBuilder 接口,例如:

@Slf4j
@Component
@CompileStatic
class BakeStage implements StageDefinitionBuilder {
  public static final String PIPELINE_CONFIG_TYPE = "bake"
  // ...
}

其次,看一个 bean StageResolver,它在初始化的时候,将所有的 StageDefinitionBuilder 作为集合传递进去。

@Bean
public StageResolver stageResolver(
    Collection<StageDefinitionBuilder> stageDefinitionBuilders,
    Optional<Collection<SimpleStage>> simpleStages,
    PluginManager pluginManager) {
  Collection<SimpleStage> stages = simpleStages.orElseGet(ArrayList::new);
  stages.addAll(pluginManager.getExtensions(SimpleStage.class));
  return new StageResolver(stageDefinitionBuilders, stages);
}

并将所有的 bean 存入一个 map 中

public StageResolver(
    Collection<StageDefinitionBuilder> stageDefinitionBuilders,
    Collection<SimpleStage> simpleStages) {
  for (StageDefinitionBuilder stageDefinitionBuilder : stageDefinitionBuilders) {
    stageDefinitionBuilderByAlias.put(stageDefinitionBuilder.getType(), stageDefinitionBuilder);
    for (String alias : stageDefinitionBuilder.aliases()) {
      if (stageDefinitionBuilderByAlias.containsKey(alias)) {
        throw new DuplicateStageAliasException("xxxooo");
      }

      stageDefinitionBuilderByAlias.put(alias, stageDefinitionBuilder);
    }
  }

  simpleStages.forEach(
      s -> stageDefinitionBuilderByAlias.put(s.getName(), new SimpleStageDefinitionBuilder(s)));
}

其中 map 的 key 与对应的 stage 的关系如下:

  1. type -> stage:stageDefinitionBuilderByAlias.put(stageDefinitionBuilder.getType(), stageDefinitionBuilder)
  2. alias -> stage:stageDefinitionBuilderByAlias.put(alias, stageDefinitionBuilder)

type 的默认实现是以 className 基础,将首字母小写后,去换掉结尾的 "Stage"、"StageDefinitionBuilder",处理完成后做为 type。代码如下:

/** @return the stage type this builder handles. */
default @Nonnull String getType() {
  return getType(this.getClass());
}

static String getType(Class<? extends StageDefinitionBuilder> clazz) {
  String className = clazz.getSimpleName();
  return className.substring(0, 1).toLowerCase()
      + className
          .substring(1)
          .replaceFirst("StageDefinitionBuilder$", "")
          .replaceFirst("Stage$", "");
}

alias 的获取方式是通过在 stage 的类上的注解:

default Collection<String> aliases() {
  if (getClass().isAnnotationPresent(Aliases.class)) {
    return Arrays.asList(getClass().getAnnotation(Aliases.class).value());
  }

  return Collections.emptyList();
}

最后,在 StartStageHandlerstage.plan() 方法中,会调用 StageResolvergetStageDefinitionBuilder() 方法。

@Override
public @Nonnull StageDefinitionBuilder builderFor(@Nonnull Stage stage) {
  return stageResolver.getStageDefinitionBuilder(
      stage.getType(), (String) stage.getContext().get("alias"));
}

在此方法中,会从 pipeline 相应的 stage 中,获取配置的 type 和 alias,并将它们作为 key,到 StageResolver 的 map stageDefinitionBuilderByAlias 中依次去 get。顺序是先用 type 去取,如果未取到,再用 alias 取到的 stage 对象。

@Nonnull
public StageDefinitionBuilder getStageDefinitionBuilder(@Nonnull String type, String typeAlias) {
  StageDefinitionBuilder stageDefinitionBuilder =
      stageDefinitionBuilderByAlias.getOrDefault(
          type, stageDefinitionBuilderByAlias.get(typeAlias));

  if (stageDefinitionBuilder == null) {
    throw new NoSuchStageDefinitionBuilderException(type, stageDefinitionBuilderByAlias.keySet());
  }

  return stageDefinitionBuilder;
}

因此,在编写一个 Stage 类时,需要做完如下两个件事:

  • 在 xxxStage 类中实现 getType() 方法以覆盖默认获取 type 的方法
  • 依靠 xxxStage 类的名称(需要与前端沟通好相应的 type 字段和 alias 字段)

前者比较明显易懂,后者不是特别容易理解,但是 spinnaker 里面大多数是以后者的形式出现的。

Read more

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd