Skip to content

Project 8: Azure Container Solutions ​

Overview ​

Build a complete container solution using Azure Container Instances (ACI), Azure Container Registry (ACR), and understand when to use AKS. This project covers the container deployment scenarios commonly tested on AZ-104.

Difficulty: Intermediate
Duration: 3-4 hours
Cost: ~$20-30/month (ACR Basic, ACI consumption)
Exam Weight: Part of Compute domain (20-25%)

Architecture Diagram ​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              DEVELOPMENT WORKFLOW                                β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Developer   │───▢│  Build       │───▢│  Push to     │───▢│  Deploy to   β”‚  β”‚
β”‚  β”‚  Dockerfile  β”‚    β”‚  Image       β”‚    β”‚  ACR         β”‚    β”‚  ACI         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     AZURE CONTAINER REGISTRY (ACR)                               β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    acrcontainerlab.azurecr.io                              β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β”‚  β”‚  Repository:     β”‚  β”‚  Repository:     β”‚  β”‚  Repository:             β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  webapp          β”‚  β”‚  api             β”‚  β”‚  worker                  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚                  β”‚  β”‚                  β”‚  β”‚                          β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  Tags:           β”‚  β”‚  Tags:           β”‚  β”‚  Tags:                   β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  - v1.0          β”‚  β”‚  - v1.0          β”‚  β”‚  - v1.0                  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  - v1.1          β”‚  β”‚  - v2.0          β”‚  β”‚  - latest                β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  - latest        β”‚  β”‚  - latest        β”‚  β”‚                          β”‚ β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  Features:                                                                 β”‚  β”‚
β”‚  β”‚  βœ… Geo-replication (Premium)    βœ… Content Trust                         β”‚  β”‚
β”‚  β”‚  βœ… Private Link                 βœ… Vulnerability Scanning                 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚
                                      β”‚ Pull Images
                                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    AZURE CONTAINER INSTANCES (ACI)                               β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    CONTAINER GROUP: cg-webapp                              β”‚  β”‚
β”‚  β”‚                    (Multi-container deployment)                            β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚
β”‚  β”‚  β”‚                        SHARED RESOURCES                             β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - IP Address: 20.x.x.x (Public)                                   β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - DNS Label: webapp-demo-12345.eastus.azurecontainer.io           β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - CPU: 2 cores (shared)                                           β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - Memory: 4 GB (shared)                                           β”‚   β”‚  β”‚
β”‚  β”‚  β”‚  - Volume: Azure Files (shared storage)                            β”‚   β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚  β”‚
β”‚  β”‚  β”‚  Container: webapp   β”‚  β”‚  Container: sidecar  β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚                      β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Image: webapp:v1.0  β”‚  β”‚  Image: nginx:latest β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Port: 80            β”‚  β”‚  Port: 8080          β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  CPU: 1.5 cores      β”‚  β”‚  CPU: 0.5 cores      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Memory: 3 GB        β”‚  β”‚  Memory: 1 GB        β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚                      β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Environment:        β”‚  β”‚  Purpose:            β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  - DB_HOST=...       β”‚  β”‚  - Reverse proxy     β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  - API_KEY=...       β”‚  β”‚  - SSL termination   β”‚                       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    CONTAINER GROUP: cg-worker                              β”‚  β”‚
β”‚  β”‚                    (Background processing)                                 β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                 β”‚  β”‚
β”‚  β”‚  β”‚  Container: worker   β”‚  Restart Policy: Always                        β”‚  β”‚
β”‚  β”‚  β”‚                      β”‚  OS Type: Linux                                 β”‚  β”‚
β”‚  β”‚  β”‚  Image: worker:v1.0  β”‚                                                 β”‚  β”‚
β”‚  β”‚  β”‚  CPU: 1 core         β”‚  Volume Mount:                                  β”‚  β”‚
β”‚  β”‚  β”‚  Memory: 2 GB        β”‚  - Azure Files: /data                          β”‚  β”‚
β”‚  β”‚  β”‚  No public IP        β”‚                                                 β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         NETWORK INTEGRATION                                      β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    VNet: vnet-containers (10.0.0.0/16)                     β”‚  β”‚
β”‚  β”‚                                                                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚  β”‚
β”‚  β”‚  β”‚  Subnet: snet-aci    β”‚  β”‚  Subnet: snet-data   β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  (10.0.1.0/24)       β”‚  β”‚  (10.0.2.0/24)       β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚                      β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Delegated to:       β”‚  β”‚  Contains:           β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  Microsoft.Container β”‚  β”‚  - Azure SQL         β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  InstanceService     β”‚  β”‚  - Storage Account   β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚                      β”‚  β”‚  - Redis Cache       β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  ACI containers can  β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  access data tier    β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β”‚  privately           β”‚  β”‚                      β”‚                       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ACI vs AKS Decision Matrix ​

ScenarioUse ACIUse AKS
Simple single-container appsβœ…βŒ
Short-lived batch jobsβœ…βŒ
Dev/test environmentsβœ…βŒ
Event-driven workloadsβœ…βŒ
Complex microservicesβŒβœ…
Need auto-scalingβŒβœ…
Service mesh requiredβŒβœ…
Persistent workloadsβŒβœ…

What You'll Learn ​

  • Create and manage Azure Container Registry
  • Build and push Docker images to ACR
  • Deploy containers using ACI
  • Configure multi-container groups
  • Set up VNet integration for ACI
  • Mount Azure Files volumes
  • Configure restart policies and probes

Phase 1: Create Azure Container Registry ​

Step 1.1: Create Resource Group ​

bash
# Set variables
LOCATION="eastus"
RG_NAME="rg-containers-lab"
ACR_NAME="acrcontainerlab$(date +%s | tail -c 6)"

# Create resource group
az group create \
  --name $RG_NAME \
  --location $LOCATION \
  --tags Project=Containers Environment=Lab

echo "Resource group created"

Step 1.2: Create Container Registry ​

bash
# Create ACR with Basic SKU
az acr create \
  --resource-group $RG_NAME \
  --name $ACR_NAME \
  --sku Basic \
  --admin-enabled true

# Get ACR login server
ACR_LOGIN_SERVER=$(az acr show \
  --name $ACR_NAME \
  --query loginServer -o tsv)

echo "ACR created: $ACR_LOGIN_SERVER"

Step 1.3: Get ACR Credentials ​

bash
# Get admin credentials
ACR_USERNAME=$(az acr credential show \
  --name $ACR_NAME \
  --query username -o tsv)

ACR_PASSWORD=$(az acr credential show \
  --name $ACR_NAME \
  --query "passwords[0].value" -o tsv)

echo "ACR Username: $ACR_USERNAME"
echo "ACR Password: $ACR_PASSWORD"

Phase 2: Build and Push Container Images ​

Step 2.1: Create Sample Web Application ​

bash
# Create project directory
mkdir -p container-app
cd container-app

# Create a simple Node.js application
cat > app.js << 'EOF'
const http = require('http');
const os = require('os');

const port = process.env.PORT || 80;
const message = process.env.MESSAGE || 'Hello from Azure Container Instance!';

const server = http.createServer((req, res) => {
  const response = {
    message: message,
    hostname: os.hostname(),
    platform: os.platform(),
    uptime: process.uptime(),
    timestamp: new Date().toISOString()
  };
  
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(response, null, 2));
});

server.listen(port, () => {
  console.log(`Server running on port ${port}`);
});
EOF

# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine

WORKDIR /app

COPY app.js .

EXPOSE 80

ENV PORT=80
ENV MESSAGE="Hello from Azure Container Instance!"

CMD ["node", "app.js"]
EOF

echo "Application files created"

Step 2.2: Build Image Using ACR Tasks ​

bash
# Build image directly in ACR (no local Docker needed!)
az acr build \
  --registry $ACR_NAME \
  --image webapp:v1.0 \
  --file Dockerfile \
  .

# Tag as latest
az acr build \
  --registry $ACR_NAME \
  --image webapp:latest \
  --file Dockerfile \
  .

echo "Image built and pushed to ACR"

Step 2.3: Verify Image in Registry ​

bash
# List repositories
az acr repository list \
  --name $ACR_NAME \
  --output table

# List tags for webapp repository
az acr repository show-tags \
  --name $ACR_NAME \
  --repository webapp \
  --output table

# Show image details
az acr repository show-manifests \
  --name $ACR_NAME \
  --repository webapp \
  --output table

Phase 3: Deploy Azure Container Instances ​

Step 3.1: Deploy Simple Container ​

bash
# Deploy container from ACR
az container create \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --image $ACR_LOGIN_SERVER/webapp:v1.0 \
  --registry-login-server $ACR_LOGIN_SERVER \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --cpu 1 \
  --memory 1.5 \
  --ports 80 \
  --dns-name-label "webapp-$(date +%s | tail -c 8)" \
  --environment-variables MESSAGE="Hello from simple ACI deployment!" \
  --restart-policy Always \
  --location $LOCATION

# Get container details
az container show \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --query "{FQDN:ipAddress.fqdn, IP:ipAddress.ip, State:instanceView.state}" \
  --output table

Step 3.2: Test the Container ​

bash
# Get FQDN
FQDN=$(az container show \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --query ipAddress.fqdn -o tsv)

# Test the application
curl http://$FQDN

