msawady’s engineering-note

なにも分からないエンジニアです。

【Python】運用スクリプトはshellじゃなくてPythonで書こうよ、という話

はじめに

  • 自分のプロジェクトではベースとなるシステムがPythonで書かれていることもあり、インフラ/運用系スクリプトも基本的にPythonで書かれています。
  • 一方で、社内の他のプロジェクトではやはりshellが主流です。。。先日もジュニアなエンジニアが先人の残した"芸術的な"シェルとにらめっこしているところを目撃しました....
  • どちらも経験した自分としては、圧倒的にPythonで書くほうが良いというのが正直な感想です。
  • 今回の記事ではPythonを利用するメリット、導入に向けた準備について書いて行きます。

Pythonを利用するメリット

大きく、以下のようなメリットがあると思います。

言語機能的な問題

shell よりも Python の方が言語として優れている!なんて一元的かつ宗教的なことは言いたくないですが、とはいえ、Pythonの方が優れている部分は多くあります。

オブジェクト指向的に書きやすい

正直、shell はいわゆるアプリの開発者にとっては中々とっつきづらいものかと思います。

プログラマーの君! 騙されるな! シェルスクリプトはそう書いちゃ駄目だ!! という話 - Qiita

上のリンクにも有るように、そもそもデータの扱い方がJavaPythonのようなオブジェクト指向言語とは発想が全く違います。

  • Python: コマンドの実行結果を変数(構造体)に格納→ロジック(if, for)を用いて変数を加工→次のコマンドのインプットに変数を渡す...
  • shell: コマンドの実行結果をパイプする→インプットをフィルタ(grep)、加工(awk, sed)してパイプ→インプットを用いて次のコマンドを実行(xargs)...

shell はどちらかと言うと関数型に近い発想で書く必要があり、複雑な処理を綺麗に書くには中々のスキルが必要になります。 そして大抵のケースでは"ステキな正規表現によるgrep"と"趣深いawkでのデータ加工"を組み合わせた"芸術的ワンライナー"が行われ、ジュニアなエンジニアは"勉強"が必要となっているように思います....

Pythonオブジェクト指向的に書けるので、ジュニアなアプリエンジニアでも少ないコストでキャッチアップできます。 特にデータ加工をif, forなどの馴染み深いやり方で行えるので、ロジックを追いやすくなりデバッグや修正も楽になります。

json, yaml の扱いが楽

AWSを使っていると、あらゆるデータがjson, yaml形式で記述されます。

shell でも jq, yq といったライブラリを使って処理を行うことは出来ますが、これも結局は上記のパイプを基本とした処理になるので、"芸術的ワンライナー"ルートに入りやすくなります。

Pythonでは組み込みライブラリのjson, yaml を使うことでdictとして扱うことができます。dictの変数になってしまえば、if, forといった手続き的なロジックを用いてデータの処理を出来るため、ロジックを追いやすくなります。

IDEの補助を受けられる

shell にも様々なIDEプラグインがあるものの、やはりPythonの方がメソッド補完、型補完、文法チェックなどの機能が充実しているように思います。

シニアなエンジニアであれば vim などのシンプルなエディタでもガリガリと書くことが出来ますが、ジュニアなエンジニアにとっては「このクラスにこのメソッド有ったっけ」「このメソッドの引数なんだっけ」などを覚える/調べる手間を省いてくれるIDEの補助は有り難いものです。 また、コーディングルールを揃えることも容易なので、プロジェクト全体的なコードの質を担保するという点においても、IDEの補助を受けられるというのは無視できないメリットです。

テストが書ける

正直、これが一番のメリットだと思います。

pytest などを利用した単体テストを書くことが出来るので、「プロダクションのコードはちゃんとテストされているのに、運用系スクリプトは全然テストされていない(Test In Productionな状態)」を回避することができます。

もちろん、運用系スクリプトはファイル/ネットワークのI/Oが多かったり、前提条件が様々だったりとテストが書きづらいものではありますが、

  • データの取得部分とフィルタ・加工部分のメソッドを分けてフィルタ・加工部分のテストを書く
  • 静的文法チェック(Lint)を走らせる
  • dry_run オプションを使って実環境での仮実行を行う

