koukiblog

たぶんweb系の話題

docker toolboxで作成したdocker0のアドレス帯の変更

デフォルトでは 172.17.0.0/16 が使用されているが、社内ネットワークの都合でこれを変更したかったのでその方法です。

docker toolboxを利用する場合、docker-machineを利用して、boot2dockerのVMが起動していると思います。

docker-machine ssh

でdocker daemonが起動しているサーバにログインして以下の作業を行います。

まずdockerを停止

docker stop

既存のbridgeを削除します。

ifconfig docker0

で、docker0インタフェースに172.17.0.0/16が設定されていることがわかります。
この設定を残したまま、dockerの起動時オプションに別にアドレス帯を指定してもエラーになってしまうので、まず削除します。

ip link del docker0 

で削除。

あとはdocker daemonをbipオプションを渡して起動する。

あまりよい方法ではないですが、
boot2docker/docker at master · boot2docker/boot2docker · GitHub

    /usr/local/bin/docker daemon -D -g "$DOCKER_DIR" -H unix:// $DOCKER_HOST $EXTRA_ARGS >> "$DOCKER_LOGFILE" 2>&1 &

    /usr/local/bin/docker daemon -D --bip=192.168.42.1/24 -g "$DOCKER_DIR" -H unix:// $DOCKER_HOST $EXTRA_ARGS >> "$DOCKER_LOGFILE" 2>&1 &

として、dockerを起動すると、 192.168.42.1/24 が適用されます。

ifconfig docker0

で結果が確認可能

参考:
Dockerのネットワークの基礎 | SOTA

mackerel-agentをchefでamazon-linuxにインストールするとエラーになった件

公式の手順にあるようにchefのcookbookを利用してインストールすると、サービスの起動時にエラーになる。

mackerel.io

エラーの内容は以下

Starting mackerel-agent:/etc/init.d/mackerel-agent: 36 :  7214 Trace/breakpoint trap   $BIN ${APIBASE:+--apibase=$APIBASE} ${APIKEY:+--apikey=$APIKEY} --pidfile=$PIDFILE --root=$ROOT $OTHER_OPTS >> $LOGFILE 2>&1

結論:
最新版(v1.2.1)を利用する

cookbook 'mackerel-agent', tag: 'v1.1.0'

ではなく

cookbook 'mackerel-agent', tag: 'v1.2.1'

以下調べた履歴

mackerel.io
で、Amazon Linux AMI 2016.03での不具合が報告されているものの、修正済みのはず。

試しにchefではなく公式の手順で手動でインストールしてみると問題なくインストールできる。
手順は下記の内容
mackerel.io

この手順で入るってことは、何か別のものをインストールしているのかも?ってことで公式の手順にある "https://mackerel.io/file/script/amznlinux/setup-yum.sh"の内容を確認してみると

# import GPG key
  gpgkey_path=`mktemp`
  curl -fsS -o $gpgkey_path https://mackerel.io/assets/files/GPG-KEY-mackerel
  rpm --import $gpgkey_path
  rm $gpgkey_path

  # add config for mackerel yum repos
  rpm -ivh http://yum.mackerel.io/amznlinux/latest/x86_64/mackerel-repo-1-0.noarch.rpm

となっていて、amazon linux用のレポジトリを用意している様子。
一方chefのrecipeは

if platform?('centos') or platform?('redhat') or platform?('amazon')
~
yum_repository "mackerel" do
    gpgkey gpgkey_url if yum_cookbook_ver >= Gem::Version.new('3.0.0')
    description "mackerel-agent monitoring"
    url "http://yum.mackerel.io/centos/$basearch"
    action :add
  end

となっていて、これが原因だった。

"http://yum.mackerel.io/centos/$basearch"を"http://yum.mackerel.io/amznlinux/$releasever/$basearch"に書き換えたところ問題なく動作した。

そしてここまでたどり着いたところで、最新のレポジトリでは対応済みなことに気づいた。。
Support Amazon Linux repo · mackerelio/cookbook-mackerel-agent@499d779 · GitHub

PhoenixでHTTP Headerを利用した認証を行う

PhoenixAPIを実装するときに、http headerを利用して簡易な認証を行う時の方法。
http headerで認証を行うplugを作成します。

/web/plugs/api_auth.ex

defmodule App.Plug.APIAuth do
  import Plug.Conn

  def init(default), do: default

  def call(conn, auth_header) do
    {key, value} = auth_header
    header = Enum.find(conn.req_headers, &elem(&1, 0) == key)

    if header && elem(header, 1) == value do
        conn
      else
        send400 conn
    end
  end

  defp send400(conn) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(400, "Bad Request")
    |> halt
  end
end

Phoenixの場合、http headerはconn.req_headersで取得できるのでそれを利用します。
{key,value}のtupleのリストになっています。
Plug.Conn – Plug v1.1.6

作成したPlugをRouterに設定すれば完了です。

web/router.ex

  pipeline :api do
    plug :accepts, ["json"]
    plug App.Plug.APIAuth, {"x-sample-key", "xxxxx"}
  end

環境毎に変更する場合は、configに追記して、Application.get_env で取得すればok

phoenix以外からphoenixのchannelを利用する

Rails + node.js(+socket.io) で作ったアプリの、node.jsの部分をphoenixに置き換えてみたときに必要だった。

