Tyler Smith

Alpine and Python, EXPOSE-ing ports and unprivileged users in containers

I went to bed last night thinking I'd spend today working on installing Jenkins on a server. Instead, I took the day to dig deeper into optimizing my images and tightening up my Dockerfiles and docker-compose.yml files. Right now it's all still fresh in my head, and my hope is that if I spend some extra time to dig deeper I won't forget everything immediately.

Alpine + Python = Headaches

One of the main recommendations I hear about building containers is use small distributions "like Alpine" as a base. Alpine is always the go-to example, and it bothered me that both my Node.js and nginx containers use Alpine while my Python container use a light version of Debian.

I looked up a tag for an Alpine-based Python image off of Docker Hub and then tried to run my Dockerfile as-is. No luck: Alpine doesn't use apt-get. I switched from apt-get to apk and tried again. No luck: apk has a different API. I looked up apk's API and tried again. No luck: libpq-dev is actually called postgresql-dev on Alpine. I tried again. No luck: psycopg2 failed to build because Alpine needs musl instead of gcc... or something.

This all seemed comically bad. I took to Google and found two articles by pythonspeed.com discussing the trouble with Alpine images for Python. One article said Alpine builds could take 50 times as long as other distros because standard PyPI wheels don't work on Alpine, meaning you need to compile all of the dependency code manually during the build process (though this could get better with PEP 656). In another article, Python Speed recommends Ubuntu or a light Debian image as the base for the container. I guess I'm sticking with the Debian-based image I already have.

EXPOSE-ing Docker ports

Today I confirmed what I thought but wasn't 100% sure of: EXPOSE in a Dockerfile makes the port available within the Docker network without making it available to the host. This is nice, because it also means that these ports aren't out there on the Internet if the container is running on a webserver. It would still be nice if Docker listened to firewall rules.

I removed the 'ports' for the front-end and back-end services on both my development and production Compose files, then added EXPOSE near the top of each Dockerfile with the relevant port.

Unprivileged users

I tried using unprivileged USERs in the front-end and back-end containers, but everything was exploding from wonky permissions. I still need to figure out how to make this work. I'm keeping my container users as root until I can dig into this a bit deeper. Thankfully SacMusic is a non-critical project with no sensitive data.

Cleaning up development stage

I reworked the development stage in my Dockerfiles so they wouldn't copy in the project files (except for requirements.txt in the Python container), opting to mount the host directories using the docker-compose.yml file. I also moved the run commands for the development stage from the docker-compose.yml file to the respective Dockerfiless to keep everything cleaner. This also leads to extremely fast development builds because I'm not installing NPM packages or system packages in the development stage of my Dockerfiles.

The downside is it makes the initial application wonkier to set up from an initial git pull. I have the steps in my head now, but just barely. I wrote thorough documentation on how to do an initial setup of the application because I will forget all of this. I thought about putting the setup steps in a script instead of the README, but I think it's important that I see the commands in front of me so I can reason about what they do.

Next steps

My next steps are taking a few days away from coding. My carpal tunnel is flaring up today, which means I need to take a break. When I was 19 I got carpal tunnel so bad that it set me back on music for a year. I'm not doing that again. Fortunately, it's not an issue that comes up often, and it usually goes away quickly with rest. I've been hitting this project hard, and doing an abnormal amount of typing from rapid-fire try-and-fail things on my Dockerfiles, Jenkinsfile, and Compose files. Normal programming tends to be a little more methodical and less frantically-slamming-on-the-keyboard.

I've been wanting to dig into some of the books I planned to read this year (the list is more aspirational than realistic at this point), and this seems like a good excuse to dig in. Maybe I'll start with Mastering Ubuntu Server: it seems appropriate with the server-focused stuff I've been doing lately.