Docker 使用入门

首先 Docker 是由 go 语言编写,基于 Linux 容器技术(LXC)、Namespace、Cgroup 和 UnionFS(联合文件系统)等技术的轻量级操作系统虚拟化解决方案。

对于 docker 的概念,可以简单理解如下:

  • 镜像(Image) 类似于虚拟机的快照,它是只读,可以以镜像为模板创建容器,在容器中的更改不会影响到原镜像。实际镜像是 UnionFS 的层级文件系统。
  • 容器(Container) 类似于轻量级的虚拟机,由 docker 镜像实例化而来,docker 推荐一个容器运行一个进程,可见其轻量程度。
  • 注册服务器(Registry) 提供在线存放 docker 镜像的在线服务,可以理解为 github 所提供的 repository 作用,当我们使用 docker run 运行一个本地不存在的镜像时,默认情况下 docker 会从 docker 官方的 registry 拉取该镜像,然后创建并运行一个容器。
  • Dockerfile 可以理解为用于构建镜像的命令和设置组合

本文将从安装开始,记录docker日常使用的一些命令、问题及深一点的理解

安装

官方有非常详细的安装方法https://www.docker.com,由于某些共知原因,通常会出现各种问题,或者会很慢,这里推荐阿里云提供的 Docker CE 镜像站,安装如下:

Ubuntu 14.04 16.04 (使用 apt-get 进行安装)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# step 1: 安装必要的一些系统工具
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# step 2: 安装 GPG 证书
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# Step 3: 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# Step 4: 更新并安装 Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce

# 安装指定版本的 Docker-CE:
# Step 1: 查找 Docker-CE 的版本:
# apt-cache madison docker-ce
# docker-ce | 17.03.1~ce-0~ubuntu-xenial | http://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# docker-ce | 17.03.0~ce-0~ubuntu-xenial | http://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# Step 2: 安装指定版本的 Docker-CE: (VERSION 例如上面的 17.03.1~ce-0~ubuntu-xenial)
# sudo apt-get -y install docker-ce=[VERSION]

CentOS 7 (使用 yum 进行安装)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启 Docker 服务
sudo service docker start

# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,你可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ee.repo
# 将 [docker-ce-test] 下方的 enabled=0 修改为 enabled=1
#
# 安装指定版本的 Docker-CE:
# Step 1: 查找 Docker-CE 的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
# Loading mirror speeds from cached hostfile
# Loaded plugins: branch, fastestmirror, langpacks
# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
# Available Packages
# Step2 : 安装指定版本的 Docker-CE: (VERSION 例如上面的 17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]

验证安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ docker version
Client:
Version: 17.09.0-ce
API version: 1.32
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:42:18 2017
OS/Arch: linux/amd64

Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:40:56 2017
OS/Arch: linux/amd64
Experimental: false

最好将当前用户添加到 docker 用户组:

1
sudo usermod -aG docker your_username

然后我们再使用上阿里云的镜像加速器:

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://your_unique_id.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

注册地址:https://cr.console.aliyun.com/#/accelerator

然后运行下hello-world试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ docker run hello-world
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.
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

Share images, automate workflows, and more with a free Docker ID:
https://cloud.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/

快速入门

基本应用

查看本地镜像

1
docker image ls

搜索远程仓库中名为 centos 的镜像

1
docker search centos

从远程仓库将相应镜像拉回本地

1
docker pull centos

默认会将 docker.io 官方的最新版本的 centos 取回本地

创建容器
从刚才拉回本地的 centos 创建一个容器,并在其中启动一个 bash,让该容器在后台运行

1
docker run -itd --name centos7 -v /home/blueyi/docker:/home/docker -p 20080:80 centos /bin/bash

