====== Clúster con 3 raspberry ====== https://www.raspberrypi.org/blog/docker-comes-to-raspberry-pi/ Instalamos Raspbian. Tarda unos 5 minutos https://www.raspberrypi.org/downloads/raspbian/ xz --decompress 2023-05-03-raspios-bullseye-arm64-lite.img.xz dd if=2023-05-03-raspios-bullseye-arm64-lite.img of=/dev/mmcblk0 Esto nos crea dos particiones en la tarjeta: bootfs rootfs Modificamos la tarjeta para poder acceder sin monitor: En la partición rootfs, ponemos IP fija cambiando el fichero añadiendo al final: rootfs/etc/dhcpcd.conf interface eth0 static ip_address=192.168.69.1/24 static routers=192.168.69.1 static domain_name_servers=192.168.69.1 Habilitamos ssh dejando un fichero en la partición boot que se llame ssh (da igual el contenido o si está vacio) touch bootfs/ssh A partir de la versión bulleyes tenemos que crear otro usuario para poder acceder: Crear el fichero en boot llamado userconf con el contenido: vim bootfs/userconf username:encrypted_password Por ejemplo, para ruth:odin sacamos la password: echo 'odin' | openssl passwd -6 -stdin $6$S3pAIx36rcMzDYsK$vzl8eX.2k07Rbje9nJ4zsFQdieKw8Wg296javxQ.VW7SdknBlk03vFKh0eI8i4VGwPxWHiJCJNnCd7E72Sh8c0 Y sería: echo 'ruth:$6$S3pAIx36rcMzDYsK$vzl8eX.2k07Rbje9nJ4zsFQdieKw8Wg296javxQ.VW7SdknBlk03vFKh0eI8i4VGwPxWHiJCJNnCd7E72Sh8c0' > bootfs/userconf Modificamos la memoria SWAP que está a 100 y la ponemos a 1024: rootfs/etc/dphys-swapfile CONF_SWAPSIZE=1024 Deshabilitamos IPV6 https://sleeplessbeastie.eu/2022/07/20/how-to-disable-ipv6-on-raspberry-pi-4/ ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 172.16.1.101/16 fe80::14b8:2700:4354:ec24/64 wlan0 DOWN echo net.ipv6.conf.all.disable_ipv6=1 | sudo tee /etc/sysctl.d/disable-ipv6.conf Grabamos cambios: sudo sysctl --system Esto para gurb pero no me convence Añadimos en la línea del fichero /boot/cmdline.txt Que tiene que ser algo así: dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=ee25660b-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh El siguiente parámetro ipv6.disable=1 Ya podemos acceder por ssh\\ Nos conectamos y seguimos modificando Con raspi-config, modificamos: -Hostname 2. Network Options N1 Hostname -Expandimos FileSystem (ya viene hecho): 7 Advanced Options A1 Expand Filesystem -Cambiamos arranque a consola (no hace falta con lite) 3 Boot Options B1 Desktop / CLI B1 Console -Cambiadmos pais Wifi: 4 Localisation Options I4 Change Wi-fi Country ES Spain ====== Wifi ====== https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md El primer nodo se conectará a una red wifi y dará internet al resto. Escanemos las redes: iwlist wlan0 scan Añadimos la red y la contraseña al final del fichero /etc/wpa_supplicant/wpa_supplicant.conf p2p_disabled=1 network={ ssid="mi_red_wifi" psk="***************" } Probamos de conectarnos a mano: wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf Reiniciamos el servicio: wpa_cli -i wlan0 reconfigure Podemos poner mas de una red en el fichero wpa_supplicant.conf y las seleccionamos con esta herramienta interactiva: wpa_cli list_networks select_network 1 Para el primer nodo le tenemos que quitar el default gw de la red 192.168.69.x. Editamos el fichero: /etc/dhcpcd.conf Y quitamos la línea: static routers=192.168.69.1 Para compartir internet: \\ **wlan0:** red con internet \\ **eth0:** red interna por cable eth0 entre raspberris 192.168.69.1, 192.168.69.2 y 192.168.69.3 Permitimos reenvio entre interfaces editando /etc/sysctl.conf: net.ipv4.ip_forward = 1 Lo añadimos en caliente sysctl -p Añadimos reglas iptables iptables -A FORWARD -o wlan0 -i eth0 -m conntrack --ctstate NEW -j ACCEPT iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE Lo hacemos persistente Hay dos formas, guardar las reglas de iptables, no me convence porque puede haber mas y no se si se sobreescriben: iptables-save > /etc/iptables.ipv4.nat Añadimos antes del exit 0 en /etc/rc.local iptables-restore < /etc/iptables.ipv4.nat Debe quedar mas o menos así: _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi iptables-restore < /etc/iptables.ipv4.nat exit 0 La segunda es poner los 3 comandos de iptables en /etc/rc.local antes del exit 0 Instalamos dnsmasq para que funcione el DNS: apt-get install dnsmasq Para poner una entrada, las añadimos en el fichero /etc/dnsmasq.conf Al final ponemos: address=/jenkins.rpicluster.com/192.168.69.2 Cada vez que añadamos una tenemos que reinicar el servicio: systemctl restart dnsmasq ====== Punto de acceso ====== https://www.raspberrypi.org/documentation/configuration/wireless/access-point.md El tercer nodo hará de Punto de acceso para que otros portátiles se conecten al clúster apt-get install dnsmasq hostapd systemctl stop dnsmasq sudo systemctl stop hostapd /etc/dhcpcd.conf interface wlan0 static ip_address=192.168.4.1/24 service dhcpcd restart mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig /etc/dnsmasq.conf interface=wlan0 dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h /etc/hostapd/hostapd.conf interface=wlan0 driver=nl80211 ssid=raspicluster hw_mode=g channel=7 wmm_enabled=0 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=2 wpa_passphrase=clusterraspi wpa_key_mgmt=WPA-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP /etc/default/hostapd DAEMON_CONF="/etc/hostapd/hostapd.conf" Reiniciamos servicios: systemctl start hostapd systemctl start dnsmasq Si da un error: Failed to start hostapd.service: Unit hostapd.service is masked. Hacemos esto: sudo systemctl unmask hostapd sudo systemctl enable hostapd sudo systemctl start hostapd Al arrancar el servidor, arranca el servicio de hostapd pero no el AP. Pero si reiniciamos el servicio después de que arranque el servidor si que arranca el AP. Por lo tanto vamos a asegurarnos que el servicio hostapd arranca el útimo: Modificamos la seccion [Unit] del fichero /usr/lib/systemd/system/hostapd.service After=network.target network-online.target Wants=network-online.target Tiene que quedar algo así: [Unit] Description=Access point and authentication server for Wi-Fi and Ethernet Documentation=man:hostapd(8) After=network.target network-online.target Wants=network-online.target Ahora hacemos que enruten las dos redes: /etc/sysctl.conf net.ipv4.ip_forward=1 Para que aplique hay que reiniciar o lanzar: sysctl -p También añadir iptables. Para hacerlo persistente lo añadimos justo antes del exit 0 en /etc/rc.local iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE Vemos si funciona: # iwconfig wlan0 IEEE 802.11 Mode:Master Tx-Power=31 dBm Retry short limit:7 RTS thr:off Fragment thr:off Power Management:on Podemos grabar logs añadiendo en: /etc/default/hostapd DAEMON_OPTS="-dd -t -f /var/log/hostapd.log" `-dd` -> "more logging" `-t` -> include timestamps `-f ` -> tells hostapd to log your data to `` ====== DOCKER ====== Al arrancar los docker, usar: -e TZ=Europe/Madrid Instalamos Docker curl -sSL https://get.docker.com | sh Añadimos el usuario pi al grupo docker usermod -aG docker ruth Para que los docker resuelvan bien dnsmasq y que conecte con docker registry hay que añadir: /etc/docker/daemon.json { "dns": [ "192.168.69.1", "8.8.8.8", "8.8.4.4" ], "insecure-registries": ["docker.raspi"] } sudo service docker restart ===== Docker Registry ===== Ponemos el puerto 80 porque el registry lo haremos inseguro para no tener que crear certificados: docker pull registry:2 docker run -d -p 80:5000 --restart=always --name registry registry:2 Creamos una imagen, fichero Dockerfile, por ejemplo una simple con git: FROM debian RUN apt-get update && \ apt-get install -y git CMD bash Creamos la imagen: docker build -t docker.raspi/git . La subimos a nuestro Registry: docker push docker.raspi/git Ahora si vamos a otro nodo, la podemos descargar: docker run -ti docker.raspi/git ===== Docker Swarm (deprecated) ===== docker run -ti resin/rpi-raspbian:latest cat /etc/os-release PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)" docker run -ti arm32v6/alpine:3.5 Apache: docker run -ti -p 8080:80 hypriot/rpi-busybox-httpd Si accedemos a 192.168.1.201:8080 nos sale web de apache Ponemos las siguientes IPs: raspiswarm1: 192.168.1.201 raspiswarm2: 192.168.1.202 raspiswarm3: 192.168.1.203 Iniciamos swarm en el nodo1: docker swarm init Para añadir otros nodos nos indica: docker swarm join \ --token SWMTKN-1-0kn5tua6jptohuvohoh5v46vb75qscdz7b35hjecynl94xne40-520qqlthfm2bhukhqpj352sxi \ 192.168.1.201:2377 Los añadimos y nos dice: This node joined a swarm as a worker. Ejecutamos un servicio: docker service create --name apache --replicas=2 -p 8080:80 hypriot/rpi-busybox-httpd Si hacemos un docker ps vemos que lo ha levantado en la 1 y la 2, pero es accesible desde las 3 ips y va balanceando. Para parar el servicio: docker service rm apache Podemos crearlo mapeando una unidad local para ver que balancea, por ejemplo: docker service create --name apache --replicas=3 -p 8080:80 --mount type=bind,src=/raspi/www,dst=/www hypriot/rpi-busybox-httpd Si creamos un /raspi/www/index.html diferente para cada servidor, veremos como balancea ===== kubernetes ===== https://kubecloud.io/setup-a-kubernetes-1-9-0-raspberry-pi-cluster-on-raspbian-using-kubeadm-f8b3b85bc2d1 Tenemos que deshabilitar swap dphys-swapfile swapoff dphys-swapfile uninstall update-rc.d dphys-swapfile remove Modificamos: /boot/cmdline.txt cgroup_enable=memory Instalamos Docker: curl -sSL get.docker.com | sh && sudo usermod pi -aG docker curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - && \ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list && \ sudo apt-get update -q && \ sudo apt-get install -qy kubeadm Inicializamos el clúster. Ponemos el rango de IPs por si tenemos varios, en este caso wlan0 que sale a internet y eth0 que es la red local: kubeadm init --apiserver-advertise-address=192.168.69.1 Cuando acaba da lo siguiente: Your Kubernetes master has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ You can now join any number of machines by running the following on each node as root: kubeadm join 192.168.69.1:6443 --token 08607b.fqzo9x9lh7fhp40r --discovery-token-ca-cert-hash sha256:3df520e18e42608c133b01af692b8881f6834319751e461c5deb8534a2055466 Ejecutamos esos comandos con el usuario pi. Miramos el estado de los nodos: kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes1 NotReady master 8m v1.10.2 Pone not ready porque falta configurar la red. El pod de dns se queda pendiente kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system etcd-kubernetes1 1/1 Running 0 2m kube-system kube-apiserver-kubernetes1 1/1 Running 0 2m kube-system kube-controller-manager-kubernetes1 1/1 Running 0 1m kube-system kube-dns-686d6fb9c-dwtmh 0/3 Pending 0 2m kube-system kube-proxy-j9tmc 1/1 Running 0 2m kube-system kube-scheduler-kubernetes1 1/1 Running 0 2m Lo podemos ver en los logs: sudo journalctl -r -u kubelet May 02 23:46:39 kubernetes1 kubelet[16196]: E0502 23:46:39.401514 16196 kubelet.go:2125] Container runtime network not ready: NetworkReady=false rea May 02 23:46:39 kubernetes1 kubelet[16196]: W0502 23:46:39.401016 16196 cni.go:171] Unable to update cni config: No networks found in /etc/cni/net.d Creamos la RED: kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" serviceaccount "weave-net" created clusterrole.rbac.authorization.k8s.io "weave-net" created clusterrolebinding.rbac.authorization.k8s.io "weave-net" created role.rbac.authorization.k8s.io "weave-net" created rolebinding.rbac.authorization.k8s.io "weave-net" created daemonset.extensions "weave-net" created Ahora vemos que ya están todos arrancados kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system etcd-kubernetes1 1/1 Running 0 6m kube-system kube-apiserver-kubernetes1 1/1 Running 0 6m kube-system kube-controller-manager-kubernetes1 1/1 Running 0 5m kube-system kube-dns-686d6fb9c-dwtmh 3/3 Running 0 6m kube-system kube-proxy-j9tmc 1/1 Running 0 6m kube-system kube-scheduler-kubernetes1 1/1 Running 0 6m kube-system weave-net-59r4p 2/2 Running 0 1m Y ya aparece bien en el listado de nodos: kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes1 Ready master 8m v1.10.2 Añadimos un nodo al cluster: kubeadm join 192.168.69.1:6443 --token 08607b.fqzo9x9lh7fhp40r --discovery-token-ca-cert-hash sha256:3df520e18e42608c133b01af692b8881f6834319751e461c5deb8534a2055466 This node has joined the cluster: * Certificate signing request was sent to master and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the master to see this node join the cluster. Desde el master miramos los nodos: pi@kubernetes1:~ $ kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes1 Ready master 45m v1.10.2 kubernetes2 NotReady 3m v1.10.2 Aparece **NotReady** porque hay que comentar la red cni en los otros nodos. Comentamos la línea que pone **KUBELET_NETWORK_ARGS** /etc/systemd/system/kubelet.service.d/10-kubeadm.conf #Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin" ===== Dashboard ===== Instalamos Dashboard: echo -n 'apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard labels: k8s-app: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kube-system' | kubectl apply -f - Se crea el servicio: clusterrolebinding.rbac.authorization.k8s.io "kubernetes-dashboard" created No he probado: $ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/alternative/kubernetes-dashboard-arm.yaml Hay que probar este: pi@kubernetes1:~ $ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/alternative/kubernetes-dashboard-arm.yaml serviceaccount "kubernetes-dashboard" created role.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created deployment.apps "kubernetes-dashboard" created service "kubernetes-dashboard" created Para ver donde se está ejecutando: # kubectl -n kube-system get service kubernetes-dashboard NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes-dashboard ClusterIP 10.103.58.176 80/TCP 13m Con el proxy debería funcionar pero no lo he conseguido: kubectl proxy Yo he hecho un tunel y abierto en local: ssh -L 8001:10.103.58.176:80 pi@192.168.69.1 Abrimos http://127.0.0.1:8001 Podemos acceder directamente al nodo, pero hay que habilitar la autenticación\\ Documentación: https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#deploying-the-dashboard-ui https://192.168.69.1:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/ ====== DESKTOP ====== Descargar la imagen con Desktop:\\ https://www.raspberrypi.com/software/operating-systems/ Raspberry Pi OS (64-bit) > Raspberry Pi OS with desktop O instalamos las X sudo apt-get install lightdm En raspi-config seleccionamos la opción 1. System Options S5 Boot / Auto Login B4 Desktop Autologin Desktop GUI, automatically logged in as 'ruth' user Para poder conectar con las X por vnc configuramos desde raspi-config: 3 Interface Options I3 VNC Enable/disable graphical remote access using RealVNC Creo que es lo mismo que instalar: realvnc-vnc-server Para no poner contraseña: /root/.vnc/config.d/vncserver-x11 Authentication=None Encryption=AlwaysOff Si queremos contraseña ojo que tiene que ser de 6 a 8 carácteres con: sudo vncpasswd -service -legacy Para reiniciar el servicio systemctl restart vncserver-x11-serviced.service ===== Arranque de una aplicación automáticamente ===== Nos aseguramos que tengamos instalado chromium-browser ~/.config/lxsession/LXDE/autostart @chromium-browser --start-fullscreen --app=http://web.raspi ===== Como cambiar de ventanas ===== Instalamos las aplicaciones necesarias apt-get install xdotool wmctrl Exportamos DISPLAY para conectarnos a la pantalla: export DISPLAY=:0 Para listar las aplicaciones abiertas: wmctrl -l 0x01200004 0 raspberrypi VNC Server 0x01600002 0 raspberrypi Sign in [Jenkins] 0x0200000b 0 raspberrypi EmulationStation Seleccionamos la que queremos llevar al frente wmctrl -i -a 0x01200004 Ahora la podemos cerrar por ejemplo: xdotool keydown Alt key F4 Vemos que se ha cerrado: wmctrl -l 0x01600002 0 raspberrypi Sign in [Jenkins] 0x0200000b 0 raspberrypi EmulationStation También podríamos cambiar entre aplicaciones simulando ALT+TAB xdotool keydown Alt key Tab keyup Alt Y con xdotool simular cualquier pulsación de teclas ===== Retropie ===== git clone --depth=1 https://github.com/RetroPie/RetroPie-Setup.git cd RetroPie-Setup chmod +x retropie_setup.sh sudo ./retropie_setup.sh Seleccionar "Basic Install" Para cambiar de una aplicación a otra remotamente: sudo apt-get install xdotool Cambiamos desde la conexión ssh al display del monitor: export DISPLAY=:0 Lanzamos el comando ALT+TAB xdotool keydown Alt key Tab keyup Alt ====== Monitorización ====== https://www.bogotobogo.com/DevOps/Docker/Docker_Prometheus_Grafana.php Vienen 2 repositorios, he levantado este docker compose y monitoriza los dockers del nodo donde se ejecuta: https://github.com/stefanprodan/dockprom ====== Botones ====== https://www.raspberrypi.com/documentation/computers/raspberry-pi.html Con el comando pinout muestra un mapa de los pins Instalamos python paquetes necesarios: apt-get install python3-pip pip3 install RPi.GPIO