エンジニアチーム内LTを始めることになりました

私はGMOペパボのムームードメインというサービスのエンジニアをしています。2ヶ月ごとにロリポップ!ヘテムルムームードメインと、サービスを横断するインフラチーム合同でTechMTGが開かれています。(1回目の模様はこちら

昨日2回目が開催されたのですが、とても良い内容が多く参考になるものばかりでとても良かったです。ペパボのテックブログにそのうちレポートが書かれると思います。

ムームードメインのエンジニアも1人TechMTGに登壇したのですが、あまり登壇経験がなく準備もすごく大変そうで、チーム内レビューもあるのですがレビュアーも登壇経験が少ないためレビューも大変でした。

チーム内LTを始めてみようと提案

ムームードメインのエンジニアは現在4人なのですが、皆登壇経験があまりなく少し苦手意識を持っているようでした。そこでチームにLTをやってみませんかともちかけたところすぐにみんなOKしてくれました。(フォロワーシップ!)
すぐにいいねと言ってくれるメンバーたちにも感謝。

f:id:kimromi:20170225144031p:plain

LTのルール

◯ 内容

サービスの仕様の話、技術的な話、エモい話、とにかく何でもあり。

◯ 時間

5分程度でまとまるくらい小さなものでOK。大きすぎると逆に負担になるからライトな感じで。

◯ 資料

スライドなりテキストなり何かしら資料としては残す。口頭だけではとにかく忘れてしまうから。

ということで早速始まった

初回、まずは自分がやるぞ!ということで、何にしようかと迷いましたが、エモい話をすることにしました。

その時のスライドがこれです。 speakerdeck.com

内容としては

  • 個人の強みを活かそう
  • その上でチームとして根幹の指針(共通認識みたいなもの)を皆で考えたい

例えば「困ったらコードで解決する」というチームの指針があったとします。何か問題が起こったときや改善したいときなどに「コードで解決するにはどうしたら良いだろう」という考えがまず自然と浮かんで浮かんできて、解決方法に悩んだり迷ったりする時間が減らせるのではないかという感じです。

資料としては全然洗練されてなくて、ただの提案LTですが気持ちは伝わったようでこうするとよさそうだ!とか話が広がってすごくよかったです。

思ったこと

最近、何事も小さく始めてみるというのが良いと思っています。大きくやろうとすると、それについて考えることが多すぎて時間がかかってそのうち熱が冷めてしまって結局なにもできなかったということになることが多いと感じます。

その1つとして小さくチーム内でLTを始められたことは良かったです。事業部全体で!会社全体で!ってなると途端にハードルが上がってしまって手を挙げづらいところがあるので、まずはこの感じで続けていきたいと思います。

Railsアプリを簡単にメンテナンス表示するにはturnout(+capistrano+whenever)が便利かもです

Railsアプリをメンテナンス表示にしたい時、みなさんどうされているのでしょうか。大規模サービスであればリバースプロキシでメンテ状態にするのが今風かもしれませんが、もっとお手軽にメンテナンスにしたいときはアプリケーション側でやってもいいかもしれません。

メンテナンス表示にする

turnoutというgemを使うと簡単そうです。

Gemfile

gem 'turnout'

tmp/maintenance.ymlというファイルがあればメンテナンス表示されます。 コメントとか許可するIPとかを書けるようです。

tmp/maintenance.yml

reason: 終了予定時間は 8/1 5:00 を予定しております。
allowed_ips:
  - 1.2.3.4

これだけでまずは全ページメンテナンスに出来ました。reasonに書いた文字列が表示されています。

f:id:kimromi:20160715142643p:plain

メンテナンス中の表示をカスタマイズしたい場合は、public/maintenance.htmlを置いておけばそのHTMLが表示されます。{{ reason }}と書いておくとyamlファイルで設定したreasonが展開されます。

public/maintenance.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <style type="text/css">
        <!--
        h1 { color: red; }
        -->
        </style>
    </head>
    <body>
    <h1>現在メンテナンス中です。</h1>
    <p>{{ reason }}</p>
    </body>
</html>

f:id:kimromi:20160715142651p:plain

簡単。

capistranoのタスクでメンテナンスの開始/終了を切り替える

まずは先程のyamlファイルをconfig/にでも置いておきましょう。

config/maintenance.yml

reason: 終了予定時間は 8/1 5:00 を予定しております。
allowed_ips:
  - 1.2.3.4

capistranoのタスクを書きます。

lib/capistrano/tasks/maintenance.rake

namespace :maintenance do
  desc 'メンテナンス開始'
  task :start do
    on roles(:web), in: :sequence do
      within current_path do
        # tmp/maintenance.ymlがなければ config/ から tmp/ にコピーする
        unless test(:test, "-f tmp/maintenance.yml")
          execute :cp, 'config/maintenance.yml tmp/maintenance.yml'
        end
      end
    end
  end

  desc 'メンテナンス終了'
  task :finish do
    on roles(:web), in: :sequence do
      within current_path do
        # tmp/maintenance.ymlがあれば削除する
        if test(:test, "-f tmp/maintenance.yml")
          execute :rm, 'tmp/maintenance.yml'
        end
      end
    end
  end
end

上のように定義しておくと

bundle exec cap production maintenance:start   # メンテナンス開始
bundle exec cap production maintenance:finish  # メンテナンス終了

のコマンド一発で開始/終了を切り替えられます!やった!

メンテナンスタイマーを仕込みたい

whenever gemでcronを仕込みましょう。やり方は同じです。指定した時刻にtmp/maintenance.ymlを置いたり消したりするだけです。

Gemfile

gem 'whenever', require: false

config/schedule.rb

set :output, "#{Whenever.path}/log/cron.log"
set :environment, :production
set :path, '/path/to/app/current'

# メンテナンス開始 (8/1 0:00)
every '00 00 1 8 *' do
  # config/maintenance.yml (copy ->) tmp/maintenance.yml
  command "cp #{Whenever.path}/config/maintenance.yml #{Whenever.path}/tmp/maintenance.yml"
end

# メンテナンス終了 (8/1 3:00)
every '00 03 1 8 *' do
  # remove tmp/maintenance.yml
  command "rm #{Whenever.path}/tmp/maintenance.yml"
end

あとはcapistranoでデプロイするときにwheneverを実行させましょう。

Capfile

require 'whenever/capistrano'

config/deploy.rb

set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
set :whenever_roles,      ->{ :cron }

config/deploy/production.rb

role :cron, %w(sshuser@app.server.com)

これでcapistranoでデプロイしたときにデプロイユーザにcrontabがつくので指定した時刻に実行されます!よい!

終わりに

unicornとかミドルウェアによっては再起動が必要と思うので、都度再起動をするような命令を入れておけば良さそうです。

アプリケーションまでアクセスすらきてほしくない時はこの方法ではダメなのでもっと手前のレイヤーでメンテナンス状態にしてください。

まぁでもcronって定期的に何かを実行させたりするやつなんでこういう1回きりのタイマーに使っていいのかというのもあるので、なんかいい案があれば教えてください!

Capistranoでデプロイした時にGithubのどのPullRequestをリリースしたかをSlackに通知する

f:id:kimromi:20160709032809j:plain

上のような感じで、Capistranoでデプロイした時にどのPullRequestがリリースされたかをSlackに通知するGemを作りました。

github.com

Capistrano v3 のみ対応です。masterブランチにマージされた最新のPullRequestを取得して通知しますので、デプロイするときは一旦masterブランチに切り替えてgit pullしてからデプロイすると間違いないです。

@linyows さんが作ったcapistrano-github-releasescapistrano-slack_notificationに完全に依存しています。ありがとうございます!

セットアップ

Gemfile

gem 'capistrano-releases-notification'

Capfile

require 'capistrano/github/releases'        # 依存
require 'capistrano/slack_notification'     # 依存
require 'capistrano/releases/notification'

config/deploy.rb

# slack webhook
set :slack_endpoint, 'https://hooks.slack.com'
set :slack_path, '/services/xxxxxxxxx/yyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzz'

# slack通知設定
set :release_notify_channel, ['#general']   # 通知チャンネル名(複数指定可)
set :release_notify_mention, ['@kimromi']   # メンション(複数指定可)

# Github Enterpriseの場合
Octokit.configure do |c|
  c.api_endpoint = 'http://your.enterprise.domain/api/v3'
  c.web_endpoint = 'http://your.enterprise.domain/'
end

config/deploy/production.rb

# デプロイ終了後に通知する設定
after 'deploy:finishing', 'release:notify'

必要なstageで追加してください。

使いみち

メッセージやタイトルも編集できますので、我がムームードメインではカスタマーサービスチームにメンションを飛ばして何がリリースされたかを通知して共有するようにしています。下の例ではPullRequestの内容まで取得しています。

f:id:kimromi:20160709032819j:plain

set :release_notify_mention, ['@cs']
set :release_notify_title, 'ムームードメイン'
set :release_notify_message, -> {
  "リリースしました。 #{fetch(:release_notify_mention).join(' ')}"
}
set :release_notify_attachment, -> {
  pull_request = Octokit.pull(fetch(:github_repo), fetch(:pull_request_id))
  [
    pull_request.title,
    pull_request.html_url,
    '------------',
    pull_request.body[0, 200]
  ].join("\n")
}

お試しあれ〜