SUSE Cloud Application Platform: Complete Deployment Guide

Tyler Maginnis | February 13, 2024

SUSE CAPCloud FoundryKubernetescontainerizationPaaS

Need Professional SUSE Enterprise Support?

Get expert assistance with your suse enterprise support implementation and management. Tyler on Tech Louisville provides priority support for Louisville businesses.

Same-day service available for Louisville area

SUSE Cloud Application Platform: Complete Deployment Guide

SUSE Cloud Application Platform (CAP) provides an enterprise-ready Cloud Foundry distribution on Kubernetes. This guide covers deployment, configuration, and management of cloud-native applications using SUSE CAP.

Introduction to SUSE CAP

SUSE Cloud Application Platform offers: - Cloud Foundry: Enterprise PaaS platform - Kubernetes Integration: Container orchestration - Multi-Cloud Support: Deploy anywhere - Developer Experience: cf push simplicity - Enterprise Features: HA, security, compliance - Stratos Console: Web-based management UI

Prerequisites and Planning

Infrastructure Requirements

# Minimum requirements for production
# Control Plane Nodes: 3 nodes
# - CPU: 4 cores per node
# - RAM: 16GB per node
# - Storage: 100GB SSD per node

# Worker Nodes: 3+ nodes
# - CPU: 8 cores per node
# - RAM: 32GB per node
# - Storage: 200GB SSD per node

# Load Balancer: Required for HA
# Storage: Persistent volume support
# Network: Overlay network (Flannel/Calico)

Supported Platforms

# Supported Kubernetes platforms
# - SUSE CaaS Platform
# - Amazon EKS
# - Azure AKS
# - Google GKE
# - VMware PKS/TKGI
# - Bare metal Kubernetes

# Kubernetes version requirements
kubectl version --short
# Client Version: v1.21+
# Server Version: v1.21+

# Required Kubernetes features
# - RBAC enabled
# - PersistentVolume support
# - LoadBalancer or NodePort services
# - Ingress controller

Kubernetes Preparation

Installing SUSE CaaS Platform

# Deploy SUSE CaaS Platform (if not using managed K8s)
# Download CaaS Platform
wget https://download.suse.com/caasplatform/

# Deploy using skuba
sudo zypper install skuba

# Initialize cluster
skuba cluster init --control-plane 192.168.1.10 my-cluster
cd my-cluster

# Bootstrap first master
skuba node bootstrap --user sles --sudo --target 192.168.1.10 master-1

# Add additional masters
skuba node join --role master --user sles --sudo --target 192.168.1.11 master-2
skuba node join --role master --user sles --sudo --target 192.168.1.12 master-3

# Add worker nodes
skuba node join --role worker --user sles --sudo --target 192.168.1.20 worker-1
skuba node join --role worker --user sles --sudo --target 192.168.1.21 worker-2
skuba node join --role worker --user sles --sudo --target 192.168.1.22 worker-3

# Verify cluster
kubectl get nodes
kubectl get pods --all-namespaces

Storage Configuration

# Configure storage class
cat > storageclass.yaml << 'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs  # or your provisioner
parameters:
  type: gp3
  iopsPerGB: "10"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
EOF

kubectl apply -f storageclass.yaml

# Verify storage
kubectl get storageclass

Network Configuration

# Install MetalLB for bare metal load balancing
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml

# Configure MetalLB
cat > metallb-config.yaml << 'EOF'
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.100-192.168.1.110
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: production
  namespace: metallb-system
EOF

kubectl apply -f metallb-config.yaml

Installing SUSE CAP

Helm Installation

# Install Helm 3
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Verify Helm
helm version

# Add SUSE repository
helm repo add suse https://kubernetes-charts.suse.com/
helm repo update

CAP Deployment Configuration

# Create values configuration
cat > scf-config-values.yaml << 'EOF'
env:
  # Domain configuration
  DOMAIN: cap.example.com
  UAA_HOST: uaa.cap.example.com

  # Use external database (recommended for production)
  ENABLE_EXTERNAL_DATABASE: true
  EXTERNAL_DATABASE_HOST: postgres.example.com
  EXTERNAL_DATABASE_PORT: 5432
  EXTERNAL_DATABASE_USER: capuser
  EXTERNAL_DATABASE_PASSWORD: SecurePassword123!

  # Use external blobstore (recommended for production)
  ENABLE_EXTERNAL_BLOBSTORE: true
  EXTERNAL_BLOBSTORE_ENDPOINT: https://s3.amazonaws.com
  EXTERNAL_BLOBSTORE_REGION: us-east-1
  EXTERNAL_BLOBSTORE_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
  EXTERNAL_BLOBSTORE_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

