Docker 2.0(进阶篇)
前言:
在上一篇文章 Docker 1.0(入门篇)中已经介绍了Docker的基本组成和相关命令,仔细阅读和实践之后,能够掌握基础的容器搭建和使用Docker。
本篇文章是Docker的进阶篇,主要是对Docker的一些原理进行深入,想要熟练的掌握Docker就必须从底层入手,透过表象看本质。
1 Docker组成再深入
1.1 镜像(image)
镜像(Image)就是一堆只读层(read-only layer)的统一视角:

右边的图我们看到了多个只读层,它们叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(运行Docker的机器)的文件系统上访问到。联合文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。
1.2 镜像层(image-layer)
为了将零星的数据整合起来,提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。

元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。
除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。
1.3 容器(Container)
容器(container)的定义和镜像(image)很相似,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

容器 = 镜像 + 读写层,并且容器的定义并没有提及是否要运行容器。
1.4 联合文件系统(UnionFS)
- 联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
- 特性:一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统。联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
2 命令再深入
-
docker create [image-id]
docker create 命令为指定的镜像(image)添加了一个可读写层,构成了一个新的容器,但这个容器并没有运行。 -
docker start [container-id]
Docker start 命令为容器文件系统创建了一个进程隔离空间,每一个容器只能够有一个进程隔离空间。 -
docker run [image-id]
docker run = docker create + docker start

从图片可以看出,docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节。
-
docker rm [container-id]
docker rm命令会移除构成容器的可读写层。注意,这个命令只能对非运行态容器执行。 -
docker commit [container-id]
docker commit -m "描述信息" -a "作者" 容器名 目标镜像名:[tag] # 编辑容器后提交容器成为一个新镜像
docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。

示例:
[root@flamingo ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 2 months ago 141MB
tomcat 9.0 b8e65a4d736d 3 months ago 680MB
centos latest 5d0da3dc9764 6 months ago 231MB
portainer/portainer latest 580c0e4e98b0 12 months ago 79.1MB
[root@flamingo ~]# docker commit -a "Satoris" -m "nginx-new" 6471a8b5c1f9 nginx001:1.0
sha256:e574e9492d0a3361d449b7ebd90dc557540377412486f7824fd1765f3569a53a
[root@flamingo ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx001 1.0 e574e9492d0a 3 seconds ago 141MB
nginx latest 605c77e624dd 2 months ago 141MB
tomcat 9.0 b8e65a4d736d 3 months ago 680MB
centos latest 5d0da3dc9764 6 months ago 231MB
portainer/portainer latest 580c0e4e98b0 12 months ago 79.1MB
3 容器数据卷(volume)
3.1 数据卷简介
我们知道,一个运行的容器有一个或多个只读层和一个读写层。在容器运行过程中,若产生了一些重要的数据或是更改了一些文件,这些更改我们应该怎么保存呢?容器关闭或重启,这些数据不受影响;但删除Docker容器,则数据将会全部丢失。除此之外也还有其他的一些问题。
存在的问题:
- 存储于联合文件系统中,不易于宿主机访问
- 容器间数据共享不便
为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供以下便利。
特点:
- volume在容器创建时就会初始化,在容器运行时就可以使用其中的文件。
- volume能在不同的容器之间共享和重用。
- 对volume中数据的操作会马上生效。
- 对volume中数据的操作不会影响到镜像本身
- volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除。
3.2 数据卷的使用
3.2.1 创建和管理卷
创建卷:
[root@flamingo ~]# docker volume create my_vol
my_vol
列出卷:
[root@flamingo ~]# docker volume ls
DRIVER VOLUME NAME
local my_vol
检查卷:
[root@flamingo ~]# docker volume inspect my_vol
[
{
"CreatedAt": "2022-03-27T11:01:08+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my_vol/_data",
"Name": "my_vol",
"Options": {},
"Scope": "local"
}
]
删除卷:
[root@flamingo ~]# docker volume rm my_vol
my_vol
docker volume prune # 删除所有未使用的卷
3.2.2 挂载命令
-v or --mount
一般来说,--mount更明确和详细。最大的区别是-v语法将所有选项组合在一个字段中,而--mount 语法将它们分开。
-
-vor--volume:由三个字段组成,由冒号字符 (:) 分隔。字段必须按正确的顺序排列,每个字段的含义不是很明显。
- 在命名卷的情况下,第一个字段是卷的名称,并且在给定的主机上是唯一的。对于匿名卷,省略第一个字段。
- 第二个字段是文件或目录在容器中挂载的路径。
- 第三个字段是可选的,是以逗号分隔的选项列表,例如ro
-
--mount:由多个键值对组成,以逗号分隔,每个由一个
<key>=<value>元组组成。--mount语法比-vor更冗长,但键的--volume顺序并不重要,标志的值更容易理解。
3.2.3 启动一个带卷的容器
指定挂载目录
docker run -it -v 主机内目录:容器内目录 镜像名/id
- 启动指定目录挂载的容器
[root@flamingo ~]# docker run -d -it -p 8000:80 -v /home/test:/home --name nginx-vol nginx /bin/bash
89db802feefd6c4f629f826a07aa615c2fda0f7af40fba4b94254c3f4750c966
-d # 后台运行
-it # 交互方式
-p # 指定端口
-v # 指定目录挂载 宿主目录:容器目录
--name # 指定名称
[root@flamingo ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89db802feefd nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8000->80/tcp, :::8000->80/tcp nginx-vol
- 进入到容器 nginx-vol:
[root@flamingo ~]# docker exec -it nginx-vol /bin/bash
root@89db802feefd:/# cd /home/
root@89db802feefd:/home# ls
root@89db802feefd:/home# # /home目录下面没有文件
- 此时在容器的/home目录下面创建文件:
root@89db802feefd:/home# touch demo.txt
root@89db802feefd:/home# ls
demo.txt
- 在宿主机上的/home/test/查看文件:
[root@flamingo ~]# cd /home/test/
[root@flamingo test]# ls
demo.txt # 在容器中创建的文件,已经保存到了宿主机目录,即使删除容器,该文件并不会被删除
- 查看nginx-vol 元数据,验证卷是否已正确创建和安装。查找Mounts部分:
"Mounts": [
{
"Type": "bind",
"Source": "/home/test",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
具名挂载
docker run -d -v 卷名:容器内目录 镜像名/id
- 启动具名挂载容器
[root@flamingo ~]# docker run -d -it -v volume-first:/home --name centos-vol centos /bin/bash
7c98a1bc320d5611fb72a45b1a63c3cb3bf566e67434f9961bcc040ccd492087
[root@flamingo ~]# docker volume ls
DRIVER VOLUME NAME
local volume-first # 可以看到卷名
- 查看挂载卷的详细信息
[root@flamingo ~]# docker volume inspect volume-first
[
{
"CreatedAt": "2022-03-27T15:38:24+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/volume-first/_data",
"Name": "volume-first",
"Options": null,
"Scope": "local"
}
]
- 验证卷是否已正确创建和安装
# docker inspect centos-vol
"Mounts": [
{
"Type": "volume",
"Name": "volume-first",
"Source": "/var/lib/docker/volumes/volume-first/_data",
"Destination": "/home",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
匿名挂载
docker run -d -v 容器内目录 镜像名/id
- 创建匿名挂载的容器
[root@flamingo ~]# docker run -d -it -v /home --name centos-vol-no centos /bin/bash
5313a430293926464cce1581f68d1f2184738d404cb845cbb01c35a61eb2f7f8
[root@flamingo ~]# docker volume ls
DRIVER VOLUME NAME
local 014936960fad48a4b3a82438078163220de615b437f9f72f5f8faf7685b26a06 # 匿名挂载
local volume-first #具名挂载
- 查看容器元数据 mount部分
[root@flamingo ~]# docker inspect centos-vol-no
"Mounts": [
{
"Type": "volume",
"Name": "014936960fad48a4b3a82438078163220de615b437f9f72f5f8faf7685b26a06",
"Source": "/var/lib/docker/volumes/014936960fad48a4b3a82438078163220de615b437f9f72f5f8faf7685b26a06/_data",
"Destination": "/home",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
备注:
所有的卷,都可以在宿主机/var/lib/docker/volumes 查看,同样产生的相关数据也存在相应卷目录下的_data文件中。
[root@flamingo ~]# cd /var/lib/docker/volumes/
[root@flamingo volumes]# ls
014936960fad48a4b3a82438078163220de615b437f9f72f5f8faf7685b26a06 metadata.db
backingFsBlockDev volume-first
4 DockerFile
4.1 Dockerfile简介:
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
4.2 使用Dockerfile
4.2.1 基本语法
| 指令 | 解释 |
|---|---|
| FROM | 构建镜像基于哪一个镜像 |
| MAINTAINER | 镜像维护者的姓名和邮箱地址 |
| RUN | 构建镜像时运行的指令 |
| CMD | 运行容器时执行的shell命令 |
| VOLUME | 指定容器挂载点到宿主机自动生成的目录或其他容器 |
| WORKDIR | 为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录 |
| EXPOSE | 声明容器的服务端口(仅仅是声明) |
| ENV | 设置容器环境变量 |
| ADD | 拷贝文件或目录到容器中,如果是URL或压缩包便会自动下载或自动解压 |
| ENTRYPOINT | 运行容器时执行的shell命令 |
4.2.2 定制镜像
建立Dockerfile文件,并输入相关指令。
这里创建一个nginx镜像,并创建一个html文件。
[root@flamingo ~]# mkdir Dockerfile
[root@flamingo ~]# cd Dockerfile/
[root@flamingo Dockerfile]# vim Dockerfile
[root@flamingo Dockerfile]# cat Dockerfile
FROM nginx
RUN echo '这是一个本地构建的ningx镜像' > /usr/share/nginx/html/index.html
4.2.3 构建镜像
在 Dockerfile 文件的存放目录下,执行构建动作。
[root@flamingo Dockerfile]# docker build -f Dockerfile -t nginx-div:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM nginx
---> 605c77e624dd
Step 2/2 : RUN echo '这是一个本地建立的ngin镜像!' > /usr/share/nginx/html/index.html
---> Running in f0834e92dba4
Removing intermediate container f0834e92dba4
---> 8929f71dffd4
Successfully built 8929f71dffd4
Successfully tagged nginx-div:1.0
-f # 指定文件,如果文件名称是`Dockerfile` 就可以不指定。
-t # 镜像的名字及标签
查看当前构建的镜像
[root@flamingo Dockerfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-div 1.0 8929f71dffd4 3 minutes ago 141MB
tomcat 9.0 b8e65a4d736d 3 months ago 680MB
centos latest 5d0da3dc9764 6 months ago 231MB
运行构建的镜像
[root@flamingo ~]# docker run -d -p 8000:80 nginx-div:1.0
7ba341c219536d0fc62a75801c67be03a20d561721ec44227958baf12cc6c68d
浏览器访问 http://116.62.141.109:8000/

发现页面乱码。查看docker容器编码格式:执行locale命令;可以看到当前编码格式为POSIX,而这种编码格式不支持中文
root@7ba341c21953:/# locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
解决办法:locale -a查看容器所有语言环境
root@7ba341c21953:/# locale -a
C
C.UTF-8
POSIX
C.UTF-8可以支持中文,只需要把容器编码设置为C.UTF-8即可
修改Dockerfile
在Dockerfile中添加一行
ENV LANG C.UTF-8
重新制作docker镜像,docker run -ti [镜像] 进入容器后执行locale发现编码格式已经被修改为C.UTF-8,之前出现的中文文件名乱码问题也没有了。
5 Docker Compose
5.1 简介
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
5.2 安装
- 安装命令
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 下载较慢
sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose # 下载很快

- 对二进制文件应用可执行权限
sudo chmod +x /usr/local/bin/docker-compose
- 测试安装
docker-compose --version

5.3 卸载
直接使用rm 命令删除相应目录下的文件
sudo rm /usr/local/bin/docker-compose
5.4 应用
5.4.1 步骤
- 使用 app定义您的应用程序的环境,Dockerfile 以便可以在任何地方复制它。
- 定义构成您的应用程序的服务,docker-compose.yml 以便它们可以在隔离环境中一起运行。
- 运行docker compose up,Docker compose 命令启动并运行您的整个应用程序。
5.4.2 实战
在此页面上,您将构建一个在 Docker Compose 上运行的简单 Python Web 应用程序。该应用程序使用 Flask 框架并在 Redis 中维护一个命中计数器。虽然示例使用 Python,但即使您不熟悉此处演示的概念,也应该可以理解。
- 为项目创建一个目录:
mkdir composetest
cd composetest

- 在项目目录中创建一个名为的文件app.py并将其粘贴到:
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
- 在项目目录中创建另一个名为的文件requirements.txt并将其粘贴以下内容:
flask
redis
- 在项目目录中,创建一个名为Dockerfile并粘贴以下内容的文件:
# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
这告诉 Docker:
- 从 Python 3.7 映像开始构建映像。
- 将工作目录设置为/code.
- 设置命令使用的环境变量flask。
- 安装 gcc 和其他依赖项
- 复制requirements.txt并安装 Python 依赖项。
- 向镜像添加元数据以描述容器正在侦听端口 5000
- 将项目中的当前目录复制.到镜像中的workdir
- 将容器的默认命令设置为flask run
- 在项目目录中创建一个名为的文件docker-compose.yml并粘贴以下内容:
version: "3.9"
services:
web:
build: .
ports:
- "8000:5000"
redis:
image: "redis:alpine"
在项目目录在一共存在四个文件:

- 使用 Compose 构建并运行您的应用程序
[root@flamingo composetest]# docker-compose up
启动时说版本不支持:

只需要将docker-compose.yml 文件中的version: "3.9" 修改为version: "2.0" 。
再次启动:

由于安装软件需等待很久时间:

最后还是启动失败,因为有些源是国外的

查阅资料之后,说将Dockefile修改:
# 修改:
RUN apk add --no-cache gcc musl-dev linux-headers
# 为:
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
再次重新启动,成功运行:

- 查看运行结果:
在浏览器中输入 http://116.62.141.109:8000/ 以查看正在运行的应用程序。刷新之后,次数增加。

参考连接:http://merrigrove.blogspot.com/2015/10/visualizing-docker-containers-and-images.html

浙公网安备 33010602011771号