Docker y Colima en macOS🔗

Docker🔗

Debido a un cambio en la licencia de Docker Desktop puede uno considerar la búsqueda de alternativas para su uso en macOS.

Una alternativa es el uso de:

  • Docker CLI: principal herramienta en la linea de comandos para la administración de imágenes y contenedores.

  • Docker Compose: plugin para Docker CLI, permite la ejecución de aplicaciones en multi-contenedores definidos en un archivo compose.yaml (o bien docker-compose.yaml) siguiendo The Compose Specification.

  • Docker Buildx: plugin para Docker CLI, permite la construcción de imágenes definidos en un archivo Containerfile (o bien Dockerfile) siguiendo The Dockerfile reference. Internamente hace uso del proyecto Moby BuildKit. Cabe mencionar que Moby BuildKit permite el uso de otros lenguajes-referencias para la definición de una imagen.

Sobre la máquina virtual

Los contenedores, en este contexto, son una tecnología propia del kernel Linux (namespaces y cgroups) por lo que su uso y ejecución es transparente en una distribución de Linux. En el caso de macOS y Windows es necesario el uso de una maquina virtual con una distribución de Linux para poder hacer uso de contenedores.

Colima🔗

Colima permite la ejecución de un container runtime (Docker Engine/CE o containerd) dentro de una máquina virtual. Internamente hace uso de Lima para ejecutar/administrar una maquina virtual y en ella ejecutar un container runtime, incluyendo la autoconfiguración de port forwarding y volume mounts en esta.

Para crear e iniciar una instancia de colima (o una maquina virtual), y en ella al container runtime:

[nihilipster@localhost:~]$ colima start --runtime docker --arch aarch64 --cpu 8 --disk 120 --memory 32 --save-config --profile vm-aarch64-docker
INFO[0000] starting colima [profile=vm-aarch64-docker]
INFO[0000] runtime: docker
INFO[0001] creating and starting ...                     context=vm
INFO[0001] downloading disk image ...                    context=vm
INFO[0026] provisioning ...                              context=docker
INFO[0027] starting ...                                  context=docker
INFO[0028] done

Sobre la instancia de colima

El nombre dado a la instancia de colima es mediante el argumento --profile, mediante el cual se puede tener distintos profiles con distintas características en cuanto a arquitectura de CPU (--arch), numero de CPUs (--cpu), tamaño en GBs de Disco Duro (--disk) y tamaño en GBs de Memoria RAM (--memory) disponibles a los contenedores que serán ejecutados en el container runtime.

Para listar la(s) instancia(s) de colima:

[nihilipster@localhost:~]$ colima list
PROFILE                  STATUS     ARCH       CPUS    MEMORY    DISK      RUNTIME    ADDRESS
vm-aarch64-docker        Running    aarch64    8       32GiB     120GiB    docker

Para ver el status de la instancia de colima:

[nihilipster@localhost:~]$ colima --profile vm-aarch64-docker status
INFO[0000] colima [profile=vm-aarch64-docker] is running using macOS Virtualization.Framework
INFO[0000] arch: aarch64
INFO[0000] runtime: docker
INFO[0000] mountType: sshfs
INFO[0000] socket: unix://$HOME/.config/colima/vm-aarch64-docker/docker.sock

Configuración de la instancia de colima

La configuración de la instancia de colima se encuentra en $XDG_CONFIG_HOME/colima/<profile>/colima.yml o $HOME/.config/colima/<profile>/colima.yml. En este caso sería $HOME/.config/colima/vm-aarch64-containerd/colima.yaml.

Para detener y/o iniciar una instancia de colima previamente creada y/o configurada:

[nihilipster@localhost:~]$ colima --profile vm-aarch64-docker stop
INFO[0000] stopping colima [profile=vm-aarch64-docker]
INFO[0000] stopping ...                                  context=docker
INFO[0000] stopping ...                                  context=vm
INFO[0001] done
[nihilipster@localhost:~]$ colima --profile vm-aarch64-docker start
INFO[0000] starting colima [profile=vm-aarch64-docker]
INFO[0000] runtime: docker
INFO[0001] starting ...                                  context=vm
INFO[0013] provisioning ...                              context=docker
INFO[0014] starting ...                                  context=docker
INFO[0015] done

Para acceder por SSH a la instancia de colima:

[nihilipster@localhost:~]$ colima --profile vm-aarch64-docker ssh
nihilipster@colima-vm-aarch64-docker:$HOME$ cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.1 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
nihilipster@colima-vm-aarch64-docker:$HOME$ exit
logout

Para ejecutar un comando por SSH dentro de la instancia de colima:

[nihilipster@localhost:~]$ colima --profile vm-aarch64-docker ssh -- cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.1 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

Docker CLI🔗

Para la comunicación de Docker CLI con el container runtime es necesario la configuración de un Docker context, el cual contiene la información necesaria para la administración de los recursos ocupados por el container runtime.

Para listar los contextos configurados en Docker CLI:

[nihilipster@localhost:~]$ docker context list
NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

El contexto por default de Docker y colima

Al momento de iniciar una instancia de colima se configurará a esta como un contexto en Docker CLI, siendo a su vez el contexto por default de Docker CLI, de tal manera que no es necesario hacer algo para que Docker CLI se comunique con el container runtime de la instancia de colima.

Si la instancia de colima se encuentra en ejecución se observará su presencia en la lista de contextos de Docker:

[nihilipster@localhost:~]$ docker context list
NAME                         DESCRIPTION                               DOCKER ENDPOINT                                                          ERROR
colima-vm-aarch64-docker *   colima [profile=vm-aarch64-docker]        unix://$HOME/.config/colima/vm-aarch64-docker/docker.sock
default                      Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

Sobre la variable de entorno $DOCKER_HOST

Otra manera de indicarle a Docker CLI la “instancia de Docker” a la cual conectarse es mediante la variable de entorno DOCKER_HOST, siendo en este caso: DOCKER_HOST="unix://$HOME/.config/colima/vm-aarch64-docker/docker.sock".

Para obtener información sobre la comunicación entre el “cliente” (Docker CLI) y el “servidor” (container runtime):

[nihilipster@localhost:~]$ docker info
Client:
 Version:    27.5.1
 Context:    colima-vm-aarch64-docker
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  0.19.3
    Path:     /opt/local/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  2.32.1
    Path:     /opt/local/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 Server Version: 27.4.0
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 88bf19b2105c8b17560993bee28a01ddc2f97182
 runc version: v1.2.2-0-g7cb3632
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.8.0-50-generic
 Operating System: Ubuntu 24.04.1 LTS
 OSType: linux
 Architecture: aarch64
 CPUs: 8
 Total Memory: 31.28GiB
 Name: colima-vm-aarch64-docker
 ID: ade88927-1797-47c2-b328-c38c8c56b4ba
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

Imágenes y contenedores🔗

Para obtener una imagen desde algun container registry, por ejemplo la imagen oficial ‘hello-world’ en el Docker Hub:

[nihilipster@localhost:~]$ docker image pull hello-world:latest
Using default tag: latest
latest: Pulling from library/hello-world
c9c5fd25a1bd: Pull complete
Digest: sha256:d715f14f9eca81473d9112df50457893aa4d099adeb4729f679006bf5ea12407
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest

Para listar las imagenes locales:

[nihilipster@localhost:~]$ docker images
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
hello-world   latest    f1f77a0f96b7   3 weeks ago   5.2kB

Para la ejecución de un contenedor, lo cual implica el uso de una imagen para la creación de dicho contenedor:

[nihilipster@localhost:~]$ 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.
    (arm64v8)
 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/

Para listar los contenedores creados localmente:

[nihilipster@localhost:~]$ docker container list --all
CONTAINER ID   IMAGE         COMMAND    CREATED      STATUS                  PORTS     NAMES
b2fe5fa59b77   hello-world   "/hello"   5 days ago   Exited (0) 5 days ago             interesting_burnell

Para crear un contenedor, en base a una imagen ya existente localmente, y ejecutarlo:

[nihilipster@localhost:~]$ docker container list --all
CONTAINER ID   IMAGE         COMMAND    CREATED      STATUS                  PORTS     NAMES
b2fe5fa59b77   hello-world   "/hello"   5 days ago   Exited (0) 5 days ago             interesting_burnell
[nihilipster@localhost:~]$ docker container create --name hola-mundo --tty --interactive hello-world:latest
845edb0399be0711e4011c212e35881f96e26873657df426cbdf17b9a1d8afc7
[nihilipster@localhost:~]$ docker container list --all
CONTAINER ID   IMAGE                COMMAND    CREATED         STATUS                  PORTS     NAMES
845edb0399be   hello-world:latest   "/hello"   4 seconds ago   Created                           hola-mundo
b2fe5fa59b77   hello-world          "/hello"   5 days ago      Exited (0) 5 days ago             interesting_burnell
[nihilipster@localhost:~]$ docker container start --attach --interactive hola-mundo

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.
    (arm64v8)
 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/

Docker Buildx🔗

Docker Buildx extiende las características de docker build, haciendo uso del proyecto Moby BuildKit, mediante el comando docker buildx.

docker build vs docker buildx

A partir de la versión 23.0 de Docker Desktop, Moby BuildKit es quien realmente es ejecutado con el comando docker build por lo que de estar haciendo uso de Docker Desktop >= 23.0 ya no sería necesario activar el uso de Moby BuildKit (mediante la variable de entorno DOCKER_BUILDKIT=1). Cabe mencionar que ademas los comandos docker build y docker buildx build tendrían el mismo comportamiento.

A manera de ejemplo se puede obtener la definición de la imagen de un contenedor de Postgres ubicada en el archivo https://github.com/docker-library/postgres/blob/master/17/bookworm/Dockerfile, el cual establece la configuración del sistema operativo Debian Bookworm, la instalación de paquetes necesarios para Postgres y el inicio de una instancia de Postgres.

Se obtiene el repositorio https://github.com/docker-library/postgres de manera local:

[nihilipster@localhost:~]$ git clone https://github.com/docker-library/postgres
Cloning into 'postgres'...
remote: Enumerating objects: 6949, done.
remote: Counting objects: 100% (152/152), done.
remote: Compressing objects: 100% (76/76), done.
remote: Total 6949 (delta 91), reused 96 (delta 76), pack-reused 6797 (from 4)
Receiving objects: 100% (6949/6949), 1.05 MiB | 3.03 MiB/s, done.
Resolving deltas: 100% (3067/3067), done.

Es posible analizar el contenido de la carpeta que contiene la definición de la imagen (postgres/17/bookworm/Dockerfile):

[nihilipster@localhost:~]$ ls postgres/17/bookworm
Dockerfile  docker-ensure-initdb.sh  docker-entrypoint.sh

Para construir la imagen en base a la definición:

[nihilipster@localhost:~/postgres/17/bookworm]$ docker buildx build --tag postgres-17.2-debian-bookworm:17.2 --file postgres/17/bookworm/Dockerfile postgres/17/bookworm/
[+] Building 49.7s (19/19) FINISHED                                                                                  docker:colima-vm-aarch64-docker
 => [internal] load build definition from Dockerfile                                                                                            0.0s
 => => transferring dockerfile: 9.86kB                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/debian:bookworm-slim                                                                         1.2s
 => [internal] load .dockerignore                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                 0.0s
 => [ 1/14] FROM docker.io/library/debian:bookworm-slim@sha256:40b107342c492725bc7aacbe93a49945445191ae364184a6d24fedb28172f6f7                 2.1s
 => => resolve docker.io/library/debian:bookworm-slim@sha256:40b107342c492725bc7aacbe93a49945445191ae364184a6d24fedb28172f6f7                   0.0s
 => => sha256:40b107342c492725bc7aacbe93a49945445191ae364184a6d24fedb28172f6f7 8.56kB / 8.56kB                                                  0.0s
 => => sha256:435e86e9f078a90c245ec35c494c44b4e9e8f53d0f8462ec3ad99f0ec6054f3d 1.04kB / 1.04kB                                                  0.0s
 => => sha256:c0ab4e530f590d58f74eae4612d0baaf70ac2981b05eeb03c0786ae7b02288ab 468B / 468B                                                      0.0s
 => => sha256:4d2547c084994a809c138e688fbe4ee14eedbc6e2defc5b1c680edd16e291473 28.04MB / 28.04MB                                                1.3s
 => => extracting sha256:4d2547c084994a809c138e688fbe4ee14eedbc6e2defc5b1c680edd16e291473                                                       0.8s
 => [internal] load build context                                                                                                               0.0s
 => => transferring context: 15.13kB                                                                                                            0.0s
 => [ 2/14] RUN set -eux;  groupadd -r postgres --gid=999;  useradd -r -g postgres --uid=999 --home-dir=/var/lib/postgresql --shell=/bin/bash   0.2s
 => [ 3/14] RUN set -ex;  apt-get update;  apt-get install -y --no-install-recommends   gnupg   less  ;  rm -rf /var/lib/apt/lists/*            4.1s
 => [ 4/14] RUN set -eux;  savedAptMark="$(apt-mark showmanual)";  apt-get update;  apt-get install -y --no-install-recommends ca-certificates  8.0s
 => [ 5/14] RUN set -eux;  if [ -f /etc/dpkg/dpkg.cfg.d/docker ]; then   grep -q '/usr/share/locale' /etc/dpkg/dpkg.cfg.d/docker;   sed -ri '/  4.1s
 => [ 6/14] RUN set -eux;  apt-get update;  apt-get install -y --no-install-recommends   libnss-wrapper   xz-utils   zstd  ;  rm -rf /var/lib/  3.3s
 => [ 7/14] RUN mkdir /docker-entrypoint-initdb.d                                                                                               0.1s
 => [ 8/14] RUN set -ex;  key='B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8';  export GNUPGHOME="$(mktemp -d)";  mkdir -p /usr/local/share/keyring  0.6s
 => [ 9/14] RUN set -ex;   export PYTHONDONTWRITEBYTECODE=1;   dpkgArch="$(dpkg --print-architecture)";  aptRepo="[ signed-by=/usr/local/shar  25.0s
 => [10/14] RUN set -eux;  dpkg-divert --add --rename --divert "/usr/share/postgresql/postgresql.conf.sample.dpkg" "/usr/share/postgresql/17/p  0.1s
 => [11/14] RUN install --verbose --directory --owner postgres --group postgres --mode 3777 /var/run/postgresql                                 0.1s
 => [12/14] RUN install --verbose --directory --owner postgres --group postgres --mode 1777 "/var/lib/postgresql/data"                          0.1s
 => [13/14] COPY docker-entrypoint.sh docker-ensure-initdb.sh /usr/local/bin/                                                                   0.0s
 => [14/14] RUN ln -sT docker-ensure-initdb.sh /usr/local/bin/docker-enforce-initdb.sh                                                          0.1s
 => exporting to image                                                                                                                          0.5s
 => => exporting layers                                                                                                                         0.5s
 => => writing image sha256:1aee1b100a039cb7863d564d5fe0b1804a55b2bbaa3bc19525b4c85aaad81faf                                                    0.0s
 => => naming to docker.io/library/postgres-17.2-debian-bookworm:17.2

Para listar la nueva imagen creada:

[nihilipster@localhost:~]$ docker images -a
REPOSITORY                      TAG       IMAGE ID       CREATED          SIZE
postgres-17.2-debian-bookworm   17.2      1aee1b100a03   25 minutes ago   458MB

Una vez creada la imagen se pueden crear tanto contenedores se quieran.

Para borrar la imagen:

[nihilipster@localhost:~]$ docker image rm postgres-17.2-debian-bookworm:17.2
Untagged: postgres-17.2-debian-bookworm:17.2
Deleted: sha256:1aee1b100a039cb7863d564d5fe0b1804a55b2bbaa3bc19525b4c85aaad81faf

Docker Compose🔗

A manera de ejemplo se crearán los servicios de Postgres (como sistema gestor de base de datos) y pgAdmin (como aplicación web).

Para obtener la definición de los servicios desde el repositorio https://github.com/docker/awesome-compose:

[nihilipster@localhost:~]$ git clone https://github.com/docker/awesome-compose
Cloning into 'awesome-compose'...
remote: Enumerating objects: 2757, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 2757 (delta 0), reused 0 (delta 0), pack-reused 2753 (from 2)
Receiving objects: 100% (2757/2757), 7.70 MiB | 10.12 MiB/s, done.
Resolving deltas: 100% (1329/1329), done.

Creando los servicios mediante docker compose:

[nihilipster@localhost:~]$ docker compose --env-file awesome-compose/postgresql-pgadmin/.env --file awesome-compose/postgresql-pgadmin/compose.yaml up
[+] Running 32/32
 ✔ pgadmin Pulled                                                                                                                                     11.1s
   ...
 ✔ postgres Pulled                                                                                                                                    15.5s
   ...
[+] Running 3/3
 ✔ Network postgresql-pgadmin_default  Created                                                                                                         0.1s
 ✔ Container pgadmin                   Created                                                                                                         0.5s
 ✔ Container postgres                  Created                                                                                                         0.5s
Attaching to pgadmin, postgres
...
pgadmin   | pgAdmin 4 - Application Initialisation
pgadmin   | ======================================
pgadmin   |
pgadmin   | postfix/postlog: starting the Postfix mail system
pgadmin   | [2025-02-13 05:50:48 +0000] [1] [INFO] Starting gunicorn 22.0.0
pgadmin   | [2025-02-13 05:50:48 +0000] [1] [INFO] Listening at: http://[::]:80 (1)
pgadmin   | [2025-02-13 05:50:48 +0000] [1] [INFO] Using worker: gthread
pgadmin   | [2025-02-13 05:50:48 +0000] [118] [INFO] Booting worker with pid: 118
...
Gracefully stopping... (press Ctrl+C again to force)
[+] Stopping 2/2
 ✔ Container pgadmin   Stopped                                                                                                                         0.5s
 ✔ Container postgres  Stopped                                                                                                                         0.1s˜˜˜˜

Accediento a pgAdmin disponible en :

pgadmin1.png

El ususuario y la contraseña fueron definidos por las variables de entorno PGADMIN_MAIL=your@email.com y PGADMIN_PW=changeit en el archivo awesome-compose/postgresql-pgadmin/.env.

pgadmin2.png

Sobre el parametro --env-file

Mediante --env-file podemos indicar las variables de entorno disponibles a los servicios. En este caso el archivo awesome-compose/postgresql-pgadmin/.env establece variables para la creación de la base de datos en Postgres y la autenticación en pgAdmin.

Tambien es posible conectar algún otro cliente de Postgres, en este caso DBeaver, teniendo en cuenta las variables de entorno POSTGRES_USER=yourUser, POSTGRES_PW=changeit y POSTGRES_DB=postgres definidas en el archivo awesome-compose/postgresql-pgadmin/.env.

dbeaver.png