sizing:
  # Scaling configuration
  diego_cell:
    count: 3
  diego_brain:
    count: 2
  router:
    count: 2

services:
  loadbalanced: true

kube:
  # Storage configuration
  storage_class:
    persistent: "fast-ssd"
    shared: "nfs"

  # Registry configuration
  registry:
    hostname: "registry.suse.com"
    username: ""
    password: ""

  # PSP configuration
  psp:
    default: "suse.cap.psp"
EOF

Deploy UAA (User Account and Authentication)

# Create namespace
kubectl create namespace uaa

# Generate certificates
# Create certificate configuration
cat > cert-config.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: uaa-cert
  namespace: uaa
type: Opaque
data:
  cert: |
    $(base64 -w 0 < /path/to/cert.pem)
  key: |
    $(base64 -w 0 < /path/to/key.pem)
EOF

kubectl apply -f cert-config.yaml

# Install UAA
helm install uaa suse/uaa \
  --namespace uaa \
  --values scf-config-values.yaml \
  --wait

# Verify UAA deployment
kubectl get pods -n uaa
kubectl get services -n uaa

Deploy Cloud Foundry

# Create namespace
kubectl create namespace scf

# Install Cloud Foundry
helm install scf suse/cf \
  --namespace scf \
  --values scf-config-values.yaml \
  --wait

# Monitor deployment
watch kubectl get pods -n scf

# Verify all components
kubectl get pods -n scf | grep -v Running | grep -v Completed

Deploy Stratos Console

# Create namespace
kubectl create namespace stratos

# Stratos configuration
cat > stratos-values.yaml << 'EOF'
console:
  service:
    type: LoadBalancer

  # SSL configuration
  sslEnabled: true
  sslCert: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
  sslKey: |
    -----BEGIN PRIVATE KEY-----
    ...
    -----END PRIVATE KEY-----

env:
  DOMAIN: stratos.cap.example.com
EOF

# Install Stratos
helm install stratos suse/console \
  --namespace stratos \
  --values stratos-values.yaml \
  --wait

# Get Stratos URL
kubectl get svc -n stratos console-ui-ext

Post-Installation Configuration

DNS Configuration

# Configure DNS entries
# Get load balancer IPs
kubectl get svc -n scf router-public

# Required DNS entries:
# *.cap.example.com -> Router LB IP
# uaa.cap.example.com -> UAA LB IP
# stratos.cap.example.com -> Stratos LB IP

# Verify DNS
nslookup api.cap.example.com
nslookup uaa.cap.example.com
nslookup stratos.cap.example.com

CF CLI Configuration

# Install CF CLI
wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add -
echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list
sudo apt update
sudo apt install cf-cli

# Configure CF CLI
cf api https://api.cap.example.com --skip-ssl-validation

# Login as admin
ADMIN_PASSWORD=$(kubectl get secret -n scf scf.var-cf-admin-password -o jsonpath='{.data.password}' | base64 -d)
cf login -u admin -p "$ADMIN_PASSWORD"

Organization and Space Setup

# Create organization
cf create-org development
cf create-org production

# Create spaces
cf target -o development
cf create-space dev
cf create-space test
cf create-space staging

cf target -o production
cf create-space prod
cf create-space disaster-recovery

# Create users
cf create-user developer DevPass123!
cf create-user deployer DeployPass123!

# Assign roles
cf set-org-role developer development OrgManager
cf set-space-role developer development dev SpaceDeveloper
cf set-space-role deployer production prod SpaceDeveloper

Application Deployment

Deploying Applications

# Sample Node.js application
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    message: 'Hello from SUSE CAP!',
    instance: process.env.CF_INSTANCE_INDEX,
    version: '1.0.0'
  });
});

app.get('/health', (req, res) => {
  res.json({ status: 'healthy' });
});

app.listen(port, () => {
  console.log(`App listening on port ${port}`);
});
EOF

