2022年5月16日 星期一

自動幫用戶開啟RichMenu與鍵盤or語音輸入

LINE在2022/5/13增加了postbackAction的屬性,讓發人員可以藉由送出一個含有postbackAction的訊息(類似底下這樣),來幫用戶來開啟(或關閉)rich menu,甚至可以開啟輸入鍵盤和語音:
enter image description here

上圖 A 的部分,是一個 Buttons Template Message,其中有四個按鈕,其類型都是我們先前介紹過的 postbackAction。

在過去,當用戶點下postbackAction按鈕時,只會讓伺服器端的WebHook收到一則訊息,但LINE 2022/5/13 增加了此功能之後,postbackAction多了兩個重要的屬性,分別是inputOption與fillInText。

inputOption可以是底下的值:

  • closeRichMenu: 關閉 rich menu
  • openRichMenu: 開啟 rich menu
  • openKeyboard: 開啟輸入鍵盤
  • openVoice: 開啟語音輸入

而 fillInText 則是當inputOption為openKeyboard時,開啟的文字輸入鍵盤中的預設文字。

例如,當建立 postbackAction 的程式碼如下:

Actions.Add(new  isRock.LineBot.PostbackAction() 
{
label  =  "開鍵盤",
data  =  "openKeyboard",
displayText  =  "開鍵盤",
fillInText  =  "預設文字",
inputOption  =  "openKeyboard"
});

則用戶點選該選單後,結果如下:
enter image description here

當您建立 postbackAction 的程式碼如下:

Actions.Add(new  isRock.LineBot.PostbackAction() 
{
label  =  "開選單",
data  =  "openRichMenu",
displayText  =  "開選單",
inputOption  =  "openRichMenu"
});

則用戶點選該PostbackAction選項,結果如下:
enter image description here

你還可以透過底下指令,來開啟語音輸入:

Actions.Add(new  isRock.LineBot.PostbackAction() 
{
label  =  "開語音輸入",
data  =  "openVoice",
displayText  =  "開語音輸入",
inputOption  =  "openVoice"
});

執行結果如下:
enter image description here

很有趣吧,這功能可以讓你的LINE Bot與用戶的互動更加的便捷, .net core 的範例程式碼在底下:
https://github.com/isdaviddong/ExLineRichMenuautomaticOpeningAndClosing

你只需要將 LineBotSDK 升級到 2.5.33-beta 以上的版本即可。

2022年5月13日 星期五

Azure DevOps 中的 Release Gate

enter image description here
在使用自動化上版的過程當中,你大概或多或少會想要使用簽核(Approval)的功能。簽核功能讓你可以自動佈署到特定站台(例如正式機)之前,形成一個 “把關” 的功能。

雖然感覺用起來很酷,但坦白說,我們對於上新版程式前的Approval行為,在態度上是不鼓勵的。因為多年下來,並沒有案例顯示,上版前的簽核真能夠減少什麼風險或是降低問題發生的機率。反倒是常常因為Approver卡住流程,造成自動化效率的降低。

如果企業是為了要有人負責,才配置這個Approver,那我們就更不鼓勵了。因為大部分的Approver其實無法在上版前真的做什麼檢查,常常有簽核權限的PM只是回頭問問工程師 : 『可以上正式機了嗎? 可以? 那我就按"同意"了唷』。這樣的橡皮圖章其實讓人很心酸。

事實上,大部分能做該做的檢查,根本早就在(也應該要在)自動化流程中被完成,而非靠人工的檢核。

更好用的機制 - Release Gate

比起Approvals的設定,Release Gate其實是配合自動化部署更好的選擇。

設定的方式類似 Approvals,只需要先將選項設為 Enabled即可啟動(下圖A),接下來就是設定要作為Gate的機制(下圖B):
enter image description here

按下Add之後,你可以選擇要作為Gate的機制:

enter image description here

系統已經內建不少的Gate類型可供選擇,其中比較常用的大概是『Invoke REST API』和『Query work items』。另外『SonarCloud Quality Gate』則是外掛的套件,配合SonarCloud軟體品質掃描用的。

當你設定了Release Gate這個機制,它將會每隔幾分鐘(最少五分鐘)檢查一次你設定的條件,當該條件成立的時候,才會允許Pipeline繼續往下進行。

