LXD, Virtualhosts y Docker - perfeccion-ar/infraestructura-clasica-y-avanzada GitHub Wiki
Relacionado:
- Configuración de VPS de Oscar
- HTTPS, Let's Encrypt y Cloudflare
- Nginx Proxy Manager GUI con servicios bajo Docker
- Página propia en Wordpress, con Apache2, MySQL, PHP y phpMyAdmin (Stack LAMP)

En la infraestructura del aula tendemos a meter todo en containers LXD.
Para poder mandar tráfico del puerto 80 del server a cada uno de los containers de los alumnos, usamos un Apache con Virtualhost. La elección de Apache sobre Nginx, es tan solo porque es más simple enseñar la sintaxis de Apache. Dejaremos Nginx para más adelante, junto con Docker.
Este Apache conviene que esté también en un container LXD. Podría estar afuera, en el mismo host. Pero no sería transportable ni estaría bien aislado.
Puesto que uno de los propósitos del taller es estimular en los alumnos la creación de sus propios servidores y VPS, y que ellos mismos algún día puedan hacer auto hosting o "MaaS" (metal-as-a-service), es que ofrezco estos apuntes de mano sobre cómo fue configurado.
Durante el desarrollo de las siguientes líneas, se crearán dos containers:
El primero es un "balancer" simple con Apache. Tiene configurado cada dominio de cada alumno en un achivo separado .conf, y adentro definido cada dominio que traiga el alumno en un VirtualHost aparte. Cada Virtualhost apunta mediante Proxy Reverse al container LXD del alumno.
El segundo es un container de ejemplo, que tendrá Django. Este container estará "escondido" en su propia red, y solo recibirá tráfico del "Balancer". Finalmente, el container con Django tiene un dominio registrado en nic.ar, llamado ecoplatonica.com.ar, con DNS gratuitos proporcionados por Cloudflare. Si no tiene un dominio, obtenga un subdominio gratuito en duckdns.org (ejemplo, catoto.duckdns.org) y apuntelo a este server, 37.27.49.225
El primer container cumple funciones de Proxy Reverso.
Pomposamente lo hemos llamado Balancer, aunque para que "balancee", propiamente dicho, entre varios containers con el mismo servicio (ejemplo, 10 djangos), necesitaría activar el módulo lbmethod_byrequests
En nuestro ejemplo, por ahora este [ balancer - proxy reverso], solo nos hará de dispatcher del puerto 80, reenviando un dominio que le configuraremos mediante Virtualhost, hacia un único container con Django.
Luego usted podrá agregar más virtualhosts que apunten hacia otros containers, con todos los servicios que se le antoje. Ejemplos:
- sitio1.com con Java (ej: un Tomcat)
- sitio2.com con Node (ej: Express)
- pepe.sitio3.com con Ruby (ej: Sinatra, Rails)
- cachita.duckdns.com con PHP (ej: un Wordpress bajo otro Apache, un Laravel bajo un Nginx)
- etc, etc
Si quiere más detalles sobre este patrón de diseño, consulte la excelente distinción que hace entre Proxy Reverso, Balancer propiamente dicho, e incluso API Gateway que hace el arquitecto de software de ByteByteGo, en este corto video: https://www.youtube.com/watch?v=RqfaTIWc3LQ

Ejemplo de creación de un container LXD para que haga de ProxyPassReverse
lxc launch ubuntu:22.04 balancer
Para más información sobre
lxc launch
, instalación de LXD y creación de containers, consulte el artículo sobre LXD
Ahora entramos al container
lxc shell balancer
Instalamos "solo" Apache. Para reducir la superficie de ataque, nada de agregar PHP ni lenguajes backend.
apt-get update && apt-get install apache2
Necesitamos activar algunos módulos
a2enmod headers
a2enmod ssl
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_connect
Quitamos la configuración del sitio de bienvenida
cd /etc/apache2/sites-available/
a2dissite 000-default.conf
rm 000-default.conf
rm default-ssl.conf
Restarteamos Apache
systemctl restart apache2
Importante: este container "balancer" debe recibir todo el tráfico del puerto 80 y 443 del host. Se hace mediante una regla de proxy LXD. Ejemplo:
lxc config device add balancer balancerport80 proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80
Para probar proxear hacia otro container, crearemos un proxy reverso hacia un servicio pequeño instalado en otro container. Por ejemplo, con Django
Para ello, salimos con exit, y en el host creamos otro container
En el host, donde hemos instalado LXD, creamos el segundo container,
lxc launch ubuntu:22.04 django-prueba
Entramos al container
lxc shell django-prueba
Instalamos Python adentro
apt-get update && apt install python3
Para liberarnos de la versión de Python por defecto que nos instala apt-get , y para no seguir instalando librerías que puedan comprometer el sistema operativo del container, instalaremos el resto de las librerías con un manejador de entornos de Python. Podemos usar Virtualnwrapper, o mi preferido, Miniconda.
La siguiente es una práctica sumamente aconsejada: Linux usa muchas librerías de Python para su funcionamiento interno. Si nos pusieramos a instalar las nuestras para nuestro proyecto, siempre como root o con sudo, podríamos estropear el sistema operativo.
Finalmente, las librerías de Python conviene administrarlas mediante pip en su propio espacio de usuario.
A continuación, como instalar Miniconda, Pip, y un Django de prueba.
Si no le salen los siguientes pasos, simplemente levante un miniserver http, con
python3 -m http.server
Para instalar Miniconda:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
~/miniconda3/bin/conda init
source ~/.bashrc
pip install virtualenvwrapper
conda update conda
conda update --all
Ahora creamos un pequeño entorno con Python 3.12, o con la versión que sea que aconseje la página de Releases de Django. A la fecha: Python 3.12 para Django 5. Esto es muy importante si venimos con un Django viejo: consulte este sitio para estar seguro: https://docs.djangoproject.com/en/5.0/faq/install/#what-python-version-can-i-use-with-django
Entonces, si nos hemos decidido en efecto, por Python 3.12:
conda create -n py3.12 Python=3.12
Activamos el entorno virtual:
conda activate py3.12
Ya tenemos nuestro Python 3.12 totalmente aparte del sistema operativo. Ahora generamos un proyecto pequeño en Django.
pip install django
django-admin startproject myproject
cd myproject
python manage.py migrate
Editamos myproject/settings.py
y dejamos esta línea así
ALLOWED_HOSTS = ["*"]
Ahora arrancamos Django. Pero si no queremos que se cierre al cerrar la terminal, conviene dejarlo corriendo bajo algún multiplexador de terminal, como Byobu
python manage.py runserver 0.0.0.0:8000
Obtendremos
Django version 5.0.7, using settings 'myproject.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
Dejamos Django corriendo en el container django-prueba.
En otra terminal, entramos al container balancer para chequear si el Apache podrá llegar hasta el container django-prueba. Pero primero, ¿cuál es la ip del container con Django?
- Para saberlo desde adentro del container:
ip ad
- Para saberlo sin entrar al container, desde el host:
lxc list
ianr@host:~$ lxc list
+------------------+---------+-----------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------------------+---------+-----------------------+------+-----------+-----------+
| balancer | RUNNING | 10.149.127.90 (eth0) | | CONTAINER | 0 |
+------------------+---------+-----------------------+------+-----------+----
| django-prueba | RUNNING | 10.149.127.200 (eth0) | | CONTAINER | 0 |
+------------------+---------+-----------------------+------+-----------+-----------+
Entonces, desde el balancer, probamos con curl, o con algún navegador de consola, como GNU w3m
sudo apt-get install w3m
w3m http://10.149.127.200:8000
Respuesta:
View release notes for Django 5.0
The install worked successfully! Congratulations!
You are seeing this page because DEBUG=True is in your settings file and you have not configured any URLs.
Seguimos en el container balanceador. Creamos un virtualhost, y adentro le definimos un dominio, y un ProxyReverse con un container LXD como destino. Aquí tiene como queda el archivo, situado en /etc/apache2/sites-available/ecoplatonica.com.ar.conf
(debe terminar en .conf)
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName ecoplatonica.com.ar
ProxyPreserveHost On
CustomLog /var/log/apache2/ecoplatonica.log combined
ErrorLog /var/log/apache2/ecoplatonica-error.log
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPreserveHost On
ProxyPass / http://10.149.127.200:8000/
ProxyPassReverse / http://10.149.127.200:8000/
</VirtualHost>
Activamos el virtualhost y reiniciamos Apache
a2ensite ecoplatonica.com.ar.conf
apachectl configtest
systemctl restart apache2
Si no tuviéramos delegado el dominio, igual podemos probar localmente con w3m. Editamos /etc/hosts
y agregamos
127.0.0.1 ecoplatonica.com.ar
Listo. Ahora el container balancer creerá que "él" tiene el sitio instalado adentro. Si hacemos
w3m ecoplatonica.com.ar
Obtendremos nuevamente, como cuando hicimos w3m http://10.149.127.200:8000
django
View release notes for Django 5.0
The install worked successfully! Congratulations!
- Se puede mandar tráfico a Docker?
- Si, se puede, pero no conviene meter Docker y LXD en el mismo host. Se pelearán por aplicar reglas de iptables, y uno u otro pueden quedarse "ciegos" de red. Entonces, Docker debería correr dentro de una VM, o dentro de un Container en un pool con BTRFS. Consulte las opciones 2 y 3 de la sección "Docker adentro de LXD".

Configuración en una VPS del alumno Oscar, alojado en Contabo, instalada LXD. Adentro, un container LXD contiene un Apache, que hace de ProxyReverse hacia otro container LXD que atiende en un puerto 3000, con un proyecto en Ruby on Rails. Se utiliza Cloudflare como DNS: - requiere solicitar invitación a [email protected] https://youtu.be/x8RyJ2q85T4
Clase mas o menos con el mismo del mismo tenor, de Sergio Alberto: https://youtu.be/Fp67WDSFyeg
Y eso es todo lo que sé sobre ProxyPassReverse 😄
Cualquier duda, consultas a [email protected]