Docker 的学习和使用

Docker 的安装 (Ubuntu)

安装 Docker 需要下面任一64位 Ubuntu 系统版本:

  • Yakkety 16.10
  • Xenial 16.04
  • Trusty 14.04

查看 Linux 内核和 gcc 版本:

1
2
3
4
> $ cat /proc/version 
>
> Linux version 4.8.0-49-generic (buildd@lcy01-26) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #52~16.04.1-Ubuntu SMP Thu Apr 20 10:55:59 UTC 2017
>

查看 Ubuntu Linux 系统版本:

1
2
3
4
5
6
7
> $ lsb_release -a
> No LSB modules are available.
> Distributor ID: Ubuntu
> Description: Ubuntu 16.04.2 LTS
> Release: 16.04
> Codename: xenial
>

Ubuntu 安装 Docker

官方网站上有各种环境下的安装指南 (https://docs.docker.com/installation/#installation) 。
通过系统自带包安装

  1. Ubuntu 14.04 版本系统中已经自带了 Docker 包,可以直接安装。
1
2
3
4
$ sudo apt-get update
$ sudo apt-get install -y docker.io
$ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
$ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io

如果使用操作系统自带包安装 Docker,目前安装的版本是比较旧的 0.9.1。 要安装更新的版本,可以通过使用 Docker 源的方式。

  1. 通过 Docker 源安装最新版本

要安装最新的 Docker 版本,首先需要安装 apt-transport-https 支持,之后通过添加源来安装。

1
2
3
4
5
6
$ sudo apt-get update 
$ sudo apt-get install apt-transport-https ca-certificates
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$ sudo apt-get update
$ sudo apt-get install lxc-docker
  1. 14.04 之前版本

如果是较低版本的 Ubuntu 系统,需要先更新内核。

1
2
3
$ sudo apt-get update
$ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring
$ sudo reboot

然后重复上面的步骤即可。
安装之后启动 Docker 服务。

1
$ sudo service docker start

CentOS 安装 Docker

Docker 配置

修改 Docker 源

/ect/docker/daemon.json

1
2
3
{
"registry-mirrors": ["https://registry.docker-cn.com","https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com","http://f1361db2.m.daocloud.io"]
}

sudo systemctl daemon-reload
sudo systemctl restart docker

Docker 修改默认存储路径

Docker 修改默认存储路径的一个方法

docker容器存放目录磁盘空间满了,转移数据修改Docker默认存储位置

停止 docker 服务

1
sudo systemctl stop docker

新建 docker 目录文件

1
sudo mkdir /data/docker

迁移 docker 文件

1
sudo mv /var/lib/docker /data/docker

修改 docker.service 文件:

1
sudo vim /usr/lib/systemd/system/docker.service

在里面的 EXECStart 的后面增加后如下:

1
--graph /data/docker \

重启 docker

1
2
3
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl start docker

查看修改结果:

1
sudo docker info | grep 'Docker Root Dir'

查看 Docker 信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看 docker 版本
$ sudo docker version

# 显示 docker 系统的信息
$ sudo docker info

# 查看 docker 状态
$ sudo systemctl status docker -l

# 刷新配置文件
$ sudo systemctl daemon-reload

# 重启 docker 服务
$ sudo systemctl restart docker

Docker 镜像

Docker 镜像就是一个只读的模板。

Docker运行容器前需要本地存在对应的镜像,如果不存在,Docker会尝试先从默认镜像仓库下载(默认使用 Docker Hub公共注册服务器中的仓库),用户也可以通过配置,使用自定义的镜像仓库。

下载镜像

启动守护进程:

1
$ sudo docker daemon

运行 以上命令如果出现一下错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
INFO[0000] API listen on /var/run/docker.sock           
INFO[0000] [graphdriver] using prior storage driver "aufs"
INFO[0001] Firewalld running: false
INFO[0001] Default bridge (docker0) is assigned with an IP address 172.17.0.1/16. Daemon option --bip can be used to set a preferred IP address
WARN[0002] Your kernel does not support swap memory limit.
INFO[0002] Loading containers: start.
.ERRO[0002] Failed to load container 23d0a208ecf8fd1bdd327f09f4b5b0130d06373f46bfa709c0f6a2e872becb25: open /var/lib/docker/containers/23d0a208ecf8fd1bdd327f09f4b5b0130d06373f46bfa709c0f6a2e872becb25/config.json: no such file or directory

INFO[0002] Loading containers: done.
INFO[0002] Daemon has completed initialization
INFO[0002] Docker daemon commit=a34a1d5 execdriver=native-0.2 graphdriver=aufs version=1.9.1
^CINFO[0123] Processing signal 'interrupt'

可以运行:

1
2
# Images, containers, volumes, or customized configuration files on your host are not automatically removed. To delete all images, containers, and volumes. 
$ sudo rm -rf /var/lib/docker

/var/lib/docker : Docker 的安装目录。

下载镜像:

1
2
3
4
5
6
7
8
9
10
$ sudo docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
aafe6b5e13de: Pull complete
0a2b43a72660: Pull complete
18bdd1e546d2: Pull complete
8198342c3e05: Pull complete
f56970a44fd4: Pull complete
Digest: sha256:f3a61450ae43896c4332bda5e78b453f4a93179045f20c8181043b26b5e79028
Status: Downloaded newer image for ubuntu:latest

命令格式为:
docker pull NAME[:TGE]
如果不显示指定 TAG,则默认选择 latest 标签,即下载仓库中最新版本的镜像。

目前 Ubuntu 的最新的镜像版本是16.04,所以以上的命令实际上相当于

1
sudo docker pull ubuntu:16.04

也相当于

1
sudo docker pull registry.hub.docker.com/ubuntu:latest

即从默认的注册服务器 registry.hub.docker.com 中的 Ubuntu 仓库中下载标记为 latest 的镜像。

下载镜像到本地后,即可随时使用该镜像了,例如利用该镜像创建一个容器,在其中运行 bash应用 :

1
2
$ sudo docker run -t -i --name ubuntu ubuntu:latest /bin/bash 
root@23d0a208ecf8:/#

-t 表示在新容器内指定一个伪终端或终端。

-i 表示允许我们对容器内的 STDIN 进行交互。

查看镜像信息

列出本地主机已有的所有镜像:

1
$ sudo docker images

搜寻镜像

使用 docker search 命令可以搜索远端仓库中共享的镜像,默认搜索 Docker Hub官方仓库中的镜像。用法为 docker search TERM ,支持的参数包括:

  • –automated=false 仅显示自动创建的镜像
  • –no-trunc=false 输入信息不截断显示
  • -s, –stars=0 指定仅显示评价为指定星级以上的镜像

例如搜索 Spring 相关镜像:

1
sudo docker search spring

删除镜像

使用 docker rmi 命令删除镜像,命令格式为 docker rmi IMAGE ,其中 IMAGE 可以是标签或者 ID。
当同一个镜像有多个标签的时候,docker rmi 命令只是删除了该镜像多个标签中的指定标签而已,并没有影响镜像文件。因此上述操作相当于只是删除了镜像的一个标签而已。但当镜像只剩下一个标签的时候就要小心了,此时再使用 docker rmi 命令会彻底删除该镜像。
当使用 docker rmi 命令后面跟上镜像的 ID(也可以是 ID 能进行区分的部分前缀串)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。
注意:当有该镜像创建的容器存在时,镜像文件默认是无法被删除的,此时可以使用参数 -f 来强行删除(不建议,存在依赖关系,强行删除可能导致一些遗留问题)。

删除所有未打 dangling 标签的镜像

1
docker rmi $(docker images -q -f dangling=true)

通过镜像的 id 来删除指定镜像

1
docker rmi <image id>

删除所有镜像

1
2
3
4
docker rmi $(docker images -q)
```

删除所有镜像

docker rmi $(docker images | grep none | awk ‘{print $3}’ | sort -r)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


#### 存出镜像

使用 `docker save` 命令导出镜像到本地文件。

#### 载入镜像

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



### Docker 容器

> Docker 利用容器来运行应用。
> 容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

#### 运行容器

```
$ sudo docker run -t -i ubuntu:16.04 /bin/bash

其中, -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
• 检查本地是否存在指定的镜像,不存在就从公有仓库下载
• 利用镜像创建并启动一个容器
• 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
• 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
• 从地址池配置一个 ip 地址给容器
• 执行用户指定的应用程序
• 执行完毕后容器被终止

-h 选项制定主机名,如果不指定 Docker 会随机分配一个字符串做主机名。

1
2
3
4
5
6
$ sudo docker run -h container -it ubuntu:16.04 /bin/bash
root@container:/#


$ sudo docker run -it ubuntu:16.04 /bin/bash
root@efa9b7b0a527:/#

守护态运行

Docker 采用了 C/S架构,包括客户端和服务端。 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者 RESTfulAPI 来进行通信。

  • Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。
  • Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。

要让 Docker 容器在后台以守护态 (Daemonized) 形式运行。此时,可以通过添加 -d 参数来实现.

容器启动后会返回一个唯一的 id。

查看容器信息

1
$ sudo docker ps

实时查看容器日志

1
docker logs -f <容器名orID>

查看容器的 root 用户名和密码

1
docker logs <容器名orID> 2>&1 | grep '^User: ' | tail -n1

因为 docker 容器启动时的 root 用户的密码是随机分配的。所以,通过这种方式就可以得到 redmine 容器的 root 用户的密码了。

启动已终止容器

使用 docker start 命令,直接将一个已经终止的容器启动运行。

终止容器

使用 docker stop 命令来终止一个运行中的容器。 当 Docker 容器中制定的应用终结时, 容器也自动终止。 终止的容器可以使用 docker ps -a 命令看到。

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

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

导出容器

导出容器到本地文件:

1
$ sudo docker export 769fc > /usr/local/ubuntu.zip

导入容器快照

从容器快照文件中再导入为镜像:

1
$ cat /usr/local/ubuntu.zip | sudo docker import - /url/local/ubuntu:v1.0

用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除容器

杀死所有正在运行的容器

1
2
3
docker kill $(docker ps -a -q)

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

删除所有已经停止的容器

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

移除所有的容器和镜像

1
docker kill $(docker ps -q) ; docker rm $(docker ps -a -q) ; docker rmi $(docker images -q -a)

Docker 仓库

仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实
际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub (https://hub.docker.com) ,存放了数量庞大的镜像供用户下载。 国内的公开
仓库包括 Docker Pool (http://www.dockerpool.com) 等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上
使用这个镜像时候,只需要从仓库上 pull 下来就可以了。
注:Docker 仓库的概念跟 Git (http://git-scm.com) 类似,注册服务器可以理解为 GitHub 这样的托管服务。

有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。docker-registry 是官方提供的工具,可以用于构建私有的镜像仓库。

数据卷

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

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

数据卷的使用,类似于Linux下对目录或者文件进行 mount 操作。

Docker 数据卷

网络配置

外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。
当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。

-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有:

  • hostPort:containerPort
  • ip:hostPort:containerPort
  • ip::containerPort
  • hostPort:containerPort

映射所有接口地址

使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行:

1
$ sudo docker run -d -p 5000:5000 training/webapp python app.py

此时默认会绑定本地所有接口上的所有地址。

映射到指定地址的指定端口

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1 :

1
$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py

映射到指定地址的任意端口

使用 ip::containerPort 绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口:

1
$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py

查看映射端口配置

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址:

1
2
$ docker port nostalgic_morse 5000
127.0.0.1:49155.

注意:

  • 容器有自己的内部网络和 ip 地址 (使用 docker inspect 可以获取所有的变量,Docker 还可以有一个可变的网络配置。)
  • -p 标记可以多次使用来绑定多个端口

例如:

1
$ sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py

容器互联

容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式。
该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。

自定义容器名称

使用 --name 标记自定义容器名称:

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

容器互联

使用 --link 参数可以让容器之间安全的进行交互, 其参数格式为 --link name:alias ,其中 name 是要链接的容器的名称, alias 是这个链接的别名.

Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的
时候并没有使用 -p 和 -P 标记,从而避免了暴露数据库端口到外部网络上。

Dockerfile

基本结构

Dockerfile由一行行命令组成,支持以 # 开头的注释行。
一般而言,Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。

指令

指令的一般格式为INSTRUCTION arguments,指令包括FROM、MAINTAINER、RUN等。下面分别介绍。

FROM

格式为 FROM imageFROM image:tag
第一条指令必须为 FROM 指令。并且在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

MAINTAINER

格式为 MAINTAINER name ,指定维护者信息。

RUN

格式为 RUN commandRUN ["executable", "param1", "param2"]
前者将在shell终端中运行命令,即 /bin/sh -c ;后者则使用 exec 执行。指定使用其他终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。

CMD

支持三种格式:

  • CMD [“executable”, “param1”, “param2”] 使用exec执行,推荐方式。
  • CMD command param1 param2 在/bin/sh中执行,提供给需要交互的应用。
  • CMD [“param1”, “param2”] 提供给ENTRYPOINT的默认参数。

指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。

EXPOSE

格式为 EXPOSE port [port ...]
告诉Docker服务端暴露的端口号,供互联系统使用。在启动容器时需要通过 -P ,Docker主机会自动分配一个端口转发到指定的端口;使用 -p 则可以具体指定哪个本地端口映射过来 (与 port 的区别是端口不发布到宿主主机,只被连接的服务访问) 。

ENV

格式为 ENV key value
指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

ADD

格式为 ADD src dest
该命令将复制指定的src到容器中的dest。其中src可以是Dockerfile所在目录的一个相对路径(文件或目录);也可以I是一个URL;还可以是一个tar文件(自动解压为目录)。

COPY

格式为 COPY src dest
复制本地主机的src(为Dockerfile所在目录的相对路径、文件或目录)为容器中的dest。目标路径不存在时,会自动创建。
当使用本地目录为源目录时,推荐使用 COPY

ENTRYPOINT

有两种格式:

  • ENTRYPOINT [“executable”, “param1”, “param2”]
  • ENTRYPOINT command param1 param2(shell中执行)

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个ENTRYPOINT时,只有最后一个生效。

VOLUME

格式为 VOLUME ["/data"]
创建一个可以从本地主机或者其他容器挂载的挂载点,一般用来存放数据库和需要保存的数据等。

USER

格式为 USER daemon
指定运行容器时的用户名或者UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户。

WORKDIR

格式为 WORKDIR /path/to/workdir
为后续的 RUNCMDENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续指令如果参数是相对路径,则会基于之前命令指定的路径。

ONBUILD

格式为 ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令。

创建镜像

编写完Dockerfile之后,可以通过 docker build 命令来创建镜像。
基本的格式为 docker build [选项] 路径 。该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有内容发送给Docker服务端,有服务端来创建镜像。因此一般建议防止Dockerfile的目录为空目录。
另外,可以通过 .dockerignore 文件(每一行添加一条匹配模式)来让Docker忽略路径下的目录和文件。
要指定镜像的标签信息,可以通过 -t 选项。
例如,指定Dockerfile所在路径为 /tmp/docker_builder/,并且希望生成镜像标签为 build_repo/first_image,可以使用下面的命令:

sudo docker build -t build_repo/first_image /tmp/docker_builder/ 

命名空间

宋宝华 - Linux namespace - Docker 背后的故事

文件系统

aufs 是一个类似于 Unionfs 的可堆叠联合文件系统。它将多个目录整合成单一的目录。ubuntu 对其有良好的支持。
vfs 是 linux 的内核中一个重要概念,这个虚拟文件系统可以让 open()、read()、write()等系统调用不用关心底层的存储介质和文件系统类型就可以工作的粘合层。

分享到:
Disqus 加载中...

如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理