Blogaomu

WEBアプリケーション開発とその周辺のメモをゆるふわに書いていきます。

Kanazawa.rb meetup #91 に参加しました #kzrb

1月以来のKanazawa.rb meetupに参加しました。今回は昨今のCOVID-19の影響によりオンライン開催となりました。

kzrb.doorkeeper.jp

初のオンライン開催

Kanazawa.rb では史上初のオンライン開催となりました。Zoomを使って参加者はそれぞれの場所からログインして常時接続、発表以外のときはマイクも常時ONという状態でやっていました。上の写真は自分の環境(ITBP武蔵のサロンスペース)で、下の写真(by @kiyohara)はZoomの参加者集合写真です。ノートPCなので2画面無いと作業しながらかつ相手の様子を見ながら喋るのが大変そうだなと思いiPadをサブディスプレイにしていました。

f:id:TAKAyuki_atkwsk:20200324213354p:plain

雑に感想を並べていきます

  • 雑談する時間帯もあったし黙々と集中して作業する時間帯もあってよかった
  • オンライン開催ということで県外在住の人(確か東京から?)が参加していた
    • 遠方でもパッと参加できるのは良いね
  • 全員常時接続なので、peer to peer やグループ間の会話ができない構造になっている
    • 専用部屋みたいのを用意すればできなくはないけど準備が煩わしい
    • パッとこういう会話ができるのが現実のmeetupの良さかもしれない
  • 発表資料を手元で見れるのがよい(前の人と被って見えないとかが無い)
  • 画面共有は慣れが必要
  • 意図的にZoomの音を切ってもよいものか?
    • 音楽聞いて作業したいときどうするか?
  • 常時接続なのでネットワーク帯域が持ってかれる
    • 用事があったついでで外から参加していたので個人用のPocket WiFiを使ってたけど、次は家からやりたい...w
  • 他の人がはまりポイントを共有していて、それを聞いた他の人からアドバイスを貰って見事に解決していた
    • Slackでいろいろリンクを共有できててよかった
  • VTuber参加よさそう
  • 懇親会無いのは寂しい

もくもく内容

EC2のユーザーデータ(cloud-init)について調べていました。Gatlingという負荷テストツールがあってそれを単発でEC2上で動かすときに自分の公開鍵とか依存パッケージをいちいち持ってくるのが大変だな、という体験があってユーザーデータを使って楽にできないか、というのを調べていました。最後まで調査できなかったので詳細は別の記事で書く予定ですが、パッケージのインストールはある程度できるようになりました。この辺見ながら試していました。

cloudinit.readthedocs.io

来月もオンラインでのもくもく会となります。

Akka Streamsを利用するTCPクライアントのメモ

Akka Streamsを利用してTCP通信ができる簡易なコードが書けることを知ったのでメモ。

詳しくは以下のページを参照。

doc.akka.io

TCPクライアントのソースコード

package io.github.takayukiatkwsk.tcpclient

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Keep, Sink, Source, Tcp}
import akka.util.ByteString

import scala.concurrent.Future

class TcpClient(implicit val system: ActorSystem) {
  private val connection = Tcp().outgoingConnection("127.0.0.1", 8080) // 接続先のホストとポート

  private val mapFlow = Flow.fromFunction[ByteString, String](b => b.utf8String)
  private val sink = Sink.headOption[String]

  def send(body: String): Future[Option[String]] = {
    Source
      .single(ByteString(body)) // TCPサーバーに送るデータ(StringからByteStringに変換)
      .via(connection)
      .via(mapFlow) // サーバーから返却されたデータをStringに変換
      .toMat(sink)(Keep.right) // 文字列を取り出す
      .run()
  }
}

object TcpClient extends App {
  implicit val actorSystem = ActorSystem("tcp-client")
  implicit val ec = actorSystem.dispatcher

  val tcpClient = new TcpClient()
  for {
    result <- tcpClient.send("Hello from TcpClient")
  } yield {
    println(result.getOrElse("Empty response"))
  }
}