這個功能可以幫助我們,在自動化的過程中減少不需要的人力介入或簽核,即可自動檢查特定條件是否成立。你也可以撰寫Azure Function,或是使用Invoke REST API方法來呼叫特定API,取得回傳值,以判斷是否可以讓Release Pipeline繼續往下運行。

在高強度(一天數次或一周數次)的頻繁交付過程中,Release Gate比起人工簽核(Approval)更是合作上版前的自動化關卡檢查機制。

來,以後試著放棄橡皮圖章吧。


參考資料:
Azure DevOps 顧問實戰
https://www.tenlong.com.tw/products/9786263241251?list_name=b-r7-zh_tw

2022年4月27日 星期三

Azure DevOps in Action - 建立Linux環境的Build Agent

Azure DevOps也可以輕易地建立在Linux環境上的Build Agent,底下我們將會採用Ubuntu的VM環境來示範這個動作。

首先,我們建議您用Azure上的Ubuntu 20.04虛擬機範本,相關的建立參數如下:
enter image description here
我採用D4s_v3的虛擬機等級,在East Asia資料中心建立該伺服器。同時為了讓我能夠從Windows環境連上該伺服器做後續設定,我選擇了開啟SSH(22) Port。

請牢記你建立時所輸入的帳號密碼。

在虛擬機建立完之後,可以透過PowerShell(我是使用 Windows Terminal)以ssh指令來連上該伺服器,並且輸入密碼:

ssh 帳號@IP

例如:
enter image description here

遠端登入成功之後,即可對該伺服器下達指令。

先整理一下我們登入後要做的事情,分別是:

  1. 安裝 .net core SDK(為了可以進行 dotnet build)
  2. 下載Azure DevOps Agent套件(壓縮檔)
  3. 解壓縮套件
  4. 安裝套件並進行設定(過程中需用到PAT)
  5. 執行Agent

整個動作,可以從Azure DevOps的Orgnization Settings開始:
enter image description here
從Orgnization Settings選單點選Agent Pools,選擇Default(即為Self-Hosted Agent),接著點選New Agent。

在出現的畫面中,請點選Linux,你會看到安裝Linux Build Agent的步驟:
enter image description here
首先,請點選上圖A的部分,複製agent套件的下載位置,在筆者截稿時,該位置為:

https://vstsagentpackage.azureedge.net/agent/2.202.1/vsts-agent-linux-x64-2.202.1.tar.gz

接著,請在powershell以ssh連線的ubuntu環境中,下達底下指令:

mkdir myagent && cd myagent

這會建立一個myagent資料夾,並且進入該資料夾中。

接著,請執行底下指令,來下載agent:

curl -O https://vstsagentpackage.azureedge.net/agent/2.202.1/vsts-agent-linux-x64-2.202.1.tar.gz

其中 curl -O 是下載檔案,而後面的url,請換成您剛才在上圖A中所複製到的最新版URL。

接著,再透過底下指令解壓縮:

tar zxvf vsts-agent-linux-x64-2.202.1.tar.gz

完成後,執行ls指令,會看到類似底下這些內容:
enter image description here
這樣我們待會就可以進行設定了。

但在此之前,我們得先準備好PAT(Personal Access Token),以及為該伺服器準備 .net core sdk環境。

參考 https://docs.microsoft.com/zh-tw/dotnet/core/install/linux-ubuntu 的說明,您可以透過運行底下指令,來安裝 .net core sdk:

wget https://packages.microsoft.com/config/ubuntu/21.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

sudo apt-get update;
sudo apt-get install -y apt-transport-https &&
sudo apt-get update &&
sudo apt-get install -y dotnet-sdk-6.0

過程中可能會需要您輸入密碼,請在powershell中執行上述指令,結果類似底下這樣:
enter image description here
若你需要安裝其它版本的.net core sdk,可以修改上面指令中的版號即可,例如:

sudo apt-get install -y dotnet-sdk-5.0

成功安裝後,我們就要進行最後一個步驟了。

請先準備好PAT,取得PAT的方式如下,請先點選Azure DevOps畫面右上角個人帳號旁邊的小人圖示:
enter image description here
接著在選單中選擇 『Personal access token』,即會出現建立PAT的畫面:
enter image description here
在出現的畫面中選擇建立新的PAT。

