---
# System prepended metadata

title: 'Basic : Docker Hands-On'
tags: [TIP]

---

# Docker Material

[TOC]

## Introduction


### Application Architecture

**Arsitektur Aplikasi Modern**

Di era digital yang terus berkembang pesat ini, arsitektur aplikasi telah mengalami banyak evolusi. Gone are the days di mana kita mengembangkan aplikasi sebagai satu blok monolitik besar, yang mana segala fitur dan fungsionalitasnya dikemas dalam satu paket raksasa. Sekarang, kita beralih ke pendekatan yang lebih modular dan fleksibel, yaitu arsitektur berbasis microservices.

Microservices adalah sebuah pendekatan dalam pengembangan software di mana aplikasi dibangun sebagai kumpulan layanan yang lebih kecil, mandiri, dan bisa di-deploy secara independen. Setiap layanan ini memiliki fungsionalitas spesifik, berkomunikasi dengan layanan lainnya melalui API yang terdefinisi dengan baik, biasanya melalui HTTP/REST atau messaging queues.

**Keuntungan Arsitektur Microservices**

- **Fleksibilitas dalam Pengembangan**: Dengan memecah aplikasi menjadi layanan-layanan yang lebih kecil, tim pengembangan bisa bekerja secara independen dan paralel, meningkatkan kecepatan dan efisiensi pengembangan.
- **Skalabilitas**: Microservices memudahkan skalabilitas aplikasi karena setiap layanan bisa diskalakan secara independen, tergantung kebutuhan.
- **Kemudahan dalam Pemeliharaan**: Memperbarui atau memperbaiki bug pada sistem berbasis microservices lebih mudah dan cepat karena hanya perlu fokus pada layanan tertentu, tanpa harus mengutak-atik keseluruhan aplikasi.
- **Resiliensi**: Jika satu layanan mengalami masalah, tidak akan langsung mengganggu layanan lain atau aplikasi secara keseluruhan, membuat sistem lebih tahan terhadap error.

### Deployment Patterns

**Era Deployment Tradisional**

Zaman dulu, ngirim atau deploy aplikasi ke lingkungan produksi itu sering kali dilakukan secara manual. Prosesnya bisa panjang dan penuh dengan risiko. Mulai dari transfer file secara manual, konfigurasi server, sampai melakukan restart layanan secara langsung di server produksi. Proses ini tidak hanya memakan waktu, tapi juga rentan terhadap human error yang bisa menyebabkan downtime atau masalah serius lainnya.

Docker muncul sebagai alat yang mempermudah proses deployment aplikasi. Dengan menggunakan kontainer, Docker memastikan bahwa aplikasi akan berjalan dengan konsisten di berbagai lingkungan, dari development hingga produksi. Ini menghilangkan masalah "works on my machine" yang sering dihadapi developer.


### Virtual Machine vs Container


Dalam dunia pengembangan dan pengoperasian aplikasi, dua teknologi yang sering dibanding-bandingkan adalah Virtual Machine (VM) dan Container. Kedua teknologi ini memiliki kelebihan dan kekurangannya masing-masing, tergantung pada kebutuhan spesifik proyek atau organisasi Anda.

**Virtual Machine: Apartemen Pribadi di Dunia Komputasi**

Virtual Machine, atau VM, adalah emulasi dari sebuah komputer sistem. Setiap VM berjalan di atas fisik hardware tetapi terisolasi dari hardware tersebut dan VM lainnya, berkat layer yang disebut Hypervisor. Hypervisor bertindak seperti manajer gedung apartemen, yang memastikan setiap "apartemen" (VM) memiliki sumber daya yang dibutuhkan dan tidak mengganggu penghuni lain.

- **Kekurangan VM**:
  - **Penggunaan Sumber Daya**: Karena setiap VM membutuhkan sistem operasi lengkap, mereka cenderung menggunakan lebih banyak sumber daya.
  - **Startup Lambat**: Memulai sebuah VM bisa memakan waktu karena harus mem-boot sistem operasi sepenuhnya.

**Container: Berbagi Sumber Daya dengan Efisien**

Container, di sisi lain, membagi sistem operasi yang sama dan menjalankan aplikasi beserta dependensinya dalam paket yang terisolasi. Ini seperti tinggal di kost-kostan, di mana kamu membagi beberapa fasilitas utama dengan penghuni lain tetapi memiliki ruang pribadi kamu sendiri.

