Docker: Bad Tips


    When I learned to drive a car, in the very first lesson the instructor went to the intersection in reverse, and then said that you shouldn’t do it — never at all. This rule I remembered immediately and for life.


    You read the “Bad Advice” to children by Grigory Oster, and you see how easily and naturally it comes to them that this cannot be done.


    A lot of articles have been written on how to write Dockerfile correctly. But I did not come across instructions on how to write the wrong Dockerfile. I fill this gap. And maybe in the projects that I get for support, there will be less such dockerfiles.


    All heroes, situations and dockerfile are fictional. If you recognize yourself, sorry.


    Create a Dockerfile, Ominous and Terrible


    Peter (Senior java / ruby ​​/ php developer): Colleague Vasily, have you already uploaded a new module to Docker?
    Basil (junior): No, I didn’t, I can’t figure it out with this Docker. So many articles on it, my eyes run wide.


    Peter: We got a deadline a year ago. Let’s help, we’ll figure it out in the process. Tell me what you can’t do there.


    Basil: I can’t choose the basic image so that it is minimal, but there was everything that was needed.
    Peter: Take the image of ubuntu, it has everything you need. And what a lot of excess, then it will come in handy. And don't forget to put the latest tag so that the version is always the latest.


    And in the Dockerfile, the first line appears:


    FROM ubuntu:latest

    Peter: What's next, what did we write our module on?
    Basil: So ruby, there a web server and a couple of service daemons should start.
    Peter: Yeah, what do we need: ruby, bundler, nodejs, imagemagick so what else ... And at the same time, upgrade to get new packages for sure.
    Basil: And we will not create the user, so as not from root?
    Peter: Oh well him, then still fool around with rights.
    Vasily: I need time, about 15 minutes, to make it all in one team, I read that ...
    (Peter rudely interrupts the meticulous and very smart June.)
    Peter: Write in separate commands, and it will be easier to read.


    Dockerfile is growing:


    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/*

    Then Igor Ivanovich burst into the office, DevOps (but more Ops than Dev), shouting:


    AI: Petya, your developers again broke the prod database, when does it end ....


    After a little skirmish, Igor Ivanovich cools down and begins to find out what his colleagues are doing here.


    AI: What are you doing?
    Basil: Peter helps me compile a Dockerfile for a new module.
    II: Let’s take a look ... What have you written here, you clean the repository with a separate command, this is an additional layer ... But how do you put the dependencies if you did not copy the Gemfile! And anyway, this is no good.
    Peter: Please go about your business, we’ll figure it out somehow.


    Igor Ivanovich sighs sadly and leaves to figure out who has broken the database.


    Peter: Yes, but about the code, he said the right thing, you need to stuff it into the image. And let's put ssh and supervisor right away, otherwise we’ll start the daemons.


    Vasily: Then I first copy Gemfile and Gemfile.lock, then I put everything in, and then I copy the entire project. If the Gemfile does not change, the layer will be taken from the cache.
    Peter: What are you all with these layers, copy everything at once. Copy right away. The first line.


    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?
    Basil: Nah, no. But I will do it quickly.
    Peter: Then do it. Let's now sketch out the init script that will run everything. So, with that, you start ssh with nohup so that we can connect to the container and see what went wrong. Then also launch supervisor. Well, then just run passenger.
    Q: But I read that there should be one process, so Docker will know that something went wrong and can restart the container.
    P: Don't bother with your head. And in general, how? How do you run it all in one process? Let Igor Ivanovich think about stability, not for nothing that he receives a salary. Our business is to write code. And anyway, let him say thanks for writing a Dockefile for him.


    After 10 minutes and two videos about cats.


    Q: I did everything. I also added comments.
    P: Show!


    Fresh version of Dockerfile:


    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
    # Устанавливаем bundler
    RUN gem install bundler
    # Устанавливаем nodejs используется для сборки статики
    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/* 
    # Запускаем скрипт, при старте контейнера, который запустит все остальное.
    CMD [“/app/init.sh”]

    P: Great, I like it. And the comments in Russian are convenient and readable, everyone would work like that. I taught you everything, then you can do it yourself. Let's go drink coffee ...


    Well, here we have got an absolutely terrible Dockerfile, from the sight of which Igor Ivanovich wants to quit and his eyes will hurt for another week. Dockerfile, of course, could be even worse, there is no limit to perfection. But for starters, it will do.


    I would like to end with a quote from Gregory Oster:


    If you’re still not firm
    in life you have chosen the road,
    and you don’t know where
    Labor should start your way,
    hit the light bulbs in the hallways -
    People will say “Thank you”.
    You will help the people to
    save electricity.


    UPD : In the comments they ask what is wrong with these Dockerfiles. The other day I will write a separate article with a parsing of errors.


    Also popular now: