この記事はZOZO AdventCalender 2023シリーズ9の4日目の記事です。
はじめに
Rust初心者の筆者がRustの環境構築から始めて、Rust実装のkubenetes clientであるkube-rs/kubeを使ってpodを立ち上げてHello, kube-rs!
するまでをハンズオンしていきます。
kubernetesの環境はEKS上に既に立ち上げてある環境を使用しますが、今回の記事ではEKSの設定などには触れません。
開発環境は以下の通りです。
- M2 Mac
- zsh
- AstroNvimを入れたNeovim
Rustの公式情報
まずは参考となる情報を探します。
一番始めに確認すべきは公式情報なので、Rustの公式サイトを確認しましょう。
ありがたいことに日本語訳も用意されています。
学ぶ
学ぶのページに色々と参考になりそうな情報が揃っています。
標準ライブラリのAPIガイドもここから飛べそうです。
その他にもThe Rust Programming Languageという書籍もオンラインで読めるようです。
非公式ではあるようですが、日本語訳もありました。
これらの情報を参考にしつつ進めていきます。
Rustの環境構築
まずはrustの環境構築から始めます。
筆者の環境はM2Macなのでmacを前提に進めていきます。
少し調べてみるとRustではrustupというツールを使ってツールチェーンの管理を行うようです。
macならHomebrewでもインストールできるようですが、ここは公式ドキュメントに従ってcurlでshellスクリプトを取得してインストールしていきたいと思います。
実行コマンドは以下の通りです。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
公式ドキュメントによると、
Rustの開発環境において、全てのツールは~/.cargo/binディレクトリにインストールされ、ここにrustc、cargo、rustupを含むRustのツールチェーンが置かれます。
よって、このディレクトリをPATH環境変数に含めるのが、Rustの開発者にとっての通例となっています。
とあるので~/.cargo/bin
をPATHに追加するように~/.zshrc
に以下を追加します。
export PATH="$HOME/.cargo/bin:$PATH"
これでひとまず最低限rustを実行する環境が整いました。
その他の開発ツール
公式でも紹介されているThe Rust Programming Language 日本語版に、付録として便利な開発ツールが載っています。
ここで紹介されている各種ツールはとりあえず入れておきましょう。
- rustfmt
- コードフォーマッタ
- rustfix
- rustをインストールすると同梱されているツールでコンパイラの警告を自動で直してくれる。
cargo fix
で実行する
- rustをインストールすると同梱されているツールでコンパイラの警告を自動で直してくれる。
- clippy
- いわゆる静的解析ツール
- rust-analyzer
- LSP(LanguageServerProtocol)に対応したServer。IDEと連携して自動補完・定義ジャンプ・インラインのエラー表示ができるようになる。
- 書籍で紹介されているのはrlsですが、rlsのREADMEに
rust-analyzer
を使うように書いてあるのでrust-analyzerを入れていきます。
nvim設定
保存時にフォーマットを自動で実行するように設定をします。
rustfmtのREADMEに各種editorでの設定方法へのリンクがあるので、
vimの設定に飛ぶとrust.vim
というvimのpluginが用意されてるようなので入れていきましょう。
筆者はAstroNvimを使っているので、~/.config/nvim/lua/user/init.lua
に設定を入れていきます。
{
"rust-lang/rust.vim",
lazy = false,
config = function()
vim.g.rustfmt_autosave = 1
end
}
これで保存時に自動でフォーマットされるようになります。
LSP実装のrust-analyzer
も準備しておきます。
AstroNvimであればmason-lspconfigがあるので:LspInstall rust
でインストールできるのでサクッと入れれます。
ひととおり環境を整えたらまずはHello, World!
してみます。
The Rust Programming Language 日本語版のHello, World!に従ってやっていきます。
fn main() {
println!("Hello, world!")
}
コードをコンパイルして実行してみます。
$ rustc main.rs
$ ./main
Hello, world!
$
実行できました!
rustのコンパイルと実行が確認できたので、早速podを立ち上げるコードを書いていきたいと思います。
Hello, Kube-rs
まずは新しいプロジェクトを作成します。
RustにはCargoというビルドシステム兼パッケージマネージャがあるので、cargo
コマンドを使ってプロジェクトを作成します。
$ cargo new hello_kubers
Created binary (application) `hello_kubers` package
$
このコマンドを実行すると以下の構成でプロジェクトが作成されます。
gitディレクトリとgitignoreも生成されるようです。
$ tree -a
.
├── .git
│ ├── HEAD
│ ├── config
│ ├── description
│ ├── hooks
│ │ └── README.sample
│ ├── info
│ │ └── exclude
│ ├── objects
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
11 directories, 8 files
$
それでは本題のkube-rsを使ってkubernetesクラスタにpodを立ち上げるまでをやっていきたいと思います。
kube-rs Install
kube-rsのREADMEに従ってインストールします。
Cargo.toml
に下記を追加しましょう。
非同期処理を行うためのライブラリであるtokio
も一緒に入れておきます。
[dependencies]
kube = { version = "0.87.1", features = ["runtime", "derive"] }
k8s-openapi = { version = "0.20.0", features = ["latest"] }
tokio = { version = "1", features = ["full"] }
podを立ち上げてHello, kube-rs!する
リポジトリを眺めているとexamplesに色々とサンプルコードが載っています。
その中にあるpod_attach.rsがちょうど今回やりたいことのサンプルになっているのでそのまま使わせてもらいましょう。
とりあえずコードを全てコピーして、Podを作成するためのリソース定義を下記のように変更します。
let p: Pod = serde_json::from_value(serde_json::json!({
"apiVersion": "v1",
"kind": "Pod",
"metadata": { "name": "test-kane8n" },
"spec": {
"containers": [{
"name": "test-kane8n",
"image": "alpine",
"command": ["sh", "-c", "echo \"Hello, kube-rs!\" && sleep 10"],
}],
}
}))?;
またpod名を指定している箇所も適宜変更します。
ビルドはcargo build
コマンドでやりましょう。
この時点でビルドしてみると大量のエラーを吐いてビルドが失敗します。
行数が多いのでここに載せるのは省きますが、どうやら色々と依存関係の設定なんかが足りてないようなのでexamplesのCargo.tomlを参考に足りてなさそうなものを足していきます。
最終的には以下のようにすることでビルドができました。
[package]
name = "hello_kubers"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["ws"]
ws = ["kube/ws"]
[dependencies]
kube = { version = "0.87.1", features = ["runtime", "derive"] }
k8s-openapi = { version = "0.20.0", features = ["latest"] }
tokio = { version = "1", features = ["full"] }
tokio-util = "0.7.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1.36"
tracing-subscriber = "0.3.3"
anyhow = "1.0.44"
futures = "0.3.17"
いったんビルドができたので、試しに実行してみましょう。
ビルドしたバイナリはtarget/debug/hello_kubers
にあります。
$ ./target/debug/hello_kubers
2023-11-30T16:03:34.812853Z INFO hello_kubers: Creating a Pod that outputs "Hello, kube-rs!"
2023-11-30T16:03:34.919201Z INFO hello_kubers: Added test-kane8n
2023-11-30T16:03:37.319434Z INFO hello_kubers: Ready to attach to test-kane8n
2023-11-30T16:03:46.967688Z INFO hello_kubers: Status { code: None, details: None, message: None, metadata: ListMeta { continue_: None, remaining_item_count: None, resource_version: None, self_link: None }, reason: None, status: Some("Success") }
$
podを起動するのに成功しました!
ちなみにkubernetesに接続するためのconfigですが、Client::try_default()
を使用する場合、kube-rs
はデフォルトのkubeconfig
ファイルの場所を探し、その場所からConfigオブジェクトを生成して使用するみたいです。
configを指定したい場合はClient::try_from_kubeconfig(kubeconfig_path)
を使えば良いです。
しかし標準出力しているはずのHello, kube-rs!
が出力されていません。(combined_output
で出力される想定だったのですが・・・
ここは一旦深掘りするのは置いておいて、もう一度examplesを眺めてみるとlog_stream.rsに良さそうなサンプルがあるので試してみます。
info!("Fetching logs for test-kane8n");
let mut logs = pods
.log_stream(
"test-kane8n",
&LogParams {
follow: true,
container: None,
tail_lines: None,
since_seconds: None,
timestamps: false,
..LogParams::default()
},
)
.await?
.lines();
while let Some(line) = logs.try_next().await? {
println!("{}", line);
}
doc.rsによると、AsyncBufRead
でログをストリームできるもののようです。
このコードを入れて試してみます。
futures::{AsyncBufReadExt}
とkube::{api::{LogParams}}
をuseするのも忘れずに
$ ./target/debug/hello_kubers
2023-11-30T16:33:08.929252Z INFO hello_kubers: Creating a Pod that outputs "Hello, kube-rs!"
2023-11-30T16:33:09.056016Z INFO hello_kubers: Added test-kane8n
2023-11-30T16:33:11.571052Z INFO hello_kubers: Ready to attach to test-kane8n
2023-11-30T16:33:21.116735Z INFO hello_kubers: Status { code: None, details: None, message: None, metadata: ListMeta { continue_: None, remaining_item_count: None, resource_version: None, self_link: None }, reason: None, status: Some("Success") }
2023-11-30T16:33:21.116922Z INFO hello_kubers: Fetching logs for test-kane8n
Hello, kube-rs!
$
Hello, kube-rs
が取得できました!
Rust初心者ですが、公式の情報とkube-rsのサンプルコードが充実していたのでそこまでハマることなく実装できたかなという所感です。
最後に今回実装したコードをGithubに上げておいたので載せておきます。
以上、Rustでkube-rsハンズオンでした。