作業ログ7/23

リッチスニペットをJSON-LD形式で追加しています。

ところで、JavaScriptで動的にHTMLを変更した場合のgoogleのインデキシングについて調べている方がいました。
http://www.centrical.com/test/google-json-ld-and-javascript-crawling-and-indexing-test.html
新しい記事なので、現状を表していると思ってよいと思います。
結果は、JSON-LDの追加以外はインデックスされ検索結果に反映されたとのことです。

多言語化をJavaScriptでやっているので動的にできればよかったのですが、ちょっと自分でも試してみようと思います。

作業ログ7/20〜7/21

しばらくシステム開発から離れていたので、最近は新しいトレンドを探っています。

Dockerについて

ちょうど一年前にDockerとElastic Beanstalkを使ってWebアプリを作ったのですが、
GoogleやAWSがコンテナサービスを始めていたりDocker関連のサービスが増えているようでした。興味はあるのですが、今は次の内容に取り組んでいます。

サーバーを使わないウェブシステム構築について

AWSのサービス一覧を見ていて、Lambdaというのが気になりました。
Lambdaはイベントをトリガーにして、プログラムコードを実行できるサービスです。最初はS3への記事のアップロードをイベントソースにして何かをやろうかと思いましたが、サンプルをいじっているうちに、サーバーを使わないシステム構成の可能性に気が付きました。AWSで言えばEC2を使わないということですが、Lambdaをアプリケーションサーバーと捉えて、S3上のJSからKinesisなどを経由すればアプリが作れるのではないかと考えました。
EC2を使わないメリットは、まずはLinux管理者としての能力が不要になることだと思います。EC2を使う時点でOSの管理がつきまといます。
あとはコスト的なメリットで、時間単位の課金から解放されます。規模の小さいサービスであれば、時間単位で課金されるよりは実行回数単位で課金のほうが安くすむのではないでしょうか。
それで、今日はhello worldを作ろうかと思っていたのですが、AWSのサービス一覧を見ていて、また気になるサービスがありました。
API Gatewayというサービスなのですが、これはまさにやってみたかったことに適しているサービスでした。例えば、RESTでLambda関数を呼び出してレスポンスを取得することができます。
それで今、VPS上で動いているWebアプリを次の構成に移行しようとしています。あえてWebサーバー、アプリサーバー、DBサーバーという言葉を使うとすると、次のような対応になります。

  • Webサーバー:Amazon S3
  • アプリサーバー:Amazon API Gateway(Lambda関数実行)
  • DBサーバー:Amazon DynamoDB

そしてEC2は使いません。
これができればかなり楽な気がしますが、まずはやってみようと思います。

作業ログ7/17

7/15、16は一区切りしたら別途書こうと思います。(AWS Lambdaをいじっています。)
今日は過去のサービスを停止させると共に、色々やりました。

