
When I was learning to drive, during the very first lesson, the instructor backed onto an intersection and then said that you can never do that. I remembered that rule immediately and for life.
You read Grigory Oster's 'Harmful Advice' to children, and you see how easily and effortlessly they understand that this is not how things should be done.
There are a ton of articles on how to properly write a Dockerfile. But I haven't come across any instructions on how to write incorrect Dockerfiles. I'm filling this gap. And maybe, in the projects I support, there will be fewer such Dockerfiles.
All characters, situations, and Dockerfiles are fictional. If you recognize yourself, sorry.
Let's create a Dockerfile, sinister and terrifying.
Peter (Senior java/ruby/php developer): Colleague Vasily, have you already pushed the new module to Docker?
Vasily (junior): No, I didn't have time, I can't figure this Docker thing out. There are so many articles; my eyes are going cross.
Peter: Our deadline was a year ago. Let me help you, and we'll figure it out as we go. Tell me what's not working.
Vasily: I can't choose a base image thatās minimal but has everything I need.
Peter: Take the ubuntu image; it has everything you need. And if it has some unnecessary stuff, it might come in handy later. And donāt forget to tag it as latest, so you always have the newest version.
And the first line in the Dockerfile appears:
FROM ubuntu:latestPeter: What's next, what did we write our module on?
Vasily: It's ruby, right? The web server and a couple of auxiliary daemons should be running.
Peter: Got it, what we need: ruby, bundler, nodejs, imagemagick, and whatever else... And also, upgrade it to make sure we get the latest packages.
Vasily: Should we create a user to avoid running as root?
Peter: Forget that, we'll deal with permissions later.
Vasily: I need some time, about 15 minutes, to wrap all this into one command. I read thatā¦
(Peter cuts off the thorough and overly smart junior rudely.)
Peter: Write them as separate commands; it will be easier to read.
The Dockerfile grows:
FROM ubuntu:latest
RUN apt-get update
RUN apt-get upgrade
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full
RUN gem install bundler
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
RUN bundle install --without development test --path vendor/bundle
RUN rm -rf /usr/local/bundle/cache/*.gem
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*Suddenly, Igor Ivanovich, DevOps (but more Ops than Dev), bursts into the office, shouting:
II: Petya, your developers have broken the production database again, when will this endā¦.
After a brief argument, Igor Ivanovich cools down and starts to figure out what his colleagues are doing.
AI: What are you busy with?
Vasily: Peter is helping me create a Dockerfile for the new module.
AI: Let me take a look⦠What have you written here? You're cleaning the repository with a separate command; this is an additional layer⦠How are you managing dependencies if you haven't copied the Gemfile? And overall, this is unacceptable.
Peter: Please go about your business; we'll sort this out.
Igor Ivanovich sighs sadly and leaves to investigate who broke the database.
Peter: Yes, but he was right about the code; we need to include it in the image. And letās install ssh and supervisor right away, otherwise how are we going to run the daemons?
Vasily: I'll first copy the Gemfile and Gemfile.lock, then install everything, and only then will I copy the entire project. If the Gemfile doesn't change, the layer will be taken from the cache.
Peter: Whatās with all this layer talk? Just copy everything at once. Copy everything. Right in the first line.
The Dockerfile now looks like this:
FROM ubuntu:latest
COPY .\/ \/app
WORKDIR \/app
RUN apt-get update
RUN apt-get upgrade
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
RUN gem install bundler
RUN curl -sL https:\/\/deb.nodesource.com\/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
RUN bundle install --without development test --path vendor\/bundle
RUN rm -rf \/usr\/local\/bundle\/cache\/*.gem
RUN apt-get clean
RUN rm -rf \/var\/lib\/apt\/lists\/* \/tmp\/* \/var\/tmp\/* Peter: So, what's next? Do you have configs for supervisor?
Vasily: No, I don't. But I'll make it quickly.
Peter: Do it later. Let's draft an init script now that will run everything. So, you start ssh with nohup, so we can connect to the container and see what went wrong. Then start supervisor the same way. And then you'll just run passenger.
V: But I read that there should be one process so Docker will know when something goes wrong and can restart the container.
P: Don't clutter your mind with nonsense. And how? How are you going to run all this in one process? Let Igor Ivanovich worry about stability; after all, he's the one getting paid. Our job is to write code. And besides, he should be grateful we wrote the Dockerfile for him.
Ten minutes and two cat videos later.
V: I did everything. I even added some comments.
P: Show me!
The latest version of the Dockerfile:
FROM ubuntu:latest
# Copying the source code
COPY .\/ \/app
WORKDIR \/app
# Updating the package list
RUN apt-get update
# Upgrading packages
RUN apt-get upgrade
# Installing required packages
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
# Installing bundler
RUN gem install bundler
# Installing nodejs used for building static files
RUN curl -sL https:\/\/deb.nodesource.com\/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
# Installing dependencies
RUN bundle install --without development test --path vendor\/bundle
# Cleaning up caches
RUN rm -rf \/usr\/local\/bundle\/cache\/*.gem
RUN apt-get clean
RUN rm -rf \/var\/lib\/apt\/lists\/* \/tmp\/* \/var\/tmp\/*
# Running the script when the container starts, which will launch everything else.
CMD ["\/app\/init.sh"]P: Great, I like it. And comments in Russian, convenient and readable, everyone should work like this. I taught you everything, you can handle it on your own now. Letās go have coffeeā¦
Well, here we have an ideally terrible Dockerfile, at the sight of which Igor Ivanovich will want to resign, and his eyes will hurt for a week after. The Dockerfile could, of course, be even worse; thereās no limit to perfection. But for starters, this will do.
I would like to conclude with a quote from Grigory Oster:
If you still haven't firmly
Chosen a path in life,
And donāt know where to begin
Your labor journey,
Go break light bulbs in the stairwells ā
People will say "Thank you."
You will help the people
Save electricity.
Source: habr.com
