docker-compose是個好東西,越用越香


 回顧前文

前文演示了在單一容器中部署 Nginx和ASP.NET Core WebApp, 正在前文評論區某大牛指出的,容器化部署 nginx+ASP.NET Core 有更符合實戰的部署選擇:多容器獨立部署。

這次記錄我在工作中利用 docker-compose部署企業級web應用。

   本文會講述企業級示例項目中用到的 docker volume、docker network、redis、sqlite、docker HealthCheck 等相關知識, 略去CentOS平台基本操作、Linux 下安裝Docker ,docker compose工具, Linux安裝Redis等前置知識點。

頭腦風暴

  圖片總是比文字更能表達思想,下面圖示幫助同學們在 頭腦中快速臨摹出本次企業級項目的業務、部署流程, 方便同學們對照實戰。

Web App業務上依賴第三方服務、容器外Redis服務、Sqlite數據庫,可以想見我們會利用到 docker Volume機制和部分容器網絡知識,

此處我們會以獨立容器分別部署ASP.NETCore WebApp、Nginx容器,docker-compose 容器編排工具登場。

操作步驟 

1. 准備應用程序部署文件

       利用dotnet publish CLI命令或者 WebDeploy工具生成部署文件,這里因為還沒有實現CI自動構建鏡像,需要手動將部署文件拷貝到如下圖示publish文件夾,現場生成鏡像。

EqidManager
├── app
│   ├── Dockerfile
│   └── publish
├── applogs
├── docker-compose.yml
├── EqidManager.db
└── nginx
    ├── Dockerfile
    └── nginx.conf

2. 應用docker-compose 工具

         這次將涉及兩個獨立的Docker容器,Docker Compose工具將兩者連接在一起。

Docker 的優勢非常明顯,尤其是對於開發者來說,它提供了一種全新的軟件發布機制:使用 docker鏡像作為軟件產品的載體,使用 docker容器提供獨立的軟件運行上下文環境,使用 docker hub 等提供鏡像的集中管理,這其中最重要的是使用 Dockerfile 定義容器的內部行為和關鍵屬性來支持軟件運行。

但實際的生產環境往往需要定義數量龐大的 docker 容器,並且容器之間具有錯綜復雜的聯系,手動的記錄和配置這些復雜的容器關系,不僅效率低下而且容易出錯。所以迫切需要一種類似於【Dockerfile定義docker容器】那樣能夠【定義容器集群編排和部署】的工具。於是Docker Compose 出現了(其實應該說 Fig 出現了,docker 收購了 Fig 並改名為 compose)。

         針對以上應用程序,在根目錄下創建docker-compose.yml 文件:
version: "3.4"

services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile
    expose:
      - "80"
    extra_hosts:
      - "dockerhost:172.18.0.1"
    environment:
      TZ: Asia/Shanghai 
    volumes:
      - type: bind
        source: /mnt/eqidmanager/eqidlogs
        target: /app/eqidlogs
      - type: bind
        source: /home/huangjun/eqidmanager/applogs
        target: /app/logs
      - type: bind
        source: /home/huangjun/eqidmanager/EqidManager.db
        target: /app/EqidManager.db
    healthcheck:
      test: ['CMD','curl','-f','http://localhost/healthcheck']
      interval: 1m30s
      timeout: 10s
      retries: 3
      start_period: 6s
logging:
options:
max-size: "200k"
max-file: "10" proxy: build: context: .
/nginx dockerfile: Dockerfile ports: - "80:80" environment: TZ: Asia/Shanghai
links:
    - app
logging:
options:
max-size: "200k"
max-file: "10"

這個配置定義了兩個服務: app、nginx

  •  對於每個服務,【build】 告訴docker-compose怎樣為每個服務構建鏡像

  • 【expose】和【ports】控制服務與 network bridge、宿主機交互的方式

  • 【links】表明鏈接另外的容器,意味着nginx啟動的時會去啟動app服務

  •  在本應用程序中有業務數據需要被持久化, 同時使用了Sqlite數據庫,所以使用 【Volumes】來映射宿主機路徑到app 容器內路徑, 注意容器掛在的源目錄必須使用絕對路徑。

  • 本應用程序中因為涉及按小時生成業務日志文件,與本地時間有很大關聯性,這里特意強調容器內外最好使用同一時區, 容器內默認時區可能與宿主機本地不符,使用【TZ】環境變量配置容器內時區

  • 應用程序在http://localhost/healthcheck 配置了健康檢查能力, 這里使用Docker內置的【HealthCheck】指令輪詢 app內的健康檢查端口, 以判斷容器是否持續以預期的方式運作, 更多信息,請參考...

  • 其中的【extra_hosts】在容器內添加主機名映射, 類比與 在我們的電腦上hosts文件中增加一行主機名映射關系, 這個稍后會細說

  • 2019-06-16日更新:  添加Logging配置節,配置web程序和nginx日志大小(10個日志文件,每個最大200k)

3. 創建獨立鏡像

① 在app目錄創建Dockerfile文件

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
WORKDIR /app
COPY publish .
EXPOSE 80
ENTRYPOINT ["dotnet","EqidManager.dll"]
View Code

      上面的Dockerfile 顯示將publish 文件件下的部署文件拷貝進docker鏡像, 配置容器在80端口監聽請求 

② 在nginx文件夾下創建Dockerfile 文件,將會使用基礎nginx鏡像和自定義的nginx.conf文件

 FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
View Code

      nginx.conf 文件與前文類似:

worker_processes 4;
 
events { worker_connections 1024; }
 
http {
    sendfile on;
 
    upstream app_servers {
        server app:80;
    }
 
    server {
        listen 80;
 
        location / {
            proxy_pass         http://app_servers;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }
}

     該nginx.conf與前文的區別是第9 行,前文因為app和nginx在一個容器,所以【upstream app_servers】配置為 server  localhost:5000, 這里我們更改為server  app:80, app是在docker-compose.yml 文件中指定的服務名稱。

4. 構建容器集合 --> 運行集合

在CentOS上安裝了docker-compose工具之后, docker-compose --help 會看到可以利用的工具指令:

// build 命令會構建/重建每一個服務, 然后使用項目名稱和服務名稱標記每個鏡像、容器

docker-compose build

// up 命令創建並運行容器

docker-compose up

如下圖示: docker-compose 默認會利用項目名稱EqidManager , 應用程序服務名稱app 構建 ImageName=“EqidManager_app”鏡像和對應容器。

本例中,訪問localhost:80可驗證是否成功部署。

網橋模式

      最后我們來探究容器集合的網絡連接, 這也是容器比較復雜的部分。

 docker引擎剛建立的時候,會新建一個docker0網橋(driver= bridge), 新加入的容器默認都會接入這個網橋。

  當執行docker-compose  up時,會創建新的網橋設備,集合內所有容器都通過該網橋交流:

① 創建名為 {project}_default 的網橋

②  以服務名app加入 {project}_default 網絡; 以服務名nginx加入 {project}_default 網絡

     每一個容器現在可使用 “app” /  “nginx”  服務名作為主機名相互訪問

為啥可以通過 服務名訪問 容器?   

是因為利用了 Docker引擎內置的DNS, 查詢服務名----》 查詢DNS(每個服務名: 對應容器IP) 

     所以在nginx.conf 文件中我們給 【upstream app_servers】配置 app:80 能正確轉發請求:

 

  docker-compose.yml文件中【extra_hosts】的用法:

     當前程序架構中使用的是宿主機的Redis服務,在app 容器內不能再使用localhost:6379引用redis服務, 因為容器內localhost 指向的是容器自身。

     【extra_hosts】指令用於主機名映射,定義宿主機在容器內別名。,可通過docker inspect [network_id] 查看宿主機在網橋上的映射IP:

[root@search-referer1 nginx]# docker inspect 0b576abb7ead
[
    {
        "Name": "eqidmanager_default",
        "Id": "0b576abb7ead9041a4aa0fe786c3e448f0ca93abe2559560e75f491bea326754",
        "Created": "2019-04-30T00:53:31.047534813+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "Containers": {
            "f0b08e0e54e7e9293211bba0eb92f55749c3de4b31dc8011c3f803c02a69000a": {
                "Name": "eqidmanager_proxy_1",
                "EndpointID": "1311f2b21b2a0ecec205e6a8902d298eece8a782f7f7ab785ded6561b8ff7c5e",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "f57f5f1f69351245932885c2de271d387df0055b1c51a17242c8bc1e941ed32b": {
                "Name": "eqidmanager_app_1",
                "EndpointID": "b89d144948ee58870e07b9d1cfc5fd42f1bfe0a4b15e4fc2905d13136acb0a7e",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "eqidmanager",
            "com.docker.compose.version": "1.24.0"
        }
    }
]
View Code

      本實例中docker-compose 新建的eqidmanager_default網橋網關是 172.18.0.1,在docker-compose.yml 文件中配置了上述【extra_hosts】,在對應的app容器內我們cat  /etc/hosts 會發現新增的映射記錄:

 

 相應的連接字符串是 :

"connectionstrings": {
    ”sqlite": "Data Source=EqidManager.db",
     "redis": "dockerhost:6379,password=****@1,connectTimeout=10000,writeBuffer=40960"
},

tip: 這里假定每次執行docker-compose up/down命令,網關/子網都不變, 實際上很有可能變動, 最好使用自定義網橋。 

       由網橋而來的流量不經過宿主機的本地回環,因此需要將宿主機上的應用(MySQL,Redis等)配置為監聽0.0.0.0。

 

 That‘s all, 編寫一個企業級docker-compose.yml 文件需要對項目業務流程和部署流程有全盤了解,同時必須要具備完備的計算機操作原理和網絡原理知識;

     當然,當你編寫完一個企業級docker-compose.yml文件並成功運行,這也印證了你已經全盤熟悉項目架構同時也重溫了計算機操作原理和網絡原理

      心中竊喜, docker-compose是個好東西,越用越香

      希望本文對初涉容器平台的同學能有一個拋磚引玉的效果。

 

https://www.cnblogs.com/JulianHuang/p/11636825.html

 

作者: JulianHuang

碼甲拙見,如有問題請下方留言大膽斧正;碼字+Visio制圖,均為原創,看官請不吝好評+關注,  ~。。~

本文歡迎轉載,請轉載頁面明顯位置注明原作者及原文鏈接

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM