ci: deploy application to target via Ansible
CI / smoke-test (push) Failing after 32s

This commit is contained in:
Mikhail Lobanov
2026-10-21 11:59:49 +03:00
parent 5a21852135
commit 7d656c1ae6
9 changed files with 211 additions and 74 deletions
+14 -74
View File
@@ -68,87 +68,27 @@ jobs:
docker push mikhaillobanov/devops-platform:latest docker push mikhaillobanov/devops-platform:latest
docker push mikhaillobanov/devops-platform:${GITHUB_SHA} docker push mikhaillobanov/devops-platform:${GITHUB_SHA}
- name: Deploy application - name: Deploy application via Ansible
if: github.event_name == 'push' && github.ref == 'refs/heads/main' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env: env:
ANSIBLE_PRIVATE_KEY: ${{ secrets.ID_RSA }}
IMAGE: mikhaillobanov/devops-platform:${{ github.sha }} IMAGE: mikhaillobanov/devops-platform:${{ github.sha }}
run: | run: |
set -eu set -eu
OLD_IMAGE=$(docker inspect --format='{{.Config.Image}}' devops-platform) apt-get update
apt-get install -y ansible openssh-client
docker pull "$IMAGE" mkdir -p /root/.ssh
docker stop devops-platform chmod 700 /root/.ssh
docker rm devops-platform
if IMAGE="$IMAGE" docker compose up -d --no-build app; then printf '%s\n' "$ANSIBLE_PRIVATE_KEY" > /root/.ssh/id_rsa
echo "New container started" chmod 600 /root/.ssh/id_rsa
else
echo "Failed to start new container; restoring old image"
docker rm -f devops-platform || true
if ! IMAGE="$OLD_IMAGE" docker compose up -d --no-build app; then
echo "ERROR: rollback failed; manual recovery required"
exit 1
fi
ROLLBACK_HEALTHY=false ssh-keyscan -H 178.154.209.109 >> /root/.ssh/known_hosts
chmod 600 /root/.ssh/known_hosts
for attempt in $(seq 1 15); do ansible-playbook \
if docker exec devops-platform python -c \ -i ansible/inventory.ini \
"import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2); assert r.status == 200"; then ansible/playbooks/deploy_app.yml \
ROLLBACK_HEALTHY=true -e "deploy_image=$IMAGE"
break
fi
sleep 2
done
if [ "$ROLLBACK_HEALTHY" = true ]; then
echo "Rollback successful; previous version is healthy"
else
echo "ERROR: rollback container failed health check; manual recovery required"
fi
exit 1
fi
HEALTHY=false
for attempt in $(seq 1 15); do
if docker exec devops-platform python -c \
"import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2); assert r.status == 200"; then
HEALTHY=true
break
fi
sleep 2
done
if [ "$HEALTHY" = true ]; then
echo "Deployment successful"
else
echo "Health check failed; rolling back"
docker stop devops-platform || true
docker rm devops-platform || true
if ! IMAGE="$OLD_IMAGE" docker compose up -d --no-build app; then
echo "ERROR: rollback failed; manual recovery required"
exit 1
fi
ROLLBACK_HEALTHY=false
for attempt in $(seq 1 15); do
if docker exec devops-platform python -c \
"import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2); assert r.status == 200"; then
ROLLBACK_HEALTHY=true
break
fi
sleep 2
done
if [ "$ROLLBACK_HEALTHY" = true ]; then
echo "Rollback successful; previous version is healthy"
else
echo "ERROR: rollback container failed health check; manual recovery required"
fi
exit 1
fi
+2
View File
@@ -0,0 +1,2 @@
[targets]
ansible-target ansible_host=178.154.209.109 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa
+2
View File
@@ -0,0 +1,2 @@
[targets]
ansible-target ansible_host=178.154.209.109 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa
@@ -0,0 +1,56 @@
---
- name: Configure Nginx reverse proxy
hosts: targets
become: true
tasks:
- name: Deploy Nginx reverse proxy configuration
ansible.builtin.template:
src: "{{ playbook_dir }}/../templates/devops-platform.conf.j2"
dest: /etc/nginx/sites-available/devops-platform
owner: root
group: root
mode: '0644'
notify: Reload Nginx
- name: Enable DevOps application site
ansible.builtin.file:
src: /etc/nginx/sites-available/devops-platform
dest: /etc/nginx/sites-enabled/devops-platform
state: link
notify: Reload Nginx
- name: Disable default Nginx site
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Reload Nginx
- name: Validate Nginx configuration
ansible.builtin.command:
cmd: nginx -t
changed_when: false
- name: Apply Nginx changes now
ansible.builtin.meta: flush_handlers
- name: Verify application through Nginx
ansible.builtin.uri:
url: http://127.0.0.1/health
method: GET
status_code: 200
return_content: true
register: proxy_health
retries: 5
delay: 2
until: proxy_health.status == 200
- name: Show proxied application health
ansible.builtin.debug:
var: proxy_health.json
handlers:
- name: Reload Nginx
ansible.builtin.service:
name: nginx
state: reloaded
+56
View File
@@ -0,0 +1,56 @@
---
- name: Deploy DevOps application
hosts: targets
become: true
vars:
deploy_image: "mikhaillobanov/devops-platform:latest"
tasks:
- name: Create application directory
ansible.builtin.file:
path: /opt/devops-platform
state: directory
owner: root
group: root
mode: '0755'
- name: Copy Docker Compose configuration
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../compose.yaml"
dest: /opt/devops-platform/compose.yaml
owner: root
group: root
mode: '0644'
- name: Deploy application with Docker Compose
ansible.builtin.command:
cmd: docker compose up -d --pull always
chdir: /opt/devops-platform
environment:
IMAGE: "{{ deploy_image }}"
register: deploy_result
changed_when: >
'Created' in deploy_result.stdout or
'Started' in deploy_result.stdout or
'Pulled' in deploy_result.stdout or
'Recreated' in deploy_result.stdout
- name: Wait for application health endpoint
ansible.builtin.uri:
url: http://127.0.0.1:8000/health
method: GET
status_code: 200
return_content: true
register: health_result
retries: 10
delay: 3
until: health_result.status == 200
- name: Show deployed image
ansible.builtin.debug:
msg: "Deployed image: {{ deploy_image }}"
- name: Show application health
ansible.builtin.debug:
var: health_result.json
+28
View File
@@ -0,0 +1,28 @@
---
- name: Install Docker Engine
hosts: targets
become: true
tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install Docker and Compose plugin
ansible.builtin.apt:
name:
- docker.io
- docker-compose-v2
state: present
- name: Ensure Docker is running
ansible.builtin.service:
name: docker
state: started
enabled: true
- name: Verify Docker Compose
ansible.builtin.command:
cmd: docker compose version
changed_when: false
+25
View File
@@ -0,0 +1,25 @@
---
- name: Install and configure Nginx
hosts: targets
become: true
tasks:
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Ensure Nginx is running
ansible.builtin.service:
name: nginx
state: started
enabled: true
- name: Deploy index page
ansible.builtin.template:
src: ../templates/index.html.j2
dest: /var/www/html/index.html
owner: root
group: root
mode: '0644'
+16
View File
@@ -0,0 +1,16 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>DevOps Lab</title>
</head>
<body>
<h1>DevOps Lab</h1>
<p>Server: {{ inventory_hostname }}</p>
<p>Managed by Ansible</p>
</body>
</html>