phoenixのchannelのclientは
github.com
にあり、このファイルを利用すればよい。
es6で書かれているが、他のファイルに依存はしていないので、babelでコンパイルしたものを配置すればok。
phoenix.jsを読み込んでしまえば、phoenixチュートリアルにあるように

  var phoenix = require("phoenix");

  socket = new phoenix.Socket("/socket", {params: {token: window.userToken}});

のようにして利用できる。

別のwebアプリケーションからphoenixのchannel使いたいケースって結構ありそうだけど、現状はこれしか方法なさそう。
(とはいえ、socket.ioほど分厚い仕様があるわけではないので、client自作してしまうのあり)

ちなみに、他の言語のクライアントは現在以下の3つらしい。

iOS
github.com


Android
github.com

C#
github.com

emberのfastbootについて調べた

前からちょっと気になっていたのだけど、時間がとれたので調べてみました。
↓の動画で説明されてます。
vimeo.com

fastbootとは、emberが用意しているSSR(サーバーサイドレンダリング)の仕組みで、nodejsでemberアプリを動かしている。

つまり、サーバサイドアプリとemberアプリはそれぞれ独立しているのが前提。
たとえば、Railsでwebアプリを作っていた場合、fastbootを利用するには、unicornなどのアプリケーションサーバとは別にnodejsが必要です。

jQueryなど、ブラウザのAPIに依存した処理があると、当然エラーになります。($.ajax('xxxx')みたいな)
ember-networkのようにemberがplolyfilを用意していて、ここは開発者が気を付けて開発する必要あり。

あと、クライアントMVCSSRで気になるのは、DOMの生成ですが、
emberの場合は、htmlbarsというemberで利用しているテンプレートエンジンがhtmlのサブセットを理解できて、それを利用しています。
HTMLBars: The Next-Generation of Templating in Ember.js

手元で確認してみたところ

<div>
<span>welcome to ember</span>
<div>
  {{outlet}}
</div>

がfastboot用にビルドを行ったところ

    buildFragment: function buildFragment(dom) {
        var el0 = dom.createDocumentFragment();
        var el1 = dom.createElement("div");
        var el2 = dom.createTextNode("\n");
        dom.appendChild(el1, el2);
        var el2 = dom.createElement("span");
        var el3 = dom.createTextNode("welcome to ember");
        dom.appendChild(el2, el3);
        dom.appendChild(el1, el2);
        var el2 = dom.createTextNode("\n");
        dom.appendChild(el1, el2);
        var el2 = dom.createElement("div");
        var el3 = dom.createTextNode("\n  ");
        dom.appendChild(el2, el3);
        var el3 = dom.createComment("");
        dom.appendChild(el2, el3);
        var el3 = dom.createTextNode("\n");
        dom.appendChild(el2, el3);
        dom.appendChild(el1, el2);
        var el2 = dom.createTextNode("\n\n");
         dom.appendChild(el1, el2);
        dom.appendChild(el0, el1);
        var el1 = dom.createTextNode("\n");
        dom.appendChild(el0, el1);
        return el0;
      }

に変換されていました。
ReactとJSXの関係に近いですね。JSXは結構制約が強いイメージだったけど、htmlbarsは自由度高そうな雰囲気。

webのフロントエンドは日々複雑になっているし、スマホアプリにAPIを提供するみたいに、サーバーサイドアプリはAPIに徹して、webのフロントエンドはクライアントMVC(SSR出来ればなおよし)で構築するのもよいんじゃないかなという気もしてきました。

emberそんなに悪くないと思うんだけど、日本語リソースが少ないのが気になるところ。
複雑なwebアプリ作るときには結構現実的な選択肢だと思うんですけどね。

Railsのforce_sslの判定方法

Railsのforce_sslがどうやってSSLかどうか見分けてるかわからなくて調べたときのメモ

https://github.com/rails/rails/blob/3d70f0740b26b0a137d7e6436f9909330f8ee888/actionpack/lib/action_controller/metal/force_ssl.rb#L76
ここまでは簡単に辿り着けるんだけど、このrequest.ssl? が何をしているのかがわからない。。


Diving in Rails - The request handling
rails request soruceとかでぐぐって見つけた記事を読むと
requestが、ActionDispatch::Requestなことがわかる

rails/request.rb at e595d91ac2c07371b441f8b04781e7c03ac44135 · rails/rails · GitHub
ActionDispatch::Reqeustを見てみる。けど、ここにもssl? はいない。けどRack::Requestを継承していることがわかる。

rack/request.rb at master · rack/rack · GitHub
Rack::Requestを見てみると、やっとssl? を発見。

if @env[HTTPS] == 'on'
'https'
elsif @env[HTTP_X_FORWARDED_SSL] == 'on'
'https'
elsif @env[HTTP_X_FORWARDED_SCHEME]
@env[HTTP_X_FORWARDED_SCHEME]
elsif @env[HTTP_X_FORWARDED_PROTO]
@env[HTTP_X_FORWARDED_PROTO].split(',')[0]
else
@env["rack.url_scheme"]
end

最終的な条件式はこれでした

Controllerから参照できるrequestは、AcitonDispatch::RequestでそれはRack::Requestを継承してるって知ってれば一瞬で解決できるんだろうけど、なかなか難しい