Skip to content

Commit

Permalink
Enable single port exposure on Che (eclipse-che#5115)
Browse files Browse the repository at this point in the history
* Toggle Che single port by enabling CHE_SINGLE_PORT in the che.env file. (CHE_SINGLE_PORT=true, default is false)

By enabling single-port, all browser traffic to Che or any workspace will be routed through the value that you have set to CHE_PORT`, or 8080 if not set. Setting this property will transform the launch sequence of Che to launch a Traefik reverse proxy. The reverse proxy will act as the traffic endpoint for all browser communications. When a new workspace is started or stopped, Che will update Traefik's configuration
with rules for how browser traffic should be routed to Che or a workspace.

It’s now using an official Traefik image (before I was using a custom made image)
There is an interceptor with a kill switch. It means interceptor is applied only if plug-in is enabled (not only if plug-in is added at compilation)
It is automatically enabled when CHE_SINGLE_PORT is turned on

docker-compose file is handling if the single_port is turned on or off and then add the traefik container and redirect port only if the property is enabled. (not enabled by default)

using —debug flag when launching che is also turning on the traffic web console to view traefik routes

It is not enabled by default, so it means that without user change, there is no overhead, no useless container started, etc.

Change-Id: I12644d9202dadc0b10104f78bb055425ca6611ac
Signed-off-by: Florent BENOIT <fbenoit@codenvy.com>
  • Loading branch information
benoitf authored May 18, 2017
1 parent 8e5f05b commit a13068e
Show file tree
Hide file tree
Showing 18 changed files with 674 additions and 0 deletions.
4 changes: 4 additions & 0 deletions assembly/assembly-wsmaster-war/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-ssh-machine</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-traefik-docker</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-url-factory</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ protected void configure() {
bind(org.eclipse.che.api.system.server.SystemEventsWebsocketBroadcaster.class).asEagerSingleton();

install(new org.eclipse.che.plugin.docker.machine.dns.DnsResolversModule());
install(new org.eclipse.che.plugin.traefik.TraefikDockerModule());

bind(org.eclipse.che.api.agent.server.filters.AddExecAgentInWorkspaceFilter.class);
bind(org.eclipse.che.api.agent.server.filters.AddExecAgentInStackFilter.class);
Expand Down
1 change: 1 addition & 0 deletions dockerfiles/cli/images.template
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
IMAGE_INIT=${BUILD_ORGANIZATION}/${BUILD_PREFIX}-init:${BUILD_TAG}
IMAGE_CHE=${BUILD_ORGANIZATION}/${BUILD_PREFIX}-server:${BUILD_TAG}
IMAGE_COMPOSE=docker/compose:1.8.1
IMAGE_TRAEFIK=traefik:v1.3.0-rc1
1 change: 1 addition & 0 deletions dockerfiles/cli/version/latest/images
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
IMAGE_INIT=eclipse/che-init:latest
IMAGE_CHE=eclipse/che-server:latest
IMAGE_COMPOSE=docker/compose:1.8.1
IMAGE_TRAEFIK=traefik:v1.3.0-rc1
22 changes: 22 additions & 0 deletions dockerfiles/init/manifests/che.env
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,28 @@
# make workspaces reachable.
#CHE_DOCKER_IP_EXTERNAL=NULL

# Usage of single-port routing.
# By enabling single-port, all browser traffic to Che or any workspace will be routed
# through the value that you have set to CHE_PORT`, or 8080 if not set. Setting this
# property will transform the launch sequence of Che to launch a Traefik reverse proxy.
# The reverse proxy will act as the traffic endpoint for all browser communications.
# When a new workspace is started or stopped, Che will update Traefik's configuration
# with rules for how browser traffic should be routed to Che or a workspace.
#
# With single-port, each service running in a workspace and exposing ports has its own hostname.
# Example : workspace agent will have a hostname like : ws-agent.workspace-id....domain.name
# By default the domain name will use nip.io which allow to provide wildcard DNS without any
# user configuration. The strategy used for the hosts is using the template provided by the
# CHE_DOCKER_SERVER__EVALUATION__STRATEGY_CUSTOM_TEMPLATE property with default value
# <serverName>.<machineName>.<workspaceId>.<wildcardNipDomain>:<chePort>
# If you've your own domain and then DNS server, you may want to add wildcard DNS entry
# matching a pattern. For example updating the property to the value
# <serverName>.<machineName>.<workspaceId>.che.foobar.com:<chePort>
# will require a wildcard *.che.foobar.com entry in DNS server resolving to the IP of the che server.
# More details on the values provided by the template can be found on documentation.
CHE_SINGLE_PORT=false



########################################################################################
##### #####
Expand Down
6 changes: 6 additions & 0 deletions dockerfiles/init/manifests/che.pp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
# please leave this as it is if you don't need no_proxy configuration
$no_proxy_for_che_workspaces = getValue("CHE_WORKSPACE_NO__PROXY","")

###############################
# Single port configuration
#
$che_single_port = getValue("CHE_SINGLE_PORT","false")


################################
# DNS resolver configuration
$dns_resolvers = getValue("CHE_DNS_RESOLVERS","")
Expand Down
1 change: 1 addition & 0 deletions dockerfiles/init/modules/base/manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@

include che
include compose
include traefik
}
6 changes: 6 additions & 0 deletions dockerfiles/init/modules/che/templates/che.env.erb
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,9 @@ JAVA_OPTS=-Xms512m -Xmx<%= @che_server_xmx %>m -Djava.security.egd=file:/dev/./u

