Flaskで実践!Blue/Greenデプロイメントハンズオン

Linux,Python

ブログ運営者
さいとう

閲覧いただきありがとうございます!"さいとう"と申します。わたしは異業種・未経験からIT業界に転職し、現在インフラエンジニアとしてクラウド環境の設計や構築・運用の支援を行っています。



コードを使ってインフラストラクチャのデプロイをする手法が広まり、インフラエンジニアにもソフトウェアエンジニアに近いスキルが求められています。


わたしが関わっているプロジェクトでは、terraformとansibleでインフラ環境をデプロイしています。ミドルウェアのバージョンアップや、新機能のリリースにおいて、"Blue/Greenデプロイメント戦略“が採用されています。切り替えは運用担当であるインフラエンジニアに任されているので、Blue/Greenデプロイメント戦略を理解している必要があります


今回の記事は、Blue/Greenデプロイメントを理解するためのハンズオンを紹介します。実際に手を動かして、仕組みを理解しましょう。

↓【PR】Flaskについて学びたい場合、めちゃくちゃおすすめです。




Blue/Greenデプロイメントハンズオン

1.Blue/Greenデプロイメントとは

Blue/Greenデプロイメントは、ソフトウェアリリースを安全かつ確実に行うためのデプロイメント戦略です。この手法を使用すると、ダウンタイムを最小限に抑え、問題が発生した場合に迅速にロールバックすることができます。

Blue/Greenデプロイメントの基本原理

  • Blue環境
    現在稼働している本番環境。
    ユーザーのリクエストを処理し、アプリケーションのサービスを提供しています。

  • Green環境
    新しいバージョンのアプリケーションをデプロイしてテストするための環境。
    現在の本番環境に影響を与えることなく、コードを検証できます。


デプロイメントフロー

  • 新しいバージョンをGreen環境にデプロイ
     新しいアプリケーションバージョンをGreen環境にデプロイします。この段階では、ユーザーのトラフィックはまだBlue環境に向いています。


  • Green環境でテストと検証
     Green環境で新しいバージョンをテストし、すべてが正常に動作することを確認します。これには、自動化されたテストや手動での確認が含まれます。


  • トラフィックの切り替え
     Green環境が正常に動作することが確認されたら、ロードバランサーやリバースプロキシ(NGINXなど)の設定を変更して、ユーザーのトラフィックをGreen環境に切り替えます。これにより、Green環境が新しい本番環境となり、Blue環境は待機状態になります。


  • モニタリング
     新しい環境(Green)が正常に動作していることを監視します。問題が発生した場合は、迅速にロールバック(元のBlue環境にトラフィックを戻す)します。


  • ロールバック
     問題が発生した場合、ロードバランサーやリバースプロキシの設定を変更して、トラフィックを元のBlue環境に戻します。これにより、ユーザーは問題のない環境に戻ります。


Blue/Greenデプロイメントのメリット

  • ダウンタイムの最小化
     デプロイ中にユーザーに影響を与えないため、ダウンタイムがほとんどありません。

  • リスクの低減
     新しいバージョンを独立した環境でテストできるため、リスクを最小限に抑えられます。

  • 迅速なロールバック
     問題が発生した場合、迅速に元の環境に戻すことができます。

Blue/Greenデプロイメントについて、基本的な概要はお分かりいただけましたでしょうか。Blue/Greenデプロイメントハンズオンを実施し、理解を深めていきましょう。


2. ハンズオンガイド

Blue/Greenデプロイメントの仕組みを理解するためのハンズオンを用意しました。Flaskアプリでサービスを提供している前提で、ダウンタイムなしで新バージョンの環境に切り替えてみましょう。

前提条件

ハンズオンを実施するにあたって、知識の前提条件は以下の通りです。

・AWSを利用
・Amazon VPC
・Amazon EC2(Amazon Linux 2023 latest)
・AWS IAM
・インターネットからの通信可能な経路の許可(MyIPを許可するだけでよいです)


もしterraformを利用している場合、↓のリポジトリを利用してください。必要な環境がセットアップされます。

https://github.com/saitou-cpi/testserver



また、以下の知識があるとより理解しやすいかと思います。

・Git
・Flask
・Nginx


必要なファイルやコマンドを用意してありますので、難しいものではありません。ぜひ気軽に試してみてください。


環境のセットアップ

Blue/Greenデプロイメントを体験するための環境を用意します。コマンドを実行する箇所には、"☆"をつけてありますので、目印にしてください。

ステップ1: パッケージインストール

まずはFlaskアプリを稼働させるためのパッケージや環境を作っていきます。


