# Deploy Docker Compose

Skill cha: [tdshift-paas](https://app.tdshift.info/skill) · Cần: project loại `compose` ở trạng thái `ready` ([tao-project](https://app.tdshift.info/skill/tao-project.md))

PaaS **không build image**. Mọi service phải dùng image đã build và push sẵn (Docker Hub, hoặc GitLab CI/CD push lên registry, xem [ci-cd-gitlab](https://app.tdshift.info/skill/ci-cd-gitlab.md)). PaaS chỉ pull image và chạy.

Ứng dụng của người dùng (có mã nguồn riêng) phải dùng **image riêng** build từ repo của họ; image chung chỉ cho service phụ như database, cache. Cách build và cấu hình: [build-image-rieng](https://app.tdshift.info/skill/build-image-rieng.md).

## Deploy trên giao diện

Trên trang project, khu **Docker Compose Editor**:

1. Dán nội dung `docker-compose.yml`, hoặc bấm **Chọn Docker Compose template** / **Xem tất cả templates ↗** để điền mẫu có sẵn (Odoo, WordPress và các template do admin thêm).
2. Điền **Public web service** (tên service nhận traffic web), **Internal HTTP port** (cổng service đó lắng nghe bên trong container), **Health path** (đường dẫn kiểm tra, bắt đầu bằng `/`, ví dụ `/` hoặc `/wp-login.php`).
3. Bấm **Deploy Compose Stack ↗**. Hiện "Đang pull và deploy toàn bộ Docker Compose stack.", trạng thái `updating`; trang tự làm mới.
4. Xong: trạng thái về `ready`. Thành công khi **Services trong project** liệt kê các service và không có thông báo lỗi. Thất bại: lỗi hiện trên trang, cấu hình cũ (nếu có) được khôi phục.

Nội dung đã gửi được lưu làm bản nháp và hiện lại trong editor lần sau, kể cả khi deploy lỗi.

Preset WordPress mở ra trang cài đặt WordPress lần đầu (`/wp-admin/install.php`): người dùng tự hoàn tất cài đặt. Preset Odoo mở trang tạo database của Odoo.

## Quy tắc Compose được chấp nhận

Bị từ chối thì trang báo "Compose không hợp lệ: <lý do>" và không có container nào được tạo.

- Cấp cao nhất chỉ được: `services`, `volumes`, `networks`, `name`. Không `include`, `extends`, `secrets`, `configs`.
- 1–12 services. Tên service, volume, network: `^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$`.
- Mỗi service **bắt buộc** `image:` (tag hoặc `@sha256:<64 hex>`). `build:` bị bỏ qua.
- Tuỳ chọn service được phép: `image, environment, restart, depends_on, volumes, networks, healthcheck, command, entrypoint, working_dir, user, stop_grace_period, init`. **Không** được `ports`, `privileged`, `container_name`, `env_file`, `cap_add`, `devices`, `network_mode`, `labels`...
- `volumes` chỉ dùng named volume dạng long syntax, khai báo ở mục `volumes` cấp cao nhất, driver `local`. Không bind mount (`./data:/data`, `/etc:/x`):
  ```yaml
  volumes:
    - {type: volume, source: app_data, target: /data}   # read_only: true là tuỳ chọn
  ```
- `environment` là map `KEY: value` (không dùng dạng danh sách `- KEY=value`). Giá trị bí mật nên để `${TEN_BIEN}` và khai báo trong Environment Variables.
- `networks` chỉ là bridge khai báo trong file; `depends_on` trỏ tới service cùng file.
- Không cần mở port: PaaS tự route domain của project tới **Public web service** qua Traefik.

Ví dụ tối thiểu:

```yaml
services:
  web:
    image: nginx:stable-alpine
    restart: unless-stopped
    volumes:
      - {type: volume, source: html, target: /usr/share/nginx/html}
volumes:
  html: {}
```
→ Public web service `web`, Internal HTTP port `80`, Health path `/`.

## Biến môi trường

Khu **Environment Variables**: bảng các biến đã lưu (biến có `PASSWORD`, `SECRET`, `TOKEN`, `API_KEY`, `AUTH`, `DATABASE_URL`… bị che). Nhập vào **Thêm hoặc cập nhật biến** mỗi dòng một `KEY=value` (tối đa 8000 ký tự) → **Lưu Environment Variables**. Biến không nhập giữ nguyên. Không được đặt `COMPOSE_FILE`, `COMPOSE_PROJECT_NAME`, `DOCKER_HOST`, `DOCKER_CONTEXT`.

Giá trị mới **chỉ áp dụng sau khi deploy lại** (bấm **Deploy Compose Stack ↗** lần nữa). Đổi mật khẩu database trong biến sau khi database đã khởi tạo không đổi mật khẩu thật của database.

## Registry riêng (image private)

Khu **Private Container Registry**: nhập **Registry Host** (chỉ hostname, ví dụ `registry.gitlab.com`), **Username**, **Access Token** (quyền `read_registry`) → **Lưu Registry**. Token được mã hoá và không hiển thị lại. **Xóa Registry credentials** để gỡ.

## Logs

Khu **Logs & Diagnostics**: log quá trình deploy, hoặc **Xem service logs** / **Logs theo service** để xem log container. Dạng text thuần: `https://app.tdshift.info/projects/<uuid>/logs/raw?type=deployment` hoặc `?type=containers&service=<tên>` (cần phiên đăng nhập). Secret trong log đã được che.

## Qua Agent API

Agent API lưu Compose (kèm web service, port, health path), đổi image, rồi deploy bằng `POST /api/v1/agent/projects/<UUID>/deploy`. Lưu xong mà chưa gọi deploy thì editor trên trang project đã có sẵn nội dung và các ô **Public web service**, **Internal HTTP port**, **Health path** đúng giá trị đã lưu. Chi tiết: [agent-api](https://app.tdshift.info/skill/agent-api.md).
