1. 同場加映: 用 Synology NAS 的 Docker 環境,執行 .NET Core CLR Console App

    Docker上一篇講完我落落長的研究過程後,這篇補上昨天想寫最後卻沒加進去的內容,就是一樣的動作改用我自己的 NAS 所提供的 Docker 環境來做 (官網)。試過之後只有一個感想... 果然買現成的實在輕鬆太多了 XD,如果不是很在意效能,只是想有個環境驗證看看,想避開整套 Linux 從無到有的 setup 過程的人可以試看看! 廢話上一篇都講過了,直接進入主題.. 這步驟跟昨天的比起來,實在是簡單太多了,這篇改一改就變成葉佩雯了,以後寫 ASPNET5 的人應該都去買台 NAS 才對... 不知以後會不會有 Visual Studio + NAS 同捆包? XD 以下是 step by step 的步驟:

    開發環境準備: Core CLR 版 "Hello World !"

    1. 開啟 Visual Studio 2015, 新增專案。這邊要留意的是專案類型,不知 RTM 後會不會改.. 我是找 Visual C# / Web / Console Application (Package) 才找到的, Console 歸類在 Web 下是有點怪.. 這邊的專案才是支援 Core CLR 的版本。建立名稱為 "HelloCoreCLR" 的新專案

    2. 左上角 runtime 切到 DNX Core 5.0,補上一行印出訊息的 Code,按下 Ctrl-F5 執行

    3. 專案的設定頁面,記得勾選 "Produce outputs on build", 才看的到編譯好的輸出檔案.. 設定完之後存檔,BUILD,到 solution / artifacts / bin / 下可以看到編譯好的檔案 編譯後的輸出,目錄結構跟過去不大一樣:
       

    事前準備: NAS + Docker

    1. NAS 安裝 Docker 套件。我只有 Synology 的,Q 牌的用戶就抱歉了~ 裝這個套件:

    2. 到 Docker / Registry 搜尋 image, keyword: microsoft/aspnet, 我是指定 tag: 1.0.0-beta8-coreclr
    3. image大小約 350mb, 完成後 DSM 會通知,到這邊準備動作就完成了

       

    佈署與執行

    1. Launch Container, 這動作等同於 docker run 這個指令。選取剛才下載的 image, 上方的 "launch" 按下去之後就有精靈引導你設定。
    2. Step 1. Container Name: NetCoreCLR

    3. Step 2, 資源限制跳過

    4. 按下 "Advanced Settings", 加掛目錄到 container 內,等等可以簡化把檔案丟進去的過程。把 NAS 的 /docker/netcore 目錄,掛載到 container 內的 /home 目錄下,取消 ReadOnly 的選項。

      完成後按下 Apply 完成設定

      最後記得,把剛才 Visual Studio 2015 編譯出來的檔案,COPY 到 NAS 的 /docker/netcore 目錄下。
    5. 完成之後,Docker Container 清單應該就會多一項 NetCoreCLR, 右邊開關打開就可以啟動這個 container 了。

    6. 選取這個 container, 按上方的 "details", 可以看到這個 container 的運作情況,切到最後一個 tab, create new termainal, 進入終端機模式

    7. 好,到這邊之後,剩下的就跟昨天那篇講得一模一樣了,先切換工作目錄到 /home/dnxcore50: 確認一下 .NET Core 版本:

    8. 第一次執行,用 dnu restore 確認是否還有相依的 package 需要下載:

    9. 準備就緒,可以執行了! dnx HelloCoreCLR.dll

        果然 NAS 包裝過的 Docker 簡單好用的多,省了很多腦細胞... 最後補張圖,大家猜猜這樣的 .NET Core CLR 環境需要吃多少記憶體? 很省啦,整個 container 才 6M ..

    看完別太興奮,馬上衝去買 NAS ...,如果真的是為了 Docker 想採購的話,務必先看一下支援清單.. 以 Synology 的來說,只有部分機種 (都是 intel cpu 為主) 才支援。詳細清單可以參考這裡:   https://www.synology.com/zh-tw/dsm/app_packages/Docker

    適用機種

    16-系列 : RS2416RP+, RS2416+, RS18016xs+
    15-系列 : RC18015xs+, DS3615xs, DS2415+, DS1815+, DS1515+, RS815RP+, RS815+, DS415+
    14-系列 : RS3614xs+, RS3614xs, RS3614RPxs, RS2414RP+, RS2414+, RS814RP+, RS814+
    13-系列 : DS2413+, RS3413xs+, RS10613xs+, DS1813+, DS1513+, DS713+
    12-系列 : DS3612xs, RS3412xs, RS3412RPxs, RS2212RP+, RS2212+, DS1812+, DS1512+, RS812RP+, RS812+, DS412+, DS712+
    11-系列 : DS3611xs, DS2411+, RS3411xs, RS3411RPxs, RS2211RP+, RS2211+, DS1511+, DS411+II, DS411+
    10-系列 : DS1010+, RS810RP+, RS810+, DS710+

    2015/11/01 .NET .Net Core Docker

  2. 在 Docker 上面執行 .NET (CoreCLR) Console App

    [caption id="attachment_411" align="alignleft" width="334"] from: blog.docker.com[/caption] Microsoft 官方宣布能在 Linux 上面運行 .NET (v5) 應用程式.. 其實這已經不是什麼新聞了,去年 Microsoft TechEd 2014 North America 就正式的發布這個消息了,不過因為種種原因,去年看到這新聞的時候,只停留在 "看看" 的階段而已,直到現在時機成熟了才開始動手研究。一來是因為官方的開發工具 Visual Studio 2015 已經 RTM 了 (這麼大的軟體,實在不大想安裝 preview 版本).. 二來 Docker 用的很高興,NAS / Ubuntu Server 也都已經準備好,執行環境我也上手了.. 萬事俱備只欠東風,於是今天就趁周末,把 .NET Core 版的 "Hello World" 丟到 Docker 裡面執行的任務給搞定了 :D Microsoft 自從新任 CEO Satya Nadella 上任後,宣布了一連串改變,我覺得對未來影響最大的就是 .NET Open Source + Support Linux 這件事了。我認為這決策帶來的影響,遠遠大於 ASP.NET5 本身開發技術及架構上的改變 (例如 MVC6.. 動態編譯.. Dependency Injection 等等)。因為前者影響的是整個 .NET 生態的改變,可能會影響到將來大型應用部屬的架構決策,而後者影響的只是開發團隊,完全是不同層級的問題..。如果你的職責是 system architect, 那千萬別忽略這個改變.. 以後 "混搭" 風格的架構一定會越來越盛行,不管是在 windows server 上面,或是在 Linux server 上都是。 在這兩年內,另一個很快竄起的技術: Docker, 也是另一個關鍵。Docker 這才出來兩年就紅翻天的 Container 技術,這種東西實在太對 architect 的胃口了。過去 VM 的技術沒甚麼不好,不過最大的問題就是: 充分虛擬化之後,帶來的副作用是多一堆 "虛擬" 的機器要管理... 每個 VM 裡面都要裝一套 OS,不論是 Linux or Windows,都要花力氣去維護,要執行這些 OS 也要花費運算資源... 舉個慘痛的經歷,我就碰過在同一台實體機器上,上面的 10 台 VM 都開始掃毒,那狀況真的只能用 Orz 來形容... 上面的 APP 都還沒認真在跑,系統就被 OS + AntiVirus 給拖垮了... Docker 只虛擬化 Application 的執行環境,巧妙的避開了 VM 過度使用帶來的副作用... 舉例來說,如果你的應用規模不大,只要一台最低規格的 VM 就跑得動的話,你會為了架構考量把他分裝在三台 VM,實作三層式架構嗎? 應該沒人會這麼幹吧? 除非你要展示應用架構,或是可預見的將來會需要 scale out, 不然這樣搞只是自找麻煩而已。 但是現在用了 Docker 你就可以盡可能的採用你認為最理想的架構,分成幾個獨立的 container 可以維持架構上的正確性,同時也不必擔心架構帶來效能的折損。 因為這個原因,了解這種跨界的應用,對我來說遠比了解 ASP.NET5 本身 coding 帶來的改變還重要的多。裝了 Visual Studio 2015 之後,第一件事不是先寫看看 MVC6,而是先弄了個 Hello World, 試看看該怎樣丟到 Linux 上面跑.. 為了這件事,前置作業就花了快一個月... 有在 follow 我的 facebook 或是訂閱 blog 的就知道,之前我都在研究 Docker ,包括在 NAS 上用我理想的架構,來架設我自己的 blog.. 也弄了台差點要被扔掉的舊筆電,架了台研究用的 Ubuntu Server .. 接下來主角要上場了,就是 .NET CoreCLR !  

    進入主題: .NET Core CLR 體驗

    終於進入主題了 =_= ,要是各位跟我一樣長期使用 Microsoft Solution 的話,應該會踢到很多塊鐵板吧? 分享一下我如何讓自己跨進來的步驟,既然目標都很清楚了 (以 docker 為主要的執行環境),所以我自己計畫的步驟是:
      1. 先用最快的時間了解 Docker 是什麼? 可以怎麼應用? 如何實作? 我挑了最無腦的作法,從 NAS 上的 Docker 套件.. 我自己有台 Synology DS-412+ , 正好支援 Docker, 於是我自己挑了個實作的目標,就是把我原本的 Blog 搬回家,打算用 Docker 架設 WordPress。部落格再怎麼少人看,至少也是我的門面吧 XD,因此有些事情是省不掉的。例如我上一篇文章就提到 reverse proxy 把對外的 port / url 歸位這件事,還有整個 application 的維護及備份等等,都是不能忽略的部分。如果你不拿一個實際的應用來練習,那你做的只會是 POC (Prove Of Concept) 層級的事情,很容易就淪為 "啊! 成功了,會跑了" 之後就告一段落,很多實繼運作要顧慮的部分就不見了。想參考這段過程的,可以看我這篇文章。順利搬完部落格,至少讓我親身體驗 Docker 的應用方式。過去我是真的養了台 PC 架 windows server, 在裝個 VM 跑 windows server 來架站的,現在可以在 NAS 裡用同樣等級的架構作一樣的事,而運算資源只有以前的 1/4 不到 (NAS: atom 2cores, 1GB ram.. PC server Q9400 4cores, 8GB ram).. 我想優勢很明顯了.. 體驗過這一段,以後要如何把 Docker 應用在整體的系統架構內,心裡就有個譜了。
      2. 親手架設實際的運作環境 NAS 的 Docker 使用實在太無腦了,害我以為裝實體 server 也是滑鼠按兩下就結束,結果真的是動手就吃鱉了 @@,過程我也不多說了,上一篇文章有紀載 :D自己動手架設的好處是,你會親自走過安裝過程的所有細節,將來要 upgrade 或是 change configuration 就不會不知如何下手了。做到這步我才發現,NAS 廠商其實還蠻強的 XD,我自己用指令來管理 Docker 就已經一個頭兩個大了,找了一些 Docker Web UI 來用 (docker-ui), 發現也很難用 @@,但是 Synology / Qnap 自己的 Docker 管理介面就做得不錯...實際走過這段 setup ubuntu, configuration docker environment 之後,以後實際佈署時會碰到什麼問題,一樣心裡大概就有個譜了。
      3. 進入主題,熟悉 .NET CoreCLR 的運用 (DNVM, DNU, DNX) 方式 這是這次的目的,我想先解決我最不熟悉的部分,往後的 Coding 我反而不擔心,好歹我有 2x 年的 coding 經驗,這應該難不倒我的... 所以我直接跳過 Coding, 拿了最基本的 Console Application, 寫了 Hello World, 以成功在 Docker 環境內執行為主要目標。這步驟搞定的話,後面的路就順了,就可以正式進入研究 ASP.NET5 的階段了這段就沒有連結可以看了 XD,對,沒錯,就是這篇文章。請繼續看下去!
     

    .NET Core CLR 的名詞定義

    開始之前,先惡補一下,這次 .NET CoreCLR 的底層改變幅度很大, 如果只打算裝起來玩一玩按一按就搞懂的話,應該沒那麼簡單。幾個一定要知道的 keyword 我先筆記一下。推薦兩篇寫得不錯的介紹:   DNVM, DNX, and DNU - Understanding the ASP.NET 5 Runtime Options Understanding the relationship between the .NET Version Manager (DNVM), the .NET Execution Environment (DNX) and .NET Development Utilities (DNU) is fundamental to developing with ASP.NET 5. In this post we will look at installing and using DNVM, DNX, and DNU on to work with ASP.NET from the command   DNN: What are DNX, DNVM, DNU and other ASP.NET 5 components? In a previous post on the upcoming ASP.NET ‘vNext’ release, I covered all of the terminology around ‘Project K’ – which is what it was known as in early betas.   Things have moved along a lot since then, and with that has come a change in terminology as the release gets closer.  The trend of calling everything ‘K’ is no more – sadly in my opinion as I liked the quirkiness of it. This post will cover the new terminology, relate it back to the Project K era terminology and relate that back to existing ASP.NET (1-4) concepts where possible.  Again, this is intended as a primer for the existing ASP.NET developer to learn what is coming next and get themselves familiar with the changes.   底下就我自己整理的內容了。幾個名詞還是要先定義一下,否則看來看去腦袋會打結:   Core CLR ( .NET Core  Common Language Runtime ) CLR 就是 Common Language Runtime, 套用 Java 的說法其實就是 VM,也就是能執行 .NET IL 的環境。包含 VM,JIT,還有 BCL (Base Class Library) 都算在內。而過去常講的 .NET Framework 就是所謂的 CLR,在 windows 平台上有完整的功能。Core CLR 則是重新包裝的可跨平台版本,包括 Linux / MacOS. Core CLR 有其他特性,向是 CLR 可以個別佈署,同時裡面的 BCL 都只需要佈署你用的到的部分即可。   DNX ( .NET Execution Environment ) .NET 的執行環境,可以執行及啟動 .NET app / ASP.NET 的命令列指令。一樣套用 Java 的說法,就像是對應到 java.exe 的東西,

      DNVM ( .NET Version Manager ) .NET 的版本維護工具,類似 APT-GET 這樣的命令列指令,用來維護及更新 DNX. DNX 各種版本的維護工具就叫做 DNVM,可別看到 VM 就以為是 Virtual Machine, 他是各種 DNX 的 Version Manager.

      DNU ( .NET Utilities ) .NET 開發人員維護工具,有點像是 Java 的 Javac.exe, 可以進行 build, 下載或是更新相依的 NuGet 套件。

    這幾個指令的關係搞清楚之後,今天的主角終於... 終於可以上場了。  

    將 .NET Core APP 放進 Docker 執行

    第一步,首先你要有個 Docker 執行環境可以用.. 其實這是小事,你有 NAS 的話,規格別太差又剛好有支援 Docker 那你就賺到了,直接用就好。不然可以像我一樣自己裝一台 Linux, 或是用 VM 都可以。如果你甚麼都沒有,可以考慮看看用 Docker Toolbox (windows 版), 他包含了 windows 上的 docker client, 也包含了 VirtualBox 虛擬環境,還有一個包裝好的 Boot2Docker, 專為執行 Docker 而生的 Linux 開機 image. 我畢竟不是用 Linux 長大的那群人,對我而言能越簡單解決環境問題越好。因此我並不打算直接在 Linux 上安裝 DNX .. 我只想搞定 Docker 後,找 Microsoft 官方提供的 Container Image 來用。我用的是這個: ASP.NET 5 Preview Docker Image ,抓下來後就有現成的執行環境.. 其他動作通通都省了,真的是適合我用的懶人包 XD 不過看了幾篇怎麼使用這 container image 的文章 (官方: Running ASP.NET 5 Applications in Linux Containers with Docker),發現跟我要的不大一樣。官方都是教你要寫 DockerFile, 把自己的 application 包裝成一個新的 image, 然後丟進去 docker 執行.. 這樣是沒錯啦,不過我只想把我的 app 丟進某個現成的環境來測試,還不想搞到自己包 image .. 於是,花了點時間,總算找到切入點。多虧這個念頭,讓我沒有一步一步照著官方文件來走,也意外地讓我多了解了 Docker 的運作機制。Docker 提供一個跟外界隔離的應用程式的執行環境,但是裡面能執行的不一定只有我的 application 啊,也不一定只能執行一個。我可以多執行一個 shell, 對我來說就好像開了一個 VM 然後 ssh 連進去一樣,這就是我現階段想要的,將來有正式的佈署需求再來照官方文件進行就好。 環境搞定後,確定可以正確操作 DNVM, DNU, DNX 等指令後, 就幾乎達成我的目標了。方向想通了,剩下的舊是查察指令文件,找出正確的操作步驟就可以了。我最後整理下來的執行步驟是: 在 Docker Host:
    1. 下載官方 image sudo docker pull microsoft/aspnet:1.0.0-beta8-coreclr
    2. 啟動 container (daemon mode, 加上 -d) sudo docker run -d --restart always microsoft/aspnet
    3. 查詢 container id: sudo docker ps -a

    4. 在 container 內執行 bash, 同時進入互動模式直到 bash 結束為止: sudo docker exec -t -i 93462d92e941 /bin/bash

      這時 command prompt 已經變了,由原本的 chicken@localhost:~$ 變成 root@93462d92e941:/# , 代表 shell 已經啟動成功,且順利進去 conainer 內了,接下來我就可以把它當作 VM 開始大玩特玩..

    5. 打開 visual studio 2015, 開 console app project, 寫一段不入流的 code, 印出 "Hello! .Net Core! " ..

    6. 完成後,想辦法把檔案丟到 container 裡面... docker 提供 cp 這個指令,可以跨過 docker host / container 的界線 copy 檔案。細節我就跳過,總之 (5) 編譯出來的檔案,我放在 container 內的 /home/ConsoleApp1/ 目錄下:

    7. 接下來就是主要步驟了,先用 dnvm list 確認你能用的 dnx 版本,有需要可以用 dnvm install 來安裝,或是用 dnvm upgrade 來升級.. 我這次要用的是 coreclr x64 的版本:

    8. 進入 dnxcore50 的目錄,用 dnu restore 指令,確認所有相依的 package 都已存在 (若沒有的話會自動到 NuGet 去抓回來)

    9. 噹噹! 萬事俱備,最後就是執行了! 用 dnx 來跑我的 console application: 為了避免有人誤會,我是用 windows command prompt 充數,順手把 OS information 印出來以資證明..
      哇哈哈,終於成功了。看到我寫的 C# code 的控制範圍能擴大到 Ubuntu Server 上,那個成就感實在是不可言喻 :D  這次碰到最大的困難,是查到的指令都是講 ASP.NET 如何在 docker 上執行,可是我只是要 run console app 啊,沒找到文章把這整傳邏輯跟做法整理再一起,只好自己摸索...。好在當年學生時代有好好的學 unix (當年用 solaris ... 自己有用過一陣子的 linux.. ), 基本觀念跟 shell script 都還有,硬是闖出一條路。 就為了這行 "Hello .Net Core!", 花了我一個上午... 不過至少達到我的目標,把最基本的命令列執行成功了!  能夠親手在 Linux 上操作 dnvm, dnu, dnx 這幾個核心指令把程式跑起來,也算值回票價了 :D 這些步驟整理起來就當作我的筆記。應該有不少人跟我一樣,不熟 Linux 又想跨入這個領域的吧? 有需要的盡管取用,也歡迎分享這篇文章~    

    2015/10/31 .NET .Net Core C# Docker 技術隨筆

  3. 終於搞定 Ubuntu Server 15.10 @@

    原本只是想在 NAS 上簡單玩玩 Docker, 為了接下來的 ASP.NET 5 做準備.. 不過實在太好用,還沒開始做正事 (ASP.NET 5), 就先把原本的 BLOG 從 GoDaddy 的 hosting 搬回來放 NAS 了,順手也架了 reverse proxy ... 現在 NAS Docker 有正式用途了, 而 NAS 的運算能力又很有限 (我的是 DS-412+, CPU 只是 atom d2700, 雙核而已, 1GB RAM), 裝沒幾個就跑開始擔心了,於是就開始想另外搞一個可以隨便玩得 docker engine 環境.. 其實 PC 上弄個 VM 是很容易啦,windows 10 內建的 hyper-v 根本就不用花甚麼功夫, 不過我想弄個像 NAS 這樣省電不大需要去管它, 24hr 開著隨時都可以用.. 就把腦筋動到古董要丟掉的筆電.. 我老姊正好有台筆電要丟掉,找我幫他先把硬碟資料清掉她才敢丟...一切都來的太巧了! 於是這台就被我拿來大整修一番...       這台的規格是 Acer Aspire 5742Z:

    • 15.6" LCD (已經裂開沒辦法顯示了)
    • 320GB HDD
    • 4G RAM
    • Intel Pentium P6200
      看來正和我需要 :D,螢幕我也不需要,直接拆掉可以放桌上當鍵盤,還不用擔心螢幕翻起來遮到我真正的螢幕... 用舊 NB 當 SERVER 好處還蠻多的,一來省電,二來快報廢的電池其實當成內建的 UPS 也不錯,夠撐幾分鐘關機就好了。 不過開始安裝 Ubuntu, 就是噩夢的開始... 平常我連使用 windows 都常常開 dos command prompt 下指令,命令列對我來說不是甚麼問題,不過 linux 從研究所畢業之後就很少在碰了,這次光安裝就踢到鐵板.. 從一開始,抓錯 USB 開機工具,做出來的開機 USB 好像只支援 net install, 硬要我設定網路才能繼續... 無奈網路卡又抓不到, 查了一堆說明又說這步驟可以跳過, 後來才發現另一套 USB 開機工具就沒這問題.. 再來,不知碰到啥問題,我抓了 12.04 LTS, 14.04 LTS, CentOS 都試過了,裝到一半跟我講 CDROM 內容不對,不過我重下載了幾次,也比對過 MD5 hash 都無誤.. 想說換 desktop 板好了,desktop 版應該會比較親民吧? 結果開到桌面就又不動了 @@ 最後把 wireless lan 卡直接拔掉,換 15.10 版,終於裝起來 =_= 果然隔行如隔山,裝好後光是要設定 SSH server, 要改固定 IP, 設定 samba 開網路分享... 都花了一些時間, 這部分就還好, 只是花點時間查指令,照著打進去就搞定... 現在終於把基本環境搞定了,特此留念 :D 過程解決的問題對於熟 linux 的人來說應該都是小兒科吧? 我就不貼了,自己記載我私人的 onenote 就好.. 貼幾張照片作紀念:   IMG_8271 (Canon PowerShot S110) (Medium) 我用雙螢幕,旁邊的螢幕是轉成直的,方便看文件。因為有現成的 D-SUB,想說裝一裝就可以改用 SSH ,就懶得把他轉正了,結果搞了半天 =_=         IMG_8272 (Canon PowerShot S110) (Medium) 這張照片可以看到,螢幕已經被我用暴力拆掉了 XD。 這次用 Microsoft 的隨身碟來裝 Linux ... 裝起來的 Linux 又要跑 Microsoft 的 ASP.NET 5 ... 你搞得我好亂啊..   至於 ASP.NET 5 ... 哈哈,再過幾個禮拜再說 XD

    2015/10/24 Docker 技術隨筆 有的沒的

  4. Docker 初體驗 - Synology DSM 上面架設 WordPress / Redmine / Reverse Proxy

    img_56358907f0c81前言: 先讓我講一點前情提要 XD,想看安裝步驟的請自己跳到後面... 在買這台 NAS 之前 (Synology DS-412+), 我是自己在家裡弄了台 PC, 裝了一堆硬碟充當個人用 file server, 同時順便在裡面架了自己常用的網站,包含這部落格的前身 (BlogEngine),還有自己用的 Visual SVN, 另外也架了 Linux VM 裝 Redmine 等等其他的東西.. 後來 PC 開始不聽話了,開始三不五時當機,心一橫,兩年多前就買了台 4Bays NAS 把 Server 換掉,頓時輕鬆許多... 只是 NAS 不比 Server, 慣用 Windows Server 的我一時找不到替代品,這些服務就一個一個搬家了。其中最重要的 BLOG,就搬到 GoDaddy 的免費 web hosting (有 IIS),繼續在上面掛著.. 用過 Synology NAS 的大概都知道,它的特色就是 DSM 很好用,也有提供很多 Package, 讓 NAS 加裝軟體就像手機逛 App Store 一樣簡單... 不過,DSM 的裝機量不比手機,很多套件要是沒有經過 Synology 包裝,要安裝就是件麻煩事了。就算有 Synology 官方的打包套件,更新或是維護也不比這些軟體的官方來的快。尤其是從小到大都是抱著 Microsoft 大腿的我更是不適應 @@ 直到幾個月前 Synology Release DSM 5.2, 正式在裡面搭載了 Docker ! 這真是天大的好消息..  :D Docker 是個好物,沒用過或沒聽過的可以參考這裡:  What is docker ? 簡單的說,是另一種虛擬化的應用。他不像 VM 是將硬體虛擬化,所以不用在上面安裝 OS,只虛擬化執行環境... 啟動速度很快,兩三秒就可以啟動了,少掉 OS 這層,整個就很輕量化,架在 Docker 裡,跟原生地執行環境速度,CPU / RAM 資源的使用沒甚麼明顯差別...  我覺得 DSM 加入 Docker 真的是 Synology 最聰明的決定了,比 Q 牌直接導入正統的虛擬化技術還實用... 畢竟個人用的 NAS 都不會有太強大的運算資源,為了這樣去拚硬體配備就有點本末倒置了。Docker 這種輕量化的虛擬化技術,正好補足了這需求 講這麼多幹嘛? 因為 Docker 實在太熱門了,所以在 Docker Hub 上大概所有熱門的應用都有人包好了,煩惱的是同樣的東西太多人包了,有時還真不知該怎麼選 @@.. 我是有官方版的就盡量挑官方的來用。因為社群的差異太大了,因此從 Docker 上可以找到的選擇,遠比從 Synology Package Center 找到的又多又廣泛,我就開始一個一個替換的計畫... 計畫要安裝的有好幾個,包括 WordPress, Redmine, MYSQL, WebSVN, 還有為了方便發布這些服務,還想裝個 Reverse Proxy... 不過流水帳我就省了,這次的範例我就拿 WordPress 跟 Reverse Proxy 當案例,給有需要的人參考步驟就好。


    正文開始 這次打算把在 GoDaddy 流浪兩年多的 Blog Engine 搬回家,於是花了點時間把資料從 BlogEngine 匯出,轉到 WordPress, 這邊很多文章有教你一步一步處理,我就跳過了。 在 Docker 上安裝 WordPress + MySQL, 也是小事一件,最花時間的就是... 就是下載 image ... 這我也跳過。比較特別的是,不知是 Synology 搭配的 Docker 套件問題,還是別的的問題 @@,WordPress 官方版的 image 開 DSM Docker 管理員的 terminal 就很容易會整個 container crash .. @@,不過因禍得福,意外發現有人用 Nginx 這個新興的 web server 架設的 WP image. NGINX 是啥? 是個俄國人寫的 web server, 世界上的佔有率大概 1x % 吧~ 它的特色就是又小又快..  Apache 走的是可以外掛很多 module 的模式,NGINX 則是走把你要的東西編譯進去的模式,跑起來又快又省記憶體,正適合我的需要。換了這個 image, 前面講的 crash 怪問題就不藥而癒.. 算是因禍得福 :D 做個筆記,我用的 image 分別是這兩個: wordpress image with php-fpm + nginx mysql offical image 架好後當然快樂的使用了。這時問題開始來了... 畢竟我用的是 NAS 內建的套件,我又不熟 Linux, 有很多東西就算我找到文章可以進去大改特改,我也不大敢動手,NAS 終究是拿來讓我日子過快樂一點的,不是要重回當黑手的日子... 因此太過古怪的技巧我就不想用了,我想盡量用正常一點的方式來設定,免得以後換個版本我就搞不定...   NETWORK 上圖是我現在家裡的網路架構,這時碰到的問題在於,PORT 80 早就被 NAS 內建的 apache 搶去用了。WordPress 若要用 80 PORT 就沒辦法了。DSM 也沒地方讓你把 PORT 80 放出來,二來就算放出來,我也沒辦法讓兩個以上的 container 都 mapping 到 80 PORT... 這樣要開放對外網站就顯得很棘手... 最終 WordPress 分配給他 8012 這個 port, 總不能叫所有的網友,以後要看我的文章要連這 PORT 吧? @@ 一般業界最常處理這種問題的方式,就是用 Reverse Proxy 了。顧名思義,Reverse Proxy 就是反向的 Proxy, 他是替 "外面" 的 User 到你家裏面的網站抓資料後,再丟給外面的 User, 用這樣的技巧將內部的網站發布出去。其實他的機制跟一般的 Web Proxy 是一模一樣的,只不過他服務的是外面的 User 抓內部的網站,跟正常的應用情境相反,所以叫 Reverse Proxy. Reverse Proxy 其實很多種應用,進階一點的 Load Balancer, Cache, HTTPS 發行 (在 Reverse Proxy 上加掛 SSL) 等等用途。在 Windows Server 上有 ARR (Application Request Routing) 這個 IIS 外掛可以用,也是另一種常用的 Reverse Proxy。回到我的狀況,由於唯一的 80 PORT 已經被 DSM 佔住了,所以沒得選擇,我開始尋找 DSM 內建的 Apache 上可以加掛的 proxy module... 結果得來完全不費工夫,內建就有 :D 架構跟方向都想好之後,就開始動手了... 各位現在看的到我翻新的 BLOG,可見是成功了! 以下是我的操作步驟:
    1. Synology DSM 的控制台,底下有 Web Station, 先用正常的介面,建立 virtual host, 綁到 columns.chicken-house.net 這個 hostname (80) 01 當然,其他 DNS 的設定你要自己搞定。我自己家裡用的 ROUTER 有內建個小型 DNS,加個 static record 就可以把 columns.chicken-house.net 對應到 NAS 的內部 IP,我自己要看我的 BLOG 不用繞到外面出國比賽再繞回來... 外面的 DNS 也要改一改,對到 router 的對外 IP,有固定 IP 的可以設 A record, 有 DDNS 的可以用 cname record. 改完可以測看看,這時應該會看到 Synology 自己準備的 404 page:02
    2. 接下來,就是要設定這個 virtual host 要扮演 reverse proxy, 轉向內部的 word press 網站了。其實整篇廢話這麼多,重點就這一段而已 @@用 SSH 登入 NAS,修改這個檔案: /etc/httpd/httpd-vhost.conf-user , 其中 line 15 ~ 25, 就是我加進去的指令,告訴 apache 在這個 virtual host 內,位於 / 以下的 http request, 都轉給 http://nas:8012 這個內部的 URL,也就是安裝 WordPress 的 container 的發行端點
      NameVirtualHost *:80
      <VirtualHost *:80>
      ServerName *
      DocumentRoot /var/services/web
      </VirtualHost>
      
      <VirtualHost *:80>
      
      ServerName columns.chicken-house.net
      DocumentRoot "/var/services/web/columns"
      ErrorDocument 403 "/webdefault/error.html"
      ErrorDocument 404 "/webdefault/error.html"
      ErrorDocument 500 "/webdefault/error.html"
      
        # start of (reverse proxy settings)
        ProxyPreserveHost On
        ProxyRequests     Off
      
        <Location / >
          ProxyPass http://nas:8012/
          ProxyPassReverse http://columns.chicken-house.net/
          Order allow,deny
          Allow from all
        </Location>
        # end of (reverse proxy settings)
      
      </VirtualHost>
      完成後,用這個指令 restart apache (httpd), 讓設定生效:
      httpd -k restart
      再用瀏覽器測試一下網址  http://columns.chicken-house.net, 應該就可以看到 wordpress 的內容了 ! 拿手機測試一下,關掉 wifi, 用 4G 連看看我自己的網站... 果然可以用正常的 URL 看到內容:wp_ss_20151012_0002
    3. 其實到這邊就大功告成了。不過... 請務必備份這個檔案 !!  DSM 的介面設計得太簡單好用了,所以當你回到 (1) 重新調整後,或是有第二個 docker container 也要依樣畫葫蘆發布的話,DSM 會把這個設定擋蓋掉 T_T,我就是因為這樣全部重來一次...
    OK,大功告成! 繞了一大圈,總算把我的 NAS 調教成可以擔負重要任務的 Personal Server 了,以前不覺得 NAS 跑的慢,現在開始覺得 RAM 好像值得加大一點了 :D,也許有人會問,這樣其實用 Synology 套件中心的 WordPress 好像也一樣不是嗎? 不不不,處女座的人是忍受不了這幾個問題的:
    1. 套件中心的 WordPress, 網址會多一段... 像這樣...http://columns.chicken-house.net/wordpress/......這我看了就很礙眼,更重要的是其他人透過 search engine 找到的連結,會點不進來... 身為點閱率破百萬的知名部落格 (並沒有) 怎麼能忍受這種現象...
    2. 套件中心的 WordPress 只能裝 "一份",我想架兩個 wordpress 就沒辦法分開管理了
    3. docker 將來要搬出去非常容易,匯出 container, 之後搬到別的地方,如 Azure / Amazon 都有提供 docker 執行環境了... synology 的套件就沒這優勢了...
    4. docker 的選擇多太多了,到 docker hub 裡找找,甚麼都有... 能選擇的數量遠遠高於 synology package center
    5. 有統一的 container 管理工具,mount storage, port mapping, cpu / ram resource management 等等都有現成的, 套件中心提供的就沒這樣的管理彈性: 03
      好,流水帳就記到這裡,當你還想再加上其他 container, 就依樣畫葫蘆就好。這篇其實沒甚麼重點,主要就是為了滿足 Synology NAS 用戶,能用 Docker 套件來做些正是用途的小技巧而已,歡迎分享 :D

    2015/10/13 Docker Tips 技術隨筆 有的沒的

  5. 處理大型資料的技巧 – Async / Await

    原本只是很單純的把大型檔案 (100mb 左右的 video) 放到 Azure Storage 的 BLOB 而已,結果效能一直不如預期般的理想。才又把過去的 thread 技巧搬出來用,結果又花了點時間改寫,用 async / await 的效果還更漂亮一點,於是就多了這篇文章 :D

    其實這次要處理的問題很單純,就是 WEB 要從 Azure Storage Blob 讀取大型檔案,處理前端的認證授權之後,將檔案做編碼處理後直接從 Response 輸出。主要要解決的問題是效能過於糟糕… 透過層層的處理,效能 (3.5 Mbps) 跟直接從 Azure Storage 取得檔案 (7.3 Mbps) 相比只剩一半左右.. 過程中監控過 SERVER 的 CPU,頻寬等等,看來這些都不是效能的瓶頸。

    為了簡化問題,我另外寫了個簡單的 Sample Code, 來呈現這問題。最後找出來的原因是,程式碼就是單純的跑 while loop, 不斷的把檔案內容讀進 buffer 並處理後,將 buffer 輸出。結果因為程式完全是 single thread 的處理方式,也沒有使用任何非同步的處理技巧,導致程式在讀取及處理時,輸出就暫停了,而在輸出時,讀取及處理的部份就暫停了,讓輸入及輸出的 I/O, 還有 CPU 都沒有達到滿載… 於是效能就打對折了。用時間軸表達,過程就如下圖:

    這樣的設計方式,同一時間只能做一件事。若把上圖換成各種資源的使用率,會發現不論是 DISK、NETWORK、CPU等等資源,都沒有同時間保持忙碌。換句話說好像公司請了三個員工,可是同時間只有一個人在做事一樣,這樣的工作安排是很沒效率的。要改善的方法就是讓三個員工都保持忙碌,同時還能亂中有序,能彼此協調共同完成任務。

    同樣的狀況應該很普遍吧? 不要說別人了,就連我自己都寫過很多這樣的 CODE … 光是 COPY 大型檔案,大家一定都是這樣寫的: 用個 while loop, 把來源檔讀進 buffer, buffer 滿了寫到目地檔,然後不斷重複這動作,直到整個檔案複製完成為止。這不是一模一樣的情況嗎? 只是大部份的人不會去考量如何加速這樣的動作而已…

    我先把目前的CODE簡化一下,拿掉一些不相關的部份,單純的用 Read() / Process() / Write() 三個空的 method 代表執行這三部份的工作,執行過程需要的時間,就用 Task.Delay( 100 ) 來取代。經簡過後的 Code 如下:

    經簡後的示意程式碼:

    public class Program
    {
        static Stopwatch read_timer = new Stopwatch();
        static Stopwatch proc_timer = new Stopwatch();
        static Stopwatch write_timer = new Stopwatch();
        static Stopwatch overall_timer = new Stopwatch();
        public static void Main(string[] args)
        {
            overall_timer.Start();
            for (int count = 0; count < 10; count++)
            {
                Read();
                Process();
                Write();
            }
            overall_timer.Stop();
            Console.WriteLine("Total Time (over all): {0} ms", overall_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Read Time:       {0} ms", read_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Process Time:    {0} ms", proc_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Write Time:      {0} ms", write_timer.ElapsedMilliseconds);
        }
        public static void Read()
        {
            read_timer.Start();
            Task.Delay(200).Wait();
            read_timer.Stop();
        }
        public static void Process()
        {
            proc_timer.Start();
            Task.Delay(300).Wait();
            proc_timer.Stop();
        }
        public static void Write()
        {
            write_timer.Start();
            Task.Delay(500).Wait();
            write_timer.Stop();
        }
    }
    

    程式執行結果:

    程式總共要花掉 10 秒鐘才執行完畢,由於完全沒有任何並行的處理,因此就是很簡單的 Read 花掉 2 秒,Process 花掉 3 秒,Write 則花掉 5 秒,加起來剛好就是總執行時間 10 秒。

    回顧一下,過去寫過幾篇如何善用多執行緒來解決各種效能問題的文章,其中兩篇跟這次的案例有關:

    1. MSDN Magazine 閱讀心得: Stream Pipeline, (2008/01/19)
    2. 生產者 vs 消費者 - BlockQueue 實作, (2008/10/18)
    3. 生產線模式的多執行緒應用, ([RUN! PC] 2008 十一月號, 2008/11/04)
    4. RUN!PC 精選文章 - 生產線模式的多執行緒應用, (2009/01/16)

    其實這些方法的目的都一樣,都是透過各種執行緒的操作技巧,讓一件大型工作的不同部份,能夠重疊在一起。這樣的話,整體完成的時間就能縮短。不過,隨著 .NET Framework 一直發展,C# 5.0 提供的 Syntax Sugar 也越來越精彩,到了 .NET Framework 4.5 開始提供了 Async / Await 的語法,能夠大幅簡化非同步模式的設計工作。

    非同步的程式設計,其實也是 multi-threading 的一種運用。簡單的說,它就是把要非同步執行的任務丟到另一條執行緒去執行,等到它執行結束後再回過頭來找它拿結果。只是為了這樣的一個動作,往往得寫上數十行程式碼,加上原本程式的結構被迫切的亂七八糟,過去往往非絕對必要,否則不會用這樣的模式。

    這次我的目的,其實用前面那幾篇的技巧就能解決了。不過這次實作我想換個方法,都已經 2013 了,有 Async / Await 為何要丟著不用? 這次就用新方法來試看看。先用上面的時間軸那張圖,來看看改進後的程式執行狀況,應該是什麼樣子:

    解釋一下這張圖: 橘色的部份代表是用非同步的方式呼叫的,呼叫後不會 BLOCK 原呼叫者,而是會立即 RETURN,兩邊同時進行。而圖中有個箭頭 + await, 則代表第二個非同步呼叫 Write() 的動作,會等待前一個 Write() 完成後才會繼續。

    Write() 跟下一次的 Read() 其實並無相依性,因此在開始 Write() 時,其實可以同時開始下一回的 Read(), 因此時間軸上標計的執行順序就可以被壓縮,調整一下執行的順序,馬上得到大幅的效能改進。這次要改善的,就是把 Read() + Process()Write() 重疊在一起,預期會有一倍的效能提升。

    想要瞭解 C# 的 async / await 該怎麼用,網路上的資源有很多,我習慣看官方的文件,有需要參考的可以看這幾篇:

    1. async (C# Reference)
    2. Asynchronous Programming with Async and Await (C# and Visual Basic)

    Async / Await 的細節我就不多說了,簡單的說在 method 宣告加上 async 的話,代表它的傳回值會被改成 Task<>, 而呼叫這個 method 會變成非同步的,一旦呼叫就會立刻 Return, 若需要這個 method 的執行結果,可用 await 等待,直到 method 已經執行完畢才會繼續…

    廢話不多說,過程就沒啥好說的了,直接來看改好的程式碼跟執行結果:

    改寫為非同步模式的 CODE:

    public class Program
    {
        static Stopwatch read_timer = new Stopwatch();
        static Stopwatch proc_timer = new Stopwatch();
        static Stopwatch write_timer = new Stopwatch();
        static Stopwatch overall_timer = new Stopwatch();
        public static void Main(string[] args)
        {
            overall_timer.Start();
            DoWork().Wait();
            overall_timer.Stop();
            Console.WriteLine("Total Time (over all): {0} ms", overall_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Read Time:       {0} ms", read_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Process Time:    {0} ms", proc_timer.ElapsedMilliseconds);
            Console.WriteLine("Total Write Time:      {0} ms", write_timer.ElapsedMilliseconds);
        }
        public static void Read()
        {
            read_timer.Start();
            Task.Delay(200).Wait();
            read_timer.Stop();
        }
        public static void Process()
        {
            proc_timer.Start();
            Task.Delay(300).Wait();
            proc_timer.Stop();
        }
        public static async Task Write()
        {
            write_timer.Start();
            await Task.Delay(500);
            write_timer.Stop();
        }
        private static async Task DoWork()
        {
            Task write_result = null;
            for (int count = 0; count < 10; count++)
            {
                Read();
                Process();
                if (write_result != null) await write_result;
                write_result = Write();
            }
            await write_result;
        }   
    }
    

    程式碼幾乎都沒有動,不過就是把 Write() 改寫為 Async 版本,同時在主程式 DoWork() 用 Task 形別,把 Write() 傳回的 Task 物件,保留到下一次呼叫 Write() 前,用 await 來確保上一個 Write() 已經完成。

    改寫過的版本,程式碼很簡單易懂,90% 以上的程式碼結構,都跟原本同步的版本是一樣的,大幅維持了程式碼的可讀性,完全不像過去用了多執行緒或是非同步的版本,整個結構都被切的亂七八糟。看看程式的執行結果,果然跟預期的一樣,整體執行時間大約為 5 秒。多出來的 660 ms, 就是第一次的 Read() + Process(), 跟最後一次的 Write() 是沒有重疊的,因此會多出 500 ms, 再加上一些執行的誤差及額外負擔,就是這 660ms 的來源了。

    最後,來看一下效能的改善。在我實際的案例裡,Read 是受限於 VM 與 Storage 之間的頻寬,固定為 200Mbps, 而 Process 是受限於 VM 的 CPU 效能,也是固定可控制的, 最後 Write 則是受限於 client 到 VM 之間的頻寬,可能從 2Mbps ~ 20Mbps 不等,這會直接影響到到 Write 需要的時間。

    不管是用 thread 或是 async ,都不是萬靈丹,主要還是看你的狀況適不適合用這方法解決。這次我的案例是用 async 的方式,將 Read / Write 閒置的時間重疊在一起,節省的時間就反應在整個工作完成的時間縮短了。因此兩者花費的時間差距如果過大,則就沒有效果了。

    我簡單列了一張表,來表達這個關係。分別針對 client 端的頻寬,從 2Mbps ~ 200Mbps, 列出使用 async 改善前後的花費時間,及效能改善的幅度:

      *200M 100M 80M 50M 20M 10M 5M 2M
    原花費時間(ms) 7000 9000 10000 13000 25000 45000 85000 205000
    ASYNC花費時間(ms) 5500 5500 5500 8500 20500 40500 80500 200500
    效能改善% 127.27% 163.64% 181.82% 152.94% 121.95% 111.11% 105.59% 102.24%

    以執行時間來看,頻寬低於 80M 之後,改善的程度就固定下來了,隨著頻寬越來越低,WRITE 需要花費的時間越來越長,改善的幅度就越來越不明顯。同樣這些數據,換成改善的百分比,換成下一張圖:

    改善幅度最好的地方,發生在 80Mbps, 這時正好是 Read() + Process() 的時間,正好跟 Write() 花費的時間一樣的地方。頻寬高於或低於這個地方,效果就開始打折扣了。通常改善幅度若低於 10%, 那就屬於 “無感” 的改善了。

    簡單的下個結論,其實任何效能問題都很類似,能用 async 改善的效能問題,一定有這種模式存在: 整個程式執行過程中,有太多等待的狀況發生。不論是 IO 等待 CPU,或是 DISK IO 等待 NETWORK IO 等等,都屬此類。從外界能觀察到的狀況,就是幾個主要的硬體資源,如 Network, CPU, DISK, Memory 等等,都沒有明顯的負載過重,但是整體效能就是無法提升,大概就屬於這種模式了。找出流程能夠重新安排的地方後,剩下的就是如何善用這些技巧 (async),把它實作出來就結束了。

    而 async / await, 處理這類問題,遠比 thread 來的有效率。就我看來,若需要大規模的平行處理,還是 thread 合適。但是像這次的案例,只是希望將片段的任務以非同步的模式進行,重點在精確的切割任務,同時要在特定的 timing 等待先前的任務完成,這時 async / await 會合適的多。

    2013/04/15 .NET ASP.NET AZURE C# MSDN Tips 多執行緒 技術隨筆