0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Tanzu Application PlatformでPVをmountするアプリをデプロイする

Last updated at Posted at 2023-09-12

Tanzu Application Platform v1.6(以下TAP)ではアプリをtypewebでデプロイするとknativeでデプロイするが、knativeの場合、デフォルトでPVCが利用できない。

$ kubectl get cm -o yaml config-features -n knative-serving |grep persistent-volume-claim
    kubernetes.podspec-persistent-volume-claim: "disabled"

一方で、typeserverでデプロイするとDeploymentでデプロイするが、こちらもPVをデプロイするオプションはなく、PVCも作成できない。
作成したアプリにPVを割り当てたい場合は不便なので、普通のDeploymentでデプロイし、その際にPVCからPVを作成し、PVが割り当てられるようにする。
今回はPVCを作成するClusterConfigTemplateを作成し、Workloadタイプを新規に作成して、そのタイプで作成したClusterConfigTemplateを利用するようにして実現する。
なお、このメモをまとめるに当たってWarroyo氏のHow to create custom workload types with TAPを参考にした。

Workloadタイプの作成

既存のworkloadタイプではPVを作成するものがないので、PVを作成できるworkloadタイプを作成する。
ここでは将来的に使いまわしやすいよう、yttでoverlayする形でYAMLを作成する。
まず、typeserverで使っているClusterConfigTemplateを取り出した後、不要なフィールドを削除し、yttでoverlay出来るベースのテンプレートをとして利用可能な形に加工した。
取り出す際に叩いたコマンドは以下。

kubectl get clusterconfigtemplates.carto.run server-template -o yaml | kubectl neat -f - > ./server-template.yaml

加工後のClusterConfigTemplateは以下。

cat << EOF > ./server-template.yaml
#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:data", "data")
---
kind: ClusterConfigTemplate
metadata:
  annotations:
    doc: #@ data.values.doc
  name: #@ data.values.name
spec:
  configPath: .data
  lifecycle: mutable
  params:
  - default:
    - containerPort: 8080
      name: http
      port: 8080
    name: ports
  ytt: #@ data.values.ytt
EOF

また、overlayするvalues.yamlの雛形は以下で作成した。ytt部分はkubectl get clusterconfigtemplates.carto.run server-template -o jsonpath={.spec.ytt}の内容をコピペしている。

#@data/values
---
name: volumes-template
doc: |
  This template supports PVC.
  You can use this at own risk.
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

  #@ 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
  #@ 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())

これを以下のように変更する。

--- values.orig.yaml	2023-09-12 08:34:55.507524055 +0000
+++ values.yaml	2023-09-12 11:39:08.929620707 +0000
@@ -1,6 +1,6 @@
 #@data/values
 ---
-name: server-template
+name: volumes-template
 doc: |
   This template supports PVC.
   You can use this at own risk.
@@ -9,7 +9,19 @@
   #@ load("@ytt:yaml", "yaml")
   #@ load("@ytt:struct", "struct")
   #@ load("@ytt:assert", "assert")
-
+  #@ load("@ytt:overlay", "overlay")
+
+  #@ def addVolumes():
+  spec:
+    containers:
+    #@overlay/match by="name"
+    - name: workload
+      #@overlay/match missing_ok=True
+      volumeMounts: #@ data.values.params.volumes.volumeMounts
+    #@overlay/match missing_ok=True
+    volumes: #@ data.values.params.volumes.volumes
+  #@ end
+
   #@ def merge_labels(fixed_values):
   #@   labels = {}
   #@   if hasattr(data.values.workload.metadata, "labels"):
@@ -57,7 +69,7 @@
   spec:
     selector:
       matchLabels: #@ data.values.config.metadata.labels
-    template: #@ data.values.config
+    template: #@ overlay.apply(data.values.config,addVolumes())
   ---
   apiVersion: v1
   kind: Service
@@ -77,13 +89,26 @@
     #@ for p in merge_ports(declared_ports, data.values.config.spec.containers):
     - #@ p
     #@ end
+
+  ---
+  apiVersion: v1
+  kind: PersistentVolumeClaim
+  metadata:
+    name: #@ data.values.workload.metadata.name + "-volumes"
+    labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })
+  spec:
+    accessModes:
+    - ReadWriteOnce
+    resources:
+      requests:
+        storage: #@ data.values.params.pvSize
+    storageClassName: default
   #@ end
