2019年 06月の投稿を表示しています

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)

前後の記事

Next:
Prev:

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"
}

export gcloudssh_default_project=""のあたりをよしなにして、IAM ロールが問題なければ source gcloudssh.sh とかして、

$ gcloudssh 

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

$ gcloudssh hoge-instance-001

とかでも接続できる。

gcloud --filter | grep で見に行くので、インスタンス名ドンピシャじゃなくて hoge とか大雑把な引数でも大丈夫。
zsh や fish なんかではよしなに読み替えて貰う必要がありそう、特に fish だと。


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

前後の記事

Next:
Prev: