はじめに
Azure Web Appsにデプロイする方法はいろいろありますが、その中でREST APIを使用してZipファイルをデプロイする方法をやってみます。
ZIP または WAR ファイルを使用した Azure App Service へのアプリのデプロイ
Deploying from a zip file or url
REST APIでZipファイルをデプロイする処理
CircleCIの再利用可能なコマンドとして実装していますが、デプロイ処理そのものはシェルスクリプトです。
REST APIを利用できる環境ならば適宜変更して利用できると思います。
REST APIでZipファイルをデプロイするCircleCIコマンド(全て)
version: 2.1
commands:
azure_zip_deploy:
description: "Azure ZIP Deploy"
parameters:
app:
type: string
user:
type: string
password:
type: string
zip:
type: string
async:
type: enum
enum: ["true", "false"]
default: "false"
steps:
- run:
name: Deploy to Azure
command: |
dir="./deploy"
mkdir ${dir}
deploy_result="${dir}/result.html"
deploy_header="${dir}/header.txt"
deploy_url="https://<< parameters.app >>.scm.azurewebsites.net/api/zipdeploy?isAsync=<< parameters.async >>"
deploy_http_code=$(curl -s -X POST -u << parameters.user >>:<< parameters.password >> ${deploy_url} -T << parameters.zip >> -o ${deploy_result} --dump-header ${deploy_header} -w '%{http_code}')
echo "Deploy HTTP code is ${deploy_http_code}."
if [ ${deploy_http_code} -eq 200 ]; then
echo "Deploy successful."
exit 0
elif [ ${deploy_http_code} -eq 202 ]; then
echo "Deploy accepted."
else
echo "Deploy failed."
exit 1
fi
pollable_url=""
regex="^Location:\s(.+)$"
while read line
do
if [[ ${line} =~ ${regex} ]]; then
pollable_url=$(echo ${BASH_REMATCH[1]} | tr -d "\r\n" | tr -d "\n")
break
fi
done < ${deploy_header}
if [ ${pollable_url} = "" ]; then
echo "Pollable URL not found."
exit 1
fi
echo "Polling deployment status."
polling_count=0
polling_wait=30
while true
do
polling_count=$((++polling_count))
status_result="${dir}/status_${polling_count}.json"
status_http_code=$(curl -sS -u << parameters.user >>:<< parameters.password >> ${pollable_url} -o ${status_result} -w '%{http_code}')
echo "Deployment status HTTP code is ${status_http_code}."
if [ ${status_http_code} -lt 200 -o ${status_http_code} -ge 300 ]; then
echo "Request status failed."
exit 1
fi
result=$(cat ${status_result})
complete=$(echo ${result} | jq '.complete')
status=$(echo ${result} | jq '.status')
status_text=$(echo ${result} | jq -r '.status_text')
if ${complete}; then
if [ ${status} -eq 4 ]; then
break
fi
echo "Deploy failed."
echo "${status_text}"
exit 1
else
echo "${status_text}"
fi
sleep ${polling_wait}
done
echo "Deploy successful."
- store_artifacts:
path: ./deploy
解説
先程のコマンドを細かく解説していきます。
パラメータについて
parameters:
app:
type: string
user:
type: string
password:
type: string
zip:
type: string
async:
type: enum
enum: ["true", "false"]
default: "false"
パラメータ | 値 |
---|---|
app | Azure App Serviceのリソース名 |
user | デプロイ ユーザ |
password | デプロイ ユーザのパスワード |
zip | zipファイルのパス |
async | 非同期デプロイ |
デプロイに時間がかかるとタイムアウトする場合があります。(デプロイは継続されてます)
その場合は非同期でデプロイします。
デプロイAPIへリクエスト
deploy_result="${dir}/result.html"
deploy_header="${dir}/header.txt"
deploy_url="https://<< parameters.app >>.scm.azurewebsites.net/api/zipdeploy?isAsync=<< parameters.async >>"
deploy_http_code=$(curl -s -X POST -u << parameters.user >>:<< parameters.password >> ${deploy_url} -T << parameters.zip >> -o ${deploy_result} --dump-header ${deploy_header} -w '%{http_code}')
curlコマンドでリクエストします。
レスポンスの本文とヘッダーはそれぞれファイルに保存し、HTTPステータスコードを変数に入れます。
if [ ${deploy_http_code} -eq 200 ]; then
echo "Deploy successful."
exit 0
elif [ ${deploy_http_code} -eq 202 ]; then
echo "Deploy accepted."
else
echo "Deploy failed."
exit 1
fi
HTTPステータスコードが200の場合は「デプロイ成功」と判断して正常終了します。
非同期デプロイをした場合は202となるので「デプロイが受理された」として処理を続行します。
それ以外は「デプロイ失敗」でエラーとします。
デプロイ ステータスURLを取得
非同期デプロイを行った場合はデプロイの結果を別途取得する必要があります。
そしてそのためのURLはヘッダー情報に含まれています。
Add ?isAsync=true to the URL to deploy asynchronously. You will receive a response as soon as the zip file is uploaded with a Location header pointing to the pollable deployment status URL.
pollable_url=""
regex="^Location:\s(.+)$"
while read line
do
if [[ ${line} =~ ${regex} ]]; then
pollable_url=$(echo ${BASH_REMATCH[1]} | tr -d "\r\n" | tr -d "\n")
break
fi
done < ${deploy_header}
if [ ${pollable_url} = "" ]; then
echo "Pollable URL not found."
exit 1
fi
ファイルに保存しておいたヘッダーからURLを抽出します。
正規表現でLocation
を探して値を変数に入れます。(改行コード入ってしまったので削除しています)
URLの有無を確認していますが、Location
が空だったケースは確認していません。
デプロイ ステータスをポーリング
一定時間毎にリクエストして結果を確認します。
次の処理ではデプロイが終了していない場合、一定時間sleep
して再度実行するようにしています。
デプロイ成功を確認したらbreak
でwhile
から出ます。
polling_count=0
polling_wait=30
while true
do
polling_count=$((++polling_count))
# リクエスト処理(後述します)
sleep ${polling_wait}
done
echo "Deploy successful."
次はステータスをリクエストする処理です。
status_result="${dir}/status_${polling_count}.json"
status_http_code=$(curl -sS -u << parameters.user >>:<< parameters.password >> ${pollable_url} -o ${status_result} -w '%{http_code}')
echo "Deployment status HTTP code is ${status_http_code}."
if [ ${status_http_code} -lt 200 -o ${status_http_code} -ge 300 ]; then
echo "Request status failed."
exit 1
fi
先程抽出したURLにリクエストします。
レスポンスの本文をファイルに保存し、HTTPステータスコードを変数に入れます。
成功は200、処理中は202になるようなのでそれ以外はエラーとしています。
result=$(cat ${status_result})
complete=$(echo ${result} | jq '.complete')
status=$(echo ${result} | jq '.status')
status_text=$(echo ${result} | jq -r '.status_text')
if ${complete}; then
if [ ${status} -eq 4 ]; then
break
fi
echo "Deploy failed."
echo "${status_text}"
exit 1
else
echo "${status_text}"
fi
先程保存したレスポンス本文(JSON)から必要な値を取り出します。
詳しい説明が見つからなかったのですがこちらのAPIと内容から「completeがtrueかつstatusが4」であればデプロイ成功と判断してよさそうです。
既知の問題
オートスワップに関して
オートスワップとの競合により、デプロイ結果が403エラーになる現象が確認されました。
ポーリングとオートスワップは相性が悪く、ポーリングが終わってから明示的にスワップする必要があるようです。
(2020/10/01時点)
Unfortunately, this is the combination (polling and autoswap) which does not work well. If you need to poll the status, either poll and do swap explicitly upon deployment completion (meaning no using auto-swap).
最後に
シェルスクリプトの経験が少ないのでheaderからLocationを抜き出したり、ポーリングしたりは苦労しました。
もっと良い書き方があればご指摘いただけると幸いです。