Things I learned while writing a Dockerfile
I've been writing a Dockerfile for work that installs Postgresql and sets up a database. I ran into a few gotchas along the way. Here's the documentation of my misconceptions and the solutions.
One Line VS Multiple Lines
I originally was using one RUN command per shell line that I wanted to exectue:
Example:
RUN service postgresql start
RUN su postgres -c "createuser -d -r -s root"
The Problem
I wanted to separate the commands in order to optimize recreation of other images. But I'd always get the error that postgres wasn't running. That's exactly because each RUN command is a separate layer of running machine. Starting postgres in a RUN command does nothing because as soon as the next RUN command is reached, I'm in a new VM, and there's no postgres running at all.
The Solution
Join commands that are part of one cohesive set up step into one RUN statement:
(Note, thanks @outrunthewolf for pointing out that line continuations \
can be used to carry the same command across multiple lines for readability).
RUN service postgresql start && \
su postgres -c "createuser -d -r -s root"
sudo
VS su
The Problem
Online guides for installing and setting up postgres instruct the user to run commands as sudo
, but this won't work with a Dockerfile because the commands are running as root, and root doesn't have access to the sudo
command.
The Solution
Use su {USER} -c "commands go here"
instead to execute commands as a different user.
An example Dockerfile
Here's an example Dockerfile that installs postgresql, creates a root
role, and creates a database called accounts
.
FROM ubuntu
MAINTAINER Murphy Randle, murphy@spacemonkey.com
## Install Postgresql deps
RUN apt-get install -y postgresql postgresql-contrib
## Postgresql setup:
RUN service postgresql start && su postgres -c "createuser -d -r -s root" && createdb accounts && service postgresql stop
ENTRYPOINT ["/bin/bash"]
Written by Murphy Randle
Related protips
7 Responses
You should maintain multiple lines for readability by adding && \ and dropping your command to the next line.
That's a good point, @outrunthewolf! Thanks!
Thanks for the tip on running su from inside a dockerfile. It was exactly what I needed.
Currently docker allows for even cleaner approch. You can just swap to specific user for next run commands. See:
RUN id
USER app
ENV HOME /app
RUN id
PS: I tend to add ENV HOME as some scripts (i.e. bower) want's to edit files in ${HOME}/.config.bla
hmm... unknown text processor. Let's try again :)
RUN id
USER app
ENV HOME /app
RUN id
@munhitsu
The USER command is useful when all RUN statements can be executed as that user. But what about when specific RUN commands need to be done under specific users? Can the USER command be used more than once in a Dockerfile?
Eg:
USER root
RUN echo 'export ORACLE_SID=XE' >> /etc/bash.bashrc
USER oracle
RUN sqplus @my_schema.sql
Yes, that's exactly how it works. You can use USER as many times as you want.