본문 바로가기

볼봇의 코딩 생활/웹 세계

[클라우드응용SW개발] 컨테이너 배포와 Azure AI 서비스

도커 이미지를 ACR에 올려 Web App으로 띄우고, 클라우드가 미리 학습해둔 AI로 Q&A 챗봇까지 만들어본 기록

 

지난 글에서 미니쿠베로 쿠버네티스를 직접 굴려봤다. 그런데 마스터와 워커 노드를 손수 세팅하는 건 실무에선 너무 번거롭다. 클라우드가 좋은 이유는 결국 이 무거운 부분을 대신 들어준다는 데 있다. 이번 주는 그 "대신 들어주는 것"을 두 방향에서 봤다. 하나는 컨테이너를 대신 호스팅해주는 것, 다른 하나는 학습된 AI 모델을 통째로 빌려주는 것. 둘 다 직접 굴려봤다.

 

AKS와 ACR — 쿠버네티스의 무게를 클라우드가

쿠버네티스 운영 부담을 클라우드가 떠안은 게 AKS(Azure Kubernetes Service)다. 고가용성·확장성·이식성이 필요한 앱을 배포·관리하기 좋고, 오픈소스 기반이라 나중에 AWS나 GCP로 옮기기도 비교적 수월하다. 핵심은 쿠버네티스 운영의 복잡성을 크게 줄여준다는 점. 노드를 탄력적으로 늘리고 줄이는 것도 서비스만 만들면 바로 된다.

그 옆에 늘 붙어 다니는 게 ACR(Azure Container Registry)다. 도커 이미지를 클라우드 안에 저장해두는 레지스트리인데, 레지스트리 자체를 클라우드에 두니 성능과 보안 이슈를 함께 푼다.

서비스역할

ACR 도커 이미지를 클라우드에 저장하는 레지스트리
AKS / Web App ACR에 저장된 이미지를 가져와 서비스로 배포

이번 실습은 AKS 대신 더 가벼운 Web App for Containers로 배포한다. 컨테이너를 레지스트리에 등록해두면 그걸 그대로 끌어와 웹 앱으로 띄워주는 관리형 서비스다. OS 패치·용량 프로비저닝·로드 밸런싱을 플랫폼이 알아서 처리하고, 트래픽에 따라 자동으로 늘었다 줄었다 한다.

Azure CLI 깔고 로그인하기

매번 포털을 클릭하는 대신 스크립트로 리소스를 다루기 위해 Azure CLI(az)를 쓴다. 가벼운 우분투 22.04 VM에 SSH로 접속해 설치했다.

# 패키지 최신화는 설치 전 루틴
sudo apt-get update
sudo apt-get upgrade -y

# 공식 원라인 설치 스크립트 (서명 키·레포 추가까지 처리)
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

az upgrade
az version          # 정상 출력되면 설치 완료

설치만으론 클라우드와 연결된 게 아니다. az login으로 붙여야 한다. VM처럼 브라우저가 없는 환경에선 디바이스 코드 방식으로 인증한다.

az login
# → 터미널에 주소와 코드가 뜬다. 브라우저로 그 주소에 접속해
#    코드를 붙여넣고 계정 인증을 마치면 연결된다.

az account list     # 구독이 여러 개면 사용할 것(예: Azure for Students) 확인

실습 큰 그림

흐름은 단순하다. 샘플 Django 앱으로 도커 이미지를 만들어 로컬 확인 → ACR을 만들어 푸시 → 그 이미지를 Web App으로 띄운다.

1. 샘플 앱 빌드 & 로컬 테스트

처음부터 코드를 짜는 시간을 아끼려고 마이크로소프트 Django 샘플을 받았다. git·docker 설치를 확인하고 클론한다.

git --version
docker version

git clone https://github.com/Azure-Samples/docker-django-webapp-linux.git
cd docker-django-webapp-linux
vi Dockerfile        # Python 3.6 기반, requirements.txt 설치, EXPOSE, init.sh 엔트리포인트

docker build -t app/svc/tutorial/custom/image .
docker images
docker run -it -p 8000:8000 app/svc/tutorial/custom/image    # Django 구동 확인
여기서 한 번 막혔다. 로컬에선 떴는데 브라우저로 http://VM공인IP:8000 접속이 안 됐다. VM의 네트워크 보안 그룹(NSG)에서 TCP 8000 포트가 막혀 있어서였다. 포털 → VM 네트워크 설정에서 인바운드 규칙에 8000을 열어주니 그제서야 샘플 사이트가 떴다.

