Code Monkey home page Code Monkey logo

tads-boilerplate's Introduction

T.A.D.S. boilerplate

Build Status License: MIT Tweet

The power of Ansible and Terraform + the simplicity of Swarm = DevOps on ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ

T.A.D.S. logo

๐ŸŽ‰ What is it?

A boilerplate to create a full Infrastructure as Code (IaC) repository, from provisioning to deployment with:

  • Terraform to create your cloud infrastructure
  • Vagrant to reproduce a production-like environment locally
  • Ansible to provision Virtual Machines and set up the Docker Swarm cluster
  • Ansible again to deploy your stacks

It handles different environments:

  • localhost: a single node Docker Swarm cluster on your machine, useful for development (demo)
  • vagrant: a 3 nodes production-like cluster deployed with Vagrant on your machine, useful for testing (demo)
  • production: your production environment! It can be created by Terraform or you can use an existing bare metal/VMs infrastructure (demo)
  • other remote production-like environments of your choice: staging, QA...

On top of that, it features:

  • A companion CLI (./tads), which is a wrapper around Terraform, Ansible and Vagrant commands. For example: ansible-playbook -i inventories/production -D --vault-id production@vault_keys/production deploy.yml becomes ./tads ansible-playbook production deploy. More convenient, don't you think? ๐Ÿ˜
  • Docker Swarm Compose files templated with Jinja2, so you can define your services once, while being able to customize them in each environment, from the same file
  • An example which implements dockerswarm.rocks' recommended good practices: traefik reverse proxy with HTTPS (even locally), and more coming soon
  • A smart /etc/hosts management to access your local and Vagrant applications with .localhost and .test https URIs
  • AES-256 encryption of your production credentials with ansible-vault

With T.A.D.S., you will be able to onboard a new developer on your project in less than 3 minutes, with just 3 commands! Even if you have a complex microservices architecture. Forget about your outdated wikis or installation procedures, they are no longer needed! See the example user README to get a preview of what your new procedures could look like.

Example of a fresh development environment setup with T.A.D.S. in 02:30!

Example of a fresh development environment setup with T.A.D.S. in 02:30!

๐ŸŽฏ Who is it for?

If you recognize yourself into some of these statements, this project is definitely for you:

  • I am the only one who understands how the production environment works
  • I still have to execute SSH commands in production and this makes me sad because I cannot rollback or be reviewed :(
  • Setting up a new development environment for a new team member takes an entire day, and a lot of resources
  • My team suffers from "Microservices Hell": we have to install many services before being able to dev
  • Developers use docker-compose on their machine, while we use something else in production
  • I want to do Infrastructure as Code (IaC)
  • I want to promote the DevOps mindset in my team
  • I don't need/want Kubernetes features and complexity
  • I don't want to be vendor locked by a service like AWS ECS
  • I start a new project and I want to bootstrap it quickly with good practices presets

On the contrary, this project might not be for you if:

  • You have a large cluster (more than 100 machines)
  • You need Kubernetes features like autoscaling

... but don't be sad, I am thinking of creating a similar project for K8s ;) Tell me if you want to help!

๐Ÿ’ช Philosophy

  • Every environment infrastructure, including dev, is versioned into one repository
  • Same development environment installation procedure for everyone
  • No SSH, no manual actions, everything must be code
  • Every change to infrastructure must be code reviewed to:
    • Avoid mistakes
    • Make other (including non-DevOps) team members able to learn
  • Everyone, not only DevOps team members, is able to:
    • Create their development environment in a minute with just one command
    • Reproduce a production-like environment locally
    • Understand the whole infrastructure
    • Propose modifications to the infrastructure, while being able to test them locally
  • This project is a boilerplate, not a framework: modify it to fulfill your needs!
  • The companion CLI is written in Bash so it is easy to understand what a command does, and it is easy to modify command behaviors or to add new ones

๐Ÿ”’ Knowledge prerequisites

Before going further, I assume that you already have the knowledge and practice with Docker Swarm mode, Ansible, Terraform, and Infrastructure as Code in general. If it is not the case, I urge you to study and practice before. You can use this material as a starter:

๐Ÿ”’ Technical prerequisites

  • Local machine:
    • Ubuntu >= 18.04 or similar (PRs are welcome to update this list)
    • Ansible >= 2.8
    • Vagrant >= 2.0 (optional)
    • Virtualbox >= 5.2 (optional)
    • Terraform >= 0.12 (optional)
  • Remote environments:
    • A cloud provider account (tested on AWS and Digital Ocean so far)
    • OR
    • An existing bare metal / VMs infrastructure, with Linux based OSes (tested on Ubuntu server 18.04 and Debian 9 so far)

Have a look at Install the required dependencies for installation procedures.

OS X: It should not be that hard to make the project run on OS X. PRs are welcome! I am also thinking of creating a dockerized version of the project to improve compatibility.

๐Ÿš€ Quick start

1. Make this repository yours

Clone this repo, create your own and push the code to it.

git clone --single-branch https://github.com/Thomvaill/tads-boilerplate.git <YOUR_PROJECT_NAME>
cd <YOUR_PROJECT_NAME>
git remote set-url origin <YOUR_REPO_URL>
git push

2. Install the required dependencies

This will install Ansible, Vagrant, Virtualbox and Terraform on your local machine:

./tads install-dependencies

You can also manually install the dependencies if your preferer.

3. Provision your local machine and deploy the example stack

  1. Copy ansible/group_vars/localhost_overrides.sample.yml to ansible/group_vars/localhost_overrides.yml
  2. Add ansible_user variable with your user name
ansible_user: <yourUserName>
  1. Provision and Deploy
./tads ansible-playbook localhost provision
./tads ansible-playbook localhost deploy

The first ./tads command will:

  • Install Docker on your local machine
  • Set up a Swarm cluster with one manager node
  • Hardcode yourcompany.localhost to your /etc/hosts file

And the second one will deploy traefik and example_app stacks. If everything went well, you are now able to access it at this URL: https://yourcompany.localhost/

4. Write your own Docker Swarm Compose files

Now that the example stack is running on your machine, you can deploy your own services.

First, you probably need to change the domains dict in ansible/group_vars/all.yml. This file contains all Ansible variables default values. These values can be overridden later in other group_vars files. You are free to add your variables in it.

Then, you can write your own Docker Swarm Compose files, following this naming convention: ansible/stacks/<STACK_NAME>/<STACK_NAME>.yml.j2 These files are Jinja2 templates. You are highly encouraged to use Ansible variables in them, so your template file can be used across all your environments. Have a look at ansible/stacks/example_app/example_app.yml.j2 to see a good example.

Finally, do not forget to add your new stacks to ansible/deploy.yml.

To help you with the ansible/group_vars directory, here is a representation of Ansible groups:

all
โ”œโ”€โ”€ dev
|   โ”œโ”€โ”€ localhost
|   โ””โ”€โ”€ vagrant
โ”œโ”€โ”€ production
โ”œโ”€โ”€ staging
โ””โ”€โ”€ any_other_remote_environment...

Each group has its _overrides counterpart, which enables you to override some variables locally in a xxx_overrides.yml file, which is not versionned. Have a look at .sample.yml files to see some examples.

While developing, perform some ./tads ansible-playbook localhost deploy to test your deployment. Do not forget to run ./tads ansible-playbook localhost provision again if you have changed domain names.

Tips:

  • You can use ./tads ansible-playbook localhost all to provision and deploy in a single command
  • You can use tags to go quicker. Example: ./tads ansible-playbook localhost all --tags dev,stack-traefik
  • Always reference your Docker images with tags! It is a bad practice to rely on the :latest tag because you don't control what will be pushed to production. With specific tags, you will have idempotent deployments and you will be able to perform rollbacks

5. Test on a Vagrant cluster

Now that you are happy with your localhost environment, you should test the provisioning and the deployment on an environment which looks more like a production environment. For instance, on localhost, you can have just one node! And maybe you forgot some dependencies that are already installed on your computer. With Vagrant, you will be able to test your stacks on a fresh 3 nodes Swarm cluster.

  1. Copy vagrant/vagrant.sample.yml to vagrant/vagrant.yml and adjust its settings
  2. Run ./tads vagrant up
  3. Run ./tads ansible-playbook vagrant all

Now, you will be able to test your stacks deployed on Vagrant. If you have kept the example app, you can test it on https://yourcompany.test/

Tips:

  • To destroy your cluster: ./tads vagrant destroy
  • To SSH into the first node: ./tads vagrant ssh vagrant-1

6. Edit and encrypt your production environment variables

Before going further, you should edit your production group_vars files:

  • ansible/group_vars/production.yml
  • ansible/group_vars/production_encrypted.yml

When you are done, do not commit production_encrypted.yml! You have to encrypt it first:

  • ./tads ansible-vault production init-key
  • ./tads ansible-vault production encrypt ansible/group_vars/production_encrypted.yml

The first command has generated a random key in ansible/vault_keys/production. You must not commit this file. You should keep it in a safe place, and share it with your authorized team members securely. If you lose it, you won't be able to decrypt your files anymore. The second one has encrypted your file with AES-256. You can now commit it.

You can still edit this file by running ./tads ansible-vault production edit ansible/group_vars/production_encrypted.yml. Always check that you do not commit an unencrypted version of this file by mistake.

7.a. Create, provision and deploy your production environment with Terraform

Now that everything is fine locally, it is time to create and deploy your production environment!

The terraform/environments/production is an AWS example. PRs are welcome for other providers! To make it work, you should:

  • Have a working SSH key pair
  • Have a registered domain name managed by Route53
  • Install AWS CLI: pip3 install awscli --upgrade --user
  • Configure your credentials: aws configure

Terraform will use this default profile credentials.

Then, you can run ./tads terraform production init and ./tads terraform production apply. This example will create:

  • A custom VPC
  • 3 subnets into separate Availability Zones, for high availability
  • 3 manager nodes and 1 worker node (with spread placement groups, for high availability)
  • 1 classic ELB to distribute TCP traffic on port 80 and 443 to the manager nodes (traefik is responsible for SSL termination)

The CLI will also create the corresponding Ansible inventory for you in ansible/inventories/production from Terraform outputs. You should commit it. You should also commit the Terraform state file, or better: use a remote state.

Then, you have to create an alias in Route53 to the ELB.

Finally, you can run ./tads ansible-playbook production all and your website will be available!

Disclaimer:

  • This is an example, you should not use it as is in production!
  • Although resources created by the example are eligible to free tier, charges may occur depending on your situation
  • Use ./tads terraform production destroy with caution :-)

