C Dorman
C Dorman

Reputation: 549

Dockerfile RUN command stops working after ROS install

I am installing ROS as instructed at http://wiki.ros.org/noetic/Installation/Ubuntu in a dockerfile. After installing, you set up the environment with 'source /opt/ros/noetic/setup.bash'. However, after I do that, every RUN command in the dockerfile stops working correctly.

In the dockerfile below, the first 'ls /' produces the expected output, but the second one does not. This is not specific to 'ls', that's just a demo of the problem; every command I RUN after the ROS environment does not have the effect expected. What is happening in the ROS environment setup to break it and how do I prevent it? I have a lot of other setup that needs to happen after setting up ROS.

FROM ubuntu:20.04

# Dependencies
RUN apt-get update && apt-get install -y lsb-release && apt-get clean all
RUN apt-get install -y gnupg2

# Set timezone so tzdata is not interactive during install
ENV TZ=America/New_York
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Install ROS, from http://wiki.ros.org/noetic/Installation/Ubuntu
RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
RUN apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 && \
    apt-get update && \
    apt install -qq -y ros-noetic-ros-base

RUN apt-get install -y python3-pip
RUN python3 -m pip install rospkg

RUN echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc

# This DOES produce what you would think
RUN ls /

SHELL ["/bin/bash", "-c", "source ~/.bashrc"]

# This DOES NOT produce what you would think
RUN ls /

Upvotes: 0

Views: 455

Answers (1)

David Maze
David Maze

Reputation: 158714

The Dockerfile SHELL directive changes the shell that's used to interpret RUN commands. The default is ["/bin/sh", "-c"], so you get

# These two commands are identical with the default SHELL
RUN ls /
RUN ["/bin/sh", "-c", "ls /"]

When you write SHELL as you've done, you instead get

SHELL ["/bin/bash", "-c", "source ~/.bashrc"]
RUN ls /
# Equivalently:
RUN ["/bin/bash", "-c", "source ~/.bashrc", "ls /"]

However, the sh -c option reads a single command word and executes it; any further options are ignored (mostly; the command word sees them as $1 positional parameters).

Mostly in Docker, shell dotfiles like .bashrc are completely ignored. Setting up environment variables using ENV directives is better. For the single main container process, you can also use an entrypoint wrapper script to load a file of variables, but this doesn't work during the container build or in docker exec debug shells.

You can possibly take advantage of sh -c's specific semantics, and knowing the command will be passed as a single additional argument, and say

SHELL ["/bin/bash", "-c", ". ~/.bashrc && $1"]

source in every Dockerfile RUN call instead suggests:

# Tell bash these are "login" shells and _should_ read shell dotfiles
SHELL ["/bin/bash", "--login", "-c"]

Upvotes: 1

Related Questions