など、様々なやり方を使ってロジックの正常性を担保することができます。また、単体テストであればCIツールと組み合わせることも可能なので、継続的に品質を担保できるとともに、リファクタや追加改修の心理障壁を下げる効果も期待できます。

ログ、標準出力をコントロールしやすい

Python には logging というログ出力のライブラリが有るため、実行時の標準出力をコントロールしやすいというメリットが有ります。

  • 普段の実行時にはファイルにのみ出力する
  • 手動実行時には標準出力にも出力する
  • -v, --verbose オプションを用意してdebugしやすくする
  • debug のときでもライブラリの出力は抑止する(boto がとにかくうるさい...!)

などなど。こうした柔軟な出力制御を柔軟に出来るのは開発時、運用時ともに地味に効いてきます。

Python 導入に向けた準備

Python のメリットを書いてきましたが、導入時にやらなければいけないことはそれなりにあります。が、それほど難しいことは無いと思います。

  • 環境をどう揃えるか
  • Pythonへのキャッチアップ
  • コーディングルール

環境をどう揃えるか

基本的なLinuxサーバにはPython2系がデフォルトでインストールされています...がPython3系を使う方がベターです(詳しくは割愛)。 また、各種ライブラリやバージョンも開発環境、ビルド環境、本番環境で揃えたいですよね。

Pythonのバージョンはそれほど難しい問題ではありません。AWSであればcloud-initを用いてインスタンス作成時にインストールすれば良いですし、puppet/ansible などを使うのも良いでしょう。どちらにしろ、簡単に自動化出来る部分です。

ライブラリについてはpip, venv を利用するのが良いでしょう。本番環境で利用するrequirements.txt を作成し、ビルド環境や開発環境ではそれを利用したvenvを作成すればライブラリを揃えることが出来ます。これも大きな問題にはならないでしょう。

Python へのキャッチアップ

Python 自体がそれほど難しい言語では無いので、特にアプリエンジニアにとっては、キャッチアップのコストはあまり問題にはなりません。
むしろ、上で書いたように shell が中々難しい言語なので、キャッチアップへのコストという面では大きく変わらないと思います。

とはいえ、最低でも1人はエバンジェリストがいると良いですね。エバンジェリストがメンバーにトレーニングを行い、その内容をドキュメントに残して、New Comer へのトレーニングはメンバーが行う...という流れを作ることが理想的ですね。

// というか「shellは出来て当たり前」みたいな雰囲気の中、ロクにトレーニングが行われないことのほうが多いし、大きな問題では...???

コーディングルール

Python には pep8, pylint などのというコーディングルールが有るので、それを利用して適宜カスタマイズしていけば良いでしょう。

Pythonのスタイルガイドとそれを守るための各種Lint・解析ツール5種まとめ! - SideCI Blog

  • 変数名は snake_case, クラス名は CamelCase
  • 1行 80 字以内
  • 1メソッドの中のローカル変数は20個まで

などのルールが定義されています。デフォルトでは中々煩いルールも有る(変数名は3文字以上、メソッドドキュメント必須など)ので、プロジェクトのサイズやメンバーのレベルに合わせて適宜カスタマイズすることをおすすめします。 また、コーディングルールに準拠しているかどうかのテストはCIと組み合わせることも容易なので、「コーディングルールに準拠していなければmasterへのマージが出来ない」などの仕組みを作ることも出来ます。

おわりに

  • 正直、自分がshellが苦手ということも大きいのですが、難しいことを無理やりshellでやろうとして、保守性の低い"芸術的ワンライナー"が量産されることへの疑問が有ったのでこの記事を書きました
  • それなりにロジックを必要とする処理を書くのであればPythonの方が書きやすいし読みやすい→どうせなら全部Pythonで書けば良いんじゃない?というのが自分の意見です。
  • Python も shell もそれぞれ難しさはあります。が、プロジェクトとして採用するならPythonの方かと思います。
    • Pythonは「環境構築やコーディングルール」といった仕組みの部分が難しい→仕組みさえ作れば後は簡単。
    • shell は「可読性を高く保つこと、ミスなく書くこと」が難しい→個人のスキルに依存し続ける

長くなりましたが、以上です。実際に運用スクリプトを書く時のargparselogging の使い方についても記事を書きたいと思います。