Nginxのreload時にmetricsはどう保持されるのか
TL;DR
NginxはreloadされてProcessが複数になった場合にも正しくMetricsが共有される
Metricsに関してはReloadを気にしなくて良い
背景
HAProxyではreload時に、古いProcessへのトラヒックが一時的に残っている場合に、新しいProcessのMetricsしか取れないという問題があった。
Envoyではこの問題をshared memoryで解決している
Nginxではどのように解決しているのか情報が見つからなかったので調べる。 Nginxで統計はhttp_stub_status_moduleを用いて収集する事が出来る。 データの取得自体は、 ngx_stat_active といった変数から呼び込んでいる。
ap = *ngx_stat_accepted;
hn = *ngx_stat_handled;
ac = *ngx_stat_active;
rq = *ngx_stat_requests;
rd = *ngx_stat_reading;
wr = *ngx_stat_writing;
wa = *ngx_stat_waiting;
b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac);
b->last = ngx_cpymem(b->last, "server accepts handled requests\n",
sizeof("server accepts handled requests\n") - 1);
b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq);
b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n",
rd, wr, wa);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = b->last - b->pos;
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
この ngx_stat_active などのメモリはどうなっているのかというとnginxのshared memoryデータ構造の中で確保されている。
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
そして、このshared構造体は、全てのProcessで共用なので、reload時にも同じメモリが利用される。 つまり、reload時にもMetricsは問題無く加算される。 Nginxのshared memoryはmmapを用いて以下のように確保されている。
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
shm->addr = (u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
return NGX_ERROR;
}
return NGX_OK;
}
そして、Nginxのreloadは以下のように行われる。
NginxはSIGHUPを受け取るとngx_reconfigureを1に設定する。
そうするとmain Processはngx_spawn_processをconfigと一緒に実行しその中でforkが実行される。
forkで作成されたProcessはshread memoryを共有するので、Nginxはreload前と後で、同じshared memory空間を利用する。
これにより、Nginxはreloadされた場合にも、metricsのMemoryを共有出来ている。
Metricsが少なすぎる気もするがこれは、OSS版のNginxの仕様なので仕方ない。
LBaaSを作るにはMetricsが少なすぎる。
これは、商用版では提供さており、1.13.10からOSS版から削除された機能である。
過去に公開されていたnginxのMetricsからmoduleを取り出したcodeもある。
このCodeを使うとupstreamのMetricsなんかもとれたりする。
このCodeでも同様にsharedメモリを使っている。