Azure: cùng kiến trúc, tên khác, vài khác biệt quan trọng

4 câu hỏi~30 phútTừ khóa: VNet, NSG, App Service, AKS, Container Apps, Azure Database for PostgreSQL, Azure Cache for Redis, Blob, Service Bus, Event Hubs, Managed Identity, Key Vault, Azure Monitor

Tiếp đi: Azure. Khách hàng mới bắt buộc chạy shop-app trên Azure, mình chỉ quen AWS.

Tin tốt: kiến trúc ở bài 18.5 giữ nguyên, phần lớn chỉ đổi tên. Bài này đi qua các tương đương, rồi tập trung vào những chỗ Azure khác thật sự.

1. Bài toán

Đưa shop-app (gateway, order, payment, inventory) lên Azure: PostgreSQL, Redis, lưu hóa đơn, messaging, không có password/connection string nào trong code.

2. Vấn đề khi bê nguyên tư duy AWS

3. Giải pháp: bản đồ dịch vụ Azure

Nhu cầuAzure
MạngVNet, subnet, NSG (Network Security Group), NAT Gateway, Private Endpoint
Cửa vàoAzure DNS, Front Door (global + WAF), Application Gateway (L7 regional + WAF), Load Balancer (L4)
Chạy appVM / App Service / AKS / Container Apps / Functions
DatabaseAzure Database for PostgreSQL – Flexible Server
CacheAzure Cache for Redis (Azure Managed Redis)
FileBlob Storage
MessagingService Bus (queue/topic), Event Hubs (streaming, có endpoint Kafka), Event Grid
ImageAzure Container Registry (ACR)
Định danhManaged Identity, Workload Identity (AKS), Entra ID RBAC
SecretKey Vault
Quan sátAzure Monitor, Log Analytics, Application Insights

Chọn nơi chạy app

VMApp ServiceContainer AppsAKS
Bạn quản lýOS, JVMChỉ JAR/containerContainer, scale ruleCluster K8s, manifest
Phù hợpLegacy, cần kiểm soátWeb app/API đơn lẻ, team nhỏMicroservices không muốn vận hành K8s, scale về 0 (KEDA)Nhiều service, hệ sinh thái Helm/Argo CD
Tương đương AWSEC2Elastic BeanstalkECS Fargate / App RunnerEKS

shop-app đã dùng Helm + Argo CD nên chọn AKS; chart giữ nguyên, chỉ đổi values (storage class, annotation identity, ingress).

4. Code từng bước

Bước 1: hạ tầng bằng Terraform (azurerm, rút gọn)

resource "azurerm_resource_group" "shop" {
  name     = "rg-shop-prod"
  location = "southeastasia"
}

resource "azurerm_kubernetes_cluster" "aks" {
  name                      = "aks-shop-prod"
  resource_group_name       = azurerm_resource_group.shop.name
  location                  = azurerm_resource_group.shop.location
  dns_prefix                = "shop"
  oidc_issuer_enabled       = true      # cho Workload Identity
  workload_identity_enabled = true
  default_node_pool {
    name           = "system"
    vm_size        = "Standard_D4s_v5"
    node_count     = 3
    vnet_subnet_id = azurerm_subnet.aks.id
    zones          = ["1", "2", "3"]
  }
  identity { type = "SystemAssigned" }
}

resource "azurerm_user_assigned_identity" "order" {
  name                = "id-order-service"
  resource_group_name = azurerm_resource_group.shop.name
  location            = azurerm_resource_group.shop.location
}

# Quyền: chỉ ghi blob vào storage hóa đơn
resource "azurerm_role_assignment" "order_blob" {
  scope                = azurerm_storage_account.invoices.id
  role_definition_name = "Storage Blob Data Contributor"
  principal_id         = azurerm_user_assigned_identity.order.principal_id
}

resource "azurerm_federated_identity_credential" "order" {
  name                = "order-service-sa"
  resource_group_name = azurerm_resource_group.shop.name
  parent_id           = azurerm_user_assigned_identity.order.id
  issuer              = azurerm_kubernetes_cluster.aks.oidc_issuer_url
  subject             = "system:serviceaccount:shop:order-service"
  audience            = ["api://AzureADTokenExchange"]
}

State để ở Azure Storage backend (blob lease làm lock), giống bài 18.4.

Bước 2: ServiceAccount dùng Workload Identity

apiVersion: v1
kind: ServiceAccount
metadata:
  name: order-service
  namespace: shop
  annotations:
    azure.workload.identity/client-id: ${ORDER_IDENTITY_CLIENT_ID}
---
# trong pod template của Deployment
metadata:
  labels:
    azure.workload.identity/use: "true"

Bước 3: Java dùng DefaultAzureCredential

<dependency>
  <groupId>com.azure.spring</groupId>
  <artifactId>spring-cloud-azure-starter-storage-blob</artifactId>
</dependency>
<dependency>
  <groupId>com.azure.spring</groupId>
  <artifactId>spring-cloud-azure-starter-keyvault-secrets</artifactId>