サーバーの整理

  • ソースコードをリポジトリへ保存
    Web画面のコードは既に保存されていましたが、バックエンドで動いていたシェルや、管理用のプログラムなどを保存しました。
  • 不要ジョブの停止
    世代管理しないデイリーのDBバックアップジョブが4年間動き続けてファイル数がすごいことに(@_@
  • Web、アプリ、DBサーバー解約
    たいして使われてないのにクラスタ構成にして毎月1万円近く払っていましたが、1台を残して解約しました。

教訓

  • 作ったものは残さず保存しておく
    管理用のちょっとしたシェルやジョブ設定など忘れがちです。
    Dockerでコンテナ化するのはいいですが、後でこの時どうしていたかちょっと見たいという時に、コンテナを起動して確認するのも面倒な気がするので、オンラインのリポジトリに保存してブラウザで手軽に確認できるようにしておきたいです。
  • 監視する
    ジョブ結果、リソース監視、サービス監視などやっていましたが、やっていない部分もあったので、一つ残らず監視するようにします。
  • クラウドサービスを使う
    VPSなどで何もかも自分でシステムを構築するのは難しいですし、構築した後が大変です。
    スケール、セキュリティ、個々のミドルウェアについてのバージョンアップなどの事は考えたくないです。
    クラウドに任せられる部分は任せていくようにします。

作業ログ7/13〜7/14

EC2のインスタンスを作ろうと思います。
調べているとEC2 Container ServiceというEC2のDockerに特化したサービスがあるようで興味を引かれましたが、
今は通常のEC2インスタンスを作成します。

インスタンスタイプの選択

t2.microを選択しました。
サービスイン直後からある程度多いアクセスが見込めているサービスを稼働させるのであれば話は変わってくると思いますが、まずは最も安いインスタンスタイプでよいと思います。
東京リージョンで1時間単位の課金だと1か月1700円程度でした。
毎月支払いの1年利用だと1300円ぐらいです。

EBSの選択

t2.microはインスタンスストレージを持たないのでEBSが必要です。
EBSには以下の種類があります。

  • 汎用(SSD)
  • プロビジョンドIOPS(SSD)
  • マグネティック

1GB当たりの価格はプロビジョンドIOPS > 汎用 > マグネティックの順で高いです。
マグネティックというのはいわゆるHDD(磁気ディスク)のことです。
デフォルトの選択値は汎用(SSD)なので、より多くのケースに対応できるのがこれなのだと思います。

EBSの料金表
http://aws.amazon.com/jp/ebs/pricing/

単純に、料金が高い=パフォーマンスが高い、かというとそうでもないようでした。
汎用SSDにはベースラインパフォーマンスというのが設定されており、3IOPS/1GiBとなっています。
これはつまり、EBS作成時に確保したサイズが大きい程パフォーマンスが高いということです。
また、「ベースライン」という通り、3IOPS/1GiBは最低限のパフォーマンスであり、
ベースラインを超えるI/Oが必要になった場合は一時的にバースト(最大3000IOPSまでパフォーマンスが上昇する)します。
もちろん必要な時にいつもバーストできるわけではなく、バーストするには「I/Oクレジット」という時間経過と共に自動的に取得するポイントのようなものを消費する必要があります。
1秒当たりに取得されるI/Oクレジットの量はEBSのサイズが大きい程多くなります。
I/Oクレジットが無くなってしまえばバーストできないので、ベースラインパフォーマンスのIOPSで動作することになります。
ですので、確保するサイズが極端に小さい場合のパフォーマンスは、おそらくマグネティック > 汎用SSDになるのではないかと思います。(試したわけではありません。)
今回は小さいサイズ(8GiB)での使用を考えているのでマグネティックを選択しました。

作成したインスタンスにローカルから接続する

インスタンス作成時に秘密鍵をダウンロードできるので、sshクライアントから接続します。

$ ssh -i 秘密鍵 ec2-user@ホスト
CPU情報

ところで2年くらい前、インスタンスを再起動するたびにcpuinfoが変わっていたのですが、
今awsのサイトを見るとt2インスタンス以外は具体的なCPU名が書いてあるので、
再起動してcpuが変わるということは無くなったのかもしれません。

$ cat /proc/cpuinfo

processor    : 0
vendor_id    : GenuineIntel
cpu family    : 6
model        : 63
model name    : Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz
stepping    : 2
microcode    : 0x25
cpu MHz        : 2394.538
cache size    : 30720 KB
physical id    : 0
siblings    : 1
core id        : 0
cpu cores    : 1
apicid        : 0
initial apicid    : 0
fpu        : yes
fpu_exception    : yes
cpuid level    : 13
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm constant_tsc rep_good nopl xtopology eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm xsaveopt fsgsbase bmi1 avx2 smep bmi2 erms invpcid
bogomips    : 4789.07
clflush size    : 64
cache_alignment    : 64
address sizes    : 46 bits physical, 48 bits virtual
power management:
メモリ情報
$ cat /proc/meminfo
MemTotal:        1020188 kB
MemFree:          537064 kB
MemAvailable:     863980 kB
Buffers:           11932 kB
Cached:           415660 kB
SwapCached:            0 kB
Active:           292848 kB
Inactive:         146492 kB
Active(anon):      11748 kB
Inactive(anon):       52 kB
Active(file):     281100 kB
Inactive(file):   146440 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:         11764 kB
Mapped:             6576 kB
Shmem:                60 kB
Slab:              31264 kB
SReclaimable:      23996 kB
SUnreclaim:         7268 kB
KernelStack:         616 kB
PageTables:         1876 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      510092 kB
Committed_AS:      54704 kB
VmallocTotal:   34359738367 kB
VmallocUsed:        2448 kB
VmallocChunk:   34359732739 kB
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        8192 kB
DirectMap2M:     1040384 kB

お金がもったいないのでインスタンスをシャットダウンしておきます。

$ sudo shutdown -h now

作業ログ7/8〜7/10

旧ブログデータの移行

前のブログもMarkdown形式だったので単純にコピーしました。
ヘッダ情報(tags、id)は新たに作成。

Hexo RSSプラグインインストール

デフォルトだとRSSマークがリンク切れになるのでプラグインをいれました。
https://github.com/hexojs/hexo-generator-feed

Hexoのコンフィグを変更(_config.yml)

デフォルトから変更した部分

  • permalink: ‘:year/:month/:id/‘
    パーマリンクを年/月/記事ID形式にした
  • language: ja
    言語設定を日本語にした
  • timezone: Asia/Tokyo
    タイムゾーンを日本にた
  • url: http://blog.douzen.net/
    このブログのURLを設定
  • author: ‘dozen yutaro’
    著者を設定
  • new_post_name: ‘:year/:month/:day-:title.md’
    新規記事ファイルのファイルを年/月ディレクトリ以下に作成してファイル名を日付-タイトル名に設定

旧URLを新URLにリダイレクトする設定

検索してみると、旧ブログの記事にリンクしてくれている方がいて、当然リンク切れになっていました・・・。
mod_rewriteで対応しました。

次にやりたい事

  • サーバーの整理と移行
    さくらのVPSを2011年頃から使っていますが、色んなプログラムが置いてあって把握できていません。
    今までは自作ウェブサービスはEC2で動かして、ちょっとした内容のものはVPSで動かしていたのですが、
    全てEC2にしたいです。
    アクセスが増えてきてもすぐ対応したいので。

  • サービス監視
    今でもhttpdのログを見るとnagiosが動いているようなんですが、何をしているか忘れたのでちゃんとしたいです。
    現状ちょっとしたサービスは無監視で、googleのウェブマスターツールから警告がきて気付くというレベルなので、
    すべてCloudWatchにしようと思います。

ブログを移行しています

以前、とあるVPSサービスとLokkaというRuby製のCMSを使用してブログを書いていたのですが、
そのVPSサービスがサービスを終了してしまい、そのまま移行しないでいました。
それで今、新しいブログを作ろうとしているのですが、次のような条件で考えています。

  • 静的コンテンツにする。
  • 記事のソースはソース管理のサービスに保存する。
  • 信頼性のあるホストを使う。

それで現状は以下の構成になっています。

  • ホスト:Amazon S3
  • 静的サイトジェネレーター:Hexo
  • デプロイ:gulp

後日これらの詳細や作業ログなどを書こうと思います。
ソース管理との連携は考え中です。

Backbone.js サンプルアプリケーション #4

はじめに

前回の続きです。


var AppView = Backbone.View.extend({
    el: $("#todoapp"),

    statsTemplate: _.template($('#stats-template').html()),

    events: {
        "keypress #new-todo": "createOnEnter",
        "click #clear-completed": "clearCompleted",
        "click #toggle-all": "toggleAllComplete"
    },

    initialize: function(){
        this.input = this.$("#new-todo");
        this.allCheckbox = this.$("#toggle-all")[0];

        this.listenTo(Todos, 'add', this.addOne);
        this.listenTo(Todos, 'reset', this.addAll);
        this.listenTo(Todos, 'all', this.render);

        this.footer = this.$('footer');
        this.main = $('#main')

        Todos.fetch();
    },

    render: function(){
        var done = Todos.done().length;
        var remaining = Todos.remaining().length;

        if(Todos.length){
            this.main.show();
            this.footer.show();
            this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
        }
        else{
            this.main.hide();
            this.footer.hide();
        }

        this.allCheckbox.checked = !remaining;
    },

    addOne: function(todo){
        var view = new TodoView({mode: todo});
        this.$("#todo-list").append(view.render().el);
    },

    addAll: function(){
        Todos.each(this.addOne, this);
    },

    createOnEnter: function(e){
        if(e.keyCode != 13) return;
        if(!this.input.val()) return;

        Todos.create({title: this.input.val()});
        this.input.val('');
    },

    clearCompleted: function(){
        _.invoke(Todos.done(), 'destroy');
        return false;
    },

    toggleAllComplete: function(){
        var done = this.allCheckbox.checked;
        Todos.each(function(todo){
            todo.save({'done':done});
        });
    }
});

var App = new AppView;

コード解説


el: $(“#todoapp”),

elにJQueryオブジェクトを指定しています。前回のliタグのViewクラスと違い、既に存在しているDOMノードを指定しています。


statsTemplate: _.template($(‘#stats-template’).html()),

Underscore.jsのtemplateメソッドです。scriptタグ内に記述してあるHTML文字列をテンプレートとして利用します。


events: {
“keypress #new-todo”: “createOnEnter”,
“click #clear-completed”: “clearCompleted”,
“click #toggle-all”: “toggleAllComplete”
},

イベント定義です。


initialize: function(){
this.input = this.$(“#new-todo”);
this.allCheckbox = this.$(“#toggle-all”)[0];

    this.listenTo(Todos, 'add', this.addOne);
    this.listenTo(Todos, 'reset', this.addAll);
    this.listenTo(Todos, 'all', this.render);

    this.footer = this.$('footer');
    this.main = $('#main')

    Todos.fetch();
},


このビューの初期化処理です。

TodoのコレクションであるTodosのイベントをサブスクリプションします。Todos.fetch();が実行されるとTodosにTodoが格納されてresetイベントが発火しaddAllがコールされて全てのTodoが描画されます。


render: function(){
var done = Todos.done().length;
var remaining = Todos.remaining().length;

    if(Todos.length){
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
    }
    else{
        this.main.hide();
        this.footer.hide();
    }

    this.allCheckbox.checked = !remaining;
},


描画メソッドです。Todosの状態によってUIを制御します。


addOne: function(todo){
var view = new TodoView({mode: todo});
this.$(“#todo-list”).append(view.render().el);
},

addAll: function(){
    Todos.each(this.addOne, this);
},

createOnEnter: function(e){
    if(e.keyCode != 13) return;
    if(!this.input.val()) return;

    Todos.create({title: this.input.val()});
    this.input.val('');
},

clearCompleted: function(){
    _.invoke(Todos.done(), 'destroy');
    return false;
},

toggleAllComplete: function(){
    var done = this.allCheckbox.checked;
    Todos.each(function(todo){
        todo.save({'done':done});
    });
}


各イベントから呼ばれる処理群です。内容は見ればなんとなく分かると思います。

以上でTodosのコードは終わりです。起点となるビュー(AppView)があり、そこからビュー、コレクション、モデルを利用している構成であることが分かります。
次はrouterやmodelをデータストアと同期するBackbone.sync()について見ていこうと思います。

Backbone.js サンプルアプリケーション #3

はじめに

前回の続きです。

1つのTodoを表すViewのコードです。


var TodoView = Backbone.View.extend({
    tagName: "li",

    template: _.template($('#item-template').html()),

    events: {
        "click .toggle": "toggleDone",
        "dblclick .view": "edit",
        "click a.destroy": "clear",
        "keypress .edit": "updateOnEnter",
        "blur .edit": "close"
    },

    initialize: function(){
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
    },

    render: function(){
        this.$el.html(this.template(this.model.toJSON()));
    },

    toggleDone: function(){
        this.model.toggle();
    },

    edit: function(){
        this.$el.addClass("editing");
        this.input.focus();
    },

    close: function(){
        var value = this.input.val();
        if(!value){
            this.clear();
        }
        else{
            this.model.save({title: value});
            this.$el.removeClass("editing");
        }
    },

    updateOnEnter: function(e){
        if(e.keyCode == 13) this.close();
    },

    clear: function(){
        this.model.destroy();
    }
});

コード解説


tagName: “li”,

これがliタグのビューであることを宣言します。この時点では画面上にこのビューによるliタグは存在しません。インスタンス化してrender結果を画面のDOMツリーに追加することで描画されます。


template: _.template($(‘#item-template’).html()),

Underscore.jsのtemplateメソッドです。scriptタグ内に記述してあるHTML文字列をテンプレートとして利用します。


events: {
“click .toggle”: “toggleDone”,
“dblclick .view”: “edit”,
“click a.destroy”: “clear”,
“keypress .edit”: “updateOnEnter”,
“blur .edit”: “close”
},

イベント定義です。


initialize: function(){
this.listenTo(this.model, ‘change’, this.render);
this.listenTo(this.model, ‘destroy’, this.remove);
},

モデルのイベントをサブスクリプションします。モデルが変更されれば、renderメソッドが実行されます。


render: function(){
this.$el.html(this.template(this.model.toJSON()));
},

テンプレートの引数にモデルを与えてHTMLをメモリ上に構築します。


toggleDone: function(){
this.model.toggle();
},

このビューに関連付いているモデルのtoggleメソッドを呼ぶ。


edit: function(){
this.$el.addClass(“editing”);
this.input.focus();
},

editingクラスを追加してフォーカスを当てる。(Todoを編集状態にする。)


close: function(){
var value = this.input.val();
if(!value){
this.clear();
}
else{
this.model.save({title: value});
this.$el.removeClass(“editing”);
}
},

テキストボックスに入力がなければclearメソッドを呼ぶ。(モデルは削除されます。)入力があれば、モデルを保存し、編集状態を抜けます。


updateOnEnter: function(e){
if(e.keyCode == 13) this.close();
},

Enterキーが押されたとき、closeメソッドを呼びます。


clear: function(){
this.model.destroy();
}

このビューに関連付いているモデルを削除します。

次は今まで定義したモデル、コレクション、ビューを使用してアプリケーションを構築します。

Backbone.js サンプルアプリケーション #2

はじめに

前回の続きです。

Todoモデルを管理するためのCollectionのコードです。

このコレクションはローカルストレージを使用して永続化します。


    model: Todo,

    localStorage: new Backbone.LocalStorage("todo-backbone"),

    done: function(){
        return this.where({done: true});
    },

    remaining: function(){
        return this.without.apply(this, this.done());
    },

    nextOrder: function(){
        if(!this.length) return 1;
        return this.last().get('order') + 1;
    },

    comparator: 'order'

コード解説


model: Todo

これがTodoモデルのコレクションであることを宣言しています。


localStorage: new Backbone.LocalStorage(“todos-backbone”)

Backbone.LocalStorageは、Backbone.jsのローカルストレージのアダプタです。

Todoをtodos-backbone名前空間に保存することを宣言しています。


done: function(){
return this.where({done: true});
}

完了したTodo(done=true)の一覧を取得します。


remaining: function(){
return this.without.apply(this, this.done());
},

完了していないTodoの一覧を取得します。


nextOrder: function(){
if(!this.length) return 1;
return this.last().get(‘order’) + 1;
}

Todoの順序性を保つために、連番を取得します。1つもTodoが無ければ1を返し、それ以外は最後のTodoの番号+1を返します。


comparator: ‘order’

モデルがorder属性の順にソートされるように指定しています。

次はViewを見ていきます。

Backbone.js サンプルアプリケーション #1

はじめに

Backbone.jsのサンプルアプリケーションのソースを理解していきます。

まずはModelです。


var Todo = Backbone.Model.extend({
    defaults: function(){
        return{
            title: "empty todo...",
            order: Todos.nextOrder(),
            done: false
        };
    },

    toggle: function(){
        this.save({done: !this.get("done")});
    }
});

コード解説

Todoモデルは3つの属性を持っています。(title、order、done)

defaultsを関数にしているのは、Todoを生成する度に新しいオブジェクトを返すようにするためです。defaultsにオブジェクトを設定した場合は、defaultsが適用された全てのTodoでオブジェクトが共有されます。

toggleはdone属性のtrue/falseを切り替えます。

次はTodoのコレクションを見ていきます。