mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-04 20:51:44 +00:00
Compare commits
122
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7946ee2696 | ||
|
|
77f05fa886 | ||
|
|
f554d104fb | ||
|
|
f4bf5c7026 | ||
|
|
1321794dc1 | ||
|
|
668a1758bd | ||
|
|
6321ac9182 | ||
|
|
40164ebcc1 | ||
|
|
56d463690f | ||
|
|
9098628b29 | ||
|
|
a6e81cff5f | ||
|
|
023d4ed189 | ||
|
|
d7c5f31c5b | ||
|
|
0a8c2e3717 | ||
|
|
57e9c4f7e5 | ||
|
|
e51462ac68 | ||
|
|
e45d0eb532 | ||
|
|
3e8da36017 | ||
|
|
1cce9a26a3 | ||
|
|
d7f8b4d43e | ||
|
|
6f7bbc3171 | ||
|
|
947087fb24 | ||
|
|
ffe7e48ed6 | ||
|
|
eeecd1cf59 | ||
|
|
3f411db15b | ||
|
|
789197f33d | ||
|
|
0c71d09921 | ||
|
|
cc8320cb58 | ||
|
|
6bf1c41b77 | ||
|
|
c22b292719 | ||
|
|
14d2083f14 | ||
|
|
ea56c5e1ce | ||
|
|
341ff018a2 | ||
|
|
e07819293a | ||
|
|
6ec8d40ae7 | ||
|
|
16d64608f3 | ||
|
|
00a27b6872 | ||
|
|
fc12b9ddce | ||
|
|
b66e5ef208 | ||
|
|
d12ea79c9d | ||
|
|
a9aaa66780 | ||
|
|
e19060dcea | ||
|
|
b0e0dbb33b | ||
|
|
0d03096b65 | ||
|
|
55e9551aaa | ||
|
|
c65afe6ba8 | ||
|
|
5745aaed22 | ||
|
|
b6f0f93c94 | ||
|
|
33b16fef43 | ||
|
|
9705c349c5 | ||
|
|
3de34af5d1 | ||
|
|
b3f3abfc94 | ||
|
|
783baec49c | ||
|
|
c1d9e7c6fb | ||
|
|
83f6dbe30a | ||
|
|
a97b89b585 | ||
|
|
29ea36a880 | ||
|
|
ed672d1609 | ||
|
|
5916664220 | ||
|
|
da4b336233 | ||
|
|
74df05ccaa | ||
|
|
2c875215b1 | ||
|
|
be40a48c12 | ||
|
|
85f7f7cfc7 | ||
|
|
e15f6fca3f | ||
|
|
d3bbaa70cd | ||
|
|
cc6f6cb2e2 | ||
|
|
c967dd289f | ||
|
|
7895ec25ea | ||
|
|
5b06c94701 | ||
|
|
5851e2da60 | ||
|
|
9eff33735a | ||
|
|
fc7697b050 | ||
|
|
1bf8954d0d | ||
|
|
dfd9f5989a | ||
|
|
d9581e861d | ||
|
|
c383ceaf37 | ||
|
|
948912f692 | ||
|
|
d19b1b927b | ||
|
|
53f5905379 | ||
|
|
60cbf4da6c | ||
|
|
183628388c | ||
|
|
fbd2267e7d | ||
|
|
a16ab243e5 | ||
|
|
b3c3c4cddc | ||
|
|
0fe5aad984 | ||
|
|
0f5e2fd479 | ||
|
|
2f7145b1c5 | ||
|
|
81efe1f32e | ||
|
|
5ba75ac343 | ||
|
|
290987fcb4 | ||
|
|
98855c863d | ||
|
|
b1f394a247 | ||
|
|
a819a60a94 | ||
|
|
33cdc7f2c4 | ||
|
|
117860577c | ||
|
|
b0ac5df367 | ||
|
|
c109095a58 | ||
|
|
d394113dfe | ||
|
|
2af7f63173 | ||
|
|
f156fb7be5 | ||
|
|
559043b953 | ||
|
|
ba8abcb3dd | ||
|
|
ebf396c6e8 | ||
|
|
47d52fb872 | ||
|
|
d167338876 | ||
|
|
e6844381f0 | ||
|
|
589922adf0 | ||
|
|
689c4e6075 | ||
|
|
43da1adedb | ||
|
|
686fe02020 | ||
|
|
1d02be1c7a | ||
|
|
edb60b950a | ||
|
|
e0e852ee6f | ||
|
|
b537508f8c | ||
|
|
37e886eb7b | ||
|
|
50f65742ef | ||
|
|
56d859d052 | ||
|
|
546a704c63 | ||
|
|
fa85dc0030 | ||
|
|
36b6e5884d | ||
|
|
90991ddb9b |
+136
-4
@@ -1,5 +1,137 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.8.3 (2015-10-12)
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
- Fix layer IDs lead to local graph poisoning (CVE-2014-8178)
|
||||||
|
- Fix manifest validation and parsing logic errors allow pull-by-digest validation bypass (CVE-2014-8179)
|
||||||
|
+ Add `--disable-legacy-registry` to prevent a daemon from using a v1 registry
|
||||||
|
|
||||||
|
## 1.8.2 (2015-09-10)
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
- Fixes rare edge case of handling GNU LongLink and LongName entries.
|
||||||
|
- Fix ^C on docker pull.
|
||||||
|
- Fix docker pull issues on client disconnection.
|
||||||
|
- Fix issue that caused the daemon to panic when loggers weren't configured properly.
|
||||||
|
- Fix goroutine leak pulling images from registry V2.
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
|
||||||
|
- Fix a bug mounting cgroups for docker daemons running inside docker containers.
|
||||||
|
- Initialize log configuration properly.
|
||||||
|
|
||||||
|
### Client:
|
||||||
|
|
||||||
|
- Handle `-q` flag in `docker ps` properly when there is a default format.
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
|
||||||
|
- Fix several corner cases with netlink.
|
||||||
|
|
||||||
|
### Contrib
|
||||||
|
|
||||||
|
- Fix several issues with bash completion.
|
||||||
|
|
||||||
|
## 1.8.1 (2015-08-12)
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
- Fix a bug where pushing multiple tags would result in invalid images
|
||||||
|
|
||||||
|
## 1.8.0 (2015-08-11)
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
+ Trusted pull, push and build, disabled by default
|
||||||
|
* Make tar layers deterministic between registries
|
||||||
|
* Don't allow deleting the image of running containers
|
||||||
|
* Check if a tag name to load is a valid digest
|
||||||
|
* Allow one character repository names
|
||||||
|
* Add a more accurate error description for invalid tag name
|
||||||
|
* Make build cache ignore mtime
|
||||||
|
|
||||||
|
### Cli
|
||||||
|
|
||||||
|
+ Add support for DOCKER_CONFIG/--config to specify config file dir
|
||||||
|
+ Add --type flag for docker inspect command
|
||||||
|
+ Add formatting options to `docker ps` with `--format`
|
||||||
|
+ Replace `docker -d` with new subcommand `docker daemon`
|
||||||
|
* Zsh completion updates and improvements
|
||||||
|
* Add some missing events to bash completion
|
||||||
|
* Support daemon urls with base paths in `docker -H`
|
||||||
|
* Validate status= filter to docker ps
|
||||||
|
* Display when a container is in --net=host in docker ps
|
||||||
|
* Extend docker inspect to export image metadata related to graph driver
|
||||||
|
* Restore --default-gateway{,-v6} daemon options
|
||||||
|
* Add missing unpublished ports in docker ps
|
||||||
|
* Allow duration strings in `docker events` as --since/--until
|
||||||
|
* Expose more mounts information in `docker inspect`
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
|
||||||
|
+ Add new Fluentd logging driver
|
||||||
|
+ Allow `docker import` to load from local files
|
||||||
|
+ Add logging driver for GELF via UDP
|
||||||
|
+ Allow to copy files from host to containers with `docker cp`
|
||||||
|
+ Promote volume drivers from experimental to master
|
||||||
|
+ Add rollover log driver, and --log-driver-opts flag
|
||||||
|
+ Add memory swappiness tuning options
|
||||||
|
* Remove cgroup read-only flag when privileged
|
||||||
|
* Make /proc, /sys, & /dev readonly for readonly containers
|
||||||
|
* Add cgroup bind mount by default
|
||||||
|
* Overlay: Export metadata for container and image in `docker inspect`
|
||||||
|
* Devicemapper: external device activation
|
||||||
|
* Devicemapper: Compare uuid of base device on startup
|
||||||
|
* Remove RC4 from the list of registry cipher suites
|
||||||
|
* Add syslog-facility option
|
||||||
|
* LXC execdriver compatibility with recent LXC versions
|
||||||
|
* Mark LXC execriver as deprecated (to be removed with the migration to runc)
|
||||||
|
|
||||||
|
### Plugins
|
||||||
|
|
||||||
|
* Separate plugin sockets and specs locations
|
||||||
|
* Allow TLS connections to plugins
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Add missing 'Names' field to /containers/json API output
|
||||||
|
- Make `docker rmi --dangling` safe when pulling
|
||||||
|
- Devicemapper: Change default basesize to 100G
|
||||||
|
- Go Scheduler issue with sync.Mutex and gcc
|
||||||
|
- Fix issue where Search API endpoint would panic due to empty AuthConfig
|
||||||
|
- Set image canonical names correctly
|
||||||
|
- Check dockerinit only if lxc driver is used
|
||||||
|
- Fix ulimit usage of nproc
|
||||||
|
- Always attach STDIN if -i,--interactive is specified
|
||||||
|
- Show error messages when saving container state fails
|
||||||
|
- Fixed incorrect assumption on --bridge=none treated as disable network
|
||||||
|
- Check for invalid port specifications in host configuration
|
||||||
|
- Fix endpoint leave failure for --net=host mode
|
||||||
|
- Fix goroutine leak in the stats API if the container is not running
|
||||||
|
- Check for apparmor file before reading it
|
||||||
|
- Fix DOCKER_TLS_VERIFY being ignored
|
||||||
|
- Set umask to the default on startup
|
||||||
|
- Correct the message of pause and unpause a non-running container
|
||||||
|
- Adjust disallowed CpuShares in container creation
|
||||||
|
- ZFS: correctly apply selinux context
|
||||||
|
- Display empty string instead of <nil> when IP opt is nil
|
||||||
|
- `docker kill` returns error when container is not running
|
||||||
|
- Fix COPY/ADD quoted/json form
|
||||||
|
- Fix goroutine leak on logs -f with no output
|
||||||
|
- Remove panic in nat package on invalid hostport
|
||||||
|
- Fix container linking in Fedora 22
|
||||||
|
- Fix error caused using default gateways outside of the allocated range
|
||||||
|
- Format times in inspect command with a template as RFC3339Nano
|
||||||
|
- Make registry client to accept 2xx and 3xx http status responses as successful
|
||||||
|
- Fix race issue that caused the daemon to crash with certain layer downloads failed in a specific order.
|
||||||
|
- Fix error when the docker ps format was not valid.
|
||||||
|
- Remove redundant ip forward check.
|
||||||
|
- Fix issue trying to push images to repository mirrors.
|
||||||
|
- Fix error cleaning up network entrypoints when there is an initialization issue.
|
||||||
|
|
||||||
## 1.7.1 (2015-07-14)
|
## 1.7.1 (2015-07-14)
|
||||||
|
|
||||||
#### Runtime
|
#### Runtime
|
||||||
@@ -144,7 +276,7 @@
|
|||||||
#### Notable Features since 1.3.0
|
#### Notable Features since 1.3.0
|
||||||
+ Set key=value labels to the daemon (displayed in `docker info`), applied with
|
+ Set key=value labels to the daemon (displayed in `docker info`), applied with
|
||||||
new `-label` daemon flag
|
new `-label` daemon flag
|
||||||
+ Add support for `ENV` in Dockerfile of the form:
|
+ Add support for `ENV` in Dockerfile of the form:
|
||||||
`ENV name=value name2=value2...`
|
`ENV name=value name2=value2...`
|
||||||
+ New Overlayfs Storage Driver
|
+ New Overlayfs Storage Driver
|
||||||
+ `docker info` now returns an `ID` and `Name` field
|
+ `docker info` now returns an `ID` and `Name` field
|
||||||
@@ -606,7 +738,7 @@
|
|||||||
- Fix broken images API for version less than 1.7
|
- Fix broken images API for version less than 1.7
|
||||||
- Use the right encoding for all API endpoints which return JSON
|
- Use the right encoding for all API endpoints which return JSON
|
||||||
- Move remote api client to api/
|
- Move remote api client to api/
|
||||||
- Queue calls to the API using generic socket wait
|
- Queue calls to the API using generic socket wait
|
||||||
|
|
||||||
#### Runtime
|
#### Runtime
|
||||||
|
|
||||||
@@ -686,7 +818,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
|||||||
- Do not add hostname when networking is disabled
|
- Do not add hostname when networking is disabled
|
||||||
* Return most recent image from the cache by date
|
* Return most recent image from the cache by date
|
||||||
- Return all errors from docker wait
|
- Return all errors from docker wait
|
||||||
* Add Content-Type Header "application/json" to GET /version and /info responses
|
* Add Content-Type Header "application/json" to GET /version and /info responses
|
||||||
|
|
||||||
#### Other
|
#### Other
|
||||||
|
|
||||||
@@ -714,7 +846,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
|||||||
#### Runtime
|
#### Runtime
|
||||||
|
|
||||||
- Only get the image's rootfs when we need to calculate the image size
|
- Only get the image's rootfs when we need to calculate the image size
|
||||||
- Correctly handle unmapping UDP ports
|
- Correctly handle unmapping UDP ports
|
||||||
* Make CopyFileWithTar use a pipe instead of a buffer to save memory on docker build
|
* Make CopyFileWithTar use a pipe instead of a buffer to save memory on docker build
|
||||||
- Fix login message to say pull instead of push
|
- Fix login message to say pull instead of push
|
||||||
- Fix "docker load" help by removing "SOURCE" prompt and mentioning STDIN
|
- Fix "docker load" help by removing "SOURCE" prompt and mentioning STDIN
|
||||||
|
|||||||
+8
-2
@@ -58,6 +58,12 @@ RUN apt-get update && apt-get install -y \
|
|||||||
libzfs-dev \
|
libzfs-dev \
|
||||||
--no-install-recommends
|
--no-install-recommends
|
||||||
|
|
||||||
|
# clr
|
||||||
|
RUN echo deb http://download.opensuse.org/repositories/home:/clearlinux:/preview/xUbuntu_15.04/ / > /etc/apt/sources.list.d/clear-containers-docker.list
|
||||||
|
RUN apt-get --allow-unauthenticated update && apt-get install -y --force-yes \
|
||||||
|
kvmtool\
|
||||||
|
linux-container
|
||||||
|
|
||||||
# Get lvm2 source for compiling statically
|
# Get lvm2 source for compiling statically
|
||||||
RUN git clone -b v2_02_103 https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2
|
RUN git clone -b v2_02_103 https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2
|
||||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
@@ -127,7 +133,7 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
|||||||
RUN gem install --no-rdoc --no-ri fpm --version 1.3.2
|
RUN gem install --no-rdoc --no-ri fpm --version 1.3.2
|
||||||
|
|
||||||
# Install registry
|
# Install registry
|
||||||
ENV REGISTRY_COMMIT 2317f721a3d8428215a2b65da4ae85212ed473b4
|
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
@@ -137,7 +143,7 @@ RUN set -x \
|
|||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_COMMIT 77bced079e83d80f40c1f0a544b1a8a3b97fb052
|
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ databases, and backend services without depending on a particular stack
|
|||||||
or provider.
|
or provider.
|
||||||
|
|
||||||
Docker began as an open-source implementation of the deployment engine which
|
Docker began as an open-source implementation of the deployment engine which
|
||||||
powers [dotCloud](https://dotcloud.com), a popular Platform-as-a-Service.
|
powers [dotCloud](https://www.dotcloud.com), a popular Platform-as-a-Service.
|
||||||
It benefits directly from the experience accumulated over several years
|
It benefits directly from the experience accumulated over several years
|
||||||
of large-scale operation and support of hundreds of thousands of
|
of large-scale operation and support of hundreds of thousands of
|
||||||
applications and databases.
|
applications and databases.
|
||||||
|
|||||||
+57
-9
@@ -115,8 +115,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the FROM lines in the Dockerfile to trusted digest references
|
// Resolve the FROM lines in the Dockerfile to trusted digest references
|
||||||
// using Notary.
|
// using Notary. On a successful build, we must tag the resolved digests
|
||||||
newDockerfile, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference)
|
// to the original name specified in the Dockerfile.
|
||||||
|
newDockerfile, resolvedTags, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to process Dockerfile: %v", err)
|
return fmt.Errorf("unable to process Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
@@ -291,7 +292,20 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
}
|
}
|
||||||
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the build was successful, now we must tag any of the resolved
|
||||||
|
// images from the above Dockerfile rewrite.
|
||||||
|
for _, resolved := range resolvedTags {
|
||||||
|
if err := cli.tagTrusted(resolved.repoInfo, resolved.digestRef, resolved.tagRef); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDockerfileRelPath uses the given context directory for a `docker build`
|
// getDockerfileRelPath uses the given context directory for a `docker build`
|
||||||
@@ -302,6 +316,22 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
|||||||
return "", "", fmt.Errorf("unable to get absolute context directory: %v", err)
|
return "", "", fmt.Errorf("unable to get absolute context directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The context dir might be a symbolic link, so follow it to the actual
|
||||||
|
// target directory.
|
||||||
|
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, err := os.Lstat(absContextDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to stat context directory %q: %v", absContextDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stat.IsDir() {
|
||||||
|
return "", "", fmt.Errorf("context must be a directory: %s", absContextDir)
|
||||||
|
}
|
||||||
|
|
||||||
absDockerfile := givenDockerfile
|
absDockerfile := givenDockerfile
|
||||||
if absDockerfile == "" {
|
if absDockerfile == "" {
|
||||||
// No -f/--file was specified so use the default relative to the
|
// No -f/--file was specified so use the default relative to the
|
||||||
@@ -467,14 +497,21 @@ func (td *trustedDockerfile) Close() error {
|
|||||||
return os.Remove(td.File.Name())
|
return os.Remove(td.File.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolvedTag records the repository, tag, and resolved digest reference
|
||||||
|
// from a Dockerfile rewrite.
|
||||||
|
type resolvedTag struct {
|
||||||
|
repoInfo *registry.RepositoryInfo
|
||||||
|
digestRef, tagRef registry.Reference
|
||||||
|
}
|
||||||
|
|
||||||
// rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in
|
// rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in
|
||||||
// "FROM <image>" instructions to a digest reference. `translator` is a
|
// "FROM <image>" instructions to a digest reference. `translator` is a
|
||||||
// function that takes a repository name and tag reference and returns a
|
// function that takes a repository name and tag reference and returns a
|
||||||
// trusted digest reference.
|
// trusted digest reference.
|
||||||
func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, err error) {
|
func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, resolvedTags []*resolvedTag, err error) {
|
||||||
dockerfile, err := os.Open(dockerfileName)
|
dockerfile, err := os.Open(dockerfileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to open Dockerfile: %v", err)
|
return nil, nil, fmt.Errorf("unable to open Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
defer dockerfile.Close()
|
defer dockerfile.Close()
|
||||||
|
|
||||||
@@ -483,7 +520,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
|||||||
// Make a tempfile to store the rewritten Dockerfile.
|
// Make a tempfile to store the rewritten Dockerfile.
|
||||||
tempFile, err := ioutil.TempFile("", "trusted-dockerfile-")
|
tempFile, err := ioutil.TempFile("", "trusted-dockerfile-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err)
|
return nil, nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
trustedFile := &trustedDockerfile{
|
trustedFile := &trustedDockerfile{
|
||||||
@@ -509,21 +546,32 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
|||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = tags.DEFAULTTAG
|
tag = tags.DEFAULTTAG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoInfo, err := registry.ParseRepositoryInfo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to parse repository info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ref := registry.ParseReference(tag)
|
ref := registry.ParseReference(tag)
|
||||||
|
|
||||||
if !ref.HasDigest() && isTrusted() {
|
if !ref.HasDigest() && isTrusted() {
|
||||||
trustedRef, err := translator(repo, ref)
|
trustedRef, err := translator(repo, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.ImageName(repo)))
|
line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.ImageName(repo)))
|
||||||
|
resolvedTags = append(resolvedTags, &resolvedTag{
|
||||||
|
repoInfo: repoInfo,
|
||||||
|
digestRef: trustedRef,
|
||||||
|
tagRef: ref,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := fmt.Fprintln(tempFile, line)
|
n, err := fmt.Fprintln(tempFile, line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
trustedFile.size += int64(n)
|
trustedFile.size += int64(n)
|
||||||
@@ -531,7 +579,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
|||||||
|
|
||||||
tempFile.Seek(0, os.SEEK_SET)
|
tempFile.Seek(0, os.SEEK_SET)
|
||||||
|
|
||||||
return trustedFile, scanner.Err()
|
return trustedFile, resolvedTags, scanner.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// replaceDockerfileTarWrapper wraps the given input tar archive stream and
|
// replaceDockerfileTarWrapper wraps the given input tar archive stream and
|
||||||
|
|||||||
+29
-10
@@ -232,6 +232,20 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
|||||||
// Prepare destination copy info by stat-ing the container path.
|
// Prepare destination copy info by stat-ing the container path.
|
||||||
dstInfo := archive.CopyInfo{Path: dstPath}
|
dstInfo := archive.CopyInfo{Path: dstPath}
|
||||||
dstStat, err := cli.statContainerPath(dstContainer, dstPath)
|
dstStat, err := cli.statContainerPath(dstContainer, dstPath)
|
||||||
|
|
||||||
|
// If the destination is a symbolic link, we should evaluate it.
|
||||||
|
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
||||||
|
linkTarget := dstStat.LinkTarget
|
||||||
|
if !filepath.IsAbs(linkTarget) {
|
||||||
|
// Join with the parent directory.
|
||||||
|
dstParent, _ := archive.SplitPathDirEntry(dstPath)
|
||||||
|
linkTarget = filepath.Join(dstParent, linkTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
dstInfo.Path = linkTarget
|
||||||
|
dstStat, err = cli.statContainerPath(dstContainer, linkTarget)
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore any error and assume that the parent directory of the destination
|
// Ignore any error and assume that the parent directory of the destination
|
||||||
// path exists, in which case the copy may still succeed. If there is any
|
// path exists, in which case the copy may still succeed. If there is any
|
||||||
// type of conflict (e.g., non-directory overwriting an existing directory
|
// type of conflict (e.g., non-directory overwriting an existing directory
|
||||||
@@ -242,15 +256,26 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
|||||||
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
|
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
var content io.Reader
|
var (
|
||||||
|
content io.Reader
|
||||||
|
resolvedDstPath string
|
||||||
|
)
|
||||||
|
|
||||||
if srcPath == "-" {
|
if srcPath == "-" {
|
||||||
// Use STDIN.
|
// Use STDIN.
|
||||||
content = os.Stdin
|
content = os.Stdin
|
||||||
|
resolvedDstPath = dstInfo.Path
|
||||||
if !dstInfo.IsDir {
|
if !dstInfo.IsDir {
|
||||||
return fmt.Errorf("destination %q must be a directory", fmt.Sprintf("%s:%s", dstContainer, dstPath))
|
return fmt.Errorf("destination %q must be a directory", fmt.Sprintf("%s:%s", dstContainer, dstPath))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
srcArchive, err := archive.TarResource(srcPath)
|
// Prepare source copy info.
|
||||||
|
srcInfo, err := archive.CopyInfoSourcePath(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
srcArchive, err := archive.TarResource(srcInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -262,12 +287,6 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
|||||||
// it to the specified directory in the container we get the disired
|
// it to the specified directory in the container we get the disired
|
||||||
// copy behavior.
|
// copy behavior.
|
||||||
|
|
||||||
// Prepare source copy info.
|
|
||||||
srcInfo, err := archive.CopyInfoStatPath(srcPath, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// See comments in the implementation of `archive.PrepareArchiveCopy`
|
// See comments in the implementation of `archive.PrepareArchiveCopy`
|
||||||
// for exactly what goes into deciding how and whether the source
|
// for exactly what goes into deciding how and whether the source
|
||||||
// archive needs to be altered for the correct copy behavior when it is
|
// archive needs to be altered for the correct copy behavior when it is
|
||||||
@@ -280,12 +299,12 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
|||||||
}
|
}
|
||||||
defer preparedArchive.Close()
|
defer preparedArchive.Close()
|
||||||
|
|
||||||
dstPath = dstDir
|
resolvedDstPath = dstDir
|
||||||
content = preparedArchive
|
content = preparedArchive
|
||||||
}
|
}
|
||||||
|
|
||||||
query := make(url.Values, 2)
|
query := make(url.Values, 2)
|
||||||
query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
|
query.Set("path", filepath.ToSlash(resolvedDstPath)) // Normalize the paths used in the API.
|
||||||
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
||||||
query.Set("noOverwriteDirNonDir", "true")
|
query.Set("noOverwriteDirNonDir", "true")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
|||||||
|
|
||||||
f := *format
|
f := *format
|
||||||
if len(f) == 0 {
|
if len(f) == 0 {
|
||||||
if len(cli.PsFormat()) > 0 {
|
if len(cli.PsFormat()) > 0 && !*quiet {
|
||||||
f = cli.PsFormat()
|
f = cli.PsFormat()
|
||||||
} else {
|
} else {
|
||||||
f = "table"
|
f = "table"
|
||||||
|
|||||||
@@ -170,9 +170,11 @@ func customFormat(ctx Context, containers []types.Container) {
|
|||||||
format += "\t{{.Size}}"
|
format += "\t{{.Size}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("ps template").Parse(format)
|
tmpl, err := template.New("").Parse(format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buffer.WriteString(fmt.Sprintf("Invalid `docker ps` format: %v\n", err))
|
buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||||
|
buffer.WriteTo(ctx.Output)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
@@ -181,8 +183,9 @@ func customFormat(ctx Context, containers []types.Container) {
|
|||||||
c: container,
|
c: container,
|
||||||
}
|
}
|
||||||
if err := tmpl.Execute(buffer, containerCtx); err != nil {
|
if err := tmpl.Execute(buffer, containerCtx); err != nil {
|
||||||
buffer = bytes.NewBufferString(fmt.Sprintf("Invalid `docker ps` format: %v\n", err))
|
buffer = bytes.NewBufferString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||||
break
|
buffer.WriteTo(ctx.Output)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if table && len(header) == 0 {
|
if table && len(header) == 0 {
|
||||||
header = containerCtx.fullHeader()
|
header = containerCtx.fullHeader()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ps
|
package ps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -10,7 +11,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContainerContextID(t *testing.T) {
|
func TestContainerPsContext(t *testing.T) {
|
||||||
containerId := stringid.GenerateRandomID()
|
containerId := stringid.GenerateRandomID()
|
||||||
unix := time.Now().Unix()
|
unix := time.Now().Unix()
|
||||||
|
|
||||||
@@ -86,3 +87,16 @@ func TestContainerContextID(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContainerPsFormatError(t *testing.T) {
|
||||||
|
out := bytes.NewBufferString("")
|
||||||
|
ctx := Context{
|
||||||
|
Format: "{{InvalidFunction}}",
|
||||||
|
Output: out,
|
||||||
|
}
|
||||||
|
|
||||||
|
customFormat(ctx, make([]types.Container, 0))
|
||||||
|
if out.String() != "Template parsing error: template: :1: function \"InvalidFunction\" not defined\n" {
|
||||||
|
t.Fatalf("Expected format error, got `%v`\n", out.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+28
-9
@@ -13,6 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -176,11 +177,16 @@ func convertTarget(t client.Target) (target, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||||
baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out)
|
aliasMap := map[string]string{
|
||||||
|
"root": "offline",
|
||||||
|
"snapshot": "tagging",
|
||||||
|
"targets": "tagging",
|
||||||
|
}
|
||||||
|
baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out, aliasMap)
|
||||||
env := map[string]string{
|
env := map[string]string{
|
||||||
"root": os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
|
"root": os.Getenv("DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE"),
|
||||||
"targets": os.Getenv("DOCKER_CONTENT_TRUST_TARGET_PASSPHRASE"),
|
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"),
|
||||||
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_SNAPSHOT_PASSPHRASE"),
|
"targets": os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"),
|
||||||
}
|
}
|
||||||
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
||||||
if v := env[alias]; v != "" {
|
if v := env[alias]; v != "" {
|
||||||
@@ -311,6 +317,22 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func selectKey(keys map[string]string) string {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
keyIDs := []string{}
|
||||||
|
for k := range keys {
|
||||||
|
keyIDs = append(keyIDs, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dmcgowan): let user choose if multiple keys, now pick consistently
|
||||||
|
sort.Strings(keyIDs)
|
||||||
|
|
||||||
|
return keyIDs[0]
|
||||||
|
}
|
||||||
|
|
||||||
func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
|
func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
|
||||||
r, w := io.Pipe()
|
r, w := io.Pipe()
|
||||||
out := io.MultiWriter(in, w)
|
out := io.MultiWriter(in, w)
|
||||||
@@ -409,16 +431,13 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
|
|||||||
|
|
||||||
ks := repo.KeyStoreManager
|
ks := repo.KeyStoreManager
|
||||||
keys := ks.RootKeyStore().ListKeys()
|
keys := ks.RootKeyStore().ListKeys()
|
||||||
var rootKey string
|
|
||||||
|
|
||||||
if len(keys) == 0 {
|
rootKey := selectKey(keys)
|
||||||
|
if rootKey == "" {
|
||||||
rootKey, err = ks.GenRootKey("ecdsa")
|
rootKey, err = ks.GenRootKey("ecdsa")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// TODO(dmcgowan): let user choose
|
|
||||||
rootKey = keys[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoService, err := ks.GetRootCryptoService(rootKey)
|
cryptoService, err := ks.GetRootCryptoService(rootKey)
|
||||||
|
|||||||
@@ -298,7 +298,13 @@ func (s *Server) postContainersKill(version version.Version, w http.ResponseWrit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.daemon.ContainerKill(name, sig); err != nil {
|
if err := s.daemon.ContainerKill(name, sig); err != nil {
|
||||||
return err
|
_, isStopped := err.(daemon.ErrContainerNotRunning)
|
||||||
|
// Return error that's not caused because the container is stopped.
|
||||||
|
// Return error if the container is not running and the api is >= 1.20
|
||||||
|
// to keep backwards compatibility.
|
||||||
|
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
||||||
|
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func allocateDaemonPort(addr string) error {
|
|||||||
|
|
||||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
||||||
if version.LessThan("1.19") {
|
if version.LessThan("1.19") {
|
||||||
if hostConfig.CpuShares > 0 {
|
if hostConfig != nil && hostConfig.CpuShares > 0 {
|
||||||
// Handle unsupported CpuShares
|
// Handle unsupported CpuShares
|
||||||
if hostConfig.CpuShares < linuxMinCpuShares {
|
if hostConfig.CpuShares < linuxMinCpuShares {
|
||||||
logrus.Warnf("Changing requested CpuShares of %d to minimum allowed of %d", hostConfig.CpuShares, linuxMinCpuShares)
|
logrus.Warnf("Changing requested CpuShares of %d to minimum allowed of %d", hostConfig.CpuShares, linuxMinCpuShares)
|
||||||
|
|||||||
+10
-11
@@ -86,7 +86,7 @@ type ImageInspect struct {
|
|||||||
Id string
|
Id string
|
||||||
Parent string
|
Parent string
|
||||||
Comment string
|
Comment string
|
||||||
Created time.Time
|
Created string
|
||||||
Container string
|
Container string
|
||||||
ContainerConfig *runconfig.Config
|
ContainerConfig *runconfig.Config
|
||||||
DockerVersion string
|
DockerVersion string
|
||||||
@@ -130,14 +130,13 @@ type CopyConfig struct {
|
|||||||
|
|
||||||
// ContainerPathStat is used to encode the header from
|
// ContainerPathStat is used to encode the header from
|
||||||
// GET /containers/{name:.*}/archive
|
// GET /containers/{name:.*}/archive
|
||||||
// "name" is the file or directory name.
|
// "name" is basename of the resource.
|
||||||
// "path" is the absolute path to the resource in the container.
|
|
||||||
type ContainerPathStat struct {
|
type ContainerPathStat struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Size int64 `json:"size"`
|
||||||
Size int64 `json:"size"`
|
Mode os.FileMode `json:"mode"`
|
||||||
Mode os.FileMode `json:"mode"`
|
Mtime time.Time `json:"mtime"`
|
||||||
Mtime time.Time `json:"mtime"`
|
LinkTarget string `json:"linkTarget"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET "/containers/{name:.*}/top"
|
// GET "/containers/{name:.*}/top"
|
||||||
@@ -215,14 +214,14 @@ type ContainerState struct {
|
|||||||
Pid int
|
Pid int
|
||||||
ExitCode int
|
ExitCode int
|
||||||
Error string
|
Error string
|
||||||
StartedAt time.Time
|
StartedAt string
|
||||||
FinishedAt time.Time
|
FinishedAt string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET "/containers/{name:.*}/json"
|
// GET "/containers/{name:.*}/json"
|
||||||
type ContainerJSONBase struct {
|
type ContainerJSONBase struct {
|
||||||
Id string
|
Id string
|
||||||
Created time.Time
|
Created string
|
||||||
Path string
|
Path string
|
||||||
Args []string
|
Args []string
|
||||||
State *ContainerState
|
State *ContainerState
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#include <tunables/global>
|
|
||||||
|
|
||||||
profile docker-default flags=(attach_disconnected,mediate_deleted) {
|
|
||||||
#include <abstractions/base>
|
|
||||||
|
|
||||||
network,
|
|
||||||
capability,
|
|
||||||
file,
|
|
||||||
umount,
|
|
||||||
|
|
||||||
deny @{PROC}/sys/fs/** wklx,
|
|
||||||
deny @{PROC}/sysrq-trigger rwklx,
|
|
||||||
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
|
|
||||||
deny @{PROC}/sys/kernel/*/** wklx,
|
|
||||||
|
|
||||||
deny mount,
|
|
||||||
|
|
||||||
deny /sys/[^f]*/** wklx,
|
|
||||||
deny /sys/f[^s]*/** wklx,
|
|
||||||
deny /sys/fs/[^c]*/** wklx,
|
|
||||||
deny /sys/fs/c[^g]*/** wklx,
|
|
||||||
deny /sys/fs/cg[^r]*/** wklx,
|
|
||||||
deny /sys/firmware/efi/efivars/** rwklx,
|
|
||||||
deny /sys/kernel/security/** rwklx,
|
|
||||||
}
|
|
||||||
+110
-30
@@ -1,6 +1,6 @@
|
|||||||
@{DOCKER_GRAPH_PATH}=/var/lib/docker
|
@{DOCKER_GRAPH_PATH}=/var/lib/docker
|
||||||
|
|
||||||
profile /usr/bin/docker (attach_disconnected) {
|
profile /usr/bin/docker (attach_disconnected, complain) {
|
||||||
# Prevent following links to these files during container setup.
|
# Prevent following links to these files during container setup.
|
||||||
deny /etc/** mkl,
|
deny /etc/** mkl,
|
||||||
deny /dev/** kl,
|
deny /dev/** kl,
|
||||||
@@ -21,51 +21,131 @@ profile /usr/bin/docker (attach_disconnected) {
|
|||||||
ipc rw,
|
ipc rw,
|
||||||
network,
|
network,
|
||||||
capability,
|
capability,
|
||||||
file,
|
owner /** rw,
|
||||||
|
/var/lib/docker/** rwl,
|
||||||
|
|
||||||
|
# For non-root client use:
|
||||||
|
/dev/urandom r,
|
||||||
|
/run/docker.sock rw,
|
||||||
|
/proc/** r,
|
||||||
|
/sys/kernel/mm/hugepages/ r,
|
||||||
|
/etc/localtime r,
|
||||||
|
|
||||||
ptrace peer=@{profile_name},
|
ptrace peer=@{profile_name},
|
||||||
|
ptrace (read) peer=docker-default,
|
||||||
|
deny ptrace (trace) peer=docker-default,
|
||||||
|
deny ptrace peer=/usr/bin/docker///bin/ps,
|
||||||
|
|
||||||
/usr/bin/docker pix,
|
/usr/bin/docker pix,
|
||||||
/sbin/xtables-multi rCix,
|
/sbin/xtables-multi rCx,
|
||||||
/sbin/iptables rCx,
|
/sbin/iptables rCx,
|
||||||
/sbin/modprobe rCx,
|
/sbin/modprobe rCx,
|
||||||
/sbin/auplink rCx,
|
/sbin/auplink rCx,
|
||||||
|
/bin/kmod rCx,
|
||||||
/usr/bin/xz rCx,
|
/usr/bin/xz rCx,
|
||||||
|
/bin/ps rCx,
|
||||||
|
/bin/cat rCx,
|
||||||
|
/sbin/zfs rCx,
|
||||||
|
|
||||||
# Transitions
|
# Transitions
|
||||||
change_profile -> docker-*,
|
change_profile -> docker-*,
|
||||||
change_profile -> unconfined,
|
change_profile -> unconfined,
|
||||||
|
|
||||||
profile /sbin/iptables {
|
profile /bin/cat (complain) {
|
||||||
signal (receive) peer=/usr/bin/docker,
|
/etc/ld.so.cache r,
|
||||||
capability net_admin,
|
/lib/** r,
|
||||||
}
|
/dev/null rw,
|
||||||
profile /sbin/auplink flags=(attach_disconnected) {
|
/proc r,
|
||||||
signal (receive) peer=/usr/bin/docker,
|
/bin/cat mr,
|
||||||
capability sys_admin,
|
|
||||||
capability dac_override,
|
|
||||||
|
|
||||||
@{DOCKER_GRAPH_PATH}/aufs/** rw,
|
# For reading in 'docker stats':
|
||||||
# For user namespaces:
|
/proc/[0-9]*/net/dev r,
|
||||||
@{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,
|
|
||||||
|
|
||||||
# The following may be removed via delegates
|
|
||||||
/sys/fs/aufs/** r,
|
|
||||||
/lib/** r,
|
|
||||||
/apparmor/.null r,
|
|
||||||
/dev/null rw,
|
|
||||||
/etc/ld.so.cache r,
|
|
||||||
/sbin/auplink rm,
|
|
||||||
/proc/fs/aufs/** rw,
|
|
||||||
/proc/[0-9]*/mounts rw,
|
|
||||||
}
|
}
|
||||||
profile /sbin/modprobe {
|
profile /bin/ps (complain) {
|
||||||
signal (receive) peer=/usr/bin/docker,
|
/etc/ld.so.cache r,
|
||||||
capability sys_module,
|
/etc/localtime r,
|
||||||
file,
|
/etc/passwd r,
|
||||||
|
/etc/nsswitch.conf r,
|
||||||
|
/lib/** r,
|
||||||
|
/proc/[0-9]*/** r,
|
||||||
|
/dev/null rw,
|
||||||
|
/bin/ps mr,
|
||||||
|
|
||||||
|
# We don't need ptrace so we'll deny and ignore the error.
|
||||||
|
deny ptrace (read, trace),
|
||||||
|
|
||||||
|
# Quiet dac_override denials
|
||||||
|
deny capability dac_override,
|
||||||
|
deny capability dac_read_search,
|
||||||
|
deny capability sys_ptrace,
|
||||||
|
|
||||||
|
/dev/tty r,
|
||||||
|
/proc/stat r,
|
||||||
|
/proc/cpuinfo r,
|
||||||
|
/proc/meminfo r,
|
||||||
|
/proc/uptime r,
|
||||||
|
/sys/devices/system/cpu/online r,
|
||||||
|
/proc/sys/kernel/pid_max r,
|
||||||
|
/proc/ r,
|
||||||
|
/proc/tty/drivers r,
|
||||||
|
}
|
||||||
|
profile /sbin/iptables (complain) {
|
||||||
|
signal (receive) peer=/usr/bin/docker,
|
||||||
|
capability net_admin,
|
||||||
|
}
|
||||||
|
profile /sbin/auplink flags=(attach_disconnected, complain) {
|
||||||
|
signal (receive) peer=/usr/bin/docker,
|
||||||
|
capability sys_admin,
|
||||||
|
capability dac_override,
|
||||||
|
|
||||||
|
@{DOCKER_GRAPH_PATH}/aufs/** rw,
|
||||||
|
@{DOCKER_GRAPH_PATH}/tmp/** rw,
|
||||||
|
# For user namespaces:
|
||||||
|
@{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,
|
||||||
|
|
||||||
|
/sys/fs/aufs/** r,
|
||||||
|
/lib/** r,
|
||||||
|
/apparmor/.null r,
|
||||||
|
/dev/null rw,
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/sbin/auplink rm,
|
||||||
|
/proc/fs/aufs/** rw,
|
||||||
|
/proc/[0-9]*/mounts rw,
|
||||||
|
}
|
||||||
|
profile /sbin/modprobe /bin/kmod (complain) {
|
||||||
|
signal (receive) peer=/usr/bin/docker,
|
||||||
|
capability sys_module,
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/lib/** r,
|
||||||
|
/dev/null rw,
|
||||||
|
/apparmor/.null rw,
|
||||||
|
/sbin/modprobe rm,
|
||||||
|
/bin/kmod rm,
|
||||||
|
/proc/cmdline r,
|
||||||
|
/sys/module/** r,
|
||||||
|
/etc/modprobe.d{/,/**} r,
|
||||||
}
|
}
|
||||||
# xz works via pipes, so we do not need access to the filesystem.
|
# xz works via pipes, so we do not need access to the filesystem.
|
||||||
profile /usr/bin/xz {
|
profile /usr/bin/xz (complain) {
|
||||||
signal (receive) peer=/usr/bin/docker,
|
signal (receive) peer=/usr/bin/docker,
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/lib/** r,
|
||||||
|
/usr/bin/xz rm,
|
||||||
|
deny /proc/** rw,
|
||||||
|
deny /sys/** rw,
|
||||||
|
}
|
||||||
|
profile /sbin/xtables-multi (attach_disconnected, complain) {
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/lib/** r,
|
||||||
|
/sbin/xtables-multi rm,
|
||||||
|
/apparmor/.null w,
|
||||||
|
/dev/null rw,
|
||||||
|
capability net_raw,
|
||||||
|
capability net_admin,
|
||||||
|
network raw,
|
||||||
|
}
|
||||||
|
profile /sbin/zfs (attach_disconnected, complain) {
|
||||||
|
file,
|
||||||
|
capability,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
FROM debian:jessie
|
FROM debian:jessie
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
FROM debian:stretch
|
FROM debian:stretch
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
FROM debian:wheezy
|
FROM debian:wheezy
|
||||||
RUN echo deb http://http.debian.net/debian wheezy-backports main > /etc/apt/sources.list.d/wheezy-backports.list
|
RUN echo deb http://http.debian.net/debian wheezy-backports main > /etc/apt/sources.list.d/wheezy-backports.list
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ for version in "${versions[@]}"; do
|
|||||||
build-essential # "essential for building Debian packages"
|
build-essential # "essential for building Debian packages"
|
||||||
curl ca-certificates # for downloading Go
|
curl ca-certificates # for downloading Go
|
||||||
debhelper # for easy ".deb" building
|
debhelper # for easy ".deb" building
|
||||||
dh-apparmor # for apparmor debhelper
|
|
||||||
dh-systemd # for systemd debhelper integration
|
dh-systemd # for systemd debhelper integration
|
||||||
git # for "git commit" info in "docker -v"
|
git # for "git commit" info in "docker -v"
|
||||||
libapparmor-dev # for "sys/apparmor.h"
|
libapparmor-dev # for "sys/apparmor.h"
|
||||||
|
|||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:precise
|
FROM ubuntu:precise
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper dh-apparmor git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:wily
|
FROM ubuntu:trusty
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:vivid
|
FROM ubuntu:vivid
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:trusty
|
FROM ubuntu:wily
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
+190
-76
@@ -27,7 +27,7 @@
|
|||||||
# This order should be applied to lists, alternatives and code blocks.
|
# This order should be applied to lists, alternatives and code blocks.
|
||||||
|
|
||||||
__docker_q() {
|
__docker_q() {
|
||||||
docker ${host:+-H "$host"} 2>/dev/null "$@"
|
docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
__docker_containers_all() {
|
__docker_containers_all() {
|
||||||
@@ -139,7 +139,7 @@ __docker_value_of_option() {
|
|||||||
local counter=$((command_pos + 1))
|
local counter=$((command_pos + 1))
|
||||||
while [ $counter -lt $cword ]; do
|
while [ $counter -lt $cword ]; do
|
||||||
case ${words[$counter]} in
|
case ${words[$counter]} in
|
||||||
$option_glob )
|
@($option_glob) )
|
||||||
echo ${words[$counter + 1]}
|
echo ${words[$counter + 1]}
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
@@ -229,11 +229,12 @@ __docker_log_driver_options() {
|
|||||||
# see docs/reference/logging/index.md
|
# see docs/reference/logging/index.md
|
||||||
local fluentd_options="fluentd-address fluentd-tag"
|
local fluentd_options="fluentd-address fluentd-tag"
|
||||||
local gelf_options="gelf-address gelf-tag"
|
local gelf_options="gelf-address gelf-tag"
|
||||||
|
local json_file_options="max-file max-size"
|
||||||
local syslog_options="syslog-address syslog-facility syslog-tag"
|
local syslog_options="syslog-address syslog-facility syslog-tag"
|
||||||
|
|
||||||
case $(__docker_value_of_option --log-driver) in
|
case $(__docker_value_of_option --log-driver) in
|
||||||
'')
|
'')
|
||||||
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $syslog_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $json_file_options $syslog_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
fluentd)
|
fluentd)
|
||||||
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
||||||
@@ -241,6 +242,9 @@ __docker_log_driver_options() {
|
|||||||
gelf)
|
gelf)
|
||||||
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
|
json-file)
|
||||||
|
COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
syslog)
|
syslog)
|
||||||
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
@@ -295,6 +299,10 @@ __docker_complete_log_driver_options() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__docker_log_levels() {
|
||||||
|
COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
|
||||||
|
}
|
||||||
|
|
||||||
# a selection of the available signals that is most likely of interest in the
|
# a selection of the available signals that is most likely of interest in the
|
||||||
# context of docker containers.
|
# context of docker containers.
|
||||||
__docker_signals() {
|
__docker_signals() {
|
||||||
@@ -312,61 +320,34 @@ __docker_signals() {
|
|||||||
COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo $cur | tr '[:lower:]' '[:upper:]')" ) )
|
COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo $cur | tr '[:lower:]' '[:upper:]')" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# global options that may appear after the docker command
|
||||||
_docker_docker() {
|
_docker_docker() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--daemon -d
|
$global_boolean_options
|
||||||
--debug -D
|
--help
|
||||||
--help -h
|
|
||||||
--icc
|
|
||||||
--ip-forward
|
|
||||||
--ip-masq
|
|
||||||
--iptables
|
|
||||||
--ipv6
|
|
||||||
--selinux-enabled
|
|
||||||
--tls
|
|
||||||
--tlsverify
|
|
||||||
--userland-proxy=false
|
|
||||||
--version -v
|
--version -v
|
||||||
"
|
"
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
--exec-root|--graph|-g)
|
--config)
|
||||||
_filedir -d
|
_filedir -d
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--log-driver)
|
|
||||||
__docker_log_drivers
|
|
||||||
return
|
|
||||||
;;
|
|
||||||
--log-level|-l)
|
--log-level|-l)
|
||||||
COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
|
__docker_log_levels
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--log-opt)
|
$(__docker_to_extglob "$global_options_with_args") )
|
||||||
__docker_log_driver_options
|
|
||||||
return
|
|
||||||
;;
|
|
||||||
--pidfile|-p|--tlscacert|--tlscert|--tlskey)
|
|
||||||
_filedir
|
|
||||||
return
|
|
||||||
;;
|
|
||||||
--storage-driver|-s)
|
|
||||||
COMPREPLY=( $( compgen -W "aufs devicemapper btrfs overlay" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
|
||||||
return
|
|
||||||
;;
|
|
||||||
$main_options_with_args_glob )
|
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
__docker_complete_log_driver_options && return
|
|
||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "$boolean_options $main_options_with_args" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$boolean_options $global_options_with_args" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
local counter="$(__docker_pos_first_nonflag $main_options_with_args_glob)"
|
local counter=$( __docker_pos_first_nonflag $(__docker_to_extglob "$global_options_with_args") )
|
||||||
if [ $cword -eq $counter ]; then
|
if [ $cword -eq $counter ]; then
|
||||||
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
||||||
fi
|
fi
|
||||||
@@ -478,6 +459,138 @@ _docker_create() {
|
|||||||
_docker_run
|
_docker_run
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_docker_daemon() {
|
||||||
|
local boolean_options="
|
||||||
|
$global_boolean_options
|
||||||
|
--disable-legacy-registry
|
||||||
|
--help
|
||||||
|
--icc=false
|
||||||
|
--ip-forward=false
|
||||||
|
--ip-masq=false
|
||||||
|
--iptables=false
|
||||||
|
--ipv6
|
||||||
|
--selinux-enabled
|
||||||
|
--userland-proxy=false
|
||||||
|
"
|
||||||
|
local options_with_args="
|
||||||
|
$global_options_with_args
|
||||||
|
--api-cors-header
|
||||||
|
--bip
|
||||||
|
--bridge -b
|
||||||
|
--default-gateway
|
||||||
|
--default-gateway-v6
|
||||||
|
--default-ulimit
|
||||||
|
--dns
|
||||||
|
--dns-search
|
||||||
|
--exec-driver -e
|
||||||
|
--exec-opt
|
||||||
|
--exec-root
|
||||||
|
--fixed-cidr
|
||||||
|
--fixed-cidr-v6
|
||||||
|
--graph -g
|
||||||
|
--group -G
|
||||||
|
--insecure-registry
|
||||||
|
--ip
|
||||||
|
--label
|
||||||
|
--log-driver
|
||||||
|
--log-opt
|
||||||
|
--mtu
|
||||||
|
--pidfile -p
|
||||||
|
--registry-mirror
|
||||||
|
--storage-driver -s
|
||||||
|
--storage-opt
|
||||||
|
"
|
||||||
|
|
||||||
|
case "$prev" in
|
||||||
|
--exec-root|--graph|-g)
|
||||||
|
_filedir -d
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--log-driver)
|
||||||
|
__docker_log_drivers
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--pidfile|-p|--tlscacert|--tlscert|--tlskey)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--storage-driver|-s)
|
||||||
|
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--storage-opt)
|
||||||
|
local devicemapper_options="
|
||||||
|
dm.basesize
|
||||||
|
dm.blkdiscard
|
||||||
|
dm.blocksize
|
||||||
|
dm.fs
|
||||||
|
dm.loopdatasize
|
||||||
|
dm.loopmetadatasize
|
||||||
|
dm.mkfsarg
|
||||||
|
dm.mountopt
|
||||||
|
dm.override_udev_sync_check
|
||||||
|
dm.thinpooldev
|
||||||
|
"
|
||||||
|
local zfs_options="zfs.fsname"
|
||||||
|
|
||||||
|
case $(__docker_value_of_option '--storage-driver|-s') in
|
||||||
|
'')
|
||||||
|
COMPREPLY=( $( compgen -W "$devicemapper_options $zfs_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
devicemapper)
|
||||||
|
COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
zfs)
|
||||||
|
COMPREPLY=( $( compgen -W "$zfs_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
compopt -o nospace
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--log-level|-l)
|
||||||
|
__docker_log_levels
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--log-opt)
|
||||||
|
__docker_log_driver_options
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
$(__docker_to_extglob "$options_with_args") )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
__docker_complete_log_driver_options && return
|
||||||
|
|
||||||
|
case "${words[$cword-2]}$prev=" in
|
||||||
|
*dm.blkdiscard=*)
|
||||||
|
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.fs=*)
|
||||||
|
COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.override_udev_sync_check=*)
|
||||||
|
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.thinpooldev=*)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_docker_diff() {
|
_docker_diff() {
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
@@ -685,8 +798,17 @@ _docker_inspect() {
|
|||||||
COMPREPLY=( $( compgen -W "--format -f --type --help" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--format -f --type --help" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
__docker_containers_and_images
|
case $(__docker_value_of_option --type) in
|
||||||
;;
|
'')
|
||||||
|
__docker_containers_and_images
|
||||||
|
;;
|
||||||
|
container)
|
||||||
|
__docker_containers_all
|
||||||
|
;;
|
||||||
|
image)
|
||||||
|
__docker_image_repos_and_tags_and_ids
|
||||||
|
;;
|
||||||
|
esac
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,7 +925,7 @@ _docker_ps() {
|
|||||||
compopt -o nospace
|
compopt -o nospace
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
-n)
|
--format|-n)
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -827,7 +949,7 @@ _docker_ps() {
|
|||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --format --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -932,15 +1054,16 @@ _docker_rmi() {
|
|||||||
_docker_run() {
|
_docker_run() {
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--add-host
|
--add-host
|
||||||
--blkio-weight
|
|
||||||
--attach -a
|
--attach -a
|
||||||
|
--blkio-weight
|
||||||
--cap-add
|
--cap-add
|
||||||
--cap-drop
|
--cap-drop
|
||||||
--cgroup-parent
|
--cgroup-parent
|
||||||
--cidfile
|
--cidfile
|
||||||
--cpuset
|
|
||||||
--cpu-period
|
--cpu-period
|
||||||
--cpu-quota
|
--cpu-quota
|
||||||
|
--cpuset-cpus
|
||||||
|
--cpuset-mems
|
||||||
--cpu-shares -c
|
--cpu-shares -c
|
||||||
--device
|
--device
|
||||||
--dns
|
--dns
|
||||||
@@ -952,8 +1075,8 @@ _docker_run() {
|
|||||||
--group-add
|
--group-add
|
||||||
--hostname -h
|
--hostname -h
|
||||||
--ipc
|
--ipc
|
||||||
--label -l
|
|
||||||
--label-file
|
--label-file
|
||||||
|
--label -l
|
||||||
--link
|
--link
|
||||||
--log-driver
|
--log-driver
|
||||||
--log-opt
|
--log-opt
|
||||||
@@ -961,14 +1084,15 @@ _docker_run() {
|
|||||||
--mac-address
|
--mac-address
|
||||||
--memory -m
|
--memory -m
|
||||||
--memory-swap
|
--memory-swap
|
||||||
|
--memory-swappiness
|
||||||
--name
|
--name
|
||||||
--net
|
--net
|
||||||
--pid
|
--pid
|
||||||
--publish -p
|
--publish -p
|
||||||
--restart
|
--restart
|
||||||
--security-opt
|
--security-opt
|
||||||
--user -u
|
|
||||||
--ulimit
|
--ulimit
|
||||||
|
--user -u
|
||||||
--uts
|
--uts
|
||||||
--volumes-from
|
--volumes-from
|
||||||
--volume -v
|
--volume -v
|
||||||
@@ -976,8 +1100,10 @@ _docker_run() {
|
|||||||
"
|
"
|
||||||
|
|
||||||
local all_options="$options_with_args
|
local all_options="$options_with_args
|
||||||
|
--disable-content-trust=false
|
||||||
--help
|
--help
|
||||||
--interactive -i
|
--interactive -i
|
||||||
|
--oom-kill-disable
|
||||||
--privileged
|
--privileged
|
||||||
--publish-all -P
|
--publish-all -P
|
||||||
--read-only
|
--read-only
|
||||||
@@ -987,7 +1113,7 @@ _docker_run() {
|
|||||||
[ "$command" = "run" ] && all_options="$all_options
|
[ "$command" = "run" ] && all_options="$all_options
|
||||||
--detach -d
|
--detach -d
|
||||||
--rm
|
--rm
|
||||||
--sig-proxy
|
--sig-proxy=false
|
||||||
"
|
"
|
||||||
|
|
||||||
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
||||||
@@ -1287,6 +1413,7 @@ _docker() {
|
|||||||
commit
|
commit
|
||||||
cp
|
cp
|
||||||
create
|
create
|
||||||
|
daemon
|
||||||
diff
|
diff
|
||||||
events
|
events
|
||||||
exec
|
exec
|
||||||
@@ -1323,41 +1450,23 @@ _docker() {
|
|||||||
wait
|
wait
|
||||||
)
|
)
|
||||||
|
|
||||||
local main_options_with_args="
|
# These options are valid as global options for all client commands
|
||||||
--api-cors-header
|
# and valid as command options for `docker daemon`
|
||||||
--bip
|
local global_boolean_options="
|
||||||
--bridge -b
|
--debug -D
|
||||||
--default-gateway
|
--tls
|
||||||
--default-gateway-v6
|
--tlsverify
|
||||||
--default-ulimit
|
"
|
||||||
--dns
|
local global_options_with_args="
|
||||||
--dns-search
|
--config
|
||||||
--exec-driver -e
|
|
||||||
--exec-opt
|
|
||||||
--exec-root
|
|
||||||
--fixed-cidr
|
|
||||||
--fixed-cidr-v6
|
|
||||||
--graph -g
|
|
||||||
--group -G
|
|
||||||
--host -H
|
--host -H
|
||||||
--insecure-registry
|
|
||||||
--ip
|
|
||||||
--label
|
|
||||||
--log-driver
|
|
||||||
--log-level -l
|
--log-level -l
|
||||||
--log-opt
|
|
||||||
--mtu
|
|
||||||
--pidfile -p
|
|
||||||
--registry-mirror
|
|
||||||
--storage-driver -s
|
|
||||||
--storage-opt
|
|
||||||
--tlscacert
|
--tlscacert
|
||||||
--tlscert
|
--tlscert
|
||||||
--tlskey
|
--tlskey
|
||||||
"
|
"
|
||||||
|
|
||||||
local main_options_with_args_glob=$(__docker_to_extglob "$main_options_with_args")
|
local host config
|
||||||
local host
|
|
||||||
|
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
local cur prev words cword
|
local cur prev words cword
|
||||||
@@ -1372,7 +1481,12 @@ _docker() {
|
|||||||
(( counter++ ))
|
(( counter++ ))
|
||||||
host="${words[$counter]}"
|
host="${words[$counter]}"
|
||||||
;;
|
;;
|
||||||
$main_options_with_args_glob )
|
# save config so that completion can use custom configuration directories
|
||||||
|
--config)
|
||||||
|
(( counter++ ))
|
||||||
|
config="${words[$counter]}"
|
||||||
|
;;
|
||||||
|
$(__docker_to_extglob "$global_options_with_args") )
|
||||||
(( counter++ ))
|
(( counter++ ))
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
|
|||||||
@@ -597,6 +597,7 @@ _docker() {
|
|||||||
"($help -d --daeamon)"{-d,--daemon}"[Enable daemon mode]" \
|
"($help -d --daeamon)"{-d,--daemon}"[Enable daemon mode]" \
|
||||||
"($help)--default-gateway[Container default gateway IPv4 address]:IPv4 address: " \
|
"($help)--default-gateway[Container default gateway IPv4 address]:IPv4 address: " \
|
||||||
"($help)--default-gateway-v6[Container default gateway IPv6 address]:IPv6 address: " \
|
"($help)--default-gateway-v6[Container default gateway IPv6 address]:IPv6 address: " \
|
||||||
|
"($help)--disable-legacy-registry[Do not contact legacy registries]" \
|
||||||
"($help)*--dns=-[DNS server to use]:DNS: " \
|
"($help)*--dns=-[DNS server to use]:DNS: " \
|
||||||
"($help)*--dns-search=-[DNS search domains to use]" \
|
"($help)*--dns-search=-[DNS search domains to use]" \
|
||||||
"($help)*--default-ulimit=-[Set default ulimit settings for containers]:ulimit: " \
|
"($help)*--default-ulimit=-[Set default ulimit settings for containers]:ulimit: " \
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ After=network.target docker.socket
|
|||||||
Requires=docker.socket
|
Requires=docker.socket
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
Type=notify
|
||||||
ExecStart=/usr/bin/docker daemon -H fd://
|
ExecStart=/usr/bin/docker daemon -H fd://
|
||||||
MountFlags=slave
|
MountFlags=slave
|
||||||
LimitNOFILE=1048576
|
LimitNOFILE=1048576
|
||||||
|
|||||||
+110
-83
@@ -70,6 +70,66 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon
|
|||||||
return container.ExtractToDir(path, noOverwriteDirNonDir, content)
|
return container.ExtractToDir(path, noOverwriteDirNonDir, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolvePath resolves the given path in the container to a resource on the
|
||||||
|
// host. Returns a resolved path (absolute path to the resource on the host),
|
||||||
|
// the absolute path to the resource relative to the container's rootfs, and
|
||||||
|
// a error if the path points to outside the container's rootfs.
|
||||||
|
func (container *Container) resolvePath(path string) (resolvedPath, absPath string, err error) {
|
||||||
|
// Consider the given path as an absolute path in the container.
|
||||||
|
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path)
|
||||||
|
|
||||||
|
// Split the absPath into its Directory and Base components. We will
|
||||||
|
// resolve the dir in the scope of the container then append the base.
|
||||||
|
dirPath, basePath := filepath.Split(absPath)
|
||||||
|
|
||||||
|
resolvedDirPath, err := container.GetResourcePath(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolvedDirPath will have been cleaned (no trailing path separators) so
|
||||||
|
// we can manually join it with the base path element.
|
||||||
|
resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath
|
||||||
|
|
||||||
|
return resolvedPath, absPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// statPath is the unexported version of StatPath. Locks and mounts should
|
||||||
|
// be aquired before calling this method and the given path should be fully
|
||||||
|
// resolved to a path on the host corresponding to the given absolute path
|
||||||
|
// inside the container.
|
||||||
|
func (container *Container) statPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) {
|
||||||
|
lstat, err := os.Lstat(resolvedPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkTarget string
|
||||||
|
if lstat.Mode()&os.ModeSymlink != 0 {
|
||||||
|
// Fully evaluate the symlink in the scope of the container rootfs.
|
||||||
|
hostPath, err := container.GetResourcePath(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
linkTarget, err = filepath.Rel(container.basefs, hostPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it an absolute path.
|
||||||
|
linkTarget = filepath.Join(string(filepath.Separator), linkTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.ContainerPathStat{
|
||||||
|
Name: filepath.Base(absPath),
|
||||||
|
Size: lstat.Size(),
|
||||||
|
Mode: lstat.Mode(),
|
||||||
|
Mtime: lstat.ModTime(),
|
||||||
|
LinkTarget: linkTarget,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StatPath stats the filesystem resource at the specified path in this
|
// StatPath stats the filesystem resource at the specified path in this
|
||||||
// container. Returns stat info about the resource.
|
// container. Returns stat info about the resource.
|
||||||
func (container *Container) StatPath(path string) (stat *types.ContainerPathStat, err error) {
|
func (container *Container) StatPath(path string) (stat *types.ContainerPathStat, err error) {
|
||||||
@@ -87,39 +147,12 @@ func (container *Container) StatPath(path string) (stat *types.ContainerPathStat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider the given path as an absolute path in the container.
|
resolvedPath, absPath, err := container.resolvePath(path)
|
||||||
absPath := path
|
|
||||||
if !filepath.IsAbs(absPath) {
|
|
||||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
|
||||||
}
|
|
||||||
|
|
||||||
resolvedPath, err := container.GetResourcePath(absPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// A trailing "." or separator has important meaning. For example, if
|
return container.statPath(resolvedPath, absPath)
|
||||||
// `"foo"` is a symlink to some directory `"dir"`, then `os.Lstat("foo")`
|
|
||||||
// will stat the link itself, while `os.Lstat("foo/")` will stat the link
|
|
||||||
// target. If the basename of the path is ".", it means to archive the
|
|
||||||
// contents of the directory with "." as the first path component rather
|
|
||||||
// than the name of the directory. This would cause extraction of the
|
|
||||||
// archive to *not* make another directory, but instead use the current
|
|
||||||
// directory.
|
|
||||||
resolvedPath = archive.PreserveTrailingDotOrSeparator(resolvedPath, absPath)
|
|
||||||
|
|
||||||
lstat, err := os.Lstat(resolvedPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.ContainerPathStat{
|
|
||||||
Name: lstat.Name(),
|
|
||||||
Path: absPath,
|
|
||||||
Size: lstat.Size(),
|
|
||||||
Mode: lstat.Mode(),
|
|
||||||
Mtime: lstat.ModTime(),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArchivePath creates an archive of the filesystem resource at the specified
|
// ArchivePath creates an archive of the filesystem resource at the specified
|
||||||
@@ -154,41 +187,25 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider the given path as an absolute path in the container.
|
resolvedPath, absPath, err := container.resolvePath(path)
|
||||||
absPath := path
|
|
||||||
if !filepath.IsAbs(absPath) {
|
|
||||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
|
||||||
}
|
|
||||||
|
|
||||||
resolvedPath, err := container.GetResourcePath(absPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// A trailing "." or separator has important meaning. For example, if
|
stat, err = container.statPath(resolvedPath, absPath)
|
||||||
// `"foo"` is a symlink to some directory `"dir"`, then `os.Lstat("foo")`
|
|
||||||
// will stat the link itself, while `os.Lstat("foo/")` will stat the link
|
|
||||||
// target. If the basename of the path is ".", it means to archive the
|
|
||||||
// contents of the directory with "." as the first path component rather
|
|
||||||
// than the name of the directory. This would cause extraction of the
|
|
||||||
// archive to *not* make another directory, but instead use the current
|
|
||||||
// directory.
|
|
||||||
resolvedPath = archive.PreserveTrailingDotOrSeparator(resolvedPath, absPath)
|
|
||||||
|
|
||||||
lstat, err := os.Lstat(resolvedPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stat = &types.ContainerPathStat{
|
// We need to rebase the archive entries if the last element of the
|
||||||
Name: lstat.Name(),
|
// resolved path was a symlink that was evaluated and is now different
|
||||||
Path: absPath,
|
// than the requested path. For example, if the given path was "/foo/bar/",
|
||||||
Size: lstat.Size(),
|
// but it resolved to "/var/lib/docker/containers/{id}/foo/baz/", we want
|
||||||
Mode: lstat.Mode(),
|
// to ensure that the archive entries start with "bar" and not "baz". This
|
||||||
Mtime: lstat.ModTime(),
|
// also catches the case when the root directory of the container is
|
||||||
}
|
// requested: we want the archive entries to start with "/" and not the
|
||||||
|
// container ID.
|
||||||
data, err := archive.TarResource(resolvedPath)
|
data, err := archive.TarResourceRebase(resolvedPath, filepath.Base(absPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -227,27 +244,21 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider the given path as an absolute path in the container.
|
// The destination path needs to be resolved to a host path, with all
|
||||||
absPath := path
|
// symbolic links followed in the scope of the container's rootfs. Note
|
||||||
if !filepath.IsAbs(absPath) {
|
// that we do not use `container.resolvePath(path)` here because we need
|
||||||
absPath = archive.PreserveTrailingDotOrSeparator(filepath.Join("/", path), path)
|
// to also evaluate the last path element if it is a symlink. This is so
|
||||||
}
|
// that you can extract an archive to a symlink that points to a directory.
|
||||||
|
|
||||||
|
// Consider the given path as an absolute path in the container.
|
||||||
|
absPath := archive.PreserveTrailingDotOrSeparator(filepath.Join(string(filepath.Separator), path), path)
|
||||||
|
|
||||||
|
// This will evaluate the last path element if it is a symlink.
|
||||||
resolvedPath, err := container.GetResourcePath(absPath)
|
resolvedPath, err := container.GetResourcePath(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// A trailing "." or separator has important meaning. For example, if
|
|
||||||
// `"foo"` is a symlink to some directory `"dir"`, then `os.Lstat("foo")`
|
|
||||||
// will stat the link itself, while `os.Lstat("foo/")` will stat the link
|
|
||||||
// target. If the basename of the path is ".", it means to archive the
|
|
||||||
// contents of the directory with "." as the first path component rather
|
|
||||||
// than the name of the directory. This would cause extraction of the
|
|
||||||
// archive to *not* make another directory, but instead use the current
|
|
||||||
// directory.
|
|
||||||
resolvedPath = archive.PreserveTrailingDotOrSeparator(resolvedPath, absPath)
|
|
||||||
|
|
||||||
stat, err := os.Lstat(resolvedPath)
|
stat, err := os.Lstat(resolvedPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -257,23 +268,23 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
return ErrExtractPointNotDirectory
|
return ErrExtractPointNotDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Need to check if the path is in a volume. If it is, it cannot be in a
|
||||||
|
// read-only volume. If it is not in a volume, the container cannot be
|
||||||
|
// configured with a read-only rootfs.
|
||||||
|
|
||||||
|
// Use the resolved path relative to the container rootfs as the new
|
||||||
|
// absPath. This way we fully follow any symlinks in a volume that may
|
||||||
|
// lead back outside the volume.
|
||||||
baseRel, err := filepath.Rel(container.basefs, resolvedPath)
|
baseRel, err := filepath.Rel(container.basefs, resolvedPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
absPath = filepath.Join("/", baseRel)
|
// Make it an absolute path.
|
||||||
|
absPath = filepath.Join(string(filepath.Separator), baseRel)
|
||||||
|
|
||||||
// Need to check if the path is in a volume. If it is, it cannot be in a
|
toVolume, err := checkIfPathIsInAVolume(container, absPath)
|
||||||
// read-only volume. If it is not in a volume, the container cannot be
|
if err != nil {
|
||||||
// configured with a read-only rootfs.
|
return err
|
||||||
var toVolume bool
|
|
||||||
for _, mnt := range container.MountPoints {
|
|
||||||
if toVolume = mnt.hasResource(absPath); toVolume {
|
|
||||||
if mnt.RW {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return ErrVolumeReadonly
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
||||||
@@ -295,3 +306,19 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||||
|
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||||
|
// cannot be configured with a read-only rootfs.
|
||||||
|
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
||||||
|
var toVolume bool
|
||||||
|
for _, mnt := range container.MountPoints {
|
||||||
|
if toVolume = mnt.hasResource(absPath); toVolume {
|
||||||
|
if mnt.RW {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return false, ErrVolumeReadonly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toVolume, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
defaultPidFile = "/var/run/docker.pid"
|
defaultPidFile = "/var/run/docker.pid"
|
||||||
defaultGraph = "/var/lib/docker"
|
defaultGraph = "/var/lib/docker"
|
||||||
defaultExec = "native"
|
defaultExec = "clr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config defines the configuration of a docker daemon.
|
// Config defines the configuration of a docker daemon.
|
||||||
|
|||||||
+20
-8
@@ -41,6 +41,14 @@ var (
|
|||||||
ErrContainerRootfsReadonly = errors.New("container rootfs is marked read-only")
|
ErrContainerRootfsReadonly = errors.New("container rootfs is marked read-only")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ErrContainerNotRunning struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrContainerNotRunning) Error() string {
|
||||||
|
return fmt.Sprintf("Container %s is not running", e.id)
|
||||||
|
}
|
||||||
|
|
||||||
type StreamConfig struct {
|
type StreamConfig struct {
|
||||||
stdout *broadcastwriter.BroadcastWriter
|
stdout *broadcastwriter.BroadcastWriter
|
||||||
stderr *broadcastwriter.BroadcastWriter
|
stderr *broadcastwriter.BroadcastWriter
|
||||||
@@ -371,7 +379,7 @@ func (container *Container) KillSig(sig int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !container.Running {
|
if !container.Running {
|
||||||
return fmt.Errorf("Container %s is not running", container.ID)
|
return ErrContainerNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// signal to the monitor that it should not restart the container
|
// signal to the monitor that it should not restart the container
|
||||||
@@ -408,7 +416,7 @@ func (container *Container) Pause() error {
|
|||||||
|
|
||||||
// We cannot Pause the container which is not running
|
// We cannot Pause the container which is not running
|
||||||
if !container.Running {
|
if !container.Running {
|
||||||
return fmt.Errorf("Container %s is not running, cannot pause a non-running container", container.ID)
|
return ErrContainerNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot Pause the container which is already paused
|
// We cannot Pause the container which is already paused
|
||||||
@@ -430,7 +438,7 @@ func (container *Container) Unpause() error {
|
|||||||
|
|
||||||
// We cannot unpause the container which is not running
|
// We cannot unpause the container which is not running
|
||||||
if !container.Running {
|
if !container.Running {
|
||||||
return fmt.Errorf("Container %s is not running, cannot unpause a non-running container", container.ID)
|
return ErrContainerNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot unpause the container which is not paused
|
// We cannot unpause the container which is not paused
|
||||||
@@ -448,7 +456,7 @@ func (container *Container) Unpause() error {
|
|||||||
|
|
||||||
func (container *Container) Kill() error {
|
func (container *Container) Kill() error {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return fmt.Errorf("Container %s is not running", container.ID)
|
return ErrContainerNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Send SIGKILL
|
// 1. Send SIGKILL
|
||||||
@@ -530,7 +538,7 @@ func (container *Container) Restart(seconds int) error {
|
|||||||
|
|
||||||
func (container *Container) Resize(h, w int) error {
|
func (container *Container) Resize(h, w int) error {
|
||||||
if !container.IsRunning() {
|
if !container.IsRunning() {
|
||||||
return fmt.Errorf("Cannot resize container %s, container is not running", container.ID)
|
return ErrContainerNotRunning{container.ID}
|
||||||
}
|
}
|
||||||
if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil {
|
if err := container.command.ProcessConfig.Terminal.Resize(h, w); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1080,8 +1088,12 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
|
|||||||
|
|
||||||
func (container *Container) networkMounts() []execdriver.Mount {
|
func (container *Container) networkMounts() []execdriver.Mount {
|
||||||
var mounts []execdriver.Mount
|
var mounts []execdriver.Mount
|
||||||
|
mode := "Z"
|
||||||
|
if container.hostConfig.NetworkMode.IsContainer() {
|
||||||
|
mode = "z"
|
||||||
|
}
|
||||||
if container.ResolvConfPath != "" {
|
if container.ResolvConfPath != "" {
|
||||||
label.SetFileLabel(container.ResolvConfPath, container.MountLabel)
|
label.Relabel(container.ResolvConfPath, container.MountLabel, mode)
|
||||||
mounts = append(mounts, execdriver.Mount{
|
mounts = append(mounts, execdriver.Mount{
|
||||||
Source: container.ResolvConfPath,
|
Source: container.ResolvConfPath,
|
||||||
Destination: "/etc/resolv.conf",
|
Destination: "/etc/resolv.conf",
|
||||||
@@ -1090,7 +1102,7 @@ func (container *Container) networkMounts() []execdriver.Mount {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if container.HostnamePath != "" {
|
if container.HostnamePath != "" {
|
||||||
label.SetFileLabel(container.HostnamePath, container.MountLabel)
|
label.Relabel(container.HostnamePath, container.MountLabel, mode)
|
||||||
mounts = append(mounts, execdriver.Mount{
|
mounts = append(mounts, execdriver.Mount{
|
||||||
Source: container.HostnamePath,
|
Source: container.HostnamePath,
|
||||||
Destination: "/etc/hostname",
|
Destination: "/etc/hostname",
|
||||||
@@ -1099,7 +1111,7 @@ func (container *Container) networkMounts() []execdriver.Mount {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if container.HostsPath != "" {
|
if container.HostsPath != "" {
|
||||||
label.SetFileLabel(container.HostsPath, container.MountLabel)
|
label.Relabel(container.HostsPath, container.MountLabel, mode)
|
||||||
mounts = append(mounts, execdriver.Mount{
|
mounts = append(mounts, execdriver.Mount{
|
||||||
Source: container.HostsPath,
|
Source: container.HostsPath,
|
||||||
Destination: "/etc/hosts",
|
Destination: "/etc/hosts",
|
||||||
|
|||||||
@@ -272,7 +272,11 @@ func populateCommand(c *Container, env []string) error {
|
|||||||
BlkioWeight: c.hostConfig.BlkioWeight,
|
BlkioWeight: c.hostConfig.BlkioWeight,
|
||||||
Rlimits: rlimits,
|
Rlimits: rlimits,
|
||||||
OomKillDisable: c.hostConfig.OomKillDisable,
|
OomKillDisable: c.hostConfig.OomKillDisable,
|
||||||
MemorySwappiness: c.hostConfig.MemorySwappiness,
|
MemorySwappiness: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.hostConfig.MemorySwappiness != nil {
|
||||||
|
resources.MemorySwappiness = *c.hostConfig.MemorySwappiness
|
||||||
}
|
}
|
||||||
|
|
||||||
processConfig := execdriver.ProcessConfig{
|
processConfig := execdriver.ProcessConfig{
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||||||
if err := daemon.mergeAndVerifyConfig(config, img); err != nil {
|
if err := daemon.mergeAndVerifyConfig(config, img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if !config.NetworkDisabled && daemon.SystemConfig().IPv4ForwardingDisabled {
|
|
||||||
warnings = append(warnings, "IPv4 forwarding is disabled.")
|
|
||||||
}
|
|
||||||
if hostConfig == nil {
|
if hostConfig == nil {
|
||||||
hostConfig = &runconfig.HostConfig{}
|
hostConfig = &runconfig.HostConfig{}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-7
@@ -35,7 +35,6 @@ import (
|
|||||||
"github.com/docker/docker/pkg/truncindex"
|
"github.com/docker/docker/pkg/truncindex"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/trust"
|
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
"github.com/opencontainers/runc/libcontainer/netlink"
|
"github.com/opencontainers/runc/libcontainer/netlink"
|
||||||
)
|
)
|
||||||
@@ -670,10 +669,6 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|||||||
if err := system.MkdirAll(trustDir, 0700); err != nil && !os.IsExist(err) {
|
if err := system.MkdirAll(trustDir, 0700); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
trustService, err := trust.NewTrustStore(trustDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not create trust store: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
eventsService := events.New()
|
eventsService := events.New()
|
||||||
logrus.Debug("Creating repository list")
|
logrus.Debug("Creating repository list")
|
||||||
@@ -682,7 +677,6 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|||||||
Key: trustKey,
|
Key: trustKey,
|
||||||
Registry: registryService,
|
Registry: registryService,
|
||||||
Events: eventsService,
|
Events: eventsService,
|
||||||
Trust: trustService,
|
|
||||||
}
|
}
|
||||||
repositories, err := graph.NewTagStore(filepath.Join(config.Root, "repositories-"+d.driver.String()), tagCfg)
|
repositories, err := graph.NewTagStore(filepath.Join(config.Root, "repositories-"+d.driver.String()), tagCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -979,7 +973,7 @@ func getDefaultRouteMtu() (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if r.Default {
|
if r.Default && r.Iface != nil {
|
||||||
return r.Iface.MTU, nil
|
return r.Iface.MTU, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,13 +167,16 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
|
|||||||
if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 {
|
if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 {
|
||||||
return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.")
|
return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.")
|
||||||
}
|
}
|
||||||
if hostConfig.MemorySwappiness != -1 && !daemon.SystemConfig().MemorySwappiness {
|
if hostConfig.MemorySwappiness != nil && !daemon.SystemConfig().MemorySwappiness {
|
||||||
warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
|
warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
|
||||||
logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
|
logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
|
||||||
hostConfig.MemorySwappiness = -1
|
hostConfig.MemorySwappiness = nil
|
||||||
}
|
}
|
||||||
if hostConfig.MemorySwappiness != -1 && (hostConfig.MemorySwappiness < 0 || hostConfig.MemorySwappiness > 100) {
|
if hostConfig.MemorySwappiness != nil {
|
||||||
return warnings, fmt.Errorf("Invalid value: %d, valid memory swappiness range is 0-100.", hostConfig.MemorySwappiness)
|
swappiness := *hostConfig.MemorySwappiness
|
||||||
|
if swappiness < -1 || swappiness > 100 {
|
||||||
|
return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100.", swappiness)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod {
|
if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod {
|
||||||
warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.")
|
warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.")
|
||||||
|
|||||||
@@ -0,0 +1,721 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package clr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"net"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
sysinfo "github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
"github.com/docker/libnetwork/netlabel"
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Clear Linux for Intel(R) Architecture
|
||||||
|
driverName = "clr"
|
||||||
|
|
||||||
|
envVarPrefix = "CLR_"
|
||||||
|
|
||||||
|
// Command used for lkvm control
|
||||||
|
lkvmName = "lkvm"
|
||||||
|
|
||||||
|
// local "latest" information
|
||||||
|
clrFile = "latest"
|
||||||
|
|
||||||
|
// upstream base URL
|
||||||
|
clrURL = "https://download.clearlinux.org"
|
||||||
|
|
||||||
|
// upstream latest release file
|
||||||
|
latestFile = "https://download.clearlinux.org/latest"
|
||||||
|
|
||||||
|
// clr kernel (not bzimage)
|
||||||
|
clrKernel = "/usr/lib/kernel/vmlinux.container"
|
||||||
|
)
|
||||||
|
|
||||||
|
type driver struct {
|
||||||
|
root string // root path for the driver to use
|
||||||
|
libPath string
|
||||||
|
initPath string
|
||||||
|
version string
|
||||||
|
apparmor bool
|
||||||
|
sharedRoot bool
|
||||||
|
activeContainers map[string]*activeContainer
|
||||||
|
machineMemory int64
|
||||||
|
containerPid int
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type activeContainer struct {
|
||||||
|
container *configs.Config
|
||||||
|
cmd *exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTapIf(c *execdriver.Command) string {
|
||||||
|
return fmt.Sprintf("tb-%s", c.ID[:12])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClrVersion(libPath string) string {
|
||||||
|
txt, err := ioutil.ReadFile(path.Join(libPath, clrFile))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Split(string(txt), "\n")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchLatest(libPath string) error {
|
||||||
|
out, err := os.Create(path.Join(libPath, clrFile))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
resp, err := http.Get(latestFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchImage(version, libPath string) error {
|
||||||
|
// TODO: Add checksum validation
|
||||||
|
outfile := fmt.Sprintf("clear-%s-containers.img.xz", version)
|
||||||
|
url := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, outfile)
|
||||||
|
outpath := path.Join(libPath, outfile)
|
||||||
|
var output []byte
|
||||||
|
|
||||||
|
logrus.Debugf("Fetching clr version: %s, %s", version, outpath)
|
||||||
|
out, err := os.Create(outpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Consider progress feedback ?
|
||||||
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// decompress the file
|
||||||
|
cmd := exec.Command("unxz", outpath)
|
||||||
|
cmd.Dir = libPath
|
||||||
|
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
logrus.Debugf("Unable to extract image %s: %s", version, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDriver creates a new clear linux execution driver.
|
||||||
|
func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
|
||||||
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
meminfo, err := sysinfo.ReadMemInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
version, err := prepareClr(libPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &driver{
|
||||||
|
apparmor: apparmor,
|
||||||
|
root: root,
|
||||||
|
libPath: libPath,
|
||||||
|
initPath: initPath,
|
||||||
|
version: version,
|
||||||
|
sharedRoot: false,
|
||||||
|
activeContainers: make(map[string]*activeContainer),
|
||||||
|
// FIXME:
|
||||||
|
machineMemory: meminfo.MemTotal,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareClr(libPath string) (string, error) {
|
||||||
|
var version = getClrVersion(libPath)
|
||||||
|
var nversion string
|
||||||
|
logrus.Debugf("%s preparing environment", driverName)
|
||||||
|
|
||||||
|
err := fetchLatest(libPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
nversion = getClrVersion(libPath)
|
||||||
|
if nversion != version && version != "" {
|
||||||
|
logrus.Debugf("Updating to clr version: %s", nversion)
|
||||||
|
err = fetchImage(nversion, libPath)
|
||||||
|
} else if version == "" {
|
||||||
|
logrus.Debugf("Installing clr version: %s", nversion)
|
||||||
|
err = fetchImage(nversion, libPath)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("Using clr version: %s", nversion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nversion, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Name() string {
|
||||||
|
return fmt.Sprintf("%s-%s", driverName, d.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||||
|
var (
|
||||||
|
term execdriver.Terminal
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
container, err := d.createContainer(c)
|
||||||
|
if err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryMiB := c.HostConfig.Memory
|
||||||
|
if memoryMiB == 0 {
|
||||||
|
memoryMiB = 1024
|
||||||
|
} else {
|
||||||
|
// docker passes the value as bytes
|
||||||
|
memoryMiB = memoryMiB / int64(math.Pow(2, 20))
|
||||||
|
}
|
||||||
|
|
||||||
|
workingDirVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "WORKINGDIR", c.WorkingDir)
|
||||||
|
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, workingDirVar)
|
||||||
|
|
||||||
|
userVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "USER", c.ProcessConfig.User)
|
||||||
|
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, userVar)
|
||||||
|
|
||||||
|
if err := d.setupNetwork(c); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ProcessConfig.Tty {
|
||||||
|
term, err = NewTtyConsole(&c.ProcessConfig, pipes)
|
||||||
|
} else {
|
||||||
|
term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
c.ProcessConfig.Terminal = term
|
||||||
|
|
||||||
|
d.Lock()
|
||||||
|
d.activeContainers[c.ID] = &activeContainer{
|
||||||
|
container: container,
|
||||||
|
cmd: &c.ProcessConfig.Cmd,
|
||||||
|
}
|
||||||
|
d.Unlock()
|
||||||
|
|
||||||
|
if err := d.generateEnvConfig(c); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.generateDockerInit(c); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range c.Mounts {
|
||||||
|
dest := path.Join(c.Rootfs, m.Destination)
|
||||||
|
|
||||||
|
if m.Destination == "/etc/hostname" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pathExists(m.Source) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := "bind"
|
||||||
|
|
||||||
|
if m.Private {
|
||||||
|
opts = opts + ",rprivate"
|
||||||
|
}
|
||||||
|
if m.Slave {
|
||||||
|
opts = opts + ",rslave"
|
||||||
|
}
|
||||||
|
|
||||||
|
// This may look racy, but it isn't since the VM isn't
|
||||||
|
// running yet.
|
||||||
|
//
|
||||||
|
// The check is necessary to handle bind mounting of
|
||||||
|
// regular files correctly since without it we may be
|
||||||
|
// attempting to create a directory where there already
|
||||||
|
// exists a normal file.
|
||||||
|
if !pathExists(dest) {
|
||||||
|
if err := os.MkdirAll(dest, 0750); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mount.Mount(m.Source, dest, "", opts); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.Writable {
|
||||||
|
if err := mount.Mount("", dest, "", "bind,remount,ro"); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer mount.Unmount(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
var args []string
|
||||||
|
// various things for lkvm
|
||||||
|
ifname := getTapIf(c)
|
||||||
|
// FIXME: Should be real hostname from like process/container struct
|
||||||
|
hostname := c.ID[0:12]
|
||||||
|
img := fmt.Sprintf("%s/clear-%s-containers.img", d.libPath, d.version)
|
||||||
|
memory := fmt.Sprintf("%d", memoryMiB)
|
||||||
|
// FIXME: Locked cores to 6 ?
|
||||||
|
cores := fmt.Sprintf("%d", 6)
|
||||||
|
ipaddr := c.NetworkSettings.IPAddress
|
||||||
|
gateway := c.NetworkSettings.Gateway
|
||||||
|
macaddr := c.NetworkSettings.MacAddress
|
||||||
|
|
||||||
|
args = append(args, c.ProcessConfig.Entrypoint)
|
||||||
|
args = append(args, c.ProcessConfig.Arguments...)
|
||||||
|
|
||||||
|
rootParams := fmt.Sprintf("root=/dev/plkvm0p1 rootfstype=ext4 rootflags=dax,data=ordered "+
|
||||||
|
"init=/usr/lib/systemd/systemd systemd.unit=container.target rw tsc=reliable "+
|
||||||
|
"systemd.show_status=false "+
|
||||||
|
"no_timer_check rcupdate.rcu_expedited=1 console=hvc0 quiet ip=%s::%s::%s::off",
|
||||||
|
ipaddr, gateway, hostname)
|
||||||
|
|
||||||
|
params := []string{
|
||||||
|
lkvmName, "run", "-c", cores, "-m", memory,
|
||||||
|
"--name", c.ID, "--console", "virtio",
|
||||||
|
"--kernel", clrKernel,
|
||||||
|
"--params", rootParams,
|
||||||
|
"--shmem", fmt.Sprintf("0x200000000:0:file=%s:private", img),
|
||||||
|
"--network", fmt.Sprintf("mode=tap,script=none,tapif=%s,guest_mac=%s", ifname, macaddr),
|
||||||
|
"--9p", fmt.Sprintf("%s,rootfs", c.Rootfs),
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("%s params %s", driverName, params)
|
||||||
|
var (
|
||||||
|
name = params[0]
|
||||||
|
arg = params[1:]
|
||||||
|
)
|
||||||
|
aname, err := exec.LookPath(name)
|
||||||
|
if err != nil {
|
||||||
|
aname = name
|
||||||
|
}
|
||||||
|
c.ProcessConfig.Path = aname
|
||||||
|
c.ProcessConfig.Args = append([]string{name}, arg...)
|
||||||
|
c.ProcessConfig.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
||||||
|
|
||||||
|
// Start the container. Since it runs synchronously, we don't Wait()
|
||||||
|
// for it since we need to check the status to determine if it did
|
||||||
|
// actually start successfully.
|
||||||
|
if err := c.ProcessConfig.Start(); err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
waitErr error
|
||||||
|
waitLock = make(chan struct{})
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
if err := c.ProcessConfig.Wait(); err != nil {
|
||||||
|
if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
|
||||||
|
waitErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(waitLock)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// FIXME: need to create state.json for Stats() to work.
|
||||||
|
c.ContainerPid = c.ProcessConfig.Process.Pid
|
||||||
|
d.containerPid = c.ProcessConfig.Process.Pid
|
||||||
|
|
||||||
|
if startCallback != nil {
|
||||||
|
logrus.Debugf("Invoking startCallback")
|
||||||
|
startCallback(&c.ProcessConfig, c.ProcessConfig.Process.Pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
oomKill := false
|
||||||
|
|
||||||
|
// Wait for the VM to shutdown
|
||||||
|
<-waitLock
|
||||||
|
exitCode := getExitCode(c)
|
||||||
|
|
||||||
|
cExitStatus, cerr := d.cleanupVM(c)
|
||||||
|
|
||||||
|
if cerr != nil {
|
||||||
|
waitErr = cerr
|
||||||
|
exitCode = cExitStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// check oom error
|
||||||
|
if oomKill {
|
||||||
|
exitCode = 137
|
||||||
|
}
|
||||||
|
|
||||||
|
return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: false}, waitErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathExists(path string) bool {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathExecutable(path string) bool {
|
||||||
|
s, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := s.Mode()
|
||||||
|
if mode&0111 != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) cleanupVM(c *execdriver.Command) (exitStatus int, err error) {
|
||||||
|
cmd := exec.Command("ip", "tuntap", "del", "dev", getTapIf(c), "mode", "tap")
|
||||||
|
var output []byte
|
||||||
|
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
waitStatus := exitError.Sys().(syscall.WaitStatus)
|
||||||
|
exitStatus = waitStatus.ExitStatus()
|
||||||
|
}
|
||||||
|
logrus.Debugf("teardown failed for vm %s: %s (%s)", c.ID, string(output), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't matter if this fails
|
||||||
|
// lkvm could have removed it, and stale sockets are not fatal
|
||||||
|
_ = os.Remove(fmt.Sprintf("%s/.lkvm/%s.sock", d.root, c.ID))
|
||||||
|
|
||||||
|
return exitStatus, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// createContainer populates and configures the container type with the
|
||||||
|
// data provided by the execdriver.Command
|
||||||
|
func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
||||||
|
return execdriver.InitContainer(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the exit code of the process
|
||||||
|
// if the process has not exited -1 will be returned
|
||||||
|
func getExitCode(c *execdriver.Command) int {
|
||||||
|
if c.ProcessConfig.ProcessState == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) lkvmCommand(c *execdriver.Command, arg string) ([]byte, error) {
|
||||||
|
args := append([]string{lkvmName}, arg)
|
||||||
|
if c != nil {
|
||||||
|
args = append(args, "--name", c.ID)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(lkvmName, args...)
|
||||||
|
cmd.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
||||||
|
return cmd.Output()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill sends a signal to workload
|
||||||
|
func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
||||||
|
// Not supported
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Pause(c *execdriver.Command) error {
|
||||||
|
_, err := d.lkvmCommand(c, "pause")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Unpause(c *execdriver.Command) error {
|
||||||
|
_, err := d.lkvmCommand(c, "resume")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate forcibly stops a container
|
||||||
|
func (d *driver) Terminate(c *execdriver.Command) error {
|
||||||
|
_, err := d.lkvmCommand(c, "stop")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) containerDir(containerID string) string {
|
||||||
|
return path.Join(d.libPath, "containers", containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDigit returns true if s can be represented as an integer
|
||||||
|
func isDigit(s string) bool {
|
||||||
|
if _, err := strconv.Atoi(s); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) getInfo(id string) ([]byte, error) {
|
||||||
|
output, err := d.lkvmCommand(nil, "list")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, line := range strings.Split(string(output), "\n") {
|
||||||
|
if i < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(strings.TrimSpace(line))
|
||||||
|
|
||||||
|
if len(fields) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isDigit(fields[0]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fields[1] != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(line), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf("-1 %s stopped", id)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type info struct {
|
||||||
|
ID string
|
||||||
|
driver *driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *info) IsRunning() bool {
|
||||||
|
output, err := i.driver.getInfo(i.ID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Error getting info for %s container %s: %s (%s)",
|
||||||
|
driverName, i.ID, err, output)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
clrInfo, err := parseClrInfo(i.ID, string(output))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return clrInfo.Running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Info(id string) execdriver.Info {
|
||||||
|
return &info{
|
||||||
|
ID: id,
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
||||||
|
// The VM doesn't expose the worload pid(s), so the only meaningful
|
||||||
|
// pid is that of the VM
|
||||||
|
return []int{d.containerPid}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TtyConsole is a type to represent a pseud-oterminal (see pty(7))
|
||||||
|
type TtyConsole struct {
|
||||||
|
MasterPty *os.File
|
||||||
|
SlavePty *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTtyConsole returns a new TtyConsole object.
|
||||||
|
func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
||||||
|
// lxc is special in that we cannot create the master outside of the container without
|
||||||
|
// opening the slave because we have nothing to provide to the cmd. We have to open both then do
|
||||||
|
// the crazy setup on command right now instead of passing the console path to lxc and telling it
|
||||||
|
// to open up that console. we save a couple of openfiles in the native driver because we can do
|
||||||
|
// this.
|
||||||
|
ptyMaster, ptySlave, err := pty.Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tty := &TtyConsole{
|
||||||
|
MasterPty: ptyMaster,
|
||||||
|
SlavePty: ptySlave,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
|
||||||
|
tty.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
processConfig.Console = tty.SlavePty.Name()
|
||||||
|
|
||||||
|
return tty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Master returns the master end of the pty
|
||||||
|
func (t *TtyConsole) Master() *os.File {
|
||||||
|
return t.MasterPty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize modifies the size of the pty terminal being used.
|
||||||
|
func (t *TtyConsole) Resize(h, w int) error {
|
||||||
|
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachPipes associates the specified pipes with the pty master.
|
||||||
|
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||||
|
command.Stdout = t.SlavePty
|
||||||
|
command.Stderr = t.SlavePty
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if wb, ok := pipes.Stdout.(interface {
|
||||||
|
CloseWriters() error
|
||||||
|
}); ok {
|
||||||
|
defer wb.CloseWriters()
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(pipes.Stdout, t.MasterPty)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if pipes.Stdin != nil {
|
||||||
|
command.Stdin = t.SlavePty
|
||||||
|
command.SysProcAttr.Setctty = true
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(t.MasterPty, pipes.Stdin)
|
||||||
|
|
||||||
|
pipes.Stdin.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes both ends of the pty.
|
||||||
|
func (t *TtyConsole) Close() error {
|
||||||
|
t.SlavePty.Close()
|
||||||
|
return t.MasterPty.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||||
|
return -1, fmt.Errorf("Unsupported: Exec is not supported by the %q driver", driverName)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up after an Exec
|
||||||
|
func (d *driver) Clean(id string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) generateEnvConfig(c *execdriver.Command) error {
|
||||||
|
data := []byte(strings.Join(c.ProcessConfig.Env, "\n"))
|
||||||
|
|
||||||
|
p := path.Join(d.libPath, "containers", c.ID, "config.env")
|
||||||
|
c.Mounts = append(c.Mounts, execdriver.Mount{
|
||||||
|
Source: p,
|
||||||
|
Destination: "/.dockerenv",
|
||||||
|
Writable: false,
|
||||||
|
Private: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return ioutil.WriteFile(p, data, 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) generateDockerInit(c *execdriver.Command) error {
|
||||||
|
p := fmt.Sprintf("%s/.containerexec", c.Rootfs)
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
if pathExecutable(p) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, c.ProcessConfig.Entrypoint)
|
||||||
|
args = append(args, c.ProcessConfig.Arguments...)
|
||||||
|
|
||||||
|
data := []byte(fmt.Sprintf("#!/bin/sh\n%s\n", strings.Join(args, " ")))
|
||||||
|
|
||||||
|
return ioutil.WriteFile(p, data, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) setupNetwork(c *execdriver.Command) error {
|
||||||
|
ifname := getTapIf(c)
|
||||||
|
|
||||||
|
var bridgeName string
|
||||||
|
var bridgeLinkName string
|
||||||
|
var output []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, info := range c.EndpointInfo {
|
||||||
|
if mac, ok := info[netlabel.MacAddress].(net.HardwareAddr); ok {
|
||||||
|
if mac.String() == c.NetworkSettings.MacAddress {
|
||||||
|
bridgeName = info[netlabel.BridgeName].(string)
|
||||||
|
bridgeLinkName = info[netlabel.BridgeLinkName].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip existing veth
|
||||||
|
cmd := exec.Command("ip", "link", "del", bridgeLinkName)
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command("ip", "tuntap", "add", "dev", ifname, "mode", "tap", "vnet_hdr")
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd = exec.Command("ip", "link", "set", "dev", ifname, "master", bridgeName)
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command("ip", "link", "set", "dev", ifname, "up")
|
||||||
|
if output, err = cmd.CombinedOutput(); err != nil {
|
||||||
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
||||||
|
if _, ok := d.activeContainers[id]; !ok {
|
||||||
|
return nil, fmt.Errorf("%s is not a key in active containers", id)
|
||||||
|
}
|
||||||
|
// FIXME:
|
||||||
|
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package clr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCannotParse = errors.New("cannot parse raw input")
|
||||||
|
)
|
||||||
|
|
||||||
|
type clrInfo struct {
|
||||||
|
Running bool
|
||||||
|
Pid int
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseClrInfo(name, raw string) (*clrInfo, error) {
|
||||||
|
if raw == "" {
|
||||||
|
return nil, ErrCannotParse
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
info = &clrInfo{}
|
||||||
|
)
|
||||||
|
|
||||||
|
fields := strings.Fields(strings.TrimSpace(raw))
|
||||||
|
|
||||||
|
// The format is expected to be:
|
||||||
|
//
|
||||||
|
// <pid> <name> <state>
|
||||||
|
//
|
||||||
|
if len(fields) != 3 {
|
||||||
|
return nil, ErrCannotParse
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Pid, err = strconv.Atoi(fields[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrCannotParse
|
||||||
|
}
|
||||||
|
|
||||||
|
if fields[1] != name {
|
||||||
|
return nil, ErrCannotParse
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Running = fields[2] == "running"
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package clr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args provided to the init function for a driver
|
||||||
|
type InitArgs struct {
|
||||||
|
User string
|
||||||
|
Gateway string
|
||||||
|
Ip string
|
||||||
|
WorkDir string
|
||||||
|
Privileged bool
|
||||||
|
Env []string
|
||||||
|
Args []string
|
||||||
|
Mtu int
|
||||||
|
Console string
|
||||||
|
Pipe int
|
||||||
|
Root string
|
||||||
|
CapAdd string
|
||||||
|
CapDrop string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArgs() *InitArgs {
|
||||||
|
var (
|
||||||
|
// Get cmdline arguments
|
||||||
|
user = flag.String("u", "", "username or uid")
|
||||||
|
gateway = flag.String("g", "", "gateway address")
|
||||||
|
ip = flag.String("i", "", "ip address")
|
||||||
|
workDir = flag.String("w", "", "workdir")
|
||||||
|
privileged = flag.Bool("privileged", false, "privileged mode")
|
||||||
|
mtu = flag.Int("mtu", 1500, "interface mtu")
|
||||||
|
capAdd = flag.String("cap-add", "", "capabilities to add")
|
||||||
|
capDrop = flag.String("cap-drop", "", "capabilities to drop")
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
return &InitArgs{
|
||||||
|
User: *user,
|
||||||
|
Gateway: *gateway,
|
||||||
|
Ip: *ip,
|
||||||
|
WorkDir: *workDir,
|
||||||
|
Privileged: *privileged,
|
||||||
|
Args: flag.Args(),
|
||||||
|
Mtu: *mtu,
|
||||||
|
CapAdd: *capAdd,
|
||||||
|
CapDrop: *capDrop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear environment pollution introduced by lxc-start
|
||||||
|
func setupEnv(args *InitArgs) error {
|
||||||
|
// Get env
|
||||||
|
var env []string
|
||||||
|
dockerenv, err := os.Open(".dockerenv")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load environment variables: %v", err)
|
||||||
|
}
|
||||||
|
defer dockerenv.Close()
|
||||||
|
if err := json.NewDecoder(dockerenv).Decode(&env); err != nil {
|
||||||
|
return fmt.Errorf("Unable to decode environment variables: %v", err)
|
||||||
|
}
|
||||||
|
// Propagate the plugin-specific container env variable
|
||||||
|
env = append(env, "container="+os.Getenv("container"))
|
||||||
|
|
||||||
|
args.Env = env
|
||||||
|
|
||||||
|
os.Clearenv()
|
||||||
|
for _, kv := range args.Env {
|
||||||
|
parts := strings.SplitN(kv, "=", 2)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
parts = append(parts, "")
|
||||||
|
}
|
||||||
|
os.Setenv(parts[0], parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup working directory
|
||||||
|
func setupWorkingDirectory(args *InitArgs) error {
|
||||||
|
if args.WorkDir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := syscall.Chdir(args.WorkDir); err != nil {
|
||||||
|
return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+32
-27
@@ -7,7 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
// TODO Windows: Factor out ulimit
|
// TODO Windows: Factor out ulimit
|
||||||
|
"github.com/docker/docker/daemon/network"
|
||||||
"github.com/docker/docker/pkg/ulimit"
|
"github.com/docker/docker/pkg/ulimit"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
)
|
)
|
||||||
@@ -150,31 +152,34 @@ type ProcessConfig struct {
|
|||||||
//
|
//
|
||||||
// Process wrapps an os/exec.Cmd to add more metadata
|
// Process wrapps an os/exec.Cmd to add more metadata
|
||||||
type Command struct {
|
type Command struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Rootfs string `json:"rootfs"` // root fs of the container
|
Rootfs string `json:"rootfs"` // root fs of the container
|
||||||
ReadonlyRootfs bool `json:"readonly_rootfs"`
|
ReadonlyRootfs bool `json:"readonly_rootfs"`
|
||||||
InitPath string `json:"initpath"` // dockerinit
|
InitPath string `json:"initpath"` // dockerinit
|
||||||
WorkingDir string `json:"working_dir"`
|
WorkingDir string `json:"working_dir"`
|
||||||
ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
|
ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
|
||||||
Network *Network `json:"network"`
|
Network *Network `json:"network"`
|
||||||
Ipc *Ipc `json:"ipc"`
|
Ipc *Ipc `json:"ipc"`
|
||||||
Pid *Pid `json:"pid"`
|
Pid *Pid `json:"pid"`
|
||||||
UTS *UTS `json:"uts"`
|
UTS *UTS `json:"uts"`
|
||||||
Resources *Resources `json:"resources"`
|
Resources *Resources `json:"resources"`
|
||||||
Mounts []Mount `json:"mounts"`
|
Mounts []Mount `json:"mounts"`
|
||||||
AllowedDevices []*configs.Device `json:"allowed_devices"`
|
AllowedDevices []*configs.Device `json:"allowed_devices"`
|
||||||
AutoCreatedDevices []*configs.Device `json:"autocreated_devices"`
|
AutoCreatedDevices []*configs.Device `json:"autocreated_devices"`
|
||||||
CapAdd []string `json:"cap_add"`
|
CapAdd []string `json:"cap_add"`
|
||||||
CapDrop []string `json:"cap_drop"`
|
CapDrop []string `json:"cap_drop"`
|
||||||
GroupAdd []string `json:"group_add"`
|
GroupAdd []string `json:"group_add"`
|
||||||
ContainerPid int `json:"container_pid"` // the pid for the process inside a container
|
ContainerPid int `json:"container_pid"` // the pid for the process inside a container
|
||||||
ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container.
|
ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container.
|
||||||
ProcessLabel string `json:"process_label"`
|
ProcessLabel string `json:"process_label"`
|
||||||
MountLabel string `json:"mount_label"`
|
MountLabel string `json:"mount_label"`
|
||||||
LxcConfig []string `json:"lxc_config"`
|
LxcConfig []string `json:"lxc_config"`
|
||||||
AppArmorProfile string `json:"apparmor_profile"`
|
AppArmorProfile string `json:"apparmor_profile"`
|
||||||
CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command.
|
CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command.
|
||||||
FirstStart bool `json:"first_start"`
|
FirstStart bool `json:"first_start"`
|
||||||
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
|
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
|
||||||
LayerFolder string `json:"layer_folder"`
|
LayerFolder string `json:"layer_folder"`
|
||||||
|
NetworkSettings *network.Settings `json:"network_settings"`
|
||||||
|
EndpointInfo []map[string]interface{} `json:"endpoint_info"`
|
||||||
|
HostConfig *runconfig.HostConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,27 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
"github.com/docker/docker/daemon/execdriver/clr"
|
||||||
"github.com/docker/docker/daemon/execdriver/lxc"
|
"github.com/docker/docker/daemon/execdriver/lxc"
|
||||||
"github.com/docker/docker/daemon/execdriver/native"
|
"github.com/docker/docker/daemon/execdriver/native"
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
||||||
|
rootPath := path.Join(root, "execdriver", name)
|
||||||
switch name {
|
switch name {
|
||||||
|
case "clr":
|
||||||
|
return clr.NewDriver(rootPath, libPath, initPath, sysInfo.AppArmor)
|
||||||
case "lxc":
|
case "lxc":
|
||||||
// we want to give the lxc driver the full docker root because it needs
|
// we want to give the lxc driver the full docker root because it needs
|
||||||
// to access and write config and template files in /var/lib/docker/containers/*
|
// to access and write config and template files in /var/lib/docker/containers/*
|
||||||
// to be backwards compatible
|
// to be backwards compatible
|
||||||
|
logrus.Warn("LXC built-in support is deprecated.")
|
||||||
return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor)
|
return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor)
|
||||||
case "native":
|
case "native":
|
||||||
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, options)
|
return native.NewDriver(rootPath, initPath, options)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unknown exec driver %s", name)
|
return nil, fmt.Errorf("unknown exec driver %s", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
apparmorProfilePath = "/etc/apparmor.d/docker"
|
||||||
|
)
|
||||||
|
|
||||||
|
type data struct {
|
||||||
|
Name string
|
||||||
|
Imports []string
|
||||||
|
InnerImports []string
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseTemplate = `
|
||||||
|
{{range $value := .Imports}}
|
||||||
|
{{$value}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
|
||||||
|
{{range $value := .InnerImports}}
|
||||||
|
{{$value}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
network,
|
||||||
|
capability,
|
||||||
|
file,
|
||||||
|
umount,
|
||||||
|
|
||||||
|
deny @{PROC}/sys/fs/** wklx,
|
||||||
|
deny @{PROC}/fs/** wklx,
|
||||||
|
deny @{PROC}/sysrq-trigger rwklx,
|
||||||
|
deny @{PROC}/mem rwklx,
|
||||||
|
deny @{PROC}/kmem rwklx,
|
||||||
|
deny @{PROC}/kcore rwklx,
|
||||||
|
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
|
||||||
|
deny @{PROC}/sys/kernel/*/** wklx,
|
||||||
|
|
||||||
|
deny mount,
|
||||||
|
|
||||||
|
deny /sys/[^f]*/** wklx,
|
||||||
|
deny /sys/f[^s]*/** wklx,
|
||||||
|
deny /sys/fs/[^c]*/** wklx,
|
||||||
|
deny /sys/fs/c[^g]*/** wklx,
|
||||||
|
deny /sys/fs/cg[^r]*/** wklx,
|
||||||
|
deny /sys/firmware/efi/efivars/** rwklx,
|
||||||
|
deny /sys/kernel/security/** rwklx,
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func generateProfile(out io.Writer) error {
|
||||||
|
compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := &data{
|
||||||
|
Name: "docker-default",
|
||||||
|
}
|
||||||
|
if tunablesExists() {
|
||||||
|
data.Imports = append(data.Imports, "#include <tunables/global>")
|
||||||
|
} else {
|
||||||
|
data.Imports = append(data.Imports, "@{PROC}=/proc/")
|
||||||
|
}
|
||||||
|
if abstractionsExists() {
|
||||||
|
data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
|
||||||
|
}
|
||||||
|
if err := compiled.Execute(out, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the tunables/global exist
|
||||||
|
func tunablesExists() bool {
|
||||||
|
_, err := os.Stat("/etc/apparmor.d/tunables/global")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if abstractions/base exist
|
||||||
|
func abstractionsExists() bool {
|
||||||
|
_, err := os.Stat("/etc/apparmor.d/abstractions/base")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func installAppArmorProfile() error {
|
||||||
|
if !apparmor.IsEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure /etc/apparmor.d exists
|
||||||
|
if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := generateProfile(f); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
|
||||||
|
// to use the parser directly we have to make sure we are in the correct
|
||||||
|
// dir with the profile
|
||||||
|
cmd.Dir = "/etc/apparmor.d"
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasAppArmorProfileLoaded(profile string) error {
|
||||||
|
file, err := os.Open("/sys/kernel/security/apparmor/profiles")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r := bufio.NewReader(file)
|
||||||
|
for {
|
||||||
|
p, err := r.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(p, profile+" ") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,7 +85,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* These paths must be remounted as r/o */
|
/* These paths must be remounted as r/o */
|
||||||
container.ReadonlyPaths = append(container.ReadonlyPaths, "/proc", "/dev")
|
container.ReadonlyPaths = append(container.ReadonlyPaths, "/dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.setupMounts(container, c); err != nil {
|
if err := d.setupMounts(container, c); err != nil {
|
||||||
@@ -200,7 +200,6 @@ func (d *driver) setPrivileged(container *configs.Config) (err error) {
|
|||||||
if apparmor.IsEnabled() {
|
if apparmor.IsEnabled() {
|
||||||
container.AppArmorProfile = "unconfined"
|
container.AppArmorProfile = "unconfined"
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
sysinfo "github.com/docker/docker/pkg/system"
|
sysinfo "github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
"github.com/opencontainers/runc/libcontainer/system"
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
@@ -51,6 +52,20 @@ func NewDriver(root, initPath string, options []string) (*driver, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apparmor.IsEnabled() {
|
||||||
|
if err := installAppArmorProfile(); err != nil {
|
||||||
|
apparmorProfiles := []string{"docker-default"}
|
||||||
|
|
||||||
|
// Allow daemon to run if loading failed, but are active
|
||||||
|
// (possibly through another run, manually, or via system startup)
|
||||||
|
for _, policy := range apparmorProfiles {
|
||||||
|
if err := hasAppArmorProfileLoaded(policy); err != nil {
|
||||||
|
return nil, fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", policy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// choose cgroup manager
|
// choose cgroup manager
|
||||||
// this makes sure there are no breaking changes to people
|
// this makes sure there are no breaking changes to people
|
||||||
// who upgrade from versions without native.cgroupdriver opt
|
// who upgrade from versions without native.cgroupdriver opt
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ func (a *Driver) Diff(id, parent string) (archive.Archive, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error {
|
func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error {
|
||||||
return chrootarchive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
|
return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiffSize calculates the changes between the specified id
|
// DiffSize calculates the changes between the specified id
|
||||||
|
|||||||
@@ -1482,12 +1482,16 @@ func (devices *DeviceSet) deactivatePool() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if d, err := devicemapper.GetDeps(devname); err == nil {
|
|
||||||
// Access to more Debug output
|
if devinfo.Exists == 0 {
|
||||||
logrus.Debugf("[devmapper] devicemapper.GetDeps() %s: %#v", devname, d)
|
return nil
|
||||||
}
|
}
|
||||||
if devinfo.Exists != 0 {
|
if err := devicemapper.RemoveDevice(devname); err != nil {
|
||||||
return devicemapper.RemoveDevice(devname)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, err := devicemapper.GetDeps(devname); err == nil {
|
||||||
|
logrus.Warnf("[devmapper] device %s still has %d active dependents", devname, d.Count)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ type Driver interface {
|
|||||||
// ApplyDiff extracts the changeset from the given diff into the
|
// ApplyDiff extracts the changeset from the given diff into the
|
||||||
// layer with the specified id and parent, returning the size of the
|
// layer with the specified id and parent, returning the size of the
|
||||||
// new layer in bytes.
|
// new layer in bytes.
|
||||||
|
// The archive.ArchiveReader must be an uncompressed stream.
|
||||||
ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
|
ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
|
||||||
// DiffSize calculates the changes between the specified id
|
// DiffSize calculates the changes between the specified id
|
||||||
// and its parent and returns the size in bytes of the changes
|
// and its parent and returns the size in bytes of the changes
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func (gdw *naiveDiffDriver) ApplyDiff(id, parent string, diff archive.ArchiveRea
|
|||||||
|
|
||||||
start := time.Now().UTC()
|
start := time.Now().UTC()
|
||||||
logrus.Debugf("Start untar layer")
|
logrus.Debugf("Start untar layer")
|
||||||
if size, err = chrootarchive.ApplyLayer(layerFs, diff); err != nil {
|
if size, err = chrootarchive.ApplyUncompressedLayer(layerFs, diff); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff archive.ArchiveReader)
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if size, err = chrootarchive.ApplyLayer(tmpRootDir, diff); err != nil {
|
if size, err = chrootarchive.ApplyUncompressedLayer(tmpRootDir, diff); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -2,6 +2,7 @@ package daemon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
@@ -91,13 +92,13 @@ func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSON
|
|||||||
Pid: container.State.Pid,
|
Pid: container.State.Pid,
|
||||||
ExitCode: container.State.ExitCode,
|
ExitCode: container.State.ExitCode,
|
||||||
Error: container.State.Error,
|
Error: container.State.Error,
|
||||||
StartedAt: container.State.StartedAt,
|
StartedAt: container.State.StartedAt.Format(time.RFC3339Nano),
|
||||||
FinishedAt: container.State.FinishedAt,
|
FinishedAt: container.State.FinishedAt.Format(time.RFC3339Nano),
|
||||||
}
|
}
|
||||||
|
|
||||||
contJSONBase := &types.ContainerJSONBase{
|
contJSONBase := &types.ContainerJSONBase{
|
||||||
Id: container.ID,
|
Id: container.ID,
|
||||||
Created: container.Created,
|
Created: container.Created.Format(time.RFC3339Nano),
|
||||||
Path: container.Path,
|
Path: container.Path,
|
||||||
Args: container.Args,
|
Args: container.Args,
|
||||||
State: containerState,
|
State: containerState,
|
||||||
|
|||||||
+3
-6
@@ -1,9 +1,6 @@
|
|||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"fmt"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerKill send signal to the container
|
// ContainerKill send signal to the container
|
||||||
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
||||||
@@ -18,12 +15,12 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
|
|||||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||||
if err := container.Kill(); err != nil {
|
if err := container.Kill(); err != nil {
|
||||||
return fmt.Errorf("Cannot kill container %s: %s", name, err)
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just send the requested signal
|
// Otherwise, just send the requested signal
|
||||||
if err := container.KillSig(int(sig)); err != nil {
|
if err := container.KillSig(int(sig)); err != nil {
|
||||||
return fmt.Errorf("Cannot kill container %s: %s", name, err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -93,9 +93,9 @@ func New(ctx logger.Context) (logger.Logger, error) {
|
|||||||
}
|
}
|
||||||
logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag)
|
logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag)
|
||||||
|
|
||||||
// logger tries to recoonect 2**64 - 1 times
|
// logger tries to recoonect 2**32 - 1 times
|
||||||
// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
|
// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
|
||||||
log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxUint32})
|
log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
|
|||||||
if !config.Follow {
|
if !config.Follow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if config.Tail == 0 {
|
|
||||||
|
if config.Tail >= 0 {
|
||||||
latestFile.Seek(0, os.SEEK_END)
|
latestFile.Seek(0, os.SEEK_END)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,12 @@ func NewLogWatcher() *LogWatcher {
|
|||||||
|
|
||||||
// Close notifies the underlying log reader to stop
|
// Close notifies the underlying log reader to stop
|
||||||
func (w *LogWatcher) Close() {
|
func (w *LogWatcher) Close() {
|
||||||
close(w.closeNotifier)
|
// only close if not already closed
|
||||||
|
select {
|
||||||
|
case <-w.closeNotifier:
|
||||||
|
default:
|
||||||
|
close(w.closeNotifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchClose returns a channel receiver that receives notification when the watcher has been closed
|
// WatchClose returns a channel receiver that receives notification when the watcher has been closed
|
||||||
|
|||||||
@@ -138,6 +138,30 @@ func (m *containerMonitor) Start() error {
|
|||||||
|
|
||||||
m.lastStartTime = time.Now()
|
m.lastStartTime = time.Now()
|
||||||
|
|
||||||
|
// Make the network settings available to the execution
|
||||||
|
// driver to allow for integration with libnetwork networking.
|
||||||
|
m.container.command.NetworkSettings = m.container.NetworkSettings
|
||||||
|
|
||||||
|
// Allow the execution driver to query memory limits
|
||||||
|
m.container.command.HostConfig = m.container.hostConfig
|
||||||
|
|
||||||
|
// Make the network endpoint details available to the execution
|
||||||
|
// driver as well.
|
||||||
|
n, _err := m.container.daemon.netController.NetworkByID(m.container.NetworkSettings.NetworkID)
|
||||||
|
if _err == nil {
|
||||||
|
var eps []map[string]interface{}
|
||||||
|
|
||||||
|
for _, ep := range n.Endpoints() {
|
||||||
|
info, err := ep.DriverInfo()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
eps = append(eps, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.container.command.EndpointInfo = eps
|
||||||
|
}
|
||||||
|
|
||||||
if exitStatus, err = m.container.daemon.Run(m.container, pipes, m.callback); err != nil {
|
if exitStatus, err = m.container.daemon.Run(m.container, pipes, m.callback); err != nil {
|
||||||
// if we receive an internal error from the initial start of a container then lets
|
// if we receive an internal error from the initial start of a container then lets
|
||||||
// return it instead of entering the restart loop
|
// return it instead of entering the restart loop
|
||||||
|
|||||||
+3
-5
@@ -76,6 +76,7 @@ func NewDaemonCli() *DaemonCli {
|
|||||||
|
|
||||||
// TODO(tiborvass): remove InstallFlags?
|
// TODO(tiborvass): remove InstallFlags?
|
||||||
daemonConfig := new(daemon.Config)
|
daemonConfig := new(daemon.Config)
|
||||||
|
daemonConfig.LogConfig.Config = make(map[string]string)
|
||||||
daemonConfig.InstallFlags(daemonFlags, presentInHelp)
|
daemonConfig.InstallFlags(daemonFlags, presentInHelp)
|
||||||
daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
|
daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
|
||||||
registryOptions := new(registry.Options)
|
registryOptions := new(registry.Options)
|
||||||
@@ -100,6 +101,7 @@ func migrateKey() (err error) {
|
|||||||
err = os.Remove(oldPath)
|
err = os.Remove(oldPath)
|
||||||
} else {
|
} else {
|
||||||
logrus.Warnf("Key migration failed, key file not removed at %s", oldPath)
|
logrus.Warnf("Key migration failed, key file not removed at %s", oldPath)
|
||||||
|
os.Remove(newPath)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -207,10 +209,6 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if cli.LogConfig.Config == nil {
|
|
||||||
cli.LogConfig.Config = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverConfig := &apiserver.ServerConfig{
|
serverConfig := &apiserver.ServerConfig{
|
||||||
Logging: true,
|
Logging: true,
|
||||||
EnableCors: cli.EnableCors,
|
EnableCors: cli.EnableCors,
|
||||||
@@ -226,7 +224,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
|
|||||||
}
|
}
|
||||||
tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
|
tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalf("foobar: %v", err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
serverConfig.TLSConfig = tlsConfig
|
serverConfig.TLSConfig = tlsConfig
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ func main() {
|
|||||||
flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
|
flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
|
||||||
|
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+" docker [ -h | --help | -v | --version ]\n\n")
|
fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+" docker [ --help | -v | --version ]\n\n")
|
||||||
fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
|
fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
|
||||||
|
|
||||||
flag.CommandLine.SetOutput(os.Stdout)
|
flag.CommandLine.SetOutput(os.Stdout)
|
||||||
|
|||||||
+8
-6
@@ -87,8 +87,8 @@ own.
|
|||||||
container with this image.
|
container with this image.
|
||||||
|
|
||||||
The container exposes port 8000 on the localhost so that you can connect and
|
The container exposes port 8000 on the localhost so that you can connect and
|
||||||
see your changes. If you are running Boot2Docker, use the `boot2docker ip`
|
see your changes. If you use Docker Machine, the `docker-machine ip
|
||||||
to get the address of your server.
|
<machine-name>` command gives you the address of your server.
|
||||||
|
|
||||||
6. Check your writing for style and mechanical errors.
|
6. Check your writing for style and mechanical errors.
|
||||||
|
|
||||||
@@ -158,18 +158,20 @@ update the root docs pages by running
|
|||||||
|
|
||||||
$ make AWS_S3_BUCKET=dowideit-docs BUILD_ROOT=yes docs-release
|
$ make AWS_S3_BUCKET=dowideit-docs BUILD_ROOT=yes docs-release
|
||||||
|
|
||||||
### Errors publishing using Boot2Docker
|
### Errors publishing using a Docker Machine VM
|
||||||
|
|
||||||
Sometimes, in a Boot2Docker environment, the publishing procedure returns this
|
Sometimes, in a Windows or Mac environment, the publishing procedure returns this
|
||||||
error:
|
error:
|
||||||
|
|
||||||
Post http:///var/run/docker.sock/build?rm=1&t=docker-docs%3Apost-1.2.0-docs_update-2:
|
Post http:///var/run/docker.sock/build?rm=1&t=docker-docs%3Apost-1.2.0-docs_update-2:
|
||||||
dial unix /var/run/docker.sock: no such file or directory.
|
dial unix /var/run/docker.sock: no such file or directory.
|
||||||
|
|
||||||
If this happens, set the Docker host. Run the following command to set the
|
If this happens, set the Docker host. Run the following command to get the
|
||||||
variables in your shell:
|
variables in your shell:
|
||||||
|
|
||||||
$ eval "$(boot2docker shellinit)"
|
docker-machine env <machine-name>
|
||||||
|
|
||||||
|
Then, set your environment accordingly.
|
||||||
|
|
||||||
## Cherry-picking documentation changes to update an existing release.
|
## Cherry-picking documentation changes to update an existing release.
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ image cache.
|
|||||||
> characters of the full image ID - which can be found using
|
> characters of the full image ID - which can be found using
|
||||||
> `docker inspect` or `docker images --no-trunc=true`.
|
> `docker inspect` or `docker images --no-trunc=true`.
|
||||||
|
|
||||||
> **Note:** if you are using a remote Docker daemon, such as Boot2Docker,
|
|
||||||
> then _do not_ type the `sudo` before the `docker` commands shown in the
|
|
||||||
> documentation's examples.
|
|
||||||
|
|
||||||
## Running an interactive shell
|
## Running an interactive shell
|
||||||
|
|
||||||
To run an interactive shell in the Ubuntu image:
|
To run an interactive shell in the Ubuntu image:
|
||||||
|
|||||||
@@ -11,111 +11,7 @@ weight = 7
|
|||||||
|
|
||||||
# Using certificates for repository client verification
|
# Using certificates for repository client verification
|
||||||
|
|
||||||
In [Running Docker with HTTPS](/articles/https), you learned that, by default,
|
The orginal content was deprecated. For information about configuring
|
||||||
Docker runs via a non-networked Unix socket and TLS must be enabled in order
|
cerficates, see [deploying a registry
|
||||||
to have the Docker client and the daemon communicate securely over HTTPS.
|
server](http://docs.docker.com/registry/deploying/). To reach an older version
|
||||||
|
of this content, refer to an older version of the documentation.
|
||||||
Now, you will see how to allow the Docker registry (i.e., *a server*) to
|
|
||||||
verify that the Docker daemon (i.e., *a client*) has the right to access the
|
|
||||||
images being hosted with *certificate-based client-server authentication*.
|
|
||||||
|
|
||||||
We will show you how to install a Certificate Authority (CA) root certificate
|
|
||||||
for the registry and how to set the client TLS certificate for verification.
|
|
||||||
|
|
||||||
## Understanding the configuration
|
|
||||||
|
|
||||||
A custom certificate is configured by creating a directory under
|
|
||||||
`/etc/docker/certs.d` using the same name as the registry's hostname (e.g.,
|
|
||||||
`localhost`). All `*.crt` files are added to this directory as CA roots.
|
|
||||||
|
|
||||||
> **Note:**
|
|
||||||
> In the absence of any root certificate authorities, Docker
|
|
||||||
> will use the system default (i.e., host's root CA set).
|
|
||||||
|
|
||||||
The presence of one or more `<filename>.key/cert` pairs indicates to Docker
|
|
||||||
that there are custom certificates required for access to the desired
|
|
||||||
repository.
|
|
||||||
|
|
||||||
> **Note:**
|
|
||||||
> If there are multiple certificates, each will be tried in alphabetical
|
|
||||||
> order. If there is an authentication error (e.g., 403, 404, 5xx, etc.), Docker
|
|
||||||
> will continue to try with the next certificate.
|
|
||||||
|
|
||||||
Our example is set up like this:
|
|
||||||
|
|
||||||
/etc/docker/certs.d/ <-- Certificate directory
|
|
||||||
└── localhost <-- Hostname
|
|
||||||
├── client.cert <-- Client certificate
|
|
||||||
├── client.key <-- Client key
|
|
||||||
└── localhost.crt <-- Registry certificate
|
|
||||||
|
|
||||||
## Creating the client certificates
|
|
||||||
|
|
||||||
You will use OpenSSL's `genrsa` and `req` commands to first generate an RSA
|
|
||||||
key and then use the key to create the certificate.
|
|
||||||
|
|
||||||
$ openssl genrsa -out client.key 4096
|
|
||||||
$ openssl req -new -x509 -text -key client.key -out client.cert
|
|
||||||
|
|
||||||
> **Warning:**:
|
|
||||||
> Using TLS and managing a CA is an advanced topic.
|
|
||||||
> You should be familiar with OpenSSL, x509, and TLS before
|
|
||||||
> attempting to use them in production.
|
|
||||||
|
|
||||||
> **Warning:**
|
|
||||||
> These TLS commands will only generate a working set of certificates on Linux.
|
|
||||||
> The version of OpenSSL in Mac OS X is incompatible with the type of
|
|
||||||
> certificate Docker requires.
|
|
||||||
|
|
||||||
## Testing the verification setup
|
|
||||||
|
|
||||||
You can test this setup by using Apache to host a Docker registry.
|
|
||||||
For this purpose, you can copy a registry tree (containing images) inside
|
|
||||||
the Apache root.
|
|
||||||
|
|
||||||
> **Note:**
|
|
||||||
> You can find such an example [here](
|
|
||||||
> http://people.gnome.org/~alexl/v1.tar.gz) - which contains the busybox image.
|
|
||||||
|
|
||||||
Once you set up the registry, you can use the following Apache configuration
|
|
||||||
to implement certificate-based protection.
|
|
||||||
|
|
||||||
# This must be in the root context, otherwise it causes a re-negotiation
|
|
||||||
# which is not supported by the TLS implementation in go
|
|
||||||
SSLVerifyClient optional_no_ca
|
|
||||||
|
|
||||||
<Location /v1>
|
|
||||||
Action cert-protected /cgi-bin/cert.cgi
|
|
||||||
SetHandler cert-protected
|
|
||||||
|
|
||||||
Header set x-docker-registry-version "0.6.2"
|
|
||||||
SetEnvIf Host (.*) custom_host=$1
|
|
||||||
Header set X-Docker-Endpoints "%{custom_host}e"
|
|
||||||
</Location>
|
|
||||||
|
|
||||||
Save the above content as `/etc/httpd/conf.d/registry.conf`, and
|
|
||||||
continue with creating a `cert.cgi` file under `/var/www/cgi-bin/`.
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
if [ "$HTTPS" != "on" ]; then
|
|
||||||
echo "Status: 403 Not using SSL"
|
|
||||||
echo "x-docker-registry-version: 0.6.2"
|
|
||||||
echo
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
if [ "$SSL_CLIENT_VERIFY" == "NONE" ]; then
|
|
||||||
echo "Status: 403 Client certificate invalid"
|
|
||||||
echo "x-docker-registry-version: 0.6.2"
|
|
||||||
echo
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Content-length: $(stat --printf='%s' $PATH_TRANSLATED)"
|
|
||||||
echo "x-docker-registry-version: 0.6.2"
|
|
||||||
echo "X-Docker-Endpoints: $SERVER_NAME"
|
|
||||||
echo "X-Docker-Size: 0"
|
|
||||||
echo
|
|
||||||
|
|
||||||
cat $PATH_TRANSLATED
|
|
||||||
|
|
||||||
This CGI script will ensure that all requests to `/v1` *without* a valid
|
|
||||||
certificate will be returned with a `403` (i.e., HTTP forbidden) error.
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ weight = 99
|
|||||||
# Automatically start containers
|
# Automatically start containers
|
||||||
|
|
||||||
As of Docker 1.2,
|
As of Docker 1.2,
|
||||||
[restart policies](/reference/commandline/cli/#restart-policies) are the
|
[restart policies](/reference/run/#restart-policies-restart) are the
|
||||||
built-in Docker mechanism for restarting containers when they exit. If set,
|
built-in Docker mechanism for restarting containers when they exit. If set,
|
||||||
restart policies will be used when the Docker daemon starts up, as typically
|
restart policies will be used when the Docker daemon starts up, as typically
|
||||||
happens after a system boot. Restart policies will ensure that linked containers
|
happens after a system boot. Restart policies will ensure that linked containers
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ First generate CA private and public keys:
|
|||||||
State or Province Name (full name) [Some-State]:Queensland
|
State or Province Name (full name) [Some-State]:Queensland
|
||||||
Locality Name (eg, city) []:Brisbane
|
Locality Name (eg, city) []:Brisbane
|
||||||
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
||||||
Organizational Unit Name (eg, section) []:Boot2Docker
|
Organizational Unit Name (eg, section) []:Sales
|
||||||
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
||||||
Email Address []:Sven@home.org.au
|
Email Address []:Sven@home.org.au
|
||||||
|
|
||||||
|
|||||||
@@ -11,81 +11,8 @@ weight = 8
|
|||||||
|
|
||||||
# Run a local registry mirror
|
# Run a local registry mirror
|
||||||
|
|
||||||
## Why?
|
The orginal content was deprecated. [An archived
|
||||||
|
version](https://docs.docker.com/v1.6/articles/registry_mirror) is available in
|
||||||
If you have multiple instances of Docker running in your environment
|
the 1.7 documentation. For information about configuring mirrors with the latest
|
||||||
(e.g., multiple physical or virtual machines, all running the Docker
|
Docker Registry version, please file a support request with [the Distribution
|
||||||
daemon), each time one of them requires an image that it doesn't have
|
project](https://github.com/docker/distribution/issues).
|
||||||
it will go out to the internet and fetch it from the public Docker
|
|
||||||
registry. By running a local registry mirror, you can keep most of the
|
|
||||||
image fetch traffic on your local network.
|
|
||||||
|
|
||||||
## How does it work?
|
|
||||||
|
|
||||||
The first time you request an image from your local registry mirror,
|
|
||||||
it pulls the image from the public Docker registry and stores it locally
|
|
||||||
before handing it back to you. On subsequent requests, the local registry
|
|
||||||
mirror is able to serve the image from its own storage.
|
|
||||||
|
|
||||||
## How do I set up a local registry mirror?
|
|
||||||
|
|
||||||
There are two steps to set up and use a local registry mirror.
|
|
||||||
|
|
||||||
### Step 1: Configure your Docker daemons to use the local registry mirror
|
|
||||||
|
|
||||||
You will need to pass the `--registry-mirror` option to your Docker daemon on
|
|
||||||
startup:
|
|
||||||
|
|
||||||
docker daemon --registry-mirror=http://<my-docker-mirror-host>
|
|
||||||
|
|
||||||
For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run:
|
|
||||||
|
|
||||||
docker daemon --registry-mirror=http://10.0.0.2:5000
|
|
||||||
|
|
||||||
**NOTE:**
|
|
||||||
Depending on your local host setup, you may be able to add the
|
|
||||||
`--registry-mirror` options to the `DOCKER_OPTS` variable in
|
|
||||||
`/etc/default/docker`.
|
|
||||||
|
|
||||||
### Step 2: Run the local registry mirror
|
|
||||||
|
|
||||||
You will need to start a local registry mirror service. The
|
|
||||||
[`registry` image](https://registry.hub.docker.com/_/registry/) provides this
|
|
||||||
functionality. For example, to run a local registry mirror that serves on
|
|
||||||
port `5000` and mirrors the content at `registry-1.docker.io`:
|
|
||||||
|
|
||||||
docker run -p 5000:5000 \
|
|
||||||
-e STANDALONE=false \
|
|
||||||
-e MIRROR_SOURCE=https://registry-1.docker.io \
|
|
||||||
-e MIRROR_SOURCE_INDEX=https://index.docker.io \
|
|
||||||
registry
|
|
||||||
|
|
||||||
## Test it out
|
|
||||||
|
|
||||||
With your mirror running, pull an image that you haven't pulled before (using
|
|
||||||
`time` to time it):
|
|
||||||
|
|
||||||
$ time docker pull node:latest
|
|
||||||
Pulling repository node
|
|
||||||
[...]
|
|
||||||
|
|
||||||
real 1m14.078s
|
|
||||||
user 0m0.176s
|
|
||||||
sys 0m0.120s
|
|
||||||
|
|
||||||
Now, remove the image from your local machine:
|
|
||||||
|
|
||||||
$ docker rmi node:latest
|
|
||||||
|
|
||||||
Finally, re-pull the image:
|
|
||||||
|
|
||||||
$ time docker pull node:latest
|
|
||||||
Pulling repository node
|
|
||||||
[...]
|
|
||||||
|
|
||||||
real 0m51.376s
|
|
||||||
user 0m0.120s
|
|
||||||
sys 0m0.116s
|
|
||||||
|
|
||||||
The second time around, the local registry mirror served the image from storage,
|
|
||||||
avoiding a trip out to the internet to refetch it.
|
|
||||||
|
|||||||
+29
-11
@@ -33,17 +33,33 @@ If you want Docker to start at boot, you should also:
|
|||||||
There are a number of ways to configure the daemon flags and environment variables
|
There are a number of ways to configure the daemon flags and environment variables
|
||||||
for your Docker daemon.
|
for your Docker daemon.
|
||||||
|
|
||||||
If the `docker.service` file is set to use an `EnvironmentFile`
|
The recommended way is to use a systemd drop-in file. These are local files in
|
||||||
(often pointing to `/etc/sysconfig/docker`) then you can modify the
|
the `/etc/systemd/system/docker.service.d` directory. This could also be
|
||||||
referenced file.
|
`/etc/systemd/system/docker.service`, which also works for overriding the
|
||||||
|
defaults from `/lib/systemd/system/docker.service`.
|
||||||
|
|
||||||
Check if the `docker.service` uses an `EnvironmentFile`:
|
However, if you had previously used a package which had an `EnvironmentFile`
|
||||||
|
(often pointing to `/etc/sysconfig/docker`) then for backwards compatibility,
|
||||||
|
you drop a file in the `/etc/systemd/system/docker.service.d`
|
||||||
|
directory including the following:
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=-/etc/sysconfig/docker
|
||||||
|
EnvironmentFile=-/etc/sysconfig/docker-storage
|
||||||
|
EnvironmentFile=-/etc/sysconfig/docker-network
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS \
|
||||||
|
$DOCKER_STORAGE_OPTIONS \
|
||||||
|
$DOCKER_NETWORK_OPTIONS \
|
||||||
|
$BLOCK_REGISTRY \
|
||||||
|
$INSECURE_REGISTRY
|
||||||
|
|
||||||
|
To check if the `docker.service` uses an `EnvironmentFile`:
|
||||||
|
|
||||||
$ sudo systemctl show docker | grep EnvironmentFile
|
$ sudo systemctl show docker | grep EnvironmentFile
|
||||||
EnvironmentFile=-/etc/sysconfig/docker (ignore_errors=yes)
|
EnvironmentFile=-/etc/sysconfig/docker (ignore_errors=yes)
|
||||||
|
|
||||||
Alternatively, find out where the service file is located, and look for the
|
Alternatively, find out where the service file is located:
|
||||||
property:
|
|
||||||
|
|
||||||
$ sudo systemctl status docker | grep Loaded
|
$ sudo systemctl status docker | grep Loaded
|
||||||
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled)
|
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled)
|
||||||
@@ -69,18 +85,20 @@ In this example, we'll assume that your `docker.service` file looks something li
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=notify
|
Type=notify
|
||||||
EnvironmentFile=-/etc/sysconfig/docker
|
ExecStart=/usr/bin/docker daemon -H fd://
|
||||||
ExecStart=/usr/bin/docker daemon -H fd:// $OPTIONS
|
|
||||||
LimitNOFILE=1048576
|
LimitNOFILE=1048576
|
||||||
LimitNPROC=1048576
|
LimitNPROC=1048576
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
Also=docker.socket
|
Also=docker.socket
|
||||||
|
|
||||||
This will allow us to add extra flags to the `/etc/sysconfig/docker` file by
|
This will allow us to add extra flags via a drop-in file (mentioned above) by
|
||||||
setting `OPTIONS`:
|
placing a file containing the following in the `/etc/systemd/system/docker.service.d`
|
||||||
|
directory:
|
||||||
|
|
||||||
OPTIONS="--graph /mnt/docker-data --storage-driver btrfs"
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/docker daemon -H fd:// --graph /mnt/docker-data --storage-driver btrfs
|
||||||
|
|
||||||
You can also set other environment variables in this file, for example, the
|
You can also set other environment variables in this file, for example, the
|
||||||
`HTTP_PROXY` environment variables described below.
|
`HTTP_PROXY` environment variables described below.
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ Follow the instructions in the plugin's documentation.
|
|||||||
|
|
||||||
The following plugins exist:
|
The following plugins exist:
|
||||||
|
|
||||||
|
* The [Blockbridge plugin](https://github.com/blockbridge/blockbridge-docker-volume)
|
||||||
|
is a volume plugin that provides access to an extensible set of
|
||||||
|
container-based persistent storage options. It supports single and multi-host Docker
|
||||||
|
environments with features that include tenant isolation, automated
|
||||||
|
provisioning, encryption, secure deletion, snapshots and QoS.
|
||||||
|
|
||||||
* The [Flocker plugin](https://clusterhq.com/docker-plugin/) is a volume plugin
|
* The [Flocker plugin](https://clusterhq.com/docker-plugin/) is a volume plugin
|
||||||
which provides multi-host portable volumes for Docker, enabling you to run
|
which provides multi-host portable volumes for Docker, enabling you to run
|
||||||
databases and other stateful containers and move them around across a cluster
|
databases and other stateful containers and move them around across a cluster
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ a container. To exit the container type `exit`.
|
|||||||
If you want your containers to be able to access the external network you must
|
If you want your containers to be able to access the external network you must
|
||||||
enable the `net.ipv4.ip_forward` rule.
|
enable the `net.ipv4.ip_forward` rule.
|
||||||
This can be done using YaST by browsing to the
|
This can be done using YaST by browsing to the
|
||||||
`Network Devices -> Network Settings -> Routing` menu and ensuring that the
|
`System -> Network Settings -> Routing` menu (for openSUSE Tumbleweed and later) or `Network Devices -> Network Settings -> Routing` menu (for SUSE Linux Enterprise 12 and previous openSUSE versions) and ensuring that the `Enable IPv4 Forwarding` box is checked.
|
||||||
`Enable IPv4 Forwarding` box is checked.
|
|
||||||
|
|
||||||
This option cannot be changed when networking is handled by the Network Manager.
|
This option cannot be changed when networking is handled by the Network Manager.
|
||||||
In such cases the `/etc/sysconfig/SuSEfirewall2` file needs to be edited by
|
In such cases the `/etc/sysconfig/SuSEfirewall2` file needs to be edited by
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ which is officially supported by Docker.
|
|||||||
>command fails for the Docker repo during installation. To work around this,
|
>command fails for the Docker repo during installation. To work around this,
|
||||||
>add the key directly using the following:
|
>add the key directly using the following:
|
||||||
>
|
>
|
||||||
> $ wget -qO- https://get.docker.com/gpg | sudo apt-key add -
|
> $ curl -sSL https://get.docker.com/gpg | sudo apt-key add -
|
||||||
|
|
||||||
### Uninstallation
|
### Uninstallation
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,24 @@ If you need to add an HTTP Proxy, set a different directory or partition for the
|
|||||||
Docker runtime files, or make other customizations, read our Systemd article to
|
Docker runtime files, or make other customizations, read our Systemd article to
|
||||||
learn how to [customize your Systemd Docker daemon options](/articles/systemd/).
|
learn how to [customize your Systemd Docker daemon options](/articles/systemd/).
|
||||||
|
|
||||||
|
## Running Docker with a manually-defined network
|
||||||
|
|
||||||
|
If you manually configure your network using `systemd-network` with `systemd` version 219 or higher, containers you start with Docker may be unable to access your network.
|
||||||
|
Beginning with version 220, the forwarding setting for a given network (`net.ipv4.conf.<interface>.forwarding`) defaults to *off*. This setting prevents IP forwarding. It also conflicts with Docker which enables the `net.ipv4.conf.all.forwarding` setting within a container.
|
||||||
|
|
||||||
|
To work around this, edit the `<interface>.network` file in
|
||||||
|
`/usr/lib/systemd/network/` on your Docker host (ex: `/usr/lib/systemd/network/80-container-host0.network`) add the following block:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Network]
|
||||||
|
...
|
||||||
|
IPForward=kernel
|
||||||
|
# OR
|
||||||
|
IPForward=true
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows IP forwarding from the container as expected.
|
||||||
|
|
||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
|
|||||||
+240
-166
@@ -10,37 +10,34 @@ parent = "smn_engine"
|
|||||||
|
|
||||||
# Mac OS X
|
# Mac OS X
|
||||||
|
|
||||||
You can install Docker using Boot2Docker to run `docker` commands at your command-line.
|
> **Note**: This release of Docker deprecates the Boot2Docker command line in
|
||||||
Choose this installation if you are familiar with the command-line or plan to
|
> favor of Docker Machine. Use the Docker Toolbox to install Docker Machine as
|
||||||
contribute to the Docker project on GitHub.
|
> well as the other Docker tools.
|
||||||
|
|
||||||
[<img src="/installation/images/kitematic.png" alt="Download Kitematic"
|
You install Docker using Docker Toolbox. Docker Toolbox includes the following Docker tools:
|
||||||
style="float:right;">](https://kitematic.com/download)
|
|
||||||
|
|
||||||
Alternatively, you may want to try <a id="inlinelink" href="https://kitematic.com/"
|
* Docker Machine for running the `docker-machine` binary
|
||||||
target="_blank">Kitematic</a>, an application that lets you set up Docker and
|
* Docker Engine for running the `docker` binary
|
||||||
run containers using a graphical user interface (GUI).
|
* Docker Compose for running the `docker-compose` binary
|
||||||
|
* Kitematic, the Docker GUI
|
||||||
## Command-line Docker with Boot2Docker
|
* a shell preconfigured for a Docker command-line environment
|
||||||
|
* Oracle VM VirtualBox
|
||||||
|
|
||||||
Because the Docker daemon uses Linux-specific kernel features, you can't run
|
Because the Docker daemon uses Linux-specific kernel features, you can't run
|
||||||
Docker natively in OS X. Instead, you must install the Boot2Docker application.
|
Docker natively in OS X. Instead, you must use `docker-machine` to create and
|
||||||
The application includes a VirtualBox Virtual Machine (VM), Docker itself, and the
|
attach to a virtual machine (VM). This machine is a Linux VM that hosts Docker
|
||||||
Boot2Docker management tool.
|
for you on your Mac.
|
||||||
|
|
||||||
The Boot2Docker management tool is a lightweight Linux virtual machine made
|
|
||||||
specifically to run the Docker daemon on Mac OS X. The VirtualBox VM runs
|
|
||||||
completely from RAM, is a small ~24MB download, and boots in approximately 5s.
|
|
||||||
|
|
||||||
**Requirements**
|
**Requirements**
|
||||||
|
|
||||||
Your Mac must be running OS X 10.6 "Snow Leopard" or newer to run Boot2Docker.
|
Your Mac must be running OS X 10.8 "Mountain Lion" or newer to install the
|
||||||
|
Docker Toolbox.
|
||||||
|
|
||||||
### Learn the key concepts before installing
|
### Learn the key concepts before installing
|
||||||
|
|
||||||
In a Docker installation on Linux, your machine is both the localhost and the
|
In a Docker installation on Linux, your physical machine is both the localhost
|
||||||
Docker host. In networking, localhost means your computer. The Docker host is
|
and the Docker host. In networking, localhost means your computer. The Docker
|
||||||
the machine on which the containers run.
|
host is the computer on which the containers run.
|
||||||
|
|
||||||
On a typical Linux installation, the Docker client, the Docker daemon, and any
|
On a typical Linux installation, the Docker client, the Docker daemon, and any
|
||||||
containers run directly on your localhost. This means you can address ports on a
|
containers run directly on your localhost. This means you can address ports on a
|
||||||
@@ -49,135 +46,243 @@ Docker container using standard localhost addressing such as `localhost:8000` or
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
In an OS X installation, the `docker` daemon is running inside a Linux virtual
|
In an OS X installation, the `docker` daemon is running inside a Linux VM called
|
||||||
machine provided by Boot2Docker.
|
`default`. The `default` is a lightweight Linux VM made specifically to run
|
||||||
|
the Docker daemon on Mac OS X. The VM runs completely from RAM, is a small ~24MB
|
||||||
|
download, and boots in approximately 5s.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In OS X, the Docker host address is the address of the Linux VM.
|
In OS X, the Docker host address is the address of the Linux VM. When you start
|
||||||
When you start the `boot2docker` process, the VM is assigned an IP address. Under
|
the VM with `docker-machine` it is assigned an IP address. When you start a
|
||||||
`boot2docker` ports on a container map to ports on the VM. To see this in
|
container, the ports on a container map to ports on the VM. To see this in
|
||||||
practice, work through the exercises on this page.
|
practice, work through the exercises on this page.
|
||||||
|
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. Go to the [boot2docker/osx-installer ](
|
If you have VirtualBox running, you must shut it down before running the
|
||||||
https://github.com/boot2docker/osx-installer/releases/latest) release page.
|
installer.
|
||||||
|
|
||||||
4. Download Boot2Docker by clicking `Boot2Docker-x.x.x.pkg` in the "Downloads"
|
1. Go to the [Docker Toolbox](https://www.docker.com/toolbox) page.
|
||||||
section.
|
|
||||||
|
|
||||||
3. Install Boot2Docker by double-clicking the package.
|
2. Click the installer link to download.
|
||||||
|
|
||||||
The installer places Boot2Docker and VirtualBox in your "Applications" folder.
|
3. Install Docker Toolbox by double-clicking the package or by right-clicking
|
||||||
|
and choosing "Open" from the pop-up menu.
|
||||||
|
|
||||||
The installation places the `docker` and `boot2docker` binaries in your
|
The installer launches the "Install Docker Toolbox" dialog.
|
||||||
`/usr/local/bin` directory.
|
|
||||||
|

|
||||||
|
|
||||||
|
4. Press "Continue" to install the toolbox.
|
||||||
|
|
||||||
|
The installer presents you with options to customize the standard
|
||||||
|
installation.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
By default, the standard Docker Toolbox installation:
|
||||||
|
|
||||||
|
* installs binaries for the Docker tools in `/usr/local/bin`
|
||||||
|
* makes these binaries available to all users
|
||||||
|
* updates any existing VirtualBox installation
|
||||||
|
|
||||||
|
Change these defaults by pressing "Customize" or "Change
|
||||||
|
Install Location."
|
||||||
|
|
||||||
|
5. Press "Install" to perform the standard installation.
|
||||||
|
|
||||||
|
The system prompts you for your password.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. Provide your password to continue with the installation.
|
||||||
|
|
||||||
|
When it completes, the installer provides you with some information you can
|
||||||
|
use to complete some common tasks.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
7. Press "Close" to exit.
|
||||||
|
|
||||||
|
|
||||||
## Start the Boot2Docker Application
|
## Running a Docker Container
|
||||||
|
|
||||||
To run a Docker container, you first start the `boot2docker` VM and then issue
|
To run a Docker container, you:
|
||||||
`docker` commands to create, load, and manage containers. You can launch
|
|
||||||
`boot2docker` from your Applications folder or from the command line.
|
|
||||||
|
|
||||||
> **NOTE**: Boot2Docker is designed as a development tool. You should not use
|
* create a new (or start an existing) Docker virtual machine
|
||||||
> it in production environments.
|
* switch your environment to your new VM
|
||||||
|
* use the `docker` client to create, load, and manage containers
|
||||||
|
|
||||||
### From the Applications folder
|
Once you create a machine, you can reuse it as often as you like. Like any
|
||||||
|
VirtualBox VM, it maintains its configuration between uses.
|
||||||
|
|
||||||
When you launch the "Boot2Docker" application from your "Applications" folder, the
|
There are two ways to use the installed tools, from the Docker Quickstart Terminal or
|
||||||
application:
|
[from your shell](#from-your-shell).
|
||||||
|
|
||||||
* opens a terminal window
|
### From the Docker Quickstart Terminal
|
||||||
|
|
||||||
* creates a $HOME/.boot2docker directory
|
1. Open the "Applications" folder or the "Launchpad".
|
||||||
|
|
||||||
* creates a VirtualBox ISO and certs
|
2. Find the Docker Quickstart Terminal and double-click to launch it.
|
||||||
|
|
||||||
* starts a VirtualBox VM running the `docker` daemon
|
The application:
|
||||||
|
|
||||||
Once the launch completes, you can run `docker` commands. A good way to verify
|
* opens a terminal window
|
||||||
your setup succeeded is to run the `hello-world` container.
|
* creates a VM called `default` if it doesn't exists, starts the VM if it does
|
||||||
|
* points the terminal environment to this VM
|
||||||
|
|
||||||
$ docker run hello-world
|
Once the launch completes, the Docker Quickstart Terminal reports:
|
||||||
Unable to find image 'hello-world:latest' locally
|
|
||||||
511136ea3c5a: Pull complete
|
|
||||||
31cbccb51277: Pull complete
|
|
||||||
e45a5af57b00: Pull complete
|
|
||||||
hello-world:latest: The image you are pulling has been verified.
|
|
||||||
Important: image verification is a tech preview feature and should not be
|
|
||||||
relied on to provide security.
|
|
||||||
Status: Downloaded newer image for hello-world:latest
|
|
||||||
Hello from Docker.
|
|
||||||
This message shows that your installation appears to be working correctly.
|
|
||||||
|
|
||||||
To generate this message, Docker took the following steps:
|

|
||||||
1. The Docker client contacted the Docker daemon.
|
|
||||||
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
|
|
||||||
(Assuming it was not already locally available.)
|
|
||||||
3. The Docker daemon created a new container from that image which runs the
|
|
||||||
executable that produces the output you are currently reading.
|
|
||||||
4. The Docker daemon streamed that output to the Docker client, which sent it
|
|
||||||
to your terminal.
|
|
||||||
|
|
||||||
To try something more ambitious, you can run an Ubuntu container with:
|
Now, you can run `docker` commands.
|
||||||
$ docker run -it ubuntu bash
|
|
||||||
|
|
||||||
For more examples and ideas, visit:
|
3. Verify your setup succeeded by running the `hello-world` container.
|
||||||
http://docs.docker.com/userguide/
|
|
||||||
|
$ docker run hello-world
|
||||||
|
Unable to find image 'hello-world:latest' locally
|
||||||
|
511136ea3c5a: Pull complete
|
||||||
|
31cbccb51277: Pull complete
|
||||||
|
e45a5af57b00: Pull complete
|
||||||
|
hello-world:latest: The image you are pulling has been verified.
|
||||||
|
Important: image verification is a tech preview feature and should not be
|
||||||
|
relied on to provide security.
|
||||||
|
Status: Downloaded newer image for hello-world:latest
|
||||||
|
Hello from Docker.
|
||||||
|
This message shows that your installation appears to be working correctly.
|
||||||
|
|
||||||
|
To generate this message, Docker took the following steps:
|
||||||
|
1. The Docker client contacted the Docker daemon.
|
||||||
|
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
|
||||||
|
(Assuming it was not already locally available.)
|
||||||
|
3. The Docker daemon created a new container from that image which runs the
|
||||||
|
executable that produces the output you are currently reading.
|
||||||
|
4. The Docker daemon streamed that output to the Docker client, which sent it
|
||||||
|
to your terminal.
|
||||||
|
|
||||||
|
To try something more ambitious, you can run an Ubuntu container with:
|
||||||
|
$ docker run -it ubuntu bash
|
||||||
|
|
||||||
|
For more examples and ideas, visit:
|
||||||
|
http://docs.docker.com/userguide/
|
||||||
|
|
||||||
|
|
||||||
A more typical way to start and stop `boot2docker` is using the command line.
|
A more typical way to interact with the Docker tools is from your regular shell command line.
|
||||||
|
|
||||||
### From your command line
|
### From your shell
|
||||||
|
|
||||||
Initialize and run `boot2docker` from the command line, do the following:
|
This section assumes you are running a Bash shell. You may be running a
|
||||||
|
different shell such as C Shell but the commands are the same.
|
||||||
|
|
||||||
1. Create a new Boot2Docker VM.
|
1. Create a new Docker VM.
|
||||||
|
|
||||||
$ boot2docker init
|
$ docker-machine create --driver virtualbox default
|
||||||
|
Creating VirtualBox VM...
|
||||||
|
Creating SSH key...
|
||||||
|
Starting VirtualBox VM...
|
||||||
|
Starting VM...
|
||||||
|
To see how to connect Docker to this machine, run: docker-machine env default
|
||||||
|
|
||||||
This creates a new virtual machine. You only need to run this command once.
|
This creates a new `default` in VirtualBox.
|
||||||
|
|
||||||
2. Start the `boot2docker` VM.
|

|
||||||
|
|
||||||
$ boot2docker start
|
The command also creates a machine configuration in the
|
||||||
|
`~/.docker/machine/machines/default` directory. You only need to run the
|
||||||
|
`create` command once. Then, you can use `docker-machine` to start, stop,
|
||||||
|
query, and otherwise manage the VM from the command line.
|
||||||
|
|
||||||
3. Display the environment variables for the Docker client.
|
2. List your available machines.
|
||||||
|
|
||||||
$ boot2docker shellinit
|
$ docker-machine ls
|
||||||
Writing /Users/mary/.boot2docker/certs/boot2docker-vm/ca.pem
|
NAME ACTIVE DRIVER STATE URL SWARM
|
||||||
Writing /Users/mary/.boot2docker/certs/boot2docker-vm/cert.pem
|
default * virtualbox Running tcp://192.168.99.101:2376
|
||||||
Writing /Users/mary/.boot2docker/certs/boot2docker-vm/key.pem
|
|
||||||
export DOCKER_HOST=tcp://192.168.59.103:2376
|
|
||||||
export DOCKER_CERT_PATH=/Users/mary/.boot2docker/certs/boot2docker-vm
|
|
||||||
export DOCKER_TLS_VERIFY=1
|
|
||||||
|
|
||||||
The specific paths and address on your machine will be different.
|
If you have previously installed the deprecated Boot2Docker application or
|
||||||
|
run the Docker Quickstart Terminal, you may have a `dev` VM as well. When you
|
||||||
|
created `default`, the `docker-machine` command provided instructions
|
||||||
|
for learning how to connect the VM.
|
||||||
|
|
||||||
4. To set the environment variables in your shell do the following:
|
3. Get the environment commands for your new VM.
|
||||||
|
|
||||||
$ eval "$(boot2docker shellinit)"
|
$ docker-machine env default
|
||||||
|
export DOCKER_TLS_VERIFY="1"
|
||||||
|
export DOCKER_HOST="tcp://192.168.99.101:2376"
|
||||||
|
export DOCKER_CERT_PATH="/Users/mary/.docker/machine/machines/default"
|
||||||
|
export DOCKER_MACHINE_NAME="default"
|
||||||
|
# Run this command to configure your shell:
|
||||||
|
# eval "$(docker-machine env default)"
|
||||||
|
|
||||||
You can also set them manually by using the `export` commands `boot2docker`
|
4. Connect your shell to the `default` machine.
|
||||||
returns.
|
|
||||||
|
$ eval "$(docker-machine env default)"
|
||||||
|
|
||||||
5. Run the `hello-world` container to verify your setup.
|
5. Run the `hello-world` container to verify your setup.
|
||||||
|
|
||||||
$ docker run hello-world
|
$ docker run hello-world
|
||||||
|
|
||||||
|
|
||||||
## Basic Boot2Docker exercises
|
## Learn about your Toolbox installation
|
||||||
|
|
||||||
At this point, you should have `boot2docker` running and the `docker` client
|
Toolbox installs the Docker Engine binary, the Docker binary on your system. When you
|
||||||
environment initialized. To verify this, run the following commands:
|
use the Docker Quickstart Terminal or create a `default` manually, Docker
|
||||||
|
Machine updates the `~/.docker/machine/machines/default` folder to your
|
||||||
|
system. This folder contains the configuration for the VM.
|
||||||
|
|
||||||
$ boot2docker status
|
You can create multiple VMs on your system with Docker Machine. So, you may have
|
||||||
$ docker version
|
more than one VM folder if you have more than one VM. To remove a VM, use the
|
||||||
|
`docker-machine rm <machine-name>` command.
|
||||||
|
|
||||||
Work through this section to try some practical container tasks using `boot2docker` VM.
|
## Migrate from Boot2Docker
|
||||||
|
|
||||||
|
If you were using Boot2Docker previously, you have a pre-existing Docker
|
||||||
|
`boot2docker-vm` VM on your local system. To allow Docker Machine to manage
|
||||||
|
this older VM, you can migrate it.
|
||||||
|
|
||||||
|
1. Open a terminal or the Docker CLI on your system.
|
||||||
|
|
||||||
|
2. Type the following command.
|
||||||
|
|
||||||
|
$ docker-machine create -d virtualbox --virtualbox-import-boot2docker-vm boot2docker-vm docker-vm
|
||||||
|
|
||||||
|
3. Use the `docker-machine` command to interact with the migrated VM.
|
||||||
|
|
||||||
|
The `docker-machine` subcommands are slightly different than the `boot2docker`
|
||||||
|
subcommands. The table below lists the equivalent `docker-machine` subcommand
|
||||||
|
and what it does:
|
||||||
|
|
||||||
|
| `boot2docker` | `docker-machine` | `docker-machine` description |
|
||||||
|
|----------------|------------------|----------------------------------------------------------|
|
||||||
|
| init | create | Creates a new docker host. |
|
||||||
|
| up | start | Starts a stopped machine. |
|
||||||
|
| ssh | ssh | Runs a command or interactive ssh session on the machine.|
|
||||||
|
| save | - | Not applicable. |
|
||||||
|
| down | stop | Stops a running machine. |
|
||||||
|
| poweroff | stop | Stops a running machine. |
|
||||||
|
| reset | restart | Restarts a running machine. |
|
||||||
|
| config | inspect | Prints machine configuration details. |
|
||||||
|
| status | ls | Lists all machines and their status. |
|
||||||
|
| info | inspect | Displays a machine's details. |
|
||||||
|
| ip | ip | Displays the machine's ip address. |
|
||||||
|
| shellinit | env | Displays shell commands needed to configure your shell to interact with a machine |
|
||||||
|
| delete | rm | Removes a machine. |
|
||||||
|
| download | - | Not applicable. |
|
||||||
|
| upgrade | upgrade | Upgrades a machine's Docker client to the latest stable release. |
|
||||||
|
|
||||||
|
|
||||||
|
## Example of Docker on Mac OS X
|
||||||
|
|
||||||
|
Work through this section to try some practical container tasks on a VM. At this
|
||||||
|
point, you should have a VM running and be connected to it through your shell.
|
||||||
|
To verify this, run the following commands:
|
||||||
|
|
||||||
|
$ docker-machine ls
|
||||||
|
NAME ACTIVE DRIVER STATE URL SWARM
|
||||||
|
dev * virtualbox Running tcp://192.168.99.100:2376
|
||||||
|
|
||||||
|
The `ACTIVE` machine, in this case `dev`, is the one your environment is pointing to.
|
||||||
|
|
||||||
### Access container ports
|
### Access container ports
|
||||||
|
|
||||||
@@ -212,11 +317,11 @@ Work through this section to try some practical container tasks using `boot2dock
|
|||||||
|
|
||||||
This didn't work. The reason it doesn't work is your `DOCKER_HOST` address is
|
This didn't work. The reason it doesn't work is your `DOCKER_HOST` address is
|
||||||
not the localhost address (0.0.0.0) but is instead the address of the
|
not the localhost address (0.0.0.0) but is instead the address of the
|
||||||
`boot2docker` VM.
|
your Docker VM.
|
||||||
|
|
||||||
5. Get the address of the `boot2docker` VM.
|
5. Get the address of the `dev` VM.
|
||||||
|
|
||||||
$ boot2docker ip
|
$ docker-machine ip dev
|
||||||
192.168.59.103
|
192.168.59.103
|
||||||
|
|
||||||
6. Enter the `http://192.168.59.103:49157` address in your browser:
|
6. Enter the `http://192.168.59.103:49157` address in your browser:
|
||||||
@@ -232,7 +337,7 @@ Work through this section to try some practical container tasks using `boot2dock
|
|||||||
|
|
||||||
### Mount a volume on the container
|
### Mount a volume on the container
|
||||||
|
|
||||||
When you start `boot2docker`, it automatically shares your `/Users` directory
|
When you start a container it automatically shares your `/Users/username` directory
|
||||||
with the VM. You can use this share point to mount directories onto your container.
|
with the VM. You can use this share point to mount directories onto your container.
|
||||||
The next exercise demonstrates how to do this.
|
The next exercise demonstrates how to do this.
|
||||||
|
|
||||||
@@ -254,7 +359,8 @@ The next exercise demonstrates how to do this.
|
|||||||
|
|
||||||
5. Start a new `nginx` container and replace the `html` folder with your `site` directory.
|
5. Start a new `nginx` container and replace the `html` folder with your `site` directory.
|
||||||
|
|
||||||
$ docker run -d -P -v $HOME/site:/usr/share/nginx/html --name mysite nginx
|
$ docker run -d -P -v $HOME/site:/usr/share/nginx/html \
|
||||||
|
--name mysite nginx
|
||||||
|
|
||||||
6. Get the `mysite` container's port.
|
6. Get the `mysite` container's port.
|
||||||
|
|
||||||
@@ -274,85 +380,53 @@ The next exercise demonstrates how to do this.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
9. Stop and then remove your running `mysite` container.
|
10. Stop and then remove your running `mysite` container.
|
||||||
|
|
||||||
$ docker stop mysite
|
$ docker stop mysite
|
||||||
$ docker rm mysite
|
$ docker rm mysite
|
||||||
|
|
||||||
## Upgrade Boot2Docker
|
|
||||||
|
|
||||||
If you running Boot2Docker 1.4.1 or greater, you can upgrade Boot2Docker from
|
## Upgrade Docker Toolbox
|
||||||
the command line. If you are running an older version, you should use the
|
|
||||||
package provided by the `boot2docker` repository.
|
|
||||||
|
|
||||||
### From the command line
|
To upgrade Docker Toolbox, download an re-run [the Docker Toolbox
|
||||||
|
installer](https://docker.com/toolbox/).
|
||||||
To upgrade from 1.4.1 or greater, you can do this:
|
|
||||||
|
|
||||||
1. Open a terminal on your local machine.
|
|
||||||
|
|
||||||
2. Stop the `boot2docker` application.
|
|
||||||
|
|
||||||
$ boot2docker stop
|
|
||||||
|
|
||||||
3. Run the upgrade command.
|
|
||||||
|
|
||||||
$ boot2docker upgrade
|
|
||||||
|
|
||||||
|
|
||||||
### Use the installer
|
## Uninstall Docker Toolbox
|
||||||
|
|
||||||
To upgrade any version of Boot2Docker, do this:
|
To uninstall, do the following:
|
||||||
|
|
||||||
1. Open a terminal on your local machine.
|
1. List your machines.
|
||||||
|
|
||||||
2. Stop the `boot2docker` application.
|
$ docker-machine ls
|
||||||
|
NAME ACTIVE DRIVER STATE URL SWARM
|
||||||
|
dev * virtualbox Running tcp://192.168.99.100:2376
|
||||||
|
my-docker-machine virtualbox Stopped
|
||||||
|
default virtualbox Stopped
|
||||||
|
|
||||||
$ boot2docker stop
|
2. Remove each machine.
|
||||||
|
|
||||||
3. Go to the [boot2docker/osx-installer ](
|
$ docker-machine rm dev
|
||||||
https://github.com/boot2docker/osx-installer/releases/latest) release page.
|
Successfully removed dev
|
||||||
|
|
||||||
4. Download Boot2Docker by clicking `Boot2Docker-x.x.x.pkg` in the "Downloads"
|
Removing a machine deletes its VM from VirtualBox and from the
|
||||||
section.
|
`~/.docker/machine/machines` directory.
|
||||||
|
|
||||||
2. Install Boot2Docker by double-clicking the package.
|
3. Remove the Docker Quickstart Terminal and Kitematic from your "Applications" folder.
|
||||||
|
|
||||||
The installer places Boot2Docker in your "Applications" folder.
|
4. Remove the `docker`, `docker-compose`, and `docker-machine` commands from the `/usr/local/bin` folder.
|
||||||
|
|
||||||
|
$ rm /usr/local/bin/docker
|
||||||
|
|
||||||
|
5. Delete the `~/.docker` folder from your system.
|
||||||
|
|
||||||
|
|
||||||
## Uninstallation
|
## Learning more
|
||||||
|
|
||||||
1. Go to the [boot2docker/osx-installer ](
|
Use `docker-machine help` to list the full command line reference for Docker Machine. For more
|
||||||
https://github.com/boot2docker/osx-installer/releases/latest) release page.
|
information about using SSH or SCP to access a VM, see [the Docker Machine
|
||||||
|
documentation](https://docs.docker.com/machine/).
|
||||||
|
|
||||||
2. Download the source code by clicking `Source code (zip)` or
|
You can continue with the [Docker User Guide](/userguide). If you are
|
||||||
`Source code (tar.gz)` in the "Downloads" section.
|
interested in using the Kitematic GUI, see the [Kitermatic user
|
||||||
|
guide](/kitematic/userguide/).
|
||||||
3. Extract the source code.
|
|
||||||
|
|
||||||
4. Open a terminal on your local machine.
|
|
||||||
|
|
||||||
5. Change to the directory where you extracted the source code:
|
|
||||||
|
|
||||||
$ cd <path to extracted source code>
|
|
||||||
|
|
||||||
6. Make sure the uninstall.sh script is executable:
|
|
||||||
|
|
||||||
$ chmod +x uninstall.sh
|
|
||||||
|
|
||||||
7. Run the uninstall.sh script:
|
|
||||||
|
|
||||||
$ ./uninstall.sh
|
|
||||||
|
|
||||||
|
|
||||||
## Learning more and acknowledgement
|
|
||||||
|
|
||||||
Use `boot2docker help` to list the full command line reference. For more
|
|
||||||
information about using SSH or SCP to access the Boot2Docker VM, see the README
|
|
||||||
at [Boot2Docker repository](https://github.com/boot2docker/boot2docker).
|
|
||||||
|
|
||||||
Thanks to Chris Jones whose [blog](http://viget.com/extend/how-to-use-docker-on-os-x-the-missing-guide)
|
|
||||||
inspired me to redo this page.
|
|
||||||
|
|
||||||
Continue with the [Docker User Guide](/userguide).
|
|
||||||
|
|||||||
@@ -111,18 +111,18 @@ install Docker using the following:
|
|||||||
|
|
||||||
1. Log into your Ubuntu installation as a user with `sudo` privileges.
|
1. Log into your Ubuntu installation as a user with `sudo` privileges.
|
||||||
|
|
||||||
2. Verify that you have `wget` installed.
|
2. Verify that you have `curl` installed.
|
||||||
|
|
||||||
$ which wget
|
$ which curl
|
||||||
|
|
||||||
If `wget` isn't installed, install it after updating your manager:
|
If `curl` isn't installed, install it after updating your manager:
|
||||||
|
|
||||||
$ sudo apt-get update
|
$ sudo apt-get update
|
||||||
$ sudo apt-get install wget
|
$ sudo apt-get install curl
|
||||||
|
|
||||||
3. Get the latest Docker package.
|
3. Get the latest Docker package.
|
||||||
|
|
||||||
$ wget -qO- https://get.docker.com/ | sh
|
$ curl -sSL https://get.docker.com/ | sh
|
||||||
|
|
||||||
The system prompts you for your `sudo` password. Then, it downloads and
|
The system prompts you for your `sudo` password. Then, it downloads and
|
||||||
installs Docker and its dependencies.
|
installs Docker and its dependencies.
|
||||||
@@ -132,7 +132,7 @@ install Docker using the following:
|
|||||||
>command fails for the Docker repo during installation. To work around this,
|
>command fails for the Docker repo during installation. To work around this,
|
||||||
>add the key directly using the following:
|
>add the key directly using the following:
|
||||||
>
|
>
|
||||||
> $ wget -qO- https://get.docker.com/gpg | sudo apt-key add -
|
> $ curl -sSL https://get.docker.com/gpg | sudo apt-key add -
|
||||||
|
|
||||||
4. Verify `docker` is installed correctly.
|
4. Verify `docker` is installed correctly.
|
||||||
|
|
||||||
@@ -197,9 +197,14 @@ When users run Docker, they may see these messages when working with an image:
|
|||||||
WARNING: Your kernel does not support cgroup swap limit. WARNING: Your
|
WARNING: Your kernel does not support cgroup swap limit. WARNING: Your
|
||||||
kernel does not support swap limit capabilities. Limitation discarded.
|
kernel does not support swap limit capabilities. Limitation discarded.
|
||||||
|
|
||||||
To prevent these messages, enable memory and swap accounting on your system. To
|
To prevent these messages, enable memory and swap accounting on your
|
||||||
enable these on system using GNU GRUB (GNU GRand Unified Bootloader), do the
|
system. Enabling memory and swap accounting does induce both a memory
|
||||||
following.
|
overhead and a performance degradation even when Docker is not in
|
||||||
|
use. The memory overhead is about 1% of the total available
|
||||||
|
memory. The performance degradation is roughly 10%.
|
||||||
|
|
||||||
|
To enable memory and swap on system using GNU GRUB (GNU GRand Unified
|
||||||
|
Bootloader), do the following:
|
||||||
|
|
||||||
1. Log into Ubuntu as a user with `sudo` privileges.
|
1. Log into Ubuntu as a user with `sudo` privileges.
|
||||||
|
|
||||||
@@ -339,9 +344,9 @@ to start the docker daemon on boot
|
|||||||
|
|
||||||
## Upgrade Docker
|
## Upgrade Docker
|
||||||
|
|
||||||
To install the latest version of Docker with `wget`:
|
To install the latest version of Docker with `curl`:
|
||||||
|
|
||||||
$ wget -qO- https://get.docker.com/ | sh
|
$ curl -sSL https://get.docker.com/ | sh
|
||||||
|
|
||||||
## Uninstallation
|
## Uninstallation
|
||||||
|
|
||||||
|
|||||||
+296
-104
@@ -9,165 +9,357 @@ parent = "smn_engine"
|
|||||||
<![end-metadata]-->
|
<![end-metadata]-->
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
> **Note:**
|
|
||||||
> Docker has been tested on Windows 7 and 8.1; it may also run on older versions.
|
|
||||||
> Your processor needs to support hardware virtualization.
|
|
||||||
|
|
||||||
The Docker Engine uses Linux-specific kernel features, so to run it on Windows
|
> **Note**: This release of Docker deprecates the Boot2Docker command line in
|
||||||
we need to use a lightweight virtual machine (VM). You use the **Windows Docker
|
> favor of Docker Machine. Use the Docker Toolbox to install Docker Machine as
|
||||||
Client** to control the virtualized Docker Engine to build, run, and manage
|
> well as the other Docker tools.
|
||||||
Docker containers.
|
|
||||||
|
|
||||||
To make this process easier, we've designed a helper application called
|
You install Docker using Docker Toolbox. Docker Toolbox includes the following Docker tools:
|
||||||
[Boot2Docker](https://github.com/boot2docker/boot2docker) which creates a Linux virtual
|
|
||||||
machine on Windows to run Docker on a Linux operating system.
|
|
||||||
|
|
||||||
Although you will be using Windows Docker client, the docker engine hosting the
|
* Docker Machine for running the `docker-machine` binary
|
||||||
containers will still be running on Linux. Until the Docker engine for Windows
|
* Docker Engine for running the `docker` binary
|
||||||
is developed, you can launch only Linux containers from your Windows machine.
|
* Kitematic, the Docker GUI
|
||||||
|
* a shell preconfigured for a Docker command-line environment
|
||||||
|
* Oracle VM VirtualBox
|
||||||
|
|
||||||
|
Because the Docker daemon uses Linux-specific kernel features, you can't run
|
||||||
|
Docker natively in Windows. Instead, you must use `docker-machine` to create and attach to a Docker VM on your machine. This VM hosts Docker for you on your Windows system.
|
||||||
|
|
||||||
|
The Docker VM is lightweight Linux virtual machine made specifically to run the
|
||||||
|
Docker daemon on Windows. The VirtualBox VM runs completely from RAM, is a
|
||||||
|
small ~24MB download, and boots in approximately 5s.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Your machine must be running Windows 7.1, 8/8.1 or newer to run Docker. Windows 10 is not currently supported. To find out what version of Windows you have:
|
||||||
|
|
||||||
|
1. Right click the Windows message and choose **System**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you aren't using a supported version, you could consider upgrading your
|
||||||
|
operating system.
|
||||||
|
|
||||||
|
2. Make sure your Windows system supports Hardware Virtualization Technology and that virtualization is enabled.
|
||||||
|
|
||||||
|
#### For Windows 8 or 8.1
|
||||||
|
|
||||||
|
Choose **Start > Task Manager** and navigate to the **Performance** tab.
|
||||||
|
Under **CPU** you should see the following:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If virtualization is not enabled on your system, follow the manufacturer's instructions for enabling it.
|
||||||
|
|
||||||
|
### For Windows 7
|
||||||
|
|
||||||
|
Run the <a
|
||||||
|
href="http://www.microsoft.com/en-us/download/details.aspx?id=592"
|
||||||
|
target="_blank"> Microsoft® Hardware-Assisted Virtualization Detection
|
||||||
|
Tool</a> and follow the on-screen instructions.
|
||||||
|
|
||||||
|
|
||||||
|
> **Note**: If you have Docker hosts running and you don't wish to do a Docker Toolbox
|
||||||
|
installation, you can install the `docker.exe` using the *unofficial* Windows package
|
||||||
|
manager Chocolately. For information on how to do this, see [Docker package on
|
||||||
|
Chocolatey](http://chocolatey.org/packages/docker).
|
||||||
|
|
||||||
|
### Learn the key concepts before installing
|
||||||
|
|
||||||
|
In a Docker installation on Linux, your machine is both the localhost and the
|
||||||
|
Docker host. In networking, localhost means your computer. The Docker host is
|
||||||
|
the machine on which the containers run.
|
||||||
|
|
||||||
|
On a typical Linux installation, the Docker client, the Docker daemon, and any
|
||||||
|
containers run directly on your localhost. This means you can address ports on a
|
||||||
|
Docker container using standard localhost addressing such as `localhost:8000` or
|
||||||
|
`0.0.0.0:8376`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In an Windows installation, the `docker` daemon is running inside a Linux virtual
|
||||||
|
machine. You use the Windows Docker client to talk to the Docker host VM. Your
|
||||||
|
Docker containers run inside this host.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Demonstration
|
In Windows, the Docker host address is the address of the Linux VM. When you
|
||||||
|
start the VM with `docker-machine` it is assigned an IP address. When you start
|
||||||
|
a container, the ports on a container map to ports on the VM. To see this in
|
||||||
|
practice, work through the exercises on this page.
|
||||||
|
|
||||||
<iframe width="640" height="480" src="//www.youtube.com/embed/TjMU3bDX4vo?rel=0" frameborder="0" allowfullscreen></iframe>
|
|
||||||
|
|
||||||
## Installation
|
### Installation
|
||||||
|
|
||||||
1. Download the latest release of the
|
If you have VirtualBox running, you must shut it down before running the
|
||||||
[Docker for Windows Installer](https://github.com/boot2docker/windows-installer/releases/latest).
|
installer.
|
||||||
2. Run the installer, which will install Docker Client for Windows, VirtualBox,
|
|
||||||
Git for Windows (MSYS-git), the boot2docker Linux ISO, and the Boot2Docker
|
|
||||||
management tool.
|
|
||||||

|
|
||||||
3. Run the **Boot2Docker Start** shortcut from your Desktop or “Program Files →
|
|
||||||
Boot2Docker for Windows”.
|
|
||||||
The Start script will ask you to enter an ssh key passphrase - the simplest
|
|
||||||
(but least secure) is to just hit [Enter].
|
|
||||||
|
|
||||||
4. The **Boot2Docker Start** will start a unix shell already configured to manage
|
1. Go to the [Docker Toolbox](https://www.docker.com/toolbox) page.
|
||||||
Docker running inside the virtual machine. Run `docker version` to see
|
|
||||||
if it is working correctly:
|
|
||||||
|
|
||||||

|
2. Click the installer link to download.
|
||||||
|
|
||||||
## Running Docker
|
3. Install Docker Toolbox by double-clicking the installer.
|
||||||
|
|
||||||
> **Note:** if you are using a remote Docker daemon, such as Boot2Docker,
|
The installer launches the "Setup - Docker Toolbox" dialog.
|
||||||
> then _do not_ type the `sudo` before the `docker` commands shown in the
|
|
||||||
> documentation's examples.
|
|
||||||
|
|
||||||
**Boot2Docker Start** will automatically start a shell with environment variables
|

|
||||||
correctly set so you can start using Docker right away:
|
|
||||||
|
|
||||||
Let's try the `hello-world` example image. Run
|
4. Press "Next" to install the toolbox.
|
||||||
|
|
||||||
$ docker run hello-world
|
The installer presents you with options to customize the standard
|
||||||
|
installation. By default, the standard Docker Toolbox installation:
|
||||||
|
|
||||||
|
* installs executables for the Docker tools in `C:\Program Files\Docker Toolbox`
|
||||||
|
* updates any existing VirtualBox installation
|
||||||
|
* adds a Docker Inc. folder to your program shortcuts
|
||||||
|
* updates your `PATH` environment variable
|
||||||
|
* adds desktop icons for the Docker Quickstart Terminal and Kitematic
|
||||||
|
|
||||||
|
This installation assumes the defaults are acceptable.
|
||||||
|
|
||||||
|
5. Press "Next" until you reach the "Ready to Install" page.
|
||||||
|
|
||||||
|
The system prompts you for your password.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. Press "Install" to continue with the installation.
|
||||||
|
|
||||||
|
When it completes, the installer provides you with some information you can
|
||||||
|
use to complete some common tasks.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
7. Press "Close" to exit.
|
||||||
|
|
||||||
|
## Running a Docker Container
|
||||||
|
|
||||||
|
To run a Docker container, you:
|
||||||
|
|
||||||
|
* create a new (or start an existing) Docker virtual machine
|
||||||
|
* switch your environment to your new VM
|
||||||
|
* use the `docker` client to create, load, and manage containers
|
||||||
|
|
||||||
|
Once you create a machine, you can reuse it as often as you like. Like any
|
||||||
|
VirtualBox VM, it maintains its configuration between uses.
|
||||||
|
|
||||||
|
There are several ways to use the installed tools, from the Docker Quickstart Terminal or
|
||||||
|
[from your shell](#from-your-shell).
|
||||||
|
|
||||||
|
### From the Docker Quickstart Terminal
|
||||||
|
|
||||||
|
1. Find the Docker Quickstart Terminal icon on your Desktop and double-click to launch it.
|
||||||
|
|
||||||
|
The application:
|
||||||
|
|
||||||
|
* opens a terminal window
|
||||||
|
* creates a `default` if it doesn't exist, starts the VM if it does
|
||||||
|
* points the terminal environment to this VM
|
||||||
|
|
||||||
|
Once the launch completes, you can run `docker` commands.
|
||||||
|
|
||||||
|
3. Verify your setup succeeded by running the `hello-world` container.
|
||||||
|
|
||||||
|
$ docker run hello-world
|
||||||
|
Unable to find image 'hello-world:latest' locally
|
||||||
|
511136ea3c5a: Pull complete
|
||||||
|
31cbccb51277: Pull complete
|
||||||
|
e45a5af57b00: Pull complete
|
||||||
|
hello-world:latest: The image you are pulling has been verified.
|
||||||
|
Important: image verification is a tech preview feature and should not be
|
||||||
|
relied on to provide security.
|
||||||
|
Status: Downloaded newer image for hello-world:latest
|
||||||
|
Hello from Docker.
|
||||||
|
This message shows that your installation appears to be working correctly.
|
||||||
|
|
||||||
|
To generate this message, Docker took the following steps:
|
||||||
|
1. The Docker client contacted the Docker daemon.
|
||||||
|
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
|
||||||
|
(Assuming it was not already locally available.)
|
||||||
|
3. The Docker daemon created a new container from that image which runs the
|
||||||
|
executable that produces the output you are currently reading.
|
||||||
|
4. The Docker daemon streamed that output to the Docker client, which sent it
|
||||||
|
to your terminal.
|
||||||
|
|
||||||
|
To try something more ambitious, you can run an Ubuntu container with:
|
||||||
|
$ docker run -it ubuntu bash
|
||||||
|
|
||||||
|
For more examples and ideas, visit:
|
||||||
|
http://docs.docker.com/userguide/
|
||||||
|
|
||||||
This should download the very small `hello-world` image and print a
|
|
||||||
`Hello from Docker.` message.
|
|
||||||
|
|
||||||
## Using Docker from Windows Command Line Prompt (cmd.exe)
|
## Using Docker from Windows Command Line Prompt (cmd.exe)
|
||||||
|
|
||||||
Launch a Windows Command Line Prompt (cmd.exe).
|
1. Launch a Windows Command Line Prompt (cmd.exe).
|
||||||
|
|
||||||
Boot2Docker command requires `ssh.exe` to be in the PATH, therefore we need to
|
The `docker-machine` command requires `ssh.exe` in your `PATH` environment
|
||||||
include `bin` folder of the Git installation (which has ssh.exe) to the `%PATH%`
|
variable. This `.exe` is in the MsysGit `bin` folder.
|
||||||
environment variable by running:
|
|
||||||
|
|
||||||
set PATH=%PATH%;"c:\Program Files (x86)\Git\bin"
|
2. Add this to the `%PATH%` environment variable by running:
|
||||||
|
|
||||||
and then we can run the `boot2docker start` command to start the Boot2Docker VM.
|
set PATH=%PATH%;"c:\Program Files (x86)\Git\bin"
|
||||||
(Run `boot2docker init` command if you get an error saying machine does not
|
|
||||||
exist.) Then copy the instructions for cmd.exe to set the environment variables
|
|
||||||
to your console window and you are ready to run docker commands such as
|
|
||||||
`docker ps`:
|
|
||||||
|
|
||||||

|
3. Create a new Docker VM.
|
||||||
|
|
||||||
|
docker-machine create --driver virtualbox my-default
|
||||||
|
Creating VirtualBox VM...
|
||||||
|
Creating SSH key...
|
||||||
|
Starting VirtualBox VM...
|
||||||
|
Starting VM...
|
||||||
|
To see how to connect Docker to this machine, run: docker-machine env my-default
|
||||||
|
|
||||||
|
The command also creates a machine configuration in the
|
||||||
|
`C:\USERS\USERNAME\.docker\machine\machines` directory. You only need to run the `create`
|
||||||
|
command once. Then, you can use `docker-machine` to start, stop, query, and
|
||||||
|
otherwise manage the VM from the command line.
|
||||||
|
|
||||||
|
4. List your available machines.
|
||||||
|
|
||||||
|
C:\Users\mary> docker-machine ls
|
||||||
|
NAME ACTIVE DRIVER STATE URL SWARM
|
||||||
|
my-default * virtualbox Running tcp://192.168.99.101:2376
|
||||||
|
|
||||||
|
If you have previously installed the deprecated Boot2Docker application or
|
||||||
|
run the Docker Quickstart Terminal, you may have a `dev` VM as well.
|
||||||
|
|
||||||
|
5. Get the environment commands for your new VM.
|
||||||
|
|
||||||
|
C:\Users\mary> docker-machine env --shell cmd my-default
|
||||||
|
|
||||||
|
6. Connect your shell to the `my-default` machine.
|
||||||
|
|
||||||
|
C:\Users\mary> eval "$(docker-machine env my-default)"
|
||||||
|
|
||||||
|
7. Run the `hello-world` container to verify your setup.
|
||||||
|
|
||||||
|
C:\Users\mary> docker run hello-world
|
||||||
|
|
||||||
## Using Docker from PowerShell
|
## Using Docker from PowerShell
|
||||||
|
|
||||||
Launch a PowerShell window, then add `ssh.exe` to your PATH:
|
1. Launch a Windows PowerShell window.
|
||||||
|
|
||||||
$Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin"
|
2. Add `ssh.exe` to your PATH:
|
||||||
|
|
||||||
and after running the `boot2docker start` command it will print PowerShell
|
PS C:\Users\mary> $Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin"
|
||||||
commands to set the environment variables to connect to the Docker daemon
|
|
||||||
running inside the VM. Run these commands and you are ready to run docker
|
|
||||||
commands such as `docker ps`:
|
|
||||||
|
|
||||||

|
3. Create a new Docker VM.
|
||||||
|
|
||||||
> NOTE: You can alternatively run `boot2docker shellinit | Invoke-Expression`
|
PS C:\Users\mary> docker-machine create --driver virtualbox my-default
|
||||||
> command to set the environment variables instead of copying and pasting on
|
|
||||||
> PowerShell.
|
|
||||||
|
|
||||||
# Further Details
|
4. List your available machines.
|
||||||
|
|
||||||
The Boot2Docker management tool provides several commands:
|
C:\Users\mary> docker-machine ls
|
||||||
|
NAME ACTIVE DRIVER STATE URL SWARM
|
||||||
|
my-default * virtualbox Running tcp://192.168.99.101:2376
|
||||||
|
|
||||||
$ boot2docker
|
5. Get the environment commands for your new VM.
|
||||||
Usage: boot2docker.exe [<options>] {help|init|up|ssh|save|down|poweroff|reset|restart|config|status|info|ip|shellinit|delete|download|upgrade|version} [<args>]
|
|
||||||
|
|
||||||
## Upgrading
|
C:\Users\mary> docker-machine env --shell powershell my-default
|
||||||
|
|
||||||
1. Download the latest release of the [Docker for Windows Installer](
|
6. Connect your shell to the `my-default` machine.
|
||||||
https://github.com/boot2docker/windows-installer/releases/latest)
|
|
||||||
|
|
||||||
2. Run the installer, which will update the Boot2Docker management tool.
|
C:\Users\mary> eval "$(docker-machine env my-default)"
|
||||||
|
|
||||||
3. To upgrade your existing virtual machine, open a terminal and run:
|
7. Run the `hello-world` container to verify your setup.
|
||||||
|
|
||||||
boot2docker stop
|
C:\Users\mary> docker run hello-world
|
||||||
boot2docker download
|
|
||||||
boot2docker start
|
|
||||||
|
## Learn about your Toolbox installation
|
||||||
|
|
||||||
|
Toolbox installs the Docker Engine binary in the `C:\Program Files\Docker
|
||||||
|
Toolbox` directory. When you use the Docker Quickstart Terminal or create a
|
||||||
|
`default` manually, Docker Machine updates the
|
||||||
|
`C:\USERS\USERNAME\.docker\machine\machines\default` folder to your
|
||||||
|
system. This folder contains the configuration for the VM.
|
||||||
|
|
||||||
|
You can create multiple VMs on your system with Docker Machine. So, you may have
|
||||||
|
more than one VM folder if you have more than one VM. To remove a VM, use the
|
||||||
|
`docker-machine rm <machine-name>` command.
|
||||||
|
|
||||||
|
## Migrate from Boot2Docker
|
||||||
|
|
||||||
|
If you were using Boot2Docker previously, you have a pre-existing Docker
|
||||||
|
`boot2docker-vm` VM on your local system. To allow Docker Machine to manage
|
||||||
|
this older VM, you can migrate it.
|
||||||
|
|
||||||
|
1. Open a terminal or the Docker CLI on your system.
|
||||||
|
|
||||||
|
2. Type the following command.
|
||||||
|
|
||||||
|
$ docker-machine create -d virtualbox --virtualbox-import-boot2docker-vm boot2docker-vm docker-vm
|
||||||
|
|
||||||
|
3. Use the `docker-machine` command to interact with the migrated VM.
|
||||||
|
|
||||||
|
The `docker-machine` subcommands are slightly different than the `boot2docker`
|
||||||
|
subcommands. The table below lists the equivalent `docker-machine` subcommand
|
||||||
|
and what it does:
|
||||||
|
|
||||||
|
| `boot2docker` | `docker-machine` | `docker-machine` description |
|
||||||
|
|----------------|------------------|----------------------------------------------------------|
|
||||||
|
| init | create | Creates a new docker host. |
|
||||||
|
| up | start | Starts a stopped machine. |
|
||||||
|
| ssh | ssh | Runs a command or interactive ssh session on the machine.|
|
||||||
|
| save | - | Not applicable. |
|
||||||
|
| down | stop | Stops a running machine. |
|
||||||
|
| poweroff | stop | Stops a running machine. |
|
||||||
|
| reset | restart | Restarts a running machine. |
|
||||||
|
| config | inspect | Prints machine configuration details. |
|
||||||
|
| status | ls | Lists all machines and their status. |
|
||||||
|
| info | inspect | Displays a machine's details. |
|
||||||
|
| ip | ip | Displays the machine's ip address. |
|
||||||
|
| shellinit | env | Displays shell commands needed to configure your shell to interact with a machine |
|
||||||
|
| delete | rm | Removes a machine. |
|
||||||
|
| download | - | Not applicable. |
|
||||||
|
| upgrade | upgrade | Upgrades a machine's Docker client to the latest stable release. |
|
||||||
|
|
||||||
|
|
||||||
|
## Upgrade Docker Toolbox
|
||||||
|
|
||||||
|
To upgrade Docker Toolbox, download an re-run [the Docker Toolbox
|
||||||
|
installer](https://www.docker.com/toolbox).
|
||||||
|
|
||||||
## Container port redirection
|
## Container port redirection
|
||||||
|
|
||||||
If you are curious, the username for the boot2docker default user is `docker`
|
If you are curious, the username for the Docker default user is `docker` and the
|
||||||
and the password is `tcuser`.
|
password is `tcuser`. The latest version of `docker-machine` sets up a host only
|
||||||
|
network adaptor which provides access to the container's ports.
|
||||||
|
|
||||||
The latest version of `boot2docker` sets up a host only network adaptor which
|
If you run a container with a published port:
|
||||||
provides access to the container's ports.
|
|
||||||
|
|
||||||
If you run a container with an exposed port:
|
$ docker run --rm -i -t -p 80:80 nginx
|
||||||
|
|
||||||
docker run --rm -i -t -p 80:80 nginx
|
Then you should be able to access that nginx server using the IP address
|
||||||
|
reported to you using:
|
||||||
|
|
||||||
Then you should be able to access that nginx server using the IP address reported
|
$ docker-machine ip
|
||||||
to you using:
|
|
||||||
|
|
||||||
boot2docker ip
|
Typically, the IP is 192.168.59.103, but it could get changed by VirtualBox's
|
||||||
|
DHCP implementation.
|
||||||
Typically, it is 192.168.59.103, but it could get changed by VirtualBox's DHCP
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
For further information or to report issues, please see the [Boot2Docker site](http://boot2docker.io)
|
|
||||||
|
|
||||||
## Login with PUTTY instead of using the CMD
|
## Login with PUTTY instead of using the CMD
|
||||||
|
|
||||||
Boot2Docker generates and uses the public/private key pair in your `%USERPROFILE%\.ssh`
|
Docker Machine generates and uses the public/private key pair in your
|
||||||
directory so to log in you need to use the private key from this same directory.
|
`%USERPROFILE%\.ssh` directory so to log in you need to use the private key from
|
||||||
|
this same directory. The private key needs to be converted into the format PuTTY
|
||||||
The private key needs to be converted into the format PuTTY uses.
|
uses. You can do this with
|
||||||
|
|
||||||
You can do this with
|
|
||||||
[puttygen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html):
|
[puttygen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html):
|
||||||
|
|
||||||
- Open `puttygen.exe` and load ("File"->"Load" menu) the private key from
|
1. Open `puttygen.exe` and load ("File"->"Load" menu) the private key from
|
||||||
`%USERPROFILE%\.ssh\id_boot2docker`
|
`%USERPROFILE%\.ssh\id_boot2docker`
|
||||||
- then click: "Save Private Key".
|
|
||||||
- Then use the saved file to login with PuTTY using `docker@127.0.0.1:2022`.
|
2. Click "Save Private Key".
|
||||||
|
|
||||||
|
3. Use the saved file to login with PuTTY using `docker@127.0.0.1:2022`.
|
||||||
|
|
||||||
## Uninstallation
|
## Uninstallation
|
||||||
|
|
||||||
You can uninstall Boot2Docker using Window's standard process for removing programs.
|
You can uninstall Docker Toolbox using Window's standard process for removing
|
||||||
This process does not remove the `docker-install.exe` file. You must delete that file
|
programs. This process does not remove the `docker-install.exe` file. You must
|
||||||
yourself.
|
delete that file yourself.
|
||||||
|
|
||||||
## References
|
## Learn more
|
||||||
|
|
||||||
If you have Docker hosts running and if you don't wish to do a
|
You can continue with the [Docker User Guide](/userguide). If you are
|
||||||
Boot2Docker installation, you can install the docker.exe using
|
interested in using the Kitematic GUI, see the [Kitermatic user
|
||||||
unofficial Windows package manager Chocolately. For information
|
guide](/kitematic/userguide/).
|
||||||
on how to do this, see [Docker package on Chocolatey](http://chocolatey.org/packages/docker).
|
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ images, or you can download Docker images that other people have already created
|
|||||||
Docker images are the **build** component of Docker.
|
Docker images are the **build** component of Docker.
|
||||||
|
|
||||||
#### Docker registries
|
#### Docker registries
|
||||||
Docker registries hold images. These are public or private stores from which you upload
|
Docker registries hold images. These are public or private stores from which you
|
||||||
or download images. The public Docker registry is called
|
upload or download images. The public Docker registry is provided with the
|
||||||
[Docker Hub](http://hub.docker.com). It provides a huge collection of existing
|
[Docker Hub](http://hub.docker.com). It serves a huge collection of existing
|
||||||
images for your use. These can be images you create yourself or you
|
images for your use. These can be images you create yourself or you can use
|
||||||
can use images that others have previously created. Docker registries are the
|
images that others have previously created. Docker registries are the
|
||||||
**distribution** component of Docker.
|
**distribution** component of Docker.
|
||||||
|
|
||||||
#### Docker containers
|
#### Docker containers
|
||||||
@@ -179,8 +179,9 @@ returns a final image.
|
|||||||
|
|
||||||
### How does a Docker registry work?
|
### How does a Docker registry work?
|
||||||
The Docker registry is the store for your Docker images. Once you build a Docker
|
The Docker registry is the store for your Docker images. Once you build a Docker
|
||||||
image you can *push* it to a public registry [Docker Hub](https://hub.docker.com) or to
|
image you can *push* it to a public registry such as the one provided by [Docker
|
||||||
your own registry running behind your firewall.
|
Hub](https://hub.docker.com) or to your own registry running behind your
|
||||||
|
firewall.
|
||||||
|
|
||||||
Using the Docker client, you can search for already published images and then
|
Using the Docker client, you can search for already published images and then
|
||||||
pull them down to your Docker host to build containers from them.
|
pull them down to your Docker host to build containers from them.
|
||||||
|
|||||||
+13
-1
@@ -12,6 +12,14 @@ parent = "mn_use_docker"
|
|||||||
|
|
||||||
The following list of features are deprecated.
|
The following list of features are deprecated.
|
||||||
|
|
||||||
|
### LXC built-in exec driver
|
||||||
|
**Deprecated In Release: v1.8**
|
||||||
|
|
||||||
|
**Target For Removal In Release: v1.10**
|
||||||
|
|
||||||
|
The built-in LXC execution driver is deprecated for an external implementation.
|
||||||
|
The lxc-conf flag and API fields will also be removed.
|
||||||
|
|
||||||
### Old Command Line Options
|
### Old Command Line Options
|
||||||
**Deprecated In Release: [v1.8.0](/release-notes/#docker-engine-1-8-0)**
|
**Deprecated In Release: [v1.8.0](/release-notes/#docker-engine-1-8-0)**
|
||||||
|
|
||||||
@@ -21,7 +29,7 @@ The flags `-d` and `--daemon` are deprecated in favor of the `daemon` subcommand
|
|||||||
|
|
||||||
docker daemon -H ...
|
docker daemon -H ...
|
||||||
|
|
||||||
The following single-dash (`-opt`) variant of certain command line options
|
The following single-dash (`-opt`) variant of certain command line options
|
||||||
are deprecated and replaced with double-dash options (`--opt`):
|
are deprecated and replaced with double-dash options (`--opt`):
|
||||||
|
|
||||||
docker attach -nostdin
|
docker attach -nostdin
|
||||||
@@ -60,3 +68,7 @@ The following double-dash options are deprecated and have no replacement:
|
|||||||
docker ps --since-id
|
docker ps --since-id
|
||||||
docker ps --before-id
|
docker ps --before-id
|
||||||
docker search --trusted
|
docker search --trusted
|
||||||
|
|
||||||
|
### Interacting with V1 registries
|
||||||
|
|
||||||
|
Version 1.8.3 adds a flag (`--disable-legacy-registry=false`) which prevents the docker daemon from `pull`, `push`, and `login` operations against v1 registries. Though disabled by default, this signals the intent to deprecate the v1 protocol.
|
||||||
|
|||||||
+2
-2
@@ -33,7 +33,7 @@ Docker currently runs only on Linux, but you can use VirtualBox to run Docker in
|
|||||||
a virtual machine on your box, and get the best of both worlds. Check out the
|
a virtual machine on your box, and get the best of both worlds. Check out the
|
||||||
[*Mac OS X*](../installation/mac/#macosx) and [*Microsoft
|
[*Mac OS X*](../installation/mac/#macosx) and [*Microsoft
|
||||||
Windows*](../installation/windows/#windows) installation guides. The small Linux
|
Windows*](../installation/windows/#windows) installation guides. The small Linux
|
||||||
distribution boot2docker can be run inside virtual machines on these two
|
distribution Docker Machine can be run inside virtual machines on these two
|
||||||
operating systems.
|
operating systems.
|
||||||
|
|
||||||
> **Note:** if you are using a remote Docker daemon, such as Boot2Docker,
|
> **Note:** if you are using a remote Docker daemon, such as Boot2Docker,
|
||||||
@@ -97,7 +97,7 @@ with several powerful functionalities:
|
|||||||
applications. Your ideal Postgresql setup can be re-used for all your future
|
applications. Your ideal Postgresql setup can be re-used for all your future
|
||||||
projects. And so on.
|
projects. And so on.
|
||||||
|
|
||||||
- *Sharing.* Docker has access to a [public registry](https://hub.docker.com)
|
- *Sharing.* Docker has access to a public registry [on Docker Hub](https://registry.hub.docker.com/)
|
||||||
where thousands of people have uploaded useful containers: anything from Redis,
|
where thousands of people have uploaded useful containers: anything from Redis,
|
||||||
CouchDB, Postgres to IRC bouncers to Rails app servers to Hadoop to base images
|
CouchDB, Postgres to IRC bouncers to Rails app servers to Hadoop to base images
|
||||||
for various Linux distros. The
|
for various Linux distros. The
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ program code and documentation code.
|
|||||||
* Run `gofmt -s -w file.go` on each changed file before
|
* Run `gofmt -s -w file.go` on each changed file before
|
||||||
committing your changes. Most editors have plug-ins that do this automatically.
|
committing your changes. Most editors have plug-ins that do this automatically.
|
||||||
|
|
||||||
|
* Run `golint` on each changed file before
|
||||||
|
committing your changes.
|
||||||
|
|
||||||
* Update the documentation when creating or modifying features.
|
* Update the documentation when creating or modifying features.
|
||||||
|
|
||||||
* Commits that fix or close an issue should reference them in the commit message
|
* Commits that fix or close an issue should reference them in the commit message
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ you continue working with your fork on this branch.
|
|||||||
|
|
||||||
## Clean your host of Docker artifacts
|
## Clean your host of Docker artifacts
|
||||||
|
|
||||||
Docker developers run the latest stable release of the Docker software (with Boot2Docker if their machine is Mac OS X). They clean their local
|
Docker developers run the latest stable release of the Docker software (with Docker Machine if their machine is Mac OS X). They clean their local
|
||||||
hosts of unnecessary Docker artifacts such as stopped containers or unused
|
hosts of unnecessary Docker artifacts such as stopped containers or unused
|
||||||
images. Cleaning unnecessary artifacts isn't strictly necessary, but it is
|
images. Cleaning unnecessary artifacts isn't strictly necessary, but it is
|
||||||
good practice, so it is included here.
|
good practice, so it is included here.
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ target="_blank">docker/docker repository</a>.
|
|||||||
|
|
||||||
$ cd ~
|
$ cd ~
|
||||||
|
|
||||||
In Windows, you'll work in your Boot2Docker window instead of Powershell or
|
In Windows, you'll work in your Docker Quickstart Terminal window instead of
|
||||||
a `cmd` window.
|
Powershell or a `cmd` window.
|
||||||
|
|
||||||
6. Create a `repos` directory.
|
6. Create a `repos` directory.
|
||||||
|
|
||||||
|
|||||||
@@ -317,9 +317,9 @@ can browse the docs.
|
|||||||
|
|
||||||
4. Enter the URL in your browser.
|
4. Enter the URL in your browser.
|
||||||
|
|
||||||
If you are running Boot2Docker, replace the default localhost address
|
If you are using Docker Machine, replace the default localhost address
|
||||||
(0.0.0.0) with your DOCKERHOST value. You can get this value at any time by
|
(0.0.0.0) with your DOCKERHOST value. You can get this value at any time by
|
||||||
entering `boot2docker ip` at the command line.
|
entering `docker-machine ip <machine-name>` at the command line.
|
||||||
|
|
||||||
5. Once in the documentation, look for the red notice to verify you are seeing the correct build.
|
5. Once in the documentation, look for the red notice to verify you are seeing the correct build.
|
||||||
|
|
||||||
|
|||||||
@@ -10,502 +10,5 @@ parent = "smn_remoteapi"
|
|||||||
|
|
||||||
# Docker Hub API
|
# Docker Hub API
|
||||||
|
|
||||||
- This is the REST API for [Docker Hub](https://hub.docker.com).
|
This API is deprecated as of 1.7. To view the old version, see the [Docker Hub API](https://docs.docker.com/v1.7/reference/api/docker-io_api/) in the 1.7 documentation.
|
||||||
- Authorization is done with basic auth over SSL
|
|
||||||
- Not all commands require authentication, only those noted as such.
|
|
||||||
|
|
||||||
# Repositories
|
|
||||||
|
|
||||||
## User repository
|
|
||||||
|
|
||||||
### Create a user repository
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(namespace)/(repo_name)/`
|
|
||||||
|
|
||||||
Create a user repository with the given `namespace` and `repo_name`.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foo/bar/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}]
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **namespace** – the namespace for the repo
|
|
||||||
- **repo_name** – the name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
WWW-Authenticate: Token signature=123abc,repository="foo/bar",access=write
|
|
||||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=write
|
|
||||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – Created
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
|
|
||||||
### Delete a user repository
|
|
||||||
|
|
||||||
`DELETE /v1/repositories/(namespace)/(repo_name)/`
|
|
||||||
|
|
||||||
Delete a user repository with the given `namespace` and `repo_name`.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
DELETE /v1/repositories/foo/bar/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **namespace** – the namespace for the repo
|
|
||||||
- **repo_name** – the name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 202
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
WWW-Authenticate: Token signature=123abc,repository="foo/bar",access=delete
|
|
||||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=delete
|
|
||||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – Deleted
|
|
||||||
- **202** – Accepted
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
|
|
||||||
## Library repository
|
|
||||||
|
|
||||||
### Create a library repository
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(repo_name)/`
|
|
||||||
|
|
||||||
Create a library repository with the given `repo_name`.
|
|
||||||
This is a restricted feature only available to docker admins.
|
|
||||||
|
|
||||||
> When namespace is missing, it is assumed to be `library`
|
|
||||||
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foobar/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}]
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **repo_name** – the library name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
WWW-Authenticate: Token signature=123abc,repository="library/foobar",access=write
|
|
||||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=write
|
|
||||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – Created
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
|
|
||||||
### Delete a library repository
|
|
||||||
|
|
||||||
`DELETE /v1/repositories/(repo_name)/`
|
|
||||||
|
|
||||||
Delete a library repository with the given `repo_name`.
|
|
||||||
This is a restricted feature only available to docker admins.
|
|
||||||
|
|
||||||
> When namespace is missing, it is assumed to be `library`
|
|
||||||
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
DELETE /v1/repositories/foobar/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **repo_name** – the library name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 202
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
WWW-Authenticate: Token signature=123abc,repository="library/foobar",access=delete
|
|
||||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=delete
|
|
||||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – Deleted
|
|
||||||
- **202** – Accepted
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
|
|
||||||
# Repository images
|
|
||||||
|
|
||||||
## User repository images
|
|
||||||
|
|
||||||
### Update user repository images
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(namespace)/(repo_name)/images`
|
|
||||||
|
|
||||||
Update the images for a user repo.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foo/bar/images HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
|
||||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}]
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **namespace** – the namespace for the repo
|
|
||||||
- **repo_name** – the name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 204
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **204** – Created
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active or permission denied
|
|
||||||
|
|
||||||
### List user repository images
|
|
||||||
|
|
||||||
`GET /v1/repositories/(namespace)/(repo_name)/images`
|
|
||||||
|
|
||||||
Get the images for a user repo.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
GET /v1/repositories/foo/bar/images HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **namespace** – the namespace for the repo
|
|
||||||
- **repo_name** – the name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
|
||||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"},
|
|
||||||
{"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds",
|
|
||||||
"checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}]
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – OK
|
|
||||||
- **404** – Not found
|
|
||||||
|
|
||||||
## Library repository images
|
|
||||||
|
|
||||||
### Update library repository images
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(repo_name)/images`
|
|
||||||
|
|
||||||
Update the images for a library repo.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foobar/images HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
|
||||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}]
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **repo_name** – the library name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 204
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **204** – Created
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active or permission denied
|
|
||||||
|
|
||||||
### List library repository images
|
|
||||||
|
|
||||||
`GET /v1/repositories/(repo_name)/images`
|
|
||||||
|
|
||||||
Get the images for a library repo.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
GET /v1/repositories/foobar/images HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **repo_name** – the library name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
|
||||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"},
|
|
||||||
{"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds",
|
|
||||||
"checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}]
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – OK
|
|
||||||
- **404** – Not found
|
|
||||||
|
|
||||||
# Repository authorization
|
|
||||||
|
|
||||||
## Library repository
|
|
||||||
|
|
||||||
### Authorize a token for a library
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(repo_name)/auth`
|
|
||||||
|
|
||||||
Authorize a token for a library repo
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foobar/auth HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Token signature=123abc,repository="library/foobar",access=write
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **repo_name** – the library name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
"OK"
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – OK
|
|
||||||
- **403** – Permission denied
|
|
||||||
- **404** – Not found
|
|
||||||
|
|
||||||
## User repository
|
|
||||||
|
|
||||||
### Authorize a token for a user repository
|
|
||||||
|
|
||||||
`PUT /v1/repositories/(namespace)/(repo_name)/auth`
|
|
||||||
|
|
||||||
Authorize a token for a user repo
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/repositories/foo/bar/auth HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Token signature=123abc,repository="foo/bar",access=write
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **namespace** – the namespace for the repo
|
|
||||||
- **repo_name** – the name for the repo
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
"OK"
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – OK
|
|
||||||
- **403** – Permission denied
|
|
||||||
- **404** – Not found
|
|
||||||
|
|
||||||
## Users
|
|
||||||
|
|
||||||
### User login
|
|
||||||
|
|
||||||
`GET /v1/users/`
|
|
||||||
|
|
||||||
If you want to check your login, you can try this endpoint
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
GET /v1/users/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
OK
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **200** – no error
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
|
|
||||||
### User register
|
|
||||||
|
|
||||||
`POST /v1/users/`
|
|
||||||
|
|
||||||
Registering a new account.
|
|
||||||
|
|
||||||
**Example request**:
|
|
||||||
|
|
||||||
POST /v1/users/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"email": "sam@docker.com",
|
|
||||||
"password": "toto42",
|
|
||||||
"username": "foobar"}
|
|
||||||
|
|
||||||
Json Parameters:
|
|
||||||
|
|
||||||
- **email** – valid email address, that needs to be confirmed
|
|
||||||
- **username** – min 4 character, max 30 characters, must match
|
|
||||||
the regular expression [a-z0-9_].
|
|
||||||
- **password** – min 5 characters
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 201 OK
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
"User Created"
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **201** – User Created
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
|
|
||||||
### Update user
|
|
||||||
|
|
||||||
`PUT /v1/users/(username)/`
|
|
||||||
|
|
||||||
Change a password or email address for given user. If you pass in an
|
|
||||||
email, it will add it to your account, it will not remove the old
|
|
||||||
one. Passwords will be updated.
|
|
||||||
|
|
||||||
It is up to the client to verify that that password that is sent is
|
|
||||||
the one that they want. Common approach is to have them type it
|
|
||||||
twice.
|
|
||||||
|
|
||||||
**Example Request**:
|
|
||||||
|
|
||||||
PUT /v1/users/fakeuser/ HTTP/1.1
|
|
||||||
Host: index.docker.io
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic akmklmasadalkm==
|
|
||||||
|
|
||||||
{"email": "sam@docker.com",
|
|
||||||
"password": "toto42"}
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
|
|
||||||
- **username** – username for the person you want to update
|
|
||||||
|
|
||||||
**Example Response**:
|
|
||||||
|
|
||||||
HTTP/1.1 204
|
|
||||||
Vary: Accept
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
""
|
|
||||||
|
|
||||||
Status Codes:
|
|
||||||
|
|
||||||
- **204** – User Updated
|
|
||||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
|
||||||
- **401** – Unauthorized
|
|
||||||
- **403** – Account is not Active
|
|
||||||
- **404** – User not found
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ of a 404.
|
|||||||
You can now supply a `stream` bool to get only one set of stats and
|
You can now supply a `stream` bool to get only one set of stats and
|
||||||
disconnect
|
disconnect
|
||||||
|
|
||||||
`GET /containers(id)/logs`
|
`GET /containers/(id)/logs`
|
||||||
|
|
||||||
**New!**
|
**New!**
|
||||||
|
|
||||||
@@ -138,6 +138,7 @@ In addition, the end point now returns the new boolean fields
|
|||||||
This endpoint now returns `Os`, `Arch` and `KernelVersion`.
|
This endpoint now returns `Os`, `Arch` and `KernelVersion`.
|
||||||
|
|
||||||
`POST /containers/create`
|
`POST /containers/create`
|
||||||
|
|
||||||
`POST /containers/(id)/start`
|
`POST /containers/(id)/start`
|
||||||
|
|
||||||
**New!**
|
**New!**
|
||||||
@@ -297,429 +298,4 @@ The `fromImage` and `repo` parameters now supports the `repo:tag` format.
|
|||||||
Consequently, the `tag` parameter is now obsolete. Using the new format and
|
Consequently, the `tag` parameter is now obsolete. Using the new format and
|
||||||
the `tag` parameter at the same time will return an error.
|
the `tag` parameter at the same time will return an error.
|
||||||
|
|
||||||
## v1.13
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.13*](/reference/api/docker_remote_api_v1.13/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`GET /containers/(name)/json`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
The `HostConfig.Links` field is now filled correctly
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
`Sockets` parameter added to the `/info` endpoint listing all the sockets the
|
|
||||||
daemon is configured to listen on.
|
|
||||||
|
|
||||||
`POST /containers/(name)/start`
|
|
||||||
`POST /containers/(name)/stop`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
`start` and `stop` will now return 304 if the container's status is not modified
|
|
||||||
|
|
||||||
`POST /commit`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
Added a `pause` parameter (default `true`) to pause the container during commit
|
|
||||||
|
|
||||||
## v1.12
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.12*](/reference/api/docker_remote_api_v1.12/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /build`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
Build now has support for the `forcerm` parameter to always remove containers
|
|
||||||
|
|
||||||
`GET /containers/(name)/json`
|
|
||||||
`GET /images/(name)/json`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
All the JSON keys are now in CamelCase
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
Trusted builds are now Automated Builds - `is_trusted` is now `is_automated`.
|
|
||||||
|
|
||||||
**Removed Insert Endpoint**
|
|
||||||
The `insert` endpoint has been removed.
|
|
||||||
|
|
||||||
## v1.11
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.11*](/reference/api/docker_remote_api_v1.11/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`GET /_ping`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now ping the server via the `_ping` endpoint.
|
|
||||||
|
|
||||||
`GET /events`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now use the `-until` parameter to close connection
|
|
||||||
after timestamp.
|
|
||||||
|
|
||||||
`GET /containers/(id)/logs`
|
|
||||||
|
|
||||||
This url is preferred method for getting container logs now.
|
|
||||||
|
|
||||||
## v1.10
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.10*](/reference/api/docker_remote_api_v1.10/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`DELETE /images/(name)`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now use the force parameter to force delete of an
|
|
||||||
image, even if it's tagged in multiple repositories. **New!**
|
|
||||||
You
|
|
||||||
can now use the noprune parameter to prevent the deletion of parent
|
|
||||||
images
|
|
||||||
|
|
||||||
`DELETE /containers/(id)`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now use the force parameter to force delete a
|
|
||||||
container, even if it is currently running
|
|
||||||
|
|
||||||
## v1.9
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.9*](/reference/api/docker_remote_api_v1.9/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /build`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
This endpoint now takes a serialized ConfigFile which it
|
|
||||||
uses to resolve the proper registry auth credentials for pulling the
|
|
||||||
base image. Clients which previously implemented the version
|
|
||||||
accepting an AuthConfig object must be updated.
|
|
||||||
|
|
||||||
## v1.8
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.8*](/reference/api/docker_remote_api_v1.8/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /build`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
This endpoint now returns build status as json stream. In
|
|
||||||
case of a build error, it returns the exit status of the failed
|
|
||||||
command.
|
|
||||||
|
|
||||||
`GET /containers/(id)/json`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
This endpoint now returns the host config for the
|
|
||||||
container.
|
|
||||||
|
|
||||||
`POST /images/create`
|
|
||||||
|
|
||||||
`POST /images/(name)/insert`
|
|
||||||
|
|
||||||
`POST /images/(name)/push`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
progressDetail object was added in the JSON. It's now
|
|
||||||
possible to get the current value and the total of the progress
|
|
||||||
without having to parse the string.
|
|
||||||
|
|
||||||
## v1.7
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.7*](/reference/api/docker_remote_api_v1.7/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`GET /images/json`
|
|
||||||
|
|
||||||
The format of the json returned from this uri changed. Instead of an
|
|
||||||
entry for each repo/tag on an image, each image is only represented
|
|
||||||
once, with a nested attribute indicating the repo/tags that apply to
|
|
||||||
that image.
|
|
||||||
|
|
||||||
Instead of:
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"VirtualSize": 131506275,
|
|
||||||
"Size": 131506275,
|
|
||||||
"Created": 1365714795,
|
|
||||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
|
||||||
"Tag": "12.04",
|
|
||||||
"Repository": "ubuntu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"VirtualSize": 131506275,
|
|
||||||
"Size": 131506275,
|
|
||||||
"Created": 1365714795,
|
|
||||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
|
||||||
"Tag": "latest",
|
|
||||||
"Repository": "ubuntu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"VirtualSize": 131506275,
|
|
||||||
"Size": 131506275,
|
|
||||||
"Created": 1365714795,
|
|
||||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
|
||||||
"Tag": "precise",
|
|
||||||
"Repository": "ubuntu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"VirtualSize": 180116135,
|
|
||||||
"Size": 24653,
|
|
||||||
"Created": 1364102658,
|
|
||||||
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
|
||||||
"Tag": "12.10",
|
|
||||||
"Repository": "ubuntu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"VirtualSize": 180116135,
|
|
||||||
"Size": 24653,
|
|
||||||
"Created": 1364102658,
|
|
||||||
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
|
||||||
"Tag": "quantal",
|
|
||||||
"Repository": "ubuntu"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
The returned json looks like this:
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"RepoTags": [
|
|
||||||
"ubuntu:12.04",
|
|
||||||
"ubuntu:precise",
|
|
||||||
"ubuntu:latest"
|
|
||||||
],
|
|
||||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
|
||||||
"Created": 1365714795,
|
|
||||||
"Size": 131506275,
|
|
||||||
"VirtualSize": 131506275
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"RepoTags": [
|
|
||||||
"ubuntu:12.10",
|
|
||||||
"ubuntu:quantal"
|
|
||||||
],
|
|
||||||
"ParentId": "27cf784147099545",
|
|
||||||
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
|
||||||
"Created": 1364102658,
|
|
||||||
"Size": 24653,
|
|
||||||
"VirtualSize": 180116135
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
`GET /images/viz`
|
|
||||||
|
|
||||||
This URI no longer exists. The `images --viz`
|
|
||||||
output is now generated in the client, using the
|
|
||||||
`/images/json` data.
|
|
||||||
|
|
||||||
## v1.6
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.6*](/reference/api/docker_remote_api_v1.6/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /containers/(id)/attach`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now split stderr from stdout. This is done by
|
|
||||||
prefixing a header to each transmission. See
|
|
||||||
[`POST /containers/(id)/attach`](
|
|
||||||
/reference/api/docker_remote_api_v1.9/#attach-to-a-container "POST /containers/(id)/attach").
|
|
||||||
The WebSocket attach is unchanged. Note that attach calls on the
|
|
||||||
previous API version didn't change. Stdout and stderr are merged.
|
|
||||||
|
|
||||||
## v1.5
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.5*](/reference/api/docker_remote_api_v1.5/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /images/create`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now pass registry credentials (via an AuthConfig
|
|
||||||
object) through the X-Registry-Auth header
|
|
||||||
|
|
||||||
`POST /images/(name)/push`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
The AuthConfig object now needs to be passed through the
|
|
||||||
X-Registry-Auth header
|
|
||||||
|
|
||||||
`GET /containers/json`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
The format of the Ports entry has been changed to a list of
|
|
||||||
dicts each containing PublicPort, PrivatePort and Type describing a
|
|
||||||
port mapping.
|
|
||||||
|
|
||||||
## v1.4
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.4*](/reference/api/docker_remote_api_v1.4/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /images/create`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
When pulling a repo, all images are now downloaded in parallel.
|
|
||||||
|
|
||||||
`GET /containers/(id)/top`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
You can now use ps args with docker top, like docker top
|
|
||||||
<container_id> aux
|
|
||||||
|
|
||||||
`GET /events`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
Image's name added in the events
|
|
||||||
|
|
||||||
## v1.3
|
|
||||||
|
|
||||||
docker v0.5.0
|
|
||||||
[51f6c4a](https://github.com/docker/docker/commit/51f6c4a7372450d164c61e0054daf0223ddbd909)
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.3*](/reference/api/docker_remote_api_v1.3/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`GET /containers/(id)/top`
|
|
||||||
|
|
||||||
List the processes running inside a container.
|
|
||||||
|
|
||||||
`GET /events`
|
|
||||||
|
|
||||||
**New!**
|
|
||||||
Monitor docker's events via streaming or via polling
|
|
||||||
|
|
||||||
Builder (/build):
|
|
||||||
|
|
||||||
- Simplify the upload of the build context
|
|
||||||
- Simply stream a tarball instead of multipart upload with 4
|
|
||||||
intermediary buffers
|
|
||||||
- Simpler, less memory usage, less disk usage and faster
|
|
||||||
|
|
||||||
> **Warning**:
|
|
||||||
> The /build improvements are not reverse-compatible. Pre 1.3 clients will
|
|
||||||
> break on /build.
|
|
||||||
|
|
||||||
List containers (/containers/json):
|
|
||||||
|
|
||||||
- You can use size=1 to get the size of the containers
|
|
||||||
|
|
||||||
Start containers (/containers/<id>/start):
|
|
||||||
|
|
||||||
- You can now pass host-specific configuration (e.g., bind mounts) in
|
|
||||||
the POST body for start calls
|
|
||||||
|
|
||||||
## v1.2
|
|
||||||
|
|
||||||
docker v0.4.2
|
|
||||||
[2e7649b](https://github.com/docker/docker/commit/2e7649beda7c820793bd46766cbc2cfeace7b168)
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.2*](/reference/api/docker_remote_api_v1.2/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
The auth configuration is now handled by the client.
|
|
||||||
|
|
||||||
The client should send it's authConfig as POST on each call of
|
|
||||||
`/images/(name)/push`
|
|
||||||
|
|
||||||
`GET /auth`
|
|
||||||
|
|
||||||
**Deprecated.**
|
|
||||||
|
|
||||||
`POST /auth`
|
|
||||||
|
|
||||||
Only checks the configuration but doesn't store it on the server
|
|
||||||
|
|
||||||
Deleting an image is now improved, will only untag the image if it
|
|
||||||
has children and remove all the untagged parents if has any.
|
|
||||||
|
|
||||||
`POST /images/<name>/delete`
|
|
||||||
|
|
||||||
Now returns a JSON structure with the list of images
|
|
||||||
deleted/untagged.
|
|
||||||
|
|
||||||
## v1.1
|
|
||||||
|
|
||||||
docker v0.4.0
|
|
||||||
[a8ae398](https://github.com/docker/docker/commit/a8ae398bf52e97148ee7bd0d5868de2e15bd297f)
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.1*](/reference/api/docker_remote_api_v1.1/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
`POST /images/create`
|
|
||||||
|
|
||||||
`POST /images/(name)/insert`
|
|
||||||
|
|
||||||
`POST /images/(name)/push`
|
|
||||||
|
|
||||||
Uses json stream instead of HTML hijack, it looks like this:
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"status":"Pushing..."}
|
|
||||||
{"status":"Pushing", "progress":"1/? (n/a)"}
|
|
||||||
{"error":"Invalid..."}
|
|
||||||
...
|
|
||||||
|
|
||||||
## v1.0
|
|
||||||
|
|
||||||
docker v0.3.4
|
|
||||||
[8d73740](https://github.com/docker/docker/commit/8d73740343778651c09160cde9661f5f387b36f4)
|
|
||||||
|
|
||||||
### Full documentation
|
|
||||||
|
|
||||||
[*Docker Remote API v1.0*](/reference/api/docker_remote_api_v1.0/)
|
|
||||||
|
|
||||||
### What's new
|
|
||||||
|
|
||||||
Initial version
|
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
||||||
|
"Labels": {
|
||||||
|
"com.example.vendor": "Acme",
|
||||||
|
"com.example.license": "GPL",
|
||||||
|
"com.example.version": "1.0"
|
||||||
|
},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -60,6 +65,7 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -71,6 +77,7 @@ List containers
|
|||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports":[],
|
"Ports":[],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw":12288,
|
"SizeRw":12288,
|
||||||
"SizeRootFs":0
|
"SizeRootFs":0
|
||||||
},
|
},
|
||||||
@@ -82,6 +89,7 @@ List containers
|
|||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
||||||
|
"Labels": {
|
||||||
|
"com.example.vendor": "Acme",
|
||||||
|
"com.example.license": "GPL",
|
||||||
|
"com.example.version": "1.0"
|
||||||
|
},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -62,6 +67,7 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -73,6 +79,7 @@ List containers
|
|||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports":[],
|
"Ports":[],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw":12288,
|
"SizeRw":12288,
|
||||||
"SizeRootFs":0
|
"SizeRootFs":0
|
||||||
},
|
},
|
||||||
@@ -84,6 +91,7 @@ List containers
|
|||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
"Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
|
||||||
|
"Labels": {
|
||||||
|
"com.example.vendor": "Acme",
|
||||||
|
"com.example.license": "GPL",
|
||||||
|
"com.example.version": "1.0"
|
||||||
|
},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -62,6 +67,7 @@ List containers
|
|||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
},
|
},
|
||||||
@@ -73,6 +79,7 @@ List containers
|
|||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports":[],
|
"Ports":[],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw":12288,
|
"SizeRw":12288,
|
||||||
"SizeRootFs":0
|
"SizeRootFs":0
|
||||||
},
|
},
|
||||||
@@ -84,6 +91,7 @@ List containers
|
|||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
"Ports": [],
|
"Ports": [],
|
||||||
|
"Labels": {},
|
||||||
"SizeRw": 12288,
|
"SizeRw": 12288,
|
||||||
"SizeRootFs": 0
|
"SizeRootFs": 0
|
||||||
}
|
}
|
||||||
@@ -1109,7 +1117,7 @@ Query Parameters:
|
|||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/x-tar
|
Content-Type: application/x-tar
|
||||||
X-Docker-Container-Path-Stat: eyJuYW1lIjoicm9vdCIsInBhdGgiOiIvcm9vdCIsInNpemUiOjQwOTYsIm1vZGUiOjIxNDc0ODQwOTYsIm10aW1lIjoiMjAxNC0wMi0yN1QyMDo1MToyM1oifQ==
|
X-Docker-Container-Path-Stat: eyJuYW1lIjoicm9vdCIsInNpemUiOjQwOTYsIm1vZGUiOjIxNDc0ODQwOTYsIm10aW1lIjoiMjAxNC0wMi0yN1QyMDo1MToyM1oiLCJsaW5rVGFyZ2V0IjoiIn0=
|
||||||
|
|
||||||
{{ TAR STREAM }}
|
{{ TAR STREAM }}
|
||||||
|
|
||||||
@@ -1120,10 +1128,10 @@ JSON object (whitespace added for readability):
|
|||||||
|
|
||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"path": "/root",
|
|
||||||
"size": 4096,
|
"size": 4096,
|
||||||
"mode": 2147484096,
|
"mode": 2147484096,
|
||||||
"mtime": "2014-02-27T20:51:23Z"
|
"mtime": "2014-02-27T20:51:23Z",
|
||||||
|
"linkTarget": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
A `HEAD` request can also be made to this endpoint if only this information is
|
A `HEAD` request can also be made to this endpoint if only this information is
|
||||||
|
|||||||
@@ -1,761 +0,0 @@
|
|||||||
<!--[metadata]>
|
|
||||||
+++
|
|
||||||
title = "The Docker Hub and the Registry v1"
|
|
||||||
description = "Documentation for docker Registry and Registry API"
|
|
||||||
keywords = ["docker, registry, api, hub"]
|
|
||||||
[menu.main]
|
|
||||||
parent="smn_hub_ref"
|
|
||||||
+++
|
|
||||||
<![end-metadata]-->
|
|
||||||
|
|
||||||
# The Docker Hub and the Registry v1
|
|
||||||
|
|
||||||
## The three roles
|
|
||||||
|
|
||||||
There are three major components playing a role in the Docker ecosystem.
|
|
||||||
|
|
||||||
### Docker Hub
|
|
||||||
|
|
||||||
The Docker Hub is responsible for centralizing information about:
|
|
||||||
|
|
||||||
- User accounts
|
|
||||||
- Checksums of the images
|
|
||||||
- Public namespaces
|
|
||||||
|
|
||||||
The Docker Hub has different components:
|
|
||||||
|
|
||||||
- Web UI
|
|
||||||
- Meta-data store (comments, stars, list public repositories)
|
|
||||||
- Authentication service
|
|
||||||
- Tokenization
|
|
||||||
|
|
||||||
The Docker Hub is authoritative for that information.
|
|
||||||
|
|
||||||
There is only one instance of the Docker Hub, run and
|
|
||||||
managed by Docker Inc.
|
|
||||||
|
|
||||||
### Docker Registry 1.0
|
|
||||||
|
|
||||||
The 1.0 registry has the following characteristics:
|
|
||||||
|
|
||||||
- It stores the images and the graph for a set of repositories
|
|
||||||
- It does not have user accounts data
|
|
||||||
- It has no notion of user accounts or authorization
|
|
||||||
- It delegates authentication and authorization to the Docker Hub Auth
|
|
||||||
service using tokens
|
|
||||||
- It supports different storage backends (S3, cloud files, local FS)
|
|
||||||
- It doesn't have a local database
|
|
||||||
- [Source Code](https://github.com/docker/docker-registry)
|
|
||||||
|
|
||||||
We expect that there will be multiple registries out there. To help you
|
|
||||||
grasp the context, here are some examples of registries:
|
|
||||||
|
|
||||||
- **sponsor registry**: such a registry is provided by a third-party
|
|
||||||
hosting infrastructure as a convenience for their customers and the
|
|
||||||
Docker community as a whole. Its costs are supported by the third
|
|
||||||
party, but the management and operation of the registry are
|
|
||||||
supported by Docker, Inc. It features read/write access, and delegates
|
|
||||||
authentication and authorization to the Docker Hub.
|
|
||||||
- **mirror registry**: such a registry is provided by a third-party
|
|
||||||
hosting infrastructure but is targeted at their customers only. Some
|
|
||||||
mechanism (unspecified to date) ensures that public images are
|
|
||||||
pulled from a sponsor registry to the mirror registry, to make sure
|
|
||||||
that the customers of the third-party provider can `docker pull`
|
|
||||||
those images locally.
|
|
||||||
- **vendor registry**: such a registry is provided by a software
|
|
||||||
vendor who wants to distribute docker images. It would be operated
|
|
||||||
and managed by the vendor. Only users authorized by the vendor would
|
|
||||||
be able to get write access. Some images would be public (accessible
|
|
||||||
for anyone), others private (accessible only for authorized users).
|
|
||||||
Authentication and authorization would be delegated to the Docker Hub.
|
|
||||||
The goal of vendor registries is to let someone do `docker pull
|
|
||||||
basho/riak1.3` and automatically push from the vendor registry
|
|
||||||
(instead of a sponsor registry); i.e., vendors get all the convenience of a
|
|
||||||
sponsor registry, while retaining control on the asset distribution.
|
|
||||||
- **private registry**: such a registry is located behind a firewall,
|
|
||||||
or protected by an additional security layer (HTTP authorization,
|
|
||||||
SSL client-side certificates, IP address authorization...). The
|
|
||||||
registry is operated by a private entity, outside of Docker's
|
|
||||||
control. It can optionally delegate additional authorization to the
|
|
||||||
Docker Hub, but it is not mandatory.
|
|
||||||
|
|
||||||
> **Note:** The latter implies that while HTTP is the protocol
|
|
||||||
> of choice for a registry, multiple schemes are possible (and
|
|
||||||
> in some cases, trivial):
|
|
||||||
>
|
|
||||||
> - HTTP with GET (and PUT for read-write registries);
|
|
||||||
> - local mount point;
|
|
||||||
> - remote docker addressed through SSH.
|
|
||||||
|
|
||||||
The latter would only require two new commands in Docker, e.g.,
|
|
||||||
`registryget` and `registryput`,
|
|
||||||
wrapping access to the local filesystem (and optionally doing
|
|
||||||
consistency checks). Authentication and authorization are then delegated
|
|
||||||
to SSH (e.g., with public keys).
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
On top of being a runtime for LXC, Docker is the Registry client. It
|
|
||||||
supports:
|
|
||||||
|
|
||||||
- Push / Pull on the registry
|
|
||||||
- Client authentication on the Docker Hub
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
### Pull
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
1. Contact the Docker Hub to know where I should download “samalba/busybox”
|
|
||||||
2. Docker Hub replies: a. `samalba/busybox` is on Registry A b. here are the
|
|
||||||
checksums for `samalba/busybox` (for all layers) c. token
|
|
||||||
3. Contact Registry A to receive the layers for `samalba/busybox` (all of
|
|
||||||
them to the base image). Registry A is authoritative for “samalba/busybox”
|
|
||||||
but keeps a copy of all inherited layers and serve them all from the same
|
|
||||||
location.
|
|
||||||
4. registry contacts Docker Hub to verify if token/user is allowed to download images
|
|
||||||
5. Docker Hub returns true/false lettings registry know if it should proceed or error
|
|
||||||
out
|
|
||||||
6. Get the payload for all layers
|
|
||||||
|
|
||||||
It's possible to run:
|
|
||||||
|
|
||||||
$ docker pull https://<registry>/repositories/samalba/busybox
|
|
||||||
|
|
||||||
In this case, Docker bypasses the Docker Hub. However the security is not
|
|
||||||
guaranteed (in case Registry A is corrupted) because there won't be any
|
|
||||||
checksum checks.
|
|
||||||
|
|
||||||
Currently registry redirects to s3 urls for downloads, going forward all
|
|
||||||
downloads need to be streamed through the registry. The Registry will
|
|
||||||
then abstract the calls to S3 by a top-level class which implements
|
|
||||||
sub-classes for S3 and local storage.
|
|
||||||
|
|
||||||
Token is only returned when the `X-Docker-Token`
|
|
||||||
header is sent with request.
|
|
||||||
|
|
||||||
Basic Auth is required to pull private repos. Basic auth isn't required
|
|
||||||
for pulling public repos, but if one is provided, it needs to be valid
|
|
||||||
and for an active account.
|
|
||||||
|
|
||||||
**API (pulling repository foo/bar):**
|
|
||||||
|
|
||||||
1. (Docker -> Docker Hub) GET /v1/repositories/foo/bar/images:
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
(looking up the foo/bar in db and gets images and checksums
|
|
||||||
for that repo (all if no tag is specified, if tag, only
|
|
||||||
checksums for those tags) see part 4.4.1)
|
|
||||||
|
|
||||||
2. (Docker Hub -> Docker) HTTP 200 OK
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
X-Docker-Endpoints: registry.docker.io [,registry2.docker.io]
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
Jsonified checksums (see part 4.4.1)
|
|
||||||
|
|
||||||
3. (Docker -> Registry) GET /v1/repositories/foo/bar/tags/latest
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
|
|
||||||
4. (Registry -> Docker Hub) GET /v1/repositories/foo/bar/images
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=read
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
<ids and checksums in payload>
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
(Lookup token see if they have access to pull.)
|
|
||||||
|
|
||||||
If good:
|
|
||||||
HTTP 200 OK Docker Hub will invalidate the token
|
|
||||||
|
|
||||||
If bad:
|
|
||||||
HTTP 401 Unauthorized
|
|
||||||
|
|
||||||
5. (Docker -> Registry) GET /v1/images/928374982374/ancestry
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
(for each image id returned in the registry, fetch /json + /layer)
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> If someone makes a second request, then we will always give a new token,
|
|
||||||
> never reuse tokens.
|
|
||||||
|
|
||||||
### Push
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
1. Contact the Docker Hub to allocate the repository name “samalba/busybox”
|
|
||||||
(authentication required with user credentials)
|
|
||||||
2. If authentication works and namespace available, “samalba/busybox”
|
|
||||||
is allocated and a temporary token is returned (namespace is marked
|
|
||||||
as initialized in Docker Hub)
|
|
||||||
3. Push the image on the registry (along with the token)
|
|
||||||
4. Registry A contacts the Docker Hub to verify the token (token must
|
|
||||||
corresponds to the repository name)
|
|
||||||
5. Docker Hub validates the token. Registry A starts reading the stream
|
|
||||||
pushed by docker and store the repository (with its images)
|
|
||||||
6. docker contacts the Docker Hub to give checksums for upload images
|
|
||||||
|
|
||||||
> **Note:**
|
|
||||||
> **It's possible not to use the Docker Hub at all!** In this case, a deployed
|
|
||||||
> version of the Registry is deployed to store and serve images. Those
|
|
||||||
> images are not authenticated and the security is not guaranteed.
|
|
||||||
|
|
||||||
> **Note:**
|
|
||||||
> **Docker Hub can be replaced!** For a private Registry deployed, a custom
|
|
||||||
> Docker Hub can be used to serve and validate token according to different
|
|
||||||
> policies.
|
|
||||||
|
|
||||||
Docker computes the checksums and submit them to the Docker Hub at the end of
|
|
||||||
the push. When a repository name does not have checksums on the Docker Hub,
|
|
||||||
it means that the push is in progress (since checksums are submitted at
|
|
||||||
the end).
|
|
||||||
|
|
||||||
**API (pushing repos foo/bar):**
|
|
||||||
|
|
||||||
1. (Docker -> Docker Hub) PUT /v1/repositories/foo/bar/
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Basic sdkjfskdjfhsdkjfh== X-Docker-Token:
|
|
||||||
true
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
- in Docker Hub, we allocated a new repository, and set to
|
|
||||||
initialized
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
(The body contains the list of images that are going to be
|
|
||||||
pushed, with empty checksums. The checksums will be set at
|
|
||||||
the end of the push):
|
|
||||||
|
|
||||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
|
|
||||||
|
|
||||||
2. (Docker Hub -> Docker) 200 Created
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
WWW-Authenticate: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
|
|
||||||
|
|
||||||
3. (Docker -> Registry) PUT /v1/images/98765432_parent/json
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
|
|
||||||
4. (Registry->Docker Hub) GET /v1/repositories/foo/bar/images
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
- Docker Hub:
|
|
||||||
will invalidate the token.
|
|
||||||
- Registry:
|
|
||||||
grants a session (if token is approved) and fetches
|
|
||||||
the images id
|
|
||||||
|
|
||||||
5. (Docker -> Registry) PUT /v1/images/98765432_parent/json
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=write
|
|
||||||
Cookie: (Cookie provided by the Registry)
|
|
||||||
|
|
||||||
6. (Docker -> Registry) PUT /v1/images/98765432/json
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Cookie: (Cookie provided by the Registry)
|
|
||||||
|
|
||||||
7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Cookie: (Cookie provided by the Registry)
|
|
||||||
|
|
||||||
8. (Docker -> Registry) PUT /v1/images/98765432/layer
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh
|
|
||||||
|
|
||||||
9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Cookie: (Cookie provided by the Registry)
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
“98765432”
|
|
||||||
|
|
||||||
10. (Docker -> Docker Hub) PUT /v1/repositories/foo/bar/images
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Basic 123oislifjsldfj== X-Docker-Endpoints:
|
|
||||||
registry1.docker.io (no validation on this right now)
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
(The image, id`s, tags and checksums)
|
|
||||||
[{“id”:
|
|
||||||
“9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
|
||||||
“checksum”:
|
|
||||||
“b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
|
|
||||||
|
|
||||||
**Return**:
|
|
||||||
|
|
||||||
HTTP 204
|
|
||||||
|
|
||||||
> **Note:** If push fails and they need to start again, what happens in the Docker Hub,
|
|
||||||
> there will already be a record for the namespace/name, but it will be
|
|
||||||
> initialized. Should we allow it, or mark as name already used? One edge
|
|
||||||
> case could be if someone pushes the same thing at the same time with two
|
|
||||||
> different shells.
|
|
||||||
|
|
||||||
If it's a retry on the Registry, Docker has a cookie (provided by the
|
|
||||||
registry after token validation). So the Docker Hub won't have to provide a
|
|
||||||
new token.
|
|
||||||
|
|
||||||
### Delete
|
|
||||||
|
|
||||||
If you need to delete something from the Docker Hub or registry, we need a
|
|
||||||
nice clean way to do that. Here is the workflow.
|
|
||||||
|
|
||||||
1. Docker contacts the Docker Hub to request a delete of a repository
|
|
||||||
`samalba/busybox` (authentication required with user credentials)
|
|
||||||
2. If authentication works and repository is valid, `samalba/busybox`
|
|
||||||
is marked as deleted and a temporary token is returned
|
|
||||||
3. Send a delete request to the registry for the repository (along with
|
|
||||||
the token)
|
|
||||||
4. Registry A contacts the Docker Hub to verify the token (token must
|
|
||||||
corresponds to the repository name)
|
|
||||||
5. Docker Hub validates the token. Registry A deletes the repository and
|
|
||||||
everything associated to it.
|
|
||||||
6. docker contacts the Docker Hub to let it know it was removed from the
|
|
||||||
registry, the Docker Hub removes all records from the database.
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> The Docker client should present an "Are you sure?" prompt to confirm
|
|
||||||
> the deletion before starting the process. Once it starts it can't be
|
|
||||||
> undone.
|
|
||||||
|
|
||||||
**API (deleting repository foo/bar):**
|
|
||||||
|
|
||||||
1. (Docker -> Docker Hub) DELETE /v1/repositories/foo/bar/
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Basic sdkjfskdjfhsdkjfh== X-Docker-Token:
|
|
||||||
true
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
- in Docker Hub, we make sure it is a valid repository, and set
|
|
||||||
to deleted (logically)
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
Empty
|
|
||||||
|
|
||||||
2. (Docker Hub -> Docker) 202 Accepted
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
WWW-Authenticate: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=delete
|
|
||||||
X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
|
|
||||||
# list of endpoints where this repo lives.
|
|
||||||
|
|
||||||
3. (Docker -> Registry) DELETE /v1/repositories/foo/bar/
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=delete
|
|
||||||
|
|
||||||
4. (Registry->Docker Hub) PUT /v1/repositories/foo/bar/auth
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=delete
|
|
||||||
|
|
||||||
**Action**:
|
|
||||||
|
|
||||||
- Docker Hub:
|
|
||||||
will invalidate the token.
|
|
||||||
- Registry:
|
|
||||||
deletes the repository (if token is approved)
|
|
||||||
|
|
||||||
5. (Registry -> Docker) 200 OK
|
|
||||||
|
|
||||||
200 If success 403 if forbidden 400 if bad request 404
|
|
||||||
if repository isn't found
|
|
||||||
|
|
||||||
6. (Docker -> Docker Hub) DELETE /v1/repositories/foo/bar/
|
|
||||||
|
|
||||||
**Headers**:
|
|
||||||
|
|
||||||
Authorization: Basic 123oislifjsldfj== X-Docker-Endpoints:
|
|
||||||
registry-1.docker.io (no validation on this right now)
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
Empty
|
|
||||||
|
|
||||||
**Return**:
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
|
|
||||||
## How to use the Registry in standalone mode
|
|
||||||
|
|
||||||
The Docker Hub has two main purposes (along with its fancy social features):
|
|
||||||
|
|
||||||
- Resolve short names (to avoid passing absolute URLs all the time):
|
|
||||||
|
|
||||||
username/projectname ->
|
|
||||||
https://registry.docker.io/users/<username>/repositories/<projectname>/
|
|
||||||
team/projectname ->
|
|
||||||
https://registry.docker.io/team/<team>/repositories/<projectname>/
|
|
||||||
|
|
||||||
- Authenticate a user as a repos owner (for a central referenced
|
|
||||||
repository)
|
|
||||||
|
|
||||||
### Without a Docker Hub
|
|
||||||
|
|
||||||
Using the Registry without the Docker Hub can be useful to store the images
|
|
||||||
on a private network without having to rely on an external entity
|
|
||||||
controlled by Docker Inc.
|
|
||||||
|
|
||||||
In this case, the registry will be launched in a special mode
|
|
||||||
(-standalone? ne? -no-index?). In this mode, the only thing which changes is
|
|
||||||
that Registry will never contact the Docker Hub to verify a token. It will be
|
|
||||||
the Registry owner responsibility to authenticate the user who pushes
|
|
||||||
(or even pulls) an image using any mechanism (HTTP auth, IP based,
|
|
||||||
etc...).
|
|
||||||
|
|
||||||
In this scenario, the Registry is responsible for the security in case
|
|
||||||
of data corruption since the checksums are not delivered by a trusted
|
|
||||||
entity.
|
|
||||||
|
|
||||||
As hinted previously, a standalone registry can also be implemented by
|
|
||||||
any HTTP server handling GET/PUT requests (or even only GET requests if
|
|
||||||
no write access is necessary).
|
|
||||||
|
|
||||||
### With a Docker Hub
|
|
||||||
|
|
||||||
The Docker Hub data needed by the Registry are simple:
|
|
||||||
|
|
||||||
- Serve the checksums
|
|
||||||
- Provide and authorize a Token
|
|
||||||
|
|
||||||
In the scenario of a Registry running on a private network with the need
|
|
||||||
of centralizing and authorizing, it's easy to use a custom Docker Hub.
|
|
||||||
|
|
||||||
The only challenge will be to tell Docker to contact (and trust) this
|
|
||||||
custom Docker Hub. Docker will be configurable at some point to use a
|
|
||||||
specific Docker Hub, it'll be the private entity responsibility (basically
|
|
||||||
the organization who uses Docker in a private environment) to maintain
|
|
||||||
the Docker Hub and the Docker's configuration among its consumers.
|
|
||||||
|
|
||||||
## The API
|
|
||||||
|
|
||||||
The first version of the api is available here:
|
|
||||||
[https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md](https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md)
|
|
||||||
|
|
||||||
### Images
|
|
||||||
|
|
||||||
The format returned in the images is not defined here (for layer and
|
|
||||||
JSON), basically because Registry stores exactly the same kind of
|
|
||||||
information as Docker uses to manage them.
|
|
||||||
|
|
||||||
The format of ancestry is a line-separated list of image ids, in age
|
|
||||||
order, i.e. the image's parent is on the last line, the parent of the
|
|
||||||
parent on the next-to-last line, etc.; if the image has no parent, the
|
|
||||||
file is empty.
|
|
||||||
|
|
||||||
GET /v1/images/<image_id>/layer
|
|
||||||
PUT /v1/images/<image_id>/layer
|
|
||||||
GET /v1/images/<image_id>/json
|
|
||||||
PUT /v1/images/<image_id>/json
|
|
||||||
GET /v1/images/<image_id>/ancestry
|
|
||||||
PUT /v1/images/<image_id>/ancestry
|
|
||||||
|
|
||||||
### Users
|
|
||||||
|
|
||||||
### Create a user (Docker Hub)
|
|
||||||
|
|
||||||
POST /v1/users:
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
{"email": "[sam@docker.com](mailto:sam%40docker.com)",
|
|
||||||
"password": "toto42", "username": "foobar"`}
|
|
||||||
|
|
||||||
**Validation**:
|
|
||||||
|
|
||||||
- **username**: min 4 character, max 30 characters, must match the
|
|
||||||
regular expression [a-z0-9_].
|
|
||||||
- **password**: min 5 characters
|
|
||||||
|
|
||||||
**Valid**:
|
|
||||||
|
|
||||||
return HTTP 201
|
|
||||||
|
|
||||||
Errors: HTTP 400 (we should create error codes for possible errors) -
|
|
||||||
invalid json - missing field - wrong format (username, password, email,
|
|
||||||
etc) - forbidden name - name already exists
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> A user account will be valid only if the email has been validated (a
|
|
||||||
> validation link is sent to the email address).
|
|
||||||
|
|
||||||
### Update a user (Docker Hub)
|
|
||||||
|
|
||||||
PUT /v1/users/<username>
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
{"password": "toto"}
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> We can also update email address, if they do, they will need to reverify
|
|
||||||
> their new email address.
|
|
||||||
|
|
||||||
### Login (Docker Hub)
|
|
||||||
|
|
||||||
Does nothing else but asking for a user authentication. Can be used to
|
|
||||||
validate credentials. HTTP Basic Auth for now, maybe change in future.
|
|
||||||
|
|
||||||
GET /v1/users
|
|
||||||
|
|
||||||
**Return**:
|
|
||||||
- Valid: HTTP 200
|
|
||||||
- Invalid login: HTTP 401
|
|
||||||
- Account inactive: HTTP 403 Account is not Active
|
|
||||||
|
|
||||||
### Tags (Registry)
|
|
||||||
|
|
||||||
The Registry does not know anything about users. Even though
|
|
||||||
repositories are under usernames, it's just a namespace for the
|
|
||||||
registry. Allowing us to implement organizations or different namespaces
|
|
||||||
per user later, without modifying the Registry's API.
|
|
||||||
|
|
||||||
The following naming restrictions apply:
|
|
||||||
|
|
||||||
- Namespaces must match the same regular expression as usernames (See
|
|
||||||
4.2.1.)
|
|
||||||
- Repository names must match the regular expression [a-zA-Z0-9-_.]
|
|
||||||
|
|
||||||
### Get all tags:
|
|
||||||
|
|
||||||
GET /v1/repositories/<namespace>/<repository_name>/tags
|
|
||||||
|
|
||||||
**Return**: HTTP 200
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"layer": "9e89cc6f",
|
|
||||||
"name": "latest"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"layer": "b486531f",
|
|
||||||
"name": "0.1.1",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
**4.3.2 Read the content of a tag (resolve the image id):**
|
|
||||||
|
|
||||||
GET /v1/repositories/<namespace>/<repo_name>/tags/<tag>
|
|
||||||
|
|
||||||
**Return**:
|
|
||||||
|
|
||||||
"9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
|
|
||||||
|
|
||||||
**4.3.3 Delete a tag (registry):**
|
|
||||||
|
|
||||||
DELETE /v1/repositories/<namespace>/<repo_name>/tags/<tag>
|
|
||||||
|
|
||||||
### 4.4 Images (Docker Hub)
|
|
||||||
|
|
||||||
For the Docker Hub to “resolve” the repository name to a Registry location,
|
|
||||||
it uses the X-Docker-Endpoints header. In other terms, this requests
|
|
||||||
always add a `X-Docker-Endpoints` to indicate the
|
|
||||||
location of the registry which hosts this repository.
|
|
||||||
|
|
||||||
**4.4.1 Get the images:**
|
|
||||||
|
|
||||||
GET /v1/repositories/<namespace>/<repo_name>/images
|
|
||||||
|
|
||||||
**Return**: HTTP 200
|
|
||||||
[{“id”:
|
|
||||||
“9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
|
||||||
“checksum”:
|
|
||||||
“[md5:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087](md5:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087)”}]
|
|
||||||
|
|
||||||
### Add/update the images:
|
|
||||||
|
|
||||||
You always add images, you never remove them.
|
|
||||||
|
|
||||||
PUT /v1/repositories/<namespace>/<repo_name>/images
|
|
||||||
|
|
||||||
**Body**:
|
|
||||||
|
|
||||||
[ {“id”:
|
|
||||||
“9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
|
||||||
“checksum”:
|
|
||||||
“sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}
|
|
||||||
]
|
|
||||||
|
|
||||||
**Return**:
|
|
||||||
|
|
||||||
204
|
|
||||||
|
|
||||||
### Repositories
|
|
||||||
|
|
||||||
### Remove a Repository (Registry)
|
|
||||||
|
|
||||||
DELETE /v1/repositories/<namespace>/<repo_name>
|
|
||||||
|
|
||||||
Return 200 OK
|
|
||||||
|
|
||||||
### Remove a Repository (Docker Hub)
|
|
||||||
|
|
||||||
This starts the delete process. see 2.3 for more details.
|
|
||||||
|
|
||||||
DELETE /v1/repositories/<namespace>/<repo_name>
|
|
||||||
|
|
||||||
Return 202 OK
|
|
||||||
|
|
||||||
## Chaining Registries
|
|
||||||
|
|
||||||
It's possible to chain Registries server for several reasons:
|
|
||||||
|
|
||||||
- Load balancing
|
|
||||||
- Delegate the next request to another server
|
|
||||||
|
|
||||||
When a Registry is a reference for a repository, it should host the
|
|
||||||
entire images chain in order to avoid breaking the chain during the
|
|
||||||
download.
|
|
||||||
|
|
||||||
The Docker Hub and Registry use this mechanism to redirect on one or the
|
|
||||||
other.
|
|
||||||
|
|
||||||
Example with an image download:
|
|
||||||
|
|
||||||
On every request, a special header can be returned:
|
|
||||||
|
|
||||||
X-Docker-Endpoints: server1,server2
|
|
||||||
|
|
||||||
On the next request, the client will always pick a server from this
|
|
||||||
list.
|
|
||||||
|
|
||||||
## Authentication and authorization
|
|
||||||
|
|
||||||
### On the Docker Hub
|
|
||||||
|
|
||||||
The Docker Hub supports both “Basic” and “Token” challenges. Usually when
|
|
||||||
there is a `401 Unauthorized`, the Docker Hub replies
|
|
||||||
this:
|
|
||||||
|
|
||||||
401 Unauthorized
|
|
||||||
WWW-Authenticate: Basic realm="auth required",Token
|
|
||||||
|
|
||||||
You have 3 options:
|
|
||||||
|
|
||||||
1. Provide user credentials and ask for a token
|
|
||||||
|
|
||||||
**Header**:
|
|
||||||
|
|
||||||
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
|
||||||
X-Docker-Token: true
|
|
||||||
|
|
||||||
In this case, along with the 200 response, you'll get a new token
|
|
||||||
(if user auth is ok): If authorization isn't correct you get a 401
|
|
||||||
response. If account isn't active you will get a 403 response.
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
|
|
||||||
200 OK
|
|
||||||
X-Docker-Token: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=read
|
|
||||||
|
|
||||||
|
|
||||||
2. Provide user credentials only
|
|
||||||
|
|
||||||
**Header**:
|
|
||||||
|
|
||||||
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
|
||||||
|
|
||||||
3. Provide Token
|
|
||||||
|
|
||||||
**Header**:
|
|
||||||
|
|
||||||
Authorization: Token
|
|
||||||
signature=123abc,repository=”foo/bar”,access=read
|
|
||||||
|
|
||||||
### 6.2 On the Registry
|
|
||||||
|
|
||||||
The Registry only supports the Token challenge:
|
|
||||||
|
|
||||||
401 Unauthorized
|
|
||||||
WWW-Authenticate: Token
|
|
||||||
|
|
||||||
The only way is to provide a token on `401 Unauthorized`
|
|
||||||
responses:
|
|
||||||
|
|
||||||
Authorization: Token signature=123abc,repository="foo/bar",access=read
|
|
||||||
|
|
||||||
Usually, the Registry provides a Cookie when a Token verification
|
|
||||||
succeeded. Every time the Registry passes a Cookie, you have to pass it
|
|
||||||
back the same cookie.:
|
|
||||||
|
|
||||||
200 OK
|
|
||||||
Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=×tamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly
|
|
||||||
|
|
||||||
Next request:
|
|
||||||
|
|
||||||
GET /(...)
|
|
||||||
Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=×tamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="
|
|
||||||
|
|
||||||
## Document version
|
|
||||||
|
|
||||||
- 1.0 : May 6th 2013 : initial release
|
|
||||||
- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new
|
|
||||||
source namespace.
|
|
||||||
|
|
||||||
@@ -114,12 +114,6 @@ images.
|
|||||||
|
|
||||||
### Environment replacement
|
### Environment replacement
|
||||||
|
|
||||||
> **Note**: prior to 1.3, `Dockerfile` environment variables were handled
|
|
||||||
> similarly, in that they would be replaced as described below. However, there
|
|
||||||
> was no formal definition on as to which instructions handled environment
|
|
||||||
> replacement at the time. After 1.3 this behavior will be preserved and
|
|
||||||
> canonical.
|
|
||||||
|
|
||||||
Environment variables (declared with [the `ENV` statement](#env)) can also be
|
Environment variables (declared with [the `ENV` statement](#env)) can also be
|
||||||
used in certain instructions as variables to be interpreted by the
|
used in certain instructions as variables to be interpreted by the
|
||||||
`Dockerfile`. Escapes are also handled for including variable-like syntax
|
`Dockerfile`. Escapes are also handled for including variable-like syntax
|
||||||
|
|||||||
@@ -10,17 +10,13 @@ parent = "smn_cli"
|
|||||||
|
|
||||||
# Using the command line
|
# Using the command line
|
||||||
|
|
||||||
> **Note:** If you are using a remote Docker daemon, such as Boot2Docker,
|
|
||||||
> then _do not_ type the `sudo` before the `docker` commands shown in the
|
|
||||||
> documentation's examples.
|
|
||||||
|
|
||||||
To list available commands, either run `docker` with no parameters
|
To list available commands, either run `docker` with no parameters
|
||||||
or execute `docker help`:
|
or execute `docker help`:
|
||||||
|
|
||||||
$ docker
|
$ docker
|
||||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||||
docker daemon [ --help | ... ]
|
docker daemon [ --help | ... ]
|
||||||
docker [ -h | --help | -v | --version ]
|
docker [ --help | -v | --version ]
|
||||||
|
|
||||||
-H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
|
-H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ parent = "smn_cli"
|
|||||||
-G, --group="docker" Group for the unix socket
|
-G, --group="docker" Group for the unix socket
|
||||||
-g, --graph="/var/lib/docker" Root of the Docker runtime
|
-g, --graph="/var/lib/docker" Root of the Docker runtime
|
||||||
-H, --host=[] Daemon socket(s) to connect to
|
-H, --host=[] Daemon socket(s) to connect to
|
||||||
-h, --help=false Print usage
|
--help=false Print usage
|
||||||
--icc=true Enable inter-container communication
|
--icc=true Enable inter-container communication
|
||||||
--insecure-registry=[] Enable insecure registry communication
|
--insecure-registry=[] Enable insecure registry communication
|
||||||
--ip=0.0.0.0 Default IP when binding container ports
|
--ip=0.0.0.0 Default IP when binding container ports
|
||||||
@@ -45,6 +45,7 @@ parent = "smn_cli"
|
|||||||
--log-driver="json-file" Default driver for container logs
|
--log-driver="json-file" Default driver for container logs
|
||||||
--log-opt=[] Log driver specific options
|
--log-opt=[] Log driver specific options
|
||||||
--mtu=0 Set the containers network MTU
|
--mtu=0 Set the containers network MTU
|
||||||
|
--disable-legacy-registry=false Do not contact legacy registries
|
||||||
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
||||||
--registry-mirror=[] Preferred Docker registry mirror
|
--registry-mirror=[] Preferred Docker registry mirror
|
||||||
-s, --storage-driver="" Storage driver to use
|
-s, --storage-driver="" Storage driver to use
|
||||||
@@ -182,7 +183,7 @@ options for `zfs` start with `zfs`.
|
|||||||
|
|
||||||
If using a block device for device mapper storage, it is best to use `lvm`
|
If using a block device for device mapper storage, it is best to use `lvm`
|
||||||
to create and manage the thin-pool volume. This volume is then handed to Docker
|
to create and manage the thin-pool volume. This volume is then handed to Docker
|
||||||
to exclusively create snapshot volumes needed for images and containers.
|
to exclusively create snapshot volumes needed for images and containers.
|
||||||
|
|
||||||
Managing the thin-pool outside of Docker makes for the most feature-rich
|
Managing the thin-pool outside of Docker makes for the most feature-rich
|
||||||
method of having Docker utilize device mapper thin provisioning as the
|
method of having Docker utilize device mapper thin provisioning as the
|
||||||
@@ -218,7 +219,7 @@ options for `zfs` start with `zfs`.
|
|||||||
* `dm.loopdatasize`
|
* `dm.loopdatasize`
|
||||||
|
|
||||||
>**Note**: This option configures devicemapper loopback, which should not be used in production.
|
>**Note**: This option configures devicemapper loopback, which should not be used in production.
|
||||||
|
|
||||||
Specifies the size to use when creating the loopback file for the
|
Specifies the size to use when creating the loopback file for the
|
||||||
"data" device which is used for the thin pool. The default size is
|
"data" device which is used for the thin pool. The default size is
|
||||||
100G. The file is sparse, so it will not initially take up this
|
100G. The file is sparse, so it will not initially take up this
|
||||||
@@ -444,6 +445,13 @@ Local registries, whose IP address falls in the 127.0.0.0/8 range, are
|
|||||||
automatically marked as insecure as of Docker 1.3.2. It is not recommended to
|
automatically marked as insecure as of Docker 1.3.2. It is not recommended to
|
||||||
rely on this, as it may change in the future.
|
rely on this, as it may change in the future.
|
||||||
|
|
||||||
|
## Legacy Registries
|
||||||
|
|
||||||
|
Enabling `--disable-legacy-registry` forces a docker daemon to only interact with
|
||||||
|
registries which support the V2 protocol. Specifically, the daemon will not
|
||||||
|
attempt `push`, `pull` and `login` to v1 registries. The exception to this
|
||||||
|
is `search` which can still be performed on v1 registries.
|
||||||
|
|
||||||
## Running a Docker daemon behind a HTTPS_PROXY
|
## Running a Docker daemon behind a HTTPS_PROXY
|
||||||
|
|
||||||
When running inside a LAN that uses a `HTTPS` proxy, the Docker Hub
|
When running inside a LAN that uses a `HTTPS` proxy, the Docker Hub
|
||||||
@@ -466,7 +474,7 @@ use the proxy
|
|||||||
`--default-ulimit` allows you to set the default `ulimit` options to use for
|
`--default-ulimit` allows you to set the default `ulimit` options to use for
|
||||||
all containers. It takes the same options as `--ulimit` for `docker run`. If
|
all containers. It takes the same options as `--ulimit` for `docker run`. If
|
||||||
these defaults are not set, `ulimit` settings will be inherited, if not set on
|
these defaults are not set, `ulimit` settings will be inherited, if not set on
|
||||||
`docker run`, from the Docker daemon. Any `--ulimit` options passed to
|
`docker run`, from the Docker daemon. Any `--ulimit` options passed to
|
||||||
`docker run` will overwrite these defaults.
|
`docker run` will overwrite these defaults.
|
||||||
|
|
||||||
Be careful setting `nproc` with the `ulimit` flag as `nproc` is designed by Linux to
|
Be careful setting `nproc` with the `ulimit` flag as `nproc` is designed by Linux to
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
AppArmor security profiles for Docker
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
AppArmor (Application Armor) is a security module that allows a system
|
||||||
|
administrator to associate a security profile with each program. Docker
|
||||||
|
expects to find an AppArmor policy loaded and enforced.
|
||||||
|
|
||||||
|
Container profiles are loaded automatically by Docker. A profile
|
||||||
|
for the Docker Engine itself also exists and is installed
|
||||||
|
with the official *.deb* packages. Advanced users and package
|
||||||
|
managers may find the profile for */usr/bin/docker* underneath
|
||||||
|
[contrib/apparmor](https://github.com/docker/docker/tree/master/contrib/apparmor)
|
||||||
|
in the Docker Engine source repository.
|
||||||
|
|
||||||
|
|
||||||
|
Understand the policies
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The `docker-default` profile the default for running
|
||||||
|
containers. It is moderately protective while
|
||||||
|
providing wide application compatability.
|
||||||
|
|
||||||
|
The system's standard `unconfined` profile inherits all
|
||||||
|
system-wide policies, applying path-based policies
|
||||||
|
intended for the host system inside of containers.
|
||||||
|
This was the default for privileged containers
|
||||||
|
prior to Docker 1.8.
|
||||||
|
|
||||||
|
|
||||||
|
Overriding the profile for a container
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
Users may override the AppArmor profile using the
|
||||||
|
`security-opt` option (per-container).
|
||||||
|
|
||||||
|
For example, the following explicitly specifies the default policy:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker run --rm -it --security-opt apparmor:docker-default hello-world
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Content trust in Docker"
|
||||||
|
description = "Enabling content trust in Docker"
|
||||||
|
keywords = ["content, trust, security, docker, documentation"]
|
||||||
|
[menu.main]
|
||||||
|
parent= "smn_content_trust"
|
||||||
|
weight=-1
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Content trust in Docker
|
||||||
|
|
||||||
|
When transferring data among networked systems, *trust* is a central concern. In
|
||||||
|
particular, when communicating over an untrusted medium such as the internet, it
|
||||||
|
is critical to ensure the integrity and publisher of all the data a system
|
||||||
|
operates on. You use Docker to push and pull images (data) to a registry. Content trust
|
||||||
|
gives you the ability to both verify the integrity and the publisher of all the
|
||||||
|
data received from a registry over any channel.
|
||||||
|
|
||||||
|
Content trust is currently only available for users of the public Docker Hub. It
|
||||||
|
is currently not available for the Docker Trusted Registry or for private
|
||||||
|
registries.
|
||||||
|
|
||||||
|
## Understand trust in Docker
|
||||||
|
|
||||||
|
Content trust allows operations with a remote Docker registry to enforce
|
||||||
|
client-side signing and verification of image tags. Content trust provides the
|
||||||
|
ability to use digital signatures for data sent to and received from remote
|
||||||
|
Docker registries. These signatures allow client-side verification of the
|
||||||
|
integrity and publisher of specific image tags.
|
||||||
|
|
||||||
|
Currently, content trust is disabled by default. You must enabled it by setting
|
||||||
|
the `DOCKER_CONTENT_TRUST` environment variable.
|
||||||
|
|
||||||
|
Once content trust is enabled, image publishers can sign their images. Image consumers can
|
||||||
|
ensure that the images they use are signed. publishers and consumers can be
|
||||||
|
individuals alone or in organizations. Docker's content trust supports users and
|
||||||
|
automated processes such as builds.
|
||||||
|
|
||||||
|
### Image tags and content trust
|
||||||
|
|
||||||
|
An individual image record has the following identifier:
|
||||||
|
|
||||||
|
```
|
||||||
|
[REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]
|
||||||
|
```
|
||||||
|
|
||||||
|
A particular image `REPOSITORY` can have multiple tags. For example, `latest` and
|
||||||
|
`3.1.2` are both tags on the `mongo` image. An image publisher can build an image
|
||||||
|
and tag combination many times changing the image with each build.
|
||||||
|
|
||||||
|
Content trust is associated with the `TAG` portion of an image. Each image
|
||||||
|
repository has a set of keys that image publishers use to sign an image tag.
|
||||||
|
Image publishers have discretion on which tags they sign.
|
||||||
|
|
||||||
|
An image repository can contain an image with one tag that is signed and another
|
||||||
|
tag that is not. For example, consider [the Mongo image
|
||||||
|
repository](https://hub.docker.com/r/library/mongo/tags/). The `latest`
|
||||||
|
tag could be unsigned while the `3.1.6` tag could be signed. It is the
|
||||||
|
responsibility of the image publisher to decide if an image tag is signed or
|
||||||
|
not. In this representation, some image tags are signed, others are not:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Publishers can choose to sign a specific tag or not. As a result, the content of
|
||||||
|
an unsigned tag and that of a signed tag with the same name may not match. For
|
||||||
|
example, a publisher can push a tagged image `someimage:latest` and sign it.
|
||||||
|
Later, the same publisher can push an unsigned `someimage:latest` image. This second
|
||||||
|
push replaces the last unsigned tag `latest` but does not affect the signed `latest` version.
|
||||||
|
The ability to choose which tags they can sign, allows publishers to iterate over
|
||||||
|
the unsigned version of an image before officially signing it.
|
||||||
|
|
||||||
|
Image consumers can enable content trust to ensure that images they use were
|
||||||
|
signed. If a consumer enables content trust, they can only pull, run, or build
|
||||||
|
with trusted images. Enabling content trust is like wearing a pair of
|
||||||
|
rose-colored glasses. Consumers "see" only signed images tags and the less
|
||||||
|
desirable, unsigned image tags are "invisible" to them.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To the consumer who does not enabled content trust, nothing about how they
|
||||||
|
work with Docker images changes. Every image is visible regardless of whether it
|
||||||
|
is signed or not.
|
||||||
|
|
||||||
|
|
||||||
|
### Content trust operations and keys
|
||||||
|
|
||||||
|
When content trust is enabled, `docker` CLI commands that operate on tagged images must
|
||||||
|
either have content signatures or explicit content hashes. The commands that
|
||||||
|
operate with content trust are:
|
||||||
|
|
||||||
|
* `push`
|
||||||
|
* `build`
|
||||||
|
* `create`
|
||||||
|
* `pull`
|
||||||
|
* `run`
|
||||||
|
|
||||||
|
For example, with content trust enabled a `docker pull someimage:latest` only
|
||||||
|
succeeds if `someimage:latest` is signed. However, an operation with an explicit
|
||||||
|
content hash always succeeds as long as the hash exists:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker pull someimage@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
|
||||||
|
```
|
||||||
|
|
||||||
|
Trust for an image tag is managed through the use of signing keys. Docker's content
|
||||||
|
trust makes use four different keys:
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| offline key | Root of content trust for a image tag. When content trust is enabled, you create the offline key once. |
|
||||||
|
| target and snapshot | These two keys are known together as the "tagging" key. When content trust is enabled, you create this key when you add a new image repository. If you have the offline key, you can export the tagging key and allow other publishers to sign the image tags. |
|
||||||
|
| timestamp | This key applies to a repository. It allows Docker repositories to have freshness security guarantees without requiring periodic content refreshes on the client's side. |
|
||||||
|
|
||||||
|
With the exception of the timestamp, all the keys are generated and stored locally
|
||||||
|
client-side. The timestamp is safely generated and stored in a signing server that
|
||||||
|
is deployed alongside the Docker registry. All keys are generated in a backend
|
||||||
|
service that isn't directly exposed to the internet and are encrypted at rest.
|
||||||
|
|
||||||
|
The following image depicts the various signing keys and their relationships:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
>**WARNING**: Loss of the offline key is **very difficult** to recover from.
|
||||||
|
>Correcting this loss requires intervention from [Docker
|
||||||
|
>Support](https://support.docker.com) to reset the repository state. This loss
|
||||||
|
>also requires **manual intervention** from every consumer that used a signed
|
||||||
|
>tag from this repository prior to the loss.
|
||||||
|
|
||||||
|
You should backup the offline key somewhere safe. Given that it is only required
|
||||||
|
to create new repositories, it is a good idea to store it offline. Make sure you
|
||||||
|
read [Manage keys for content trust](/security/trust/trust_key_mng) information
|
||||||
|
for details on creating, securing, and backing up your keys.
|
||||||
|
|
||||||
|
## Survey of typical content trust operations
|
||||||
|
|
||||||
|
This section surveys the typical trusted operations users perform with Docker
|
||||||
|
images.
|
||||||
|
|
||||||
|
### Enable content trust
|
||||||
|
|
||||||
|
Enable content trust by setting the `DOCKER_CONTENT_TRUST` environment variable.
|
||||||
|
Enabling per-shell is useful because you can have one shell configured for
|
||||||
|
trusted operations and another terminal shell for untrusted operations. You can
|
||||||
|
also add this declaration to your shell profile to have it turned on always by
|
||||||
|
default.
|
||||||
|
|
||||||
|
To enable content trust in a `bash` shell enter the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DOCKER_CONTENT_TRUST=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Once set, each of the "tag" operations require key for trusted tag. All of these
|
||||||
|
commands also support the `--disable-content-trust` flag. This flag allows
|
||||||
|
publishers to run individual operations on tagged images without content trust on an
|
||||||
|
as-needed basis.
|
||||||
|
|
||||||
|
|
||||||
|
### Push trusted content
|
||||||
|
|
||||||
|
To create signed content for a specific image tag, simply enable content trust and push
|
||||||
|
a tagged image. If this is the first time you have pushed an image using content trust
|
||||||
|
on your system, the session looks like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker push docker/trusttest:latest
|
||||||
|
The push refers to a repository [docker.io/docker/trusttest] (len: 1)
|
||||||
|
9a61b6b1315e: Image already exists
|
||||||
|
902b87aaaec9: Image already exists
|
||||||
|
latest: digest: sha256:d02adacee0ac7a5be140adb94fa1dae64f4e71a68696e7f8e7cbf9db8dd49418 size: 3220
|
||||||
|
Signing and pushing trust metadata
|
||||||
|
You are about to create a new offline signing key passphrase. This passphrase
|
||||||
|
will be used to protect the most sensitive key in your signing system. Please
|
||||||
|
choose a long, complex passphrase and be careful to keep the password and the
|
||||||
|
key file itself secure and backed up. It is highly recommended that you use a
|
||||||
|
password manager to generate the passphrase and keep it safe. There will be no
|
||||||
|
way to recover this key. You can find the key in your config directory.
|
||||||
|
Enter passphrase for new offline key with id a1d96fb:
|
||||||
|
Repeat passphrase for new offline key with id a1d96fb:
|
||||||
|
Enter passphrase for new tagging key with id docker.io/docker/trusttest (3a932f1):
|
||||||
|
Repeat passphrase for new tagging key with id docker.io/docker/trusttest (3a932f1):
|
||||||
|
Finished initializing "docker.io/docker/trusttest"
|
||||||
|
```
|
||||||
|
When you push your first tagged image with content trust enabled, the `docker` client
|
||||||
|
recognizes this is your first push and:
|
||||||
|
|
||||||
|
- alerts you that it will create a new offline key
|
||||||
|
- requests a passphrase for the key
|
||||||
|
- generates an offline key in the `~/.docker/trust` directory
|
||||||
|
- generates a tagging key for in the `~/.docker/trust` directory
|
||||||
|
|
||||||
|
The passphrase you chose for both the offline key and your content key-pair should
|
||||||
|
be randomly generated and stored in a *password manager*.
|
||||||
|
|
||||||
|
It is important to note, if you had left off the `latest` tag, content trust is skipped.
|
||||||
|
This is true even if content trust is enabled and even if this is your first push.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker push docker/trusttest
|
||||||
|
The push refers to a repository [docker.io/docker/trusttest] (len: 1)
|
||||||
|
9a61b6b1315e: Image successfully pushed
|
||||||
|
902b87aaaec9: Image successfully pushed
|
||||||
|
latest: digest: sha256:a9a9c4402604b703bed1c847f6d85faac97686e48c579bd9c3b0fa6694a398fc size: 3220
|
||||||
|
No tag specified, skipping trust metadata push
|
||||||
|
```
|
||||||
|
|
||||||
|
It is skipped because as the message states, you did not supply an image `TAG`
|
||||||
|
value. In Docker content trust, signatures are associated with tags.
|
||||||
|
|
||||||
|
Once you have an offline key on your system, subsequent images repositories
|
||||||
|
you create can use that same offline key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker push docker.io/docker/seaside:latest
|
||||||
|
The push refers to a repository [docker.io/docker/seaside] (len: 1)
|
||||||
|
a9539b34a6ab: Image successfully pushed
|
||||||
|
b3dbab3810fc: Image successfully pushed
|
||||||
|
latest: digest: sha256:d2ba1e603661a59940bfad7072eba698b79a8b20ccbb4e3bfb6f9e367ea43939 size: 3346
|
||||||
|
Signing and pushing trust metadata
|
||||||
|
Enter key passphrase for offline key with id a1d96fb:
|
||||||
|
Enter passphrase for new tagging key with id docker.io/docker/seaside (bb045e3):
|
||||||
|
Repeat passphrase for new tagging key with id docker.io/docker/seaside (bb045e3):
|
||||||
|
Finished initializing "docker.io/docker/seaside"
|
||||||
|
```
|
||||||
|
|
||||||
|
The new image has its own tagging key and timestamp key. The `latest` tag is signed with both of
|
||||||
|
these.
|
||||||
|
|
||||||
|
|
||||||
|
### Pull image content
|
||||||
|
|
||||||
|
A common way to consume an image is to `pull` it. With content trust enabled, the Docker
|
||||||
|
client only allows `docker pull` to retrieve signed images.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker pull docker/seaside
|
||||||
|
Using default tag: latest
|
||||||
|
Pull (1 of 1): docker/trusttest:latest@sha256:d149ab53f871
|
||||||
|
...
|
||||||
|
Tagging docker/trusttest@sha256:d149ab53f871 as docker/trusttest:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
The `seaside:latest` image is signed. In the following example, the command does not specify a tag, so the system uses
|
||||||
|
the `latest` tag by default again and the `docker/cliffs:latest` tag is not signed.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker pull docker/cliffs
|
||||||
|
Using default tag: latest
|
||||||
|
no trust data available
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the tag `docker/cliffs:latest` is not trusted, the `pull` fails.
|
||||||
|
|
||||||
|
|
||||||
|
### Disable content trust for specific operations
|
||||||
|
|
||||||
|
A user that wants to disable content trust for a particular operation can use the
|
||||||
|
`--disable-content-trust` flag. **Warning: this flag disables content trust for
|
||||||
|
this operation**. With this flag, Docker will ignore content-trust and allow all
|
||||||
|
operations to be done without verifying any signatures. If we wanted the
|
||||||
|
previous untrusted build to succeed we could do:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat Dockerfile
|
||||||
|
FROM docker/trusttest:notrust
|
||||||
|
RUN echo
|
||||||
|
$ docker build --disable-content-trust -t docker/trusttest:testing .
|
||||||
|
Sending build context to Docker daemon 42.84 MB
|
||||||
|
...
|
||||||
|
Successfully built f21b872447dc
|
||||||
|
```
|
||||||
|
|
||||||
|
The same is true for all the other commands, such as `pull` and `push`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker pull --disable-content-trust docker/trusttest:untrusted
|
||||||
|
...
|
||||||
|
$ docker push --disable-content-trust docker/trusttest:untrusted
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related information
|
||||||
|
|
||||||
|
* [Manage keys for content trust](/security/trust/trust_key_mng)
|
||||||
|
* [Automation with content trust](/security/trust/trust_automation)
|
||||||
|
* [Play in a content trust sandbox](/security/trust/trust_sandbox)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Use trusted images"
|
||||||
|
description = "Use trusted images"
|
||||||
|
keywords = ["trust, security, docker, index"]
|
||||||
|
[menu.main]
|
||||||
|
identifier="smn_content_trust"
|
||||||
|
parent= "mn_docker_hub"
|
||||||
|
weight=4
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Use trusted images
|
||||||
|
|
||||||
|
The following topics are available:
|
||||||
|
|
||||||
|
* [Content trust in Docker](/security/trust/content_trust)
|
||||||
|
* [Manage keys for content trust](/security/trust/trust_key_mng)
|
||||||
|
* [Automation with content trust](/security/trust/trust_automation)
|
||||||
|
* [Play in a content trust sandbox](/security/trust/trust_sandbox)
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Automation with content trust"
|
||||||
|
description = "Automating content push pulls with trust"
|
||||||
|
keywords = ["trust, security, docker, documentation, automation"]
|
||||||
|
[menu.main]
|
||||||
|
parent= "smn_content_trust"
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Automation with content trust
|
||||||
|
|
||||||
|
Your automation systems that pull or build images can also work with trust. Any automation environment must set `DOCKER_TRUST_ENABLED` either manually or in in a scripted fashion before processing images.
|
||||||
|
|
||||||
|
## Bypass requests for passphrases
|
||||||
|
|
||||||
|
To allow tools to wrap docker and push trusted content, there are two
|
||||||
|
environment variables that allow you to provide the passphrases without an
|
||||||
|
expect script, or typing them in:
|
||||||
|
|
||||||
|
- `DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE`
|
||||||
|
- `DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE`
|
||||||
|
|
||||||
|
Docker attempts to use the contents of these environment variables as passphrase
|
||||||
|
for the keys. For example, an image publisher can export the repository `target`
|
||||||
|
and `snapshot` passphrases:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ export DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE="u7pEQcGoebUHm6LHe6"
|
||||||
|
$ export DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE="l7pEQcTKJjUHm6Lpe4"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, when pushing a new tag the Docker client does not request these values but signs automatically:
|
||||||
|
|
||||||
|
``bash
|
||||||
|
$ docker push docker/trusttest:latest
|
||||||
|
The push refers to a repository [docker.io/docker/trusttest] (len: 1)
|
||||||
|
a9539b34a6ab: Image already exists
|
||||||
|
b3dbab3810fc: Image already exists
|
||||||
|
latest: digest: sha256:d149ab53f871 size: 3355
|
||||||
|
Signing and pushing trust metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building with content trust
|
||||||
|
|
||||||
|
You can also build with content trust. Before running the `docker build` command, you should set the environment variable `DOCKER_CONTENT_TRUST` either manually or in in a scripted fashion. Consider the simple Dockerfile below.
|
||||||
|
|
||||||
|
```Dockerfilea
|
||||||
|
FROM docker/trusttest:latest
|
||||||
|
RUN echo
|
||||||
|
```
|
||||||
|
|
||||||
|
The `FROM` tag is pulling a signed image. You cannot build an image that has a
|
||||||
|
`FROM` that is not either present locally or signed. Given that content trust
|
||||||
|
data exists for the tag `latest`, the following build should succeed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker build -t docker/trusttest:testing .
|
||||||
|
Using default tag: latest
|
||||||
|
latest: Pulling from docker/trusttest
|
||||||
|
|
||||||
|
b3dbab3810fc: Pull complete
|
||||||
|
a9539b34a6ab: Pull complete
|
||||||
|
Digest: sha256:d149ab53f871
|
||||||
|
```
|
||||||
|
|
||||||
|
If content trust is enabled, building from a Dockerfile that relies on tag without trust data, causes the build command to fail:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker build -t docker/trusttest:testing .
|
||||||
|
unable to process Dockerfile: No trust data for notrust
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related information
|
||||||
|
|
||||||
|
* [Content trust in Docker](/security/trust/content_trust)
|
||||||
|
* [Manage keys for content trust](/security/trust/trust_key_mng)
|
||||||
|
* [Play in a content trust sandbox](/security/trust/trust_sandbox)
|
||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Manage keys for content trust"
|
||||||
|
description = "Manage keys for content trust"
|
||||||
|
keywords = ["trust, security, root, keys, repository"]
|
||||||
|
[menu.main]
|
||||||
|
parent= "smn_content_trust"
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Manage keys for content trust
|
||||||
|
|
||||||
|
Trust for an image tag is managed through the use of keys. Docker's content
|
||||||
|
trust makes use four different keys:
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| offline key | Root of content trust for a image tag. When content trust is enabled, you create the offline key once. |
|
||||||
|
| target and snapshot | These two keys are known together as the "tagging" key. When content trust is enabled, you create this key when you add a new image repository. If you have the offline key, you can export the tagging key and allow other publishers to sign the image tags. |
|
||||||
|
| timestamp | This key applies to a repository. It allows Docker repositories to have freshness security guarantees without requiring periodic content refreshes on the client's side. |
|
||||||
|
|
||||||
|
With the exception of the timestamp, all the keys are generated and stored locally
|
||||||
|
client-side. The timestamp is safely generated and stored in a signing server that
|
||||||
|
is deployed alongside the Docker registry. All keys are generated in a backend
|
||||||
|
service that isn't directly exposed to the internet and are encrypted at rest.
|
||||||
|
|
||||||
|
## Choosing a passphrase
|
||||||
|
|
||||||
|
The passphrases you chose for both the offline key and your tagging key should
|
||||||
|
be randomly generated and stored in a password manager. Having the tagging key
|
||||||
|
allow users to sign image tags on a repository. Passphrases are used to encrypt
|
||||||
|
your keys at rest and ensures that a lost laptop or an unintended backup doesn't
|
||||||
|
put the private key material at risk.
|
||||||
|
|
||||||
|
## Back up your keys
|
||||||
|
|
||||||
|
All the Docker trust keys are stored encrypted using the passphrase you provide
|
||||||
|
on creation. Even so, you should still take care of the location where you back them up.
|
||||||
|
Good practice is to create two encrypted USB keys.
|
||||||
|
|
||||||
|
It is very important that you backup your keys to a safe, secure location. Loss
|
||||||
|
of the tagging key is recoverable; loss of the offline key is not.
|
||||||
|
|
||||||
|
The Docker client stores the keys in the `~/.docker/trust/private` directory.
|
||||||
|
Before backing them up, you should `tar` them into an archive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ tar -zcvf private_keys_backup.tar.gz ~/.docker/trust/private
|
||||||
|
$ chmod 600 private_keys_backup.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lost keys
|
||||||
|
|
||||||
|
If a publisher loses keys it means losing the ability to sign trusted content for
|
||||||
|
your repositories. If you lose a key, contact [Docker
|
||||||
|
Support](https://support.docker.com) (support@docker.com) to reset the repository
|
||||||
|
state.
|
||||||
|
|
||||||
|
This loss also requires **manual intervention** from every consumer that pulled
|
||||||
|
the tagged image prior to the loss. Image consumers would get an error for
|
||||||
|
content that they already downloaded:
|
||||||
|
|
||||||
|
```
|
||||||
|
could not validate the path to a trusted root: failed to validate data with current trusted certificates
|
||||||
|
```
|
||||||
|
|
||||||
|
To correct this, they need to download a new image tag with that is signed with
|
||||||
|
the new key.
|
||||||
|
|
||||||
|
## Related information
|
||||||
|
|
||||||
|
* [Content trust in Docker](/security/trust/content_trust)
|
||||||
|
* [Automation with content trust](/security/trust/trust_automation)
|
||||||
|
* [Play in a content trust sandbox](/security/trust/trust_sandbox)
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Play in a content trust sandbox"
|
||||||
|
description = "Play in a trust sandbox"
|
||||||
|
keywords = ["trust, security, root, keys, repository, sandbox"]
|
||||||
|
[menu.main]
|
||||||
|
parent= "smn_content_trust"
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Play in a content trust sandbox
|
||||||
|
|
||||||
|
This page explains how to set up and use a sandbox for experimenting with trust.
|
||||||
|
The sandbox allows you to configure and try trust operations locally without
|
||||||
|
impacting your production images.
|
||||||
|
|
||||||
|
Before working through this sandbox, you should have read through the [trust
|
||||||
|
overview](content_trust.md).
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
These instructions assume you are running in Linux or Mac OS X. You can run
|
||||||
|
this sandbox on a local machine or on a virtual machine. You will need to
|
||||||
|
have `sudo` privileges on your local machine or in the VM.
|
||||||
|
|
||||||
|
This sandbox requires you to install two Docker tools: Docker Engine and Docker
|
||||||
|
Compose. To install the Docker Engine, choose from the [list of supported
|
||||||
|
platforms]({{< relref "installation.md" >}}). To install Docker Compose, see the
|
||||||
|
[detailed instructions here]({{< relref "compose/install" >}}).
|
||||||
|
|
||||||
|
Finally, you'll need to have `git` installed on your local system or VM.
|
||||||
|
|
||||||
|
## What is in the sandbox?
|
||||||
|
|
||||||
|
If you are just using trust out-of-the-box you only need your Docker Engine
|
||||||
|
client and access to Docker's own public hub. The sandbox mimics a
|
||||||
|
production trust environment, and requires these additional components:
|
||||||
|
|
||||||
|
| Container | Description |
|
||||||
|
|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| nostarysandbox | A container with the latest version of Docker Engine and with some preconfigured certifications. This is your sandbox where you can use the `docker` client to test trust operations. |
|
||||||
|
| Registry server | A local registry service. |
|
||||||
|
| Notary server | The service that does all the heavy-lifting of managing trust |
|
||||||
|
| Notary signer | A service that ensures that your keys are secure. |
|
||||||
|
| MySQL | The database where all of the trust information will be stored |
|
||||||
|
|
||||||
|
The sandbox uses the Docker daemon on your local system. Within the `nostarysandbox`
|
||||||
|
you interact with a local registry rather than the public Docker Hub. This means
|
||||||
|
your everyday image repositories are not used. They are protected while you play.
|
||||||
|
|
||||||
|
When you play in the sandbox, you'll also create root and tagging keys. The
|
||||||
|
sandbox is configured to store all the keys and files inside the `notarysandbox`
|
||||||
|
container. Since the keys you create in the sandbox are for play only,
|
||||||
|
destroying the container destroys them as well.
|
||||||
|
|
||||||
|
|
||||||
|
## Build the sandbox
|
||||||
|
|
||||||
|
In this section, you build the Docker components for your trust sandbox. If you
|
||||||
|
work exclusively with the Docker Hub, you would not need with these components.
|
||||||
|
They are built into the Docker Hub for you. For the sandbox, however, you must
|
||||||
|
build your own entire, mock production environment and registry.
|
||||||
|
|
||||||
|
### Configure /etc/hosts
|
||||||
|
|
||||||
|
The sandbox' `notaryserver` and `sandboxregistry` run on your local server. The
|
||||||
|
client inside the `notarysandbox` container connects to them over your network.
|
||||||
|
So, you'll need an entry for both the servers in your local `/etc/hosts` file.
|
||||||
|
|
||||||
|
1. Add an entry for the `notaryserver` to `/etc/hosts`.
|
||||||
|
|
||||||
|
$ sudo sh -c 'echo "127.0.0.1 notaryserver" >> /etc/hosts'
|
||||||
|
|
||||||
|
2. Add an entry for the `sandboxregistry` to `/etc/hosts`.
|
||||||
|
|
||||||
|
$ sudo sh -c 'echo "127.0.0.1 sandboxregistry" >> /etc/hosts'
|
||||||
|
|
||||||
|
|
||||||
|
### Build the notarytest image
|
||||||
|
|
||||||
|
1. Create a `notarytest` directory on your system.
|
||||||
|
|
||||||
|
$ mkdir notarysandbox
|
||||||
|
|
||||||
|
2. Change into your `notarysandbox` directory.
|
||||||
|
|
||||||
|
$ cd notarysandbox
|
||||||
|
|
||||||
|
3. Create a `notarytest` directory then change into that.
|
||||||
|
|
||||||
|
$ mkdir notarytest
|
||||||
|
$ cd nostarytest
|
||||||
|
|
||||||
|
4. Create a filed called `Dockerfile` with your favorite editor.
|
||||||
|
|
||||||
|
5. Add the following to the new file.
|
||||||
|
|
||||||
|
FROM debian:jessie
|
||||||
|
|
||||||
|
ADD https://master.dockerproject.org/linux/amd64/docker /usr/bin/docker
|
||||||
|
RUN chmod +x /usr/bin/docker \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
tree \
|
||||||
|
vim \
|
||||||
|
git \
|
||||||
|
ca-certificates \
|
||||||
|
--no-install-recommends
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
RUN git clone -b trust-sandbox https://github.com/docker/notary.git
|
||||||
|
RUN cp /root/notary/fixtures/root-ca.crt /usr/local/share/ca-certificates/root-ca.crt
|
||||||
|
RUN update-ca-certificates
|
||||||
|
|
||||||
|
ENTRYPOINT ["bash"]
|
||||||
|
|
||||||
|
6. Save and close the file.
|
||||||
|
|
||||||
|
7. Build the testing container.
|
||||||
|
|
||||||
|
$ docker build -t nostarysandbox .
|
||||||
|
Sending build context to Docker daemon 2.048 kB
|
||||||
|
Step 0 : FROM debian:jessie
|
||||||
|
...
|
||||||
|
Successfully built 5683f17e9d72
|
||||||
|
|
||||||
|
|
||||||
|
### Build and start up the trust servers
|
||||||
|
|
||||||
|
In this step, you get the source code for your notary and registry services.
|
||||||
|
Then, you'll use Docker Compose to build and start them on your local system.
|
||||||
|
|
||||||
|
1. Change to back to the root of your `notarysandbox` directory.
|
||||||
|
|
||||||
|
$ cd notarysandbox
|
||||||
|
|
||||||
|
2. Clone the `notary` project.
|
||||||
|
|
||||||
|
$ git clone -b trust-sandbox https://github.com/docker/notary.git
|
||||||
|
|
||||||
|
3. Clone the `distribution` project.
|
||||||
|
|
||||||
|
$ git clone https://github.com/docker/distribution.git
|
||||||
|
|
||||||
|
4. Change to the `notary` project directory.
|
||||||
|
|
||||||
|
$ cd notary
|
||||||
|
|
||||||
|
The directory contains a `docker-compose` file that you'll use to run a
|
||||||
|
notary server together with a notary signer and the corresponding MySQL
|
||||||
|
databases. The databases store the trust information for an image.
|
||||||
|
|
||||||
|
5. Build the server images.
|
||||||
|
|
||||||
|
$ docker-compose build
|
||||||
|
|
||||||
|
The first time you run this, the build takes some time.
|
||||||
|
|
||||||
|
6. Run the server containers on your local system.
|
||||||
|
|
||||||
|
$ docker-compose up -d
|
||||||
|
|
||||||
|
Once the trust services are up, you'll setup a local version of the Docker
|
||||||
|
Registry v2.
|
||||||
|
|
||||||
|
7. Change to the `nostarysandbox/distribution` directory.
|
||||||
|
|
||||||
|
8. Build the `sandboxregistry` server.
|
||||||
|
|
||||||
|
$ docker build -t sandboxregistry .
|
||||||
|
|
||||||
|
9. Start the `sandboxregistry` server running.
|
||||||
|
|
||||||
|
$ docker run -p 5000:5000 --name sandboxregistry sandboxregistry &
|
||||||
|
|
||||||
|
## Playing in the sandbox
|
||||||
|
|
||||||
|
Now that everything is setup, you can go into your `nostarysandbox` container and
|
||||||
|
start testing Docker content trust.
|
||||||
|
|
||||||
|
|
||||||
|
### Start the notarysandbox container
|
||||||
|
|
||||||
|
In this procedure, you start the `notarysandbox` and link it to the running
|
||||||
|
`notary_notaryserver_1` and `sandboxregistry` containers. The links allow
|
||||||
|
communication among the containers.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock --link notary_notaryserver_1:notaryserver --link sandboxregistry:sandboxregistry nostarysandbox
|
||||||
|
root@0710762bb59a:/#
|
||||||
|
```
|
||||||
|
|
||||||
|
Mounting the `docker.sock` gives the `nostarysandbox` access to the `docker`
|
||||||
|
deamon on your host, while storing all the keys and files inside the sandbox
|
||||||
|
container. When you destroy the container, you destroy the "play" keys.
|
||||||
|
|
||||||
|
### Test some trust operations
|
||||||
|
|
||||||
|
Now, you'll pull some images.
|
||||||
|
|
||||||
|
1. Download a `docker` image to test with.
|
||||||
|
|
||||||
|
# docker pull docker/trusttest
|
||||||
|
docker pull docker/trusttest
|
||||||
|
Using default tag: latest
|
||||||
|
latest: Pulling from docker/trusttest
|
||||||
|
|
||||||
|
b3dbab3810fc: Pull complete
|
||||||
|
a9539b34a6ab: Pull complete
|
||||||
|
Digest: sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
|
||||||
|
Status: Downloaded newer image for docker/trusttest:latest
|
||||||
|
|
||||||
|
2. Tag it to be pushed to our sandbox registry:
|
||||||
|
|
||||||
|
# docker tag docker/trusttest sandboxregistry:5000/test/trusttest:latest
|
||||||
|
|
||||||
|
3. Enable content trust.
|
||||||
|
|
||||||
|
# export DOCKER_CONTENT_TRUST=1
|
||||||
|
|
||||||
|
4. Identify the trust server.
|
||||||
|
|
||||||
|
# export DOCKER_CONTENT_TRUST_SERVER=https://notaryserver:4443
|
||||||
|
|
||||||
|
This step is only necessary because the sandbox is using its own server.
|
||||||
|
Normally, if you are using the Docker Public Hub this step isn't necessary.
|
||||||
|
|
||||||
|
5. Pull the test image.
|
||||||
|
|
||||||
|
# docker pull sandboxregistry:5000/test/trusttest
|
||||||
|
Using default tag: latest
|
||||||
|
no trust data available
|
||||||
|
|
||||||
|
You see an error, because this content doesn't exist on the `sandboxregistry` yet.
|
||||||
|
|
||||||
|
6. Push the trusted image.
|
||||||
|
|
||||||
|
# docker push sandboxregistry:5000/test/trusttest:latest
|
||||||
|
The push refers to a repository [sandboxregistry:5000/test/trusttest] (len: 1)
|
||||||
|
a9539b34a6ab: Image successfully pushed
|
||||||
|
b3dbab3810fc: Image successfully pushed
|
||||||
|
latest: digest: sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c size: 3348
|
||||||
|
Signing and pushing trust metadata
|
||||||
|
You are about to create a new root signing key passphrase. This passphrase
|
||||||
|
will be used to protect the most sensitive key in your signing system. Please
|
||||||
|
choose a long, complex passphrase and be careful to keep the password and the
|
||||||
|
key file itself secure and backed up. It is highly recommended that you use a
|
||||||
|
password manager to generate the passphrase and keep it safe. There will be no
|
||||||
|
way to recover this key. You can find the key in your config directory.
|
||||||
|
Enter passphrase for new offline key with id 8c69e04:
|
||||||
|
Repeat passphrase for new offline key with id 8c69e04:
|
||||||
|
Enter passphrase for new tagging key with id sandboxregistry:5000/test/trusttest (93c362a):
|
||||||
|
Repeat passphrase for new tagging key with id sandboxregistry:5000/test/trusttest (93c362a):
|
||||||
|
Finished initializing "sandboxregistry:5000/test/trusttest"
|
||||||
|
latest: digest: sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a size: 3355
|
||||||
|
Signing and pushing trust metadata
|
||||||
|
|
||||||
|
7. Try pulling the image you just pushed:
|
||||||
|
|
||||||
|
# docker pull sandboxregistry:5000/test/trusttest
|
||||||
|
Using default tag: latest
|
||||||
|
Pull (1 of 1): sandboxregistry:5000/test/trusttest:latest@sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c
|
||||||
|
sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c: Pulling from test/trusttest
|
||||||
|
b3dbab3810fc: Already exists
|
||||||
|
a9539b34a6ab: Already exists
|
||||||
|
Digest: sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c
|
||||||
|
Status: Downloaded newer image for sandboxregistry:5000/test/trusttest@sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c
|
||||||
|
Tagging sandboxregistry:5000/test/trusttest@sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c as sandboxregistry:5000/test/trusttest:latest
|
||||||
|
|
||||||
|
|
||||||
|
### Test with malicious images
|
||||||
|
|
||||||
|
What happens when data is corrupted and you try to pull it when trust is
|
||||||
|
enabled? In this section, you go into the `sandboxregistry` and tamper with some
|
||||||
|
data. Then, you try and pull it.
|
||||||
|
|
||||||
|
1. Leave the sandbox container running.
|
||||||
|
|
||||||
|
2. Open a new bash terminal from your host into the `sandboxregistry`.
|
||||||
|
|
||||||
|
$ docker exec -it sandboxregistry bash
|
||||||
|
296db6068327#
|
||||||
|
|
||||||
|
3. Change into the registry storage.
|
||||||
|
|
||||||
|
You'll need to provide the `sha` you received when you pushed the image.
|
||||||
|
|
||||||
|
# cd /var/lib/registry/docker/registry/v2/blobs/sha256/aa/aac0c133338db2b18ff054943cee3267fe50c75cdee969aed88b1992539ed042
|
||||||
|
|
||||||
|
4. Add malicious data to one of the trusttest layers:
|
||||||
|
|
||||||
|
# echo "Malicious data" > data
|
||||||
|
|
||||||
|
5. Got back to your sandbox terminal.
|
||||||
|
|
||||||
|
6. List the trusttest image.
|
||||||
|
|
||||||
|
# docker images | grep trusttest
|
||||||
|
docker/trusttest latest a9539b34a6ab 7 weeks ago 5.025 MB
|
||||||
|
sandboxregistry:5000/test/trusttest latest a9539b34a6ab 7 weeks ago 5.025 MB
|
||||||
|
sandboxregistry:5000/test/trusttest <none> a9539b34a6ab 7 weeks ago 5.025 MB
|
||||||
|
|
||||||
|
7. Remove the `trusttest:latest` image.
|
||||||
|
|
||||||
|
# docker rmi -f a9539b34a6ab
|
||||||
|
Untagged: docker/trusttest:latest
|
||||||
|
Untagged: sandboxregistry:5000/test/trusttest:latest
|
||||||
|
Untagged: sandboxregistry:5000/test/trusttest@sha256:1d871dcb16805f0604f10d31260e79c22070b35abc71a3d1e7ee54f1042c8c7c
|
||||||
|
Deleted: a9539b34a6aba01d3942605dfe09ab821cd66abf3cf07755b0681f25ad81f675
|
||||||
|
Deleted: b3dbab3810fc299c21f0894d39a7952b363f14520c2f3d13443c669b63b6aa20
|
||||||
|
|
||||||
|
8. Pull the image again.
|
||||||
|
|
||||||
|
# docker pull sandboxregistry:5000/test/trusttest
|
||||||
|
Using default tag: latest
|
||||||
|
...
|
||||||
|
b3dbab3810fc: Verifying Checksum
|
||||||
|
a9539b34a6ab: Pulling fs layer
|
||||||
|
filesystem layer verification failed for digest sha256:aac0c133338db2b18ff054943cee3267fe50c75cdee969aed88b1992539ed042
|
||||||
|
|
||||||
|
You'll see the the pull did not complete because the trust system was
|
||||||
|
unable to verify the image.
|
||||||
|
|
||||||
|
## More play in the sandbox
|
||||||
|
|
||||||
|
Now, that you have a full Docker content trust sandbox on your local system,
|
||||||
|
feel free to play with it and see how it behaves. If you find any security
|
||||||
|
issues with Docker, feel free to send us an email at <security@docker.com>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ Let's create a directory and a `Dockerfile` first.
|
|||||||
$ cd sinatra
|
$ cd sinatra
|
||||||
$ touch Dockerfile
|
$ touch Dockerfile
|
||||||
|
|
||||||
If you are using Boot2Docker on Windows, you may access your host
|
If you are using Docker Machine on Windows, you may access your host
|
||||||
directory by `cd` to `/c/Users/your_user_name`.
|
directory by `cd` to `/c/Users/your_user_name`.
|
||||||
|
|
||||||
Each instruction creates a new layer of the image. Let's look at a simple
|
Each instruction creates a new layer of the image. Let's look at a simple
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ parent = "smn_applied"
|
|||||||
Docker allows you to run applications inside containers. Running an
|
Docker allows you to run applications inside containers. Running an
|
||||||
application inside a container takes a single command: `docker run`.
|
application inside a container takes a single command: `docker run`.
|
||||||
|
|
||||||
> **Note:** if you are using a remote Docker daemon, such as Boot2Docker,
|
>**Note**: Depending on your Docker system configuration, you may be required to
|
||||||
> then _do not_ type the `sudo` before the `docker` commands shown in the
|
>preface each `docker` command on this page with `sudo`. To avoid this behavior,
|
||||||
> documentation's examples.
|
>your system administrator can create a Unix group called `docker` and add users
|
||||||
|
>to it.
|
||||||
|
|
||||||
## Hello world
|
## Hello world
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user