Logo docker

En este apunte describo como instalar Alpine Linux en una máquina virtual en mi servidor QEMU/KVM y cómo instalar Docker en ella. Necesitaba, para pruebas de concepto y servicios caseros, poder instalar contenedores sobre un servidor con Docker que ocupase “poquísimo”. ¿Se puede instalar un Host Docker encima de una Máquina Virtual?. La respuesta es un sí rotundo, de hecho es un lugar excelente para hacerlo, sobre todo en entornos de laboratorio, caseros, pequeños despliegues.


Introducción

Necesitaba montar microservicios encima de Docker (hacía tiempo que no (jugaba con Docker) dudé entre añadir Docker a mi servidor donde tengo KVM, dedicar un PC antiguo a Docker o pensar en algo más creativo…

Al final decidí optar por la tercera opción: montar máquinas virtuales dedicadas a contenedores Docker corriendo en mi servidor KVM. Mi potente y pequeño servidor Meerkat de System76 es donde tengo todas mis VM’s, pues bien, algunas de ellas van a soportar microservicio encima de Docker.

La segunda opción (añadir Docker a mi servidor donde tengo KVM) hubiese sido un infierno con el networking (openvswitch + docker switches + iptables), así que opté por aislar y contener problemas/troubleshooting de Docker en VM’s dedicadas. Así que mi servidor de VM’s queda como sigue:

  • Hardware: Meerkat de System76
  • Software: Pop!_OS, Ubuntu Server LTS.
  • Networking: Open vSwtich
  • QEMU/KVM con Hypervisor
  • Varios Guest’s con máquinas virtuales corriendo Linux (Ubuntu Server LTS) y servicios.
  • Varios Guest’s appliances como Umbrella o vWLC de Cisco.
  • Varios Guest’s con máquinas virtuales corriendo Alpine Linux con Docker y contenedores para servicios (git, nodered, …).


¿Donde ejecutar Docker?

Lo primero que necesitaba decidir es sobre qué SO iba a montar Docker, teniendo en cuenta que:

  • Voy a correr Docker dentro de una Máquina Virtual en mi Servidor QEMU/KVM.
  • El Sistema Operativo Guest solo va a tener Docker, no necesito una distribución enorme.
  • Busco algo pequeño, fácil de mantener y robusto

Entre las diferentes opciones que he visto por ahí he optado por Alpine Linux


Máquina virtual con Alpine Linux

Veamos un ejemplo creando una una VM l

  • Descargo Alpine Linux desde Downloads > VIRTUAL > Slimmed down kernel. Optimized for virtual systems, x86_64 (solo 52MB), es la versión más compacta posible.
    luis@sol:~/kvm/base$ wget https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/x86_64/alpine-virt-3.15.3-x86_64.iso
    luis@sol:~/kvm/base$ wget https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/x86_64/alpine-virt-3.15.3-x86_64.iso.sha256
    luis@sol:~/kvm/base$ sha256sum -c alpine-virt-3.15.3-x86_64.iso.sha256
    alpine-virt-3.15.3-x86_64.iso: La suma coincide
    
  • Creo un puerto estático en mi switch virtual (más info aquí: Open vSwitch y KVM).
    luis@sol:~/kvm/base$ sudo ovs-vsctl list-br
    solbr
    luis@sol:~/kvm/base$ sudo ovs-vsctl list-ports solbr
    eth0
    :
    v100vnet12  (miro cual es el último que tenía dado de alta)
    :
    luis@sol:~$ sudo ovs-vsctl add-port solbr v100vnet13 tag=100 -- set Interface v100vnet13 type=internal
    
  • Creo el directorio donde ubicaré el fichero de la máquina virtual
    luis@sol:~/kvm$ mkdir docker
    
  • Creo una máquina virtual desde virt-manager con 1GB de RAM, 1 CPU, disco de 4GB y una NIC virtio, usando la imagen: alpine-virt-3.15.3-x86_64.iso, la llamo docker.tudominio.com y en la configuración de red uso el interfaz que acabo de crear v100vnet13.
    luis@sol:~$ virt-manager
    
Creo VM desde virt-manager
  • Arranco la VM y entro en el setup de Alpine (más info en esta guía).
    luis@sol:~/kvm/gitea-traefik-docker$ virsh console docker.tudominio.com
    localhost login: root
    Welcome to Alpine!
    :
    localhost:~#
    :
    # export SWAP_SIZE=0
    # setup-alpine
    Select keyboard layout: [none] es
    Select variant (or 'abort'): es
    Enter system hostname (fully qualified form, e.g. 'foo.example.org') [localhost] docker
    Available interfaces are: eth0.
    Which one do you want to initialize? (or '?' or 'done') [eth0]
    Ip address for eth0? (or 'dhcp', 'none', '?') [dhcp] 192.168.100.225/24
    Gateway? (or 'none') [none] 192.168.100.1
    Do you want to do any manual network configuration? (y/n) [n] n
    DNS domain name? (e.g 'bar.com') tudominio.com
    DNS nameserver(s)? 192.168.100.224
    Changing password for root
    Which timezone are you in? ('?' for list) [UTC] Europe/Madrid
    HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none') [none]
    Enter mirror number (1-71) or URL to add (or r/f/e/done) [1]
    Which SSH server? ('openssh', 'dropbear' or 'none') [openssh]
    Which disk(s) would you like to use? (or '?' for help or 'none') [none] vda
    How would you like to use it? ('sys', 'data', 'crypt', 'lvm' or '?' for help) [?] sys
    WARNING: Erase the above disk(s) and continue? (y/n) [n] y
    Installation is complete. Please reboot.
    docker:~# reboot
    
  • Hago login como root e instalo unas cuantas herramientas útiles.
    docker:~# apk add iproute2 nano tzdata
    docker:~# cp /usr/share/zoneinfo/Europe/Madrid /etc/localtime
    docker:~# echo "Europe/Madrid" >  /etc/timezone
    docker:~# apk del tzdata
    
  • Creo mi usuario luis y configuro ssh
    docker:~# addgroup -g 1000 luis
    docker:~# adduser -h /home/luis -s /bin/ash -G luis --u 1000 luis
    docker:~# adduser luis wheel
    docker:~# su - luis
    docker:~$
    docker:~$ ssh-keygen -t rsa -b 2048 -C "luis@docker.tudominio.com"
    :
    docker:~$ exit
    
  • Creo authorized_keys (apunte sobre SSH en linux)
  • Modifico SSH para que trabaje solo con clave pública/privada
    docker:~$ su -
    Password:
    docker:~# cat /etc/ssh/sshd_config
    # Config LuisPa
    Port 22
    PubkeyAuthentication yes
    PasswordAuthentication no
    AuthenticationMethods publickey
    AllowAgentForwarding yes
    AllowTcpForwarding yes
    GatewayPorts yes
    AddressFamily inet
    PrintMotd no
    Subsystem sftp /usr/lib64/misc/sftp-server
    AcceptEnv LANG LC_*
    docker:~# service sshd restart
    
  • Creo el fichero /etc/nanorc (fuente aquí) para el editor nano
  • Acelero el tiempo de boot a unos 5 segundos
    docker:~# cat /boot/extlinux.conf
    # Generated by update-extlinux 6.04_pre1-r9
    #DEFAULT menu.c32                        # Comento esta línea
    DEFAULT virt                             # Añadida, virt = nombre más abajo
    PROMPT 0
    MENU TITLE Alpine/Linux Boot Menu
    MENU HIDDEN
    MENU AUTOBOOT Alpine will be booted automatically in # seconds.
    TIMEOUT 30
    LABEL virt
    MENU LABEL Linux virt
    LINUX vmlinuz-virt
    INITRD initramfs-virt
    APPEND root=UUID=bff03f67-29ee-4525-96d9-3096a1799fc7 modules=sd-mod,usb-storage,ext4 quiet rootfstype=ext4
    MENU SEPARATOR
    


Instalación de Docker y Docker Compose

  • Habilito el Community repository
    git:~# cat /etc/apk/repositories
    #/media/cdrom/apks
    http://dl-cdn.alpinelinux.org/alpine/v3.15/main
    http://dl-cdn.alpinelinux.org/alpine/v3.15/community   <== Descomento esta línea
    
  • Añado la siguiente línea al fichero /etc/sudoers
    # User rules for luis
    luis ALL=(ALL) NOPASSWD:ALL
    
  • Creo un par de scripts de apoyo
    nodered:~# cat > /usr/bin/e
    #!/bin/ash
    /usr/bin/nano "${*}"
    nodered:~# chmod 755 /usr/bin/e
    :
    nodered:~# cat > /usr/bin/confcat
    #!/bin/ash
    # By LuisPa 1998
    # confcat: quita las lineas con comentarios, muy util como sustituto
    # a "cat" para ver contenido sin los comentarios.
    #
    grep -vh '^[[:space:]]*#' "$@" | grep -v '^//' | grep -v '^;' | grep -v '^$' | grep -v '^!' | grep -v '^--'
    nodered:~# chmod 755 /usr/bin/confcat
    :
    nodered:~# cat > /usr/bin/s
    #!/bin/ash
    /usr/bin/sudo -i
    nodered:~# chmod 755 /usr/bin/s
    
  • Actualizo el sistema e instalo herramientas muy útiles además de docker y docker-compose
    docker:~# apk update
    docker:~# apk upgrade --available
    docker:~# apk add bash-completion procps util-linux
    docker:~# apk add readline findutils sed coreutils sudo
    docker:~# apk add docker docker-bash-completion docker-compose docker-compose-bash-completion docker-cli-compose
    docker:~# rc-update add docker boot
    docker:~# service docker start
    
  • Añado mi usuario al grupo docker y hago un reboot…
    git:~# addgroup luis docker
    git:~# reboot -f
    
  • Pruebo Docker (con la última imagen de alpine, que ocupa poquísimo…)
    docker:~$ docker pull alpine:latest
    docker:~$ docker images
    REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
    alpine       latest    76c8fb57b6fc   3 days ago   5.57MB
    docker:~$ docker create -t -i  --name myalpine alpine:latest
    5f1fefa539848f9e0fe995bf2e9c426def69ca48bfacc51bdb509197939c041e
    docker:~$ docker start myalpine
    / # exit
    docker:~$ docker exec -it myalpine /bin/ash
    docker:~$ docker stop myalpine
    docker:~$ docker rm myalpine
    
  • Que no te sorprenda que docker stop myalpinetarde un rato en pararse, aquí tienes la explicacion.