Skip to content
Open

Lab09 #4551

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0c0054e
feat: implement lab01 devops info service
BlazZ1t Jan 28, 2026
708b7cd
Merge pull request #1 from BlazZ1t/lab01
BlazZ1t Feb 4, 2026
556c051
Finished lab2
BlazZ1t Feb 4, 2026
362b183
Add tests and github actions workflow
BlazZ1t Feb 12, 2026
4723e53
Add workflow file
BlazZ1t Feb 12, 2026
d190f18
Fix github workflow file
BlazZ1t Feb 12, 2026
f68a57d
Adjust the pipeline, add documentation, update python_app/README.md
BlazZ1t Feb 12, 2026
76a8495
Adjust the pipeline again so it runs
BlazZ1t Feb 12, 2026
a72f9e2
finish lab 4
BlazZ1t Feb 19, 2026
19679a2
Update dockerfile to expose correct port
BlazZ1t Feb 25, 2026
53686d8
Update github workflow file
BlazZ1t Feb 25, 2026
0b27669
Just work
BlazZ1t Feb 25, 2026
589fac2
Fix pipeline
BlazZ1t Feb 25, 2026
c1d7f30
Fix pipeline
BlazZ1t Feb 25, 2026
023aa94
Fix dockerfile
BlazZ1t Feb 25, 2026
2fabf6d
Finish lab 5
BlazZ1t Feb 26, 2026
800d60b
finished with lab
BlazZ1t Mar 5, 2026
d552bf4
add github actions workflow
BlazZ1t Mar 5, 2026
eac3123
fix workflow
BlazZ1t Mar 5, 2026
3d8525a
skip lint
BlazZ1t Mar 5, 2026
ba02160
another fix
BlazZ1t Mar 5, 2026
0c99e8b
another fix
BlazZ1t Mar 5, 2026
941fd6a
Add json logging to python app
BlazZ1t Mar 12, 2026
78ea6ea
Adjust
BlazZ1t Mar 12, 2026
7e43688
Adjust github workflow
BlazZ1t Mar 12, 2026
6181dd6
Adjust github workflow
BlazZ1t Mar 12, 2026
2344f7f
Adjust github workflow
BlazZ1t Mar 12, 2026
9cae28b
Adjust lint in python app
BlazZ1t Mar 12, 2026
178daca
Finish lab07
BlazZ1t Mar 12, 2026
f6d60c9
Add prometheus client
BlazZ1t Mar 19, 2026
71033de
Fix linter errors
BlazZ1t Mar 19, 2026
ef556f6
Fix linter errors
BlazZ1t Mar 19, 2026
1237752
Adjust dependencies
BlazZ1t Mar 19, 2026
50dce8a
Finish lab8
BlazZ1t Mar 19, 2026
4f316cd
Adjust dockerfile
BlazZ1t Mar 28, 2026
515ec14
Adjust dockerfile
BlazZ1t Mar 28, 2026
d156232
Add new version of python app
BlazZ1t Mar 28, 2026
99d9f01
finish lab9
BlazZ1t May 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/ansible-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Ansible Deployment

on:
push:
branches: [ main, master ]
paths:
- 'ansible/**'
- '.github/workflows/ansible-deploy.yml'
pull_request:
branches: [ main, master ]
paths:
- 'ansible/**'

jobs:
# lint:
# name: Ansible Lint
# runs-on: ubuntu-latest

# steps:
# - name: Checkout code
# uses: actions/checkout@v4

# - name: Set up Python
# uses: actions/setup-python@v5
# with:
# python-version: '3.12'

# - name: Install dependencies
# run: |
# pip install ansible ansible-lint

# - name: Create Vault password file
# run: |
# echo "${{ secrets.VAULT_PASS }}" > ansible/.vault_pass
# chmod 600 ansible/.vault_pass

# - name: Run ansible-lint
# run: |
# cd ansible
# ansible-lint playbooks/*.yml

deploy:
name: Deploy Application
# needs: lint
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install Ansible and dependencies
run: |
pip install ansible
ansible-galaxy collection install community.docker

- name: Create Vault password file
run: |
echo "${{ secrets.VAULT_PASS }}" > ansible/.vault_pass
chmod 600 ansible/.vault_pass

- name: Run Ansible playbook
run: |
cd ansible
ansible-playbook playbooks/provision.yml
ansible-playbook playbooks/deploy.yml

- name: Verify deployment
run: |
docker ps
47 changes: 47 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Test Python App

on:
push:
paths:
- 'app_python/**'
- '.github/**'