# Package.json
cat > package.json << 'EOF'
{
  "name": "cap-sample-app",
  "version": "1.0.0",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.0"
  }
}
EOF

# Cloud Foundry manifest
cat > manifest.yml << 'EOF'
---
applications:
- name: sample-app
  memory: 256M
  instances: 3
  routes:
  - route: sample-app.cap.example.com
  env:
    NODE_ENV: production
  services:
  - sample-database
  - sample-cache
EOF

# Deploy application
cf push

# Check application status
cf apps
cf app sample-app

Blue-Green Deployment

# Blue-green deployment script
cat > blue-green-deploy.sh << 'EOF'
#!/bin/bash
APP_NAME=$1
DOMAIN="cap.example.com"

# Push new version as green
cf push "$APP_NAME-green" -f manifest.yml -n "$APP_NAME-green"

# Test green deployment
curl -f "https://$APP_NAME-green.$DOMAIN/health" || exit 1

# Switch routes
cf map-route "$APP_NAME-green" "$DOMAIN" -n "$APP_NAME"
cf unmap-route "$APP_NAME" "$DOMAIN" -n "$APP_NAME"

# Clean up old version
cf delete "$APP_NAME" -f
cf rename "$APP_NAME-green" "$APP_NAME"

echo "Deployment complete!"
EOF

chmod +x blue-green-deploy.sh
./blue-green-deploy.sh sample-app

Service Management

Database Services

# Deploy PostgreSQL service broker
cat > postgres-broker-values.yaml << 'EOF'
postgresqlDatabase: servicebroker
postgresqlUsername: broker
postgresqlPassword: BrokerPass123!

broker:
  service:
    name: postgresql
    id: postgresql-service-broker
    description: PostgreSQL database service
  plans:
  - name: small
    id: postgresql-small
    description: Small PostgreSQL instance
    free: true
    metadata:
      cpu: 1
      memory: 1Gi
      storage: 10Gi
  - name: medium
    id: postgresql-medium
    description: Medium PostgreSQL instance
    free: false
    metadata:
      cpu: 2
      memory: 4Gi
      storage: 50Gi
EOF

# Install PostgreSQL broker
helm install postgresql-broker suse/postgresql-broker \
  --namespace scf \
  --values postgres-broker-values.yaml

# Register service broker
cf create-service-broker postgresql broker BrokerPass123! \
  http://postgresql-broker.scf.svc.cluster.local:8080

# Enable service access
cf enable-service-access postgresql

# Create service instance
cf create-service postgresql small sample-database

Redis Services

# Deploy Redis service
cat > redis-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: scf
spec:
  selector:
    app: redis
  ports:
  - port: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: scf
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6-alpine
        ports:
        - containerPort: 6379
        resources:
          limits:
            memory: "256Mi"
            cpu: "500m"
EOF

kubectl apply -f redis-service.yaml

# Create user-provided service
cf create-user-provided-service sample-cache \
  -p '{"host":"redis-service.scf.svc.cluster.local","port":"6379"}'

Monitoring and Logging

Prometheus Integration

# Deploy Prometheus Operator
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# Install Prometheus
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.adminPassword=AdminPass123!

# Configure Cloud Foundry metrics
cat > cf-metrics-config.yaml << 'EOF'
apiVersion: v1
kind: ServiceMonitor
metadata:
  name: cf-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: cf-metrics
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
EOF

kubectl apply -f cf-metrics-config.yaml

Log Aggregation

# Deploy Fluentd for log collection
cat > fluentd-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
  namespace: scf
data:
  fluent.conf: |
    <source>
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      tag kubernetes.*
      <parse>
        @type json
        time_format %Y-%m-%dT%H:%M:%S.%NZ
      </parse>
    </source>

    <filter kubernetes.**>
      @type kubernetes_metadata
    </filter>

    <match **>
      @type elasticsearch
      host elasticsearch.logging.svc.cluster.local
      port 9200
      logstash_format true
      logstash_prefix cf-logs
    </match>
EOF

# Deploy Fluentd DaemonSet
cat > fluentd-daemonset.yaml << 'EOF'
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: scf
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
        volumeMounts:
        - name: config
          mountPath: /fluentd/etc
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: fluentd-config
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
EOF

kubectl apply -f fluentd-config.yaml
kubectl apply -f fluentd-daemonset.yaml