- **Kelebihan Container**:
  - **Efisiensi Sumber Daya**: Karena container berbagi kernel OS, mereka jauh lebih ringan daripada VM dan menggunakan sumber daya yang lebih sedikit.
  - **Startup Cepat**: Container dapat dimulai dalam hitungan detik, membuatnya ideal untuk skala aplikasi secara dinamis.
  - **Portabilitas**: Aplikasi dalam container dapat dengan mudah dijalankan di lingkungan apa pun tanpa perlu menyesuaikan.



*** 
## Docker Overview

**Definisi Docker**

**Mengapa Docker Itu Penting?**

Nah, sebelum kita masuk ke teknisnya, mari kita bahas dulu kenapa sih Docker ini penting banget buat kita yang berkecimpung di dunia pengembangan software. Jadi, Docker ini menyediakan solusi untuk masalah klasik yang sering ditemui developer: "Aplikasi saya jalan dengan sempurna di komputer saya, kok pas di tempat lain malah error, ya?" 

Docker datang sebagai penyelamat dengan konsep kontainerisasi. Bayangin, aplikasi kita dengan semua kebutuhan dan ketergantungannya dibungkus dengan rapi dalam sebuah kontainer. Kontainer ini bisa dijalankan di mana saja, mulai dari komputer pribadi kita, server di kantor, sampai ke server cloud yang jauh di sana. Singkatnya, Docker memastikan bahwa "It works on my machine" bisa berubah jadi "It works everywhere".


Docker telah mengubah cara kita mengembangkan, mengirimkan, dan menjalankan aplikasi dengan memberikan solusi kontainerisasi yang efisien. Untuk memahami bagaimana Docker dapat melakukan ini, penting untuk mengenal arsitektur dasar Docker dan komponen-komponen utamanya.

### Docker Architecture

Arsitektur Docker terdiri dari beberapa komponen utama yang bekerja bersama untuk memungkinkan pembuatan, penjalanan, dan distribusi kontainer.

**Docker Daemon**

Docker Daemon (`dockerd`) adalah proses background yang mengelola objek-objek Docker seperti kontainer, gambar (images), jaringan, dan volume. Daemon Docker menerima perintah dari Docker Client melalui CLI atau REST API, mengelola operasi Docker, dan berkomunikasi dengan daemon lain untuk mengelola layanan Docker.

**Docker Client**

Docker Client (`docker`) adalah cara utama pengguna berinteraksi dengan Docker. Ketika Anda menggunakan perintah seperti `docker run` atau `docker build`, Docker Client mengirimkan perintah ini ke Docker Daemon, yang kemudian menjalankannya. Client Docker dapat berkomunikasi dengan lebih dari satu daemon.

**Docker Registry**

Docker Registry menyimpan Docker images. Docker Hub adalah registry publik yang paling dikenal dan digunakan untuk berbagi gambar secara luas. Namun, pengguna juga dapat menjalankan registry pribadi mereka sendiri. Ketika Anda menggunakan perintah `docker pull` atau `docker push`, images diambil atau dikirim ke registry yang ditentukan.


**Cara Kerja Docker**

1. **Inisiasi Dockerfile**: Pengguna mendefinisikan Dockerfile yang memuat instruksi tentang cara membangun image.
2. **Build**: Docker Client memberitahu Docker Daemon untuk membangun image dari Dockerfile.
3. **Penyimpanan**: Image yang dibangun disimpan di Docker Registry untuk distribusi.
4. **Pull**: Pengguna atau sistem lain dapat menarik images dari registry untuk digunakan.
5. **Eksekusi**: Docker Daemon mengambil images dan menjalankan kontainer berdasarkan images tersebut.



### Building Dockerfile

Bayangin kamu mau bikin kue. Apa yang kamu butuh? Resep dong. Nah, dalam dunia Docker, resep untuk membuat aplikasi kamu itu namanya Dockerfile. Dockerfile ini kayak panduan step-by-step buat Docker dalam mengemas aplikasi kamu jadi satu paket lengkap yang siap dijalankan sebagai kontainer.

**Apa Itu Dockerfile?**

Dockerfile adalah sebuah file teks tanpa ekstensi yang berisi serangkaian instruksi untuk Docker tentang cara membangun sebuah Docker Image. Image yang dibangun ini nantinya bisa kamu gunakan untuk menjalankan aplikasi kamu di lingkungan apa pun.

