Files
michaelschiemer/deployment/ansible/playbooks/build-initial-image.yml
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

433 lines
16 KiB
YAML

---
- name: Build and Push Initial Docker Image
hosts: production
become: no
gather_facts: yes
vars:
vault_file: "{{ playbook_dir }}/../secrets/production.vault.yml"
build_repo_path: "/home/deploy/michaelschiemer"
build_repo_url: "{{ git_repository_url_default | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
build_repo_branch_default: "main"
# Local repository path for cloning (temporary)
local_repo_path: "/tmp/michaelschiemer-build-{{ ansible_date_time.epoch }}"
# Check if local repository exists (project root)
local_repo_check_path: "{{ playbook_dir | default('') | dirname | dirname | dirname }}"
image_name: "{{ app_name | default('framework') }}"
image_tag_default: "latest"
registry_url: "{{ docker_registry | default('localhost:5000') }}"
registry_username: "{{ vault_docker_registry_username | default(docker_registry_username_default | default('admin')) }}"
registry_password: "{{ vault_docker_registry_password | default('') }}"
pre_tasks:
- name: Verify vault file exists
ansible.builtin.stat:
path: "{{ vault_file }}"
register: vault_stat
delegate_to: localhost
become: no
- name: Load vault secrets
ansible.builtin.include_vars:
file: "{{ vault_file }}"
when: vault_stat.stat.exists
no_log: yes
ignore_errors: yes
delegate_to: localhost
become: no
- name: Verify registry password is set
ansible.builtin.fail:
msg: |
Registry password is required!
Please set vault_docker_registry_password in:
{{ vault_file }}
Or pass it via extra vars:
-e "registry_password=your-password"
when: registry_password | string | trim == ''
tasks:
- name: Set build variables
ansible.builtin.set_fact:
build_repo_branch: "{{ build_repo_branch | default(build_repo_branch_default) }}"
image_tag: "{{ build_image_tag | default(image_tag_default) }}"
- name: Display build information
ansible.builtin.debug:
msg: |
Building Docker Image:
- Repository: {{ build_repo_url }}
- Branch: {{ build_repo_branch }}
- Build Path: {{ build_repo_path }}
- Registry: {{ registry_url }}
- Image: {{ image_name }}:{{ image_tag }}
- Username: {{ registry_username }}
- name: Check if local repository exists (project root)
ansible.builtin.stat:
path: "{{ local_repo_check_path }}/.git"
delegate_to: localhost
register: local_repo_exists
become: no
- name: Configure Git to skip SSL verification for git.michaelschiemer.de (local)
ansible.builtin.command: |
git config --global http.https://git.michaelschiemer.de.sslVerify false
delegate_to: localhost
changed_when: false
failed_when: false
become: no
when: not local_repo_exists.stat.exists
- name: Determine Git URL with authentication if token is available
ansible.builtin.set_fact:
git_repo_url_with_auth: >-
{%- if vault_git_token is defined and vault_git_token | string | trim != '' -%}
https://{{ vault_git_token }}@git.michaelschiemer.de/michael/michaelschiemer.git
{%- elif vault_git_username is defined and vault_git_username | string | trim != '' and vault_git_password is defined and vault_git_password | string | trim != '' -%}
https://{{ vault_git_username }}:{{ vault_git_password }}@git.michaelschiemer.de/michael/michaelschiemer.git
{%- else -%}
{{ build_repo_url }}
{%- endif -%}
no_log: yes
- name: Debug Git URL (without credentials)
ansible.builtin.debug:
msg: |
Git Repository Configuration:
- Original URL: {{ build_repo_url }}
- Local repo exists: {{ 'YES' if local_repo_exists.stat.exists else 'NO' }}
- Local repo path: {{ local_repo_check_path }}
- Using authentication: {{ 'YES' if (vault_git_token is defined and vault_git_token | string | trim != '') or (vault_git_username is defined and vault_git_username | string | trim != '') else 'NO' }}
- Auth method: {{ 'Token' if (vault_git_token is defined and vault_git_token | string | trim != '') else 'Username/Password' if (vault_git_username is defined and vault_git_username | string | trim != '') else 'None' }}
no_log: yes
- name: Use existing local repository or clone to temporary location
block:
- name: Clone repository to temporary location (local)
ansible.builtin.git:
repo: "{{ git_repo_url_with_auth }}"
dest: "{{ local_repo_path }}"
version: "{{ build_repo_branch }}"
force: yes
update: yes
delegate_to: localhost
register: git_result
changed_when: git_result.changed
environment:
GIT_SSL_NO_VERIFY: "1"
no_log: yes
when: not local_repo_exists.stat.exists
- name: Set local repository path to existing project root
ansible.builtin.set_fact:
source_repo_path: "{{ local_repo_check_path }}"
when: local_repo_exists.stat.exists
- name: Set local repository path to cloned temporary location
ansible.builtin.set_fact:
source_repo_path: "{{ local_repo_path }}"
when: not local_repo_exists.stat.exists
- name: Display repository source
ansible.builtin.debug:
msg: |
Repository source:
- Using existing local repo: {{ 'YES' if local_repo_exists.stat.exists else 'NO' }}
- Source path: {{ source_repo_path }}
- Branch: {{ build_repo_branch }}
- name: Ensure build directory exists on server
ansible.builtin.file:
path: "{{ build_repo_path }}"
state: directory
mode: '0755'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: Copy repository to server (excluding .git and build artifacts)
ansible.builtin.synchronize:
src: "{{ source_repo_path }}/"
dest: "{{ build_repo_path }}/"
delete: no
recursive: yes
rsync_opts:
- "--chmod=D755,F644"
- "--exclude=.git"
- "--exclude=.gitignore"
- "--exclude=node_modules"
- "--exclude=vendor"
- "--exclude=.env"
- "--exclude=.env.*"
- "--exclude=*.log"
- "--exclude=.idea"
- "--exclude=.vscode"
- "--exclude=.DS_Store"
- "--exclude=*.swp"
- "--exclude=*.swo"
- "--exclude=*~"
- "--exclude=.phpunit.result.cache"
- "--exclude=coverage"
- "--exclude=.phpunit.cache"
- "--exclude=public/assets"
- "--exclude=storage/logs"
- "--exclude=storage/framework/cache"
- "--exclude=storage/framework/sessions"
- "--exclude=storage/framework/views"
- name: Clean up temporary cloned repository
ansible.builtin.file:
path: "{{ local_repo_path }}"
state: absent
delegate_to: localhost
become: no
when:
- not local_repo_exists.stat.exists
- local_repo_path is defined
- name: Check if Dockerfile.production exists on server
ansible.builtin.stat:
path: "{{ build_repo_path }}/Dockerfile.production"
register: dockerfile_stat
- name: Fail if Dockerfile.production not found
ansible.builtin.fail:
msg: |
Dockerfile.production not found at {{ build_repo_path }}/Dockerfile.production
Please verify:
1. The repository was copied successfully to {{ build_repo_path }}
2. The Dockerfile.production file exists in the repository
3. The source repository path is correct: {{ source_repo_path }}
when: not dockerfile_stat.stat.exists
- name: Check if entrypoint script exists on server
ansible.builtin.stat:
path: "{{ build_repo_path }}/docker/entrypoint.sh"
register: entrypoint_stat
- name: Display entrypoint script status
ansible.builtin.debug:
msg: |
Entrypoint Script Check:
- Path: {{ build_repo_path }}/docker/entrypoint.sh
- Exists: {{ entrypoint_stat.stat.exists | default(false) }}
{% if entrypoint_stat.stat.exists %}
- Mode: {{ entrypoint_stat.stat.mode | default('unknown') }}
- Size: {{ entrypoint_stat.stat.size | default(0) }} bytes
{% endif %}
when: not ansible_check_mode
- name: Fail if entrypoint script not found
ansible.builtin.fail:
msg: |
Entrypoint script not found at {{ build_repo_path }}/docker/entrypoint.sh
This file is required for the Docker image build!
Please verify:
1. The file exists in the source repository: {{ source_repo_path }}/docker/entrypoint.sh
2. The rsync operation copied the file successfully
when: not entrypoint_stat.stat.exists
- name: Convert entrypoint script to LF line endings
ansible.builtin.shell: |
sed -i 's/\r$//' "{{ build_repo_path }}/docker/entrypoint.sh"
when: entrypoint_stat.stat.exists
changed_when: false
failed_when: false
- name: Verify entrypoint script has LF line endings
ansible.builtin.shell: |
if head -1 "{{ build_repo_path }}/docker/entrypoint.sh" | od -c | grep -q "\\r"; then
echo "CRLF_DETECTED"
else
echo "LF_ONLY"
fi
register: line_ending_check
changed_when: false
when: entrypoint_stat.stat.exists
- name: Display line ending check result
ansible.builtin.debug:
msg: |
Entrypoint Script Line Endings:
- Status: {{ line_ending_check.stdout | default('unknown') }}
{% if 'CRLF_DETECTED' in line_ending_check.stdout %}
⚠️ WARNING: Script still has CRLF line endings after conversion attempt!
{% else %}
✅ Script has LF line endings
{% endif %}
when:
- entrypoint_stat.stat.exists
- not ansible_check_mode
- name: Login to Docker registry
community.docker.docker_login:
registry_url: "{{ registry_url }}"
username: "{{ registry_username }}"
password: "{{ registry_password }}"
no_log: yes
register: login_result
- name: Verify registry login
ansible.builtin.debug:
msg: "✅ Successfully logged in to {{ registry_url }}"
when: not login_result.failed | default(false)
- name: Fail if registry login failed
ansible.builtin.fail:
msg: |
Failed to login to Docker registry!
Registry: {{ registry_url }}
Username: {{ registry_username }}
Please verify:
1. The registry is running and accessible
2. The username and password are correct
3. The registry URL is correct
when: login_result.failed | default(false)
- name: Verify Docker Buildx is available
ansible.builtin.command: docker buildx version
register: buildx_check
changed_when: false
failed_when: false
- name: Warn if Buildx is not available
ansible.builtin.debug:
msg: |
⚠️ Docker Buildx not found. BuildKit features may not work.
Install with: apt-get install docker-buildx-plugin
when: buildx_check.rc != 0
- name: Set build cache option
ansible.builtin.set_fact:
build_no_cache: "{{ build_no_cache | default('false') | bool }}"
- name: Build and push Docker image with BuildKit
ansible.builtin.shell: |
set -e
BUILD_ARGS=""
{% if build_no_cache | bool %}
BUILD_ARGS="--no-cache"
{% endif %}
DOCKER_BUILDKIT=1 docker buildx build \
--platform linux/amd64 \
--file {{ build_repo_path }}/Dockerfile.production \
--tag {{ registry_url }}/{{ image_name }}:{{ image_tag }} \
--push \
--progress=plain \
$BUILD_ARGS \
{{ build_repo_path }}
register: build_result
environment:
DOCKER_BUILDKIT: "1"
changed_when: build_result.rc == 0
failed_when: build_result.rc != 0
async: 3600
poll: 10
- name: Display build result
ansible.builtin.debug:
msg: |
Build result:
- Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}
- Return code: {{ build_result.rc | default('unknown') }}
- Changed: {{ build_result.changed | default(false) }}
- Failed: {{ build_result.failed | default(false) }}
when: build_result is defined
- name: Display build output on failure
ansible.builtin.debug:
msg: |
Build failed! Output:
{{ build_result.stdout_lines | default([]) | join('\n') }}
Error output:
{{ build_result.stderr_lines | default([]) | join('\n') }}
when:
- build_result is defined
- build_result.rc | default(0) != 0
- name: Verify image exists locally
ansible.builtin.command: |
docker image inspect {{ registry_url }}/{{ image_name }}:{{ image_tag }}
register: image_check
changed_when: false
failed_when: false
- name: Verify entrypoint script in built image
shell: |
CONTAINER_ID=$(docker create {{ registry_url }}/{{ image_name }}:{{ image_tag }} 2>/dev/null) && \
if docker cp $CONTAINER_ID:/usr/local/bin/entrypoint.sh /tmp/entrypoint_verify.sh 2>&1; then \
echo "FILE_EXISTS"; \
ls -la /tmp/entrypoint_verify.sh; \
head -3 /tmp/entrypoint_verify.sh; \
file /tmp/entrypoint_verify.sh; \
rm -f /tmp/entrypoint_verify.sh; \
else \
echo "FILE_NOT_FOUND"; \
fi && \
docker rm $CONTAINER_ID >/dev/null 2>&1 || true
register: image_entrypoint_check
changed_when: false
failed_when: false
- name: Display entrypoint verification result
debug:
msg: |
==========================================
Entrypoint Script Verification in Built Image
==========================================
Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}
Verification Result:
{{ image_entrypoint_check.stdout | default('Check failed') }}
{% if 'FILE_NOT_FOUND' in image_entrypoint_check.stdout %}
⚠️ CRITICAL: Entrypoint script NOT FOUND in built image!
This means the Docker build did not copy the entrypoint script.
Possible causes:
1. The COPY command in Dockerfile.production failed silently
2. The docker/entrypoint.sh file was not in the build context
3. There was an issue with the multi-stage build
Please check:
1. Build logs above for COPY errors
2. Verify docker/entrypoint.sh exists: ls -la {{ build_repo_path }}/docker/entrypoint.sh
3. Rebuild with verbose output to see COPY step
{% elif 'FILE_EXISTS' in image_entrypoint_check.stdout %}
✅ Entrypoint script found in built image
{% endif %}
==========================================
- name: Display image information
ansible.builtin.debug:
msg: |
✅ Image built and pushed successfully!
Registry: {{ registry_url }}
Image: {{ image_name }}:{{ image_tag }}
Local: {{ 'Available' if image_check.rc == 0 else 'Not found locally' }}
Next steps:
1. Run setup-infrastructure.yml to deploy the application stack
2. Or manually deploy using docker-compose
when: image_check.rc == 0
- name: Warn if image not found locally
ansible.builtin.debug:
msg: |
⚠️ Image was pushed but not found locally.
This is normal if the image was pushed to a remote registry.
Verify the image exists in the registry:
curl -u {{ registry_username }}:{{ registry_password }} http://{{ registry_url }}/v2/{{ image_name }}/tags/list
when: image_check.rc != 0