開発合宿してきました

2/9~2/11の3連休は開発合宿と称した温泉旅行に行ってきました.
宿泊先は伊香保のホテル天坊で、ホテル内に飲み屋もあり、比較的宿泊しやすい施設だったと思います.

いざ合宿

学生時代からの友人達との合宿だったので、がっつりとした開発合宿にはせず、開発もやりつつ温泉で日々の疲れを取るという目的がありました.
具体的に何をやっていたかと言うと

  • Gremrins, Inc.
  • それぞれのやりたい開発・技術的トピックに取り組む

の2つになります.

1つ目のGremrins, Inc.はゲームなので詳細は割愛しますが、Macでも動作するすごろくゲームなのでオススメしておきます.
2つ目のそれぞれのやりたい開発・技術的トピックに取り組むで、私が選んだのはiPad ProのブラウザからSession Managerで開発ができるか? です.

私は普段、ノートPCを持たずにiPad Proだけを持って、喫茶店で読書をしています.
読書もできる・音ゲーもできる・動画も見れる・RAW現像もできる、といったできることが増えてきているのですが、iPad Proで開発もできると嬉しくありませんか?
専用のアプリを使ったり、SSHクライアントアプリを使えば、実際に開発することもできます.
ただ専用のアプリだとできることが限られますし、SSHだとそのポートでの通信を事前に許可しておかなければなりません.
仮にiPadで開発するということであれば、SSHクライアントでマシンに接続して開発した方が良さそうです.
ですが、今回は挑戦したかったということもあり、インバウンドの通信を許可しなくても使えるセッションマネージャーをiPadのブラウザから操作できるのかやってみました.

結果としては

結果は、できなくもないが生産性がガタ落ちするです. Ctrlキーが使えないため、シェル上での操作やCUIのアプリケーションの操作が難しくなっています. Ctrlキーが使えないというのはキーが存在しないというわけではなく、iOSのブラウザでの制約でキーコードが0になるということが問題です.

詳細に興味がある方はスライドを見てください.

開発合宿という名の温泉旅行でしたが、次回はもう少し場所としても開発合宿っぽくしていこうと思います.

Delayed::Job再起動時にジョブがロックされたままにさせない

Delayed::Jobを気軽に再起動させたいために挙動確認した時のメモです.

実行環境

また、Delayed::Jobを起動する際に共通するオプションとして-n 2を指定する前提で書きます.

ソースコードは、graceful_delayedにまとめてあります.

再起動時にジョブがロックされたままにさせない

Delayed::Jobをbin/delayed_job経由で操作すると、daemonsというGemでWorkerプロセスが起動します.
このdaemonsは停止命令(stop)が実行されると、そのプロセスで処理が終了するまで待ち、プロセスを終了させます.
終了待機時間のデフォルト値は20秒となっており、20秒経っても処理が終了しない場合は処理を強制終了させます.

さてDelayed::Jobで20秒以上かかるジョブ実行中に、強制終了するとどうなるでしょうか?

実行中のジョブがロックされたまま、データベースに残り続けてしまいます.
こうなってしまうとQueueに残り続けてしまうゴミデータとなりますし、最悪の場合処理が失敗しているかもしれません.
では、ジョブがロックされたままにならないようにするにはどうしたら良いでしょうか?
私が考えた方法は2つあります.

  • raise_signal_exceptions:termもしくはtrueを指定する
  • daemonsの終了待機時間をDelayed::Worker.max_run_timeに数秒足した値を指定する(例: --daemon-options=-w,{seconds})

どちらが良いということもないはずなので、戦略的にどちらが良いのかを決めるのが良いでしょう.

このサイトの記事一覧・詳細が表示されていなかった

先週の金曜日から今日にかけて記事一覧・詳細が表示されていない現象に遭遇しました.
理由は、submoduleで組み込んでいるhello-friendというテーマで記事対象となるディレクトリ名の変更が行われていたためです.

とりあえずは、以下の対応を実施した.

  • 最新のテーマで動作するようにディレクトリ構造を変更
  • 最新のテーマで動作するように設定ファイルの変更
  • hello-friendリポジトリのrelease watch
  • 定期的に自動ビルドを行っている処理でsubmoduleの更新が入らないようにする

これ今日記事を書こうとしなかったら絶対に気づかなかったので危ないですね.

秘密鍵から公開鍵を生成する

sshで利用する秘密鍵から対応する公開鍵を生成する時のメモです.

公開鍵を生成する

ssh-keygenの-yオプションを使って、秘密鍵から公開鍵を生成することができます. ※1

$ ssh-keygen -y -f id_ed25519
ssh-ed25519 XXXXXXXXXXX

秘密鍵に対してパスワードが設定されている場合は、パスワードの入力が求められます.

$ ssh-keygen -y -f id_ed25519
Enter passphrase:
ssh-ed25519 XXXXXXXXXXX

さいごに

今回は秘密鍵から公開鍵を生成しましたが、これを利用すれば秘密鍵に使えるパスワードの確認もできます. ケースとしてはssh-keygenで鍵を生成した直後に、設定したパスワードの有効性を確認することができますしね.

※1 参考: https://euske.github.io/openssh-jman/ssh-keygen.html

2019年の抱負

2019年になり既に20日以上経って今更ですが、去年の抱負の振り返りと今年の抱負を振り返ろうと思います.

昨年の抱負

khasegawa.hatenablog.com

昨年の抱負は2つありました.

  • 自己の体調管理を見直す
  • 日々の学びをアウトプットする

まず、自己の体調管理について振り返ろうと思います.
例年に比べると対策を取り安定してましたが、1度病気になると長引くというのが正直辛かったです.
感染性胃腸炎で1週間休んだら、次の週はそれが原因で急性胃腸炎になるという「もう一回遊べる○ん!」と言われているかのような辛さでした.

次は日々の学びをアウトプットするについてです.
これは誰がどうみても達成できていなかった.
というよりも、そもそも存在忘れていました.
うん、完全に.

今年の抱負

去年の抱負は、目標はあれどそれに対する具体的な計画はありませんでした. そのため、今年はTrelloで今年のパーソナルゴールとして管理するようにしています.

f:id:corrupt952:20210131103308j:plain

具体的にどういった目標を経てているのかは見せられない項目もあるため伏せますが、ボードで俯瞰して管理できるととても良いです.
全てのゴールを達成するのは私のスキルと仕事との兼ね合いで難しいと思いますが、1つでも多く達成できるようにしてみます.

直近達成できそうなゴールは開発合宿です. 2月中旬なのでその時を今か今かと楽しみにしています.

Envoy vs Nginx: HTTP/1.1 Reverse Proxy Performance

HTTP/1.1のリバースプロキシとして、EnvoyとNginxとの軽めのベンチマークを取りました.
しっかりとしたベンチマークとはいえませんが、少しは参考になる結果かもしれません.

TL; DR

  • リバースプロキシコンテナは、CPU・Memoryともにリソースの制限をする
  • リバースプロキシとサービスはHTTP/1.1でTCPソケットで通信する
  • wrk -t 10 -c 10 http://hostで出力結果を取る
  • Nginxの方が約1msほど早かったが、はっきりいって誤差
  • corrupt952/surveyに今回の記事の元となるコードを置いている

実行環境

  • docker ... 18.09.0
  • docker-compose ... 1.23.2
  • wrk ... 4.1.0

準備

今回もdocker-composeを利用して環境を作ります.
コードは、corrupt952/surveyにあります.

docker-compose.yml

Envoy・Nginx・サービス代わりのNginxの3つを定義します

version: '3'
services:
  envoy:
    build:
      context: .
      dockerfile: dockerfiles/envoy/Dockerfile
    expose:
      - "80"
    ports:
      - "8000:80"

  nginx:
    build:
      context: .
      dockerfile: dockerfiles/nginx/Dockerfile
    expose:
      - "80"
    ports:
      - "8001:80"

  service:
    image: nginx:stable-alpine
    expose:
      - "80"

dockerfiles/envoy/Dockerfile

Envoyのサンプルコードを参考にしていますが、不要なコードは削除しました.

FROM envoyproxy/envoy-alpine:01d726a41bdd790c16765e1d321cb50590574eb0

COPY dockerfiles/envoy/front-envoy.yaml /etc/front-envoy.yaml
CMD ["/usr/local/bin/envoy", "-c", "/etc/front-envoy.yaml", "--service-cluster", "front-proxy"]

dockerfiles/envoy/front-envoy.yaml

Envoyのサンプルコードを参考にしてサービスへリダイレクトするようにしています.

admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001

static_resources:
  listeners:
    - address:
        socket_address:
          address: 0.0.0.0
          port_value: 80
      filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              codec_type: HTTP1
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                  - name: backend
                    domains:
                      - "*"
                    routes:
                      - match:
                          prefix: "/"
                        route:
                          cluster: service
              http_filters:
                - name: envoy.router
                  config: {}
  clusters:
    - name: service
      connect_timeout: 0.25s
      type: strict_dns
      lb_policy: round_robin
      hosts:
        - socket_address:
            address: service
            port_value: 80

dockerfiles/nginx/Dockerfile

次に紹介するdefault.confをNginxが読み込める位置に定義しておきます.

FROM nginx:stable-alpine
COPY dockerfiles/nginx/default.conf /etc/nginx/default.conf

dockerfiles/nginx/default.conf

サービスへプロキシするように定義するだけなので特筆することはありません.

server {
    listen 80 default_server;
    server_name _;

    location / {
        proxy_pass http://service/;
    }
}

これで準備は終わりです.

ベンチマークしてみる

wrkというツールを使い、簡易的にベンチマークを実行してみます.
コネクション数10,スレッド数10に設定にします.(これといって根拠はないです)

Envoy

$ wrk -t 10 -c 10 http://localhost:8000
Running 10s test @ http://localhost:8000
  10 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.45ms    2.02ms  46.58ms   96.63%
    Req/Sec   433.29    121.99     1.17k    82.34%
  43501 requests in 10.10s, 35.39MB read
