Trong bài viết này, chúng ta sẽ tạo Docker Image cho SQL Server, tích hợp thư viện AspNetCore.HealthChecks.SqlServer vào ứng dụng ASP.NET Core, sau đó dùng Terraform để triển khai cả hai container (ASP.NET Core và SQL Server) cùng lúc lên Azure Container Instances (ACI).
Cấu trúc dự án
Chúng ta cần một folder riêng cho Dockerfile của SQL Server để tránh nhầm lẫn và một folder mới cho Terraform.tf-aci-fullstack/
├── main.tf
├── variables.tf
└── outputs.tf
sql-db-image/
└── Dockerfile
AspNetCoreGettingStarted/
├── *.*
├── Dockerfile
└── appsettings.json
SQL Server
Chúng ta sẽ sử dụng image chính thức của Microsoft.# Use the official Microsoft SQL Server image for Linux
FROM mcr.microsoft.com/mssql/server:2019-latest
# Set environment variables for SQL Server configuration
ENV ACCEPT_EULA=Y
ENV SA_PASSWORD=YourStrongPassword123!
ENV MSSQL_PID=Developer
# Expose the default SQL Server port
EXPOSE 1433
ASP.NET Core
Tạo ứng dụng ASP.NET Core. Thêm Health Check vào project ASP.NET Coredotnet add package AspNetCore.HealthChecks.SqlServer
Cập nhật appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=sql-db,1433;Initial Catalog=master;Persist Security Info=False;User ID=sa;Password=YourStrongPassword123!;Encrypt=True;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=30;"
}
}
Cập nhật file program.cs
builder.Services.AddControllersWithViews();
builder.Services.AddHealthChecks()
.AddSqlServer(
connectionString: builder.Configuration.GetConnectionString("DefaultConnection"),
healthQuery: "SELECT 1",
name: "sqlserver",
failureStatus: HealthStatus.Unhealthy,
tags: new[] { "db", "sql" }
);
var app = builder.Build();
//...
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(new
{
status = report.Status.ToString(),
checks = report.Entries.Select(e => new {
name = e.Key,
status = e.Value.Status.ToString(),
duration = e.Value.Duration.ToString()
})
}));
}
});
//...
Cập nhật Dockerfile
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS build-env
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim
WORKDIR /app
COPY --from=build-env /app/out .
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80
ENTRYPOINT ["dotnet", "AspNetCoreGettingStarted.dll"]
Local Test với docker
Để mô phỏng môi trường ACI (nơi các container có thể giao tiếp qua tên service), chúng ta cần tạo một Docker Network.# 1. Create a custom bridge network
docker network create my-aci-network
# 2. Run SQL Server container (hostname is its name: sql-db)
docker run -d --name sql-db --network my-aci-network yourdockerhubuser/mssql-dev:v1
# 3. Run ASP.NET Core container, using the SQL container name as the server name
# We override the Server name (SQL_SERVER_NAME) and Password (SA_PASSWORD) via ENV vars.
docker run -d --name aspnet-web --network my-aci-network \
-p 8080:80 \
yourdockerhubuser/aspnet-app:v1
# 4. Check Health Status
# After a few seconds for SQL Server to start, check the health endpoint:
curl http://localhost:8080/health
Lưu ý về Network và Service Discovery:
- Lệnh
docker network createtạo một mạng ảo nội bộ (Custom Bridge Network). - Khi bạn chạy containers và gắn chúng vào cùng một network (
--network aci-local-network), Docker sẽ tự động sử dụng **tên container** (sql-db) làm **hostname/DNS name** để container Web App có thể kết nối. Đây chính xác là cách ACI và Kubernetes hoạt động (Service Discovery).
Terraform
Trong phần này, chúng ta sẽ đẩy (upload) hai Docker images lên Azure Container Registry (ACR), sau đó triển khai (deploy) chúng lên Azure Container Instances (ACI).Azure Container Registry (ACR)
Azure Container Registry (ACR) là dịch vụ Registry Docker riêng tư được Azure quản lý hoàn toàn, cho phép chúng ta lưu trữ, quản lý và phân phối các container image một cách an toàn.
Trong quy trình DevOps này, ACR đóng vai trò là trung tâm lưu trữ container image — nơi chúng ta:
- Build Docker image cho ứng dụng (ví dụ: ASP.NET và SQL Server) trên máy local hoặc trong CI/CD pipeline.
- Push (đẩy) các image đó lên ACR bằng lệnh docker push hoặc thông qua Terraform / Azure CLI.
- Terraform sau đó sẽ lấy image từ ACR để triển khai container lên Azure Container Instances (ACI).
Tạo Instance Azure Container Registry
Trên Azure Portal => Create a resource => Chọn Container Registries.
Gõ Registry Name, ví dụ: nhatkyhoctap.azurecr.io
Sau khi tạo xong, bạn vào Container Registry => Settings => Access keys, sau đó enable Admin user. Bạn sẽ dùng settings này để deploy image lên App Service
Mở Visual Studio lên, chọn Terminal => Git Bash$ docker login nhatkyhoctap.azurecr.io
Authenticating with existing credentials...
Login did not succeed, error: Error response from daemon: Get "https://whizregistry.azurecr.io/v2/": unauthorized: authentication required, visit https://aka.ms/acr/authorization for more information.
Username (nhatkyhoctap): nhatkyhoctap
Password:
Login Succeeded
Trong Visual Studio Code, nhấp chọn biểu tượng Docker, chọn image => Push
Executing task: docker image push nhatkyhoctap.azurecr.io/demo:latest
The push refers to repository [nhatkyhoctap.azurecr.io/nhatkyhoctap]
7a6051a3589b: Preparing
a63d75c8cef3: Preparing
Trường hợp local image của bạn không phải là nhatkyhoctap.azurecr.io, bạn cần phải đổi tag lại, đảm bảo khi push image lên, image sẽ được push lên Azure Container Registries:
docker tag anbinhtrong/aspnet-app:v1 nhatkyhoctap.azurecr.io/aspnet-app:v1
Để xem images được push được Azure Container Registries, bạn vào: Container Registries > Pannel > Services > Repositories
Push Images lên Azure Container Registry (ACR)
Docker cần biết đích đến của image trước khi push. Giả sử ACR của bạn có địa chỉ url: anbinhtrong.azurecr.ioChúng ta sẽ gắn lại thẻ (tag) cho image local (ví dụ: ananh/...) để trỏ về ACR của mình (anbinhtrong.azurecr.io/...). Sử dụng tag :v3 cho các image đã fix lỗi (Globalization, SQL Batching)
docker tag ananh/aspnet-app:v3 anbinhtrong.azurecr.io/aspnet-app:v3
# Push Web App Image into ACR
docker push anbinhtrong.azurecr.io/aspnet-app:v3
docker tag ananh/mssql-dev:v3 anbinhtrong.azurecr.io/mssql-dev:v3
# Push SQL DB Image into ACR
docker push sd2294.azurecr.io/mssql-dev:v3
Lưu ý: Việc tách biệt tên image giữa local (ananh/...) và Registry (anbinhtrong.azurecr.io/...) là bắt buộc trong Docker.
Khi push, Docker dựa vào địa chỉ Registry để biết nơi upload image.
Implement Terraform
File: variables.tf: Khai báo các thông tin cơ bản: Resource Group, Region, ACR, Image tags và mật khẩu SQL Server.# Declare required Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
# General Azure Variables
variable "resource_group_name" {
description = "Name of the resource group to create"
type = string
default = "nhatkyhoctap"
}
variable "location" {
description = "Azure region to deploy resources"
type = string
default = "Southeast Asia"
}
# Azure Container Registry (ACR) Variables
variable "acr_name" {
description = "Globally unique name for the Azure Container Registry"
type = string
default = "nhatkyhoctap"
}
# Container Image Tags
variable "app_image_tag" {
description = "Tag for the ASP.NET Core application image in ACR"
type = string
default = "v1"
}
variable "db_image_tag" {
description = "Tag for the SQL Server database image in ACR"
type = string
default = "v2"
}
# Credentials & Secrets (For SQL Server)
variable "sa_password" {
description = "SA Password for SQL Server. Use a complex one!"
type = string
sensitive = true
default = "YourStrongPassword123!"
}
File main.tf: Đây là file chính, nơi chúng ta kết hợp Web App + SQL Server trong cùng một Azure Container Group
# Configure the Azure Provider
provider "azurerm" {
features {}
}
# 1. Create a Resource Group
data "azurerm_resource_group" "rg_existing" {
name = var.resource_group_name
}
# 2. Data Source: Reference the existing Azure Container Registry
# This resource is required to store the images we pushed in Step 1.1
data "azurerm_container_registry" "acr_existing" {
name = var.acr_name
resource_group_name = data.azurerm_resource_group.rg_existing.name
}
# 3. Deploy Azure Container Group (ACI) with 2 containers
resource "azurerm_container_group" "aci_fullstack" {
name = "aci-fullstack-web-sql"
location = var.location
resource_group_name = data.azurerm_resource_group.rg_existing.name
ip_address_type = "Public"
# Create a unique DNS name label based on the resource group name
dns_name_label = "${var.resource_group_name}-${substr(uuid(), 0, 8)}"
os_type = "Linux"
# CRUCIAL: ACI needs credentials to pull images from the private ACR
image_registry_credential {
server = data.azurerm_container_registry.acr_existing.login_server
username = data.azurerm_container_registry.acr_existing.admin_username
password = data.azurerm_container_registry.acr_existing.admin_password
}
# Container 1: ASP.NET Core Web App
container {
name = "aspnet-web"
# Image path uses ACR login server and the defined image tag
image = "${data.azurerm_container_registry.acr_existing.login_server}/aspnet-app:${var.app_image_tag}"
cpu = 0.5
memory = 1.5
ports {
port = 80
protocol = "TCP"
}
# CRUCIAL: Pass environment variables (using fixed Connection String)
environment_variables = {
# The App will connect to the SQL Server container using its name localhost as the hostname
"ConnectionStrings__DefaultConnection" = "Server=localhost,1433;Initial Catalog=master;User Id=sa;Password=${var.sa_password};Encrypt=False;TrustServerCertificate=False;MultipleActiveResultSets=False;Connection Timeout=30;"
}
}
# Container 2: SQL Server Database
container {
name = "sql-db" # This name is used as the hostname by the web app
# Image path uses ACR login server and the defined image tag
image = "${data.azurerm_container_registry.acr_existing.login_server}/mssql-dev:${var.db_image_tag}"
cpu = 2.0
memory = 4.0
# CRUCIAL: Environment variables for SQL Server setup
environment_variables = {
"ACCEPT_EULA" = "Y"
"SA_PASSWORD" = var.sa_password
"MSSQL_PID" = "Developer"
}
}
# Expose the web app publicly on port 80
exposed_port {
port = 80
protocol = "TCP"
}
}
File output.tf
# Output the public URL for the application
output "web_app_url" {
description = "The public URL for the ASP.NET Core application"
value = "http://${azurerm_container_group.aci_fullstack.fqdn}"
}
# Output the public Health Check endpoint URL
output "health_check_url" {
description = "The public Health Check endpoint URL"
value = "http://${azurerm_container_group.aci_fullstack.fqdn}/health"
}
# Output the ACR Login Server for reference
output "acr_login_server" {
description = "The login server name for Azure Container Registry"
value = data.azurerm_container_registry.acr_existing.login_server
}
Thực hiện lệnh
terraform init
terraform apply -auto-approveTerraform sẽ:
- Kéo image aspnet-app:v3 và mssql-dev:v3 từ ACR.
- Tạo Container Group aci-fullstack-web-sql trên Azure.
- Xuất ra public URL của ứng dụng và endpoint /health
Kết luận
ACR là kho trung tâm giúp quản lý và bảo mật image nội bộ.
Terraform tự động hóa toàn bộ quy trình deploy container lên Azure.
Cấu trúc này có thể mở rộng cho môi trường thực tế — chỉ cần thêm volume Azure Files, subnet, hoặc scale-out bằng ACI/AKS.

Nhận xét
Đăng nhận xét