地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

CI/CD Conference 2023でインフラのCI/CDについてお話しました #CICD2023

event.cloudnativedays.jp

3/20に開催されたCI/CD Conference 2023にオンラインで登壇しました。当日は会場へは行けませんでしたがTwitterなんかを見ていてもすごい盛り上がりだったのを感じました。

event.cloudnativedays.jp

speakerdeck.com

なぜ登壇しようと思ったのか

cloudnativedays.medium.com

カンファレンステーマである「Continuous 〜ともに回す高速なアプリケーション開発ライフサイクル〜」への共感があったからでした。CI/CDやCloudNativeはインフラ領域を想像することが多くそういったイベントはインフラエンジニアが多く参加するものに思えていました。上記本文を引用するとクラウドネイティブはインフラだけの話ではありません。Kubernetesやコンテナはその一要素に過ぎず、アプリケーションを載せてビジネスや社会を加速させるツールになって初めて成立するものです。と書かれています。ビジネスや社会を加速させるにはインフラ領域を触るエンジニアのみがそれらの技術を触るだけではなくアプリケーションエンジニアと協同して行く必要があると考えていました。発表の概要としてはインフラのCI/CDを改善することでアプリケーションエンジニアとともにインフラを作っていく環境を作っていく話をしました。その中でもともに作っていくという部分にフォーカスして話すことができテーマに沿った発表ができたかなと思っています。

その他

8月福岡!11月東京!勢いがすごいですね!福岡ネタを作って行きたい!

次回はオフラインで参加をしてイベント後の懇親会まで参加して交流をできればなと思います!

YAPC::Kyoto 2023で障害対応について登壇してきた #yapcjapan

yapcjapan.org

YAPC::Kyoto 2023で登壇してきました!これまでオフラインイベントで登壇といえば少人数でのイベントくらいでこの規模で話すのは初だったので緊張してましたが始まってしまえばとても楽しめて話せてとても良い機会になりました!ありがとうございます!ほぼ満席(多分)で発表後の質問やTwitterでも反響があってとても嬉しかったです。トレーニングなどのコスト周りの内容はもう少し補足が必要だったなと思ったのでブログなり資料修正をして追記しようかなと思います。

speakerdeck.com

セッションもとても刺さるものが多くて学びや今後のやる気に繋がるものが多くてよかったです。(詳細については会社のテックブログに記載しようかなと思います。)。懇親会ではネットの向こう側だった方と多く話す機会を得ることができ場を作って頂いたHelpfeelさんにはとても感謝です。

その他

YAPCラーメンもしてきました

フォーと念願だった夜鳴きそば

観光もしてきた

最後に

次は広島ということなので1年間実績を上げてまた登壇できればなと思います!

【nginx】proxy_ignore_client_abortとか499の話

HTTP 499 エラーはクライアントがHTTPリクエストを送った後にレスポンスを待たずに切断(FIN)を送った場合にnginxがログに出力するステータス。

実験

以下のように意図的に30秒くらい時間がかかるwebサーバを用意しておく

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("recv")
    hello := []byte("Hello World!!!")
    time.Sleep(time.Second * 5)
    _, err := w.Write(hello)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("end")
}

func main() {
    http.HandleFunc("/hello", helloHandler)
    fmt.Println("Server Start Up........")
    log.Fatal(http.ListenAndServe("localhost:8081", nil))
}

nginxを用意する。proxy_passさえ用意されていればなんでもよい

worker_processes  1;

events {
    worker_connections  1024;
}


http {
    default_type  application/octet-stream;
    sendfile        on;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    keepalive_timeout  65;
    server {
        listen       80 reuseport;
        server_name  localhost;

        location / {
        proxy_ignore_client_abort off;
            proxy_pass   http://127.0.0.1:8081;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

これに対してcurlを実行してsleepしてる最中にcurlを止めてみる

$ curl localhost/hello

そうするとnginxには499のログが出力される

127.0.0.1 - - [23/Feb/2023:12:56:35 +0900] "GET /hello HTTP/1.1" 499 0 "-" "curl/7.81.0" "-"

この時にnginxでは何が起きているのかを見ていく。upstream、クライアントとの通信はngx_http_upstream_check_broken_connectionで管理している。ここでは最後にクライアントとのコネクションが繋がっているかを確認していて切断済みならngx_http_upstream_finalize_requestを呼び出す。どうやって実現しているかというとnginxが使っているepoll_wait(2)がTCP でピアがシャットダウンしたことを検出したかどうかで判断している。ユーザーとのコネクションがない状態でupstreamとコネクションがつながっている状態=499となる

static void
ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
    ngx_event_t *ev)

    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {
        if (!u->cacheable && u->peer.connection) { // ユーザーとのコネクションが
            ngx_log_error(NGX_LOG_INFO, ev->log, err,
                        "epoll_wait() reported that client prematurely closed "
                        "connection, so upstream connection is closed too");
            ngx_http_upstream_finalize_request(r, u,
                                               NGX_HTTP_CLIENT_CLOSED_REQUEST); 
            return;
        }

ngx_http_upstream_finalize_requestではいろいろじょうけんを見つつupstreamとの接続を切断する処理となっている。

github.com

static void
ngx_http_upstream_finalize_request(ngx_http_request_t *r,
    ngx_http_upstream_t *u, ngx_int_t rc)
{
        ngx_close_connection(u->peer.connection);

これだけをみるとわかるがupstreamにはTCP-FINが飛んでくるだけになる。つまり受信側の切断検出は、recv()がlength==0で返ってくるかなどを見てあげる必要がある。普通にアプリケーション書いてるだけでは実はアプリケーションは継続を続ける(はず)。今回の場合はFINで閉じるのでsend(2)してれば2回目で気づける。(send()自体はカーネルの送信バッファにデータコピー)

あとはEPOLLRDHUPというフラグをepollで検出するようにアプリケーションを実装するとよさそう。webアプリケーションがそんな実装をしているケースはあるのだろうか?ちょっと調べてみたがなさそうだったのでどうなんだろう?という気持ち

ymmt.hatenablog.com