Ingress によるサービス公開! nginx-ingress-controller を試してみる。

こんにちは k-jun です。今回は nginx-ingress-controller を試してみます。

https://github.com/kubernetes/ingress-nginx

そもそも ingress とはなんぞやという話ですが、k8s における 外部からのアクセスを内部の適切な Pod へ ルーティングする LB のようなものです。

https://kubernetes.io/docs/concepts/services-networking/ingress/

Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.

ここらへんの リソースは自分が前触った際には クラウドサービスの LB をプラグイン連携して使用するみたいになっていた気がしますが、いつの間にか nginx を使用できるようになっていたみたいですね。

Setup

ここ を参考にセットアップを進めていきます。

アプリケーションをデプロイしていきます。使用するイメージはサンプルアプリケーションのもの。

$ kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
deployment.apps/web created
$ kubectl expose deployment web --type=NodePort --port=8080
service/web exposed
$ kubectl get service web
NAME   TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
web    NodePort   10.97.252.61   <none>        8080:31074/TCP   35s

Run

ということで、本題の Ingress を作っていきます。

$ kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml

中身で叩いている yaml はこんな感じ。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080

ingress が起動している様子もコマンドから確認できます。

$ kubectl get ingress
NAME              CLASS    HOSTS              ADDRESS        PORTS   AGE
example-ingress   <none>   hello-world.info   35.190.25.54   80      8m58s

External IP でアクセスしてみるとこんな感じ。

