Merge pull request #2549 from mitchellh/f-docker-provisioner

docker provisioner
This commit is contained in:
Mitchell Hashimoto 2013-12-03 17:47:42 -08:00
commit 146bc34019
13 changed files with 463 additions and 0 deletions

View File

@ -0,0 +1,15 @@
module VagrantPlugins
module Docker
module Cap
module Debian
module DockerConfigureAutoStart
def self.docker_configure_auto_start(machine)
if ! machine.communicate.test('grep -q \'\-r=true\' /etc/init/docker.conf')
machine.communicate.sudo("sed -i.bak 's/docker -d/docker -d -r=true/' /etc/init/docker.conf ")
end
end
end
end
end
end
end

View File

@ -0,0 +1,13 @@
module VagrantPlugins
module Docker
module Cap
module Debian
module DockerConfigureVagrantUser
def self.docker_configure_vagrant_user(machine)
machine.communicate.sudo("usermod -a -G docker #{machine.config.ssh.username || "vagrant"}")
end
end
end
end
end
end

View File

@ -0,0 +1,26 @@
module VagrantPlugins
module Docker
module Cap
module Debian
module DockerInstall
def self.docker_install(machine, version)
package = 'lxc-docker'
package << "-#{version}" if version != :latest
machine.communicate.tap do |comm|
# TODO: Perform check on the host machine if aufs is installed and using LXC
if machine.provider_name != :lxc
comm.sudo("lsmod | grep aufs || modprobe aufs || apt-get install -y linux-image-extra-`uname -r`")
end
comm.sudo("apt-get install -y --force-yes -q curl")
comm.sudo("curl http://get.docker.io/gpg | apt-key add -")
comm.sudo("echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list")
comm.sudo("apt-get update")
comm.sudo("apt-get install -y --force-yes -q xz-utils #{package} -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'")
end
end
end
end
end
end
end

View File

@ -0,0 +1,13 @@
module VagrantPlugins
module Docker
module Cap
module Debian
module DockerStartService
def self.docker_start_service(machine)
machine.communicate.sudo("service docker start")
end
end
end
end
end
end

View File

@ -0,0 +1,13 @@
module VagrantPlugins
module Docker
module Cap
module Linux
module DockerInstalled
def self.docker_installed(machine)
machine.communicate.test("test -f /usr/bin/docker", sudo: true)
end
end
end
end
end
end

View File

@ -0,0 +1,43 @@
require 'set'
module VagrantPlugins
module Docker
class Config < Vagrant.plugin("2", :config)
attr_reader :images, :containers
attr_accessor :version
def initialize
@images = Set.new
@containers = Hash.new
@version = UNSET_VALUE
end
def images=(images)
@images = Set.new(images)
end
def pull_images(*images)
@images += images.map(&:to_s)
end
def run(name, **options)
params = options.dup
params[:image] = name
# TODO: Validate provided parameters before assignment
@containers[name.to_s] = params
end
def finalize!
@version = "latest" if @version == UNSET_VALUE
@version = @version.to_sym
end
def merge(other)
super.tap do |result|
result.pull_images(*(other.images + self.images))
end
end
end
end
end

View File

@ -0,0 +1,78 @@
require 'digest/sha1'
module VagrantPlugins
module Docker
class DockerClient
def initialize(machine)
@machine = machine
end
def pull_images(*images)
@machine.communicate.tap do |comm|
images.each do |image|
@machine.ui.info(I18n.t("vagrant.docker_pulling_single", name: image))
comm.sudo("docker images | grep -q #{image} || docker pull #{image}")
end
end
end
def start_service
if !daemon_running? && @machine.guest.capability?(:docker_start_service)
@machine.guest.capability(:docker_start_service)
end
end
def daemon_running?
@machine.communicate.test('test -f /var/run/docker.pid')
end
def run(containers)
containers.each do |name, config|
cids_dir = "/var/lib/vagrant/cids"
config[:cidfile] ||= "#{cids_dir}/#{Digest::SHA1.hexdigest name}"
@machine.ui.info(I18n.t("vagrant.docker_running", name: name))
@machine.communicate.sudo("mkdir -p #{cids_dir}")
run_container({
name: name
}.merge(config))
end
end
def run_container(config)
raise "Container's cidfile was not provided!" if !config[:cidfile]
id = "$(cat #{config[:cidfile]})"
if container_exist?(id)
start_container(id)
else
create_container(config)
end
end
def container_exist?(id)
@machine.communicate.test("sudo docker ps -a -q | grep -q #{id}")
end
def start_container(id)
if !container_running?(id)
@machine.communicate.sudo("docker start #{id}")
end
end
def container_running?(id)
@machine.communicate.test("sudo docker ps -q | grep #{id}")
end
def create_container(config)
args = "-cidfile=#{config[:cidfile]} -d "
args << config[:args] if config[:args]
@machine.communicate.sudo %[
rm -f #{config[:cidfile]}
docker run #{args} #{config[:image]} #{config[:cmd]}
]
end
end
end
end

View File

