Software Architecture
Catatan: Tulisan ini dibuat sepenuhnya dalam rangka kegiatan mata kuliah PPL. Segala isi dari artikel ini tidak menjamin kelengkapan dan keutuhan sebagai pedoman untuk diimplementasikan dalam suatu proyek.
Arsitektur Sistem Informasi Praktikum (SIP)
Sistem Informasi Praktikum dibangun dengan arsitektur semi — microservice. Alasan menggunakan microservice karena proyek kami berisi 10 orang (2 kelompok) sehingga untuk mempercepat iterasi pembuatan produk, continuous deployment, dan produktifitas developer rencana untuk beralih ke microservice menjadi pilihan pertama, meskipun pada awal-awal Setup agak sulit karena perbedaan-perbedaan pendapat mengenai arsitektur dan lain-lain.
Selanjutnya, bentuk dari arsitektur proyek Sistem Informasi Praktikum adalah sebagai berikut.
Pada bagian Frontend kami menggunakan React JS, Backend menggunakan Django Rest Framework, Nginx sebagai Reverse Proxy dan Load Balancer, SSO sebagai auth third party yang disediakan Universitas Indonesia, dan Relational Database yang digunakan PostgreSQL karena memiliki banyak support + tim kami sudah cukup mengenal karena pernah menggunakan itu sebelumnya. Lalu setelah melihat arsitekur tersebut bagaimana peran Docker dalam mengkontainerisasi servis backend kami? Jadi apa itu Docker sebenarnya?
Apa itu Docker?
Docker adalah suatu alat yang dapat mereduksi gap antara Development dan Deployment pada fase iterasi Software Development.
Sebagai contoh saja, misal kita ingin menyesuaikan kode yang berjalan di mesin kita, asumsi teman Anda mengerjakan di Linux dan yang lain menggunakan Windows. Lalu salah satu dari anggota tadi memiliki problem seperti dependencies, setup database, line-break (linter), dan masalah lainnya. Proses ini tentunya akan memakan waktu sehingga produktifitas kemungkinan mengalami penurunan. Mungkin salah satu yang terfikirkan adalah menggunakan VM (VirtualMachine), tapi sorry bro susah dan repot kalau setup VM + memakan resource cukup besar untuk server.
Kenapa Docker?
Sebenernya banyak jenis-jenis container , seperti CRI-O, Podman, Docker, LXD, dan lain-lain. Namun, kali ini kami menggunakan Docker karena sudah cukup populer dan memiliki banyak support.
Lalu kenapa Docker? dibandingkan VM, Docker lebih ringan karena Docker tidak mengcopy seluruh bagian operasi sistem. Docker menggunakan beberapa instance dari hal-hal di Linux seperti namespace Linux (etc, mount, net, pid) sehingga ukuran umumnya lebih kecil dibandingkan VM.
Docker berjalan independen dan terisolasi sehingga tidak menginterupsi proses lain. Oleh karena itu, Docker dapat dijalankan sebanyak mungkin (tetapi tetap melihat resource server OS yang sedang berjalan).
Bagaimana Docker diimplementasikan pada proyek?
Setelah docker diinstall, langkah selanjutnya adalah mendefinisikan project tersebut menjadi suatu Docker Image.
Docker image adalah ekivalen dengan istilah “snapshot” pada virtual machine. Docker image berisi file proyek yang ingin di-execute dan nantinya siap dijalankan pada suatu container. Docker images bersifat immutable yang artinya tidak dapat dimodifikasi, namun bisa diduplikasi dan dibagikan atau dihapus. Positifnya jika ada perubahan atau ingin melakukan rollback (kembali ke versi sebelum), image tadi masih terdapat disana dan dapat digunakan kembali. Berikut langkah-langkahnya
Pertama, siapkan Dockerfile dan definisikan bagaimana aplikasi tersebut siap di-execute pada saat perintah docker dijalankan ketika runtime.
# DockerfileFROM python:3.8ENV PYTHONUNBUFFEREDENV PYTHONDONTWRITEBYTECODE 1RUN mkdir /appWORKDIR /appCOPY requirements.txt /app/RUN pip install -r requirements.txtCOPY . /app/
Langkah selanjutnya, pastikan posisi tempat path dari proyek kita berada, dan build image tersebut dengan menjalankan perintah
docker build -t <nama-image>:<tag> .
Pada proyek kami nama dan tag dari image yang di build adalah
// berada pada path root project "."docker build -t mkppl-backend:latest .
Setelah itu, kita jalankan container (dari image) dengan perintah :
docker run -p 8000:8000 mkppl-backend:latest
Deployment dengan docker-compose.yml
Ketika kita dihadapkan untuk melakukan serve suatu aplikasi dengan berbagai stacks yang terikat, maka docker compose dapat membantu mendefinisikan stacks apa saja yang dapat di-link ke aplikasi yang bersangkutan. Stacks disini sudah berupa container-container untuk memprovide multi container service.
Disini kelompok kami menggunakan PostgreSQL sebagai DBMS dan Django sebagai Backend nya pada lingkungan staging, namun sebelum di serve ke staging, saya melakukan test terlebih dahulu di lingkungan development, lalu docker compose hadir untuk membantu hal itu. Kenapa memakai docker dan docker-compose? karena saya malas melakukan instalasi + bisa mengganggu dependensi lain pada PC Host.
Tahapan-tahapan yang perlu dilakukan untuk menggunakan docker compose:
- Pastikan image dari PostgreSQL atau DBMS(MySQL, MariaDB, dll) sudah ada atau di-define di
docker-compose.yml
. - Definisikan
Dockerfile
dari aplikasi seperti penjelasan sebelumnya. - Definisikan
docker-compose.yml
, isinya berupa environment yang dibutuhkan untuk masing-masing service. Service yang digunakan kali ini db (database-PostgreSQL) dan web (backend-django).
Berikut adalah docker-compose.yml
proyek kelompok kami
version: "3.7"
services:
db:
image: postgres
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432"
volumes:
- ./db_backend:/var/lib/postgresql/data/
networks:
- backend
web:
build: .
command: python3 manage.py runserver 0.0.0.0:9000 --settings=sip.settings.staging
volumes:
- .:/app
- ./sip/settings/static:/app/static
environment:
SECRET_KEY: ${SECRET_KEY}
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASS: ${DB_PASS}
DB_HOST: ${DB_HOST}
DB_PORT: ${DB_PORT}
ports:
- "8001:9000"
depends_on:
- db
networks:
- backend
networks:
backend:
driver: bridge
docker-compose.yml
memerlukan notasi-notasi untuk mendefinisikan apa saja yang dibutuhkan seperti services dan networks.
Pada servis db kita lihat port yang didefinisikan adalah 5432 (default) dan volume database di mount dari /var/lib/postgresql/data
ke /db_backend
(lokasi di host PC).
Selanjutnya adalah servis web, servis diatas mengekspos port dari dalam (docker), yaitu port 9000 dan di binding keluar (public) dengan port 8001. Tidak lupa untuk menyiapkan .env
file untuk menyimpan environment variables sehingga siap di load oleh konfigurasi settings dari servis web backend-django. Setiap environment variables yang di-load menggunakan notasi${NAMA_ENV}
.
Lalu bagaimana environment tersebut dapat dilihat?
Tentunya ada file config yang bernama .env
. File tersebut akan dibaca oleh docker-compose dan secara otomatis variable yang akan dibutuhkan akan disesuaikan. Pada implementasi kami menggunakan gitlab
sebagai version control untuk mem-passing variable tersebut.
Yang terakhir adalah context network
disini menggunakan bridge agar container tadi dapat berkomunikasi dalam network yang sama (untuk development).
Setelah file-file yang dibutuhkan telah ada, maka langkah selanjutnya adalah menjalankan perintah sebagai berikut:
docker-compose build
docker-compose up -d
(daemon), disini untuk serve up aplikasi
Jika servis aplikasi sudah berhasil di up, maka selanjutnya jalankan perintah docker ps
untuk melihat status container, kurang lebih perintah akan menghasilkan tampilan seperti ini
Setelah itu, kita akses container tersebut untuk menjalankan migrate
pada aplikasi backend-django tersebut. Perintah untuk mengeksekusinya adalah
docker exec -it <container_id> /bin/bash
Jika sudah masuk environment container, jalankan perintah
python manage.py makemigrations
dan python manage.py migrate
Kita bisa akses servis backend tersebut pada port 8001
Jika sudah selesai, maka servis dapat dimatikan dengan perintah
docker-compose down
Continuous Deployment
Pada bagian sebelumnya kami masih melakukan hal manual, yaitu dengan masuk ke dalam container dan melakukan up service dari dalam.
Oleh karena itu kami menerapkan yang namanya continuous deployment agar bisa mempercepat iterasi pada pengembangan sistem di produksi.
Sudah diberitahukan diawal bahwa kami menggunakan gitlab
sebagai bagian dari CD itu sendiri, maka script .gitlab-ci.yml
diperlukan didalamnya. Berikut adalah script yang kami gunakan:
Yang perlu diperhatikan dari script diatas adalah pada bagian job deploy_vm
disini kami menggunakan Ubuntu sebagai base image dan jika dilihat perlu scp
dan ssh
dalam proses deploy. Kembali ke masalah .env
tadi, jadi untuk mengirimkan berkas .env
agar dapat dibaca oleh docker-compose.yml
yaitu dengan mendeskripsikan before_script
dan melakukan echo environment variables ke .env
.
Setelah itu, .env
akan di copy ke proxy server
(dalam kasus ini kami tidak bisa langsung ke server tujuan untuk deploy) dan selanjutnya file .env
di copy lagi ke server tujuan
setelah melakukan ssh
ke proxy server
.
Jika sudah masuk ke server tujuan
langkah selanjutnya adalah masuk ke directory yang diinginkan dan kita melakukan pull
dengan git.
Jika sudah masuk, maka service dimatikan, di-build, dan di-up lagi dengan perintah docker-compose.
Command:
cd ~/Documents/mkppl-si-penilaian-dan-evaluasi-praktikum && git pull origin staging && docker-compose down && docker-compose build && docker-compose up -d
Bagaimana jika ingin deploy pada Cloud? seperti AWS atau Azure
Sebenarnya tahapan yang diperlukan mirip-mirip, jika pada Cloud atau VPS biasa kita menggunakan jenkins, travis-ci, gitlab-ci sebagai helper CI/CD tentunya akan ada perbedaan config dan sintaks, namun konsep untuk melakukan deploy adalah sama yaitu dari manual kita otomasi. Mungkin pada Cloud akan agak sedikit berbeda karena ada masalah konfigurasi yang perlu di-setting lebih lanjut, seperti IaM, Ingress/Egress, dll. Untuk lebih lanjut bisa dibaca pada dokumentasi masing-masing provider.
Sekian artikel mengenai Software Architecture, semoga bermanfaat.
Reference:
https://dev.to/matthias/getting-started-with-docker-compose-4gg9
https://searchitoperations.techtarget.com/definition/Docker-image