commit 4bbffd8a3dd401ee91ea0c3d1f54523dd8fbe3e5 Author: Andreas Martin Date: Mon Jan 19 12:27:03 2026 +0100 init Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19c2705 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/* +files/image_digests.yml +.vault-pass \ No newline at end of file diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..fc6a46d --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,140 @@ +[defaults] +#inventory = /etc/ansible/hosts +inventory = inventory.yaml +#library = ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules +#module_utils = ~/.ansible/plugins/module_utils:/usr/share/ansible/plugins/module_utils +#remote_tmp = ~/.ansible/tmp +#local_tmp = ~/.ansible/tmp +#forks = 5 +#poll_interval = 0.001 +#ask_pass = False +#transport = smart + + +host_key_checking = False +remote_user = root + +# private_key_file = ssh/ssh_deploy_private +vault_password_file = .vault-pass + + +[inventory] +# List of enabled inventory plugins and the order in which they are used. +#enable_plugins = host_list, script, auto, yaml, ini, toml + +# Ignore these extensions when parsing a directory as inventory source +#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry + +# ignore files matching these patterns when parsing a directory as inventory source +#ignore_patterns= + +# If 'True' unparsed inventory sources become fatal errors, otherwise they are warnings. +#unparsed_is_failed = False + + +[privilege_escalation] +#become = False +#become_method = sudo +#become_ask_pass = False + + +## Connection Plugins ## + +# Settings for each connection plugin go under a section titled '[[plugin_name]_connection]' +# To view available connection plugins, run ansible-doc -t connection -l +# To view available options for a connection plugin, run ansible-doc -t connection [plugin_name] +# https://docs.ansible.com/ansible/latest/plugins/connection.html + +[paramiko_connection] +#record_host_keys=False +#pty = False + +#look_for_keys = False +#host_key_auto_add = True + + +[ssh_connection] +#ssh_args = -F ssh/config +#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s +#control_path_dir = ~/.ansible/cp + +#control_path = +#pipelining = False +#scp_if_ssh = smart +#transfer_method = smart +#sftp_batch_mode = False +#usetty = True +#retries = 3 + + +[persistent_connection] +#connect_timeout = 30 +#command_timeout = 30 +[sudo_become_plugin] +#flags = -H -S -n +#user = root + + +[selinux] +# file systems that require special treatment when dealing with security context +# the default behaviour that copies the existing context or uses the user default +# needs to be changed to use the file system dependent context. +#special_context_filesystems=fuse,nfs,vboxsf,ramfs,9p,vfat + +# Set this to True to allow libvirt_lxc connections to work without SELinux. +#libvirt_lxc_noseclabel = False + + +[colors] +#highlight = white +#verbose = blue +#warn = bright purple +#error = red +#debug = dark gray +#deprecate = purple +#skip = cyan +#unreachable = red +#ok = green +#changed = yellow +#diff_add = green +#diff_remove = red +#diff_lines = cyan + + +[diff] +# Always print diff when running ( same as always running with -D/--diff ) +#always = False + +# Set how many context lines to show in diff +#context = 3 + +[galaxy] +# Controls whether the display wheel is shown or not +#display_progress= + +# Validate TLS certificates for Galaxy server +#ignore_certs = False + +# Role or collection skeleton directory to use as a template for +# the init action in ansible-galaxy command +#role_skeleton= + +# Patterns of files to ignore inside a Galaxy role or collection +# skeleton directory +#role_skeleton_ignore="^.git$", "^.*/.git_keep$" + +# Galaxy Server URL +#server=https://galaxy.ansible.com + +# A list of Galaxy servers to use when installing a collection. +#server_list=automation_hub, release_galaxy + +# Server specific details which are mentioned in server_list +#[galaxy_server.automation_hub] +#url=https://cloud.redhat.com/api/automation-hub/ +#auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token +#token=my_ah_token +# +#[galaxy_server.release_galaxy] +#url=https://galaxy.ansible.com/ +#token=my_token diff --git a/build.yml b/build.yml new file mode 100644 index 0000000..587018d --- /dev/null +++ b/build.yml @@ -0,0 +1,122 @@ +--- +- name: Lese Docker-Compose Images und erstelle Digest-Datei + hosts: localhost + gather_facts: false + vars: + compose_file: "files/docker-compose.yml" + digest_file: "files/image_digests.yml" + target_arch: "amd64" + + tasks: + - name: Prüfe, ob docker-compose.yml existiert + ansible.builtin.stat: + path: "{{ compose_file }}" + register: compose_stat + failed_when: not compose_stat.stat.exists + + - name: Lese docker-compose.yml + ansible.builtin.include_vars: + file: "{{ compose_file }}" + name: compose_data + + - name: Extrahiere Service-Namen und Images + ansible.builtin.set_fact: + service_images: "{{ compose_data.services | dict2items }}" + + - name: Zeige gefundene Services und Images + ansible.builtin.debug: + msg: "Service: {{ item.key }} → Image: {{ item.value.image }}" + loop: "{{ service_images }}" + when: item.value.image is defined + + - name: Hole Manifest-Liste für jedes Image + ansible.builtin.command: > + skopeo inspect docker://{{ item.value.image }} --raw + register: manifests + loop: "{{ service_images }}" + when: item.value.image is defined + changed_when: false + failed_when: false + + - name: Extrahiere amd64-Digest für jedes Image + ansible.builtin.set_fact: + service_digests: "{{ service_digests | default({}) | combine({item.item.key: digest_info}) }}" + loop: "{{ manifests.results }}" + when: + - item.item.value.image is defined + - item.rc == 0 + - digest != 'N/A' + vars: + manifest_json: "{{ item.stdout | from_json }}" + image_name: "{{ item.item.value.image.split(':')[0] }}" + digest: >- + {%- if manifest_json.manifests is defined -%} + {%- set ns = namespace(found='') -%} + {%- for m in manifest_json.manifests -%} + {%- if m.platform.architecture == target_arch and m.platform.os == 'linux' -%} + {%- set ns.found = m.digest -%} + {%- endif -%} + {%- endfor -%} + {{ ns.found if ns.found else 'N/A' }} + {%- elif manifest_json.config is defined and manifest_json.config.digest is defined -%} + {{ manifest_json.config.digest }} + {%- else -%} + N/A + {%- endif -%} + digest_info: + image: "{{ image_name }}@{{ digest }}" + + - name: Zeige Service-Digest-Mapping + ansible.builtin.debug: + msg: "Service: {{ item.key }} → Image mit Digest: {{ item.value.image }}" + loop: "{{ service_digests | dict2items }}" + when: service_digests is defined + + - name: Erstelle image_digests.yml + ansible.builtin.copy: + content: | + # Automatisch generiert von Ansible + # Verwendung: docker-compose -f docker-compose.yml -f image_digests.yml up -d + + services: + {% for service_name, service_config in service_digests.items() %} + {{ service_name }}: + image: {{ service_config.image }} + {% endfor %} + dest: "{{ digest_file }}" + when: service_digests is defined and service_digests | length > 0 + + - name: Zeige Erfolgsmeldung + ansible.builtin.debug: + msg: | + ✅ Digest-Datei wurde erstellt: {{ digest_file }} + + Verwendung: + docker-compose -f {{ compose_file }} -f {{ digest_file }} up -d + + Services mit Digests: + {% for service_name, service_config in service_digests.items() %} + - {{ service_name }}: {{ service_config.image }} + {% endfor %} + when: service_digests is defined and service_digests | length > 0 + + - name: Warnung, falls keine Digests gefunden wurden + ansible.builtin.debug: + msg: "⚠️ Keine gültigen Digests für {{ target_arch }} gefunden!" + when: service_digests is not defined or service_digests | length == 0 + +# create compose config + - name: Erstelle Docker Compose Konfigurationsdatei + ansible.builtin.shell: | + test -d build || mkdir build && + docker compose -f files/docker-compose.yml -f files/image_digests.yml config + register: compose_config + + - name: Speichere Docker Compose Konfigurationsdatei + ansible.builtin.copy: + content: "{{ compose_config.stdout }}" + dest: build/docker-compose.yml + backup: true + mode: '0644' + +... diff --git a/deploy.yml b/deploy.yml new file mode 100644 index 0000000..f6e577f --- /dev/null +++ b/deploy.yml @@ -0,0 +1,12 @@ +--- +- name: deploy Docker-Compose + hosts: localhost + gather_facts: no + + tasks: + - name: Docker compose up + community.docker.docker_compose_v2: + project_src: build + state: present + +... diff --git a/files/docker-compose.yml b/files/docker-compose.yml new file mode 100644 index 0000000..1cd31c0 --- /dev/null +++ b/files/docker-compose.yml @@ -0,0 +1,36 @@ +# compose.yml + +services: + traefik: + image: traefik:v3.6.4 + container_name: traefik + command: + # API und Dashboard aktivieren + - "--api.insecure=true" + - "--api.dashboard=true" + # Docker Provider aktivieren + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + # Entrypoints definieren + - "--entrypoints.web.address=:80" + ports: + - "80:80" # HTTP + - "8080:8080" # Traefik Dashboard + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - traefik-net + + whoami: + image: traefik/whoami + container_name: whoami + labels: + - "traefik.enable=true" + - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" + - "traefik.http.routers.whoami.entrypoints=web" + networks: + - traefik-net + +networks: + traefik-net: + name: traefik-net diff --git a/inventory.yaml b/inventory.yaml new file mode 100644 index 0000000..ece60a1 --- /dev/null +++ b/inventory.yaml @@ -0,0 +1,4 @@ +all: + hosts: + localhost: + ansible_connection: local \ No newline at end of file