Monta un nodo bitcoin en 5 minutos

Bueno, cinco minutos es lo que dedicarás a configurar el software, pero luego tendrá que bajarse todo el blockchain y tardará una eternidad y se comerá todo el disco duro que tengamos disponible, pero bueno, vamos a intentar montar un nodo completo bitcoin para testnet (estamos probando) de manera que podamos hacerle consultas sin tener que llamar a otros servicios.

En un próximo post ya os contaré como hacer un explorador completo (con las mismas restricciones de tiempo que este). Por ahora vamos a preparar los materiales. Vamos a necesitar una máquina con ubuntu (o el sistema operativo de vuestra preferencia) y unos 200Gb de disco (para testnet, si queremos la blockchain de mainnet serán como 800Gb). Yo, en mi caso lo he hecho sobre un contenedor ubuntu en proxmox (ya os contaré qué tal con proxmox cuando tenga un rato).

Para crear mi contenedor he usado un script de la comunidad (aqui) pero vosotros podéis usar un ordenador de casa o una máquina virtual que pueda ejecutar docker y docker compose.

Yo la he configurado con 4 CPUs y 4Gb de memoria y me ha servido perfectamente. No os olvidéis de tener los 200Gb disponibles y montados en algún sitio de vuestra máquina. Una vez que tenemos docker y docker compose instalados y funcionando solo tenemos que crear un docker-compose.yml con este contenido:

services:
  bitcoin-testnet:
    image: bitcoin/bitcoin:28.0rc1
    container_name: bitcoin-testnet
    ports:
      - "18443:18443"
      - "18444:18444"
    command:
      - -printtoconsole
      - -testnet=1
      - -rest
      - -rpcbind=0.0.0.0
      - -rpcallowip=0.0.0.0/0
      - -rpcport=18443
      - -rpcuser=miusuario
      - -rpcpassword=mipass
      - -server
      - -txindex
    volumes:
      - /bitcoin-data:/home/bitcoin/.bitcoin
    restart: always

En este caso nos vamos a bajar una imagen preconfigurada de bitcoin (aunque si no os fiáis siempre podéis compilar la vuestra sacando el Dockerfile de github. Abriremos los puertos 18443 y 18444 para poder acceder al nodo desde fuera (recordad, si tenéis nat hacer una redirección del puerto en el router). Además, tenemos nuestro disco con 200Gb en el directorio /bitcoin-data y lo montamos en /home/bitcoin/.bitcoin en la imagen.

Las siguientes opciones son para ver el log en consola, usar testnet, activar el RPC por rest y enlazarnos con todas las IPs de la imagen. Además, configuramos que se usará un usuario miusuario con password mipass, se ejecutará como servidor y vamos a mantener índices de todo el blockchain…

Y con esto ya podemos lanzarlo:

docker compose up -d

Si todo va bien en unas cuantas (muchas) horas ya tendríamos una copia de la blockchain de testnet y podremos preguntar, por ejemplo, cual es el último bloque (suponiendo que nuestra máquina tenga la IP 192.168.1.55) usando curl, por ejemplo:

curl --user miusuario:mipass --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockcount", "params": []}' -H 'content-type: text/plain;' http://192.168.1.55:18443

Que nos devolvería algo como esto (el número de bloque, obviamente) será distinto en vuestro caso:

{"result":3774033,"error":null,"id":"curltest"}

Y, lo dicho, intentaré que el próximo post sea de cómo hacer un explorador de testnet que podamos utilizar via web… Id preparando más disco duro.

Generar imágenes por IA en tu propio ordenador

Ya vimos en la entrada anterior como poder tener nuestro propio chat-gpt sin pagar nada a nadie usando modelos opensource y nuestra GPU, ahora le toca el turno a la posibilidad de generar imágenes por Inteligencia Artificial mediante el mismo método, en casa, de forma privada y sin tener que pagar licencias. Al lío…

La imagen anterior ha sido generada en mi ordenador, con mi tarjeta gráfica y con un prompt muy sencillito, básicamente le he pedido un robot pintando con pinceles en la pantalla del ordenador… Y me ha salido esto (hay más parámetros, pero no he tocado nada especial). Para generar estas imágenes vamos a utilizar Stable Diffussion, que es un modelo de aprendizaje automático para generar imágenes digitales de alta calidad a partir de descripciones en lenguaje natural (wikipedia). Es de código abierto y no impone restricciones a las imágenes que produce.

Como somos hombres (y mujeres) de acción os voy a dar la receta rápida para tener stable difussion y un interfaz de usuario (automatic1111) funcionando en cuestión de minutos (bueno, esto depende de vuestra conexión a internet que hay muchos gigas que descargarse). La receta original, que os recomiendo seguir si queréis experimentar un poco más con el tema, la saqué de aquí: https://github.com/AbdBarho/stable-diffusion-webui-docker pero yo he preparado una imagen que ya tiene todo lo necesario, así que lo único que tenéis que hacer es crear un archivo docker-compose.yml con este contenido:

services:
  sd-auto:
    image: yoprogramo/sd-auto:78
    ports:
      - "7860:7860"
    volumes:
      - ./data:/data
      - ./output:/output
    stop_signal: SIGKILL
    environment:
      - CLI_ARGS=--allow-code --medvram --xformers --enable-insecure-extension-access --api
      - COMMANDLINE_ARGS=--share
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids:
                - "0"
              capabilities:
                - compute
                - utility

Luego, estoy suponiendo que usáis linux y tenéis la configuración de docker y de la tarjeta gráfica que ya vimos en el anterior post, solo hay que ejecutar:

docker compose up -d

Como os he dicho el proceso de descarga inicial de la imagen y del modelo van a tardar un poco (reservaos mínimo 20Gb para todo), pero si todo va bien en unos minutos podréis acceder a la url http://localhost:7860 y veréis la interfaz de AUTOMATIC1111 para stable difussion.

Inicialmente el modelo descargado es sdv1.5-pruned-emaonly que tiene sus limitaciones pero cabe en casi todas la memorias. Ya solo queda hacer la prueba, poniendo algo en el prompt y dandole a Generate.

Si no tienes demasiada memoria en tu tarjeta gráfica te saldrá algo como esto:

Pero si has sido capaz de generar una imagen, se abre todo un abanico de modelos que probar y opciones con las que trastear… El primer sitio para visitar es este:

https://civitai.com

Depurando PHP con Xdebug y Docker

Hasta hace poco he estado depurando PHP al viejo estilo, poniendo error_log, print_r y demás mensajes aquí y allá. La verdad es que era algo poco dañino y que, si programas bien, tampoco da mucho trabajo. Sin embargo echaba de menos las facilidades de java en el eclipse donde podía depurar de manera increíblemente eficiente sin modificar nada de mi código. El caso es que utilizando docker como base del desarrollo/despliegue de PHP se hacía muy complicado configurar las cosas para depurar. Al final, por circunstancias que no vienen al caso, me animé a ver cómo podríamos depurar código php desplegado usando docker… Y os hago aquí un resumen:

El problema básico

Supongamos que queremos hacer un programa básico en php y desplegarlo para poder verlo en nuestro navegador. Por ahora nos vamos a limitar a este archivo

phpinfo.php

<?php
phpinfo();

Si, es un archivo muy simplón, pero primero tenemos que desplegarlo utilizando docker y verlo en un navegador. Para ello creamos un directorio para nuestro proyecto (llamemosle php-samples, por ejemplo) y dentro del mismo creamos un subdirectorio llamado web y dentro de este directorio creamos el archivo phpinfo.php con el contenido anterior.

La forma más rápida

En el directorio superior creamos un archivo llamado docker-compose.yml con este contenido:

services:
  php:
    image: yoprogramo/php8.2:1.0.2
    volumes:
        - ./web:/var/www/Website
    ports:
        - 8080:80

Ya puestos cargamos el proyecto en visual studio code y nos quedará algo así:

Desde el terminal, o desde el menú de la extensión docker si la tenéis instalada, levantamos el contenedor. Una vez levantado ya podremos acceder a la url:

http://localhost:8080/phpinfo.php

Vale, prueba superada, ya tenemos nuestro servidor ejecutando php, ahora crearemos otro archivo dentro de web con un programa en php un poco más complejo para poder depurarlo a gusto. Vamos a llamar a ese archivo index.php y le daremos este contenido:

<?php
function isPrime($num) {
    if ($num <= 1) return false;
    if ($num == 2) return true;
    if ($num % 2 == 0) return false;
    for ($i = 3; $i <= sqrt($num); $i += 2) {
        if ($num % $i == 0) return false;
    }
    return true;
}

$randomNumber = rand(0, 19);

echo "<table border='1'>";
echo "<tr><th>Número</th><th>Primos menores que $randomNumber</th></tr>";
echo "<tr><td>$randomNumber</td><td>";

for ($i = 0; $i < $randomNumber; $i++) {
    if (isPrime($i)) {
        echo "$i ";
    }
}

echo "</td></tr>";
echo "</table>";
?>

Ahora podemos acceder a la url index.php (o no poner ruta ya que es el archivo que se cargará por defecto)

Vale, pero todo este rollo era para poder depurar el código, para poder hacerlo tenemos que hacer tres cosas:

  • Cambiar la imagen docker por yoprogramo/php8.2:1.0.2-xdebug en el docker-compose.yml y añadir este apartado al servicio php:
extra_hosts:
  - "host.docker.internal:host-gateway"
  • Instalar el plugin «PHP Debug»
  • Configurar el visual studio code para php, para ello pinchamos en el icono de depuración (run and debug) y pinchamos donde pone «create a launch.json file)

