仕事でこのfailsは何をfailsとしてカウントしてるんだっけ?となったので調べった。
公式を読む。max_failsは指定された回数失敗するとupstreamから外されるということが書いてある。この失敗とは?についての言及はここには無い
max_fails=number
sets the number of unsuccessful attempts to communicate with the server that should happen in the duration set by the fail_timeout parameter to consider the server unavailable for a duration also set by the fail_timeout parameter. By default, the number of unsuccessful attempts is set to 1. The zero value disables the accounting of attempts. What is considered an unsuccessful attempt is defined by the proxy_next_upstream, fastcgi_next_upstream, uwsgi_next_upstream, scgi_next_upstream, memcached_next_upstream, and grpc_next_upstream directives.
で、よくわからないのでぐぐってみると以下の記事がヒットした。内容を読むとconnectionタイムアウトかread/writeのタイムアウトが失敗とみなすらしい。加えてL7レベルでの失敗もproxy_next_upstreamに定義することで追加することも出来る模様。
recruit.gmo.jp
errorは対向のサーバがダウンしている時などが該当します。timeoutはproxy_connect_timeout、proxy_send_timeout、proxy_read_timeoutいずれかの閾値が該当します。invalid_headerは様々な理由がありそうですが、対向のサーバが正常に動作していないといったことが考えられます。
またproxy_next_upstreamでhttp_500, http_502, http_503, http_504が定義されている時は、これらも失敗とみなされます。
この話を以下のような最小構成のconfigから見つけるのは少し難しいかもと感じてしまった。(自分で0から書くならよいが初めて触ったconfigだと自分の場合は気づくのに時間がかかりそう...)
upstream app {
server 127.0.0.1:10080 max_fails=100 fail_timeout=10;
server 127.0.0.2:10080 max_fails=100 fail_timeout=10;
}
server {
listen 80 default_server;
location / {
proxy_pass http://app;
}
}
おまけ
暇(じゃないけど)なのでソースを読んで見る
max_failsの判定処理は以下
static ngx_int_t
ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
{
if (peer->max_fails
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
continue;
}
peer->failsが失敗数peerはupstreamとのコネクションなど(結構巨大)を管理している構造体。failsをインクリメントしてるのは以下。
void
ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
if (state & NGX_PEER_FAILED) {
now = ngx_time();
peer->fails++;
peer->accessed = now;
peer->checked = now;
if (peer->max_fails) {
peer->effective_weight -= peer->weight / peer->max_fails;
if (peer->fails >= peer->max_fails) {
ngx_log_error(NGX_LOG_WARN, pc->log, 0,
"upstream server temporarily disabled");
}
}
NGX_PEER_FAILEDになるケースは以下
static void
ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_uint_t ft_type)
{
ngx_msec_t timeout;
ngx_uint_t status, state;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http next upstream, %xi", ft_type);
if (u->peer.sockaddr) {
if (u->peer.connection) {
u->state->bytes_sent = u->peer.connection->sent;
}
if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403
|| ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
{
state = NGX_PEER_NEXT;
} else {
state = NGX_PEER_FAILED;
}
u->peer.free(&u->peer, u->peer.data, state);
u->peer.sockaddr = NULL;
}
NGX_PEER_FAILEDに入るかどうかはngx_http_upstream_next()の呼び出し元で決める模様。ngx_http_upstream_next()を呼んだ時点で403と404以外はfailsはインクリメントされる。
static ngx_int_t
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
if (u->peer.tries > 1
&& ((u->conf->next_upstream & mask) == mask)
&& !(u->request_sent && r->request_body_no_buffering)
&& !(timeout && ngx_current_msec - u->peer.start_time >= timeout))
{
ngx_http_upstream_next(r, u, un->mask);
return NGX_OK;
}
指定できる値は以下の構造体で管理されているので実は書き換えることで410とか599とか謎なステータスで次のupstreamへ処理を移すことも可能
static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = {
{ 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },
{ 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },
{ 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ 0, 0 }
};