---
title: Tanzu Application Platformでtype=serverのWorkloadに対してIngressを作る
tags: ["Kubernetes", "Cartographer", "kind", "Tanzu", "TAP", "Carvle"]
categories: ["Dev", "CaaS", "Kubernetes", "TAP"]
date: 2023-08-12T11:38:16Z
updated: 2023-08-13T06:01:17Z
---

Tanzu Application Platform (1.6時点)では、[こちらのドキュメント](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/workloads-workload-types.html)に記載されているように、Out of the Box (OOTB) Supply Chainsにて以下のWorkload Typeがサポートサポートされています。

* `type=web` ... スケーラブルなWebアプリケーションを想定したもの。KnativeのServiceリソースをが作成される。Scale to Zero、Zero to Nがサポートされる。
* `type=server` ... トラディショナルなWebアプリケーションを想定したもの。K8s標準のDeployment、Serviceリソースが作成される。
* `type=worker` ... キューを処理するバックグラウンドアプリケーションを想定したもの。K8s標準のDeploymentが作成される。


`type=server`でアプリをデプロイした場合、Ingressでアプリを公開したい場合はIngressリソースを何かしらの方法で作成する必要があります。
以下の3つの方法を紹介します。

* `kubectl`で直接Ingressリソースを作成する
* OOTB SupplyChainで作成されるリソースにIngressを追加する
* Carvel Package Supply Chainを使用する

---

**目次**
<!-- toc -->

### `kubectl`で直接Ingressリソースを作成する

まずはサンプルアプリのWorkloadを作成します。

```
tanzu apps workload apply hello-nodejs \
  --app hello-nodejs \
  --git-repo https://github.com/making/hello-nodejs \
  --git-branch master \
  --type server \
  -n demo
```

このWorkloadから作成されるマニフェストは次のコマンドで確認できます。

```
kubectl get cm -n demo hello-nodejs-server -ojsonpath='{.data.delivery\.yml}'
```

次の出力が得られます。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-nodejs
  annotations:
    kapp.k14s.io/update-strategy: fallback-on-replace
    ootb.apps.tanzu.vmware.com/servicebinding-workload: "true"
    kapp.k14s.io/change-rule: upsert after upserting servicebinding.io/ServiceBindings
  labels:
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: run
      app.kubernetes.io/part-of: hello-nodejs
      apps.tanzu.vmware.com/workload-type: server
      carto.run/workload-name: hello-nodejs
  template:
    metadata:
      annotations:
        conventions.carto.run/applied-conventions: |-
          appliveview-sample/app-live-view-appflavour-check
          spring-boot-convention/auto-configure-actuators-check
          spring-boot-convention/app-live-view-appflavour-check
        developer.conventions/target-containers: workload
      labels:
        app.kubernetes.io/component: run
        app.kubernetes.io/part-of: hello-nodejs
        apps.tanzu.vmware.com/workload-type: server
        carto.run/workload-name: hello-nodejs
    spec:
      containers:
      - image: ghcr.io/making/workloads/hello-nodejs-demo@sha256:052ffee7966eeda9cf3a0d6a255f70b443ed0c39ab24591ab4b9ab857b30995b
        name: workload
        resources: {}
        securityContext:
          runAsUser: 1000
      serviceAccountName: default
---
apiVersion: v1
kind: Service
metadata:
  name: hello-nodejs
  labels:
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
spec:
  selector:
    app.kubernetes.io/component: run
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server
    carto.run/workload-name: hello-nodejs
  ports:
  - targetPort: 8080
    port: 8080
    name: http
```

このアプリを例えば、`hello-nodejs-demo.tap.192-168-228-200.sslip.io` で公開したい場合 (envoyのExternalIPが`192.168.228.201`である前提です)、次のコマンドでIngressを作成すれば良いです。
TAPのmulti cluster構成の場合は、Runクラスタで実行してください。
ただし、`app-editor`には**このコマンドを実行する権限がありません**。`app-operator`であれば実行可能です。

* TLSを使用しない場合
```
kubectl create ingress hello-nodejs -n demo \
  --rule="hello-nodejs-demo.tap.192-168-228-200.sslip.io/*=hello-nodejs:8080"
```

アプリにアクセスします。

```
$ curl -sv http://hello-nodejs-demo.tap.192-168-228-200.sslip.io
> GET / HTTP/1.1
> Host: hello-nodejs-demo.tap.192-168-228-200.sslip.io
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 200 OK
< x-powered-by: Express
< content-type: text/html; charset=utf-8
< content-length: 14
< etag: W/"e-LQS9yOYOT+WGITCx9XjB8GC9nDI"
< date: Thu, 10 Aug 2023 06:03:01 GMT
< x-envoy-upstream-service-time: 5
< server: envoy
< 
Hello World!!
```

* TLSの証明書をcert-managerで発行する場合

```
kubectl create ingress hello-nodejs -n demo \
  --rule="hello-nodejs-demo.tap.192-168-228-200.sslip.io/*=hello-nodejs:8080,tls=hello-nodejs-tls" \
  --annotation cert-manager.io/cluster-issuer=tap-ingress-selfsigned
```

上記のannotationは`tap-ingress-selfsigned`というClusterIssuerが存在することが前提です。

アプリにアクセスします。

```
$ curl -skv https://hello-nodejs-demo.tap.192-168-228-200.sslip.io
> GET / HTTP/2
> Host: hello-nodejs-demo.tap.192-168-228-200.sslip.io
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/2 200 
< x-powered-by: Express
< content-type: text/html; charset=utf-8
< content-length: 14
< etag: W/"e-LQS9yOYOT+WGITCx9XjB8GC9nDI"
< date: Thu, 10 Aug 2023 06:03:53 GMT
< x-envoy-upstream-service-time: 5
< server: envoy
< 
Hello World!!
```

* Contourの[TLS Certificate Delegation](https://projectcontour.io/docs/1.25/config/tls-delegation/)を使用する場合

```
kubectl create ingress hello-nodejs -n demo \
  --rule="hello-nodejs-demo.tap.192-168-228-200.sslip.io/*=hello-nodejs:8080,tls=tap-default-tls" \
  --annotation projectcontour.io/tls-cert-namespace=tanzu-system-ingress
```

上記のannotationは`tanzu-system-ingress` namespaceに`*.tap.192-168-228-200.sslip.io`に対するWildcard証明書を含む`tap-default-tls`というSecretが存在し、TLSCertificateDelegationで参照可能になっていることが前提です。

`kubectl create`を実行する代わりにYAMLを管理したい場合は、上記のコマンドに`--dry-run=client -oyaml`オプションをつければ良いです。
また、Contour以外のIngressを使用したい場合は`--class`でIngressClass名を指定すれば良いです。

例えば次のようにTAPのRunクラスタに[ingress-nginx](https://kubernetes.github.io/ingress-nginx/)がインストールされている場合、

```
$ kubectl get ingressclass
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       3m30s
```

次のように`--class=nginx`をつければ良いです。 (ingress-nginx-controllerのExternalIPが`192.168.228.201`である前提です。)

```
kubectl create ingress hello-nodejs -n demo \
  --rule="hello-nodejs-demo.tap.192-168-228-201.sslip.io/*=hello-nodejs:8080" \
  --class=nginx
```

アプリにアクセスします。

```
$ curl -sv http://hello-nodejs-demo.tap.192-168-228-201.sslip.io
> GET / HTTP/1.1
> Host: hello-nodejs-demo.tap.192-168-228-201.sslip.io
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 10 Aug 2023 06:11:16 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 14
< Connection: keep-alive
< X-Powered-By: Express
< ETag: W/"e-LQS9yOYOT+WGITCx9XjB8GC9nDI"
< 
Hello World!!
```

この方法はTAPのカスタマイズが不要で、シンプルである反面、デフォルトでは開発者(`app-editor`)がIngressを作成できないので、`app-operator`に作成を依頼するか、次のように`app-editor`に権限を追加する必要があります。

```yaml
kubectl apply -f -  << 'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: app-editor-with-ingress
  labels:
    apps.tanzu.vmware.com/aggregate-to-app-editor: "true"
rules:
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
  - create
  - patch
  - update
  - delete
  - deletecollection
EOF
```

また、Ingressリソースを別途管理する手間が発生します。
次のOOTB SupplyChainで作成されるリソースにIngressを追加する方法であれば、これらの課題は解決されます。

### OOTB SupplyChainで作成されるリソースにIngressを追加する

以下のドキュメントに書かれている通り、ClusterConfigTemplateをカスタマイズすることでWorkloadから生成されるマニフェストにIngressを追加することができます。 <br>
https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/workloads-server.html#define-a-workload-type-that-exposes-server-workloads-outside-the-cluster-5

この場合、`type=server-ingress`のように新規のWorkload Typeを追加するか、既存の`type=server`にIngressを追加するかの選択肢があります。

#### 新規のWorkload Typeを追加する

既存の`server-template`をコピーして、`server-ingress-template`を作成します。

`server-ingress-template`を以下に定義します。`server-template`からの差分は`<------------ Added ---------------->`の部分です。

```yaml
apiVersion: carto.run/v1alpha1
kind: ClusterConfigTemplate
metadata:
  name: server-ingress-template
spec:
  configPath: .data

  params:
  - name: ports
    default:
    - containerPort: 8080
      port: 8080
      name: http

  healthRule:
    alwaysHealthy: {}

  ytt: |
    #@ load("@ytt:data", "data")
    #@ load("@ytt:yaml", "yaml")
    #@ load("@ytt:struct", "struct")
    #@ load("@ytt:assert", "assert")

    #@ def merge_labels(fixed_values):
    #@   labels = {}
    #@   if hasattr(data.values.workload.metadata, "labels"):
    #@     labels.update(data.values.workload.metadata.labels)
    #@   end
    #@   labels.update(fixed_values)
    #@   return labels
    #@ end

    #@ def intOrString(v):
    #@   return v if type(v) == "int" else int(v.strip()) if v.strip().isdigit() else v
    #@ end

    #@ def merge_ports(ports_spec, containers):
    #@   ports = {}
    #@   for c in containers:
    #@     for p in getattr(c, "ports", []):
    #@       ports[p.containerPort] = {"targetPort": p.containerPort, "port": p.containerPort, "name": getattr(p, "name", str(p.containerPort))}
    #@     end
    #@   end
    #@   for p in ports_spec:
    #@     targetPort = getattr(p, "containerPort", p.port)
    #@     type(targetPort) in ("string", "int") or fail("containerPort must be a string or int")
    #@     targetPort = intOrString(targetPort)
    #@     
    #@     port = p.port
    #@     type(port) in ("string", "int") or fail("port must be a string or int")
    #@     port = int(port)
    #@     ports[p.port] = {"targetPort": targetPort, "port": port, "name": getattr(p, "name", str(p.port))}
    #@   end
    #@   return ports.values()
    #@ end

    #! <------------ Added ---------------->
    #@ def merge_annotations(fixed_values):
    #@   annotations = {}
    #@   if hasattr(data.values.params, "annotations"):
    #@     annotations.update(data.values.params.annotations)
    #@   end
    #@   annotations.update(fixed_values)
    #@   return annotations
    #@ end
    #! <------------ Added ---------------->
  
    #@ def delivery():
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: #@ data.values.workload.metadata.name
      annotations:
        kapp.k14s.io/update-strategy: "fallback-on-replace"
        ootb.apps.tanzu.vmware.com/servicebinding-workload: "true"
        kapp.k14s.io/change-rule: "upsert after upserting servicebinding.io/ServiceBindings"
      labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })
    spec:
      selector:
        matchLabels: #@ data.values.config.metadata.labels
      template: #@ data.values.config
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: #@ data.values.workload.metadata.name
      labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })
    spec:
      selector: #@ data.values.config.metadata.labels
      ports:
      #@ hasattr(data.values.params, "ports") and len(data.values.params.ports) or assert.fail("one or more ports param must be provided.")
      #@ declared_ports = {}
      #@ if "ports" in data.values.params:
      #@   declared_ports = data.values.params.ports
      #@ else:
      #@   declared_ports = struct.encode([{ "containerPort": 8080, "port": 8080, "name": "http"}])
      #@ end
      #@ for p in merge_ports(declared_ports, data.values.config.spec.containers):
      - #@ p
      #@ end
    #! <------------ Added ---------------->
    #@ ingress_domain = "tap.192-168-228-200.sslip.io"
    #@ cluster_issuer = "tap-ingress-selfsigned"
    #@ port = intOrString(data.values.params.ports[0].port) if hasattr(data.values.params, "ports") and len(data.values.params.ports) > 0 and hasattr(data.values.params.ports[0], "port") else 8080
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: #@ data.values.workload.metadata.name
      annotations: #@ merge_annotations({"cert-manager.io/cluster-issuer": cluster_issuer, "kapp.k14s.io/change-rule": "upsert after upserting Services"})
      labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })
    spec:
      tls:
        - secretName: #@ "{}-tls".format(data.values.workload.metadata.name)
          hosts:
          - #@ "{}-{}.{}".format(data.values.workload.metadata.name, data.values.workload.metadata.namespace, ingress_domain)
      rules:
      - host: #@ "{}-{}.{}".format(data.values.workload.metadata.name, data.values.workload.metadata.namespace, ingress_domain)
        http:
          paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: #@ data.values.workload.metadata.name
                port:
                  number: #@ port
    #! <------------ Added ---------------->
    #@ end

    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: #@ data.values.workload.metadata.name + "-server"
      labels: #@ merge_labels({ "app.kubernetes.io/component": "config" })
    data:
      delivery.yml: #@ yaml.encode(delivery())
```

この内容を`server-ingress-template.yaml`に保存し、applyします。TAPのmulti cluster構成の場合は、Buildクラスタで実行してください。

```
kubectl apply -f server-ingress-template.yaml
```

またSupplyChainから生成されたIngressリソースをDeliverableが作成できるように、次のコマンドでClusterRoleを追加します。TAPのmulti cluster構成の場合は、Runクラスタで実行してください。

```yaml
cat << 'EOF' > deliverable-with-ingress.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deliverable-with-ingress
  labels:
    apps.tanzu.vmware.com/aggregate-to-deliverable: "true"
rules:
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
  - create
  - patch
  - update
  - delete
  - deletecollection
EOF
kubectl apply -f deliverable-with-ingress.yaml
```

`tap-values.yaml`に以下の内容を追加します。

```yaml
ootb_supply_chain_basic:
  supported_workloads:
    - type: web
      cluster_config_template_name: config-template
    - type: server
      cluster_config_template_name: server-template
    - type: worker
      cluster_config_template_name: worker-template
    - type: server-ingress #! <--- Added
      cluster_config_template_name: server-ingress-template
```

設定するキーは`ootb_supply_chain_<suppy_chain>`であり、上記の例は`supply_chain: basic`の場合です。

次のコマンドでTAPを更新します。TAPのmulti cluster構成の場合は、Buildクラスタで実行してください。

```
tanzu package installed update -n tap-install tap --values-file tap-values.yaml
```

次のコマンドを実行して、利用可能なWorkload Typeに`server-ingress`が追加されたことを確認してください。

```
$ tanzu apps cluster-supply-chain get source-to-url
---
# source-to-url: Ready
---
Supply Chain Selectors
   TYPE          KEY                                             OPERATOR   VALUE
   expressions   apps.tanzu.vmware.com/workload-type             In         web
   expressions   apps.tanzu.vmware.com/workload-type             In         server
   expressions   apps.tanzu.vmware.com/workload-type             In         worker
   expressions   apps.tanzu.vmware.com/workload-type             In         server-ingress
   expressions   apps.tanzu.vmware.com/carvel-package-workflow   DoesNotExist
```

次のコマンドで、`type=server-ingress`のWorkloadを作成します。

```
kubectl delete ingress hello-nodejs -n demo # if exists
tanzu apps workload apply hello-nodejs \
  --app hello-nodejs \
  --git-repo https://github.com/making/hello-nodejs \
  --git-branch master \
  --type server-ingress \
  -n demo
```

次のコマンドでWorkloadによって作成されたYAMLを確認できます。

```
kubectl get cm -n demo hello-nodejs-server -ojsonpath='{.data.delivery\.yml}'
```

次のようなYAMLが出力されます。Ingressが追加されています。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-nodejs
  annotations:
    kapp.k14s.io/update-strategy: fallback-on-replace
    ootb.apps.tanzu.vmware.com/servicebinding-workload: "true"
    kapp.k14s.io/change-rule: upsert after upserting servicebinding.io/ServiceBindings
  labels:
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server-ingress
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: run
      app.kubernetes.io/part-of: hello-nodejs
      apps.tanzu.vmware.com/workload-type: server-ingress
      carto.run/workload-name: hello-nodejs
  template:
    metadata:
      annotations:
        conventions.carto.run/applied-conventions: |-
          appliveview-sample/app-live-view-appflavour-check
          spring-boot-convention/auto-configure-actuators-check
          spring-boot-convention/app-live-view-appflavour-check
        developer.conventions/target-containers: workload
      labels:
        app.kubernetes.io/component: run
        app.kubernetes.io/part-of: hello-nodejs
        apps.tanzu.vmware.com/workload-type: server-ingress
        carto.run/workload-name: hello-nodejs
    spec:
      containers:
      - image: ghcr.io/making/workloads/hello-nodejs-demo@sha256:052ffee7966eeda9cf3a0d6a255f70b443ed0c39ab24591ab4b9ab857b30995b
        name: workload
        resources: {}
        securityContext:
          runAsUser: 1000
      serviceAccountName: default
---
apiVersion: v1
kind: Service
metadata:
  name: hello-nodejs
  labels:
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server-ingress
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
spec:
  selector:
    app.kubernetes.io/component: run
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server-ingress
    carto.run/workload-name: hello-nodejs
  ports:
  - targetPort: 8080
    port: 8080
    name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-nodejs
  annotations:
    cert-manager.io/cluster-issuer: tap-ingress-selfsigned
    kapp.k14s.io/change-rule: upsert after upserting Services
  labels:
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server-ingress
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
spec:
  tls:
  - secretName: hello-nodejs-tls
    hosts:
    - hello-nodejs-demo.tap.192-168-228-200.sslip.io
  rules:
  - host: hello-nodejs-demo.tap.192-168-228-200.sslip.io
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: hello-nodejs
            port:
              number: 8080
```

Supply ChainからIngressリソースが作成されたことを確認できます。

```
$ kubectl get ing -n demo --show-labels
NAME           CLASS    HOSTS                                            ADDRESS           PORTS     AGE   LABELS
hello-nodejs   <none>   hello-nodejs-demo.tap.192-168-228-200.sslip.io   192.168.228.200   80, 443   96s   app.kubernetes.io/component=run,app.kubernetes.io/part-of=hello-nodejs,apps.tanzu.vmware.com/workload-type=server-ingress,carto.run/workload-name=hello-nodejs,kapp.k14s.io/app=1691647255322645686,kapp.k14s.io/association=v1.4aa1edeb0a707e6a5e81cfde4668a7b0
```

```
$ curl -ks https://hello-nodejs-demo.tap.192-168-228-200.sslip.io 
Hello World!!
```


ClusterConfigTemplateの追加とClusterRoleの追加を`tanzu package install`のタイミングでまとめて行いたい場合は、次のようにoverlayとしてファイルをSecretに登録すれば良いです。

```
# Muti Cluster構成の場合はBuild Clusterにて
kubectl -n tap-install create secret generic ootb-templates-server-ingress-template \
  -o yaml \
  --dry-run=client \
  --from-file=server-ingress-template.yaml \
  | kubectl apply -f-
```

```
# Muti Cluster構成の場合はRun Clusterにて
kubectl -n tap-install create secret generic tap-auth-deliverable-with-ingress \
  -o yaml \
  --dry-run=client \
  --from-file=deliverable-with-ingress.yaml \
  | kubectl apply -f-
```

`tap-values.yaml`に以下の設定を追加。

```yaml
package_overlays:
- name: ootb-templates
  secrets:
  - name: ootb-templates-server-ingress-template # Muti Cluster構成の場合はBuild Clusterにて
- name: tap-auth
  secrets:
  - name: tap-auth-deliverable-with-ingress # Muti Cluster構成の場合はRun Clusterにて
```

TAPを更新します。

```
tanzu package installed update -n tap-install tap --values-file tap-values.yaml
```

#### 既存の`type=server`にIngressを追加する

新規のWorkload Typeを追加する方法はTemplateを自分で管理できるので、カスタマイズしやすい一方、
コピー元の`type=server`のテンプレートが更新されたら、更新内容を反映させる必要があります。

次のoverlayを適用して、既存のClusterConfigTemplate `server-template`を変更します。

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: ootb-templates-overlay-ingress
  namespace: tap-install
type: Opaque
stringData:
  overlay-ingress.yaml: |
    #@ def merge_annotations_def_string():
    #@   return '''
    #@ #@ def merge_annotations(fixed_values):
    #@ #@   annotations = {}
    #@ #@   if hasattr(data.values.params, "annotations"):
    #@ #@     annotations.update(data.values.params.annotations)
    #@ #@   end
    #@ #@   annotations.update(fixed_values)
    #@ #@   return annotations
    #@ #@ end
    #@ '''
    #@ end
    
    #@ load("@ytt:overlay", "overlay")
    #@ load("@ytt:data", "data")
    #@ ingress_domain = data.values.ingress_domain
    #@ cluster_issuer = data.values.cluster_issuer
    #@overlay/match by=overlay.subset({"kind":"ClusterConfigTemplate", "metadata": {"name": "server-template"}})
    ---
    spec:
      #@overlay/replace via=lambda left, right: "{}\n{}".format(left.replace("#@ def delivery():", '\n'.join([ merge_annotations_def_string(), "#@ def delivery():"])), '\n'.join(['  {}'.format(x) for x in right.replace("INGRESS_DOMAIN", ingress_domain).replace("CLUSTER_ISSUER", cluster_issuer).split('\n')]))
      ytt: |
        #@yaml/text-templated-strings
        ingress.yml: |
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          metadata:
            name: (@= data.values.workload.metadata.name @)
            annotations:
          (@= '\n'.join(['    {}'.format(x) for x in yaml.encode(merge_annotations({ "cert-manager.io/cluster-issuer": "CLUSTER_ISSUER", "kapp.k14s.io/change-rule": "upsert after upserting Services" })).split('\n')]) @)
            labels: 
          (@= '\n'.join(['    {}'.format(x) for x in yaml.encode(merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })).split('\n')]) @)
          spec:
            tls:
              - secretName: (@= data.values.workload.metadata.name @)-tls
                hosts:
                - (@= "{}-{}.{}".format(data.values.workload.metadata.name, data.values.workload.metadata.namespace, "INGRESS_DOMAIN") @)
            rules:
            - host: (@= "{}-{}.{}".format(data.values.workload.metadata.name, data.values.workload.metadata.namespace, "INGRESS_DOMAIN") @)
              http:
                paths:
                - pathType: Prefix
                  path: /
                  backend:
                    service:
                      name: (@= data.values.workload.metadata.name @)
                      port:
                        number: (@= str(data.values.params.ports[0].port) if hasattr(data.values.params, "ports") and len(data.values.params.ports) > 0 and hasattr(data.values.params.ports[0], "port") else "8080" @)
```

このファイルを`ootb-templates-overlay-ingress.yaml`に保存して、applyします。

```
kubectl apply -f ootb-templates-overlay-ingress.yaml
```

`tap-values.yaml`に次の設定を追加します。

```yaml
ootb_templates:
  ingress_domain: tap.192-168-228-200.sslip.io
  cluster_issuer: tap-ingress-selfsigned

package_overlays:
- name: ootb-templates # Muti Cluster構成の場合はBuild Clusterにて
  secrets:
  - name: ootb-templates-overlay-ingress
```

TAPを更新します。

```
tanzu package installed update -n tap-install tap --values-file tap-values.yaml
```

`type=server`でWorkloadを作成します。

```
tanzu apps workload apply hello-nodejs \
  --app hello-nodejs \
  --git-repo https://github.com/making/hello-nodejs \
  --git-branch master \
  --type server \
  -n demo
