1

Let's say I've got an app where I want to test someone if he/she know how to deal with docker. To do so I've prepared working dockerfile with simple docker-compose.yml, as a sample I used Rails app, as follows:

dockerfile

FROM ruby:2.7.1

RUN apt-get update -qq && apt-get install -y nodejs

RUN mkdir /app
WORKDIR /app

COPY Gemfile Gemfile.lock ./
RUN gem install bundler
RUN bundle install

# Copy the main application.
COPY . ./

# Expose the applications port to the host machine
EXPOSE 3000

# Command to run when the container is started.
CMD bundle exec rails s -p 3000 -b '0.0.0.0'

docker-compose.yml

version: '3.8'

services:
  app:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app
    ports:
      - "3000:3000"

entrypoint.sh

#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

So now I want to broke these working setup with some common mistakes - do you know such a common mistakes? Delete ports maybe or change app location in dockerfile, what are common mistakes in such setup?

mr_muscle
  • 2,536
  • 18
  • 61
  • In my opinion, you should always (1) follow the [best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/), (2) not forget to actually set the `ENTRYPOINT` which it appears you have, (3) always pin the versions of whatever you're installing (preferably passing the versions as `ARG`'s and persisting the version in the containers `ENV`), (4) [lint your `Dockerfile`](https://github.com/hadolint/hadolint) and (5) split `apt-get update` and `apt-get install` on multiple lines, set `--no-install-recommends` in the `apt -get install`, (6) `WORKDIR` creates the dir.. – masseyb Oct 01 '20 at 15:58
  • declaring an entrypoint and not using it is already interesting. If you want to make it complicate, you can reproduce the error in this question: https://stackoverflow.com/questions/64156551/docker-mysql-4-0-27-start-problem-after-creating-dockerfile-entrypoint-sh – Stefano Oct 01 '20 at 15:58
  • 1
    Can take this to over to [code review](https://codereview.stackexchange.com) and can show you everything I'd change before even thinking about how I'd intentionally mess it up. – masseyb Oct 01 '20 at 16:02
  • Here is a example of an intentionally broken project that I created for work: https://github.com/yields-io/broken-minimal-keycloak – masseyb Nov 23 '20 at 11:00

1 Answers1

0

TL;DR;

If you're trying to test their knowledge of docker then your Dockerfile is already spoiled with common mistakes. If it's testing their knowledge of whatever is running inside the container created from an image built from your Dockerfile then it depends on what the application does and if you're trying to test them on their capabilities of debugging an application in general then the sky is the limit on how many hoops you can make them jump from incorrect binaries for the OS, incorrect file permissions, encoding issues, resource allocation, networking, ...


Current common mistakes

  1. "Split long or complex RUN statements on multiple lines separated with backslashes ..."
  2. Pin the version of the apt packages that you're installing (note: this is by far the most common mistake I see)
  3. Don't install packages recommended by apt that you do not need
  4. "When you clean up the apt cache by removing /var/lib/apt/lists it reduces the image size ..."
  5. "the instructions RUN, COPY, ADD create layers" and the WORKDIR instruction will create the directory if it doesn't already exist - removing the RUN mkdir app removes a layer from the final image
  6. Pin the version of gem packages
  7. Chain commands in the RUN instruction to reduce the amount of layers in the final image
  8. Set the ENTRYPOINT instruction in your Dockerfile
  9. Use JSON notation for CMD and ENTRYPOINT arguments

Note: debain based images perform cleanup after every apt-get install but hadolint will complain if you don't explicitly remove the apt files. You can either ignore the linting error or explicitly cleanup after the install, I prefer the latter.

A hadolint'd version of your Dockerfile:

FROM ruby:2.7.1

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        nodejs=10.21.0~dfsg-1~deb10u1 && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY Gemfile Gemfile.lock ./
RUN gem install bundler:2.1.4 && \ 
    bundle install

COPY . ./

EXPOSE 3000

ENTRYPOINT ["entrypoint.sh"]
CMD ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "'0.0.0.0'"]

Concerning the docker-compose configuration, setting the command is redundant:

version: '3.8'
services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - "3000:3000"

... regarding the ports being open it really depends on what your expectations are. If you're expecting the person to advise you that mapping ports is meh and that you should proxy traffic to the service (i.e. through nginx) then yes that should be considered an issue.

Additional common mistakes

A non-exhaustive list of common mistakes I often come across:

  1. COPY'ing a tar archive into an image then un-compressing the tar in 2 instructions
COPY files.tar.gz /
RUN tar czvf files.tar.gz

Can be replaced with ADD (removes a layer from the final image) i.e.: ADD files.tar.gz /

  1. Changing directories then uncompressing tar files
RUN cd / && tar xzvf files.tar.gz

Can change directories with tar's -C option before un-compressing the file i.e. tar -C / xzvf files.tar.gz

  1. curl'ing tar files then un-compressing them in 2 RUN's
RUN curl -Lo files.tar.gz https://example.com/files.tar.gz
RUN tar xzvf files.tar.gz

Can pipe the curl output into tar in a single RUN instruction i.e.: curl https://example.com/files.tar.gz | tar xzv

  1. Using pipe's without setting SHELL options ^ should be:
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl https://example.com/files.tar.gz | tar -C / xzv
  1. COPY'ing things from a multi-staged build that shouldn't be copied into the final layer:
FROM debian:buster-slim as build
# build everything
FROM scratch as target
COPY --from=build /some/random/file .
  1. COPY'ing secrets into builds (use buildkit and your ssh-agent for cloning repositories in the build)
masseyb
  • 3,745
  • 1
  • 17
  • 29