상세 컨텐츠

본문 제목

[Azure] Azure 웹앱(App Service)으로 API 서버 만들기 - 1

IT 이야기/IT 상식

by 리치윈드 - windFlex 2023. 1. 7. 13:55

본문

반응형

 

Azure 웹앱 (App Service)에 Python/Flask API 서버를 만들어 보자.

 

[요약]

  1. Python/Flask 소스 준비
  2. Azure Portal에서 App Service 생성 -> 일단 source 는 설정하지 않음
  3. App Service > 배포센터 > 코드 : Local Git으로 설정
  4. App Service > 구성 > LocalGit/FTP 인증정보 (Credential) 확인
  5. Local PC/개발PC의 Terminal에서 Git Push (인증정보와 함께)
Python/Flask는 Azure App Service Container에서 Default Setting 되어 있어서 별다른 할것이 없음 (단순함)
FastAPI 등 기본지원 하지 않는 WSGI는 추가 설정을 해야함
JAVA/Node 등과 같이 Build Process가 포함된 부분은 Build에 관련한 설정이 필요

 

 

0. [배경] - Azure 웹앱 (App Service)로 Python API 서버 만들기

AI/ML 연구하는 사람들의 90%는 Jupyter Notebook 에서 전처리/모델을 개발하고 실험 및 과제를 종료 한다고 한다. 

이렇게 하면, 자신이 개발한 모델이 우수할 지라도 어느 순간엔가 그냥 사라져 버릴 가능성이 높다. 

특히 대기업 같은 경우는, 한해 목표와 KPI를 만족 했다면 그 다음해에는 아무도 신경 쓰지 않는 경우가 많다.

 

일부, 모델이 상용화 적용되는 경우는, Web 개발 파트 혹은 아웃소싱사에서 모델을 Deploy한다. 

이러한, 이유로 정작 모델을 개발한 사람은 자신이 만든 모델이 활용되는 성취감을 100% 느끼지 못한다. 

 

필자는, 여러가지 내적 동기중에서 작은계단 (Small Step)을 하나씩 걸어올라거면서 성취감/자존감을 하나씩 쌓아 올리는 것이 매우 중요하다고 생각한다. 여러 훌륭한 연구자들 중에 이 부분이 부족하여 자신감이 결여된 인재들을 여럿 보았다. 

 

따라서, 최소의 노력으로 본인들이 개발한 모델/모듈/API를 배포해 보는 것은, 자존감/동기부여/실력향상/업무 등 대부분의 측면에서 긍적적인 효과를 가져온다. 중요한 부분은 "최소한의 노력"이라는 것이다. 

 

다행하게도 요즈음 AWS/Azure/GCP 등 클라우드 환경의 일반화로 서버/네트워크 장비 등 자체구매/운영 (On-Premise) 하지 않아도 되기 때문에, 목표로 하는 최소한의 노력이라는 관점에서 매우 유용한다. 특히, PaaS 서비스 또는 Container를 지원하는 서비스를 사용하여 별다른 신경을 써 주지 않고도 운영/배포 할 수 있다. (단, 처음 학습하는것이 조금 더 시간이 걸릴것 이다. )

 


우선 가장 단순화를 하여 서비스를 Cloud에 올리는 것을 목표로 하여 보자. 

여러 클라우드 서비스가 있겠지만, 필자는 Azure를 사용할 예정이다. AWS가 좀 더 자유도가 높을 것이나, 필자가 보유한 구독이 Azure에 있기 때문에 Azure를 사용하고 있다. (필자 또한 AWS가 더 익숙하긴 하다. ㅜ_ㅜ) Azure 계정/구독이 없는 분은 무료 구독을 사용하면 된다. (약 6개월~1년정도는 사용할 수 있을것이다. )

 

Azure에는 App Service라는 웹 어플리케이션 용 PaaS를 제공한다. Java, NodeJS/Javascript, Python, Ruby, PHP 등 대부분의 웹프레임워크를 지원한다. 이 서비스를 이용하면 굳이 WEB/WAS/DB 등 서버를 별도 (VM, On-Prem.)을 생성/구축하지 않고 웹서비스를 구축할 수 있다. 클라우드 환경에서 VM을 활용해서 구축한다면, VM을 생성하고, OS설치/운영하고, OS에 필요 라이브러리를 설치/구동하고, 개발모듈을 올려서 연동하는 등 다수의 구축작업을 해야 한다. 또한 다른 구성요소(Component)간의 연동을 고려한다면 할일이 많아지고, 복잡해지는 만큼 관리요소/운영요소/장애요소 또한 많아지게 된다. Azure App Service, Azure Functions을 활용하면 이러한 부분을 간소화 해서 구축/운영할 수 있다. 

 

앞서 거론한 바와 같이, 우리는 가장 단순화하여 진행하는 것을 목표로 하고 있으니, 여러 프레임워크중 Python으로 웹프레임워크를 구축/배포해 보도록 하자.

Python은 Build 과정이 없이 구동되기 때문에 가장 단순하다. Java, Javascript 등은 Build에 관련된 별도의 yml 파일을 생성해야 된다. 어떤 라이브러리 환경에서, 빌드하고, 배포를 위한 설정들을 지정해 주어야 하므로 다소 복잡해 질 수 있다. 이런 부분은 추후 다루도록 하겠다. 

 


다음과 같은 순서로 진행하도록 한다. 

  1. Python Web Source Code 확보
  2. Azure App Service 생성
  3. Source Code 배포 (Azure Upload)
  4. 구동 확인
  • 참조 : https://learn.microsoft.com/ko-kr/azure/app-service/quickstart-python

 

1. Python Web Source Code 

본 포스팅에서는, Python Web Source Code는 각자 개발한 내용으로 사용할 수 있으나, 최대한 단순화 하기 위해서 Azure에서 제공하는 Sample을 Github에서 다운로드 받아 사용하도록 한다. 아래는 Python Web Framework인 Flask의 Hello World이다. 

해당 부분 관련하여, 이미 개발코드를 가지고 있다면, 각자 판단에 따라서 진행하면 될것이다. 그러나, 우선 가장 단순한 경우를 테스트하고 응용하는 것이 가장 쉽게 가는 길이라고 생각한다. 나중에 본인의 코드에서 에러가 발생한다면 그 차이점을 인지하여 실수를 고쳐나가기 수월하다. 

Terminal / CMD 에서 아래와 같이 `git clone <url>`으로 다운로드 받아주자.

$ git clone https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart
$ cd msdocs-python-fastapi-quickstart
$ ll

웹프레임워크 샘플 파일 (MS flask first example )

 

(소스코드 동작 확인) 로컬에서 정상 동작하는지 일단 확인을 해 보자. 

$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install -r requirements.txt
$ flask run

 

웹브라우저에서 접속해서 확인해 보자 : `http://localhost:5000/`

Flask 외에 다른 Web Framework를 사용하다면, 기본 Port가 달라질 수 있다. 예를 들어, Django, FastAPI는 기본 Port로 8000을 사용한다. 

Azure flask web sample 구동 화면 (Local 구동)

 

 

 

2. Azure App Service 생성/설정

다음으로 Azure에 App Service를 생성해 준다. App Service를 생성하는 방법은, 1) Azure Portal, 2) Azure CLI, 3) VS Code, 4) Azure SDK 등 다양하다. 다만, 각 행위자체를 GUI로 하느냐, 명령어로 하느냐, IDE로 하느냐 등만 차이가 있을 뿐이다. 본 포스팅에서는 가장 일반적인 Azure Portal (GUI)를 사용해서 진행하도록 하자. 

 

