メドピア開発者ブログスマートフォン用の表示で見るメドピア開発者ブログ集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。SREの侘美(たくみ)です。普段はRails/Vue.js/terraform/Lambdaなどを書いています。趣味は飼い猫と遊ぶことで、生傷が絶えません。入社してから約半年間、Railsのプロジェクトで実装をしつつ、合間に開発環境の改善をいろいろとやってきました。けっこうな分量となったので、紹介したいと思います。本記事で扱う開発環境とは下記2つを指すこととします。主な改善対象である、「MedPeer」サービスの特徴をご紹介します。Macを利用コンテナ数は旧システム、認証システムを入れて32個ソースコードはdocker-syncを使ってローカルとコンテナ内で同期RSpecやrubocopの実行もコンテナ内で実行システム構成としては、ざっくり下図のようになります。私が入社した当時、MedPeerサービスの開発環境には下記のような課題がありました。立ち上げていると電池をモリモリ消費する状態でした。開発環境のコンテナ群を立ち上げっぱなしで、ミーティングに出ると、だいたい、2時間程度で電池が切れてしまうくらいでした。コマンドでプロセスのCPU使用率を確認したところ、SpringのプロセスがCPUを常に利用していることを特定できました。consoleやRSpecの実行等、ロードを伴うコマンドの実行を高速にしてくれます。MedPeerサービスではロード対象となるファイル数が多いためか、常時それなりのCPUを消費していました。Springには、DISABLE_SPRINGという環境変数を指定することで、無効にする機能があります。この環境変数を設定して一律で無効にすることもできるのですが、電池消費よりもスピードを優先するエンジニアも当然いますので、任意に設定できるように対応しました。medpeer.jpの開発環境にはdirenvが導入されているので、これを利用し、各エンジニアのローカルで指定した環境変数に応じて、コンテナ内にDISABLE_SPRINGを引き渡すように設定しています。起動が不安定対策メモリ消費が多いこともあり、開発環境を落とすことが多いのですが、起動しようとすると、Railsの起動や依存コンテナの起動に失敗することが多々ありました。原因は様々あったのですが、大半は下記のように起動順序によるものでした。gem、npmの依存ライブラリのインストール量で起動順序が変わっているDBやElasticsearch、fluentd等依存コンテナの起動より先にこれらに接続するコンテナが立ち上がり、エラーとなるもちろん、docker-composeによるlinksやdepends_onを指定し、コンテナの起動順序は担保しているのですが、これらではコンテナ内のプロセスがreadyになったことまでは担保してくれません。そこで、コンテナ内のプロセスが応答するまでwaitしてくれるufoscout/docker-compose-waitを利用することにしました。似たツールはいくつもあるのですが、複数のコンテナ/ポートへの疎通チェックができる点で、docker-compose-waitを採用しました。マウントし、環境変数と起動時のcommandを設定することで、指定したコンテナの指定したポートに疎通することをチェックしてから、任意のコマンドを実行することが可能です。サンプルのdocker-compose.ymlは以下のようになります。elasticsearchコンテナの設定この設定により、依存モジュールのインストール状況等で、各コンテナの起動スピードが多少変化しても、コンテナの起動順序を担保し、起動に失敗し辛い設定することができました。脱docker-syncdocker-syncが不安定であることもエンジニア内で問題視されていました。異常にCPUを消費するときがある(暴走状態!)ホスト-コンテナ間の同期が遅いときがあり、ソースコードの変更が反映されていないときがある(突然の死!)(上記の問題はdocker-syncが利用しているunison起因であることまでは確認しています)docker-syncはネイティブなDocker環境ではないMac等の環境において、ホスト-コンテナ間のファイルの参照を高速化されるために開発されたツールです。MacはVM上でLinuxを起動し、そのLinux上のDockerを利用する形なので、ホスト-コンテナ間のファイルの参照(同期)が遅いものとして有名です。volumeのcacheオプションを利用することで、docker-syncをやめて安定/高速なソースコード同期を実現しようとチャレンジしてみました。dockerでは、volumeマウントのdelegatedオプションを利用することで、ホスト-コンテナ間でのファイルの参照の遅延を許容し、高速な参照を実現することが可能です。./app:/app:delegated結論から言うと、脱docker-syncはできませんでした。開発環境を立ち上げ、複数のページの表示速度を計測した結果、いずれも約2.3倍程度表示スピードが劣化し、ページの表示に2~3秒かかるようになってしまいました。この数値はしばしば見かける「docker-syncで約2倍程度高速になる」という記述とも合致しており、確からしい結果となりました。Macで開発を行う場合、docker-syncが最も高速であるという知見を得ました。volumeのcacheオプションだけでも開発環境のページ描画はそこまで遅くならないので、安定性を重視してdocker-syncの利用は不要だと思います。docker-syncを利用していないプロジェクトにdelegatedオプションを導入したところ、jestの実行が数倍高速になるといった副次効果を得ることもできました。依存モジュールをコンテナ内に閉じ込めるこちらは、docker-syncを導入していないプロジェクトに導入した設定です。Macのvolumeマウントはとても遅いので、ホスト側のファイルを参照する量が増えるほど、コンテナ内でのRailsの挙動は遅くなります。上記の課題を解決するため、下記のような対策を採りました。現状、vendor/bundle配下のファイルがホスト側で必要になるシーンが特にないため、vendor/bundle以下のファイルはコンテナ内のみに存在するように構成を変更します。volumeを使い、コンテナのライフサイクルとは別に永続化しています。具体的なdocker-compose.ymlファイルは下記のようになります。{}図で示すと、下図のようになります。beforeafterこの構成により、コンテナ内から参照するホスト側のファイル数を減らし、Railsの動作を高速化することができます。Linux対応MedPeerサービスの一部のテスト環境は、Ubuntu上に構築した開発環境で動作しています。過去にdocker-sync導入後、Ubuntu上の環境は別ブランチで構築する構成となっていたため、メンテナンスされず放置される傾向となっていました。OSでも開発環境が構築できるように修正しました。具体的な方法としては、docker-composeのoverride機能を利用します。upコマンドで環境を立ち上げます。OS上では、環境変数にCOMPOSE_FILE=docker-compose.yml:linux.ymlを設定し、docker-compose.ymlに加え、linux.ymlを読み込み、設定を一部上書きします。upコマンドで環境を立ち上げます。mountの仕組みでソースコードをマウントするように設定します。OSでも環境が立ち上がるように構築されています。認証機能の有無の切り替えわれわれのプロジェクトの中には既存の認証サービスと連携するものがいくつかあります。開発環境でも本番環境と同等の認証の仕組みを動かそうとすると、認証に関係するコンテナだけで、13個ものコンテナを追加で起動する必要があります。これでは明らかにローカルマシンのリソース消費が増えてしまうため、開発環境では認証機能をダミーに変更し、これら13個のコンテナを削除したい気持ちになります。一方で認証機能の検証を行いたいシーンもあります。そこで、新しいいくつかのプロジェクトでは開発環境で認証機能の有無を切り替えられるようにし、認証機能無しで開発環境を立ち上げた場合は、余計なコンテナが起動しないように設定しています。こちらに関しても、docker-composeのoverride機能で実現しています。認証機能を利用しない構成でdocker-compose.ymlファイルを用意し、これに認証サービスを追加するためのauth.ymlファイルを用意します。これらのdocker-composeで利用するファイルは、USE_AUTH環境変数をdirenvを利用して設定し、Makefile中でCOMPOSE_FILE変数に設定することで制御しています。Rails内部の実装でも認証をダミーの実装に切り替える必要があるため、USE_AUTH環境変数をRailsのコンテナに渡し、実装が切り替わるようにしています。認証機能を利用しない構成の場合は、ベースとなるdocker-compose.ymlのみで構築されます。認証機能を利用する場合は、Makefileにて環境変数COMPOSE_FILE=docker-compose.yml:auth.ymlを設定することで、認証機能を追加します。COMPOSE_FILEに指定したことで、下記のauth.ymlでdocker-compose.ymlがoverrideされ、下の図のような構成でコンテナが立ち上がります。認証機能を利用する構成このような切り替え機構を導入することで、ほとんどの開発シーンでは認証機能を省略し、省リソースでの開発環境を実現することができました。「MedPeer」サービスの開発環境には、一部の機能の動作を検証すためのコンテナや、モバイル版含めてすべてのサービスが動作するように下記のコンテナも含まれています。デフォルトはmaximumな構成となっています。起動しているコンテナの中には、下記のようなものも含まれています。メールの内容を確認するためのmailcatcherコンテナS3へのファイルアップロードを検証するためのminioコンテナモバイル版等、一部の機能で利用するAPI用コンテナこういったコンテナはすべての開発者の環境で必要となるわけではないので、不要なコンテナを停止させることで、開発環境のリソース消費量を削減しておきたいです。一から構築する場合は、何度か紹介したdocker-composeのoverrideを利用し、デフォルトをminimumな構成とし、特定の開発時に必要なコンテナは別ファイルで定義し、環境変数で切り替えるのが良いでしょう。今回はすでにデフォルトがmaximumな構成となっているため、docker-composeのoverride機能を利用し、不要なコンテナは起動後即終了するように設定することで、コンテナの起動数を減らす方向としました。こちらも最小構成で十分な開発者は、direnvを利用し環境変数にCOMPOSE_FILEを設定することで、構成の切り替えをできるようにしています。disabled']個人ごとのカスタマイズを可能に上記を応用することで、自分のみのコンテナを追加したり、逆に特定のコンテナを起動しないようにするといったカスタマイズも可能になっています。docker-compose.custom.ymlのようなファイルを作成し、任意の設定を追加します。.git/info/excludeに設定しコミットから除外します。環境変数のCOMPOSE_FILEを設定し、作成したdocker-compose.custom.ymlを読み込むようにすることで切り替えを実現できます。まとめ今までの半年間で行ってきた開発環境の改善を振り返ってみました。単純なものからちょっとテクニカルなものまで、いろいろやったなあ、という感想です。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。涙の数だけ強くなるフロントエンドエンジニア村上(@pipopotamas...2019-07-04開発合宿に行ってきました!@松本メドピアのサーバーサイドサウナーの川井田です。エンジニアの宮原です。〜群馬県みなかみ町へのメドピア開発合宿日誌〜こんにちは。メドピアエンジニアの保立です。月別アーカイブリンク医師専用サイト「MedPeer」エンジニア採用はこちら!スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。