@ -0,0 +1,39 @@
module VagrantPlugins
module Docker
class DockerInstaller
def initialize(machine, version)
@machine = machine
@version = version
end
# This handles verifying the Docker installation, installing it if it was
# requested, and so on. This method will raise exceptions if things are
# wrong.
def ensure_installed
if !@machine.guest.capability?(:docker_installed)
@machine.ui.warn(I18n.t("vagrant.docker_cant_detect"))
return
end
if !@machine.guest.capability(:docker_installed)
@machine.ui.info(I18n.t("vagrant.docker_installing", version: @version.to_s))
@machine.guest.capability(:docker_install, @version)
if !@machine.guest.capability(:docker_installed)
raise DockerError, :install_failed
end
end
if @machine.guest.capability?(:docker_configure_auto_start)
@machine.guest.capability(:docker_configure_auto_start)
else
@machine.env.ui.warn I18n.t('vagrant.docker_auto_start_not_available')
end
if @machine.guest.capability?(:docker_configure_vagrant_user)
@machine.guest.capability(:docker_configure_vagrant_user)
end
end
end
end
end

View File

@ -0,0 +1,48 @@
require "vagrant"
module VagrantPlugins
module Docker
class Plugin < Vagrant.plugin("2")
name "docker"
description <<-DESC
Provides support for provisioning your virtual machines with
Docker images and containers.
DESC
config(:docker, :provisioner) do
require_relative "config"
Config
end
guest_capability("debian", "docker_install") do
require_relative "cap/debian/docker_install"
Cap::Debian::DockerInstall
end
guest_capability("debian", "docker_configure_auto_start") do
require_relative "cap/debian/docker_configure_auto_start"
Cap::Debian::DockerConfigureAutoStart
end
guest_capability("debian", "docker_configure_vagrant_user") do
require_relative "cap/debian/docker_configure_vagrant_user"
Cap::Debian::DockerConfigureVagrantUser
end
guest_capability("debian", "docker_start_service") do
require_relative "cap/debian/docker_start_service"
Cap::Debian::DockerStartService
end
guest_capability("linux", "docker_installed") do
require_relative "cap/linux/docker_installed"
Cap::Linux::DockerInstalled
end
provisioner(:docker) do
require_relative "provisioner"
Provisioner
end
end
end
end

View File

@ -0,0 +1,43 @@
require_relative "docker_client"
require_relative "docker_installer"
module VagrantPlugins
module Docker
class DockerError < Vagrant::Errors::VagrantError
error_namespace("vagrant.provisioners.docker")
end
# TODO: Improve handling of vagrant-lxc specifics (like checking for apparmor
# profile stuff + autocorrection)
class Provisioner < Vagrant.plugin("2", :provisioner)
def initialize(machine, config, installer = nil, client = nil)
super(machine, config)
# TODO: Rename to installer / client (drop docker suffix)
@installer = installer || DockerInstaller.new(@machine, config.version)
@client = client || DockerClient.new(@machine)
end
def provision
@logger = Log4r::Logger.new("vagrant::provisioners::docker")
@logger.info("Checking for Docker installation...")
@installer.ensure_installed
# Attempt to start service if not running
@client.start_service
raise DockerError, :not_running if !@client.daemon_running?
if config.images.any?
@machine.ui.info(I18n.t("vagrant.docker_pulling_images"))
@client.pull_images(*config.images)
end
if config.containers.any?
@machine.ui.info(I18n.t("vagrant.docker_starting_containers"))
@client.run(config.containers)
end
end
end
end
end

View File

@ -42,6 +42,21 @@ en:
to automatically delete Chef nodes and clients. to automatically delete Chef nodes and clients.
chef_run_list_empty: |- chef_run_list_empty: |-
Warning: Chef run list is empty. This may not be what you want. Warning: Chef run list is empty. This may not be what you want.
docker_installing: |-
Installing Docker (%{version}) onto machine...
docker_pulling_images:
Pulling Docker images...
docker_pulling_single: |-
-- Image: %{name}
docker_running: |-
-- Container: %{name}
docker_starting_containers:
Starting Docker containers...
docker_auto_start_not_available: |-
Unable to configure automatic restart of Docker containers on
the guest machine
provisioner_cleanup: |- provisioner_cleanup: |-
Running cleanup tasks for '%{name}' provisioner... Running cleanup tasks for '%{name}' provisioner...
@ -1317,3 +1332,7 @@ en:
playbook_path_invalid: "`playbook` for the Ansible provisioner does not exist on the host system: %{path}" playbook_path_invalid: "`playbook` for the Ansible provisioner does not exist on the host system: %{path}"
inventory_path_invalid: "`inventory_path` for the Ansible provisioner does not exist on the host system: %{path}" inventory_path_invalid: "`inventory_path` for the Ansible provisioner does not exist on the host system: %{path}"
extra_vars_invalid: "`extra_vars` for the Ansible provisioner must be a hash or a path to an existing file. Received: %{value} (as %{type})" extra_vars_invalid: "`extra_vars` for the Ansible provisioner must be a hash or a path to an existing file. Received: %{value} (as %{type})"
docker:
not_running: "Docker is not running on the guest VM."
install_failed: "Docker installation failed."

