技術的な話題 に関する投稿を表示しています

grpc-go の interceptor を理解した

元実装はここ
GitHub - grpc/grpc-go: The Go language implementation of gRPC. HTTP/2 based RPC

Interceptor の例としてはここ
GitHub - grpc-ecosystem/go-grpc-middleware: Golang gRPC Middlewares: interceptor chaining auth logging retries and more.

要はなにかというと Laravel をやっていたときに使っていた Middleware と自分では理解した。
Middleware - Laravel - The PHP Framework For Web Artisans

リクエストを処理する前後に、任意の処理を差し込めるもの。

リクエストを中断したり、ウェイトしたり、前処理したり。
あるいはリクエスト処理の後に、かかった時間やステータスについてのメトリクスを取ったり、といったことができる。

これはサーバだけでなく、クライアント側にも差し込めるので、クライアントリクエストする前に何処に対してリクエストしようとしているか、とか、なんか色々できそうではある。

自分でも書いてみる。

func AccessLogUnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		clientIP := "unknown"
		if p, ok := peer.FromContext(ctx); ok {
			clientIP = p.Addr.String()
		}

		ua := ""
		if md, ok := metadata.FromIncomingContext(ctx); ok {
			if u, ok := md["user-agent"]; ok {
				ua = strings.Join(u, ",")
			}
		}

		ctxzap.AddFields(
			ctx,
			zap.String("access.clientip", clientIP),
			zap.String("access.useragent", ua),
		)

		return handler(ctx, req)
	}
}

これは UnaryServerInterceptor で、つまりは単一リクエスト用のもの。
peer を使ってリクエスト元 IP と Metadata に入っている UserAgent を取得し、それを ctxzap を使って追加フィールドとして設定しておくもの。

なので go-grpc-middleware の grpc_zap.UnaryServerInterceptor が設定されていないと使えない。(そこで ctxzap が設定されるので)
go-grpc-middleware/logging/zap at master · grpc-ecosystem/go-grpc-middleware · GitHub

peer はここ。リクエスト元についての情報が簡単に入っている。
grpc-go/peer at master · grpc/grpc-go · GitHub

Metadata についてはここ。ざっくりとは HTTP Header みたいなやつ、と理解したけど大体あってそう。
grpc-go/grpc-metadata.md at master · grpc/grpc-go · GitHub

ctxzap は先に出した go-grpc-middleware で定義されるもの。
go-grpc-middleware/logging/zap/ctxzap at master · grpc-ecosystem/go-grpc-middleware · GitHub

gRPC リクエストの context に zap.Logger を出し入れできるもので、リクエストごとに Logger が設定されているような状態にできる。
ので、例えばリクエストの認証情報を詰めたりとかできて便利ちゃん。

実際に組み込もうとするとこんな感じ。

func NewGRPCServer() *grpc.Server {
	return grpc.NewServer(
		grpc_middleware.WithUnaryServerChain(
			grpc_zap.UnaryServerInterceptor(zap.L()),
			interceptor.AccessLogUnaryServerInterceptor(),
		),
	)
}

で、この interceptor を使った gRPC サーバに対してリクエストを送ってみると、こんな感じにログがでる。よさそう

{
  "level": "info",
  "ts": 1562940752.853247,
  "caller": "zap/server_interceptors.go:40",
  "msg": "finished unary call with code OK",
  "grpc.start_time": "2019-07-12T23:12:32+09:00",
  "system": "grpc",
  "span.kind": "server",
  "grpc.service": "helloworld.Greeter",
  "grpc.method": "SayHello",
  "access.clientip": "127.0.0.1:65516",
  "access.useragent": "my-user-agent/1.0.0 grpc-go/1.19.1",
  "grpc.code": "OK",
  "grpc.time_ms": 0.3019999861717224
}

ちなみにクライアントから UserAgent をどうやって設定するかはこうする。

client, err := grpc.DialContext(ctx, address, grpc.WithUserAgent("my-user-agent/1.0.0"), ...)

