Nedávno som narazil na zdanlivo ľahko riešiteľný problém, ktorý sa v konečnom dôsledku ukázal trochu zložitejším. Predstavte si aplikáciu, ktorá sa pripája k databáze. Nič zvláštne. Súbor docker-compose.yml by mohol vyzerať napríklad takto:

version: '3.2'
services:
  db:
  image: postgresql:12
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_NON_ROOT_USER: ${POSTGRES_USER}
      POSTGRES_NON_ROOT_USER_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ${STORAGE_PATH}/${INSTANCE_NAME}/db:/var/lib/postgresql/data
  app:
  image: myapp:1
    depends_on:
        - db
    environment:
      INSTANCE_NAME: ${INSTANCE_NAME}
      DB_HOST: db
      DB_NAME: ${POSTGRES_DB}
      DB_USER: ${POSTGRES_USER}
      DB_PASSWORD: ${POSTGRES_PASSWORD}
    links:
      - db:db
    volumes:
      - ${STORAGE_PATH}/${INSTANCE_NAME}/data:/srv/web/data
    ports:
      - "${INSTANCE_PORT}:8080"

Vidíme, že kontajner app beží vedľa kontajnera db. Po spustení služby príkazom docker-compose up -d chceme overiť, že oba kontajnery bežia a aké porty vystavujú (alebo v prípade databázy skrývajú). Jedným zo spôsobov je využiť argument --format príkazu docker ps nasledovne:

docker ps --format "table {{.Names}}\t{{.Ports}}"

Výsledok tohto príkazu by mohol vyzerať takto:

NAMES      PORTS
app_1      0.0.0.0:8080->8080/tcp
db_1       5432/tcp

Pridávanie ďalších daemonov #

Jednou z výhod ekosystému Docker je možnosť horizontálneho škálovania služieb. Ak by sme teda chceli spustiť ďalšiu inštanciu služby na rovnakom hostiteľovi, mohli by sme jednoducho skopírovať adresár služby a upraviť premenné v súbore .env. Dve premenné, ktoré musia byť zmenené, sú INSTANCE_NAME a predovšetkým INSTANCE_PORT. Zmena portu je potrebná, pretože inak by sa kontajnery pokúšali naviazať na rovnaký port, čo očividne nechceme.

Tento daemon môže byť spustený rovnakým spôsobom ako predchádzajúci. Na sledovanie bežiacich kontajnerov trochu upravíme príkaz docker ps, aby sme rozlíšili kontajnery patriace rôznym službám:

docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"

Výstup by mohol byť podobný tomuto:

CONTAINER ID        NAMES      PORTS
0cd27fa1c363        app_1      0.0.0.0:8080->8080/tcp
bd2832c8f6fc        app_1      0.0.0.0:8081->8080/tcp
057a4002ce66        db_1       5432/tcp
347a51256c16        db_1       5432/tcp

Všimnite si, že aplikácie sú naviazané na porty 8080 a 8081 na hostiteľskom systéme. V tomto prístupe je jeden veľký problém. Okrem ID kontajnera totiž, v závislosti od ostatných aspektov konfigurácie kontajnera, nemusí byť nič ľahko dostupné v celom výstupe docker ps, čo by nám skutočne pomohlo rozlíšiť ľudsky čitateľnou formou, o čom sú tie kontajnery.

Labels prichádzajú na pomoc! #

Jedným zo spôsobov riešenia tohto problému je použitie takzvaných labels. Labels umožňujú nastaviť metadáta väčšine Docker objektov, vrátane:

  • Images
  • Containers
  • Local daemons
  • Volumes
  • Networks
  • Swarm nodes
  • Swarm services

Podľa dokumentácie možno labels použiť na organizáciu images, zaznamenávanie informácií o licenciách, anotovanie vzťahov medzi kontajnermi, volumes a sieťami, alebo akýmkoľvek spôsobom, ktorý má zmysel pre vašu firmu alebo aplikáciu.

Label môžete ľahko zadať za behu, ale pre jeho trvalejšie zachovanie ho možno pridať do Dockerfile cez inštrukciu LABEL:

LABEL instance=red # or instance=blue

Po priradení labels a upravení parametra formátu tak, aby zahŕňal labels (ktoré sú predvolene skryté):

docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}\t{{.Labels}}"

Výstup môže vyzerať takto:

CONTAINER ID        NAMES      PORTS                      LABELS
0cd27fa1c363        app_1      0.0.0.0:8080->8080/tcp     instance=red
bd2832c8f6fc        app_1      0.0.0.0:8081->8080/tcp     instance=blue
057a4002ce66        db_1       5432/tcp
347a51256c16        db_1       5432/tcp

Toto je pekný prístup, keď je Dockerfile pod našou kontrolou, ale to vždy neplatí. Tvrdím, že toto je skôr výnimka ako norma.

Dockerfile nie je môj vlastný #

Tento problém bol vyriešený vo verzii Compose file 3.3. Detaily nájdete v jej dokumentácii. Pridanie labels do Compose súboru je pohodlnejšie, keď je vám služba distribuovaná cez neho.

Pre úplnosť, upravený Compose súbor by mohol vyzerať takto:

version: '3.3' # version bumped to 3.3 or higher
services:
  db:
  image: postgresql:12
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_NON_ROOT_USER: ${POSTGRES_USER}
      POSTGRES_NON_ROOT_USER_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ${STORAGE_PATH}/${INSTANCE_NAME}/db:/var/lib/postgresql/data
  app:
  image: myapp:1
    depends_on:
        - db
    environment:
      INSTANCE_NAME: ${INSTANCE_NAME}
      DB_HOST: db
      DB_NAME: ${POSTGRES_DB}
      DB_USER: ${POSTGRES_USER}
      DB_PASSWORD: ${POSTGRES_PASSWORD}
    links:
      - db:db
    volumes:
      - ${STORAGE_PATH}/${INSTANCE_NAME}/data:/srv/web/data
    ports:
      - "${INSTANCE_PORT}:8080"
    labels: # labels in Compose file instead of Dockerfile
      - "instance-name": ${INSTANCE_NAME}

Takto je pri zobrazení labels cez príkaz docker ps viditeľný aj názov inštancie.