**Komponen Utama Dockerfile**

Dockerfile terdiri dari berbagai instruksi yang dimengerti oleh Docker, seperti:

- `FROM`: Memulai dengan menentukan base image. Misal, `FROM ubuntu:18.04` berarti kamu mulai dengan image Ubuntu versi 18.04 sebagai dasar.
- `WORKDIR`: Mengatur direktori kerja di dalam image. Contoh, `WORKDIR /app` akan membuat direktori `/app` dan menjadikannya sebagai direktori kerja.
- `COPY` atau `ADD`: Menyalin file dan direktori dari komputer kamu ke dalam image. `COPY . /app` berarti menyalin semua file di direktori saat ini ke dalam `/app` di image.
- `RUN`: Menjalankan perintah saat membangun image. Ini bisa untuk menginstal paket, membuat file, atau seting lainnya. Misal, `RUN apt-get update && apt-get install -y git`.
- `CMD`: Menentukan perintah default yang dijalankan saat kontainer di-start. Contoh, `CMD ["python", "app.py"]` akan menjalankan `app.py` dengan Python.

**Contoh Dockerfile Sederhana**

Misalnya, kamu ingin membuat aplikasi web Python sederhana:

```Dockerfile
# Pilih base image
FROM python:3.8

# Set direktori kerja
WORKDIR /app

# Salin semua file ke dalam image
COPY . .

# Instal dependencies
RUN pip install -r requirements.txt

# Tentukan perintah yang dijalankan saat kontainer start
CMD ["python", "./app.py"]
```

Setelah Dockerfile kamu siap, kamu bisa membangun Docker Image dengan perintah:

```
docker build -t nama-aplikasi-kamu .
```

Tanda titik . di akhir berarti Docker akan mencari Dockerfile di direktori saat ini.

### Environment Variables

Dalam pengembangan aplikasi, sering kali kita butuh cara yang fleksibel untuk mengkonfigurasi aplikasi tergantung lingkungan tempat aplikasi itu dijalankan. Misalnya, setting database di lingkungan development bisa berbeda dengan di production. Nah, di sinilah keajaiban variabel lingkungan atau Environment Variables berperan dalam Docker.

**Apa Itu Environment Variables?**

Environment Variables adalah variabel yang dapat digunakan untuk menyetel konfigurasi yang bisa berubah-ubah dalam aplikasi tanpa harus mengubah kode. Dalam konteks Docker, environment variables bisa sangat berguna untuk menyesuaikan perilaku kontainer saat runtime, memudahkan konfigurasi aplikasi di berbagai lingkungan.

**Menggunakan Environment Variables**

Kamu bisa menentukan environment variables saat menjalankan kontainer dengan perintah `docker run` menggunakan flag `-e` atau `--env`:

```
docker run -d -e "DATABASE_URL=jdbc:mysql://localhost:3306/mydb" my_web_app
```

### Docker Images

Docker Image adalah template baca-saja yang berisi instruksi untuk membuat kontainer Docker. Images digunakan untuk menyimpan dan mengirimkan aplikasi dan lingkungan eksekusinya. Anda dapat membangun images sendiri atau menggunakan images yang dibuat oleh orang lain dan dihosting di registry.


Bayangin ini kayak blueprint atau resep dari aplikasi kita. Semua yang perlu ada di dalam kontainer, termasuk aplikasi dan lingkungannya, ada di sini.

**Mengapa Docker Image itu Penting?**

Coba bayangkan, kamu punya aplikasi yang ingin dijalankan di berbagai lingkungan, dari laptop pribadi, server di kantor, hingga cloud. Docker Image inilah yang memastikan aplikasi kamu bisa berjalan konsisten di semua tempat itu tanpa hambatan. Ia membungkus aplikasi kamu beserta semua yang dibutuhkan aplikasi itu untuk berjalan: kode aplikasi, runtime, library, variabel lingkungan, dan file konfigurasi lainnya. Semacam koper siap pakai yang tinggal dibawa ke mana saja.




### Docker Containers

Setelah kita ngomongin tentang Docker Images sebagai blueprint, sekarang kita masuk ke jantungnya Docker: Kontainer. Bayangin Docker Image itu seperti cetakan kue, sedangkan Docker Container itu kuenya sendiri. Kontainer adalah instansi nyata yang berjalan dari sebuah image, yang bisa kamu lihat, sentuh, dan tentu saja, jalankan aplikasi kamu di dalamnya.

