Skip to content

JamesFNGibbons/Symfony-Azure-AKS-Helm-Deployment-Template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Symfony App Helm Chart For Azure Kubernetes (AKS)

A production-ready Helm chart for deploying Symfony applications to Azure Kubernetes Service (AKS) with integrated Azure Key Vault support via Workload Identity.

Features

  • PHP-FPM Deployment: Symfony application container with configurable resources
  • Nginx Deployment: Web server with optimized configuration for Symfony
  • Redis Deployment: In-cluster Redis for caching and message queuing
  • Azure Key Vault Integration: Secure secret management using Azure Workload Identity
  • MySQL SSL Support: Pre-configured with Azure MySQL CA certificate
  • Ingress Configuration: Ready-to-use ingress with TLS support
  • Resource Management: Configurable CPU and memory requests/limits
  • Health Checks: Built-in liveness and readiness probes

Prerequisites

Azure Resources

  1. Azure Kubernetes Service (AKS) Cluster

    • AKS cluster with Workload Identity enabled
    • Kubernetes version 1.20 or higher
  2. Azure Key Vault

    • Key Vault with your application secrets stored
    • Access policies configured for the Managed Identity
  3. Azure Managed Identity

    • User-assigned managed identity created
    • Permissions granted to access Key Vault secrets
  4. Workload Identity Configuration

    • OIDC issuer enabled on your AKS cluster
    • Federated identity credential linking ServiceAccount to Managed Identity

Kubernetes Components

  • Secrets Store CSI Driver: Installed and configured in your cluster
  • Azure Key Vault Provider: Secrets Store CSI driver provider for Azure
  • Ingress Controller: Nginx ingress controller (or compatible)
  • Cert-Manager: For automatic TLS certificate management (optional but recommended)

Azure Setup

1. Enable Workload Identity on AKS

az aks update \
  --resource-group <your-resource-group> \
  --name <your-aks-cluster> \
  --enable-oidc-issuer \
  --enable-workload-identity

2. Create User-Assigned Managed Identity

az identity create \
  --resource-group <your-resource-group> \
  --name <your-managed-identity-name>

Note the clientId from the output - you'll need this for the Helm values.

3. Grant Key Vault Access

# Get the managed identity principal ID
PRINCIPAL_ID=$(az identity show \
  --resource-group <your-resource-group> \
  --name <your-managed-identity-name> \
  --query principalId -o tsv)

# Grant Key Vault access
az keyvault set-policy \
  --name <your-key-vault-name> \
  --object-id $PRINCIPAL_ID \
  --secret-permissions get list

4. Create Federated Identity Credential

# Get the OIDC issuer URL
AKS_OIDC_ISSUER=$(az aks show \
  --resource-group <your-resource-group> \
  --name <your-aks-cluster> \
  --query "oidcIssuerProfile.issuerUrl" -o tsv)

# Get the managed identity client ID
MANAGED_IDENTITY_CLIENT_ID=$(az identity show \
  --resource-group <your-resource-group> \
  --name <your-managed-identity-name> \
  --query clientId -o tsv)

# Create federated identity credential
az identity federated-credential create \
  --name <federated-credential-name> \
  --identity-name <your-managed-identity-name> \
  --resource-group <your-resource-group> \
  --issuer $AKS_OIDC_ISSUER \
  --subject system:serviceaccount:<namespace>:<release-name>-symfony-app-workload-identity \
  --audience api://AzureADTokenExchange

Important: Replace <namespace> and <release-name> with your actual namespace and Helm release name. The ServiceAccount name follows the pattern: <release-name>-symfony-app-workload-identity.

5. Install Secrets Store CSI Driver

# Add the Helm repository
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts

# Install the driver
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
  --namespace kube-system \
  --set syncSecret.enabled=true

6. Install Azure Key Vault Provider

# Install the provider
kubectl apply -f https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/deployment/provider-azure-installer.yaml

Installation

1. Configure Values

Copy and customize the values.yaml file:

cp values.yaml my-values.yaml
# Edit my-values.yaml with your configuration

2. Update Required Values

At minimum, update these values in my-values.yaml:

azure:
  tenantId: "<YOUR-AZURE-TENANT-ID>"
  managedIdentityClientId: "<YOUR-MANAGED-IDENTITY-CLIENT-ID>"

azureKeyVault:
  keyVaultName: "<YOUR-KEY-VAULT-NAME>"
  secrets:
    # Map your Key Vault secrets to environment variables
    - keyVaultName: "APP-SECRET"
      kubernetesKey: "APP_SECRET"
    # ... add your other secrets

deployments:
  php:
    image: "your-registry/your-app/php:latest"
    env:
      DB_HOST: "your-database-host.mysql.database.azure.com"
      # ... other environment variables

  nginx:
    image: "your-registry/your-app/nginx:latest"
    ingress:
      host: "your-domain.com"
      annotations:
        cert-manager.io/cluster-issuer: "letsencrypt-production"

