From 17e379371f0002684fbeab9c9471f8c6011dce5e Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Thu, 30 Mar 2017 15:36:16 -0700 Subject: [PATCH] Deployment template and README for DC/OS --- README.dcos.md | 92 ++++++++ README.md | 25 +++ dcos/generate-config.ps1 | 9 + dcos/template.json | 439 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 565 insertions(+) create mode 100644 README.dcos.md create mode 100644 dcos/generate-config.ps1 create mode 100644 dcos/template.json diff --git a/README.dcos.md b/README.dcos.md new file mode 100644 index 000000000..e0fcb2fc7 --- /dev/null +++ b/README.dcos.md @@ -0,0 +1,92 @@ +# eShopOnContainers on DC/OS + +## Prerequisites +* A private Docker registry. Follow Azure Container Registry's [guide](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) to create one. +* A DC/OS cluster. Follow Azure Container Service's [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-deployment) to create one. +* The `dcos` CLI (DC/OS docs: [Installing the CLI](https://dcos.io/docs/1.8/usage/cli/install/)) + +## Prepare the Cluster +1. Install Marathon-LB. ([DC/OS docs](https://dcos.io/docs/1.8/usage/service-discovery/marathon-lb/usage/)) +1. Mount an Azure file share on each agent of the cluster at `/mnt/share`. See the Azure Container Service [documentation](https://docs.microsoft.com/en-us/azure/container-service/container-service-dcos-fileshare) for guidance. +1. Copy your private registry credentials to the mounted file share as described in the Azure Container Service [documentation](https://docs.microsoft.com/en-us/azure/container-service/container-service-dcos-acr). + +## Deploy the Application +1. Build the eShopOnContainers Docker images, and push them to your registry. +1. Open an SSH tunnel to your cluster as described in the ACS documentation: [Connect with an ACS cluster](https://docs.microsoft.com/en-us/azure/container-service/container-service-connect#connect-to-a-dcos-or-swarm-cluster) +1. Open a PowerShell window with the `dcos` CLI on your path. +1. Navigate to your local `eShopOnContainers` repository's `dcos` directory. +1. Run `generate-config.ps1` to generate configuration for the eShopOnContainers services. The script requires URLs for your cluster's agents and your private registry. Your agents' URL is of the form `[dns prefix]agents.[Azure region].cloudapp.azure.com`: +>```powershell +>./generate-config.ps1 -agentsFqdn mydcosagents.centralus.cloudapp.azure.com -registry myregistry.azurecr.io +>``` +6. Use the `dcos` CLI to deploy the application: +>```powershell +>dcos marathon group add eShopOnContainers.json +>``` + +# DC/OS 101 +DC/OS is an operating system based on Apache Mesos, a distributed systems kernel. Similar to the way a desktop OS manages a single machine's compute resources, abstracting away the details of core scheduling and memory allocation, DC/OS manages the compute resources of a cluster of heterogenous machines. + +Like a desktop OS, DC/OS is general-purpose; we won't deploy eShopOnContainers to it directly. Instead we'll deploy to Marathon, a container orchestration platform which runs atop DC/OS. + +## A Marathon Application +The Maration API defines applications in JSON. Container application definitions are simple. For example, here's one for the eShopOnContainers SQL server: +>```json +>{ +> "id": "sql-data", +> "container": { +> "type": "DOCKER", +> "docker": { +> "image": "microsoft/mssql-server-linux:ctp1-4", +> "network": "BRIDGE", +> "portMappings": [ +> { +> "hostPort": 1433, +> "labels": { +> "VIP_0": "eshopsql-data:1433" +> } +> } +> ] +> } +> }, +> "env": { +> "ACCEPT_EULA": "Y", +> "SA_PASSWORD": "Pass@word" +> }, +> "healthChecks": [ +> { +> "protocol": "TCP", +> "gracePeriodSeconds": 30, +> "intervalSeconds": 60, +> "timeoutSeconds": 30, +> "maxConsecutiveFailures": 3, +> "port": 1433 +> } +> ], +> "cpus": 0.1, +> "mem": 1024, +> "instances": 1 +>}, +>``` +The definition straightforwardly specifies the image, environment variables, compute resources, and health checks for the container. Marathon's port configuration is more complex; for details, see the [Marathon documentation](https://mesosphere.github.io/marathon/docs/ports.html). Basically, the above definition binds the container's port 1433 to the host's port 1433. It also specifies a DC/OS Virtual IP, `eshopsql-data:1433`. Other containers can use this to find an IP for `sql-data` via Marathon's layer 4 load balancer using a simple naming convention: `eshopsql-data` becomes `eshopsql-data.marathon.l4lb.thisdcos.directory`. + +## Exposing an Application +Marathon-LB is a useful tool for load balancing applications. It includes HAProxy and, using label metadata from the Marathon event bus, dynamically generates its configuration to match the state of Marathon applications. For example, here are the labels for the `webmvc` application: +>```json +>{ +> "id": "webmvc", +> ... +> "labels": { +> "HAPROXY_GROUP": "external", +> "HAPROXY_0_VHOST": "mydcosagents.centralus.cloudapp.azure.com", +> "HAPROXY_0_MODE": "http", +> "HAPROXY_0_PATH": "/webmvc" +> }, +> ... +>} +>``` +This tells the Marathon-LB application responsible for the "external" group to proxy `http://mydcosagents.centralus.cloudapp.azure.com/webmvc` to a `webmvc` container. When `webmvc` deploys, Marathon-LB generates appropriate configuration for, and restarts, HAProxy. + +## Further Reading +* [DC/OS Docs](https://dcos.io/docs/1.8/) +* [Marathon Docs](https://mesosphere.github.io/marathon/docs/) diff --git a/README.md b/README.md index 62f74759a..e50e38331 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,31 @@ https://github.com/dotnet/eShopOnContainers/wiki/04.-Setting-eShopOnContainer-so The Windows Containers scenario is currently being implemented/tested yet. The application should be able to run on Windows Nano Containers based on different Docker base images, as well, as the .NET Core services have also been tested running on plain Windows (with no Docker). The app was also partially tested on "Docker for Mac" using a development MacOS machine with .NET Core and VS Code installed, which is still a scenario using Linux containers running on the VM setup in the Mac by the "Docker for Windows" setup. But further testing and feedback on Mac environments and Windows Containers, from the community, will be appreciated. +## DC/OS +### Prerequisites +* A private Docker registry. Follow Azure Container Registry's [guide](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) to create one. +* A DC/OS cluster. Follow Azure Container Service's [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-deployment) to create one. +* The `dcos` CLI (DC/OS docs: [Installing the CLI](https://dcos.io/docs/1.8/usage/cli/install/)) + +### Prepare the Cluster +1. Install Marathon-LB. ([DC/OS docs](https://dcos.io/docs/1.8/usage/service-discovery/marathon-lb/usage/)) +1. Mount an Azure file share on each agent of the cluster at `/mnt/share`. See the Azure Container Service [documentation](https://docs.microsoft.com/en-us/azure/container-service/container-service-dcos-fileshare) for guidance. +1. Copy your private registry credentials to the mounted file share as described in the Azure Container Service [documentation](https://docs.microsoft.com/en-us/azure/container-service/container-service-dcos-acr). + +### Deploy the Application +1. Build the eShopOnContainers Docker images, and push them to your registry. +1. Open an SSH tunnel to your cluster as described in the ACS documentation: [Connect with an ACS cluster](https://docs.microsoft.com/en-us/azure/container-service/container-service-connect#connect-to-a-dcos-or-swarm-cluster) +1. Open a PowerShell window with the `dcos` CLI on your path. +1. Navigate to your local `eShopOnContainers` repository's `dcos` directory. +1. Run `generate-config.ps1` to generate configuration for the eShopOnContainers services. The script requires URLs for your cluster's agents and your private registry. Your agents' URL is of the form `[dns prefix]agents.[Azure region].cloudapp.azure.com`: +>```powershell +>./generate-config.ps1 -agentsFqdn mydcosagents.centralus.cloudapp.azure.com -registry myregistry.azurecr.io +>``` +6. Use the `dcos` CLI to deploy the application: +>```powershell +>dcos marathon group add eShopOnContainers.json +>``` + ## Sending feedback and pull requests As mentioned, we'd appreciate to your feedback, improvements and ideas. You can create new issues at the issues section, do pull requests and/or send emails to eshop_feedback@service.microsoft.com diff --git a/dcos/generate-config.ps1 b/dcos/generate-config.ps1 new file mode 100644 index 000000000..afd76b76a --- /dev/null +++ b/dcos/generate-config.ps1 @@ -0,0 +1,9 @@ +Param( + [Parameter(Mandatory=$true)][string]$agentsFqdn, + [Parameter(Mandatory=$true)][string]$registry +) + +(Get-Content template.json) | + Foreach-Object { + $_.Replace("AGENTS_FQDN", $agentsFqdn).Replace("REGISTRY", $registry) + } | Set-Content eShopOnContainers.json \ No newline at end of file diff --git a/dcos/template.json b/dcos/template.json new file mode 100644 index 000000000..5915276fc --- /dev/null +++ b/dcos/template.json @@ -0,0 +1,439 @@ +{ + "id": "/eshop", + "apps": [ + { + "id": "basket-data", + "container": { + "type": "DOCKER", + "docker": { + "image": "redis:3.2-alpine", + "network": "BRIDGE", + "portMappings": [ + { + "hostPort": 6379, + "labels": { + "VIP_0": "eshopbasket-data:6379" + } + } + ] + } + }, + "healthChecks": [ + { + "protocol": "TCP", + "gracePeriodSeconds": 30, + "intervalSeconds": 60, + "timeoutSeconds": 30, + "maxConsecutiveFailures": 3, + "port": 6379 + } + ], + "cpus": 0.1, + "mem": 1024, + "instances": 1 + }, + { + "id": "basket-api", + "dependencies": [ + "basket-data", + "rabbitmq" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80/basket", + "ConnectionString": "eshopbasket-data.marathon.l4lb.thisdcos.directory", + "EventBusConnection": "eshoprabbitmq.marathon.l4lb.thisdcos.directory", + "IdentityUrl": "http://AGENTS_FQDN/id" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/basket.api", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/basket" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "catalog", + "dependencies": [ + "rabbitmq", + "sql-data" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80/catalog", + "ConnectionString": "Server=eshopsql-data.marathon.l4lb.thisdcos.directory;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", + "EventBusConnection": "eshoprabbitmq.marathon.l4lb.thisdcos.directory", + "ExternalCatalogBaseUrl": "http://AGENTS_FQDN/catalog" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/catalog.api", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/catalog" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "identity", + "dependencies": [ + "sql-data" + ], + "env": { + "MvcClient": "http://AGENTS_FQDN/webmvc", + "SpaClient": "http://AGENTS_FQDN", + "ASPNETCORE_URLS": "http://0.0.0.0:80/id", + "ConnectionStrings__DefaultConnection": "Server=eshopsql-data.marathon.l4lb.thisdcos.directory;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/identity.api", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/id" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "ordering", + "dependencies": [ + "rabbitmq", + "sql-data" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80/ordering", + "ConnectionString": "Server=eshopsql-data.marathon.l4lb.thisdcos.directory;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", + "EventBusConnection": "eshoprabbitmq.marathon.l4lb.thisdcos.directory", + "IdentityUrl": "http://AGENTS_FQDN/id" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/ordering.api", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/ordering" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "rabbitmq", + "container": { + "type": "DOCKER", + "docker": { + "image": "rabbitmq:3.6-alpine", + "network": "BRIDGE", + "portMappings": [ + { + "hostPort": 5672, + "labels": { + "VIP_0": "eshoprabbitmq:5672" + } + } + ] + } + }, + "healthChecks": [ + { + "protocol": "TCP", + "gracePeriodSeconds": 30, + "intervalSeconds": 60, + "timeoutSeconds": 30, + "maxConsecutiveFailures": 3, + "port": 5672 + } + ], + "cpus": 0.1, + "mem": 256, + "instances": 1 + }, + { + "id": "sql-data", + "container": { + "type": "DOCKER", + "docker": { + "image": "microsoft/mssql-server-linux:ctp1-4", + "network": "BRIDGE", + "portMappings": [ + { + "hostPort": 1433, + "labels": { + "VIP_0": "eshopsql-data:1433" + } + } + ] + } + }, + "env": { + "ACCEPT_EULA": "Y", + "SA_PASSWORD": "Pass@word" + }, + "healthChecks": [ + { + "protocol": "TCP", + "gracePeriodSeconds": 30, + "intervalSeconds": 60, + "timeoutSeconds": 30, + "maxConsecutiveFailures": 3, + "port": 1433 + } + ], + "cpus": 0.1, + "mem": 1024, + "instances": 1 + }, + { + "id": "webmvc", + "dependencies": [ + "basket-api", + "catalog", + "identity", + "ordering" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80/webmvc", + "BasketUrl": "http://AGENTS_FQDN/basket", + "CallBackUrl": "http://AGENTS_FQDN/webmvc", + "CatalogUrl": "http://AGENTS_FQDN/catalog", + "IdentityUrl": "http://AGENTS_FQDN/id", + "OrderingUrl": "http://AGENTS_FQDN/ordering" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/webmvc", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/webmvc" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "webspa", + "dependencies": [ + "basket-api", + "catalog", + "identity", + "ordering" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80", + "BasketUrl": "http://AGENTS_FQDN/basket", + "CallBackUrl": "http://AGENTS_FQDN/webmvc", + "CatalogUrl": "http://AGENTS_FQDN/catalog", + "IdentityUrl": "http://AGENTS_FQDN/id", + "OrderingUrl": "http://AGENTS_FQDN/ordering" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/webspa", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "healthChecks": [ + { + "path": "/hc", + "protocol": "HTTP", + "gracePeriodSeconds": 60, + "intervalSeconds": 60, + "timeoutSeconds": 10, + "maxConsecutiveFailures": 3, + "ignoreHttp1xx": false + } + ], + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + }, + { + "id": "webstatus", + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://0.0.0.0:80/webstatus", + "BasketUrl": "http://AGENTS_FQDN/basket", + "CatalogUrl": "http://AGENTS_FQDN/catalog", + "IdentityUrl": "http://AGENTS_FQDN/id", + "OrderingUrl": "http://AGENTS_FQDN/ordering", + "mvc": "http://AGENTS_FQDN/webmvc", + "spa": "http://AGENTS_FQDN/webspa" + }, + "instances": 1, + "cpus": 0.1, + "mem": 128, + "container": { + "docker": { + "image": "REGISTRY/eshop/webstatus", + "forcePullImage": true, + "portMappings": [ + { + "containerPort": 80 + } + ], + "network": "BRIDGE" + } + }, + "labels": { + "HAPROXY_GROUP": "external", + "HAPROXY_0_VHOST": "AGENTS_FQDN", + "HAPROXY_0_MODE": "http", + "HAPROXY_0_PATH": "/webstatus" + }, + "uris": [ + "file:///mnt/share/docker.tar.gz" + ] + } + ] +}