Make a Docker Lab With Linux, Mac and Windows

Here’s a quickie realization for folks like me who naively figured it would be easy to integrate my Windows and Mac VS Code users with Docker. This realization resulted in me building a bare-metal Linux box to make everything work a lot easier for our Docker lab.

Docker Is Easy, Usually

It’s easy for most engineers to use Docker if you are working on one platform. If you’re working exclusively in Windows, MacOS or Linux, then you’re probably not going to hit the speed bump I’m about to describe.

This advice will resonate for IT pros who need to integrate Docker into an enterprise with Mac and Windows developers.

Ignore The Windows Docker Strategy

Sometimes I get led astray in my devops studies because I am lured into a vendor strategy.

I see a shiny object that makes me feel better. I am like a fish chasing a lure because this newfangled vendor strategy promises me things will be glorious once I buy into the strategy. What really happens is that I get hooked on the vendor’s offering.

If you are working with Node, PHP,  or Golang to build a cloud app, then you should know that the Windows Docker strategy is crap. 

Docker Lab
This is the good way to make a Docker Lab

The containerization “strategy” announced by Docker and Microsoft in 2017 is a good example of vendors luring in IT pros with talk of nirvana. Here is that strategy in a nutshell: if you want to run Windows servers within a Docker container, that is now possible. You still need Hyper-V running underneath Docker on a Windows 2016 server, so whatever.

Hopefully this little realization will save someone else the time and bother I wasted going down a few rabbit holes.

Linux Rules Devops

As it is with all things devops, it is always best to go back to mother, i.e. Linux.

After studying the Docker documentation and tuning my network, I realized that I needed Docker to run on a dedicated Linux platform. If I told my Docker clients that DOCKER_HOST was the Linux server, I figured I might have a solution that worked! SPOILER ALERT — It does and it’s spectacular.

Here’s the real zinger that got me to set up a dedicated server. The documentation on setting up networks and exposing containers is for Linux. The Docker networking instructions give solutions using iptables in Linux.

Set Up Your Docker Lab Server

Take note that I am using an open, unauthenticated port on the Docker server for control communication, which Docker does not recommend. You can implement TLS security on your ports to tighten things up if needed.

I went with a bare metal Linux installation for a couple of reasons. First, Docker involves the use of virtualization technology, and it’s always best to avoid nesting virtualizations. Also, just about any spare PC will do for this lab setup. Even a five-year-old desktop with a 120 GB SSD will be an awesome Linux lab server. 

I only spent an hour sitting in the lab setting up my new server. I used the latest LTS version of Ubuntu, but several Linux distros may be used for your Docker Linux host. If you use another distro, then check for distribution-specific instructions for how to open Docker port 2375.

To set up a simple Mac and Windows Docker lab without security, follow these instructions.

  1. Start with a working VS Code installation on Mac and Windows.
  2. Install Docker locally on both Mac and Windows developer workstations.
  3. Integrate VS Code with Docker on Mac and Windows. Make sure you have the Docker extension installed and working properly.
  4. Prepare a bare-metal server from the distribution ISO with Ubuntu Server 18.04 LTS.
  5. Assign the server a fixed, private IP, such as 10.0.0.20.
  6. Remove AppArmor on the Linux server to improve performance.
  7. Follow these instructions to install Docker-CE.
  8. To open up port 2375 update the following system files and reboot your server (source). 
# File: /etc/default/docker
# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="tcp://10.0.0.20:2375 -H unix:///var/run/docker.sock"

# File: /lib/systemd/system/docker.service
## Add EnviromentFile + add "$DOCKER_OPTS" at end of ExecStart
## After change exec "systemctl daemon-reload"
EnvironmentFile=/etc/default/docker
ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_OPTS

Update Mac and Windows Environments

Start configuring your clients by adding the following line to your  .zshrc and .bashrc files on the Mac:

export DOCKER_HOST=tcp://10.0.0.20:2375

On Windows, go into the System control panel, Advanced Settings, Environment Variables and add the following:

DOCKER_HOST=tcp://10.0.0.20:2375

If you are using Windows Subsystem for Linux (WSL), and you use Docker with WSL, then add the export statement to your .zshrc and .bashrc files too.

Restart VS Code and any terminal or shell programs you have running. Launch a new shell and test it with docker info. You should see Ubuntu 18.04 OS listed in the output.

Troubleshooting

First, double-check the export statements and Environment Variable settings in your client environments. Make sure you have the “:2375” on the end.

If you have doubts whether you have successfully opened up port 2375 on your Linux server check the port manually. First, make sure you have telnet installed on your Windows, Mac or WSL. Issue this telnet command to see if the port on host 10.0.0.20 is open.

$ telnet 10.0.0.20 2375

If the port is open, then telnet will continue to run and you will need to quit it with CTRL-C or CTRL-]. If the port is not open, then you will get a communication refused error message.

Celebration Time!

After installing your new host, disable the Docker daemons running on Mac and Windows. The Docker CLI works without the local servers running.

