Skip to content

k3s

Kubernetes en miniatura y productivo con k3s

k3s es un entorno ligero para Kubernetes que puede ser usado en entornos productivos, por ejemplo con tus propias instancias de computo (VPS), y actualmente viene incluido en Rancher Desktop. También puede ser usado desde un contenedor Docker con k3d, incluso se puede gestionar con Dokku.

Entre sus casos de uso es ideal para servidores domésticos, laboratorios para desarrollo de software y aplicaciones, IoT, así como para moderar costos en Kubernetes (aunque la gestión recae en ti, tu equipo o empresa). De hecho, puedes usar la capa gratuita de AWS con una instancia de computo EC2 dado que solo requiere 512 MB de memoria (ocupa en principio menos de 100 MB).

Veamos a continuación una introdución a esta tecnología.

Instalación de k3s

Según el sitio de k3s, para instalarlo en entornos Linux (o WSL - Windows Subsystem for Linux) simplemente se ejecuta:

bash
curl -sfL https://get.k3s.io | sh -

Si tu sistema es macOS o quieres usarlo desde contenedor puedes revisar el proyecto k3d. k3s también se usa con Rancher Desktop.
Si deseas deshabilitar Traefik (el cual viene por defecto) podrías usar mejor: curl -sfL https://get.k3s.io | sh -s - --disable=traefik

Podemos verificar la disponiblidad de nuestro Cluster, ejecutando lo siguiente (por ejemplo en Linux Ubuntu):

bash
sudo su
kubectl get nodes
kubectl get pods -n kube-system

Según se requiera (salvo que el servicio se encuentre activo), se puede iniciar k3s ejecutando: k3s server &

Conviene configurar la variable KUBECONFIG para administrar debidamente el Cluster con nuestro usuario. De hecho, podemos establecer una carpeta de configuración y definir KUBECONFIG para nuestro usuario. Por ejemplo, podríamos usar comandos como los siguientes:

bash
mkdir -p .kube/config
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
export KUBECONFIG=$HOME/.kube/config
curl -sS https://webinstall.dev/k9s | bash

Desinstalación de k3s

En ocasiones en que haces laboratorios, si quisieras restablecer el cluster y desinstalar k3 podrías ejecutr para Linux los siguientes comandos:

bash
sudo /usr/local/bin/k3s-uninstall.sh
sudo rm -rf /etc/rancher/k3s
sudo rm -rf /var/lib/rancher/k3s

Alternativamente, también se podría eliminar los recursos con kubectl delete all --all --all-namespaces y reiniciar el servicio con sudo systemctl restart k3s

Después de esto puedes reinstalar k3s si es el caso (considerando restablecer archivos de KUBECONFG). Para resumir el escenario de reinstalación con un ejemplo, podríamos usar comandos como los siguientes:

bash
sudo /usr/local/bin/k3s-uninstall.sh
sudo rm -rf /etc/rancher/k3s
sudo rm -rf /var/lib/rancher/k3s

curl -sfL https://get.k3s.io | sh -s - --disable=traefik
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
export KUBECONFIG=$HOME/.kube/config
echo $KUBECONFIG
curl -sS https://webinstall.dev/k9s | bash
curl ifconfig.me

Usando k3s con k3d (instalación en Docker)

Para un entorno de desarrollo local, es posible habilitar k3s dentro de contenedores Docker usando k3d. Para ello se instala k3d con el siguiente comando:

bash
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

Alternativamente: wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

Una vez instalado se procede a crear un Cluster con un comando como el siguiente:

bash
k3d cluster create localcluster --port "80:80@loadbalancer"

localcluster correspondería al nombre asignado en el equipo.
--port "80:80@loadbalancer se agrega solo si se requiere configurar puertos.

De este modo, ya podemos usar kubectl, por ejemplo:

bash
kubectl get pods -A

Otro comando de k3d que puede ser últil es el de importar la imágen que se usa en un contenedor, así:

bash
k3d image import youruser/webapp:latest -c localcluster

Nuestro primer Pod en k3s

