Build and Deploy A Multi-Container Application in Azure Container Service
Build and Deploy A Multi-Container Application in Azure Container Service
By Paolo Salvatori
March 2018
Build and deploy a multi-container application
Contents
Overview .........................................................................................................................................................4
In this guide ...................................................................................................................................................4
Prerequisites for the development computer...................................................................................5
Contents of this project.............................................................................................................................5
Architecture ...................................................................................................................................................7
Configuration .....................................................................................................................................................8
TodoApi service configuration ...............................................................................................................8
TodoWeb service configuration .......................................................................................................... 10
How configuration works in ASP.NET Core .................................................................................... 11
Define the Docker images and containers ........................................................................................... 11
Push the Docker images to Docker Hub or Azure Container Registry...................................... 13
Push Docker images to Docker Hub ................................................................................................. 13
Create and push to an Azure Container Registry ......................................................................... 14
Deploy the application on a Kubernetes cluster................................................................................ 15
Create an ACS–Kubernetes cluster .................................................................................................... 15
Create an AKS cluster .............................................................................................................................. 17
Use a ConfigMap in Kubernetes to define non-sensitive configuration data ................... 17
Use a secret in Kubernetes to define sensitive configuration data ....................................... 18
Deploy to ACS–Kubernetes from a local machine ....................................................................... 20
Configure Kubernetes with the services and deployments ...................................................... 23
Load balancer service type for the Kubernetes cluster .............................................................. 24
Assign a custom DNS to the public IP of the front-end service .................................................. 27
Deploy the application to AKS from Cloud Shell............................................................................... 30
Create a ConfigMap using a YAML file ............................................................................................ 31
Create a secret using a YAML file ....................................................................................................... 32
Deploy the application ........................................................................................................................... 33
Configure NGINX ingress controller for TLS termination on Kubernetes ................................ 38
Install the multi-container application to Kubernetes ................................................................ 40
Package and deploy the application using Helm ......................................................................... 44
Azure services used by this project......................................................................................................... 51
Learn more ....................................................................................................................................................... 52
2
Build and deploy a multi-container application
List of figures
Figure 1. Simplified architecture for deploying the multi-container application to ACS or AKS. ........ 7
Figure 4. Configuration of the front-end load balancers for the Kubernetes cluster. ........................... 25
Figure 7. Command line information about the nodes in the cluster. ......................................................... 27
Figure 8. Using GoDaddy to replace the NS records with the Azure DNS names servers................... 29
Figure 10. External IP address exposed by the NGINX ingress controller. ................................................. 44
© 2018 Microsoft Corporation. This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR
IMPLIED, IN THIS SUMMARY. The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.
3
Build and deploy a multi-container application
Overview
To run a multi-container application on Azure, you have several deployment choices. This guide and the
accompanying GitHub scripts demonstrate how to create a multi-container application using ASP.NET Core
and Docker, then deploy it on an Azure Container Service Kubernetes cluster on Linux.
The multi-container application adopts a microservices architecture that consists of a collection of small,
autonomous services. Each service is self-contained and implements a single business capability.
Microservices have the following characteristics:
• Each microservice is a defined by a separate code base, configuration data, and data package that can
be managed by a small development team.
• Each microservice can be built using different programming languages, technology stacks, libraries,
and frameworks.
• Microservices communicate with each other using well-defined application programming interfaces
(APIs). Internal implementation details of each service are hidden from other services.
• Microservices can be versioned and deployed independently. A team can update an existing service
without rebuilding and redeploying an entire application.
• Services are responsible for persisting their own data or external state. This approach differs from the
traditional model, where a separate data layer handles data persistence.
For detailed guidance about building a microservices architecture on Azure, see Designing, building, and
operating microservices on Azure.
In this guide
This guide is intended for software developers with some familiarity with multi-container applications and
includes a sample multi-container application created with ASP.NET Core. This guide shows you how to
deploy it on Azure in two ways:
• To Azure Container Service–Kubernetes (ACS–Kubernetes) cluster.
In each of these scenarios, the cluster can pull images from either Docker Hub or Azure Container Registry.
These alternatives are described and sample configuration files are provided on GitHub.
• Use environment variables in a Dockerfile, Docker Compose file, or Kubernetes templates to specify
application settings.
4
Build and deploy a multi-container application
• Clone or download this container solution into a directory on your local machine.
• Replace the values of the scripts’ placeholder parameters as described throughout this guide.
The Visual Studio solution available in the GitHub repository is composed of the following contents that
can be used to build and deploy the sample multi-container application to a an ACS–Kubernetes or AKS
cluster:
• TodoWeb: This project is an ASP.NET Core Web application that represents the front end of the
solution. The user interface is composed of a set of Razor pages that can be used to browse, create,
delete, update, and see the details about a collection of to-do items stored in an Azure Cosmos DB
collection. The front-end service is configured to send logs, events, traces, requests, dependencies, and
exceptions to Application Insights.
• TodoApi: This project contains an ASP.NET Core Web API service that is invoked by the TodoWeb
front-end service to access the data stored in an Azure Cosmos DB SQL API database. Each time a
CRUD operation is performed by any of the methods exposed by the TodoController, the back-end
service sends a notification message to a Service Bus queue. You can use Service Bus Explorer to read
messages from the queue. The back-end service is configured to send logs, events, traces, requests,
dependencies and exceptions to Application Insights. The back-end service adopts Swagger to expose
a machine-readable representation of its RESTful API.
\Scripts
• ACS-Kubernetes-Cluster folder contains the create-kubernetes-acs-cluster.cmd command file used to
create an Azure Container Service Kubernetes cluster.
5
Build and deploy a multi-container application
Fabric, and other Azure services. For more information, see Introduction to private Docker container
registries in Azure.
• Azure-DNS folder contains the create-azure-dns-for-kubernetes-todoapi-service.cmd command file
used to create an Azure DNS service. It is used to resolve a website or service name to its IP address.
For more information, see Azure DNS overview.
• Push-Docker-Images-Scripts contains the push-images-to-azure-container-registry.cmd command
file used to push Docker images to an Azure Container Registry. It also contains the push-images-to-
docker-hub.cmd script is used to push Docker images to a Docker Hub repository. For more
information, see Deploy and use Azure Container Registry.
\Scripts\Kubernetes
These scripts are used to compose and deploy the multi-container application.
• create-todolist-secret.cmd: This command file can be used to create the todolist-secret object in the
Kubernetes cluster using the todolist-secret.yml that contains sensitive configuration data used by the
multi-container application.
• install-helm.sh: This bash script is used to install and initialize Helm, a tool for managing Kubernetes
charts.
• install-nginx-ingress-controller.sh: This bash script is used to install the NGINX ingress controller in
your Kubernetes cluster.
• scale-nginx-ingress-controller-replicas.sh: This bash script is used to scale out the number of replicas
used by the NGINX ingress controller.
• create-certificate.sh: This bash script is used to create a test certificate for Kubernetes.
• create-tls-secret.sh: This bash script is used to create a secret in your Kubernetes cluster using the self-
signed certificate.
6
Build and deploy a multi-container application
NOTE: Both the front-end (TodoWeb) and back-end (TodoApi) containerized services use the
microsoft/aspnetcore:2.0 image as the base Docker image. For more information, see Official .NET
Docker images.
Architecture
The GitHub repository includes scripts for deploying the TodoList multi-container application using an
ACS–Kubernetes or AKS cluster as shown in Figure 1.
Figure 1. Simplified architecture for deploying the multi-container application to ACS or AKS.
For more information about Kubernetes, see Kubernetes Services in the Kubernetes documentation.
7
Build and deploy a multi-container application
Configuration
In ASP.NET Core, the configuration API provides a way of configuring an app based on a list of name-value
pairs. Configuration is read at runtime from multiple sources. The name-value pairs can be grouped
hierarchically. ASP.NET Core includes configuration providers for:
• Command-line arguments
• Environment variables
NOTE: To manage sensitive configuration data, the application uses Kubernetes secrets objects.
• The RepositoryService element contains the CosmosDb element containing the EndpointUri,
PrimaryKey, DatabaseName, and CollectionName of the Azure Cosmos DB database holding the data.
• The NotificationService element contains the ServiceBus element, which in turn contains the
ConnectionString of the Service Bus namespace used by the notification service, and the QueueName
setting, which holds the name of the queue where the back-end service sends a message any time a
CRUD operation is performed on a document.
• The DataProtection element contains the BlobStorage element, which in turn contains the
ConnectionString of the storage account used by the data protection and the ContainerName setting,
which holds the name of the container where the data protection system stores the key. For more
information, see Data Protection in ASP.NET Core.
• The Application Insights element contains the instrumentation key of the Application Insights
resource used by the service for diagnostics, logging, performance monitoring, analytics, and alerting.
• The Logging element contains the log level for the various logging providers.
NOTE: Ignore the AzureKeyVault section. It is available for use only when storing secret values
in Key Vault but is not used in this deployment.
{
"AzureKeyVault": {
"Certificate": {
8
Build and deploy a multi-container application
"CertificateEnvironmentVariable": "",
"KeyEnvironmentVariable": ""
},
"ClientId": "",
"Name": ""
},
"RepositoryService": {
"CosmosDb": {
"EndpointUri": "",
"PrimaryKey": "",
"DatabaseName": "",
"CollectionName": ""
}
},
"NotificationService": {
"ServiceBus": {
"ConnectionString": "",
"QueueName": ""
}
},
"DataProtection": {
"BlobStorage": {
"ConnectionString": "",
"ContainerName": ""
}
},
"ApplicationInsights": {
"InstrumentationKey": ""
},
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"LogLevel": {
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
9
Build and deploy a multi-container application
}
}
• The TodoApiService element contains the EndpointUri of the TodoApi. This setting is used to contain
the name of the TodoApi service.
• The DataProtection element contains the BlobStorage element, which in turn contains the
ConnectionString of the storage account used by the data protection and the ContainerName setting,
which holds the name of the container where the data protection system stores the key.
• The Application Insights element contains the instrumentation key of the Application Insights
resource used by the service for diagnostics, logging, performance monitoring, analytics, and alerting.
• The Logging element contains the log level for the various logging providers.
NOTE: Ignore the AzureKeyVault section. It is available for use when storing secret values in
Key Vault but is not used in this deployment.
{
"AzureKeyVault": {
"Certificate": {
"CertificateEnvironmentVariable": "",
"KeyEnvironmentVariable": ""
},
"ClientId": "",
"Name": ""
},
"TodoApiService": {
"EndpointUri": ""
},
"DataProtection": {
"BlobStorage": {
"ConnectionString": "",
"ContainerName": ""
}
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
10
Build and deploy a multi-container application
},
"ApplicationInsights": {
"InstrumentationKey": ""
}
}
• appsettings.json
• appsettings.<EnvironmentName>.json
• environment variables
Configuration consists of a hierarchical list of name-value pairs in which the nodes are separated by a
colon. To retrieve a value, access the Configuration indexer with the corresponding item's key. For example,
if you want to retrieve the value of the QueueName setting from the configuration of the TodoApi service,
you have to use the following format.
var queueName = Configuration["NotificationService:ServiceBus:QueueName"];
If you want to create an environment variable to provide a value for a setting defined in the
appsettings.json file, you can replace : (colon) with __ (double underscore).
NotificationService__ServiceBus__QueueName=todoapi
The CreateDefaultBuilder helper method specifies environment variables last, so that the local environment
can override anything set in deployed configuration files. This allows you to define settings in the
appsettings.json file, but leave their value empty, and specify their value using environment variables.
11
Build and deploy a multi-container application
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "TodoApi.dll"]
The Dockerfile of the TodoWeb service shows the different entry point:
FROM microsoft/aspnetcore:2.0
ARG source
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "TodoWeb.dll"]
Visual Studio Tools for Docker also creates the docker-compose.yml and docker-compose-override.yml
files that you can use to test the application locally.
Here is docker-compose.yml:
version: '3'
services:
todoapi:
image: todoapi
build:
context: ./TodoApi
dockerfile: Dockerfile
todoweb:
image: todoweb
build:
context: ./TodoWeb
dockerfile: Dockerfile
dockerfile: Dockerfile
Here is docker-compose-override.yml:
version: '3'
services:
todoapi:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- RepositoryService__CosmosDb__EndpointUri=COSMOS_DB_ENDPOINT_URI
- RepositoryService__CosmosDb__PrimaryKey=DOCUMENT_DB_PRIMARY_KEY
- RepositoryService__CosmosDb__DatabaseName=TodoApiDb
- RepositoryService__CosmosDb__CollectionName=TodoApiCollection
- NotificationService__ServiceBus__ConnectionString=SERVICE_BUS_CONNECTIONSTRING
- NotificationService__ServiceBus__QueueName=todoapi
- DataProtection__BlobStorage__ConnectionString=STORAGE_ACCOUNT_CONNECTION_STRING
- DataProtection__BlobStorage__ContainerName=todoapi
- ApplicationInsights__InstrumentationKey=APPLICATION_INSIGHTS_INSTRUMENTATION_KEY
ports:
12
Build and deploy a multi-container application
- "80"
todoweb:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- TodoApiService__EndpointUri=todoapi
- DataProtection__BlobStorage__ConnectionString=STORAGE_ACCOUNT_CONNECTION_STRING
- DataProtection__BlobStorage__ContainerName=todoweb
- ApplicationInsights__InstrumentationKey=APPLICATION_INSIGHTS_INSTRUMENTATION_KEY
ports:
- "80"
Before debugging the application in Visual Studio, make the following changes to the docker-compose-
override.yml file:
• Replace SERVICE_BUS_CONNECTION_STRING with the connection string of your Service Bus Messaging
namespace.
REM tag the local todoapi:v1 image with the name of the DOCKER_HUB_REPOSITORY
docker tag todoapi:latest DOCKER_HUB_REPOSITORY/todoapi:v1
13
Build and deploy a multi-container application
REM tag the local todoweb:v1 image with the name of the DOCKER_HUB_REPOSITORY
docker tag todoweb:latest DOCKER_HUB_REPOSITORY/todoweb:v1
REM Create an Azure Container Registry. The name of the Container Registry must be unique
az acr create --resource-group ContainerRegistryResourceGroup --name
AZURE_CONTAINER_REGISTRY --sku Basic --admin-enabled true
To tag and register the images in your repository on Docker Hub, execute the push-images-to-azure-
container-registry.cmd command file. Make sure to replace the AZURE_CONTAINER_REGISTRY placeholder
with the name of your Azure Container Registry.
REM Login to the newly created Azure Container Registry
call az acr login --name AZURE_CONTAINER_REGISTRY
REM Each container image needs to be tagged with the loginServer name of the registry.
REM This tag is used for routing when pushing container images to an image registry.
REM Save the loginServer name to the AKS_CONTAINER_REGISTRY environment variable.
for /f "delims=" %%a in ('call az acr list --resource-group ContainerRegistryResourceGroup -
-query "[].{acrLoginServer:loginServer}" --output tsv') do @set AKS_CONTAINER_REGISTRY=%%a
REM tag the local todoapi:v1 image with the loginServer of the container registry
docker tag todoapi:v1 %AKS_CONTAINER_REGISTRY%/todoapi:v1
REM tag the local todoweb:v1 image with the loginServer of the container registry
docker tag todoweb:v1 %AKS_CONTAINER_REGISTRY%/todoweb:v1
14
Build and deploy a multi-container application
Either way, to run commands on the cluster, use the kubectl command line interface. You can download
Azure CLI 2.0 from GitHub.
To manage Kubernetes entities such as services, pods, and deployments, you use the Kubernetes web
interface shown in the following figure. The last command in the script is used to launch a proxy and
browse the Kubernetes web UI.
15
Build and deploy a multi-container application
For more information, see Deploy a Kubernetes cluster in Azure Container Service
16
Build and deploy a multi-container application
You can also create the cluster from the Azure Cloud Shell. In this case, you can skip the az aks install-cli
command, because the kubectl command line interface is already installed in your shell.
• aspNetCoreEnvironment/ASPNETCORE_ENVIRONMENT
• todoApiServiceBusQueueName/NotificationService__ServiceBus__QueueName
• todoApiServiceEndpointUri/TodoApiService__EndpointUri
• todoWebDataProtectionBlobStorageContainerName/DataProtection__BlobStorage__ContainerName
• todoApiDataProtectionBlobStorageContainerName/DataProtection__BlobStorage__ContainerName
The following YAML file can be used to create a ConfigMap object named todolist-configmap that
contains a value for the above parameters.
todolist-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
17
Build and deploy a multi-container application
name: todolist-configmap
namespace: default
data:
aspNetCoreEnvironment: Development
todoApiServiceBusQueueName: todoapi
todoApiServiceEndpointUri: todoapi
todoWebDataProtectionBlobStorageContainerName: todoweb
todoApiDataProtectionBlobStorageContainerName: todoapi
The create-todolist-configmap.cmd command can be used to create the todolist-configmap object in the
Kubernetes cluster.
kubectl create --filename todolist-configmap.yml --record
You can use one of the following commands to read the value of the keys from the todolist-configmap:
# Get todolist-configmap
kubectl get configmaps todolist-configmap -o yaml
# Describe todolist-configmap
kubectl describe configmap todolist-configmap
• Unique values can be used for different deployments of the same application—particularly useful in a
multitenant environment where the same Kubernetes cluster hosts multiple instances of the same
application, each with different configuration settings.
The first step is to create a YAML file to define a secret. Each item in the file must be base64-encoded. The
multi-container sample uses a secret to define a value for the following parameter/environment variable
pairs:
• cosmosDbEndpointUri/RepositoryService__CosmosDb__EndpointUri
• cosmosDBPrimaryKey/RepositoryService__CosmosDb__PrimaryKey
• cosmosDbDatabaseName/RepositoryService__CosmosDb__DatabaseName
• cosmosDbCollectionName/RepositoryService__CosmosDb__CollectionName
• serviceBusConnectionString/NotificationService__ServiceBus__ConnectionString
• dataProtectionBlobStorageConnectionString/DataProtection__BlobStorage__ConnectionString
• applicationInsightsInstrumentationKey/ApplicationInsights__InstrumentationKey
18
Build and deploy a multi-container application
The following YAML file can be used to create a secret object named todolist-secret that contains a value
for the above parameters.
todolist-secret.yml
apiVersion: v1
kind: Secret
metadata:
name: todolist-secret
type: Opaque
data:
cosmosDbEndpointUri: BASE64-ENCODED-COSMOS-DB-ENDPOINT-URI
cosmosDBPrimaryKey: BASE64-ENCODED-COSMOS-DB-PRIMARY-KEY
cosmosDbDatabaseName: BASE64-ENCODED-COSMOS-DB-DATABASE-NAME
cosmosDbCollectionName: BASE64-ENCODED-COSMOS-DB-COLLECTION-NAME
serviceBusConnectionString: BASE64-ENCODED-SERVICE-BUS-CONNECTION-STRING
dataProtectionBlobStorageConnectionString: BASE64-ENCODED-BLOB-STORAGE-CONNECTION-STRING
applicationInsightsInstrumentationKey: BASE64-ENCODED-APP-INSIGHTS-INSTRUMENTATION-KEY
On Windows, you can use the following command to translate a value into a base64 format.
powershell "[convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(\"value\"))"
In a Linux bash shell, you can use the base64 command line utility to encode a value to a base64
format.
echo -n "value" | base64
The create-todolist-secret.cmd command can be used to create the todolist-secret object in the
Kubernetes cluster:
kubectl create --filename todolist-secret.yml --record
You can use kubectl to read the value of a setting defined from a secret object. For example, you can
use the following bash command to read the value of cosmosDbCollectionName parameter.
# Get secret
kubectl get secret todolist-secret -o jsonpath="{.data.cosmosDbCollectionName}" | base64 --
decode; echo
19
Build and deploy a multi-container application
For more information about how to deploy OSBA in your Kubernetes cluster and how to provision and bind
services, please see the following resources:
• Integrate with Azure-managed services using Open Service Broker for Azure (OSBA)
• Connect your applications to Azure with Open Service Broker for Azure
The Docker images can be pulled from an Azure Container Registry or from Docker Hub. The GitHub
solution contains scripts and YAML files to accomplish both tasks in the Scripts/Kubernetes-Scripts folder.
todolist-deployments-and-services-from-docker-hub.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: todoapi
labels:
app: todoapi
spec:
replicas: 3
selector:
matchLabels:
app: todoapi
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: todoapi
spec:
containers:
- name: todoapi
image: paolosalvatori/todoapi:v2
ports:
- containerPort: 80
20
Build and deploy a multi-container application
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: RepositoryService__CosmosDb__EndpointUri
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbEndpointUri
- name: RepositoryService__CosmosDb__PrimaryKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDBPrimaryKey
- name: RepositoryService__CosmosDb__DatabaseName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbDatabaseName
- name: RepositoryService__CosmosDb__CollectionName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbCollectionName
- name: NotificationService__ServiceBus__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: serviceBusConnectionString
- name: NotificationService__ServiceBus__QueueName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceBusQueueName
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
21
Build and deploy a multi-container application
---
apiVersion: v1
kind: Service
metadata:
name: todoapi
labels:
app: todoapi
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
selector:
app: todoapi
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: todoweb
labels:
app: todoweb
spec:
replicas: 3
selector:
matchLabels:
app: todoweb
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: todoweb
spec:
containers:
- name: todoweb
image: paolosalvatori/todoweb:v2
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: TodoApiService__EndpointUri
valueFrom:
configMapKeyRef:
name: todolist-configmap
22
Build and deploy a multi-container application
key: todoApiServiceEndpointUri
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoWebDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: todoweb
labels:
app: todoweb
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
selector:
app: todoweb
NOTE: To map a secret to an environment variable in a pod specification, create a secret object
in Kubernetes, modify the pod definition for the appropriate containers, and then make sure the
code looks for values in the right environment variables. For details, see Using secrets as
environment variables in the Kubernetes documentation.
To list the newly created services and deployments, use the kubectl command line interface to run the
following commands:
REM Get services
kubectl get services
23
Build and deploy a multi-container application
As the following figure shows, you can see the TodoApi and TodoWeb services and the TodoApi and
TodoWeb deployments.
Kubernetes service types allow you to specify the kind of service you want. The default is ClusterIP. A
service of type LoadBalancer also has a cluster IP, the virtual, internal IP used by the kube-proxy running on
each node of the cluster. It’s worth noting that if you define the service type as ClusterIP, no public IP is
exposed over the Internet, and consequently, no load balancing rule is created.
• NodePort: Exposes the service on each node’s IP at a static port (the NodePort). A ClusterIP service, to
which the NodePort service will route, is automatically created. You can contact the NodePort service
from outside the cluster by sending a request.
• LoadBalancer: Exposes the service externally using a cloud provider’s load balancer. NodePort and
ClusterIP services, to which the external load balancer will route, are automatically created.
• ExternalName: Maps the service to the contents of the externalName field (for example,
foo.bar.example.com) by returning a CNAME record with its value. No proxying of any kind is set up.
This type requires kube-dns version 1.7 or later.
24
Build and deploy a multi-container application
In our example, if you want to provide the ability to call the REST services exposed by the TodoApi service
to external applications running outside of the Kubernetes cluster, and not only to the TodoWeb service
running on the same cluster, you have to specify LoadBalancer as a service type.
As the following figure shows, you can use Azure portal to see the front-end IP configuration for the Azure
Load Balancer used by Azure Container Service in front of the Kubernetes cluster nodes.
Figure 4. Configuration of the front-end load balancers for the Kubernetes cluster.
Key:
The first row contains an IP address corresponding to the public IP of the azure-vote-front service,
a Quickstart sample deployed on the same Kubernetes cluster.
The second row contains an IP address corresponding to the Public IP of the TodoApi service.
The third row contains an IP address corresponding to the Public IP of the TodoWeb service.
25
Build and deploy a multi-container application
You can also use Azure portal to see the load balancing rules defined for the load balancer used in front of
the cluster nodes. Note that there is a rule for each public IP on the port 80, all sharing the same backend
pool.
Azure portal also displays information about the back-end pools. As Figure 9 shows, in this topology, the
Azure Container Service–Kubernetes cluster uses a single back-end pool composed of just three nodes.
26
Build and deploy a multi-container application
As Figure 10 shows, you can retrieve information about cluster nodes by running the following command:
REM Get nodes
kubectl get nodes
Alternatively, if you want to use the TodoApi service only as a back-end service from the TodoWeb service,
and you don't want to expose it publicly, you can specify ClusterIP as its service type. This value makes the
service accessible only from within the cluster.
• Kubernetes on Azure
For example, to deploy babosbird.com, a sample domain created by the author, the steps are as follows:
1. Create a public domain using a domain registrar such as GoDaddy. For this example, babosbird.com
was created.
27
Build and deploy a multi-container application
2. To create the Azure DNS and related record, run the create-azure-dns-for-kubernetes-todoapi-
service.cmd script:
REM Create a resource group for the DNS Zone and records
az group create --name DnsResourceGroup --location westeurope
REM Create a DNS zone called babosbird.com in the resource group DnsResourceGroup
az network dns zone create --name babosbird.com --resource-group DnsResourceGroup --tags
environment=K8s type=DNS
REM Create an A record for the public ip exposed by the public load balancer created for the
TodoApi service in Kubernetes
az network dns record-set a add-record --resource-group DnsResourceGroup --zone-name
babosbird.com --record-set-name www --ipv4-address 13.93.46.58
az network dns record-set a add-record --resource-group DnsResourceGroup --zone-name
babosbird.com --record-set-name * --ipv4-address 13.93.46.58
REM Retrieve the name servers for your zone. These name servers should be configured with
REM the domain name registrar where you purchased the domain name
az network dns zone list --resource-group DnsResourceGroup --query
"[?name=='babosbird.com']|[0].nameServers" --output json
The last command in particular can be used to retrieve the name servers. Azure DNS automatically
creates authoritative NS records in your zone containing the assigned name servers. You need these
name servers to delegate the custom domain to Azure DNS.
3. When the DNS zone is created and you have the name servers, update the parent domain with the
Azure DNS name servers. Each registrar has its own DNS management tools to change the name server
records for a domain. In the registrar's DNS management page, edit the NS records and replace the NS
records with the ones Azure DNS created. Figure 8 shows how this is done in GoDaddy.
28
Build and deploy a multi-container application
Figure 8. Using GoDaddy to replace the NS records with the Azure DNS names servers.
This command verifies the front-end service IP that is responding to the public domain you delegated
to your Azure DNS.
29
Build and deploy a multi-container application
5. Browse to the front-end web site using the custom domain I registered as Figure 9 shows.
For more information about how to create an Azure DNS service and use the Azure CLI to manage zones
and records, please see the following resources:
• How to manage DNS Zones in Azure DNS using the Azure CLI 2.0
• Manage DNS records and recordsets in Azure DNS using the Azure CLI 2.0
For more information to delegate a public domain to Azure DNS, see the following articles:
You use a YAML file to define the services and deployments of the multi-container application and run it
from Cloud Shell. The first step is to run a command file from your local computer using the Azure CLI. The
command file sets up the Azure environment as follows:
30
Build and deploy a multi-container application
Command file
REM Create a resource group for the storage account
az group create --name RESOURCE_GROUP --location LOCATION --output jsonc
REM upload the YAML containing the definition of services and deployments to the clouddrive
from a command-prompt on the local machine
az storage file upload --account-name STORAGE_ACCOUNT_NAME --account-key
STORAGE_ACCOUNT_PRIMARY_KEY --share-name SHARE_NAME --source PATH_TO_YAML_FILE
• Replace STORAGE_ACCOUNT_PRIMARY_KEY with the primary key of the new storage account.
• Replace SHARE_NAME with the name of the new file share in the storage account.
• Replace PATH_TO_YAML_FILE with the path to the YAML containing the definition of the services and
deployments of the multi-container application.
For more information about how to mount a file share from an Azure Cloud Shell, see Persist files in Azure
Cloud Shell.
• aspNetCoreEnvironment/ASPNETCORE_ENVIRONMENT
• todoApiServiceBusQueueName/NotificationService__ServiceBus__QueueName
• todoApiServiceEndpointUri/TodoApiService__EndpointUri
• todoWebDataProtectionBlobStorageContainerName/DataProtection__BlobStorage__ContainerName
• todoApiDataProtectionBlobStorageContainerName/DataProtection__BlobStorage__ContainerName
31
Build and deploy a multi-container application
The following YAML file can be used to create a ConfigMap object named todolist-configmap that
contains a value for the above parameters.
todolist-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: todolist-configmap
namespace: default
data:
aspNetCoreEnvironment: Development
todoApiServiceBusQueueName: todoapi
todoApiServiceEndpointUri: todoapi
todoWebDataProtectionBlobStorageContainerName: todoweb
todoApiDataProtectionBlobStorageContainerName: todoapi
The create-todolist-configmap.cmd command can be used to create the todolist-configmap object in the
Kubernetes cluster.
kubectl create --filename todolist-configmap.yml --record
You can use one of the following commands to read the value of the keys from todolist-configmap:
# Get todolist-configmap
kubectl get configmaps todolist-configmap -o yaml
# Describe todolist-configmap
kubectl describe configmap todolist-configmap
• cosmosDbEndpointUri\RepositoryService__CosmosDb__EndpointUri
• cosmosDBPrimaryKey\RepositoryService__CosmosDb__PrimaryKey
• cosmosDbDatabaseName\RepositoryService__CosmosDb__DatabaseName
• cosmosDbCollectionName\RepositoryService__CosmosDb__CollectionName
• serviceBusConnectionString\NotificationService__ServiceBus__ConnectionString
• dataProtectionBlobStorageConnectionString\DataProtection__BlobStorage__ConnectionString
• applicationInsightsInstrumentationKey\ApplicationInsights__InstrumentationKey
The following YAML file can be used to create a secret object named todolist-secret that contains the
values for these parameters. Before running the script, make sure to replace the placeholders in the
todolist-secret.yml file with the corresponding base64-encoded value for each configuration setting.
32
Build and deploy a multi-container application
todolist-secret.yml
apiVersion: v1
kind: Secret
metadata:
name: todolist-secret
type: Opaque
data:
cosmosDbEndpointUri: BASE64-ENCODED-COSMOS-DB-ENDPOINT-URI
cosmosDBPrimaryKey: BASE64-ENCODED-COSMOS-DB-PRIMARY-KEY
cosmosDbDatabaseName: BASE64-ENCODED-COSMOS-DB-DATABASE-NAME
cosmosDbCollectionName: BASE64-ENCODED-COSMOS-DB-COLLECTION-NAME
serviceBusConnectionString: BASE64-ENCODED-SERVICE-BUS-CONNECTION-STRING
dataProtectionBlobStorageConnectionString: BASE64-ENCODED-BLOB-STORAGE-CONNECTION-STRING
applicationInsightsInstrumentationKey: BASE64-ENCODED-APP-INSIGHTS-INSTRUMENTATION-KEY
On Windows, you can use the following command to translate a value into a base64 format:
powershell "[convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(\"value\"))"
In a Linux bash shell, you can use the base64 command line utility to encode a value to a base64 format:
echo -n "value" | base64
The create-secret-in-kubernetes.cmd script can be used to create the todolist-secret object in the
Kubernetes cluster.
create-secret-in-kubernetes.cmd
kubectl create --filename todolist-secret.yml --record
33
Build and deploy a multi-container application
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: todoapi
spec:
containers:
- name: todoapi
image: AZURE_CONTAINER_REGISTRY_NAME.azurecr.io/todoapi:v1
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: RepositoryService__CosmosDb__EndpointUri
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbEndpointUri
- name: RepositoryService__CosmosDb__PrimaryKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDBPrimaryKey
- name: RepositoryService__CosmosDb__DatabaseName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbDatabaseName
- name: RepositoryService__CosmosDb__CollectionName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbCollectionName
- name: NotificationService__ServiceBus__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: serviceBusConnectionString
- name: NotificationService__ServiceBus__QueueName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceBusQueueName
- name: DataProtection__BlobStorage__ConnectionString
34
Build and deploy a multi-container application
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: todoapi
labels:
app: todoapi
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
selector:
app: todoapi
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: todoweb
labels:
app: todoweb
spec:
replicas: 3
selector:
matchLabels:
app: todoweb
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: todoweb
spec:
containers:
35
Build and deploy a multi-container application
- name: todoweb
image: AZURE_CONTAINER_REGISTRY_NAME.azurecr.io/todoweb:v1
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: TodoApiService__EndpointUri
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceEndpointUri
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoWebDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: todoweb
labels:
app: todoweb
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
selector:
app: todoweb
3. Run the following command from the Azure Cloud Shell to deploy the multi-container application to
your Kubernetes cluster:
kubectl create --filename todolist-deployments-and-services-from-azure-container-registry.yml --rec
ord
36
Build and deploy a multi-container application
4. To display EXTERNAL-IP of the TodoWeb front-end service, run the following command:
kubectl get services
5. To verify that the application works as expected, browse to the public IP of the TodoWeb front-end
service. It should look like this:
37
Build and deploy a multi-container application
The NGINX ingress controller is a daemon deployed as a Kubernetes pod that watches the API server's
/ingresses endpoint for updates to the ingress resource. Using this controller, you can implement several
patterns such as path-based fanout, SSL passthrough, TLS termination, and basic or digest HTTP
authentication.
You can deploy the NGINX ingress controller to your Kubernetes cluster in Azure using either the kubectl
CLI or Helm, a tool for managing Kubernetes configurations.
The following steps install Helm, install NGINX, and create a secret containing the certificate and private
key used for TLS termination:
1. Use the \Scripts\Helm\install-helm.sh bash script below to install and initialize Helm.
#!/bin/bash
# Install helm on the local machine
# Download Helm
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
# Execute get_helm.sh
$ ./get_helm.sh
# Initialize helm
helm init
NOTE: The helm init command is used to install Helm components in a Kubernetes cluster and
make client-side configurations. For more information, see Use Helm with Azure Container Service
(AKS) and the Helm documentation.
2. Install the NGINX ingress controller in your Kubernetes cluster using the following bash script:
# Installs nginx-ingress using helm
helm install stable/nginx-ingress -n nginx-ingress
3. Scale out the number of replicas used by the NGINX ingress controller to from 1 to 3 using the
following bash script:
# Scale the number of replicas of the nginx-ingress-controller to 3
kubectl scale deployment nginx-ingress-controller --replicas=3
38
Build and deploy a multi-container application
5. Create a self-signed certificate suitable for testing using the following bash script:
# Create a self-signed certificate. Note: private and public keys are saved locally
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj
"/CN=nginxsvc/O=nginxsvc"
6. Create a secret in your Kubernetes cluster using the self-signed certificate and its private key. Run the
following bash script:
# Use the private and public key to create a secret used for SSL termination
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
IMPORTANT: The data keys must be named tls.crt and tls.key. In a production environment,
you should replace the self-signed certificate with a valid certificate issued by a trusted certificate
authority.
As an alternative to step 6, you can also use the following YAML file to define the tls-secret:
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: tls-secret
data:
tls.crt: [BASE64_ENCODED_CERT]
tls.key: [BASE64_ENCODED_KEY]
Then, to create the tls-secret in your Kubernetes cluster based on this YAML file, run the following bash
command:
# Create tls-secret using YAML file
kubectl create -f /mnt/c/[PATH-TO-YAML-FILE]/tls-secret.yml
For more information about Helm, see The Kubernetes Package Manager on GitHub. For more information
about NGINX ingress controller, see the Readme file for its deployment.
This guide shows how to manually create and deploy a certificate for TLS termination to a Kubernetes
cluster. However, you can use kube-lego for automatic certificate generation. The open source kube-lego
app acquires certificates for Kubernetes Ingress resources from Let's Encrypt, which issues certificates for
free.
For more information about kube-lego, see Configure Https / TLS / SSL on Kubernetes with Kube-Lego
hosted on Azure Container Service.
39
Build and deploy a multi-container application
todolist-deployments-and-services-from-docker-hub-ssl.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{.Values.backend}}
labels:
app: {{.Values.backend}}
spec:
replicas: 3
selector:
matchLabels:
app: {{.Values.backend}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: {{.Values.backend}}
spec:
containers:
- name: {{.Values.backend}}
image: {{.Values.imageRegistry}}/{{.Values.backendImage}}:{{.Values.backendTag}}
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: RepositoryService__CosmosDb__EndpointUri
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbEndpointUri
- name: RepositoryService__CosmosDb__PrimaryKey
valueFrom:
40
Build and deploy a multi-container application
secretKeyRef:
name: todolist-secret
key: cosmosDBPrimaryKey
- name: RepositoryService__CosmosDb__DatabaseName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbDatabaseName
- name: RepositoryService__CosmosDb__CollectionName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbCollectionName
- name: NotificationService__ServiceBus__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: serviceBusConnectionString
- name: NotificationService__ServiceBus__QueueName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceBusQueueName
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: {{.Values.backend}}
spec:
type: {{.Values.backendServiceType}}
ports:
- protocol: TCP
port: 80
selector:
app: {{.Values.backend}}
---
41
Build and deploy a multi-container application
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{.Values.frontend}}
labels:
app: {{.Values.frontend}}
spec:
replicas: 3
selector:
matchLabels:
app: {{.Values.frontend}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: {{.Values.frontend}}
spec:
containers:
- name: {{.Values.frontend}}
image: {{.Values.imageRegistry}}/{{.Values.frontendImage}}:{{.Values.frontendTag}}
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: TodoApiService__EndpointUri
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceEndpointUri
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoWebDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
42
Build and deploy a multi-container application
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: {{.Values.frontend}}
spec:
type: {{.Values.frontendServiceType}}
ports:
- protocol: TCP
port: 80
selector:
app: {{.Values.frontend}}
nginx-ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
spec:
tls:
- secretName: tls-secret
backend:
serviceName: ssl-todoweb
Then, to create the nginx-ingress object in your Kubernetes cluster based on this YAML file, run the
following bash command:
create-nginx-ingress.sh
# Create nginx-ingress using a YAML file
kubectl create -f /mnt/c/[PATH-TO-YAML-FILE]/nginx-ingress.yml
43
Build and deploy a multi-container application
Now the NGINX ingress controller is configured to implement SSL termination and route traffic to the ssl-
todoweb service via HTTPS. To display the external IP address exposed by the NGINX ingress controller
service, run the following bash command as Figure 10 shows:
With this information, you can now verify that the application works as expected. Browse to the external IP
address of the NGINX ingress controller front-end service. You should see the application as Figure 11
shows.
44
Build and deploy a multi-container application
The directory name is the name of the chart—in this case, Todolist. The Todolist chart is organized as a
collection of the following files in this directory:
• Chart.yaml: A YAML file containing the chart name, version, keywords, description, and other
information.
• LICENSE: An optional plain text file containing the license for the chart.
• values.yaml: A mandatory file containing the default configuration values for this chart.
• charts/: An optional directory containing any charts upon which this chart depends. This directory is
not used in this deployment. Todolist has no chart dependencies.
• templates/: An optional directory of templates that, when combined with values, will generate valid
Kubernetes manifest files.
• templates/Deployment.yaml: A YAML file containing the definition of the TodoApi and TodoWeb
services and deployments.
45
Build and deploy a multi-container application
For more information about charts, see Charts in the Helm documentation.
This project uses actions as placeholders for values defined in the values.yaml file. This technique is
extremely powerful, because you can parametrize an entire template, including the service type, container
image location, and service and deployment name.
Chart.yaml
name: TodoList
version: 1.0.0
description: todolist multi-container app
keywords:
- todoapi
home: https://github.com/paolosalvatori/service-fabric-acs-kubernetes-multi-container-app
sources:
- https://github.com/paolosalvatori/service-fabric-acs-kubernetes-multi-container-app
maintainers: # (optional)
- name: Paolo
url: https://github.com/paolosalvatori
engine: gotpl
appVersion: 1.0.0
values.yaml
imageRegistry: "paolosalvatori"
frontendImage: "todoweb"
backendImage: "todoapi"
frontendTag: "v2"
backendTag: "v2"
frontend: "todoweb"
backend: "todoapi"
frontendServiceType: "LoadBalancer"
backendServiceType: "LoadBalancer"
templates/Deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{.Values.backend}}
labels:
app: {{.Values.backend}}
spec:
46
Build and deploy a multi-container application
replicas: 3
selector:
matchLabels:
app: {{.Values.backend}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: {{.Values.backend}}
spec:
containers:
- name: {{.Values.backend}}
image: {{.Values.imageRegistry}}/{{.Values.backendImage}}:{{.Values.backendTag}}
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: RepositoryService__CosmosDb__EndpointUri
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbEndpointUri
- name: RepositoryService__CosmosDb__PrimaryKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDBPrimaryKey
- name: RepositoryService__CosmosDb__DatabaseName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbDatabaseName
- name: RepositoryService__CosmosDb__CollectionName
valueFrom:
secretKeyRef:
name: todolist-secret
key: cosmosDbCollectionName
- name: NotificationService__ServiceBus__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: serviceBusConnectionString
- name: NotificationService__ServiceBus__QueueName
47
Build and deploy a multi-container application
value: {{.Values.queueName}}
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: {{.Values.backend}}
spec:
type: {{.Values.backendServiceType}}
ports:
- protocol: TCP
port: 80
selector:
app: {{.Values.backend}}
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{.Values.frontend}}
labels:
app: {{.Values.frontend}}
spec:
replicas: 3
selector:
matchLabels:
app: {{.Values.frontend}}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: {{.Values.frontend}}
spec:
containers:
48
Build and deploy a multi-container application
- name: {{.Values.frontend}}
image: {{.Values.imageRegistry}}/{{.Values.frontendImage}}:{{.Values.frontendTag}}
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: aspNetCoreEnvironment
- name: TodoApiService__EndpointUri
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoApiServiceEndpointUri
- name: DataProtection__BlobStorage__ConnectionString
valueFrom:
secretKeyRef:
name: todolist-secret
key: dataProtectionBlobStorageConnectionString
- name: DataProtection__BlobStorage__ContainerName
valueFrom:
configMapKeyRef:
name: todolist-configmap
key: todoWebDataProtectionBlobStorageContainerName
- name: ApplicationInsights__InstrumentationKey
valueFrom:
secretKeyRef:
name: todolist-secret
key: applicationInsightsInstrumentationKey
---
apiVersion: v1
kind: Service
metadata:
name: {{.Values.frontend}}
spec:
type: {{.Values.frontendServiceType}}
ports:
- protocol: TCP
port: 80
selector:
app: {{.Values.frontend}}
49
Build and deploy a multi-container application
Make sure you have created the todolist-config and todolist-secret in your Kubernetes cluster as explained
earlier. Then, if you want to install the todolist application, run the following bash command:
# Install package
helm install --name todolist TodoList-1.0.0.tgz
Customize a deployment
Another advantage of Helm is that you can use its CLI to customize a deployment. You can override all or
part of the values provided in the values.yaml file in the chart by specifing an alternate values.yaml file.
For example, say you want to deploy a version of the application that uses HTTPS instead of HTTP. To do
this, create an alternative values.yaml file and change the service type for both the TodoApi and TodoWeb
services from LoadBalancer to ClusterIP. A good practice is to add the ssl- prefix to the name of the
corresponding services and deployments. To do this:
50
Build and deploy a multi-container application
Azure Container Service with Managed Kubernetes Service (AKS) manages your hosted Kubernetes
environment, making it quick and easy to deploy and manage containerized applications without container
orchestration expertise. It also eases ongoing operations and maintenance by provisioning, upgrading, and
scaling resources on demand, without taking your applications offline.
Application Insights is an extensible Application Performance Management (APM) service for monitoring
live web applications. It detects performance anomalies and includes powerful analytics tools to help you
diagnose issues and to understand what users do with your apps. It works for apps on a wide variety of
platforms including .NET, Node.js, and J2EE, hosted on-premises or in the cloud.
Service Bus Messaging is a reliable message delivery service for brokered or third-party communications
that connect applications through the cloud. Service Bus messaging with queues, topics, and subscriptions
can be thought of as asynchronous, or temporally decoupled. Producers (senders) and consumers
(receivers) do not have to be online at the same time. The messaging infrastructure reliably stores
messages in a "broker" (for example, a queue) until the consuming party is ready to receive them.
Azure Cosmos DB is a globally distributed, multi-model database that can elastically and independently
scale throughput and storage across any number of Azure's geographic regions. Azure Cosmos DB
supports multiple data models and popular APIs for accessing and querying data. The sample in this
solution uses the Azure Cosmos DB SQL API, which provides a schema-less JSON database engine with SQL
querying capabilities.
Azure Container Registry is a managed Docker registry service based on the open-source Docker Registry
2.0. You can create and maintain Azure container registries to store and manage your private Docker
container images. Container registries in Azure work with your existing container development and
deployment pipelines and draw on the body of Docker community expertise.
Azure Domain Name System (DNS) is responsible for translating (or resolving) a website or service name to
its IP address. Azure DNS is a hosting service for DNS domains, providing name resolution using the Azure
infrastructure. By hosting your domains in Azure, you can manage your DNS records using the same
credentials, APIs, tools, and billing as your other Azure services.
51
Build and deploy a multi-container application
Learn more
The Azure Container Service with Kubernetes Documentation provides details on the container features
and scenarios. The following useful links contain more in depth information:
• Deploy Kubernetes cluster for Linux containers
• Tutorial: how to create and deploy a a multi-container application on ACS and Kubernetes
• What is Docker?
52