終電23時11分って早くね?

都内のIT企業で働くカラオケ大好きエンジニアの雑記

PHPカンファレンス仙台2019に参加しました!

phpcon-sendai.net

最高&最高でした!(当社比1.5倍)
今回は大きなカンファレンスではじめてスピーカーとして参加させていただきました。

登壇資料

Togetterもまとめてみました。

「レガシーなPHPアプリケーションのリプレイスプロジェクトにおける取り組み 」PHPカンファレンス仙台 2019 - Togetter

聴きに来てくださったみなさま、Tweetしてくださったみなさま、質問してくださったみなさま、この場を借りてありがとうございました!
何か1つでも持ち帰っていただけたものがあるとしたらとてもうれしいです。

登壇内容

  • リプレイスの決め手となった問題点はなんだったか?
  • どのようなリプレイスプランを立てたか?
  • プロジェクトはどう管理したか?
  • 新アプリケーションで取り組んだことはなにか?
  • 今後のチャレンジは?

というような流れで、我々の会社で取り組んでいるPHPアプリケーションのリプレイスについてお話しさせていただきました。

リプレイスプロジェクトの中でも特に大事だったなぁと思う部分にはツイートもいただいたのですが、

というような、「対象の(業務)ドメインの知識を知る」という部分がとても大切ではないかなと強く感じています。
しっかりとドメインの知識を知るということがベースになり、よいコードがかけたり、よい進め方ができることに繋がって行くのではないかと思う次第であります。

といったものの、うまくいかなかったことやまだまだできていないことがたくさんあるので、その部分については粛々とカイゼンできるようにやっていきたいと思っています。

まとめ

スピーカーのみなさま、スポンサーのみなさま、参加者のみなさま、スタッフのみなさま!!

ありがとうございました!!!

余談

カンファレンスに参加者・スピーカー・スタッフで参加するという実績を全て解除しました!

「PHPフレームワーク Laravel Webアプリケーション開発」を読めば開発現場で使える「ヒント」を見つけられる

今回、レビュワーをさせていただくというありがたい経験をさせていただきました!ありがとうございます。
見本誌を頂戴いたしましたので、感想を書いていきたいと思います。

f:id:blue_goheimochi:20180926182634j:plain
PHPフレームワーク Laravel Webアプリケーション開発

初心者から1歩踏み出したい!という人にはぴったりの本

とのっけから、「初心者にはちょっと・・・」というような見出しになってしまいましたが、先にそれを否定しておきます。

初心者を完全に置き去りにしているのか?というとそんなことは全くなく、「第1部 Laravelの基礎 Chapter1 Laravelの概要」ではLaravelとはどんなフレームワークか?というところからはじまり、開発環境の構築、初めてのアプリケーション作成(ルーティング、ビューの設定、ログイン認証、テストコード、マイグレーション、イベント)と1つ1つ進めていけば「Laravelってこんな感じなのねー」というのを理解しながら読み進めていけるのではないかな?と思います。
Chapter1でいったん、全体感を掴んだあとに「Chapter2 Laravelアーキテクチャ」で実際に手を動かした内容を踏まえながら、「Laravelはこう動いているんだよ」というのをさらに深く学んでいくというような構成になっているので、初心者の方でも順を追ってLaravelを学んでいくことができると思います。

初心者から1歩踏み出す

ある程度、「もうLaravelを使っているよ!」という人は、「Chapter3 アプリケーションアーキテクチャ」から読み始めるとよいのかなと思っています。
この章では、まずMVCADR(Action Domain Responder)というアプリケーション開発をする時にとりいれるパターンを対比させながらどのように責務を分けると、よりよいアプリケーションになるのか?というヒントを示してくれます。

そこからさらに、レイヤードアーキテクチャリポジトリパターン、ドメイン駆動設計などのエッセンスに触れながら、実際、どういうパターンが良い・悪いというような話ではなく、ご自身の現場で立ち向かっているアプリケーションの規模の大小や開発チームの人数、開発期間などを考えながら「このアプリケーションにとってどういう設計が望ましいのか?」というのをご自身で考えるときの指針・ヒントとして活用できるような内容になっていると思います。

全3部の構成になっている

  • 第1部 Laravelの基礎
  • 第2部 実践パターン
  • 第3部 Laravelアプリケーション開発手法

の構成になっており、前述の部分は第1部の内容にあたります。

第2部 実践パターン

第1部だけでも、かなり実践的にアプリケーション設計についてどう考えるのかということを学べるなぁという印象なのですが、第2部ではLaravelで標準で使えるような基本的なことはもちろんですがさらに、

  • Chapter5 データベース
  • Chapter6 認証と認可
    • JWT認証、OAuthクライアントによる認証・認可
  • Chapter7 処理の分離
    • イベント、キュー、CQRS
  • Chapter9 テスト

のように、一部の抜粋ではありますが、それぞれのChapterでLaravelをさらに1歩進んで使うためのヒントが散りばめられています。

第3部 Laravelアプリケーション開発手法

Chapter11 テスト駆動開発の実践」では、第1部、第2部で学んだことを、モバイルアプリケーションをTDDで実装するというお題になっていて、小さいコードからリファクタリングを進めながら徐々にアプリケーションを構築していくというフローを体験していくことができます。
本当に丁寧に1つ1つ進んでいくので、実際の業務に活かすイメージをもって読み進められるのではないかなと思いますので、ぜひ読みながら一緒に手を動かしてみて欲しいです!

まとめ

と、なんとまぁまとまりがありませんが、初心者の方もステップアップしながら読めると思いますし、 初心者から1歩踏み出したいという方にはヒントがたくさん散りばめられていて新しい発見をすることができるのではないかな思います。

Laravelでアプリケーション開発を実践されている皆さまには当然オススメしたい本であるとともに、別のフレームワークを使っているよという皆様であっても、何かステップアップのためのヒントになることがあるのではないかなーと思いますので、ぜひ読んでみていただきたいです!

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

おまけ

f:id:blue_goheimochi:20180926182656j:plain
vs エリック・エヴァンスのドメイン駆動設計

めっちゃ厚い!

PHPerKaigi 2018に前々夜祭から参加して最高でした

f:id:blue_goheimochi:20180309144131j:plain

phperkaigi.jp

最高&最高でした!(当社比1.5倍)
自身初のスタッフとしての参加でした。
せっかくなので、どうしてスタッフ参加したの?というところまでさかのぼって雑に書き残しておきたいなーと思ったので雑に書いていきたいと思います。

PHPerKaigiのスタッフやりたいです!と言った日

2017年10月25日のPHP勉強会で @tomzoh さんが「PHPerKaigiやるよ!」という発表をしていた日がその日でした。

speakerdeck.com

実はそのちょっと前に「何かPHPのカンファレンス的なのをやるらしい」というのを他の方から聞いていて、
「次のPHP勉強会で声かけよう・・・!」と思っていたのもあり、PHP勉強会に参加&突撃して @tomzoh さんに声をかけさせてもらったという次第です。実際は、PHP勉強会の懇親会中には声をかけられず(ビビッて)2次会のグリフォンで話しかけました。

ほんとにすごく緊張してて、なんて声かけたかは完全に忘れましたが、
この1歩がなかったら、「最高だった!」と思えてないんじゃないかなと考えると大きな1歩だった気がします。

スタッフをやりたいと思ったきっかけ

@shin1x1 さんのPodcastPHPの現場」の「12. カンファレンスの現場(tomzoh / cakephper)」を聞いたあとに無性に「やってみたいぞー」ってなりました。
Podcastではカンファレンスに一般・スピーカー・スタッフ(実行委員長)として参加されたことのあるお三方が、参加する側・主催する側の話などの色々な視点からカンファレンスについてお話しているのが印象的ですごく面白く聞いていて、主催側楽しそうだ・・とだんだんと(何回も聞くうちに)気持ちが高まっていったのですが、そんな中でも印象に残っていたのが @cakephper さんが懇親会における自分ルールとして「懇親会で1人になっている人に話しかけに行く」というのを挙げていたところでした。

Podcastの中で「誰かと話して帰ったほうが満足度は高い!大人なんだから自分から(話しかけて)楽しまなきゃ!」という話もあったのですが、自分自身が懇親会などでボッチになってしまった経験がある(大人なのに!)ので、なるほど、そういうとき話しかけられたとしたら自分ならうれしい(しホッとする)なと思っていました。

「懇親会にきてる人は、喋りたいと少しは思っている人」という話もされていて、そう話したいんだけどビビってしまうんです・・・とも思っていましたw

でもその @cakephper さんメソッドを使って少しでも自分のようなになってしまう人が少なくなって、「楽しかった!」って帰る人が増やせるかもと思ったのがスタッフやりたいなと思った一番の理由だった気がします。
(なんと前述のグリフォンには @cakephper さんもいらっしゃって勝手にうおおーってなってました)

まだ聞いていない方はぜひ聞いてみてください!
と、どうやら「PHPerKaigi の現場」というのが新たに収録されるとのこと!楽しみ。

blog.shin1x1.com

[追記:2018-03-27]
「PHPerKaigi の現場」の収録が公開されました!!

前回のと合わせて聞くと1.5倍(当社比)面白いと思うのでまだの方はぜひです!

開催までの間スタッフとしてどんなことをやった??

実は何回か行われたスタッフMTGのうち1回しか参加できませんでした・・・。(12月中旬頃にあった初回MTGにも参加できず)
乗り遅れてしまったのと、自分のチキンさがあいまって、主体的に動けずあまり役に立たなくてとても申し訳ない気持ちでいっぱいです。。
少しでも何かやらねば・・・ということで、前々夜祭で宣伝をさせていただいたのですが、それが唯一のやれたことかなと(勝手に)思っています。

PHPerKaigi 前々夜祭(非公式)

laravel-meetup-tokyo.connpass.com

ということで、宣伝をしたのが「Laravel Meetup Tokyo vol.10」でした。
非公式ですが、「Laravel MeetUp Tokyo PHPerKaigi 前々夜祭バージョン!」ということで開催されていました。

そこでLTの3番手で宣伝をしてきました!
内容としては2017年7月に「Laravel Meetup Tokyo vol.9」で発表した内容の振り返りと現状がどうなっているか?のような発表だったのですが、

ということで、この宣伝の効果があったのかどうか定かではないですが、「ちゃんと仕事したー!」というのを勝手に自分に言い聞かせながら当日を迎えることになります。(もし、このLTで行くことを決めた方がいらっしゃったら教えてくださいw)

PHPerKaigi 前夜祭

ドキドキしながらCoconeriホールに向かい、会場準備したり受け付け準備したり荷物が届いたり袋詰めしたりビデオの準備したり、何もかもがはじめての私は右往左往しながら、みなさんの動きにがんばってついていくという感じでした。
他のスタッフの方は別のカンファレンススタッフもしたことがある人が多いようでとてもテキパキとあっという間に会場が出来上がって行きました!すごかった。

袋詰めがホントに高速でした。

前夜祭はTrack A以外の場所にいた感じだったのでトークは聞けなかったのですが、QRコードをスキャンしたり、バドワイザーを飲んだり、Track Bの準備をしたり、ソフトドリンクを買いに行ったり、バドワイザーを飲んだりしていました。

あっという間にクロージングとなり、少し片付けをしてから PHPreParty へ移動しました。

PHPerKaigi 前夜祭終了後に・・・

参加者のみなさんのお見送り最中に、1人の方が名札を見せながら近づいてこられて、「明日名札どうするか聞かれるのかな?」とか思ったのですが、そうではありませでんした・・!なぜ話しかけてくれたか?なんですが、なんと「PHPerKaigi 前々夜祭」の発表(宣伝)を見ていただいたからだったんです。

すごい。めちゃくちゃにうれしい瞬間でした。
スタッフをされていた @uzulla さんもおっしゃっているのですが、

トーク以外の所について言えば、アイコンつきの名札はとてもいいですね、話している相手が誰なのかわかる。

PHPerKaigi 2018に行ってきた、やってきた、話してきた #phperkaigi - uzullaがブログ

をまさに体感した瞬間でした。
もしアイコンが印刷されていなかったとしたら、見つけていただけなかったかもしれないし、自分からも見つけられなかったのかもしれないなぁーと。本当によかった!
@kunit さんとはほとんどお話はできなかったのでどこかでお話せねば)

PHPerKaigi 本編

前日、寝る前にTwitterで前夜祭のTLをニヤニヤしながら読んでいたのですが、寝落ちしていて、パッと起きてスマホ見たら電源が切れていて、「えっ・・・」と思って時計を確認したらまだ大丈夫な時間・・・セーフ・・!という目覚めから1日がスタート。

この日の前半はQRコードスキャン業を行い、受付縮小に伴い、Track BのLaravel相談会をがっつり覗き込んだり、IRTに人が多くなってきたので椅子を運んだりなどをしていた。
Track Bの雰囲気はすごく良かった気がします!後半はTrack Aで質問マイクを持っていく職人をしていました。
ランチタイムセッションのあとは急にTrack Aの温度が上がるのを感じで「人間ごはんを食べると発熱するんだなぁ」とか意味わからないことを考えながら空調の調整ボタンを押したりしていた。

少しだけ聞けた発表の中では、

speakerdeck.com

BEAR.Sunday 作者の @koriym さんが発表の中で言っていた、

がとても心に残っています。

あとは漢の中の漢にいつかなりたい。(Hack)

少しTrack Bの片付けをしてあっという間にLTがはじまりあっという間に終わり。すごい熱量。

「Lumenで堅牢なAPIを設計する」の えんどぅーさん(@Fendo181)の落ちが面白すぎました!

PHPerKaigi 懇親会

ついに懇親会! @cakephper さんメソッドを使いました!!
緊張しすぎててどなたと喋ったかが分からない・・・のですが、ボッチ(になっていたのかは定かではないのですが)お一人でいた方に突撃して話しました!きっと少しは楽しんでいただけたのではないだろうか・・
もしかしたらほかにもお一人だったかたがいたのかな・・・

鞠花がおいしかったりハンバーガーがおいしかったり、ロゴの秘密が明かされたりとまたあっという間に過ぎていきました。
ものすごい余韻を残しつつの終了。

PHPostParty

懇親会のあとさらに池袋に移動し、最後のワイワイ。
そーだいさん(@soudai1025)さんとチームの文化作り的な話をさせていただいたのがとても心に残っていて、
「1人~2人で社内勉強会とか何かしら行動に移すと、ポツポツと人が増えてきて、どこかのタイミングで急にブレイクスルーがくるんですよね〜」というようなことをおっしゃっていました。
これは前々夜祭で発表した部分にもつながってるなと思ったのですが、「なるほどやっぱりまだまだ自分は頑張りが足らないのかもなー!」と感じました。ブレイクスルーがくるまで頑張らねば。

まとめ?

ものすごく雑に脈絡なく思ったことやあった出来事を書くだけになってしまったのですが、わたしのPHPerKaigi 2018はこんな感じで個人的にはものすごく最高&最高でした。

