CI/CD を知った開発チームにとっては、デプロイも時間のかかるめんどうなプロセスに見えてくるものです。 CI/CD の第一歩が継続的なテストとすれば、デプロイを自動化する方法は CI/CD のレベル 2 と言えます。

継続的デプロイメントによってリリースまでの時間を短縮することで、アプリケーションの品質向上に多くの時間を掛けられるようになります。 このチュートリアルでは、自動化された継続的デプロイメントによって、Flask アプリケーションを PythonAnywhere にデプロイする方法をご紹介します。 このチュートリアルを完了することで、CI/CD の実践者として “1 つ上” のレベルに到達できることでしょう。

前提条件

本チュートリアルを始めるには、次の準備が必要です。

本チュートリアルはさまざまな CI/CD プラットフォームに適用できますが、例として CircleCI を使用しています。 CircleCI アカウントをお持ちでない場合は、こちらから無料アカウントを作成してください

このチュートリアルでは、サンプルアプリケーションとして Flask API を使用します。 この Flask アプリケーションでは、ブックの作成と取得に使用する Swagger API を公開します。 次の GitHub リポジトリをクローンして、アプリケーションのソースコードを取得してください。 git clone git@github.com:mwaz/automating-flask-deployments-with-pythonanywhere.git

クローンしたリポジトリは、後で説明する PythonAnywhere の設定時にのみ使用します。

PythonAnywhere とは?

PythonAnywhere は、AWS、Azure、Google Cloud、Heroku などに代わりうるホスティングプラットフォームです。 クラウドベースで使いやすく、オンラインでの開発とデプロイに便利です。

PythonAnywhere をセットアップする

PythonAnywhere は、初めてでも簡単にセットアップを行えます。 このプラットフォームには、次の特長があります。

  • Python ライブラリがプリインストールされている
  • 1 つのアプリケーションに複数の環境を設定できる
  • スケジュール実行タスクがサポートされているので、定期的なデータベースのクリーンアップや、cron ジョブを使用した反復タスクが可能

登録した PythonAnywhere の Hacker アカウントにログインし、web タブを開いて新しい webappを作成します。 今回のプロジェクトでは、Manual configuration (手動で設定) オプションを使用します。 これにより、仮想環境を設定し、プロジェクト固有の追加ライブラリをインストールできるようになります。

PythonAnywhere で作成するWebアプリ

セットアップが完了すると、Web アプリケーションのダッシュボードにリダイレクトされます。 このダッシュボードのコンソールインターフェイスで、アプリケーションをさらに細かく設定できます。

PythonAnywhere アプリケーションダッシュボード

コンソールから、アプリケーション用の仮想環境を作成しましょう。Python バージョンを宣言できるので、3.9 を指定します。 以下のコマンドを実行します。

mkvirtualenv flask-venv --python=’/usr/bin/python3.9

PythonAnywhere コンソールにアプリケーションをクローンする

次に、GitHub から PythonAnywhere コンソールにアプリケーションをクローンします。 これは重要なステップです。なぜなら、後で GitHub から変更をリモートでプルするために使用する pushpull のリンクも、この手順で設定されるからです。 コンソールで次のコマンドを実行します。

git clone https://github.com/mwaz/automating-flask-deployments-with-pythonanywhere.git

依存関係をインストールする

cd automating-flask-deployments-with-pythonanywhere コマンドを実行して、ディレクトリを移動します。その後、pip install pipenv コマンドで pipenv パッケージマネージャーをインストールします。

pipenv install コマンドで、すべての依存関係をインストールします。

Flask アプリケーションをクローンしたら、プロジェクトの場所を PythonAnywhere に知らせる必要があります。 そのために、Web タブを使用して場所を設定します。 bash コンソール (右端のハンバーガーメニュー) を開いたまま Web タブを開き、プロジェクトのソースコードのパス、仮想環境のパス、および Swagger UI の YAML ファイルを含む静的ファイルを設定します。

コンフィグ コード リポジトリ

次のようにソースコードのパスを /home/<ユーザー名>/<プロジェクト名> に設定します。

仮想環境を作成すると、その環境でコンソールを開けるようになりますが このチュートリアルでは無視して構いません。

