D4.1 Τι είναι Docker Compose για Ansible Users 🐳

Docker Compose vs Individual Containers

┌─────────────────────────────────────────────────────────┐
│  Individual Containers (D3)                             │
│  ├── Ένα task ανά container                             │
│  ├── Πολύς κώδικας για πολλά containers                 │
│  └── Δύσκολο να διαχειριστείς dependencies              │
├─────────────────────────────────────────────────────────┤
│  Docker Compose                                         │
│  ├── Ένα αρχείο για ΟΛΑ τα containers                   │
│  ├── Αυτόματη διαχείριση dependencies                   │
│  ├── Built-in networking                                │
│  └── Εύκολο up/down ολόκληρου stack                     │
└─────────────────────────────────────────────────────────┘

📝 Αναλογία:

Individual containers ≈ Tasks σε playbook
Docker Compose        ≈ Role (ομαδοποιεί τα πάντα)

Η Δομή ενός docker-compose.yml

# docker-compose.yml — βασική δομή
version: "3.8"        # ← compose file format version

services:             # ← τα containers

  web:                # ← όνομα service
    image: nginx:alpine
    ports:
      - "80:80"
    depends_on:
      - app           # ← ξεκινά ΜΕΤΑ το app

  app:
    image: myapp:1.0
    environment:
      DB_HOST: db
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:              # ← named volumes
  db_data:

networks:             # ← custom networks (optional)
  frontend:
  backend:

docker_compose_v2 Module

tasks:

  # ── Βασικό deploy ─────────────────────────
  - name: Deploy stack
    community.docker.docker_compose_v2:
      project_src: /opt/myapp    # ← φάκελος με docker-compose.yml
      state:       present       # ← present=up, absent=down

  # ── Με pull πριν deploy ───────────────────
  - name: Deploy με pull
    community.docker.docker_compose_v2:
      project_src: /opt/myapp
      pull:        always        # ← always/missing/never
      state:       present

  # ── Με custom compose file ────────────────
  - name: Deploy με custom file
    community.docker.docker_compose_v2:
      project_src:   /opt/myapp
      files:
        - docker-compose.yml
        - docker-compose.prod.yml  # ← override file
      state: present

  # ── Remove stack ──────────────────────────
  - name: Remove stack
    community.docker.docker_compose_v2:
      project_src:    /opt/myapp
      state:          absent
      remove_volumes: true     # ← διέγραψε volumes

  # ── Με project name ───────────────────────
  - name: Deploy με custom project name
    community.docker.docker_compose_v2:
      project_src:  /opt/myapp
      project_name: myapp_prod  # ← default: folder name
      state:        present

  # ── Recreate services ─────────────────────
  - name: Force recreate
    community.docker.docker_compose_v2:
      project_src: /opt/myapp
      state:       present
      recreate:    always       # ← always/never/auto

Compose vs docker_container — Πότε τι

┌──────────────────────┬────────────────┬────────────────────┐
│ Χαρακτηριστικό       │ docker_        │ docker_            │
│                      │ container      │ compose_v2         │
├──────────────────────┼────────────────┼────────────────────┤
│ Ένα container        │ ✅ Ιδανικό     │ Overkill           │
│ Πολλά containers     │ Πολύς κώδικας  │ ✅ Ιδανικό         │
│ Dependencies         │ Χειροκίνητα    │ ✅ Αυτόματα        │
│ Dev workflow         │ ⚠️             │ ✅ docker compose  │
│ Production           │ ✅ Fine-grained│✅ Εύκολο           │
│ Flexibility          │ ✅ Μέγιστη     │ ⚠️ Compose syntax  │
│ Portability          │ ⚠️ Ansible     │ ✅ Παντού          │
└──────────────────────┴────────────────┴────────────────────┘

Compose File Versions

# Version 3.x — για Docker Swarm & standalone
version: "3.8"        # ← τελευταία 3.x

# Version 2.x — μόνο standalone
version: "2.4"

# Χωρίς version — νέο format (Docker Compose v2+)
# (απλά παραλείπεις το version field)
services:
  web:
    image: nginx

💡 Σύσταση: Χρησιμοποίησε version: "3.8" ή νεότερο για καλύτερη συμβατότητα.


Σημαντικά Compose Keywords

services:
  myservice:

    # ── Image ─────────────────────────────────
    image: nginx:1.25.3-alpine

    # ── Build (αντί για image) ────────────────
    build:
      context:    ./docker
      dockerfile: Dockerfile
      args:
        VERSION: "1.0"

    # ── Ports ─────────────────────────────────
    ports:
      - "80:80"
      - "127.0.0.1:8080:8080"  # ← bind σε specific IP

    # ── Environment ───────────────────────────
    environment:
      NODE_ENV: production
      PORT: 3000
    env_file:
      - .env                   # ← από αρχείο

    # ── Volumes ───────────────────────────────
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./config:/app/config:ro

    # ── Networks ──────────────────────────────
    networks:
      - frontend
      - backend

    # ── Dependencies ──────────────────────────
    depends_on:
      db:
        condition: service_healthy   # ← περίμενε health check

    # ── Health check ──────────────────────────
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout:  10s
      retries:  3

    # ── Restart ───────────────────────────────
    restart: unless-stopped

    # ── Resources ─────────────────────────────
    deploy:
      resources:
        limits:
          memory: 512m
          cpus:   "0.5"

    # ── Labels ────────────────────────────────
    labels:
      app:     myapp
      version: "1.0"

    # ── Logging ───────────────────────────────
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

