Corriendo Rust en mi cluster K3S Oracle ARM32v7


rust

Introducción

En este artículo vamos a explorar cómo crear un proyecto de Rust de ejemplo y un Dockerfile para correrlo en arquitecturas ARM32v7.


Para configurar el clúster usé este proyecto k3s-oci-cluster, ya que Oracle ofrece una capa gratuita muy generosa para cargas de trabajo ARM, quizás quieras probarlo o usar tu clúster de raspberry pi.


El código fuente de este artículo está aquí RCV y la imagen de docker está aquí.


Prerrequisitos

El clúster es opcional, si tenés algún dispositivo usando Linux en ARM32v7 o ARM64v8 deberías poder usar los ejemplos de docker.


Vamos al ejemplo

Creando el proyecto

Vamos a crear un nuevo proyecto de Rust con Cargo, como notarás obtendremos un proyecto muy básico que nos ayudará a comenzar:

 cargo new rcv
     Created binary (application) `rcv` package
     
 cd rcv
 ls
Cargo.toml  src

Nuestro ejemplo y las dependencias

Estaba pensando en procesar archivos markdown y mostrarlos como HTML, así que básicamente eso es lo que hace el código. Está lejos de ser óptimo, pero es lo suficientemente bueno para ilustrar el ejemplo. Primero, vamos a agregar algunos crates para el servidor web (Actix) y para convertir markdown a html.

[package]
name = "rcv"
version = "0.1.0"
edition = "2021"

# Ver más claves y sus definiciones en https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-web = "4"
markdown = "0.3"

Vamos a agregar algo de código

Este simple snippet solo escucha solicitudes GET en /, registra una línea con la marca de tiempo en Unix y la IP, y devuelve el contenido del archivo cv.md, que es mi Currículum Vitae.

use actix_web::{get, App, HttpResponse, HttpServer};
use actix_web::{HttpRequest, Responder};
use std::fs;
use std::time::{Duration, SystemTime};

extern crate markdown;

#[get("/")]
async fn root(req: HttpRequest) -> impl Responder {
    let con_info = req.connection_info();
    let peer_ip = &req.peer_addr().unwrap().to_string();

    let ip = con_info
        .realip_remote_addr()
        .unwrap()
        .split(':')
        .next()
        .unwrap_or(peer_ip);

    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap_or(Duration::from_millis(0));

    println!("[{}][{}]: Processing cv request...", now.as_secs(), ip);

    let data = fs::read_to_string("./cv.md").expect("Unable to read file");
    let html: String = markdown::to_html(&data);

    HttpResponse::Ok().body(html)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(root))
        .bind(("0.0.0.0", 8080))?
        .run()
        .await
}

logs

[1662136558][127.0.0.1]: Processing cv request...
[1662136591][127.0.0.1]: Processing cv request...

En este punto tenemos lo suficiente para ejecutar y probar localmente, pero ¿qué pasa con otras arquitecturas? (Estoy corriendo en linux-amd64). Podés probarlo localmente si querés, ejecutando cargo run.


Dockerfile para ARM32v7

Este Dockerfile se puede optimizar y asegurar de muchas maneras, pero para simplificar es suficiente para empezar a trabajar en algo. Además, proporcionaremos la seguridad en tiempo de ejecución a través de las APIs de Kubernetes.

Tenemos que considerar dos cosas aquí: primero, necesitamos crear un binario ARM32v7 usando Rust, y luego necesitamos una imagen de Docker para esa arquitectura. Eso es básicamente lo que hace el Dockerfile.

## builder
FROM rust:1.63.0 as builder

RUN apt update && apt upgrade -y
RUN apt install -y g++-arm-linux-gnueabihf libc6-dev-armhf-cross

RUN rustup target add armv7-unknown-linux-gnueabihf
RUN rustup toolchain install stable-armv7-unknown-linux-gnueabihf

WORKDIR /usr/src/app

COPY . .

ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
    CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
    CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++

RUN cargo build --target armv7-unknown-linux-gnueabihf --release

