[Docker] docker compose + Dockerfile 綜合應用

PC Chen
程式乾貨
Published in
6 min readJul 17, 2022

有鑒於工作上接觸到各種啟動 container 的特殊需求,我慢慢地從單一Dockerfie :建立 image 並啟動 container,變成進階版的 docker compose: 一次建立多個 images 並啟動相互關聯的 container,讓容器間彼此互通有無、共同串聯起來形成服務~
(對docker, container還不熟的建議 點我看這篇

這讓我有個大膽的想法😈,既然 Dockerfile 可以建立 image,那我在 docker compose 的時候能不能用這個建立好的 image 來啟動 container?

答案是:YES!

玩到現在驚覺,當初碰到 docker 就跟小傑與奇犽一樣,剛接觸到念能力一樣,覺得這也不過如此。
事實證明我 too young too simple 了,容器的妙用實在是博大精深,這也是為何現在軟體界都是這種微服務的模式。
就讓西索來教我們登大人吧(容器的水可是很深的喔😵‍💫)

容器的水可是很深的😈

先來講講使用場景吧

舉個簡單的例子:假設我需要一個可以運行 python 的環境、還有一個 redis server 的環境,我要執行 python script 在 redis 上做一些資料儲存與運算。我一開始可能會這樣子啟動、seperate-compose.yml會這樣寫:

兩個容器,彼此用網路連接
seperate-compose.yml

用指令 docker compose -f seperate-compose.yml up -d來啟動之後,就可以啟動兩個 container(py-env, redis)

成功啟動 py-env, redis 兩個容器

接著進去剛啟動的 py-env 容器,bash 啟動 python環境連看看 redis,成功在redis這個container中塞入一筆資料(Key: “test”, Value: 1)。來敲指令吧~

$ docker exec -it py-env sh #先進入 py-env 並啟動 shell
# python3 #在容器裡運行python
>>> import redis #載入redis套件
>>> r = redis.Redis(host='redis', port=6379) #連進容器的redis
>>> r.set("test", 1) #塞一筆資料看看
True #代表塞入成功!
>>> exit() #退出python
# exit #退出container

接著來 redis 這個容器看一下資料情形吧~一樣敲指令~

$ docker exec -it redis sh
# redis-cli #進入redis client端
127.0.0.1:6379> get test #獲取剛剛的key值: test
"1" # 成功返回Value
127.0.0.1:6379>
分別進入 py-env, redis 這兩個容器測試資料

BUT…我想要換個做法

這樣我每次都要啟動 py-env, redis這兩個 container 互連才能夠把服務建立起來,我能不能一次到位?只啟動一個 container 就好呢?

i.e 我想要一個 container 同時擁有 python 與 redis 的服務!

一個容器兩種享受,我全都要 🤤

眼尖的讀者可發現在前面的seperate-compose.yml ,其實兩個容器的基底image分別是 python:3.9-slimredis:6-alpine3.15 ,那要在哪裡找一個基底image、是在 build 的時候同時擁有 python+redis的環境呢?

答案就在一開始,我要自己寫一個 Dockerfile 定義好 image。接著再利用 docker compose 把這個強大的 image 啟動成 container💪

先來看看 Dockerfile吧

首先,用下面這個 Dockerfile 建立起 image,簡單敘述一下裡頭做了些什麼事情:

Dockerfile

再來就是 docker compose 的 yml 檔

重頭戲來撰寫 combine-compose.yml

combine-compose.yml

接著執行 docker compose -f combine-compose.yml up -d 來啟動 container

只剩下一個 redis 容器,還擁有 python 環境喔 🥳

接下來進去 redis 這個 container 玩玩看吧

  1. 進入容器並啟動shell
    $ docker exec -it redis sh
  2. 啟動python並嘗試在redis中塞入一筆資料,再退出python
    /data# python3
    >>> import redis
    >>> r = redis.Redis(host='redis', port=6379)
    >>> r.set("test_comb", 1)
    True
    >>> exit()
  3. 去redis-server看看資料有沒有成功塞入
    /data# redis-cli
    127.0.0.1:6379> get test_comb
    "1"
你有發現嗎?所有動作都在 redis 這個容器裡完成

如此一氣呵成~都在同一個容器內完成🥴

總結

核心概念就是先建立 image,才能啟動 container。建立 image 的方式可以用現成的 docker pull、或是自己寫 Dockerfile 建立; 啟動 container 的方式可以用一般 docker run 指令、或是本文中 docker compose 的方式一次啟動多個。只要掌握住了,萬變不離其宗😎

以上程式碼都放在 github 上了,有興趣的讀者可以抓下來玩玩看

延伸閱讀

以現今的趨勢,其實還是以啟動多容器來架構整個服務比較常見。我會有這種搞怪的做法,單純是因為我想要在 redis 啟動時可以 config 做一些特殊設定,例如:在每天的半夜12點reset鍵值、或是每隔1小時新增一個鍵值。

這些需求沒有辦法用 redis 本身提供的指令做到,所以我只能另外啟動一個 python 環境運行 python script,來做 redis config 特殊設定。

當然可能還是有redis本身支援的方式,就有勞各位大神如果有更好的做法,還請不吝告知小弟,大家互相學習增長:)

--

--

PC Chen
程式乾貨

喜歡接觸與動手實作各種軟體技術的後端數據工程師 A data- backend engineer who is enthusiastic in learning and implementing any techniques in software engineering.