6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Djangoのコマンドにするほどじゃないけど意外と使う処理をどうするか

Posted at

こんにちは。LAPRASでテクニカルサポートを担当しているdenzowです。

この記事は、LAPRAS Advent Calendar 2020 5日目の記事です。1週間以内の遅刻は遅刻じゃないのでセーフです。

前置き

LAPRASではDjangoを使用してサービスを開発しています。Djangoにはmanagements配下にCommandクラスを継承したクラスを定義することで独自のコマンドを実装することができます。

$ python manage.py hogehoge_command

LAPRASでも当然、多くのコマンドが実装されておりバッチ処理や有事の際のオペレーション等に利用されています。現在は独自に実装したコマンドが70程あります。

一方で、できるだけ避けたいことですが日々リリースされ続けているため、意図しない挙動でお客様にご迷惑をおかけしてしまいデータの修正等のオペレーションが発生することがあります。
これらを対処するときには通常、アプリケーションが稼働しているpodにログインしmanage.py shellでDjangoShellを起動した上で手作業をします。しかし、修正までに時間がかかる場合はこれらの処理を簡略化、あるいは誰でも出来るように共通化したい要望がでてきます。

それってコマンドじゃないの

たしかに現在やっている処理をDjangoのコマンドとして定義できれば一番ですが、実際は難しいことが多いです。

  • 今すぐ必要な処理であることが多くリリースまで待てない
  • リリース後にメインコードの変更によりそのまま使うことができない可能性が高い
  • そもそもバグ修正により不要になるコマンドであるためライフサイクルも短い

このような問題があります。そのため、理想としては

  • メインコードとは独立した、より早いリリースサイクル
  • 書き換えが容易
  • 他のメンバーも共有できる

これらを満たす方法が必要でした。

で、どうしたか

メインコードとは別のsupport-commandsというリポジトリを用意しました。以下はディレクトリ構成の一部抜粋です。

support-commands
└── bin
    ├── .common
    │   └── scout.sh        -- podログインのための環境情報等
    ├── .nanika_syori
    │   └── script.py       -- 実際に行う処理内容
    ├── scoutshell           -- kubectl経由でmanage.py shellに処理を流し込むスクリプト
    └── nanika_syori         -- 実際に行いたい処理のエントリーポイント

LAPRASではアプリケーションの実行基盤にk8sを使用しており、テクニカルサポートを行うメンバーはいずれもpodにログインする権限を持っています。そこでkubectlの標準入力経由でスクリプトをmanage.py shell に流し込む方針を録りました。direnvを利用しているなら.envrcexport PATH=$PATH:$(pwd)/binを書いておけば、このリポジトリ配下に移動すると以下の様にそのままコマンドを実行することができます。

$ nanika_syori 12345

もう少し詳細

scout.sh

scout.shは作業を行うpodを選択するためのシェルスクリプトです。

scout.sh
#!/bin/bash

POD_NAMESPACE="k8sのネームスペース"
SCOUT_POD_PREFIX='podのprefix'
MANAGE='poetry run ./manage.py'
SHELL_PLUS='poetry run ./manage.py shell_plus'
POD_NAME=$(...miserarenaiyo...)
if [[ -z "${POD_NAME}" ]]; then
    echo '[ERROR] POD not found.'
    exit 2
fi

scoutshell

scoutshell
#!/bin/bash

SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
source ${SCRIPT_DIR}/.common/scout.sh

echo "EXEC: kubectl exec -n ${POD_NAMESPACE} -it ${POD_NAME} ${SHELL_PLUS}"
envsubst < $1 | kubectl exec -n ${POD_NAMESPACE} -it ${POD_NAME} ${SHELL_PLUS}

引数で渡されたファイルにenvsubstを使って一部書き換えた上でmanage.py shellに流し込みます。envsubstを使っているのは、処理に与える引数が必要な場合に動的に書き換える必要があるためです。

nanika_syori

これはサンプルなので実際には違いますが、大抵このような処理になっています。

#!/bin/bash

SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
SCRIPT_NAME=`basename "$0"`

export ARG1=$1

if [[ -z "${ARG1}" ]]; then
    echo '[ERROR] ARG1 not found.'
    exit 2
fi

scoutshell ${SCRIPT_DIR}/.${SCRIPT_NAME}/script.py

引数をARG1といった変数に詰め替えとチェックを行い、実際の処理がかかれたscript.pyを先程のscoutshellに渡して起動します。script.pyをこのスクリプトに内包させることもできますが管理画面道なので分ける形式を取っています。

.${SCRIPT_NAME}/script.py

hoge_id = ${ARG1}
print(f'HELLO {hoge_id}')

処理内容はあくまで一例ですが、${ARG1} は最終的にscoutshellenvsubstによって置換されてからmanage.py shellに流し込まれます。

以下の様に実行した場合を例とします。

$ nanika_syori 12345

そうすると、12345ARG1という変数に詰め替えが行われ、最終的にmanage.py shellに渡されるスクリプトは以下になります。

hoge_id = 12345
print(f'HELLO {hoge_id}')

これで任意の引数を取らせつつ、処理が実行できるようになっています。

まとめ

LAPRASでのテクニカルサポートが利用するリポジトリの一部をご紹介しました。なかなか刺さる人もいない内容ですがどなたかに伝われば幸いです。また、このリポジトリのblameの結果に自分以外の名前を刻んでくれるメンバーが増えればなお幸いです。

6
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?