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

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

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分って早くね?

【Laravel Practice02】デプロイサーバーにAnsibleをインストール

f:id:blue_goheimochi:20160624022454p:plain

デプロイサーバーにAnsibleをインストールします。

前回はこちら
【Laravel Practice01】デプロイサーバー、Webサーバー、DBサーバーの準備 - 終電23時15分って早くね?

前回も言いましたが本当はたぶんデプロイサーバーに向けてAnsibleをホストOSから実行すればよいです。
が、Windows環境で(面倒なので)ローカルからAnsibleを実行するのではなくデプロイサーバーにインストールしてみます。

デプロイサーバーにログイン

手順どおりにいけばlaravel-serversディレクトリにいると思いますのでそこで、

$ vagrant ssh deploy

というコマンドでデプロイサーバーにログインします。
複数の仮想サーバーがある場合はvagrant ssh [名前]SSHで対象サーバーにログインできます。
仮想サーバーが1つしかない場合は、名前を明示せずvagrant upだけでOKです。

EPELリポジトリの導入

エンタープライズ Linux 用の拡張パッケージ(EPEL) は、 Red Hat Enterprise Linux (RHEL) 向けの高品質なアドオンパッケージであり、CentOSScientific Linux (SL) のような RHEL からスピンオフしたディストリビューションと互換性のある、Fedora プロジェクトで有志によって作成されたパッケージになります。FedoraRHEL のアップストリームであり、EPEL のアドオンパッケージは主に RHEL 向けにビルドされた Fedora リポジトリをソースとしています。

https://fedoraproject.org/wiki/EPEL/ja

CentOSの標準パッケージには含まれないパッケージをyumでインストールするためにEPELリポジトリを導入します。
Ansibleはこちらのリポジトリからインストールするような想定です。

$ sudo yum install -y epel-release

デプロイサーバのコンソールから上記コマンドで、EPELリポジトリのインストールができます。

Ansibleのインストール

今回利用しているCentOSyumUbuntuのaptなどのパッケージ管理システムを使えば、
ソースコードを落としてきて個別にインストールする必要がないので楽ですね。

最新版が使いたい!となると、パッケージとして提供されてないケースがあるのでそういう場合は手動で最新版をインストールする方法をぐぐって試してみてください。

$ sudo yum install -y ansible

と、yumでさくっとインストールすると、

$ ansible --version
ansible 2.1.0.0

という感じでAnsibleのバージョン2.1がインストールできました。

vagrat up 時に自動でEPELリポジトリとAnsibleをインストールするようにしてみる

前項まででいったん、今回の目的である「デプロイサーバーにAnsibleをインストールする」というのは完了です。
が、せっかくなのでVagrantの機能を使って、vagrant upした際に自動的にデプロイサーバーにEPELリポジトリとAnsibleをインストールしてみます。

Vagrantのプロビジョニングの機能

vagrant upで仮想環境を起動したあとに実行する処理を指定できるのが、Vagrantのプロビジョニングの機能です。

www.vagrantup.com

仮想環境起動後に実行可能な詳しい処理は公式サイトをご確認ください。
ChefやPuppet、Ansibleやシェルスクリプトなどなどで処理を指定する事が可能ですが、
今回はシェルスクリプトでやります。

Vagrantfileの記述

github.com

とても簡単!

server.vm.provision "shell", :path => "provision.sh"

shellの部分でプロビジョニングの方法(chefとかansibleとか)を指定し、
シェルスクリプトの場合は実行するファイルを指定します。

provision.shの記述

github.com


こちらも簡単ですね。

冪等性を意識するために、if文なんかを書いてみました。

yumでインストールしたEPELリポジトリrpmコマンドとgrepを使ってチェックしています。

if [ -n "`rpm -q epel-release | grep -w 'not installed'`" ]; then
  sudo yum install -y epel-release
fi

rpm -qはパッケージの詳細を表示するコマンドです。

インストールされている場合は

