容器是Docker 核心概念
简单的说,容器是独立运行的一个或一组应用,以及它们的运行状态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统和跑着上面的应用
启动容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一种是将在终止状态的容器重启启动。
因为Docker的容器实在太轻量级了,很多时候用户都是随时删除和新建容器。
新建并启动
所需要的命令主要为 docker run
.
例如,惜命的命令输出一个“Hello world”,之后终止容器:
$ docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world
这跟在本地直接执行/bin/echo 'hello world'
几乎感觉不出任何区别。
下面的命令则启动一个bash终端,允许用户进行交互。
$ docker run -t -i ubuntu:14.04 /bin/bash
root@akjlajladjalkj:/#
其中-t
选项是让Docker分配一个伪终端并绑定到容器的标准输入上,-i
则是让容器的标准输入保持打开。
在交互模式下,用户可以通过所创建的终端来输入命令,例如
$ docker run -t -i ubuntu:latest /bin/bash
root@c9679f0b028f:/# pwd
/
root@c9679f0b028f:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
利用docker run
来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定定向,不存在就从共有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个IP地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
###启动已经终止容器
可以利用docker start
命令,直接将一个已经终止的容器启动运行。
容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必须的。除此之外,并没有其它的资源。可以在伪终端利用ps
或者top
来查看进程信息。
root@c9679f0b028f:/# ps
PID TTY TIME CMD
1 pts/0 00:00:00 bash
12 pts/0 00:00:00 ps
可见,容器中仅运行了指定bash应用。这种特点使得Docker对资源的利用率极高,是货真价实的轻量级虚拟化。
后台运行
更多的时候,需要让Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。
此时,可以通过添加-d
参数来实现。
下面举两个例子说明一下。
如果不使用 -d
参数运行容器:
$ docker run ubuntu:14.04 /bin/sh -c "while true;do echo hello world ;sleep 1;done"
hello world
hello world
hello world
hello world
hello world
hello world
容器会把输出的结果(STDOUT)打印到宿主机上面
如果使用了-d
参数运行容器:
$ docker run -d ubuntu:14.04 /bin/sh -c "while true;do echo hello world ;sleep 1;done"
b4085ffedeaa669b0678bc6848caa690c20c30432190c9bac2fef1b7f101ea52
此时容器会在后台运行并不会把输出的结果( STDOUT)打印到宿主机上面(输出结果可以用docker logs
查看).
注: 容器是否会长久运行,是和docker run
指定的命令有关,和-d
参数无关.
使用-d
参数启动后会返回一个唯一的id,也可以通过docker ps
命令来查看容器信息.
$ docker ps
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b4085ffedeaa ubuntu:14.04 "/bin/sh -c 'while..." 2 minutes ago Up 2 minutes laughing_agnesi
获取容器的输出信息,可以通过docker logs
命令:
docker logs b4085ffedeaa
hello world
hello world
hello world
hello world
hello world
终止容器
可以使用docker stop
来终止一个运行中的容器.
此外,当Docker容器中指定的应用终结时,容器也自动终止.例如对于上一章节只启动了一个终端的容器,用户通过exit
命令退出终端时,所创建的容器立刻终止.
终止状态的容器可以用docker ps -a
命令看到,例如:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b4085ffedeaa ubuntu:14.04 "/bin/sh -c 'while..." 6 minutes ago Up 6 minutes laughing_agnesi
742163facfde ubuntu:14.04 "/bin/sh -c 'while..." 7 minutes ago Exited (0) 7 minutes ago cranky_babbage
处于终止状态的容器,可以通过docker start
命令来重新启动.
此外,docker restart
命令会将一个运行状态的容器终止,然后在重新启动它.
进入容器
在使用-d
参数时,容器启动后会进入后台.某些时候需要进入容器进行操作,有很多种方法,包括使用docker attach
命令或nsenter
工具等.
attach 命令
docker attach
是Docker自带的命令.下面实例如何使用该命令.
$ docker run -idt ubuntu
a0e87f2b6a5178862e23c26ac637ed41945da1080d92c13d4da357508ef3eeac
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0e87f2b6a51 ubuntu "/bin/bash" 20 seconds ago Up 17 seconds loving_curie
$ docker attach loving_curie
root@a0e87f2b6a51:/#
但是使用attach
命令有时候并不方便. 当多个窗口同时attach到同一个容器的时候,所有窗口都会同步显示. 当某个窗口因命令阻塞时,其他窗口也无法执行操作了.
nsenter 命令
安装
nsenter
工具在util-linux包2.23版本后包含. 如果系统中util-linux 包没有该命令,可按照下面的方法从源码安装.
$ cd /tmp/ && wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz && tar zxf util-linux-2.24.tar.gz && cd util-linux-2.24
$ ./configure --without-ncurses
$ make nsenter && sudo cp nsenter /usr/local/bin
使用
nsenter
启动一个新的shell进程(默认是/bin/bash),同时会把这个新进程切换到和目标进程相同的命名空间,这样就相当于进入容器内部.nsenter要正常工作需要有root权限.
为了连接到容器,需要找到容器的第一个进程的PID,可以通过下面命令获取:
#PID=$(docker inspect --format "" <container>)
$ PID=$(docker inspect --format "" a0e87f2b6a51)
$ echo $PID
23692
通过这个PID就可以连接到这个容器:
nsenter --target $PID --mount --uts --ipc --net --pid
如果无法通过以上命令连接到这个容器,有可能是因为宿主的默认shell在容器中并不存在,比如zsh,可以使用如下命令显示的使用bash.
nsenter --target $PID --mount --uts --ipc --net --pid -- /usr/bin/env \
> --ignore-environment HOME=/root /bin/bash --login
下面给出一个完整的例子:
$ docker run -idt ubuntu
9484c98f26a2265fc10cead1b8ba25aafaf47c571a66c5e1debc544c8d5bd896
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9484c98f26a2 ubuntu "/bin/bash" 7 seconds ago Up 3 seconds cranky_kala
$ PID=$(docker-pid 9484c98f26a2)
$ sudo nsenter -t $PID -m -u -i -n -p su - root
root@9484c98f26a2:~#
使用.bashrc_docker 更加方便:
# Some useful commands to use docker.
# Author: yeasy@github
# Created:2014-09-25
alias docker-pid="sudo docker inspect --format ''"
alias docker-ip="sudo docker inspect --format ''"
#the implementation refs from https://github.com/jpetazzo/nsenter/blob/master/docker-enter
function docker-enter() {
#if [ -e $(dirname "$0")/nsenter ]; then
#Change for centos bash running
if [ -e $(dirname '$0')/nsenter ]; then
# with boot2docker, nsenter is not in the PATH but it is in the same folder
NSENTER=$(dirname "$0")/nsenter
else
# if nsenter has already been installed with path notified, here will be clarified
NSENTER=$(which nsenter)
#NSENTER=nsenter
fi
[ -z "$NSENTER" ] && echo "WARN Cannot find nsenter" && return
if [ -z "$1" ]; then
echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]"
echo ""
echo "Enters the Docker CONTAINER and executes the specified COMMAND."
echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
else
PID=$(sudo docker inspect --format "" "$1")
if [ -z "$PID" ]; then
echo "WARN Cannot find the given container"
return
fi
shift
OPTS="--target $PID --mount --uts --ipc --net --pid"
if [ -z "$1" ]; then
# No command given.
# Use su to clear all host environment variables except for TERM,
# initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH,
# and start a login shell.
#sudo $NSENTER "$OPTS" su - root
sudo $NSENTER --target $PID --mount --uts --ipc --net --pid su - root
else
# Use env to clear all host environment variables.
echo $@
sudo $NSENTER --target $PID --mount --uts --ipc --net --pid env -i $@
fi
fi
}
将.bashrc_docker
加入到.bashrc
中
echo "[ -f ~/.bashrc_docker ] && . ~/.bashrc_docker" >> ~/.bashrc ; source ~/.bashrc
这个文件定义了很多方便使用的Docker命令,如 docker-pid 可以获取容器的pid而docker-enter
可以进入容器或直接在容器内执行命令.
$ echo $(docker-pid <container>)
$ docker-enter <container> ls
$ docker-enter 9484c98f26a2
root@9484c98f26a2:~#
导出和导入容器
导出容器
如果要导出本地某个容器,可以使用docker export
命令.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9484c98f26a2 ubuntu "/bin/bash" 3 hours ago Up 3 hours cranky_kalam
$ sudo docker export 9484c98f26a2 > ubuntu.tar
$ ll -h ubuntu.tar
-rw-r--r-- 1 root root 98M Nov 28 15:03 ubuntu.tar
这样将导出容器快照到本地文件.
导入容器快照
可以使用docker import
从容器快照文件中再导入为镜像,例如:
$ cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0
sha256:4032c07fd9fbb7e33fc072b53da5068bffabe33e7d53330bdfe703351da27aa2
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu v1.0 4032c07fd9fb 15 seconds ago 98.4MB
此外,也可以通过指定URL或者某个目录来导入,例如:
$ docker import http://example.com/exampleimage.tgz example/imagerepo
注:
用户既可以使用docker load
来导入镜像存储文件到本地镜像库,也可以使用docker imort
来导入一个容器快照到本地镜像库.这两者的去吧在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大.此外,从容器快照文件导入时可以重新制定标签等元数据信息.
删除容器
可以使用docker rm
来删除一个处于终止状态的容器,例如:
$ docker rm trusting_newton
如果要删除一个运行中的容器,可以添加-f
参数
清理所有处于终止状态的容器
用docker ps -a
命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用```docker rm $(docker ps -a -q)可以全部清理掉.