Security Configuration

Network Policies

# Restrict inter-namespace communication
cat > network-policy.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: cf-namespace-isolation
  namespace: scf
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: scf
    - namespaceSelector:
        matchLabels:
          name: uaa
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: scf
    - namespaceSelector:
        matchLabels:
          name: uaa
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53
EOF

kubectl apply -f network-policy.yaml

Application Security Groups

# Create ASG for database access
cat > db-asg.json << 'EOF'
[
  {
    "protocol": "tcp",
    "destination": "10.0.0.0/24",
    "ports": "5432",
    "description": "Allow PostgreSQL access"
  },
  {
    "protocol": "tcp",
    "destination": "10.0.0.0/24",
    "ports": "3306",
    "description": "Allow MySQL access"
  }
]
EOF

# Create and bind ASG
cf create-security-group db-access db-asg.json
cf bind-running-security-group db-access
cf bind-staging-security-group db-access

Certificate Management

# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml

# Configure Let's Encrypt issuer
cat > letsencrypt-issuer.yaml << 'EOF'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
EOF

kubectl apply -f letsencrypt-issuer.yaml

# Configure certificate for CAP
cat > cap-certificate.yaml << 'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: cap-tls
  namespace: scf
spec:
  secretName: cap-tls-secret
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: '*.cap.example.com'
  dnsNames:
  - '*.cap.example.com'
  - 'cap.example.com'
EOF

kubectl apply -f cap-certificate.yaml

Backup and Recovery

Backup Strategy

# Backup script for CAP
cat > backup-cap.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/cap-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"

# Backup Cloud Controller database
kubectl exec -n scf deployment/cf-database -c cf-database -- \
  pg_dump -U cloud_controller cloud_controller > \
  "$BACKUP_DIR/cloud_controller.sql"

# Backup UAA database
kubectl exec -n uaa deployment/uaa-database -c uaa-database -- \
  pg_dump -U uaa uaa > "$BACKUP_DIR/uaa.sql"

# Backup persistent volumes
kubectl get pv -o json > "$BACKUP_DIR/persistent-volumes.json"
kubectl get pvc --all-namespaces -o json > "$BACKUP_DIR/persistent-volume-claims.json"

# Backup configurations
kubectl get configmap -n scf -o yaml > "$BACKUP_DIR/scf-configmaps.yaml"
kubectl get secret -n scf -o yaml > "$BACKUP_DIR/scf-secrets.yaml"

# Backup Helm releases
helm list --all-namespaces -o json > "$BACKUP_DIR/helm-releases.json"

echo "Backup completed to $BACKUP_DIR"
EOF

chmod +x backup-cap.sh

# Schedule backup
echo "0 2 * * * /usr/local/bin/backup-cap.sh" | crontab -

Disaster Recovery

# DR procedure
cat > disaster-recovery.sh << 'EOF'
#!/bin/bash
# CAP Disaster Recovery

BACKUP_DIR=$1

if [ -z "$BACKUP_DIR" ]; then
    echo "Usage: $0 <backup-directory>"
    exit 1
fi

# Restore databases
kubectl exec -i -n scf deployment/cf-database -c cf-database -- \
  psql -U cloud_controller cloud_controller < \
  "$BACKUP_DIR/cloud_controller.sql"

kubectl exec -i -n uaa deployment/uaa-database -c uaa-database -- \
  psql -U uaa uaa < "$BACKUP_DIR/uaa.sql"

# Restore configurations
kubectl apply -f "$BACKUP_DIR/scf-configmaps.yaml"
kubectl apply -f "$BACKUP_DIR/scf-secrets.yaml"

# Restart components
kubectl rollout restart deployment -n scf
kubectl rollout restart deployment -n uaa

echo "Disaster recovery completed"
EOF

chmod +x disaster-recovery.sh

Performance Tuning

Diego Cell Optimization

# Tune Diego cells for performance
cat > diego-tuning-values.yaml << 'EOF'
sizing:
  diego_cell:
    count: 5
    disk_ephemeral: 100
    memory: 32768
    cpu: 8000