$ rpm -q epel-release
epel-release-7-6.noarch

インストールされていない場合は

$ rpm -q epel-release
package epel-release is not installed

のような結果になるので、grep -w 'not installed'でワード検索し、
インストールされていない場合のみインストールするような感じです。
たぶん他のパッケージでも応用して使える気がします。

Ansibleはwhichを使ってチェックしています

if [ ! `which ansible` ]; then
  sudo yum install -y ansible
fi

whichはコマンドを探してくれます。
コマンドがある場合はそのパスとともに結果が返ってきます。

$ which ansible
/usr/bin/ansible

ない場合は、

$ which ansible
/usr/bin/which: no ansible in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin)

no ansibleと出力されていてコマンドが存在しないことが分かります。

whichコマンドの終了ステータス(成功したら 0、失敗したら 1)で判定して、Ansibleがインストールされていない場合のみインストールを実行します。

デプロイサーバーを一度破棄する

$ vagrant destroy -f deploy

上記のコマンドで一度、デプロイサーバーを破棄します。仮想環境を削除できます。
-fのオプションは強制的に削除するオプションで、これをつけないと削除するかどうかを確認されるので、「Y」を応答して削除しましょう。

デプロイサーバーを再構築する

$ vagrant up deplo

前回すでに打ったコマンドと同じですが、再度デプロイサーバーを構築するためにvagrant upを実行します。
そしたらしばらくコンソールを眺めてみましょう。

==> deploy: Running provisioner: shell...
    deploy: Running: C:/Users/HOGEHOGE/AppData/Local/Temp/vagrant-XXXXXXXXXX.sh


のようなログが出てきたらシェルでのプロビジョニングがはじまります。

デプロイサーバーにログインして、EPELとAnsibleを確認する

$ vagrant ssh deploy

でログインし、

$ rpm -q epel-release
epel-release-7-6.noarch
$ which ansible
/usr/bin/ansible
$ ansible --version
ansible 2.1.0.0

となれば無事Vagrantでの自動のプロビジョニングは完了です!

まとめ

  • EPELリポジトリをインストールする
  • EPELリポジトリCentOSの標準パッケージに含まれないパッケージを提供してくれる
  • AnsibleはEPELリポジトリからインストールする
  • yumでインストールできるから簡単ですね!
  • ubuntuの場合はapt-getでインストールできる
  • Vagrantfileにシェルでのプロビジョニングを自動実行するように追加
  • シェルスクリプト以外でもChefやAnsibleなどが起動完了後に実行できる
  • 冪統制をシェルスクリプトでも考えてみると楽しい
  • Ansibleなどの構成管理ツールはそんな冪統制をよしなにしてくれたりもするよ

次は、デプロイサーバーでAnsibleをデプロイサーバー自身に実行してみたいと思います。

次の記事はこちら!
【Laravel Practice03】デプロイサーバーにAnsibleを実行する - 終電23時15分って早くね?


初めてのAnsible

初めてのAnsible

【Laravel Practice01】デプロイサーバー、Webサーバー、DBサーバーの準備

f:id:blue_goheimochi:20160622231431p:plain

Laravelを勉強するにあたって、色々な方面にも興味が出てしまったので、
Laravelアプリケーションを動かすためのサーバー環境構築とか含めて色々やってみようと思ったのでログとして残してみます。

今のところですが、画像のような感じで、

  • デプロイサーバー
  • Webサーバー
  • DBサーバー

を用意して、

  • 各サーバーはAnsibleでプロビジョニングする
  • デプロイサーバーからlaravel/envoyでデプロイする
  • サンプルのLaravelアプリケーションを作る

っていうところまではできたらいいなと思っています。
デプロイは他のツールも試してみたいなぁ。
とりあえずローカルにVirtualBoxVagrantで仮想環境を立ててやってみます。

デプロイサーバー、Webサーバー、DBサーバーの準備