2. 리소스 그룹 · 관리 ID · ACR 만들기

한국 중부에 리소스 그룹을 만들고, 나중에 권한 연결에 쓸 관리 ID(Managed Identity)도 미리 만들어둔다.

az group create --name app-dev --location koreacentral
az identity create --name my_id --resource-group app-dev

이미지를 저장할 ACR을 만든다. --admin-enabled true로 관리자 자격 증명을 켜두면 그걸로 이미지를 푸시할 수 있다.

az acr create --name helloaiacr001 --resource-group app-dev --sku Basic --admin-enabled true

# 접속용 username / password 확인
az acr credential show --name helloaiacr001 --resource-group app-dev
ACR 이름은 영문·숫자만 된다. 강의 예시처럼 hello_ai_acr_001같이 언더스코어를 넣으면 거부된다(필자도 처음에 이걸로 튕겼다). helloaiacr001처럼 특수문자 없이 지어야 한다.

3. 이미지에 태그 달고 ACR로 푸시

로컬 이미지를 그냥 푸시하면 로컬에만 남는다. ACR 주소(이름.azurecr.io)로 로그인하고, 그 주소에 맞춰 태그를 새로 단 뒤 푸시해야 한다.

# 2단계에서 확인한 username/password로 로그인
docker login helloaiacr001.azurecr.io -u helloaiacr001 -p [password]

# ACR 주소·경로를 포함한 새 태그 지정
docker tag app/svc/tutorial/custom/image helloaiacr001.azurecr.io/django-webapp:latest

# 클라우드 레지스트리로 푸시
docker push helloaiacr001.azurecr.io/django-webapp:latest

4. Pull 권한 주고 Web App 생성

웹 앱이 ACR에서 이미지를 가져오려면 권한이 필요하다. 만들어둔 관리 ID에 ACR Pull 권한을 준다. 이 부분은 마이크로소프트 공식 자습서 스크립트를 거의 그대로 쓰되 이름만 바꿨다.

# 관리 ID의 보안 주체 ID와 레지스트리 ID를 변수에 담기
principalId=$(az identity show --name my_id --resource-group app-dev --query principalId -o tsv)
registryId=$(az acr show --name helloaiacr001 --resource-group app-dev --query id -o tsv)

# 관리 ID에게 ACR Pull 권한 부여
az role assignment create --assignee $principalId --scope $registryId --role AcrPull

이제 웹 앱을 실행할 App Service 플랜을 리눅스 기반으로 만들고, 그 위에 ACR 이미지를 가리키는 웹 앱을 생성한다.

# 리눅스 플랜 (--is-linux 가 실행 환경·비용을 결정)
az appservice plan create --name my-plan --resource-group app-dev --is-linux --sku B1

# ACR에 푸시한 이미지로 웹 앱 생성
az webapp create --resource-group app-dev --plan my-plan --name hello-ai-webapp \
  --deployment-container-image-name helloaiacr001.azurecr.io/django-webapp:latest

# 컨테이너가 8000번을 쓰므로 포트 지정
az webapp config appsettings set --resource-group app-dev --name hello-ai-webapp \
  --settings WEBSITES_PORT=8000

# 웹 앱에 관리 ID 할당 (그 ID의 client id로 ACR pull 인증 연결)
az webapp identity assign --resource-group app-dev --name hello-ai-webapp

# CI/CD 사용 설정
az webapp deployment container config --enable-cd true \
  --resource-group app-dev --name hello-ai-webapp

흐름만 정리하면 관리 ID에 Pull 권한 → 웹 앱에 그 ID 연결 → 웹 앱이 그 권한으로 ACR 이미지를 끌어와 구동이다. 비밀번호를 코드에 박지 않고 ID 기반으로 인증하는 게 핵심이다.

5. 배포 확인

포털에서 app-dev 리소스 그룹에 관리 ID·ACR·App Service가 다 생겼는지 보고, App Service의 배포 센터로 들어간다. 단일 컨테이너로 잡혀 있는지, ACR이 제대로 연결됐는지, 인증에 쓴 관리 ID와 이미지 버전이 맞는지 점검한다. 마지막으로 App Service가 주는 기본 도메인 주소를 클릭하면, 푸시한 도커 이미지 기반 웹 서비스가 인터넷에서 도는 걸 확인할 수 있다.

인프라뿐 아니라 모델까지 빌려준다

