katsuyukikun’s diary

とある天パーエンジニアのblog

アジャイル開発の本を読んで

引き続き、アジャイル開発系の本を読んで似たようなことが印象に残ったのでメモ程度に残しておきます。

読んだ本はこちらです。

 

アジャイルサムライ−達人開発者への道−

 

アジャイルソフトフェア開発の奥義

 

アジャイルな見積りと計画づくり ~価値あるソフトウェアを育てる概念と技法

 

印象に残ったこと

・ユーザストーリー(誰のために何をやり、なぜやるのか)を理解すること

・フィーチャーごとに短いスパンで実装していく

・ストーリーポイント(あるフィーチャーに比べて何倍くらいの労力がかかるか)で工数を見積もる

・すべきでないこと、やらないことを決める

・優先順位をつけるときの判断の要素として4つ(価値、コスト、得られる知識や意義、低減できるリスク)で分けて考える

 

ユーザストーリー(誰のために何をやり、なぜやるのか)を理解すること

ここは特に読んでいてハッとしてしまいました。

現状だと結構社内システム系の構築をしているので実際の業務を行なっている人がどうなのかみたいなことを考えて行動しがちでした。ただ本当に考えるべきは、そのシステム改善によって価値提供できるエンドユーザーに対してどうあるべきなのかを本来ならば考えなければならない項目なのではないかと思い、今までの行動を反省しました。

 

ビジネスなので結局は価値提供した先に金銭が発生します。それは誰なのか、その人がどういうことに困っていて、どういう価値を提供することで何が改善できるのかという視点から物事の思考をスタートせなばとひしひしと感じていますw

 

ストーリーポイントで工数を見積もる

これは結構実戦でもすぐに役に立つことでした。過去いろんな大きさの機能などを実装してきていたので、あの時のこういう実装はこのくらいかかっていたからそれに比べると、、、とかもしくはあの時の実装に比べて今実装しようとしているものがどのくらいなのかを相対的に判断するのは結構やりやすい感覚が個人的にはあります。

 

 

優先順位をつけるときの判断の要素として4つ

知識の獲得とリスクの低減が可能なテーマ、、、システムの使い勝手や機能についてのフィードバックなどのフィーチャーを早い段階にスケジュールする。

といったようにこの4つの要素の優先順位の付け方として、「価値とコストの面から暫定的なスケジュールを決め、あとは調整」というイメージで進めるのが良さそう。。

ただ、これが正解というわけではないのでその場その場でどういう判断をするのか、その時にメリットデメリットを理解して上で進められるのかが重要になるのだなと思いました。

 

まとめ

こういう本を今まで読んだことがありませんでした。ただこういうものを知った上だけでは意味がないので、自分が行動する時に少し時間をかけながらでも自分の頭で考えて合っているやり方を模索できればと思っています。

 

 

終わり。

達人プログラマーを読んで

コードを書いていていろんな先輩方からレビューをいただき「良い環境に入ったなぁぁ」と思いつつも、もっと自分でもなんとかしなければということで初心に戻り現在こちらの2冊を読んでいます。

達人プログラマー 職人から名匠への道

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

で今回は達人プログラマーについて読んでて何かしら残しておいてあとで自分で見返したいと思ったので自分用に残していきます。

読んで印象に残ったこと

  • 自らの技術に関心を持つこと
  • 自分の仕事について考えること
  • 悪い設計、悪い意思決定、質の悪いコードを見つけたらその場でどうにか対処する
  • 知識ポートフォリオを充実させる
  • 一つのエディタを熟知して生産性をあげる

特に「自らの技術に関心を持つこと」「自分の仕事について考えること」「知識ポートフォリオを充実させる」は印象に残っています。

自らの技術に関心を持つこと

コードのレベルでいうと

  • 普段使っている言語で何か記述するときにその1行1行に意味を込められているのか
  • その1行がどう動いているのか理解しているか

設計の話だと

  • どういう理由でその構成にしているのか
  • その構成にすることでのメリット、デメリットは何でなぜそれを選択したのかを明確なのか

こういった風に「考えること」が大切でありmustです。

自分の仕事について考えること

