5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZOZOAdvent Calendar 2023

Day 4

Rust kube-rsハンズオン

Last updated at Posted at 2023-12-03

この記事は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で実行する
  • clippy
    • いわゆる静的解析ツール
  • rust-analyzer
    • LSP(LanguageServerProtocol)に対応したServer。IDEと連携して自動補完・定義ジャンプ・インラインのエラー表示ができるようになる。
    • 書籍で紹介されているのはrlsですが、rlsのREADMErust-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!に従ってやっていきます。

main.rs
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も一緒に入れておきます。

Cargo.toml
[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を作成するためのリソース定義を下記のように変更します。

main.rs
    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を参考に足りてなさそうなものを足していきます。
最終的には以下のようにすることでビルドができました。

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ハンズオンでした。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?