├── .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 | ![Build status](https://spylkkanen.visualstudio.com/kubernetes-microservices/_apis/build/status/kubernetes-microservices-Docker%20container-CI) 2 | ![Release status](https://spylkkanen.vsrm.visualstudio.com/_apis/public/Release/badge/91705780-23f2-4c51-8f8d-bd251a855be4/1/1) 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 | ![Docker application structure](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/docker-application-structure.PNG) 19 | 20 | ## Kubernetes hosted application 21 | Simplyfied overal structure of application networking. 22 | ![Kubernetes application structure](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/kubernetes-application-structure.PNG) 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 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_services_kubernetes_authentication.png) 80 | 81 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_ci_build_images.png) 82 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_ci_build_images_agent_job.png) 83 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_ci_build_images_agent_build_services.png) 84 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_ci_build_images_agent_push_services.png) 85 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_ci_build_images_agent_publish_artifact.png) 86 | 87 | ## Continuous Delivery (CD) 88 | 89 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline.png) 90 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_github.png) 91 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr_deployment_trigger.png) 92 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline_artifact_azurecr.png) 93 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_job.png) 94 | ![](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Azure-and-VSTS/vsts_cd_pipeline_agent_k8s_deploy.png) 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 | ![Docker memory and cpu](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Environment/docker-about.PNG) 11 | 12 | ## Set memory and cpu 13 | Enable atleast 4GB memory and 4 cpu's for docker to host kubernetes applications. 14 | ![Docker memory and cpu](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Environment/docker-memory-and-cpu.PNG) 15 | 16 | ## Enable kubernetes 17 | Install kubernetes to your local with `Enable Kubernetes` checkbox and also remember to select `Kubernetes` orchestrator. 18 | ![Enable kubernetes](https://github.com/spylkkanen/kubernetes-microservices/blob/master/docs/Environment/docker-enable-kubernetes.PNG) 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 | --------------------------------------------------------------------------------