いがにんのぼやき

若手WEBエンジニアのブログ。IT、WEB、バンド、アニメ。

Docker上での、GitサブモジュールによるNode.jsエラー

igatea-Ubuntu-PC:co.tech igatea$ docker-compose exec php npm install
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t git://github.com/jeroennoten/webpack-stream.git
npm ERR! 
npm ERR! fatal: Not a git repository: ../.git/modules/src
npm ERR! 
npm ERR! exited with error code: 128

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2017-11-14T06_51_41_841Z-debug.log

GitのサブモジュールのディレクトリをDockerコンテナ上に共有しているのが問題だった。
この場合はsrcディレクトリをサブモジュールとしていた。
単純にサブモジュールだけじゃなく、Git全体を含める形で解決した。
そもそもサブモジュールを使用せず、指定のディレクトリをgitignoreに追加してそのままそのディレクトリにリポジトリを展開しても良いかもしれない。

UbuntuでVirtualbox、Vagrantのエラー

Dockerを使用してnpm runをしようとするとPermission deniedが出る問題あり。やったことを残すためにこの記事は残しておきますが、参考にしないでください。

sh: 1: webpack: Permission denied

自分用メモ。

Virtualboxを入れるのもひと手間だった。 具体的な記述に残していないが、下記で行けた。

askubuntu.com

Secure bootを無効にするとかだった気がする。

また、UbuntuVagrantを使用するときに、外部ディスクを使用するときにパーミッションエラーが出るための対応。
こんな感じのエラーがでる。

The private key to connect to this box via SSH has invalid permissions
set on it. The permissions of the private key should be set to 0600, otherwise SSH will
ignore the key. Vagrant tried to do this automatically for you but failed. Please set the
permissions on the following file to 0600 and then try running this command again:

/media/igatea/TOSHIBA-1TB/Vagrant/centos7/.vagrant/machines/default/virtualbox/private_key

Note that this error occurs after Vagrant automatically tries to
do this for you. The likely cause of this error is a lack of filesystem
permissions or even filesystem functionality. For example, if your
Vagrant data is on a USB stick, a common case is that chmod is
not supported. The key will need to be moved to a filesystem that
supports chmod.

GUIで外部のHDDのマウントの設定をしていたが、パーミッションの変更ができないっぽい。
なので下記を参考にマウント設定のfstabをいじっていく。

hakushisky.blog.shinobi.jp

askubuntu.com

最終的に/etc/fstabに下記の行を追加して再起動。

UUID=B07E713F7E710000 /media/igatea/TOSHIBA-1TB auto auto,users,permissions 0 0

その後、Vagrantディレクトリのprivate_keyがrootの777になっているのでパーミッションを変更してあげて終了。

chown igatea .vagrant/machines/default/virtualbox/private_key 
chmod 600 .vagrant/machines/default/virtualbox/private_key 

ユーザーがrootのままだと下記のエラーが発生する

The private key to connect to the machine via SSH must be owned
by the user running Vagrant. This is a strict requirement from
SSH itself. Please fix the following key to be owned by the user
running Vagrant:

/media/igatea/TOSHIBA-1TB/Vagrant/centos7/.vagrant/machines/default/virtualbox/private_key

ISUCON7に参加した

参加しました。

事前に練習会をやったのでそのままの流れで。 igatea.hatenablog.com

最終スコアは15164、ベストスコアは33272でした。

f:id:igatea:20171022153957p:plain

トップは云十万単位のスコアを出していて、壁は厚いなあと思った。

チーム構成

担当は練習会と同じ感じ。

言語はPHPです。

今回のISUCONの課題

今回は掲示板のような、チャットのようなサイトでした。
しかもサーバーが3台構成。
このサーバーをどう扱っていくかが重要だった気がします。

どんなことをやったか

僕らは1台をWEB、1台をDBとして活用した。
余った1台は実験サーバーになってて何かうまく使いたかったが時間的に出来なかった。

やったこととして最初はサーバーへの接続確認。 確認できたところでミドルウェアやアプリケーションと言った、設定含めた各ファイルのバックアップ。

そこらへんは他の方にお願いした。
その間に最初の30分で僕は仕様の把握。
こんなかんじ。

f:id:igatea:20171022211745j:plain

前回予選の問題よりもかなり複雑だった。

その後はnginxのログをうまくデータとして扱ってくれるツールとしてalpを使用した。
alpから重いところ探してアプリケーションの改善に力を入れた。

一番重かったのが画像表示だったけど、メンバーが早々に画像がDBに入っているところを発見して、それをファイル出力にするということに取り組んでくれた。
ここらへんめっちゃありがたかった。
ので、自分は他を俯瞰的に。

ちょっと困ったのが前回の予選問題だとここを改善すれば凄く早くなるなってところが局所だったんだけど、今回は画像のところ以外分散していて困った。

有効だった対策

ベンチのスコアが変動しすぎて(ベンチガチャとか言われてた)どれが有効打になっているのか判断つきづらかった。
なので、これは凄くスコアが上がったっていうものと恐らくスコア上がっただろうっていう取り組みに分けてみる。

凄く上がった
  • SELECT句のカラムを徹底的に絞り込み
  • SQLの発行回数を少なくするように結合を活用

基本的にSELECT句のカラム指定の*は全て削除して、カラム指定するようにした。
getのhistoryとmessageは無駄にクエリを投げまくっていたのでアプリケーションコードを一部書き換えた。
ここらへんで1万、2万スコアが上がった。

恐らく上がった
  • 同じループを2回以上やっているところの解消
  • initialize時にSQLのキャッシュを作るために先にクエリを発行

正直スコアが変動しすぎて効果があったのかわからないレベルだった。

もっとうまくやりたかったなあって点

ベンチ設定ミス

設定を保存を押してなくてずっとベンチを回すサーバーを間違ってた。
てっきりチェックボックスつけとけばいいのかと思っててメンバーに指摘されて気付いた。
上がらないからとマージしなかったブランチをマージしていったらガンガン点数が上がって最高だった。

根本解決

クエリとかロジック周りは直したつもりだけど、それ以上スコアの上昇が見られなかった。
もっと根本的に改善しなければこれ以上上がらないなという壁を感じた。
例えば今回は3台のサーバーが用意されていたわけだけど、それをフルに使用することができなかった。
画像をWEBサーバーに書きだしたことにより、DBへの負荷は軽くなったんだけど、WEBサーバーがCPUが100%に張り付いている状態だった。
自分たちはWEBサーバー1台を余らせている状況だったので、もったいなかったなあと。
終わったあと話してたのはそのサーバーを画像配信サーバーにできたらよかったかもってこと。
今回メンバー的にインフラ、ミドルウェア周りに詳しいという人がいなかったので取り組むにはとても難しい状況だった。

得た知見

仕事上、ガンガンサーバー設定をいじるわけでも無いし、ましてやWindow Serverの方が触ることが多かった。
しかもあまりアプリケーション改善もDB周りへの配慮を実務でするということがなかったので物凄く経験になった。
これはまた別に記事を書きたいと思う。

最後に

凄く楽しいコンテストだった。
メンバーが色々サポートしてくれて、かなり自由にアプケーション側を弄っていけて最高でした。
一緒に組んでくれたごまさん、くーむさん、本当にありがとうございました!
めちゃくちゃ勉強になりました!

Dockerでnginx + php-fpm環境を立てる

最近ちょっとずつDockerを触っている。
手慣れると本当に爆足で環境構築できて、本当に便利だ。

今回はdocker-composeを使用してnginxとphp-fpmのサーバーを立ててみる。

nginx

まずnginxから。

docker-compose.ymlというファイルを作成し、下記のようにする。

version: '2'
services:
# localhost:8080
  nginx:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      - ./web:/web

これで、webというディレクトリ配下を見るnginxのコンテナが立ち上がる。

php-fpm

servicesのところに、下記を追加する

  php:
    image: php:fpm
    volumes:
      - ./web:/web

phpのときにweb配下のphpを読み込むようになる。

連携

nginxとphp-fpmを連携するとこんな感じになる。

version: '2'

services:
# localhost:8080
  nginx:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      - ./web:/web
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf # php-fpmのための設定の読み込み
    links: # host追加
      - php

  php:
    image: php:fpm
    volumes:
      - ./web:/web

追加したのはphp-fpmのためのnginxの設定ファイルの共有と、その設定ファイル内で使用するホストのための記述。

default.confは雑にこんな感じ。

