Setting up Ansible AWX using a docker environment - Part 2 (the docker-compose approach)

In a previous post I wrote about setting up Ansible AWX using a docker environment.

This used Ansible to read in an inventory file, dynamically generate scripts which stored environment variables as well as an overall docker-compose file.

In this post I shall include further details on the files generated using the Ansible approach of setting up AWX.

Then use docker-compose to create the AWX environment.

Files generated using Ansible

When running Ansible to setup AWX. The following files are generated.

  1. credentials.py
  2. docker-compose.yml
  3. environment.sh
  4. nginx.conf
  5. SECRET_KEY

 Note

As these hold environment specific details you will need to modify these files first to match your environment setup.

These files were generated using the stock inventory file - https://github.com/ansible/awx/blob/devel/installer/inventory

The inventory file tells Ansible where to store the following files. This is identified by the following line.

A user’s home directory under a hidden directory called .awx

docker_compose_dir="~/.awx/awxcompose"

credentials.py

    DATABASES = {
        'default': {
            'ATOMIC_REQUESTS': True,
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': "awx",
            'USER': "awx",
            'PASSWORD': "awxpass",
            'HOST': "postgres",
            'PORT': "5432",
        }
    }
    
    BROKER_URL = 'amqp://{}:{}@{}:{}/{}'.format(
        "guest",
        "awxpass",
        "rabbitmq",
        "5672",
        "awx")
    
    CHANNEL_LAYERS = {
        'default': {'BACKEND': 'asgi_amqp.AMQPChannelLayer',
                    'ROUTING': 'awx.main.routing.channel_routing',
                    'CONFIG': {'url': BROKER_URL}}
    }
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '{}:{}'.format("memcached", "11211")
        },
        'ephemeral': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        },
    }
    

environment.sh

    DATABASE_USER=awx
    DATABASE_NAME=awx
    DATABASE_HOST=postgres
    DATABASE_PORT=5432
    DATABASE_PASSWORD=awxpass
    MEMCACHED_HOST=memcached
    MEMCACHED_PORT=11211
    RABBITMQ_HOST=rabbitmq
    RABBITMQ_PORT=5672
    AWX_ADMIN_USER=admin
    AWX_ADMIN_PASSWORD=password

SECRET_KEY

awxsecret

 Note

It’s very important that this stay the same between upgrades or you will lose the ability to decrypt your credentials

docker-compose.yml

In the example below some of the volumes require changing.

  • /volume1/docker-data = should match the path to where you would want to host static AWX projects (rather than remotely via a Git repository) * ~/.awx/awx-compose = should match the location where you have all your files
    version: '2'
    services:
    
      web:
        image: ansible/awx_web:9.2.0
        container_name: awx_web
        depends_on:
          - rabbitmq
          - memcached
          - postgres
        ports:
          - "80:8052"
        hostname: awxweb
        user: root
        restart: unless-stopped
        volumes:
          - "~/.awx/awxcompose/SECRET_KEY:/etc/tower/SECRET_KEY"
          - "~/.awx/awxcompose/environment.sh:/etc/tower/conf.d/environment.sh"
          - "~/.awx/awxcompose/credentials.py:/etc/tower/conf.d/credentials.py"
          - "~/.awx/awxcompose/nginx.conf:/etc/nginx/nginx.conf:ro"
          - "/volume1/docker-data/awx/projects:/var/lib/awx/projects:rw"
          - "/etc/localtime:/etc/localtime:ro"
        environment:
          http_proxy: 
          https_proxy: 
          no_proxy:
    
      task:
        image: ansible/awx_task:9.2.0
        container_name: awx_task
        depends_on:
          - rabbitmq
          - memcached
          - web
          - postgres
        hostname: awx
        user: root
        restart: unless-stopped
        volumes:
          - "~/.awx/awxcompose/SECRET_KEY:/etc/tower/SECRET_KEY"
          - "~/.awx/awxcompose/environment.sh:/etc/tower/conf.d/environment.sh"
          - "~/.awx/awxcompose/credentials.py:/etc/tower/conf.d/credentials.py"
          - "/volume1/docker-data/awx/projects:/var/lib/awx/projects:rw"
          - "/etc/localtime:/etc/localtime:ro"
        environment:
          http_proxy: 
          https_proxy: 
          no_proxy: 
    
      rabbitmq:
        image: ansible/awx_rabbitmq:3.7.4
        container_name: awx_rabbitmq
        restart: unless-stopped
        environment:
          RABBITMQ_DEFAULT_VHOST: "awx"
          RABBITMQ_DEFAULT_USER: "guest"
          RABBITMQ_DEFAULT_PASS: "awxpass"
          RABBITMQ_ERLANG_COOKIE: cookiemonster
          http_proxy: 
          https_proxy: 
          no_proxy: 
    
      memcached:
        image: "memcached:alpine"
        container_name: awx_memcached
        restart: unless-stopped
        environment:
          http_proxy: 
          https_proxy: 
          no_proxy: 
    
      postgres:
        image: postgres:10
        container_name: awx_postgres
        restart: unless-stopped
        volumes:
          - /volume1/docker-data/awx/postgres/10/data/:/var/lib/postgresql/data/pgdata:Z
        environment:
          POSTGRES_USER: awx
          POSTGRES_PASSWORD: awxpass
          POSTGRES_DB: awx
          PGDATA: /var/lib/postgresql/data/pgdata
          http_proxy: 
          https_proxy: 
          no_proxy:

nginx.conf

If you were using nginx then the awx_web docker container makes use of this conf file.

    #user awx;
    
    worker_processes  1;
    
    pid        /tmp/nginx.pid;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
        server_tokens off;
    
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log /dev/stdout main;
    
        map $http_upgrade $connection_upgrade {
            default upgrade;
            ''      close;
        }
    
        sendfile        on;
        #tcp_nopush     on;
        #gzip  on;
    
        upstream uwsgi {
            server 127.0.0.1:8050;
            }
    
        upstream daphne {
            server 127.0.0.1:8051;
        }
    
        
        server {
                    listen 8052 default_server;
            
            # If you have a domain name, this is where to add it
            server_name _;
            keepalive_timeout 65;
    
            # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
            add_header Strict-Transport-Security max-age=15768000;
            add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
            add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
    
            # Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009)
            add_header X-Frame-Options "DENY";
    
            location /nginx_status {
              stub_status on;
              access_log off;
              allow 127.0.0.1;
              deny all;
            }
    
            location /static/ {
                alias /var/lib/awx/public/static/;
            }
    
            location /favicon.ico { alias /var/lib/awx/public/static/favicon.ico; }
    
            location /websocket {
                # Pass request to the upstream alias
                proxy_pass http://daphne;
                # Require http version 1.1 to allow for upgrade requests
                proxy_http_version 1.1;
                # We want proxy_buffering off for proxying to websockets.
                proxy_buffering off;
                # http://en.wikipedia.org/wiki/X-Forwarded-For
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                # enable this if you use HTTPS:
                proxy_set_header X-Forwarded-Proto https;
                # pass the Host: header from the client for the sake of redirects
                proxy_set_header Host $http_host;
                # We've set the Host header, so we don't need Nginx to muddle
                # about with redirects
                proxy_redirect off;
                # Depending on the request value, set the Upgrade and
                # connection headers
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
            }
    
            location / {
                # Add trailing / if missing
                rewrite ^(.*)$http_host(.*[^/])$ $1$http_host$2/ permanent;
                uwsgi_read_timeout 120s;
                uwsgi_pass uwsgi;
                include /etc/nginx/uwsgi_params;            proxy_set_header X-Forwarded-Port 443;
                uwsgi_param HTTP_X_FORWARDED_PORT 443;
            }
        }
    }

Using docker-compose

Once you have validated that the files shown above are correct and match your environment.

This assumes you have all the files stored within your home directory

~/.awx/docker-compose

  cd ~/.awx/docker-compose
  docker-compose up -d

Docker containers

This will setup and run the 5 docker containers.

  1. awx_web
  2. awx_task
  3. awx_postgres
  4. awx_memcached
  5. awx_rabbitmq
Last updated on 15 Apr 2020
Published on 15 Apr 2020