Para hacer un ejercicio bien ágil, clásico y esencial, podemos usar un servicio como Nginx ejecutando las siguientes sentencias:

bash
kubectl create deployment nginx --image=nginx
kubectl scale --replicas=1 deployment/nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get svc nginx -o jsonpath='{.spec.ports[0].nodePort}'
exit

Usando kubectl se ha logrado crear un depliegue de una sola replica, exponer un servicio y obtener el puerto para consultar en un navegador.

Proyecto de ejemplo para k3s

Plantearemos un clásico y esencial ejemplo con Javascript para crear un Pod de una aplicación en k3s. Veamos el código correspondiente a un servicio que llamaremos server.js, el Dockerfile y el manifiesto del Pod que llamaremos webapp.yaml. Veamos:

  • server.js
js
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hi from k3s!');
});

server.listen(3000, () => {
  console.log('Server listening on port 3000');
});
  • Dockerfile
Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY server.js .
CMD ["node", "server.js"]
EXPOSE 3000
  • webapp.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  containers:
    - name: webapp
      image: docker.io/youruser/webapp:latest
      ports:
        - containerPort: 3000

Se debe reemplazar youruser por el usuario correspondiente (o toda la url de la imagen del contenedor)

Para preparar y disponer de la imágen del contenedor ejecutamos las siguientes sentencias:

bash
docker build -t youruser/webapp .
docker login
docker tag youruser/webapp:latest youruser/webapp:latest
docker push youruser/webapp:latest

En caso de que se use el servicio ECR de AWS, ejecutamos las siguientes sentencias:

bash
docker build -t youruser/webapp .
aws ecr create-repository --repository-name webapp --region us-east-1
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker tag youruser/webapp:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/webapp:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/webapp:latest

Para correr y probar el Pod ejecutamos las siguientes sentencias:

bash
sudo kubectl create deployment webapp --image=webapp
sudo kubectl scale --replicas=1 deployment/webapp
sudo kubectl expose deployment webapp --port=3000 --type=NodePort
sudo kubectl port-forward webapp 3000:3000
sudo kubectl get svc webapp -o jsonpath='{.spec.ports[0].nodePort}'
curl http://localhost:3000
curl ifconfig.me
exit

Ahora podemos probar consultando la dirección desde un navegador.

Instalando k3s en una instancia Amazon EC2

Para establecer un ejemplo de uso con instancias Amazon EC2 podemos usar CloudFormation y preparar una personalización de usuario con el atributo UserData. Veamos la plantilla de ejemplo:

yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: An EC2 sample template for Free Tier usage

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
    MinLength: 1

Resources:
  LabEC2Instance:
    Type: "AWS::EC2::Instance"
    Properties: 
      ImageId: "ami-00a929b66ed6e0de6"
      InstanceType: t2.micro
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref LabEC2SecurityGroup
      BlockDeviceMappings:
        -
          DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp2
            VolumeSize: 15
            DeleteOnTermination: true
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          # Make a swap file with 2GB
          dd if=/dev/zero of=/swapfile bs=2M count=1024
          chmod 600 /swapfile
          mkswap /swapfile
          swapon /swapfile
          echo "/swapfile swap swap defaults 0 0" >> /etc/fstab

          # Install k3s (Lightweight Kubernetes)
          curl -sfL https://get.k3s.io | sh -

          # Enable access to the cluster for user
          mkdir -p /home/ec2-user/.kube
          cp /etc/rancher/k3s/k3s.yaml /home/ec2-user/.kube/config
          chown -R ec2-user:ec2-user /home/ec2-user/.kube
          echo "export KUBECONFIG=/home/ec2-user/.kube/config" >> /home/ec2-user/.bashrc
          echo "alias k='kubectl'" >> /home/ec2-user/.bashrc

          # Install Helm Chart
          curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

          # Setup start up service for k3s
          systemctl enable k3s
          systemctl start k3s
          sudo dnf update
          sudo dnf install -y git
          sudo dnf install -y postgresql17

  LabEC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0  # My IP
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 30000
          ToPort: 32767
          CidrIp: 0.0.0.0/0  # NodePort range

Outputs:
  EC2InstanceId:
    Description: ID of the created EC2 instance
    Value: !Ref LabEC2Instance
    Export:
      Name: EC2InstanceId

En esencia, se usa una instancia de capa gratuita con sistema Amazon Linux 2, y con UserData se agrega memoria de intercambio y k3s como servicio de Kubernetes.
Podemos probar la plantilla de ejemplo desde la consola web de AWS.

Experimentando Kubernetes con k3s y servicios de ejemplo

Al usar k3s tenemos disponible Traefik para el Ingress (que actúa también como un proxy) y en caso de pensar en un API Gateway como Apache APISIX, quizás sea mejor deshabilitar Traefik, instalar y configurar Apache APISIX (salvo por la gestión de certificados).

Script para instalar k3s y Apache APISIX reemplazando Traefik

En ese escenario planteamos para UserData un cambio consistente en modificar la línea de instalación de k3s así:

yaml
          curl -sfL https://get.k3s.io | sh -s - --disable=traefik

Lo que quiere decir esto es que se agrega en la línea de instalación de k3s es parámetro - --disable=traefik para deshabilitar Traefik.

Para instalar Apache APISIX e iniciar nuestra configuración podemos usar las siguientes sentencias:

bash
export APISIX_NS="apilab"
export MY_HOST="ec2-111-222-333-444.compute-1.amazonaws.com"
export MY_EMAIL="[email protected]"
mkdir k8s

helm repo add apisix https://charts.apiseven.com
helm repo update
kubectl create namespace $APISIX_NS

cat <<EOF > k8s/values-apisix.yaml
gateway:
  type: NodePort
  http:
    enabled: true
    port: 9080
  tls:
    enabled: true
    port: 9443
etcd:
  replicaCount: 1
  resources:
    limits:
      cpu: "100m"
      memory: "128Mi"
    requests:
      cpu: "50m"
      memory: "64Mi"
ingress-controller:
  enabled: true
  config:
    apisix:
      serviceNamespace: $APISIX_NS
plugins:
  - limit-req
  - prometheus
dashboard:
  enabled: true
  service:
    type: ClusterIP
    port: 9000
EOF

helm install apisix apisix/apisix -n $APISIX_NS -f k8s/values-apisix.yaml

Con esto obtendremos un manifiesto para configurar Apache APISIX como controlador Ingress de tipo NodePort, entendiendo que será el punto para gestionar API's, aunque puede usarse LoadBalancer si se aplica un balanceador.

En caso de usar certificados auto-firmados usamos un secreto para el certificado. Para ello podemos ejecutar los siguientes comandos:

bash
openssl req -x509 -newkey rsa:2048 -nodes -keyout k8s/tls.key -out k8s/tls.crt -days 365 -subj "/CN=example.com"
kubectl create secret tls apisix-tls-secret --cert=k8s/tls.crt --key=k8s/tls.key -n $APISIX_NS

cat <<EOF > k8s/tls.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
  name: apisix-tls
  namespace: $APISIX_NS
spec:
  hosts:
  - "$MY_HOST"
  secret:
    name: apisix-tls-secret
    namespace: $APISIX_NS
EOF

kubectl apply -f k8s/tls.yaml

A continuación usaremos una aplicación disponible en Internet (httpbin) que nos servirá para probar una ruta:

bash
kubectl run httpbin --image=kennethreitz/httpbin --port=80 -n $APISIX_NS
kubectl expose pod httpbin --port=80 -n $APISIX_NS

cat <<EOF > k8s/route-apisix.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: httpbin-route
  namespace: $APISIX_NS
spec:
  http:
  - name: httpbin-route
    match:
      paths:
      - "/api/*"
    backends:
    - serviceName: httpbin
      servicePort: 80
    plugins:
    - name: proxy-rewrite
      config:
        regex_uri: ["^/api/(.*)", "/$1"]
EOF

kubectl apply -f k8s/route-apisix.yaml

