Tutorial: Package and deploy containers as a Service Fabric application using Yeoman

This tutorial is part two in a series. In this tutorial, a template generator tool (Yeoman) is used to generate a Service Fabric application definition. This application can then be used to deploy containers to Service Fabric. In this tutorial you learn how to:

  • Install Yeoman
  • Create an application package using Yeoman
  • Configure settings in the application package for use with containers
  • Build the application
  • Deploy and run the application
  • Clean up the application

Prerequisites

  • The container images pushed to Azure Container Registry created in Part 1 of this tutorial series are used.
  • Linux development environment is set up.

Install Yeoman

Service fabric provides scaffolding tools to help create applications from terminal using Yeoman template generator. Follow the steps below to ensure you have the Yeoman template generator.

  1. Install nodejs and NPM on your machine. Note that, Mac OSX users will have to use the package manager Homebrew

    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
    nvm install node 
    
  2. Install Yeoman template generator on your machine from NPM

    npm install -g yo
    
  3. Install the Service Fabric Yeoman container generator

    npm install -g generator-azuresfcontainer
    

Package a Docker image container with Yeoman

  1. To create a Service Fabric container application, in the 'container-tutorial' directory of the cloned repository, run the following command.

    yo azuresfcontainer
    
  2. Please type in "TestContainer" to name your application

  3. Please type in "azurevotefront" to name your application service.

  4. Provide the container image path in ACR for the frontend repo - for example '<acrName>.azurecr.io/azure-vote-front:v1'. The <acrName> field must be the same as the value that was used in the previous tutorial.

  5. Press Enter to leave the Commands section empty.

  6. Specify an instance count of 1.

The following shows the input and output of running the yo command:

? Name your application TestContainer
? Name of the application service: azurevotefront
? Input the Image Name: <acrName>.azurecr.io/azure-vote-front:v1
? Commands:
? Number of instances of guest container application: 1
   create TestContainer/TestContainer/ApplicationManifest.xml
   create TestContainer/TestContainer/azurevotefrontPkg/ServiceManifest.xml
   create TestContainer/TestContainer/azurevotefrontPkg/config/Settings.xml
   create TestContainer/TestContainer/azurevotefrontPkg/code/Dummy.txt
   create TestContainer/install.sh
   create TestContainer/uninstall.sh

To add another container service to an application already created using Yeoman, perform the following steps:

  1. Change directory one level to the TestContainer directory, for example, ./TestContainer
  2. Run yo azuresfcontainer:AddService
  3. Name the service 'azurevoteback'
  4. Provide the container image path for Redis - 'redis:alpine'
  5. Press Enter to leave the Commands section empty
  6. Specify an instance count of "1".

The entries for adding the service used are all shown:

? Name of the application service: azurevoteback
? Input the Image Name: redis:alpine
? Commands:
? Number of instances of guest container application: 1
   create TestContainer/azurevotebackPkg/ServiceManifest.xml
   create TestContainer/azurevotebackPkg/config/Settings.xml
   create TestContainer/azurevotebackPkg/code/Dummy.txt

For the remainder of this tutorial, we are working in the TestContainer directory. For example, ./TestContainer/TestContainer. The contents of this directory should be as follows.

$ ls
ApplicationManifest.xml azurevotefrontPkg azurevotebackPkg

Configure the application manifest with credentials for Azure Container Registry

For Service Fabric to pull the container images from Azure Container Registry, we need to provide the credentials in the ApplicationManifest.xml.

Sign in to your ACR instance. Use the az acr login command to complete the operation. Provide the unique name given to the container registry when it was created.

az acr login --name <acrName>

The command returns a Login Succeeded message once completed.

Next, run the following command to get the password of your container registry. This password is used by Service Fabric to authenticate with ACR to pull the container images.

az acr credential show -n <acrName> --query passwords[0].value

In the ApplicationManifest.xml, add the code snippet under the ServiceManifestImport element for the front end service. Insert your acrName for the AccountName field and the password returned from the previous command is used for the Password field. A full ApplicationManifest.xml is provided at the end of this document.

<Policies>
  <ContainerHostPolicies CodePackageRef="Code">
    <RepositoryCredentials AccountName="<acrName>" Password="<password>" PasswordEncrypted="false"/>
  </ContainerHostPolicies>
</Policies>

Configure communication and container port-to-host port mapping

Configure communication port

Configure an HTTP endpoint so clients can communicate with your service. Open the ./TestContainer/azurevotefrontPkg/ServiceManifest.xml file and declare an endpoint resource in the ServiceManifest element. Add the protocol, port, and name. For this tutorial, the service listens on port 80. The following snippet is placed under the ServiceManifest tag in the resource.

