# Docker 学习

参考视频:狂神说

# Docker 概述

Docker 是基于 Go 语言开发的

官网:https://www.docker.com/

文档:https://docs.docker.com/

hub:https://hub.docker.com/

# 一般的虚拟机

1647419493312

虚拟机技术的缺点:

  1. 资源占用十分多
  2. 冗余步骤多
  3. 启动很慢!

# 容器化技术

容器化技术不是模拟一个完整的操作系统

1647419459524

比较 Docker 和虚拟机技术的不同:

  • 传统虚拟机,会虚拟一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件。
  • 容器内的应用直接运行在宿主机的内容,容器第是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
  • 每个容器间是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响。

# 安装步骤

  1. 官网安装手册:https://docs.docker.com/engine/install/centos/

  2. 确定是 CentOS7 以上版本

  3. 安装 docker 依赖库:

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

  4. 添加 Docker CE 的软件源信息

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

  5. 安装 Docker CE。

    1
    2
    yum makecache fast
    yum -y install docker-ce

  6. 设置镜像仓库

    1
    2
    3
    4
    yum-config-manager \
    --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo

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

  7. 启动 Docker

    1
    systemctl start docker

  8. 测试命令

    1
    2
    3
    docker version
    docker run hello-world
    docker images

  9. 卸载

    1
    2
    3
    4
    5
    systemctl stop docker

    yum -y remove docker-ce docker-ce-cli containerd.io

    rm -rf /var/lib/docker

# 阿里云镜像加速

阿里云找到容器镜像服务 / 镜像加速器

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

# run 命令

1647433767783

# 底层原理

# Docker 是怎么工作的

Docker 是一个 Client-Server 结构的系统,Docker 守护进程运行在主机上,然后通过 Socket 连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器,是一个运行时环境,就是我们面前说到的集装箱。

1647434113254

# 为什么 Docker 比较 VM 快

  1. docker 有着比虚拟机更少的抽象层。由于 docker 不需要 Hypervisor 实现硬件资源虚拟化,运行在 docker 容器上的程序直接使用的都是实际物理机的硬件资源。因此在 cpu、内存利用率上 docker 将在效率上有明显优势。
  2. docker 利用的是宿主机的内核,而不需要 Guest OS。当新建一个容器时,docker 不需要和虚拟机一样加载操作系统内核。加载操作系统内核这个过程比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载 Guest OS,这个新建过程是分钟级别。而 docker 由于直接利用宿主机的操作系统,则省略了这个过程,因此新建一个 docker 容器只需要几秒钟

1647434712492

虚拟机容器
占用磁盘空间非常大 GB小 MB
启动速度慢,分钟级别快,秒级
运行形态运行于 Hypervisor直接运行在宿主机内核上
并发性一台宿主机上十几个,最多几十个上百
性能逊与宿主机接近本地进程
资源利用率

# Docker 常用命令

# 帮助命令

1
2
3
docker version  # 显示版本信息
docker info # 显示Docker系统信息,包括镜像和容器
docker --help # 帮助

# 镜像命令

