#mynavi(Azureメモ)
#setlinebreak(on);

* 概要 [#q629b743]
#html(<div class="pl10">)
VMにアクセスする Azure Functions の開発手順を記載する。
#html(</div>)

* 目次 [#uf1ea711]
#contents
- 関連
-- [[Azureメモ]]
-- [[Azure CLI の操作]]
-- [[GoからInfluxDBにアクセスする]]
-- [[Azure Functions を Go で書く]]
- 参考
-- [[Premium プランの関数アプリを作成する:https://docs.microsoft.com/ja-jp/azure/azure-functions/scripts/functions-cli-create-premium-plan]]
-- [[Azure Functions の Premium プラン:https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-premium-plan]]
-- [[サンプルスクリプト:https://docs.microsoft.com/ja-jp/azure/azure-functions/scripts/functions-cli-create-premium-plan#sample-script]]
-- [[Azure Functions のデプロイ テクノロジ:https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-deployment-technologies]]
-- [[Azure Functions の zip デプロイ:https://docs.microsoft.com/ja-jp/azure/azure-functions/deployment-zip-push]]
-- [[チュートリアル: Functions を Azure 仮想ネットワークに統合する:https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-vnet]]
-- [[アプリを Azure 仮想ネットワークと統合する:https://docs.microsoft.com/ja-jp/azure/app-service/web-sites-integrate-with-vnet]]
-- [[Azure Functions のネットワーク オプション:https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-networking-options]]

* 作成する関数の仕様 [#i6e368a7]
#html(<div class="pl10">)

以降の手順で作成する関数の仕様 及び リソースイメージは以下の通り。

- Blobストレージへのファイルアップロードをトリガーに起動する関数とする。
- 関数はVMで稼働する InfluxDB にサンプルデータを登録する。(ファイル内容に関係なく...)

#html(<div style="display: inline-block; border: 1px solid #333">)
&ref(sample-azure-image.png,nolink);
#html(</div>)

#html(</div>)

* 作成するファイル [#h684fc8d]
#html(<div class="pl10">)

以降の手順で作成する全ファイルは以下の通り

#html(){{
<div style="padding: 0px 10px 10px 10px; border: 1px solid #333;">
<pre style="display: inline-block; margin: 0; padding-right: 10px; font-size: 1rem; background: transparent; border: 0px; vertical-align: top;">
.
├── 0_env.sh
├── 1_create_resources.sh
├── 2_setup_influxdb.sh
├── 3_deploy_funcapp.sh
├── 9_remove_resources.sh
├── functions
│&#160;&#160; ├── BlobTrigger
│&#160;&#160; │&#160;&#160; └── function.json
│&#160;&#160; ├── go_server_sample.exe
│&#160;&#160; ├── go_server_sample.go
│&#160;&#160; ├── host.json
│&#160;&#160; └── local.settings.json
├── pem
│&#160;&#160; ├── id_rsa
│&#160;&#160; └── id_rsa.pub
</pre>
<pre style="display: inline-block; margin: 0;  font-size: 1rem; background: transparent; border: 0px; vertical-align: top;">
 
... リソース名の定義など
... リソース作成用のシェル
... VMのInfluxDBのセットアップ用シェル
... 関数アプリのデプロイ用シェル
... リソース一括削除用のシェル
 
... 内部関数名(トリガー名)
... 関数のトリガー定義
... カスタムハンドラー(実行可能ファイル)
... カスタムハンドラー(ソース)
... 関数アプリの定義ファイル
... ローカル実行用の設定ファイル
... 作成するVMのSSH鍵の退避先(VM作成時に自動生成)
 
 
</pre>
</div>
}}

#html(</div>)


* リソース作成/デプロイ用のシェル作成 [#z256443a]

#html(<div class="pl10">)

リソース作成用のシェルを以下の通り作成する。
当記事では全てCLIをシェル化したが、恐らく ARM(Azure Resource Manager)を使うのが正解。
※https://azure.microsoft.com/ja-jp/features/resource-manager/

// START tabs1
#html(){{
<div id="tabs1">
  <ul>
    <li><a href="#tabs1-0">0_env.sh</a></li>
    <li><a href="#tabs1-1">1_create_resources.sh</a></li>
    <li><a href="#tabs1-2">2_setup_influxdb.sh</a></li>
    <li><a href="#tabs1-3">3_deploy_funcapp.sh</a></li>
    <li><a href="#tabs1-9">9_remove_resources.sh</a></li>
  </ul>
}}

// START tabs1-0
#html(<div id="tabs1-0">)

このシェルでは作成する各リソースの名前の定義を行っている。
※ -p 付きで実行すればコンソールへの表示も行うようにしている。

0_env.sh
#mycode2(){{
#!/bin/bash

# 全てのリソース名に付与する接頭文字 (Storageアカウント名などは世界でユニークな必要があるので他ユーザと被らないような名前を付ける)
PREFIX=XXXXXX

# サブスクリプションID (Application Insight を使用しない場合は空)
subscriptionId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# リージョン
region=japanwest
insightsRegion=japaneast # 2020/7時点では Appplication Insights で西日本(japanwest)は使用できない

# リソースグループ名
# ( az group delete --name $resourceGroup で一括削除可能 )
resourceGroup=${PREFIX}ResourceGroup

# ストレージアカウント名
storageAccountName=${PREFIX}straccount
storageSku=Standard_LRS

# Storageコンテナ名
storageContainerIn=${PREFIX}-strcontainer-i
storageContainerOut=${PREFIX}-strcontainer-o

# 仮想ネットワーク名
vnetName=${PREFIX}VNet
vnetPrefix=10.1.0.0/16

# ネットワークセキュリティグループ
nsgName=${vnetName}SecGrp
nsgPubRuleName=${nsgName}PubRule
nsgPubInboundPort="22"
#nsgPubInboundPort="443"
nsgPriRuleName=${nsgName}PriRule
nsgPriInboundPort="8086"

# VM用のサブネット
vmSubnetName=${PREFIX}VmSubnet
vmSubnetPrefix=10.1.1.0/24

# 関数アプリ用のサブネット
funcSubnetName=${PREFIX}FuncSubnet
funcSubnetPrefix=10.1.2.0/24

# 仮想マシン
vmName=${PREFIX}Vm
vmImage=UbuntuLTS      # "az vm image list -o table" で利用可能なイメージの一覧を確認可能
vmIpAddress=10.1.1.5
vmUser=sample

# 関数アプリ名
funcAppName=${PREFIX}FuncApp

# 使用するFunctionsのバージョン
funcVersion=2

# 関数アプリのプラン名
funcPlanName=${PREFIX}premiumplan
funcPlanSku=EP1

# Application insights
insightsName=${PREFIX}Insights
insightsDays=30


if [ "$1" == "-p" ]; then
  echo "region              : $region"
  echo "resourceGroup       : $resourceGroup"
  echo "storageAccountName  : $storageAccountName"
  echo "storageSku          : $storageSku"
  echo "storageContainerIn  : $storageContainerIn"
  echo "storageContainerOut : $storageContainerOut"
  echo "vnetName            : $vnetName"
  echo "vnetPrefix          : $vnetPrefix"
  echo "nsgName             : $nsgName"
  echo "nsgPubRuleName      : $nsgPubRuleName"
  echo "nsgPubInboundPort   : $nsgPubInboundPort"
  echo "nsgPriRuleName      : $nsgPriRuleName"
  echo "nsgPriInboundPort   : $nsgPriInboundPort"
  echo "vmSubnetName        : $vmSubnetName"
  echo "vmSubnetPrefix      : $vmSubnetPrefix"
  echo "vmName              : $vmName"
  echo "vmImage             : $vmImage"
  echo "vmIpAddress         : $vmIpAddress"
  echo "vmUser              : $vmUser"
  echo "funcAppName         : $funcAppName"
  echo "funcVersion         : $funcVersion"
  echo "funcPlanName        : $funcPlanName"
  echo "funcPlanSku         : $funcPlanSku"
  echo "funcSubnetName      : $funcSubnetName"
  echo "funcSubnetPrefix    : $funcSubnetPrefix"
  echo "insightsName        : $insightsName"
  echo "insightsDays        : $insightsDays"
  echo "insightsRegion      : $insightsRegion"
fi
}}

#html(</div>)
// END tabs1-0

// START tabs1-1
#html(<div id="tabs1-1">)

このシェルでは各リソースを作成する。
※関数アプリは中身が空のアプリとして作成される。(create のみ行っている)

1_create_resources.sh
#mycode2(){{
#!/bin/bash

source ./0_env.sh

# リソースグループの作成
echo az group create
az group create \
  --name $resourceGroup \
  --location $region

# ストレージアカウントの作成
echo az storage account create
az storage account create \
  --name $storageAccountName \
  --location $region \
  --resource-group $resourceGroup \
  --sku $storageSku

# Storageコンテナの作成
echo "az storage container create(in)"
az storage container create \
  --name $storageContainerIn \
  --resource-group $resourceGroup \
  --account-name $storageAccountName
echo "az storage container create(out)"
az storage container create \
  --name $storageContainerOut \
  --resource-group $resourceGroup \
  --account-name $storageAccountName

# NSG(ネットワークセキュリティグループの)作成
echo az network nsg create
az network nsg create \
  --resource-group $resourceGroup \
  --name $nsgName

# NSGルール(Public)
echo az network nsg rule create
az network nsg rule create \
  --resource-group $resourceGroup \
  --nsg-name $nsgName \
  --name $nsgPubRuleName \
  --access Allow \
  --protocol Tcp \
  --direction Inbound \
  --priority 100 \
  --source-address-prefix Internet \
  --source-port-range "*" \
  --destination-port-range $nsgPubInboundPort

# NSGルール(Private) # TODO: 同一ネットワーク内であれば不要
#echo az network nsg rule create
#az network nsg rule create \
#  --resource-group $resourceGroup \
#  --nsg-name $nsgName \
#  --name $nsgPriRuleName \
#  --access Allow \
#  --protocol Tcp \
#  --direction Inbound \
#  --priority 110 \
#  --source-address-prefix VirtualNetwork \
#  --source-port-range "*" \
#  --destination-port-range $nsgPriInboundPort

# 仮想ネットワーク 及び サブネット作成
echo az network vnet create
az network vnet create \
    --name $vnetName \
    --resource-group $resourceGroup \
    --address-prefixes $vnetPrefix \
    --subnet-name $vmSubnetName \
    --subnet-prefixes $vmSubnetPrefix \
    --network-security-group $nsgName

# 仮想マシンの作成
# TODO: 管理用の踏み台サーバは別で用意する
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 \
  --subnet $vmSubnetName \
  --vnet-name $vnetName \
  --private-ip-address $vmIpAddress \
  --admin-username $vmUser \
  --custom-data 2_setup_influxdb.sh \
  --output json \
  --verbose
  # --public-ip-address "" # TODO: 踏み台以外はPublicアクセスなしにする

# 生成されたSSH鍵を移動( --ssh-dest-key-path が効かない為 )
# ※ https://docs.microsoft.com/en-us/cli/azure/vm?view=azure-cli-latest#az-vm-create
rm -rf pem
mkdir -p pem
if [ -e ~/.ssh/id_rsa ]; then
  mv ~/.ssh/id_rsa ./pem/
  mv ~/.ssh/id_rsa.pub ./pem/
fi

# InfluxDB用のポート開放
echo az vm open-port
az vm open-port \
  --resource-group $resourceGroup \
  --name $vmName \
  --port 8086

# Application Insights 拡張が利用できない場合は追加インストール
x=`az monitor app-insights --help 2>&1`
if [ "$?" != "0" ]; then
  az extension add -n application-insights
fi

# Application Insights コンポーネント作成
echo az monitor app-insights component create
if [ "$subscriptionId" != "" ]; then
  az monitor app-insights component create \
      --app $insightsName \
      --location $insightsRegion \
      --resource-group $resourceGroup \
      --query-access Enabled \
      --retention-time $insightsDays \
      --subscription $subscriptionId
fi

# プレミアムプランの作成(プレミアムプランでないと関数アプリからのVNetアクセス機能は提供されない)
echo az functionapp plan create
az functionapp plan create \
  --name $funcPlanName \
  --resource-group $resourceGroup \
  --location $region \
  --sku $funcPlanSku

# 関数アプリの作成
echo az functionapp create
if [ "$subscriptionId" != "" ]; then
  az functionapp create \
    --name $funcAppName \
    --storage-account $storageAccountName \
    --plan $funcPlanName \
    --resource-group $resourceGroup \
    --functions-version $funcVersion \
    --app-insights $insightsName
else
  az functionapp create \
    --name $funcAppName \
    --storage-account $storageAccountName \
    --plan $funcPlanName \
    --resource-group $resourceGroup \
    --functions-version $funcVersion
fi

# 関数アプリの環境変数の設定
echo "az functionapp config appsettings"
az functionapp config appsettings set \
    --name $funcAppName \
    --resource-group $resourceGroup \
    --settings "DB_HOST=$vmIpAddress" "DB_PORT=8086" "DB_NAME=sampledb" "DB_USER=sample" "DB_PW=sample"

# 関数アプリのVNet統合用のサブネット
echo az network vnet subnet create
az network vnet subnet create \
    --name $funcSubnetName \
    --resource-group $resourceGroup \
    --vnet-name $vnetName \
    --address-prefixes $funcSubnetPrefix

# 関数アプリのVNet統合(当コマンドはプレビュー版の為、将来で変更/削除される可能性あり)
echo az functionapp vnet-integration add
az functionapp vnet-integration add \
    --name $funcAppName \
    --resource-group $resourceGroup \
    --vnet $vnetName \
    --subnet $funcSubnetName
}}


#html(</div>)
// END tabs1-1

// START tabs1-2
#html(<div id="tabs1-2">)

このシェルはVM作成時に実行されるVMの初期化スクリプト。
az vm create 時の --custom-data として指定している為、手動で実行する必要はない。
処理内容は InfluxDBのセットアップ。(docker を使用)

2_setup_influxdb.sh
#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_sample
    container_name: influxdb_sample
    volumes:
      - ./influxdb:/var/lib/influxdb
      - ./influxdb.conf:/etc/influxdb/influxdb.conf
    ports:
      - 8086:8086
_EOYML_

# 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`
  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"
}}


#html(</div>)
// END tabs1-2

// START tabs1-3
#html(<div id="tabs1-3">)

このシェルでは関数アプリのビルド/デプロイを行う。
関数アプリのOSはデフォルトのWindowsを使用している為、Windowsm用にビルドしている。

3_deploy_funcapp.sh
#mycode2(){{
#!/bin/bash

source 0_env.sh

rm -rf functions.zip

exefile=`cat functions/host.json | grep defaultExecutablePath | awk '{print $2}' | sed 's/"//g'`

cd functions
rm -rf $exefile
#GOOS=linux GOARCH=amd64 go build -o $exefile
#GOOS=windows GOARCH=386 go build -o $exefile
GOOS=windows GOARCH=amd64 go build -o $exefile

zip -r ../functions.zip *

cd ../ 

az functionapp deployment source config-zip -g $resourceGroup -n $funcAppName --src functions.zip
}}


#html(</div>)
// END tabs1-3

// START tabs1-9
#html(<div id="tabs1-9">)

このシェルはリソースの一括削除用のシェル。
やっている事はリソースグループを指定して削除しているだけ。

9_remove_resources.sh
#mycode2(){{
#!/bin/bash

source ./0_env.sh
az group delete --name $resourceGroup
}}