# java opts for ws agent
CHE_WORKSPACE_JAVA_OPTIONS=<%= scope.lookupvar('che::workspace_java_options') %> <% if ! @http_proxy_for_che_workspaces.empty? or ! @https_proxy_for_che_workspaces.empty? -%>-Dhttp.proxySet=true<% end -%><% if ! @http_proxy_for_che_workspaces.empty? -%><% if ! @http_proxy_for_che_workspaces.empty? and @http_proxy_for_che_workspaces.include? '@' -%> -Dhttp.proxyUser=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[0].split(':')[0] %> -Dhttp.proxyPassword=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[0].split(':')[1] %> -Dhttp.proxyHost=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[1].split(':')[0] %> -Dhttp.proxyPort=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[1].split(':')[1].gsub(/\/.*/,'') %><% else -%> -Dhttp.proxyHost=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split(':')[0] %> -Dhttp.proxyPort=<%= @http_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split(':')[1].gsub(/\/.*/,'') %><% end -%><% end -%><% if ! @https_proxy_for_che_workspaces.empty? -%><% if @https_proxy_for_che_workspaces.include? '@' -%> -Dhttps.proxyUser=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[0].split(':')[0] %> -Dhttps.proxyPassword=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[0].split(':')[1] %> -Dhttps.proxyHost=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[1].split(':')[0] %> -Dhttps.proxyPort=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split('@')[1].split(':')[1].gsub(/\/.*/,'') %><% else -%> -Dhttps.proxyHost=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split(':')[0] %> -Dhttps.proxyPort=<%= @https_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split(':')[1].gsub(/\/.*/,'') %><% end -%><% end -%><% if ! @che_no_proxy.empty? -%> -Dhttp.nonProxyHosts='<%= @no_proxy_for_che_workspaces.gsub(/^https?\:\/\//, '').gsub(/^www./,'').split(",").uniq.join("|") %>|'<% end -%>

# Enable single port options
<% if scope.lookupvar('che::che_single_port') == "true" -%>
CHE_DOCKER_SERVER__EVALUATION__STRATEGY=custom
CHE_PLUGIN_TRAEFIK_ENABLED=true
<% end -%>
31 changes: 31 additions & 0 deletions dockerfiles/init/modules/compose/templates/docker-compose.yml.erb
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,22 @@ che:
- '32001:32001'
- '32101:32101'
<% end -%>
<% if scope.lookupvar('che::che_single_port') == 'true' -%>
- 8080
<% else -%>
- '<%= scope.lookupvar('che::che_port') -%>:<%= scope.lookupvar('che::che_port') -%>'
<% end -%>
<% if scope.lookupvar('che::che_env') == 'development' -%>
- '<%= scope.lookupvar('che::che_debug_port') -%>:<%= scope.lookupvar('che::che_debug_port') -%>'
<% end -%>
<% if scope.lookupvar('che::che_single_port') == 'true' -%>
labels:
traefik.che.frontend.backend: "che-server"
traefik.che.frontend.entryPoints: "http"
traefik.che.port: "<%= scope.lookupvar('che::che_port') -%>"
traefik.che.frontend.rule: "PathPrefix:/"
<% end -%>

restart: always
container_name: <%= ENV["CHE_CONTAINER_NAME"] %>
<% if scope.lookupvar('che::che_user') != 'root' -%>
Expand All @@ -40,3 +52,22 @@ che:
<% if ! @dns_resolvers.empty? -%>
<%= " dns:" + "\n" + @dns_resolvers.split(",").map { |val| " - #{val}" }.join("\n") %>
<% end -%>
<% if scope.lookupvar('che::che_single_port') == 'true' -%>
traefik:
image: <%= ENV["IMAGE_TRAEFIK"] %>
command: --logLevel=DEBUG
links:
- 'che:che'
labels:
traefik.enable: "false"
ports:
- '<%= scope.lookupvar('che::che_port') -%>:<%= scope.lookupvar('che::che_port') -%>'
<% if scope.lookupvar('che::che_env') == 'development' -%>
- '7070:7070'
<% end -%>
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- '<%= scope.lookupvar('che::che_instance') -%>/config/traefik.toml:/etc/traefik/traefik.toml'
<% end -%>
10 changes: 10 additions & 0 deletions dockerfiles/init/modules/traefik/manifests/init.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class traefik {

# creating traefik.toml
file { "/opt/che/config/traefik.toml":
ensure => "present",
content => template("traefik/traefik.toml.erb"),
mode => "644",
}

}
91 changes: 91 additions & 0 deletions dockerfiles/init/modules/traefik/templates/traefik.toml.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
################################################################
# Global configuration
################################################################

# Timeout in seconds.
# Duration to give active requests a chance to finish during hot-reloads
#
# Optional
# Default: 10
#
# graceTimeOut = 10

# Enable debug mode
#
# Optional
# Default: false
#
debug = true

# Periodically check if a new version has been released
#
# Optional
# Default: true
#
checkNewVersion = false

# Traefik logs file
# If not defined, logs to stdout
#
# Optional
#
# traefikLogsFile = "log/traefik.log"

# Access logs file
#
# Optional
#
#accessLogsFile = "/tmp/log/access.log"

# Log level
#
# Optional
# Default: "ERROR"
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
# Messages at and above the selected level will be logged.
#
# logLevel = "ERROR"

# Backends throttle duration: minimum duration in seconds between 2 events from providers
# before applying a new configuration. It avoids unnecessary reloads if multiples events
# are sent in a short amount of time.
#
# Optional
# Default: "2"
#
# ProvidersThrottleDuration = "5"

# If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used.
# If you encounter 'too many open files' errors, you can either change this value, or change `ulimit` value.
#
# Optional
# Default: http.DefaultMaxIdleConnsPerHost
#
# MaxIdleConnsPerHost = 200

# If set to true invalid SSL certificates are accepted for backends.
# Note: This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
# Optional
# Default: false
#
# InsecureSkipVerify = true

# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
#
# Optional
# Default: ["http"]
#
# defaultEntryPoints = ["http", "https"]

<% if scope.lookupvar('che::che_env') == 'development' -%>
[web]
address = ":7070"
<% end -%>

[entryPoints]
[entryPoints.http]
address = ":<%= scope.lookupvar('che::che_port') -%>"

[docker]
watch = true
83 changes: 83 additions & 0 deletions plugins/plugin-traefik/plugin-traefik-docker/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-plugin-traefik-parent</artifactId>
<groupId>org.eclipse.che.plugin</groupId>
<version>5.11.0-SNAPSHOT</version>
</parent>
<artifactId>che-plugin-traefik-docker</artifactId>
<packaging>jar</packaging>
<name>Che Plugin :: Traefik :: Docker</name>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-docker-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-docker-machine</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockitong</groupId>
<artifactId>mockitong</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
Loading

0 comments on commit a13068e

Please sign in to comment.