
본 포스팅에서는 Azure 환경에서 Linux/Windows VM 기반의 SQL Server(IaaS)를 구축하고, 이를 Azure SQL Database(PaaS)로 마이그레이션한 뒤 웹 앱(Flask)의 연동을 전환하는 전체 과정을 실습함.
Part 1: IaaS 구성 Part 2: PaaS 마이그레이션
───────────────── ──────────────────────────
┌──────────────┐ ┌─────────────────────────┐
│ 로컬 PC │ │ Azure SQL Database │
│ SSMS ───────┼──── 1433 ──┐ │ (S0 / 10 DTU) │
│ 브라우저 ───┼──── 5000 │ │ CarMarket DB │
└──────────────┘ │ │ └────────────┬────────────┘
│ │ │ │
▼ ▼ │ Part 3: 앱 전환 │
┌──────────────┐ │ │
│ Azure VM │ ← DMS/sqlcmd ───────────────────────┘
│ Ubuntu 24.04│
│ SQL Server │
│ Flask App │
└──────────────┘
시스템 흐름: 데이터 수집 → 전처리/벡터화 → Cosmos DB 저장 → 사용자 쿼리 → 챗봇(RAG) 응답
핵심 기술: Azure Cosmos DB, Azure OpenAI, Gradio, Azure Functions
환경변수를 미리 설정하여 대화형 입력을 줄이고 터미널(Mac/Linux/WSL)에서 한 줄로 스크립트를 실행함.
********# Azure CLI 설치 (Windows)
winget install -e --id Microsoft.AzureCLI
# 환경변수 설정 및 스크립트 실행
export RG=3dt026-rg-carmarket-lab
export LOC=koreacentral
export SA_PASSWORD='CarMarket@2026'
bash "/mnt/z/microsoft/강의자료/애저VM을 활용한 SQL서버 운영및관리/scripts/01-setup-vm-sqlserver.sh"
💡 bash 스크립트 수행 작업 내역
1. Azure CLI 로그인 확인 및 SSH 키 검증
2. Resource Group 및 Ubuntu 24.04 VM (B2s) 생성
3. NSG(네트워크 보안 그룹)에서 22(SSH), 1433(SQL), 5000(Flask) 포트 오픈
4. SQL Server 2025 Developer Edition 설치
5. 0.0.0.0 바인딩 (외부 SSMS 접근 허용)
6. CarMarket DB 스키마 생성 및 시드 데이터 5건 입력
7. Flask 앱 배포 및 systemd 서비스 등록
🚨 트러블슈팅: 스크립트 실행 오류 시 해결 방안
Windows 환경에서 문제가 발생할 경우 PowerShell 7을 이용한 스크립트 실행을 권장함.
# (1) PowerShell 7 설치
winget install --id Microsoft.PowerShell --source winget
# (2) 새로 설치된 PowerShell 7(pwsh) 실행 후 실행 정책 변경
pwsh
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# (3) PowerShell용 스크립트 실행
cd "Z:\microsoft\강의자료\애저VM을 활용한 SQL서버 운영및관리\scripts"
.\01-setup-vm-sqlserver.ps1
# (4) 필요시 az CLI 설치 후 3번 다시 실행
winget install --id Microsoft.AzureCLI --source winget
********스크립트 완료 후 출력된 연결 정보를 바탕으로 로컬 SSMS에서 접속함.
| 항목 | 값 |
|---|---|
| 서버 유형 | 데이터베이스 엔진 |
| 서버 이름 | 20.200.136.216,1433 (본인의 VM IP 입력) |
| 인증 | SQL Server 인증 |
| 로그인 | ** |
| 암호 | **** |
SSMS에 접속하여 시딩된 데이터를 확인함.
USE CarMarket;
GO
-- 1. 테이블 목록 확인
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
-- 2. 차량 매물 확인
SELECT c.Brand, c.Model, c.Year,
FORMAT(c.Price, 'N0') AS Price,
FORMAT(c.Mileage, 'N0') AS Mileage,
u.Name AS Seller
FROM Cars c
JOIN Users u ON c.SellerId = u.UserId
ORDER BY c.Price DESC;
-- 3. 인덱스 확인
SELECT i.name AS IndexName,
t.name AS TableName,
COL_NAME(ic.object_id, ic.column_id) AS ColumnName
FROM sys.indexes i
JOIN sys.tables t ON i.object_id = t.object_id
JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE i.name LIKE 'IX_%';
브라우저에서 http://<VM_PUBLIC_IP>:5000/ 에 접속하여 CarMarket 웹 UI가 표시되는지 확인. 터미널에서 API 테스트도 진행함.
# 환경 변수 로드
source ~/.carmarket-env
# 헬스체크
curl http://$PUBIP:5000/health
# 차량 목록 조회
curl http://$PUBIP:5000/api/cars
# 브랜드 필터 적용 조회
curl "http://$PUBIP:5000/api/cars?brand=BMW"
학습 편의를 위해 1433 포트를 외부에 직접 오픈했으나, 프로덕션 환경에서는 절대 금지해야 함. 실무 권장 방법:
ssh -L 1433:localhost:1433 azureuser@<PUBIP> 연결 후 SSMS 접속| 방식 | 적합한 경우 | 다운타임 |
|---|---|---|
| sqlcmd 직접 (본 실습) | 소규모 DB, 학습용 | 짧음 (분 단위) |
| bacpac export/import | 중소규모, 일회성 마이그레이션 | 중간 (DB 크기에 비례) |
| Azure DMS (온라인) | 대규모, 프로덕션 | 최소화 (거의 없음) |
본 실습은 sqlcmd 직접 방식을 기본으로 하며, 선택적으로 Azure DMS 리소스를 생성함.
. C:\Users\jaeky\.carmarket-env.ps1
.\02-migrate-to-azure-sql 1.ps1
💡 스크립트 수행 내역:
1. Azure SQL Server(논리 서버) 및 Database(S0) 생성
2. 방화벽 규칙 설정 (VM IP, 로컬 IP, Azure 서비스 허용)
3. VM SQL Server에서 스키마 및 데이터를 SQL 스크립트로 추출
4. Azure SQL Database에 스키마/시드 데이터 적용
5. Azure DMS 리소스 생성 (선택)
6. 소스 ↔ 대상 데이터 일치 검증
(마이그레이션 오류 발생 시 롤백 및 재진행 명령어)
.\99-cleanup.ps1
& ".\01-setup-vm-sqlserver 2.ps1"
. C:\Users\jaeky\.carmarket-env.ps1
& ".\02-migrate-to-azure-sql 2.ps1"
| 항목 | 값 |
|---|---|
| 서버 이름 | sql-carmarket-vcoaq.database.windows.net |
| 인증 | SQL Server 인증 |
| 로그인 | sqladmin |
| 암호 | **** |
-- 1. 테이블 구조 비교
SELECT TABLE_NAME,
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = t.TABLE_NAME) AS ColumnCount
FROM INFORMATION_SCHEMA.TABLES t
WHERE TABLE_TYPE = 'BASE TABLE'
ORDER BY TABLE_NAME;
-- 2. 행 수 비교 (소스와 동일해야 함)
SELECT 'Users' AS TableName, COUNT(*) AS RowCount FROM Users
UNION ALL
SELECT 'Cars', COUNT(*) FROM Cars
UNION ALL
SELECT 'Inquiries', COUNT(*) FROM Inquiries;
-- 3. 데이터 내용 확인
SELECT c.Brand, c.Model, c.Year,
FORMAT(c.Price, 'N0') AS Price,
u.Name AS Seller
FROM Cars c
JOIN Users u ON c.SellerId = u.UserId
ORDER BY c.Price DESC;
-- 4. Azure SQL 특유 정보 확인
SELECT @@VERSION AS SQLVersion,
DB_NAME() AS DatabaseName,
DATABASEPROPERTYEX(DB_NAME(), 'Edition') AS Edition,
DATABASEPROPERTYEX(DB_NAME(), 'ServiceObjective') AS ServiceTier;
| 항목 | VM SQL Server (IaaS) | Azure SQL Database (PaaS) |
|---|---|---|
| @@VERSION | SQL Server 2025 CU… | Microsoft SQL Azure… |
| Edition | Developer | SQL Azure |
| 서버 관리 | 사용자 관리 (패치, 백업, HA) | Microsoft 관리 (자동) |
| sys.databases | master, CarMarket 등 전체 표시 | CarMarket만 표시 |
| SQL Agent | 사용 가능 | 없음 (Elastic Jobs로 대체) |
| 가격 모델 | VM 시간 과금 | DTU/vCore 과금 |
| 백업 | 수동 구성 | 자동 (7~35일 보존) |
| 고가용성 | 수동 Always On 구성 | 기본 내장 (99.99% SLA) |
로컬에서 제공된 쉘 스크립트(03-switch-app-to-azure-sql.sh 또는 .ps1)를 실행하거나, VM 내부에 SSH로 접속하여 수동으로 수정함.
# VM 내부 수동 전환 방법
ssh azureuser@20.41.98.214 # 본인 VM IP로 변경
cd ~/sqlvm_usedcar/app
# 1. 현재 DB_USER 확인
grep 'DB_USER' app.py
# 2. app.py 수정 (환경변수에서 읽도록 변경)
sed -i 's/DB_USER = "sa"/DB_USER = os.environ.get("DB_USER", "sa")/' app.py
# 3. .env 확인 및 추가
cat .env
grep -q 'DB_USER' .env || echo 'DB_USER=sqladmin' >> .env
# 4. 서비스 재시작 및 헬스체크 확인
sudo systemctl restart carmarket
curl -s http://localhost:5000/health
# 헬스체크 (db: connected 확인)
curl http://$PUBIP:5000/health
# 차량 목록 (Azure SQL에서 조회)
curl http://$PUBIP:5000/api/cars
# 매물 등록 테스트 (Azure SQL에 INSERT)
curl -X POST http://$PUBIP:5000/api/cars \
-H "Content-Type: application/json" \
-d '{"seller_id":1,"brand":"기아","model":"카니발","year":2023,"price":38000000,"mileage":10000}'
연결에 문제가 발생할 경우 백업된 환경설정 파일을 복원하여 VM으로 즉시 롤백 가능함.
ssh azureuser@$PUBIP
cp ~/sqlvm_usedcar/app/.env.vm-backup ~/sqlvm_usedcar/app/.env
sudo systemctl restart carmarket
비용 발생을 막기 위해 실습 종료 후 반드시 리소스를 정리함.
source ~/.carmarket-env
# 방법 1: VM만 중지 (데이터 유지, 비용 중단)
bash scripts/99-cleanup.sh
# 방법 2: 전체 삭제 (복구 불가)
bash scripts/99-cleanup.sh --delete
migration-lab/
├── Migration_Lab_Guide.md ← 이 문서
└── scripts/
├── 01-setup-vm-sqlserver.sh ← VM + SQL Server 생성 및 세팅
├── 02-migrate-to-azure-sql.sh ← Azure SQL DB로 데이터 마이그레이션
├── 03-switch-app-to-azure-sql.sh ← Flask 앱 DB 연결 전환
└── 99-cleanup.sh ← 리소스 정리
1. SSMS에서 VM SQL Server 접속 실패
# NSG 1433 포트 오픈 확인
az network nsg rule list -g $RG --nsg-name <NSG_NAME> -o table
# SQL Server 바인딩 포트 확인 (0.0.0.0인지 검증)
ssh azureuser@$PUBIP "sudo ss -tlnp | grep 1433"
# 바인딩 문제 수정 및 재시작
sudo /opt/mssql/bin/mssql-conf set network.ipaddress 0.0.0.0 && sudo systemctl restart mssql-server
(주의: SSMS 연결 시 "서버 인증서 신뢰" 옵션 체크 필수)
2. Azure SQL Database 방화벽 오류
에러: Client with IP address 'x.x.x.x' is not allowed...
# 해결: 클라이언트 IP 허용 추가
az sql server firewall-rule create \
-g $RG -s $SQL_SERVER_NAME \
-n AllowMyNewIP \
--start-ip-address <YOUR_IP> --end-ip-address <YOUR_IP>
3. Flask 앱 Azure SQL 연결 실패
# .env 설정 확인
ssh azureuser@$PUBIP "cat ~/sqlvm_usedcar/app/.env"
# VM에서 Azure SQL 연결 테스트
sqlcmd -S $SQL_FQDN -U $SQL_ADMIN -P "$SQL_ADMIN_PASSWORD" -d CarMarket -Q "SELECT 1"
# ODBC Driver 존재 확인
odbcinst -q -d
4. DMS 마이그레이션 실패
0.0.0.0으로 바인딩되어 있는지 확인[https://portal.azure.com](https://portal.azure.com)에 접속 및 로그인.AzureSQLServerVMStandard_D2s_v5 (2 vCPU, 8GiB)3dt026 / 암호: 3dt026@sqlvm (메모 필수)D:\).✅ Azure Cloud Shell에서 계정 정보 확인
az account show --output table
# 출력 결과 (예시)
EnvironmentName HomeTenantId IsDefault Name State TenantId
AzureCloud 5fb256f0-fbf2-40d2-81d5-bac1b32c419d True 대한상공회의소 Data School Enabled 5fb256f0-fbf2-40d2-81d5-bac1b32c419d
.rdp 파일 실행.3dt026 / 3dt026@sqlvm) 입력 후 인증서 경고에서 [예] 클릭.🧹 Windows VM 정리 단계
사용하지 않을 때는 VM을 중지(할당 취소)하여 비용 발생을 방지하거나, 리소스 그룹 단위로 완전 삭제를 권장함.</VM_PUBLIC_IP>