注: 作業ディレクトリのパスを取得するには、bash コマンド $pwd で親ディレクトリを確認します。

順調ですね。 Flask プロジェクトのセットアップが完了し、PythonAnywhere で正常にホストできました。 Web アプリケーションへのリンクを開いて PythonAnywhere のデフォルト画面を確認しましょう。

PythonAnywhere アプリページ

とは言え、まだいくつかのステップが残っています。

現在、アプリケーションのページには、PythonAnywhere のデフォルトのウェルカムメッセージが表示されています。 Flask アプリケーションを開くには、PythonAnywhere の設定ファイルにいくつかの変更を加える必要があります。

ダッシュボードから WSGI 設定ファイルを開き、設定を変更します。

WSGI ロケーション

WSGI (Web Server Gateway Interface) は、Web サーバー用の Python インターフェイスです。 この設定ファイルに Python コードを記述することで、コードをサーバー上で実行できます。このファイルで Web サーバーを設定します。 WSGI によって、Web サーバーから Flask バックエンドへ、また Web サーバーからリクエスト元へリクエストを転送できます。

Flask アプリケーションの実行を有効にするために、設定ファイルから hello world コードを削除しましょう。

その後、プロジェクトディレクトリへのパスとアプリケーションのエントリポイントを設定します。

import sys

# sys.path にプロジェクトディレクトリを追加する
project_home = '/home/waweru/automating-flask-deployments-with-pythonanywhere'
if project_home not in sys.path:
    sys.path = [プロジェクトのホームディレクトリ] + sys.path

# Flask アプリをインポートする (WSGI を動作させるには "application" いう名前にする必要がある)
from run import app as application

変更を保存してファイルを閉じます。 次に、アプリをリロードし、URL に api-docs というサフィックスを追加します。 これで、Flask Swagger のドキュメントページが読み込まれます。

 Swagger ページ PythonAnywhere

これで、PythonAnywhere への Flask アプリケーションのデプロイが完了しました。

次は、メインブランチへのデプロイのたびに CircleCI でアプリケーションが自動的にデプロイされるように、設定を行いましょう。

CircleCI との連携をセットアップする

このプロジェクトの CI/CD を設定するために、SSH を使用して PythonAnywhere サーバーに接続します。 これにより、CircleCI の実行後にパイプライン実行が成功した場合に、GitHub のメインブランチから最新の変更をプルできるようになります。

注: SSH は、ネットワークサービスを使用して、安全でないネットワーク経由で安全に接続するためのネットワークプロトコルです。Secure Shell または Secure Socket Shell とも呼ばれることがあります。 SSH はリモートコマンドラインでのログインや、コマンドのリモート実行などに使用されています。

次の図に、CircleCI および PythonAnywhere と SSH を使用して CI/CD を実装するしくみを示します。

 SSH CircleCI

図に示したように、コードを GitHub にプッシュすると、PythonAnywhere へのコードのデプロイが開始されす。 デプロイの完了後、PythonAnywhere サーバーに接続して、メインブランチから最新のコードをプルできます。 しくみがわかったところで、次はこのしくみを実現する CircleCI の設定ファイルを記述しましょう。

ダッシュボードで CircleCI を初期化する

CircleCI でプロジェクトを設定するために、CircleCI ダッシュボードにアクセスします。 Projects (プロジェクト) セクションに移動します。 お使いの GitHub ユーザー名または組織に関連付けられているすべての GitHub リポジトリが一覧表示されます。 このチュートリアルでは、automating-flask-deployments-with-pythonanywhere をリポジトリとして設定します。

Projects (プロジェクト) ダッシュボードで、使用するリポジトリの Set Up Project (プロジェクトのセットアップ) を選択します。 既存の設定ファイルを使用するオプション (Fastest (最速)) を選択します。 これで、最初の設定手順は完了です。

注: この手順は、リポジトリをすでにクローンしている場合は必須ではありません。ただし、実際のプロジェクトでリポジトリを設定する方法を学ぶうえで重要です。

まず、ルートディレクトリに .circleci ディレクトリを作成し、config.yml ファイルを追加します。 この設定ファイルに、すべてのプロジェクト用の CircleCI 設定を記述します。 設定の完了後、設定ファイルで CircleCI Orb を使用し Python API テストを実行できるようになります。

CircleCI をセットアップする

CircleCI 設定ファイルを編集し、テストの実行とアプリケーション PythonAnywhere へのアプリケーションのデプロイを行うように設定しましょう。 .circleci/config.yaml ファイルに、次のように入力します。

version: 2.1
orbs:
  python: circleci/python@1.2

workflows:
  build-app-with-test:
    jobs:
      - build-and-test
      - deploy:
          requires:
            - build-and-test
jobs:
  build-and-test:
    docker:
      - image: cimg/python:3.9
    steps:
      - checkout
      - run:
          name: 依存関係のインストール
          command: |
            pipenv --three
            pipenv install
      - run:
          name: テストの実行
          command: pipenv run pytest
  deploy:
    docker:
      - image: cimg/python:3.9
    steps:
      - checkout
      - run:
          name: SSH 経由のデプロイ
          command: |
            ssh-keyscan -H ssh.pythonanywhere.com >> ~/.ssh/known_hosts
            ssh $SSH_USER@$SSH_HOST "cd automating-flask-deployments-with-pythonanywhere; git pull";

上記の CircleCI 設定ファイルでは、2 つのジョブを作成しています。 最初のジョブは、依存関係のインストールとテストを行う build-and-test ジョブです。 次の deploy ジョブでは、PythonAnywhere へのアプリケーションのデプロイのみを実行します。 また、テストが成功した場合にのみデプロイが実行されるように、build-and-test ジョブの成功後に deploy ジョブを実行するように設定しています。

jobs:
  - build-and-test
  - deploy:
      requires:
        - build-and-test

テストに合格すれば、deploy ジョブでアプリケーションを PythonAnywhere にデプロイできます。 ただし、アプリケーションをデプロイするには、PythonAnywhere アカウントで SSH キーを生成し、GitHub アカウントに追加する必要があります。

SSH キーを生成する

PythonAnywhere への接続で、毎回パスワードを使うのは手間がかかります。 パスワードを毎回入力せずに PythonAnywhere に接続できるようにするために、PythonAnywhere コンソールで SSH キーを生成しましょう。 PythonAnywhere コンソールで次のコマンドを実行します。

ssh-keygen -t rsa -b 2048

セキュリティを強化するためにパスフレーズを入力するように求められますが、 空白のままにしてもかまいません。

 Generating public/プライベートキー

これでキーの生成は完了です。 今後は、CircleCI でキーを使用して PythonAnywhere に自動で接続できます。 これについては、次のセクションで説明します。

ただしその前に、パブリックキーが認証済みであることを PythonAnywhere に認識させる必要があります。 PythonAnywhere コンソールで次のコマンドを実行し、~/.ssh/authorized_keys にパブリックキーを追加します。

ssh-copy-id waweru@ssh.pythonanywhere.com

プロンプトが表示されたら、パスワードを入力してください。 認証が完了すると、以後はパスワードを使用せずに PythonAnywhere に接続できるようになります。 とても便利ですね。 では、PythonAnywhere コンソールで次のコマンドを実行して、テストしましょう。

ssh -i ~/.ssh/id_rsa_pub waweru@ssh.pythonanywhere.com

パスワードを入力せずにコンソールから PythonAnywhere サーバーにログインできれば、テストは成功です。 次は CircleCI に SSH キーを追加しましょう。

CircleCI に SSH キーを追加する

前のセクションの手順で、PythonAnywhere アカウント用のプライベートキーとパブリックキーを生成できました。 今後は、CircleCI にプライベートキーをコピーしましょう。

サーバーのオーバーヘッドを避けるため、プライベートキーを表示してクリップボードにコピーします。 PythonAnywhere コンソールで次のコマンドを入力します。

 cat ~/.ssh/id_rsa

 Generating public/プライベートキー

プライベートキーをクリップボードにコピーしたら、CircleCI プロジェクトの [Project Settings (プロジェクト設定)] に移動し、プライベートキーを追加します。

プライベートキーの追加

