セブ島で仕事する、というお話

今フィリピンのセブ島に長期滞在しています。

僕はフリーランスなので、基本的にネットさえあればどこでも働ける状況です。これから先、法人化や人の雇用など、こうも自由ではいれなくなると予測されるので、今のうちにふらふらしておこうとか思っています。

滞在する場所に求めるもの

僕が滞在する場所を考えるとき、以下の部分を主に考慮します。

  1. 1,仕事&技術的な学習がはかどること。何はなくとも一番大事。
  2. 2,生活コストがかからないこと。お金ためたいので。
  3. 3,人生のスパイス的な+α

やはり若いうちは、知見の蓄積と、周囲の評価を獲得することが最も重要です。
また、自己資本は、何も証明することなく自分に与えられる猶予ですので、貯金も重要です。ただ、常識的な範囲の生活コストの規模感では、1と2には大きな重要度の差があります。

セブ(Cebu City)のイメージ

事前情報からのイメージは以下の感じでした。

  • 1→wifiのつながるカフェが多数あり、ホテルでの作業も可。ただwifiが不安定。
  • 2→物価安い!ホテル滞在でも日本より全然お金かからない!
  • 3→英語が格安で勉強できる!ビーチリゾート!

ざっくりと、そこまで仕事環境を悪化させず、安価に英語を勉強しながら充実した休日をすごせるんじゃないか。というのが楽観的な見方でした。
一番重要な仕事環境に関し、僕は日本で電源+wifi(自前)+空調+適度な緊張感+時間無制限という最強な環境を手に入れていました。よって物理的な環境の悪化は避けられないのですが、エンジニアの友人と共に行くため、モチベーション+知見の面でカバーできるのではと読んでいました。
英語は正直直近の必要性はないので、おまけレベルです。そのため英語学校通いは選択肢にいれませんでした。

セブに関する記事

以下の記事をこれから書いていこうと思っています。

生活環境とかコスト
  • ・航空券
  • ・セブの物価
  • ・セブでのお食事
スパイス
  • ・セブでの個人英語学習
  • ・セブのビーチ
  • ・セブの女の子

GoogleAnalyticsで自分のアクセスを除外

という罠

ググると、IPによる方法とCookieを使う方法のヘルプが
社内からのアクセスを除外するにはどうすればよいですか?-アナリティクスヘルプ
しかし実はこれ古いヘルプで、記載のタグでCookieによる除外はできません
僕は動的IPなのでCookieで除外しようとしてがっつりはまりました。こういう一見簡単な作業でつまると悲しいのでメモ書きを。
ブラウザはChrome20.0です。

Cookieによるアクセスの除外

必要なのは3ステップ。

  • ・除外用のページを作成する
  • ・除外するブラウザでページにアクセスし、Cookieをset
  • ・Analytics設定でフィルタを作成する
除外用のページを作成する

Cookieをsetするためのページを作ってアップします。
ページに必要な条件は以下の3つ。

  • ・除外したいドメイン上にアップする
  • ・Analyticsのトラックタグを記載する
  • ・bodyタグを下記に
  • <body onLoad="javascript:_gaq.push(['_setVar','test_value']);">


アップして動作確認できたコードです。UAは自分のものを設定してください。

<html>
<head>
<title>No Report Cookie Setter</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex,nofollow">
</head>
<body onLoad="javascript:_gaq.push(['_setVar', 'test_value']);">
  <script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXXXXX-X']);
  _gaq.push(['_trackPageview']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
  </script>
</body>
</html>


onloadはもちろんimgタグでも動きます。WAFを使っている場合、レイアウトテンプレをカットするのがちょっと手間で、bodyタグは面倒だったりします。
※念のためですが、bodyタグを変更するのは、Cookieセット用のページのみです。テンプレートに突っ込んで、サイト全体に適用しないようにします。

Analytics設定でフィルタを作成する

アナリティクス設定→プロパティ→フィルタ→新しいフィルタ

  • フィルタ名 : 任意
  • フィルタの種類 : カスタム フィルタ→ 除外
  • フィルタ フィールド : ユーザー定義
  • フィルタ パターン : さっきの文字列(例だとtest_value
  • 大文字と小文字を区別 : いいえ

うまくいかない場合

Cookieを見る

Chromeの場合
右クリック→要素の検証→Resources→Cookies→サイトドメイン で確認。

Name : __utmv
Value : xxxxxxxxx(数字).test_value
みたいなのがあればCookieは正しく設定されています。
これでフィルタが間違っているのか、ページのコードが間違っているのかあたりをつけます。

リアルタイムだと除外されない

Analyticsのベータ機能リアルタイムでは、Cookieとフィルタを正しく設定しても除外されていないので注意。当日分のコンテンツあたりで確認すると良いのでは。

トラックコード内で設定する方法

やっていることは同じですが、一応別の記載方法も

<script type="text/javascript">
    var _gaq = _gaq || [];
    _gaq.push(['_setVar', 'test_value']);
    _gaq.push(['_setAccount', 'UA-XXXXXXXX-X']);
    _gaq.push(['_trackPageview']);

 // etc...
 </script>
こちらもサイト全体のコードをこれにしないように注意です。

CakePHP2.1をgit push一発でデプロイ

やはりデプロイは自動化しなければまずいということで。全て手で書き換え+ftpでアップロード状態からgit push一発で完了させるようにしました。ディレクトリの配置や環境は前記事準拠です。
CakePHP2.1をさくらのレンタルサーバー+マルチドメイン使用でデプロイ

やったこと
前提

Gitでソースのバージョン管理をしている

コードで環境の差分を吸収

開発環境と本番環境で設定が異なる部分を、全てコードで吸収します。
僕の場合書き換えたのは以下の4ファイルです。

  • ・bootstrap.php
  • ・core.php
  • ・database.php
  • ・webroot/index.php

基本方針は、IPを見て場合分けです。
bootstrapに定義したURL定数と、coreのdebug設定をこんな感じで書き換え

if (env('SERVER_ADDR') !== '127.0.0.1') {
  #本番処理
} else {
  #開発処理
}


databaseはコンストラクタを定義。設定の配列$devと$prodを作って$defaultに割り当て

  public function __construct() {
    if (env('SERVER_ADDR') !== '127.0.0.1') {
      $this->default = $this->prod;
    }else {
      $this->default = $this->dev;
    }
  }
ちなみに後のマイグレーション時に本番DBへ接続しに行ってエラーに…
indexは env('SERVER_ADDR') が使えず、 $_SERVER["REMOTE_ADDR"] を使ってルート、コアのパスを設定
if ($_SERVER["REMOTE_ADDR"] !== '127.0.0.1') {
  #本番処理
} else {
  #開発処理
}


同一コードで正常に動作することを確かめます。CSSや画像パスの設定が必要な場合もあります。僕はbootstrap内で定数ROOT_URL、ASSETS_URLを切り替えて運用しています。

DBのマイグレーション

CakeDC / migrations
データベースのスキーマ変更が自動で反映されるようにします。スキーマファイル(schema.php)と現在のデータベースを比較し、差分を埋める作成時刻付のマイグレーションファイルを作成。schema_migrationsテーブルでマイグレーションファイルの適用状況を管理し、未適用のものを実行という流れみたいです。

インストール

プラグインフォルダに展開

#myapp(app)/Plugin
$ git clone https:github.com/CakeDC/migrations.git

デプロイ時に一緒に本番サーバーにも入る予定でしたが、他のリポジトリで管理されてるファイルは自動的にignoreされるんですね…僕は.gitフォルダを削除しました。これでpush時に本番にもPluginが設置されます。

開発環境でマイグレーションの準備

まずは開発環境のデータベーススキーマを本番環境で再現。既に本番に作ってあっても大丈夫です。


#schema_migrationsテーブルを作成
$ cake Migrations.migration -p Migrations
#最初のマイグレーションファイルを作成
$ cake Migrations.migration generate
ちなみにここで本番環境を見に行きエラーが発生したので、一時的にdatabase.phpを書き換えて対応しました…

本番環境でのマイグレーションの実行

開発環境で作成したマイグレーションファイルを本番に設置した上で、以下の手順を踏めば本番環境にデータベースを再現できます。


#schema_migrationsテーブルを作成
% cake Migrations.migration -p Migrations
#未適用のマイグレーションファイルを実行
% cake Migrations.migration all

既に本番環境にテーブル等作成済みの場合エラーが発生します。成功したことにしますか?と聞かれるので、成功したことにすれば問題ないです。
An error occurred when processing the migration:
  Migration: 1339467948_first_migration
  Error: Table "areas" already exists in database.
---------------------------------------------------------------
Do you want to mark the migration as successful?. [y]es or [a]bort. (y/a)

スキーマ変更の手順

開発環境で現在のスキーマを準備→テーブルを変更→マイグレーションファイルを作成→スキーママイグレーションファイルを本番にpush→本番で実行の流れです


 #開発で

#
その時点のschema.phpを作成

$ cake schema generate
 #ここでSQLでもツールでもいいのでテーブルの構造を変更する
#
差分のマイグレーションファイルを作成
$ cake Migrations.migration generate

 #本番で
#
未適用のマイグレーションファイルを実行
% cake Migrations.migration all

git push をフックにスクリプト実行

大まかには、変更をcommit→リモートリポジトリにpush→pushをフックにスクリプトが実行され本番環境にpull+追加処理でデプロイ完了という流れです。

さくらレンタルサーバーにgitを導入


#一時ディレクトリを作り移動
% mkdir /home/user/tmp
% cd /home/user/tmp

#wgetアーカイブを拾ってくる
#アーカイブを解凍

% tar xvf git-git-v1.7.11-rc2-0-ga1a031d.tar.gz
% cd git-git-42e15c9
#gmakeでビルド
% gmake
% gmake install

#$HOME/binにインストールされ、$HOME/binがパスに追加されている


#.cshrcを読み込むと、gitコマンドが使える
% source /home/user/.cshrc

リモートリポジトリの追加

まずは本番サーバーにログインしリモートリポジトリを作成


#リポジトリ用のディレクトリ作成
% mkdir /home/user/repos/myapp.git

#bareリポジトリ(作業ディレクトリをもたないリポジトリ)作成
% cd myapp.git
% git init --bare

続いて開発環境のリポジトリに今作成したリモートリポジトリを登録


#既存のリポジトリにリモートリポジトリとして登録
$ git remote add origin user@user.sakura.ne.jp:/home/user/repos/myapp.git
#pushで変更を反映
$ git push origin master

フックスクリプトの作成

gitはリポジトリへの行動をフックにスクリプトを実行する機能を持っています。
.git/hooks内のファイルに記載し、ファイル名から.sampleを取り除けば実行されます。自分で作成する場合はスクリプトに実行権限を与える必要があります。
今回はpushをフックにしたいのでpost-updateを使用

% mv /home/user/repos/myapp.git/hooks/post-update.sample /home/user/repos/myapp.git/hooks/post-update

内容は以下の通り
#!/bin/sh

#リモートリポジトリから本番環境への展開
(cd /home/user/myapp(app); git --git-dir=.git pull)
#webroot内のファイルの転送
(cp -r /home/user/myapp(app)/myapp(webroot)/* /home/user/www/myapp(webroot)/)
#マイグレーションの実行
(cd /home/user/myapp; cake Migrations.migration all)

リモートリポジトリから本番環境への展開

myappディレクトリを置きたい場所に、先ほどのリモートリポジトリからclone

% git clone user@user.sakura.ne.jp:home/user/repos/myapp.git

あとはgit pullでファイルが展開されます。シェルスクリプト内では --git-dir=.git と指定する必要があるみたいです。

webroot内のファイルを転送

今回のサーバー環境では、appとwebrootの位置関係が変わっています。元のリポジトリと構造が違うので、pullした後app/webroot内のファイルを公開フォルダ上にコピーすることにしました。

% cp -r /home/user/myapp(app)/myapp(webroot)/* /home/user/www/myapp(webroot)/)
なお、mvコマンドは同名ファイルに対して処理ができません。gitの場合変更があったファイルのみ転送されるため、いったんディレクトリを削除してmvもできないので、cpを使います。

マイグレーションの実行

未実行のマイグレーションファイルがあれば実行してくれます。

振り返ると結構色々やったなと。今はかなりプロダクトに集中したいのですが、デプロイ自動化の恩恵は相当大きいので、やってよかったと思います。今回はサーバー1台の運用で恩恵が小さいと判断しましたが、時間を見つけてcapistranoも勉強したいところです。

CakePHP2.1をさくらのレンタルサーバー+マルチドメイン使用でデプロイ

ウキウキでリリースのところ、予想外のトラブルに見舞われました…
サーバーはさくらのレンタルサーバースタンダード。Apacheは1.3。ドメインはスタードメインで取得したものを、さくらのマルチドメイン機能で使用。
http:// mydomain.comで、CakePHPのrootに接続するようにします。

今回の内容

マルチドメイン設定

さくら側の設定

サーバーコントロールパネルの「ドメイン設定」→「新しいドメインの追加」で、ドメイン名とDocumentRootの指定をします。今回は www/myapp(webroot) に設定。

ドメインレジスタ側の設定

ネームサーバーの設定をさくら指定のものに書き換えます。待ち時間はケースバイケースですが、1時間後には設定したドメインでアクセス可能になりました。

ディレクトリ構造とCakeのindex.php設定

ディレクトリ構造はこんな感じ。wwwがさくらデフォルトのApacheDocumentRootディレクトリです。

/home
    /user_name
        /lib
            /Cake2.1(core)
        /myapp(app)
            /www
        /myapp(webroot)


さくらのマルチドメイン機能では、追加したドメインごとにDocumentRootを設定します。ただしwww以下という制約があり、appを公開フォルダ上に置くのを避けるには、appとwebrootの位置関係を変えざるを得ないです。
myapp(webroot)/index.phpは以下のように変更。

# 変更前 define('ROOT', dirname(dirname(dirname(__FILE__))));if (!defined('ROOT')) {
  define('ROOT', DS . 'home' . DS . 'user_name');
}

# 変更前 define('APP_DIR', basename(dirname(dirname(__FILE__))));if (!defined('APP_DIR')) {
  define('APP_DIR', 'myapp');
}

# コメントを外す
define('CAKE_CORE_INCLUDE_PATH',  DS . 'home' . DS . 'user_name' . DS . 'lib' . DS . 'Cake2.1' . DS . 'lib');

この時点でのエラー

ブラウザ : NOT FOUND
Apacheエラーログ : File does not exist
root(http://mydomain.com)だけは正しく表示。Cakeのroutes.phpでrootのアクションを変更すれば、そのアクションは動作する状態に。

.htaccessの設定

制約の範囲と内容が不明瞭なのですが、今回のケースでは、実際のファイルディレクトリ構造とリクエスURIの構造が異なる場合アクセスできないようです。

対処法

myapp(webroot)内に、下記を記載した.htaccessを設置します。しかし.htaccessって体感できるレベルで遅くなりますね…

<IfModule mod_rewrite.c>

    RewriteEngine On

    RewriteCond %{REQUEST_FILENAME} !-f

    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

    RewriteBase /

</IfModule>
無事全てのページが表示されるようになりました。
本来CakePHPで解釈されるroot以降の部分を、事前にクエリストリングに書き換えてから渡すようにRewriteRuleを設定したわけです。
RewriteCondは適用条件を指定するもので、!-fは実際に存在するファイル名を除外するという意味です。画像やCSSに書き換えが適用されないようにしています。

.htaccessを設置したが書き方が間違っていた時のエラー

ブラウザ : Internal Server error
Apacheエラーログ : mod_rewrite: maximum number of internal redirects reached. Assuming configuration error. Use 'RewriteOptions MaxRedirects' to increase the limit if neccessary.
なお、RewriteOptions MaxRedirectsを1~15まで設定してみましたが、エラーは解決しませんでした。

検索すれば似た事例が出ますが、古いバージョンの情報だったりCakeを公開ディレクトリ上におく情報が多かったので参考までに。
ちなみに現在は、全て手動で書き換えて上げています。早く自動化しなければ。

Herokuで"No account specified."と言われloginできない

Herokuにログインできない問題の解決についてです。

Herokuにloginできない

https://devcenter.heroku.com/articles/quickstart
ここを見ながら進めていました。大雑把に言うと

  • 1, Herokuにsign up
  • 2, メールのリンクからactivate
  • 3, Heroku Toolbeltをインストール
  • 4, login&公開鍵の送信

この流れで使えるようになる予定でした。

この4の段階で、下記エラーが

$ heroku login
Enter your Heroku credentials.
Email: MAIL
Password (typing will be hidden):
 !    No account specified.
 !    
 !    Run this command with --account <account name>
 !    
 !    You can also add it as a git config attribute with:
 !      git config heroku.account work


状況としては、2までは問題なく完了


3の段階はHeroku Toolbeltのインストールではなく、gitはhomebrew、foremanとherokuはgemで入れてある、という状況でした。

$ gem list
:
foreman (0.46.0)
:
heroku (2.26.2)
因果関係の検討はつきませんが、これが原因なのかもしれません。

heroku-accountsプラグインによる解決

heroku-accountsプラグインをインストールし、アカウントを作ったところ、loginできました。

$ heroku login
Enter your Heroku credentials.
Email: mail
Password (typing will be hidden):
Authentication successful.

手順

プラグインをインストール

$ heroku plugins:install git://github.com/ddollar/heroku-accounts.git
heroku-accounts installed


任意のアカウントを作成

$ heroku accounts:add NAME
Enter your Heroku credentials.
Email: MAIL
Password (typing will be hidden):

Add the following to your ~/.ssh/config

Host heroku.NAME
  HostName heroku.com
  IdentityFile /PATH/TO/PRIVATE/KEY
  IdentitiesOnly yes


Add the followingの部分を記載

$ sudo vim ~/.ssh/config


作ったアカウントをデフォルトに

$ heroku accounts:default NAME


アカウントを確認。*印がデフォルトアカウント

$ heroku accounts
* NAME


これでloginすればいけるはずです。僕は公開鍵を送信してからloginしました。

$ heroku keys:add /PATH_TO_KEY/PUBKEY.pub
Uploading SSH public key /PATH_TO_KEY/PUBKEY.pub... done
$ heroku login


本来heroku-accountsは複数アカウント管理用のプラグインみたいです。根本解決ではありませんが、一応参考まで。
なお、エラー文言にある--account accountnameをつけてloginやgit configのやり方は、色々な形式で試行錯誤しましたがうまく行きませんでした。

Rails3.2以降、rakeのdb:fixtures:loadでCSVはインポートできない

Railsでfixturesを使ってダミーデータをインポートしたかったのですが、YAMLで大量のデータを作るのは大変そう。で、CSVをインポートしようとすると…

$ rake db:fixtures:load FIXTURES=items

#エラー文

rake aborted!

No such file or directory - / path_to_app /test/fixtures/items.yml

.ymlがないぞと言われます。コマンドに.csvを付けても同様のエラーです。
調べてみると、もはやfixturesがCSVをサポートしていないことが判明。

CSV Fixtures are deprecated and support will be removed in Rails 3.2.0.
Ruby on Rails 3.1 Release Notes

CSVYAMLに変換するなりプラグインなりあるだろうとも思いましたが、素直にMySQLクライアントからインポートしました。Sequel Pro使いやすいです。
ググるとまだCSVYAMLもいけるよって記事ばかりなのでご注意を。

Apacheは起動しているが、ブラウザでlocalhostが確認できない

脱XAMPP実行時に、ひっかかったことのメモ。
OSXデフォルトのApacheを使うということで、System Preference -> Sharing -> Web Sharing にチェックで起動するはずでしたが…

localhostに接続できない

原因

DocumentRootディレクトリを削除していることによる、httpd.confのsyntax errorでした。
163~168行目の記述は下記でしたが、肝心の~/Webディレクトリを過去に削除していました。

#

# DocumentRoot: The directory out of which you will serve your

# documents. By default, all requests are taken from this directory, but

# symbolic links and aliases may be used to point to other locations.

#

DocumentRoot "/Users/RM/Web"

対応

まずはhttpd(Apache)の起動確認。起動しています

$ sudo apachectl start

Password:

org.apache.httpd: Already loaded

ここでOSXApache設定ファイル、/private/etc/apache2/httpd.confの更新日が結構最近なことに気づく。そういえば始めたばっかの時にバックアップとらずにいじってました。で、多分httpd.confに問題があるのだろうと推測。
まずは使用しているのが/private/etc/apache2/httpd.confで間違いないか確認したい。が、方法はわからないので他に同名ファイルないか確認してよしとする
$ locate httpd.conf
続いてsyntaxerrorを確認
$ httpd -t
ここで168行目のパスがディレクトリではないと言われ、上記問題を発見。
~/Webディレクトリを作成して完了ですが、403 Forbiddenです。

403 Forbiddenの解決

原因

168行目DocumentRootの設定と195行目 のずれ。これはさすがに自分でいじってないはずだけど…

対応

とりあえずForbiddenて言われたらパーミッションだろということで、 ls -l で確認。755で問題なさそう。
次いで/private/var/log/apache2/error_logを見てみます。client denied by server configurationとのこと。
ここらへんの情報でググってみるも、症状は似ていても、解決はVirtualhostがらみとか、user.confがとかで微妙に違う。ちょっと焦りだす。
やむをえずApacheの設定項目の解説を見て、一つ一つ確認することに。
参考 : httpd.conf の設定
ここで原因に気付き、195行目のパスを168行目と一致させる

#

# This should be changed to whatever you set DocumentRoot to.

#

<Directory "/Users/UserName/Web">

無事indexが表示されたのでした。

十分に理解していない部分がある不安と、とりあえず行き詰まることなく解決まで持っていけたことへの喜びを同時に味わいました。インフラは早々に誰かにお任せしたいと思っているので、一旦これで良しとします。