## release
FROM arm32v7/rust:1.63

WORKDIR /usr/src/app

COPY --from=builder /usr/src/app/target/armv7-unknown-linux-gnueabihf/release/rcv /usr/src/app
COPY --from=builder /usr/src/app/cv.md /usr/src/app

CMD ["/usr/src/app/rcv"]

Usando docker para armar la imagen y subirla al registro (docker image)

Se puede encontrar aqui.

 docker build . -f Dockerfile.armv7
Sending build context to Docker daemon  894.2MB
Step 1/14 : FROM rust:1.63.0 as builder
 ---> 2a7d3c69bbf0
Step 2/14 : RUN apt update && apt upgrade -y
 ---> Using cache
 ---> 3a3120a0cb99
Step 3/14 : RUN apt install -y g++-arm-linux-gnueabihf libc6-dev-armhf-cross
 ---> Using cache
 ---> 78e25363f688
Step 4/14 : RUN rustup target add armv7-unknown-linux-gnueabihf
 ---> Using cache
 ---> dc97e983b392
Step 5/14 : RUN rustup toolchain install stable-armv7-unknown-linux-gnueabihf
 ---> Using cache
 ---> a1266f5b3cfa
Step 6/14 : WORKDIR /usr/src/app
 ---> Using cache
 ---> ff1efb7d4bce
Step 7/14 : COPY . .
 ---> a0ceff61a547
Step 8/14 : ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc     CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc     CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
 ---> Running in 0249ca22cd26
Removing intermediate container 0249ca22cd26
 ---> a803cfa71e49
Step 9/14 : RUN cargo build --target armv7-unknown-linux-gnueabihf --release
 ---> Running in 533c57b89b90
    Updating crates.io index
 Downloading crates ...
  Downloaded actix-macros v0.2.3
  Downloaded firestorm v0.5.1
  Downloaded bytestring v1.1.0
  Downloaded futures-task v0.3.23
  Downloaded actix-service v2.0.2
  Downloaded crc32fast v1.3.2
  Downloaded httpdate v1.0.2
  Downloaded miniz_oxide v0.5.3
  Downloaded generic-array v0.14.6
  Downloaded itoa v1.0.3
  Downloaded once_cell v1.13.1
  Downloaded jobserver v0.1.24
  Downloaded rand v0.8.5
  Downloaded rustc_version v0.4.0
  Downloaded local-channel v0.1.3
  Downloaded serde_json v1.0.85
  Downloaded sha1 v0.10.1
  Downloaded tokio-util v0.7.3
  Downloaded zstd-safe v5.0.2+zstd.1.5.2
  Downloaded unicode-normalization v0.1.21
  Downloaded bitflags v1.3.2
  Downloaded actix-rt v2.7.0
  Downloaded actix-utils v3.0.0
  Downloaded slab v0.4.7
  Downloaded ryu v1.0.11
  Downloaded serde_urlencoded v0.7.1
  Downloaded actix-router v0.5.0
  Downloaded bytes v1.2.1
  Downloaded ahash v0.7.6
  Downloaded alloc-no-stdlib v2.0.3
  Downloaded zstd v0.11.2+zstd.1.5.2
  Downloaded tinyvec_macros v0.1.0
  Downloaded form_urlencoded v1.0.1
  Downloaded cpufeatures v0.2.4
  Downloaded cookie v0.16.0
  Downloaded convert_case v0.4.0
  Downloaded derive_more v0.99.17
  Downloaded actix-codec v0.5.0
  Downloaded futures-sink v0.3.23
  Downloaded actix-server v2.1.1
  Downloaded crypto-common v0.1.6
  Downloaded actix-web-codegen v4.0.1
  Downloaded h2 v0.3.14
  Downloaded httparse v1.7.1
  Downloaded futures-util v0.3.23
  Downloaded num_cpus v1.13.1
  Downloaded markdown v0.3.0
  Downloaded local-waker v0.1.3
  Downloaded time-macros v0.2.4
  Downloaded percent-encoding v2.1.0
  Downloaded scopeguard v1.1.0
  Downloaded smallvec v1.9.0
  Downloaded typenum v1.15.0
  Downloaded tracing-core v0.1.29
  Downloaded unicode-bidi v0.3.8
  Downloaded tinyvec v1.6.0
  Downloaded url v2.2.2
  Downloaded signal-hook-registry v1.4.0
  Downloaded socket2 v0.4.6
  Downloaded semver v1.0.13
  Downloaded rand_core v0.6.3
  Downloaded flate2 v1.0.24
  Downloaded matches v0.1.9
  Downloaded fnv v1.0.7
  Downloaded block-buffer v0.10.2
  Downloaded ppv-lite86 v0.2.16
  Downloaded pin-project-lite v0.2.9
  Downloaded mio v0.8.4
  Downloaded num_threads v0.1.6
  Downloaded paste v1.0.8
  Downloaded language-tags v0.3.2
  Downloaded indexmap v1.9.1
  Downloaded mime v0.3.16
  Downloaded rand_chacha v0.3.1
  Downloaded version_check v0.9.4
  Downloaded log v0.4.17
  Downloaded parking_lot_core v0.9.3
  Downloaded lock_api v0.4.8
  Downloaded getrandom v0.2.7
  Downloaded tracing v0.1.36
  Downloaded digest v0.10.3
  Downloaded http v0.2.8
  Downloaded alloc-stdlib v0.2.1
  Downloaded futures-core v0.3.23
  Downloaded time v0.3.14
  Downloaded idna v0.2.3
  Downloaded actix-http v3.2.1
  Downloaded actix-web v4.1.0
  Downloaded brotli-decompressor v2.3.2
  Downloaded regex v1.6.0
  Downloaded regex-syntax v0.6.27
  Downloaded pipeline v0.5.0
  Downloaded memchr v2.5.0
  Downloaded unicode-ident v1.0.3
  Downloaded cc v1.0.73
  Downloaded aho-corasick v0.7.18
  Downloaded quote v1.0.21
  Downloaded pin-utils v0.1.0
  Downloaded cfg-if v1.0.0
  Downloaded base64 v0.13.0
  Downloaded proc-macro2 v1.0.43
  Downloaded adler v1.0.2
  Downloaded lazy_static v1.4.0
  Downloaded hashbrown v0.12.3
  Downloaded serde v1.0.144
  Downloaded autocfg v1.1.0
  Downloaded parking_lot v0.12.1
  Downloaded syn v1.0.99
  Downloaded tokio v1.20.1
  Downloaded encoding_rs v0.8.31
  Downloaded zstd-sys v2.0.1+zstd.1.5.2
  Downloaded libc v0.2.132
  Downloaded brotli v3.3.4
   Compiling libc v0.2.132
   Compiling cfg-if v1.0.0
   Compiling memchr v2.5.0
   Compiling autocfg v1.1.0
   Compiling log v0.4.17
   Compiling version_check v0.9.4
   Compiling pin-project-lite v0.2.9
   Compiling futures-core v0.3.23
   Compiling bytes v1.2.1
   Compiling parking_lot_core v0.9.3
   Compiling once_cell v1.13.1
   Compiling smallvec v1.9.0
   Compiling scopeguard v1.1.0
   Compiling serde v1.0.144
   Compiling proc-macro2 v1.0.43
   Compiling itoa v1.0.3
   Compiling typenum v1.15.0
   Compiling quote v1.0.21
   Compiling unicode-ident v1.0.3
   Compiling futures-task v0.3.23
   Compiling syn v1.0.99
   Compiling futures-util v0.3.23
   Compiling pin-utils v0.1.0
   Compiling futures-sink v0.3.23
   Compiling percent-encoding v2.1.0
   Compiling alloc-no-stdlib v2.0.3
   Compiling local-waker v0.1.3
   Compiling tinyvec_macros v0.1.0
   Compiling matches v0.1.9
   Compiling crc32fast v1.3.2
   Compiling zstd-safe v5.0.2+zstd.1.5.2
   Compiling fnv v1.0.7
   Compiling regex-syntax v0.6.27
   Compiling ppv-lite86 v0.2.16
   Compiling paste v1.0.8
   Compiling adler v1.0.2
   Compiling httparse v1.7.1
   Compiling hashbrown v0.12.3
   Compiling encoding_rs v0.8.31
   Compiling convert_case v0.4.0
   Compiling firestorm v0.5.1
   Compiling time-macros v0.2.4
   Compiling serde_json v1.0.85
   Compiling num_threads v0.1.6
   Compiling unicode-bidi v0.3.8
   Compiling bitflags v1.3.2
   Compiling ryu v1.0.11
   Compiling mime v0.3.16
   Compiling language-tags v0.3.2
   Compiling base64 v0.13.0
   Compiling httpdate v1.0.2
   Compiling lazy_static v1.4.0
   Compiling pipeline v0.5.0
   Compiling tinyvec v1.6.0
   Compiling actix-utils v3.0.0
   Compiling alloc-stdlib v0.2.1
   Compiling form_urlencoded v1.0.1
   Compiling tracing-core v0.1.29
   Compiling miniz_oxide v0.5.3
   Compiling http v0.2.8
   Compiling bytestring v1.1.0
   Compiling generic-array v0.14.6
   Compiling ahash v0.7.6
   Compiling cookie v0.16.0
   Compiling lock_api v0.4.8
   Compiling tokio v1.20.1
   Compiling slab v0.4.7
   Compiling indexmap v1.9.1
   Compiling brotli-decompressor v2.3.2
   Compiling tracing v0.1.36
   Compiling flate2 v1.0.24
   Compiling aho-corasick v0.7.18
   Compiling unicode-normalization v0.1.21
   Compiling actix-service v2.0.2
   Compiling socket2 v0.4.6
   Compiling signal-hook-registry v1.4.0
   Compiling mio v0.8.4
   Compiling getrandom v0.2.7
   Compiling num_cpus v1.13.1
   Compiling jobserver v0.1.24
   Compiling brotli v3.3.4
   Compiling idna v0.2.3
   Compiling rand_core v0.6.3
   Compiling parking_lot v0.12.1
   Compiling regex v1.6.0
   Compiling cc v1.0.73
   Compiling rand_chacha v0.3.1
   Compiling rand v0.8.5
   Compiling time v0.3.14
   Compiling url v2.2.2
   Compiling local-channel v0.1.3
   Compiling markdown v0.3.0
   Compiling zstd-sys v2.0.1+zstd.1.5.2
   Compiling block-buffer v0.10.2
   Compiling crypto-common v0.1.6
   Compiling digest v0.10.3
   Compiling sha1 v0.10.1
   Compiling actix-router v0.5.0
   Compiling serde_urlencoded v0.7.1
   Compiling tokio-util v0.7.3
   Compiling actix-rt v2.7.0
   Compiling actix-server v2.1.1
   Compiling actix-codec v0.5.0
   Compiling h2 v0.3.14
   Compiling derive_more v0.99.17
   Compiling actix-web-codegen v4.0.1
   Compiling actix-macros v0.2.3
   Compiling zstd v0.11.2+zstd.1.5.2
   Compiling actix-http v3.2.1
   Compiling actix-web v4.1.0
   Compiling rcv v0.1.0 (/usr/src/app)
    Finished release [optimized] target(s) in 1m 44s
