Найти тему
Один Rust не п...Rust

Kubernetes & Rust

Оглавление

Для чего нужна данная статья? :

- Создать оператор в Kubernetes кластере.

Зачем Вам это уметь? :

Научиться управлять пользовательскими ресурсами (CRD) в кластере Kubernetes.

Создайте новый проект на Rust:

cargo new my-k8s-operator

cd my-k8s-operator

Откройте файл Cargo.toml и добавьте следующие зависимости:

[dependencies]

tokio = { version = "1", features = ["full"] }

kube = { version = "0.80", features = ["runtime", "derive"] }

serde = { version = "1.0", features = ["derive"] }

serde_json = "1.0"

thiserror = "1.0"

log = "0.4"

env_logger = "0.9"


Давайте определим нашу пользовательскую структуру данных (CRD), которая будет управляться оператором.

crd.rs

use kube::CustomResource;

use serde::{Deserialize, Serialize};

#[derive(CustomResource, Debug, Clone, Serialize, Deserialize, Default)]

#[kube(group = "example.com", version = "v1", kind = "App", namespaced)]

#[kube(status = "AppStatus")]

pub struct AppSpec {

pub name: String,

pub replicas: i32,

}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]

pub struct AppStatus {

pub available_replicas: i32,

}

Теперь реализуем логику оператора, которая будет отслеживать изменения в CRD и выполнять соответствующие действия.

main.rs

mod crd;

use crate::crd::{App, AppSpec, AppStatus};

use kube::{

api::{Api, Patch, PatchParams},

runtime::{controller::{self, Controller}, reflector::ObjectRef,

watcher::Config},

Client, ResourceExt,

};

use serde_json::json;

use std::sync::Arc;

use tokio::time::{sleep, Duration};

use tracing::{info, instrument};


#[instrument(skip(apps))]

async fn reconcile(app: Arc<App>, apps: Api<App>) -> Result<(),

controller::Error> {

let name = app.name_any();

let ns = app.namespace().unwrap();

info!("Reconciling {}/{}", ns, name);

let spec = app.spec.clone();

let status = AppStatus {

available_replicas: spec.replicas,

// Простая логика: установка статуса равным желаемому количеству реплик

};

let patch = json!({

"status": status,

});

apps.patch_status(&name, &PatchParams::apply("kube-rs"),

&Patch::Merge(&patch)).await?;

Ok(())

}

async fn error_policy(_app: Arc<App>, _error: &controller::Error, _ctx:

controller::Context<()>) -> controller::ReconcilerAction {

sleep(Duration::from_secs(5)).await;

controller::ReconcilerAction {

requeue_after: Some(Duration::from_secs(5)),

}

}
#[tokio::main]

async fn main() -> Result<(), Box<dyn std::error::Error>> {

env_logger::init();

let client = Client::try_default().await?;

let apps: Api<App> = Api::all(client.clone());

let context = controller::Context::new(());

Controller::new(apps.clone(), Config::default())

.run(reconcile, error_policy, context)

.for_each(|res| async move {

match res {

Ok(o) => info!("Reconciled {:?}", o),

Err(e) => info!("Reconcile failed: {:?}", e),

}

})

.await;

Ok(())

}


reconcile— Основная функция, которая возникает при каждом выступе CRD. В ней определены, какие действия необходимо активировать в зависимости от текущего состояния ресурса.

error_policy— функция, которая определяет поведение оператора при устранении ошибок. В данном случае она просто повторяет это через 5 секунд.

Controller::new— Создаёт контроллер, который будет отслеживать изменения ресурсов CRD и сохранять функцию reconcile.

Для сборки и запуска оператора:

cargo build --release
Dockerfile для оператора:

FROM rust:slim-buster AS builder

WORKDIR /app

COPY . .

RUN cargo build --release

FROM debian:buster-slim

COPY --from=builder /app/target/release/k8s-operator /usr/local/bin/

ENTRYPOINT ["k8s-operator"]

Возьмите Docker-образ:

docker build -t your-username/k8s-operator:latest .

Загрузите образ в реестр Docker:

docker push your-username/k8s-operator:latest

Создайте манифест для оператора развертывания в Kubernetes:

apiVersion: apps/v1

kind: Deployment

metadata:

name: k8s-operator

spec:

replicas: 1

selector:

matchLabels:

app: k8s-operator

template:

metadata:

labels:

app: k8s-operator

spec:
containers:

- name: k8s-operator

image: your-username/k8s-operator:latest

imagePullPolicy: Always

Примените манифесты и разверните оператора:

kubectl apply -f operator-deployment.yaml

Создайте объект вашего пользовательского ресурса (CRD) и наблюдайте за действиями оператора - my-app.yaml:

apiVersion: example.com/v1

kind: App

metadata:

name: my-app

namespace: default

spec:

name: "My Rust App"

replicas: 3

Замените этот файл:

kubectl apply -f my-app.yaml

Теперь ваш оператор будет следить за типом объектов App и обновлять статус на основе количества реплик, указанного в характеристиках.