今回TCPサーバーはnc(netcat)コマンドを利用してちゃちゃっと確認できるようにしました。

### 8080ポートでListenして、hello, worldを返す
$ echo -n 'hello world' | nc -l 8080

クライアントを実行してみます。

### ncコマンドとは別のターミナルで実行
$ sbt 'runMain io.github.takayukiatkwsk.tcpclient.TcpClient'

TCPサーバーに Hello from TcpClient というデータが送られてきて標準出力に出力されます。

$ echo -n 'hello world' | nc -l 8080
Hello from TcpClient

その後、TCPクライアントはTCPサーバーからの返信を受けて、ターミナルに hello world というデータが出力されます。

$ sbt 'runMain io.github.takayukiatkwsk.tcpclient.TcpClient'
...
hello world

感想としては、Akka Streamsのみで比較的シンプルにソースコードを書くことができ、レスポンスの扱いもFuture値を使うことができるのでハンドリングしやすいなと思いました。リソースのオープン・クローズも隠蔽されていて使う側としては非常に楽だなあと感じます。選択肢の一つとして覚えておくと良さげですね。

Kanazawa.rb meetup #89 に参加しました #kzrb

2020年になって最初の Kanazawa.rb です。今年もやっていきましょう。

kzrb.doorkeeper.jp

meetup.kzrb.org

もくもく進捗

久しぶりにWebアプリ作ってみようかなーとネタが浮んだのでユーザーストーリーの作成と必要になりそうな技術の調査を行いました。どういうお題かというとブログの最新記事を音声化して購読できるものです。アイデアとしてはよくあるものだとは思いますが自分が欲しいなあと少しでも思ったので作ってみることにしました。

ユーザーストーリーの作成は見様見まねでやってみました。最初はファイルに箇条書きで思いつくままに書いていたのですが、それぞれのユーザーストーリーの関連性や依存を洗い出したかったので付箋に書いてペタペタ貼って考えてみました。結果以下の画像のように並び替えました。

机のスペースが限られているので自分のみ分かる配置になっていますが左から右にストーリーが進んで目的が達成されるという並びにしています。縦方向は派生するパスを表しています。並べてみると最初はこの部分の一連のストーリーが実装できていればいいか、というようなことが決めやすいなという気付きがありました。

残りの時間は技術的な課題を洗い出して一つずつ調査実装していきました(全ては終わっていない)。今回はサーバレスな基盤でRubyを使って実装してみようと考えてます。趣味アプリなので自分の使いたい技術(Rubyは最近触れてなかったので...)を使うというところと、音声変換や非同期処理などクラウド基盤やサーバレスアーキテクチャに乗せた方がもろもろ楽できるのかなというのが技術選定の背景です。調査実装してみて構成を変える可能性はありますが。

ギャラリー

こう見るとタイトル重要だなというのを再認識させられます。

懇親会

www.hotpepper.jp

最近オープンしたという「十三」にて一次会!炭火で鶏肉を焼きながらあれこれ喋ってました。レガシーリソースと新規開発、リモートワークとテクノロジー、ラジオで聴いた分身ロボットのカフェとセルフ介護の話、にいがた酒の陣、社内ルール、ガリベンガーVなどについて話しました。お腹いっぱいでチキン南蛮食べられなかった...。

二次会は安心の「だんまや水産」にて。細巻き美味しかった。トークはあんまり覚えてませんw

次回

決済プラットフォームのStripeミートアップやりますよ。もう使ってるよって方はぜひ事例を共有して、興味あるよって方は気になりポイントを聞いてみる絶好の機会です!

eventregist.com

2019年個人的振り返り

今年もこの季節です。

www.blogaomu.com

仕事

引き続きZOZOのお仕事を手伝っておりますが、去年(=2018年)から運用に入っていたサービスが終了し、次のプロダクトのリリースに向けてあれこれやっているという状況です。役割としては技術調査を行ったり開発効率を高めるためのツールを作ったりということがメインでした。初期実装は自分の方でざっくりやって続きは若者に委譲していくというパターンのやり方が多かったです。やっていく上では思想も含めて引き継いでいくというのが大事だと感じたし、同時に難しさも感じました。一年を通しては上手くいったなーということより上手くいかなかったなーということの方が多かった気がします。

