Serverless Containers

Jun 15, 2018·
Toros Gökkurt
· 7 min read

Introduction

The evolution of cloud computing has been marked by the continuous pursuit of developer productivity and operational simplicity. Two paradigms have emerged as game-changers: serverless computing and containers. While each brought its own advantages, they also came with trade-offs. Serverless containers represent the convergence of these technologies, offering the best of both worlds.

The Evolution: From VMs to Serverless Containers

Traditional Virtual Machines

In the early cloud era, developers had to provision and manage virtual machines. This meant:

  • Managing operating systems and patches
  • Configuring networking and security
  • Scaling infrastructure manually
  • Paying for idle capacity

Containers

Containers revolutionized application packaging and deployment:

  • Consistency: “Works on my machine” became a thing of the past
  • Portability: Run anywhere containers are supported
  • Efficiency: Lightweight compared to VMs
  • Isolation: Secure application boundaries

However, containers still required management:

  • Orchestration platforms like Kubernetes
  • Node provisioning and maintenance
  • Cluster scaling and monitoring
  • Infrastructure cost optimization

Serverless Computing

Serverless (Functions-as-a-Service) simplified operations dramatically:

  • No infrastructure management: Just deploy code
  • Auto-scaling: From zero to millions of requests
  • Pay-per-use: Only pay for actual execution time
  • Event-driven: Natural integration with cloud events

But serverless had limitations:

  • Runtime constraints: Limited to supported languages
  • Execution time limits: Typically 5-15 minutes maximum
  • Cold starts: Initial request latency
  • Vendor lock-in: Platform-specific APIs

The Serverless Container Revolution

Serverless containers combine the flexibility of containers with the operational simplicity of serverless computing. You get:

  • Any runtime: Bring your own language, framework, or binary
  • Container portability: Use the same images across environments
  • Zero infrastructure management: No nodes to provision or patch
  • Per-second billing: Pay only for what you use
  • Automatic scaling: From zero to thousands of containers
  • Integration with orchestrators: Works with Kubernetes via Virtual Kubelet

The Virtual Kubelet Innovation

Virtual Kubelet is the bridge between Kubernetes and serverless container services. It appears as a node in your Kubernetes cluster but actually provisions containers in serverless services like ACI or AWS Fargate.

Benefits of Virtual Kubelet:

  • Unified control plane: Manage both traditional and serverless containers via kubectl
  • Burst capacity: Scale beyond your cluster limits instantly
  • Mixed workloads: Run persistent services on dedicated nodes, bursty workloads serverlessly
  • Cost optimization: Pay per second only for burst workloads

As Brendan Burns noted, “The future of Kubernetes is serverless” - and Virtual Kubelet is making that future a reality.

Cloud Provider Implementations

Azure Container Instances (ACI)

Azure Container Instances is Microsoft’s serverless container service, offering:

  • Fast startup: Containers start in seconds
  • Flexible sizing: CPU and memory configurations
  • Per-second billing: No minimum charges
  • Windows and Linux: Support for both OSes
  • GPU support: For AI/ML workloads
  • Integration: Works with Logic Apps, Functions, and Kubernetes

Creating a container in ACI:

az container create \
  --resource-group myResourceGroup \
  --name mycontainer \
  --image mcr.microsoft.com/azuredocs/aci-helloworld \
  --cpu 1 \
  --memory 1 \
  --ports 80 \
  --dns-name-label aci-demo \
  --environment-variables 'KEY=value'

Kubernetes integration with ACI Connector:

# Install ACI Connector
az aks install-connector \
  --resource-group myResourceGroup \
  --name myAKSCluster \
  --connector-name myaciconnector

# Deploy pods to ACI
kubectl run nginx --image=nginx --replicas=10 \
  --overrides='{"spec": {"nodeSelector": {"kubernetes.io/hostname": "virtual-kubelet-myaciconnector-linux"}}}'

AWS Fargate

AWS Fargate allows running containers without managing EC2 instances:

  • ECS and EKS integration: Works with both orchestrators
  • Task-level isolation: Each task gets dedicated kernel
  • VPC networking: Full networking capabilities
  • IAM integration: Fine-grained access control

Fargate with ECS:

{
  "family": "sample-fargate-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "my-container",
      "image": "nginx:latest",
      "portMappings": [
        {
          "containerPort": 80,
          "protocol": "tcp"
        }
      ]
    }
  ]
}

Google Cloud Run

Cloud Run combines serverless containers with automatic HTTPS and custom domains:

  • Request-based scaling: Scale to zero when idle
  • Custom domains: Built-in SSL certificates
  • WebSocket support: Real-time communications
  • gRPC support: Modern RPC protocols
# Deploy a container
gcloud run deploy my-service \
  --image gcr.io/my-project/my-image \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

Use Case Scenarios

1. Batch Processing & ETL

Perfect for data processing jobs that run periodically:

apiVersion: batch/v1
kind: Job
metadata:
  name: data-processor
spec:
  template:
    spec:
      nodeSelector:
        type: virtual-kubelet
      containers:
      - name: processor
        image: myregistry/data-processor:latest
        env:
        - name: INPUT_FILE
          value: "s3://mybucket/data.csv"
      restartPolicy: Never

2. CI/CD Build Agents

Ephemeral build environments that scale with your pipeline:

  • No idle agent costs
  • Fresh environment for each build
  • Parallel build execution
  • Custom build images

3. Event-Driven Workloads

Responding to cloud events with containerized logic:

Azure Logic Apps with ACI:

{
  "triggers": {
    "When_a_blob_is_added": {
      "type": "ApiConnection",
      "inputs": {
        "host": {
          "connection": {
            "name": "@parameters('$connections')['azureblob']['connectionId']"
          }
        }
      }
    }
  },
  "actions": {
    "Create_container_group": {
      "type": "ApiConnection",
      "inputs": {
        "host": {
          "connection": {
            "name": "@parameters('$connections')['aci']['connectionId']"
          }
        },
        "body": {
          "location": "eastus",
          "containers": [{
            "name": "processor",
            "image": "myregistry/processor:latest"
          }]
        }
      }
    }
  }
}

4. Static Site Generation Pipeline

Automated Hugo site building and deployment:

  1. Developer pushes markdown content to Git
  2. Webhook triggers Azure Logic App or AWS Lambda
  3. Serverless container starts with Hugo installed
  4. Site is compiled and deployed to CDN
  5. Container terminates automatically

Cost: Pay only for the ~10 seconds of build time!

5. API Microservices with Auto-scaling

Services that need to scale from zero to handle traffic spikes:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: api-service
spec:
  template:
    spec:
      containers:
      - image: gcr.io/myproject/api:latest
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: "1000m"
            memory: "512Mi"

Management Approaches

1. Programmatic Management with SDKs

Azure SDK for Node.js:

const { ContainerInstanceManagementClient } = require("@azure/arm-containerinstance");
const { DefaultAzureCredential } = require("@azure/identity");

const client = new ContainerInstanceManagementClient(
  new DefaultAzureCredential(),
  subscriptionId
);

await client.containerGroups.createOrUpdate(
  resourceGroup,
  containerGroupName,
  {
    location: "eastus",
    containers: [{
      name: "mycontainer",
      image: "nginx:latest",
      resources: {
        requests: { cpu: 1, memoryInGB: 1.5 }
      },
      ports: [{ port: 80 }]
    }],
    osType: "Linux",
    ipAddress: {
      type: "Public",
      ports: [{ port: 80, protocol: "TCP" }]
    }
  }
);

2. Infrastructure as Code

Terraform for multi-cloud serverless containers:

