###### tags: `LSA`
# 軟體開發中的自動化
[TOC]
## 自動化
- 自動化是一個概念,將原本由人類來做的事情,加入科技來**減少**人類的勞動,通常會將重複性的任務自動化。
- 自動化可以導入到各種領域,包括製造業、人工智慧和商業軟體等等。
### 自動化舉例
- 自動販賣機
![](https://i.imgur.com/0qCZM9m.png =100x)
> [圖片來源](https://www.irasutoya.com/2016/10/blog-post_863.html)
- 工廠
![](https://i.imgur.com/b7nneEL.png =150x)
> [圖片來源](https://www.irasutoya.com/2015/09/blog-post_504.html)
- 程式
![](https://i.imgur.com/qiGw1BP.png =150x)
> [圖片來源](https://www.irasutoya.com/2019/09/blog-post_45.html)
- `top`:動態顯示系統的即時狀態和 process
- ![](https://i.imgur.com/I0Adaf0.png =400x)
- 自動休眠
- ![](https://i.imgur.com/BitOy8i.png)
:::info
**和自動化有關的英文單字**
| |意思|應用|
| ------- | -------- | -------- |
|automated|以機器取代人力|ATM|
|automatic|泛指任何自發的動作,動作有固定規則|無人車|
|autonomous|有能力自己做決定,其中可能有複雜的規則|有人工智慧的無人車|
- 人類涉及程度:automatic > autonomous
:::
### 為什要自動化
- 程式設計師的 3 個美德 :star:
1. 懶惰(Laziness):程式設計師寫程式讓它幫人做事
2. 不耐煩(Impatience):程式設計師不能忍受電腦怠惰
3. 驕傲(Hubris):程式設計師會寫出完美的程式讓其他人無可挑剔,本身也很自豪
- 提高品質和一致性
- 有效率、縮短工作時間
- 取代部份人力,減少人事成本
#### 缺點
- 初始成本高
- 技術門檻較高
- 自動化的悖論:自動化系統的效率越高,人類的參與程度越低,但這些人角色更加關鍵。
:::info
**「KUROKO」**
{%youtube RTkGe69XNgY%}
:::
## LINUX 上的自動化
### cron & crontab
- cron:定期運行某些任務(command、Bash script)的**服務(service)**
- crond:背景運行 cron 的程式,讀取 crontab 檔
- crontab:規定了什麼時間做什麼工作的**檔案**
:::info
**BASH Script**
![](https://i.imgur.com/1fV2IeQ.png =200x)
> [圖片來源](https://medium.com/@yihengwu/%E7%A8%8B%E5%BC%8F%E7%AD%86%E8%A8%98-shell-script-%E7%B0%A1%E6%98%93%E7%AD%86%E8%A8%98-841cfc3ae3ab)
- shell:一種讓使用者可以和 Kernel 互動溝通的橋樑。
- bash:一種 shell。
- bash script:將需要重覆執行的指令寫成檔案讓 shell 執行。
- terminal:讓使用者跟 Shell 互動的工具。
> 使用者下`ls`指令並按下 Enter => Terminal 把`ls`指令字串傳給 Shell => Shell 執行 => 回傳純文字結果給 Terminal => Terminal 呈現結果給試用者看
- 練習
- 新增檔案 `touch age.sh`
- 輸入 script 內容 `vim touch age.sh`
```bash=
#!/bin/bash
#Just a bash sample
read -p "Please enter your age:" Y_AGE
echo "You are $Y_AGE years old." ```
- 將檔案設定為**可執行** `chmod +x age.sh`
- 執行`./age.sh`
![](https://i.imgur.com/NmrFiNN.png)
:::
- 和 cron 有關的目錄
- `/var/spool/cron/crontabs/<使用者>`:一般使用者編寫排程(`crontab -e`)就是寫在個別的檔案
- `/etc/cron.hourly/` 放每小時執行一次的 job
- `/etc/cron.daily/` 放每天執行一次的 job
- `/etc/cron.weekly/` 放每週執行一次的 job
- `/etc/cron.monthly/` 放每月執行一次的 job
- `/etc/cron.d/` 放其他定時任務
> 放在目錄裡面的都會定時執行,定時目錄可在/etc/crontab中設定
- 設定檔 `/etc/crontab`
```bash=
# 指定了系統要使用哪個shell
SHELL=/bin/sh
# 指定了系統執行命令的路徑
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# /etc/cron.hourly中的指令每小時的 17 分被執行一次
17 * * * * root cd / && run-parts --report /etc/cron.hourly
# 如果 anacron 有排程,就先執行 anacron
# cron.daily中的指令每天 6:25 分被執行一次
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
# cron.weekly中的指令每個星期日的 6:47 分被執行一次
47 6 7 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
# cron.monthly中的指令每月 1 號的 6:52 分被執行一次
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
```
- 設定排程的方式
1. 直接編寫任務
2. 使用目錄
- crontab 指令格式
```txt=
.---------------- minute (0 - 59)
| .------------- hour (0 - 23)
| | .---------- day of month (1 - 31)
| | | .------- month (1 - 12) OR jan,feb,mar,apr ...
| | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
| | | | |
* * * * * user-name command to be executed
```
| 特殊字號 | 代表意義 |
| --- | ---- |
| * | 任何時刻|
| , | 表示列舉|
| - | 代表一段時間範圍|
| / | 每隔多久|
- crontab 指令範例
- `* * * * * <指令>` 每分鐘執行一次
- `0 * * * * <指令>` 每小時執行一次
- `0 1 * * * <指令>` 每天 1 點執行一次
- `0 9,17 * * * <指令>` 每天 9:00 和17:00 執行一次
- `0 1 1-20 * * <指令>` 每月 1-20 號每天執行一次
- `*/5 * * * * <指令>` 每 5 分鐘執行一次
- 可搭配 https://crontab.guru/ 網站使用
![](https://i.imgur.com/FLrcN7X.png =600x)
- 指令
- `crontab -e` 編輯使用者的 crontab 指令
- `crontab -l` 列出使用者擁有的 crontab
- `crontab -u <使用者> -l` 列出特定使用者擁有的 crontab
- `crontab -r` 將使用者的 crontab 全部清除(如果只要刪除一項用 `crontab -e` 編輯)
- `service cron start` 啟用
- `service cron stop` 停止
- `service cron restart` 重啟
- `service cron reload` 重新載入設定
- `service cron status` 查看狀態
#### 練習:每分鐘將現在時間輸入至 `time.txt`
- `crontab -e` 開始編輯 crontab
- 選文字編輯器
- 加入排程
```shell=
* * * * * echo `date` >> /home/<使用者>/time.txt
```
> 指令和檔案的路徑用**絕對路徑**
- `crontab -l`列出使用者的 crontab
- `service cron start` 啟動 cron
- `service cron status` 查看 cron 執行狀態
- `cat time.txt`
![](https://i.imgur.com/kTNbg2z.png =300x)
## 敏捷開發
:::info
**瀑布式開發**
- 傳統的軟體開發流程,一開始就詳細定義和規劃軟體架構,再花很多的時間依照步驟進行開發。
![](https://i.imgur.com/YApRSjY.png =250x)
> [圖片來源](https://medium.com/@chathmini96/waterfall-vs-agile-methodology-28001a9ca487)
- 優點:
- 有完整的規劃
- 讓開發時比較流暢
- 缺點:
- 計畫趕不上變化,需要花很多時間從頭設計流程
- 可能開發完了卻發現軟體已經不符合市場需求!
:::
### 敏捷開發(Agile Software Development)
- 敏捷開發是一套價值觀 & 精神,提供人們在軟體開發的過程中做決策的方向。
- 敏捷宣言
```txt=
個人與互動 重於 流程與工具
可用的軟體 重於 詳盡的文件
與客戶合作 重於 合約協商
回應變化 重於 遵循計劃
也就是說,雖然右側項目有其價值,但我們更重視左側項目。
```
- 為什麼需要敏捷開發?
- 現今的軟體可能每隔 2、3 週就版本更新一次,對開發團隊有時間壓力,用舊方法也較沒效率。
- 目的:**及早開發、及早獲得回饋(Feedback)**,以便可以一直配合市場和客戶的需求,提供最好的軟體給客戶。
- 優點
- 提早發現問題
- 靈活性高
- 客戶有需求變化時,不用大規模重新規劃就可以修改軟體,滿足使用者不斷變化的需求
- 缺點
- 因為一開始沒有詳細的設計規劃,發展的方向和目標較容易失焦
- 敏捷開發是一種**價值**,要怎麼**實做**才能實現敏捷開發?
- 敏捷開發的方式(框架):Scrum、看板
### Scrum
- 將軟體開發的時程(設計 > 開發 > 測試 > 發行)切成多個 Sprint (衝刺期)循序地完成,Sprint 的期間通常是 **2 週**。
![](https://i.imgur.com/0ZRBIwL.png)
> [圖片來源](https://www.soldevelo.com/blog/is-agile-always-the-best-solution-for-software-development-projects/)
#### Scrum 會議
1. 計畫會議(Sprint Planning Meeting)
- 每個 Sprint 的一開始舉行,決定這個 Sprint 期間的任務。
2. 每日立會(Daily Standup Meeting)
- Sprint 期間每天花 15 分鐘做進度回報、調整,包含:
- 昨天完成了哪些工作
- 今天打算做什麼工作
- 是否遇上什麼障礙
3. 檢討會議(Sprint Review Meeting)
- 每個 Sprint 的最後舉行,主要用來檢視該 Sprint 期間待辦項目的達成狀況。
4. 回顧會議(Sprint Retrospective Meeting)
- 每個 Sprint 結束後,回顧這次完成的成果,並找出可以改善的地方,讓下次的衝刺更為順利。
### DevOps
- DevOps = Development(開發)+ Operations(維運)
- 透過將開發及維運兩個團隊相互合作,持續交互並兼顧軟體品質及安全性
- 為什麼需要 DevOps?
- DevOps 像是 Agile 的延伸
- 敏捷開發主要著重在軟體的開發(Development)部份,但實際情況是開發與維運(Operations)團隊經常由於溝通不良,運作上處處衝突
- 換句話說,為了增強整個過程循環的速度,才會在敏捷開發後也套用一些 DevOps 的流程,強化敏捷開發效益。
![](https://i.imgur.com/cF8fSzz.png =600x)
> [圖片來源](https://www.delta-n.nl/devops/wat-is-devops/)
- 流程
1. 計劃(Plan):規劃要做哪些事項、現有的資源(時間、金錢),並輕重緩急排序
1. 程式碼(Code):寫程式實現計畫好的事項
1. 構建(Build):為寫好程式碼建置能執行的環境,例如 compile 或 import modules,以便之後做測試
1. 測試(Test):確認程式正確、維護軟體品質、符合規劃
1. 發布(Release):測試好的程式要 Release
1. 部署(Deploy):把測試好的軟體部署到生產環境中
1. 維運(Operate):開機、啟動軟體,持續的維護運作,協助客戶處理問題
1. 監控(Monitor):查看運行狀態如何、有沒有 bug,找出可以改進的地方,接收客戶回饋,變成下一個 Sprint Cycle 的目標項目
:::info
- **Code - Build - Test** 為一個小循環,可套用敏捷開發。如果期間失敗就一直循環,直到成功才能進入 Release 階段。
- **Operate - Monitor** 為一個小循環。當需要更新時(例如一 sprint 的時間)就整理要修改的部份、結束小循環,開始下一次 Plan 階段。
:::
:::info
**CI/CD**:將測試、建置到部署流程自動化,以減輕人工的負擔。
- CI(Continuous Integration, 持續整合):自動化將程式 Build 起來並進行 Test
- CD(Continuous Delivery, 持續交付):自動化將專案部屬到測試環境,但先不部屬到生產環境
- CD(Continuous Deployment,持續部屬):自動化將專案到生產環境
![](https://i.imgur.com/rAjCv3L.png)
> [圖片來源](https://aws.plainenglish.io/devops-102-lifecycle-and-ci-cd-b18923240d49)
:::
- DevOps 中各個流程都可以自動化,有助於加速循環速度,以下列舉幾個自動化工具:
1. 計劃(Plan)
1. 程式碼(Code):GitHub Copilot、VSCode Tabnine
1. 構建(Build):npm
1. 測試(Test):Selenium
1. 發布(Release):Jenkins
1. 部署(Deploy):Ansible、CHEF
1. 維運(Operate)
1. 監控(Monitor):Nagios
## 自動化測試
### 軟體測試
- 軟體測試:確認已經做好的軟體或應用程式,有沒有達成規格書的目標,而且沒有出現非預期的錯誤,測試期間能找出愈多 bug 愈好。
- 為什麼需要軟體測試
- 掌控軟體品質
- 預防勝於治療
- 有數據證實軟體可以如預期運作,無形中給予程式師對這個系統的信心
### 軟體測試的種類
![](https://i.imgur.com/ceVmB2j.png =400x)
#### 測試的層次
- Unit Tests(單元測試)
- 單元測試是軟體測試中最小單位的測試,在程式內每個小功能(Function、Module)是否正確執行
- 由程式程式師親自針對每個單元寫不同的測試程式
- 此時修正錯誤是最容易也最省時間的
- Integration Tests(整合測試)
- 整合測試是在多個功能、模組組合起來或連結相關外部服務後,是否能正確執行
- 外部服務
1. 連到資料庫
2. 使用網路
3. 進行檔案存取(IO)
4. 需要對測試環境進行特別的動作(例如需要先編輯設定檔)
- [整合測試的失敗例子](https://twitter.com/juliehubs/status/1078363114666627072)
- End-to-End Testing(E2E testing, 端對端測試)
- 從使用者的角度(end)對系統(end)進行測試,看使用者能否接受系統
- 比起單元測試,在系統測試過程中發現的錯誤通常需花多時間修補
- User Acceptance Testing(使用者接受度測試)
#### 測試方法
- Black-Box Testing(黑箱測試)
- 測試者在不知道內部程式碼的情況下,測試輸入特定 input,產生特定 output
- 通常應用於軟體開發的後期,例如 UI 測試
- ![](https://i.imgur.com/2FYiR56.png =400x)
> [圖片來源](https://testerhome.com/topics/8594)
- White-Box Testing(白箱測試)
- 測試者在知道軟體內部程式的情況下,設計測試資料,例如驗證資料流的流動路徑(Data Flow),並正確輸出
- 通常應用於軟體開發的前期,單元測試、整合測試
- ![](https://i.imgur.com/OnncpP5.png =400x)
> [圖片來源](https://testerhome.com/topics/8594)
#### 軟體品質
- Functional Testinging(功能測試)
- 把軟體的各個功能劃分成不同測試項,且保證測試項涵蓋各種功能的組合,測試每個部份都正確運作
- Performance Testing(效能測試)
- 測試軟體是否能在特定工作負載下的達成它的目標並維持穩定性
- Stress Testing (壓力測試):不斷給予軟體壓力直到它發生系統癱瘓,找出軟體容量的極限在哪裡
- 例如一個網站設計容納是 100 個人同時點擊,壓力測試就要是採用 120 個同時點擊的條件測試
- Load Testing(負載測試):在軟體所能承受的範圍內,觀察不同負載下軟體的回應時間、占用的系統資源(如CPU、記憶體)等數據,觀察它是否能夠達到預期水準
- Usability Testing(可用性測試)
- 以使用者的角度來看,是否可以簡單地操作軟體
- Security Testing(安全性測試)
- 測試軟體的安全性並找出潛在風險
- 主要目的是找出軟體存在的安全隱患,並檢查它對攻擊的防範能力
#### 執行方法
- Dynamic Testing(動態測試)
- 實際去操作程式或軟體並驗證結果
- Static Testing(靜態測試)
- 不用實際去操作程式或軟體,用分析或檢查規格書、程式結構、source code、介面等來檢查軟體的正確性
### 手動測試
- 以人工的方式進行測試
- 手動測試最明確的案例是在 User Acceptance Testing
- 例如開發一個網站,就到網站上的各種按鈕進行手動點擊,確認流程無誤
- 優點
- 適用範圍廣
- 可以進行隨機測試
- 隨機測試(Random Testing):在熟悉軟體的各項功能的情況下,根據使用者的經驗及相關知識進行測試,有時會包括到之前沒有的測試案例
- 與自動化測試相比,初期投資便宜
- 缺點
- 無法重現
- 執行時間長(尤其是回歸測試)
- 回歸測試(Regression Testing):是指重複執行以前的全部或部分相同的測試工作
- 例如:在軟體新加入一個模組後,再對它和相關的其他模組測試一次
- 須配合測試人員的上班時間
- 一致性較低,人工測試總會有人為因素導致的一些錯誤,比自動測試不可靠
- 從長遠來看,成本比自動測試高
- 何時使用手動測試
- 當專案處於初始開發階段時,自動測試功能尚未完成前
- User Acceptance Testing
- 比較適用在短生命週期的產品
### 自動測試
- 使用支援自動化測試的工具來執行測試、比較結果並生成測試報告的過程。
- 例如:程式可以自動發 requests、執行功能
- 為什麼需要自動化測試
- 重複性的工作
- 有些測試對測試人員的技術要求較高(白箱測試)、有些技術要求較低
- 透過 UI 測試太慢
- 只能在特定環境執行
- 單元測試也是自動化測試 ( Test Automation ) 的核心
- 優點
- 提高效率,縮短開發時間
- 可重複使用
- 架構最佳化
- 為了之後可以引進自動測試,開發時工程師就會採用較良好的架構規劃(例如把外部 api 呼叫包裝起來)
- 可以在任何時間進行測試
- 相比人工測試,自動測試可以在任何時間進行,更容易避免與開發的衝突。
- 可以一邊開發一邊測試,盡早發現 bug 並解決
- 缺點
- 初始費用很昂貴
- 有局限性、無法進行隨機測試
- 自動測試只會測試程式寫到的內容
- 需要長期維護,版本疊代和功能變更
- 何時使用自動化測試
- 開發週期長的軟體
- 需要反覆多次進行且耗時的測試項目
- 例如:購物網站上重要的基本流程,需要定期檢查是否可以正確執行。
- 導入自動化後成本下降很多的測試項目
- 效能測試(如負載、效能、壓力測試)、單元測試、整合測試、回歸測試
:::info
**SaaS 與自動化測試**
隨著 SaaS(軟體即服務)變得越來越普遍,許多軟體產品以**長期、持續**的開發方式運作,每次版本更新都需進行相同或不同項目的測試,也增加了測試工時的增加。
測試的預算不與軟體規模成正比,而且在快速變化的用戶需求背景下,軟體更新週期也越來越短。
一方面時間和金錢都不夠,一方面測試項目愈來愈多,草率地進行測試可能導致軟體品質下降。此時就很適合導入測試自動化。
:::
### 手動測試 & 自動測試
- 引入自動化測試不代表取代人工測試
- 是否引入自動測試是一種取捨
### ![](https://i.imgur.com/IzHZDs4.png =40x) Selenium
- Selenium 一個開源的網頁應用程式自動化測試框架,也常常被用來做爬蟲。
- 自動化測試框架:框架簡單來說是一個「Rule Set」,已經制定好很多規則和功能,使用者只要遵循這些這些規則,就可以輕鬆寫出自動進行 UI 測試的程式。
- 測試環境
| 作業系統 | Linux、Windows、Solaris、Mac OS |
| -------- | -------- |
| 程式語言 | C#、Java、JavaScript、Python、Ruby、R 語言 |
| 瀏覽器 |Apple Safari、Google Chrome、Internet Explorer、Microsoft Edge、Mozilla Firefox |
- Selenium 是一群工具的總稱,由以下 3 個工具組成
![](https://i.imgur.com/CFrpT9P.png =400x)
1. Selenium WebDriver:讓測試程式可以像是真的使用者在使用瀏覽器的行為
2. Selenium IDE:一種輔助程式開發的應用軟體,讓不會寫程式的人可以用 IDE 寫測試程式
3. Selenium Grid:讓使用者可以同時在多個不同環境的瀏覽器執行測試
- 自動測試的流程
![](https://i.imgur.com/OaTK2N7.png =500x)
> [圖片來源](https://www.xdnote.com/selenium-phantomjs-chromeheadless%20/)
:::info
**Selenium WebDriver**
- Selenium Core(後來沒被再使用)
- 可以自動操作瀏覽器的 **JavaScript** 程式 javaScriptTestRunner。
- 開源後改名為**Selenium Core**
- 缺點:速度慢、不能在 FireFox 上使用(因為同源政策)
:::success
**同源政策(Same Origin Policy)**
![](https://i.imgur.com/GKPSnR9.png)
> [圖片來源](https://www.softwaretestinggenius.com/introduction-to-selenium-automation-tutorial/)
- 同源政策是瀏覽器的安全性政策,目的是保護使用者。
- 規定一般情況下只有「同源」的資源才可相互存取。
- 同源:通訊協定、Domain 跟 Port 一致的資源
![](https://i.imgur.com/HAFCrrY.png =300x)
> [圖片來源](https://medium.com/%E7%A8%8B%E5%BC%8F%E7%8C%BF%E5%90%83%E9%A6%99%E8%95%89/same-origin-policy-%E5%90%8C%E6%BA%90%E6%94%BF%E7%AD%96-%E4%B8%80%E5%88%87%E5%AE%89%E5%85%A8%E7%9A%84%E5%9F%BA%E7%A4%8E-36432565a226)
- 若測試員要測試一個網頁,他必須把網頁和測試程式放在同一個 server 上才行。
:::
- Selenium RC(後來沒被再使用)
- 為了繞過同源政策的問題 **Selenium RC**。
- 在測試程式和網站之間架一個 HTTP server 作為中間人
1. 攔截、驗證在瀏覽器和 AUT(Application Under Test)之間傳遞的 HTTP 消息,欺騙瀏覽器測試程式在同一個 domain -> 繞過同源政策
2. 將測試程式自動操作網頁元素的程式碼翻譯為 Javascript -> 可以用不同程式語言寫測試程式
![](https://i.imgur.com/N1uYmZq.png =200x)
> [圖片來源](https://www.journaldev.com/25395/what-is-selenium-introduction-to-selenium)
- 缺點:
- 程式的穩定性會依翻譯品質有落差
- 架構較複雜、執行速度慢
- WebDriver(後來沒被再使用)
- 利用瀏覽器**原生的 API**,另外包成測試 API
- 程式可以透過 WebDriver 直接操作網頁的元素,甚至操作瀏覽器本身(螢幕截圖,視窗大小,安裝插件之類的)。
![](https://i.imgur.com/3yYTcyX.png)
> [圖片來源](https://www.journaldev.com/25395/what-is-selenium-introduction-to-selenium)
- 速度和穩定性都大大提高
- 缺點:不同瀏覽器的原生 API 有些微不同,需要根據不同瀏覽器寫不同版本的 WebDriver。
- Selenium WebDriver
- Google 也有繼續開發 Selenium RC,在 2009 年 Google 測試自動化大會(GTAC)時相關開發人員決定將此項目和 WebDriver 合併,重新命名為 **Selenium WebDriver**。
:::
:::info
**Selenium IDE**
- 把 Selenium 的核心程式變成**瀏覽器插件**,可以將程式在網頁瀏覽器做過的動作錄製、回放(Capture/Replay)給使用者看。
:::
:::info
**Selenium Grid**
- Selenium Grid 能夠同時(平行)在多台機器、多個瀏覽器上測試,從而減少整體測試的執行時間 -> 大量測試
- 採用 Hub/Node 方式
![](https://i.imgur.com/OGZxwAd.png =400x)
> [圖片來源](https://javascript.plainenglish.io/docker-selenium-grid-setup-7e66d60a926a)
- Hub(集線器)
- Hub/Node 測試網路的中心計算機,接受運行使用者的測試請求
- 分配工作給符合測試條件的 Node 執行
- 一個 Grid 網路內只有一個 Hub
- Node(節點)
- Node 是與 Hub 連接的測試機(Test Machine),從 Hub 接收請求並運行測試
- 有不同作業系統和瀏覽器可以設定
- 一個 Grid 網路內可以有多個 Node
- 優點
- 用各種不同瀏覽器、作業系統和機器來進行測試
- 節省測試時間
- ![](https://i.imgur.com/FG2xInL.png =400x)
:::
#### 練習:自動開啟瀏覽器,搜尋「ncnu opensource」
- 下載 Firefox WebDriver
- `wget https://github.com/mozilla/geckodriver/releases/download/v0.31.0/geckodriver-v0.31.0-linux64.tar.gz
`
- 解壓縮
- `tar xvzf geckodriver-v0.31.0-linux64.tar.gz`
- 移動 geckodriver
- `sudo mv geckodriver /usr/bin/geckodriver`
- 安裝 python3 套件
- `pip3 install selenium`
- `touch test.py`
```python=
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.Firefox()
driver.get("https://www.google.com/") # 前往特定網址
element = driver.find_element_by_class_name("gLFyf.gsfi") # 取得搜尋框
element.send_keys("ncnu opensource") # 在搜尋框輸入"ncnu opensource"
element.send_keys(Keys.ENTER) # Enter 搜尋
time.sleep(3)
driver.close() # 關閉瀏覽器視窗
```
- `python3 test.py`
- [測試影片](https://drive.google.com/file/d/1Ekg5P8K_oIW6sJxAu-3Cz9-egybq_yeL/view?usp=sharing)
![](https://i.imgur.com/hbniD6S.png =500x)
:::info
**library 參考資料**
- [取得網頁元素](https://selenium-python.readthedocs.io/locating-elements.html#)
- [操作網頁元素](https://www.selenium.dev/selenium/docs/api/py/webdriver/selenium.webdriver.common.action_chains.html#module-selenium.webdriver.common.action_chains)
- [控制下拉選單](https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.select.html#module-selenium.webdriver.support.select)
- [檢查網頁元素](selenium.webdriver.support.expected_conditions)
- [等待](https://selenium-python.readthedocs.io/waits.html)
:::
## 自動化部署
- 部署:將測試完成的軟體更新到機器上並公開對外服務,包括設定環境變數、安裝套件等。
- 為什麼需要自動化部署?
- 每次部署軟體時,需安裝相關的工具和套件,有時還要進行個別微調,手動部署可能容易發生非預期的錯誤,自動化部署較可靠。
- 當大量部署時,可以減少部署時間和工作量。
- 幫助實現敏捷開發和 DevOps。
- 常見的自動化佈署工具例如:Ansible、Chef、Puppet。
:::info
**Ansible & Terraform**
- Ansible:已經有 VM 或實體機後,用於應用程式層自動部署,EX:網頁應用程式
- Terraform:主要用於基礎設施自動部署,EX:網域、VM、DATABASE、伺服器、網路、APACHE
![](https://i.imgur.com/kcQ4693.png =300x)
> [圖片來源](https://files.ifi.uzh.ch/ddis/emba/fois_Software_de_scorm/Software/de/html/le1_learningObject4.html)
**Cloud-init**
- 所有 Ubuntu 內建的工具。
- 可以在 VM 初次開機時就將想要的檔案或設定與系統一併弄好,不用手動處理。
- 例如:安裝軟體、新增 user、新增 SSH 金鑰檔案到某資料夾、設定系統變數等等。
:::
### Ansible
- Ansible 是開源的自動化部署工具,可以用來一次管理很多台機器、執行大量和重複的任務。
- 如果同時有多台機器,使用 Ansible 統一部署,可確保在部署過程中**參考同一組態檔案**,不會發生人為失誤或是不同部署人員部署順序不一致。
- 特色:
- :+1: Agentless(無客戶端):被 Ansible 管理的主機不用再安裝 Ansible
- 完全使用 SSH 與遠端 Server 溝通
- 使用 YAML 格式,較容易學習與維護
- Python API
:::info
**Ansible 的命名由來**
- 取自作者最喜歡的小說《安德的遊戲》。Ansible 是一種可以在太空中即時通訊的裝置,主角群用它對太空艦隊做統一指揮。
- 就像我們用 Ansible 管理大量的遠端伺服器。
:::
### Ansible 結構
![](https://i.imgur.com/jqYS6Tz.png =400x)
> [圖片來源](https://mansyruss.medium.com/ansible-%E6%8E%A8%E5%8B%95devops%E8%87%AA%E5%8B%95%E5%8C%96%E7%B3%BB%E7%B5%B1%E9%83%A8%E7%BD%B2%E5%B7%A5%E5%85%B7-56e997cab4e8)
- `Control node`:安裝了 Ansible 的機器,可以控制多台`Managed node`。
- `Managed node`:被 Ansible 管理的機器,可以是多台不同作業系統的機器。
| | Python | Ansible | SSH |
| ------ | ------ | ---- |-|
| Control node |V|V|V|
| Managed node |V| |V|
- `config`:Ansible 設定檔,例如去哪裡讀哪些檔案、defalt 設定。
- 檔案位置 `/etc/ansible/ansible.cfg`
- [ansible.cfg 參數](https://github.com/ansible/ansible/blob/stable-2.9/examples/ansible.cfg)
- `Inventory`
1. 定義和分類有哪些 `managed node` 需要管理
2. 設定 `managed node` 相關參數,例如 SSH port 號、登入名稱等等
- 檔案位置 `/etc/ansible/hosts`
- [Ansible Inventory 相關參數](https://chusiang.github.io/ansible-docs-translate/intro_inventory.html)
- `Modules(模組)`:很多個小程式,讓 `Managed node` 可以執行命令。
- 檔案位置`/usr/share/ansible/`
- EX:Command Module、Copy Module
- [Ansible Module 清單](https://docs.ansible.com/ansible/2.8/modules/modules_by_category.html)
- `Playbooks`:自動化的腳本。
- 檔案位置 `/etc/ansible/`
- `Roles`:模組化管理 Playbooks 的目錄。
- 檔案位置 `/etc/ansible/roles`
```txt=
roles/
common/ # role name
tasks/
main.yml # <-- tasks file can include smaller files if warranted
handlers/
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/
main.yml # <-- variables associated with this role
defaults/
main.yml # <-- default lower priority variables for this role
meta/
main.yml # <-- role dependencies
```
:::info
**常用的 Ansible Modules**
| 模組名 | 種類 | 概要 | 對應 linux 指令功能|
| ----- | -------- | -------- |--|
| Command Module | 指令模組 | 在遠端上執行 Shell 的指令,但不支援變數($HOME, $PATH 等)和 特殊符號(`;` `>` `<` `&`)。執行指令時不會受到使用者的環境變數影響,因此較安全。|Shell 指令|
|Shell Module|指令模組|在遠端執行 Shell 指令,支援變數和特殊符號。|Shell 指令|
|Copy Module|檔案模組|複製本地的檔案到遠端|scp|
|File Module|檔案模組|在遠端建立和刪除檔案、目錄 、軟連結|chown, ln, mkdir, touch|
|Stat Module|檔案模組|檢查檔案狀態|stat|
|Service Module|系統模組|管理遠端系統服務|service|
|Ping Module|系統模組|測試遠端主機是否可以本機溝通,如果連線正常會回傳一個 "pong" 訊息|-|
:::
### 使用方式
#### 1. Ad-Hoc 命令
- 單行命令,一次只能做一件事情
- 快速又簡單
- 不能重複使用
- 適合用於一些臨時任務,例如: ping、複製文件、短暫關閉遠端伺服器
- 指令`/usr/bin/ansible <host-pattern> [-m module_name] `
- `<host-pattern>`:要被控制的遠端主機。可以指定單一台主機名、群組名、全部(all)或本機(localhost)
- `-m`:要執行的功能(Module),預設 Command Module
- `-a`:命令內容
- `-f`:一次同時執行幾台遠端主機,預設 fork = 5
- `-u`:ssh 連線的帳號
- `-i`:指定 inventory 檔案路徑,預設`/etc/ansible/hosts`
- `-v`:顯示詳細資訊
- `-T`:ssh 連線超時時間,默認是 10 秒
- `--list-host`:印出有哪些主機會執行這個命令,但不會實際執行
- `/usr/bin/ansible-doc <module_name>`:查看 Module 功能
- `/usr/bin/ansible-playbook <filename.yml> `:執行 Playbook
- `-C` `--check`:測試 playbook,但不會真的執行
- `--ask-become-pass`:輸入 become_user 密碼
:::info
**Ad-hoc**:來自拉丁文,通常用來形容為一個特定的任務而專門設定的解決方案。
:::
#### 2. Playbooks
- 事先將一連串操作寫成腳本,之後可以一鍵執行
- 可重複執行
- 適合用於部署複雜的系統環境,例如部署資料庫、建立 webserver
- [Ansible Playbook 的變數](https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html#playbook-keywords)
- Playbook 組成
1. `Playbook:一個檔案(`.yml`),一個 Playbook 中可以有很多 Plays
2. `Play`:將 host 和 task 連接起來,一個 Play 中可以有很多 Tasks
3. `Task`:是工作的最基本單位,一個 Task 會 call 一個 module 做一件事
![](https://i.imgur.com/QWhKdQb.png =300x)
> [圖片來源](https://itopic.org/ansible-2.html)
#### 命令在遠端執行的過程
1. 本機 Terminal 輸入 Ad-Hoc 命令集或執行 Playbooks
2. 將 Playbooks 拆解為 Plays
3. 將 Play 組織成 Ansible 可以識別的任務
4. 呼叫任務涉及的模組和外掛
5. 去 Inventory 找要變更的遠端主機
6. 將任務以臨時檔案或命令的形式,SSH 傳輸到遠端主機
7. 執行並返回執行結果
8. (如果過程中有產生臨時檔案,自動刪除)
:::info
**執行結果的顏色**
- 紅色:執行失敗
- 綠色:執行成功但遠端主機沒被改變
- 黃色:執行成功且遠端主機有被改變
- 洋紅色:警告訊息
:::
:::info
**YAML 格式**
1. 檔案第一行都是 `---` 表明一個檔案的開始
2. `#` 表示註釋
3. `<鍵>: <值>` :物件
4. `- <值>` :陣列元素
5. 同一個層次的元素保持相同的縮排
6. `{{ <variable> }}` 引用變數
```yaml=
---
# 一位職工記錄
name: Example Developer
job: Developer
skill: Elite
employed: True
foods:
- Apple
- Orange
# 也可以寫 foods: [Apple, Orange]
languages:
ruby: Elite
python: Elite
dotnet: Lame
# 也可以寫
# languages: {ruby: Elite, python: Elite, sdotnet: Lamedotnet: Lame}
```
:::
### 實做
#### 本機安裝 Ansible
- `sudo apt update`
- `sudo apt upgrade`
- `sudo apt install software-properties-common`
- `sudo add-apt-repository --yes --update ppa:ansible/ansible`
- `sudo apt install ansible`
- `ansible --version`
- 安裝 OpenSSH server
#### SSH
- 本機和遠端主機都安裝 SSH
- `sudo apt install openssh-server`
- 在本機生成使用者金鑰
> Ansible 預設使用金鑰登入
- `ssh-keygen -t rsa`
- 複製到遠端主機
- `ssh-copy-id -i ~/.ssh/id_rsa.pub <user>@<ip>`
:::info
設定登入遠端主機 SSH user 有 4 種方式:
1. `ansible.cfg`:inventory 中全部主機都用同一使用者登入
```txt=
remote_user = <使用者帳號>
```
2. `hosts`:每台主機分別設定使用者 :+1:
> 讓 Playbooks 主要邏輯
```txt=
vm1 ansible_ssh_host=192.168.1.127 ansible_user=<使用者帳號>
```
3. `Playbooks`:每個 Task 分別設定使用者
```txt=
---
- name: update webservers
hosts: webservers
remote_user: <使用者帳號>
tasks:
- name: thing to do first in this playbook
. . .
```
4. ansible 指令:每次下 Ad-Hoc 指令時設定使用者
```shell=
ansible ... -u <使用者帳號>
```
:::
#### 編寫 Ansible 設定檔
- `cd /etc/ansible`
- `sudo vim ansible.cfg`
```txt=
[defaults]
inventory = /etc/ansible/hosts
roles_path = /etc/ansible/roles
library = /usr/share/ansible
remote_user = <使用者>
```
- `sudo vim hosts`
```txt=
# 定義一個 webserver 類別
[webserver]
<代號> ansible_ssh_host=<遠端主機 IP>
#vm1 ansible_ssh_host=192.168.1.127
#vm2 ansible_ssh_host=192.168.1.131
```
#### Command Module:echo
:::info
**Ping Module**
- **和 ICMP 的 ping 不同**,只是一個測試連線的功能,不會發送封包出去。
- 測試連線:
1. 網路是否連通
2. SSH 帳號登錄和權限
3. Python 環境是否能運行 playbook
- 若以上條件皆符合,遠端主機會回傳 "Pong"
:::
- (Ad-Hoc)在本機印出 Hello World
- `ansible localhost -m command -a 'echo Hello World.'`
![](https://i.imgur.com/pQznri2.png =500x)
#### Ping Module
- (Ad-Hoc)確認本機和遠端主機連線正常
- `ansible all -m ping`
![](https://i.imgur.com/s357fFH.png =400x)
> webserver、vm1
- `ansible all -m ping --limit vm1`
![](https://i.imgur.com/qzHaB1L.png =500x)
- `ansible all -m ping -v --limit vm1`
![](https://i.imgur.com/mhj0bH8.png =500x)
- (Playbook)確認本機和遠端主機連線正常
- `sudo vim playbook.yml`
```yml=
---
- hosts: webserver
tasks:
# task 1
- name: test connection
ping:
register: message
# task 2
- name: print debug message
debug:
msg: "{{ message }}"
```
- `ansible-playbook -C playbook.yml` 測試 playbook.yml
- `ansible-playbook playbook.yml` 執行 playbook.yml
![](https://i.imgur.com/cU330aw.png =500x)
#### Setup Module:取得遠端主機有關的裝置資訊
- (Ad-Hoc)取得遠端主機有關的裝置資訊
- `ansible all -m setup`
- `ansible all -m setup -a "filter=ansible_distribution*"`
#### 安裝 Nginx
- (Playbook)在遠端主機安裝 Nginx
- `sudo vim install.yml`
```yaml=
---
- hosts: webserver
become: true
tasks:
- name: install nginx
apt:
name: nginx
state: latest
```
- `ansible-playbook install.yml --ask-become-pass`
- 輸入密碼
- 瀏覽器訪問 `http://<遠端主機 IP>:80`
![](https://i.imgur.com/0MBPOlU.png =400x)
- (Playbook)上傳 `index.html` 到遠端主機並重啟
- `sudo vim index.html`
```html=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello</title>
</head>
<body style="background-color: rgb(211, 211, 217);">
<h1 style="color: cornflowerblue; text-align: center;">123456789</h1>
</body>
</html>
```
- `sudo vim install.yml`
```yaml=
---
#install nginx
# ...
- name: edit index.html
copy:
src: /etc/ansible/index.html
dest: /var/www/html/index.nginx-debian.html
- name: restart nginx
service:
name: nginx
state: restarted
enabled: yes
```
- `ansible-playbook index.yml --ask-become-pass `
- 瀏覽器訪問 `http://<遠端主機 IP>:80`
![](https://i.imgur.com/BFew5CD.png)
:::info
**NOPASSWD**
- `/etc/sudoers` 中可以設定那些使用者執行指令時不需要輸入 sudo 密碼。
- 參數:`NOPASSWD`
- 指令:`sudoers`
- 大量部署 VM 時,可用 Cloud-init 設定。
:::
#### 將部署 Nginx 的流程變成一個 Role
```txt=
/etc/ansible/
├── ansible.cfg
├── hosts
├── index.html
├── playbook_webserver.yml
├── roles
│ └── webserver
│ └── tasks
└────────────└── main.yml
```
- `cd roles`
- `sudo mkdir webserver`
- `sudo mkdir ./webserver/tasks`
- `sudo vim ./webserver/tasks/main.yml`
```yaml=
---
- name: install nginx
apt:
name: nginx
state: latest
- name: edit index.html
copy:
src: /etc/ansible/index.html
dest: /var/www/html/index.nginx-debian.html
- name: restart nginx
service:
name: nginx
state: restarted
enabled: yes
```
- `cd /etc/ansible`
- `sudo vim playbook_webserver.yml`
```yaml=
---
- hosts: webserver
roles:
- { role: webserver, become: yes }
```
- `ansible-playbook playbook_webserver.yml --ask-become-pass `