”自らの技術に関心を持つこと”に近いです。ただ対象が技術なのか、そうでないかの違いに過ぎないと個人的には感じました。

  • 自分は今何をやっているのか
  • 自分は本当にそこに向かっているのか

こういうことを常に考えながら、物事を判断していくというのが仕事について考えることです。

知識ポートフォリオ

  • 少なくとも年に1つ以上は新しい言語をマスターする
  • 3ヶ月ごとに関連ある技術書を読み込む。もしマスターできたなら他のところにも突っ込む
  • 技術書籍以外の本を読む
  • 社外の人と技術的な話をする
  • 常に最先端にいる

新しい言語も最近ちょっと学べていないので改めて学んでいかなければいけないです。その時に時間がかかっても良いから一行ずつなんでこうなのかを理解して進めていきたいと思います。

最先端のいるというところは今まで全くできていなかったのでとりあえずここ2つを押さえておくことにしましたw

1: Association for Computing Machinery

2: www.computer.org

まとめ

一日一字を記さば
一年にして三百六十字を得、
一夜一時を怠らば、
百歳の間三万六千時を失う。

- 吉田松陰 -

毎日少しずつ行うことで習慣化していきます。 とりあえず明日からは

  • 「最先端にいる」のURLに目を通すのを朝の7時 ~ 8時にやる
  • 新しい言語については、今までマスターというレベルでないものがちらほらあるのでそこを洗い出し興味のあるものからやる

という感じで進めていきます。

終わり。

【kintone × marketo連携】両方へ入門してみた②

まさか前回の続きを書くことになるなんて思ってもいませんでしたが、こちらも僕が「あったらいいなー」と思ったので残しておきます。(すごく長くなってしまいました。)

ちなみに前回のがこちら。

katsuyukikun.hatenablog.com

前回の内容をよく見ると

kintone → marketo

の連携のみの説明になっていました。 なので今回は逆側、

marketo → kintone

の連携について書いていこうかなと思います。

やりたいこと

特定の時刻に自動的に起動し、特定のリードのセット(集まり)に対して一度にメール処理を行うことができてかつ、そのメールの開封がkintoneで確認できること

今回の場合の知っておくべきこと

さてさて、今回の登場人物は以下です。

  • スマートキャンペーン

こいつだけです。ただこいつは

  • スマートリスト
  • フロー

という2つの要素を持っているので、スマートキャンペーンというよりこいつらのことを知る必要があります。

スマートキャンペーンとは

個人的にはmarketoの根幹部分にあたるところでないかと思っています。ただじゃあ何かと言われると難しいです。

なぜか? → 設定次第でなんでもできちゃうから

なので基本的に何かmarketoでしようと思ったら最初にこいつから手をつけることになると思います。 これに関してはこれ以上説明できないのでやめますw

スマートリストとは

作成方法とかは画像つきで載っているのでそのまま真似するだけでokです。

バッチおよびトリガーのスマートキャンペーンを理解する - Japanese - Product Documentation

そして、このスマートリストですが2つの役割に分かれます。

  • バッチスマートキャンペーン

    特定の時刻に起動し、特定のリードのセットに対して一度に処理を行います。たとえば、「愛媛県」にいるすべてのリードにメールを送信する場合に使用されます。

  • トリガースマートキャンペーン

    トリガー起動イベントに基づいて、一度に1つのリードに対して処理を行います。たとえば、メール内のリンクがクリックされた場合です。

ドキュメントはさすがですね。まさにこのままで特別わかりにくいこともないと思います。

どこでその区分が分かれるのか、、実際に見てみると

f:id:katsuyukikun:20180816210654p:plain

2つの選び方があります。

  • トリガー
  • フィルター

この2つの選択の仕方で役割が変わります

f:id:katsuyukikun:20180816210948p:plain

  • フィルターのみならバッチスマートキャンペーン
  • フィルターありなしに関わらずトリガーが入るならトリガースマートキャンペーンになります。

フローとは

次はフローについてです。こいつは実際の動作を定義するところです。

f:id:katsuyukikun:20180816211640p:plain

画像にも書いてありますが、右のメニューバーから実際のやりたいことを選ぶだけです。

対応方針

最初

単純にmarketo側が何かメール開封した時の為のトリガー的なのものを用意しているだろうと思っていてこんな感じをイメージしていました。 自作する時とかは画像仕込んで、開封ログ取るとかはやってましたし。。

