---
title: Cartographer 0.0.6をkindで試す
tags: ["Kubernetes", "Cartographer", "kpack", "Cloud Native Buildpacks", "Paketo"]
categories: ["Dev", "CaaS", "Kubernetes", "Cartographer"]
date: 2021-10-06T13:36:13Z
updated: 2021-10-07T05:30:43Z
---

"Cloud Native Supply Chains"と呼ばれる[Cartographer](https://cartographer.sh)がオープンソース化されたので、早速試してみます。
Cartographerの概要を説明した後に、今回はCartographerをkindにインストールし、PaaSのような感覚で"Source to URL"でアプリケーションを簡単デプロイできることを体験します。

**目次**



<!-- toc -->

### Cartographerとは

Cartographerがやってくれることを簡単にいうと、任意のk8sリソースのoutput(status)を使って次のk8sリソースに反映させるという橋渡しを行い、アプリケーションをリリースするための"Supply Chain"を提供することです。
例えばgitにpushしたソースコードをpullしてコンテナイメージを作成し、そのイメージをマニフェストに反映してデプロイするというフローを実現したい場合、

1. [Flux](https://fluxcd.io) の [GitRepository](https://fluxcd.io/docs/components/source/gitrepositories/) リソースがgitレポジトリがwatchし、変更があればソースコードをtar.gzファイルに圧縮してファイルサーバーに保存する
2. [kpack](https://github.com/pivotal/kpack) の [Image](https://github.com/pivotal/kpack/blob/main/docs/image.md) リソースが1.のURLからソースコードを取得し、 [Cloud Native Buildpacks](https://buildpacks.io) を使用してコンテナイメージを作成し、Dockerレジストリにイメージをpushする (Source to Image)
3. [Knative](https://knative.dev) の [Service](https://github.com/knative/specs/blob/main/specs/serving/knative-api-specification-1.0.md#service) リソースが2.のimageを使用してアプリケーションをデプロイする (Image to URL)

という連携が考えられますが、Cartographerはこれらのリソースの変更をwatchし、↓の図のようにリソース間の値の受け渡しを自動で行います。

![image](https://user-images.githubusercontent.com/106908/136144130-872f6bc9-ec94-4960-9e2a-a00f6f9af93b.jpeg)

例えば
1. watchしているgitリポジトリに対して新しいcommitがpushされることにより 、GitRepositoryリソースの `.status.artifact.url` が変更されれば、その値を自動でImageリソースの `.spec.source.blob.url` に設定する
2. 1.の後や、kpackの [ClusterBuilder](https://github.com/pivotal/kpack/blob/main/docs/builders.md#cluster-builders) や [ClusterStack](https://github.com/pivotal/kpack/blob/main/docs/stack.md) (OSのベースイメージ)がアップデートされることにより、Imageリソースの `.status.latestImage` が変更されれば、その値を自動でServiceリソースの `.spec.template.spec.containers[0].image` に設定する

ということをCartographerが行います。 
これらの組み合わせや値の受け渡し方法は自由にカスタマイズできて、下の図のように使い慣れたコンポーネントを選択して、組織にあった"Supply Chain"を定義することができます。
k8sリソースの入出力がfitすれば、どんなコンポーネントでも間に挟むことができます。例えばコンテナイメージをビルドする前にユニットテストを実行したり、イメージをビルドした後にイメージをスキャンすることもできます。

[ClusterSupplyChain](https://cartographer.sh/docs/v0.0.6/reference/#clustersupplychain) リソースにこのSupply Chainを定義できます。またSupply Chainへの入力(e.g. gitリポジトリのURLなど)を [Workload](https://cartographer.sh/docs/v0.0.6/reference/#workload) リソースに定義できます。

![image](https://content.cdntwrk.com/files/aHViPTYzOTc1JmNtZD1pdGVtZWRpdG9yaW1hZ2UmZmlsZW5hbWU9aXRlbWVkaXRvcmltYWdlXzYxNWI4NmE0MjE0NzMucG5nJnZlcnNpb249MDAwMCZzaWc9YjMxMDdmNDZmMTIwYjhiNzFhNTY4NTg0OGE2YWIyOTc%253D)
(図は https://tanzu.vmware.com/content/blog/vmware-tanzu-application-platform-beta-2-announcement より)

同じようなことをCI/CDツールを組み合わせて既に行っている組織も多いのではないかと思いますが、
Cartographerの良いところはDeveloperとOperatorの責任の分離を明確に行っている点です。

下の図のように、Operatorが定義したClusterSupplyChainを使ってDeveloperがWorkloadを定義しアプリケーションをデプロイできます。

![image](https://user-images.githubusercontent.com/106908/136133334-872d0ce8-f4e4-4a27-8286-3aecd1c7a710.png)
(図は https://cartographer.sh/docs/v0.0.6/ より)

CI/CDツールの組み合わせの場合は、
"誰がマニフェストやCIパイプラインを作るのか"や"Operatorがテンプレートを配布し、Developerがそれをカスタマイズする運用をするとテンプレートの変更を反映するのが大変"という課題を解決することができます。



### Getting Started

前置きが長くなりましたが、早速試してみます。

ここではKnativeの代わりにシンプルに標準のDeployment・Service・Ingressを使用します。Supply Chainは次の図のようになります。

![image](https://user-images.githubusercontent.com/106908/136181993-3655c270-40b6-4d6d-b896-b57dbe7d438d.jpeg)


#### kindクラスタの作成

まずはkindクラスタを作成します。[`extraPortMappings`](https://kind.sigs.k8s.io/docs/user/ingress/) を設定してLaptop上の80/443ポートを通じてIngressにアクセスできるようにします。

```
curl -sL https://github.com/projectcontour/contour/raw/main/examples/kind/kind-expose-port.yaml > kind-expose-port.yaml
kind create cluster --config kind-expose-port.yaml
```

#### コンポーネント群のインストール

今回のSupply Chainを作るために必要な次のコンポーネント群をインストールします。

* [cert-manager](https://github.com/jetstack/cert-manager)
* [kpack](https://github.com/pivotal/kpack)
* [Flux2 (source controller)](https://github.com/fluxcd/source-controller)
* [Contour (ingress)](https://github.com/projectcontour/contour)

```
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml
kubectl apply -f https://github.com/pivotal/kpack/releases/download/v0.4.0/release-0.4.0.yaml
kubectl create namespace gitops-toolkit
kubectl create clusterrolebinding gitops-toolkit-admin --clusterrole=cluster-admin --serviceaccount=gitops-toolkit:default
kubectl apply -n gitops-toolkit -f https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml -f https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.deployment.yaml
kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
```

インストール後のPod一覧は次の通りです。

```
$ kubectl get pod -A
NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
cert-manager         cert-manager-7c6f78c46d-xncwh                1/1     Running   0          77s
cert-manager         cert-manager-cainjector-668d9c86df-dskgc     1/1     Running   0          77s
cert-manager         cert-manager-webhook-764b556954-zfb7c        1/1     Running   0          77s
gitops-toolkit       source-controller-764686cbbd-tjpk2           1/1     Running   0          76s
kpack                kpack-controller-98c4fd6c7-qld5f             1/1     Running   0          77s
kpack                kpack-webhook-65d59b7ffd-d2x8l               1/1     Running   0          77s
kube-system          coredns-558bd4d5db-gjlmc                     1/1     Running   0          77s
kube-system          coredns-558bd4d5db-xlp85                     1/1     Running   0          77s
kube-system          etcd-kind-control-plane                      1/1     Running   0          86s
kube-system          kindnet-zrzgv                                1/1     Running   0          77s
kube-system          kube-apiserver-kind-control-plane            1/1     Running   0          86s
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0          93s
kube-system          kube-proxy-mq9fx                             1/1     Running   0          77s
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0          86s
local-path-storage   local-path-provisioner-547f784dff-qb9jx      1/1     Running   0          77s
projectcontour       contour-7cc4786577-gpvgn                     1/1     Running   0          75s
projectcontour       contour-7cc4786577-pmwch                     1/1     Running   0          75s
projectcontour       envoy-n7q6l                                  2/2     Running   0          66s
```

#### Cartographerのインストール

次にCartographerをインストールします。

[ドキュメント](https://cartographer.sh/docs/v0.0.6/install/) の通りにインストールしますが、
現時点のドキュメントは [Carvel](https://carvel.dev) を使った手順のみ用意されています。
この方法は [Air-gapped環境](https://carvel.dev/imgpkg/docs/latest/air-gapped-workflow/) にもインストール可能な、応用の効くやり方なのですが、少し手間がかかります。

[こちらのissue](https://github.com/vmware-tanzu/cartographer/issues/198) の通り、 `kubectl apply -f cartographer.yaml` でインストールできる方法は今度準備するようです。

##### Carvel Toolsのインストール

まずは [Carvel](https://carvel.dev/#install) のツール群をインストールします。

```
brew tap vmware-tanzu/carvel
brew install ytt kbld kapp imgpkg kwt vendir
```

あるいは

```
curl -sL https://carvel.dev/install.sh | bash
```

を実行してください。

本記事では `imgpkg`、`kbld`、`kapp` を使用します。次のバージョンで動作確認しています。

```
$ imgpkg version
imgpkg version 0.19.0

Succeeded
$ kbld version
kbld version 0.31.0

Succeeded
$ kapp version
kapp version 0.42.0
```

##### Carvelを使ったCartographerのインストール

バージョン [0.0.6](https://github.com/vmware-tanzu/cartographer/releases/tag/v0.0.6) 時点では、Cartographerのマニフェスト及びコンテナイメージは [imgpkg](https://carvel.dev/imgpkg/docs/latest/) のtar形式で配布されています。
これはコンテナイメージを別途コンテナレジストリにrelocationする想定です。

ここではコンテナレジストリとして、GitHub Packages(ghcr.io)を使用します。
このコンテナレジストリは後ほど設定するkpackでも使用するので、ユーザー名とパスワードを環境変数に設定します。

```
export GITHUB_USERNAME=making
export GITHUB_API_TOKEN=*************

docker login ghcr.io -u ${GITHUB_USERNAME} -p ${GITHUB_API_TOKEN}
```

次のコマンドでCartographerのtarファイルをダウンロードして、`imgpkg` コマンドを使用してGitHub Packagesへrelocationします。

```
curl -sL https://github.com/vmware-tanzu/cartographer/releases/download/v0.0.6/bundle.tar > /tmp/bundle.tar
imgpkg copy --tar /tmp/bundle.tar --to-repo ghcr.io/${GITHUB_USERNAME}/cartographer-bundle --lock-output /tmp/cartographer-bundle.lock.yaml
```

次のようなログが出力されます。

```
copy | importing 2 images...

35.20 MiB / 35.27 MiB [===================================================================================================================================================================================================================================================]  99.80% 3.20 MiB/s 10s

copy | done uploading images
Succeeded
```

GitHub Packagesを確認すると `cartographer-bundle` というイメージがアップロードされていることがわかります。  

![image](https://user-images.githubusercontent.com/106908/136062122-5948e18a-cc7d-4a50-bce6-49f76c38cae1.png)

次に、次のコマンドを実行して、relocation後のimageの設定が反映されたmanifestファイル群を取得します。

```
imgpkg pull --lock /tmp/cartographer-bundle.lock.yaml --output /tmp/cartographer
```

次のようなログが出力されます。

```
Pulling bundle 'ghcr.io/making/cartographer-bundle@sha256:f7e402cfa7c9404afdb51f9c6967c688e2595660b9335429ed5a880fc2caa80f'
  Extracting layer 'sha256:ec8f865d6051db9570e067cf3604a19face114a6d1e8920b61bae44eae64078d' (1/1)

Locating image lock file images...
The bundle repo (ghcr.io/making/cartographer-bundle) is hosting every image specified in the bundle's Images Lock file (.imgpkg/images.yml)

Succeeded
```

次のようなファイルが取得されます。imgpkgの [bundle形式](https://carvel.dev/imgpkg/docs/latest/resources/#bundle) です。

```
$ find /tmp/cartographer 
/tmp/cartographer
/tmp/cartographer/.imgpkg
/tmp/cartographer/.imgpkg/images.yml
/tmp/cartographer/config
/tmp/cartographer/config/cartographer.yaml
/tmp/cartographer/config/overlays
/tmp/cartographer/config/overlays/.gitkeep
/tmp/cartographer/config/objects
/tmp/cartographer/config/objects/kapp-secret-ignore.yaml
```

次のコマンドでこのmanifestをデプロイします。ここでは`kbld`と`kapp`コマンドを使用します。

```
kubectl create namespace cartographer-system
kubectl create secret -n cartographer-system docker-registry private-registry-credentials --docker-server=ghcr.io --docker-username=${GITHUB_USERNAME} --docker-password=${GITHUB_API_TOKEN}
kbld -f /tmp/cartographer | kapp deploy -a cartographer -c -f - -y
```

インストールが完了すれば、次のコマンドを実行してどのようなリソースが作成されたか確認できます。

```
$ kapp inspect -a cartographer -t
Target cluster 'https://127.0.0.1:51332' (nodes: kind-control-plane)

Resources in app 'cartographer'

Namespace            Name                                         Kind                            Owner    Conds.  Rs  Ri  Age  
(cluster)            clusterimagetemplates.carto.run              CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
(cluster)            clustersourcetemplates.carto.run             CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
cartographer-system  cartographer-webhook                         Certificate                     kapp     1/1 t   ok  -   29s  
cartographer-system   L cartographer-webhook-chq25                CertificateRequest              cluster  2/2 t   ok  -   28s  
(cluster)            pipelines.carto.run                          CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
(cluster)            clustersupplychainvalidator                  ValidatingWebhookConfiguration  kapp     -       ok  -   30s  
cartographer-system  cartographer-webhook                         Secret                          kapp     -       ok  -   30s  
(cluster)            clustersupplychains.carto.run                CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
(cluster)            clusterconfigtemplates.carto.run             CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
cartographer-system  cartographer-webhook                         Service                         kapp     -       ok  -   29s  
cartographer-system   L cartographer-webhook                      Endpoints                       cluster  -       ok  -   29s  
cartographer-system   L cartographer-webhook-kd7mx                EndpointSlice                   cluster  -       ok  -   29s  
(cluster)            clustertemplates.carto.run                   CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
(cluster)            workloads.carto.run                          CustomResourceDefinition        kapp     2/2 t   ok  -   30s  
(cluster)            cartographer-cluster-admin                   ClusterRoleBinding              kapp     -       ok  -   30s  
cartographer-system  cartographer-controller                      ServiceAccount                  kapp     -       ok  -   30s  
cartographer-system  selfsigned-issuer                            Issuer                          kapp     1/1 t   ok  -   29s  
cartographer-system  cartographer-controller                      Deployment                      kapp     2/2 t   ok  -   29s  
cartographer-system   L cartographer-controller-f664d5bc          ReplicaSet                      cluster  -       ok  -   29s  
cartographer-system   L.. cartographer-controller-f664d5bc-vhg64  Pod                             cluster  4/4 t   ok  -   29s  
cartographer-system  private-registry-credentials                 Secret                          kapp     -       ok  -   30s  
(cluster)            runtemplates.carto.run                       CustomResourceDefinition        kapp     2/2 t   ok  -   30s  

Rs: Reconcile state
Ri: Reconcile information

22 resources

Succeeded
```

#### kpackの設定

次にkpackの設定を行います。

##### imagePullSecretの設定

`default` namespaceの`default` service accountに対してghcr.ioへの`imagePullSecret`を設定しておきます。

```
cat <<EOF > dockerconfig.yaml
apiVersion: v1
kind: Secret
metadata:
  name: regsecret
  namespace: default
type: kubernetes.io/dockerconfigjson
stringData:
  .dockerconfigjson: |
    {
      "auths": {
        "https://ghcr.io": {
          "username": "${GITHUB_USERNAME}",
          "password": "${GITHUB_API_TOKEN}"
        }
      }
    }
EOF
kubectl apply -f dockerconfig.yaml
kubectl patch -n default serviceaccount default -p "{\"secrets\":[{\"name\":\"regsecret\"}],\"imagePullSecrets\":[{\"name\":\"regsecret\"}]}"
```

##### ClusterBuilderの設定

[Paketo Buildpacks](https://paketo.io) を使ったClusterBuilder及び、ClusterStore、ClusterStackを定義します。
ここではGo、Java、Node.js、Rubyのみ対応します。

```yaml
cat <<EOF > clusterbuilder.yaml
apiVersion: kpack.io/v1alpha1
kind: ClusterStore
metadata:
  name: default
spec:
  sources:
  - image: gcr.io/paketo-buildpacks/ruby
  - image: gcr.io/paketo-buildpacks/nodejs
  - image: gcr.io/paketo-buildpacks/go
  - image: gcr.io/paketo-buildpacks/java
---
apiVersion: kpack.io/v1alpha1
kind: ClusterStack
metadata:
  name: base
spec:
  id: io.buildpacks.stacks.bionic
  buildImage:
    image: paketobuildpacks/build:base-cnb
  runImage:
    image: paketobuildpacks/run:base-cnb
---
apiVersion: kpack.io/v1alpha1
kind: ClusterBuilder
metadata:
  name: base
spec:
  serviceAccountRef:
    name: default
    namespace: default
  tag: ghcr.io/${GITHUB_USERNAME}/clusterbuilder:base
  stack:
    name: base
    kind: ClusterStack
  store:
    name: default
    kind: ClusterStore
  order:
  - group:
    - id: paketo-buildpacks/ruby
  - group:
    - id: paketo-buildpacks/nodejs
  - group:
    - id: paketo-buildpacks/go
  - group:
    - id: paketo-buildpacks/java
EOF
kubectl apply -f clusterbuilder.yaml 
```

しばらくするとClusterBuilderに対応するbuilderのイメージがアップロードされます。

```
$ kubectl get clusterbuilder
NAME   LATESTIMAGE                                                                                                  READY
base   ghcr.io/making/clusterbuilder:base@sha256:c3640f0f6e1643e692d7f2abacaee766dfb7f76fa84adc1bdc6ecdc5dcc29cfa   True
```

GitHub Packagesでそのイメージを確認できます。

![image](https://user-images.githubusercontent.com/106908/136072771-54443aa5-21a4-42d1-b9ab-b242e6561cf2.png)

> ⚠️ GitHub PackagesのFreeプランを使用する場合、private repositoryに対するストーレジと転送量の制約があるため、場合によってはpublic repositoryへ変更した方が良いかもしれません。

#### Supply Chainの設定

いよいよCartographerのSupply Chainを定義します。再掲になりますが、次のようなSupply Chainを作成します。

![image](https://user-images.githubusercontent.com/106908/136181993-3655c270-40b6-4d6d-b896-b57dbe7d438d.jpeg)

CartographerではSupply Chainを構成するテンプレートとして次の4種類を利用できます。

* [ClusterSourceTemplate](https://cartographer.sh/docs/v0.0.6/reference/#clustersourcetemplate) ... outputがソースコードの情報(urlとrevision)であるk8sリソースのテンプレート定義
* [ClusterImageTemplate](https://cartographer.sh/docs/v0.0.6/reference/#clusterimagetemplate) ... outputがコンテナイメージの情報(image name)であるk8sリソースのテンプレート定義
* [ClusterConfigTemplate](https://cartographer.sh/docs/v0.0.6/reference/#clusterconfigtemplate) ... outputが修正されたconfigであるk8sリソースのテンプレート定義
* [ClusterTemplate](https://cartographer.sh/docs/v0.0.6/reference/#clustertemplate) ... outputを必要としないk8sリソースのテンプレート定義

上記の例では

* GitRepositoryリソースのテンプレート定義　-> ClusterSourceTemplate
* Imageリソースのテンプレート定義 -> ClusterImageTemplate
* Deploy, Service, Ingressリソースのテンプレート定義 -> ClusterTemplate

に対応します。

##### Templatesの作成

各種テンプレートを次のように作成します。

```yaml
cat <<'EOF' | sed "s/CHANGE_ME/${GITHUB_USERNAME}/" > supply-chain-templates.yaml                       
apiVersion: carto.run/v1alpha1
kind: ClusterSourceTemplate
metadata:
  name: source
spec:
  urlPath: .status.artifact.url
  revisionPath: .status.artifact.revision
  template:
    apiVersion: source.toolkit.fluxcd.io/v1beta1
    kind: GitRepository
    metadata:
      name: $(workload.metadata.name)$
      labels:
        app.kubernetes.io/part-of: $(workload.metadata.name)$
    spec:
      interval: 3m
      url: $(workload.spec.source.git.url)$
      ref: $(workload.spec.source.git.ref)$
      ignore: ""
---
apiVersion: carto.run/v1alpha1
kind: ClusterImageTemplate
metadata:
  name: image
spec:
  imagePath: .status.latestImage
  template:
    apiVersion: kpack.io/v1alpha1
    kind: Image
    metadata:
      name: $(workload.metadata.name)$
      labels:
        app.kubernetes.io/part-of: $(workload.metadata.name)$
    spec:
      tag: ghcr.io/CHANGE_ME/$(workload.metadata.name)$
      serviceAccount: default
      builder:
        kind: ClusterBuilder
        name: base
      source:
        blob:
          url: $(sources.source.url)$
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
  name: app-deploy-deployment
spec:
  template:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: $(workload.metadata.name)$
      labels:
        app.kubernetes.io/part-of: $(workload.metadata.name)$
    spec:
      template:
        metadata:
          labels:
            app.kubernetes.io/part-of: $(workload.metadata.name)$          
        spec:
          containers:
          - name: workload
            image: $(images.image.image)$
            env: $(workload.spec.env)$
            resources: $(workload.spec.resources)$
            ports:
            - name: http-port
              containerPort: 8080
            securityContext:
              runAsUser: 1000
      selector:
        matchLabels:
          app.kubernetes.io/part-of: $(workload.metadata.name)$
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
  name: app-deploy-service
spec:
  template:
    apiVersion: v1
    kind: Service
    metadata:
      name: $(workload.metadata.name)$
      labels:
        app.kubernetes.io/part-of: $(workload.metadata.name)$
    spec:
      ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 8080
      selector:
        app.kubernetes.io/part-of: $(workload.metadata.name)$ 
      type: ClusterIP
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
  name: app-deploy-ingress
spec:
  template:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: $(workload.metadata.name)$
      labels:
        app.kubernetes.io/part-of: $(workload.metadata.name)$ 
    spec:
      rules:
      - host: $(workload.metadata.name)$-127-0-0-1.sslip.io
        http:
          paths:
          - backend:
              service:
                name: $(workload.metadata.name)$
                port:
                  number: 80
            path: /
            pathType: Exact
EOF
kubectl apply -f supply-chain-templates.yaml
```

##### SupplyChainの定義

定義したテンプレートを組み合わせたSupply ChainをClusterSupplyChainリソースとして次のように定義します。

```yaml
cat <<'EOF' > supply-chain.yaml
apiVersion: carto.run/v1alpha1
kind: ClusterSupplyChain
metadata:
  name: supply-chain
spec:
  selector:
    app.tanzu.vmware.com/workload-type: web
  components:
  - name: source-provider
    templateRef:
      kind: ClusterSourceTemplate
      name: source
  - name: image-builder
    templateRef:
      kind: ClusterImageTemplate
      name: image
    sources:
    - component: source-provider
      name: source
  - name: deployer-deployment
    templateRef:
      kind: ClusterTemplate
      name: app-deploy-deployment
    images:
    - component: image-builder
      name: image
  - name: deployer-service
    templateRef:
      kind: ClusterTemplate
      name: app-deploy-service  
  - name: deployer-ingress
    templateRef:
      kind: ClusterTemplate
      name: app-deploy-ingress
EOF
kubectl apply -f supply-chain.yaml
```

ここまではOperatorの役目です。

#### Workloadのデプロイ

このSupply Chainを使って、いよいよアプリケーションをデプロイします。ここからはDeveloperの役目です。

##### Goアプリのデプロイ

まずはGoのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-golang を使用します。

次のWorkloadを作成します。Developerとして定義する内容はこれだけです。どのようにコンテナイメージが作られてどのようにk8sへデプロイするかを意識する必要がありません。

```yaml
cat <<EOF > hello-golang.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  labels:
    app.tanzu.vmware.com/workload-type: web
  name: hello-golang
spec:
  source:
    git:
      url: https://github.com/making/hello-golang.git
      ref:
        branch: master
  env:
  - name: TARGET
    value: World            
  resources: {}    
EOF
kubectl apply -f hello-golang.yaml
```

実際にどのような処理が行われているかをログで確認します。複数のコンテナで処理が行われるので [stern](https://github.com/stern/stern) を使用すると良いです。 

ソースコード(tar.gz)が取得され、そのソースコードがGoのものであることが検出され、ビルドが行われて、イメージがpushされ、その後アプリケーションが起動していることがわかります。

![workload-demo](https://user-images.githubusercontent.com/106908/136251374-f4f2c738-6f39-4ddb-aa2f-8d3ec8ed2612.gif)

```
$ stern 'hello-golang.*'    

+ hello-golang-build-1-rqzsd-build-pod › prepare
hello-golang-build-1-rqzsd-build-pod prepare Build reason(s): CONFIG
hello-golang-build-1-rqzsd-build-pod prepare CONFIG:
hello-golang-build-1-rqzsd-build-pod prepare 	resources: {}
hello-golang-build-1-rqzsd-build-pod prepare 	- source: {}
hello-golang-build-1-rqzsd-build-pod prepare 	+ source:
hello-golang-build-1-rqzsd-build-pod prepare 	+   blob:
hello-golang-build-1-rqzsd-build-pod prepare 	+     url: http://source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz
hello-golang-build-1-rqzsd-build-pod prepare Loading secret for "https://ghcr.io" from secret "regsecret" at location "/var/build-secrets/regsecret"
hello-golang-build-1-rqzsd-build-pod prepare Downloading source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz...
hello-golang-build-1-rqzsd-build-pod prepare Successfully downloaded source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz in path "/workspace"
- hello-golang-build-1-rqzsd-build-pod › prepare
+ hello-golang-build-1-rqzsd-build-pod › detect
hello-golang-build-1-rqzsd-build-pod detect 4 of 7 buildpacks participating
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/ca-certificates 2.3.2
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-dist         0.6.0
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-mod-vendor   0.3.1
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-build        0.4.1
- hello-golang-build-1-rqzsd-build-pod › detect
+ hello-golang-build-1-rqzsd-build-pod › analyze
hello-golang-build-1-rqzsd-build-pod analyze Previous image with name "ghcr.io/making/hello-golang" not found
- hello-golang-build-1-rqzsd-build-pod › analyze
+ hello-golang-build-1-rqzsd-build-pod › build
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build Paketo CA Certificates Buildpack 2.3.2
hello-golang-build-1-rqzsd-build-pod build   https://github.com/paketo-buildpacks/ca-certificates
hello-golang-build-1-rqzsd-build-pod build   Launch Helper: Contributing to layer
hello-golang-build-1-rqzsd-build-pod build     Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
hello-golang-build-1-rqzsd-build-pod build Paketo Go Distribution Buildpack 0.6.0
hello-golang-build-1-rqzsd-build-pod build   Resolving Go version
hello-golang-build-1-rqzsd-build-pod build     Candidate version sources (in priority order):
hello-golang-build-1-rqzsd-build-pod build       go.mod    -> ">= 1.16"
hello-golang-build-1-rqzsd-build-pod build       <unknown> -> ""
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build     Selected Go version (using go.mod): 1.17
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build   Executing build process
hello-golang-build-1-rqzsd-build-pod build     Installing Go 1.17
hello-golang-build-1-rqzsd-build-pod build       Completed in 7.806s
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build Paketo Go Mod Vendor Buildpack 0.3.1
hello-golang-build-1-rqzsd-build-pod build   Checking module graph
hello-golang-build-1-rqzsd-build-pod build     Running 'go mod graph'
hello-golang-build-1-rqzsd-build-pod build       Completed in 6ms
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build   Skipping build process: module graph is empty
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build Paketo Go Build Buildpack 0.4.1
hello-golang-build-1-rqzsd-build-pod build   Executing build process
hello-golang-build-1-rqzsd-build-pod build     Running 'go build -o /layers/paketo-buildpacks_go-build/targets/bin -buildmode pie .'
hello-golang-build-1-rqzsd-build-pod build       Completed in 15.708s
hello-golang-build-1-rqzsd-build-pod build 
hello-golang-build-1-rqzsd-build-pod build   Assigning launch processes:
hello-golang-build-1-rqzsd-build-pod build     web: /layers/paketo-buildpacks_go-build/targets/bin/hello-golang
hello-golang-build-1-rqzsd-build-pod build     hello-golang: /layers/paketo-buildpacks_go-build/targets/bin/hello-golang
hello-golang-build-1-rqzsd-build-pod build 
- hello-golang-build-1-rqzsd-build-pod › build
+ hello-golang-build-1-rqzsd-build-pod › export
hello-golang-build-1-rqzsd-build-pod export Adding layer 'paketo-buildpacks/ca-certificates:helper'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'paketo-buildpacks/go-build:targets'
hello-golang-build-1-rqzsd-build-pod export Adding 1/1 app layer(s)
hello-golang-build-1-rqzsd-build-pod export Adding layer 'launcher'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'config'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'process-types'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.lifecycle.metadata'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.build.metadata'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.project.metadata'
hello-golang-build-1-rqzsd-build-pod export Setting default process type 'web'
hello-golang-build-1-rqzsd-build-pod export Saving ghcr.io/making/hello-golang...
hello-golang-build-1-rqzsd-build-pod export *** Images (sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a):
hello-golang-build-1-rqzsd-build-pod export       ghcr.io/making/hello-golang
hello-golang-build-1-rqzsd-build-pod export       ghcr.io/making/hello-golang:b1.20211006.005809
hello-golang-build-1-rqzsd-build-pod export Adding cache layer 'paketo-buildpacks/go-dist:go'
hello-golang-build-1-rqzsd-build-pod export Adding cache layer 'paketo-buildpacks/go-build:gocache'
- hello-golang-build-1-rqzsd-build-pod › export
+ hello-golang-566c6c75c7-b6tdr › workload
hello-golang-566c6c75c7-b6tdr workload 2021/10/06 00:59:38 helloworld: starting server...
hello-golang-566c6c75c7-b6tdr workload 2021/10/06 00:59:38 helloworld: listening on port 8080
```

このWorkloadからSupply Chainを経由して作成されるリソースを次のコマンドで確認します。

```
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress  

NAME                              AGE
workload.carto.run/hello-golang   2m31s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-golang   1/1     1            1           61s

NAME                                       READY   STATUS      RESTARTS   AGE
pod/hello-golang-566c6c75c7-b6tdr          1/1     Running     0          61s
pod/hello-golang-build-1-rqzsd-build-pod   0/1     Completed   0          2m25s

NAME                          LATESTIMAGE                                                                                           READY
image.kpack.io/hello-golang   ghcr.io/making/hello-golang@sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a   True

NAME                                        IMAGE                                                                                                 SUCCEEDED
build.kpack.io/hello-golang-build-1-rqzsd   ghcr.io/making/hello-golang@sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a   True

NAME                                                  URL                                          READY   STATUS                                                              AGE
gitrepository.source.toolkit.fluxcd.io/hello-golang   https://github.com/making/hello-golang.git   True    Fetched revision: master/1f8f29d92011f329b695223a35bf3f480c05f8a2   2m31s

NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/hello-golang   ClusterIP   10.96.31.196   <none>        80/TCP    61s
service/kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP   7h54m

NAME                                    　CLASS   HOSTS                             ADDRESS   PORTS   AGE
ingress.networking.k8s.io/hello-golang    <none>  hello-golang-127-0-0-1.sslip.io             80      61s
```

IngressのURL( http://hello-golang-127-0-0-1.sslip.io )にアクセスします。

```
$ curl http://hello-golang-127-0-0-1.sslip.io
Hello World!
```

"Hello World!"が返りました。

WorkloadにはソースコードのURLしか書いていないですが、アプリがURLでアクセスなところまで用意されました。
これはPaaSで実現されていた"Source to URL"に近い体験です。


ここでは割愛しますが、ソースコードを変更してgit pushすれば新しいイメージが作成されて、再デプロイされます。

Workloadを削除すれば、各種リソースも削除されます。
```
kubectl delete -f hello-golang.yaml
```

##### Javaアプリのデプロイ

次にJavaのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/tanzu-japan/hello-tanzu を使用します。

次のWorkloadを作成します。

```yaml
cat <<EOF > hello-tanzu.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  labels:
    app.tanzu.vmware.com/workload-type: web
  name: hello-tanzu
spec:
  source:
    git:
      url: https://github.com/tanzu-japan/hello-tanzu.git
      ref:
        branch: main
  env: []
  resources: {}        
EOF
kubectl apply -f hello-tanzu.yaml
```

同様にsternでログを確認すれば、ソースコード取得後Mavenによるビルドが行われ、イメージ作成後にデプロイされてTomcatが起動していることがわかります。
なお、Mavenによるビルドは2回目移行はキャッシュされます。

```
stern 'hello-tanzu.*'     
```

ログは[こちら](https://gist.github.com/making/4fd4dda55b097938095cbcf1a505c24a)

このWorkloadからSupply Chainを経由して作成されるリソースを次のコマンドで確認します。

```
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress  

NAME                             AGE
workload.carto.run/hello-tanzu   12m

NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-tanzu   1/1     1            1           9m43s

NAME                                      READY   STATUS      RESTARTS   AGE
pod/hello-tanzu-54dd5899f4-hsxb6          1/1     Running     0          9m43s
pod/hello-tanzu-build-1-fvt6g-build-pod   0/1     Completed   0          11m

NAME                         LATESTIMAGE                                                                                          READY
image.kpack.io/hello-tanzu   ghcr.io/making/hello-tanzu@sha256:ab5c70080c2b8a73b67d90b1255a1f92a7a6261d03cf35f5812457214b00c626   True

NAME                                       IMAGE                                                                                                SUCCEEDED
build.kpack.io/hello-tanzu-build-1-fvt6g   ghcr.io/making/hello-tanzu@sha256:ab5c70080c2b8a73b67d90b1255a1f92a7a6261d03cf35f5812457214b00c626   True

NAME                                                 URL                                              READY     STATUS                       AGE
gitrepository.source.toolkit.fluxcd.io/hello-tanzu   https://github.com/tanzu-japan/hello-tanzu.git   Unknown   reconciliation in progress   12m

NAME                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/hello-tanzu   ClusterIP   10.96.96.239   <none>        80/TCP    9m43s
service/kubernetes    ClusterIP   10.96.0.1      <none>        443/TCP   8h

NAME                                    CLASS    HOSTS                            ADDRESS   PORTS   AGE
ingress.networking.k8s.io/hello-tanzu   <none>   hello-tanzu-127-0-0-1.sslip.io             80      9m43s
```

IngressのURL( http://hello-tanzu-127-0-0-1.sslip.io )にブラウザでアクセスして動作確認します。

![image](https://user-images.githubusercontent.com/106908/136128060-004f3a9f-1aed-4dd6-b573-7250360f724f.png)

Workloadを削除します。

```
kubectl delete -f hello-tanzu.yaml 
```

##### Node.jsアプリのデプロイ

次にNode.jsのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-nodejs を使用します。

次のWorkloadを作成します。

```yaml
cat <<EOF > hello-nodejs.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  name: hello-nodejs  
  labels:
    app.tanzu.vmware.com/workload-type: web
spec:
  source:
    git:
      url: https://github.com/making/hello-nodejs
      ref:
        branch: master  
EOF
kubectl apply -f hello-nodejs.yaml
```

同様にsternでログを確認すれば、ソースコード取得後npmによるビルドが行われていることがわかります。
なお、npmによるビルドは2回目移行はキャッシュされます。

```
stern 'hello-nodejs.*'    
```

ログは[こちら](https://gist.github.com/making/9bdb2bc5806332d0efb54473fca4d068)

```
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress  

NAME                              AGE
workload.carto.run/hello-nodejs   7m18s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-nodejs   1/1     1            1           6m23s

NAME                                       READY   STATUS      RESTARTS   AGE
pod/hello-nodejs-7f87556847-kk4bk          1/1     Running     0          6m23s
pod/hello-nodejs-build-1-qxxwq-build-pod   0/1     Completed   0          7m12s

NAME                          LATESTIMAGE                                                                                           READY
image.kpack.io/hello-nodejs   ghcr.io/making/hello-nodejs@sha256:b11ec565f737dda90a76cbfa87aae92cdc348fccdf36de1d0b387c229cb5e4df   True

NAME                                        IMAGE                                                                                                 SUCCEEDED
build.kpack.io/hello-nodejs-build-1-qxxwq   ghcr.io/making/hello-nodejs@sha256:b11ec565f737dda90a76cbfa87aae92cdc348fccdf36de1d0b387c229cb5e4df   True

NAME                                                  URL                                      READY   STATUS                                                              AGE
gitrepository.source.toolkit.fluxcd.io/hello-nodejs   https://github.com/making/hello-nodejs   True    Fetched revision: master/9b066f581bd86f6580b73209439bd5cb4b3af523   7m18s

NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/hello-nodejs   ClusterIP   10.96.88.240   <none>        80/TCP    6m23s
service/kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP   8h

NAME                                     CLASS    HOSTS                             ADDRESS   PORTS   AGE
ingress.networking.k8s.io/hello-nodejs   <none>   hello-nodejs-127-0-0-1.sslip.io             80      6m23s
```


IngressのURL( http://hello-nodejs-127-0-0-1.sslip.io )にcurlでアクセスして動作確認します。

```
$ curl http://hello-nodejs-127-0-0-1.sslip.io
Hello World!
```

Workloadを削除します。

```
kubectl delete -f hello-nodejs.yaml 
```

##### Rubyアプリのデプロイ

次にRubyのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-ruby を使用します。

次のWorkloadを作成します。

```yaml
cat <<EOF > hello-ruby.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  name: hello-ruby  
  labels:
    app.tanzu.vmware.com/workload-type: web
spec:
  source:
    git:
      url: https://github.com/making/hello-ruby
      ref:
        branch: main
  env:
  - name: PORT
    value: "8080"        
EOF
kubectl apply -f hello-ruby.yaml
```

同様にsternでログを確認します。

```
stern 'hello-ruby.*'      
```

ログは[こちら](https://gist.github.com/making/a97d99d6aa8cbc2513a65a109a9c8389)

```
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress  

NAME                            AGE
workload.carto.run/hello-ruby   8m7s

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-ruby   1/1     1            1           7m27s

NAME                                     READY   STATUS      RESTARTS   AGE
pod/hello-ruby-5cbcd9546f-xcf7g          1/1     Running     0          7m27s
pod/hello-ruby-build-1-9nmz8-build-pod   0/1     Completed   0          8m1s

NAME                        LATESTIMAGE                                                                                         READY
image.kpack.io/hello-ruby   ghcr.io/making/hello-ruby@sha256:5c2bcd9182a62c9fcb191f2f1d9639f0eb86836943093a553d9b00d363223cfe   True

NAME                                      IMAGE                                                                                               SUCCEEDED
build.kpack.io/hello-ruby-build-1-9nmz8   ghcr.io/making/hello-ruby@sha256:5c2bcd9182a62c9fcb191f2f1d9639f0eb86836943093a553d9b00d363223cfe   True

NAME                                                URL                                    READY   STATUS                                                            AGE
gitrepository.source.toolkit.fluxcd.io/hello-ruby   https://github.com/making/hello-ruby   True    Fetched revision: main/b259d9f2bd6f7da6b0fc266f1d8bb6db56a199fd   8m7s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/hello-ruby   ClusterIP   10.96.117.16   <none>        80/TCP    7m27s
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP   9h

NAME                                   CLASS    HOSTS                           ADDRESS   PORTS   AGE
ingress.networking.k8s.io/hello-ruby   <none>   hello-ruby-127-0-0-1.sslip.io             80      7m27s
```

IngressのURL( http://hello-ruby-127-0-0-1.sslip.io )にcurlでアクセスして動作確認します。

```
$ curl http://hello-ruby-127-0-0-1.sslip.io 
Hello World!
```

Workloadを削除します。

```
kubectl delete -f hello-ruby.yaml 
```

---

Cartographer 0.0.6をkindにインストールして様々なアプリケーションをデプロイしました。
Cartographerの面白さを少しは感じてもらえたのではないでしょうか。

次はKnativeやTektonとの連携を試そうと思います。

ちなみにVMworld Japan 2021でCarographerについて話します。興味のある方はぜひご登録ください。
https://vmworld.jp/content/MA11160/
2021/11/26 13:40-14:20です。