**Apa Itu Docker Container?**

Docker Container adalah paket software ringan, portabel, dan isolasi yang memungkinkan aplikasi untuk berjalan dengan cepat dan konsisten di berbagai lingkungan komputasi. Kontainer Docker mengemas aplikasi dan semua yang dibutuhkan untuk menjalankan aplikasi tersebut, termasuk sistem operasi, kode aplikasi, runtime, sistem perpustakaan, dan file konfigurasi.

**Mengelola Kontainer**

Docker juga menyediakan berbagai perintah untuk mengelola kontainer, seperti:

* `docker ps`: Menampilkan semua kontainer yang berjalan.
* `docker stop`: Menghentikan kontainer yang sedang berjalan.
* `docker rm`: Menghapus kontainer yang sudah berhenti.
* `docker logs`: Melihat log dari kontainer yang berjalan.

### Cgroups and Namespaces

Ketika kita berbicara tentang kontainer dan bagaimana mereka merevolusi cara kita mengembangkan, mengirimkan, dan menjalankan aplikasi, dua konsep penting yang harus kita pahami adalah Cgroups (Control Groups) dan Namespaces. Kedua mekanisme ini adalah fitur inti dari kernel Linux yang memungkinkan kontainerisasi bekerja dengan efisien dan aman.

**Cgroups: Mengelola Sumber Daya**

Cgroups, atau Control Groups, adalah fitur Linux yang memungkinkan pembagian dan pembatasan sumber daya sistem (seperti CPU, memori, bandwidth I/O) untuk sekelompok proses. Dengan Cgroups, kita bisa memastikan bahwa setiap kontainer hanya menggunakan bagian sumber daya yang telah ditentukan, mencegah satu kontainer dari mengambil sumber daya lebih dari yang seharusnya dan mempengaruhi kontainer atau proses lain pada sistem.


**Namespaces: Isolasi Proses**

Namespaces adalah fitur yang menyediakan isolasi untuk sekelompok proses, sehingga proses dalam satu namespace tidak bisa melihat atau berinteraksi dengan proses di namespace lain. Ini memungkinkan kita untuk menjalankan banyak kontainer pada satu sistem host tanpa kontainer tersebut saling mengganggu. Namespaces membungkus setiap kontainer dalam "dinding" virtualnya sendiri, menjaga isolasi lingkungan operasi.

- **Jenis-Jenis Namespaces Utama**:
  - **PID Namespace**: Isolasi ID proses, memungkinkan setiap kontainer memiliki proses dengan PID 1 dan seterusnya.
  - **Network Namespace**: Isolasi stack jaringan, memungkinkan setiap kontainer memiliki tampilan jaringan sendiri, termasuk alamat IP dan routing table.
  - **Mount Namespace**: Isolasi filesystem, memungkinkan setiap kontainer memiliki filesystem root sendiri.
  - Dan lainnya, seperti User, UTS, IPC, dan Cgroup Namespace.

### Docker Network

Saat kita ngomongin kontainer dan aplikasi yang berjalan di dalamnya, nggak bisa lepas dari yang namanya jaringan atau network. Docker Network ini adalah fitur keren dari Docker yang memungkinkan kontainer-kontainer untuk berkomunikasi satu sama lain dan juga dengan dunia luar. Mirip seperti jaringan sosial tapi buat kontainer, gitu deh.

**Kenapa Docker Network itu Penting?**

Bayangin kalau setiap kontainer itu orang di suatu acara. Tanpa adanya jaringan, mereka bakal kesulitan berkomunikasi. Docker Network memastikan semua "orang" (baca: kontainer) ini bisa ngobrol dan berinteraksi dengan lancar, baik di dalam satu "ruangan" (host) maupun antar "ruangan" (host).

### Docker Volumes

Ketika kita ngobrolin tentang Docker dan kontainer-kontainer kecenya, ada satu pertanyaan yang sering muncul: "Gimana sih cara kita nyimpen data yang bisa bertahan meskipun kontainernya udah dihapus?" Nah, jawabannya ada pada Docker Volumes.

**Apa Itu Docker Volumes?**