上面的参数解释如下:

  • -itd:是 -i -t -d 的简写。-i 表示保持打开标准输入流(stdin),无论是否连接到此容器。-t 为容器分配一个虚拟的 tty。-d 表示后台模式运行容器,即容器启动后将断开与当前终端的连接。
  • --name:这是为容器起一个名字,之后与容器的交互需要用到,当然用容器的 Id 也是可以的(就是 run 命令后那个巨长的回显)。当然,不写的话也会有默认的名字。
  • -v:表示挂载宿主主机目录到容器的目录(宿主机目录路径:容器目录路径)。当然可以设置读写属性。
  • -p:表示映射主机端口至容器端口(主机端口:容器端口)。这个参数可以重复出现,映射多个端口。
  • centos:本地镜像名称(centos),如果本地没有名为 centos 我镜像,Docker 会自动搜索并下载远程仓库中的最新镜像。
  • /bin/bash:这个是本次启动的镜像要执行的任务。这个门道比较多,我在后面会说一下。

查看运行中的容器

1
2
3
docker container ls
# 或者
docker ps

查看所有容器

1
2
3
docker container ls -a
# 或者
docker ps -a

连接容器

1
docker attach centos7

然后我们查看一下容器 centos7 的系统信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@c5eb1263ab93 /]# cat /etc/*release
CentOS Linux release 7.4.1708 (Core)
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

CentOS Linux release 7.4.1708 (Core)

查看当前容器中的运行的进程:

1
2
3
4
[root@c5eb1263ab93 /]# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 11784 2852 pts/0 Ss 03:18 0:00 /bin/bash
root 18 0.0 0.0 47456 3336 pts/0 R+ 06:24 0:00 ps -aux

从未见过如此少的一次系统运行的所有进程,后面会详细说明原因。

现在想退出容器怎么办呢,使用exit或者给个ctrl+d,但一但 exit 出来这个 bash,也就表示容器中运行的唯一进程被关闭,代表着容器中的任务运行完成,容器就会停止。

启动停止的容器

1
docker start centos7

在容器中执行命令

通过docker exec命令可以在容器中执行命令,而不用进入容器的 bash。
查看容器 centos7 中运行的进程:

1
docker exec centos7 ps -aux

启动 bash 并挂载输入输出流(前台模式):

1
docker exec -it centos7 bash

这样我们就相当于另起一个 bash,并直接登录,需要退出时正常 exit 或者ctrl+d即可。当然也可以运行一个带-d参数的相应命令,但我们通过docker attach centos7连接时,依然连接的是最初创建容器时的 bash。

停止容器

1
docker stop centos7

删除容器

1
docker rm centos7

默认只能删除停止后的容器,如果要强行删除,可以使用-f参数。

所以针对容器名的操作都可以通过CONTAINER ID来操作,CONTAINER ID使用时只要不会冲突,可以只用前几位,类似于 git 中的commit id

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0651f9e070bc ubuntu "/bin/bash" 3 hours ago Exited (127) 18 minutes ago ubuntu17
c5eb1263ab93 centos "/bin/bash" 4 hours ago Exited (137) 4 minutes ago centos7
ed454bbb94fc centos:latest "/bin/bash" 4 days ago Exited (255) 4 days ago elastic_engelbart
96e80b0dc916 hello "python app.py" 5 days ago Exited (255) 4 days ago 0.0.0.0:4000->80/tcp affectionate_beaver
2dbe5ef3a2b2 c4d750cbd468 "python app.py" 5 days ago Exited (0) 5 days ago pensive_murdock
92a611c8b2cb 05a3bd381fc2 "/hello" 5 days ago Exited (0) 5 days ago awesome_darwin
a7012bd520ef ubuntu "/bin/bash" 2 weeks ago Exited (1) 2 weeks ago nervous_curran
a182da89dbd8 c4d750cbd468 "python app.py" 3 weeks ago Exited (0) 3 weeks ago serene_thompson
d55a2ccdf7f7 53ee9f9056e8 "python app.py" 4 weeks ago Exited (0) 4 weeks ago frosty_thompson
3986f345898e 53ee9f9056e8 "python app.py" 4 weeks ago Exited (137) 4 weeks ago sleepy_roentgen
12204ac36056 53ee9f9056e8 "python app.py" 4 weeks ago Exited (0) 4 weeks ago distracted_ride
ad95e10fe6c3 ac5dec7779c5 "/bin/sh -c 'pip -..." 4 weeks ago Exited (2) 4 weeks ago thirsty_turing
b2aec3f6bcc8 352e58e1c04c "python app.py" 4 weeks ago Exited (0) 4 weeks ago nervous_mestorf
8e2ae86cb786 352e58e1c04c "python app.py" 4 weeks ago Exited (0) 4 weeks ago

