Migración de Base de datos Postgres entre LXC y KVM, ¡al vuelo!
Bueno, en esta entrada explicaré un trabajo que tuve que hacer de migrar una base de datos Postgres existente en un contenedor LXC con Sistema Operativo Debian Jessie hacia una Máquina Virtual KVM con Sistema Operativo Debian Jessie.
Aunque a algunos les pueda parecer muy difícil, fue una tarea relativamente sencilla y bonita de realizar. Aunque he de reconocer que llegué a un punto que me tuvo un par de horillas un poco frustado. Esa frustación vino porque no apliqué los 3 principios básicos de GNU/Linux que todo Sysadmin debe tener siempre presente:
- Comprobar permisos y propietarios de todos los ficheros en juego.
- Comprobar ficheros de configuración
- Si se ha modificado algún fichero de configuración, comenzar de nuevo en el punto 1.
1. Introducción
Explicaré con cierto detalle el problema y la resolución que llevé a cabo.
Se parte de un contendor LXC con Debian Jessie. Hay que instalarle PostgreSQL y crear una base de datos que luego será traspasada a la máquina virtual KVM.
El esquema de red es el siguiente. Tanto el contenedor como la máquina virtual están conectadas a un ‘Bridge Linux aislado‘ que existe en el host anfitrión. Hay una regla de ‘iptables‘ que hace DNAT desde la interfaz que tiene salida a la red local hacia la base de datos Postgres, con la idea de que cuando se haga el traslado desde LXC a KVM tan solo haya que cambiar la regla DNAT de iptables.
Con esto se consigue que a la vista de, por ejemplo, una aplicación web que esté haciendo consultas a la base de datos ‘virtualizada‘ que tenemos en el host anfitrión, la base de datos no ha parado de funcionar en nigún momento, a pesar que la hemos cambiado de máquina.
Para la correcta resolución del problema es necesario un script que haga el proceso de forma prácticamente instantánea, yo opté por hacerlo en BASH.
Este es esquema gráfico:
De lo que se trata es de trasladar la base de datos y cambiar esa regla de iptables de la 10.10.5.2 hacia la 10.10.5.3.
2. Creación del escenario
2.1. Configuración de la red
El anfitrión es un sistema operativo Debian Jessie con 2 Bridges Linux.
‘br0’ con la interfaz ‘eth0’ como esclavo y una IP obtenida mediante DHCP.
‘br1’ es un bridge aislado, esto es, no tiene ninguna interfaz física conectada, con la dirección ‘10.10.5.1/24’.
Este es el fichero de configuración ‘/etc/network/interfaces’ que define el escenario:
oot@zhora:~# cat /etc/network/interfaces # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). source /etc/network/interfaces.d/* # The loopback network interface auto lo br0 br1 iface lo inet loopback # The primary network interface allow-hotplug eth0 iface eth0 inet manual iface br0 inet dhcp bridge_ports eth0 bridge_stp off # disable Spanning Tree Protocol bridge_waitport 0 # no delay before a port becomes available bridge_fd 0 # no forwarding delay iface br1 inet static address 10.10.5.1 netmask 255.255.255.0 bridge_stp off # disable Spanning Tree Protocol bridge_waitport 0 # no delay before a port becomes available bridge_fd 0 # no forwarding delay root@zhora:~#
Se tendrá también el bit de reenvío ipv4 activo.
root@zhora:~# sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1 root@zhora:~#
2.2. Máquina KVM
Parto con una máquina KVM que tiene un sistema operativo Debian Jessie actualizado en Diciembre, con PostgreSQL y la base de datos por defecto que se crea de forma automática durante la instalación mediante los repositorios de Debian.
La configuración de la red para este espacio será estática con ‘ipv4 = 10.10.5.3/24’ y estará conectada a ‘br1’. Como no necesita navegar por Internet no configuraré DNS.
El ‘hostname’ es: ‘postgreskvm’.
También tiene instalado, configurado y activado ‘openssh’.
2.3. Contenedor LXC
Tanto el ‘hostname’ como el nombre LXC del contenedor se llamarán: ‘postgreslxc’.
2.3.1. Creación del contenedor
root@zhora:~# LANG=C SUITE=jessie MIRROR=http://httpredir.debian.org/debian lxc-create -n postgreslxc -t debian
Hago un primer inicio de sesión sin tener configurada la red del contenedor para cambiar la contraseña de ‘root’ y comprobar que se creó correctamente.
root@zhora:~# lxc-console -n postgreslxc Connected to tty 1 Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself Debian GNU/Linux 8 postgreslxc tty1 postgreslxc login: root Password: Linux postgreslxc 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@postgreslxc:~# passwd root Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully root@postgreslxc:~# logout Debian GNU/Linux 8 postgreslxc tty1 postgreslxc login: root@zhora:~# root@zhora:~# lxc-stop -n postgreslxc root@zhora:~#
2.3.2. Configuración de la red para ‘postgreslxc’
‘postgreslxc’ va estar conectado a ‘br1’ a través de ‘eth0’, que es una interfaz de tipo ‘veth’, con una dirección de red estática ‘ipv4=10.10.5.2/24’ y como puerta de enlace ‘10.10.5.1’.
Para la configuración de las DNS haré un ‘enlace duro’ del fichero ‘/etc/resolv.conf’ y así no tendré que reescribirlas de nuevo según los parámetros de la red local en la que se encuentre el contenedor.
root@zhora:/var/lib/lxc# cat << EOF >> postgreslxc/config lxc.network.type = veth lxc.network.link = br1 lxc.network.flags = up lxc.network.ipv4 = 10.10.5.2/24 lxc.network.ipv4.gateway = 10.10.5.1 lxc.network.name = eth0 EOF root@zhora:/var/lib/lxc# # aunque no es necesario comento la siguiente línea root@zhora:/var/lib/lxc# sed -ri 's/(.*network.*empty.*)/#\1/' postgreslxc/config root@zhora:/var/lib/lxc# # establezco la configuración de la red del contenedor de forma manual root@zhora:/var/lib/lxc# sed -ri 's/dhcp/manual/' postgreslxc/rootfs/etc/network/interfaces root@zhora:/var/lib/lxc# ln -f {,postgreslxc/rootfs}/etc/resolv.conf root@zhora:/var/lib/lxc#
2.4. Reglas de ‘iptables’
Ahora hay que hacer que las interfaces que cuelgan de ‘br1’ tengan acceso al exterior. Para ello crearé una regla SNAT que traduzca las direcciones privadas de la red bajo ‘br1’.
Como señalé en el punto 2.2 no es necesario para la máquina KVM pero sí lo es para el contenedor, puesto que tenemos que instalarle ‘PostgreSQL’.
La regla a crear es la siguiente:
iptables -t nat -A POSTROUTING -s 10.10.5.0/24 -o br0 -j MASQUERADE
La agregaré a la configuración del bridge ‘br1’ de la máquina anfitriona para que se cree justo al levantar el bridge.
root@zhora:/var/lib/lxc# sed -rn '/iface br1/,$p' /etc/network/interfaces iface br1 inet static address 10.10.5.1 netmask 255.255.255.0 bridge_stp off # disable Spanning Tree Protocol bridge_waitport 0 # no delay before a port becomes available bridge_fd 0 # no forwarding delay post-down iptables -t nat -D POSTROUTING -s 10.10.5.0/24 -o br0 -j MASQUERADE post-up iptables -t nat -A POSTROUTING -s 10.10.5.0/24 -o br0 -j MASQUERADE root@zhora:/var/lib/lxc#
También añado la línea con el comando para que elimine la regla cuando se desactive el bridge.
Esa regla cambiará todas las ipv4 provenientes del bridge ‘br1’ por la ipv4 que tenga en ese momento ‘br0’ que como ya dije antes es dinámica.
2.5. Instalación de PostgreSQL en el contenedor ‘postgreslxc’
root@postgreslxc:~# apt-get install postgresql
Durante las pruebas previas a la resolución, y aquí es donde tuve ciertos problemas por no aplicar las 3 máximas que escribí al principio, descubrí que instalar PostgreSQL en LXC difiere en la instalación hecha en la máquina virtual KVM en el UID y GID del usuario ‘postgres’. En este momento tuve que decidir entre 3 opciones posibles para resolver la situación:
- Hacer la migración cambiando de propietario mediante ‘chown -R postgres. /etc/postgresql/…‘ los ficheros en la MV-KVM para que coincidan con los UID y GID del usuario ‘postgres‘ en el nuevo entorno. Particularmente a mí me parece chapucero y poco elegante.
- Crear el usuario ‘postgres’ con el UID/GID que tenga en la máquina KVM antes de hacer la instalación del paquete ‘postgresql‘ en LXC, de esta forma nos aseguramos que losUID/GID coincidirán cuando se haga el traslado. Una solución muy digna.
- Crear un usuario con el mismo UID/GID en ambas máquinas y crear el clúster ‘postgres‘ para él. Con esto al hacer el traslado coincidirán los UID/GID. Otra muy digna y es la que se hará aquí.
A mi modo de ver las 2 últimas opciones son igualmente válidas pero la última tenía un handicap añadido y era, aprender a crear clústers ‘postgres‘ para usuarios… por eso elegí la última :-D.
Como ya he dicho, para evitar problemas de propietarios en el traslado del directorio de datos de PostgreSQL hacia KVM, voy a crear un clúster con el propietario ‘hlc’ de ‘uid=gid=2000’. Para ese propósito hay que crear ese usuario tanto en el entorno de ‘virtualización completa’ como en el contenedor LXC.
root@postgreslxc:~# groupadd -g 2000 hlc root@postgreslxc:~# useradd -m -s /bin/bash -u 2000 -g 2000 -G postgres -c 'Usuario para el cluster de PG' hlc root@postgreslxc:~# passwd hlc Enter new UNIX password: hlc Retype new UNIX password: passwd: password updated successfully root@postgreslxc:~#
Creo el usuario en la máquina ‘postgreskvm’
Aunque realmente no correspondería crear el usuario en este apartado, lo hago aquí para
root@postgreskvm:~# groupadd -g 2000 hlc root@postgreskvm:~# useradd -m -s /bin/bash -u 2000 -g 2000 -G postgres -c 'Usuario para el cluster de PG' hlc root@postgreskvm:~# passwd hlc Enter new UNIX password: hlc Retype new UNIX password: passwd: password updated successfully root@postgreskvm:~#
2.6. Añadir volumen lógico para el cluster
Para añadir el volumen lógico hay que apagar el contenedor (se puede hacer sin apagar el contenedor pero cuando resolví el problema andaba escaso de tiempo y no pude hacerlo, algún día lo dcoumentaré aquí mismo).
root@zhora:~# lxc-stop -n postgreslxc root@zhora:~#
2.6.1. Creación del volumen lógico
Creo el volumen lógico y le creo el sistema de archivos:
root@zhora:~# lvcreate -L 5G -n dataPG debvg1 Logical volume "dataPG" created root@zhora:~# mkfs.ext4 -L DATAPG /dev/debvg1/dataPG mke2fs 1.42.12 (29-Aug-2014) Discarding device blocks: done Creating filesystem with 1310720 4k blocks and 327680 inodes Filesystem UUID: 91d3bd4e-fbb2-40f0-9535-36d40b980598 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done root@zhora:~#
2.6.2. Configuración del contenedor ‘postgreslxc’
Compruebo que ‘fstab’ está incluido al iniciar el contenedor.
root@zhora:~# grep 'fstab' /var/lib/lxc/postgreslxc/config lxc.mount = /var/lib/lxc/postgreslxc/fstab root@zhora:~#
Añado la línea correspondiente al ‘fstab’:
root@zhora:~# printf '/dev/debvg1/dataPG home/hlc/postgresql ext4 defaults 0 0\n' | column -t >> /var/lib/lxc/postgreslxc/fstab root@zhora:~# cat !$ cat /var/lib/lxc/postgreslxc/fstab /dev/debvg1/dataPG home/hlc/postgresql ext4 defaults 0 0 root@zhora:~#
Creo la ruta de montaje antes de lanzar el contenedor y le asigno el propietario ‘2000’:
root@zhora:~# mkdir /var/lib/lxc/postgreslxc/rootfs/home/hlc/postgresql root@zhora:~# chown 2000:2000 !$ chown 2000:2000 /var/lib/lxc/postgreslxc/rootfs/home/hlc/postgresql root@zhora:~#
Ya se puede iniciar el contenedor…
2.7. Creación del clúster en postgreslxc
Antes de crear el cluster para el usuario ‘hlc’ hay que cambiar el propietario del nuevo montaje y eliminar el ‘lost+found’ para que el comando ‘pg_createcluster’ se pueda ejecutar con éxito.
root@postgreslxc:~# rm -r /home/hlc/postgresql/lost+found/ root@postgreslxc:~# chown hlc. /home/hlc/postgresql/ root@postgreslxc:~#
Una vez preparada la ruta de destino del clúster se puede proceder a su creación.
root@postgreslxc:~# cd /home/hlc/ root@postgreslxc:/home/hlc# BASEDIR='/home/hlc/postgresql/9.4' root@postgreslxc:/home/hlc# DATADIR="${BASEDIR}/data" root@postgreslxc:/home/hlc# LOGDIR="${BASEDIR}/log" root@postgreslxc:/home/hlc# SOCKETDIR="${BASEDIR}/socket" root@postgreslxc:/home/hlc# PORT=5433 root@postgreslxc:/home/hlc# pg_createcluster -u 2000 -g 2000 \ -d $DATADIR -l $LOGDIR -s $SOCKETDIR -p $PORT \ 9.4 hlc Creating new cluster 9.4/hlc ... config /etc/postgresql/9.4/hlc data /home/hlc/postgresql/9.4//data locale C Flags of /home/hlc/postgresql/9.4//data set as -------------e-C port 5433 root@postgreslxc:/home/hlc#
2.7.1. Variables del entorno
Establezco las variables del entorno para el usuario ‘hlc’:
root@postgreslxc:/home/hlc# printf "export PGHOST=$SOCKETDIR\nexport PGPORT=$PORT\nexport PGUSER=hlc\n" >> /home/hlc/.bashrc
2.7.2. Creación del usuario y base de datos para la práctica
Usuario
hlc@postgreslxc:~$ createuser --interactive -P practicalxc -h /home/hlc/postgresql/9.4/socket/ -p 5433 Enter password for new role: practicalxc Enter it again: Shall the new role be a superuser? (y/n) y hlc@postgreslxc:~$
Base de datos
hlc@postgreslxc:~$ createdb -O practicalxc -p 5433 -h /home/hlc/postgresql/9.4/socket/ practicalxc 'Base de datos para la practica de LXC' hlc@postgreslxc:~$
Comprobación
hlc@postgreslxc:~$ pg_ctlcluster 9.4 hlc start hlc@postgreslxc:~$ psql psql (9.4.9) Type "help" for help. hlc=# \q hlc@postgreslxc:~$
2.7.3.Configuración de los parámetros de acceso a la base de datos
Dirección ipv4 por la que aceptar peticiones
Configuro el clúster para ‘hlc’ en la ipv4 del contenedor.
hlc@postgreslxc:~$ grep 'listen' /etc/postgresql/9.4/hlc/postgresql.conf listen_addresses = 'localhost,10.10.5.2' # what IP address(es) to listen on; hlc@postgreslxc:~$
Direcciones ipv4 aceptadas para el acceso
Configuro el clúster para aceptar conexiones de cualquier usuario provenientes de cualquier dirección ipv4 habida y por haber xD.
hlc@postgreslxc:~$ grep '^host' /etc/postgresql/9.4/hlc/pg_hba.conf host all all 127.0.0.1/32 md5 host all all 0.0.0.0/0 md5 host all all ::1/128 md5 hlc@postgreslxc:~$
Reinicio el clúster
hlc@postgreslxc:~$ pg_ctlcluster 9.4 hlc restart hlc@postgreslxc:~$
2.7.4. Configuración de openssh
He dejado configurado y activado el servidor ssh en el contenedor.
2.8. Configuración de la máquina ‘postgreskvm’
Aquí no hay que crear el clúster, lo que sí debe existir es el directorio de configuración del clúster ‘hlc’ en ‘/etc/postgresql/9.4’.
Lo haré desde el contenedor con los siguientes comandos:
root@postgreslxc:~# scp -rp /etc/postgresql/9.4/hlc/ 10.10.5.3:/etc/postgresql/9.4/ 1>/dev/null root@postgreslxc:~# ssh 10.10.5.3 chown hlc:hlc -R /etc/postgresql/9.4/hlc root@postgreslxc:~# ssh 10.10.5.3 sudo -u hlc mkdir /home/hlc/postgresql root@postgreslxc:~# ssh 10.10.5.3 sed -ri 's/10.10.5.2/10.10.5.3/' /etc/postgresql/9.4/hlc/postgresql.conf root@postgreslxc:~#
3. Regla DNAT para acceso al clúster PG
Para acceder desde el exterior a nuestra base de datos, es necesario crear una regla que redirija las peticiones de un puerto del bridge ‘br0’ hacia el puerto de nuestro clúster en el contenedor LXC.
La regla sería esta:
iptables -t nat -A PREROUTING -p tcp --dport 5432 -i br0 -j DNAT --to 10.10.5.2:5433
La agregaré a la sección de ‘br1‘ en el ‘interfaces’ de la anfitriona para que se ejecute al levantar el bridge.
Así es como queda la sección ‘br1’ finalmente:
root@zhora:/home/manuel# sed -n '/iface br1/,$p' /etc/network/interfaces iface br1 inet static bridge_ports none address 10.10.5.1 netmask 255.255.255.0 bridge_stp off # disable Spanning Tree Protocol bridge_waitport 0 # no delay before a port becomes available bridge_fd 0 # no forwarding delay post-up sysctl -w net.ipv4.ip_forward=1 post-up iptables -t nat -A POSTROUTING -s 10.10.5.0/24 -o br0 -j MASQUERADE post-up iptables -t nat -A PREROUTING -p tcp --dport 5432 -i br0 -j DNAT --to 10.10.5.2:5433 post-down iptables -t nat -F post-down iptables -F post-down sysctl -w net.ipv4.ip_forward=0 root@zhora:/home/manuel#
4. Script de traslado del clúster
El script necesario para la resolución del ejercicio básicamente debe recorrer los siguientes pasos:
- Comprobar que la máquina kvm está encendida
- Encender si no lo está
- Comprobar que puede acceder mediante ssh a las dos máquinas
- Parar el clúster en ‘postgreslxc’
- Desmontar el volumen lógico del contenedor
- Conectar el volumen lógico a la máquina virtual en caliente
- Montar el dispositivo de bloques
- Iniciar el clúster
- Cambiar regla iptables
#!/usr/bin/env bash # ZONA DE CONSTANTES # ================== KVMDOM='practicalxc' UsuarioPG='hlc' IPLXC='10.10.5.2' IPKVM='10.10.5.3' VersionPG='9.4' ClusterPG='hlc' BaseDir='postgresql' ClusterPath="/home/${UsuarioPG}/${BaseDir}" VOLUMENLOGICO='/dev/debvg1/dataPG' SSHCMD='ssh -q -o BatchMode=yes' TARGETDVC='vdb' EXTIFACE='br0' EXTPORT=5432 INTPORT=5433 function Salida { printf 'Saliendo del script..\n' exit } trap Salida EXIT INT TERM function ComprobarKVM(){ printf 'Comprobando estado de la máquina KVM...\n' if [[ $(virsh dominfo $KVMDOM | grep -Ei '.*state:[[:space:]]*running.*') ]]; then printf 'Máquina encendida..\n' else printf 'Máquina apagada.. Encendiendo..\n' maxintentos=3 intento=0 while (( intento < maxintentos )) && [[ ! $(virsh dominfo $KVMDOM | grep -Ei '.*state:[[:space:]]*running.*') ]]; do ((intento++)) printf "Intentando arrancar la máquina KVM (${intento}/${maxintentos})...\n" virsh start $KVMDOM [[ ! $(virsh dominfo $KVMDOM | grep -Ei '.*state:[[:space:]]*running.*') ]] && sleep 5 done [[ ! $(virsh dominfo $KVMDOM | grep -Ei '.*state:[[:space:]]*running.*') ]] && \ printf 'Error iniciando la máquina KVM...\n' && \ exit 1 fi } function ComprobarCreds(){ printf 'Comprobando credenciales SSH\n' error=0 for HOSTCHK in $IPLXC $IPKVM; do for USERCHK in root $UsuarioPG; do $SSHCMD ${USERCHK}@${HOSTCHK} exit STATUS=$? (( STATUS != 0 )) && \ printf "Error en $HOSTCHK con usuario: $USERCHK\n" && \ printf "Estado: $STATUS\n" && \ error=1 done done (( error == 1 )) && \ printf 'Comprueba las credenciales antes de ejecutar el script...\n' && \ exit 1 printf 'Credenciales comprobadas con éxito.\n' } function PararCluster(){ printf 'Parando el clúster...' $SSHCMD ${UsuarioPG}@${IPLXC} pg_ctlcluster -m fast ${VersionPG} ${ClusterPG} stop &>/dev/null exito=$? (( exito != 0 )) && \ printf 'No se puede parar el cluster en LXC...\nNo se desmonta el volumen.\n' && \ exit 1 printf 'Cluster parado.\n' } function DesmontarVolumen(){ printf 'Desmontando el volumen..\n' $SSHCMD root@${IPLXC} umount $ClusterPath &>/dev/null exito=$? (( exito != 0 )) && \ printf 'No se puede desmontar el volumen.. No se conecta a KVM\n' && \ exit 1 printf 'Volumen desmontado con éxito.\n' } function ConectarVolumen(){ printf 'Conectando disco a KVM...\n' virsh attach-disk $KVMDOM \ --source $VOLUMENLOGICO \ --target $TARGETDVC \ --targetbus virtio \ --driver qemu \ --subdriver raw \ --cache none \ --sourcetype block \ --live (( $? != 0 )) && \ printf 'No se pudo conectar el volumen.. No se sigue con el script\n' && \ exit 1 printf 'Disco conectado con éxito.\n' } function MontarDispositivo(){ printf 'Montando unidad en vdb...\n' $SSHCMD root@${IPKVM} mount /dev/${TARGETDVC} ${ClusterPath} &>/dev/null (( $? != 0 )) && \ printf 'No se pudo montar el disco.. No se sigue con el script\n' && \ exit 1 printf 'Unidad montada con éxito.\n' } function IniciarCluster(){ printf 'Iniciando clúster en KVM...\n' $SSHCMD ${UsuarioPG}@${IPKVM} pg_ctlcluster ${VersionPG} ${ClusterPG} start &>/dev/null (( $? != 0 )) && \ printf 'No se pudo iniciar el clúster.. No se sigue con el script\n' && \ exit 1 printf 'Cluster iniciado con éxito.\n' } function CrearReglasIpTables(){ printf 'Creando reglas DNAT de IPTABLES...\n' iptables -t nat -D PREROUTING -p tcp --dport ${EXTPORT} -i ${EXTIFACE} -j DNAT --to ${IPLXC}:${INTPORT} iptables -t nat -A PREROUTING -p tcp --dport ${EXTPORT} -i ${EXTIFACE} -j DNAT --to ${IPKVM}:${INTPORT} } if [[ -z $1 ]]; then ComprobarKVM ComprobarCreds PararCluster DesmontarVolumen ConectarVolumen MontarDispositivo IniciarCluster CrearReglasIpTables elif [[ $1 == '--help' ]]; then printf 'ComprobarKVM\n' printf 'ComprobarCreds\n' printf 'PararCluster\n' printf 'DesmontarVolumen\n' printf 'ConectarVolumen\n' printf 'MontarDispositivo\n' printf 'IniciarCluster\n' printf 'CrearReglasIpTables\n' else if [[ $2 == '-d' ]]; then printf 'Modo depuración\n' set -x $1 set +x else $1 fi fi
5.Prueba de resolución del ejercicio
He colgado un vídeo en youtube con una pequeña demostración del proceso de traslado.
La conexión del cliente la hago desde otro ordenador en la misma LAN que el servidor de base de datos aunque bien podría estar en cualquier pc que tenga acceso a Internet.
Comentarios recientes