```

次のコマンドでWorkloadによって作成されたIngressのYAMLを確認できます。

```
kubectl get cm -n demo hello-nodejs-server -ojsonpath='{.data.ingress\.yml}'
```

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-nodejs
  annotations:
    cert-manager.io/cluster-issuer: tap-ingress-selfsigned
    kapp.k14s.io/change-rule: upsert after upserting Services
    
  labels: 
    app.kubernetes.io/part-of: hello-nodejs
    apps.tanzu.vmware.com/workload-type: server
    app.kubernetes.io/component: run
    carto.run/workload-name: hello-nodejs
    
spec:
  tls:
    - secretName: hello-nodejs-tls
      hosts:
      - hello-nodejs-demo.tap.192-168-228-200.sslip.io
  rules:
  - host: hello-nodejs-demo.tap.192-168-228-200.sslip.io
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: hello-nodejs
            port:
              number: 8080
```

Supply ChainからIngressリソースが作成されたことを確認できます。

```
$ kubectl get ing -n demo --show-labels
NAME           CLASS    HOSTS                                            ADDRESS           PORTS     AGE    LABELS
hello-nodejs   <none>   hello-nodejs-demo.tap.192-168-228-200.sslip.io   192.168.228.200   80, 443   154m   app.kubernetes.io/component=run,app.kubernetes.io/part-of=hello-nodejs,apps.tanzu.vmware.com/workload-type=server,carto.run/workload-name=hello-nodejs,kapp.k14s.io/app=1691647255322645686,kapp.k14s.io/association=v1.4aa1edeb0a707e6a5e81cfde4668a7b0
```

新規のWorkload Typeを追加する場合に比べて、既存のWorkload Typeを更新する方法は、TAPのバージョンアップでコピー元のtemplateの変更を自動でマージするので、変更を反映させる手間が入りません。
一方で、overlayが次のバージョンでも機能するかどうかはわかりません。バージョンアップ時には必ず動作検証する必要があります。


Supply ChainからIngressを生成する方法は、自動でIngressリソースができるので便利なのですが、Workloadを作る時点で作成されるIngressのhost名が固定されてしまう点です。
今回の例だとIngressリソースに`hello-nodejs-demo.tap.192-168-228-200.sslip.io`がハードコードされます。
アプリが動作する環境(Runクラスタなど)が1環境の場合は問題ないのですが、複数のRunクラスタにデプロイしたい場合は問題になります。Supply Chainから生成されるマニフェストを環境に応じてパラメータで変更することができません。

