From 75a9d3b5c8193788d220029ebf3d4134f99f7a6c Mon Sep 17 00:00:00 2001 From: tysker Date: Mon, 5 Jan 2026 10:40:22 +0100 Subject: [PATCH 1/4] feat(terraform) add app firewall inbound rule for port 80 --- infrastructure/terraform/main.tf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/infrastructure/terraform/main.tf b/infrastructure/terraform/main.tf index c0cee22..082e5d3 100644 --- a/infrastructure/terraform/main.tf +++ b/infrastructure/terraform/main.tf @@ -123,6 +123,14 @@ resource "linode_firewall" "monitoring_fw" { ipv4 = ["192.168.0.0/16"] } + inbound { + label = "allow-http" + action = "ACCEPT" + protocol = "TCP" + ports = "80" + ipv4 = ["0.0.0.0/0"] + } + inbound_policy = "DROP" outbound_policy = "ACCEPT" From bd1a545f917e92496e20128202f26e23d42877cf Mon Sep 17 00:00:00 2001 From: tysker Date: Mon, 5 Jan 2026 11:04:34 +0100 Subject: [PATCH 2/4] feat(ansible): deploy app container on app server --- ansible/group_vars/all.yml | 6 +++++ ansible/playbooks/deploy_app.yml | 6 +++++ ansible/roles/deploy_app/tasks/main.yml | 32 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 ansible/playbooks/deploy_app.yml create mode 100644 ansible/roles/deploy_app/tasks/main.yml diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index e708103..11b8e0c 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -1,3 +1,9 @@ ansible_python_interpreter: /usr/bin/python3 devops_user: devops devops_public_key: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/linode.pub') }}" +app_image: "ghcr.io/tysker/cloud_devops_app:77ecd38" +app_container_name: "cloud-devops-app" +app_container_port: 5000 +app_public_port: 80 +ghcr_username: "tysker" +ghcr_token: "{{ lookup('env', 'GHCR_TOKEN') }}" diff --git a/ansible/playbooks/deploy_app.yml b/ansible/playbooks/deploy_app.yml new file mode 100644 index 0000000..def8e08 --- /dev/null +++ b/ansible/playbooks/deploy_app.yml @@ -0,0 +1,6 @@ +- name: Deploy Flask app container + hosts: app + gather_facts: true + become: true + roles: + - deploy_app diff --git a/ansible/roles/deploy_app/tasks/main.yml b/ansible/roles/deploy_app/tasks/main.yml new file mode 100644 index 0000000..8316bda --- /dev/null +++ b/ansible/roles/deploy_app/tasks/main.yml @@ -0,0 +1,32 @@ +- name: (Optional) Login to GHCR + community.docker.docker_login: + registry_url: ghcr.io + username: "{{ ghcr_username }}" + password: "{{ ghcr_token }}" + when: + - ghcr_username is defined + - ghcr_token is defined + - ghcr_token | length > 0 + +- name: Pull application image + community.docker.docker_image: + name: "{{ app_image }}" + source: pull + +- name: Ensure application container is running + community.docker.docker_container: + name: "{{ app_container_name }}" + image: "{{ app_image }}" + state: started + restart_policy: unless-stopped + ports: + - "{{ app_public_port }}:{{ app_container_port }}" + +- name: Wait for /health to return 200 on the server + ansible.builtin.uri: + url: "http://localhost/health" + status_code: 200 + register: healthcheck + retries: 10 + delay: 2 + until: healthcheck.status == 200 From 79c1e9bda23219f028282b16fefcc68aadde3b36 Mon Sep 17 00:00:00 2001 From: tysker Date: Mon, 5 Jan 2026 11:10:29 +0100 Subject: [PATCH 3/4] docs(readme): document Stage 9 app deployment --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 55aa5af..ffd8bb7 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,20 @@ for application deployment and monitoring. Docker is intentionally not installed on the jump server. +### Stage 9 — Application Deployment (Docker + Ansible) + +**What:** +Deployed the Flask application container to the application server using Ansible. + +**Why:** +A repeatable deployment reduces manual steps and ensures consistent environments. + +**How:** +- Pulled a pinned image tag from GHCR (`ghcr.io/tysker/cloud_devops_app:77ecd38`). +- Ran the container with `restart: unless-stopped`. +- Exposed HTTP on port 80 mapped to container port 5000. +- Added an Ansible health check against `/health`. + ### Access Model - Direct SSH access is allowed only to the jump server. From 0c20685bb30b876698a60837038e9e149767338b Mon Sep 17 00:00:00 2001 From: tysker Date: Mon, 5 Jan 2026 11:13:30 +0100 Subject: [PATCH 4/4] docs(readme): document Stage 9 app deployment --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffd8bb7..f801303 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ The project is built in incremental stages. Each stage adds a new DevOps capabil - Stage 6: Ansible bootstrap & access control - Stage 7: SSH hardening - Stage 8: Docker installation (via Ansible) -- Stage 9: Application deployment +- Stage 9: Application deployment - Stage 10: Monitoring stack (Prometheus & Grafana) - Stage 11: TLS certificates & reverse proxy @@ -328,7 +328,8 @@ A chronological log describing the work done in each stage. - Procced to Stage 6: Ansible bootstrap & access control - Procced to Stage 7: SSH hardening - Procced to Stage 8: Docker installation (via Ansible) -- Procced to Stage 9 Application deployment using Docker and GHCR +- Procced to Stage 9: Application deployment using Docker and GHCR +- Procced to Stage 10: Stage 10: Monitoring stack (Prometheus & Grafana) ## Git Workflow & Conventions