我想启动最后一个容器:

1
docker start 8

因为容器 ID 中以8开始的只有这一个,并不会歧义。

强制删除刚刚启动的容器:

1
docker rm -f 8

Docker 官方 Get Started

来自官方Get Started包含了 docker 的常用使用场景下的使用方式

Dockerfile

Dockerfile 用于定义在容器运行后的内部环境应该包含哪些东西,例如下面这个用于运行 flask 程序的 python 环境的 Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

然后在requirements.txt中放入我们的依赖:

1
2
Flask
Redis

flask 的 hellowrld 的app.py代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"

html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)

创建 docker 镜像

创建名为 hello 的镜像:

1
docker build -t hello .

然后会看到一下下载和配置过程,最后有一个Successfully的提示,并且跟了一个 id 和 tag,该 id 即为该镜像的唯一标识符,同一个 Dockerfile 可以创建同一镜像 ID 但不同名的多个镜像。

查看镜像:

1
docker images # 或者 docker image ls

运行镜像:

1
docker run -p 4000:80 hello

-p为端口映射,运行成功后可以通过http://localhost:4000访问,或者你的主机 IP 跟4000的端口号。

后台运行:

1
docker run -d -p 4000:80 hello

查看运行中的容器:

1
docker container ls

停止运行中的容器:

1
docker container stop <id>

push 自己的镜像到docker repository

登录到cloud.docker.com

1
docker login

为镜像添加标签:

1
docker tag image username/repository:tag

然后 push 就可以了:

1
docker push username/repository:tag

在新环境中,从远程 pull 并且运行:

1
docker run -p 4000:80 username/repository:tag

对 docker 深一点的理解

先查看一下我当前的系统环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

$ uname -a
Linux hik 4.4.0-103-generic #126-Ubuntu SMP Mon Dec 4 16:23:28 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

现在我们尝试拉取一个官方的 centos 镜像并运行:

1
docker run -t -i centos:latest /bin/bash

一段时间下载之后会进入新镜像 centos 创建的容器,并在其中运行 bash。前面已经细解释过该命令参数的意义。

现在我们再来查看一下系统信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@ed454bbb94fc /]# cat /etc/*release
CentOS Linux release 7.4.1708 (Core)
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

CentOS Linux release 7.4.1708 (Core)

[root@ed454bbb94fc /]# uname -a
Linux ed454bbb94fc 4.4.0-103-generic #126-Ubuntu SMP Mon Dec 4 16:23:28 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

神奇的事情发生了,我们运行着的 centos 容器,竟然使用的是本地操作系统 ubuntu 的 linux 内核,所以说 docker 可以看成是超轻量级的虚拟机。事实上,它只是使用 linux 容器和命名空间等技术,帮助我们实现进程的资源隔离。下面我们做一些验证。

在 centos 容器中运行 top,可以看到进程中只有一个 top 和一个 bash 在运行。

然后我们再来查看一下宿主系统(ubuntu)中是否有 top 在运行:

1
2
ps -ef | grep top
root 12461 7786 0 09:26 pts/0 00:00:00 top

下面来自网上的一段引用:

这个 top 进程就是我们在容器中运行的 top,其实 docker 容器中运行的进程实际上就是宿主机上的进程。docker 实际上使用了命名空间(namespace)来对进程进行隔离,使不同 namespace 的进程彼此不可见,同时使用 cgroup 来对彼此隔离的进程的资源进行限制,docker 的容器(container)其实就是一个进程的容器,而并不是一个全虚拟化的操作系统,所以他不会有什么 init 进程。docker 将进程、进程所需要的操作系统、运行环境称为容器。所以它比传统的基于 hypervisor 的虚拟机拥有更高的效率,并使用更低的资源。它实际上是一个内核级别的虚拟化技术,容器还是在使用宿主机的内核,为了证实上述内容,我们可以在容器中用如下命令查看 docker 的内核版本:

你也可以使用free -hm等命令查看容器的硬件信息,发现是与宿主主机信息是一样的。

