commit e9d217ac807f0cbfead6aa48b56eb7ed2ec323f9 Author: Luca Date: Mon Sep 25 18:42:20 2023 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56fb762 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +facts/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ad509b --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Deploy Zammad via Ansible and docker-compose + +## Prerequisites + +* Docker and docker-compose (v1, Ansible does not support v2) +* Traefik v2 + * Externally defined Docker network `traefik` + * Entrypoint `websecure` + * Certificate resolver `lehttp` +* PostgreSQL on host + * Listening on socket in `/var/run/postgresql/` + * Database (`zammad_database_name`, default: `zammad`) and user (`zammad_database_user`, default: `zammad`) created + * (To use containerized Postgres, replace `postgres` service in `roles/zammad/templates/docker-compose.yml.j2` as follows:) +```yaml + postgres: + image: postgres:alpine + restart: unless-stopped + environment: + POSTGRES_DB: '{{ zammad_database_name }}' + POSTGRES_PASSWORD: '{{ zammad_database_password }}' + POSTGRES_USER: '{{ zammad_database_user }}' + networks: + - internal +``` +* enough RAM (or set `zammad_enable_search` to `no`) + +## Deploy + +Add target host(s) to group `zammad` in `inventory/hosts` and copy `inventory/host_vars/example` to `inventory/host_vars/{hostname}` for each host. +Adjust values as needed and run: + +``` +ansible-playbook -D -t zammad site.yml +``` + +### Upgrade + +``` +ansible-playbook -D -t zammad -e 'zammad_upgrade=yes' site.yml +``` diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..0b0f9f8 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,10 @@ +[defaults] +inventory = inventory +gathering = smart +fact_caching = jsonfile +fact_caching_connection = facts +fact_caching_timeout = 1200 +nocows = True + +[ssh_connection] +pipelining = True diff --git a/inventory/group_vars/zammad.yml b/inventory/group_vars/zammad.yml new file mode 100644 index 0000000..6a1f0bc --- /dev/null +++ b/inventory/group_vars/zammad.yml @@ -0,0 +1,9 @@ +--- + +zammad_memcached_version: '1' + +zammad_opensearch_version: '1' + +zammad_redis_version: '7' + +zammad_version: 'stable' diff --git a/inventory/host_vars/example/zammad.yml b/inventory/host_vars/example/zammad.yml new file mode 100644 index 0000000..9128fc0 --- /dev/null +++ b/inventory/host_vars/example/zammad.yml @@ -0,0 +1,7 @@ +--- + +zammad_database_password: '' + +zammad_num_workers: 4 + +zammad_rule: Host(`zammad.example.com`) diff --git a/inventory/hosts b/inventory/hosts new file mode 100644 index 0000000..3a02982 --- /dev/null +++ b/inventory/hosts @@ -0,0 +1 @@ +[zammad] diff --git a/roles/zammad/defaults/main.yml b/roles/zammad/defaults/main.yml new file mode 100644 index 0000000..3e572bf --- /dev/null +++ b/roles/zammad/defaults/main.yml @@ -0,0 +1,19 @@ +--- + +zammad_database_name: zammad +zammad_database_user: zammad + +zammad_enable_search: yes + +zammad_memcached_memory: 256M +zammad_memcached_memory_limit: 512M + +zammad_num_workers: 0 + +zammad_opensearch_memory_limit: 1G +zammad_opensearch_memory_max: 512M +zammad_opensearch_memory_min: 512M + +zammad_redis_memory_limit: 512M + +zammad_upgrade: no diff --git a/roles/zammad/tasks/main.yml b/roles/zammad/tasks/main.yml new file mode 100644 index 0000000..27a8bfa --- /dev/null +++ b/roles/zammad/tasks/main.yml @@ -0,0 +1,39 @@ +--- + +- name: zammad_datadirs + file: + path: '{{ item.path }}' + state: directory + owner: '{{ item.owner | default(omit) }}' + group: '{{ item.group | default(omit) }}' + loop: + - path: /srv/zammad/search + owner: 1000 + group: 1000 + - path: /srv/zammad/zammad + owner: 1000 + group: 1000 + +- name: zammad_composedir + file: + path: /etc/docker/compose/zammad + state: directory + mode: 0700 + +- name: zammad_composefile + template: + dest: /etc/docker/compose/zammad/docker-compose.yml + src: docker-compose.yml.j2 + mode: 0600 + +- name: zammad_search + include_tasks: search.yml + when: zammad_enable_search + +- name: zammad_compose + community.docker.docker_compose: + project_src: /etc/docker/compose/zammad + build: '{{ zammad_upgrade }}' + remove_orphans: yes + environment: + ELASTICSEARCH_REINDEX: "{{ 'true' if zammad_upgrade else 'false' }}" diff --git a/roles/zammad/tasks/search.yml b/roles/zammad/tasks/search.yml new file mode 100644 index 0000000..9339ef4 --- /dev/null +++ b/roles/zammad/tasks/search.yml @@ -0,0 +1,17 @@ +--- + +- name: zammad_search_builddir + file: + path: /etc/docker/compose/zammad/opensearch + state: directory + +- name: zammad_search_dockerfile + template: + dest: /etc/docker/compose/zammad/opensearch/Dockerfile + src: opensearch/Dockerfile.j2 + +- name: zammad_search_sysctl + sysctl: + name: vm.max_map_count + value: '262144' + sysctl_set: yes diff --git a/roles/zammad/templates/docker-compose.yml.j2 b/roles/zammad/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..fa0f31f --- /dev/null +++ b/roles/zammad/templates/docker-compose.yml.j2 @@ -0,0 +1,167 @@ +--- + +services: + init: + image: 'zammad/zammad-docker-compose:{{ zammad_version | mandatory }}' + restart: on-failure + command: + - zammad-init + depends_on: + - postgres + environment: + <<: &cache + MEMCACHE_SERVERS: 'memcached:11211' + REDIS_URL: 'redis://redis:6379' +{% if zammad_enable_search %} + ELASTICSEARCH_HOST: search + ELASTICSEARCH_REINDEX: '${ELASTICSEARCH_REINDEX:-false}' +{% else %} + ELASTICSEARCH_ENABLED: 'false' +{% endif %} + POSTGRESQL_DB: '{{ zammad_database_name }}' + POSTGRESQL_DB_CREATE: 'false' + POSTGRESQL_HOST: postgres + POSTGRESQL_PASS: '{{ zammad_database_password | mandatory }}' + POSTGRESQL_USER: '{{ zammad_database_user }}' + networks: + - internal + volumes: + - /srv/zammad/zammad:/opt/zammad + + memcached: + image: 'memcached:{{ zammad_memcached_version }}-alpine' + restart: unless-stopped + command: + - memcached + - -m + - '{{ zammad_memcached_memory }}' + deploy: + resources: + limits: + memory: '{{ zammad_memcached_memory_limit }}' + networks: + - internal + + nginx: + image: 'zammad/zammad-docker-compose:{{ zammad_version | mandatory }}' + restart: unless-stopped + command: + - zammad-nginx + depends_on: + - railsserver + environment: + NGINX_SERVER_SCHEME: 'https' + ZAMMAD_RAILSSERVER_HOST: 'railsserver' + ZAMMAD_WEBSOCKET_HOST: 'websocket' + expose: + - 8080 + networks: + - internal + - traefik + volumes: + - /srv/zammad/zammad:/opt/zammad + labels: + traefik.enable: 'true' + traefik.docker.network: traefik + traefik.http.routers.zammad.entrypoints: websecure + traefik.http.routers.zammad.rule: '({{ zammad_rule | mandatory }}) && !Path(`/auth/sso`)' + traefik.http.routers.zammad.tls.certresolver: lehttp + + postgres: + image: git.luj0ga.de/luca/postgres-tcpify:1 + restart: unless-stopped + networks: + - internal + volumes: + - /var/run/postgresql:/var/run/postgresql + + railsserver: + image: 'zammad/zammad-docker-compose:{{ zammad_version | mandatory }}' + restart: unless-stopped + command: + - zammad-railsserver + depends_on: + - memcached + - postgres + - redis + environment: + <<: *cache + ZAMMAD_WEB_CONCURRENCY: '{{ zammad_num_workers }}' + networks: + - internal + volumes: + - /srv/zammad/zammad:/opt/zammad + + redis: + image: 'redis:{{ zammad_redis_version }}-alpine' + restart: unless-stopped + deploy: + resources: + limits: + memory: '{{ zammad_redis_memory_limit }}' + networks: + - internal + + scheduler: + image: 'zammad/zammad-docker-compose:{{ zammad_version | mandatory }}' + restart: unless-stopped + command: + - zammad-scheduler + depends_on: + - memcached + - railsserver + - redis + environment: *cache + networks: + - internal + volumes: + - /srv/zammad/zammad:/opt/zammad +{% if zammad_enable_search %} + + search: + build: opensearch + image: 'opensearch-ingest-attachment:{{ zammad_opensearch_version | mandatory }}' + restart: unless-stopped + deploy: + resources: + limits: + memory: '{{ zammad_opensearch_memory_limit }}' + environment: + DISABLE_INSTALL_DEMO_CONFIG: 'true' + DISABLE_SECURITY_PLUGIN: 'true' + OPENSEARCH_JAVA_OPTS: '-Xms{{ zammad_opensearch_memory_min }} -Xmx{{ zammad_opensearch_memory_max }}' + bootstrap.memory_lock: 'true' + compatibility.override_main_response_version: 'true' + discovery.type: single-node + networks: + - internal + ulimits: + memlock: + hard: -1 + soft: -1 + nofile: + hard: 65536 + soft: 65536 + volumes: + - /srv/zammad/search:/usr/share/opensearch/data +{% endif %} + + websocket: + image: 'zammad/zammad-docker-compose:{{ zammad_version | mandatory }}' + restart: unless-stopped + command: + - zammad-websocket + depends_on: + - memcached + - railsserver + - redis + environment: *cache + networks: + - internal + volumes: + - /srv/zammad/zammad:/opt/zammad + +networks: + internal: + traefik: + external: yes diff --git a/roles/zammad/templates/opensearch/Dockerfile.j2 b/roles/zammad/templates/opensearch/Dockerfile.j2 new file mode 100644 index 0000000..1365668 --- /dev/null +++ b/roles/zammad/templates/opensearch/Dockerfile.j2 @@ -0,0 +1,3 @@ +FROM public.ecr.aws/opensearchproject/opensearch:{{ zammad_opensearch_version | mandatory }} + +RUN /usr/share/opensearch/bin/opensearch-plugin install --batch ingest-attachment diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..e28b365 --- /dev/null +++ b/site.yml @@ -0,0 +1,5 @@ +--- + +- hosts: zammad + roles: + - { role: zammad, tags: zammad }