PR TIMESデザイナー&エンジニアブログ BREAK TIMES

PR TIMES Developer Blog(デザイナー&エンジニアによる開発者ブログ)

PR TIMES Developer Blog

当ブログは下記URLに移転しました。
https://developers.prtimes.jp/

簡単にビデオ通話アプリをつくれるSkyWayのご紹介☆*:.。. [WebRTC]

PR TIMES エンジニアのうさみです。
先日、開発していたアプリをお披露目した時のことです。

社内スタッフの声:
「このアプリにビデオ通話機能があるといいですね」
「・・・というより、無いと不便で困ります」

私:
「・・・(あと1日でビデオ通話機能の追加!?)」

元々の仕様としては、SlackのAPIを叩いて指定したSlackのチャンネルへ通知するというシンプルな機能だけだったのですが、急遽ビデオ通話機能を追加することになったのでした。こういうシチュエーション、よくありますよね・・・

ビデオ通話機能をもったアプリ開発の経験は無く、時間もありませんでした。また、社内でのみ利用するアプリでしたので、開発コストを掛けずに確実に動作させるために何かいい方法は無いかと調べている中で、NTTコミュニケーションズ様の「SkyWay」というサービスを知りました。

nttcom.github.io

以下のサイトには、NTTコミュニケーションズ様が「SkyWay」を開発された経緯や今後の展開について記載されています。(2015年8月時点)

IP電話の次に起こるパラダイムシフト "WebRTC"への挑戦
http://www.ntt.co.jp/journal/1508/files/jn201508036.pdf

国内製で且つ大手企業さまによるサービス提供、また、公式のサンプルや日本語のドキュメントもあるため、取り入れやすそうだと直感し、利用してみることにしました。何より無料で利用できるとのいうのはうれしいです (* ´ - ` *)

WebRTCを利用するのは初めてでしたので、そもそも「WebRTC」とは何かというところから・・・

WebRTC (Web Real-Time Communication)とはWorld Wide Web Consortium (W3C)が提唱するリアルタイムコミュニケーション用のAPIの定義で、プラグイン無しでウェブブラウザ間のボイスチャットビデオチャット、ファイル共有ができる。

引用元:WebRTC - Wikipedia


下記サイトではWebRTCについてまとめていらっしゃいます。

・WebRTC とはなんだったのか ← WebRTCの基本的なことはコチラ
https://gist.github.com/voluntas/67e5a26915751226fdcf

・WebRTC トラノマキ ← WebRTCの今後についてはコチラ
https://gist.github.com/voluntas/59a135343538c290e515

つまり、チャット機能やファイル共有機能、そしてビデオ通話機能を実装するために必要となるもの(面倒なところ)を提供してくれるサービスですね。サーバーを用意してバックエンド側を実装する必要がないので、クライアント側のアプリ開発に注力することができるのはいいですね。


どういうものか把握できましたので、いざ実装を・・・といきたいところですが、今回はご紹介までにさせていただき、実装方法については割愛させていただきます。

下記の公式のサイトとサンプルを見ていただくと実装にあたり丁寧に説明されていますのでご確認ください。

・SkyWay iOS/Android SDK ← SkyWayを利用するにあたっての事前準備についてはコチラ
https://nttcom.github.io/skyway/info-mobile.html

・SkyWay iOS SDK DOCS ← SkyWayを利用して開発している際に参考にするページはコチラ
https://nttcom.github.io/skyway/docs/#iOS

・SkyWay iOSサンプルアプリ ← チャット機能やビデオ通話機能を搭載したアプリのサンプルはコチラ
https://github.com/nttcom/SkyWay-iOS-Sample

公式のサイトの他に下記サイトも参考にさせていただきました。

qiita.com


実際に実装してみたところ、あっという間にビデオ通話アプリをつくることができました。1時間・・・いえ、30分もかからなかったでしょうか・・・まさに開発コストをかけずに実現することができました! ただ、実機での検証には十分に時間を費やす必要がありますね ( ´ - ` )

以上の参考にさせていただいたサイトやサンプルでは、端末を識別するのに利用するIDが動的なのですが、今回つくりたかったアプリは固定IDにする必要がありました。

次回は、固定IDによる実装方法をご紹介できればと思います☆*:.。.

複数サーバーでバッチを平行起動した時に便利すぎたSQLトランザクション

こんにちは、PR TIMESエンジニアの落合です!

みなさんも、普段バッチを作成する事があると思います。
1台のサーバーで、10分に1回などの実行に関しては、処理内容の事だけを考えてコーディングすればいいですが、
負荷分散や、バッチサーバーを冗長化する場合、複数台のサーバーで同時刻に実行したい場合もあると思います。
ただ、何も考慮せずに、複数台で起動してしまうと、重複した処理を実施してしまいます。
今回は、そういった時に、MySQLトランザクション機能を使って、簡単に解消出来た便利な機能を紹介させていただければと思います!

前提

サンプル用のテーブルは、以下のようになっている前提です。
データは、適当に10万件追加しました。

CREATE TABLE IF NOT EXISTS `site` (
  `id` int(13) NOT NULL AUTO_INCREMENT,
  `site_name` varchar(256) NOT NULL,
  `status` int(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='サイト情報';

プログラムに関しては、10万件のデータを1回のバッチで1000件ずつ処理を行い、5分置きに実行するようなバッチです。
今回は、サンプルなので、処理はせずに1件あたり1秒のスリープを実行しています。

$host = "localhost";
$user = "xxxxxxxx";
$pass = "xxxxxxxx";
$db = "develop";
$dsn = "mysql:dbname=$db;$host=$host;charset=utf8";

//まずは、DB接続
try{
    $pdo = new PDO ($dsn, $user, $pass);
} catch(PDOException $e) {
    var_dump("DB connected error!");
    exit();
}

// selectで1000件の情報を取得する。
$sql = "select * from site where status=0 Limit 2";
$res = $pdo->query($sql);

foreach($res as $v){
    // 取得したデータを、実行中のステータスに変更する。
    $sql = "UPDATE site SET status=1 where id=" . $v["id"];
    $res = $pdo->exec($sql);

    //実行処理内容が1秒かかるとします。
    sleep(1);

    // 取得したデータを、完了のステータスに変更する。
    $sql = "UPDATE site SET status=2 where id=" . $v["id"];
    $res = $pdo->exec($sql);
}
// 未実行件数を取得して、0件の場合は、ステータスを0に戻す。
$sql = "select count(*) as cnt from site where status=0";
$res = $pdo->query($sql);
$count_data = $res->fetch();
if($count_data["cnt"] == 0){
    $sql = "UPDATE site SET status=2 where id=" . $v["id"];
    $res = $pdo->exec($sql);
}

処理時間が同時刻に複数サーバーで実行した場合、ループ処理中に時間がかかると、最初に起動したバッチと、2台目に起動したバッチが、取得する内容が重複する為、このままだと分散処理が正常に行えない事象が発生してしまいます。

SQLトランザクションを使用して解消!

MySQLトランザクションには、4段階の分離レベルが存在しています。
ここで、全てを話してしまうと長くなってしまうので、割愛します。
以下のページに詳しく解説してありますので、ご参考にしてください!
MySQLのトランザクション分離レベル

さて、このトランザクションの挙動を応用して、解決したいと思います。
まず、アップデート自体を禁止する為の設定や、読み込み自体を拒否するような設定などがありますが、
今回はserializableに設定を行って、commitを実行しない限り、ロック中のレコードには、selectをしても返却されない状態にしたいと思います。

ロック用のテーブルを用意する

今回のSQLでは、該当するレコードをロックしてしまうので、処理実行中かを判断する為のテーブルを用意します。
そこに、レコードで実行プログラム名を追加していきます。

CREATE TABLE IF NOT EXISTS `cron_lock` (
  `id` int(13) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='ロックテーブル';

ALTER TABLE `cron_lock` ADD PRIMARY KEY (`id`);

INSERT INTO `cron_lock` (`id`, `name`) VALUES(1, 'test.php');

それでは、ロックデーブルが完成したら、sshを2個立ち上げて、テストしてみます。

#プロセスAで実行
mysql> use develop
mysql> SET tx_isolation = SERIALIZABLE;
mysql> SET innodb_lock_wait_timeout=30;
mysql> START TRANSACTION;
mysql> select * from cron_lock where name='test.php' FOR UPDATE;
+----+----------+
| id | name     |
+----+----------+
|  1 | test.php |
+----+----------+
1 row in set (0.00 sec)

次に同じクエリを実行したら、レスポンスが返却されない事がわかると思います。

#プロセスBで実行
mysql> use develop
mysql> SET tx_isolation = SERIALIZABLE;
mysql> SET innodb_lock_wait_timeout=30;
mysql> START TRANSACTION;
mysql> select * from cron_lock where name='test.php' FOR UPDATE;
|
# ロックされているので返却を待っている状態です。

次に、プロセスAをcommitして、ロックを解除すると、プロセスBで順番待ちしているselectが実行されます。

#プロセスAで実行
mysql> COMMIT;

すると、プロセスBのSelectの結果が返却されると思います。

#プロセスBで実行
mysql> select * from cron_lock where name='test.php' FOR UPDATE;
+----+----------+
| id | name     |
+----+----------+
|  1 | test.php |
+----+----------+
1 row in set (20.91 sec)

結果は分かっていても、心の中では、オォォーって感動してしまいます。(笑)
さて、この挙動を応用して、複数同時起動しても実行待機してくれる仕組みをプログラムで作成していきたいと思います。

負荷分散対応バッチのプログラムについて

さて、先ほど作成したcron_logテーブルを用いて、プログラムにしていきたいと思います。

$db = "develop";
$dsn = "mysql:dbname=$db;$host=$host;charset=utf8";
//まずは、PDOでDB接続を行う。
try{
    $pdo = new PDO ($dsn, $user, $pass);
} catch(PDOException $e) {
    var_dump("DB connected error!");
    exit();
}

// cron_lockトランザクションを設定して、レコードをロックする。
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query("SET innodb_lock_wait_timeout=30;");
$pdo->beginTransaction();
$sql = "select * from cron_lock where name='test.php' FOR UPDATE;";
$res = $pdo->query($sql);

// selectで1000件の情報を取得する。
$sql = "select * from site where status=0 Limit 2";
$res = $pdo->query($sql);
foreach($res as $v){
    // 取得したデータを、全件処理開始フラグを設定する。
    $sql = "UPDATE site SET status=1 where id=" . $v["id"];
    $array[] = $v;
}

// cron_lock解除
$pdo->commit();

foreach($array as $v){
    //実行処理内容が1秒かかるとします。
    sleep(1);

    // 取得したデータを、完了のステータスに変更する。
    $sql = "UPDATE site SET status=2 where id=" . $v["id"];
    $res = $pdo->exec($sql);
}

// 未実行件数を取得して、0件の場合は、ステータスを0に戻す。
$sql = "select count(*) as cnt from site where status=0";
$res = $pdo->query($sql);
$count_data = $res->fetch();
if($count_data["cnt"] == 0){
    $sql = "UPDATE site SET status=2 where id=" . $v["id"];
    $res = $pdo->exec($sql);
}

最後に

今回のコードは、あくまでも参考までのコードでしかありませんが、これらをクラス化しておき、
色んな多重起動するバッチに使用するのもいいかと思います。
設計時に、色々考え、考慮したのですが、毎分実行するようなプログラムで、処理が追いつかない時に、複数のプロセスで同時起動したい場合などにも使える機能だと思います。
実行時間短縮に、プロセスをForkして、並列処理を実行して、速度を担保したとしても、複数サーバーで実行出来ていれば、冗長化出来ているので、安心できます。

今回は、簡単だと思った事が、ちゃんと仕様として、考えたら、意外と難しかったというハマりについて向き合った結果、トランザクションの新たな使い方を発見したので、書かせていただきました。
これを機に、複数サーバーによる、バッチを同時起動する事で、サービスの事前処理などの速度改善に役立てたいと思っています。

リソース監視ツール:dstat

こんにちは、エンジニアの呉です。

今日はサーバーリソース監視についてお話ししたいと思います。Linuxではいろいろなコマンドがあります。例えばtop,uptime,vmstat,iotopなど、今回紹介したのはdstatコマンドです。

まずインストールしましょう。以下のコマンドで簡単にできます。

sudo yum -y install dstat

次dstatコンマンドを実行してみましょう

dstat

f:id:breaktimes:20160201000741p:plain

コマンドの書式

dstat [option/plugin] [更新時間] [ファイル]
✴︎[ ]省略可

デフォルトで1秒毎に以下の情報を更新されます。
  • total-cpu-usage :CPU
  • dsk/total :ディスク
  • net/total :ネットワーク
  • paging :ページング
  • system :システム
主なオプションは
dstat -h

f:id:breaktimes:20160201002926p:plain

プラグイン

データーベースなどの監視も行えます。dstatの場合はPythonスクリプトで書かれたプラグインで対応できます。
プラグインの場所は/usr/share/dstat/の配下にあります。以下のコマンドはプラグインのリストが表示できます。

dstat —list

サーバー監視は結構大変な仕事でした。いろんなツールを使って分析したほうがいいと思います。

「急成長サービスをつくっている人の考え方」- トランスリミット高場氏に登壇頂きます。

デザイナーの新井です。
PR TIMESでは、不定期で「BREAK TIMES NITE」という名前で外部講師を招いてセミナーをしています。6回目の今回は新オフィスでの初めての開催となります。
f:id:breaktimes:20160125125606j:plain
登壇講師にForbes誌の「日本の起業家ランキング2016ー凄いスタートアップ55選ー」や「注目のUnder30起業家10人」にも選出され、脳トレアプリ「Brain Wars」や「Brain Dots」と立て続けにグローバルな人気サービスをつくっているトランスリミットの高場大樹氏をお迎えして「急成長サービスをつくっている人の考え方」をテーマにお送りします。
アプリ開発のアイデアが生まれるまでのプロセスや、世界中の人を熱狂させるものづくりの秘密について、よいものを作るために意識していること、大切なこと、重要なこととは何かについて話していただきます。
エンジニアの方、起業を考えている方、お近くの方などみなさまのお越しをお待ちしております!
breaktimes.connpass.com

PR TIMESオフィス移転しました!

PR TIMESフロントエンジニアの山田です。

外苑前にオフィス移転してから1週間が経ちました。
エンジニアですが今回の移転プロジェクトにもメンバーとして関わっていましたので、ご紹介します。

まず以前のオフィスでは休憩スペースも小さく、他部署の方々とあまり交流する動線がない状態でした。そこで新オフィスでは広いカフェスペースやソファスペースを作り、集まれるような場にしています。
f:id:breaktimes:20160125200958j:plain

続きを読む

書籍紹介「ヘルシープログラマ」生産性リソースとしての健康

PRTIMESエンジニアの深川です。

今回は書籍の紹介です。昨年読んでよかった書籍の一つの「ヘルシープログラマ」について紹介します。

長時間座ったまま仕事を続けるプログラマには、腰痛、手首の痛み、目の痛みや頭痛がつきものです。少しの間イスから立ち上がったり、食生活を意識したり、体重などの数値を計測するなど、生活習慣を変えるだけで健康状態は改善します。 本書は、身体を意識することの重要性やウォーキングが健康に及ぼす好影響、栄養を意識した食事、簡単なエクササイズを解説し、さらに眼精疲労や腰痛など、プログラマが直面する様々な健康問題を回避し克服するアイデアとテクニックを紹介します。日本語版では吉岡弘隆氏による「散歩とイングレス」を付録として収録。プログラミングを快適に続けたいと考える技術者必携の一冊です。

http://www.oreilly.co.jp/books/9784873117287/

ヘルシープログラマ ―プログラミングを楽しく続けるための健康Hack

ヘルシープログラマ ―プログラミングを楽しく続けるための健康Hack

■目次

  1. 紹介書籍の目次
  2. 他の方の感想
  3. どこを読むべきか
  4. どこが良いか
  5. 自分はどうするか?(どうしたか?)
  6. まとめ

■1. 紹介書籍の目次

1章 変化を起こそう
1.1 健康のユニットテスト
1.2 精神と身体のつながり
1.3 健康へのイテレーティブなアプローチ
1.4 習慣の背景にある科学
1.5 習慣の再プログラミング
1.6 振り返り

2章 健康のブートストラップ
2.1 脚の上での思考
2.2 もっと健康になるために歩こう
2.3 人生の時間
2.4 歩き方を学ぼう
2.5 外に出よう
2.6 振り返り

3章 椅子よさらば?
3.1 座っていることは害だと考えられている
3.2 起立の真実
3.3 作業環境の拡張
3.4 振り返り

4章 アジャイルなダイエット
4.1 ダイエットに対するイテレーティブなアプローチ
4.2 特異なダイエットよりも栄養素のバランスを
4.3 食べ過ぎていませんか?
4.4 トレンドへの追従よりもカロリーの計測を
4.5 摂取カロリーの調整
4.6 お仕着せのメニューよりも個人のお好みを
4.7 振り返り

5章 頭痛と眼精疲労の対策
5.1 視覚のユニットテスト
5.2 コンピュータビジョン症候群への対策
5.3 頭痛の引き金への対策
5.4 頭痛への対応
5.5 振り返り

6章 腰痛への対策
6.1 コアの筋肉のユニットテスト
6.2 背中の解剖学
6.3 動力源の強化
6.4 エルゴノミクスの改善
6.5 振り返り

7章 手首痛への対策
7.1 手首のユニットテスト
7.2 手首痛の原因
7.3 痛みを生じさせないためのエクササイズ
7.4 アレクサンダーテクニークによる緊張の緩和
7.5 リストブレイスによる動きの制限
7.6 振り返り

8章 実践的なエクササイズ
8.1 頭脳のエクササイズ
8.2 健康的なポモドーロブレイク
8.3 記録を残す
8.4 健康のためのゲームプレイ
8.5 Webでの健康法
8.6 振り返り

9章 個室の外で考えよう
9.1 ビタミン Dの摂取
9.2 ビタミン Dの誇大広告を照らしだす
9.3 免疫システムの強化
9.4 一般的な風邪への対処
9.5 木陰で考える
9.6 振り返り

10章 健康のリファクタリング
10.1 ウォーミングアップ
10.2 フィットネスの様々な側面
10.3 フィットネスのユニットテスト
10.4 自分というハードウェアのアップグレード
10.5 振り返り

11章 チームを作ろう
11.1 メッセージの送信
11.2 健康への投資
11.3 仲間と楽しむ
11.4 より良いチームの構築
11.5 振り返り

12章 進め、健康なプログラマ
12.1 継続的改善
12.2 ソーシャルな習慣を作り出す
12.3 健康であることの喜び

付録A 散歩とイングレス

http://www.oreilly.co.jp/books/9784873117287/

■3. どこを読むべきか

1章は本書の考え方のコアとなる章なので、ここを読んでみて自分にフィットする内容かまず確認してみると良いと思います。

本書が合う合わないに関わらず、5章から7章の頭痛、眼精疲労、腰痛、手首痛への対策はプログラマの生産性に健康面からリスクとなりやすいところなので一読してみることをおすすめします。

■4. どこが良いか

本書の良い所について、いくつか挙げてみます。

他の感想でも挙げられている通り、なぜ健康が良いかという理由をプログラマという職業を楽しく生産的に継続するために必要だとはっきりと定義しているところです。そのために現実(自分の身体状況とコンディション)をきちんと捉えて自分の身体の健康をハックしていこうという視点で、様々な方法を記している部分にあります。

また、健康づくりの捉え方も単語や考え方がプログラマと親和性が他界の部分が多々あるので、ソフトウェアエンジニアとその隣接職業の方が興味を持ちやすく、健康への取り組みへの敷居が低く感じやすいと思いました。

■5. 自分はどうするか?(どうしたか?)

本書は全体を通して、ソフトウェア開発でよく適用されるイテレーションやインクリメンタルといった考え方を健康に適用して健康の継続性と増進に対するコスト軽減とモチベーション維持を狙うことや、そもそも健康を定義して(今回はプログラマの生産性と幸福)、各部分の状態をテストすることで早期に問題を発見できるようにするというような考え方が通底していて、私はとても良いと思いました。

私は以前から慢性的な頭痛に悩んでいます。本書の内容を読んでから気をつけていることについて書きます。

□レビューをする(医療機関を利用する)

健康や病気にについての情報はネットや書籍で用意に調べることができるようになりましたが、プログラマの観点からテストやレビューを自分単体で完結することは難しいと思います。定期的なメンテナンスや問題発生時の切り分けは専門ドメインの知識をもった人に任せるほうが、的確な対応ができると思います。

頭痛については頭痛専門外来に通ってみた結果、通常の内科で相談したとき、より詳細な診断と処方を頂きました。

頭痛や睡眠障害といった症状が具体的なものについては専門外来が良いと思われますが、症状がはっきりしない場合や疾病の自覚症状がない場合も含めて定期的に身体をチェックすることは健康を維持には良いと思います。なかなか億劫になりがちですが、健康診断や人間ドックで定期的にアラート監視をしておけば障害(身体の不調)の検知に役立つのではないでしょうか。

□作業する場所を意図的に変える

本書でも同じ姿勢を続けたままの健康リスクについて書かれていました。逆に言えば、意図的に作業する姿勢を変えたり、作業する場所を変えることは健康にとって良いことです。PRTIMESでは今月の18日からオフィスを移転して、自分の座席以外でも気分転換に作業できるスペースが増えました。今後は時間のログと取りながら、意識的に時間をきめてこんな風にスタンディングで作業したり、別の椅子で作業してみようかと思っています。生産性向上の試みとして、ポモドーロテクニックにも興味はあるので、少しづつ試行錯誤しつつ試してみようと思います。

www.lifehacker.jp

prtimes.jp

■6. まとめ

本書が注目された理由として、健康を気にしないといけない年齢になった職業プログラマが増えたというところはあると思うのですが、
ソフトウェアエンジニアとその隣接職業というのは、不健康な生活習慣になりがちだと思います。

年齢性別に関係なく、本書に目を通して継続的な生産性の土台となるハードウェア(身体)をメンテナンスするきっかけを作ってはいかがでしょうか。

ヘルシープログラマ ―プログラミングを楽しく続けるための健康Hack

ヘルシープログラマ ―プログラミングを楽しく続けるための健康Hack

PR TIMESエンジニア・デザイナー向け3日間のハッカソンインターン募集開始 最優秀者には入社2年目の給与で2017年度の内定確約!

株式会社PR TIMESは、2017年卒業予定の大学生・大学院生・高等専門学校生を対象とした3日間にわたるハッカソン形式の【PR TIMESウィンターインターン2016】を2016年2月22日(月)から24日(水)に開催します!

f:id:breaktimes:20160114161232j:plain

ハッカソンでは、2~3人のチームに分かれフローデータ、ストックデータなどPR TIMESが持つ2万件以上にのぼるプレスリリースのビッグデータを解析してハッキングを行い、新しいサービスの開発を体験いただきます。

1日目はチームビルディング・アイデアソンを中心に活動していただき、2日目にチームアイデアピッチ後、フィードバックを基に実際にハッキングを開始、最終日の3日目にハッキングデモ、審査、表彰となります。期間中は、社員がサポートエンジニアスタッフとして開発に関する技術的な面などをサポートします。また今回のハッカソンで参加者に発表いただくアイディアは、実際にサービス化することも検討しています。

インターンを通して個人別最優秀賞に選ばれた方には、最終日に2017年度の内定通知書(辞退可)をお渡しします。入社後の給与は大卒初任給を飛び越えて、入社2年目の実績と同額とします。(300,000円/月 賞与年2回)

最優秀チームには、「iPad Pro」または「Surface Pro 4」のどちらかご希望のものを各チームメンバーへ贈呈します。

PR TIMESハッカソンインターンの募集対象となる学生は、エンジニアまたはデザイナーを志望される、WEBサービスまたはiOS/Androidアプリの企画、開発、デザインのいずれかの経験がある方とします。ハッカソン参加が初めてでも、当日実際のデータを使いながら、社員により詳しいレクチャーを行います。皆様のご応募お待ちしております。
prtimes.co.jp

続きを読む

開発環境構築にansibleを使用する

こんにちは、エンジニアのアカイです。

vmやコンテナを複数作成する場合、各環境に様々なものをインストールすることになりますが、同じようなコマンドをそれぞれに対して実行するのも面倒なので、いわゆる構成管理ツールを使用するのはもはや常識になっている感があります。

これをもう少し噛み砕いて言うと、
よくブログ記事などで「~のインストール手順」等として紹介されている内容を、
自動化するプログラム、ということになるでしょう。

今回は、とてもシンプルで学習コストも低そうなansibleを使用して、
単純なパッケージのインストールよりは少し複雑なもの(主にソースからビルドするもの)の例を紹介します。

想定しているのは開発環境として使用するものです。
本番環境作成時には、詳細について慎重に検証する必要があると思いますのでご了承ください。

環境はubuntu 15.04です。
CentOS等のrpmディストリビューションの場合は適宜読み替えてください。

また、ユーザー名は'username'としていますので、実際は存在するユーザー名に変更する必要があります。

[準備1] ansibleインストール

$ sudo aptitude install ansible

まずはansibleを使用できるようにします。

今回の内容では複雑な機能はほとんど使用しませんが、ansibleの基本的な使用方法は以下を参照してください。

ansible


ansibleはpython2系で動作しますので、python3を使用している人は注意が必要です。

具体的にはFAQを参照してください。


実行内容はYAMLファイルに記述します(playbook)。

playbookの実行は以下のように行います。

$ ansible-playbook -i hostname, xxx.yml
$ ansible-playbook -c local -i localhost, xxx.yml # localhostの場合

通常はhost設定ファイルに対象を記述するのが一般的なようです。

[準備2] 依存パッケージ群のインストール等

---
- hosts: all
  sudo: true
  user: username
  vars:
    install_packages:
      - build-essential
      - bison
      - curl
      - zlib1g-dev
      - libssl-dev
      - libyaml-dev
      - libsqlite3-dev
      - sqlite3
      - libxml2-dev
      - libxslt1-dev
      - autoconf
      - libncurses5-dev
      - libmysqlclient-dev
      - wget
      - gcc
      - make
      - automake
      - vim
      - xzip
      - gettext
      - tclsh
      - tcl8.6-dev
      - libcurl4-gnutls-dev
  tasks:
    - name: install aptitude
      command: apt-get -y install aptitude
    - name: update packages
      command: aptitude -y update
    - name: install packages
      command: aptitude -y install {{ item }}
      with_items: install_packages
    - name: locale settings
      shell: locale-gen en_US.UTF-8 ; locale-gen ja_JP.UTF-8 ; update-locale LANG=en_US.UTF-8


主にビルドする環境を整えるため、依存するパッケージのインストールを行っています。

ついでにlocaleの設定も行っています。

共通系の設定はこのようなymlを準備しておいて、そこに追加するようにすれば効率が良いです。

ただし、様々な種類の環境に対応していると、パッケージ数がかなりのものになると思いますので、場合によっては種類別に分割したほうがよいかもしれません。

rpm系の場合はパッケージ名が違っていることが多いので、注意が必要です。

emacs

---
- hosts: all
  sudo: true
  user: username
  vars:
    src: /usr/src
    emacs_ver: 24.5
  tasks:
    - stat: path={{ src }}/emacs-{{ emacs_ver }}.tar.xz
      register: emacs_src
    - stat: path={{ src }}/emacs-{{ emacs_ver }}
      register: emacs_dir
    - name: get emacs src
      get_url: url=http://ftp.jaist.ac.jp/pub/GNU/emacs/emacs-{{ emacs_ver }}.tar.xz dest={{ src }}/emacs-{{ emacs_ver }}.tar.xz
      when: not emacs_src.stat.exists
    - name: extract emacs src
      command: tar xJf emacs-{{ emacs_ver }}.tar.xz chdir={{ src }} creates={{ src }}/emacs-{{ emacs_ver }}
      when: not emacs_dir.stat.exists
    - name: configure emacs
      command: >
        ./configure
        --without-toolkit-scroll-bars
        --without-xaw3d
        --without-sound
        --without-pop
        --without-xpm
        --without-tiff
        --without-rsvg
        --without-gconf
        --without-gsettings
        --without-selinux
        --without-gpm
        --without-makeinfo
        --without-imagemagick
        --without-x
        chdir={{ src }}/emacs-{{ emacs_ver }}
    - name: make emacs
      command: make chdir={{ src }}/emacs-{{ emacs_ver }}
    - name: make install emacs
      command: make install chdir={{ src }}/emacs-{{ emacs_ver }}

このパターンは、ソースからビルド・インストールする場合では比較的一般的な形式だと思います。

実際に使用する際にはインストール後の設定ファイル設置等も行っています。

特筆することはあまりありませんが、強いて言うと、configureで複数オプションを設定する際、
'command: >' に続けてコマンドラインを記述することで、複数行に書くことができます。

git

---
- hosts: all
  sudo: true
  user: username
  vars:
    src: /usr/src
    git_version: 2.7.0
  tasks:
    - stat: path={{ src }}/git-{{ git_version }}.tar.xz
      register: git_src
    - stat: path={{ src }}/git-{{ git_version }}
      register: git_dir
    - name: remove package
      command: aptitude -y purge git-core git
    - name: get src
      get_url: url=https://www.kernel.org/pub/software/scm/git/git-{{ git_version }}.tar.xz dest={{ src }}/git-{{ git_version }}.tar.xz
      when: not git_src.stat.exists
    - name: extract src
      command: tar xJf git-{{ git_version }}.tar.xz chdir={{ src }}
      when: not git_dir.stat.exists
    - name: configure
      command: ./configure chdir={{ src }}/git-{{ git_version }}
    - name: make install
      command: make install chdir={{ src }}/git-{{ git_version }}
    - name: clone git-flow
      command: git clone --recursive git://github.com/nvie/gitflow.git chdir={{ src }}
    - name: install git-flow
      command: make install chdir={{ src }}/gitflow

gitの最新版とgit-flowを続けてインストールするymlです。

このパターンもemacsの場合と大差ないですが、少し違うところと言えば、git-flowのソースをgit cloneで取得しているあたりでしょうか。

また、重複を避けるためaptでgitを削除しています。

gitのコード自体もgithubからzipファイルをダウンロードする等して取得できますが、

何かと都合がよさそうなのでversion別に用意されたものを指定しています。

rbenv

事前にfiles/rbenv/rbenv.sh を準備します。
内容は以下の通りです。

export RBENV_ROOT=/usr/local/rbenv
export PATH=$RBENV_ROOT/bin:$PATH
eval "$(rbenv init -)"
---
- hosts: all
  user: username
  sudo: true
  vars:
    ruby_ver: 2.3.0
    target: /usr/local/rbenv
    rbenv_path: /usr/local/rbenv/bin/rbenv
  tasks:
    - stat: path={{ target }}
      register: rbenv
    - name: clone rbenv
      command: git clone https://github.com/sstephenson/rbenv.git {{ target }}
      when: not rbenv.stat.exists
    - stat: path={{ target }}/plugins/ruby-build
      register: ruby_build
    - name: clone ruby-build
      command: git clone https://github.com/sstephenson/ruby-build.git {{ target }}/plugins/ruby-build
      when: not ruby_build.stat.exists
    - name: add rbenv.sh
      copy: src=files/rbenv/rbenv.sh dest=/etc/profile.d/rbenv.sh
    - name: install ruby
      shell: . /etc/profile ; rbenv install -s {{ ruby_ver }}
    - name: set ruby version
      shell: . /etc/profile ; rbenv global {{ ruby_ver }}

rbenvのインストールと指定バージョンのrubyをビルド・インストールを行っています。

公式のインストール手順はホームディレクトリが対象になっていますが、
/usr/local/rbenvへインストールするパターンにしています。

環境変数の設定等は、インストール実行時には参照されません。

これは、/etc/profileがログインシェルでロードされるからでしょう。

そのため、コマンド実行前に毎回ロードする方法で解決しています( . /etc/profile)。




この類のツールは、場合によっては一度設定した内容を変更せずに長く使い続ける可能性もあります。

同種のツールに puppet / chef 等ありますが、
ansibleはおそらく最もシンプルに使用する限り、すぐに使い始めることができると思いますので、
この分野にまだあまり手を出していない人には、特におすすめいたします。

カラーを指定するとアイコン画像が指定の色に変わる仕組みをつくってみる☆*:.。.[合成処理 / マスク処理]

PR TIMES エンジニアのうさみです。
iOSアプリを開発する機会がありまして、テーマカラーの値を指定するとそのテーマカラーの画面に切り替わる仕組みを取り入れることにしました。

テキストの色や背景色は下記のようにプロパティを変更すればいいだけなので、簡単に実現できますね。

UILabel *label = [UILabel alloc] initWithFrame:CGRectMake(0, 0, 100.0f, 20.0f)];
label.textColor = [UIColor redColor];

UIView *view = [UIView alloc] initWithFrame:CGRectMake(0, 0, 100.0f, 100.0f)];
view.backgroundColor = [UIColor greenColor];

この仕組みを実装している中で、例えば下記のようなアイコンも指定したカラーにできたら、色ごとにアイコンを用意する必要も無くなるし、デザインの変更がある度にIllustratorPhotoshopを開いて画像を書き出す手間も無くなっていいなと思うように・・・

f:id:breaktimes:20160112122947p:plain
<画像1> icon.png

どのように実現できるか考えてみたところ、画像処理に強いわけでもない私が思い浮かぶ方法として下記の2つが思い浮かびました。

1. 画像データを解析して、任意のカラーの領域を塗りつぶす
2. 画像の合成処理を駆使する

今回はなるべく簡単な方法で実現してみたいと思いましたので、2の方法で実装してみました。

まず、画像は<画像1>のように下記の色で構成されることが前提となります。
・背景領域:透明部分
・色が変更されない領域:白地部分
・色が変更される領域:黒地部分

例えば、赤色を指定した場合は下記のような画像となることを期待します。

f:id:breaktimes:20160112122917p:plain
<画像2>

それでは実装してまいります!

まず、描画する領域を取得しておきます。

    // 描画領域を取得
    UIImage *image = [UIImage imageNamed:@"icon"];
    CGSize  size = image.size;
    CGRect  imageRect = CGRectMake(0, 0, size.width, size.height);

次に、合成する際に必要な画像の表示領域全面を塗りつぶした画像を生成して準備しておきます。

    // カラーを指定
    UIColor *color = [UIColor redColor];

    // 指定カラーで全面塗りつぶした画像を生成
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, imageRect);
    UIImage *colorRectImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123301p:plain
