Skip to content
Open

Lab05 #2714

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
87 changes: 87 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Python CI (app_python)

on:
push:
branches: ["master"]
paths:
- "app_python/**"
- ".github/workflows/python-ci.yml"
pull_request:
paths:
- "app_python/**"
- ".github/workflows/python-ci.yml"

concurrency:
group: python-ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test-and-lint:
runs-on: ubuntu-latest
defaults:
run:
working-directory: app_python

steps:
- name: Checkout
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
app_python/requirements-dev.txt

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt -r requirements-dev.txt

- name: Lint (ruff)
run: |
ruff check .

- name: Run tests (pytest)
run: |
pytest -q

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

- name: Snyk scan (dependencies)
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: snyk test --file=requirements.txt --severity-threshold=high || true


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

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

- name: Generate CalVer version
run: |
echo "VERSION=$(date -u +%Y.%m.%d)-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV

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

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./app_python
file: ./app_python/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-info-service:${{ env.VERSION }}
${{ secrets.DOCKERHUB_USERNAME }}/devops-info-service:latest
19 changes: 18 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
test
test
# Terraform
terraform/.terraform/
terraform/.terraform.lock.hcl
terraform/terraform.tfstate
terraform/terraform.tfstate.backup
terraform/*.tfvars

# Yandex Cloud SA key
.yc/

venv/
*/venv/
__pycache__/
.terraform/
.pulumi/
*.tfstate
*.tfstate.backup
6 changes: 6 additions & 0 deletions ansible/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Ansible
*.retry
.vault_pass
__pycache__/
ansible/.venv/
13 changes: 13 additions & 0 deletions ansible/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[defaults]
inventory = inventory/hosts.ini
roles_path = roles
host_key_checking = False
remote_user = vboxuser
retry_files_enabled = False
forks = 10
timeout = 30

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

## 1. Architecture Overview

- **Ansible Version:** 2.16+
- **Target OS:** Ubuntu 24.04 LTS
- **Cloud Provider:** Yandex Cloud
- **Application:** DevOps Info Service (FastAPI)
- **Container Runtime:** Docker

This lab implements a fully automated, role-based infrastructure provisioning and container deployment system using Ansible.

### Why Roles?

Roles were used instead of monolithic playbooks to achieve:

- Modularity
- Reusability
- Separation of concerns
- Clean project structure
- Easier scalability and maintenance

---

## 2. Role Structure

### common role
Purpose: Basic system preparation

Tasks:
- Update APT cache
- Install essential packages (curl, git, vim, htop, python3-pip)
- Configure timezone

Idempotency ensured using:
- `apt` module with `state: present`
- `timezone` module

---

### docker role
Purpose: Install and configure Docker

Tasks:
- Install Docker from Ubuntu repository
- Enable and start docker service
- Add user to docker group
- Install python3-docker for Ansible Docker modules

Handlers:
- Restart Docker service (if needed)

All tasks are state-based and idempotent.

---

### app_deploy role
Purpose: Deploy containerized application securely

Tasks:
- Pull Docker image
- Remove old container if exists
- Run container with restart policy
- Wait for application port
- Perform health check via HTTP

Security:
- Docker Hub credentials stored in encrypted Vault file
- `no_log: true` used for sensitive tasks

---

## 3. Idempotency Demonstration

### First Run

Initial execution resulted in multiple `changed` tasks because packages and services were installed.

### Second Run

Second execution showed:


changed=0


This confirms idempotency.

Idempotency is achieved by:
- Using declarative modules
- Avoiding raw shell commands
- Defining desired system state explicitly

---

## 4. Application Deployment Verification

After deployment:

- Container is running (`docker ps`)
- Port 5000 is exposed publicly
- Health endpoint returns HTTP 200
- Root endpoint returns system metadata

Public URL:

http://93.77.190.119:5000

Health endpoint:

http://93.77.190.119:5000/health

---

## 5. Ansible Vault

Sensitive variables are stored in:


group_vars/all.yml


File is encrypted using:


$ANSIBLE_VAULT;1.1;AES256


Vault ensures:
- Secrets are not stored in plaintext
- Safe version control
- Secure automation

---

## 6. Key DevOps Principles Applied

- Infrastructure as Code
- Idempotent configuration management
- Secure secret management
- Containerized deployment
- Automated verification
- Role-based modular architecture

---

## 7. Conclusion

The system successfully provisions infrastructure, installs Docker, and deploys a containerized application using Ansible roles.

The solution is:

- Idempotent
- Secure
- Modular
- Reproducible
- Production-ready
Binary file added ansible/docs/screenshots/curl-health.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/provision-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/screenshots/provision-second.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
$ANSIBLE_VAULT;1.1;AES256
32356462313737333333373263633564396636643732653235326563636663386333656433356634
3231343764306434303939316564373334623765323934610a396632613631303061363339643061
30613132343937343461373936633532343937643635393130333730643633353235336562613733
3539373561303261650a653739656565623434316664346162623833393566306538663837316263
33393134643665393033313339653562363938653064313739393735393131613361366161396236
37383065353531336334646135383530636463303135316436646637646330353365363665366436
36666538646530396161636166373130313334383332613866386535333734323462613337323265
62303634626336356134316461656666373165666631376231326439393862333337666662616131
62356136633630373965356463366362373365393832626362373637356533336635383337656561
38306466663430626431623735653463373337396364666236366433313332376466356234663535
32656261383164613437303332646532336537343833343932323337636239383534326664356665
30616166616534396235656437343465346163376234366232643232663765386531623238653735
39646430333830343664373939333766326431376638336161613630373332646138306639653439
66366333363332356532343065646237653562643937633163346165643966623638633235393030
38353735653636366264303164326230666339623039643933373036306233333637656339643733
38333435303966646536663865653666303166346339346330316338343935633361663634303839
61396264316132353762323931346130353239613366656531343735653464396536306664623130
3030333933376564363832626232623763653961313135386465
5 changes: 5 additions & 0 deletions ansible/inventory/hosts.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[webservers]
lab04-vm ansible_host=93.77.190.119 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_ed25519

[webservers:vars]
ansible_python_interpreter=/usr/bin/python3
10 changes: 10 additions & 0 deletions ansible/playbooks/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
- name: Deploy application container
hosts: webservers
become: yes

collections:
- community.docker

roles:
- app_deploy
8 changes: 8 additions & 0 deletions ansible/playbooks/provision.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- name: Provision web servers
hosts: webservers
become: yes

roles:
- common
- docker
21 changes: 21 additions & 0 deletions ansible/roles/app_deploy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
# Defaults for app_deploy role

# Docker Hub username (can be overridden via Vault)
dockerhub_username: "fayzullin" # Поставь свой логин, если другой

# App / image name
app_name: "devops-info-service"

# Full image name and tag
docker_image: "{{ dockerhub_username }}/{{ app_name }}"
docker_image_tag: "latest"

# Container and port
app_port: 5000
app_container_name: "{{ app_name }}"
app_restart_policy: "unless-stopped"

docker_restart_policy: "{{ app_restart_policy }}"
# Environment variables for the container (if needed)
app_env: {}
5 changes: 5 additions & 0 deletions ansible/roles/app_deploy/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: restart app container
community.docker.docker_container:
name: "{{ app_container_name }}"
state: restarted
Loading