Esto nos creará un archivo por defecto donde tendremos que modificar la configuración «Listen for Xdebug» para añadir esto:

"pathMappings": {
   "/var/www/Website": "${workspaceRoot}/web"
}

Pulsamos sobre el icono de iniciar depuración con es configuración (o pulsamos F5)

Con todo configurado solo nos queda reiniciar el docker con la nueva imagen y poner algún breakpoint en el código (eso se hace pinchando a la izquierda del número de línea donde queramos que se pare la ejecución, por ejemplo dentro del bucle de index.php

Ahora volvemos a cargar la página index.php y si todo ha ido bien la ejecución se parará y podremos ver en el editor los valores de las variables

Y continuar la ejecución o hacerlo paso a paso

Si queréis saber cual es el camino difícil o cómo hacerlo con vuestras propias imágenes docker, solo tenéis que preguntar…

Añadir https y let’s encrypt a tu aplicación con docker

Una vez que hemos empezado a «dockerizar» aplicaciones, y antes de saltar al siguiente nivel (kubernetes por ejemplo) nos encontramos con la necesidad de pasar a produccion las aplicaciones que vamos desarrollando y, quizá, utilizar un gestor como Kubernetes nos haga más complicado utilizar https. Hay dos soluciones principales que he utilizado para distintos servicios y que os voy a comentar muy brevemente aquí: usar apache como proxy inverso instalado en la máquina host o utilizar un docker con el proxy inverso en nginx.

Para los dos casos vamos a suponer que tenemos un contenedor docker con una aplicación web que tenemos expuesto en el puerto 8080 (por ejemplo).

Método 1: Apache Nativo

Empecemos por el primer sistema, el que primero se me ocurrió y que tiene sus ventajas y sus inconvenientes. Básicamente consiste en instalar de manera nativa el servidor apache, el módulo mod_proxy y hacer que actúe como un proxy inverso para los dominios que necesitemos. Os explico los pasos suponiendo que estáis instalando en una máquina ubuntu recien provisionada:

sudo apt-get install -y apache2 libapache2-mpm-itk
sudo a2enmod rewrite
sudo ufw allow in "Apache Full"
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install -y python-certbot-apache
sudo service apache2 restart

Llegados a este paso debemos crear un archivo de configuración para la aplicación web y dejarlo en /etc/apache2/sites-available/misitio.conf algo como esto:

<VirtualHost *:80>
	ServerName www.misitio.com
	AssignUserId miusuario miusuario

	ServerAdmin [email protected]
	DocumentRoot /home/miusuario/web

	<Directory /home/miusuario/web>
                Options FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>
</VirtualHost>

Lo relevante es el nombre del sitio y un directorio para las páginas, que no vamos a utilizar, pero que tiene que existir para las validaciones posteriores. En este caso estoy usando también el módulo itk para que se ejecute con un usuario sin privilegos. Posteriormente a esto ejecutamos:

sudo a2ensite misitio
sudo service apache2 restart

Con esto ya tendremos el servicio apache levantado y respondiendo a peticiones, por lo que podemos solicitar el certificado (recuerda que el dns del servicio debe apuntar a la dirección IP del servidor).

sudo certbot

Esto nos preguntará qué dominios queremos proteger y si todo ha ido bien nos generará un archivo midominio-le-ssl.conf que contendrá ya los enlaces a los certificados y configuración asociada. Con lo que ya podrías acceder a https://www.midominio.com

Ahora queda la parte en la que «conectamos» este servidor con nuestro docker que, recordemos, está corriendo en el puerto 8080, para ello modificaremos el archivo de configuración que nos ha creado certbot añadiendo estas líneas:

ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/

Reiniciamos apache y ya tenemos enganchado nuestro docker a https.

Método 2: docker que nos lo hace todo

Si el método anterior nos parece un poco pesado o no queremos tener que guardar la configuración particular de una máquina, podemos optar por añadir esto a nuestro archivo docker-compose (teniendo en cuenta que hemos llamado miservicio al servicio que tenemos en el 8080):

    https-portal:
        image: steveltn/https-portal:1
        depends_on:
            - miservicio
        ports:
            - 80:80
            - 443:443
        restart: always
        volumes:
            - ./ssl_certs:/var/lib/https-portal
        environment:
            DOMAINS: 'www.midonio.com -> http://miservicio:8080 #production' 

Y eso es todo, el servidor al levantarse se encarga de pedir los certificados y guardarlos en el directorio ssl_certs que será el único que tenemos que persistir para evitar tener que pedirlos cada vez que arranque el servidor.

Cada uno de los dos métodos tiene sus pros y sus contras (hacerlo en kubernetes es otra historia y no aplica ninguno de estos métodos), pero básicamente si queremos exponer más de un contenedor (en distintos docker-compose) la única manera es usar el proxy inverso nativo, si todo lo que queréis servir por https está en un solo docker-compose el segundo método es mucho más cómodo.

Monta tu propio cluster Kubernetes

Llevo los últimos meses intentando aprender Kubernetes después de que la experiencia con Docker fuese tan satisfactoria en todos los aspectos. No obstante con Kubernetes caía una y otra vez en los problemas de la complejidad inherente a una plataforma tan adaptada para los pasos a producción de grandes aplicaciones. Muchos de los tutoriales (incluyendo los propios de kubernetes) te instaban a instalarte minikube o usar algunos playgrounds disponibles online como Katacoda o Play with kubernetes. Al final lo que era evidente es que necesitaba un cluster k8s para poder aprender un poco más de kubernetes.

Minikube tiene importantes restricciones y los otro playground son de usar y tirar, por lo que, al final, si quería aprender de verdad tenía que construirme mi propio cluster… Y ahora mismo no me apetece pagar por tener algo puramente experimental, así que aproveché y, dado que tengo dos sobremesa en casa, decidí instalar el cluster en mis propios ordenadores y poder disfrutar de toda la potencia de kubernetes. Aquí un resumen muy resumido de lo que hay que hacer en ubuntu 18.04 (que es lo que tenía en los dos):

Primer paso: instalar docker

Eso creo que ya lo hemos tratado aquí en algunas ocasiones, no obstante ha mejorado mucho la forma de instalarlo desde entonces (en todas las máquinas):

sudo apt install docker.io
sudo systemctl enable docker
sudo addgroup docker ${USER}

Paso 2: instalar kubernetes

Igualmente en todos los nodos:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
sudo apt-get install kubeadm kubelet kubectl
sudo apt-mark hold kubeadm kubelet kubectl

Si todo ha ido bien podemos ver la versión que hemos instalado:

kubeadm version

Paso 3: inicializar cluster

Para inicializar el cluster primero debemos asignar un nombre a cada nodo, además, previamente tendremos que desactivar el swap que no se lleva bien con este sistema, primero con el master y luego con el resto:

sudo swapoff -a
sudo hostnamectl set-hostname master-node

Y luego en el resto:

sudo hostnamectl set-hostname worker01

Con todos los nodos ya con nombre podemos inicializar en el maestro el cluster:

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Como resultado (y si todo va bien) el comando nos devolverá el comando a ejecutar en cada uno de los nodos, algo así como:

kubeadm join --discovery-token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234..cdef 1.2.3.4:6443

Debemos guardar ese comando ya que lo tendremos que ejecutar posteriormente en cualquier nodo que queramos unir al cluster

Para poder administrar con kubectl necesitamos guardar la configuración que acabamos de generar en el usuario que estemos usando… Dentro del master es sencillo:

kubernetes-master:~$ mkdir -p $HOME/.kube
kubernetes-master:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
kubernetes-master:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Con esto ya podremos lanzar nuestros comandos kubectl contra nuestro nuevo cluster

Paso 4: Desplegar red en el cluster

Tal como está configurado ahora mismo no hay forma de comunicarse entre los pods y el resto, vamos a instalar flannel como red virtual, para ello ejecutar:

sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Al cabo de un rato podremos ver si los pods están correctamente desplegados con:

kubectl get pods --all-namespaces

Paso 5: añadir nodos a la red

Como ya dijimos en el paso 3, tenemos un comando a ejecutar en cada nodo de la red para unirse al cluster que acabamos de crear. Ejecutamos ese comando en cada uno de los nodos que queremos unir y luego, dentro del master, podemos comprobar si están presentes todos los nodos y el estado en que están:

kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-node Ready master 3d17h v1.18.5
worker01 Ready 3d17h v1.18.5
worker02 Ready 2d22h v1.18.5
worker03 Ready 2d17h v1.18.5

Si todos están en estado Ready hemos triunfado… Listos para desplegar lo que queramos en nuestro cluster casero… A ver si nos da tiempo a explorarlo con cierta extensión.