Docker container behind NGINX Frontend - ghomem/legacy_puppet_infrastructure GitHub Wiki

Installing the docker module

The first step would be installing the docker module. Because of a bug in the offical docker module that makes containers with the latest tag redownload after every agent run, we forked the project and added a fix for that.

To install this version, please do the following:

On the puppet master, switch to root with sudo su - . If you already have the docker module installed, remove it with

puppet module uninstall puppetlabs-docker

Clone our fork of the repository to a folder named docker:

cd /etc/puppetlabs/code/environments/production/modules
git clone https://github.com/ghomem/puppetlabs-docker-compat.git docker
chown -R deployment:deployment docker

and create a link pointing to it in the deployment home directory:

sudo ln -s /etc/puppetlabs/code/environments/production/modules/docker /home/deployment/docker
git config --global --add safe.directory /etc/puppetlabs/code/environments/production/modules/docker
# upgrade the version of puppetlabs-powershell
puppet module upgrade puppetlabs-powershell --version 4.0.0

After this is done, we need to switch the branch of the repository to the one with our fix:

su - deployment
cd docker
git checkout puppetlabs_docker_issue_627_compat
# Exit from the deployment user
exit
# restart the puppet service as root
systemctl restart puppetserver.service

With that done, you can check that the module is indeed installed with the correct version, which should be 3.14.0:

sudo puppet module list | grep docker

Node declaration example for hello world docker

The node declaration of a simple docker-based hello world could be written as:

node 'hello-world-node' {

  # this declaration allows for docker to manage its own firewall rules
  class { 'puppet_infrastructure::node_base' : firewall_strict_purge => false, firewall_ignore_patterns => ['docker'] }
  include passwd_common

  $app_domain = 'helloworld.domain.eu'
  $app_port   = '5007'
  $nameserver = '8.8.8.8'

  class {'docker':
    use_upstream_package_source => false,
    service_overrides_template  => false,
    docker_ce_package_name      => 'docker.io',
    dns                         => $nameserver,
  }
  
  puppet_infrastructure::docker_container { 'flask-helloworld':
    image     => 'digitalocean/flask-helloworld',
    app_port  => '5000',
    host_port => $app_port,
  }

  # nginx reverse proxy frontend
  include puppet_infrastructure::nginx_base

  class { 'puppet_infrastructure::nginx_frontend':
    domain                  => $app_domain,
    frontend_sslprefix      => 'star.domain.eu',
    backend_hosts_and_ports => ["localhost:${app_port}", ],
    backend_protocol        => 'http',
    letsencrypt_certificate => true,
  }

  # Allow HTTP/HTTPS connections
  include puppet_infrastructure::firewall_addon_web

}

Node declaration for registry imported images

If you wish to import images from one or more private or public registries, your node declaration would be written as:

node 'ubuntu-node' {

  # this declaration allows for docker to manage its own firewall rules
  class { 'puppet_infrastructure::node_base' : firewall_strict_purge => false, firewall_ignore_patterns => ['docker'] }
  include passwd_common

  $nameserver = '8.8.8.8'
  $domain = 'domain.eu'
  $priv_registry = 'ghcr.io'
  $username = 'exampleuser'
  $token = 'MYPRIVATETOKEN'
  $custom_network = 'my-net'

  class {'docker':
    use_upstream_package_source => false,
    service_overrides_template  => false,
    docker_ce_package_name      => 'docker.io',
    dns                         => $nameserver,
    require  => Class['puppet_infrastructure::node_base'],
  }

  # setup a custom network so that the containers communicate between each other
  docker_network { $custom_network:
    ensure   => present,
  }
  
  # a container pulled from a private registry
  puppet_infrastructure::docker_container { 'app-name':
    image      => "${priv_registry}/${username}/app-name/",
    digest     => 'sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    app_port   => '3000',
    username   => $username,
    token      => $token,
    network    => $custom_network,
  }

  # another container pulled from the same private registry
  puppet_infrastructure::docker_container { 'other-app-name':
    image      => "${priv_registry}/${username}/app-name/",
    tag        => 'latest',
    app_port   => '4000',
    username   => $username,
    token      => $token,
    network    => $custom_network,
    env        => ['FOO=BAR', 'FOO2=BAR2'],
  }

  # a container pulled from a public registry
  puppet_infrastructure::docker_container { 'hello-world':
    image      => 'infrastructureascode/hello-world',
    app_port   => '8080',
    network    => $custom_network,
  }

  # nginx reverse proxy frontend
  include puppet_infrastructure::nginx_base

  puppet_infrastructure::ssl_nginx_domain { "star.${domain}":
    sslprefix               =>  "star.${domain}",
    letsencrypt_certificate => true,
  }

  # the reverse proxy for the first container
  puppet_infrastructure::nginx_frontend_domain { 'myservice.${domain}':
    domain                                => 'myservice.${domain}',
    frontend_sslprefix                    => 'star.${domain}',
    backend_hosts_and_ports               => ['localhost:3000', ],
    backend_protocol                      => 'http',
    manage_ssl                            => false,
    letsencrypt_certificate               => true,
  }

  # the reverse proxy for the second container
  puppet_infrastructure::nginx_frontend_domain { 'myservice2.${domain}':
    domain                                => 'myservice2.${domain}',
    frontend_sslprefix                    => 'star.${domain}',
    backend_hosts_and_ports               => ['localhost:4000', ],
    backend_protocol                      => 'http',
    manage_ssl                            => false,
    letsencrypt_certificate               => true,
  }

  # the reverse proxy for the third container
  puppet_infrastructure::nginx_frontend_domain { 'myservice3.${domain}':
    domain                                => 'myservice3.${domain}',
    frontend_sslprefix                    => 'star.${domain}',
    backend_hosts_and_ports               => ['localhost:8080', ],
    backend_protocol                      => 'http',
    manage_ssl                            => false,
    letsencrypt_certificate               => true,
  }

  # Allow HTTP/HTTPS connections
  include puppet_infrastructure::firewall_addon_web
}

Creating custom networks is not mandatory for running individual containers or multiple containers that do not require intercommunication. However, for scenarios where containers need to communicate with each other, setting up one or more custom networks is essential. Containers on the same network can communicate with each other using their container names. To verify this inter-container communication, after deploying and running the agent with the provided code, execute the following command to test if two containers within the same network can successfully ping each other:

sudo docker exec -it app-name ping other-app-name