Docker Volumes adalah mekanisme yang disediakan oleh Docker untuk menyimpan atau membagikan data di antara kontainer-kontainer atau antara host dan kontainer. Volume ini diatur oleh Docker Daemon dan terpisah dari siklus hidup kontainer itu sendiri. Artinya, data yang kamu simpan di volume bakal tetap ada meski kontainer sudah lenyap.

**Kenapa Pakai Docker Volumes?**

- **Persistensi Data**: Data penting seperti database atau file konfigurasi aplikasi perlu bertahan meski kontainer di-restart atau dihapus. Docker Volumes memastikan data tersebut aman.
- **Berbagi Data**: Kamu bisa menggunakan volume untuk berbagi data antar kontainer atau antara host dan kontainer, memudahkan proses development dan testing.
- **Efisiensi**: Dengan volume, read dan write operasi lebih cepat dibandingkan menyimpan data di layers file system kontainer.

**Cara Kerja Docker Volumes**

Saat kamu membuat volume dengan Docker, Docker Daemon akan menyimpan volume tersebut di dalam host filesystem tetapi di luar filesystem kontainer. Kamu bisa menentukan pathnya sendiri atau membiarkan Docker yang mengatur lokasinya.

# Docker Hands-On

## Pre-Requisite

**My Personal Host Spesification**

```o
            .-/+oossssoo+/-.               raflihadiana@Kamado
        `:+ssssssssssssssssss+:`           -------------------
      -+ssssssssssssssssssyyssss+-         OS: Ubuntu 22.04.2 LTS on Windows 10 x86_64
    .ossssssssssssssssssdMMMNysssso.       Kernel: 5.15.133.1-microsoft-standard-WSL2
   /ssssssssssshdmmNNmmyNMMMMhssssss/      Uptime: 12 hours, 20 mins
  +ssssssssshmydMMMMMMMNddddyssssssss+     Packages: 723 (dpkg), 6 (snap)
 /sssssssshNMMMyhhyyyyhmNMMMNhssssssss/    Shell: bash 5.1.16
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Theme: Adwaita [GTK3]
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   Icons: Adwaita [GTK3]
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   Terminal: Windows Terminal
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   CPU: AMD Ryzen 5 4600H with Radeon Graphics (12) @ 2.994GHz
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   GPU: 4e65:00:00.0 Microsoft Corporation Device 008e
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Memory: 1158MiB / 3601MiB
 /sssssssshNMMMyhhyyyyhdNMMMNhssssssss/
  +sssssssssdmydMMMMMMMMddddyssssssss+
   /ssssssssssshdmNNNNmyNMMMMhssssss/
    .ossssssssssssssssssdMMMNysssso.
      -+sssssssssssssssssyyyssss+-
        `:+ssssssssssssssssss+:`
            .-/+oossssoo+/-.
```

You need to install **Docker** based on your Host OS. Then install **gcloud client** for accessing the Google Cloud Service Provider troughout terminal.

### Docker Container

1. Install Docker Engine

- Set up Docker's apt repository 
    ```bash
    # Add Docker's official GPG key:
    sudo apt-get update
    sudo apt-get install ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc

    # Add the repository to Apt sources:
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
    ```
- Install the Docker packages
    ```bash
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    ```
- Post install to run without sudo
    ```bash
    sudo groupadd docker
    ```
    ```bash
    sudo usermod -aG docker $USER
    ```    
    ```bash
    newgrp docker
    ```
    ```bash
    docker run hello-world
    ```    
### Google Cloud CLI

2. Install Google Cloud CLI

    **Installation Link:** https://cloud.google.com/sdk/docs/install

* Download the binary by curl
    ```bash
    curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-463.0.0-linux-x86_64.tar.gz
    ```
* Extract the binary to be output `/google-cloud-sdk`
    ```bash
    tar -xf google-cloud-cli-463.0.0-linux-x86_64.tar.gz
    ```

* Execute the script
    ```bash
    ./google-cloud-sdk/install.sh
    ```
* Start the project inisialization
    ```bash
    ./google-cloud-sdk/bin/gcloud init
    ```
    
    <details> <summary>Expected Output</summary>
           
    ```output=
    Your browser has been opened to visit:
    https://accounts.google.com/o/oauth2/...
    ```
    ```output=
    Pick cloud project to use:
     [1] boot-camp-gdsc
    ```
    
    ```output=
    Your Google Cloud SDK is configured and ready to use!
    ```
    </details>
    
    