後ろに grpc-go/1.19.1 的な文言がついてしまうのはそうなんだけど、まあ、いいんじゃないかな。


あと余談なのだけど grpc-go と go-grpc-middleware とで grpc と go が入れ替わってていつもどっちだ!?!?となる


ワンソースでドッと試したのはこんなかんじ。
example にある helloworld な proto をそのまま利用するとお手軽簡単に gRPC 周辺の様子みれて便利。

package main

import (
	"context"
	"net"
	"strings"
	"time"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
)

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	ctxzap.Extract(ctx).Info("Received", zap.Any("in.name", in.Name))
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
	port := "127.0.0.1:13000"

	l, _ := zap.NewDevelopment()
	zap.ReplaceGlobals(l)

	// server
	go func() {
		logger := zap.L().Named("Server")

		listener, err := net.Listen("tcp", port)
		if err != nil {
			logger.Error("failed to listen: %v", zap.Error(err))
		}
		defer listener.Close()

		s := grpc.NewServer(
			grpc_middleware.WithUnaryServerChain(
				grpc_zap.UnaryServerInterceptor(logger),
				accessLogUnaryServerInterceptor(),
			),
		)
		defer s.Stop()

		pb.RegisterGreeterServer(s, &server{})

		if err := s.Serve(listener); err != nil {
			logger.Error("failed to serve: %v", zap.Error(err))
		}
	}()

	// client
	go func() {
		time.Sleep(2 * time.Second)
		ctx := context.Background()
		logger := zap.L().Named("Client")

		client, err := grpc.DialContext(
			ctx,
			port,
			grpc.WithUserAgent("my-user-agent/1.0.0"),
			grpc.WithInsecure(),
		)
		defer client.Close()

		if err != nil {
			logger.Error("failed create client", zap.Error(err))
		}

		greeterClient := pb.NewGreeterClient(client)

		logger.Info("Do request")

		response, err := greeterClient.SayHello(ctx, &pb.HelloRequest{Name: "sters"})

		logger.Info("", zap.Any("response.Message", response.Message), zap.Error(err))
	}()

	time.Sleep(5 * time.Second)
	zap.L().Info("Shutdown")
}

func accessLogUnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		clientIP := "unknown"
		if p, ok := peer.FromContext(ctx); ok {
			clientIP = p.Addr.String()
		}

		useragent := ""
		if md, ok := metadata.FromIncomingContext(ctx); ok {
			if ua, ok := md["user-agent"]; ok {
				useragent = strings.Join(ua, ",")
			}
		}

		ctxzap.AddFields(
			ctx,
			zap.String("access.clientip", clientIP),
			zap.String("access.useragent", useragent),
		)

		return handler(ctx, req)
	}
}

最近パッケージ読み込み出来るようになったって聞いたので The Go Playground で実行できる。
ココ にある

nginx のアクセスログを LTSV にする

アクセスログ LTSV が便利だよって聞いたので設定するだけ設定した。
活用するのはこれから。

そもそも LTSV って? Labeled Tab-separated Values (LTSV)

Labeled な TSV だそうで、タブ区切りで処理するときにラベル付いてるので 1 番目が〜2 番目が〜みたいなことをしなくて便利。

そのサイトにもやり方がざっくり書いてあるが、いったん全部入りでこうした。
量多すぎて辛かったら一部捨てる感じで。

log_format main "\ttime:$time_local"
    "\thost:$remote_addr"
    "\tforwardedfor:$http_x_forwarded_for"
    "\tuser:$remote_user"
    "\treq:$request"
    "\tmethod:$request_method"
    "\turi:$request_uri"
    "\tprotocol:$server_protocol"
    "\tstatus:$status"
    "\tsize:$body_bytes_sent"
    "\treqsize:$request_length"
    "\treferer:$http_referer"
    "\tua:$http_user_agent"
    "\tvhost:$host"
    "\treqtime:$request_time"
    "\tcache:$upstream_http_x_cache"
    "\truntime:$upstream_http_x_runtime"
    "\tapptime:$upstream_response_time"
    "\tmsec:$msec"
    "\tproxy_add_x_forwarded_for:$proxy_add_x_forwarded_for"
    "\tscheme:$scheme"
    "\tstatus:$status"
    "\tupstream_addr:$upstream_addr"