server {
    index index.php index.html;
    server_name php-docker.local;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /web;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

fastcgi_passのphp:9000はlinksに書いたphpからホスト解決される。

データベースやXDEBUGを使えるか検証していないので、そこらへんを触ったらまた記事にしたいと思う。

ISUCON模擬練習会に参加した

とある技術コミュニティで行うことになった、ISUCON模擬練習会に参加した。
去年の予選問題を元に、擬似的にISUCONをゆるっとやってみた感じ。
主催者の方がベンチマークのポータルを立ててくれて、それを元にスコア上げて見るみたいなことをした。
本番と同じチームで行って、僕達はPHPで試してみた。
担当としてはざっくり下記。

流れ

覚えている感じ、チームでやった流れ

  1. SSHとかGitの設定
  2. MySQLのデータ、スキーマのダンプ
  3. MySQLのチューニング
  4. alpを入れてnginxのログ解析
  5. MySQLのスローログ出力
  6. 画面上から仕様の把握
  7. blackfire導入
  8. アプリケーションコードのチューニング

練習会を終えて

推測ではなく解析

まさにこれだなあと思った。
仕様見ててここらへんが重いんだろうなとは分かりつつも、本当かはわからないので。
ログは正直です。
ログを見てやばそうなとこから手をつけるとポイントが上がっていく実感を得た。

キャッシュの職人芸

問題の解説を改めて見てみて、うちのチームはキャッシュ周りに手を加えることができなかったので、本番までにそこらの問題を埋めたいなあと思ってる。
例えば予選問題だと、毎回正規表現を生成する処理がボトルネックとなっていたんだけど、それをうまくキャッシュするとか。

時間足らん

何時間も作業するとはいえ、やっぱり時間足らんなあと。
初めてなので手慣れてない感はあったので本番はもっとスムーズに勧められるかも?

仲間が心強い

ずっとWindowsを触ることのほうが多い職場だったのでLinuxが頓珍漢。
そんなとき仲間が心強かった。
いろんな処理を簡単にしてくれたり、問題を見つけてくれたり、相談できたり。
マジ感謝。

雑に雰囲気を感じられてよかった

時間を気にしつつ怒涛に環境を整えて進めていく感じが凄く焦るし面白かった。
序盤は正直動けなくて地蔵になりかけた(すまん)

これから

ISUCONのために練習ってことになってるけど、周りの知識を吸収できるし、めっちゃ面白いのでISUCON関係なくこれからもやっていきたい練習会だなと思った。

ゼロから始めるデータベース操作を読み始めた02

北海道に旅行に行っていたので、間が空いてしまった。
また学習しての気付きなどを書いていく。

各句の記述順序

  1. SELECT
  2. FROM
  3. WHRE
  4. GROUP BY
  5. HAVING
  6. ORDER BY

大雑把な実行順序

  1. FROM
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. SELECT
  6. ORDER BY

GROUP BY句では別名は使えない
SELECTやGROUP BYの結果の行の並び順はランダム

どちらにもかける条件は下記の方針で行うと分かりやすい

  • WHERE句=行に対する条件指定
  • HAVING句=グループに対する条件指定

ORDER BYを使用した時、NULLは先頭か末尾にまとめられる。
DBMSによってどちらにするか指定する昨日もある

INSERTで明示的にデフォルト値を挿入するにはDEFAULT

SELECTした結果をそのままテーブルにINSERTすることができる

INSERT INTO AlphaCopy (a, b, c)
    SELECT a, b, c FROM Alpha;

テーブル内行全削除(TRUNCATEのほうが基本は早い)

DELETE FROM テーブル名;
TRUNCATE テーブル名;

ビューは内部的にそのSELECT文を実行し仮想的なテーブルに見せるもの
サブクエリは更に使い捨てのビュー
サブクエリには原則的に名前をつける

必ず1行1列だけの戻り地を返す、スカラ・サブクエリ WHERE句での比較に使える

相関サブクエリ

SELECT a, b, c
    FROM Alpha AS A1
    WHERE a > (SELECT AVG(a)
                FROM Alpha AS A2
                WHERE A1.b = A2.b
                GROUP BY b);

文字列連結は||またはCONCAT関数
NULLを値へ変換する関数COALESCE

LIKEでのパターンマッチで使用できる文字列

  • % 0文字以上の文字列
  • _ 任意の1文字

IN句はテーブルを引数に取ることができる

続く

完全に忘れてんなーってのと知らんかったなーってのと。
ここまででひとつのテーブルに対してってのが多かったけど次からは結合関係になるのかな

ざっくりPHPカンファレンスメモ

PHPカンファレンスに参加してきた。 ざっくりしたメモ。

phpcon.php.gr.jp

OPCacheの最適化器の最適化

OPCacheの機能
バイトコードの最適化のお話

定数の最適化の挙動がすごいなーと。
もうコンパイラじゃん!ってなった

Lancersバージョンアップ

お姉さんが動いた!という喜びのコミット
目をつぶっていたNOTICEエラーを消して行った
別ブランチを作成してバージョンアップを行なって行った。
masterブランチの更新も走っていたので差分を取り込むのが大変であると。
Upgrads Shellだけでは動かなかった
自動で新しいメソッド、関数に変換するシェルを作成した(App::importをApp::usesに変換とか)

CakePHP1.3でできることを先にしておく
paramをdataにするとか

CircleCIでチェックし変更をする
CakePHP1.3と2.8を同居させることに成功した
Codeceptionで動作を担保する

phpのバージョンアップ -> カナリアテスト
CakePHPおんバージョンアップ -> 2バージョンを用意して書き換えて行く、UnitTestも2バージョン用意、Codeceptionがあるから安心

Realtime Messaging with Firebase

チームにはAPIエンジニア2
iOS/Android 1

2ヶ月でのリリース
なるべく作らない方針

microserviceとして切り出さない
クラウドサービスを使い倒す
スコープを削りきる

Firebase

Googleの提供するBaaS
モバイルに非常に特化して多機能

Firebase Realtime Database
Firebaseの機能の一つ

サンプル

{
    "messages" : {
        "1": {
        "text": "hello, firebase",
        "user": "sota1235"
    }
}

認証機能を追加したりできる

Rule

ノードに対して数字のみとか書き込み禁止とかできる

rulesには.writeにfalse、その中のroomsには.writeと.readにtrueでroomsだけに制限することができる

Ruleの適用

GUI or REST APIで設定
ハマりどころがあるらしくREST APIがおすすめ

スキーマ設計

Subscribe側を意識する 非正規化した方がいい場面もある
後方互換性を意識

配列がない

addすると自動でキーが振られて配列みたいに作成される

放送後はデータを読み込ませない
特定のライブIDが放送中かどうかのフラグを管理し、読み込みを許可する
root.child('alive_lives').child($live_id).val === trueとすることで他のノードの値を引っこ抜いて書きこみ、読み込みを制御できる
admin権限でルールを無視して放送中フラグを変更する

Write戦略

スマホからFirebaseへの書き込みは行わせない(APIを通す)
攻撃、認可など工数を考えたため
APIからEnqueue Job(Q4M)、Worker Process(PHP)がDequeue Job、Firebaseに投げる

FirebaseをSPOFににしない

野良SDKでkreait/firebase-phpをおすすめ

Read戦略

クライアントは受け取ったデータを表示するだけ(バリデーションはかけない)

スケーリング

Realtime Databaseはスケールアップできない
常用インスタンスと高負荷用インスタンスを分ける

料金は従量課金
何台インスタンスを立てていても通信しなければタダ
Firebase ProjectsとGCP Projectは1対1

Cloud Firestoreで変わった点

自動スケールアウトするようになった
SDKのI/Fがよくなった(Where的なことができるようになった)
RDよりLatencyが伸びる可能性がある

VS

新規プロダクトは基本Cloud Firestoreでよい
ReadWriteオペレーションが大量に発生する場合はCloud Firestoreの方がお金がかかる

何がよかったか

ユーザー体験
シンプルなアーキテクチャ
待機開発リリースを実現

運用、追加開発しづらいPHPアプリケーションに未来を与える方法

しやすい

やりやすいとは

  • テストコードがある
  • アプリケーションモニタリングがある
  • デプロイが容易(CIサーバー・1コマンド)

モニタリングを追加

New Relic見える化 各コントローラー、エンドポイントのレスポンスの記録を確認できる

新規機能追加

既存のアプリケーションを触らず、別のアプリケーションを立てて修正して行く Slim 3.xとALBを使用して追加していく

セッション

session管理はデータベースに書き込むようにした