¿Qué es lxc?
La Wikipedia nos da la siguiente información:
LXC (Linux Containers) es una tecnología de virtualización en el nivel de sistema operativo (SO) para Linux. LXC permite que un servidor físico ejecute múltiples instancias de sistemas operativos aislados, conocidos como Servidores Privados Virtuales (SPV o VPS en inglés) o Entornos Virtuales (EV). LXC no provee de una máquina virtual, más bien provee un entorno virtual que tiene su propio espacio de procesos y redes.
¿Por qué querríamos usar lxc?
Lxc nos provee de un entorno virtual en el que podremos arrancar diferentes sistemas operativos, además, podremos hacer copias de estos sitemas, pasarlos de una máquina física a otra, hacer snapshots, y muchas cosas más.
Para demostrarlo, vamos a realizar nuestra prueba de concepto instalando un gitlab en uno de estos entornos.
Caso práctico
Lo primero que tenemos que hacer, es comprobar si tenemos instalado lxc, para ello vamos a ejecutar el siguiente comando:
lxc --version
3.0.3
En caso de que no lo tuviesemos instalados podemos instalarlo con nuestro gestor de paquetes, en mi caso apt
apt install lxc
Los comandos básicos de los que disponemos podemos verlos con:
lxc --help
Description:
Command line client for LXD
All of LXD's features can be driven through the various commands below.
For help with any of those, simply call them with --help.
Usage:
lxc [command]
Available Commands:
alias Manage command aliases
cluster Manage cluster members
config Manage container and server configuration options
console Attach to container consoles
copy Copy containers within or in between LXD instances
delete Delete containers and snapshots
exec Execute commands in containers
file Manage files in containers
help Help about any command
image Manage images
info Show container or server information
launch Create and start containers from images
list List containers
move Move containers within or in between LXD instances
network Manage and attach containers to networks
operation List, show and delete background operations
profile Manage profiles
publish Publish containers as images
remote Manage the list of remote servers
rename Rename containers and snapshots
restart Restart containers
restore Restore containers from snapshots
snapshot Create container snapshots
start Start containers
stop Stop containers
storage Manage storage pools and volumes
version Show local and remote versions
Flags:
--all Show less common commands
--debug Show all debug messages
--force-local Force using the local unix socket
-h, --help Print help
-v, --verbose Show all information messages
--version Print version number
Use "lxc [command] --help" for more information about a command.
Vamos a ver que contenedores tenemos funcionando:
lxc list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
Como podemos comprobar, no tenemos ningún contenedor, vamos a listar las imágenes que tenemos disponibles con el siguiente comando:
lxc image list images:
Vamos a utilizar la imagen ubuntu:16.04, así que lanzaremos…
lxc launch ubuntu:16.04
En el comando anterior, podemos indicar después del nombre de la imagen (ubuntu:16.04) un nombre identificativo para el contenedor.
También podemos indicar el flag -c security.nesting=true, que necesitaremos para hacer funcionar docker dentro del contenedor lxc
En este caso no hemos puesto el flag, para poder ver el error que nos da docker si no lo ponemos y como solucionarlo
Una vez que termine el comando, podremos ver el contenedor con lxc list
lxc list
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
| skilled-bat | RUNNING | 10.61.111.23 (eth0) | fd42:1f1a:7d4f:4c55:216:3eff:fe59:fde4 (eth0) | PERSISTENT | 0 |
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
Podremos iniciar el contenedor con lxc start nombreDelContenedor
, pararlos con lxc stop nombreDelContenedor
, crear un snapshot con lxc snapshot nombreDelContenedor
o levantar un segundo contenedor con otra instancia del mismo, u otro sitema operativo.
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
| topical-koi | RUNNING | 10.61.111.52 (eth0) | fd42:1f1a:7d4f:4c55:216:3eff:fe46:7a85 (eth0) | PERSISTENT | 1 |
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
| ubuntu1804 | STOPPED | | | PERSISTENT | 0 |
+-------------+---------+---------------------+-----------------------------------------------+------------+-----------+
Una vez tenemos nuestro contenedor lxc funcionando, nos vamos a conectar a el y vamos instalar docker.io y docker-compose, esto lo vamos a hacer en el contenedor topical-koi
, asi que nos conectaremos a el con lxc exec topical-koi bash
Ahora que estamos dentro, vamos a realizar diferentes tareas. Actualizaremos apt, instalaremos docker.io, docker-compose y openssh-server. Además, meteremos nuestra llave publica a authorized_keys para poder conectarnos por ssh
apt update && apt install -y docker.io docker-compose openssh-server
Esta parte es opcional, pero es útil si queremos conectarnos desde otra máquina
root@topical-koi:~# echo "ssh-rsa tuclavesshpublica athos@athos-Z97P-D3" >> /home/ubuntu/.ssh/authorized_keys
root@topical-koi:~# echo "ssh-rsa tuclavesshpublica athos@athos-Z97P-D3" >> /root/.ssh/authorized_keys
Después de haber instalado docker, al hacer un lxc list
veremos que nos ha creado una interface de red nueva.
athos@athos-Z97P-D3:~/Documents/athosnetwork.es/front$ lxc list
+-------------+---------+----------------------+-----------------------------------------------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------+---------+----------------------+-----------------------------------------------+------------+-----------+
| topical-koi | RUNNING | 172.17.0.1 (docker0) | fd42:1f1a:7d4f:4c55:216:3eff:fe46:7a85 (eth0) | PERSISTENT | 1 |
| | | 10.61.111.52 (eth0) | | | |
+-------------+---------+----------------------+-----------------------------------------------+------------+-----------+
| ubuntu1804 | STOPPED | | | PERSISTENT | 0 |
+-------------+---------+----------------------+-----------------------------------------------+------------+-----------+
Además, vemos que nos podemos conectar desde fuera del contenedor con ssh
athos@athos-Z97P-D3:~/$ ssh ubuntu@10.61.111.52
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@topical-koi:~$
Vamos a probar docker, para ello ejecutaremos:
ubuntu@topical-koi:~$ docker run hello-world
docker: Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"proc\\\" to rootfs \\\"/var/lib/docker/overlay2/d7f6e572823333a168cda30337b4acfb578ed9d2e5e058a77da05f87f7962e3d/merged\\\" at \\\"/proc\\\" caused \\\"permission denied\\\"\"": unknown.
ERRO[0000] error waiting for container: context canceled
Es posible que en vez de este error, nos de un error de permisos, que solucionaremos añadiendo a nuestro usuario al grupo docker
sudo usermod -aG docker $USER
más información aquí
Volviendo al error que nos ha dado, lo solucionaremos haciendo lo siguiente:
Primero pararemos nuestro contenedor lxc stop topical-koi
Despues pondremos security.nesting a true lxc config set topical-koi security.nesting true
Y pondremos security.nesting a true lxc config set topical-koi security.privileged true
Volvemos a iniciar el contenedor lxc start topical-koi
Nos conectamos por ssh al contenedor ssh ubuntu@10.61.111.52
Y probamos si esta vez docker funciona
ubuntu@topical-koi:~$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Bien!! ya estamos preparados para lanzar un contenedor docker con gitlab-ce
Podemos mirar la documentación de gitlab aquí
Vamos a probar a ejecutar la imagen según nos dicen en la página de la documentación:
sudo docker run --detach \
--hostname gitlab.example.com \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
Al ejecutarlo, vemos que nos da un error, diciendo que el puerto 22 está en uso, así que moficiamos un poco el comando docker y hacemos que el mapeo del puerto que dirige al 22 del contenedor sea otro, para que no haya conflicto con el ssh del contenedor (lxc)
Previamente limpiamos con docker system prune -a --volumes
sudo docker run --detach \
--hostname gitlab.example.com \
--publish 443:443 --publish 80:80 --publish 222:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
Como podemos ver, esta vez el contenedor se ha levantado
ubuntu@topical-koi:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aeeac3ecb436 gitlab/gitlab-ce:latest "/assets/wrapper" 21 seconds ago Up 19 seconds (health: starting) 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:222->22/tcp gitlab
Ahora, si accedemos a la ip del contenedor lxc (10.61.111.52) podremos ver el gitlab. El proceso de instalación, tarda un par de minutos, así que se paciente
Después de introducir la contraseña, podremos loguear como usuario root
Vamos a hacer la prueba de fuego, que es crear un repo, y hacer un push a el. Para esto, primero vamos a introducir nuestra clave ssh. Esto lo haremos en settings
-> ssh keys
Una vez hecho esto y creado el repo en nuestro gitlab, nos lo vamos a clonar. Hay que mencionar, que como hemos cambiado el puerto ssh del contenedor de gitlab, el comando varía un poco con respecto al que nos da gitlab al crear el repo.
Gitlab nos da el siguiente comando:
git clone git@10.61.111.23:root/prueba.git
Esto funcionaría si no hubiesemos cambiado el puerto, al haberlo hecho, tendremos que ejecutar este otro comando:
git clone ssh://git@10.61.111.23:222/root/prueba.git
git clone ssh://git@10.61.111.23:222/root/prueba.git
Cloning into 'prueba'...
warning: You appear to have cloned an empty repository.
Ahora creamos un archivo y lo pusheamos
echo "## Esto funciona" > readme.md
git add .
git commit -m "Primer commit"
[master 8c61654] Primer commit
1 file changed, 1 insertion(+)
create mode 100644 readme.md
git push origin master
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), 464 bytes | 464.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To ssh://10.61.111.23:222/root/prueba.git
* [new branch] master -> master
Y como podemos ver en nuestro gitlab, tenemos nuestro commit
Y con esto tenemos nuestro servidor gitlab montado.