depends_on — Σειρά εκκίνησης

services:

  nginx:
    image: nginx:alpine
    depends_on:
      app:
        condition: service_healthy   # ← περίμενε healthy

  app:
    image: myapp:1.0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started   # ← μόνο started

  db:
    image: postgres:15-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

  redis:
    image: redis:alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 3

# ── Σειρά εκκίνησης: ──────────────────────────
# 1. db    (ξεκινά πρώτο)
# 2. redis (ξεκινά πρώτο)
# 3. app   (αφού db + redis healthy)
# 4. nginx (αφού app healthy)

Ansible + Compose — Workflow

┌─────────────────────────────────────────────────────────┐
│  Ansible Controller                                     │
│  ├── docker-compose.yml.j2  (Jinja2 template)           │
│  ├── .env.j2                (secrets template)          │
│  └── playbook.yml                                       │
│                │                                        │
│                │ 1. Render templates                    │
│                │ 2. Copy to server                      │
│                ▼                                        │
│  ┌─────────────────────────────────────────────────┐    │
│  │  Server                                         │    │
│  │  /opt/myapp/                                    │    │
│  │  ├── docker-compose.yml  (rendered)             │    │
│  │  └── .env                (rendered, no_log)     │    │
│  │                │                                │    │
│  │                ▼                                │    │
│  │  docker_compose_v2: state=present               │    │
│  │                │                                │    │
│  │                ▼                                │    │
│  │  ┌──────────────────────────────────────────┐   │    │
│  │  │  Stack: myapp                            │   │    │
│  │  │  nginx → app → postgres → redis          │   │    │
│  │  └──────────────────────────────────────────┘   │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

Βασικό παράδειγμα

---
# playbooks/compose-basic.yml

- name: Basic Docker Compose Deploy
  hosts: "{{ target | default('all_managed') }}"
  become: true
  gather_facts: true

  vars:
    app_name:    myapp
    app_version: "1.0.0"
    app_dir:     "/opt/{{ app_name }}"

  tasks:

    # ── Δημιουργία directory ───────────────────
    - name: Create app directory
      ansible.builtin.file:
        path:  "{{ app_dir }}"
        state: directory
        mode:  '0755'

    # ── Deploy compose file ───────────────────
    - name: Deploy docker-compose.yml
      ansible.builtin.copy:
        dest:  "{{ app_dir }}/docker-compose.yml"
        mode:  '0644'
        content: |
          version: "3.8"

          services:
            web:
              image: nginx:1.25.3-alpine
              ports:
                - "80:80"
              restart: unless-stopped
              healthcheck:
                test:     ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost/"]
                interval: 30s
                timeout:  10s
                retries:  3

    # ── Deploy stack ──────────────────────────
    - name: Deploy stack
      community.docker.docker_compose_v2:
        project_src:  "{{ app_dir }}"
        project_name: "{{ app_name }}"
        state:        present
      register: compose_result

    # ── Status ────────────────────────────────
    - name: Stack status
      ansible.builtin.debug:
        msg:
          - "Stack: {{ app_name }}"
          - "Changed: {{ compose_result.changed }}"

    # ── Verify ────────────────────────────────
    - name: Verify deployment
      ansible.builtin.uri:
        url:         http://localhost
        status_code: 200
      retries:       5
      delay:         5
      register:      http_check
      ignore_errors: true

    - name: Result
      ansible.builtin.debug:
        msg: "HTTP: {{ '✅ OK' if not http_check.failed else '❌ Failed' }}"
# Εκτέλεση
ansible-playbook playbooks/compose-basic.yml \
    --limit nextcloud -v

Σύνοψη D4.1

Docker Compose για Ansible Users
├── Compose vs individual containers:
│   ├── Compose   → πολλά containers, dependencies
│   └── container → fine-grained control
│
├── docker_compose_v2 module:
│   ├── project_src → φάκελος με compose file
│   ├── state: present/absent
│   ├── pull: always/missing/never
│   └── recreate: always/auto/never
│
├── Compose keywords:
│   ├── services, volumes, networks
│   ├── depends_on + condition
│   ├── healthcheck
│   └── deploy.resources (limits)
│
└── Ansible + Compose workflow:
    ├── Template → render → copy → compose up
    └── Secrets με .env + no_log