Requests/sec:   4305.17
Transfer/sec:      3.50MB

Nginx

$ wrk -t 10 -c 10 http://localhost:8001
Running 10s test @ http://localhost:8001
  10 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.65ms    0.96ms  13.85ms   88.35%
    Req/Sec   631.85    129.09     1.20k    66.53%
  63373 requests in 10.10s, 51.37MB read
Requests/sec:   6271.63
Transfer/sec:      5.08MB

Nginxの方が約1ms早いという結果になりました.
ですが、実際この差は膨大なリクエストを捌く必要がある大規模なサービスでなければ誤差といえる範疇かと思います.

最後に

今回は、HTTP/1.1のFront Proxyという役割でやってみましたが、Nginx・Envoyともにサポートしている領域が他にもあります.
そういった点を踏まえながら最適なモノを選んでいくのが良いでしょう.

EnvoyでHTTP/1.1の通信をプロキシする

EnvoyはHTTP/1.1とHTTP/2ともにサポートしているということで、HTTP/1.1のFront Proxyができるのか試してみました.

TL; DR

  • front-envoy.ymlcodec_typeHTTP1に変更すれば、HTTP/1.1で通信するようになる
  • corrupt952/surveyに今回の記事の元となるコードを置いている

Envoyとは

Cloud Nativeアプリケーションのために設計されたOSSなプロキシソフトウェアのことです.
KubernetesやMicroservicesの話が出てくると、必ずと言っていいほどに出てくるソフトウェアですね.

  • Circuit Breaker
  • Distributed Tracing
  • リクエストのリトライ
  • Service Discovery

などといったマイクロサービスが抱える諸問題を扱えるため、アプリケーション側で扱わなくてよくなり実装がシンプルになります.
また、マイクロサービスでなくても導入するメリットが存在するため、どのような機能があるのかを調べてみるのも良いでしょう.

Envoyが外部からのインバウンドな通信を受け取り、適切なアプリケーションへリバースプロキシすることもできます.
公式ドキュメントでは、Front Proxyというページに書かれています. ※
また、同一ページ内にサンプルコードへのリンクもあるので、簡単に試すことができます.

※ 記事作成時点で証明書が失効しているためブラウザによっては接続できません

環境

  • docker ... 18.09.0
  • docker-compose ... 1.23.2

HTTP/1.1で通信させる

公式が用意しているサンプルコードでは、Envoyからサービス用のコンテナのEnvoyへHTTP/2で通信しています.
その通信をHTTP/1.1にしてみます.
また今回作成したコードは、corrupt952/surveyにおいてあります.

概要

まずは大体の概要について書きます.
Client - HTTP1.1 -> Envoy - HTTP1.1 -> NGINX(HTTP1.1サーバ) といった構成で通信します.

docker-compose.yml

次にdocker-compose.ymlについてです.
サンプルコードをベースに作っていますが、異なるのはバックエンドに置いてあるのはごく普通のNginxコンテナです.
Nginxを使っている理由は特にありません.

version: '3'
services:
  envoy:
    build:
      context: .
      dockerfile: dockerfiles/envoy/Dockerfile
    expose:
      - "80"
      - "8001"
    ports:
      - "8000:80"
      - "8001:8001"

  nginx:
    image: nginx:stable-alpine
    expose:
      - "80"

dockerfiles/envoy/Dockerfile

EnvoyのDockerfileもサンプルコードを参考にしていますが、不要そうな行は削除しました

FROM envoyproxy/envoy-alpine:01d726a41bdd790c16765e1d321cb50590574eb0

COPY dockerfiles/envoy/front-envoy.yaml /etc/front-envoy.yaml
CMD ["/usr/local/bin/envoy", "-c", "/etc/front-envoy.yaml", "--service-cluster", "front-proxy"]

dockerfiles/envoy/front-envoy.yaml

今回のキモとなるEnvoyの設定ファイルです.
こちらも他と同様にサンプルコードを参考にしています.

admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001

static_resources:
  listeners:
    - address:
        socket_address:
          address: 0.0.0.0
          port_value: 80
      filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              codec_type: HTTP1
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                  - name: backend
                    domains:
                      - "*"
                    routes:
                      - match:
                          prefix: "/"
                        route:
                          cluster: nginx
              http_filters:
                - name: envoy.router
                  config: {}
  clusters:
    - name: nginx
      connect_timeout: 0.25s
      type: strict_dns
      lb_policy: round_robin
      hosts:
        - socket_address:
            address: nginx
            port_value: 80

細々と変更している箇所はありますが、重要なのはcodec_typeです.
公式ドキュメントのcodec_typeを見ると、指定可能な値は

  • AUTO
  • HTTP1
  • HTTP2

の3つです.
今回は、HTTP/1.1を強制させたいため、HTTP1を指定します.
たったこれだけでHTTP/1.1で通信させることが可能になりました.

最後に

今回は、EnvoyでHTTP/1.1のFront Proxyをやってみました.
これといって難しいことはありませんでしたが、ドキュメントを読みながらEnvoyを少し知るよいきっかけになりました.