.NET開發者啟程Docker之路
經常聽到很多企業說,我們還沒用Docker,還在觀望中,以后會用的。其實Docker通過將近三年的發展,它已經成熟了,能夠為企業帶來巨大的商業價值。
未來普及是必然趨勢,使用還是要趁早才好。所以還在觀望中的朋友們,不要再猶豫了,Dokcer不只是別人的玩具,更是屬于我們每個技術人員。
為此,希云特意為大家分享微軟Azure MVP專家Elton Stoneman的經驗之談:“如何在Docker里運行.NET應用”。
Elton Stoneman
Elton是軟件架構師、微軟MVP,自2000年以來在Pluralsight Author上帶來很多成功的解決方案。他主要開發環境是在Azure和微軟軟件棧上,但也意識到跨平臺的發展帶來了很多機遇,他在Pluralsight上最新的課程也是如此,涵蓋了Azure上的大數據應用和Ubuntu基礎。
Docer容器
Docker讓我們能用更少的資源,更快的速度來構建運行應用程序,所以應用容器技術(Docker就是其中一種)在當今那么受歡迎。我們可以在只能跑幾個虛擬機的物理機器上跑上百個容器,容器的部署效率非常高,而且能用版本管理。
如果你在微軟的環境下工作,那你可能以為容器只是其他平臺的開發者能用的技術(例如LAMP,NodeJs或者Java),但是實際上你已經能在Docker里運行.NET應用了。下面,將詳細介紹如何操作。
應用容器
應用容器是一種快捷且輕量的計算單元,能讓你在一個物理(或虛擬)服務器上承載大量計算任務。一個已被容器化的應用,會被部署在一個包含了所有依賴的鏡像上,這里據說的依賴,包括了最小化安裝的操作系統。這些鏡像非常輕量,通常只有幾百兆,而且啟動只需要數秒。
Docker已經引領了容器技術的發展,讓構建、分發和使用都更加簡單,容器技術在過去幾年是一個熱門話題。原因很簡單:這種技術不僅能讓你在單個節點上運行成千上百個容器,而且,它的分發和運行速度讓人欲罷不能。所以到現在,它已經成為很多團隊開發、測試和構建流程的核心。另外,它的出現也讓人們加速轉向更易于擴展的無狀態架構。
如果按目前的發展趨勢,很可能再過幾年應用容器就會成為我們默認的部署方式,容器技術即將正式應用在Windows平臺上。但其實現在你就可以用Docker來運行.NET核心的應用,下面我們來提前體驗一下這種激動人心的技術吧。
不只是別人的玩具
容器技術使用了Linux內核的特性,使容器內的應用能像運行在原生系統上那樣調用系統指令。所以你既需要在容器中運行Linux系統,也需要Linux系統來運行容器。
但實際上Linux也可以作為虛擬機運行在Windows或者OS/X上, Docker Toolbox 把所有相關的都打成了一個包,讓我們能輕松上手在Windows上使用容器,只需要花幾分鐘下載安裝好,它帶有一個Linux虛擬機,底層使用的是 VirtualBox 。
微軟正在大力推進Docker在非Linux下運行的開發工作,在Windows Server 2016上,我們將能在Windows原生系統上運行Docker容器;而在容器中,我們將能運行Windows Nano Server系統,所以屆時我們將把.NET應用運行在原生系統上。
.NET Core
而現在,我們可以使用跨平臺的.NET Core去在Linux容器中打包應用,Docker有公共鏡像倉庫,我已經把一個示例鏡像推到上邊了,你安裝好Docker之后,可以通過以下命令來體驗一個基礎版的.NET內核應用:
shdocker run sixeyed/coracle-hello-world
第一次運行這個命令,會從Docker Hub下載容器的鏡像,這會花費一段時間,但你下次運行時,由于這個鏡像已經存在于你本地,所以將會立即運行,這個簡單的應用會輸出當前時間和日期:
雖然功能很簡單,但它是使用了.NET中的Console.WriteLine(),而這個容器中的系統是Ubuntu Server。也就是說,我們現在有了一個運行在Linux上的.NET應用容器鏡像,這樣相當于我們可以在任何地方運行.NET應用了。
.NET Core 是一個開源版的.NET,它與原來的.NET關注點并不一樣,它是一個模塊化的框架,也就是說你可以只引入你需要的依賴,這個框架本身是由NuGet包組成。
在你要運行.NET Core應用在Linux(或OS/X或Windows)上,你需要安裝DNX運行時組件,這不是官方的.NET運行,而是一個瘦身版的.NET運行環境(DNX)。
當你定義了一個Docker鏡像,你可以從一個基鏡像開始構建, sixeyed/coreclr-base 這個鏡像發布在Hub上,在它里邊DNX已經安裝配置好了,要把你的應用容器化的話,只需要在這個基鏡像中加入你的代碼,下一部分,我們看看應該怎么做。
The Uptimer App
在GitHub上有一個.NET Core應用,它會去ping一個URL,然后記錄一些關鍵字段,例如響應碼和響應時間,把結果寫到Azure blob上。代碼在 coreclr-uptimer 倉庫。
它是一個.NET Core 應用,所以它和典型的Visual Studio solution應用結構不同,它沒有solution文件,它的Sixeyed.Uptimer相當于solution,還有一個project.json定義了應用運行的配置和相關依賴:
```json
</p>
<p>
“frameworks”: {
</p>
<p>
“dnxcore50″: {
</p>
<p>
“dependencies”: {
</p>
<p>
“Microsoft.CSharp”: “4.0.1-beta-23516″,
</p>
<p>
“WindowsAzure.Storage”: “6.1.1-preview”,
</p>
<p>
“System.Net.Http”: “4.0.1-beta-23516″
</p>
<p>
…
</p>
<p>
```
</p>
<p>
這里我定義了應用運行在dnxcore50上,這是DNX最新的版本。還有那些依賴全部都是NuGet的包,.NET Core應用像普通應用那樣使用NuGet,但你可以只引用用于構建.NET Core的包,這里使用了WindowsAzure.Storage和System.Net.Http 。
</p>
<p>
以下這段代碼使用了標準C#,包括timer,disposables 和 AAT (Async, Await and Tasks):
</p>
<p>
```java
</p>
<p>
var request = new HttpRequestMessage(HttpMethod.Get, url);
</p>
<p>
using (var client = new HttpClient())
</p>
<p>
{
</p>
<p>
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
</p>
<p>
}
</p>
<p>
```
</p>
<p>
然后這樣把輸出寫到Azure中:
</p>
<p>
```java
</p>
<p>
var blockId = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
</p>
<p>
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
</p>
<p>
{
</p>
<p>
stream.Position = 0;
</p>
<p>
await blockBlob.PutBlockAsync(blockId, stream, md5, access, options, context);
</p>
<p>
}
</p>
<p>
```
</p>
<p>
project.json文件能從任何機器上用源碼構建,而不需要指定平臺,所以這應用的代碼能運行于Windows,OS/X和Linux,也就是說能把它打包到Docker容器中。
</p>
<p>
容器通過Dockerfile來定義,這個文件中包含了構建和運行鏡像的所有程序。通常來說,需要創建一個目錄用于容器的定義,這個目錄包含了Dockerfile和所有用到的文件,例如Sixeyed.Uptimer的代碼目錄。
</p>
<p>
<img alt=".NET開發者啟程Docker之路" src="https://simg.open-open.com/show/b875efe12470905b7941fd179a7909bf.gif" width="670" height="359" />
</p>
<p>
構建這個容器后,會得到一個包含編譯后應用的鏡像。所以在容器的定義中,需要告訴Docker去使用.NET Core基鏡像,并且把應用的代碼復制進去。Dockerfile的語法是自解釋的, <a href="/misc/goto?guid=4958977246128699270" target="_blank">整個流程</a> 只需要7行代碼 :
</p>
<p>
```sh
</p>
<p>
FROM sixeyed/coreclr-base:1.0.0-rc1-final
</p>
<p>
MAINTAINER Elton Stoneman < <a href="/misc/goto?guid=4958977246223483451" target="_blank">elton@sixeyed.com</a> >
</p>
<h2>
deploy the app code
</h2>
<p>
COPY /Sixeyed.Uptimer /opt/sixeyed-uptimer
</p>
<p>
```
</p>
<p>
Docker剛剛構建鏡像時,只有一個裝有.NET Core的Ubuntu Server鏡像,應用的代碼文件都復制進容器中了,但是還沒有構建。要構建一個.NET Core 應用,首先要運行dnu,這會從NuGet獲取全部依賴包,可以用<code>RUN</code>指令:
</p>
<p>
```sh
</p>
<p>
WORKDIR opt/sixeyed-uptimer
</p>
<p>
RUN dnu restore
</p>
<p>
```
</p>
<p>
<code>WORKDIR</code>指令設置了當前目錄,所以dnu命令會讀取應用目錄下的project.json文件去獲取依賴。
</p>
<p>
這時,鏡像中已經具備了所有需要的條件,所以在Dockerfile最后的部分,它告訴容器要運行什么。使用ENV指令添加了DNX的路徑到環境變量中,ENTRYPOINT指定當容器開始運行時,會執行dnx命令。
</p>
<p>
```sh
</p>
<p>
ENV PATH /root/.dnx/runtimes/dnx-coreclr-linux-x64.1.0.0-rc1-final/bin:$PATH
</p>
<p>
ENTRYPOINT [“dnx”, “run”]
</p>
<p>
```
</p>
<p>
上邊就是定義這個容器的方法,你可以在本地構建,也可以我在Docker Hub上發布的版本<code>sixeyed/coreclr-uptimer</code>:
</p>
sh
docker pull sixeyed/coreclr-uptimer
這個.NET應用需要兩個參數,一個是需要ping的URL,一個是ping的頻率。它還需要配置Azure Storage的賬號用于存放ping的結果,這個配置會讀取環境變量STORAGE_CONNECTION_STRING。
連接字符串是可變的,不能把它放到鏡像構建過程中。為了在運行容器時能傳這個進去,可以使用環境變量或者一個獨立的文件。
運行了這個容器的一個實例,它會每10秒ping 我的博客,并且把結果保存起來:
shdocker run -e STORAGE_CONNECTION_STRING=’connection-string’ sixeyed/coreclr-uptimer https://blog.sixeyed.com 00:00:10
可以在任何Docker主機運行這個命令,無論是一個開發筆記本,還是虛擬機,或者是云上的容器,無論宿主機的系統是什么,它都會運行同樣的代碼。
Docker的架構方案
上邊介紹的應用只是小兒科,那么到底它有什么用呢?把網站的響應時間記錄下來或許并不是很有用,但是這個項目涉及到一個實際問題,這個問題是我們一直不滿意傳統方式的地方,而在Docker下可以有更好的實現。
想象一下這個場景,把一組REST接口開放給客戶端,其中很多都面臨很大的業務壓力,所以我們希望能每幾秒就ping一下這些接口,來實時檢查接口是否出現了問題。市場上的產品沒有周期性的ping,所以我們寫了自己的工具:
這些任務中的代碼與本文的一樣,但是如果你寫了一個龐大的應用來處理多個URL,那就會添加了很多不必要的復雜性。
需要存儲每個URL,需要一個調度器,需要管理多個并發的任務,需要一個機制來檢測這個應用是否失效....一下子,這個監控的組件就變得大而復雜了,越是穩定,那它就會越龐大復雜。
在Docker的方案中,實現這些就簡單得多了。我把核心的代碼放到一個.NET 應用中,只實現了ping單個URL和記錄返回值 ,這就是全部了,最多就只有100行代碼,而其他需求就可以用其他適當的技術來滿足。
為了ping多個URL,可以啟動多個實例,而Docker會幫你管理資源。而且如果我們用的是云服務,便可以根據需要隨時擴容或縮容,Mesos和Kubernetes提供了集成Docker的管理層。
這段示例代碼啟動了多個后臺實例,每個實例監控不同的域名,會每10秒ping a.com一次,每20秒ping b.com一次,每30秒ping c.com一次。
```sh
</p>
<p>
docker run -i -d –env-file /etc/azure-env.list sixeyed/coreclr-uptimer <a href="/misc/goto?guid=4958977246299322999" target="_blank">http://a.com</a> 00:00:10
</p>
<p>
docker run -i -d –env-file /etc/azure-env.list sixeyed/coreclr-uptimer <a href="/misc/goto?guid=4958977246389133273" target="_blank">http://b.com</a> 00:00:20
</p>
<p>
docker run -i -d –env-file /etc/azure-env.list sixeyed/coreclr-uptimer <a href="/misc/goto?guid=4958977246476636767" target="_blank">http://c.com</a> 00:00:30
</p>
<p>
```
</p>
<p>
–env-file參數告訴Docker去指定路徑下找到連接字符串,這樣就可以確保那些文件的安全。-i和-d參數告訴Docker在后臺運行,并且保持輸入通道的打開。
</p>
<p>
這樣監控單個站點的功能很簡單,也應該保持簡單。而需要監控的站點越多,就需要往這個腳本文件添加越多行。但是每個容器占用的資源是非常少的。
</p>
<p>
為了看到它能怎樣擴容,我們在Azure上啟動了一個虛擬機,使用了庫里的Ubuntu Server鏡像,上邊已經安裝好了最新的Docker,用了一個D1-spec設備,它主要的配置是單核和3.5GB內存。
</p>
<p>
然后我們拉取了coreclr-uptimer鏡像,并運行一個腳本監控50個互聯網上最受歡迎的網站。這些容器幾秒就啟動了,但是它用了幾分鐘來運行dnx來構建應用。
</p>
<p>
當這些容器都準備好了,我們同時監控了50個域名 ,每隔10到30秒ping一次,這時機器資源只占了10-20%。
</p>
<p>
<img alt=".NET開發者啟程Docker之路" src="https://simg.open-open.com/show/eb553004a266659107bd887a88b77de3.png" width="670" height="380" />
</p>
<p>
這是容器的一個完美用例,當我們有很多獨立的任務要運行時,可以使用這種架構,在同一個節點上運行數百個容器也不會占用太多資源。
</p>
<h2>
部署 .NET Core 應用
</h2>
<p>
你可以在Mac和Linux上構建和運行.NET應用,但是你也要在這些平臺上開發,Visual Studio Code是一個Visual Studio的精簡版,可以使用.NET Core的項目結構,并且提供了多種語言的語法高亮,如Node和Dockerfile:
</p>
<p>
<img alt=".NET開發者啟程Docker之路" src="https://simg.open-open.com/show/67387744b00795f2ef47b483a68fbeb1.png" width="669" height="420" />
</p>
<p>
你甚至可以使用標準的本文編輯器來寫.NET Core代碼 , <a href="/misc/goto?guid=4958866325544103262" target="_blank">OmniSharp</a> 項目添加了.NET項目的格式化特性,還有很受歡迎的跨平臺編輯器Sublime Text:
</p>
<p>
<img alt=".NET開發者啟程Docker之路" src="https://simg.open-open.com/show/a0da0f390225220a5232a42987175f57.png" width="672" height="418" />
</p>
<p>
但是這些項目都還在初期階段,如果你在Windows上用Visual Studio安裝了ASP.NET 5 ,你可以用VS來構建調試.NET Core的代碼。
</p>
<p>
Visual Studio 2015可以使用project.json文件來加載源碼到項目中,你能用上所有智能提示,調試和類導航,但是目前NuGet包管理器還不能用在上邊,所以你可以這樣嘗試把標準的.NET添加到一個.NET Core項目,但是會得到一個類似這樣的提示:
</p>
sh
NU1002: The dependency CommonServiceLocator 1.3.0 in project Sixeyed.Uptimer does not support framework DNXCore,Version=v5.0.
所以完整版Visual Studio還是我更常用的.NET Core應用開發IDE,其他替代版雖然輕量,但是功能還不夠完善。
Visual Studio 2015能讓.NET Core應用程序開發起來像.NET原生的應用那樣,設置全都是類似的,在項目的屬性頁,你可以配置開啟調試模式,并且可以使用跨平臺特性來支持DNX運行。
未來的Docker and .NET
Windows Server 2016很快就要發布了,它不需要虛擬機層就能直接支持Docker容器,所以sixeyed/coreclr-uptimer鏡像將能運行在原生的Windows上。
相對Linux基礎鏡像來說,在容器中能運行的Windows系統版本是Nano Server,這個系統也是基于模塊化架構的,目標是要構造非常小的鏡像(目前不到200MB,比1.5GB的Windows Server Core要小得多,但仍然比僅有44MB的Ubuntu鏡像要大很多),所以, .NET Core將仍會引入整個框架,即使容器已經能滿足了。
我們期待的是屆時是否能像這樣子定義一個鏡像:
shFROM windowsnanoserver.
Dockerfile現在還不能兼容微軟的鏡像定義,微軟現在在用PowerShell和Desired State配置。同時,Windows Nano Server如果能免費使用,那對基于Windows的鏡像被發布到Docker Hub上來說也是十分便利。
但是如果Dockerfile格式并不能兼容Windows Server,對于把開源社區上的Dockerfile轉換成PowerShell格式的翻譯器項目來說,那將是一個很好的發展機會。
應用容器正在改變軟件的設計、構建和發布方式,現在對于.NET項目來說也該好好利用起來了。未來勢必是Docker的時代,希云,作為Docker私有云領導者!利用Docker技術提供更好的云計算產品和服務。cSphere1.0版本已發布,正式商用!歡迎來電咨詢:400-686-156
如了解更多docker相關知識,請觀看培訓視頻: https://csphere.cn/training !
</div>來自: http://dockone.io/article/949