たけまるの日記

たけまるの日記です。web関係の技術ネタが多いですが、好きなことを適当に書いています。

CakePHPのMigrationでMySQLのmediumtextを扱う方法

従来手動でAlter管理していたDBにCakePHPのMigrationを導入しようとした所、mediumtext/longtextを扱うのにハマったのでメモ。

MySQLのtext型は最長65,535byteの可変長文字列型ですが、UTF8換算で約2万文字となり、少し容量が足りずmediumtext(最長16MB)を使いたくなる場面があります。しかしCakePHPのMigration Pluginを使ってみると、どうもtext型のみのサポートのようでした。

以下の様なテーブルをまずは作成します。

mysql> CREATE TABLE testtable (id int, message text, medium_message mediumtext, long_message longtext);

これを元にMigrationファイルを作成します。

$ Console/cake Migrations.migration generate -f

結果作成されたMigrationファイルは

'id' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false, 'key' => 'primary'),
'message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'medium_message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'long_message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),

となり、text/mediumtext/longtextを区別せず、すべてtext型として認識されてしまいました。

これでは困るのでCakePHPのコア部分に手を入れて対応することにしました。
とはいえコアファイルを直接編集するわけではなく、CakePHPのクラスオーバーライドの仕組みを利用します。これはapp/Libにコアファイルと同名のクラスファイルを置くと、該当クラスをロードする時にコアのクラスではなく、app/Lib側のクラスに置き換えて読み込む機能です。これでCakePHPのコア自体をアプリのレポジトリに入れなくても済みます。
詳しくは以下をご覧ください。
CakePHP のクラスをオーバーライドする

実際に書き換えた部分は以下です。
元となるMySQLクラスはCakePHP 2.5.8のものです。

## project_path/app/Lib/Model/Datasource/Mysql.php
    // 116行付近
    public $columns = array(
        'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
        'string' => array('name' => 'varchar', 'limit' => '255'),
        'mediumtext' => array('name' => 'mediumtext'),
        'longtext' => array('name' => 'longtext'),
        'text' => array('name' => 'text'),
    
    // 777行付近
        if (strpos($col, 'mediumtext') !== false) {
            return 'mediumtext';
        }
        if (strpos($col, 'longtext') !== false) {
            return 'longtext';
        }
        if (strpos($col, 'text') !== false) {
            return 'text';
        }

全内容はこちら。
Gist : Mysql.php

これでmediumtext/longtextを利用することができるようになりました。

ちなみにmediumtext/longtextを公式サポートしない理由としては、CakePHPとしてはMySQLPostgreSQL等、DBMSが変わっても同じアプリケーションコードで動くことを目指しているため、DBMSによってかき分ける必要がないようにするのが困難なため、という所だと理解しました。
参考 : LONGTEXT fields on schema generate

今回修正したコードもMySQLを使うことだけを考慮したものですので、そのあたりご理解の上参考にしていただければと思います。

AWSのログインにハードウェアMFAを導入してみた

AWSアカウントのログインにハードウェアMFAを導入してみました。
所謂ワンタイムパスワードで、トークンに表示される数字を入力しないとログインできないようになります。

とりあえず買ったのがコレ。
Ezio Time-based 6-digit OTP Display Card for Use with Amazon Web Services Only

デバイスは新旧2種類出ていて、古いほうが当然安いのですが、同期がずれるという情報があったため新しい方にしました。

amazon.comでしか買えないのが難点ですが、表示が英語とはいえ画面や購入フローは日本のアマゾンと一緒なので案外簡単です。 住所を英語で入れないといけないのですが、行き先が日本という事さえわかれば実際に届けるのは日本人なわけなので、最終的に日本人に分かるように書けばOK。

配送を早い方を選んで、商品代金$19.99 + 送料$15.43 = $35.42となりました。

火曜の朝注文したらその日のうちにアメリカから出荷され、木曜の昼に到着するというクオリティ。アマゾンすげえ。DHLもすげえ。

こんな感じで届きました。

f:id:takemaru123:20150205200701j:plain

開けたらこんな感じ。

f:id:takemaru123:20150205200721j:plain

必要なのはこのカードだけ。
普通のクレジットカードと同じサイズで、感覚的にはそれより薄い!

f:id:takemaru123:20150205200730j:plain

オレンジのボタンを押すと番号が出ます。普段は非表示です。

f:id:takemaru123:20150205200743j:plain

裏側。潰した所のシリアルキーをAWS側に登録して利用します。

f:id:takemaru123:20150205201441j:plain

登録の方法はこちらを参考に。 http://yoshidashingo.hatenablog.com/entry/2014/10/13/164230

ちなみにIAMユーザー(後述)に全権限を与えていても、支払情報をIAMユーザーが見れるようにAWSアカウントでログインして設定しないと見れませんので設定します。

参考
https://www.agilegroup.co.jp/technote/aws-iam.html

登録したらこのカードは金庫にでもしまっておきます。普段使うことはありません。 AWSアカウントが親玉で、その中に実作業をするIAMユーザーを作成し、普段はそちらを使うという感じです。IAMユーザーごとに細かく権限設定ができます。

IAM関係はこちらを参考にするといい感じです。 http://qiita.com/yoshidashingo/items/cd206daca0596659b440

普段使うIAMユーザーのMFAはスマフォにアプリを入れて対応します。 いくつかアプリがあるのですが、メジャーな「Google Authenticator」はスマフォを紛失したりすると復旧できないため却下です。参考サイトでも紹介している「Authy」を使うことにしました。こちらはスマフォを変えても復活することができます。

ちなみにIAMユーザーのMFA変更は最悪AWSアカウントでログインすればできますが、AWSアカウントのMFA再設定はAWSアカウントでログインしない限り不可能です。万一AWSアカウントのMFAを無くしたりすると、電話でAWSサポートに変更依頼(英語)しないといけないらしいので、厳重に管理しましょう。

個人のAWSならrootアカウントもVirtualMFAで良いと思います。 今回は業務利用だったため、AWSアカウントの属人化を避けるためにハードウェアMFAを導入してみました。 あとちょっと厨二心も満たされましたw

デバイス自体は安いのに送料が同じくらいかかるのが難点ですが、ひとつ試してみてはいかがでしょうか。

Ingressエージェント活動に絶対導入すべき3つのアイテム

Ingressをはじめて早1ヶ月、ようやくレベル7まで到達しレベル8まであと少しという所まで来ました。よく訓練されたエージェントの皆様は夜な夜な自宅周辺を徘徊するのがデフォだと思いますが、私も例にもれず夜中チャリンコで走り回ってはCFを作る日々を過ごしております。

そこで、私がエージェント活動をするのに外せない3つのアイテムをご紹介したいと思います。

RAMマウント

最初は自転車のカゴに直接iPhoneを置いたりズボンのポケットに入れたりして走っていたのですが、このマウントを付けてから本当に快適になりました。

f:id:takemaru123:20150118041802j:plain

そう、知る人ぞ知るRAMマウントです。

米国製で米軍も採用しているシロモノです。 少し値ははりますが、これを買って後悔することは無いと断言しておきます。

RAMマウントは種類がかなりたくさん出ていて、何をどこにどう取り付けるか次第で必要なパーツを組み合わせて使います。 実際に自転車にiPhone6 plusを取り付けるのに以下のパーツを導入しました。

まず自転車のハンドル部分にマウントベースを取り付けるのに「RAM-B-231U」を、iPhone6 plusをホールドするのに「RAM-HOL-UN10BU」、その2つを繋ぐのに「RAM-B-201U-A」という構成です。

ちなみにiPhone6(無印)やそれ以前のiPhoneには「RAM-HOL-UN7BU」を使うのが良いようです。

このiPhoneをマウントする部分ですが、四隅のゴムパーツで本体を挟みこむような構造になっています。本当にこれで大丈夫なのか?と最初は思いましたが、実際取り付けて走ってみるともの凄く安定していて、不安感がまったくありません。乱雑に段差を超えたりしてもびくともしません。またベースとマウントを繋ぐジョイントもうまく考えられていて、かなり自由に角度や位置、高さを調整できます。

モバイルバッテリー

Ingressは本当に電池がすぐなくなりますので予備バッテリーは必須です。私はこれを使っています。

2A出力ができるので急速充電が可能です。気づいたらバッテリー残り1%なんて時も、これさえあればタイミングを逃しません。 私は普段の持ち歩きも考慮してミニサイズ(6000mAh)のほうにしていますが、1日中活動するなら大きい方(10400mAh)を買ったほうが良いでしょう。

手袋

それとこの時期(現在1月)に必須なのがスマホ手袋。さすがに素手で自転車を乗るのは厳しいです。手袋を脱がずに操作でき大変快適です。かじかんだ手では正確に攻撃できませんよ!!

まとめ

如何でしたでしょうか。これらを導入すればエージェント活動がより快適になることは間違いなし!! エージェントの皆様はいますぐ導入することをお勧めします!

最小ダウンタイムでMySQLレプリケーションを構築する

既に稼働しているMySQLレプリケーションを構築する場合の手順です。 結論から言えば、高々1回のマスターDB再起動でレプリケーションの構築が可能で、通常であればダウンタイム1分以内です。 作業中にマスターDBに長時間ロックがかかることもありません。

対象はMySQL5.6ですが、MySQL5系なら同様の手順でいけると思います。 MySQL5.6からはGTIDレプリケーションもサポートされましたが、今回は諸事情により見送ることにしました。

以下手順です。

マスターのbinlog出力を有効にする

master# vi /usr/my.cnf
[mysqld]
log-bin=mysql-bin
server-id=1

master# service mysql restart

唯一のダウンタイムがこれですが、既にバイナリログが有効になっている場合はこの手順は不要です。

レプリケーション用のユーザーを作成

(master)mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'slaveip' IDENTIFIED BY 'slavepass';

マスターのdump取得

master# mysqldump --all-databases --master-data --single-transaction > dbdump.sql

「--master-data」がキモですね。dumpと同時に現在のバイナリログの位置を取得できます。 また、「--single-transaction」することにより、テーブル間の整合性がとれた状態のダンプを、ロックなしに取得できます。(これをつけないダンプ中は書込ロックされます)

スレーブ側の設定

slave# vi /usr/my.cnf
[mysqld]
log-bin=mysql-bin
server-id=10

slave# service mysql restart

スレーブ側をレストア

slave# mysql < dbdump.sql

レプリケーション開始

$ grep -i "CHANGE MASTER TO" dbdump.sql
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=12345678;

(slave)mysql> CHANGE MASTER TO
  MASTER_HOST='masterip',
  MASTER_USER='repl',
  MASTER_PASSWORD='slavepass',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=12345678;
(slave)mysql> START SLAVE;

確認

(master)mysql> show master status;
(slave)mysql> show slave status;

参考

http://mysql.manual.php.to/replication.html#replication-howto http://qiita.com/hit/items/7747394e8f2f6a515535 http://tmtms.hatenablog.com/entry/20110713/mysqldump

1クエリで一度に大量のレコードをupdateしてはいけない

2億レコードくらい入ってるテーブルにこれ流したらディスク逼迫して死んだ。

mysql> UPDATE table SET col = NULL;

数時間後、繋がってるサービスの画面が「500 Internal Server Error」。 みたらディスク空きが0になってた。

なにがでかくなってたかというと

# ls -alh /var/lib/mysql
(抜粋)
-rw-rw----   1 mysql mysql  18G Jan  8 16:58 ibdata1

ちなみにinnodb_file_per_tableは有効(デフォルト)である。 要はテーブルの実データはibdata1には入ってない。 のにこの容量である。

というわけで調べたら、トランザクションの一貫性のため、ibdata1にはUNDOログ(更新前データのスナップショット)が書き込まれるとの事。1トランザクションで全レコードを更新しようとしたため、2億レコード分のスナップショットを作るはめになったらしい。ちなみにこのファイル、一度でかくなったら小さくなることはない。つまり詰んだ。

おかげでリカバリために多くの労力を費やすはめになったのであった。

もう一度言う。

1クエリで一度に大量のレコードをupdateしてはいけない。

参考

https://hiroakis.com/blog/2013/08/22/mysql-%E3%81%AA%E3%81%9Cibdata1%E3%81%8C%E8%82%A5%E5%A4%A7%E5%8C%96%E3%81%99%E3%82%8B%E7%90%86%E7%94%B1%E8%A8%98%E4%BA%8B%E3%81%AE%E6%84%8F%E8%A8%B3/

さくらクラウドでルートパーティションの容量を拡張する方法

追記:この手順を実施する場合の注意を記事中下部に追加しました。

あけましておめでとうございます。

訳あってさくらクラウドを使うことになりました。

250Gのディスクを選択してVMを作成したのだけど、さくらが用意したCentOSのイメージ「[public] 20GB CentOS 6.6 64bit (基本セット)」で起動したらOS上で20Gしか認識されていない…。

それもそのはずで、OSイメージは20Gを前提に作成されたものでした。

別途「100G」というのも用意されていたのですが、今回は250Gにしたかったのでなんとか拡張してみました。

(クリティカルな操作なので自己責任で、バックアップ必須!)

VM作成

  1. 普通にVM作成
    • ディスクソース : [アーカイブ(簡単)]
    • アーカイブ選択 : [public] 20GB CentOS 6.6 64bit (基本セット)
    • ディスクサイズ : [20G]
    • 作成後すぐに起動 : [しない]

追加ディスクの作成(本命ディスク)

  1. 管理画面 [ストレージ] -> [追加]押下
  2. 以下設定
    • ティスクソース : [ブランク]
    • ディスクサイズ : [250GB]
    • 名前 : 「testvm-250G」
  3. [作成]押下

拡張作業

  1. 管理画面 [サーバ] -> 該当VMを選択 -> [詳細] -> [ディスク] -> [接続]押下
  2. [testvm-250G]を選択して[更新]
  3. 接続#1に元の[testvm]、接続#2に[testvm-250G]が接続されているのを確認する
  4. VM起動
  5. 以下コマンド実行
### 起動ドライブはvda、追加したドライブがvdbとして認識されている
# ls /dev/vd*
/dev/vda  /dev/vda1  /dev/vda2  /dev/vdb

### 中身をコピー(3分半程度、速い…!)
# dd if=/dev/vda of=/dev/vdb bs=512M

### パーティションの拡張
# parted /dev/vdb
(parted) unit s
(parted) print
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 524288000s
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start     End        Size       File system     Name     Flags
 1      64s       8400487s   8400424s   linux-swap(v1)  primary
 2      8400488s  41943006s  33542519s  ext4            primary  boot

(parted) rm 2
(parted) mkpart primary 8400488s -1s

# ※途中パーティションを修復するか聞いてくるので、F(Fix)を選択する
# ※警告が出てきてもyes/Ignoreを選択して進む

(parted) print
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 268GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name     Flags
 1      32.8kB  4301MB  4301MB  linux-swap(v1)  primary
 2      4301MB  268GB   264GB   ext4            primary
 
(parted) quit

# e2fsck -f /dev/vdb2
# resize2fs /dev/vdb2

一旦パーティションを削除して(!)、再度より大きいパーティションを作るというのがキモですね。こんな事して大丈夫なんですねえ。。

元ディスクをVMから外す、動作確認

  1. 該当VMをシャットダウン
  2. 管理画面 [サーバ] -> 該当VMを選択 -> [詳細] -> [ディスク] -> [接続]押下
  3. 元ディスク(接続#1)の▼を選択して[取外し]
  4. 該当VM起動
  5. 容量が増えてるか確認
  6. 必要なければ元のディスクは削除
[root@testvm ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda2       243G  1.4G  229G   1% /
tmpfs           499M     0  499M   0% /dev/shm

参考

http://knowledge.sakura.ad.jp/tech/925/

http://ubuntuforums.org/showthread.php?t=1389429

一応参考サイトと違うのは、参考サイトはMBRパーティションが切られているのでfdiskで作業していましたが、CentOS6のイメージだとGPTでパーティションを切っていたため、partedで作業しました。

最初参考サイトを見て、もうちょっとマシな方法がないかと調べたのですが結局この方法となりました。 なんかもうちょっといい方法はないものでしょうかねえ。。

さくらクラウドについて思ったこと

  • さくらVPSと比べると割高
  • さくらVPSが優秀過ぎる
  • とはいえさくらVPSはスケールアップができないのでその点は安心感がある
  • さくらVPSとローカルネットワークが繋がれば最高だけど戦略上しなそう
  • 石狩第一内のVPSクラウド(グローバルIP)間の通信はほぼ理論値通り(100Mbps)出た

追記 2015.2.2

この手順ではデータ不整合となる可能性について指摘を受けました。

よく見ると参考サイトのさくらナレッジでも下部に追記がありました(^^;;;

以下引用

[※編集部追記:本手順の際、ファイルシステムをマウント中のまま dd するとコピー先で不整合が発生する可能性があるため、レスキューモードもしくはシングルユーザーモードで実行ください]

確かに起動中のパーティションをddでコピーする場合、作業中に他のプロセスがディスクを書き込む可能性があるため、データ整合性を保証できない可能性があります。私は検証環境として手軽に用意したかったので問題ないですが、ミッションクリティカルな用途の場合は正攻法で行ったほうがいいと思います。

sshを繋ぎっぱなしにするautossh for mac

sshって結構切れます。 特に一定時間何もコマンドを打っていなかったり、画面に変化がないようなアイドル時間が一定以上続くと切れます。 sshをトンネリングで使っていると切れると困るので、切れても自動で再接続してくれるようにしましょう。

まずはHomebrewでautosshをインストール。

$ brew install autossh

あとは以下のコマンドを打つだけ。

$ autossh -f -M 0 -N -D 1080 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" user@host

簡単にオプションについて

  • -f バックグラウンドで動かす

  • -M 0 よくわからんのでおまじない的

autosshの他、以下のように透過的にsshのオプションを指定できる

  • -N シェルを使わない

  • -D 指定ポートでSocks5として振る舞う autosshを使う最大の目的

  • -o sshのパラメータを指定 詳細は以下

  • ServerAliveInterval 45 切断防止のため、指定秒数間隔で応答確認をする

  • ServerAliveCountMax 2 指定回数応答がない場合は切断する(その場合autosshが再接続する)

autosshの終了

$ killall autossh