Corriendo Rust en mi cluster K3S Oracle ARM32v7
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.
-
Comentarios
Online: 0
Por favor inicie sesión para poder escribir comentarios.