昨天我們用 Compose file 一次描述三個容器, Compose file 如下:
// docker-compose-bridge.yml
version: '2'
services:
    database:
        image: mongo:4.1
        container_name: mongo4
        ports:
            - "27017:27017"
        volumes:
            - "./data/mongo/data:/data/db"
    frontend:
        image: ithelp/frontend:1.0.0
        container_name: ithelp.frontend
        ports:
            - "80:80"
        volumes:
            - "./data/nginx/log:/var/log/nginx"
    backend:
        image: ithelp/backend:1.0.0
        container_name: ithelp.backend
        ports:
            - "3001:3001"
        command: >
            /bin/bash -c "
            sleep 15;
            npm run start;"
        environment:
            PORT: 3001
            NODE_ENV: "development"
            MONGODB_URL: "mongodb://database:27017"
在用指令 docker-compose 建立並執行它們:
仔細看有一行:Creating network "ithelp-30dayfullstack-day30_default" with the default driver。這是什麼呢?
這就是今天要討論的內容:network 。 network 很重要,因為關係它各服務間的通信。
我們會重心放在如何使用,而不是放在解析 docker network。先學會使用,再去深究其原理。有興趣的人可以挑戰看看這篇 Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks。
今天的內容全是指在單一主機用 Docker Compose 可能會發生的 network 情況。在還沒進到叢集的世界之前,不同主機的連線,用 IP 連就可以了。光是用 Docker Compose 你就能感受到 docker 的威力。
在開始前,我們開一個 ithelp-30dayfullstack-Day30 資料夾,所有的 Compose file 都會放在裡面。
這是 Docker Compose 預設的網路模式,所有有 service 預設 network_mode: 都是 bridge (見 network_mode)。當我們 docker-compose up 時,會建立一個名為 <dir_name>_default 的 network 出來,所有 bridge mode 的 container 會自己配網路卡並接上那個預設的 network。
我們在回憶中的 Compose file,用圖解如下:
圖片重繪自:Networking for Docker Containers (a Primer) Part I
172.28.0.0/16是指一網路的網段從172.28.0.1 ~ 172.28.255.255,子網路的主機可以互相通訊。 16 是指 16 bits 的子網路遮罩255.255.0.0。
你可以用
docker network ls列出所有 network
Container 間的通訊有幾個方法:
backend:
        …略
        environment:
            PORT: 3001
            NODE_ENV: "development"
            MONGODB_URL: "mongodb://database:27017"
backend 可以訪問 database。docker network inspect ithelp-30dayfullstack-day30_default 可以查詢「接上此 network 的 container 資訊」
ithelp-30dayfullstack-day30_default 有三個容器及它們配置的 IP。另外還有 gateway 和 subnet。若外界 192.168.0.5 要訪問我們的主機 192.168.0.2:80 的 ithelp.front 服務,我們要設定 --publish/-p。就是我們在 Docker file 中的 ports: 或 docker run -p 設定 publish。同時, Network Address Translation(NAT) 也會自動設定內外部 network 的轉換。

從外界來看,邏輯上就像:
外界不會發現內部的網路。
若 container 要與外界通訊,可以跟往常一樣用 hostname / ip。除非你刻意去調整 dns/host 之類的設定,不然正常使用就可以了,我們不打算深究。
反而, Container 存取 HOST 是有可能發生的,因為在還沒完全引入 docker 前,我們可能在 HOST 已架好 MongoDB / MySQL 之類的服務。接下來考慮兩種可能的情況。
container 建立時會配置網路設定,沒重建立的話 IP 是不會變的。 在 bridge mode 下,NAT 那單元可以直接看成是 HOST,所以 HOST 的 IP 在 DOCKER0 BRIDGET network 中的位置就是 gateway IP 。因此,在容器中,要訪問到 HOST 是 DOCKER0 BRIDGET 下的  gateway IP 而不是 127.0.0.1 (127.0.0.1 是指容器本身)。
你可以登入 container (
docker run -it <id/container_name> bash) 試打看看(ex: telenet/ping/wget/curl),就可以知道有沒有通。
指令 docker network inspect ithelp-30dayfullstack-day30_default 就可以查到 gateway IP。
練習時,你可能要
docker-compose -f docker-compose-bridge.yml down刪除之前的容器、網路。
容器內的環境有時需要 hard code 寫 HOST 的 IP,像是 Nginx 要導轉到 HOST 的舊網址、Nginx 要導轉轉到某個 Container 或 MonoDB 連線網址寫死在 Compose file。這時候每次重建 containers 網路就會變了。因此,我們可以客製化 bridge 的設定固定 IP,一方面可以固定 Container IP 又可以固定 HOST IP。
為了固定 IP,我們要建立一個 network ithelp_application 設定裡面的 subnet: 和 gateway:,接著,每個容器使用 ithelp_application network 並指定固定 IP。 如下:
version: '2'
services:
    database:
        image: mongo:4.1
        container_name: mongo4
        networks:
            ithelp_application:
                ipv4_address: 172.28.0.2
        ports:
            - "27017:27017"
        volumes:
            - "./data/mongo/data:/data/db"
    frontend:
        image: ithelp/frontend:1.0.0
        container_name: ithelp.frontend
        networks:
            ithelp_application:
                ipv4_address: 172.28.0.3
        ports:
            - "80:80"
        volumes:
            - "./data/nginx/log:/var/log/nginx"
    backend:
        image: ithelp/backend:1.0.0
        container_name: ithelp.backend
        networks:
            ithelp_application:
                ipv4_address: 172.28.0.4
        ports:
            - "3001:3001"
        command: >
            /bin/bash -c "
            sleep 15;
            npm run start;"
        environment:
            PORT: 3001
            NODE_ENV: "development"
            MONGODB_URL: "mongodb://database:27017"
networks:
    ithelp_application:
        driver: bridge
        ipam:
            driver: default
            config:
                - subnet: 172.28.0.0/16
                  gateway: 172.28.0.1
執行 docker-compose -f docker-compose-bridge-static.yml up,完成後去看看  docker network inspect ithelp-30dayfullstack-day30_ithelp_application,就是我們固定的 IP
練習時,你可能要
docker-compose -f docker-compose-bridge-static.yml down刪除之前的容器、網路。
這是最簡單的模式,直接想成 container 執行在 HOST 的 process,它監聽什麼 port , HOST 什麼 port 就會被使用,連 --publish/-p 都不用了。
這模式的通訊比較簡單,不像 bridge mode 複雜。127.0.0.1 就是指 HOST,所以每個容器可以用 127.0.0.1:<port> 通訊,與其它的 HOST 服務也是一樣;外界就是用 192.168.0.2。
version: '2'
services:
    database:
        image: mongo:4.1
        container_name: mongo4
        network_mode: "host"
        volumes:
            - "./data/mongo/data:/data/db"
    frontend:
        image: ithelp/frontend:1.0.0
        container_name: ithelp.frontend
        network_mode: "host"
        volumes:
            - "./data/nginx/log:/var/log/nginx"
    backend:
        image: ithelp/backend:1.0.0
        container_name: ithelp.backend
        network_mode: "host"
        command: >
            /bin/bash -c "
            sleep 15;
            npm run start;"
        environment:
            PORT: 3001
            NODE_ENV: "development"
            MONGODB_URL: "mongodb://127.0.0.1:27017"

圖片重繪自:Networking for Docker Containers (a Primer) Part I
我們介紹了 Bridge mode、Host mode,它們有各自的特色:
bridge mode:
Host mode:
到這裡為止,我打算就結束今天的內容。今天的最後我們來回顧一下,我們做了什麼事、什麼事沒做?
先恭喜看到這的你,熬過三十天的疲勞轟炸。我們歷經了三個周目:
在 Day 1 - 前言/開發環境準備 曾提過我們三十天要涵盖的內容如下,若有提到我們就把它們劃掉
我想達成率只有 65% 左右,有些內容還被我刪減,離我想寫完整的東西還有一段路。每天的內容一直調整,很多時候因為時間不夠、篇幅有限、考慮到完整性、可讀性,我只能做刪減。但是,不論我怎麼調整,目標只有一個: 帶讀者瀏覽從前端到後端再到 DevOps 的歷程。
本主題只是打開你在各個技術的大門,能否或需不需要踏入就要由你自行決定了。我相信一定有很多專門的主題或文章可以說的更好,希望我能作為你看那些文章的墊腳石。
因為寫的很趕,寫完後,只能校稿一次,也許會有不通順的地方請見諒。內容不完整或缺少的部分,我目前在計畫找時間補上。未來的文章,有可能會在 Medium 或 iT邦幫忙 同時發佈,喜歡的話可以 Follow,謝謝各位。
恭喜完賽!謝謝分享~訂閱這個系列讓我收穫很多!
謝謝妳回饋
能讓讀者學到東西,一切的幸苦都值得了。
恭喜大大完賽,雖然內容不是很懂,但感受的到,撰寫時的用心,謝謝您!恭喜。
謝謝你的回應
第一次參加鐵人賽,我還有很多進步空間,我會再努力的
我發現 小信豬的原始部落 - [Docker] Bridge Network 簡介 寫的很好,邊實驗邊解釋並得到結論,有興趣的人可以看一下,會對 Bridge Network 更了解。