読者です 読者をやめる 読者になる 読者になる

AWS Lambdaのimagemagickでリサイズ失敗問題について

結論

先に結論から言うと、Lambdaがデフォルトで提供しているimagemagickライブラリだと特定の画像のリサイズで失敗するっぽいです。 ローカルでnpm install imagemagickしたパッケージを含めてlambdaにzipアップロードして、同じコード同じ画像で試して正常に動いたので間違いないはず。

問題のエラー

以下Lambda functionを「Edit code inline」で入力して、S3によくある普通の画像をアップロードして実行させました。*1

var im = require('imagemagick');
var aws = require('aws-sdk');
var s3 = new aws.S3();
var thumbnailSize = 600;

function resizeImage(context, bucket, key, data) {
    var contentType = data.ContentType;
    var extension = contentType.split('/').pop();
    im.resize({
        srcData: data.Body,
        format: extension,
        timeout: 0,
        width: thumbnailSize
    }, function(err, stdout, stderr) {
        if (err) {
            console.log(err);
            context.done('resize failed: '+key);
            return;
        }

        // avoid endless loop
        if (bucket.match(/-thumbnail$/i)) {
            context.done('no support bucket: '+bucket);
            return;
        }

        var thumbnailBucket = bucket + "-thumbnail";
        var thumbnailKey = 'thumbnail/' + key.split('.')[0] + "-thumbnail." + extension;
        s3.putObject({
            Bucket: thumbnailBucket,
            Key: thumbnailKey,
            Body: new Buffer(stdout, 'binary'),
            ContentType: contentType
        }, function(err, res) {
            if (err) {
                console.log(err);
                context.done('error putting object: '+key);
            } else {
                console.log('create thumbnail: '+thumbnailKey);
                context.done();
            }
        });
    });
}

var createThumbnail = function(event, context) {
    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));

    s3.getObject({
        Bucket: bucket,
        Key: key,
        IfMatch: event.Records[0].s3.object.eTag
    }, function(err, data) {
        if (err) {
            console.log(err);
            context.done('error getting object: '+key);
            return;
        }

        resizeImage(context, bucket, key, data);
    });
};

exports.handler = function(event, context) {
    createThumbnail(event, context);
};

すると以下のログがCloud Watchに出力されました。

{ [Error: Command failed: ] timedOut: false, killed: true, code: null, signal: 'SIGKILL' }

メモリを上げ下げしたり、画像の解像度・ファイルサイズ、画像の形式・メタ情報まで色々変更して試してみても改善されず。 最終手段で、imagemagickを含めてアップして実行するとすんなり動きました。意味がわからない。

まとめ

素直にnpmで最新版(安定版)を準備してまとめてLambdaにzipアップロードが一番良いと思います。 ちなみにzipアップロードですが、以下のようなshellでzipを作ってます。そのままフォルダを圧縮するとエラーになるので気をつけてください。

#!/bin/sh
zip -r lambda index.js node_modules

*1:こちらを参考にさせてもらいました。

qiita.com

GitHub Desktopのメリット・デメリット

はじめに

この記事は、「Git Advent Calendar 2015」の17日目の記事です。 正直Gitには詳しくないので、日頃お世話になっている「GitHub Desktop」について書きます。

続きを読む

FuelPHP 2.0 α版を使ってみた

はじめに

これは、FuelPHP Advent Calendar 2015 の13日目です。

現在、FuelPHP 1.7.3が安定版として公式ページに公開されていますが、今年1月にFuelPHP 2.0 α がリリースされました。 FuelPHP 2.0 α版を実際にインストールして使ってみようと思います。

FuelPHP1系との違い

アプリケーションとコンポーネントという仕組みを導入するにあたって大きな変更になるため 1系とは別のgithubアカウントで管理されています。1系はfuelですが、2系はfuelphpです。

おおまかな変更は以下です。

  • PHP5.4以上
  • FuelPHPのパッケージがすべてcomposerで管理
  • componentという単位でアプリケーションを作成する
  • 複数のcomponentを追加でき、別のcomponentを利用することも可能
  • 命名規則がスネークケースからキャメルケースに変更

例えば、デフォルトのWelcomeクラスは以下のように変わりました。

<?php
class Controller_Welcome extends Controller
{
    /**
    * The basic welcome message
    *
    * @access  public
    * @return  Response
    */
    public function action_index()
    {
        return Response::forge(View::forge('welcome/index'));
    }

    /**
    * A typical "Hello, Bob!" type example.  This uses a Presenter to
    * show how to use them.
    *
    * @access  public
    * @return  Response
    */
    public function action_hello()
    {
        return Response::forge(Presenter::forge('welcome/hello'));
    }
    // ・・・省略
}
<?php
class Welcome extends Helpers\Base
{
    /**
    * The basic welcome message
    *
    * @access  public
    * @return  View
    */
    public function actionIndex()
    {
        return \View::forge('welcome/index');
    }

    /**
    * A typical "Hello, Bob!" type example.  This uses named parameters in
    * the route, and a Presenter to show you how to use them.
    *
    * @access  public
    * @return  Presenter
    */
    public function actionHello()
    {
        return \Presenter::forge('welcome/hello')
            ->set('name', $this->request->getParam('name', 'World'));
    }
    // ・・・省略
}

とても読みやすくていいですね!

動作確認

私は、AnsibleとVagrantでCentOS6.5+PHP5.6+PHP-FPM+Nginx1.8+MySQL5.6の環境をMac上に作成して検証しました。

fuelphp/fuelphp · GitHub をクローンして、composerで必要なライブラリをインストールします。

git clone https://github.com/fuelphp/fuelphp.git fuelphp2
cd fuelphp2
composer install

その後、設定したドメインにアクセスしてみると、

f:id:gurimmer:20151213003129p:plain

Fatal error: Call to undefined method Whoops\Handler\JsonResponseHandler::onlyForAjaxRequests() in /vagrant/fuelphp2/vendor/fuelphp/foundation/src/Error.php on line 215

エラーが発生。。。修正しましょう。。。*1

fuelphp/foundation の修正

エラーの通り、fuelphp/foundationリポジトリError.php:215あたりを見てみると

<?php
$this->whoops->pushHandler($this->pagehandler);

// next on the stack goes the JSON handler, to deal with AJAX reqqests
$jsonHandler = new JsonResponseHandler;
$jsonHandler->onlyForAjaxRequests(true);
//$jsonHandler->addTraceToOutput(true);

$this->whoops->pushHandler($jsonHandler);

どうもこの$jsonHandler->onlyForAjaxRequests(true);が定義されていないようです。 今度はfilp/whoops/src/Whoops/Handler/JsonResponseHandler.phpをのぞいてみるとaddTraceToOutputというfunctionが定義されていました。おや?

f:id:gurimmer:20151213004410p:plain

コメントアウトされてる・・・ということでここのコメントアウトを入れ替えます。(おそらく、変更されることがわかってたのでしょう)

<?php
//$jsonHandler->onlyForAjaxRequests(true);
$jsonHandler->addTraceToOutput(true);

これでデフォルトページが表示されると思います。

f:id:gurimmer:20151213005003p:plain

f:id:gurimmer:20151213005219p:plain

左下のバージョン情報が「2.0-dev」になっていますね。

ベンチマーク

