LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

経験者が送る新卒入社後あたふたしないために心がけたい5箇条

はじめに

みなさんこんにちは。 16新卒入社したネイティブアプリチームの井上です。
2017年4月に入社された新卒のみなさんは、研修を経て現場での業務が始まるor始まっている頃かと思います。
正直言うと、私はチームに配属直後 とっってもあたふたしてました

〜配属前の私〜
「たくさん学んで早く活躍するぞ! (^ω^)」

〜配属後の私〜
「逃げちゃダメだ。逃げちゃダメだ。逃げちゃダメだ。うわあああああ(‘ω’)あああぁぁぁぁぁ」

はい、リアルにこんな感じでした。
例えば以下のようなことをやらかしてました。

  • リリース期日に実装が間に合わない
  • リリース後、実装箇所で落ちる
  • 自分のミスを自分でフォローできない
  • ディスカッションで発言できない
  • ブログ書いたらほぼ全直し(←new 本記事)

などなど…。

きっと新卒入社される方の中には、やる気に満ち溢れていて、これからが楽しみで仕方がないという人がたくさんいると思います。

そこで本記事では、みなさんにぜひ心がけてほしい点を「配属直後にあたふたしないための処方箋」として、私自身の経験を元にまとめていきたいと思います。

配属後のスタートダッシュで転ばないために参考にしてください。

アプリチームの仕事

実際に経験した業務は以下のようなもでした。
それぞれ簡単に内容を紹介していきます。

  • 施策検討
  • iOSアプリの新規開発
  • Android・iOSアプリの改善・グロース
  • APIの実装

施策検討

 プロダクトに携わる以上は、そこにどんな課題があり、どういう価値を提供すべきなのかということを、チームの一人一人が自分自身の中でしっかり理解し、納得することがとても重要です。弊社アプリチームの場合、職種関係なくチーム全員でさまざまなディスカッションを行い、「解消すべき課題は何か」「目的・手段は適切か」「いつまでにやるべきなのか」等を検討します。

iOSアプリの新規開発

 2016年秋頃からiOSアプリを開発することになり、私もそこにjoinしました。iOSアプリはSwiftで実装し、ライブラリの選定や基盤処理の実装、画面作成・メインロジックと幅広い範囲の業務を経験しました。
 この期間は余裕がなくかなり忙しかったですが、業務としてアプリの新規開発に携わるという非常に貴重な経験ができました。

アプリの改善・グロース

 日常的に行う業務のメインはAndroid・iOSアプリの改善とグロースです。施策検討時に決定した内容を、タスクとして各担当者に振り分け開発を進めていきます。
 チーム内のエンジニアはOSごとに担当が決まっているわけではなく、全員が両OSをカバーしています。施策によっては一時的に専任担当をつけるときはありますが、基本的にはどちらの開発も行うため、両OSの開発スキルが必要とされます。

web APIの実装

 弊社のアプリでは、サーバーとのデータのやりとりにweb APIを利用しているため、その開発・保守もアプリチームの担当です。
 web APIはRuby on Railsで実装されており、Android Java・Swifttの知識だけでなくサーバーサイド開発の知識も必要です。

働いて気づいた現実

f:id:omega999:20160227172243j:plain

配属前に、「やりたいこと」「やること」については事前に伺っており、私自身配属が楽しみでした。しかしいざ業務が始まってみると、

〜業務内容を聞いた私〜
「いっぱいやることある楽しそう、幅広く学べそう、グロースグロースぅ!(˘ω˘)」

〜業務が始まった後の私〜
「開発時間かけすぎた、アイディアまとまらない、ちょ時間足りない、助けて神様(˘ω˘;)」

はい、見事に理想と現実のギャップに悩まされました。
納得できる成果が見えなかったため、なかなか自信がつきませんでした
当時、特に悩んでいたことは以下のようなものです。

  • 満足できないアウトプット
  • スムーズに定着しないスキル

満足できないアウトプット

〜タスクが決まった私〜
「来週までに完成だから前日には完成させて別のタスクもやろう(˘ω˘)」

〜タスク締め切り前の私〜
「あれバグが、これ落ちるじゃんか、何やってんだ自分(˘ω˘;)」

 当時は目の前にある一つのタスクにしか手を付けられず、しかも十分な質・スピードで対応できなかったため、自分の成果に自信がをもてない状態でした。
 チーム配属後の最初のタスクは思ったよりも複雑で、予定よりもかなり時間を要してしまいました。その後のタスクでは、リリース後に不具合が発生したこともあり、とっても凹んでいたのを覚えています。
 自分がプロダクトにどういう貢献ができているかを考えたとき、何ができているんだろう?とモヤモヤしてました。チームに貢献できていないのではないかという思い込みで、1人勝手に悩んでいました。

スムーズに定着しないスキル

〜学び始めた私〜
「アプリ・サーバーサイド両方必要だからたくさん覚えないと(˘ω˘)」
〜学んできた私〜
「Android開発してたら、iOS開発忘れた、あれAPIも、おいおい身についてるのかこれ(˘ω˘;)」

 もともと私はAndroidアプリ開発経験がなかったため、配属直後はわからないことだらけでした。iOSは少しかじってましたが業務レベルだと全然通用せず。 さらに一つのことを覚えて、一つのことを忘れる時もありました。
 「何がわからないのかわからない」状態で、覚えなければいけないことが多すぎ、コーディングを楽しむと言うよりは、タスクをこなせるだけの知識を身に着けようと必死でした。
 十分に内容を理解し、キレイなコードで実装することが理想でしたが、当時は熟考するだけの余裕がなく、納期も守れず非常に悔しかったです。その後も自分で納得できるような仕事をできるようになるまでには、時間がかかりました。

あたふたしないために心がけたい5箇条

f:id:omega999:20170305224358j:plain

毎日がむしゃらに過ごしてきて、気づけばあっという間に1年が経っていました。
改めて振り返ると、当時もっと心がけておくべきだったなと思うことがいろいろ出てきます。
こうした経験から学んだことを今回は5つ紹介します。

  • 今の成果を認める
  • 最短最大の学習をする
  • 「マイペース」を作る
  • 好奇心を育む
  • 手段を正当化しない

今の成果を認める

〜今の成果を認められた私〜
「画面のデザインできた、ロジックも実装、MTGでも発言、前より成長している感じある(^ω^)」

 当時はアウトプットの質に満足いかないことが多かったですが、不完全であろうと、あるいは締め切りに遅れようとも、そこで何かしらの成果は出してきました。こだわりきれず、納得できない、という否定的な思いばかり抱く時もあると思います。
 そのような時には、まず、 現状の力を理解するために今の成果を認めることから始めるべきです。私はタスクが終わるごとに振り返りをし、そこからの学び等を整理しました。

  • 技術的に学んだこと
  • 意識的に改善したこと
  • つまずいたこと

 1つのタスクをこなし、それによって、自分がどこまでできるようになったのかを把握することはとても重要です。現状を理解すれば問題もわかるはずです。私の場合はチームメンバーからFBをもらうことができ、客観的に自分を評価できました。もし自分で成果を認められないと悩んでしまうなら、周りの方々にFBをお願いしてみると良いと思います。
 このように現状を理解する積み重ねがあるからこそ、学びを理解し成長を感じられるようになると思います。

最短最大の学習をする

〜最短最大の学習を意識した私〜
「効率的に業務を進められて、タスク間に合ってる、作業も早くなった(^ω^)」

 私は以前から、疑問に思ったことを調べるために多くの時間をかけがちで、業務の中でも答えが見つからないものを延々と調べていたことが何度もありました。それは実はプロダクト独自の仕様であったり、まだ情報が出回っていない不具合だったりしました。もう少しで答えが見つかる気がする、そんな思い込みで多くの時間を浪費してしまい、そしていつの間にか危険信号が点滅してしまっているのです。
 まずは 目の前の課題をどうすれば最短で解決できるのか、それを考えることが重要です。手段は多くありますが、まずはその調査方法が最短経路かを一度考え直してみましょう。

  • チームメイトに聞く
  • ネットで調査
  • 社内wikiから探す

 これまでは理解するまで時間をかけて解決してきましたが、業務という限られた時間の中ではその方法では効率が悪い時もあることに気づきました。 学習効率の非効率性を限りなく排除し、最短最大の学習方法を選択することが重要です。
 そして、当時周りの方々からよく言っていただいた 「わからないことは聞いてください」という言葉の本質はここにあるのだと理解できました。
 時間をかけるべきもの、時間をかけるべきではないもの、その判断を効率よく行うための方法にたどり着くには、自分ひとりの力では難しいこともあります。

「マイペース」を作る

〜マイペースを意識した私〜
「前より覚えがスムーズになった気がする、技術も吸収してる、学習に余裕がある感じ(^ω^)」

 業務のことで悩んでばかりいると、考えることに疲れてしまい、その結果、当時は自分の学習ペースが完全に乱れてしまっていました。気持ちが焦ってばかりで単純に疲弊していたんだと思います。未熟であるから焦るというのは良い刺激ですが、空回りしてしまっては問題の解決には至りませんし、むしろ悪化させる一方です。
 最短最大の学習をすることと上記で述べましたが、その 自分にあった学習方法を習慣化し維持できるようにマイペースを保つことが重要です。足りないと思っていた時間もペースを整えればうまく調整できることもあります。例えば私は以下の項目を見直してマイペースを整えました。

  • 生活リズムを整える
  • 自由時間を決める
  • 趣味の開発をする

 ちなみにマイペースとは無茶をしないということではありません。時には無茶だと思える挑戦も必要です。そういうときは計画的な無茶をすべきです。業務だけに浸らずに、やりたいことをこなし、学習意欲とモチベーションを維持していくことが重要です。

好奇心を育む

〜好奇心を取り戻した私〜
「こういう設計にすれば良いのか、早く家帰って作業しよう(^ω^)」

 入社してからずっと思っていましたが、弊社エンジニアの方々は技術に対する好奇心がとても強いなと思っていました。(弊社に限ったことではなく、エンジニアって好奇心の強い人が多いと思いますが)
 私のペースが乱れていた時を振り返ると、学びたいことよりも必要なことを優先して学習し、気持ちの余裕があまりなかったです。 自分の興味・関心の幅を広げ好奇心を育むことは、学習のモチベーションに直結しますし、なにより充実感を得られますよ。なのでアプリ開発業務以外のことをやる時間を増やしました。

  • 業務以外の開発
  • 技術書を読む
  • webの技術調査

 当時は「何を学ぶべきか」に迷ってなかなか学習もスムーズにいかなかったのを覚えています。業務優先ばかりで、好きなことをおろそかにすることはやめましょう。技術をもっと好きになるため好奇心には素直に向き合っていきましょう。

手段を正当化しない

〜手段を正当化しなくなった私〜
「これって最適?要件整理しなおそう、実装時間かかるな、今は人に聞こう、柔軟に対応できるようになった(^ω^)」

 目的を言語化し、目の前に掲げて行動することは重要なことです。一方で、目的設定をせず手段ベースで物事が進んでしまうのは、ゴールを見失うことになってしまい注意が必要です。
 私は焦って仕事をしていた時、形式的なことだけを重視した手段ベースの考えで行動してしまい、目的を後付けしてしまう時がありました。しかし、後付の目的設定をすると 手段が正当化されてしまい目的を達成するための手段が適切であったかの判断ができなくなります。そうならないためには、目的等をもう一度考えてみましょう。

  • 達成すべき目的はなにか
  • 得られる効果はなにか
  • 今の状況で最も良い手段はどれか

 振り返ると、あのときは別の方法を取った方が良かっただろうと思うことが多々あります。目的の理解が十分でないときには注意が必要です。
 このように、後付けの目的設定をしてしまっていると気付いた時は、何かを見落としているかもしれないと考え、その場面で最も良い手段を選択できるように心がけることが重要です。