CircleCI を使用してデプロイする

CircleCI へのデプロイに必要な基本の準備が完了しました。 CircleCI config.yml ファイル内にある SSH 経由のデプロイステップに移動しましょう。 ここで、キースキャン コマンド (ssh-keyscan -H ssh.pythonanywhere.com >> ~/.ssh/known_hosts) を使用します。 これにより、PythonAnywhere アカウント用の SSH キーがフェッチされ、deploy ジョブのランナーの ~/.ssh/known_hosts ファイルに追加されます。

さらに、以下のコマンドを実行して、SSH を使用して認証を行い GitHub からコードをプルします。

ssh $SSH_USER@$SSH_HOST "cd automating-flask-deployments-with-pythonanywhere; git pull";

$SSH_USER 変数と $SSH_HOST 変数を使用することで、SSH ユーザーとホスト名をマスクしながら認証を行っていることに注目してください。 これを行うには、CircleCI の設定ページの Environment Variables (環境変数)SSH_USERSSH_HOST を追加します。 変数の追加の詳細については、こちらを参照してください。

 ホストとユーザー

SSH_HOST は SSH 経由での PythonAnywhere 上のアクセス先であり、ssh.pythonanywhere.com を指定します。 SSH_USER には、お使いの PythonAnywhere アカウントのユーザー名を指定します。

これで、CircleCI を使用してアプリケーションを PythonAnywhere にデプロイできるようになりました。

 Sequential runs

すでに説明したように、deploy ジョブは build-and-test ジョブの後に実行されます。 これは、deploy ジョブの実行条件として、build-and-test ジョブの成功を設定しているからです。build-and-test ジョブに失敗すると、deploy ジョブも失敗します。

 PythonAnywhere ジョブの成功

PythonAnywhere でアプリケーションをリロードする

PythonAnywhere にアプリケーションが正常にデプロイされたことを確認するには、PythonAnywhere ダッシュボードを使用して手動でリロードする必要がありますが、 この作業は、PythonAnywhere アカウント内に bash スクリプトを作成することで省略できます。 つまり、スクリプトにより、デプロイ後にアプリケーションを自動的にリロードするのです。 このスクリプトは reload.sh という名前にしましょう。スクリプトに、<ユーザー名>_pythonanywhere_com_wsgi.py ファイルを再作成 (更新) するコマンドを追加します。 以下のコマンドで、アプリケーションのリロードを制御します。

#!/bin/bash
touch var/www/<ユーザー名>_pythonanywhere_com_wsgi.py

ルートフォルダーの reload.sh にスクリプトを追加したら、PythonAnywhere コンソールでスクリプトを実行可能にします。 コンソールで次のコマンドを実行します。

chmod +x reload.sh

このコマンドにより、PythonAnywhere で変更が検出されるたびにスクリプトが bash で実行可能になります。 これで、デプロイを開始するたびにスクリプトが実行され、アプリケーションがリロードされるようになりました。 お疲れさまでした!PythonAnywhere でのデプロイを自動化するという目標を達成できました!

まとめ

このチュートリアルでは、デプロイ用のアプリケーションを準備して、PythonAnywhere 環境を設定し、SSH を使用してデプロイを自動化する方法を学びました。 また、PythonAnywhere へのデプロイにおける SSH キーの重要性についても学びました。 さらに、すべてのパラメーター (テストや CircleCI ジョブの成功) が満たされた場合にのみデプロイが実行されるようにアプリケーションを設定する方法も学び、CI/CD の実践的な知識を身に付けました。 最後に、デプロイするたびに PythonAnywhere で自動リロードを行う方法も学びました。 このチュートリアルが、チームの CI/CD 実践スキルをレベルアップさせるお役に立ったなら幸いです。 またお会いする日まで、良いコーディングを!


Waweru Mwaura 氏は品質工学を専門とするソフトウェア エンジニアです。生涯学習の実践者という顔も持ち、 Packt で執筆者を務めながら、工学や金融、テクノロジーの書籍を愛読しています。 Mwaura 氏の詳細な情報は、こちらの Web プロフィールをご覧ください。

さんの他の投稿を読む Waweru Mwaura