Skip to content

Template Heat for Docker Environment

Deploy

You can easily and quickly deploy a Docker environment with Traefik and Portainer using a Heat template that we provide:

openstack stack create --template https://docs.infomaniak.cloud/tutorials/05.docker/docker.yml docker \
--parameter key='<YOUR_KEY>' \
--parameter EMAIL='<YOUR_EMAIL>' \
--parameter NDD='<YOUR_NDD>'

Warning

Remember to replace the variables <YOUR_KEY> by your key pair , <YOUR_EMAIL> by your email address and <YOUR_NDD> by your domain name !

After a few minutes, you can use the following command to display your Docker environment informations:

$ openstack stack show docker

+-----------------------+----------------------------------------------------------------------------------------------------------------------------------+
| Field                 | Value                                                                                                                            |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------+
| id                    | f88dde5f-3729-4893-b06b-b6e57a9620af                                                                                             |
| stack_name            | docker                                                                                                                           |
| description           | Docker Environment                                                                                                               |
| creation_time         | 2021-11-11T13:50:53Z                                                                                                             |
| updated_time          | None                                                                                                                             |
| stack_status          | CREATE_COMPLETE                                                                                                                  |
| stack_status_reason   | Stack CREATE completed successfully                                                                                              |
| parameters            | EMAIL: xxxxxxxx@xxxxxxxxx.xxx                                                                                                    |
|                       | NDD: wiki-tech.fr                                                                                                                |
|                       | OS::project_id: 7368f02b559648d0a9ff15bff29b464f                                                                                 |
|                       | OS::stack_id: f88dde5f-3729-4893-b06b-b6e57a9620af                                                                               |
|                       | OS::stack_name: docker                                                                                                           |
|                       | flavor: a4-ram8-disk50-perf1                                                                                                     |
|                       | floating_network_id: ext-floating1                                                                                               |
|                       | image: Debian 11 bullseye                                                                                                      |
|                       | key: PAPAMICA-INFOKEY                                                                                                            |
|                       | network: docker-network                                                                                                          |
|                       | subnet_id: docker-subnet                                                                                                         |
|                       |                                                                                                                                  |
| outputs               | - description: IP                                                                                                                |
|                       |   output_key: server_IP                                                                                                          |
|                       |   output_value: /!\ Don't forget to redirect traefik.xxxxx.xxx and portainer.xxxxx.xxx                                           |
|                       |     to 195.15.XXX.XX!                                                                                                            |
|                       | - description: Portainer URL                                                                                                     |
|                       |   output_key: portainer_url                                                                                                      |
|                       |   output_value: https://portainer.xxxxx.xxx                                                                                      |
|                       | - description: Traefik URL                                                                                                       |
|                       |   output_key: traefik_url                                                                                                        |
|                       |   output_value: https://traefik.xxxxx.xxx (admin / xXXxxXXXxx1111XXX1)                                                           |
|                       |                                                                                                                                  |
| links                 | - href: https://api.pub1.infomaniak.cloud/v1/xxxxxxxxxxxxxxxxxxx/stacks/docker/f88dde5f-3729-4893-b06b-b6e57a9620af              |
|                       |   rel: self                                                                                                                      |
|                       |                                                                                                                                  |
| deletion_time         | None                                                                                                                             |
| notification_topics   | []                                                                                                                               |
| capabilities          | []                                                                                                                               |
| disable_rollback      | True                                                                                                                             |
| timeout_mins          | None                                                                                                                             |
| stack_owner           | PCU-U2CAZJ4                                                                                                                      |
| parent                | None                                                                                                                             |
| stack_user_project_id | 1fff4ec137f842e0b1c9e022c7fcafa4                                                                                                 |
| tags                  | []                                                                                                                               |
|                       |                                                                                                                                  |
+-----------------------+----------------------------------------------------------------------------------------------------------------------------------+

In this example, Traefik is accessible from : https://traefik.xxxxx.xxx with admin / xXXxxXXXxx1111XXX1 and Portainer is accessible from : https://portainer.xxxxx.xxx

traefik.png

portainer.png

Heat Template

