Skip to content
Open

Lab06 #2873

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
91 changes: 91 additions & 0 deletions .github/workflows/ansible-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Ansible Deployment

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

jobs:
lint:
name: Lint Ansible Code
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 ansible-lint
run: pip install ansible-lint

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

deploy:
name: Deploy Application
needs: lint
runs-on: ubuntu-latest
if: github.event_name == 'push'

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

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

- name: Install Ansible
run: pip install ansible

- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.VM_HOST }} >> ~/.ssh/known_hosts

- name: Create inventory
run: |
mkdir -p ansible/inventory
echo "[webservers]" > ansible/inventory/hosts.ini
echo "${{ secrets.VM_HOST }} ansible_user=${{ secrets.VM_USER }}" >> ansible/inventory/hosts.ini

- name: Decrypt vault password
run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass

- name: Run Ansible playbook
run: |
cd ansible
ansible-playbook playbooks/deploy.yml \
-i inventory/hosts.ini \
--vault-password-file /tmp/vault_pass

- name: Cleanup
if: always()
run: rm -f /tmp/vault_pass

verify:
name: Verify Deployment
needs: deploy
runs-on: ubuntu-latest
if: success()

steps:
- name: Check application
run: |
sleep 10
curl -f http://${{ secrets.VM_HOST }}:8000/health || exit 1
echo "Application verified successfully"
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
Loading