f:id:katsuyukikun:20180816203946p:plain

ただドキュメントをザーッと軽くみてもそれらしきものが見当たらない。。 なのでちゃんとドキュメント読まないといけないのか〜となり、ちゃんと読み始めました。

スマートキャンペーン - Japanese - Product Documentation

2度目

実際の動作の部分なので(上で説明した)フローを見ればなんとなく想像できるかなーと思ってフローで何ができるのかをポチポチしながら確認。

そうすると・・・ウェブフックを発見しました。

実際にドラッグしてみるとこんな感じ。

f:id:katsuyukikun:20180816212358p:plain

ということでウェブフックを使って外部APIを叩くという方針になります。

f:id:katsuyukikun:20180816213411p:plain

ただここで問題発生!!!!

marketoで用意されているwebhookは「GETとPOST」しか用意されていません。 今回僕はkintoneへPUTで投げたかったので直接叩くのは不可能になりました。

最終

webhookからPOSTでAPIGatewayを呼び出し、lambdaをキック。 kintoneがIP制限していたのでNat Gatewayを用いて設定したElastic IPをkintone側で許可してアクセスするようにしました。

f:id:katsuyukikun:20180816214019p:plain

この辺のやり方はまた別途書きます。

marketo内での設定

さて外側の部分はできたのでいよいよ本題に入っていきます。 残りやることとしては以下です。

  • webhookの設定
  • リストとフローの設定

webhookの設定

ここ読めばokです!

ウェブフックを作成する - Japanese - Product Documentation

ヘッダの設定は「カスタムヘッダ」というところから設定できます。 テンプレートにはpostするときのデータを入れます。 f:id:katsuyukikun:20180817130316p:plain

リストとフローの設定

方針のところでもありますが大きく設定するのが2つで

  • メール送付
  • メール開封とともにウェブフック起動

です。

どうやるか

結論から言うと

  • バッチスマートキャンペーンとトリガースマートキャンペーンを組み合わせる

ことでできます。もう少し具体的に言うと

  • バッチスマートキャンペーンからトリガースマートキャンペーンを呼ぶ
  • トリガースマートキャンペーンで「メール送付とウェブフック起動」設定

f:id:katsuyukikun:20180816231249p:plain

カオスですねwww

まあただこうやると特定の時刻に自動的に起動し、特定のリードのセットに対して一度に処理を行いうことができるかつそのメールの開封がkintoneへ送付することができます。

どう実現したか

一応ドキュメントも載せておきますが、僕はこれ読んでもさっぱりでしたww

[キャンペーンをリクエスト]を使って、トリガー付きスマートキャンペーンを営業に設定する - Japanese - Product Documentation

ただここではスマートキャンペーンをリクエストできるということがわかります。

なので考えました。

バッチスマートキャンペーンからトリガースマートキャンペーンをリクエストできれば解決できるのではないかと。。。

実際にフローの中を調査するとキャンペーンリクエストというものがありました。これで呼び出せるっぽいです。

具体的にはこんな感じ

バッチスマートキャンペーン

  • フィルターは適当にメルアドで指定しています。
  • 「メール送付」して「キャペーンを呼び出し」をしています。 f:id:katsuyukikun:20180817130540p:plain

トリガースマートキャンペーン

ここではメールを開封したのかどうかの確認のために加えています。 ここを挟まないとフィルターに対象のユーザーが引っかからず、開封1回目が取得できない。

f:id:katsuyukikun:20180817130841p:plain

なぜできないのかはやってみるとわかりますが一応説明
  • リクエストを受け取った時点でトリガーキャンペーンは実行される
  • なのでトリガーキャンペーンのリストのフィルターでメール開封ずみユーザーのみ取得するようにする
  • ただメール開封ずみかどうかはmarketoのDBを参照している
  • なのでmarketoのDBに入るタイミングウェブフックのタイミングウェブフックの方が後に実行されなければならない
  • ということで待機するようにキャンペーンを追加した

f:id:katsuyukikun:20180817132812p:plain

トリガースマートキャンペーン

  • 最後はウェブフックで飛ばしています。

f:id:katsuyukikun:20180817131325p:plain

まとめ

振り返るとカオス極まりないのでもし他に良い方法あれば教えて欲しいですww

おまけ

送ったメールの内容をkintoneへ送れない??

送付したメールの内容はどこにも値を保持していないので単純な

marketo → kintone

では対応できなかったです。(僕の理解の範疇なのでもしこうやればできるよってのがあれば教えて欲しいです。)

メールに返信したかどうかをkintoneへ送れない??

こちらどうやら不可能ではないらしいです。ただ工数かかるので一旦置いておくという判断にしました。

以下marketoのコミュニティの方から教えていただいたこと。

Human replies (an end user hitting Reply in their mail client) aren't 
processed by Marketo.
If you use a custom subdomain of your corporate domain
(or an entirely separate domain) for Marketo emails,
you can set replies to that domain to go via a 3rd-party app
 or hosted service which can perform further sorting/processing.

まあなので現実ライン、SES経由とかになりそうな感じがしますけどね。 この辺りも対応したら記事でも書こうと思います。

終わり。

【kintone × marketo連携】両方へ入門してみた

先週の1週間ずっとこれらの連携周りで調べていて、あまり情報ないなぁと思い結構時間かかってしまったのでログ残しておきます。

kintone

名前聞いたことある程度だったので軽く調べてみました。

kintoneとは

サイボウズさんが自社で開発しているクラウドサービスです。 ググってみると色々説明出てきますが、個人的には「webデータベースのアプリを構築してくれるサービス」という認識で良いかなって思っています。

「アプリ」という単位でフロント側とDBを持ち、フロントをそのアプリを使う人が自分で勝手にカスタマイズできる業務支援システムというイメージです。(厳密にいうと違うのかもしれませんが、僕はこのイメージがしっくりきました)

         f:id:katsuyukikun:20180813195703p:plain

カラム名はフロント側で設定したものになってしまうのでこの辺を設計しないと、カオスになるのはなんとなく目に見えると思いますww フロントを簡単にいじれてかつそれがDBに入るので営業さんとかのPDCAを高速に回すとかにはすごく良いんですけどね。。

marketo

こちらも名前を聞いたことがあるという程度だったので軽く調べました。

marketoとは

マーケティングオートメーション」というやつで、顧客一人一人の行動を追え、タイミングタイミングでアクションを変えたりすることができるプラットフォームです。個人的には「そのユーザーが自社(サービス)に興味をどのくらい持っているのかを可視化できて、その興味や状態によってどうアクションするのかを選択できるツール」くらいの認識ですがw

    f:id:katsuyukikun:20180813201716p:plain

documentの見方

どちらも親切なことにdocumentが用意されています。なので基本的にはそこを読めば解決できます。またどちらもdeveloper用の質問サイト?が用意されており、過去のログもみることができるので同じ問題にぶち当たった人がどう解決したのかなどもわかるので便利です!

ただ僕が一瞬あれ?と思った箇所があったのでそこだけ説明しますw
それはmarketoのdocumentです。

developers.marketo.com

連携させたかったのでapiのdocumentをみたのですが、全て英語です。なので英語が苦手な人は少し辛いかもしれないですね。。頑張りましょ!

でその中でみていくとpostする際のデータの持たせ方が載っています。

http://developers.marketo.com/rest-api/endpoint-reference/lead-database-endpoint-reference/#!/Leads/syncLeadUsingPOST

f:id:katsuyukikun:20180813202759p:plain

これをみたとき僕は一瞬「めっちゃデータ構造複雑だな、SyncLeadRequestとLeadと・・・を持たせるのかよ〜。どう持たせれば良いのかな?」と思ってましたww

f:id:katsuyukikun:20180813203209p:plain

actionはstringで持たせるよ!とか書いているのに途中でLeadというのが出てきます。それを下で構造的に説明しているだけでしたw

inputの中身ArrayでLeadがHashで書かれており、パラメータのタイプがbodyとなっているのでcurlだとこんな感じになりますね

curl -H 'Content-Type:application/json' -d "{"action":"createOnly","input":[{"id":1, "reason": ""}]}" https://test.co.jp/

やりたかったこと

