LoginSignup
6
10

More than 3 years have passed since last update.

Python製WebアプリケーションをDockerで運用する

Last updated at Posted at 2020-08-29

前回:EmbyをDockerで運用する
これはそろそろDockerを使えるようになりたい筆者の試行錯誤の軌跡です。

Python製WebアプリケーションをDockerで

前回はメディアストリーミングサービスのEmbyをコンテナに移行しました。
Untitled(11).png

今回はPython製Webアプリケーションを展開できるDockerイメージを作り、
Web Socket使用のアプリケーションとDjangoアプリケーションをDockerに移行します。
ついでに静的コンテンツもコンテナに移動し、VMサーバーを閉じます。
Untitled(12).png

静的コンテンツ

VMサーバーにあったものをDockerホストに移動し、コンテナ起動時に配信ディレクトリへバインドしました。

docker-compose.yml
    deploy:
        image: nginx
        restart: always
        ports:
            - 80:80
            - 443:443
        volumes:
            - /home/fclef/docker/server/deploy/nginx/conf:/etc/nginx/conf.d
+           - /home/fclef/docker/server/deploy/content/html:/usr/share/nginx/html

Python製アプリケーションのDocker運用

Web Socket使用アプリケーションもPython製(bottle)なので、Python製アプリケーションを簡単に展開できるコンテナを目指します。

コンテナ設計

僕はよくPythonでWebアプリケーションを作ります。
なので、アプリケーションごとイメージに固めてしまうというよりは、
どんなPythonアプリケーションでも動かせる土台をイメージ化し、
具体的なアプリケーション自体はgitの情報をコンテナ起動時に渡して、コンテナ内でクローン、展開させます。
Untitled(9) (1).png

Dockerfile

僕がPythonで何かを作るときは必ずPipenvを使います。
また、Webアプリケーションのときはソケット化してnginxで配信するので、
イメージの時点でPipenv, nginxをインストールしておきます。

Dockerfile
FROM python:3.8-slim-buster

# 依存パッケージのインストール
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get install -y git make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libs
qlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev nginx

# Pyenvのインストール
RUN git clone https://github.com/yyuu/pyenv.git /.pyenv
ENV PYENV_ROOT /.pyenv
ENV PATH $PYENV_ROOT/bin:$PATH
RUN pyenv init - | bash
# Pipenvのインストール
RUN pip install pipenv
ENV PIPENV_VENV_IN_PROJECT 1
ENV PIPENV_YES true

RUN mkdir /app # アプリケーション配備ディレクトリ
RUN mkdir /script # 各種スクリプト配置ディレクトリ

COPY up.sh /script # 起動スクリプト

# 起動スクリプト内でgitプロジェクトをappにクローンするので移動しておく
WORKDIR /app
# Webアプリケーションの配信はメインWebサーバを介するリバースプロキシなので、80ポートだけ空けておく
EXPOSE 80
RUN chmod +x /script/up.sh
ENTRYPOINT [ "/script/up.sh" ] # コンテナ起動時、起動スクリプトup.shを実行する

コンテナ起動スクリプト

アプリケーションのgit情報はコンテナ起動時に環境変数で渡します。
僕の環境ではなぜかsshでのgitのクローンができないため、httpsでクローンする前提です。
クローンしたら、プロジェクトルートディレクトリにあるPipfileを解析し、必要なPythonバージョンを決定します。
Pythonライブラリの中には、依存するプログラムがインストールされている必要があるケースがあるので、
それらをインストールするためのスクリプト(dependencies.sh)をコンテナ起動時にバインドしておき、ここで呼び出します。
Python製Webアプリケーションのソケット化はgunicornを使うので、gunicornもインストールしておきます。

up.sh
#!/bin/bash

# 環境変数からgit情報を取得する
gitCloneUrl=${GIT_CLONE_URL}
gitUserName=${GIT_USER_NAME}
gitPassword=${GIT_PASSWORD}
gitCloneTarget=`echo ${gitCloneUrl} | sed -r "s#^(https?://)(.*)#\1${gitUserName}:${gitPassword}@\2#g
"`

# プロジェクト名を取得する
projectName=`echo ${gitCloneUrl} | sed -r "s#.*/(\w+)\.git#\1#g"`
echo "■ ■ ■ PROJECT NAME <${projectName}> ■ ■ ■"

git clone ${gitCloneTarget}

# Pipfileからpythonバージョンを取得する
cd ${projectName}
pythonVersion=`grep python_version Pipfile | sed -r 's/python_version = "(.+)"/\1/g'`
echo "■ ■ ■ PYTHON VERSION <${pythonVersion}> ■ ■ ■"

# インストールするPythonライブラリの中に他プログラムに依存するものがある場合は、ここでインストールしておく。
if [ -f /script/dependencies.sh ]; then
    source /script/dependencies.sh
fi

pipenv --python ${pythonVersion}
pipenv install
pipenv install gunicorn

curPath=`pwd`
export APP_ROOT=${curPath}

chmod +x /script/run.sh
service nginx start
/script/run.sh # アプリケーション起動スクリプト
while true; do sleep 1000; done

コンテナの起動

例として、Djangoアプリケーションを起動する構成をお見せします。

docker-compose.yml抜粋
django:
    image: pipenv-gunicorn
    restart: always
    environment:
        GIT_CLONE_URL: https://xxxxxxxx/user/project.git
        GIT_USER_NAME: user
        GIT_PASSWORD: password
    volumes:
        - /home/fclef/docker/server/app/dependencies.sh:/script/dependencies.sh
        - /home/fclef/docker/server/app/run.sh:/script/run.sh
        - /home/fclef/docker/server/app/app.conf:/etc/nginx/conf.d/nginx_gunicorn.co
nf
    depends_on:
        - deploy
        - gitlab
dependencies.sh
apt-get -y --no-install-recommends install libpq-dev
run.sh
cd /app/project

source .venv/bin/activate

python manage.py collectstatic --noinput
python manage.py makemigrations
python manage.py migrate

deactivate

/app/project/.venv/bin/gunicorn \
          --access-logfile /var/log/socket_success.log \
          --error-logfile /var/log/socket_error.log \
          --workers 1 \
          --bind unix:/run/socket.sock \
          config.wsgi:application
app.conf
server {
    listen       80;
    listen [::]:80;
    server_name xxxx.xxx.xxx;

    root /app;

    location /static {
        alias /app/project/static;
    }

    location / {
        include /etc/nginx/proxy_params;
        proxy_pass http://unix:/run/socket.sock;
    }

    location ~ ^/apple-touch-icon(.*)\.png$ {
        root /app/project/;
        rewrite ^/apple-touch-icon(.+)\.png$ /static/img/apple-touch-icon.png break;
    }
}

アプリケーション本体はコンテナ起動時にgitからクローン、
データは別コンテナのPosgreSQLに保存してあるので、
このコンテナは何度作成し直しても正常に運用状態のサービスを展開できます。

基本的に上記イメージと起動方法で大抵のPythonアプリケーションは動かせるようになりました。
gitクローンからデプロイまでの手順が単純なのでアプリケーションを外出しして再利用しやすいイメージが作成できましたが、
環境構築が複雑なアプリケーションの場合は、アプリケーションごとイメージにしてしまった方が保守性があがるかもしれません。

以上、VMサーバー群をコンテナに移行するシリーズでした。

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10