斜槓工程師的問題筆記

如何使用容器來協助進行開發(下)

透過Docker將環境建置時間縮短100倍

問題情境

承接上一篇文章,目前已經可以透過單個容器來達成下面的成果。

  1. 啟動一個直接運行的專案或服務。
  2. 啟動一個擁有合適環境的容器,並且掛載要開法的專案進去,免除在本地安裝環境的麻煩。

那現在遇到新的問題了,如果我同時有多個專案要執行,每個專案需要的環境也不同,那我光是啟動容器的前置作業就得花很多時間。倒不如還一開始痛苦一點將所有環境建置完成。 要解決這個問題,就該 docker-compose 上場了。

docker-compose 是何方神聖,要怎麼使用?

docker-compose是一套工具,可以讓我們用一個簡單的配置文件來定義和管理多個容器。想像它是一套寫好的使用說明書,docker會按照說明書的內容啟動所有需要的容器,而這一切只需要一行指令就可以完成:

docker-compose up

那我們開始吧!

我們一樣透過專案來實作。在上一篇文章中,我們已經學會使用 Dockerfile 來建置屬於自己的鏡像,並且在該鏡像的基礎上啟動容器。我們的最終目標是能夠透過一行指令,就將專案中的前端畫面與後端伺服器同時啟動,並且會隨著我的專案內容調整而即時更新內容。 我們先將想要見到的成果描述一次。

「我希望可以啟動兩個容器,一個叫做 backend_dev,一個叫做 frontend_dev。這兩個容器分別是夠過我的 backend 以及 frontend 資料夾下的 Dockerfile-env 建置的鏡像啟動的。由於我想要夠執行開發,因此他需要掛載各自資料夾底下的所有檔案。在容器啟動後,我希望他自動執行專案的啟動指令,並且分別將我電腦上的port號 3001 以及 3002 對應到他們開放的 3000 端口上,好讓我可以透過網路與他們互動。喔對了,幫我將 backend_dev 設定一個環境變數 RAILS_ENV 為 development。」

上面描述資訊量有點多的內容,就是我們要寫給 docker 的說明書,也就是專案底下的 docker-compose.yml 檔案。

version: '3.8'
services:
  backend:
    build: 
      context: ./backend
      dockerfile: Dockerfile-env
    container_name: backend_dev
    volumes:
      - ./backend:/backend
    ports:
      - "3001:3000"
    environment:
      - RAILS_ENV=development
    command: >
      bash -c "bundle check || bundle install && 
              rails server -b '0.0.0.0'"
  frontend:
    build: 
      context: ./frontend
      dockerfile: Dockerfile-env
    container_name: frontend_dev
    volumes:
      - ./frontend:/frontend
    ports:
      - "3002:3000"
    command: >
      bash -c "npm install &&
               npm start"  

我們一行一行來看裡面的內容,看他們如何分別與上面的指令對應,以及實際使用的方法。 首先要注意的是,docker-compose 是透過縮排來定義階層的,因此如果調整到縮排會讓腳本無法確定你想表達的內容。

  • version: '3.8':指定docker-compose的版本,現階段可先不用調整它。
  • services::定義服務,即我們要啟動的容器有哪些,我們這裡定義的是下面提到的 backend 和 frontend。
  • backend 和 frontend::這是我們定義的兩個服務,每個服務都有自己的一組配置。
  • build::指定鏡像的建置方式,包括context和dockerfile。
  • context: ./backend:設置鏡像建置的上下文路徑。
  • dockerfile: 指定用於建置鏡像的Dockerfile,這裡我們不使用上一篇文章使用的 Dockerfile,而是新的,被我們命名為 Dockerfile-env 的檔案。
  • container_name::可以指定容器啟動後的名稱。
  • volumes::定義掛載的資料夾,這裡將本地的backend和frontend資料夾掛載到容器中。
  • ports::將主機的端口映射到容器的端口,這樣我們可以在主機上訪問容器內的服務,在 backend 中,我們主機如果呼叫3001 port號就會被轉到容器 backend 的 3000。
  • environment::用來設置環境變數,這裡我們為backend容器設置了RAILS_ENV=development,假設 frontend 有設定一些環境變數的話,也可以設定在這裡,在專案中執行 process.env 就會看到我們設定的變數。
  • command::指定容器啟動後要執行的命令,這樣啟動後就會自動啟動我們的專案。

在理解之後,我們只要實際上在終端機中在 docker-compose.yml 所在的路徑打下:

docker-compose up -d

其中-d跟我們上一篇提到的一樣,是代表後台運行的意思,可以不用加。執行完後,就可以在 docker desktop 看到啟動後的容器。 為了體會他實際在執行中,我們可以在本地瀏覽器輸入localhost:3002,會看到前端的畫面。 他實際上呼叫了後端一支寫好的 api,只要在輸入框中輸入任意文字。就可以看到後端回傳的哈囉訊息。

如果不相信,還可以自己在後端專案下 backend/app/controllers/hello_controller.rb中,修改回傳的訊息,再次按下 submit 按鈕就會即時更新囉。 這樣,docker-compose會根據配置文件啟動並管理我們的兩個服務,節省了很多手動操作的時間和精力。

好耶,我會了,然後呢?

到這邊,我們只是很粗淺的學習 docker 在真正實務上的用法,沒有深挖底層的原理,但我們已經可以根據自己需求的環境建置客製化的鏡像。也可以透過 docker-compose 一次啟動多個容器。 希望可以用這些最核心的知識,減少大家在開發過程中環境建置的困擾。如果真的對容器很感興趣的話,可以在自己研究容器的內容,我後續也會再慢慢補上相關的筆記。

All rights reserved.