そういうことを感じてかまたは無意識にかは分かりませんが、自身の方向性について考えようとする機会が増えて模索をしていた年でもありました。

  • スキルの棚卸し
    • いまできること、将来やっていたいことを明らかにするためマインドマップでまとめていた。それから先は進められていない。
  • AWS認定
    • どの程度やれているのかを把握するのに受験した。新しく知れたこともあったし、得意不得意について言語化しやすくなった。
  • 人に会って話を聞く
    • meetupやライトな会社訪問などで外の世界の話を聞く機会を作った。特にチームとして何か解決するみたいな部分。

コミュニティ活動

定常的なもの

  • Kanazawa.rb 9回
  • JAWS-UG金沢 7回
  • Code for Kanazawa Civic Hack Night 1回

Kanazawa.rbでは今期も運営メンバーに入ったり飛び込みLTしたりで参加してる感出せたなーと思います。JAWS-UG金沢ではあまり発表できなかったので来年は2回以上発表できるようにしたいです。Code for Kanazawaは初めて参加してみたものの、コミュニティとしての関わり方が他の2つと異なって少し難しさを感じました。一応 ha4go にはほんのり貢献できたのでよかったです。

大規模イベントや単発もの

  • JAWS DAYS 2019
  • HashiCorp Terraform & Vault Enterprise 勉強会 in 金沢
  • 突撃!!隣のアーキテクチャ
  • ScalaMatsuri 2019
  • Algolia 勉強会 in 金沢
  • Google Cloud Next '19 in Tokyo
  • AWS Community Day Kanazawa
  • 松本CTOが語るテックカンパニーとDMMの目指す先 DMM Meetup in 金沢

特にAWS Community Day Kanazawaは結果的に100名超が参加するイベントになりましたが、運営スタッフの一員として関われたのは経験として大きかったなと思います。詳しくは個別に記事を書く予定ですが、ゴールから必要なことを割り出してこれらを一つ一つ実行していく力が問われました。個人的には会の直前までふわふわしていましたが無事に開催できてよかったです。

個人開発

今年はプロダクトは作りませんでした。PCにsandboxというディレクトリがあるのですがその中で写経したりサンプルアプリを作ったりするぐらいでしたね。引き出しは少しずつ増えているはずなのでアイデアを形にするのを来年はできたらよいなと思います。

来年も上手い酒を飲めるように精進。

Kanazawa.rb meetup #88 に参加しました #kzrb

あっという間に2019年も12月を迎えていますね。金沢は冬らしくない気候が続いておりあまり年末という感じがしません。それでも12月のKanazawa.rbは年末LT大会。毎年の恒例行事です。

kzrb.doorkeeper.jp

meetup.kzrb.org

LT

発表に使用したスライドはこちらです。

speakerdeck.com

今年の後半において2つのAWS認定を取得したので動機や勉強方法、ふりかえりなどをさらっと発表しました。AWSイベントにおける認定ラウンジは人権、というパワーワードも出つつ折角なので来年のAWS Summit(もし参加できたら)ではラウンジ入ってみたいですね。発表に含めなかったところでは、先にデベロッパーアソシエイトに合格してそこそこの出来ですっかり油断し、次に受けたSysOpsアドミニストレーターアソシエイトでは合格ラインギリギリで通過してしまいもう少し勉強したかったなというのと運用に比較的関われてないというのが改めて分かったという印象でした。来年はソリューションアーキテクトアソシエイトと上級の認定を取れるように精進していきたいです。

LT大会全体としては、18名もの参加者が各々取り組んでいることや興味を持っていることを発表していて、改めてKanazawa.rbの懐の広さを感じました。

懇親会

meat-meet.owst.jp

