Fusicきばんブログ

Fusic基盤ユニットの非公式技術ブログ

nginx-proxy(Docker)で設定なしリバースプロキシ

こんにちは桜川です。

Docker便利ですね!

自分の開発環境はWebサーバーにアクセスするのに、前段にNginxでリーバースプロキシを設置してホスト名を切り替えてアクセスしてます。
この方法で基本困ってなかったのですが、地味にめんどくさいのがNginxへのリバースプロキシ設定の追加です。

コンテナ新規起動 => Nginxのリバースプロキシ設定の追加 => Nginx再起動

この設定1分あれば完了する。。。
けど、めんどくさい。。。
しかも、設定ファイルの管理もめんどくさい。。。

やっぱりあった

nginx-proxy を利用するとコンテナに起動・終了を検知して自動でリバースプロキシの設定を行ってくれます!
nginx-proxy 知らなかったよ。。。
ググってすらなかった。。。

github.com

jwilder/nginx-proxy

とりあえずhttpアクセスできるようにする

リバースプロキシを利用して、バックグラウンドにApacheを起動するにはこれだけでいい
httpd1.example.com httpd2.example.com はhostsファイルにでも設定しておいてください

  • nginx がリバースプロキシ用のコンテナ
  • httpd1 httpd2 がサンプル用にApacheを起動してます。
version: '3'
services:
  nginx:
    container_name: nginx
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
  httpd1:
    container_name: httpd1
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd1.example.com
  httpd2:
    container_name: httpd2
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd2.example.com
  • jwilder/nginx-proxy
    • nginx-proxyはコンテナの起動・停止イベントを受け取る必要がある為、 docker.sock をマウントする必要があります。
  • http
    • コンテナにアクセスしたいホスト名を VIRTUAL_HOST で設定します
    • アクセスする必要があるコンテナで expose しておいてください

ここまでは簡単でよかった。サクッと出来た

SSLを使おうとした場合にちょっと手間取りました

SSLアクセス用に証明書を設置する

SSLアクセスするためには、証明書を nginx-proxy コンテナ内の /etc/nginx/certsホスト名.key ホスト名.crt で設置しておく必要があります。
なんだけど、開発環境だしホスト名毎にSSL証明書を作るのはちょっと。。。

と思ってたらやっぱりありました。使用する証明書を選ぶ機能。
CERT_NAME を設定するとアクセスするときに使用する証明書を選択することが出来ます。
※設定しない場合は、 ホスト名.crt, ホスト名.key が使用される

version: '3'
services:
  nginx:
    container_name: nginx
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs # ここに証明書を設置する
  httpd1:
    container_name: httpd1
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd1.example.com
      - CERT_NAME=default # 使用する証明書名(default.crt, default.keyが使用される。)
  httpd2:
    container_name: httpd2
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd2.example.com
      - CERT_NAME=default # 使用する証明書名(default.crt, default.keyが使用される。)

これでSSLアクセスできるようになった!
クリア!
と思っていたら、 http がすべて https にリダイレクトされている。。。
デフォルトでは自動でリダイレクトされるみたいです。

自動リダイレクトを無効にする

HTTPS_METHODnoredirect を設定することで自動リダイレクトを無効にできるようです。

version: '3'
services:
  nginx:
    container_name: nginx
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs # ここに証明書を設置する
  httpd1:
    container_name: httpd1
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd1.example.com
      - CERT_NAME=default # 使用する証明書名(default.crt, default.keyが使用される)
      - HTTPS_METHOD=noredirect # リダイレクトを無効にする
  httpd2:
    container_name: httpd2
    image: httpd
    environment:
      - VIRTUAL_HOST=httpd2.example.com
      - CERT_NAME=default # 使用する証明書名(default.crt, default.keyが使用される)
      - HTTPS_METHOD=noredirect # リダイレクトを無効にする

これで目的を達成!
めでたしめでたし。

まとめ

  • httpアクセスだけでよければ、 exposeVIRTUAL_HOST を設定するだけ
  • SSLアクセスする必要がある場合は証明書を設置する
  • 必要に応じて、 CERT_NAMEHTTPS_METHOD を設定する

ここが嵌るよCircleCI 1.0

基盤ユニットの貞方(@sadapon2008)です。 CircleCI 1.0+CakePHP3+PostgreSQLのテストで嵌った出来事を紹介します。

1. ドキュメントで紹介されているPHPのバージョンが古い

2017年7月現在でのCircleCIの↓のドキュメントではPHP 7.0.4が例として書かれています。

https://circleci.com/docs/1.0/language-php/

これが結構古いバージョンで、PHPUnitを実行するとsegmentation faultが発生したりします。

↓を参考に7.0.x系なら7.0.17を使うのが無難です。

https://circleci.com/docs/1.0/build-image-trusty/#php

machine:
  php:
    version: 7.0.17

2. デフォルトでxdebugが有効になっている

xdebugを有効にしておくとテストの実行時間が長くなってしまいます。必要がなければ、circle.ymlで以下のように無効にしましょう。

dependencies:
  pre:
    - rm /opt/circleci/php/$(phpenv global)/etc/conf.d/xdebug.ini

3. PostgreSQLのデフォルトの照合順序がen_US.UTF-8になっている

これはPaaS系だとありがちな話ですが、デフォルトの照合順序がen_US.UTF-8と英語になっています。これを解消するには、以下のようにデータベースを作成する際に明示的に指定するのが無難です。

database:
  override:
    - psql -U ubuntu postgres -c "CREATE DATABASE blog_test with template = template0 encoding = 'UTF8' lc_collate = 'ja_JP.UTF-8' lc_ctype = 'ja_JP.UTF-8';"

ただし、日本語のロケールを指定するには自分で追加しないといけないので、circle.ymlで以下のようにしておく必要があります。なお、デフォルトのPostgreSQLのバージョンが変わった場合はパスの変更が必要ですので要注意です。

machine:
  pre:
    - sudo locale-gen ja_JP.UTF-8
    - sudo -E -u postgres PGDATA=/var/lib/postgresql/9.5/main /usr/lib/postgresql/9.5/bin/pg_ctl stop
    - sudo -E -u postgres rm -r /var/lib/postgresql/9.5/main
    - sudo -E -u postgres PGDATA=/var/lib/postgresql/9.5/main /usr/lib/postgresql/9.5/bin/initdb --encoding=UTF8 --no-locale --username=ubuntu
    - sudo -E -u postgres PGDATA=/var/lib/postgresql/9.5/main /usr/lib/postgresql/9.5/bin/pg_ctl start

参考:

qiita.com

4. デフォルトのタイムゾーンはUTC

JSTに変えたい場合は、circle.ymlで以下のようにします。

machine:
  timezone:
    Asia/Tokyo

Stack Clash脆弱性対応まとめ

基盤ユニットの貞方(@sadapon2008)です。 Stack Clash脆弱性の対応について調べた情報をまとめておきます。

Stack Clashとは

セキュリティ企業のQualys社が6月19日に公表した、ローカルからの特権昇格の恐れがある脆弱性です。 対象OSはLinux系、BSD系、Solaris系に影響があるとのことです。 今のところリモートからの攻撃成功可能性は低いようですが、昔問題になったShellShockのようなものと組み合わさるとまずいです。

Qualys社の公表記事はこちらです。

https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash

メディア記事

www.itmedia.co.jp

japan.zdnet.com

news.mynavi.jp

www.security-next.com

thinkit.co.jp

対応方法

根本的にはアプリケーションバイナリの再コンパイルが必要とのことですが、kernelとglibcで影響を軽減するためのアップデートが公開されているようです。kernelとglibcを更新したらシステムを再起動をした方がよさそうです。 ここではLinux系のアップデート情報をまとめておきます。

Red Hat Enterprise Linux(RHEL)

RedHat社が特設サイトを開設しています。

https://access.redhat.com/security/vulnerabilities/stackguard

RHEL5~7のアップデートが公開されています。

CentOS

CentOS6~7のアップデートが公開されています。

[CentOS-announce] CESA-2017:1486 Important CentOS 6 kernel Security Update

[CentOS-announce] CESA-2017:1480 Important CentOS 6 glibc Security Update

[CentOS-announce] CESA-2017:1481 Important CentOS 7 glibc Security Update

[CentOS-announce] CESA-2017:1484 Important CentOS 7 kernel Security Update

CentOS5はすでに本家のRHEL5が通常サポート終了で延長サポートフェーズになってしまったため、RHEL5のアップデートが一般公開されておらず、CentOS5のアップデートパッケージを作成できない状況のようです。 海外フォーラムで見かけたアドバイスとして「有償サポートつきのRHEL5を購入して移行する」というのもありましたがそのあたりはシステム次第でしょう。 また、独自にCentOS5のサポートを提供しているベンダもあるようです。

www.miraclelinux.com

Ubuntu

公式の質問フォーラムに情報がまとまっていました。

askubuntu.com

Amazon Linux

アップデートが公開されています。

ALAS-2017-844

ALAS-2017-845

AWS全般についてはこちらにまとまっています。

Linux Security Advisories - June 2017

複数AWSアカウントのRoute53で管理されている複数HostedZoneの複数DNSレコードをうまく変更する

基盤ユニットの小山 ( @k1LoW ) です。

そろそろ PHPカンファレンス福岡2017ですね!

弊社Fusicでは前日に会社会議室を開放してクーラーの効いているWiFiスポットを提供しますので、是非ご利用下さい。 Fusicは天神にありますしアクセスも良いです。福岡の開発会社をみるいい機会ですよ!

fusic.co.jp

さらにカンファレンスの次の日は無限ビール(?)を開催します!

fusic.doorkeeper.jp

楽しみですね!


さて、以前ちょっと面倒なDNS設定変更作業がありましたので、その作業手順をメモしておこうと思います。

主にRubyの話です。

DNS設定変更作業内容

1つ1つの作業はただのDNSレコードの設定変更だったのですが、少し面倒で

  • 複数のAWSアカウントのRoute53で管理されているHostedZone
  • HostedZoneも複数
  • HostedZone内の変更対象のDNSレコードも複数
  • それぞれ変更内容が異なる
  • DNSレコードの先にはそれぞれWebサーバが存在。設定変更後も同じWebサーバにリクエストが通る前提

というものを、(Webサイトが存在するので)できるだけ短時間に実施する必要がありました。

結果として、Codenize.toolsによるコード化ができ、+Rubyスクリプトでの事前のテストで変更内容の確認ができたので心理的ストレスなく当日を迎えられました。

作業内容

1. AWSリソース操作の権限をつなげる

対象Route53が複数のAWSアカウントにまたがっていたので、まずはAWSリソースの操作権限を1つのIAM Userにつなげました。

メインのAWSアカウントでIAM Userを作成し、それ以外のアカウントにはAssume Role用のIAM Roleを作成して連携させています。

~/.aws/credentials~/.aws/config は以下のようになりました。

# ~/.aws/credentials
[change-task-user-main]
aws_access_key_id = XXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ~/.aws/config
[profile change-task-role-a]
role_arn = arn:aws:iam::11111111111111:role/change-task
source_profile = change-task-user-main

[profile change-task-role-b]
role_arn = arn:aws:iam::222222222222:role/change-task
source_profile = change-task-user-main

[profile change-task-role-c]
role_arn = arn:aws:iam::333333333333:role/change-task
source_profile = change-task-user-main

2. DNSの設定をCodenizeする

Route53の設定は特にコードでの管理がされていませんでしたので、まずはコードで管理できるようにします。

Codenize.toolsの1つであるRoadworkerを利用します。

RoadworkerはRoute53の設定をコード化して管理運用するツールです。

Codenize.toolsの各ツールにはエクスポート機能があるのでいつでもコード化できます。

以下のようにRoute53の設定をRoadworkerのexport機能でエクスポートしました。

$ AWS_PROFILE=change-task-user-main bundle exec roadwork -e -o Routefile.main
$ AWS_PROFILE=change-task-role-a bundle exec roadwork -e -o Routefile.a
$ AWS_PROFILE=change-task-role-b bundle exec roadwork -e -o Routefile.b
$ AWS_PROFILE=change-task-role-c bundle exec roadwork -e -o Routefile.c

3. CodenizeしたDNS設定を使って、別のAWSアカウントでテスト環境を構築する

エクスポートしたRoutefileを使って別のAWSアカウントでRoute53のテスト環境を構築します。

$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.c

これで本番環境のDNS環境(本番DNS)とテスト用のDNS環境(テストDNS)ができました。

4. HTTPリクエストのテストを作成する

本番環境NS、テスト環境NS、そして(念の為)GoogleのNSを使ったHTTPリクエストのテストを作成します。

NSの変更は Resolv::DefaultResolver#replace_resolvers で切り替えました。

HTTPリクエストのテストは取得したHTMLに期待する文字列が含まれているかどうかの簡単なテストです。

ざっくりですが以下のような感じで構築しました。

require 'spec_helper'

dns = {
  production: 'XXX.XXX.XXX.XXX',
  test: 'YYY.YYY.YYY.YYY',
  google: '8.8.8.8'
}

specs = [
  { fqdn: 'example.com', url: 'http://example.com', expected: 'Production Site' },
  { fqdn: 'www.example.com', url: 'http://www.example.com', expected: 'Production Site' },
  { fqdn: 'example.jp', url: 'https://example.jp', expected: '本番サイト' },
  { fqdn: 'blog.example.org', url: 'blog.example.org', expected: 'Production Blog' }
]

dns_req_spec(dns, specs)
# spec_helper.rb
def dns_req_spec(dns, specs)
  dns.each_with_index do |d, idx|
    specs.each do |spec|
      describe "#{spec[:fqdn]} #{idx} DNS" do
        before(:context) do
          resolver = Resolv::DNS.new(nameserver: [d])
          Resolv::DefaultResolver.replace_resolvers([Resolv::Hosts.new, resolver])
        end
        it "#{spec[:url]} http access" do
          connection = Faraday.new spec[:url] do |conn|
            conn.use FaradayMiddleware::FollowRedirects
            conn.adapter :net_http
          end
          res = connection.get '/'
          expect(res.body.force_encoding('UTF-8')).to include(spec[:expected])
        end
      end
    end
  end
end

これで、本番環境NS、テスト環境NS、GoogleのNSの3つにおいて正常にHTTPリクエストが通るかどうかのテストができました。 (正確には対象DNSレコードがCNAMEの場合はテストできませんが)

(ちなみに、Route53のDNSレコードの設定が反映されたかどうかのテストであれば roadwork -t で実施できます。今回はテストDNSというものがあったので利用していません。)

5. テストDNSのDNSレコードの設定を変更する

テストDNSを今回の変更内容に合わせて変更します。

Routefileを編集してRoadworkerの apply オプションで一気に適用できるから便利ですね。

[Edit Routefile.*]
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.c

先ほど4で作成したHTTPリクエストテストを実行して、DNSレコード設定変更後も正常にHTTPリクエストが通ることを確認します。

6. 本番DNSのDNSレコードの設定を変更する

既に検証はテストDNSで終わっているので、先ほど修正したRoutefileを本番のRoute53にそれぞれ apply していけば完了です。

$ AWS_PROFILE=change-task-user-main bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-role-a bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-role-b bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-role-c bundle exec roadwork -a -f Routefile.c

再度、先ほど4で作成したHTTPリクエストテストを実行して、DNSレコード設定変更後も正常にHTTPリクエストが通ることを確認します。

これで完了です。

まとめ

インフラの設定をコード化(Codenize)することによってその後の作業を効率化、さらに作業漏れの軽減ができました。

Rubyで「リゾルバを変更する」という処理を書けることで、事前のテスト検証も実施できました。

今回のDNSがAPIで設定変更可能なRoute53でなかったと思うとちょっとツラいですね。

もし同じようなDNS設定変更があった場合の、何かの参考になれば幸いです。

mockmock × Google Cloud Platform( × Slack)

こんにちは。Fusic 基盤ユニットの濱野です。
今日は、先日 4/6 に正式リリースされた 弊社サービス mockmock を使ってみた話を書いてみようと思います。

mockmock とは?

- mockmockはIoTの開発支援サービス。
- クラウド上に仮想デバイスを作成し、開発中のサーバーに疑似データを送ってくれる。
- デバイスの制約を受けずにIoTのサーバーアプリの開発が進められるので、とっても楽。

というサービスです。

目標

mockmock から Google Cloud Platform(以下、GCP) に作ったAPI を叩き、
Slack に通知を飛ばす
とりあえず、シンプルにWeb API を叩けるようになる!!!

まず最初に、GCPにAPI サーバーを作ります。

使用したのは、永年無料で使用できると噂の f1-microです(※1)。
こちらのサイト https://blog.apar.jp/web/6966/ を参考にさせて頂きました。

環境は、

$ nginx -v
nginx version: nginx/1.10.2

$ php -v
PHP 7.0.17 (cli) (built: Mar 14 2017 15:14:30) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies

です。

ちなみに、f1-micro では、メモリが 0.6 GB しかありません。
docker の mysql image をrun しようとしましたが、
メモリ不足で起動すらできませんでした。
後日、省メモリについても調べます。。。

PHP のコード

docroot に、2種類のファイルを作成しました。

1つめ
index.php

<?php
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  echo json_encode(['key' => '鍵']);
}

こちら、後ほど説明しますが、
mockmock のプロジェクトを作る際に必要になる処理です。
‘鍵’ と書いたところに、mockmock のプロジェクト作成時に提示される 認証キー を入れます。

2つめ
success.php

<?php
function send_to_slack($message) {
  $webhook_url = 'https://hooks.slack.com/services/カスタムインテグレーションのURL';
  $options = array(
    'http' => array(
      'method' => 'POST',
      'header' => 'Content-Type: application/json',
      'content' => json_encode($message),
    )
  );
  $response = file_get_contents($webhook_url, false, stream_context_create($options));
  return $response === 'ok';
}
$message = array(
  'username' => 'mockmock',
  'text' => 'fooooo!!!',
);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  // request body に json が送られてくる際、$_POST は空になるので、この処理が必要。
  $json = file_get_contents('php://input');
  $array = json_decode($json, true);
  if ($array['status'] == 'success') {
    $date = new DateTime();
    $now = $date->format('Y-m-d\TH:i:s.u');
    $message['text'] = 'success'.$now;
  } else {
    $message['text'] = 'error';
  }
   send_to_slack($message);
} else {
  echo send_to_slack($message);
}

とりあえず動くことを目標に頑張りました。
slack 通知の送り方は、この辺を参考にしました。ありがとうございます。
http://qiita.com/hoto17296/items/621a6e16f23785a543f3 http://qiita.com/gorogoroyasu/items/a73ba2180d0934f51485 (これ、自分の記事です。)

これで、サーバー側完成です。

(file_get_contents(‘php://input’) を知らなくて、結構ハマリました。 普段はCakePHPを使って開発しているので、素書きのPHPが難しい。。。)

mockmock 側の設定

次に、mockmock 側の設定の話を書いていきます。

公式のヘルプ
を参考にさせていただきました。

ヘルプに沿って、こんな感じの設定を書きます。

f:id:adiboy:20170409233749p:plain

そして、疎通確認を行います。 注意点は、mockmockからのリクエストが GETで飛んでくることです。 まあ、URL にアクセスしてるだけだから当たり前といえば当たり前なのですが。。。

次に、mockグループの新規作成を行います。

と言っても、適当に名前をつけるだけです。

そのあと、mockステータス を設定します。 新規作成を押したあと、 f:id:adiboy:20170409233829p:plain こんな感じで設定します。 f:id:adiboy:20170409233842p:plain

データテンプレート というのは、どのようなデータを送信するか のテンプレートのことです。
ヘルプが参考になります。 https://console.mock-mock.com/help#help_data_template
デフォルトで、 {'hello': 'world'} というデータを送るようなテンプレートがセットされます。
ここを自由にカスタマイズできる仕組みです。

その後、 mock のタブを開き、新規登録を押すと、準備完了です。
f:id:adiboy:20170409233915p:plain

あとは、操作 ボタンから、 起動 を選択するだけ。
数秒 ~ 数十秒 したら、 mockからAPI にデータが飛んできます。

ということで

mockmock は、本来IoT のデバイスの代わりとして使うものですが、 任意のデータをWebAPIに送れるので、GCPに作ったAPI を叩き、Slack に通知を飛ばしてみました。 ハローワールドすることはできたかな? と思います。
少しでもとっつきやすいと思っていただけたら幸いです。

次回は、もう少し mockmock を掘り下げてみようと思います。
楽しい週末でした(※3)!

Slack のスクショ f:id:adiboy:20170409234915p:plain

※1
GCP を使うのは、完全に今回が初めてです。
SSH するのが大変でした。
普通に ~/.SSH/authorized_keys に公開鍵を設定するだけではだめらしい。
http://qiita.com/kosuke_nishaya/items/3d9a95f559d0c22d8134
のような設定が必要らしいです。

※2
もともと、 AWS の API Gateway を叩いて、 Lambda からSlack に通知を飛ばす的な、
サーバーレス的なやつをしたかったのですが、
どうもうまく行きませんでした。

まあ、AWS の知識は皆無に等しいので、詳しい方々は問題ないと思いますが。。。
それで、こんな感じでGCP を使うことにしたのです。

※3
http://ghostshell.jp/ を見に行く予定だったのに、
予定が大幅にずれ込んだため、泣く泣く延期します。
ネタバレはしないでください。

CakePHP3の負荷テストでの落とし穴

基盤ユニットの貞方(@sadapon2008)です。

CakePHP3のアプリをApacheBenchで負荷テストした際にはまった落とし穴をご紹介します。

前提

  • CentOS 7.3 + Apache 2.4 + PHP 7.0.17(remi版のmod_php)

落とし穴:ApacheBenchでCakePHP3の負荷テストをやってみたら全く性能が出ない

非常に簡単な負荷テストとして、静的HTML、素のPHP、CakePHP3.4.3でGETに対して簡単なHTMLを返すだけのものを用意し、 下記のコマンドでApacheBenchを実行してRequests per secondを測ってました。

$ ab -t 10 -c 20 -n 1000000 http://a.b.c.d/

すると、CakePHP3だけ3桁以上性能が悪い結果が出て(´・ω・`)となりました。 CakePHP3のデバッグモードはオフにしています。

  • 静的HTML: 約 8,000 req/s
  • 素のPHP: 約 7,500 req/s
  • CakePHP3: 約 2 req/s

原因

さすがにこの結果はおかしいだろうと原因を調査したところ、下記の記事を見つけました。

stackoverflow.com

PHP 5.4以上ではHTTPのリクエストのConnectionヘッダを指定しないとheader()が非常に遅くなるとのことでした。 CakePHP3のソースを見てみると↓の箇所で使っているheader()がまさに該当していました。

https://github.com/cakephp/cakephp/blob/3.4.3/src/Http/ResponseEmitter.php#L144

ワークアラウンド

前述の記事を参考にab -kを指定してConnection: keep-aliveが有効になるようにしてみました。

$ ab -t 10 -c 20 -n 1000000 -k http://a.b.c.d/

その結果、かなり改善しました!

  • CakePHP3 before: 約 2 req/s
  • CakePHP3 after: 約 700 req/s

元記事によるとクローラーなど行儀の悪いクライアントはConnectionヘッダが欠落していることもあるとのことなので、例えばApacheなら以下のような設定を行うことで、Connectionヘッダを強制することが可能です。

※ (2017/3/28追記)HTTP/1.1ではConnectionヘッダを指定しない場合はkeep-aliveをデフォルトとするようでしたのでプロトコルのバージョンをHTTP/1.0に絞り込む設定に変更しました。

<If "%{SERVER_PROTOCOL} =~ m#^HTTP/1\.0#i">
SetEnvIfNoCase Connection "^(keep-alive|close)$" APP_CONNECTION=1
RequestHeader set Connection close env=!APP_CONNECTION
</If>

↑の設定をすると-kを指定せずとも同じくらいの結果が得られました。

Rust言語でゆるくツールを作ってみるお話

Fusicの中野です。

最近、弊社の東京事務所内で人気のRust(プログラミング言語)について。

Rust言語

Rust言語についてのイメージは

  • 所有権と借用
  • 難しい
  • GCがない
  • 速いっぽい

とかでしょうか。

特に、所有権と借用のメモリ管理まわりの概念が、少し難しい言語かなと思います。

しかし、パターンマッチ・型推論・総称型・マクロなど、とても魅力的な要素がたくさん ある言語なので、今回はRust言語の勉強用にちょっとしたツールを作ってみました。

作ってみたツール

ということで、

github.com

主な機能として

  • $HOME/.ssh/config をパースする
  • Host <何か> の何か部分を取り出して、tmuxのパネルを分割してSSHで接続
  • それぞれの接続先にコマンドを送りつける

です。

使用したライブラリとして、

$HOME/.ssh/configのパース => rust版parsecのnom
readlineっぽいやつ => liner

を使わせていただきました。

nomはparsecとそっくりだったので、exampleとガヴリールドロップアウトを見ながら、 さくっと利用する事ができました。

感想として

Rustは難しい部分もあるかと思いますが、コンパイルが通れば気持ちいいですし そこそこ想定通りの動きをしてくれます。

rustup・rustfmt・racerなどの開発用ツールや、Rustの日本語記事や日本語訳ドキュメントなど、 簡単に利用するための環境が充実してきました。

Rust言語が気になっている人は、軽く試してみてはいかがでしょうか?