Workaroundは[Gitopsモード](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-gitops-vs-regops.html#gitops-0)を利用して、別のRunクラスタに対しては次のようなoverlayをGitレポジトリ上に作れば、デプロイ時にhost名が上書きされます。

```yaml
#@ load("@ytt:overlay", "overlay")
#@ ingress_domain = "hello-nodejs-demo.production.example.com"
#@overlay/match by=overlay.subset({"kind":"Ingress"})
---
spec:
  tls:
  #@overlay/match by=overlay.index(0)
  - hosts:
    #@overlay/match by=overlay.index(0)
    - #@ ingress_domain
  rules:
  #@overlay/match by=overlay.index(0)
  - host: #@ ingress_domain
```

### Carvel Package Supply Chainを使用する

Supply Chainから生成されるマニフェストを環境に応じてパラメータで変更することができないという課題を解決するのが[Carvel Package Supply Chains](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-config-deploy-multi-env.html)です。
TAP 1.5でAlphaバージョンとして導入され、TAP 1.6時点ではBetaバージョンです。まだ、実験的な機能で制約が多く、 OOTB Basic Supply Chainでのみ利用可能です。

Carvel Package Supply Chainsは通常のSupply Chainのようにマニフェストを作り、pushしますが、そのマニフェストをデプロイするためにDeliverableを作る代わりに、CarvelのPackageリソースを作成します。
このPackageを実行環境（Runクラスタなど）にインストールします。この時作成するPackageInstallリソースに対するパラメータを渡すことで実行環境に応じてパラメータを変更することができるようになります。
Carvel Package Supply Chainsでは`type=server`の場合にIngressリソースも作成されます。

なお、TAP 1.6時点では、マニフェストのpush先はGit Repositoryのみなので、[Gitopsモード](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-gitops-vs-regops.html#gitops-0)が必須です。

Carvel Package Supply Chainsを有効するには、`tap-values.yaml`に次の設定を明示的に追加する必要があります。multi clusterの場合は、Buildクラスタでこの設定が必要です。

```yaml
ootb_supply_chain_basic:
  carvel_package:
    workflow_enabled: true
```

この設定を有効にして、次のコマンドでTAPを更新します。

```
tanzu package installed update -n tap-install tap --values-file tap-values.yaml 
```

> ⚠️ Ingressの作成が重複するため、`tap-values.yaml`から`ootb-templates-overlay-ingress`のoverlay設定を削除してください。


Supply Chainリストを確認すると`source-to-url-package`が追加されていることがわかります。

```
$ tanzu apps cluster-supply-chain list
NAME                         READY   AGE
basic-image-to-url           Ready   6h31m
basic-image-to-url-package   Ready   89s
source-to-url                Ready   6h31m
source-to-url-package        Ready   89s
```

このSupply Chainを使用するためのパラメータを確認すると、次のようにラベルで `apps.tanzu.vmware.com/carvel-package-workflow=true` を設定する必要があります。 

```
$ tanzu apps cluster-supply-chain get source-to-url-package
---
# source-to-url-package: Ready
---
Supply Chain Selectors
   TYPE          KEY                                             OPERATOR   VALUE
   expressions   apps.tanzu.vmware.com/workload-type             In         web
   expressions   apps.tanzu.vmware.com/workload-type             In         server
   expressions   apps.tanzu.vmware.com/workload-type             In         worker
   expressions   apps.tanzu.vmware.com/carvel-package-workflow   In         true
```

実際に次のコマンドでCarvel Package Supply Chainを使ったWorkloadを作成します。 GitOpsモードを使うための設定は本記事では省略します。

```
tanzu apps workload apply hello-nodejs \
  --app hello-nodejs \
  --git-repo https://github.com/making/hello-nodejs \
  --git-branch master \
  --label apps.tanzu.vmware.com/carvel-package-workflow=true \
  --type server \
  --param gitops_branch=main \
  --param gitops_commit_message=Bump \
  --param gitops_server_address=https://github.com \
  --param gitops_repository_owner=making \
  --param gitops_repository_name=tap-gitops-manifests \
  --param gitops_user_email=makingx+bot@gmail.com \
  --param gitops_user_name=making-bot \
  --param gitops_ssh_secret=git-basic \
  -n demo
```

WorkloadのReadyになると、次のようなリソースが作成されます。

```
$ tanzu apps workload get hello-nodejs --namespace demo 
📡 Overview
   name:        hello-nodejs
   type:        server
   namespace:   demo

💾 Source
   type:       git
   url:        https://github.com/making/hello-nodejs
   branch:     master
   revision:   master@sha1:fde413c0fba0003c218a60bde69c8e254d3b15a6

📦 Supply Chain
   name:   source-to-url-package

   NAME               READY   HEALTHY   UPDATED   RESOURCE
   source-provider    True    True      6m25s     gitrepositories.source.toolkit.fluxcd.io/hello-nodejs
   image-provider     True    True      5m29s     images.kpack.io/hello-nodejs
   config-provider    True    True      5m22s     podintents.conventions.carto.run/hello-nodejs
   app-config         True    True      5m22s     configmaps/hello-nodejs-server
   service-bindings   True    True      5m22s     configmaps/hello-nodejs-with-claims
   api-descriptors    True    True      5m22s     configmaps/hello-nodejs-with-api-descriptors
   carvel-package     True    True      5m8s      taskruns.tekton.dev/hello-nodejs-carvel-package-j86xm
   config-writer      True    True      4m55s     runnables.carto.run/hello-nodejs-pkg-cfg-writer

🚚 Delivery

   Delivery resources not found.

💬 Messages
   No messages found.

🛶 Pods
   NAME                                    READY   STATUS      RESTARTS   AGE
   hello-nodejs-build-1-build-pod          0/1     Completed   0          6m27s
   hello-nodejs-carvel-package-j86xm-pod   0/3     Completed   0          5m22s
   hello-nodejs-pkg-cfg-writer-jsg9x-pod   0/2     Completed   0          5m6s
```

`hello-nodejs-carvel-package-****`でマニフェストを`imgpkg`でバンドル化してpushし、`hello-nodejs-pkg-cfg-writer-****`でそのimgpkgBundleをPackageリソースに定義し、gitレポジトリにpushします。
実際にpushされたPackageマニフェストは https://github.com/making/tap-gitops-manifests/blob/main/hello-nodejs.demo.tap/packages/20230811080453.0.0.yml です。

バンドル化されたマニフェストを次のコマンドでダウンロード可能です。(visibilityをpublicにしてあるので実際に実行して内容を確認できます)

```
imgpkg pull -b ghcr.io/making/workloads/hello-nodejs-demo-bundle@sha256:b0a013325c4c089befffd72643d576fb1109bebadce87f2f7b3615aef5ed9d75 -o /tmp/hello-nodejs
```

ではこのGit上にpushされたPackageリソースをapplyします。multi clusterの場合はRunクラスタにapplyします。
ドキュメントではここでGitOpsツールを使用することを推奨しており、

* [CarvelのAppリソース](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-delivery-with-carvel-app.html)
* [FluxのGitRepositoryリソース](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-delivery-with-flux.html)
* [ArgoCDのApplicationリソース](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-delivery-with-argo.html)

を使用する方法が紹介されています。前者の2つは追加のインストールなしで利用可能です。

ここではセットアップを省略するため、GitOpsを使用せず単純に次のように`kubectl apply`でPackageリソースを作成します。

```
kubectl apply -f https://github.com/making/tap-gitops-manifests/raw/main/hello-nodejs.demo.tap/packages/20230811080453.0.0.yml -n demo
```

次のPackageリソースができました。

```
$ kubectl get package -n demo
NAME                                                     PACKAGEMETADATA NAME    VERSION                            AGE
hello-nodejs.demo.tap.20230811080453.0.0+build.fde413c   hello-nodejs.demo.tap   20230811080453.0.0+build.fde413c   5s
```

このPackageのパラメータを確認します。

```
$ tanzu package available get -n demo hello-nodejs.demo.tap/20230811080453.0.0+build.fde413c --values-schema

  KEY             DEFAULT                 TYPE     DESCRIPTION                                                         
  replicas        1                       integer  Number of replicas.                                                 
  workload_name   ""                      string   Required. Name of the workload, used by K8s Ingress HTTP rules.     
  cluster_issuer  tap-ingress-selfsigned  string   CertManager Issuer to use to generate certificate for K8s Ingress.  
  hostname        ""                      string   If set, K8s Ingress will be created with HTTP rules for hostname.   
  port            8080                    integer  Port number for the backend associated with K8s Ingress. 
```

Ingressを利用するために、`workload_name`を`hostname`を設定します。

```
cat <<EOF > hello-nodejs-values.yaml
workload_name: hello-nodejs
hostname: hello-nodejs-demo.stg.192-168-228-200.sslip.io
EOF
```

次のコマンドでこのパッケージをインストールします。multi clusterの場合はRunクラスタで実行します。`-v`に`>= 0.0.0`を指定することで、新しいPackageがKubernetes上に作成されると自動でその新しいバージョンにアップデートされます。

```
tanzu package install -n demo hello-nodejs -p hello-nodejs.demo.tap -v ">= 0.0.0" --values-file hello-nodejs-values.yaml
```

インストールが完了したら、次のようなリソースが作成されたことがわかります。

```
$ kubectl get pkgi,deploy,svc,ing -n demo 
NAME                                               PACKAGE NAME            PACKAGE VERSION                    DESCRIPTION           AGE
packageinstall.packaging.carvel.dev/hello-nodejs   hello-nodejs.demo.tap   20230811080453.0.0+build.fde413c   Reconcile succeeded   12s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-nodejs   1/1     1            1           9s

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/hello-nodejs   ClusterIP   10.96.113.106   <none>        8080/TCP   9s

NAME                                     CLASS    HOSTS                                            ADDRESS           PORTS     AGE
ingress.networking.k8s.io/hello-nodejs   <none>   hello-nodejs-demo.stg.192-168-228-200.sslip.io   192.168.228.200   80, 443   9s
```

Ingressで公開されたURLにアクセス可能です。

```
$ curl -k https://hello-nodejs-demo.stg.192-168-228-200.sslip.io 
Hello World!!
```

Carvel Package Supply Chainの場合、通常のSupply Chainの`type=server`なWorkloadで作成されるリソース(Deployment, Service)に加えて、Ingressが作成されました。
追加作成されるリソースはoverlayで定義されています。このoverlayファイルと変更可能なパラメータの定義はTAP 1.6から`tap-values.yaml`で上書き可能です。
デフォルトでは次のような定義になっています。

```yaml
ootb_templates:
  carvel_package:
    parameters:
      - selector:
          matchLabels:
            apps.tanzu.vmware.com/workload-type: server
        schema: |
          #@data/values-schema
          ---
          #@schema/title "Workload name"
          #@schema/desc "Required. Name of the workload, used by K8s Ingress HTTP rules."
          #@schema/example "tanzu-java-web-app"
          #@schema/validation min_len=1
          workload_name: ""
  
          #@schema/title "Replicas"
          #@schema/desc "Number of replicas."
          replicas: 1
  
          #@schema/title "Port"
          #@schema/desc "Port number for the backend associated with K8s Ingress."
          port: 8080
  
          #@schema/title "Hostname"
          #@schema/desc "If set, K8s Ingress will be created with HTTP rules for hostname."
          #@schema/example "app.tanzu.vmware.com"
          hostname: ""
  
          #@schema/title "Cluster Issuer"
          #@schema/desc "CertManager Issuer to use to generate certificate for K8s Ingress."
          cluster_issuer: "tap-ingress-selfsigned"
        overlays: |
          #@ load("@ytt:overlay", "overlay")
          #@ load("@ytt:data", "data")
          #@overlay/match by=overlay.subset({"apiVersion":"apps/v1", "kind": "Deployment"})
          ---
          spec:
            #@overlay/match missing_ok=True
            replicas: #@ data.values.replicas
  
          #@ if data.values.hostname != "":
          ---
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          metadata:
            name: #@ data.values.workload_name
            annotations:
              cert-manager.io/cluster-issuer:  #@ data.values.cluster_issuer
              ingress.kubernetes.io/force-ssl-redirect: "true"
              kubernetes.io/ingress.class: contour
              kapp.k14s.io/change-rule: "upsert after upserting Services"
            labels:
              app.kubernetes.io/component: "run"
              carto.run/workload-name:  #@ data.values.workload_name
          spec:
            tls:
              - secretName: #@ data.values.workload_name
                hosts:
                - #@ data.values.hostname
            rules:
            - host: #@ data.values.hostname
              http:
                paths:
                - pathType: Prefix
                  path: /
                  backend:
                    service:
                      name: #@ data.values.workload_name
                      port:
                        number: #@ data.values.port
          #@ end
```

IngressにContourを利用するannotationがついています。前述のようにNginx Ingressを使うようにカスタマイズしてみます。
`tap-values.yaml`に次の設定を行います。


```yaml
ootb_templates:
  carvel_package:
    parameters:
    - selector:
        matchLabels:
          apps.tanzu.vmware.com/workload-type: server
      schema: |
        #@data/values-schema
        ---
        #@schema/title "Workload name"
        #@schema/desc "Required. Name of the workload, used by K8s Ingress HTTP rules."
        #@schema/example "tanzu-java-web-app"
        #@schema/validation min_len=1
        workload_name: ""

        #@schema/title "Replicas"
        #@schema/desc "Number of replicas."
        replicas: 1

        #@schema/title "Port"
        #@schema/desc "Port number for the backend associated with K8s Ingress."
        port: 8080

        #@schema/title "Hostname"
        #@schema/desc "If set, K8s Ingress will be created with HTTP rules for hostname."
        #@schema/example "app.tanzu.vmware.com"
        hostname: ""

        #@schema/title "Cluster Issuer"
        #@schema/desc "CertManager Issuer to use to generate certificate for K8s Ingress."
        cluster_issuer: "tap-ingress-selfsigned"
      overlays: |
        #@ load("@ytt:overlay", "overlay")
        #@ load("@ytt:data", "data")
        #@overlay/match by=overlay.subset({"apiVersion":"apps/v1", "kind": "Deployment"})
        ---
        spec:
          #@overlay/match missing_ok=True
          replicas: #@ data.values.replicas

        #@ if data.values.hostname != "":
        ---
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:
          name: #@ data.values.workload_name
          annotations:
            cert-manager.io/cluster-issuer:  #@ data.values.cluster_issuer
            ingress.kubernetes.io/force-ssl-redirect: "true"
            #! kubernetes.io/ingress.class: contour <----- Removed
            kapp.k14s.io/change-rule: "upsert after upserting Services"
          labels:
            app.kubernetes.io/component: "run"
            carto.run/workload-name:  #@ data.values.workload_name
        spec:
          ingressClassName: nginx #! <----- Added
          tls:
            - secretName: #@ data.values.workload_name
              hosts:
              - #@ data.values.hostname
          rules:
          - host: #@ data.values.hostname
            http:
              paths:
              - pathType: Prefix
                path: /
                backend:
                  service:
                    name: #@ data.values.workload_name
                    port:
                      number: #@ data.values.port
        #@ end
```

次のコマンドでTAPを更新します。

```
tanzu package installed update -n tap-install tap --values-file tap-values.yaml 
```

Workloadの状態を確認すると、`hello-nodejs-carvel-package-****`と`hello-nodejs-pkg-cfg-writer-****`が新たに生成されていることがわかります。
テンプレートの変更を検知して、新しいマニフェストのimgpkg bundle作成とgit pushが行われました。

```
$ tanzu apps workload get hello-nodejs --namespace demo 
📡 Overview
   name:        hello-nodejs
   type:        server
   namespace:   demo

💾 Source
   type:       git
   url:        https://github.com/making/hello-nodejs
   branch:     master
   revision:   master@sha1:fde413c0fba0003c218a60bde69c8e254d3b15a6

📦 Supply Chain
   name:   source-to-url-package

   NAME               READY     HEALTHY   UPDATED   RESOURCE
   source-provider    True      True      9m41s     gitrepositories.source.toolkit.fluxcd.io/hello-nodejs
   image-provider     True      True      8m45s     images.kpack.io/hello-nodejs
   config-provider    True      True      8m38s     podintents.conventions.carto.run/hello-nodejs
   app-config         True      True      8m38s     configmaps/hello-nodejs-server
   service-bindings   True      True      8m38s     configmaps/hello-nodejs-with-claims
   api-descriptors    True      True      8m38s     configmaps/hello-nodejs-with-api-descriptors
   carvel-package     True      True      11s       taskruns.tekton.dev/hello-nodejs-carvel-package-v7psj
   config-writer      Unknown   Unknown   4s        runnables.carto.run/hello-nodejs-pkg-cfg-writer

🚚 Delivery

   Delivery resources not found.

💬 Messages
   Workload [HealthyConditionRule]:   Not all Steps in the Task have finished executing

🛶 Pods
   NAME                                    READY   STATUS      RESTARTS   AGE
   hello-nodejs-7455c98c94-c4zlt           1/1     Running     0          4m23s
   hello-nodejs-build-1-build-pod          0/1     Completed   0          9m44s
   hello-nodejs-carvel-package-j86xm-pod   0/3     Completed   0          8m39s
   hello-nodejs-carvel-package-v7psj-pod   0/3     Completed   0          26s
   hello-nodejs-pkg-cfg-writer-ctxfx-pod   0/2     Completed   0          13s
   hello-nodejs-pkg-cfg-writer-jsg9x-pod   0/2     Completed   0          8m23s

To see logs: "tanzu apps workload tail hello-nodejs --namespace demo --timestamp --since 1h"
```

生成されたPackageリソースは https://github.com/making/tap-gitops-manifests/blob/main/hello-nodejs.demo.tap/packages/20230811081306.0.0.yml です。
バンドル化されたマニフェストは次のコマンドでダウンロードできます。

```
imgpkg pull -b ghcr.io/making/workloads/hello-nodejs-demo-bundle@sha256:8971d6979d6823edbe502162ddb9ab6dde665ac807857dbe4ea89857ce2db28c -o /tmp/hello-nodejs
```

新しいPackageを`kubectl`でapplyします。GitOpsを使用している場合(推奨)は自動で新しいPackageが追加されます。

```
kubectl apply -f https://github.com/making/tap-gitops-manifests/raw/main/hello-nodejs.demo.tap/packages/20230811081306.0.0.yml -n demo
```

次のコマンドで新しいPackageを確認できます。

```
$ kubectl get package -n demo  
NAME                                                     PACKAGEMETADATA NAME    VERSION                            AGE
hello-nodejs.demo.tap.20230811080453.0.0+build.fde413c   hello-nodejs.demo.tap   20230811080453.0.0+build.fde413c   5m53s
hello-nodejs.demo.tap.20230811081306.0.0+build.fde413c   hello-nodejs.demo.tap   20230811081306.0.0+build.fde413c   6s
```

PackageInstallが新しいパッケージを検出して、自動でインストールします。次のコマンドで新しいバージョンのパッケージがデプロイされていることがわかり、Ingressのnginxのものが利用されていることがわかります。

```
$ kubectl get pkgi,deploy,svc,ing -n demo 
NAME                                               PACKAGE NAME            PACKAGE VERSION                    DESCRIPTION           AGE
packageinstall.packaging.carvel.dev/hello-nodejs   hello-nodejs.demo.tap   20230811081306.0.0+build.fde413c   Reconcile succeeded   5m44s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello-nodejs   1/1     1            1           5m41s

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/hello-nodejs   ClusterIP   10.96.113.106   <none>        8080/TCP   5m41s

NAME                                     CLASS   HOSTS                                            ADDRESS           PORTS     AGE
ingress.networking.k8s.io/hello-nodejs   nginx   hello-nodejs-demo.stg.192-168-228-200.sslip.io   192.168.228.201   80, 443   5m41s
```

Nginx Ingressを経由したRoutingを利用するために、次のようにhostnameを変更します。


```yaml
cat <<EOF > hello-nodejs-values.yaml
workload_name: hello-nodejs
hostname: hello-nodejs-demo.stg.192-168-228-201.sslip.io
EOF
```

新しいパラメータを次のコマンドで反映します。

```
tanzu package installed update -n demo hello-nodejs --values-file hello-nodejs-values.yaml
```

次のコマンドでIngressのhostnameが更新されたことを確認します。

```
$ kubectl get pkgi,deploy,svc,ing -n demo 
NAME                                               PACKAGE NAME            PACKAGE VERSION                    DESCRIPTION           AGE
packageinstall.packaging.carvel.dev/hello-nodejs   hello-nodejs.demo.tap   20230811081306.0.0+build.fde413c   Reconcile succeeded   6m26s

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

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/hello-nodejs   ClusterIP   10.96.113.106   <none>        8080/TCP   6m23s

NAME                                     CLASS   HOSTS                                            ADDRESS           PORTS     AGE
ingress.networking.k8s.io/hello-nodejs   nginx   hello-nodejs-demo.stg.192-168-228-201.sslip.io   192.168.228.201   80, 443   6m23s
```

新しいhostnameを使ってアプリにアクセスします。

```
$ curl -k https://hello-nodejs-demo.stg.192-168-228-201.sslip.io
Hello World!!
```

Carvel Package Supply Chainを使うと実行環境(Runクラスタ)毎に、パラメータを変えられることを試せました。
TAP 1.6時点では、実行環境の設定が少し煩雑に感じます。

---

`type=server`でアプリをデプロイした場合に、Ingressでアプリを公開したい場合の3つの方法を紹介しました。

* `kubectl`で直接Ingressリソースを作成する
* OOTB SupplyChainで作成されるリソースにIngressを追加する
* Carvel Package Supply Chainを使用する

TAP 1.6時点では、どれも一長一短です。Carvel Package Supply ChainがGAになれば、これが一番いい選択かもしれません。
現時点では`kubectl`で直接Ingressリソースを作成するか、OOTB SupplyChainで作成されるリソースにIngressを追加するのが現実的ではないでしょうか。