Now it’s time to bask in the glory of your conquest. Run into the next office and claim victory!

Start a PHP 7.2 Slim Project on Ubuntu 18.04

I use Slim, a lightweight PHP framework for creating HTTP applications and APIs using “routes.”

Here’s my formula for deploying my Slim app on Ubuntu 18.04 with PHP 7.2. This has worked on GCP and AWS, as well as my own hosted cluster.

Please note that I will be working with a raw sudo terminal session, so I will omit the use of sudo from these instructions.

One Fresh LAMP Image, Please

Let us start with a fresh installation of Ubuntu 18.04.

# important!
apt update
apt -y upgrade
reboot

I like using tasksel to install LAMP (Apache, MySql and PHP). tasksel is the menu you encounter when installing Ubuntu from an ISO. If I am installing on a cloud service, I don’t get the opportunity to use this menu, so I have to install it manually.

apt install -y tasksel
tasksel
# Scroll down to LAMP Server
# Hit Spacebar to select
# Tab to the OK button and hit Enter

After I install MySql, I always secure it.

mysql_secure_installation
# Follow the prompts and accept all security recommendations

Use Certbot for Free SSL

Hooray for Certbot and Let’s Encrypt! Now it only takes a few minutes to configure Apache with SSL certificates.

Configure Public Domain Names

Super-important first step: assign a domain name you control to the public IP address of your hosted Ubuntu instance. The public clouds give you a public IP when you set up a new instance. Use that IP address to set up DNS A records for your host.

For example, if I have a domain called mydomain.com, and I want a host to be called api.mydomain.com and www.mydomain.com, and I want mydomain.com to work as well, and my public-facing IP address is 34.34.34.34, then I need these A records in my mydomain.com.db DNS zone file:

@    14400  IN  A  34.34.34.34
api  14400  IN  A  34.34.34.34
www 14400 IN A 34.34.34.34

Use Certbot to Install Let’s Encrypt Certificates

Start by installing Certbot and accepting the license terms.

add-apt-repository ppa:certbot/certbot
# Hit Enter to accept the terms
apt install -y python-certbot-apache

Run the certbot command as shown, entering all of your domain names. Enter your email address for identification and sign up for the EFF.org newsletter! Pick the option to automatically redirect your HTTP traffic to HTTPS.

certbot --apache -d mydomain.com -d www.mydomain.com -d api.mydomain.com
# Enter your email address
# Pick the option to redirect HTTP to HTTPS

Install PHP Modules and Composer

Slim uses the popular Composer module management system for PHP. I need a few PHP modules to get Composer to work with my Slim projects.

apt install -y composer zip php-curl php-xml php-mbstring php-zip

Load Project Files

For day-to-day work on a PHP/Slim project, I use a regular, unprivileged user account. I set up a new account with the adduser command. In this example the username vern is just an example. Select any username you want.

adduser vern
# Select a strong password
# Complete the "Full Name" field
# Hit Enter for the remaining prompts

Now, I need to impersonate the new user and load the project files from GitHub (or wherever I have my repository) into the project directory. After that I bring in all the dependent modules by running Composer.

In this example, I start a new Slim project called myproject using the Slim Skeleton repository.

cd ~vern
su vern
git clone https://github.com/slimphp/Slim-Skeleton.git myproject
cd myproject
composer install
exit

The last step in developer account preparation is to give Apache ownership of the log directory. Change vern to your developer account name.

chown www-data:www-data /home/vern/myproject/logs

Configure Apache for Slim

Edit the Apache SSL configuration file that was generated by Certbot:

vi /etc/apache2/sites-enabled/000-default-le-ssl.conf

The contents should like like this.

<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
ServerName api.mydomain.com
SSLCertificateFile /etc/letsencrypt/live/api.mydomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.mydomain.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Change the DocumentRoot directive to point to the project’s public directory.

DocumentRoot /home/vern/myproject/public

Add the following <Directory> directive before the </VirtualHost> tag.

<Directory "/home/vern/myproject/public">
  Options Indexes FollowSymLinks MultiViews
   AllowOverride all
   Require all granted
   <IfModule mod_rewrite.c>
    RewriteEngine on
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
   </IfModule>
</Directory>

Finally, your 000-default-le-ssl.conf file should look like this:

<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin webmaster@localhost
DocumentRoot /home/vern/myproject/public
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
ServerName api.telnexus.com
SSLCertificateFile /etc/letsencrypt/live/api.mydomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.mydomain.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
<Directory "/home/vern/myproject/public">
   Options Indexes FollowSymLinks MultiViews
   AllowOverride all
   Require all granted
   <IfModule mod_rewrite.c>
     RewriteEngine on
     RewriteCond %{REQUEST_FILENAME} !-f
     RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
   </IfModule>
</Directory>
</VirtualHost>
</IfModule>

Save and restart Apache.

apache2ctl restart

Bask In The Glory!

Fire up your browser and go to https://api.mydomain.com/ and you should see the Slim default page.