4
\$\begingroup\$

I'm getting started with Docker, and I've set up a very simple image built from a Dockerfile. It's based FROM the ubuntu image, and I'm downloading and building apache from source.

I'm just curious if I'm on the right tracks, if there's a more "Docker" way of doing what I'm doing, or any other things that I could be doing different/better.

It's running in the default VM that Kitematic adds for me, and I'm building the image with docker build -t webserver ., then testing with docker run -p 80:80 -i -t webserver /bin/bash

FROM ubuntu:latest

RUN apt-get -qq  update -y
RUN apt-get -qq install build-essential -y

RUN mkdir install-files
WORKDIR /install-files
RUN mkdir apache
WORKDIR /install-files/apache
RUN apt-get -qq install wget -y >/dev/null
RUN wget http://mirrors.ukfast.co.uk/sites/ftp.apache.org//apr/apr-1.5.2.tar.gz >/dev/null
RUN tar -xvzf apr-1.5.2.tar.gz >/dev/null
WORKDIR /install-files/apache/apr-1.5.2
RUN ./configure >/dev/null
RUN make >/dev/null
RUN make install >/dev/null

WORKDIR /install-files/apache
RUN wget http://apache.mirror.anlx.net//apr/apr-util-1.5.4.tar.gz >/dev/null
RUN tar -xvzf apr-util-1.5.4.tar.gz >/dev/null
WORKDIR /install-files/apache/apr-util-1.5.4
RUN ./configure --with-apr=/usr/local/apr/ >/dev/null
RUN make >/dev/null
RUN make install >/dev/null


WORKDIR /install-files/apache/
RUN apt-get -qq install libpcre3 libpcre3-dev >/dev/null
RUN wget http://mirror.ox.ac.uk/sites/rsync.apache.org//httpd/httpd-2.4.18.tar.gz >/dev/null
RUN tar -xvzf httpd-2.4.18.tar.gz >/dev/null
WORKDIR /install-files/apache/httpd-2.4.18
RUN ./configure --with-apr=/usr/local/apr/ >/dev/null
RUN make >/dev/null
RUN make install >/dev/null

RUN mkdir /tech
RUN ln -s /usr/local/apache2/ /tech/httpd

RUN apt-get -qq install nano -y
ADD startup.sh /root/startup.sh
RUN chmod 777 /root/startup.sh

WORKDIR /
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

Do you really have a good reason for this permission setup?

RUN chmod 777 /root/startup.sh

It's a bit ironic to use the -v flag of tar, and then redirecting the output to /dev/null here:

RUN tar -xvzf httpd-2.4.18.tar.gz >/dev/null

You can drop that flag and the redirection, the outcome should be the same.


I'm not very familiar with Docker, but I'm wondering what will happen if any of the commands fail. For example, if make fails, you probably don't want to run the make install command that follows it. I don't have a solution for that, but error handling is something to think about.


The repetitive redirections and duplicated strings don't look pretty, and look hard to maintain. There gotta be a better way. Perhaps you could put the shell commands into a proper .bash script, enjoy all the power of regular bash scripting (variables, functions), and in the docker file just fetch that script using wget, and then execute it.

\$\endgroup\$
3
\$\begingroup\$

A few comments:

  • I’m not sure why you’re so keen to suppress any and all output from your RUN commands. That information can be useful when you’re trying to work out why your image hasn’t built properly. It can be a bit verbose when it goes fine, but it’s useful when it fails.

    (Docker’s inbuilt networking is sometimes a bit flakey for me, and apt-get and yum just fail when trying to retrieve indexes. Being able to see their output as they can’t connect out is always useful.)

  • No comments! A Dockerfile should be treated like any other piece of code. There should be comments explaining why it was written this way, what the intended outcome is, and so on. This file could be quite hard to maintain.

    For example, there are a couple of hard-coded URLs to mirrors that pull tarfiles. Here are some maintenance questions that comments might help me answer:

    • When should I rev the version numbers in those URLs? I don’t want to be getting insecure versions of packages.
    • Where should I look for an alternative if those mirrors go away?
    • What are these packages for? When can I know if I don’t need them any more?

  • Each line in a Dockerfile adds a new layer to the image’s filesystem, so you want to avoid creating unnecessary layers. For example, four lines at the top of the first block can be condensed into two:

    RUN mkdir -p /install-files/apache
    WORKDIR /install-files/apache
    

    You could also use the -O flag of wget to avoid having to cut out the first WORKDIR line.

  • You’re installing nano and wget with apt-get, but it’s not obvious why. I feel like the former could be replaced with curl (which is built-in), and if you ever need a text editor inside your container, install it when you need it. Don’t fill your container with things you don’t need.

\$\endgroup\$
1
  • \$\begingroup\$ WORKDIR will create the directories if they do not already exist, the RUN mkdir ... instructions can be removed entirely. \$\endgroup\$
    – masseyb
    Commented Oct 31, 2019 at 14:13
2
\$\begingroup\$

Old question but you can do a lot to reduce the size of your final image (RUN, COPY and ADD create layers).

Here's my version, with specific observations as comments:

# I would suggest pinning a specific version of the image that you're extending
# ubuntu:latest today may not be the same as ubuntu:latest tomorrow
FROM ubuntu:18.04

# combining the apt-get update and install RUN instructions removes layers
# all packages that you need can be installed in the same RUN (using curl instead of wget)
RUN apt-get -qq update -y && \
    apt-get -qq install -y \
        build-essential \
        curl \
        libpcre3 \
        libpcre3-dev \
        nano

# WORKDIR will create the directories if they do not already exist
# removing the RUN's removes an additional 2 layers
WORKDIR /install-files/apache/apr-1.6.5
# combine the curl | tar RUN instructions (note: versions updated)
# install apr, expat, apr-utils and httpd in a condensed fashion and purge the installation files
# the final image is 374MB without the installation files, 468MB with the installation files
RUN curl -L http://mirrors.ukfast.co.uk/sites/ftp.apache.org/apr/apr-1.6.5.tar.gz | tar xvz -C /install-files/apache && \
    ./configure && \
    make && \
    make install && \
    rm -rf /install-files/apache/apr-1.6.5

WORKDIR /install-files/apache/expat-2.2.9
RUN curl -L https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.gz | tar xvz -C /install-files/apache && \
    ./configure && \
    make && \
    make install && \
    rm -rf /install-files/apache/expat-2.2.9

WORKDIR /install-files/apache/apr-util-1.6.1
RUN curl -L http://mirrors.ukfast.co.uk/sites/ftp.apache.org/apr/apr-util-1.6.1.tar.gz | tar xvz -C /install-files/apache && \
    ./configure --with-apr=/usr/local/apr/ && \
    make && \
    make install && \
    rm -rf /install-files/apache/apr-util-1.6.1

WORKDIR /install-files/apache/httpd-2.4.41
RUN curl http://mirror.ox.ac.uk/sites/rsync.apache.org//httpd/httpd-2.4.41.tar.gz | tar xvz -C /install-files/apache && \
    ./configure --with-apr=/usr/local/apr --with-expat=/usr/local/expat && \
    make && \
    make install && \
    rm -rf /install-files/apache/httpd-2.4.41

WORKDIR /
RUN mkdir /tech && \ 
    ln -s /usr/local/apache2/ /tech/httpd
# 1. "Although ADD and COPY are functionally similar, generally speaking, COPY is preferred."
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy
# 2. It's common practice to name the entrypoint script "docker-entrypoint.sh"
# 3. would suggest following the principle of least privilege
# https://digitalguardian.com/blog/what-principle-least-privilege-polp-best-practice-information-security-and-compliance
# COPY preserves file permissions - set them on the host and remove the chmod layer altogether
COPY docker-entrypoint.sh /root

# finally, our ENTRYPOINT
ENTRYPOINT ["/root/docker-entrypoint.sh"]
\$\endgroup\$
3
  • \$\begingroup\$ @TobySpeight "you can do a lot to reduce the size of your final image" is what prompted me to write this version. "the final image is 374MB without the installation files, 468MB with the installation files" is an improvement over the original along with all the best practices - e.g. the review is in-line as comments in the Dockerfile... \$\endgroup\$
    – masseyb
    Commented Nov 1, 2019 at 9:56
  • 1
    \$\begingroup\$ Ah, I've just realised that you hid your review in the comments of your replacement! That wasn't obvious (and not the usual way to review), so I've edited your answer to make it clearer. \$\endgroup\$ Commented Nov 1, 2019 at 11:26
  • \$\begingroup\$ Thanks for the edit. I intentionally put them in-line as per the comment here: "No comments! A Dockerfile should be treated like any other piece of code. There should be comments explaining why it was written this way ...". \$\endgroup\$
    – masseyb
    Commented Nov 1, 2019 at 11:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.