Merge branch 'master' into load-profile

Conflicts:
	daemon/execdriver/native/create.go
	daemon/execdriver/native/driver.go

Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
This commit is contained in:
Guillaume J. Charmes 2014-04-21 10:31:51 -07:00
commit 813cebc64f
No known key found for this signature in database
GPG key ID: B33E4642CB6E3FF3
277 changed files with 50785 additions and 2480 deletions

View file

@ -82,6 +82,9 @@ RUN go get code.google.com/p/go.tools/cmd/cover
# TODO replace FPM with some very minimal debhelper stuff
RUN gem install --no-rdoc --no-ri fpm --version 1.0.2
# Get the "busybox" image source so we can build locally instead of pulling
RUN git clone https://github.com/jpetazzo/docker-busybox.git /docker-busybox
# Setup s3cmd config
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg

View file

@ -1,4 +1,4 @@
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-integration test-integration-cli
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-integration test-integration-cli validate
# to allow `make BINDDIR=. shell` or `make BINDDIR= test`
BINDDIR := bundles
@ -11,7 +11,8 @@ DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
DOCKER_RUN_DOCS := docker run --rm -it -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)"
# to allow `make DOCSDIR=docs docs-shell`
DOCKER_RUN_DOCS := docker run --rm -it -p $(if $(DOCSPORT),$(DOCSPORT):)8000 $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -e AWS_S3_BUCKET
default: binary
@ -25,10 +26,13 @@ cross: build
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
docs: docs-build
$(DOCKER_RUN_DOCS)
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" mkdocs serve
docs-shell: docs-build
$(DOCKER_RUN_DOCS) bash
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" bash
docs-release: docs-build
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./release.sh
test: build
$(DOCKER_RUN_DOCKER) hack/make.sh binary test test-integration test-integration-cli
@ -39,6 +43,9 @@ test-integration: build
test-integration-cli: build
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
validate: build
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
shell: build
$(DOCKER_RUN_DOCKER) bash

View file

@ -2,8 +2,8 @@ package builtins
import (
api "github.com/dotcloud/docker/api/server"
"github.com/dotcloud/docker/daemon/networkdriver/bridge"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/runtime/networkdriver/bridge"
"github.com/dotcloud/docker/server"
)

View file

@ -3,7 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/dotcloud/docker/runtime/graphdriver/devmapper"
"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
"os"
"path"
"sort"

82
contrib/mkimage-alpine.sh Executable file
View file