jobs:
test:
name: Test/Lint app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.14
- name: Install dependencies
run: pip install -r app_python/requirements-dev.txt
- name: Run linter
run: flake8 app_python/app.py app_python/tests/
- name: Run pytest
run: pytest .
push:
name: Push docker image to Dockerhub
runs-on: ubuntu-latest
needs: test
steps:
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Get current date for versioning
uses: Kaven-Universe/github-action-current-date-time@v1
id: current-time
with:
format: "YYYY.MM.DD"
- name: Build and push docker image
uses: docker/build-push-action@v5
with:
context: "{{defaultContext}}:app_python/"
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops_app:latest, ${{ secrets.DOCKERHUB_USERNAME }}/devops_app:${{ steps.current-time.outputs.time }}



4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
test
test
ansible/.vault_pass
ansible/.secrets/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ansible.python.interpreterPath": "/usr/bin/python"
}
12 changes: 12 additions & 0 deletions ansible/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[defaults]
inventory = inventory/hosts.ini
roles_path = roles
host_key_checking = False
remote_user = ubuntu
retry_files_enabled = False
vault_password_file = .vault_pass

[privilege_escalation]
become = True
become_method = sudo
become_user = root
208 changes: 208 additions & 0 deletions ansible/docs/LAB05.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# LAB 05 — Ansible Fundamentals Documentation

## 1. Architecture Overview

- **Ansible version (control node):** `ansible [core 2.20.2]`
- **Target VM:** `blazz1t-vm` (`95.31.23.50`) from Lab 4
- **Target OS:** Ubuntu VM (Lab requirement: Ubuntu 22.04/24.04)
- **Execution model:** role-based playbooks (`provision.yml`, `deploy.yml`) with reusable roles in `roles/`

### Project Structure (implemented)

```text
ansible/
├── ansible.cfg
├── inventory/
│ ├── hosts.ini
│ └── group_vars/
│ └── all.yml # vaulted secrets for inventory scope
├── group_vars/
│ └── all.yml # vaulted secrets
├── playbooks/
│ ├── provision.yml
│ └── deploy.yml
├── roles/
│ ├── common/
│ │ ├── defaults/main.yml
│ │ └── tasks/main.yml
│ ├── docker/
│ │ ├── defaults/main.yml
│ │ ├── tasks/main.yml
│ │ └── handlers/main.yml
│ └── web_app/
│ ├── defaults/main.yml
│ ├── tasks/main.yml
│ └── handlers/main.yml
└── docs/
└── LAB05.md
```

### Why roles instead of monolithic playbooks?

Roles separate provisioning and deployment concerns, reduce duplication, and make each component reusable across environments. This also keeps playbooks thin and readable (`roles:` only), while task logic and defaults stay in dedicated role directories.

---

## 2. Roles Documentation

### Role: `common`

- **Purpose:** baseline OS preparation.
- **Tasks:**
- update apt cache (`cache_valid_time: 3600`)
- install essential packages (`python3-pip`, `curl`, `git`, `vim`, `htop`)
- **Variables (defaults):** `common_packages`
- **Handlers:** none
- **Dependencies:** none

### Role: `docker`

- **Purpose:** install and configure Docker runtime on Ubuntu.
- **Tasks:**
1. install apt prerequisites
2. create `/etc/apt/keyrings`
3. add Docker GPG key
4. add Docker apt repository
5. install Docker packages (`docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`, `docker-compose-plugin`)
6. install `python3-docker`
7. ensure Docker service is enabled and started
8. add target user to `docker` group
- **Variables (defaults):**
- `docker_apt_prerequisites`
- `docker_packages`
- `docker_python_package`
- `docker_manage_user`
- `docker_arch_map`, `docker_arch`
- **Handlers:** `restart docker`
- **Dependencies:** none (used after `common` in `provision.yml`)

### Role: `web_app`

- **Purpose:** authenticate to Docker Hub and deploy/update Python app container.
- **Tasks:**
1. Docker Hub login (`community.docker.docker_login`, `no_log: true`)
2. pull image (`community.docker.docker_image`, `source: pull`)
3. stop old container (if running)
4. remove old container
5. run new container (`5000:5000`, restart policy, env)
6. wait for app port
7. verify `/health` endpoint via `uri`
- **Variables (defaults):**
- `app_name`
- `docker_image`
- `docker_image_tag`
- `app_port`
- `app_container_name`
- `app_restart_policy`
- `app_environment`
- **Handlers:** `restart application container`
- **Dependencies:** Docker runtime from `docker` role

---

## 3. Idempotency Demonstration

### First run (`playbooks/provision.yml`)

Expected and observed behavior: tasks create/modify system state, so many tasks are marked `changed`.

![Provision first run](./provision_yml_first.png)

### Second run (`playbooks/provision.yml`)

