diff --git a/README.dcos.md b/README.dcos.md new file mode 100644 index 000000000..8877d7588 --- /dev/null +++ b/README.dcos.md @@ -0,0 +1,32 @@ +# eShopOnContainers on DC/OS + +## Prerequisites +* 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. +* 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. +* The `dcos` CLI. See [DC/OS: Installing the CLI](https://dcos.io/docs/1.8/usage/cli/install/) for more details. + +## Prepare the Cluster +1. Install Marathon-LB to your cluster. This can be done with the DC/OS cli (`dcos package install marathon-lb`) or web interface. See the [DC/OS docs](https://dcos.io/docs/1.8/usage/service-discovery/marathon-lb/usage/) for more information. +1. Provide Marathon credentials for your container registry. + * 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. + * Copy your private registry credentials to the mounted 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. +1. Open a PowerShell window in your local `eShopOnContainers` repository's `dcos` directory. +1. Tag the `eshop/...` images with your registry, then push them. + * `tagpush.ps1` can do this for you, e.g.: + >``` + >./tagpush.ps1 myregistry.azurecr.io + >``` +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. Ensure `dcos` is on your path. +1. Run `generate-config.ps1` to generate configuration for the eShopOnContainers services. The script requires hostnames for your cluster's agents and your private registry. Your agents' hostname is of the form `[dns prefix]agents.[Azure region].cloudapp.azure.com`: +>``` +>./generate-config.ps1 -agentsFqdn mydcosagents.centralus.cloudapp.azure.com -registry myregistry.azurecr.io +>``` +7. Use the `dcos` CLI to deploy the application: +>``` +>dcos marathon group add eShopOnContainers.json +>``` +You can watch the deployment progress in the DC/OS web interface at [http://localhost/#/services/](http://localhost/#/services/). diff --git a/README.md b/README.md index 4a4e1d3c3..868b0cbf7 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,40 @@ https://github.com/dotnet-architecture/eShopOnContainers/wiki/04.-Setting-eShopO ## Orchestrators: Kubernetes and Service Fabric See at the [Wiki](https://github.com/dotnet-architecture/eShopOnContainers/wiki) the posts on setup/instructions about how to deploy to Kubernetes or Service Fabric in Azure (although you could also deploy to any other cloud or on-premises). +## DC/OS + +### Prerequisites +* 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. +* 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. +* The `dcos` CLI. See [DC/OS: Installing the CLI](https://dcos.io/docs/1.8/usage/cli/install/) for more details. + +### Prepare the Cluster +1. Install Marathon-LB to your cluster. This can be done with the DC/OS cli (`dcos package install marathon-lb`) or web interface. See the [DC/OS docs](https://dcos.io/docs/1.8/usage/service-discovery/marathon-lb/usage/) for more information. +1. Provide Marathon credentials for your container registry. + * 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. + * Copy your private registry credentials to the mounted 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. +1. Open a PowerShell window in your local `eShopOnContainers` repository's `dcos` directory. +1. Tag the `eshop/...` images with your registry, then push them. + * `tagpush.ps1` can do this for you, e.g.: + >``` + >./tagpush.ps1 myregistry.azurecr.io + >``` +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. Ensure `dcos` is on your path. +1. Run `generate-config.ps1` to generate configuration for the eShopOnContainers services. The script requires hostnames for your cluster's agents and your private registry. Your agents' hostname is of the form `[dns prefix]agents.[Azure region].cloudapp.azure.com`: +>``` +>./generate-config.ps1 -agentsFqdn mydcosagents.centralus.cloudapp.azure.com -registry myregistry.azurecr.io +>``` +7. Use the `dcos` CLI to deploy the application: +>``` +>dcos marathon group add eShopOnContainers.json +>``` +You can watch the deployment progress in the DC/OS web interface at [http://localhost/#/services/](http://localhost/#/services/). + + ## Sending feedback and pull requests As mentioned, we'd appreciate 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/tagpush.ps1 b/dcos/tagpush.ps1 new file mode 100644 index 000000000..bacf26104 --- /dev/null +++ b/dcos/tagpush.ps1 @@ -0,0 +1,10 @@ +Param( + [Parameter(Mandatory=$true)][string]$registry +) + +$images = ("eshop/basket.api", "eshop/catalog.api", "eshop/identity.api", "eshop/ordering.api", "eshop/webmvc", "eshop/webspa", "eshop/webstatus") +foreach ($image in $images) { + $newTag = "$registry/$image" + docker tag $image $newTag + docker push $newTag +} 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" + ] + } + ] +}