Salah satu yang menarik dari docker adalah dukungan komunitas yang sangat kuat. Docker bahkan menjadi standar dalam deployment kubernetes
(kita akan membahas kubernetes di bagian selanjutnya).
Dalam repo ini, saya akan menunjukkan contoh pemanfaatan docker secara nyata. Pada repo ini kita akan membangun aplikasi "notepad" online seperti ini menggunakan redis
dan node-js
.
Perlu diingat, bahwa redis
umumnya dipakai untuk caching, sehingga tidak cocok untuk dijadikan penyimpanan permanen. Jika teman-teman berminat mengembangkan aplikasi ini lebih lanjut, silahkan menggunakan mysql
, mongo
atau database engine lain.
- docker
- docker-compose
- node-js & npm
- kubectl
- akun docker-hub
- akun okteto
- ketekunan dan kemauan untuk belajar :)
Catatan:
- Saya menggunakan ubuntu-WSL dalam windows
- Untuk menginstall docker di windows, kalian harus menggunakan windows 10 pro dengan hyper-v yang telah diaktifkan
- Ikuti panduan ini untuk mengakses docker-for-windows dari dalam WSL
- Atau gunakan linux :)
Docker adalah platform virtualisasi di level OS. Docker biasa dipakai untuk membuat container
.
Container sendiri adalah paket software yang terisolasi (saling lepas satu sama lain). Adapun isolasi yang dilakukan hanya di level aplikasi saja. Dengan demikian satu container
dan container
lain masih bisa berbagi kernel
yang sama. Sifat kernel-sharing inilah yang membedakan docker-container dengan virtual machine.
Docker melakukan virtualisasi di level aplikasi, sehingga semua container
di satu mesin memakai kernel OS yang sama.
Jika kalian memakai linux, maka docker-container kalian akan memakai kernel yang sama dengan OS utama. Sebaliknya, jika teman-teman menggunakan windows/mac, maka akan ada level virtualisasi tambahan untuk emulasi kernel linux.
Virtual machine melakukan virtualisasi di level hardware, sehingga antar VM menggunakan kernel, alokasi RAM, dan alokasi CPU yang berbeda. Akibatnya untuk membuat virtual-machine, kita diwajibkan membuat spesifikasi RAM dan CPU untuk setiap VM.
Ikuti panduan di situs resmi docker
Untuk membuat sebuah docker-container
, kita perlu terlebih dahulu mendefinisikan spesifikasi-nya. Spesifikasi inilah yang kita kenal dengan sebutan docker-image
.
Saat ini sudah ada banyak sekali docker-image
siap pakai. Kalian bisa mengunjungi docker hub untuk mencari docker-image yang kalian butuhkan.
Kebetulan, docker-image untuk redis pun sudah tersedia dan siap dipakai.
Untuk men-download docker-image, kita bisa menggunakan perintah docker pull
. Berikut contoh penggunaannya:
docker image pull redis:latest
Kalian juga bisa melihat daftar docker-images
yang tersedia di komputer lokal dengan menjalankan perintah
docker image ls
Tips: Biasanya perintah docker memiliki format seperti ini:
docker <entitas> <aksi> [opsi]
Jadi kalian bisa sedikit "asal tebak", tidak perlu menghafal semua perintah :)
Untuk membuat docker container dengan nama my-redis
dari image redis:latest
, kalian bisa menjalankan perintah berikut:
docker run --name my-redis -p 6379:6379 -d redis:latest
Ada beberapa parameter yang sering dipakai pada perintah docker run
:
--name
untuk setting nama container.-p
untuk port forwarding. Format yang dipakai adalah-p <host-port>:<container-port>
. Jadi seandainya kita ingin port6379
pada container kita bisa diakses melalui port4000
pada localhost, maka kita bisa melakukan perintah ini:docker run --name my-redis -p 4000:6379 -d redis:latest
.-d
untuk men-spesifikasikan nama image.-e
untuk setting environmant variable. Format yang digunakan adalah-e <nama-variable>=<isi-variable>
. Untuk saat ini, belum ada environment yang perlu di set.-v
untuk membuat persistance volume di host. Format yang digunakan adalah-v <path-di-host>:<path-di-container>
. Untuk saat ini, kita tidak akan menggunakan volume.
Catatan:
- Jalankan
docker start my-redis
dandocker stop my-redis
untuk menjalankan atau memberhentikan containermy-redis
.- Jalankan
docker ps
untuk melihat list container yang sedang berjalan.
Pada dasarnya, redis banyak digunakan untuk caching. Redis menyimpan cache kita dalam RAM. Oleh sebab itu, io-access-time
relatif lebih kecil daripada RDBMS seperti MySQL yang melakukan penyimpanan di harddisk.
Karena ini pula, sangat tidak disarankan untuk menggunakan redis sebagai media penyimpanan utama (seperti yang akan kita lakukan sebentar lagi... he3x).
Cache pada redis disimpan dengan format key-value
. Jika pada komputer kalian terinstall redis-cli atau aplikasi redis-client lain, kalian bisa mencoba perintah-perintah berikut:
> set password rahasia
OK
> get password
"rahasia"
> keys *
1) "password"
> del password
(integer) 1
Kembali ke tujuan awal yang sudak kita sepakati tadi, kita ingin membuat aplikasi notepad-online. Untuk kita membutuhkan sebuah web-application.
Coding web-application kita terletak di folder app
dari repository ini.
Untuk menjalankannya, kalian bisa menuliskan perintah berikut:
cd app
npm install
node index
Aplikasi akan berjalan di port 3000. Kalian bisa mengaksesnya menggunakan alamat https://localhost:3000
pada browser.
Aplikasi yang sudah kita buat ini memiliki beberapa konfigurasi yang bisa diubah melalui environment variable.
Secara khusus perhatikan bagian kode ini:
const httpPort = process.env.HTTP_PORT || 3000;
const redisHost = process.env.REDIS_HOST || 'localhost';
const redisPort = parseInt(process.env.REDIS_PORT || '6379', 10);
Ketiga baris itu memungkinkan kalian bisa mengubah isi variable httpPort
, redisHost
, dan redisPort
tanpa mengubah kode sama sekali.
Untuk mencobanya, coba lakukan perintah berikut
HTTP_PORT=4000 node index
Nah, sekarang kita ingin meng-kontainer kan aplikasi kita. Untuk melakukan ini, kita bisa harus terlebih dahulu mendefinisikan spesifikasi docker-image
.
Spesifikasi docker-image
biasanya disimpan pada file Dockerfile
.
Berikut isi Dockerfile
untuk Web-app kita:
# image kita adalah turunan dari node:latest
FROM node:latest
# di sini kita mendefinisikan bahwa working directory di container adalah
# /usr/src/app
WORKDIR /usr/src/app
# di sini kita meng-copy package.json dan package-lock.json ke
# working directory container (./)
COPY package*.json ./
# setelah package.json dan package-lock.json ter-copy ke
# working directory container, kita menjalankan npm install untuk
# men-download library external ke container
RUN npm install
# terakhir, kita meng-copy semua source yang ada ke working directory container
COPY . .
# bagian ini sebenarnya tidak terlalu penting, hanya untuk dokumentasi saja,
# supaya orang lain tahu bahwa aplikasi kita ter-expose di port 3000
# tanpa perlu membuka source-code kita.
EXPOSE 3000
# untuk menjalankan aplikasi kita, perintah yang perlu dieksekusi adalah
# "node index"
CMD [ "node", "index" ]
Satu hal lagi yang menarik, kita bisa mem-bundle sejumlah docker-image supaya bisa didistribusikan dengan lebih mudah.
Dengan demikian, kalian hanya membutuhkan docker
dan docker-compose
untuk menjalankan aplikasi notepad-online kita. Kalian bahkan tidak perlu menginstall Node.js !!!
Berikut adalah docker-compose.yaml
yang kita gunakan:
version: '2.0'
services:
# web adalah alias untuk web-app kita
web:
build: ./app # aplikasi kita menggunakan Dockerfile di folder `./app`
ports:
- "8080:3000" # kalian bisa mengakses aplikasi kita melalui localhost:8080
environment:
- HTTP_PORT=3000
- REDIS_PORT=6379
- REDIS_HOST=redis # perhatikan, di sini kita menggunakan alias
depends_on:
- redis
# redis adalah alias untuk redis container
redis:
image: redis:latest # untuk redis, kita akan menggunakan image redis:latest
ports:
- "6379:6379" # kalian bisa mengakses redis melalui localhost:6379
Untuk menjalankan docker-compose, kalian bisa menuliskan perintah docker-compose up
Setelah menjalankan docker-compose, kalian bisa coba menjalankan perintah docker ps
.
Di sini kalian akan melihat bahwa nama container yang dibuat dengan menggunakan docker-compose adalah <nama-folder>_<service-name>
.
Bagaimana jika kita ingin mendistribusikan docker-image kita tanpa mendistribusikan source code?
Dalam kasus seperti itu, kita bisa mempublikasikan image kita ke docker-hub (atau registry lain).
Untuk itu, pertama-tama kalian harus memiliki akun di hub.docker.com
.
Setelah memiliki akun di docker-hub, kalian perlu membuat repository
. Dalam kasus ini, saya membuat repository gofrendi/notepad
.
docker login
docker tag docker-workshop_web <user-name>/notepad
docker push <user-name>/notepad
Catatan: saat tulisan ini dibuat, ada bug pada package
docker-compose
ubuntu. Jika kalian menggunakan WSL-ubuntu, maka kalian bisa menjalankan perintah di atas dengan menggunakan power-shell.
Kini orang lain bisa membuat docker-compose sekalipun tak memiliki source-code kita:
version: '2.0'
services:
web:
image: gofrendi/notepad:latest
ports:
- "8080:3000"
environment:
- HTTP_PORT=3000
- REDIS_PORT=6379
- REDIS_HOST=redis
depends_on:
- redis
redis:
image: redis:latest
ports:
- "6379:6379"
Kubernetes adalah container-orchestration-system. Secara sederhana, kubernetes memungkinkan kalian untuk melakukan scaling, restart service, dan deployment secara lebih mudah.
Dalam kesempatan ini kita belum akan membahas kubernetes secara terlalu detail.
Saat ini, salah satu penyedia layanan kubernetes yang cukup ramah pemula dan tidak perlu credit-card adalah okteto
Kalian bisa mencoba membuat akun di okteto, dan membuat namespace.
Usai membuat namespace, kalian bisa mendownload credential okteto-kube.config
dan menjalankan perintah berikut:
export KUBECONFIG=./okteto-kube.config
kubectl get all
kubectl apply -f redis-k8s.yaml
kubectl apply -f notepad-k8s.yaml
Mari kita bahas sedikit tentang apa yang terjadi di sini.
Kubernetes sebenarnya bisa diakses melalui HTTP API, namun biasanya para developer lebih suka menggunakan program kubectl
supaya bisa melakukan deployment dari terminal/console.
Dalam prakteknya, banyak perusahaan yang sudah memanfaatkan pipeline CI/CD (Continuous Integration/Continuous Deployment). Dengan adanya CI/CD ini, developer bisa melakukan deployment secara otomatis, bergantung pada aksi yang didefinisikan. Misalnya, seteiap kali merge ke branch master, atau setiap kali ada penambahan tag tertentu pada sebuat commit.
Pada kesempatan ini, kita tidak akan membahas CI/CD, kita hanya akan fokus pada deployment saja.
Biasanya penyedia layanan kubernetes (GCP/AWS/Azure/Octeto/Digital Ocean, dsb) menyediakan file autentikasi yang perlu kita download di komputer lokal. kubectl
mengenal file autentikasi ini berdasarkan environment variable KUBECONFIG
.
Inilah yang kita lakukan di baris pertama:
export KUBECONFIG=./okteto-kube.config
Perintah kubectl get all
biasa dipakai untuk melihat resource apa saja yang sudah ter-deploy ke cloud. Biasanya, kita juga menggunakan perintah ini untuk memastikan apakah kubectl
di komputer kita sudah terhubung ke cloud.
Perintah kubectl apply
berguna untuk men-deploy deployment/service dan artifact lain ke cloud.
Nah, mungkin muncul pertanyaan baru, apa itu deployment
, dan apa itu service
?
Sebenarnya di balik layar, kubernetes meng-orchestrate beberapa VM/komputer. VM/komputer ini kita kenal dengan sebutan node
. Saat kita men-deploy sesuatu, maka kubernetes akan meletakkannya pada node-node yang masih memiliki kapasitas.
Dalam satu node, bisa terdapat banyak pod
. Satu pod umumnya berisi satu docker-container
(bisa lebih).
Sekarang bayangkan situs berita semacam kumparan, detik, atau tirto. Menurut kalian, situs-situs tersebut lebih sering dipakai untuk membaca berita, atau menulis berita?
Tentu saja, lebih banyak dipakai untuk membaca berita. Dalam hal ini kita bisa asumsikan bahwa pod
untuk membaca data dan menampilkan ke user harus lebih banyak daripada pod
untuk menuliskan berita (yang biasanya dipakai jurnalis).
Nah, supaya pod-pod tadi bisa berkoordinasi satu sama lain, dibutuhkan semacam gateway/load-balancer yang bertugas membagi beban kerja pod. Untuk itu, pod-pod tadi perlu dibungkus dalam satu service
.
Pada contoh situs berita kita tadi, mungkin kita hanya memiliki 2 service saja, writer
dan reader
. Namun masing-masing service
membungkus beberapa pod
. Katakan saja, service writer
membungkus 10 pod, sedangkan service reader
membungkus 2 pod.
Mari kita lihat contoh deployment
dan service
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: notepad
spec:
replicas: 1 # di sini, kita hanya akan punya satu pod. Pada kenyataannya bisa dibuat auto-scale
selector:
matchLabels:
app: notepad
template:
metadata:
labels:
app: notepad
spec:
containers: # sebenarnya bisa ada banyak container, tapi umumnya satu
- image: gofrendi/notepad # nama docker image yang kita pakai
name: notepad # nama container
env: # environment variable
- name: HTTP_PORT
value: "3000"
- name: REDIS_PORT
value: "6379"
- name: REDIS_HOST
value: "redis" # ini me-refer ke service "redis"
apiVersion: v1
kind: Service
metadata:
name: notepad
labels:
app: notepad
annotations:
dev.okteto.com/auto-ingress: "true" # Ini supaya bisa diakses dari luar
spec:
type: ClusterIP
ports: # daftar port yang akan kita forward ke pod.
- name: notepad
port: 3000
targetPort: 3000
selector:
app: notepad