$ curl -I http://35.190.25.54 -H 'Host: hello-world.info'
HTTP/1.1 200 OK
date: Sat, 04 Dec 2021 13:35:51 GMT
content-length: 60
content-type: text/plain; charset=utf-8
x-envoy-upstream-service-time: 0
server: istio-envoy
x-envoy-decorator-operation: web.default.svc.cluster.local:8080/*
Via: 1.1 google
$ curl -I http://35.190.25.54 -H 'Host: hello-world.dummy'
HTTP/1.1 404 Not Found
Date: Sat, 04 Dec 2021 13:37:48 GMT
Content-Length: 74
Content-Type: text/plain; charset=utf-8
Via: 1.1 google

HostName の制限もしっかりと生きている様子。外部 IP が簡単に付与できて感激です。数年前はここまで簡単にできなかった記憶。

$ kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
deployment.apps/web2 created

path ごとの振り分けを試すべく、2つ目のサンプルアプリケーションを作成。

$ kubectl expose deployment web2 --port=8080 --type=NodePort
$ kubectl expose deployment web2 --port=8080 --type=NodePort
$ curl -s https://kubernetes.io/examples/service/networking/example-ingress.yaml > ./ingress.yaml
$ vim ./ingress.yaml
$ cat ./ingress.yaml
$ cat ./ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080
          - path: /v2
            pathType: Prefix
            backend:
              service:
                name: web2
                port:
                  number: 8080
$ curl http://35.190.25.54/v2 -H 'Host: hello-world.info'
Hello, world!
Version: 2.0.0
Hostname: web2-5d47994f45-nj67t

いい感じに、version 2 の方にアクセスが飛んでいますね。 ということで、nginx-ingress-controller を使用することで、いい感じにサービスを外部に公開できることがわかりました。 それでは今回はこのへんで。

ブラウザからターミナル! XTerm を試してみる。

こんにちは k-jun です。今回はブラウザでターミナルが再現できるライブラリ XTerm を試してみます。

https://github.com/xtermjs/xterm.js

以前カロリーメイトのドリンク版の広告サイトが似たようなことをやっていた気がします。これ や。

久しぶりに遊んでみたら、前からあったのか面白い機能を見つけましたw。宗教戦争はごめんとw。

caloriemateliquid $ vim
DON’T FIGHT ANY MORE!
This is a neutral zone.
caloriemateliquid $ emacs
DON’T FIGHT ANY MORE!
This is a neutral zone.

Setup

$ mkdir xterm
$ cd xterm
$ npm install xterm
# xterm/index.html
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
    <script src="node_modules/xterm/lib/xterm.js"></script>
  </head>
  <body>
    <div id="terminal"></div>
    <script>
      var term = new Terminal();
      term.open(document.getElementById('terminal'));
      term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
    </script>
  </body>
</html>

Run

作成した index.html にブラウザからアクセスしてみます。

f:id:K-jun1221:20211127170825p:plain

なんか違う...。何も入力できないし...。本当にただターミナルっぽいものが出現しただけじゃん...。 調べてみると input と output は自分で設定する必要があるんだとか。えぇ...

Xterm.js is not bash. Xterm.js can be connected to processes like bash and let you interact with them (provide input, receive output).

なんのためのライブラリ...。まあ、Terminal に接続するとかではなく完全に Terminal が画面に現れただけでしたし。 それでは今回はこのへんで。

分散メッセージングシステム!? NSQ を試してみる。

こんにちは k-jun です。今回はリアルタイムな分散メッセージングシステム NSQ を試してみます。

https://github.com/nsqio/nsq

star 数から見るにかなり有名なプロダクトのようですね。各種クラウドサービスの キューイングサービスももとにしている技術は同じだったりするんでしょうか...?

Setup

$ brew install nsq

Run

ここ に従っていきます。

以下 3つのコマンドをそれぞれ 別ターミナルで起動します。

$ nsqlookupd
$ nsqd --lookupd-tcp-address=127.0.0.1:4160
$ nsqadmin --lookupd-http-address=127.0.0.1:4161

1つ目は名前解決、2つめは NSQ 本体となる daemon, 3つ目は GUI とかでしょうか。

f:id:K-jun1221:20211127163948p:plain

ということでここに 何かを放り込んでみます。

$ curl -d 'hello world 1' 'http://127.0.0.1:4151/pub?topic=test'
OK

何やら Topic が追加されましたね。SNS とかの Topic もここから来ているんでしょうか。

f:id:K-jun1221:20211127164305p:plain

手順にしたがって、nsq_to_file というコマンドを実行してみます。一緒にさらなるメッセージも追加。

$ curl -d 'hello world 2' 'http://127.0.0.1:4151/pub?topic=test'
OK
~/source-code/remix/my-remix-app master*
$ curl -d 'hello world 3' 'http://127.0.0.1:4151/pub?topic=test'
OK
$ nsq_to_file --topic=test --output-dir=/tmp --lookupd-http-address=127.0.0.1:4161
2021/11/27 16:44:10 INF    1 [test/nsq_to_file] querying nsqlookupd http://127.0.0.1:4161/lookup?topic=test
2021/11/27 16:44:10 INF    1 [test/nsq_to_file] (MacBook-Pro.local:4150) connecting to nsqd
[nsq_to_file] 2021/11/27 16:44:10.864197 INFO: [test/nsq_to_file] opening /tmp/test.MacBook-Pro.2021-11-27_16.log
[nsq_to_file] 2021/11/27 16:44:10.864381 INFO: [test/nsq_to_file] syncing 1 records to disk
[nsq_to_file] 2021/11/27 16:44:40.614075 INFO: [test/nsq_to_file] syncing 2 records to disk

管理画面を見てみて、キューがなくなっていることに気づきました。

f:id:K-jun1221:20211127164705p:plain

つまり、nsq_to_file というのはおそらく Topic を listen してファイルに吐き出しているものでは。と /tmp ディレクトリを見に行くと入ってた。

$ tail -f /tmp/test.MacBook-Pro.2021-11-27_16.log
hello world 1
hello world 2
hello world 3

なるほどこうやってメッセージをやり取りできるのか。

ドキュメントをよく見てみると、使用していた daemon の説明も載っています。

https://nsq.io/components/utilities.html を見てみると、他にもいろいろと便利そうなコマンドがあるんですね。

ということで分散メッセージングライブラリの NSQ を見てみました。今回はこのへんで。

次世代のフロントフレームワーク! Remix を試してみる。

こんにちは k-jun です。今回は React Router の作成者が、作ったフレームワーク Remix を試してみます。

https://github.com/remix-run/remix

README によると work in progress ということですが、面白そうなので触ってみてしまいます。

Setup

create-remix で一撃でした。

$ npx create-remix@latest
Need to install the following packages:
  create-remix@latest
Ok to proceed? (y) y

R E M I X

💿 Welcome to Remix! Let's get you set up with a new project.

? Where would you like to create your app? ./my-remix-app
? Where do you want to deploy? Choose Remix if you're unsure, it's easy to change deployment targets. Remix App Server
? TypeScript or JavaScript? TypeScript
? Do you want me to run `npm install`? Yes

Run

では早速起動してみます。

$ npm run dev

> dev
> remix dev

Watching Remix app in development mode...
💿 Built in 373ms
Remix App Server started at http://localhost:3000
GET / 200 - - 43.888 ms

f:id:K-jun1221:20211127160232p:plain

おおお、ひとまず立ち上がることは立ち上がりましたね。

中身を見てみましたが、一番近いのは React ですね。拡張子は tsx ですし。

こちら に従って続きを作っていきます。

app/root.tsx の内容を以下のように書き換えます。

import { LiveReload, Outlet } from "remix";

export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <title>Remix: So great, it's funny!</title>
      </head>
      <body>
        <Outlet />
        {process.env.NODE_ENV === "development" ? (
          <LiveReload />
        ) : null}
      </body>
    </html>
  );
}

そして、app/routes/index.tsx を以下のように追加すると、いい感じに。

export default function IndexRoute() {
  return <div>Hello Index Route</div>;
}

変更して気づきましたが、Remix 反映がめちゃくちゃ早いですね。変わったことにすら気づかない Flutter レベルで早いです...。 の component が少なからず影響しているようです。良さげ。

f:id:K-jun1221:20211127161529p:plain

どうもこの という component がかなり便利そうです。下位の component を自動でここに格納するようです。 つまり、以下のように app/routes/jokes.tsx と app/routes/jokes/new.tsx を追加すると。

import { Outlet } from "remix";

export default function JokesRoute() {
  return (
    <div>
      <h1>J🤪KES</h1>
      <main>
        <Outlet />
      </main>
    </div>
  );
}
export default function NewJokeRoute() {
  return (
    <div>
      <p>Add your own hilarious joke</p>
      <form method="post">
        <div>
          <label>
            Name: <input type="text" name="name" />
          </label>
        </div>
        <div>
          <label>
            Content: <textarea name="content" />
          </label>
        </div>
        <div>
          <button type="submit" className="button">
            Add
          </button>
        </div>
      </form>
    </div>
  );
}

こんな感じになる。

f:id:K-jun1221:20211127161910p:plain

絵文字の箇所は app/routes/jokes.tsx から来ているがそれ以外は app/routes/jokes/new.tsx ってことですね。 ワイルドカードを含むルーティングには app/routes/jokes/$jokeId.tsx のように指定するようです。

ということで軽く触るだけしてみましたが、なかなかおもしろそうです。本格的には展開されていないので真の力はこれからですが。 それでは今回はこのへんで。

全 Cloud Resource を PostgreSQL に ! CloudQuery を試してみる。

こんにちは k-jun です。今回はパブリッククラウドに存在するリソースを全てレコード化して SQL で検索できるようにする CloudQuery を試してみます。

https://github.com/cloudquery/cloudquery

README.md を見た感じは、PostgreSQL を用意してコマンドを実行するだけみたいですね。ユースケースは正直全く思いついていませんが、列挙できるのは意外と便利かもしれません。

Setup

$ brew install cloudquery/tap/cloudquery
$ cloudquery init aws 

生成された設定ファイルを見てみると、PostgreSQL の接続情報が書かれています。ということで、これと同じ接続情報で PostgreSQL を起動していきます。

$ cat config.hcl
 ...
  connection {
    dsn = "host=localhost user=postgres password=pass database=postgres port=5432 sslmode=disable"
  }
...
$ docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -d postgres

Run

満を持して fetch を実行してみるも失敗している様子。

$ cloudquery fetch
Provider fetch complete.

Provider aws fetch summary:  ✓ Total Resources fetched: 5        ⚠️ Warnings: 0   ❌ Errors: 203

調べるとファイルオープンの上限に引っかかっていたようなので調査して解消。

https://discussions.apple.com/thread/251000125

$ cloudquery fetch
...
Provider aws fetch summary:  ✓ Total Resources fetched: 2        ⚠️ Warnings: 0   ❌ Errors: 0

良さそう。PostgreSQL の中身を見てみる。

f:id:K-jun1221:20211127143739p:plain

EC2 だけでもこれだけテーブルが作成されている...! すごい...! MySQL も対応していたら、業務導入をよりかんたんに検討できるかもしれないのになぁ...。

postgres=# SELECT * FROM aws_ec2_instances;
-[ RECORD 1 ]--------------------------------------+-------------------------------------------------------
cq_id                                              | 964684cc-xxxx-xxxx-xxxx-250dac7de772
meta                                               | {"last_updated": "2021-11-27T05:31:30Z"}
account_id                                         | 9367844xxxxx
region                                             | ap-northeast-1
id                                                 | i-0f7141eff3f2xxxxx
ami_launch_index                                   | 0
architecture                                       | x86_64
capacity_reservation_id                            |
cap_reservation_preference                         | open
cap_reservation_target_capacity_reservation_id     |
cap_reservation_target_capacity_reservation_rg_arn |
client_token                                       |
cpu_options_core_count                             | 1
cpu_options_threads_per_core                       | 1
ebs_optimized                                      | f
ena_support                                        | t
enclave_options_enabled                            | f
hibernation_options_configured                     | f
hypervisor                                         | xen
iam_instance_profile_arn                           |
iam_instance_profile_id                            |
image_id                                           | ami-02d36247c5bc58c23
instance_lifecycle                                 |
instance_type                                      | t2.micro
kernel_id                                          |
key_name                                           | k-jun
...

レコードの中身を見てみたけれども、本当に全列挙という感じで情報が全部入っている。これはなかなかすごいぞ...! Cloud Config なんて使用しなくてもいい感じにリソースの監視ができるかもしれない。良さげ。

ということで今回はこのへんで。

GraphQL + NewSQL!? FaunaDB を試してみる。

こんにちは k-jun です。今回は Spanner の論文から派生した NewSQL の一種 FaunaDB を試してみたいと思います。

https://fauna.com/

どうやら、DB 自体は OSS ではないようですね。DB という主要コンポーネントを特定のベンダーに頼る点は結構マイナスですがやっていきます。 せっかく Spanner から派生しているのになんだかもったいないですね。

Setup

こちらチュートリアルに従っていきます。

f:id:K-jun1221:20211125065822p:plain

サインアップ後のダッシュボードはこんな感じ。データベースを作っていきます。

f:id:K-jun1221:20211125070119p:plain

サンプルのデータを突っ込んだ状態でデータベースを作成しました。中身がダッシュボードからでも確認できるようで、Json らしきものが確認できます。 インデックスの仕組みもあるようで、他の DB 同様に肝となる箇所です。

ドキュメントを参照する限りではインデックスは作り放題みたいに書いてありますが、本当かしら。

you can have hundreds of indexes without affecting overall performance.

f:id:K-jun1221:20211125070556p:plain

FQL と呼ばれる軽いデータ整形関数を DB にて記述できるようです。MySQL での Function なんかに相当するものでしょうか。かゆいところに手が届くといいですね。

f:id:K-jun1221:20211125070637p:plain

Run

ではでは、実際に Query を発行して取得結果を見ていきます。

f:id:K-jun1221:20211125070954p:plain

この Paginate 関数はやばいですね。ページネーションはアンチパターンがあったりといろいろとめんどくさいですし。これ、MySQL にも欲しいなー。

f:id:K-jun1221:20211125071052p:plain

これも表現のレベルが高度。Paginate の 結果を key としてその詳細情報を取ってきて関数に渡しているようですね。かなり面白いです。

f:id:K-jun1221:20211125071230p:plain

GraphQL の Interface 経由でアクセスできることも見ていきます。リンクはこれ

データベースを作成。

f:id:K-jun1221:20211125075924p:plain

スキーマを GraphQL 形式で記述していきます。

$ cat ~/Desktop/schema.gql
type Todo {
   title: String!
   completed: Boolean
}

type Query {
   allTodos: [Todo!]
   todosByCompletedFlag(completed: Boolean!): [Todo!]
}

作成したスキーマを食わせたところ、見知った画面が開きましたね。

f:id:K-jun1221:20211125080136p:plain

mutation で document も生成できていそうです。

f:id:K-jun1221:20211125080226p:plain

使い勝手は完全に GraphQL ですね。チュートリアルを実行した限りでは、NewSQL の何たるかが全く伝わってこなかったので少し調べてみます。

https://docs.fauna.com/fauna/current/comparisons/compare-faunadb-vs-dynamodb

DynamoDB との比較記事があったのでざっと読みましたが、以下以外に特徴はなさそうだと感じました。

  • GraphQL の Interface をサポートしている
  • Index へのデータタイプが制限されず柔軟
  • ACID トランザクションをサポート

比べるべきは Spanner などであって、DynamoDB ではないはずですね。ここらへん のスター数も微妙ですし、OSS でないことも相まってそこまで活発的ではないのかもしれません。 TiDB や、CockroachDB の利点の一つにベンダーロックインされる Aurora, Spanner と違って、OSS ってのがあるのでやっぱり重要ですよね...。スケールするのかどうかも微妙。

それでは今回はこの辺で。

実行は Terminal で結果は Web から!benchmark ツール plow で遊んでみる。

こんにちは k-jun です。今回は星の数ほどあるベンチマークツールの一つ plow を試してみます。

https://github.com/six-ddc/plow

特徴として結果がブラウザで閲覧できるというのをあげており、その点はたしかに便利そうです。

Setup

$ brew install plow

Run

ヘルプはこんな感じ。

$ plow --help
usage: plow [<flags>] <url>

A high-performance HTTP benchmarking tool with real-time web UI and terminal displaying

Examples:

  plow http://127.0.0.1:8080/ -c 20 -n 100000
  plow https://httpbin.org/post -c 20 -d 5m --body @file.json -T 'application/json' -m POST

Flags:
      --help                   Show context-sensitive help.
  -c, --concurrency=1          Number of connections to run concurrently
  -n, --requests=-1            Number of requests to run
  -d, --duration=DURATION      Duration of test, examples: -d 10s -d 3m
  -i, --interval=200ms         Print snapshot result every interval, use 0 to print once at the end
      --seconds                Use seconds as time unit to print
      --body=BODY              HTTP request body, if start the body with @, the rest should be a filename to read
      --stream                 Specify whether to stream file specified by '--body @file' using chunked encoding or to read into memory
  -m, --method="GET"           HTTP method
  -H, --header=K:V ...         Custom HTTP headers
      --host=HOST              Host header
  -T, --content=CONTENT        Content-Type header
      --cert=CERT              Path to the client's TLS Certificate
      --key=KEY                Path to the client's TLS Certificate Private Key
  -k, --insecure               Controls whether a client verifies the server's certificate chain and host name
      --listen=":18888"        Listen addr to serve Web UI
      --timeout=DURATION       Timeout for each http request
      --dial-timeout=DURATION  Timeout for dial addr
      --req-timeout=DURATION   Timeout for full request writing
      --resp-timeout=DURATION  Timeout for full response reading
      --socks5=ip:port         Socks5 proxy
      --auto-open-browser      Specify whether auto open browser to show Web charts
      --version                Show application version.

  Flags default values also read from env PLOW_SOME_FLAG, such as PLOW_TIMEOUT=5s equals to --timeout=5s

Args:
  <url>  request url

今回は、actix-web にご協力を頂きます。localhost:8088 で起動して、いざ尋常に勝負。

$ plow http://localhost:8088/
Benchmarking http://localhost:8088/ using 1 connection(s).
@ Real-time charts is listening on http://[::]:18888

Summary:
  Elapsed      45.4s
  Count       317111
    2xx       317111
  RPS       6984.779
  Reads    0.859MB/s
  Writes   0.386MB/s

Statistics   Min     Mean    StdDev    Max
  Latency   117µs    141µs    32µs   6.059ms
  RPS       6430.9  6981.28  192.46  7248.04

Latency Percentile:
  P50     P75    P90    P95    P99   P99.9  P99.99
  130µs  145µs  164µs  187µs  249µs  530µs  774µs

Latency Histogram:
  139µs    307060  ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  182µs      8300  ■
  253µs      1317
  376µs       294
  556µs       102
  694µs        28
  1.104ms       4
  1.416ms       6

なんかいい感じ。並列度を変更して実験してみる。

$ plow http://localhost:8088/ -c 100
Benchmarking http://localhost:8088/ using 100 connection(s).
@ Real-time charts is listening on http://[::]:18888

Summary:
  Elapsed      16.2s
  Count       709390
    2xx       709390
  RPS      43788.941
  Reads    5.387MB/s
  Writes   2.422MB/s

Statistics    Min       Mean    StdDev     Max
  Latency    260µs    2.281ms   1.972ms  57.008ms
  RPS       38219.68  43751.67  4139.92  51467.19

Latency Percentile:
  P50        P75      P90      P95      P99     P99.9     P99.99
  1.601ms  2.358ms  3.859ms  5.497ms  10.98ms  20.886ms  33.886ms

Latency Histogram:
  2.181ms   692236  ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  5.123ms    1486312.634ms    2101
  23.926ms     109
  31.328ms      51
  39.959ms       7
  48.915ms       2
  54.739ms      21

他のツールと比較してもヌルヌル動くバーがあるので使用していて楽しいですね。そして、並列度 100 で 40k RPS を達成する actix-web のヤバさよ...。 ではでは、ブラウザに表示してみます。

$ plow http://localhost:8088/ -c 100 --listen :18888

良い感じにグラフ表示ができました。やはりグラフ化は良いですね。情報量が一気に増えます。40k RPS が達成されていたのは最初の方だけで時間とともに低下。 30k 程度で落ち着きました。

f:id:K-jun1221:20211119034416p:plain

ベンチマークツールは wrk がお気に入りだったのですが、この plow も良さそうです。グラフを簡単に作成できるところは 単純で純粋な強みですね。 wrk も比較で試してみました。wrk のほうがやっぱり性能は良さそうですが、情報量の少なさにびっくりですね。時系列のグラフデータがいかに偉大化が分かります。

$ wrk -d 60 -t 6 -c 100 http://127.0.0.1:8088
Running 1m test @ http://127.0.0.1:8088
  6 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.44ms    2.18ms  61.88ms   91.17%
    Req/Sec     7.48k     1.37k   11.15k    69.83%
  2678038 requests in 1.00m, 329.46MB read
Requests/sec:  44631.71
Transfer/sec:      5.49MB

グラフ化が正義なので、plow のほうが良さそうに思えてきました。急所だけ wrk を使うようにしようと思います。それでは今回はこのへんで。