The TICK Stack as a Docker Application Package

This is actually one of the main features of the Docker Application Package — the possibility to use different settings for different environments.

Let’s consider a dev and a prod environment and suppose that those two only differ when it comes to the port exposed by the application to the outside world:Telegraf listens on port 8000 in dev and 9000 in prod.

Chronograf listens on port 8001 in dev and 9001 in prod.

Note: of course, in a real world application, differences between dev and prod would not be limited to a port number.

The current example is over-simplified, so it makes it easier to grasp the main concepts.

To do so, we will first create a settings file for each environment and then we will modify the docker-compose.

yml file to add some placeholders.

The settings.

yml file is used to define some default ports for both Telegraf and Chronograf services.

// settings.

ymlports: telegraf: 8186 chronograf: 8888We also define dev.

yml to specify different values for the development environment,// dev.

ymlports: telegraf: 8000 chronograf: 8001and prod.

yml, for the production environment.

// prod.

ymlports: telegraf: 9000 chronograf: 9001Let’s now modify the docker-compose.

yml file, and change the published ports of Telegraf and Chronograf, so they’re using the variables defined in the above files.

$ cat tick.

dockerapp/docker-compose.

ymlversion: "3.

6"services: telegraf: image: telegraf configs: — source: telegraf-conf target: /etc/telegraf/telegraf.

conf ports: — ${ports.

telegraf}:8186 influxdb: image: influxdb chronograf: image: chronograf ports: — ${ports.

chronograf}:8888 command: ["chronograf", "–influxdb-url=http://influxdb:8086"] kapacitor: image: kapacitor environment: — KAPACITOR_INFLUXDB_0_URLS_0=http://influxdb:8086configs: telegraf-conf: file: .

/telegraf.

confAs we can see in the changes above, the way to access the port number of Telegraf is to use the ports.

telegraf notation.

The same goes for the Chronograf port.

The render command allows to generate the Docker Compose file substituting the variables ${ports.

XXX} with the content of the settings file specified.

The default settings.

yml is used if none are specified.

As we can see below the Telegraf port is now 8186, and the Chronograf one is 8888.

$ docker-app renderversion: "3.

6"services: chronograf: command: — chronograf — –influxdb-url=http://influxdb:8086 image: chronograf ports: — mode: ingress target: 8888 published: 8888 protocol: tcp influxdb: image: influxdb kapacitor: environment: KAPACITOR_INFLUXDB_0_URLS_0: http://influxdb:8086 image: kapacitor telegraf: configs: — source: telegraf-conf target: /etc/telegraf/telegraf.

conf image: telegraf ports: — mode: ingress target: 8186 published: 8186 protocol: tcpconfigs: telegraf-conf: file: telegraf.

confIf we specify a setting file in the render command, the values within that file are used.

As we can see on the following example, if we use dev.

yml during the rendering, Telegraf is published on port 8000 and Chronograf on port 8001.

$ docker-app render -f dev.

ymlversion: "3.

6"services: chronograf: command: — chronograf —-influxdb-url=http://influxdb:8086 image: chronograf ports: — mode: ingress target: 8888 published: 8001 protocol: tcp influxdb: image: influxdb kapacitor: environment: KAPACITOR_INFLUXDB_0_URLS_0: http://influxdb:8086 image: kapacitor telegraf: configs: — source: telegraf-conf target: /etc/telegraf/telegraf.

conf image: telegraf ports: — mode: ingress target: 8186 published: 8000 protocol: tcpconfigs: telegraf-conf: file: telegraf.

confInspect the applicationAs we can see below, the inspect command provides all the information related to the application:Its metadata.

The services involved (including the number of replicas defined, the ports exposed and the image used).

The settings used.

The attached files.

(All the files used on top of the docker-compose.

yml)$ docker-app inspecttick 0.

1.

0Maintained by: lucServices (4) Replicas Ports Image — — — — — — — — — — — — — — — -influxdb 1 influxdbchronograf 1 8888 chronografkapacitor 1 kapacitortelegraf 1 8186 telegrafSettings (2) Value — — — — — — — — -ports.

chronograf 8888ports.

telegraf 8186Attachments (4) Size — — — — — — — — — — dev.

yml 43Bprod.

yml 43Btelegraf.

conf 668BDeploy the application on a SwarmAs the stack uses a Docker Config based on the telegraf.

conf file, we need to copy this file over into the tick.

dockerapp folder.

Docker Application Package has allowed you to embed configuration files since version 0.

6).

The folder structure is then as follows:$ tree .

├── telegraf.

conf├── tick.

dockerapp│ ├── dev.

yml│ ├── docker-compose.

yml│ ├── metadata.

yml│ ├── prod.

yml│ ├── settings.

yml│ └── telegraf.

conf└── tick.

ymlUsing the following command, we deploy the application on a Swarm through a Docker stack.

$ docker-app deploy -f prod.

ymlCreating network tick_defaultCreating config tick_telegraf-confCreating service tick_influxdbCreating service tick_chronografCreating service tick_kapacitorCreating service tick_telegrafListing the services created, we can see the values from the prod.

yml settings file have been taken into account (as the exposed ports are 9000 and 9001 for Telegraf and Chronograf respectively).

Note: there is currently a small issue in 0.

6.

0, as the deploy command doesn’t always pick up the attachments from the package, but uses the one in the current folder in some cases.

This issue is addressed here, and will be fixed in the 0.

6.

1 release.

Deploy an application on a non-Swarm hostWe could deploy the application on a non-Swarm Docker host, for development purposes, with the following command:$ docker-app render -f dev.

yml | docker-compose -f — upThe command can be split into two parts:The rendering, using the dev.

yml settings file, to generate the final Docker Compose file.