env:
  # Garden settings
  GARDEN_DESTROY_CONTAINERS_ON_START: false
  GARDEN_GRAPH_CLEANUP_THRESHOLD_IN_MB: 10240

  # Diego settings
  DIEGO_EXECUTOR_MEMORY_CAPACITY_MB: 30720
  DIEGO_EXECUTOR_DISK_CAPACITY_MB: 98304
  DIEGO_EXECUTOR_MAX_CONTAINERS: 250

  # Rep settings
  REP_EVACUATION_TIMEOUT: 600
  REP_SKIP_CERT_VERIFY: true
EOF

# Update deployment
helm upgrade scf suse/cf \
  --namespace scf \
  --values scf-config-values.yaml \
  --values diego-tuning-values.yaml \
  --wait

Router Performance

# Optimize GoRouter
cat > router-tuning-values.yaml << 'EOF'
sizing:
  router:
    count: 3
    cpu: 2000
    memory: 4096

env:
  # Router settings
  ROUTER_BACKENDS_MAX_CONNS: 500
  ROUTER_KEEP_ALIVE: 30
  ROUTER_LOAD_BALANCER_HEALTHY_THRESHOLD: 5
  ROUTER_MAX_IDLE_CONNECTIONS: 900
  ROUTER_STATUS_PASS: RouterPass123!
  ROUTER_STATUS_USER: router_status

  # Timeout settings
  ROUTER_CLIENT_CERT_VALIDATION: none
  ROUTER_DRAIN_WAIT: 120
  ROUTER_REQUESTED_ROUTE_REGISTRATION_INTERVAL_IN_SECONDS: 20
EOF

# Apply router optimization
helm upgrade scf suse/cf \
  --namespace scf \
  --values scf-config-values.yaml \
  --values router-tuning-values.yaml \
  --wait

Troubleshooting

Common Issues

# Check component health
kubectl get pods -n scf | grep -v Running | grep -v Completed

# View logs for failed pods
kubectl logs -n scf <pod-name> -c <container-name>

# Check events
kubectl get events -n scf --sort-by='.lastTimestamp'

# Verify DNS resolution
kubectl run -it --rm debug --image=busybox --restart=Never -- \
  nslookup api.cap.example.com

# Test internal connectivity
kubectl run -it --rm debug --image=nicolaka/netshoot --restart=Never -- \
  curl -v http://cloud-controller-ng.scf.svc.cluster.local:9022/v2/info

# Check certificate issues
kubectl describe certificate -n scf
kubectl describe certificaterequest -n scf

# Debug application issues
cf logs <app-name> --recent
cf events <app-name>
cf ssh <app-name>

Performance Diagnostics

# Check resource usage
kubectl top nodes
kubectl top pods -n scf

# Analyze Diego cell capacity
cf curl /v2/info | jq '.usage'

# Check router statistics
cf curl /v2/router_groups

# Monitor system metrics
kubectl port-forward -n monitoring svc/prometheus-grafana 3000:80
# Access Grafana at http://localhost:3000

Best Practices

Production Recommendations

# Production checklist
cat > production-checklist.md << 'EOF'
# SUSE CAP Production Checklist

## Infrastructure
- [ ] Minimum 3 control plane nodes
- [ ] Minimum 3 worker nodes
- [ ] Dedicated Diego cells for production apps
- [ ] External database configured
- [ ] External blobstore configured
- [ ] Load balancer with health checks

## Security
- [ ] TLS certificates configured
- [ ] Network policies applied
- [ ] RBAC properly configured
- [ ] Regular security updates
- [ ] Application security groups defined
- [ ] Secrets encrypted at rest

## Monitoring
- [ ] Prometheus metrics collection
- [ ] Log aggregation configured
- [ ] Alerting rules defined
- [ ] Dashboard for key metrics
- [ ] Application performance monitoring

## Backup
- [ ] Automated database backups
- [ ] Blobstore backup strategy
- [ ] Configuration backups
- [ ] Tested recovery procedures
- [ ] Off-site backup storage

## Operations
- [ ] Documented runbooks
- [ ] Change management process
- [ ] Capacity planning metrics
- [ ] Regular health checks
- [ ] Update procedures tested
EOF

Conclusion

SUSE Cloud Application Platform provides a robust enterprise PaaS solution on Kubernetes. Proper planning, security configuration, and operational procedures ensure successful deployment and management of cloud-native applications. Regular monitoring, updates, and capacity planning maintain platform health and performance.