Corriendo Rust en mi cluster K3S Oracle ARM32v7

2020-09-02 | Gabriel Garrido | 9 min de lectura
Share:
On this page
  1. Introducción
  2. Prerrequisitos
  3. Vamos al ejemplo
  4. Creando el proyecto
  5. Nuestro ejemplo y las dependencias
  6. Vamos a agregar algo de código
  7. logs
  8. Dockerfile para ARM32v7
  9. Usando docker para armar la imagen y subirla al registro (docker image)
  10. Revisemos rápidamente los manifiestos
  11. Desplegándolo
  12. Extra
  13. Limpiando los recursos
  14. Notas finales
  15. Errata

Apoya este blog

Si te resulta util este contenido, considera apoyar el blog.

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.



$ Comentarios

Online: 0

Por favor inicie sesión para poder escribir comentarios.

2020-09-02 | Gabriel Garrido