Skip to content

Commit a03ee27

Browse files
authored
Merge pull request #99 from delphi-hub/feature/traefikIntegration
Added support for Traefik reverse-proxy
2 parents d77a3cf + 3b17b9a commit a03ee27

18 files changed

+223
-48
lines changed

OpenAPISpecification.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ schemes:
2626
- https
2727
- http
2828
paths:
29+
/configuration:
30+
get:
31+
tags:
32+
- Meta
33+
summary: Retreives general configuration information
34+
description: Retrieves an object containing the most important configuration values of the registry. This contains the docker and traefik URIs.
35+
operationId: configurationInfo
36+
responses:
37+
'200':
38+
description: Configuration object
39+
schema:
40+
type: object
41+
properties:
42+
DockerHttpApi:
43+
type: string
44+
example: "172.0.2.1:9095"
45+
TraefikProxyUri:
46+
type: string
47+
example: "172.0.2.1:80"
2948
/users/authenticate:
3049
post:
3150
tags:

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ this repository is purely experimental!
1111

1212
## What is the registry component?
1313
The Delphi registry is a server that provides access to all information and operations needed to set up, run and manage the Delphi system. By default, the REST interface is exposed at *0.0.0.0:8087*, and contains endpoints for:
14+
1415
* Retrieving a list of all instances of a certain type (Crawler, WebApi, WebApp, ElasticSearch)
1516
* Retrieving the whole network graph (all instances and links between instances)
1617
* Deploying new instances of a certain type to a docker host
@@ -37,16 +38,32 @@ For Linux users, checkout Delphi Registry repository and execute the command
3738
```
3839
sudo bash ./Delphi_install.sh
3940
```
40-
inside the registry's root directory. This installation script will create the required repositories, build the docker images, and register them directly at the local docker registry.
41+
inside the ```/Setup``` directory. This installation script will create the required repositories, build the docker images, and register them directly at the local docker registry.
4142
The registry requires an initial instance of ElasticSearch to be running.
4243