#html(</div>)
// END tabs1-9

#html(</div>)
// END tabs1

#html(<script>$(function() { $("#tabs1").tabs(); });</script>)

#html(</div>)


* リソースの作成 [#kef97978]
#html(<div class="pl10">)

ログイン
#myterm2(){{
az login
}}

リソース作成
#myterm2(){{
./1_create_resources.sh
}}

リソースグループ配下に作成されたリソースをAzure Portal からみると下図の通り。
#html(<div style="display: inline-block; border: 1px solid #333">)
&ref(azure_resources.png,nolink);
#html(</div>)


#html(</div>)

* VM 上に InfluxDB が正しく作成されているか確認 [#db5b14c8]
#html(<div class="pl10">)

作成したVMのPublic IPを確認
#myterm2(){{
az vm list-ip-addresses -o table
VirtualMachine    PublicIPAddresses    PrivateIPAddresses
----------------  -------------------  --------------------
XXXXXXXXX          XX.XX.XXX.XX         10.1.1.5
}}

VMにSSH接続
#myterm2(){{
source ./0_env.sh
ssh -i ./pem/id_rsa $vmUser@確認したPublic IP
}}

InfluxDBにサンプルDBが作成されているか確認 (作成に少し時間がかかるので数分待ってから確認する)
#myterm2(){{
# dockerコンテナが起動しているか確認
sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
daffbdd72d06        influxdb:1.8        "/entrypoint.sh infl…"   10 minutes ago      Up 10 minutes       0.0.0.0:8086->8086/tcp   influxdb_sample