-
   ---
   apiVersion: v1
   kind: ConfigMap
   metadata:
-    name: #@ data.values.workload.metadata.name + "-server"
+    name: #@ data.values.workload.metadata.name + "-volumes"
     labels: #@ merge_labels({ "app.kubernetes.io/component": "config" })
   data:
     delivery.yml: #@ yaml.encode(delivery())

編集内容としては、#@ def addVolumes():#@ endで関数を定義し、PVのmount処理を定義している。
また、#@ overlay.apply(data.values.config,addVolumes())で関数呼び出しによってDeploymentの方に処理を追加している。
PVCに関しては #@ def delivery():の最後に新規に作成するようにしている。
名前が#@ data.values.workload.metadata.name + "-volumes"としてしまっているため、WorkloadでaddVolumesの中身を定義する際は、workload名-volumesにする必要がある点に注意。
また、 #@ def delivery():の中に入れないと反映されない点も注意。
これを新しいClusterConfigTemplateとしてapplyする。

 ytt -f server-template.yaml -f values.yaml  | kubectl apply -f -

作成したClusterConfigTemplateをtypeとして指定できるよう、typeを定義する。
typeの定義はTAPのインストール時に作成したtap-values.yamlに追記する形で行う。
なお、supported_workloadsを定義するとデフォルト値に上書きされる形で定義されるため、デフォルトのwebserverなどが消えてしまう。そのため、デフォルト値に追記する形で定義する。

--- tap-values.yaml	2023-08-30 01:15:30.272824163 +0000
+++ tap-values-pvc.yaml	2023-09-12 08:36:32.691925655 +0000
@@ -48,6 +48,10 @@
 profile: full

 supply_chain: testing_scanning
+ootb_supply_chain_testing_scanning:
+  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-with-volumes
+    cluster_config_template_name: volumes-template

 contour:
   envoy:

設定をTAPに反映させる。

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

また、PVCの作成権限がなく、このままWorkloadを作成すると、以下のようなエラーになってしまう。

    kapp: Error: Checking existence of resource persistentvolumeclaim/tanzu-java-web-app-hoge-volumes (v1) namespace: tap-demo:
      API server says: persistentvolumeclaims "tanzu-java-web-app-hoge-volumes" is forbidden:
        User "system:serviceaccount:tap-demo:default" cannot get resource "persistentvolumeclaims" in API group "" in the namespace "tap-demo" (reason: Forbidden)

そのため、TAPが使うアカウントの権限も強化しておく。

kubectl create rolebinding pvc-for-test --clusterrole edit --user="system:serviceaccount:tap-demo:default" -n tap-demo

検証

Workloadをデプロイする。
前回デプロイした、Tanzu Java Web Appを再利用し、config/workload.yamlのparamsを以下のように編集する。

  params:
    - name: pvSize
      value: 2G
    - name: volumes
      value:
        volumes:
        - name: data-mount
          persistentVolumeClaim:
            claimName: tanzu-java-web-app-pvc-volumes
        volumeMounts:
         - name: data-mount
           mountPath: /data

これにより、先程作成したaddVolumesやPVCに値を渡す。
Workloadをデプロイする。前回と違って今回作成したtypeを--type server-with-volumesとして指定する。

tanzu apps workload create tanzu-java-web-app-pvc -f ./config/workload.yaml --type server-with-volumes --label app.kubernetes.io/part-of=tanzu-java-web-app-pvc --label apps.tanzu.vmware.com/has-tests="true" --yes --namespace tap-demo

指定したPVCが作成されている事がわかる。

$ kubectl get pvc tanzu-java-web-app-hoge-volumes -n tap-demo
NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
tanzu-java-web-app-hoge-volumes   Bound    pvc-17467f81-4c2a-4714-9b1a-7a877b1d9ca7   1908Mi     RWO            default        2m35s

Podも無事起動した。

$ kubectl get pod tanzu-java-web-app-pvc-575fcdd7f8-r8qsl -n tap-demo
NAME                                      READY   STATUS    RESTARTS   AGE
tanzu-java-web-app-pvc-575fcdd7f8-r8qsl   1/1     Running   0          41s

describeで見ると、mountも問題なさそうだ。

    Mounts:
      /data from data-mount (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xhlsv (ro)

なお、今回typeserverにあっていないWorkloadを使ったため、Podの設定とかは適切なものになっていない。(Readiness probeでエラーが出たりする)
あくまでもお試しという点でご了承を。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?