Skip to content
Open
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
58 changes: 58 additions & 0 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Python CI

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

jobs:
test-build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: "pip"

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

- name: Lint
run: ruff check app_python

- name: Run tests
run: pytest app_python/tests

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

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

- name: Snyk Scan
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: snyk test --file=app_python/requirements.txt

- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: ./app_python
push: true
tags: |
maksimmenshikh/devops-info-service:${{ env.VERSION }}
maksimmenshikh/devops-info-service:latest
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
test
*.retry
.vault_pass
ansible/inventory/*.pyc
__pycache__/
.github
terraform/authorized_key.json
ansible/vault_pass.txt
terraform/terraform.tfvars
*.tfstate*
**/.terraform/*
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
127 changes: 127 additions & 0 deletions ansible/docs/LAB05.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# LAB05.md — Ansible Implementation Documentation

## 1. Architecture Overview

**Ansible version used:** 2.17.0 (core).
**Target VM OS and version:** Ubuntu 22.04 LTS (Yandex Cloud Compute Image).

### Role Structure Explanation

The project hierarchy is built on modularity:

```text
ansible/
├── inventory/
│ ├── hosts.ini # List of servers
│ └── group_vars/ # Group variables (including secrets)
├── playbooks/ # Scripts (provision.yml, deploy.yml)
└── roles/ # Roles directory
├── common/ # Basic OS configuration
├── docker/ # Docker installation and setup
└── app_deploy/ # Application deployment
```

**Why roles instead of monolithic playbooks?**
Roles allow the configuration logic to be divided into independent blocks. This makes the code readable, facilitates reuse (e.g., the `docker` role can be used in other projects), and simplifies debugging.

---

## 2. Roles Documentation

### Role: `common`

- **Purpose:** System preparation. Updates package cache and installs basic utilities (`curl`, `git`, `apt-transport-https`).
- **Variables:** Typically does not require specific variables.
- **Handlers:** None.
- **Dependencies:** None.

### Role: `docker`

- **Purpose:** Full Docker Engine installation cycle: adding GPG keys, repository, package installation, and service startup.
- **Variables:** `docker_packages` (list of packages to install).
- **Handlers:** `restart docker` (triggers when daemon configuration changes).
- **Dependencies:** `common`.

### Role: `app_deploy`

- **Purpose:** Docker Hub authentication, image download (`pull`), and container startup with port forwarding.
- **Variables:** `dockerhub_username`, `docker_image`, `docker_image_tag`, `app_port`.
- **Handlers:** None.
- **Dependencies:** `docker`.

---

## 3. Idempotency Demonstration

### Analysis of Runs:

- **First run (`provision.yml`):** Status `changed=...`. Ansible detects the absence of Docker and required packages, and executes all tasks for their installation.
- **Second run (`provision.yml`):** Status `ok=... changed=0`. Ansible checks the system state, sees that Docker is already installed and the service is running, so no changes are made.

**Explanation:**
Idempotency is achieved because Ansible describes the desired state, not a sequence of commands. Modules (e.g., `apt` or `docker_container`) first check the current state of the resource and make changes only when necessary.

---

## 4. Ansible Vault Usage

### How You Store Credentials Securely:

All sensitive data (Docker Hub password) is stored in the `inventory/group_vars/all.yml` file, encrypted using the AES256 algorithm.

### Vault Password Management Strategy:

A `vault_pass.txt` file is used, which is excluded from Git via `.gitignore`. This allows playbooks to be run automatically without manual password entry.

### Example of Encrypted File:

```text
$ANSIBLE_VAULT;1.1;AES256
36313334643033623766623662663935643431613562363065623862366165613330663738663363
... (encrypted data) ...
```

**Why Ansible Vault is Important:**
It allows storing the entire infrastructure as code (IaC) in public or shared repositories without the risk of password or token leaks.

---

## 5. Deployment Verification

### Container Status:

```bash
89.169.148.180 | CHANGED | rc=0 >>
CONTAINER ID IMAGE STATUS NAMES
f8d9a2b1c3e4 maksimmenshikh/devops-info-service:lab02 Up 12 minutes devops-info-service
```

### Health Check Verification:

```bash
curl http://89.169.148.180
{"status": "healthy"}
```

---

## 6. Key Decisions

- **Why use roles instead of plain playbooks?**
Roles ensure code cleanliness and allow project scalability. This is an industry standard that facilitates collaboration.
- **How do roles improve reusability?**
A role can be easily transferred to another project or published on Ansible Galaxy. For example, the Docker installation role is independent of the specific application.
- **What makes a task idempotent?**
Using built-in Ansible modules that check the status (e.g., file existence or running process) before performing an action.
- **How do handlers improve efficiency?**
Handlers allow heavy operations (e.g., server reboot) to be performed only once at the very end and only if there were actual changes.
- **Why is Ansible Vault necessary?**
Without it, storing secrets in Git is impossible, which violates the "infrastructure as code" concept and creates security risks.

---

## 7. Challenges

- **CRLF vs LF:** There was an issue with invisible Windows characters in encrypted files, causing Docker Hub authorization errors. Resolved using `dos2unix`.
- **Not a TTY:** Difficulties arose with opening the editor for Vault when working through Git Bash/WSL. Resolved by using `--vault-password-file`.
- **Cloud Permissions:** Initially, there were insufficient quotas and access rights in Yandex Cloud (Security Groups). Resolved by adding the `vpc.admin` role to the service account.
Binary file added ansible/docs/screenshots/connection_check.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_check.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/docker_check.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/idempotency.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/playbook_deploy.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_launch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions ansible/inventory/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
$ANSIBLE_VAULT;1.1;AES256
34313437303138653636386266316465306361646164613634383530313037333536316638376238
3565316332613333316462396564343332633664363465370a343737643862393562623933353234
65636462386462386462643536306233386465336465656262393733386662313335643230303631
3166366162616366630a306665376138656534306437303236633866626432336461363931663762
38663834663762323134633736663135303165376537613236353630653263363532646136363734
37323536656262323531666564613838326530383832393163323839353435356262353531353837
64313437353763346363323639646538653736373165333638666461323563643334633533303138
66353564393664336461343633336336666331353761313831633532663964626134666332316565
34616237373262366663663132633961323331353633386561376562356564396332373434653831
64613530353934613839353366323164313266323439666439336334333663323030303133363063
66636663303536613838663666353564343762333934343438353063326366366430666335353066
33386437653830636233623936303638633838313164313737326337643764333833343764623462
31336235666339383838623935336635636131383038643535653937363336386435623236323061
34353435363363356263663730326138333837363432333837663366386561393465313861613934
61633666643032623261356562356439353138636431303562383832323538663863333766363535
30656133343966653461656264333165353532636639303337313031666133623731643436623236
61303730326130326230616230373532613866383631393033616634323431376661
2 changes: 2 additions & 0 deletions ansible/inventory/hosts.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[web]
89.169.148.180 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa_linux
6 changes: 6 additions & 0 deletions ansible/playbooks/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: deploy application
hosts: web
become: yes

roles:
- app_deploy
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: web
become: yes

roles:
- common
- docker
1 change: 1 addition & 0 deletions ansible/roles/app_deploy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
restart_policy: unless-stopped
4 changes: 4 additions & 0 deletions ansible/roles/app_deploy/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- name: restart container
docker_container:
name: "{{ app_container_name }}"
state: restarted
25 changes: 25 additions & 0 deletions ansible/roles/app_deploy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
- name: login to docker hub
community.docker.docker_login:
username: "{{ dockerhub_username }}"
password: "{{ dockerhub_password }}"
no_log: false

- name: pull docker image
community.docker.docker_image:
name: "{{ docker_image }}"
tag: "{{ docker_image_tag }}"
source: pull

- name: stop old container
community.docker.docker_container:
name: "{{ app_container_name }}"
state: absent

- name: run container
community.docker.docker_container:
name: "{{ app_container_name }}"
image: "{{ docker_image }}:{{ docker_image_tag }}"
state: started
restart_policy: "{{ restart_policy }}"
ports:
- "{{ app_port }}:{{ app_port }}"
6 changes: 6 additions & 0 deletions ansible/roles/common/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
common_packages:
- python3-pip
- curl
- git
- vim
- htop
9 changes: 9 additions & 0 deletions ansible/roles/common/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- name: update apt cache
apt:
update_cache: yes
cache_valid_time: 3600

- name: install common packages
apt:
name: "{{ common_packages }}"
state: present
1 change: 1 addition & 0 deletions ansible/roles/docker/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docker_user: ubuntu
4 changes: 4 additions & 0 deletions ansible/roles/docker/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- name: restart docker
service:
name: docker
state: restarted
35 changes: 35 additions & 0 deletions ansible/roles/docker/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
- name: add docker gpg key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present

- name: add docker repo
apt_repository:
repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present

- name: install docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
update_cache: yes

- name: ensure docker running
service:
name: docker
state: started
enabled: yes

- name: add user to docker group
user:
name: "{{ docker_user }}"
groups: docker
append: yes

- name: install python docker module
apt:
name: python3-docker
state: present
9 changes: 9 additions & 0 deletions app_python/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
__pycache__/
*.pyc
venv/
.venv/
.git
.gitignore
README.md
docs/
.env
12 changes: 12 additions & 0 deletions app_python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Python
__pycache__/
*.py[cod]
venv/
*.log

# IDE
.vscode/
.idea/

# OS
.DS_Store
17 changes: 17 additions & 0 deletions app_python/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM python:3.13-slim

RUN useradd -m appuser

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

USER appuser

EXPOSE 5000

CMD ["python", "app.py"]
Loading