diff --git a/src/counter/src/counterService/Counter.cs b/src/counter/src/counterService/Counter.cs index a45e579e..0a528f85 100644 --- a/src/counter/src/counterService/Counter.cs +++ b/src/counter/src/counterService/Counter.cs @@ -8,6 +8,7 @@ namespace Microsoft.ServiceFabricMesh.Samples.Counter.Service using System; using System.Globalization; using System.IO; + using System.Text; using System.Threading; using System.Threading.Tasks; @@ -103,7 +104,12 @@ private static void WriteCounterValue(string stateFilePath, long value) { try { - File.WriteAllText(stateFilePath, value.ToString()); + using (var file = new FileStream(stateFilePath, FileMode.OpenOrCreate, FileAccess.Write)) + { + var bytes = Encoding.ASCII.GetBytes(value.ToString()); + file.Write(bytes, 0, bytes.Length); + file.Flush(); + } } catch(Exception e) { diff --git a/templates/README.md b/templates/README.md index 2a5a2940..79cae09f 100644 --- a/templates/README.md +++ b/templates/README.md @@ -18,6 +18,7 @@ This command will output all information about one network resource. To get the |Sample|Scenario Description|Deploy to Azure - Linux|Deploy to Azure - Windows| |------------|--------------------|-----------------------|-------------------------| | [Hello World App](./helloworld) | Deploy a single container microservices application from public or private container registry and connect to the service endpoint. | | | -| [Counter App](./counter) | Store state by mounting Azure Files based volume inside the container.

**Note:** This template requires an Azure Files file share to already be provisioned [Instructions](https://docs.microsoft.com/en-us/azure/storage/files/storage-how-to-create-file-share) | | | +| [Counter App](./counter) | Store state by mounting Azure Files based volume inside the container.

**Note:** This template requires an Azure Files file share to already be provisioned [Instructions](https://docs.microsoft.com/en-us/azure/storage/files/storage-how-to-create-file-share) | | | +| [Counter App (Service Fabric VolumeDisk)](./counter) | Store state on Service Fabric Volume Disk based volume inside the container. | | | | [Voting App](./voting) | Create an application with a frontend and backend service that uses DNS-based resolution. | | | | [Visual Objects App](./visualobjects) | Scale and upgrade microservices within an application. | [Look here](./visualobjects/) | [Look here](./visualobjects/) | \ No newline at end of file diff --git a/templates/counter/counter.azurefilesvolume.linux.json b/templates/counter/counter.azurefilesvolume.linux.json new file mode 100644 index 00000000..f1b5e90d --- /dev/null +++ b/templates/counter/counter.azurefilesvolume.linux.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the resources (e.g. westus, eastus, westeurope)." + } + }, + "fileShareName": { + "type": "string", + "metadata": { + "description": "Name of the Azure Files file share that provides the volume for the container." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Name of the Azure storage account that contains the file share." + } + }, + "storageAccountKey": { + "type": "securestring", + "metadata": { + "description": "Access key for the Azure storage account that contains the file share." + } + }, + "stateFolderName": { + "type": "string", + "defaultValue": "CounterService", + "metadata": { + "description": "Folder in which to store the state. Provide a empty value to create a unique folder for each container to store the state. A non-empty value will retain the state across deployments, however if more than one applications are using the same folder, the counter may update more frequently." + } + } + }, + "resources": [ + { + "apiVersion": "2018-09-01-preview", + "name": "counterAzureFileShareAccountKey", + "type": "Microsoft.ServiceFabricMesh/secrets", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "inlinedValue", + "contentType": "text/plain", + "description": "Access key for the Azure storage account that contains the file share." + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterAzureFileShareAccountKey/v1", + "type": "Microsoft.ServiceFabricMesh/secrets/values", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/counterAzureFileShareAccountKey" + ], + "properties": { + "value": "[parameters('storageAccountKey')]" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterVolume", + "type": "Microsoft.ServiceFabricMesh/volumes", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/counterAzureFileShareAccountKey/values/v1" + ], + "properties": { + "description": "Azure Files storage volume for counter App.", + "provider": "SFAzureFile", + "azureFileParameters": { + "shareName": "[parameters('fileShareName')]", + "accountName": "[parameters('storageAccountName')]", + "accountKey": "[resourceId('Microsoft.ServiceFabricMesh/secrets/values','counterAzureFileShareAccountKey','v1')]" + } + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterNetwork", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Azure Service Fabric Mesh Counter Application network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterApp", + "type": "Microsoft.ServiceFabricMesh/applications", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetwork", + "Microsoft.ServiceFabricMesh/volumes/counterVolume" + ], + "properties": { + "description": "Azure Service Fabric Mesh Counter Application.", + "services": [ + { + "name": "counterService", + "properties": { + "description": "A web service that serves the counter value stored in the Azure Files volume.", + "osType": "linux", + "codePackages": [ + { + "name": "counterCode", + "image": "seabreeze/azure-mesh-counter:0.1-alpine", + "volumeRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/volumes', 'counterVolume')]", + "destinationPath": "/app/data" + } + ], + "endpoints": [ + { + "name": "counterServiceListener", + "port": 80 + } + ], + "environmentVariables": [ + { + "name": "STATE_FOLDER_NAME", + "value": "[parameters('stateFolderName')]" + } + ], + "resources": { + "requests": { + "cpu": 0.5, + "memoryInGB": 0.5 + } + } + } + ], + "replicaCount": 1, + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetwork')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] + } + ] + } + } + ] + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for counter sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { + "applicationName": "counterApp", + "serviceName": "counterService", + "endpointName": "counterServiceListener" + } + } + ] + } + } + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('counterGateway').ipAddress]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/templates/counter/counter.azurefilesvolume.windows.json b/templates/counter/counter.azurefilesvolume.windows.json new file mode 100644 index 00000000..4a01c604 --- /dev/null +++ b/templates/counter/counter.azurefilesvolume.windows.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the resources (e.g. westus, eastus, westeurope)." + } + }, + "fileShareName": { + "type": "string", + "metadata": { + "description": "Name of the Azure Files file share that provides the volume for the container." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Name of the Azure storage account that contains the file share." + } + }, + "storageAccountKey": { + "type": "securestring", + "metadata": { + "description": "Access key for the Azure storage account that contains the file share." + } + }, + "stateFolderName": { + "type": "string", + "defaultValue": "CounterService", + "metadata": { + "description": "Folder in which to store the state. Provide a empty value to create a unique folder for each container to store the state. A non-empty value will retain the state across deployments, however if more than one applications are using the same folder, the counter may update more frequently." + } + } + }, + "resources": [ + { + "apiVersion": "2018-09-01-preview", + "name": "counterAzureFileShareAccountKey", + "type": "Microsoft.ServiceFabricMesh/secrets", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "inlinedValue", + "contentType": "text/plain", + "description": "Access key for the Azure storage account that contains the file share." + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterAzureFileShareAccountKey/v1", + "type": "Microsoft.ServiceFabricMesh/secrets/values", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/counterAzureFileShareAccountKey" + ], + "properties": { + "value": "[parameters('storageAccountKey')]" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterVolume", + "type": "Microsoft.ServiceFabricMesh/volumes", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/counterAzureFileShareAccountKey/values/v1" + ], + "properties": { + "description": "Azure Files storage volume for counter App.", + "provider": "SFAzureFile", + "azureFileParameters": { + "shareName": "[parameters('fileShareName')]", + "accountName": "[parameters('storageAccountName')]", + "accountKey": "[resourceId('Microsoft.ServiceFabricMesh/secrets/values','counterAzureFileShareAccountKey','v1')]" + } + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterNetworkWindows", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Azure Service Fabric Mesh Counter Application network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterAppWindows", + "type": "Microsoft.ServiceFabricMesh/applications", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetworkWindows", + "Microsoft.ServiceFabricMesh/volumes/counterVolume" + ], + "properties": { + "description": "Azure Service Fabric Mesh Counter Application.", + "services": [ + { + "name": "counterService", + "properties": { + "description": "A web service that serves the counter value stored in the Azure Files volume.", + "osType": "windows", + "codePackages": [ + { + "name": "counterCode", + "image": "seabreeze/azure-mesh-counter:0.1-nanoserver-1709", + "volumeRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/volumes', 'counterVolume')]", + "destinationPath": "C:\\app\\data" + } + ], + "endpoints": [ + { + "name": "counterServiceListener", + "port": 80 + } + ], + "environmentVariables": [ + { + "name": "STATE_FOLDER_NAME", + "value": "[parameters('stateFolderName')]" + } + ], + "resources": { + "requests": { + "cpu": 0.5, + "memoryInGB": 0.5 + } + } + } + ], + "replicaCount": 1, + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetworkWindows')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] + } + ] + } + } + ] + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "counterGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for counter sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { + "applicationName": "counterAppWindows", + "serviceName": "counterService", + "endpointName": "counterServiceListener" + } + } + ] + } + } + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('counterGatewayWindows').ipAddress]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/templates/counter/mesh_rp.linux.json b/templates/counter/counter.sfreliablevolume.linux.json similarity index 61% rename from templates/counter/mesh_rp.linux.json rename to templates/counter/counter.sfreliablevolume.linux.json index 07d0687e..66ae4e51 100644 --- a/templates/counter/mesh_rp.linux.json +++ b/templates/counter/counter.sfreliablevolume.linux.json @@ -4,28 +4,11 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } }, - "fileShareName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Files file share that provides the volume for the container." - } - }, - "storageAccountName": { - "type": "string", - "metadata": { - "description": "Name of the Azure storage account that contains the file share." - } - }, - "storageAccountKey": { - "type": "securestring", - "metadata": { - "description": "Access key for the Azure storage account that contains the file share." - } - }, "stateFolderName": { "type": "string", "defaultValue": "CounterService", @@ -36,13 +19,24 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", + "name": "counterNetwork", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Azure Service Fabric Mesh Counter Application network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", "name": "counterApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", "dependsOn": [ - "Microsoft.ServiceFabricMesh/networks/counterAppNetwork", - "Microsoft.ServiceFabricMesh/volumes/counterVolume" + "Microsoft.ServiceFabricMesh/networks/counterNetwork" ], "properties": { "description": "Azure Service Fabric Mesh Counter Application.", @@ -50,15 +44,19 @@ { "name": "counterService", "properties": { - "description": "A web service that serves the counter value stored in the Azure Files volume.", + "description": "A web service that serves the counter value stored in Service Fabric Reliable volume.", "osType": "linux", "codePackages": [ { - "name": "counterService", + "name": "counterCode", "image": "seabreeze/azure-mesh-counter:0.1-alpine", - "volumeRefs": [ + "volumes": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/volumes', 'counterVolume')]", + "name": "sfvol", + "creationParameters": { + "kind": "ServiceFabricVolumeDisk", + "sizeDisk": "Small" + }, "destinationPath": "/app/data" } ], @@ -85,7 +83,12 @@ "replicaCount": 1, "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterAppNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetwork')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] } ] } @@ -94,42 +97,39 @@ } }, { - "apiVersion": "2018-07-01-preview", - "name": "counterAppNetwork", - "type": "Microsoft.ServiceFabricMesh/networks", + "apiVersion": "2018-09-01-preview", + "name": "counterGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", "location": "[parameters('location')]", - "dependsOn": [], + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetwork" + ], "properties": { - "description": "Azure Service Fabric Mesh Counter Application network.", - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "counterServiceIngress", - "publicPort": "80", + "description": "Service Fabric Mesh Gateway for counter sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "counterApp", "serviceName": "counterService", "endpointName": "counterServiceListener" } - ] - } - } - }, - { - "apiVersion": "2018-07-01-preview", - "name": "counterVolume", - "type": "Microsoft.ServiceFabricMesh/volumes", - "location": "[parameters('location')]", - "dependsOn": [], - "properties": { - "description": "Azure Files storage volume for counter App.", - "provider": "SFAzureFile", - "azureFileParameters": { - "shareName": "[parameters('fileShareName')]", - "accountName": "[parameters('storageAccountName')]", - "accountKey": "[parameters('storageAccountKey')]" - } + } + ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('counterGateway').ipAddress]", + "type": "string" + } + } } \ No newline at end of file diff --git a/templates/counter/mesh_rp.windows.json b/templates/counter/counter.sfreliablevolume.windows.json similarity index 61% rename from templates/counter/mesh_rp.windows.json rename to templates/counter/counter.sfreliablevolume.windows.json index 22e738d6..eed0f5d3 100644 --- a/templates/counter/mesh_rp.windows.json +++ b/templates/counter/counter.sfreliablevolume.windows.json @@ -4,28 +4,11 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } }, - "fileShareName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Files file share that provides the volume for the container." - } - }, - "storageAccountName": { - "type": "string", - "metadata": { - "description": "Name of the Azure storage account that contains the file share." - } - }, - "storageAccountKey": { - "type": "securestring", - "metadata": { - "description": "Access key for the Azure storage account that contains the file share." - } - }, "stateFolderName": { "type": "string", "defaultValue": "CounterService", @@ -36,13 +19,24 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", + "name": "counterNetworkWindows", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Azure Service Fabric Mesh Counter Application network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", "name": "counterAppWindows", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", "dependsOn": [ - "Microsoft.ServiceFabricMesh/networks/counterAppWindowsNetwork", - "Microsoft.ServiceFabricMesh/volumes/counterVolumeWindows" + "Microsoft.ServiceFabricMesh/networks/counterNetworkWindows" ], "properties": { "description": "Azure Service Fabric Mesh Counter Application.", @@ -50,15 +44,19 @@ { "name": "counterService", "properties": { - "description": "A web service that serves the counter value stored in the Azure Files volume.", + "description": "A web service that serves the counter value stored in Service Fabric Reliable volume.", "osType": "windows", "codePackages": [ { - "name": "counterService", + "name": "counterCode", "image": "seabreeze/azure-mesh-counter:0.1-nanoserver-1709", - "volumeRefs": [ + "volumes": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/volumes', 'counterVolumeWindows')]", + "name": "sfvol", + "creationParameters": { + "kind": "ServiceFabricVolumeDisk", + "sizeDisk": "Small" + }, "destinationPath": "C:\\app\\data" } ], @@ -85,7 +83,12 @@ "replicaCount": 1, "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterAppWindowsNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetworkWindows')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] } ] } @@ -94,42 +97,39 @@ } }, { - "apiVersion": "2018-07-01-preview", - "name": "counterAppWindowsNetwork", - "type": "Microsoft.ServiceFabricMesh/networks", + "apiVersion": "2018-09-01-preview", + "name": "counterGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", "location": "[parameters('location')]", - "dependsOn": [], + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/counterNetworkWindows" + ], "properties": { - "description": "Azure Service Fabric Mesh Counter Application network.", - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "counterServiceIngress", - "publicPort": "80", + "description": "Service Fabric Mesh Gateway for counter sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'counterNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "counterAppWindows", "serviceName": "counterService", "endpointName": "counterServiceListener" } - ] - } - } - }, - { - "apiVersion": "2018-07-01-preview", - "name": "counterVolumeWindows", - "type": "Microsoft.ServiceFabricMesh/volumes", - "location": "[parameters('location')]", - "dependsOn": [], - "properties": { - "description": "Azure Files storage volume for counter App.", - "provider": "SFAzureFile", - "azureFileParameters": { - "shareName": "[parameters('fileShareName')]", - "accountName": "[parameters('storageAccountName')]", - "accountKey": "[parameters('storageAccountKey')]" - } + } + ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('counterGatewayWindows').ipAddress]", + "type": "string" + } + } } \ No newline at end of file diff --git a/templates/counter/readme.md b/templates/counter/readme.md new file mode 100644 index 00000000..ecca13c2 --- /dev/null +++ b/templates/counter/readme.md @@ -0,0 +1,102 @@ +--- +title: Store state in an Azure Service Fabric Mesh application by mounting an Azure Files based volume inside the container | Microsoft Docs +description: Learn how to store state in an Azure Service Fabric Mesh application by mounting an Azure Files based volume inside the container using the Azure CLI. +services: service-fabric-mesh +documentationcenter: .net +author: rwike77 +manager: jeconnoc +editor: caiwang +ms.assetid: +ms.service: service-fabric-mesh +ms.devlang: azure-cli +ms.topic: conceptual +ms.tgt_pltfrm: NA +ms.workload: NA +ms.date: 11/21/2018 +ms.author: ryanwi +ms.custom: mvc, devcenter +--- + +# Store state in an Azure Service Fabric Mesh application by mounting an Azure Files based volume inside the container + +This article shows how to store state in Azure Files by mounting a volume inside the container of a Service Fabric Mesh application. In this example, the Counter application has an ASP.NET Core service with a web page that shows counter value in a browser. + +The `counterService` periodically reads a counter value from a file, increments it and write it back to the file. The file is stored in a folder that is mounted on the volume backed by Azure Files share. + +## Prerequisites + +You can use the Azure Cloud Shell or a local installation of the Azure CLI to complete this task. To use the Azure CLI with this article, ensure that `az --version` returns at least `azure-cli (2.0.43)`. Install (or update) the Azure Service Fabric Mesh CLI extension module by following these [instructions](service-fabric-mesh-howto-setup-cli.md). + +## Sign in to Azure + +Sign in to Azure and set your subscription. + +```azurecli-interactive +az login +az account set --subscription "" +``` + +## Create a file share + +Create an Azure file share by following these [instructions](/azure/storage/files/storage-how-to-create-file-share). The storage account name, storage account key and the file share name are referenced as ``, ``, and `` in the following instructions. These values are available in your Azure portal: +* - Under **Storage Accounts**, it is the name of the storage account you used when you created the file share. +* - Select your storage account under **Storage Accounts** and then select **Access keys** and use the value under **key1**. +* - Select your storage account under **Storage Accounts** and then select **Files**. The name to use is the name of the file share you just created. + +## Create a resource group + +Create a resource group to deploy the application to. The following command creates a resource group named `myResourceGroup` in a location in the eastern United States. + +```azurecli-interactive +az group create --name myResourceGroup --location eastus +``` + +## Deploy the template + +Create the application and related resources using the following command, and provide the values for `storageAccountName`, `storageAccountKey` and `fileShareName` from the earlier [Create a file share](#create-a-file-share) step. + +The `storageAccountKey` parameter in the template is a secure string. It will not be displayed in the deployment status and `az mesh service show` commands. Ensure that it is correctly specified in the following command. + +The following command deploys a Linux application using the [counter.azurefilesvolume.linux.json template](https://sfmeshsamples.blob.core.windows.net/templates/counter/counter.azurefilesvolume.linux.json). To deploy a Windows application, use the [counter.azurefilesvolume.windows.json template](https://sfmeshsamples.blob.core.windows.net/templates/counter/counter.azurefilesvolume.windows.json). Be aware that larger container images may take longer to deploy. + +```azurecli-interactive +az mesh deployment create --resource-group myResourceGroup --template-uri https://sfmeshsamples.blob.core.windows.net/templates/counter/counter.azurefilesvolume.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}, \"fileShareName\": {\"value\": \"\"}, \"storageAccountName\": {\"value\": \"\"}, \"storageAccountKey\": {\"value\": \"\"}}" +``` + +In a few minutes, the command should return json output. The "outputs" section prints the public ip addres . +``` + "outputs": { + "publicIPAddress": { + "type": "String", + "value": "" + } +``` +## Open the application + +The deployment command will return the public IP address of the service endpoint. Once the application successfully deploys, get the public IP address for the service endpoint and open it from a browser. It will display a web page with the counter value being updated every second. + +The network resource name for this application is `counterNetwork`. You can see info about the app such as its description, location, resource group, etc. by using the following command: + +```azurecli-interactive +az mesh network show --resource-group myResourceGroup --name counterNetwork +``` + +## Verify that the application is able to use the volume + +The application creates a file named `counter.txt` in the file share inside `counter/counterService` folder. The content of this file is the counter value being displayed on the web page. + +The file may be downloaded using any tool that enables browsing an Azure Files file share, such as the [Microsoft Azure Storage Explorer](https://azure.microsoft.com/features/storage-explorer/). + +## Delete the resources + +Frequently delete the resources you are no longer using in Azure. To delete the resources related to this example, delete the resource group in which they were deployed (which deletes everything associated with the resource group) with the following command: + +```azurecli-interactive +az group delete --resource-group myResourceGroup +``` + +## Next steps + +- View the Azure Files volume sample application on [GitHub](https://github.com/Azure-Samples/service-fabric-mesh/tree/master/src/counter). +- To learn more about Service Fabric Resource Model, see [Service Fabric Mesh Resource Model](service-fabric-mesh-service-fabric-resources.md). +- To learn more about Service Fabric Mesh, read the [Service Fabric Mesh overview](service-fabric-mesh-overview.md). diff --git a/templates/helloworld/mesh_rp.linux.json b/templates/helloworld/helloworld.linux.json similarity index 64% rename from templates/helloworld/mesh_rp.linux.json rename to templates/helloworld/helloworld.linux.json index 2f3b6362..225786c2 100644 --- a/templates/helloworld/mesh_rp.linux.json +++ b/templates/helloworld/helloworld.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "helloWorldNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "helloWorldIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Network for HelloWorld sample.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/helloWorldNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "helloWorldApp", "serviceName": "helloWorldService", "endpointName": "helloWorldListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "helloWorldApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -40,11 +61,10 @@ "Microsoft.ServiceFabricMesh/networks/helloWorldNetwork" ], "properties": { - "description": "Service Fabric Mesh HelloWorld Application!", + "description": "Service Fabric Mesh HelloWorld Application.", "services": [ { "type": "Microsoft.ServiceFabricMesh/services", - "location": "[parameters('location')]", "name": "helloWorldService", "properties": { "description": "Service Fabric Mesh Hello World Service.", @@ -80,7 +100,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetwork')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] } ] } @@ -88,5 +113,11 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('helloWorldGateway').ipAddress]", + "type": "string" + } + } } \ No newline at end of file diff --git a/templates/helloworld/mesh_rp.private_registry.linux.json b/templates/helloworld/helloworld.private_registry.linux.json similarity index 51% rename from templates/helloworld/mesh_rp.private_registry.linux.json rename to templates/helloworld/helloworld.private_registry.linux.json index 01bd2305..48230fc9 100644 --- a/templates/helloworld/mesh_rp.private_registry.linux.json +++ b/templates/helloworld/helloworld.private_registry.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -29,51 +30,97 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryPassword", + "type": "Microsoft.ServiceFabricMesh/secrets", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "inlinedValue", + "contentType": "text/plain", + "description": "Azure container registry password." + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryPassword/v1", + "type": "Microsoft.ServiceFabricMesh/secrets/values", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/helloWorldPrivateRegistryPassword" + ], + "properties": { + "value": "[parameters('registry-password')]" + } + }, + { + "apiVersion": "2018-09-01-preview", "name": "helloWorldPrivateRegistryNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Network for HelloWorld sample deployed from private registry.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/helloWorldPrivateRegistryNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample deployed from private registry.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldPrivateRegistryNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "helloWorldPrivateRegistryApp", "serviceName": "helloWorldService", "endpointName": "helloWorldListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "helloWorldPrivateRegistryApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", "dependsOn": [ - "Microsoft.ServiceFabricMesh/networks/helloWorldPrivateRegistryNetwork" + "Microsoft.ServiceFabricMesh/networks/helloWorldPrivateRegistryNetwork", + "Microsoft.ServiceFabricMesh/secrets/helloWorldPrivateRegistryPassword/values/v1" ], "properties": { - "description": "SeaBreeze example application with container deployed from private image registry.", + "description": "Service Fabric Mesh HelloWorld application with container deployed from private registry.", "services": [ { "type": "Microsoft.ServiceFabricMesh/services", "location": "[parameters('location')]", "name": "helloWorldService", "properties": { - "description": "SeaBreeze example service with container deployed from private image registry.", + "description": "Service Fabric Mesh HelloWorld service with container deployed from private registry.", "osType": "linux", "codePackages": [ { "name": "helloWorldCode", - "image": "azure-mesh-helloworld:1.1-alpine", + "image": "[concat(parameters('registry-server'), '/seabreeze/azure-mesh-helloworld:1.1-alpine')]", "imageRegistryCredential": { "server": "[parameters('registry-server')]", "username": "[parameters('registry-username')]", - "password": "[parameters('registry-password')]" + "password": "[resourceId('Microsoft.ServiceFabricMesh/secrets/values','helloWorldPrivateRegistryPassword','v1')]" }, "endpoints": [ { @@ -92,7 +139,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldPrivateRegistryNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldPrivateRegistryNetwork')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] } ] } @@ -100,5 +152,11 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('helloWorldPrivateRegistryGateway').ipAddress]", + "type": "string" + } + } } \ No newline at end of file diff --git a/templates/helloworld/helloworld.private_registry.windows.json b/templates/helloworld/helloworld.private_registry.windows.json new file mode 100644 index 00000000..b445e8d1 --- /dev/null +++ b/templates/helloworld/helloworld.private_registry.windows.json @@ -0,0 +1,162 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the resources (e.g. westus, eastus, westeurope)." + } + }, + "registry-server": { + "type": "string", + "metadata": { + "description": "Private image registry server. For example, .azurecr.io" + } + }, + "registry-username": { + "type": "string", + "metadata": { + "description": "User name for the private image registry server. For Azure Container Registry the CLI command 'az acr credential show --name --query \"username\"' will show the username." + } + }, + "registry-password": { + "type": "securestring", + "metadata": { + "description": "Password for the private image registry server. For Azure Container Registry the CLI command 'az acr credential show --name --query \"passwords[0].value\"' will show the password." + } + } + }, + "resources": [ + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryPassword", + "type": "Microsoft.ServiceFabricMesh/secrets", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "inlinedValue", + "contentType": "text/plain", + "description": "Azure container registry password." + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryPassword/v1", + "type": "Microsoft.ServiceFabricMesh/secrets/values", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/secrets/helloWorldPrivateRegistryPassword" + ], + "properties": { + "value": "[parameters('registry-password')]" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryNetworkWindows", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Service Fabric Mesh Network for HelloWorld sample deployed from private registry.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/helloWorldPrivateRegistryNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample deployed from private registry.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldPrivateRegistryNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { + "applicationName": "helloWorldPrivateRegistryAppWindows", + "serviceName": "helloWorldService", + "endpointName": "helloWorldListener" + } + } + ] + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldPrivateRegistryAppWindows", + "type": "Microsoft.ServiceFabricMesh/applications", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/helloWorldPrivateRegistryNetworkWindows", + "Microsoft.ServiceFabricMesh/secrets/helloWorldPrivateRegistryPassword/values/v1" + ], + "properties": { + "description": "Service Fabric Mesh HelloWorld application with container deployed from private registry.", + "services": [ + { + "type": "Microsoft.ServiceFabricMesh/services", + "location": "[parameters('location')]", + "name": "helloWorldService", + "properties": { + "description": "Service Fabric Mesh HelloWorld service with container deployed from private registry.", + "osType": "windows", + "codePackages": [ + { + "name": "helloWorldCode", + "image": "[concat(parameters('registry-server'), '/seabreeze/azure-mesh-helloworld:1.1-windowsservercore-1709')]", + "imageRegistryCredential": { + "server": "[parameters('registry-server')]", + "username": "[parameters('registry-username')]", + "password": "[resourceId('Microsoft.ServiceFabricMesh/secrets/values','helloWorldPrivateRegistryPassword','v1')]" + }, + "endpoints": [ + { + "name": "helloWorldListener", + "port": "80" + } + ], + "resources": { + "requests": { + "cpu": "1", + "memoryInGB": "1" + } + } + } + ], + "replicaCount": "1", + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldPrivateRegistryNetworkWindows')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] + } + ] + } + } + ] + } + } + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('helloWorldPrivateRegistryGatewayWindows').ipAddress]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/templates/helloworld/mesh_rp.windows.json b/templates/helloworld/helloworld.windows.json similarity index 58% rename from templates/helloworld/mesh_rp.windows.json rename to templates/helloworld/helloworld.windows.json index 96f193aa..6d8f1e4c 100644 --- a/templates/helloworld/mesh_rp.windows.json +++ b/templates/helloworld/helloworld.windows.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,27 +12,49 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "helloWorldNetworkWindows", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Network for HelloWorld Window sample.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "helloWorldGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "tags": {}, + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/helloWorldNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld Windows sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "helloWorldAppWindows", "serviceName": "helloWorldService", "endpointName": "helloWorldListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "helloWorldAppWindows", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -39,11 +62,10 @@ "Microsoft.ServiceFabricMesh/networks/helloWorldNetworkWindows" ], "properties": { - "description": "Service Fabric Mesh HelloWorld Application!", + "description": "Service Fabric Mesh HelloWorld Windows Application.", "services": [ { "type": "Microsoft.ServiceFabricMesh/services", - "location": "[parameters('location')]", "name": "helloWorldService", "properties": { "description": "Service Fabric Mesh Hello World Service.", @@ -69,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetworkWindows')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetworkWindows')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] } ] } @@ -77,5 +104,11 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('helloWorldGatewayWindows').ipAddress]", + "type": "string" + } + } } \ No newline at end of file diff --git a/templates/helloworld/readme.md b/templates/helloworld/readme.md new file mode 100644 index 00000000..c75b377d --- /dev/null +++ b/templates/helloworld/readme.md @@ -0,0 +1,71 @@ +# Deploy Hello World application to Azure Service Fabric Mesh + +This article shows how to deploy a simple application to Azure Service Fabric Mesh that uses the container images from a public registry, to deploy application from a private registry, see **[Deploying from a private registry](readme.private-registry.md)**. + +## Set up Service Fabric Mesh CLI + +You can use the Azure Cloud Shell or a local installation of the Azure CLI to complete this quickstart. Install Azure Service Fabric Mesh CLI extension module by following these [instructions](https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-howto-setup-cli). + +## Sign in to Azure +Sign in to Azure and set your subscription. + +```azurecli +az login +az account set --subscription "" +``` + +## Create a resource group + +An Azure resource group is a logical container into which Azure resources are deployed and managed. Use the following command to create a resource group named *myResourceGroup* in the *eastus* location. + +```azurecli +az group create --name myResourceGroup --location eastus +``` + +## Deploy the application + +Create the application and related resources in the resource group using the `az mesh deployment create` command. The following will deploy the Hello World Linux application using the [helloworld.linux.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.linux.json). If you want to deploy a Windows application instead, use the [helloworld.windows.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.linux.json) instead. Windows container images are larger than Linux container images and may take more time to deploy. + +*Note: both these templates reference a container image in a public registry. If you would like to learn how to deploy an application that references container images from a private registry, see [Deploying from a private registry](readme.private-registry.md)* + +If you're using a Bash console or Windows Command Prompt, run the following: + + +```azurecli +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}" +``` + +If you're using a PowerShell console, run the following: + +```azurecli +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.linux.json --parameters "{'location': {'value': 'eastus'}}" +``` +In a few minutes, if the deployment is successful the command will return the list of created resources and an output property for the public IP address. + +## See the app in action + +### Open the application +Once the application successfully deploys, copy the public IP address from the `publicIPAddress` output property. Open the IP address in a web browser. A web page with the Azure Service Fabric Mesh logo displays. + +You can also obtain the public IP address from the details of the `helloWorldGateway` resource using the following command. The name of the gateway for Windows application is `helloWorldGatewayWindows`. + +```azurecli +az mesh gateway show -g myResourceGroup -n helloWorldGateway -o table +``` + +### Check the application details +You can check the application's status using the `az mesh app show` command. This command provides useful information that you can follow up on. + +The application name for the Linux app is `helloWorldApp` and for Windows app is `helloWorldAppWindows`, to gather the details on the application execute the following command: + +```azurecli +az mesh app show --resource-group myResourceGroup --name helloWorldApp +``` + +## Clean up + +When you are ready to delete the application, run the [az group delete][az-group-delete] command to remove the resource group and the application and network resources it contains. + +```azurecli +az group delete --name myResourceGroup +``` \ No newline at end of file diff --git a/templates/helloworld/readme.private-registry.md b/templates/helloworld/readme.private-registry.md new file mode 100644 index 00000000..ac38e644 --- /dev/null +++ b/templates/helloworld/readme.private-registry.md @@ -0,0 +1,231 @@ +# Deploy a Service Fabric Mesh app from a private container image registry + +This article shows how to deploy a simple Azure Service Fabric Mesh app that uses container images from a public registry. + +## Install Docker + +### Windows 10 + +Download and install the latest version of [Docker Community Edition for Windows][download-docker] to support the containerized Service Fabric apps used by Service Fabric Mesh. + +During installation, select **Use Windows containers instead of Linux containers** when asked. + +If Hyper-V is not enabled on your machine, Docker's installer will offer to enable it. Click **OK** to do so if prompted. + +### Windows Server 2016 + +If you don't have the Hyper-V role enabled, open PowerShell as an administrator and run the following command to enable Hyper-V, and then restart your computer. For more information, see [Docker Enterprise Edition for Windows Server][download-docker-server]. + +```powershell +Install-WindowsFeature -Name Hyper-V -IncludeManagementTools +``` + +Restart your computer. + +Open PowerShell as an administrator and run the following commands to install Docker: + +```powershell +Install-Module DockerMsftProvider -Force +Install-Package Docker -ProviderName DockerMsftProvider -Force +Install-WindowsFeature Containers +``` +## Setup Service Fabric Mesh CLI + +You can use the Azure Cloud Shell or a local installation of the Azure CLI to complete this quickstart. Install Azure Service Fabric Mesh CLI extension module by following these [instructions](https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-howto-setup-cli). + +## Sign in to Azure +Sign in to Azure and set your subscription. + +```azurecli +az login +az account set --subscription "" +``` + +## Create a resource group + +An Azure resource group is a logical container into which Azure resources are deployed and managed. Use the following command to create a resource group named *myResourceGroup* in the *eastus* location. + +```azurecli +az group create --name myResourceGroup --location eastus +``` + +## Create a container registry and push an image to it + +### Create a container registry + +Create an Azure container registry (ACR) instance using the `az acr create` command. The registry name must be unique within Azure and contain 5-50 alphanumeric characters. In the following example, the name *myContainerRegistry007* is used. If you get an error that the registry name is in use, choose a different name. Use that name everywhere `` appears in these instructions. + +```azurecli +az acr create --resource-group myResourceGroup --name myContainerRegistry007 --sku Basic +``` + +When the registry is created, you'll see output similar to the following: + +```json +{ + "adminUserEnabled": false, + "creationDate": "2017-09-08T22:32:13.175925+00:00", + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/myContainerRegistry007", + "location": "eastus", + "loginServer": "myContainerRegistry007.azurecr.io", + "name": "myContainerRegistry007", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroup", + "sku": { + "name": "Basic", + "tier": "Basic" + }, + "status": null, + "storageAccount": null, + "tags": {}, + "type": "Microsoft.ContainerRegistry/registries" +} +``` + +### Push the image to Azure Container Registry + +To push an image to an Azure container registry (ACR), you must first have a container image. If you don't yet have any local container images, run the following command to pull the an image from Docker Hub (you may need to switch Docker to work with Linux images by right-clicking the docker icon and selecting **Switch to Linux containers**). + +If you are deploying Windows application, please use Windows image `seabreeze/azure-mesh-helloworld:1.1-windowsservercore-1709`. + +```bash +docker pull seabreeze/azure-mesh-helloworld:1.1-alpine +``` + +Before you can push an image to your registry, you must tag it with the fully qualified name of your ACR login server. + +Run the following command to get the full login server name of your ACR instance. + +```azurecli +az acr list --resource-group myResourceGroup --query "[].{acrLoginServer:loginServer}" --output table +``` + +The full login server name that is returned will be referred to as `` throughout the rest of this article. + +Now tag your docker image using the `docker tag` command. In the command below, replace `` with the login server name reported by the command above. The following example tags the seabreeze/azure-mesh-helloworld:1.1-alpine image. If you are using a different image, substitute the image name in the following command. + +```bash +docker tag seabreeze/azure-mesh-helloworld:1.1-alpine /seabreeze/azure-mesh-helloworld:1.1-alpine +``` + +For example: `docker tag seabreeze/azure-mesh-helloworld:1.1-alpine myContainerRegistry007.azurecr.io/seabreeze/azure-mesh-helloworld:1.1-alpine` + +Log in to the Azure Container Registry. + +```bash +az acr login -n +``` + +For example: `az acr login -n myContainerRegistry007` + +Push the image to the azure container registry with the following command: + +```bash +docker push /seabreeze/azure-mesh-helloworld:1.1-alpine +``` + +For example: `docker push myContainerRegistry007.azurecr.io/seabreeze/azure-mesh-helloworld:1.1-alpine` + +### List container images + +The following example lists the repositories in a registry. The examples that follow assume you are using the azure-mesh-helloworld:1.1-alpine image. If you are using a different image, substitute its name where the azure-mesh-helloworld image is used. + +```azurecli +az acr repository list --name --output table +``` +For example: `az acr repository list --name myContainerRegistry007 --output table` + +Output: + +```bash +Result +------------------------------- +seabreeze/azure-mesh-helloworld +``` + +The following example lists the tags on the **azure-mesh-helloworld** repository. + +```azurecli +az acr repository show-tags --name --repository seabreeze/azure-mesh-helloworld --output table +``` + +For example: `az acr repository show-tags --name myContainerRegistry007 --repository seabreeze/azure-mesh-helloworld --output table` + +Output: + +```bash +Result +-------- +1.1-alpine +``` + +The preceding output confirms the presence of `azure-mesh-helloworld:1.1-alpine` in the private container registry. . + +## Retrieve credentials for the registry + +> [!IMPORTANT] +> Enabling the admin user on an Azure container registry is not recommended for production scenarios. It is done here to keep this demonstration brief. For production scenarios, use a [service principal](https://docs.microsoft.com/azure/container-registry/container-registry-auth-service-principal) for both user and system authentication in production scenarios. + +In order to deploy a container instance from the registry that was created, you must provide credentials during the deployment. Enable the admin user on your registry with the following command: + +```azurecli-interactive +az acr update --name --admin-enabled true +``` + +For example: `az acr update --name myContainerRegistry007 --admin-enabled true` + +Get the registry server name, user name, and password by using the following commands: + +```azurecli-interactive +az acr list --resource-group myResourceGroup --query "[].{acrLoginServer:loginServer}" --output table +az acr credential show --name --query username +az acr credential show --name --query "passwords[0].value" +``` + +The values provided by preceding commands are referenced as ``, ``, and `` below. + +## Deploy the application + +Create the application and related resources in the resource group using the `az mesh deployment create` command, and provide the credentials from the previous step. The following will deploy the Hello World Linux application using the [helloworld.private_registry.linux.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.private_registry.linux.json). If you want to deploy a Windows application instead, use the [helloworld.private_registry.windows.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.private_registry.windows.json) instead. Windows container images are larger than Linux container images and may take more time to deploy. + +If you're using a Bash console or Windows Command Prompt, run the following: + +```azurecli-interactive +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.private_registry.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}, \"registry-server\": {\"value\": \"\"}, \"registry-username\": {\"value\": \"\"}, \"registry-password\": {\"value\": \"\"}}" +``` + +If you're using a PowerShell console, run the following: + +```azurecli-interactive +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/helloworld/helloworld.private_registry.windows.json --parameters "{'location': {'value': 'eastus'}, 'registry-server': {'value': ''}, 'registry-username': {'value': ''}, 'registry-password': {'value': ''}}" +``` + +In a few minutes, if the deployment is successful the command will return the list of created resources and an output property for the public IP address. + +## See the app in action + +### Open the application +Once the application successfully deploys, copy the public IP address from the `publicIPAddress` output property. Open the IP address in a web browser. A web page with the Azure Service Fabric Mesh logo displays. + +You can also obtain the public IP address from the details of the `helloWorldPrivateRegistryGateway` resource using the following command. The name of the gateway for Windows application is `helloWorldPrivateRegistryGatewayWindows`. + +```azurecli +az mesh gateway show -g myResourceGroup -n helloWorldPrivateRegistryGateway -o table +``` + +### Check the application details +You can check the application's status using the `az mesh app show` command. This command provides useful information that you can follow up on. + +The application name for the Linux app is `helloWorldPrivateRegistryApp` and for Windows app is `helloWorldPrivateRegistryAppWindows`, to gather the details on the application execute the following command: + +```azurecli +az mesh app show --resource-group myResourceGroup --name helloWorldPrivateRegistryApp +``` + +## Clean up + +When you are ready to delete the application, run the [az group delete][az-group-delete] command to remove the resource group and the application and network resources it contains. + +```azurecli +az group delete --name myResourceGroup +``` \ No newline at end of file diff --git a/templates/ingress/meshingress.linux.json b/templates/ingress/meshingress.linux.json new file mode 100644 index 00000000..8b4d74e0 --- /dev/null +++ b/templates/ingress/meshingress.linux.json @@ -0,0 +1,193 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the resources (e.g. westus, eastus, westeurope)." + } + }, + "stateFolderName": { + "type": "string", + "defaultValue": "CounterService", + "metadata": { + "description": "Folder in which to store the state. Provide a empty value to create a unique folder for each container to store the state. A non-empty value will retain the state across deployments, however if more than one applications are using the same folder, the counter may update more frequently." + } + } + }, + "resources": [ + { + "apiVersion": "2018-09-01-preview", + "name": "meshNetworkLinux", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Service Fabric Mesh Network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "ingressGatewayLinux", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "tags": {}, + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/meshNetworkLinux" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for Linux mesh samples.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkLinux')]" + }, + "http": [ + { + "name": "web", + "port": 80, + "hosts": [ + { + "name": "*", + "routes": [ + { + "match": { + "path": { + "value": "/hello", + "rewrite": "/", + "type": "Prefix" + } + }, + "destination": { + "applicationName": "meshAppLinux", + "serviceName": "helloWorldService", + "endpointName": "helloWorldListener" + } + }, + { + "match": { + "path": { + "value": "/counter", + "rewrite": "/", + "type": "Prefix" + } + }, + "destination": { + "applicationName": "meshAppLinux", + "serviceName": "counterService", + "endpointName": "counterServiceListener" + } + } + ] + } + ] + } + ] + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "meshAppLinux", + "type": "Microsoft.ServiceFabricMesh/applications", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/meshNetworkLinux" + ], + "properties": { + "description": "Service Fabric Mesh sample Application.", + "services": [ + { + "type": "Microsoft.ServiceFabricMesh/services", + "name": "helloWorldService", + "properties": { + "description": "Service Fabric Mesh Hello World Service.", + "osType": "linux", + "codePackages": [ + { + "name": "helloWorldCode", + "image": "seabreeze/azure-mesh-helloworld:1.1-alpine", + "endpoints": [ + { + "name": "helloWorldListener", + "port": "80" + } + ], + "resources": { + "requests": { + "cpu": "1", + "memoryInGB": "1" + } + } + } + ], + "replicaCount": "1", + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkLinux')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] + } + ] + } + }, + { + "name": "counterService", + "properties": { + "description": "A web service that serves the counter value stored in the Azure Files volume.", + "osType": "linux", + "codePackages": [ + { + "name": "counterService", + "image": "seabreeze/azure-mesh-counter:0.1-alpine", + "endpoints": [ + { + "name": "counterServiceListener", + "port": 80 + } + ], + "environmentVariables": [ + { + "name": "STATE_FOLDER_NAME", + "value": "[parameters('stateFolderName')]" + } + ], + "resources": { + "requests": { + "cpu": 0.5, + "memoryInGB": 0.5 + } + } + } + ], + "replicaCount": 1, + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkLinux')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] + } + ] + } + } + ] + } + } + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('ingressGatewayLinux').ipAddress]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/templates/ingress/meshingress.windows.json b/templates/ingress/meshingress.windows.json new file mode 100644 index 00000000..f5fb12a1 --- /dev/null +++ b/templates/ingress/meshingress.windows.json @@ -0,0 +1,193 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location of the resources (e.g. westus, eastus, westeurope)." + } + }, + "stateFolderName": { + "type": "string", + "defaultValue": "CounterService", + "metadata": { + "description": "Folder in which to store the state. Provide a empty value to create a unique folder for each container to store the state. A non-empty value will retain the state across deployments, however if more than one applications are using the same folder, the counter may update more frequently." + } + } + }, + "resources": [ + { + "apiVersion": "2018-09-01-preview", + "name": "meshNetworkWindows", + "type": "Microsoft.ServiceFabricMesh/networks", + "location": "[parameters('location')]", + "dependsOn": [], + "properties": { + "kind": "Local", + "description": "Service Fabric Mesh Network.", + "networkAddressPrefix": "10.0.0.0/24" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "ingressGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "tags": {}, + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/meshNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for Windows mesh samples.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkWindows')]" + }, + "http": [ + { + "name": "web", + "port": 80, + "hosts": [ + { + "name": "*", + "routes": [ + { + "match": { + "path": { + "value": "/hello", + "rewrite": "/", + "type": "Prefix" + } + }, + "destination": { + "applicationName": "meshAppWindows", + "serviceName": "helloWorldService", + "endpointName": "helloWorldListener" + } + }, + { + "match": { + "path": { + "value": "/counter", + "rewrite": "/", + "type": "Prefix" + } + }, + "destination": { + "applicationName": "meshAppWindows", + "serviceName": "counterService", + "endpointName": "counterServiceListener" + } + } + ] + } + ] + } + ] + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "meshAppWindows", + "type": "Microsoft.ServiceFabricMesh/applications", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/meshNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh sample Application.", + "services": [ + { + "type": "Microsoft.ServiceFabricMesh/services", + "name": "helloWorldService", + "properties": { + "description": "Service Fabric Mesh Hello World Service.", + "osType": "windows", + "codePackages": [ + { + "name": "helloWorldCode", + "image": "seabreeze/azure-mesh-helloworld:1.1-windowsservercore-1709", + "endpoints": [ + { + "name": "helloWorldListener", + "port": "80" + } + ], + "resources": { + "requests": { + "cpu": "1", + "memoryInGB": "1" + } + } + } + ], + "replicaCount": "1", + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkWindows')]", + "endpointRefs": [ + { + "name": "helloWorldListener" + } + ] + } + ] + } + }, + { + "name": "counterService", + "properties": { + "description": "A web service that serves the counter value stored in the Azure Files volume.", + "osType": "windows", + "codePackages": [ + { + "name": "counterService", + "image": "seabreeze/azure-mesh-counter:0.1-nanoserver-1709", + "endpoints": [ + { + "name": "counterServiceListener", + "port": 80 + } + ], + "environmentVariables": [ + { + "name": "STATE_FOLDER_NAME", + "value": "[parameters('stateFolderName')]" + } + ], + "resources": { + "requests": { + "cpu": 0.5, + "memoryInGB": 0.5 + } + } + } + ], + "replicaCount": 1, + "networkRefs": [ + { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'meshNetworkWindows')]", + "endpointRefs": [ + { + "name": "counterServiceListener" + } + ] + } + ] + } + } + ] + } + } + ], + "outputs": { + "publicIPAddress": { + "value": "[reference('ingressGatewayWindows').ipAddress]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/templates/todolist/mesh_rp.windows.json b/templates/todolist/mesh_rp.windows.json index 9b87f2b1..f9c40c7d 100644 --- a/templates/todolist/mesh_rp.windows.json +++ b/templates/todolist/mesh_rp.windows.json @@ -3,7 +3,7 @@ "contentVersion": "1.0.0.0", "parameters": { "location": { - "defaultValue": "eastus", + "defaultValue": "[resourceGroup().location]", "type": "String", "metadata": { "description": "Location of the resources." @@ -89,7 +89,7 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "todolistapp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -195,7 +195,7 @@ } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "todolistappNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", diff --git a/templates/todolist/mesh_rp.windows.parameters.json b/templates/todolist/mesh_rp.windows.parameters.json index afd6343f..f2f6c485 100644 --- a/templates/todolist/mesh_rp.windows.parameters.json +++ b/templates/todolist/mesh_rp.windows.parameters.json @@ -2,41 +2,38 @@ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json", "contentVersion": "1.0.0.0", "parameters": { - "location": { - "value": "eastus" - }, "registryPassword": { - "value": "MyPassword" - }, - "registryUserName": { - "value": "" - }, - "registryServer": { - "value": ".azurecr.io" - }, - "frontEndImage": { - "value": ".azurecr.io/seabreeze/azure-mesh-todo-webfrontend:1.0-nanoserver-1709" - }, - "serviceImage": { - "value": ".azurecr.io/seabreeze/azure-mesh-todo-service:1.0-nanoserver-1709" - }, - "frontEndCpu": { - "value": "0.5" - }, - "serviceCpu": { - "value": "0.5" - }, - "frontEndMemory":{ - "value": "1.0" - }, - "serviceMemory":{ - "value": "1.0" - }, - "frontEndReplicaCount":{ - "value": "1" - }, - "serviceReplicaCount":{ - "value": "1" - } + "value": "MyPassword" + }, + "registryUserName": { + "value": "" + }, + "registryServer": { + "value": ".azurecr.io" + }, + "frontEndImage": { + "value": ".azurecr.io/seabreeze/azure-mesh-todo-webfrontend:1.0-nanoserver-1709" + }, + "serviceImage": { + "value": ".azurecr.io/seabreeze/azure-mesh-todo-service:1.0-nanoserver-1709" + }, + "frontEndCpu": { + "value": "0.5" + }, + "serviceCpu": { + "value": "0.5" + }, + "frontEndMemory":{ + "value": "1.0" + }, + "serviceMemory":{ + "value": "1.0" + }, + "frontEndReplicaCount":{ + "value": "1" + }, + "serviceReplicaCount":{ + "value": "1" + } } } diff --git a/templates/visualobjects/README.md b/templates/visualobjects/README.md index 7a7c91a5..6b7fb4c6 100644 --- a/templates/visualobjects/README.md +++ b/templates/visualobjects/README.md @@ -1,19 +1,79 @@ -# Service Fabric Mesh Sample Application - Visual Objects +# Deploy the Visual Objects application to Azure Service Fabric Mesh -This folder contains the deployment templates for the Visual Object sample. This sample shows you how to deploy, upgrade and scale out a mesh application. +This dir contains the deployment templates for the Visual Object sample based on pre-built images that we create. The Visual Objects sample app shows you how to deploy, scale, and upgrade a Mesh application. This app consists of two services, a `worker` service which is responsible for tracking an object that moves, and a `web` service that renders the object(s) in a UI. -You will need the Azure CLI and the Mesh extension to deploy this sample. Follow this link for instructions on how to install the [Azure CLI and Mesh extension](https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-howto-setup-cli) +The deployments steps will guide you through deploying the `base` version of the app, which will deploy a single "visual object" you will see rendered in the web UI. We will then scale up the worker service to 3 instances, thus creating 3 objects that will show up individually in the web UI. The last step will be to upgrade the application (which changes the way each worker is represented in the UI). + +## Set up Service Fabric Mesh CLI + +You can use the Azure Cloud Shell or a local installation of the Azure CLI to complete this quickstart. Install Azure Service Fabric Mesh CLI extension module by following these [instructions](https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-howto-setup-cli). + +## Sign in to Azure +Sign in to Azure and set your subscription. + +```azurecli +az login +az account set --subscription "" +``` + +## Create a resource group + +An Azure resource group is a logical container into which Azure resources are deployed and managed. Use the following command to create a resource group named *myResourceGroup* in the *eastus* location. + +```azurecli +az group create --name myResourceGroup --location eastus +``` + +## Deploy the application + +### Deploy the base version + +Create the application and related resources in the resource group using the `az mesh deployment create` command. The following will deploy the Visual Objects Linux application using the [mesh_rp.base.linux.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.base.linux.json). If you want to deploy a Windows application instead, use the [mesh_rp.base.windows.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.base.windows.json) instead. Windows container images are larger than Linux container images and may take more time to deploy. Deploy Visual Object using the following steps: -1. Login to you Azure account - ``az login`` +```azurecli +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.base.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}" +``` + +### See it in action + +Once the application successfully deploys, copy the public IP address from the `publicIPAddress` output property. Open up `:8080` in your web browser to see the UI displayed. You should see one triangle flying around, representing the single instance worker service that was deployed. Leave this page open as you progress through the scale out and upgrade steps to see the changes! + +### Scale the application + +The next step here is to scale the worker service up to 3 instances. 3 is the current limit imposed for service instances while Mesh is still in private previw - see [Mesh FAQ](https://docs.microsoft.com/azure/service-fabric-mesh/service-fabric-mesh-faq) for updated information on resource limits for Mesh. + +For this step, we will be using the [mesh_rp.scaleout.linux.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.scaleout.linux.json). If you chose to deploy the sample as a Windows application in the prior step, use the [mesh_rp.scaleout.windows.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.scaleout.windows.json) in the following command instead. + +```azurecli +az mesh deployment create --resource-group myResourceGroup --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.scaleout.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}" +``` + +There is only one difference between the template used in this step and the one prior (scalout vs. base) - where the `worker` service is being described in the JSON, you will see `"replicaCount"` set to 3 instead of 1. + +### See it in action + +In a few minutes, your web service should update and be rendering 3 triangles instead! Cool! + +### Upgrade the application + +We're now going to upgrade the same `worker` service to use a "new" image. Previously, the worker service replicas were using the seabreeze/azure-mesh-visualobjects-worker:1.1-stretch image, but with this deployment, we will be using a template that uses the seabreeze/azure-mesh-visualobjects-worker:1.1-rotate-stretch image. The template being used to upgrade the app is the [mesh_rp.upgrade.linux.json template](https://github.com/Azure-Samples/service-fabric-mesh/blob/2018-09-01-preview/templates/visualobjects/mesh_rp.upgrade.linux.json). If you chose to deploy the sample as a Windows application in the prior steps, use the [mesh_rp.upgrade.windows.json template](https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/2018-09-01-preview/templates/visualobjects/mesh_rp.upgrade.windows.json) in the following command instead. + +```azurecli +az mesh deployment create --resource-group --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/master/templates/visualobjects/mesh_rp.upgrade.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}" +``` + +The difference between this template and the one previously deployed is in the container image being use in the code package for the `worker` service. Another thing to point out here is that though both the scale out and the upgrade were just deployments of an updated template on the same application resource, they did result in two different types of changes - the former is more of a config change since the code packages being deployed are not change and only the request number of replicas changed, whereas the latter results in a full rolling upgrade for the applciation, where the container images are updated for a specific code package in a service. -1. Create a resource group - ``az group create --name --location eastus`` +### See it in action -1. Deploy the base template - ``az mesh deployment create --resource-group --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/master/templates/visualobjects/mesh_rp.base.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}"`` Once the deployment is complete, you can navigate to the application IP to see flying triangles. +Back in the web UI, as the replicas start to get upgraded, their respective triangles will start rotating. -1. Scale out the application - ``az mesh deployment create --resource-group --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/master/templates/visualobjects/mesh_rp.scaleout.linux.json --parameters "{\"location\": {\"value\": \"eastus"}}"`` Once the deployment is completed, you will now see three flying triangles, each representing an replica of the backend service. +## Clean up -1. Upgrade the application - ``az mesh deployment create --resource-group --template-uri https://raw.githubusercontent.com/Azure-Samples/service-fabric-mesh/master/templates/visualobjects/mesh_rp.upgrade.linux.json --parameters "{\"location\": {\"value\": \"eastus\"}}"`` As the replicas get upgrade, the triangles will start rotating. +When you are ready to delete the application, run the [az group delete][az-group-delete] command to remove the resource group and the application and network resources it contains. -[The source code for the sample can be found here](../../src/visualobjects) \ No newline at end of file +```azurecli +az group delete --name myResourceGroup +``` \ No newline at end of file diff --git a/templates/visualobjects/mesh_rp.base.linux.json b/templates/visualobjects/mesh_rp.base.linux.json index 1c7d67b3..23117220 100644 --- a/templates/visualobjects/mesh_rp.base.linux.json +++ b/templates/visualobjects/mesh_rp.base.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "10.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsApp", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] -} \ No newline at end of file + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGateway').ipAddress]", + "type": "string" + } + } + +} diff --git a/templates/visualobjects/mesh_rp.base.windows.json b/templates/visualobjects/mesh_rp.base.windows.json index 895a4f98..11ba6ac5 100644 --- a/templates/visualobjects/mesh_rp.base.windows.json +++ b/templates/visualobjects/mesh_rp.base.windows.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetworkWindows", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "2.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for visualObject sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsAppWindows", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsAppWindows", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGatewayWindows').ipAddress]", + "type": "string" + } + } + } \ No newline at end of file diff --git a/templates/visualobjects/mesh_rp.scaleout.linux.json b/templates/visualobjects/mesh_rp.scaleout.linux.json index 5a4e5ad5..e95baea6 100644 --- a/templates/visualobjects/mesh_rp.scaleout.linux.json +++ b/templates/visualobjects/mesh_rp.scaleout.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "10.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsApp", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] -} \ No newline at end of file + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGateway').ipAddress]", + "type": "string" + } + } + +} diff --git a/templates/visualobjects/mesh_rp.scaleout.windows.json b/templates/visualobjects/mesh_rp.scaleout.windows.json index 5f5e8dc6..71b1fa50 100644 --- a/templates/visualobjects/mesh_rp.scaleout.windows.json +++ b/templates/visualobjects/mesh_rp.scaleout.windows.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetworkWindows", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "2.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for visualObject sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsAppWindows", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsAppWindows", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGatewayWindows').ipAddress]", + "type": "string" + } + } + } \ No newline at end of file diff --git a/templates/visualobjects/mesh_rp.upgrade.linux.json b/templates/visualobjects/mesh_rp.upgrade.linux.json index 54f78de7..53c280be 100644 --- a/templates/visualobjects/mesh_rp.upgrade.linux.json +++ b/templates/visualobjects/mesh_rp.upgrade.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "10.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGateway", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetwork" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for HelloWorld sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsApp", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetwork')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] -} \ No newline at end of file + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGateway').ipAddress]", + "type": "string" + } + } + +} diff --git a/templates/visualobjects/mesh_rp.upgrade.windows.json b/templates/visualobjects/mesh_rp.upgrade.windows.json index 9215d787..6b196eeb 100644 --- a/templates/visualobjects/mesh_rp.upgrade.windows.json +++ b/templates/visualobjects/mesh_rp.upgrade.windows.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,28 +12,48 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsNetworkWindows", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", "dependsOn": [], "properties": { - "addressPrefix": "10.0.0.4/22", - "ingressConfig": { - "layer4": [ - { - "name": "webIngress", - "publicPort": "80", + "kind": "Local", + "description": "Service Fabric Mesh Visual Objects network!", + "networkAddressPrefix": "2.0.0.0/22" + } + }, + { + "apiVersion": "2018-09-01-preview", + "name": "visualObjectsGatewayWindows", + "type": "Microsoft.ServiceFabricMesh/gateways", + "location": "[parameters('location')]", + "dependsOn": [ + "Microsoft.ServiceFabricMesh/networks/visualObjectsNetworkWindows" + ], + "properties": { + "description": "Service Fabric Mesh Gateway for visualObject sample.", + "sourceNetwork": { + "name": "Open" + }, + "destinationNetwork": { + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + }, + "tcp": [ + { + "name": "web", + "port": 80, + "destination": { "applicationName": "visualObjectsAppWindows", "serviceName": "web", "endpointName": "webListener" } - ] - } + } + ] } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "visualObjectsAppWindows", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", @@ -70,7 +91,12 @@ "replicaCount": "1", "networkRefs": [ { - "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]" + "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'visualObjectsNetworkWindows')]", + "endpointRefs": [ + { + "name": "webListener" + } + ] } ] } @@ -105,5 +131,12 @@ ] } } - ] + ], + "outputs": { + "publicIPAddress": { + "value" : "[reference('visualObjectsGatewayWindows').ipAddress]", + "type": "string" + } + } + } \ No newline at end of file diff --git a/templates/voting/mesh_rp.linux.json b/templates/voting/mesh_rp.linux.json index b2534fb9..75afcfe1 100644 --- a/templates/voting/mesh_rp.linux.json +++ b/templates/voting/mesh_rp.linux.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,7 +12,7 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "votingapp-network-linux", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", @@ -33,7 +34,7 @@ } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "votingapp-linux", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]", diff --git a/templates/voting/mesh_rp.windows.json b/templates/voting/mesh_rp.windows.json index 8b929d41..309868b8 100644 --- a/templates/voting/mesh_rp.windows.json +++ b/templates/voting/mesh_rp.windows.json @@ -4,6 +4,7 @@ "parameters": { "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { "description": "Location of the resources (e.g. westus, eastus, westeurope)." } @@ -11,7 +12,7 @@ }, "resources": [ { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "VotingAppNetwork", "type": "Microsoft.ServiceFabricMesh/networks", "location": "[parameters('location')]", @@ -33,7 +34,7 @@ } }, { - "apiVersion": "2018-07-01-preview", + "apiVersion": "2018-09-01-preview", "name": "VotingApp", "type": "Microsoft.ServiceFabricMesh/applications", "location": "[parameters('location')]",