migrate infra from single server to multi-server cluster
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
@@ -0,0 +1,9 @@
|
||||
Unattended-Upgrade::Allowed-Origins {
|
||||
"${distro_id}:${distro_codename}-security";
|
||||
};
|
||||
Unattended-Upgrade::Package-Blacklist {
|
||||
};
|
||||
Unattended-Upgrade::DevRelease "false";
|
||||
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
Unattended-Upgrade::Automatic-Reboot "false";
|
||||
@@ -0,0 +1,16 @@
|
||||
admin_user: 'admin'
|
||||
# Passwords are generated by this command: ansible all -i localhost, -m debug -a 'msg={{ "" | password_hash("sha512", "") }}'
|
||||
admin_password: '$6$rounds=656000$C43bVkou378rvtx8$J1KJZ6uw/KnLGc7nFSixAdsNDKUaSw.SGA38ZtZHwHB8eB5a/rRm1qJ8QR/wIxxylLJ0i7ByaW9CAjpbDYaQH/' # 7V#4kW9RvJfmxAnJV8zWbLrp%%SXRJ#kvGkowR98TY # salt=C43bVkou378rvtx8
|
||||
cephadm_password: '$6$rounds=656000$nE9mLm8AbEshiHr7$9HkqmR2Fr.0gKXwzml7CPhaWY9Ul4t0hdHiFldAI8ShISp6yB0NvehbTDnCKnfBO5UTL591IsNKda54WMa6MS.' # fM5$kZP4d!%$ZocBkhj85q^2GuwwN35YHxS!neC3AM # salt=nE9mLm8AbEshiHr7
|
||||
ssh_port: 22
|
||||
cephfs_name: "shared-fs"
|
||||
ceph_osd_device: "/dev/sdb"
|
||||
public_interface: 'eth0'
|
||||
private_interface: 'enp7s0'
|
||||
authorized_keys:
|
||||
- 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKtYTptTN9ggoy0aUKXmxaPKpclEn86jM7s5UtTw1JJI'
|
||||
hetzner_api_key: "wqwAS4LINKOxgkyeECrHFOq1MRiOhFe236UyNBjs7fIhmOcKrXCmuncVPyt0S7Ac"
|
||||
|
||||
ceph_volume: /mnt/cephfs
|
||||
main_domain: avicenna.hamburg
|
||||
traefik_public_net: traefik_public
|
||||
@@ -0,0 +1,10 @@
|
||||
[all:children]
|
||||
managers
|
||||
workers
|
||||
|
||||
[managers]
|
||||
manager-node-1 ansible_host=188.245.52.124 ansible_connection=ssh ansible_user=root ansible_ssh_private_key_file=./.local/secure/private_key
|
||||
manager-node-2 ansible_host=116.202.18.42 ansible_connection=ssh ansible_user=root ansible_ssh_private_key_file=./.local/secure/private_key
|
||||
manager-node-3 ansible_host=167.235.227.234 ansible_connection=ssh ansible_user=root ansible_ssh_private_key_file=./.local/secure/private_key
|
||||
|
||||
[workers]
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
# - name: Nodes initialisieren und härten
|
||||
# hosts: all
|
||||
# become: true
|
||||
|
||||
# roles:
|
||||
# - role: common
|
||||
# tags: common
|
||||
# - role: ssh_hardening
|
||||
# tags: ssh
|
||||
# - role: ufw_firewall
|
||||
# tags: firewall
|
||||
# - role: fail2ban
|
||||
# tags: fail2ban
|
||||
# handlers:
|
||||
# - name: restart sshd
|
||||
# ansible.builtin.service:
|
||||
# name: ssh
|
||||
# state: restarted
|
||||
# - name: restart fail2ban
|
||||
# ansible.builtin.service:
|
||||
# name: fail2ban
|
||||
# state: restarted
|
||||
# - name: Setup Ceph Cluster and CephFS
|
||||
# hosts: all
|
||||
# become: true
|
||||
# roles:
|
||||
# - role: ceph_setup
|
||||
|
||||
# - name: Docker Swarm initialisieren
|
||||
# hosts: all
|
||||
# become: true
|
||||
# roles:
|
||||
# - role: docker_swarm
|
||||
|
||||
- name: Infrastruktur Dienste bereitstellen
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
roles:
|
||||
- traefik
|
||||
# - authentik
|
||||
# # - dockge
|
||||
- portainer
|
||||
# - signoz
|
||||
# - signoz-infra
|
||||
# - leantime
|
||||
- kestra
|
||||
- hyperdx
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
- name: CEPH | Private IP des ersten Managers ermitteln
|
||||
ansible.builtin.set_fact:
|
||||
ceph_bootstrap_ip: "{{ hostvars[inventory_hostname]['ansible_' + private_interface]['ipv4']['address'] }}"
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: CEPH | Cluster auf dem ersten Manager initialisieren (Bootstrap)
|
||||
ansible.builtin.command:
|
||||
cmd: "cephadm bootstrap --mon-ip {{ ceph_bootstrap_ip }}"
|
||||
creates: /etc/ceph/ceph.conf
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: CEPH | Öffentlichen SSH-Schlüssel von cephadm abrufen
|
||||
ansible.builtin.command: "cephadm shell -- ceph cephadm get-pub-key"
|
||||
register: cephadm_pub_key
|
||||
changed_when: false
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | Öffentlichen Schlüssel von cephadm auf allen Knoten für root verteilen
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ hostvars[groups['managers'][0]]['cephadm_pub_key'].stdout }}"
|
||||
state: present
|
||||
key_options: 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty'
|
||||
|
||||
- name: CEPH | Andere Knoten zum Ceph-Cluster hinzufügen
|
||||
ansible.builtin.command:
|
||||
cmd: "ceph orch host add {{ item }} {{ hostvars[item]['ansible_' + private_interface]['ipv4']['address'] }}"
|
||||
loop: "{{ groups['all'] }}"
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | Prüfen, ob bereits OSDs (Speichergeräte) vorhanden sind
|
||||
ansible.builtin.command: "ceph osd ls"
|
||||
register: existing_osds
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | Spezifische Festplatte ({{ ceph_osd_device }}) auf jedem Knoten als OSD hinzufügen
|
||||
ansible.builtin.command: "ceph orch daemon add osd {{ item }}:{{ ceph_osd_device }}"
|
||||
loop: "{{ groups['all'] }}"
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
when: existing_osds.stdout | length == 0
|
||||
|
||||
- name: CEPH | Prüfen, ob CephFS bereits existiert
|
||||
ansible.builtin.command: "ceph fs ls -f json"
|
||||
register: cephfs_list
|
||||
changed_when: false
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | CephFS Pools und Dateisystem erstellen, falls nicht vorhanden
|
||||
block:
|
||||
- name: Metadaten-Pool für CephFS erstellen
|
||||
ansible.builtin.command: "ceph osd pool create {{ cephfs_name }}_metadata"
|
||||
- name: Daten-Pool für CephFS erstellen
|
||||
ansible.builtin.command: "ceph osd pool create {{ cephfs_name }}_data"
|
||||
- name: CephFS-Dateisystem erstellen
|
||||
ansible.builtin.command: "ceph fs new {{ cephfs_name }} {{ cephfs_name }}_metadata {{ cephfs_name }}_data"
|
||||
when: cephfs_list.stdout | from_json | length == 0
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | Metadaten-Server (MDS) für CephFS starten
|
||||
ansible.builtin.command: "ceph orch apply mds {{ cephfs_name }} --placement=2"
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
when: cephfs_list.stdout | from_json | length == 0
|
||||
|
||||
- name: CEPH | Ceph Admin-Schlüssel für das Mounten abrufen
|
||||
ansible.builtin.command: "ceph auth get-key client.admin"
|
||||
register: ceph_admin_key
|
||||
changed_when: false
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
|
||||
- name: CEPH | Mount-Punkt für CephFS erstellen
|
||||
ansible.builtin.file:
|
||||
path: /mnt/cephfs
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: CEPH | CephFS auf allen Knoten mounten (und in /etc/fstab eintragen)
|
||||
ansible.posix.mount:
|
||||
path: /mnt/cephfs
|
||||
src: "{{ hostvars[groups['managers'][0]]['ceph_bootstrap_ip'] }}:/"
|
||||
fstype: ceph
|
||||
opts: "name=admin,secret={{ ceph_admin_key.stdout }}"
|
||||
state: mounted
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
- name: COMMON | Systempakete aktualisieren und upgraden
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
upgrade: dist
|
||||
autoremove: true
|
||||
autoclean: true
|
||||
|
||||
- name: COMMON | Notwendige Pakete installieren
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- ufw
|
||||
- fail2ban
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
- docker-ce
|
||||
- python3-pip
|
||||
- chrony
|
||||
- lvm2
|
||||
- cephadm
|
||||
- ceph-common
|
||||
state: present
|
||||
|
||||
- name: COMMON | Chrony Dienst starten und aktivieren
|
||||
ansible.builtin.service:
|
||||
name: chronyd
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: COMMON | Docker Dienst starten und aktivieren
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: COMMON | Einen dedizierten Admin-Benutzer erstellen
|
||||
ansible.builtin.user:
|
||||
name: "{{ admin_user }}"
|
||||
password: "{{ admin_password}}"
|
||||
shell: /bin/bash
|
||||
groups: sudo,docker
|
||||
append: true
|
||||
state: present
|
||||
|
||||
- name: COMMON | SSH-Schlüssel für den Admin-Benutzer einrichten
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ admin_user }}"
|
||||
key: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ authorized_keys }}"
|
||||
|
||||
- name: COMMON | Ensure group "cephadm" exists
|
||||
group:
|
||||
name: cephadm
|
||||
state: present
|
||||
|
||||
- name: COMMON | cephadm-Benutzer erstellen
|
||||
ansible.builtin.user:
|
||||
name: "cephadm"
|
||||
password: "{{ cephadm_password }}"
|
||||
shell: /bin/bash
|
||||
groups: sudo,docker
|
||||
append: yes
|
||||
state: present
|
||||
|
||||
- name: COMMON | .ssh Verzeichnis für cephadm-Benutzer erstellen
|
||||
ansible.builtin.file:
|
||||
path: /home/cephadm/.ssh
|
||||
state: directory
|
||||
|
||||
- name: COMMON | Passwortloses Sudo für cephadm-Benutzer erlauben
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/sudoers.d/91-cephadm-nopasswd"
|
||||
content: "cephadm ALL=(ALL) NOPASSWD: ALL"
|
||||
mode: '0440'
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
- name: COMMON | ed25519 SSH-Schlüssel für cephadm-Benutzer generieren (nur auf dem ersten Manager)
|
||||
community.crypto.openssh_keypair:
|
||||
path: /home/cephadm/.ssh/id_ed25519
|
||||
type: ed25519
|
||||
owner: cephadm
|
||||
group: cephadm
|
||||
mode: '0600'
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: COMMON | Öffentlichen SSH-Schlüssel von cephadm abrufen
|
||||
ansible.builtin.slurp:
|
||||
src: /home/cephadm/.ssh/id_ed25519.pub
|
||||
register: cephadm_ssh_pub_key
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: COMMON | Öffentlichen SSH-Schlüssel von cephadm auf allen Knoten verteilen
|
||||
ansible.posix.authorized_key:
|
||||
user: cephadm
|
||||
key: "{{ hostvars[groups['managers'][0]]['cephadm_ssh_pub_key']['content'] | b64decode }}"
|
||||
state: present
|
||||
|
||||
- name: COMMON | Automatische Sicherheitsupdates konfigurieren
|
||||
ansible.builtin.copy:
|
||||
src: assets/50unattended-upgrades
|
||||
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: COMMON | Periodische Auto-Updates aktivieren
|
||||
ansible.builtin.copy:
|
||||
src: assets/20auto-upgrades
|
||||
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
- name: SWARM | Ensure Docker SDK for Python is installed
|
||||
ansible.builtin.apt:
|
||||
name: python3-docker
|
||||
state: present
|
||||
|
||||
- name: SWARM | Get interface IP address for the manager
|
||||
ansible.builtin.set_fact:
|
||||
manager_ip: "{{ hostvars[inventory_hostname]['ansible_' + private_interface]['ipv4']['address'] }}"
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: SWARM | Initialize the Docker Swarm
|
||||
community.docker.docker_swarm:
|
||||
state: present
|
||||
advertise_addr: "{{ manager_ip }}"
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
register: swarm_init_result
|
||||
|
||||
- name: SWARM | Get the join tokens
|
||||
community.docker.docker_swarm_info:
|
||||
register: swarm_info
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: SWARM | Verify that join tokens were fetched
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- swarm_info is defined
|
||||
- swarm_info.swarm_facts is defined
|
||||
- swarm_info.swarm_facts.JoinTokens.Manager is defined
|
||||
- swarm_info.swarm_facts.JoinTokens.Worker is defined
|
||||
fail_msg: "Konnte die Join-Tokens vom Swarm Manager nicht abrufen. Ist der Swarm korrekt initialisiert?"
|
||||
success_msg: "Join-Tokens erfolgreich abgerufen."
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: SWARM | Join manager nodes to the Swarm
|
||||
community.docker.docker_swarm:
|
||||
state: join
|
||||
remote_addrs: [ "{{ hostvars[groups['managers'][0]]['manager_ip'] }}:2377" ]
|
||||
join_token: "{{ hostvars[groups['managers'][0]]['swarm_info']['swarm_facts']['JoinTokens']['Manager'] }}"
|
||||
when: inventory_hostname in groups['managers']
|
||||
|
||||
- name: SWARM | Join worker nodes to the Swarm
|
||||
community.docker.docker_swarm:
|
||||
state: join
|
||||
remote_addrs: [ "{{ hostvars[groups['managers'][0]]['manager_ip'] }}:2377" ]
|
||||
join_token: "{{ hostvars[groups['managers'][0]]['swarm_info']['swarm_facts']['JoinTokens']['Worker'] }}"
|
||||
when: inventory_hostname in groups['workers']
|
||||
|
||||
- name: SWARM | Verify Swarm Cluster State (run on manager)
|
||||
ansible.builtin.command: docker node ls
|
||||
register: swarm_nodes
|
||||
changed_when: false
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
|
||||
- name: SWARM | Display cluster state
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ swarm_nodes.stdout_lines }}"
|
||||
when: inventory_hostname == groups['managers'][0]
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: FAIL2BAN | Eine lokale Jail-Konfiguration erstellen
|
||||
ansible.builtin.template:
|
||||
src: jail.local.j2
|
||||
dest: /etc/fail2ban/jail.local
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: restart fail2ban
|
||||
@@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
bantime = 1h
|
||||
findtime = 10m
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = {{ ssh_port }}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<logger>
|
||||
<level>debug</level>
|
||||
<console>true</console>
|
||||
<log remove="remove" />
|
||||
<errorlog remove="remove" />
|
||||
</logger>
|
||||
|
||||
<listen_host>0.0.0.0</listen_host>
|
||||
<http_port>8123</http_port>
|
||||
<tcp_port>9000</tcp_port>
|
||||
<interserver_http_host>ch-server</interserver_http_host>
|
||||
<interserver_http_port>9009</interserver_http_port>
|
||||
|
||||
<max_connections>4096</max_connections>
|
||||
<keep_alive_timeout>64</keep_alive_timeout>
|
||||
<max_concurrent_queries>100</max_concurrent_queries>
|
||||
<uncompressed_cache_size>8589934592</uncompressed_cache_size>
|
||||
<mark_cache_size>5368709120</mark_cache_size>
|
||||
|
||||
<path>/var/lib/clickhouse/</path>
|
||||
<tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
|
||||
<user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
|
||||
|
||||
<user_directories>
|
||||
<users_xml>
|
||||
<path>users.xml</path>
|
||||
</users_xml>
|
||||
</user_directories>
|
||||
<!-- <users_config>users.xml</users_config> -->
|
||||
<default_profile>default</default_profile>
|
||||
<default_database>default</default_database>
|
||||
<timezone>UTC</timezone>
|
||||
<mlock_executable>false</mlock_executable>
|
||||
|
||||
<!-- Prometheus exporter -->
|
||||
<prometheus>
|
||||
<endpoint>/metrics</endpoint>
|
||||
<port>9363</port>
|
||||
<metrics>true</metrics>
|
||||
<events>true</events>
|
||||
<asynchronous_metrics>true</asynchronous_metrics>
|
||||
<errors>true</errors>
|
||||
</prometheus>
|
||||
|
||||
<!-- Query log. Used only for queries with setting log_queries = 1. -->
|
||||
<query_log>
|
||||
<database>system</database>
|
||||
<table>query_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_log>
|
||||
|
||||
<!-- Metric log contains rows with current values of ProfileEvents, CurrentMetrics collected
|
||||
with "collect_interval_milliseconds" interval. -->
|
||||
<metric_log>
|
||||
<database>system</database>
|
||||
<table>metric_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
<collect_interval_milliseconds>1000</collect_interval_milliseconds>
|
||||
</metric_log>
|
||||
|
||||
<!--
|
||||
Asynchronous metric log contains values of metrics from
|
||||
system.asynchronous_metrics.
|
||||
-->
|
||||
<asynchronous_metric_log>
|
||||
<database>system</database>
|
||||
<table>asynchronous_metric_log</table>
|
||||
<!--
|
||||
Asynchronous metrics are updated once a minute, so there is
|
||||
no need to flush more often.
|
||||
-->
|
||||
<flush_interval_milliseconds>7000</flush_interval_milliseconds>
|
||||
</asynchronous_metric_log>
|
||||
|
||||
<!--
|
||||
OpenTelemetry log contains OpenTelemetry trace spans.
|
||||
-->
|
||||
<opentelemetry_span_log>
|
||||
<!--
|
||||
The default table creation code is insufficient, this <engine> spec
|
||||
is a workaround. There is no 'event_time' for this log, but two times,
|
||||
start and finish. It is sorted by finish time, to avoid inserting
|
||||
data too far away in the past (probably we can sometimes insert a span
|
||||
that is seconds earlier than the last span in the table, due to a race
|
||||
between several spans inserted in parallel). This gives the spans a
|
||||
global order that we can use to e.g. retry insertion into some external
|
||||
system.
|
||||
-->
|
||||
<engine>
|
||||
engine MergeTree
|
||||
partition by toYYYYMM(finish_date)
|
||||
order by (finish_date, finish_time_us, trace_id)
|
||||
</engine>
|
||||
<database>system</database>
|
||||
<table>opentelemetry_span_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</opentelemetry_span_log>
|
||||
|
||||
|
||||
<!-- Crash log. Stores stack traces for fatal errors.
|
||||
This table is normally empty. -->
|
||||
<crash_log>
|
||||
<database>system</database>
|
||||
<table>crash_log</table>
|
||||
|
||||
<partition_by />
|
||||
<flush_interval_milliseconds>1000</flush_interval_milliseconds>
|
||||
</crash_log>
|
||||
|
||||
<!-- Profiling on Processors level. -->
|
||||
<processors_profile_log>
|
||||
<database>system</database>
|
||||
<table>processors_profile_log</table>
|
||||
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</processors_profile_log>
|
||||
|
||||
<!-- Uncomment if use part log.
|
||||
Part log contains information about all actions with parts in MergeTree tables (creation, deletion,
|
||||
merges, downloads).-->
|
||||
<part_log>
|
||||
<database>system</database>
|
||||
<table>part_log</table>
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</part_log>
|
||||
|
||||
<!-- Trace log. Stores stack traces collected by query profilers.
|
||||
See query_profiler_real_time_period_ns and query_profiler_cpu_time_period_ns settings. -->
|
||||
<trace_log>
|
||||
<database>system</database>
|
||||
<table>trace_log</table>
|
||||
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</trace_log>
|
||||
|
||||
<!-- Query thread log. Has information about all threads participated in query execution.
|
||||
Used only for queries with setting log_query_threads = 1. -->
|
||||
<query_thread_log>
|
||||
<database>system</database>
|
||||
<table>query_thread_log</table>
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_thread_log>
|
||||
|
||||
<!-- Query views log. Has information about all dependent views associated with a query.
|
||||
Used only for queries with setting log_query_views = 1. -->
|
||||
<query_views_log>
|
||||
<database>system</database>
|
||||
<table>query_views_log</table>
|
||||
<partition_by>toYYYYMM(event_date)</partition_by>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_views_log>
|
||||
|
||||
<remote_servers>
|
||||
<hdx_cluster>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>ch-server</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</hdx_cluster>
|
||||
</remote_servers>
|
||||
|
||||
<distributed_ddl>
|
||||
<path>/clickhouse/task_queue/ddl</path>
|
||||
</distributed_ddl>
|
||||
|
||||
<format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
|
||||
</clickhouse>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<profiles>
|
||||
<default>
|
||||
<max_memory_usage>10000000000</max_memory_usage>
|
||||
<use_uncompressed_cache>0</use_uncompressed_cache>
|
||||
<load_balancing>in_order</load_balancing>
|
||||
<log_queries>1</log_queries>
|
||||
</default>
|
||||
</profiles>
|
||||
|
||||
<users>
|
||||
<default>
|
||||
<password></password>
|
||||
<profile>default</profile>
|
||||
<networks>
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
<quota>default</quota>
|
||||
</default>
|
||||
<api>
|
||||
<password>api</password>
|
||||
<profile>default</profile>
|
||||
<networks>
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
<quota>default</quota>
|
||||
</api>
|
||||
<worker>
|
||||
<password>worker</password>
|
||||
<profile>default</profile>
|
||||
<networks>
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
<quota>default</quota>
|
||||
</worker>
|
||||
</users>
|
||||
|
||||
<quotas>
|
||||
<default>
|
||||
<interval>
|
||||
<duration>3600</duration>
|
||||
<queries>0</queries>
|
||||
<errors>0</errors>
|
||||
<result_rows>0</result_rows>
|
||||
<read_rows>0</read_rows>
|
||||
<execution_time>0</execution_time>
|
||||
</interval>
|
||||
</default>
|
||||
</quotas>
|
||||
</clickhouse>
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: HYPERDX | Verzeichnisse erstellen
|
||||
ansible.builtin.file:
|
||||
path: "/mnt/cephfs/hyperdx/{{ item.path }}"
|
||||
state: directory
|
||||
owner: "{{ item.uid }}"
|
||||
group: "{{ item.gid }}"
|
||||
mode: '0755'
|
||||
recurse: yes
|
||||
loop:
|
||||
- { path: 'mongo', uid: 999, gid: 999 } # MongoDB Standard
|
||||
- { path: 'clickhouse/data', uid: 101, gid: 101 } # ClickHouse Standard
|
||||
- { path: 'clickhouse/logs', uid: 101, gid: 101 }
|
||||
- { path: 'clickhouse/config', uid: 101, gid: 101 }
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: HYPERDX | ClickHouse Konfiguration kopieren
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/mnt/cephfs/hyperdx/clickhouse/config/"
|
||||
owner: 101
|
||||
group: 101
|
||||
mode: '0644'
|
||||
loop:
|
||||
- files/config.xml # Lokal in deinem Ansible Repo
|
||||
- files/users.xml
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: HYPERDX | Generate Compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: '{{ data_dir }}/hyperdx.yml'
|
||||
mode: 0644
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: HYPERDX | Deploy stack
|
||||
community.docker.docker_stack:
|
||||
state: present
|
||||
name: hyperdx
|
||||
compose:
|
||||
- '{{ data_dir }}/hyperdx.yml'
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
@@ -0,0 +1,100 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mongo:5.0.14-focal
|
||||
volumes:
|
||||
- "{{ data_dir }}/mongo:/data/db"
|
||||
networks:
|
||||
- internal
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
# placement:
|
||||
# constraints: [node.role == worker] # DBs besser auf Workern lassen wenn möglich
|
||||
|
||||
otel-collector:
|
||||
image: "clickhouse/clickstack-otel-collector:2"
|
||||
environment:
|
||||
CLICKHOUSE_ENDPOINT: 'tcp://ch-server:9000?dial_timeout=10s'
|
||||
HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE: "default"
|
||||
HYPERDX_LOG_LEVEL: "{{ hyperdx_log_level | default('info') }}"
|
||||
OPAMP_SERVER_URL: 'http://app:{{ hyperdx_opamp_port | default(4320) }}'
|
||||
# Ingestion Ports direkt via Swarm Mesh exponieren für Performance
|
||||
ports:
|
||||
- "4317:4317" # OTLP gRPC
|
||||
- "4318:4318" # OTLP HTTP
|
||||
# - "8888:8888" # Metrics (optional)
|
||||
networks:
|
||||
- internal
|
||||
- shared-observability
|
||||
- traefik_public
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik_public"
|
||||
- "traefik.http.routers.otel-collector.rule=Host(`{{ otlp_domain }}`)"
|
||||
- "traefik.http.routers.otel-collector.entrypoints=https"
|
||||
- "traefik.http.routers.otel-collector.tls.certresolver=main"
|
||||
- "traefik.http.services.otel-collector.loadbalancer.server.port=4318"
|
||||
|
||||
app:
|
||||
image: "hyperdx/hyperdx:2"
|
||||
environment:
|
||||
# URLs anpassen für Traefik Erreichbarkeit
|
||||
FRONTEND_URL: "https://{{ hdx_domain }}"
|
||||
HYPERDX_APP_URL: "https://{{ hdx_domain }}"
|
||||
|
||||
HYPERDX_API_KEY: "{{ hyperdx_api_key }}"
|
||||
HYPERDX_API_PORT: "{{ hyperdx_api_port | default(8000) }}"
|
||||
HYPERDX_APP_PORT: "{{ hyperdx_app_port | default(8080) }}"
|
||||
HYPERDX_LOG_LEVEL: "{{ hyperdx_log_level | default('info') }}"
|
||||
MINER_API_URL: 'http://miner:5123' # Falls miner benötigt wird (in original compose nicht definiert?)
|
||||
MONGO_URI: 'mongodb://db:27017/hyperdx'
|
||||
SERVER_URL: "http://127.0.0.1:{{ hyperdx_api_port | default(8000) }}"
|
||||
OPAMP_PORT: "{{ hyperdx_opamp_port | default(4320) }}"
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: 'http://otel-collector:4318'
|
||||
OTEL_SERVICE_NAME: 'hdx-oss-app'
|
||||
USAGE_STATS_ENABLED: "{{ usage_stats_enabled | default('false') }}"
|
||||
# Clickhouse Connection String (Default User/Pass from Clickhouse Image)
|
||||
DEFAULT_CONNECTIONS: >-
|
||||
[{"name":"Local ClickHouse","host":"http://ch-server:8123","username":"default","password":""}]
|
||||
DEFAULT_SOURCES: '{{ hyperdx_default_sources | to_json }}'
|
||||
networks:
|
||||
- internal
|
||||
- traefik_public
|
||||
deploy:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik_public"
|
||||
- "traefik.http.routers.hyperdx.rule=Host(`{{ subdomain }}.{{ main_domain }}`)"
|
||||
- "traefik.http.routers.hyperdx.entrypoints=https"
|
||||
- "traefik.http.routers.hyperdx.tls.certresolver=main"
|
||||
- "traefik.http.services.hyperdx.loadbalancer.server.port={{ hyperdx_app_port | default(8080) }}"
|
||||
|
||||
ch-server:
|
||||
image: clickhouse/clickhouse-server:25.6-alpine
|
||||
environment:
|
||||
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
|
||||
volumes:
|
||||
- "{{ data_dir }}/clickhouse/config/config.xml:/etc/clickhouse-server/config.xml"
|
||||
- "{{ data_dir }}/clickhouse/config/users.xml:/etc/clickhouse-server/users.xml"
|
||||
- "{{ data_dir }}/clickhouse/data:/var/lib/clickhouse"
|
||||
- "{{ data_dir }}/clickhouse/logs:/var/log/clickhouse-server"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
# placement:
|
||||
# constraints: [node.role == worker]
|
||||
networks:
|
||||
- internal
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: overlay
|
||||
traefik_public:
|
||||
external: true
|
||||
shared-observability:
|
||||
external: true
|
||||
@@ -0,0 +1,107 @@
|
||||
data_dir: "{{ ceph_volume }}/hyperdx"
|
||||
subdomain: "hdx"
|
||||
|
||||
hdx_domain: "{{ subdomain }}.{{ main_domain }}"
|
||||
otlp_domain: "otlp.{{ main_domain }}"
|
||||
|
||||
# Generiere einen sicheren Key: `openssl rand -hex 16`
|
||||
hyperdx_api_key: "2bb00477e073d6e2b72e310c39785fa1"
|
||||
hyperdx_api_port: 8000
|
||||
hyperdx_app_port: 8080
|
||||
hyperdx_log_level: "info"
|
||||
hyperdx_opamp_port: 4320
|
||||
|
||||
usage_stats_enabled: "false"
|
||||
|
||||
# vars/main.yml
|
||||
|
||||
# ... (deine anderen Variablen) ...
|
||||
|
||||
# Definition der Datenquellen für das Frontend
|
||||
hyperdx_default_sources:
|
||||
- name: "Logs"
|
||||
kind: "log"
|
||||
from:
|
||||
databaseName: "default"
|
||||
tableName: "otel_logs"
|
||||
timestampValueExpression: "TimestampTime"
|
||||
displayedTimestampValueExpression: "Timestamp"
|
||||
implicitColumnExpression: "Body"
|
||||
serviceNameExpression: "ServiceName"
|
||||
bodyExpression: "Body"
|
||||
eventAttributesExpression: "LogAttributes"
|
||||
resourceAttributesExpression: "ResourceAttributes"
|
||||
defaultTableSelectExpression: "Timestamp,ServiceName,SeverityText,Body"
|
||||
severityTextExpression: "SeverityText"
|
||||
traceIdExpression: "TraceId"
|
||||
spanIdExpression: "SpanId"
|
||||
connection: "Local ClickHouse"
|
||||
traceSourceId: "Traces"
|
||||
sessionSourceId: "Sessions"
|
||||
metricSourceId: "Metrics"
|
||||
|
||||
- name: "Traces"
|
||||
kind: "trace"
|
||||
from:
|
||||
databaseName: "default"
|
||||
tableName: "otel_traces"
|
||||
timestampValueExpression: "Timestamp"
|
||||
displayedTimestampValueExpression: "Timestamp"
|
||||
implicitColumnExpression: "SpanName"
|
||||
serviceNameExpression: "ServiceName"
|
||||
bodyExpression: "SpanName"
|
||||
eventAttributesExpression: "SpanAttributes"
|
||||
resourceAttributesExpression: "ResourceAttributes"
|
||||
defaultTableSelectExpression: "Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName"
|
||||
traceIdExpression: "TraceId"
|
||||
spanIdExpression: "SpanId"
|
||||
durationExpression: "Duration"
|
||||
durationPrecision: 9
|
||||
parentSpanIdExpression: "ParentSpanId"
|
||||
spanNameExpression: "SpanName"
|
||||
spanKindExpression: "SpanKind"
|
||||
statusCodeExpression: "StatusCode"
|
||||
statusMessageExpression: "StatusMessage"
|
||||
connection: "Local ClickHouse"
|
||||
logSourceId: "Logs"
|
||||
sessionSourceId: "Sessions"
|
||||
metricSourceId: "Metrics"
|
||||
|
||||
- name: "Metrics"
|
||||
kind: "metric"
|
||||
from:
|
||||
databaseName: "default"
|
||||
tableName: ""
|
||||
timestampValueExpression: "TimeUnix"
|
||||
resourceAttributesExpression: "ResourceAttributes"
|
||||
metricTables:
|
||||
gauge: "otel_metrics_gauge"
|
||||
histogram: "otel_metrics_histogram"
|
||||
sum: "otel_metrics_sum"
|
||||
_id: "682586a8b1f81924e628e808"
|
||||
id: "682586a8b1f81924e628e808"
|
||||
connection: "Local ClickHouse"
|
||||
logSourceId: "Logs"
|
||||
traceSourceId: "Traces"
|
||||
sessionSourceId: "Sessions"
|
||||
|
||||
- name: "Sessions"
|
||||
kind: "session"
|
||||
from:
|
||||
databaseName: "default"
|
||||
tableName: "hyperdx_sessions"
|
||||
timestampValueExpression: "TimestampTime"
|
||||
displayedTimestampValueExpression: "Timestamp"
|
||||
implicitColumnExpression: "Body"
|
||||
serviceNameExpression: "ServiceName"
|
||||
bodyExpression: "Body"
|
||||
eventAttributesExpression: "LogAttributes"
|
||||
resourceAttributesExpression: "ResourceAttributes"
|
||||
defaultTableSelectExpression: "Timestamp,ServiceName,SeverityText,Body"
|
||||
severityTextExpression: "SeverityText"
|
||||
traceIdExpression: "TraceId"
|
||||
spanIdExpression: "SpanId"
|
||||
connection: "Local ClickHouse"
|
||||
logSourceId: "Logs"
|
||||
traceSourceId: "Traces"
|
||||
metricSourceId: "Metrics"
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
- name: KESTRA | Ensure data directory
|
||||
ansible.builtin.file:
|
||||
path: '{{ data_dir }}/data/data'
|
||||
state: directory
|
||||
mode: '0755'
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: KESTRA | Ensure db directory
|
||||
ansible.builtin.file:
|
||||
path: '{{ data_dir }}/data/db'
|
||||
state: directory
|
||||
mode: '0755'
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: KESTRA | Konfigurationsdatei für tmpfiles.d erstellen
|
||||
ansible.builtin.copy:
|
||||
content: "d /tmp/kestra-wd 0755 root root -"
|
||||
dest: /etc/tmpfiles.d/kestra-wd.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: KESTRA | Create Kestra working directory
|
||||
ansible.builtin.file:
|
||||
path: /tmp/kestra-wd
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: KESTRA | Generate Compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: '{{ data_dir }}/kestra.yml'
|
||||
mode: 0644
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: KESTRA | Deploy stack
|
||||
community.docker.docker_stack:
|
||||
state: present
|
||||
name: kestra
|
||||
compose:
|
||||
- /mnt/cephfs/kestra/kestra.yml
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
@@ -0,0 +1,92 @@
|
||||
networks:
|
||||
internal:
|
||||
{{ traefik_public_net }}:
|
||||
external: true
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
volumes:
|
||||
- {{ data_dir }}/data/db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: kestra
|
||||
POSTGRES_USER: kestra
|
||||
POSTGRES_PASSWORD: k3str4
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
networks:
|
||||
- internal
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
kestra:
|
||||
image: kestra/kestra:v0.24.2
|
||||
entrypoint: /bin/bash
|
||||
# Note that this is meant for development only. Refer to the documentation for production deployments of Kestra which runs without a root user.
|
||||
user: "root"
|
||||
command:
|
||||
- -c
|
||||
- /app/kestra server standalone --worker-thread=128
|
||||
volumes:
|
||||
- {{ data_dir }}/data/data:/app/storage
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /tmp/kestra-wd:/tmp/kestra-wd
|
||||
environment:
|
||||
KESTRA_CONFIGURATION: |
|
||||
datasources:
|
||||
postgres:
|
||||
url: jdbc:postgresql://postgres:5432/kestra
|
||||
driverClassName: org.postgresql.Driver
|
||||
username: kestra
|
||||
password: k3str4
|
||||
kestra:
|
||||
tutorialFlows:
|
||||
enabled: false
|
||||
traces:
|
||||
root: DEFAULT
|
||||
micronaut:
|
||||
metrics:
|
||||
export:
|
||||
otlp:
|
||||
enabled: true
|
||||
url: http://signoz_otel-collector:4318/v1/metrics
|
||||
otel:
|
||||
traces:
|
||||
exporter: otlp
|
||||
exporter:
|
||||
otlp:
|
||||
endpoint: http://signoz_otel-collector:4318
|
||||
server:
|
||||
basic-auth:
|
||||
username: ma@coachhamburg.com
|
||||
password: "igyozi9B87yTeiQ6z2sbe8Y4aQLJV58jdaCNu"
|
||||
repository:
|
||||
type: postgres
|
||||
storage:
|
||||
type: local
|
||||
local:
|
||||
base-path: "/app/storage"
|
||||
queue:
|
||||
type: postgres
|
||||
tasks:
|
||||
tmp-dir:
|
||||
path: /tmp/kestra-wd/tmp
|
||||
url: http://localhost:8080/
|
||||
networks:
|
||||
- {{ traefik_public_net }}
|
||||
- internal
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.swarm.network={{ traefik_public_net }}"
|
||||
- "traefik.http.routers.kestra.rule=Host(`{{ subdomain }}.{{ main_domain }}`)"
|
||||
- "traefik.http.routers.kestra.entrypoints=https"
|
||||
- "traefik.http.routers.kestra.tls=true"
|
||||
- "traefik.http.routers.kestra.tls.certresolver=main"
|
||||
- "traefik.http.services.kestra.loadbalancer.server.port=8080"
|
||||
@@ -0,0 +1,2 @@
|
||||
subdomain: workflow
|
||||
data_dir: "{{ ceph_volume }}/kestra"
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
- name: PORTAINER | Ensure data directories
|
||||
ansible.builtin.file:
|
||||
path: '{{ data_dir }}/data'
|
||||
state: directory
|
||||
mode: '0755'
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: PORTAINER | Generate Compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: '{{ data_dir }}/portainer.yml'
|
||||
mode: 0644
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: PORTAINER | Deploy stack
|
||||
community.docker.docker_stack:
|
||||
state: present
|
||||
name: portainer
|
||||
compose:
|
||||
- '{{ data_dir }}/portainer.yml'
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
@@ -0,0 +1,39 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
agent:
|
||||
image: portainer/agent:2.33.5
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /var/lib/docker/volumes:/var/lib/docker/volumes
|
||||
networks:
|
||||
- {{ traefik_public_net }}
|
||||
deploy:
|
||||
mode: global
|
||||
placement:
|
||||
constraints: [node.platform.os == linux]
|
||||
|
||||
portainer:
|
||||
image: portainer/portainer-ce:2.33.5
|
||||
command: -H tcp://portainer_agent:9001 --tlsskipverify
|
||||
volumes:
|
||||
- {{ data_dir }}/data:/data
|
||||
networks:
|
||||
- {{ traefik_public_net }}
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.swarm.network={{ traefik_public_net }}"
|
||||
- "traefik.http.routers.portainer.rule=Host(`{{ subdomain }}.{{ main_domain }}`)"
|
||||
- "traefik.http.routers.portainer.entrypoints=https"
|
||||
- "traefik.http.routers.portainer.tls=true"
|
||||
- "traefik.http.routers.portainer.tls.certresolver=main"
|
||||
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||
placement:
|
||||
constraints: [node.hostname == manager-node-3]
|
||||
|
||||
networks:
|
||||
{{ traefik_public_net }}:
|
||||
external: true
|
||||
@@ -0,0 +1,2 @@
|
||||
subdomain: port
|
||||
data_dir: "{{ ceph_volume }}/portainer"
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
- name: SSH | Ensure privilege separation directory exists
|
||||
ansible.builtin.file:
|
||||
path: /run/sshd
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: SSH | Root-Login nur mit Schlüssel erlauben
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^#?PermitRootLogin'
|
||||
line: 'PermitRootLogin prohibit-password'
|
||||
validate: 'sshd -t -f %s'
|
||||
notify: restart sshd
|
||||
|
||||
- name: SSH | Passwort-Authentifizierung deaktivieren
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^#?PasswordAuthentication'
|
||||
line: 'PasswordAuthentication no'
|
||||
validate: 'sshd -t -f %s'
|
||||
notify: restart sshd
|
||||
|
||||
- name: SSH | Leere Passwörter verbieten
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^#?PermitEmptyPasswords'
|
||||
line: 'PermitEmptyPasswords no'
|
||||
validate: 'sshd -t -f %s'
|
||||
notify: restart sshd
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
[http]
|
||||
[http.middlewares]
|
||||
[http.middlewares.authentik.forwardAuth]
|
||||
address = "http://authentik_server:9000/outpost.goauthentik.io/auth/traefik"
|
||||
trustForwardHeader = true
|
||||
authResponseHeaders = [
|
||||
"X-authentik-username",
|
||||
"X-authentik-groups",
|
||||
"X-authentik-email",
|
||||
"X-authentik-name",
|
||||
"X-authentik-uid",
|
||||
"X-authentik-jwt",
|
||||
"X-authentik-meta-jwks",
|
||||
"X-authentik-meta-outpost",
|
||||
"X-authentik-meta-provider",
|
||||
"X-authentik-meta-app",
|
||||
"X-authentik-meta-version"
|
||||
]
|
||||
@@ -0,0 +1,80 @@
|
||||
[global]
|
||||
checkNewVersion = true
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[experimental]
|
||||
otlpLogs = true
|
||||
|
||||
[core]
|
||||
defaultRuleSyntax = "v2"
|
||||
|
||||
[accessLog]
|
||||
filePath = "/logs/access.log"
|
||||
format = "json"
|
||||
|
||||
# Enable the Dashboard
|
||||
[api]
|
||||
dashboard = true
|
||||
|
||||
# Write out Traefik logs
|
||||
[log]
|
||||
level = "INFO"
|
||||
format = "json"
|
||||
filePath = "/logs/traefik.log"
|
||||
# [log.otlp.http]
|
||||
# endpoint = "http://signoz_otel-collector:4318/v1/logs"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.http.redirections.entryPoint]
|
||||
to = "https"
|
||||
scheme = "https"
|
||||
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
# [entryPoints.https.http.tls]
|
||||
# certResolver = "main"
|
||||
|
||||
# OTel
|
||||
# [tracing]
|
||||
# serviceName = "traefik"
|
||||
# [tracing.otlp.http]
|
||||
# endpoint = "http://signoz_otel-collector:4318/v1/traces"
|
||||
# [tracing.otlp.http.tls]
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# # Metrics
|
||||
# [metrics]
|
||||
# addInternals = false
|
||||
# [metrics.otlp]
|
||||
# serviceName = "traefik"
|
||||
# addEntryPointsLabels = true
|
||||
# addRoutersLabels = true
|
||||
# addServicesLabels = true
|
||||
# [metrics.otlp.http]
|
||||
# endpoint = "http://signoz_otel-collector:4318/v1/metrics"
|
||||
# [metrics.otlp.grpc]
|
||||
# endpoint = "monitoring_alloy:4317"
|
||||
# insecure = true
|
||||
|
||||
# Let's Encrypt
|
||||
[certificatesResolvers.main.acme]
|
||||
email = "ma@coachhamburg.com"
|
||||
storage = "acme.json"
|
||||
# uncomment to use staging CA for testing
|
||||
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
# [certificatesResolvers.main.acme.tlsChallenge]
|
||||
[certificatesResolvers.main.acme.dnsChallenge]
|
||||
provider = "hetzner"
|
||||
# Uncomment to use HTTP validation, like a caveman!
|
||||
# [certificatesResolvers.main.acme.httpChallenge]
|
||||
# entryPoint = "http"
|
||||
|
||||
[providers]
|
||||
[providers.swarm]
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
exposedByDefault = false
|
||||
[providers.file]
|
||||
directory = "/etc/traefik/dynamic"
|
||||
watch = true
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
- name: TRAEFIK | Copy Stack Files
|
||||
copy:
|
||||
directory_mode: true
|
||||
src: traefik
|
||||
dest: "{{ ceph_volume }}"
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: TRAEFIK | Generate Compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ data_dir }}/traefik.yml"
|
||||
mode: 0644
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: TRAEFIK | Ensure permissions on acme.json
|
||||
ansible.builtin.file:
|
||||
path: "{{ data_dir }}/data/acme.json"
|
||||
mode: '0600'
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: TRAEFIK | traefik_public Netzwerk erstellen
|
||||
community.docker.docker_network:
|
||||
name: traefik_public
|
||||
driver: overlay
|
||||
state: present
|
||||
attachable: yes
|
||||
ipam_config:
|
||||
- subnet: '172.16.200.0/24'
|
||||
gateway: '172.16.200.1'
|
||||
run_once: true
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
|
||||
- name: TRAEFIK | Deploy app stack
|
||||
community.docker.docker_stack:
|
||||
state: present
|
||||
name: traefik
|
||||
compose:
|
||||
- "{{ data_dir }}/traefik.yml"
|
||||
delegate_to: "{{ groups['managers'][0] }}"
|
||||
run_once: true
|
||||
@@ -0,0 +1,52 @@
|
||||
services:
|
||||
app:
|
||||
image: traefik:v3.6.2
|
||||
ports:
|
||||
- target: 80
|
||||
published: 80
|
||||
protocol: tcp
|
||||
mode: host
|
||||
- target: 443
|
||||
published: 443
|
||||
protocol: tcp
|
||||
mode: host
|
||||
- target: 8080
|
||||
published: 8080
|
||||
protocol: tcp
|
||||
environment:
|
||||
- HETZNER_API_TOKEN={{ hetzner_api_key }}
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- {{ data_dir }}/config:/etc/traefik
|
||||
- {{ data_dir }}/data/logs:/logs
|
||||
- {{ data_dir }}/data/acme.json:/acme.json
|
||||
# healthcheck:
|
||||
# test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||
# timeout: 1s
|
||||
# interval: 10s
|
||||
# retries: 3
|
||||
# start_period: 10s
|
||||
networks:
|
||||
- {{ traefik_public_net }}
|
||||
# Global mode makes an instance of traefik listen on _every_ node, so that regardless of which
|
||||
# node the request arrives on, it'll be forwarded to the correct backend service.
|
||||
deploy:
|
||||
mode: global
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.swarm.network={{ traefik_public_net }}"
|
||||
- "traefik.http.routers.api.rule=Host(`{{ subdomain }}.{{ main_domain }}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||
- "traefik.http.routers.api.entrypoints=https"
|
||||
{% if use_authentik %} - "traefik.http.routers.api.middlewares=authentik@file"{% endif %}
|
||||
# - "traefik.http.routers.api.tls.domains[0].main={{ main_domain }}"
|
||||
# - "traefik.http.routers.api.tls.domains[0].sans=*.{{ main_domain }}"
|
||||
- "traefik.http.routers.api.tls=true"
|
||||
- "traefik.http.routers.api.tls.certresolver=main"
|
||||
- "traefik.http.routers.api.service=api@internal"
|
||||
- "traefik.http.services.dummy.loadbalancer.server.port=9999"
|
||||
placement:
|
||||
constraints: [node.role == manager]
|
||||
|
||||
networks:
|
||||
{{ traefik_public_net }}:
|
||||
external: true
|
||||
@@ -0,0 +1,3 @@
|
||||
subdomain: router
|
||||
use_authentik: false
|
||||
data_dir: "{{ ceph_volume }}/traefik"
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
- name: FIREWALL | UFW auf Standardeinstellungen zurücksetzen
|
||||
community.general.ufw:
|
||||
state: reset
|
||||
|
||||
- name: FIREWALL | Standardmäßig allen ausgehenden Traffic erlauben
|
||||
community.general.ufw:
|
||||
direction: outgoing
|
||||
policy: allow
|
||||
|
||||
- name: FIREWALL | Standardmäßig allen eingehenden Traffic blockieren
|
||||
community.general.ufw:
|
||||
direction: incoming
|
||||
policy: deny
|
||||
|
||||
- name: FIREWALL | Eingehenden SSH-Traffic auf öffentlichem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ ssh_port }}"
|
||||
proto: tcp
|
||||
interface: "{{ public_interface }}"
|
||||
direction: in
|
||||
|
||||
- name: FIREWALL | Eingehenden SSH-Traffic auf privatem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ ssh_port }}"
|
||||
proto: tcp
|
||||
interface: "{{ private_interface }}"
|
||||
direction: in
|
||||
|
||||
- name: FIREWALL | Eingehenden HTTP/HTTPS-Traffic auf öffentlichem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item.port }}"
|
||||
proto: "{{ item.proto }}"
|
||||
interface: "{{ public_interface }}"
|
||||
direction: in
|
||||
with_items:
|
||||
- { port: '80', proto: 'tcp' }
|
||||
- { port: '443', proto: 'tcp' }
|
||||
|
||||
- name: FIREWALL | Ceph Monitor Ports auf privatem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
interface: "{{ private_interface }}"
|
||||
direction: in
|
||||
with_items:
|
||||
- '3300'
|
||||
- '6789'
|
||||
|
||||
- name: FIREWALL | Ceph OSD/MGR Port-Range auf öffentlichem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "6800:7568"
|
||||
proto: tcp
|
||||
interface: "{{ private_interface }}"
|
||||
direction: in
|
||||
|
||||
- name: FIREWALL | Docker Swarm Management Ports auf privatem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "2377"
|
||||
proto: tcp
|
||||
interface: "{{ private_interface }}"
|
||||
direction: in
|
||||
|
||||
- name: FIREWALL | Docker Swarm Discovery/Network Ports auf privatem Interface erlauben
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item.port }}"
|
||||
proto: "{{ item.proto }}"
|
||||
interface: "{{ private_interface }}"
|
||||
direction: in
|
||||
with_items:
|
||||
- { port: '7946', proto: 'tcp' }
|
||||
- { port: '7946', proto: 'udp' }
|
||||
- { port: '4789', proto: 'udp' }
|
||||
|
||||
- name: FIREWALL | UFW aktivieren
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
Reference in New Issue
Block a user