ローカルにVirtualBoxVagrantでCentOS7の仮想環境を3台用意します。
VirtualboxVagrantは各自ご用意ください。
ちなみに私はWindows環境ですが、Chocolateyというソフト(Macでいうbrewみたいなやつ)でインストールしてます。簡単。

Vagrantfile

github.com

こちらのコミットをご覧ください。
3つのサーバーを起動するだけのVagrantfileになっております。
README.mdにも書きましたが、

$ git clone https://github.com/blue-goheimochi/laravel-servers.git
$ cd laravel-server
$ vagrant up

で、3つのサーバーをすべて起動する事ができます。

サーバーの定義
config.vm.define "deploy" do |server|
~~~~~~
end

config.vm.define "web" do |server|
~~~~~~
end

config.vm.define "db" do |server|
~~~~~~
end

と、Vagrantfileに記述することで複数のサーバーを定義できます。
deploywebdbという名前を設定しています。

個別にサーバーを起動したい場合は、

$ vagrant up deploy

のように、定義した名前をvagrant up [名前]とつけることで個別に起動できます。

Boxファイルの指定

各サーバーで同じBoxファイルを指定しています。

今回はbento/centos-7.1というBoxを使います。
CentOS7のBoxファイルです。

server.vm.box = "bento/centos-7.1"

【参考】bentoプロジェクトのBoxファイル一覧
bento | Atlas by HashiCorp
chef社が正式にサポートしているbentoというプロジェクトのBoxのようです。
多分安心して使っていいはず・・・!
色々なディストリビューションがそろってます。

IPアドレスの指定

IPアドレスは各サーバー別々のものを指定します。
すでに仮想サーバーがローカルに存在する場合は、それともかぶらないIPアドレスを指定しましょう。

server.vm.network "private_network", ip: "192.168.33.70"
vagrant-vbguestプラグインの自動アップデートを無効にする

vagrant-vbguestの自動アップデートを無効にします。

if defined?(server.vbguest)
  server.vbguest.auto_update = false
end

boxファイルが作成されたときのVirtualBoxのバージョンと自分がインストールしているVirtualBoxのバージョンが違う際に、
VirtualBox Guest Additionsを自動で更新してくれるプラグインvagrant-vbguestプラグインなのですが、
自動アップデートされてしまう際に、フォルダの共有が失敗する場合があるっぽかったので無効にしています。

アップデートしたい場合は手動で

$ vagrant vbguest deploy

と実行すればできると思います。

【参考】dotless-de/vagrant-vbguest
https://github.com/dotless-de/vagrant-vbguest

VirtualBoxの設定

VirtualBox周りの設定です。

vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]

okisanjp.hatenablog.jp
上記サイトを参考に、わたしの環境でネットワークが遅かったので指定しています。

vb.gui = false

これをtrueにすると仮想ディスプレイが一緒に起動しますが、いらないのでfalseを指定。

vb.memory = 1024
vb.cpus = 2

こちらはメモリとCPUのコア数設定です。

まとめ

  • デプロイサーバー、Webサーバー、DBサーバーの3台をローカルに準備
  • Vagrantfileで複数台の仮想環境を指定できる
  • 個別にサーバーを起動することも可能
  • BoxファイルはChef社のbentoプロジェクトのBoxファイルが安心っぽい
  • IPアドレスはかぶらないように設定する
  • vagrant-vbguestの自動アップデートは無効にした
  • VirtualBoxのネットワークが遅いときの設定をいれた
  • 仮想ディスプレイはオフ

次は、デプロイサーバーにAnsibleをインストールして自分自身をプロビジョニングしてみたいと思います。

本当はたぶんデプロイサーバーに向けてAnsibleをホストOSから実行すればよいのですが、
Windows環境で(面倒なので)ローカルからAnsibleを実行するのではなく、
デプロイサーバー(兼プロビジョニングサーバー)という風に位置づけてAnsibleのインストールをしたいと思います。


次の記事はこちら
【Laravel Practice02】デプロイサーバーにAnsibleをインストール - 終電23時15分って早くね?