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
This commit is contained in:
Dean Troyer 2013-08-04 19:53:19 -05:00
parent 415360b231
commit 2aa2a89cdb
14 changed files with 391 additions and 3 deletions

View File

@ -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:

View File

@ -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}')

View File

@ -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}

105
exercises/docker.sh Executable file
View File

@ -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 "*********************************************************************"

View File

@ -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}

View File

@ -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}

View File

@ -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
# =======================

View File

@ -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}

View File

@ -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
}

View File

@ -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:

View File

@ -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"

13
tools/docker/README.md Normal file
View File

@ -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.

75
tools/docker/install_docker.sh Executable file
View File

@ -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

View File

@ -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