# サンプルDBが作成されているか確認
sudo docker exec -i influxdb_sample influx --execute "show databases"
name: databases
name
----
sampledb
_internal
}}

#html(</div>)

#html(</div>)


* 関数アプリの作成 [#x09d7bdd]
#html(<div class="pl10">)


// START tabs2
#html(){{
<div id="tabs2">
  <ul>
    <li><a href="#tabs2-1">host.json</a></li>
    <li><a href="#tabs2-2">local.settings.json</a></li>
    <li><a href="#tabs2-3">go_server_sample.go</a></li>
    <li><a href="#tabs2-4">BlobTrigger?/function.json</a></li>
  </ul>
}}

// START tabs2-1
#html(<div id="tabs2-1">)

functions/host.json
#mycode2(){{
{
    "version": "2.0",
    "httpWorker": {
        "description": {
            "defaultExecutablePath": "go_server_sample.exe"
        }
    },
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}
}}

#html(</div>)
// END tabs2-1

// START tabs2-2
#html(<div id="tabs2-2">)

functions/local.settings.json
#mycode2(){{
{
    "IsEncrypted": false,
    "Values": {
      "AzureWebJobsStorage": "UseDevelopmentStorage=true",
      "DB_HOST": "localhost",
      "DB_PORT": "8086",
      "DB_NAME": "sampledb",
      "DB_USER": "sample",
      "DB_PW": "sample"
    }   
}
}}

