├── .dockerignore
├── .editorconfig
├── .env
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── azure
└── kubernetes-cluster-template
│ ├── DeploymentHelper.cs
│ ├── deploy.ps1
│ ├── deploy.sh
│ ├── deployer.rb
│ ├── parameters.json
│ └── template.json
├── backend-api1
├── .env
├── Dockerfile
├── api
│ ├── health.js
│ └── message.js
├── package-lock.json
├── package.json
└── server.js
├── backend-api2
├── .env
├── Dockerfile
├── api
│ ├── health.js
│ └── message.js
├── package-lock.json
├── package.json
└── server.js
├── build.ps1
├── docker-compose.yml
├── docs
├── Azure-and-VSTS
│ ├── README.md
│ ├── vsts_cd_pipeline.png
│ ├── vsts_cd_pipeline_agent_job.png
│ ├── vsts_cd_pipeline_agent_k8s_deploy.png
│ ├── vsts_cd_pipeline_artifact_azurecr.png
│ ├── vsts_cd_pipeline_artifact_azurecr_deployment_trigger.png
│ ├── vsts_cd_pipeline_artifact_github.png
│ ├── vsts_ci_build_images.png
│ ├── vsts_ci_build_images_agent_build_services.png
│ ├── vsts_ci_build_images_agent_job.png
│ ├── vsts_ci_build_images_agent_publish_artifact.png
│ ├── vsts_ci_build_images_agent_push_services.png
│ └── vsts_services_kubernetes_authentication.png
├── DockerApplicationQuickLaunch
│ └── README.md
├── Environment
│ ├── README.md
│ ├── docker-about.PNG
│ ├── docker-enable-kubernetes.PNG
│ └── docker-memory-and-cpu.PNG
├── KubernetesApplicationQuickLaunch
│ └── README.md
├── Other
│ └── README.md
├── docker-application-structure.PNG
└── kubernetes-application-structure.PNG
├── frontend
├── .env
├── Dockerfile
├── app.js
├── index.html
├── package-lock.json
├── package.json
└── server.js
└── kubernetes
├── backend-api-1.azure.deploy.yaml
├── backend-api-1.deploy.yaml
├── backend-api-1.svc.yaml
├── backend-api-2.azure.deploy.yaml
├── backend-api-2.deploy.yaml
├── backend-api-2.svc.yaml
├── frontend.azure.deploy.yaml
├── frontend.deploy.yaml
├── frontend.svc.yaml
├── ingress.azure.yaml
├── ingress.yaml
├── reinstall.ps1
└── status.ps1
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Standard stuff
2 | Dockerfile*
3 | **/*Dockerfile*
4 | docker-compose*
5 | .dockerignore
6 | .git
7 | .gitignore
8 | readme.md
9 | **/readme.md
10 | **/LICENSE
11 | .vscode/
12 |
13 | # Node stuff
14 | node_modules
15 | **/node_modules
16 | **/*.log
17 |
18 | # Outputs, binaries etc
19 |
20 | # Project specific folders
21 | kubernetes/
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | #DOCKER_REGISTRY_URL=
2 | DOCKER_REG=
3 | DOCKER_TAG=
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # IDEs and editors
7 | /.idea
8 | .project
9 | .classpath
10 | .c9/
11 | *.launch
12 | .settings/
13 | *.sublime-workspace
14 |
15 | # misc
16 | /.sass-cache
17 | /connect.lock
18 | /coverage
19 | /libpeerconnection.log
20 | npm-debug.log
21 | testem.log
22 | /typings
23 | yarn-error.log
24 |
25 | # e2e
26 | /e2e/*.js
27 | /e2e/*.map
28 |
29 | # System Files
30 | .DS_Store
31 | Thumbs.db
32 |
33 | # C# Dotnet Core Stuff
34 | *.swp
35 | *.*~
36 | project.lock.json
37 | .DS_Store
38 | *.pyc
39 |
40 | # User-specific files
41 | *.suo
42 | *.user
43 | *.userosscache
44 | *.sln.docstates
45 |
46 | # Build results
47 | [Dd]ebug/
48 | [Dd]ebugPublic/
49 | [Rr]elease/
50 | [Rr]eleases/
51 | x64/
52 | x86/
53 | build/
54 | bld/
55 | [Bb]in/
56 | [Oo]bj/
57 | [Pp]ub/
58 | msbuild.log
59 | msbuild.err
60 | msbuild.wrn
61 |
62 | # These contain secrets!
63 | #.env
64 | azure.json
65 | *.myparam.json
66 | appsettings.json
67 | environment.live.ts
68 | myvalues.yaml
69 | **/*.bot
70 |
71 | # Visual Studio Code - BC changed my mind, allowed this in the repo
72 | #.vscode
73 |
74 | # Compiled output
75 | dist/
76 | tmp/
77 | tmp.zip
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Debug Node - Frontend",
11 | "cwd": "${workspaceFolder}/node/frontend",
12 | //"preLaunchTask": "NPM Install - Node Frontend",
13 | "program": "${workspaceFolder}/node/frontend/server.js",
14 | "args": [
15 | "${workspaceFolder}/angular/dist"
16 | ]
17 | },
18 | {
19 | "type": "node",
20 | "request": "launch",
21 | "name": "Debug Node - API",
22 | //"preLaunchTask": "NPM Install - Node API",
23 | "cwd": "${workspaceFolder}/node/data-api",
24 | "program": "${workspaceFolder}/node/data-api/server.js"
25 | },
26 | {
27 | "type": "node",
28 | "request": "launch",
29 | "name": "Debug Bot",
30 | //"preLaunchTask": "NPM Install - Node API",
31 | "cwd": "${workspaceFolder}/bot",
32 | "program": "${workspaceFolder}/bot/app.js"
33 | },
34 | {
35 | "name": "Debug Orleans - Silo",
36 | "type": "coreclr",
37 | "request": "launch",
38 | "preLaunchTask": "Dotnet Build - Orleans Silo",
39 | "program": "${workspaceRoot}/orleans/Silo/bin/Debug/netcoreapp2.0/Silo.dll",
40 | "args": [],
41 | "cwd": "${workspaceRoot}/orleans/Silo",
42 | "stopAtEntry": false,
43 | "console": "internalConsole",
44 | "internalConsoleOptions": "openOnSessionStart"
45 | },
46 | {
47 | "name": "Debug Orleans - API",
48 | "type": "coreclr",
49 | "request": "launch",
50 | "preLaunchTask": "Dotnet Build - Orleans API",
51 | "program": "${workspaceRoot}/orleans/API/bin/Debug/netcoreapp2.0/API.dll",
52 | "args": [],
53 | "cwd": "${workspaceFolder}/orleans/API",
54 | "stopAtEntry": false,
55 | "internalConsoleOptions": "openOnSessionStart",
56 | "launchBrowser": {
57 | "enabled": true,
58 | "args": "${auto-detect-url}/api/values",
59 | "windows": {
60 | "command": "cmd.exe",
61 | "args": "/C start ${auto-detect-url}/api/values"
62 | },
63 | "osx": {
64 | "command": "open"
65 | },
66 | "linux": {
67 | "command": "xdg-open"
68 | }
69 | },
70 | "env": {
71 | "ASPNETCORE_ENVIRONMENT": "Development"
72 | },
73 | "sourceFileMap": {
74 | "/Views": "${workspaceFolder}/Views"
75 | }
76 | },
77 | {
78 | "name": ".NET Core Attach",
79 | "type": "coreclr",
80 | "request": "attach",
81 | "processId": "${command:pickProcess}"
82 | }
83 | ]
84 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | //
3 | // You will need to change these as required!
4 | //
5 | "appservice-site": "smilr-live",
6 | "appservice-user": "bcdeployer"
7 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | //
7 | // Check the .vscode/settings.json file for config values
8 | //
9 | {
10 | "label": "Docker Build - Node images",
11 | "type": "shell",
12 | "command": "docker-compose build",
13 | "problemMatcher": []
14 | },
15 | {
16 | "label": "Docker Push - Node images",
17 | "type": "shell",
18 | "command": "docker-compose push",
19 | "problemMatcher": []
20 | },
21 | {
22 | "label": "Docker Build - Orleans images",
23 | "type": "shell",
24 | "options": {
25 | "cwd": "orleans"
26 | },
27 | "command": "docker-compose build",
28 | "problemMatcher": []
29 | },
30 | {
31 | "label": "Docker Push - Orleans images",
32 | "type": "shell",
33 | "options": {
34 | "cwd": "orleans"
35 | },
36 | "command": "docker-compose push",
37 | "problemMatcher": []
38 | },
39 | {
40 | "label": "Azure App Service Deploy",
41 | "type": "shell",
42 | "command": "powershell.exe ./azure/appservice/deploy-frontend.ps1 -appService ${config:appservice-site} -deployUser ${config:appservice-user}",
43 | "problemMatcher": []
44 | },
45 | {
46 | "label": "Dotnet Build - Orleans API",
47 | "command": "dotnet",
48 | "type": "process",
49 | "args": [
50 | "build",
51 | "${workspaceFolder}/orleans/API/API.csproj"
52 | ],
53 | "problemMatcher": "$msCompile",
54 | "group": {
55 | "kind": "build",
56 | "isDefault": true
57 | }
58 | },
59 | {
60 | "label": "Dotnet Build - Orleans Silo",
61 | "command": "dotnet",
62 | "type": "process",
63 | "args": [
64 | "build",
65 | "${workspaceFolder}/orleans/Silo/Silo.csproj"
66 | ],
67 | "problemMatcher": "$msCompile",
68 | "group": {
69 | "kind": "build",
70 | "isDefault": true
71 | }
72 | },
73 | {
74 | "label": "Dotnet Publish - Orleans Project",
75 | "command": "dotnet",
76 | "type": "process",
77 | "args": [
78 | "publish",
79 | "${workspaceFolder}/orleans/OrleansContainer.sln",
80 | "-c",
81 | "Release",
82 | "-o",
83 | "pub"
84 | ],
85 | "problemMatcher": "$msCompile"
86 | },
87 |
88 | {
89 | "label": "NPM Install - Node API",
90 | // VSCode has bugs when NPM is installed in both WSL & Windows. Hardcode path to NPM
91 | // Change as required.
92 | "command": "npm",
93 | "type": "shell",
94 | "options": {
95 | "cwd": "node/data-api"
96 | },
97 | "args": [
98 | "install"
99 | ],
100 | "problemMatcher": []
101 | },
102 | {
103 | "label": "NPM Install - Node Frontend",
104 | "command": "npm",
105 | "type": "shell",
106 | "options": {
107 | "cwd": "node/frontend"
108 | },
109 | "args": [
110 | "install"
111 | ],
112 | "problemMatcher": []
113 | }
114 | ]
115 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2018 Sami Pylkkänen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |
4 | # Overview
5 | This is simple nodejs based application designed to showcase microservices in docker and kubernetes. Application contains one web application and two REST api services where one of the service is internal which is not directly accessible from internet.
6 |
7 | Configuration demonstrates basics how to setup local docker registry and local kubernetes hosting. e.g. If you want to create own private cloud solution.
8 |
9 | :grey_exclamation: Please notice that example is documented based on Windows host.
10 |
11 |
12 | # Architecture
13 |
14 | All application services used in showcase are written with NodeJS.
15 |
16 | ## Docker hosted application
17 | Simplyfied overal structure of application networking.
18 | 
19 |
20 | ## Kubernetes hosted application
21 | Simplyfied overal structure of application networking.
22 | 
23 |
24 |
25 | # Repository Structure
26 | The main levels of the repository directory tree are laid out as follows
27 | ```
28 | .
29 | ├── backend-api1 NodeJS backend service 1.
30 | │ └── api REST api's.
31 | ├── backend-api2 NodeJS internal backend service 2.
32 | │ ├── api REST api's.
33 | ├── docs Documentation
34 | ├── frontend NodeJS hosted webapp.
35 | ├── kubernetes Kubernetes installation scripts to setup application to kubernetes.
36 | ├── docker-compose.yml Docker compose to setup application to docker.
37 | ```
38 |
39 |
40 | # Environment
41 | Document describes environment used in this showcase.
42 | > [Environment](https://github.com/spylkkanen/kubernetes-microservices/tree/master/docs/Environment/README.md)
43 |
44 |
45 | # Docker application quick launch
46 | Document uses powershell script to show docker image build and push to local container repository. Finally presentation starts containers in local docker service and has some quick browser based test cases.
47 | > [Docker application quick launch](https://github.com/spylkkanen/kubernetes-microservices/tree/master/docs/DockerApplicationQuickLaunch/README.md)
48 |
49 |
50 | # Kubernetes application quick launch
51 | Document uses powershell script to show docker image build and push to local container repository and how to deploy container registry images to local kubernetes cluster. Finally presentation starts containers in local kubernetes cluster and has some quick browser based test cases.
52 | > [Kubernetes application quick launch](https://github.com/spylkkanen/kubernetes-microservices/tree/master/docs/KubernetesApplicationQuickLaunch/README.md)
53 |
54 |
55 | # Azure and VSTS CI/CD integration
56 | Maybe local cloud is not what you are looking for and morelikely you want to same in Cloud. This documentation shows how to integrate Azure and VSTS Continuous Integration and Continuous Development.
57 | > [Visual Studio Team Service CI/CD configuration](https://github.com/spylkkanen/kubernetes-microservices/tree/master/docs/Azure-and-VSTS/README.md)
58 |
59 |
60 | # Other usefull commands and etc.
61 | This section basically contains some notes.
62 | > [Notes](https://github.com/spylkkanen/kubernetes-microservices/tree/master/docs/Other/README.md)
63 |
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/DeploymentHelper.cs:
--------------------------------------------------------------------------------
1 | // Requires the following Azure NuGet packages and related dependencies:
2 | // package id="Microsoft.Azure.Management.Authorization" version="2.0.0"
3 | // package id="Microsoft.Azure.Management.ResourceManager" version="1.4.0-preview"
4 | // package id="Microsoft.Rest.ClientRuntime.Azure.Authentication" version="2.2.8-preview"
5 |
6 | using Microsoft.Azure.Management.ResourceManager;
7 | using Microsoft.Azure.Management.ResourceManager.Models;
8 | using Microsoft.Rest.Azure.Authentication;
9 | using Newtonsoft.Json;
10 | using Newtonsoft.Json.Linq;
11 | using System;
12 | using System.IO;
13 |
14 | namespace PortalGenerated
15 | {
16 | ///
17 | /// This is a helper class for deploying an Azure Resource Manager template
18 | /// More info about template deployments can be found here https://go.microsoft.com/fwLink/?LinkID=733371
19 | ///
20 | class DeploymentHelper
21 | {
22 | string subscriptionId = "your-subscription-id";
23 | string clientId = "your-service-principal-clientId";
24 | string clientSecret = "your-service-principal-client-secret";
25 | string resourceGroupName = "resource-group-name";
26 | string deploymentName = "deployment-name";
27 | string resourceGroupLocation = "resource-group-location"; // must be specified for creating a new resource group
28 | string pathToTemplateFile = "path-to-template.json-on-disk";
29 | string pathToParameterFile = "path-to-parameters.json-on-disk";
30 | string tenantId = "tenant-id";
31 |
32 | public async void Run()
33 | {
34 | // Try to obtain the service credentials
35 | var serviceCreds = await ApplicationTokenProvider.LoginSilentAsync(tenantId, clientId, clientSecret);
36 |
37 | // Read the template and parameter file contents
38 | JObject templateFileContents = GetJsonFileContents(pathToTemplateFile);
39 | JObject parameterFileContents = GetJsonFileContents(pathToParameterFile);
40 |
41 | // Create the resource manager client
42 | var resourceManagementClient = new ResourceManagementClient(serviceCreds);
43 | resourceManagementClient.SubscriptionId = subscriptionId;
44 |
45 | // Create or check that resource group exists
46 | EnsureResourceGroupExists(resourceManagementClient, resourceGroupName, resourceGroupLocation);
47 |
48 | // Start a deployment
49 | DeployTemplate(resourceManagementClient, resourceGroupName, deploymentName, templateFileContents, parameterFileContents);
50 | }
51 |
52 | ///
53 | /// Reads a JSON file from the specified path
54 | ///
55 | /// The full path to the JSON file
56 | /// The JSON file contents
57 | private JObject GetJsonFileContents(string pathToJson)
58 | {
59 | JObject templatefileContent = new JObject();
60 | using (StreamReader file = File.OpenText(pathToJson))
61 | {
62 | using (JsonTextReader reader = new JsonTextReader(file))
63 | {
64 | templatefileContent = (JObject)JToken.ReadFrom(reader);
65 | return templatefileContent;
66 | }
67 | }
68 | }
69 |
70 | ///
71 | /// Ensures that a resource group with the specified name exists. If it does not, will attempt to create one.
72 | ///
73 | /// The resource manager client.
74 | /// The name of the resource group.
75 | /// The resource group location. Required when creating a new resource group.
76 | private static void EnsureResourceGroupExists(ResourceManagementClient resourceManagementClient, string resourceGroupName, string resourceGroupLocation)
77 | {
78 | if (resourceManagementClient.ResourceGroups.CheckExistence(resourceGroupName) != true)
79 | {
80 | Console.WriteLine(string.Format("Creating resource group '{0}' in location '{1}'", resourceGroupName, resourceGroupLocation));
81 | var resourceGroup = new ResourceGroup();
82 | resourceGroup.Location = resourceGroupLocation;
83 | resourceManagementClient.ResourceGroups.CreateOrUpdate(resourceGroupName, resourceGroup);
84 | }
85 | else
86 | {
87 | Console.WriteLine(string.Format("Using existing resource group '{0}'", resourceGroupName));
88 | }
89 | }
90 |
91 | ///
92 | /// Starts a template deployment.
93 | ///
94 | /// The resource manager client.
95 | /// The name of the resource group.
96 | /// The name of the deployment.
97 | /// The template file contents.
98 | /// The parameter file contents.
99 | private static void DeployTemplate(ResourceManagementClient resourceManagementClient, string resourceGroupName, string deploymentName, JObject templateFileContents, JObject parameterFileContents)
100 | {
101 | Console.WriteLine(string.Format("Starting template deployment '{0}' in resource group '{1}'", deploymentName, resourceGroupName));
102 | var deployment = new Deployment();
103 |
104 | deployment.Properties = new DeploymentProperties
105 | {
106 | Mode = DeploymentMode.Incremental,
107 | Template = templateFileContents,
108 | Parameters = parameterFileContents["parameters"].ToObject()
109 | };
110 |
111 | var deploymentResult = resourceManagementClient.Deployments.CreateOrUpdate(resourceGroupName, deploymentName, deployment);
112 | Console.WriteLine(string.Format("Deployment status: {0}", deploymentResult.Properties.ProvisioningState));
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/deploy.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Deploys a template to Azure
4 |
5 | .DESCRIPTION
6 | Deploys an Azure Resource Manager template
7 |
8 | .PARAMETER subscriptionId
9 | The subscription id where the template will be deployed.
10 |
11 | .PARAMETER resourceGroupName
12 | The resource group where the template will be deployed. Can be the name of an existing or a new resource group.
13 |
14 | .PARAMETER resourceGroupLocation
15 | Optional, a resource group location. If specified, will try to create a new resource group in this location. If not specified, assumes resource group is existing.
16 |
17 | .PARAMETER deploymentName
18 | The deployment name.
19 |
20 | .PARAMETER templateFilePath
21 | Optional, path to the template file. Defaults to template.json.
22 |
23 | .PARAMETER parametersFilePath
24 | Optional, path to the parameters file. Defaults to parameters.json. If file is not found, will prompt for parameter values based on template.
25 | #>
26 |
27 | param(
28 | [Parameter(Mandatory=$True)]
29 | [string]
30 | $subscriptionId,
31 |
32 | [Parameter(Mandatory=$True)]
33 | [string]
34 | $resourceGroupName,
35 |
36 | [string]
37 | $resourceGroupLocation,
38 |
39 | [Parameter(Mandatory=$True)]
40 | [string]
41 | $deploymentName,
42 |
43 | [string]
44 | $templateFilePath = "template.json",
45 |
46 | [string]
47 | $parametersFilePath = "parameters.json"
48 | )
49 |
50 | <#
51 | .SYNOPSIS
52 | Registers RPs
53 | #>
54 | Function RegisterRP {
55 | Param(
56 | [string]$ResourceProviderNamespace
57 | )
58 |
59 | Write-Host "Registering resource provider '$ResourceProviderNamespace'";
60 | Register-AzureRmResourceProvider -ProviderNamespace $ResourceProviderNamespace;
61 | }
62 |
63 | #******************************************************************************
64 | # Script body
65 | # Execution begins here
66 | #******************************************************************************
67 | $ErrorActionPreference = "Stop"
68 |
69 | # sign in
70 | Write-Host "Logging in...";
71 | Login-AzureRmAccount;
72 |
73 | # select subscription
74 | Write-Host "Selecting subscription '$subscriptionId'";
75 | Select-AzureRmSubscription -SubscriptionID $subscriptionId;
76 |
77 | # Register RPs
78 | $resourceProviders = @("microsoft.containerservice","microsoft.resources");
79 | if($resourceProviders.length) {
80 | Write-Host "Registering resource providers"
81 | foreach($resourceProvider in $resourceProviders) {
82 | RegisterRP($resourceProvider);
83 | }
84 | }
85 |
86 | #Create or check for existing resource group
87 | $resourceGroup = Get-AzureRmResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue
88 | if(!$resourceGroup)
89 | {
90 | Write-Host "Resource group '$resourceGroupName' does not exist. To create a new resource group, please enter a location.";
91 | if(!$resourceGroupLocation) {
92 | $resourceGroupLocation = Read-Host "resourceGroupLocation";
93 | }
94 | Write-Host "Creating resource group '$resourceGroupName' in location '$resourceGroupLocation'";
95 | New-AzureRmResourceGroup -Name $resourceGroupName -Location $resourceGroupLocation
96 | }
97 | else{
98 | Write-Host "Using existing resource group '$resourceGroupName'";
99 | }
100 |
101 | # Start the deployment
102 | Write-Host "Starting deployment...";
103 | if(Test-Path $parametersFilePath) {
104 | New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath -TemplateParameterFile $parametersFilePath;
105 | } else {
106 | New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath;
107 | }
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 |
5 | # -e: immediately exit if any command has a non-zero exit status
6 | # -o: prevents errors in a pipeline from being masked
7 | # IFS new value is less likely to cause confusing bugs when looping arrays or arguments (e.g. $@)
8 |
9 | usage() { echo "Usage: $0 -i -g -n -l " 1>&2; exit 1; }
10 |
11 | declare subscriptionId=""
12 | declare resourceGroupName=""
13 | declare deploymentName=""
14 | declare resourceGroupLocation=""
15 |
16 | # Initialize parameters specified from command line
17 | while getopts ":i:g:n:l:" arg; do
18 | case "${arg}" in
19 | i)
20 | subscriptionId=${OPTARG}
21 | ;;
22 | g)
23 | resourceGroupName=${OPTARG}
24 | ;;
25 | n)
26 | deploymentName=${OPTARG}
27 | ;;
28 | l)
29 | resourceGroupLocation=${OPTARG}
30 | ;;
31 | esac
32 | done
33 | shift $((OPTIND-1))
34 |
35 | #Prompt for parameters is some required parameters are missing
36 | if [[ -z "$subscriptionId" ]]; then
37 | echo "Your subscription ID can be looked up with the CLI using: az account show --out json "
38 | echo "Enter your subscription ID:"
39 | read subscriptionId
40 | [[ "${subscriptionId:?}" ]]
41 | fi
42 |
43 | if [[ -z "$resourceGroupName" ]]; then
44 | echo "This script will look for an existing resource group, otherwise a new one will be created "
45 | echo "You can create new resource groups with the CLI using: az group create "
46 | echo "Enter a resource group name"
47 | read resourceGroupName
48 | [[ "${resourceGroupName:?}" ]]
49 | fi
50 |
51 | if [[ -z "$deploymentName" ]]; then
52 | echo "Enter a name for this deployment:"
53 | read deploymentName
54 | fi
55 |
56 | if [[ -z "$resourceGroupLocation" ]]; then
57 | echo "If creating a *new* resource group, you need to set a location "
58 | echo "You can lookup locations with the CLI using: az account list-locations "
59 |
60 | echo "Enter resource group location:"
61 | read resourceGroupLocation
62 | fi
63 |
64 | #templateFile Path - template file to be used
65 | templateFilePath="template.json"
66 |
67 | if [ ! -f "$templateFilePath" ]; then
68 | echo "$templateFilePath not found"
69 | exit 1
70 | fi
71 |
72 | #parameter file path
73 | parametersFilePath="parameters.json"
74 |
75 | if [ ! -f "$parametersFilePath" ]; then
76 | echo "$parametersFilePath not found"
77 | exit 1
78 | fi
79 |
80 | if [ -z "$subscriptionId" ] || [ -z "$resourceGroupName" ] || [ -z "$deploymentName" ]; then
81 | echo "Either one of subscriptionId, resourceGroupName, deploymentName is empty"
82 | usage
83 | fi
84 |
85 | #login to azure using your credentials
86 | az account show 1> /dev/null
87 |
88 | if [ $? != 0 ];
89 | then
90 | az login
91 | fi
92 |
93 | #set the default subscription id
94 | az account set --subscription $subscriptionId
95 |
96 | set +e
97 |
98 | #Check for existing RG
99 | az group show --name $resourceGroupName 1> /dev/null
100 |
101 | if [ $? != 0 ]; then
102 | echo "Resource group with name" $resourceGroupName "could not be found. Creating new resource group.."
103 | set -e
104 | (
105 | set -x
106 | az group create --name $resourceGroupName --location $resourceGroupLocation 1> /dev/null
107 | )
108 | else
109 | echo "Using existing resource group..."
110 | fi
111 |
112 | #Start deployment
113 | echo "Starting deployment..."
114 | (
115 | set -x
116 | az group deployment create --name "$deploymentName" --resource-group "$resourceGroupName" --template-file "$templateFilePath" --parameters "@${parametersFilePath}"
117 | )
118 |
119 | if [ $? == 0 ];
120 | then
121 | echo "Template has been successfully deployed"
122 | fi
123 |
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/deployer.rb:
--------------------------------------------------------------------------------
1 | require 'azure_mgmt_resources'
2 |
3 | class Deployer
4 |
5 | # Initialize the deployer class with subscription, resource group and resource group location. The class will raise an
6 | # ArgumentError if there are empty values for Tenant Id, Client Id or Client Secret environment variables.
7 | #
8 | # @param [String] subscription_id the subscription to deploy the template
9 | # @param [String] resource_group the resource group to create or update and then deploy the template
10 | # @param [String] resource_group_location the location of the resource group
11 | def initialize(subscription_id, resource_group, resource_group_location)
12 | raise ArgumentError.new("Missing template file 'template.json' in current directory.") unless File.exist?('template.json')
13 | raise ArgumentError.new("Missing parameters file 'parameters.json' in current directory.") unless File.exist?('parameters.json')
14 | @resource_group = resource_group
15 | @subscription_id = subscription_id
16 | @resource_group_location = resource_group_location
17 | provider = MsRestAzure::ApplicationTokenProvider.new(
18 | ENV['AZURE_TENANT_ID'],
19 | ENV['AZURE_CLIENT_ID'],
20 | ENV['AZURE_CLIENT_SECRET'])
21 | credentials = MsRest::TokenCredentials.new(provider)
22 | @client = Azure::ARM::Resources::ResourceManagementClient.new(credentials)
23 | @client.subscription_id = @subscription_id
24 | end
25 |
26 | # Deploy the template to a resource group
27 | def deploy
28 | # ensure the resource group is created
29 | params = Azure::ARM::Resources::Models::ResourceGroup.new.tap do |rg|
30 | rg.location = @resource_group_location
31 | end
32 | @client.resource_groups.create_or_update(@resource_group, params).value!
33 |
34 | # build the deployment from a json file template from parameters
35 | template = File.read(File.expand_path(File.join(__dir__, 'template.json')))
36 | deployment = Azure::ARM::Resources::Models::Deployment.new
37 | deployment.properties = Azure::ARM::Resources::Models::DeploymentProperties.new
38 | deployment.properties.template = JSON.parse(template)
39 | deployment.properties.mode = Azure::ARM::Resources::Models::DeploymentMode::Incremental
40 |
41 | # build the deployment template parameters from Hash to {key: {value: value}} format
42 | deploy_params = File.read(File.expand_path(File.join(__dir__, 'parameters.json')))
43 | deployment.properties.parameters = JSON.parse(deploy_params)["parameters"]
44 |
45 | # put the deployment to the resource group
46 | @client.deployments.create_or_update(@resource_group, 'azure-sample', deployment)
47 | end
48 | end
49 |
50 | # Get user inputs and execute the script
51 | if(ARGV.empty?)
52 | puts "Please specify subscriptionId resourceGroupName resourceGroupLocation as command line arguments"
53 | exit
54 | end
55 |
56 | subscription_id = ARGV[0] # Azure Subscription Id
57 | resource_group = ARGV[1] # The resource group for deployment
58 | resource_group_location = ARGV[2] # The resource group location
59 |
60 | msg = "\nInitializing the Deployer class with subscription id: #{subscription_id}, resource group: #{resource_group}"
61 | msg += "\nand resource group location: #{resource_group_location}...\n\n"
62 | puts msg
63 |
64 | # Initialize the deployer class
65 | deployer = Deployer.new(subscription_id, resource_group, resource_group_location)
66 |
67 | puts "Beginning the deployment... \n\n"
68 | # Deploy the template
69 | deployment = deployer.deploy
70 |
71 | puts "Done deploying!!"
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "resourceName": {
6 | "value": "microservice-cluster"
7 | },
8 | "location": {
9 | "value": ""
10 | },
11 | "dnsPrefix": {
12 | "value": "microservice"
13 | },
14 | "agentCount": {
15 | "value": 1
16 | },
17 | "agentVMSize": {
18 | "value": "Standard_B2ms"
19 | },
20 | "servicePrincipalClientId": {
21 | "value": null
22 | },
23 | "servicePrincipalClientSecret": {
24 | "value": null
25 | },
26 | "kubernetesVersion": {
27 | "value": "1.11.2"
28 | },
29 | "workspaceName": {
30 | "value": ""
31 | },
32 | "omsWorkspaceId": {
33 | "value": ""
34 | },
35 | "workspaceRegion": {
36 | "value": ""
37 | },
38 | "enableHttpApplicationRouting": {
39 | "value": true
40 | },
41 | "networkPlugin": {
42 | "value": "kubenet"
43 | },
44 | "enableRBAC": {
45 | "value": false
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/azure/kubernetes-cluster-template/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "resourceName": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "The name of the Managed Cluster resource."
9 | }
10 | },
11 | "location": {
12 | "type": "string",
13 | "metadata": {
14 | "description": "The location of AKS resource."
15 | }
16 | },
17 | "dnsPrefix": {
18 | "type": "string",
19 | "metadata": {
20 | "description": "Optional DNS prefix to use with hosted Kubernetes API server FQDN."
21 | }
22 | },
23 | "osDiskSizeGB": {
24 | "type": "int",
25 | "defaultValue": 0,
26 | "metadata": {
27 | "description": "Disk size (in GB) to provision for each of the agent pool nodes. This value ranges from 0 to 1023. Specifying 0 will apply the default disk size for that agentVMSize."
28 | },
29 | "minValue": 0,
30 | "maxValue": 1023
31 | },
32 | "agentCount": {
33 | "type": "int",
34 | "defaultValue": 3,
35 | "metadata": {
36 | "description": "The number of agent nodes for the cluster."
37 | },
38 | "minValue": 1,
39 | "maxValue": 50
40 | },
41 | "agentVMSize": {
42 | "type": "string",
43 | "defaultValue": "Standard_D2_v2",
44 | "metadata": {
45 | "description": "The size of the Virtual Machine."
46 | }
47 | },
48 | "servicePrincipalClientId": {
49 | "metadata": {
50 | "description": "Client ID (used by cloudprovider)"
51 | },
52 | "type": "securestring"
53 | },
54 | "servicePrincipalClientSecret": {
55 | "metadata": {
56 | "description": "The Service Principal Client Secret."
57 | },
58 | "type": "securestring"
59 | },
60 | "osType": {
61 | "type": "string",
62 | "defaultValue": "Linux",
63 | "allowedValues": [
64 | "Linux"
65 | ],
66 | "metadata": {
67 | "description": "The type of operating system."
68 | }
69 | },
70 | "kubernetesVersion": {
71 | "type": "string",
72 | "defaultValue": "1.7.7",
73 | "metadata": {
74 | "description": "The version of Kubernetes."
75 | }
76 | },
77 | "enableOmsAgent": {
78 | "type": "bool",
79 | "defaultValue": true,
80 | "metadata": {
81 | "description": "boolean flag to turn on and off of omsagent addon"
82 | }
83 | },
84 | "workspaceRegion": {
85 | "type": "string",
86 | "defaultValue": "East US",
87 | "metadata": {
88 | "description": "Specify the region for your OMS workspace"
89 | }
90 | },
91 | "workspaceName": {
92 | "type": "string",
93 | "metadata": {
94 | "description": "Specify the name of the OMS workspace"
95 | }
96 | },
97 | "omsWorkspaceId": {
98 | "type": "string",
99 | "metadata": {
100 | "description": "Specify the resource id of the OMS workspace"
101 | }
102 | },
103 | "omsSku": {
104 | "type": "string",
105 | "defaultValue": "standalone",
106 | "allowedValues": [
107 | "free",
108 | "standalone",
109 | "pernode"
110 | ],
111 | "metadata": {
112 | "description": "Select the SKU for your workspace"
113 | }
114 | },
115 | "enableHttpApplicationRouting": {
116 | "type": "bool",
117 | "defaultValue": true,
118 | "metadata": {
119 | "description": "boolean flag to turn on and off of http application routing"
120 | }
121 | },
122 | "networkPlugin": {
123 | "type": "string",
124 | "allowedValues": [
125 | "azure",
126 | "kubenet"
127 | ],
128 | "metadata": {
129 | "description": "Network plugin used for building Kubernetes network."
130 | }
131 | },
132 | "maxPods": {
133 | "type": "int",
134 | "defaultValue": 30,
135 | "metadata": {
136 | "description": "Maximum number of pods that can run on a node."
137 | }
138 | },
139 | "enableRBAC": {
140 | "type": "bool",
141 | "defaultValue": true,
142 | "metadata": {
143 | "description": "boolean flag to turn on and off of RBAC"
144 | }
145 | }
146 | },
147 | "resources": [
148 | {
149 | "apiVersion": "2018-03-31",
150 | "dependsOn": [],
151 | "type": "Microsoft.ContainerService/managedClusters",
152 | "location": "[parameters('location')]",
153 | "name": "[parameters('resourceName')]",
154 | "properties": {
155 | "kubernetesVersion": "[parameters('kubernetesVersion')]",
156 | "enableRBAC": "[parameters('enableRBAC')]",
157 | "dnsPrefix": "[parameters('dnsPrefix')]",
158 | "addonProfiles": {
159 | "httpApplicationRouting": {
160 | "enabled": "[parameters('enableHttpApplicationRouting')]"
161 | },
162 | "omsagent": {
163 | "enabled": "[parameters('enableOmsAgent')]",
164 | "config": {
165 | "logAnalyticsWorkspaceResourceID": "[parameters('omsWorkspaceId')]"
166 | }
167 | }
168 | },
169 | "agentPoolProfiles": [
170 | {
171 | "name": "agentpool",
172 | "osDiskSizeGB": "[parameters('osDiskSizeGB')]",
173 | "count": "[parameters('agentCount')]",
174 | "vmSize": "[parameters('agentVMSize')]",
175 | "osType": "[parameters('osType')]",
176 | "storageProfile": "ManagedDisks"
177 | }
178 | ],
179 | "servicePrincipalProfile": {
180 | "ClientId": "[parameters('servicePrincipalClientId')]",
181 | "Secret": "[parameters('servicePrincipalClientSecret')]"
182 | },
183 | "networkProfile": {
184 | "networkPlugin": "[parameters('networkPlugin')]"
185 | }
186 | },
187 | "tags": {}
188 | },
189 | {
190 | "type": "Microsoft.Resources/deployments",
191 | "name": "SolutionDeployment",
192 | "apiVersion": "2017-05-10",
193 | "resourceGroup": "[split(parameters('omsWorkspaceId'),'/')[4]]",
194 | "subscriptionId": "[split(parameters('omsWorkspaceId'),'/')[2]]",
195 | "properties": {
196 | "mode": "Incremental",
197 | "template": {
198 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
199 | "contentVersion": "1.0.0.0",
200 | "parameters": {},
201 | "variables": {},
202 | "resources": [
203 | {
204 | "apiVersion": "2015-11-01-preview",
205 | "type": "Microsoft.OperationsManagement/solutions",
206 | "location": "[parameters('workspaceRegion')]",
207 | "name": "[concat('ContainerInsights', '(', split(parameters('omsWorkspaceId'),'/')[8], ')')]",
208 | "properties": {
209 | "workspaceResourceId": "[parameters('omsWorkspaceId')]"
210 | },
211 | "plan": {
212 | "name": "[concat('ContainerInsights', '(', split(parameters('omsWorkspaceId'),'/')[8], ')')]",
213 | "product": "[concat('OMSGallery/', 'ContainerInsights')]",
214 | "promotionCode": "",
215 | "publisher": "Microsoft"
216 | }
217 | }
218 | ]
219 | }
220 | },
221 | "dependsOn": []
222 | }
223 | ],
224 | "outputs": {
225 | "controlPlaneFQDN": {
226 | "type": "string",
227 | "value": "[reference(concat('Microsoft.ContainerService/managedClusters/', parameters('resourceName'))).fqdn]"
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/backend-api1/.env:
--------------------------------------------------------------------------------
1 | PORT=
2 | SERVICE_API2_ENDPOINT=
--------------------------------------------------------------------------------
/backend-api1/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:8-alpine
2 |
3 | LABEL version="1.0.0"
4 | ARG basedir="backend-api1"
5 | WORKDIR ${basedir}/ .
6 |
7 | # Copy package.json
8 | COPY ${basedir}/package*.json ./
9 |
10 | # Install npm packages
11 | RUN npm install --silent
12 |
13 | # Copy project files to the workdir.
14 | COPY ${basedir}/ .
15 |
16 | # Install bash inside container. Only if you need to debug app inside of the container.
17 | RUN apk update && apk add bash
18 |
19 | EXPOSE 9010
20 | CMD npm start
--------------------------------------------------------------------------------
/backend-api1/api/health.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const routes = express.Router();
3 |
4 | //
5 | // GET /api/health
6 | routes
7 | .get('/api/health', function (req, res, next) {
8 | res.type('application/json');
9 | res.status(200).send();
10 | })
11 |
12 | module.exports = routes;
13 |
--------------------------------------------------------------------------------
/backend-api1/api/message.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const parse = require('url-parse')
3 | const express = require('express');
4 | const routes = express.Router();
5 |
6 | //
7 | // GET /api/message
8 | routes
9 | .get('/api/message', function (req, res, next) {
10 | res.type('application/json');
11 |
12 | var data = {
13 | service1: {
14 | message: "This is message from service 1."
15 | },
16 | service2: {
17 | message: null
18 | },
19 | }
20 |
21 | var service2endpoint = process.env.SERVICE_API2_ENDPOINT;
22 | var url = parse(service2endpoint, true);
23 |
24 | var options = {
25 | host: url.hostname,
26 | port: url.port,
27 | path: url.pathname + '/message'
28 | }
29 |
30 | console.log("Service 1 was called. Calling Service 2 => " + JSON.stringify(options));
31 |
32 | var req = http.get(options, function(response) {
33 | var body = '';
34 | response.on('data', function(d) {
35 | body += d;
36 | });
37 | response.on('end', function() {
38 | var service2responce = JSON.parse(body);
39 | data.service2.message = service2responce.message;
40 |
41 | console.log("Message received from service 2: " + JSON.stringify(data));
42 | res.status(200).send(data);
43 | });
44 | });
45 |
46 | req.on('error', function(e) {
47 | console.log("Message received from service 2. error=" + JSON.stringify(e));
48 | res.status(500).send({"error": "Something went wrong in service 2!"});
49 | });
50 | })
51 |
52 | module.exports = routes;
53 |
--------------------------------------------------------------------------------
/backend-api1/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smilr-data-api",
3 | "version": "3.2.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.5",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
11 | "requires": {
12 | "mime-types": "~2.1.18",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "applicationinsights": {
17 | "version": "1.0.2",
18 | "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.2.tgz",
19 | "integrity": "sha1-XQF8EYWsIsgom7IiBB0l+Cx8MVk=",
20 | "requires": {
21 | "diagnostic-channel": "0.2.0",
22 | "diagnostic-channel-publishers": "0.2.1",
23 | "zone.js": "0.7.6"
24 | }
25 | },
26 | "array-flatten": {
27 | "version": "1.1.1",
28 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
29 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
30 | },
31 | "basic-auth": {
32 | "version": "2.0.0",
33 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
34 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
35 | "requires": {
36 | "safe-buffer": "5.1.1"
37 | }
38 | },
39 | "body-parser": {
40 | "version": "1.18.2",
41 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
42 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
43 | "requires": {
44 | "bytes": "3.0.0",
45 | "content-type": "~1.0.4",
46 | "debug": "2.6.9",
47 | "depd": "~1.1.1",
48 | "http-errors": "~1.6.2",
49 | "iconv-lite": "0.4.19",
50 | "on-finished": "~2.3.0",
51 | "qs": "6.5.1",
52 | "raw-body": "2.3.2",
53 | "type-is": "~1.6.15"
54 | }
55 | },
56 | "bson": {
57 | "version": "1.0.6",
58 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz",
59 | "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ=="
60 | },
61 | "buffer-shims": {
62 | "version": "1.0.0",
63 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
64 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
65 | },
66 | "bytes": {
67 | "version": "3.0.0",
68 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
69 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
70 | },
71 | "content-disposition": {
72 | "version": "0.5.2",
73 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
74 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
75 | },
76 | "content-type": {
77 | "version": "1.0.4",
78 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
79 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
80 | },
81 | "cookie": {
82 | "version": "0.3.1",
83 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
84 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
85 | },
86 | "cookie-signature": {
87 | "version": "1.0.6",
88 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
89 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
90 | },
91 | "core-util-is": {
92 | "version": "1.0.2",
93 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
94 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
95 | },
96 | "cors": {
97 | "version": "2.8.4",
98 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
99 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
100 | "requires": {
101 | "object-assign": "^4",
102 | "vary": "^1"
103 | }
104 | },
105 | "debug": {
106 | "version": "2.6.9",
107 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
108 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
109 | "requires": {
110 | "ms": "2.0.0"
111 | }
112 | },
113 | "depd": {
114 | "version": "1.1.2",
115 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
116 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
117 | },
118 | "destroy": {
119 | "version": "1.0.4",
120 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
121 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
122 | },
123 | "diagnostic-channel": {
124 | "version": "0.2.0",
125 | "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz",
126 | "integrity": "sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=",
127 | "requires": {
128 | "semver": "^5.3.0"
129 | }
130 | },
131 | "diagnostic-channel-publishers": {
132 | "version": "0.2.1",
133 | "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz",
134 | "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM="
135 | },
136 | "dotenv": {
137 | "version": "5.0.1",
138 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
139 | "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
140 | },
141 | "ee-first": {
142 | "version": "1.1.1",
143 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
144 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
145 | },
146 | "encodeurl": {
147 | "version": "1.0.2",
148 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
149 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
150 | },
151 | "es6-promise": {
152 | "version": "3.2.1",
153 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
154 | "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
155 | },
156 | "escape-html": {
157 | "version": "1.0.3",
158 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
159 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
160 | },
161 | "etag": {
162 | "version": "1.8.1",
163 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
164 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
165 | },
166 | "express": {
167 | "version": "4.16.3",
168 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
169 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
170 | "requires": {
171 | "accepts": "~1.3.5",
172 | "array-flatten": "1.1.1",
173 | "body-parser": "1.18.2",
174 | "content-disposition": "0.5.2",
175 | "content-type": "~1.0.4",
176 | "cookie": "0.3.1",
177 | "cookie-signature": "1.0.6",
178 | "debug": "2.6.9",
179 | "depd": "~1.1.2",
180 | "encodeurl": "~1.0.2",
181 | "escape-html": "~1.0.3",
182 | "etag": "~1.8.1",
183 | "finalhandler": "1.1.1",
184 | "fresh": "0.5.2",
185 | "merge-descriptors": "1.0.1",
186 | "methods": "~1.1.2",
187 | "on-finished": "~2.3.0",
188 | "parseurl": "~1.3.2",
189 | "path-to-regexp": "0.1.7",
190 | "proxy-addr": "~2.0.3",
191 | "qs": "6.5.1",
192 | "range-parser": "~1.2.0",
193 | "safe-buffer": "5.1.1",
194 | "send": "0.16.2",
195 | "serve-static": "1.13.2",
196 | "setprototypeof": "1.1.0",
197 | "statuses": "~1.4.0",
198 | "type-is": "~1.6.16",
199 | "utils-merge": "1.0.1",
200 | "vary": "~1.1.2"
201 | },
202 | "dependencies": {
203 | "statuses": {
204 | "version": "1.4.0",
205 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
206 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
207 | }
208 | }
209 | },
210 | "finalhandler": {
211 | "version": "1.1.1",
212 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
213 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
214 | "requires": {
215 | "debug": "2.6.9",
216 | "encodeurl": "~1.0.2",
217 | "escape-html": "~1.0.3",
218 | "on-finished": "~2.3.0",
219 | "parseurl": "~1.3.2",
220 | "statuses": "~1.4.0",
221 | "unpipe": "~1.0.0"
222 | },
223 | "dependencies": {
224 | "statuses": {
225 | "version": "1.4.0",
226 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
227 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
228 | }
229 | }
230 | },
231 | "forwarded": {
232 | "version": "0.1.2",
233 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
234 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
235 | },
236 | "fresh": {
237 | "version": "0.5.2",
238 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
239 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
240 | },
241 | "http-errors": {
242 | "version": "1.6.3",
243 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
244 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
245 | "requires": {
246 | "depd": "~1.1.2",
247 | "inherits": "2.0.3",
248 | "setprototypeof": "1.1.0",
249 | "statuses": ">= 1.4.0 < 2"
250 | }
251 | },
252 | "iconv-lite": {
253 | "version": "0.4.19",
254 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
255 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
256 | },
257 | "inherits": {
258 | "version": "2.0.3",
259 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
260 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
261 | },
262 | "ipaddr.js": {
263 | "version": "1.6.0",
264 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
265 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
266 | },
267 | "isarray": {
268 | "version": "1.0.0",
269 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
270 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
271 | },
272 | "jsotp": {
273 | "version": "1.0.2",
274 | "resolved": "https://registry.npmjs.org/jsotp/-/jsotp-1.0.2.tgz",
275 | "integrity": "sha1-EnfLXvstfuyc2tYygVZrr/WhsS8=",
276 | "requires": {
277 | "jssha": "^2.3.1"
278 | }
279 | },
280 | "jssha": {
281 | "version": "2.3.1",
282 | "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz",
283 | "integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po="
284 | },
285 | "media-typer": {
286 | "version": "0.3.0",
287 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
288 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
289 | },
290 | "merge-descriptors": {
291 | "version": "1.0.1",
292 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
293 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
294 | },
295 | "methods": {
296 | "version": "1.1.2",
297 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
298 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
299 | },
300 | "mime": {
301 | "version": "1.4.1",
302 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
303 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
304 | },
305 | "mime-db": {
306 | "version": "1.33.0",
307 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
308 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
309 | },
310 | "mime-types": {
311 | "version": "2.1.18",
312 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
313 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
314 | "requires": {
315 | "mime-db": "~1.33.0"
316 | }
317 | },
318 | "mongodb": {
319 | "version": "2.2.35",
320 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.35.tgz",
321 | "integrity": "sha512-3HGLucDg/8EeYMin3k+nFWChTA85hcYDCw1lPsWR6yV9A6RgKb24BkLiZ9ySZR+S0nfBjWoIUS7cyV6ceGx5Gg==",
322 | "requires": {
323 | "es6-promise": "3.2.1",
324 | "mongodb-core": "2.1.19",
325 | "readable-stream": "2.2.7"
326 | }
327 | },
328 | "mongodb-core": {
329 | "version": "2.1.19",
330 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.19.tgz",
331 | "integrity": "sha512-Jt4AtWUkpuW03kRdYGxga4O65O1UHlFfvvInslEfLlGi+zDMxbBe3J2NVmN9qPJ957Mn6Iz0UpMtV80cmxCVxw==",
332 | "requires": {
333 | "bson": "~1.0.4",
334 | "require_optional": "~1.0.0"
335 | }
336 | },
337 | "morgan": {
338 | "version": "1.9.0",
339 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
340 | "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
341 | "requires": {
342 | "basic-auth": "~2.0.0",
343 | "debug": "2.6.9",
344 | "depd": "~1.1.1",
345 | "on-finished": "~2.3.0",
346 | "on-headers": "~1.0.1"
347 | }
348 | },
349 | "ms": {
350 | "version": "2.0.0",
351 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
352 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
353 | },
354 | "negotiator": {
355 | "version": "0.6.1",
356 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
357 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
358 | },
359 | "object-assign": {
360 | "version": "4.1.1",
361 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
362 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
363 | },
364 | "on-finished": {
365 | "version": "2.3.0",
366 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
367 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
368 | "requires": {
369 | "ee-first": "1.1.1"
370 | }
371 | },
372 | "on-headers": {
373 | "version": "1.0.1",
374 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
375 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
376 | },
377 | "parseurl": {
378 | "version": "1.3.2",
379 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
380 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
381 | },
382 | "path-to-regexp": {
383 | "version": "0.1.7",
384 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
385 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
386 | },
387 | "process-nextick-args": {
388 | "version": "1.0.7",
389 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
390 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
391 | },
392 | "proxy-addr": {
393 | "version": "2.0.3",
394 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
395 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
396 | "requires": {
397 | "forwarded": "~0.1.2",
398 | "ipaddr.js": "1.6.0"
399 | }
400 | },
401 | "qs": {
402 | "version": "6.5.1",
403 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
404 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
405 | },
406 | "range-parser": {
407 | "version": "1.2.0",
408 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
409 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
410 | },
411 | "raw-body": {
412 | "version": "2.3.2",
413 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
414 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
415 | "requires": {
416 | "bytes": "3.0.0",
417 | "http-errors": "1.6.2",
418 | "iconv-lite": "0.4.19",
419 | "unpipe": "1.0.0"
420 | },
421 | "dependencies": {
422 | "depd": {
423 | "version": "1.1.1",
424 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
425 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
426 | },
427 | "http-errors": {
428 | "version": "1.6.2",
429 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
430 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
431 | "requires": {
432 | "depd": "1.1.1",
433 | "inherits": "2.0.3",
434 | "setprototypeof": "1.0.3",
435 | "statuses": ">= 1.3.1 < 2"
436 | }
437 | },
438 | "setprototypeof": {
439 | "version": "1.0.3",
440 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
441 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
442 | }
443 | }
444 | },
445 | "readable-stream": {
446 | "version": "2.2.7",
447 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
448 | "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
449 | "requires": {
450 | "buffer-shims": "~1.0.0",
451 | "core-util-is": "~1.0.0",
452 | "inherits": "~2.0.1",
453 | "isarray": "~1.0.0",
454 | "process-nextick-args": "~1.0.6",
455 | "string_decoder": "~1.0.0",
456 | "util-deprecate": "~1.0.1"
457 | }
458 | },
459 | "require_optional": {
460 | "version": "1.0.1",
461 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
462 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
463 | "requires": {
464 | "resolve-from": "^2.0.0",
465 | "semver": "^5.1.0"
466 | }
467 | },
468 | "resolve-from": {
469 | "version": "2.0.0",
470 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
471 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
472 | },
473 | "safe-buffer": {
474 | "version": "5.1.1",
475 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
476 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
477 | },
478 | "semver": {
479 | "version": "5.5.0",
480 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
481 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
482 | },
483 | "send": {
484 | "version": "0.16.2",
485 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
486 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
487 | "requires": {
488 | "debug": "2.6.9",
489 | "depd": "~1.1.2",
490 | "destroy": "~1.0.4",
491 | "encodeurl": "~1.0.2",
492 | "escape-html": "~1.0.3",
493 | "etag": "~1.8.1",
494 | "fresh": "0.5.2",
495 | "http-errors": "~1.6.2",
496 | "mime": "1.4.1",
497 | "ms": "2.0.0",
498 | "on-finished": "~2.3.0",
499 | "range-parser": "~1.2.0",
500 | "statuses": "~1.4.0"
501 | },
502 | "dependencies": {
503 | "statuses": {
504 | "version": "1.4.0",
505 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
506 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
507 | }
508 | }
509 | },
510 | "serve-static": {
511 | "version": "1.13.2",
512 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
513 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
514 | "requires": {
515 | "encodeurl": "~1.0.2",
516 | "escape-html": "~1.0.3",
517 | "parseurl": "~1.3.2",
518 | "send": "0.16.2"
519 | }
520 | },
521 | "setprototypeof": {
522 | "version": "1.1.0",
523 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
524 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
525 | },
526 | "statuses": {
527 | "version": "1.5.0",
528 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
529 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
530 | },
531 | "string_decoder": {
532 | "version": "1.0.3",
533 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
534 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
535 | "requires": {
536 | "safe-buffer": "~5.1.0"
537 | }
538 | },
539 | "swagger-ui-express": {
540 | "version": "3.0.9",
541 | "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-3.0.9.tgz",
542 | "integrity": "sha1-6SqsKdtjjEMX4thy7yFfMwppP1c="
543 | },
544 | "type-is": {
545 | "version": "1.6.16",
546 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
547 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
548 | "requires": {
549 | "media-typer": "0.3.0",
550 | "mime-types": "~2.1.18"
551 | }
552 | },
553 | "unpipe": {
554 | "version": "1.0.0",
555 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
556 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
557 | },
558 | "util-deprecate": {
559 | "version": "1.0.2",
560 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
561 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
562 | },
563 | "utils-merge": {
564 | "version": "1.0.1",
565 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
566 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
567 | },
568 | "vary": {
569 | "version": "1.1.2",
570 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
571 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
572 | },
573 | "zone.js": {
574 | "version": "0.7.6",
575 | "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz",
576 | "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk="
577 | }
578 | }
579 | }
580 |
--------------------------------------------------------------------------------
/backend-api1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend-api1",
3 | "version": "1.0.0",
4 | "main": "server.js",
5 | "engines": {
6 | "node": "^8.11.1"
7 | },
8 | "description": "",
9 | "scripts": {
10 | "start": "node server.js"
11 | },
12 | "license": "MIT",
13 | "repository": "https://github.com/spylkkanen/kubernetes-microservices",
14 | "dependencies": {
15 | "body-parser": "^1.18.2",
16 | "cors": "^2.8.4",
17 | "dotenv": "^5.0.0",
18 | "express": "^4.16.2",
19 | "url-parse": "^1.4.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend-api1/server.js:
--------------------------------------------------------------------------------
1 | // Load .env file if it exist.
2 | require('dotenv').config()
3 |
4 | // Load in modules.
5 | const express = require('express');
6 | const app = express();
7 | const bodyParser = require('body-parser')
8 | const cors = require('cors');
9 |
10 | // Allow all CORS.
11 | app.use(cors());
12 |
13 | // Parse any JSON.
14 | app.use(bodyParser.json());
15 |
16 | // Routing to controllers
17 | app.use(require('./api/message'));
18 | app.use(require('./api/health'));
19 |
20 | // Catch all unhandled requests.
21 | app.use('*', function (req, res, next) {
22 | res.sendStatus(400);
23 | })
24 |
25 | // Start the Express server
26 | var port = process.env.PORT || 19010;
27 | var server = app.listen(port, function () {
28 | var port = server.address().port;
29 | console.log(`### Server listening on ${server.address().port}`);
30 | });
--------------------------------------------------------------------------------
/backend-api2/.env:
--------------------------------------------------------------------------------
1 | PORT=
--------------------------------------------------------------------------------
/backend-api2/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:8-alpine
2 |
3 | LABEL version="1.0.0"
4 | ARG basedir="backend-api2"
5 | WORKDIR ${basedir}/ .
6 |
7 | # Copy package.json
8 | COPY ${basedir}/package*.json ./
9 |
10 | # Install npm packages
11 | RUN npm install --silent
12 |
13 | # Copy project files to the workdir.
14 | COPY ${basedir}/ .
15 |
16 | # Install bash inside container. Only if you need to debug app inside of the container.
17 | RUN apk update && apk add bash
18 |
19 | EXPOSE 9020
20 | CMD npm start
--------------------------------------------------------------------------------
/backend-api2/api/health.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const routes = express.Router();
3 |
4 | //
5 | // GET /api/health
6 | routes
7 | .get('/api/health', function (req, res, next) {
8 | res.type('application/json');
9 | res.status(200).send();
10 | })
11 |
12 | module.exports = routes;
13 |
--------------------------------------------------------------------------------
/backend-api2/api/message.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const routes = express.Router();
3 |
4 | //
5 | // GET /api/message
6 | routes
7 | .get('/api/message', function (req, res, next) {
8 | res.type('application/json');
9 |
10 | console.log("Service 2 was called.");
11 |
12 | var data = {
13 | message: "This is message from service 2."
14 | }
15 |
16 | res.status(200).send(data)
17 | })
18 |
19 | module.exports = routes;
20 |
--------------------------------------------------------------------------------
/backend-api2/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smilr-data-api",
3 | "version": "3.2.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.5",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
11 | "requires": {
12 | "mime-types": "~2.1.18",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "applicationinsights": {
17 | "version": "1.0.2",
18 | "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.2.tgz",
19 | "integrity": "sha1-XQF8EYWsIsgom7IiBB0l+Cx8MVk=",
20 | "requires": {
21 | "diagnostic-channel": "0.2.0",
22 | "diagnostic-channel-publishers": "0.2.1",
23 | "zone.js": "0.7.6"
24 | }
25 | },
26 | "array-flatten": {
27 | "version": "1.1.1",
28 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
29 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
30 | },
31 | "basic-auth": {
32 | "version": "2.0.0",
33 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
34 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
35 | "requires": {
36 | "safe-buffer": "5.1.1"
37 | }
38 | },
39 | "body-parser": {
40 | "version": "1.18.2",
41 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
42 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
43 | "requires": {
44 | "bytes": "3.0.0",
45 | "content-type": "~1.0.4",
46 | "debug": "2.6.9",
47 | "depd": "~1.1.1",
48 | "http-errors": "~1.6.2",
49 | "iconv-lite": "0.4.19",
50 | "on-finished": "~2.3.0",
51 | "qs": "6.5.1",
52 | "raw-body": "2.3.2",
53 | "type-is": "~1.6.15"
54 | }
55 | },
56 | "bson": {
57 | "version": "1.0.6",
58 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz",
59 | "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ=="
60 | },
61 | "buffer-shims": {
62 | "version": "1.0.0",
63 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
64 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
65 | },
66 | "bytes": {
67 | "version": "3.0.0",
68 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
69 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
70 | },
71 | "content-disposition": {
72 | "version": "0.5.2",
73 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
74 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
75 | },
76 | "content-type": {
77 | "version": "1.0.4",
78 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
79 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
80 | },
81 | "cookie": {
82 | "version": "0.3.1",
83 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
84 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
85 | },
86 | "cookie-signature": {
87 | "version": "1.0.6",
88 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
89 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
90 | },
91 | "core-util-is": {
92 | "version": "1.0.2",
93 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
94 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
95 | },
96 | "cors": {
97 | "version": "2.8.4",
98 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
99 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
100 | "requires": {
101 | "object-assign": "^4",
102 | "vary": "^1"
103 | }
104 | },
105 | "debug": {
106 | "version": "2.6.9",
107 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
108 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
109 | "requires": {
110 | "ms": "2.0.0"
111 | }
112 | },
113 | "depd": {
114 | "version": "1.1.2",
115 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
116 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
117 | },
118 | "destroy": {
119 | "version": "1.0.4",
120 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
121 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
122 | },
123 | "diagnostic-channel": {
124 | "version": "0.2.0",
125 | "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz",
126 | "integrity": "sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=",
127 | "requires": {
128 | "semver": "^5.3.0"
129 | }
130 | },
131 | "diagnostic-channel-publishers": {
132 | "version": "0.2.1",
133 | "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz",
134 | "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM="
135 | },
136 | "dotenv": {
137 | "version": "5.0.1",
138 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
139 | "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
140 | },
141 | "ee-first": {
142 | "version": "1.1.1",
143 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
144 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
145 | },
146 | "encodeurl": {
147 | "version": "1.0.2",
148 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
149 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
150 | },
151 | "es6-promise": {
152 | "version": "3.2.1",
153 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
154 | "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
155 | },
156 | "escape-html": {
157 | "version": "1.0.3",
158 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
159 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
160 | },
161 | "etag": {
162 | "version": "1.8.1",
163 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
164 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
165 | },
166 | "express": {
167 | "version": "4.16.3",
168 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
169 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
170 | "requires": {
171 | "accepts": "~1.3.5",
172 | "array-flatten": "1.1.1",
173 | "body-parser": "1.18.2",
174 | "content-disposition": "0.5.2",
175 | "content-type": "~1.0.4",
176 | "cookie": "0.3.1",
177 | "cookie-signature": "1.0.6",
178 | "debug": "2.6.9",
179 | "depd": "~1.1.2",
180 | "encodeurl": "~1.0.2",
181 | "escape-html": "~1.0.3",
182 | "etag": "~1.8.1",
183 | "finalhandler": "1.1.1",
184 | "fresh": "0.5.2",
185 | "merge-descriptors": "1.0.1",
186 | "methods": "~1.1.2",
187 | "on-finished": "~2.3.0",
188 | "parseurl": "~1.3.2",
189 | "path-to-regexp": "0.1.7",
190 | "proxy-addr": "~2.0.3",
191 | "qs": "6.5.1",
192 | "range-parser": "~1.2.0",
193 | "safe-buffer": "5.1.1",
194 | "send": "0.16.2",
195 | "serve-static": "1.13.2",
196 | "setprototypeof": "1.1.0",
197 | "statuses": "~1.4.0",
198 | "type-is": "~1.6.16",
199 | "utils-merge": "1.0.1",
200 | "vary": "~1.1.2"
201 | },
202 | "dependencies": {
203 | "statuses": {
204 | "version": "1.4.0",
205 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
206 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
207 | }
208 | }
209 | },
210 | "finalhandler": {
211 | "version": "1.1.1",
212 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
213 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
214 | "requires": {
215 | "debug": "2.6.9",
216 | "encodeurl": "~1.0.2",
217 | "escape-html": "~1.0.3",
218 | "on-finished": "~2.3.0",
219 | "parseurl": "~1.3.2",
220 | "statuses": "~1.4.0",
221 | "unpipe": "~1.0.0"
222 | },
223 | "dependencies": {
224 | "statuses": {
225 | "version": "1.4.0",
226 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
227 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
228 | }
229 | }
230 | },
231 | "forwarded": {
232 | "version": "0.1.2",
233 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
234 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
235 | },
236 | "fresh": {
237 | "version": "0.5.2",
238 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
239 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
240 | },
241 | "http-errors": {
242 | "version": "1.6.3",
243 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
244 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
245 | "requires": {
246 | "depd": "~1.1.2",
247 | "inherits": "2.0.3",
248 | "setprototypeof": "1.1.0",
249 | "statuses": ">= 1.4.0 < 2"
250 | }
251 | },
252 | "iconv-lite": {
253 | "version": "0.4.19",
254 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
255 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
256 | },
257 | "inherits": {
258 | "version": "2.0.3",
259 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
260 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
261 | },
262 | "ipaddr.js": {
263 | "version": "1.6.0",
264 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
265 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
266 | },
267 | "isarray": {
268 | "version": "1.0.0",
269 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
270 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
271 | },
272 | "jsotp": {
273 | "version": "1.0.2",
274 | "resolved": "https://registry.npmjs.org/jsotp/-/jsotp-1.0.2.tgz",
275 | "integrity": "sha1-EnfLXvstfuyc2tYygVZrr/WhsS8=",
276 | "requires": {
277 | "jssha": "^2.3.1"
278 | }
279 | },
280 | "jssha": {
281 | "version": "2.3.1",
282 | "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz",
283 | "integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po="
284 | },
285 | "media-typer": {
286 | "version": "0.3.0",
287 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
288 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
289 | },
290 | "merge-descriptors": {
291 | "version": "1.0.1",
292 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
293 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
294 | },
295 | "methods": {
296 | "version": "1.1.2",
297 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
298 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
299 | },
300 | "mime": {
301 | "version": "1.4.1",
302 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
303 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
304 | },
305 | "mime-db": {
306 | "version": "1.33.0",
307 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
308 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
309 | },
310 | "mime-types": {
311 | "version": "2.1.18",
312 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
313 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
314 | "requires": {
315 | "mime-db": "~1.33.0"
316 | }
317 | },
318 | "mongodb": {
319 | "version": "2.2.35",
320 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.35.tgz",
321 | "integrity": "sha512-3HGLucDg/8EeYMin3k+nFWChTA85hcYDCw1lPsWR6yV9A6RgKb24BkLiZ9ySZR+S0nfBjWoIUS7cyV6ceGx5Gg==",
322 | "requires": {
323 | "es6-promise": "3.2.1",
324 | "mongodb-core": "2.1.19",
325 | "readable-stream": "2.2.7"
326 | }
327 | },
328 | "mongodb-core": {
329 | "version": "2.1.19",
330 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.19.tgz",
331 | "integrity": "sha512-Jt4AtWUkpuW03kRdYGxga4O65O1UHlFfvvInslEfLlGi+zDMxbBe3J2NVmN9qPJ957Mn6Iz0UpMtV80cmxCVxw==",
332 | "requires": {
333 | "bson": "~1.0.4",
334 | "require_optional": "~1.0.0"
335 | }
336 | },
337 | "morgan": {
338 | "version": "1.9.0",
339 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
340 | "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
341 | "requires": {
342 | "basic-auth": "~2.0.0",
343 | "debug": "2.6.9",
344 | "depd": "~1.1.1",
345 | "on-finished": "~2.3.0",
346 | "on-headers": "~1.0.1"
347 | }
348 | },
349 | "ms": {
350 | "version": "2.0.0",
351 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
352 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
353 | },
354 | "negotiator": {
355 | "version": "0.6.1",
356 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
357 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
358 | },
359 | "object-assign": {
360 | "version": "4.1.1",
361 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
362 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
363 | },
364 | "on-finished": {
365 | "version": "2.3.0",
366 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
367 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
368 | "requires": {
369 | "ee-first": "1.1.1"
370 | }
371 | },
372 | "on-headers": {
373 | "version": "1.0.1",
374 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
375 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
376 | },
377 | "parseurl": {
378 | "version": "1.3.2",
379 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
380 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
381 | },
382 | "path-to-regexp": {
383 | "version": "0.1.7",
384 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
385 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
386 | },
387 | "process-nextick-args": {
388 | "version": "1.0.7",
389 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
390 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
391 | },
392 | "proxy-addr": {
393 | "version": "2.0.3",
394 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
395 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
396 | "requires": {
397 | "forwarded": "~0.1.2",
398 | "ipaddr.js": "1.6.0"
399 | }
400 | },
401 | "qs": {
402 | "version": "6.5.1",
403 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
404 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
405 | },
406 | "range-parser": {
407 | "version": "1.2.0",
408 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
409 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
410 | },
411 | "raw-body": {
412 | "version": "2.3.2",
413 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
414 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
415 | "requires": {
416 | "bytes": "3.0.0",
417 | "http-errors": "1.6.2",
418 | "iconv-lite": "0.4.19",
419 | "unpipe": "1.0.0"
420 | },
421 | "dependencies": {
422 | "depd": {
423 | "version": "1.1.1",
424 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
425 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
426 | },
427 | "http-errors": {
428 | "version": "1.6.2",
429 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
430 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
431 | "requires": {
432 | "depd": "1.1.1",
433 | "inherits": "2.0.3",
434 | "setprototypeof": "1.0.3",
435 | "statuses": ">= 1.3.1 < 2"
436 | }
437 | },
438 | "setprototypeof": {
439 | "version": "1.0.3",
440 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
441 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
442 | }
443 | }
444 | },
445 | "readable-stream": {
446 | "version": "2.2.7",
447 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
448 | "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
449 | "requires": {
450 | "buffer-shims": "~1.0.0",
451 | "core-util-is": "~1.0.0",
452 | "inherits": "~2.0.1",
453 | "isarray": "~1.0.0",
454 | "process-nextick-args": "~1.0.6",
455 | "string_decoder": "~1.0.0",
456 | "util-deprecate": "~1.0.1"
457 | }
458 | },
459 | "require_optional": {
460 | "version": "1.0.1",
461 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
462 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
463 | "requires": {
464 | "resolve-from": "^2.0.0",
465 | "semver": "^5.1.0"
466 | }
467 | },
468 | "resolve-from": {
469 | "version": "2.0.0",
470 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
471 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
472 | },
473 | "safe-buffer": {
474 | "version": "5.1.1",
475 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
476 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
477 | },
478 | "semver": {
479 | "version": "5.5.0",
480 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
481 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
482 | },
483 | "send": {
484 | "version": "0.16.2",
485 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
486 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
487 | "requires": {
488 | "debug": "2.6.9",
489 | "depd": "~1.1.2",
490 | "destroy": "~1.0.4",
491 | "encodeurl": "~1.0.2",
492 | "escape-html": "~1.0.3",
493 | "etag": "~1.8.1",
494 | "fresh": "0.5.2",
495 | "http-errors": "~1.6.2",
496 | "mime": "1.4.1",
497 | "ms": "2.0.0",
498 | "on-finished": "~2.3.0",
499 | "range-parser": "~1.2.0",
500 | "statuses": "~1.4.0"
501 | },
502 | "dependencies": {
503 | "statuses": {
504 | "version": "1.4.0",
505 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
506 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
507 | }
508 | }
509 | },
510 | "serve-static": {
511 | "version": "1.13.2",
512 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
513 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
514 | "requires": {
515 | "encodeurl": "~1.0.2",
516 | "escape-html": "~1.0.3",
517 | "parseurl": "~1.3.2",
518 | "send": "0.16.2"
519 | }
520 | },
521 | "setprototypeof": {
522 | "version": "1.1.0",
523 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
524 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
525 | },
526 | "statuses": {
527 | "version": "1.5.0",
528 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
529 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
530 | },
531 | "string_decoder": {
532 | "version": "1.0.3",
533 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
534 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
535 | "requires": {
536 | "safe-buffer": "~5.1.0"
537 | }
538 | },
539 | "swagger-ui-express": {
540 | "version": "3.0.9",
541 | "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-3.0.9.tgz",
542 | "integrity": "sha1-6SqsKdtjjEMX4thy7yFfMwppP1c="
543 | },
544 | "type-is": {
545 | "version": "1.6.16",
546 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
547 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
548 | "requires": {
549 | "media-typer": "0.3.0",
550 | "mime-types": "~2.1.18"
551 | }
552 | },
553 | "unpipe": {
554 | "version": "1.0.0",
555 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
556 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
557 | },
558 | "util-deprecate": {
559 | "version": "1.0.2",
560 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
561 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
562 | },
563 | "utils-merge": {
564 | "version": "1.0.1",
565 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
566 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
567 | },
568 | "vary": {
569 | "version": "1.1.2",
570 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
571 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
572 | },
573 | "zone.js": {
574 | "version": "0.7.6",
575 | "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz",
576 | "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk="
577 | }
578 | }
579 | }
580 |
--------------------------------------------------------------------------------
/backend-api2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend-api2",
3 | "version": "1.0.0",
4 | "main": "server.js",
5 | "engines": {
6 | "node": "^8.11.1"
7 | },
8 | "description": "",
9 | "scripts": {
10 | "start": "node server.js"
11 | },
12 | "license": "MIT",
13 | "repository": "https://github.com/spylkkanen/kubernetes-microservices",
14 | "dependencies": {
15 | "body-parser": "^1.18.2",
16 | "cors": "^2.8.4",
17 | "dotenv": "^5.0.0",
18 | "express": "^4.16.2"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend-api2/server.js:
--------------------------------------------------------------------------------
1 | // Load .env file if it exist.
2 | require('dotenv').config()
3 |
4 | // Load in modules.
5 | const express = require('express');
6 | const app = express();
7 | const bodyParser = require('body-parser')
8 | const cors = require('cors');
9 |
10 | // Allow all CORS.
11 | app.use(cors());
12 |
13 | // Parse any JSON.
14 | app.use(bodyParser.json());
15 |
16 | // Routing to controllers
17 | app.use(require('./api/message'));
18 | app.use(require('./api/health'));
19 |
20 | // Catch all unhandled requests.
21 | app.use('*', function (req, res, next) {
22 | res.sendStatus(400);
23 | })
24 |
25 | // Start the Express server
26 | var port = process.env.PORT || 19020;
27 | var server = app.listen(port, function () {
28 | var port = server.address().port;
29 | console.log(`### Server listening on ${server.address().port}`);
30 | });
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 |
2 | docker image rm localhost:5000/microservice-example-1/frontend --force
3 | docker image rm localhost:5000/microservice-example-1/backend-api1 --force
4 | docker image rm localhost:5000/microservice-example-1/backend-api2 --force
5 |
6 | $ENV:DOCKER_REG="localhost:5000/";$ENV:DOCKER_TAG=":latest";docker-compose build --force-rm --pull
7 |
8 | docker-compose push
9 |
10 | docker tag localhost:5000/microservice-example-1/frontend localhost:5000/microservice-example-1/frontend:latest
11 | docker push localhost:5000/microservice-example-1/frontend:latest
12 | docker tag localhost:5000/microservice-example-1/backend-api1 localhost:5000/microservice-example-1/backend-api1:latest
13 | docker push localhost:5000/microservice-example-1/backend-api1:latest
14 | docker tag localhost:5000/microservice-example-1/backend-api2 localhost:5000/microservice-example-1/backend-api2:latest
15 | docker push localhost:5000/microservice-example-1/backend-api2:latest
16 |
17 | curl http://localhost:5000/v2/_catalog/
18 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | frontend:
5 | image: ${DOCKER_REG}microservice-example-1/frontend${DOCKER_TAG}
6 | build:
7 | context: .
8 | dockerfile: frontend/Dockerfile
9 | args:
10 | build_config: production
11 | ports:
12 | - 9000:3000
13 | environment:
14 | - PORT=3000
15 | - SERVICE_API1_ENDPOINT=http://localhost:9010/api
16 | networks:
17 | - microservice-example-network
18 |
19 | backend-api1:
20 | image: ${DOCKER_REG}microservice-example-1/backend-api1${DOCKER_TAG}
21 | build:
22 | context: .
23 | dockerfile: backend-api1/Dockerfile
24 | args:
25 | build_info: Built from Docker compose
26 | ports:
27 | - 9010:3010
28 | depends_on:
29 | - frontend
30 | environment:
31 | - PORT=3010
32 | - SERVICE_API2_ENDPOINT=http://backend-api2:3020/api
33 | networks:
34 | - microservice-example-network
35 |
36 | backend-api2:
37 | image: ${DOCKER_REG}microservice-example-1/backend-api2${DOCKER_TAG}
38 | build:
39 | context: .
40 | dockerfile: backend-api2/Dockerfile
41 | args:
42 | build_info: Built from Docker compose
43 | #ports:
44 | # - 9020:3020
45 | expose:
46 | - 3020
47 | depends_on:
48 | - backend-api1
49 | environment:
50 | - PORT=3020
51 | networks:
52 | - microservice-example-network
53 |
54 | networks:
55 | microservice-example-network:
56 |
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/README.md:
--------------------------------------------------------------------------------
1 | # Azure and VSTS CI/CD integration
2 |
3 | ## Azure setup
4 | Azure Kubernetes cluster creation template can be found from `azure/kubernetes-cluster-template` folder. But below are short list of creation parameters.
5 |
6 | 1. Create `Kubernetes Service`
7 | 2. Setup
8 | - Basic
9 | - Subscription: Select your own.
10 | - Resource Group: Create new or use existing one.
11 | - Kubernetes cluster name: Your choice.
12 | - Region: Select closest region.
13 | - Kubernetes version: `1.11.2` example is made this this version.
14 | - DNS name prefix. Your choice.
15 | - Scale
16 | - Node size: `Standard B2ms`. 2vcpus, 8 GB memory, 16GB ssd.
17 | - Node count: `1` is enough for testing.
18 | - Authentication
19 | - Service principal: Default `(new) default service principal`.
20 | - Enable RBAC: Default `No`.
21 | - Networking
22 | - HTTP application routing: Default `Yes`.
23 | - Network configuration: Default `Basic`.
24 | - Monitoring
25 | - Enable containet monitoring: Default `Yes`.
26 | - Log Analytics workspace: Default selection.
27 |
28 | ### Azure Container registry authentication
29 | https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication
30 | Open `Azure Container registry` and select `Access keys`. Select `Enable` for `Admin user`.
31 | or
32 | ```
33 | az acr update -n --admin-enabled true
34 | ```
35 | Now you should be able to connect to you Azure container registry.
36 |
37 | Login to Azure Container registry.
38 | ```
39 | az acr login --name
40 | ```
41 | ### Azure kubernetes cluster connection
42 | https://docs.microsoft.com/en-us/azure/aks/tutorial-kubernetes-deploy-cluster
43 | Get Azure kubernetes cluster credentials.
44 | ```
45 | az aks get-credentials --resource-group myResourceGroup --name
46 | ```
47 | Select context for `kubectl`.
48 | ```
49 | kubectl config use-context
50 | ```
51 |
52 | ### Create Azure kubernetes connection settings for VSTS
53 | https://github.com/dtzar/blog/tree/master/CD-Kubernetes-VSTS
54 | 1. Create VSTS pipeline
55 | 2. Add Kubernetes service to VSTS and create connection between VSTS and Kubernetes
56 | az aks create --name --resource-group --generate-ssh-keys
57 | 3. View created connection configuration.
58 | ```
59 | kubectl config view
60 | ```
61 | or manually from folder
62 | - Linux: `${HOME}/.kube/config`
63 | - Windows: `%UserProfile%\.kube\config`
64 |
65 | ### Kubernetes Azure CR authentication
66 | ```
67 | kubectl create secret docker-registry acr-auth --docker-server .azurecr.io --docker-username --docker-password --docker-email
68 | ```
69 |
70 | ### Azure kubernetes ingress controller installation
71 | https://blogs.msdn.microsoft.com/mihansen/2018/05/31/kubernetes-ingress-in-azure-government/
72 | ```
73 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
74 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/cloud-generic.yaml
75 | ```
76 |
77 | ## Continuous Integration (CI)
78 |
79 | 
80 |
81 | 
82 | 
83 | 
84 | 
85 | 
86 |
87 | ## Continuous Delivery (CD)
88 |
89 | 
90 | 
91 | 
92 | 
93 | 
94 | 
95 |
96 |
97 | # Other Azure notes
98 |
99 | az acr login --name
100 |
101 | kubectl config use-context
102 |
103 | https://github.com/dtzar/blog/tree/master/CD-Kubernetes-VSTS
104 | 1. Create VSTS pipeline
105 | 2. Add Kubernetes service to VSTS and create connection between VSTS and Kubernetes
106 | az aks create --name --resource-group --generate-ssh-keys
107 | 3. kubectl config view
108 | - ${HOME}/.kube/config for Linux systems
109 | - %UserProfile%\.kube\config for Windows machines
110 |
111 | kubenetes <-> azure ACR authentication
112 | kubectl create secret docker-registry acr-auth --docker-server .azurecr.io --docker-username --docker-password --docker-email
113 |
114 | https://mohitgoyal.co/2017/09/21/configure-cicd-for-dockerized-apps-using-vsts-to-kubernetes-cluster-in-acs/
115 |
116 |
117 | Create azure cluster connection.
118 | az aks get-credentials --resource-group --name
119 |
120 | Switch to azure cluster.
121 | kubectl config use-context
122 |
123 | Show Azure cluster information.
124 | kubectl cluster-info
125 |
126 |
127 | Azure NgInx controller install
128 | https://blogs.msdn.microsoft.com/mihansen/2018/05/31/kubernetes-ingress-in-azure-government/
129 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
130 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/cloud-generic.yaml
131 |
132 |
133 | Get azure DNS zone name.
134 | az aks show --resource-group spylkkanen-microservices --name spylkkanen --query addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName -o table
135 |
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_job.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_k8s_deploy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_k8s_deploy.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr_deployment_trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr_deployment_trigger.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_github.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_ci_build_images.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_ci_build_images.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_ci_build_images_agent_build_services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_ci_build_images_agent_build_services.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_ci_build_images_agent_job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_ci_build_images_agent_job.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_ci_build_images_agent_publish_artifact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_ci_build_images_agent_publish_artifact.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_ci_build_images_agent_push_services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_ci_build_images_agent_push_services.png
--------------------------------------------------------------------------------
/docs/Azure-and-VSTS/vsts_services_kubernetes_authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Azure-and-VSTS/vsts_services_kubernetes_authentication.png
--------------------------------------------------------------------------------
/docs/DockerApplicationQuickLaunch/README.md:
--------------------------------------------------------------------------------
1 | # Docker application quick launch
2 |
3 | Open `kubernetes-microservices-example` folder.
4 |
5 | Run command to build and run application in docker.
6 | ```
7 | docker-compose up --build
8 | ```
9 | Result
10 | ```
11 | Building frontend
12 | Step 1/11 : FROM node:8-alpine
13 | ---> df48b68da02a
14 | Step 2/11 : LABEL version="1.0.0"
15 | ---> Using cache
16 | ---> 3a216bb3f0a4
17 | Step 3/11 : ARG basedir="frontend"
18 | ---> Using cache
19 | ---> e80eece9e0b9
20 | Step 4/11 : ENV NODE_ENV production
21 | ---> Using cache
22 | ---> 18fddf9fd538
23 | Step 5/11 : WORKDIR ${basedir}/ .
24 | ---> Using cache
25 | ---> 0855136e3ac6
26 | Step 6/11 : COPY ${basedir}/package*.json ./
27 | ---> Using cache
28 | ---> 9f46ad9cba02
29 | Step 7/11 : RUN npm install --silent
30 | ---> Using cache
31 | ---> e341a0c4ea79
32 | Step 8/11 : COPY ${basedir}/ .
33 | ---> Using cache
34 | ---> 3552085b0718
35 | Step 9/11 : RUN apk update && apk add bash
36 | ---> Using cache
37 | ---> d6165beeff70
38 | Step 10/11 : EXPOSE 9000
39 | ---> Using cache
40 | ---> 5efbb141f38a
41 | Step 11/11 : CMD npm start
42 | ---> Using cache
43 | ---> 34eaa8780cd9
44 | [Warning] One or more build-args [build_config] were not consumed
45 | Successfully built 34eaa8780cd9
46 | Successfully tagged localhost:5000/microservice-example-1/frontend:latest
47 | Building backend-api1
48 | Step 1/10 : FROM node:8-alpine
49 | ---> df48b68da02a
50 | Step 2/10 : LABEL version="1.0.0"
51 | ---> Using cache
52 | ---> 3a216bb3f0a4
53 | Step 3/10 : ARG basedir="backend-api1"
54 | ---> Using cache
55 | ---> 5d4661422d36
56 | Step 4/10 : WORKDIR ${basedir}/ .
57 | ---> Using cache
58 | ---> 642afd1c3af0
59 | Step 5/10 : COPY ${basedir}/package*.json ./
60 | ---> Using cache
61 | ---> 26b1f36a9851
62 | Step 6/10 : RUN npm install --silent
63 | ---> Using cache
64 | ---> a0e6fe19c392
65 | Step 7/10 : COPY ${basedir}/ .
66 | ---> Using cache
67 | ---> 77b05288bb3e
68 | Step 8/10 : RUN apk update && apk add bash
69 | ---> Using cache
70 | ---> a5d578f90271
71 | Step 9/10 : EXPOSE 9010
72 | ---> Using cache
73 | ---> 422800d48387
74 | Step 10/10 : CMD npm start
75 | ---> Using cache
76 | ---> b22dad5ef63c
77 | [Warning] One or more build-args [build_info] were not consumed
78 | Successfully built b22dad5ef63c
79 | Successfully tagged localhost:5000/microservice-example-1/backend-api1:latest
80 | Building backend-api2
81 | Step 1/10 : FROM node:8-alpine
82 | ---> df48b68da02a
83 | Step 2/10 : LABEL version="1.0.0"
84 | ---> Using cache
85 | ---> 3a216bb3f0a4
86 | Step 3/10 : ARG basedir="backend-api2"
87 | ---> Using cache
88 | ---> 7778fab8cee1
89 | Step 4/10 : WORKDIR ${basedir}/ .
90 | ---> Using cache
91 | ---> 4c88b41ff572
92 | Step 5/10 : COPY ${basedir}/package*.json ./
93 | ---> Using cache
94 | ---> 3e9b5fce9948
95 | Step 6/10 : RUN npm install --silent
96 | ---> Using cache
97 | ---> 1737ecca03ec
98 | Step 7/10 : COPY ${basedir}/ .
99 | ---> Using cache
100 | ---> 0464789a1c98
101 | Step 8/10 : RUN apk update && apk add bash
102 | ---> Using cache
103 | ---> a3efd6d24cff
104 | Step 9/10 : EXPOSE 9020
105 | ---> Using cache
106 | ---> 205a9a8f1d8d
107 | Step 10/10 : CMD npm start
108 | ---> Using cache
109 | ---> c4cdbd7a6daa
110 | [Warning] One or more build-args [build_info] were not consumed
111 | Successfully built c4cdbd7a6daa
112 | Successfully tagged localhost:5000/microservice-example-1/backend-api2:latest
113 | Creating kubernetes-microservices-example_frontend_1 ... done
114 | Creating kubernetes-microservices-example_backend-api1_1 ... done
115 | Creating kubernetes-microservices-example_backend-api2_1 ... done
116 | Attaching to kubernetes-microservices-example_frontend_1, kubernetes-microservices-example_backend-api1_1, kubernetes-microservices-example_backend-api2_1
117 | frontend_1 |
118 | frontend_1 | > frontend@1.0.0 start /frontend/ .
119 | frontend_1 | > node server.js
120 | frontend_1 |
121 | frontend_1 | ### Content dir = '/frontend/ .'
122 | frontend_1 | ### Server listening on 3000
123 | backend-api1_1 |
124 | backend-api1_1 | > backend-api1@1.0.0 start /backend-api1/ .
125 | backend-api1_1 | > node server.js
126 | backend-api1_1 |
127 | backend-api1_1 | ### Server listening on 3010
128 | backend-api2_1 |
129 | backend-api2_1 | > backend-api2@1.0.0 start /backend-api2/ .
130 | backend-api2_1 | > node server.js
131 | backend-api2_1 |
132 | backend-api2_1 | ### Server listening on 3020
133 | ```
134 |
135 | ## Test application
136 | ###### Verify that frontend javascript file can be loaded.
137 | ```
138 | http://localhost:9000/app.js
139 | ```
140 | Result
141 | ```
142 | You should see frontend application javascript code.
143 | ```
144 |
145 | ###### Verify that frontend can give configuration.
146 | ```
147 | http://localhost:9000/api/config
148 | ```
149 | Result
150 | ```
151 | {"service1endpoint":"http://localhost:9010/api"}
152 | ```
153 |
154 | ###### Verify that you can retrieve data from backend-api1.
155 | ```
156 | http://localhost:9010/api/message
157 | ```
158 | Result
159 | ```
160 | {"service1":{"message":"This is message from service 1."},"service2":{"message":"This is message from service 2."}}
161 | ```
162 |
163 | ###### Verify that you can't retrieve data from backend-api2.
164 | ```
165 | http://localhost:9020/api/message
166 | ```
167 | Result
168 | ```
169 | You should see 502 http status code.
170 | ```
171 |
172 | ###### Verify that you can run complete application.
173 | ```
174 | http://localhost:9000/
175 | ```
176 | Result
177 | ```
178 | This is message from service 1.
179 |
180 | This is message from service 2.
181 | ```
--------------------------------------------------------------------------------
/docs/Environment/README.md:
--------------------------------------------------------------------------------
1 | # Environment
2 |
3 | ## Allow powershell script execution
4 | Open powershell as Adminstrator. Run command to enable powershell script execution on local machine.
5 | ```
6 | Set-ExecutionPolicy remoteSigned
7 | ```
8 | ## Docker Community Edition
9 | Install Docker Community Edition. https://docs.docker.com/docker-for-windows/install/
10 | 
11 |
12 | ## Set memory and cpu
13 | Enable atleast 4GB memory and 4 cpu's for docker to host kubernetes applications.
14 | 
15 |
16 | ## Enable kubernetes
17 | Install kubernetes to your local with `Enable Kubernetes` checkbox and also remember to select `Kubernetes` orchestrator.
18 | 
19 |
20 | ## Install kubernetes ingress controller
21 | https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md
22 |
23 | Install ingress controller with command
24 | ```
25 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
26 | ```
27 |
28 | Verify installation with commands.
29 | ```
30 | kubectl get pods -n ingress-nginx
31 | kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch
32 | ```
33 | Result
34 | ```
35 | NAME READY STATUS RESTARTS AGE
36 | default-http-backend-7c5bc89cc9-wpc2p 1/1 Running 5 8d
37 | nginx-ingress-controller-5b6864749-t8qff 1/1 Running 5 8d
38 | NAMESPACE NAME READY STATUS RESTARTS AGE
39 | ingress-nginx nginx-ingress-controller-5b6864749-t8qff 1/1 Running 5 8d
40 | ```
41 |
42 | Alternative way to install ingress.
43 | https://stackoverflow.com/questions/49845021/getting-an-kubernetes-ingress-endpoint-ip-address
44 | ```
45 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/namespace.yaml
46 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/default-backend.yaml
47 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/configmap.yaml
48 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/tcp-services-configmap.yaml
49 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/udp-services-configmap.yaml
50 | ```
51 |
52 | ## Create local private registry
53 | Run command to create local docker image registry. https://docs.docker.com/registry/deploying/
54 | ```
55 | docker run -d -p 5000:5000 --restart=always --name registry registry:2
56 | ```
57 | Result
58 | ```
59 | cb6bdd494a2a94d329a03100cb9251e9ef2f5690f50f2522212e8f32d40c9b48
60 | ```
--------------------------------------------------------------------------------
/docs/Environment/docker-about.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Environment/docker-about.PNG
--------------------------------------------------------------------------------
/docs/Environment/docker-enable-kubernetes.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Environment/docker-enable-kubernetes.PNG
--------------------------------------------------------------------------------
/docs/Environment/docker-memory-and-cpu.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/Environment/docker-memory-and-cpu.PNG
--------------------------------------------------------------------------------
/docs/KubernetesApplicationQuickLaunch/README.md:
--------------------------------------------------------------------------------
1 | # Kubernetes application quick launch
2 |
3 | Open `kubernetes-microservices-example` folder.
4 |
5 | Run command to build docker images and push images them to local registry.
6 | ```
7 | ./build.ps1
8 | ```
9 | Result
10 | ```
11 | Untagged: localhost:5000/microservice-example-1/frontend:latest
12 | Untagged: localhost:5000/microservice-example-1/frontend@sha256:16874db16ea58a1a75acd8488a6e46a919b51405938c7e0f25acee394b9c5e95
13 | Untagged: localhost:5000/microservice-example-1/backend-api1:latest
14 | Untagged: localhost:5000/microservice-example-1/backend-api1@sha256:a705d9a0c938105ff5faf16552e5c62a99c27e96b78c1b17006d3964890ad2d1
15 | Untagged: localhost:5000/microservice-example-1/backend-api2:latest
16 | Untagged: localhost:5000/microservice-example-1/backend-api2@sha256:6e8f5b642de39958ebd3a6b5cc0227f42ee8364c48a64070f43f725e5f249ed1
17 | Building frontend
18 | Step 1/11 : FROM node:8-alpine
19 | 8-alpine: Pulling from library/node
20 | Digest: sha256:d75742c5fd41261113ed4706f961a21238db84648c825a5126ada373c361f46e
21 | Status: Image is up to date for node:8-alpine
22 | ---> df48b68da02a
23 | Step 2/11 : LABEL version="1.0.0"
24 | ---> Using cache
25 | ---> 3a216bb3f0a4
26 | Step 3/11 : ARG basedir="frontend"
27 | ---> Using cache
28 | ---> e80eece9e0b9
29 | Step 4/11 : ENV NODE_ENV production
30 | ---> Using cache
31 | ---> 18fddf9fd538
32 | Step 5/11 : WORKDIR ${basedir}/ .
33 | ---> Using cache
34 | ---> 0855136e3ac6
35 | Step 6/11 : COPY ${basedir}/package*.json ./
36 | ---> Using cache
37 | ---> 9f46ad9cba02
38 | Step 7/11 : RUN npm install --silent
39 | ---> Using cache
40 | ---> e341a0c4ea79
41 | Step 8/11 : COPY ${basedir}/ .
42 | ---> Using cache
43 | ---> 3552085b0718
44 | Step 9/11 : RUN apk update && apk add bash
45 | ---> Using cache
46 | ---> d6165beeff70
47 | Step 10/11 : EXPOSE 9000
48 | ---> Using cache
49 | ---> 5efbb141f38a
50 | Step 11/11 : CMD npm start
51 | ---> Using cache
52 | ---> 34eaa8780cd9
53 | [Warning] One or more build-args [build_config] were not consumed
54 | Successfully built 34eaa8780cd9
55 | Successfully tagged localhost:5000/microservice-example-1/frontend:latest
56 | Building backend-api1
57 | Step 1/10 : FROM node:8-alpine
58 | 8-alpine: Pulling from library/node
59 | Digest: sha256:d75742c5fd41261113ed4706f961a21238db84648c825a5126ada373c361f46e
60 | Status: Image is up to date for node:8-alpine
61 | ---> df48b68da02a
62 | Step 2/10 : LABEL version="1.0.0"
63 | ---> Using cache
64 | ---> 3a216bb3f0a4
65 | Step 3/10 : ARG basedir="backend-api1"
66 | ---> Using cache
67 | ---> 5d4661422d36
68 | Step 4/10 : WORKDIR ${basedir}/ .
69 | ---> Using cache
70 | ---> 642afd1c3af0
71 | Step 5/10 : COPY ${basedir}/package*.json ./
72 | ---> Using cache
73 | ---> 26b1f36a9851
74 | Step 6/10 : RUN npm install --silent
75 | ---> Using cache
76 | ---> a0e6fe19c392
77 | Step 7/10 : COPY ${basedir}/ .
78 | ---> Using cache
79 | ---> 77b05288bb3e
80 | Step 8/10 : RUN apk update && apk add bash
81 | ---> Using cache
82 | ---> a5d578f90271
83 | Step 9/10 : EXPOSE 9010
84 | ---> Using cache
85 | ---> 422800d48387
86 | Step 10/10 : CMD npm start
87 | ---> Using cache
88 | ---> b22dad5ef63c
89 | [Warning] One or more build-args [build_info] were not consumed
90 | Successfully built b22dad5ef63c
91 | Successfully tagged localhost:5000/microservice-example-1/backend-api1:latest
92 | Building backend-api2
93 | Step 1/10 : FROM node:8-alpine
94 | 8-alpine: Pulling from library/node
95 | Digest: sha256:d75742c5fd41261113ed4706f961a21238db84648c825a5126ada373c361f46e
96 | Status: Image is up to date for node:8-alpine
97 | ---> df48b68da02a
98 | Step 2/10 : LABEL version="1.0.0"
99 | ---> Using cache
100 | ---> 3a216bb3f0a4
101 | Step 3/10 : ARG basedir="backend-api2"
102 | ---> Using cache
103 | ---> 7778fab8cee1
104 | Step 4/10 : WORKDIR ${basedir}/ .
105 | ---> Using cache
106 | ---> 4c88b41ff572
107 | Step 5/10 : COPY ${basedir}/package*.json ./
108 | ---> Using cache
109 | ---> 3e9b5fce9948
110 | Step 6/10 : RUN npm install --silent
111 | ---> Using cache
112 | ---> 1737ecca03ec
113 | Step 7/10 : COPY ${basedir}/ .
114 | ---> Using cache
115 | ---> 0464789a1c98
116 | Step 8/10 : RUN apk update && apk add bash
117 | ---> Using cache
118 | ---> a3efd6d24cff
119 | Step 9/10 : EXPOSE 9020
120 | ---> Using cache
121 | ---> 205a9a8f1d8d
122 | Step 10/10 : CMD npm start
123 | ---> Using cache
124 | ---> c4cdbd7a6daa
125 | [Warning] One or more build-args [build_info] were not consumed
126 | Successfully built c4cdbd7a6daa
127 | Successfully tagged localhost:5000/microservice-example-1/backend-api2:latest
128 | Pushing frontend (localhost:5000/microservice-example-1/frontend:latest)...
129 | The push refers to repository [localhost:5000/microservice-example-1/frontend]
130 | 935ba74cf4d8: Layer already exists
131 | 9758ab13c113: Layer already exists
132 | 16d236c94835: Layer already exists
133 | b59eac191a7d: Layer already exists
134 | 2b4cefb9501e: Layer already exists
135 | 8b59e4cead98: Layer already exists
136 | 7aa09d2ca0a3: Layer already exists
137 | df64d3292fd6: Layer already exists
138 | latest: digest: sha256:16874db16ea58a1a75acd8488a6e46a919b51405938c7e0f25acee394b9c5e95 size: 1996
139 | Pushing backend-api1 (localhost:5000/microservice-example-1/backend-api1:latest)...
140 | The push refers to repository [localhost:5000/microservice-example-1/backend-api1]
141 | d935f7c8a365: Layer already exists
142 | 63754cfe9038: Layer already exists
143 | b0004ce82fd2: Layer already exists
144 | 165ae900e867: Layer already exists
145 | 2d5ae6ee428a: Layer already exists
146 | 8b59e4cead98: Layer already exists
147 | 7aa09d2ca0a3: Layer already exists
148 | df64d3292fd6: Layer already exists
149 | latest: digest: sha256:a705d9a0c938105ff5faf16552e5c62a99c27e96b78c1b17006d3964890ad2d1 size: 1996
150 | Pushing backend-api2 (localhost:5000/microservice-example-1/backend-api2:latest)...
151 | The push refers to repository [localhost:5000/microservice-example-1/backend-api2]
152 | d7019ff82462: Layer already exists
153 | a483eb063fbb: Layer already exists
154 | 58b5ec8fa2ab: Layer already exists
155 | b6a193233f40: Layer already exists
156 | 97ffcc33d464: Layer already exists
157 | 8b59e4cead98: Layer already exists
158 | 7aa09d2ca0a3: Layer already exists
159 | df64d3292fd6: Layer already exists
160 | latest: digest: sha256:6e8f5b642de39958ebd3a6b5cc0227f42ee8364c48a64070f43f725e5f249ed1 size: 1996
161 | The push refers to repository [localhost:5000/microservice-example-1/frontend]
162 | 935ba74cf4d8: Layer already exists
163 | 9758ab13c113: Layer already exists
164 | 16d236c94835: Layer already exists
165 | b59eac191a7d: Layer already exists
166 | 2b4cefb9501e: Layer already exists
167 | 8b59e4cead98: Layer already exists
168 | 7aa09d2ca0a3: Layer already exists
169 | df64d3292fd6: Layer already exists
170 | latest: digest: sha256:16874db16ea58a1a75acd8488a6e46a919b51405938c7e0f25acee394b9c5e95 size: 1996
171 | The push refers to repository [localhost:5000/microservice-example-1/backend-api1]
172 | d935f7c8a365: Layer already exists
173 | 63754cfe9038: Layer already exists
174 | b0004ce82fd2: Layer already exists
175 | 165ae900e867: Layer already exists
176 | 2d5ae6ee428a: Layer already exists
177 | 8b59e4cead98: Layer already exists
178 | 7aa09d2ca0a3: Layer already exists
179 | df64d3292fd6: Layer already exists
180 | latest: digest: sha256:a705d9a0c938105ff5faf16552e5c62a99c27e96b78c1b17006d3964890ad2d1 size: 1996
181 | The push refers to repository [localhost:5000/microservice-example-1/backend-api2]
182 | d7019ff82462: Layer already exists
183 | a483eb063fbb: Layer already exists
184 | 58b5ec8fa2ab: Layer already exists
185 | b6a193233f40: Layer already exists
186 | 97ffcc33d464: Layer already exists
187 | 8b59e4cead98: Layer already exists
188 | 7aa09d2ca0a3: Layer already exists
189 | df64d3292fd6: Layer already exists
190 | latest: digest: sha256:6e8f5b642de39958ebd3a6b5cc0227f42ee8364c48a64070f43f725e5f249ed1 size: 1996
191 |
192 |
193 | StatusCode : 200
194 | StatusDescription : OK
195 | Content : {"repositories":["microservice-example-1/backend-api1","microservice-example-1/backend-api2","microservice-example-1/frontend"]}
196 |
197 | RawContent : HTTP/1.1 200 OK
198 | Docker-Distribution-Api-Version: registry/2.0
199 | X-Content-Type-Options: nosniff
200 | Content-Length: 163
201 | Content-Type: application/json; charset=utf-8
202 | Date: Sun, 16 Sep 2018 12:40:46 GMT...
203 | Forms : {}
204 | Headers : {[Docker-Distribution-Api-Version, registry/2.0], [X-Content-Type-Options, nosniff], [Content-Length, 163], [Content-Type, application/json; charset=utf-8]...}
205 | Images : {}
206 | InputFields : {}
207 | Links : {}
208 | ParsedHtml : System.__ComObject
209 | RawContentLength : 163
210 | ```
211 |
212 | Open `kubernetes-microservices-example\kubernetes` folder.
213 |
214 | Deploy application to kubernetes application with command
215 | ```
216 | ./reinstall.ps1
217 | ```
218 | Result
219 | ```
220 | deployment.extensions "backend-api-1" deleted
221 | deployment.extensions "backend-api-2" deleted
222 | deployment.extensions "frontend" deleted
223 | ingress.extensions "microservices-example-ingress" deleted
224 | No resources found
225 | service "backend-api-1-svc" deleted
226 | service "backend-api-2-svc" deleted
227 | service "frontend-svc" deleted
228 | service "kubernetes" deleted
229 | warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
230 | pod "backend-api-1-66cc9fcfff-mmmqw" deleted
231 | pod "backend-api-1-66cc9fcfff-n2llg" deleted
232 | pod "backend-api-1-66cc9fcfff-n2vdj" deleted
233 | pod "backend-api-2-58bd6dc59b-8pgtj" deleted
234 | pod "backend-api-2-58bd6dc59b-g8nsr" deleted
235 | pod "backend-api-2-58bd6dc59b-nf7jt" deleted
236 | pod "frontend-586984c5dc-q8qzh" deleted
237 | pod "frontend-586984c5dc-w84ck" deleted
238 | No resources found
239 | No resources found
240 | service "backend-api-1-svc" created
241 | service "backend-api-2-svc" created
242 | service "frontend-svc" created
243 | deployment.apps "backend-api-1" created
244 | deployment.apps "backend-api-2" created
245 | deployment.apps "frontend" created
246 | ingress.extensions "microservices-example-ingress" created
247 | ```
248 |
249 | Check kubernetes application status. Wait until you will see all pods in `Running` state and that ingress has external `Address`. Also see that pods does not have external access. Application is only accessible through ingress.
250 | ```
251 | ./status.ps1
252 | ```
253 | Result
254 | ```
255 | NAME READY STATUS RESTARTS AGE
256 | backend-api-1-66cc9fcfff-44gvc 1/1 Running 0 2m
257 | backend-api-1-66cc9fcfff-w6kq6 1/1 Running 0 2m
258 | backend-api-1-66cc9fcfff-z2967 1/1 Running 0 2m
259 | backend-api-2-58bd6dc59b-6tq97 1/1 Running 0 2m
260 | backend-api-2-58bd6dc59b-bw9tn 1/1 Running 0 2m
261 | backend-api-2-58bd6dc59b-t9bf8 1/1 Running 0 2m
262 | frontend-586984c5dc-bvzx2 1/1 Running 0 2m
263 | frontend-586984c5dc-d4hqk 1/1 Running 0 2m
264 | NAME HOSTS ADDRESS PORTS AGE
265 | microservices-example-ingress localhost localhost 80 2m
266 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
267 | backend-api-1-svc ClusterIP 10.106.200.16 80/TCP 2m
268 | backend-api-2-svc ClusterIP 10.101.241.106 80/TCP 2m
269 | frontend-svc ClusterIP 10.97.53.55 80/TCP 2m
270 | kubernetes ClusterIP 10.96.0.1 443/TCP 2m
271 | ```
272 |
273 | ## Test application
274 | ###### Verify that frontend javascript file can be loaded.
275 | ```
276 | http://localhost/app.js
277 | ```
278 | Result
279 | ```
280 | You should see frontend application javascript code.
281 | ```
282 |
283 | ###### Verify that frontend can give configuration.
284 | ```
285 | http://localhost/api/config
286 | ```
287 | Result
288 | ```
289 | {"service1endpoint":"http://localhost:9010/api"}
290 | ```
291 |
292 | ###### Verify that you can retrieve data from backend-api-1-svc.
293 | ```
294 | http://localhost/api/message
295 | ```
296 | Result
297 | ```
298 | {"service1":{"message":"This is message from service 1."},"service2":{"message":"This is message from service 2."}}
299 | ```
300 |
301 | ###### Verify that you can run complete application.
302 | ```
303 | http://localhost/
304 | ```
305 | Result
306 | ```
307 | This is message from service 1.
308 |
309 | This is message from service 2.
310 | ```
--------------------------------------------------------------------------------
/docs/Other/README.md:
--------------------------------------------------------------------------------
1 | # Other
2 |
3 | ```
4 | kubectl get services --all-namespaces
5 | REM kubectl get pods --all-namespaces -l app=ingress-nginx --watch
6 | ```
7 | ---
8 |
9 | ## Remove unused images
10 | https://gist.github.com/bastman/5b57ddb3c11942094f8d0a97d461b430
11 | ```
12 | docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
13 | ```
14 | ---
15 |
16 | ## Build images to local registry
17 | https://docs.docker.com/compose/reference/push/
18 | Open .env file and set DOCKER_REG=localhost:5000/
19 | ```
20 | docker-compose build --force-rm --pull
21 | ```
22 | or
23 | ```
24 | $ENV:DOCKER_REG="localhost:5000/";docker-compose build --force-rm --pull
25 | ```
26 | or
27 | ```
28 | $ENV:DOCKER_REG="localhost:5000/";$ENV:DOCKER_TAG=":latest";docker-compose build --force-rm --pull
29 | ```
30 | ---
31 |
32 | Build tag images
33 | ```
34 | docker tag image-name:tag-name localhost:5000/image-name:tag-name
35 | docker push localhost:5000/image-name:tag-name
36 | ```
37 | ---
38 |
39 | ## Push images to repository
40 | ```
41 | docker-compose push
42 | ```
43 | or
44 | ```
45 | docker push localhost:5000/image-name:tag-name
46 | ```
47 | ---
48 |
49 | ## Verify images from registry
50 | https://docs.docker.com/registry/spec/api/#listing-repositories
51 | command
52 | ```
53 | curl http://localhost:5000/v2/_catalog/
54 | ```
55 | ---
56 |
57 | ## Open kubernetes pods live log
58 | Show pod console logs
59 | ```
60 | kubectl logs frontend-657dcc9684-dj8z8
61 | ```
62 | ---
63 |
64 | ## Open kubernetes pod bash command line
65 | ```
66 | kubectl exec -it frontend-866f7bff9f-7gf88 -- /bin/bash
67 | ```
--------------------------------------------------------------------------------
/docs/docker-application-structure.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/docker-application-structure.PNG
--------------------------------------------------------------------------------
/docs/kubernetes-application-structure.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spylkkanen/kubernetes-microservices/4946f2808a77a2acb9e87f77c52c61644c42a39f/docs/kubernetes-application-structure.PNG
--------------------------------------------------------------------------------
/frontend/.env:
--------------------------------------------------------------------------------
1 | SERVICE_API1_ENDPOINT=
2 | PORT=
--------------------------------------------------------------------------------
/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:8-alpine
2 |
3 | LABEL version="1.0.0"
4 | ARG basedir="frontend"
5 | ENV NODE_ENV production
6 | WORKDIR ${basedir}/ .
7 |
8 | # Copy package.json
9 | COPY ${basedir}/package*.json ./
10 |
11 | # Install npm packages
12 | RUN npm install --silent
13 |
14 | # Copy project files to the workdir.
15 | COPY ${basedir}/ .
16 |
17 | # Install bash inside container. Only if you need to debug app inside of the container.
18 | RUN apk update && apk add bash
19 |
20 | EXPOSE 9000
21 | CMD npm start
--------------------------------------------------------------------------------
/frontend/app.js:
--------------------------------------------------------------------------------
1 | function runExample() {
2 | var xhr = new XMLHttpRequest();
3 | xhr.open('GET', 'api/config', true);
4 | xhr.responseType = 'json';
5 | xhr.onload = function() {
6 | var status = xhr.status;
7 | if (status === 200) {
8 | var xhr2 = new XMLHttpRequest();
9 | xhr2.open('GET', xhr.response.service1endpoint + '/message', true);
10 | xhr2.responseType = 'json';
11 | xhr2.onload = function() {
12 | var status2 = xhr2.status;
13 | if (status2 === 200) {
14 |
15 | document.getElementById("service1message").innerHTML = xhr2.response.service1.message;
16 | document.getElementById("service2message").innerHTML = xhr2.response.service2.message;
17 | } else {
18 | document.getElementById("errormessage").innerHTML = JSON.stringify(xhr2.response);
19 | }
20 | };
21 |
22 | xhr2.send();
23 | } else {
24 | document.getElementById("errormessage").innerHTML = JSON.stringify(xhr2.response);
25 | }
26 | };
27 |
28 | xhr.send();
29 | }
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Microservice example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.5",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
11 | "requires": {
12 | "mime-types": "2.1.20",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "array-flatten": {
17 | "version": "1.1.1",
18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
20 | },
21 | "body-parser": {
22 | "version": "1.18.2",
23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
25 | "requires": {
26 | "bytes": "3.0.0",
27 | "content-type": "1.0.4",
28 | "debug": "2.6.9",
29 | "depd": "1.1.2",
30 | "http-errors": "1.6.3",
31 | "iconv-lite": "0.4.19",
32 | "on-finished": "2.3.0",
33 | "qs": "6.5.1",
34 | "raw-body": "2.3.2",
35 | "type-is": "1.6.16"
36 | }
37 | },
38 | "bytes": {
39 | "version": "3.0.0",
40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
42 | },
43 | "content-disposition": {
44 | "version": "0.5.2",
45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
47 | },
48 | "content-type": {
49 | "version": "1.0.4",
50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
52 | },
53 | "cookie": {
54 | "version": "0.3.1",
55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
57 | },
58 | "cookie-signature": {
59 | "version": "1.0.6",
60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
62 | },
63 | "debug": {
64 | "version": "2.6.9",
65 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
66 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
67 | "requires": {
68 | "ms": "2.0.0"
69 | }
70 | },
71 | "depd": {
72 | "version": "1.1.2",
73 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
74 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
75 | },
76 | "destroy": {
77 | "version": "1.0.4",
78 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
79 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
80 | },
81 | "dotenv": {
82 | "version": "5.0.1",
83 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
84 | "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
85 | },
86 | "ee-first": {
87 | "version": "1.1.1",
88 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
89 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
90 | },
91 | "encodeurl": {
92 | "version": "1.0.2",
93 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
94 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
95 | },
96 | "escape-html": {
97 | "version": "1.0.3",
98 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
99 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
100 | },
101 | "etag": {
102 | "version": "1.8.1",
103 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
104 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
105 | },
106 | "express": {
107 | "version": "4.16.3",
108 | "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
109 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
110 | "requires": {
111 | "accepts": "1.3.5",
112 | "array-flatten": "1.1.1",
113 | "body-parser": "1.18.2",
114 | "content-disposition": "0.5.2",
115 | "content-type": "1.0.4",
116 | "cookie": "0.3.1",
117 | "cookie-signature": "1.0.6",
118 | "debug": "2.6.9",
119 | "depd": "1.1.2",
120 | "encodeurl": "1.0.2",
121 | "escape-html": "1.0.3",
122 | "etag": "1.8.1",
123 | "finalhandler": "1.1.1",
124 | "fresh": "0.5.2",
125 | "merge-descriptors": "1.0.1",
126 | "methods": "1.1.2",
127 | "on-finished": "2.3.0",
128 | "parseurl": "1.3.2",
129 | "path-to-regexp": "0.1.7",
130 | "proxy-addr": "2.0.4",
131 | "qs": "6.5.1",
132 | "range-parser": "1.2.0",
133 | "safe-buffer": "5.1.1",
134 | "send": "0.16.2",
135 | "serve-static": "1.13.2",
136 | "setprototypeof": "1.1.0",
137 | "statuses": "1.4.0",
138 | "type-is": "1.6.16",
139 | "utils-merge": "1.0.1",
140 | "vary": "1.1.2"
141 | }
142 | },
143 | "finalhandler": {
144 | "version": "1.1.1",
145 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
146 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
147 | "requires": {
148 | "debug": "2.6.9",
149 | "encodeurl": "1.0.2",
150 | "escape-html": "1.0.3",
151 | "on-finished": "2.3.0",
152 | "parseurl": "1.3.2",
153 | "statuses": "1.4.0",
154 | "unpipe": "1.0.0"
155 | }
156 | },
157 | "forwarded": {
158 | "version": "0.1.2",
159 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
160 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
161 | },
162 | "fresh": {
163 | "version": "0.5.2",
164 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
165 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
166 | },
167 | "http-errors": {
168 | "version": "1.6.3",
169 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
170 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
171 | "requires": {
172 | "depd": "1.1.2",
173 | "inherits": "2.0.3",
174 | "setprototypeof": "1.1.0",
175 | "statuses": "1.4.0"
176 | }
177 | },
178 | "iconv-lite": {
179 | "version": "0.4.19",
180 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
181 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
182 | },
183 | "inherits": {
184 | "version": "2.0.3",
185 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
186 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
187 | },
188 | "ipaddr.js": {
189 | "version": "1.8.0",
190 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
191 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
192 | },
193 | "media-typer": {
194 | "version": "0.3.0",
195 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
196 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
197 | },
198 | "merge-descriptors": {
199 | "version": "1.0.1",
200 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
201 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
202 | },
203 | "methods": {
204 | "version": "1.1.2",
205 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
206 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
207 | },
208 | "mime": {
209 | "version": "1.4.1",
210 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
211 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
212 | },
213 | "mime-db": {
214 | "version": "1.36.0",
215 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
216 | "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
217 | },
218 | "mime-types": {
219 | "version": "2.1.20",
220 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
221 | "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
222 | "requires": {
223 | "mime-db": "1.36.0"
224 | }
225 | },
226 | "ms": {
227 | "version": "2.0.0",
228 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
229 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
230 | },
231 | "negotiator": {
232 | "version": "0.6.1",
233 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
234 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
235 | },
236 | "on-finished": {
237 | "version": "2.3.0",
238 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
239 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
240 | "requires": {
241 | "ee-first": "1.1.1"
242 | }
243 | },
244 | "parseurl": {
245 | "version": "1.3.2",
246 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
247 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
248 | },
249 | "path-to-regexp": {
250 | "version": "0.1.7",
251 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
252 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
253 | },
254 | "proxy-addr": {
255 | "version": "2.0.4",
256 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
257 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
258 | "requires": {
259 | "forwarded": "0.1.2",
260 | "ipaddr.js": "1.8.0"
261 | }
262 | },
263 | "qs": {
264 | "version": "6.5.1",
265 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
266 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
267 | },
268 | "range-parser": {
269 | "version": "1.2.0",
270 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
271 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
272 | },
273 | "raw-body": {
274 | "version": "2.3.2",
275 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
276 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
277 | "requires": {
278 | "bytes": "3.0.0",
279 | "http-errors": "1.6.2",
280 | "iconv-lite": "0.4.19",
281 | "unpipe": "1.0.0"
282 | },
283 | "dependencies": {
284 | "depd": {
285 | "version": "1.1.1",
286 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
287 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
288 | },
289 | "http-errors": {
290 | "version": "1.6.2",
291 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
292 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
293 | "requires": {
294 | "depd": "1.1.1",
295 | "inherits": "2.0.3",
296 | "setprototypeof": "1.0.3",
297 | "statuses": "1.4.0"
298 | }
299 | },
300 | "setprototypeof": {
301 | "version": "1.0.3",
302 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
303 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
304 | }
305 | }
306 | },
307 | "safe-buffer": {
308 | "version": "5.1.1",
309 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
310 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
311 | },
312 | "send": {
313 | "version": "0.16.2",
314 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
315 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
316 | "requires": {
317 | "debug": "2.6.9",
318 | "depd": "1.1.2",
319 | "destroy": "1.0.4",
320 | "encodeurl": "1.0.2",
321 | "escape-html": "1.0.3",
322 | "etag": "1.8.1",
323 | "fresh": "0.5.2",
324 | "http-errors": "1.6.3",
325 | "mime": "1.4.1",
326 | "ms": "2.0.0",
327 | "on-finished": "2.3.0",
328 | "range-parser": "1.2.0",
329 | "statuses": "1.4.0"
330 | }
331 | },
332 | "serve-static": {
333 | "version": "1.13.2",
334 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
335 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
336 | "requires": {
337 | "encodeurl": "1.0.2",
338 | "escape-html": "1.0.3",
339 | "parseurl": "1.3.2",
340 | "send": "0.16.2"
341 | }
342 | },
343 | "setprototypeof": {
344 | "version": "1.1.0",
345 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
346 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
347 | },
348 | "statuses": {
349 | "version": "1.4.0",
350 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
351 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
352 | },
353 | "type-is": {
354 | "version": "1.6.16",
355 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
356 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
357 | "requires": {
358 | "media-typer": "0.3.0",
359 | "mime-types": "2.1.20"
360 | }
361 | },
362 | "unpipe": {
363 | "version": "1.0.0",
364 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
365 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
366 | },
367 | "utils-merge": {
368 | "version": "1.0.1",
369 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
370 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
371 | },
372 | "vary": {
373 | "version": "1.1.2",
374 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
375 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
376 | }
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "1.0.0",
4 | "main": "server.js",
5 | "engines": {
6 | "node": "^8.11.1"
7 | },
8 | "description": "",
9 | "scripts": {
10 | "start": "node server.js"
11 | },
12 | "license": "MIT",
13 | "repository": "https://github.com/spylkkanen/kubernetes-microservices",
14 | "dependencies": {
15 | "dotenv": "^5.0.0",
16 | "express": "^4.16.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/server.js:
--------------------------------------------------------------------------------
1 | // Load in modules.
2 | var express = require('express');
3 | var app = express();
4 |
5 | // Serve static content from working directory (or '.') by default
6 | var staticContentDir = process.argv[2] || __dirname;
7 | console.log(`### Content dir = '${staticContentDir}'`);
8 |
9 | // Serve all static content (index.html, js, css, assets, etc.)
10 | app.use('/', express.static(staticContentDir));
11 |
12 | // Handle config request.
13 | app.get('/api/config', function (req, res) {
14 | var data = {
15 | service1endpoint: process.env.SERVICE_API1_ENDPOINT || 9010
16 | };
17 |
18 | res.send(data);
19 | });
20 |
21 | // Redirect all other requests to index.html.
22 | app.use('*', function(req, res) {
23 | res.sendFile(`${staticContentDir}/index.html`);
24 | });
25 |
26 | // Start the Express server.
27 | var port = process.env.PORT || 19000;
28 | var server = app.listen(port, function () {
29 | var port = server.address().port;
30 | console.log(`### Server listening on ${server.address().port}`);
31 | });
32 |
--------------------------------------------------------------------------------
/kubernetes/backend-api-1.azure.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 1 deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: backend-api-1
9 | labels:
10 | app: backend-api-1
11 | spec:
12 | replicas: 3
13 | selector:
14 | matchLabels:
15 | app: backend-api-1
16 | template:
17 | metadata:
18 | labels:
19 | app: backend-api-1
20 | spec:
21 | imagePullSecrets:
22 | - name: acr-auth
23 |
24 | containers:
25 | - name: backend-api-1-pods
26 |
27 | image: spylkkanen.azurecr.io/microservice-example-1/backend-api1:latest
28 | imagePullPolicy: Always
29 |
30 | ports:
31 | - containerPort: 3010
32 |
33 | env:
34 | - name: SERVICE_API2_ENDPOINT
35 | value: http://backend-api-2-svc:80/api
36 | - name: PORT
37 | value: "3010"
38 |
39 | livenessProbe:
40 | httpGet:
41 | path: /api/health
42 | port: 3010
43 | initialDelaySeconds: 3
44 | periodSeconds: 1800
45 |
46 | # resources:
47 | # requests:
48 | # memory: "256M"
49 | # cpu: 0.25
50 |
--------------------------------------------------------------------------------
/kubernetes/backend-api-1.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 1 deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: backend-api-1
9 | labels:
10 | app: backend-api-1
11 | spec:
12 | replicas: 3
13 | selector:
14 | matchLabels:
15 | app: backend-api-1
16 | template:
17 | metadata:
18 | labels:
19 | app: backend-api-1
20 | spec:
21 | containers:
22 | - name: backend-api-1-pods
23 |
24 | image: localhost:5000/microservice-example-1/backend-api1:latest
25 | imagePullPolicy: Always
26 |
27 | ports:
28 | - containerPort: 3010
29 |
30 | env:
31 | - name: SERVICE_API2_ENDPOINT
32 | value: http://backend-api-2-svc:80/api
33 | - name: PORT
34 | value: "3010"
35 |
36 | livenessProbe:
37 | httpGet:
38 | path: /api/health
39 | port: 3010
40 | initialDelaySeconds: 3
41 | periodSeconds: 20
42 |
43 | resources:
44 | requests:
45 | memory: "256M"
46 | cpu: 0.25
47 |
--------------------------------------------------------------------------------
/kubernetes/backend-api-1.svc.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 1 ClusterIP.
3 | #
4 |
5 | kind: Service
6 | apiVersion: v1
7 | metadata:
8 | name: backend-api-1-svc
9 | spec:
10 | type: ClusterIP
11 | ports:
12 | - protocol: TCP
13 | port: 80
14 | targetPort: 3010
15 | selector:
16 | app: backend-api-1
--------------------------------------------------------------------------------
/kubernetes/backend-api-2.azure.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 2 deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: backend-api-2
9 | labels:
10 | app: backend-api-2
11 | spec:
12 | replicas: 3
13 | selector:
14 | matchLabels:
15 | app: backend-api-2
16 | template:
17 | metadata:
18 | labels:
19 | app: backend-api-2
20 | spec:
21 | imagePullSecrets:
22 | - name: acr-auth
23 |
24 | containers:
25 | - name: backend-api-2-pods
26 |
27 | image: spylkkanen.azurecr.io/microservice-example-1/backend-api2:latest
28 | imagePullPolicy: Always
29 |
30 | ports:
31 | - containerPort: 3020
32 |
33 | env:
34 | - name: PORT
35 | value: "3020"
36 |
37 | livenessProbe:
38 | httpGet:
39 | path: /api/health
40 | port: 3020
41 | initialDelaySeconds: 3
42 | periodSeconds: 1800
43 |
44 | # resources:
45 | # requests:
46 | # memory: "256M"
47 | # cpu: 0.25
48 |
--------------------------------------------------------------------------------
/kubernetes/backend-api-2.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 2 deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: backend-api-2
9 | labels:
10 | app: backend-api-2
11 | spec:
12 | replicas: 3
13 | selector:
14 | matchLabels:
15 | app: backend-api-2
16 | template:
17 | metadata:
18 | labels:
19 | app: backend-api-2
20 | spec:
21 | containers:
22 | - name: backend-api-2-pods
23 |
24 | image: localhost:5000/microservice-example-1/backend-api2:latest
25 | imagePullPolicy: Always
26 |
27 | ports:
28 | - containerPort: 3020
29 |
30 | env:
31 | - name: PORT
32 | value: "3020"
33 |
34 | livenessProbe:
35 | httpGet:
36 | path: /api/health
37 | port: 3020
38 | initialDelaySeconds: 3
39 | periodSeconds: 20
40 |
41 | resources:
42 | requests:
43 | memory: "256M"
44 | cpu: 0.25
45 |
--------------------------------------------------------------------------------
/kubernetes/backend-api-2.svc.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Backend api 1 ClusterIP.
3 | #
4 |
5 | kind: Service
6 | apiVersion: v1
7 | metadata:
8 | name: backend-api-2-svc
9 | spec:
10 | type: ClusterIP
11 | ports:
12 | - protocol: TCP
13 | port: 80
14 | targetPort: 3020
15 | selector:
16 | app: backend-api-2
--------------------------------------------------------------------------------
/kubernetes/frontend.azure.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Frontend deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: frontend
9 | labels:
10 | app: frontend
11 | spec:
12 | replicas: 2
13 | selector:
14 | matchLabels:
15 | app: frontend
16 | template:
17 | metadata:
18 | labels:
19 | app: frontend
20 | spec:
21 | imagePullSecrets:
22 | - name: acr-auth
23 |
24 | containers:
25 | - name: frontend-pods
26 |
27 | image: spylkkanen.azurecr.io/microservice-example-1/frontend:latest
28 | imagePullPolicy: Always
29 |
30 | ports:
31 | - containerPort: 3000
32 |
33 | env:
34 | - name: SERVICE_API1_ENDPOINT
35 | value: /api
36 | - name: PORT
37 | value: "3000"
38 |
39 | livenessProbe:
40 | httpGet:
41 | path: /
42 | port: 3000
43 | initialDelaySeconds: 3
44 | periodSeconds: 1800
45 |
46 | # resources:
47 | # requests:
48 | # memory: "64M"
49 | # cpu: 0.125
--------------------------------------------------------------------------------
/kubernetes/frontend.deploy.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Frontend deployment.
3 | #
4 |
5 | kind: Deployment
6 | apiVersion: apps/v1
7 | metadata:
8 | name: frontend
9 | labels:
10 | app: frontend
11 | spec:
12 | replicas: 2
13 | selector:
14 | matchLabels:
15 | app: frontend
16 | template:
17 | metadata:
18 | labels:
19 | app: frontend
20 | spec:
21 | containers:
22 | - name: frontend-pods
23 |
24 | image: localhost:5000/microservice-example-1/frontend:latest
25 | imagePullPolicy: Always
26 |
27 | ports:
28 | - containerPort: 3000
29 |
30 | env:
31 | - name: SERVICE_API1_ENDPOINT
32 | value: /api
33 | - name: PORT
34 | value: "3000"
35 |
36 | livenessProbe:
37 | httpGet:
38 | path: /
39 | port: 3000
40 | initialDelaySeconds: 3
41 | periodSeconds: 20
42 |
43 | resources:
44 | requests:
45 | memory: "64M"
46 | cpu: 0.125
--------------------------------------------------------------------------------
/kubernetes/frontend.svc.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Frontend ClusterIP.
3 | #
4 | kind: Service
5 | apiVersion: v1
6 | metadata:
7 | name: frontend-svc
8 | spec:
9 | type: ClusterIP
10 | ports:
11 | - protocol: TCP
12 | port: 80
13 | targetPort: 3000
14 | selector:
15 | app: frontend
--------------------------------------------------------------------------------
/kubernetes/ingress.azure.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ingress routing.
3 | #
4 |
5 | kind: Ingress
6 | apiVersion: extensions/v1beta1
7 | metadata:
8 | name: "microservices-example-ingress"
9 | annotations:
10 | kubernetes.io/ingress.class: addon-http-application-routing
11 | spec:
12 | rules:
13 | #- host: spylkkanen.f791b2e6cfe44a199bfd.northeurope.aksapp.io
14 | - http:
15 | paths:
16 | - path: /api/config
17 | backend:
18 | serviceName: frontend-svc
19 | servicePort: 80
20 | - path: /api
21 | backend:
22 | serviceName: backend-api-1-svc
23 | servicePort: 80
24 | - path: /
25 | backend:
26 | serviceName: frontend-svc
27 | servicePort: 80
28 |
--------------------------------------------------------------------------------
/kubernetes/ingress.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Ingress routing.
3 | #
4 |
5 | kind: Ingress
6 | apiVersion: extensions/v1beta1
7 | metadata:
8 | name: "microservices-example-ingress"
9 | annotations:
10 | spec:
11 | rules:
12 | - host: localhost
13 | http:
14 | paths:
15 | - path: /api/config
16 | backend:
17 | serviceName: frontend-svc
18 | servicePort: 80
19 | - path: /api
20 | backend:
21 | serviceName: backend-api-1-svc
22 | servicePort: 80
23 | - path: /
24 | backend:
25 | serviceName: frontend-svc
26 | servicePort: 80
27 |
--------------------------------------------------------------------------------
/kubernetes/reinstall.ps1:
--------------------------------------------------------------------------------
1 | kubectl delete deployments --all
2 | kubectl delete ingress --all
3 | kubectl delete statefulsets --all
4 | kubectl delete services --all
5 | kubectl delete pods --all --grace-period=0 --force
6 | kubectl delete PersistentVolumeClaims --all
7 | kubectl delete persistentvolumes --all
8 | kubectl create -f .\backend-api-1.svc.yaml -f .\backend-api-2.svc.yaml -f .\frontend.svc.yaml -f .\backend-api-1.deploy.yaml -f .\backend-api-2.deploy.yaml -f .\frontend.deploy.yaml -f .\ingress.yaml
9 |
--------------------------------------------------------------------------------
/kubernetes/status.ps1:
--------------------------------------------------------------------------------
1 | kubectl get pods
2 | kubectl get ingress
3 | kubectl get services
4 |
--------------------------------------------------------------------------------