kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

Facebook APIのバージョンアップ対応 バージョンとは

Graph APIのバージョンアップをすることがあったので
対応内容とか周辺知識をまとめておきます。
マーケティングAPIは対象外です。

バージョンについて

まず、バージョンアップといっても何をバージョンアップすればいいか
分からなかったので調べました。

見るべきところは、アプリ自体のバージョンと
Graph APIを実際にコールしているところのバージョンです。
私の場合は、Graph APIを直接叩くことはしていなく
phpやjsなどのSDKを用いていたので、そこを見ました。

アプリのバージョン

facebook for developers で自分が管理しているアプリのバージョンです。
アプリのダッシュボードの画面で、今どのバージョンを使っているか確認できます。

ここのバージョンがいったい何に影響しているのかは
よく分かりませんでした。
多分、Graph APIでバージョンを指定しないで叩いたときのデフォルトの
バージョンなんだと思います。

Graph APIを叩くときに指定するバージョン

facebookが出しているsdkで指定するバージョンです。
SDKの初期化時に指定する方法と、
APIを叩くときに指定する方法があります。

php

手元にあった sdk5.4.4では、
default_graph_version を指定しなくても大丈夫ですが、
Facebook\Facebook の中を除くと

// @todo v6: Throw an InvalidArgumentException if "default_graph_version" is not set

というコメントがあったので、将来的に必須になるようです。

初期化時に指定するデフォルトバージョン

$fb = new Facebook\Facebook([
  'app_id' => '{app-id}',
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.2',
  ]);

叩くときに指定する方法

初期化時にバージョンを指定しなかった場合には
ここでバージョンを指定する必要があります。

$fb->get('/v2.9/me/feed', '{access-token}');

or

$fb->get('/me/feed', '{access-token}', null, 'v2.9');

js

いいね!ボタン構成ツールで取得できるコードを見ると
バージョンを指定している箇所があります。
私の場合は、いいねボタンくらいしか使ってなかったので
ここだけしかありませんでした。

生成されたもの

<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v2.9&appId={app-id}";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

自前で初期化している場合

<script>
window.fbAsyncInit = function() {
    FB.init({appId  : '{app-id}',
             status : true,
             cookie : true,
             xfbml  : true,
             version: 'v2.9'
    });
};
(function(d, s, id) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) return;
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/ja_JP/sdk.js";
        fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

Gitのリベース途中でエラーメッセージ - VCS_INFO_get_data_git:223

現象

Gitのリベース途中でコンフリクトの修正中に起こった問題です。
git checkout --oursとか打つと、下記のメッセージが出てきて困りました。

VCS_INFO_get_data_git:223: no such file or directory: .git/rebase-apply/msg-clean

解決方法

oh-my-zshのissueで話題に上がってました。

zshのバージョンを上げると解決するって書いてあるんですが、
私の場合は、意図したバージョンのzshが使えてなかったために起きてました。

環境によっては、zshのバージョンの確認方法に気をつけなればなりません。
バージョンの確認方法は2つあって、

  • zsh –version
  • echo $ZSH_VERSION

です。

❯ zsh --version
zsh 5.3.1 (x86_64-apple-darwin15.6.0)
❯ echo $ZSH_VERSION
5.0.8

私の場合、上記のような結果となってした。

何をしたか

/etc/shells に /usr/local/bin/zshを追記して、 改めて chsh -s /usr/local/bin/zsh

無事に解決しました。

AnsibleのPlaybookを作る前に考えておくこと

この記事はAnsible Advent Calendar 2016 - Qiitaの24日目の記事になります。
Ansibleで個人開発環境を構築して、一通り使ってみて感じたことを残しておきます。
基本的に自分のメモ程度なので、何かあったらコメントお願い致します。

ベスト・プラクティス

取り敢えず読んでおきます。
更新されているので、たまに読み返すと新しい情報が追加されていることがあります。

Best Practices — Ansible Documentation

ディレクトリ構成

ベスト・プラクティスで下記2つが紹介されています。

qiitaでも別解として話題に上がっています。

どれがいいかはプロジェクト次第です。
作り始める前にどれがいいか検討すべきですが、決められなければ、
とりあえず、ベスト・プラクティスのDirectory Layoutで始めてみてあとで構成変えるでもいいと思います。

ちゃんと設計する

最初からロールを分割しすぎない

「早すぎる最適化」はよくないです。 ロール名を変数名の接頭辞にするのが慣例なので、
タスクを別のロールに移動するとか考えるとリファクタリングコストがそこそこかかります。

