Cartographerのインストールとサプライチェーン

[Cartographer](https://cartographer.sh)はKubernetes上のリソースを組み合わせて、ソフトウェアのデプロイに必要なサプライチェーンを構成することを可能にします。例えば、ソフトウェアのビルドに[kpack](https://buildpacks.io/docs/tools/kpack/)、ビルドしたイメージのデプロイに[Knative](https://knative.dev/)を利用してサプライチェーンを構成することで、Source-to-URLを実現することが可能です。

この記事では[Tanzu Community Edition(TCE)](https://tanzucommunityedition.io/)で作成したKubernetesクラスターにCartographerをインストールして、[チュートリアルにあるサプライチェーン](https://cartographer.sh/docs/v0.3.0/tutorials/first-supply-chain/)を構成してCartographerの基本的な使い方を確認してみます。

## Cartographerのインストール

TCEでは[Carvel(kapp)によるパッケージシステム](https://qiita.com/masanara/items/220eacf08e3e7f52f545)が採用されており、[パッケージリポジトリ](https://tanzucommunityedition.io/packages/)を追加することでKubernetesにソフトウェアを追加することが可能です。今回はパケッケージとしてCartographerをインストールするため、TCE Kubernetesクラスターにパッケージリポジトリを登録します。

```bash
$ tanzu package repository add tce-repo --url projects.registry.vmware.com/tce/main:0.11.0 --namespace tanzu-package-repo-global
```

パッケージリポジトリを登録すると、利用可能なパッケージを確認することができ、cartographerが含まれていることが確認できます。

```bash
$ tanzu package available list -n tanzu-package-repo-global
  NAME                                                 DISPLAY-NAME                 SHORT-DESCRIPTION
                                                         LATEST-VERSION
  app-toolkit.community.tanzu.vmware.com               App-Toolkit package for TCE  Kubernetes-native toolkit to support application lifecycle
                                                         0.1.0
  cartographer.community.tanzu.vmware.com              Cartographer                 Kubernetes native Supply Chain Choreographer.
                                                         0.2.2
  cert-injection-webhook.community.tanzu.vmware.com    cert-injection-webhook       The Cert Injection Webhook injects CA certificates and proxy environment variables into pods      
                                                         0.1.0
  cert-manager.community.tanzu.vmware.com              cert-manager                 Certificate management
                                                         1.6.1
  contour.community.tanzu.vmware.com                   contour                      An ingress controller
                                                         1.20.1
  external-dns.community.tanzu.vmware.com              external-dns                 This package provides DNS synchronization functionality.
                                                         0.10.0
  fluent-bit.community.tanzu.vmware.com                fluent-bit                   Fluent Bit is a fast Log Processor and Forwarder
                                                         1.7.5
  fluxcd-source-controller.community.tanzu.vmware.com  Flux Source Controller       The source-controller is a Kubernetes operator, specialised in artifacts acquisition from external sources such as Git, Helm repositories and S3 buckets.  0.21.2
  gatekeeper.community.tanzu.vmware.com                gatekeeper                   policy management
                                                         3.7.0
  grafana.community.tanzu.vmware.com                   grafana                      Visualization and analytics software
                                                         7.5.11
  harbor.community.tanzu.vmware.com                    harbor                       OCI Registry
                                                         2.3.3
  knative-serving.community.tanzu.vmware.com           knative-serving              Knative Serving builds on Kubernetes to support deploying and serving of applications and functions as serverless containers                               1.0.0
  kpack.community.tanzu.vmware.com                     kpack                        kpack builds application source code into OCI compliant images using Cloud Native Buildpacks      
                                                         0.5.1
  local-path-storage.community.tanzu.vmware.com        local-path-storage           This package provides local path node storage and primarily supports RWO AccessMode.
                                                         0.0.20
  multus-cni.community.tanzu.vmware.com                multus-cni                   This package provides the ability for enabling attaching multiple network interfaces to pods in Kubernetes                                                 3.7.1
  prometheus.community.tanzu.vmware.com                prometheus                   A time series database for your metrics
                                                         2.27.0-1
  velero.community.tanzu.vmware.com                    velero                       Disaster recovery capabilities
                                                         1.8.0
  whereabouts.community.tanzu.vmware.com               whereabouts                  A CNI IPAM plugin that assigns IP addresses cluster-wide
```

2022年4月30日現在の最新のcartographerは[v0.3.0](https://github.com/vmware-tanzu/cartographer/releases/tag/v0.3.0)ですが[TCE v0.11.0パッケージに含まれているcartographer](https://github.com/vmware-tanzu/community-edition/blob/v0.11.0/addons/packages/cartographer/metadata.yaml))は[v0.2.2](https://github.com/vmware-tanzu/cartographer/releases/tag/v0.2.2)でした。

```bash
$ tanzu package available list cartographer.community.tanzu.vmware.com -n tanzu-package-repo-global 
  NAME                                     VERSION  RELEASED-AT
  cartographer.community.tanzu.vmware.com  0.2.2    2022-03-01 19:19:34 +0000 UTC  
```

cartographerはcert-managerに依存しているため、まずはcert-managerをパッケージとしてインストールします。

```bash
$ tanzu package install cert-manager --package-name cert-manager.community.tanzu.vmware.com --version 1.6.1 -n tanzu-package-repo-global
```

インストールが成功すると、`Reconcile succeeded`になります。

```bash
$ tanzu package installed list -n tanzu-package-repo-global
  NAME          PACKAGE-NAME                             PACKAGE-VERSION  STATUS
  cert-manager  cert-manager.community.tanzu.vmware.com  1.6.1            Reconcile succeeded
```

同じ様にcartographerをインストールします。

```bash
$ tanzu package install cartographer --package-name cartographer.community.tanzu.vmware.com --version 0.2.2 -n tanzu-package-repo-global
```

cert-managerとcartographerが`Reconcile succeeded`となります。

```bash
$ tanzu package installed list -n tanzu-package-repo-global
  NAME          PACKAGE-NAME                             PACKAGE-VERSION  STATUS
  cartographer  cartographer.community.tanzu.vmware.com  0.2.2            Reconcile succeeded
  cert-manager  cert-manager.community.tanzu.vmware.com  1.6.1            Reconcile succeeded
```

Podを確認するとcert-managerとcartographer-controllerがRunningとなっています。

```bash
$ kubectl get pod -n cert-manager 
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-564ddd9fbb-tvl2m              1/1     Running   0          2m16s
cert-manager-cainjector-67ddfc67c8-7dh59   1/1     Running   0          2m16s
cert-manager-webhook-5ff6c47844-4g6qw      1/1     Running   0          2m16s

$ kubectl get pod -n cartographer-system 
NAME                                      READY   STATUS    RESTARTS   AGE
cartographer-controller-c7dd8889b-qc5xc   1/1     Running   0          2m12s
```

cartographerをインストールすることにより以下のCRDが追加されます。

```bash
$ kubectl get crd | grep carto
clusterconfigtemplates.carto.run                                   2022-04-30T08:44:07Z
clusterdeliveries.carto.run                                        2022-04-30T08:44:07Z
clusterdeploymenttemplates.carto.run                               2022-04-30T08:44:07Z
clusterimagetemplates.carto.run                                    2022-04-30T08:44:07Z
clusterruntemplates.carto.run                                      2022-04-30T08:44:08Z
clustersourcetemplates.carto.run                                   2022-04-30T08:44:09Z
clustersupplychains.carto.run                                      2022-04-30T08:44:09Z
clustertemplates.carto.run                                         2022-04-30T08:44:09Z
deliverables.carto.run                                             2022-04-30T08:44:09Z
runnables.carto.run                                                2022-04-30T08:44:09Z
workloads.carto.run                                                2022-04-30T08:44:09Z
```

## Cartographerの使い方

catographerはClusterSupplyChainリソースによって定義されたルールに基づいてKubernetesリソースを処理します。今回のチュートリアルではWorkloadリソースを入力としてDeploymentを作成するサプライチェーンを作成します。

### ClusterSupplyChainの作成

以下のマニフェストでClusterSupplyChainを定義します。

- `resource`フィールドにはこのClusterSupplyChainで作成するリソースを定義します。この例ではdeployという名前のリソースを作成します。`templateRef`で指定している[ClusterTemplate](https://cartographer.sh/docs/v0.3.0/reference/template/#clustertemplate)によりdeployリソースの内容が定義されます。ClusterTemplateはClusterSupplyChain内に出力を持たないKubernetresリソースの作成に利用します。ClusterTemplateの内容は後述します。([ClusterTemplate](https://cartographer.sh/docs/v0.3.0/reference/template/#clustertemplate)以外のテンプレートも組み合わせて定義することにより、複雑なClusterSupplyChainを構成することが可能です)
- `serviceAccount`リソースはこのClusterSupplyChainを実行するServiceAccountを指定します。
- `selector`フィールドはこのClusterSupplyChainによって処理されるWorkloadリソースのセレクタを指定しており、この例では`workload-type: pre-built`というラベルがついたWorkloadリソースに対してこのClusterSupplyChainが実行されます。

```bash
apiVersion: carto.run/v1alpha1
kind: ClusterSupplyChain
metadata:
  name: supply-chain-simple
spec:
  resources:
    - name: deploy
      templateRef:
        kind: ClusterTemplate
        name: app-deploy
  serviceAccountRef:
    name: cartographer-pre-built-sa
    namespace: default
  selector:
    workload-type: pre-built
```

上記マニフェストでClusterSupplyChain supply-chain-simpleを作成します。

```bash
$ kubectl apply -f clustersupplychain.yaml
clustersupplychain.carto.run/supply-chain-simple created

$ kubectl get clustersupplychains.carto.run
NAME                  AGE
supply-chain-simple   9s
```

### ClusterTemplateの作成

ClusterSupplyChainで利用するClusterTemplateを定義します。Workloadリソースのパラメータを参照して、Deploymentリソースを作成するため、specとしてDeploymentのマニフェストテンプレートを定義し、Workloadリソースの`.metadata.name`や`.spec.image`等を参照します。

```yaml
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
  name: app-deploy
spec:
  template:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: $(workload.metadata.name)$-deployment
      labels:
        app: $(workload.metadata.name)$
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: $(workload.metadata.name)$
      template:
        metadata:
          labels:
            app: $(workload.metadata.name)$
        spec:
          containers:
            - name: $(workload.metadata.name)$
              image: $(workload.spec.image)$
```

### ServiceAccountの作成

cartographerはWorkloadリソースが作成されたことを認識してDeploymentを作成するため、Deploymentリソースの作成権限があるRole(`deploy-image-role`)を作成し、ClusterSupplyChainの実行時のサービスアカウント(`cartographer-pre-built-sa`)を作成し、このロールを割り当てる(`cartographer-prebuilt-role-binding`)必要があります。

```yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cartographer-pre-built-sa
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deploy-image-role
rules:
  - apiGroups:
      - apps
    resources:
      - deployments
    verbs:
      - list
      - create
      - update
      - delete
      - patch
      - watch
      - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: cartographer-prebuilt-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: deploy-image-role
subjects:
  - kind: ServiceAccount
    name: cartographer-pre-built-sa
```

上記マニフェストによりServiceAccount/Role/RoleBindingを作成します。

```bash
$ kubectl apply -f rbac.yaml
serviceaccount/cartographer-pre-built-sa created
role.rbac.authorization.k8s.io/deploy-image-role created
rolebinding.rbac.authorization.k8s.io/cartographer-prebuilt-role-binding created
```

### Workloadの作成

Workloadリソースを作成します。Workloadリソースは`.spec.image`として`docker.io/nginxdemos/hello:latest`を指定するだけのシンプルなものです。Workloadリソースではimageの他にもsourceとしてソースコードの場所(gitやOCIイメージ)、環境変数等を指定することが可能です。([Workload and Supply Chain Custom Resources](https://cartographer.sh/docs/v0.3.0/reference/workload/#workload))

```yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  name: hello
  labels:
    workload-type: pre-built
spec:
  image: docker.io/nginxdemos/hello:latest
```

上記マニフェストでWorkloadリソースを作成します。

```bash
$ kubectl apply -f workload.yaml
workload.carto.run/hello created
```

Workloadを確認すると`Ready`となっており、DeploymentとPodが作成されています。

```bash
$ kubectl get deploy,pod
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-deployment   3/3     3            3           107s

NAME                                   READY   STATUS    RESTARTS   AGE
pod/hello-deployment-5dddb657c-b2mnk   1/1     Running   0          107s
pod/hello-deployment-5dddb657c-hd262   1/1     Running   0          107s
pod/hello-deployment-5dddb657c-wn44f   1/1     Running   0          107s
```

`kubectl tree`でリソースを確認するとDeploymentやPodがhello Workloadによって作成されていることが確認できます。

```bash
$ kubectl tree workload hello
NAMESPACE  NAME                                        READY  REASON  AGE
default    Workload/hello                              True   Ready   114s
default    └─Deployment/hello-deployment               -              112s
default      └─ReplicaSet/hello-deployment-5dddb657c   -              112s
default        ├─Pod/hello-deployment-5dddb657c-b2mnk  True           112s
default        ├─Pod/hello-deployment-5dddb657c-hd262  True           112s
default        └─Pod/hello-deployment-5dddb657c-wn44f  True           112s
```

### Workloadの確認

hello Workloadリソースのステータスを確認してみます。`ResourcesSubmitted`が`True`となっているため、ClusterSupplyChainによるリソースの作成が完了したことを示しています。

```yaml
status:
  conditions:
  - lastTransitionTime: "2022-05-02T05:26:32Z"
    message: ""
    reason: Ready
    status: "True"
    type: SupplyChainReady
  - lastTransitionTime: "2022-05-02T05:26:34Z"
    message: ""
    reason: ResourceSubmissionComplete
    status: "True"
    type: ResourcesSubmitted
  - lastTransitionTime: "2022-05-02T05:26:34Z"
    message: ""
    reason: Ready
    status: "True"
    type: Ready
  observedGeneration: 1
  supplyChainRef:
    kind: ClusterSupplyChain
    name: supply-chain-simple
```

## まとめ

Cartographerのサプライチェーンを使うことで、Workloadリソースに基づいてKubernetes上の様々なリソースを作成することが可能です。このチュートリアルでは、コンテナイメージを指定したWorkloadリソースからDeploymentリソースを作成しました。より現実的な利用方法である、[fluxcd/source-controller](https://github.com/fluxcd/source-controller)、[kpack](https://buildpacks.io/docs/tools/kpack/)、[Knative](https://knative.dev/)によるSource-to-URLを構成するには、[TCEのパッケージ](https://tanzucommunityedition.io/packages/)として提供されている[Application Toolkit(app-toolkit)](https://tanzucommunityedition.io/docs/v0.11/package-readme-app-toolkit-0.1.0/)を利用することができます。app-toolkitには、cert-manager、cartographer、fluxcd-source-controller、kpack、knative-serving、contourがすべて含まれており、以下のようなClusterSupplyChainを作成して実際にSource-to-URLを行うことが可能です。

```yaml
apiVersion: carto.run/v1alpha1
kind: ClusterSupplyChain
metadata:
  name: supply-chain
spec:
  serviceAccountRef:
    name: workload-user-sa
  selector:
    apps.tanzu.vmware.com/workload-type: web
  resources:
  - name: source-provider
    templateRef:
      kind: ClusterSourceTemplate
      name: source
  - name: image-builder
    templateRef:
      kind: ClusterImageTemplate
      name: image
    params:
    - name: image_prefix
      value: masanara/
    sources:
    - resource: source-provider
      name: source
  - name: deployer
    templateRef:
      kind: ClusterTemplate
      name: app-deploy
    images:
    - resource: image-builder
      name: image
```

[CartographerのLive Editor](https://cartographer.sh/live-editor/)を使うとClusterSupplyChainを可視化することが可能です。
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/7a183421-90de-1a00-882f-638c67d72ab4.png)](https://cartographer.sh/live-editor/index.html?pako=eJyNkb1uwzAMhHc_hV5ANrJ6CzJ1bYuuBSMzjRD9EJTk1H36WrId2KgLZD3edyJPQPoDOWjvWqGAo685uaY_gKErHKqbdl0rTiaFiPyWiMxwuoJ2lcUIHURoKyEcWGxFKFOpyjgQqjwKyL1WeFTKJxdf8ZLFhbh7vhkPnUyjTQYofoMqep5sQBTqCO4n1b29A2OtvG0eWBwop-B5NDMGn1hhyKRcViqSJPa97pBLZkRLBiI-dhFie2Rh3mfX7FjHrfK1hS-U56TNs-kvmdgNL1lFIWCwYcI3L30S40V_z1gPJo0TCwHcSDRFXpWQ4aWV_Sr-vatDMn548qTda8afk1NKkcv6O1v9LXBTxy-0Sta9)