From 2aa2a89cdb9071cea919116e283c16ac9dd841d6 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Sun, 4 Aug 2013 19:53:19 -0500 Subject: [PATCH] Add support for Docker as Nova hypervisor * Add basic support for hypervisor plugins in lib/nova_plugins * Add lib/nova_plugins/hypervisor-docker to use Docker as a Nova hypervisor. * Add tools/install_docker.sh to install the Docker daemon and registry container, download base image and import * Configure Nova to use docker plugin * Add docker exercise and skip unsupported ones Nova blueprint: new-hypervisor-docker Change-Id: I9e7065b562dce2ce853def583ab1165886612227 --- README.md | 6 +- clean.sh | 5 ++ exercises/boot_from_volume.sh | 3 + exercises/docker.sh | 105 +++++++++++++++++++++++ exercises/euca.sh | 3 + exercises/floating_ips.sh | 3 + exercises/sec_groups.sh | 3 + exercises/volumes.sh | 3 + lib/nova | 17 +++- lib/nova_plugins/hypervisor-docker | 132 +++++++++++++++++++++++++++++ stack.sh | 18 +++- tools/docker/README.md | 13 +++ tools/docker/install_docker.sh | 75 ++++++++++++++++ unstack.sh | 8 ++ 14 files changed, 391 insertions(+), 3 deletions(-) create mode 100755 exercises/docker.sh create mode 100644 lib/nova_plugins/hypervisor-docker create mode 100644 tools/docker/README.md create mode 100755 tools/docker/install_docker.sh diff --git a/README.md b/README.md index 6426e9a4ed..99e983887e 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,14 @@ Read more at http://devstack.org (built from the gh-pages branch) IMPORTANT: Be sure to carefully read `stack.sh` and any other scripts you execute before you run them, as they install software and may alter your networking configuration. We strongly recommend that you run `stack.sh` in a clean and disposable vm when you are first getting started. -# Devstack on Xenserver +# DevStack on Xenserver If you would like to use Xenserver as the hypervisor, please refer to the instructions in `./tools/xen/README.md`. +# DevStack on Docker + +If you would like to use Docker as the hypervisor, please refer to the instructions in `./tools/docker/README.md`. + # Versions The devstack master branch generally points to trunk versions of OpenStack components. For older, stable versions, look for branches named stable/[release] in the DevStack repo. For example, you can do the following to create a diablo OpenStack cloud: diff --git a/clean.sh b/clean.sh index f7d15dfe4e..a443ac82d0 100755 --- a/clean.sh +++ b/clean.sh @@ -64,6 +64,11 @@ cleanup_nova cleanup_neutron cleanup_swift +# Do the hypervisor cleanup until this can be moved back into lib/nova +if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + cleanup_nova_hypervisor +fi + # cinder doesn't always clean up the volume group as it might be used elsewhere... # clean it up if it is a loop device VG_DEV=$(sudo losetup -j $DATA_DIR/${VOLUME_GROUP}-backing-file | awk -F':' '/backing-file/ { print $1}') diff --git a/exercises/boot_from_volume.sh b/exercises/boot_from_volume.sh index 36524ede4b..fe27bd0956 100755 --- a/exercises/boot_from_volume.sh +++ b/exercises/boot_from_volume.sh @@ -44,6 +44,9 @@ source $TOP_DIR/exerciserc # the exercise is skipped is_service_enabled cinder || exit 55 +# Also skip if the hypervisor is Docker +[[ "$VIRT_DRIVER" == "docker" ]] && exit 55 + # Instance type to create DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny} diff --git a/exercises/docker.sh b/exercises/docker.sh new file mode 100755 index 0000000000..0672bc0087 --- /dev/null +++ b/exercises/docker.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# **docker** + +# Test Docker hypervisor + +echo "*********************************************************************" +echo "Begin DevStack Exercise: $0" +echo "*********************************************************************" + +# This script exits on an error so that errors don't compound and you see +# only the first error that occurred. +set -o errexit + +# Print the commands being run so that we can see the command that triggers +# an error. It is also useful for following allowing as the install occurs. +set -o xtrace + + +# Settings +# ======== + +# Keep track of the current directory +EXERCISE_DIR=$(cd $(dirname "$0") && pwd) +TOP_DIR=$(cd $EXERCISE_DIR/..; pwd) + +# Import common functions +source $TOP_DIR/functions + +# Import configuration +source $TOP_DIR/openrc + +# Import exercise configuration +source $TOP_DIR/exerciserc + +# Skip if the hypervisor is not Docker +[[ "$VIRT_DRIVER" == "docker" ]] || exit 55 + +# Import docker functions and declarations +source $TOP_DIR/lib/nova_plugins/hypervisor-docker + +# Image and flavor are ignored but the CLI requires them... + +# Instance type to create +DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny} + +# Boot this image, use first AMI image if unset +DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ami} + +# Instance name +VM_NAME=ex-docker + + +# Launching a server +# ================== + +# Grab the id of the image to launch +IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME:latest " | get_field 1) +die_if_not_set $LINENO IMAGE "Failure getting image $DOCKER_IMAGE_NAME" + +# Select a flavor +INSTANCE_TYPE=$(nova flavor-list | grep $DEFAULT_INSTANCE_TYPE | get_field 1) +if [[ -z "$INSTANCE_TYPE" ]]; then + # grab the first flavor in the list to launch if default doesn't exist + INSTANCE_TYPE=$(nova flavor-list | head -n 4 | tail -n 1 | get_field 1) +fi + +# Clean-up from previous runs +nova delete $VM_NAME || true +if ! timeout $ACTIVE_TIMEOUT sh -c "while nova show $VM_NAME; do sleep 1; done"; then + die $LINENO "server didn't terminate!" +fi + +# Boot instance +# ------------- + +VM_UUID=$(nova boot --flavor $INSTANCE_TYPE --image $IMAGE $VM_NAME | grep ' id ' | get_field 2) +die_if_not_set $LINENO VM_UUID "Failure launching $VM_NAME" + +# Check that the status is active within ACTIVE_TIMEOUT seconds +if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then + die $LINENO "server didn't become active!" +fi + +# Get the instance IP +IP=$(nova show $VM_UUID | grep "$PRIVATE_NETWORK_NAME" | get_field 2) +die_if_not_set $LINENO IP "Failure retrieving IP address" + +# Private IPs can be pinged in single node deployments +ping_check "$PRIVATE_NETWORK_NAME" $IP $BOOT_TIMEOUT + +# Clean up +# -------- + +# Delete instance +nova delete $VM_UUID || die $LINENO "Failure deleting instance $VM_NAME" +if ! timeout $TERMINATE_TIMEOUT sh -c "while nova list | grep -q $VM_UUID; do sleep 1; done"; then + die $LINENO "Server $VM_NAME not deleted" +fi + +set +o xtrace +echo "*********************************************************************" +echo "SUCCESS: End DevStack Exercise: $0" +echo "*********************************************************************" + diff --git a/exercises/euca.sh b/exercises/euca.sh index b8b283a8fb..64c0014236 100755 --- a/exercises/euca.sh +++ b/exercises/euca.sh @@ -41,6 +41,9 @@ fi # Import exercise configuration source $TOP_DIR/exerciserc +# Skip if the hypervisor is Docker +[[ "$VIRT_DRIVER" == "docker" ]] && exit 55 + # Instance type to create DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny} diff --git a/exercises/floating_ips.sh b/exercises/floating_ips.sh index f93a727df6..2833b650ba 100755 --- a/exercises/floating_ips.sh +++ b/exercises/floating_ips.sh @@ -38,6 +38,9 @@ fi # Import exercise configuration source $TOP_DIR/exerciserc +# Skip if the hypervisor is Docker +[[ "$VIRT_DRIVER" == "docker" ]] && exit 55 + # Instance type to create DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny} diff --git a/exercises/sec_groups.sh b/exercises/sec_groups.sh index 6b67291cde..7d80570326 100755 --- a/exercises/sec_groups.sh +++ b/exercises/sec_groups.sh @@ -33,6 +33,9 @@ source $TOP_DIR/openrc # Import exercise configuration source $TOP_DIR/exerciserc +# Skip if the hypervisor is Docker +[[ "$VIRT_DRIVER" == "docker" ]] && exit 55 + # Testing Security Groups # ======================= diff --git a/exercises/volumes.sh b/exercises/volumes.sh index 028d19b36a..e536d16249 100755 --- a/exercises/volumes.sh +++ b/exercises/volumes.sh @@ -42,6 +42,9 @@ source $TOP_DIR/exerciserc # exercise is skipped. is_service_enabled cinder || exit 55 +# Also skip if the hypervisor is Docker +[[ "$VIRT_DRIVER" == "docker" ]] && exit 55 + # Instance type to create DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny} diff --git a/lib/nova b/lib/nova index 842c098624..3486aa8fb9 100644 --- a/lib/nova +++ b/lib/nova @@ -169,6 +169,13 @@ function cleanup_nova() { fi sudo rm -rf $NOVA_STATE_PATH $NOVA_AUTH_CACHE_DIR + + # NOTE(dtroyer): This really should be called from here but due to the way + # nova abuses the _cleanup() function we're moving it + # directly into cleanup.sh until this can be fixed. + #if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + # cleanup_nova_hypervisor + #fi } # configure_nova_rootwrap() - configure Nova's rootwrap @@ -650,7 +657,9 @@ function install_novaclient() { # install_nova() - Collect source and prepare function install_nova() { if is_service_enabled n-cpu; then - if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then + if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + install_nova_hypervisor + elif [[ "$VIRT_DRIVER" = 'libvirt' ]]; then if is_ubuntu; then install_package kvm install_package libvirt-bin @@ -728,6 +737,9 @@ function start_nova() { screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM" done else + if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + start_nova_hypervisor + fi screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM" fi screen_it n-crt "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-cert" @@ -754,6 +766,9 @@ function stop_nova() { for serv in n-api n-cpu n-crt n-net n-sch n-novnc n-xvnc n-cauth n-spice n-cond n-cond n-cell n-cell n-api-meta; do screen -S $SCREEN_NAME -p $serv -X kill done + if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + stop_nova_hypervisor + fi } diff --git a/lib/nova_plugins/hypervisor-docker b/lib/nova_plugins/hypervisor-docker new file mode 100644 index 0000000000..4c8fc279b0 --- /dev/null +++ b/lib/nova_plugins/hypervisor-docker @@ -0,0 +1,132 @@ +# lib/nova_plugins/docker +# Configure the Docker hypervisor + +# Enable with: +# VIRT_DRIVER=docker + +# Dependencies: +# ``functions`` file +# ``nova`` and ``glance`` configurations + +# install_nova_hypervisor - install any external requirements +# configure_nova_hypervisor - make configuration changes, including those to other services +# start_nova_hypervisor - start any external services +# stop_nova_hypervisor - stop any external services +# cleanup_nova_hypervisor - remove transient data and cache + +# Save trace setting +MY_XTRACE=$(set +o | grep xtrace) +set +o xtrace + + +# Defaults +# -------- + +# Set up default directories +DOCKER_DIR=$DEST/docker +DOCKER_REPO=${DOCKER_REPO:-https://github.com/dotcloud/openstack-docker.git} +DOCKER_BRANCH=${DOCKER_BRANCH:-master} + +DOCKER_UNIX_SOCKET=/var/run/docker.sock +DOCKER_PID_FILE=/var/run/docker.pid +DOCKER_REGISTRY_PORT=${DOCKER_REGISTRY_PORT:-5042} + +DOCKER_IMAGE=${DOCKER_IMAGE:-http://get.docker.io/images/openstack/docker-ut.tar.gz} +DOCKER_IMAGE_NAME=docker-busybox +DOCKER_REGISTRY_IMAGE=${DOCKER_REGISTRY_IMAGE:-http://get.docker.io/images/openstack/docker-registry.tar.gz} +DOCKER_REGISTRY_IMAGE_NAME=docker-registry +DOCKER_REPOSITORY_NAME=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT}/${DOCKER_IMAGE_NAME} + +DOCKER_PACKAGE_VERSION=${DOCKER_PACKAGE_VERSION:-0.6.1} +DOCKER_APT_REPO=${DOCKER_APT_REPO:-https://get.docker.io/ubuntu} + + +# Entry Points +# ------------ + +# clean_nova_hypervisor - Clean up an installation +function cleanup_nova_hypervisor() { + stop_service docker + + # Clean out work area + sudo rm -rf /var/lib/docker +} + +# configure_nova_hypervisor - Set config files, create data dirs, etc +function configure_nova_hypervisor() { + git_clone $DOCKER_REPO $DOCKER_DIR $DOCKER_BRANCH + + ln -snf ${DOCKER_DIR}/nova-driver $NOVA_DIR/nova/virt/docker + + iniset $NOVA_CONF DEFAULT compute_driver docker.DockerDriver + iniset $GLANCE_API_CONF DEFAULT container_formats ami,ari,aki,bare,ovf,docker + + sudo cp -p ${DOCKER_DIR}/nova-driver/docker.filters $NOVA_CONF_DIR/rootwrap.d +} + +# install_nova_hypervisor() - Install external components +function install_nova_hypervisor() { + # So far this is Ubuntu only + if ! is_ubuntu; then + die $LINENO "Docker is only supported on Ubuntu at this time" + fi + + # Make sure Docker is installed + if ! is_package_installed lxc-docker; then + die $LINENO "Docker is not installed. Please run tools/docker/install_docker.sh" + fi + + local docker_pid + read docker_pid <$DOCKER_PID_FILE + if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then + die $LINENO "Docker not running" + fi +} + +# start_nova_hypervisor - Start any required external services +function start_nova_hypervisor() { + local docker_pid + read docker_pid <$DOCKER_PID_FILE + if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then + die $LINENO "Docker not running, start the daemon" + fi + + # Start the Docker registry container + docker run -d -p ${DOCKER_REGISTRY_PORT}:5000 \ + -e SETTINGS_FLAVOR=openstack -e OS_USERNAME=${OS_USERNAME} \ + -e OS_PASSWORD=${OS_PASSWORD} -e OS_TENANT_NAME=${OS_TENANT_NAME} \ + -e OS_GLANCE_URL="${SERVICE_PROTOCOL}://${GLANCE_HOSTPORT}" \ + -e OS_AUTH_URL=${OS_AUTH_URL} \ + $DOCKER_REGISTRY_IMAGE_NAME ./docker-registry/run.sh + + echo "Waiting for docker registry to start..." + DOCKER_REGISTRY=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT} + if ! timeout $SERVICE_TIMEOUT sh -c "while ! curl -s $DOCKER_REGISTRY; do sleep 1; done"; then + die $LINENO "docker-registry did not start" + fi + + # Tag image if not already tagged + if ! docker images | grep $DOCKER_REPOSITORY_NAME; then + docker tag $DOCKER_IMAGE_NAME $DOCKER_REPOSITORY_NAME + fi + + # Make sure we copied the image in Glance + DOCKER_IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME ") + if ! is_set DOCKER_IMAGE ; then + docker push $DOCKER_REPOSITORY_NAME + fi +} + +# stop_nova_hypervisor - Stop any external services +function stop_nova_hypervisor() { + # Stop the docker registry container + docker kill $(docker ps | grep docker-registry | cut -d' ' -f1) +} + + +# Restore xtrace +$MY_XTRACE + +# Local variables: +# mode: shell-script +# End: diff --git a/stack.sh b/stack.sh index 8f59328792..c3f69adcf1 100755 --- a/stack.sh +++ b/stack.sh @@ -319,6 +319,13 @@ source $TOP_DIR/lib/neutron source $TOP_DIR/lib/baremetal source $TOP_DIR/lib/ldap +# Look for Nova hypervisor plugin +NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins +if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + # Load plugin + source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER +fi + # Set the destination directories for other OpenStack projects OPENSTACKCLIENT_DIR=$DEST/python-openstackclient @@ -1013,6 +1020,10 @@ if is_service_enabled cinder; then init_cinder fi + +# Compute Service +# --------------- + if is_service_enabled nova; then echo_summary "Configuring Nova" # Rebuild the config file from scratch @@ -1027,10 +1038,15 @@ if is_service_enabled nova; then fi + if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + # Configure hypervisor plugin + configure_nova_hypervisor + + # XenServer # --------- - if [ "$VIRT_DRIVER" = 'xenserver' ]; then + elif [ "$VIRT_DRIVER" = 'xenserver' ]; then echo_summary "Using XenServer virtualization driver" if [ -z "$XENAPI_CONNECTION_URL" ]; then die $LINENO "XENAPI_CONNECTION_URL is not specified" diff --git a/tools/docker/README.md b/tools/docker/README.md new file mode 100644 index 0000000000..976111f750 --- /dev/null +++ b/tools/docker/README.md @@ -0,0 +1,13 @@ +# DevStack on Docker + +Using Docker as Nova's hypervisor requries two steps: + +* Configure DevStack by adding the following to `localrc`:: + + VIRT_DRIVER=docker + +* Download and install the Docker service and images:: + + tools/docker/install_docker.sh + +After this, `stack.sh` should run as normal. diff --git a/tools/docker/install_docker.sh b/tools/docker/install_docker.sh new file mode 100755 index 0000000000..d659ad104b --- /dev/null +++ b/tools/docker/install_docker.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# **install_docker.sh** - Do the initial Docker installation and configuration + +# install_docker.sh +# +# Install docker package and images +# * downloads a base busybox image and a glance registry image if necessary +# * install the images in Docker's image cache + + +# Keep track of the current directory +SCRIPT_DIR=$(cd $(dirname "$0") && pwd) +TOP_DIR=$(cd $SCRIPT_DIR/../..; pwd) + +# Import common functions +source $TOP_DIR/functions + +# Load local configuration +source $TOP_DIR/stackrc + +FILES=$TOP_DIR/files + +# Get our defaults +source $TOP_DIR/lib/nova_plugins/hypervisor-docker + +SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60} + + +# Install Docker Service +# ====================== + +# Stop the auto-repo updates and do it when required here +NO_UPDATE_REPOS=True + +# Set up home repo +curl https://get.docker.io/gpg | sudo apt-key add - +install_package python-software-properties && \ + sudo sh -c "echo deb $DOCKER_APT_REPO docker main > /etc/apt/sources.list.d/docker.list" +apt_get update +install_package --force-yes lxc-docker=${DOCKER_PACKAGE_VERSION} + +# Start the daemon - restart just in case the package ever auto-starts... +restart_service docker + +echo "Waiting for docker daemon to start..." +DOCKER_GROUP=$(groups | cut -d' ' -f1) +CONFIGURE_CMD="while ! /bin/echo -e 'GET /v1.3/version HTTP/1.0\n\n' | socat - unix-connect:$DOCKER_UNIX_SOCKET | grep -q '200 OK'; do + # Set the right group on docker unix socket before retrying + sudo chgrp $DOCKER_GROUP $DOCKER_UNIX_SOCKET + sudo chmod g+rw $DOCKER_UNIX_SOCKET + sleep 1 +done" +if ! timeout $SERVICE_TIMEOUT sh -c "$CONFIGURE_CMD"; then + die $LINENO "docker did not start" +fi + + +# Get Docker image +if [[ ! -r $FILES/docker-ut.tar.gz ]]; then + (cd $FILES; curl -OR $DOCKER_IMAGE) +fi +if [[ ! -r $FILES/docker-ut.tar.gz ]]; then + die $LINENO "Docker image unavailable" +fi +docker import - $DOCKER_IMAGE_NAME <$FILES/docker-ut.tar.gz + +# Get Docker registry image +if [[ ! -r $FILES/docker-registry.tar.gz ]]; then + (cd $FILES; curl -OR $DOCKER_REGISTRY_IMAGE) +fi +if [[ ! -r $FILES/docker-registry.tar.gz ]]; then + die $LINENO "Docker registry image unavailable" +fi +docker import - $DOCKER_REGISTRY_IMAGE_NAME <$FILES/docker-registry.tar.gz diff --git a/unstack.sh b/unstack.sh index 2268b90458..f053bcddd8 100755 --- a/unstack.sh +++ b/unstack.sh @@ -65,6 +65,14 @@ if [[ -n "$SCREEN" ]]; then fi fi +# Shut down Nova hypervisor plugins after Nova +NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins +if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then + # Load plugin + source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER + stop_nova_hypervisor +fi + # Swift runs daemons if is_service_enabled s-proxy; then stop_swift