initial commit

This commit is contained in:
Ludovic Cartier 2022-08-05 20:59:55 +02:00
commit da764e602e
19 changed files with 587 additions and 0 deletions

56
README.md Normal file
View File

@ -0,0 +1,56 @@
Docker services
===============
The present role :
- installs Docker on host
- installs various services through containers and docker-compose manifest
It has bienn tested on :
- Debian 9
- Debian 10
- Debian 11
Role variables
---------------
| Variable | Type | Choices | Default | Comment |
|----------------------------------------------|---------|------------------------------------------------------------------------------------|-------------------------|-----------------|
Dependencies
------------
None.
Example Playbook
----------------
- hosts: example
ignore_errors: "{{ ansible_check_mode }}" # ignore errors only in check mode !
roles:
- { role: docker-services, tags: ['docker-services'] }
Example variables
-----------------
---
docker_services:
- traefik
- watchtower
- grafana
traefik_domain: 'mydomain.com'
traefik_letsencrypt_email: 'cert@mydomain.com'
TODO
----
License
-------
MIT Modern
Author Information
------------------
Written by Ludovic Cartier <ludovic.cartier@brainsys.io>

10
TODO Normal file
View File

@ -0,0 +1,10 @@
##### traefik
template / vars for basic auth ? for each user / pass
-> several users OK ?
global auth vs service auth ?
#### grafana
auth / pas auth ?
push provisionner
push dashboard

52
defaults/main.yml Normal file
View File

@ -0,0 +1,52 @@
---
# grafana
grafana_auth_anonymous_enabled: true
grafana_auth_anonymous_org_role: Editor # Viewer
grafana_auth_anonymous_org_name: 'Main Org.'
grafana_auth_disable_login_form: true
grafana_editors_can_admin: false
grafana_users_viewers_can_edit: false
grafana_log_level: error
grafana_router_logging: false
grafana_disable_sanitize_html: true
# provisionning dashboards
# see https://grafana.com/docs/administration/provisioning/#dashboards
awh_services_grafana_provisionning_dashboards:
apiVersion: 1
providers:
- name: 'Grafana Dashboards'
orgId: 1
folder: ''
folderUid: ''
type: file
disableDeletion: false
editable: true
updateIntervalSeconds: 11
options:
path: /var/lib/grafana/dashboards
# provisionning datasources.
# see https://grafana.com/docs/administration/provisioning/#datasources
awh_services_grafana_provisionning_datasources:
- name: loki
type: loki
access: proxy
url: http://loki:3100
jsonData:
httpMode: GET
editable: false
isDefault: false
#apiVersion: 1
#datasources:
- name: prometheus
type: prometheus
access: proxy
database: prometheus
url: http://10.0.226.252:9090
jsonData:
httpMode: GET
editable: false
isDefault: true

17
files/traefik/logrotate Normal file
View File

@ -0,0 +1,17 @@
/var/log/docker/docker-daemon.log
/var/log/docker/*.log {
daily
missingok
rotate 356
compress
compresscmd /bin/bzip2
uncompresscmd /bin/bunzip2
compressoptions -9
compressext .bz2
dateext
dateformat -%Y%m%d-%s
dateyesterday
notifempty
create 640 root adm
sharedscripts
}

12
files/traefik/rsyslog Normal file
View File

@ -0,0 +1,12 @@
# {{ ansible_managed }}
$Template docker_split_log,"/var/log/docker/%syslogtag:R,ERE,1,DFLT:docker_(.+)(\[[0-9]+\]):--end%.log"
$Template docker-daemon_log,"/var/log/docker/docker-daemon.log"
# docker services logs splitted per tag prefixed docker_<servicename>
:syslogtag, startswith, "docker_" ?docker_split_log
:syslogtag, startswith, "docker_" stop
:syslogtag, startswith, "dockerd" ?docker-daemon_log
:syslogtag, startswith, "dockerd" stop

18
handlers/main.yml Normal file
View File

@ -0,0 +1,18 @@
---
- name: traefik-restart
systemd:
name: docker-compose@traefik
state: restarted
tags: ['docker_traefik']
- name: watchtower-restart
systemd:
name: docker-compose@watchtower
state: restarted
tags: ['docker_watchtower']
- name: grafana-restart
systemd:
name: docker-compose@grafana
state: restarted
tags: ['docker_grafana']

9
meta/main.yml Normal file
View File

@ -0,0 +1,9 @@
---
galaxy_info:
author: Ludovic Cartier
description: Install and configure Docker and services
company: brainsys
license: GPLv3
min_ansible_version: 2.8
issue_tracker_url: https://github.com/brainsys-io/ansible-role-docker-services/issues
github_branch: master

37
tasks/base.yml Normal file
View File

@ -0,0 +1,37 @@
---
#- name: debug
# debug:
# msg: "Service: {{ service }}"
- name: "{{ service }} | create docker-compose directory"
file:
path: /opt/docker-compose/{{ service }}
state: directory
mode: '0755'
tags: [ 'docker_{{ service }}' ]
- name: "{{ service }} | copy docker-compose file"
template:
src: compose/{{ service }}.yml.j2
dest: /opt/docker-compose/{{ service }}/docker-compose.yml
owner: root
group: root
mode: 0644
notify: "{{ service }}-restart"
tags: [ 'docker_{{ service }}' ]
- name: "{{ service }} | install unit file to systemd"
template:
src: systemd/docker-compose.service.j2
dest: /etc/systemd/system/docker-compose@{{ service }}.service
owner: root
group: root
mode: 0600
tags: [ 'docker_{{ service }}' ]
- name: "{{ service }} | enable service"
systemd:
daemon_reload: yes
name: docker-compose@{{ service }}
enabled: true
tags: [ 'docker_{{ service }}' ]

56
tasks/docker.yml Normal file
View File

@ -0,0 +1,56 @@
---
- name: add official GPG key
apt_key:
url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
state: present
tags: ['docker']
- name: add repository
apt_repository:
repo: "deb https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
state: present
tags: ['docker']
- name: install packages
apt:
name:
- docker-ce
- docker-ce-cli
- docker-compose
state: present
tags: ['docker']
- name: create docker-compose directory
file:
path: /opt/docker-compose
state: directory
mode: '0755'
tags: ['docker']
- name: install Python module
pip:
name:
- docker
- docker-compose
tags: ['docker']
- name: ensure Docker is started and enabled at boot
service:
name: docker
state: started
enabled: true
tags: ['docker']
- name: copy rsyslog config
copy:
src: traefik/rsyslog
dest: /etc/rsyslog.d/10-docker.conf
mode: '0644'
force: yes
- name: copy logrotate config
copy:
src: traefik/logrotate
dest: /etc/logrotate.d/docker
mode: '0644'
force: yes

67
tasks/grafana.yml Normal file
View File

@ -0,0 +1,67 @@
---
- name: grafana | check vars are defined
assert:
that:
- grafana_admin_password is defined
- grafana_auth_anonymous_org_role is defined
- grafana_auth_anonymous_org_name is defined
- grafana_domain is defined
tags: ['docker_grafana']
- include_tasks: base.yml
tags: ['docker_grafana']
- name: grafana | create docker volume data
docker_volume:
name: grafana__var_lib_grafana
tags: ['docker_grafana']
- name: grafana | create provisioning dashboards docker volume
docker_volume:
name: grafana__etc_grafana_provisioning_dashboards
tags: ['docker_grafana']
- name: grafana | create provisioning datasources docker volume
docker_volume:
name: grafana__etc_grafana_provisioning_datasources
tags: ['docker_grafana']
#- name: grafana | ensure data perms
# file:
# path: '{{ item }}'
# owner: '472'
# group: '472'
# state: directory
# with_items:
# - '{{ register_docker_volume_grafana__var_lib_grafana.ansible_facts.docker_volume.Mountpoint }}'
# - '{{ register_docker_volume_grafana__var_lib_grafana.ansible_facts.docker_volume.Mountpoint }}/dashboards'
# - '{{ register_docker_volume_grafana__etc_grafana_provisioning_dashboards.ansible_facts.docker_volume.Mountpoint }}'
# - '{{ register_docker_volume_grafana__etc_grafana_provisioning_datasources.ansible_facts.docker_volume.Mountpoint }}'
# notify: 'docker restart grafana'
# tags: ['grafana']
#
#- name: grafana | configure provisionning dashboards
# copy:
# dest: '{{ register_docker_volume_grafana__etc_grafana_provisioning_dashboards.ansible_facts.docker_volume.Mountpoint }}/local.yml'
# content: |
# {{ grafana_provisionning_dashboards|to_nice_yaml }}
# notify: 'docker restart grafana'
# tags: ['grafana']
#
#- name: grafana | configure provisionning datasources
# copy:
# dest: '{{ register_docker_volume_grafana__etc_grafana_provisioning_datasources.ansible_facts.docker_volume.Mountpoint }}/datasources.yml'
# content: |
# {{ grafana_provisionning_datasources|to_nice_yaml }}
# notify: 'docker restart grafana'
# tags: ['grafana']
#
#- name: grafana | download dashboard
# get_url:
# url: '{{ item.url }}'
# dest: '{{ register_docker_volume_grafana__var_lib_grafana.ansible_facts.docker_volume.Mountpoint + "/dashboards/" + item.name }}.json'
# force: '{{ item.force|default(grafana_dashboards_force|default("no")) }}'
# with_items: '{{ grafana_dashboards|default([]) }}'
# loop_control:
# label: '{{ item.name }}'
# tags: ['grafana']

17
tasks/main.yml Normal file
View File

@ -0,0 +1,17 @@
---
- name: requirements
include_tasks: requirements.yml
- name: docker
include_tasks: docker.yml
- name: services
vars:
service: "{{ item }}"
include_tasks: "{{ item }}.yml"
tags:
- docker_traefik
- docker_watchtower
- docker_grafana
with_items:
- "{{ docker_services }}"

19
tasks/requirements.yml Normal file
View File

@ -0,0 +1,19 @@
---
- name: update APT Cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: pre-requirements install
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg-agent
- software-properties-common
- python3-pip
- virtualenv
- python3-setuptools
- gnupg2
state: present

37
tasks/traefik.yml Normal file
View File

@ -0,0 +1,37 @@
---
- name: traefik | check vars are defined
assert:
that:
- traefik_domain is defined
- traefik_letsencrypt_email is defined
tags: ['docker_traefik']
- include_tasks: base.yml
tags: ['docker_traefik']
- name: traefik | create docker network
docker_network:
name: 'traefik'
tags: ['docker_traefik']
- name: traefik | create letsencrypt docker volume
docker_volume:
name: traefik__letsencrypt
register: register_docker_volume_traefik__letsencrypt
tags: ['docker_traefik']
- name: traefik | create config docker volume
docker_volume:
name: traefik__etc_traefik
register: register_docker_volume_traefik__etc_traefik
tags: ['docker_traefik']
- name: traefik | copy configuration file
template:
src: config/traefik/traefik.yml.j2
dest: /var/lib/docker/volumes/traefik__etc_traefik/_data/traefik.yml
owner: root
group: root
mode: 0644
notify: traefik-restart
tags: ['docker_traefik']

3
tasks/watchtower.yml Normal file
View File

@ -0,0 +1,3 @@
---
- include_tasks: base.yml
tags: ['docker_watchtower']

View File

@ -0,0 +1,61 @@
---
version: '3.7'
networks:
traefik:
external: true
volumes:
grafana__var_lib_grafana:
external: true
grafana__etc_grafana_provisioning_dashboards:
external: true
grafana__etc_grafana_provisioning_datasources:
external: true
services:
grafana:
image: grafana/grafana:{{ grafana_version | default("latest") }}
container_name: grafana
restart: 'unless-stopped'
volumes:
- grafana__var_lib_grafana:/var/lib/grafana
- grafana__etc_grafana_provisioning_dashboards:/etc/grafana/provisioning/dashboards
- grafana__etc_grafana_provisioning_datasources:/etc/grafana/provisioning/datasources
labels:
traefik.enable: true
traefik.docker.network: traefik
traefik.http.routers.grafana.rule: Host(`{{ grafana_domain }}`)
traefik.http.routers.grafana.tls: true
traefik.http.routers.grafana.tls.certresolver: letsencrypt
traefik.http.routers.grafana.entrypoints: websecure
traefik.http.services.grafana.loadbalancer.server.port: 3000
environment:
GF_AUTH_ANONYMOUS_ENABLED: "{{ grafana_auth_anonymous_enabled|string|lower }}"
GF_AUTH_ANONYMOUS_ORG_ROLE: "{{ grafana_auth_anonymous_org_role }}"
GF_AUTH_ANONYMOUS_ORG_NAME: "{{ grafana_auth_anonymous_org_name }}"
GF_AUTH_DISABLE_LOGIN_FORM: "{{ grafana_auth_disable_login_form|string|lower }}"
GF_AUTH_EDITORS_CAN_ADMIN: "{{ grafana_editors_can_admin|string|lower }}"
GF_SECURITY_ADMIN_PASSWORD: "{{ grafana_admin_password }}"
GF_USERS_VIEWERS_CAN_EDIT: "{{ grafana_users_viewers_can_edit|string|lower }}"
GF_ROOT_URL: "{{ grafana_domain }}"
GF_SMTP_ENABLED: "false"
GF_LOG_LEVEL: "{{ grafana_log_level|string }}"
GF_ROUTER_LOGGING: "{{ grafana_router_logging|string|lower }}"
GF_PANELS_DISABLE_SANITIZE_HTML: "{{ grafana_disable_sanitize_html|string|lower }}"
{% if grafana_install_plugins is defined %}
GF_INSTALL_PLUGINS: "{{ grafana_install_plugins|string|lower }}"
{% endif %}
{% if grafana_smtp_enabled is defined %}
GF_SMTP_ENABLED: "{{ grafana_smtp_enabled|string|lower }}"
GF_SMTP_HOST: "{{ grafana_smtp_host|string }}"
GF_SMTP_FROM_ADDRESS: "{{ grafana_smtp_from_address|string }}"
GF_SMTP_FROM_NAME: "{{ grafana_smtp_from_name|string }}"
GF_SMTP_SKIP_VERIFY: "{{ grafana_smtp_skip_verify|string|lower }}"
{% endif %}
logging:
driver: syslog
options:
tag: docker_grafana
networks:
- traefik

View File

@ -0,0 +1,45 @@
---
version: '3.7'
networks:
traefik:
external: true
volumes:
traefik__etc_traefik:
external: true
traefik__letsencrypt:
external: true
services:
traefik:
image: traefik:latest
container_name: traefik
restart: 'unless-stopped'
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik__etc_traefik:/etc/traefik:ro
- traefik__letsencrypt:/letsencrypt
command:
- "--providers.providersthrottleduration=100"
labels:
traefik.enable: true
traefik.http.routers.traefik.rule: Host(`{{ traefik_domain }}`)
traefik.http.routers.traefik.entrypoints: websecure
traefik.http.routers.traefik.service: api@internal
traefik.http.routers.traefik.tls: true
traefik.http.routers.traefik.tls.certresolver: letsencrypt
traefik.http.routers.traefik.middlewares: auth
traefik.http.routers.dashboard.rule: Host(`{{ traefik_domain }}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
traefik.http.middlewares.auth.basicauth.users: "ludal:$$apr1$$N3vklVTY$$zrq2kwkaVdynGlakyb4J7."
traefik.http.middlewares.auth.basicauth.realm: {{ traefik_domain}} - restricted access
logging:
driver: syslog
options:
tag: docker_traefik
networks:
- traefik

View File

@ -0,0 +1,22 @@
version: '3.7'
services:
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
TZ: {{ watchtower_tz | default("Europe/Paris") }}
{% if watchtower_notifications is defined %}
WATCHTOWER_NOTIFICATIONS: {{ watchtower_notifications }}
WATCHTOWER_NOTIFICATION_URL: {{ watchtower_notifications_url }}
{% endif %}
WATCHTOWER_SCHEDULE: "{{ watchtower_schedule | default('0 0 3 * * *') }}"
WATCHTOWER_CLEANUP: "{{ watchtower_cleanup | default('true') }}"
WATCHTOWER_DEBUG: {{ watchtower_debug | default('"false"') }}
{% if watchtower_http_api_metrics is defined %}
WATCHTOWER_HTTP_API_METRICS: "{{ watchtower_http_api_metrics }}"
WATCHTOWER_HTTP_API_TOKEN: "{{ watchtower_http_api_token | default('changeme_') }}"
{% endif %}

View File

@ -0,0 +1,35 @@
log:
level: INFO
providers:
docker:
network: traefik
exposedByDefault: false
global:
sendAnonymousUsage: false
api:
dashboard: true
entryPoints:
openvpn:
address: :1194
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: :443
certificatesResolvers:
letsencrypt:
acme:
email: {{ traefik_letsencrypt_email }}
storage: /letsencrypt/acme.json
tlschallenge: true

View File

@ -0,0 +1,14 @@
[Unit]
Description=%i service with docker compose
PartOf=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/opt/docker-compose/{{ service }}
ExecStart=/usr/bin/docker-compose up -d
ExecStop=/usr/bin/docker-compose down
[Install]
WantedBy=multi-user.target