# Docker的容器、镜像与数据卷

# Docker概述

# Docker为什么出现

一款产品从应用到上线,需要配置负复杂的运行环境,需要考虑不同版本环境的兼容,而且不能跨平台,移植到另一台服务器又得重新配置一遍。

Docker通过镜像将应用程序所需的系统环境进行打包,解决环境配置的诸多问题。

Docker将应用程序运行在容器上,而容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。“一次封装、到处运行”。

在容器技术出现之前,业界网红是虚拟机。虚拟机的代表,是VMWareOpenStack。虚拟机技术是虚拟出一套硬件之后,在其上运行一个完整的操作系统,在操作系统上再运行所需应用。

Docker和虚拟机的区别?

  • 虚拟机占用资源多,冗余步骤多,启动慢
  • 容器内的应用直接运行于宿主机内核,容器没有自己的内核,也没有硬件虚拟,因此更为轻便。
  • 每个容器相互隔离,每个容器有自己的文件系统,容器间进程不会互相影响,能区分计算资源。

# Docker能干嘛

  • 更快的应用交付和部署
  • 更便捷的升级和扩容
  • 更简单的系统运维
  • 更高效的计算资源利用

# Docker安装

# Centos7

这里使用了腾讯云个人用户1个月试用的云服务器

官网地址 (opens new window)

安装:

# step 1: 安装必要的一些系统工具  
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加添加阿里云镜像
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
sudo yum makecache fast  # 生成缓存
sudo yum -y install docker-ce
# Step 5: 开启Docker服务
sudo service docker start
# Step 6: 检查是否安装成功
docker version

卸载:

sudo yum remove docker-ce
sudo rm -rf /var/lib/docker

说明:

  • yum-utils管理yum配置和存储库,device-mapper-persistent-data用于逻辑卷管理,lvm2用于动态调整磁盘分区大小

# 腾讯云镜像加速

容器镜像服务个人版使用说明 (opens new window)

docker login ccr.ccs.tencentyun.com --username=xxxxxxxxxxx

执行以下命令,打开 /etc/docker/daemon.json 配置文件。

vim /etc/docker/daemon.json

i 切换至编辑模式,添加以下内容,并保存。

{
   "registry-mirrors": [
   "https://mirror.ccs.tencentyun.com"
  ]
}

执行以下命令,重启 Docker 即可。示例命令以 CentOS 7 为例。

sudo systemctl restart docker

查看镜像仓库地址

sudo docker info

# Docker镜像

# 镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

# 镜像加载原理

UnionFS(联合文件系统):

  • Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
  • Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
  • 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录.

Docker镜像加载原理:

  • docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS
  • bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux启动时,bootfs被加载到内存,提供启动内核所需的文件。启动完成后,系统会从bootfs切换到rootfs,并卸载bootfs。
  • rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

平时安装虚拟机和centos都是好几个G,为什么Docker这里只需要200M?

  • 对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就好了。对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。

什么是bootfs和rootfs?

  • bootfs Linux 操作系统启动时最早加载的文件系统,通常包含内核映像(Kernel Image)和初始的 RAM 文件系统。
  • rootfs 是 Linux 系统的根文件系统,包含了操作系统运行所需的所有文件。它包括 /bin/lib/etc/dev 等目录,以及应用程序和用户数据。

# 分层理解

[root@VM-20-7-centos home]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
e4fff0779e6d: Already exists  # 重复的层不会再下载
d1dde3db2ec5: Pull complete
1d321a003dde: Pull complete
d65aedb2f012: Pull complete
4018f93716a2: Pull complete
b0967b02e8cf: Pull complete
4f4fb700ef54: Pull complete
d288b86f5d06: Pull complete
Digest: sha256:878983f8f5045b28384fc300268cec62bca3b14d5e1a448bec21f28cfcc7bf78
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

思考:为什么Docker镜像要采用这种分层的结构呢? 最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机 只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服 务了,而且镜像的每一层都可以被共享。

所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之 上,创建新的镜像层。

镜像分层

Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统 一的文件系统。

镜像的修改层

这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新 镜像层添加到镜像当中。

存储引擎:

  • Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
  • Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层。

下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。

镜像统一视图

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!

这一层就是我们的容器层,容器层之下都叫镜像层。

# 镜像commit

docker commit 提交容器成为一个新的本地镜像

# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]

实战测试:官方的tomcat的webapps目录下是空的,我们可以自己制作一个镜像,让它包含初始内容

# 启动默认的tomcat
docker run -d --name tomcat01 -p 4396:8080 tomcat:9.0

# 进入容器
docker exec -it tomcat01 /bin/bash

