Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

DjangoマイグレーションをWebアプリが無停止のまま安全に反映する方法

f:id:hirokiky:20191101104548j:plain

Djangoはデータベースマイグレーションの機能を持っています。 ですが、 実際、Djangoマイグレーションってどう使うの? という疑問が多いかと思います。

docs.djangoproject.com

この記事では、 マイグレーションを稼働中のアプリケーションに、無停止でどう反映すれば良いのか を説明します。 前提としてWebアプリ、データベースは本番環境に1系統づつあるとします。

基本的に無停止でマイグレーションを実行するのは 絶対に安全という方法ではないので、動作確認などをして慎重に反映する必要があります

無停止でマイグレーションを反映する基本

マイグレーション無停止で行う場合、「マイグレーションとアプリのリリースはどちらを先にすべきか」 という話になります (マイグレーションをするということは、アプリケーションの変更も必要になります)。

上記ではない場合、アプリケーションを停止する時間が必要になります(アプリケーションを停止、マイグレーションを実行、アプリケーションを変更して実行する)。

マイグレーションの3種類

アプリケーションを無停止のままマイグレーションを反映するには、まずマイグレーションの種類を判断する必要があります。 ここでは大きく分けて3つのマイグレーションと考えられます。

追加、変更、削除それぞれのマイグレーションをどう反映するのかが重要です。

動作確認は必要

無停止でマイグレーションを実行するのは、Webアプリが誤動作を起こすリスクを持っています。 方法は後述しますが、アプリケーションの作り方によって問題が発生し得ます。また、動作が保証される状態でもありません。 本番環境へリリースする前に、マイグレーションを実行しても問題ないか開発環境で必ず動作確認しましょう

マイグレーションを実行するタイミング

アプリケーションを無停止でマイグレーションを実行するには種類と実行するタイミングが重要です。 それぞれマイグレーションの種類ごとに、稼働中のWebアプリにマイグレーションを実行する方法を説明します。

テーブル、カラムを追加するマイグレーションの場合

テーブル、カラムの追加をするマイグレーションは、アプリのリリースより前に実行できます。

テーブル、カラムが増えたところで、Django製のアプリケーションには基本的に影響がありません。 ですので、Webアプリケーションのリリース前にマイグレーションを実行します。

  1. マイグレーションを実行する
  2. Webアプリケーションをリリースする

f:id:hirokiky:20191101104548j:plain
追加するマイグレーションを無停止で反映

テーブルやカラムが増えても、既存のDjango製のWebアプリケーションの動作や、通信量には影響がありません。 DjangoSQLを実行するときに、取得するフィールド(カラム)を全て指定するからです。

ただし、DjangoのQuerySetでextraやrawを使う場合や、生SQLを実行する場合はこの限りではありません。 アプリケーション内に影響するプログラムは無いかは事前にちゃんと確認しましょう。

カラムの削除をするマイグレーション

カラムの削除をする場合は、先にWebアプリケーションをリリースします。 アプリケーションが先にリリースされることで削除対象のフィールド(カラム)は使われなくなります。

  1. Webアプリケーションのリリース
  2. マイグレーション実行

f:id:hirokiky:20191101104612j:plain
削除するマイグレーションを無停止で反映する方法

削除対象のカラムが完全に使われなくなったことを確認してからマイグレーションを実行できます。 ただし「カラムが絶対にWebアプリ内で使われていない」ことを確認するのは大変です。慎重に確認した後、マイグレーションを実行しましょう。

カラムを変更するマイグレーション

カラムを変更するマイグレーションの場合は注意が必要です。 マイグレーションを無停止で実行できるかどうかはマイグレーションの内容によります。

例えばカラム名を変更するマイグレーションは無停止ではリリースできません。 どちらを先に実行しても、稼働中のWebアプリケーションに影響を与えます。

f:id:hirokiky:20191101104027j:plain
変更するマイグレーションを反映する方法

  • 無停止でリリースできない
    • テーブル、カラム名の変更
    • カラムの型を変更
  • 無停止でリリースできる(可能性がある)
    • カラムの最大長などを変更
    • インデックスの追加、削除

マイグレーションの内容を理解して、無停止で安全に反映できるかを確認しましょう。 カラムの最大長変更なども、稼働中のアプリケーションに影響を与えますので注意しましょう。

マイグレーションが複数ある場合

カラムの追加するマイグレーション、削除するマイグレーションが複数ある場合、Webアプリが無停止のままでは同時に反映できません。 その場合はひとつひとつマイグレーションの実行と、アプリケーションのリリースをする必要があります。

Djangoマイグレーションを1つづつ実行する方法は別のブログ記事で説明します。

まとめ

Webアプリを無停止のままマイグレーションを反映するには、マイグレーションの内容を理解してリリースする必要があります。

改めてになりますが、本番環境にリリースする前には開発環境で動作確認しましょう。 開発環境で、無停止でリリースできることを確認してから本番環境でリリースしましょう。

Djangoマイグレーションの内容を確認する方法や、1つづつ反映する方法は別のブログ記事で説明します。

続きはこちらです!

blog.hirokiky.org

ブログ記事に書くPythonコードはPEP8を厳密に守るべき?

ブログに書くコードスニペットはPEP8を厳密に守るべきでしょうか。

pep8-ja.readthedocs.io

ブログ記事に書くPythonコードはPEP8を気にしすぎなくて良いのでは

僕はブログ記事に書くPythonコードは、PEP8をそこまで気にしないで良いと思っています。 ブログに書くコードはブログの記事中で読まれるので、普通のコードのようにエディターなどで読まれるコードとは違うからです。

  • エディターでプログラムを読むときとブログで読むときは違う
  • 開発プロジェクト内では必要な、書き方の統一がブログには不要
  • てきとうに伝えたいだけなので厳密に気にするのは面倒

もちろんブログ記事内でも読みにくいコードは記事にとって良くないですし、PEP8を全く考慮しない書き方を「この書き方が良いものだ」と伝えるのは良くないです。 ですが、自分の記事で厳密に気にしすぎたり、まして他人のブログ記事に「PEP8準拠していないぞ」とわざわざ言う必要は無いと思います。 僕もPEP8は守ろうと思っていますが、ブログ記事内で守っていないこともあります。

PEP8を守って読みにくいとき

ブログ記事にPythonコードを書いて、PEP8を守ると読みにくくなるときがあります。 例えば以下のようなPythonコードを見ると、 main() 関数の上2行が間延びして見えるときがあります。

import os

HERE = os.path.dirname(__file__)


def main():
    print(HERE)

このくらいのコードをいくつか連続して書くと、ブログ記事は読みにくくなると思います。 PEP8違反にはなりますが、行を詰めたほうが読みやすい場合もあると思います。

HERE = os.path.dirname(__file__)

def main():
    print(HERE)

むしろ import os も分かりきっているので書かなくて良いかなという気すらします。 ブログという媒体においては、下の例のほうが大切だと思います。

なにかの戒律を厳密に守る(強いる)ことよりも、場所や場合によって一番良い方法で書くのが良いのではないでしょうか。

PEP8を読んでみる

PEP8を開いてみると、あまり厳密に気にしすぎるのは違うよねと書いています。

### 一貫性にこだわりすぎるのは、狭い心の現れである

スタイルガイドは一貫性に関するものです。
このスタイルガイドに合わせることは重要ですが、プロジェクトの中で一貫性を保つことはもっと重要です。
一番重要なのは、特定のモジュールや関数の中で一貫性を保つことです。

しかし、一貫性を崩すべき場合があることも知っておいてください
 - つまり、このスタイルガイドが適用されない場合があります。
疑問に思ったときは、あなたの判断を優先してください。
他の例を調べ、一番良さそうなものを決めて下さい。そして、躊躇せずに質問して下さい!

https://pep8-ja.readthedocs.io/ja/latest/

まとめ

PEP8は可読性のために大切だと思いますが、ブログ記事に書くPythonコードまでそこまで細かく気にする必要は無いと思います。

他にオススメの記事

blog.hirokiky.org

invokeで実行されるコマンドをプロジェクトルートから実行する方法

invoke(pyinvoke)というプロジェクト管理用のツールを使っていたときに出た疑問の解決方法を説明します。 とはいえ、私自身もまだこの方法で良いのか疑問なので、より良い方法を知っている人はぜひ教えてください。

invoke(pyinvoke)とは

プロジェクトでの開発を進める際に、よくあるコマンド操作をまとめておくことがよくあります。 例えば不要なファイルを削除したり、フォーマッターを適用したり、本番用にビルドしたりなどがあります。 invoke(pyinvoke)を使うとPythonでシェルでのコマンド操作を簡単に書けるようになります。

www.pyinvoke.org

invokeでプロジェクトルートから実行する

invoke でコマンドを実行(c.run(...))するときは、invoke ... を実行したパスでコマンドが実行されます。 invoke のタスクを定義したファイルは以下のように走査されますが、実行パスは変わりません。

docs.pyinvoke.org

ですが、例えばフォーマッターを全体に適応したいときなどはプロジェクトルートからコマンドを実行したくなります。 例えば以下のようなプロジェクト構成のとき、 mymodule 以下で invoke ... としても、 tasks.py を含むディレクトリーから実行したいときです。

+- tasks.py
+- setup.py
+- mymodule
    +- foo.py
    +- bar.py

そういう場合は、以下のように tasks.py を書くと良いでしょう。

import os

from invoke import task


HERE = os.path.dirname(__file__)


@task
def mytask(c):
    c.run("...")

こうすると、 invoke mytask はプロジェクトルート(tasks.py を含むディレクトリー)で実行されます。 私はプロジェクト全体のフォーマットを整えるタスクに活用しています。

@task
def format(c):
    c.run("black .")
    c.run("isort -rc .")

このプロジェクトルートから実行する方法については、invokeのドキュメントやIssueを調べても良い回答は見つかりませんでした。 唯一近そうなのがこのIssueでした。

github.com

もしより良い方法を知っている方がいれば教えてください。

仕事の精度、家政の精度。なぜ核家族の育児は大変なのか

家事、育児の話題続きで申し訳ないです。 今僕が育児休暇中なので考える機会が多いのです。

ですがこういう課題の多い分野ほどビジネスチャンスが大きいと思うので、記録として残しておきます。

仕事の"精度"と家政の"精度"は大きく違う

仕事と家政の違いは何でしょうか?働きに外に出る人と専業主夫/婦の揉める種として以下のような話を聞きます。

  • 私の外仕事のほうが大変だ
  • 私は毎日24時間仕事してるようなものだ

僕は仕事と家政の違いは以下のように分けて考えるべきだと思います

  • 一般的な仕事: 80%〜120%の精度の仕事を、週に5回、8時間働く
  • (自宅における)家政、育児: 40%〜60%の精度の仕事を、週に7回、12時間働く

一般的に仕事というのはまず100%が当たり前という世界です。 手順があるのであれば、その手順を間違えないことが大切になってきます。そのうえでプラスアルファを出せればより良いと捉えられます (もちろんクリエイティブな仕事はこの限りではありませんが、基本にある考え方は理解してもらえると思います)。

一方、自宅における家政というのはかなり精度が低くて良いです。 四角い角と丸く掃いたところで、売上が落ちるようなことはありません。 一日トイレ掃除をサボっても、まぁ生きてはいけるでしょう。お客様商売のようなシビアさはありません (気を遣う必要がある親族と同居している、配偶者が細かい点に厳しい場合などは精度が求められますが、これも基本的な部分は理解してもらえると思います)。

育児の大変さは、精度を高めようとすることだ

育児が大変なのは、その家政において精度が求められる(求めてしまう)ことにあります。 内的な要因としても「命を預かる以上」という責任感が産まれますし、「よく育てたい」という気持ちや、親戚の期待、子供への社会の厳しさ(泣き声に苦情がくる)もあって仕事に高精度を求められがちです。 ですが前述したようにこの仕事は言ってしまえば7D24Hに近い仕事です。そこで100%以上の精度を常に出していると燃え尽きてしまいます。

たしかに「子供が泣かないようにしないと」「ちゃんと消毒しないと」「ミルクをあげてちゃんと体重を増やさないと」など気にすべきことは多いです。ですがこれらすべてに100%の精度を求めると仕事が多すぎます。 「まぁ泣いてもいいや」「サカザキ菌なんてそうそういないやろ」「それなりに体重増えてるし大丈夫やろ」くらいの雑な気持ちでいかないといけません。

なので 育児は仕事ができる人ほど辛い ものだと思います。 予測不能で、論理的に解釈不可能で、膨大な仕事があるからです。でも、雑で良いんです。

育児は核家族には不可能。でもなぜ外注しにくいのか

育児をしてみて感じたのは、人類の子供というものが核家族で育てるようにできていないことです。 子供の世話をしながら家事も仕事もするというのは不可能です。現代においては社会保障制度がある程度充実しているので誤魔化しながら運用されていますが、そもそも不可能と言うべきです。

とはいえ親族に頼れというのも前時代的です。 では「代行」を使うのはなぜ難しいのでしょう。

それは「家事・育児代行」とすると「一般的な仕事」になり、80%〜120%の精度が求められるようになるから です。 とくに育児であれば命を預かる仕事なので、精度は100%以上を求められるでしょう。それを発注するとなると自分でやるより割高になるのは無理ありません。

100%の仕事として24時間の仕事を依頼するとなると、もちろん数万円単位の支払いとなります。 家政として自分でやったり親戚に任せるのであれば、50%くらいの精度ですが、それほど高額な仕事にはなりません(一日子供を親に預けていて、5万円、10万円を支払うことはないでしょう)。

だからといって「50%の精度の育児」を買う人はいないでしょう。命を預ける仕事だからです。 この 仕事に期待する精度の意識差 が、核家族を追い込むことになっています。

解決策は?

僕が考えるなかでは、信頼をベースにした仕事の依頼しかないと感じています。 お金が動くと100%精度の仕事を求められるので、贈与経済のような信頼を元にしたやり取りがうまくいくかもしれません。 信頼し合える人たち同士が、お互いに50%の仕事を担保し合えるような関係です。 今の社会では親族くらいしかありませんが、そういったコミュニティーを形成するサービスというのは考えられます(田舎の閉鎖的になったコミュニティーにも問題はあるので)。

ともあれ、局所最適化した都市型経済というのは、東京で毎日通勤電車に乗る単身者に向いたものなのだなと感じています。

皆さんのアイディアで、ぜひ日本の核家族(そして僕)を救ってください。

育児が大変なのは基準や業務の曖昧さからくる?

免責: この記事は個人の戯言です この記事に政治、宗教的な立場はありません。また、何かを他人に訴える文章でもありません。 僕がただ感じたことを書いているだけです。

本文

育児において何が苦労するのか。作業自体の労力もちろんですが、僕は情報の曖昧さ(基準や業務内容の曖昧さ)からくる余分な疲れというのも多いのではないか?と思っています。 「どういうこと?」「基準?業務?」と思う人にほど読んで欲しいところです。育児や家政には基準、業務設計や正解がないから大変だという話をします。

(これは早朝に、ミルクをあげるブサイクな僕です)

例えば新生児を育てたことがある人なら、誰しも以下のような疑問を持ったことだと思います

  • 授乳間隔はどれほど守るべきなのか
  • 授乳の時間はどれくらいかけて良いものなのか
  • なぜ毎度沸騰して70℃以上にしたお湯でミルクを作るべきなのか
  • 哺乳瓶を洗う洗剤は、必ず乳幼児用を使うべきなのか

色々な情報を調べたり教えて貰えるので知ることはできるでしょう。産科の看護師さんに教えてもらったり、親に教えてもらったり、役所の勉強会で知ったり、ネットで調べて知ったりできます。でも情報のソースが多くて混乱することもあることと思います。

僕が腑に落ちないと言っている点は何でしょうか。それは「基準」がないことです。調べたり教えて貰っても、明確な答えが常に100%あるわけではありません(知っているものは後述します)。 義務教育やプロトコル、規格のような基準、ある種の「バイブル」はなく、あくまでも個々人として対応する必要があります。

情報がどれも曖昧な理由は2つ考えられます

  1. 安全性の絶対的な基準がないこと
  2. 子供による個体差の違いが大きいこと

言ってしまえば、育児(や家政)は業務設計や組織管理ができていない職場です。 そのゴール設定、ルール、手順、定款、労基を自分で作る必要がある場だとすら言えます。

育児においてその基準があれば親のストレスは随分軽減されると感じています。 例えば調乳や授乳の基準があれば「これだけやっとけば大丈夫。それ以上は趣味範囲の頑張り」というのが分かって気持ちも楽になるでしょう。 例えば赤ちゃんが泣いたときも、「100dBまではご近所さんからも許容されている」と分かっていれば「泣かせておけばいいや」と思えるかもしれません。

何が言いたいかというと、僕は今世界で一番、世の親というものに共感しているということです。

1. 安全性の基準はどこに

まず目的が「子供の健康的な発育」ということで、安全性をどこまで求めるべきかという基準はそもそも存在しません(究極はもちろん個人の自由になります)。 大昔の人類であれば滅菌も殺菌も気にすること無く生きてきたことと思いますが、それに併せて疾病や死亡リスクも高かったんだろうと想像できます。だが現代ではどこまで求めれば良いのでしょうか?

現代においては知られているリスクをもとに、なるべくリスクを減らすようにしたいことと思います。後述するように比較的信頼できる基準はあるので、それを参考にするのが良いでしょう。

でも世の情報にはお気持ちレベルのものも多いです。例えば哺乳瓶などの滅菌をする「ミルトン」は水で洗い流す必要はないとメーカーが表示していますが、ネットで調べると「いちおう水で洗い流しています」という情報も見つけられます。それはもはや神経質すぎではないでしょうか。

個人がそうなるのも理解できます。基準がないがゆえ、どこまで「リスクを許容したうえで楽をできる」かが分からないからです。 信頼できる基準があって、それを信頼すれば、もう少し楽な気持ちで育児に望めるのではないでしょうか。

調乳のガイドライン

安全性の基準は探せばあります。もちろんメーカーの製品が書いてあることは、個人の想像よりは信頼できるでしょう。 また、例えば調乳については、WHOとFAOの出したガイドラインを訳したものを厚生労働省が公開しています。

www.mhlw.go.jp

これについては、以下のように明確に書かれており分かりやすです

  • 前提として無菌状態の調整粉乳を作ることは難しい
  • E Sakazaki、Salmonellaの感染リスクを下げたい
  • 以下の手順をガイドラインとして推奨
    • 哺乳瓶などは滅菌すること
    • 一度沸騰させたお湯で調乳すること
    • 70℃以上のお湯で調乳すること
    • 調乳後2時間以内に使用しなかったミルクは捨てること

また、誕生後6ヶ月までは母乳を推奨するとも書かれています。 これは原典が2007年のガイドラインなので古い情報かもしれませんが、無いよりは良い情報です。 これで「70℃以上で調乳」の根拠も分かります。

2.個体差

ここまで基準やなんやと書いていますが、正直、基準を用意するなど無理なことも理解しています。 それは子供の個体差が大きいからです。

もちろん調乳やら疾病リスクについては基準は設けられるでしょう。 でも「どういう姿勢で授乳すべきか」「どのくらいの感覚で授乳すべきか」「洗剤には何を使うべきか」「なぜ泣くのか」「便の頻度はどのくらいか」などは個体差が大きすぎて基準が設けられません。 例えばウチの娘はそれほど泣かなきません(でしたが、一ヶ月くらいになる頃によく泣くようになりました)。ミルクは飲みますが、飲んでるときの休憩はよくするなどあります。僕の赤ちゃん時代は石鹸に弱く沐浴剤を使っていたそうですし、子供によってはベビーオイルで肌荒れするなど個体差が多くあります。

よくよく考えてみれば、大人も自分自身も個体差があり、自分の健康にすらもよく分かっていません。 健康についても数多くの宗教がありますし、どれが正解かも分かりません。僕はボディーソープはダブが良いですし、皆、気がつけば低糖質ダイエットにハマっています。だがそれの恒久的な正解はなく個人の好みの問題です。

育児が難しい理由は、それら「宗教の選択」が自己責任の範疇を少し超えていることです。 あくまでも子供という他人を面倒みないといけないのに、自己責任の範疇で面倒を見る必要があることです。 これが育児ストレスやノイローゼの大きな一因になっていると思います。マジメな人ほど、100%の安全に寄せようとして心労を重ねているように見えます。

言ってしまえば誰しもが素人ながらルールもなく、絶対の正解もなく、あいまいな成果を求められている状態です。管理体制のない組織のようですね。 管理視点での仕事において大切なのは、業務設計と、ゴールの基準と、評価制度 だ(と僕個人は考えている)。育児や家政にはそれがなく、自分や家庭内で策定する必要があります。 実に自主性を求められる職場です。もはや小さな起業ではないでしょうか?

まとめ

僕が何をあなたに伝えたいか、ということはありません。 新生児の育児は大変だねという共感です。 強いて言うなら、もう少し業務設計やマネジメント的な視点を持ち込むと良いかもしれません。 以下の本や僕のブロク記事をオススメします。

気が向けば、「家政のための業務設計入門」というブログ記事でも書こうと思います。

はじめの一歩を踏み出そう―成功する人たちの起業術

はじめの一歩を踏み出そう―成功する人たちの起業術

blog.hirokiky.org

blog.hirokiky.org

highlight.jsで行ごとに区切りつつハイライトする

現在のhighlight.jsではこのstateを引き回す書き方は非推奨となっており、今後廃止される予定です。

highlight.js というコードハイライトをするJavaScriptのライブラリーがあります。

highlightjs.org

このライブラリーで、「行ごと」にHTML要素を分割しつつハイライトする方法を説明します。

highilght.jsの基本的な使い方をおさらい

highlight.jsは基本的に関数を呼ぶだけでコードハイライトができるので便利です。 以下の例では "# Heading 1" という文字列をMarkdownとしてハイライトしています。

import hljs from 'highlight.js'

let result = hljs.highlight("markdown", "# Heading1")
console.log(result.value)  // '<span class="hljs-section"># Heading 1</span>'

highlight.js は '<span class="hljs-section"># Heading 1</span>' のようなハイライト用のHTMLタグが埋められた結果を返します。 .hljs-section クラスにCSSを当てることでハイライトができるというものです。

失敗例: 行を分けてからハイライトするとうまくいかない

画面の仕様により、表示するハイライト済みのコードを行ごとに区切りたいことがあります。 例えば以下のように、行番号を表示しつつコードを表示したいときなどがあります。

<table>
  <tr>
    <td>1</td>
    <td><span class="hljs-section"># Heading1</span></td>
  </tr>
  <tr>
    <td>2</td>
    <td>This is body</td>
  </tr>
</table>

もちろん行番号を表示するコードの実装方法も様々ですが、画面や動作の都合上 <table> で実装するとします。 このとき、肝心のコードの内容は # Heading 1This is body に行ごとに区切る必要があります。

以下のように行ごとにハイライトするのは良くありません。

import hljs from 'highlight.js'

let body = `# Heading 1
This is body

\`\`\`python
import this
\`\`\`
`

for (let row of body.split("\n")) {
  let result = hljs.highlight("markdown", row)
  console.log(result.value)
}

理由は、複数行にまたがるシンタックスMarkdownならコードブロックなど)に対応できないからです。 上記の例だと```pythonimport this 、 ``` が別々に解釈されるので、サブブロック(import this)のハイライトが効かなくなります。

回答: 状態を共有しながらハイライトする

highlight.jsの .highilght(...) 関数は第4引数に状態を渡せます。 ハイライトした結果の状態を引き継いで渡すことで、複数行にまたがるシンタックスでもうまく解釈されます。

function lineByLineHighilght (body) {
  let state = null
  return body.split("\n").map(function (row) {
    let result = hljs.highlight('markdown', row, true, state);
    state = result.top  // result.topの状態を次に受け渡す
    return result.value + "<br/>"
  })
}

こうすることで行ごとに区切りつつ、行ごとにハイライトが効くようになります (行ごとに毎度 <span class="...">...</span> は正しく閉じられます)。

8月23日のAWSの大規模障害でMultiAZでもALB(ELB)が特定条件で500エラーを返すことがあったという話

このブログ記事で 「MultiAZ」にしていたら何事も全て大丈夫という認識を変えられると嬉しいです (当該の時点で障害起こした人はちゃんとMultiAZにしてなかったんでしょ?という人の認識も変えられると嬉しいです)。

MultiAZにしておくことは基本 です。 その上でも、 安心しきらずに監視は必要 という話をしています。

  • MultiAZ構成にしておきましょう
  • そのうえで監視、検知、トレーサビリティを大切にしましょう

MultiAZ要らないという見当外れの解釈はしないでください (一部、間違えた解釈をしてるコメントも見受けられましたが、大いに違います)。

前提

2019-08-23、AWSで大規模な障害が起こりました。 障害の一般的な内容は以下のとおりです。

私は、AWSの障害については仕方のないものだと思っています。 このブログ記事はAWSの障害や対応について論じたり、補償などを求めるものではありません。誤解なきようお願いします。

8月29日追記: AWSからこの記事での報告と一致する説明が追記されました

www.publickey1.jp

tech.nikkeibp.co.jp

以前のレポートではALBについての説明などありませんでしたが、8月29日時点で追記が確認されました。 やはり、他のマネージドサービスでも影響があり、 ALBでも一部の構成の場合エラーが発生していたと説明されています 。 その構成の場合はMultiAZ構成でも障害の影響を受けていたということです。

ALB(ELB)がたまに500を返すようになる

23日午後6時30分ごろから、ロードバランサーが500を返すようになりました。 障害が主に発生していたのは午後1時か、2時ごろだったので、後になってからALBが500を返すようになったという状態です。

  • ALB自体が500を返す(バックエンドからの500ではない)
  • バックエンドのサーバーは異常なし
  • ブラウザーでアクセスしても問題ないが、 特定の別サーバーからアクセスするとたまに500になる

昼過ぎのころもインスタンスが勝手に死んだりしていましたが、分散した構成でしたので大規模な問題にはなりませんでした。 「特定のAvailabilityZoneで障害なんだな、MultiAZにしていて良かった」と僕はそのときは思っていました。

が、そのあとロードバランサーが突然500を返すようになります。 最初はアプリの問題かと色々調べたのですが、結局アプリサーバーは正常に稼働しており、ロードバランサー自体が500を返していると特定できました。

後になっての知識ですが、同時刻に冷却システムの復旧とインスタンスの復旧、一部のインスタンスで(ハードウェアの限界による)リタイアがあったようなので、その当たりが関係しているのかもしれません。

ALBのデプロイをするAZで、特定のAZを使わなくすると解決

ALBを利用させているAZから、特定のAZを消すと問題なく動作するようになりました。 ALBの設定で配置する対象のサブネットを変更しました(VPCのサブネットはAvailabilityZoneに紐付いています)。

もちろん「特定のAZで障害があった」とは私も認識していましたが、ALBの設定(ALBをデプロイするAZの設定)まで変えようとは思っていませんでした。

f:id:hirokiky:20190823200044p:plain
事態の収束を告げるグラフ

これはALB自体のモニタリングのグラフです。ロードバランサー自体が返している5xxエラーの数です。ロードバランサーは通常、正常に稼働しているインスタンスが1つもない場合などに5xx系のエラーを返しますが、このときはALB以下には正常なインスタンスが紐付いていました。自動で治る雰囲気が無かったのでALBのAZの設定を変えると、グラフ右端のように問題は解決しました。

f:id:hirokiky:20190826134101p:plain
全体像(時刻はUTC

今回はすぐに検知して、調査、対応できましたが、 大きな問題でなく「静かに少し死ぬ」、そして影響範囲は大きいというのは大変でした。検知やトレーサビリティーの大切さを思い知りました

今回の問題について

今回の障害について、「MultiAZ構成だったから大丈夫だった」というような声をSNSなどでも見聞きしましたが、今回発生したすべての問題をそれだけで解決できるとは思いません(障害を起こしたサービスに対して安易な批判をするのは控えたほうが良いでしょう)。 MultiAZは当たり前として、監視や復旧のための準備が必要 です(このALBの問題もそうですし、RDSの応答がなくなる問題や、コンソール画面を操作できなくなる問題、AMIがビルドできない問題もあったと話に聞いています)。 AWSのマネージドサービス全体に関わる問題ですし、システム構成、アプリケーションの作り、時の運に影響して問題は発生し得る状況でした。

障害がどう影響したかなどは詳しくは分かりません。もちろんアプリサーバー側が古いコネクションを持っており問題を抱えたALBにアクセスしていたのか?なども考えられますが、ALBの設定を変更すればすぐに治ったので決定的にそれが原因だとも言えない状態です。

ともかく安易に「コレをしていたから大丈夫」、「コレをしないのが悪いんだ」と思わずに、今一度問題が起こっていないか、今後何をできるかを考えたほうが良いでしょう。 特定のAZでインスタンスが落ちちゃうくらいならMultiAZで対応できますが、ロードバランサーで問題が発生する & 自動でキレイに元通りにならないと対応は簡単ではなくなります。

検知の重要性

インフラを信じる、お金を使うということは大事ですが、今回のようにクラウドサービス自体が予期しない動作になることもあると勉強になりました。

「MultiAZだから大丈夫」というのも、そもそもクラウドサービス自体がすべて正常に動作しているから言えることです。予期しない状況ですべてのシステム、アプリがうまく動作するとも限りません。足がかりにしている地盤そのものに問題が発生する場合どう検知して対応したら良いでしょうか?

インフラ環境を信じすぎず、自分のアプリも信じすぎず、問題が発生する前提でちゃんと監視、検知、トレースすることは重要だと勉強になりました。

関連する他の記事

似た事象にあったBoardさんのとても誠実でまとまった報告

the-board.jp

今回の私が取った対応の具体的な操作方法

dev.classmethod.jp

他にオススメの記事

blog.hirokiky.org

blog.hirokiky.org