Linux中以單容器部署Nginx+ASP.NET Core

Linux中以單容器部署Nginx+ASP.NET Core
引言
  正如前文提到的,強烈推薦在生產環境中使用反向代理服務器轉發請求到Kestrel Http服務器,本文將會實踐將Nginx --->ASP.NET Core 部署架構容器化的過程。

Nginx->ASP.NET Coe部署架構容器化
  在Docker中部署Nginx--->ASP.NETCore 有兩種選擇, 第一種是在單容器內部署Nginx+ASP.NET Core, 這是本文着重要講述的,另外一種是以獨立容器分別部署Nginx和ASP.NET Core,容器之間通過Docker內建的network bridge完成通信(請關注後續博文)。

本次實踐將會使用.NET Core CLI 創建默認的web應用
1
2
3
4
5
mkdir app
cd app
dotnet new web
dotnet restore
dotnet build
  之後將項目發佈到指定目錄(dotnet publish), 發佈產生的文件將會用於鏡像打包。

構建鏡像
  本次將以 ASP.NETCore Runtime Image【mcr.microsoft.com/dotnet/core/aspnet:2.2】 作爲基礎鏡像, 該鏡像包含.NET Core Runtime、ASP.NET Core框架組件、依賴項, 該鏡像爲生產部署做了一些優化。
坑1:本次部署的是web app,不要使用【mcr.microsoft.com/dotnet/core/runtime:2.2】作爲基礎鏡像,啓動容器會報錯:

It was not possible to find any compatible framework version
The specified framework 'Microsoft.AspNetCore.App', version '2.2.0' was not found.

因爲該基礎鏡像不包含ASP.NET Core框架組件。
  
  本次Dokefile的定義將會包含nginx,在容器內啓用Nginx標準配置代理請求到Kestrel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2

RUN apt-get update
RUN apt-get install -y nginx

WORKDIR /app
COPY bin/Debug/netcoreapp2.2/publish .

COPY ./startup.sh .
RUN chmod 755 /app/startup.sh

RUN rm /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx

ENV ASPNETCORE_URLS http://+:5000
EXPOSE 5000 80

CMD ["sh", "/app/startup.sh"]
  Line 1 指定基礎鏡像

  Line 3-4 從Debian package management store安裝Nginx

  Line 6-7 設置工作目錄,放置ASP.NET Core WebApp部署包

  Line 9-10 設置啓動腳本

  Line 12-13 設置nginx配置文件

  Line 15-16 設置ASP.NETCore Kestrel在5000端口上監聽, 暴露5000,80 端口給容器外部

  Line 18 稍後給出啓動腳本

tip: 需要理解容器內是一個獨立的linux環境,Dockfile中EXPOSE用於指示容器打算暴露的端口。

    這裏可只暴露80端口給外部,但是必須給ASPNETCORE_URLS定義一個非80端口,作爲容器內kestrel監聽端口。

最終(tree -L 1)輸出的app目錄結構如下

1
2
3
4
5
6
7
8
9
10
11
12
.
├── app.csproj
├── appsettings.Development.json
├── appsettings.json
├── bin
├── Dockerfile
├── nginx.conf
├── obj
├── Program.cs
├── Properties
├── Startup.cs
└── startup.sh
  

Nginx配置
  創建以上Dockerfile中需要的nginx配置文件,在同一目錄,vim nginx.conf 創建文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
worker_processes 4;

events { worker_connections 1024; }

http {

sendfile on;

upstream app_servers {
    server 127.0.0.1:5000;
}

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;
    }
}

}
  Line 8-10 定義一組服務器(這裏只有webapp), 資源名稱(app_servers)可用在本文件任意位置。  

  Line 13 通知Nginx在80端口監聽

  Line 15-22 指示所有的請求都需要被代理到app_servers

  總之,這個文件定義了Nginx在80端口監聽外部請求,並將請求轉發給同一容器的5000端口。

啓動腳本
  對於Docker容器,只能使用一個CMD(或ENTRYPOINT定義),但是這種反向代理配置需要啓動Nginx和Kestrel, 所以我們定義一個腳本去完成這兩個任務

1
2
3

!/bin/bash

service nginx start
dotnet /app/app.dll

構建鏡像
  docker build -t example/hello-nginx .

  該鏡像名稱爲 example/hello-nginx 觀察輸出,會看到Dockerfile 中定義的每一步輸出。

  該鏡像構建Dockerfile與vs docker tool生成的dockerfile進行對比,該文件生成的鏡像更小,充分利用了鏡像分層的理念。

運行鏡像
  docker run --name test -it -d -p 8080:80 example/test

  該容器名稱爲test, 現在可從 http://localhost:8080 端口訪問webapp, 通過curl -s -D - localhost:8080 -o /dev/null 驗證

  通過shell終端進入容器內部, 可進一步分別探究Nginx和Kestrel服務:

  docker exec -it test bash

1
2
3
4
5
6
7
8
9
10
11
12
13

curl -s -D - localhost:80 -o /dev/null

HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Fri, 24 Feb 2017 14:45:03 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked

curl -s -D - localhost:5000 -o /dev/null

HTTP/1.1 200 OK
Date: Fri, 24 Feb 2017 14:45:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Kestrel
tip:對於正在運行的容器,可使用docker exec -it [container_id] [command] 進入容器內部探究容器

  對於啓動失敗的容器,可使用docker logs [container_id] 查看容器輸出日誌

瞭解一下docker的網絡基礎知識:

  當Docker守護進程以其默認的配置參數在宿主機啓動時,會創建一個名爲docker0的Linux網橋設備, 該網橋會自動分配滿足標準的私有IP段的隨機IP直至和子網, 該子網決定了所有新創建容器將被分配的容器IP地址所屬網段。

可使用 docker inspect [container_id] 查看network部分配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---- 截取自 docker inspect [container_id]的輸出---

"Networks": {

            "bridge": {
                "IPAMConfig": null,
                "Links": null,
                "Aliases": null,
                "NetworkID": "a74331df40dc8c94483115256538304f1cbefe9f65034f20780a27271e6db606",
                "EndpointID": "4f35ea62c1715bd9f6855bc82ada06e1bf5e58291dabb42e92ebc9552c6f017b",
                "Gateway": "172.17.0.1",
                "IPAddress": "172.17.0.3",
                "IPPrefixLen": 16,
                "IPv6Gateway": "",
                "GlobalIPv6Address": "",
                "GlobalIPv6PrefixLen": 0,
                "MacAddress": "02:42:ac:11:00:03",
                "DriverOpts": null
            }
        }

  其中列出的NetworkID 正是 docker network ls 名爲bridge的網橋, 這便是默認建立的docker0 網橋(docker inspect networkid 可繼續探究)。

正如上面所說,ASP.NET Core有兩種容器化反向代理部署架構,後續將會實踐以獨立容器分別部署Nginx、ASP.NET Core。

作者:Julian_醬

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章