# 复制webapps
cp -r webapps.dist/* webapps

# 退出
exit

# 重启tomcat
docker restart tomcat01

# commit 提交镜像
[root@VM-20-7-centos home]# docker commit -m="add webapps" -a="huyadish" 6525ee04867f tomcat02:1.0
sha256:eea1dcc18db2ebe7b6d88abac28e793d8df457deb76fb99473b97053f28d66cb

# 查看tomcat02比tomcat 大了一点
[root@VM-20-7-centos home]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
tomcat02              1.0       eea1dcc18db2   26 seconds ago   468MB
tomcat                9.0       90717097970a   12 days ago      463MB

# 启动新的tomcat
[root@VM-20-7-centos home]# docker run -d -p 4396:8080 tomcat02:1.0

学习方式思考:先简单理解概念,再动手实践,最后实践和理论结合一次性搞定这个知识

到这里才算入门docker

# 容器数据卷

# 什么是容器数据卷

如果数据都在容器中,那么删除容器数据就会丢失!需求:数据可以持久化

MySQL,容器删了同时也删除了数据库!需求:Mysql数据可以存储在本地!

卷技术可以通过目录的挂载,将容器内目录,挂载到Linux上。多个容器之间数据可以共享!

# 使用数据卷

  • 使用命令来挂载
# docker run -v 宿主机目录:容器目录
docker run -it -v /home/test:/home centos /bin/bash

# 启动后通过docker inspect 查看容器详细信息
docker inspect e6512dddaf3f

执行之后宿主机的/home/test目录会和容器的/home目录,双向同步

即使容器关了,修改宿主机目录内容,重新打开容器,对应目录内容同样会更新

# 实战:安装MySQL

# 获取镜像
[root@VM-20-7-centos /]# docker pull mysql:8.0

# 启动mysql
-d 后台运行
-p 端口映射
-v 目录挂载  宿主机目录:容器目录  可以使用多个 -v
-e 设置环境
[root@VM-20-7-centos /]# docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql8.0 mysql:8.0

# 宿主机产生了对应目录
[root@VM-20-7-centos /]# ls /home/mysql
conf  data

# 启动成功后开启3306端口,并通过远程navicat连接
[root@VM-20-7-centos /]# ufw allow 3306

# 查看宿主机目录
[root@VM-20-7-centos /]# cd /home/mysql/data
[root@VM-20-7-centos data]# ls
auto.cnf       binlog.index  client-cert.pem    #ib_16384_1.dblwr  ibtmp1        mysql       performance_schema  server-cert.pem  undo_001
binlog.000001  ca-key.pem    client-key.pem     ib_buffer_pool     #innodb_redo  mysql.ibd   private_key.pem     server-key.pem   undo_002
binlog.000002  ca.pem        #ib_16384_0.dblwr  ibdata1            #innodb_temp  mysql.sock  public_key.pem      sys
[root@VM-20-7-centos data]#

# navicat中创建test数据库

# 再次查看 发现多了test数据库
[root@VM-20-7-centos data]# ls
auto.cnf       binlog.index  client-cert.pem    #ib_16384_1.dblwr  ibtmp1        mysql       performance_schema  server-cert.pem  test
binlog.000001  ca-key.pem    client-key.pem     ib_buffer_pool     #innodb_redo  mysql.ibd   private_key.pem     server-key.pem   undo_001
binlog.000002  ca.pem        #ib_16384_0.dblwr  ibdata1            #innodb_temp  mysql.sock  public_key.pem      sys              undo_002

假设将容器删除,本地的数据不会丢失

# 具名和匿名挂载

# 匿名挂载  -v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 查看所有的卷的情况
[root@VM-20-7-centos data]# docker volume ls
DRIVER    VOLUME NAME
local     2da55574a37b01a128232cf449171dc6c24b8467a3229c87a3f629e8e10764ab
local     615a8c9be191c64c440449cb995769c2b26118a523e572dc2e7eecdc7f85dde9

# 具名挂载 -v 卷名:容器内路径
root@VM-20-7-centos home]# docker run -P -d --name nginx02 -v volume01:/etc/nginx nginx
67726065cc8668f6c7f067955bbd14ba78908794538cfff23c452f79b3cc529c
[root@VM-20-7-centos home]#
[root@VM-20-7-centos home]#

# 可以看到我们命名的卷volume01
[root@VM-20-7-centos home]# docker volume ls
DRIVER    VOLUME NAME
local     2da55574a37b01a128232cf449171dc6c24b8467a3229c87a3f629e8e10764ab
local     615a8c9be191c64c440449cb995769c2b26118a523e572dc2e7eecdc7f85dde9
local     volume01

# 查看卷的位置 docker volume inspect 卷名
[root@VM-20-7-centos home]# docker volume inspect volume01
[
    {
        "CreatedAt": "2024-08-19T15:12:34+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/volume01/_data",
        "Name": "volume01",
        "Options": null,
        "Scope": "local"
    }
]

所有docker容器内的卷,没有指定目录的情况下都是在宿主机的/var/lib/docker/volumes/xxxxx/_data

通过具名挂载可以方便得找到卷,大多数情况使用具名挂载

拓展:

# 通过 -v 容器内路径:ro rw 改变读写权限
# ro readonly 只读
# rw readwrite 读写,默认值

# 设置了ro 容器内部就不能改变卷了,只能通过宿主机操作
docker run -P -d --name nginx02 -v volume01:/etc/nginx:ro nginx
docker run -P -d --name nginx02 -v volume01:/etc/nginx:rw nginx

# 初识DockerFile

DockerFile就是用来构建Docker镜像的脚本文件。

通过脚本可以生成镜像,镜像是分层的,脚本是一个个的命令,每个命令都是一层!

  • 编辑dockerfile
# 创建一个dockerfile文件,明明建议dockerfile

# 文件内容 指令(大写) 参数
FROM centos

VOLUME ["volume01","volume02"]

CMD echo "---end---"

CMD /bin/bash

# 这里的每个命令都是镜像的一层
  • 使用dockerfile生成镜像
[root@VM-20-7-centos docker-test-volume]# docker build -f dockerfile1  -t huyadish/centos:1.0 .
[+] Building 0.2s (5/5) FINISHED                  
 => [internal] load build definition from dockerfile1
 => => transferring dockerfile: 117B
 => [internal] load metadata for docker.io/library/centos:latest
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [1/1] FROM docker.io/library/centos:latest
 => exporting to image
 => => exporting layers
 => => writing image sha256:2771b5374df0a3d1fec5d343805c3291ee35f76cf68d2b731a421cb308eae93f
 => => naming to docker.io/huyadish/centos:1.0
 
 # 查看生成的镜像
[root@VM-20-7-centos docker-test-volume]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
centos                latest    5d0da3dc9764   2 years ago     231MB
huyadish/centos       1.0       2771b5374df0   2 years ago     231MB
  • 使用自定义镜像运行容器
[root@VM-20-7-centos volumes]# docker run -it --name mycentos 2771b5374df0 /bin/bash
# 可以看到容器已自动生成了数据卷 volume01  volume02
[root@6a9e1fc9d112 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
# 创建文件
[root@6a9e1fc9d112 /]# cd volume01/
[root@6a9e1fc9d112 volume01]# touch container.txt
[root@6a9e1fc9d112 volume01]# ls
container.txt
# 退出
[root@6a9e1fc9d112 volume01]# exit
exit

# 查看启动的容器
[root@VM-20-7-centos volumes]# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                     PORTS     NAMES
6a9e1fc9d112   2771b5374df0   "/bin/bash"   8 minutes ago   Exited (0) 6 minutes ago             mycentos
# 拖过容器ID查看 详细信息
[root@VM-20-7-centos volumes]# docker inspect 6a9e1fc9d112

# 可以看到volume01 的在宿主机的地址 /var/lib/docker/volumes/b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f/_data

# 进入数据卷默认路径
[root@VM-20-7-centos volumes]# cd /var/lib/docker/volumes/

# 进入volume01 路径
[root@VM-20-7-centos volumes]# cd b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f/
[root@VM-20-7-centos b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f]# cd _data/
# 可以看到创建的测试文件
[root@VM-20-7-centos _data]# ls
container.txt

# 数据卷容器

多个mysql 同步数据

# 启动centos01
[root@VM-20-7-centos _data]# docker run -it --name centos01 huyadish/centos:1.0 /bin/bash
[root@2b8a31b8f8ee /]#

# 启动centos02 通过--volumes-from 将数据卷绑定到centos01
[root@VM-20-7-centos _data]# docker run -it --name centos02 --volumes-from centos01  huyadish/centos:1.0 /bin/bash

# 在外部查看运行状况
[root@VM-20-7-centos ~]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED              STATUS              PORTS     NAMES
2d381d6ee79c   huyadish/centos:1.0   "/bin/sh -c /bin/bash"   34 seconds ago       Up 33 seconds                 centos02
2b8a31b8f8ee   huyadish/centos:1.0   "/bin/bash"              About a minute ago   Up About a minute             centos01

# 进入centos01
[root@VM-20-7-centos ~]# docker attach 2b8a31b8f8ee
[root@2b8a31b8f8ee /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
[root@2b8a31b8f8ee /]# cd volume01
[root@2b8a31b8f8ee volume01]# ls

# 创建测试文件
[root@2b8a31b8f8ee volume01]# touch rong.java
[root@2b8a31b8f8ee volume01]# ls
rong.java

# 进入centos02
[root@2d381d6ee79c /]# cd volume01
[root@2d381d6ee79c volume01]# ls
rong.java

因为centos01和centos02的数据卷对应宿主机的同一个位置

centos01和centos02对宿主机的数据卷是共享拷贝机制,即使删除centos01,centos02的数据依然在

Last Updated: 11/18/2024, 4:01:47 PM