最近武蔵にできた肉料理のお店で懇親会。食べるのと話すのに夢中で全然写真撮れませんでしたw 話題としては、機械学習のおすすめ入門本の紹介、Python初心者がJupyter Nootbookを最初から使うべきか問題、リモートワークのつらみ、オープンオフィスの生産性、チームをリードする立場になった話、勉強会と領域初心者の問題などなど個人的には盛り上がりました。残念ながら私は1次会で撤収。

来年もKanazawa.rb盛り上げていきます。

HTTP/2独習メモ2日目(nghttpxでTLS終端)

HTTP/2の学習シリーズ。本当はKanazawa.rb #87もくもく会でやっていたことなんですが参加エントリーを書かない内に次のKanazawa.rbが来そうなので学んだことだけ抜き出してメモっておこうというモチベーションです。

www.blogaomu.com

前回はクライアント-サーバー間のHTTP/2通信についてGoでサーバーを実装し確認するという内容でした。今回は間にリバースプロキシを挟んでTLS終端を担ってもらいます。よくありがちな構成をシンプルにしたものですね。

構成

nghttpx

まずは nghttpx の設定です。基本的な項目のみになっています。注目すべきは2行目の backend で proto=h2 で HTTP/2 プロトコルで接続するようになります。

frontend=0.0.0.0,8443
backend=server,8080;;proto=h2
private-key-file=/etc/nghttpx/cert/server.key
certificate-file=/etc/nghttpx/cert/server.crt

backend

h2cでリクエストを受け付けるように修正したBackendの実装は以下になります。h2c というパッケージが用意されているのでこれを利用します。

package main

import (
    "io"
    "log"
    "net/http"

    "golang.org/x/net/http2"
    "golang.org/x/net/http2/h2c"
)

func main() {
    handler := http.HandlerFunc(index)
    h2s := &http2.Server{}
    srv := &http.Server{
        Addr:    ":8080",
        Handler: h2c.NewHandler(handler, h2s),
    }

    log.Fatal(srv.ListenAndServe())
}

func index(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello World! by HTTP/2")
}

オーケストレーション・リクエストの実行

前回サーバーはdocker化したので、docker-composeでリバースプロキシを含めて構成したいと思います。

version: "3.7"
services:
  server:
    build:
      context: server
  reverse-proxy:
    image: dit4c/nghttpx
    ports:
      - "8443:8443"
    volumes:
      - "./nghttpx:/etc/nghttpx"
      - "./cert:/etc/nghttpx/cert"
    depends_on:
      - server

プロジェクトは以下の構成です。

.
├── cert
│   ├── server.crt
│   └── server.key
├── docker-compose.yml
├── nghttpx
│   └── nghttpx.conf
└── server
      ├── Dockerfile
      └── main.go

これで全ての構成の準備ができたので docker-compose を起動します。

$ docker-compose up

別のターミナルでHTTPSリクエストを行います。

$ curl -v -k https://localhost:8443

*   Trying ::1:8443...
* TCP_NODELAY set
* Connected to localhost (::1) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /usr/local/etc/openssl@1.1/cert.pem
  CApath: /usr/local/etc/openssl@1.1/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=JP
*  start date: Dec 17 23:13:26 2019 GMT
*  expire date: Dec 16 23:13:26 2020 GMT
*  issuer: C=JP
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fe729822a00)
> GET / HTTP/2
> Host: localhost:8443
> user-agent: curl/7.67.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 22
< date: Thu, 19 Dec 2019 07:40:30 GMT
< server: nghttpx
< via: 2 nghttpx
<
* Connection #0 to host localhost left intact
Hello World! by HTTP/2

リバースプロキシを挟んでHTTP/2サーバーにリクエストし正常にレスポンスすることを確認できました。

また、dockerネットワークに接続して tcpdumpWireshark で確認してみます。(tcpdumpイメージは前回参照)

$ docker run \
    --net=container:$(docker ps -f "name=reverse-proxy" --format "{{.ID}}") \
    -v $(pwd)/tcpdump:/var/dump-data \
    tcpdump tcpdump -i eth0 -w /var/dump-data/eth0-2.pcap

https://i.gyazo.com/a8aac324b6fed700367dce17a6e30ef1.png