# View container logs
az container logs \
  --resource-group $RG_NAME \
  --name aci-webapp-simple

Phase 4: Multi-Container Groups ​

Step 4.1: Create Multi-Container YAML Definition ​

bash
cat > container-group.yaml << EOF
apiVersion: '2021-09-01'
location: $LOCATION
name: cg-multicontainer
properties:
  containers:
  - name: webapp
    properties:
      image: $ACR_LOGIN_SERVER/webapp:v1.0
      ports:
      - port: 80
        protocol: TCP
      resources:
        requests:
          cpu: 1.0
          memoryInGB: 1.5
      environmentVariables:
      - name: MESSAGE
        value: "Hello from multi-container group!"
      - name: PORT
        value: "80"
  - name: sidecar
    properties:
      image: nginx:alpine
      ports:
      - port: 8080
        protocol: TCP
      resources:
        requests:
          cpu: 0.5
          memoryInGB: 0.5
      command:
      - "/bin/sh"
      - "-c"
      - |
        echo 'server { listen 8080; location / { proxy_pass http://localhost:80; } }' > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'
  osType: Linux
  ipAddress:
    type: Public
    ports:
    - port: 80
      protocol: TCP
    - port: 8080
      protocol: TCP
    dnsNameLabel: multicontainer-$(date +%s | tail -c 8)
  restartPolicy: Always
  imageRegistryCredentials:
  - server: $ACR_LOGIN_SERVER
    username: $ACR_USERNAME
    password: $ACR_PASSWORD
tags:
  environment: lab
  project: containers
type: Microsoft.ContainerInstance/containerGroups
EOF

echo "Container group YAML created"

Step 4.2: Deploy Multi-Container Group ​

bash
# Deploy using YAML
az container create \
  --resource-group $RG_NAME \
  --file container-group.yaml

# Verify deployment
az container show \
  --resource-group $RG_NAME \
  --name cg-multicontainer \
  --query "{State:instanceView.state, IP:ipAddress.ip, FQDN:ipAddress.fqdn}" \
  --output table

# View logs from specific container
az container logs \
  --resource-group $RG_NAME \
  --name cg-multicontainer \
  --container-name webapp

az container logs \
  --resource-group $RG_NAME \
  --name cg-multicontainer \
  --container-name sidecar

Phase 5: Mount Azure Files Volume ​

Step 5.1: Create Storage Account and File Share ​

bash
# Create storage account
STORAGE_NAME="staci$(date +%s | tail -c 8)"
az storage account create \
  --resource-group $RG_NAME \
  --name $STORAGE_NAME \
  --sku Standard_LRS \
  --location $LOCATION

# Get storage key
STORAGE_KEY=$(az storage account keys list \
  --resource-group $RG_NAME \
  --account-name $STORAGE_NAME \
  --query "[0].value" -o tsv)

# Create file share
az storage share create \
  --name "container-data" \
  --account-name $STORAGE_NAME \
  --account-key $STORAGE_KEY

# Upload a test file
echo "Configuration data from Azure Files" > config.txt
az storage file upload \
  --share-name "container-data" \
  --source "config.txt" \
  --account-name $STORAGE_NAME \
  --account-key $STORAGE_KEY

echo "Storage account and file share created"

Step 5.2: Deploy Container with Volume Mount ​

bash
# Deploy container with Azure Files mount
az container create \
  --resource-group $RG_NAME \
  --name aci-with-volume \
  --image $ACR_LOGIN_SERVER/webapp:v1.0 \
  --registry-login-server $ACR_LOGIN_SERVER \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --cpu 1 \
  --memory 1.5 \
  --ports 80 \
  --dns-name-label "aci-volume-$(date +%s | tail -c 8)" \
  --azure-file-volume-account-name $STORAGE_NAME \
  --azure-file-volume-account-key $STORAGE_KEY \
  --azure-file-volume-share-name "container-data" \
  --azure-file-volume-mount-path "/mnt/data" \
  --location $LOCATION

echo "Container with volume mount deployed"

# Verify volume mount by executing command in container
az container exec \
  --resource-group $RG_NAME \
  --name aci-with-volume \
  --exec-command "ls -la /mnt/data"

Phase 6: VNet Integration ​

Step 6.1: Create Virtual Network ​

bash
# Create VNet
az network vnet create \
  --resource-group $RG_NAME \
  --name vnet-containers \
  --address-prefix 10.0.0.0/16 \
  --subnet-name snet-aci \
  --subnet-prefix 10.0.1.0/24 \
  --location $LOCATION

# Delegate subnet to ACI
az network vnet subnet update \
  --resource-group $RG_NAME \
  --vnet-name vnet-containers \
  --name snet-aci \
  --delegations Microsoft.ContainerInstance/containerGroups

