通過 Azure Pipelines 實現(xiàn)持續(xù)集成之docker容器化及自動化部署
Intro
Azure DevOps Pipeline 現(xiàn)在對于公開的項目完全免費贞铣,這對于開源項目來講無疑是個巨大的好消息,在 Github 的 Marketplace 里有個 Azure Pipeline浑玛,就是微軟的 Azure DevOps Pipeline。
實現(xiàn) Docker 容器化的持續(xù)集成
實現(xiàn)的目標(biāo):
- push 代碼自動打包 docker 鏡像并上傳至docker hub
- ssh 自動部署到虛擬機(jī)上
有了docker image 之后后面就可以按照自己的需求加以定制了携添,比如通過ssh部署到服務(wù)器或者進(jìn)行服務(wù)通知等巩梢。
新建 Pipeline
可以在 Azure 的 devops 新建一個 pipelines 的項目來專門管理 Github 上的pipeline
新建一個pipeline
第一次使用的話,會需要進(jìn)行授權(quán)
授權(quán)之后就可以選擇 Github 上的項目了知染,選擇要配置的項目
可以基于模板創(chuàng)建也可以選擇下面基于已有的 yaml 文件創(chuàng)建
Azure pipeline config
這里提供一份示例肋僧,源代碼在這里
:
# Docker image
# Build a Docker image to deploy, run, or push to a container registry.
# Add steps that use Docker Compose, tag images, push to a registry, run an image, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
pool:
vmImage: 'Ubuntu 16.04'
variables:
imageName: '$(dockerId)/activityreservation'
steps:
- script: |
docker build -f Dockerfile -t $(imageName) .
docker login -u $(dockerId) -p $(pswd)
docker push $(imageName)
pipeline 配置解析
- agent pool 配置
通過 vmImage 來指定要用來執(zhí)行 build 任務(wù)的 agent
pool:
vmImage: 'Ubuntu 16.04'
- variables
可以通過 variables 來指定一些全局變量,這里我用了一個 imageName 的變量來設(shè)置 docker 鏡像的名稱
variables:
imageName: 'activityreservation'
- 敏感信息的存儲
要上傳 docker 鏡像持舆,我這里是直接上傳到 docker hub 上色瘩,需要 docker 的用戶名以及密碼,pipeline 可以設(shè)置一些不配置在 pipeline 配置文件里的其它配置,一些敏感信息就可以這樣配置來保證安全訪問
可以將 pipeline 獨有的一些配置放在 Pipeline Variables
里逸寓,一些比較通用的居兆,別的 pipeline 也會使用的變量可以放到一個 Variable groups
,然后在 pipeline 的 variables 里 link 一下對應(yīng)的 Variable Group 就可以使用 group 里配置的變量了竹伸,我把 docker 的 username 和 password 配置在了一個 docker 的 Variable Group 里。
- docker 鏡像的打包以及上傳
配置 pipeline 的 step,step 對應(yīng)的就是需要 build agent 去執(zhí)行的task
steps:
- script: |
docker build -f Dockerfile -t $(imageName) .
docker login -u $(dockerId) -p $(pswd)
docker push $(imageName)
配置上面的腳本我們就可以自動 build 并 push docker 鏡像了钢颂,你也可以使用微軟提供的 docker task模板拜银,這里我是直接使用腳本去做了。
build 完成之后再去 docker hub 上查看對應(yīng)的 docker 鏡像就會發(fā)現(xiàn) docker 鏡像已經(jīng)更新了操灿。
在 vm 上自動部署 docker 鏡像
首先要在 pipeline 上新建一個 SSH 的 Service Connection
steps:
- script: |
docker build -f Dockerfile -t $(imageName) .
docker login -u $(dockerId) -p $(pswd)
docker push $(imageName)
- task: SSH
displayName: 'Run shell inline on remote machine'
inputs:
sshEndpoint: 'weihanli-vm'
runOptions: inline
inline: |
containers=$(docker ps -q --filter name=activityreservation)
if test -n "$containers"; then
docker stop $(docker ps -q --filter name=activityreservation) >> /dev/null 2>&1
rc=$?
if [[ $rc != 0 ]];
then
echo 'failed to stop container...'
exit $rc;
fi
fi
containers1=$(docker ps -q -a --filter name=activityreservation)
if test -n "$containers1"; then
docker rm $(docker ps -q -a --filter name=activityreservation) >> /dev/null 2>&1
rc=$?
if [[ $rc != 0 ]];
then
echo 'failed to remove container...'
exit $rc;
fi
fi
docker pull $(imageName):latest >> /dev/null 2>&1
rc=$?
if [[ $rc != 0 ]];
then
echo 'failed to pull container...'
exit $rc;
fi
docker run -d -p 7010:80 --name activityreservation --link redis:redis-server $(imageName):latest >> /dev/null 2>&1
rc=$?
if [[ $rc != 0 ]];
then
echo 'failed to run container...'
exit $rc;
fi
danglings=$(docker images -f "dangling=true" -q)
if test -n "$danglings"; then
docker rmi $(docker images -f "dangling=true" -q) >> /dev/null 2>&1
rc=$?
if [[ $rc != 0 ]];
then
echo 'failed to remove danglings container...'
exit $rc;
fi
fi
sshEndpoint 設(shè)置為連接的名稱,inline 后面是在遠(yuǎn)程執(zhí)行的腳本久窟,大概流程如下:
- 檢查是否有指定名稱的 container 在運行瘸羡,如果有 stop 并 remove
- 拉取最新的 docker 鏡像
- 運行 docker 容器
- 移除可能的懸掛鏡像(名稱為 none 的中間鏡像)
完整的 azure-pipelines 配置可以參考:https://github.com/WeihanLi/ActivityReservation/blob/7fbe08d7aa233dd7a3f3622e0c73aa6514d33e0b/azure-pipelines.yml
驗證
配置完成之后我們就可以提交代碼搓茬,就會自動出發(fā) build卷仑,自動執(zhí)行我們定義的 pipeline 任務(wù),按照上面的配置的話粘昨,就會先 build 并 push Docker 鏡像到 docker hub窜锯,然后 SSH 到遠(yuǎn)程服務(wù)器锚扎,遠(yuǎn)程過去之后執(zhí)行腳本,停掉并移除指定的 docker 容器(如果有)然后拉取并部署最新的docker鏡像芍秆,最后清理資源妖啥,刪除 docker 懸掛鏡像对碌。
示例項目
現(xiàn)在有幾個項目是這種模式去自動化部署的朽们,源代碼以及 pipeline 的配置都在 Github 上
現(xiàn)在這個項目的部署模式是這樣的:
前面一個 nginx 作為反向代理,后面是直接跑在 docker 容器里
nginx 示例配置:
server {
listen 80;
listen 443;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
server_name reservation.weihanli.xyz;
location / {
proxy_pass http://localhost:7010;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
這里會把 reservation.weihanli.xyz
的請求轉(zhuǎn)發(fā)到 localhost:7010
,也就是這個 docker 鏡像映射的本地端口
Memo
如果有什么問題或建議惜姐,歡迎與我聯(lián)系