# AWS Fargate
resource "aws_ecs_task_definition" "app" {
  family                   = "app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"
  
  container_definitions = jsonencode([{
    name  = "app"
    image = "myapp:latest"
    portMappings = [{
      containerPort = 80
    }]
  }])
}

# Azure Container Instances
resource "azurerm_container_group" "app" {
  name                = "app"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  os_type             = "Linux"
  
  container {
    name   = "app"
    image  = "myapp:latest"
    cpu    = "0.5"
    memory = "1.5"
    
    ports {
      port     = 80
      protocol = "TCP"
    }
  }
}

3. Event-Driven Orchestration

AWS EventBridge + Lambda + Fargate:

import boto3

ecs = boto3.client('ecs')

def lambda_handler(event, context):
    # Triggered by EventBridge
    response = ecs.run_task(
        cluster='my-cluster',
        launchType='FARGATE',
        taskDefinition='data-processor',
        networkConfiguration={
            'awsvpcConfiguration': {
                'subnets': ['subnet-xxx'],
                'assignPublicIp': 'ENABLED'
            }
        },
        overrides={
            'containerOverrides': [{
                'name': 'processor',
                'environment': [{
                    'name': 'INPUT_FILE',
                    'value': event['detail']['object']['key']
                }]
            }]
        }
    )
    return response

Cost Optimization Strategies

1. Right-Sizing

Match container resources to actual needs:

resources:
  requests:
    cpu: "250m"      # 0.25 CPU cores
    memory: "512Mi"  # 512 MiB RAM
  limits:
    cpu: "500m"
    memory: "1Gi"

2. Spot/Preemptible Instances

For fault-tolerant workloads, use cheaper spot capacity:

# Azure Container Instances with spot
resource "azurerm_container_group" "batch" {
  priority = "Spot"
  
  container {
    name   = "batch-processor"
    image  = "processor:latest"
    cpu    = "2"
    memory = "4"
  }
}

3. Reserved Capacity

For predictable workloads, reserve capacity:

  • AWS Fargate Savings Plans
  • Azure Reserved Instances
  • GCP Committed Use Discounts

4. Scaling Policies

Implement intelligent scaling:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 0  # Scale to zero
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Best Practices

1. Optimize Container Images

  • Use multi-stage builds
  • Choose minimal base images (Alpine, distroless)
  • Leverage layer caching
  • Scan for vulnerabilities
# Multi-stage build
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app

FROM gcr.io/distroless/static
COPY --from=builder /app/app /
ENTRYPOINT ["/app"]

2. Implement Health Checks

containers:
- name: api
  livenessProbe:
    httpGet:
      path: /health
      port: 8080
    initialDelaySeconds: 10
    periodSeconds: 5
  readinessProbe:
    httpGet:
      path: /ready
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 3

3. Secrets Management

Never hardcode credentials:

containers:
- name: app
  env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: password

4. Logging and Monitoring

Structured logging and observability:

const logger = require('pino')();

logger.info({
  event: 'container_started',
  version: process.env.VERSION,
  region: process.env.REGION
});

The Future of Serverless Containers

The serverless container landscape continues to evolve:

  • WebAssembly integration: Even faster cold starts
  • Edge computing: Run containers at CDN edge locations
  • GPU support: Serverless AI/ML workloads
  • Confidential computing: Hardware-encrypted containers
  • Multi-cloud abstraction: Platform-agnostic deployments

Conclusion

Serverless containers represent a paradigm shift in cloud computing. They eliminate the operational overhead of managing infrastructure while preserving the flexibility and portability of containers. Whether you’re building batch processing pipelines, event-driven microservices, or auto-scaling APIs, serverless containers offer a compelling solution.

The combination of container portability, serverless simplicity, and pay-per-use economics makes this technology ideal for modern cloud-native applications. As platforms mature and tooling improves, serverless containers will become the default choice for many workloads.

Start experimenting with ACI, Fargate, or Cloud Run today, and experience the future of application deployment!

Resources