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
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"