同じミドルウェアのタスクでも分割したほうがいいタスクがある

例えば、MySQLのロールの中で

  • MySQLを起動するLinuxユーザー作成
  • インストール
  • プロジェクト用のデータベースの作成

をすべて一つのロールでやらないほうがいいかもしれないです。
Playbookにした時に、ユーザーの作成とインストールまで行って、
何か別のタスクを実行した後に、データベースの作成という流れにしたいときに困ります。

共通で使う変数

各ロールやPlaybookで共通で使用したい変数名の命名規則は最初に決めておいたほうがいいです
後から変更するとそこそこリファクタリングコストがかかります。

部分実行しやすいようにする

Playbookの開発中に毎回毎回すべてのタスクを実行していては効率が悪いです。
--start-at-task--tags を使えるようにしておくと再実行しやすいので時短につながります。
なので、タスクやロールにtagはつけておいたほうがいいです。
上記オプションをつけて実行するとき、pre_taskは実行されないっぽいので注意が必要です。

冪等性をどこまで担保するか

なんでもかんでも冪等性を担保しようとすると辛い場面が出てきます。

ドライラン対策

確認のために -C をつけて実行することがあると思います。
しかし、一部のモジュール(commandやshell)はこのドライランに対応していないです。
冪等性を担保するなら自分でどうにかする必要があります。

不要になったタスクをどこまで面倒みるか

例えば、Vagrantで各自の開発環境を用意するとして、そこに流すPlaybookの場合を想定します。
前のバージョンでは作っていたけど、いらなくなったディレクトリが出てきたとき、次のバージョンで削除するタスクを書いておくのか?
という問題が出てきます。
前のバージョンを実行していたメンバーが次のバージョンを実行したときに
そのいらなくなったディレクトリを削除するためだけに タスクを残しておくと本来必要のないコードが残っている状態になります。
コードの見通しが悪くなるので、タスク自体消したほうがいいですが、いつ消すかはちゃんと考えておかないとメンバーに迷惑がかかることもあります。

モジュール一覧を一通り眺めてみる

意外と細かいことをやってくれるモジュールもあるのでcommandモジュール使う前に、
Ansibleのドキュメントのモジュール一覧を見てみるとよいです。
apt_key とか apt_repository とかcommandでやりがちなモジュールもあったりします。

Vagrant環境での注意

ディレクトリのオーナー、パーミッション

Ansibleの問題ではなくVagrant(VirtualBox)の使い方の問題です。
ホストOSのパーミッションはゲストOSでは変えられないことを念頭に置きましょう。

ディレクトリのオーナーやパーミッションを変えるようなタスクを作るとき
ホストOSとの共有ディレクトリは、Vagrant側から流したPlaybookでは変更できません。
Ansibleの実行結果はchengedとなりますが、実際には変わりません。

個人開発環境を作るときなんかは、ソースは共有ディレクトリに置くことが多いと思うので
その辺意識しておかないと、なんで変わってないんだとなってはまってしまうので注意です。

例えば、ソースを置くディレクトリは、 wwwをオーナーにしたい場合を考えます。 Vagrantfileでディレクトリ共有の設定でwwwを指定すれば、ゲストOS側でそのオーナーになります。 しかし、そこに指定するユーザーは、Playbook流す前は存在しない場合、
1つのPlaybookだけで解決しようとするとなかなか難しい状況に遭遇します。
ユーザーやグループの追加はプレイブック分けたほうがいいかもしれません。 vagrant up だけで環境整うと嬉しいけど、そこまでいくにはいろいろ考えないといけないことが多いです。

saharaを使う

Playbookの開発を行うときにVagrantを使うなら sahara というプラグインを使うとよいです。
Playbookを流しては、失敗して、修正し、もう一度流す。みたいなことを繰り返します。
ある程度動作が固まってきたときに、ゲストOSをまっさらな状態にしてから流し直すことをやると思います。 そんなときは sahara が便利です。
他にも同じようなことができるプラグインはありますが、
ゲストOSの状態をバージョン管理したいとかでなければsahara で十分です。

Ansibleの流し方を考える

Vagrantでは、ゲストOSに対してAnsibleを実行する方法が幾つかあります。
Vagrantのプロビジョナを使う場合は、Shell, Ansible, Ansible Local のどれかを使うことになるでしょう。
どれがいいかはプロジェクト次第だと思うので、一概に言えません。