heat_template_version: rocky
description:  Docker Environment
parameters:
  image:
    type: string
    description: image use for docker
    default: Debian 11 bullseye
  key:
    type: string
    description: SSH key to connect to the servers
  flavor:
    type: string
    description: flavor used by docker
    default: a4-ram8-disk50-perf1
  network:
    type: string
    description: network used by docker
    default: docker-network
  subnet_id:
    type: string
    description: dedicated subnet for docker
    default: docker-subnet
  floating_network_id:
    type: string
    description: UUID of a Neutron external network
    default: ext-floating1
  EMAIL:
    type: string
    description: Email for Let's Encrypt
  NDD:
    type: string
    description: Domain for URL (example.com)

resources:
  wait_condition:
    type: OS::Heat::WaitCondition
    properties:
      handle: { get_resource: wait_handle }
      timeout: 600

  wait_handle:
    type: OS::Heat::WaitConditionHandle

  random_password_traefik:
    type: OS::Heat::RandomString
    properties:
      length: 16


  # security group
  docker_security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: "docker_security_group"
      description: >
        Allows ICMP, SSH, HTTP & HTTPS default port
      rules:
         - { direction: ingress, protocol: icmp }
         - { direction: ingress, protocol: tcp, port_range_min: 22, port_range_max: 22 }
         - { direction: ingress, protocol: tcp, port_range_min: 80, port_range_max: 80 }
         - { direction: ingress, protocol: tcp, port_range_min: 443, port_range_max: 443 }

  # network resources
  docker_network:
    type: OS::Neutron::Net
    properties:
      name: { get_param: network }
      value_specs:
        mtu: 1500

  docker_subnet:
    type: OS::Neutron::Subnet
    properties:
      name: 'docker-subnet'
      network_id: { get_resource: docker_network }
      cidr: "10.11.3.0/24"
      dns_nameservers:
        - "84.16.67.69"
        - "84.16.67.70"
      ip_version: 4

  docker_router:
    type: OS::Neutron::Router
    properties:
      name:  'docker-router'
      external_gateway_info: { network: ext-floating1 }

  docker_router_subnet_interface:
    type: OS::Neutron::RouterInterface
    properties:
      router_id: { get_resource: docker_router }
      subnet: { get_resource: docker_subnet }

  docker_port:
    type: OS::Neutron::Port
    properties:
      network: { get_resource: docker_network }
      security_groups: [ { get_resource: docker_security_group } ]
      fixed_ips:
        - subnet_id: { get_resource: docker_subnet }

  docker_floating:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network_id: { get_param: floating_network_id }
      port_id: { get_resource: docker_port }

  # instance
  server:
    type: OS::Nova::Server
    depends_on: [ docker_router]
    properties:
      flavor: { get_param: flavor }
      image: { get_param: image }
      key_name: {get_param: key}
      networks:
        - port: { get_resource: docker_port }
      user_data:
        str_replace:
          template: |
            #!/bin/bash -v
            apt update && apt upgrade -y
            apt install -y curl apt-transport-https ca-certificates gnupg2 software-properties-common apache2-utils
            curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | apt-key add -
            add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
            apt-get update
            apt-get -y install docker-ce docker-compose
            systemctl enable docker
            systemctl start docker
            mkdir -p /apps/traefik
            cat <<EOF >>/apps/traefik/traefik.yml
            api:
              dashboard: true
            entryPoints:
              http:
                address: ":80"
              https:
                address: ":443"
            providers:
              docker:
                endpoint: "unix:///var/run/docker.sock"
                exposedByDefault: false
            certificatesResolvers:
              http:
                acme:
                  email: $EMAIL
                  storage: acme.json
                  httpChallenge:
                    entryPoint: http
            providers.file:
                filename: "/etc/traefik/dynamic_conf.toml"
                watch: true
            EOF
            cat <<EOF >>/apps/traefik/config.yml
            http:
              middlewares:
                https-redirect:
                  redirectScheme:
                    scheme: https
                default-headers:
                  headers:
                    frameDeny: true
                    sslRedirect: true
                    browserXssFilter: true
                    contentTypeNosniff: true
                    forceSTSHeader: true
                    stsIncludeSubdomains: true
                    stsPreload: true
                secured:
                  chain:
                    middlewares:
                    - default-headers
            EOF
            touch /apps/traefik/acme.json
            chmod 600 /apps/traefik/acme.json
            docker network create proxy
            USERPASS=$(echo $(htpasswd -nb admin $PASSWORD_TRAEFIK) | sed -e s/\\$/\\$\\$/g)
            cat <<EOF >>/apps/traefik/docker-compose.yml
            version: '2'
            services:
              traefik:
                image: traefik:latest
                container_name: traefik
                restart: unless-stopped
                security_opt:
                  - no-new-privileges:true
                networks:
                  - proxy
                ports:
                  - 80:80
                  - 443:443
                volumes:
                  - /etc/localtime:/etc/localtime:ro
                  - /var/run/docker.sock:/var/run/docker.sock:ro
                  - /apps/traefik/traefik.yml:/traefik.yml:ro
                  - /apps/traefik/acme.json:/acme.json
                  - /apps/traefik/config.yml:/config.yml:ro
                labels:
                  - traefik.enable=true
                  - traefik.http.routers.traefik.entrypoints=http
                  - traefik.http.routers.traefik.rule=Host("traefik.$NDD")
                  - traefik.http.middlewares.traefik-auth.basicauth.users=$USERPASS
                  - traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https
                  - traefik.http.routers.traefik.middlewares=traefik-https-redirect
                  - traefik.http.routers.traefik-secure.entrypoints=https
                  - traefik.http.routers.traefik-secure.rule=Host("traefik.$NDD")
                  - traefik.http.routers.traefik-secure.middlewares=traefik-auth
                  - traefik.http.routers.traefik-secure.tls=true
                  - traefik.http.routers.traefik-secure.tls.certresolver=http
                  - traefik.http.routers.traefik-secure.service=api@internal
            networks:
              proxy:
                external: true
            EOF
            docker-compose -f /apps/traefik/docker-compose.yml up -d
            mkdir -p /apps/portainer
            cat <<EOF >>/apps/portainer/docker-compose.yml
            version: '2'
            services:
              portainer:
                image: portainer/portainer-ce:latest
                container_name: portainer
                restart: unless-stopped
                security_opt:
                  - no-new-privileges:true
                networks:
                  - proxy
                volumes:
                  - /etc/localtime:/etc/localtime:ro
                  - /var/run/docker.sock:/var/run/docker.sock:ro
                  - /apps/portainer/data:/data
                labels:
                  - traefik.enable=true
                  - traefik.http.routers.portainer.entrypoints=http
                  - traefik.http.routers.portainer.rule=Host("portainer.$NDD")
                  - traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https
                  - traefik.http.routers.portainer.middlewares=portainer-https-redirect
                  - traefik.http.routers.portainer-secure.entrypoints=https
                  - traefik.http.routers.portainer-secure.rule=Host("portainer.$NDD")
                  - traefik.http.routers.portainer-secure.tls=true
                  - traefik.http.routers.portainer-secure.tls.certresolver=http
                  - traefik.http.routers.portainer-secure.service=portainer
                  - traefik.http.services.portainer.loadbalancer.server.port=9000
                  - traefik.docker.network=proxy

            networks:
              proxy:
                external: true
            EOF
            docker-compose -f /apps/portainer/docker-compose.yml up -d

            wc_notify --data-binary '{"status": "SUCCESS"}'

          params:
            wc_notify: { get_attr: ['wait_handle', 'curl_cli'] }
            $NDD: { get_param: NDD }
            $EMAIL: { get_param: EMAIL }
            $PASSWORD_TRAEFIK:  { get_attr: [random_password_traefik, value] }


outputs:
  traefik_url:
    value:
      str_replace:
        template: https://traefik.NDD (admin / PASSWORD)
        params:
          NDD: { get_param: NDD }
          PASSWORD: { get_attr: [random_password_traefik, value] }
    description: "Traefik URL"

  portainer_url:
    value:
      str_replace:
        template: https://portainer.NDD
        params:
          NDD: { get_param: NDD }
    description: "Portainer URL"

  server_IP:
    value:
      str_replace:
        template: /!\ Don't forget to redirect traefik.NDD and portainer.NDD to IP !
        params:
          NDD: { get_param: NDD }
          IP: { get_attr: [docker_floating, floating_ip_address] }
    description: "IP"