最後に

 1年を振り返ると、自分はダメダメだったということがわかりました。そして今はそれらが改善されているかのように本記事を書いていますが、まだまだ未熟者です。
 ですが、社員皆様のお力添えのお陰でできることは増え着実に成長していることに喜びを感じています。いつもご指導ありがとうございます。

 そして新卒の皆さんはあたふたせず自分のやりたいことを実現していってください!
 私と同じようにうまくいかないなーと思った方の助けになれば幸いです。

おまけ(開発しているアプリ)

f:id:omega999:20170816180201j:plain:w115f:id:omega999:20170816180211j:plain:w115f:id:omega999:20170816180218j:plain:w115f:id:omega999:20170816180221j:plain:w115f:id:omega999:20170816180223j:plain:w115

弊社アプリの紹介をさせていただきます。 業務として担当したアプリについてですが、当時から弊社では転職ナビ(旧:ジョブセンスリンク)のアプリが公開されていました。
求人を評価することができる機能や、希望にあった求人を探しやすくする機能を取り入れており、分かりやすいUIとサクサクとした動作で、とても使いやすいアプリだと思います。
当時はAndroidアプリのみでしたが、2017年1月にはiOS版も公開し、今では多くのユーザーに利用されています。 ぜひみなさんも利用してみてください。

play.google.com

itunes.apple.com

Gitのコミットの裏側で起こっていること

はじめまして。1ヶ月でエンジニアになろうとした山浦です。

先日Gitのことを突っ込んで調べる機会があり、Gitの仕組みって面白いねということを同僚に話していたら「面白いね。ところでGitって実装できる?実装できないと分かったとは言えないよね?」となぜか煽られるということがありました。

そうか、実装できないと分かったとは言えないのか、それも一理あるかもしれない。そう思い、Gitの仕組みを実装できるレベルまで掘り下げて調べてみました。

今回は実装はしないものの(過度に記事が複雑になるので)、Gitの根幹である git add コマンドと git commit コマンドの裏側で起こっていることを紹介します。

差分かスナップショットか?

ここで早速クイズです。

コミットで保存されているのはソースコードの差分でしょうか?スナップショットでしょうか?

今回の記事の中で解説していきますので、少し考えながら読み進めてみてください。 ちなみにこのデータの持ち方がGitと他のバージョン管理システム (Subversion等) との大きな違いだったりします。

git add コマンドの裏側で起こっていること

それでは、git add コマンドでインデックスに登録する裏側で何が起こっているのかをまず見ていきます。

$ git init sample
$ cd sample
$ echo 'Hello, world!' > sample.txt
$ git add sample.txt

git add コマンドを実行すると裏側では、①圧縮ファイルが作成され②インデックスに追記されます。これらを順に見ていきます。

f:id:livesense-made:20170813134138p:plain

①圧縮ファイルが作成される

git add コマンドを実行すると、addしたファイルを圧縮したファイルが .git/objects 以下に保存されます。

$ find .git/objects -type f
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b

これが先ほどの git add コマンドで追加されたファイルです。 このファイルはblob (カタマリ) オブジェクトと呼ばれていて、ファイルの内容を圧縮したものになります。

どのようにblobオブジェクトを作成しているかというと、まず、ファイルの内容にヘッダーを付け加えたものをSHA1でハッシュ化します (それがblobオブジェクトのID)。IDの先頭2文字をサブディレクトリの名前に、残り38文字をそのディレクトリ内のblobオブジェクトのファイル名にします。そして、ファイルの内容にヘッダーを付け加えたものをzlibライブラリを用いて圧縮し、blobオブジェクトのファイルに書き込みます。こうやってblobオブジェクトは作成され、ファイルが圧縮保存されます。

f:id:livesense-made:20170820100304p:plain

なお、blobオブジェクトの圧縮前の内容を確認したい時は、blobオブジェクトのIDに対して git cat-file -p コマンドを使うことで確認できます。

$ git cat-file -p af5626b4a114abcb82d63db7c8082c3c4756e51b
Hello, world!

さて、ここでファイルに追記して git add コマンドを実行するとどうなるでしょう。

$ echo 'Good morning.' >> sample.txt
$ git add sample.txt
$ find .git/objects -type f
.git/objects/67/dcebe5e80cb4513b614624763ce08cf3346d8f # 新規追加されたファイル
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b # 1回目のgit addで生成されたblobオブジェクト

すると、元々のblobオブジェクトは残ったまま、もう一つのファイルが追加されています。このファイルの中身はいったい何でしょう?

$ git cat-file -p 67dcebe5e80cb4513b614624763ce08cf3346d8f
Hello, world!
Good morning.

現状のsample.txtの内容が圧縮されていました。このように、git add コマンドを実行すると、その時点でのファイルの内容全部がblobオブジェクトという形で圧縮され記録されます。

ここでblobオブジェクトに関する重要な点をまとめます。

  • blobオブジェクトのIDはSHA1ハッシュで作成されているため、blobオブジェクトはファイル名に関係なく内容が同じなら同じIDになります。
  • blobオブジェクトのデータベースは追記のみで更新されません。イミュータブルなオブジェクトです。(ファイルを変更・削除してもblobオブジェクトは変更・削除されません)

②インデックスに追記される

ファイルはblobオブジェクトの形式で圧縮保存されるわけですが、blobオブジェクトはファイルの内容を圧縮しただけで、ファイル名の情報をどこにも保持していません。

そこで、ファイルの構造と名前を保持するために登場するのがインデックスです。

git add コマンドを実行すると、blobオブジェクトが作成された後、インデックスに追記されます。インデックスは、.git/index というバイナリファイルで管理されています。プロジェクトのある時点でのディレクトリツリー全体を表すデータを持ちます。

インデックスの中身を確認してみましょう。git ls-files –stage コマンドで .git/indexの内容を見ることができます。

$ git ls-files --stage
100644 67dcebe5e80cb4513b614624763ce08cf3346d8f 0       sample.txt

左からファイルモード (パーミッション)、(blob) オブジェクトのID、ステージ、ファイルパスが表示されています。ステージはマージコンフリクトのためにある数値です。通常0ですが、マージコンフリクトが起きた場合、ベースバージョン、一方のブランチのバージョン、他方のブランチのバージョンの3つをそれぞれステージ1、2、3としてインデックスに保持します。

このように、git add した時点でのblobオブジェクトのIDとファイルパスを紐付けて管理することで、ディレクトリーツリーの情報を保存します。

インデックスには次の特徴があります。

  • 次のコミットの準備をする場所。git commit すると、インデックスの情報を元にコミットされます。
  • バイナリファイルにパーミッション、オブジェクトのID、ファイルパスを持ちます。
  • treeオブジェクトの鋳型となります。treeオブジェクトに関してはこの後紹介します。

以上の2つが、git add コマンドでインデックスに追加する際に裏側で起こっていることです。

git commit コマンドの裏側で起こっていること

さて、ここからはコミットについて見ていきます。

$ git commit -m "first commit"
[master (root-commit) ef56b89] first commit
 1 file changed, 2 insertions(+)
 create mode 100644 sample.txt

git commit コマンドを実行すると裏側では、③treeオブジェクトが作成され④commitオブジェクトが作成され⑤ブランチの先頭を指すリファレンスが書き換えられます。

f:id:livesense-made:20170813153434p:plain ※ 図は①〜④まで

③treeオブジェクトが作成される

git commit コマンドを実行するとまず、treeオブジェクトが .git/objects 以下に保存されます。

$ find .git/objects -type f
.git/objects/2f/b1bd43dc899bcb3d8c1245e359716459ad992a # 今回追加されたファイル
.git/objects/67/dcebe5e80cb4513b614624763ce08cf3346d8f # 2回目のgit addで生成されたblobオブジェクト
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b # 1回目のgit addで生成されたblobオブジェクト
.git/objects/ef/56b8952e72e3be4b06f61ecef53a9d282a4568 # 今回追加されたファイル

今回追加されたファイルのうち、.git/objects/2f/b1bd43dc899bcb3d8c1245e359716459ad992a を先に見ていきましょう。このファイルは、treeオブジェクトと呼ばれているものになります。

# -t オプションを付けるとオブジェクトのタイプを表示します
$ git cat-file -t 2fb1bd43dc899bcb3d8c1245e359716459ad992a
tree

# -p オプションを付けるとオブジェクトの中身を表示します
$ git cat-file -p 2fb1bd43dc899bcb3d8c1245e359716459ad992a
100644 blob 67dcebe5e80cb4513b614624763ce08cf3346d8f    sample.txt

treeオブジェクトは、左からファイルモード(パーミッション)、オブジェクトのタイプ、オブジェクトのID、ファイルパスが表示されています。中身がインデックスに似ていますね。 treeオブジェクトもインデックスと同じように、オブジェクトのIDとファイルパスを紐付けることで、ディレクトリーツリーを保持するためのものになります。

インデックスとの違いは、新たにコミットするたびにtreeオブジェクトも追加で作成されるという点です。コミットすると、そのたびにtreeオブジェクトとcommitオブジェクトが作成され、その時点でのディレクトリーツリーが毎回保存されます。そうすることでGitは変更履歴を保存しているのです。それに対してインデックスは、インデックスした時点で .git/index を毎回上書します。あくまでインデックスは、変更をまとめてコミットするための準備をする場所です。インデックスすると .git/index にディレクトリーツリーが上書きされ、コミットするとその時点での .git/index の情報を元にtreeオブジェクトが作成されるわけです。

次に、ディレクトリを追加するとtreeオブジェクトがどのようにディレクトリーツリーを保存するのかを見ていきましょう。

$ mkdir src
$ echo 'main file.' > src/main.txt
$ git add src
$ git commit -m "add directory"
[master ebf5ca0] add directory
 1 file changed, 1 insertion(+)
 create mode 100644 src/main.txt

# masterブランチ上での最後のコミットが指しているツリーオブジェクトの中身を表示します
# git cat-file -p 9a49569a4956b912f7ea59f0efbdb4a5c4d18a19aee9bb でも同じです
$ git cat-file -p master^{tree}
100644 blob 67dcebe5e80cb4513b614624763ce08cf3346d8f    sample.txt
040000 tree 94d5c7eab249212c58445ace5acadf10ed991e0c    src

# srcのtreeオブジェクトの中身を表示します
$ git cat-file -p 94d5c7eab249212c58445ace5acadf10ed991e0c
100644 blob 258dda95ffff5919f3ea5894c3bfaafdf225bf57    main.txt

上記のtreeオブジェクトの構造を図で表すと、以下のようになります。

f:id:livesense-made:20170813162224p:plain

このようにtreeオブジェクトは、1つ以上のblobオブジェクトかtreeオブジェクトを保持する階層構造になっています。そして、treeオブジェクトからblobオブジェクトをたどることで、その時点のスナップショットを確認することができます。

ここでtreeオブジェクトの特徴をまとめます。

  • コミット時に作成されます。
  • 構造や名前をもたないblobオブジェクトにファイルシステムとしての構造を与えます。
  • blobオブジェクトかtreeオブジェクトを保持しています。

④commitオブジェクトが作成される

さて、treeオブジェクトにより、コミット時のディレクトリーツリーが分かるようになりました。

しかしまだ、そのコミットを誰が、いつ、何のために変更したのかという情報がありません。それを保持するのがcommitオブジェクトです。

コミットすると、treeオブジェクトが作成された後に、.git/objects 以下にcommitオブジェクトが作成されます。

$ find .git/objects -type f
.git/objects/25/8dda95ffff5919f3ea5894c3bfaafdf225bf57 # 3回目のgit addで生成されたblobオブジェクト
.git/objects/2f/b1bd43dc899bcb3d8c1245e359716459ad992a # 1回目のgit commitで生成されたtreeオブジェクト
.git/objects/67/dcebe5e80cb4513b614624763ce08cf3346d8f # 2回目のgit addで生成されたblobオブジェクト
.git/objects/94/d5c7eab249212c58445ace5acadf10ed991e0c # 最新のコミットで追加されたtreeオブジェクト
.git/objects/9a/4956b912f7ea59f0efbdb4a5c4d18a19aee9bb # 最新のコミットで追加されたtreeオブジェクト
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b # 1回目のgit addで生成されたblobオブジェクト
.git/objects/eb/f5ca03e94fe2244bb784fc4fddabf80e27a2e7 # 最新のコミットで追加されたcommitオブジェクト
.git/objects/ef/56b8952e72e3be4b06f61ecef53a9d282a4568 # 1回目のgit commitで生成されたcommitオブジェクト

# git cat-file -p HEAD でも同じ。最新コミットのcommitオブジェクトの内容を表示します
$ git cat-file -p ebf5caebf5ca03e94fe2244bb784fc4fddabf80e27a2e7
tree 9a4956b912f7ea59f0efbdb4a5c4d18a19aee9bb
parent ef56b8952e72e3be4b06f61ecef53a9d282a4568
author Harry <harry@author.co.jp> 1502608263 +0900
committer Harry <harry@author.co.jp> 1502608263 +0900

add directory

commitオブジェクトの中身は、コミットが作成された時点のトップレベルのツリー、親コミット、作者とコミッターの情報、空行、コミットメッセージです。このように、ツリーを保存することでコミットした時点のスナップショットが、作者とコミッターの情報からいつ誰が、コミットメッセージからなぜ変更したのかということが分かるようになっています。

なお、親コミットとは直前のコミットになります。親コミットの内容も確認してみましょう。

# parentに記載されているcommitオブジェクトの内容を表示します
$ git cat-file -p ef56b8952e72e3be4b06f61ecef53a9d282a4568
tree 2fb1bd43dc899bcb3d8c1245e359716459ad992a
author Harry <harry@author.co.jp> 1502606667 +0900
committer Harry <harry@author.co.jp> 1502606667 +0900

first commit

今回の親コミットは、最初のコミットになります。そのコミット内容が表示されています。

ここまでのGitリポジトリ内のオブジェクトの様子を図でまとめます。

f:id:livesense-made:20170813170631p:plain

Gitはcommitオブジェクトにtreeオブジェクトを保持することで、その時点でのスナップショットが分かるようにしています。 加えて、直前のコミットを親コミットとして保持しておくことで、コミットの履歴を辿れるようにしているわけです。これがGitのバージョン管理の根幹をなしている仕組みです。

ここでcommitオブジェクトの特徴をまとめます。

  • コミット時点でのスナップショットを示すオブジェクトです。treeオブジェクトのIDを持っていて、それがスナップショットの情報になります。
  • parentを持っていて、履歴をたどることができます。
  • なお、マージコミットの場合はparentを2つ持つことになります。

⑤ブランチの先頭を指すリファレンスが書き換えられる

git commit コマンドを実行すると、commitオブジェクト作成後に、現在のブランチの先頭を指すリファレンスが書き換えられます。

そもそも現在のブランチの情報はどこで保持されているかというと、HEADになります。HEADとは、現在使用しているブランチの先頭を表していて、.git/HEAD に保存されています。早速確認してみましょう。

$ cat .git/HEAD
ref: refs/heads/master

これを見ると、HEADは refs/heads/master へのリファレンスになっていることが分かります。HEADというのは、現在のブランチへのリファレンスで、ブランチをチェックアウトしたタイミングで書き換えられます。

では次に、refs/heads/master を確認してみます。

$ cat .git/refs/heads/master
ebf5ca03e94fe2244bb784fc4fddabf80e27a2e7

$ git cat-file -p ebf5caebf5ca03e94fe2244bb784fc4fddabf80e27a2e7
tree 9a4956b912f7ea59f0efbdb4a5c4d18a19aee9bb
parent ef56b8952e72e3be4b06f61ecef53a9d282a4568
author Harry <harry@author.co.jp> 1502608263 +0900
committer Harry <harry@author.co.jp> 1502608263 +0900

add directory

refs/heads/master は、masterブランチの先頭のcommitオブジェクトへのリファレンスになっていました。

Gitはコミットすると、refs/heads/master に最新コミットのIDを記載することで、現在のブランチの先頭コミットがどれなのかを記録しているのです(ブランチが変わると、refs/heads/ 以下が該当ブランチ名に変わります)。

まとめ

さて、はじめにコミットで保存されているのはソースコードの差分かスナップショットか、というクイズを出したのを覚えていらっしゃるでしょうか?

コミットで保存されているのは「スナップショット」でした。

git add コマンドを実行すると①圧縮ファイルが作成され②インデックスに追記されます。
git commit コマンドを実行すると③treeオブジェクトが作成され④commitオブジェクトが作成され⑤ブランチの先頭を指すリファレンスが書き換えられます。
commmitオブジェクトがtreeオブジェクトのIDを保持することで、コミット時点のスナップショットが分かるようになっているのでしたね。

ちなみに、他のバージョン管理システム (Subversion等) の多くは、基点とするバージョンへの差分としてデータを保持しています。Gitが差分ではなくスナップショットとしてデータを持っていることは、ブランチを運用する際に特に大きなメリットとなります。多くのバージョン管理システムの場合だと、データを差分として保持しているため、ブランチの作成時に全ファイルを新たなディレクトリにコピーする必要があり、ブランチ作成に長い時間がかかります。それに対してGitの場合だと、データをスナップショットとして保持ているため、ブランチの作成時はブランチのリファレンスを追加するだけですみ (ref/heads/ブランチ名 のファイルにcommitオブジェクトへのリファレンスを指定するだけ)、非常に高速です。

Gitの実態は、今回見てきた3つのオブジェクト(blobオブジェクト、treeオブジェクト、commitオブジェクト。これらをGitオブジェクトと呼びます)を中心に構成されています。もちろん他にもリファレンスやタグ等もありますが、これがGitの一番コアな部分です。 Gitのコマンドや操作の多くは、これら3つのオブジェクトに対して何らかの操作をしているだけです。

今回Gitを詳しく調べてみて、Gitは大変シンプルな構成だと思いました。本内容が皆様のGitライフの一助になれば幸いです。

参考文献

9つのWebサイトモニタリングサービスの使用感まとめ

  • はじめに
  • モニタリングサービスまとめ
    • No.1 Site24x7
    • No.2 StatusCake
    • No.3 Pingdom
    • No.4 UPTRENDS
    • No.5 GTMetrix
    • No.6 DareBoost
    • No.7 NodePing
    • No.8 Monitoring Plus
    • No.9 UptimeRobot
  • まとめ
  • おわりに

はじめに

今年4月に中途で入社したインフラストラクチャーグループ所属の内河です。 最近、ジョブセンスのインフラ担当になりました。 オンプレのサーバを触りつつAnsibleのPlaybookを書いたり、AWSのリソース設定をTerraform化しています。

現在、リブセンスではサーバの監視・外形監視にはMackerelを、WebサイトのモニタリングにはNewRelicやDatadogを利用していることが多いです。 しかし、こういったサービスの移り変わりは早く、新しいサービスが出ていたり、既存のサービスも使い勝手が変わっているかもしれません。 今回の記事は、9つのモニタリングサービスについて以下の要件を満たしているか、実際に触ってみた感想や費用について纏めてみました。

  • UIはグラフィカルで直感的か
  • 以下の点をモニタリング出来るか
     1. レスポンスタイムが分かるか
     2. アップタイムが分かるか
     3. DOMContentLoadedやLoad時間が分かるとベター

※ 有名なMackerelやNewRelicなどは除いております。ご了承ください。
※ 記載した費用は2017/08/10日現在の割引を適用していない費用に基いています。

それでは実際に見てみましょう。

続きを読む

Livesense式 開発合宿マニュアル part2

f:id:taise:20170708180350j:plain

就活会議ユニットのエンジニアをしている大政です。
最近、データ分析基盤を開発する部署から異動になりました。

開発合宿、やっていますか?
半年前に、Livesense式 開発合宿マニュアル part1という記事を書きましたが、今回はその続きです。

開発合宿を有意義なものとするために、いろいろな試行錯誤がありました。
3年間やってきた中で得られたノウハウを共有できればと思います。

開発合宿アンチパターン

  • 会場に到着してから何を作るか考え始める
  • 持ち物の準備不足
  • 旅行として満喫してしまう

会場に到着してから何を作るか考え始める

各自でもくもく取り組むタイプの開発合宿で発生しやすいです。
いいものが考えつかないまま、あれこれ探している間に何時間も過ぎてしまい、気づいたときにはタイムアップ、という流れです。

開発合宿は、会場への移動や食事などで、自由に開発できる時間は意外と限られています。
その貴重な時間は、全て開発につぎ込まなければ、作りたいものを完成させられずに終わってしまいます。

持ち物の準備不足

宿泊施設によっては、電源タップがないため交代で充電しないといけなかったり、Wi-Fiありとなっていても、回線が弱くてみんなでつなぐとまともに使えないケースもあります。
開発に集中するためにも、環境を整えるための準備が大事です。

旅行として満喫してしまう

開発合宿は、温泉地や海辺などで行うことが多く、誘惑が多いです。
ともすると、1日中遊んでしまって開発が疎かになってしまう、ということもありがちです。

気分転換は必要ですが、開発合宿で遊びすぎてしまうと後ろめたさはあるし、開発時間がもったいないです。
開発時間と遊ぶ時間は、ある程度参加者全員で揃えておいたほうが無難です。

有意義な合宿にするために

合宿のしおりをつくる

リブセンスの開発合宿では、集合場所や開催場所、移動ルートや費用などをざっくりまとめた合宿のしおりを作っています。
参加者で事前に集まってしおりの読み合わせをすると、合宿に行く機運が高まります。しおりを作るタイミングで、開発時間と遊ぶ時間をある程度決めておくと、気持ちよく過ごせるでしょう。

また、後述するさまざまな点で、しおりの読み合わせは役に立ちます。

f:id:taise:20170802143330p:plain

持ち物リストを作る

前回ご紹介した開発合宿施設は、至れりつくせりで困ることはほとんど無いと思います。とはいえ、事前に持ち物リストを作ってちゃんとチェックしておくことに越したことはありません。