では、速度的にはどうなのでしょうか? 同じ環境で、1.7.3 と 2.0で簡単に比べてみました。

  • 1系:
    • Requests per second: 28.588 [#/sec] (mean)
    • Time per request: 179.407 [ms] (mean)
  • 2系
    • Requests per second: 4.754 [#/sec] (mean)
    • Time per request: 1053.7414 [ms] (mean)

圧倒的に1系がはやいですねwコンポーネント化による影響なのでしょうか。

まとめ

FuelPHP V2 α をインストールして試してみました。 コンポーネント化が進み、コンポーネントを追加するだけで色々カスタマイズができるようになるのは嬉しいなと感じました。例えば、モデル部分をコンポーネント化して、フロントと管理画面両方でさくっと使いまわせるの良いなと思う。

ただ、V2の開発がほぼ停止しており、悲しい気持ちです。。。

*1:プルリクを送っておこうと思います

PHPカンファレンス2015に行ってきました

福岡から東京蒲田まで遠出してみました。 イベントのためにこんなに遠出したのは、B'zのライブ以外では初めて。

f:id:gurimmer:20151010232338j:plain

きっかけ

6月にあった「PHPカンファレンス福岡」にスポンサーとして参加させていただいたんですが、 これがとても楽しかった!それがもっと大きな規模で東京であるならもっと面白いんじゃないかと思ったのがきっかけ。

あとPHPの産みの親「Rasmus Lerdorf」さんが来るというのもとても大きかった。

感想

PHPの話たくさん聞けて、有名な方々とお会いできたのがとても良かった!めったに聴く機会がない英語セッションも聞けて最高だった!勇気を振り絞ってRasmusさんと話せたのも良い経験。ただ英語がしゃべれなかったことを今日ほど悔やんだ日はないくらい悔しかった。(けどツーショットとれたので満足w)

f:id:gurimmer:20151010225407j:plain

聴いたセッションについて

PHPの今とこれから2015

f:id:gurimmer:20151010232328j:plain

今のPHPの現状と今後どうなっていくのかがとてもわかりやすくまとまっていてとても良かった!

意外だったのが、サポートが切れているPHP5.4以前を利用しているユーザが実に84%もいることに驚いた。 あとから「mb_string」エクステンション書いてる人と聞いて腰を抜かした。天才ですね。

今どきのSQLインジェクションの話題総まとめ

生で徳丸さん見れて感動。

『ウェブアプリの脆弱性の責任は開発者ではなく発注者(セキュリティー対策の指示がない場合)』だけど、 『SQLインジェクションについては「専門家としての責務」を重視して、開発側に過失があったと判決が出た(初めての判例)』ということなのでVAddy必須ですね。

他のトラックでやっている英語セッションが聴きたくて途中退室。

From PHP to Machine Code

f:id:gurimmer:20151010232404j:plain

英語だったのでほぼ聞き取れなかったけど、スライドとtwitterの反応を見ながらなんとなく内容を把握してた。「コンパイラ作ると楽しいからみんなも作ればいいよ」みたいな話だったと思う。

多分冗談を言っていたんだと思うけど、それについていけてた人が数名ほどいてうらやましかった。

超イケメンだった(謎

お昼

場所探し大変だったし、量多めでお腹いっぱいになって眠くなってしまった。 蒲田は餃子が美味しいというウワサを聞いていたが結局食べれず...

基調講演

PHP7についてと、いろんなPHPアプリケーションのベンチマーク結果がずらりと紹介された。

WordPressベンチマークだとPHP7はHHVMに負けてたけど、『Feedback-Directed Optimization(FDO)』というコンパイル最適化ツール?をかけるとHHVMとほぼ同等の結果がでてた。もはや意地ですね。

ベンチマークでチラッとでてたWordrobeというCMSが気になったので試してみたい。

あと、PHP7のテストをして欲しいという話の流れで出た「PHP 20バージョン入りのVagrant Box」はとても気になる。

github.com

質問タイムで「PHPフレームワークについて何か思うことがあれば聞きたい」という質問に対して、 「質問する相手を間違ってる。僕はPHPは使ってない。ただのC技術者だよ」という答えにハッとした。PHP開発者はPHP使わないw

f:id:gurimmer:20151012221742j:plain

今どきのPHP開発現場 - 2015年秋 -

PhpStorm+Vagrant+GitHub+TravisCI+herokuで開発する今どきの環境説明。

Scrutinizerという、コードフォーマットや静的解析をしてくれるSaaSがあるみたい。知らなかったので使ってみようと思う。

ツールにはいろんな人の知見が貯まってる』という言葉にグッと来た。

新原さんの話し方は、スッと内容が入ってくるしとても親しみやすい感じ。見習いたい。

脆弱性もバグ、だからテストしよう!

福岡のサービスで、グローバルにサービス展開しているVAddy。市川さんを応援しに見に行ってきた。

サービスの芯がちゃんとしていて、ブレずに進んできている感じは本当にすごい。勉強になります。

LT

弊社社員が登壇するので、前の方に陣取りました。 どなたも本当に面白く、為になるLTばかりで、私が応募したLTとか絶対できないと思った。

福岡PHPカンファレンスの開催日時が気になります。(北海道も気になる)

懇親会

PHPのuzullaさんの懇親会LTが面白すぎたw

f:id:gurimmer:20151010232601j:plain

あと、strlenとかchownの関数やコマンドの発音問題も面白かった。Rasmusさんに直接聞いて答えを教えていただいたり、とても貴重だ。

PHPカンファレンスTシャツ着てた仲間でuzullaさんのブログデビューしましたw

uzulla.hateblo.jp

nekogetさんに撮っていただいてたw

f:id:gurimmer:20151012222607j:plain

二次会

東北から関西に方々と色々お話出来て楽しかった!

東北の人なのに九州のこと色々知っててびっくりしたw

まとめ

PHPカンファレンスとても楽しかった!!!!

こういうイベントに参加すると、知らないことがたくさんあること、頑張ってる人たくさんいることをあらためて気づかせてくれるのでいいなと思う。とても良い刺激になりモチベーションが上がります。

運営スタッフの皆様、登壇者の方々、本当にお疲れさまでした。ありがとうございました。

来年は登壇できるように頑張ろう。

Creative Leadership Camp in Koriyamaに行ってきた話

弊社社員で3月6日〜7日の1泊2日で、「Creative Leadership Camp in Koriyama」と題して、弊社社員12名で行ってきました。

co-ba koriyama

そもそも、当日まで何をするのか詳しく聞かされないまま福島県郡山市まで移動です。 合宿の場所は「co-ba koriyama」というコワーキングスペースを借りて行いました

http://tsukuruba.com/co-ba/koriyama/

f:id:gurimmer:20150329183649j:plain

SAFECASTとKAOSPILOT

今回の合宿は、SAFECASTのJoeさんとKAOSPILOTの大本綾さんのコラボで実現しました。 Safecastについてはこちらを読んでもらえればわかると思います。

blog.safecast.org

綾さんについてもこちらを読んでもらえればKAOSPILOTについてもわかるかと

markezine.jp

Joeさんと綾さん f:id:gurimmer:20150329185520j:plain

内容

2グループに分かれて、1グループは「Safecastのガイガーカウンターを制作する」が課題で、もう1グループは「そのガイガーカウンターをみんなに使ってもらうためには?」という課題に取り組みました。

僕は後者のグループで、2チーム(3名ずつ)に分かれて、「約1日半自由です。どうぞ」という感じでスタートw ちょっとポカーンという感じでした。

ガイガーカウンターを作ってる様子 f:id:gurimmer:20150329185625j:plain

Safecastの車(コレにガイガーカウンターを付けて計測してる) f:id:gurimmer:20150329185734j:plain

みんなでミーティングしてる様子 f:id:gurimmer:20150329185643j:plain

結果

正直なところ、考えもちゃんとまとまらないまま時間が来てしまった感じ。。。

初日は、とりあえずコンセプトだったり・ルール、方向性を決めて終わった感じでした。 二日目は、現地の人に聞かないとわからないという結論にいたりました。 外に出てコンビニの店員さんにインタビューしたり、ラジオ局に行ってみたり、タクシーの運転手に聴いてみたりしました。

これは公園にあった、除染の看板。公園にはガイガーカウンターがほぼ必ずついているのがビックリ f:id:gurimmer:20150329231742j:plain

インタビューして収集できたことを、綾さんから「共感map」というものに書いてまとめると考えがまとまると言われて試してみました。

f:id:gurimmer:20150329190536j:plain

共感mapとは、真ん中に人がいて、

  • 頭のところに「何を考えているのか」「何を感じているのか」を書く
  • 耳のところに「何を聞いているのか」を書く
  • 口のところに「何を言っているのか」「どんな行動しているのか」を書く
  • 目のところに「何を見ているのか」を書く
  • 左下に「邪魔になるもの」「壁になるもの」を書く
  • 右下に「得られるもの」「協力できること」を書く

これを書くことによって、何をすべきなのかがわかってギリギリ発表できたという感じ。

ちなみに、出来上がったガイガーカウンターはこちら f:id:gurimmer:20150329231827j:plain

まとめ

joeさんはほぼ英語だったというのもあって、半分以上言ってることが分からなかったw けど、joeさんがいっていた「簡単に作れるって本当にいいことなのか?」ということを言っていて、自分の中にない視点みたいなのが発見できてものすごくハッとした。

あと、「デザイン思考」というのが大きなテーマのようになっていたと思うんだけど、これがUXの構造化シナリオ法の考え方と似てるんじゃないかなと思った。 顧客ありきで、その顧客が抱えている根本的な問題を聞きだす・発見して、それをちゃんとデザインに落としこむみたいなフローという感じがどちらもした。 といっても簡単にできることじゃない。けど練習すればできるよとKAOSPILOTの人たちはいいたいんだろうなと。

もっとUXとかデザイン思考について体験したいなと思えた2日間でした。

photo credit: Café au lait via photopin (license)