PAT是替代你的帳號密碼的令牌,在你指定的有限時間內,出示該令牌就等於具有你所賦予的權限。

建立好了之後,請保留該PAT,它的長像大概是底下這樣:

zs4l6xp7xwhnj7lp8ta7i4q3xhtfasaxs3vp6xxwrj6iger7i63ob2eq

接著,請回到剛才的powershell視窗,在myagent資料夾底下執行:

./config.sh

執行過程中,會需要輸入PAT:
enter image description here
請留意,上圖2中的URL,要輸入的是你的Azure DevOps站台位置,而輸入PAT之前(上圖3),會先要你按一個Enter(請仔細注意上面的英文敘述),然後才是輸入PAT(按下滑鼠右鍵即可貼上)。

後面的幾個選項都按Enter即可,完成後,你會看到Setting Saved.

接著,再執行 ./run.sh ,你會發現,系統已經開始運作,並且在等待jobs了 :
enter image description here
這時候,你就可以使用這個 agent pool來進行build job了。

你可以依照先前我們介紹過的方式,建立一個 .net core 程式的CI Build Pipeline,唯一不同的地方是,這次(下圖1)Agent Pool選擇的是Default(Private) ,其它的設定均不變:
enter image description here
當你運行該Pipeline時,會發現,這個Build Machine比Azure DevOps內建的來的快很多,自然是因為這台VM我們建立時選擇的等級是很高檔的。

沒多久,整個CI Build就順利的完成了:
enter image description here
你也會看到,PowerShell中,Ubuntu的Command Line出現相對應的提示訊息:
enter image description here
表示job被成功的完成了。

如果要停止該agent,可以在命令列按下CTRL+C,如果要移除該agent,只需要下達 ./config.sh remove指令,並輸入PAT即可:
enter image description here
建立一個私有的Linux Build Agent就是這麼簡單。

相關資源:

[書籍]Azure DevOps顧問實戰:
https://www.tenlong.com.tw/products/9786263241251?list_name=b-r7-zh_tw

[課程]敏捷開發專案管理與Azure DevOps實戰
https://www.studyhost.tw/NewCourses/ALM

2022年4月26日 星期二

No hosted parallelism has been purchased or granted.

近期上課時,許多學員在申請好Azure DevOps站台後,使用Build Pipeline時,可能會發現它發生了類似底下這樣的錯誤訊息:
enter image description here
完整的錯誤訊息是:

##[error]No hosted parallelism has been purchased or granted. To request a free parallelism grant, please fill out the following form https://aka.ms/azpipelines-parallelism-request

這段錯誤訊息的起因,是由於2021年3月之後,微軟已經取消了預設的免費pipeline使用,若您要使用免費的pipeline,必須依照上述的訊息,填寫申請表(網址如下):
https://aka.ms/azpipelines-parallelism-request

經過同學測試,如果申請成功,你會收到微軟寄來的信件,告知您已經可以使用。但因為填寫申請表曠日廢時(大概要2-3個工作天),若您不想等待,我們還有一個更簡單(但可能需要點費用)的方法。你可以用登入Azure DevOps相同的帳號,申請一個免費的Azure Trial訂閱,接著將該訂閱綁定於Azure DevOps服務即可:
enter image description here
當成功設定好訂閱(Subscription)連結之後,您必須將Paid parallel jobs設定為1(參考下圖):
enter image description here
設定完成儲存之後,立即可以使用Pipeline

特別注意,每一個 Paid parallel jobs 可能會花費 $40USD唷,請特別留意費用的產生。

2022年3月3日 星期四

使用IoC與DI有何意義? (四) 使用IoC與DI提高可測試性

enter image description here這一系列的文章,持續維持著一年更新一篇的進度,😛。

今天適逢良辰吉日,我們再次來談談,如何透過IoC與DI增加系統的可測試性。

關於單元測試(Unit Test)

我們知道,有單元測試的存在,我們得以放膽的修改程式碼,無須擔心程式碼的頻繁異動將造成意料之外的副作用。

也因此,有一些團隊追求著程式碼單元測試的覆蓋率。