<画像3>

    // 黒色で全面塗りつぶした画像を生成
    UIGraphicsBeginImageContext(size);
    context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
    CGContextFillRect(context, imageRect);
    UIImage *blackRectImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123026p:plain
<画像4>

    // 白色で全面塗りつぶした画像を生成
    UIGraphicsBeginImageContext(size);
    context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);
    CGContextFillRect(context, imageRect);
    UIImage *whiteRectImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123255p:plain
<画像5>

準備ができたら、これらの生成した画像とアイコンの画像を上手く合成して、期待している<画像2>の画像を生成します!

まず、直感で思いついたのは、白地はそのままで黒地部分の色を塗りつぶすということは、ブレンドモード「比較(明)」にした<画像3>をアイコン画像の上に被せたら実現できちゃうのでは?!・・・とやってみたところ。。

    // 指定カラー塗りつぶし画像をブレンドモード「比較(明)」でアイコン画像に被せる
    UIGraphicsBeginImageContext(size);
    [image drawInRect:imageRect];
    [colorRectImage drawInRect:imageRect blendMode:kCGBlendModeLighten alpha:1.0f];
    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123342p:plain
<画像6>

透明部分も塗りつぶされてしまいました・・・
透明部分も塗りつぶされることなく、そのままでいてほしいのです。。
他のブレンドモードも確認して、上記コードのblendModeを変更することで実現できるかなと期待するも、さすがにこんなに簡単なコードだけでは実現は無理のようです。。