;
access_log  /var/log/nginx/access.log  main;

Confuluence を Markdown で書く

Confuluence の世界に Markdown で書いてたものを持っていくとか Markdown でサクッと書きたいなあ、などというのが微妙に不便しているので、誰かベストソリューションを教えてほしい。

Markdown for Confluence | Atlassian Marketplace

これは Confuluence のページに Markdown なコンテンツを埋め込める、というもの。
Markdown で Confuluence のページを書けるといえばそう。けど、これだと Markdown の世界と Confuluence の世界がつながらないので、ちょっと微妙…、かも?

Confluence Wiki マークアップ - アトラシアン製品ドキュメント

マークアップ機能はでっかい Markdown や、やや処理にクセがあるっぽく、うまく出来ないときの修正の仕方がいまいちわからない。
ただ Confuluence Wiki 記法はうまく処理されるっぽい。
というわけで Markdown から Confuluence Wiki 記法にするそれっぽいのを書いてみて、手元だけはすこし楽できるようにしてみた。

GitHub - sters/md2cw: Markdown syntax to Confluence Wiki syntax

converter を見てもらうとわかるけれど Markdowm のパーサーやレンダラーとして動いてくれる blackfriday というのがある。

GitHub - russross/blackfriday: Blackfriday: a Markdown processor for Go

ぼくはそれぞれの要素が Confuluence でいうどれにあたるかを書いたくらいしかしていないくらいに、世の中は便利。


git(というよりは svn が近いか)のそれみたいに、プル、プッシュ、マージが出来るようなクライアントとかあってもいいかもなーとか思った。
そうなるともはや Confuluence である必要はそんなにないかもしれないけれども。。


それはそうとして、このリポジトリ、go getでうまくいれられないくない?(日本語崩壊)
go modulesとgo getのお気持ちがちょっとまだわからない…

kubectl で Service から Pod の紐づけを一発で確認した

まとめとしては -o json | jq な感じで頑張るのがとりあえずは正解っぽい。

$ kubectl get -o json endpoints | jq '.items[] | [.metadata.name, ([.subsets[].addresses[].targetRef.name] | join(", "))] | @tsv' --raw-output
http-server    http-86d67d8d47-46fm8, http-86d67d8d47-x487n, http-86d67d8d47-nxgdx
http-server-dev    http-dev-58fc975f8f-z2lvw, http-dev-58fc975f8f-tjczr

シェルむけの関数を組んで kubectl にオプション渡せるようにしたら便利、かも、と思ってざっくり書いた。
あたらしく Service を作ったりときにセレクタミスってないかな?とかを見るのに便利、かも。

#!/bin/sh
function kube_lookup_pod() {
    kubectl get -o json endpoints $@ \
    | jq -r \
      '["Service", "Pods"], (.items[] | [.metadata.name, ([.subsets[].addresses[].targetRef.name] | join(", "))]) | @tsv' \
    | column -t
}
$ kube_lookup_pod --namespace foo-namespace
Service               Pods
http-server           http-86d67d8d47-46fm8,      http-86d67d8d47-x487n,      http-86d67d8d47-nxgdx
http-server-dev       http-dev-58fc975f8f-z2lvw,  http-dev-58fc975f8f-tjczr

そこまでの経緯もメモっておく。

Kubernetes の Service の一覧はこうやってみえるが、これはどれに紐付いているかが分からない。

$ kubectl get svc
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
http-server            ClusterIP   None         <none>        9100/TCP            10h
http-server-dev        ClusterIP   None         <none>        9100/TCP            10h

ワンチャン describe なら Endpoint がでてくるので、ギリギリわかる。

$ kubectl describe svc http-server
Name:              http-server
Namespace:         foo-namespace
Labels:            <none>
Annotations:       <none>
Selector:          app=http-server
Type:              ClusterIP
IP:                None
Port:              http-server  9100/TCP
TargetPort:        9100/TCP
Endpoints:         10.108.30.146:9100,10.108.35.228:9100,10.108.54.229:9100
Session Affinity:  None
Events:            <none>

