Deploying Applications on Cloud Infrastructure
# Deploying Applications on Cloud Infrastructure ## Introduction Deploying applications on cloud infrastructure requires a different approach than traditional data center deployments. Cloud environ
# Deploying Applications on Cloud Infrastructure
Introduction
Deploying applications on cloud infrastructure requires a different approach than traditional data center deployments. Cloud environments enable automated provisioning, elastic scaling, and rapid iteration through CI/CD pipelines, containerization, and infrastructure as code.
This tutorial covers application deployment strategies on cloud infrastructure, including CI/CD pipelines, containerization with Docker and Kubernetes, serverless deployment, blue-green and canary deployments, infrastructure as code, and deployment best practices.
By the end of this guide, you will be able to deploy and manage applications on cloud infrastructure using modern DevOps practices.
Technical Background
Deployment Strategies
| Strategy | Description | Risk | Downtime | Rollback Speed | |----------|-------------|------|----------|----------------| | Recreate | Stop old, start new | High | Yes | Slow | | Rolling | Replace instances gradually | Medium | No | Medium | | Blue-Green | Switch traffic between environments | Low | No | Fast | | Canary | Route small % traffic to new version | Low | No | Fast | | A/B Testing | Route traffic based on user segments | Low | No | Fast |
Cloud Deployment Models
| Model | Description | Best For | |-------|-------------|----------| | Lift-and-Shift | Move VMs as-is to cloud | Legacy migration | | Containerize | Package apps in containers | Microservices migration | | Refactor | Modify app for cloud-native | Cloud optimization | | Rebuild | Rewrite app using cloud services | New development | | Replace | Replace with SaaS | Non-differentiating workloads |
CI/CD Pipeline Stages
Code Commit → Build → Test → Security Scan → Package → Deploy to Staging → Integration Test → Deploy to Production → MonitorPrerequisites
- Cloud provider account
- Version control repository (Git)
- Container registry (Docker Hub, ECR, ACR, GCR)
- CI/CD platform (GitHub Actions, GitLab CI, Jenkins, Azure DevOps)
- Understanding of containers and orchestration
Architecture
CI/CD Pipeline Architecture
graph LR
subgraph Develop [Development]
Code[Code Commit]
Build[Build]
Test[Unit Tests]
end
subgraph Package [Package]
Image[Container Image Build]
Scan[Security Scan]
Push[Push to Registry]
end
subgraph Staging [Staging Environment]
DeployStaging[Deploy to Staging]
TestStaging[Integration Tests]
end
subgraph Production [Production Environment]
Approval[Manual Approval]
DeployProd[Deploy to Production]
Verify[Smoke Tests]
Monitor[Monitoring]
end
Code --> Build --> Test --> Image --> Scan --> Push --> DeployStaging --> TestStaging --> Approval --> DeployProd --> Verify --> MonitorBlue-Green Deployment Architecture
Traffic Router
│
├──▶ Blue Environment (v1.0) ──▶ Active
│
└──▶ Green Environment (v2.0) ──▶ Standby
│
◀── Switch traffic after validationCanary Deployment Architecture
Traffic Router
│
├── 95% ──▶ Stable Version (v1.0)
│
└── 5% ──▶ Canary Version (v2.0)
│
◀── Monitor metrics, then increase %Step-by-Step Implementation
Step 1: Containerize Application
**Purpose**: Package application in Docker container for cloud deployment.
**Dockerfile Example**:
# Multi-stage build for Node.js application
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]**Commands**:
# Build Docker image
docker build -t my-company/web-app:1.0.0 .
docker build -t my-company/web-app:latest .
# Test container locally
docker run -d -p 3000:3000 --name web-app-test my-company/web-app:1.0.0
# Push to container registry
docker tag my-company/web-app:1.0.0 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:1.0.0
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:1.0.0
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:latest
# Verify image in registry
aws ecr describe-images --repository-name web-app**Expected output**: Container image built, tested, and pushed to registry.
**Verification**:
- Container runs locally and responds on port 3000
- Image pushed to registry successfully
- Image scan results available (if configured)
Step 2: Create CI/CD Pipeline
**Purpose**: Automate build, test, and deployment processes.
**GitHub Actions Example** (`.github/workflows/deploy.yml`):
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: 123456789012.dkr.ecr.us-east-1.amazonaws.com
IMAGE_NAME: web-app
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Run unit tests
run: |
docker run ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} npm test
- name: Run security scan
run: |
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy-staging:
needs: build-and-test
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
aws ecs update-service --cluster staging --service web-app --force-new-deployment
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production (blue-green)
run: |
./scripts/blue-green-deploy.sh ${{ github.sha }}**Expected output**: Automated pipeline builds, tests, and deploys application on code changes.
**Verification**:
- Pipeline runs on code push
- Build succeeds
- Tests pass
- Application deployed to staging and production
Step 3: Deploy to Kubernetes
**Purpose**: Deploy containerized application on Kubernetes cluster.
**Kubernetes Manifest Example** (`deployment.yaml`):
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
version: v1
spec:
containers:
- name: web-app
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
selector:
app: web-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70**Commands**:
# Apply manifests
kubectl apply -f deployment.yaml
# Check deployment status
kubectl rollout status deployment/web-app
# Check pods
kubectl get pods -l app=web-app
# Check service
kubectl get service web-app
# Scale deployment
kubectl scale deployment web-app --replicas=5
# Update image
kubectl set image deployment/web-app web-app=123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app:1.1.0
# Rollback if needed
kubectl rollout undo deployment/web-app**Expected output**: Application deployed on Kubernetes with load balancing and auto-scaling.
**Verification**:
- Pods running and ready
- Service endpoint accessible
- Auto-scaling based on CPU utilization
- Health probes passing
Step 4: Implement Blue-Green Deployment
**Purpose**: Deploy new version with zero downtime and instant rollback.
**Blue-Green Deployment Script** (`scripts/blue-green-deploy.sh`):
#!/bin/bash
set -e
IMAGE_TAG=$1
CURRENT_COLOR=$(kubectl get service web-app -o jsonpath='{.spec.selector.color}')
NEW_COLOR="green"
if [ "$CURRENT_COLOR" = "green" ]; then
NEW_COLOR="blue"
fi
echo "Deploying $IMAGE_TAG to $NEW_COLOR environment"
# Deploy new version to inactive environment
kubectl set image deployment/web-app-$NEW_COLOR web-app=$IMAGE_TAG
kubectl rollout status deployment/web-app-$NEW_COLOR --timeout=300s
# Run smoke tests against new environment
kubectl run smoke-test --image=curlimages/curl --rm -i --restart=Never -- \
curl -f http://web-app-$NEW_COLOR:3000/health || exit 1
# Switch traffic to new environment
kubectl patch service web-app -p "{\"spec\":{\"selector\":{\"color\":\"$NEW_COLOR\"}}}"
echo "Traffic switched to $NEW_COLOR environment with version $IMAGE_TAG"
# Keep old environment for quick rollback (don't delete immediately)
echo "Previous environment ($CURRENT_COLOR) retained for rollback"**Expected output**: New version deployed to inactive environment, traffic switched after validation.
**Verification**:
- New version running in inactive environment
- Smoke tests pass
- Traffic switched to new version
- Application accessible through service
Step 5: Implement Canary Deployment
**Purpose**: Gradually roll out new version to subset of users.
**Istio Canary Example**:
# DestinationRule for two versions
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: web-app
spec:
host: web-app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
# VirtualService with 90/10 traffic split
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: web-app
spec:
hosts:
- web-app
http:
- route:
- destination:
host: web-app
subset: v1
weight: 90
- destination:
host: web-app
subset: v2
weight: 10**Commands**:
# Apply Istio configuration
kubectl apply -f destination-rule.yaml
kubectl apply -f virtual-service.yaml
# Deploy v2 alongside v1
kubectl apply -f deployment-v2.yaml
# Monitor metrics
kubectl get destinationrule web-app -o yaml
kubectl get virtualservice web-app -o yaml
# Gradually increase v2 traffic
# Edit VirtualService to change weights (80/20, 60/40, etc.)
# Full cutover to v2
# Edit VirtualService to send 100% to v2
# Remove v1
kubectl delete deployment web-app-v1**Expected output**: New version gradually rolled out with ability to rollback based on metrics.
**Verification**:
- Both versions running simultaneously
- Traffic split according to weights
- Metrics (error rate, latency) acceptable for canary version
- Full cutover completed successfully
Step 6: Implement Infrastructure as Code
**Purpose**: Define infrastructure and deployment configuration in code.
**Terraform Example** (`main.tf`):
provider "aws" {
region = "us-east-1"
}
resource "aws_ecr_repository" "web_app" {
name = "web-app"
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_ecs_cluster" "production" {
name = "production"
setting {
name = "containerInsights"
value = "enabled"
}
}
resource "aws_ecs_service" "web_app" {
name = "web-app"
cluster = aws_ecs_cluster.production.id
task_definition = aws_ecs_task_definition.web_app.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = [aws_subnet.private.*.id]
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
load_balancers {
target_group_arn = aws_lb_target_group.web_app.arn
container_name = "web-app"
container_port = 3000
}
}
resource "aws_lb" "web" {
name = "web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = [aws_subnet.public.*.id]
enable_deletion_protection = true
}
resource "aws_lb_target_group" "web_app" {
name = "web-app-tg"
port = 3000
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip"
health_check {
path = "/health"
port = 3000
}
}**Commands**:
# Initialize Terraform
terraform init
# Review planned changes
terraform plan
# Apply changes
terraform apply -auto-approve
# Destroy infrastructure (when no longer needed)
terraform destroy -auto-approve**Expected output**: Infrastructure defined and provisioned via code.
**Verification**:
- Infrastructure resources created as defined
- Changes reviewable before application
- Infrastructure reproducible across environments
Configuration Examples
GitHub Actions Pipeline (Complete)
name: Build, Test, Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: docker-compose -f docker-compose.test.yml up --abort-on-container-exit
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build and push image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy-staging:
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: kubectl set image deployment/web-app web-app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Blue-green deploy
run: ./scripts/blue-green-deploy.sh ${{ github.sha }}Helm Chart Example
web-app/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
hpa.yaml
ingress.yaml
serviceaccount.yaml**values.yaml**:
replicaCount: 3
image:
repository: 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-app
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: LoadBalancer
port: 80
targetPort: 3000
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256MiVerification
Deployment Verification
# Check deployment status
kubectl rollout status deployment/web-app
# Check pods
kubectl get pods -l app=web-app
# Check service
kubectl get service web-app
# Check ingress
kubectl get ingress web-app
# Test application
curl -f http://<load-balancer-dns>/health
# Check ECS service
aws ecs describe-services --cluster production --services web-app
# Check Lambda
aws lambda get-function --function-name process-imageExpected Results
- Application deployed and running
- Health checks passing
- Load balancer distributing traffic
- Auto-scaling configured
- Pipeline automated (code push → deploy)
Troubleshooting
| Symptom | Possible Cause | Diagnostic Command | Corrective Action | Verification | |---------|---------------|-------------------|-------------------|--------------| | Pods CrashLoopBackOff | Application error, missing config | `kubectl logs <pod>` | Fix application code or config | Pods running | | Service unavailable | No healthy endpoints | `kubectl get endpoints` | Check pod readiness, service selector | Service has endpoints | | Deployment fails | Image pull error | `kubectl describe pod` | Fix image reference, registry auth | Deployment succeeds | | Pipeline fails | Test or build error | Pipeline logs | Fix code, update tests | Pipeline green | | Canary metrics degraded | New version has issues | Monitoring dashboard | Rollback canary (set weight to 0) | Stable metrics | | Auto-scaling not working | Metrics not available | `kubectl get hpa` | Install metrics server | HPA scaling |
Best Practices
1. **Use CI/CD pipelines**: Automate build, test, and deployment. 2. **Containerize applications**: Consistent environments across development and production. 3. **Use infrastructure as code**: Define infrastructure in Terraform/CloudFormation. 4. **Implement health checks**: Liveness and readiness probes for Kubernetes. 5. **Use blue-green or canary deployments**: Reduce deployment risk. 6. **Scan container images**: Security vulnerabilities in dependencies. 7. **Set resource requests and limits**: Prevent resource starvation. 8. **Implement rollback procedures**: Quick recovery from failed deployments. 9. **Monitor deployments**: Track deployment frequency, lead time, failure rate. 10. **Separate environments**: Development, staging, production with appropriate promotion gates.
Conclusion
Deploying applications on cloud infrastructure requires modern DevOps practices including containerization, CI/CD pipelines, infrastructure as code, and advanced deployment strategies. By containerizing applications, automating pipelines, using Kubernetes for orchestration, and implementing blue-green/canary deployments, organizations can deploy frequently with low risk. Infrastructure as code ensures consistency and reproducibility across environments.
References
- Kubernetes Documentation (https://kubernetes.io/docs/)
- Docker Documentation (https://docs.docker.com/)
- AWS ECS Documentation (https://docs.aws.amazon.com/ecs/)
- AWS EKS Documentation (https://docs.aws.amazon.com/eks/)
- GitHub Actions Documentation (https://docs.github.com/actions)
- Terraform Documentation (https://www.terraform.io/docs/)
- Istio Documentation (https://istio.io/latest/docs/)