3. Install the Chart

helm install <release-name> . \
  --namespace <namespace> \
  --create-namespace \
  -f my-values.yaml

4. Verify Installation

# Check pods
kubectl get pods -n <namespace>

# Check secrets (should be synced from Key Vault)
kubectl get secrets -n <namespace>

# Check ingress
kubectl get ingress -n <namespace>

Configuration

Azure Key Vault Integration

The chart automatically creates a SecretProviderClass that syncs secrets from Azure Key Vault into Kubernetes secrets. Secrets are mounted as environment variables in the PHP pods.

To use a secret from Key Vault, set the environment variable value to "_FROM_AZURE_KEYVAULT":

env:
  APP_SECRET: "_FROM_AZURE_KEYVAULT"
  DB_PASSWORD: "_FROM_AZURE_KEYVAULT"

Then map the Key Vault secret name to the Kubernetes key:

azureKeyVault:
  secrets:
    - keyVaultName: "APP-SECRET"  # Name in Azure Key Vault
      kubernetesKey: "APP_SECRET"  # Environment variable name

Refreshing Secrets

When secrets change in Azure Key Vault, update the refreshToken value to force a refresh:

azureKeyVault:
  refreshToken: "20241210190000"  # Use current timestamp

MySQL SSL Configuration

The chart includes Azure MySQL CA certificate support. The certificate is automatically mounted to /etc/ssl/certs/mysql-ca-cert.pem in PHP pods.

Important: Update the mysql.caCert value in values.yaml with your Azure MySQL CA certificate. You can download it from the Azure portal or use the default DigiCert Global Root CA certificate.

Redis Configuration

Redis is automatically configured for Symfony Messenger. The MESSENGER_TRANSPORT_DSN is set to use Redis:

MESSENGER_TRANSPORT_DSN: "doctrine://default?auto_setup=0"

The Redis host is automatically set to the service name created by the chart.

Image Pull Secrets

If using a private container registry:

imagePullSecrets:
  - name: ghcr-secret

Create the secret first:

kubectl create secret docker-registry ghcr-secret \
  --docker-server=ghcr.io \
  --docker-username=<username> \
  --docker-password=<token> \
  --namespace <namespace>

Resource Management

Each deployment (PHP, Nginx, and Redis) has configurable CPU and memory resource requests and limits. This allows you to control resource allocation and ensure proper scheduling and performance.

Understanding Requests and Limits

  • Requests: The minimum resources guaranteed to the pod. Kubernetes uses this for scheduling decisions.
  • Limits: The maximum resources a pod can consume. If exceeded, the pod may be throttled (CPU) or terminated (memory).

Default Resource Configuration

The chart provides sensible defaults for each component:

PHP Deployment

deployments:
  php:
    resources:
      requests:
        cpu: "100m"      # 0.1 CPU cores
        memory: "256Mi"  # 256 MiB
      limits:
        cpu: "500m"      # 0.5 CPU cores
        memory: "512Mi"  # 512 MiB

Recommended for: Most Symfony applications with moderate traffic.

Nginx Deployment

deployments:
  nginx:
    resources:
      requests:
        cpu: "100m"      # 0.1 CPU cores
        memory: "128Mi"  # 128 MiB
      limits:
        cpu: "250m"      # 0.25 CPU cores
        memory: "256Mi"  # 256 MiB

Recommended for: Standard web server workloads. Nginx is typically CPU-bound rather than memory-bound.

Redis Deployment

deployments:
  redis:
    resources:
      requests:
        cpu: "50m"       # 0.05 CPU cores
        memory: "128Mi"  # 128 MiB
      limits:
        cpu: "200m"      # 0.2 CPU cores
        memory: "256Mi"  # 256 MiB

Recommended for: Small to medium caching and message queue workloads.

Customizing Resources

Adjust resources based on your application's needs:

deployments:
  php:
    resources:
      requests:
        cpu: "200m"      # Increase for CPU-intensive operations
        memory: "512Mi"  # Increase for memory-intensive applications
      limits:
        cpu: "1000m"     # Allow bursts up to 1 CPU core
        memory: "1Gi"    # Allow up to 1 GiB memory

  nginx:
    resources:
      requests:
        cpu: "200m"      # Increase for high-traffic sites
        memory: "256Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"

  redis:
    resources:
      requests:
        cpu: "100m"
        memory: "256Mi"  # Increase if caching large datasets
      limits:
        cpu: "500m"
        memory: "512Mi"

Resource Sizing Guidelines

Small Applications (< 100 req/s)

  • PHP: 100m CPU / 256Mi memory
  • Nginx: 100m CPU / 128Mi memory
  • Redis: 50m CPU / 128Mi memory

