docker 入门

avatar

概述

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

优点

  1. 简化程序:

Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的 任务,在Docker容器的处理下,只需要数秒就能完成。

  1. 避免选择恐惧症:

如果你有选择恐惧症,还是资深患者。Docker 帮你 打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。

  1. 节省开支:

一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。

应用场景

  • Web 应用的自动化打包和发布。
  • 自动化测试和持续集成、发布。
  • 在服务型环境中部署和调整数据库或其他的后台应用。
  • 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。

架构

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
容器与镜像的关系类似于面向对象编程中的对象与类。

Docker 面向
容器 对象
镜像

avatar

相关链接

Title Des
Docker 镜像(Images) Docker 镜像是用于创建 Docker 容器的模板
Docker 容器(Container) 容器是独立运行的一个或一组应用
Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api) 与 Docker 的守护进程通信
Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器
Docker 仓库(Registry) Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure

安装

Ubuntu 安装

首先要确认你的 Ubuntu 版本是否符合安装 Docker 的前提条件。如果没有问题,你可以通过下边的方式来安装 Docker :

  1. 使用具有sudo权限的用户来登录你的Ubuntu。
  2. 查看你是否安装了wget
1
2
3
4
5
6
$ which wget

如果wget没有安装,先升级包管理器,然后再安装它。

$ sudo apt-get update
$ sudo apt-get install wget
  1. 获取最新版本的 Docker 安装包
1
$ wget -qO- https://get.docker.com/ | sh
  1. 验证 Docker 是否被正确的安装
1
$ sudo docker run hello-world

上边的命令会下载一个测试镜像,并在容器内运行这个镜像。

CentOS 安装

移除旧的版本

1
2
3
4
5
6
7
8
9
10
$ sudo yum remove docker 
docker-client
docker-client-latest
docker-common
docker-latest
docker-latest-logrotate
docker-logrotate
docker-selinux
docker-engine-selinux
docker-engine

安装一些必要的系统工具:

1
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2

添加软件源信息:

1
$ sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

更新 yum 缓存:

1
$ sudo yum makecache fast

安装 Docker-ce:

1
$ sudo yum -y install docker-ce

启动 Docker 后台服务

1
$ sudo systemctl start docker

验证 Docker 是否被正确的安装

1
$ sudo docker run hello-world

上边的命令会下载一个测试镜像,并在容器内运行这个镜像。

镜像加速

鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决.
通过修改daemon配置文件/etc/docker/daemon.json来使用加速器

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

使用

镜像

在 Docker 的术语里,一个只读层被称为镜像,一个镜像是永久不会变的。

由于 Docker 使用一个统一文件系统,Docker 进程认为整个文件系统是以读写方式挂载的。 但是所有的变更都发生顶层的可写层,而下层的原始的只读镜像文件并未变化。由于镜像不 可写,所以镜像是无状态的。

每一个镜像都可能依赖于由一个或多个下层的组成的另一个镜像。我们有时说,下层那个 镜像是上层镜像的父镜像。

一个没有任何父镜像的镜像,谓之基础镜像。

列出本地镜像

使用 docker images 可以列出本地主机上的镜像

1
2
3
4
5
6
7
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
php 7.2 7cae463c729f 2 weeks ago 398MB
nginx latest 540a289bab6c 2 weeks ago 126MB
mysql 5.7 cd3ed0dfff7e 3 weeks ago 437MB
ubuntu 14.04 2c5e00d77a67 5 months ago 188MB
hello-world latest fce289e99eb9 10 months ago 1.84kB

各个选项说明

  • REPOSTITORY:表示镜像的仓库源
  • TAG:镜像的标签
  • IMAGE ID:镜像ID
  • CREATED:镜像创建时间
  • SIZE:镜像大小

查找镜像

我们可以从 Docker Hub 网站来搜索镜像,Docker Hub 网址为:https://hub.docker.com/
我们也可以使用 docker search 命令来搜索镜像

1
$ docker search ubuntu

获取镜像

当我们在本地主机上使用一个不存在的镜像时 Docker 就会自动下载这个镜像。如果我们想预先下载这个镜像,我们可以使用 docker pull 命令来下载它。

1
2
3
4
5
6
7
8
9
$ docker pull ubuntu:14.04
14.04: Pulling from library/ubuntu
a7344f52cb74: Pull complete
515c9bb51536: Pull complete
e1eabe0537eb: Pull complete
4701f1215c13: Pull complete
Digest: sha256:2f7c79927b346e436cc14c92bd4e5bd778c3bd7037f35bc639ac1589a7acfa90
Status: Downloaded newer image for ubuntu:14.04
docker.io/library/ubuntu:14.04

下载完成后,我们可以直接使用这个镜像来运行容器。

1
$ docker run -t -i ubuntu:14.04 /bin/bash

创建镜像

当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。

  1. 从已经创建的容器中更新镜像,并且提交这个镜像
  2. 使用 Dockerfile 指令来创建一个新的镜像
更新镜像

更新镜像之前,先使用下载的镜像启动容器。

1
2
$ docker run -t -i ubuntu:14.04 /bin/bash
[email protected]:/#

在运行的容器内使用 apt-get update 命令进行更新。
在完成操作之后,输入 exit 命令来退出这个容器。

此时ID为0bbebbd3e195的容器,是按我们的需求更改的容器。我们可以通过命令 docker commit 来提交容器副本。

1
2
$ docker commit -m "apt-get update" -a "Axkeson" 0bbebbd3e195 ubuntu:dev
sha256:f7ce3235203891aec7a2c130a87844c0473e2d8af08750b3883284ae2aa51b53

各个参数说明:

  • -m: 提交的描述信息
  • -a: 指定镜像作者
  • 0bbebbd3e195: 容器ID
  • ubuntu:dev: 指定要创建的目标镜像名

使用 docker images 来查看新创建的镜像

1
2
3
4
5
6
7
8
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu dev f7ce32352038 About a minute ago 188MB
php 7.2 7cae463c729f 2 weeks ago 398MB
nginx latest 540a289bab6c 2 weeks ago 126MB
mysql 5.7 cd3ed0dfff7e 3 weeks ago 437MB
ubuntu 14.04 2c5e00d77a67 5 months ago 188MB
hello-world latest fce289e99eb9 10 months ago 1.84kB

之后,可以使用新的镜像来启动容器

1
2
$ docker run -t -i ubuntu:dev /bin/bash
[email protected]:/#

使用 Dockerfile 构建镜像

使用 docker commit 来扩展一个镜像比较简单,但是不方便在一个团队中分享。我们可以使用 docker build 来创建一个新的镜像。为此,首先需要创建一个 Dockerfile,包含一些如何创建镜像的指令

新建一个目录和一个 Dockerfile

1
2
3
$ mkdir docker
$ cd docker/
$ touch Dockerfile

Dockerfile 中每一条指令都创建镜像的一层,每一个指令的前缀都必须是大写的

1
2
3
4
5
# This is a comment
FROM ubuntu:14.04
MAINTAINER Docker Axkeson <[email protected]>
RUN apt-get -qq update
EXPOSE 80

然后,我们使用 Dockerfile 文件,通过 docker build 命令来构建一个镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ docker build -t axkeson/ubuntu:dev .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:14.04
---> 2c5e00d77a67
Step 2/4 : MAINTAINER Docker Axkeson <[email protected]>
---> Running in e59c86a66883
Removing intermediate container e59c86a66883
---> a1ae3d914b38
Step 3/4 : RUN apt-get -qq update
---> Running in 0f613f00c78b
Removing intermediate container 0f613f00c78b
---> 8ad734c56442
Step 4/4 : EXPOSE 80
---> Running in 262038d95663
Removing intermediate container 262038d95663
---> a3d12b32a13d
Successfully built a3d12b32a13d
Successfully tagged axkeson/ubuntu:dev

参数说明:

  • -t :指定要创建的目标镜像名
  • . :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径

使用 docker images 查看创建的镜像已经在列表中存在,镜像ID为a3d12b32a13d,我们可以使用新的镜像来创建容器

1
2
3
4
5
6
7
8
9
10
11
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
axkeson/ubuntu dev a3d12b32a13d 3 minutes ago 202MB
ubuntu dev f7ce32352038 22 minutes ago 188MB
php 7.2 7cae463c729f 2 weeks ago 398MB
nginx latest 540a289bab6c 2 weeks ago 126MB
mysql 5.7 cd3ed0dfff7e 3 weeks ago 437MB
ubuntu 14.04 2c5e00d77a67 5 months ago 188MB
hello-world latest fce289e99eb9 10 months ago 1.84kB
$ docker run -t -i axkeson/ubuntu:dev /bin/bash
[email protected]:/#

Dockerfile 基本的语法是

  • 使用#来注释
  • FROM 指令告诉 Docker 使用哪个镜像作为基础
  • MAINTAINER 是维护者的信息
  • RUN开头的指令会在创建中运行,比如安装一个软件包,在这里使用 apt-get 来安装了一些软件
  • EXPOSE 指令来向外部开放端口
  • CMD 指令来描述容器启动后运行的程序等

上传镜像

可以通过 docker push 命令,把自己创建的镜像上传到仓库中来共享。例如,用户在 Docker Hub 上完成注册后,可以推送自己的镜像到仓库中

1
2
3
4
$ sudo docker push axkeson/ubuntu
The push refers to a repository [axkeson/ubuntu] (len: 1)
Sending image list
Pushing repository axkeson/ubuntu (3 tags)

移除本地镜像

如果要移除本地的镜像,可以使用 docker rmi 命令。注意 docker rm 命令是移除容器

1
2
3
4
$ docker rmi ubuntu:dev
Untagged: ubuntu:dev
Deleted: sha256:f7ce3235203891aec7a2c130a87844c0473e2d8af08750b3883284ae2aa51b53
Deleted: sha256:fc8ca62746e4e5f5e4eebed68ef9b89fa4648f8194b03cc81fa6d19ff06d43de

导出镜像

如果要导出镜像到本地文件,可以使用 docker save 命令

1
2
3
4
5
6
7
8
9
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
axkeson/ubuntu dev a3d12b32a13d 28 minutes ago 202MB
php 7.2 7cae463c729f 2 weeks ago 398MB
nginx latest 540a289bab6c 2 weeks ago 126MB
mysql 5.7 cd3ed0dfff7e 3 weeks ago 437MB
ubuntu 14.04 2c5e00d77a67 5 months ago 188MB
hello-world latest fce289e99eb9 10 months ago 1.84kB
$ docker save -o ubuntu_14.04.tar axkeson/ubuntu:dev

载入镜像

可以使用 docker load 从导出的本地文件中再导入到本地镜像库

1
2
3
4
5
$ docker load --input ubuntu_14.04.tar
Loaded image: axkeson/ubuntu:dev
或者
$ docker load < ubuntu_14.04.tar
Loaded image: axkeson/ubuntu:dev

容器

启动容器

基于镜像ubuntu:14.04新建一个容器并启动

下面的命令输出一个 “Hello World”,之后终止容器。

1
2
$ docker run ubuntu:14.04 /bin/echo "Hello world"
Hello world

下面的命令则启动一个 bash 终端,允许用户进行交互。

1
2
3
4
5
6
$ docker run -t -i ubuntu:14.04 /bin/bash
[email protected]:/#
[email protected]:/# pwd
/
[email protected]:/# ll
total 72

参数说明:

  • -t:让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上
  • -i:让容器的标准输入保持打开
  • -d:容器启动后会进入后台。 某些时候需要进入容器进行操作,有很多种方法,包括使用docker attach 命令或 nsenter 工具等

查看容器

使用 docker ps 可以查看我们正在运行的容器

1
2
3
4
5
$ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
ba4bed03628aa8092feb0378345050f401089b8b9e04327c95012fdff48dd166
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba4bed03628a ubuntu:14.04 "/bin/sh -c 'while t…" 3 seconds ago Up 2 seconds eager_solomon

进入容器

在使用 -d 参数时,容器启动后会进入后台。 某些时候需要进入容器进行操作,有很多种方法,包括使用docker attach 命令或 nsenter 工具等

attach 命令

docker attach 是Docker自带的命令。下面示例如何使用该命令

1
2
3
4
5
6
7
$ docker run -idt ubuntu
15e22b1c1e5345aa848a599f3ff12a9abdcaa8f294bff77ddef816ac2d398772
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15e22b1c1e53 ubuntu "/bin/bash" 6 seconds ago Up 5 seconds thirsty_almeida
$ docker attach 15e22b1c1e53
[email protected]:/#

但是使用 attach 命令有时候并不方便。当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了

nsenter 命令

TODO

终止容器

可以使用 docker stop 来终止一个运行中的容器,此外,当Docker容器中指定的应用终结时,容器也自动终止。用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。
终止状态的容器可以用 docker ps -a 命令看到。

处于终止状态的容器,可以通过 docker start 命令来重新启动。

此外,docker restart 命令会将一个运行态的容器终止,然后再重新启动它。

1
2
3
$ docker stop 206d3189e0b5
$ docker start 206d3189e0b5
$ docker restart 206d3189e0b5

移除容器

可以使用 docker rm 来删除一个处于终止状态的容器,删除容器时,容器必须是停止状态

1
$ docker rm ba4bed03628a

Volumen

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 卷会一直存在,直到没有容器使用

创建一个数据卷

在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。

下面创建一个 web 容器,并加载一个数据卷到容器的 /webapp 目录。

1
$ docker run -d -P --name web -v /webapp training/webapp python app.py

注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器

挂载一个主机目录作为数据卷

使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。

1
$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp 目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

*注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。

Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

1
$ docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py

挂载一个本地主机文件作为数据卷

-v 标记也可以从主机挂载单个文件到容器中

1
$ docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

这样就可以记录在容器输入过的命令了。

*注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed –in-place,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录