Azure Portal에 접속 (https://portal.azure.com)하여 로그인을하고, 좌측 상단 메뉴에서 "리소스 만들기" 누른다. 또는 상단에 추천메뉴에 리소스 만들기가 위치해 있다. (찾기 쉬울것이다.) 

Azure Portal 리소스 만들기

 

리소스 만들기를 실행하여, 여러가지 Azure의 지원 기능이 나온다. 그중에서 웹앱을 선택하자. 만약 여기까지 경로를 찾지 못했다면, 상단 리소스 메뉴에서 "웹앱" 또는 "App Service"를 입력하면 된다.  이제 실제 "웹앱 만들기" 페이지로 들어와 중요 정보를 몇가지 입력해야 한다. 

Azure 웹앱 (App Service) 생성 (좌), 웹앱 만들기 및 설정 (우)

 

웹앱 만들기 페이지에서 가장 중요한 설정 부분은 다음과 같다. 

  • 구독/리소스그룹 : 본인의 구독과 리소스 그룹을 선택 or 생성해 주면 된다. 
  • 웹앱 이름 : 겹치지 않도록 이름을 설정하자. 예) "azure-webapp-python-test"
    이 이름은 웹앱 생성후, "https://<웹앱이름>.azurewebsites.net"로 azure 기본 제공하는 subdomain name이 된다. 
  • 게시 방식 : 코드, docker 컨테이너, 정적웹앱. 우리는 가장 단순하게 "코드"를 선택하도록 하자. 
  • 런타임 스택 : Python 3.9를 선택. 다른버전 선택도 가능하다. 
  • 운영체제 : Linux. (Windows를 선택해도 무방하나, 본 포스팅은 Linux를 기반으로 진행한다.)
  • 지역 : 본인이 편한 위치를 선택하면 된다. Azure는 Korea Central Region이 있으므로, 이것을 선택하는 것을 추천한다. 
  • 가격정책플랜 : 우리는 테스트 해보는 것이기 때문에 기본으로 사용하자. 
  • "검토 + 만들기 " 버튼을 누른다. 유효성이 검토되면 설정에 대한 요약페이지가 출력되고, 최종적으로 하단에 "만들기" 버튼을 또 한번 눌러 주어야 한다. 이후. "...배포 진행 중"이라는 페이지가 출력 될 것이다. 2~3분 기다리면 "배포 성공" 메세지가 출력된다. 
  • "리소스 이동" 버튼을 눌러준다. 
배포 탭으로 가면, Github Actions를 설정하는 메뉴가 나온다. 그러나, 현 시점에서 Github Actions은 사용하지 않을 예정이다. 우리는 단순하게 Local Git을 사용하도록 하자. 이후에 Build가 필요한 NodeJS or JAVA 등을 사용할 때는 Github Actions이 매우 유용하다.

 

웹앱 생성완료 후 리소스 이동 (좌), 웹앱 개요 및 배포센터 (우)

 

이제 Azure 웹앱 (App Service)가 생성이 완료되었다. "리소스로 이동"하여 웹앱의 왼쪽 메뉴중 "개요"를 보면, 전반적인 웹앱의 정보가 출력된다. 여기에서 중요한 것은 우측 상단에 있는 URL, FTP/FTPS 등의 정보가 매우 중요하다. FTP를 통하여 배포할 때 사용할 수 있다. (민감정보이므로 타인에게 노출되지 않도록 한다.)

이제 우선 해야할 일을, 웹앱에 코드를 배포하는 일 이므로, 좌측에 "배포 센터"로 이동한다. (영어식 표기, Deployment Center)

본 포스팅에서는 FTP로 배포하지는 않도록한다. 이유인 즉, FTP 배포는 실제 잘 사용하지 않을 뿐만 아니라 오류/실수 발생할 때의 대책이 없기 때문에, 정말 테스트할 때 이외는 가급적 사용하지 않도록 하자. 대부분은 Git을 사용할 것이다. ML 엔지니어 입장에서 Git을 사용하는 웹(Front/Backend)개발자들과 협업하기 위해서도 기술스택을 맞추어 두는 것이 바람직 하다. 
배포센터 버튼을 누르면, 페이지가 출력될때가지 다소 시간이 걸릴 수도 있다. 

 