Endpoint も get できるがこのままではわからない。

$ kubectl get endpoints
NAME                   ENDPOINTS                                                              AGE
http-server            10.108.30.146:9100,10.108.35.228:9100,10.108.54.229:9100               10h
http-server-dev        10.108.30.149:9100,10.108.32.247:9100                                  10h

describe してもだめ。

$ kubectl describe endpoints http-server
Name:         http-server
Namespace:    foo-namespace
Labels:       <none>
Annotations:  <none>
Subsets:
  Addresses:          10.108.30.146,10.108.35.228,10.108.54.229
  NotReadyAddresses:  <none>
  Ports:
    Name         Port  Protocol
    ----         ----  --------
    http-server  9100  TCP

Events:  <none>

おもむろに -o json やったら実はデータとしては取れることがわかった。

$ kubectl get endpoints http-server -o json
{
    "apiVersion": "v1",
    "kind": "Endpoints",
    "metadata": {
        "name": "http-server",
        ...省略...
    },
    "subsets": [
        {
            "addresses": [
                {
                    ...省略...
                    "ip": "10.108.30.146",
                    "nodeName": ".......",
                    "targetRef": {
                        "kind": "Pod",
                        "name": "http-86d67d8d47-46fm8",
                        "namespace": "foo-namespace",
...省略...

あとは jq でがんばったら取れる。

$ kubectl get -o json endpoints | jq '.items[] | [.metadata.name, ([.subsets[].addresses[].targetRef.name] | join(", "))] | @tsv' --raw-output
http-server    http-86d67d8d47-46fm8, http-86d67d8d47-x487n, http-86d67d8d47-nxgdx
http-server-dev    http-dev-58fc975f8f-z2lvw, http-dev-58fc975f8f-tjczr

書いてるときに試行錯誤してたけど jq の使い方について学びを得た

  • Object ならキーを選んで、配列ならインデックスを選ぶか [] で全選択して、というのをパイプしたら概ね抽出できる
  • join は配列に対してしか使えないので、一覧が出てきそうなやつでも配列にしてからパイプする
  • フラットな配列なら末尾で @csv や @tsv すると便利、そのあと column にパイプするとさらに便利
  • そのときは --raw-output オプションもセットでやると OK
  • あとはマニュアルとにらめっこ jq Manual (development version)

grpc のリクエストをガシガシ送るツールghz

gRPC なエンドポイントのパフォーマンスをみたり、負荷試験をしたいなあという話をしたら ghz というツールがあるよと教えてもらったので、これを使ってみる。

ghz · Simple gRPC benchmarking and load testing tool

公式にはリリースページからインストールしてくれやと書かれている。
が go get でとれるのでこれでいいのでは… latest を使うことになるのでリリースされてるものと微妙に差異があるかも。

go get github.com/bojand/ghz/cmd/ghz/

ab ライクに使えるので、ヘルプを見たらまあ問題なく使えるはず。
Usage · ghz

Kubernetes 上で動くアプリケーションに対して実施するとこんな感じ。
ポートフォワードしてから、

kubectl port-forward svc/foo 5000

※Service に向けてポートフォワードしてますが kubectl コマンド実行時に宛先 Pod を解決してしまうので、ロードバランスしないです。

別ターミナルで、こういう具合 proto の指定がないときは Server Reflection して勝手にやってくれる、超便利。
そっちの話題はここにある。 grpc/server-reflection.md at master · grpc/grpc · GitHub

$ ghz \
    --call foo.bar.Service.RPC \
    -d "$(jo foo='bar')" \
    -m "{\"Authorization\":\"BEARER $TOKEN\"}" \
    -q 4 \
    -c 2 \
    -n 10 \
    --insecure \
    localhost:5000

Summary:
  Count:    10
  Total:    1.37 s
  Slowest:    243.03 ms
  Fastest:    111.49 ms
  Average:    149.98 ms
  Requests/sec:    7.29

Response time histogram:
  111.491 [1]    |∎∎∎∎∎∎∎∎
  124.645 [5]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  137.799 [0]    |
  150.953 [2]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  164.107 [0]    |
  177.261 [0]    |
  190.415 [0]    |
  203.569 [0]    |
  216.723 [0]    |
  229.877 [0]    |
  243.031 [2]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎

Latency distribution:
  10% in 118.75 ms
  25% in 119.66 ms
  50% in 123.21 ms
  75% in 243.01 ms
  90% in 243.03 ms
  0% in 0 ns
  0% in 0 ns

Status code distribution:
  [OK]   10 responses

リクエストパラメータつくるのに jo を使っている。
GitHub - jpmens/jo: JSON output from a shell

上記の例だと 最大 4 req/sec になるように、 2 並列でリクエストし、 10 リクエスト送ったら終わる、というオプションになっている。
なので、レスポンスタイム次第ではあるが、サクッとレスポンスがあれば 1 秒ちょっとで完了する。

レスポンスタイムの分布であったり、パーセンタイルもでたり、サクッと負荷かけて様子を見るのに便利そう。

もちろん、オプションの指定が必要だけど 1 リクエストだけ送ることもできるので、普段使いにもいいんじゃなかろうか。

また Go のパッケージとしても使えるっぽい。
Package · ghz
ちょっと用途が思い浮かんでないけど、例えば、複数の RPC があって、順番にテストしたいんだよね〜〜〜みたいなときとか CI 的に負荷をかけたいんだよね〜〜とか、(シェルスクリプトでさくっと ghz ラッパーでもいいのでは感)

Go で 定期的に Ping する

疎通確認であったり、コネクションのkeepalive的な用途として、定期的に "ping" をしたいんだよなあ、という話があり
ぐるぐる回しっぱなしでよきに使えそうな感じのコードを書いてみた。

GitHub - sters/pinger: Ping somethings for use warmup and standby or others.

いまは土台と、HTTPしかないけど、MySQLとかなんかまあ他にもできそう

使い方はここを見てもらうのがわかりやすいかも
pinger/http_test.go at master · sters/pinger · GitHub

実践的にはこういう感じになるのかな。

ctx := context.Background()

fooPinger := pinger.NewWorker(...)
barPinger := pinger.NewWorker(...)

eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error { return fooPinger.Run(ctx) })
eg.Go(func() error { return barPinger.Run(ctx) })

if err := eg.Wait(); err != nil {
    ...
}

すくなくとも、いま主に触っているソレだとこういう感じで書くことになると思う。

余談だけど...

  • type Hoge interface{}を書いて使ったりしていると、これがダックタイピングかとどっしり理解したので、やっばりやってみないとわからんなという気持ち
  • sync パッケージ、使うものの並列にアクセスされる可能性あるやろなあ、ロックするか、くらいの感覚しかまだわかってないので要修行
  • goroutine はちょっとわかってきた感じがある。おしごとでもぼちぼち書いたりしてる
  • これまで簡単なツールをサクッとつくるの Node.js か PHP 使ってたことが多いけどもう Go 言語でよいのでは、と思えるくらいには書けるようになってきた感じ。
  • よく使うようなライブラリとかツール群はまだイマイチなので都度 :innocent:

gcloud ssh をいろんな Google Compute Engine インスタンスに向けるアレ

都合、ひとつの GCP プロジェクトにある多数の GCE に繋いだりする場合がある、かもしれない。
で、このインスタンス、増えたり減ったり変わったりして、毎回丁寧にエイリアスを組むのが厳しいので、シュッと選択出来るようなスクリプトを書いた。

このあたりが必要。

#!/bin/sh

__check_command() {
    which $1 > /dev/null
    if [ $? = 1 ]; then
        echo "$1: command not found"
        return 1
    fi
}

export gcloudssh_default_project=""

gcloudssh() {
    __check_command "gcloud"
    if [ $? = 1 ]; then
        return 1
    fi
    __check_command "peco"
    if [ $? = 1 ]; then
        return 1
    fi

    gcloud="gcloud --project \"$gcloudssh_default_project\""
    filter="peco"

    if [ $# = 1 ]; then
        opt="--filter=$1"
        filter="grep $1"
    fi

    echo 'Loading gcloud instances...'
    instance=$(eval "$gcloud compute instances list $opt" | grep -v NAME | eval "$filter" | sed -e 's/  */ /g')
    name=$(echo $instance | cut -d ' ' -f 1 | tr -d ' ')
    zone=$(echo $instance | cut -d ' ' -f 2 | tr -d ' ')

    if [ "$name" = "" ]; then
        echo "Stopped."
        return
    fi
    if [ "$zone" = "" ]; then
        echo "Stopped."
        return
    fi

    echo "Name=$name / Zone=$zone"
    echo "Connecting..."
    echo

    eval "$gcloud compute ssh --zone $zone $name"
}

IAM ロールが問題なければ source gcloudssh.sh とかして、

$ gcloudssh 

で GCE インスタンスの一覧を peco なインタラクティブな選択ができる。あとは選べば gcloud ssh でつないでくれる。

$ gcloudssh hoge-instance-001

とかでも接続できる。

gcloud --filter | grep で見に行くので、インスタンス名ドンピシャじゃなくて hoge とか大雑把な引数でも大丈夫。


ちなみに gcloud ssh の説明はここ
インスタンスに接続する | Compute Engine ドキュメント | Google Cloud

needs-restarting

某所の某 CentOS7 なサーバー、たまに何も考えずにログインしては yum update -y して、更にたまに reboot してるけどこれもはや cron で自動でよいのではと思ったのでそうする。

そういえば、アップデート内容によっては再起動が必要かを調べてくれるツールがあったなあぼんやり思い出した。
調べてみたら needs-restarting だった。そのままだ。

yum-utils に入っているらしいので、それでいれる。

$ sudo yum search needs-restarting
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
...
============================================================================================================= Matched: needs-restarting =============================================================================================================
dnf-plugins-core.noarch : Core Plugins for DNF
python2-dnf-plugins-core.noarch : Core Plugins for DNF
yum-utils.noarch : Utilities based around the yum package manager

$ sudo yum install yum-utils
...
$ needs-restarting --help
Usage:
    needs-restarting: Report a list of process ids of programs that started
                    running before they or some component they use were updated.

Options:
  -h, --help        show this help message and exit
  -u, --useronly    show processes for my userid only
  -r, --reboothint  only report whether a full reboot is required (returns 1)
                    or not (returns 0)
  -s, --services    list the affected systemd services only

フルリブートが必要なのか、特定のサービスだけでいいのか、をオプションで切り替えられる模様。

こうしておいた。

$ cat /etc/cron.daily/yum
#!/bin/sh
yum update -y

$ cat /etc/cron.daily/needs-restarting-reboothint
#!/bin/sh
needs-restarting --reboothint

$ cat /etc/cron.daily/needs-restarting-services
#!/bin/sh
needs-restarting --services

確認した結果、全自動で再起動するようにしてもいいけど、何かが何かに引っかかって立ち上がらなくなったりして、まったく気付かないというのは少し怖いので、とりあえずは通知するだけにしておく。
cron はすでに通知…、というか MAILTO にかかれていて、本文に色々書かれていたので、このままで OK ということにしておく。

Kubernetes の Liveness と Readiness

簡単にまとめると Liveness に失敗したらザキ Readiness に失敗したらやけつく息。…いや逆にわからんような気もする。

Liveness は Pod が稼働できるかどうかの判定をする。判定に失敗した場合は Kubernetes がその Pod を作り直す。
Pod が提供するべき最低限の機能すら提供できない場合にこのチェックを失敗させるべきだろう。
たとえば自分のアプリケーションが待ち受けるサーバーを提供するんだけど、そこへの疎通が一切できないとか。それが最初から起きるならアプリケーションは起動しないだろうが、起動してから何かしらの理由でサーバが落ちるといったことが無いわけではないはず。

Readiness は Pod と疎通できるかの判定をする。判定に失敗すると Kubernetes が Service から切り離す。このとき Pod は生きたままになる。
デッカい初期データを読み込むなどと起動に時間のかかるようなアプリケーションだったとき、アプリケーションは無事に起動する(=Liveness をパスできる)が、リクエストを受け付けることができないので、そういった場合は Readiness を設定して、Serviceから切り離されるべきだろう。

 

あと、他のアプリケーションに依存している部分を Liveness や Readiness には入れないほうがよいはず。
その確認をいれていると、回り回って全ての依存関係が全滅するとか Liveness / Readiness のデッドロックが起こってしまいそうな予感がする。

A は B への疎通確認を Liveness にいれて、 B は C への疎通確認を Liveness にいれて、としたときに、仮に C が何かの影響で疎通できないような状態になると B が作り直され A が作り直され、結局 A → B → C とつながった機能が提供できなくなる。

接続できるけど Unavailable な状態、 HTTP でいうと 5xx なステータスを、アプリケーションあるいはプロキシするサイドカーな何かで返すほうがよいのでは?
ほら nginx も upstream につながらなくなっても nginx は終了せずに「upstream に繋がらないけど」というエラーが出るじゃん?
これを各 Pod でもできるようにしたほうがいいと思うのよ。

 

Liveness も Readiness もどちらも、監視までの時間、監視する間隔、判定失敗とするまでの許容する失敗回数といったオプション値を設定できる。
このあたりは公式ドキュメントを見るとまとまってるのでこまかい話はそっちを見るようにしたらよい。
Configure Liveness and Readiness Probes - Kubernetes

それと Liveness も Readiness も、どちらも時間や間隔の調整は必要なものの、基本的にはヘビーな処理が発生しないようにする必要がある。
Liveness や Readiness のチェックでかかる負荷によって、自分自身をを失敗させる原因にならならないようにするために。

brew まとめて更新する

とりあえず更新するようの便利セット。 fish 使ってるので function にしてるけど単に alias でもよいとおもう。

function brews
  brew update
  brew upgrade
  brew cask upgrade
  brew cleanup
  brew doctor
end

話変わるけどこういうことやるとポータビリティ上がってよさそう。

brew list > brew-list.txt
brew cask list > brew-cask-list.txt
mas list > mas-list.txt

やってみた。

brew list

apr
apr-util
aspell
autoconf
bash-completion
brotli
c-ares
clang-format
composer
curl-openssl
fish
fontconfig
freetds
freetype
gd
gdbm
gettext
ghq
git
git-chglog
glib
glide
gmp
go
graphviz
gts
icu4c
jansson
jasper
jemalloc
jo
jpeg
jq
kubectx
kubernetes-cli
kubetail
libev
libevent
libffi
libiconv
libidn
libmetalink
libpng
libpq
libssh2
libtiff
libtool
libyaml
libzip
make
mas
mcrypt
mhash
mysql
netpbm
nghttp2
oniguruma
openldap
openssl
pcre
pcre2
peco
php@7.1
pkg-config
pv
python
python@2
readline
rtmpdump
socat
sphinx-doc
sqlite
sshfs
stern
telepresence
the_silver_searcher
tidy-html5
torsocks
trash
tree
unixodbc
watch
webp
xz

brew cask list

charles
clipy
docker
firefox
goland
google-chrome
google-cloud-sdk
iterm2
java
minikube
osxfuse
phpstorm
sequel-pro
slack
station
visual-studio-code

mas list

540348655 Monosnap (3.5.7)
682658836 GarageBand (10.3.2)
408981434 iMovie (10.1.11)
409201541 Pages (8.0)
409183694 Keynote (9.0.1)
539883307 LINE (5.15.0)
409203825 Numbers (6.0)

なるたけ brew に載っておくと管理がお手軽でいいよ、と聞いてからできるだけ移した。

語彙崩壊してる

1 2 3 4 5 6 7 8 9 10 11 12 13 14