AWSのCloudFrontからコンテンツをHTTP2 & gzipで高速に配信する

こんにちは、高橋です。

みなさん快適で楽しいAWSライフを送れていますか?

 

f:id:cluex-developers:20161105012605p:plain

   

実は2ヶ月ほど前、AWSからCloudFrontでHTTP2によるコンテンツの配信ができるようになったので早速導入して見ました。

Amazon CloudFront now supports HTTP/2

S3のバケットにあるCSSやJS、画像などのアセットをCloudFrontから配信するというパターンは AWSでインフラを構築するサービスではよくあると思います。

今回はこういったS3上にあるアセットをHTTP2とgzipを使用してCloudFrontから配信するための設定方法などを見ていきたいと思います。

設定そのものは3分くらいで終わります。

CloudFrontにHTTP2を適用する

先ずはAWSのコンソールを開き、CloudFrontを選択します。 f:id:cluex-developers:20161105002030p:plain

リンクまたはチェックボックスを押して「Distribution Settings」からディストリビューションの設定画面に飛びます。 f:id:cluex-developers:20161105002453p:plain

「General」タブの「Edit」ボタンから基本設定の変更ページへ飛びます。 f:id:cluex-developers:20161105011519p:plain

「Supported HTTP Versions」をHTTP2が含まれているものを選択して、右下の「Yes, Edit」ボタンを押します。 f:id:cluex-developers:20161105002830p:plain

ちなみに「Supported HTTP Versions」の右にある黒いボタンを押すと、下記のような説明が出てきます。

For viewer requests, choose the versions of the HTTP protocol that you want CloudFront to accept. In general, HTTP/2 is faster. CloudFront always forwards requests to the origin using HTTP/1.1.

日本語にすると

ユーザーからCloudFrontへのリクエストの際に使用するHTTPプロトコルを選択してください。一般的にHTTP2は速いっすよ。CloudFrontからオリジンへのリクエストはHTTP 1.1だけ使うでー。

といった感じでしょうか。

ユーザーからのリクエストはHTTP2を使用できますが、CloudFront内部でのリクエストではHTTP2はまだ未対応のようです。

   

とはいえ、ひとまずこれで設定完了です!楽勝ですね!

 

あとは設定が完了するまで時間を潰して、 設定が完了したらブラウザから確認できればOK!

Chromeなどのブラウザのデベロッパーツールから見てみると..... f:id:cluex-developers:20161105003848p:plain

レスポンスは304になっていますが、「Protocol」のところが「h2」になっていれば設定完了です!

   

CloudFrontでgzip圧縮による配信をする

では次にファイルをgzip圧縮して配信する設定を見ていきましょう。

   

先ほどのディストリビューションの設定画面に飛びます。 f:id:cluex-developers:20161105002453p:plain

今回は「Behaviors」タブを選択して、gzip配信にしたいものにチェックボックスを入れて「Edit」を押します。 f:id:cluex-developers:20161105102550p:plain

下にスクロールしていくと、一番下に「Compress Objects Automatically」という設定項目があるので、「Yes」を選択してから右下の「Yes, Edit」を押して設定完了!

f:id:cluex-developers:20161105013123p:plain

ちなみに「Compress Objects Automatically」の右にある黒いボタンを押すと、下記のような説明が出てきます。

Select whether you want CloudFront to automatically compress content for web requests that include Accept-Encoding: gzip in the request header. CloudFront compresses files of certain types for both Amazon S3 and custom origins.

日本語にすると

わいに頼んでくれたらリクエストヘッダにAccept-Encoding: gzipがあった時はCloudFrontの方でコンテンツの圧縮しとくで。S3とか別のオリジンのファイルでも圧縮できそうなら、わいが勝手にしとくやでー。

といった感じでしょうか。

   

圧縮倍率はCloudFrontの方でよしなに決めてくれるようです。

   

ということで設定が終わったのでブラウザから確認して見ましょう!

先ほどと同様にChromeなどでデベロッパーツールを開き、CloudFrontから配信されているファイルのレスポンスヘッダを確認します。

f:id:cluex-developers:20161105005957p:plain

「content-encoding: gzip」となっていますね!

これでCloudFrontにHTTP2とgzip配信の設定が完了しました!

手軽にできてすぐに効果が期待できるので、気になった方は試して見てください!

     

We're hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さい!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

Mockを使って、Facebookログイン部分のRSpecを書いてみた!!

こんにちは、エンジニアの神山です。 最近、テストカバレッジを上げるためRSpecを書きまくっています。ちなみに最初は90%でしたが、苦闘の末95%まで上がりました。結構骨が折れましたね。

その中でも大変だったのがFacebookログイン部分のテストです。外部APIを使っており、そこの部分のテストの書き方が分からなくて悩んでいました。 色々と調べてみるとモックを使うとうまいことテスト出来るよという文献を見つけました。

f:id:cluex-developers:20161105203341p:plain

ということで今回は、外部API部分のテストの問題点、モックとは何か、またそれをどのようにテストに使うのかにフォーカスして記事を書きました。

外部API部分のテストの問題点

今回悩んだのは外部APIを使用している部分のテストをどのように書くかということです。

例えば、「ログインしようとしているユーザーのFacebookのアカウント情報を取得し、すでにDBに登録されていればログインさせる」ということです。 しかしこのテストを行うには、実際にFacebookAPIよりアカウント情報を取得しなくてはいけません。

しかしテストで外部APIを利用することには、幾つかのデメリットがあります。

  • 外部APIやその周辺で、予測や対応ができないエラーが起きることがある
  • 外部APIの使用に制限があったり、金銭などが発生する場合がある
  • 外部APIに紐付いているプロダクトにリアルに影響が起こる(Facebookの場合、実際に投稿がされてしまうなど)

では、どうすれば外部APIを利用せずに、外部API部分を使用している部分のテストを出来るのでしょうか。 そのようなときに使うのが、「モック」です。