#html(</div>)
// END tabs2-2

// START tabs2-3
#html(<div id="tabs2-3">)

functions/go_server_sample.go
#mycode2(){{
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "math/rand"
    "time"
    "github.com/influxdata/influxdb-client-go"
)

type ReturnValue struct {
    Data string
}
type InvokeResponse struct {
    Outputs     map[string]interface{}
    Logs        []string
    ReturnValue interface{}
}

type InvokeRequest struct {
    Data     map[string]interface{}
    Metadata map[string]interface{}
}

func blobTriggerHandler(w http.ResponseWriter, r *http.Request) {

    var invokeReq InvokeRequest
    d := json.NewDecoder(r.Body)
    decodeErr := d.Decode(&invokeReq)
    if decodeErr != nil {
        http.Error(w, decodeErr.Error(), http.StatusBadRequest)
        return
    }
    fmt.Println("The JSON data is:invokeReq metadata......")
    fmt.Println(invokeReq.Metadata)

    returnValue := invokeReq.Data["triggerBlob"]
    returnValue := invokeReq.Data["blobData"]

    invokeResponse := InvokeResponse{Logs: []string{"test log1", "test log2"}, ReturnValue: returnValue}

    js, err := json.Marshal(invokeResponse)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // VM(InfluxDB)へのアクセス確認
    dbHost := getEnv("DB_HOST", "localhost")
    dbPort := getEnv("DB_PORT", "8086")
    dbName := getEnv("DB_NAME", "unknown")
    dbUser := getEnv("DB_USER", "unknown")
    dbPw   := getEnv("DB_PW", "unknown")
    client := influxdb2.NewClient(fmt.Sprintf("http://%s:%s", dbHost, dbPort), fmt.Sprintf("%s:%s", dbUser, dbPw))
    create_sample(client, dbName)
    client.Close()

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}

