エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

サーバサイドアプリケーションにおけるリリース時の不確実性を減らす

エムスリーエンジニアリンググループの松原@ma2geです。この記事はエムスリーAdvent Calendarの23日目の記事です。

実は10月よりチームリーダーからエンジニアに戻り、開発メインの仕事を再びしています。 エンジニアリングマネージャーの振り子という話が一時期話題となっていたのですが、まさにそれをしているところです。*1 *2

現況

さてソフトウェアエンジニアとして今何をしているかというと、 クラウド電子カルテ「エムスリーデジカル」の開発チームに入り運用から開発まで一通り対応をしています。

デジカルチームの主な技術スタックは Rails, Angular, React, Scala などに加えて、インフラ周りは AWS の各種サービスを利用しています。 基本的にエンジニアは得意分野を生かしつつもなんでもやるスタンスで今の所仕事をしており、 フロントからインフラまで一通り対応しています。楽しい。

私はというとサーバサイド(特に Rails)に強みを持ちつつも、フロントからインフラまで見つつ仕事をしています。 フロントは以前主に触っていたのが Backbone.js から一気にジャンプして React ということで、いくつかの時代を乗り越えて一気に洗練された世界観に触れられたり、 Auto Scaling が有効に使われているインフラ環境を生で体験できたりと日々新しいことを学べています。楽しい。

さてデジカルはまだまだこれから事業を伸ばしていこうというフェーズで比較的プロダクトとしては若く技術も比較的新し目のものが多いです。 しかしそれでも初期のプロダクトからは数年経過しており徐々にレガシーとの戦いも目立つようになってきています。 すなわちやるべきことが溜まっている状態で改善できることしかありません。楽しい。

今回はこの中でデプロイの仕組みを改善した話をします。

f:id:ma2gedev:20191220154313j:plain
本文とは関係ありませんがピアノの内部構造

発端

事の発端はとある日のリリース後に起こりました。

ユーザからアクセスされる JavaScript や CSS リソース(以下リソース)に付与されている finger print の値にリリースしたものとは異なるものが若干混ざっていました。 若干、、いやな響きですね。

デジカルでは Git flow で開発を行っていて、リリース時は master にマージ後にタグをきってリリース業務を行っています。 リリース物は CI によって ECR に Docker イメージを登録し、そのイメージを使って ECS へのリリースを行っています。

f:id:ma2gedev:20191221152028p:plain
デプロイまでのフロー

Docker イメージ作成時にはリソースのファイルパスに finger print を付与しています。 これによってリリース毎に必要としているリソースを適切に参照する形をとっており、誤ったリソースへのアクセスを防いでいます。 そのためこういった問題が基本的には発生しないような対処はできているはずです。

例えばすぐに浮かびそうな疑うべき点は以下のようにいくつかあるのですが、いずれも確認し原因ではありませんでした。

  • リソースの生成方法が実はサーバで随時生成していて時間によって変わってしまった?

=> ECR にリソース生成後のイメージを配っており、そこからのデプロイをしているので起こりようがない

  • 実はユーザが古い環境を利用し続けていて、異なるリソースにアクセスし続けている?

=> finger print の値がリリース前にアクセスされていたものとも異なる

=> ECS のタスクも全て新しいイメージに切り替わっている

この辺りまで周りと色々調査して私は考え込んでしまったのですが、こちらの記事も書いているテックリードの山口がもしかして ECR に同じタグで異なるイメージを登録してしまったのでは?と疑問を投げかけてくれました。 最初はそんなまさかとは思ったのですが、調べてみると CI で回しているイメージ作成プロセスが master へのマージタイミングと、リリースのタグを付与したタイミングの両方で起動しており、それぞれで commit hash をもとにしたタグでイメージを登録していました。Git でタグを付与した場合、commit hash は変わらないため、全く同じタグでイメージが上書きされてしまっていたのです。

またこの時はちょうど私がリリースを担当したのですが、手順が悪くリリースのタグを切る前の master の状態でリリースを実施してしまいました。 先述の図で言うと 1. から 4. までの一連のプロセスが完了した後に、リリースのタグを切ってしまいもう一度 1. が行われてイメージが更新された状態です。 その結果リリース直後に起動したインスタンスは古いイメージをもとに起動しているため、アクセスしたユーザには古い finger print 付きリソースの URL が送られ、あとあと Auto Scaling などで起動したインスタンスは上書き後のイメージをもとに起動、新しい finger print 付きリソースへのアクセスが行われることとなってしまいました。

ヒヤヒヤしましたがリリース直後のタイミングでは古い ECR イメージで起動しているインスタンスを停止して事なきを得ました。

対策

さて無事対処後はなんとかなりましたが、我々はソフトウェアエンジニアですので同じ過ちを繰り返さぬようリリースをより確実なものとしたいです。 それもルールではなくできれば仕組みで解決したいですね。

例えば安直に考えると、「タグを付与した後にリリースすること」という運用ルールで縛ることも可能ですが、間に人が入る以上ミスは起こりえるでしょう。 ということで周りですぐに相談に乗ってもらい解決策を以下の通り実施しました。

  • リリースタグをつけた時には image を作らない
  • そもそもイメージを上書きできないようにする

前者はそもそも master と同じ commit hash なので無駄なタスクでもありました。そのため CI からタグをつけた時のジョブを削除して対応しました。

後者に関しては ECR にイメージタグを変更できないようにする機能があり Terraform を通してそれを利用しました*3。 先人の知恵ということで、いわゆるパッケージレジストリ(RubyGems など)が備えているように、リリース物の削除はできても変更はできないようにするアイデアを拝借しました。上書きできないので、今回の事象がそもそも起こらないようにできます。Immutable はこういったところにも生きてきますね。

上記を通して同様のリリースミスを防ぐことを実現できました。

まとめ/We're hiring!

ちょっとした内容でしたがリリース時の不確実性を減らしたことについてせっかくなのでまとめてみました。 今のチームでもエンジニアリングで解決できることがたくさんあり、事業の成長とともにこれからも増えていく見込みです。 もし腕に覚えのある方がいましたら是非エムスリーも検討してみてください。

jobs.m3.com