View File

@ -150,6 +150,7 @@
<li<%= sidebar_current("provisioning-ansible") %>><a href="/v2/provisioning/ansible.html">Ansible</a></li> <li<%= sidebar_current("provisioning-ansible") %>><a href="/v2/provisioning/ansible.html">Ansible</a></li>
<li<%= sidebar_current("provisioning-chefsolo") %>><a href="/v2/provisioning/chef_solo.html">Chef Solo</a></li> <li<%= sidebar_current("provisioning-chefsolo") %>><a href="/v2/provisioning/chef_solo.html">Chef Solo</a></li>
<li<%= sidebar_current("provisioning-chefclient") %>><a href="/v2/provisioning/chef_client.html">Chef Client</a></li> <li<%= sidebar_current("provisioning-chefclient") %>><a href="/v2/provisioning/chef_client.html">Chef Client</a></li>
<li<%= sidebar_current("provisioning-docker") %>><a href="/v2/provisioning/docker.html">Docker</a></li>
<li<%= sidebar_current("provisioning-puppetapply") %>><a href="/v2/provisioning/puppet_apply.html">Puppet Apply</a></li> <li<%= sidebar_current("provisioning-puppetapply") %>><a href="/v2/provisioning/puppet_apply.html">Puppet Apply</a></li>
<li<%= sidebar_current("provisioning-puppetagent") %>><a href="/v2/provisioning/puppet_agent.html">Puppet Agent</a></li> <li<%= sidebar_current("provisioning-puppetagent") %>><a href="/v2/provisioning/puppet_agent.html">Puppet Agent</a></li>
<li<%= sidebar_current("provisioning-salt") %>><a href="/v2/provisioning/salt.html">Salt</a></li> <li<%= sidebar_current("provisioning-salt") %>><a href="/v2/provisioning/salt.html">Salt</a></li>

View File

@ -0,0 +1,112 @@
---
page_title: "Docker - Provisioning"
sidebar_current: "provisioning-docker"
---
# Docker Provisioner
**Provisioner name: `"docker"`**
The docker provisioner can automatically install [Docker](http://www.docker.io),
pull Docker containers, and configure certain containers to run on boot.
The docker provisioner is ideal for organizations that are using
Docker as a means to distribute things like their application or services.
Or, if you're just getting started with Docker, the Docker provisioner
provides the easiest possible way to begin using Docker since the provisioner
automates installing Docker for you.
As with all provisioners, the Docker provisioner can be used along with
all the other provisioners Vagrant has in order to setup your working
environment the best way possible. For example, perhaps you use Puppet to
install services like databases or web servers but use Docker to house
your application runtime. You can use the Puppet provisioner along
with the Docker provisioner.
## Options
The docker provisioner takes various options. None are required. If
no options are required, the Docker provisioner will only install Docker
for you (if it isn't already installed).
* `images` (array) - A list of images to pull using `docker pull`. You
can also use the `pull_images` function. See the example below this
section for more information.
* `version` (string) - The version of Docker to install. This defaults to
"latest" and will install the latest version of Docker.
## Pulling Images
The docker provisioner can automatically pull images from the
Docker registry for you. There are two ways to specify images to
pull. The first is as an array using `images`:
```ruby
Vagrant.configure("2") do |config|
config.vm.provision "docker",
images: ["ubuntu"]
end
```
This will cause Vagrant to pull the "ubuntu" image from the registry
for you automatically.
The second way to pull images is to use the `pull_images` function.
Each call to `pull_images` will _append_ the images to be pulled. The
`images` variable, on the other hand, can only be used once.
Additionally, the `pull_images` function cannot be used with the
simple configuration method for provisioners (specifying it all in one line).
```ruby
Vagrant.configure("2") do |config|
config.vm.provision "docker" do |d|
d.pull_images "ubuntu"
d.pull_images "vagrant"
end
end
```
## Running Containers
In addition to pulling images, the Docker provisioner can run and start
containers for you. This lets you automatically start services as part of
`vagrant up`.
Running containers can only be configured using the Ruby block syntax with
the `do...end` blocks. An example of running a container is shown below:
```ruby
Vagrant.configure("2") do |config|
config.vm.provision "docker" do |d|
d.run "rabbitmq"
end
end
```
This will `docker run` a container with the "rabbitmq" image. In addition
to the name, the `run` method accepts a set of options, all optional:
* `image` (string) - The image to run. This defaults to the first argument
but can also be given here as an option.
* `cmd` (string) - The command to start within the container. If not specified,
then the containers default "run" command will be used, such as the
"run" command specified when the container was built.
* `args` (string) - Extra arguments for `docker run` on the command line.
These are raw arguments that are passed directly to Docker.
For example, here is how you would configure Docker to run a container
with the Vagrant shared directory mounted inside of it:
```ruby
Vagrant.configure("2") do |config|
config.vm.provision "docker" do |d|
d.run "ubuntu",
cmd: "bash -l",
args: "-v '/vagrant:/var/www'"
end
end
```