serverus: app_ports.py проверяет опубликованные порты приложения до изменений - по docker compose config (переменные из .env подставлены) против слушающих сокетов сервера и портов контейнеров других приложений. Занятый порт больше не роняет up после замены файлов. Конвенция: только мост; не-HTTP сервисы публикуют свои порты, 80 и 443 - у caddy; bind-mount только своих файлов из data/<сервис>/ с :ro. Описано, что упавший up после синхронизации файлы не откатывает. App: uploads.check предупреждает о 80/443 вместо любых портов и о bind-mount из папки приложения вне data/ или без :ro. docs/apps/rustdesk: приложение (hbbs, hbbr, ключ генерируем сами), genkey.py и check.py - проверка сервера по протоколу RustDesk снаружи. Проверено на стенде Debian 13: деплой, повторный деплой, отказ второго приложения на тех же портах до изменений, перезагрузка сервера. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
136 lines
5.1 KiB
YAML
136 lines
5.1 KiB
YAML
# extravars: apps_dir, caddy_image, local_certs
|
|
#
|
|
# Docker из репозитория Docker, логи контейнеров с ротацией, автоматические обновления безопасности, папка приложений,
|
|
# сеть web и ingress: приложение caddy, которое публикует 80 и 443. Caddy подключает блоки приложений
|
|
# из data/caddy/sites/ и сам выпускает сертификаты для их доменов. local_certs - сертификаты своего CA Caddy.
|
|
# Фаервол настраивается первым: nftables при старте стирает все правила, Docker ставит свои поверх.
|
|
- name: Install Docker platform
|
|
hosts: target
|
|
gather_facts: true
|
|
vars:
|
|
caddy_dir: "{{ apps_dir }}/caddy"
|
|
tasks:
|
|
- ansible.builtin.import_tasks: tasks/firewall.yml
|
|
vars:
|
|
firewall_tcp_ports: [ "{{ ansible_port }}", 80, 443 ]
|
|
firewall_udp_ports: [ 443 ]
|
|
# трафик контейнеров идет через forward: его фильтрует Docker
|
|
firewall_forward: true
|
|
|
|
- name: Install prerequisites
|
|
ansible.builtin.apt:
|
|
name: [ ca-certificates, curl, python3, unattended-upgrades ]
|
|
state: present
|
|
update_cache: true
|
|
|
|
- name: Enable automatic security updates
|
|
ansible.builtin.copy:
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
mode: "0644"
|
|
content: |
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
|
|
- name: Create keyrings directory
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Download Docker key
|
|
ansible.builtin.get_url:
|
|
url: https://download.docker.com/linux/debian/gpg
|
|
dest: /etc/apt/keyrings/docker.asc
|
|
mode: "0644"
|
|
|
|
- name: Add Docker repository
|
|
ansible.builtin.copy:
|
|
dest: /etc/apt/sources.list.d/docker.sources
|
|
mode: "0644"
|
|
content: |
|
|
Types: deb
|
|
URIs: https://download.docker.com/linux/debian
|
|
Suites: {{ ansible_facts.distribution_release }}
|
|
Components: stable
|
|
Architectures: {{ 'arm64' if ansible_facts.architecture == 'aarch64' else 'amd64' }}
|
|
Signed-By: /etc/apt/keyrings/docker.asc
|
|
|
|
- name: Create Docker config directory
|
|
ansible.builtin.file:
|
|
path: /etc/docker
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Configure Docker daemon
|
|
ansible.builtin.copy:
|
|
dest: /etc/docker/daemon.json
|
|
mode: "0644"
|
|
content: |
|
|
{
|
|
"log-driver": "local",
|
|
"log-opts": {"max-size": "10m", "max-file": "3"}
|
|
}
|
|
register: docker_daemon
|
|
|
|
- name: Install Docker
|
|
ansible.builtin.apt:
|
|
name: [ docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, docker-compose-plugin ]
|
|
state: present
|
|
update_cache: true
|
|
|
|
# после частых перезапусков (фаервол, release) systemd может держать Docker остановленным по лимиту запусков
|
|
- name: Reset Docker start limit
|
|
ansible.builtin.command: systemctl reset-failed docker.service
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Start Docker
|
|
ansible.builtin.systemd_service:
|
|
name: docker
|
|
enabled: true
|
|
state: "{{ 'restarted' if docker_daemon.changed else 'started' }}"
|
|
|
|
- name: Create apps directory
|
|
ansible.builtin.file:
|
|
path: "{{ apps_dir }}"
|
|
state: directory
|
|
mode: "0700"
|
|
|
|
- name: Create web network
|
|
ansible.builtin.shell: docker network inspect web >/dev/null 2>&1 || { docker network create web && echo created; }
|
|
register: web_network
|
|
changed_when: "'created' in web_network.stdout"
|
|
|
|
- name: Create ingress directories
|
|
ansible.builtin.file:
|
|
path: "{{ caddy_dir }}/data/caddy/sites"
|
|
state: directory
|
|
mode: "0700"
|
|
|
|
- name: Write ingress compose file
|
|
ansible.builtin.template:
|
|
src: templates/caddy-compose.yaml.j2
|
|
dest: "{{ caddy_dir }}/docker-compose.yaml"
|
|
mode: "0600"
|
|
|
|
# Caddyfile смонтирован папкой: файл, замененный целиком, контейнер увидел бы только после пересоздания
|
|
- name: Write Caddyfile
|
|
ansible.builtin.template:
|
|
src: templates/Caddyfile.j2
|
|
dest: "{{ caddy_dir }}/data/caddy/Caddyfile"
|
|
mode: "0644"
|
|
register: caddyfile
|
|
|
|
- name: Start ingress
|
|
ansible.builtin.command: docker compose up -d --remove-orphans
|
|
args:
|
|
chdir: "{{ caddy_dir }}"
|
|
register: caddy_up
|
|
changed_when: "'Started' in caddy_up.stderr or 'Recreated' in caddy_up.stderr"
|
|
|
|
- name: Reload Caddyfile
|
|
ansible.builtin.command: docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
|
|
args:
|
|
chdir: "{{ caddy_dir }}"
|
|
when: caddyfile.changed and not caddy_up.changed
|