## Working with Docker

In this demo we'll cover two application example to use.

### 1. [Supabase Backend Application ](https://github.com/hiskandaryps/express-example-app)

> This APIs was developed using:
> - Node.Js v18.13.0
> - Express v4.18.2
> - Supabase 

**How to run this APIs manually**
- Clone this project to your GCP project’s repository
    ```bash
    git clone https://github.com/hiskandaryps/express-example-app
    ``` 
- Install all the required depedencies
    ```bash
    npm install
    ``` 
- Provide the required data for the `.env` file, that consist of : `SUPABASE_URL`, `SUPABASE_KEY`, `PORT`

    ```bash
    cp .env.example .env
    ```
    
    Here's the value for the `.env`
    ```conf=
    PORT=3000
    VERSION=v1

    SUPABASE_URL=https://bwstuipaunilvlgodbzq.supabase.co
    SUPABASE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJ3c3R1aXBhdW5pbHZsZ29kYnpxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDY3OTg0MjEsImV4cCI6MjAyMjM3NDQyMX0.dDRT8SkJho2JYSuK0DGbW1drdybzZp7Nf88SU3kK96A
    ```
- Run the back-end application
    ```bash
    npm start
    ``` 

**How to run Dockerize the App**

1. Create Dockerfile to build the image

    - Initialize the file
        ```
        cd express-example-app
        touch Dockerfile
        ```
    - Configure the Dockerfile align with API Spec
        ```o=
        FROM node:18-alpine
        WORKDIR /opt/app
        COPY . .
        RUN npm install
        EXPOSE 3000
        CMD [ "npm", "start"]
        ```        
        
    - Create a file called .dockerignore

        ```o
        .idea
        .gitignore
        node_modules/
        ```
    - Build the image
    
        ```
        docker build -t gdsc/test .
        ```
        
