正式走入工作崗位一個月,接著實習的工作內容繼續,還是承擔團隊的Develop & Operation & Research任務,恰好我個人也比較喜歡什麼都接觸一些.現時在應用開發部門設計了一套包括日誌處理,監控報警,持續集成,版本發佈與回滾,自動化管理在內的基礎平臺的架構,當然這套體系肯定還尚未成熟,這裡經過老大的許可先把項目的部署與回滾架構發上來,做個記錄也方便日後tuning.
為什麼是ansible?
8月初剛過來時我壓根沒聽過ansible只知道puppet,正好那時團隊準備做項目自動化管理相關的工作,也就把自動化配置管理工具搜了一遍,列了個優缺點比較表,發現ansible完勝.至於為什麼,我就一句話:“沒有agent,沒有server,用ssh批量管理主機”.玩過puppet和chef的同學應該秒懂了.
另外,ansible也是Devops工具集中躥升速度最快的配置管理工具之一.截止至本年度7月份,Devops使用度&接受度排行榜中前四比特分別是Puppet,Chef,Docker,Ansible.而後者預計很快將超越前兩個工具.
以其配寘上的簡潔,使用上的順手,功能上的强大,好了繼續下一節.
自動化管理
ansible的細節這裡略過,單說這套架構,現時包括如下功能:
- 開發者機器上集中管理所有遠程主機
- 對主機選擇性管理
- 要求規範化的項目基本目錄結構
- 線上機發佈版本與回滾
- 代理機類比成線上機便於測試
- 管理線上機服務的啟動/停止/重啓
接下來從整套機制的架構到細節逐步介紹.
規範化項目目錄結構
在未引入Docker情况下,為了做到項目(應用程序,配寘資訊,狀態數據,依賴工具鏈等)之間的隔離,這裡定義了一種項目的目錄規範.
線上項目一般都是位於某個目錄(比如/opt/app)下麵,不同的子目錄用以區分不同的項目,每個項目目錄的基本結構如下:
/opt/app
/uopt/app/example-project
├── current # 指向项目最新发布的版本的软链接 ** 重要 **
├── releases # 包含所有的历史发布版本(以及当前版本) ** 重要 **
├── tmp # rsync 临时目录
├── shared # 发布工具使用的目录 ** 重要 **
├── log # 日志存放
├── run # 运行时数据
├── util # 外部工具
└── node_modules # Node.js Package 形式的外部工具, 如 pm2 ** 重要 **
每個項目可以在這個目錄結構上進行擴展,比如添加其他目錄,但上述標注**重要**的目錄不建議删除或修改.
其中current目錄指向的就是該項目的原代碼,即開發階段所熟悉的目錄結構.
Ansible Playbook目錄結構
在使用ansible時,current所指代的程式碼目錄中,應包含ansible playbook的配寘目錄,舉個例子:
/opt/app/my_app/current/deploy ❯❯❯ tree -a -L 4
# 以下结构参考 ansible 官方给出的最佳实践
.
├── group_vars
│ ├── all
│ ├── nodes
│ │ └── vars
│ └── proxy
│ └── vars
├── host_vars # 测试环境下只需增加对应主机的变量文件即可覆盖预设变量
│ ├── 10.0.5.130
│ ├── 192.168.1.17
│ │ ├── vars
│ │ └── vault # 加密变量
│ ├── 192.168.5.68
│ │ ├── vars
│ │ └── vault
│ └── 192.168.5.69
│ ├── vars
│ └── vault
├── production # 线上环境 inventory
├── roles
│ ├── config
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── templates
│ │ ├── config.json.j2
│ │ ├── logstash.conf.j2
│ │ └── macros.j2
│ ├── node
│ │ └── tasks
│ │ └── main.yml
│ ├── proxy
│ │ └── tasks
│ │ └── main.yml
│ ├── schedule
│ │ └── tasks
│ │ └── main.yml
│ └── worker
│ └── tasks
│ └── main.yml
├── rollback.yml
├── .rsync_ignore # 从代理机到线上机同步时需要忽略的文件/目录列表
├── site.yml
└── staging # 测试环境 inventory
其餘檔案的解釋和作用見ansible官方檔案.
發佈和回滾
在執行發佈回滾操作上,我在開源roles ansistrano的基礎上做了不少改動以適配我們的應用場景,並以新的roles發佈在ansible galaxy上(https://github.com/upyun-dev/ansistrano-deploy | https://github.com/upyun-dev/ ansistrano-rollback),具體的程式碼實現這裡就不說了.
來看看怎麼用吧:
# 安装依赖 roles
$ ansible-galaxy install upyun-dev.ansistrano-deploy
$ ansible-galaxy install upyun-dev.ansistrano-rollback
# 在所有机器上执行项目部署, 发布版本并重启服务任务
ansible-playbook -i <staging | production> site.yml
# 在所有机器上执行项目回滚并重启服务任务
ansible-playbook -i <staging | production> rollback.yml
但是同構化proxy和nodes往往不是我們想要的結果,多數情况下我們可能只把proxy作為純粹的代理機使用,並不需要類比線上機器執行部署/回滾.這種情況可以通過指定--tag或者--limit來選擇要執行的任務:
# tag: proxy, nodes, config
# 仅在 proxy 上执行部署发布
ansible-playbook -i hosts site.yml --tag="proxy"
# 仅在 proxy 上执行回滚
ansible-playbook -i hosts rollback.yml --tag="proxy"
# 仅在 nodes 上执行部署发布以及服务启动
ansible-playbook -i hosts site.yml --tag="nodes"
# 只在属于proxy group的主机上执行tasks
ansible-playbook -i <staging | production> site.yml --limit=proxy
本地測試/開發
- 在本地測試與開發上,ansible playbook配寘目錄中的一些變數可能需要稍做修改,囙此你可以創建一個host_var檔案,比如:#檔案:host_vars/localhost#項目名app_name: my_app_test#項目的父級目錄dest_prefix:“/tmp”#分支/版本version: test#代理機的地址sync_host: [email protected]
在本地測試與開發上,ansible playbook配寘目錄中的一些變數可能需要稍做修改,囙此你可以創建一個host_var檔案,比如:
# 文件: host_vars/localhost
# 项目名
app_name: my_app_test
# 项目的父级目录
dest_prefix: "/tmp"
# 分支/版本
version: test
# 代理机的地址
sync_host: [email protected]
- 此外,inventory內容也可能根據測試主機的地址及ssh的配寘不同而需要修改.
此外,inventory內容也可能根據測試主機的地址及ssh的配寘不同而需要修改.
- 確保以下正常proxy和node上啟動sshd(確保nodes可以通過ssh訪問)保證本地shell沒有多餘ouput(確保以ssh管道同步數據時沒有誤差產生)安裝外部roles
確保以下正常
- proxy和node上啟動sshd(確保nodes可以通過ssh訪問)
- 保證本地shell沒有多餘ouput(確保以ssh管道同步數據時沒有誤差產生)
- 安裝外部roles
注意當你選擇使用rsync的ssh模式同步數據時,一定要保證代理機上的登入shell沒有多餘的輸出(當時被這個坑了…).
專案啟動腳本
由於引入規範化項目結構,導致了冗餘目錄的產生以及項目狀態數據路徑(如pm2)的變化.為了遮罩本地以及線上部署差异,透明化本地開發,這裡對項目的啟動腳本做了修改,使其根據不同的開發環境判斷如何啟動項目,如:
PRJ_DIR=$(cd $APP_ROOT_DIR/../.. && pwd)
APP_DIR_PARENT=$(cd $APP_ROOT_DIR/.. && pwd)
if [[ -L $APP_DIR_PARENT/current ]]; then
PRJ_DIR=$APP_DIR_PARENT
elif [[ ! -d $PRJ_DIR/releases || ! -L $PRJ_DIR/current ]]; then
PRJ_DIR=${APP_ROOT_DIR}
fi
PM2_BIN="$PRJ_DIR/node_modules/pm2/bin/pm2" # 指定新的pm2可执行文件路径
最終結果體現為:如果並沒有遵循如上所述的規範化目錄結構,那麼將之後的部署作為本地開發環境下的部署.否則作為線上/測試環境下的部署.