Please enable JavaScript to view the comments powered by Disqus.

구글 애널리틱스 API를 사용한 Flask 앱을 uWSGI와 nginx로 배포한 과정

main 이미지 출처: Link

포스트 통계 기능을 위해 사용한 Google Analytics 서비스 API

이 블로그는 CMS 서비스나 별도의 백엔드 서비스가 연결되어 있지 않은 웹사이트라서 따로 관리하고 있는 통계 데이터가 없다. 대신 구글 애널리틱스는 처음부터 연결해 뒀기 때문에 페이지뷰를 확인하는 것은 가능하다. 저번에 블로그 검색 기능을 추가했으니, 이번에는 인기 포스트를 보여주는 기능을 추가하고 싶어서 구글 애널리틱스 Reporting API의 힘을 빌리기로 했다. 그런데… 서비스 애플리케이션으로 지원하는 언어로 Java, Python, PHP를 지원하고 자바스크립트는 없는 게 아닌가? 자바스크립트가 백엔드 언어로 활용되기 시작한 역사가 오래되지 않긴 했지만 그래도 node.js를 만든 구글에서 지원하지 않는다는 건 조금 아쉬웠다. 그래서 공부도 할겸 파이썬으로 개발을 진행하기로 했다.

서비스 애플리케이션을 위한 인증 시나리오

구글 API를 사용하기 위한 OAuth 2.0 인증 시나리오에는 서비스 애플리케이션, 설치형 앱, 웹 애플리케이션이 있다.

서비스 애플리케이션은 백엔드(내가 구현한 앱)와 백엔드(구글 애널리틱스 API) 사이에서 인증을 진행한다. 따라서 웹사이트를 사용하는 엔드 유저는 인증을 하지 않아도 된다. 이 블로그에서는 비인증 유저에게 통계 데이터를 제공해야 하므로 나는 서비스 애플리케이션에 해당하는 시나리오로 인증을 구현했다.

serviceaccount 서비스 애플리케이션 인증 시나리오

웹 애플리케이션은 엔드 유저에게 로그인을 요구한다. 이 시나리오는 소셜 로그인이나 SNS 공유를 하려고 할 때 해당 서비스에 로그인을 요구하는 사례에 해당한다.

webflow 웹 애플리케이션 인증 시나리오

그리고 나머지 하나는 설치형 애플리케이션을 위한 시나리오인데, 안드로이드, iOS 앱 및 기타 설치형 애플리케이션에 해당하는 시나리오다.

Flask 프레임워크

인기 있는 파이썬 프레임워크로는 Django, Flask가 있는데 Django는 지원하는 기능이 무척 많고 덩치가 크다. 나는 최소한의 기능만 있으면 되기에 가벼움 그 자체인 Flask를 선택했다. Flask 홈페이지에도 “Flask is a microframework for Python”이라고 소개되어 있을 정도다.

처음 시도한 프레임워크였기에 환경 설정, 에디터 설정, 패키지 관리 등 개발 외적인 작업에 들어간 시간이 많았지만 필요한 기능 자체는 시작일, 종료일과 컨텐츠 경로를 기준으로 한 페이지뷰 카운트 데이터를 가져오는 것 하나여서 아주 오랜 시간이 걸리지는 않았다. 그리고 개발을 진행하다 보니, 파이썬은 문법이 무척 간결해서 익숙해지기만 한다면 무척 사용하기 즐거울 것 같았다. 언젠가 파이썬으로 더 많은 것을 해 보고 싶어졌다.

uWSGI로 파이썬 앱 배포하기

WSGI는 Web Server Gateway Inerface의 약자로서, 웹 서버(etc. nginx)와 파이썬 웹 애플리케이션 사이의 통신을 위한 프로토콜이다. 과거에는 파이썬 앱이 웹 서버와 통신하기 위한 선택지에 제한이 있었기에 보다 다양한 방법으로 통신하기 위해 만들어졌다고 한다.

Flask로 만든 앱은 WSGI 스펙을 기반으로 한 앱이 된다. 그런데 nginx 웹 서버는 WSGI 스펙을 구현해두지 않았기 때문에 Flask 앱과 직접 통신할 수가 없다. 그래서 uWSGI 서버를 중간에 둔다. uWSGI는 WSGI 스펙을 구현한 서버인 동시에 프로토콜이며, nginx와도 통신할 수 있다. uWSGI가 nginx와 Flask 앱 중간에서 미들웨어 역할을 하게 되는 것이다.

uWSGI 엔트리 포인트 작성

기존의 앱 메인 파일 외에 uWSGI 서버 실행을 위한 엔트리 포인트 역할을 할 모듈을 별도로 추가한다.

# wsgi.py
from app import app

if __name__ == "__main__":
    app.run()

uWSGI 서버 실행

uwsgi를 설치한 후 엔트리 포인트로 서버를 구동한다.

uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app

커맨드라인으로 실행도 가능하지만 역시 설정파일을 따로 두는 것이 좋다.

# myproject.ini
[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = blogapi.sock # uWSGI가 만드는 socket 파일의 이름. nginx와 연결할 때 사용된다.
chmod-socket = 660
vacuum = true

설정파일을 만든 후에는 아래와 같은 방식으로 실행한다.

uwsgi —ini blogapi.ini

실행하면 앱을 실행한 위치에 blogapi.sock라는 이름의 소켓 파일이 만들어질 것이다.

nginx 서버에 uWSGI 소켓 연결

nginx에 가상 호스트를 추가하고 uWSGI 소켓을 연결한다.

# /etc/nginx/sites-enabled/blogapi
server {
    listen 80;
    server_name blogapi.rhostem.com;

    location / {
        # uWSGI가 만든 소켓 파일을 연결한다.
        uwsgi_pass unix:/home/ubuntu/www/blogapi/blogapi.sock;
        include uwsgi_params;
    }
}

nginx 서버를 재시작하면 이제 http://blogapi.rhostem.com을 통해 앱에 접속할 수 있다.

Flask 앱 자동 시작을 위한 시스템 서비스 등록

서버가 재시작될 때 Flask 앱도 자동으로 시작하도록 시스템 서비스 파일을 만들어서 등록하면 앱 관리가 더욱 편해진다. ubuntu의 시스템 서비스 파일은 /etc/systemd/system 폴더에 만든다.

# /etc/systemd/system/blogapi.service

[Unit]
Description=uWSGI instance to serve flask app blog-api
After=network.target

[Service]
User=ubuntu # 로그인 사용자
Group=www-data
WorkingDirectory=/home/ubuntu/www/blogapi # 실행 디렉토리
Environment="PATH=/home/ubuntu/www/blogapi/venv/bin" # 환경 변수
ExecStart=/home/ubuntu/www/blogapi/venv/bin/uwsgi --ini blogapi.ini # 실행 명령어

[Install]
WantedBy=multi-user.target # 자동 실행

서비스 실행 명령어는 아래와 같다.

sudo systemctl start blogapi
sudo systemctl enable blogapi

그 외의 작업들

이 외에도 파이썬 프로젝트를 위한 virtualenv 설정, pip로 패키지 관리하기, CORS 설정, blog.rhostem.com이 https 프로토콜을 사용하기 때문에 Certbot으로 무료 인증서 설치하기 등의 작업이 필요했다. 이번에 구현한 파이썬 서버는 아래 링크된 저장소에서 확인할 수 있다.

rhostem/blog-api: api service for blog

참고 자료