モックとは何か。

モックを直感的に説明すると、「本物のふりをするニセモノのプログラム」です。下記の記事の言葉を使わせて頂きました。

qiita.com

例えばユーザーのFacebook情報を取得するとき、FacebookAPIにリクエストが発生したら予め用意したニセモノのプログラムを呼ぶようにすることが出来ます。 また「aが呼ばれたときに本当はbを返すのだけど、ここのテストだけはcを返したい」というときに使用したりもします。

つまりモックとは、外部APIなどテストを行うために必要だけれど使用できない、再現することが難しい場合に、その役割を担ってくれるものです。

ちなみにモックとスタブの違いは?

モックと似た言葉にスタブというものがあります。気になって調べてみたのですが、両者の違いは使用目的だそうです。基本的にどちらもニセモノを作り出すことには変わらないようです。

ただその違いを明確に理解するのはなかなか難しく、またあまり意識する必要もないという意見もあったので、今回は保留にしました。時間があるときに色々と調べてみようと思います。

ちなみに、分かりやすく書いてある記事があったので、載せておきます。

uehaj.hatenablog.com

jp.corp-sansan.com

モックの準備

Omniauth部分のテストに関しては以下に従っております。

github.com

まずモックにすべきところを整理しましょう。 今回はユーザーのFacebook情報を取得する際にFacebookAPIを用います。そしてFacebookに登録されている名前やEmailを取得します。 そのため、今回モックにするところはFacebookAPIにリクエストがあったときに返す部分です。 以下をHelperに記します。

def facebook_mock(name, email)
  OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new(
    provider: 'facebook',
    uid: 1234567890,
    info: {
      name: name,
      email: email
    },
    credentials: {
      token: 'hogepiyo1234'
    }
  )
end


OmniAuth.config.test_mode = true

たとえば、facebook_mock('foo', 'bar')とすれば、以下の値が得られます。

{ provider: 'facebook', uid: 1234567890, info: { name: 'foo', email: 'bar' } , credentials: { token: 'hogepiyo1234' } }

またOmniAuth.config.test_mode = trueはOmniauthを用いたテストの際に必要になるので、一緒に記述しておいてください。これを記述すると、FacebookAPIにリクエストが送られそうになると、それを中止してすぐにコールバックしてくれます。

RSpec

では、Facebookログイン部分の一連のテストを書いていきます。

まず、ユーザーを作成します。

let(:user) { User.create(name: 'hoge', email: 'hoge@hoge.com') }

テストの前にOmniAuth.config.mock_auth[:facebook]を初期化し、facebook_mockをセットします。

OmniAuth.config.mock_auth[:facebook] = nil
Rails.application.env_config['omniauth.auth'] = facebook_mock(
  name: user.name,
  email: user.email
)

準備はこれだけです。では実際にFacebookログインをしてみます。今回は分かり易くボタンを押してFacebookログインをする形式にしてみました。

click_link 'Facebookを利用してログインする'

さて、ボタンが押されると通常はFacebookAPIにリクエストが走りますが、テストではすぐにコールバックされます。 ただモックのおかげで、request.env['omniauth.auth']には下記の値が入っております。

{ provider: 'facebook', uid: 1234567890, info: { name: 'hoge', email: 'hoge@hoge.com' } , credentials: { token: 'hogepiyo1234' } }

そのため、request.env['omniauth.auth']['info']name: 'hoge', email: 'hoge@hoge.com'の組み合わせを取得できるようになります。あとはこれをDBと照合して、有効であるかどうかを確かめればテスト終了です。

テスト全体は以下のようになります。

discribe 'login via Facebook' do
  let(:user) { User.create(name: 'hoge', email: 'hoge@hoge.com') }


  before do
    OmniAuth.config.mock_auth[:facebook] = nil
    Rails.application.env_config['omniauth.auth'] = facebook_mock(
      email: user.email,
      name: user.name 
    )
    click_link 'Facebookを利用してログインする'
  end


  it 'should succeed' do
    expect(page.status_code).to eq 200
  end
end

ちなみに検証部分がステータスコードの判別しかないですが、ログインされているかどうかを簡単に確認できる指標が欲しいですね。 例えば、expect(current_user).to eq userのようなものがあれば。。

以上、Mockを使ったRspecの書き方でした。読んで頂きありがとうこざいました。 アドバイスなどありましたら是非お願いします!

We're hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さい!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

参考文献

qiita.com

jp.corp-sansan.com

uehaj.hatenablog.com

github.com

Docker for Macが遅い問題をdocker-syncで解決する

こんにちは。エンジニアの志村です。 Docker for Mac便利ですね!

docs.docker.com

日本語でも様々な記事が出てきています。

私もVagrant + Dockerをメインに使用しておりましたが、ついにDocker for Macに乗り換えました。 弊社では、dev環境をDockerにしているのですが、Vagrant + Dockerの時は快適に開発が出来ました。 ただ、VMを使わなくなった途端にめちゃくちゃ動作が重くなりました。 docker-compose --service-port ●●という感じで、rails serverとwebpack-dev-serverを立ち上げるのですが、ブラウザからの読込が劇的に遅い…。seedデータ突っ込むのもめちゃくちゃ遅い… フォーラムでも話題になっていますね。

forums.docker.com

私の環境でいうと、Vagrant + Dockerの時よりも、体感10倍近く速度が落ちました…。

なぜ遅いのか

forums.docker.com

このDockerの中の人の回答を見ると、osxfsを用いてファイルシステムイベントを検知・監視していることが主な原因っぽいですね。 勿論、その他環境により差異はありますが、ファイルシステムを監視する方法を替えれば解決できそうです。

docker-sync

ファイル同期で速度早いと良く言われているのがrsyncですね。 docker-dev-osxという選択肢もありますが、これはDocker for Mac未対応… どうしようかなーとフォーラムを眺めているとdocker-syncなるものがありました。 フォーラムの中でも評価が良く、結構な人が使ってそうなのでdocker-syncを使用したいと思います。

