maco's life

主にエンジニアリングと読書について書いていきます。

nginxでメンテナンスモード時の503と通常時の503でhtmlを分ける方法

nginxでメンテモードを実装した。機能としては/tmp/503ファイルがあったら、nginxがメンテナンス画面を出すというもの。

最初は503のstatusだったらメンテナンス画面をだすという実装だったけど、メンテナンス以外の時で503の場合は別のviewを出したかったので下記のようなconfigファイルにした。

server {
  listen 80;
  server_name  _;

  error_page 503 @503_error;
  set $maintenance 0;

  if (-f "/tmp/503") {
    set $maintenance 1;
  }

  if ($maintenance = 1) {
     return 503;
  }

  location @503_error {
    root /etc/nginx/html;
    internal;
    expires 0;
    if ($maintenance = 1) {
      rewrite ^(.*)$ /maintenance/503.html break;
    }

    if ($maintenance != 1) {
      rewrite ^(.*)$ /503.html break;
    }
  }

  location / {
    ...
  }

ポイントとしては、

  • 503のstatusの時はカスタムエラーページを出せるように error_page 503 @503_error;を設定すること
  • location @503_errorの内部では
    • キャッシュさせないようにexpires 0を指定する
    • ifとrewriteを使いviewの出し分けを実装しているが、302になってしまうとSEO的に困るのでinternalを指定して内部的な処理にしているところ

かなと。

最初 location /503.htmlのような指定を使っていたけど、これだと上手くlocationに入らないケースがあって、そこは正直良く挙動の理解ができないです..

@でlocationの指定を行うようにしたら意図した挙動になりました。うーん違いがよくわらん

Nginxのngx_http_sub_module#sub_filterの挙動

nginxには定義した特定のcontent-typeの時にレスポンスの中の文字列を書き換えるsub_filterという機能があります。

こちらですね。Module ngx_http_sub_module

できることは文字列の書き換えで、

sub_filter 'example.com' 'www.example.com'; 

と定義するとレスポンスの中のexample.comという文字列をwww.example.comに書き換えてくれます。sub_filter_onceのon,offで繰り返し置換するかどうかの設定をすることができ、sub_filter_typesで置換の対象とするcontent-typeを複数指定できます。利用シーンとしては、特定のURLにアクセスしたときだけでリクエストの中身書き換えたいとか、アプリケーション側の変更無しでサイトドメイン等変更したい時などが考えられます。

sub_filterを使っていて少しハマった挙動としては、例えば hoge.example.comfuga.example.comwww.example.comに書き換えたいという時は、

sub_filter `hoge.example.com` `fuga.example.com`;
sub_filter `fuga.example.com` `www.example.com`;

みたいな書き方をするとhoge.example.comfuga.example.comに書き換わった後に、書き換わったfuga.example.comwww.example.comには書き換わらないのでちゃんと

sub_filter `hoge.example.com` `www.example.com`;
sub_filter `fuga.example.com` `www.example.com`;

と丁寧に書いてあげる必要があります。またgzip圧縮指定をしているとレスポンスの中身の書き換えが正常に行われないので置換したいURLへのアクセスには proxy_set_header Accept-Encoding "";を指定のlocationなりにつけてあげる必要があります。

「データベース技術 実践入門」読了

GW中どうせなら積んでいた本を読もうと思い、2012年ごろに出版された「データベース技術 実践入門」を読んだ。書いてあった内容は、実践入門というより広く薄い内容ではあったけど、自分が気にしていなかったことや知らなかったことなどもあったし、技術の振返りとして読んで面白かった。印象に残ったことは下記の内容。

  • innodb_log_file_size
    • サイズ大: 更新処理の劣化はないが、クラッシュ時のリカバリが低速になる
    • サイズ小: チェックポイントが頻繁に起こるため更新処理は劣化するが、クラッシュ時のリカバリが高速になる
  • クエリが詰まってレスポンスが一時的に返らなく現象には「ストール」という名称がある
  • データーベースのコピーを取った時にデータの一致性を見るにはchecksumを見る方法もある
  • ステートメントベースでのレプリケーションだとUUID()NOW()がそのまま実行されるため、必ずしも同じデータになるとは限らない
  • mysqlbinlogを使う場合に1つのトランザクションが複数のbinlogにまたがる状態がある。mysqlbinlogは最後にROLLBACKを行うので、中途半端な状態になることがある
  • binlogファイルの削除はrmで削除するとmysqlからは認識したままになるので、PURGE MASTER LOGS TOで行う
  • MyISAMでバックアップを取る時は、データベースにロックを掛けた後にsyncを実行して、ディスクに書き出された状態でバックアップを行う
  • 単純なSELECT文を実行するときには、HandlerSocketPluginを使うことでクエリ実行時の無駄なコストを削減できる
  • B-Treeはブランチにデータを持つので、カウントの処理がB+Treeとくらべて遅い
  • MySQLの実装思想について
  • MySQL-Pluginの実装方法まとめ

これらが気になった点。本を読んでいてMySQL5.6が出たぐらいのころの本なので現状とは少し違ってきている点もあるかもしれない。だけど時代的背景からみるMySQLの使い方や基本的な内容の振返りなどまんべんなく見れて良かった。

フロントエンドについて全く詳しくない自分がReactを使ってみた感想

ここ2ヶ月色々あって毎日終電まで作業していたせいもあってブログの更新がなかなかできてなかった。まだ山を超えてないのだけれど息抜きとちょっと気になることがあったのでブログ書こうかなとなった次第です。

先日とある時に、「Reactちょっと触ってみました」みたいな会話から「Reactどうです?」って聞かれて「C#みたい雰囲気でしっくりきた」って返したのだけれど、思い返すとまったく言いたいこと伝えられてないし、意味わからなすぎるよ!!ってなったので冷静に思っていることを文字で書きます。

Webのフロントエンドはほっっとんど触ったことがなく、ちょっと触れたよーっていうものだとjs, reactぐらいなので、その両方からみた時にReactがいいなーっておもったところは以下でした。

  • classとして扱えスコープが小さいので、同名の関数がぶつかったりせずにオブジェクト思考的に扱える
  • componentとしてviewを小さなパーツで区切れるので、dryなコードを作りやすく扱いやすい
  • webpackを使ってビルドできるので、環境依存になりにくい
  • eventhandlerなども書きやすかった(これは完全な主観)

みたいなところがあるかなと。バックエンドの出身の私から見るとオブジェクト思考的に扱えて、LL言語のような手軽さでコーディングできるのですごくしっくりきて入りやすいなって思いました。

みたいなことを伝えたかったのですが、どうも言葉がでてこなかった…変に緊張したりするとたまにやっちゃうので意識的に気をつけるようにしていきたいなと。

これからも頑張るぞい

伝え方が9割!?

伝え方が9割という本を読んだ。

確かに伝え方を学ぶことで、選択が変わるということがよく理解できる本だった。

さっそく会社で実践してみたら、「めっちゃいいと思う。けどなんか見ているものが大きすぎる」みたいな捉えられ方をしてしまったので、使う場面とかも気にしたほうがよさそう。 ※タイトルは本を読んで得たノウハウをそのままつけた

MyISAMの暖気してくれる君をgoで書いた

github.com

雑ですが、必要に迫られて作ってみました。

作成の経緯

RDS上でMyISAMの暖気がしたかったのです。RDSだとデータディレクトリcatでOSのキャッシュに載せるとかできないからクエリなどでできないか調べつつ作りました。先人の方々で作っていらっしゃる方いないかなと思って調べてたのですが、やはりInnoDBを使われているケースが殆どでMyISAMはなかなか見つかりませんでした。InnoDBの暖気はmysql5.6から簡単になったし本当に最高な感じしかないですね。(※1)

あと久しぶりにgolangを学びも兼ねて書いてみたくなったので、言語はgoを用いました。

MyISAMの暖気

以下の2つを実行したら再起動直後のウォームアップとして実行しました。

  • LOAD INDEX INTO CACHE <table_name> ※2
    • インデックスをmysqlのキャッシュに載せる
  • SELECT * FROM <table_name>
    • データをOSのキャッシュに載せる

はじめはLOAD INDEX INTO CACHEだけでいけるのかなと思ったけど、ある程度件数があるデータの場合インデックスを使ったクエリでもデータの読み込みに時間がかかったため、SELECT * FROM <table_name>を実行しデータがOSキャッシュにのるか試してみました。結果、約3千万レコードあるテーブルで15秒ちょっとかかってたクエリが1秒で返ってくるようになったし、SELECTでもちゃんとのってる感じはする…。

RDSとEC2 on MySQLの場合と2パターンで試したけど、

RDS EC2 on MySQL
読み込み速度 15秒 -> 1秒 15秒 -> 0.03秒
メモリ インスタンス作成時と変わらず cached memoryが2GB増

みたいな感じでRDSの方はちょっとどうみたらいいかわからなかった。AWSコンソール上でみれるRDSのメモリは純粋にmysqlで使われるメモリのみなのだろうか。一方MyISAMの場合EC2 on MySQLの方がパフォーマンス的にもメモリの使われ方的にも顕著な感じでした。

実装したもの

簡単先述したクエリ2つのクエリを各テーブルごとにgoroutineとして実行してくれるツールを作りました。使い方などはREADMEをご参照ください。

今後

  • 現状engineがMyISAMなテーブル全部に対して実行してるけど、きっと全部キャッシュに乗らないケースがあるだろうからtable名指定とかもできるようにしないとかも。
  • golangについて素人なので、実装の改善やらないと。
  • これでよかったの感ある。良い暖気の方法があれば知りたい。

参考

主にgoで実装するにあたり下記のドキュメント・記事を参考にさせていただきました。ありがとうございました。

mackerel-agentをECSで使う

ECSインスタンス立ち上げる時のuserdata内にmackerelのインストール処理を書いたのですが、今後設定変更する時にインスタンス作り直すか、またはECSインスタンス内にsshして設定を変えないといけなかったりなどで、インスタンスの状態を管理するのがイマイチでした。調べたらmackerel-agentのdockerコンテナがあったのでそちら使えばインスタンス上に状態持たなくてよさそうみたいな考えにいたり、ecsのタスク定義の設定方法備忘録書きました。

(といってもdocker runのコマンドをタスク定義の設定に落とし込んだだけ)

1. ECSタスク定義

適当なタスク名をつける f:id:Maco_Tasu:20170307181515p:plain

2. ECSインスタンス上のボリューム設定

ここに設定したものがコンテナ内へのマウントポイントとして使える。マウント先は公式のドキュメント通りに指定。 f:id:Maco_Tasu:20170307182250p:plain

3. コンテナの設定

docker hubからimageを取得する場合は下記のスクショのような感じ。tagは適宜変えてください。 f:id:Maco_Tasu:20170307181603p:plain

  • apikey: mackerelのapi key
  • auto_retirement: false
    • コンテナが消えた際も自動退役させないようにする
  • enable_docker_plugin: true
    • docker-pluginを有効にする f:id:Maco_Tasu:20170307181850p:plain

2で設定したボリューム設定をコンテナへマウントする f:id:Maco_Tasu:20170307182132p:plain

これで設定はおわり。

余談

esc-deployで本番にデプロイすると新旧のタスク分mackerelのグラフに反映されて古いコンテナの項目がしばらく残り続けて不安な気持ちになりますが、公式ドキュメントにもあるように

コンテナを新規に起動すると自動的にグラフに項目が追加され、コンテナを終了すると数時間後に自動的にその項目が見れなくなります。

とのことなので特に心配しないでも良さそうです。