decadence

個人のメモ帳

RustでもServer::Starterでhot deployをする

Server::Starterを利用すれば、hot-deployが実現出来る。

Server::Starter とは

start_server をざっくりと説明すると、 start_server が作成したsocketを子プロセスへ共有して、あとは start_server にSIGHUPのシグナルを送ってあげることで、子プロセスを入れ替えてhot deployが出来るものだ。詳しくは以下の記事を読んでも良いし、実装自体も少ないのでコードを読めば良い。

Server::Starterから学ぶhot deployの仕組み - $shibayu36->blog;

この子プロセスというのは、start_server の子プロセスになるだけで、以下のような特定の条件を満たせればなんでも良いのだ。

  • hot deploy時に start_server がSIGTERMを子プロセスへ送るので、SIGTERMによりgraceful shutdownを行う
  • 子プロセス起動時に start_server が作成したファイルディスクリプタの情報が環境変数で渡ってくるので、それを用いてacceptする (しなくてもいい)

この対応は、過去にも多くの人がやってきたことではあるが、Rustでも簡単に出来るように、今回、子プロセス用のcrateを作成した。

https://crates.io/crates/server-starter-listener

SERVER_STARTER_PORT と TcpListener / UnixListener

通常tcp接続のためのTcpListenerやunix domain socketのためのUnixListenerは、以下のようにbindをして利用する。

TcpListener::bind("localhost:8080")
UnixListener::bind("/path/to/socket")

しかし、 start_server を利用する場合は、親プロセスで作成されたファイルディスクリプタを流用する必要がある。start_server では SERVER_STARTER_PORT という環境変数から情報が渡ってくるため、こちらからファイルディスクリプタを取得する。ファイルディスクリプタを流用するインターフェースはRustにはきちんと備わっており、明示的に以下のようにFromRawFdをuseすると利用することが出来る。

use std::os::unix::io::FromRawFd;

let tcp_listener = unsafe { TcpListener.from_raw_fd(fd) };
let uds_listener = unsafe { UnixListener::from_raw_fd(fd) };

server-starter-listener-rs では、このlistenerを取得する部分までを行ってくれる実装になっている。

actix-web

Rustでよく使われるWebFrameworkの一つがactix-webである。actix-webではTcpListenerを受け取るAPIが用意されているためserver-starter-listener-rsから受け取ったlistenerをそのまま渡してあげれば良い。

let listener = server_starter_listener::listeners()?.pop()?;
HttpServer::new(|| {
  App::new()
     .wrap(middleware::Logger::default())
     .service(web::resource("/hello").route(web::get().to(|| HttpResponse::Ok())))
}).listen(listener)?.run()?

また、 unix domain socketを利用する listen_uds も用意されており、こちらは以下のようにfeatureを有効にすると利用出来る。

[dependencies]
actix-web = { version = "...", features = ["uds"] }

最初に書いたように子プロセスへはSIGTERMを送った際にgraceful shutdownする必要があるのだが、以下にある通り、actix-webではgraceful shutdownが出来るようになっている。

actix-website/server.md at e5fb02d5af86d0abe505a0bf6684362f368c333f · actix/actix-website · GitHub

以上から、Rust (主にactix-web) ではServer::Starterを使えば、気軽にhot deploy出来ることが分かる。

こちらで利用したサンプルコードは、以下のrepositoryのexamplesにもある。

github.com

最後に

こちらの記事は FOLIO Advent Calender 2019 として書かれた。

今の所、社内にRustのコードは1つも無いが、社内Slackに #lang-rust channelはある。

qiita.com