<Resources>
  <Endpoints>
    <!-- This endpoint is used by the communication listener to obtain the port on which to 
            listen. Please note that if your service is partitioned, this port is shared with 
            replicas of different partitions that are placed in your code. -->
    <Endpoint Name="azurevotefrontTypeEndpoint" UriScheme="http" Port="80" Protocol="http"/>
  </Endpoints>
</Resources>

Similarly, modify the Service Manifest for the backend service. Open the ./TestContainer/azurevotebackPkg/ServiceManifest.xml and declare an endpoint resource in the ServiceManifest element. For this tutorial, the redis default of 6379 is maintained. The following snippet is placed under the ServiceManifest tag in the resource.

<Resources>
  <Endpoints>
    <!-- This endpoint is used by the communication listener to obtain the port on which to 
            listen. Please note that if your service is partitioned, this port is shared with 
            replicas of different partitions that are placed in your code. -->
    <Endpoint Name="azurevotebackTypeEndpoint" Port="6379" Protocol="tcp"/>
  </Endpoints>
</Resources>

Providing the UriSchemeautomatically registers the container endpoint with the Service Fabric Naming service for discoverability. A full ServiceManifest.xml example file for the backend service is provided at the end of this article as an example.

Map container ports to a service

In order to expose the containers in the cluster, we also need to create a port binding in the 'ApplicationManifest.xml'. The PortBinding policy references the Endpoints we defined in the ServiceManifest.xml files. Incoming requests to these endpoints get mapped to the container ports that are opened and bounded here. In the ApplicationManifest.xml file, add the following code to bind port 80 and 6379 to the endpoints. A full ApplicationManifest.xml is available at the end of this document.

<ContainerHostPolicies CodePackageRef="Code">
    <PortBinding ContainerPort="80" EndpointRef="azurevotefrontTypeEndpoint"/>
</ContainerHostPolicies>
<ContainerHostPolicies CodePackageRef="Code">
    <PortBinding ContainerPort="6379" EndpointRef="azurevotebackTypeEndpoint"/>
</ContainerHostPolicies>

Add a DNS name to the backend service

For Service Fabric to assign this DNS name to the backend service, the name needs to be specified in the ApplicationManifest.xml. Add the ServiceDnsName attribute to the Service element as shown:

<Service Name="azurevoteback" ServiceDnsName="redisbackend.testapp">
  <StatelessService ServiceTypeName="azurevotebackType" InstanceCount="1">
    <SingletonPartition/>
  </StatelessService>
</Service>

The frontend service reads an environment variable to know the DNS name of the Redis instance. This environment variable is already defined in the Dockerfile that was used to generate the Docker image and no action needs to be taken here.

ENV REDIS redisbackend.testapp

The following code snippet illustrates how the front-end Python code picks up the environment variable described in the Dockerfile. No action needs to be taken here.

# Get DNS Name
redis_server = os.environ['REDIS']

# Connect to the Redis store
r = redis.StrictRedis(host=redis_server, port=6379, db=0)

At this point in the tutorial, the template for a Service Package application is available for deployment to a cluster. In the subsequent tutorial, this application is deployed and ran in a Service Fabric cluster.

Create a Service Fabric cluster

To deploy the application to Azure, you need a Service Fabric cluster to run the application. The following commands create a five-node cluster in Azure. The commands also create a self-signed certificate, adds it to a key vault, and downloads the certificate locally as a PEM file. The new certificate is used to secure the cluster when it deploys and is used to authenticate clients.

#!/bin/bash

# Variables
ResourceGroupName="containertestcluster" 
ClusterName="containertestcluster" 
Location="eastus" 
Password="q6D7nN%6ck@6" 
Subject="containertestcluster.eastus.cloudapp.azure.com" 
VaultName="containertestvault" 
VmPassword="Mypa$$word!321"
VmUserName="sfadminuser"

# Login to Azure and set the subscription
az login

az account set --subscription <mySubscriptionID>

# Create resource group
az group create --name $ResourceGroupName --location $Location 

# Create secure five node Linux cluster. Creates a key vault in a resource group
# and creates a certficate in the key vault. The certificate's subject name must match 
# the domain that you use to access the Service Fabric cluster.  
# The certificate is downloaded locally as a PEM file.
az sf cluster create --resource-group $ResourceGroupName --location $Location \ 
--certificate-output-folder . --certificate-password $Password --certificate-subject-name $Subject \ 
--cluster-name $ClusterName --cluster-size 5 --os UbuntuServer1604 --vault-name $VaultName \ 
--vault-resource-group $ResourceGroupName --vm-password $VmPassword --vm-user-name $VmUserName

Note

The web front end service is configured to listen on port 80 for incoming traffic. By default, port 80 is open on your cluster VMs and the Azure load balancer.

For more information about creating your own cluster, see Create a Service Fabric cluster on Azure.

Build and deploy the application to the cluster

You can deploy the application the Azure cluster using the Service Fabric CLI. If Service Fabric CLI is not installed on your machine, follow instructions here to install it.

Connect to the Service Fabric cluster in Azure. Replace the sample endpoint with your own. The endpoint must be a full URL similar to the one below. The PEM file is the self-signed certificate that was previously created.

sfctl cluster select --endpoint https://containertestcluster.eastus.cloudapp.azure.com:19080 --pem containertestcluster22019013100.pem --no-verify

Use the install script provided in the TestContainer directory to copy the application package to the cluster's image store, register the application type, and create an instance of the application.

./install.sh

Open a browser and navigate to Service Fabric Explorer at https://containertestcluster.eastus.cloudapp.azure.com:19080/Explorer. Expand the Applications node and note that there is an entry for your application type and another for the instance.

Service Fabric Explorer

In order to connect to the running application, open a web browser and go to the cluster url - for example http://containertestcluster.eastus.cloudapp.azure.com:80. You should see the Voting application in the web UI.

Screenshot shows the Azure Voting App with buttons for Cats, Dogs, and Reset, and totals.

Clean up

Use the uninstall script provided in the template to delete the application instance from the cluster and unregister the application type. This command takes some time to clean up the instance and the 'install.sh' command cannot be run immediately after this script.

./uninstall.sh

Examples of completed manifests

ApplicationManifest.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ApplicationManifest ApplicationTypeName="TestContainerType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="azurevotefrontPkg" ServiceManifestVersion="1.0.0"/>
    <Policies>
    <ContainerHostPolicies CodePackageRef="Code">
        <RepositoryCredentials AccountName="myaccountname" Password="<password>" PasswordEncrypted="false"/>
        <PortBinding ContainerPort="80" EndpointRef="azurevotefrontTypeEndpoint"/>
    </ContainerHostPolicies>
        </Policies>
  </ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="azurevotebackPkg" ServiceManifestVersion="1.0.0"/>
      <Policies>
        <ContainerHostPolicies CodePackageRef="Code">
          <PortBinding ContainerPort="6379" EndpointRef="azurevotebackTypeEndpoint"/>
        </ContainerHostPolicies>
      </Policies>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="azurevotefront">
      <StatelessService ServiceTypeName="azurevotefrontType" InstanceCount="1">
        <SingletonPartition/>
      </StatelessService>
    </Service>
    <Service Name="azurevoteback" ServiceDnsName="redisbackend.testapp">
      <StatelessService ServiceTypeName="azurevotebackType" InstanceCount="1">
        <SingletonPartition/>
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

Front-end ServiceManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="azurevotefrontPkg" Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" >

   <ServiceTypes>
      <StatelessServiceType ServiceTypeName="azurevotefrontType" UseImplicitHost="true">
   </StatelessServiceType>
   </ServiceTypes>

   <CodePackage Name="code" Version="1.0.0">
      <EntryPoint>
         <ContainerHost>
            <ImageName>acrName.azurecr.io/azure-vote-front:v1</ImageName>
            <Commands></Commands>
         </ContainerHost>
      </EntryPoint>
      <EnvironmentVariables>
      </EnvironmentVariables>
   </CodePackage>

  <Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Name="azurevotefrontTypeEndpoint" UriScheme="http" Port="80" Protocol="http"/>
    </Endpoints>
  </Resources>

 </ServiceManifest>

Redis ServiceManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="azurevotebackPkg" Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" >

   <ServiceTypes>
      <StatelessServiceType ServiceTypeName="azurevotebackType" UseImplicitHost="true">
   </StatelessServiceType>
   </ServiceTypes>

   <CodePackage Name="code" Version="1.0.0">
      <EntryPoint>
         <ContainerHost>
            <Commands></Commands>
         </ContainerHost>
      </EntryPoint>
      <EnvironmentVariables>
      </EnvironmentVariables>
   </CodePackage>
     <Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Name="azurevotebackTypeEndpoint" Port="6379" Protocol="tcp"/>
    </Endpoints>
  </Resources>
 </ServiceManifest>

Next steps

In this tutorial, multiple containers were packaged into a Service Fabric application using Yeoman. This application was then deployed and run on a Service Fabric cluster. The following steps were completed:

  • Install Yeoman
  • Create an application package using Yeoman
  • Configure settings in the application package for use with containers
  • Build the application
  • Deploy and run the application
  • Clean up the application

Advance to the next tutorial to learn about failover and scaling of the application in Service Fabric.