Docker 其他常用命令

仅涉及前面没有用到或者没有仔细解释的命令
命令中nameid一样,下述中的container指容器的名称或 ID,image指镜像的名称或 ID

镜像相关

删除镜像

1
docker rmi <image>

删除镜像前必须先删除容器,或者使用-f参数。

删除所有镜像

1
docker rmi $(docker images | awk '{if (NR>4) {print $3}}')

如果镜像有关系容器未删除,可以使用-f参数强制删除

查看镜像历史

1
docker image history <image>

保存和恢复镜像

应用场景,当创建自己的镜像之后希望迁移到其他机器上,除了可以通过推送到公网,然后再 pull 回来之外,还可以保存到本地文件,然后拷贝过去之后再恢复

保存和恢复镜像有2组命令:save-loadexport-import

save-load

支持一次保存多个镜像,当然使用load恢复的时候也会一次恢复多个镜像

保存镜像

1
docker save centos:latest > my_centos.tar

save命令后面也可以跟容器

恢复镜像:

1
docker load < my_centos.tar

export-import

docker export的操作对象需要是容器,而不是镜像,所以适用于制作基础镜像的场景

导出镜像:

1
docker export -o my_centos_ex.tar centos7

导入镜像:

1
docker import my_centos_ex.tar blueyi/centos7:latest

两种方式的区别

save导出的镜像tar包会包含所有历史信息和层,这样就可以支持层回滚了,但export导致的镜像tar包不包含历史信息,所以它的导出文件也会小一些,可以在导入后使用后面的镜像历史命令查询。

import导入export的镜像时支持指定镜像名

显示镜像历史

1
docker history <image>

保存容器为一个新的镜像

1
docker commit <container> <NEW_IMAGE_NAME>

容器相关

删除所有容器

1
docker rm $(docker -ps -a -q)

强制 kill 掉容器

1
docker kill 《容器名 /ID>

它与前面用过的docker stop的区别是,docker stop执行时会先向容器中 PID 为 1 的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止,如果等待时间达到设定的时间,或者默认的 10 秒,则会继续发送SIGKILL系统信号强行 kill 掉进程。如docker stop --time=20 <container_name>
docker kill类似于 Linux 系统的kill,如:docker kill --singal=SIGINT <container_name>,默认不加指定信号相当于kill -9kill -SIGKILL,强行终止进程。

后台运行一个容器,并映射端口和文件夹

前面已经使用过了,再举一例

1
docker run -idt --name debian8 -p 8080:80 -p 8022:22 -v /var/cpp:/mnt/cpp -v /home/blueyi/download:/mnt/download debian:latest /bin/bash

文件夹映射也称数据卷挂载,这样就可以使容器中使用的数据能够持久化地保存到本地

为了避免运行后它立即退出,所以打开了输入流-i,分配虚拟 tty-t,在其中执行/bin/bash

端口映射

如果只是-p 80则docker会随机从宿主主机中选择一个端口映射到该容器中的80端口

如果端口使用使用大写的 P(即-P),则 docker 会随机映射一个49000~49900的端口到内部容器开放的网络端口

端口映射也可以通过指定范围一次映射多个端口,例如-v 2000-3000:2000-3000,注意在我的测试中如果这个范围太大,比如超过10000个端口映射会导致容器启动失败

显示容器的内容的改变

1
docker diff <container>

显示容器中的进程信息

1
docker top <container>

重启容器

1
docker restart <container>

attach到容器

1
docker attach <container>

前面已经用过,这样进入容器后当需要退出时,使用exit会导致容器停止。我们可以使用快捷键:先按ctrl+p,再按ctrl+q来退出容器

连接容器

docker 的容器连接分为 2 种情况,一种是同一主机上不同容器之间的连接,使用--link的方式进行;另一种是跨主机连接,使用ambassador实现,这里只试验使用link方式连接,跨主机连接以后用到了再学习。

1
docker run --name=mysql_client1 --link=mysql_server:db -t -i mysql_client /usr/bin/mysql

表示从名为mysql_client的镜像创建一个名为mysql_client1的容器,并将其链接到 mysql_server,同时将 mysql_server的别名命名为db

容器连接后可以查看其/etc/hosts文件中对相应容器的 IP 容器名以及容器 id 进行了映射

查看容器日志

1
docker logs <container>

从容器中拷贝数据到本地

1
docker cp <container>:/etc/profile .

会将容器上的/etc/profile拷贝到当前目录,支持目录拷贝

其他

查看 docker 对象的底层信息

通过docker inspect可以查看较低层的 docker 容器以及 docker 镜像的内部信息:

1
docker inspect <image_name/container_name>

当然id也一样。

数据卷

关于数据卷前面已经多次使用,就是通过-v 宿主机文件 / 目录:容器里对应的文件 / 目录:权限参数在运行容器之前为其添加数据卷映射,容器中不存在的挂载目录会自动创建,不指定权限时默认为读写。
如果-v后面只跟一个目录,该目录将会在容器在创建,并在本地的/var/lib/docker/volumes目录下产生相应的一个对应目录,目录名随机,可以通过以下命令查看具体挂载信息及相应目录

1
docker inspect --format='{{.Mounts}}' <container>

或者使用命令:

1
docker inspect <container>  | grep volumes

注意:/var/lib/docker/volumes下的文件夹,也就是挂载的数据卷只有在以下情况才会被删除,否则该目录中会遗留很多名称很长的目录

  • docker rm -v删除容器时添加了-v选项
  • docker run --rm运行容器时添加了--rm选项

数据卷容器

如果用户需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器也是一个容器,但是它的目的是专门用来提供数据卷供其他容器挂载。

例如,创建一个数据卷容器 dbdata,并在其中创建一个数据卷挂载到 /dbdata

1
docker run -it -v /dbdata --name dbdata centos

然后,可以在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷。

例如创建 db1 和 db2 两个容器,并从 dbdata 容器挂载数据卷:

1
2
docker run -it --volumes-from dbdata --name db1 ubuntu
docker run -it --volumes-from dbdata --name db2 ubuntu

此时,容器 db1 和 db2 都挂载同一个数据卷到相同的 /dbdata 目录。三个容器任何一方在该目录下的写入,其他容器都可以看到。

可以多次使用--volumes-from参数来从多个容器挂载多个数据卷。还可以从其他已经挂载了容器卷的容器来挂载数据卷。
使用--volumes-from参数所挂载数据卷的容器自身并不需要保持在运行状态。
如果删除了挂载的容器(包括 dbdata、db1 和 db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时显式使用docker rm -v命令来指定同时删除关联的容器。

利用数据卷容器来迁移数据

可以利用数据卷容器对其中的数据卷进行备份、恢复,以实现数据的迁移。

备份

使用下面的命令来备份 dbdata 数据卷容器内的数据卷:

1
docker run --volumes-from dbdata -v $(pwd):/backup --name worker centos tar cvf /backup/backup.tar /dbdata

首先利用 centos 镜像创建了一个容器 worker。使用 –volumes-from dbdata 参数来让 worker 容器挂载 dbdata 容器的数据卷(即 dbdata 数据卷), 使用 -v $(pwd):/backup 参数来挂载本地的当前目录到 worker 容器的 /backup 目录。worker 容器启动后,使用了 tar cvf /backup/backup.tar /dbdata 命令来将 /dbdata 下内容备份为容器内的 /backup/backup.tar,即宿主主机当前目录下的 backup.tar

恢复

如果要将数据恢复到一个容器,可以按照下面的步骤操作。

首先创建一个带有数据卷的容器 dbdata2:

1
docker run -v /dbdata --name dbdata2 centos /bin/bash

然后创建另一个新的容器,挂载 dbdata2 的容器,并使用 untar 解压备份文件到所挂载的容器卷中:

1
2
3
docker run --volumes-from dbdata2 -v $(pwd):/backup --name worker centos bash
cd /dbdata
tar xvf /backup/backup.tar

其他问题

64位系统中的docker是否可以使用32位镜像

经测试是可以的,需要内核依然使用的是宿主主机的64位内核,但其中的32程序和库都可以正常使用

参考

  1. Docker CE 镜像源站
  2. Docker Get Started
  3. 其他大量互联网资源