Part 1이 "컨테이너를 대신 굴려준다"였다면, 클라우드는 한 걸음 더 나가서 이미 학습된 AI 모델도 그대로 빌려준다. 음성 인식, 번역, 이미지 분석, 생성형 AI까지 — 직접 모델을 만들 필요 없이 API만 호출하면 된다. Azure가 제공하는 AI 서비스를 한 바퀴 둘러보고, 그중 하나로 실제 고객센터 챗봇을 만들어봤다.

Azure AI 서비스 한 바퀴

Azure AI Foundry
(구 Azure OpenAI)
GPT-4o 등 LLM. 챗봇·생성형 AI를 내 앱에 연동. 학습용은 가성비 좋은 GPT-4o-mini 추천
Azure AI Language 감정 분석, 키워드 추출, 언어 감지, 그리고 Q&A(고객센터)
Azure AI Translator 텍스트·실시간 음성 번역. 빠르고 저렴(극강 품질은 GPT-4o, 일반 번역은 이쪽)
Azure AI Speech STT(받아쓰기), TTS(읽어주기), 실시간 음성 번역, 화자 인식
Azure AI Vision 객체 감지, 얼굴 인식, OCR(이미지 속 글자 추출)
Azure AI Document Intelligence 영수증·송장 등 양식 문서의 구조를 분석해 데이터 추출
Custom Vision 내 이미지를 직접 학습시켜 분류 모델 생성
Bot / Content Safety / ML / Search 챗봇 제작 / 유해 콘텐츠 감지 / 전통 ML / AI 기반 검색

이 모든 서비스가 Python·C#·Java 등 주류 언어를 다 지원해서, 익숙한 언어로 학습된 모델을 그대로 가져다 쓸 수 있다.

Custom Vision 맛보기 — 짜장면·짬뽕·탕수육

본 실습 전에 Custom Vision을 잠깐 만져봤다. 내가 가진 이미지를 학습시켜 분류기를 만드는 서비스다.

  • customvision.ai에서 프로젝트 생성. 타입은 Classification + Multiclass, 음식이라 도메인은 Food로 잡으면 인식률이 올라간다.
  • 짜장면·짬뽕·탕수육 사진을 올리고 각각 태그를 단다.
  • Train(Quick Training 기준 10~15분) → 정확도 지표 확인.
  • Quick Test: 학습에 안 쓴 새 짜장면 이미지 URL을 넣으면 "짜장면 85%"처럼 예측한다.
  • Publish하면 Prediction URL이 생기고, 이미지/URL을 보내면 분류 결과를 돌려주는 API로 앱에 붙일 수 있다.

본 실습: 고객센터 Q&A 봇 만들기

이제 Azure AI Language Service로 고객센터를 만든다. 핵심은 지식을 흡수시켜 Knowledge Base(지식 기반)를 만드는 것. PDF·Word·Excel·텍스트는 물론 웹사이트 URL도 학습 자료로 넣을 수 있다. 전체는 6단계다.

단계할 일

1~2 언어 리소스 생성 → Language Studio 프로젝트 생성
3~4 데이터 연동으로 Knowledge Base 구축 → 학습
5~6 테스트 → 배포 후 앱/웹에 통합

1. 언어 서비스 리소스 만들기

포털에서 언어서비스를 검색해 만든다. Q&A를 쓰려면 생성 시 사용자 지정 질문 답변 항목을 반드시 체크해야 한다(추가 설치되며 비용이 붙으니 테스트 후 정리 필요). 지역은 속도·비용을 생각해 East US 등으로 통일하고, 연결되는 Azure Search 가격 계층은 최소 Basic 이상을 권장한다. 책임 있는 AI 알림에 동의하고 배포한다.

2. 학습 데이터 — 서울시 FAQ

서울시 관광 정보 FAQ 웹사이트를 학습 자료로 썼다. 질문을 누르면 답이 펼쳐지는 구조이고 한 페이지에 10개 정도 Q&A가 있다. URL을 보니 끝의 페이지 번호 값만 바꾸면 1~5페이지가 나오는 패턴이라, 이걸 그대로 활용해 전체를 등록할 수 있었다.

3. Language Studio 프로젝트 생성

리소스에서 Language Studio 시작하기로 들어가, 새 프로젝트로 Custom Question Answering을 고른다. 데이터가 한국어이니 언어는 Korean, 이름은 서울-FAQ로 지정했다. 지식 기반에 없는 질문이 들어왔을 때 나갈 기본 답변(Default answer)도 미리 친절하게 적어둔다("해당 질문에 대한 답을 찾지 못했습니다. 다시 시도해 주세요." 같은 식).

4. 데이터 연동 & 지식 베이스 편집