우리는 앞서 웹앱을 생성할 때, 설정 단계를 Skip하였기 때문에 배포센터에서 더욱 상세한 설정을 한다. 가장 중요한 대분류는 소스를 어떤 방법으로 배포할 것인가에 대한 결정이다. 실제 상용에서는 Stage->Production으로 나누어서 지속배포(CI/CD)체계를 구축할 것이나, 본 포스팅은 단순화하여, "로컬 Git"을 사용하여 배포하는 것으로 진행한다. "소스 > 로컬 Git"을 선택해 주자.  (배포 하는중...)이라는 메세지가 출력된다. (잠시 기다리자)

웹앱 (app service)의 소스 배포방식 : 로컬 Git 선택 (좌), 저장 실행(중), FTP 자격증명의 인증정보 (우)

 

저장 버튼을 누르지 않으면 아무런 일도 발생하지 않는다. 설정 저장을 하면, 웹앱 서비스 (Container)가 재시작 된다. 컨테이너가 처음부터 시작되기 때문에 반영되는데 1~2분 정도 소요가 될수 있다. 

 

설정 저장이 완료 되면, 해당 페이지에 "Git Clone URI" 정보가 출력된다. 이 URI로 우리는 `git push`를 실행하게 되니, URI을 복사해 두자.  또한, git push를 할 때 인증정보는 "FTP 자격증명" 메뉴에서 확인 할 수 있다. 해당 탭에서 GIT Clone URI를 다시 확인할 수 있으며, 사용자 이름/암호를 확인할 수 있다. (당연하겠지만 취급에 주의가 필요하다.) 향후에는 "게시 프로필 관리"를 통해서 인증이 관리되어야 할것이다. 

Local Git 설정이 완료되면, "FTP 자격증명" 메뉴의 명칭은 "로컬 Git/FTPS 자격 증명"으로 변경된다. 

여기에서 중요한점 중 하나는, 사용자 이름이다. 필자의 경우, 이 때문에 인증에 어려움을 겪었다. 사용자 이름은 기본적으로 다음과 같이 표기되어 있다. 

<웹앱 이름>\$<웹앱 이름>

실제 Git Push 할 때 인증은 "$<웹앱 이름>" 부분만 사용된다. 예를 들어 표기된 사용자 이름이 "flask-test\$flask-test"라면, 실제 사용되는 부분은 $를 포함한 "$flask-test" 부분이 해당된다. 이부분을 잘못 입력하면 인증실패로 git push가 실행되지 않는다. 패스워드의 경우 전체를 복사하여 사용하면 된다.  다음은 공식 가이드에서 제공하는 이미지 이다. 

Local Git에서 사용자 이름 영역을 조심해야 한다.

 

이제 Azure Portal에서 설정할 내용은 완료하였다. 다음은 Terminal 에서 Git push를 진행하면된다. 


본 포스팅에서는 별도 빌드 또는 추가 설정이 필요없기 때문에 "설정/구성"을 추가로 진행하지 않았다. 그러나, 상세설정 들이 필요하다면 아래처럼 "구성" 메뉴에서 "application 설정", "시작 명령어" 등을 상세 지정할 수 있다. "구성>애플리케이션 설정"은 Application 내부에서 사용할 별도의 설정을 지정할 수 있는데, 우리는 여기는 넘어가도록 하자.  FAST API 배포는 Flask 보다는 조금 더 복잡한데, 이 때 다시 한번 거론 하도록 하겠다. 

추가 설정이 필요한 "애플리케이션 설정", 일반설정 및 시작 명령 설정하는 화면의 예

 

 

3. Local Git - 소스코드 배포 

앞에서 우리는 git clone을 통하여 Azure Flask Sample 코드를 다운로드 받았다. 해당 폴더로 이동하여 Git Push를 진행하도록 하자. 이 때 <GIT URI>는 Azure Portal의 "Local Git 자격증명"에서 확인한 Git Clone URI이다. 사용자명/패스워드는 이전 절에서 거론했던 내용을 사용하자. 

