Skip to content
Open

Lab06 #2875

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

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

jobs:
lint:
name: Ansible Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install ansible-lint
run: pip install ansible ansible-lint

- name: Run ansible-lint
run: ansible-lint playbooks/*.yml
working-directory: ansible

deploy:
name: Deploy Application
needs: lint
runs-on: self-hosted
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4

- name: Install Ansible and dependencies
run: |
pip install --user ansible docker
echo "$HOME/.local/bin" >> $GITHUB_PATH
ansible-galaxy collection install community.docker

- name: Deploy with Ansible
run: |
cd ansible
echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass
ansible-playbook playbooks/deploy.yml \
--vault-password-file /tmp/vault_pass \
-i inventory/hosts.ini \
-e "ansible_host=localhost"
rm /tmp/vault_pass

- name: Verify Deployment
run: |
sleep 10
curl -f http://localhost:5000 || exit 1
curl -f http://localhost:5000/health || exit 1
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

on:
push:
branches: [main, lab03]
pull_request:
branches: [main, lab03]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Cache pip packages
id: pip-cache
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('app_python/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-

- name: Install dependencies
run: |
echo "Cache hit: ${{ steps.pip-cache.outputs.cache-hit }}"
pip install -r app_python/requirements.txt

- name: Lint
run: flake8 .
working-directory: app_python

- name: Test
run: pytest tests/ -v
working-directory: app_python

- name: Install Snyk CLI
run: npm install -g snyk

- name: Run Snyk test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: snyk test --file=requirements.txt --severity-threshold=high
working-directory: app_python

docker:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4

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

- name: Generate version
run: echo "VERSION=$(date +%Y.%m.%d)" >> $GITHUB_ENV

- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./app_python
push: true
tags: |
roma3213/info_service:${{ env.VERSION }}
roma3213/info_service:latest
26 changes: 25 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
test
test

# Terraform
*.tfstate
*.tfstate.*
.terraform/
terraform.tfvars
*.tfvars
.terraform.lock.hcl

# Pulumi
pulumi/venv/
Pulumi.*.yaml

# Credentials
*.pem
*.key
*-credentials.json
credentials

# Ansible
*.retry
.vault_pass
ansible/inventory/*.pyc
__pycache__/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# DevOps Engineering: Core Practices

[![Ansible Deployment](https://github.com/TurikRoma/DevOps-Core-Course/actions/workflows/ansible-deploy.yml/badge.svg)](https://github.com/TurikRoma/DevOps-Core-Course/actions/workflows/ansible-deploy.yml)
[![Labs](https://img.shields.io/badge/Labs-18-blue)](#labs)
[![Exam](https://img.shields.io/badge/Exam-Optional-green)](#exam-alternative)
[![Duration](https://img.shields.io/badge/Duration-18%20Weeks-lightgrey)](#course-roadmap)
Expand Down
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
161 changes: 161 additions & 0 deletions ansible/docs/LAB05.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Lab 05 — Ansible Fundamentals

## 1. Architecture Overview

**Ansible version:** 2.16.3

**Target VM:** Ubuntu 22.04 LTS on GCP (e2-micro, us-central1-a)

**Control node:** WSL Ubuntu on Windows (MINGW64)

**Role structure:**

```
ansible/
├── inventory/
│ └── hosts.ini
├── roles/
│ ├── common/
│ │ ├── tasks/main.yml
│ │ └── defaults/main.yml
│ ├── docker/
│ │ ├── tasks/main.yml
│ │ ├── handlers/main.yml
│ │ └── defaults/main.yml
│ └── app_deploy/
│ ├── tasks/main.yml
│ ├── handlers/main.yml
│ └── defaults/main.yml
├── playbooks/
│ ├── site.yml
│ ├── provision.yml
│ └── deploy.yml
├── group_vars/
│ └── all.yml (encrypted)
└── ansible.cfg
```

**Why roles instead of monolithic playbooks?** Reusability — each role is independent and can be used across projects. Clear separation of concerns: common setup, Docker installation, and app deployment are logically separate tasks.

---

## 2. Roles Documentation

### common

**Purpose:** Basic system setup — update apt cache, install essential packages.

**Variables:**
- `common_packages` — list of packages (python3-pip, curl, git, vim, htop, wget, unzip)

**Handlers:** None.

**Dependencies:** None.

### docker

**Purpose:** Install Docker CE from official repository, configure service, add user to docker group.

**Variables:**
- `docker_user` — user to add to docker group (default: ubuntu)
- `docker_version` — Docker version constraint (default: latest)

**Handlers:**
- `restart docker` — restarts Docker service when configuration changes

**Dependencies:** None (but should run after common).

### app_deploy

**Purpose:** Deploy containerized application — pull image from Docker Hub, run container, verify health.

**Variables:**
- `app_port` — application port (default: 5000)
- `app_restart_policy` — container restart policy (default: unless-stopped)

**Handlers:**
- `restart app container` — restarts application container

**Dependencies:** docker role must be applied first.

---

## 3. Idempotency Demonstration

**First run** (`ansible-playbook playbooks/provision.yml`):

![First provision run](screenshots/04-provision-first-run.png)

Result: `ok=11, changed=1` — only apt cache update triggered "changed" (expected behavior, cache was stale). All other tasks already in desired state from previous runs.

**Second run:**

![Second provision run](screenshots/05-provision-second-run.png)

Result: `ok=10, changed=0` — zero changes. All tasks report "ok" (green). Cache is still fresh (`cache_valid_time: 3600`).

**Analysis:** Roles are idempotent because they use stateful Ansible modules (`apt` with `state: present`, `service` with `state: started`, `user` with `append: yes`). These modules check current state before making changes. Running playbooks multiple times is safe — only applies changes when state drifts from desired.

---

## 4. Ansible Vault Usage

**What is stored:** Docker Hub credentials (`dockerhub_username`, `dockerhub_password`), application configuration (`app_name`, `docker_image`, `app_port`).

**Vault file:** `group_vars/all.yml` — encrypted with `ansible-vault encrypt`.

**Password management:** `--ask-vault-pass` flag on each run. No `.vault_pass` file committed.

**Encrypted file (proof):**

![Vault encrypted](screenshots/09-vault-encrypted.png)

**Why Vault is important:** Credentials must never be stored in plaintext in version control. Vault encrypts sensitive data with AES256, allowing safe commits while keeping secrets protected.

---

## 5. Deployment Verification

**Deploy run** (`ansible-playbook playbooks/deploy.yml --ask-vault-pass`):

![Deploy output](screenshots/06-deploy-run.png)

Result: `ok=9, changed=2` — pulled image and started container. Health check passed: `"status": "healthy"`.

**Container status** (`ansible webservers -a "docker ps"`):

![Docker ps](screenshots/07-docker-ps.png)

```
CONTAINER ID IMAGE STATUS PORTS NAMES
75ac7dc24d34 roma3213/info_service:latest Up 0.0.0.0:5000->5000/tcp info_service
```

**Health check verification:**

![Curl output](screenshots/08-curl-health.png)

- `curl http://104.197.249.40:5000/health` → `{"status":"healthy","timestamp":"2026-03-05T00:13:57"}`
- `curl http://104.197.249.40:5000/` → Full service info (name, version, system, runtime, endpoints)

---

## 6. Key Decisions

**Why use roles instead of plain playbooks?** Reusability, organization, maintainability. Use same role across projects, share via Ansible Galaxy.

**How do roles improve reusability?** Self-contained with own tasks, handlers, defaults. Mix and match in different playbooks without duplication.

**What makes a task idempotent?** Stateful modules: `apt: state=present`, `service: state=started`, `file: state=directory`. Check current state before acting.

**How do handlers improve efficiency?** Only run when notified, execute once at end of play. Prevent unnecessary service restarts.

**Why is Ansible Vault necessary?** Encrypts sensitive data (AES256) for safe storage in version control. `no_log: true` hides credentials from task output.

---

## 7. Challenges

- **WSL world writable directory:** Ansible ignores `ansible.cfg` in `/mnt/c/` (Windows filesystem). Fix: `export ANSIBLE_CONFIG=/mnt/c/DevOps-Core-Course/ansible/ansible.cfg`
- **Vault variables not loaded by deploy.yml:** `group_vars/all.yml` not picked up automatically. Fix: explicit `-e @group_vars/all.yml` flag
- **WSL terminal inconvenience:** vim opens incorrectly in WSL on Windows, `ansible-vault create` unusable. Fix: created file with `echo` and encrypted with `ansible-vault encrypt`
Loading