Manage Sources → Add Source에서 URL을 추가한다. 페이지 구조 파악 방식으로 Auto Detect를 고르면, 특정 태그 없이도 AI가 어디가 질문이고 답인지 패턴으로 알아챈다. 파악한 URL 패턴으로 1~5페이지를 쭉 추가하고 Add All을 누르면 내용을 긁어와 Q&A로 분리해 저장한다.

Edit Knowledge Base에서 잘 들어왔는지 확인하고, 빠진 건 더하기 버튼으로 수동(Manual) 추가도 된다. 바꾼 뒤엔 반드시 Save Change를 눌러야 다시 학습해 반영된다.

5. 테스트 — 오탈자도 잡는다

실험 아이콘으로 테스트 창을 열어 '시티투어 버스', '할인 이벤트' 등을 물어보면 답을 찾아준다. 인상적이었던 건 '서티투어 버스'처럼 오탈자가 있어도 정확히 찾아준다는 점. 단순 키워드 일치 검색이 아니라 단어를 숫자화해 좌표계로 유사도를 비교하기 때문이다. 답을 못 찾는 질문('환승' 등)은 매뉴얼로 추가해 더 똑똑하게 만들 수 있다.

6. 배포 & 호출 정보 확보

테스트가 끝나면 Deploy knowledge base에서 Deploy 버튼 하나로 클라우드에 배포된다. Get Prediction URL을 누르면 외부에서 호출할 Prediction URL과 인증 키(Ocp-Apim-Subscription-Key)를 받을 수 있다. 이 둘이면 앱에 붙일 준비 끝이다.

Streamlit으로 웹앱 붙이기

받은 URL과 키로 파이썬 웹앱을 만들었다. UI는 Streamlit으로 빠르게 잡고, 질문은 requests로 POST 호출한다.

pip install streamlit
import streamlit as st
import requests

api_url = "https://[리소스이름].cognitiveservices.azure.com/language/:query-knowledgebases" \
          "?projectName=서울-FAQ&api-version=2021-10-01&deploymentName=production"
api_key = "[Ocp-Apim-Subscription-Key]"

def askQuestion(question):
    headers = {
        "Ocp-Apim-Subscription-Key": api_key,
        "Content-Type": "application/json",
    }
    payload = {"question": question}
    response = requests.post(api_url, headers=headers, json=payload)

    if response.status_code == 200:
        return response.json()["answers"][0]["answer"]
    return f"오류가 발생했습니다 (status: {response.status_code})"

st.title("서울 관광 FAQ 챗봇")
question = st.text_input("궁금한 점을 입력하세요")

if st.button("질문하기"):
    with st.spinner("답변을 찾는 중..."):     # 응답이 느려도 대기 상태를 알려준다
        answer = askQuestion(question)
    st.write(answer)

soulfaq.py — Prediction URL과 키만 바꾸면 그대로 동작한다. URL·페이로드 형식은 포털 샘플 리퀘스트에서 복사하는 게 정확하다.

streamlit run soulfaq.py

브라우저에 뜬 입력창에 질문을 넣고 버튼을 누르면, 우리가 학습시킨 서울시 FAQ 지식 기반에서 답을 찾아 보여준다. status_code == 200이면 JSON에서 첫 번째 답변을 꺼내 출력하고, 아니면 에러 메시지를 돌려주는 단순한 구조다. 응답이 느릴 때 st.spinner로 로딩을 보여주는 작은 처리만 더해도 체감이 확 좋아진다.

정리하면

이번 주는 클라우드가 "대신 들어주는 것"을 두 방향으로 확인했다. Part 1에서는 도커 이미지를 ACR에 올려 Web App으로 띄우는 전 과정을 az 명령으로 처리했다 — 포털 클릭 대신 스크립트로 표준화해 반복할 수 있다는 게 핵심이었다. Part 2에서는 학습된 AI를 API로 빌려와 지식 기반 챗봇을 단시간에 붙였다. 모델을 직접 만들지 않고도 고급 자연어 기능을 앱에 통합할 수 있었다.

실용적으로 가장 오래 남은 건 막혔던 지점들이다. NSG 8000 포트, 영문·숫자만 되는 ACR 이름, 그리고 관리 ID로 비밀번호 없이 ACR을 당겨오는 인증 흐름. AI 쪽에서는 오탈자도 잡아내는 좌표계 유사도 검색이 의외로 인상적이었다. 명령 옵션과 API 형식은 자주 바뀌니, 외우기보다 공식 자습서와 포털 샘플을 띄워놓고 변수만 갈아 쓰는 습관이 결국 가장 빠르고 정확하다.