</dependency>   <!-- version qua BOM spring-cloud-azure-dependencies -->
# application.yml
spring:
  cloud:
    azure:
      storage.blob.account-name: ${INVOICE_STORAGE_ACCOUNT}   # không có account key
      keyvault.secret.property-sources[0].endpoint: ${KEYVAULT_URI}
  datasource:
    url: jdbc:postgresql://${DB_HOST}:5432/shop?sslmode=require
    username: ${DB_USER}
    password: ${db-password}     # secret "db-password" đọc từ Key Vault
@Service
@RequiredArgsConstructor
class InvoiceStorage {
    private final BlobServiceClient blobs;   // starter tự tạo, dùng DefaultAzureCredential

    public String save(long orderId, byte[] pdf) {
        String name = "invoices/%d.pdf".formatted(orderId);
        blobs.getBlobContainerClient("invoices")
             .getBlobClient(name)
             .upload(BinaryData.fromBytes(pdf), true);
        return name;
    }
}

Với PostgreSQL có thể bỏ luôn password: bật Entra authentication và dùng spring.datasource.azure.passwordless-enabled: true, app lấy token từ Managed Identity.

Bước 4: kiểm tra

kubectl -n shop exec deploy/order-service -- env | grep AZURE_   # AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE
curl -X POST https://api.shop.example/orders/1001/invoice
az storage blob list --account-name "$INVOICE_STORAGE_ACCOUNT" -c invoices --auth-mode login -o table

5. Kiến trúc tham chiếu

Internet ─► Front Door + WAF ─► Application Gateway (AGIC) hoặc Ingress NGINX
                                    │
          ┌─────── VNet 10.30.0.0/16 (rg-shop-prod) ───────────────────┐
          │  snet-aks (3 zone): gateway ─► order / payment / inventory │
          │        │ Workload Identity                                  │
          │        ▼                                                    │
          │  Private Endpoint: PostgreSQL Flexible (zone-redundant HA), │
          │                    Redis, Blob, Service Bus, Key Vault      │
          └─────────────────────────────────────────────────────────────┘
  ACR (pull bằng managed identity của AKS) · Azure Monitor / App Insights · Entra ID

6. Bảng ánh xạ AWS ↔ Azure

NhómAWSAzureKhác biệt đáng nhớ
Tổ chứcAccount, OrganizationsSubscription, Resource Group, Management GroupResource Group gom vòng đời và quyền
MạngVPC, subnetVNet, subnetSubnet AWS gắn 1 AZ; subnet Azure trải mọi zone
FirewallSecurity Group, NACLNSG, ASGNSG gắn subnet hoặc NIC, có cả allow/deny theo priority
Kết nối riêngVPC endpoint / PrivateLinkPrivate Endpoint / Private Link—
LBALB / NLBApplication Gateway / Load Balancer—
EdgeCloudFront + WAFFront Door + WAF—
VMEC2Virtual Machines—
PaaS appElastic Beanstalk / App RunnerApp Service / Container AppsContainer Apps có KEDA, scale về 0
KubernetesEKSAKSAKS không tính phí control plane ở tier Free
ServerlessLambdaFunctions—
PostgreSQLRDS / AuroraDatabase for PostgreSQL Flexible—
RedisElastiCacheAzure Cache for Redis—
Object storageS3Blob StorageBlob nằm trong Storage Account
QueueSQSService Bus queueService Bus có session, transaction, DLQ sẵn
Pub/subSNSService Bus topic / Event Grid—
StreamingMSK / KinesisEvent HubsEvent Hubs nói được giao thức Kafka
RegistryECRACR—
Định danh workloadIAM role, IRSAManaged Identity, Workload IdentityQuyền = role assignment ở một scope
SecretSecrets Manager, KMSKey VaultKey Vault gộp secret, key, certificate
Quan sátCloudWatch, X-RayAzure Monitor, Log Analytics, App InsightsTruy vấn log bằng KQL
IaC gốcCloudFormation, CDKARM, BicepTerraform dùng được cho cả hai

7. Khi nào nên / không nên

Managed Identity với Service Principal khác nhau chỗ nào? Mình thấy tài liệu cũ toàn hướng dẫn tạo Service Principal rồi lấy client secret.

Đúng rồi, tài liệu cũ hay dùng Service Principal + client secret. Cả hai đều là danh tính trong Entra ID, khác ở chỗ ai giữ credential:

Service Principal + secretManaged Identity
CredentialBạn giữ client secret/cert, phải rotateAzure tự quản, không ai thấy
Vòng đờiTạo, xóa taySystem-assigned: gắn theo resource; user-assigned: tái dùng được
Dùng khiỨng dụng chạy ngoài Azure không có federationMọi workload chạy trên Azure

CI (GitHub Actions) ngoài Azure cũng không cần secret: dùng federated credential (OIDC) trên app registration, giống OIDC với AWS ở bài 18.2.

Service Bus và Event Hubs nghe giống nhau, order-service phát event "OrderPlaced" thì nên dùng cái nào?

Hai cái giải hai bài khác nhau, giống RabbitMQ vs Kafka:

"OrderPlaced" để payment và inventory xử lý: Service Bus topic với 2 subscription. Nếu shop-app đang dùng Kafka + outbox/CDC và muốn giữ code, chọn Event Hubs (Kafka endpoint).

Phỏng vấn hỏi về Azure thì trả lời sao?

Q1. Bạn deploy Spring Boot microservices lên Azure thế nào?

🇻🇳 Tiếng Việt:

Image build trong CI, push lên ACR. Chạy trên AKS (hoặc Container Apps nếu ít service), deploy bằng Helm qua Argo CD. Traffic vào qua Front Door và Application Gateway có WAF. PostgreSQL Flexible Server, Azure Cache for Redis, Blob, Service Bus, tất cả qua Private Endpoint trong VNet. Hạ tầng bằng Terraform, log và metric vào Azure Monitor và Application Insights.

🇬🇧 English:

CI builds the image and pushes it to ACR. Services run on AKS, or Container Apps for a small number of services, deployed with Helm through Argo CD. Traffic enters through Front Door and Application Gateway with WAF. We use PostgreSQL Flexible Server, Azure Cache for Redis, Blob Storage and Service Bus, all reached through Private Endpoints in the VNet. Infrastructure is Terraform, and telemetry goes to Azure Monitor and Application Insights.

Q2. Làm sao để không có secret trong cấu hình ứng dụng trên Azure?

🇻🇳 Tiếng Việt:

Dùng Managed Identity; trên AKS là Workload Identity liên kết ServiceAccount với user-assigned identity. Cấp quyền bằng role assignment đúng scope, ví dụ Storage Blob Data Contributor trên một storage account. Code dùng DefaultAzureCredential. Secret còn lại để trong Key Vault, PostgreSQL có thể dùng Entra authentication không cần password.

🇬🇧 English:

We use Managed Identity; on AKS, Workload Identity links a ServiceAccount to a user-assigned identity. Permissions are role assignments at the narrowest scope, for example Storage Blob Data Contributor on one storage account. Code uses DefaultAzureCredential. Remaining secrets live in Key Vault, and PostgreSQL can use Entra authentication without a password.

Q3. Bạn đã dùng AWS, chuyển sang Azure thấy khác gì?

🇻🇳 Tiếng Việt:

Kiến trúc gần như ánh xạ một-một: VPC thành VNet, IAM role thành Managed Identity, S3 thành Blob, SQS thành Service Bus, EKS thành AKS. Khác biệt chính là mô hình tổ chức Subscription và Resource Group, quyền dựa trên Entra ID và role assignment, subnet trải nhiều zone, và Event Hubs hỗ trợ giao thức Kafka. Vì code tách storage và messaging sau interface, phần đổi chủ yếu là cấu hình và hạ tầng.

🇬🇧 English:

The architecture maps almost one to one: VPC to VNet, IAM roles to Managed Identity, S3 to Blob, SQS to Service Bus, EKS to AKS. The main differences are the Subscription and Resource Group model, permissions based on Entra ID role assignments, subnets spanning zones, and Event Hubs speaking the Kafka protocol. Because our code hides storage and messaging behind interfaces, the change was mostly configuration and infrastructure.

Cách nhớ ngắn gọn


Tự kiểm tra

Workload Identity trên AKS cần những mảnh nào?

Cluster bật OIDC issuer + workload identity, user-assigned identity, federated credential trỏ tới ServiceAccount, annotation client-id trên SA và label use trên pod.

Tương đương của SQS và SNS trên Azure?

Service Bus queue (SQS); Service Bus topic hoặc Event Grid (SNS).

App Service và Container Apps khác nhau chỗ nào?

App Service chạy web app/API đơn lẻ (JAR hoặc container). Container Apps hướng microservices, có KEDA autoscale, scale về 0, Dapr, revision traffic split.

Private Endpoint giải quyết gì?

Cho PaaS (PostgreSQL, Blob, Key Vault) một IP riêng trong VNet, tắt truy cập public.

Event Hubs hợp với tình huống nào hơn Service Bus?

Streaming lượng lớn, cần replay, consumer tự giữ offset, hoặc muốn dùng client Kafka.

Tổng kết chương 18

BàiChủ đềÝ chính
18.1GitCommit bất biến, branch là con trỏ; rebase cái của mình, merge cái của chung; revert trên main, reflog để cứu; trunk-based + feature flag
18.2CI pipelinebuild → test → scan → image → push; build once deploy many; tag git SHA; secret qua OIDC
18.3CD & zero-downtimeRolling / blue-green / canary / feature flag; migration expand-contract; GitOps với Argo CD
18.4TerraformDeclarative; plan trước apply; state remote + lock; module theo môi trường; xử lý drift
18.5AWSVPC 3 tầng subnet, EKS/ECS/Lambda, RDS, ElastiCache, S3, SQS/SNS/MSK, IAM role/IRSA, CloudWatch
18.6AzureVNet/NSG, AKS/Container Apps, PostgreSQL Flexible, Blob, Service Bus/Event Hubs, Managed Identity, Key Vault, Monitor