然而,高覆蓋率的單元測試並不意味著就會有更高品質的程式碼,反而真正重要的是,如何為我們系統中的核心邏輯、重要的核心函式,適當的添加單元測試。

所以,我們接下來要來談一談,如何透過IoC與DI增加程式碼的可測試性。

關於可測試性

你大概也已經知道,我們可以透過Visual Studio為特定的method來建立單元測試。特別是重要的商業邏輯運算函式、或是API,都是單元測試非常能發揮功能的好對象。

然而,你可能會發現有些程式碼似乎難以測試,例如底下這個例子:

Console.Write("請輸入金額(USD):");
var amount = int.Parse(Console.ReadLine()); //100
Console.Write("請輸入人數:");
var people = int.Parse(Console.ReadLine()); //5
Financy f = new Financy();
var CostByPeople = f.SplitMoney(amount, people);
Console.Write(CostByPeople);

上面這段程式碼看起來簡單,實際上,也真的很簡單😎。

SplitMoney()是一個計算旅遊消費金額分攤的函式。

假設,你和一夥人出國旅遊,在路邊店家吃了一餐,總共100美元,你想計算每個人要負擔多少台幣,就可以使用這個函式。呼叫SplitMoney()時,傳入『美金總金額』和『人數』兩個參數,它就幫你算出一個人要付的台幣金額。

假設我們要針對這個函式進行單元測試,乍看之下似乎並不難,但我們看SplitMoney()的具體內容:

public class Financy
{
  public double SplitMoney(double USDAmount, int People)
  {
    var currencyConverter = new CurrencyConverter();
    //使用到外部函式(抓取匯率)
    double rate = currencyConverter.Convert("USD",  "TWD");
    //計算台幣總金額
    double Total = USDAmount * rate;
    //回傳一個人需要付多少錢(台幣)
    return Total / People;
  }
}

這下問題來了。

我們SplitMoney()這個方法本身不難,也不過就是把總金額除以旅遊人數,就算出平均金額了。倘若我們需要為SplitMoney()這個方法撰寫單元測試,似乎也沒啥問題。

但這邊出現了個障礙,你知道的,單元測試是以程式來驗證程式,也就是說,我要從外部來驗證一個函式是否正確,就撰寫單元測試函式,來呼叫我想驗證的函式,傳入固定的參數,我想驗證的函式理當也會有著同樣固定的回傳結果。藉由判斷回傳結果是否與預期的一致,我就可以驗證該函式是否正確。

但剛才說,這邊出了點問題,因為SplitMoney()這個方法在計算的過程中呼叫到了Convert這個函式:

//使用到外部函式(抓取匯率)
double rate = currencyConverter.Convert("USD", "TWD");

上面這個方法,抓取的是『即時』的匯率,而因為匯率是浮動的,這表示,當我們每一次呼叫這個函式時,即便傳入的,是同樣的參數,也可能得到不同的結果!

如此一來,該函式SplitMoney()就沒有可測試性(testability),因為我們無法去驗證它,是否在被修改後,還維持著一致的運算邏輯。

該如何解決前面提到的問題?

使用fake類別提高可測試性

我們先釐清問題在哪,顯然,問題的來源不是來自於SplitMoney()這個方法本身,而是來自於SplitMoney()這個方法執行的過程中,所呼叫的即時匯率抓取函式。

倘若,我們能夠在執行單元測試方法時,也就是運行掛載著[TestMethod()] 這個attribute的單元測試方法時,將SplitMoney()中抓取即時匯率的函式給換掉,改成永遠回傳固定值(例如台幣與美金的兌換永遠維持在 27:1),如此一來,我們的單元測試函式,就可以撰寫了,因為我們每次重複呼叫時,都可以得到一個穩定的預期結果值。

不過,我們在執行單元測試的時候,雖然抓取的匯率必須是固定的值,但一般呼叫的時候,則必須要保持是抓取到動態的匯率才行呀。

為了實現這個功能,我們可以針對SplitMoney()中所用到的抓取匯率的這個類別,設計一個相同的偽裝類別(Fake Class)。為了這麼做,我們先把抓取匯率的這個類別CurrencyConverter抽提出介面ICurrencyConverter,同時調整一下原本的CurrencyConverter類別,讓它繼承自介面ICurrencyConverter:

   public interface ICurrencyConverter
   {
     float Convert(string From, string To);
   }
   
   public class CurrencyConverter : ICurrencyConverter
   {
      public float Convert(string From, string To)
      {
         //…具體程式碼請參考 github
         // https://github.com/isdaviddong/HOL-UnitTestWithIoC_After
         return data;
      }
   }

然後,再繼承ICurrencyConverter介面設計出一個fake類別:

//建立fake類別
public class FakeCurrencyConverter : ICurrencyConverter
{
   public float Convert(string From, string To)
   {
      return 27.67222F;
   }
}

你會發現,我們在假類別中,把匯率固定在27.6,如此一來,倘若我們在單元測試方法中,可以讓SplitMoney()採用這個假類別FakeCurrencyConverter,而非使用真的CurrencyConverter類別去抓取即時匯率,那就可以在單元測試方法中,維持SplitMoney()回傳值的穩定性(冪等性)。如此一來,就可以正確的撰寫單元測試了。

那我們要怎麼讓單元測試中的程式碼在呼叫SplitMoney()方法的時候,採用假類別,而一般程式碼呼叫SplitMoney()方法的時候,卻又使用真的類別呢?

請注意,這時候,建構子注入就要出現了。

透過IoC與DI提高可測試性

就物件導向程式設計的概念來說,如果A方法對於B類別有依賴,我們可以幫B類別設計(重構出)一個介面C,將A方法程式碼中對於B類別的依賴,改寫成對介面C的依賴。

這其實,就是剛才我們做的事情。

我們對SplitMoney()所倚賴的類別CurrencyConverter進行重構,為它建立介面ICurrencyConverter,並且繼承該介面建立出一個類似CurrencyConverter類別的假類別FakeCurrencyConverter。

這樣有何好處?

如此一來,我們可以調整原本SplitMoney()方法的程式碼,把寫死依賴CurrencyConverter類別的這個狀況,改為依賴介面ICurrencyConverter:

ICurrencyConverter _CurrencyConverter;

public double SplitMoney(double USDAmount, int People)
{
   //var currencyConverter = new CurrencyConverter();
   //使用到外部函式(抓取匯率)
   double rate = _CurrencyConverter.Convert("USD", "TWD");
   //計算台幣總金額
   double Total = USDAmount * rate;
   //回傳一個人需要付多少錢(台幣)
   return Total / People;
  }

然後,在建立該類別的時候,把_CurrencyConverter先預設設定為CurrencyConverter類別即可。

這樣做的好處是,有需要時,我們可以動態替換抓取匯率的類別,將其改成fake類別,以便於固定抓取到的匯率回傳值。進而讓SplitMoney()方法的計算結果冪等,好讓我們可以撰寫單元測試。

因此,我們依照上面的邏輯,把程式碼改成底下這樣,實現所謂的建構子注入:

public class Financy
{
   //加入建構子注入
   ICurrencyConverter _CurrencyConverter;
   public Financy(ICurrencyConverter currencyConverter)
   {
      _CurrencyConverter = currencyConverter;
   }

   public Financy()
   {
      //預設狀況下,用標準類別
      _CurrencyConverter = new CurrencyConverter();
   }

   public double SplitMoney(double USDAmount, int People)
   {
      //var currencyConverter = new CurrencyConverter();
      //使用到外部函式(抓取匯率)
      double rate = _CurrencyConverter.Convert("USD", "TWD");
      //計算台幣總金額
      double Total = USDAmount * rate;
      //回傳一個人需要付多少錢(台幣)
      return Total / People;
   }
}

也就是說,我們在呼叫這個SplitMoney()方法前,在建立其所屬的類別Financy時,可以把將來要具體使用的抓取匯率的類別,以建構子參數的方式傳入,而不是寫死在SplitMoney()方法中。如此一來,我們就可以在運行單元測試的時候,採用fake類別:

[TestClass()]
public class FinancyTests
{
   [TestMethod()]
   public void SplitMoneyTest()
   {
      //注入測試用的fake類別實作
      Financy f = new Financy(new FakeCurrencyConverter());
      var CostByPeople = f.SplitMoney(100, 5);
      Assert.IsTrue(CostByPeople.ToString().StartsWith("553.444"));
   }
}

