Compare commits
7
Commits
8da028ef5a
...
1aff50b74e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1aff50b74e | ||
|
|
7f9758a924 | ||
|
|
05c933e2e0 | ||
|
|
eba541a2e2 | ||
|
|
8208f3f779 | ||
|
|
af2fe22bfa | ||
|
|
757ba52377 |
@@ -209,7 +209,7 @@ ansible-playbook --tags update site.yml
|
||||
|
||||
**Update process:**
|
||||
1. Stops the application
|
||||
2. Pulls latest images for all services
|
||||
2. Pulls images according to `app_compose_pull` (defaults to `policy`; set to `always` to force fresh pulls)
|
||||
3. Recreates containers with new images
|
||||
4. Runs health checks (if enabled)
|
||||
|
||||
@@ -290,13 +290,23 @@ The file will be copied as-is to the application directory without Jinja2 proces
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Required Externals
|
||||
|
||||
These variables have no defaults and must be set by the calling playbook (typically as group_vars or host_vars). They are consumed by the defaults shown further down; if you override every derived variable that uses them, you can leave the corresponding external unset.
|
||||
|
||||
| Variable | Description | Consumed by |
|
||||
|----------|-------------|-------------|
|
||||
| `host_root_path` | Base directory on the **target host** under which each app's directory is created (e.g. `/srv/apps`, `/opt/docker`) | `app_dir` |
|
||||
| `backup_path` | Base directory on the **controller** where fetched backup archives are stored (e.g. `~/backups`) | `app_backup_path` |
|
||||
| `app_roles_path` | Absolute path to the roles directory that contains your per-app shim roles; used so this role can locate the shim role's `templates/` directory (e.g. `{{ playbook_dir }}/roles`) | `app_templates_path` |
|
||||
| `app_role_name` | Name of the per-app shim role that depends on this one (e.g. `my-app`, `jellyfin`); combined with `app_roles_path` to find its `templates/` directory | `app_templates_path` |
|
||||
|
||||
### Core Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `app_name` | Required | Unique application instance name |
|
||||
| `role_name` | Required | Role name for template paths |
|
||||
| `app_dir` | `{{ host_root_path }}/{{ app_name }}` | Application directory |
|
||||
| `app_dir` | `{{ host_root_path }}/{{ app_name }}` | Application directory on the target host |
|
||||
| `app_uid` | `{{ ansible_facts.user_uid }}` | File ownership UID |
|
||||
| `app_gid` | `{{ ansible_facts.user_gid }}` | File ownership GID |
|
||||
| `app_permission_mode` | `"0640"` | File permission mode |
|
||||
@@ -309,8 +319,9 @@ The file will be copied as-is to the application directory without Jinja2 proces
|
||||
| `app_compose_file` | - | Static compose file path |
|
||||
| `app_compose_content` | - | Inline compose content |
|
||||
| `app_compose_validate` | `true` | Validate compose syntax |
|
||||
| `app_compose_pull` | `policy` | Image pull strategy |
|
||||
| `app_compose_pull` | `policy` | Image pull strategy (also used by `update` — set to `always` to force pulls on update) |
|
||||
| `app_compose_recreate` | `auto` | Container recreate strategy |
|
||||
| `app_compose_project_name` | `{{ app_dir \| basename }}` | Compose project name; override when your compose file sets a non-default project name |
|
||||
|
||||
### Backup Configuration
|
||||
|
||||
@@ -335,6 +346,7 @@ The file will be copied as-is to the application directory without Jinja2 proces
|
||||
|----------|---------|-------------|
|
||||
| `app_health_check` | `true` | Enable health checks |
|
||||
| `app_health_check_method` | `docker` | Check method (`docker`/`http`) |
|
||||
| `app_container_name` | `{{ app_name }}` | Container name inspected by the `docker` health check; override when your compose service uses a different `container_name` |
|
||||
| `app_health_check_url` | - | HTTP endpoint for health check |
|
||||
| `app_health_check_retries` | `30` | Number of check retries |
|
||||
| `app_health_check_delay` | `10` | Delay between checks (seconds) |
|
||||
@@ -369,44 +381,44 @@ app_extra_templates:
|
||||
The role is organized into logical task files for selective execution:
|
||||
|
||||
```
|
||||
├── setup.yaml # Validation, directories, and templates
|
||||
├── restore.yaml # Backup restoration logic
|
||||
├── deploy.yaml # Docker Compose deployment and startup
|
||||
├── health_check.yaml # Health monitoring and verification
|
||||
├── backup.yaml # Backup creation and management
|
||||
├── update.yaml # Container update and image pulling
|
||||
└── manage_compose.yaml # Docker Compose lifecycle management
|
||||
├── setup.yml # Validation, directories, and templates
|
||||
├── restore.yml # Backup restoration logic
|
||||
├── deploy.yml # Docker Compose deployment and startup
|
||||
├── health_check.yml # Health monitoring and verification
|
||||
├── backup.yml # Backup creation and management
|
||||
├── update.yml # Container update and image pulling
|
||||
└── manage_compose.yml # Docker Compose lifecycle management
|
||||
```
|
||||
|
||||
### Task Execution Flow
|
||||
|
||||
1. **Setup** (`setup.yaml`) - [setup, deploy]
|
||||
1. **Setup** (`setup.yml`) - [setup, deploy]
|
||||
- Docker/Compose validation
|
||||
- Variable validation and security checks
|
||||
- Directory creation
|
||||
- Additional template deployment
|
||||
|
||||
2. **Restore** (`restore.yaml`) - [restore, never]
|
||||
2. **Restore** (`restore.yml`) - [restore, never]
|
||||
- Backup file selection and validation
|
||||
- Service stopping
|
||||
- Archive extraction
|
||||
- Service restart
|
||||
|
||||
3. **Deploy** (`deploy.yaml`) - [deploy]
|
||||
3. **Deploy** (`deploy.yml`) - [deploy]
|
||||
- Docker Compose file creation
|
||||
- Container startup and management
|
||||
|
||||
4. **Health Check** (`health_check.yaml`) - [deploy, healthcheck]
|
||||
4. **Health Check** (`health_check.yml`) - [deploy, healthcheck]
|
||||
- Service health verification
|
||||
- Endpoint monitoring
|
||||
|
||||
5. **Backup** (`backup.yaml`) - [backup, never]
|
||||
5. **Backup** (`backup.yml`) - [backup, never]
|
||||
- Service stopping (optional)
|
||||
- Archive creation
|
||||
- Backup copying and cleanup
|
||||
- Service restart
|
||||
|
||||
6. **Update** (`update.yaml`) - [update, never]
|
||||
6. **Update** (`update.yml`) - [update, never]
|
||||
- Service stopping
|
||||
- Image pulling
|
||||
- Container recreation
|
||||
|
||||
@@ -21,7 +21,9 @@ app_compose_validate: true
|
||||
app_compose_pull: policy
|
||||
app_compose_recreate: auto
|
||||
app_compose_start: true
|
||||
app_compose_project_name: "{{ app_dir | basename }}"
|
||||
|
||||
app_container_name: "{{ app_name }}"
|
||||
app_health_check: true
|
||||
app_health_check_method: docker
|
||||
app_health_check_retries: 30
|
||||
@@ -34,6 +34,6 @@
|
||||
when: _compose_type == 'content'
|
||||
register: compose_file
|
||||
|
||||
- ansible.builtin.include_tasks: manage_compose.yaml
|
||||
- ansible.builtin.include_tasks: manage_compose.yml
|
||||
when: app_compose_start | default(true)
|
||||
tags: [deploy]
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
- name: Wait for containers to be healthy
|
||||
- name: Wait for container to be healthy
|
||||
community.docker.docker_container_info:
|
||||
name: "{{ app_name }}"
|
||||
name: "{{ app_container_name }}"
|
||||
register: container_info
|
||||
until: container_info.container.State.Health.Status | default('healthy') == 'healthy'
|
||||
retries: "{{ app_health_check_retries | default(30) }}"
|
||||
@@ -21,29 +21,23 @@
|
||||
when: app_health_check | default(true) and app_health_check_method | default('docker') == 'http' and app_health_check_url is defined
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check if all containers are running
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose ps --services --filter status=running
|
||||
chdir: "{{ app_dir }}"
|
||||
register: running_services
|
||||
when: app_health_check | default(true)
|
||||
|
||||
- name: Check total services
|
||||
ansible.builtin.command:
|
||||
cmd: docker compose ps --services
|
||||
chdir: "{{ app_dir }}"
|
||||
register: total_services
|
||||
- name: Inspect compose project containers
|
||||
community.docker.docker_host_info:
|
||||
containers: true
|
||||
containers_all: true
|
||||
containers_filters:
|
||||
label: "com.docker.compose.project={{ app_compose_project_name }}"
|
||||
register: compose_containers
|
||||
when: app_health_check | default(true)
|
||||
|
||||
- name: Verify deployment success
|
||||
vars:
|
||||
_total: "{{ compose_containers.containers | length }}"
|
||||
_running: "{{ compose_containers.containers | selectattr('State', 'equalto', 'running') | list | length }}"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- running_services.stdout_lines | length == total_services.stdout_lines | length
|
||||
fail_msg: "Not all containers are running. Running: {{ running_services.stdout_lines | length }}, Total: {{ total_services.stdout_lines | length }}"
|
||||
- compose_containers.containers | length > 0
|
||||
- _running == _total
|
||||
fail_msg: "Not all containers are running. Running: {{ _running }}, Total: {{ _total }}"
|
||||
success_msg: "All containers are running successfully."
|
||||
when: app_health_check | default(true) and total_services.stdout_lines is defined
|
||||
|
||||
- name: Log deployment status
|
||||
ansible.builtin.debug:
|
||||
msg: "Deployment {{ 'successful' if (running_services.stdout_lines | length == total_services.stdout_lines | length) else 'failed' }}"
|
||||
when: app_health_check | default(true) and total_services.stdout_lines is defined
|
||||
when: app_health_check | default(true) and compose_containers.containers is defined
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
|
||||
- ansible.builtin.include_tasks: setup.yaml
|
||||
tags: [setup, deploy]
|
||||
- ansible.builtin.include_tasks: restore.yaml
|
||||
tags: [restore, never]
|
||||
- ansible.builtin.include_tasks: deploy.yaml
|
||||
tags: [deploy]
|
||||
- ansible.builtin.include_tasks: health_check.yaml
|
||||
tags: [deploy, healthcheck]
|
||||
- ansible.builtin.include_tasks: backup.yaml
|
||||
tags: [backup, never]
|
||||
- ansible.builtin.include_tasks: update.yaml
|
||||
tags: [update, never]
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
|
||||
- ansible.builtin.include_tasks: setup.yml
|
||||
tags: [setup, deploy]
|
||||
- ansible.builtin.include_tasks: restore.yml
|
||||
tags: [restore, never]
|
||||
- ansible.builtin.include_tasks: deploy.yml
|
||||
tags: [deploy]
|
||||
- ansible.builtin.include_tasks: health_check.yml
|
||||
tags: [deploy, healthcheck]
|
||||
- ansible.builtin.include_tasks: backup.yml
|
||||
tags: [backup, never]
|
||||
- ansible.builtin.include_tasks: update.yml
|
||||
tags: [update, never]
|
||||
@@ -72,21 +72,6 @@
|
||||
state: stopped
|
||||
when: _restore_path is defined
|
||||
|
||||
- name: Debug directory permissions before deletion
|
||||
ansible.builtin.shell: |
|
||||
ls -la "{{ item }}"
|
||||
ls -la "{{ item | dirname }}"
|
||||
whoami
|
||||
id
|
||||
loop: "{{ app_backup_subdirectories | default([]) }}"
|
||||
when: _restore_path is defined
|
||||
become: true
|
||||
register: debug_perms
|
||||
|
||||
- name: Show debug output
|
||||
ansible.builtin.debug:
|
||||
var: debug_perms
|
||||
|
||||
- name: Remove existing backup subdirectories for clean restore
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
@@ -18,11 +18,12 @@
|
||||
that:
|
||||
- app_name is defined
|
||||
- app_name | length > 0
|
||||
- app_name is match('^[a-zA-Z0-9_-]+$')
|
||||
- app_name is match('^[a-zA-Z0-9_.-]+$')
|
||||
- host_root_path is defined
|
||||
- host_root_path | length > 0
|
||||
- host_root_path is match('^/[a-zA-Z0-9/_-]*$')
|
||||
fail_msg: "Validation failed for {{ app_name | default('undefined') }}: Required variables missing or contain invalid characters. app_name and host_root_path must be defined and contain only alphanumeric, underscore, hyphen, and slash characters."
|
||||
- host_root_path is match('^/[a-zA-Z0-9./_-]*$')
|
||||
- not (host_root_path is match('.*\.\..*'))
|
||||
fail_msg: "Validation failed for {{ app_name | default('undefined') }}: Required variables missing or contain invalid characters. app_name and host_root_path must be defined, contain only alphanumeric, dot, underscore, hyphen, and slash characters, and must not contain path traversal sequences."
|
||||
|
||||
- name: Set compose source type
|
||||
ansible.builtin.set_fact:
|
||||
@@ -43,7 +44,7 @@
|
||||
- name: Validate directory paths
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- app_dir is match('^/[a-zA-Z0-9/_-]*$')
|
||||
- app_dir is match('^/[a-zA-Z0-9./_-]*$')
|
||||
- not (app_dir is match('.*\.\..*'))
|
||||
fail_msg: "Security validation failed for {{ app_name }}: app_dir '{{ app_dir }}' contains invalid characters or path traversal sequences"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- name: Pull latest container images
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_dir }}"
|
||||
pull: always
|
||||
pull: "{{ app_compose_pull | default('always') }}"
|
||||
|
||||
- name: Start application with new images
|
||||
community.docker.docker_compose_v2:
|
||||
@@ -19,7 +19,7 @@
|
||||
register: update_restart
|
||||
|
||||
- name: Verify application health after update
|
||||
ansible.builtin.include_tasks: health_check.yaml
|
||||
ansible.builtin.include_tasks: health_check.yml
|
||||
when: app_health_check | default(true)
|
||||
|
||||
rescue:
|
||||
Reference in New Issue
Block a user