다음 명령어를 실행하여 Azure에 소스코드를 배포하도록 하자.

$ git remote add azure <GIT URI>
$ git push -u azure main:master
변경 사항이 있다면, `git add` 와 `git commit` 을 통하여 리파지토리에 반영하도록 하자. 
Azure 웹앱에서 기본 구동하는 브랜치는 master이다. 이 때문에, 최근에 사용하는 기본 브랜치명 'main'과 다를 수 있는데, 이들을 매핑해 주기 위해, `git push -u azure main` 대신 `git push -u azure main:master`를 사용하였다.  만약 사용하는 브랜치가 "master"로 되어 있다면, `git push -u azure master`를 사용하면 되겠다.

 

이제 소스코드 배포를 완료 했다. 앞서 거론했든이 Python은 별다른 빌드를 하지 않기 때문에, 별다른 설정파일 없이 웹앱을 생성할 수 있다. 이제 웹앱의 "개요"페이지에서 확인할 수 있는 웹앱 URL을 확인하고, 웹브라우저로 접속해 보기 바란다.

아래와 같이 웹브라우저에서 정상 동작하는 것을 확인할 수 있다. 해당 예제에서는 Python/Jinja2템플릿을 사용하여 웹페이지 (Frontent)를 구성했으나, API서버를 구축/배포하기 위해서 굳이 웹페이지가 필요하지 않을 수 있다. Flask/FastAPI/Django 등의 프레임워크에서 해당 Rest Api를 잘 정의하여 사용하면 된다. 

Azure 배포 완료된 예 (좌), RestAPI 리스트 (중), API 구현 코드 (우)

 

 

 

기타.  Azure 웹앱의 기본 동작 

Azure 웹앱 (App Service)는 기본적으로 Docker Container로 구동한다. 동작하는 구조를 살펴보면, 막무가내식 프로세스를 외우는것보다 더 효율적 일 수 있다. 필자의 경우, Azure 웹앱 앞부분에 NginX가 구동되고 있는 것을 알지 못했다. 이 때문에, 기본적으로 제공하는 웹프레임워크 외 FastAPI 등을 배포(Deploy)할 때 애를 많이 먹었다. 

내부적으로 Azure 웹앱 (App Service)은 Oryx 라는 Container 빌드 패키지를 사용한다. oryx의 상세 파라미터는 아래를 참조 바란다. 

https://github.com/microsoft/Oryx/blob/main/doc/configuration.md


우선 Azure 웹앱이 Python 환경에서 사용하는 Docker Container구성을 살펴보면, 다음과 같은 Component로 이루어져 있다. 

Browser <-> nginx <-> /tmp/uwsgi.sock <-> uWSGI <-> Your Python app <-> Psycopg2 <-> remote PostgreSQL database
참조: https://github.com/Azure-App-Service/python

Client의 브라우저에서 우리의 앱(App)까지 도달하기 위해서 그 사이에 Nginx와, uWSGI를 거쳐야하는 구조이다. 즉, nginx 등 설정에 따라서, 우리의 APP가 영향 받을 수 있다는 것이다.

이 점을 인지하고 있어야 한다.
그러나, nginx가 동작하는 부분은 ssh서 내부를 살펴봐도 없다. 
아마도, 웹앱(App Service)지원하는 host/hypervisor에서 동작하는 듯 하다. 
다만, user가 확인할 수 있는 부분은 `netstat -an`을 실행하면, unix소켓으로 app쪽으로 포워딩 해주는 것을 확인할 수 있다. 

또한, 해당 Docker Container에 기본으로 설치된 웹프레임워크는 Flask와 Django이다. 이 두 프레임워크 외의 다른 웹프레임워크는 커스텀 설정을 해야 한다는 것이다. 

 


Azure 웹앱에서 사용하는 Python Container의 Dockerfile을 살펴 보면 다음과 같다. 이 Dockerfile을 살펴보면, 내부에서 어떤 동작을 하는지 상세하게 이해 할 수 있을 것이다. 