7.b. Provision and deploy your production environment to an existing infrastructure

If you don't want to use a cloud provider, you can use classic Virtual Machines. For a production environment, you should have at least 3 manager nodes, so 3 VMs. They should be fresh installs. Ubuntu server 18.04 or Debian 9 is fine.

  1. Make sure you can SSH into the VMs with your key pair
  2. Copy ansible/inventories/production.sample-baremetal to ansible/inventories/production
  3. Edit it
  4. Run ./tads ansible-playbook production all and your website will be available!

8. Add other remote environments

You can add other remote environments, like production.

For Terraform, you just have to duplicate terraform/environments/production to the directory of your choice, eg staging. After editing it, you can run ./tads terraform staging apply, it will create the ansible/inventories/staging inventory file.

For an existing bare metal infrastructure, you just have to create the ansible/inventories/staging inventory file.

Then, in Ansible, you have to create these files:

  • ansible/group_vars/staging_encrypted.yml
  • ansible/group_vars/staging.yml

Then, create the ansible-vault key and encrypt the file:

  • ./tads ansible-vault staging init-key
  • ./tads ansible-vault staging encrypt ansible/group_vars/staging_encrypted.yml

Finally, provision and deploy! ./tads ansible-playbook staging all

9. Make your team members autonomous

It is one of this project's goals: DevOps is not a job, it is a mindset! Now that you have a beautiful IaC, it is time to onboard your team members.

  1. Replace this README.md by README.example.md and customize it, so your team can use the project easily
  2. Make your developers use this project to configure their machine and develop
  3. Let your developers update/create stacks on their own, show them how to test their changes locally
  4. Enjoy :-)

โ“ FAQ

Where is the companion CLI documentation?

There is no documentation of the CLI since you will probably modify it, or add new commands! To get some help, just run ./tads. Do not hesitate also to have a look at the source into the scripts directory. This CLI is just a wrapper of Terraform, Ansible and Vagrant commands.

What if I don't want to deploy all the stacks locally?

Use Ansible tags! Example if you just want to deploy the traefik and myapp stack: ./tads ansible-playbook localhost deploy --tags stack-traefik,stack-myapp.

How to do Continuous Delivery / Deployment?

I have not taken the time to develop this feature properly yet. But basically what you can do is:

  • Always referer to your Docker images with tags
  • Manage those tags into a separate tags.yml file
  • Write a script that can update a tag, perform a ./tads ansible-playbook production deploy, and in case of success commit and push the file
  • Make your CI/CD tool use this script in your deployment pipeline

How to manage persistant storage?

This problematic is beyond the scope of this project and depends a lot on your infrastructure / cloud provider. I advise you to have a look at REX-Ray.

This might be a future feature to implement this plugin in the boilerplate.

Contributing

Pull Requests are more than welcome! Please read CONTRIBUTING.md for more details.

Development:

./tads install-dependencies --dev
make lint
make test

Acknowledgments

License

This project is licensed under the MIT license, Copyright (c) 2019 Thomas Vaillant. For more information see LICENSE file.

tads-boilerplate's People

Contributors

thomvaill avatar toby-griffiths avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tads-boilerplate's Issues

Add Continuous Deployment support - better CI/CD integration

As mentioned in the README, I did not have time to develop this feature properly yet.

I used to do CD with a T.A.D.S. similar repository by (roughly):

  • Always referer to Docker images with tags
  • Manage those tags into a separate tags.yml file
  • Write a script that can update a tag, perform a ./tads ansible-playbook production deploy, and in case of success commit and push the file
  • Make the CI/CD tool use this script in the deployment pipeline

We may also consider the option of using Portainer's API to achieve CD.
But I'm not really in favor of this because the main advantage of commiting the newly deployed tags is that all developers can stay in sync with the prod.

geerlinguy ansible role versions

Describe the bug
setup fails on ./tads ansible-playbook localhost provision

reports that pip is not found - apperas to be an error with the gerrlingguy.pip role. Solved this issue by updating the
requirements.yaml to the following:

###
# ansible-galaxy requirements
##
# Automatically installed before each T.A.D.S. Ansible command
##

- src: geerlingguy.pip
  version: 2.1.0

- src: geerlingguy.docker
  version: 3.1.2

To Reproduce
Follow the exact steps in the README.

Expected behavior
Expected deployment to work as shown in demo video.

Environment (please complete the following information):

  • OS/distro: Ubuntu 20.04 desktop
  • Ansible version: 2.9.6
  • Terraform version: v0.15.5
  • Cloud provider: n/a
  • Vagrant version: 2.2.6

Additional context
note the default version of terraform installed by the script is now outdated.

Integrate REX-Ray to manage persistent storage easily

Describe the solution you'd like
Persistent storage is a hassle with Docker Swarm, and is not covered at all in this project for now.
I propose to integrate REX-Ray installation and configuration to the playbooks.
It supports all famous cloud providers and Vagrant!
So we would be able to test it into the vagrant environment.
On localhost we would use classic volumes.

Describe alternatives you've considered

... but those plugins are not open sourced and are cloud provider specific.

Additional context
Developing this feature would enable us to add more examples / pre-setupped services:

  • portainer
  • swarmprom
  • ELK

Possible caveats:

Add OS X support

Make the project work on OS X.

Since I don't have a Mac, help is welcome :-)

For this issue, we have to consider to potentially dockerize the CLI + Ansible + Terraform.
However I don't think dockerizing Vagrant is possible.
We have to think about the best option to do this.

subdomains do not obtain letsencrypt certificate

Describe the bug
I cant get letsencrypt certificates for subdomains.

To Reproduce
I have the A records of the subdomains pointing to the Server.
In the stack yaml file i added the known subdomains:

traefik.frontend.rule=Host:{{ domains.main }},{{ domains.main_www }},{{ domains.eco_ivobathke }},{{ domains.oxid_ivobathke }}

The letsencrypt block in traefik.yml.j2 is:

{% if letsencrypt %} # you can also use conditional statements
      - --acme
      - --acme.acmeLogging
      - --acme.storage=traefik/acme/account
      - --acme.entryPoint=https
      - --acme.email=admin@{{ domains.main }}
      - --acme.httpchallenge.entrypoint=http
      - --acme.domains={{ domains.main }},{{ domains.main_www }}
{% endif %}

