ハウテレビジョン開発者ブログスマートフォン用の表示で見るハウテレビジョン開発者ブログ『外資就活ドットコム』を日夜開発している技術陣がプログラミングネタ・業務改善ネタ・よしなしごとについて記していきます。弊社は「外資就活ドットコム」というWebサービスを開発・運営している会社です。サービスイン当初はイケイケガンガンで高速開発・高速リリースをうたっていましたが、開発者が増えるにしたがって様々な問題が発生してきました。今回はプロダクトのリリースにまつわる問題を解決するために弊社で採用した開発ワークフローについて紹介します。どんな問題が起こっていたのか?Capistranoによる自動デプロイは実現していた我々ですが、それですべてがうまくいったわけではありませんでした。具体的には以下の様な問題が発生していました。デプロイできる環境を用意するのが面倒である。各デプロイ担当者がデプロイツールをインストールする必要がある。デプロイツールを更新していない場合には失敗する。デプロイ対象サーバーにデプロイ担当者の権限を与える必要がある。全員がデプロイできるようにしたいため、人数が増える度に設定する。リリースしたものの、すぐに不具合が発覚することが度々ある。開発環境と本番環境との差が原因であることもしばしばある。関しては、各人がデプロイ環境を準備するのではなく、すでに準備された環境上でデプロイを実行することができれば解決できそうです。関しては、本番によく似た検証環境で事前に確認できるようにすれば防げそうです。そこで今回の目標は、次の2点を中心に、開発ワークフローを再設計することにします。運用環境の種別まずは検証環境を含めた運用環境の種別ついて考えましょう。Webサービスでは開発から本番までをフェーズに区切り、それぞれのフェーズにおいて別々の環境で運用するのが典型的な形式だと思います。我々は4つのフェーズと環境に分けることにしました。一般ユーザーが利用する環境であり、一般的には本番環境と呼ばれます。常に安定して動作していることが求められます。環境としては1つだけ存在します。事前検証環境production環境とほぼ同一の環境です。これも1つだけ存在します。この環境を用意することで前述の問題点(2.)を解決します。この環境が行うべき「検証」にはWebアプリケーションのリリース直前の動作確認だけでなく、以下の点も含まれることになりました。開発された機能を検証するための環境です。リリース直前だけでなく、開発中からプロダクトマネージャーやオペレーターの方に実際に使ってみてもらって、仕様との齟齬がないかなどを確認します。開発された機能ごとに作成することになるので、これは常に複数の環境が存在しえます。リリース前にはここで入念に動作テストを行います。開発者が機能を実装するための環境です。現在は開発者各自のラップトップ内にVagrantでVMを起動し、Chefで本番と似たような環境を構築します。詳細は別の記事に譲ります。デプロイを実行する環境デプロイを実行する環境をどこに準備すべきでしょうか?それには継続的インテグレーション(CI)サービスを利用するのが最適でしょう。継続的インテグレーションとは、ソフトウェア開発において品質改善のためにビルドやテストなどを継続的に実行していく手法のことを指します。CIなどがSaaSとして提供しています。どのツール/サービスを使うかはともかく、これらは多くの場合、デプロイのためのステップや機能も提供しています。デプロイ環境をCIツール内に構築することで、デプロイ担当者各人が用意する必要は無くなりますし、また各人に運用サーバーへのアクセス権限を与える必要も無くなります。このようなサービスではCIはソースコードのPUSHをきっかけに実行されるので、デプロイのきっかけもソースコードのPUSHのタイミングになります。CIツールを使えば各開発者が特に気を払わなくても自動的にデプロイする環境が構築できます。CIを採用し、自動デプロイの機能を使って、前項で構築した環境にデプロイするプロセスを構築することにしました。ここで必要となるのが、どのような条件でどの環境にデプロイするのかというルールです。ルールが無ければ自動化できません。そこで次に、これまでの条件にマッチするブランチングモデルを規定することにしました。混ぜたようなモデルになりました。下記がその概略図です。弊社の開発チームでは以前から上記とほぼ同じブランチングモデルが規定されており、特に問題なく運用されていました。今回はそのモデルに対して、自動デプロイにまつわる変更を少し加えただけです。以下でそれぞれのブランチの役割について解説しますが、自動デプロイにまつわるものだけでなく、元々の開発スタイルについての考慮が含まれていることをご承知おきください。開発のメインブランチmasterブランチは開発のメインブランチです。常に存在し、そして常にデプロイ可能な状態を保つことが期待されます。Requestを経由したもののみ受け付けます。マージされるとユニットテストがCIで自動的に実行されます。テストが成功すると自動的にstaging環境へデプロイするように設定します。必然的にstaging環境と常に同一になります。小規模な機能開発topicブランチは比較的小規模な機能開発、または不具合を修正する際に作成します。その粒度が小規模かどうかは主観的に判断してかまいません。目安としては、1人で開発が完了するようなものと考えて差し支えないでしょう。topicブランチはmasterから分岐します。ブランチ名は機能を端的に示すものが望まれます。中規模以上の機能開発releaseブランチは中規模以上の機能開発をする際に作成します。何をもって中規模とするかは主観的に判断してかまいません。masterブランチにマージするまでに相当な時間を要することが予想される場合と考えれば良いと思います。releaseブランチはmasterブランチから分岐します。中規模の開発では複数の機能が実装されることが多く、releaseブランチは機能を端的に示す名前を付けられないかもしれません。その場合はリリース目標の日付やプロジェクト名などをつけましょう。いったブランチ名が付けられたことがあります。「このリリースが終わったら、俺、温泉に行くんだ...。」といった願望が込められています。releaseブランチが作成されたらtesting環境を構築することが望まれます。featureブランチはreleaseブランチから派生する細かい単位の機能開発をする際に作成します。releaseブランチとfeatureブランチとの関係はmasterブランチとtopicブランチとの関係とよく似ています。1つのブランチに対して複数人で直接コミットすればパニックになること必至です。そこでより小さな機能単位で開発するため、releaseブランチからさらに分岐させます。先ほどのreleaseブランチの名前が入ります。することで、最終的にreleaseブランチをmasterブランチにマージする際、あらかじめマージしておくべきfeatureブランチの取りこぼしを防ぐことができます。いったブランチ名になります。本番環境と同一productionブランチはproduction環境にデプロイするための特別なブランチです。開発者がチェックアウトして利用することは基本的にありません。productionブランチにマージされるとCIを経由して自動的にproduction環境へデプロイするように設定します。Requestのみを受け付けるようにします。テストが成功したリビジョンのみがマージされるように設定することで、CIによるテストは除外しても問題ありません。非常によく似たモデルです。その2つのモデルと我々のモデルとの大きな違いはreleaseブランチとfeatureブランチではないかと思っています。releaseブランチはあるのですが役割が違います。役割が違うのに同じ名前なので、読まれている方は混乱してしまうと思いますが、我々のreleaseブランチはメンテナンスブランチではありません。名前はともあれ、releaseブランチとfeatureブランチは、中規模機能開発において作業ブランチがmasterブランチから大きく乖離する場合でも開発が滞らないようにするために作られています。2つのモデルはmasterブランチ以外のブランチからさらにブランチを切るフローがないため「ビッグバンリリース」が非常に難しいと考えています。細かい変更を順次リリースし続けることが理想的ではありますが、世の中そんなにうまくはいかないものです。運用環境とブランチングモデルを規定し、それぞれが紐付いたところで、これらをワークフローとしてまとめてみましょう。ここでは要件定義などの実装前プロセスは省いているのであしからず。1.実装まずはdevelopment環境でプログラムの実装です。プログラムを書き上げたら(書き上げなくても)GitHubにプッシュします。Requestを作成して同僚に意見を求めることができます。2.受け入れテストプログラムの実装が完了したらプロダクトマネージャーやオペーレーターに受け入れテストを依頼します。受け入れテストを行うのはtesting環境です。testing環境の起動はHubotで実行できるようにしてあります。Requestを作成し、他の開発者にコードレビューを依頼します。コードレビューの詳細については別の記事に譲ります。4.staging環境へデプロイコードレビューに合格したら作業ブランチをmasterブランチにマージします。コンフリクトしていなければGitHubの画面上でボタンを押すだけでマージできます。masterブランチマージされたことが自動的にCIに通知され、CIツールがでユニットテストを実行します。ユニットテストに合格したら、自動的にstaging環境へデプロイされるように設定してあります。staging環境へデプロイされたら軽く動作確認を行います。5.production環境へデプロイ任意のタイミングでmasterブランチをproductionブランチにマージします。Requestを作ります。デプロイ前の作業を確認した上でマージボタンを押して、あとはCIが行うデプロイプロセスを確認します。これでproduction環境へのデプロイが完了します。副次的な効果この開発ワークフローで運用を始めた結果、当初の目的には含まれていない良い効果も得られました。代表的なものは以下の2点になります。デプロイの権限をGitHubの管理と統合できたこれまでデプロイを実行する際には公開鍵をデプロイ対象のサーバーに配置するといった作業が発生しており、リポジトリに書き込みできる人とデプロイを実行できる人に意図しない差が発生することがありました。細かい権限管理を行いたい組織にとってはそれでも良いかもしれませんが、我々の規模では過剰な権限分けであり、注意を払わなければいけない範囲を拡大させるだけでした。CIでデプロイするようにしたことでその準備作業も無くなり、また該当リポジトリへのWrite権限があればデプロイできるというように開発に必要な権限とデプロイに必要な権限が一致するようになりました。Requestを見ることにより明確かつ探しやすくなりました。これまでもデプロイ時にコミットメッセージをSlackへ通知していたのですが、この程度の情報では誰がどのような変更をデプロイしたのかをすぐに把握することができませんでした。CIでデプロイするようにしたことで、CIの管理画面で誰でも同時に、または後からデプロイの実行結果を確認することができ、より大きな安心感につながっています。何とか練り上げてきたこのワークフローですが、まだまだ足りないところも多いと感じています。現状のフローではmasterブランチにマージした後、staging環境で挙動を確認する時間をあまりとっていません。testing環境で確認を充分に行っているので良しとしていますが、やはりmasterブランチにマージされた状態で確認しなければ本当の確認になっていないとも言えます。我々はできるだけ早くユーザーに価値を届けることを重視し、開発速度を上げてサービスの提供と修正を細かく繰り返すことで必要な品質レベルを確保しつつ新規機能を最速で提供するべきだと判断し、このワークフローを採用しました。ロールバックがむしろ面倒になった仮にあるデプロイによって問題が発生した場合はロールバックをする必要がありますが、CIでロールバックを行うことはできないため、デプロイツールを利用するか、サーバーへログインして操作するか、マージコミットをRevertして再度デプロイする必要があります。CIでデプロイできることによってデプロイツールが手元に用意されていないことが多くなったため、リリース時のトラブルの際にはむしろ慌てるという状況になってしまいました。まとめリリースにまつわる問題点を、運用環境およびデプロイプロセスを見直し、開発ワークフローを再設計することで解決しました。記事の都合上、これらの施策は一気に行ったような書き方になりましたが、実際はそういうわけではなく、段階を経て進めていたものです。我々と同じような開発の問題を抱えているチームがありましたら、少しずつ部分的にでも取り入れてみていただければと思います。このワークフローは現在の弊社の状況に最適化された方法であり、会社の風土や規模、さらには技術レベルが変わることによって内容は常に変わってくるものであると思います。皆さんもそれぞれのチームに合ったワークフローを構築してみてください。見る今回紹介した一連のプロセス改善については、上記の書籍を読めばより深く理解できると思います。私はもっと早く読んでおけば良かったと後悔しました。そのプロセスを徹底的に自動化すること、が説かれています。開発に混乱をもたらし、リリース作業に苦労し、ユーザーへ価値を届けられない状況を打破する方法を指南しています。17:57外資就活ドットコムの開発ワークフローの再設計Tweet関連記事2018-07-20業務未経験からエンジニアになって半年を振り返るこんにちは、ハウテレビジョンエンジニアのFです。データ解析本部のn_maoです。@a...2014-09-05nginxのアクセスログにユーザーIDを記録する方法こんにちは。来週末のPyConが待ち遠しくてたまらない祖山です。はてなブログPro『外資就活ドットコム』の開発チームです。技術ネタ・プログラミングネタ等々書いていきます。業務未経験からエンジニアになって半年を振り返るDocker初心者がRails+MySQLの環境構築をDockerでやってみた月別アーカイブ検索はてなブログをはじめよう!howtvさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。