###### tags: `lsa`
# 自動化、DevOps、GitHub Actions
[TOC]
## 自動化介紹
- 什麼是自動化
- 減少人類付出勞力以及介入的機會
- 利用軟體代替人力不斷重複執行任務
### 自動化特點
- 節省時間
> 機器不需要休息,日夜操它就對了
<!-- > 如果軟體都用人工進行測試,錯誤修正後,可能又要重新打指令進行測試,這無形中會浪費很多時間 -->
- 減少錯誤,提高效率
> 人會勞累就會分神,機器不會累,會按照指示一直做
<!-- > 在測試的過程中,人可能沒有發現到更改過後的程式碼和其他地方有衝突,而此時若有自動化測試則會將所有要測試的條件執行一次(比如以前有執行過得測試),有時候會發現意想不到的錯誤出現 -->
- 增加產品改良時間
> 有些開發流程已經改為自動化(ex:整合),公司可能將原本多派的測試人員改為培訓開發人員,有更多的人力開發或是改良產品
- 相對於人類的大腦,機器缺乏彈性
> 簡單的機器畢竟不是人類的大腦,所以只會按照指示工作。人類有大腦可以在做的過程中思考,機器不會變通。
<!-- - 初始投入成本高(和人工測試比較)
> 自動化需要有技術人員幫忙開發,還沒看到成品就要投入資金發展技術,也需要有人進行維護,確保目前的測試是目前的需求。 -->
### 自動化舉例
- 送餐機器人