スタッフをやらせていただいたおかげで体験できたことが、たくさんあったということは間違いないと思います!!いろいろ感じたこと学んだことをこれからに活かしていきたい!

そして振り返ってみるとやはり1番最初の1歩がなかったらこんなに最高に思えなかったかなと思うので、スピーカーでもスタッフでも懇親会で一声かけるでも、仕事で何かやってみるとかでも、新しい趣味に挑戦しようでも、もし何かを「ちょっとやってみようかな」と思っている人がいたら1歩踏み出してみるのがよいのかなと思います!
来年もスタッフできるといいな!

参加者のみなさま、スピーカーのみなさま、スポンサーのみなさま、スタッフのみなさま、本当にありがとうございました!!
また来年よろしくお願いいたします!

PHP CodeSnifferでtestsディレクトリにだけ除外ルールを追加する

最近はPSR-2の規約に沿ってコードを書くことを意識しています。

www.php-fig.org

特にチームで開発している状況でコーディングスタイルのルールが明確に決まってないと、
A「インデントはスペース2つ!」
B「いや、スペース4つ!!」
C「タブやろ」
みたいな細かい話になりがちだと思います。
(PSR-2ではスペース4つのルール)

そんなときにPSR-2のようなコーディングスタイルガイドに寄り添っていくのはとてもいいなーと感じています。
(自分が今まで書いてきたルールと違う部分は慣れるまでちょっと気持ち悪いですが)

なぜtestsディレクトリだけに除外ルールを追加したかったか?

プロジェクトのコード全体をPSR-2に沿って書くようにしてたなかで、
テストを書く際に2つ困ったことがあったからです。

  • テスト名を日本語にした場合にエラーになる
  • 抽象クラス(abstract)のテストの場合にエラーになる

テスト名を日本語にした場合にエラーになる

PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
----------------------------------------------------------------------
SOURCE                                                           COUNT
----------------------------------------------------------------------
PSR1.Methods.CamelCapsMethodName.NotCamelCaps                    9
----------------------------------------------------------------------
A TOTAL OF 9 SNIFF VIOLATIONS WERE FOUND IN 1 SOURCE
----------------------------------------------------------------------

「メソッドがキャメルケースではないよ!」というエラーになります。

/**
 * @test
 */
public function 保存ができること()
{
    $this->todo->addItem('アイテム1');
    $this->assertTrue($this->todo->save());
}

public function test取得ができること()
{
    $this->todo->addItem('アイテム1');
    $this->assertTrue($this->todo->save());
}

PHP@testアノテーションをつけることで、日本語だけの関数名でテストを書けます。
(下のようにtestをプレフィックスにつけても日本語で書けます。)

日本語なのでエラーになるのは当然ですね。
英語が苦手なメンバーがいたり、日本語のほうがテスト内容が分かりやすかったりはするのでたまに日本語でテストを書いたりしています。

でもアプリケーションのコードでは関数は日本語で書いていない。
全体の除外ルールとしてしまうこともできますが、アプリケーションのコードのほうで、もしキャメルケースで書かれていない場合はエラーとして拾えなくなってしまいます。

抽象クラス(abstract)のテストの場合にエラーになる

PHP CODE SNIFFER VIOLATION SOURCE SUMMARY
----------------------------------------------------------------------
SOURCE                                                           COUNT
----------------------------------------------------------------------
PSR1.Classes.ClassDeclaration.MultipleClasses                    2
----------------------------------------------------------------------
A TOTAL OF 2 SNIFF VIOLATIONS WERE FOUND IN 1 SOURCE
----------------------------------------------------------------------

「1つのファイルに複数クラスの定義があるよ!」というエラーになります。

抽象クラスのテストをしたいときは、テストのクラスのなかにもう1つクラスを書いています。

class FugaTest extends \PHPUnit\Framework\TestCase
{
    public function testReturnFuga()
    {
        $fuga = new Fuga();
        $this->assertSame('fuga', $fuga->get());
    }
}

class Fuga extends Hoge
{
}

めちゃくちゃ適当ですがこんな感じ。

2つのルールをtestsディレクトリで除外するphpcs.xmlの書き方

PSR1.Methods.CamelCapsMethodName.NotCamelCaps
PSR1.Classes.ClassDeclaration.MultipleClasses

この2つのルールをtestsディレクトリ以下のみ除外するには下記のように設定すればOKでした。

<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
    <exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
    <exclude-pattern>*/tests/*</exclude-pattern>
</rule>

それぞれのルールに対して除外ディレクトリを設定してあげればOKです。
全体としてはこんな感じ。

<?xml version="1.0"?>
<ruleset name="Custom_PSR2">
  <description>Custom ruleset Based on PSR2</description>
  <exclude-pattern>*/vendor/*</exclude-pattern>
  <exclude-pattern>*/bootstrap/cache/*</exclude-pattern>
  <exclude-pattern>*/database/*</exclude-pattern>
  <exclude-pattern>*/node_modules/*</exclude-pattern>
  <exclude-pattern>*/public/*</exclude-pattern>
  <exclude-pattern>*/resources/*</exclude-pattern>
  <exclude-pattern>*/storage/*</exclude-pattern>
  <rule ref="PSR2">
    <exclude name="Generic.Files.LineLength.TooLong" />
  </rule>
  <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
    <exclude-pattern>*/tests/*</exclude-pattern>
  </rule>
  <rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
    <exclude-pattern>*/tests/*</exclude-pattern>
  </rule>
</ruleset>

※メソッド名が長くなりがちなので全体のルールにGeneric.Files.LineLength.TooLongも設定してます

ちょっと場所が分かりづらいですが、PHP CodeSnifferのWikiにこのやり方で書いてあります。
github.com

MySQL5.5.6(以下?)でSupersetがうまく動かない(場合がある)

f:id:blue_goheimochi:20170210100609p:plain

AirbnbによるオープンソースのBIツールのSuperset

github.com

Re:dashもちょろちょろ触っているのですが、
ダッシュボードのカスタマイズが柔軟そうだなー
と思いSupersetの環境を作ってみたのですがデモは動くんだけど、
手元のDBに接続してみるとうまくいかない・・・となりました。

原因

おそらくMySQL5.5.6(以下?)で発生するSTR_TO_DATEをWHERE句に使うことによるバグ。
MySQL Bugs: #56271: STR_TO_DATE in date compare incorrect results

Supersetが内部でdateのフォーマットを整形した形でSQLを発行しているのですが、
その部分でSTR_TO_DATEが使われているためだと思われます。

if target_type.upper() in ('DATETIME', 'DATE'):
    return "STR_TO_DATE('{}', '%Y-%m-%d %H:%i:%s')".format(
        dttm.strftime('%Y-%m-%d %H:%M:%S'))
return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S'))

superset/db_engine_specs.py at 37fb56c61ceb339079ff117971fc91bdd678db80 · airbnb/superset · GitHub
ifの条件にはまらなければ(DATETIME、DATEのカラムじゃなければ)大丈夫なのかな。

どんなSQLが発行されるか?

こんな。

mysql> SELECT COUNT(*) AS count
    -> FROM items
    -> WHERE created_at >= STR_TO_DATE('2000-01-01 00:00:00', '%Y-%m-%d %H:%i:%s')
    -> AND created_at <= STR_TO_DATE('2017-12-31 23:59:59', '%Y-%m-%d %H:%i:%s');

一見正しそうに見える。

STR_TO_DATEの結果

mysql> SELECT STR_TO_DATE('2000-01-01 00:00:00', '%Y-%m-%d %H:%i:%s');
+---------------------------------------------------------+
| STR_TO_DATE('2000-01-01 00:00:00', '%Y-%m-%d %H:%i:%s') |
+---------------------------------------------------------+
| 2000-01-01 00:00:00                                     |
+---------------------------------------------------------+

mysql> SELECT STR_TO_DATE('2017-12-31 23:59:59', '%Y-%m-%d %H:%i:%s');
+---------------------------------------------------------+
| STR_TO_DATE('2017-12-31 23:59:59', '%Y-%m-%d %H:%i:%s') |
+---------------------------------------------------------+
| 2017-12-31 23:59:59                                     |
+---------------------------------------------------------+

たぶん大丈夫。

SQLの結果

mysql> SELECT COUNT(*) AS count
    -> FROM items
    -> WHERE created_at >= STR_TO_DATE('2000-01-01 00:00:00', '%Y-%m-%d %H:%i:%s')
    -> AND created_at <= STR_TO_DATE('2017-12-31 23:59:59', '%Y-%m-%d %H:%i:%s');
+-------+
| count |
+-------+
|     0 |
+-------+

0件・・・・・

STR_TO_DATEを使わないで直接指定してみる

mysql> SELECT COUNT(*) AS count
    -> FROM items
    -> WHERE created_at >= '2000-01-01 00:00:00'
    -> AND created_at <= '2017-12-31 23:59:59';
+-------+
| count |
+-------+
|   999 |
+-------+

カウントがちゃんと取れる・・・・。

MySQLのバージョン確認

mysql> SELECT version();
+--------------+
| version()    |
+--------------+
| 5.5.6-rc-log |
+--------------+

MySQLの別のバージョンで試す

mysql> SELECT version();
+-------------------+
| version()         |
+-------------------+
| 5.7.12-0ubuntu1.1 |
+-------------------+

mysql> SELECT COUNT(*) AS count
    -> FROM items
    -> WHERE created_at >= STR_TO_DATE('2000-01-01 00:00:00', '%Y-%m-%d %H:%i:%s')
    -> AND created_at <= STR_TO_DATE('2017-12-31 23:59:59', '%Y-%m-%d %H:%i:%s');
+-------+
| count |
+-------+
|   999 |
+-------+

>|abc|
mysql> SELECT COUNT(*) AS count
    -> FROM items
    -> WHERE created_at >= '2000-01-01 00:00:00'
    -> AND created_at <= '2017-12-31 23:59:59';
+-------+
| count |
+-------+
|   999 |
+-------+

どっちも取れる・・・!!!!

MySQLのリリースノートをあさったり

MySQL 5.5 Release Notes

MySQL Bugs: #56271: STR_TO_DATE in date compare incorrect results
これだ!

51 Changes in MySQL 5.5.7 (2010-10-14, Release Candidate)

Comparison of one STR_TO_DATE() result with another could return incorrect results. (Bug #56271)

5.5.7で直ったっぽい!!!!

まとめ

ということで、5.5.7以上を使っていればたぶん大丈夫だと思われます。
5.5.6以下でもうまく行くケースはたぶんある。(TIMESTAMPとかのカラムをTime Columnに設定すればたぶん大丈夫)
strftimeで変換したあともう一度STR_TO_DATEする必要あるのか・・な・・?

【Laravel Practice04】AnsibleでNTPサーバーを構築

f:id:blue_goheimochi:20160629162151p:plain

デプロイサーバーにNTPサーバーをAnsibleで構築してみます。
今回、仮想環境にはCentOS7を使っているので、chronyをインストールしてみます。

ntpdとchronyの違い

CentOS6まではデフォルトのNTPサーバーはntpdだったようですが、
CentOS7ではchronyが標準となっているようです。

大きな違いとしては

ntpd と chronyd の大きな違いの 1 つは、コンピューターのクロックを管理するために使われるアルゴリズムにあります。

とのことで、システムクロックをntpdより迅速に調整することが可能らしいです。

[参考]
第15章 chrony スイートを使用した NTP 設定

yumモジュールでchronyをインストール

今回作業したところは、以下のコミットログにまとまっていますのでご参照ください。
github.com
今回は、ansible/roles/chrony/tasks/main.ymlの1ファイルのみ追加しています。

- name: install chrony
  yum: name=chrony state=latest

Ansibleのyumモジュールでchronyをインストールする際の記述です。

$ sudo yum install -y chrony

と同等の処理が行われます。

Ansible yumモジュールのドキュメント
yum - Manages packages with the yum package manager — Ansible Documentation

他のパッケージをインストールしたい場合は、
たとえばhttpdであれば

- name: install httpd
  yum: name=httpd state=latest

と書けばOKです!簡単ですね。

デフォルトで設定されているNTPサーバーを無効にする

デフォルトで設定されているNTPサーバーを無効にします。
pool.ntp.orgのサーバーは世界中すべてのタイムサーバーを指定しているそうで、
時刻を精度よく保つためには近いゾーンのものを設定するのがよいみたい。
(あとで設定します)

[参考]
pool.ntp.org: プールを利用するためにNTPを設定する方法は?

chronyの設定ファイルは/etc/chrony.confに作成されていると思うので、これを今回はAnsibleのreplaceモジュールで操作してみます。

Ansible replaceモジュールのドキュメント
replace - Replace all instances of a particular string in a file using a back-referenced regular expression. — Ansible Documentation

- name: disable servers
  replace: >
    dest=/etc/chrony.conf
    regexp='^(server \d+.centos.pool.ntp.org iburst)'
    replace='# \1'

destで対象のファイルを指定して、
regexpでファイル内の変更したい箇所を正規表現で指定できます。
replaceは変換後の文字列ですね。

今回は、regexpでマッチする行の先頭に#がつくようにタスクを書いてみました。
2回目以降のAnsible実行の際は、正規表現に当てはまらなくなるので、変更はされません。
changedになってしまう場合は正規表現の書き方を変えてみるとよいと思われます。

日本のタイムゾーンのNTPサーバーを追加する

先述した通り、最も近い日本のタイムゾーンのNTPサーバーを設定します。

- name: add servers
  blockinfile:
    dest: /etc/chrony.conf
    state: present
    content: |

      # ntp servers
      server ntp.jst.mfeed.ad.jp iburst
      server ntp.nict.jp iburst

Ansible blockinfileモジュールのドキュメント
blockinfile - Insert/update/remove a text block surrounded by marker lines. — Ansible Documentation

blockinfileはAnsible 2.0から追加されたモジュールです。
複数行を追加したいときなんかに使います。

insertafter: '[対象の文字列(正規表現可)]'を指定すると対象の行の後に追加したりできますが、今回はファイルの一番最後に、NTPサーバーの設定を追記するようにしています。
詳しい書き方はAnsibleのドキュメントをご参照ください。

ntp.jst.mfeed.ad.jpntp.nict.jpの2つのNTPサーバーが日本標準時を提供してくれているのでそちらを指定させていただきました。

時刻同期を内部から許可する設定を追加

ローカルネットワークからのアクセスを許可する設定を追加します。

- name: add allow ntp client
  lineinfile: >
    dest=/etc/chrony.conf
    insertafter="#allow 192.168/16"
    line="allow 192.168.1.0/24"

Ansible lineinfileモジュールのドキュメント
lineinfile - Ensure a particular line is in a file, or replace an existing line using a back-referenced regular expression. — Ansible Documentation

今度はlineinfileモジュールを使ってみました。
1行の行追加の場合、こちらのモジュールを使うとよさそう。
#allow 192.168/16の下の行にallow 192.168.1.0/24を追加するタスクです。

サービスの起動と自動起動の設定

- name: chrony start and enable
  service: name=chronyd state=started enabled=yes

serviceモジュールを使って、chronydのスタートと自動起動の設定ができます。

Ansible serviceモジュールのドキュメント
service - Manage services. — Ansible Documentation

$ systemctl start chronyd
$ systemctl enable chronyd

のコマンドの実行と同様のタスクになります。
nameに対象のサービスを指定することで別のサービスに対しても実行可能です。

手動での時刻あわせ

手動での時刻あわせのコマンドを実行します。

- name: adjust the time
  shell: chronyc -a makestep
  changed_when: false

shellモジュールはその名前の通り、シェルコマンドを実行できるモジュールです。

Ansible shellモジュールのドキュメント
shell - Execute commands in nodes. — Ansible Documentation

shellモジュールを利用する場合、冪統制は自分で担保する必要があります。(と思います。。)
特に先述のファイルに何かを追加するような処理をshellモジュールで書くと、実行するたびに同様の記述がファイルに書き込まれてしまいます。

例)

- name: add hoge
  shell: echo 'hoge' >> /tmp/text.txt

このような場合実行するたびにhoge/tmp/text.txtに追記されてしまい、そのつど、changedのステータスになってしまいます。
Ansibleのモジュールにはそういう冪統制を担保してくれるものが多いと思うので、可能な限りAnsibleのモジュールに頼ったほうがよいでしょう。
様々なモジュールがあるのでぜひドキュメントを眺めてみてください。

今回の場合は、手動の時刻あわせのコマンドを実行することになりますが、これは毎回実行されても影響はないと考えます。(ファイルの書き込みとかは起こらないと思うので)
こういう場合は、changed_when: falseを指定しておくと必ず、okのステータスになりますので指定してみました。
changed_when: [条件]という風な指定で、条件に合致したときだけchangedとできるので、特定の条件がある場合はそのように記述しましょう。

以上、NTPサーバーを構築するときのAnsibleのタスクを説明してみました!
内容はボリューミーですが、Ansibleの基本っぽい部分が分かるのではないかなぁと思います。

Ansibleはドキュメントもしっかりしていて、日本語の情報も結構あるので、
「このコマンドに対応するモジュールはあるかな?」と思ったら検索してみてください!
大体あります。笑

まとめ

  • CentOS7はchronyが標準のNTPサーバー
  • ntpdよりchronydのほうが時間調整が早いらしい
  • Ansible yumモジュールの簡単な使い方
  • Ansible replaceモジュールの簡単な使い方
  • NTPサーバーはタイムゾーンが近いところを設定する
  • Ansible lineinfileモジュールの簡単な使い方
  • Ansible serviceモジュールの簡単な使い方
  • Ansible shellモジュールの簡単な使い方
  • shellモジュールを使うときは冪統制は自分で考えなきゃいけないから可能な限りAnsibleのモジュールを使うと楽
  • Ansibleはドキュメントがしっかりしてるので見るとはかどる

次回もAnsibleを使ってさらにデプロイサーバーの環境を整えます!
ユーザー追加とかを想定中。

ツッコミお待ちしております!!

初めてのAnsible

初めてのAnsible

【Laravel Practice03】デプロイサーバーにAnsibleを実行する

f:id:blue_goheimochi:20160624231936p:plain

まずはAnsibleをデプロイサーバーに向けて実行してみます。
AnsibleがインストールされているPC自身にプロビジョニングができるので、
Macの環境構築なんかもAnsibleでできるみたいです!

blog.shin1x1.com

ところでAnsibleとは?

ChefやPuppetやItamaeと同列に扱われる、構成管理ツールの1つです。
エージェントレスでシンプルなところが特徴とされています。

エージェントレス?

Chefなど一部の構成管理ツールは、対象のサーバーにもChefがインストールされている必要があったりします。
Ansibleは対象のサーバーにsshで接続できれば、対象のサーバーに特別な準備はいりません。

Ansibleについての説明はしますが、最小限な感じになると思うので、
分からない場合は、コメントいただいたり、ググったり、下の本を読んでみたりしてください・・・!

初めてのAnsible

初めてのAnsible

とりあえずコマンドを実行してみる

とりあえずエラーが発生しますが、コマンドを打ってみます。

まずは

$ vagrant ssh deploy

で、デプロイサーバーにログイン。

$ sudo ansible-playbook -i /vagrant/hosts -l deploy -c local /vagrant/ansible/deploy.yml
ERROR! the playbook: /vagrant/ansible/deploy.yml could not be found

はい、エラーになりましたね。
/vagrant/ansible/deploy.ymlというPlaybookはありません!とのことです。
ひとつずつ解決していきます。

プレイブック?

プレイブックとは作業手順書と置き換えて考えていただければいいと思います。
サーバを構築するための作業手順書です。

構成管理ツールがない時代は、秘伝のテキストファイルにサーバーの構成方法のメモ書きが残されていたりしていたそうですが、
そのような手順を「YAML」という形式で記述したものがプレイブックになります。

デプロイサーバー用のプレイブックということでdeploy.ymlという名前にしてみました。

deploy.yml

- hosts: deploy
  become: yes
  roles:
    - common

こんな感じのファイルになります。

1行目ではhosts: deployとして、対象のホストを指定しています。

勘のいいかたはお気づきかもしれませんが、最初に打ったコマンドでhostsのファイルをしております。

-i /vagrant/hostsでhostsのファイルを指定できます。
デフォルトでは/etc/ansible/hostsのファイルが参照されます。

hostsファイル

[deploy]
127.0.0.1

こんな感じでhostsは設定します。ローカル環境を示す127.0.0.1またはlocalhostを指定します。

2行目のbecome: yessudoで実行する設定です。
実行するユーザーを切り替える際にはbecome_user: deployのような感じでユーザー名を指定します。

3-4行目ではrole(ロール)を指定しています。

laravel-servers
  └─ansible
      └─roles
          └─common
              └─tasks
              │   └─main.yml
              └─vars
                   └─main.yml

こんな感じのディレクトリ構造になっていて、rolesのディレクトリの中にcommonディレクトリを配置するような構造です。
ロールで管理しなくてもプレイブックに直接記述する方法でもOKなのですが、役割が分かりやすくなるので、この形が自分の好みです。

tasksディレクトリの中のmain.ymlに実際にサーバーに対して実行したい処理を書いていきます。

main.yml

github.com

上記のファイルをご参照ください。
たくさん色々書いてあります・・・!笑

name: XXXXXXXXXXで書かれている1つ1つタスクと呼ばれる部分です。
名前は分かりやすい名前をつけてあげましょう。

yum:service:shellなどはAnsibleが用意しているモジュールです。

All Modules — Ansible Documentation

http://docs.ansible.com/ansible/list_of_all_modules.html

すごくたくさんもモジュールが用意してあります。
shellcommandのモジュールを使えば、通常のコマンドとしてもかけます。
が、モジュールを使えば、たとえばyumであれば、

- name: install yum-cron
  yum: name=yum-cron state=latest

と書くことで、インストールされてなければインストール、すでにインストールされていなければ何もしないというような冪統制を考慮してくれるので、書ける方法があればモジュール経由で書くのがいいかと思います。

各モジュールはドキュメントやグーグル先生に聞いて調べてみてください。。笑

varsディレクトリのmain.yml

vars/main.yml
timezone: Asia/Tokyo

ロール用の変数を宣言する事ができます。

role/main.yml
- name: set timezone
  shell: timedatectl set-timezone "{{ timezone }}"

という感じで、{{}}で囲って変数を使用します。

commonロールのタスク

下記が今回実行するタスクです

以上が実行されます。

ここまでの作業のコミットは以下になります。
github.com

次から再度コマンド実行していきますので、上のファイルと同じ構成で各ファイルを配置してみてください。

再度コマンドを実行

今度はコマンドを実行してもエラーがなく進むと思います。完了するまでにちょっと時間がかかります。

$ sudo ansible-playbook -i /vagrant/hosts -l deploy -c local /vagrant/ansible/deploy.yml
PLAY [deploy] ******************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [common : install epel repository] ****************************************
changed: [127.0.0.1]

~~~~~~~~~~~~~~~~~

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=13   changed=10   unreachable=0    failed=0

ok:は実行されたタスク
changed:は今回の実行で変更が行われたタスク
というようなイメージです。

13個が実行さで10個のタスクで今回変更があったということになります。

もう一度同じコマンドを打ってみましょう。

$ sudo ansible-playbook -i /vagrant/hosts -l deploy -c local /vagrant/ansible/deploy.yml
PLAY [deploy] ******************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [common : install epel repository] ****************************************
ok: [127.0.0.1]

~~~~~~~~~~~~~~~~~

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=12   changed=0    unreachable=0    failed=0

前回より早く終わったかと思います。
12個が実行さで今回変更はありませんでした。
数が1つ減っているのは、skippingになっているためです。
指定の条件に合致しない場合は実行されないタスクがあるので、これはこれで正常な動作になります。
気になる人はset timezoneのタスクを見てみてくださいね。

補足ですが-c localのオプションはAnsibleをローカルモードで実行したい場合のオプションになります。

まとめ

  • AnsibleはMacの開発環境構築にも使える!
  • Ansibleとはエージェントレスでシンプルな構成管理ツールの1つである
  • 同様の構成管理ツールにはChef、Puppet、Itamaeなどがある
  • プレイブックに構築手順を「YAML」形式で書く
  • hostsは適切に設定しましょう
  • ロールに切り出すと役割ごとにタスクを管理しやすいと思う
  • Ansibleのモジュールはたくさんあるのでドキュメントを見る
  • ロール用に変数も定義できる
  • ローカルモードで実行したい場合は-c localのオプションをつける

次回はAnsibleを使ってさらにデプロイサーバーの環境を整えていきます・・!

次の記事はこちら!
【Laravel Practice04】AnsibleでNTPサーバーを構築 - 終電23時15分って早くね?