Removing intermediate container 533c57b89b90
 ---> 433b7b6c53f5
Step 10/14 : FROM arm32v7/rust:1.63
 ---> d0646b193e07
Step 11/14 : WORKDIR /usr/src/app
 ---> Using cache
 ---> d3245c5f0d73
Step 12/14 : COPY --from=builder /usr/src/app/target/armv7-unknown-linux-gnueabihf/release/rcv /usr/src/app
 ---> Using cache
 ---> b1847312a2fe
Step 13/14 : COPY --from=builder /usr/src/app/cv.md /usr/src/app
 ---> 67c24d175043
Step 14/14 : CMD ["/usr/src/app/rcv"]
 ---> [Warning] The requested image's platform (linux/arm/v7) does not match the detected host platform (linux/amd64) and no specific platform was requested
 ---> Running in d20e832fdb10
Removing intermediate container d20e832fdb10
 ---> 4236fbee04d0
Successfully built 4236fbee04d0


 docker tag 4236fbee04d0 kainlite/rcv:armv7-2
 docker push kainlite/rcv:armv7-2
The push refers to repository [docker.io/kainlite/rcv]
e6c497a8be6a: Pushed
41cb37c86eb4: Layer already exists
659939c01292: Layer already exists
54a3ca211559: Layer already exists
1f4f3f20d97e: Layer already exists
d55191df9034: Layer already exists
403a5f26ee02: Layer already exists
6c3d1ef471ee: Layer already exists
b74e98d1b921: Layer already exists
armv7-2: digest: sha256:86be73465a5e4819b97d4aafe8195b977a4e9b1d6ff3780315972ad23223f812 size: 2216

