Alex's Slip-box

These are my org-mode notes in sort of Zettelkasten style

Containerized project environments

Instead of messing about with version managers and local environment setup when writing code, these days I prefer to just use docker and skip that hassle. The trade off is the little bit of overhead setting up a ~Dockerfile and docker-compose.yml. This is mostly boiler plate with some minor customization for what is needed for my chosen language and project needs (ie, adding system dependencies like pandoc or something), so it’s not that big of a deal.

The example here uses Ruby Gem project, but can easily be adapted for any language.

# Just a Dockerfile

We could just use a Dockerfile alone (ie, not use Docker Compose at all). That would go something like this:

# Setup

Create an empty project directory and add a Dockerfile.

# The Dockerfile

Add this to the Dockerfile.

FROM ruby:3.0.0

# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1


COPY Gemfile Gemfile.lock *.gemspec ./
RUN bundle install

COPY . .

CMD ["/bin/bash"]

# Scaffold the project

I need to generate all the boilerplate files, install the dependencies and generate the Gemfile.lock. This can be done by building a container on the fly from the same image specified in the Dockerfile (eg, ruby:3.0.0), mapping a volume from local project directory to the working directory (eg, /app) and passing the commands to docker run

docker run --rm -v "$PWD":/app -w /app ruby:3.0.0 bundle gem my_proj && mv my_proj/* ./ && rm -rf my_proj

# Fill out the .gemspec

Before the dependencies can be installed, a valid .gemspec is required. The bare minimum I need is:

  • authors
  • email
  • summary
  • description
  • version (change this to a string and remove the require_relative)
    • I mean it’s kinda silly to declare the version in a ruby file, isn’t it?

The remaining meta data can be deleted or commented out.

# Install dependencies and generate the Gemfile.lock

With the .gemspec filled out, I can install the dependencies. This is the same as before, but just do a bundle install.

docker run --rm -v "$PWD":/app -w /app ruby:3.0.0 bundle install

# Build the container and Write code

  • Build the container from the image and tag it. docker build -t my-proj .
  • Run the container: docker run -it --rm -v "$PWD":/app my-proj
    • The entry point drops me into a bash prompt inside the container.
  • Write code.

# Add Docker Compose (optional)

Docker Compose is totally optional, but there’s some advantages:

  • The compose file could be a global file that specifies different environments.
  • Easier to create volumes and using PWD means the volume is always bound to the the working dir from which you run docker compose.

# Add docker-compose.yml

This builds off the Dockerfile and the setup above.

version: "3.6"

    # this is the same as the CMD in Dockerfile (this overrides it, actually)
    command: /bin/bash 
    build: .
      - ${PWD}:/app:cached # filesyncing volume so don't have to rebuild.
      - "12345:12345" # Expose a port (ie, serivce-ports) to the host if needed
      # Add environment variables
      LANG: C.UTF-8
    working_dir: /app

To run it:

docker-compose run --rm --service-ports ruby
  • The --service-ports is to expose the ports on a run command (as opposed to docker-compose up which would be used when doing something like running a server and the ports would be exposed normally)

# Global docker-compose.yml

…or if using a global docker-compose.yml

docker-compose -f ~/path/to/global/docker-compose.yml run --rm ruby

# Other Examples

# VueJS project

The Dockerfile

FROM node:16.2-alpine3.11


COPY package.json package-lock.json
RUN npm install

COPY . .

CMD ["/bin/sh"]

Open a shell prompt and setup the project:

docker run --rm -it -v "$PWD":/app -w /app node:16.2-alpine3.11 sh
  • npm install -g @vue/cli
  • vue create my-project
  • mv my-project/* my-project/.gitignore ./
  • rmdir my-project

The docker-compose.yml

version: "3.6"

    command: npm run serve
    build: .
      - ${PWD}:/app:cached
      - "8080:8080"
      LANG: C.UTF-8
    working_dir: /app

Run it with docker-compose up