推薦在生產環境中使用反向代理服務器轉發請求到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應用
mkdir?app cd?app dotnet?new?web dotnet?restore dotnet?build
之后將項目發布到指定目錄(dotnet publish), 發布產生的文件將會用于鏡像打包。
相關教程:Linux視頻教程
?構建鏡像
本次將以 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 versionThe specified framework ‘Microsoft.AspNetCore.App’, version ‘2.2.0’ was not found.- Check application dependencies and target a framework version installed at:/usr/share/dotnet/- Installing .NET Core prerequisites might help resolve this problem:https://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409- The .NET Core framework and SDK can be installed from:https://aka.ms/dotnet-download因為該基礎鏡像不包含ASP.NET Core框架組件。
本次Dokefile的定義將會包含nginx,在容器內啟用Nginx標準配置代理請求到Kestrel:
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目錄結構如下
. ├──?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 創建文件:
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, 所以我們定義一個腳本去完成這兩個任務? ??
#!/bin/bash service?nginx?start dotnet?/app/app.dll
相關教程:Linux視頻教程
?構建鏡像
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
#?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部分配置:
----?截取自?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。?