/**
 * 環境変数の取得.
 */
func getEnv(envName string, defaultValue string) string {

    value, exists := os.LookupEnv(envName)
    if exists {
        return value
    } else {
        return defaultValue
    }   
}

/**
 * サンプルデータ登録(InfluxDB).
 */
func create_sample(client influxdb2.Client, dbName string) {

    fmt.Printf("Write START\n")

    writeAPI := client.WriteAPIBlocking("", fmt.Sprintf("%s/autogen", dbName))

    // サンプルデータ登録
    for i := 0; i < 5; i++ {
        p := influxdb2.NewPointWithMeasurement("sample").
            AddTag("tag1", "sample1").
            AddTag("tag2", fmt.Sprintf("row%d", i+1)).
            AddField("field1", i + 1). 
            AddField("field2", rand.Float64()).
            SetTime(time.Now())
        err := writeAPI.WritePoint(context.Background(), p)
        if err != nil {
            fmt.Printf("Write2 error: %s\n", err.Error())
        } else {
            fmt.Printf("Write2 success.\n")
        }   
    }   

    fmt.Printf("Write END\n")
}

func main() {
    httpInvokerPort, exists := os.LookupEnv("FUNCTIONS_HTTPWORKER_PORT")
    if exists {
        fmt.Println("FUNCTIONS_HTTPWORKER_PORT: " + httpInvokerPort)
    }   
    mux := http.NewServeMux()
    mux.HandleFunc("/BlobTrigger", blobTriggerHandler)
    log.Println("Go server Listening...on httpInvokerPort:", httpInvokerPort)
    log.Fatal(http.ListenAndServe(":"+httpInvokerPort, mux))
}}