Este bloque usa la clase ApisixRoute para definir una ruta inicial exponiendo el servicio de ejemplo en el puerto 80.
Adicionalmente, en los plugins podemos redirigir el servicio para usar https

Podemos ver el puerto del servicio de Apache APISIX ejecutando lo siguiente:

bash
kubectl get svc apisix-gateway -n $APISIX_NS

Ahora, podemos probar usando alguna otra aplicación disponible, por ejemplo, para n8n podríamos ejecutar un comando como el siguiente:

bash
helm install n8n oci://8gears.container-registry.com/library/n8n --version 1.0.0 -n $APISIX_NS

Script para instalar k3s con Traefik como Ingress

Aunque k3s viene con Traefik, podríamos configurarlo de forma personalizada y definir nuestras rutas para el Ingress. En este ejercicio usaramos como ejemplo n8n para validar un servicio. Veamos el "script":

bash
export APILAB_NS="apilab"
mkdir k8s
cd k8s
alias k="kubectl"

curl -sfL https://get.k3s.io | sh -s - --disable=traefik
k create namespace $APILAB_NS
helm repo add traefik https://traefik.github.io/charts
helm repo update

cat <<EOF > values-traefik.yaml
image:
  tag: "3.3.5"
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
ports:
  web:
    port: 80
    exposedPort: 80
    protocol: TCP
  websecure:
    port: 443
    exposedPort: 443
    protocol: TCP
service:
  type: LoadBalancer
  spec:
    externalTrafficPolicy: Local
logs:
  general:
    level: DEBUG
EOF

cat <<EOF > n8n-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: n8n-ingress
  namespace: $APILAB_NS
  annotations:
    kubernetes.io/ingress.ingressClassName: traefik
    traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
  rules:
  - host: ec2-35-153-159-68.compute-1.amazonaws.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: n8n-svc
            port:
              number: 80
EOF

cat <<EOF > n8n-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
  namespace: $APILAB_NS
spec:
  replicas: 1
  selector:
    matchLabels:
      app: n8n
  template:
    metadata:
      labels:
        app: n8n
    spec:
      containers:
      - name: n8n
        image: n8nio/n8n:latest
        ports:
        - containerPort: 5678
        env:
        - name: N8N_HOST
          value: "0.0.0.0"
        - name: N8N_PORT
          value: "5678"
        - name: N8N_PROTOCOL
          value: "http"
        - name: N8N_SECURE_COOKIE
          value: "false"
        - name: N8N_RUNNERS_ENABLED
          value: "true"
EOF

cat <<EOF > n8n-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: n8n-svc
  namespace: $APILAB_NS
spec:
  selector:
    app: n8n
  ports:
    - port: 80
      targetPort: 5678
  type: ClusterIP
EOF

helm install traefik traefik/traefik -n $APISIX_NS -f values-traefik.yaml
k apply -f n8n-ingress.yaml
k apply -f n8n-pod.yaml
k apply -f n8n-svc.yaml

k get svc -n $APISIX_NS traefik
k logs -n $APISIX_NS -l app.kubernetes.io/name=traefik

Si en el archivo n8n-pod.yaml especificamos un host como n8n.local (en lugar de 0.0.0.0), debemos editar el archivo /etc/hosts y agregar la IP externa de Kubernetes con el nombre n8n.local

Script para instalar k3s con Traefik (Ingress) con certificados SSL

Ahora, ajustemos algunas cosas del ejercicio anterior para agregar certicados SSL. Veamos el "script" para ello:

bash
export APILAB_NS="apilab"
export MY_EMAIL="[email protected]"
mkdir k8s
cd k8s
alias k="kubectl"

curl -sfL https://get.k3s.io | sh -s - --disable=traefik
k create namespace $APILAB_NS
helm repo add traefik https://traefik.github.io/charts
helm repo update

cat <<EOF > values-traefik.yaml
image:
  tag: "3.3.5"
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
ports:
  web:
    port: 80
    exposedPort: 80
    protocol: TCP
  websecure:
    port: 443
    exposedPort: 443
    protocol: TCP
service:
  type: LoadBalancer
  spec:
    externalTrafficPolicy: Local
providers:
  kubernetesIngress:
    enabled: true
    namespaces:
    - kube-system
    - $APILAB_NS
additionalArguments:
  - "--certificatesresolvers.letsencrypt.acme.email=$MY_EMAIL"
  - "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
persistence:
  enabled: true
  path: /data
  size: 128Mi
logs:
  general:
    level: DEBUG
EOF

cat <<EOF > n8n-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
  namespace: $APILAB_NS
spec:
  replicas: 1
  selector:
    matchLabels:
      app: n8n
  template:
    metadata:
      labels:
        app: n8n
    spec:
      containers:
      - name: n8n
        image: n8nio/n8n:latest
        ports:
        - containerPort: 5678
        env:
        - name: N8N_HOST
          value: "0.0.0.0"
        - name: N8N_PORT
          value: "5678"
        - name: N8N_PROTOCOL
          value: "https"
        - name: N8N_RUNNERS_ENABLED
          value: "true"
---
apiVersion: v1
kind: Service
metadata:
  name: n8n-svc
  namespace: $APILAB_NS
spec:
  selector:
    app: n8n
  ports:
    - port: 5678
      targetPort: 5678
  type: ClusterIP
EOF

cat <<EOF > n8n-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: n8n-ingress
  namespace: $APILAB_NS
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
    traefik.ingress.kubernetes.io/router.tls.domains.0.main: "localhost"
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: n8n-svc
            port:
              number: 5678
  tls:
  - hosts:
    - "localhost"
EOF

helm install traefik traefik/traefik -n kube-system -f values-traefik.yaml
k apply -f n8n-app.yaml
k apply -f n8n-ingress.yaml

Vericamos el estado de esta configuración con los siguientes comandos:

bash
k get pods -n kube-system -l app.kubernetes.io/name=traefik
k get svc -n kube-system traefik
k logs -n kube-system -l app.kubernetes.io/name=traefik
k describe ingress n8n-ingress -n $APILAB_NS

Script para instalar k3s usando Gateway API con Traefik

Si en lugar de Ingress usaramos Gateway API, tendríamos un "script" como el siguiente:

bash
export APILAB_NS="apilab"
mkdir k8s
cd k8s
alias k="kubectl"

curl -sfL https://get.k3s.io | sh -s - --disable=traefik
k get crds | grep gateway.networking.k8s.io
k create namespace $APILAB_NS
helm repo add traefik https://traefik.github.io/charts
helm repo update

cat <<EOF > values-traefik.yaml
image:
  tag: "3.3.5"
providers:
  kubernetesGateway:
    enabled: true
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
ports:
  web:
    port: 8000
    exposedPort: 80
    protocol: TCP
  websecure:
    port: 8443
    exposedPort: 443
    protocol: TCP
service:
  type: LoadBalancer
  spec:
    externalTrafficPolicy: Local
logs:
  general:
    level: DEBUG
EOF

cat <<EOF > n8n-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
  namespace: $APISIX_NS
spec:
  replicas: 1
  selector:
    matchLabels:
      app: n8n
  template:
    metadata:
      labels:
        app: n8n
    spec:
      containers:
      - name: n8n
        image: n8nio/n8n:latest
        ports:
        - containerPort: 5678
        env:
        - name: N8N_HOST
          value: "0.0.0.0"
        - name: N8N_PORT
          value: "5678"
        - name: N8N_PROTOCOL
          value: "http"
        - name: N8N_SECURE_COOKIE
          value: "false"
EOF

cat <<EOF > n8n-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: n8n-svc
  namespace: $APISIX_NS
spec:
  selector:
    app: n8n
  ports:
  - protocol: TCP
    port: 5678
    targetPort: 5678
  type: ClusterIP
EOF

cat <<EOF > gatewayclass-traefik.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: traefik
  namespace: $APISIX_NS
spec:
  controllerName: traefik.io/gateway-controller
EOF

cat <<EOF > gateway-traefik.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public-gateway
  namespace: $APISIX_NS
spec:
  gatewayClassName: traefik
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      kinds:
      - kind: HTTPRoute
EOF

cat <<EOF > httproute-n8n.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: n8n-route
  namespace: $APISIX_NS
spec:
  parentRefs:
    - name: public-gateway
      namespace: $APISIX_NS
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: n8n-svc
          port: 5678
EOF

helm install traefik traefik/traefik -n $APISIX_NS -f values-traefik.yaml
k apply -f n8n-pod.yaml
k apply -f n8n-svc.yaml
k apply -f gatewayclass-traefik.yaml
k apply -f gateway-traefik.yaml
k apply -f httproute-n8n.yaml

k describe gatewayclass traefik
k describe gateway public-gateway -n $APISIX_NS
k logs -n $APISIX_NS -l app.kubernetes.io/name=traefik
k get svc -n $APISIX_NS traefik

Si en lugar de los archivos n8n-pod.yaml y n8n-svc.yaml se usa Helm, es posible instalar n8n con un comando como el siguiente:

bash
helm install n8n oci://8gears.container-registry.com/library/n8n \
  --namespace $APISIX_NS \
  --set persistence.enabled=false \
  --set n8n.basicAuth.user=admin \
  --set n8n.basicAuth.password=admin \
  --set n8n.protocol=http \
  --set n8n.path="" \
  --set n8n.port=5678

Script para instalar k3s y Apache APISIX como Ingress

Para seguir ilustrando ejemplos, en este caso presentaremos un "script" para usar APISIX como Ingress y un servicio como n8n.

bash
export APISIX_NS="apilab"
export MY_HOST="ec2-111-222-333-444.compute-1.amazonaws.com"
export MY_EMAIL="[email protected]"
mkdir k8s
cd k8s
curl -sfL https://get.k3s.io | sh -s - --disable=traefik
helm repo add apisix https://charts.apiseven.com
helm repo update

Antes de instalar APISIX y n8N configuraremos los siguientes archivos dentro del folder k8s...

  • values-apisix.yaml
yaml
image:
  tag: "3.12.0"
gateway:
  type: NodePort
  http:
    enabled: true
    port: 80
    nodePort: 30800
  externalTrafficPolicy: Local
ingress-controller:
  enabled: true
  image:
    tag: "1.8.4"
  config:
    kubernetes:
      enableGatewayAPI: false
      watchNamespace: apilab
    apisix:
      adminKey: "edd1c9f034335f136f87ad84b625c8f1"
      serviceName: apisix-gateway
      serviceNamespace: apilab
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "512Mi"
etcd:
  enabled: true
  replicaCount: 1
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "512Mi"
plugins:
  - prometheus
  - limit-req
logs:
  default:
    level: debug
  • n8n-ingress.yaml
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: n8n-ingress
  namespace: apilab
  annotations:
    kubernetes.io/ingress.ingressClassName: apisix
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: n8n-svc
            port:
              number: 5678
  • n8n-app.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
  namespace: apilab
spec:
  replicas: 1
  selector:
    matchLabels:
      app: n8n
  template:
    metadata:
      labels:
        app: n8n
    spec:
      containers:
      - name: n8n
        image: n8nio/n8n:latest
        ports:
        - containerPort: 5678
        env:
        - name: N8N_HOST
          value: "0.0.0.0"
        - name: N8N_PORT
          value: "5678"
        - name: N8N_PROTOCOL
          value: "http"
        - name: N8N_SECURE_COOKIE
          value: "false"
---
apiVersion: v1
kind: Service
metadata:
  name: n8n-svc
  namespace: apilab
spec:
  selector:
    app: n8n
  ports:
    - port: 80
      targetPort: 5678
      protocol: TCP
  type: ClusterIP

Finalmente aplicamos los anteriores manifiestos así:

bash
helm install apisix apisix/apisix -n $APISIX_NS --create-namespace -f values-apisix.yaml
kubectl apply -f n8n-ingress.yaml
kubectl apply -f n8n-app.yaml

Script para instalar k3s y Apache APISIX usando Gateway API

Continuando con otro ejemplo, prepararemos un "script" para usar APISIX con Gateway API y un servicio como n8n.

bash
export APISIX_NS="apilab"
export MY_HOST="ec2-111-222-333-444.compute-1.amazonaws.com"
export MY_EMAIL="[email protected]"
mkdir k8s
cd k8s
curl -sfL https://get.k3s.io | sh -s - --disable=traefik
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml

helm repo add apisix https://charts.apiseven.com
helm repo update

helm install apisix apisix/apisix \
  --namespace $APISIX_NS \
  --create-namespace \
  --set gateway.type=NodePort \
  --set gateway.http.nodePort=30800 \
  --set etcd.replicaCount=1 \
  --set ingressController.enabled=true \
  --set ingressController.image.tag="1.8.4" \
  --set ingressController.config.apisix.serviceNamespace=$APISIX_NS \
  --set ingressController.config.apisix.serviceName=apisix-gateway

helm install apisix-dashboard apisix/apisix-dashboard \
  --namespace apisix \
  --set service.type=NodePort \
  --set service.nodePort=30007

helm install n8n oci://8gears.container-registry.com/library/n8n \
  --namespace $APISIX_NS \
  --set persistence.enabled=false \
  --set n8n.basicAuth.user=admin \
  --set n8n.basicAuth.password=admin \
  --set n8n.protocol=http \
  --set n8n.path="" \
  --set n8n.port=5678

He quí otra manera de instalar APISIX para usar Gateway API:

bash
export APISIX_NS="apilab"
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
kubectl get crds | grep gateway.networking.k8s.io

helm install apisix apisix/apisix \
  --namespace $APISIX_NS \
  --create-namespace \
  --set gateway.type=NodePort \
  --set gateway.http.nodePort=30800 \
  --set etcd.replicaCount=1 \
  --set ingressController.enabled=true \
  --set ingressController.image.tag="1.8.4" \
  --set ingressController.config.kubernetes.enableGatewayAPI=true \
  --set ingressController.config.apisix.serviceNamespace=$APISIX_NS \
  --set ingressController.config.apisix.serviceName=apisix-gateway

Luego de instalar APISIX y n8N configuraríamos los siguientes archivos...

  • gatewayclass-apisix.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: apisix
spec:
  controllerName: apisix.apache.org/v1
  • gateway-apisix.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public-gateway
  namespace: apilab
spec:
  gatewayClassName: apisix
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: All
  • httproute-n8n.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: n8n-route
  namespace: apilab
spec:
  parentRefs:
    - name: public-gateway
      namespace: apilab
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: n8n-svc
          port: 5678

Finalmente aplicamos los anteriores manifiestos así:

bash
helm install apisix apisix/apisix -n $APISIX_NS -f values-apisix.yaml
kubectl apply -f gatewayclass-apisix.yaml
kubectl apply -f gateway-apisix.yaml
kubectl apply -f httproute-n8n.yaml
kubectl apply -f n8n-app.yaml

Adicionalmente, podemos usar algunas de las siguientes sentencias para verficación del estado de la configuración si algo fallara:

bash
helm get values apisix -n $APISIX_NS
kubectl describe gatewayclass apisix
kubectl describe gateway public-gateway -n $APISIX_NS
kubectl describe httproute n8n-route -n $APISIX_NS
kubectl get svc -n $APISIX_NS
kubectl get svc -n $APISIX_NS apisix-gateway
kubectl logs -n $APISIX_NS -l app.kubernetes.io/name=apisix
kubectl get pods -n $APISIX_NS -l app.kubernetes.io/component=ingress-controller
kubectl logs -n $APISIX_NS -l app.kubernetes.io/component=ingress-controller | grep -i gateway
kubectl logs -n $APISIX_NS -l app=n8n
kubectl logs -n kube-system -l app.kubernetes.io/name=k3s
kubectl port-forward svc/n8n-service -n $APISIX_NS 5678:5678