Run Semaphore Ansible web UI in Docker Container

Posted on 138 views

Semaphore is an open-source tool that provides a beautiful web interface to run ansible playbooks. This tool written in Go languages can be installed on Windows, Linux(x64, ARM, ARM64), and macOS systems. This tool comes in handy when your project has grown and you do not need to deploy it from the command line.

The amazing features associated with Semaphore Ansible web UI are:

  • Pure Go allows one to cross-compile it for several platforms. This is the main reason why it is preferred by DevOps.
  • It allows one to run playbooks by schedule
  • View detailed logs of any runs at any time
  • Group playbooks to projects
  • Easily manage environments, inventories, repositories, and access keys.
  • You can use it to build, deploy and roll back if needed from the browser.
  • Get notified about playbook runs
  • It allows one to grant other users privileges to run playbooks

In addition to the above features, Semaphore supports MySQL, PostgreSQL, and BoltDB. There are many ways one can install and use the Semaphore Ansible web UI. These include using Snap, package managers, binary files e.t.c.

This guide offers a deep illustration of how to run the Semaphore Ansible web UI in Docker Container.

Before you Begin.

This guide requires one to have both the Docker engine and docker-compose installed. Docker can be installed on Linux systems using the aid from the guide below.

Once the Docker Engine has been installed, add your system user to the Docker group with the command:

sudo usermod -aG docker $USER
newgrp docker

Proceed and install Docker-compose with the dedicated guide:

Verify the installation.

$ docker-compose version
Docker Compose version v2.4.1

Now start and enable docker to run on system boot.

sudo systemctl start docker && sudo systemctl enable docker

Step 1 – Provision the Semaphore Container.

The container will contain two parts, i.e

  • Database – (MySQL) for data storage
  • Ansible Semaphore(latest image)

Create the docker-compose file using the below commands:

mkdir semaphore && cd semaphore
vim docker-compose.yml

Add the below lines to the file replacing variables where needed.

version: '2'

services:

  mysql:
    ports:
      - 3306:3306
    image: mysql:5.6
    container_name: mysql
    hostname: mysql
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
      MYSQL_DATABASE: semaphore_db
      MYSQL_USER: semaphore_user
      MYSQL_PASSWORD: StrongPassw0rd

  semaphore:
    ports:
      - 3000:3000
    image: ansiblesemaphore/semaphore:latest
    container_name: semaphore
    environment:
      SEMAPHORE_DB_USER: semaphore_user
      SEMAPHORE_DB_PASS: StrongPassw0rd
      SEMAPHORE_DB_HOST: mysql
      SEMAPHORE_DB_PORT: 3306
      SEMAPHORE_DB: semaphore_db
      SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
      SEMAPHORE_ADMIN_PASSWORD: AdminPassword
      SEMAPHORE_ADMIN_NAME: admin
      SEMAPHORE_ADMIN_EMAIL: [email protected]
      SEMAPHORE_ADMIN: admin
      SEMAPHORE_ACCESS_KEY_ENCRYPTION: MflCLIUF5bn6Lgkuwy4BoAdIFhoZ4Ief2oocXmuZSjs=
    depends_on:
      - mysql

In the file, replace:

  • MYSQL_USER and SEMAPHORE_DB_USER with the desired database user.
  • MYSQL_PASSWORD and SEMAPHORE_DB_PASS with the database password for the user
  • SEMAPHORE_ADMIN_PASSWORD with the password for the admin user for Semaphore Ansible web UI
  • SEMAPHORE_ACCESS_KEY_ENCRYPTION with the key to encrypt the access keys in your database. This key encryption can be generated using the command:
$ head -c32 /dev/urandom | base64
MflCLIUF5bn6Lgkuwy4BoAdIFhoZ4Ief2oocXmuZSjs=

Step 2 – Run Semaphore Ansible web UI in Docker Container

Now with the container provisioned, we can run Semaphore using the command:

docker-compose up -d

Execution output:

[+] Running 19/19
 ⠿ semaphore Pulled                                                                                                                   21.2s
   ⠿ 4e9f2cdf4387 Pull complete                                                                                                        0.9s
   ⠿ 8eba5c7ef78e Pull complete                                                                                                        1.3s
   ⠿ 4b2e0cc6dd21 Pull complete                                                                                                       18.3s
   ⠿ 5d4e24327603 Pull complete                                                                                                       18.4s
   ⠿ b53de92b8d36 Pull complete                                                                                                       18.9s
   ⠿ 8ada155f2bd8 Pull complete                                                                                                       19.2s
 ⠿ mysql Pulled                                                                                                                       19.0s
   ⠿ 35b2232c987e Pull complete                                                                                                        5.1s
   ⠿ fc55c00e48f2 Pull complete                                                                                                        5.5s
   ⠿ 0030405130e3 Pull complete                                                                                                        6.2s
   ⠿ e1fef7f6a8d1 Pull complete                                                                                                        6.9s
   ⠿ 1c76272398bb Pull complete                                                                                                        7.6s
   ⠿ f57e698171b6 Pull complete                                                                                                        9.4s
   ⠿ f5b825b269c0 Pull complete                                                                                                        9.8s
   ⠿ dcb0af686073 Pull complete                                                                                                       10.4s
   ⠿ 27bbfeb886d1 Pull complete                                                                                                       15.6s
   ⠿ 6f70cc868145 Pull complete                                                                                                       15.9s
   ⠿ 1f6637f4600d Pull complete                                                                                                       16.3s
[+] Running 3/3
 ⠿ Network semaphore_default        Created                                                                                            0.3s
 ⠿ Container semaphore-mysql-1      Started                                                                                            1.6s
 ⠿ Container semaphore-semaphore-1  Started                                                                                            1.5s

Verify if the containers are running with services exposed.

$ docker ps 
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS          PORTS                                       NAMES
ea51aeb59791   ansiblesemaphore/semaphore:latest   "/sbin/tini -- /usr/…"   17 seconds ago   Up 15 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   semaphore
abf873ba28c2   mysql:5.6                           "docker-entrypoint.s…"   18 seconds ago   Up 16 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp   mysql

Step 3 – Secure Semaphore Ansible web UI with SSL certificates.

This guide demonstrates how to secure the Semaphore Ansible web UI with Let’s Encrypt and Self-signed certificates.

Begin by installing the Nginx web server.

##On RHEL/CentOS/Rocky Linux 8
sudo yum install nginx

##On Debian/Ubuntu
sudo apt install nginx

Proceed and create the virtual host file for Semaphore.

sudo vim /etc/nginx/conf.d/semaphore.conf

In the opened file, add the lines below:

upstream semaphore 
    server 127.0.0.1:3000;
  

server 
        listen 80;
        server_name semaphore.example.com;
        client_max_body_size 0;
        chunked_transfer_encoding on;

    location / 
      proxy_pass http://semaphore/;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_buffering off;
      proxy_request_buffering off;
    

    location /api/ws 
      proxy_pass http://semaphore/api/ws;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Origin "";
    

Save the file and proceed to generate SSL certificates.

1. Secure Semaphore Ansible web UI with Let’s Encrypt SSL.

Let’s Encrypt is used to issue free trusted SSL certificates to a domain name for free. To be able to generate the SSL certificates, install the required packages.

##On RHEL 8/CentOS 8/Rocky Linux 8/Fedora
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm 
sudo dnf install certbot python3-certbot-nginx

##On Debian/Ubuntu
sudo apt install certbot python3-certbot-nginx

Issue SSL certificates with the command:

sudo certbot --nginx

Proceed as below:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): Enter a valid Email address here          

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Account registered.

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: semaphore.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1

Requesting a certificate for semaphore.example.com
Performing the following challenges:
http-01 challenge for semaphore.example.com
....
Successfully received certificate.
Certificate is saved at: a2enmod ssl

That is it, you will have your certificate added to your Nginx config file.

2. Secure Semaphore Ansible web UI with Self-signed Certificates.

In case you do not have an FQDN, you can generate Self-signed Certificates for your domain name/IP Address as below.

Create a config file to be used to generate the certificates.

$ vim semaphore_ssl.conf
[req]
default_bits       = 2048
default_keyfile    = semaphore_ssl.key
distinguished_name = req_distinguished_name
req_extensions     = req_ext
x509_extensions    = v3_ca

[req_distinguished_name]
countryName                 = Country Name (2 letter code)
countryName_default         = KE
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Nairobi
localityName                = Locality Name (eg, city)
localityName_default        = Nairobi
organizationName            = Organization Name (eg, company)
organizationName_default    = Computingforgeeks
organizationalUnitName      = organizationalunit
organizationalUnitName_default = Development
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_default          = Your_IP-Address
commonName_max              = 64

[req_ext]
subjectAltName = @alt_names

[v3_ca]
subjectAltName = @alt_names

[alt_names]
DNS.1   = localhost
DNS.2   = Your_IP-Address

Generate the SSL certificates using OpenSSL as below:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout semaphore_ssl.key -out semaphore_ssl.crt -config semaphore_ssl.conf

Press enter to the end and you will have an SSL certificate pair generated. Copy the certificates to the /etc/ssl/certs directory.

sudo cp semaphore_ssl.crt /etc/ssl/certs/semaphore_ssl.crt
sudo mkdir -p /etc/ssl/private/
sudo cp semaphore_ssl.key /etc/ssl/private/semaphore_ssl.key

Now edit your Nginx config file as below.

sudo vim /etc/nginx/conf.d/semaphore.conf

Make the below changes to the file.

upstream semaphore 
    server 127.0.0.1:3000;
  

server 
        server_name semaphore.example.com;
        client_max_body_size 0;
        chunked_transfer_encoding on;

    location / 
      proxy_pass http://semaphore/;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_buffering off;
      proxy_request_buffering off;
    

    location /api/ws 
      proxy_pass http://semaphore/api/ws;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Origin "";
    
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/ssl/certs/semaphore_ssl.crt;
    ssl_certificate_key /etc/ssl/private/semaphore_ssl.key;

server 
    if ($host = semaphore.example.com) 
        return 301 https://$host$request_uri;
    
    listen 80;
    server_name semaphore.example.com;
    return 404;

Save the file and restart Nginx.

sudo systemctl restart nginx

Step 4 – Access Semaphore Ansible web UI

At this point, you will have Semaphore accessible via HTTPS. Allow HTTP and HTTPS through the firewall.

##For Firewalld
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload

##For UFW
sudo ufw allow http
sudo ufw allow https

Now proceed and access the Semaphore Ansible web UI using the URL https://doamin_name

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container

Login using the admin user and password created. Proceed and create a project.

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container-1

Once a project has been created, the dashboard will be granted as below.

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container-2-1024x438

Create a task template. A template is used to define how ansible will run the playbook. Creating a new template requires one to provide:

  • Inventory
  • Playbook filename
  • Playbook repository
  • Environment
  • Vault password file
  • Extra CLI arguments e.t.c

The task template can be:

  • Task
  • Build
  • Deploy

task simply runs the specified playbook.

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container-3

The build template is used to create artifacts. Here, you need to specify the start version of the artifact.

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container-4

The deploy template now deploys artifacts to the server. This is normally associated with the build template.

How-To-Run-Semaphore-Ansible-web-UI-in-Docker-Container-5

Step 5 – Manage the Containers.

In this guide, we have two containers that can be managed using the docker start and stop commands below:

##To stop
docker stop semaphore
docker stop mysql

##To start
docker start semaphore
docker start mysql

##To delete
docker rm semaphore
docker rm mysql

These containers can be managed as system services. To be able to achieve this, we will create service files for each container.

For MYSQL

$ sudo vim /etc/systemd/system/mysql_container.service
[Unit]
Description=MySQL container

[Service]
Restart=always
ExecStart=/usr/bin/docker start -a mysql
ExecStop=/usr/bin/docker stop -t 2 mysql

[Install]
WantedBy=local.target

For Semaphore.

$ sudo vim /etc/systemd/system/semaphore_container.service
[Unit]
Description=Semaphore container

[Service]
Restart=always
ExecStart=/usr/bin/docker start -a semaphore
ExecStop=/usr/bin/docker stop -t 2 semaphore

[Install]
WantedBy=local.target

Reload the system daemon.

sudo systemctl daemon-reload

Start and enable the container services to run on boot.

##For MySQL
sudo systemctl start mysql_container
sudo systemctl enable mysql_container

##For Semaphore
sudo systemctl start semaphore_container
sudo systemctl enable semaphore_container

Check the status of the service:

$ systemctl status mysql_container semaphore_container
● mysql_container.service - MySQL container
   Loaded: loaded (/etc/systemd/system/mysql_container.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2022-04-28 10:48:47 EDT; 1min 11s ago
 Main PID: 8906 (docker)
    Tasks: 5 (limit: 23682)
   Memory: 38.7M
   CGroup: /system.slice/mysql_container.service
           └─8906 /usr/bin/docker start -a mysql

● semaphore_container.service - Semaphore container
   Loaded: loaded (/etc/systemd/system/semaphore_container.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2022-04-28 10:49:09 EDT; 49s ago
 Main PID: 8957 (docker)
    Tasks: 6 (limit: 23682)
   Memory: 19.4M
   CGroup: /system.slice/semaphore_container.service
           └─8957 /usr/bin/docker start -a semaphore

That is it!

Now you can easily deploy applications/services using semaphore Ansible web UI. I hope this was helpful

coffee

Gravatar Image
A systems engineer with excellent skills in systems administration, cloud computing, systems deployment, virtualization, containers, and a certified ethical hacker.