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

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

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

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

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

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

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

- name: Run tests
run: |
cd app_python
pytest

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

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

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: app_python
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-info-python:${{ env.VERSION }}
${{ secrets.DOCKERHUB_USERNAME }}/devops-info-python:latest

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

- name: Run Snyk test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: snyk test --all-projects --severity-threshold=high
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
104 changes: 104 additions & 0 deletions ansible/docs/LAB05.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Lab 5 - Ansible Implementation Documentation

## 1. Architecture Overview
- Ansible version: 2.15+ (tested on macOS with Python 3.10)

- Target VM OS: Ubuntu 22.04 LTS (5.15.0-151-generic)

- Role structure:
```
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
```

- Why roles: Roles separate concerns (system provisioning, Docker setup, app deployment), making playbooks modular, reusable, and easier to maintain.

## 2. Roles Documentation
**common**
- **Purpose**: Update system, install essential packages, optionally set timezone.
- **Variables**:
- `common_packages` — list of packages (`python3-pip, curl, git, vim, htop`),
- `timezone` — default: `UTC`
- **Handlers**: None
- **Dependencies**: None

**docker**
- **Purpose**: Install Docker, configure repository, ensure Docker service running, manage user access.
- **Variables**:
- `docker_version` — optional version constraints,
- `docker_user` — user to add to docker group
- **Handlers**: `restart docker` — triggered when Docker config changes
- **Dependencies**: `common` role should run first

**app_deploy**
- **Purpose**: Pull and run containerized Python app securely using Vault credentials.
- **Variables**:
- `dockerhub_username`, `dockerhub_password` (vaulted);
- `app_name`, `docker_image_tag`, `app_port`, `app_container_name`
- **Handlers**:
- `restart application container` — restarts container if configuration changes
- **Dependencies**: Depends on `docker` role

## 3. Idempotency Demonstration
**First run of** `provision.yml`:

![first run](screenshots/first%20run.png)

**Second run**:

![first run](screenshots/second%20run.png)

**Analysis**:
- First run: packages installed, Docker configured, user added — all tasks changed state.
- Second run: nothing changed because tasks checked current state before applying changes.
- **Idempotency**: Roles and tasks are designed to only make changes if the target state differs from the actual state.

## 4. Ansible Vault Usage
- **Secure storage**: Docker Hub credentials stored in `group_vars/all.yml` encrypted via `ansible-vault`.
- **Vault password strategy**: Use `--ask-vault-pass` or password file (not committed to repo).
- **Importance**: Protects sensitive credentials from accidental exposure in version control.

## 5. Deployment Verification

**Playbook run**:
![playbook run](screenshots/deployment%20playbook.png)

**Container status**:
![container status](screenshots/docker%20ps.png)

**Health check**:
![main endpoint](screenshots/main%20endpoint.png)
![health check](screenshots/health%20check.png)

**Handler execution**: The restart handler triggered only when container needed restart.

## 6. Key Decisions

- **Why roles**: Keep playbooks modular, easier to maintain, and reusable.
- **Reusability**: Roles can be applied to multiple hosts or projects without duplicating logic.
- **Idempotent tasks**: Ensure they check state before applying changes (apt, docker, etc.).
- **Handlers efficiency**: Avoid unnecessary restarts; only run when a change occurs.
- **Vault necessity**: Keeps sensitive credentials secure, prevents leaks in source control.

## 7. Challenges
- Docker Hub login failing when vault not loaded → resolved by including `vars_files` in playbook.
- Container inaccessible from outside → fixed by restarting.
- Handlers misconfigured (`state: restarted` not valid) → corrected to `started`.
Binary file added ansible/docs/screenshots/deployment playbook.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 ps.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/first run.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/health 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/main endpoint.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/second run.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
$ANSIBLE_VAULT;1.1;AES256
62383930616563623538613238376263636261346431313235336635306364666265396136643735
3533346236663734363532396636666263373730333636320a393539646163613962323634343835
37653330636339653435623037336261623138383633383063626139626564616534663737393861
3361656430633465390a386562333438303162386332343438626335636663613865393837343635
64616233623831623863633463663262613232623139663761306235666538303038363130376234
38336534663035633039336239623861383037336134643864633730343931303137633731383735
38346132666332336239316235353831333363616430626363313133646339306438383037306135
34663736303464346139663430376436306638313961616335323135653239366230373864346137
39613438316636323932316132646562323665656433656539636662386163653465623939396138
32393832323366666465333339303536353935626339653932303732393738396232303265396134
30333233323765353637303730336536336263343364363966626438396235653237343633646232
33653161386135663961313161633166396164383064656166613132316136306238666363373434
62393836323630626131373933326234306234643936353232343763363034356539313066383237
33613764613132373433663665633134393931646364613632323365396166643238336530636362
30343438366233646635663135633364636536326333323564313361643933656266653834333466
65653336386638646639373861323363623938333834343263373363353739366665616363343335
34373264356665333337623832613761353166346631633966663030393936663163316438666132
38386639643535623765366366303264343865353531313139333834306638613931636239616334
666539343762303634333235313535333233
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]
lab04-vm ansible_host=93.77.182.194 ansible_user=ubuntu
9 changes: 9 additions & 0 deletions ansible/playbooks/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
- name: Deploy application
hosts: webservers
become: yes
vars_files:
- ../group_vars/all.yml

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
Empty file added ansible/playbooks/site.yml
Empty file.
6 changes: 6 additions & 0 deletions ansible/roles/app_deploy/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
app_port: 5000
app_container_name: "{{ app_name }}"
docker_image_tag: latest
restart_policy: unless-stopped
env_vars: {}
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 application container
community.docker.docker_container:
name: "{{ app_container_name }}"
image: "{{ docker_image }}:{{ docker_image_tag }}"
state: started
restart_policy: "{{ restart_policy }}"
published_ports:
- "{{ app_port }}:{{ app_port }}"
env:
"{{ env_vars }}"
recreate: yes
48 changes: 48 additions & 0 deletions ansible/roles/app_deploy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
- name: Login to Docker Hub
community.docker.docker_login:
username: "{{ dockerhub_username }}"
password: "{{ dockerhub_password }}"
no_log: true

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

- name: Stop existing container if running
community.docker.docker_container:
name: "{{ app_container_name }}"
state: stopped
image: "{{ docker_image }}:{{ docker_image_tag }}"
ignore_errors: yes

- name: Remove old container if exists
community.docker.docker_container:
name: "{{ app_container_name }}"
state: absent
ignore_errors: yes

- name: Run new container
community.docker.docker_container:
name: "{{ app_container_name }}"
image: "{{ docker_image }}:{{ docker_image_tag }}"
state: started
restart_policy: "{{ restart_policy }}"
published_ports:
- "{{ app_port }}:{{ app_port }}"
env:
"{{ env_vars }}"
notify: restart application container

- name: Wait for application to be ready
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ app_port }}"
timeout: 60

- name: Verify health endpoint
ansible.builtin.uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
8 changes: 8 additions & 0 deletions ansible/roles/common/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
common_packages:
- python3-pip
- curl
- git
- vim
- htop
timezone: "Europe/Moscow"
14 changes: 14 additions & 0 deletions ansible/roles/common/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600

- name: Install common packages
apt:
name: "{{ common_packages }}"
state: present

- name: Set timezone
timezone:
name: "{{ timezone }}"
7 changes: 7 additions & 0 deletions ansible/roles/docker/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
docker_packages:
- docker-ce
- docker-ce-cli
- containerd.io
docker_user: ubuntu
docker_version: ""
5 changes: 5 additions & 0 deletions ansible/roles/docker/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: restart docker
service:
name: docker
state: restarted
36 changes: 36 additions & 0 deletions ansible/roles/docker/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present

- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] 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 service is running and enabled
service:
name: docker
state: started
enabled: yes

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

- name: Install python3-docker
pip:
name: docker
notify: restart docker
1 change: 1 addition & 0 deletions app_go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
devops-info
Loading