Docker 容器

2017/11/22 docker

容器是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)可以全部清理掉.

Search

    Table of Contents