Search code examples
ruby-on-railsrubydockerdockerfilefig

How can I mount a volume from a data container while preserving the owner and permissions?


I'm using Fig and attempting to use a data volume container to share uploaded files between a Rails web server and a Resque worker running in another container. To do this the data volume container defines a /rails/public/system volume which is meant to be used to share these files. The Rails and Resque processes run as a rails user in their respective containers which are both based of the markb/litdistco image. All together the fig.yml looks like this:

redis:
  image: redis:2.8.17
  volumes_from:
    - file
web:
  image: markb/litdistco
  command: /usr/bin/start-server /opt/nginx/sbin/nginx
  ports:
    - 80:8000
    - 443:4430
  environment:
    DATABASE_URL:
  links:
   - redis
  volumes_from:
    - file
worker:
  image: markb/litdistco
  command: /usr/bin/start-server "bundle exec rake environment resque:work QUEUE=litdistco_offline RAILS_ENV=production"
  environment:
    DATABASE_URL:
  links:
   - redis
  volumes_from:
    - file
file:
  image: markb/litdistco
  command: echo "datastore"
  volumes:
    - /var/redis
    - /rails/log
    - ./config/container/ssl:/etc/ssl

When the web and worker containers are running, I can see the /rails/public/system directory in both, however it is owned by the root user in both containers and the permissions on the directory prevent the rails user from writing to this directory.

For reference there are two Dockerfiles which go into making the markb/litdistco container. The first defines a base image I use for local development (Dockerfile):

# This Dockerfile is based on the excellent blog post by SteveLTN:
#
#    http://steveltn.me/blog/2014/03/15/deploy-rails-applications-using-docker/
#
# KNOWN ISSUES:
#
# * Upgrading passenger or ruby breaks nginx directives with absolute paths

# Start from Ubuntu base image
FROM ubuntu:14.04

MAINTAINER Mark Bennett <[email protected]>

# Update package sources
RUN apt-get -y update

# Install basic packages
RUN apt-get -y install build-essential libssl-dev curl

# Install basics
RUN apt-get -y install tmux vim
RUN apt-get install -y libcurl4-gnutls-dev

# Install libxml2 for nokogiri
RUN apt-get install -y libxslt-dev libxml2-dev

# Install mysql-client
RUN apt-get -y install mysql-client libmysqlclient-dev

# Add RVM key and install requirements
RUN command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
RUN curl -sSL https://get.rvm.io | bash -s stable
RUN /bin/bash -l -c "rvm requirements"

# Create rails user which will run the app
RUN useradd rails --home /rails --groups rvm

# Create the rails users home and give them permissions
RUN mkdir /rails
RUN chown rails /rails

RUN mkdir -p /rails/public/system
RUN chown rails /rails/public/system
# Add configuration files in repository to filesystem
ADD config/container/start-server.sh /usr/bin/start-server
RUN chown rails /usr/bin/start-server
RUN chmod +x /usr/bin/start-server

# Make a directory to contain nginx and give rails user permission
RUN mkdir /opt/nginx
RUN chown rails /opt/nginx

# Switch to rails user that will run app
USER rails

# Install rvm, ruby, bundler
WORKDIR /rails
ADD ./.ruby-version /rails/.ruby-version
RUN echo "gem: --no-ri --no-rdoc" > /rails/.gemrc
RUN /bin/bash -l -c "rvm install `cat .ruby-version`"
RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"

# Install nginx
RUN /bin/bash -l -c "gem install passenger --no-ri --no-rdoc"
RUN /bin/bash -l -c "passenger-install-nginx-module"
ADD config/container/nginx-sites.conf.TEMPLATE /opt/nginx/conf/nginx.conf.TEMPLATE
ADD config/container/set-nginx-paths.sh /rails/set-nginx-paths.sh
RUN /bin/bash -l -c "source /rails/set-nginx-paths.sh"

# Copy the Gemfile and Gemfile.lock into the image.
# Temporarily set the working directory to where they are.
WORKDIR /tmp
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock

# bundle install
RUN /bin/bash -l -c "bundle install"

# Add rails project to project directory
ADD ./ /rails

# set WORKDIR
WORKDIR /rails

# Make sure rails has the right owner
USER root
RUN chown -R rails:rails /rails

# Publish ports
EXPOSE 3000
EXPOSE 4430
EXPOSE 8000

This is tagged as the litdistco-base image, then I use config/containers/production/Dockerfile to generate the image that I tag as markb/litdistco and run in staging and production.

# Start from LitDistCo base image
FROM litdistco-base

MAINTAINER Mark Bennett <[email protected]>

USER rails

# Setup volumes used in production
VOLUME ["/rails/log", "/rails/public/system"]

# Build the application assets
WORKDIR /rails
RUN /bin/bash -l -c "touch /rails/log/production.log; chmod 0666 /rails/log/production.log"
RUN /bin/bash -l -c "source /etc/profile.d/rvm.sh; bundle exec rake assets:precompile"

Can anyone possibly explain how I can get the data container volume to mount as writeable by the rails user. I'd very much like to avoid running any of the Ruby processes as root, even inside a container.

For some context I should also mention that I'm developing the images in Docker in boot2docker on Mac OS X, then running them on a Google Compute Engine instance on an Ubuntu 14.04 host. Thanks!


Solution

  • I would modify your image a little bit. Write a shell script that wraps the /usr/bin/start-server command in your fig.yml and place that inside your container.

    Then you can chown rails anything that you need before starting up your server.

    Running the container with a default user rails is not really needed either, as long as you start up the server as the rails user: sudo -u rails /usr/bin/start-server (or something like that).

    Personally haven't used the litdistco-base image yet, so do not know all the specifics on how it works.