Lý Thuyết Cốt Lõi
1. Container vs VM — Nền Tảng Docker
Container và Virtual Machine đều là công nghệ ảo hóa nhưng ở các lớp khác nhau. Hiểu sự khác biệt giúp chọn đúng công nghệ cho từng workload.
| Tiêu chí | Virtual Machine | Container |
|---|---|---|
| Ảo hóa | Hardware (Hypervisor) | OS kernel (namespaces/cgroups) |
| Khởi động | Vài phút (boot OS đầy đủ) | Vài giây (process start) |
| Kích thước | GB (cả OS image) | MB (chỉ app + dependencies) |
| Isolation | Mạnh — kernel riêng | Nhẹ hơn — chia sẻ kernel host |
| Tài nguyên | Cao (RAM/CPU dành riêng) | Thấp (chia sẻ OS resources) |
| Portability | Trung bình (phụ thuộc hypervisor) | Cao — chạy giống nhau mọi nơi |
| Phù hợp | Legacy app, full OS access, high isolation | Microservices, CI/CD, batch jobs |
- • Docker Engine: runtime chạy container trên host OS
- • Dockerfile: script định nghĩa cách build image từng bước
- • Docker Image: template read-only — layers của filesystem
- • Docker Container: instance đang chạy của image (có writable layer)
- • Docker Hub / ACR: registry lưu trữ và phân phối images
- • docker-compose: định nghĩa multi-container app bằng YAML
- • Private Docker registry được quản lý bởi Azure
- • Tích hợp với Entra ID — không cần quản lý credentials thủ công
- • Geo-replication: đồng bộ images qua nhiều region
- • ACR Tasks: build image trực tiếp trên cloud (không cần local Docker)
- • Vulnerability scanning: quét lỗ hổng bảo mật tự động
- • SKUs: Basic / Standard / Premium (geo-replication, private endpoint)
2. Azure Container Instances (ACI) — Serverless Containers
ACI là cách nhanh nhất và đơn giản nhất để chạy container trên Azure — không cần cấu hình VM, không cần Kubernetes cluster. Tính phí theo giây sử dụng thực tế (vCPU và memory).
Container khởi động trong vài giây. Không cần provision VM hay cài đặt runtime. Pull image từ ACR hoặc Docker Hub và chạy ngay.
Tính theo giây — chỉ trả tiền khi container đang chạy. Không tốn phí khi container dừng. Phù hợp batch job chạy không liên tục.
Hypervisor-level isolation — mỗi container group chạy trên kernel riêng. Hỗ trợ VNet integration, Managed Identity, Secret từ Key Vault.
- • CPU: 0.1 → 4 vCPU (theo bước 0.1)
- • Memory: 0.1 → 16 GB (theo bước 0.1)
- • GPU: hỗ trợ NVIDIA GPU (K80, P100, V100)
- • OS: Linux hoặc Windows containers
- • Restart policy: Always / OnFailure / Never
- • Storage: Azure Files mount, emptyDir, secret volume
- • DNS label: tên miền công khai
name.region.azurecontainer.io
- • Batch processing: xử lý file, ETL pipeline, report generation
- • CI/CD build agent: chạy build/test và tự xóa sau khi xong
- • Event-driven tasks: kích hoạt bởi Logic Apps, Event Grid
- • Dev/Test environment: tạo env tạm thời, xóa sau khi test
- • API prototype: test container image trước khi lên AKS/App Service
- • Machine learning inference: chạy model trong container
3. Container Groups — Multi-Container Pods
Container Group là đơn vị scheduling của ACI — tương đương Kubernetes Pod. Nhiều container trong cùng 1 group chia sẻ localhost network, storage volumes, và lifecycle. Chỉ hỗ trợ trên Linux containers.
- • Tất cả containers trong group chạy trên cùng host node
- • Chia sẻ IP address và port namespace
- • Containers giao tiếp với nhau qua
localhost - • Volume mount chung: Azure Files, emptyDir, secret, configMap
- • Group có 1 public IP — port mapping từng container
- • CPU/Memory cấp phát cho toàn group, chia giữa containers
- • App container: chứa logic nghiệp vụ chính (vd: web API)
- • Sidecar container: hỗ trợ app container (cùng group)
- • Logging sidecar: đọc log file từ shared volume, đẩy lên Azure Monitor
- • Proxy sidecar: Envoy proxy xử lý TLS termination và circuit breaker
- • Init container: chạy trước app container (setup, migration DB)
| Tiêu chí | ACI | AKS | App Service (container) |
|---|---|---|---|
| Quản lý | Serverless — không cần cluster | Managed Kubernetes cluster | PaaS — quản lý như web app |
| Scale | Manual / qua Logic Apps | HPA, KEDA, cluster autoscaler | Autoscale theo plan |
| Chi phí | Per-second, rẻ nhất cho burst | Node pool VM (trả cả khi idle) | Theo plan (Standard+) |
| Use case | Batch, short-lived, dev/test | Production microservices phức tạp | Web app, API đơn giản |
4. Networking, Storage & Bảo Mật ACI
- • Public IP: ACI nhận IP công khai, truy cập từ internet
- • DNS label:
myapp.southeastasia.azurecontainer.io - • VNet deployment: ACI trong subnet riêng — chỉ private IP
- • Port mapping: expose port cụ thể (TCP/UDP) ra ngoài
- • Không có Load Balancer: chỉ 1 IP per group — dùng Application Gateway phía trước nếu cần LB
- • Azure Files: SMB share — persistent storage, tồn tại sau khi container xóa
- • emptyDir: temporary storage — mất khi container restart
- • secret: inject Kubernetes-style secrets vào filesystem
- • gitRepo: clone git repo vào container lúc khởi động
- • Mount path:
/mnt/datatrong container
- • Managed Identity: container dùng identity để access Azure resources
- • Environment variables: inject config — dùng
secureValuecho secrets - • Private registry: pull image từ ACR với Managed Identity hoặc credential
- • VNet isolation: container không có public IP khi deploy vào VNet
- • Non-root user: chạy container với USER non-root trong Dockerfile
- • az container logs: xem stdout/stderr của container
- • az container exec: shell vào container đang chạy để debug
- • az container attach: attach live output stream
- • Log Analytics workspace: gửi container logs lên Azure Monitor
- • Metrics: CPU/memory usage qua Azure Monitor
Bài Tập Thực Hành (Lab)
Tạo Resource Group và chạy ACI đầu tiên từ Docker Hub
# Tạo Resource Group
az group create \
--name rg-az104-m25 \
--location southeastasia \
--tags Course=AZ-104 Module=25 Environment=Lab
# Chạy ACI đơn giản từ public Docker Hub image (nginx)
az container create \
--name aci-nginx-m25 \
--resource-group rg-az104-m25 \
--image nginx:latest \
--cpu 0.5 \
--memory 0.5 \
--ports 80 \
--ip-address Public \
--dns-name-label "aci-nginx-m25-$(date +%s | tail -c 6)" \
--os-type Linux \
--restart-policy Always
# Xem trạng thái container
az container show \
--name aci-nginx-m25 \
--resource-group rg-az104-m25 \
--query "{name:name, state:instanceView.state, ip:ipAddress.ip, fqdn:ipAddress.fqdn, cpu:containers[0].resources.requests.cpu, memory:containers[0].resources.requests.memoryInGb}" \
--output table
# Xem logs container
az container logs \
--name aci-nginx-m25 \
--resource-group rg-az104-m25
aci-nginx-m25 → Overview: xác nhận State = Running, Public IP và FQDN hiển thị. Mở trình duyệt truy cập FQDN → xác nhận trang nginx welcome page xuất hiện. Vào Containers → Logs xem access log.
Tạo Azure Container Registry và đẩy image lên ACR
# Tên ACR phải unique toàn cầu, chỉ chữ và số
ACR_NAME="acraz104m25$(date +%s | tail -c 6)"
echo "ACR name: $ACR_NAME"
# Tạo Azure Container Registry (Basic SKU)
az acr create \
--name "$ACR_NAME" \
--resource-group rg-az104-m25 \
--sku Basic \
--admin-enabled true \
--location southeastasia
# Lấy login server
ACR_SERVER=$(az acr show \
--name "$ACR_NAME" \
--resource-group rg-az104-m25 \
--query loginServer --output tsv)
echo "ACR Login Server: $ACR_SERVER"
# Build image trực tiếp trên ACR (dùng ACR Tasks — không cần Docker local)
# Sử dụng Dockerfile từ sample GitHub repo
az acr build \
--registry "$ACR_NAME" \
--image "myapp:v1.0" \
https://github.com/Azure-Samples/aci-helloworld
# Liệt kê images trong ACR
az acr repository list \
--name "$ACR_NAME" \
--output table
az acr repository show-tags \
--name "$ACR_NAME" \
--repository myapp \
--output table
# Lấy credentials ACR để pull image
ACR_USERNAME=$(az acr credential show \
--name "$ACR_NAME" \
--query username --output tsv)
ACR_PASSWORD=$(az acr credential show \
--name "$ACR_NAME" \
--query "passwords[0].value" --output tsv)
echo "ACR Username: $ACR_USERNAME"
# Chạy ACI từ private ACR image
az container create \
--name aci-myapp-m25 \
--resource-group rg-az104-m25 \
--image "$ACR_SERVER/myapp:v1.0" \
--cpu 0.5 \
--memory 0.5 \
--ports 80 \
--ip-address Public \
--dns-name-label "aci-myapp-m25-$(date +%s | tail -c 6)" \
--registry-login-server "$ACR_SERVER" \
--registry-username "$ACR_USERNAME" \
--registry-password "$ACR_PASSWORD" \
--os-type Linux \
--environment-variables APP_ENV=Lab APP_VERSION=v1.0
# Kiểm tra container mới
az container show \
--name aci-myapp-m25 \
--resource-group rg-az104-m25 \
--query "{state:instanceView.state, fqdn:ipAddress.fqdn}" \
--output table
acraz104m25... → Repositories → xác nhận myapp với tag v1.0. Vào Container instances → aci-myapp-m25 → truy cập FQDN → xác nhận app "Hello World from Azure Container Instances!" hiển thị đúng.
Triển khai Container Group nhiều container bằng YAML
# Tạo YAML định nghĩa container group: app + sidecar logger
cat > /tmp/container-group-m25.yaml << 'EOF'
apiVersion: '2021-10-01'
location: southeastasia
name: cg-az104-m25
properties:
containers:
- name: app-container
properties:
image: nginx:alpine
resources:
requests:
cpu: 0.5
memoryInGb: 0.5
ports:
- port: 80
protocol: TCP
environmentVariables:
- name: APP_NAME
value: HoaTranLab-Demo
- name: APP_SECRET
secureValue: SuperSecretPassword123
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
- name: logger-sidecar
properties:
image: busybox:latest
resources:
requests:
cpu: 0.1
memoryInGb: 0.1
command:
- sh
- -c
- "while true; do cat /shared-logs/access.log 2>/dev/null || echo 'waiting for logs...'; sleep 10; done"
volumeMounts:
- name: shared-logs
mountPath: /shared-logs
volumes:
- name: shared-logs
emptyDir: {}
ipAddress:
type: Public
ports:
- port: 80
protocol: TCP
dnsNameLabel: cg-az104-m25-demo
osType: Linux
restartPolicy: Always
tags:
Course: AZ-104
Module: "25"
type: Microsoft.ContainerInstance/containerGroups
EOF
# Deploy container group từ YAML
az container create \
--resource-group rg-az104-m25 \
--file /tmp/container-group-m25.yaml
# Xem trạng thái container group
az container show \
--name cg-az104-m25 \
--resource-group rg-az104-m25 \
--query "{state:instanceView.state, containers:containers[].name, ip:ipAddress.ip}" \
--output json
# Xem logs của từng container trong group
az container logs \
--name cg-az104-m25 \
--resource-group rg-az104-m25 \
--container-name app-container
az container logs \
--name cg-az104-m25 \
--resource-group rg-az104-m25 \
--container-name logger-sidecar
# Exec vào container để kiểm tra
az container exec \
--name cg-az104-m25 \
--resource-group rg-az104-m25 \
--container-name app-container \
--exec-command "/bin/sh"
cg-az104-m25 → Containers: xác nhận 2 containers (app-container và logger-sidecar) đều ở trạng thái Running. Vào tab Logs từng container → xác nhận secureValue không hiện trong logs. Truy cập IP → xác nhận nginx đang chạy.
Mount Azure Files volume — Persistent storage cho container
STORAGE_NAME="staz104m25$(date +%s | tail -c 6)"
SHARE_NAME="aci-data"
# Tạo Storage Account cho Azure Files
az storage account create \
--name "$STORAGE_NAME" \
--resource-group rg-az104-m25 \
--location southeastasia \
--sku Standard_LRS \
--kind StorageV2 \
--https-only true
# Tạo Azure File Share
az storage share create \
--name "$SHARE_NAME" \
--account-name "$STORAGE_NAME" \
--quota 5
# Lấy storage key
STORAGE_KEY=$(az storage account keys list \
--resource-group rg-az104-m25 \
--account-name "$STORAGE_NAME" \
--query "[0].value" --output tsv)
# Tạo file test trên share để kiểm tra mount
az storage file upload \
--account-name "$STORAGE_NAME" \
--account-key "$STORAGE_KEY" \
--share-name "$SHARE_NAME" \
--source /tmp/container-group-m25.yaml \
--path "test-file.yaml"
# Chạy ACI với Azure Files volume mount
az container create \
--name aci-files-m25 \
--resource-group rg-az104-m25 \
--image alpine:latest \
--cpu 0.25 \
--memory 0.25 \
--command-line "sh -c 'ls /mnt/data && echo \"Files mounted successfully\" && cat /mnt/data/test-file.yaml | head -5 && sleep 300'" \
--azure-file-volume-account-name "$STORAGE_NAME" \
--azure-file-volume-account-key "$STORAGE_KEY" \
--azure-file-volume-share-name "$SHARE_NAME" \
--azure-file-volume-mount-path /mnt/data \
--os-type Linux \
--restart-policy Never
# Chờ container chạy và xem kết quả
sleep 15
az container logs \
--name aci-files-m25 \
--resource-group rg-az104-m25
aci-files-m25 → Logs: xác nhận output "Files mounted successfully" và nội dung file YAML được in ra. Vào Storage accounts → File shares → aci-data → xác nhận file test-file.yaml vẫn tồn tại sau khi container kết thúc (persistent storage).
Cleanup — Dọn dẹp tài nguyên
# Xóa toàn bộ Resource Group (ACI, ACR, Storage Account)
az group delete --name rg-az104-m25 --yes --no-wait
# Xác nhận đang xóa
az group show --name rg-az104-m25 --query "properties.provisioningState" -o tsv
# Kết quả mong đợi: Deleting
Kết Quả Đầu Ra
So sánh isolation level, boot time, kích thước, tài nguyên và use case phù hợp cho từng công nghệ ảo hóa
Hiểu Dockerfile, Image, Container, Registry — biết build image qua ACR Tasks không cần Docker local
Chạy container từ Docker Hub, tạo ACR, build và push image, deploy ACI từ private registry với credentials
Định nghĩa multi-container group bằng YAML, hiểu shared network/storage, triển khai logger sidecar
Mount Azure File Share vào container — dữ liệu tồn tại sau khi container xóa, chia sẻ giữa nhiều containers
So sánh ACI vs AKS vs App Service Container — biết khi nào dùng ACI, khi nào cần AKS cho orchestration phức tạp
Ứng Dụng Thực Tế
Tình huống 1: Công ty kế toán — Batch job xuất báo cáo thuế cuối tháng
Mỗi cuối tháng cần chạy job xử lý dữ liệu từ hàng nghìn hóa đơn, xuất PDF báo cáo VAT cho từng khách hàng. Job chạy ~2 giờ, sau đó không cần resource.
ACI với restart policy = Never. Container đọc dữ liệu từ Azure SQL, xuất PDF vào Azure Files share. Kích hoạt tự động bằng Logic Apps vào ngày 1 hàng tháng. Container tự terminate sau khi hoàn tất.
Container 2 vCPU, 4 GB RAM. Azure Files mount tại /output để lưu PDF. Managed Identity lấy SQL connection string từ Key Vault. Container group trong VNet — không public IP. ACR Premium lưu image.
Chi phí ~5,000đ/lần chạy (tính theo giây thực tế). So với VM chạy 24/7: tiết kiệm >95%. Không quản lý VM. PDF output tồn tại trong Azure Files dài hạn. Tự động hoàn toàn — không cần IT thủ công.
Tình huống 2: Startup SaaS — CI/CD build agent tự scale theo demand
Team dev 15 người có lúc đồng thời trigger 10 build pipeline, lúc khác không có build nào. CI agent cần co giãn linh hoạt mà không lãng phí VM luôn chạy.
ACI làm ephemeral build agent cho Azure DevOps. Mỗi pipeline job tạo 1 ACI mới (custom Docker image chứa build tools), chạy build + test, push artifact, rồi tự xóa. Container group với sidecar artifact uploader.
ACR Premium lưu build agent image. ACI 1 vCPU, 2 GB RAM per build. Azure DevOps YAML pipeline dùng pool: {name: "Azure Container Instance"}. emptyDir volume chia sẻ workspace giữa build và sidecar upload containers.
Zero idle cost — không trả tiền lúc không build. Mỗi build trong môi trường sạch — không artifact pollution giữa các build. Parallel build không giới hạn (tùy ngân sách). Build time giảm 30% nhờ container nhỏ, khởi động nhanh.
Tình huống 3: Công ty logistics — AI inference container xử lý ảnh đơn hàng
Hệ thống phân loại tự động đơn hàng bằng AI: chụp ảnh nhãn hàng, container chạy model AI phân loại, kết quả đưa vào hệ thống WMS. Traffic không đều — bùng phát lúc nhận hàng buổi sáng.
ACI với GPU (K80) chạy ONNX model inference. Event Grid trigger từ Blob Storage (ảnh mới upload) → Logic App → tạo ACI → inference → ghi kết quả vào Azure SQL → ACI tự xóa. Container group: main inference + sidecar result-writer.
ACI 1 vCPU, 4 GB RAM + GPU K80. Container image trong ACR Premium (geo-replicated). Model file mount từ Azure Files. Managed Identity cho Blob và SQL access. VNet deployment — không IP công khai. Log Analytics cho monitoring AI inference latency.
Accuracy phân loại 97% so với nhân viên thủ công 85%. Chi phí GPU inference ~2,000đ/1,000 ảnh. Xử lý 500 ảnh/giờ lúc cao điểm mà không cần GPU VM luôn chạy. Dữ liệu không rời VNet nội bộ — đáp ứng yêu cầu bảo mật của đối tác logistics nước ngoài.