From e004041de2ec5c14633a50a445ff63ef4260a36c Mon Sep 17 00:00:00 2001 From: Toilal Date: Sat, 4 Apr 2015 17:50:16 +0200 Subject: [PATCH] more dockerization work [From @joshdata: This is part of @toilal's work in #377 and https://github.com/Toilal/mailinabox/commit/1eb77c332bca0a74a37f792460903a1fb660befc. The changes are: * Separates out the runit configuration from starting Mail-in-a-Box setup so that Mail-in-a-Box setup does not block the starting of runit services and we can assume that runit is running during setup (i.e. we can restart services). * Adds a SKIP_INSTALL flag so that the container can be restarted without re-running the whole Mail-in-a-Box setup. * Made containers/docker/run more flexible. * I'm also adding some "|| exit 0"s to the run script to stop if there are any docker errors. * I'm also adding the prereqs installs from questions.sh into Dockerfile so we don't have to reinstall each time. ] --- Dockerfile | 31 +++--- containers/docker/init.sh | 79 -------------- containers/docker/my_init.d/10-mailinabox.sh | 58 ++++++++++ containers/docker/run | 93 +++++++++++----- containers/docker/runit/rsyslogd.sh | 3 - containers/docker/tools/configure_services.sh | 103 ++++++++++++++++++ setup/dns.sh | 4 +- setup/functions.sh | 20 ++-- setup/owncloud.sh | 1 + setup/questions.sh | 2 + setup/start.sh | 8 -- 11 files changed, 258 insertions(+), 144 deletions(-) delete mode 100755 containers/docker/init.sh create mode 100755 containers/docker/my_init.d/10-mailinabox.sh delete mode 100755 containers/docker/runit/rsyslogd.sh create mode 100755 containers/docker/tools/configure_services.sh diff --git a/Dockerfile b/Dockerfile index 3663649c..f5eb3c42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,15 @@ FROM phusion/baseimage:0.9.16 # Dockerfile metadata. MAINTAINER Joshua Tauberer (http://razor.occams.info) -EXPOSE 25 53/udp 53/tcp 80 443 587 993 -VOLUME /data +EXPOSE 25 53/udp 53/tcp 80 443 587 993 4190 +VOLUME /home/user-data + +# Use baseimage's init system. A correct init process is required for +# process #1 in order to have a functioning Linux system. +CMD ["/sbin/my_init"] + +# Create the user-data user, so the start script doesn't have to. +RUN useradd -m user-data # Docker has a beautiful way to cache images after each step. The next few # steps of installing system packages are very intensive, so we take care @@ -35,20 +42,16 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get upgrade -y # Install packages needed by Mail-in-a-Box. ADD containers/docker/apt_package_list.txt /tmp/mailinabox_apt_package_list.txt RUN DEBIAN_FRONTEND=noninteractive apt-get install -y $(cat /tmp/mailinabox_apt_package_list.txt) -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y rsyslog -RUN rm -f /tmp/mailinabox_apt_package_list.txt -RUN apt-get clean -# Create the user-data user, so the start script doesn't have to. -RUN useradd -m user-data +# from questions.sh -- needs merging into the above line +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y dialog python3 python3-pip +RUN pip3 install "email_validator==0.1.0-rc4" # Now add Mail-in-a-Box to the system. ADD . /usr/local/mailinabox -# We can't know things like the IP address where the container will eventually -# be deployed until the container is started. We also don't want to create any -# private keys during the creation of the image --- that should wait until the -# container is started too. So our whole setup process is deferred until the -# container is started. -RUN mkdir -p /etc/my_init.d -RUN ln -s /usr/local/mailinabox/containers/docker/init.sh /etc/my_init.d/20-mailinabox.sh +# Configure runit services. +RUN /usr/local/mailinabox/containers/docker/tools/configure_services.sh + +# Add my_init scripts +ADD containers/docker/my_init.d/* /etc/my_init.d/ diff --git a/containers/docker/init.sh b/containers/docker/init.sh deleted file mode 100755 index 8f7e377e..00000000 --- a/containers/docker/init.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# This script is used within containers to turn it into a Mail-in-a-Box. -# It is referenced by the Dockerfile. You should not run it directly. -######################################################################## - -# Local configuration details were not known at the time the Docker -# image was created, so all setup is defered until the container -# is started. That's when this script runs. - -# If we're not in an interactive shell, set defaults. -if [ ! -t 0 ]; then - export PUBLIC_IP=auto - export PUBLIC_IPV6=auto - export PRIMARY_HOSTNAME=auto - export CSR_COUNTRY=US - export NONINTERACTIVE=1 -fi - -# The phusion/baseimage base image we use for a working Ubuntu -# replaces the normal Upstart system service management with -# a ligher-weight service management system called runit that -# requires a different configuration. We need to create service -# run files that do not daemonize. - -# For most of the services, there is a common pattern we can use: -# execute the init.d script that the Ubuntu package installs, and -# then poll for the termination of the daemon. -function make_runit_service { - INITD_NAME=$1 - WAIT_ON_PROCESS_NAME=$2 - mkdir -p /etc/service/$INITD_NAME - cat > /etc/service/$INITD_NAME/run < /etc/mailinabox.conf << EOF; +STORAGE_USER=$STORAGE_USER +STORAGE_ROOT=$STORAGE_ROOT +PRIMARY_HOSTNAME=$PRIMARY_HOSTNAME +PUBLIC_IP=$PUBLIC_IP +PUBLIC_IPV6=$PUBLIC_IPV6 +PRIVATE_IP=$PRIVATE_IP +PRIVATE_IPV6=$PRIVATE_IPV6 +CSR_COUNTRY=$CSR_COUNTRY +EOF +fi diff --git a/containers/docker/run b/containers/docker/run index e9a310ed..ddb4997f 100755 --- a/containers/docker/run +++ b/containers/docker/run @@ -3,75 +3,112 @@ # ================================================================== # # Run this script from the base directory of the Mail-in-a-Box -# repository (i.e. run as 'containers/docker/run'). +# repository (i.e. run as './containers/docker/run'). +# +# Set these optional environment variables as needed: +# * HOST_HTTP_PORT: Host http: port to bind (default: 80). +# * HOST_HTTPS_PORT: Host https: port to bind (default: 443). +# * SKIP_BUILD: Skip the build of docker image (default: unset). +# * NODNS: Skip mapping of DNS ports (53 tcp/upd). They are not always available on host, as another DNS server can be running (default: unset). +# * CONTAINER_NAME: Name of the main container (default: mailinabox). +# * CONTAINER_DATA_NAME: Name of the data container (default: mailinabox-data). +# * NONINTERACTIVE: Use this when mailinabox is already installed on the volume container. Else, it's not recommanded (default: unset). # # A base image is created first. The base image installs Ubuntu # packages and pulls in the Mail-in-a-Box source code. This is # defined in Dockerfile at the root of this repository. # -# A mailinabox-userdata container is started next. This container +# A mailinabox-data container is created next. This container # contains nothing but a shared volume for storing user data. # It is segregated from the rest of the live system to make backups # easier. # -# The mailinabox-services container is started last. It is the +# The mailinabox container is started last. It is the # real thing: it runs the mailinabox image. This container will -# initialize itself and will initialize the mailinabox-userdata +# initialize itself and will initialize the mailinabox-data # volume if the volume is new. # Build or rebuild the image. # Rebuilds are very fast. -tput setaf 2 -echo "Building/updating base image (mailinabox)..." -tput setaf 7 +HOST_HTTP_PORT=${HOST_HTTP_PORT:-80} +HOST_HTTPS_PORT=${HOST_HTTPS_PORT:-443} +CONTAINER_NAME=${CONTAINER_NAME:-mailinabox} +CONTAINER_DATA_NAME=${CONTAINER_DATA_NAME:-${CONTAINER_NAME}-data} -docker build -q -t mailinabox . - -if ! docker ps -a | grep mailinabox-userdata > /dev/null; then +if [ -z "$SKIP_BUILD" ]; then tput setaf 2 - echo - echo "Creating a new container for your data (mailinabox-userdata)..." + echo "Building/updating base image (mailinabox)..." tput setaf 7 - docker run -d \ - --name mailinabox-userdata \ + docker build -q -t mailinabox . || exit 1 +fi; + +if ! docker inspect ${CONTAINER_DATA_NAME} > /dev/null; then + tput setaf 2 + echo + echo "Creating a new container for your data (${CONTAINER_DATA_NAME})..." + tput setaf 7 + + docker create \ + --name ${CONTAINER_DATA_NAME} \ -v /home/user-data \ - scratch /bin/does-not-exist-but-thats-ok + phusion/baseimage:0.9.16 || exit 1 else tput setaf 2 echo - echo "Using existing container mailinabox-userdata for your data." + echo "Using existing container ${CONTAINER_DATA_NAME} for your data." tput setaf 7 fi # End a running container. - -if docker ps -a | grep mailinabox-services > /dev/null; then +if docker inspect ${CONTAINER_NAME} > /dev/null; then tput setaf 2 echo - echo "Destroying mailinabox-services container..." + echo "Destroying ${CONTAINER_NAME} container..." tput setaf 7 - docker rm -f mailinabox-services + docker rm -f ${CONTAINER_NAME} fi # Start container. - tput setaf 2 echo -echo "Starting new container (mailinabox-services)..." +echo "Starting new container (${CONTAINER_NAME})..." tput setaf 7 +# Run the services container +# detached if NONINTERACTIVE is set, +# interactively if NONINTERACTIVE is not set, # Notes: # * Passing through SKIP_NETWORK_CHECKS makes it easier to do testing # on a residential network. - +# * --privileged flag cause an issue with bind9/named failing to start in this case +# see docker/docker#7318 docker run \ - --privileged \ -v /dev/urandom:/dev/random \ - -p 25 -p 53/udp -p 53/tcp -p 80 -p 443 -p 587 -p 993 \ - --name mailinabox-services \ - --volumes-from mailinabox-userdata \ + -p 25:25 \ + $([ -z "$NODNS" ] && echo "-p 53:53/udp -p 53:53/tcp") \ + -p $HOST_HTTP_PORT:80 \ + -p $HOST_HTTPS_PORT:443 \ + -p 587:587 \ + -p 993:993 \ + -p 4190:4190 \ + --name ${CONTAINER_NAME} \ + --volumes-from ${CONTAINER_DATA_NAME} \ + --restart always \ + $([ ! -z "$NONINTERACTIVE" ] && echo "-d") \ + -it \ + -e "IS_DOCKER=1" \ -e "SKIP_NETWORK_CHECKS=$SKIP_NETWORK_CHECKS" \ - mailinabox + mailinabox \ + || exit 1 + +if [ -z "$NONINTERACTIVE" ]; then + tput setaf 2 + echo + echo "Restarting container ${CONTAINER_NAME}..." + tput setaf 7 + + docker restart ${CONTAINER_NAME} || exit 1 +fi \ No newline at end of file diff --git a/containers/docker/runit/rsyslogd.sh b/containers/docker/runit/rsyslogd.sh deleted file mode 100755 index 497d38ad..00000000 --- a/containers/docker/runit/rsyslogd.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -rsyslogd -n diff --git a/containers/docker/tools/configure_services.sh b/containers/docker/tools/configure_services.sh new file mode 100755 index 00000000..3a070081 --- /dev/null +++ b/containers/docker/tools/configure_services.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# The phusion/baseimage base image we use for a working Ubuntu +# replaces the normal Upstart system service management with +# a ligher-weight service management system called runit that +# requires a different configuration. We need to create service +# run files that do not daemonize. + +# This removes /etc/init.d service if service exists in runit. +# It also creates a symlink from /usr/bin/sv to /etc/init.d/$service +# to support SysV syntax: service $service or /etc/init.d/$service +SERVICES=/etc/service/* +for f in $SERVICES +do + service=$(basename "$f") + if [ -d /etc/service/$service ]; then + if [ -f /etc/init.d/$service ]; then + mv /etc/init.d/$service /etc/init.d/$service.lsb + chmod -x /etc/init.d/$service.lsb + fi + ln -s /usr/bin/sv /etc/init.d/$service + fi +done + +# Create runit services from sysv services. For most of the services, +# there is a common pattern we can use: execute the init.d script that +# the Ubuntu package installs, and then poll for the termination of +# the daemon. +function make_runit_service { + INITD_NAME=$1 + WAIT_ON_PROCESS_NAME=$2 + mkdir -p /etc/service/$INITD_NAME + cat > /etc/service/$INITD_NAME/run < /etc/service/$service/log/run <