而運行一般程式的時候,採用正常的類別(當沒有傳入建構子參數,則預設使用一般類別):

Console.Write("請輸入金額(USD):");
var amount = int.Parse(Console.ReadLine()); //100
Console.Write("請輸入人數:");
var people = int.Parse(Console.ReadLine()); //5
Console.ReadLine();
//採用正常的類別
Financy f = new Financy();
var CostByPeople = f.SplitMoney(amount, people);

這樣一來,是不是讓原本不易撰寫測試程式的程式碼,變成可以輕易測試了呢? 這就是可測試性(testability)的提升。

如果你需要程式碼,請參考:
https://github.com/isdaviddong/HOL-UnitTestWithIoC_After


本文內容來自於『團隊開發與架構設計實務』課程,我們最近要開課囉,依照過去經驗這門課幾乎都是秒殺,如果你需要預先保留席次,請點選這裡登記唷。

若您要詢問問題,或是需要即時取得更多相關訊息,可按這裡加入FB專頁。
若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2022年2月23日 星期三

Azure DevOps in Action - 脫離組織

你知道的,有些地方,進去很容易,脫離就有點困難。
我說的不是黑道堂口,也不是邪教異端,我說的是 Azure DevOps organization。

不只一次,我被問過這個問題:『當被加入一個Azure DevOps組織之後,要怎麼離開呀?』

沒錯,你尋遍整個portal,沒有離開的選項。也就是說,倘若該組織的admin不把你移除,你似乎永遠會屬於某個組織,這導致於,每次你登入的時候,左方選單裡面就有一大多你根本已經不會去用的組織站台:
enter image description here

這些站台多還不打緊,它還讓我無法建立新的組織:
enter image description here

這使得我下定決心,要脫離組織的綑綁。
尋找了一會兒,發現,原來還是可以脫離組織的,但這功能不在portal上,而是在 https://aex.dev.azure.com/ 這個頁面:
enter image description here

在這個畫面上會列出你的帳號參與了哪些組織。
原來,在這個畫面,只要展開你不是owner的組織,右下角就會有一個 Leave 聯結按紐,把它給點下去,出現了警告訊息:
enter image description here

但,這就是我要的呀。
點選 Leave 後,徹底脫離組該織的所有的掌控與專案了。

2022年2月11日 星期五

tech and business predictions and expectations for 2022

In 2021, the original expectation of the whole world was that life could return to normal through the launch of a vaccine, but this idea was once again interrupted by uncertain factors.

Omicron allows the world to face a new wave of challenges, and at the same time, it also makes companies start to think about how to respond if they need to coexist with the virus for a long time. So, in 2022, we’ll have chances of seeing these things happening underneath.

  1. More remote collaborative operation modes will appear
    IT vendors have promoted online conferencing software for many years in the past. In these days of the epidemic, there has been subversive growth. After breaking through this barrier, more technologies and tools for remote cooperation will emerge. besides, 5G and the artificial intelligence will make the future “cooperation” full of imagination. In the past, most remote collaborations were people-to-people collaborations – co-finishing a project, co-authoring an article, co-developing a piece of code, but in the future, AI tools in the cloud may also join to the collaboration. Become a virtual employee in the enterprise. Act as a real-time translator, secretary, or even a consultant to provide information or advice while meeting remotely online.

  2. There will be changes in management methods, and the need for mid-level managers will be reduced
    Over the past year, companies have gradually realized that not all work has to be done in the office. Even if some employees are working from home, the proper use of information tools can also exert their output value. In the past, the practice of relying on middle-level managers for communication, coordination, or management of grass-roots personnel will face challenges. Enterprises will adopt more virtualized office environments, flatter organizations, fewer reporting layers, and more use of IT tools for direct communication, all of which will happen more dramatically in 2022. As a result, It will reduce the dependence of enterprises on middle-level managers, and the related workflow automation software tools will be paid more attention.

  3. The implementation of enterprise automation and productivity tools will be accelerated
    Over the past few years, we have seen DevOps tools effectively automate the software development process with great success across industries. Due to the unstable situation of the epidemic, companies will need to introduce more similar process automation tools in different areas. In the past two years, global users’ demand for online conferencing software has increased significantly. If the epidemic continues, various process automation tools, remote collaboration tools, digital dashboard and performance management tools will become important support for enterprises to face challenges in the future.