@ -0,0 +1,82 @@
#!/bin/sh
set -e
[ $(id -u) -eq 0 ] || {
printf >&2 '%s requires root\n' "$0"
exit 1
}
usage() {
printf >&2 '%s: [-r release] [-m mirror] [-s]\n' "$0"
exit 1
}
tmp() {
TMP=$(mktemp -d /tmp/alpine-docker-XXXXXXXXXX)
ROOTFS=$(mktemp -d /tmp/alpine-docker-rootfs-XXXXXXXXXX)
trap "rm -rf $TMP $ROOTFS" EXIT TERM INT
}
apkv() {
curl -s $REPO/$ARCH/APKINDEX.tar.gz | tar -Oxz |
grep '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2
}
getapk() {
curl -s $REPO/$ARCH/apk-tools-static-$(apkv).apk |
tar -xz -C $TMP sbin/apk.static
}
mkbase() {
$TMP/sbin/apk.static --repository $REPO --update-cache --allow-untrusted \
--root $ROOTFS --initdb add alpine-base
}
conf() {
printf '%s\n' $REPO > $ROOTFS/etc/apk/repositories
}
pack() {
local id
id=$(tar --numeric-owner -C $ROOTFS -c . | docker import - alpine:$REL)
docker tag $id alpine:latest
docker run -i -t alpine printf 'alpine:%s with id=%s created!\n' $REL $id
}
save() {
[ $SAVE -eq 1 ] || return
tar --numeric-owner -C $ROOTFS -c . | xz > rootfs.tar.xz
}
while getopts "hr:m:s" opt; do
case $opt in
r)
REL=$OPTARG
;;
m)
MIRROR=$OPTARG
;;
s)
SAVE=1
;;
*)
usage
;;
esac
done
REL=${REL:-edge}
MIRROR=${MIRROR:-http://nl.alpinelinux.org/alpine}
SAVE=${SAVE:-0}
REPO=$MIRROR/$REL/main
ARCH=$(uname -m)
tmp
getapk
mkbase
conf
pack
save

View file

@ -57,6 +57,7 @@ mknod -m 666 $DEV/tty0 c 4 0
mknod -m 666 $DEV/full c 1 7
mknod -m 600 $DEV/initctl p
mknod -m 666 $DEV/ptmx c 5 2
ln -sf /proc/self/fd $DEV/fd
tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
docker run -i -t archlinux echo Success.

View file

@ -1,17 +1,17 @@
package runtime
package daemon
import (
"encoding/json"
"errors"
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/image"
"github.com/dotcloud/docker/links"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -64,7 +64,7 @@ type Container struct {
stdin io.ReadCloser
stdinPipe io.WriteCloser
runtime *Runtime
daemon *Daemon
waitLock chan struct{}
Volumes map[string]string
@ -76,42 +76,6 @@ type Container struct {
activeLinks map[string]*links.Link
}
// FIXME: move deprecated port stuff to nat to clean up the core.
type PortMapping map[string]string // Deprecated
type NetworkSettings struct {
IPAddress string
IPPrefixLen int
Gateway string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
}
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
var outs = engine.NewTable("", 0)
for port, bindings := range settings.Ports {
p, _ := nat.ParsePort(port.Port())
if len(bindings) == 0 {
out := &engine.Env{}
out.SetInt("PublicPort", p)
out.Set("Type", port.Proto())
outs.Add(out)
continue
}
for _, binding := range bindings {
out := &engine.Env{}
h, _ := nat.ParsePort(binding.HostPort)
out.SetInt("PrivatePort", p)
out.SetInt("PublicPort", h)
out.Set("Type", port.Proto())
out.Set("IP", binding.HostIp)
outs.Add(out)
}
}
return outs
}
// Inject the io.Reader at the given path. Note: do not close the reader
func (container *Container) Inject(file io.Reader, pth string) error {
if err := container.Mount(); err != nil {
@ -148,10 +112,6 @@ func (container *Container) Inject(file io.Reader, pth string) error {
return nil
}
func (container *Container) When() time.Time {
return container.Created
}
func (container *Container) FromDisk() error {
data, err := ioutil.ReadFile(container.jsonPath())
if err != nil {
@ -358,14 +318,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
})
}
func populateCommand(c *Container) {
func populateCommand(c *Container, env []string) {
var (
en *execdriver.Network
driverConfig = make(map[string][]string)
)
en = &execdriver.Network{
Mtu: c.runtime.config.Mtu,
Mtu: c.daemon.config.Mtu,
Interface: nil,
}
@ -402,18 +362,7 @@ func populateCommand(c *Container) {
Resources: resources,
}
c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
}
func (container *Container) ArgsAsString() string {
var args []string
for _, arg := range container.Args {
if strings.Contains(arg, " ") {
args = append(args, fmt.Sprintf("'%s'", arg))
} else {
args = append(args, arg)
}
}
return strings.Join(args, " ")
c.command.Env = env
}
func (container *Container) Start() (err error) {
@ -423,186 +372,50 @@ func (container *Container) Start() (err error) {
if container.State.IsRunning() {
return nil
}
// if we encounter and error during start we need to ensure that any other
// setup has been cleaned up properly
defer func() {
if err != nil {
container.cleanup()
}
}()
if container.ResolvConfPath == "" {
if err := container.setupContainerDns(); err != nil {
return err
}
if err := container.setupContainerDns(); err != nil {
return err
}
if err := container.Mount(); err != nil {
return err
}
if container.runtime.config.DisableNetwork {
container.Config.NetworkDisabled = true
container.buildHostnameAndHostsFiles("127.0.1.1")
} else {
if err := container.allocateNetwork(); err != nil {
return err
}
container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
if err := container.initializeNetworking(); err != nil {
return err
}
// Make sure the config is compatible with the current kernel
if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
container.Config.Memory = 0
}
if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
container.Config.MemorySwap = -1
}
if container.runtime.sysInfo.IPv4ForwardingDisabled {
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
}
container.verifyDaemonSettings()
if err := prepareVolumesForContainer(container); err != nil {
return err
}
// Setup environment
env := []string{
"HOME=/",
"PATH=" + DefaultPathEnv,
"HOSTNAME=" + container.Config.Hostname,
}
if container.Config.Tty {
env = append(env, "TERM=xterm")
}
// Init any links between the parent and children
runtime := container.runtime
children, err := runtime.Children(container.Name)
linkedEnv, err := container.setupLinkedContainers()
if err != nil {
return err
}
if len(children) > 0 {
container.activeLinks = make(map[string]*links.Link, len(children))
// If we encounter an error make sure that we rollback any network
// config and ip table changes
rollback := func() {
for _, link := range container.activeLinks {
link.Disable()
}
container.activeLinks = nil
}
for linkAlias, child := range children {
if !child.State.IsRunning() {
return fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
}
link, err := links.NewLink(
container.NetworkSettings.IPAddress,
child.NetworkSettings.IPAddress,
linkAlias,
child.Config.Env,
child.Config.ExposedPorts,
runtime.eng)
if err != nil {
rollback()
return err
}
container.activeLinks[link.Alias()] = link
if err := link.Enable(); err != nil {
rollback()
return err
}
for _, envVar := range link.ToEnv() {
env = append(env, envVar)
}
}
}
// because the env on the container can override certain default values
// we need to replace the 'env' keys where they match and append anything
// else.
env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
env := container.createDaemonEnvironment(linkedEnv)
// TODO: This is only needed for lxc so we should look for a way to
// remove this dep
if err := container.generateEnvConfig(env); err != nil {
return err
}
if container.Config.WorkingDir != "" {
container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
pthInfo, err := os.Stat(path.Join(container.basefs, container.Config.WorkingDir))
if err != nil {
if !os.IsNotExist(err) {
return err
}
if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
return err
}
}
if pthInfo != nil && !pthInfo.IsDir() {
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
}
}
envPath, err := container.EnvConfigPath()
if err != nil {
if err := container.setupWorkingDirectory(); err != nil {
return err
}
populateCommand(container)
container.command.Env = env
if err := setupMountsForContainer(container, envPath); err != nil {
populateCommand(container, env)
if err := setupMountsForContainer(container); err != nil {
return err
}
// Setup logging of stdout and stderr to disk
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
return err
}
if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
if err := container.startLoggingToDisk(); err != nil {
return err
}
container.waitLock = make(chan struct{})
callbackLock := make(chan struct{})
callback := func(command *execdriver.Command) {
container.State.SetRunning(command.Pid())
if command.Tty {
// The callback is called after the process Start()
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
// which we close here.
if c, ok := command.Stdout.(io.Closer); ok {
c.Close()
}
}
if err := container.ToDisk(); err != nil {
utils.Debugf("%s", err)
}
close(callbackLock)
}
// We use a callback here instead of a goroutine and an chan for
// syncronization purposes
cErr := utils.Go(func() error { return container.monitor(callback) })
// Start should not return until the process is actually running
select {
case <-callbackLock:
case err := <-cErr:
return err
}
return nil
return container.waitForStart()
}
func (container *Container) Run() error {
@ -683,42 +496,18 @@ func (container *Container) allocateNetwork() error {
var (
env *engine.Env
err error
eng = container.runtime.eng
eng = container.daemon.eng
)
if container.State.IsGhost() {
if container.runtime.config.DisableNetwork {
env = &engine.Env{}
} else {
currentIP := container.NetworkSettings.IPAddress
job := eng.Job("allocate_interface", container.ID)
if currentIP != "" {
job.Setenv("RequestIP", currentIP)
}
env, err = job.Stdout.AddEnv()
if err != nil {
return err
}
if err := job.Run(); err != nil {
return err
}
}
} else {
job := eng.Job("allocate_interface", container.ID)
env, err = job.Stdout.AddEnv()
if err != nil {
return err
}
if err := job.Run(); err != nil {
return err
}
job := eng.Job("allocate_interface", container.ID)
if env, err = job.Stdout.AddEnv(); err != nil {
return err
}
if err := job.Run(); err != nil {
return err
}
if container.Config.PortSpecs != nil {
utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
return err
}
@ -733,58 +522,23 @@ func (container *Container) allocateNetwork() error {
bindings = make(nat.PortMap)
)
if !container.State.IsGhost() {
if container.Config.ExposedPorts != nil {
portSpecs = container.Config.ExposedPorts
}
if container.hostConfig.PortBindings != nil {
bindings = container.hostConfig.PortBindings
}
} else {
if container.NetworkSettings.Ports != nil {
for port, binding := range container.NetworkSettings.Ports {
portSpecs[port] = struct{}{}
bindings[port] = binding
}
}
if container.Config.ExposedPorts != nil {
portSpecs = container.Config.ExposedPorts
}
if container.hostConfig.PortBindings != nil {
bindings = container.hostConfig.PortBindings
}
container.NetworkSettings.PortMapping = nil
for port := range portSpecs {
binding := bindings[port]
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
binding = append(binding, nat.PortBinding{})
if err := container.allocatePort(eng, port, bindings); err != nil {
return err
}
for i := 0; i < len(binding); i++ {
b := binding[i]
portJob := eng.Job("allocate_port", container.ID)
portJob.Setenv("HostIP", b.HostIp)
portJob.Setenv("HostPort", b.HostPort)
portJob.Setenv("Proto", port.Proto())
portJob.Setenv("ContainerPort", port.Port())
portEnv, err := portJob.Stdout.AddEnv()
if err != nil {
return err
}
if err := portJob.Run(); err != nil {
eng.Job("release_interface", container.ID).Run()
return err
}
b.HostIp = portEnv.Get("HostIP")
b.HostPort = portEnv.Get("HostPort")
binding[i] = b
}
bindings[port] = binding
}
container.WriteHostConfig()
container.NetworkSettings.Ports = bindings
container.NetworkSettings.Bridge = env.Get("Bridge")
container.NetworkSettings.IPAddress = env.Get("IP")
container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
@ -797,7 +551,7 @@ func (container *Container) releaseNetwork() {
if container.Config.NetworkDisabled {
return
}
eng := container.runtime.eng
eng := container.daemon.eng
eng.Job("release_interface", container.ID).Run()
container.NetworkSettings = &NetworkSettings{}
@ -810,12 +564,12 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
)
pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin)
exitCode, err = container.runtime.Run(container, pipes, callback)
exitCode, err = container.daemon.Run(container, pipes, callback)
if err != nil {
utils.Errorf("Error running container: %s", err)
}
if container.runtime != nil && container.runtime.srv != nil && container.runtime.srv.IsRunning() {
if container.daemon != nil && container.daemon.srv != nil && container.daemon.srv.IsRunning() {
container.State.SetStopped(exitCode)
// FIXME: there is a race condition here which causes this to fail during the unit tests.
@ -838,8 +592,8 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
container.stdin, container.stdinPipe = io.Pipe()
}
if container.runtime != nil && container.runtime.srv != nil {
container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
if container.daemon != nil && container.daemon.srv != nil {
container.daemon.srv.LogEvent("die", container.ID, container.daemon.repositories.ImageName(container.Image))
}
close(container.waitLock)
@ -885,7 +639,7 @@ func (container *Container) KillSig(sig int) error {
if !container.State.IsRunning() {
return nil
}
return container.runtime.Kill(container, sig)
return container.daemon.Kill(container, sig)
}
func (container *Container) Kill() error {
@ -962,10 +716,10 @@ func (container *Container) ExportRw() (archive.Archive, error) {
if err := container.Mount(); err != nil {
return nil, err
}
if container.runtime == nil {
if container.daemon == nil {
return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
}
archive, err := container.runtime.Diff(container)
archive, err := container.daemon.Diff(container)
if err != nil {
container.Unmount()
return nil, err
@ -1012,22 +766,22 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
}
func (container *Container) Mount() error {
return container.runtime.Mount(container)
return container.daemon.Mount(container)
}
func (container *Container) Changes() ([]archive.Change, error) {
return container.runtime.Changes(container)
return container.daemon.Changes(container)
}
func (container *Container) GetImage() (*image.Image, error) {
if container.runtime == nil {
if container.daemon == nil {
return nil, fmt.Errorf("Can't get image of unregistered container")
}
return container.runtime.graph.Get(container.Image)
return container.daemon.graph.Get(container.Image)
}
func (container *Container) Unmount() error {
return container.runtime.Unmount(container)
return container.daemon.Unmount(container)
}
func (container *Container) logPath(name string) string {
@ -1080,7 +834,7 @@ func (container *Container) GetSize() (int64, int64) {
var (
sizeRw, sizeRootfs int64
err error
driver = container.runtime.driver
driver = container.daemon.driver
)
if err := container.Mount(); err != nil {
@ -1089,7 +843,7 @@ func (container *Container) GetSize() (int64, int64) {
}
defer container.Unmount()
if differ, ok := container.runtime.driver.(graphdriver.Differ); ok {
if differ, ok := container.daemon.driver.(graphdriver.Differ); ok {
sizeRw, err = differ.DiffSize(container.ID)
if err != nil {
utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
@ -1182,29 +936,32 @@ func (container *Container) DisableLink(name string) {
}
func (container *Container) setupContainerDns() error {
if container.ResolvConfPath != "" {
return nil
}
var (
config = container.hostConfig
runtime = container.runtime
config = container.hostConfig
daemon = container.daemon
)
resolvConf, err := utils.GetResolvConf()
if err != nil {
return err
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(runtime.config.DnsSearch) > 0 {
if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
var (
dns = utils.GetNameservers(resolvConf)
dnsSearch = utils.GetSearchDomains(resolvConf)
)
if len(config.Dns) > 0 {
dns = config.Dns
} else if len(runtime.config.Dns) > 0 {
dns = runtime.config.Dns
} else if len(daemon.config.Dns) > 0 {
dns = daemon.config.Dns
}
if len(config.DnsSearch) > 0 {
dnsSearch = config.DnsSearch
} else if len(runtime.config.DnsSearch) > 0 {
dnsSearch = runtime.config.DnsSearch
} else if len(daemon.config.DnsSearch) > 0 {
dnsSearch = daemon.config.DnsSearch
}
container.ResolvConfPath = path.Join(container.root, "resolv.conf")
f, err := os.Create(container.ResolvConfPath)
@ -1227,3 +984,198 @@ func (container *Container) setupContainerDns() error {
}
return nil
}
func (container *Container) initializeNetworking() error {
if container.daemon.config.DisableNetwork {
container.Config.NetworkDisabled = true
container.buildHostnameAndHostsFiles("127.0.1.1")
} else {
if err := container.allocateNetwork(); err != nil {
return err
}
container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
}
return nil
}
// Make sure the config is compatible with the current kernel
func (container *Container) verifyDaemonSettings() {
if container.Config.Memory > 0 && !container.daemon.sysInfo.MemoryLimit {
log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
container.Config.Memory = 0
}
if container.Config.Memory > 0 && !container.daemon.sysInfo.SwapLimit {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
container.Config.MemorySwap = -1
}
if container.daemon.sysInfo.IPv4ForwardingDisabled {
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
}
}
func (container *Container) setupLinkedContainers() ([]string, error) {
var (
env []string
daemon = container.daemon
)
children, err := daemon.Children(container.Name)
if err != nil {
return nil, err
}
if len(children) > 0 {
container.activeLinks = make(map[string]*links.Link, len(children))
// If we encounter an error make sure that we rollback any network
// config and ip table changes
rollback := func() {
for _, link := range container.activeLinks {
link.Disable()
}
container.activeLinks = nil
}
for linkAlias, child := range children {
if !child.State.IsRunning() {
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
}
link, err := links.NewLink(
container.NetworkSettings.IPAddress,
child.NetworkSettings.IPAddress,
linkAlias,
child.Config.Env,
child.Config.ExposedPorts,
daemon.eng)
if err != nil {
rollback()
return nil, err
}
container.activeLinks[link.Alias()] = link
if err := link.Enable(); err != nil {
rollback()
return nil, err
}
for _, envVar := range link.ToEnv() {
env = append(env, envVar)
}
}
}
return env, nil
}
func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
// Setup environment
env := []string{
"HOME=/",
"PATH=" + DefaultPathEnv,
"HOSTNAME=" + container.Config.Hostname,
}
if container.Config.Tty {
env = append(env, "TERM=xterm")
}
env = append(env, linkedEnv...)
// because the env on the container can override certain default values
// we need to replace the 'env' keys where they match and append anything
// else.
env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
return env
}
func (container *Container) setupWorkingDirectory() error {
if container.Config.WorkingDir != "" {
container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
pthInfo, err := os.Stat(path.Join(container.basefs, container.Config.WorkingDir))
if err != nil {
if !os.IsNotExist(err) {
return err
}
if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
return err
}
}
if pthInfo != nil && !pthInfo.IsDir() {
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
}
}
return nil
}
func (container *Container) startLoggingToDisk() error {
// Setup logging of stdout and stderr to disk
if err := container.daemon.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
return err
}
if err := container.daemon.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
return err
}
return nil
}
func (container *Container) waitForStart() error {
callbackLock := make(chan struct{})
callback := func(command *execdriver.Command) {
container.State.SetRunning(command.Pid())
if command.Tty {
// The callback is called after the process Start()
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
// which we close here.
if c, ok := command.Stdout.(io.Closer); ok {
c.Close()
}
}
if err := container.ToDisk(); err != nil {
utils.Debugf("%s", err)
}
close(callbackLock)
}
// We use a callback here instead of a goroutine and an chan for
// syncronization purposes
cErr := utils.Go(func() error { return container.monitor(callback) })
// Start should not return until the process is actually running
select {
case <-callbackLock:
case err := <-cErr:
return err
}
return nil
}
func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error {
binding := bindings[port]
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
binding = append(binding, nat.PortBinding{})
}
for i := 0; i < len(binding); i++ {
b := binding[i]
job := eng.Job("allocate_port", container.ID)
job.Setenv("HostIP", b.HostIp)
job.Setenv("HostPort", b.HostPort)
job.Setenv("Proto", port.Proto())
job.Setenv("ContainerPort", port.Port())
portEnv, err := job.Stdout.AddEnv()
if err != nil {
return err
}
if err := job.Run(); err != nil {
eng.Job("release_interface", container.ID).Run()
return err
}
b.HostIp = portEnv.Get("HostIP")
b.HostPort = portEnv.Get("HostPort")
binding[i] = b
}
bindings[port] = binding
return nil
}

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"github.com/dotcloud/docker/nat"

View file

@ -1,9 +1,16 @@
package runtime
package daemon
import (
"container/list"
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/execdrivers"
"github.com/dotcloud/docker/daemon/execdriver/lxc"
"github.com/dotcloud/docker/daemon/graphdriver"
_ "github.com/dotcloud/docker/daemon/graphdriver/vfs"
_ "github.com/dotcloud/docker/daemon/networkdriver/bridge"
"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
"github.com/dotcloud/docker/daemonconfig"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
@ -14,13 +21,6 @@ import (
"github.com/dotcloud/docker/pkg/selinux"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/runtime/execdriver/execdrivers"
"github.com/dotcloud/docker/runtime/execdriver/lxc"
"github.com/dotcloud/docker/runtime/graphdriver"
_ "github.com/dotcloud/docker/runtime/graphdriver/vfs"
_ "github.com/dotcloud/docker/runtime/networkdriver/bridge"
"github.com/dotcloud/docker/runtime/networkdriver/portallocator"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -44,7 +44,7 @@ var (
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
)
type Runtime struct {
type Daemon struct {
repository string
sysInitPath string
containers *list.List
@ -76,17 +76,17 @@ func remountPrivate(mountPoint string) error {
return mount.ForceMount("", mountPoint, "none", "private")
}
// List returns an array of all containers registered in the runtime.
func (runtime *Runtime) List() []*Container {
// List returns an array of all containers registered in the daemon.
func (daemon *Daemon) List() []*Container {
containers := new(History)
for e := runtime.containers.Front(); e != nil; e = e.Next() {
for e := daemon.containers.Front(); e != nil; e = e.Next() {
containers.Add(e.Value.(*Container))
}
return *containers
}
func (runtime *Runtime) getContainerElement(id string) *list.Element {
for e := runtime.containers.Front(); e != nil; e = e.Next() {
func (daemon *Daemon) getContainerElement(id string) *list.Element {
for e := daemon.containers.Front(); e != nil; e = e.Next() {
container := e.Value.(*Container)
if container.ID == id {
return e
@ -97,17 +97,17 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
// Get looks for a container by the specified ID or name, and returns it.
// If the container is not found, or if an error occurs, nil is returned.
func (runtime *Runtime) Get(name string) *Container {
if c, _ := runtime.GetByName(name); c != nil {
func (daemon *Daemon) Get(name string) *Container {
if c, _ := daemon.GetByName(name); c != nil {
return c
}
id, err := runtime.idIndex.Get(name)
id, err := daemon.idIndex.Get(name)
if err != nil {
return nil
}
e := runtime.getContainerElement(id)
e := daemon.getContainerElement(id)
if e == nil {
return nil
}
@ -116,18 +116,18 @@ func (runtime *Runtime) Get(name string) *Container {
// Exists returns a true if a container of the specified ID or name exists,
// false otherwise.
func (runtime *Runtime) Exists(id string) bool {
return runtime.Get(id) != nil
func (daemon *Daemon) Exists(id string) bool {
return daemon.Get(id) != nil
}
func (runtime *Runtime) containerRoot(id string) string {
return path.Join(runtime.repository, id)
func (daemon *Daemon) containerRoot(id string) string {
return path.Join(daemon.repository, id)
}
// Load reads the contents of a container from disk
// This is typically done at startup.
func (runtime *Runtime) load(id string) (*Container, error) {
container := &Container{root: runtime.containerRoot(id)}
func (daemon *Daemon) load(id string) (*Container, error) {
container := &Container{root: daemon.containerRoot(id)}
if err := container.FromDisk(); err != nil {
return nil, err
}
@ -140,19 +140,19 @@ func (runtime *Runtime) load(id string) (*Container, error) {
return container, nil
}
// Register makes a container object usable by the runtime as <container.ID>
func (runtime *Runtime) Register(container *Container) error {
if container.runtime != nil || runtime.Exists(container.ID) {
// Register makes a container object usable by the daemon as <container.ID>
func (daemon *Daemon) Register(container *Container) error {
if container.daemon != nil || daemon.Exists(container.ID) {
return fmt.Errorf("Container is already loaded")
}
if err := validateID(container.ID); err != nil {
return err
}
if err := runtime.ensureName(container); err != nil {
if err := daemon.ensureName(container); err != nil {
return err
}
container.runtime = runtime
container.daemon = daemon
// Attach to stdout and stderr
container.stderr = utils.NewWriteBroadcaster()
@ -164,8 +164,8 @@ func (runtime *Runtime) Register(container *Container) error {
container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
}
// done
runtime.containers.PushBack(container)
runtime.idIndex.Add(container.ID)
daemon.containers.PushBack(container)
daemon.idIndex.Add(container.ID)
// FIXME: if the container is supposed to be running but is not, auto restart it?
// if so, then we need to restart monitor and init a new lock
@ -192,7 +192,7 @@ func (runtime *Runtime) Register(container *Container) error {
if err != nil {
utils.Debugf("cannot find existing process for %d", existingPid)
}
runtime.execDriver.Terminate(cmd)
daemon.execDriver.Terminate(cmd)
}
if err := container.Unmount(); err != nil {
utils.Debugf("ghost unmount error %s", err)
@ -202,10 +202,10 @@ func (runtime *Runtime) Register(container *Container) error {
}
}
info := runtime.execDriver.Info(container.ID)
info := daemon.execDriver.Info(container.ID)
if !info.IsRunning() {
utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
if runtime.config.AutoRestart {
if daemon.config.AutoRestart {
utils.Debugf("Restarting")
if err := container.Unmount(); err != nil {
utils.Debugf("restart unmount error %s", err)
@ -234,9 +234,9 @@ func (runtime *Runtime) Register(container *Container) error {
return nil
}
func (runtime *Runtime) ensureName(container *Container) error {
func (daemon *Daemon) ensureName(container *Container) error {
if container.Name == "" {
name, err := generateRandomName(runtime)
name, err := generateRandomName(daemon)
if err != nil {
name = utils.TruncateID(container.ID)
}
@ -245,8 +245,8 @@ func (runtime *Runtime) ensureName(container *Container) error {
if err := container.ToDisk(); err != nil {
utils.Debugf("Error saving container name %s", err)
}
if !runtime.containerGraph.Exists(name) {
if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
if !daemon.containerGraph.Exists(name) {
if _, err := daemon.containerGraph.Set(name, container.ID); err != nil {
utils.Debugf("Setting default id - %s", err)
}
}
@ -254,7 +254,7 @@ func (runtime *Runtime) ensureName(container *Container) error {
return nil
}
func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
func (daemon *Daemon) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
if err != nil {
return err
@ -263,13 +263,13 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
return nil
}
// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
func (runtime *Runtime) Destroy(container *Container) error {
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
func (daemon *Daemon) Destroy(container *Container) error {
if container == nil {
return fmt.Errorf("The given container is <nil>")
}
element := runtime.getContainerElement(container.ID)
element := daemon.getContainerElement(container.ID)
if element == nil {
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
}
@ -278,42 +278,42 @@ func (runtime *Runtime) Destroy(container *Container) error {
return err
}
if err := runtime.driver.Remove(container.ID); err != nil {
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
if err := daemon.driver.Remove(container.ID); err != nil {
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
}
initID := fmt.Sprintf("%s-init", container.ID)
if err := runtime.driver.Remove(initID); err != nil {
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
if err := daemon.driver.Remove(initID); err != nil {
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
}
if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
utils.Debugf("Unable to remove container from link graph: %s", err)
}
// Deregister the container before removing its directory, to avoid race conditions
runtime.idIndex.Delete(container.ID)
runtime.containers.Remove(element)
daemon.idIndex.Delete(container.ID)
daemon.containers.Remove(element)
if err := os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
}
return nil
}
func (runtime *Runtime) restore() error {
func (daemon *Daemon) restore() error {
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
fmt.Printf("Loading containers: ")
}
dir, err := ioutil.ReadDir(runtime.repository)
dir, err := ioutil.ReadDir(daemon.repository)
if err != nil {
return err
}
containers := make(map[string]*Container)
currentDriver := runtime.driver.String()
currentDriver := daemon.driver.String()
for _, v := range dir {
id := v.Name()
container, err := runtime.load(id)
container, err := daemon.load(id)
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
fmt.Print(".")
}
@ -332,12 +332,12 @@ func (runtime *Runtime) restore() error {
}
register := func(container *Container) {
if err := runtime.Register(container); err != nil {
if err := daemon.Register(container); err != nil {
utils.Debugf("Failed to register container %s: %s", container.ID, err)
}
}
if entities := runtime.containerGraph.List("/", -1); entities != nil {
if entities := daemon.containerGraph.List("/", -1); entities != nil {
for _, p := range entities.Paths() {
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
fmt.Print(".")
@ -353,12 +353,12 @@ func (runtime *Runtime) restore() error {
// Any containers that are left over do not exist in the graph
for _, container := range containers {
// Try to set the default name for a container if it exists prior to links
container.Name, err = generateRandomName(runtime)
container.Name, err = generateRandomName(daemon)
if err != nil {
container.Name = utils.TruncateID(container.ID)
}
if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
if _, err := daemon.containerGraph.Set(container.Name, container.ID); err != nil {
utils.Debugf("Setting default id - %s", err)
}
register(container)
@ -372,38 +372,38 @@ func (runtime *Runtime) restore() error {
}
// Create creates a new container from the given configuration with a given name.
func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) {
var (
container *Container
warnings []string
)
img, err := runtime.repositories.LookupImage(config.Image)
img, err := daemon.repositories.LookupImage(config.Image)
if err != nil {
return nil, nil, err
}
if err := runtime.checkImageDepth(img); err != nil {
if err := daemon.checkImageDepth(img); err != nil {
return nil, nil, err
}
if warnings, err = runtime.mergeAndVerifyConfig(config, img); err != nil {
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
return nil, nil, err
}
if container, err = runtime.newContainer(name, config, img); err != nil {
if container, err = daemon.newContainer(name, config, img); err != nil {
return nil, nil, err
}
if err := runtime.createRootfs(container, img); err != nil {
if err := daemon.createRootfs(container, img); err != nil {
return nil, nil, err
}
if err := container.ToDisk(); err != nil {
return nil, nil, err
}
if err := runtime.Register(container); err != nil {
if err := daemon.Register(container); err != nil {
return nil, nil, err
}
return container, warnings, nil
}
func (runtime *Runtime) checkImageDepth(img *image.Image) error {
func (daemon *Daemon) checkImageDepth(img *image.Image) error {
// We add 2 layers to the depth because the container's rw and
// init layer add to the restriction
depth, err := img.Depth()
@ -416,7 +416,7 @@ func (runtime *Runtime) checkImageDepth(img *image.Image) error {
return nil
}
func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
if config != nil {
if config.PortSpecs != nil {
for _, p := range config.PortSpecs {
@ -429,9 +429,9 @@ func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
return false
}
func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
warnings := []string{}
if runtime.checkDeprecatedExpose(img.Config) || runtime.checkDeprecatedExpose(config) {
if daemon.checkDeprecatedExpose(img.Config) || daemon.checkDeprecatedExpose(config) {
warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
}
if img.Config != nil {
@ -445,14 +445,14 @@ func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *imag
return warnings, nil
}
func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
func (daemon *Daemon) generateIdAndName(name string) (string, string, error) {
var (
err error
id = utils.GenerateRandomID()
)
if name == "" {
name, err = generateRandomName(runtime)
name, err = generateRandomName(daemon)
if err != nil {
name = utils.TruncateID(id)
}
@ -465,19 +465,19 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
name = "/" + name
}
// Set the enitity in the graph using the default name specified
if _, err := runtime.containerGraph.Set(name, id); err != nil {
if _, err := daemon.containerGraph.Set(name, id); err != nil {
if !graphdb.IsNonUniqueNameError(err) {
return "", "", err
}
conflictingContainer, err := runtime.GetByName(name)
conflictingContainer, err := daemon.GetByName(name)
if err != nil {
if strings.Contains(err.Error(), "Could not find entity") {
return "", "", err
}
// Remove name and continue starting the container
if err := runtime.containerGraph.Delete(name); err != nil {
if err := daemon.containerGraph.Delete(name); err != nil {
return "", "", err
}
} else {
@ -490,7 +490,7 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
return id, name, nil
}
func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) {
// Generate default hostname
// FIXME: the lxc template no longer needs to set a default hostname
if config.Hostname == "" {
@ -498,7 +498,7 @@ func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
}
}
func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
func (daemon *Daemon) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
var (
entrypoint string
args []string
@ -513,18 +513,18 @@ func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string,
return entrypoint, args
}
func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
var (
id string
err error
)
id, name, err = runtime.generateIdAndName(name)
id, name, err = daemon.generateIdAndName(name)
if err != nil {
return nil, err
}
runtime.generateHostname(id, config)
entrypoint, args := runtime.getEntrypointAndArgs(config)
daemon.generateHostname(id, config)
entrypoint, args := daemon.getEntrypointAndArgs(config)
container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument
@ -537,34 +537,34 @@ func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img
Image: img.ID, // Always use the resolved image id
NetworkSettings: &NetworkSettings{},
Name: name,
Driver: runtime.driver.String(),
ExecDriver: runtime.execDriver.Name(),
Driver: daemon.driver.String(),
ExecDriver: daemon.execDriver.Name(),
}
container.root = runtime.containerRoot(container.ID)
container.root = daemon.containerRoot(container.ID)
return container, nil
}
func (runtime *Runtime) createRootfs(container *Container, img *image.Image) error {
func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {
// Step 1: create the container directory.
// This doubles as a barrier to avoid race conditions.
if err := os.Mkdir(container.root, 0700); err != nil {
return err
}
initID := fmt.Sprintf("%s-init", container.ID)
if err := runtime.driver.Create(initID, img.ID, ""); err != nil {
if err := daemon.driver.Create(initID, img.ID, ""); err != nil {
return err
}
initPath, err := runtime.driver.Get(initID)
initPath, err := daemon.driver.Get(initID)
if err != nil {
return err
}
defer runtime.driver.Put(initID)
defer daemon.driver.Put(initID)
if err := graph.SetupInitLayer(initPath); err != nil {
return err
}
if err := runtime.driver.Create(container.ID, initID, ""); err != nil {
if err := daemon.driver.Create(container.ID, initID, ""); err != nil {
return err
}
return nil
@ -572,7 +572,7 @@ func (runtime *Runtime) createRootfs(container *Container, img *image.Image) err
// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
func (daemon *Daemon) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
// FIXME: freeze the container before copying it to avoid data corruption?
if err := container.Mount(); err != nil {
return nil, err
@ -595,13 +595,13 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
containerImage = container.Image
containerConfig = container.Config
}
img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
if err != nil {
return nil, err
}
// Register the image if needed
if repository != "" {
if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
return img, err
}
}
@ -618,31 +618,31 @@ func GetFullContainerName(name string) (string, error) {
return name, nil
}
func (runtime *Runtime) GetByName(name string) (*Container, error) {
func (daemon *Daemon) GetByName(name string) (*Container, error) {
fullName, err := GetFullContainerName(name)
if err != nil {
return nil, err
}
entity := runtime.containerGraph.Get(fullName)
entity := daemon.containerGraph.Get(fullName)
if entity == nil {
return nil, fmt.Errorf("Could not find entity for %s", name)
}
e := runtime.getContainerElement(entity.ID())
e := daemon.getContainerElement(entity.ID())
if e == nil {
return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
}
return e.Value.(*Container), nil
}
func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
name, err := GetFullContainerName(name)
if err != nil {
return nil, err
}
children := make(map[string]*Container)
err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
c := runtime.Get(e.ID())
err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
c := daemon.Get(e.ID())
if c == nil {
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
}
@ -656,25 +656,25 @@ func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
return children, nil
}
func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error {
fullName := path.Join(parent.Name, alias)
if !runtime.containerGraph.Exists(fullName) {
_, err := runtime.containerGraph.Set(fullName, child.ID)
if !daemon.containerGraph.Exists(fullName) {
_, err := daemon.containerGraph.Set(fullName, child.ID)
return err
}
return nil
}
// FIXME: harmonize with NewGraph()
func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
runtime, err := NewRuntimeFromDirectory(config, eng)
func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
daemon, err := NewDaemonFromDirectory(config, eng)
if err != nil {
return nil, err
}
return runtime, nil
return daemon, nil
}
func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
if !config.EnableSelinuxSupport {
selinux.SetDisabled()
}
@ -693,9 +693,9 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
return nil, err
}
runtimeRepo := path.Join(config.Root, "containers")
daemonRepo := path.Join(config.Root, "containers")
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
if err := os.MkdirAll(daemonRepo, 0700); err != nil && !os.IsExist(err) {
return nil, err
}
@ -774,8 +774,8 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
return nil, err
}
runtime := &Runtime{
repository: runtimeRepo,
daemon := &Daemon{
repository: daemonRepo,
containers: list.New(),
graph: g,
repositories: repositories,
@ -790,19 +790,19 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
eng: eng,
}
if err := runtime.checkLocaldns(); err != nil {
if err := daemon.checkLocaldns(); err != nil {
return nil, err
}
if err := runtime.restore(); err != nil {
if err := daemon.restore(); err != nil {
return nil, err
}
return runtime, nil
return daemon, nil
}
func (runtime *Runtime) shutdown() error {
func (daemon *Daemon) shutdown() error {
group := sync.WaitGroup{}
utils.Debugf("starting clean shutdown of all containers...")
for _, container := range runtime.List() {
for _, container := range daemon.List() {
c := container
if c.State.IsRunning() {
utils.Debugf("stopping %s", c.ID)
@ -823,22 +823,22 @@ func (runtime *Runtime) shutdown() error {
return nil
}
func (runtime *Runtime) Close() error {
func (daemon *Daemon) Close() error {
errorsStrings := []string{}
if err := runtime.shutdown(); err != nil {
utils.Errorf("runtime.shutdown(): %s", err)
if err := daemon.shutdown(); err != nil {
utils.Errorf("daemon.shutdown(): %s", err)
errorsStrings = append(errorsStrings, err.Error())
}
if err := portallocator.ReleaseAll(); err != nil {
utils.Errorf("portallocator.ReleaseAll(): %s", err)
errorsStrings = append(errorsStrings, err.Error())
}
if err := runtime.driver.Cleanup(); err != nil {
utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
if err := daemon.driver.Cleanup(); err != nil {
utils.Errorf("daemon.driver.Cleanup(): %s", err.Error())
errorsStrings = append(errorsStrings, err.Error())
}
if err := runtime.containerGraph.Close(); err != nil {
utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
if err := daemon.containerGraph.Close(); err != nil {
utils.Errorf("daemon.containerGraph.Close(): %s", err.Error())
errorsStrings = append(errorsStrings, err.Error())
}
if len(errorsStrings) > 0 {
@ -847,55 +847,55 @@ func (runtime *Runtime) Close() error {
return nil
}
func (runtime *Runtime) Mount(container *Container) error {
dir, err := runtime.driver.Get(container.ID)
func (daemon *Daemon) Mount(container *Container) error {
dir, err := daemon.driver.Get(container.ID)
if err != nil {
return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err)
}
if container.basefs == "" {
container.basefs = dir
} else if container.basefs != dir {
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
runtime.driver, container.ID, container.basefs, dir)
daemon.driver, container.ID, container.basefs, dir)
}
return nil
}
func (runtime *Runtime) Unmount(container *Container) error {
runtime.driver.Put(container.ID)
func (daemon *Daemon) Unmount(container *Container) error {
daemon.driver.Put(container.ID)
return nil
}
func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
return differ.Changes(container.ID)
}
cDir, err := runtime.driver.Get(container.ID)
cDir, err := daemon.driver.Get(container.ID)
if err != nil {
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
}
defer runtime.driver.Put(container.ID)
initDir, err := runtime.driver.Get(container.ID + "-init")
defer daemon.driver.Put(container.ID)
initDir, err := daemon.driver.Get(container.ID + "-init")
if err != nil {
return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
}
defer runtime.driver.Put(container.ID + "-init")
defer daemon.driver.Put(container.ID + "-init")
return archive.ChangesDirs(cDir, initDir)
}
func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
if differ, ok := runtime.driver.(graphdriver.Differ); ok {
func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
if differ, ok := daemon.driver.(graphdriver.Differ); ok {
return differ.Diff(container.ID)
}
changes, err := runtime.Changes(container)
changes, err := daemon.Changes(container)
if err != nil {
return nil, err
}
cDir, err := runtime.driver.Get(container.ID)
cDir, err := daemon.driver.Get(container.ID)
if err != nil {
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
}
archive, err := archive.ExportChanges(cDir, changes)
@ -904,26 +904,26 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
}
return utils.NewReadCloserWrapper(archive, func() error {
err := archive.Close()
runtime.driver.Put(container.ID)
daemon.driver.Put(container.ID)
return err
}), nil
}
func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
return runtime.execDriver.Run(c.command, pipes, startCallback)
func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
return daemon.execDriver.Run(c.command, pipes, startCallback)
}
func (runtime *Runtime) Kill(c *Container, sig int) error {
return runtime.execDriver.Kill(c.command, sig)
func (daemon *Daemon) Kill(c *Container, sig int) error {
return daemon.execDriver.Kill(c.command, sig)
}
// Nuke kills all containers then removes all content
// from the content root, including images, volumes and
// container filesystems.
// Again: this will remove your entire docker runtime!
func (runtime *Runtime) Nuke() error {
// Again: this will remove your entire docker daemon!
func (daemon *Daemon) Nuke() error {
var wg sync.WaitGroup
for _, container := range runtime.List() {
for _, container := range daemon.List() {
wg.Add(1)
go func(c *Container) {
c.Kill()
@ -931,63 +931,63 @@ func (runtime *Runtime) Nuke() error {
}(container)
}
wg.Wait()
runtime.Close()
daemon.Close()
return os.RemoveAll(runtime.config.Root)
return os.RemoveAll(daemon.config.Root)
}
// FIXME: this is a convenience function for integration tests
// which need direct access to runtime.graph.
// which need direct access to daemon.graph.
// Once the tests switch to using engine and jobs, this method
// can go away.
func (runtime *Runtime) Graph() *graph.Graph {
return runtime.graph
func (daemon *Daemon) Graph() *graph.Graph {
return daemon.graph
}
func (runtime *Runtime) Repositories() *graph.TagStore {
return runtime.repositories
func (daemon *Daemon) Repositories() *graph.TagStore {
return daemon.repositories
}
func (runtime *Runtime) Config() *daemonconfig.Config {
return runtime.config
func (daemon *Daemon) Config() *daemonconfig.Config {
return daemon.config
}
func (runtime *Runtime) SystemConfig() *sysinfo.SysInfo {
return runtime.sysInfo
func (daemon *Daemon) SystemConfig() *sysinfo.SysInfo {
return daemon.sysInfo
}
func (runtime *Runtime) SystemInitPath() string {
return runtime.sysInitPath
func (daemon *Daemon) SystemInitPath() string {
return daemon.sysInitPath
}
func (runtime *Runtime) GraphDriver() graphdriver.Driver {
return runtime.driver
func (daemon *Daemon) GraphDriver() graphdriver.Driver {
return daemon.driver
}
func (runtime *Runtime) ExecutionDriver() execdriver.Driver {
return runtime.execDriver
func (daemon *Daemon) ExecutionDriver() execdriver.Driver {
return daemon.execDriver
}
func (runtime *Runtime) Volumes() *graph.Graph {
return runtime.volumes
func (daemon *Daemon) Volumes() *graph.Graph {
return daemon.volumes
}
func (runtime *Runtime) ContainerGraph() *graphdb.Database {
return runtime.containerGraph
func (daemon *Daemon) ContainerGraph() *graphdb.Database {
return daemon.containerGraph
}
func (runtime *Runtime) SetServer(server Server) {
runtime.srv = server
func (daemon *Daemon) SetServer(server Server) {
daemon.srv = server
}
func (runtime *Runtime) checkLocaldns() error {
func (daemon *Daemon) checkLocaldns() error {
resolvConf, err := utils.GetResolvConf()
if err != nil {
return err
}
if len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
runtime.config.Dns = DefaultDns
daemon.config.Dns = DefaultDns
}
return nil
}

View file

@ -1,11 +1,11 @@
// +build !exclude_graphdriver_aufs
package runtime
package daemon
import (
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver/aufs"
"github.com/dotcloud/docker/graph"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/runtime/graphdriver/aufs"
"github.com/dotcloud/docker/utils"
)

7
daemon/daemon_btrfs.go Normal file
View file

@ -0,0 +1,7 @@
// +build !exclude_graphdriver_btrfs
package daemon
import (
_ "github.com/dotcloud/docker/daemon/graphdriver/btrfs"
)

View file

@ -0,0 +1,7 @@
// +build !exclude_graphdriver_devicemapper
package daemon
import (
_ "github.com/dotcloud/docker/daemon/graphdriver/devmapper"
)

View file

@ -1,9 +1,9 @@
// +build exclude_graphdriver_aufs
package runtime
package daemon
import (
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
)
func migrateIfAufs(driver graphdriver.Driver, root string) error {

View file

@ -2,10 +2,10 @@ package execdrivers
import (
"fmt"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/lxc"
"github.com/dotcloud/docker/daemon/execdriver/native"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/runtime/execdriver/lxc"
"github.com/dotcloud/docker/runtime/execdriver/native"
"path"
)

View file

@ -2,9 +2,9 @@ package lxc
import (
"fmt"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/cgroups"
"github.com/dotcloud/docker/pkg/label"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"log"

View file

@ -3,9 +3,9 @@ package lxc
import (
"encoding/json"
"fmt"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/netlink"
"github.com/dotcloud/docker/pkg/user"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/syndtr/gocapability/capability"
"io/ioutil"
"net"

View file

@ -1,8 +1,8 @@
package lxc
import (
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/label"
"github.com/dotcloud/docker/runtime/execdriver"
"strings"
"text/template"
)

View file

@ -3,7 +3,7 @@ package lxc
import (
"bufio"
"fmt"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/daemon/execdriver"
"io/ioutil"
"math/rand"
"os"

View file

@ -1,7 +1,7 @@
package configuration
import (
"github.com/dotcloud/docker/runtime/execdriver/native/template"
"github.com/dotcloud/docker/daemon/execdriver/native/template"
"testing"
)

View file

@ -4,12 +4,12 @@ import (
"fmt"
"os"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
"github.com/dotcloud/docker/daemon/execdriver/native/template"
"github.com/dotcloud/docker/pkg/apparmor"
"github.com/dotcloud/docker/pkg/label"
"github.com/dotcloud/docker/pkg/libcontainer"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/runtime/execdriver/native/configuration"
"github.com/dotcloud/docker/runtime/execdriver/native/template"
)
// createContainer populates and configures the container type with the

View file

@ -3,12 +3,6 @@ package native
import (
"encoding/json"
"fmt"
"github.com/dotcloud/docker/pkg/apparmor"
"github.com/dotcloud/docker/pkg/cgroups"
"github.com/dotcloud/docker/pkg/libcontainer"
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
"github.com/dotcloud/docker/pkg/system"
"github.com/dotcloud/docker/runtime/execdriver"
"io"
"io/ioutil"
"log"
@ -18,6 +12,13 @@ import (
"strconv"
"strings"
"syscall"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/apparmor"
"github.com/dotcloud/docker/pkg/cgroups"
"github.com/dotcloud/docker/pkg/libcontainer"
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
"github.com/dotcloud/docker/pkg/system"
)
const (

View file

@ -5,7 +5,7 @@
package native
import (
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/daemon/execdriver"
"io"
"os"
"os/exec"

View file

@ -24,8 +24,8 @@ import (
"bufio"
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/graphdriver"
mountpk "github.com/dotcloud/docker/pkg/mount"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/utils"
"os"
"os/exec"

View file

@ -5,7 +5,7 @@ import (
"encoding/hex"
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"io/ioutil"
"os"
"path"

View file

@ -11,7 +11,7 @@ import "C"
import (
"fmt"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"os"
"path"
"syscall"

View file

@ -4,7 +4,7 @@ package devmapper
import (
"fmt"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"os"

View file

@ -4,7 +4,7 @@ package devmapper
import (
"fmt"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"io/ioutil"
"path"
"runtime"

View file

@ -2,7 +2,7 @@ package vfs
import (
"fmt"
"github.com/dotcloud/docker/runtime/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"os"
"os/exec"
"path"

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"sort"
@ -14,7 +14,7 @@ func (history *History) Len() int {
func (history *History) Less(i, j int) bool {
containers := *history
return containers[j].When().Before(containers[i].When())
return containers[j].Created.Before(containers[i].Created)
}
func (history *History) Swap(i, j int) {

View file

@ -0,0 +1,42 @@
package daemon
import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
)
// FIXME: move deprecated port stuff to nat to clean up the core.
type PortMapping map[string]string // Deprecated
type NetworkSettings struct {
IPAddress string
IPPrefixLen int
Gateway string
Bridge string
PortMapping map[string]PortMapping // Deprecated
Ports nat.PortMap
}
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
var outs = engine.NewTable("", 0)
for port, bindings := range settings.Ports {
p, _ := nat.ParsePort(port.Port())
if len(bindings) == 0 {
out := &engine.Env{}
out.SetInt("PublicPort", p)
out.Set("Type", port.Proto())
outs.Add(out)
continue
}
for _, binding := range bindings {
out := &engine.Env{}
h, _ := nat.ParsePort(binding.HostPort)
out.SetInt("PrivatePort", p)
out.SetInt("PublicPort", h)
out.Set("Type", port.Proto())
out.Set("IP", binding.HostIp)
outs.Add(out)
}
}
return outs
}

View file

@ -2,13 +2,13 @@ package bridge
import (
"fmt"
"github.com/dotcloud/docker/daemon/networkdriver"
"github.com/dotcloud/docker/daemon/networkdriver/ipallocator"
"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
"github.com/dotcloud/docker/daemon/networkdriver/portmapper"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/iptables"
"github.com/dotcloud/docker/pkg/netlink"
"github.com/dotcloud/docker/runtime/networkdriver"
"github.com/dotcloud/docker/runtime/networkdriver/ipallocator"
"github.com/dotcloud/docker/runtime/networkdriver/portallocator"
"github.com/dotcloud/docker/runtime/networkdriver/portmapper"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"log"
@ -32,7 +32,7 @@ var (
// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
// In theory this shouldn't matter - in practice there's bound to be a few scripts relying
// on the internal addressing or other stupid things like that.
// The shouldn't, but hey, let's not break them unless we really have to.
// They shouldn't, but hey, let's not break them unless we really have to.
"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
"10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive
"10.1.42.1/16",

View file

@ -3,8 +3,8 @@ package ipallocator
import (
"encoding/binary"
"errors"
"github.com/dotcloud/docker/daemon/networkdriver"
"github.com/dotcloud/docker/pkg/collections"
"github.com/dotcloud/docker/runtime/networkdriver"
"net"
"sync"
)

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"github.com/dotcloud/docker/utils"

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import "sort"

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"fmt"

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"fmt"
@ -51,14 +51,14 @@ func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[
}
type checker struct {
runtime *Runtime
daemon *Daemon
}
func (c *checker) Exists(name string) bool {
return c.runtime.containerGraph.Exists("/" + name)
return c.daemon.containerGraph.Exists("/" + name)
}
// Generate a random and unique name
func generateRandomName(runtime *Runtime) (string, error) {
return namesgenerator.GenerateRandomName(&checker{runtime})
func generateRandomName(daemon *Daemon) (string, error) {
return namesgenerator.GenerateRandomName(&checker{daemon})
}

View file

@ -1,4 +1,4 @@
package runtime
package daemon
import (
"testing"

View file

@ -1,9 +1,9 @@
package runtime
package daemon
import (
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"os"
@ -33,9 +33,14 @@ func prepareVolumesForContainer(container *Container) error {
return nil
}
func setupMountsForContainer(container *Container, envPath string) error {
func setupMountsForContainer(container *Container) error {
envPath, err := container.EnvConfigPath()
if err != nil {
return err
}
mounts := []execdriver.Mount{
{container.runtime.sysInitPath, "/.dockerinit", false, true},
{container.daemon.sysInitPath, "/.dockerinit", false, true},
{envPath, "/.dockerenv", false, true},
{container.ResolvConfPath, "/etc/resolv.conf", false, true},
}
@ -80,7 +85,7 @@ func applyVolumesFrom(container *Container) error {
}
}
c := container.runtime.Get(specParts[0])
c := container.daemon.Get(specParts[0])
if c == nil {
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0])
}
@ -162,7 +167,7 @@ func createVolumes(container *Container) error {
return err
}
volumesDriver := container.runtime.volumes.Driver()
volumesDriver := container.daemon.volumes.Driver()
// Create the requested volumes if they don't exist
for volPath := range container.Config.Volumes {
volPath = filepath.Clean(volPath)
@ -195,7 +200,7 @@ func createVolumes(container *Container) error {
// Do not pass a container as the parameter for the volume creation.
// The graph driver using the container's information ( Image ) to
// create the parent.
c, err := container.runtime.volumes.Create(nil, "", "", "", "", nil, nil)
c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil)
if err != nil {
return err
}

View file

@ -1,8 +1,8 @@
package daemonconfig
import (
"github.com/dotcloud/docker/daemon/networkdriver"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/runtime/networkdriver"
"net"
)

View file

@ -1,19 +1,45 @@
FROM ubuntu:12.04
MAINTAINER Nick Stinemates
#
# docker build -t docker:docs . && docker run -p 8000:8000 docker:docs
# See the top level Makefile in https://github.com/dotcloud/docker for usage.
#
FROM debian:jessie
MAINTAINER Sven Dowideit <SvenDowideit@docker.com> (@SvenDowideit)
# TODO switch to http://packages.ubuntu.com/trusty/python-sphinxcontrib-httpdomain once trusty is released
RUN apt-get update && apt-get install -yq make python-pip python-setuptools vim-tiny git pandoc
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq make python-pip python-setuptools
RUN pip install mkdocs
# installing sphinx for the rst->md conversion only - will be removed after May release
# pip installs from docs/requirements.txt, but here to increase cacheability
RUN pip install Sphinx==1.2.1
RUN pip install sphinxcontrib-httpdomain==1.2.0
ADD . /docs
RUN make -C /docs clean docs
RUN pip install Sphinx==1.2.1
RUN pip install sphinxcontrib-httpdomain==1.2.0
# add MarkdownTools to get transclusion
# (future development)
#RUN easy_install -U setuptools
#RUN pip install MarkdownTools2
# this week I seem to need the latest dev release of awscli too
# awscli 1.3.6 does --error-document correctly
# https://github.com/aws/aws-cli/commit/edc2290e173dfaedc70b48cfa3624d58c533c6c3
RUN pip install awscli
# get my sitemap.xml branch of mkdocs and use that for now
RUN git clone https://github.com/SvenDowideit/mkdocs &&\
cd mkdocs/ &&\
git checkout docker-markdown-merge &&\
./setup.py install
ADD . /docs
ADD MAINTAINERS /docs/sources/humans.txt
WORKDIR /docs
#build the sphinx html
#RUN make -C /docs clean docs
#convert to markdown
#RUN ./convert.sh
WORKDIR /docs/_build/html
CMD ["python", "-m", "SimpleHTTPServer"]
# note, EXPOSE is only last because of https://github.com/dotcloud/docker/issues/3525
EXPOSE 8000
EXPOSE 8000
CMD ["mkdocs", "serve"]

View file

@ -1,2 +1,3 @@
James Turnbull <james@lovedthanlost.net> (@jamtur01)
Sven Dowideit <SvenDowideit@fosiki.com> (@SvenDowideit)
O.S. Tezer <ostezer@gmail.com> (@OSTezer)

157
docs/README.md Normal file → Executable file
View file

@ -4,77 +4,49 @@ Docker Documentation
Overview
--------
The source for Docker documentation is here under ``sources/`` in the
form of .rst files. These files use
[reStructuredText](http://docutils.sourceforge.net/rst.html)
formatting with [Sphinx](http://sphinx-doc.org/) extensions for
structure, cross-linking and indexing.
The source for Docker documentation is here under ``sources/`` and uses
extended Markdown, as implemented by [mkdocs](http://mkdocs.org).
The HTML files are built and hosted on
[readthedocs.org](https://readthedocs.org/projects/docker/), appearing
via proxy on https://docs.docker.io. The HTML files update
The HTML files are built and hosted on https://docs.docker.io, and update
automatically after each change to the master or release branch of the
[docker files on GitHub](https://github.com/dotcloud/docker) thanks to
post-commit hooks. The "release" branch maps to the "latest"
documentation and the "master" branch maps to the "master"
documentation and the "master" (unreleased development) branch maps to the "master"
documentation.
## Branches
**There are two branches related to editing docs**: ``master`` and a
``doc*`` branch (currently ``doc0.8.1``). You should normally edit
docs on the ``master`` branch. That way your fixes will automatically
get included in later releases, and docs maintainers can easily
cherry-pick your changes to bring over to the current docs branch. In
the rare case where your change is not forward-compatible, then you
could base your change on the appropriate ``doc*`` branch.
``docs`` branch. You should always edit
docs on a local branch of the ``master`` branch, and send a PR against ``master``.
That way your fixes
will automatically get included in later releases, and docs maintainers
can easily cherry-pick your changes into the ``docs`` release branch.
In the rare case where your change is not forward-compatible,
you may need to base your changes on the ``docs`` branch.
Now that we have a ``doc*`` branch, we can keep the ``latest`` docs
Now that we have a ``docs`` branch, we can keep the [http://docs.docker.io](http://docs.docker.io) docs
up to date with any bugs found between ``docker`` code releases.
**Warning**: When *reading* the docs, the ``master`` documentation may
**Warning**: When *reading* the docs, the [http://beta-docs.docker.io](http://beta-docs.docker.io) documentation may
include features not yet part of any official docker
release. ``Master`` docs should be used only for understanding
bleeding-edge development and ``latest`` (which points to the ``doc*``
release. The ``beta-docs`` site should be used only for understanding
bleeding-edge development and ``docs.docker.io`` (which points to the ``docs``
branch``) should be used for the latest official release.
If you need to manually trigger a build of an existing branch, then
you can do that through the [readthedocs
interface](https://readthedocs.org/builds/docker/). If you would like
to add new build targets, including new branches or tags, then you
must contact one of the existing maintainers and get your
readthedocs.org account added to the maintainers list, or just file an
issue on GitHub describing the branch/tag and why it needs to be added
to the docs, and one of the maintainers will add it for you.
Getting Started
---------------
To edit and test the docs, you'll need to install the Sphinx tool and
its dependencies. There are two main ways to install this tool:
### Native Installation
Install dependencies from `requirements.txt` file in your `docker/docs`
directory:
* Linux: `pip install -r docs/requirements.txt`
* Mac OS X: `[sudo] pip-2.7 install -r docs/requirements.txt`
### Alternative Installation: Docker Container
If you're running ``docker`` on your development machine then you may
find it easier and cleaner to use the docs Dockerfile. This installs Sphinx
in a container, adds the local ``docs/`` directory and builds the HTML
docs inside the container, even starting a simple HTTP server on port
8000 so that you can connect and see your changes.
Docker documentation builds are done in a docker container, which installs all
the required tools, adds the local ``docs/`` directory and builds the HTML
docs. It then starts a HTTP server on port 8000 so that you can connect
and see your changes.
In the ``docker`` source directory, run:
```make docs```
This is the equivalent to ``make clean server`` since each container
starts clean.
If you have any issues you need to debug, you can use ``make docs-shell`` and
then run ``mkdocs serve``
# Contributing
@ -84,8 +56,8 @@ starts clean.
``../CONTRIBUTING.md``](../CONTRIBUTING.md)).
* [Remember to sign your work!](../CONTRIBUTING.md#sign-your-work)
* Work in your own fork of the code, we accept pull requests.
* Change the ``.rst`` files with your favorite editor -- try to keep the
lines short and respect RST and Sphinx conventions.
* Change the ``.md`` files with your favorite editor -- try to keep the
lines short (80 chars) and respect Markdown conventions.
* Run ``make clean docs`` to clean up old files and generate new ones,
or just ``make docs`` to update after small changes.
* Your static website can now be found in the ``_build`` directory.
@ -94,27 +66,13 @@ starts clean.
``make clean docs`` must complete without any warnings or errors.
## Special Case for RST Newbies:
If you want to write a new doc or make substantial changes to an
existing doc, but **you don't know RST syntax**, we will accept pull
requests in Markdown and plain text formats. We really want to
encourage people to share their knowledge and don't want the markup
syntax to be the obstacle. So when you make the Pull Request, please
note in your comment that you need RST markup assistance, and we'll
make the changes for you, and then we will make a pull request to your
pull request so that you can get all the changes and learn about the
markup. You still need to follow the
[``CONTRIBUTING``](../CONTRIBUTING) guidelines, so please sign your
commits.
Working using GitHub's file editor
----------------------------------
Alternatively, for small changes and typos you might want to use
GitHub's built in file editor. It allows you to preview your changes
right online (though there can be some differences between GitHub
markdown and Sphinx RST). Just be careful not to create many commits.
Markdown and mkdocs Markdown). Just be careful not to create many commits.
And you must still [sign your work!](../CONTRIBUTING.md#sign-your-work)
Images
@ -122,62 +80,25 @@ Images
When you need to add images, try to make them as small as possible
(e.g. as gif). Usually images should go in the same directory as the
.rst file which references them, or in a subdirectory if one already
.md file which references them, or in a subdirectory if one already
exists.
Notes
-----
Publishing Documentation
------------------------
* For the template the css is compiled from less. When changes are
needed they can be compiled using
To publish a copy of the documentation you need a ``docs/awsconfig``
file containing AWS settings to deploy to. The release script will
create an s3 if needed, and will then push the files to it.
lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.less -o main.css``
```
[profile dowideit-docs]
aws_access_key_id = IHOIUAHSIDH234rwf....
aws_secret_access_key = OIUYSADJHLKUHQWIUHE......
region = ap-southeast-2
```
Guides on using sphinx
----------------------
* To make links to certain sections create a link target like so:
The ``profile`` name must be the same as the name of the bucket you are
deploying to - which you call from the docker directory:
```
.. _hello_world:
Hello world
===========
This is a reference to :ref:`hello_world` and will work even if we
move the target to another file or change the title of the section.
```
The ``_hello_world:`` will make it possible to link to this position
(page and section heading) from all other pages. See the [Sphinx
docs](http://sphinx-doc.org/markup/inline.html#role-ref) for more
information and examples.
* Notes, warnings and alarms
```
# a note (use when something is important)
.. note::
# a warning (orange)
.. warning::
# danger (red, use sparsely)
.. danger::
* Code examples
* Start typed commands with ``$ `` (dollar space) so that they
are easily differentiated from program output.
* Use "sudo" with docker to ensure that your command is runnable
even if they haven't [used the *docker*
group](http://docs.docker.io/en/latest/use/basics/#why-sudo).
Manpages
--------
* To make the manpages, run ``make man``. Please note there is a bug
in Sphinx 1.1.3 which makes this fail. Upgrade to the latest version
of Sphinx.
* Then preview the manpage by running ``man _build/man/docker.1``,
where ``_build/man/docker.1`` is the path to the generated manfile
``make AWS_S3_BUCKET=dowideit-docs docs-release``

86
docs/asciinema.patch Normal file
View file

@ -0,0 +1,86 @@
diff --git a/docs/sources/examples/hello_world.md b/docs/sources/examples/hello_world.md
index 6e072f6..5a4537d 100644
--- a/docs/sources/examples/hello_world.md
+++ b/docs/sources/examples/hello_world.md
@@ -59,6 +59,9 @@ standard out.
See the example in action
+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/7658.js&quot;id=&quot;asciicast-7658&quot; async></script></body>"></iframe>
+
+
## Hello World Daemon
Note
@@ -142,6 +145,8 @@ Make sure it is really stopped.
See the example in action
+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/2562.js&quot;id=&quot;asciicast-2562&quot; async></script></body>"></iframe>
+
The next example in the series is a [*Node.js Web
App*](../nodejs_web_app/#nodejs-web-app) example, or you could skip to
any of the other examples:
diff --git a/docs/asciinema.patch b/docs/asciinema.patch
index e240bf3..e69de29 100644
--- a/docs/asciinema.patch
+++ b/docs/asciinema.patch
@@ -1,23 +0,0 @@
-diff --git a/docs/sources/examples/hello_world.md b/docs/sources/examples/hello_world.md
-index 6e072f6..5a4537d 100644
---- a/docs/sources/examples/hello_world.md
-+++ b/docs/sources/examples/hello_world.md
-@@ -59,6 +59,9 @@ standard out.
-
- See the example in action
-
-+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/7658.js&quot;id=&quot;asciicast-7658&quot; async></script></body>"></iframe>
-+
-+
- ## Hello World Daemon
-
- Note
-@@ -142,6 +145,8 @@ Make sure it is really stopped.
-
- See the example in action
-
-+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/2562.js&quot;id=&quot;asciicast-2562&quot; async></script></body>"></iframe>
-+
- The next example in the series is a [*Node.js Web
- App*](../nodejs_web_app/#nodejs-web-app) example, or you could skip to
- any of the other examples:
diff --git a/docs/sources/examples/hello_world.md b/docs/sources/examples/hello_world.md
index 6e072f6..c277f38 100644
--- a/docs/sources/examples/hello_world.md
+++ b/docs/sources/examples/hello_world.md
@@ -59,6 +59,8 @@ standard out.
See the example in action
+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/7658.js&quot;id=&quot;asciicast-7658&quot; async></script></body>"></iframe>
+
## Hello World Daemon
Note
@@ -142,6 +144,8 @@ Make sure it is really stopped.
See the example in action
+<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/2562.js&quot;id=&quot;asciicast-2562&quot; async></script></body>"></iframe>
+
The next example in the series is a [*Node.js Web
App*](../nodejs_web_app/#nodejs-web-app) example, or you could skip to
any of the other examples:
diff --git a/docs/sources/use/workingwithrepository.md b/docs/sources/use/workingwithrepository.md
index 2122b8d..49edbc8 100644
--- a/docs/sources/use/workingwithrepository.md
+++ b/docs/sources/use/workingwithrepository.md
@@ -199,6 +199,8 @@ searchable (or indexed at all) in the Central Index, and there will be
no user name checking performed. Your registry will function completely
independently from the Central Index.
+<iframe width="640" height="360" src="//www.youtube.com/embed/CAewZCBT4PI?rel=0" frameborder="0" allowfullscreen></iframe>
+
See also
[Docker Blog: How to use your own

53
docs/convert.sh Executable file
View file

@ -0,0 +1,53 @@
#!/bin/sh
cd /
#run the sphinx build first
make -C /docs clean docs
cd /docs
#find sources -name '*.md*' -exec rm '{}' \;
# convert from rst to md for mkdocs.org
# TODO: we're using a sphinx specific rst thing to do between docs links, which we then need to convert to mkdocs specific markup (and pandoc loses it when converting to html / md)
HTML_FILES=$(find _build -name '*.html' | sed 's/_build\/html\/\(.*\)\/index.html/\1/')
for name in ${HTML_FILES}
do
echo $name
# lets not use gratuitious unicode quotes that cause terrible copy and paste issues
sed -i 's/&#8220;/"/g' _build/html/${name}/index.html
sed -i 's/&#8221;/"/g' _build/html/${name}/index.html
pandoc -f html -t markdown --atx-headers -o sources/${name}.md1 _build/html/${name}/index.html
#add the meta-data from the rst
egrep ':(title|description|keywords):' sources/${name}.rst | sed 's/^:/page_/' > sources/${name}.md
echo >> sources/${name}.md
#cat sources/${name}.md1 >> sources/${name}.md
# remove the paragraph links from the source
cat sources/${name}.md1 | sed 's/\[..\](#.*)//' >> sources/${name}.md
rm sources/${name}.md1
sed -i 's/{.docutils .literal}//g' sources/${name}.md
sed -i 's/{.docutils$//g' sources/${name}.md
sed -i 's/^.literal} //g' sources/${name}.md
sed -i 's/`{.descname}`//g' sources/${name}.md
sed -i 's/{.descname}//g' sources/${name}.md
sed -i 's/{.xref}//g' sources/${name}.md
sed -i 's/{.xref .doc .docutils .literal}//g' sources/${name}.md
sed -i 's/{.xref .http .http-post .docutils$//g' sources/${name}.md
sed -i 's/^ .literal}//g' sources/${name}.md
sed -i 's/\\\$container\\_id/\$container_id/' sources/examples/hello_world.md
sed -i 's/\\\$TESTFLAGS/\$TESTFLAGS/' sources/contributing/devenvironment.md
sed -i 's/\\\$MYVAR1/\$MYVAR1/g' sources/reference/commandline/cli.md
# git it all so we can test
# git add ${name}.md
done
#annoyingly, there are lots of failures
patch --fuzz 50 -t -p2 < pr4923.patch || true
patch --fuzz 50 -t -p2 < asciinema.patch || true

View file

@ -0,0 +1,197 @@
diff --git a/docs/Dockerfile b/docs/Dockerfile
index bc2b73b..b9808b2 100644
--- a/docs/Dockerfile
+++ b/docs/Dockerfile
@@ -4,14 +4,24 @@ MAINTAINER SvenDowideit@docker.com
# docker build -t docker:docs . && docker run -p 8000:8000 docker:docs
#
-RUN apt-get update && apt-get install -yq make python-pip python-setuptools
-
+RUN apt-get update && apt-get install -yq make python-pip python-setuptools
RUN pip install mkdocs
+RUN apt-get install -yq vim-tiny git pandoc
+
+# pip installs from docs/requirements.txt, but here to increase cacheability
+RUN pip install Sphinx==1.2.1
+RUN pip install sphinxcontrib-httpdomain==1.2.0
+
ADD . /docs
+
+#build the sphinx html
+RUN make -C /docs clean docs
+
WORKDIR /docs
-CMD ["mkdocs", "serve"]
+#CMD ["mkdocs", "serve"]
+CMD bash
# note, EXPOSE is only last because of https://github.com/dotcloud/docker/issues/3525
EXPOSE 8000
diff --git a/docs/theme/docker/layout.html b/docs/theme/docker/layout.html
index 7d78fb9..0dac9e0 100755
--- a/docs/theme/docker/layout.html
+++ b/docs/theme/docker/layout.html
@@ -63,48 +63,6 @@
<body>
-<div id="wrap">
-<div class="navbar navbar-static-top navbar-inner navbar-fixed-top ">
- <div class="navbar-dotcloud">
- <div class="container">
-
- <div style="float: right" class="pull-right">
- <ul class="nav">
- <li id="nav-introduction"><a href="http://www.docker.io/" title="Docker Homepage">Home</a></li>
- <li id="nav-about"><a href="http://www.docker.io/about/" title="About">About</a></li>
- <li id="nav-gettingstarted"><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
- <li id="nav-community"><a href="http://www.docker.io/community/" title="Community">Community</a></li>
- <li id="nav-documentation" class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
- <li id="nav-blog"><a href="http://blog.docker.io/" title="Docker Blog">Blog</a></li>
- <li id="nav-index"><a href="http://index.docker.io/" title="Docker Image Index, find images here">INDEX <img class="inline-icon" alt="link to external site" src="{{ pathto('_static/img/external-link-icon.png', 1) }}" title="external link"> </a></li>
- </ul>
- </div>
-
- <div class="brand-logo">
- <a href="http://www.docker.io" title="Docker Homepage"><img src="{{ pathto('_static/img/docker-top-logo.png', 1) }}" alt="Docker logo"></a>
- </div>
- </div>
- </div>
-</div>
-
-<div class="container-fluid">
-
- <!-- Docs nav
- ================================================== -->
- <div class="row-fluid main-row">
-
- <div class="sidebar bs-docs-sidebar">
- <div class="page-title" >
- <h4>DOCUMENTATION</h4>
- </div>
-
- {{ toctree(collapse=False, maxdepth=3) }}
- <form>
- <input type="text" id="st-search-input" class="st-search-input span3" placeholder="search in documentation" style="width:210px;" />
- <div id="st-results-container"></div>
- </form>
- </div>
-
<!-- body block -->
<div class="main-content">
@@ -114,111 +72,7 @@
{% block body %}{% endblock %}
</section>
- <div class="pull-right"><a href="https://github.com/dotcloud/docker/blob/{{ github_tag }}/docs/sources/{{ pagename }}.rst" title="edit this article">Edit this article on GitHub</a></div>
</div>
- </div>
-</div>
-
-<div id="push-the-footer"></div>
-</div> <!-- end wrap for pushing footer -->
-
-<div id="footer">
- <div class="footer-landscape">
- <div class="footer-landscape-image">
- <!-- footer -->
- <div class="container">
- <div class="row footer">
- <div class="span12 tbox">
- <div class="tbox">
- <p>Docker is an open source project, sponsored by <a href="https://www.docker.com">Docker Inc.</a>, under the <a href="https://github.com/dotcloud/docker/blob/master/LICENSE" title="Docker licence, hosted in the Github repository">apache 2.0 licence</a></p>
- <p>Documentation proudly hosted by <a href="http://www.readthedocs.org">Read the Docs</a></p>
- </div>
-
- <div class="social links">
- <a title="Docker on Twitter" class="twitter" href="http://twitter.com/docker">Twitter</a>
- <a title="Docker on GitHub" class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
- <a title="Docker on Reddit" class="reddit" href="http://www.reddit.com/r/Docker/">Reddit</a>
- <a title="Docker on Google+" class="googleplus" href="https://plus.google.com/u/0/b/100381662757235514581/communities/108146856671494713993">Google+</a>
- <a title="Docker on Facebook" class="facebook" href="https://www.facebook.com/docker.run">Facebook</a>
- <a title="Docker on SlideShare" class="slideshare" href="http://www.slideshare.net/dotCloud">Slideshare</a>
- <a title="Docker on Youtube" class="youtube" href="http://www.youtube.com/user/dockerrun/">Youtube</a>
- <a title="Docker on Flickr" class="flickr" href="http://www.flickr.com/photos/99741659@N08/">Flickr</a>
- <a title="Docker on LinkedIn" class="linkedin" href="http://www.linkedin.com/company/dotcloud">LinkedIn</a>
- </div>
-
- <div class="tbox version-flyer ">
- <div class="content">
- <p class="version-note">Note: You are currently browsing the development documentation. The current release may work differently.</p>
-
- <small>Available versions:</small>
- <ul class="inline">
- {% for slug, url in versions %}
- <li class="alternative"><a href="{{ url }}{%- for word in pagename.split('/') -%}
- {%- if word != 'index' -%}
- {%- if word != '' -%}
- {{ word }}/
- {%- endif -%}
- {%- endif -%}
- {%- endfor -%}"
- title="Switch to {{ slug }}">{{ slug }}</a></li>
- {% endfor %}
- </ul>
- </div>
- </div>
-
-
- </div>
- </div>
- </div>
- </div>
- <!-- end of footer -->
- </div>
-
-</div>
-
-
-<script type="text/javascript" src="{{ pathto('_static/js/docs.js', 1) }}"></script>
-
-<!-- Swiftype search -->
-
-<script type="text/javascript">
- var Swiftype = window.Swiftype || {};
- (function() {
- Swiftype.key = 'pWPnnyvwcfpcrw1o51Sz';
- Swiftype.inputElement = '#st-search-input';
- Swiftype.resultContainingElement = '#st-results-container';
- Swiftype.attachElement = '#st-search-input';
- Swiftype.renderStyle = "overlay";
- // from https://swiftype.com/questions/how-can-i-make-more-popular-content-rank-higher
- // Use "page" for now -- they don't subgroup by document type yet.
- Swiftype.searchFunctionalBoosts = {"page": {"popularity": "linear"}};
-
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.async = true;
- script.src = "//swiftype.com/embed.js";
- var entry = document.getElementsByTagName('script')[0];
- entry.parentNode.insertBefore(script, entry);
- }());
-</script>
-
-
-<!-- Google analytics -->
-<script type="text/javascript">
-
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', 'UA-6096819-11']);
- _gaq.push(['_setDomainName', 'docker.io']);
- _gaq.push(['_setAllowLinker', true]);
- _gaq.push(['_trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
- })();
-
-</script>
</body>
</html>

126
docs/mkdocs.yml Executable file
View file

@ -0,0 +1,126 @@
site_name: Docker Documentation
#site_url: http://docs.docker.io/
site_url: /
site_description: Documentation for fast and lightweight Docker container based virtualization framework.
site_favicon: img/favicon.png
dev_addr: '0.0.0.0:8000'
repo_url: https://github.com/dotcloud/docker/
docs_dir: sources
include_search: true
use_absolute_urls: true
# theme: docker
theme_dir: ./theme/mkdocs/
theme_center_lead: false
include_search: true
copyright: Copyright &copy; 2014, Docker, Inc.
google_analytics: ['UA-6096819-11', 'docker.io']
pages:
# Introduction:
- ['index.md', 'About', 'Docker']
- ['introduction/index.md', '**HIDDEN**']
- ['introduction/understanding-docker.md', 'About', 'Understanding Docker']
- ['introduction/technology.md', 'About', 'The Technology']
- ['introduction/working-with-docker.md', 'About', 'Working with Docker']
- ['introduction/get-docker.md', 'About', 'Get Docker']
# Installation:
- ['installation/index.md', '**HIDDEN**']
- ['installation/mac.md', 'Installation', 'Mac OS X']
- ['installation/ubuntulinux.md', 'Installation', 'Ubuntu']
- ['installation/rhel.md', 'Installation', 'Red Hat Enterprise Linux']
- ['installation/gentoolinux.md', 'Installation', 'Gentoo']
- ['installation/google.md', 'Installation', 'Google Cloud Platform']
- ['installation/rackspace.md', 'Installation', 'Rackspace Cloud']
- ['installation/amazon.md', 'Installation', 'Amazon EC2']
- ['installation/softlayer.md', 'Installation', 'IBM Softlayer']
- ['installation/archlinux.md', 'Installation', 'Arch Linux']
- ['installation/frugalware.md', 'Installation', 'FrugalWare']
- ['installation/fedora.md', 'Installation', 'Fedora']
- ['installation/openSUSE.md', 'Installation', 'openSUSE']
- ['installation/cruxlinux.md', 'Installation', 'CRUX Linux']
- ['installation/windows.md', 'Installation', 'Microsoft Windows']
- ['installation/binaries.md', 'Installation', 'Binaries']
# Examples:
- ['use/index.md', '**HIDDEN**']
- ['use/basics.md', 'Examples', 'First steps with Docker']
- ['examples/index.md', '**HIDDEN**']
- ['examples/hello_world.md', 'Examples', 'Hello World']
- ['examples/nodejs_web_app.md', 'Examples', 'Node.js web application']
- ['examples/python_web_app.md', 'Examples', 'Python web application']
- ['examples/mongodb.md', 'Examples', 'MongoDB service']
- ['examples/running_redis_service.md', 'Examples', 'Redis service']
- ['examples/postgresql_service.md', 'Examples', 'PostgreSQL service']
- ['examples/running_riak_service.md', 'Examples', 'Running a Riak service']
- ['examples/running_ssh_service.md', 'Examples', 'Running an SSH service']
- ['examples/couchdb_data_volumes.md', 'Examples', 'CouchDB service']
- ['examples/apt-cacher-ng.md', 'Examples', 'Apt-Cacher-ng service']
- ['examples/https.md', 'Examples', 'Running Docker with HTTPS']
- ['examples/using_supervisord.md', 'Examples', 'Using Supervisor']
- ['examples/cfengine_process_management.md', 'Examples', 'Process management with CFEngine']
- ['use/working_with_links_names.md', 'Examples', 'Linking containers together']
- ['use/working_with_volumes.md', 'Examples', 'Sharing Directories using volumes']
- ['use/puppet.md', 'Examples', 'Using Puppet']
- ['use/chef.md', 'Examples', 'Using Chef']
- ['use/workingwithrepository.md', 'Examples', 'Working with a Docker Repository']
- ['use/port_redirection.md', 'Examples', 'Redirect ports']
- ['use/ambassador_pattern_linking.md', 'Examples', 'Cross-Host linking using Ambassador Containers']
- ['use/host_integration.md', 'Examples', 'Automatically starting Containers']
#- ['user-guide/index.md', '**HIDDEN**']
# - ['user-guide/writing-your-docs.md', 'User Guide', 'Writing your docs']
# - ['user-guide/styling-your-docs.md', 'User Guide', 'Styling your docs']
# - ['user-guide/configuration.md', 'User Guide', 'Configuration']
# ./faq.md
# Docker Index docs:
- ['index/index.md', '**HIDDEN**']
# - ['index/home.md', 'Docker Index', 'Help']
- ['index/accounts.md', 'Docker Index', 'Accounts']
- ['index/repos.md', 'Docker Index', 'Repositories']
- ['index/builds.md', 'Docker Index', 'Trusted Builds']
# Reference
- ['reference/index.md', '**HIDDEN**']
- ['reference/commandline/cli.md', 'Reference', 'Command line']
- ['reference/builder.md', 'Reference', 'Dockerfile']
- ['reference/run.md', 'Reference', 'Run Reference']
- ['articles/index.md', '**HIDDEN**']
- ['articles/runmetrics.md', 'Reference', 'Runtime metrics']
- ['articles/security.md', 'Reference', 'Security']
- ['articles/baseimages.md', 'Reference', 'Creating a Base Image']
- ['use/networking.md', 'Reference', 'Advanced networking']
- ['reference/api/index_api.md', 'Reference', 'Docker Index API']
- ['reference/api/registry_api.md', 'Reference', 'Docker Registry API']
- ['reference/api/registry_index_spec.md', 'Reference', 'Registry & Index Spec']
- ['reference/api/docker_remote_api.md', 'Reference', 'Docker Remote API']
- ['reference/api/docker_remote_api_v1.10.md', 'Reference', 'Docker Remote API v1.10']
- ['reference/api/docker_remote_api_v1.9.md', 'Reference', 'Docker Remote API v1.9']
- ['reference/api/remote_api_client_libraries.md', 'Reference', 'Docker Remote API Client Libraries']
# Contribute:
- ['contributing/index.md', '**HIDDEN**']
- ['contributing/contributing.md', 'Contribute', 'Contributing']
- ['contributing/devenvironment.md', 'Contribute', 'Development environment']
# - ['about/license.md', 'About', 'License']
- ['jsearch.md', '**HIDDEN**']
# - ['static_files/README.md', 'static_files', 'README']
#- ['terms/index.md', '**HIDDEN**']
# - ['terms/layer.md', 'terms', 'layer']
# - ['terms/index.md', 'terms', 'Home']
# - ['terms/registry.md', 'terms', 'registry']
# - ['terms/container.md', 'terms', 'container']
# - ['terms/repository.md', 'terms', 'repository']
# - ['terms/filesystem.md', 'terms', 'filesystem']
# - ['terms/image.md', 'terms', 'image']

12836
docs/pr4923.patch Normal file

File diff suppressed because it is too large Load diff

63
docs/release.sh Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -e
set -o pipefail
usage() {
cat >&2 <<'EOF'
To publish the Docker documentation you need to set your access_key and secret_key in the docs/awsconfig file
(with the keys in a [profile $AWS_S3_BUCKET] section - so you can have more than one set of keys in your file)
and set the AWS_S3_BUCKET env var to the name of your bucket.
make AWS_S3_BUCKET=beta-docs.docker.io docs-release
will then push the documentation site to your s3 bucket.
EOF
exit 1
}
[ "$AWS_S3_BUCKET" ] || usage
#VERSION=$(cat VERSION)
BUCKET=$AWS_S3_BUCKET
export AWS_CONFIG_FILE=$(pwd)/awsconfig
[ -e "$AWS_CONFIG_FILE" ] || usage
export AWS_DEFAULT_PROFILE=$BUCKET
echo "cfg file: $AWS_CONFIG_FILE ; profile: $AWS_DEFAULT_PROFILE"
setup_s3() {
echo "Create $BUCKET"
# Try creating the bucket. Ignore errors (it might already exist).
aws s3 mb s3://$BUCKET 2>/dev/null || true
# Check access to the bucket.
echo "test $BUCKET exists"
aws s3 ls s3://$BUCKET
# Make the bucket accessible through website endpoints.
echo "make $BUCKET accessible as a website"
#aws s3 website s3://$BUCKET --index-document index.html --error-document jsearch/index.html
s3conf=$(cat s3_website.json)
aws s3api put-bucket-website --bucket $BUCKET --website-configuration "$s3conf"
}
build_current_documentation() {
mkdocs build
}
upload_current_documentation() {
src=site/
dst=s3://$BUCKET
echo
echo "Uploading $src"
echo " to $dst"
echo
#s3cmd --recursive --follow-symlinks --preserve --acl-public sync "$src" "$dst"
aws s3 sync --acl public-read --exclude "*.rej" --exclude "*.rst" --exclude "*.orig" --exclude "*.py" "$src" "$dst"
}
setup_s3
build_current_documentation
upload_current_documentation

15
docs/s3_website.json Normal file
View file

@ -0,0 +1,15 @@
{
"ErrorDocument": {
"Key": "jsearch/index.html"
},
"IndexDocument": {
"Suffix": "index.html"
},
"RoutingRules": [
{ "Condition": { "KeyPrefixEquals": "en/latest/" }, "Redirect": { "ReplaceKeyPrefixWith": "" } },
{ "Condition": { "KeyPrefixEquals": "en/master/" }, "Redirect": { "ReplaceKeyPrefixWith": "" } },
{ "Condition": { "KeyPrefixEquals": "en/v0.6.3/" }, "Redirect": { "ReplaceKeyPrefixWith": "" } },
{ "Condition": { "KeyPrefixEquals": "jsearch/index.html" }, "Redirect": { "ReplaceKeyPrefixWith": "jsearch/" } }
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

8
docs/sources/articles.md Normal file
View file

@ -0,0 +1,8 @@
# Articles
## Contents:
- [Docker Security](security/)
- [Create a Base Image](baseimages/)
- [Runtime Metrics](runmetrics/)

View file

@ -0,0 +1,60 @@
page_title: Create a Base Image
page_description: How to create base images
page_keywords: Examples, Usage, base image, docker, documentation, examples
# Create a Base Image
So you want to create your own [*Base
Image*](../../terms/image/#base-image-def)? Great!
The specific process will depend heavily on the Linux distribution you
want to package. We have some examples below, and you are encouraged to
submit pull requests to contribute new ones.
## Create a full image using tar
In general, youll want to start with a working machine that is running
the distribution youd like to package as a base image, though that is
not required for some tools like Debians
[Debootstrap](https://wiki.debian.org/Debootstrap), which you can also
use to build Ubuntu images.
It can be as simple as this to create an Ubuntu base image:
$ sudo debootstrap raring raring > /dev/null
$ sudo tar -C raring -c . | sudo docker import - raring
a29c15f1bf7a
$ sudo docker run raring cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=13.04
DISTRIB_CODENAME=raring
DISTRIB_DESCRIPTION="Ubuntu 13.04"
There are more example scripts for creating base images in the Docker
GitHub Repo:
- [BusyBox](https://github.com/dotcloud/docker/blob/master/contrib/mkimage-busybox.sh)
- CentOS / Scientific Linux CERN (SLC) [on
Debian/Ubuntu](https://github.com/dotcloud/docker/blob/master/contrib/mkimage-rinse.sh)
or [on
CentOS/RHEL/SLC/etc.](https://github.com/dotcloud/docker/blob/master/contrib/mkimage-yum.sh)
- [Debian /
Ubuntu](https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debootstrap.sh)
## Creating a simple base image using `scratch`
There is a special repository in the Docker registry called
`scratch`, which was created using an empty tar
file:
$ tar cv --files-from /dev/null | docker import - scratch
which you can `docker pull`. You can then use that
image to base your new minimal containers `FROM`:
FROM scratch
ADD true-asm /true
CMD ["/true"]
The Dockerfile above is from extremely minimal image -
[tianon/true](https://github.com/tianon/dockerfiles/tree/master/true).

View file

@ -0,0 +1,434 @@
page_title: Runtime Metrics
page_description: Measure the behavior of running containers
page_keywords: docker, metrics, CPU, memory, disk, IO, run, runtime
# Runtime Metrics
Linux Containers rely on [control
groups](https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt)
which not only track groups of processes, but also expose metrics about
CPU, memory, and block I/O usage. You can access those metrics and
obtain network usage metrics as well. This is relevant for "pure" LXC
containers, as well as for Docker containers.
## Control Groups
Control groups are exposed through a pseudo-filesystem. In recent
distros, you should find this filesystem under
`/sys/fs/cgroup`. Under that directory, you will see
multiple sub-directories, called devices, freezer, blkio, etc.; each
sub-directory actually corresponds to a different cgroup hierarchy.
On older systems, the control groups might be mounted on
`/cgroup`, without distinct hierarchies. In that
case, instead of seeing the sub-directories, you will see a bunch of
files in that directory, and possibly some directories corresponding to
existing containers.
To figure out where your control groups are mounted, you can run:
grep cgroup /proc/mounts
## Enumerating Cgroups
You can look into `/proc/cgroups` to see the
different control group subsystems known to the system, the hierarchy
they belong to, and how many groups they contain.
You can also look at `/proc/<pid>/cgroup` to see
which control groups a process belongs to. The control group will be
shown as a path relative to the root of the hierarchy mountpoint; e.g.
`/` means “this process has not been assigned into a
particular group”, while `/lxc/pumpkin` means that
the process is likely to be a member of a container named
`pumpkin`.
## Finding the Cgroup for a Given Container
For each container, one cgroup will be created in each hierarchy. On
older systems with older versions of the LXC userland tools, the name of
the cgroup will be the name of the container. With more recent versions
of the LXC tools, the cgroup will be `lxc/<container_name>.`
For Docker containers using cgroups, the container name will be the full
ID or long ID of the container. If a container shows up as ae836c95b4c3
in `docker ps`, its long ID might be something like
`ae836c95b4c3c9e9179e0e91015512da89fdec91612f63cebae57df9a5444c79`. You can look it up with `docker inspect`
or `docker ps -notrunc`.
Putting everything together to look at the memory metrics for a Docker
container, take a look at
`/sys/fs/cgroup/memory/lxc/<longid>/`.
## Metrics from Cgroups: Memory, CPU, Block IO
For each subsystem (memory, CPU, and block I/O), you will find one or
more pseudo-files containing statistics.
### Memory Metrics: `memory.stat`
Memory metrics are found in the "memory" cgroup. Note that the memory
control group adds a little overhead, because it does very fine-grained
accounting of the memory usage on your host. Therefore, many distros
chose to not enable it by default. Generally, to enable it, all you have
to do is to add some kernel command-line parameters:
`cgroup_enable=memory swapaccount=1`.
The metrics are in the pseudo-file `memory.stat`.
Here is what it will look like:
cache 11492564992
rss 1930993664
mapped_file 306728960
pgpgin 406632648
pgpgout 403355412
swap 0
pgfault 728281223
pgmajfault 1724
inactive_anon 46608384
active_anon 1884520448
inactive_file 7003344896
active_file 4489052160
unevictable 32768
hierarchical_memory_limit 9223372036854775807
hierarchical_memsw_limit 9223372036854775807
total_cache 11492564992
total_rss 1930993664
total_mapped_file 306728960
total_pgpgin 406632648
total_pgpgout 403355412
total_swap 0
total_pgfault 728281223
total_pgmajfault 1724
total_inactive_anon 46608384
total_active_anon 1884520448
total_inactive_file 7003344896
total_active_file 4489052160
total_unevictable 32768
The first half (without the `total_` prefix)
contains statistics relevant to the processes within the cgroup,
excluding sub-cgroups. The second half (with the `total_`
prefix) includes sub-cgroups as well.
Some metrics are "gauges", i.e. values that can increase or decrease
(e.g. swap, the amount of swap space used by the members of the cgroup).
Some others are "counters", i.e. values that can only go up, because
they represent occurrences of a specific event (e.g. pgfault, which
indicates the number of page faults which happened since the creation of
the cgroup; this number can never decrease).
cache
: the amount of memory used by the processes of this control group
that can be associated precisely with a block on a block device.
When you read from and write to files on disk, this amount will
increase. This will be the case if you use "conventional" I/O
(`open`, `read`,
`write` syscalls) as well as mapped files (with
`mmap`). It also accounts for the memory used by
`tmpfs` mounts, though the reasons are unclear.
rss
: the amount of memory that *doesnt* correspond to anything on disk:
stacks, heaps, and anonymous memory maps.
mapped\_file
: indicates the amount of memory mapped by the processes in the
control group. It doesnt give you information about *how much*
memory is used; it rather tells you *how* it is used.
pgfault and pgmajfault
: indicate the number of times that a process of the cgroup triggered
a "page fault" and a "major fault", respectively. A page fault
happens when a process accesses a part of its virtual memory space
which is nonexistent or protected. The former can happen if the
process is buggy and tries to access an invalid address (it will
then be sent a `SIGSEGV` signal, typically
killing it with the famous `Segmentation fault`
message). The latter can happen when the process reads from a memory
zone which has been swapped out, or which corresponds to a mapped
file: in that case, the kernel will load the page from disk, and let
the CPU complete the memory access. It can also happen when the
process writes to a copy-on-write memory zone: likewise, the kernel
will preempt the process, duplicate the memory page, and resume the
write operation on the process own copy of the page. "Major" faults
happen when the kernel actually has to read the data from disk. When
it just has to duplicate an existing page, or allocate an empty
page, its a regular (or "minor") fault.
swap
: the amount of swap currently used by the processes in this cgroup.
active\_anon and inactive\_anon
: the amount of *anonymous* memory that has been identified has
respectively *active* and *inactive* by the kernel. "Anonymous"
memory is the memory that is *not* linked to disk pages. In other
words, thats the equivalent of the rss counter described above. In
fact, the very definition of the rss counter is **active\_anon** +
**inactive\_anon** - **tmpfs** (where tmpfs is the amount of memory
used up by `tmpfs` filesystems mounted by this
control group). Now, whats the difference between "active" and
"inactive"? Pages are initially "active"; and at regular intervals,
the kernel sweeps over the memory, and tags some pages as
"inactive". Whenever they are accessed again, they are immediately
retagged "active". When the kernel is almost out of memory, and time
comes to swap out to disk, the kernel will swap "inactive" pages.
active\_file and inactive\_file
: cache memory, with *active* and *inactive* similar to the *anon*
memory above. The exact formula is cache = **active\_file** +
**inactive\_file** + **tmpfs**. The exact rules used by the kernel
to move memory pages between active and inactive sets are different
from the ones used for anonymous memory, but the general principle
is the same. Note that when the kernel needs to reclaim memory, it
is cheaper to reclaim a clean (=non modified) page from this pool,
since it can be reclaimed immediately (while anonymous pages and
dirty/modified pages have to be written to disk first).
unevictable
: the amount of memory that cannot be reclaimed; generally, it will
account for memory that has been "locked" with `mlock`
. It is often used by crypto frameworks to make sure that
secret keys and other sensitive material never gets swapped out to
disk.
memory and memsw limits
: These are not really metrics, but a reminder of the limits applied
to this cgroup. The first one indicates the maximum amount of
physical memory that can be used by the processes of this control
group; the second one indicates the maximum amount of RAM+swap.
Accounting for memory in the page cache is very complex. If two
processes in different control groups both read the same file
(ultimately relying on the same blocks on disk), the corresponding
memory charge will be split between the control groups. Its nice, but
it also means that when a cgroup is terminated, it could increase the
memory usage of another cgroup, because they are not splitting the cost
anymore for those memory pages.
### CPU metrics: `cpuacct.stat`
Now that weve covered memory metrics, everything else will look very
simple in comparison. CPU metrics will be found in the
`cpuacct` controller.
For each container, you will find a pseudo-file `cpuacct.stat`,
containing the CPU usage accumulated by the processes of the container,
broken down between `user` and `system` time. If youre not familiar
with the distinction, `user` is the time during which the processes were
in direct control of the CPU (i.e. executing process code), and `system`
is the time during which the CPU was executing system calls on behalf of
those processes.
Those times are expressed in ticks of 1/100th of a second. Actually,
they are expressed in "user jiffies". There are `USER_HZ`
*"jiffies"* per second, and on x86 systems,
`USER_HZ` is 100. This used to map exactly to the
number of scheduler "ticks" per second; but with the advent of higher
frequency scheduling, as well as [tickless
kernels](http://lwn.net/Articles/549580/), the number of kernel ticks
wasnt relevant anymore. It stuck around anyway, mainly for legacy and
compatibility reasons.
### Block I/O metrics
Block I/O is accounted in the `blkio` controller.
Different metrics are scattered across different files. While you can
find in-depth details in the
[blkio-controller](https://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt)
file in the kernel documentation, here is a short list of the most
relevant ones:
blkio.sectors
: contain the number of 512-bytes sectors read and written by the
processes member of the cgroup, device by device. Reads and writes
are merged in a single counter.
blkio.io\_service\_bytes
: indicates the number of bytes read and written by the cgroup. It has
4 counters per device, because for each device, it differentiates
between synchronous vs. asynchronous I/O, and reads vs. writes.
blkio.io\_serviced
: the number of I/O operations performed, regardless of their size. It
also has 4 counters per device.
blkio.io\_queued
: indicates the number of I/O operations currently queued for this
cgroup. In other words, if the cgroup isnt doing any I/O, this will
be zero. Note that the opposite is not true. In other words, if
there is no I/O queued, it does not mean that the cgroup is idle
(I/O-wise). It could be doing purely synchronous reads on an
otherwise quiescent device, which is therefore able to handle them
immediately, without queuing. Also, while it is helpful to figure
out which cgroup is putting stress on the I/O subsystem, keep in
mind that is is a relative quantity. Even if a process group does
not perform more I/O, its queue size can increase just because the
device load increases because of other devices.
## Network Metrics
Network metrics are not exposed directly by control groups. There is a
good explanation for that: network interfaces exist within the context
of *network namespaces*. The kernel could probably accumulate metrics
about packets and bytes sent and received by a group of processes, but
those metrics wouldnt be very useful. You want per-interface metrics
(because traffic happening on the local `lo`
interface doesnt really count). But since processes in a single cgroup
can belong to multiple network namespaces, those metrics would be harder
to interpret: multiple network namespaces means multiple `lo`
interfaces, potentially multiple `eth0`
interfaces, etc.; so this is why there is no easy way to gather network
metrics with control groups.
Instead we can gather network metrics from other sources:
### IPtables
IPtables (or rather, the netfilter framework for which iptables is just
an interface) can do some serious accounting.
For instance, you can setup a rule to account for the outbound HTTP
traffic on a web server:
iptables -I OUTPUT -p tcp --sport 80
There is no `-j` or `-g` flag,
so the rule will just count matched packets and go to the following
rule.
Later, you can check the values of the counters, with:
iptables -nxvL OUTPUT
Technically, `-n` is not required, but it will
prevent iptables from doing DNS reverse lookups, which are probably
useless in this scenario.
Counters include packets and bytes. If you want to setup metrics for
container traffic like this, you could execute a `for`
loop to add two `iptables` rules per
container IP address (one in each direction), in the `FORWARD`
chain. This will only meter traffic going through the NAT
layer; you will also have to add traffic going through the userland
proxy.
Then, you will need to check those counters on a regular basis. If you
happen to use `collectd`, there is a nice plugin to
automate iptables counters collection.
### Interface-level counters
Since each container has a virtual Ethernet interface, you might want to
check directly the TX and RX counters of this interface. You will notice
that each container is associated to a virtual Ethernet interface in
your host, with a name like `vethKk8Zqi`. Figuring
out which interface corresponds to which container is, unfortunately,
difficult.
But for now, the best way is to check the metrics *from within the
containers*. To accomplish this, you can run an executable from the host
environment within the network namespace of a container using **ip-netns
magic**.
The `ip-netns exec` command will let you execute any
program (present in the host system) within any network namespace
visible to the current process. This means that your host will be able
to enter the network namespace of your containers, but your containers
wont be able to access the host, nor their sibling containers.
Containers will be able to “see” and affect their sub-containers,
though.
The exact format of the command is:
ip netns exec <nsname> <command...>
For example:
ip netns exec mycontainer netstat -i
`ip netns` finds the "mycontainer" container by
using namespaces pseudo-files. Each process belongs to one network
namespace, one PID namespace, one `mnt` namespace,
etc., and those namespaces are materialized under
`/proc/<pid>/ns/`. For example, the network
namespace of PID 42 is materialized by the pseudo-file
`/proc/42/ns/net`.
When you run `ip netns exec mycontainer ...`, it
expects `/var/run/netns/mycontainer` to be one of
those pseudo-files. (Symlinks are accepted.)
In other words, to execute a command within the network namespace of a
container, we need to:
- Find out the PID of any process within the container that we want to
investigate;
- Create a symlink from `/var/run/netns/<somename>`
to `/proc/<thepid>/ns/net`
- Execute `ip netns exec <somename> ....`
Please review [*Enumerating Cgroups*](#enumerating-cgroups) to learn how to find
the cgroup of a pprocess running in the container of which you want to
measure network usage. From there, you can examine the pseudo-file named
`tasks`, which containes the PIDs that are in the
control group (i.e. in the container). Pick any one of them.
Putting everything together, if the "short ID" of a container is held in
the environment variable `$CID`, then you can do
this:
TASKS=/sys/fs/cgroup/devices/$CID*/tasks
PID=$(head -n 1 $TASKS)
mkdir -p /var/run/netns
ln -sf /proc/$PID/ns/net /var/run/netns/$CID
ip netns exec $CID netstat -i
## Tips for high-performance metric collection
Note that running a new process each time you want to update metrics is
(relatively) expensive. If you want to collect metrics at high
resolutions, and/or over a large number of containers (think 1000
containers on a single host), you do not want to fork a new process each
time.
Here is how to collect metrics from a single process. You will have to
write your metric collector in C (or any language that lets you do
low-level system calls). You need to use a special system call,
`setns()`, which lets the current process enter any
arbitrary namespace. It requires, however, an open file descriptor to
the namespace pseudo-file (remember: thats the pseudo-file in
`/proc/<pid>/ns/net`).
However, there is a catch: you must not keep this file descriptor open.
If you do, when the last process of the control group exits, the
namespace will not be destroyed, and its network resources (like the
virtual interface of the container) will stay around for ever (or until
you close that file descriptor).
The right approach would be to keep track of the first PID of each
container, and re-open the namespace pseudo-file each time.
## Collecting metrics when a container exits
Sometimes, you do not care about real time metric collection, but when a
container exits, you want to know how much CPU, memory, etc. it has
used.
Docker makes this difficult because it relies on `lxc-start`, which
carefully cleans up after itself, but it is still possible. It is
usually easier to collect metrics at regular intervals (e.g. every
minute, with the collectd LXC plugin) and rely on that instead.
But, if youd still like to gather the stats when a container stops,
here is how:
For each container, start a collection process, and move it to the
control groups that you want to monitor by writing its PID to the tasks
file of the cgroup. The collection process should periodically re-read
the tasks file to check if its the last process of the control group.
(If you also want to collect network statistics as explained in the
previous section, you should also move the process to the appropriate
network namespace.)
When the container exits, `lxc-start` will try to
delete the control groups. It will fail, since the control group is
still in use; but thats fine. You process should now detect that it is
the only one remaining in the group. Now is the right time to collect
all the metrics you need!
Finally, your process should move itself back to the root control group,
and remove the container control group. To remove a control group, just
`rmdir` its directory. Its counter-intuitive to
`rmdir` a directory as it still contains files; but
remember that this is a pseudo-filesystem, so usual rules dont apply.
After the cleanup is done, the collection process can exit safely.

View file

@ -0,0 +1,258 @@
page_title: Docker Security
page_description: Review of the Docker Daemon attack surface
page_keywords: Docker, Docker documentation, security
# Docker Security
> *Adapted from* [Containers & Docker: How Secure are
> They?](http://blog.docker.io/2013/08/containers-docker-how-secure-are-they/)
There are three major areas to consider when reviewing Docker security:
- the intrinsic security of containers, as implemented by kernel
namespaces and cgroups;
- the attack surface of the Docker daemon itself;
- the "hardening" security features of the kernel and how they
interact with containers.
## Kernel Namespaces
Docker containers are essentially LXC containers, and they come with the
same security features. When you start a container with
`docker run`, behind the scenes Docker uses
`lxc-start` to execute the Docker container. This
creates a set of namespaces and control groups for the container. Those
namespaces and control groups are not created by Docker itself, but by
`lxc-start`. This means that as the LXC userland
tools evolve (and provide additional namespaces and isolation features),
Docker will automatically make use of them.
**Namespaces provide the first and most straightforward form of
isolation**: processes running within a container cannot see, and even
less affect, processes running in another container, or in the host
system.
**Each container also gets its own network stack**, meaning that a
container doesnt get a privileged access to the sockets or interfaces
of another container. Of course, if the host system is setup
accordingly, containers can interact with each other through their
respective network interfaces — just like they can interact with
external hosts. When you specify public ports for your containers or use
[*links*](../../use/working_with_links_names/#working-with-links-names)
then IP traffic is allowed between containers. They can ping each other,
send/receive UDP packets, and establish TCP connections, but that can be
restricted if necessary. From a network architecture point of view, all
containers on a given Docker host are sitting on bridge interfaces. This
means that they are just like physical machines connected through a
common Ethernet switch; no more, no less.
How mature is the code providing kernel namespaces and private
networking? Kernel namespaces were introduced [between kernel version
2.6.15 and
2.6.26](http://lxc.sourceforge.net/index.php/about/kernel-namespaces/).
This means that since July 2008 (date of the 2.6.26 release, now 5 years
ago), namespace code has been exercised and scrutinized on a large
number of production systems. And there is more: the design and
inspiration for the namespaces code are even older. Namespaces are
actually an effort to reimplement the features of
[OpenVZ](http://en.wikipedia.org/wiki/OpenVZ) in such a way that they
could be merged within the mainstream kernel. And OpenVZ was initially
released in 2005, so both the design and the implementation are pretty
mature.
## Control Groups
Control Groups are the other key component of Linux Containers. They
implement resource accounting and limiting. They provide a lot of very
useful metrics, but they also help to ensure that each container gets
its fair share of memory, CPU, disk I/O; and, more importantly, that a
single container cannot bring the system down by exhausting one of those
resources.
So while they do not play a role in preventing one container from
accessing or affecting the data and processes of another container, they
are essential to fend off some denial-of-service attacks. They are
particularly important on multi-tenant platforms, like public and
private PaaS, to guarantee a consistent uptime (and performance) even
when some applications start to misbehave.
Control Groups have been around for a while as well: the code was
started in 2006, and initially merged in kernel 2.6.24.
## Docker Daemon Attack Surface
Running containers (and applications) with Docker implies running the
Docker daemon. This daemon currently requires root privileges, and you
should therefore be aware of some important details.
First of all, **only trusted users should be allowed to control your
Docker daemon**. This is a direct consequence of some powerful Docker
features. Specifically, Docker allows you to share a directory between
the Docker host and a guest container; and it allows you to do so
without limiting the access rights of the container. This means that you
can start a container where the `/host` directory
will be the `/` directory on your host; and the
container will be able to alter your host filesystem without any
restriction. This sounds crazy? Well, you have to know that **all
virtualization systems allowing filesystem resource sharing behave the
same way**. Nothing prevents you from sharing your root filesystem (or
even your root block device) with a virtual machine.
This has a strong security implication: if you instrument Docker from
e.g. a web server to provision containers through an API, you should be
even more careful than usual with parameter checking, to make sure that
a malicious user cannot pass crafted parameters causing Docker to create
arbitrary containers.
For this reason, the REST API endpoint (used by the Docker CLI to
communicate with the Docker daemon) changed in Docker 0.5.2, and now
uses a UNIX socket instead of a TCP socket bound on 127.0.0.1 (the
latter being prone to cross-site-scripting attacks if you happen to run
Docker directly on your local machine, outside of a VM). You can then
use traditional UNIX permission checks to limit access to the control
socket.
You can also expose the REST API over HTTP if you explicitly decide so.
However, if you do that, being aware of the abovementioned security
implication, you should ensure that it will be reachable only from a
trusted network or VPN; or protected with e.g. `stunnel`
and client SSL certificates.
Recent improvements in Linux namespaces will soon allow to run
full-featured containers without root privileges, thanks to the new user
namespace. This is covered in detail
[here](http://s3hh.wordpress.com/2013/07/19/creating-and-using-containers-without-privilege/).
Moreover, this will solve the problem caused by sharing filesystems
between host and guest, since the user namespace allows users within
containers (including the root user) to be mapped to other users in the
host system.
The end goal for Docker is therefore to implement two additional
security improvements:
- map the root user of a container to a non-root user of the Docker
host, to mitigate the effects of a container-to-host privilege
escalation;
- allow the Docker daemon to run without root privileges, and delegate
operations requiring those privileges to well-audited sub-processes,
each with its own (very limited) scope: virtual network setup,
filesystem management, etc.
Finally, if you run Docker on a server, it is recommended to run
exclusively Docker in the server, and move all other services within
containers controlled by Docker. Of course, it is fine to keep your
favorite admin tools (probably at least an SSH server), as well as
existing monitoring/supervision processes (e.g. NRPE, collectd, etc).
## Linux Kernel Capabilities
By default, Docker starts containers with a very restricted set of
capabilities. What does that mean?
Capabilities turn the binary "root/non-root" dichotomy into a
fine-grained access control system. Processes (like web servers) that
just need to bind on a port below 1024 do not have to run as root: they
can just be granted the `net_bind_service`
capability instead. And there are many other capabilities, for almost
all the specific areas where root privileges are usually needed.
This means a lot for container security; lets see why!
Your average server (bare metal or virtual machine) needs to run a bunch
of processes as root. Those typically include SSH, cron, syslogd;
hardware management tools (to e.g. load modules), network configuration
tools (to handle e.g. DHCP, WPA, or VPNs), and much more. A container is
very different, because almost all of those tasks are handled by the
infrastructure around the container:
- SSH access will typically be managed by a single server running in
the Docker host;
- `cron`, when necessary, should run as a user
process, dedicated and tailored for the app that needs its
scheduling service, rather than as a platform-wide facility;
- log management will also typically be handed to Docker, or by
third-party services like Loggly or Splunk;
- hardware management is irrelevant, meaning that you never need to
run `udevd` or equivalent daemons within
containers;
- network management happens outside of the containers, enforcing
separation of concerns as much as possible, meaning that a container
should never need to perform `ifconfig`,
`route`, or ip commands (except when a container
is specifically engineered to behave like a router or firewall, of
course).
This means that in most cases, containers will not need "real" root
privileges *at all*. And therefore, containers can run with a reduced
capability set; meaning that "root" within a container has much less
privileges than the real "root". For instance, it is possible to:
- deny all "mount" operations;
- deny access to raw sockets (to prevent packet spoofing);
- deny access to some filesystem operations, like creating new device
nodes, changing the owner of files, or altering attributes
(including the immutable flag);
- deny module loading;
- and many others.
This means that even if an intruder manages to escalate to root within a
container, it will be much harder to do serious damage, or to escalate
to the host.
This wont affect regular web apps; but malicious users will find that
the arsenal at their disposal has shrunk considerably! You can see [the
list of dropped capabilities in the Docker
code](https://github.com/dotcloud/docker/blob/v0.5.0/lxc_template.go#L97),
and a full list of available capabilities in [Linux
manpages](http://man7.org/linux/man-pages/man7/capabilities.7.html).
Of course, you can always enable extra capabilities if you really need
them (for instance, if you want to use a FUSE-based filesystem), but by
default, Docker containers will be locked down to ensure maximum safety.
## Other Kernel Security Features
Capabilities are just one of the many security features provided by
modern Linux kernels. It is also possible to leverage existing,
well-known systems like TOMOYO, AppArmor, SELinux, GRSEC, etc. with
Docker.
While Docker currently only enables capabilities, it doesnt interfere
with the other systems. This means that there are many different ways to
harden a Docker host. Here are a few examples.
- You can run a kernel with GRSEC and PAX. This will add many safety
checks, both at compile-time and run-time; it will also defeat many
exploits, thanks to techniques like address randomization. It
doesnt require Docker-specific configuration, since those security
features apply system-wide, independently of containers.
- If your distribution comes with security model templates for LXC
containers, you can use them out of the box. For instance, Ubuntu
comes with AppArmor templates for LXC, and those templates provide
an extra safety net (even though it overlaps greatly with
capabilities).
- You can define your own policies using your favorite access control
mechanism. Since Docker containers are standard LXC containers,
there is nothing “magic” or specific to Docker.
Just like there are many third-party tools to augment Docker containers
with e.g. special network topologies or shared filesystems, you can
expect to see tools to harden existing Docker containers without
affecting Dockers core.
## Conclusions
Docker containers are, by default, quite secure; especially if you take
care of running your processes inside the containers as non-privileged
users (i.e. non root).
You can add an extra layer of safety by enabling Apparmor, SELinux,
GRSEC, or your favorite hardening solution.
Last but not least, if you see interesting security features in other
containerization systems, you will be able to implement them as well
with Docker, since everything is provided by the kernel anyway.
For more context and especially for comparisons with VMs and other
container systems, please also see the [original blog
post](http://blog.docker.io/2013/08/containers-docker-how-secure-are-they/).

View file

@ -0,0 +1,7 @@
# Contributing
## Contents:
- [Contributing to Docker](contributing/)
- [Setting Up a Dev Environment](devenvironment/)

View file

@ -0,0 +1,24 @@
page_title: Contribution Guidelines
page_description: Contribution guidelines: create issues, conventions, pull requests
page_keywords: contributing, docker, documentation, help, guideline
# Contributing to Docker
Want to hack on Docker? Awesome!
The repository includes [all the instructions you need to get
started](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md).
The [developer environment
Dockerfile](https://github.com/dotcloud/docker/blob/master/Dockerfile)
specifies the tools and versions used to test and build Docker.
If youre making changes to the documentation, see the
[README.md](https://github.com/dotcloud/docker/blob/master/docs/README.md).
The [documentation environment
Dockerfile](https://github.com/dotcloud/docker/blob/master/docs/Dockerfile)
specifies the tools and versions used to build the Documentation.
Further interesting details can be found in the [Packaging
hints](https://github.com/dotcloud/docker/blob/master/hack/PACKAGERS.md).

View file

@ -0,0 +1,147 @@
page_title: Setting Up a Dev Environment
page_description: Guides on how to contribute to docker
page_keywords: Docker, documentation, developers, contributing, dev environment
# Setting Up a Dev Environment
To make it easier to contribute to Docker, we provide a standard
development environment. It is important that the same environment be
used for all tests, builds and releases. The standard development
environment defines all build dependencies: system libraries and
binaries, go environment, go dependencies, etc.
## Install Docker
Dockers build environment itself is a Docker container, so the first
step is to install Docker on your system.
You can follow the [install instructions most relevant to your
system](https://docs.docker.io/en/latest/installation/). Make sure you
have a working, up-to-date docker installation, then continue to the
next step.
## Install tools used for this tutorial
Install `git`; honest, its very good. You can use
other ways to get the Docker source, but theyre not anywhere near as
easy.
Install `make`. This tutorial uses our base Makefile
to kick off the docker containers in a repeatable and consistent way.
Again, you can do it in other ways but you need to do more work.
## Check out the Source
git clone http://git@github.com/dotcloud/docker
cd docker
To checkout a different revision just use `git checkout`
with the name of branch or revision number.
## Build the Environment
This following command will build a development environment using the
Dockerfile in the current directory. Essentially, it will install all
the build and runtime dependencies necessary to build and test Docker.
This command will take some time to complete when you first execute it.
sudo make build
If the build is successful, congratulations! You have produced a clean
build of docker, neatly encapsulated in a standard build environment.
## Build the Docker Binary
To create the Docker binary, run this command:
sudo make binary
This will create the Docker binary in
`./bundles/<version>-dev/binary/`
### Using your built Docker binary
The binary is available outside the container in the directory
`./bundles/<version>-dev/binary/`. You can swap your
host docker executable with this binary for live testing - for example,
on ubuntu:
sudo service docker stop ; sudo cp $(which docker) $(which docker)_ ; sudo cp ./bundles/<version>-dev/binary/docker-<version>-dev $(which docker);sudo service docker start
> **Note**:
> Its safer to run the tests below before swapping your hosts docker binary.
## Run the Tests
To execute the test cases, run this command:
sudo make test
If the test are successful then the tail of the output should look
something like this
--- PASS: TestWriteBroadcaster (0.00 seconds)
=== RUN TestRaceWriteBroadcaster
--- PASS: TestRaceWriteBroadcaster (0.00 seconds)
=== RUN TestTruncIndex
--- PASS: TestTruncIndex (0.00 seconds)
=== RUN TestCompareKernelVersion
--- PASS: TestCompareKernelVersion (0.00 seconds)
=== RUN TestHumanSize
--- PASS: TestHumanSize (0.00 seconds)
=== RUN TestParseHost
--- PASS: TestParseHost (0.00 seconds)
=== RUN TestParseRepositoryTag
--- PASS: TestParseRepositoryTag (0.00 seconds)
=== RUN TestGetResolvConf
--- PASS: TestGetResolvConf (0.00 seconds)
=== RUN TestCheckLocalDns
--- PASS: TestCheckLocalDns (0.00 seconds)
=== RUN TestParseRelease
--- PASS: TestParseRelease (0.00 seconds)
=== RUN TestDependencyGraphCircular
--- PASS: TestDependencyGraphCircular (0.00 seconds)
=== RUN TestDependencyGraph
--- PASS: TestDependencyGraph (0.00 seconds)
PASS
ok github.com/dotcloud/docker/utils 0.017s
If $TESTFLAGS is set in the environment, it is passed as extra
arguments to go test. You can use this to select certain tests to run,
eg.
> TESTFLAGS=-run \^TestBuild\$ make test
If the output indicates "FAIL" and you see errors like this:
server.go:1302 Error: Insertion failed because database is full: database or disk is full
utils_test.go:179: Error copy: exit status 1 (cp: writing '/tmp/docker-testd5c9-[...]': No space left on device
Then you likely dont have enough memory available the test suite. 2GB
is recommended.
## Use Docker
You can run an interactive session in the newly built container:
sudo make shell
# type 'exit' or Ctrl-D to exit
## Build And View The Documentation
If you want to read the documentation from a local website, or are
making changes to it, you can build the documentation and then serve it
by:
sudo make docs
# when its done, you can point your browser to http://yourdockerhost:8000
# type Ctrl-C to exit
**Need More Help?**
If you need more help then hop on to the [\#docker-dev IRC
channel](irc://chat.freenode.net#docker-dev) or post a message on the
[Docker developer mailing
list](https://groups.google.com/d/forum/docker-dev).

25
docs/sources/examples.md Normal file
View file

@ -0,0 +1,25 @@
# Examples
## Introduction:
Here are some examples of how to use Docker to create running processes,
starting from a very simple *Hello World* and progressing to more
substantial services like those which you might find in production.
## Contents:
- [Check your Docker install](hello_world/)
- [Hello World](hello_world/#hello-world)
- [Hello World Daemon](hello_world/#hello-world-daemon)
- [Node.js Web App](nodejs_web_app/)
- [Redis Service](running_redis_service/)
- [SSH Daemon Service](running_ssh_service/)
- [CouchDB Service](couchdb_data_volumes/)
- [PostgreSQL Service](postgresql_service/)
- [Building an Image with MongoDB](mongodb/)
- [Riak Service](running_riak_service/)
- [Using Supervisor with Docker](using_supervisord/)
- [Process Management with CFEngine](cfengine_process_management/)
- [Python Web App](python_web_app/)

View file

@ -0,0 +1,112 @@
page_title: Running an apt-cacher-ng service
page_description: Installing and running an apt-cacher-ng service
page_keywords: docker, example, package installation, networking, debian, ubuntu
# Apt-Cacher-ng Service
> **Note**:
>
> - This example assumes you have Docker running in daemon mode. For
> more information please see [*Check your Docker
> install*](../hello_world/#running-examples).
> - **If you dont like sudo** then see [*Giving non-root
> access*](../../installation/binaries/#dockergroup).
> - **If youre using OS X or docker via TCP** then you shouldnt use
> sudo.
When you have multiple Docker servers, or build unrelated Docker
containers which cant make use of the Docker build cache, it can be
useful to have a caching proxy for your packages. This container makes
the second download of any package almost instant.
Use the following Dockerfile:
#
# Build: docker build -t apt-cacher .
# Run: docker run -d -p 3142:3142 --name apt-cacher-run apt-cacher
#
# and then you can run containers with:
# docker run -t -i --rm -e http_proxy http://dockerhost:3142/ debian bash
#
FROM ubuntu
MAINTAINER SvenDowideit@docker.com
VOLUME ["/var/cache/apt-cacher-ng"]
RUN apt-get update ; apt-get install -yq apt-cacher-ng
EXPOSE 3142
CMD chmod 777 /var/cache/apt-cacher-ng ; /etc/init.d/apt-cacher-ng start ; tail -f /var/log/apt-cacher-ng/*
To build the image using:
$ sudo docker build -t eg_apt_cacher_ng .
Then run it, mapping the exposed port to one on the host
$ sudo docker run -d -p 3142:3142 --name test_apt_cacher_ng eg_apt_cacher_ng
To see the logfiles that are tailed in the default command, you can
use:
$ sudo docker logs -f test_apt_cacher_ng
To get your Debian-based containers to use the proxy, you can do one of
three things
1. Add an apt Proxy setting
`echo 'Acquire::http { Proxy "http://dockerhost:3142"; };' >> /etc/apt/conf.d/01proxy`
2. Set an environment variable:
`http_proxy=http://dockerhost:3142/`
3. Change your `sources.list` entries to start with
`http://dockerhost:3142/`
**Option 1** injects the settings safely into your apt configuration in
a local version of a common base:
FROM ubuntu
RUN echo 'Acquire::http { Proxy "http://dockerhost:3142"; };' >> /etc/apt/apt.conf.d/01proxy
RUN apt-get update ; apt-get install vim git
# docker build -t my_ubuntu .
**Option 2** is good for testing, but will break other HTTP clients
which obey `http_proxy`, such as `curl`, `wget` and others:
$ sudo docker run --rm -t -i -e http_proxy=http://dockerhost:3142/ debian bash
**Option 3** is the least portable, but there will be times when you
might need to do it and you can do it from your `Dockerfile`
too.
Apt-cacher-ng has some tools that allow you to manage the repository,
and they can be used by leveraging the `VOLUME`
instruction, and the image we built to run the service:
$ sudo docker run --rm -t -i --volumes-from test_apt_cacher_ng eg_apt_cacher_ng bash
$$ /usr/lib/apt-cacher-ng/distkill.pl
Scanning /var/cache/apt-cacher-ng, please wait...
Found distributions:
bla, taggedcount: 0
1. precise-security (36 index files)
2. wheezy (25 index files)
3. precise-updates (36 index files)
4. precise (36 index files)
5. wheezy-updates (18 index files)
Found architectures:
6. amd64 (36 index files)
7. i386 (24 index files)
WARNING: The removal action may wipe out whole directories containing
index files. Select d to see detailed list.
(Number nn: tag distribution or architecture nn; 0: exit; d: show details; r: remove tagged; q: quit): q
Finally, clean up after your test by stopping and removing the
container, and then removing the image.
$ sudo docker stop test_apt_cacher_ng
$ sudo docker rm test_apt_cacher_ng
$ sudo docker rmi eg_apt_cacher_ng

View file

@ -0,0 +1,152 @@
page_title: Process Management with CFEngine
page_description: Managing containerized processes with CFEngine
page_keywords: cfengine, process, management, usage, docker, documentation
# Process Management with CFEngine
Create Docker containers with managed processes.
Docker monitors one process in each running container and the container
lives or dies with that process. By introducing CFEngine inside Docker
containers, we can alleviate a few of the issues that may arise:
- It is possible to easily start multiple processes within a
container, all of which will be managed automatically, with the
normal `docker run` command.
- If a managed process dies or crashes, CFEngine will start it again
within 1 minute.
- The container itself will live as long as the CFEngine scheduling
daemon (cf-execd) lives. With CFEngine, we are able to decouple the
life of the container from the uptime of the service it provides.
## How it works
CFEngine, together with the cfe-docker integration policies, are
installed as part of the Dockerfile. This builds CFEngine into our
Docker image.
The Dockerfiles `ENTRYPOINT` takes an arbitrary
amount of commands (with any desired arguments) as parameters. When we
run the Docker container these parameters get written to CFEngine
policies and CFEngine takes over to ensure that the desired processes
are running in the container.
CFEngine scans the process table for the `basename`
of the commands given to the `ENTRYPOINT` and runs
the command to start the process if the `basename`
is not found. For example, if we start the container with
`docker run "/path/to/my/application parameters"`,
CFEngine will look for a process named `application`
and run the command. If an entry for `application`
is not found in the process table at any point in time, CFEngine will
execute `/path/to/my/application parameters` to
start the application once again. The check on the process table happens
every minute.
Note that it is therefore important that the command to start your
application leaves a process with the basename of the command. This can
be made more flexible by making some minor adjustments to the CFEngine
policies, if desired.
## Usage
This example assumes you have Docker installed and working. We will
install and manage `apache2` and `sshd`
in a single container.
There are three steps:
1. Install CFEngine into the container.
2. Copy the CFEngine Docker process management policy into the
containerized CFEngine installation.
3. Start your application processes as part of the
`docker run` command.
### Building the container image
The first two steps can be done as part of a Dockerfile, as follows.
FROM ubuntu
MAINTAINER Eystein Måløy Stenberg <eytein.stenberg@gmail.com>
RUN apt-get -y install wget lsb-release unzip ca-certificates
# install latest CFEngine
RUN wget -qO- http://cfengine.com/pub/gpg.key | apt-key add -
RUN echo "deb http://cfengine.com/pub/apt $(lsb_release -cs) main" > /etc/apt/sources.list.d/cfengine-community.list
RUN apt-get update
RUN apt-get install cfengine-community
# install cfe-docker process management policy
RUN wget https://github.com/estenberg/cfe-docker/archive/master.zip -P /tmp/ && unzip /tmp/master.zip -d /tmp/
RUN cp /tmp/cfe-docker-master/cfengine/bin/* /var/cfengine/bin/
RUN cp /tmp/cfe-docker-master/cfengine/inputs/* /var/cfengine/inputs/
RUN rm -rf /tmp/cfe-docker-master /tmp/master.zip
# apache2 and openssh are just for testing purposes, install your own apps here
RUN apt-get -y install openssh-server apache2
RUN mkdir -p /var/run/sshd
RUN echo "root:password" | chpasswd # need a password for ssh
ENTRYPOINT ["/var/cfengine/bin/docker_processes_run.sh"]
By saving this file as `Dockerfile` to a working
directory, you can then build your container with the docker build
command, e.g. `docker build -t managed_image`.
### Testing the container
Start the container with `apache2` and
`sshd` running and managed, forwarding a port to our
SSH instance:
docker run -p 127.0.0.1:222:22 -d managed_image "/usr/sbin/sshd" "/etc/init.d/apache2 start"
We now clearly see one of the benefits of the cfe-docker integration: it
allows to start several processes as part of a normal
`docker run` command.
We can now log in to our new container and see that both
`apache2` and `sshd` are
running. We have set the root password to "password" in the Dockerfile
above and can use that to log in with ssh:
ssh -p222 root@127.0.0.1
ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 07:48 ? 00:00:00 /bin/bash /var/cfengine/bin/docker_processes_run.sh /usr/sbin/sshd /etc/init.d/apache2 start
root 18 1 0 07:48 ? 00:00:00 /var/cfengine/bin/cf-execd -F
root 20 1 0 07:48 ? 00:00:00 /usr/sbin/sshd
root 32 1 0 07:48 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 34 32 0 07:48 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 35 32 0 07:48 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 36 32 0 07:48 ? 00:00:00 /usr/sbin/apache2 -k start
root 93 20 0 07:48 ? 00:00:00 sshd: root@pts/0
root 105 93 0 07:48 pts/0 00:00:00 -bash
root 112 105 0 07:49 pts/0 00:00:00 ps -ef
If we stop apache2, it will be started again within a minute by
CFEngine.
service apache2 status
Apache2 is running (pid 32).
service apache2 stop
* Stopping web server apache2 ... waiting [ OK ]
service apache2 status
Apache2 is NOT running.
# ... wait up to 1 minute...
service apache2 status
Apache2 is running (pid 173).
## Adapting to your applications
To make sure your applications get managed in the same manner, there are
just two things you need to adjust from the above example:
- In the Dockerfile used above, install your applications instead of
`apache2` and `sshd`.
- When you start the container with `docker run`,
specify the command line arguments to your applications rather than
`apache2` and `sshd`.

View file

@ -0,0 +1,48 @@
page_title: Sharing data between 2 couchdb databases
page_description: Sharing data between 2 couchdb databases
page_keywords: docker, example, package installation, networking, couchdb, data volumes
# CouchDB Service
> **Note**:
>
> - This example assumes you have Docker running in daemon mode. For
> more information please see [*Check your Docker
> install*](../hello_world/#running-examples).
> - **If you dont like sudo** then see [*Giving non-root
> access*](../../installation/binaries/#dockergroup)
Heres an example of using data volumes to share the same data between
two CouchDB containers. This could be used for hot upgrades, testing
different versions of CouchDB on the same data, etc.
## Create first database
Note that were marking `/var/lib/couchdb` as a data
volume.
COUCH1=$(sudo docker run -d -p 5984 -v /var/lib/couchdb shykes/couchdb:2013-05-03)
## Add data to the first database
Were assuming your Docker host is reachable at `localhost`. If not,
replace `localhost` with the public IP of your Docker host.
HOST=localhost
URL="http://$HOST:$(sudo docker port $COUCH1 5984 | grep -Po '\d+$')/_utils/"
echo "Navigate to $URL in your browser, and use the couch interface to add data"
## Create second database
This time, were requesting shared access to `$COUCH1`'s volumes.
COUCH2=$(sudo docker run -d -p 5984 --volumes-from $COUCH1 shykes/couchdb:2013-05-03)
## Browse data on the second database
HOST=localhost
URL="http://$HOST:$(sudo docker port $COUCH2 5984 | grep -Po '\d+$')/_utils/"
echo "Navigate to $URL in your browser. You should see the same data as in the first database"'!'
Congratulations, you are now running two Couchdb containers, completely
isolated from each other *except* for their data.

View file

@ -0,0 +1,166 @@
page_title: Hello world example
page_description: A simple hello world example with Docker
page_keywords: docker, example, hello world
# Check your Docker installation
This guide assumes you have a working installation of Docker. To check
your Docker install, run the following command:
# Check that you have a working install
$ sudo docker info
If you get `docker: command not found` or something
like `/var/lib/docker/repositories: permission denied`
you may have an incomplete Docker installation or insufficient
privileges to access docker on your machine.
Please refer to [*Installation*](../../installation/)
for installation instructions.
## Hello World
> **Note**:
>
> - This example assumes you have Docker running in daemon mode. For
> more information please see [*Check your Docker
> install*](#check-your-docker-installation).
> - **If you dont like sudo** then see [*Giving non-root
> access*](../../installation/binaries/#dockergroup)
This is the most basic example available for using Docker.
Download the small base image named `busybox`:
# Download a busybox image
$ sudo docker pull busybox
The `busybox` image is a minimal Linux system. You
can do the same with any number of other images, such as
`debian`, `ubuntu` or
`centos`. The images can be found and retrieved
using the [Docker index](http://index.docker.io).
$ sudo docker run busybox /bin/echo hello world
This command will run a simple `echo` command, that
will echo `hello world` back to the console over
standard out.
**Explanation:**
- **"sudo"** execute the following commands as user *root*
- **"docker run"** run a command in a new container
- **"busybox"** is the image we are running the command in.
- **"/bin/echo"** is the command we want to run in the container
- **"hello world"** is the input for the echo command
**Video:**
See the example in action
<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/7658.js&quot;id=&quot;asciicast-7658&quot; async></script></body>"></iframe>
<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/7658.js&quot;id=&quot;asciicast-7658&quot; async></script></body>"></iframe>
## Hello World Daemon
> **Note**:
>
> - This example assumes you have Docker running in daemon mode. For
> more information please see [*Check your Docker
> install*](#check-your-docker-installation).
> - **If you dont like sudo** then see [*Giving non-root
> access*](../../installation/binaries/#dockergroup)
And now for the most boring daemon ever written!
We will use the Ubuntu image to run a simple hello world daemon that
will just print hello world to standard out every second. It will
continue to do this until we stop it.
**Steps:**
CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done")
We are going to run a simple hello world daemon in a new container made
from the `ubuntu` image.
- **"sudo docker run -d "** run a command in a new container. We pass
"-d" so it runs as a daemon.
- **"ubuntu"** is the image we want to run the command inside of.
- **"/bin/sh -c"** is the command we want to run in the container
- **"while true; do echo hello world; sleep 1; done"** is the mini
script we want to run, that will just print hello world once a
second until we stop it.
- **$container_id** the output of the run command will return a
container id, we can use in future commands to see what is going on
with this process.
<!-- -->
sudo docker logs $container_id
Check the logs make sure it is working correctly.
- **"docker logs**" This will return the logs for a container
- **$container_id** The Id of the container we want the logs for.
<!-- -->
sudo docker attach --sig-proxy=false $container_id
Attach to the container to see the results in real-time.
- **"docker attach**" This will allow us to attach to a background
process to see what is going on.
- **"sig-proxy=false"** Do not forward signals to the container;
allows us to exit the attachment using Control-C without stopping
the container.
- **$container_id** The Id of the container we want to attach to.
Exit from the container attachment by pressing Control-C.
sudo docker ps
Check the process list to make sure it is running.
- **"docker ps"** this shows all running process managed by docker
<!-- -->
sudo docker stop $container_id
Stop the container, since we dont need it anymore.
- **"docker stop"** This stops a container
- **$container_id** The Id of the container we want to stop.
<!-- -->
sudo docker ps
Make sure it is really stopped.
**Video:**
See the example in action
<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/2562.js&quot;id=&quot;asciicast-2562&quot; async></script></body>"></iframe>
<iframe width="640" height="480" frameborder="0" sandbox="allow-same-origin allow-scripts" srcdoc="<body><script type=&quot;text/javascript&quot;src=&quot;https://asciinema.org/a/2562.js&quot;id=&quot;asciicast-2562&quot; async></script></body>"></iframe>
The next example in the series is a [*Node.js Web
App*](../nodejs_web_app/#nodejs-web-app) example, or you could skip to
any of the other examples:
- [*Node.js Web App*](../nodejs_web_app/#nodejs-web-app)
- [*Redis Service*](../running_redis_service/#running-redis-service)
- [*SSH Daemon Service*](../running_ssh_service/#running-ssh-service)
- [*CouchDB
Service*](../couchdb_data_volumes/#running-couchdb-service)
- [*PostgreSQL Service*](../postgresql_service/#postgresql-service)
- [*Building an Image with MongoDB*](../mongodb/#mongodb-image)
- [*Python Web App*](../python_web_app/#python-web-app)

View file

@ -0,0 +1,107 @@
page_title: Docker HTTPS Setup
page_description: How to setup docker with https
page_keywords: docker, example, https, daemon
# Running Docker with https
By default, Docker runs via a non-networked Unix socket. It can also
optionally communicate using a HTTP socket.
If you need Docker reachable via the network in a safe manner, you can
enable TLS by specifying the tlsverify flag and pointing Dockers
tlscacert flag to a trusted CA certificate.
In daemon mode, it will only allow connections from clients
authenticated by a certificate signed by that CA. In client mode, it
will only connect to servers with a certificate signed by that CA.
> **Warning**:
> Using TLS and managing a CA is an advanced topic. Please make you self
> familiar with openssl, x509 and tls before using it in production.
## Create a CA, server and client keys with OpenSSL
First, initialize the CA serial file and generate CA private and public
keys:
$ echo 01 > ca.srl
$ openssl genrsa -des3 -out ca-key.pem
$ openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem
Now that we have a CA, you can create a server key and certificate
signing request. Make sure that "Common Name (e.g. server FQDN or YOUR
name)" matches the hostname you will use to connect to Docker or just
use \* for a certificate valid for any hostname:
$ openssl genrsa -des3 -out server-key.pem
$ openssl req -new -key server-key.pem -out server.csr
Next were going to sign the key with our CA:
$ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-out server-cert.pem
For client authentication, create a client key and certificate signing
request:
$ openssl genrsa -des3 -out client-key.pem
$ openssl req -new -key client-key.pem -out client.csr
To make the key suitable for client authentication, create a extensions
config file:
$ echo extendedKeyUsage = clientAuth > extfile.cnf
Now sign the key:
$ openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem \
-out client-cert.pem -extfile extfile.cnf
Finally you need to remove the passphrase from the client and server
key:
$ openssl rsa -in server-key.pem -out server-key.pem
$ openssl rsa -in client-key.pem -out client-key.pem
Now you can make the Docker daemon only accept connections from clients
providing a certificate trusted by our CA:
$ sudo docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
-H=0.0.0.0:4243
To be able to connect to Docker and validate its certificate, you now
need to provide your client keys, certificates and trusted CA:
$ docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem \
-H=dns-name-of-docker-host:4243
> **Warning**:
> As shown in the example above, you dont have to run the
> `docker` client with `sudo` or
> the `docker` group when you use certificate
> authentication. That means anyone with the keys can give any
> instructions to your Docker daemon, giving them root access to the
> machine hosting the daemon. Guard these keys as you would a root
> password!
## Other modes
If you dont want to have complete two-way authentication, you can run
Docker in various other modes by mixing the flags.
### Daemon modes
- tlsverify, tlscacert, tlscert, tlskey set: Authenticate clients
- tls, tlscert, tlskey: Do not authenticate clients
### Client modes
- tls: Authenticate server based on public/default CA pool
- tlsverify, tlscacert: Authenticate server based on given CA
- tls, tlscert, tlskey: Authenticate with client certificate, do not
authenticate server based on given CA
- tlsverify, tlscacert, tlscert, tlskey: Authenticate with client
certificate, authenticate server based on given CA
The client will send its client certificate if found, so you just need
to drop your keys into \~/.docker/\<ca, cert or key\>.pem

View file

@ -0,0 +1,89 @@
page_title: Building a Docker Image with MongoDB
page_description: How to build a Docker image with MongoDB pre-installed
page_keywords: docker, example, package installation, networking, mongodb
# Building an Image with MongoDB
> **Note**:
>
> - This example assumes you have Docker running in daemon mode. For
> more information please see [*Check your Docker
> install*](../hello_world/#running-examples).
> - **If you dont like sudo** then see [*Giving non-root
> access*](../../installation/binaries/#dockergroup)
The goal of this example is to show how you can build your own Docker
images with MongoDB pre-installed. We will do that by constructing a
`Dockerfile` that downloads a base image, adds an
apt source and installs the database software on Ubuntu.
## Creating a `Dockerfile`
Create an empty file called `Dockerfile`:
touch Dockerfile
Next, define the parent image you want to use to build your own image on
top of. Here, well use [Ubuntu](https://index.docker.io/_/ubuntu/)
(tag: `latest`) available on the [docker
index](http://index.docker.io):
FROM ubuntu:latest
Since we want to be running the latest version of MongoDB well need to
add the 10gen repo to our apt sources list.
# Add 10gen official apt source to the sources list
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list
Then, we dont want Ubuntu to complain about init not being available so
well divert `/sbin/initctl` to
`/bin/true` so it thinks everything is working.
# Hack for initctl not being available in Ubuntu
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -s /bin/true /sbin/initctl
Afterwards well be able to update our apt repositories and install
MongoDB
# Install MongoDB
RUN apt-get update
RUN apt-get install mongodb-10gen
To run MongoDB well have to create the default data directory (because
we want it to run without needing to provide a special configuration
file)
# Create the MongoDB data directory
RUN mkdir -p /data/db
Finally, well expose the standard port that MongoDB runs on, 27107, as
well as define an `ENTRYPOINT` instruction for the
container.
EXPOSE 27017
ENTRYPOINT ["usr/bin/mongod"]
Now, lets build the image which will go through the
`Dockerfile` we made and run all of the commands.
sudo docker build -t <yourname>/mongodb .
Now you should be able to run `mongod` as a daemon
and be able to connect on the local port!
# Regular style
MONGO_ID=$(sudo docker run -d <yourname>/mongodb)
# Lean and mean
MONGO_ID=$(sudo docker run -d <yourname>/mongodb --noprealloc --smallfiles)
# Check the logs out
sudo docker logs $MONGO_ID
# Connect and play around
mongo --port <port you get from `docker ps`>
Sweet!

Some files were not shown because too many files have changed in this diff Show more