echo "VNet created with ACI delegation"

Step 6.2: Deploy Container in VNet ​

bash
# Get subnet ID
SUBNET_ID=$(az network vnet subnet show \
  --resource-group $RG_NAME \
  --vnet-name vnet-containers \
  --name snet-aci \
  --query id -o tsv)

# Deploy container in VNet (no public IP)
az container create \
  --resource-group $RG_NAME \
  --name aci-vnet-private \
  --image $ACR_LOGIN_SERVER/webapp:v1.0 \
  --registry-login-server $ACR_LOGIN_SERVER \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --cpu 1 \
  --memory 1.5 \
  --ports 80 \
  --subnet $SUBNET_ID \
  --location $LOCATION

# Get private IP
PRIVATE_IP=$(az container show \
  --resource-group $RG_NAME \
  --name aci-vnet-private \
  --query ipAddress.ip -o tsv)

echo "Container deployed in VNet with private IP: $PRIVATE_IP"

Phase 7: Container Management Operations ​

Step 7.1: Start, Stop, and Restart Containers ​

bash
# Stop container
az container stop \
  --resource-group $RG_NAME \
  --name aci-webapp-simple

# Check state
az container show \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --query instanceView.state -o tsv

# Start container
az container start \
  --resource-group $RG_NAME \
  --name aci-webapp-simple

# Restart container
az container restart \
  --resource-group $RG_NAME \
  --name aci-webapp-simple

Step 7.2: View Container Metrics ​

bash
# Get container events
az container show \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --query instanceView.events \
  --output table

# Stream logs (real-time)
az container attach \
  --resource-group $RG_NAME \
  --name aci-webapp-simple

# Execute command in running container
az container exec \
  --resource-group $RG_NAME \
  --name aci-webapp-simple \
  --exec-command "/bin/sh"

Phase 8: ACR Advanced Features ​

Step 8.1: Configure ACR Tasks for Auto-Build ​

bash
# Create a quick task that builds on git push (requires GitHub token)
# This is a simulation - in real scenario, connect to your repo

# Manual trigger build with custom tag
az acr build \
  --registry $ACR_NAME \
  --image webapp:v2.0 \
  --build-arg VERSION=2.0 \
  .

# List all tasks
az acr task list \
  --registry $ACR_NAME \
  --output table

Step 8.2: Enable Content Trust (Premium SKU) ​

bash
# Note: Requires Premium SKU
# az acr update --name $ACR_NAME --sku Premium

# Enable content trust
# az acr config content-trust update \
#   --registry $ACR_NAME \
#   --status enabled

Step 8.3: Purge Old Images ​

bash
# Delete images older than 30 days (dry run)
az acr run \
  --registry $ACR_NAME \
  --cmd "acr purge --filter 'webapp:.*' --ago 30d --dry-run" \
  /dev/null

# Actually purge (remove --dry-run)
# az acr run \
#   --registry $ACR_NAME \
#   --cmd "acr purge --filter 'webapp:.*' --ago 30d" \
#   /dev/null

Summary Table ​

ComponentPurposeConfiguration
ACRImage registryBasic SKU, admin enabled
ACI SimpleSingle container1 CPU, 1.5 GB RAM
ACI Multi-containerWebapp + sidecar1.5 CPU, 2 GB RAM total
ACI with VolumePersistent storageAzure Files mount
ACI in VNetPrivate networkingDelegated subnet

Exam Tips ​

  1. ACR SKUs: Basic (10GB), Standard (100GB), Premium (500GB + geo-replication)
  2. ACI Restart Policies: Always, OnFailure, Never
  3. ACI Resource Limits: Max 4 CPUs, 16 GB memory per container group
  4. VNet Integration: Requires subnet delegation
  5. Multi-container: Containers share localhost networking

Cleanup ​

bash
# Delete all containers
az container delete --resource-group $RG_NAME --name aci-webapp-simple --yes
az container delete --resource-group $RG_NAME --name cg-multicontainer --yes
az container delete --resource-group $RG_NAME --name aci-with-volume --yes
az container delete --resource-group $RG_NAME --name aci-vnet-private --yes

# Delete resource group
az group delete --name $RG_NAME --yes --no-wait

# Cleanup local files
cd ..
rm -rf container-app

echo "Cleanup initiated"

Key Takeaways ​

  1. ACR Tasks: Build images without local Docker
  2. ACI: Serverless containers, pay per second
  3. Multi-container: Sidecar pattern for auxiliary services
  4. Volume Mounts: Azure Files for persistent data
  5. VNet Integration: Private container deployments
  6. ACI vs AKS: ACI for simple/short-lived, AKS for complex orchestration

Released under the MIT License.