Skip to content
Open

Lab05 #2739

Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Goal


## Changes


## Testing


## Artifacts & Screenshots


## Checklist
73 changes: 73 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Python CI/CD

on:
push:
branches: [ main, lab03 ]
paths:
- 'app_python/**'
- '.github/workflows/python-ci.yml'
pull_request:
branches: [ main ]
paths:
- 'app_python/**'
- '.github/workflows/python-ci.yml'

jobs:
test:
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'
cache: 'pip'
cache-dependency-path: 'app_python/requirements.txt'

- name: Install dependencies
run: |
cd app_python
pip install -r requirements.txt

- name: Run linter
run: |
cd app_python
flake8 app.py

- name: Run tests
run: |
cd app_python
pytest tests/test_app.py

- name: Security scan with pip-audit
run: |
cd app_python
pip install pip-audit
pip-audit -r requirements.txt || echo "Security scan completed"

build:
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

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

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./app_python
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/fastapi-lab-app:latest
${{ secrets.DOCKER_USERNAME }}/fastapi-lab-app:$(date +%Y.%m.%d)
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
test
test
all.yml
11 changes: 11 additions & 0 deletions ansible/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[defaults]
inventory = inventory/hosts.ini
roles_path = roles
host_key_checking = False
remote_user = ubuntu
retry_files_enabled = False

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

## 1. Architecture Overview

### Ansible version used

```bash
ansible --version
ansible [core 2.20.1]
```

### Target VM OS and version

**Ubuntu 22.04 LTS**

### Role structure explanation

1. `common` role configures base system for server
2. `docker` role installs and configures Docker
3. `app_deploy` role deploys the actual application

`common` role -> `docker` role -> `app_deploy` role

### Why roles instead of monolithic playbooks?

Using roles instead of monolithic playbooks ensures better readability as this provides clear separation of concerns and it's easier to debug and update isolated components.

## 2. Roles Documentation

### `common` role:

**Purpose:** Configures base system for server
**Variables:** Common packages and timezone
**Handlers:** No handlers
**Dependencies:** No dependencies

### `docker` role:

**Purpose:** Installs and configures Docker
**Variables:** Dokcer version, Docker Compose version, Docker users, Docker repository URL, Docker GPG key URL
**Handlers:** Docker restart
**Dependencies:** Depends on `common` role

### `deploy` role:

**Purpose:** Deploys the actual application
**Variables:** Application settings, Docker settings, environment variables, health check and vault variables
**Handlers:** App container restart and reload
**Dependencies:** Depends on `docker` role

## 3. Idempotency Demonstration

### Terminal output from FIRST provision.yml run

![](screenshots/first-provision.jpg)

### Terminal output from SECOND provision.yml run

![](screenshots/second-provision.jpg)

### Analysis: What changed first time? What didn't change second time?

Most of the tasks were changed first time. Packets, Docker, container weren't on the server. Then second time only 1 task was changed because I set `cache_valid_time: 3600` for cache update. Ansible checks system state and doesn't do unnecessary actions. If everything is set properly, nothing changes.

### Explanation: What makes your roles idempotent?

I used `state: present' that ensures packages are intalled.

## 4. Ansible Vault Usage

### How you store credentials securely

I use **Ansible Vault** to encrypt sensitive data.

### Vault password management strategy

```bash
ansible-playbook playbooks/deploy.yml --ask-vault-pass
```

### Example of encrypted file (show it's encrypted!)

![](screenshots/encrypted-data.jpg)

### Why Ansible Vault is important

Ansible Vault is important because passwords are encrypted and it's safe in case of pushing file to git.

## 5. Deployment Verification

### Terminal output from deploy.yml run

![](screenshots/deploy.jpg)

### Container status: docker ps output

![](screenshots/docker-ps.jpg)

### Health check verification: curl outputs

```bash
curl http://62.84.120.249:5000/health | jq
{
"status": "healthy",
"timestamp": "2026-02-26T20:42:46.002Z",
"uptime_seconds": 10337
}
```

## 6. Key Decisions

### Why use roles instead of plain playbooks?

- **Organization** - Roles group related tasks, variables, and handlers together
- **Readability** - Playbooks become clean and simple (just list roles)
- **Reusability** - Same role can be used in multiple playbooks
- **Maintainability** - Easier to update and debug isolated components

### How do roles improve reusability?

- **Parameterization** - Variables make roles adaptable to different environments
- **Encapsulation** - All dependencies are contained within the role
- **Sharing** - Roles can be shared via Ansible Galaxy
- **Composability** - Mix and match roles for different server types

### What makes a task idempotent?

- **State checking** - Modules check current state before making changes
- **Declarative syntax** - Describe the desired state, not how to achieve it
- **Conditionals** - Tasks run only when needed (e.g., when: container_info.exists)
- **No "latest"** - Using state: present instead of state: latest
- **Idempotent modules** - Ansible modules are designed to be idempotent

### How do handlers improve efficiency?

- **Run-once** - Execute only once, even if notified by multiple tasks
- **Conditional execution** - Run only when changes actually occur
- **Order control** - Execute at the end of the play, not during
- **Resource savings** - Prevent unnecessary restarts (e.g., restart Docker once, not multiple times)

### Why is Ansible Vault necessary?

- **Security** - Encrypts sensitive data (passwords, tokens, keys)
- **Version control safe** - Can commit encrypted files to git
- **Compliance** - Meets security standards and audit requirements
- **Team collaboration** - Share code without sharing secrets
- **Multi-environment** - Different passwords for dev/staging/production
Binary file added ansible/docs/image.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/screenshots/deploy.jpg
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/screenshots/docker-ps.jpg
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/screenshots/encrypted-data.jpg
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/screenshots/first-provision.jpg
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/screenshots/second-provision.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions ansible/inventory/hosts.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[webservers]
vm ansible_host=62.84.120.249 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/yc-key
45 changes: 45 additions & 0 deletions ansible/playbooks/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
- name: Deploy Python containerized application
hosts: webservers
become: yes
gather_facts: yes

vars_files:
- ../group_vars/all.yml

roles:
- name: app_deploy
vars:
app_environment:
ENVIRONMENT: production
LOG_LEVEL: info
APP_VERSION: "{{ docker_image_tag }}"

pre_tasks:
- name: Verify vault variables are loaded
debug:
msg: "Vault variables loaded successfully: {{ dockerhub_username is defined}}"
when: dockerhub_username is defined

post_tasks:
- name: Get running containers
command: docker ps
register: docker_ps
changed_when: false

- name: Show running containers
debug:
var: docker_ps.stdout_lines

- name: Test application main endpoint
uri:
url: "http://{{ ansible_default_ipv4.address | default('localhost') }}:{{ app_host_port }}/"
method: GET
status_code: 200
register: main_endpoint
ignore_errors: yes

- name: Show application endpoints status
debug:
msg:
- "Main endpoint: {{ main_endpoint.status | default('FAILED') }}"
- "Health endpoint: {{ health_result.status | default('unknown') }}"
7 changes: 7 additions & 0 deletions ansible/playbooks/provision.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- name: Provision web servers
hosts: webservers
become: yes

roles:
- common
- docker
Empty file added ansible/playbooks/site.yml
Empty file.
14 changes: 14 additions & 0 deletions ansible/roles/app_deploy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
app_name: fastapi-lab-app
app_port: 5000
app_host_port: 5000
app_container_name: "{{ app_name }}"
docker_image_tag: latest
restart_policy: unless-stopped

app_environment:
ENVIRONMENT: development
LOG_LEVEL: debug

health_check_retries: 30
health_check_delay: 2
health_endpoint: /health
12 changes: 12 additions & 0 deletions ansible/roles/app_deploy/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- name: restart app container
docker_container:
name: "{{ app_container_name }}"
state: started
restart: yes
become: yes

- name: reload app container
docker_container:
name: "{{ app_container_name }}"
restart: yes
become: yes
Loading