172.0.0.3 が reverse-proxy で 172.0.0.2 が backend server です。これらの間の通信もHTTP/2で行われていますね。よかったよかった。

まとめ

今回はnghttpxを利用してTLS終端およびバックエンドサーバーへのリバースプロキシを行いました。また、HTTP/2でのリクエスト・レスポンスを確認できました。余談ですが docker-compose.yml をさくっと書けたのは日頃の作業が身に付いてる感じがあって良かったです。

今回のソースコードはこちらにアップロードしています。 github.com

Kanazawa.rb meetup #86 に参加しました #kzrb

最近涼しくなっていよいよ秋だなあと感じるこの頃、みなさまいかがお過ごしでしょうか?今回のkzrbは意識高いもくもく会 @ DMM Games でした。

kzrb.doorkeeper.jp

ちなみに、お昼はビリヤニ(ヤギ)。

HTTP/2の学習

分かるようでよく分からないHTTP/2について以下の資料を見ながら学習していました。

tools.ietf.org

summerwind.jp

8pockets.booth.pm

HTTP/1.1には無かった概念がいくつか出てきていたのでその辺をふむふむ言いながら読んでいました。特にフレームという単位でデータを送受信するというのが特徴的でなるほどと思った部分でした。実際に手元でも確認したいなと思い、クライアント-サーバー間の通信を見てみようと思いtcpdumpで取得したデータをWiresharkで開いて確認するというのを進めていました。クライアントはHTTP/2対応のcurl、サーバーはGoで実装したものを利用しました。server.crt server.key はローカル環境で作成したものです。

package main

import (
    "io"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    var srv http.Server
    srv.Addr = ":8080"

    http2.ConfigureServer(&srv, nil)
    http.HandleFunc("/", index)

    srv.ListenAndServeTLS("server.crt", "server.key")
}

func index(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello World! by HTTP/2")
}

*1

先ほどのソースコードをdockerビルド時にビルドし、dockerコンテナで実行できるように。

FROM golang:1.13

WORKDIR /go/src/app
COPY . .

RUN go get -d -v .
RUN go install -v .

CMD ["app"]

これらを準備してひとまずcurlでリクエストできるか確認。

# サーバー起動
% docker run --rm -p 8080:8080 --name http2-server <image>:latest

# 別のターミナルウィンドウで
% curl -v --http2 https://localhost:8080 --insecure

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=JP
*  start date: Oct 19 05:07:49 2019 GMT
*  expire date: Oct 18 05:07:49 2020 GMT
*  issuer: C=JP
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f8e6c806800)
> GET / HTTP/2
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 22
< date: Mon, 21 Oct 2019 07:44:23 GMT
<
* Connection #0 to host localhost left intact
Hello World! by HTTP/2* Closing connection 0

良さげですね。で、今度はtcpdumpしてみました。以下の記事を参考にしながら。

medium.com

% docker build -t tcpdump - <<EOF 
FROM ubuntu 
RUN apt-get update && apt-get install -y tcpdump 
CMD tcpdump -i eth0 
EOF

% docker network create demo-net
# --nameを指定
% docker run --network demo-net --rm -p 8080:8080 --name http2-server server:latest
# tcpdumpで出力されるファイル置き場
% mkdir tcpdump
# tcpdump実行
% docker run --net=container:http2-server -v $(pwd)/tcpdump:/var/dump-data tcpdump tcpdump -i eth0 -w /var/dump-data/eth0.pcap

この流れで eth0.pcap ファイル生成されて、wiresharkで開くことができました。が、https://wiki.wireshark.org/HTTP2のような形式では表示されずヘッダー情報などが確認できませんでした。この辺はもう少し調べてみたいと思います。

懇親会

tabelog.com

tabelog.com

今回は5人と少人数でしたがその分濃い話で盛り上がりました。信頼の昇龍、お腹一杯であまり食べられなかったのが悔やまれます。某交差点から離れるほど良い店に当たる確率が高くなるという話を聞いてなるほどなーと思いました😇

*1:『はじめてのHTTP/2』41ページより引用