docker image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 列出本地主机上镜像
[root@iZc3ncrhf90zylZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 c20987f18b13 2 months ago 448MB
hello-world latest feb5d9fea6a5 5 months ago 13.3kB
centos latest 5d0da3dc9764 6 months ago 231MB

# 解释
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的ID
CREATED 镜像创建时间
SIZE 镜像大小

# 同一仓库源可以有多个TAG,代表这个仓库源有不同版本,我们使用REPOSITORY:TAG 定义不同镜像,不定义镜像版本默认使用lastest镜像

# 可选项
-a 列出本地所有镜像
-q 只显示镜像id
--digests 显示镜像摘要信息

docker search

1
2
3
4
5
6
7
8
# 搜索镜像
[root@iZc3ncrhf90zylZ ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 12254 [OK]
# docker search 某个镜像名称 对应DockerHub仓库中镜像

# 可选项
--filter=stars=50 列出收藏数不小于指定值的镜像

docker pull

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 下载镜像
[root@kuangshen ~]# docker pull mysql
Using default tag: latest # 不写tag,默认是latest
latest: Pulling from library/mysql
54fec2fa59d0: Already exists # 分层下载
bcc6c6145912: Already exists
951c3d959c9d: Already exists
05de4d0e206e: Already exists
319f0394ef42: Already exists
d9185034607b: Already exists
013a9c64dadc: Already exists
42f3f7d10903: Pull complete
c4a3851d9207: Pull complete
82a1cc65c182: Pull complete
a0a6b01efa55: Pull complete
bca5ce71f9ea: Pull complete
Digest:
sha256:61a2a33f4b8b4bc93b7b6b9e65e64044aaec594809f818aeffbff69a893d1944 #
签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实位置
# 指定版本下载
[root@kuangshen ~]# docker pull mysql:5.7
....

docker rmi

1
2
3
4
# 删除镜像
docker rmi -f 镜像id # 删除单个
docker rmi -f 镜像名:tag 镜像名:tag # 删除多个
docker rmi -f $(docker images -qa) # 删除全部

# 容器命令

<font> 说明:有镜像才能创建容器,我们这里使用 centos 的镜像进行测试,就是一个虚拟的 centos</font>

新建容器并启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 命令
docker run [OPTIONS] IMAGE [COMMAND][ARG...]
# 常用参数说明
--name="Name" # 给容器指定一个名字
-d # 后台方式运行容器,并返回容器的id!
-i # 以交互模式运行容器,通过和 -t 一起使用
-t # 给容器重新分配一个终端,通常和 -i 一起使用
-P # 随机端口映射(大写)
-p # 指定端口映射(小结),一般可以有四种写法
ip:hostPort:containerPort
ip::containerPort
本地端口:容器端口 (常用)
containerPort
# 测试
[root@kuangshen ~]# docker images
REPOSITORY TAG IMAGE ID CREATED
SIZE
centos latest 470671670cac 3 months ago
237MB
# 使用centos进行用交互模式启动容器,在容器内执行/bin/bash命令!
[root@kuangshen ~]# docker run -it centos /bin/bash
[root@dc8f24dd06d0 /]# ls # 注意地址,已经切换到容器内部了!
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@dc8f24dd06d0 /]# exit # 使用 exit 退出容器
exit
[root@kuangshen ~]#

删除容器

1
2
3
docker rm 容器id # 删除指定容器
docker rm -f $(docker ps -a -q) # 删除所有容器
docker ps -a -q|xargs docker rm # 删除所有容器

# 常用其他命令

后台启动

1
2
3
4
5
6
# 命令 docker run -d 镜像名
[root@iZc3ncrhf90zylZ ~]# docker run -d centos
53126313685d9f43c9bca5c16f4714a1b86816deb4732adb9d7dab0975034317
# 问题docker ps 发现centos停止了
# 常见的坑,docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会 自动停止
# nginx容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

查看日志

1
2
3
4
5
# -t 显示时间戳
# -f 打印最新的日志
# --tail 数字 显示多少条!

[root@iZc3ncrhf90zylZ ~]# docker logs -f -t --tail 10 492e64524782

查看容器中运行的进程信息,支持 ps 命令参数

1
2
3
4
5
6
7
# 命令
docker top 容器id

# 测试
[root@iZc3ncrhf90zylZ ~]# docker top 492e64524782
UID PID PPID C STIME TTY TIME CMD
root 1455 11434 0 17:45 pts/0 00:00:00 /bin/bash

查看容器 / 镜像的元数据

1
2
3
4
5
# 命令
docker inspect 容器id

# 测试
[root@iZc3ncrhf90zylZ ~]# docker inspect 492e64524782

进入正在运行的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 命令1
docker exec -it 容器id bashShell

# 测试1
[root@iZc3ncrhf90zylZ ~]# docker exec -it 492e64524782 /bin/bash
[root@492e64524782 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:45 pts/0 00:00:00 /bin/bash
root 15 0 0 10:11 pts/1 00:00:00 /bin/bash
root 29 15 0 10:11 pts/1 00:00:00 ps -ef

# 命令2
docker attach 容器id

# 测试2
[root@iZc3ncrhf90zylZ ~]# docker attach 4f48f187ab25

# 区别
# exec 是在容器中打开新的终端,并且可以启动新的进程
# attach 直接进入容器启动命令终端,不会启动新的进程

从容器内拷贝文件到主机上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 命令
docker cp 容器id:容器内路径 目的主机路径

# 测试
[root@iZc3ncrhf90zylZ home]# docker attach 492e64524782
[root@492e64524782 /]# cd /home
[root@492e64524782 home]# ls
[root@492e64524782 home]# touch test.java
[root@492e64524782 home]# ls
test.java
[root@492e64524782 home]# exit
exit
[root@iZc3ncrhf90zylZ home]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
492e64524782 centos "/bin/bash" 2 hours ago Exited (0) 10 seconds ago vibrant_buck
53126313685d centos "/bin/bash" 2 hours ago Exited (0) 2 hours ago pensive_hamilton
[root@iZc3ncrhf90zylZ home]# docker cp 492e64524782:/home/test.java /home
[root@iZc3ncrhf90zylZ home]# ll
total 4
drwx------ 3 admin admin 4096 Feb 7 15:27 admin
-rw-r--r-- 1 root root 0 Mar 17 19:35 test.java
-rw-r--r-- 1 root root 0 Mar 17 19:33 windlin

# 小结

1647517132421

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
attach  	#当前shell下attach连接指定环境
build #通过Dockerfile定制镜像
commit #提交当前容器为新的镜像
cp #从容器中拷贝指定文件或者目录到宿主机中
create # 创建一个新的容器,同run,但不启动容器
diff # 查看 docker 容器变化
events # 从 docker 服务获取容器实时事件
exec # 在已存在的容器上运行命令
export # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history S # 展示一个镜像形成历史
images # 列出系统当前镜像
import # 从tar包中的内容创建一个新的文件系统映像[对应export]
info # 显示系统相关信息
inspect # 查看容器详细信息
kill # kill 指定 docker 容器
load # 从一个 tar 包中加载一个镜像[对应 save]
login # 注册或者登陆一个docker 源服务器
logout # 从当前 Docker registry 退出
logs # 输出当前容器日志信息
port #查看映射端口对应的容器内部源端口
pause # 暂停容器
ps # 列出容器列表
pull #从docker镜像源服务器拉取指定镜像或者库镜像
push #推送指定镜像或者库镜像至docker源服务器
restart # 重启运行的容器
rm # 移除一个或者多个容器
rmi # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run # 创建一个新的容器并运行一个命令
save # 保存一个镜像为一个tar 包[对应 load]
search # 在 docker hub 中搜索镜像
start # 启动容器
stop # 停止容器
tag # 给源中镜像打标签
top # 查看容器中运行的进程信息
unpause # 取消暂停容器
version # 查看 docker 版本号
wait # 截取容器停止时的退出状态值

# 练习

# 安装 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1、搜索镜像
[root@iZc3ncrhf90zylZ home]# docker search nginx
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 13159 [OK]

# 2、拉取镜像
[root@iZc3ncrhf90zylZ home]# docker pull nginx

# 3、启动容器
[root@iZc3ncrhf90zylZ home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 2 months ago 141MB
[root@iZc3ncrhf90zylZ home]# docker run -d --name nginx01 -p 3344:80 nginx

# 4、测试访问
[root@iZc3ncrhf90zylZ home]# curl localhost:3344

# 5、进入容器

# 安装 tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 官方文档解释
# -it :交互模式
# --rm:容器启动成功并退出以后容器就自动移除,一般在测试情况下使用!
docker run -it --rm tomcat:9.0
# 1、下载tomcat镜像
docker pull tomcat
# 2、启动
docker run -d -p 8080:8080 --name tomcat9 tomcat
# -p 使用 本地端口:容器端口
# 3、进入tomcat
docker exec -it tomcat9 /bin/bash

# 4、思考:我们以后要部署项目,还需要进入容器中,是不是十分麻烦,要是有一种技术,可以将容器内和我们Linux进行映射挂载就好了?我们后面会将数据卷技术来进行挂载操作,也是一个核心内容,这里大家先听听名词就好,我们很快就会讲到!

# 可视化面板

  • Porttainer

1
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

# Docker 镜像讲解

# 镜像是什么

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

# Docker 镜像加载原理

# UnionFs(联合文件系统)

UnionFS(联合文件系统):Union 文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一虚拟文件系统下。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性;一次同时加载多个文件系统,但从外面看起来。只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

# Docker 镜像加载原理

docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS

bootfs(boot file system)主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel。Linux 刚启动时会加载 bootfs 文件系统,在 Docker 镜像最底层是 bootfs。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核,当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。

rootfs(root file system),在 boots 之上。包含的就是典型 Linux 系统中的 /dev./proc,/bin,/etc 等标准目录和文件。rootfs 就是各种不同操作系统发行版,如 Ubuntu,Centos 等等。

平时我们安装进虚拟机的 Centos 都是好几个 G,docker 为什么 200 多 m?

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

# 分层理解

# 分层的镜像

我们可以去下载一个镜像,注意观察日志输出,可以看到一层一层地在下载!

== 思考:== 为什么 Docekr 要采用分层的结构呢?

最大的好处,资源共享!

查看镜像分层的方式可以通过 docker image inspect 命令

理解:

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

举一个简单的例子,加入基于 Ubantu linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python 包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。

# 特点

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

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

1647601588847

# 镜像 Commit

docker commit 从容器创建一个新的镜像

1
2
3
4
5
6
7
8
9
docker commit 提交容器副本使之成为一个新的镜像!
# 语法
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

#测试
[root@iZc3ncrhf90zylZ ~]# docker commit -m="增加了webapps" -a="windlinxy" tomcat01 tomcat_ex:1.0

# 注意:commit的时候,容器的名字不能有大写,否则报错:invalid reference format

# 容器数据卷

# 什么是容器数据卷

docker 理念回顾:

将应用和运行环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的!

就好比,你安装一个 Mysql。结果你把容器删了就相当于删库跑路了,这 TM 也太扯了吧!

所以我们希望容器之间有可能可以共享数据,Docker 容器产生的数据,如果不通过 docker commit 生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也没有了!这样是行不通的!

作用:

卷就是目录或者文件,存在一个或者多个容器中,由 docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System,提供一些用于持续存储或共享数据的特性:

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会再容器删除时删除其挂载的数据卷。

特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

所以:总结一句话,容器的持久化,以及容器间的继承和数据共享!

# 使用数据卷

<font> 方式一 :</font > 容器中直接使用命令来添加

挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#命令
docker run -it -v 宿主机绝对路径目录:容器内目录 镜像名

# 测试
[root@iZc3ncrhf90zylZ ~]# docker run -it -v /home/ceshi:/home centos /bin/bash
[root@17f40c5e1e8c /]# cd /home
[root@17f40c5e1e8c home]# ls
[root@17f40c5e1e8c home]# touch mount.java

[root@iZc3ncrhf90zylZ home]# cd ceshi
[root@iZc3ncrhf90zylZ ceshi]# ls
mount.java

# 宿主机目录是创建的
# 运行后查看容器的信息可看到挂载信息
"Mounts": [
{
"Type": "bind",
"Source": "/home/ceshi",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]

测试容器和宿主机之间数据共享:可以发现,在容器中,创建的会在宿主机中看到!

容器停止退出后,主机修改的数据是否同步:

  1. 停止容器
  2. 在宿主机上修改文件,增加些内容
  3. 启动刚才停止的容器
  4. 查看对应的文件,数据依然同步

# 使用 docker 安装 mysql

1
2
3
4
5
6
7
8
9
10
# 搜索镜像 docker search mysql
# 拉取镜像 docker pull mysql:5.7
# 创建容器并运行
docker run -d -p 3307:3306 \
-v /home/mysql/conf:/etc/mysql/conf.d \
-v /home/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root --name mysql_01 mysql:5.7
# -e 配置环境变量 -v 挂载卷 宿主机绝对路径目录:容器内目录

# 测试连接(ip:3307)没有问题

<font> 存疑:为什么在挂载文件的时候使用 conf.d 的配置,而不是使用 my.cnf</font>

# 通过 Docker File 来添加(了解)

DockerFile 是用来构建 Docker 镜像的构建文件,是由一些列

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 1、我们在宿主机 /home 目录下新建一个docker-test-volume文件夹
[root@iZc3ncrhf90zylZ home]# mkdir docker-test-volume

# 说明:在编写DockerFile文件中使用VOLUME指令给镜像添加一个或多个数据卷
VOLUME["/dataVolumeContainer1","/dataVolumeContainer2"]

# 出于可移植和共享的考虑,我们之前使用 -v 主机目录:容器目录 这种方式不能够直接在DockerFile中实现。
# 出于宿主机目录是依赖于特定宿主机的,并不能保证在所有宿主机上都存在这样的特定目录。

# 2、编写DockerFile文件
[root@iZc3ncrhf90zylZ home]# mkdir docker-test-volume
[root@iZc3ncrhf90zylZ home]# cd docker-test-volume/
[root@iZc3ncrhf90zylZ docker-test-volume]# pwd
/home/docker-test-volume
[root@iZc3ncrhf90zylZ docker-test-volume]# clear
[root@iZc3ncrhf90zylZ docker-test-volume]# vim dockerfile01
FROM centos
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
CMD echo "-------end------"
CMD /bin/bash

# 3、build后生成镜像,获得一个新镜像
[root@iZc3ncrhf90zylZ docker-test-volume]# docker build -f /home/docker-test-volume/dockerfile01 -t centos_op:1.0 .

[root@iZc3ncrhf90zylZ docker-test-volume]# docker run -it centos_op:1.0
[root@9827179b0667 /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Mar 19 10:02 dataVolumeContainer
drwxr-xr-x 2 root root 4096 Mar 19 10:02 dataVolumeContainer1

# 问题:通过上述步骤,容器内的卷目录地址就已经知道了,但是对应的主机目录地址在哪里呢?
[root@iZc3ncrhf90zylZ volumes]# docker inspect 9827179b0667
# 查看volumes
"Mounts": [
{
"Type": "volume",
"Name": "497b7dbcc55b65d68034b0995bdb513ed235439283eebc16061038ad83dc3328",
"Source": "/var/lib/docker/volumes/497b7dbcc55b65d68034b0995bdb513ed235439283eebc16061038ad83dc3328/_data",
"Destination": "/dataVolumeContainer1",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "43abd3237309540f73e0a7a121070c08ca075f416da0959efc36e56530a0a0a2",
"Source": "/var/lib/docker/volumes/43abd3237309540f73e0a7a121070c08ca075f416da0959efc36e56530a0a0a2/_data",
"Destination": "/dataVolumeContainer",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],

# 容器数据卷在主机对应位置:
/var/lib/docker/volumes/

注意:如果访问出现了 cannot open dirctory:Permission denied

解决方法: 在挂载目录后多加一个 --privileged=true 即可

# 匿名和具名挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 匿名挂载的缺点,不好维护,通常使用命令 docker volume维护
docker volume ls

# 具名挂载
-v 卷名:/容器内路径
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx nginx

# 查看挂载路径
docker volume inspect nginxconfig

# 测试
[root@iZc3ncrhf90zylZ ~]# docker volume inspect nginxconfig
[
{
#创建时间
"CreatedAt": "2022-03-19T16:03:49+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginxconfig/_data",
"Name": "nginxconfig",
"Options": null,
"Scope": "local"
}
]

# 判断挂载的是卷名而不是本机目录名?
不是:开始就是卷名
是:开始就是目录名

# 改变文件读写权限
# ro:只读 rw:读写
例子;
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:ro nginx

# 数据卷容器

命名的容器挂载数据卷,其他容器通过这个父容器实现数据共享,挂载数据卷容器,称之为数据卷容器

我们使用上一步的 centos_op 为镜像,运行容器 docker01,docker02,docker02,他们都有容器数据卷:/datavolumContainer1 和 /datavolumContainer2

使用 --volumes-from 进行共享数据

退出不停止: ctrl + P + Q

  1. 先启动一个父容器 docker01,然后在 dataVolumeContainer2 新增文件
  2. 创建 docker02,docker03 让他们继承 docker01 --volumes-from
  3. 回到 docker01 发现可以看到 02 和 03 添加的共享文件
  4. 删除 docker01,docker02 修改后 docker03 还能不能访问
  5. 删除 docker02 ,docker03 还能不能访问
  6. 新建 docker04 继承 docker03,然后再删除 docker03,看下是否可以访问!

得出结论:
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。
存储在本机的文件则会一直保留!

# DockerFile

大家想想,Nginx,tomcat,mysql 这些镜像都是哪里来的?

流程:开发应用 -->DockerFile==> 上传到仓库(私有仓库,公有仓库)=> 下载镜像 => 启动运行

# 什么是 DockerFile

dockerfile 是用来构建 Docker 镜像的构建文件,是由一系列命令和参数构成的脚本。

构建步骤:

  1. 编写 DockerFile 文件
  2. docker build 构建镜像
  3. docker run

dockerfile 文件我们刚才已经编写过了一次,这里我们继续使用 centos 来看!

# DockerFile 构建过程

基础知识:

  1. 每条保留字都必须为大写字母且后端要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层,并对镜像进行提交

流程:

  1. docker 从基础镜像运行一个容器
  2. 执行一条指令并对容器做出修改
  3. 执行类似 docker commit 的操作提交一个新的镜像层
  4. Docker 再基于刚提交的镜像运行一个新容器
  5. 执行 dockerfile 中的下一条指令知道所有指令都执行完成

说明:

从应用软件的角度来看,DockerFile,docker 镜像与 docker 容器分别代表软件的三个不同阶段。

  • DockerFile 是软件的原材料
  • Docker 镜像是软件的交付品
  • Docker 容器则是软件的运行状态

DockerFIle 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署与运维,三者缺一不可。

1647702227780

DockerFile:需要定义一个 DockerFile,DockerFile 定义了进程需要的一切东西。DockerFile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统发行版、服务进程和内核进程(当引用进行需要和系统服务和内核进程打交道,这时需要考虑如何设计 namespace 的权限控制)等等。

  • Docker 镜像:在 DockerFile 定义了一个文件之后,Docker build 时会产生一个 Docker 镜像,当运行 Docker 镜像时,会真正开始提供服务;

  • Docker 容器:容器是直接提供服务的。

# DockerFile 指令

1
2
3
4
5
6
7
8
9
10
11
12
FROM		# 基础镜像,当前新镜像是基于哪个镜像的
LABEL # 镜像维护者的姓名混合邮箱地址
RUN # 容器构建时需要运行的命令
EXPOSE # 当前容器对外保留出的端口
WORKDIR # 指令在创建容器后,终端默认登录的进来工作目录,一个落脚点
ENV # 用来在构建镜像过程中设置环境变量
ADD # 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY # 类似ADD,拷贝文件和目录到镜像中!
VOLUME # 容器数据卷,用于数据保存和持久化工作
CMD # 指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD指令,但只有最后一个生效!
ENTRYPOINT # 指定一个容器启动时要运行的命令!和CMD一样
ONBUILD # 当构建一个被继承的DockerFile时运行命令,父镜像在被子镜像继承后,父镜像的ONBUILD被触发

BUILDBothrun
FROMWORKDIRCMD
LABELUSERENV
COPYEXPOSE
ADDVOLUME
RUNENTRYPOINT
ONBUILD
.dockerignore

实战

Docker Hub 中的 99% 的镜像都是通过 base 镜像(Scratch)中安装和配置需要的软件构建出来的

自定义一个 centos

1、编写 DockerFile

查看下官网默认的 CentOS 的情况:

目的,使我们自己的镜像具备如下:登录后的默认路径、vim 编辑器、查看网络配置 ifconfig 支持

1、准备编写 DockerFile

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM centos
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates"
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim # 出错
RUN yum -y install net-tools # 出错
EXPOSE 80
CMD echo $MYPATH
CMD echo "----------end--------"
CMD /bin/bash

2、构建

docker build -f dockerfile地址 -t 新镜像的名字:[TAG] .

1
2
[root@iZc3ncrhf90zylZ dockertest]# docker build -t test01:0.1 .

3、运行

docker run -it 名字:[TAG]

4、列出镜像的变更历史

docekr history 镜像名

CMD 和 ENTRYPOINT 的区别

我们之前说过,两个命令都是指定一个容器启动时要运行的命令

**CMD:**Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换!

**ENTRYPOINT:**docker run 之后的参数会被当做参数传递给 ENTRPOINT,之后形成新的命令组合!

测试:

CMD 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 1、构建dockerfile
[root@kuangshen home]# vim dockerfile-cmd-test
[root@kuangshen home]# cat dockerfile-cmd-test
FROM centos
CMD [ "ls", "-a" ]
# 2、build 镜像
[root@kuangshen home]# docker build -f dockerfile-cmd-test -t cmdtest .
Sending build context to Docker daemon 22.02kB
Step 1/2 : FROM centos
---> 470671670cac
Step 2/2 : CMD [ "ls", "-a" ]
---> Running in a3072987de38
Removing intermediate container a3072987de38
---> 554bc6952657
Successfully built 554bc6952657
Successfully tagged cmdtest:latest
# 3、执行
[root@kuangshen home]# docker run 554bc6952657
.dockerenv
bin
dev
etc
home
lib
lib64
......
# 4、如果我们希望用 -l 列表展示信息,我们就需要加上 -l参数
[root@kuangshen home]# docker run cmdtest -l
docker: Error response from daemon: OCI runtime create failed:
container_linux.go:349: starting container process caused "exec: \"-l\":
executable file not found in $PATH": unknown.
# 问题:我们可以看到可执行文件找不到的报错,executable file not found。
# 之前我们说过,跟在镜像名后面的是 command,运行时会替换 CMD 的默认值。
# 因此这里的 -l 替换了原来的 CMD,而不是添加在原来的 ls -a 后面。而 -l 根本不是命令,所
以自然找不到。
# 那么如果我们希望加入 -l 这参数,我们就必须重新完整的输入这个命令:
docker run cmdtest ls -al

ENTRYPOINT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 1、构建dockerfile
[root@kuangshen home]# vim dockerfile-entrypoint-test
[root@kuangshen home]# cat dockerfile-entrypoint-test
FROM centos
ENTRYPOINT [ "ls", "-a" ]
# 2、build 镜像
[root@kuangshen home]# docker build -f dockerfile-entrypoint-test -t
entrypointtest .
Sending build context to Docker daemon 23.04kB
Step 1/2 : FROM centos
---> 470671670cac
Step 2/2 : ENTRYPOINT [ "ls", "-a" ]
---> Running in bac4ae055630
Removing intermediate container bac4ae055630
---> ae07199f9144
Successfully built ae07199f9144
Successfully tagged entrypointtest:latest
# 3、执行
[root@kuangshen home]# docker run ae07199f9144
.dockerenv
bin
dev
etc
home
lib
lib64
......
# 4、测试-l参数,发现可以直接使用,这里就是一种追加,我们可以明显的知道 CMD 和
ENTRYPOINT 的区别了
[root@kuangshen home]# docker run entrypointtest -l
total 56
drwxr-xr-x 1 root root 4096 May 12 04:21 .
drwxr-xr-x 1 root root 4096 May 12 04:21 ..
-rwxr-xr-x 1 root root 0 May 12 04:21 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 May 12 04:21 dev
drwxr-xr-x 1 root root 4096 May 12 04:21 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
.....

自定义镜像 tomcat

  1. mkdir -p myuser/build/tomcat_self
  2. 在目录下 touch read.txt
  3. 将 JDK 和 tomcat 安装的压缩包拷贝进上一步目录
  4. 在 myuser/build/tomcat_self 目录下新建 Dockerfile 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@iZc3ncrhf90zylZ tomcat_self]# cat Dockerfile 
FROM centos
LABEL version="1.0"
#把宿主机当前上下文的read.txt拷贝到容器/usr/local/路径下
COPY read.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中
ADD apache-tomcat-8.5.59.tar.gz /usr/local
ADD openlogic-openjdk-jre-8u262-b10-linux-x64.tar.gz /usr/local
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/openlogic-openjdk-jre-8u262-b10-linux-64
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.59
ENV CATALINA_BASE /usr/local/apache-tomcat-8.5.59
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
# 启动时运行tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.22/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.22/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-8.5.59/bin/startup.sh && tail -F /usr/local/apache-tocamt-8.5.59/bin/logs/catalina.out



#测试
[root@iZc3ncrhf90zylZ tomcat_self]# docker run -it -p 8080:8080 --name tomcat02 tomcat_self /bin/bash

5、构建镜像

6、运行启动 run

1
2
3
docker run -d -p 9090:8080 --name mydiytomcat -v \
/home/dockertest/build/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps/test -v \
/home/dockertest/build/tomcat/tomcat9logs/:/usr/local/apache-tomcat-9.0.22/logs --priviledged=true diytomcat

备注:Docker 挂载主机目录 Docker 访问出现 cannot open directory .: Permission denied
解决办法:在挂载目录后多加一个 --privileged=true 参数即可

7、验证测试访问 curl localhost:9090

8、结合前面学习的容器卷将测试的 web 服务 test 发布

# 发布镜像

# DockerHub

注册 dockerhub https://hub.docker.com/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1、查看登录命令[root@iZc3ncrhf90zylZ tomcat_self]#  docker login --help

Usage: docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username

# 2、登录
[root@iZc3ncrhf90zylZ tomcat_self]# docker login -u yourname
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

# 3、将镜像发布到自己账号下(注意要将镜像名进行修改为 yourname/image)
docker push kuangshen/diytomcat:1.0

# 阿里云镜像

  1. 登录阿里云
  2. 找到容器镜像服务
  3. 创建命名空间
  4. 创建镜像仓库
  5. 测试推送发布
  6. 在阿里云查看镜像效果

# 总结

image-20220322154032494

更新于 阅读次数 本文阅读量:

请我喝[茶]~( ̄▽ ̄)~*

Windlinxy 微信支付

微信支付

Windlinxy 支付宝

支付宝