Deployment dengan Docker

Norman Bintang
LEARNFAZZ
Published in
5 min readApr 1, 2019
Arsitektrur LEARNFAZZ

Arsitektur LEARNFAZZ terdiri dari:

  1. Aplikasi Android, yang bertugas untuk memberikan tampilan ke pengguna. Ditulis menggunakan flutter.
  2. Backend Server, yang bertugas untuk menghandle logic dari aplikasi, seperti Login, Enroll ke Course, dan sebagainya. Ditulis menggunakan golang.
  3. Database Server, yang bertugas untuk menyimpan data-data yang ada, seperti data Pengguna, Course, dan sebagainya. Menggunakan postgres.
  4. Storage & Messaging Server, yang bertugas untuk menyimpan berkas-berkas yang diunggah oleh pengguna seperti foto maupun dokumen lainnya, dan juga untuk memberikan notifikasi ke aplikasi android (Fitur notifikasi saat ini belum diimplementasikan). Menggunakan firebase.

Untuk Aplikasi Android dan Storage & Messaging Server tidak memerlukan docker untuk deploymentnya. Maka, saya hanya akan membahas deployment Backend Server dan Database Server.

Deployment yang menggunakan docker kami lakukan dengan menggunakan https://portainer.docker.ppl.cs.ui.ac.id yang telah disediakan oleh fasilkom.

Hal yang perlu dilakukan untuk mendeploy aplikasi dengan portainer:

  1. Menyiapkan image

Image adalah sebuah executable package yang terdiri dari aplikasi, runtime, library, environment variable, dan file konfigurasi. Untuk database, karena kami menggunakan postgres, imagenya bisa didapatkan dari https://hub.docker.com/_/postgres . Sedangkan untuk backend server, image harus dibuat sendiri.

Membuat image kami lakukan dengan menjalankan:

docker build -t registry.docker.ppl.cs.ui.ac.id/pplb4/back-end:${ENVIRONMENT} .

${ENVIRONMENT} adalah sebuah variable yang menandakan untuk environment apa image tersebut dibuat. Kami mememiliki 2 environment, yaitu development dan staging.

. adalah relative path di mana aplikasi yang akan dibuild berada.

Kemudian perintah docker build akan membuatkan sebuah image dengan mengikuti langkah-langkah yang dituliskan dalam file bernama Dockerfile.

Dockerfile

# 1
FROM golang
# 2
ENV GOPKG $GOPATH/src/gitlab.cs.ui.ac.id/ppl-fasilkom-ui/2019/PPLB4/back-end
ENV GO111MODULE on
# 3
RUN mkdir -p $GOPKG
COPY . $GOPKG/
WORKDIR $GOPKG
# 4
RUN GOBIN=`pwd`/output go install infrastructure/cmd/main.go
RUN ./dependency.sh
# 5
ENTRYPOINT ["./docker-entrypoint.sh"]

dependency.sh

#!/bin/shcurl -s https://packagecloud.io/install/repositories/golang-migrate/migrate/script.deb.sh | bashapt-get install -y migrate

docker-entrypoint.sh

#!/bin/shmigrate -path infrastructure/migrations/ -database ${DB_SOURCE} up
./output/main

Blok #1 mendeklarasikan dependency image aplikasi kami, yaitu membutuhkan image golang.

Blok #2 mendeklarikan environment variable yang dibutuhkan saat build.

Blok #3 menyiapkan directory di mana aplikasi kami akan disimpan dalam image.

Blok #4 membuat executable dari aplikasi kami, dan menginstall dependency apa saja yang dibutuhkan. Beda dependency yang di sini dengan blok #1 adalah, dependency pada blok #1 khusus untuk image docker, sementara dependency pada blok #4 dependency selain image docker.

Blok #5 berisi perintah yang dijalankan saat container dijalankan. Ini kami gunakan untuk melakukan migration database dan menjalankan aplikasi server.

Setelah membuat image, agar image bisa digunakan di portainer, image harus dipush. Cara untuk push image:

docker push registry.docker.ppl.cs.ui.ac.id/pplb4/back-end:${ENVIRONMENT}

Perhatikan bahwa yang dipush haruslah sesuai dengan tag yang digunakan saat build.

2. Menyiapkan volume

