AWS Elastic BeanstalkでDjangoカスタムコマンドを定期実行する

この記事はエヌ次元株式会社 Advent Calendar 2022の13日目の記事です。

https://qiita.com/advent-calendar/2022/nzigen

Django × AWS Elastic Beanstalkを使って個人的にWeb アプリケーションを構築した際に、
Djangoカスタムコマンドの定期実行を実装しようとして、躓いた部分があったので備忘録もかねて投稿させて頂きます。


## Djangoカスタムコマンド定期実行の実装
### 必要なこと
1. 定期実行したいDjangoカスタムコマンドの作成
2. cron.configの作成

#### Djangoカスタムコマンドの作成
定期実行したいDjangoカスタムコマンドを作成します。
WebAPIから情報を取ってきて、データベースのデータを更新するという処理を実装しました。(実際のコードからは簡略化してます)

```python:management/commands/update_data.py
import os
import requests

from django.core.management.base import BaseCommand
from django.utils import timezone

from main.models import Team,Match

from django import setup
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Sampleproject.settings')
setup()


class Command(BaseCommand):
    help = 'Update data from webapi'

    def handle(self, *args, **options):
        leagues = League.objects.all()
        league_codes = [league.code for league in leagues]

        for league_code in league_codes:
            self.update_standing(league_code)
            self.update_match(league_code)

    def get_dict_from_api(self, url):
        # webapiへリクエスト、レスポンスデータの加工など
        return data

    def update_match(self, league_code):
        target_url = 'webapi_url'
        input_dict = self.get_dict_from_api(target_url)
        for match_info in input_dict['matches']:
            # updateしたいオブジェクトをDBからselect
            match = Match.objects.get(match_day=match_info['matchday'], 
            # ...
            # 中略(データの更新処理など)
            # ...
            match.save()

```



#### configファイルの設定
.ebextensionsディレクトリ内にconfigファイルを作成します。
ここでは
- 環境変数の設定、Djangoカスタムコマンドを実行するシェルスクリプトの作成
- Cronの設定ファイルの作成
- クリーンアップ
を行っています。


```.ebextensions/update_data_cron.config

files:
    # シェルスクリプトの作成
    /usr/local/bin/update_data_script.sh:
        mode: "000755"
        owner: root
        group: root
        content: |
            #!/bin/bash
            source /var/app/venv/staging-LQM1lest/bin/activate
            cd /var/app/current
            # DB接続情報を環境変数へセッティング
            RDS_USERNAME=`/opt/elasticbeanstalk/bin/get-config environment -k RDS_USERNAME`
            export RDS_USERNAME
            RDS_PASSWORD=`/opt/elasticbeanstalk/bin/get-config environment -k RDS_PASSWORD`
            export RDS_PASSWORD
            RDS_HOSTNAME=`/opt/elasticbeanstalk/bin/get-config environment -k RDS_HOSTNAME`
            export RDS_HOSTNAME
            RDS_PORT=`/opt/elasticbeanstalk/bin/get-config environment -k RDS_PORT`
            export RDS_PORT
            RDS_DB_NAME=`/opt/elasticbeanstalk/bin/get-config environment -k RDS_DB_NAME`
            export RDS_DB_NAME
            # Djangoコマンドの実行
            python manage.py update_data

    # cronの設定
    /etc/cron.d/update_data_cron:
        mode: "000644"
        owner: root
        group: root
        # UTC22:30に定時実行(日本時間7:30)
        content: |
            30 22 * * * root /usr/local/bin/update_data_script.sh

# クリーンアップ
commands:
  rm_old_cron:
    command: "rm -fr /etc/cron.d/*.bak"
    ignoreErrors: true

```


### 注意点(躓いたポイント)
RDSへのアクセスが発生するカスタムコマンドを実行する場合、コマンド実行環境の環境変数にRDSへの接続情報を入れておく必要があります。
ElasticBeanstalkでは[get-config](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/custom-platforms-scripts.html)コマンドを使ってRDSの接続情報を取得できるので、それを利用して環境変数に接続情報をsetしています。
自動的に設定してくれるものと勘違いして詰まりました、、、



### 所感
AWS Elastic BeanstalkはEC2、VPC、RDSなどを自動でセッティングしてくれる便利なサービスですが、拡張的な機能を設定するには、各種configファイルを設定する必要があります。
まだまだ勉強中ですが、公式のドキュメントは充実しているので、うまく活用していければと思います。

### 参考資料

https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-django.html

https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/custom-platforms-scripts.html