Projek Introduction to Devops ini adalah lanjutan dari projek Introduction to Web Development Architecture. Projek ini berfokus pada melakukan deployment pada aplikasi website hingga mencapai tahap aplikasi web tersebut mendapatkan nama domain sehingga dapat diakses lewat internet. Projek ini menggunakan beberapa tools atau teknik pemrograman, seperti Docker (Dockerfile dan Docker Compose), CI/CD Pipeline, Nginx, dan Selenium.
FROM python:3.10.12-bullseye
WORKDIR /app
EXPOSE 5000
# Install pip requirements
COPY . /app
RUN python -m pip install -r requirements.txt
CMD ["python", "run.py"]
- FROM python:3.10.12-bullseye:
Menggunakan base image dari Python versi 3.10.12 pada sistem operasi Debian Bullseye. Ini menjadi dasar untuk image yang akan dibuat.
- WORKDIR /app:
Menetapkan direktori kerja (working directory) di dalam container ke "/app". Ini adalah lokasi di dalam container di mana file-file proyek akan ditempatkan dan perintah-perintah selanjutnya akan dijalankan.
- EXPOSE 5000:
Baris ini menginstruksikan Docker untuk membuka port 5000 saat image dijalankan. Baris ini memberi tahu Docker untuk mengizinkan koneksi masuk ke port tersebut.
- COPY . /app:
Menyalin seluruh konten dari direktori saat ini (di mana Dockerfile berada) ke direktori "/app" di dalam container. Ini mencakup file-file yang diperlukan untuk menjalankan aplikasi.
- RUN python -m pip install -r requirements.txt:
Menjalankan perintah untuk menginstal dependencies yang didefinisikan dalam file "requirements.txt" ke dalam container. Ini dilakukan selama proses pembuatan image.
- CMD ["python", "run.py"]:
Menentukan perintah default yang akan dijalankan ketika container dijalankan. Dalam hal ini, container akan menjalankan skrip Python "python run.py".
FROM node:18
WORKDIR /frontend
COPY package*.json ./
COPY .env ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 8080
CMD ["npm", "run", "preview"]
1.FROM node:18:
Menggunakan base image dari Node.js versi 18. Ini adalah dasar untuk image yang akan dibuat.
- WORKDIR /frontend:
Menetapkan direktori kerja (working directory) di dalam container ke "/frontend". Ini adalah lokasi di dalam container di mana file-file proyek akan ditempatkan dan perintah-perintah selanjutnya akan dijalankan.
- COPY package.json ./*:
Menyalin file package.json dan package-lock.json dari direktori saat ini ke direktori kerja di dalam container. Ini dilakukan terlebih dahulu untuk memanfaatkan layer cache Docker dan mengoptimalkan proses pembangunan.
- COPY .env ./:
Menyalin file ".env" dari direktori saat ini ke direktori kerja di dalam container. Ini memasukkan konfigurasi lingkungan yang diperlukan oleh aplikasi.
- RUN npm install:
Menjalankan perintah untuk menginstal semua dependencies yang didefinisikan dalam file package.json.
- COPY . ./:
Menyalin seluruh konten dari direktori saat ini ke direktori kerja di dalam container. Ini mencakup file-file proyek dan aplikasi yang akan dijalankan.
- RUN npm run build:
Menjalankan perintah untuk membangun aplikasi frontend. Ini termasuk kompilasi kode, pengelolaan dependensi, dan langkah-langkah pembangunan lainnya.
- EXPOSE 8080:
Baris ini menginstruksikan Docker untuk membuka port 8080 saat image dijalankan. Baris ini memberi tahu Docker untuk mengizinkan koneksi masuk ke port tersebut.
- CMD ["npm", "run", "preview"]:
Menentukan perintah default yang akan dijalankan ketika container dijalankan. Dalam hal ini, container akan menjalankan skrip "npm run preview".
version: '3.4'
services:
flask-app:
container_name: flask-compose
image: flask:0.0.6
build:
context: ./Final/Flask
dockerfile: Dockerfile
restart: always
ports:
- "5010:5000"
depends_on:
- postgres
postgres:
container_name: postgres-compose
image: postgres:12
restart: always
ports:
- "5433:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
volumes:
- postgres-data:/var/lib/postgresql/data
frontend:
image: frontend:latest
build:
context: ./Final/vue_project
dockerfile: Dockerfile
ports:
- 8090:8080
depends_on:
- flask-app
volumes:
postgres-data:
- version: '3.4':
Mendefinisikan versi format Docker Compose yang akan digunakan. Dalam hal ini, versi 3.4 digunakan.
- services:
Bagian ini mulai mendefinisikan layanan-layanan yang akan dijalankan sebagai kontainer.
- flask-app:
-
Mendefinisikan layanan untuk aplikasi Flask.
-
container_name: flask-compose: Menetapkan nama kontainer menjadi "flask-compose".
-
image: flask:0.0.6: Menggunakan image Docker bernama "flask:0.0.6".
-
build:: Mendefinisikan bagaimana image akan dibangun.
-
context: ./Final/Flask: Menentukan direktori konteks untuk build.
-
dockerfile: Dockerfile: Menentukan file Dockerfile yang akan digunakan.
-
restart: always: Menetapkan agar kontainer selalu di-restart jika berhenti atau terhenti.
-
ports:: Menetapkan pemetaan port, dengan port 5010 dari host diarahkan ke port 5000 di kontainer.
-
depends_on:: Menetapkan bahwa layanan ini bergantung pada layanan postgres.
- postgres:
-
Mendefinisikan layanan untuk database PostgreSQL.
-
container_name: postgres-compose: Menetapkan nama kontainer menjadi "postgres-compose".
-
image: postgres:12: Menggunakan image Docker PostgreSQL versi 12.
-
restart: always: Menetapkan agar kontainer selalu di-restart jika berhenti atau terhenti.
-
ports:: Menetapkan pemetaan port, dengan port 5433 dari host diarahkan ke port 5432 di kontainer.
-
environment:: Menetapkan variabel lingkungan untuk konfigurasi PostgreSQL.
-
volumes:: Menetapkan volume untuk menyimpan data PostgreSQL.
- frontend:
-
Mendefinisikan layanan untuk aplikasi frontend.
-
image: frontend:latest: Menggunakan image Docker bernama "frontend:latest".
-
build:: Mendefinisikan bagaimana image akan dibangun.
-
context: ./Final/vue_project: Menentukan direktori konteks untuk build.
-
dockerfile: Dockerfile: Menentukan file Dockerfile yang akan digunakan.
-
ports:: Menetapkan pemetaan port, dengan port 8090 dari host diarahkan ke port 8080 di kontainer.
-
depends_on:: Menetapkan bahwa layanan ini bergantung pada layanan flask-app.
- volumes::
Mendefinisikan volume Docker bernama "postgres-data" yang digunakan oleh layanan postgres untuk menyimpan data PostgreSQL
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
import chromedriver_autoinstaller
from pyvirtualdisplay import Display
display = Display(visible=0, size=(300,300))
display.start()
chromedriver_autoinstaller.install()
chrome_options = webdriver.ChromeOptions()
options = [
"--window-size=1200, 1200",
"--ignore-certificate-errors"
]
for option in options:
chrome_options.add_argument(options)
class TestFormElements:
def setup_class(cls):
cls.driver = webdriver.Chrome(options=chrome_options)
cls.driver.get("http://localhost:5173/login")
def teardown_class(cls):
cls.driver.quit()
def test_nama_label(self):
nama_label = self.driver.find_element(By.XPATH, "//label[@for='username']")
assert nama_label.text == "Username", "pengecekan nama gagal"
def test_password_label(self):
password_label = self.driver.find_element(By.XPATH, "//label[@for='password']")
assert password_label.text == "Password", "pengecekan password gagal"
- Import Libraries:
-
import pytest: Mengimport library Pytest untuk menjalankan test case.
-
from selenium import webdriver: Mengimport webdriver dari Selenium untuk mengontrol browser.
-
from selenium.webdriver.common.by import By: Mengimport metode untuk menemukan elemen di halaman web.
-
import chromedriver_autoinstaller: Mengimport library untuk menginstal ChromeDriver secara otomatis.
-
from pyvirtualdisplay import Display: Mengimport library untuk membuat virtual display (digunakan untuk menjalankan tes di lingkungan tanpa GUI).
- Konfigurasi Virtual Display:
-
display = Display(visible=0, size=(300, 300)): Membuat virtual display dengan ukuran 300x300 piksel dan tidak terlihat.
-
display.start(): Memulai virtual display.
- Instalasi ChromeDriver:
- chromedriver_autoinstaller.install(): Mengunduh dan menginstal ChromeDriver.
- Konfigurasi Chrome Options:
-
chrome_options = webdriver.ChromeOptions(): Membuat objek untuk menyimpan opsi untuk browser Chrome.
-
options : Daftar opsi untuk browser Chrome
-
--window-size=1200, 1200: Mengatur ukuran jendela browser menjadi 1200x1200 piksel.
-
--ignore-certificate-errors: Mengabaikan kesalahan sertifikat SSL.
-
for option in options: Menambahkan setiap opsi ke objek chrome_options.
- Test Class:
- class TestFormElements:: Mendefinisikan kelas untuk menampung test case.
- Setup dan Teardown:
-
def setup_class(cls): Dijalankan sebelum semua test case dalam kelas ini dijalankan:
-
cls.driver = webdriver.Chrome(options=chrome_options): Membuat instance Chrome webdriver dengan opsi yang telah ditentukan.
-
cls.driver.get("http://localhost:5173/login"): Membuka halaman login di browser.
-
def teardown_class(cls): Dijalankan setelah semua test case selesai dijalankan:
-
cls.driver.quit(): Menutup browser.
- Test Case:
-
def test_nama_label(self): Test case untuk memeriksa label nama:
-
nama_label = self.driver.find_element(By.XPATH, "//label[@for='username']]"): Menemukan label nama menggunakan XPath.
-
assert nama_label.text == "Username", "pengecekan nama gagal": Memastikan teks label nama adalah "Username".
-
def test_password_label(self): Test case untuk memeriksa label password:
-
password_label = self.driver.find_element(By.XPATH, "//label[@for='password']]"): Menemukan label password.
-
assert password_label.text == "Password", "pengecekan password gagal": Memastikan teks label password adalah "Password".
name: CI (Continuous Integration)
on:
pull_request:
branches: [ "main" ]
jobs:
build-testing:
name: Build and Testing
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Docker Compose
run: |
sudo apt-get update
sudo apt-get install -y docker-compose
- name: Build and Run Container
run: |
sudo docker compose up -d
- name: Install Requirements for Testing
run: |
pip install -r testing\requirements.txt
- name: Testing
run: |
sleep 20
pytest testing/test.py
- Name:
name: CI (Continuous Integration): Memberi nama pada workflow, memudahkan identifikasi.
- On:
-
pull_request: Menentukan bahwa workflow ini akan dijalankan ketika ada pull request dibuat.
-
branches: [ "main" ]: Menentukan bahwa workflow ini hanya akan dijalankan untuk pull request yang dibuat terhadap branch "main".
- Jobs:
Bagian ini mendefinisikan tugas-tugas yang akan dijalankan dalam workflow.
- build-testing:
-
name: Build and Testing: Memberi nama pada job ini.
-
runs-on: windows-latest: Menentukan bahwa job ini akan dijalankan pada runner Windows.
- Steps:
Bagian ini mendefinisikan langkah-langkah yang akan dijalankan dalam job.
- Checkout repository:
uses: actions/checkout@v2: Mengambil kode dari repository ke runner.
- Install Docker Compose:
sudo apt-get update: Memperbarui daftar paket yang tersedia. sudo apt-get install -y docker-compose: Menginstal Docker Compose.
- Build and Run Container:
sudo docker compose up -d: Menjalankan perintah Docker Compose untuk membangun dan menjalankan kontainer.
- Install Requirements for Testing:
pip install -r testing\requirements.txt: Menginstal dependensi yang diperlukan untuk menjalankan tes.
- Testing:
sleep 20: Menunggu 20 detik untuk memastikan kontainer sudah berjalan dengan baik. pytest testing/test.py: Menjalankan tes yang terdapat pada file testing/test.py.
name: CD (Continuous Delivery & Deployment)
on: workflow_dispatch
jobs:
build-push:
name: Build and Push Image To DockerHub
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push flaskapp
uses: docker/build-push-action@v4
with:
context: ./Final/Flask
file: ./Final/Flask/Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/flask:${{ github.run_number }}/Flask:latest
- name: Build and push postgres
uses: docker/build-push-action@v4
with:
context: ./Final/vue_project
file: ./Final/vue_project/Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/vue_project:${{ github.run_number }}/vue_project:latest
deploy:
name: Deploy to Server
runs-on: self-hosted
needs: build-push
steps:
- name : Pull latest images
run: |
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/Flask:latest
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/vue_project:latest
- name: Stop and Remove Existing Containers and Networks
run: |
docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)
docker network prune -f
- name: Create Network and Run containers
run : |
sudo docker compose up -d
- name: Remove unused data
run: |
docker system prune -af
- Name:
name: CD (Continuous Delivery & Deployment): Memberi nama pada workflow, memudahkan identifikasi.
- On:
workflow_dispatch: Menentukan bahwa workflow ini akan dijalankan secara manual, bukan otomatis saat ada event tertentu.
- Jobs:
Bagian ini mendefinisikan tugas-tugas yang akan dijalankan dalam workflow.
- build-push:
- name: Build and Push Image To DockerHub: Memberi nama pada job ini.
- runs-on: windows-latest: Menentukan bahwa job ini akan dijalankan pada runner windows.
- Steps (build-push):
-
Checkout Repository: Mengambil kode dari repository ke runner.
-
Login to Docker Hub: Login ke Docker Hub untuk mendorong image.
-
Set up Docker Buildx: Mengatur Docker Buildx untuk membangun image multi-arsitektur.
-
Build and push flaskapp: Membangun dan mendorong image flaskapp ke Docker Hub.
-
Build and push vue_project: Membangun dan mendorong image vue_project ke Docker Hub.
- deploy:
-
name: Deploy to Server: Memberi nama pada job ini.
-
runs-on: self-hosted: Menentukan bahwa job ini akan dijalankan pada runner yang di-host sendiri.
-
needs: build-push: Menentukan bahwa job ini bergantung pada job build-push untuk selesai terlebih dahulu.
- Steps (deploy):
-
Pull latest images: Menarik image terbaru dari Docker Hub.
-
Stop and Remove Existing Containers and Networks: Menghentikan dan menghapus kontainer dan jaringan yang sudah ada.
-
Create Network and Run containers: Membuat jaringan dan menjalankan kontainer menggunakan Docker Compose.
-
Remove unused data: Menghapus data yang tidak digunakan untuk menghemat ruang disk.
name: CD (Continue Deployment)
on: workflow_dispatch
jobs:
deploy:
name: Deploy to Server
runs-on: self-hosted
steps:
- name : Pull newest images
run: |
docker pull ${{secrets.DOCKERHUB_USERNAME}}//Flask:${{github.run_number}}
docker pull ${{secrets.DOCKERHUB_USERNAME}}//vue_project:${{github.run_number}}
- name : Stop and Remove Existing Containers and Networks
run: |
docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)
docker network prune -f
- name : Create Network and Run Containers
run: |
docker network create vite
docker run -d -p 5000:5000 --network vite --hostname Flask --mount "type=volume,source=pgdata,destination=/var/lib" --name flask -e
sleep 5
docker run -d -p 5173:5173 --network vite --hostname vue_project %{{ secret.DOCKERHUB_USERNAME}}/vue_project:%{{github.run_number}}
- name : Remove unused data
run: |
docker system prune -af
- Name:
name: CD (Continue Deployment): Memberi nama pada workflow, memudahkan identifikasi.
- On:
workflow_dispatch: Menentukan bahwa workflow ini akan dijalankan secara manual, bukan otomatis saat ada event tertentu.
- Jobs:
Bagian ini mendefinisikan tugas-tugas yang akan dijalankan dalam workflow.
- deploy:
-
name: Deploy to Server: Memberi nama pada job ini.
-
runs-on: self-hosted: Menentukan bahwa job ini akan dijalankan pada runner yang di-host sendiri (bukan runner GitHub).
- Steps:
Pull newest images:
- docker pull ...: Menarik image terbaru dari Docker Hub menggunakan tag yang menyertakan nomor run workflow, memastikan penggunaan image yang sesuai dengan deployment ini.
Stop and Remove Existing Containers and Networks:
- docker stop ...: Menghentikan semua kontainer yang sedang berjalan.
- docker rm ...: Menghapus semua kontainer yang telah dihentikan.
- docker network prune -f: Menghapus semua jaringan yang tidak digunakan.
Create Network and Run Containers:
- docker network create vite: Membuat jaringan baru bernama "vite" untuk menghubungkan kontainer.
- docker run ... flask: Menjalankan kontainer untuk aplikasi Flask:
- -d: Menjalankan kontainer di background.
- -p 5000:5000: Memetakan port internal 5000 ke port 5000 pada host.
- --network vite: Menempatkan kontainer pada jaringan "vite".
- --hostname Flask: Memberikan hostname "Flask" pada kontainer.
- --mount ...: Memasang volume bernama "pgdata" ke direktori "/var/lib" di dalam kontainer, kemungkinan untuk penyimpanan data PostgreSQL.
- --name flask: Memberikan nama "flask" pada kontainer.
- -e: Mengatur variabel lingkungan, namun nilainya tidak ditampilkan dalam kode yang diberikan.
- sleep 5: Menunggu 5 detik untuk memastikan kontainer Flask sudah berjalan dengan baik sebelum menjalankan kontainer vue_project.
- docker run ... vue_project: Menjalankan kontainer untuk aplikasi vue_project dengan pengaturan yang mirip dengan kontainer Flask.
Remove unused data:
docker system prune -af: Menghapus semua data yang tidak digunakan, seperti image, kontainer, dan volume yang tidak lagi terpakai, untuk menghemat ruang disk.