Secara default, docker akan menyimpan storage di dalam container. Tetapi untuk database, tentu kita tidak mau kehilangan data yang ada saat container databasenya diganti. Untuk itu perlu dibuat volume.

Membuat volume dapat dilakukan dengan:

Membuat volume

Karena kami memiliki 2 environment, kami membuat 2 buah volume, 1 untuk development dan 1 untuk staging.

3. Menyiapkan network

Network dibutuhkan agar container-container yang dibuat bisa berkomunikasi satu sama lain.

Cara membuat network:

Membuat network

Driver yang digunakan adalah bridge. Untuk Network configuration, karena saat itu saya malas berpikir, saya hanya mengikuti sesuai placeholder dan bisa bekerja :p

Driver network bridge merupakan default network driver. Bridge memungkinkan container-container yang berada pada host dan network yang sama untuk saling berkomunikasi dan tetap terisolasi dengan container yang berada pada network berbeda.

Berbeda dengan driver network host. Driver host menghilangkan isolasi network antara container dengan hostnya. Ini membuat container dapat diakses dari luar environment docker. Cara mengaksesnya adalah dengan mengakses IP dari hostnya dan pilih port yang sesuai dengan containernya.

Di sini kami menggunakan driver bridge karena kami menginginkan isolasi container dari network yang berbeda.

4. Membuat container

Container adalah sebuah instance dari image yang sedang berjalan. Satu image yang sama bisa dibuatkan banyak container. Kami membuat container sebanyak jumlah environment yang ada.

Untuk memudahkan pembuatan container, kami melakukannya dengan menggunakan docker-compose, atau dalam portainer disebut dengan stack.

Berikut adalah file docker-compose.yml kami.

version: '2'services:
postgres:
image: postgres:latest
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- learnfazz-development-postgres_data:/var/lib/postgresql/data
networks:
private:
ipv4_address: ${DB_PRIVATE_IP}
backend:
depends_on:
- "postgres"
image: registry.docker.ppl.cs.ui.ac.id/pplb4/back-end:${ENVIRONMENT}
ports:
- ${BACKEND_PUBLIC_PORT}:8080
environment:
DB_SOURCE: postgres://${DB_USER}:${DB_PASSWORD}@${DB_PRIVATE_IP}:5432/${DB_NAME}?sslmode=disable
networks:
private:
ipv4_address: ${BACKEND_PRIVATE_IP}
volumes:
learnfazz-development-postgres_data:
name: learnfazz-${ENVIRONMENT}-postgres_data
external: true
networks:
private:
external:
name: learnfazz-${ENVIRONMENT}-private

Semua yang ditulis seperti ${} akan digantikan dengan nilai dari environment variable yang ditulis saat pembuatan stack.

Dari sana terlihat kami memiliki 2 service, yaitu postgres sebagai database dan backend sebagai aplikasi kami.

Service postgres menggunakan volumes agar data tidak disimpan dalam container, namun disimpan dalam volume yang digunakan. Nama dari volumes yang digunakan haruslah sesuai dengan yang dibuat pada langkah 2. Untuk database, jika memang tidak dibutuhkan, port tidak perlu diexpose ke publik demi keamanan.

Service backend memiliki atribut depends_on postgres, yang artinya service ini akan dibuat setelah service postgres selesai dibuat. Backend juga menuliskan ports apa saja yang diexpose ke public.

Saya sempat menemukan masalah dalam membuat stack. Ketika itu saya ingin membuat stack dengan nama learnfazz-development-stack. Walaupun terdapat notifikasi pembuatan stack sukses, saat saya cek isi dari stack tersebut, containernya tidak ada.

Container gagal dibuat

Saya jadi bingung, apakah file docker-compose saya yang salah atau memang portainernya yang ngebug. Saya mulai frustrasi. Karena mengetik nama learnfazz-development-stack cukup panjang, saya iseng untuk membuat stack dengan nama haha menggunakan file docker-compose yang sama. Secara ajaib, container berhasil terbuat.

Pembuatan stack dengan nama haha berhasil

Hal tersebut membuat saya yakin file docker-compose saya valid, saya coba lagi membuat stack dengan nama learnfazz-development-stack. Ternyata container gagal terbuat. Setelah cukup lama, barulah saya sadari bahwa containernya gagal terbuat apabila nama stack mengandung karakter -. Semoga dengan membaca ini Anda tidak terperangkap dengan jebakan tersebut.

--

--