Notes while launching a microservice LAMP stack (Amazon Linux, Docker)

This post is a log starting with an empty ec2-box (Amazon Linux) and moving toward a LAMP stack with a container for apache/php and a container for mysql. It assumes you already have images for apache/php and mysql. Local system is ubuntu linux. Containers use alpine linux.


Installing docker on a ec2 host box (see here).

Update the hosts' package manager and install docker.

sudo yum update -y
sudo yum install -y docker

Start the docker daemon.

sudo service docker start

Add ec2-user to the docker group.

sudo usermod -a -G docker ec2-user

Relogin.


For ease of ssh access, You can add the ec2 host to ~/.ssh/config file. I'll name my host "fred" and the user will be ssh user will be "ec2-user".

Host fred
  HostName 34.229.74.15
  Port 22
  User ec2-user
  IdentityFile [path/to/private_key]

Then you can simply run ssh name_of_host to ssh into the box.


Docker images can be zipped up and loaded using docker save and docker load

First we look at our built images to see which ones we want to zip up. Then we save the images to files.

docker save -o [path_where_to_save] [image_name]

Assuming we've put all the saved images in the docker_images folder, we can then transfer all the images we've made over to our host machine.

scp * fred:/home/jerry/docker_images

However, this can take a while if your images are large. I wanted to transfer faster so I decided to compress the images before transfer. I'll use lrzip to compress the images.

To install lrzip on the Amazon Linux host machine, you'll need to first enabled the EPEL repo.

sudo vim /etc/yum.repos.d/epel.repo

Change enabled=0 to enabled=1 in the epel section. Exit and view the repolist with sudo yum repolist. You should get some updates. Afterwards, you can install lrzip.

sudo yum install -y lrzip

You can zip the folder using lrzip, like so:

lrzip -z docker_images

Which will take a 400MB LAMP stack and compress it to 51MB. After transferring the compressed archive with scp, I can unpack the docker_images.tar.lrz file on the host box.

lrzip -d docker_images.tar.lrz

Which give me a docker_images.tar file. Now I can extract the files...

tar -xf docker_images.tar

Which will put the images in the docker_images folder. To install the images, I use docker load.

docker load -i my-apache
docker load -i my-mysql
docker load -i my-php

Now I can view the available images on the host box with docker images.


Next I need to transfer my project files to the host. I want git to manage files, so I created a "repo" folder on the host and initialized a bare repo.

git init --bare

Now I can add the "bare" repo as a remote repository on my local repository.

git remote add fred fred:/home/ec2-user/repo

Now I can easily push my repo to "fred".

git push fred master

And on the host box, I can create a clone of the repo.

git clone /home/ec2-user/repo project

Now I have a good workflow for pushing changes from my local filesystem to the host box. First I push to the "fred" remote. Then I go to the host and pull from the "origin" remote. Both remotes point to /home/ec2-user/repo.


So now that I have my project files and images, I can start up the stack with docker compose. First I install docker-compose on the host box (instructions). Then I can run the same docker-compose command I run on my local system.

To test the stack, I can add port 80 to my EC2 instance's inbound security group rules, and add an entry to my local system's hostfile.

111.111.111.111 mywebsite.com

I can now view my site at mywebsite.com.


Next I need to install an SSL certificate.

For testing purposes, I'll run the setup process inside the container, and later add the process to my apache Dockerfile. My containers run on alpine linux. Before running apk add apache2-ssl, I'll run apk update, otherwise I get "WARNING: Ignoring APKINDEX" and "ERROR: unsatisfiable constraints:" errors (see here).

apk update
apk add apache2-ssl

I now have mod_ssl.so inside the apache modules folder.

To get a letsencrypt certificate, I first install certbot on my local machine. Then I manually requested an ssl using the dns challenge.

sudo certbot certonly --manual --preferred-challenges dns

After running I now had a fullchain.pem, chain.pem, cert.pem, and privkey.pem files in the /etc/letsencrypt/live/sitename.com folder, and get this message.

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/sitename.com/fullchain.pem. Your cert
   will expire on 2017-10-13. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

There was a file in etc/apache2/conf.d named ssl.conf which was created when I installed mod_ssl.

I went through the file and made the appropriate modifications for my server config (changing document root, changing where log files are created, seting ServerName directive, and changing where SSLCertificateFile, and SSLCertificateKeyFile points to)

By default SSLCertificateFile and SSLCertificateKeyFile points to /etc/ssl/apache2. Since I would like to eventually have certbot installed on the box and running automatically, I decided to point SSLCertificateFile to the location that certbot generates certificates in.

# Server Certificate
SSLCertificateFile /etc/letsencrypt/live/sitename.com/fullchain.pem
# Server Private Key
SSLCertificateKeyFile /etc/letsencrypt/live/sitename.com/privkey.pem

I then created the letsencrypt folder manually in the container.

mkdir -p /etc/letsencrypt/live

Then I copied /etc/letsencrypt/live/sitename.com from local box to remote box. Then I copied the files from remote box into the container.

docker cp [folder_with_ssl_files] [containername]:/etc/letsencrypt/live/sitename.com

Since ssl operates over port 443, I needed to open port 443 on my EC2 instance. My apache Docker container already exposed port 443, and my run command mapped port 443 on the host box to 443 on the container.

After setting up ssl.conf I tried to restart the server and go this error.

httpd: Syntax error on line 166 of /etc/apache2/httpd.conf: Cannot load modules/mod_ssl.so into server: Error relocating /web/modules/mod_ssl.so: X509_up_ref: symbol not found

Running ldd /web/modules/mod_ssl.so it looked like there was a missing library that mod_ssl couldn't find. In the end, the only solution that I could get to work was to upgrade alpine from 3.5 to 3.6 on my apache image. Before doing that, I attempted to compile apache from source. I'll include a partial Dockerfile documenting that path for reference.

FROM nimmis/alpine-micro:3.6

RUN wget -P /tmp http://apache.mirrors.pair.com//httpd/httpd-2.4.27.tar.gz
RUN wget -P /tmp http://apache.claz.org/apr/apr-util-1.6.0.tar.gz
RUN wget -P /tmp http://apache.claz.org/apr/apr-1.6.2.tar.gz

RUN cd /tmp && \
    tar -zxvf httpd-2.4.27.tar.gz && \
    tar -zxvf apr-1.6.2.tar.gz && \
    tar -zxvf apr-util-1.6.0.tar.gz && \
    mv apr-1.6.2 httpd-2.4.27/srclib/apr && \
    mv apr-util-1.6.0 httpd-2.4.27/srclib/apr-util && \
    rm *.tar.gz && \
    cd httpd-2.4.27

Using apache memcache on alpine linux

There doesn't appear to be support for mod_pagespeed in alpine linux. I started down the path of compiling mod_pagespeed myself, but decided to look into out-of-the-box apache caching instead. However, this should install the prerequisites for compiling mod_pagespeed.

RUN apk add subversion git alpine-sdk
RUN cd /tmp && \
    git clone -b latest-stable --recursive https://github.com/pagespeed/mod_pagespeed.git