例えば見過ごしがちな持ち物として、ヘッドホンがあります。
会議室を借り切って開発をする場合、他の参加者の会話やBGMが気になる方もいるでしょう。 その際にヘッドホンがとても役立ちます。

MacのACアダプタは、比較的短いため、あまり自由に動けなかったり、延長コードの形状によっては、Macのアダプタがあまりつなげられない場合もあります。
その際に、Macを購入するとついてくる延長コードがあると大変便利です。

リブセンスでは以下のような持ち物リストを使っています。

  • PC*
  • ACアダプタ*
  • お泊りセット*
  • 携帯の充電ケーブル*
  • 電源アダプタ 延長コード (Mac)
  • ヘッドホン
  • お酒 🍶

*required

この他、ボードゲームを持っていったりするメンバーもいます。 合宿のしおりに、個人の持ち物と、全体で分担する持ち物を書いておくとスムーズです。

お酒の持ち込みは、宿泊施設によっては禁止されていたり、持ち込み料が必要なケースがあります。 事前にご確認ください。

出発前に何をやるか決める

会場に到着する前に、何をやるか決めてもらうことが大事です。
しおりの読み合わせをした時に、参加者に一人ひとりやることを聞いていき、 決まっていない人はその場で一緒に考えつつ、必ず決めてもらうようにしています。

当日までに作るものが変わっていても構わなくて、考える切っ掛けを事前に作ることが大事だと思っています。

中間発表/成果発表

合宿中に発表の場を作るのはとても重要です。
多くの人は締切がないと頑張れません。

初めてやった開発合宿は、各自がそれぞれだらだら開発してなんとなく終わってしまいました。 日々の進捗は中間発表で、最後は成果発表で自分のやったことを他の参加者に説明すると決まっていると、 何らかの形で動くもの・成果が見えるものを作りたくなります。

継続するために

これまでのノウハウを活かして開発合宿がうまく行ったら、ぜひ2回、3回と続けたいものです。 最後は継続のための工夫をお伝えしたいと思います。

  • 定期開催の流れを作る
  • 運営を複数人で分担する
  • 個別に勧誘する

定期開催の流れを作る

「毎年何月に開発合宿をやる」と決めておくと、それに合わせて開催の準備をすることができて、自然と定期開催することが来ます。
リブセンスでは大体6月と11月に開催しています。宿の繁忙期を避けるためです。

運営を複数人で分担する

最初期は1人で運営するのもやむを得ないかもしれませんが、日程調整から宿の予約など、やることは結構多いため大変です。 自分1人でやりきろうとすると、自分の気持ちが切れたときに、開発合宿も開催されなくなってしまいます。

参加メンバーに相談して一緒に準備してもらうと良いでしょう。
運営メンバーが複数になると、負担も軽くなり、他のメンバーからの開催のプレッシャーもかかるため、継続しやすくなります。

勧誘する

毎回参加してくれるメンバーも、子供が生まれるなどのライフステージや環境の変化で参加できなくなるということがあります。初期メンバーだけでは、どんどん参加者は減ってしまいます。 新しいメンバーに参加してもらい、刺激が得られたり、マンネリ化を防ぎましょう。

ただし、新しく参加するのはなかなかハードルが高いのも事実です。 開催の告知をするだけでは、「参加してもいいけどどうしようかな」と悩む方も多いようです。

直接声をかけたり、仲の良い同期同士で誘い合ってもらうことで、参加のハードルが下がります。勧誘をして、合宿仲間を増やしましょう。
開発合宿を楽しんでくださった方は、繰り返し参加して頂けます。

おわりに

part2は以上です。
ぜひこれらのノウハウを活用して、楽しい開発合宿を開催して頂けたら幸いです。

就活会議トップページをリニューアルしたときのCSS設計のお話

16卒で入社したエンジニアの田口です。 就活会議というメディアで、主にフロントエンド開発を担当しています。5月にトップページをフルリニューアルしましたので、その時のCSS設計についてお話しいたします。

就活会議のフロントエンド開発では、FLOCSSを採用しています。
1カラムのレイアウトの場合、2,3カラムの場合と比較して

  • どのパーツをLayoutにすべきか
  • どこまで細かくProjectとしてきり出すべきか

が曖昧になりやすく、開発者同士の議論が増え、コードの修正も多くなりました。

そこでパーツ単位の実例を、「ファーストビュー」と「企業口コミ」の2つを用いて、トップページのCSS設計について解説していきます。

トップページのファーストビュー

f:id:livesense-made:20170629122439p:plain

こちらのトップページのファーストビューでは、背景に動画を流し、その上にヘッダーパーツ、真ん中にメッセージパーツ、その下にテキストパーツがある構成になっています。このような場合、どのような設計をするのが適切でしょうか?

Layoutのクラスを作るべきか、Projectの単位はどうするのか、など悩みどころは多いかもしれません。 今回は以下のクラス設計で実装しました。Firebug等で実際に確認していただければと思います。

f:id:livesense-made:20170725151127p:plain

ポイントとしては、Layoutの責務とProjectの責務を明確に分離したことです。 l-top-visualというファイルにファーストビューのLayoutに関するクラスをまとめています。

_top-visual.scss
//------------------------------
//トップページのビジュアルのスタイル
//------------------------------

.l-top-visual {}
.l-top-visual__video {} 
.l-top-visual__filter {}
.l-top-visual__header {}
.l-top-visual__message {}
.l-top-visual__appeal {}

今回はabsoluteで、ヘッダーや中心のメッセージパーツなどの位置を指定しているため、場所を指定する要素はLayoutに書き、その中にProjectファイルでパーツのクラスを入れました。 他にもposition指定は1つのファイルにまとめておきたかったいう理由もあります。

一般的にLayoutのクラスはl-header, l-sidebarのように汎用的に使うことを想定されています。 しかし、今回はProjectファイルにLayoutの要素を持たせるのをやめて、Layoutの責務とパーツ単位の責務を明確に分離する方針をとりました。

企業口コミのパーツ

f:id:livesense-made:20170629123311p:plain

続いてこちらの企業口コミのパーツです。1つのProjectファイルで設計した場合、以下のようになるかと思います。

_top-word-mouths.scss

//-----------------------------------
//トップページの口コミ、口コミパネルのスタイル
//-----------------------------------

.p-top-word-mouths {}
.p-top-word-mouths__title {}
.p-top-word-mouths__desc {}
.p-top-word-mouths__content {}
.p-top-word-mouths__content-inner {}
.p-top-word-mouths__panel {}
.p-top-word-mouths__panel__img {}
.p-top-word-mouths__panel__img-mask {}
.p-top-word-mouths__panel__img-text {}
.p-top-word-mouths__panel__content {}
.p-top-word-mouths__panel__content-company {}
.p-top-word-mouths__panel__content-star {}
.p-top-word-mouths__panel__content-star-text {}
.p-top-word-mouths__panel__content-category {}
.p-top-word-mouths__panel__content-text {}
.p-top-word-mouths__panel__btn-area {}
.p-top-word-mouths__panel__btn {}
.p-top-word-mouths__panel__img {}

各クラスの要素を書いていないためわかりにくいですが、1ファイルでコードが200行を超えてしまい、かつElementのElementが存在してしまいます。 最初はこのように設計していたのですが、最終的には

  • .p-top-word-mouths
  • .p-top-word-mouth-panel

の2つのプロジェクトファイルに分けて作成しました。

f:id:livesense-made:20170706180343p:plain

各ファイルのクラスは以下のようになっています。

_top-word-mouths.scss
//------------------------------
//トップページの口コミのスタイル
//------------------------------

.p-top-word-mouths {}
.p-top-word-mouths__title {}
.p-top-word-mouths__desc {}
.p-top-word-mouths__content {}
.p-top-word-mouths__content-inner {}
_top-word-mouth-panel.scss
//-----------------------------------
//トップページの口コミのパネルのスタイル
//-----------------------------------

.p-top-word-mouth-panel {}
.p-top-word-mouth-panel__img {}
.p-top-word-mouth-panel__img-mask {}
.p-top-word-mouth-panel__img-text {}
.p-top-word-mouth-panel__content {}
.p-top-word-mouth-panel__content-company {}
.p-top-word-mouth-panel__content-star {}
.p-top-word-mouth-panel__content-star-text {}
.p-top-word-mouth-panel__content-category {}
.p-top-word-mouth-panel__content-text {}
.p-top-word-mouth-panel__btn-area {}
.p-top-word-mouth-panel__btn {}

この粒度であれば、1つのProjectファイルにまとめても良いかもしれませんが、 就活会議では以下をHTML・CSSのコーディング規約としてルール化しています。

  • ElementのElementは作らない
  • ファイル数が増えるのを許容して細かくProjectファイルを切り出す

この2点により

  • クラス名が長くなることを避けられる
  • 各ファイルの行数を少なくすることにより、ファイルの見通しが良くなる
  • ProjectをComponentにする際の変更が容易になる
  • HTML構造が変わった際に、クラス名を変更することを避けられる
  • Viewを見た際に、開発者がどのファイルを見ればいいかすぐにわかる

というメリットが得られます。 そのため、就活会議ではElementのElementを許可せず、Projectファイルを小さく切リ出す方針をとりました。

簡単ではありますが、トップページの実例を用いて就活会議のCSS設計についてご紹介しました。CSS設計の参考になれば幸いです。

Railsでの秘匿情報の取り扱いを運用しやすくするgemを作った話

f:id:livesense-made:20170720182249j:plainみなさんこんにちは。ジョブセンスでエンジニアをしている小沼です。初めまして。 16新卒で入社して現在2年目になります。 去年はSQLで円グラフを書いたりしていました。

さて、本題に入ります。先日新規でRailsリポジトリを立ち上げたのですが、その時に秘匿情報の取り扱いについて考えたことがあったので紹介いたします。

Rails5.1から導入されたEncrypted Secretsについて

Rails5.1から標準でEncryptedSecretsという機能を使うことができるようになりました。

参考: Rails 5.1: Loving JavaScript, System Tests, Encrypted Secrets, and more | Riding Rails

使い方はざっくりと以下の通りです。

# 鍵とsecretsを準備
# 鍵ファイルは自動で.gitignoreに追記される
$ bundle exec rails secrets:setup

# 編集
$ bundle exec rails secrets:edit

上記の操作で秘匿情報をセキュアにgit管理することができます。 暗号化された情報はconfig/secrets.yml.encに保存されていて、アプリケーションの初期化に際してconfig/secrets.ymlの内容とmergeされます。

実際にアプリケーション上で取り扱う際は、
config/application.rbまたはconfig/environments/*.rbconfig.read_encrypted_secrets = trueとした上、Rails.application.secrets.hogehogeで参照することができます。

正直なところ、かゆいところに手が届かない

新規で作成したリポジトリではRails5.1を使っていたので、EncryptedSecretsを使うことができる環境でした。 しかしながら実際の運用を始めてみると、EncryptedSecretsは少々使いにくいなと思うことがでてきました。

そのように感じた主な理由は以下3点です。

  • 暗号化された情報は一行で記述されるため、コンフリクトが起きやすい
    • 当然、変更のたびにファイル全体が変更されるため、どの情報がいつ変更されたのか確認できない
  • ファイル全体が暗号化されるため、そもそもなんの情報が暗号化されているのかすらわからない
  • せめてansible-vault showみたいな閲覧するだけのコマンドが欲しい

なのでぼくの欲求を満たしてくれるGemを作った

実現したいことは以下の通りです。

  • 暗号化するのはYAMLの末端(葉)で、かつkey: valuevalueのみ
    • (それによってセキュアさが落ちることは許容する)
  • 暗号化したあともYAMLの構造は守る
  • 複合化した情報を手軽に参照できるコマンドを提供する

そしてできたのがleml(Leaf Encrypt YAML)です。

lemlの概要

インストールと使い方

lemlはRailsのプラグインとして実装されていて、秘匿情報はrakeタスクによって閲覧、編集を可能にしています。 実際のRailsプロジェクトはGemfile/Gemfile.lockによってgemを管理することが多いと思います。通常のgemをインストール手順と同じく、

# Gemfile
gem 'leml'

とした上で、$ bundle install

初期化

その後、初期化を行います。

$ bundle exec rake leml:init
Complete!
create  config/leml.key
create  config/leml.yml

Caution Don't forget add key file in gitignore

※ 自動的に鍵ファイルは.gitignoreに追加されません(今後機能として追加していく予定です)
※ デフォルトでYAMLのシンタックスハイライトが効いて欲しかったので本家のファイル名にある.encサフィックスは削除させてもらいました

編集/閲覧

暗号化されたファイルの編集、閲覧は以下のように行います。

$ EDITOR=vim bundle exec rake leml:edit

これでvimが立ち上がり秘匿情報の編集ができるようになります。環境変数'EDITOR'を利用するのでお好きなエディタをセットしてください。 保存して終了すると、内容を暗号化した上でconfig/leml.ymlが更新されます。編集する必要はなく、内容を確認できればいい場合は以下のコマンドで標準出力に出力することも可能です。

$ bundle exec rake leml:show

実際に使ってみると以下のようになります。
元のYAML

---
development:
  gem:
    leml:
      author: onunu
      github: https://github.com/onunu
  favorite_precure: twinkle

上記の内容を暗号化すると、

$ cat config/leml.yml
---
development:
  gem:
    leml:
      author: aFR1RWUyT0RoVTJzSlgrZ0ptVmlqdz09LS0vL2hzZXVLMzZtTFJ5dk1ZSzdqQ2lRPT0=--bced3fd4f521287b819edb677460e2518bcefb8b
      github: ZW9PcTBYMy8xakZQUXhqVmhWNlNxNXBKMmM3Z3FLTDZKVEdoRmFzUnB4Z2JScTNKMlo1VktiK0NZS01JVlpLWC0tc2hHTTlrTytOYXpJckxIMmhhNWV5QT09--5340c65706b5619afcc81318dca47c033aecf65f
  favorite_precure: T21uN2R0NjhsNjdHOHgzY1d6WHp5RDRibnNYVHBOY2l4TC9rcmc1UGZ5OD0tLXllQnQrTXk0Q3lVdmtTdzAzQVprVXc9PQ==--e2568a8cfa84055f367dce531f5e287ac5182033

階層構造があっても問題ありません。何が暗号化されているのか一目瞭然! 実際のアプリケーションから参照する時は、通常のsecretsと同様に扱うことができます。

$ bundle exec rails c
Running via Spring preloader in process 68212
Loading development environment (Rails 5.1.2)
irb(main):001:0> Rails.application.secrets.favorite_precure
=> "twinkle"

なにをしているのか

Railtieについて

lemlはRailsの初期化プロセスに相乗りする形でsecretsをmergeしていますが、これはrailtieを使うことで比較的簡単に実装することができます。 lemlでは以下のようにしてRailsの初期化プロセスを拡張しました。

# leml/lib/leml/railtie.rb
module Leml
  class Railtie < Rails::Engine
    initializer 'Decrypt Leml file' do
      require 'leml/core'
      Leml::Core.new.merge_secrets
    end
  end
end

RailtieはRails公式のプラグイン機構です。Railtieに基づいて各コンポーネントを組み合わせることで、Railsの様々な処理を手軽に拡張することができます。自作のgem(今回でいうところのLemlモジュール内)でRails::Engineクラスを継承したRailtieクラスを定義することで、Railsが処理を行う際に相乗りさせてもらえます。

参考: Rails::Railtie

今回は初期化プロセスを拡張したいので、内部でinitializerメソッドを定義しました。これにより、Railsが実行する初期化と同時に、initializerメソッド内で記述した処理をRailsが行ってくれます。

各タスクについて

lemlでは各操作をrakeタスクで提供しています。 Rails pluginではleml(gem名)/lib/tasks配下にrakeファイルを設置することで、Railsプロジェクトのタスクとして読み取ってもらえるだけでなく、タスクの実行時にはRailsの初期化プロセスが行われた後でタスクを実行してくれます。

lemlのrakeファイルは以下のようになっています。

# leml/lib/tasks/leml_tasks.rake
require 'leml/core'

namespace :leml do
  desc 'initialize secrets yaml'
  task :init => :environment do
    Leml::Core.setup
  end

  desc 'edit encrypted yaml'
  task :edit => :environment do
    Leml::Core.new.edit
  end

  desc 'show encrypted yaml'
  task :show => :environment do
    Leml::Core.new.show
  end
end

終わりに

いかがでしたか? 自分で書いたファイル数は4つくらいでしたし、それぞれ基本的なことしかしていません。 それでも自分のやりたかったことはある程度実現できました。Railsの懐の深さに驚きです。

また本家のEncryptedSecretsの実装、Railtieの仕組みなどを理解するのにソースコードを読みましたが大変勉強になりました。やはり一次資料はソースコードですね。

lemlはオープンソースで提供しているので、みなさん使ってみてくださいね! まだまだ至らぬ所はありますが、メンテ頑張っていきたいと思います。ご要望やバグ報告、その他ご指摘などはissueを立ち上げていただけると幸いです。

ご注意

lemlはYAMLファイル中のkey: valueのうちvalueだけを暗号化します。当然、keyは平文のまま残るため、keyからvalueの値を推測されてしまう危険性をもっています。 そのリスクを許容できない場合はRails標準のEncryptedSecretsを利用するか、あるいは併用するのがよいでしょう。 ご利用の際にはご注意ください。

転職クチコミサービスでのプロダクトチーム文化と取り組み

背景

転職クチコミサービスの転職会議を開発するプロダクトチームは2014年頃には6名ほどの小さな組織でした。

2017年現在は30名強と、徐々にメンバーを増やしながら成長してきました。

今回はこのチームで作り上げてきた文化の話をしたいと思います。

Team Geekとの出会い

僕たちが成長する上で強く影響を受けた書籍がTeam Geekです。

HRT(謙虚・尊敬・信頼)などで有名な名著ですが、チーム文化についても重要な項目として語られています。

この文化というのは非常に捉えづらいものではありますが、文化を意識してチームを作る事が非常に大切だったなと感じています。

Spotify engineering cultureとの出会い

文化について意識的に作り上げる段階において、強く影響を受けたのがSpotifyの資料です。

前後編合わせて30分程度の短い動画ながら、Agileの精神、Leanな開発、上手な「失敗」の仕方など、非常に濃い内容が含まれています。

2014年の資料であるにもかかわらず、今でも共感できる内容がたくさん含まれています。

皆が同じ目標を向き、自律的に動く組織文化

Spotifyの資料の中で特に影響を受けたのが「高いAlignmentかつ高いAutonomyな組織」のモデルです。

少々馴染みの薄い言葉なので、それぞれ意味を訳してみます。

Alignmentは、「整列」や「同調」「一致性」「団結」など、一方向へ向かう様子を表しています。

Autonomyは、「自治」・「自律」という意味になります。

つまり「同じ方向を向いた状態」でかつ「自律的である」という事になります。

この2つの概念は、それぞれ相反するような概念に思えるかもしれません。

しかしSpotifyの資料においては、むしろ「高い一致性こそが高い自律を可能にする」とも語られ、どちらも高い状態であることを理想であるとしています。

f:id:livesense-made:20170705234101p:plain

もう少し深掘りすると、理想像はこのようなことと言えます。

  • 明確なミッションを持ち、メンバーとリーダー全員が、同じ方向を向いて同じ目標に向かって進む
  • メンバーは「どう解決するか?どれが最適か?」の部分を考えて実行する。
  • リーダーは「何が課題か?解決のために何が必要か?」に責務を持つ

Team Geekでもミッション・ステートメントの重要さや、サーバントリーダーなど類似する考え方があり、これらの重要さが語られています。

この「明確なミッションを高いレベルで共有し、高い一致性を持ち高い自治性があるチーム」という理想像は、転職会議のチーム文化へ強く影響を与えてくれました。

高い一致性と自律性を持ったチームで何が出来るか?

それでは高い一致性と自律があるチームは何が良くて、何が出来るのでしょうか?

ここからは僕たちが理想像のチームだからこそ出来たと感じていることを紹介していきます。

とりあえずやってみる → 上手く行ったら残す/駄目ならすぐ捨てる

新しい仕組みやツールへのトライアル・施策は様々な懸念があったり、障壁が高いなどがあり、どうしても腰が重くなりがちです。

転職会議では「とりあえずやってみよう」を合言葉に、フットワークを軽くアクションを起こす文化を推奨しています。 まず小さくやってみる→運用してみる→上手く行ったら残す / 駄目なら修正するか撤退する、という流れです。

Spotifyの資料では、Part2の「Waste-repellent Culture」という項目で類似の事が紹介されています。 また、Lean Startupの考えともほとんど同じです。

当然、各々が何も考えず無作為にいろんなものを取り入れてしまえば混沌としてしまう可能性もありますが、各自が責務と自由のバランスの上で行えているので今のところ適切にワークしています。

標準化(Standalization) より 異花受粉(Cross-pollination)

リブセンスではPHPやRuby on Rails、Go、Reactなどがよく使われています。

これらはすべて誰かが決めたわけではなく、自然発生的に広まっていきました。

Spotifyの資料においては異花受粉(Cross-pollination)という呼び方で、この徐々に良いツールがチームからチームへ伝播してデファクトスタンダードとして広まる現象を説明しています。併せて、一貫性と柔軟性が保たれる効果をもたらされる利点も説明されています。

新しい言語やツール、ライブラリを一つのチームが取り入れて、それが良いものであれば隣のチーム・リポジトリにも使われるというフローは、開発にはよくあるごく自然な現象です。

しかし、その現象に対して「足並みをそろえて標準化するより、より良いものが自然に伝播していく文化には利点がある」ということをしっかり認識することで、「とりあえずやってみる」の精神をより加速させて来ることが出来ました。

今日まで、良い温度感で技術の新陳代謝を進めることが出来ていると感じています。

「とりあえずやってみる」の部分でも懸念として上がりましたが、こう聞いてしまうと「いろんなライブラリが入って、学習コストが高くなってしまうのでは?」と感じる方もいるかもしれません。

こちらも同様で、各自が正しく目的を捉えた上で適切な技術選定を心がけているため、極端に突飛な選定はされず、各自の責任の持てる範囲でのチャレンジがなされています。

Internal Open-Source Model

Spotifyの資料ではInternal Open-source Modelというのが紹介されており、転職会議でもこの考え方を取り入れました。

これはチームで開発されるシステムを全て内部的なオープンソースと同じように扱い、特定のチームが特定のシステムだけ触るのではなく、必要になったチームがPull Requestをするというやり方です。

この方法はPayPalのInnerSourceも近いかもしれません。

転職会議は現在クチコミ関連の他にも・求人・会員情報・ログイン認証・企業向け管理画面など、大小幾つかのシステムが独立的に動いています。

これらのシステムに対し、専属のチームを設けるのではなくそれぞれのチームが自分たちのミッション・施策に応じて、各チームが必要なシステムに必要なだけPull Requestをしてリリースして作業を進めています。

一つのシステムに対して必ず一つのチームが改修する体制にしている場合、自分たちの施策が他のチームの優先度で左右されてしまったりということがありがちです。

Internal Open-Source Modelを取り入れることによって、各チームが他のチームに依存せずプロジェクトを進めることが出来ています。

リポジトリオーナー

通常のオープンソースにコアなメンテナが存在するのに倣って、各リポジトリに複数人のオーナーとサブオーナーを設けています。 *1 これはいくつかのリポジトリで試験的に行ってみて、うまくいきそうだったので現在展開しています。

オーナー・サブオーナーはOSSのメンテナと同じようにコードレビューを行い、リポジトリの品質や一貫性を担保することが主な責務で、その代わりにリポジトリのライブラリ選定やコーディング規約などもおまかせしています(先に説明したCross-pollinationによる伝播がチーム内でも適度に行われる事も狙っています)。

基本的にはそのリポジトリを頻繁に触るチームのメンバーや、そのシステムに詳しいメンバーから選出しています。システムを触らなくなってきたり、オーナー業務が偏ってきた場合には適切に交代や追加をしていく予定です。

品質の保持や一定の自由度が出来た上での副次的な効果として、コードレビューが一部のメンバーへ極度に偏ったり、逆にレビュアーが多すぎて緊張感の無い薄いレビューになってしまう問題が緩和出来る効果がありました。

また若手メンバーにもサブオーナーになってもらい責務を持った視点でレビューをしてもらうことで、育成の機会になるという部分も見られそうだと感じています。

「やってあげる」よりも「出来るようにする」(Self-serve Model)

転職会議がAWS化した時期を境に、SRE(Site Reliability Engineering)チームが設立されました。 *2

SREチームはサイトのパフォーマンス向上やセキュリティ向上、安定的な運用を目指しているチームです。

まだまだAWSに移行したばかりで、ノウハウが足りない、メンバーの学習が追いつかないなど、様々課題もあります。

新しいシステムの作成などAWSに触る部分が出てきた場合、慣れているSREチームのメンバーが作業をしてしまう方が素早く出来るのですが、どうしても作業が集中してボトルネックになってしまいがちです。

そのためSREチームが直接作業するのは極力減らし、どんどんメンバーが各自で「出来るようになる」という土壌を作ることを心がけています。

フロントエンドにおいても類似です。フロントエンドが得意なメンバーが作業を請け負うのではなく、できるだけ触ったことが無いメンバーをアサインしたり、勉強会を執り行ったりして「出来るようにする」ということを意識しています。

デザイナーとフロントエンドエンジニアのやり取りにおいても、CSSやテンプレート、JSXなどを全部フロントエンドエンジニアがやってしまうとボトルネックになってしまう事があるため、ある程度からはデザイナーだけでもどんどん作業が出来るようにしています。

どちらもInternal Open-Source Modelと同様、各自に信頼を置いてやれる領域を可能な限りで広げ、なるべく他のチームやメンバーがボトルネックになるのを避けるような取り組みです。

部分的なリモート勤務

リブセンスには今のところリモートワークなどの制度はありませんが、転職会議チームでは会社の規則に反せず、チームで適切に運用できる範囲でのリモート勤務を取り入れています。

導入にあたっては、オフィス出社を前提とした上で上手くリモートの自由度を取り入れているQuipperさんの記事を参考にさせていただきました

基本的にはこのようなルールで進めています。

  • 基本的にはオフィスに出社するのが前提。対面の会話は大事にする
    • 在宅勤務をするときはチャットでコメントする。
    • 用事などリモート関係なく出勤時間がずれる場合もチャットでコメント
  • タイムシフトのためのリモートではないので、基本的に作業時間はずらさない
    • だいたい11:00〜18:00でみんなが働いているので、そのぐらいを目安とする
  • 体調不良を理由にしたリモートワークはナシ
    • 低いパフォーマンスで仕事せず、しっかり休んで回復させる

朝や午後に数時間利用したり週に1度丸々リモートにするなど、利用の仕方はメンバーによって様々です。

数ヶ月やってみて今のところ混乱や問題は無く、快適に運用できています。これもメンバーが高い一致性と自律性を持って仕事に取り組めている文化があってこそ上手く行ったことではないかなと感じています。

まとめ

ここまで転職会議プロダクトチームの文化を一部ですが紹介してきました。 今回のお話で興味を持った方は是非Team GeekやSpotify engineering cultureをじっくりと見ていただければなと思います。

転職会議は今後も成長させたいと感じていますし、文化もどんどん強くしていきたいと感じています。

様々変わる部分も今後出てくると思いますが、ミッションに対して高いレベルで一致をしつつ自律した組織を理想像とする志はいつまでも保持していきたいなと感じています。

転職会議では僕たちと文化を育ててくれるメンバーを募集しています!

http://recruit.livesense.co.jp/

*1:なんていう記事を書いている間にgithubにcode ownersという機能が実装されてました。

*2: 転職会議のAWS化の苦労話などは、また別なタイミングで出来たらなと思います

テストの取り組み、あるいは体のよい車輪の再発明について

ジョブセンスのエンジニアをしている松下です。
コードレビューと大人の話し合い、若手から作りの相談を受け付けることが最近の主な担当業務です。

先日自分のチームが取り組んだプロジェクトは、古いバージョンのPHPで実装されているアルバイト応募機能を、Ruby 2.4に置き換えるというものでした。
応募というのはコンバージョンに直接的に関わるため、機能に様々な工夫が加えられる等で煩雑化し、コードも荒れ模様。 それに対し単にRubyポーティングするだけでは意味が薄いので、不要な機能の削減やコードクオリティの向上に力を入れたプロジェクトでした。

さて、コンバージョンに直接関わる機能ということは入念なテストが重要となってきます。単にエラーを出したくないというのはもちろん、フォームでは個人情報の入力もあるためです。
ここではそのテストにおける取り組みの1つを紹介いたします。

テストで楽をしたい

前述の通り、テスト対象はアルバイトへの応募機能です。
そして自分が実装担当した部分は、とある経路で応募フォームに来た場合に、そこにフォームに加えてアルバイトの詳細情報を表示するというものでした。

まずフォームの入力〜SUBMITのテストについては、手堅さが最も求められる上に自動化しきるのも難しいためリリース前にテスト項目書を作成しての手動テストを実施しました。
チーム全員でテスト項目を出し合い、相互レビューによって内容を担保します。 (RSpecによる単体テストと、CapybaraにFeatureテストはある程度記述した上でです)

次に考えるべきは、フォームと並んで表示されるアルバイト詳細情報表示のテストです。
その実装は、詳細情報の表示となると項目数が多く、またそれを適切にユーザに見せるためのViewロジックが必要となります。
単純なものでは時給額の値が1000の場合に「1,000円」と表示するようなものであったり(これはRailsのヘルパにありますが)、アルバイトの形態に応じて表記を変更するといったものとなります。

こうしたViewロジックは基本的にはDraperに寄せ、その単体テストを書くことで挙動の保証をしています。
しかし当然ながらクラスの単体テストで全てを担保できるわけではあいません。
結合レベルの話もありますが、Viewテンプレート側に多少ロジックのははみ出ることはありますし、またそもそものところでDBに想定外のデータが含まれていてエラーとなることもあります。

想定外のデータについて補足します。
ジョブセンスは、学生ベンチャーに始まって東証1部上場までしたリブセンスの急成長を支えたサービスです。 したがって、正規化されていない場合やジェイウォーク、バリデーションが仕込みきれていなかった等で特殊なデータがDBに格納されている場合も睨む必要があります。

しかし発生し得るパターンを確認し、網羅的にそのテストデータを作成するのは非常にコストがかかります。
また、これはView層なのでサービス改善による変化が起きやすく、テストの資産価値は相対的には低めです。

ではどうするか。
既存のアルバイトデータに対して網羅的にアクセスして確認するのみです。
幸い我々には10数万件のアルバイト実データがあります。 そしてリブセンスでは、そんな本番DBのデータを開発用DBへ日次で同期させる仕組みがあるため(事故防止で個人情報はマスキングをかけています)、開発環境でも網羅的なアクセスをすることが容易です。

これにより、「え、そんなデータのパターンがあってエラーになったの?!」という状況を未然に防ぐこととしました。
細かい表示内容を担保することももちろんですが、まず最優先で担保したいのは「そもそもエラーにならない」ことです。

ツールを考える

指定したリクエストパターンに対してガツガツとHTTPしてくれるツール、と言えばJMeterあたりが使えそうでしょうか。
が、自分は練度の低いエンジニアであるため、使い方を調べるところからスタートした結果、案外時間がかかるかも知れない…。
それであれば、やることは単純ですし手に馴染んだ言語でササっと書いてしまった方が早いしモチベーションも上げやすいと考えました。
また、DBアクセス、URL生成、HTTPリクエスト、結果出力と、1つのコード内でシームレスにできるのは楽でもあります。

自分のチームにおいてはRubyが一級市民ではありますが、パフォーマンスを考えると並列アクセスがしやすい言語の方が良いです。 Rubyでもできないことはないですが、せっかくなので他の言語を使いましょう。

というわけで、比較的手に馴染んでいて並列処理のしやすいClojureを選びました。
使い捨てのコードなので、後に誰かがメンテがするようなこともなく苦情も来ません。たぶん。 (このようにして業務の中で第二言語の練度を高めていくのは、個人的に好きな学習スタイルです)

実装する (体よく車輪を再発明する)

まずは雛形を作ります。

$ lein new app reqs

そしてproject.cljに依存ライブラリを記述した上で、REPLを起動してsrc/reqs/core.cljにコードを書いていきます。

HTTPリクエストにはclj-httpを用います。 {:async? true}オプションによる非同期リクエストをベースにしているので、明示的に並列処理は書いていないです。

(ns reqs.core
  (:require [clojure.core.async :as async]
            [clj-http.client :as client])
  (:gen-class))

(defn- fetch-jobs-from-db
  "DBから求人情報を取得する"
  [] ...)

(defn- job->url
  "求人情報からエラー確認対象のURLに変換する"
  [job] ...)

(defn- async-parallel-req
  "指定したURL群に対して非同期にがががっとリクエストする"
  [urls ch]
  (doseq [url urls]
    (client/get url {:async? true}
                (fn [_] (async/put! ch (str "OK " url)))
                (fn [_] (async/put! ch (str "NG " url))))))

(defn- async-parallel-req-with-log
  "リクエストしてその結果をシリアルに出力する"
  [urls]
  (let [ch (async/chan)]
    ;; 出力を一本化するためのgoマクロ
    (async/go
      (loop [i 0]
        (let [log (async/<! ch)]
          (println (str i ": " log))
          (recur (inc i)))))
    (async-parallel-req urls ch)))

(defn -main [& args]
  (->> (fetch-jobs-from-db)
       (map job->url)
       async-parallel-req-with-log))

fetch-jobs-from-dbのところは、自分はKormaを用いました。
ここで対象となるレコード数が多い場合には遅延シーケンスを使って少しずつDBから読み込むようにするのもアリでしょう。

さて、これで完成です。
$ lein runっと。

出力の一本化

printlnするところでgoマクロを使っていますが、これは並列実行処理が同時に標準出力を用いた場合への対応です。
この対応をしなかった場合には、概ねは以下のように表示されるものの

OK https://domain.com/path/to/123
OK https://domain.com/path/to/124
OK https://domain.com/path/to/125

たまに以下のようになります。

OK https://domain.com/path/to/126OK https://domain.com/path/to/127

OK https://domain.com/path/to/128

したがって出力処理を一本化して、表示が崩れないようにしています。

なお、今回は標準出力を使っていますが、出力先が同時書き込みできない類のものであればこのような対処が必要となります。

ちなみに起動が遅いのが気になるのなら

JVM系言語の宿命ですね。

コンパイルをしてDripを用いるのはいかがでしょうか。
Dripをインストールした上で以下のようにします。

$ lein uberjar
$ drip -jar target/uberjar/reqs-0.1.0-SNAPSHOT-standalone.jar

結果

上記のプログラムと約10数万件あるジョブセンスの求人データを用い、2件のバグを発見しました。

これは前職での経験ですが、テスト専門の業者に依頼したところ、社内メンバーの半分のテストケースで1.5倍のバグが検知されたという話がありました。
リブセンスにおいてはテストコードを書く文化が根付いているものの、それとは別の話として、テストが自動であろうと手動であろうと「いかに効果的なテストパターンを想定するか」は重要であると考えさせられます。

テストにおいても効果的なLazyinessを発揮していきつつ、またモチベーティブに仕事ができるよう心掛けていきたいです。

気軽に試せるエンタープライズSDS「ScaleIO」を試してみた

こんにちは、インフラグループの水野です。
みなさんネットワークストレージ大好きですよね?
NFSやFC(Fiber Channel)-SAN、iSCSI、GlusterFS、Ceph、広義の意味ではオブジェクトストレージのAmazon S3、Swift etc… 長年利用されているものから新しいもの、エンタープライズからオープンソースまで様々あると思います。
今回はScaleIOの導入検証する機会がありましたのでご紹介させていただきます。

What’s ScaleIO?

EMC ScaleIO (以下ScaleIO)はEMCが提供しているエンタープライズ向けのSoftware Defined Storageプロダクトです。
同様のものとしてOSSのCephがしばしば挙げられます。

ScaleIOは以下のような特徴を持っています。

  • ソフトウェアで定義された、ブロックストレージを提供
  • 数千ノード規模まで対応しており、台数の増減が容易
  • 一部ノードがダウンしても運用継続可能な耐障害性を備える
  • 高速、高性能(特にCephより高速であると謳っている)
    • オーバーヘッドも少ないため、ハイパーバイザーなどとの同居が可能
  • インストールが容易、管理用のUIが便利

2015年ごろから検証用途であれば機能・容量共に無制限で利用できるようになったということがあり、気軽に検証できるようになりました。

いざ導入

公式で提供されているドキュメントが非常にわかりやすいのでそちらにしたがって進めていきます。

構成

ScaleIOは下記コンポーネントで構成されます。

  • GW(GateWay)
  • IM(Installation Manager)
  • MDM(Meta Data Manager)
  • TB(Tie Breaker)
  • SDS(ScaleIO Data Server)
  • SDC(ScaleIO Data Client)

また、公式ドキュメントではスタンダードな最少3台構成と冗長性を高めた最少5台構成が提案されています。

  • 最少3台構成
    • Master MDM * 1
    • Slave MDM * 1
    • TB * 1
  • 最少5台構成
    • Master MDM * 1
    • Slave MDM * 2
    • TB * 2

今回は最少3台構成+GW / IM用1台の合計4台構成で試してみます。 ScaleIOが扱うボリューム領域を別途用意する必要がありますが、今回はLVMを使って切り出すことにしました。
詳細情報は以下のとおりです。

役割 IPアドレス ディストリビューション ScaleIO用の論理ボリューム名(参照してるデバイス名)
GW / IM 192.168.10.1 Ubuntu 16.04 LTS
Master MDM 192.168.10.2 Ubuntu 16.04 LTS /dev/VolGroup00/lv_scaleio( /dev/dm-2 )
Slave MDM 192.168.10.3 Ubuntu 16.04 LTS /dev/VolGroup00/lv_scaleio( /dev/dm-2 )
TB 192.168.10.4 Ubuntu 16.04 LTS /dev/VolGroup00/lv_scaleio( /dev/dm-2 )

最終的には以下のような全体像になります。 f:id:nashiox:20170622201229p:plain

インストール

パッケージをダウンロード

インストールに必要なパッケージ群をダウンロードします。 必要なものは以下からダウンロードできます。

日本語版のダウンロードページだと現在(2017/06/21) v.1.32.3が、海外版のダウンロードページだとv.2.0.1.2がダウンロードできます。日本語ページのバージョンがちょっと古いですね。
今回は最新のv.2.0.1.2を利用するため海外版のページからScaleIO Linux版をダウンロードリンクから落とします。

ダウンロードしたものは以下のようにして解凍しておきましょう。

$ unzip path/to/ScaleIO_Linux_v2.0.zip
$ ls ScaleIO_2.0.1.2_Complete_Linux_SW_Download
Documentation                                      ScaleIO_2.0.1.2_Gateway_for_Windows_Download       ScaleIO_2.0.1.2_SLES_12.1_Download
ScaleIO_2.0.1.2_GPG-RPM-KEY_Download               ScaleIO_2.0.1.2_RHEL_OEL6_Download                 ScaleIO_2.0.1.2_UBUNTU_14.04_Download
ScaleIO_2.0.1.2_GUI_for_Linux_Download             ScaleIO_2.0.1.2_RHEL_OEL7_Download                 ScaleIO_2.0.1.2_UBUNTU_16.04_Download
ScaleIO_2.0.1.2_GUI_for_Windows_Download           ScaleIO_2.0.1.2_SLES_11.3_Download                 ScaleIO_2.0.1.2_XEN_6.5_Download
ScaleIO_2.0.1.2_Gateway_for_Linux_Download         ScaleIO_2.0.1.2_SLES_12.0_Download                 ScaleIO_v2.0.x_Linux_Windows_Quick_Start_Guide.pdf

ScaleIO Gatewayのインストール

まずはインストーラーをセットアップするため、ScaleIO Gatewayをインストールします。

Gatewayインストール用のrpm/debパッケージがあります。

$ ls ScaleIO_2.0.1.2_Complete_Linux_SW_Download/ScaleIO_2.0.1.2_Gateway_for_Linux_Download
EMC-ScaleIO-gateway-2.0-12000.122.x86_64.rpm ScaleIO_Complete_Config.csv                  emc-scaleio-gateway_2.0-12000.122_amd64.deb
EMC_ScaleIO_Software_Agreement.txt           ScaleIO_Minimal_Config.csv

今回はUbuntuで利用するのでemc-scaleio-gateway_2.0-12000.122_amd64.debをGatewayホストにアップロードしておきます。

インストーラー画面の初期パスワードを決めてインストールを行います。

[root]
### rpmとjavaが必要なので予め入れておきます
$ apt-get install rpm openjdk-8-jre

### 初期パスワードをGATEWAY_AMDIN_PASSWORD環境変数に渡してインストールします
$ GATEWAY_ADMIN_PASSWORD={{ パスワード }} dpkg -i /tmp/emc-scaleio-gateway_2.0-12000.122_amd64.deb
~~~~~ 省略 ~~~~~
The EMC ScaleIO Gateway is running. PID=11422.

https://192.168.10.1(Gatewayをインストールしたアドレス)にアクセスをしてインストーラー画面が表示されればOKです。 初回の接続が重く、タイムアウトする場合があるのでローカルからcurlなどでアクセスしておくと良いです。

デフォルトでは80/443でリッスンしていますが、ポートを変えたい場合は/opt/emc/scaleio/gateway/conf/server.xml内の${http.port}${ssl.port}を任意のポートに変えて、gatewayを再起動します。

$ sudo systemctl restart scaleio-gateway

ScaleIOのインストール

https://192.168.10.1でインストーラーにアクセスし、User name: adminPassword: {{ パスワード }}でログインします。 f:id:nashiox:20170621214628p:plain

ログインができると次のような画面になります。 f:id:nashiox:20170621214710p:plain

ガイドに従って、Get Startedボタンからinstallation packageのアップロードに進みます。 f:id:nashiox:20170621215243p:plain

Blowseボタンを押してインストールに必要なパッケージをアップロードしていきます。 ダウンロードしてきたパッケージ群の中に各ディストリビューションのインストールパッケージが入っています。

$ ls ScaleIO_2.0.1.2_Complete_Linux_SW_Download/ScaleIO_2.0.1.2_UBUNTU_16.04_Download
EMC-ScaleIO-lia-2.0-12000.122.Ubuntu.16.04.x86_64.tar    EMC-ScaleIO-sds1-2.0-12000.122.Ubuntu.16.04.x86_64.tar   EMC-ScaleIO-xcache-2.0-12000.122.Ubuntu.16.04.x86_64.tar
EMC-ScaleIO-mdm-2.0-12000.122.Ubuntu.16.04.x86_64.tar    EMC-ScaleIO-sds2-2.0-12000.122.Ubuntu.16.04.x86_64.tar   EMC_ScaleIO_Software_Agreement.txt
EMC-ScaleIO-sdc-2.0-12000.122.Ubuntu.16.04.x86_64.tar    EMC-ScaleIO-sds3-2.0-12000.122.Ubuntu.16.04.x86_64.tar
EMC-ScaleIO-sds-2.0-12000.122.Ubuntu.16.04.x86_64.tar    EMC-ScaleIO-sds4-2.0-12000.122.Ubuntu.16.04.x86_64.tar

今回はUbuntu16.04なのでそこからtarファイルをすべて選択してUploadボタンを押します。 アップロードが完了すると次の画面のようになると思います。 問題なければProceed to Installボタンで次に進みます。 f:id:nashiox:20170621215705p:plain

次はScaleIOクラスタの構成情報をアップロードします。 クラスタ構成を記述した以下のようなCSVを用意します。

IPs,Password,Operating System,Is MDM/TB,Is SDS,SDS Device List,Is SDC
192.168.10.2,rootパスワード,linux,Master,Yes,/dev/dm-2,Yes
192.168.10.3,rootパスワード,linux,Slave,Yes,/dev/dm-2,Yes
192.168.10.4,rootパスワード,linux,TB,Yes,/dev/dm-2,Yes

上記ファイルをBlowseボタンで選択し、Upload Installation CSVボタンでアップロードします。 f:id:nashiox:20170621220539p:plain

アップロードが完了すると次のような画面になると思います。 f:id:nashiox:20170621220143p:plain

MDM Password、LIA Passwordをそれぞれ入力し、Licenseにチェックを入れます。 あとはTopologyの内容がCSVに設定した項目とあっているかを確認して、次へ進みます。

ここまで来るとあとはほぼ自動でインストールが進みます。 各インストールフェーズが終わるたびにボタンを押して進んでいきます。 f:id:nashiox:20170621220705p:plain

インストールが全て完了すると以下のようになります。 Mark Operation Completedボタンを押して完了しましょう。 f:id:nashiox:20170621220815p:plain

バグフィックス

Ubuntu16.04にインストールをしていくと、SDCがうまくインストールできない事象にぶつかりました(同様にCentOS7でもインストールしてみましたがそちらでは発生しませんでした)。
ぶつかったのは以下の通りですが、一通り解決した方法を記載しておきます。

  • systemdのunitファイルが無い
  • unitファイルが利用する実行ファイルのパスが違う
  • driverを取得しに行くところの設定が無い

systemdのunitファイルがない

以下のパスにありました。

$ sudo cp -a /opt/emc/scaleio/sdc/bin/sdc.service /etc/systemd/system/
$ sudo systemctl daemon-reload

unitファイルが利用する実行ファイルのパスが違う

先ほど配置したファイルを書き換えました。

$ diff -u /opt/emc/scaleio/sdc/bin/sdc.service /etc/systemd/system/sdc.service
--- /opt/emc/scaleio/sdc/bin/sdc.service    2016-12-23 15:13:30.000000000 +0900
+++ /etc/systemd/system/sdc.service 2017-06-22 15:30:39.117453761 +0900
@@ -11,8 +11,8 @@
 After=network.target

 [Service]
-ExecStart=/opt/emc/scaleio/sdc/bin/scini start > /dev/null 2<>/dev/null
-ExecStop=/opt/emc/scaleio/sdc/bin/scini stop > /dev/null 2<>/dev/null
+ExecStart=/etc/init.d/scini start > /dev/null 2<>/dev/null
+ExecStop=/etc/init.d/scini stop > /dev/null 2<>/dev/null
 Restart=always
 RestartSec=0
 RemainAfterExit=true


$ sudo systemctl daemon-reload

driverを取得しに行くところの設定が無い

下記URLを参考に/bin/emc/scaleio/scini_sync/driver_sync.confを書き換えました。

EMC Community Network - DECN: ScaleIO: Set SDC Performance Profile and Set SDC Name Failed

github.com

使ってみる

今回はOpenStackのcinderバックエンドとして組み込んでみます。
/etc/cinder/cinder.confを次のように書き換えます。

$ sudo vim /etc/cinder/cinder.conf
[Default]
enabled_backends = lvm,scaleio  ### scaleioを追記

### 以下を最下部に追記
[scaleio]
volume_driver = cinder.volume.drivers.dell_emc.scaleio.driver.ScaleIODriver   ### 新しめのOpenStackなら同梱されてます
volume_backend_name = scaleio
san_ip = {{ ScaleIO GatewayのIP }}
sio_protection_domain_name = default
sio_storage_pool_name = defaultSP
sio_storage_pools = default:defaultSP
san_login = admin
san_password = {{ MDMのパスワード }}
san_thin_provision = false

$ sudo systemctl restart openstack-cinder-api openstack-cinder-scheduler

### openstack側にvolume設定をします
$ cinder type-create scaleio
$ cinder type-key scaleio set volume_backend_name=scaleio

これでOpenStack側でボリュームを作成する際に、scaleioをストレージの種別で選択すればScaleIO上にボリュームが作られます。
f:id:nashiox:20170622193550p:plain

オーバーヘッドが少ないのでコンピュートノードにScaleIOをインストールすることもできるそうです。

ノードの増減

ノードを増やすときはインストールのときと同様にWebインストーラーから行います。 先ほど用意したCSVを以下のように修正しましょう。

IPs,Password,Operating System,Is MDM/TB,Is SDS,SDS Device List,Is SDC
192.168.10.2,rootパスワード,linux,Master,Yes,/dev/dm-2,Yes
192.168.10.3,rootパスワード,linux,Slave,Yes,/dev/dm-2,Yes
192.168.10.4,rootパスワード,linux,TB,Yes,/dev/dm-2,Yes
192.168.10.5,rootパスワード,linux,,Yes,/dev/dm-2,Yes

192.168.10.5を追加してみました。 あとは先ほど同様CSVをアップロードしてインストールを進めていきます。 この時、下記画面でAdd to existing sys.に変えるのを忘れないようにします。 f:id:nashiox:20170621221614p:plain

これで簡単にノードを増やすことができます。

再起動等でノードが切り離された場合でも自動で復旧しますし、データはミラーリングされているため、一部ノードが切り離されたとしても継続して動作します。 この辺も非常に頭が良くて便利です。

ScaleIO GUIで見てみる

Windowsに同梱のGUIツールをインストールしてみました。 表示はこのような感じになります。 f:id:nashiox:20170622174310p:plain

ストレージ容量やIO速度、管理してる台数、アラートなどなど様々な項目があり非常に見やすいです。

まとめ

GUIでインストール・管理も簡単、ノードの増減による拡張性が高いなどさすがはエンタープライズ製品だなという感じでした。 エンタープライズ製品のストレージを触るためには通常費用が発生するので気軽に試すという機会はなかなか作れませんが、検証用途なら機能無制限で無料で利用できるというのは非常に良かったです。 本番導入にはライセンスが必要になりますので、導入は予算との兼ね合いになると思います。 しかしながら、管理・運用の容易さを考えると検討の価値は十分あるのではないかと思いました。

AWSの構成図をいい感じに出力してくる「Cloudcraft」を使ってみた

こんにちは、リブセンスのインフラグループに所属している竹本です。 主にDOOR賃貸(AWS)や転職ドラフト ITエンジニア版 / デザイナ版(オンプレ)のインフラまわりを担当しています。 インフラグループでは、各プロダクト毎に担当者(窓口役)をつけて定期的にローテーションをしているため、業務引き継ぎの際に構成図が必要な場面が多々あります。 そこで本日はAWSの構成図をいい感じに出力してくれる「Cloudcraft」についてご紹介したいと思います。

背景

DOOR賃貸では、ここ1年でELBをALBにリプレースしたり、一部APをRails化したりとインフラの構成が日々変化しています。 しかしながら、構成図の更新は手間がかかることもあり、後回しにしがちです。 また、作成者によって粒度が違うので解説が必要だったり、手作業なので漏れや間違いが発生する可能性もあります。

今回は、そんな課題を解決してくれる可能性を秘めたツール「Cloudcraft」を試してみました。

Cloudcraft

概要

AWSの構成図を3Dでいい感じに描けるアプリです。 使い方は、こちらからSign upすればすぐに使えます。 Google OAuthにも対応してます。 見た目かなりカッコイイです。

f:id:livesense-made:20170524110347p:plain

有料の「Pro Solo」プラン以上を契約すると、「Live Sync機能」が使えます。 この機能は、IAMにCloudcraft用のロールを作成し、「ReadOnlyAccess」ポリシーを付与することで利用できるようになります。 登録したアカウントのAWS環境をスキャンし、構成図を作成してくれるという優れものです。

料金プラン

詳細はこちら から確認できます。

f:id:livesense-made:20170529124459p:plain

Freeプランでも構成図を作成したり、Exportしたり、各コンポーネント(EC2等)のコストを表示できます。 f:id:livesense-made:20170524110830p:plain

Cloudcraftを使ってみた

一通り使ってみた所感をまとめたいと思います。 ご活用頂ければ幸いです。

初回作成

Live Sync機能を使って最初にできた構成図がこちら。 f:id:livesense-made:20170524112606p:plain なんだこれは(笑) カオス過ぎて記念にスクリーンショット取ってしまいました。 スキャンから構成図の作成まで自動でやってくれると思っていたのですが、 コンポーネントの配置は自分でやらないといけないようです。

最終的に出来上がった構成図

主要な部分のみですが、DOOR賃貸のステージング環境構成図を作ってみました。

f:id:livesense-made:20170529134554p:plain

良かった点

Live Sync機能

すでにご紹介した通り、スキャンすれば当該AWSアカウントで使用しているコンポーネントが自動でリストアップされます。 漏れ無く構成図を書くことができますね。

例) コンポーネントのリスト

f:id:livesense-made:20170529132210p:plain

関係する各コンポーネント間を自動でリンク

Live Syncでリストアップされたコンポーネント(例えばRoute53)をクリックするだけで、 Route53とそれに紐づくELBが画面上に出力されます。 不要なリソースが起動している等、予想と違う構成になっていた場合に気づきやすいので有用ですね。

例) 構成図のサンプル

f:id:livesense-made:20170524113701p:plain

フィルタ機能

フィルタ機能があり、例えば「staging」と入力すれば、インタンス名やtag等にstagingと入っているものだけが表示されます。 タグをうまく使えば、環境や機能ごとに構成図が作れそうです。

例) フィルタ入力画面

f:id:livesense-made:20170525184945p:plain

コンポーネントの自動更新

DOOR賃貸では、EC2を blue/greenでデプロイしています。 AMIに変更があれば、EC2のblue/greenを入れ替えるのですが、なんとその変更にも自動で追従してくれます。

blue/greenデプロイの詳細についてこちらのブログをご参照ください。

構成図を作成するだけで、月額料金が分かる

「BUDGET」タブを選択すれば、構成図上にあるコンポーネントの料金を出力してくれます。 設計の段階でおおよそのコストが分かるのはありがたいですね。

例) バジェット画面のサンプル

f:id:livesense-made:20170529153310p:plain

マネジメントコンソールへのリンク

例えば、構成図上のEC2コンポーネントをクリックすると以下のような画面になります。

例) EC2の詳細画面

f:id:livesense-made:20170529154256p:plain

出力されるインタンスIDをクリックするとマネジメントコンソールの当該EC2ページに遷移します。 ELB等の他コンポーネントも同様です。

あったらいいなと思う機能

サブネットの自動配置

サブネットがあるとかなり見やすくなるのですが、 自動ではサブネットは追加されません。手動でサブネットを追加しようと思うと画力が必要になってきます。 サブネットの自動追加機能が待たれます。

注意点

有料プランの場合、グリッドのサイズは無制限ですが、freeプランの場合は制限があります。 グリッドが足りなくなる可能性がありますのでご注意ください。

例) 有料プランでグリッドを無制限にした場合

f:id:livesense-made:20170526165722p:plain

まとめ

ある程度直感的に操作できるようなってはいますが、それでも操作に慣れるまでは時間がかかります。 また、構成図にサブネットがあるのとないのでは仕上がりがだいぶ違ってきますが、今のところ自動でサブネットは追加されません。 Pro Solo以上のプランを利用する場合、月49ドル以上の費用がかかってしまいますが、環境によっては費用以上の効果を発揮すると思います。 例えば、一度しっかりと作り込んでおけば、インフラ構成に変更があったとしても差分は自動更新してくれる(配置は手動ですが)ので、更新はかなり楽になると思います。 また、追記漏れや作成者によって粒度が違うといった問題も解消されます。 無料でも試せるのでこれを機会に検証してみることをオススメします。

番外編

havaも使ってみた

今回ご紹介したCloudcraft以外にも、同様にAWSの構成図を作れるhavaというサービスがあったのでご紹介します。 こちらはスキャンすると構成図の作成まですべて自動でやってくれます。 仕上がりがこちら。

f:id:livesense-made:20170526170530p:plain

すごく縦長になってしまいました。縦置きのディスプレイが必要な長さです。 こちらは構成図自体の修正はできないようです。 シンプルな構成であれば活用できるかもしれません。 よかったらこちらもお試しください。