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.