The world has entered its second year in the face of Covid-19. In the past two years, whether it is the information industry or the entire society, it has suffered a considerable impact, AI and the virtual reality technology continues to develop rapidly. In 2022 , will have the opportunity to apply it in medical, education, psychological counseling and counseling and other scenarios. These will be our challenges and opportunities in the coming year.

2022年1月25日 星期二

Azure DevOps In Action - 設計支援Docker/Container的Pipeline

近幾年Docker/Container技術相當被業界重視,而微軟的 .net 開發技術也適時的支援了容器化的功能。為了實現容器化, .net 開發技術必須支援跨平台,特別是 asp.net,這也造就了 .net core的誕生。

因此,現在您不管用何種開發工具,也可以產出能運行在Linux環境上的asp.net應用程式,這同時也表示,asp.net理所當然的也可以支援Linux Container了。

Docker file

要讓asp.net專案產出支援Docker的image非常簡單,你可以在建立asp.net專案的時候,勾選『啟用Docker』,『Docker OS』選擇Linux:
enter image description here
或者,你也可以在一開始沒有啟用Docker支援的專案中,點選滑鼠右鍵,選擇加入→Docker支援:
enter image description here
Visual Studio會幫你在專案中建立一個類似底下這樣的Docker File:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["webapp01.csproj", ""]
RUN dotnet restore "./webapp01.csproj"
COPY . .
WORKDIR  "/src/."
RUN dotnet build "webapp01.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "webapp01.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "webapp01.dll"]

這個docker file就是我們用來建立docker image的關鍵。

總的來說,透過asp.net建立一個容器化應用的流程是底下這樣:
enter image description here

asp.net的原始程式碼無須修改,只要加入了docker file,就足以產生docker image,而產生出的docker image,我們需要將其送上Docker Hub(或其他registry,例如azure container registry),以供用戶下載使用。

上面這一段動作,平時大多都可以在Visual Studio中完成。如果你的開發工具是VS Code,那你也可以透過 .net CLI搭配Docker Command來完成。從原始程式碼依照docker file的流程產出image的這個動作我們一般稱為docker build。

Docker task

知道上面的概念後,我們接著來看,現在我們想要實現的,就是在Azure DevOps當中來設計出可以自動完成docker build並且將產出的image推送上docker hub的自動化Pipeline。

其中的關鍵在於『Docker』task:
enter image description here

你可以先透過asp.net core範本建立一個Pipeline,並且在流程中加入Docker task。這個task本身就足以運行docker build與docker push指令:
enter image description here

藉此把產出的image送上docker hub(或azure container resgistry)。

其中有幾個需要稍微說明一下的參數,Container registry的部分,請選擇『New』:
enter image description here

接著,在出現的視窗中,選擇Docker Hub,然後輸入你的Docker ID與密碼即可:
enter image description here

按下Verify之後,系統會驗證你的帳密正確性,如果確定沒有錯誤,你就可以設定一個連線的名稱,然後按下Verify and Save將其儲存:
enter image description here

儲存後,你就可以在Container registry下拉選項中看到該連線項目:
enter image description here

另外,還有幾個你需要注意的選項:
enter image description here

像是 repository 路徑你必須給你在docker hub上所建立的repository名稱(上圖A),而上圖B之中的,則是專案的docker file所在路徑,一般保留上述這樣即可,這表示Pipeline會在專案的所有檔案中搜尋。

而Tags的部分,我們之所以用『$(Build.BuildId)』,是因為這個數字每一次build都不同,這樣如果你要把成品(docker image)上傳到Docker Hub的時候,才不會因為版號相同而有所衝突。

例如,這個建立好的docker image如下:
enter image description here

由於你每次建置的(Build.BuildId)都不會相同,這樣,即便pipeline被多次重覆執行,也不會發生錯誤,而(Build.BuildId)都不會相同,這樣,即便pipeline被多次重覆執行,也不會發生錯誤,而(Build.BuildId)剛好也可以做為類似版號的存在。

