Docker 基本教學 - 從無到有 Docker-Beginners-Guide
教你用 Docker 建立 Django + PostgreSQL 📝
- Youtube Tutorial PART 1 - Docker 基本教學 - 從無到有 Docker-Beginners-Guide
- 目錄 - Youtube Tutorial PART 2 - 用 Docker 實戰 Django 以及 Postgre
- 目錄 - Youtube Tutorial PART 3 - Docker 基本教學 - 透過 portainer 管理 Docker
- 目錄 - Youtube Tutorial PART 4 - Docker push image to Docker Hub 教學
其他說明
- Youtube Tutorial - Ubuntu(Linux) 如何安裝 docker
- 目錄 - Youtube Tutorial - docker-compose networks 說明
- 目錄 - Youtube Tutorial - Docker container 內如何連接到本機 localhost 服務
- 目錄 - Youtube Tutorial - docker-compose up/down 和 restart 的差異
- 目錄 - Youtube Tutorial - Linux 教學 - 開機自動啟動 docker / compose
- 目錄 - Youtube Tutorial - Docker 基本教學 - 在 docker compose 中善用 Environment variables
- 目錄 - Youtube Tutorial - 如何清除 Docker container log
- 目錄 - Youtube Tutorial - Docker 中的 JSON File logging driver(container log)
- 目錄 - Youtube Tutorial - docker 教學 Health Check
- 目錄 - 介紹 docker-compose-override
- 目錄 - 介紹 docker-compose-profiles
- 目錄 - 介紹 yaml anchors
- 目錄 - 介紹 cadvisor - 可監控 docker 容器
Containers as a Service ( CaaS ) - 容器如同服務
算是近幾年才開始紅的技術,蠻多公司都有使用 Docker,而且真的很方便,值得大家去了解一下 😄
如果你有環境上不統一的問題? 請用 Docker 😄
如果你有每次建立環境都快抓狂的問題? 請用 Docker 😊
如果你想要高效率、輕量、秒開的環境,請用 Docker 😊
如果你不想搞死自己,請用 Docker 😄
如果你想潮到出水,請一定要用 Docker 😆
Docker 是一個開源專案,出現於 2013 年初,最初是 Dotcloud 公司內部的 Side-Project。
它基於 Google 公司推出的 Go 語言實作。( Dotcloud 公司後來改名為 Docker )
技術原理我們這邊就不提了,簡單提一下他的好處。
我們先來看看官網的說明
Comparing Containers and Virtual Machines ( 傳統的虛擬化 )
從這張圖可以看出 Containers 並沒有 OS ,容量自然就小,而且啟動速度神快
詳細可參考 https://www.docker.com/what-container
Virtual Machines 是什麼?
類似 https://www.virtualbox.org/,我們可能用它裝裝看其他作業系統,例如說
我是 MAC,但我想玩 Windows,我就會在 MAC 中裝 VM 並且灌 Windows 系統。
一個表格了解 Docker 有多棒 👍
Feauture | Containers | Virtual Machines ( 傳統的虛擬化 ) |
---|---|---|
啟動 | 秒開 | 最快也要分鐘 |
容量 | MB | GB |
效能 | 快 | 慢 |
支援數量 | 非常多 Containers | 10多個就很了不起了 |
複製相同環境 | 快 | 超慢 |
不理解:question::question::question:
我們來看一張圖,包準你懂
圖的來源 https://blog.jayway.com/2015/03/21/a-not-very-short-introduction-to-docker/
潮~ 不解釋 😆
比起像是 https://www.virtualbox.org/,Docker 的利用率更高,我們可以設定更多
的 Containers ,而且啟動速度飛快!!:flushed:
相信大家都有每次搞電腦的環境都搞的很煩的經驗 😠
假設今天公司來了個新同事,就又要幫他建立一次環境 😑
不然就是,我的電腦 run 起來正常阿~ 你的怎麼不行,是不是 xxx 版本的關係阿 😂
相信大家多多少少都遇過上面這些事情,我們可以透過 Docker 來解決這些問題,
保持大家環境一致,而且要建立的時候也很快 😄
對於 DevOps 來說,最希望的就是可以設定一次,將來在其他地方都可以快速建立環境且正常執行。
建議大家先了解一下 Docker 中的幾個名詞,分別為
Image
映像檔,可以把它想成是以前我們在玩 VM 的 Guest OS( 安裝在虛擬機上的作業系統 )。
Image 是唯讀( R\O )
Container
容器,利用映像檔( Image )所創造出來的,一個 Image 可以創造出多個不同的 Container,
Container 也可以被啟動、開始、停止、刪除,並且互相分離。
Container 在啟動的時候會建立一層在最外(上)層並且是讀寫模式( R\W )。
這張圖解釋了 Image 是唯讀( R\O )以及 Container 是讀寫模式( R\W ) 的關係
更多關係可參考 https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#images-and-layers
Registry
可以把它想成類似 GitHub,裡面存放了非常多的 Image ,可在 Docker Hub 中查看。
更詳細的我這邊就不再解釋惹,留給大家做作功課:stuck_out_tongue:
Windows
請先到 Docker 官網
https://www.docker.com/docker-windows
下載 stable 版本
接下來就是無腦安裝,安裝完後他會叫你登出電腦,點下去後就會幫你登出電腦
接著如果你的電腦沒有啟用 Hyper-V ,他會叫你重啟電腦 (一樣,點下去就對惹)
( 更多可 Hyper-V 介紹請參考 https://docs.microsoft.com/zh-tw/virtualization/hyper-v-on-windows/about/ )
重新開機後,你就會發現可愛的 Docker 在右下角蹦出來惹
我們可以再用 cmd 確認一下是否有成功安裝
docker --version
docker-compose --version
記得再設定一個東西 Shared Drives
裝完了之後,建議大家再多裝一個 Kitematic,他是 GUI 介面的,方便你使用 Docker,
( 後面會再介紹一個更贊的 GUI 介面 portainer 😁 )
我知道打指令很潮,但還是建議裝一下。
直接對著你的 Docker 圖示右鍵,就可以看到 Kitematic
下載回來直接解壓縮雙點擊即可使用
MAC
MAC 我本身也有,但因為更早之前就裝了,步驟就沒記錄了,基本上大同小異
https://www.docker.com/docker-mac
Linux
Youtube Tutorial-Ubuntu(Linux) 如何安裝 docker
這裡使用 Ubuntu 當作範例,
雖然在 ubuntu 中有 snap
可以非常快速的安裝 docker,
但在這邊我們不使用 snap
的方法安裝:smile:
請參考官方文件步驟安裝,
Get Docker Engine - Community for Ubuntu
Get Docker Engine - Community for Ubuntu
安裝後步驟 (optional:exclamation:), 但建議參考一下
Post-installation steps for Linux
docker-compose 的安裝
系統資源分配問題,
假如你是使用 windows 或是 mac 的 docker,
你會有一個界面可以設定你要分多少的 cpu 以及 ram 給你的 docker,
通常會在 Preferences -> Advanced, 有 GUI 界面,
但如果是使用 linux, 就不會有這個界面, 因為在 Linux 中,
會自動依照系統的資源進行分配.
接著介紹一些 Docker 的指令,
Docker 的指令真的很多,這裡就介紹我比較常用的或是實用的指令
查看目前 images
docker images
建立 image
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
詳細的參數可參考 https://docs.docker.com/engine/reference/commandline/create/
範例 ( 建立一個名稱為 busybox 的 image )
docker create -it --name busybox busybox
刪除 Image
docker rmi [OPTIONS] IMAGE [IMAGE...]
查看目前運行的 container
docker ps
查看目前全部的 container( 包含停止狀態的 container )
docker ps -a
新建並啟動 Container
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
舉個例子
docker run -d -p 80:80 --name my_image nginx
-d
代表在 Detached( 背景 )執行,如不加 -d
,預設會 foreground ( 前景 ) 執行
-p
代表將本機的 80 port 的所有流量轉發到 container 中的 80 port
--name
設定 container 的名稱
在舉一個例子
docker run -it --rm busybox
--rm
代表當 exit container 時,會自動移除 container。 ( incompatible with -d )
更詳細的可參考 https://docs.docker.com/engine/reference/run/
啟動 Container
docker start [OPTIONS] CONTAINER [CONTAINER...]
如果想讓他在前景跑順便觀看輸出 , 可以使用以下指令
docker start -a [OPTIONS] CONTAINER [CONTAINER...]
--attach
或 -a
代表 Attach STDOUT/STDERR and forward signals.
更詳細的可參考 https://docs.docker.com/engine/reference/commandline/start/
( container ID 寫幾個就可以了,和 Git 的概念是一樣的 ,
不了解 Git 可以參考 Git-Tutorials GIT基本使用教學 )
停止 Container
docker stop [OPTIONS] CONTAINER [CONTAINER...]
重新啟動 Container
docker restart [OPTIONS] CONTAINER [CONTAINER...]
删除 Container
docker rm [OPTIONS] CONTAINER [CONTAINER...]
--volumes , -v
加上這個參數,會移除掉連接到這個 container 的 volume。
可參考 https://docs.docker.com/engine/reference/commandline/rm/
進入 Container
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it <Container ID> bash
使用 root 使用者進入
docker exec -u 0 -it <Container ID> bash
docker exec -u root -it <Container ID> bash
打指令比較潮,或是說你也可以透過 Kitematic 進入。
當我們進入了 Container 之後,有時候想看一下裡面 Linux 的版本,
這時候可以使用以下指令查看
cat /etc/os-release
查看 Container 詳細資料
docker inspect [OPTIONS] NAME|ID [NAME|ID...]
查看 log
docker logs [OPTIONS] CONTAINER
--follow
, -f
, Follow log output
更詳細的可參考 https://docs.docker.com/engine/reference/commandline/logs/
從最後 100 行開始追蹤,
docker logs -f --tail 100 CONTAINER
或是
docker logs -f -n 100 CONTAINER
可以透過 --since
從指定時間到現在的 log,
例如,
docker logs --since 2023-04-13T09:20:00 <container_id>
從 10 分鐘前到現在的 log
docker logs --since 10m CONTAINER
1 小時前到現在的 log
docker logs --since 1h CONTAINER
如果想指定時間, 查看特定區間的 logs,
可以先使用 -t
找出 docker 的時間格式,
docker logs -t CONTAINER
接著就可以使用 --since
或 --until
指定時間段,
一定要用 docker 的時間格式, 不然會無法生效.
這邊來個組合, 找出 8:10 ~ 8:30 的全部 log
docker logs --since 2023-12-10T8:10:00.346748975Z --until 2023-12-10T8:30:00.346748975Z CONTAINER
也可以把 log 寫進去檔案中,
docker logs CONTAINER >> access.log
如果上述指令沒有生效, 請修改成以下
docker logs CONTAINER >& access.log
也可以先過濾 log 再寫進檔案中,
docker logs CONTAINER | grep "29/Mar/2022" >> access_tmp.log
如果上述指令沒有生效, 請修改成以下
docker logs CONTAINER 2>&1 | grep "29/Mar/2022" >& access_tmp.log
顯示容器資源 ( CPU , I/O ...... )
docker stats [OPTIONS] [CONTAINER...]
也可以加上 --no-stream
docker stats --no-stream
--no-stream
Disable streaming stats and only pull the first result.
注意:exclamation::exclamation:這邊得到的 memory usage 會比實際上的還要小,
因為這邊的值是再減去 cache usage memory.
相關 issues 可參考 moby/moby#32253
On Linux, the Docker CLI reports memory usage by subtracting cache usage from the total memory usage.
詳細說明可參考 https://docs.docker.com/engine/reference/commandline/stats/
也可參考 https://docs.docker.com/config/containers/runmetrics/
查看 container 中正在執行的 processes
docker top CONTAINER
停止指定的 CONTAINER 中全部的 processes
docker pause CONTAINER [CONTAINER...]
執行 docker pause
之後,可以試這用 docker ps
查看,會發現
還是有在執行,這裡拿 docker stop
比較一下,差異如下。
docker stop
: process 級別。
docker pause
: container 級別。
恢復指定暫停的 CONTAINER 中全部的 processes
docker unpause CONTAINER [CONTAINER...]
docker tag
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
範例
docker tag 0e5574283393 twtrubiks/nginx:version1.0
更多可參考 https://docs.docker.com/engine/reference/commandline/tag/
儲存 (備份) image 成 tar 檔案
[OPTIONS] IMAGE [IMAGE...]
範例
docker save busybox > busybox.tar
或
docker save --output busybox.tar busybox
或 ( 也可以一次備份多個 )
docker save -o images.tar postgres:9.6 busybox
更多可參考 https://docs.docker.com/engine/reference/commandline/save/
載入 image
docker load [OPTIONS]
範例
docker load < busybox.tar
或
docker load -i busybox.tar
更多可參考 https://docs.docker.com/engine/reference/commandline/load/
顯示 image 的 history,查詢 image 的每一層
docker history [OPTIONS] IMAGE
在 docker 中,一層一層的概念很重要。
更多可參考 https://docs.docker.com/engine/reference/commandline/history/
剛剛有教大家如何儲存 (備份) images, 載入 images,
還有另外一種是 export 和 import containers,
docker export container 請參考 https://docs.docker.com/engine/reference/commandline/export/。
docker import container 請參考 https://docs.docker.com/engine/reference/commandline/import/。
其他指令
刪除所有 dangling images
docker image prune
移除全部 unused images (不只 dangling images)
docker image prune -a
更多資訊可參考 image_prune
停止所有正在運行的 Container
docker container stop $(docker ps -q)
更多資訊可參考 container_stop
移除全部停止的 containers
docker container prune
更多資訊可參考 container_prune
教學說明請點選 entrypoint-tutorial
接下來要介紹 Volume,Volume 是 Docker 最推薦存放 persisting data( 數據 )的機制,
使用 Volume 有下列優點
- Volumes are easier to back up or migrate than bind mounts.
- You can manage volumes using Docker CLI commands or the Docker API.
- Volumes work on both Linux and Windows containers.
- Volumes can be more safely shared among multiple containers.
- Volume drivers allow you to store volumes on remote hosts or cloud providers, to encrypt the contents of volumes, or to add other functionality.
- A new volume's contents can be pre-populated by a container.
在 container 的可寫層中,使用 volume 是一個比較好的選擇,因為他不會增加 container 的容量,
volume 的內容存在於 container 之外。
也可參考下圖
更詳細的可參考 https://docs.docker.com/engine/admin/volumes/volumes/
查看目前的 volume
docker volume ls [OPTIONS]
創造一個 volume
docker volume create [OPTIONS] [VOLUME]
刪除一個 volume
docker volume rm [OPTIONS] VOLUME [VOLUME...]
查看 volume 詳細資料
docker volume inspect [OPTIONS] VOLUME [VOLUME...]
移除全部未使用的 volume
docker volume prune [OPTIONS]
也可以建立 readonly 的 volumes (容器內 readonly)
docker-compose.yml
方法如下,
version: '3.5'
services:
nginx:
image: nginx
ports:
- "80:80"
volumes:
- "nfs-data:/data:ro,z"
volumes:
nfs-data:
如果要可讀寫, 就設定 rw
.
volumes 在容器內的確不能寫 (只能讀)
使用以下的指令查看 Mounts, 觀察它的 Mode
docker inspect <container ID>
也可以建立 NFS volumes,
docker-compose.yml
方法如下,
version: '3.5'
services:
nginx:
image: nginx
ports:
- "80:80"
volumes:
- "nfs-data:/data"
volumes:
nfs-data:
driver: local
driver_opts:
type: nfs
o: nfsvers=4,addr=ip,rw
device: ":/path/to/dir"
可以用以下指令查看設定
docker volume ls
docker inspect <volume name>
NFS 相關文章可參考 linux-nfs-server - 如何在 ubuntu 啟用 NFS Server
建議大家花點時間研究 docker 中的 network,會蠻有幫助的 😃
查看目前 docker 的網路清單
docker network ls [OPTIONS]
詳細可參考 https://docs.docker.com/engine/userguide/networking/
docker 中的網路主要有三種 Bridge、Host、None,預設皆為 Bridge 模式。
指定 network 範例 ( 指定使用 host
網路 )
docker run -it --name busybox --rm --network=host busybox
建立 network
docker network create [OPTIONS] NETWORK
移除 network
docker network rm NETWORK [NETWORK...]
移除全部未使用的 network
docker network prune [OPTIONS]
查看 network 詳細資料
docker network inspect [OPTIONS] NETWORK [NETWORK...]
將 container 連接 network
docker network connect [OPTIONS] NETWORK CONTAINER
更多詳細資料可參考 https://docs.docker.com/engine/reference/commandline/network_connect/
Disconnect container network
docker network disconnect [OPTIONS] NETWORK CONTAINER
更多詳細資料可參考 https://docs.docker.com/engine/reference/commandline/network_disconnect/
這個方法是官方推薦的 👍
透過內建的 DNS 伺服器,可以直接使用容器名稱,就可解析出 IP,不需要再使用 IP 讓容器互相
溝通,我們只需要知道容器的名稱就可以連接到容器。
更多詳細資料可參考 https://docs.docker.com/engine/userguide/networking/#user-defined-networks
再來要介紹 docker-compose,可參考官網 https://docs.docker.com/compose/
Compose 是定義和執行多 Container 管理的工具,不懂我在說什麼:question::question::question:
試著想想看,通常一個 Web 都還會有 DB,甚至可能還有 Redis 或 Celery,
所以說我們需要有 Compose 來管理這些,透過 docker-compose.yml
( YML 格式 ) 文件。
docker-compose.yml
的寫法可參考 https://docs.docker.com/compose/compose-file/
也可以直接參考官網範例 https://docs.docker.com/compose/compose-file/#compose-file-structure-and-examples
Compose 的 Command-line 很多和 Docker 都是類似的,
可參考 https://docs.docker.com/glossary/?term=compose
查看目前 Container
docker-compose ps
加上 -q
的話,只顯示 id
docker-compose ps -q
啟動 Service 的 Container
docker-compose start [SERVICE...]
停止 Service 的 Container ( 不會刪除 Container )
docker-compose stop [options] [SERVICE...]
重啟 Service 的 Container
docker-compose restart [options] [SERVICE...]
Builds, (re)creates, starts, and attaches to containers for a service
docker-compose up [options] [--scale SERVICE=NUM...] [SERVICE...]
加個 -d
,會在背景啟動,一般建議正式環境下使用。
docker-compose up -d
然後如果你有很多個 docker-compose.yml
docker-compose-dev.yml
,
你可以透過 -f
決定你要執行哪一個, 範例如下,
docker-compose -f ./docker-compose-dev.yml up -d
-f
--file FILE
Specify an alternate compose file
(default: docker-compose.yml
)
up
這個功能很強大,建議可以參考 https://docs.docker.com/compose/reference/up/
如果你希望每次都重新 build image,可以加上
--build
( Build images before starting containers. )
docker-compose up -d --build
docker-compose down
docker-compose down [options]
down
這個功能也建議可以參考 https://docs.docker.com/compose/reference/down/
舉個例子
docker-compose down -v
加個 -v
就會順便幫你把 volume 移除( 移除你在 docker-compose.yml
裡面設定的 volume )
在指定的 Service 中執行一個指令
docker-compose run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
[ARGS...]
舉個例子
docker-compose run web bash
在 web Service 中執行 bash
指令
可參考 https://docs.docker.com/compose/reference/run/
觀看 Service logs
docker-compose logs [options] [SERVICE...]
檢查 docker-compose.yml
格式是否正確
docker-compose config
如下指令,和 docker exec
一樣
docker-compose exec
範例 ( 進入 web 這個 service 的 bash )
docker-compose exec web bash
顯示被使用到的 container 中的 images 清單
docker-compose images
移除 service containers
docker-compose rm
Pushes images 到 docker hub
docker-compose push
目前這個指令其實我也搞不太懂,可參考 docker/compose#4283
官網也解釋的沒有很清楚 https://docs.docker.com/compose/reference/push/
先來談 docker-compose up/down
,
假如今天你修改了 docker-compose.yml
又或是更新了 image,
當你要重建 docker , 有幾種方法,
方法一.
先停止 container, 執行 docker-compose down
再執行 docker-compose up
.
方法二.
不需要停止 container, 直接執行 docker-compose up -d
.
(他會自動幫你重建, 很方便, 不需要多一步先關閉 container )
結論, 只要你的 docker-compose.yml
有任何變動, 一定要執行 docker-compose up
才會生效.
再來談 docker-compose restart
,
請看官方文件 docker-compose restart, 如果你對 docker-compose.yml
修改, 然後使用這個指令, 是不會生效的,
但是, 如果你是改 code (可能是 python code), 那這個指令是有效的.
這邊多補充 docker-compose networks 的觀念,因為剛好最近有用到:smile:
version: '3.5'
services:
db:
container_name: 'postgres'
image: postgres
environment:
POSTGRES_PASSWORD: password123
ports:
- "5432:5432"
# (HOST:CONTAINER)
volumes:
- pgdata:/var/lib/postgresql/data/
networks:
- proxy
web:
build: ./api
command: python manage.py runserver 0.0.0.0:8000
restart: always
volumes:
- api_data:/docker_api
# (HOST:CONTAINER)
ports:
- "8000:8000"
# (HOST:CONTAINER)
depends_on:
- db
networks:
- proxy
volumes:
api_data:
pgdata:
networks:
proxy:
# external:
name: my_network
先把 version 改成 3.5,因為這版本才開始有 networks name 的概念,在
db 以及 web 中都加了 networks ( 自己定義的 ),定義的地方在最後面,
proxy 是名稱 ( 類似 volumes 的概念 ),external
option 的意思代表
是不是要參考外部別人已經定義好的 network ( 所以如果找不到就會報錯 ),
但如果不加上 external
option,也就代表是自己定義的,會幫你自動建立
你所定義的 network,名稱為 my_network。
如果你都完全沒有定義 networks,預設就是資料夾的名稱_default 。
在 docker-compose 中有兩種方法可以暴露容器 ports,
分別是 ports 和 expose,
...
ports:
- "5000:5000" # 绑定 container 中的 5000 port 到 本機(HOST) 的 5000 port
# (HOST:CONTAINER)
- "5001:5000" # 绑定 container 中的 5000 port 到 本機(HOST) 的 5001 port
- "5000" # 绑定 container 中的 5000 port 到本機的任意 port (本機會隨機被分配到一個 port)
...
隨機 port 範例,
這邊使用 dpage/pgadmin4 這個 images 來示範,
docker run -p 80 \
-e "[email protected]" \
-e "PGADMIN_DEFAULT_PASSWORD=SuperSecret" \
-d dpage/pgadmin4
如果我們執行兩次以上指令,你會發現本機被分配到兩個隨機的 ports (如下圖),
本機被隨機分配到 32768 以及 32769 port,
這邊不管我們怎麼設定 ports,這些 ports 都會暴露給本機 (HOST) 以及其他 containers,這點很重要:exclamation::exclamation:
也就是說,如果本機 5001 ports 被使用了,其他的 containers 就無法使用 5001 ports,
可能要改成5002 ports 之類的。
...
expose:
- "4000"
- "6000"
...
expose 是將 port 暴露給其他容器。
expose 和 ports 最大的差別就是在 expose 不會暴露 port 給本機(HOST),
所以 本機(HOST)絕對無法被訪問,但 containers 內可以被訪問,
所以說如果今天你的容器想要在 本機(HOST) 被訪問,一定要使用 ports 方式。
ports 和 expose 差異
簡單說,就是 ports 可以被 本機(HOST) 和 containers 訪問 ; 而
expose 是本機(HOST) 無法被訪問,只有在 containers 中可以被訪問。
假設今天在本機上有一個 A 服務, 他是使用 docker run 起來的,
而本機上還有一個 B 服務, 是用 vscode run 起來的 (非 docker),
這時候我有一個需求, 我想要將我的 A 服務連線到我的 B 服務,
也就是從 docker 內的服務連接到本機 localhost.
比較簡單的方法, 就是透過 docker 內的這個參數,
host.docker.internal:host-gateway
.
在你的 yml 裡面加上,
version: '3.5'
services:
web:
......
extra_hosts:
- "host.docker.internal:host-gateway"
......
這樣當你在容器內, 就可以順利訪問本機:smile:
curl http://host.docker.internal:8069
也可以參考 docker compose 安裝 pgadmin4,
假設今天不考慮使用網路的方法, 如果一個容器 db 是在 5432, 另一個容器是 pgadmin4,
這樣要怎麼透過 pgadmin4 連接到我的本機的 5432 呢😵💫
答案就是使用 host.docker.internal:host-gateway
.
可以把它想成是一個類似 github 的地方,只不過裡面變成是存 docker 的東西,當然,
也可以自己架,但會有一些額外的成本,像是網路,維護等等,這部分就要自己衡量了:grinning:
接下來教大家如何將 image push 到 Docker Registry 😃
首先,先登入 Docker Registry ( 註冊流程很簡單,我就跳過了 )
docker login
舉個例子,先 run 一個 busybox 的容器
docker run -it busybox
接著在裡面新增一筆資料
echo 'text' > data.txt
然後打開另一個 terminal ,使用 docker ps
查看目前容器的 id
再來使用像 git 一樣的方式 commit
docker commit
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
可參考 https://docs.docker.com/engine/reference/commandline/commit/
docker commit -m "test" 4fb4ef51e917 twtrubiks/my_busybox
-m
commit message ,和 git 一樣。
twtrubiks/my_busybox 則為我們定義的 REPOSITORY。
如果需要 tag , 也可以增加
docker commit -m "test" 4fb4ef51e917 twtrubiks/my_busybox:v1
( 如果沒定義 tag , 則會顯示 latest )
這時候可以用 docker images
查看
最後 push
docker push twtrubiks/my_busybox
docker 是一層一層的概念,他只會 push 自己新增的幾層上去而已,
所以不用擔心整個 image 很大,要上傳很久
最後可以到 https://hub.docker.com/ 確認是否有成功 😄
上面介紹了那麼多,來實戰一下是必須的 😆
我們使用 Django-REST-framework 基本教學 - 從無到有 DRF-Beginners-Guide 來當範例
有幾個地方必須修改一下,
將 settings.py
裡面的 db 連線改成 PostgreSQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'password123',
'HOST': 'db',
'PORT': 5432,
}
}
建議也將 ALLOWED_HOSTS = []
改為 ALLOWED_HOSTS = ['*']
( 這只是方便,實務上不會這樣使用 )
再來是兩個很重要的檔案,分別為 Dockerfile
和 docker-compose.yml
Dockerfile
FROM python:3.8.12
LABEL maintainer twtrubiks
ENV PYTHONUNBUFFERED 1
RUN mkdir /docker_api
WORKDIR /docker_api
COPY . /docker_api/
RUN pip install -r requirements.txt
詳細可參考 https://docs.docker.com/engine/reference/builder/
docker-compose.yml
version: '3'
services:
db:
container_name: 'postgres'
image: postgres
environment:
POSTGRES_PASSWORD: password123
ports:
- "5432:5432"
# (HOST:CONTAINER)
volumes:
- pgdata:/var/lib/postgresql/data/
web:
build: ./api
command: python manage.py runserver 0.0.0.0:8000
restart: always
volumes:
- api_data:/docker_api
# (HOST:CONTAINER)
ports:
- "8000:8000"
# (HOST:CONTAINER)
depends_on:
- db
volumes:
api_data:
pgdata:
詳細可參考 https://docs.docker.com/compose/compose-file/#compose-file-structure-and-examples
溫馨小提醒 1 ❤️
可能有人會問為什麼我是使用 0.0.0.0
,而不是使用 127.0.0.1
❓❓
python manage.py runserver 0.0.0.0:8000
127.0.0.1
,並不代表真正的 本機,我們經常認為他是本機是因為我們電腦的 host
預設都幫你設定好了:smirk:
詳細的 host
設定教學可參考 hosts-設定檔 以及 查詢內網 ip,
0.0.0.0
才是真正的代表,當下 ( 本 ) 網路中的本機 ✏️
如果大家想更深入的了解,可 google 再進一步的了解 127.0.0.1
以及 0.0.0.0
的差異 😄
溫馨小提醒 2 ❤️
這邊要特別提一下 depends_on
這個參數,
詳細可參考 https://docs.docker.com/compose/compose-file/#depends_on,
上面連結中有一段說明很值得看
depends_on does not wait for db and redis to be 「ready」 before starting web - only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.
以我的 docker-compose.yml 為例,啟動順序雖然為 db -> web,但他不會等待 db 啟動完成後才啟動 web,
也就是說,還是有可能 web 比 db 先啟動完成,這樣就需要重啟 web service,否則會無法連上 db 😭
如果真的要控制啟動順序,請參考 Controlling startup order。
溫馨小提醒 3 ❤️
docker-compose.yml
其實使用 docker run
也是可以完成的,例如這個範例中,如果使用
docker run
來寫,會變成這樣。
首先,為了讓容器彼此可以溝通,我們先建立一個網路 ( User-defined networks ),
docker network create my_network
db 容器
docker run --name db -v pgdata:/var/lib/postgresql/data/ -p 5432:5432 --network=my_network -e POSTGRES_PASSWORD=password123 postgres
接下來先去 api 資料夾中 build 出 image
docker build --tag web_image .
--tag , -t
, tag 這個 image 名稱為 web_image
也可以是
docker build -t user/repo:tag .
web 容器
docker run --name web -v api_data:/docker_api -p 8000:8000 --network=my_network --restart always web_image python manage.py runserver 0.0.0.0:8000
以上這樣,和 docker-compose.yml
其實是一樣的:open_mouth:
設定完了之後,接下來我們就可以啟動他了
docker-compose up
接下來你會看到類似的畫面
假如你出現了類似的畫面
代表 database 還在建立的時候,你的 web ( Django ) 就去連接他,
所以導致連接不上,這時候我們可以先終止他 ( 按 Ctrl+C )
接著在重新 docker-compose up
我們成功啟動了 ( db 連線也正常 )
❗ commit 已經更新為自動 migrate:exclamation:
但你仔細看上圖,你會發現他說你還沒 migrate
接下來我們開啟另一個 cmd 進入 web 的 service,
透過剛剛介紹的指令進入 service
docker ps
docker exec -it <Container ID> bash
或是說也可以從 Kitematic 進入,
進入後我們可以開始 migrate
python manage.py makemigrations musics
python manage.py migrate
順便在建立一個 superuser
python manage.py createsuperuser
❗ commit 已經更新為自動建立 superuser:exclamation:
請參考 docker-compose.yml 中的 environment ( 如下 ),
DJANGO_SUPERUSER_USERNAME
DJANGO_SUPERUSER_PASSWORD
DJANGO_SUPERUSER_EMAIL
接著我們可以試著使用 GUI 介紹連接 db,
因為我們是用 PostgreSQL ,可以透過 pgadmin 連線
我們剛剛 migrate 的東西確實有存在
我們不需要再重新啟動
直接可以開開心心的去瀏覽 http://127.0.0.1:8000/api/music/
大家一定會看到很熟悉的畫面
接著依照自己剛剛設定的帳密登入進去即可
以上整個環境,都是在 Docker 中 😮
如果我們再 Ctrl+C 退出,重新啟動一次 docker-compose up
這次就不會再和你說你沒有 migrate 了
除了 Kitematic 之外,還有其他不錯的推薦給大家,
這次要介紹的就是 portainer 功能強大又好用 🔥
其實如果去看看 Kitematic 以及 portainer 的 github,
你會發現 portainer 感覺比較有在 maintenance 😄
而且我使用了 portainer 之後,真心大推 😃
安裝方法可參考 https://portainer.io/install.html
docker volume create portainer_data
docker run --name=portainer -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
-d
-p
在前面的 docker run
有介紹過代表的含意,--name
只是命名而已。
Note 1
: The -v /var/run/docker.sock:/var/run/docker.sock option is available on Linux environments only.
Note 2
: The -v portainer_data:/data portainer/portainer option will persist Portainer data in portainer_data on the host where Portainer is running. You can specify another location on your filesystem.
( 建立起來之後,就依照 container 的操作即可 )
之後查看 http://localhost:9000/ 就會看到下圖
然後設定帳號、密碼
選 Local or Remote
畫面真的不錯看,而且資訊也很豐富 😍
相信我,你使用完他之後,你會默默的邊緣化 Kitematic 😏
這個推薦給大家,有時候會遇到 port 被佔用,用指令查比較方便
Linux
安裝 net-tools
sudo apt install net-tools
查看誰佔用 80 port
sudo netstat -lnp | grep -w ':80'
-l
, --listening
display listening server sockets.
-n
, --numeric
don't resolve names.
-p
, --programs
display PID/Program name for sockets.
也可以使用 lsof
sudo lsof -i :80
-i
select IPv[46] files.
Windows
查看所有 port 的佔用狀況
netstat -ano
查看指定 port 的佔用狀況,例如現在想要查看 port 5432 佔用的狀況
netstat -aon|findstr "5432"
查看 PID 對應的 process
tasklist|findstr "2016"
停止 PID 為 6093 的 process
taskkill /f /PID 6093
停止 vscode.exe process
taskkill /f /t /im vscode.exe
MAC
將 port 為 8000 的 process 全部停止
sudo lsof -t -i tcp:8000 | xargs kill -9
查看指定 port 的佔用狀況,例如現在想要查看 port 5432 佔用的狀況
lsof -i tcp:5432
Youtube Tutorial - 如何清除 Docker container log
docker 的 container log 都會在 /var/lib/docker/containers
裡面
( 前提是你使用官方的安裝方法, Youtube Tutorial - Ubuntu(Linux) 如何安裝 docker)
如果你是使用 snap
安裝 docker, 路徑則會在 /var/snap/docker/common/var-lib-docker/containers
.
log 是一個 json 的檔案
如果你一直不去管他, log 就會越來越大:scream:
以下狀況這個 log 會被清除, 就是修改了 docker-compose.yml
或是
你執行了 docker-compose down
, 這些 logs 都會被清除 (因為 containers 重新建立).
(docker-compose stop
不受影響, 因為只是暫停而已)
建立大家可參考 docker-compose up/down 和 restart 的差異
那你可能會問我, 如果我很長一段時間都不會修改 docker-compose.yml
以及執行
docker-compose down
該怎麼辦:sob: (因為 log 可能會長很快)
這邊提供大家一個方法, 使用 linux 中的 truncate 指令(可參考 Linux 指令教學 - truncate)
刪除全部 container 的 logs
truncate -s 0 /var/lib/docker/containers/*/*-json.log
但是有時候只希望針對(清除)某個 container 的 logs, 這時候就可以使用以下的指令
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <container_name_or_id>)
(container_name_or_id
請換上自己 container 的 id 或 name)
其中的 docker inspect --format='{{.LogPath}}' <container_name_or_id>
只是顯示路徑而已.
但還有一個更好的方法, 直接透過 docker 內的 JSON File logging driver.
Youtube Tutorial - Docker 中的 JSON File logging driver(container log)
在 docker 中 json-file driver 是默認的 default logging driver, 詳細可參考 json-file
所以我們可以透過這個設定限制 log 的大小,
logging:
driver: "json-file"
options:
max-file: "1" # default 是 1
max-size: "200m" # default 是 -1, 也就是沒有限制
設定完之後重新啟動 docker-compose, 可以使用以下的指令查看是否生效
docker inspect --format '{{.HostConfig.LogConfig}}' CONTAINER
這樣設定完之後, 就不用再擔心 container log 吃掉大量的容量了:smile:
直接來看一個範例 docker-compose.yml
version: '3.5'
services:
web:
image: odoo:17.0
depends_on:
db:
condition: service_healthy
ports:
- "8069:8069"
healthcheck:
test: curl -fs http://localhost:8069/web/database/selector || exit 1
interval: 10s
timeout: 5s
retries: 5
volumes:
- odoo-web-data:/var/lib/odoo
- ./config:/etc/odoo
db:
image: postgres:16
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=odoo
- POSTGRES_PASSWORD=odoo
- PGDATA=/var/lib/postgresql/data/pgdata
healthcheck:
test: ["CMD-SHELL", "pg_isready -U odoo"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- odoo-db-data:/var/lib/postgresql/data/pgdata
volumes:
odoo-web-data:
odoo-db-data:
當執行時, 你會發現多了一個 health: starting
如下圖,
當(每)過了 10 秒 (interval: 10s
) 之後, 如果順利啟動會變成 (healthy)
如下圖,
當(每)過了 10 秒之後, 如果連續失敗很多次 (retries: 5
),
則會顯示 (unhealthy)
如下圖,
docker 的 Health Check 會回傳你數字,
0 代表成功,container is healthy
1 代表失敗,假設失敗超過指定次數(retries: 5
), container is unhealthy
至於 depends_on 底下的 condition: service_healthy
代表必須檢查通過,
才會啟動, 可參考 Control startup, 有以下三種,
service_started
如果沒有特別指定, 就是這一種.
service_healthy
service_completed_successfully
Docker 算是我最近才開始接觸的,所以也算是新手,如果我有任何講錯的,歡迎和我說,我會再修改 😀
Docker 可以玩的真的很多,延伸參考
-
實戰 Docker + Jenkins + Django + Postgres 📝 - 結合 Jenkins
-
實戰 Docker + Django + Nginx + uWSGI + Postgres - Load Balance 📝
也可以再玩玩 Docker Swarm ( 分散式系統 ) 😆
最後,希望大家在學習 Docker 的過程中,遇到不懂的,可以去找資料並且了解他, 順便補足一些之前不足的知識。
- Mac
- Python 3.8.12
- windows 10
文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡:laughing:
綠界科技ECPAY ( 不需註冊會員 )
歐付寶 ( 需註冊會員 )
MIT license