Expected and observed behavior: no configuration drift, tasks converge to desired state and are mostly/all `ok`.

![Provision second run](./provision_yml_second.png)

### Analysis

- First run changed state because packages/repos/service/group membership had to be configured.
- Second run did not reapply changes due to idempotent modules (`apt`, `service`, `user`, `file`, `apt_repository`) with desired state declarations.
- This allows safe repeated execution and recovery from partial failures.

---

## 4. Ansible Vault Usage

Sensitive data is stored in vaulted files and decrypted automatically via `ansible.cfg`:

```ini
[defaults]
vault_password_file = .vault_pass
```

Vaulted variables include:

- SSH/become credentials (`ansible_password`, `ansible_become_password`)
- Docker Hub credentials (`dockerhub_username`, `dockerhub_password`)
- Deployment variables (`app_name`, `docker_image`, `docker_image_tag`, `app_port`, `app_container_name`)

Example encrypted file header (`inventory/group_vars/all.yml`):

```yaml
$ANSIBLE_VAULT;1.1;AES256
39353632363062313937356432663237316164663962313739316134626164613631373039353332
6538613039343263396261363233303263343136666163620a336431343664613032636161623861
```

Vault password strategy:

- keep `.vault_pass` local only
- file permissions `600`
- never commit `.vault_pass` or unencrypted secrets
- commit only vaulted files

---

## 5. Deployment Verification

### Deploy playbook execution

Deployment run evidence:

![Deploy playbook run](./deployment.png)

### Container status and endpoint checks

Evidence for running container and successful endpoint checks:

![Container status and endpoints](./container_status_and_endpoints.png)

Checks performed:

- `ansible webservers -a "docker ps"`
- `curl http://<VM-IP>:5000/health`
- `curl http://<VM-IP>:5000/`

---

## 6. Key Decisions

### Why use roles instead of plain playbooks?

Roles keep automation modular and maintainable by isolating responsibility (base setup, Docker setup, app deployment). This improves readability and allows independent updates without rewriting large playbooks.

### How do roles improve reusability?

Each role can be reused in other projects or other hosts by changing only variables. The same role logic can be applied to staging/production with different inventories or vaulted values.

### What makes a task idempotent?

Idempotent tasks declare desired state (`present`, `started`, `absent`) rather than imperative shell commands. Re-running them does not create extra side effects when the target is already compliant.

### How do handlers improve efficiency?

Handlers run only when notified by a changed task, so services are not restarted on every run. This reduces unnecessary restarts and speeds up stable deployments.

### Why is Ansible Vault necessary?

Vault protects secrets while keeping infrastructure code in version control. It enables secure collaboration without exposing credentials in plaintext files or shell history.

---

## 7. Challenges

- SSH authentication initially failed until connection variables were correctly loaded from vaulted inventory-scoped group vars.
- YAML indentation issues (tabs) caused parsing errors; fixed by normalizing files to spaces.
- Connectivity variability (permission denied/timeouts) required separate validation of credentials vs. network reachability.

Binary file added ansible/docs/available_tags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/clean_reinstallation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/container_status_and_endpoints.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/deployment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/docker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/docker_compose_container.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/normal_deployment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/only_packages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/provision_yml_first.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/provision_yml_second.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/scenario_4a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/scenario_4b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/second_run_no_changed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ansible/docs/wipe_only.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
$ANSIBLE_VAULT;1.1;AES256
37346562636264353031653839366236623831656163656330636466303938343531613961616565
3262363866393863313833303836303035613236323036610a373162316431316131306165636638
34366339356236303634663231306439333366633934643765313434336131623235316162376262
3233336366393461390a656438346162376162343030303363303239656138663664626634343236
61363730376165303234653865356330316361663436316263396461363765313230343332643063
61386332303166383736363065316565353331393039636530643634663766383237383361356263
63383433386439316134346539356663653932353536343537323530306239633265303766313132
39663862616262666538313839663133336431306630323365616461373033373039396437336363
64333966613637663362313836323838346435323564356637636463303435356236336437393236
38343737643862323433646639356139633839333537306334373032336434623663643930323332
31363861623362633832646363653534393334646664363237663432333334323364336463613765
33306237633139633162643634643661386239633837653934633435636131383136306266623066
33383835393464376536306663323134643730623133623464323132373866373261613866643338
39333461663363396662313562346336323833383063626235313232303637366638323531383964
31353162623839646430396630376335393462303930663832303165643162326532346137363162
65613338363034363962323963663935633461663165306266393437633666313431623236633532
64663431613366316431626531653262653762303931333462386365373661313033336461353439
30663564303563346538636363353739646438386463666438643363663538623161643739343833
303463383233636632633662386531386363
Loading