From 2cfe2b8a83163e075cc5f35e9ad8a75616c91b5d Mon Sep 17 00:00:00 2001 From: Joao Fernandes Date: Mon, 4 Dec 2017 14:14:36 -0800 Subject: [PATCH] Introduce Interlock (#311) * Introduce Interlock * Organize directory structure for Interlock --- _data/toc.yaml | 56 +++++ .../guides/interlock/configuration/index.md | 87 +++++++ .../interlock/configuration/service-labels.md | 27 +++ .../guides/interlock/extensions/haproxy.md | 28 +++ .../3.0/guides/interlock/extensions/nginx.md | 30 +++ datacenter/ucp/3.0/guides/interlock/index.md | 46 ++++ .../ucp/3.0/guides/interlock/install/index.md | 82 +++++++ .../install/interlock_production_deploy.png | Bin 0 -> 31489 bytes .../3.0/guides/interlock/install/offline.md | 38 +++ .../guides/interlock/install/production.md | 84 +++++++ .../guides/interlock/intro/architecture.md | 39 ++++ .../ucp/3.0/guides/interlock/intro/index.md | 60 +++++ .../ucp/3.0/guides/interlock/ops/index.md | 36 +++ .../ucp/3.0/guides/interlock/ops/tuning.md | 35 +++ .../ucp/3.0/guides/interlock/usage/canary.md | 107 +++++++++ .../ucp/3.0/guides/interlock/usage/context.md | 57 +++++ .../interlock/usage/host-mode-networking.md | 145 ++++++++++++ .../ucp/3.0/guides/interlock/usage/index.md | 61 +++++ .../usage/interlock_service_clusters.png | Bin 0 -> 22783 bytes .../usage/interlock_ssl_passthrough.png | Bin 0 -> 24098 bytes .../usage/interlock_ssl_termination.png | Bin 0 -> 22517 bytes .../3.0/guides/interlock/usage/redirects.md | 64 +++++ .../interlock/usage/service-clusters.md | 200 ++++++++++++++++ .../3.0/guides/interlock/usage/sessions.md | 130 +++++++++++ .../ucp/3.0/guides/interlock/usage/ssl.md | 220 ++++++++++++++++++ .../3.0/guides/interlock/usage/websockets.md | 35 +++ 26 files changed, 1667 insertions(+) create mode 100644 datacenter/ucp/3.0/guides/interlock/configuration/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/configuration/service-labels.md create mode 100644 datacenter/ucp/3.0/guides/interlock/extensions/haproxy.md create mode 100644 datacenter/ucp/3.0/guides/interlock/extensions/nginx.md create mode 100644 datacenter/ucp/3.0/guides/interlock/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/install/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/install/interlock_production_deploy.png create mode 100644 datacenter/ucp/3.0/guides/interlock/install/offline.md create mode 100644 datacenter/ucp/3.0/guides/interlock/install/production.md create mode 100644 datacenter/ucp/3.0/guides/interlock/intro/architecture.md create mode 100644 datacenter/ucp/3.0/guides/interlock/intro/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/ops/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/ops/tuning.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/canary.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/context.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/host-mode-networking.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/index.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/interlock_service_clusters.png create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/interlock_ssl_passthrough.png create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/interlock_ssl_termination.png create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/redirects.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/service-clusters.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/sessions.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/ssl.md create mode 100644 datacenter/ucp/3.0/guides/interlock/usage/websockets.md diff --git a/_data/toc.yaml b/_data/toc.yaml index 89294ea621..9311db83ef 100644 --- a/_data/toc.yaml +++ b/_data/toc.yaml @@ -1732,6 +1732,62 @@ manuals: title: Manage secrets - path: /datacenter/ucp/3.0/guides/user/secrets/grant-revoke-access/ title: Grant access to secrets + - sectiontitle: Interlock + section: + - title: Interlock overview + path: /datacenter/ucp/3.0/guides/interlock/ + - sectiontitle: Introduction + section: + - title: What is Interlock + path: /datacenter/ucp/3.0/guides/interlock/intro/ + - title: Architecture + path: /datacenter/ucp/3.0/guides/interlock/intro/architecture/ + - sectiontitle: Deployment + section: + - title: Get started + path: /datacenter/ucp/3.0/guides/interlock/install/ + - title: Production + path: /datacenter/ucp/3.0/guides/interlock/install/production/ + - title: Offline install + path: /datacenter/ucp/3.0/guides/interlock/install/offline/ + - sectiontitle: Configuration + section: + - title: Interlock + path: /datacenter/ucp/3.0/guides/interlock/configuration/ + - title: Service labels + path: /datacenter/ucp/3.0/guides/interlock/configuration/service-labels/ + - sectiontitle: Extensions + section: + - title: Nginx + path: /datacenter/ucp/3.0/guides/interlock/extensions/nginx/ + - title: HAProxy + path: /datacenter/ucp/3.0/guides/interlock/extensions/haproxy/ + - sectiontitle: Deploy apps with Interlock + section: + - title: Basic deployment + path: /datacenter/ucp/3.0/guides/interlock/usage/ + - title: Applications with SSL + path: /datacenter/ucp/3.0/guides/interlock/usage/ssl/ + - title: Application redirects + path: /datacenter/ucp/3.0/guides/interlock/usage/redirects/ + - title: Persistent (sticky) sessions + path: /datacenter/ucp/3.0/guides/interlock/usage/sessions/ + - title: Websockets + path: /datacenter/ucp/3.0/guides/interlock/usage/websockets/ + - title: Canary application instances + path: /datacenter/ucp/3.0/guides/interlock/usage/canary/ + - title: Service clusters + path: /datacenter/ucp/3.0/guides/interlock/usage/service-clusters/ + - title: Context/Path based routing + path: /datacenter/ucp/3.0/guides/interlock/usage/context/ + - title: Host mode networking + path: /datacenter/ucp/3.0/guides/interlock/usage/host-mode-networking/ + - sectiontitle: Operations + section: + - title: Updates + path: /datacenter/ucp/3.0/guides/interlock/ops/ + - title: Tuning + path: /datacenter/ucp/3.0/guides/interlock/ops/tuning/ - path: /datacenter/ucp/3.0/reference/api/ title: API reference - path: /datacenter/ucp/3.0/guides/release-notes/ diff --git a/datacenter/ucp/3.0/guides/interlock/configuration/index.md b/datacenter/ucp/3.0/guides/interlock/configuration/index.md new file mode 100644 index 0000000000..918c23d1e5 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/configuration/index.md @@ -0,0 +1,87 @@ +--- +title: Configure Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +Interlock configuration is managed via file as [TOML](https://github.com/toml-lang/toml). +The following will describe how to configure the various components of Interlock. + +## Core +The core configuration handles the Interlock service itself. The following options +are available: + +| Option | Type | Description | +|:-------------------|:------------|:----------------------------------------------------------------------------------------------| +| `ListenAddr` | string | address to serve the Interlock GRPC API (default: `:8080`) | +| `DockerURL` | string | path to the socket or TCP address to the Docker API (default: `unix:///var/run/docker.sock`) | +| `TLSCACert` | string | path to the CA certificate for connecting securely to the Docker API | +| `TLSCert` | string | path to the certificate for connecting securely to the Docker API | +| `TLSKey` | string | path to the key for connecting securely to the Docker API | +| `AllowInsecure` | bool | skip TLS verification when connecting to the Docker API via TLS | +| `PollInterval` | string | interval to poll the Docker API for changes (default: `3s`) | +| `EndpointOverride` | string | override the default GRPC API endpoint for extensions (by default this is detected via Swarm) | +| `Extensions` | []Extension | array of extensions as listed below | + +## Extension +Interlock must contain at least one extension to service traffic. The following options are +available to configure the extensions. + +| Option | Type | Description | +|:-------------------|:-----------------------------|:---------------------------------------------------------------------| +| `Image` | string | name of the Docker Image to use for the extension service | +| `Args` | []string | arguments to be passed to the Docker extension service upon creation | +| `Labels` | map[string]string | labels to be added to the extension service | +| `ServiceName` | string | name of the extension service | +| `ProxyImage` | string | name of the Docker Image to use for the proxy service | +| `ProxyArgs` | []string | arguments to be passed to the Docker proxy service upon creation | +| `ProxyLabels` | map[string]string | labels to be added to the proxy service | +| `ProxyServiceName` | string | name of the proxy service | +| `ProxyConfigPath` | string | path in the service for the generated proxy config | +| `ServiceCluster` | string | name of the cluster this extension services | +| `PublishMode` | string (`ingress` or `host`) | publish mode that the proxy service uses | +| `PublishedPort` | int | port that the proxy service serves non-SSL traffic | +| `PublishedSSLPort` | int | port that the proxy service serves SSL traffic | +| `Template` | string | Docker config object that is used as the extension template | +| `Config` | Config | proxy configuration used by the extensions as listed below | + +## Proxy +The following options are made available to the extensions. The extensions use whichever they need to configure +the proxy service. This provides a way for the user to provide overrides to the extension configuration. + +Interlock passes extension configuration through directly to the extension. Therefore, each extension has +different configuration options available. See the docs for each extension for the officially supported options. + +## Example Configuration +The following is an example configuration to use with the Nginx extension. + +```toml +ListenAddr = ":8080" +DockerURL = "unix:///var/run/docker.sock" +PollInterval = "3s" + +[Extensions] + [Extensions.default] + Image = "docker/interlock-extension-nginx:latest" + Args = ["-D"] + ProxyImage = "nginx:alpine" + ProxyArgs = [] + ProxyConfigPath = "/etc/nginx/nginx.conf" + ServiceCluster = "" + PublishMode = "ingress" + PublishedPort = 80 + TargetPort = 80 + PublishedSSLPort = 443 + TargetSSLPort = 443 + [Extensions.default.Config] + User = "nginx" + PidPath = "/var/run/proxy.pid" + WorkerProcesses = 1 + RlimitNoFile = 65535 + MaxConnections = 2048 + [Extensions.default.Labels] + extension_name = "defaultExtension" + [Extensions.default.ProxyLabels] + proxy_name = "defaultProxy" +``` diff --git a/datacenter/ucp/3.0/guides/interlock/configuration/service-labels.md b/datacenter/ucp/3.0/guides/interlock/configuration/service-labels.md new file mode 100644 index 0000000000..6484f08f36 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/configuration/service-labels.md @@ -0,0 +1,27 @@ +--- +title: Interlock service labels +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +Applications that publish using Interlock use service labels to configure how they are published. +The following table describes the available options and what they do. + +| Label | Description | Example | +|:---------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------| +| `com.docker.lb.hosts` | Comma separated list of the hosts that the service should serve | `example.com,test.com` | +| `com.docker.lb.port` | Port to use for internal upstream communication | `8080` | +| `com.docker.lb.network` | Name of network the proxy service should attach to for upstream connectivity | `app-network-a` | +| `com.docker.lb.context_root` | Context or path to use for the application | `/app` | +| `com.docker.lb.context_root_rewrite` | Boolean to enable rewrite for the context root | `true` | +| `com.docker.lb.ssl_only` | Boolean to force SSL for application | `true` | +| `com.docker.lb.ssl_cert` | Docker secret to use for the SSL certificate | `example.com.cert` | +| `com.docker.lb.ssl_key` | Docker secret to use for the SSL key | `example.com.key` | +| `com.docker.lb.websocket_endpoints` | Comma separated list of endpoints to configure to be upgraded for websockets | `/ws,/foo` | +| `com.docker.lb.service_cluster` | Name of the service cluster to use for the application | `us-east` | +| `com.docker.lb.ssl_backend` | Enable SSL communication to the upstreams | `true` | +| `com.docker.lb.ssl_backend_tls_verify` | Verification mode for the upstream TLS | `none` | +| `com.docker.lb.sticky_session_cookie` | Cookie to use for sticky sessions | `none` | +| `com.docker.lb.redirects` | Semi-colon separated list of redirects to add in the format of `,`. Example: (`http://old.example.com,http://new.example.com;`) | `none` | +| `com.docker.lb.ssl_passthrough` | Enable SSL passthrough | `false` | diff --git a/datacenter/ucp/3.0/guides/interlock/extensions/haproxy.md b/datacenter/ucp/3.0/guides/interlock/extensions/haproxy.md new file mode 100644 index 0000000000..714a1beff5 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/extensions/haproxy.md @@ -0,0 +1,28 @@ +--- +title: Use HAProxy with Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +The following configuration options are available: + +| Option | Type | Description | +|:--------------------|:-------|:--------------------------------------------------------------------------------| +| `PidPath` | string | path to the pid file for the proxy service | +| `MaxConnections` | int | maximum number of connections for proxy service | +| `ConnectTimeout` | int | timeout in seconds for clients to connect | +| `ClientTimeout` | int | timeout in seconds for the service to send a request to the proxied upstream | +| `ServerTimeout` | int | timeout in seconds for the service to read a response from the proxied upstream | +| `AdminUser` | string | username to be used with authenticated access to the proxy service | +| `AdminPass` | string | password to be used with authenticated access to the proxy service | +| `SSLOpts` | string | options to be passed when configuring SSL | +| `SSLDefaultDHParam` | int | size of DH parameters | +| `SSLVerify` | string | SSL client verification | +| `SSLCiphers` | string | SSL ciphers to use for the proxy service | +| `SSLProtocols` | string | enable the specified TLS protocols | + +## Notes + +When using SSL termination the certificate and key must be combined into a single certificate (i.e. `cat cert.pem key.pem > combined.pem`). The HAProxy extension +will use the certificate label only to configure SSL. diff --git a/datacenter/ucp/3.0/guides/interlock/extensions/nginx.md b/datacenter/ucp/3.0/guides/interlock/extensions/nginx.md new file mode 100644 index 0000000000..5639efc3b6 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/extensions/nginx.md @@ -0,0 +1,30 @@ +--- +title: Use NGINX with Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +The following configuration options are available for the Nginx extension: + +| Option | Type | Description | +|:------------------------|:-------|:----------------------------------------------------------------------------------------------------| +| `User` | string | user to be used in the proxy | +| `PidPath` | string | path to the pid file for the proxy service | +| `MaxConnections` | int | maximum number of connections for proxy service | +| `ConnectTimeout` | int | timeout in seconds for clients to connect | +| `SendTimeout` | int | timeout in seconds for the service to send a request to the proxied upstream | +| `ReadTimeout` | int | timeout in seconds for the service to read a response from the proxied upstream | +| `IPHash` | bool | specifies that requests are distributed between servers based on client IP addresses | +| `SSLOpts` | string | options to be passed when configuring SSL | +| `SSLDefaultDHParam` | int | size of DH parameters | +| `SSLDefaultDHParamPath` | string | path to DH parameters file | +| `SSLVerify` | string | SSL client verification | +| `WorkerProcesses` | string | number of worker processes for the proxy service | +| `RLimitNoFile` | int | number of maxiumum open files for the proxy service | +| `SSLCiphers` | string | SSL ciphers to use for the proxy service | +| `SSLProtocols` | string | enable the specified TLS protocols | +| `AccessLogPath` | string | Path to use for access logs (default: `/dev/stdout`) | +| `ErrorLogPath` | string | Path to use for error logs (default: `/dev/stdout`) | +| `MainLogFormat` | string | [Format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) to use for main logger | +| `TraceLogFormat` | string | [Format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) to use for trace logger | diff --git a/datacenter/ucp/3.0/guides/interlock/index.md b/datacenter/ucp/3.0/guides/interlock/index.md new file mode 100644 index 0000000000..310defd38a --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/index.md @@ -0,0 +1,46 @@ +--- +title: Interlock overview +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +Interlock is an application routing and load balancing system for Docker Swarm. It uses +the Docker Remote API to automatically configure extensions such as Nginx or HAProxy for +application traffic. + +## About + +- [Introduction](intro/index.md) + - [What is Interlock](intro/index.md) + - [Architecture](intro/architecture.md) +- [Deployment](install/) + - [Requirements](install/index.md#requirements) + - [Installation](install/index.md#deployment) + +## Configuration + +- [Interlock configuration](configuration/index.md) +- [Service labels](configuration/service-labels.md) + +## Extensions + +- [NGINX](extensions/nginx.md) +- [HAProxy](extensions/haproxy.md) + +## Usage + +- [Basic deployment](usage/index.md) +- [Applications with SSL](usage/ssl.md) +- [Application redirects](usage/redirects.md) +- [Persistent (sticky) sessions](usage/sessions.md) +- [Websockets](usage/websockets.md) +- [Canary application instances](usage/canary.md) +- [Service clusters](usage/service-clusters.md) +- [Context/path based routing](usage/context.md) +- [Host mode networking](usage/host-mode-networking.md) + +## Operations + +- [Updates](ops/index.md) +- [Tuning](ops/tuning.md) diff --git a/datacenter/ucp/3.0/guides/interlock/install/index.md b/datacenter/ucp/3.0/guides/interlock/install/index.md new file mode 100644 index 0000000000..da9c04f39c --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/install/index.md @@ -0,0 +1,82 @@ +--- +title: Get started with Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + + +## Requirements + +- [Docker](https://www.docker.com) version 17.06+ is required to use Interlock +- Docker must be running in [Swarm mode](https://docs.docker.com/engine/swarm/) +- Internet access (see [Offline Installation](offline.md) for installing without internet access) + +## Deployment +Interlock uses a configuration file for the core service. The following is an example config +to get started. In order to utilize the deployment and recovery features in Swarm we will +create a Docker Config object: + +```bash +$> cat << EOF | docker config create service.interlock.conf - +ListenAddr = ":8080" +DockerURL = "unix:///var/run/docker.sock" +PollInterval = "3s" + +[Extensions] + [Extensions.default] + Image = "interlockpreview/interlock-extension-nginx:2.0.0-preview" + Args = ["-D"] + ProxyImage = "nginx:alpine" + ProxyArgs = [] + ProxyConfigPath = "/etc/nginx/nginx.conf" + ServiceCluster = "" + PublishMode = "ingress" + PublishedPort = 80 + TargetPort = 80 + PublishedSSLPort = 443 + TargetSSLPort = 443 + [Extensions.default.Config] + User = "nginx" + PidPath = "/var/run/proxy.pid" + WorkerProcesses = 1 + RlimitNoFile = 65535 + MaxConnections = 2048 +EOF +oqkvv1asncf6p2axhx41vylgt +``` + +Next we will create a dedicated network for Interlock and the extensions: + +```bash +$> docker network create -d overlay interlock +``` + +Now we can create the Interlock service. Note the requirement to constrain to a manager. The +Interlock core service must have access to a Swarm manager, however the extension and proxy services +are recommended to run on workers. See the [Production](production.md) section for more information +on setting up for an production environment. + +```bash +$> docker service create \ + --name interlock \ + --mount src=/var/run/docker.sock,dst=/var/run/docker.sock,type=bind \ + --network interlock \ + --constraint node.role==manager \ + --config src=service.interlock.conf,target=/config.toml \ + interlockpreview/interlock:2.0.0-preview -D run -c /config.toml +sjpgq7h621exno6svdnsvpv9z +``` + +There should be three (3) services created. One for the Interlock service, +one for the extension service and one for the proxy service: + +```bash +$> docker service ls +ID NAME MODE REPLICAS IMAGE PORTS +lheajcskcbby modest_raman replicated 1/1 nginx:alpine *:80->80/tcp *:443->443/tcp +oxjvqc6gxf91 keen_clarke replicated 1/1 interlockpreview/interlock-extension-nginx:2.0.0-preview +sjpgq7h621ex interlock replicated 1/1 interlockpreview/interlock:2.0.0-preview +``` + +The Interlock traffic layer is now deployed. Continue with the [Deploying Applications](/usage/index.md) to publish applications. diff --git a/datacenter/ucp/3.0/guides/interlock/install/interlock_production_deploy.png b/datacenter/ucp/3.0/guides/interlock/install/interlock_production_deploy.png new file mode 100644 index 0000000000000000000000000000000000000000..0aeb14fd258f12e72d5a5135d987aaa38ef41782 GIT binary patch literal 31489 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 ai8J)k<+nB+yL-rMr%s$X{_?29&Hn?sx2IkJ literal 0 HcmV?d00001 diff --git a/datacenter/ucp/3.0/guides/interlock/install/offline.md b/datacenter/ucp/3.0/guides/interlock/install/offline.md new file mode 100644 index 0000000000..d28a3540d9 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/install/offline.md @@ -0,0 +1,38 @@ +--- +title: Deploy Interlock offline +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +To install Interlock on a Docker cluster without internet access the Docker images will +need to be loaded. This guide will show how to export the images from a local Docker +engine to then be loaded to the Docker Swarm cluster. + +First, using an existing Docker engine save the images: + +```bash +$> docker save docker/interlock:latest > interlock.tar +$> docker save docker/interlock-extension-nginx:latest > interlock-extension-nginx.tar +$> docker save nginx:alpine > nginx.tar +``` + +Note: replace `docker/interlock-extension-nginx:latest` and `nginx:alpine` with the corresponding +extension and proxy image if you are not using Nginx. + +You should have two files: + +- `interlock.tar`: This is the core Interlock application +- `interlock-extension-nginx.tar`: This is the Interlock extension for Nginx +- `nginx:alpine`: This is the official Nginx image based on Alpine + +Copy these files to each node in the Docker Swarm cluster and run the following to load each image: + +```bash +$> docker load < interlock.tar +$> docker load < interlock-extension-nginx.tar +$> docker load < nginx:alpine.tar +``` + +After running on each node, you can continue to the [Deployment](index.md#deployment) section to +continue the installation. diff --git a/datacenter/ucp/3.0/guides/interlock/install/production.md b/datacenter/ucp/3.0/guides/interlock/install/production.md new file mode 100644 index 0000000000..b5fd014e28 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/install/production.md @@ -0,0 +1,84 @@ +--- +title: Deploy Interlock for Production +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +## Production Deployment +In this section you will find documentation on configuring Interlock +for a production environment. If you have not yet deployed Interlock please +see the [Getting Started](index.md) section as this information builds upon the +basic deployment. This example will not cover the actual deployment of infrastructure. +It assumes you have a vanilla Swarm cluster (`docker init` and `docker swarm join` from the nodes). +See the [Swarm](https://docs.docker.com/engine/swarm/) documentation if you need help +getting a Swarm cluster deployed. + +In this example we will configure an eight (8) node Swarm cluster. There are three (3) managers +and five (5) workers. Two of the workers are configured with node labels to be dedicated +ingress cluster load balancer nodes. These will receive all application traffic. +There is also an upstream load balancer (such as an Elastic Load Balancer or F5). The upstream +load balancers will be statically configured for the two load balancer worker nodes. + +This configuration has several benefits. The management plane is both isolated and redundant. +No application traffic hits the managers and application ingress traffic can be routed +to the dedicated nodes. These nodes can be configured with higher performance network interfaces +to provide more bandwidth for the user services. + +![Interlock 2.0 Production Deployment](interlock_production_deploy.png) + +## Node Labels +We will configure the load balancer worker nodes (`lb-00` and `lb-01`) with node labels in order to pin the Interlock Proxy +service. Once you are logged into one of the Swarm managers run the following to add node labels +to the dedicated ingress workers: + +```bash +$> docker node update --label-add nodetype=loadbalancer lb-00 +lb-00 +$> docker node update --label-add nodetype=loadbalancer lb-01 +lb-01 +``` + +You can inspect each node to ensure the labels were successfully added: + +```bash +{% raw %} +$> docker node inspect -f '{{ .Spec.Labels }}' lb-00 +map[nodetype:loadbalancer] +$> docker node inspect -f '{{ .Spec.Labels }}' lb-01 +map[nodetype:loadbalancer] +{% endraw %} +``` + +## Configure Proxy Service +Once we have the node labels we can re-configure the Interlock Proxy service to be constrained to those +workers. Again, from a manager run the following to pin the proxy service to the ingress workers: + +```bash +$> docker service update --replicas=2 \ + --constraint-add node.labels.nodetype==loadbalancer \ + --stop-signal SIGTERM \ + --stop-grace-period=5s \ + $(docker service ls -f 'label=type=com.docker.interlock.core.proxy' -q) +``` + +This updates the proxy service to have two (2) replicas and ensure they are constrained to +the workers with the label `nodetype==loadbalancer` as well as configure the stop signal for the tasks +to be a `SIGTERM` with a grace period of five (5) seconds. This will ensure that Nginx closes the connections +before exiting to ensure the client request is finished. + +Inspect the service to ensure the replicas have started on the desired nodes: + +```bash +$> docker service ps $(docker service ls -f 'label=type=com.docker.interlock.core.proxy' -q) +ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS +o21esdruwu30 interlock-proxy.1 nginx:alpine lb-01 Running Preparing 3 seconds ago +n8yed2gp36o6 \_ interlock-proxy.1 nginx:alpine mgr-01 Shutdown Shutdown less than a second ago +aubpjc4cnw79 interlock-proxy.2 nginx:alpine lb-00 Running Preparing 3 seconds ago +``` + +Once configured you can update the settings in the upstream load balancer (ELB, F5, etc) with the +addresses of the dedicated ingress workers. This will direct all traffic to these nodes. + +You have now configured Interlock for a dedicated ingress production environment. See the [Configuration](/config/interlock/) section +if you want to continue tuning. diff --git a/datacenter/ucp/3.0/guides/interlock/intro/architecture.md b/datacenter/ucp/3.0/guides/interlock/intro/architecture.md new file mode 100644 index 0000000000..8ea52cca76 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/intro/architecture.md @@ -0,0 +1,39 @@ +--- +title: Interlock architecture +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +The following are definitions that are used: + +- Cluster: A group of compute resources running Docker +- Swarm: A Docker cluster running in Swarm mode +- Upstream: An upstream container that serves an application +- Proxy Service: A service that provides load balancing and proxying (such as Nginx) +- Extension Service: A helper service that configures the proxy service +- Service Cluster: A service cluster is an Interlock extension+proxy service +- GRPC: A high-performance RPC framework + +## Services +Interlock runs entirely as Docker Swarm services. There are three core services +in an Interlock routing layer: core, extension and proxy. + +## Core +The core service is responsible for interacting with the Docker Remote API and building +an upstream configuration for the extensions. This is served on a GRPC API that the +extensions are configured to access. + +## Extension +The extension service is a helper service that queries the Interlock GRPC API for the +upstream configuration. The extension service uses this to configure +the proxy service. For proxy services that use files such as Nginx or HAProxy the +extension service generates the file and sends it to Interlock using the GRPC API. Interlock +then updates the corresponding Docker Config object for the proxy service. + +## Proxy +The proxy service handles the actual requests for the upstream application services. These +are configured using the data created by the corresponding extension service. + +Interlock manages both the extension and proxy service updates for both configuration changes +and application service deployments. There is no intervention from the operator required. diff --git a/datacenter/ucp/3.0/guides/interlock/intro/index.md b/datacenter/ucp/3.0/guides/interlock/intro/index.md new file mode 100644 index 0000000000..92a4566e34 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/intro/index.md @@ -0,0 +1,60 @@ +--- +title: What is Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +Interlock is an application routing proxy service for Docker. + +## Design Goals + +- Fully integrate with Docker (Swarm, Services, Secrets, Configs) +- Enhanced configuration (context roots, TLS, zero downtime deploy, rollback) +- Support external load balancers (nginx, haproxy, F5, etc) via extensions +- Least privilege for extensions (no Docker API access) + +Interlock was designed to be a first class application routing layer for Docker. +The following are the high level features it provides: + +## Automatic Configuration +Interlock uses the Docker API for configuration. The user does not have to manually +update or restart anything to make services available. + +## Native Swarm Support +Interlock is fully Docker native. It runs on Docker Swarm and routes traffic using +cluster networking and Docker services. + +## High Availability +Interlock runs as Docker services which are highly available and handle failures gracefully. + +## Scalability +Interlock uses a modular design where the proxy service is separate. This allows an +operator to individually customize and scale the proxy layer to whatever demand. This is +transparent to the user and causes no downtime. + +## SSL +Interlock leverages Docker Secrets to securely store and use SSL certificates for services. Both +SSL termination and TCP passthrough are supported. + +## Context Based Routing +Interlock supports advanced application request routing by context or path. + +## Host Mode Networking +Interlock supports running the proxy and application services in "host" mode networking allowing +the operator to bypass the routing mesh completely. This is beneficial if you want +maximum performance for your applications. + +## Blue-Green and Canary Service Deployment +Interlock supports blue-green service deployment allowing an operator to deploy a new application +while the current version is serving. Once traffic is verified to the new application the operator +can scale the older version to zero. If there is a problem the operation is quickly reversible. + +## Service Cluster Support +Interlock supports multiple extension+proxy combinations allowing for operators to partition load +balancing resources for uses such as region or organization based load balancing. + +## Least Privilege +Interlock supports (and recommends) being deployed where the load balancing +proxies do not need to be colocated with a Swarm manager. This makes the +deployment more secure by not exposing the Docker API access to the extension or proxy services. diff --git a/datacenter/ucp/3.0/guides/interlock/ops/index.md b/datacenter/ucp/3.0/guides/interlock/ops/index.md new file mode 100644 index 0000000000..d840dc7ff6 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/ops/index.md @@ -0,0 +1,36 @@ +--- +title: Update Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +The following describes how to update Interlock. There are two parts +to the upgrade. First, the Interlock configuration must be updated +to specify the new extension and/or proxy image versions. Then the Interlock +service will be updated. + +First we will create the new configuration: + +```bash +$> docker config create service.interlock.conf.v2 +``` + +Then you can update the Interlock service to remove the old and use the new: + +```bash +$> docker service update --config-rm service.interlock.conf interlock +$> docker service update --config-add source=service.interlock.conf.v2,target=/config.toml interlock +``` + +Next update the Interlock service to use the new image: + +```bash +$> docker service update \ + --image interlockpreview/interlock@sha256:d173014908eb09e9a70d8e5ed845469a61f7cbf4032c28fad0ed9af3fc04ef51 \ + interlock +``` + +This will update the Interlock core service to use the `sha256:d173014908eb09e9a70d8e5ed845469a61f7cbf4032c28fad0ed9af3fc04ef51` +version of Interlock. Interlock will start and check the config object which has the new extension version and will +perform a rolling deploy to update all extensions. diff --git a/datacenter/ucp/3.0/guides/interlock/ops/tuning.md b/datacenter/ucp/3.0/guides/interlock/ops/tuning.md new file mode 100644 index 0000000000..e8d93c6e04 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/ops/tuning.md @@ -0,0 +1,35 @@ +--- +title: Tune Interlock +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +It is [recommended](/install/production/) to constrain the proxy service to multiple dedicated worker nodes. +Here are a few other tips for tuning: + +## Stopping +You can adjust the stop signal and period by using the `stop-signal` and `stop-grace-period` settings. For example, +to set the stop signal to `SIGTERM` and grace period to ten (10) seconds use the following: + +```bash +$> docker service update --stop-signal=SIGTERM --stop-grace-period=10s interlock-proxy +``` + +## Update Actions +In the event of an update failure the default Swarm action is to "pause". This will cause no more updates to happen from +Interlock until operator intervention. You can change this behavior by setting the `update-failure-action` setting. For example, +to automatically rollback to the previous configuration upon failure use the following: + +```bash +$> docker service update --update-failure-action=rollback interlock-proxy +``` + +## Update Interval +By default Interlock will configure the proxy service by rolling update. If you would like to have more time between proxy +updates, such as to let the service settle, you can use the `update-delay` setting. For example, if you want to have +thirty (30) seconds between updates you can use the following: + +```bash +$> docker service update --update-delay=30s interlock=proxy +``` diff --git a/datacenter/ucp/3.0/guides/interlock/usage/canary.md b/datacenter/ucp/3.0/guides/interlock/usage/canary.md new file mode 100644 index 0000000000..9118fc582b --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/canary.md @@ -0,0 +1,107 @@ +--- +title: Canary application instances +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a service and deploy an updated service as canary instances. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the initial service: + +```bash +$> docker service create \ + --name demo-v1 \ + --network demo \ + --detach=false \ + --replicas=4 \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --env METADATA="demo-version-1" \ + ehazlett/docker-demo +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local`: + +```bash +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to demo.local (127.0.0.1) port 80 (#0) +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Wed, 08 Nov 2017 20:28:26 GMT +< Content-Type: text/plain; charset=utf-8 +< Content-Length: 120 +< Connection: keep-alive +< Set-Cookie: session=1510172906715624280; Path=/; Expires=Thu, 09 Nov 2017 20:28:26 GMT; Max-Age=86400 +< x-request-id: f884cf37e8331612b8e7630ad0ee4e0d +< x-proxy-id: 5ad7c31f9f00 +< x-server-info: interlock/2.0.0-development (147ff2b1) linux/amd64 +< x-upstream-addr: 10.0.2.4:8080 +< x-upstream-response-time: 1510172906.714 +< +{"instance":"df20f55fc943","version":"0.1","metadata":"demo-version-1","request_id":"f884cf37e8331612b8e7630ad0ee4e0d"} +``` + +Notice the `metadata` with `demo-version-1`. + +Now we will deploy a "new" version: + +```bash +$> docker service create \ + --name demo-v2 \ + --network demo \ + --detach=false \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --env METADATA="demo-version-2" \ + --env VERSION="0.2" \ + ehazlett/docker-demo +``` + +Since this has a replica of one (1) and the initial version has four (4) replicas 20% of application traffic +will be sent to `demo-version-2`: + +```bash +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"23d9a5ec47ef","version":"0.1","metadata":"demo-version-1","request_id":"060c609a3ab4b7d9462233488826791c"} +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"f42f7f0a30f9","version":"0.1","metadata":"demo-version-1","request_id":"c848e978e10d4785ac8584347952b963"} +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"c2a686ae5694","version":"0.1","metadata":"demo-version-1","request_id":"724c21d0fb9d7e265821b3c95ed08b61"} +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"1b0d55ed3d2f","version":"0.2","metadata":"demo-version-2","request_id":"b86ff1476842e801bf20a1b5f96cf94e"} +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"c2a686ae5694","version":"0.1","metadata":"demo-version-1","request_id":"724c21d0fb9d7e265821b3c95ed08b61"} +``` + +To increase traffic to the new version add more replicas with `docker scale`: + +```bash +$> docker service scale demo-v2=4 +demo-v2 +``` + +To complete the upgrade, scale the `demo-v1` service to zero (0): + +```bash +$> docker service scale demo-v1=0 +demo-v1 +``` + +This will route all application traffic to the new version. If you need to rollback, simply scale the v1 service +back up and v2 down. diff --git a/datacenter/ucp/3.0/guides/interlock/usage/context.md b/datacenter/ucp/3.0/guides/interlock/usage/context.md new file mode 100644 index 0000000000..deaa539397 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/context.md @@ -0,0 +1,57 @@ +--- +title: Context/path based routing +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a service using context or path based routing. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the initial service: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.context_root=/app \ + --label com.docker.lb.context_root_rewrite=true \ + --env METADATA="demo-context-root" \ + ehazlett/docker-demo +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local`: + +```bash +$> curl -vs -H "Host: demo.local" http://127.0.0.1/app/ +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) +> GET /app/ HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Fri, 17 Nov 2017 14:25:17 GMT +< Content-Type: text/html; charset=utf-8 +< Transfer-Encoding: chunked +< Connection: keep-alive +< x-request-id: 077d18b67831519defca158e6f009f82 +< x-proxy-id: 77c0c37d2c46 +< x-server-info: interlock/2.0.0-dev (732c77e7) linux/amd64 +< x-upstream-addr: 10.0.1.3:8080 +< x-upstream-response-time: 1510928717.306 +... +``` diff --git a/datacenter/ucp/3.0/guides/interlock/usage/host-mode-networking.md b/datacenter/ucp/3.0/guides/interlock/usage/host-mode-networking.md new file mode 100644 index 0000000000..a97de9fbce --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/host-mode-networking.md @@ -0,0 +1,145 @@ +--- +title: Host mode networking +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In some scenarios operators cannot use the overlay networks. Interlock supports +host mode networking in a variety of ways (proxy only, Interlock only, application only, hybrid). + +In this example we will configure an eight (8) node Swarm cluster that uses host mode +networking to route traffic without using overlay networks. There are three (3) managers +and five (5) workers. Two of the workers are configured with node labels to be dedicated +ingress cluster load balancer nodes. These will receive all application traffic. + +This example will not cover the actual deployment of infrastructure. +It assumes you have a vanilla Swarm cluster (`docker init` and `docker swarm join` from the nodes). +See the [Swarm](https://docs.docker.com/engine/swarm/) documentation if you need help +getting a Swarm cluster deployed. + +Note: when using host mode networking you will not be able to use the DNS service discovery as that +requires overlay networking. You can use other tooling such as [Registrator](https://github.com/gliderlabs/registrator) +that will give you that functionality if needed. + +We will configure the load balancer worker nodes (`lb-00` and `lb-01`) with node labels in order to pin the Interlock Proxy +service. Once you are logged into one of the Swarm managers run the following to add node labels +to the dedicated load balancer worker nodes: + +```bash +$> docker node update --label-add nodetype=loadbalancer lb-00 +lb-00 +$> docker node update --label-add nodetype=loadbalancer lb-01 +lb-01 +``` + +You can inspect each node to ensure the labels were successfully added: + +```bash +{% raw %} +$> docker node inspect -f '{{ .Spec.Labels }}' lb-00 +map[nodetype:loadbalancer] +$> docker node inspect -f '{{ .Spec.Labels }}' lb-01 +map[nodetype:loadbalancer] +{% endraw %} +``` + +Next, we will create a configuration object for Interlock that specifies host mode networking: + +```bash +$> cat << EOF | docker config create service.interlock.conf - +ListenAddr = ":8080" +DockerURL = "unix:///var/run/docker.sock" +PollInterval = "3s" + +[Extensions] + [Extensions.default] + Image = "interlockpreview/interlock-extension-nginx:2.0.0-preview" + Args = [] + ServiceName = "interlock-ext" + ProxyImage = "nginx:alpine" + ProxyArgs = [] + ProxyServiceName = "interlock-proxy" + ProxyConfigPath = "/etc/nginx/nginx.conf" + PublishMode = "host" + PublishedPort = 80 + TargetPort = 80 + PublishedSSLPort = 443 + TargetSSLPort = 443 + [Extensions.default.Config] + User = "nginx" + PidPath = "/var/run/proxy.pid" + WorkerProcesses = 1 + RlimitNoFile = 65535 + MaxConnections = 2048 +EOF +oqkvv1asncf6p2axhx41vylgt +``` + +Note the `PublishMode = "host"` setting. This instructs Interlock to configure the proxy service for host mode networking. + +Now we can create the Interlock service also using host mode networking: + +```bash +$> docker service create \ + --name interlock \ + --mount src=/var/run/docker.sock,dst=/var/run/docker.sock,type=bind \ + --constraint node.role==manager \ + --publish mode=host,target=8080 \ + --config src=service.interlock.conf,target=/config.toml \ + interlockpreview/interlock:2.0.0-preview -D run -c /config.toml +sjpgq7h621exno6svdnsvpv9z +``` + +## Configure Proxy Services +Once we have the node labels we can re-configure the Interlock Proxy services to be constrained to the +workers. Again, from a manager run the following to pin the proxy services to the load balancer worker nodes: + +```bash +$> docker service update \ + --constraint-add node.labels.nodetype==loadbalancer \ + interlock-proxy +``` + +Now we can deploy the application: + +```bash +$> docker service create \ + --name demo \ + --detach=false \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --publish mode=host,target=8080 \ + --env METADATA="demo" \ + ehazlett/docker-demo +``` + +This will run the service using host mode networking. Each task for the service will have a high port (i.e. 32768) and use +the node IP address to connect. You can see this when inspecting the headers from the request: + +```bash +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +curl -vs -H "Host: demo.local" http://127.0.0.1/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Fri, 10 Nov 2017 15:38:40 GMT +< Content-Type: text/plain; charset=utf-8 +< Content-Length: 110 +< Connection: keep-alive +< Set-Cookie: session=1510328320174129112; Path=/; Expires=Sat, 11 Nov 2017 15:38:40 GMT; Max-Age=86400 +< x-request-id: e4180a8fc6ee15f8d46f11df67c24a7d +< x-proxy-id: d07b29c99f18 +< x-server-info: interlock/2.0.0-preview (17476782) linux/amd64 +< x-upstream-addr: 172.20.0.4:32768 +< x-upstream-response-time: 1510328320.172 +< +{"instance":"897d3c7b9e9c","version":"0.1","metadata":"demo","request_id":"e4180a8fc6ee15f8d46f11df67c24a7d"} +``` diff --git a/datacenter/ucp/3.0/guides/interlock/usage/index.md b/datacenter/ucp/3.0/guides/interlock/usage/index.md new file mode 100644 index 0000000000..5be38759da --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/index.md @@ -0,0 +1,61 @@ +--- +title: Basic deployment +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +Once Interlock has been deployed you are now ready to launch and publish applications. +Using [Service Labels](https://docs.docker.com/engine/reference/commandline/service_create/#set-metadata-on-a-service--l-label) +the service is configured to publish itself to the load balancer. + +Note: the examples below assume a DNS entry (or local hosts entry if you are testing local) exists +for each of the applications. + +To publish we will create a Docker Service using two labels: + +- `com.docker.lb.hosts` +- `com.docker.lb.port` + +The `com.docker.lb.hosts` label instructs Interlock where the service should be available. +The `com.docker.lb.port` label instructs what port the proxy service should use to access +the upstreams. + +In this example we will publish a demo service to the host `demo.local`. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will deploy the application: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + ehazlett/docker-demo +6r0wiglf5f3bdpcy6zesh1pzx +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local` + +```bash +$> curl -s -H "Host: demo.local" http://127.0.0.1/ping +{"instance":"c2f1afe673d4","version":"0.1",request_id":"7bcec438af14f8875ffc3deab9215bc5"} +``` + +To increase service capacity use the Docker Service [Scale](https://docs.docker.com/engine/swarm/swarm-tutorial/scale-service/) command: + +```bash +$> docker service scale demo=4 +demo scaled to 4 +``` + +The four service replicas will be configured as upstreams. The load balancer will balance traffic +across all service replicas. diff --git a/datacenter/ucp/3.0/guides/interlock/usage/interlock_service_clusters.png b/datacenter/ucp/3.0/guides/interlock/usage/interlock_service_clusters.png new file mode 100644 index 0000000000000000000000000000000000000000..84ad5f189830973c0b5bc51303093afbc482186c GIT binary patch literal 22783 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

##{p;=+(+v9lW6Yt_7RNs-osyA`c%Pxq{Yndw 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 R_NY)<8!P+cxMMyy{|jFYY<&O# literal 0 HcmV?d00001 diff --git a/datacenter/ucp/3.0/guides/interlock/usage/interlock_ssl_termination.png b/datacenter/ucp/3.0/guides/interlock/usage/interlock_ssl_termination.png new file mode 100644 index 0000000000000000000000000000000000000000..d49625d28738afc13905a57045c0aef998bc879f GIT binary patch literal 22517 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 Wf9i2}B~BS@J$85Xa%pr9yzw8U3-Nsb literal 0 HcmV?d00001 diff --git a/datacenter/ucp/3.0/guides/interlock/usage/redirects.md b/datacenter/ucp/3.0/guides/interlock/usage/redirects.md new file mode 100644 index 0000000000..8120dee2bd --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/redirects.md @@ -0,0 +1,64 @@ +--- +title: Application redirects +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a service and configure a redirect from `old.local` to `new.local`. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the service with the redirect: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --label com.docker.lb.hosts=old.local,new.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.redirects=http://old.local,http://new.local \ + --env METADATA="demo-new" \ + ehazlett/docker-demo +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://new.local` +with a redirect configured that sends `http://old.local` to `http://new.local`: + +```bash +$> curl -vs -H "Host: old.local" http://127.0.0.1 +* Rebuilt URL to: http://127.0.0.1/ +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) +> GET / HTTP/1.1 +> Host: old.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 302 Moved Temporarily +< Server: nginx/1.13.6 +< Date: Wed, 08 Nov 2017 19:06:27 GMT +< Content-Type: text/html +< Content-Length: 161 +< Connection: keep-alive +< Location: http://new.local/ +< x-request-id: c4128318413b589cafb6d9ff8b2aef17 +< x-proxy-id: 48854cd435a4 +< x-server-info: interlock/2.0.0-development (147ff2b1) linux/amd64 +< + +302 Found + +

302 Found

+
nginx/1.13.6
+ + +``` diff --git a/datacenter/ucp/3.0/guides/interlock/usage/service-clusters.md b/datacenter/ucp/3.0/guides/interlock/usage/service-clusters.md new file mode 100644 index 0000000000..6c362ba505 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/service-clusters.md @@ -0,0 +1,200 @@ +--- +title: Service clusters +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + + +In this example we will configure an eight (8) node Swarm cluster that uses service clusters +to route traffic to different proxies. There are three (3) managers +and five (5) workers. Two of the workers are configured with node labels to be dedicated +ingress cluster load balancer nodes. These will receive all application traffic. + +This example will not cover the actual deployment of infrastructure. +It assumes you have a vanilla Swarm cluster (`docker init` and `docker swarm join` from the nodes). +See the [Swarm](https://docs.docker.com/engine/swarm/) documentation if you need help +getting a Swarm cluster deployed. + +![Interlock Service Clusters](interlock_service_clusters.png) + +We will configure the load balancer worker nodes (`lb-00` and `lb-01`) with node labels in order to pin the Interlock Proxy +service. Once you are logged into one of the Swarm managers run the following to add node labels +to the dedicated ingress workers: + +```bash +$> docker node update --label-add nodetype=loadbalancer --label-add region=us-east lb-00 +lb-00 +$> docker node update --label-add nodetype=loadbalancer --label-add region=us-west lb-01 +lb-01 +``` + +You can inspect each node to ensure the labels were successfully added: + +```bash +{% raw %} +$> docker node inspect -f '{{ .Spec.Labels }}' lb-00 +map[nodetype:loadbalancer region:us-east] +$> docker node inspect -f '{{ .Spec.Labels }}' lb-01 +map[nodetype:loadbalancer region:us-west] +{% endraw %} +``` + +Next, we will create a configuration object for Interlock that contains multiple extensions with varying service clusters: + +```bash +$> cat << EOF | docker config create service.interlock.conf - +ListenAddr = ":8080" +DockerURL = "unix:///var/run/docker.sock" +PollInterval = "3s" + +[Extensions] + [Extensions.us-east] + Image = "interlockpreview/interlock-extension-nginx:2.0.0-preview" + Args = ["-D"] + ServiceName = "interlock-ext-us-east" + ProxyImage = "nginx:alpine" + ProxyArgs = [] + ProxyServiceName = "interlock-proxy-us-east" + ProxyConfigPath = "/etc/nginx/nginx.conf" + ServiceCluster = "us-east" + PublishMode = "host" + PublishedPort = 80 + TargetPort = 80 + PublishedSSLPort = 443 + TargetSSLPort = 443 + [Extensions.us-east.Config] + User = "nginx" + PidPath = "/var/run/proxy.pid" + WorkerProcesses = 1 + RlimitNoFile = 65535 + MaxConnections = 2048 + [Extensions.us-east.Labels] + ext_region = "us-east" + [Extensions.us-east.ProxyLabels] + proxy_region = "us-east" + + [Extensions.us-west] + Image = "interlockpreview/interlock-extension-nginx:2.0.0-preview" + Args = ["-D"] + ServiceName = "interlock-ext-us-west" + ProxyImage = "nginx:alpine" + ProxyArgs = [] + ProxyServiceName = "interlock-proxy-us-west" + ProxyConfigPath = "/etc/nginx/nginx.conf" + ServiceCluster = "us-west" + PublishMode = "host" + PublishedPort = 80 + TargetPort = 80 + PublishedSSLPort = 443 + TargetSSLPort = 443 + [Extensions.us-west.Config] + User = "nginx" + PidPath = "/var/run/proxy.pid" + WorkerProcesses = 1 + RlimitNoFile = 65535 + MaxConnections = 2048 + [Extensions.us-west.Labels] + ext_region = "us-west" + [Extensions.us-west.ProxyLabels] + proxy_region = "us-west" +EOF +oqkvv1asncf6p2axhx41vylgt +``` +Note that we are using "host" mode networking in order to use the same ports (`80` and `443`) in the cluster. We cannot use ingress +networking as it reserves the port across all nodes. If you want to use ingress networking you will have to use different ports +for each service cluster. + +Next we will create a dedicated network for Interlock and the extensions: + +```bash +$> docker network create -d overlay interlock +``` + +Now we can create the Interlock service: + +```bash +$> docker service create \ + --name interlock \ + --mount src=/var/run/docker.sock,dst=/var/run/docker.sock,type=bind \ + --network interlock \ + --constraint node.role==manager \ + --config src=service.interlock.conf,target=/config.toml \ + interlockpreview/interlock:2.0.0-preview -D run -c /config.toml +sjpgq7h621exno6svdnsvpv9z +``` + +## Configure Proxy Services +Once we have the node labels we can re-configure the Interlock Proxy services to be constrained to the +workers for each region. Again, from a manager run the following to pin the proxy services to the ingress workers: + +```bash +$> docker service update \ + --constraint-add node.labels.nodetype==loadbalancer \ + --constraint-add node.labels.region==us-east \ + interlock-proxy-us-east +$> docker service update \ + --constraint-add node.labels.nodetype==loadbalancer \ + --constraint-add node.labels.region==us-west \ + interlock-proxy-us-west +``` + +We are now ready to deploy applications. First we will create individual networks for each application: + +```bash +$> docker network create -d overlay demo-east +$> docker network create -d overlay demo-west +``` + +Next we will deploy the application in the `us-east` service cluster: + +```bash +$> docker service create \ + --name demo-east \ + --network demo-east \ + --detach=true \ + --label com.docker.lb.hosts=demo-east.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.service_cluster=us-east \ + --env METADATA="us-east" \ + ehazlett/docker-demo +``` + +Now we deploy the application in the `us-west` service cluster: + +```bash +$> docker service create \ + --name demo-west \ + --network demo-west \ + --detach=true \ + --label com.docker.lb.hosts=demo-west.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.service_cluster=us-west \ + --env METADATA="us-west" \ + ehazlett/docker-demo +``` + +Only the service cluster that is designated will be configured for the applications. For example, the `us-east` service cluster +will not be configured to serve traffic for the `us-west` service cluster and vice versa. We can see this in action when we +send requests to each service cluster. + +When we send a request to the `us-east` service cluster it only knows about the `us-east` application (be sure to ssh to the `lb-00` node): + +```bash +{% raw %} +$> curl -H "Host: demo-east.local" http://$(docker node inspect -f '{{ .Status.Addr }}' lb-00)/ping +{"instance":"1b2d71619592","version":"0.1","metadata":"us-east","request_id":"3d57404cf90112eee861f9d7955d044b"} +$> curl -H "Host: demo-west.local" http://$(docker node inspect -f '{{ .Status.Addr }}' lb-00)/ping + +404 Not Found + +

404 Not Found

+
nginx/1.13.6
+ + +{% endraw %} +``` + +Application traffic is isolated to each service cluster. Interlock also ensures that a proxy will only be updated if it has corresponding updates +to its designated service cluster. So in this example, updates to the `us-east` cluster will not affect the `us-west` cluster. If there is a problem +the others will not be affected. diff --git a/datacenter/ucp/3.0/guides/interlock/usage/sessions.md b/datacenter/ucp/3.0/guides/interlock/usage/sessions.md new file mode 100644 index 0000000000..06d2285a76 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/sessions.md @@ -0,0 +1,130 @@ +--- +title: Persistent (sticky) sessions +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a service and configure the proxy for persistent (sticky) sessions. + +# Cookies +In the following example we will show how to configure sticky sessions using cookies. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the service with the cookie to use for sticky sessions: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --replicas=5 \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.sticky_session_cookie=session \ + --label com.docker.lb.port=8080 \ + --env METADATA="demo-sticky" \ + ehazlett/docker-demo +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local` +and configured to use sticky sessions: + +```bash +$> curl -vs -c cookie.txt -b cookie.txt -H "Host: demo.local" http://127.0.0.1/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> Cookie: session=1510171444496686286 +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Wed, 08 Nov 2017 20:04:36 GMT +< Content-Type: text/plain; charset=utf-8 +< Content-Length: 117 +< Connection: keep-alive +* Replaced cookie session="1510171444496686286" for domain demo.local, path /, expire 0 +< Set-Cookie: session=1510171444496686286 +< x-request-id: 3014728b429320f786728401a83246b8 +< x-proxy-id: eae36bf0a3dc +< x-server-info: interlock/2.0.0-development (147ff2b1) linux/amd64 +< x-upstream-addr: 10.0.2.5:8080 +< x-upstream-response-time: 1510171476.948 +< +{"instance":"9c67a943ffce","version":"0.1","metadata":"demo-sticky","request_id":"3014728b429320f786728401a83246b8"} +``` + +Notice the `Set-Cookie` from the application. This is stored by the `curl` command and sent with subsequent requests +which are pinned to the same instance. If you make a few requests you will notice the same `x-upstream-addr`. + +# IP Hashing +In this example we show how to configure sticky sessions using client IP hashing. This is not as flexible or consistent +as cookies but enables workarounds for some applications that cannot use the other method. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the service with the cookie to use for sticky sessions using IP hashing: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --replicas=5 \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.ip_hash=true \ + --env METADATA="demo-sticky" \ + ehazlett/docker-demo +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local` +and configured to use sticky sessions: + +```bash +$> curl -vs -H "Host: demo.local" http://127.0.0.1/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Wed, 08 Nov 2017 20:04:36 GMT +< Content-Type: text/plain; charset=utf-8 +< Content-Length: 117 +< Connection: keep-alive +< x-request-id: 3014728b429320f786728401a83246b8 +< x-proxy-id: eae36bf0a3dc +< x-server-info: interlock/2.0.0-development (147ff2b1) linux/amd64 +< x-upstream-addr: 10.0.2.5:8080 +< x-upstream-response-time: 1510171476.948 +< +{"instance":"9c67a943ffce","version":"0.1","metadata":"demo-sticky","request_id":"3014728b429320f786728401a83246b8"} +``` + +You can use `docker service scale demo=10` to add some more replicas. Once scaled, you will notice that requests are pinned +to a specific backend. + +Note: due to the way the IP hashing works for extensions, you will notice a new upstream address when scaling replicas. This is +expected as internally the proxy uses the new set of replicas to decide on a backend on which to pin. Once the upstreams are +determined a new "sticky" backend will be chosen and that will be the dedicated upstream. diff --git a/datacenter/ucp/3.0/guides/interlock/usage/ssl.md b/datacenter/ucp/3.0/guides/interlock/usage/ssl.md new file mode 100644 index 0000000000..098be89599 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/ssl.md @@ -0,0 +1,220 @@ +--- +title: Applications with SSL +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a demo service to the host `demo.local` using SSL. + +# SSL Termination +In this example we will be using Docker [Secrets](https://docs.docker.com/engine/swarm/secrets/) +to centrally and securely store SSL certificates in order to terminate SSL at the proxy service. +Application traffic is encrypted in transport to the proxy service which terminates SSL and then +uses unencrypted traffic inside the secure datacenter. + +![Interlock SSL Termination](interlock_ssl_termination.png) + +First we will generate certificates for the example: + +```bash +$> openssl req \ + -new \ + -newkey rsa:4096 \ + -days 3650 \ + -nodes \ + -x509 \ + -subj "/C=US/ST=SomeState/L=SomeCity/O=Interlock/CN=demo.local" \ + -keyout demo.local.key \ + -out demo.local.cert +``` + +This should result in two files being created: `demo.local.cert` and `demo.local.key`. Next we +will use these to create Docker Secrets. + +```bash +$> docker secret create demo.local.cert demo.local.cert +ywn8ykni6cmnq4iz64um1pj7s +$> docker secret create demo.local.key demo.local.key +e2xo036ukhfapip05c0sizf5w +``` + +Now we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.ssl_cert=demo.local.cert \ + --label com.docker.lb.ssl_key=demo.local.key \ + ehazlett/docker-demo +6r0wiglf5f3bdpcy6zesh1pzx +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `https://demo.local`. + +Note: for this to work you must have an entry for `demo.local` in your local hosts (i.e. `/etc/hosts`) file. +You cannot use a host header as in other examples due to the way [SNI](https://tools.ietf.org/html/rfc3546#page-8) works. + +```bash +$> curl -vsk https://demo.local/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to demo.local (127.0.0.1) port 443 (#0) +* ALPN, offering http/1.1 +* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt + CApath: none +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Server hello (2): +* TLSv1.2 (IN), TLS handshake, Certificate (11): +* TLSv1.2 (IN), TLS handshake, Server key exchange (12): +* TLSv1.2 (IN), TLS handshake, Server finished (14): +* TLSv1.2 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.2 (OUT), TLS change cipher, Client hello (1): +* TLSv1.2 (OUT), TLS handshake, Finished (20): +* TLSv1.2 (IN), TLS change cipher, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 +* ALPN, server accepted to use http/1.1 +* Server certificate: +* subject: C=US; ST=SomeState; L=SomeCity; O=Interlock; CN=demo.local +* start date: Nov 8 16:23:03 2017 GMT +* expire date: Nov 6 16:23:03 2027 GMT +* issuer: C=US; ST=SomeState; L=SomeCity; O=Interlock; CN=demo.local +* SSL certificate verify result: self signed certificate (18), continuing anyway. +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: nginx/1.13.6 +< Date: Wed, 08 Nov 2017 16:26:55 GMT +< Content-Type: text/plain; charset=utf-8 +< Content-Length: 92 +< Connection: keep-alive +< Set-Cookie: session=1510158415298009207; Path=/; Expires=Thu, 09 Nov 2017 16:26:55 GMT; Max-Age=86400 +< x-request-id: 4b15ab2aaf2e0bbdea31f5e4c6b79ebd +< x-proxy-id: a783b7e646af +< x-server-info: interlock/2.0.0-development (147ff2b1) linux/amd64 +< x-upstream-addr: 10.0.2.3:8080 + +{"instance":"c2f1afe673d4","version":"0.1",request_id":"7bcec438af14f8875ffc3deab9215bc5"} +``` + +Since the certificate and key is stored securely in Swarm you can safely scale this service as well as the proxy +service and Swarm will handle granting access to the credentials only as needed. + +# SSL Passthrough +In this example we will be using SSL passthrough to ensure encrypted communication from the request to the application +service. This ensures maximum security as there is no unencrypted transport. + +![Interlock SSL Passthrough](interlock_ssl_passthrough.png) + +First we will generate certificates for our application: + +```bash +$> openssl req \ + -new \ + -newkey rsa:4096 \ + -days 3650 \ + -nodes \ + -x509 \ + -subj "/C=US/ST=SomeState/L=SomeCity/O=Interlock/CN=demo.local" \ + -keyout app.key \ + -out app.cert +``` + +This should result in two files being created: `app.cert` and `app.key`. Next we +will use these to create Docker Secrets. + +```bash +$> docker secret create app.cert app.cert +ywn8ykni6cmnq4iz64um1pj7s +$> docker secret create app.key app.key +e2xo036ukhfapip05c0sizf5w +``` + +Now we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --secret source=app.cert,target=/run/secrets/cert.pem \ + --secret source=app.key,target=/run/secrets/key.pem \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.ssl_passthrough=true \ + --env METADATA="demo-ssl-passthrough" \ + ehazlett/docker-demo --tls-cert=/run/secrets/cert.pem --tls-key=/run/secrets/key.pem +``` + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `https://demo.local`. + +Note: for this to work you must have an entry for `demo.local` in your local hosts (i.e. `/etc/hosts`) file. +You cannot use a host header as in other examples due to the way [SNI](https://tools.ietf.org/html/rfc3546#page-8) works. + +```bash +$> curl -vsk https://demo.local/ping +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to demo.local (127.0.0.1) port 443 (#0) +* ALPN, offering http/1.1 +* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt + CApath: none +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Server hello (2): +* TLSv1.2 (IN), TLS handshake, Certificate (11): +* TLSv1.2 (IN), TLS handshake, Server key exchange (12): +* TLSv1.2 (IN), TLS handshake, Server finished (14): +* TLSv1.2 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.2 (OUT), TLS change cipher, Client hello (1): +* TLSv1.2 (OUT), TLS handshake, Finished (20): +* TLSv1.2 (IN), TLS change cipher, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 +* ALPN, server accepted to use http/1.1 +* Server certificate: +* subject: C=US; ST=SomeState; L=SomeCity; O=Interlock; CN=demo.local +* start date: Nov 8 16:39:45 2017 GMT +* expire date: Nov 6 16:39:45 2027 GMT +* issuer: C=US; ST=SomeState; L=SomeCity; O=Interlock; CN=demo.local +* SSL certificate verify result: self signed certificate (18), continuing anyway. +> GET /ping HTTP/1.1 +> Host: demo.local +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Connection: close +< Set-Cookie: session=1510159255159600720; Path=/; Expires=Thu, 09 Nov 2017 16:40:55 GMT; Max-Age=86400 +< Date: Wed, 08 Nov 2017 16:40:55 GMT +< Content-Length: 78 +< Content-Type: text/plain; charset=utf-8 +< +{"instance":"327d5a26bc30","version":"0.1","metadata":"demo-ssl-passthrough"} +``` + +Application traffic will travel securely fully encrypted from the request all the way to the application service. +Notice that Interlock cannot add the metadata response headers (version info, request ID, etc) as this is using +TCP passthrough and cannot add the metadata. diff --git a/datacenter/ucp/3.0/guides/interlock/usage/websockets.md b/datacenter/ucp/3.0/guides/interlock/usage/websockets.md new file mode 100644 index 0000000000..6c3bec3dc4 --- /dev/null +++ b/datacenter/ucp/3.0/guides/interlock/usage/websockets.md @@ -0,0 +1,35 @@ +--- +title: Websockets +description: Learn about Interlock, an application routing and load balancing system + for Docker Swarm. +keywords: ucp, interlock, load balancing +--- + +In this example we will publish a service and configure support for websockets. + +First we will create an overlay network so that service traffic is isolated and secure: + +```bash +$> docker network create -d overlay demo +1se1glh749q1i4pw0kf26mfx5 +``` + +Next we will create the service with websocket endpoints: + +```bash +$> docker service create \ + --name demo \ + --network demo \ + --detach=false \ + --label com.docker.lb.hosts=demo.local \ + --label com.docker.lb.port=8080 \ + --label com.docker.lb.websocket_endpoints=/ws \ + ehazlett/websocket-chat +``` + +Note: for this to work you must have an entry for `demo.local` in your local hosts (i.e. `/etc/hosts`) file. +This uses the browser for websocket communication so you will need to have an entry or use a routable domain. + +Interlock will detect once the service is available and publish it. Once the tasks are running +and the proxy service has been updated the application should be available via `http://demo.local`. Open +two instances of your browser and you should see text on both instances as you type.