Docker 2.0(进阶篇)

前言:

在上一篇文章 Docker 1.0(入门篇)中已经介绍了Docker的基本组成和相关命令,仔细阅读和实践之后,能够掌握基础的容器搭建和使用Docker。
本篇文章是Docker的进阶篇,主要是对Docker的一些原理进行深入,想要熟练的掌握Docker就必须从底层入手,透过表象看本质。

1 Docker组成再深入

1.1 镜像(image)

镜像(Image)就是一堆只读层(read-only layer)的统一视角:

image

右边的图我们看到了多个只读层,它们叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(运行Docker的机器)的文件系统上访问到。联合文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。

1.2 镜像层(image-layer)

为了将零星的数据整合起来,提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。
image

元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。
除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。

1.3 容器(Container)

容器(container)的定义和镜像(image)很相似,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

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

image

从图片可以看出,docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节。

  • docker rm [container-id]
    docker rm命令会移除构成容器的可读写层。注意,这个命令只能对非运行态容器执行。

  • docker commit [container-id]

docker commit -m "描述信息" -a "作者" 容器名 目标镜像名:[tag]  # 编辑容器后提交容器成为一个新镜像

docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。
image

示例:

[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
  1. 启动指定目录挂载的容器
    [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
  1. 进入到容器 nginx-vol:
[root@flamingo ~]# docker exec -it nginx-vol /bin/bash
root@89db802feefd:/# cd /home/
root@89db802feefd:/home# ls
root@89db802feefd:/home#  # /home目录下面没有文件
  1. 此时在容器的/home目录下面创建文件:
root@89db802feefd:/home# touch demo.txt
root@89db802feefd:/home# ls
demo.txt
  1. 在宿主机上的/home/test/查看文件:
[root@flamingo ~]# cd /home/test/
[root@flamingo test]# ls
demo.txt  # 在容器中创建的文件,已经保存到了宿主机目录,即使删除容器,该文件并不会被删除
  1. 查看nginx-vol 元数据,验证卷是否已正确创建和安装。查找Mounts部分:
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/test",
                "Destination": "/home",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

具名挂载

docker run -d  -v 卷名:容器内目录  镜像名/id   
  1. 启动具名挂载容器
[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    # 可以看到卷名
  1. 查看挂载卷的详细信息
[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"
    }
]
  1. 验证卷是否已正确创建和安装
# 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
  1. 创建匿名挂载的容器
[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   #具名挂载

  1. 查看容器元数据 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/

image

发现页面乱码。查看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 安装

  1. 安装命令
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   # 下载很快

image

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

image

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,但即使您不熟悉此处演示的概念,也应该可以理解。

  1. 为项目创建一个目录:
 mkdir composetest
 cd composetest

image

  1. 在项目目录中创建一个名为的文件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)
  1. 在项目目录中创建另一个名为的文件requirements.txt并将其粘贴以下内容:
flask
redis
  1. 在项目目录中,创建一个名为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
  1. 在项目目录中创建一个名为的文件docker-compose.yml并粘贴以下内容:
version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

在项目目录在一共存在四个文件:
image

  1. 使用 Compose 构建并运行您的应用程序
[root@flamingo composetest]# docker-compose up

启动时说版本不支持:
image

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

再次启动:

image

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

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

image

查阅资料之后,说将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

再次重新启动,成功运行:

image

  1. 查看运行结果:

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

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

posted @ 2022-03-27 01:21  Satoris  阅读(28)  评论(0)    收藏  举报