docker-sync by EugenMayer

docker-syncはRubyベースで書かれています。

docker-syncの使い方

  1. docker-sync, fswatchをインストールします。
$ gem install docker-sync
$ brew install fswatch

ファイルの同期に関してはrsync, unisonが選択出来ます。 rsyncがホスト→ゲストの一方向の通信なのに対し、unisonはNFSのように双方向の通信が可能なようです。 今回はrsyncを使用します。 unisonに関してはまた記事を書ければと思います。

$ brew tap homebrew/dupes
$ brew install rsync

※ schema.rb, Gemfile.lock等、ゲスト(Docker)側で生成されるファイルに関してはdocker cpコマンドを使用するとホスト側に持ってくることが出来ます。

# Docker内のschema.rbをPC側に持ってくる
$ docker cp コンテナ:/var/www/db/schema.rb ./db/schema.rb
  1. docker-syncの設定 docker-syncはymlファイルに設定を記述します。 Railsを触っている身としては普段から使用しているのでありがたいです。

docker-syncはDocker Composeを使用します。 production環境やstaging環境でもDockerを使用している方は、docker-compose.ymlの他に、開発環境用のymlファイルを用意する必要があります。 私は開発環境のみなので、docker-compose.ymlを直接編集してしまいます。

公式のwikiに載っているので参考下さい。

github.com

docker-sync.yml

syncs:
  # docker-syncで使用するvolume名を記載
  web-sync:
    notify_terminal: true
    # ホスト側(PC)のパスを記載 今回はRailsのルートディレクトリを指定
    src: './'
    # ゲスト側(Docker)にマウントするパスを記載 Docker内の/var/wwwにマウントされる
    dest: '/var/www'

    # IPの設定
    sync_host_ip: '127.0.0.1'

    # rsyncのポート設定
    sync_host_port: 10871

    # rsyncさせないファイル類を記載
    sync_excludes: ['Gemfile.lock', 'Gemfile', 'config.rb', '.sass-cache/', 'sass/', 'sass-cache/', 'composer.json' , 'bower.json', 'package.json', 'Gruntfile*', 'bower_components/', 'node_modules/', '.gitignore', '.git/', '*.coffee', '*.scss', '*.sass']

    sync_excludes_type: 'Path'

    sync_args: '-v'

    # ファイル同期方法の選択 rsync or unison
    sync_strategy: 'rsync'

    # ゲスト側でのユーザー名の指定
    sync_user: 'test'

    sync_userid: '5000'

    # ゲスト側でのgroupの指定
    sync_group: 'testgroup'

    sync_groupid: '6000'

    # fswatchで変更を検知させないファイルやディレクトリ sync_excludesとかぶせとけばよいかと
    watch_excludes: ['.*/.git', '.*/node_modules', '.*/bower_components', '.*/sass-cache', '.*/.sass-cache', '.*/.sass-cache', '.coffee', '.scss', '.sass', '.gitignore']

    watch_args: '-v'

    watch_strategy: 'fswatch'   

docker-compose.yml

docker-compose.ymlにはversion1, version2と2種類の記述方法があります。 ここではversion2を使用します。

version: '2'
services:
  database:
    image: postgres:latest
    volumes_from:
      - datastore
    expose:
      - '5432'

  elasticsearch:
    image: elasticsearch:latest
    volumes_from:
      - datastore
    ports:
      - '9200:9200'

  redis:
    image: redis:latest
    ports:
      - '6379:6379'
    volumes_from:
      - datastore

  memcached:
    image: memcached:latest
    ports:
      - '11211:11211'
    volumes_from:
      - datastore

  datastore:
    build: docker/datastore

  web:
    build: .
    command: bash -c 'rm -f tmp/pids/server.pid && bundle exec foreman start && bundle exec sidekiq -C config/sidekiq.yml -L tmp/sidekiq.log -d'
    volumes:
      # 下記で宣言したvolume名を指定
      - web-sync:/var/www:rw
      - /var/www/client/node_modules
    ports:
      - '3000:3000'
      - '8080:8080'
    links:
      - database
      - elasticsearch
      - redis
      - memcached

# docker-syncで使用するvolume ここで宣言した名前がdocker-sync.ymlで使用される
volumes:
  web-sync:
    external: true

このような感じです。 環境によって差異があると思いますので、適宜調整をお願いします。 一番重要なのは、volumesのsync volumeの設定と、アプリケーション(Rails)側のコンテナでvolumesを指定する部分です。

docker-syncの起動

$ docker-sync start

もしくは

$ docker-sync-stack start

で起動します!

docker-sync-stack startの場合は、docker-sync startと同時にdocker-compose upも行われます。 私はpry-byebugを使用したいので、普段docker-compose run --service-port webで起動しているため、ターミナルを一枚docker-sync startに使用し、さらにdocker-compose run --service-port webを起動させるようにしています。

ベンチマークはとっていないのですが、Vagrant + DockerをNFSで同期させたのと同じ、もしくはそれ以上に早くなりました! GUIで様々な設定が出来、VMとかも必要ないのでDocker for Macは便利ですね。 是非docker-syncを使って快適な開発環境を構築してみては如何でしょうか?

We’re hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さいませ!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

今年2回目の開発合宿に行って来ました!@土浦・筑波

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

最近都内は急に肌寒くなってきましたが、みなさんお元気でしょうか?

今回は茨城県の土浦・筑波エリアで開催!

先日、2泊3日で茨城県の土浦・筑波の方に開発合宿に行ってきました!

台風が近づいている中での開催となり、心配な所もありましたが無事に終わったので、 今回はその様子をまとめようと思います。

 

2月の開発合宿に続いて、今年2回目の開催となります。 Cluexでは合宿を利用して 普段なかなか手のつけられない技術的課題や技術的負債への取り組みや、 理念会議といったメンバー全員でじっくり話し合う場を設けるなど といったことをしています。

ちなみに前回(2016年2月)の日光での合宿の様子はこちら cluex-developers.hateblo.jp

古民家ゲストハウス「jicca」

f:id:cluex-developers:20161014105046j:plain

今回の宿泊先には2泊3日の全日貸切で、筑波山の近くにある 茨城県石岡市のゲストハウス「jicca」さんを使わせてもらいました。

jicca-gh.com

ここのゲストハウスはクラウドファンディングを活用して、 もともと実家の母屋だったものをゲストハウスに改装したそうです。

 

ここの近辺は豊かな自然が溢れていて、とても落ち着いている長閑な場所で、

jiccaは最近は雑誌やラジオなどで度々話題になっているそうです。

 

こちらを運営されている比企(ひき)さんという方は、もともと大手企業で活躍された後に 現在はこちらの「jicca」を運営されつつ個人で中小企業診断士として中小企業コンサルをされている方で、とても暖かい方でした。

   

今回の合宿でのTRY

合宿に参加したのはエンジニア4人・ディレクター3人の合計7名。

合宿中はエンジニア陣は2人で1チーム、ディレクター3人で1チームの全部で3チームに別れて目標を立てました。 合宿の目標はそれぞれのチームであらかじめ決めておき、合宿当日はすぐに作業が開始できる様にしておきました。

ディレクターチーム 売上を上げるためのコンテンツマーケティングの運用方針を決める

今後の事業成長戦略を各々が考えて発表

Web appチーム Rspecのテストカバレッジを95%達成

インフラ&ネイティブアプリチーム CoffeeScriptからES2015(ES6)への完全移行

     

開発合宿1日目

台風が近づいてる影響で初日は曇り空。

午前9時、都内某所に集まり出発です!

都内から土浦までは高速道路を使用して、車でおよそ1時間半ほどの距離にあります。

f:id:cluex-developers:20161014110311j:plain

新卒3人組

前日も遅くまでオフィスに残っていたみたいで非常に眠そうですね。

   

土浦までの道中は高速道路も渋滞なく順調に進み、

守谷SAで途中休憩を挟んでほどなくして土浦に到着です。

   

宿に着く前に、激安ジャングルで物資調達です。

車内では眠そうだった彼も元気になったみたいです。

f:id:cluex-developers:20161014111650j:plain

 

そして11時過ぎにjiccaへ到着。

 

到着してからまずは全体で各チームの合宿中の目標を共有し、 終わり次第すぐに各自作業に取り掛かります。

   

ディレクターチームは主に話し合いが多いので1階にて作業。

エンジニアの方は机と椅子が用意されている2階に上がり作業を開始。

 

ディレクターチーム

f:id:cluex-developers:20161014132340j:plain

 

エンジニアチーム

f:id:cluex-developers:20161014132912j:plain

   

   

 

1日目の夜は夕食と温泉を兼ねて、

車で10分ほど移動して「やさと温泉 ゆりの郷」へ行きました。

www.yurinosato.jp

   

この近辺は日帰り温泉が500~1000円くらいで点在しているので、 その中から一番近場の場所を選びました。

  f:id:cluex-developers:20161014133300j:plain

  f:id:cluex-developers:20161014133340j:plain

「jicca」のオーナーの比企さんも一緒に温泉にお供して頂きました。

  f:id:cluex-developers:20161014133657j:plain

     

茨城県は「常陸牛」や「ローズポーク」が有名なようです。

こちらの常陸牛は柔らかくてとても美味しいお肉でした。

f:id:cluex-developers:20161014134306j:plain

f:id:cluex-developers:20161014184004j:plain

     

夕食後

 

本日はアルコールは一切なしで、 夕食を食べた後はすぐ宿に戻って作業再開です。

 

f:id:cluex-developers:20161014184229j:plain

   

ディレクターチームは順調に進んでいるようで、午前12時頃には就寝。

   

エンジニアの方はまだまだ続きます。

f:id:cluex-developers:20161014135638j:plain

f:id:cluex-developers:20161014135809j:plain

午前4時

まだ続きます。

f:id:cluex-developers:20161014170527j:plain

     

初日が終わり、全員が寝静まったのは午前6時頃。

長い長い1日目が終了です。

     

開発合宿2日目

f:id:cluex-developers:20161014141004j:plain

台風は進路を変えていなくなってしまい、 晴れ晴れとした天気になりました。

   

午前9時、起床。

全員で集まり1日のスケジュール確認です。

f:id:cluex-developers:20161014140301j:plain

 

午前中は引き続き各チームごとで作業。

 

昼食は手打ち蕎麦が有名とのことで、宿の近くの蕎麦屋さん「かまたや」へ

創業60年以上の老舗蕎麦屋だそうです。

f:id:cluex-developers:20161014141622j:plain

www.ibarakiguide.jp

   

蕎麦はもちろん、天ぷらがめちゃめちゃ美味しいです。

f:id:cluex-developers:20161014152519j:plain

     

宿に戻り、今回の合宿のもうひとつの一大イベントに入ります。

 

Cluex理念会議

f:id:cluex-developers:20161014142338j:plain

 

2日目は昼食後から6時間ほどに渡り、

ディレクター・エンジニア含めて全員で「理念会議」といったものを行います。

 

前回の合宿に続き、

今回も個人個人の将来・会社の将来、自分がこれからどうしていきたいか・どうなりたいのかといったことを発表して、

それをどうすれば実現できるのかということをみんなで話し合っていきます。

   

・・・5時間経過後

   

f:id:cluex-developers:20161014142542j:plain

 

ということで、写真にはあまり変化がないですが、

一人一人にフォーカスして個人の思想やその背景にある思い、

そしてそれぞれの将来に関してじっくり話し合ってきました。

   

f:id:cluex-developers:20161014171731j:plain

 

f:id:cluex-developers:20161014171853j:plain

   

理念会議が終わった頃には日がすっかり暮れてしまいました。

これからやや遅めの夕食です。

   

2日目の夕食はBBQ!

f:id:cluex-developers:20161014145526j:plain

「jicca」の方で茨城県産の食材をたくさんご用意して頂いて、みんなでBBQです。

今回のBBQに望むためのチーム編成はこちら

     

 

調理部隊

f:id:cluex-developers:20161014143738j:plain

f:id:cluex-developers:20161014144102j:plain

     

バーベキュー屋の店員

火起こし部隊

f:id:cluex-developers:20161014143842j:plain

       

おじさんたち

火起こし部隊の後ろで待機です。 

f:id:cluex-developers:20161014150015j:plain

       

そして・・・・

     

いざBBQ!

f:id:cluex-developers:20161014144402j:plain

f:id:cluex-developers:20161014144448j:plain

f:id:cluex-developers:20161014145426j:plain

f:id:cluex-developers:20161014144559j:plain

     

普段はこういった機会がなかなか無いので、

みんなでお酒を飲みながら語り合って、貴重な時間を過ごせました。

   

BBQを堪能し、ほどなくして2日目も終了です。

今日はみんな疲れが出てきたようで、午前12時にはみんな就寝したみたいです。

     

開発合宿3日目 (最終日)

3日目も天気に恵まれた、気持ちのいい朝を迎えました。

午前9時に起床し、10時にチェックアウトです。

f:id:cluex-developers:20161014104553j:plain

   

3日目は土浦市街の方で貸会議を借りました。夕方まではこちらで各チーム作業を行い、最後に経過発表です。

お借りしたのは、「ワークヒル土浦」の特別会議室。社内の会議などであれば法人でも利用ができます。

10時~17時の利用で4000円ほどと、なかなかのお手頃価格です。

土浦市勤労者総合福祉センター「ワークヒル土浦」

   

人数のわりに、とても立派な会議室でした。

f:id:cluex-developers:20161014151114j:plain

こちらで一通り作業をした後、

各チームからの合宿目標の結果発表と振り返りを行いました。

       

今回の合宿で取り組んでいた、 「ES2015(ES6)の導入」や「Rspecカバレッジ」に関しては、追ってブログにまとめたいと思いますので、しばしお待ちください。

       

観光編

JAXA(筑波宇宙センター)に入ってきました!

f:id:cluex-developers:20161014175051j:plain

     

合宿最終日、会議室で結果発表などを終えた後、

20分ほど車を走らせて茨城県つくば市にある「JAXA 筑波宇宙センター」に行ってきました。

筑波宇宙センター | ファン!ファン!JAXA!

   

日本の宇宙産業を牽引するJAXA

アメリカではNASAスペースシャトルが引退し、SpaceXなどの民間企業へのシフトが進んでいますが、そんな宇宙を相手にする仕事はロマンがあってすごいなあと個人的には思うところです。

最先端の技術たちを駆使して宇宙という過酷な環境に挑むって本当にすごいです。

教育にも最先端の技術をどんどん導入していきたいです。

     

という訳で、JAXA見学です!

   

ISSの日本実験棟「きぼう」

f:id:cluex-developers:20161014153413j:plain

中にも入れちゃいます。

f:id:cluex-developers:20161014175208j:plain

   

宇宙服 (1着あたり10億円するそうです)

f:id:cluex-developers:20161014153511j:plain

興味津々です。

f:id:cluex-developers:20161014174004j:plain

f:id:cluex-developers:20161014153844j:plain

   

その他にも衛星やロケット、エンジンなどが実物大で見れます。

f:id:cluex-developers:20161014154253j:plain

f:id:cluex-developers:20161014174904j:plain

f:id:cluex-developers:20161014175003j:plain

       

施設の方(JAXAを引退された方?)に、

宇宙に関して、衛星や衛星に使用している技術など

色々なお話を付きっきりで伺うことができました。

 

f:id:cluex-developers:20161014154645j:plain

ありがとうございました。

f:id:cluex-developers:20161014181714j:plain

         

合宿のまとめ

前回に引き続き、今回も有意義な合宿となりました。

 

今回の合宿での反省点や改善点は次回以降にまた活かしていきたいと思いますが、

これから開発合宿をやろうと考えている方にも、気をつけるポイントなどをあげておこうと思います。

 

Keep

・メンバーのチームワーク強化に繋がった。

・宿泊代がお手頃。

・旅のお供に温泉は大事。

・ご飯が美味しい。

・宿にネット環境が整っている。

・自然溢れる環境なのでとてもリフレッシュできた。

・近くに娯楽施設がないので、逆に集中できた。

   

Problem

・床に座って作業するときは座椅子があると良かった。

・作業に当てられる時間が意外と短い。合宿中にやることの選択と集中が大事。

   

開発合宿のポイント

合宿にあたって気をつけていたことは、

・合宿の目標には数値的な結果が見えやすい目標を。

→ 加えて、合宿の開始と同時に、すぐに作業に取り掛かれる様に準備しておくと時間を無駄にせずに済みます。

→ 開発合宿の醍醐味として合宿中に立てた目標を達成することがとても大事だと思うので、どうしても終わらなそうであれば合宿前にある程度の下準備をしておくといいかと思います。

 

・お酒は最終日の前夜のみに抑える。

→ ダメになりやすい人は控えましょう。お酒を飲むとせっかくの目標もどうでもよくなっちゃいます。

 

・ネット環境の確認。

wifiの有無はもちろん、速度面なども事前に確認しておくと安心です。

→ もしもの時に、モバイwifiを各自で持参しておくと何かと便利ですが、圏外になることもしばしばあります。

 

・電源の確保。

→ 作業する場所で電源が使用できるかはもちろんですが、延長ケーブルを持参すると便利なこともよくあるので、事前に確認しておくと安心です。

 

・お昼に温泉は入らない。

→ 温泉に入ると気持ちが満たされてせっかくの目標もどうでもよくなっちゃいます。ダメになりやすい人は控えましょう。

 

・近隣の施設や観光地を調べておく。

→ 特に宿泊施設内もしくは近場にコンビニがあるか確認しておくと安心です。24時間営業ではないコンビニもあるので気をつけておくといいかと思います。

 

・予定は詰め込み過ぎない。

→ 食事処の予約や会議室の予約など、時間的な余裕を持って対応できる様にしておくといいかと思います。

→ 合宿中にやることも基本的には1~2つに絞ると雑念なく集中できるのではないかと思います。

 

番外編

懐かしきスマブラ!!

実は遡ること2日目のBBQの後、懐かしきスーパーファミコン任天堂64が出現!

jiccaの方で貸していただきました!

   

プロジェクターも用意して頂いて懐かしい気分に浸りつつ、ガチバトル。

f:id:cluex-developers:20161014155218j:plain

f:id:cluex-developers:20161014175817j:plain

   

jiccaで飼われている猫です。

猫かわいいですね、猫。

f:id:cluex-developers:20161014180231j:plain f:id:cluex-developers:20161014180047j:plain

いかがでしたでしょうか?

日々の業務では対応できない経営課題や技術課題に取り組むために、新しい技術の試験の場に、新しい事業の創出の場に、開発合宿やってみてもいいかもしれません。

   

個人的にはリフレッシュもできてES6の導入も無事に進み、温泉に入れて美味しいご飯を食べれていい合宿でした。 これからまた更に事業スピードあげていきたいところです。

       

We're hiring!!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています!

気になった方はご連絡ください!!

ビジネスサイド

www.wantedly.com

www.wantedly.com

エンジニア

www.wantedly.com

cluex.co.jp

SPDYも廃止されたのでnginxでhttp2使いたいけど、そもそもhttp2ってなに?っていうお話

こんにちは、高橋です。最近はnginxとrailsとswiftをぐるぐるしています。

nginxは非常にパワフルなwebサーバーで、アクセス数が増大した時した時でも非常に安定してトラフィックを捌いてくれています。

Ruby on Railsを使用する場合には、unicornやpumaなどのアプリケーションサーバーと併用して使うことが多いかと思いますが、どうしてもアプリケーションサーバーの方がボトルネックになってしまいます。nginxの処理能力をどれだけ極限まで高めても、結局はアプリケーションサーバーの方で処理しきれずに503が返ってきてしまうので、どうやってアプリケーションサーバーの方を安定的に処理能力をあげるか最近は悩むところです。。

 

最近ではAWSのEC2などでautoscalingもよく使われているので、1台あたりのトラフィックの処理能力はそれほどカリカリにまで高める必要はないのかもしれません。

 

ということで前置きはこんな所で、今回は(僕が勝手に)社内にもhttp2を取り入れたいなーと思い、色々調べてサーバー上で試験的に試してみたので、まとめてみました。

 

nginxでHTTP/2を使用する

Googleが提唱していたSPDYを取り込んだ形でhttp/2が標準化される流れとなり、これから本格的に広まって行きそうですが、nginxではバージョン1.9.5からhttp/2をサポートするようになりました。

HTTP/2とは

http/2は、http/1.1でプロトコルレベルでの問題となっていたデータ送受信における効率の悪さを克服する形で、http/1.1ではクライアントとサーバーがシングルシーケンスでデータをやりとりを行なっていたのが、http/2ではmultipleにシーケンスのやりとりを行うことができるようになりました。これによってデータ転送と処理の効率が上がりwebページを表示するまでのスピードが上がるというわけです。

http/1.1の場合 http/1.1ではブラウザのTCP同時接続は最大4~6に制限されています。 HTTP Head of Line Blockingによりブラウザのネットワークはこんな感じに、ひとつのファイルの読み込みが終わるまでは、それ以降のファイルの読み込みはブロックされます。

f:id:cluex-developers:20160928165756p:plain

   

http/2を使用した場合 同時に複数のファイルの読み込みを行うようになります。http/2ではブラウザの最大同時リクエスト数は100以上が可能とされていています。

f:id:cluex-developers:20160928165916p:plain

 

webブラウザのHTTP/2対応状況

2016年9月現在のブラウザの対応状況はこんな感じです。ChromeFirefoxなどのメジャーなブラウザではほぼほぼ対応していますが、IEsafariなどでは一部対応といった感じのようです。

Can I use... Support tables for HTML5, CSS3, etc f:id:cluex-developers:20160928203910p:plain

   

HTTP/1.1からHTTP/2への主な変更点

  • 根本となる部分はhttp/1.1と同じ
  • http/1.1のセマンティックスは変更しない
  • サーバーへのTCP接続数を1つに限定
  • TLSの機構を使用してプロトコルを自動選択
  • バイナリープロトコル
  • 多重化送信

などといったことが挙げられます。

HTTP/2を使用すると現在のTLSは一部機能が使用できない

よりセキュアな通信を確立するために、http/2は現在のTLSの一部機能を制限することで、セキュアなプロトコルとして現在は使用されています。今後http/2に対応した形の新しいTLS、もしくはそれに替わるものが出てくるかと思います。

制限される内容はこちら

  • プロトコル選択にはNPNではなくALPNを使用
  • サーバー認証を共有できる接続は接続共有が可能
  • TLSプロトコルは1.2以上のみが使用可能
  • TLS Compressionの禁止
  • Renegotiationの禁止
  • DH鍵は2048bit以上を使用
  • AEAD(認証付き暗号)のうち、GCMとCCM以外の暗号方式は利用できない

 

 

NPN (Next Protocol Negotiation) からALPN (Applicaiton-Layer Protocol Negotiation) へ

http/2に先行して、NPN (Next Protocol Negotiation) はTLS false start方式の次の規格としてgoogleが提唱し、SPDYを支える次期アプリケーション層プロトコルネゴシエーションとして2010年ごろに登場しました。

背景的にはwebサイトの表示速度にはネットワークの回線速度よりもRTTの影響が大きく関わっており、TCP接続のハンドシェイクを毎回行うところに非効率さがあったと判断したのかと思います。

これに関しては2011年にAkamaiが行なった調査を簡単にまとめた2012年の記事があるので、下記の引用画像を見ていただくとwebサイトの表示速度がRTT依存というのがよりわかりやすいかと思います。

f:id:cluex-developers:20160928235245p:plain 引用元: Latency: The New Web Performance Bottleneck - igvita.com

   

これまではNPNが使用されていましたが、今後の流れとしてはhttp/2での後押しもありALPNが一般的となりそうです。

   

NPNとALPNの簡単な違いは、サーバーとクライアントのどちらが使用するアプリケーションプロトコルの決定を行うかというところで、NPNではクライアント側によるプロトコルの選択が行われていましたが、ALPNではサーバー側がクライアントの使用できるアプリケーションプロトコルの中から使用するプロトコルを選択するという形になります。

 

ALPNを使用するにはOpenSSLのバージョン1.0.2以上が必要となりますが、サーバーのOSによっては最新のOpenSSLを入れてもALPNを使用できないこともあります。

   

CentOSでHTTP/2とALPNを使う

CentOSでhttp/2とALPNが使用できるのはCentOS 7 以上のバージョンとなります。

   

UbuntuでALPNを使う

Ubuntuでhttp/2とALPNを使用する場合は、今年リリースされた Ubuntu 16.04 LTS (Xenial) 以上のバージョンになります。

   

nginxでのhttp2を設定

現在、nginxでhttp/2を使うには、ngx_http_v2_moduleが必要となります。

Module ngx_http_v2_module

再ビルドなどに関しては他にも記事があるので割愛しますが、 サーバーのOSとOpenSSLのバージョンがhttp/2に対応していれば、nginxで使用するのは非常に簡単かと思います。

   

最後の最後で、もはやおまけみたいな感じになってしまいますが、listenディレクティブにhttp2と入れるだけで、基本的には完了です。

server {
  listen 443 ssl http2;

  # ~~~~~
}

付け加えるとしたら、SSLプロトコルの設定や、ssl_ciphersなどの設定などでしょうか。その辺りに関しましては、こちらの記事を流し読み程度にご参考にしていただけると嬉しいです。

cluex-developers.hateblo.jp

     

googleはもちろんのこと、facebooktwitterといった名だたるwebサービスで既にproductionへ導入されているhttp/2で、 徐々に様々なwebサービスで導入され始めていますが、本格的に普及するまではもうしばらく時間がかかりそうな気もします。

また進展があったら、記事にしたいと思います!

Cluexではエンジニアの方を探しています!

cluex.co.jp

AWS Lambdaを使用し、CloudfrontのInvalidationを走らせる

こんにちは。エンジニアの志村です。
先日assets on S3についての実装を行いました。   その際の記事は下記になります。

cluex-developers.hateblo.jp

今回はasset_syncにより、S3にassetファイルがアップロードされた段階でCloudfrontのInvalidationをLambdaを使用して走らせるという処理を実装したいと思います!

Invalidationとは

Cloudfrontのキャッシュを明示的に消す機能です。

dev.classmethod.jp

クラスメソッドさんの上記の記事が非常に分かりやすいかと思います。

キャッシュを使用するのに重要なポイントは適切なキャッシュ期間を設けることです。それを行わないと古いファイルがいつまで経っても配信され続けてしまいます。
特にassets on S3を使用する場合には、明示的にファイルをInvalidationしないといつまで経っても最新のassetsファイルがユーザーに配信されなくなります。
このような状況を防ぐために、S3にファイルが転送された段階で、assetsのキャッシュを消去する必要があります。

Lambdaの実装

ではLambdaの実装を始めていきます。

AWSコンソールからLambdaを選択します

「Create Lambda Function」を選択します

f:id:cluex-developers:20160916104010p:plain

今回はBlueprintは使用しないので「Skip」を選択します

f:id:cluex-developers:20160916104022p:plain

S3を選択します。

f:id:cluex-developers:20160916104040p:plain

項目 説明
Bucket assets用のBucketを選択
Event Type S3にassetsファイルがCreateされた段階でInvalidationを走らせたいので「Object Created(All)」を選択
Enable Trigger チェックを入れると、設定したイベントが走るようになります(production等で設定する場合は注意!)

※Prefix, Suffixは必要であれば入力して下さい。

LambdaFunctionを作成します

f:id:cluex-developers:20160916104141p:plain

項目 説明
Name 適当な名前を入力
Description 分かりやすい説明を入力
Runtime 今回はPythonにします

コードは下記のコードが非常に分かりやすかったのでそれを使用します。

www.cloudberrylab.com

※DistributionIDは自身のCloudfrontディストリビューションのIDを入力して下さい

from __future__ import print_function

import boto3
import time

def lambda_handler(event, context):
    for items in event["Records"]:
        path = "/" + items["s3"]["object"]["key"]
    print(path)
    client = boto3.client('cloudfront')
    invalidation = client.create_invalidation(DistributionId='ディストリビューションID',
        InvalidationBatch={
            'Paths': {
                'Quantity': 1,
                'Items': [path]
        },
        'CallerReference': str(time.time())
    })

因みにですが、boto3はPython用のAWS SDKです。 Lambda FunctionをPythonで書く際には必ずと言って良いほど良く使用します。 ドキュメントも充実しているので非常に使い勝手が良いと思います。

Boto 3 Documentation — Boto 3 Docs 1.4.0 documentation

ロールやその他の設定をします

「Lambda function handler and role」のRole→「Create a custom role」を選択します。 f:id:cluex-developers:20160916104432p:plain

そうすると下記のような画面が現れるので、下記のCloudfrontのInvalidationを許可するロールを作成します。 f:id:cluex-developers:20160916104616p:plain

下記のようなポリシードキュメントになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "cloudfront:CreateInvalidation"
        ],
        "Resource": [
            "*"
        ]
    }
  ]
}

最後に「Create function」を押下すれば完成です!

使い方

asset_syncでデプロイすると勝手にInvalidationが走ってくれます。 下記の画面で確認が出来ます。

AWSコンソールからCloudfrontを選択します

該当するディストリビューションのIDを選択します

f:id:cluex-developers:20160916104912p:plain

「Invalidations」タブを選択すると、Invalidationsの状態が閲覧出来ます。ここでデプロイ直後にStatusがProgressとなっていればトリガーがきちんと走っています。

f:id:cluex-developers:20160916105140p:plain

以上になります。 キャッシュの扱いって本当難しいなーと感じますが、この様に自動化してしまえば特に意識することも少なくなって、よりCloudfrontが使いやすくなりますね。

Rackの基礎

こんにちは!! エンジニアとしてインターンしている村田です。

今回はズバリRackについて書いていきたいと思います。

Rackに関しては日本語の資料もあまり多くないため、特にRailsの初心者の方はよく分からないまま見過ごしていることも多いかもしれません。

私も初心者なので探すのに苦労しました。。

そこで自分もまだまだではありますが、これを読んで自分と同じような初心者の方々がRackのぼんやりとしたイメージをつかんでいただけるように書いていきたいと思います。

それでは早速ですがやっていきましょう!

Rackが誕生した背景

まず何故Rackが必要なのか? ここがわからないと、話が頭に入ってこないですよね。

これはPSGIというPythonのインターフェースの仕様に影響を受けました。

以前のPythonフレームワーク界は、様々フレームワークが開発されたものの、そのフレームワークが特定のWebサーバーに依存した状態でした。 ただし、これではサーバーの乗り換え時の負担が大きかったりして、開発者も頭を悩ませることが多かったようです。 そこで、ある特定のルールを定めて(インターフェース)、そのルールのもとフレームワークとWebサーバーの開発を進めることにしました。

これがPSGIの正体です。

RubyフレームワークRailsSinatraを筆頭に多様性に富みますし、WebサーバーもWebrickをはじめとして様々な独自のデプロイ方法を持っています。

すると、当然PSGIの代わりとなるシステムが必要になりまして、そうやって検討されていった結果現在のRackが誕生していくことになります。

Rackってなに??

さていよいよ本題にいきましょう。

Rackとは、rackupというコマンドを打つことで指定したファイルを参照しながらWebサーバーを立ち上げられるようにしているライブラリ(gem)です。 もう少し機能自体の説明をするならば、HTTPの送受信処理を担当するモジュールです。

指定したファイル(デフォルトではconfig.ru。ちなみにruはruckupの略。)には独自のRuby DSLで様々なミドルウェアやアプリケーションを参照するように書かれています。 これをもとにWebサーバーを立ち上げていくことになります。

ビジュアル的に説明をするとこんな感じになっています。

f:id:cluex-developers:20160916102713p:plain

このようにミドルウェアを介して、RailsなどのRackアプリケーションに対してWebサーバーから来たHTTPをパースしたりしています。

試しにbundle exec rake middlewareと打ってみましょう。 例えばこのような感じになって出てくると思います。

use Raven::Rack
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd239f96d58>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use BetterErrors::Middleware
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CacheStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Remotipart::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
use ExceptionNotification::Rack
use Bullet::Rack
use OmniAuth::Strategies::Facebook
run Rails::Application.routes

ここに書いてあるuseの対象がRackミドルウェアです。 そして最後に書いてあるrunの対象はRackアプリケーション(Rails)になります。 ちなみにuserunはRack DSLなのですが、今回は詳しく触れませんので興味がある方は調べてみてください。

ruckupの正体

こちらをご覧いただければお分かり頂けると思いますが、実はrackupコマンドがやっているのはRack::Server.startをしているだけなんですよね。

と言われてもRack::Serverとかよく知らないって方がこの記事を読んでくださっていると思いますので、説明します。

Railsで開発をしているとrails s、省略しないで書くと、rails serverというコマンドを打つことがあると思います。 このrails serverというコマンドも実は省略形でして、正確にはRails::Server.startというコマンドを打っています。

何か先ほどとても似ているコマンドを見ましたよね? そうRack::Server.startです。

もちろんこの二つは関係がないわけではなく、Rails::ServerはRack::Serverのサブクラスになっているのです。

継承する中で、ポートを3000番にするなど様々な違いを作っているようですが基本的な動きの部分は引き継いでいることがわかっていただけたと思います。

Rackミドルウェアの話

Rackミドルウェアはもちろん自分で追加することもできますが、そのためには簡単なルールを幾つか覚えなくてはなりません。

1つのHashオブジェクトを引数に取る「call」メソッドが実装されていること callメソッドは「ステータスコード」、「ヘッダーを表現したHash」、「eachに反応するオブジェクト」の3つの要素を持った配列を返すこと

上にある2つがそのルールになります。 また慣習的にcallメソッドの引数にはenvを用いることも覚えておくと得をするかもしれません。

詳しく話を始めるとDSLの説明やその他いろいろな話を書かないといけないので割愛しますが、Railsのようなアプリケーションも最後はcallで呼ばれるので

lamda do |env|
[200, {"Content-Type" => "text/html"}, ["Hello, world!"]]
end

簡単に言ってしまえばこのような形で動いていることになります。 これは先ほどの条件を満たしていますよね。

最後に

私の説明では物足りなかった部分も多かったと思います。 でもこれをきっかけにしてRackのイメージを掴めた方が一人でもいらっしゃればとても嬉しいです。

私もわからないことだらけなのでもっと勉強していこうと思いますが、最後にこの記事を作る上で参考にさせていただいた記事を載せておこうと思います。

RailsGuides

Rackとは何か

Rack解説

パーフェクトRubyonRails

それでは失礼いたします。