Revisemos rápidamente los manifiestos

Los manifiestos son bastante simples, podés verlos aquí. Como podés observar, estamos restringiendo el usuario y los privilegios del contenedor usando el SecurityContext del pod y del contenedor.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rcv-deployment
  labels:
    name: rcv
spec:
  replicas: 3
  selector:
    matchLabels:
      name: rcv
  template:
    metadata:
      labels:
        name: rcv
    spec:
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
      containers:
      - name: rcv
        image: kainlite/rcv:armv7-2
        ports:
        - containerPort: 8080
        securityContext:
          allowPrivilegeEscalation: false
---
apiVersion: v1
kind: Service
metadata:
  name: rcv
spec:
  selector:
    name: rcv
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rcv-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: "rcv.techsquad.rocks"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: rcv
            port:
              number: 8080

Desplegándolo

Asumiendo que ya tenés un clúster en funcionamiento, esto se puede desplegar de la siguiente manera. Verás un deployment, un service y los recursos de ingress. También vas a necesitar una entrada DNS si querés usarlo como lo hice aquí:

 kubectl apply -f manifests/
deployment.apps/rcv-deployment created
ingress.networking.k8s.io/rcv-ingress created
service/rcv created

 kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
rcv-deployment-55588c6f68-5gshl   1/1     Running   0          7s
rcv-deployment-55588c6f68-d7r84   1/1     Running   0          7s
rcv-deployment-55588c6f68-zw27j   1/1     Running   0          7s

 kubectl get ingress
NAME          CLASS   HOSTS                 ADDRESS      PORTS   AGE
rcv-ingress   nginx   rcv.techsquad.rocks   10.0.0.104   80      12s

Extra

Podés verlo funcionando acá, un Curriculum Vitae en HTML muy básico. Si no funciona, no te preocupes, estoy planeando actualizar el clúster y agregar https al ejemplo para otro artículo, eventualmente estará de nuevo online. Sin embargo, si querés verlo de todos modos, probá ejecutar el ejemplo y construyendo la imagen en tu propia máquina.

Para más detalles y ver cómo encaja todo, te animo a clonar el repositorio, probarlo y modificarlo para hacer tu propia versión.


Limpiando los recursos

Para limpiar los recursos podés hacer esto:

 kubectl delete -f manifests
deployment.apps "rcv-deployment" deleted
ingress.networking.k8s.io "rcv-ingress" deleted
service "rcv" deleted

Notas finales

No te olvides de revisar los enlaces si querés aprender más sobre los ejemplos. ¡Espero que lo hayas disfrutado! Nos vemos en twitter o github.

El código fuente de este artículo está aquí.


Errata

Si encontrás algún error o tenés alguna sugerencia, mandame un mensaje para que pueda corregirlo.



No tienes cuenta? Regístrate aqui

Ya registrado? Iniciar sesión a tu cuenta ahora.

Iniciar session con GitHub
Iniciar sesion con Google
  • Comentarios

    Online: 0

Por favor inicie sesión para poder escribir comentarios.

by Gabriel Garrido