若pipeline正確執行,其結果會是:

你會發現,pipeline中的docker push指令順利完成,同時docker hub上的image也更新了一版:
enter image description here

從上面的介紹中你可能也會發現,其實一開始並不一定要使用asp.net core範本來做為pipeline的設計基礎,其實用空的範本(empty)也可以,因為,整個build project與build image的動作,其實都是透過docker task中的指令來完成的,因此,原本asp.net core的那幾個restore/build/pulish動作根本是可以直接槓掉(或移除)的:
enter image description here

只需要透過docker task即可幫我們完成相關的工作。除非您希望在Build Docker Image之前,先進行單元測試,這樣的話,則可以考慮保留asp.net core預設範本中的tasks。

以上,就可以輕鬆的自動化產生docker image並送上registry囉。

2022年1月17日 星期一

Azure DevOps in Action - 在CI Pipeline中發佈NuGet套件

我們在前面的章節曾經介紹過,軟體重用性提升一個很大的要素就是套件化。時至今日,不管你用哪一種軟體開發語言,大概都離不開各式各樣的套件庫。

因此,我們在前面談到Azure Repos的章節中,曾經約略介紹過如何建立套件,也介紹過利用Azure DevOps的Artifacts功能來建立的私有(Private)套件庫:
enter image description here

你大概已經知道,我們可以將組件(.dll)封裝成具有版號的套件,以便於分享給團隊甚至網際網路上所有開發人員來使用。只是先前,我們封裝好的套件(.unpkg)都是以手動方式上傳到 nuget.org,我們接著來看看,如何自動化完成這件事情。

設計自動發佈套件的Pipeline

一個自動化建置出NuGet套件並且上傳的CI Pipeline並不難設計,就建立 .net core的套件而言,你可以直接使用 ASP.NET Core的範本來修改即可:
enter image description here

接著將範本中的Publish改為(Pack):
enter image description here

因為套件本身不是網頁,因此沒必要『Publish』出什麼,倒是需要產生出 .unpkg檔案,因此我們需要執行dotnet的Pack command。

然而光Pack還不夠,你還得上傳到 nuget.org,這部分則可以透過 NuGet push task(下圖B)來完成:
enter image description here

上圖中的NuGet push task,其實是在運行NuGet的push command(上圖D),該task會在預設(上圖E)的位置嘗試尋找我們先前透過 Pack Nuget命令所建置出的 .nupkg 檔案,然後將其上傳發送到指定的套件庫。

因此,我們需要進行上傳位置的設定。

第一次連線時你必須點選(上圖F)右方的『+New』按鈕,接著會出現底下畫面:
enter image description here

在出現的畫面中,我們必須輸入ApiKey(上圖C)以便於具有連線到NuGet的權限,後續可以讓Pipeline把套件檔案上傳到Nuget。

一般來說,公開的NuGet套件庫位於:
https://api.nuget.org/v3/index.json

如同先前介紹過的,你只需要有Microsoft Account即可登入並且上傳套件。請先用你的Microsoft Account登入nuget.org
enter image description here

登入後你會發現右上角的頭像圖示選單中,就有一個API Keys選項,透過該選項,你就可以取得一組API Key。

取得API Key之後,把該Key填入下圖C的位置,設定好連線名稱(下圖D):
enter image description here

按下Save鈕之後即可。

建立好連線,並且整個Pipeline被運行之後,你應當會看到自動建置出的套件被上傳到nuget了:
enter image description here

如果要上傳到Azure DevOps的Artifacts,其實也很簡單:
enter image description here

只需要把Task的Target feed選項設為『This organization/collection』即可。

備註:

如果讀者想自己試試看,您可以參考筆者在Ggithub上的source code,位於:https://github.com/isdaviddong/HealthMgrPackageDemo.git
將其Clone到您的專案後,請修改底下檔案:
HealthMgrPackageDemo/HealthMgr.csproj
將其中程式碼 HealthMgr-[yourName] 中的 [yourName] 換掉,成為您的姓名(必須是全球唯一值),因為這個值會影響套件的ID,而該ID在整個nuget平台必須是唯一的,否則將無法上傳。

熱門文章