Ansible: How to set docker container labels with dynamic key names?
Asked Answered
L

2

7

I try to start docker containers with traefik labels. To create a traefik router for a container you have to put some labels like this.

app_names:
  - tower01
  - tower02

docker_labels:
    awx_web:
      traefik.enable: "true"
      traefik.http.routers.{{ app_name }}.entrypoints: "http"
      traefik.http.routers.{{ app_name }}.rule: "Host(`{{ app_server_fqdn }}`)"
      traefik.http.routers.{{ app_name }}.middlewares: "https-redirect@file"
      traefik.http.routers.{{ app_name }}-sec.entrypoints: "https"
      traefik.http.routers.{{ app_name }}-sec.rule: "Host(`{{ app_server_fqdn }}`)"
      traefik.http.routers.{{ app_name }}-sec.tls: "true"
      traefik.http.routers.{{ app_name }}-sec.tls.options: "myTLSOptions@file"
      traefik.http.routers.{{ app_name }}-sec.tls.certresolver: "le"
      traefik.http.routers.{{ app_name }}-sec.middlewares: "default-headers@file"
      traefik.http.services.{{ app_name }}.loadbalancer.server.port: "8052"
      com.centurylinklabs.watchtower.enable: "{{ autoupdate_container[loop_item] }}"

and use a task similar to this:

- name: "{{ app_name }} | create awx web container"
  docker_container:
    name:           "{{ app_name }}-web"
    hostname:       "awxweb"
    user:           "root"
    image:          "ansible/awx_web:{{ docker_image[loop_item] | default('latest') }}"
    env:            "{{ docker_env[loop_item] | default(omit) }}"
    networks:       [ name: "{{ app_name }}" ]
    purge_networks: true
    volumes:        "{{ docker_volumes[loop_item] | default(omit) }}"
    restart_policy: "unless-stopped"
    labels:         "{{ docker_labels[loop_item] | default(omit) }}"
    state:          "{{ state | default('started') }}"
  loop: "{{ app_names }}"

Of course the labels should be:

traefik.http.routers.tower01.entrypoints: "http"
traefik.http.routers.tower01.rule: "Host(`{{ app_server_fqdn }}`)"
traefik.http.routers.tower01.middlewares: "https-redirect@file"
traefik.http.routers.tower01-sec.entrypoints: "https"
traefik.http.routers.tower01-sec.rule: "Host(`{{ app_server_fqdn }}`)"
traefik.http.routers.tower01-sec.tls: "true"
traefik.http.routers.tower01-sec.tls.options: "myTLSOptions@file"
traefik.http.routers.tower01-sec.tls.certresolver: "le"
traefik.http.routers.tower01-sec.middlewares: "default-headers@file"
traefik.http.services.tower01.loadbalancer.server.port: "8052"

Nevertheless Ansible doesn't process the jinja variable in the key name.

Any idea?

Loophole answered 25/5, 2020 at 20:57 Comment(0)
G
2

You need to use a dictionary.

- name: Create Traefik labels's dictionary
  set_fact:
      my_labels: "{{ my_labels | default({}) | combine ({ item.key : item.value }) }}"
  with_items:
    - { 'key': 'traefik.enable' , 'value': 'true'}
    - { 'key': 'traefik.http.routers.{{ app_name }}.entrypoints' , 'value': 'http'}
    - { 'key': 'traefik.http.routers.{{ app_name }}.rule' , 'value': 'Host(`{{ app_server_fqdn }}`)'}
    - { 'key': 'traefik.http.routers.{{ app_name }}.middlewares' , 'value': 'https-redirect@file'}
    - ...

And use your new dictionary in docker_container :

- name: "{{ app_name }} | create awx web container"
  docker_container:
    name:           "{{ app_name }}-web"
    hostname:       "awxweb"
    user:           "root"
    image:          "ansible/awx_web:{{ docker_image[loop_item] | default('latest') }}"
    env:            "{{ docker_env[loop_item] | default(omit) }}"
    networks:       [ name: "{{ app_name }}" ]
    purge_networks: true
    volumes:        "{{ docker_volumes[loop_item] | default(omit) }}"
    restart_policy: "unless-stopped"
    labels:         "{{ my_labels }}"
    state:          "{{ state | default('started') }}"

With your loop, you can create diffrent dictionary : https://www.middlewareinventory.com/blog/ansible-dict/

Glabrescent answered 28/9, 2020 at 10:40 Comment(0)
F
1

You can define container labels by providing vars to your docker_container task:

---
- hosts: localhost
  gather_facts: false
  vars:
    app_server_fqdn: example.com
    app_names:
      - tower01
      - tower02
  tasks:
    - debug:
        msg: "{{ docker_labels }}"
      loop: "{{ app_names }}"
      vars:
        docker_labels: >-
          {{
            {
              "traefik.enable": "true",
              "traefik.http.routers.{}.entrypoints".format(item): "http",
              "traefik.http.routers.{}.rule".format(item): "Host(`{}`)".format(app_server_fqdn),
              "traefik.http.routers.{}.middlewares".format(item): "https-redirect@file",
              "traefik.http.routers.{}-sec.entrypoints".format(item): "https",
              "traefik.http.routers.{}-sec.rule".format(item): "Host(`{}`)".format(app_server_fqdn),
              "traefik.http.routers.{}-sec.tls".format(item): "true",
              "traefik.http.routers.{}-sec.tls.options".format(item): "myTLSOptions@file",
              "traefik.http.routers.{}-sec.tls.certresolver".format(item): "le",
              "traefik.http.routers.{}-sec.middlewares".format(item): "default-headers@file",
              "traefik.http.services.{}.loadbalancer.server.port".format(item): "8052"
            }
          }}

In this case you can use other filters (ternary for ex.) and more complex data for apps:

---
- hosts: localhost
  gather_facts: false
  vars:
    app_server_fqdn: example.com
    app_names:
      - name: tower01
        port: 8052
      - name: tower02
        port: 8053
      - name: tower03

  tasks:
    - debug:
        msg: "{{ docker_labels }}"
      loop: "{{ app_names }}"
      loop_control:
        loop_var: app
        label: "{{ app.name }}"
      vars:
        docker_labels: >-
          {{
            {
              "traefik.enable": "true",
              "traefik.http.routers.{}.entrypoints".format(app.name): "http",
              "traefik.http.routers.{}.rule".format(app.name): "Host(`{}`)".format(app_server_fqdn),
              "traefik.http.routers.{}.middlewares".format(app.name): "https-redirect@file",
              "traefik.http.routers.{}-sec.entrypoints".format(app.name): "https",
              "traefik.http.routers.{}-sec.rule".format(app.name): "Host(`{}`)".format(app_server_fqdn),
              "traefik.http.routers.{}-sec.tls".format(app.name): "true",
              "traefik.http.routers.{}-sec.tls.options".format(app.name): "myTLSOptions@file",
              "traefik.http.routers.{}-sec.tls.certresolver".format(app.name): "le",
              "traefik.http.routers.{}-sec.middlewares".format(app.name): "default-headers@file"
            } | combine(
                  (app.port is defined) | ternary(
                    {'traefik.http.services.{}.loadbalancer.server.port'.format(app.name): app.port}, {}
                  )
                )
          }}

Result:

TASK [debug] ********************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=tower01) => 
  msg:
    traefik.enable: 'true'
    traefik.http.routers.tower01-sec.entrypoints: https
    traefik.http.routers.tower01-sec.middlewares: default-headers@file
    traefik.http.routers.tower01-sec.rule: Host(`example.com`)
    traefik.http.routers.tower01-sec.tls: 'true'
    traefik.http.routers.tower01-sec.tls.certresolver: le
    traefik.http.routers.tower01-sec.tls.options: myTLSOptions@file
    traefik.http.routers.tower01.entrypoints: http
    traefik.http.routers.tower01.middlewares: https-redirect@file
    traefik.http.routers.tower01.rule: Host(`example.com`)
    traefik.http.services.tower01.loadbalancer.server.port: 8052
ok: [localhost] => (item=tower02) => 
  msg:
    traefik.enable: 'true'
    traefik.http.routers.tower02-sec.entrypoints: https
    traefik.http.routers.tower02-sec.middlewares: default-headers@file
    traefik.http.routers.tower02-sec.rule: Host(`example.com`)
    traefik.http.routers.tower02-sec.tls: 'true'
    traefik.http.routers.tower02-sec.tls.certresolver: le
    traefik.http.routers.tower02-sec.tls.options: myTLSOptions@file
    traefik.http.routers.tower02.entrypoints: http
    traefik.http.routers.tower02.middlewares: https-redirect@file
    traefik.http.routers.tower02.rule: Host(`example.com`)
    traefik.http.services.tower02.loadbalancer.server.port: 8053
ok: [localhost] => (item=tower03) => 
  msg:
    traefik.enable: 'true'
    traefik.http.routers.tower03-sec.entrypoints: https
    traefik.http.routers.tower03-sec.middlewares: default-headers@file
    traefik.http.routers.tower03-sec.rule: Host(`example.com`)
    traefik.http.routers.tower03-sec.tls: 'true'
    traefik.http.routers.tower03-sec.tls.certresolver: le
    traefik.http.routers.tower03-sec.tls.options: myTLSOptions@file
    traefik.http.routers.tower03.entrypoints: http
    traefik.http.routers.tower03.middlewares: https-redirect@file
    traefik.http.routers.tower03.rule: Host(`example.com`)
Fuel answered 7/4, 2021 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.