> [圖片來源](https://vickylife.com/powerofmeat/)
- 自動包裝機

> [圖片來源](http://www.packviwell.com.tw/front/bin/ptdetail.phtml?Part=EK-203&Category=2517)
- 搬運機器

> [圖片來源---00:18](https://www.youtube.com/watch?v=QkwktcQBz1k)
- 室內養殖循環系統

> [圖片來源](https://www.kenmec.com/tw/product_category.aspx?id=34)
<!-- - 無人商店
 -->
- 測試腳本
:::info
#### 測試腳本是什麼?
用來測試程式的一系列指令,模擬人工自動測試軟體,檢查軟體或應用程式的功能、穩定性等等。
#### 為什麼要有測試腳本
- 減少手動測試的麻煩
- 減少測試時間
- 完整測試軟體或應用程式的功能
:::
## 軟體開發生命週期(Software Development Life Cycle,SDLC)
> [參考資料](https://aws.amazon.com/tw/what-is/sdlc/)
> [參考資料](https://colearning.magnific.biz/SDLC-Overview/)
#### SDLC 介紹
SDLC 是一個管理框架,定義軟體開發的每個階段需要做到哪些任務,讓開發團隊以及客戶清楚地了解整體設計、開發。
<!-- 客戶的需求有千萬種,一個專案可能需要多種職位一起開發討論,合作的對象不只一個人,跨職協作、技術交流、軟體開發等,對象太多可能難管理,有 SDLC 更容易讓大家分攤工作。 -->
#### SDLC 特點
- 事先規劃,降低專案風險
- 有系統的交待開發狀況,提高開發流程的可視度
### SDLC 流程圖

> [圖片來源](https://nix-united.com/blog/software-development-life-cycle-nix-approach-to-sdlc/)
- Strategy
- 做需求訪問,並製作文件,以確定開發範圍和目標
- 設定系統的目標和討論可能會遇到的問題
- Design
- 依照上一步的需求文件做設計,規劃 Wireframe/UI/UX、DB、系統架構、流程等
- 讓使用者以及團隊清楚了解系統操作和呈現
- Development
- 依照前面設計的系統架構實做出來
- Testing
- 對開發完的軟體進行偵錯
- 確保系統發布後可以穩定正常運行
- 逐一檢測是否符合客戶的需求
- Deployment
- 最終的軟體會放到伺服器,發布給使用者
- 可以有許多複本,且每一個是獨立的
- 使用者和開發者可以不同步
> 開發者以及使用者分別為 A、B,A、B兩個相同環境,開發人員在 A 進行升級、不影響 B
:::info
**Alpha**
- 使用對象:開發團隊
- 僅提供內部使用
- 把所有新舊功能全部測試一次
**Beta**
- 使用對象:任何人
- 對外開放
- 提供使用者實際使用,收集使用者的 Feedback
:::
<!-- > FIXME: [了解 Alpha、Beta 版本的測試環境](https://zh.wikipedia.org/zh-tw/%E8%BB%9F%E4%BB%B6%E7%89%88%E6%9C%AC%E9%80%B1%E6%9C%9F) -->
- Maintenance
- 產品發布後,若使用者有問題就需要有維運人員更正錯誤或是監控軟體運作是否有異常
### 瀑布式開發

> [圖片來源](https://medium.com/pm-explorer/%E7%94%A2%E5%93%81%E9%96%8B%E7%99%BC%E5%BE%9E%E7%80%91%E5%B8%83%E5%88%B0%E6%95%8F%E6%8D%B7-72bc758b5798)
- 最傳統的軟體開發模型
- 事前會詳細討論每一階段完成的任務,規劃整體系統架構
- 優點
- 架構明確,清楚定義每一個階段任務
- 每一個階段的開發環境、預期目標、流程等等都會用文件紀錄
- 事前會將所有階段詳細且清楚的規劃,開始進行就可依照規劃快速進行
- 缺點
- 市場驗證時間長
- 軟體開發完後,客戶才會在最後看到成果
> 一年後開發完結果客戶覺得現在不需要,那團隊可能會陷入尷尬的情況 :money_with_wings:
- 沒有彈性
- 開發中途客戶無法臨時增加需求
- 開發團隊開發過程中發現規劃有誤或與客戶的要求不符合
- 規劃不完整,重新開發的機會高
- 開發團隊會依照先前的計畫開發,過程不允許變更,一旦發現需要修正起初規劃的計劃書,開發團隊會退回規劃(Plan)的階段重新規劃
- 使用時機
- 客戶的項目要求不會改變
- 開發大型專案
### 敏捷式開發

> [圖片來源](https://ithelp.ithome.com.tw/articles/10281464?sc=iThomeR)
- 為什麼會發展
- 為了因應時代的步調,解決開發過程中需求快速變化的問題,因此有了敏捷式開發
- 開發過程會分成多個子項目,每一個子項目就會規劃一個週期分別獨立運行
- 敏捷 != 快,不是讓開發的過程變快,而是短時間可以調整客戶需求
- 敏捷宣言(Agile Manifesto)
* 個人與互動 重於 流程與工具
> 強調團隊間的交流,如果有成員在開發的過程中有遇到問題或是找到更好的工具,可以討論是否要更動
* 可用的軟體 重於 詳盡的文件
> 強調盡早完成產品,完成後,團隊多花一些時間來開發產品功能
* 與客戶合作 重於 合約協商
> 強調持續與客戶溝通,定期與客戶討論功能或產品呈現是否符合客戶的預期
* 回應變化 重於 遵循計劃
> 強調價值,開發的過程中發現客戶需求改變,即時修改專案
> [四大宣言](https://agilemanifesto.org/iso/zhcht/manifesto.html)
- 優點
- 開發前不需要嚴格的規劃
- 彈性高
- 客戶臨時要求新增功能
- 風險較低
- 專案裡的一個功能完成,就可以直接呈現給客戶看,若有誤可臨時更正
- 缺點
- 文件少
- 若開發團隊的流動性高,開發的文件過少,新人加入團隊時,缺少可以參考、理解的工具,會導致專案開發時間可能超出預期
### 瀑布式開發 vs 敏捷式開發
| | Waterfall | Agile |
| - | --------- | ----- |
|事前規劃| :eyeglasses: 嚴格 |  鬆散 |
| 彈性 | :arrow_down: 低 | :arrow_up: 高 |
| 風險 | :arrow_up: 高 | :arrow_down: 低 |
| 團隊模式 | :page_facing_up: 有文件詳盡規劃 | :chart_with_upwards_trend: 過程中學習 |
| 行為導向 | :face_with_monocle: 預測導向 | :money_mouth_face: 價值導向|
<!--  -->
## DevOps
- 開發團隊和維運團隊的衝突
- Developers:
- 目標:會因應客戶需求,時常更動軟體並更新版本
- 文件轉交:轉交給維運團隊的部署相關的文件內容不夠充分
- 軟體執行的環境不同:軟體會在開發環境、測試環境執行
- Operators
- 目標:因為怕更新後, 導致系統不穩定,所以會盡量阻擋版本更新
- 文件轉交:看文件需要長時間摸索,或者是看不懂而不知道軟體如何執行
- 軟體執行環境不同:會將測試完成的軟體部屬到生產環境
:::spoiler

:::
### 如何興起
在 DevOps 模式興起前,整體的 SDLC 的步驟會分別由不同團隊進行,例如:開發團隊負責將計畫實踐、編寫、測試軟體,維運團隊負責處理、檢查軟體發布後的 bug。團隊之間在瀑布式開發的模式下,階段任務是分開的,不同團隊有不同的目標,彼此的資訊也不共享,時常會有資訊不對稱的狀況發生,因而造成衝突的發生。
在敏捷式開發興起,Patrick Debois 參加了資料中心遷移的計畫,而當時他作為系統管理員,在工作的過程中,覺得開發團隊與維運團隊協調間有各種衝突,導致軟體沒辦法速部署發佈,於是 Patrick Debois 與 Andrew Clay Shafer 將 **持續整合(Continuous Integration)** 和 **持續部署Continuous deployment** 的概念加入到 DevOps
> FIXME: [Dev_Ops 念法](https://www.youtube.com/watch?v=047kkVHp2lc&ab_channel=GlossikaTaiwan)
### 定義
- 是一種模式
- DevOps 組成為 **dev**elopment and **op**eration**s**
- 目的:減少開發團隊和維運團隊的衝突
### 解決哪些問題
- 盡量消除任何減慢開發過程的機會
- 透過自動化減少人工執行時間,讓團隊專心在修改專案
- 自動化可反覆確認軟體是否有問題,減少人工錯誤
- 提升偵錯的速度
- 減少變動軟體範圍,增加更新速度
- 少量並頻繁更新,使團隊縮小偵錯範圍
- 減少開發團隊與維運團隊的衝突
### 流程

> [圖片來源](https://hackmd.io/@kennychenfight/ByhWV1tvI)
- Plan
- 做需求文件
- 將專案分成多個子項目
- Code
- 產出程式碼,進行開發
- Build
- 檢查模組之間有沒有衝突或程式碼所需要的版本是否正確
- 將程式碼打包成執行檔
<!-- > compile dependence
> 打包成zip或是docker等等 -->
- Test
- 執行看看有沒有 bug
- 查看軟體有沒有符合需求
- Release
- 軟體同時間有多種版本,選定一種作為最後釋出給使用者的版本
- Deploy
- 將軟體移動到生產環境
- Operate
- 系統上線後,需要搭配的硬體設備、備份等
- Monitor
- 監控軟體在運作和發佈的過程是否有異常
- 可以觀察到某些套件或是軟體特定版本的問題
- 接收客戶的回饋,觀察使用者的使用體驗
> [參考資料](https://blog.hubspot.com/website/devops-pipeline)
### 可自動化的步驟

<!--  -->
<!-- > [圖片參考](https://www.primotech.com/blog/ci-cd-pipeline-everything-you-need-to-know/) -->
* 持續整合 Continuous Integration
* 定期整合開發團隊的軟體
* 持續交付 Continuous Delivery
* **手動**將軟體移動到生產環境
* 持續部署 Continuous Deployment
* 將軟體**自動**更新到生產環境
* 維運
* 維護並監視軟體的運作、log,有問題即時通報
- 目的
- 透過自動化工具,降低人工花費的時間
### 協助工具
| 流程 | 協助工具舉例 |
| -------- | -------- |
| Plan | X |
| Code | GitHub |
| Build | Maven、Gradle、npm |
| Test | Selenuim、JUnit |
| Release | X |
| Deploy&Operate| Ansible、Docker、Kubernetes |
| Monitor | Nagios、Splunk |
### 持續整合(Continuous Integration,CI)
- 流程
- 程式開發: 紀錄每一位成員的更新以及整合後的結果,如果與其他成員的程式有衝突可以立即得知
- 自動建置:讓成員可以將最新整合的程式碼進行建置
- 自動測試:讓成員可以在本機執行指令,確保專案的功能是否有 bug 或符合客戶需求
- 目的
- 提升整體的一致性
- 可以隨時 pull 其他人更新後的程式碼
- 可以隨時 push 到遠端伺服器,和其他人的程式碼一起整合
- 資訊透明化
- 可以隨時看其他人新增、修改、刪除檔案的紀錄
- 及早發現
- 每一次更新,就會整合程式碼進行測試,查看是不是和其他人有衝突
- 減少人工錯誤
- 打指令可能打錯
- 忘記安裝套件
- 減少風險
### 持續交付(Continuous Delivery, CD)/持續佈署(Continuous Deployment,CD)

> [圖片來源](https://newrelic.com/blog/best-practices/continuous-delivery-continuous-deployment-continuous-integration)
- 兩者差異
- 持續交付的部署會先部署到測試環境,再依照開發人員的決定手動更新到生產環境
<!-- > [參考資料](https://aws.amazon.com/tw/devops/continuous-delivery/) -->
- 流程
- 自動部署:透過自動化的方式,將寫好的軟體更新到 server 並對外公開
- 環境配置:將所有需要的工具加入(硬體設備、備份等)
- 自動偵測、監控:駭客攻擊漏洞或使用者反饋,系統自動回報
- 目的
- 可以預先將軟體快速部署到測試環境,觀察是否有問題
- 快速將軟體部署到生產環境
- 以最快的速度將最新版的專案送到使用者手上
<!-- ## 自動化部署
- 部署的工作
- 安裝專案所需的相關套件
- 維運團隊將開發團隊測試好的專案放到正式機,提供終端使用者使用
- 為什麼需要自動化部署
- 部屬的工作在每次更新都會重複執行,每次需要設定環境配置,若專案大,要安裝的時間就會提升,自動化可以不用讓成員守在電腦前安裝
- 開發人員將打包、測試完的專案丟給維運人員,安裝套件或是配置環境,手動部署可能容易發生非預期的錯誤
- 每一個人安裝的套件版本可能都會有微小的差別
- 目的
- 減少重複性質的工作
- 提升效率,加速部署過程
- 減少出包
- 提升一致性
- 縮短反饋時間 -->
<!--  -->
## 實做
### Git
- 什麼是 Git
- 分散式的版本控制系統
<!-- 
> [圖片來源](https://gitbook.tw/chapters/introduction/what-is-git) -->
- 特色
- 版本控制
- 紀錄每一位成員的歷史紀錄
- 可隨時切換任意時間點的版本
- 檔案體積小
- 用 snapshot 做紀錄
- 紀錄每一個版本間的差異
- 分散式架構
- 成員在本機就可以操作
> 開發時,不需要登入遠端伺服器,當要 pull 或是 push 的時候,才需要登入執行
- trunk-based 開發
- 分支可獨立測試
> 最後確定功能要新增到 main branch,就可以將分支整合到 main,避免新開發的功能不理想或是有 bug,影響到整體專案
:::info
#### 集中式
大家新增修改刪除檔案的操作在同一台 Server,有人更改 A 檔案,同一時間其他人不能修改
#### 分散式
大家各自在自己的本機上新增修改刪除,因為是在自己的主機上操作,所以同一時間可以更動同一份
| | 集中式 | 分散式 |
| -------- | -------- | -------- |
| 操作方式 | 登入 Server | 大部分在本機操作 |
| 使否要網路 | 是 | 否 (只有 pull push 才會需要網路) |
| 系統 | CVS | Git|
:::
### GitHub
- 甚麼是 GitHub
- 基於 Git 創造的伺服器
- 存放 source code 的地方
- 特色
- 儲存檔案
- 程式碼同步
- 可以直接在本機操作執行並整合到遠端,讓其他 Contributers 同步
- 倉庫許可證(licensed warehouse):開發者不一定要遵守自由軟體標準,可以自己定義 `LICENSE`

> [圖片來源](https://github.com/hubotio/hubot/blob/master/LICENSE.md)
:::info
#### 自由軟體
任何人可以不受限制的使用、複製、修改等,使用者使用後不會收取任何費用,且開發後的軟體可以作為商業使用。
:::
### GitHub Actions
- 什麼是 GitHub Actions
- 是一個持續整合/持續部屬開發平台
- 可以自己定義軟體開發流程並自動執行
- 特色
- 有第三方工作流程可以直接使用

- 可以自己定義 `.yml` 做自動化建置、部署
- GitHub 提供三種不同系統(Windows、Ubuntu、MacOS)的虛擬機可以執行程式,也可以使用自己的主機執行程式
:::info
**虛擬機(VM)**
- 什麼是 VM
- 模擬實體電腦
- 當執行的時候,將實體的電腦資源(CPU, RAM, 網路卡...)借一部分來用
- VM 的優點
- 會影分身:一個實體機器可以創無限多個 VM
- 安全性佳:獨立的一個環境,若發生問題不會影響實體的機台
:::
<!-- > [資料來源](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) -->
<!-- - 可以做到哪些事
- 可以設定觸發事件,自動執行 CI/CD
- 持續整合 Code -> Build -> Test
- 持續部屬 Test -> Deploy

> 原本建置、測試、部署要一一設定環境,工具一多,環境會變雜亂 -->
### GitHub Action 架構

#### Event
觸動自動化流程的契機
- pull request
- push
- delete
...
~~ 範例 ~~
```cmd=
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
delete:
branches: [ "main" ]
```
> [取得更多資訊](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)
#### Jobs
<!-- 
> [圖片來源](https://ithelp.ithome.com.tw/articles/10262377) -->
<!-- 
> [圖片來源](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) -->
- Job
- 同一個環境下執行設定的 Step
- 一系列的 Job,只要中間有執行錯誤,就會立即終止
- Step
- 可以下指令或 Action
- 按照順序進行
- 同一個 Job 裡面運許所有的 Action 共享資料
- Action
- 可以將自己寫的流程發布到 marketplace

- 內容可以自己編寫或在 marketplace 引用其他人已經寫好的 Action
<!-- ### 安裝
- 安裝 Git
```terminel=
sudo apt-get install git
```
- -->
#### Runner
- 代理程式,執行程式的 Server
- GitHub 提供三種不同的虛擬機(Ubuntu, Windows, MacOS)也可以自行架設
### 關於 .yml
<!--  -->
<!--  -->
- `.yml` 存放位置 `./.github/workflows`

> 會將所有的觸發事件、流程步驟等寫在 `.yml` 裡面
#### .yml 組成元件

- `name` 流程名稱
- `on` 做哪些事情會觸發 `<yaml file>`
- `branch` 要在哪個 branch 才可以觸發事件
> [更多觸發事件](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push)
- `workflow_dispatch` 手動觸發的條件
- `runs-on` 要跑在哪一種系統上 (Unbuntu, Windows, macOs)
:::spoiler 系統參考

> [圖片來源](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners)
:::
- `jobs` workflow 要做哪些工作
- `<job name>` 工作名稱(名稱自由取)
- `steps` 工作的步驟
- `name` 項目名稱
- `run` 要跑的指令
- `uses` 引用要執行的 Action
> 第一步:將 Job 要執行的工作放到 Runner 執行,通常 GitHub 已經寫好,直接引用就可以了
> [以下範例的引用來源](https://github.com/actions/checkout)
### Runner 如何和 GitHub 溝通
Runner 使用 long poll 與 GitHub 進行溝通
<!-- 
> [圖片來源](https://www.pubnub.com/blog/http-long-polling/) -->

> [圖片來源](https://www.educative.io/answers/what-is-http-long-polling)
> [參考資料](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners)
1. `Client(Runner)` 發出請求
2. `Server(GitHub)` 接收請求後,如果身上有 `Client` 想要的資料,會立即發 response 回覆
3. `Client` 收到 `response` 立即處理資訊,然後發送再發送 `request`
4. `Server` 接收到 `response`,因為沒有資料,所以不會回覆 `response`,並持續與 `Client` 保持通訊
<!-- :::success
Runner 與 GitHub 連接 50 秒,GitHub 都沒有 response 給 Runner 的話,GitHub 會回覆 Response 告訴 Runner 沒有資料更新,Runner 接收後再重新發 request 給 GitHub

::: -->
**建立 GitHub repo**
- 建立 repo

- 初始化 repo
```cmd=
echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin <your repo location>
git push -u origin main
```
### 自建 runner
- 使用 GitHub 提供的 runner
- 有時數限制
> GitHub 提供的 runner 會有時數限制,超過就要付費
- 每一次執行結束都會清空 runner 的所有資料
> 100個文件要下載到 ruuner, 10 分鐘後要再執行一次,就要全部重新下載
- 硬體是固定的
> 官方設備觸及不了,團隊無法因應軟體需求新增硬體設備(CPU、RAM 等)
- 到 GitHub 新增的專案,點擊 Settings :arrow_right: Runners

- 點擊 New self-hosted runner

- 建立資料夾,並到剛剛創建的資料夾下
```cmd=
mkdir actions-runner && cd actions-runner
```
- 安裝 curl
```cmd=
sudo apt install curl
```
- 下載最新的 runner package
```cmd=
curl -o actions-runner-linux-x64-2.303.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.303.0/actions-runner-linux-x64-2.303.0.tar.gz
```
<!-- 
 -->
<!-- - 驗證??
```cmd=
echo "e4a9fb7269c1a156eb5d5369232d0cd62e06bec2fd2b321600e85ac914a9cc73 actions-runner-linux-x64-2.303.0.tar.gz" | shasum -a 256 -c
``` -->
- 解壓縮
```cmd=
tar xzf ./actions-runner-linux-x64-2.303.0.tar.gz
```

- 建立 runner
```cmd=
./config.sh --url https://github.com/ChiHua0918/learnActions --token <token>
```
- 每一次新增 runner,GitHub 會給一串 token,複製系統給予的 token 即可進行配置。token 有時效限制,如果網頁停滯一段時間 token 會失效,當失效的時候,重整頁面即可取得新的 token。

- runner 設定


- `group` 設定只可以運用在哪些專案上
- `Enter the name of runner` 為 runner 取名字
- `Enter additional labels` 可以幫 runner 新增自己的標籤,如果要設 2 個以上的標籤,必須用逗號隔開,而且中間不可以有任何空格
- Github 會幫 runner 預設 `self-hosted`, `Linux`, `X64` 三個預設的標籤
- `Enter name of work folder` runner 要執行的的資料夾(預設資料夾是 `_work`)
- 執行 runner
```cmd=
./run.sh
```
> 如果要使用自己架設的 runner 執行 GitHub Action 的 workflow,要記得先將剛剛創建好的 runner 執行
<!-- https://docs.github.com/en/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service -->
<!-- - 可以自己定義標籤,在寫 `.yml` 檔案的時候,會依照標籤選 runner
```yml=
runs-on: [self-hosted, linux]
```
- `self-hosted` 自己建的 runner。
- `linux` runner with Linux。
-->
#### 簡單實做 CI workflow
- `main.yml` 範例
:::spoiler 程式碼
```yml=
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "try"
try:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# runs-on: self-hosted
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
# - uses: ./github/workflow/test.yml
# Runs a single command using the runners shell
- name: Run a one-line script
run: echo Hello, world!
# Runs a set of commands using the runners shell
- name: Run a multi-line script
run: |
echo simple test,
echo It is great.
- name: test input a name string
run: python3 test.py Amber
```
:::
- `test.py`
:::spoiler 程式碼
```python=
import sys
def printName(name):
print("My name is",name)
def main(name):
print("dd")
printName(name)
if __name__ == "__main__":
main(sys.argv[1])
```
:::
- 紅色為執行失敗,綠色為執行成功,黃色代表執行中

### Secrets
<!-- #### 註冊 GitHub Token
- 到 Settings

- 左邊欄位最下面,點擊 Developer Settings

- 左邊欄位點擊 Personal access tokens ,再按下 Generate new token

- 設定名字、有效期限、可以運用在哪些專案
 -->
#### 在 GitHub 設定 Secrets
:::warning
**為什麼需要 Secrets**
有時候要在 `.yml` 中執行登入,就在 `.yml` 中輸入帳號密碼,但如果目前的 repo 是公開的,大家都可以直接看到此人的帳號密碼是什麼。
GitHub 內可以設定 Secrets,在 `.yml` 內以類似環境變數方式取得,即使 `.yml` 暴露在外,也不用擔心敏感資訊外流。
:::
- 到 GitHub Actions 放入 secrets

> 在撰寫 `.yml` 時,其他人就不會看到你的帳號以及 token
- 設定帳號密碼

:::success
#### 小實做
**[註冊 Docker Hub](https://hub.docker.com/signup/)**
- 輸入 "名字、帳號、密碼" 完成後,點擊註冊會出現以下畫面

> 紫色遮住的部份是各位自己的資料
- 到信箱點擊驗證,即可完成驗證

- 在 github action 用 secrets 登入 docker hub
:::spoiler `main.yml`
```cmd=
name: CI_CD
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
login:
# runs-on: [self-hosted, linux]
runs-on: ubuntu-latest
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checks-out your repository under $GITHUB_WORKSPAC
uses: actions/checkout@v3
- name: login in docker hub
# uses: docker/login-action@v2
env:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
run: |
docker login -u $username -p $password
```
:::
### GitHub Action 做 CI/CD
實做的工作流程為將已經寫好的軟體打包成 docker image,docker image 再部屬到 Container 執行。
- workflow

<!-- **取得 token**
- 到 GitHub 網站,右上角點擊 setting

- 左邊按 Security,右邊按 New Access Token

- 取得 Docker Hub Token

-->
**安裝 docker 相關套件**
- 安裝 docker
```cmd=
sudo apt install docker.io
```
- 更改權限
```cmd=
sudo chmod 666 /var/run/docker.sock
```
> - 因為一般使用者沒有權限執行 docker,所以更改權限讓使用者可以執行
> - user 需要透過 docker.sock 與 Docker daemon 進行溝通,如果沒有權限就不能建立、刪除、執行、停止 Container
> - 若出現以下資訊
> `Got permission denied while trying to connect to the Docker daemon socket`
> [解決方式 --- 參考資料](https://newbedev.com/got-permission-denied-while-trying-to-connect-to-the-docker-daemon-socket-at-unix-var-run-docker-sock-post-http-2fvar-2frun-2fdocker-sock-v1-24-auth-dial-unix-var-run-docker-sock-connect-permission-denied-code-example)
- 安裝 docker-compose,自動化建置 Container 需要的步驟
```cmd=
sudo apt install docker-compose
<!-- ```
:::info
**docker-compose**
- 管理
> FIXME: 為什麼需要 docker-compose,他的作用是什麼
:::
:::info
### Container
> FIXME: 什麼是 Container
::: -->
- 在專案中建立 `requirements.txt` 所有要安裝的套件版本
```cmd=
Flask==2.2.2
```
:::warning
`requirements.txt` 請記得放在要複製的專案裡面。
以本次實做舉例,`requirements.txt` 要放在 flask 資料夾裡
:::
- 在專案中建立 `dockerfile` build docker image
```cmd=
FROM python:3.10
ENV PYTHONUNBUFFERED 1
RUN mkdir /app
WORKDIR /app
COPY ./flask /app
RUN pip install -r requirements.txt
CMD ["python3", "app.py"]
```
- 在專案中建立 `docker-compose.yml` 建立服務時,方便管理 images 的執行
```cmd=
version: "3"
services:
app:
image: demo
ports:
- "8080:8080"
```
#### 設定 secrets
因為實做的 yaml file 會用到下列敏感資訊,所以先將以下資訊放到 secrets (**要記得先註冊 Docker Hub!**)
- DOCKERHUB_USERNAME
- DOCKERHUB_PASSWORD
#### 建立 Docker Hub repo
本實做會將建立好的 Docker Image 放到 Docker Hub 做紀錄。
Docker Image 只可以在本機端執行操作,其他的電腦如果不想要重新 build 的話,可以將 Docker Image 推到 Docker Hub,其他電腦就可以從 Docker Hub 上 pull 下來
- 進入 Docker Hub 首頁點選 Create a Repo

- 進入 repo 總覽後,點選右上角 Create a Repo

- 設定 repo 相關資訊

#### 建立 GitHub Actions 的 workflow
- 到 GitHub Actions 新增 workflow

- 建立 `test.yml` 定義 CI/CD 流程
```yml=
name: CI_CD
on:
push:
branches: ["main"]
jobs:
try:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: echo hello
run: echo Hello!
test:
runs-on: ['self-hosted','demo']
steps:
- uses: actions/checkout@v3
- name: test python file
run: python3 test.py Amber
build:
runs-on: ['self-hosted','build_deploy']
steps:
- uses: actions/checkout@v3
- name: login docker hub
env:
username: ${{secrets.DOCKERHUB_USERNAME}}
password: ${{secrets.DOCKERHUB_PASSWORD}}
run: docker login -u $username -p $password
- name: build a docker image
env:
imageName: ${{secrets.DOCKERHUB_USERNAME}}/demo
run: docker build -t $imageName .
- name: push to docker hub
env:
imageName: ${{secrets.DOCKERHUB_USERNAME}}/demo
run: docker push $imageName
deploy:
needs: build
runs-on: ['self-hosted','build_deploy']
steps:
- name: create a docker-compose.yml
run: touch docker-compose.yml
- name: write in docker-compose
env:
imageName: ${{secrets.DOCKERHUB_USERNAME}}/demo
run: |
imageName=$imageName
echo -e "version: '3'\nservices:\n app:\n image: $imageName\n ports:\n - '8080:8080'" > docker-compose.yml
- name: run image
run: docker-compose up -d
```
[GitHub_Action 實做](https://vimeo.com/820121702/94057f8210?share=copy)
[GitHub_Action_CICD](https://vimeo.com/820508762/65fea39b84?share=copy)
<!-- 其他
:::info
#### docker 指令
- 建立 Docker Image
```cmd=
docker build -t <docker image name> <dockerfile path>
```
- ex: `docker build -t flask .`
- `-t` 設定 docker image 的 name
- `.` 當前目錄
- 查看 Images
```cmd=
docker images
```
- 查看 Container 清單
```cmd=
// 列出所有執行中的 Container
docker ps
```

- `-a` 列出所有執行、停止的 Container
- `-l` 顯示最新的 Container 資訊
- `-q` 列出最執行中的 Container ID
- 拿 Docker Image 建立並啟動 Container
```cmd=
docker run -d <image name> --name <Container name>
```
- `-d` 顯示背景執行
- `--name` 設定 Container 的名字
- 啟動已經存在的 Container
```cmd=
docker start <Container ID>
```
- 停止目前執行 Container
```cmd=
docker stop <Container ID>
```
::: -->
<!-- https://g0v.hackmd.io/@MRLyM3ynSfmNj1QrR_csrQ/SJx85m828 -->
<!-- - docker hub login
https://github.com/docker/login-action
-->
<!--  -->
<!--  -->
<!-- https://github.com/docker/login-action -->