This generated Compose file is piped to the second command, which reads the file from the standard input and runs the Compose application.

Note: this is just for the records, as there is no real point in deploying the app with Docker Compose here.

Indeed, the config primitive would not be taken into account by Compose as it’s a Swarm-only thing.

TestsAs we deployed the application using the settings file related to the production environment (prod.

yml), we will send data to port 9000 (the port published by Telegraf), and check the result on port 9001 (the port published by Chronograf).

Let’s send out some dummy data.

For this purpose, we use the image lucj/genx which is a simple Go application that can generate data following linear or cosine distributions (will be enhanced in the future).

$ docker run lucj/genxUsage of /genx: -duration string duration of the generation (default “1d”) -first float first value for linear type -last float last value for linear type (default 1) -max float max value for cos type (default 25) -min float min value for cos type (default 10) -period string period for cos type (default “1d”) -step string step / sampling period (default “1h”) -type string type of curve (default “cos”)In this example we simulate a temperature following a cosine curve.

Let’s generate three days of data, with a one day period, min/max values of 10/25 and a sampling step of one hour.

Obviously not a real world temperature model but enough for the tests.

)$ docker run lucj/genx:0.

1 -type cos -duration 3d -min 10 -max 25 -step 1h > /tmp/dataWe then send those data to the Telegraf endpoint with a couple of shell commands:PORT=9000endpoint="http://localhost:$PORT/write"cat /tmp/data | while read line; do ts="$(echo $line | cut -d' ' -f1)000000000" value=$(echo $line | cut -d' ' -f2) curl -i -XPOST $endpoint –data-binary "temp value=${value} ${ts}"doneFrom the Chronograf UI, we can see the data was correctly received.

Push the application to Docker HubOnce the application is working fine, it can be distributed through the Docker Hub via a simple push.

$ docker-app pushsha256:bb16877acb67.

3462d4ac81a1cf440We can then see it’s there, alongside the “regular” Docker images of my Docker Hub account.

The application is now ready to be used by anyone.

If you want to go and modify this application, just use the fork command and specify (optional) a folder in which all the application’s assets will be downloaded.

Below is an example of the usage of the fork command.

$ docker-app fork lucj/tick.

dockerapp:0.

1.

0 myuser/tick –path /tmp/mytickappGenerate a HELM chartWe saw above how easy it is to deploy the application on a Swarm.

It’s not much harder to have it ready to be deployed on a Kubernetes cluster.

This happens through the creation of a HELM chart (HELM being the Kubernetes Package Manager).

The following command creates the chart:$ docker-app helm$ tree .

├── telegraf.

conf├── tick.

chart│ ├── Chart.

yaml│ ├── templates│ │ └── stack.

yaml│ └── values.

yaml├── tick.

dockerapp│ ├── dev.

yml│ ├── docker-compose.

yml│ ├── metadata.

yml│ ├── prod.

yml│ ├── settings.

yml│ └── telegraf.

conf└── tick.

ymlAs we can see, a folder and a couple of files were generated in the process:Charts.

yaml contains the project metadata.

description: ""keywords: []maintainers:- name: luc <>name: tickversion: 0.

1.

0templates/stack.

yaml contains the Kubernetes manifest of the application.

A special Stack resource is used here.

kind: StackapiVersion: compose.

docker.

com/v1beta2metadata: name: tick generatename: "" namespace: "" selflink: "" uid: "" resourceversion: "" generation: 0 creationtimestamp: "0001–01–01T00:00:00Z" deletiontimestamp: null deletiongraceperiodseconds: null labels: {} annotations: {} ownerreferences: [] initializers: null finalizers: [] clustername: spec: services: — name: chronograf command: — chronograf — –influxdb-url=http://influxdb:8086 image: chronograf ports: — mode: ingress target: {{.

Values.

ports.

chronograf}} published: 8888 protocol: tcp — name: influxdb image: influxdb — name: kapacitor environment: KAPACITOR_INFLUXDB_0_URLS_0: http://influxdb:8086 image: kapacitor — name: telegraf configs: — source: telegraf-conf target: /etc/telegraf/telegraf.

conf image: telegraf ports: — mode: ingress target: {{.

Values.

ports.

telegraf}} published: 8186 protocol: tcp configs: telegraf-conf: file: telegraf.

confvalues.

yaml contains the default values that will be used in the placeholders above.

As we did not specify any settings file when generating the HELM chart, the values from settings.

yml are used.

ports: chronograf: 8081 telegraf: 8080Once the helm chart is generated, the application can be deployed on Kube with the same deploy command we used above, but with the additional -o kubernetes flag indicating the orchestrator to use (the default is Swarm).

$ docker-app deploy -o kubernetesWaiting for the stack to be stable and running…chronograf: Ready [pod status: 1/1 ready, 0/1 pending, 0/1 failed]influxdb: Ready [pod status: 1/1 ready, 0/1 pending, 0/1 failed]kapacitor: Ready [pod status: 1/1 ready, 0/1 pending, 0/1 failed]telegraf: Ready [pod status: 1/1 ready, 0/1 pending, 0/1 failed]Stack tick is stable and runningAs we use the default settings, the published ports are 8080 for Telegraf and 8081 for Chronograf (values defined in values.

yaml)Note: the deployment on Kubernetes only works on Docker Desktop or Docker Enterprise Edition which run the server side component needed to deal with the stack resource.

SummaryI hope this article provides some insights on the Docker Application Package.

The project is still quite young — only a couple of months old at the time of writing — so breaking changes may occur before it reaches 1.

0.

0.

It nevertheless looks really promising, and I’ll follow its evolution in future articles.

Thanks Gareth Rushgrove & Christ Crone for the review, and for pointing out additional resources.

.. More details

Leave a Reply