44+
To allow access to Delphi components deployed via Docker, the registry supports the reverse-proxy [Traefik](https://traefik.io/). While it is running, it will automatically detected containers deployed by the registry, and provide access to them using the host specified in each instances' ```Host``` attribute.
45+
Windows users can install Traefik (using Docker) based on [this tutorial](https://docs.traefik.io/#the-traefik-quickstart-using-docker). For Linux users, Traefik will be installed and started by the installation script mentioned above.
46+
47+
**Note:** Traefik must be running inside the same Docker network as the containers it is associated with. By default the name for this network is expected to be ```delphi```. Windows users have to manually create it using ```docker network create delphi``` before starting Traefik. If you want to change this network name, please follow these steps:
48+
49+
1. Go to ```docker-compose.yml``` file (for Windows: Create during tutorial; for Linux found in ```/Setup```)
50+
51+
2. Change the item *services->traefik->networks* to your new network name
52+
53+
3. Change the item *networks->delphi->external:true* to *networks->your-network-name->external:true*. Save and close the file
54+
55+
4. Change the ````traefikDockerNetwork`` setting in the configuration file to your new network name (see section below for details)
56+
4357
## Adapt the configuration file
4458
Before you can start the application, you have to make sure your configuration file contains valid data. The file can be found at *src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Configuration.scala*, and most of its attributes are string or integer values. The following table describes the attributes in more detail.
4559

4660
|Attribute | Type | Default Value |Explanation |
4761
| :---: | :---: | :---: | :--- |
4862
|```bindHost``` | ```String``` | ```"0.0.0.0"``` | Host address that the registry server should be bound to |
4963
|```bindPort``` | ```Int``` | ```8087``` | Port that the registry server should be reachable at |
64+
|```traefikBaseHost``` | ```String``` | ```"delphi.de"``` | The host part of the URL that traefik is configured to append to instance URLs. |
65+
|```traefikDockerNetwork``` | ```String``` | ```"delphi"``` | The Docker network Traefik is configured to use. |
66+
|```traefikUri``` | ```String``` | ```"http://172.17.0.1:80"``` | The URI that the Traefik reverse-proxy is hosted at.|
5067
|```defaultCrawlerPort``` | ```Int``` | ```8882``` | Port that Delphi Crawlers are reachable at. This may only be adapted if you manually changed the default port of crawlers before registering the respective image. |
5168
|```defaultWebApiPort``` | ```Int``` | ```8080``` | Port that Delphi WebAPIs are reachable at. This may only be adapted if you manually changed the default port of WebAPIs before registering the respective image. |
5269
|```defaultWebAppPort``` | ```Int``` | ```8085``` | Port that Delphi WebApps are reachable at. This may only be adapted if you manually changed the default port of WebApps before registering the respective image. |
@@ -58,7 +75,7 @@ Before you can start the application, you have to make sure your configuration f
5875
|```uriInLocalNetwork``` | ```String``` | ```"http://172.17.0.1:8087"``` | URI that the registry is reachable at for all docker containers. In most of the use-cases this is going to be the gateway of the default docker bridge.<br>**Note:** For OSX you have to set this value to the DNS name of the docker host, which is ```http://host.docker.internal:8087``` (If the registry is running on the host).|
5976
|```maxLabelLength``` | ```Int``` | ```50``` | Maximum number of characters for instance labels. Longer labels will be rejected.|
6077
|```dockerOperationTimeout``` | ```Timeout``` | ```Timeout(20 seconds)``` | Default timeout for docker operations. If any of the async Docker operations (deploy, stop, pause, ..) takes longer than this, it will be aborted.|
61-
|```defaultDockerUri``` | ```String``` | ```http://localhost:9095``` | Default uri to connect to docker. It will be used if the environment variable ```DELPHI_DOCKER_HOST``` is not specified.|
78+
|```dockerUri``` | ```String``` | ```http://localhost:9095``` | Default uri to connect to docker. It will be used if the environment variable ```DELPHI_DOCKER_HOST``` is not specified.|
6279
|```jwtSecretKey``` | ```String``` | ```changeme``` | Secret key to use for JWT signature (HS256). This setting can be overridden by specifying the ```JWT_SECRET``` environment variable.|
6380
|```useInMemoryInstanceDB``` | ```Boolean``` | ```true``` | If set to true, all instance data will be kept in memory instead of using a MySQL database.|
6481
|```instanceDatabaseHost``` | ```String``` | ```"jdbc:mysql://localhost/"``` | Host that the MySQL instance database is reachable at (only necessary if *useInMemoryInstanceDB* is false).|

Delphi_install.sh renamed to Setup/Delphi_install.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#!/usr/bin/env bash
22

3+
####SETTINGS####
4+
DELPHI_NETWORK_NAME=delphi
5+
36
####DELPHI INSTALLATION#####
47
#########################
58

@@ -10,7 +13,12 @@ sudo chown $SUDO_USER $LOGFILE
1013
echo " Starting installation of Delphi Application"
1114
echo " Logs can be found at: $LOGFILE"
1215

16+
echo " Setting up Traefik..."
17+
sudo -u $SUDO_USER bash -c "docker network create $DELPHI_NETWORK_NAME &>> $LOGFILE"
18+
sudo -u $SUDO_USER bash -c "docker-compose up -d &>> $LOGFILE"
19+
echo " **Setup of Traefik completed**"
1320

21+
echo " Cloning Delphi ..."
1422
sudo -u $SUDO_USER bash -c "git clone --progress https://github.com/delphi-hub/delphi.git --recurse-submodules>> $LOGFILE"
1523
echo " **Cloning of Delphi Repositories completed**"
1624

@@ -27,7 +35,7 @@ echo " **Cloning of Delphi Repositories completed**"
2735
## Installing Images and Creating Volumes
2836

2937
echo " Building Delphi Images. This step might take several minutes"
30-
sudo -u $SUDO_USER bash -c "sbt docker:publishLocal &>> $LOGFILE"
38+
sudo -u $SUDO_USER bash -c "cd ..;sbt docker:publishLocal &>> $LOGFILE"
3139
echo " Delphi-Registry Image built"
3240
sudo -u $SUDO_USER bash -c "(cd ./delphi/delphi-webapi;sbt docker:publishLocal) &>> $LOGFILE"
3341
echo " Delphi-WebApi Image built"

Setup/docker-compose.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
version: '3.5'
2+
3+
services:
4+
5+
traefik:
6+
restart: always
7+
image: traefik:1.6
8+
container_name: traefik
9+
ports:
10+
- "80:80"
11+
- "8080:8080"
12+
networks:
13+
- delphi
14+
volumes:
15+
- ./traefik.toml:/etc/traefik/traefik.toml
16+
- /var/run/docker.sock:/var/run/docker.sock:ro
17+
18+
networks:
19+
delphi:
20+
external: true
21+
22+

Setup/traefik.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[entryPoints]
2+
[entryPoints.http]
3+
address = ":80"
4+
5+
[api]
6+
7+
[docker]
8+
9+
domain = "delphi.de"
10+
11+

src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Configuration.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ class Configuration( ) {
99
val bindHost: String = "0.0.0.0"
1010
val bindPort: Int = 8087
1111

12+
//Traefik data
13+
val traefikBaseHost: String = "delphi.de"
14+
val traefikDockerNetwork: String = "delphi"
15+
val traefikUri: String = "http://172.17.0.1:80"
16+
1217

1318
val recoveryFileName : String = "dump.temp"
1419

@@ -32,7 +37,7 @@ class Configuration( ) {
3237
val maxLabelLength: Int = 50
3338

3439
val dockerOperationTimeout: Timeout = Timeout(20 seconds)
35-
val defaultDockerUri: String = "http://localhost:9095"
40+
val dockerUri: String = sys.env.getOrElse("DELPHI_DOCKER_HOST", "http://localhost:9095")
3641

3742
val jwtSecretKey: String = sys.env.getOrElse("JWT_SECRET", "changeme")
3843

src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Docker/Container.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ case class ContainerConfig(
2525
Image: String,
2626
Entrypoint: Option[Seq[String]] = None,
2727
Cmd: Seq[String] = Seq.empty,
28-
Env: Seq[String] = Seq.empty)
28+
Env: Seq[String] = Seq.empty,
29+
Labels: Map[String, String] = Map.empty[String,String],
30+
ExposedPorts: Map[String, EmptyExposedPortConfig] = Map.empty,
31+
NetworkingConfig: NetworkConfig = NetworkConfig(Map.empty))
32+
33+
case class NetworkConfig(EndpointsConfig: Map[String, EmptyEndpointConfig])
34+
case class EmptyEndpointConfig()
35+
case class EmptyExposedPortConfig()
2936

3037
case class Networks(
3138
IPAddress: String

src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Docker/ContainerCommands.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,17 @@ class ContainerCommands(connection: DockerConnection) extends JsonSupport with C
178178
response.status match {
179179
case StatusCodes.OK =>
180180
Unmarshal(response.entity).to[String].map { json =>
181-
val out = json.parseJson.asJsObject.getFields("NetworkSettings")
182-
out match {
183-
case Seq(network) => Networks(network.asJsObject.fields("IPAddress").toString())
184-
case _ => throw DeserializationException("Cannot find required field NetworkSettings/IPAddress")
181+
182+
Try[Networks]{
183+
val ip = json.parseJson.asJsObject.fields("NetworkSettings")
184+
.asJsObject.fields("Networks")
185+
.asJsObject.fields(Registry.configuration.traefikDockerNetwork)
186+
.asJsObject.getFields("IPAddress").head.toString.replace("\"", "")
187+
Networks(ip)
188+
} match {
189+
case Success(network) => network
190+
case Failure(ex) =>
191+
throw DeserializationException(s"Failed to extract IPAddress from docker with message ${ex.getMessage}")
185192
}
186193

187194
}

src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Docker/DockerActor.scala

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,41 @@ class DockerActor(connection: DockerConnection) extends Actor with ActorLogging
3636
}
3737

3838
case create(componentType, instanceId, containerName) =>
39-
val containerConfig = ContainerConfig(Image = DockerImage.getImageName(componentType),
40-
Env = Seq(s"INSTANCE_ID=$instanceId", s"DELPHI_IR_URI=${Registry.configuration.uriInLocalNetwork}",
41-
s"DELPHI_JWT_SECRET=${Registry.configuration.jwtSecretKey}"))
39+
40+
val instancePort = componentType match {
41+
case ComponentType.Crawler => Registry.configuration.defaultCrawlerPort
42+
case ComponentType.WebApi => Registry.configuration.defaultWebApiPort
43+
case ComponentType.WebApp => Registry.configuration.defaultWepAppPort
44+
case t => throw new RuntimeException(s"Invalid component type $t, cannot deploy container.")
45+
}
46+
47+
val networkConfig = NetworkConfig(Map(Registry.configuration.traefikDockerNetwork -> EmptyEndpointConfig()))
48+
49+
val traefikHostUrl = componentType.toString.toLowerCase + instanceId.toString + "." + Registry.configuration.traefikBaseHost
50+
51+
val containerConfig = ContainerConfig(
52+
Image = DockerImage.getImageName(componentType),
53+
Env = Seq(
54+
s"INSTANCE_ID=$instanceId",
55+
s"DELPHI_IR_URI=${Registry.configuration.uriInLocalNetwork}",
56+
s"DELPHI_JWT_SECRET=${Registry.configuration.jwtSecretKey}"
57+
),
58+
Labels = Map("traefik.frontend.rule" -> s"Host:$traefikHostUrl"),
59+
ExposedPorts = Map(s"$instancePort/tcp" -> EmptyExposedPortConfig()),
60+
NetworkingConfig = networkConfig
61+
)
4262

4363
val createCommand = Try(Await.result(container.create(containerConfig, containerName), Duration.Inf))
4464
createCommand match {
4565
case Failure(ex) => sender ! Failure(ex)
4666
case Success(containerResult) =>
4767
Await.ready(container.start(containerResult.Id), Duration.Inf)
48-
log.info(s"Docker Instance created and started")
68+
4969
val containerInfo = Await.result(container.get(containerResult.Id), Duration.Inf)
5070

51-
val instancePort = componentType match {
52-
case ComponentType.Crawler => Registry.configuration.defaultCrawlerPort
53-
case ComponentType.WebApi => Registry.configuration.defaultWebApiPort
54-
case ComponentType.WebApp => Registry.configuration.defaultWepAppPort
55-
case t => throw new RuntimeException(s"Invalid component type $t, cannot deploy container.")
56-
}
71+
log.info(s"Docker Instance created and started, ip is ${containerInfo.IPAddress}, host is $traefikHostUrl")
5772

58-
log.info("ip address is " + containerInfo.IPAddress)
59-
sender ! Success(containerResult.Id, containerInfo.IPAddress, instancePort)
73+
sender ! Success(containerResult.Id, containerInfo.IPAddress, instancePort, traefikHostUrl)
6074
}
6175

6276
case stop(containerId) =>

src/main/scala/de/upb/cs/swt/delphi/instanceregistry/Docker/DockerConnection.scala

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,8 @@ import scala.concurrent.Future
1212
object DockerConnection {
1313

1414

15-
def fromEnvironment(configuration: Configuration)(implicit system: ActorSystem, materializer: Materializer): DockerConnection = {
16-
def env(key: String): Option[String] = sys.env.get(key).filter(_.nonEmpty)
17-
18-
val host = env("DELPHI_DOCKER_HOST").getOrElse {
19-
configuration.defaultDockerUri
20-
}
21-
DockerHttpConnection(host)
15+
def fromEnvironment(configuration: Configuration) (implicit system: ActorSystem, mat: Materializer): DockerConnection = {
16+
DockerHttpConnection(configuration.dockerUri)
2217
}
2318
}
2419

0 commit comments

Comments
 (0)