[Dockerfile]

FROM mcr.microsoft.com/oryx/python:3.7-20190712.5
LABEL maintainer="appsvc-images@microsoft.com"

# Web Site Home
ENV HOME_SITE "/home/site/wwwroot"

#Install system dependencies
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        openssh-server \
        vim \
        curl \
        wget \
        tcptraceroute \
    && pip install --upgrade pip \
    && pip install subprocess32 \
    && pip install gunicorn \ 
    && pip install virtualenv \
    && pip install flask 

WORKDIR ${HOME_SITE}

EXPOSE 8000
ENV PORT 8000
ENV SSH_PORT 2222

# setup SSH
RUN mkdir -p /home/LogFiles \
     && echo "root:Docker!" | chpasswd \
     && echo "cd /home" >> /etc/bash.bashrc 

COPY sshd_config /etc/ssh/
RUN mkdir -p /opt/startup
COPY init_container.sh /opt/startup/init_container.sh

# setup default site
RUN mkdir /opt/defaultsite
COPY hostingstart.html /opt/defaultsite
COPY application.py /opt/defaultsite

# configure startup
RUN chmod -R 777 /opt/startup
COPY entrypoint.py /usr/local/bin

ENTRYPOINT ["/opt/startup/init_container.sh"]

 


그렇다면, Dockerfile에 따라 Container를 실행하고 초기화하는 startup.sh을 확인해 보자.  웹앱은 지정된 프레임워크를 탑재한 Container를 하나 띄우고, 시작 설정을 실행한다. 이 부분이 Azure Portal에서 선택한 프레임워크에 따라서 결정된다. 이후 Container 시작 후에 "run"해주는 startup.sh을 실행 시켜 준다. (python의 경우) startup.sh의 내용을 잠깐 살펴 보면 아래와 같다. 들여다 보면,

[startup.sh]

cd /tmp/8dxxxxxxxxxxxxxa6

export APP_PATH="/tmp/8dxxxxxxxxxxxxxa6"
if [ -z "$HOST" ]; then
                export HOST=0.0.0.0
fi
if [ -z "$PORT" ]; then
                export PORT=80
fi

export PATH="/opt/python/3.9.15/bin:${PATH}"
echo 'export VIRTUALENVIRONMENT_PATH="/tmp/8dxxxxxxxxxxxxxa6/antenv"' >> ~/.bashrc
echo '. antenv/bin/activate' >> ~/.bashrc
PYTHON_VERSION=$(python -c "import sys; print(str(sys.version_info.major) + '.' + str(sys.version_info.minor))")
echo Using packages from virtual environment 'antenv' located at '/tmp/8daed8eb14755a6/antenv'.
export PYTHONPATH=$PYTHONPATH:"/tmp/8dxxxxxxxxxxxxxa6/antenv/lib/python$PYTHON_VERSION/site-packages"
echo "Updated PYTHONPATH to '$PYTHONPATH'"
. antenv/bin/activate
GUNICORN_CMD_ARGS="--timeout 600 --access-logfile '-' --error-logfile '-' -c /opt/startup/gunicorn.conf.py --chdir=/tmp/8dxxxxxxxxxxxxxa6" gunicorn app:app

 

 

기타.  Azure 웹앱 디버깅 

 

추가적으로 웹앱에서는 Container 내부로 직접 접근하는 수단을 몇가지 제공한다. 

  • 환경변수
  • SSH
  • Bash
  • Log

Container/Sandbox에 직접 접근할 수 있는 수단 (SSH, BASH)을 제공하고, Log 등을 확인 할 수 있다. 

 

이중에서 가장 유용한 SSH 접속은 다음 URL로 바로 접근 할 수 있으니, 유용하게 사용하기 바란다. 

https://<앱이름>.scm.azurewebsites.net/webssh/host

 

 

반응형

관련글 더보기

댓글 영역