Webアプリケーション(Flask)のCI/CD環境構築 -Dockerを用いたデプロイ編-
コンテンツ
はじめに
Webアプリケーション開発を始めたいと思い、のちのちデプロイのことを考えなくて済むようにCI/CD環境を構築しました。Webアプリケーションは、Flask + uwsgi + nginxを使って開発予定で、CI/CD環境はGitlabと Dockerで構築しました。
Webアプリケーション(Flask)のCI/CD環境構築
最低限のFlaskアプリを作成
Flaskのチュートリアルを見て、ルートが /hello(http://domain or address/hello)のとき Hello, World を返すWebアプリを作成しました。appディレクトリの中の__init__.pyファイルがWebアプリの本体です。
myproject/ ├── app/ │ └── __init__.py └── tests ├── conftest.py └── test_factory.py
__init__.py
from flask import Flask # create Flask application function def create_app(test_config=None): # create and configure the app app = Flask(__name__, instance_relative_config=True) app.config.from_mapping( SECRET_KEY='dev', ) if test_config is None: # load the instance config (not testing) app.config.from_pyfile('config.py', silent=True) else: # load the instance test config app.config.from_mapping(test_config) # hello page @app.route('/hello') def hello(): return 'Hello, World!' return app # Flask application instance application = create_app()
同時に最低限のテストも作成しました。Flaskのチュートリアルに従ってpytestを用いました。
conftest.py
import pytest from app import create_app @pytest.fixture def app(): app = create_app({ 'TESTING': True, }) return app @pytest.fixture def client(app): return app.test_client()
test_factory.py
from app import create_app def test_config(): assert not create_app().testing assert create_app({'TESTING': True}).testing def test_hello(client): response = client.get('/hello') assert response.data == b'Hello, World!'
配布パッケージの作成
デプロイ時にリポジトリをgit clone
して必要なファイルをそろえてもよかったのですが、Flaskのチュートリアルでは配布パッケージにしていたのでそれにならって配布パッケージにしてそれをもとにデプロイしようと思いました。
setup.py
from setuptools import find_packages, setup
setup(
name='app',
version='0.0.0',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=[
'flask',
],
)
Dockerを使ってとりあえずローカルで動かす
WebアプリのFlaskとWebサーバのNginxの間には、uwsgiをインターフェースとして使用します。Nginxとuwsgiは、unixドメインソケットで接続します。以下がイメージです。
Webアプリコンテナのイメージを作成するDcoekrfileをdeploy配下に作成しました。requirements.txt, uwsgi.iniとapp-0.0.0-py3-none-any.whlをイメージに含め、必要なインストールをした後に、uwsgiコマンドを実行するようになっています。元のイメージは公式のpythonを使用しました。
myproject/ ├── app/ ├── tests/ ├── deploy/ │ ├── Dockerfile │ ├── nginx.conf │ ├── requirements.txt │ └── uwsgi.ini ├── dist/ │ └── app-0.0.0-py3-none-any.whl ├── docker-compose.yml
Dockerfile
FROM python:3.9.7
WORKDIR /var/www/
ENV FLASK_APP=app
# コンテキスト(buildコマンドを実行したカレントディレクトリ)がプロジェクトディレクトリ(このファイルのディレクトリの親ディレクトリ)
COPY deploy/requirements.txt ./
COPY deploy/uwsgi.ini ./
COPY /dist/app-0.0.0-py3-none-any.whl ./
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install ./app-0.0.0-py3-none-any.whl
CMD ["uwsgi","--ini","./uwsgi.ini"]
requirements.txt
Flask
uwsgi
uwsgi.ini
[uwsgi]
module = app:application
master = true
socket = /tmp/uwsgi.sock
chmod-socket = 666
logto = /var/log/uwsgi.log
WebアプリのコンテナとWebサーバのコンテナを立ち上げるdocker-compose.ymlを作成しました。
docker-compose.yml
version: "3"
services:
app:
build:
context: .
dockerfile: deploy/Dockerfile
volumes:
- socket:/tmp
web:
image: nginx:latest
ports:
- "10080:80"
volumes:
- socket:/tmp
volumes:
socket:
docker compose コマンドで立ち上げた Webサーバコンテナにアタッチして /etc/nginx/conf.d/default.conf の location / の設定を以下のように書き換えて nginx のサービスをリロードします(service nginx reload
)。これでローカルでの環境でデプロイできるようになりました(Dockerのインストールが必要)。ただ、コーディングのデバッグのときには Flask の組み込みのサーバを用います。
Webサーバコンテナ /etc/nginx/conf.d/default.conf
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
実行例
Windows の docker engine で起動したコンテナに localhost:10080 でアクセスできなかったので、Webサーバコンテナ内で curl コマンドを実行してみました。