From 1b943fbf3e639b6e664b0549b9a33ae111bdb557 Mon Sep 17 00:00:00 2001 From: norimicry Date: Sat, 13 Apr 2019 02:06:51 +0200 Subject: [PATCH] Terraform deploy on hetzner cloud --- playbook.yml | 38 +- roles/nickjj.docker/.gitignore | 23 + roles/nickjj.docker/.travis.yml | 16 + roles/nickjj.docker/CHANGELOG.md | 190 +++++++++ roles/nickjj.docker/LICENSE | 22 + roles/nickjj.docker/README.md | 397 ++++++++++++++++++ roles/nickjj.docker/defaults/main.yml | 65 +++ roles/nickjj.docker/handlers/main.yml | 6 + roles/nickjj.docker/meta/.galaxy_install_info | 1 + roles/nickjj.docker/meta/main.yml | 26 ++ roles/nickjj.docker/tasks/main.yml | 193 +++++++++ .../etc/apt/preferences.d/docker-ce.pref.j2 | 4 + .../templates/etc/docker/daemon.json.j2 | 11 + .../system/docker.service.d/custom.conf.j2 | 3 + .../docker.service.d/environment.conf.j2 | 4 + .../system/docker.service.d/options.conf.j2 | 5 + roles/nickjj.docker/tests/.yamllint.yml | 5 + roles/nickjj.docker/tests/test.py | 61 +++ roles/nickjj.docker/tests/test.yml | 41 ++ terra.sh | 9 + terraform/main.tf | 41 ++ terraform/ssh/key | 27 ++ terraform/ssh/key.pub | 1 + terraform/vars.auto.tfvars | 2 + 24 files changed, 1189 insertions(+), 2 deletions(-) create mode 100755 roles/nickjj.docker/.gitignore create mode 100755 roles/nickjj.docker/.travis.yml create mode 100755 roles/nickjj.docker/CHANGELOG.md create mode 100755 roles/nickjj.docker/LICENSE create mode 100755 roles/nickjj.docker/README.md create mode 100755 roles/nickjj.docker/defaults/main.yml create mode 100755 roles/nickjj.docker/handlers/main.yml create mode 100644 roles/nickjj.docker/meta/.galaxy_install_info create mode 100755 roles/nickjj.docker/meta/main.yml create mode 100755 roles/nickjj.docker/tasks/main.yml create mode 100755 roles/nickjj.docker/templates/etc/apt/preferences.d/docker-ce.pref.j2 create mode 100755 roles/nickjj.docker/templates/etc/docker/daemon.json.j2 create mode 100755 roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/custom.conf.j2 create mode 100755 roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/environment.conf.j2 create mode 100755 roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/options.conf.j2 create mode 100755 roles/nickjj.docker/tests/.yamllint.yml create mode 100644 roles/nickjj.docker/tests/test.py create mode 100755 roles/nickjj.docker/tests/test.yml create mode 100755 terra.sh create mode 100644 terraform/main.tf create mode 100644 terraform/ssh/key create mode 100644 terraform/ssh/key.pub create mode 100644 terraform/vars.auto.tfvars diff --git a/playbook.yml b/playbook.yml index 6101a7a..e5c68c2 100644 --- a/playbook.yml +++ b/playbook.yml @@ -1,10 +1,44 @@ --- -- name: Example +- name: Stardew hosts: "all" become: true roles: - role: "nickjj.docker" tags: ["docker"] - - role "stardew" + + tasks: + - name: Creates directory + file: + path: /var/docker/stardew + state: directory + - name: "Copy Shit over" + copy: + src: ./docker-compose.yml + dest: /var/docker/stardew/docker-compose.yml + - name: "Copy Shit over" + copy: + src: ./asound.conf + dest: /var/docker/stardew/asound.conf + - name: "Copy Shit over" + copy: + src: ./init.sh + dest: /var/docker/stardew/init.sh + - name: "Copy Shit over" + copy: + src: ./Dockerfile + dest: /var/docker/stardew/Dockerfile + - name: "Copy Shit over" + copy: + src: ./config + dest: /var/docker/stardew/config + - name: Pip install docker for Ansible's docker_login and docker_service modules + pip: + name: ["docker", "docker-compose"] + - name: "Start docker-compose" + docker_service: + project_src: "/var/docker/stardew" + pull: yes + + diff --git a/roles/nickjj.docker/.gitignore b/roles/nickjj.docker/.gitignore new file mode 100755 index 0000000..d498f61 --- /dev/null +++ b/roles/nickjj.docker/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +*/**.DS_Store +._* +.*.sw* +*~ +.idea/ +.vscode/ +*.retry + +__pycache__/ +*.py[cod] +*$py.class +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ diff --git a/roles/nickjj.docker/.travis.yml b/roles/nickjj.docker/.travis.yml new file mode 100755 index 0000000..6b9882c --- /dev/null +++ b/roles/nickjj.docker/.travis.yml @@ -0,0 +1,16 @@ +--- + +services: "docker" + +env: + - distro: "ubuntu1604" + - distro: "ubuntu1804" + - distro: "debian9" + +script: + # Download test shim. + - wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/nickjj/d12353b5b601e33cd62fda111359957a/raw + - chmod +x ${PWD}/tests/test.sh + + # Run tests. + - ${PWD}/tests/test.sh diff --git a/roles/nickjj.docker/CHANGELOG.md b/roles/nickjj.docker/CHANGELOG.md new file mode 100755 index 0000000..82c5595 --- /dev/null +++ b/roles/nickjj.docker/CHANGELOG.md @@ -0,0 +1,190 @@ +# Changelog + +## v1.8.0 + +*Released: December 19th 2018* + +- Change `docker__apt_key_server` to `docker__apt_key_url` +- `docker__version` and `docker__compose_version` are back to being empty strings by default + +## v1.7.0 + +*Released: December 18th 2018* + +- `docker__version` and `docker__compose_version` are now both undefined by default +- Change `docker__users` to default to `docker__users: ["{{ ansible_env.SUDO_USER | d('root') }}"]` +- Check for an existing Docker Compose file before trying to symlink it + +## v1.6.0 + +*Released: December 15th 2018* + +### Features + +- Docker and Docker Compose can now be installed to their latest versions by default +- A Virtualenv will be created for any PIP packages that get installed +- The `docker-compose` package is installed through PIP (complete with latest / version pinning) +- The `docker` package is installed through PIP (all of Ansible's `docker_*` modules now work) +- Symlinks are created to put `docker-compose` and `python-docker` on the system's path +- Better documentation to demonstrate how to downgrade / upgrade Docker versions +- Docker login's config path can now be configured +- Overall improved documentation and tests + +### Variables + +- Remove `python-pip` from `docker__package_dependencies` +- Remove `docker__install_docker_compose` +- Remove `docker__compose_download_url` +- Remove `docker__default_daemon_json_log_max_size` +- Remove `docker__default_daemon_json_log_max_file` +- Add `docker__state` to control the Docker APT package's state +- Add `docker__pip_dependencies` to install various APT dependencies to run PIP +- Add `docker__pip_virtualenv` to create a Virtualenv for PIP +- Add `docker__default_pip_packages` to install default PIP packages +- Add `docker__pip_packages` to install additional PIP packages +- Add `docker__pip_docker_compose_state` to control the Docker Compose PIP package's state +- Add `docker__pip_docker_state` to control the Docker PIP package's state +- Add `config_path` property to the `docker__registries` list +- Add `docker__cron_jobs_prune_flags` to configure which prune flags get set +- Change `docker__default_daemon_json` to log to journald by default +- Change `docker__channel` from being a string to a list + +## v1.5.0 + +*Released: November 11th 2018* + +- Rename `docker__install_docker__compose` to `docker__install_docker_compose` +- Bump Docker Compose version to 1.23 +- Change systemd options to use `-H unix://` to be compatible with 18.09 by default +- Install `python-pip` apt package as a dependency for pip installing `docker` +- Pip install `docker` so Ansible's `docker_login` and `docker_service` modules work +- Remove unnecessary "Remove Upstart config file" task +- Remove unnecessary "Install Python for managing Docker login credentials" task +- Remove unnecessary `enabled: true` in the systemd restart handler (it starts on boot by default) + +## v1.4.0 + +*Released: October 31st 2018* + +- Rename `docker__daemon_options` to `docker__daemon_json` +- Rename `docker__daemon_options_log_max_size` to `docker__default_daemon_json_log_max_size` +- Rename `docker__daemon_options_log_max_file` to `docker__default_daemon_json_log_max_file` +- Add `docker__daemon_flags` for setting systemd unit file Docker daemon options +- Add `docker__systemd_override` for setting custom systemd directives for the Docker service +- Rename `docker__cron_tasks` to `docker__cron_jobs` +- `cron_file` can now be configured with cron jobs to write out cron jobs in `/etc/cron.d` +- Add `user` to cron jobs since we're now using `cron_file` +- Drastically improve documentation + +## v1.3.0 + +*Released: October 21st 2018* + +- Variables are now using the `docker__*` style instead of `docker_*` +- Add configuration value for Docker Compose download URL +- Make style changes based on yamllint and ansible-lint + +## v1.2.0 + +*Released: October 11th 2018* + +- Remove ability to remove the `docker-*` package +- Add documentation on how to remove Docker if you need to downgrade versions +- Let Docker manage its own systemd unit file +- Allow environment configuration using the systemd `docker.service.d/*` pattern + +## v1.1.0 + +*Released: October 9th 2018* + +- Add `-H fd://` to the daemon options at the systemd unit file level +- Update systemd unit file as per Docker's latest settings +- Convert to using `/etc/docker/daemon.json` for setting daemon options +- Add variables to configure log size and max number of files +- Default to rotating logs after 10 gigs of space is used (was previously unlimited) +- System prune cron job now sets the `-a` flag to remove unused images + +## v1.0.0 + +*Released: September 19th 2018* + +- Update role to be compliant and depend on Ansible 2.5+ +- Add official support for Ubuntu 16.04 / 18.04 and Debian Jessie / Stretch +- Default to Docker v18.06 +- Default to Docker Compose v1.22 +- Default to the stable channel instead of edge +- Add support for configuring 1 or more registries (thanks to @Mykhailo Chalyi for starting this) +- Add ability to remove Docker by setting `docker__remove_package: True` +- Fix APT GPG key issues (thanks to @bidossessi for starting this) +- Add proper version pinning support +- Remove `docker__apt_package_name` because the package name has been simplified thanks to pinning +- Redirect system prune's cron output to `/dev/null` +- Extract Docker's package dependencies into `docker__package_dependencies` +- Add more tests + +## v0.2.3 + +*Released: April 13th 2018* + +- Default to Docker v18.04 +- Default to Docker Compose v1.21 +- Expose `docker__apt_package_name` to customize the APT package name + +## v0.2.2 + +*Released: March 28th 2018* + +- Default to Docker v18.03 +- Default to Docker Compose v1.20 + +## v0.2.1 + +*Released: February 14th 2018* + +- Default to Docker v18.02 +- Default to Docker Compose v1.19 + +## v0.2.0 + +*Released: January 25th 2018* + +- Change version strategy to be separate from Docker releases (it was a bad idea!) +- Change `docker__options` to `docker__daemon_options` +- Default to Docker v18.01 on the CE edge channel +- Fix systemd service so Docker loads after `network-online.target` instead of `network.target` +- Add cron job to clean up after Docker +- Add proper tests and support for Ubuntu 16, Debian Stretch and Debian Jessie +- Update format and style consistencies + +## v17.12 + +*Released: January 11th 2018* + +- Default to Docker v17.12 on the CE edge channel +- Default to Docker Compose v1.18 + +## v17.06 + +*Released: June 28th 2017* + +- Default to Docker v17.06 on the CE edge channel +- Default to Docker Compose v1.14 +- Update code base to support Docker's new version format + +## v0.1.2 + +*Released: October 9th 2016* + +- Fix apt.cache https could not be found error + +## v0.1.1 + +*Released: October 9th 2016* + +- Fix issue where `docker-engine` package was not found + +## v0.1.0 + +*Released: October 8th 2016* + +- Initial release diff --git a/roles/nickjj.docker/LICENSE b/roles/nickjj.docker/LICENSE new file mode 100755 index 0000000..7b168ac --- /dev/null +++ b/roles/nickjj.docker/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nick Janetakis nick.janetakis@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/nickjj.docker/README.md b/roles/nickjj.docker/README.md new file mode 100755 index 0000000..021c1f2 --- /dev/null +++ b/roles/nickjj.docker/README.md @@ -0,0 +1,397 @@ +## What is ansible-docker? [![Build Status](https://secure.travis-ci.org/nickjj/ansible-docker.png)](http://travis-ci.org/nickjj/ansible-docker) + +It is an [Ansible](http://www.ansible.com/home) role to: + +- Install Docker (editions, channels and version pinning are all supported) +- Install Docker Compose using PIP (version pinning is supported) +- Install the `docker` PIP package so Ansible's `docker_*` modules work +- Manage Docker registry login credentials +- Configure 1 or more users to run Docker without needing root access +- Configure the Docker daemon's options and environment variables +- Configure a cron job to run Docker clean up commands + +## Why would you want to use this role? + +If you're like me, you probably love Docker. This role provides everything you +need to get going with a production ready Docker host. + +By the way, if you don't know what Docker is, or are looking to become an expert +with it then check out +[Dive into Docker: The Complete Docker Course for Developers](https://diveintodocker.com/?utm_source=ansibledocker&utm_medium=github&utm_campaign=readmetop). + +## Supported platforms + +- Ubuntu 16.04 LTS (Xenial) +- Ubuntu 18.04 LTS (Bionic) +- Debian 9 (Stretch) + +--- + +*You are viewing the master branch's documentation which might be ahead of the +latest release. [Switch to the latest release](https://github.com/nickjj/ansible-docker/tree/v1.8.0).* + +--- + +## Quick start + +The philosophy for all of my roles is to make it easy to get going, but provide +a way to customize nearly everything. + +### What's configured by default? + +The latest Docker CE and Docker Compose will be installed, Docker disk clean up +will happen once a week and Docker container logs will be sent to `journald`. + +### Example playbook + +```yml +--- + +# site.yml + +- name: Example + hosts: "all" + become: true + + roles: + - role: "nickjj.docker" + tags: ["docker"] +``` + +Usage: `ansible-playbook site.yml -t docker` + +### Installation + +`$ ansible-galaxy install nickjj.docker` + +## Default role variables + +### Installing Docker + +#### Edition + +Do you want to use "ce" (community edition) or "ee" (enterprise edition)? + +```yml +docker__edition: "ce" +``` + +#### Channel + +Do you want to use the "stable", "edge", "testing" or "nightly" channels? You +can add more than one (order matters). + +```yml +docker__channel: ["stable"] +``` + +#### Version + +- When set to "", the current latest version of Docker will be installed +- When set to a specific version, that version of Docker will be installed and pinned + +```yml +docker__version: "" + +# For example, pin it to 18.06. +docker__version: "18.06" + +# For example, pin it to a more precise version of 18.06. +docker__version: "18.06.1" +``` + +*Pins are set with `*` at the end of the package version so you will end up +getting minor and security patches unless you pin an exact version.* + +##### Upgrade strategy + +- When set to `"present"`, running this role in the future won't install newer +versions (if available) +- When set to `"latest"`, running this role in the future will install newer +versions (if available) + +```yml +docker__state: "present" +``` + +##### Downgrade strategy + +The easiest way to downgrade would be to uninstall the Docker package manually +and then run this role afterwards while pinning whatever specific Docker version +you want. + +```sh +# An ad-hoc Ansible command to stop and remove the Docker CE package on all hosts. +ansible all -m systemd -a "name=docker-ce state=stopped" \ + -m apt -a "name=docker-ce autoremove=true purge=true state=absent" -b +``` + +### Installing Docker Compose + +Docker Compose will get PIP installed inside of a Virtualenv. This is covered +in detail in another section of this README file. + +#### Version + +- When set to "", the current latest version of Docker Compose will be installed +- When set to a specific version, that version of Docker Compose will be installed +and pinned + +```yml +docker__compose_version: "" + +# For example, pin it to 1.23. +docker__compose_version: "1.23" + +# For example, pin it to a more precise version of 1.23. +docker__compose_version: "1.23.2" +``` + +*Upgrade and downgrade strategies will be explained in the other section of this +README.* + +### Configuring users to run Docker without root + +A list of users to be added to the `docker` group. + +Keep in mind this user needs to already exist, this role will not create it. If +you want to create users, check out my +[user role](https://github.com/nickjj/ansible-user). + +This role does not configure User Namespaces or any other security features +by default. If the user you add here has SSH access to your server then you're +effectively giving them root access to the server since they can run Docker +without `sudo` and volume mount in any path on your file system. + +In a controlled environment this is safe, but like anything security related +it's worth knowing this up front. You can enable User Namespaces and any +other options with the `docker__daemon_json` variable which is explained later. + +```yml +# Try to use the sudo user by default, but fall back to root. +docker__users: ["{{ ansible_env.SUDO_USER | d('root') }}"] + +# For example, if the user you want to set is different than the sudo user. +docker__users: ["admin"] +``` + +### Configuring Docker registry logins + +Login to 1 or more Docker registries (such as the +[Docker Hub](https://hub.docker.com/)). + +```yml +docker__registries: + - #registry_url: "https://index.docker.io/v1/" + username: "your_docker_hub_username" + password: "your_docker_hub_password" + #email: "your_docker_hub@emailaddress.com" + #reauthorize: false + #config_path: "$HOME/.docker/config.json" + #state: "present" +docker__registries: [] +``` + +*Properties prefixed with \* are required.* + +- `registry_url` defaults to `https://index.docker.io/v1/` +- *`username` is your Docker registry username +- *`password` is your Docker registry password +- `email` defaults to not being used (not all registries use it) +- `reauthorize` defaults to `false`, when `true` it updates your credentials +- `config_path` defaults to `(ansible_env.PWD | d('/root')) + '/.docker/config.json'` +- `state` defaults to "present", when "absent" the login will be removed + +### Configuring the Docker daemon options (json) + +Default Docker daemon options as they would appear in `/etc/docker/daemon.json`. + +```yml +docker__default_daemon_json: | + "log-driver": "journald" + +# Add your own additional daemon options without overriding the default options. +# It follows the same format as the default options, and don't worry about +# starting it off with a comma. The template will add the comma if needed. +docker__daemon_json: "" +``` + +### Configure the Docker daemon options (flags) + +Flags that are set when starting the Docker daemon cannot be changed in the +`daemon.json` file. By default Docker sets `-H unix://` which means that option +cannot be changed with the json options. + +Add or change the starting Docker daemon flags by supplying them exactly how +they would appear on the command line. + +```yml +# Each command line flag should be its own item in the list. +# +# Using a Docker version prior to 18.09? +# You must set `-H fd://` instead of `-H unix://`. +docker__daemon_flags: + - "-H unix://" +``` + +*If you don't supply some type of `-H` flag here, Docker will fail to start.* + +### Configuring the Docker daemon environment variables + +```yml +docker__daemon_environment: [] + +# For example, here's how to set a couple of proxy environment variables. +docker__daemon_environment: + - "HTTP_PROXY=http://proxy.example.com:80" + - "HTTPS_PROXY=https://proxy.example.com:443" +``` + +### Configuring advanced systemd directives + +This role lets the Docker package manage its own systemd unit file and adjusts +things like the Docker daemon flags and environment variables by using +the systemd override pattern. + +If you know what you're doing, you can override or add to any of Docker's systemd +directives by setting this variable. Anything you place in this string will be +written to `/etc/systemd/system/docker.service.d/custom.conf` as is. + +```yml +docker__systemd_override: "" +``` + +### Configuring Docker related cron jobs + +By default this will safely clean up disk space used by Docker every Sunday at +midnight. + +```yml +# `a` removes unused images (useful in production). +# `f` forces it to happen without prompting you to agree. +docker__cron_jobs_prune_flags: "af" + +docker__cron_jobs: + - name: "Docker disk clean up" + job: "docker system prune -{{ docker__cron_jobs_prune_flags }} > /dev/null 2>&1" + schedule: ["0", "0", "*", "*", "0"] + cron_file: "docker-disk-clean-up" + #user: "{{ (docker__users | first) | d('root') }}" + #state: "present" +``` + +*Properties prefixed with \* are required.* + +- *`name` is the cron job's description +- *`job` is the command to run in the cron job +- *`schedule` is the [standard cron job](https://en.wikipedia.org/wiki/Cron#Overview) +format for every Sunday at midnight +- *`cron_file` writes a cron file to `/etc/cron.d` instead of a user's individual crontab +- `user` defaults to the first `docker__users` user or root if that's not available +- `state` defaults to "present", when "absent" the cron file will be removed + +### Configuring the APT package manager + +Docker requires a few dependencies to be installed for it to work. You shouldn't +have to edit any of these variables. + +```yml +# List of packages to be installed. +docker__package_dependencies: + - "apt-transport-https" + - "ca-certificates" + - "cron" + - "gnupg2" + - "software-properties-common" + +# The Docker PGP key id used to sign the Docker package. +docker__apt_key_id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88" + +# The Docker PGP key server address. +docker__apt_key_url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg" + +# The Docker upstream APT repository. +docker__apt_repository: > + deb [arch=amd64] + https://download.docker.com/linux/{{ ansible_distribution | lower }} + {{ ansible_distribution_release }} {{ docker__channel | join (' ') }} +``` + +### Installing Python packages with Virtualenv and PIP + +#### Configuring Virtualenv + +Rather than pollute your server's version of Python, all PIP packages are +installed into a Virtualenv of your choosing. + +```yml +docker__pip_virtualenv: "/usr/local/lib/docker/virtualenv" +``` + +#### Installing PIP and its dependencies + +This role installs PIP because Docker Compose is installed with the +`docker-compose` PIP package and Ansible's `docker_*` modules use the `docker` +PIP package. + +```yml +# This will attempt to install the correct version of PIP based on what your +# configured Ansible Python interpreter is set to (ie. Python 2 or 3). +docker__pip_dependencies: + - "python-setuptools" + - "python{{ '3' if ansible_python.version.major == 3 else '' }}-pip" +``` + +#### Installing PIP packages + +```yml +docker__default_pip_packages: + - name: "docker" + state: "{{ docker__pip_docker_state }}" + - name: "docker-compose" + version: "{{ docker__compose_version }}" + path: "/usr/local/bin/docker-compose" + src: "{{ docker__pip_virtualenv + '/bin/docker-compose' }}" + state: "{{ docker__pip_docker_compose_state }}" + +# Add your own PIP packages with the same properties as above. +docker__pip_packages: [] +``` + +*Properties prefixed with \* are required.* + +- *`name` is the package name +- `version` is the package version to be installed (or "" if this is not defined) +- `path` is the destination path of the symlink +- `src` is the source path to be symlinked +- `state` defaults to "present", other values can be "forcereinstall" or "absent" + +##### PIP package state + +- When set to `"present"`, the package will be installed but not updated on +future runs +- When set to `"forcereinstall"`, the package will always be (re)installed and +updated on future runs +- When set to `"absent"`, the package will be removed + +```yml +docker__pip_docker_state: "present" +docker__pip_docker_compose_state: "present" +``` + +#### Working with Ansible's `docker_*` modules + +This role uses `docker_login` to login to a Docker registry, but you may also +use the other `docker_*` modules in your own roles. They are not going to work +unless you instruct Ansible to use this role's Virtualenv. + +At either the inventory, playbook or task level you'll need to set +`ansible_python_interpreter: "/usr/bin/env python-docker"`. This works because +this role symlinks the Virtualenv's Python binary to `python-docker`. + +You can look at this role's `docker_login` task as an example on how to do it +at the task level. + +## License + +MIT diff --git a/roles/nickjj.docker/defaults/main.yml b/roles/nickjj.docker/defaults/main.yml new file mode 100755 index 0000000..6f68e39 --- /dev/null +++ b/roles/nickjj.docker/defaults/main.yml @@ -0,0 +1,65 @@ +--- + +docker__edition: "ce" +docker__channel: ["stable"] +docker__version: "" +docker__state: "present" + +docker__compose_version: "" + +docker__users: ["{{ ansible_env.SUDO_USER | d('root') }}"] + +docker__registries: [] + +docker__default_daemon_json: | + "log-driver": "journald" + +docker__daemon_json: "" + +docker__daemon_flags: + - "-H unix://" + +docker__daemon_environment: [] + +docker__systemd_override: "" + +docker__cron_jobs_prune_flags: "af" +docker__cron_jobs: + - name: "Docker disk clean up" + job: "docker system prune -{{ docker__cron_jobs_prune_flags }} > /dev/null 2>&1" + schedule: ["0", "0", "*", "*", "0"] + cron_file: "docker-disk-clean-up" + user: "{{ (docker__users | first) | d('root') }}" + +docker__package_dependencies: + - "apt-transport-https" + - "ca-certificates" + - "cron" + - "gnupg2" + - "software-properties-common" + +docker__apt_key_id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88" +docker__apt_key_url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg" +docker__apt_repository: > + deb [arch=amd64] + https://download.docker.com/linux/{{ ansible_distribution | lower }} + {{ ansible_distribution_release }} {{ docker__channel | join (' ') }} + +docker__pip_dependencies: + - "python-setuptools" + - "python{{ '3' if ansible_python.version.major == 3 else '' }}-pip" + +docker__pip_virtualenv: "/usr/local/lib/docker/virtualenv" + +docker__default_pip_packages: + - name: "docker" + state: "{{ docker__pip_docker_state }}" + - name: "docker-compose" + version: "{{ docker__compose_version }}" + path: "/usr/local/bin/docker-compose" + src: "{{ docker__pip_virtualenv + '/bin/docker-compose' }}" + state: "{{ docker__pip_docker_compose_state }}" +docker__pip_packages: [] + +docker__pip_docker_state: "present" +docker__pip_docker_compose_state: "present" diff --git a/roles/nickjj.docker/handlers/main.yml b/roles/nickjj.docker/handlers/main.yml new file mode 100755 index 0000000..8bc85ed --- /dev/null +++ b/roles/nickjj.docker/handlers/main.yml @@ -0,0 +1,6 @@ +--- + +- name: Restart Docker + systemd: + name: "docker" + state: "restarted" diff --git a/roles/nickjj.docker/meta/.galaxy_install_info b/roles/nickjj.docker/meta/.galaxy_install_info new file mode 100644 index 0000000..56f1c9c --- /dev/null +++ b/roles/nickjj.docker/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Fri Apr 12 21:50:33 2019', version: v1.8.0} diff --git a/roles/nickjj.docker/meta/main.yml b/roles/nickjj.docker/meta/main.yml new file mode 100755 index 0000000..e1778ae --- /dev/null +++ b/roles/nickjj.docker/meta/main.yml @@ -0,0 +1,26 @@ +--- + +galaxy_info: + role_name: "docker" + author: "Nick Janetakis" + description: "Install and configure Docker / Docker Compose." + license: "MIT" + min_ansible_version: 2.5 + + platforms: + - name: "Ubuntu" + versions: + - "xenial" + - "bionic" + - name: "Debian" + versions: + - "stretch" + + galaxy_tags: + - "containers" + - "compose" + - "docker" + - "packaging" + - "system" + +dependencies: [] diff --git a/roles/nickjj.docker/tasks/main.yml b/roles/nickjj.docker/tasks/main.yml new file mode 100755 index 0000000..2914c65 --- /dev/null +++ b/roles/nickjj.docker/tasks/main.yml @@ -0,0 +1,193 @@ +--- + +- name: Disable pinned Docker version + file: + dest: "/etc/apt/preferences.d/docker-ce.pref" + state: "absent" + when: not docker__version | d() + +- name: Enable pinned Docker version + template: + src: "etc/apt/preferences.d/docker-ce.pref.j2" + dest: "/etc/apt/preferences.d/docker-ce.pref" + owner: "root" + group: "root" + mode: "0644" + when: docker__version | d() + +- name: Install Docker's dependencies + apt: + name: "{{ docker__package_dependencies + docker__pip_dependencies }}" + +- name: Add Docker's public PGP key to the APT keyring + apt_key: + id: "{{ docker__apt_key_id }}" + url: "{{ docker__apt_key_url }}" + +- name: Configure Docker's upstream APT repository + apt_repository: + repo: "{{ docker__apt_repository }}" + update_cache: true + +- name: Install Docker + apt: + name: "docker-{{ docker__edition }}" + state: "{{ docker__state }}" + +- name: Check for existing Docker Compose file + stat: + path: "/usr/local/bin/docker-compose" + register: docker__register_docker_compose + +- name: Remove Docker Compose if it's been installed without PIP + file: + path: "/usr/local/bin/docker-compose" + state: "absent" + when: + - docker__register_docker_compose.stat.exists + - docker__register_docker_compose.stat.isreg | d(false) + +- name: Install Virtualenv + pip: + name: "virtualenv" + +- name: Install Python packages + pip: + name: > + {{ item.name }}{% if item.version | d() %}=={{ item.version }}{% endif %} + virtualenv: "{{ docker__pip_virtualenv }}" + state: "{{ item.state | d('present') }}" + loop: "{{ docker__default_pip_packages + docker__pip_packages }}" + when: item.name | d() + +- name: Symlink Python binary to /usr/local/bin/python-docker + file: + path: "/usr/local/bin/python-docker" + src: "{{ docker__pip_virtualenv }}/bin/python" + state: "link" + +- name: Symlink selected Python package binaries to /usr/local/bin + file: + path: "{{ item.path }}" + src: "{{ item.src }}" + state: "link" + loop: "{{ docker__default_pip_packages + docker__pip_packages }}" + when: + - item.state | d("present") != "absent" + - item.path | d() and item.src | d() + +- name: Add user(s) to "docker" group + user: + name: "{{ item }}" + groups: "docker" + append: true + loop: "{{ docker__users }}" + +- name: Create Docker configuration directories + file: + path: "{{ item }}" + state: "directory" + owner: "root" + group: "root" + mode: "0755" + loop: + - "/etc/docker" + - "/etc/systemd/system/docker.service.d" + +- name: Configure Docker daemon options (json) + template: + src: "etc/docker/daemon.json.j2" + dest: "/etc/docker/daemon.json" + owner: "root" + group: "root" + mode: "0644" + when: docker__default_daemon_json or docker__daemon_json + notify: ["Restart Docker"] + +- name: Configure Docker daemon options (flags) + template: + src: "etc/systemd/system/docker.service.d/options.conf.j2" + dest: "/etc/systemd/system/docker.service.d/options.conf" + owner: "root" + group: "root" + mode: "0644" + register: docker__register_daemon_flags + when: docker__daemon_flags + notify: ["Restart Docker"] + +- name: Configure Docker daemon environment variables + template: + src: "etc/systemd/system/docker.service.d/environment.conf.j2" + dest: "/etc/systemd/system/docker.service.d/environment.conf" + owner: "root" + group: "root" + mode: "0644" + register: docker__register_daemon_environment + when: docker__daemon_environment + notify: ["Restart Docker"] + +- name: Configure custom systemd unit file override + template: + src: "etc/systemd/system/docker.service.d/custom.conf.j2" + dest: "/etc/systemd/system/docker.service.d/custom.conf" + owner: "root" + group: "root" + mode: "0644" + register: docker__register_custom_override + when: docker__systemd_override | d() + notify: ["Restart Docker"] + +- name: Reload systemd daemon + systemd: + daemon_reload: true + when: (docker__register_daemon_flags | d() and + docker__register_daemon_flags is changed) + or (docker__register_daemon_environment | d() and + docker__register_daemon_environment is changed) + or (docker__register_custom_override | d() and + docker__register_custom_override is changed) + notify: ["Restart Docker"] + +- name: Restart Docker now to make sure `docker login` works + meta: "flush_handlers" + +- name: Manage Docker registry login credentials + docker_login: + registry_url: "{{ item.registry_url | d(omit) }}" + username: "{{ item.username }}" + password: "{{ item.password }}" + email: "{{ item.email | d(omit) }}" + reauthorize: "{{ item.reauthorize | d(omit) }}" + config_path: "{{ item.config_path | d((ansible_env.PWD | d('/root')) + '/.docker/config.json') }}" + state: "{{ item.state | d('present') }}" + loop: "{{ docker__registries }}" + when: item.username | d() and item.password | d() + vars: + ansible_python_interpreter: "{{ '/usr/bin/env python-docker' if docker__pip_virtualenv | d() else ansible_python.executable }}" + +- name: Remove Docker related cron jobs + file: + path: "/etc/cron.d/{{ item.cron_file }}" + state: "absent" + loop: "{{ docker__cron_jobs }}" + when: + - item.state | d("present") == "absent" + - item.name | d() and item.job | d() + - item.schedule | d() and item.cron_file | d() + +- name: Create Docker related cron jobs + cron: + name: "{{ item.name }}" + job: "{{ item.job }}" + minute: "{{ item.schedule[0] }}" + hour: "{{ item.schedule[1] }}" + day: "{{ item.schedule[2] }}" + month: "{{ item.schedule[3] }}" + weekday: "{{ item.schedule[4] }}" + cron_file: "{{ item.cron_file }}" + user: "{{ item.user | d('root') }}" + loop: "{{ docker__cron_jobs }}" + when: + - item.state | d("present") != "absent" + - item.name | d() and item.job | d() + - item.schedule | d() and item.cron_file | d() diff --git a/roles/nickjj.docker/templates/etc/apt/preferences.d/docker-ce.pref.j2 b/roles/nickjj.docker/templates/etc/apt/preferences.d/docker-ce.pref.j2 new file mode 100755 index 0000000..8485240 --- /dev/null +++ b/roles/nickjj.docker/templates/etc/apt/preferences.d/docker-ce.pref.j2 @@ -0,0 +1,4 @@ +Explanation: Pin added by Ansible role "{{ role_name }}" +Package: docker-ce +Pin: version {{ docker__version }}* +Pin-Priority: 600 diff --git a/roles/nickjj.docker/templates/etc/docker/daemon.json.j2 b/roles/nickjj.docker/templates/etc/docker/daemon.json.j2 new file mode 100755 index 0000000..fcfa70c --- /dev/null +++ b/roles/nickjj.docker/templates/etc/docker/daemon.json.j2 @@ -0,0 +1,11 @@ +{ +{%- if docker__default_daemon_json | d() -%} +{{ docker__default_daemon_json | indent(2, true) }} +{%- endif -%} +{%- if (docker__default_daemon_json | d()) and (docker__daemon_json | d()) -%} +, +{%- endif -%} +{%- if docker__daemon_json | d() -%} +{{ docker__daemon_json | indent(2, true) }} +{%- endif -%} +} diff --git a/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/custom.conf.j2 b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/custom.conf.j2 new file mode 100755 index 0000000..2ee35bd --- /dev/null +++ b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/custom.conf.j2 @@ -0,0 +1,3 @@ +# {{ ansible_managed }} + +{{ docker__systemd_override }} diff --git a/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/environment.conf.j2 b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/environment.conf.j2 new file mode 100755 index 0000000..245e0d0 --- /dev/null +++ b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/environment.conf.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} + +[Service] +Environment="{{ docker__daemon_environment | join('" "') }}" diff --git a/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/options.conf.j2 b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/options.conf.j2 new file mode 100755 index 0000000..f710170 --- /dev/null +++ b/roles/nickjj.docker/templates/etc/systemd/system/docker.service.d/options.conf.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +[Service] +ExecStart= +ExecStart=/usr/bin/dockerd {{ docker__daemon_flags | join(" ") }} diff --git a/roles/nickjj.docker/tests/.yamllint.yml b/roles/nickjj.docker/tests/.yamllint.yml new file mode 100755 index 0000000..7e370ff --- /dev/null +++ b/roles/nickjj.docker/tests/.yamllint.yml @@ -0,0 +1,5 @@ +--- + +extends: "default" +rules: + line-length: "disable" diff --git a/roles/nickjj.docker/tests/test.py b/roles/nickjj.docker/tests/test.py new file mode 100644 index 0000000..ec8dfb3 --- /dev/null +++ b/roles/nickjj.docker/tests/test.py @@ -0,0 +1,61 @@ +import re + + +def test_docker_version(host): + assert 0 == host.run("docker --version").rc + + +def test_pinned_docker_version(host): + existing_docker_version = host.check_output("docker --version") + host.run("sudo apt-get update") + host.run("sudo apt-get upgrade") + docker_version_after_apt_update = host.check_output("docker --version") + + assert existing_docker_version == docker_version_after_apt_update + + +def test_able_to_access_docker_without_root(host): + assert "docker" in host.user("test").groups + + +def test_daemon_json_is_configured(host): + daemon_json = host.file("/etc/docker/daemon.json") + + assert daemon_json.contains("journald") + assert daemon_json.contains("8.8.8.8") + + +def test_customized_environment_systemd_unit_file(host): + unit_file = "/etc/systemd/system/docker.service.d/environment.conf" + file_contents = host.file(unit_file).content_string + + assert re.search(r"HTTP_PROXY=.*HTTPS_PROXY=.*", file_contents) + + +def test_customized_daemon_flags_systemd_unit_file(host): + unit_file = "/etc/systemd/system/docker.service.d/options.conf" + file_contents = host.file(unit_file).content_string + + assert "-H fd://" in file_contents + assert "--debug" in file_contents + + +def test_customized_systemd_override(host): + unit_file = "/etc/systemd/system/docker.service.d/custom.conf" + file_contents = host.file(unit_file).content_string + + assert "ATest" in file_contents + + +def test_docker_compose_is_pip_installed_and_symlinked(host): + assert 0 == host.run("docker-compose --version").rc + + +def test_python_docker_is_symlinked(host): + assert 0 == host.run("python-docker --version").rc + + +def test_docker_clean_up_cron_job(host): + cron_conf = host.file("/etc/cron.d/docker-disk-clean-up").content_string + + assert "test docker system prune -af" in cron_conf diff --git a/roles/nickjj.docker/tests/test.yml b/roles/nickjj.docker/tests/test.yml new file mode 100755 index 0000000..f6c4c72 --- /dev/null +++ b/roles/nickjj.docker/tests/test.yml @@ -0,0 +1,41 @@ +--- + +- hosts: "all" + become: true + + vars: + docker__version: "18.06" + + docker__users: ["test"] + + docker__daemon_json: | + "dns": ["8.8.8.8"] + + docker__daemon_environment: + - "HTTP_PROXY=http://proxy.a.com:80" + - "HTTPS_PROXY=https://proxy.a.com:443" + + docker__daemon_flags: + - "-H fd://" + - "--debug" + + docker__systemd_override: | + [Unit] + Invalid=Directive + + [Service] + ThisIsJust=ATest + + roles: + - role: "role_under_test" + + pre_tasks: + - name: Add test user + user: + name: "test" + shell: "/bin/bash" + + - name: Run the equivalent of "apt-get update" + apt: + update_cache: true + changed_when: false diff --git a/terra.sh b/terra.sh new file mode 100755 index 0000000..1b79bb8 --- /dev/null +++ b/terra.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Generate SSH keys +ssh-keygen -f terraform/ssh/key -q -P "" + +# Run Terraform +cd terraform +terraform init +terraform apply diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..d51db88 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,41 @@ + +variable "hcloud_token" {} + +provider "hcloud" { + token = "${var.hcloud_token}" +} + +resource "hcloud_ssh_key" "main" { + name = "main_key" + public_key = "${file("./ssh/key.pub")}" +} + + +resource "hcloud_server" "game_node" { + name = "game-node" + image = "debian-9" + server_type = "cx11" + ssh_keys = ["${hcloud_ssh_key.main.id}"] + + provisioner "remote-exec" { + inline = [ + "uname -a", + ] + + connection { + host = "${self.ipv4_address}" + type = "ssh" + user = "root" + private_key = "${file("./ssh/key")}" + } + } + + provisioner "local-exec" { + environment { + PUBLIC_IP = "${self.ipv4_address}" + } + + working_dir = "." + command = "ansible-playbook -u root --private-key ./ssh/key ../playbook.yml -i ${self.ipv4_address}," + } +} diff --git a/terraform/ssh/key b/terraform/ssh/key new file mode 100644 index 0000000..501a57f --- /dev/null +++ b/terraform/ssh/key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAoIp7IVBt+ZsPnPOLmxN/NpDK1m2dugLtdYNgrbbdKYaCYwkfMZuq +SFikUF3sJJSMm7py4pXaFehCxjf+vGyZIxREeLikNRbDkncW8PiWsOpbWM2Ito+/af0jbx +jFndcwoQLnLxNRT3ZtVokKKpMFY921NLPINumDsmDrRKOFNneEFDCdeqiO2uWGinvJSMk8 +2ONqSm0Pim78V+kbJuGJ9dW9IzR+BDC13gCwW1bAzri8zK09G1RlQRyzL2cDQ2l/ToEdUa +LXgGLA0tGScWdjjCAkSldPAXlfWtnY0gaOp8EPJ2cf0ReR1UZEhHJBDxxp2RBD5+he135m +8sQ/w7qhQQAAA8Cw0a1CsNGtQgAAAAdzc2gtcnNhAAABAQCginshUG35mw+c84ubE382kM +rWbZ26Au11g2Cttt0phoJjCR8xm6pIWKRQXewklIybunLildoV6ELGN/68bJkjFER4uKQ1 +FsOSdxbw+Jaw6ltYzYi2j79p/SNvGMWd1zChAucvE1FPdm1WiQoqkwVj3bU0s8g26YOyYO +tEo4U2d4QUMJ16qI7a5YaKe8lIyTzY42pKbQ+KbvxX6Rsm4Yn11b0jNH4EMLXeALBbVsDO +uLzMrT0bVGVBHLMvZwNDaX9OgR1RoteAYsDS0ZJxZ2OMICRKV08BeV9a2djSBo6nwQ8nZx +/RF5HVRkSEckEPHGnZEEPn6F7XfmbyxD/DuqFBAAAAAwEAAQAAAQEAgXAl5q5afS2wYPNc +VCRcJTqI0GqBCUDjBCRxBp6xopF4/zCTngPMP5h4ovOXOccrt0OU7Wu08oTZkcgD8xf8QH +RO4Ka+fq9j7+tk69UCapIKJIdS+x7bTjFzKFfovWKLKsaUbzg8Dr2FWogTUlBHwEU5nMx3 +WQ7dyCyshCLzfP2I7HX/Mm2waAOQ6xaavaelfigXoOZxJ/Vm4wqerj3Sv5FDmeyNaClAfY +xBW8HRunAyS+dYEEcvk4P0PtSbgD6nV2e0I78OxXyQgBqNfta0I7aYIRjc7aJYbF+qQCIW +2cYZMjX/juTzESFY0z1dV6FyCiV2IS9URpINfOoaTYlFoQAAAIEAo+2rDZMwOsPs9N7yN4 +g0bS5htLTXIvpPFOO09LxXtQfZ4UxZqYD440Rww2L6xvcqXrMAEla9qbcAxLB27AcdMuIL +bJ0yMpoy3RHxGcnmoPmhrUX3Dl5uR4X1eReHxMwVoxuZAv9uloVfweqUYT3gEnhkBPDWtF +Tu+lwjzm6mlOwAAACBAMyRh3SsiB1e9of97PqcSW4TYMCvCa8vScN/6HT/0IfErbmawOQ8 +dlqjD+t+V9PKDgmXX/KDvkkzPxA82ysItWk0JdoYHU2EKlrZ3YK1HDpvvj2EOt1h24QtuR +s9goGdRXYiKJ3Gn5QM0LPF/ErVQq9eNOaXErmgWRPu/H0vh/4PAAAAgQDI5z6ORd3rgc49 +Jow4lIjwgIgtTbm4bFKJTeYNZq+kcqOFl1WG/4v1kozurwNHa9UoC5b9qIcIgbyTn9Taua +CN1mDxY4U3fC9Mwb8XF8g4SuEghorWcRI3+Gp3Re5ow33ylwTTpIMRGBYr1VZLFTrFjCVG +kjGXxsETOSccHOG7rwAAAAhhM3hAaWR1bgE= +-----END OPENSSH PRIVATE KEY----- diff --git a/terraform/ssh/key.pub b/terraform/ssh/key.pub new file mode 100644 index 0000000..27543ae --- /dev/null +++ b/terraform/ssh/key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCginshUG35mw+c84ubE382kMrWbZ26Au11g2Cttt0phoJjCR8xm6pIWKRQXewklIybunLildoV6ELGN/68bJkjFER4uKQ1FsOSdxbw+Jaw6ltYzYi2j79p/SNvGMWd1zChAucvE1FPdm1WiQoqkwVj3bU0s8g26YOyYOtEo4U2d4QUMJ16qI7a5YaKe8lIyTzY42pKbQ+KbvxX6Rsm4Yn11b0jNH4EMLXeALBbVsDOuLzMrT0bVGVBHLMvZwNDaX9OgR1RoteAYsDS0ZJxZ2OMICRKV08BeV9a2djSBo6nwQ8nZx/RF5HVRkSEckEPHGnZEEPn6F7XfmbyxD/DuqFB a3x@idun diff --git a/terraform/vars.auto.tfvars b/terraform/vars.auto.tfvars new file mode 100644 index 0000000..3d06ca0 --- /dev/null +++ b/terraform/vars.auto.tfvars @@ -0,0 +1,2 @@ +# Enter Hetzner Cloud Token +hcloud_token = ""