そこで、下記の手順で処理することでできるのではと考えました。

1. アイコン画像自体の領域をマスクするためのマスク画像を生成

    UIGraphicsBeginImageContext(size);
    [image drawInRect:imageRect];
    [blackRectImage drawInRect:imageRect blendMode:kCGBlendModeSourceIn alpha:1.0f];
    [whiteRectImage drawInRect:imageRect blendMode:kCGBlendModeDarken alpha:1.0f];
    UIImage *maskImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123320p:plain
<画像7>


2. 指定カラー塗りつぶし画像をブレンドモード「比較(明)」でアイコン画像と合成

    UIGraphicsBeginImageContext(size);
    [image drawInRect:imageRect];
    [colorRectImage drawInRect:imageRect blendMode:kCGBlendModeLighten alpha:1.0f];
    UIImage * coloredBaseImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

f:id:breaktimes:20160112123342p:plain
<画像8>


3. 2で生成した画像を1のマスク画像でマスク処理

    maskImageRef = maskImage.CGImage;
    maskRef = CGImageMaskCreate(CGImageGetWidth(maskImageRef),
                                CGImageGetHeight(maskImageRef),
                                CGImageGetBitsPerComponent(maskImageRef),
                                CGImageGetBitsPerPixel(maskImageRef),
                                CGImageGetBytesPerRow(maskImageRef),
                                CGImageGetDataProvider(maskImageRef), NULL, NO);
    maskedImageRef = CGImageCreateWithMask(coloredBaseImage.CGImage, maskRef);
    UIImage *coloredImage = [UIImage imageWithCGImage:maskedImageRef];

    CGImageRelease(maskImageRef);
    CGImageRelease(maskRef);
    CGImageRelease(maskedImageRef);