2. Run the custom application image


    - Run the image with customized port to expose
        ```
        docker run -p 8080:4000 -d gdsc/test
        ```
    - Validate if the containner is running with `docker ps`
        ```o
        CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                       NAMES
        20d00bcf7d9d   gdsc/test      "docker-entrypoint.s…"   16 minutes ago   Up 16 minutes   0.0.0.0:8080->3000/tcp, :::8080->3000/tcp   laughing_margulis
        ```        
    - Validate by Postman testing
        ![image](https://hackmd.io/_uploads/Bk2yCi7iT.png)

### 2. [PostgreSQL Backend Application](https://github.com/raflihadiana/nodejs-example-app)

> This APIs was developed using:
> - Node.Js v18.13.0
> - Express v4.18.2
> - PostgreSQL

**How to run this APIs manually**
- Clone this project to your GCP project’s repository
    ```bash
    git clone https://github.com/raflihadiana/nodejs-example-app
    cd nodejs-example-app/src
    ``` 
- Install all the required depedencies
    ```bash
    npm install
    ``` 
- Start a PostgreSQL databases and create the tables

    ```bash
    docker run -it -e "POSTGRES_HOST_AUTH_METHOD=trust" -p 5432:5432 postgres
    npm run migrate
    ```
    
- Run the back-end application
    ```bash
    npm start
    ``` 

**How to run Dockerize the App**

1. Create Dockerfile to build the image

    - Initialize the file
        ```
        cd nodejs-example-app
        touch Dockerfile
        ```
    - Configure the Dockerfile align with API Spec
        ```o=
        FROM node:18.16.0-alpine3.17
        RUN mkdir -p /opt/app
        WORKDIR /opt/app
        COPY src/package.json src/package-lock.json .
        RUN npm install
        COPY src/ .
        EXPOSE 3000
        CMD [ "npm", "start"]
        ```        

    - Create a file called .dockerignore

        ```o
        .git
        .gitignore
        src/node_modules/
        ```

    - Build the image
    
        ```
        docker build -t addressbook .
        ```
        
2. Run the custom application image


    - Run the image with customized port to expose
        ```
        docker run -it -p 3000:3000 addressbook
        ```
    - Run the image with `docker compose` for network & DB Configuration

        ```o
        # docker-compose.yml

        version: "3.9"
        services:
          postgres:
            image: postgres:latest
            environment:
              POSTGRES_USER: postgres
              POSTGRES_PASSWORD: postgres
            ports: 
              - '5432:5432'
            volumes:
              - addressbook-db:/var/lib/postgresql/data

          addressbook:
            build:
              context: .
            environment:
              DB_SCHEMA: postgres
              DB_USER: postgres
              DB_PASSWORD: postgres
              DB_HOST: postgres
            depends_on:
              - postgres
            ports:
              - '3000:3000'

        volumes:
          addressbook-db:
        ```
        ```o
        docker compose run addressbook npm run migrate
        ```    
        ```o
        docker compose up --build
        ``` 
    - Validate by Postman testing
        ![image](https://hackmd.io/_uploads/S1_PJpSjp.png)

    - If you want to stop the container

        ```o
        docker compose down
        ``` 


## Push to Registries

### Docker Hub Registry

1. Push the Image to Dockerhub

    - Login in the terminal to your Docker Account
        ```
        docker login
        ```
    - Change the Docker Image tag align with Dockerhub Repository
        ```
        docker tag gdsc/test raflihadiana/gdsc:test
        ```
    - Push to the Default Registry Dockerhub
        ```
        docker push raflihadiana/gdsc:test
        ```
        <details><summary>Expected Output</summary>
    
        ```o
        The push refers to repository [docker.io/raflihadiana/gdsc]
        def9ba6d524e: Pushed
        295b4b0b0a9e: Pushed
        a75f8c5b7682: Pushed
        b325b33b9813: Pushed
        4a0d315ad53e: Pushed
        29e213bad130: Pushed
        d4fc045c9e3a: Pushed
        test: digest: sha256:1d9dd48767e5d9351cbbbae9236cda8c57e87586a7b1569e339c0a6db8e30b6f size: 1786
        ```
    
        ![image](https://hackmd.io/_uploads/ry7lGnmjp.png)

        </details>

### GCP Artifact Repository 

2. Push the Image to GCP Artifact Repository 

    - Go to your Google Cloud Console then Create a repository for your image
        ![Test](https://hackmd.io/_uploads/HkQudnXop.png)
    
    - Copy this path as the configured location server
        ![testt](https://hackmd.io/_uploads/rkY4ihXsa.png)
       
    
        Mine is `asia-southeast2-docker.pkg.dev/boot-camp-gdsc/gdsc-cc2`
        
    - Authenticate yourself in CLI for Registry Access
        ```
        gcloud auth asia-southeast2-docker.pkg.dev/boot-camp-gdsc/gdsc-cc2
        ```
    - Change your local image tag to your image domain path
        
        ```
        docker tag gdsc/test asia-southeast2-docker.pkg.dev/boot-camp-gdsc/gdsc-cc2:test
        ```
    - Push your image to configured location repository path
        
        ```
        docker push asia-southeast2-docker.pkg.dev/boot-camp-gdsc/gdsc-cc2:test
        ```
        
        <details><summary>Expected Output</summary>
    
        ```o
        The push refers to repository [asia-southeast2-docker.pkg.dev/boot-camp-gdsc/gdsc-cc2/version]
        def9ba6d524e: Pushed
        295b4b0b0a9e: Pushed
        a75f8c5b7682: Pushed
        b325b33b9813: Pushed
        4a0d315ad53e: Pushed
        29e213bad130: Pushed
        d4fc045c9e3a: Pushed
        test: digest: sha256:1d9dd48767e5d9351cbbbae9236cda8c57e87586a7b1569e339c0a6db8e30b6f size: 1786
        ```

        </details>
        
    - Validate the pushed image in your Artifact Registry Console
        ![image](https://hackmd.io/_uploads/rJ6yhnQi6.png)



## Troubleshoot

1. **Docker Credentials Error**
    
    Error Statement:
    
    ![image](https://hackmd.io/_uploads/SJwiNjXsT.png)
    ```
    Error saving credentials: error storing credentials – err: exec: “docker-credential-desktop”: executable file not found in $PATH, out:
    ```
    
    <details> <summary>Solution</summary>
           
    There are several ways to resolve this issue. One easiest way would be making any of the following changes in `~/.docker/config.json` file.

    - Replace “credsStore” by “credStore” as many people suggest in other blogs.
    - Replace “credsStore” by any value.
    - Remove “credsStore” property from the config.
    
    For more detail solution you can check it [here](https://sparkbyexamples.com/docker/docker-error-storing-credentials-err-exec-docker-credential-desktop-executable-file-not-found-in-path-out/).
    </details>
    