サーバに接続します。

   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Sat Aug  3 08:39:24 2024 from 175.177.44.70
[ec2-user@ip-10-0-1-32 ~]$ 



☆↓必要なパッケージをインストールします。

sudo dnf install -y python3-pip git nginx
pip install virtualenv



ステップ2: ファイル配置

Flaskアプリケーションのスクリプトを用意します。今回はBlue/Greenデプロイメントの仕組みの理解がキモなので、スクリプトはわたしのリポジトリからcloneしましょう。


ディレクトリ構造は以下の通りです。

BlueGreenTest/
│
├── blue/
│   ├── app.py
│   └── run.sh
│
├── green/
│   ├── app.py
│   └── run.sh
│
├── nginx/
│   └── nginx.conf
│
├── requirements.txt
├── deploy.sh
├── rollback.sh
└── check_default_env.sh


☆↓リポジトリをcloneしてください。

git clone https://github.com/saitou-cpi/BlueGreenTest.git



今回使うファイルの確認をしておきましょう。メインとなるFlaskのapp.pyは"Hello"と返すシンプルなスクリプトになっています。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, Flask with Gunicorn and Nginx!"

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



“nginx.conf"は次の通りです。

worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    sendfile on;
    keepalive_timeout 65;

    # Adjust types_hash settings to resolve warning
    types_hash_max_size 4096;
    types_hash_bucket_size 128;

    # Define upstream servers for blue and green environments
    upstream blue {
        server 127.0.0.1:8000;
    }

    upstream green {
        server 127.0.0.1:8001;
    }

    # Map environment variable to upstream servers
    map $upstream_env $upstream_servers {
        blue blue;
        green green;
    }

    server {
        listen 80;

        server_name example.com;

        location / {
            set $upstream_env blue;  # Set default environment
            proxy_pass http://$upstream_servers;  # Use mapped upstream server
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /green {
            proxy_pass http://green/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /blue {
            proxy_pass http://blue/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Flaskにも開発用のwebサーバが実装されていますが、本番稼働での使用は非推奨です。


おすすめの構成は、Flask + Gunicorn + Nginxです。


Nginxをプロキシサーバとして外からのリクエストを処理し、Gunicornを介してFlaskアプリの到達します。Blue/Greenデプロイメントを実現するために、デフォルトにはないUpstreamセクションとMapセクションを追加し、環境切り替えを容易にしてあります。


↓Blue環境とGreen環境を切り替えるためのスクリプト“deploy.sh"です。引数に"blue"もしくは、"green"を入れることで、切り替えが可能です。

#!/bin/bash

if [ "$#" -ne 1 ]; then
    echo "Usage: $0 {blue|green}"
    exit 1
fi

DEPLOY_ENV=$1
UPSTREAM_ENV=""

if [ "$DEPLOY_ENV" == "blue" ]; then
    UPSTREAM_ENV="green"
    TARGET_DIR="./blue/"
    SERVICE_NAME="blue.service"
elif [ "$DEPLOY_ENV" == "green" ]; then
    UPSTREAM_ENV="blue"
    TARGET_DIR="./green/"
    SERVICE_NAME="green.service"
else
    echo "Invalid environment: $DEPLOY_ENV"
    echo "Usage: $0 {blue|green}"
    exit 1
fi

# Pull latest code
git pull origin main

# Apply changes to the target environment
cp -r ./new_code/* $TARGET_DIR

# Restart the target environment (assumes you are using a systemd service or similar)
sudo systemctl restart $SERVICE_NAME

# Wait for a manual confirmation before promoting to production
echo "http://example.com/$DEPLOY_ENV を確認し、問題なければ 'y'、問題がある場合は 'Ctrl+C' を押してください..."
read -n 1 -s

# Swap upstream environment to point to the target as the main environment
if [ "$DEPLOY_ENV" == "blue" ]; then
    sudo sed -i 's/set $upstream_env green;/set $upstream_env blue;/g' /etc/nginx/nginx.conf
else
    sudo sed -i 's/set $upstream_env blue;/set $upstream_env green;/g' /etc/nginx/nginx.conf
fi

sudo nginx -s reload

# Restart both environments to apply the new configuration
sudo systemctl restart blue.service
sudo systemctl restart green.service

echo "Deployment complete. $DEPLOY_ENV is now the new production environment."



さいごに、切り替えした後にアプリに問題があった場合、ロールバックするためのスクリプト"rollback.sh"を用意しました。

#!/bin/bash

if [ "$#" -ne 1 ]; then
    echo "Usage: $0 {blue|green}"
    exit 1
fi

ROLLBACK_ENV=$1

if [ "$ROLLBACK_ENV" == "blue" ]; then
    CURRENT_ENV="green"
elif [ "$ROLLBACK_ENV" == "green" ]; then
    CURRENT_ENV="blue"
else
    echo "Invalid environment: $ROLLBACK_ENV"
    echo "Usage: $0 {blue|green}"
    exit 1
fi

# Wait for a manual confirmation before rolling back
echo "Rolling back to $ROLLBACK_ENV environment. Press 'y' to confirm, or 'Ctrl+C' to cancel."
read -n 1 -s

# Swap upstream environment to point back to the rollback environment
if [ "$ROLLBACK_ENV" == "blue" ]; then
    sudo sed -i 's/set $upstream_env green;/set $upstream_env blue;/g' /etc/nginx/nginx.conf
else
    sudo sed -i 's/set $upstream_env blue;/set $upstream_env green;/g' /etc/nginx/nginx.conf
fi

sudo nginx -s reload

# Restart both environments to apply the old configuration
sudo systemctl restart blue.service
sudo systemctl restart green.service

echo "Rollback complete. $ROLLBACK_ENV is now the new production environment."



ステップ3: 仮想環境準備

☆仮想環境を作成し、必要なパッケージをインストールします。

virtualenv .venv
source .venv/bin/activate
cd BlueGreenTest/
pip install -r requirements.txt



☆スクリプトの実行権限を付与します。

sudo chmod u+x deploy.sh check_default_env.sh rollback.sh 



ステップ4: サービス作成

☆各環境のFlaskアプリケーションを管理するために、以下の内容でSystemdサービスファイルを作成します。※もしユーザーを作成していた場合、パスを適宜変更してください。

sudo cp /home/ec2-user/BlueGreenTest/service_files/blue.service /etc/systemd/system/blue.service
sudo cp /home/ec2-user/BlueGreenTest/service_files/green.service /etc/systemd/system/green.service



☆↓新しいサービスファイルを認識させるために、systemdをリロードします。

sudo systemctl daemon-reload



☆↓サービスを有効化して、自動的に起動するように設定します。

sudo systemctl enable blue.service
sudo systemctl enable green.service



ステップ5: SELinux無効化

Amazon Linux 2023のAMIを利用しているEC2インスタンスでは、SELinuxが有効化されています。


デフォルトのSELinuxのポリシーでは、Flaskアプリのインターフェースとして利用する"gunicorn"の実行が拒否されます。gunicornの実行許可ルールを作成し、ポリシーを適用していきます。


☆↓まずはサービスを起動します。gunicornの実行が拒否されているので、statusを見るとfatalとなっています。

sudo systemctl start blue.service
sudo systemctl start green.service
sudo systemctl restart blue.service
sudo systemctl restart green.service



☆↓次のコマンドで、ポリシーを作成します。

sudo cat /var/log/audit/audit.log | grep "gunicorn"
sudo grep "gunicorn" /var/log/audit/audit.log | audit2allow -M custom_policy



☆↓作成したポリシーをSELinuxに適用します。

sudo semodule -i custom_policy.pp



☆↓サービスを再起動してみましょう。

sudo systemctl restart blue.service
sudo systemctl restart green.service



☆↓正常に起動されたか確認します。

sudo systemctl status blue.service
sudo systemctl status green.service


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ sudo systemctl status blue.service
● blue.service - Gunicorn instance to serve blue environment
     Loaded: loaded (/etc/systemd/system/blue.service; enabled; preset: disabled)
     Active: active (running) since Sun 2024-08-04 07:57:10 UTC; 7s ago
   Main PID: 26464 (gunicorn)
      Tasks: 5 (limit: 9247)
     Memory: 64.1M
        CPU: 589ms
     CGroup: /system.slice/blue.service
<省略>

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ sudo systemctl status green.service
● green.service - Gunicorn instance to serve blue environment
     Loaded: loaded (/etc/systemd/system/green.service; enabled; preset: disabled)
     Active: active (running) since Sun 2024-08-04 07:57:14 UTC; 1min 23s ago
   Main PID: 26474 (gunicorn)
      Tasks: 5 (limit: 9247)
     Memory: 64.3M
        CPU: 587ms
     CGroup: /system.slice/green.service
<省略>


☆↓適用したポリシーは不要なので、削除します。

rm custom_policy.*



ステップ6: Nginx起動

☆nginxの有効化と起動を実施します。

sudo systemctl enable nginx
sudo systemctl start nginx



ステップ7: nginx.confの配置と適用

Flaskアプリにリクエストをプロキシしてもらうための設定ファイルを[/etc/nginx/]に配置し、設定を適用します。


☆↓まずは設定ファイルを配置します。

sudo cp /home/ec2-user/BlueGreenTest/nginx/nginx.conf /etc/nginx/nginx.conf



☆↓設定ファイルを適用し、nginxを再起動しましょう。

sudo nginx -t
sudo nginx -s reload
sudo systemctl status nginx


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ sudo nginx -s reload
(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ sudo systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
     Active: active (running) since Sun 2024-08-04 07:59:24 UTC; 13min ago
    Process: 26621 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
    Process: 26622 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
    Process: 26623 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
   Main PID: 26624 (nginx)
      Tasks: 3 (limit: 9247)
     Memory: 3.5M
        CPU: 67ms



☆すべての設定が終わりましたので、ブラウザからhttpでアクセスし、確認してみましょう。

http://<パブリックIPアドレス>/



Hello, Flask with Gunicorn and Nginx!“と表示されていればOKです。


☆デフォルトではBlue環境にルーティングされます。続いて、Green環境も問題ないか確認してみましょう。

http://<パブリックIPアドレス>/green



こちらも"Hello, Flask with Gunicorn and Nginx!“と表示されていればOKです。


環境のセットアップは以上です。


3. 動作確認

Blue/Greenデプロイメントを体験してみましょう。


流れは以下の通りです。

1.新しいバージョンのデプロイ(new_code/⇒green/)

2.テストと検証(Green環境)

3.トラフィックの切り替え(Blue環境からGreen環境)

4.ロールバックの手順(Green環境からBlue環境)

1.新しいバージョンのデプロイ(new_code/⇒green/)

☆まずは現在の本番環境がBlue環境なのか、Green環境なのか確認しましょう。

./check_default_env.sh


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ ./check_default_env.sh
The current default environment is: blue



☆続いて、new_code/app.pyを変更します。"Hello, Flask with Gunicorn and Nginx!"の末尾に"!!!!!!!"を追加してみましょう。

vi new_code/app.py


【変更前】

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, Flask with Gunicorn and Nginx!"

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


【変更後】

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, Flask with Gunicorn and Nginx!!!!!!"

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



2.テストと検証(Green環境)

☆"deploy.sh"を実行し、環境を切り替えていきます。"deploy.sh"には検証機能も含まれておりますので、実行してみましょう。

./deploy.sh green


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ ./deploy.sh green
http://example.com/green を確認し、問題なければ 'y'、問題がある場合は 'Ctrl+C' を押してください...


☆確認メッセージが表示されたと思います。green環境を確認してみましょう。

http://<パブリックIPアドレス>/green



変更が反映されていますね。


3.トラフィックの切り替え(Blue環境からGreen環境)

変更に問題なければ、コマンドラインで"y"を押しましょう。Blue環境からGreen環境に切り替わります。


☆さきほどまでデフォルトではBlue環境にルーティングされていましが、Green環境にルーティングされるようになるはずです。

http://<パブリックIPアドレス>/




☆Blue環境はどうなったかというと、

http://<パブリックIPアドレス>/blue



Blue環境は古い状態でそのまま稼働していますね。


4.ロールバックの手順(Green環境からBlue環境)

☆もし変更に問題があったら、ロールバックしなければなりません。現在の本番環境がBlue環境なのか、Green環境なのか一応確認しましょう。

./check_default_env.sh


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ ./check_default_env.sh
The current default environment is: green



☆それでは、Green環境からBlue環境にロールバックします。

./rollback.sh blue


【実行結果】

(.venv) [ec2-user@ip-10-0-1-238 BlueGreenTest]$ ./rollback.sh blue
Rolling back to blue environment. Press 'y' to confirm, or 'Ctrl+C' to cancel.
Rollback complete. blue is now the new production environment.



☆確認してみましょう。

http://<パブリックIPアドレス>/



Blue/Greenデプロイメントのハンズオンは以上です。


4. まとめ

Blue/Greenデプロイメントはユーザーへの影響を最小限に抑えつつ、新バージョンの提供や修正を行うことができます。


インフラエンジニアとして、サービスレベル契約(SLA)の定義に則り、システムの停止時間は限りなくゼロに抑えなければいけません。


今回紹介したBlue/Greenデプロイメントはゼロダウンタイムを実現できる方法の一つです。実際のプロジェクトではさらに複雑ですが、仕組みを理解し、導入に取り組んでみてください。


↓【PR】Flaskについて学びたい場合、絶対おすすめです。




参考リンク:ブルーグリーン・デプロイメントとは(Red Hat),Blue/Green デプロイ(AWS)