I am reading about defining main & sans domain in the traefik.toml file.
https://doc.traefik.io/traefik/v1.7/configuration/acme/#domains
But i dont see how to use this here, maybe to switch to toml config instead of commands with the traefik image?

Checking the logs i see:

Datastore sync error: object lock value: expected ...

Seems related to: traefik/traefik#3487

Expected behavior
Traefik should automatically request letsencrypt certificates for known subdomains.

Example Proviosion playbook failed

Describe the bug
Im up to this command from your example
> ./tads ansible-playbook localhost provision

Tasks are executing fine up to this one:

PLAY [docker:&dockerswarm_manager] *************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************ok: [127.0.0.1]

TASK [load docker info as facts] ***************************************************************************************************************************************An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: Value of unknown type: <class 'urllib3.exceptions.ProtocolError'>, ('Connection aborted.', error(2, 'No such file or directory'))
fatal: [127.0.0.1]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1575456373.86-208554238811029/AnsiballZ_docker_info_facts\", line 102, in <module>\n
 _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1575456373.86-208554238811029/AnsiballZ_docker_info_facts\", line 94, in _ansiballz_main\n
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n
File \"/root/.ansible/tmp/ansible-tmp-1575456373.86-208554238811029/AnsiballZ_docker_info_facts\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible.modules.docker_info_facts', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python2.7/runpy.py\", line 188, in run_module\n    fname, loader, pkg_name)\n  File \"/usr/lib/python2.7/runpy.py\", line 82, in _run_module_code\n    mod_name, mod_fname, mod_loader, pkg_name)\n
File \"/usr/lib/python2.7/runpy.py\", line 72, in _run_code\n    exec code in run_globals\n  File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/modules/docker_info_facts.py\", line 78, in <module>\n
File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/modules/docker_info_facts.py\", line 69, in main\n  File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/module_utils/basic.py\", line 2093, in fail_json\n  File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/module_utils/basic.py\", line 2065, in _return_formatted\n  File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/module_utils/basic.py\", line 418, in remove_values\n
File \"/tmp/ansible_docker_info_facts_payload_5ZDtsE/ansible_docker_info_facts_payload.zip/ansible/module_utils/basic.py\", line 401, in _remove_values_conditions\nTypeError: Value of unknown type: <class 'urllib3.exceptions.ProtocolError'>, ('Connection aborted.', error(2, 'No such file or directory'))\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

Any Idea how where to look at?

To Reproduce
Steps to reproduce the behavior:
> ./tads ansible-playbook localhost provision

Expected behavior
A clear and concise description of what you expected to happen.

Environment (please complete the following information):

  • OS/distro: Latest Ubuntu docker image, on windows Docker Desktop

Additional context
Add any other context about the problem here.

Dependency Installation Generates an Error During Terraform Install

Describe the bug
Running the command ./tads install-dependencies generates the following error:

TASK [Install Terraform (linux)] ***************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (ansible.legacy.command) module: warn. Supported parameters include: _raw_params, _uses_shell, argv, chdir, creates, executable, removes, stdin, stdin_add_newline, strip_empty_ends."}

To Reproduce
Steps to reproduce the behavior:

  1. Clone the project from master
  2. Install dependencies

Expected behavior
Dependencies are installed without error.

Environment (please complete the following information):

  • OS/distro: Ubuntu 22.04.2 LTS
  • Ansible version: 2.14.4
  • Terraform version: N/A
  • Cloud provider: N/A
  • Vagrant version: N/A

Additional context
Appears to be related to this StackOverflow question.

Following changes fixes the issue:

diff --git a/ansible/install-dependencies.yml b/ansible/install-dependencies.yml
index 619743d..6b828a5 100644
--- a/ansible/install-dependencies.yml
+++ b/ansible/install-dependencies.yml
@@ -47,7 +47,7 @@
         TERRAFORM_VERSION: 0.12.12
       args:
         creates: ~/.local/bin/terraform
-        warn: False
+          # warn: False
       when: ansible_facts['os_family'] != "Darwin"

     - name: Install Terraform Homebrew Tap (Mac)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.