kintoneで更新した時に、marketoのAPI叩いてmarketoにleadの(見込み客のことをleadと呼ぶ)新規作成or更新をかける。

      f:id:katsuyukikun:20180813204234p:plain

実装

kintone側

データ更新かけた後の処理の実装方法はこちら

developer.cybozu.io

marketoのAPIは外部APIになるのでこちら

外部APIの実行 – cybozu developer network

これら2つを合わせるだけなので

(function() {
 
    "use strict";
    //レコード追加画面の保存成功後イベント
    kintone.events.on('app.record.create.submit.success', function(event) {
      var postHeader = {"Content-Type": "application/json"}
      var postData = {"action":"createOnly", "lookupField": "email","input":[{"email":"test@test.co.jp", "firstName": "test"}]}
      kintone.proxy('https://*****.***.co.jp/rest/v1/leads.json?access_token=xxxxx', postHeader',postData}, function(body, status, headers) {
        //success
        console.log(status, JSON.parse(body), headers);
      }, function(error) {
        //error
        console.log(error);  //proxy APIのレスポンスボディ(文字列)を表示
      });
    });
})();

marketo側

このURLのdocument通りに、API叩くようのユーザーを作成し、そのユーザーにAPIロールを付与してアクセストークンを発行する。

Quick Start Guide for Marketo REST API - Marketo Developers

そのあとはREST APIのdocumentをみて対応させていく。

Lead Database - Marketo Developers

新規作成の時は、/rest/v1/leads.jsonでinputのところに初期設定したい値を入れるだけです。 ちなみにlookupFieldというのは、データ整合性をとる時にどこのフィールド(カラムのことですね)を対応させるのかということです。 今回だと

"lookupField": "email"

としたので、marketo側でメルアドを参照し同じメルアドがあったら更新、なかったら作成という挙動になります。

挙動

kintoneで更新かけると、marketoの「Lead Database」というところの「全ての顧客」にデータが作成されます。更新されるときはそのユーザーが更新されます。

まとめ

蓋を開けてみると、非常にシンプルですが初めてだとそもそもの理解に時間がかかるものだなと思いました。こういうものでも時間をかけずにできるように、今回詰まったところをもう一度洗い出して、なぜ詰まったのかを考え次に繋げたいです。

終わり。

新規システム構築する際の仕事の進め方

転職初日に新規立ち上げの部署への移動が決定し、最近は四苦八苦しながらプロジェクトを前に進めています。

 

その進め方で今度やるときはこうして進められると初動としてはよかったのではないかと思ったのでログ残します。

 

その前に

前提

サービス自体はすでにありました。簡単にいうと人材を集客して教育し事業所へ流す、その時の事業所への紹介料でマネタイズするというビジネスモデルです。

 

   f:id:katsuyukikun:20180728135833p:plain

 

そしてざっくりとした業務フローですが営業がもともと強い会社なので、集客してきたユーザーに架電し、面談実施を行いそこで事業所へ紹介できる人材なのかを見極める。そして、見極めた後、就業支援やら教育などをして一定の力をつけて事業所へ流すという感じです。 

 

 状態

縦割りのチーム分けになっています。

なので架電部隊は架電部隊でデータを管理し、教育支援やらなんやらのところはまた別の方法でデータ管理しているという状態です。

 

そして、 データ連携もところどころうまくできていなく、担当の方が手作業で入力しているという感じでした。

 

やったこと

全体像の把握

正直アサインはされたものの「何して良いのかわからない、そもそもここはどこなんだ?」という状態だったので、各フローのリーダーや事業責任者などへのヒアリングを行いました。

 

そこでは主に以下を聞いていました。

  • そもそもなぜシステムを入れるという判断になったのか
  • KPIから乖離しているポイントはどこでどのくらい離れているのか
  • なぜそのような乖離が生まれたのかの仮説を持っているのか
  • 各フローは現在どういう作業を行なっているのか
  • その中で今困っていることはなにか
  • 今後どのようにしていきたいなどの理想はあるか

あとはその話を聞いてもっと知りたいことなどを追加で聞きました。

 

これを通して全体像の把握は完了し、なんとなくボンヤリとした各部門の業務フローと困っていることはわかります。

 

各フローの実際の作業の把握

ボンヤリとした、業務フローはわかったのですが具体的にどういう風に行なっているのはやはり実際に足を運ばないと理解はできなかったので各部門の見学なり体験なりしました。

フローの詳細、どんなデータをどこにどうやって管理しているのか、そしてそのデータはどこへ連携しているのかの理解できます。

 

理想形の設計

ここは未だ手が出ず。

何を理想形としどうあるのが業務的に一番楽で成果出るのか、そうあるにはシステム的にどうあるべきなのかを考える必要がありました。

ただこれを考えるにあたり、重要視したのが問題の切り出し方。

 

「切り出しが大きすぎると成果までに時間がかかる」

 

→なるべく問題を小さくしながら随時改善の効果が実際の現場に反映されるようにしないといけない。

  

なのでここを模索しながら、成果に繋がるところを改善していく方針にしました。

 

現状の問題の洗い出し

各フローのヒアリングや見学・体験を通して一旦下記を洗い出すことにしました。

  • どこが改善できそうなのか
  • どこに手を加えるのが一番事業としてインパクトがあるのか

 

そして、洗い出した中で自分なりの解決策を考え、その工数を見積もる。

見積もったあと、各部署に提案。そのまま実行して良いのか、それとも修正した方が良いのかを詳細を詰めていきました。

 

実行

スプリント形式の方が、自分はやりやすいのでgithubでissue管理し、何をいつまでにやるのかというのを管理することにしました。

 

反省

なんと言っても、スピード感は遅すぎたように思います。

というのも最初は「ビジネスモデルを理解し、どうあるべきなのか理想形を考える」というとてつもなく大きなものから考えようとして、時間が無駄にすぎていっていました。

また理想形ができたところで、現状のフローをいきなりガラッと変えることはあまりないと思います。ましてはその理想形が本当にベストなのかも未定です。

この場合は現状のもう実際に動き出しているフローがあるのでそこをどう改善するか、どういう設計にした方が効率良いのか、そういうところを考え先に実行しながら、同時進行で理想形を設計していくというのがトータル早く成果に結びついたのではないかなと思っています。

 

まとめ

やることとしては以下の順番がよいかなと現時点では考えています。

  1. 全体像の把握
  2. 各フローの理解
  3. 現状の問題点の洗い出し + 理想形の模索
  4. その問題点に関して自分なりの解決案を考える + 理想形の模索
  5. 工数見積もり
  6. 相談と修正
  7. 実装 + 理想形の模索
  8. 運用と改善 + 理想形の模索

 

上流工程の本とかを読んでもイメージはつくが実際にどう進めていいのかなかなかわからなかったので、 引き続き毎日考えながら次へ活かせるようにやっていきたい。

 

 

 

終わり。

 

 

 

 

 

 

【rails5.2 + mysql + rspec】既存のDB上でrspecを実行したらTable doesnt existと言われた。

以前、docker上にDB立ててリモートからdumpしてrestoreするの時間かかるからそのままリモートDBに繋げるようにすれば楽だよねーって記事を書きました。

katsuyukikun.hatenablog.com


僕が今回業務で対応していたのが、DBからデータを取得するだけのアプリケーションだったのでこれで良いと思っていました。


そう、思っていたのです。。。。


だってselectだけだから間違えてDB吹っ飛ぶなんてことは絶対ないと思っていたから・・・



しかし今回、DB吹っ飛びましたw

開発用DBなので別に影響範囲もくそもないのですが、精神的に穏やかでなかったのでログとして残します。


構成

今回はこんな感じ。


f:id:katsuyukikun:20180724213515p:plain


もともとPHPで書かれたアプリケーションとそのDBとしてmysqlを利用していました。

今回僕は、phpのアプリケーションのある動作をフックにし、そのmysqlのDBともう一つ違うところにあるDBにアクセスし、データ統合やら整形をして、違うDBにpostするというアプリケーションをrailsで対応しようとしていました。

実際

僕のやることとしては、
「新しいレポジトリ作成して、既存のDBたちにアクセスできるように0からのアプリケーションを作る」
です。

なのでロジックやらなんやら書いてテスト書いてdeployフロー作って、deployさせるってかんじというのはすぐ思いつくと思います。


なので実行しました。


そしてテストを書きます。

Gemfileにrspec系のgemを加えます。

group :development, :test do
・
・
・
  gem 'rspec-rails'
  gem 'rails-controller-testing'
end


そしてターミナル上でbundle install。僕はdocker使っていたのでこんなかんじです。

$ docker-compose run web bundle install


そしてrspecで必要なファイルを用意する必要があるのでrailsのコマンドを打ちます。

$ docker-compose run web bundle exec rails generate rspec:install


これで必要なものが完成。

で、テスト書いてrspecを実行。

$ docker-compose run web bundle exec rspec spec/


そしたらTable doesnt existとエラーになりました。
この後は何回やってもこのエラーになってしまいます。

なぜか??

rspecで必要なファイルを用意した時に使ったrailsのコマンド

$ docker-compose run web bundle exec rails generate rspec:install


を実行した時にrails_helper.rbというファイルが作られます。
その中のコードを見ていくと

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!


というのがあります。
結論、この設定のせいでDBが吹っ飛びました。


これrails4.1から設定されているものらしいです。
github.com


コードを追う

githubでいうとここですね。

github.com


maintain_test_schemaというのは、テスト実行時にActive Recordがテスト用データベーススキーマをdb/schema.rb(またはdb/structure.sql)に基いて最新の状態にするかどうかを指定するものらしいです。

で、続きでここですね。

github.com


でこのif文を見てみます。

if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?

今回はこれですね。

    def any_migrations?
      migrations.any?
    end

結論

僕はphpを触ったことがないのでなんとも言えないのですが、今回はphpでDBがもともと作成されていました。
つまり僕が新しく作ったrailsのアプリケーションにはmigrationのファイルが一つもないわけです。

そして

          current_config = Base.connection_config
            Base.clear_all_connections!
            system("bin/rails db:test:prepare")
            # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
            Base.establish_connection(current_config)

このロジックが流れ、新しいデータベースを作りにいってしまいました。
結果、何もない綺麗さっぱりなDBが出来てしまった→DBが吹っ飛んだ
ということになっていたらしいです。


コメントアウトでも書いてあるのですが、自然と作られるものってそこまで読まないので気をつけたいものです。。


というか、やっぱり何かあったときのためにdumpしようねって話ですね。


おまけ(docker内へのdumpの仕方)

ターミナルで

$ mysqldump -u USER_NAME -p -h HOST_NAME DB_NAME > ~/Desktop/hoge.sql

OUTPUT_FILE_NAME:これはtest.sql的なので空ファイルを自分で作っておくということです。


でdump出来ます。
もし踏み台使わないと行けない場合は、トンネル掘ってから実行すれば問題ないです。

でdockerでdb作るときにvolume忘れずにセットしする。

docker-compose.ymlの中で忘れずに。

    volumes:
      - ./db/mysql/volumes:/var/lib/mysql

docker起動してからdumpしたファイルをコピーし、docker内に入ってrestoreする。

$ docker cp ~/Desktop/hoge.sql コンテナの名前:/var/lib/mysql
$ docker exec -it コンテナの名前 /bin/bash
# mysql -u root -p 適当なdatabase名 < /var/lib/mysql



終わり。

【rails5.2 + mysql5.7】docker上にrailsアプリケーションを立ててリモートDBと繋ぐ

先週くらいからようやくdockerに入門しました!(遅い・・)

本とか自分のPC上では軽く触ったことあったのですが、業務でがっつりdockerで環境構築をしたことはなかったです。

そして今回業務で環境構築する際に、

「どうせならdockerでやろう」

と思って作業してみたもののちょびっと詰まったところがあったのでこれから入門する人の参考になればと思いログとして残しておきます!

 

構成

rails5.2、mysql5.7で作ろうと思っていました。

dockerの公式のチュートリアルでもありますが、結構docker上にアプリケーションとDBの両方置いちゃうケースがあると思います。

 f:id:katsuyukikun:20180718205812p:plain  

  なので僕も最初これで環境作ろうと思っていました。

ただ今回の環境構築では新規DBを作成ではなく既存のDBを使いたい!!

既存のDBをダンプしてそれを新規DBでリストアするか〜とも思いましたが、データ量かなり大きそうで断念。。。。

 

ということで結局この構成にしました。

f:id:katsuyukikun:20180718210333p:plain  

初めての構成だったので本当にできるのか不安だったのでまずはローカルからリモートのDBに繋げられるかを確認。

f:id:katsuyukikun:20180718221547p:plain

リモートDBサーバーのホスト名(xxx:xxx:xxx:xxx)

ユーザー名(hoge)

データベース名(database_name)

$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p

結果は

bash: mysql: command not found

それ以前の問題でしたwww

brewを入れているのでmysqlの5.7をインストールした後にもう一度実行しました

$ brew install mysql@5.7
$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p
bash: mysql: command not found

結果は変わらず・・

$ which mysql

と打っても何も出ないので、パスを通すことにしました。 (なぜこうなるのかは後で調べるつもりです) 僕は.bash_profileにいつも書くのでそこに追記します。

$ echo export PATH=/usr/local/opt/mysql@5.7/bin:$PATH > .bash_profile 
$ source .bash_profile
$ which mysql
/usr/bin/mysql

準備できた。

$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p
Access denied for user

おっふ、、、弾かれましたな。

インフラチームに聞いたところローカルからは直接繋げられないよ、とのことでした。

なので踏み台を利用して繋ぐ方針に変更。

f:id:katsuyukikun:20180718213546p:plain

トンネル掘って繋げれるようにしました。
(「sshポートフォワーディング」とかでググると出てくると思います)

僕の場合、ローカルから踏み台サーバーへは

ssh 踏み台サーバーのホスト名

でログインできていたので以下になりました。

$ ssh -NL 3307:db_host:3306 ssh_host
$


db_hostはデータベースのホスト名
ssh_hostは踏み台のホスト名

接続確認

$ mysql -h 127.0.0.1 -P 3307 -u db_user -p

> これでmysqlにログインできるはずです


db_userはデータベースログインのユーザ名

やめたかったら

$ ps aux | grep ssh

とやると、フォワーディングしているプロセスが出てくるのでkillすればokです!

ここまできてようやく今回の構成が決まりました汗。

dockerからローカル経由してフォワーディング先のリモートDBに繋げそうですね。

f:id:katsuyukikun:20180718215028p:plain

コード

f:id:katsuyukikun:20180719002005p:plain

あとはdockerからローカルのところを実装すればうまくいきそうです!

なのでコードを書いていきましょう。チュートリアルと同じなので迷うこともないかなと思います。

まず作業するディレクトリを作り、その上でDockerfileとdocker-compose.ymlとGemfile、Gemfile.lockを作ります。

$ mkdir work
$ cd work
$ touch Dockerfile
$ touch docker-compose.yml
$ touch Gemfile
$ touch Gemfile.lock

そして各々コードを記述していきます。

Dockerfileの中身

FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp
Gemfileの中身

source 'https://rubygems.org'
gem 'rails', '5.2.0'

Gemfile.lockは空。

docker-compose.ymlの中身

version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"

コンテナ上でrails newします。

$ docker-compose run web rails new . --force --database=mysql

新しいファイルができたらconfig/database.ymlを修正します。 ここ最初どう書けば良いのか迷いましたが最終的にこれでいけました!

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: リモートDBのユーザー名
  password: リモートDBのパスワード
  host: docker.for.mac.localhost
  port: 3307

development:
  <<: *default
  database:リモートDBのデータベース名

dockerからローカルのMacを見るときはdocker.for.mac.localhostで見てくれるそうです! あと今回、フォワーディングのポートを3307にしているのでport: 3307にしています。 ここも適宜変更してください!

ここまでできたらあとは起動するだけです!

$ docker-compose up --build

localhost:3000にアクセスするとお馴染みの画面が表示されるはずです。

f:id:katsuyukikun:20180718220141p:plain

全然関係ない話。

今回初めて知ったのですが、Active Recordって使えるmysqlのバージョンがあったんですね笑。 ORマッパーもあるし、よくよく考えると当たり前なんですが意識したことが一度もなかったです!

というのも今回既存のDBのmysqlが5.0.9という状態で・・・アクセスしたら

Your version of MySQL (5.0.9) is too old. Active Record supports MySQL >= 5.1.10.

という見たことないエラー出てちょっと感動しましたw こういうところもしっかりみておきたいですね!

github.com

終わり。。