#html(</div>)
// END tabs2-3

// START tabs2-4
#html(<div id="tabs2-4">)

functions/BlobTrigger/function.json
#mycode2(){{
{
  "bindings" : [ {
    "type" : "blobTrigger",
    "direction" : "in",
    "name" : "triggerBlob",
    "name" : "blobData",
    "path" : "作成したストレージコンテナ名1/{name}",
    "dataType" : "binary",
    "connection" : "AzureWebJobsStorage"
  },
  {
    "type" : "blob",
    "direction" : "out",
    "name" : "$return",
    "path" : "作成したストレージコンテナ名2/{name}",
    "dataType" : "binary",
    "connection" : "AzureWebJobsStorage"
  } ]
}
}}

#html(</div>)
// END tabs2-4

#html(</div>)
// END tabs2

#html(<script>$(function() { $("#tabs2").tabs(); });</script>)


#html(</div>)

* 関数アプリのデプロイ [#v8360ab2]
#html(<div class="pl10">)

デプロイ
#myterm2(){{
./3_deploy_funcapp.sh
}}

#html(</div>)

* 動作確認 [#rcf31f7d]
#html(<div class="pl10">)

接続文字列の確認
#myterm2(){{
source 0_env.sh
az storage account show-connection-string --name $storageAccountName
{
  "connectionString": "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=xxxxxxxxxxx;AccountKey=xxxxxxxxxxxxxx"
}
}}

ストレージエクスプローラにStorageアカウントをアタッチ
#html(<div class="p20" style="border: 1px solid #333"><div class="pl20 ib">)
&ref(strexp1.png,nolink);
#html(</div><div class="pl20 ib">)
&ref(strexp2.png,nolink);
#html(</div><div class="pl20 ib">)
&ref(strexp3.png,nolink);
#html(</div></div>)

ファイルをアップロード
#html(<div style="padding: 20px; border: 1px solid #333">)
&ref(strexp4.png,nolink);
#html(</div>)


VMに接続して InfluxDB にデータが登録されているか確認
#myterm2(){{
ssh -i ./pem/id_rsa $vmUser@確認したPublic IP
sudo docker exec -i influxdb_sample influx -database sampledb -username sample --execute "select * from sample"
name: sample
time                field1 field2              tag1    tag2
----                ------ ------              ----    ----
1596015616248498800 1      0.6046602879796196  sample1 row1
1596015616436070000 2      0.9405090880450124  sample1 row2
1596015616451632700 4      0.4377141871869802  sample1 row4
1596015616451632700 3      0.6645600532184904  sample1 row3
1596015616467330200 5      0.4246374970712657  sample1 row5
}}

#html(</div>)

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS