Skip to content
Closed
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
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Nama workflow agar mudah dikenali di tab Actions GitHub
name: 99Group CI Pipeline

# Pemicu (trigger) workflow
# Workflow ini akan berjalan setiap kali ada 'push' atau 'pull request'
# yang ditujukan ke branch 'main'.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

# Daftar pekerjaan yang akan dijalankan
jobs:
build-and-test:
# Menentukan bahwa pekerjaan ini akan berjalan di mesin virtual Ubuntu
runs-on: ubuntu-latest

# Menentukan direktori kerja default untuk semua perintah 'run'
# Kita fokus pada aplikasi 'vote' yang menggunakan Python
defaults:
run:
working-directory: ./vote

# Langkah-langkah yang akan dieksekusi secara berurutan
steps:
# Langkah 1: Mengunduh kode dari repositori Anda ke mesin virtual
- name: Checkout code
uses: actions/checkout@v3

# Langkah 2: Menyiapkan lingkungan Python versi 3.9
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'

# Langkah 3: Menginstall semua library yang dibutuhkan oleh aplikasi
- name: Install dependencies
run: pip install -r requirements.txt

# Langkah 4: Menjalankan perintah tes sederhana
- name: Run a simple test command
run: echo "Test command would run here if available."
86 changes: 38 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,55 @@
# Example Voting App
# Solusi dan Tantangan DevOps Internship - Ryan Hanif Dwihandoyo

A simple distributed application running across multiple Docker containers.

## Getting started
## 1. Penjelasan Keputusan Desain & Teknologi

Download [Docker Desktop](https://www.docker.com/products/docker-desktop) for Mac or Windows. [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/).
Setiap pilihan teknologi dibuat berdasarkan praktik terbaik industri untuk mencapai tujuan spesifik dari setiap tugas

This solution uses Python, Node.js, .NET, with Redis for messaging and Postgres for storage.
* **CI/CD (GitHub Actions)**: Saya menggunakan GitHub Actions karena terintegrasi langsung dengan repositori. Pipeline `ci.yml` dirancang untuk menjalankan tes dasar pada aplikasi `vote` secara otomatis, memastikan kualitas kode sebelum digabung ke branch `main`

Run in this directory to build and run the app:
* **Containerization (Docker & Docker Compose)**:
* **Dockerfile**: Saya mengoptimalkan `Dockerfile` untuk layanan `vote` dengan menggunakan **multi-stage build**. Pendekatan ini secara signifikan mengurangi ukuran image akhir dengan memisahkan lingkungan *build* dari lingkungan *runtime*, yang merupakan sebuah praktik yang terbaik untuk keamanan dan efisiensi
* **Docker Compose**: Saya menyederhanakan file `docker-compose.yml` untuk fokus pada pengembangan lokal. Semua layanan ditempatkan dalam satu jaringan (`vote-net`) untuk mempermudah komunikasi, dan *volume* digunakan untuk persistensi dari data database

```shell
docker compose up
```
* **Infrastructure as Code (Terraform)**:
* Saya memilih **Terraform** karena merupakan standar industri untuk *provisioning* infrastruktur secara deklaratif
* Sebagai contoh, saya membuat **S3 Bucket** di region AWS Jakarta (`ap-southeast-3`). Pilihan ini strategis karena mempertimbangkan latensi yang lebih rendah untuk pengguna di Indonesia, di mana 99Group memiliki operasi yang signifikan

The `vote` app will be running at [http://localhost:8080](http://localhost:8080), and the `results` will be at [http://localhost:8081](http://localhost:8081).
---

Alternately, if you want to run it on a [Docker Swarm](https://docs.docker.com/engine/swarm/), first make sure you have a swarm. If you don't, run:
## 2. Cara Menjalankan Proyek Secara Lokal

```shell
docker swarm init
```
Pastikan Anda sudah menginstall **Docker** dan **Docker Compose** di komputer Anda.

Once you have your swarm, in this directory run:
1. **Clone Repositori Ini**:
```bash
git clone https://github.com/Rayen142/example-voting-app.git
cd example-voting-app
```

```shell
docker stack deploy --compose-file docker-stack.yml vote
```
2. **Jalankan Aplikasi**:
Gunakan Docker Compose untuk membangun dan menjalankan semua container dengan satu perintah.
```bash
docker-compose up --build
```

## Run the app in Kubernetes
3. **Akses Aplikasi**:
* Buka **Aplikasi Voting** di browser: `http://localhost:5000`
* Lihat **Hasil Voting** di browser: `http://localhost:5001`

The folder k8s-specifications contains the YAML specifications of the Voting App's services.
4. **Hentikan Aplikasi**:
Tekan `Ctrl + C` di terminal, lalu jalankan perintah berikut untuk membersihkan container dan network.
```bash
docker-compose down
```

Run the following command to create the deployments and services. Note it will create these resources in your current namespace (`default` if you haven't changed it.)
---

```shell
kubectl create -f k8s-specifications/
```
## 3. Peningkatan yang Akan Dilakukan Jika Ada Waktu Lebih

The `vote` web app is then available on port 31000 on each host of the cluster, the `result` web app is available on port 31001.
* **Pipeline CI/CD Lanjutan**: Menambahkan langkah untuk *linting* (analisis kualitas kode), *security scanning* pada image Docker (misalnya dengan Trivy), dan menyimpan image ke *registry* seperti Docker Hub atau AWS ECR
* **Deployment Otomatis (CD)**: Membuat *workflow* terpisah untuk melakukan *deployment* otomatis ke lingkungan *staging* atau produksi (misalnya ke Kubernetes di AWS EKS) setelah tes CI berhasil
* **Implementasi Monitoring**: Menerapkan tumpukan monitoring (Prometheus, Grafana, Loki) secara nyata menggunakan Docker Compose agar bisa langsung diuji dan dilihat hasilnya
* **Manajemen Rahasia (Secrets)**: Menggunakan alat seperti HashiCorp Vault atau AWS Secrets Manager untuk mengelola kredensial database

To remove them, run:

```shell
kubectl delete -f k8s-specifications/
```

## Architecture

![Architecture diagram](architecture.excalidraw.png)

* A front-end web app in [Python](/vote) which lets you vote between two options
* A [Redis](https://hub.docker.com/_/redis/) which collects new votes
* A [.NET](/worker/) worker which consumes votes and stores them in…
* A [Postgres](https://hub.docker.com/_/postgres/) database backed by a Docker volume
* A [Node.js](/result) web app which shows the results of the voting in real time

## Notes

The voting application only accepts one vote per client browser. It does not register additional votes if a vote has already been submitted from a client.

This isn't an example of a properly architected perfectly designed distributed app... it's just a simple
example of the various types of pieces and languages you might see (queues, persistent data, etc), and how to
deal with them in Docker at a basic level.
**[Tonton Video Penjelasan di Google Drive](https://drive.google.com/file/d/1x33fz4TJ9i-fSgA2QyV70XDVj9yshVQU/view?usp=drivesdk)**
119 changes: 42 additions & 77 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,96 +1,61 @@
# version is now using "compose spec"
# v2 and v3 are now combined!
# docker-compose v1.27+ required
# Menentukan versi syntax Docker Compose yang digunakan
version: '3.8'

# Mendefinisikan semua layanan (container) yang akan dijalankan
services:

# Layanan untuk antarmuka voting (frontend)
vote:
build:
context: ./vote
target: dev
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
volumes:
- ./vote:/usr/local/app
# Membangun image dari Dockerfile di dalam folder ./vote
build: ./vote
# Memetakan port 5000 di komputer Anda ke port 80 di dalam container
ports:
- "8080:80"
- "5000:80"
# Menghubungkan layanan ini ke jaringan 'vote-net'
networks:
- front-tier
- back-tier
- vote-net

result:
build: ./result
# use nodemon rather than node for local dev
entrypoint: nodemon --inspect=0.0.0.0 server.js
depends_on:
db:
condition: service_healthy
volumes:
- ./result:/usr/local/app
ports:
- "8081:80"
- "127.0.0.1:9229:9229"
# Layanan untuk database Redis (antrian pesan)
redis:
# Menggunakan image Redis resmi yang ringan
image: redis:alpine
networks:
- front-tier
- back-tier
- vote-net

# Layanan worker (backend) yang memproses vote
worker:
build:
context: ./worker
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
networks:
- back-tier

redis:
image: redis:alpine
volumes:
- "./healthchecks:/healthchecks"
healthcheck:
test: /healthchecks/redis.sh
interval: "5s"
# Membangun image dari Dockerfile di dalam folder ./worker
build: ./worker
# Menghubungkan ke jaringan yang sama
networks:
- back-tier
- vote-net

# Layanan untuk database PostgreSQL (penyimpanan data)
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
# Menggunakan image PostgreSQL versi 9.4
image: postgres:9.4
# Menyimpan data database secara permanen di volume 'db-data'
volumes:
- "db-data:/var/lib/postgresql/data"
- "./healthchecks:/healthchecks"
healthcheck:
test: /healthchecks/postgres.sh
interval: "5s"
# Pengaturan environment untuk koneksi yang lebih mudah (hanya untuk development)
environment:
POSTGRES_HOST_AUTH_METHOD: trust
networks:
- back-tier
- vote-net

# this service runs once to seed the database with votes
# it won't run unless you specify the "seed" profile
# docker compose --profile seed up -d
seed:
build: ./seed-data
profiles: ["seed"]
depends_on:
vote:
condition: service_healthy
# Layanan untuk menampilkan hasil voting
result:
# Membangun image dari Dockerfile di dalam folder ./result
build: ./result
ports:
- "5001:80"
networks:
- front-tier
restart: "no"

volumes:
db-data:
- vote-net

# Mendefinisikan jaringan agar semua container bisa saling berkomunikasi
networks:
front-tier:
back-tier:
vote-net:

# Mendefinisikan volume untuk penyimpanan data persisten
volumes:
db-data:
29 changes: 29 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# --- Konfigurasi Provider AWS ---
# Memberitahu Terraform bahwa kita akan menggunakan provider AWS
# untuk membuat resource di cloud Amazon
# Region bisa diubah sesuai kebutuhan, saya menggunakan jakarta
provider "aws" {
region = "ap-southeast-3" # Contoh: region Asia Pasific (Jakarta)
}

# --- Definisi Resource: S3 Bucket ---
# Blok ini adalah "resep" yang dimana untuk membuat satu resource,
# dalam hal ini sebuah S3 bucket (tempat penyimpanan file).
resource "aws_s3_bucket" "app_bucket" {
bucket = "99group-challenge-bucket-ryan-54321"

# 'tags' adalah label untuk mengorganisir dan mengidentifikasi resource
tags = {
Name = "DevOps Challenge Bucket"
Environment = "Development"
Owner = "Ryan"
}
}

# --- Output: Menampilkan Nama Bucket ---
# Blok ini digunakan untuk menampilkan nilai tertentu (seperti nama bucket)
# setelah Terraform berhasil membuat resource, akan sangat berguna untuk verifikasi
output "bucket_name" {
value = aws_s3_bucket.app_bucket.bucket
description = "The name of the S3 bucket created."
}
47 changes: 47 additions & 0 deletions monitoring_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Rencana Monitoring Aplikasi Voting

## 1. Alat yang Akan Digunakan

Untuk memastikan *observability* (kemampuan memantau dan memahami kondisi sistem), saya akan menggunakan tumpukan teknologi standar industri yang sering disebut "Stak PLG":

* **Prometheus**: Untuk mengumpulkan dan menyimpan metrik (data numerik) dari setiap layanan. Misalnya, penggunaan CPU, jumlah *request*, atau jumlah vote yang masuk
* **Loki**: Untuk mengumpulkan dan menganalisis log (catatan teks) dari semua container. Ini sangat berguna untuk *debugging* dan melacak *error*
* **Grafana**: Untuk membuat *dashboard* visual yang interaktif dari data metrik (Prometheus) dan log (Loki) agar mudah dibaca
* [cite_start]**Alertmanager**: Terintegrasi dengan Prometheus untuk mengirim notifikasi (misalnya ke email atau Slack) jika ada metrik yang melewati ambang batas tertentu

---
## 2. Metrik Kunci yang Akan Dipantau

Saya akan fokus pada tiga kategori metrik utama:

* **Metrik Sistem (Dasar)**:
* Penggunaan CPU dan Memori per container
* Ketersediaan (Uptime) setiap layanan
* Penggunaan disk pada volume database (`db-data`)

* **Metrik Aplikasi (Bisnis)**:
* **`http_requests_total`**: Jumlah total *request* HTTP yang diterima oleh layanan `vote` dan `result`.
* **`http_request_duration_seconds`**: Latensi atau waktu respons dari setiap *request*.
* Jumlah vote per detik yang masuk melalui layanan `vote`.
* Jumlah antrian di Redis untuk memastikan *worker* tidak kewalahan.

* **Metrik Database**:
* Jumlah koneksi aktif ke PostgreSQL
* Tingkat *query* per detik

---
## 3. Contoh Konfigurasi Prometheus

contoh dasar bagaimana file `prometheus.yml` akan dikonfigurasi untuk mengambil data metrik dari layanan yang berjalan. Ini mengasumsikan setiap layanan tersebut telah memiliki *endpoint* `/metrics`. [cite: 36]

```yaml
# prometheus.yml

scrape_configs:
- job_name: 'voting-app'
# Konfigurasi untuk menemukan target secara dinamis
# dari layanan yang didefinisikan di Docker Compose
static_configs:
- targets: ['vote:80', 'result:80', 'worker:80']
labels:
group: 'production'
Loading