我是首次接觸 Docker 而且距離成爲一名 Linux 高手還有很遠的一段路程。所以,這裏的不少想法是來自一個新手。html
內容linux
安裝按照 https://www.microsoft.com/net/core 上的介紹在你的電腦上安裝 .NET Core 。這將會同時在 Windows 上安裝 dotnet 命令行工具以及最新的 Visual Studio 工具。nginx
源代碼git
你能夠直接到 GitHub 上找最到最新完整的源代碼。
github
轉換到 .NET CORE 1.0web
天然地,當我考慮如何把 API 從 .NET Core RC1 升級到 .NET Core 1.0 時想到的第一個求助的地方就是谷歌搜索。我是按照下面這兩條很是全面的指導來進行升級的:docker
當你遷移代碼的時候,我建議仔細閱讀這兩篇指導,由於我在沒有閱讀第一篇指導的狀況下又嘗試瀏覽第二篇,結果感到很是迷惑和沮喪。數據庫
我不想描述細節上的改變由於你能夠看 GitHub 上的提交。這兒是我所做改變的總結:json
惟一令我真正頭疼的事是須要移動 Serilog。我本能夠實現本身的文件記錄器,可是我刪除了文件記錄功能,由於我不想爲了此次操做在這件事情上花費精力。api
不幸的是,將有大量的第三方開發者扮演追趕 .NET Core 1.0 的角色,我很是同情他們,由於他們一般在休息時間還堅持工做但卻依舊根本沒法接近靠攏微軟的可用資源。我建議閱讀 Travis Illig 的文章 .NET Core 1.0 發佈了,但 Autofac 在哪兒?這是一篇關於第三方開發者觀點的文章。
作了這些改變之後,我能夠從project.json 目錄恢復、構建並運行 dotnet,能夠看到 API 又像之前同樣工做了。
經過 Docker 運行
在我寫這篇文章的時候, Docker 只可以在 Linux 系統上工做。在 Windows 系統和 OS X 上有 beta 支持 Docker,可是它們都必須依賴於虛擬化技術,所以,我選擇把 Ubuntu 14.04 看成虛擬機來運行。若是你尚未安裝過 Docker,請按照指導來安裝。
我最近閱讀了一些關於 Docker 的東西,但我直到如今尚未真正用它來幹任何事。我假設讀者尚未關於 Docker 的知識,所以我會解釋我所使用的全部命令。
HELLO DOCKER
在 Ubuntu 上安裝好 Docker 以後,我所進行的下一步就是按照 https://www.microsoft.com/net/core#docker 上的介紹來開始運行 .NET Core 和 Docker。
首先啓動一個已安裝有 .NET Core 的容器。
docker run -it microsoft/dotnet:latest
-it 選項表示交互,因此你執行這條命令以後,你就處於容器以內了,能夠如你所但願的那樣執行任何 bash 命令。
而後咱們能夠執行下面這五條命令來在 Docker 內部運行起來微軟 .NET Core 控制檯應用程序示例。
mkdir hwapp cd hwapp dotnet new dotnet restore dotnet run
你能夠經過運行 exit 來離開容器,而後運行 Docker ps -a 命令,這會顯示你建立的那個已經退出的容器。你能夠經過上運行命令 Docker rm <container_name> 來清除容器。
掛載源代碼
個人下一步驟是使用和上面相同的 microsoft/dotnet 鏡像,可是將爲咱們的應用程序以數據卷的方式掛載上源代碼。
首先簽出有相關提交的倉庫:
git clone https://github.com/niksoper/aspnet5-books.git cd aspnet5-books/src/MvcLibrary git checkout dotnet-core-1.0
如今啓動一個容器來運行 .NET Core 1.0,並將源代碼放在/book 下。注意更改 /path/to/repo這部分文件來匹配你的電腦:
docker run -it / -v /path/to/repo/aspnet5-books/src/MvcLibrary:/books / microsoft/dotnet:latest
如今你能夠在容器中運行應用程序了!
cd /books dotnet restore dotnet run
做爲一個概念性展現這的確很棒,可是咱們可不想每次運行一個程序都要考慮如何把源代碼安裝到容器裏。
增長一個 DOCKERFILE
個人下一步驟是引入一個 Dockerfile,這可讓應用程序很容易在本身的容器內啓動。
個人 Dockerfile 和 project.json 同樣位於 src/MvcLibrary目錄下,看起來像下面這樣:
FROM microsoft/dotnet:latest # 爲應用程序源代碼建立目錄 RUN mkdir -p /usr/src/books WORKDIR /usr/src/books # 複製源代碼並恢復依賴關係 COPY . /usr/src/books RUN dotnet restore # 暴露端口並運行應用程序 EXPOSE 5000 CMD [ "dotnet", "run" ]
嚴格來講,RUN mkdir -p /usr/src/books 命令是不須要的,由於 COPY 會自動建立丟失的目錄。
Docker 鏡像是按層創建的,咱們從包含 .NET Core 的鏡像開始,添加另外一個從源代碼生成應用程序,而後運行這個應用程序的層。
添加了 Dockerfile 之後,我經過運行下面的命令來生成一個鏡像,並使用生成的鏡像啓動一個容器(確保在和 Dockerfile 相同的目錄下進行操做,而且你應該使用本身的用戶名)。
docker build -t niksoper/netcore-books . docker run -it niksoper/netcore-books
你應該看到程序可以和以前同樣的運行,不過這一次咱們不須要像以前那樣安裝源代碼,由於源代碼已經包含在 docker 鏡像裏面了。
暴露併發布端口
這個 API 並非特別有用,除非咱們須要從容器外面和它進行通訊。 Docker 已經有了暴露和發佈端口的概念,但這是兩件徹底不一樣的事。
據 Docker 官方文檔:
EXPOSE指令通知 Docker 容器在運行時監聽特定的網絡端口。EXPOSE指令不可以讓容器的端口可被主機訪問。要使可被訪問,你必須經過 -p 標誌來發佈一個端口範圍或者使用 -p 標誌來發布全部暴露的端口
EXPOSE 指令只是將元數據添加到鏡像上,因此你能夠如文檔中說的認爲它是鏡像消費者。從技術上講,我本應該忽略 EXPOSE 5000 這行指令,由於我知道 API 正在監聽的端口,但把它們留下頗有用的,而且值得推薦。
在這個階段,我想直接從主機訪問這個 API ,所以我須要經過-p 指令來發布這個端口,這將容許請求從主機上的端口 5000 轉發到容器上的端口 5000,不管這個端口是否是以前經過 Dockerfile 暴露的。
docker run -d -p 5000:5000 niksoper/netcore-books
經過 -d 指令告訴 docker 在分離模式下運行容器,所以咱們不能看到它的輸出,可是它依舊會運行並監聽端口 5000。你能夠經過 docker ps來證明這件事。
所以,接下來我準備從主機向容器發起一個請求來慶祝一下:
curl http://localhost:5000/api/books
它不工做。
重複進行相同 curl 請求,我看到了兩個錯誤:要麼是 curl: (56) Recv failure: Connection reset by peer,要麼是 curl: (52) Empty reply from server。
我返回去看 docker run 的文檔,而後再次檢查我所使用的 -p 選項以及 Dockerfile 中的 EXPOSE指令是否正確。我沒有發現任何問題,這讓我開始有些沮喪。
從新振做起來之後,我決定去諮詢當地的一個 Scott Logic DevOps 大師 - Dave Wybourn(也在這篇 Docker Swarm 的文章裏提到過),他的團隊也曾遇到這個實際問題。這個問題是我沒有配置過 Kestral,這是一個全新的輕量級、跨平臺 web 服務器,用於 .NET Core 。
默認狀況下, Kestrel 會監聽 http://localhost:5000。但問題是,這兒的localhost是一個迴路接口。
據維基百科:
在計算機網絡中,localhost 是一個表明本機的主機名。本地主機能夠經過網絡迴路接口訪問在主機上運行的網絡服務。經過使用迴路接口能夠繞過任何硬件網絡接口。
當運行在容器內時這是一個問題,由於 localhost 只可以在容器內訪問。解決方法是更新 Startup.cs裏的 Main 方法來配置 Kestral 監聽的 URL:
public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseUrls("http://*:5000") // 在全部網絡接口上監聽端口 5000 .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); }
經過這些額外的配置,我能夠重建鏡像,並在容器中運行應用程序,它將可以接收來自主機的請求:
docker build -t niksoper/netcore-books . docker run -d -p 5000:5000 niksoper/netcore-books curl -i http://localhost:5000/api/books
我如今獲得下面這些相應:
HTTP/1.1 200 OK Date: Tue, 30 Aug 2016 15:25:43 GMT Transfer-Encoding: chunked Content-Type: application/json; charset=utf-8 Server: Kestrel [{"id":"1","title":"RESTful API with ASP.NET Core MVC 1.0","author":"Nick Soper"}]
在產品環境中運行 KESTREL
微軟的介紹:
Kestrel 能夠很好的處理來自 ASP.NET 的動態內容,然而,網絡服務部分的特性沒有如 IIS,Apache 或者 Nginx 那樣的全特性服務器那麼好。反向代理服務器可讓你不用去作像處理靜態內容、緩存請求、壓縮請求、SSL 端點這樣的來自 HTTP 服務器的工做。
所以我須要在個人 Linux 機器上把 Nginx 設置成一個反向代理服務器。微軟介紹瞭如何發佈到 Linux 生產環境下的指導教程。我把說明總結在這兒:
這些內容已經超出了本文的範圍,所以我將側重於如何把 Nginx 配置成一個反向代理服務器。天然地,我經過 Docker 來完成這件事。
在另外一個容器中運行NGINX
個人目標是在第二個 Docker 容器中運行 Nginx 並把它配置成咱們的應用程序容器的反向代理服務器。
我使用的是來自 Docker Hub 的官方 Nginx 鏡像。首先我嘗試這樣作:
docker run -d -p 8080:80 --name web nginx
這啓動了一個運行 Nginx 的容器並把主機上的 8080 端口映射到了容器的 80 端口上。如今在瀏覽器中打開網址 http://localhost:8080 會顯示出 Nginx 的默認登陸頁面。
如今咱們證明了運行 Nginx 是多麼的簡單,咱們能夠關閉這個容器。
docker rm -f web
把 NGINX 配置成一個反向代理服務器
能夠經過像下面這樣編輯位於 /etc/nginx/conf.d/default.conf 的配置文件,把 Nginx 配置成一個反向代理服務器:
server { listen 80; location / { proxy_pass http://localhost:6666; } }
經過上面的配置可讓 Nginx 將全部對根目錄的訪問請求代理到 http://localhost:6666。記住這裏的 localhost 指的是運行 Nginx 的容器。咱們能夠在 Nginx容器內部利用捲來使用咱們本身的配置文件:
docker run -d -p 8080:80 / -v /path/to/my.conf:/etc/nginx/conf.d/default.conf / nginx
注意:這把一個單一文件從主機映射到容器中,而不是一個完整目錄。
在容器間進行通訊
Docker 容許內部容器經過共享虛擬網絡進行通訊。默認狀況下,全部經過 Docker 守護進程啓動的容器均可以訪問一種叫作「橋」的虛擬網絡。這使得一個容器能夠被另外一個容器在相同的網絡上經過 IP 地址和端口來引用。
你能夠經過監測inspect容器來找到它的 IP 地址。我將從以前建立的 niksoper/netcore-books 鏡像中啓動一個容器並監測inspect它:
docker run -d -p 5000:5000 --name books niksoper/netcore-books docker inspect books
咱們能夠看到這個容器的 IP 地址是 "IPAddress": "172.17.0.3"。
因此如今若是我建立下面的 Nginx 配置文件,並使用這個文件啓動一個 Nginx 容器, 它將代理請求到個人 API :
server { listen 80; location / { proxy_pass http://172.17.0.3:5000; } }
如今我可使用這個配置文件啓動一個 Nginx 容器(注意我把主機上的 8080 端口映射到了 Nginx 容器上的 80 端口):
docker run -d -p 8080:80 / -v ~/dev/nginx/my.nginx.conf:/etc/nginx/conf.d/default.conf / nginx
一個到http://localhost:8080 的請求將被代理到應用上。注意下面 curl 響應的 Server 響應頭:
DOCKER COMPOSE
在這個地方,我爲本身的進步而感到高興,但我認爲必定還有更好的方法來配置 Nginx,能夠不須要知道應用程序容器的確切 IP 地址。另外一個當地的 Scott Logic DevOps 大師 Jason Ebbin 在這個地方進行了改進,並建議使用 Docker Compose。
概況描述一下,Docker Compose 使得一組經過聲明式語法互相鏈接的容器很容易啓動。我不想再細說 Docker Compose 是如何工做的,由於你能夠在以前的文章中找到。
我將經過一個我所使用的 docker-compose.yml 文件來啓動:
version: '2' services: books-service: container_name: books-api build: . reverse-proxy: container_name: reverse-proxy image: nginx ports: - "9090:8080" volumes: - ./proxy.conf:/etc/nginx/conf.d/default.conf
這是版本 2 語法,因此爲了可以正常工做,你至少須要 1.6 版本的 Docker Compose。
這個文件告訴 Docker 建立兩個服務:一個是給應用的,另外一個是給 Nginx 反向代理服務器的。
BOOKS-SERVICE
這個與 docker-compose.yml 相同目錄下的 Dockerfile 構建的容器叫作 books-api。注意這個容器不須要發佈任何端口,由於只要可以從反向代理服務器訪問它就能夠,而不須要從主機操做系統訪問它。
REVERSE-PROXY
這將基於 nginx 鏡像啓動一個叫作 reverse-proxy 的容器,並將位於當前目錄下的 proxy.conf 文件掛載爲配置。它把主機上的 9090 端口映射到容器中的 8080 端口,這將容許咱們在http://localhost:9090上經過主機訪問容器。
proxy.conf 文件看起來像下面這樣:
server { listen 8080; location / { proxy_pass http://books-service:5000; } }
這兒的關鍵點是咱們如今能夠經過名字引用books-service,所以咱們不須要知道 books-api 這個容器的 IP 地址!
如今咱們能夠經過一個運行着的反向代理啓動兩個容器(-d意味着這是獨立的,所以咱們不能看到來自容器的輸出):
docker compose up -d
驗證咱們所建立的容器:
docker ps
最後來驗證咱們能夠經過反向代理來控制該 API :
curl -i http://localhost:9090/api/books
怎麼作到的?
Docker Compose 經過建立一個新的叫作 mvclibrary_default 的虛擬網絡來實現這件事,這個虛擬網絡同時用於books-api 和 reverse-proxy 容器(名字是基於 docker-compose.yml 文件的父目錄)。
經過docker network ls來驗證網絡已經存在:
你可使用 docker network inspect mvclibrary_default 來看到新的網絡的細節:
注意 Docker 已經給網絡分配了子網:"Subnet": "172.18.0.0/16"。/16 部分是無類域內路由選擇(CIDR),完整的解釋已經超出了本文的範圍,但 CIDR 只是表示 IP 地址範圍。運行 docker network inspect bridge 顯示子網:"Subnet": "172.17.0.0/16",所以這兩個網絡是不重疊的。
如今用 docker inspect books-api 來確認應用程序的容器正在使用該網絡:
注意容器的兩個別名("Aliases")是容器標識符(3c42db680459)和由 docker-compose.yml 給出的服務名(books-service)。咱們經過books-service 別名在自定義 Nginx 配置文件中來引用應用程序的容器。這本能夠經過 docker network create 手動建立,可是我喜歡用 Docker Compose,由於它能夠乾淨簡潔地將容器建立和依存捆綁在一塊兒。
結論
因此如今我能夠經過幾個簡單的步驟在 Linux 系統上用 Nginx 運行應用程序,不須要對主機操做系統作任何長期的改變:
git clone https://github.com/niksoper/aspnet5-books.git cd aspnet5-books/src/MvcLibrary git checkout blog-docker docker-compose up -d curl -i http://localhost:9090/api/books
我知道我在這篇文章中所寫的內容不是一個真正的生產環境就緒的設備,由於我沒有寫任何有關下面這些的內容,絕大多數下面的這些主題都須要用單獨一篇完整的文章來敘述。
對我來講這是一個很是有趣的學習經歷,由於有一段時間我對探索 ASP.NET Core 的跨平臺支持很是好奇,使用 「Configuratin as Code」 的 Docker Compose 方法來探索一下 DevOps 的世界也是很是愉快而且頗有教育意義的。
若是你對 Docker 很好奇,那麼我鼓勵你來嘗試學習它 或許這會讓你離開溫馨區,不過,有可能你會喜歡它?