From 88936783479111e53aa74fd9ceed17e562d34190 Mon Sep 17 00:00:00 2001 From: Joel DeTeves Date: Sun, 2 Jan 2022 23:37:09 -0800 Subject: [PATCH] Add Kubernetes manifests for TRMM --- kubernetes/README.md | 20 +++++ kubernetes/certs.yaml | 15 ++++ kubernetes/deployment/tactical-backend.yaml | 66 +++++++++++++++ kubernetes/deployment/tactical-celery.yaml | 81 +++++++++++++++++++ kubernetes/deployment/tactical-frontend.yaml | 46 +++++++++++ kubernetes/deployment/tactical-init-pod.yaml | 64 +++++++++++++++ .../deployment/tactical-meshcentral.yaml | 81 +++++++++++++++++++ kubernetes/deployment/tactical-mongodb.yaml | 62 ++++++++++++++ kubernetes/deployment/tactical-redis.yaml | 56 +++++++++++++ kubernetes/deployment/tactical-secrets.yaml | 16 ++++ kubernetes/deployment/tactical-web.yaml | 76 +++++++++++++++++ .../deployment/tactical-websockets.yaml | 62 ++++++++++++++ kubernetes/namespace.yaml | 7 ++ kubernetes/network-policy.yaml | 62 ++++++++++++++ kubernetes/nlb.yaml | 20 +++++ kubernetes/pvc.yaml | 69 ++++++++++++++++ 16 files changed, 803 insertions(+) create mode 100644 kubernetes/README.md create mode 100644 kubernetes/certs.yaml create mode 100644 kubernetes/deployment/tactical-backend.yaml create mode 100644 kubernetes/deployment/tactical-celery.yaml create mode 100644 kubernetes/deployment/tactical-frontend.yaml create mode 100644 kubernetes/deployment/tactical-init-pod.yaml create mode 100644 kubernetes/deployment/tactical-meshcentral.yaml create mode 100644 kubernetes/deployment/tactical-mongodb.yaml create mode 100644 kubernetes/deployment/tactical-redis.yaml create mode 100644 kubernetes/deployment/tactical-secrets.yaml create mode 100644 kubernetes/deployment/tactical-web.yaml create mode 100644 kubernetes/deployment/tactical-websockets.yaml create mode 100644 kubernetes/namespace.yaml create mode 100644 kubernetes/network-policy.yaml create mode 100644 kubernetes/nlb.yaml create mode 100644 kubernetes/pvc.yaml diff --git a/kubernetes/README.md b/kubernetes/README.md new file mode 100644 index 0000000..d76af65 --- /dev/null +++ b/kubernetes/README.md @@ -0,0 +1,20 @@ +# TacticalRMM Kubernetes manifests +**Author:** [Joel DeTeves](https://github.com/joeldeteves) + +**Desription:** TacticalRMM Kubernetes manifests tested & working on Digital Ocean managed Kubernetes (DOKS). + +**Disclaimer:** _These manifests rely on an experimental developer images and as such are NOT SUPPORTED, at least until the required changes are merged into the main image. I have done my best to make them as secure as possible them however I am NOT responsible for anything that happens to you or your data as a result of using these files. Please do your due dilligence security-wise and open a Github issue if you wish to report a problem. USE AT YOUR OWN RISK. By using these files you agree that you are the sole entity responsible for any damages that may arise as a result._ + +# Pre-requisites +- A working Kubernetes cluster +- Kubernetes [NFS provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner) or another storage provisioner that supports ```ReadWriteMany``` + +# Deploying the files +1. ```kubectl apply -f namespace.yaml``` +2. ```kubectl apply -f .``` +3. ```kubectl apply -f deployment/ -R``` + +# Questions / Concerns +Please open an issue in Github or you can also check in the [Tactical RMM Discord Channel](https://discord.gg/upGTkWp). + +**Note: I am not affiliated with TRMM or AmidaWare; I am a community contributor. Please direct TRMM-related issues to the appropriate channels.** diff --git a/kubernetes/certs.yaml b/kubernetes/certs.yaml new file mode 100644 index 0000000..8178c01 --- /dev/null +++ b/kubernetes/certs.yaml @@ -0,0 +1,15 @@ +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: rmm-mydomain-com + namespace: tacticalrmm +spec: + secretName: rmm-mydomain-com-tls + commonName: rmm.mydomain.com + dnsNames: + - rmm.mydomain.com + - api.rmm.mydomain.com + - mesh.rmm.mydomain.com + issuerRef: + name: letsencrypt + kind: ClusterIssuer \ No newline at end of file diff --git a/kubernetes/deployment/tactical-backend.yaml b/kubernetes/deployment/tactical-backend.yaml new file mode 100644 index 0000000..89e4a2d --- /dev/null +++ b/kubernetes/deployment/tactical-backend.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-backend + name: tactical-backend +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-backend + strategy: + type: Recreate + template: + metadata: + labels: + network/api-db: "true" + network/redis: "true" + network/proxy: "true" + service: tactical-backend + spec: + containers: + - name: trmm-backend + image: tacticalrmm/tactical:0.10.5-dev + args: + - tactical-backend + resources: {} + env: + - name: CERT_PUB_PATH + value: /etc/ssl/certs/custom/tls.crt + - name: CERT_PRIV_PATH + value: /etc/ssl/certs/custom/tls.key + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + - mountPath: /etc/ssl/certs/custom + name: tactical-certs + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data + - name: tactical-certs + secret: + secretName: rmm-mydomain-com-tls +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-backend + name: tactical-backend +spec: + ports: + - name: "http" + port: 80 + targetPort: 80 + - name: "https" + port: 443 + targetPort: 443 + selector: + service: tactical-backend diff --git a/kubernetes/deployment/tactical-celery.yaml b/kubernetes/deployment/tactical-celery.yaml new file mode 100644 index 0000000..46a1c0b --- /dev/null +++ b/kubernetes/deployment/tactical-celery.yaml @@ -0,0 +1,81 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-celery + name: tactical-celery +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-celery + strategy: + type: Recreate + template: + metadata: + labels: + network/api-db: "true" + network/redis: "true" + service: tactical-celery + spec: + securityContext: + runAsUser: 1000 + fsGroup: 1000 + containers: + - name: trmm-celery + image: tacticalrmm/tactical:0.10.5-dev + args: + - tactical-celery + resources: {} + env: + - name: CERT_PUB_PATH + value: /etc/ssl/certs/custom/tls.crt + - name: CERT_PRIV_PATH + value: /etc/ssl/certs/custom/tls.key + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-celerybeat + name: tactical-celerybeat +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-celerybeat + strategy: + type: Recreate + template: + metadata: + labels: + network/api-db: "true" + network/redis: "true" + service: tactical-celerybeat + spec: + containers: + - name: trmm-celerybeat + image: tacticalrmm/tactical:latest + args: + - tactical-celerybeat + resources: {} + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data diff --git a/kubernetes/deployment/tactical-frontend.yaml b/kubernetes/deployment/tactical-frontend.yaml new file mode 100644 index 0000000..38ebb1e --- /dev/null +++ b/kubernetes/deployment/tactical-frontend.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-frontend + name: tactical-frontend +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-frontend + strategy: {} + template: + metadata: + labels: + service: tactical-frontend + spec: + containers: + - name: trmm-frontend + image: tacticalrmm/tactical-frontend:latest + resources: {} + env: + - name: API_HOST + value: api.rmm.mydomain.com + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-frontend + name: tactical-frontend +spec: + ports: + - name: "http" + port: 80 + targetPort: 80 + - name: "https" + port: 443 + targetPort: 443 + selector: + service: tactical-frontend diff --git a/kubernetes/deployment/tactical-init-pod.yaml b/kubernetes/deployment/tactical-init-pod.yaml new file mode 100644 index 0000000..2495ba5 --- /dev/null +++ b/kubernetes/deployment/tactical-init-pod.yaml @@ -0,0 +1,64 @@ +apiVersion: v1 +kind: Pod +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + network/api-db: "true" + network/proxy: "true" + service: tactical-init + name: tactical-init +spec: + containers: + - args: + - tactical-init + image: tacticalrmm/tactical:0.10.5-dev + name: trmm-init + env: + - name: API_HOST + value: api.rmm.mydomain.com + - name: APP_HOST + value: rmm.mydomain.com + - name: MESH_HOST + value: mesh.rmm.mydomain.com + - name: MESH_WS_URL + value: ws://tactical-meshcentral:443 + - name: MESH_USER + value: meshuser + - name: POSTGRES_HOST + value: 10.137.88.210 + - name: POSTGRES_PASS + valueFrom: + secretKeyRef: + name: tactical-secrets + key: postgres-password + - name: POSTGRES_USER + value: tacticalrmm + - name: POSTGRES_DB + value: tacticalrmm + - name: TRMM_PASS + valueFrom: + secretKeyRef: + name: tactical-secrets + key: trmm-password + - name: TRMM_USER + value: tacticalrmm + - name: CERT_PUB_PATH + value: /etc/ssl/certs/custom/tls.crt + - name: CERT_PRIV_PATH + value: /etc/ssl/certs/custom/tls.key + resources: {} + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + - mountPath: /etc/ssl/certs/custom + name: tactical-certs + restartPolicy: OnFailure + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data + - name: tactical-certs + secret: + secretName: rmm-mydomain-com-tls +status: {} diff --git a/kubernetes/deployment/tactical-meshcentral.yaml b/kubernetes/deployment/tactical-meshcentral.yaml new file mode 100644 index 0000000..8810cd2 --- /dev/null +++ b/kubernetes/deployment/tactical-meshcentral.yaml @@ -0,0 +1,81 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-meshcentral + name: tactical-meshcentral +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-meshcentral + strategy: + type: Recreate + template: + metadata: + labels: + network/mesh-db: "true" + network/proxy: "true" + service: tactical-meshcentral + spec: + containers: + - name: trmm-meshcentral + image: tacticalrmm/tactical-meshcentral:0.10.5-dev + resources: {} + env: + - name: MESH_HOST + value: mesh.rmm.mydomain.com + - name: MESH_USER + value: meshuser + - name: MESH_PASS + valueFrom: + secretKeyRef: + name: tactical-secrets + key: mesh-password + - name: MESH_PERSISTENT_CONFIG + value: "0" + - name: MONGODB_USER + value: mongodbuser + - name: MONGODB_PASSWORD + valueFrom: + secretKeyRef: + name: tactical-secrets + key: mongodb-password + - name: NGINX_HOST_IP # Point to NGINX service + value: tactical-nlb + - name: WS_MASK_OVERRIDE # Enable for Traefik compatibility + value: "0" + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + - mountPath: /home/node/app/meshcentral-data + name: mesh-data + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data + - name: mesh-data + persistentVolumeClaim: + claimName: mesh-data +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-meshcentral + name: tactical-meshcentral +spec: + ports: + - name: "http" + port: 80 + targetPort: 80 + - name: "https" + port: 443 + targetPort: 443 + selector: + service: tactical-meshcentral diff --git a/kubernetes/deployment/tactical-mongodb.yaml b/kubernetes/deployment/tactical-mongodb.yaml new file mode 100644 index 0000000..e3fcd05 --- /dev/null +++ b/kubernetes/deployment/tactical-mongodb.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-mongodb + name: tactical-mongodb +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-mongodb + strategy: + type: Recreate + template: + metadata: + labels: + network/mesh-db: "true" + service: tactical-mongodb + spec: + securityContext: + runAsUser: 2000 + fsGroup: 2000 + containers: + - name: trmm-mongodb + image: mongo:4.4 + resources: {} + env: + - name: MONGO_INITDB_DATABASE + value: meshcentral + - name: MONGO_INITDB_ROOT_USERNAME + value: mongodbuser + - name: MONGO_INITDB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: tactical-secrets + key: mongodb-password + volumeMounts: + - mountPath: /data/db + name: mongo-data + restartPolicy: Always + volumes: + - name: mongo-data + persistentVolumeClaim: + claimName: mongo-data +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-mongodb + name: tactical-mongodb +spec: + ports: + - name: "27017" + port: 27017 + targetPort: 27017 + selector: + service: tactical-mongodb diff --git a/kubernetes/deployment/tactical-redis.yaml b/kubernetes/deployment/tactical-redis.yaml new file mode 100644 index 0000000..485dc8f --- /dev/null +++ b/kubernetes/deployment/tactical-redis.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-redis + name: tactical-redis +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-redis + strategy: + type: Recreate + template: + metadata: + labels: + network/redis: "true" + service: tactical-redis + spec: + securityContext: + runAsUser: 1000 + fsGroup: 1000 + containers: + - name: trmm-redis + image: redis:6.0-alpine + args: + - redis-server + - --appendonly + - "yes" + resources: {} + volumeMounts: + - mountPath: /data + name: redis-data + restartPolicy: Always + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: redis-data +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-redis + name: tactical-redis +spec: + ports: + - name: "6379" + port: 6379 + targetPort: 6379 + selector: + service: tactical-redis diff --git a/kubernetes/deployment/tactical-secrets.yaml b/kubernetes/deployment/tactical-secrets.yaml new file mode 100644 index 0000000..ecaf279 --- /dev/null +++ b/kubernetes/deployment/tactical-secrets.yaml @@ -0,0 +1,16 @@ +# CAUTION: THIS FILE IS FOR DEMONSTRATION PURPOSES ONLY +# DO NOT UPLOAD SECRETS TO YOUR GIT REPOSITORY !!!! +# Secrets must be encoded using base64 (ensure there is no newline at the end of the file): +# echo -n 'mysupersecretpassword' | base64 -w0 + +apiVersion: v1 +kind: Secret +metadata: + name: tactical-secrets + namespace: tacticalrmm +data: + # Default password: changeme + trmm-password: Y2hhbmdlbWU= + mesh-password: Y2hhbmdlbWU= + mongodb-password: Y2hhbmdlbWU= + postgres-password: Y2hhbmdlbWU= diff --git a/kubernetes/deployment/tactical-web.yaml b/kubernetes/deployment/tactical-web.yaml new file mode 100644 index 0000000..0f6bb29 --- /dev/null +++ b/kubernetes/deployment/tactical-web.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-nlb + name: tactical-web +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-nlb + strategy: + type: Recreate + template: + metadata: + labels: + network/proxy: "true" + service: tactical-nlb + spec: + containers: + - name: nginx + image: tacticalrmm/tactical-nginx:0.10.5-dev + resources: {} + env: + - name: API_HOST + value: api.rmm.mydomain.com + - name: APP_HOST + value: rmm.mydomain.com + - name: MESH_HOST + value: mesh.rmm.mydomain.com + - name: NGINX_RESOLVER + value: kube-dns.kube-system.svc.cluster.local + - name: BACKEND_SERVICE + value: tactical-backend.tacticalrmm.svc.cluster.local + - name: FRONTEND_SERVICE + value: tactical-frontend.tacticalrmm.svc.cluster.local + - name: MESH_SERVICE + value: tactical-meshcentral.tacticalrmm.svc.cluster.local + - name: WEBSOCKETS_SERVICE + value: tactical-websockets.tacticalrmm.svc.cluster.local + - name: CERT_PUB_PATH + value: /etc/ssl/certs/custom/tls.crt + - name: CERT_PRIV_PATH + value: /etc/ssl/certs/custom/tls.key + ports: + - containerPort: 80 + - containerPort: 443 + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + - mountPath: /etc/ssl/certs/custom + name: tactical-certs + - name: trmm-nats + image: tacticalrmm/tactical-nats:latest + resources: {} + env: + - name: API_HOST + value: api.rmm.mydomain.com + ports: + - containerPort: 4222 + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + - mountPath: /etc/ssl/certs/custom + name: tactical-certs + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data + - name: tactical-certs + secret: + secretName: rmm-mydomain-com-tls +status: {} diff --git a/kubernetes/deployment/tactical-websockets.yaml b/kubernetes/deployment/tactical-websockets.yaml new file mode 100644 index 0000000..0f5b0d8 --- /dev/null +++ b/kubernetes/deployment/tactical-websockets.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-websockets + name: tactical-websockets +spec: + replicas: 1 + selector: + matchLabels: + service: tactical-websockets + strategy: + type: Recreate + template: + metadata: + labels: + network/api-db: "true" + network/redis: "true" + network/proxy: "true" + service: tactical-websockets + spec: + securityContext: + runAsUser: 1000 + fsGroup: 1000 + containers: + - name: trmm-websockets + image: tacticalrmm/tactical:latest + args: + - tactical-websockets + resources: {} + volumeMounts: + - mountPath: /opt/tactical + name: tactical-data + restartPolicy: Always + volumes: + - name: tactical-data + persistentVolumeClaim: + claimName: tactical-data +--- +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-websockets + name: tactical-websockets +spec: + ports: + - name: "http" + port: 80 + targetPort: 80 + - name: "https" + port: 443 + targetPort: 443 + - name: "8383" + port: 8383 + targetPort: 8383 + selector: + service: tactical-websockets diff --git a/kubernetes/namespace.yaml b/kubernetes/namespace.yaml new file mode 100644 index 0000000..a1dac98 --- /dev/null +++ b/kubernetes/namespace.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tacticalrmm + labels: + app: tacticalrmm diff --git a/kubernetes/network-policy.yaml b/kubernetes/network-policy.yaml new file mode 100644 index 0000000..93ea242 --- /dev/null +++ b/kubernetes/network-policy.yaml @@ -0,0 +1,62 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: proxy +spec: + ingress: + - {} + podSelector: + matchLabels: + network/proxy: "true" +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: api-db + namespace: tacticalrmm +spec: + ingress: + - from: + - podSelector: + matchLabels: + network/api-db: "true" + - podSelector: + matchLabels: + network/proxy: "true" + podSelector: + matchLabels: + network/api-db: "true" +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: mesh-db + namespace: tacticalrmm +spec: + ingress: + - from: + - podSelector: + matchLabels: + network/mesh-db: "true" + - podSelector: + matchLabels: + network/proxy: "true" + podSelector: + matchLabels: + network/mesh-db: "true" +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: redis + namespace: tacticalrmm +spec: + ingress: + - from: + - podSelector: + matchLabels: + network/redis: "true" + podSelector: + matchLabels: + network/redis: "true" diff --git a/kubernetes/nlb.yaml b/kubernetes/nlb.yaml new file mode 100644 index 0000000..59ac54b --- /dev/null +++ b/kubernetes/nlb.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-nlb + name: tactical-nlb +spec: + type: LoadBalancer + externalTrafficPolicy: Local + ports: + - name: "http" + port: 80 + - name: "https" + port: 443 + - name: "nats" + port: 4222 + selector: + service: tactical-nlb diff --git a/kubernetes/pvc.yaml b/kubernetes/pvc.yaml new file mode 100644 index 0000000..902c20e --- /dev/null +++ b/kubernetes/pvc.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: tactical-data + namespace: tacticalrmm + labels: + app: tacticalrmm + service: tactical-data + +spec: + accessModes: + - ReadWriteMany + storageClassName: nfs-client + resources: + requests: + storage: 200Mi +status: {} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mesh-data + namespace: tacticalrmm + labels: + app: tacticalrmm + service: mesh-data + +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +status: {} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mongo-data + namespace: tacticalrmm + labels: + app: tacticalrmm + service: mongo-data + +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +status: {} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: redis-data + namespace: tacticalrmm + labels: + app: tacticalrmm + service: redis-data + +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +status: {}