私は、 Shell を使用して、Ansibleのインストールのみを行い、
Playbookの実行はVagrantに入って、手動でやる方法を取りました。
Playbookの開発中は何度も実行するし、どう変わったかの確認もするため、結局sshするからです。
そもそも開発メンバーの中に、Windowsの人がいたので、Ansibleは選択できませんでした。

テストコード

Ansibleに限った話ではないですが、考えておいたほうがいいです。
Ansibleのタスクに対してテストコードを書く場合、今ならServerSpecを選択することが多いと思います。
そこまで大きなプロジェクトでないなら、書かないほうがコストやすいことのほうが多いかもしれないと感じました。
書くなら書き始める時期もちゃんと検討しましょう。

実行ユーザー

  • remote_user
  • become_user

はちゃんと運用時のことを想定して考えておきましょう。
Vagrant環境だとvagrantだけでいろいろいけちゃいますが、Vagrant以外の環境のこともちゃんと考えないと後が大変です。

他のサーバーにsshするとき(git cloneなど)にどの秘密鍵を使うのか、
その秘密鍵はどうやって管理するのか
はたまた、エージェント転送で解決していくのかも先にある程度考えておくといいと思います。

becomeの単位

Playbook単位でbecome: Trueとするかタスク単位で切り替えて行くのかは
上記実行ユーザーと一緒に考えておきましょう。

変数の分割

apache_document_root:
  name: www
  owner: www
  group: www
  mode: 0755

みたいなハッシュが defaults にあったときに apache_document_root.name だけ上書きしたい時に困ります。 Ansibleはデフォルトでハッシュを置換するので、別のところで

apache_document_root:
  name: foo

とかできないです。 ansible.cfg で置換ではなくマージに変えることもできますが、
全体的な影響(自分が中身をよく知らないロールとか)を考えたときに、他は大丈夫なのかという問題が出てきます。
あと、Ansibleをよく知らないメンバーが、マージがデフォルトだと思ってしまうこともあるかもしれません。

ansible-galaxyで管理するロールの配置場所

これは正解が分かりませんでした。
前提として下記の条件があったとします。

  • プロジェクトごとにPlaybookを管理するリポジトリを持つ
  • プロジェクト特有のロールは、このリポジトリに入れる
  • Ansibleを実行するサーバーは、共有で複数のプロジェクトがそこから実行する

デフォルトだと、システムグローバルに置かれますが、それはしたくないです。
それぞれのプロジェクトで、同じロールの違うバージョンを使う可能性があるからです。
require.yml で、個別の名前をつければ対応できると思いますが、しっくりこないです。

結局、下記のような運用にしました。

  • ansible.cfg の中でインストール場所に ./roles を指定する
  • require.ymlの中で、インストールするの名前を role-xxx というような命名規則にする
  • .gitignoreroles/role-* 追加して、galaxyで管理しているロールは除外する

Ansibleのソースを読む

Ansibleのエラーメッセージはあまり親切ではないので、よく分からないことが多いです。
どうにもならないエラーに出会ったら、Ansibleのソースを読んでみることをおすすめします。
homebrewで入れたなら下記辺りに入っているはずです。

/usr/local/Cellar/ansible/2.1.0.0/libexec/lib/python2.7/site-packages/ansible/modules

ここに coreextras というディレクトリがあり、それぞれの中にカテゴライズされたモジュールがあります。
モジュールが coreextras のどっちに属するかは公式ドキュメントに書いてあります。

例えば、gitモジュールだったら、ここの下の方に This is a Core Module と記載されています。
もしくは、モジュール一覧で (E) がついていれば、 extras 扱い
gitモジュールは Source Control Modules にカテゴライズされているので

/usr/local/Cellar/ansible/2.1.0.0/libexec/lib/python2.7/site-packages/ansible/modules/core/source_control/git.py

がソースになります。

Tips

jinja2のフィルターを活用する

例えば、標準出力の整形には from_jsonfrom_ini が結構便利です。
自分で冪等性を担保したいときなど、一旦設定を情報などを確認すると思うのですが、
標準出力に出た内容をAnsible(jinja2)で使えるハッシュに直すときなどによく使いました。

group_varsはイベントリファイルの書き方で意図しない動きになる

  • group_vars
    • web.yml
    • db.yml
    • batch.yml

というような構成になっているときにそれぞれで、 common_main_user という変数を定義しているとします。
かつ、hosts(イベントリファイル)の中で、下記のようにすべて同じものを指している場合

[web]
localhost

[db]
localhost

[batch]
localhost

ansible-playook -i hosts db.yml と実行しても、 多分、期待通りに動かないでしょう。
common_main_user の中身が db.yml に指定したものとは限らないからです。
group_vars の中を探しに行く際に、 マシン名をベースに、最初に見つかったグループを取得するようです。
この例だと、多分、 batch.yml に指定したものが入ってくると思います。
Vagrantで開発しているときなどは、全部を localhost などにしがちですが、これをやるとハマることがあります。
意図したとおりに動かしたい場合、
開発中なら一旦他のグループのlocalhostコメントアウトしておくとか、xip.ioを使うとかしたほうがいいと思います。

blockinfile便利

tipsでもなんでもないですが、よく存在を忘れるのでメモ。
blockinfile モジュールを使うとファイルへの追記などが楽になります。
使うときは marker を指定しておいたほうが無難です。

LinuxユーザーのIDの固定はそれなりに考える必要がある

データベースサーバーでマスタースレーブ構成を取りたいときは、サーバーを複数台用意すると思います。
その時、それぞれのサーバーでユーザーのIDを固定化しておきたいという要望があったとします。
userモジュールでID指定をすれば固定できますが、よく考えてから固定化するしたほうがいいです。

sudo apt-get install mysql-server などとパッケージ管理からインストールすると
mysqlのユーザー、グループも一緒に作られます。そして、mysqlのサービスも起動された状態になります。
ユーザーのIDを変更するには、そのユーザーでサービスが起動していてはいけないので、一旦サービスを止める必要があります。
なので、例えばこんな感じのタスクになる。

- apt: name=mysql-server state=present
- service: name=mysql-server state=stopped
- user: name=mysql uid=9999

本番環境でこんなのは流せないです。
先にユーザーを作ってしまうという手もありますが、
例えば、redis-serverは、redisというユーザーがすでに存在するとaptによるインストールが失敗します。
なので、ユーザーのIDの固定化はタスクとかロールとかがごちゃごちゃしがちになるので、本当に必要かどうか考えたほうがいいです。

Ansibleはshellでコマンドを流しているわけではない

Ansibleを触り始めた頃に思い込みをしていたので、メモとして残しておきます。
Ansibleはsshで他のサーバーに接続してタスクを実行していきますが、各タスクのshellのコマンドを実行するわけではないです。
例えば、gitのクローンは git clone git@github.com/foo/bar.git をshell上で実行しているのではなく、上記を行うpythonスクリプトを送り込んで、そのスクリプトを実行しています。
エラーが起こったときなど、実際どのようなコマンドが発行されたかをつい知りたくなりますが、
コマンド履歴などに残っているようなshellのコマンドを探そうとしても見つかりません。

Ansible Galaxyでロールの管理 - コマンド編

個人(主にGithub)でAnsible Galaxyでロールを管理する上で、
よく使うサブコマンドは、initinstallの2つです。
import,search,removeなどは、Ansible Galaxyにある
ロールを使う時によく使うサブコマンドです。

ただ、実際のプロジェクトでは、
Ansible Galaxyにあるロールをあまり使うことはないのかなと思ってます。
既存のロールは、不要なタスクがあったり、タスクが足りなかったりで
自分のプロジェクトにジャストフィットするものは
あまりないという印象です。

自分でロールを開発する時に参考として見るという使い方が
一番しっくり来ています。

init

ロール開発をする雛形を生成してくれるコマンドです。
ansible-galaxy init ansible-role-foo という形で使用します。
ansible-role-foo という部分は自分で指定します。

大体このコマンドで作成したディレクトリをそのまま
リポジトリとして扱っていくことが多いと思います。

ロール名

Ansible Galaxyのサイトでは、 リポジトリ名 = ロール名 になっていないものが多いですが、
個人で管理する場合は、リポジトリ名 = ロール名にしてしまうのが簡単だと思います。
私は、 ansbile-role- か 単に role- を接頭辞につけることが多いです。

ディレクトリ構造

実行すると下記のような構造になります。

ansible-role-foo
├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

何をどこに書くのかは、公式ドキュメントにあるベストプラクティス
従うのが今のところ良いのではないかと思います。
日本語で解説されているサイトも多いのでググるといろいろ出てきます。

install

playbookで使用したいロールをローカルに落としてくるのに利用します。
私は、基本的に require.yml に記載したものをインストールするときに使用しています。
ansible-galaxy install -r require.yml のような形で実行します。

実際のプロジェクトでは、ansible-galaxy install -fr require.yml
f オプションを追加して使うことが多いです。fはフォースオプションで、
ロールを落としてくる先に、すでに同名のロールが存在した場合、上書きするオプションです。

Playbookを一旦開発し終わった後でも、ロールに対して修正することがよくあります
そんなときに、再度インストールし直すには、fオプションつけないと更新できません。

ちなみにこの時、 fr の順番に気をつけないとエラーがでることがあるようです。

Ansible Galaxyでロールの管理 - リポジトリ編

リポジトリについて

Ansible Galaxyで管理していく場合、
リポジトリはロール単位で管理してくことになります。
それとは別にPlaybookを管理していくリポジトリも必要です。

そのため、Playbookを管理する一つのリポジトリと、
そのPlaybookに必要な分のロールのリポジトリができあがります。

Playbook用のリポジトリの雛形

前回の記事で少し触れましたが、
私はよく使う構成をPlaybookのリポジトリの雛形として
githubにおいています。 そのリポジトリ に入れているものの紹介をしたいと思います。

.editorconfig

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true


[*.md]
trim_trailing_whitespace = false

Ansibleはyml形式で書いていくのでその設定を入れています。
README用に *.md の設定も入れています。

ansible.cfg

Ansibleの設定ファイルです。 AnsibleのGithubリポジトリにあるexampleのものをベースとしていますが、 以下だけ変更しています。

inventory      = hosts
roles_path    = roles

リポジトリ直下にそのプロジェクト用のhostsファイルを
作成することが多いのでinventory = hosts を設定しています。

また、Ansible Galaxyでインストールするロールは、
プロジェクトディレクトリ(Playbookのリポジトリ)直下のrolesディレクトリに
インストールしたいのでroles_path = roles を設定しています。
複数プロジェクトが共存する環境下で、お互いに影響がでないことを考えてです。

.gitignore

*.retry
roles/role-*

playbookを実行すると.retryというファイルを生成する場合があるので
それをリポジトリに含めいないようにしています。
roles/role-* に関しては入れたり入れなかったりです。

ansible.cfgでroles_path = rolesの設定を入れると
リポジトリ内のrolesにAnsible Galaxyで持ってきたロール群が配置されます。
それらは、リポジトリ管理したくないので除外しています。
ただ、roles配下すべてを除外してしまうと、
そのPlaybookのリポジトリだけで必要な使い回す必要のないロールも除外されてしまいます。
なので、Ansible Galaxyでインストールするものは、
role-xxxという命名規則でインストールされるようにして、
その命名規則にあったものだけ除外しています。

require.yml

Ansible Galaxyでインストールするロールを記載しておくためのファイルです。
このファイルにPlaybookで必要なロールを記載しておくことで、
複数のロールを一気にインストールできます。
npmのpackage.josnとかcomposerのcomposer.jsonみたいなものです。
Ansible Galaxyでは、npmやcomposerほど高度なことはできませんが。。

雛形のリポジトリには、Ansible Galaxyの説明に書いてあったサンプルを
そのまま記載しています。

一番多く使うのは、Githubかつバージョン、name指定の方法です。
下記のような書き方です。

 - src: https://github.com/kkkw/ansible-role-example
   version: master
   name: role-example

phpmdで特定のメソッド、クラスだけルールを除外する

自分用メモ

phpmdで静的解析を行ってましたが、
フレームワークの仕様上
どうしても必要のない引数を定義したメソッドを
書くことになってしまいました。

今回のケースでは
unsedUnusedFormalParameter
引っかかってしまいました。
nameingShortVariableのように除外するルールを
xmlに追加しようと思ったのですが、
UnusedFormalParameterには
そういったパラメーターはありませんでした。

SuppressWarningsをphpdocに記載して回避します。 クラスかメソッドのphpdocに記載します。

/**
 *
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
class Bar {

    public function foo($unusedFormalParameter)
    {
        //code
    }

}

参考

Laravelのルートパラメーターのフォーマット指定を簡略化

自分用メモ Laravel5.2で確認しました。

Laravelのルートパラメーター

下記の {id} の部分のことです。
ルートパラメーターというらしいです。

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

フォーマットの指定の簡略化

whereメソッドを指定することで、
ルートパラメーターのフォーマットを指定することができます。

ただ、{id} などよく使うものは、何回も書きたくないって時には、 RouteServiceProviderbootメソッドに記述することで、
whereを使用することもなく、指定できます。 routes.phpが肥大化するのを防ぐ意味もあります。

public function boot(Router $router)
{
    $router->pattern('id', '[0-9]+');

    parent::boot($router);
}

参考

https://laravel.com/docs/5.2/routing