###### 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 `