#mynavi(Azureメモ) #setlinebreak(on); * 概要 [#cb2ae081] #html(<div class="pl10">) App Service は スケール等にも簡単に対応可能な為、アプリケーションの公開には非常に有用なサービスだが、 Grafana の様にデフォルトでローカルにデータを保存するようなアプリケーションの場合、「''スケール時や再起動時にデータを引き継ぐことが出来ない''」。 そこで、MySQL を使用して外部にデータを保存/利用する方法を記載する。 #html(</div>) * 目次 [#xaebdc2a] #contents - 関連 -- [[Azureメモ]] -- [[Azure上のGrafanaでAD認証(App Service版)]] -- [[Azure VM上にGrafanaをデプロイ]] -- [[Azure上のGrafanaをカスタムドメインで公開]] -- [[Azure上のGrafanaでAD認証(VM版)]] * 作成する環境 [#j6868bd3] #html(<div class="pl10">) 下図のような環境を構築する。 #html(<div class="ib border ml10 mb20">) &ref(azure_grafana_app_service.png,nolink); #html(</div>) ** Grafana のローカルDBに MySQL を採用した経緯 [#r52cf943] #html(<div class="pl10">) Azure App Service のアプリケーションで使用するファイルを外部化する方法として、Azure Files(ファイル共有) や Azure Disk 等があるが、 Azure Files(ファイル共有) で Grafana の内部データである SQLite のファイルを外部化(共有化)しようとしたが、これは出来なかった(※)。 ※「Server shutdown" logger=server reason="Service init failed: Migration failed err: database is locked"」 となる。 ※ Azure Disk は単純にコストの問題で採用を見送った。 調べてみると同様の事象は報告されており(※)、こちらでは AzureDisk を使用して回避したとの事だが、当記事では MySQL を使用して外部化する事とした。 ※ https://github.com/kubernetes/kubernetes/issues/59755 #html(</div>) ** 仮想マシンについて [#xaba1f5a] #html(<div class="pl10">) 仮想マシンには以下の環境を構築する。(docker-compose を使用して構築) - Grafana に表示するグラフデータ用の InfluxDB - Grafana自体のデータを保持する為の MySQL #html(</div>) #html(</div>) * リソースの作成 [#s32d67cc] #html(<div class="pl10">) いつも通りシェル化しておく。(最終的には ARM に移行) #html(){{ <div id="tabs1"> <ul> <li><a href="#tabs1-0">0_env.sh</a></li> <li><a href="#tabs1-1">1_vm_resources.sh</a></li> <li><a href="#tabs1-2">2_vm_setup.sh</a></li> <li><a href="#tabs1-3">3_app_resources.sh</a></li> </ul> }} // START tabs1-0 #html(<div id="tabs1-0">) #mycode2(){{ #!/bin/bash PREFIX=リソース名の頭に付ける接頭文字 subscriptionId=サブスクリプションID region=japanwest # リソースグループ resourceGroup=${PREFIX}Resources # 仮想ネットワーク vnetName=${PREFIX}VNet vnetPrefix=10.1.0.0/16 # セキュリティグループ名 nsgName=${vnetName}SecGrp nsgPubRuleName=${nsgName}PubRule # 仮想マシンを配置するサブネット vmSubnetName=${PREFIX}VmSubnet vmSubnetPrefix=10.1.1.0/24 # VNet統合用のサブネット exSubnetName=${PREFIX}ExSubnet exSubnetPrefix=10.1.2.0/24 # 仮想マシンの情報 vmName=${PREFIX}Vm vmImage=UbuntuLTS vmIpAddress=10.1.1.5 vmUser=sample # App Service(Grafana)用 registryName=${PREFIX}registry grafanaImageName=${PREFIX}-grafana webappName=${PREFIX}-grafana }} #html(</div>) // END tabs1-0 // START tabs1-1 #html(<div id="tabs1-1">) #mycode2(){{ #!/bin/bash # 設定の読み込み source 0_env.sh # リソース作成 if [ "$1" == "--create" ]; then # リソースグループの作成 echo "az group create" az group create --name $resourceGroup --location $region # NSG(ネットワークセキュリティグループの)作成 echo "az network nsg create" az network nsg create --resource-group $resourceGroup --name $nsgName # InfluxDB接続用のNSGルール ※TODO: source-port-range は App Service 側の送信IPに合わせて絞っておいた方が良い echo "az network nsg rule create(influxdb)" az network nsg rule create \ --resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}1\ --access Allow --protocol Tcp --direction Inbound --priority 100 \ --source-address-prefix Internet --source-port-range "*" --destination-port-range "8086" # MySQL接続用のNSGルール ※TODO: source-port-range は App Service 側の送信IPに合わせて絞っておいた方が良い echo "az network nsg rule create(mysql)" az network nsg rule create \ --resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}2 \ --access Allow --protocol Tcp --direction Inbound --priority 101 \ --source-address-prefix Internet --source-port-range "*" --destination-port-range "3306" # SSH接続用のNSGルール echo "az network nsg rule create(ssh)" az network nsg rule create \ --resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}3 \ --access Allow --protocol Tcp --direction Inbound --priority 1000 \ --source-address-prefix Internet --source-port-range "*" --destination-port-range "22" # 仮想ネットワーク 及び サブネット作成 echo "az network vnet create" az network vnet create \ --name $vnetName --resource-group $resourceGroup \ --address-prefixes $vnetPrefix --network-security-group $nsgName \ --subnet-name $vmSubnetName --subnet-prefixes $vmSubnetPrefix # App Service とのVNet統合用のサブネットを作成 echo "az vnet subnet create for vnet integration" az network vnet subnet create \ --name $exSubnetName \ --resource-group $resourceGroup \ --vnet-name $vnetName \ --address-prefixes $exSubnetPrefix # 仮想マシンの作成 rm -rf ~/.ssh/id_rsa rm -rf ~/.ssh/id_rsa.pub echo "az vm create" az vm create \ --resource-group $resourceGroup --name $vmName --image $vmImage --generate-ssh-keys \ --vnet-name $vnetName --subnet $vmSubnetName \ --private-ip-address $vmIpAddress --admin-username $vmUser \ --public-ip-address-dns-name `echo $vmName | tr '[A-Z]' '[a-z]'` \ --custom-data 2_vm_setup.sh # ポート開放 echo "az vm open-port(3306)" az vm open-port --resource-group $resourceGroup --name $vmName --port 3306 --priority 901 echo "az vm open-port(8086)" az vm open-port --resource-group $resourceGroup --name $vmName --port 8086 --priority 902 # 生成されたSSH鍵を退避しておく mkdir -p pem if [ -e ~/.ssh/id_rsa ]; then mv ~/.ssh/id_rsa ./pem/id_rsa_${vmName} mv ~/.ssh/id_rsa.pub ./pem/id_rsa_${vmName}.pub fi fi # リソース削除 if [ "$1" == "--delete" ]; then echo az group delete az group delete --name $resourceGroup fi }} #html(</div>) // END tabs1-1 // START tabs1-2 #html(<div id="tabs1-2">) &color(red){''【注意】''}; vm create 時の --custom-data で指定するシェルには マルチバイトが含まれていると create に失敗する。 ※ エラーメッセージ 「'latin-1' codec can't encode characters in position XXX-XXX: ordinal not in range(256)」 #mycode2(){{ #!/bin/bash apt-get update apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose mkdir /tmp/influx_docker && cd /tmp/influx_docker cat <<_EOCONF_ >influxdb.conf [meta] dir = "/var/lib/influxdb/meta" [data] dir = "/var/lib/influxdb/data" engine = "tsm1" wal-dir = "/var/lib/influxdb/wal" [http] enabled = true flux-enabled = true _EOCONF_ cat <<_EOYML_>docker-compose.yml version: "3" services: influxdb: image: influxdb:1.8 hostname: influxdb_for_grafana container_name: influxdb_for_grafana volumes: - ./data/influxdb:/var/lib/influxdb - ./influxdb.conf:/etc/influxdb/influxdb.conf ports: - "8086:8086" mysql: image: mysql:8.0.1 hostname: mysql_for_grafana container_name: mysql_for_grafana ports: - "3306:3306" environment: MYSQL_DATABASE: grafana MYSQL_USER: grafana MYSQL_PASSWORD: grafana MYSQL_ROOT_PASSWORD: admin _EOYML_ mkdir -p data/influxdb chmod 777 data/influxdb # start influxdb docker-compose up -d # wait until influxdb starts while [ true ]; do sleep 10 x=`sudo docker exec -i influxdb_sample influx --execute "show databases" 2>&1` x=`sudo docker exec -i influxdb_for_grafana influx --execute "show databases" 2>&1` if [ "$?" == "0" ]; then break fi done # create sample database and sample user. docker exec -i influxdb_sample influx --execute "create database sampledb" docker exec -i influxdb_sample influx --execute "create user sample with password 'sample' WITH ALL PRIVILEGES" docker exec -i influxdb_for_grafana influx --execute "create database sampledb" docker exec -i influxdb_for_grafana influx --execute "create user sample with password 'sample' WITH ALL PRIVILEGES" }} #html(</div>) // END tabs1-2 // START tabs1-3 #html(<div id="tabs1-3">) #mycode2(){{ #!/bin/bash # リソース名の読み込み source 0_env.sh # リソース作成 if [ "$1" == "--create" ]; then # コンテナレジストリ作成 echo "az acr create ( $registryName )" az acr create -n $registryName -g $resourceGroup --sku standard --admin-enabled true # レジストリユーザ名/パスワード取得 acr_credential="`az acr credential show --name $registryName -o table | tail -1`" registryUser="`echo "$acr_credential" | awk '{print $1}'`" registryPwd="`echo "$acr_credential" | awk '{print $2}'`" cat <<_EO_DOCKERFILE_>Dockerfile FROM grafana/grafana:7.1.1 # オリジナルプラグインのコピー等 #COPY my_plungin/ /var/lib/grafana/plugins/my_plungin/ _EO_DOCKERFILE_ # DockerイメージをビルドしてACRに登録 echo "az acr build ( $grafanaImageName )" az acr build --registry $registryName --image ${grafanaImageName} . # App Service プラン作成 ( Standard プラン以上でないと VNet統合が使用できない ) echo "az appservice plan create ( ${webappName}-plan )" az appservice plan create -n ${webappName}-plan -g $resourceGroup --is-linux --sku S1 # App Service アプリ作成(Grafana) echo "az webapp create ( $webappName )" az webapp create -g $resourceGroup -p ${webappName}-plan -n $webappName \ -i ${registryName}.azurecr.io/${grafanaImageName}:latest --docker-registry-server-user ${registryUser} \ --docker-registry-server-password ${registryPwd} # docker-compose だとVNet統合がうまく認識されない # (2020/8時点で compose 対応はプレビューらしので素直に単一Dockerで行う) # --multicontainer-config-type compose \ # --multicontainer-config-file docker-compose.yml \ # VNet統合の作成 echo "az webapp vnet-integration add" az webapp vnet-integration add \ --name $webappName \ --resource-group $resourceGroup \ --subnet $exSubnetName \ --vnet $vnetName # App Service 環境変数設定 echo "az webapp config appsettings ( $webappName )" az webapp config appsettings set \ -g $resourceGroup -n $webappName \ --settings "GF_SERVER_ROOT_URL=https://${webappName}.azurewebsites.net" \ "GF_SECURITY_ADMIN_PASSWORD=admin" "GF_DATABASE_TYPE=mysql" "GF_DATABASE_HOST=${vmIpAddress}:3306" \ "GF_DATABASE_NAME=grafana" "GF_DATABASE_USER=grafana" "GF_DATABASE_PASSWORD=grafana" \ "WEBSITES_PORT=3000" # コンテナロギングをONにする ( コンテナログがポータルから確認できる ) echo "az webapp log config" az webapp log config --name $webappName --resource-group $resourceGroup --docker-container-logging filesystem --level information # 再起動 echo "webapp restart" az webapp restart -n $webappName -g $resourceGroup echo "Application URL: https://${webappName}.azurewebsites.net" fi # リソース削除(App Service関連のみ) if [ "$1" == "--delete" ]; then echo "delete webapp" az webapp delete -n $webappName -g $resourceGroup echo "delete appservice plan" az appservice plan delete -y -n ${webappName}-plan -g $resourceGroup echo "delete acr" az acr delete -y -n $registryName -g $resourceGroup fi }} #html(</div>) // END tabs1-3 #html(</div>) // END tabs1 #html(<script>$(function() { $("#tabs1").tabs(); });</script>) ** VM関連のリソースを作成 [#h492e87b] #myterm2(){{ ./1_vm_resources.sh --create }} ** App Service のリソースを作成 [#c4e8cf3a] #myterm2(){{ ./3_app_resources.sh --create }} #html(</div>) * 動作確認 [#n9269eef] #html(<div class="pl10">) https://${webappName}.azurewebsites.net にアクセスして動作確認。 #html(</div>)