f:id:breaktimes:20160112122917p:plain
<画像9>


・・・!!
期待したとおりの画像が生成できました!

以上の処理で固定で指定したimageとcolorを下記のように外部から指定できる関数にしておけば、簡単に色の変更ができるようになりますね。

// 関数
- (UIImage*)coloredImage:(UIImage*)image color:(UIColor*)color
{
    // Check image
    if (!image) {
        return image;
    }
    
    // Check color
    if (!color) {
        return image;
    }

      :

   〜 上記の処理 〜

      :


    return coloredImage;

}

// 呼び出し
UIImage *image = [UIImage imageNamed:@"icon"];
UIImage *coloredImage = [self coloredImage:image color:[UIColor redColor]];


とりあえず、これが簡単な方法かなと思いましたが、他にもっといい方法があれば、是非教えていただきたいです (* ´ - ` *)

今回は急ぎということもあり、Objective-Cでの実装でした(汗)
まだまだ、Objective-CからSwiftへと移行できていないので、Swiftをもっと触らないとなぁと思うのでありました ☆*:.。.

mongodb3.2でシャーディングを複数台で実践してみた

あけましておめでとうございます!!
チーフエンジニアの落合です!

それでは、新年1発目のネタですが・・・・MongoDB3.2です!
現在、PR TIMESで扱っているサービスに、MongoDBを導入検討を行っております。

導入する事を前提に、複数台のサーバーを用いて、
シャーディング構成(レプリカセットは無し)を作成しましたので、
本日は、構築手順を報告したいと思います。

前提

まずは、以下の通りに3台のVPSを開発環境として用意しています。
今回は、さくらインターネットVPSを使用し、スイッチを使ってプライベートネットワークを構築しています。

サーバー名* プライベートIP* OS*
mongo01 192.168.0.1 Centos6.5
mongo02 192.168.0.2 Centos6.5
mongo03 192.168.0.3 Centos6.5

インストール手順

MongoDBのインストール手順は、https://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/:mongodb公式サイトを参考にしながら3台全てに実施しています。
まずは、yumリポジトリを追加します。

$ vim /etc/yum.repos.d/mongodb-org-3.2.repo
[mongodb-org-3.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/
gpgcheck=0
enabled=0

次に、この登録したリポジトリを使用してインストールを実施します。

$ yum -y install --enablerepo=mongodb-org-3.2  mongodb-org

準備が完了しましたので、早速シャーディングを構築していきたいと思います。

続きを読む