nginx mirror module の罠?の話
注意
- nginx v1.24.0で確認しています。
- ソースコードを呼んだとかではないので、利用する際には十分検証を行ってください。
内容
Nginx には、ミラーモジュール ( ngx_http_mirror_module ) というモジュールがあり、
server { listen :80; location /mirror { internal; proxy_pass http://origin2; } location / { mirror /mirror proxy_pass http://origin1; } }
このような ForwardProxy の設定を行うことで
[Client] -> [nginx] -+-------------------> [origin1] `- - -(mirror)- - -> [origin2]
このように origin1
にリクエストを行いつつ、同様のリクエストを origin2
にも行い、origin2
のレスポンスは無視することができるというものです。
しかし、実際に使ってみると罠?と思われる挙動が合ったので、ここにメモしておきます。
🪤1:ミラー側が遅延するとレスポンスが遅延する
以下のように origin1 と origin2 のレイテンシが異なる場合、遅い方に引きずられます。
[Client] -> [nginx] <---+--(100ms)--> [origin1] +--(300ms)--------------> [origin2]
負荷試験など、遅いレイテンシに引きずられたくない場合は、以下のように、mirror側にタイムアウト設定を実施すると回避できます。
server { listen :80; location /mirror { internal; proxy_pass http://origin2; proxy_connect_timeout 100ms; proxy_read_timeout 100ms; proxy_send_timeout 100ms; } location / { mirror /mirror proxy_pass http://origin1; } }
🪤2:ミラー側のKeepAliveが有効にならない
以下のように、origin1 / origin2ともにKeepAliveの設定を設定しているにも関わらず、
server { listen :80; location /mirror { internal; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://origin2; } location / { mirror /mirror proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://origin1; } }
以下のように、mirror側のKeepAliveが有効にならず都度接続増えてしまう場合、
[Client] ---> [nginx] <-+--(KeepAlive有効)----> [origin1] +--(KeepAlive無効)----> [origin2]
mirrorの中ではKeepAliveが有効にならないようなので、別サーバとしてKeepAliveを有効にして、proxy_passの先に指定することで回避することが可能です。
upstream nginx_proxy_socket { unix:/var/run/nginx-proxy.sock } server { listen unix:/var/run/nginx-proxy.sock location / { internal; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://origin2; } } server { listen :80; location /mirror { internal; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://nginx_proxy_socket$request_uri; } location / { mirror /mirror proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://origin1; } }
HTTP/gRPCハイブリッドなアプリ
gRPCはHTTP/1.1で受けれないのでHTTP/2としてリクエストをハンドリングできるようにしてやって、HTTP/2かつapplication/grpcの場合にgrpc側で処理してやるようにすればOK
addr := ":8080" if v, ok := os.LookupEnv("BIND"); ok { addr = v } grpcServer = grpc.NewServer() reflection.Register(grpcServer) mux := http.NewServeMux() mux.Handle("/live", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte{'o', 'k'}) })) httpServer = &http.Server{ Addr: addr, Handler: h2c.NewHandler( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { grpcServer.ServeHTTP(w, r) } else { mux.ServeHTTP(w, r) } }), &http2.Server{}), } httpServer.ListenAndServe()
M1Mac で gvm+arm版goを利用する
ローカル環境のGoバージョン管理方法としてgvmを利用しているがM1 Macに変えたタイミングでgvm経由でインストールしたGoのdelveがうまく動かなかったりが発生していたので、rosetta2経由でintelバイナリを利用するのではなく、arm版バイナリを利用することで問題が解消した。
以下にgvm経由でにarm版goを利用する方法をメモしておく
$ cd /tmp $ wget https://go.dev/dl/go1.18beta1.darwin-arm64.tar.gz $ tar zxvf /tmp/go1.18beta1.darwin-arm64.tar.gz $ mv go ~/.gvm/gos/go1.18beta1-arm $ cp ~/.gvm/environments/go1.17.2-arm ~/.gvm/environments/go1.18beta1-arm $ vim ~/.gvm/environments/go1.18beta1-arm :%s/1.17.2-arm/1.18beta1-arm/g $ mkdir ~/.gvm/pkgsets/go1.18beta1-arm
自分用 PC環境メモ
ゲーミングPC(自作)
- Motherboard
- B550M Pro4
- CPU
- Ryzen5 5600x (3.7-4.6GHz / 6C/12T/ Zen3 / AM4 / 65W)
- Memory
- CFD CX1 (DDR4-3200 16G x2)
- VGA
- Inno3D GEFORCE RTX 3070 ICHILL X3
- Storage
- SDSSDHII-480G-J26C
- CT525MX300SSD1
- SP240GBSS3S70S25
- Keyboard
- Mouse
- Case
- CoolerMaster MASTERBOX E500L
- PSU
事務処理&開発用
- Motherboard
- ASRock Z370M Pro4
- CPU
- Memory
- Crucial CT8G4DFS824A (DDR4-2400 8GB)x2
- Storage
- OCZ VTX4-25SAT3-256G
- Keyboard
- TK-P05FBK
- Mouse
- Logicool G700s
- Case
- IN WIN BK623
- PSU
KRPW-SXP400W(400W / 420W Peak)
\ Line +3.3V +5V +12V -12V +5Vsb MaxA 17A 14A 33A 0.3A 2.5A MaxW > 90w 396W 3.6W 12.5W
ラップトップ
MacBookPro 15inch 2018
その他
- Monitor
- MonitorArm
- ヘッドフォン
Spring + Webauthn4jでWebAuthnやってみる
年末年始の時間を使ってYubicoのWebAuthnServer使ってWebAuthnのサンプルアプリを作ろうといろいろ試行錯誤してたら、
Web+DB PRESS Vol.114でWebAuthnの特集やってたので、成果物としてはただ写経しただけになってしまった。
今度Yubico/WebAuthnServer使ってリベンジしてみる
【メモ】SpringOne2019報告会スライドまとめ
SpringOne Platform 2019概要 + Resilience4j + LTした話
Spring Initializrをハックする
// todo
Let's イベント駆動 on Spring Cloud Stream
// todo
www.slideshare.netSpring 18年の歴史
www.slideshare.net
Spring HATEOAS
Pack to the future
// スライド見つけれないマン
GKEからPubsubへの接続でタイムアウトが発生する件
このアプリをビルド&AlpineのDockerイメージ化したものをGKEで動かして、Topicを取得するときに(内部的にはPubSubに接続する時)タイムアウトエラーで落ちてしまう現象が発生した
$ cat Dockerfile FROM alpine:3.8 RUN apk add bash tree pstree ENTRYPOINT [ "/opt/pubsub" ] $ cat pubsub.go package main import ( "cloud.google.com/go/pubsub" "context" "fmt" "os" "time" ) func main() { projectName := os.Getenv("PROJECT") topicName := os.Getenv("TOPIC") subscriptionName := os.Getenv("SUBSCRIPTION") ctx := context.Background() ctx, _ = context.WithTimeout(ctx, 5*time.Second) client, err := pubsub.NewClient(ctx, projectName) if err != nil { panic(err) } topicExists, err := client.Topic(topicName).Exists(ctx) if err != nil { panic(err) } fmt.Println(topicName, topicExists) subscription := client.Subscription(subscriptionName) subscriptionExists, err := subscription.Exists(ctx) if err != nil { panic(err) } fmt.Println(subscriptionName, subscriptionExists) go func() { err = subscription.Receive(ctx, func(rcvCtx context.Context, message *pubsub.Message) { fmt.Println("RCV", message.ID, string(message.Data), message.Attributes) }) }() time.Sleep(500 * time.Millisecond) if err != nil { panic(err) } fmt.Println("Wait for signal...") sigCh := make(chan os.Signal, 1) signal.Notify( sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) fmt.Println("SignalRcv ->", <-sigCh) }
結論結論としては、Alpineに ca-certificates がなかったため、Pubsubのエンドポイント ( https://pubsub.googleapis.com )に接続しようとして接続できなかった模様。
なので apk add ca-certificates してパッケージをインストールすることでタイムアウト(に見えるhttps接続エラー)は出なくなった。