Medium Applications (100-1000 req/s)

  • PHP: 200m-500m CPU / 512Mi-1Gi memory
  • Nginx: 200m-500m CPU / 256Mi-512Mi memory
  • Redis: 100m-200m CPU / 256Mi-512Mi memory

Large Applications (> 1000 req/s)

  • PHP: 500m-2000m CPU / 1Gi-4Gi memory
  • Nginx: 500m-1000m CPU / 512Mi-1Gi memory
  • Redis: 200m-500m CPU / 512Mi-2Gi memory

Monitoring Resource Usage

Monitor actual resource consumption to optimize your settings:

# View current resource usage
kubectl top pods -n <namespace>

# View detailed resource metrics
kubectl describe pod <pod-name> -n <namespace>

Best Practices

  1. Start Conservative: Begin with default values and scale up based on monitoring data
  2. Set Limits: Always set limits to prevent resource exhaustion
  3. Match Requests to Limits: For predictable workloads, set requests close to limits
  4. Monitor OOMKills: Check for Out-of-Memory kills indicating insufficient memory limits
  5. CPU Throttling: Monitor for CPU throttling which may indicate CPU limits are too low
  6. Vertical Pod Autoscaler: Consider using VPA for automatic resource adjustment (requires additional setup)

Disabling Resource Limits

To disable resource limits (not recommended for production):

deployments:
  php:
    resources: {}  # Empty object removes resource constraints

Warning: Removing resource limits can lead to resource exhaustion and affect other workloads in your cluster.

Environment Variables

Symfony Core

  • APP_ENV: Environment (prod, dev, test)
  • APP_SECRET: Application secret (recommended: use Key Vault)
  • APP_DEBUG: Debug mode (0 or 1)

Database

  • DB_HOST: MySQL hostname
  • DB_PORT: MySQL port (default: 3306)
  • DB_NAME: Database name
  • DB_USER: Database username
  • DB_PASSWORD: Database password
  • MYSQL_CA_CERT: Path to CA certificate (default: /etc/ssl/certs/mysql-ca-cert.pem)
  • MYSQL_SSL_VERIFY_SERVER_CERT: Enable SSL verification (default: true)

Mailer

  • MAILER_DSN: Mailer DSN (supports Azure Communication Services, SMTP, etc.)
  • MAILER_FROM: Default sender email address
  • MAILER_FROM_NAME: Default sender name

Redis

  • REDIS_HOST: Redis hostname (auto-configured if set to "REPLACED_BY_TEMPLATE")
  • REDIS_PORT: Redis port (default: 6379)

AWS S3 (Optional)

  • AWS_ACCESS_KEY_ID: AWS access key
  • AWS_SECRET_ACCESS_KEY: AWS secret key
  • AWS_S3_REGION: S3 region
  • AWS_S3_BUCKET: S3 bucket name
  • AWS_S3_ENDPOINT: Custom endpoint (leave empty for AWS)
  • AWS_S3_URL: S3 bucket URL

Upgrading

helm upgrade <release-name> . \
  --namespace <namespace> \
  -f my-values.yaml

Uninstalling

helm uninstall <release-name> --namespace <namespace>

Troubleshooting

Secrets Not Syncing from Key Vault

  1. Verify the ServiceAccount has the correct annotation:

    kubectl get serviceaccount <release-name>-symfony-app-workload-identity -n <namespace> -o yaml

    Should show: azure.workload.identity/client-id: <your-client-id>

  2. Check the SecretProviderClass:

    kubectl get secretproviderclass -n <namespace>
    kubectl describe secretproviderclass <release-name>-symfony-app-keyvault-secrets -n <namespace>
  3. Check pod events:

    kubectl describe pod <php-pod-name> -n <namespace>
  4. Verify federated identity credential matches the ServiceAccount:

    # The subject should match:
    # system:serviceaccount:<namespace>:<release-name>-symfony-app-workload-identity

Pods Failing to Start

  1. Check pod logs:

    kubectl logs <pod-name> -n <namespace>
  2. Verify image pull secrets if using private registry:

    kubectl get secrets -n <namespace>
  3. Check resource limits:

    kubectl describe pod <pod-name> -n <namespace>

Database Connection Issues

  1. Verify MySQL CA certificate is correctly configured in values.yaml
  2. Check database credentials are correctly set or synced from Key Vault
  3. Verify network connectivity from pods to database:
    kubectl exec -it <php-pod-name> -n <namespace> -- ping <db-host>

Ingress Not Working

  1. Verify ingress controller is installed:

    kubectl get pods -n ingress-nginx
  2. Check ingress configuration:

    kubectl get ingress -n <namespace>
    kubectl describe ingress <release-name>-symfony-app-ingress -n <namespace>
  3. Verify DNS is pointing to your ingress controller's external IP

License

Copyright (C) 2024 James Gibbons jgibbons@121digital.co.uk

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.

About

A goto helm deployment template for Azure Kubernetes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages