Docker你好

Olivia的小跟班 Lv4

题记

本文用于记录docker的相关学习。

Docker是什么

Docker网址:docker

image-20230520143923319

docker对进程进行封装隔离,属于操作系统层面的虚拟化技术

由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器

docker 应用场景

  • 自动化测试和持续集成、发布
  • Web 应用的自动化打包和发布
  • 后台应用易部署

docker 的优势

  • 快速, 一致的交付应用程序
  • 可移植,可扩展
  • 轻巧,快速,经济,高效,压榨 linux 自身资源

Docker 能做什么?

先来说说Docker和虚拟机有啥不一样的

以前的虚拟机这样的,系统占用资源大,很多步骤是冗余的,并且启动还很慢,不能忍

Nnp2dPcgnH.png!large

现在的 Docker 是这个样子的,

容器之间互相隔离,互补干扰,一起运行在同一个操作系统上,最大化使用操作系统资源

img

Docker 技术和虚拟机技术的不同?

  • 每个容器间都是相互隔离的,他们有属于自己的文件系统,相互不会有影响
  • 容器没有自己的内核,没有自己的硬件,容器内的应用是直接运行在宿主机的内核中
  • 传统的虚拟机是虚拟出一个硬件,运行完成的操作系统,在其上面运行应用

那么 Docker 具体能做什么?

做 DevOps

做 DevOps 有如下几个提升点:

  • 应用可以更快捷的部署和交付

以前麻烦的安装步骤一去不复返,使用 Docker 容器化后,打包镜像发布测试,一键部署及运行

  • 可以更方便的升级和扩容

使用 Docker,将项目打包成镜像,升级方便,扩容方便

  • 开发,运维,测试都会更简单

再也不用担心开发环境,测试环境,运维环境不一致的情况了

  • 更高效的利用资源

Docker 是运行在宿主机的内核中,可以在这台物理主机上部署多个Docker实例

Docker的组成

Docker 使用客户端 - 服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器

Docker的三个基本概念:

img

  • 镜像

相当于是一个 root 文件系统,类似于一个模板,这是静态的

  • 容器

相当于从模板拉出来的一个实例,容器通过镜像来创建,我们可以对他做创建,启动,停止,暂停,删除等操作

  • 仓库

用来保存镜像的,可以看做是一个代码控制中心

Docker的安装和使用

day9 wsl2和docker的安装与迁移 |青训营笔记 - 掘金 (juejin.cn)

docker run 的流程

img

  • docker run 现在本地找对应的镜像,若有则直接运行
  • 若没有就去 docker hub 上下载,若有就下载到本地后运行
  • 若没有就直接报错

Docker的底层原理

Docker 是如何工作的?

img

docker 是一个 C/S 模型,docker 的后台守护进行运行在主机上,客户端和服务端通过套接字 Socket 通信

docker 服务端收到 docker 客户端的指令时,则执行该指令

为什么 Docker 比 虚拟机快呢?

在网络上找了一张图,咱们对比一下就明确了

img

如图,Docker 比虚拟机快的原因如下:

  • docker 比虚拟机的抽象层更少
  • docker 利用的是宿主机的内核,而虚拟机是需要新建一个 OS

基于如上2点,虚拟机启动时,会加载操作系统,启动慢,时间基本上是分钟级

docker 启动的时候,不需要加载操作系统内核,因此快,时间基本上是秒级

Docker的常用命令

基本帮助命令

1
2
3
4
5
6
7
8
# 查看 docker 的基本版本信息
docker version

# 查看 docker 的系统信息,如镜像和容器数量
docker info

# 查看某个命令的帮助
docker xx命令 --help

咱们可以看官方的帮助文档:Reference documentation

image-20230520145226169

镜像命令

docker images 查看镜像

Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]

查看本机上的镜像:

image-20230520145342422

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

可选参数:

1
2
3
Options:
-a, --all 显示所有的镜像
-q, --quiet 只显示镜像ID

image-20230520145500827

docker search 搜索镜像

搜索redis为例

image-20230520145823665

  1. NAME(名称):镜像的名称或仓库名称。
  2. DESCRIPTION(描述):镜像的描述或说明,通常包含有关镜像内容和用途的信息。
  3. STARS(收藏数):镜像的收藏数,表示该镜像受欢迎程度的指标。
  4. OFFICIAL(官方镜像):如果该镜像是官方维护的镜像,则显示为”OK”,表示来自Redis官方或Docker官方的镜像。
  5. AUTOMATED(自动构建):如果该镜像是通过自动化流程进行构建的,则显示为”OK”,表示镜像是通过自动构建过程生成的。

加上参数

过滤 STARS 大于 2000 的 镜像

image-20230520150148603

我们也可以在 docker hub 上面直接在页面上搜索镜像

image-20230520150332784

docker pull下载镜像

docker pull [OPTIONS] NAME[:TAG|@DIGEST]

下载 redis 镜像为例

image-20230520150831408

因此上述的下载操作:docker pull redisdocker pull docker.io/library/redis:latest 一致

dockerhub 上面 可以查看到 redis 支持的版本

img

我们下载一个 6 版本的 redis

image-20230520151201969

可以看到 下载 版本 6 的 redis 的时候,没有分层下载了,说明在上述看到的分层下载他们是共用的

查看刚才的安装的镜像

image-20230520151230995

docker rmi 删除镜像

1
2
3
4
5
删除单个镜像	docker rmi -f 容器ID

删除多个镜像 docker rmi -f 容器ID 容器ID 容器ID

删除全部镜像 docker rmi -f $(docker images -q)

image-20230520151443324

容器命令

容器是基于镜像创建的,我们来下载一个 ubuntu 镜像

1
docker pull ubuntu

image-20230520155310034

docker run 新建并启动容器

docker run [参数] 镜像名字 [指令] [参数]

常用参数说明:

1
2
3
4
5
--name="xxx"    	# 运行容器的名字
-d # 后台方式运行
-it # 交互的方式运行
-p # 指定容器的端口号 例如 -p 6379:6380 常用的操作有 -p 主机端口:容器端口
-P # 随机指定端口号

启动 容器里面的 ubuntu,通过主机名字,我们已经可以看出来主机切换了

image-20230520155510518

退出容器

  • 键入 exit 命令,容器会退出
  • 使用快捷键 Ctrl + P + Q ,回到主机,容器不会退出

docker ps 查看容器

docker ps [OPTIONS]

image-20230520155818757

可选参数:

1
2
3
4
5
        	 # 查看正在运行的容器
-a # 查看运行过的容器
-n=xx # 查看运行过的前 xx 个容器
-s # 查看容器运行的 大小
-q # 查看容器 ID

docker rm 删除容器

1
2
3
4
docker rm 容器ID        				# 删除未运行的容器
docker rm -f 容器ID # 强制删除正在运行的容器
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -aq | xargs docker rm # 删除所有容器

image-20230520160043684

start,restart,stop,kill 开启,重启,停止,强制停止容器

1
2
3
4
docker start 容器ID
docker restart 容器ID
docker stop 容器ID
docker kill 容器ID

常用其他命令

docker run -d 后台启动容器

1
2
3
4
5
6
# 后台启动一个 ubuntu
docker run -d ubuntu

# 查看 运行的容器
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

发现没有容器正在运行

原因如下:

  • docker 后台启动服务,需要有一个前台的进程,否则 docker 发现没有应用,则会将该服务停止

我们主动加一个前台进程,看看效果

1
2
3
4
5
6
7
# 临时加上一个前台进程
docker run -d ubuntu /bin/bash -c "while true;do echo xiaozhupeiqi;sleep 2;done"

# 查看正在运行的容器
docker ps
CONTAINER ID IMAGE COMMAND
10ba0e687434 ubuntu "/bin/bash -c 'while…"

可以看出,docker ps 命令已经可以查看到正在运行的容器了,OK

docker logs 查看日志

docker logs [参数] 容器 ID

1
2
3
4
Options:
-f # 和输出保持一致
-n # 输出最近的几行
-t # 打印时间戳

查看上述容器的日志

image-20230520160900627

docker top 查看容器中进程信息
docker top 容器 ID

image-20230520161004651

docker inspect 查看镜像元数据

docker inspect 容器 ID

image-20230520161039111

输出信息中省略了大量信息

docker exec 进入当前运行的容器

docker exec [参数] 容器 ID 指令 [指令的参数]

image-20230520161142367

docker attach 进入容器中正在执行的程序

docker attach 容器 ID

image-20230520161323115

docker exec 和 docker attach 的区别

docker exec:进入容器,会新开一个终端,可以正常操作

docker attach:进入容器正在执行的终端,不会启动新的终端

docker cp 将容器内文件拷贝到主机内

docker cp 容器 ID: 容器文件路径 主机路径

docker stats:查看docker内服务内存状态

image-20230520161445114

Docker部署nginx

搜索 nginx 镜像

  • 使用 docker search nginx

image-20230520153352429

  • 或者在 dockerhub 上搜索 nginx,具体的版本和详细信息会更加全面,一般使用官方的

image-20230520153512801

拉取 nginx 镜像

拉取 nginx 镜像,我们这里就拉取最新版本的 nginx

我之前就有了nginx镜像,所以是这样的

image-20230520153616819

创建并运行容器

  • 新建一个容器命名为 nginx1
  • nginx 默认端口是 80,将 docker 容器中的 80 端口映射程 主机中的 8888 端口
  • 设置后台运行 nginx 容器

image-20230520153807627

验证

使用 curl 命令,访问一下 主机的 8888 端口,查看是否访问 OK

image-20230520153912800

咱们也可以进入到 nginx docker 容器中,直接访问 80 端口

image-20230520154041382

小结

因为我们在创建 nginx1 容器的时候,将主机的 8888 端口,映射到了 容器 nginx1 的 80 端口,因此可以访问主机的 8888 端口来访问到 nginx1 容器中的 80 端口

img

此时,可以访问我的阿里云服务器的 8888 端口,实际是可以访问到我的 nginx1 容器中的 nginx 服务器

img

docker run –-help

用法: docker run [参数] 镜像 [命令] [命令的参数列表…]

Run a command in a new container

参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-a, –attach list Attach to STDIN, STDOUT or STDERR
-c, –cpu-shares int CPU shares (relative weight)
-d, –detach 后台运行容器
-e, –env list 设置环境变量
-h, –hostname string Container host name
-i, –interactive Keep STDIN open even if not attached
-l, –label list Set meta data on a container
-m, –memory bytes 内存限制
-p, –publish list Publish a container’s port(s) to the host
-P, –publish-all Publish all exposed ports to random ports
-t, –tty Allocate a pseudo-TTY
-u, –user string 用户名或者 uid
-v, –volume list 挂载卷
-w, –workdir string 设置容器中的工作目录

安装portainer

1.拉取portainer镜像

image-20230520161720723

2.运行portainer镜像

image-20230520161752741

3.访问portainer可视化界面

image-20230520161850086

这个就是本地的dockerimage-20230520162022374

portainer其他操作可参考这篇文章:Docker可视化界面portainer的安装与使用

镜像相关原理

镜像是什么?

镜像是一种轻量级的,可执行的独立的软件包。

镜像用来打包软件的运行环境和基于运行环境开发的软件,它包含运行某些软件所需要的所有内容,例如:代码,运行时库,环境变量和配置文件等等

所有的应用,可以直接打包 docker 镜像,一键部署,一键运行

得到镜像方式有哪些?

  • 直接拷贝其他 docker 镜像
  • 自己制作一个镜像 DockerFile
  • 从远程仓库下载,如 dockerhub

Docker 镜像的加载原理

UnionFS,是联合文件系统,还记的我们 docker 学习二 里面安装 redis 的时候,出现的分层下载吗

img

这就是联合文件系统

1
2
3
UnionFS 联合文件系统,是一种分层,轻量级并且高性能的文件系统
它支持对文件系统的修改作为一次提交来一层一层的叠加,同时可以将不同目录挂载到一个虚拟文件系统下
UnionFS 联合文件系统是 Docker 镜像的基础,镜像还可以通过分层来继承,基于基础的镜像,我们可以制作成各种应用镜像

特性:

联合文件系统一次同时加载多个文件系统,联合加载会把各层的文件系统叠加起来,最终的文件系统会包含所有底层的文件和目录

Docker 的镜像加载原理是什么呢?

iSeHkh05Eq.png!large

Docker 的镜像是有一层一层的文件系统组成的,这个层级的文件系统就叫做联合文件系统,一般底下的层都是共用的

一般系统启动,是一个加载的过程,这个过程是 bootloader 引导加载 kernel,linux 操作系统刚启动的时候还会加载 bootfs 文件系统,而咱们的 Docker 镜像最底层就是 bootfs

bootfs

boot file system 是一个文件系统,主要是包含 bootloader 和 kernel

当 boot 加载完毕之后,整个内核都在运行正在内存中的,此时内存的使用权已经由 bootfs 交接给内核,这个时候系统会将 bootfs 卸载掉

rootfs

在来说一下 rootfs,root file system,根文件系统,它是在 bootfs 之上的,就是包含了,linux 操作系统中的 /dev ,/proc,/bin,/etc 等目录和文件

例如我们知道的 rootfs 有 centos,ubuntu 等等

分层原理

我们来下载一个 redis 看看效果

img

Docker 是按照层级进行下载,之前下载过的层级就不会再次下载了,我们可以通过 docker inspect 查看 redis 的细节

1
2
3
4
5
6
7
8
9
# docker inspect redis:latest
...
"RootFS": {
"Type": "layers",
"Layers": [ "sha256:814bff7343242acfd20a2c841e041dd57c50f0cf844d4abd2329f78b992197f4", "sha256:dd1ebb1f5319785e34838c7332a71e5255bda9ccf61d2a0bf3bff3d2c3f4cdb4", "sha256:11f99184504048b93dc2bdabf1999d6bc7d9d9ded54d15a5f09e36d8c571c32d", "sha256:e461360755916af80821289b1cbc503692cf63e4e93f09b35784d9f7a819f7f2", "sha256:45f6df6342536d948b07e9df6ad231bf17a73e5861a84fc3c9ee8a59f73d0f9f",
"sha256:262de04acb7e0165281132c876c0636c358963aa3e0b99e7fbeb8aba08c06935"
]
},
...

如上结果我们可以看到,redis 的层级与我们 pull 拉取镜像时候的层级一致

那么 Docker 为什么要采用分层下载呢?

主要是为了共享资源

例如我们的多个镜像都是从基础镜像构建而来,那么宿主机只需要在机器上保留一份基础镜像即可,并且内存中也只需要加载一份基础镜像,就可以为所有需要的容器服务,而且镜像的每一层也都是可以共享的

3QKXFuDGPC.png!large

我们可以这样来理解:

所有的 Docker 镜像都来源于基础镜像,我们增加或修改镜像内容的时候,就会在当前镜像层上面,新建一个镜像层,这就例如 windows 里面的一个安全补丁

如上面图例:

我们在镜像的第一层,放置 file1,file2,镜像的第二层放置 file3,file4,镜像的第三层放置 file3.1 (file3.1 是 file3 的一个新版本)

那么,我们在打包镜像的时候,就会合并出 一个镜像里面,有 4 个文件,此处也就是 4 个 layer

当我们下载这个最终合并的镜像时,就会一次下载上述的 4 个 layer

Docker 镜像的特点:

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

上面说的这一层就是咱们的容器层,容器层下面是镜像层,如下图所示

img

如何提交咱们的镜像

Docker 提交原理和命令与 Git 类似

docker commit

提交当前容器,成为一个新的版本

我们一般会这样使用

1
docker commit -m="描述信息" -a="作者" 容器ID 目标镜像名:[TAG]

举个例子:

image-20230520165606144

image-20230520165613749

image-20230520165824883

如果我们想要保存自己的容器当前状态,咱们就可以通过 commit 来提交,获得一个想要的镜像,这就有点像使用虚拟机打快照一样

容器数据卷

什么是容器数据卷

思考一个问题,我们为什么要使用 Docker?

主要是为了可以将应用和环境进行打包成镜像,一键部署。

再思考一个问题,容器之间是相互隔离的,如果我们在容器中部署类似 mysql 这样的组件,如果把该容器删除掉,那么 mysql 的数据也会被删掉了,数据丢失了,咱们删库跑路真刺激

事实上,我们可不能让这么有风险的事情存在,因此有了卷技术

卷技术是容器之间可以共享数据的技术,Docker 容器中产生数据,将数据同步到本地

例如咱们将 Docker mysql 容器中的 /usr/mysql 目录挂载到宿主机的 /home/mysql 目录

img

使用卷技术,我们就可以让数据得以持久化

实际上操作起来就是挂载目录,将 Docker 容器里面的目录,挂载到宿主机上的某个目录,这就可以将数据持久化和同步了, Docker 容器间的数据共享仍然是这样做的

咱们如何使用数据卷?

image-20230520170835295

image-20230520170854589

image-20230520170914370

mysql 实战一波

咱们再来一个实战,我们一起来看看数据卷如何使用

下载 5.7 版本的 mysql docker 镜像,也可以下载其他版本,这个没有关系

image-20230520171101769

启动镜像,直接使用 -v 来挂载目录

使用方式

1
docker run -it -v 主机目录:容器的目录

开始启动镜像

咱们可以参考 dockerhub 上的文档

img

img

image-20230520171412192

1
2
3
4
5
6
# 解释一下上述命令
-d 后台运行
-p 宿主机端口:容器端口 端口映射
-v 宿主机目录:容器目录 挂载卷
-e 设置环境变量
--name 设置启动容器的名字

咱们可以通过 window 的 workbench 来远程连接一下 mysql

我的是云服务器,因此输入云服务器的地址,端口填入 8888 端口

默认用户名是 root , 密码是 123456

image-20230520173731597

测试连接 ok ,我们可以来进入数据库

img

咱们在 workbench 中新建一个数据库

看看这个数据库是否会在我们的宿主机上面有同步

image-20230520173926802

image-20230520173837137

果然是有的,再次 nice,这就达到了数据持久化的效果,这就是咱们从认识数据卷到使用数据卷的一个简单流程,咱们可以慢慢的深入下去

具名挂载和匿名挂载

以启动一个 nginx 为例子:

具名挂载:

docker run -d --name nginx3 -v JM:/etc/nginx:rw nginx

匿名挂载:

docker run -d --name nginx3 -v /etc/nginx:rw nginx

rw可读可写

ro只读,只能宿主机才能写

image-20230520175730754

查看数据挂载卷

image-20230520175843490

查看挂载具体目录

image-20230520180012985

image-20230520180450292

数据卷容器

Dockerfile

Dockerfile 就是用来构建 docker 镜像的构建文件,关于 Dockerfile 详细的我们在后面一期说到,此处先用用

他是一个命令脚本,通过这个脚本可以生成我们想要的镜像,镜像是分层的,一层一层的,脚本也是一个一个的命令,每个命令就是一层

我们可以来看一个小小的例子

自己写一个 Dockerfile 来构建自己的镜像,我们以 ubuntu 为例子

写一个简单的 dockerfile1

1
2
3
4
5
6
7
8
9
# vim dockerfile1

FROM ubuntu

VOLUME ["volume1","volume2"]

CMD echo "====successfully===="

CMD /bin/bash

解释一下:

  • FROM

来源基础镜像为 ubuntu

  • VOLUME

挂载,可以匿名挂载,也可以是具名挂载 ,默认会挂载到 docker 专门挂载的目录

  • CMD

指定可以使用命令

构建我们自己的镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# docker build -f dockerfile1 -t xiaomotong/ubuntu .
Sending build context to Docker daemon 1.346GB
Step 1/4 : FROM ubuntu
---> 1318b700e415
Step 2/4 : VOLUME ["volume1","volume2"]
---> Running in d7b475cacb22
Removing intermediate container d7b475cacb22
---> b8ac33cfbcfd
Step 3/4 : CMD echo "====successfully===="
---> Running in 35c98a625a9e
Removing intermediate container 35c98a625a9e
---> 67b6faf43370
Step 4/4 : CMD /bin/bash
---> Running in b2e1e0ad8d9b
Removing intermediate container b2e1e0ad8d9b
---> b26faaedefac
Successfully built b26faaedefac
Successfully tagged xiaomotong/ubuntu:latest

通过上述我们可以看到 docker 构建镜像的时候是一层一层的,一个命令一个命令的执行的,一个命令就是一层

  • docker build

构建我们自己的镜像

  • -f

指定 dockerfile 的文件

  • -t

目标,即我们 docker 镜像的名字

后面跟着生成镜像的位置

通过我们构建的镜像的创建并启动容器

1
2
3
4
5
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xiaomotong/ubuntu latest b26faaedefac 6 minutes ago 72.8MB

# docker run -it b26faaedefac

执行完上述命令之后,我们可以看到容器里面目录如下:

img

从图中我们可以看到 volume1 和 volume2,这就是我们刚才构建容器时候做的匿名挂载,那么我们使用 docker inspect 命令来看看这俩挂载卷具体是挂载到宿主机的哪个位置,并测试一个同步数据

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 inspect b29995f4178d
...
"Mounts": [
{
"Type": "volume",
"Name": "a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332",
"Source": "/var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data",
"Destination": "volume1",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "975ae74c8716f5e85ccf784c716291cffda2158baf6b3f9e145ffc1ea353cb7b",
"Source": "/var/lib/docker/volumes/975ae74c8716f5e85ccf784c716291cffda2158baf6b3f9e145ffc1ea353cb7b/_data",
"Destination": "volume2",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...

通过 docker inspect 可以看出 volume1 和 volume2 具体是挂载到宿主机的目录为 /var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data , 和 var/lib/docker/volumes/975ae74c8716f5e85ccf784c716291cffda2158baf6b3f9e145ffc1ea353cb7b/_data

咱们在容器挂载中创建一个文件,测试是否可以同步数据

在容器中的 volume1 中创建一个文件 xiaomotong.txt,写入字符串 hello world

1
2
3
4
5
6
7
root@b29995f4178d:/# cd volume1
root@b29995f4178d:/volume1# echo hello world >> xiaomotong.txt
root@b29995f4178d:/volume1# ll
total 12
drwxr-xr-x 2 root root 4096 Aug 5 15:01 ./
drwxr-xr-x 1 root root 4096 Aug 5 14:54 ../
-rw-r--r-- 1 root root 12 Aug 5 15:01 xiaomotong.txt

查看宿主机对应的挂载目录,确认是否同步数据

1
2
3
4
root@iZuf66y3tuzn4wp3h02t7pZ:/var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data# ls
xiaomotong.txt
root@iZuf66y3tuzn4wp3h02t7pZ:/var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data# cat xiaomotong.txt
hello world

果然同步 ok,nice

那么我们有没有想过,现在是容器和宿主机之间同步数据,可是容器和容器之间是如何同步数据的呢?

数据卷容器

数据卷容器,例如容器 1 通过指令 --volumes-from 挂载到容器 2 上的某个目录,那么容器 2 就是父容器,这个时候就实现了两个容器之间数据同步,容器 2 就是数据卷容器

img

来实战一个小例子:

用刚才我们制作的镜像,创建 2 容器,先创建 docker2 ,再创建docker1,并且 docker1 挂载到 docker2 上,并在 docker2 挂载的目录,volume1 里面创建一个文件,xiaomotong.txt,我们验证 docker1 volume1 里面是否也有这个文件

1
2
3
4
5
6
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
xiaomotong/ubuntu latest b26faaedefac 38 minutes ago 72.8MB

# docker run -it --name docker2 b26faaedefac
# docker run -it --name docker1 --volumes-from docker2 b26faaedefac

b26faaedefac 是我们自己制作的镜像的 ID

主要是使用 --volumes-from 指令,我们就可以将两个容器进行挂载

docker1

1
2
root@3ed3ca51118f:/volume1# ls
root@3ed3ca51118f:/volume1# touch xiaomotong.txt

docker2

1
2
root@e9e1a0c46331:/volume1# ls
xiaomotong.txt

果然,两个容器互相同步数据了

上述的例子,不仅仅是两个容器之间挂载,进行同步数据,多个容器挂载也是同样的道理,例如再来一个容器 docker3 挂载到 docker2 上,也是一样的效果

那么他们都是如何同步数据的呢?

容器间同步数据的原理是通过拷贝的方式,例如在 docker2 上面的挂载上创建了一个文件 2,这个时候 docker1 挂载了 docker2,也就是说 docker2 是父容器,那么 docker1 就会将文件 2 拷贝到自己对应的挂载中

img

反之,如果 docker1 在自己的挂载中创建了文件 1,那么文件 1 也会被 docker2 拷贝到自己的挂载中

若这个时候,我们删除 docker2 ,那么 docker1 挂载中的文件会丢失吗?

答案是不会的,因为他们是通过拷贝的方式,并不是共享一个副本

那么我们回顾一下上一期我们创建 mysql 容器的时候,若我们创建多个,那么我们是不是就很容易让他们数据同步,且删除其中一个容器,数据也不会丢失了

例如:

1
2
3
#docker run  -d -p 8888:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql1 mysql:5.7

#docker run -d -p 8888:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql2 --volumes-from mysql1 mysql:5.7

Dockerfile编写和实战

我们开始来一起学习 DockerFile 的知识点

DcokerFile 是用来构建 docker 镜像的文件,是一个命令参数脚本

一般 docker 镜像的构建步骤:

1、编写一个 dockerfile 文件

2、docker build 构建成为一个镜像

3、docker run 运行镜像

4、docker push 发布镜像(咱们可以发布到 DockerHub,也可以发布到阿里云上面)

我们来看看官方的镜像是咋玩的

例如我们在 DockerHub 上搜索 ubuntu ,看看官网的 DockerFile 是啥样子的

Docker ubuntu

img

img

点击链接我们会进入到 git 仓库上,也是 DockerFile

img

咱们看到就 3 行 Docker 命令,是官方做成镜像的 DockerFile,所以这个官方的 ubuntu 镜像是非常简单的,阉割版本的,甚至连 clear 命令都没有,ll 命令也没有

很多的官方镜像包都是非常简单的,很多功能都是没有的,我们通常会自己搭建自己的镜像来满足我们的各种需求

DockerFile 的构建过程

官方能构建镜像,我们也可以自己的镜像

DockerFile 的基础知识:

  • 每个 DockerFile 的保留字(指令),都必须是大写的
  • DockerFile 脚本执行是按照顺序执行的
  • #表示注释
  • 每一个指令都会创建提交一个新的镜像层,并提交

可以在网络上找到这样的图片,可以看到镜像是一层一层的,可以在浏览器上搜索到 DockerFile 里面的指令解释

img

dockerfile 是面向开发的,我们以后在做项目的时候,是直接发布一个镜像,交付的是一个镜像,就需要编写 DockerFile 文件,这个文件非常的简单!

咱们必须要掌握 Docker 镜像,逐渐成为企业交付的标准了。

咱们学习的过程是先会使用别人的东西,再去研究别人是怎么写的,进而我们也学会如何去写,去开发

例如:

我们先学习使用了,

Docker Images:通过 DockerFile 构建生产的镜像,最终发布和运行的产品

Docker 容器:容器服务就是镜像运行起来的服务器

现在我们开始详细学习 DockerFIle:构建文件,定义了一切的步骤,这是源代码

DockerFile 的指令

图片来源于网络,我们一一解释一波

fMAlVQD7b2.png!large

FROM
基础的镜像,一切都是从这里开始的

MAINTAINER(已过时,推荐使用LABEL)
指明镜像是谁写的,写下自己的姓名和邮箱

RUN
镜像构建的时候需要运行的命令

ADD
加入某些配置,例如加入 mysql 的压缩包,添加内容

WORKDIR
镜像的工作目录

VOLUME
挂载目录

EXPOSE
暴露端口 和 -p 是一个效果

CMD
指定这个容器启动的时候执行的命令,只会是最优一个指令进行生效,会被替代

ENTRYPOINT
指定这个容器启动的时候执行的命令,可以追加

ONBUILD
当构建一个被继承的 DockerFIle ,这个时候就会运行 ONBUILD 的指令,触发相应的动作

COPY
与 ADD 类似,此命令是将文件拷贝到镜像中

ENV
构建的时候设置环境变量

乍一看感觉 CMD 和 ENTRYPOINT 功能好像差不多,但是还是不太清楚具体区别在哪里,文章末尾会有详细说明

实战

我们自己来做一个自定一个 ubuntu 镜像

官方的 ubuntu 是阉割版本的,很多工具和命令都是不支持的,那么我们就自己加进去,自给自足

img

自己写一个 DockerFile

这里需要注意的是,基本上 99% 的镜像,都是基于这个基础镜像 scratch,我们可以看到官方的 DockerFIle 也是基于这个镜像来玩的

docker-brew-ubuntu-core/Dockerfile

img

那么我们可以基于这个 ubuntu 来进行自定义,加入一些我们需要的工具,如 vimifconfig

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

# 更新源
RUN apt-get update

# 安装vim
RUN apt-get install -y vim

# 安装net-tools
RUN apt-get install -y net-tools

# 设置环境变量
ENV MYPATH /usr/local
# 设置镜像工作目录
WORKDIR $MYPATH

# 暴露端口
EXPOSE 8888

# 执行echo命令
CMD echo "---- end ----"

CMD /bin/bash

开始构建

docker build -f dockerfile2 -t xmtubuntu .Windows下在dockerfile文件的目录下运行这个命令

如果不在 DockerFile 中写入 apt-get update 更新源,会出现下面这个问题,这个要注意

img

执行上述命令,会看到如下打印信息,最终会看到 Successfully,即为构建成功

image-20230526153313505

验证结果

docker images查看我们的镜像

image-20230526153427432

docker history 查看我们镜像的构建过程

image-20230526153538622

xmtubuntu 这个镜像的构建过程我们可以清晰的看出都执行了哪些步骤,当然,同样的方式,我们也可以看看官方的镜像是如何构建的,我们来看看官方 ubuntu 的

image-20230526153623101

官方的就很简单,阉割了很多东西,我们可以看出官方的 ubuntu 就 2 个步骤,第一个是加入 ubuntu 压缩包,第二个就是 /bin/bash

我们查看我们的自定义镜像 xmtubuntu

image-20230526153947189

CMD 和 ENTRYPOINT 的区别

CMD
指定这个容器启动的时候执行的命令,只会是最优一个指令进行生效,会被替代

ENTRYPOINT
指定这个容器启动的时候执行的命令,可以追加

如何理解呢?我们来做一个对比试验就可以很好的理解上述的解释说明,docker 里面有很多命令会有这样的微小区别,我们可以举一反三,慢慢深入学习

CMD 的例子

写一个简单的 DockerFile 文件名为 dockerfile-cmd

1
2
FROM xmtubuntu
CMD ["ls","-a"]

构建镜像

image-20230526154335749

创建并启动容器

docker run 容器id,可以看到如下效果

我们尝试在启动容器时候追加命令

docker run 容器id -l,就会有如下报错

image-20230526154448726

原因如下:

使用 CMD 指令是(例如我们的例子是 ls -a),我们在启动容器的时候,后面追加的命令(-l)会把 ls -a 替换掉,由于 -l 不是一个命令,因此报错

ENTRYPOINT 的例子

写一个简单的 DockerFile 文件名为 dockerfile-entrypoint

1
2
FROM xmtubuntu
ENTRYPOINT ["ls","-a"]

构建镜像,创建并启动容器和 CMD 的例子一模一样,咱们直接启动容器的效果和 CMD 的例子也是一模一样,我们直接来看启动容器并追加参数的例子

image-20230526154809486

可以看出使用 ENTRYPOINT 是可以在后面追加参数的,使用 CMD 若指令后面追加参数,那么会覆盖 CMD 指定的指令

那么,对于以后遇到相关的指令,我们也可以举一反三,做对比试验,这样我们就会理解的更加清楚

如何发布我们的镜像

1、登录 dockerhub

没有注册的 xdm 可以注册一个,hub.docker.com/

1
2
3
4
5
6
7
# docker login -u xxxx用户名
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

2、修改我们的镜像 tag

1
docker tag 我们的镜像id 我们的docker用户名/镜像名字:版本

3、将镜像推到我们自己的仓库中

img

image-20230526155617944

发布镜像的时候,也是按照一层一层的提交的

最后补充一个网络上找到的图片,现在看这张图就能更清晰的明白其中的原理了

img

Docker网络

开始理解 docker

一开始,咱们思考一下,宿主机怎么和容器通信呢?

img

说容器之间是相互隔离的,那么他们是否可以通信?又是如何通信的呢?

开始探索

我们先来看看咱环境中的镜像都有些啥,有 xmtubuntu

image-20230526155856278

再来看看宿主机的网卡信息

ip addr 来查看咱们宿主机的网卡信息

img

我们发现有一个 docker0,是因为我们的宿主机上面安装了 docker 的服务,docker 会给我生成一个虚拟网卡,图中的这个 docker0 就是虚拟网卡信息

创建并启动一个 docker 命名为 ubuntu1

1
docker run -it --name ubuntu1 -P xmtubuntu

查看一下宿主机网卡信息

查看宿主机的网卡信息

img

再查看 ubuntu1 的网卡信息,docker 也会默认给我们的容器分配 ip 地址

img

可以发现宿主机的网卡信息 docker0 下面多了 117: veth838e165@if116:,ubuntu1 的网卡信息上也正好有 116: eth0@if117

我们发现这些 veth 的编号是成对出现的,咱们的宿主机就可以和 ubuntu1 进行通信了

使用宿主机(docker0)和 ubuntu1 互相 ping

docker0 ping ubuntu1 ok

img

ubuntu1pingdocker0,同样的 ok

img

咱们可以尝试再创建并启动一个 docker 命名为 ubuntu2,方法和上述完全一致

1
docker run -it -P --name ubuntu2 xmtubuntu

进入容器,使用 ip a 查看到 ubuntu2 的网卡信息。

进入容器,使用 ip a 查看到 ubuntu2 的网卡信息

img

宿主机上面查看网信息

img

宿主机上面又多了一个 veth , 119: veth0b29558@if118

ubuntu2 上的网卡信息是 118: eth0@if119,他们同样是成对出现的,小伙伴看到这里应该明白了吧

ubuntu1 ping ubuntu2 呢?

ubuntu1 对应 172.18.0.2

ubuntu2 对应 172.18.0.3

1
2
3
4
5
# docker exec -it ubuntu1 ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.070 ms
64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.077 ms

仍然是可以通信,非常 nice

原理是什么?

上述的探索,我们发现宿主机创建的容器,都可以直接 ping 通宿主机,那么他们的原理是啥呢?

细心的 xdm 应该可以看出来,上述的例子中,veth 是成对出现的,上述宿主机和容器能够进行网络通信,得益于这个技术 veth-pair

veth-pair

veth-pair 是一对虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连

正是因为这个特性,veth-pair 在此处就是充当了一个桥梁,连接各种虚拟设备

img

通过上图我们可以得出如下结论:

  • ubuntu1 和 ubuntu2 他们是公用一个路由器,也就是 docker0,ubuntu1 能 ping 通 ubuntu2 是因为 docker0 帮助其转发的
  • 所有的容器在不指定路由的情况下,都是以 docker0 作为路由,docker 也会给我们的容器分配一个可用的 ip
  • docker0 是在宿主机上面安装 docker 服务就会存在的

img

那么通过上图我们就知道,容器和宿主机之前是通过桥接的方式来打通网络的。

Dcoker 中所有的网络接口都是虚拟的,因为虚拟的转发效率高呀,当我们删除某一个容器的时候,这个容器对应的网卡信息,也会被随之删除掉

那么我们可以思考一下,如果都是通过找 ip 地址来通信,如果 ip 变化了,那么我们岂不是找不到正确的容器了吗?我们是否可以通过服务名来访问容器呢?

当然是可以的,当我们在创建和启动容器的时候加上–link 就可以达到这个效果

我们再创建一个容器 ubuntu3,让他 link 到 ubuntu2

1
docker run -it --name ubuntu3 -P --link ubuntu2 xmtubuntu
1
2
3
4
5
6
# docker exec -it ubuntu3 ping ubuntu2
PING ubuntu2 (172.18.0.3) 56(84) bytes of data.
64 bytes from ubuntu2 (172.18.0.3): icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from ubuntu2 (172.18.0.3): icmp_seq=2 ttl=64 time=0.085 ms
64 bytes from ubuntu2 (172.18.0.3): icmp_seq=3 ttl=64 time=0.092 ms
64 bytes from ubuntu2 (172.18.0.3): icmp_seq=4 ttl=64 time=0.073 ms

很明显,我们可以到看到 ubuntu3 可以通过服务名 ubuntu2 直接和 ubuntu2 通信,但是反过来是否可以呢?

1
2
docker exec -it ubuntu2 ping ubuntu3
ping: ubuntu3: Name or service not known

不行?这是为什么呢?

我们来查看一下 ubuntu3 的本地 /etc/hosts 文件就清楚了

img

看到这里,这就清楚了 link 的原理了吧,就是在自己的 /etc/hosts 文件中,加入一个 host 而已,这个知识点我们可以都知悉一下,但是这个 link 还是好搓,不好,他需要在创建和启动容器的时候使用,用起来不方便

那么我们有没有更好的办法的呢?

自定义网络

可以使用 docker network ls 查看宿主机 docker 的网络情况

image-20230526162225858

网络模式

bridge
桥接,docker0 默认使用 bridge 这个名字

host
和宿主机共享网络

none
不配置网络

container
容器网络连通,这个模式用的非常少,因为局限性很大

现在咱们可以自定义个网络,来连通两个容器

自定义网络

自定义应该mynet网络

1
2
# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
9a597fc31f1964d434181907e21ff7010738f3f7dc35ba86bf7434f05a6afc4a

docker network create
创建一个网络

–driver
指定驱动是 bridge

–subnet
指定子网

–gateway
指定网关

此处我们设置子网是 –subnet 192.168.0.0/16,网关是 192.168.0.1,那么我们剩下可以使用的 ip 就是 192.168.0.2 – 192.168.255.254 , 192.168.255.255 是广播地址

清空已有的容器

清空所有测试的容器,减去干扰

创建并启动 2 个容器,分别是 ubuntu1 和 ubuntu2

1
2
# docker run -it -P --name ubuntu1 --net mynet xmtubuntu
# docker run -it -P --name ubuntu2 --net mynet xmtubuntu

此时我们可以查看一下宿主机的网卡信息,并验证两个容器直接通过容器名字是否可以通信

img

我们思考一下自定义网络的好处

咱们自定义 docker 网络,已经帮我们维护好了对应关系,这样做的好处是容器之间可以做到网络隔离,

例如

一堆 redis 的容器,使用 192.168.0.0/16 网段,网关是 192.168.0.1

一堆 mongodb 的容器,使用 192.167.0.0/16 网段,网关是 192.167.0.1

这样就可以做到子网很好的隔离开来,不同的集群使用不同的子网,互不影响

gKWXC34bTV.png!large

那么子网间是否可以打通呢?

网络连通

两个不同子网内的容器如何连通了呢?

img

我们绝对不可能让不同子网的容器不通过路由的转发而直接通信,这是不可能的,子网之间是相互隔离的

但是我们有办法让 ubuntu3 这个容器通过和 mynet 打通,进而转发到 ubuntu1 或者 ubuntu2 中就可以了

打通子网

我们查看 docker network 的帮助手册

1
docker network -h

image-20210807203841520

可以使用 docker network connect 命令实现,在查看一下帮助文档

1
2
3
4
5
6
# docker network connect -h
Flag shorthand -h has been deprecated, please use --help

Usage: docker network connect [OPTIONS] NETWORK CONTAINER

Connect a container to a network

开始打通

1
docker network connect mynet ubuntu3

这个时候我们可以查看一下 mynet 网络的详情

IAZ0ptRRqo.png!large

可以看到在 mynet 网络上,又增加了一个容器,ip 是 192.168.0.4

没错,docker 处理这种网络打通的事情就是这么简单粗暴,直接在 ubuntu3 容器上增加一个虚拟网卡,让 ubuntu3 能够和 mynet 网络打通

img

宿主机当然也相应的多了一个 veth

现在,要跨网络操作别人的容器,我们就可以使用 docker network connect 的方式将网络打通,开始干活了

Compose 内容编排官网初步体验

我们前面的文章学习了 docker ,为什么还要 Compose 呢?Compose 到底是个啥玩意?

Docker Compose 可以来轻松的高效的管理容器,定义运行多个容器

咱们一起来看看官方的介绍Docker Compose overview

Compose 是什么

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

讲了三个点:

  • Compose 可以定义和运行多个容器
  • 需要使用给到 YAML 配置文件
  • 单个命令就可以创建和启动所有的服务

Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases.

Docker Compose 可以运行在所有的环境中

Using Compose is basically a three-step process:

1、Define your app’s environment with a Dockerfileso it can be reproduced anywhere.

2、Define the services that make up your app in docker-compose.ymlso they can be run together in an isolated environment.

3、Run docker compose up and the Docker compose command starts and runs your entire app. You can alternatively run docker-compose up using the docker-compose binary.

三个步骤:

  • 需要定义好 Dockerfile ,保证它在任何环境都能运行
  • 在 docker-compose.yml 文件中定义好 services,那么这个 yml 文件如何写呢?services 咋定义呢
  • 使用 docker-compose binary 启动项目

总结上述官方说明:

Docker Compose 用于批量容器编排

如果一个项目中的多个微服务(几十个或者几百个),我们都一个一个的使用 docker run 是不是很傻?而且对于运维来说也是一个非常不友好的事情,优化这样的问题,我们有了 Docker Compose

Compose 在 Docker 中默认就有吗?

Docker 中默认是没有 Compose 的,Compose 是 Docker 官方的开源项目,我们使用 Compose ,是需要自己另外安装的

Compose 的 yml 文件如何编写?

一起来看看官方文档的 yml 是怎样的结构:

A docker-compose.yml looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: "3.9"  # optional since v1.27.0
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
  • services

指定服务

  • volumes

指定挂载卷

通过官方文档的上述说明,我们可以知道 Compose 有这么 2 个重要的概念:

  • services 服务,就是容器和相关的应用
  • 项目,就是一组关联的容器

Compose 安装

Docker Compose install

1、咱们选择在 linux 下面安装 Docker Compose ,运行如下指令进行安装

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

安装成功后在我们的 linux 目录 /usr/local/bin/ 会有 docker-compose 程序

2、 给程序 docker-compose 加上可执行的权限

1
sudo chmod +x /usr/local/bin/docker-compose

3、安装成功,查看 docker-compose 的版本,看到如下信息即为成功

1
2
3
4
5
# docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.7.10
OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019

Compose 官方案例体验

Try Docker Compose

咱们安装好了 docker-compose ,我们一起来体验一下官方的 例子,先会使用,再来研究

准备环境和代码

1、创建 compose 测试目录,自己可以在任意目录下执行如下指令

1
2
mkdir composetest
cd composetest

2、编写 app.py 文件

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)

该 py 文件的功能就是,注册了一个路由为 /,我们访问服务器的 / 时,程序会去读取 redis 的计数器,来确认这个网站是访问了第几次了

3、创建一个文件 requirements.txt,用于之后的安装

requirements.txt

1
2
flask
redis

创建 DockerFile 文件

写 Dockerfile 文件

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
# 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"]

Dockerfile 文件中的含义是:

  • 基于 python:3.7-alpine 构建镜像
  • 设置工作目录为 /code
  • 设置 FLASK_APP 环境变量
  • 设置 FLASK_RUN_HOST 环境变量
  • 运行 apk add –no-cache gcc musl-dev linux-headers 指令
  • 拷贝文件 requirements.txt 到容器中
  • 运行 pip 安装 requirements.txt 中的组件
  • 暴露 5000 端口
  • 拷贝 . 到 .
  • 执行 flask run 命令

定义 Compose 文件(yml 文件)

docker-compose.yml

1
2
3
4
5
6
7
8
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"

这个 compose 文件定义了 2 个服务

  • web 服务,暴露的是 5000 端口
  • redis

构建和运行我们的 Compose

运行指令前,我们来查看一下我们的 compose 测试目录都有些啥了:

image-20230528150629967

开始构建

1
#docker compose up

image-20230529121718417

这个是因为我已经构建过一次了。

实际上的过程是这样的:

可以看到执行指令 docker-compose up 之后, Compose 也是在一层一层的执行,并且我们可以看到 compose 是最先建立了一个自定义网络

Creating network "composetest_default" with the default driver

看到这里,我们发现 Compose 会自动帮我们创建 redis 容器和 web 容器

Creating composetest_web_1 ... done
Creating composetest_redis_1 ... done

最后,我们看到 Compose 帮我们将 redis 和 web 启动起来了,程序正常运行,

咱们在宿主机使用 curl 命令,来请求一下这个 web 服务

1
curl http://localhost:5000/

image-20230529121840283

image-20230529121908658

果然 ok,官方的 compose 体验 no problem ,nice

查看一下镜像

使用 docker images 查看一下镜像,发现多了 composetest_web,python,redis alpine 版本 ,这些也都是 compose 自动为我们做的,非常方便(哥们没有python呢?,可好像不影响)

image-20230529122011021

查看一下网络

1
docker network ls

compose 构建的时候,一开始就会为我们创建一个网络

image-20230529122115838

疑问?

细心的朋友发现了,我们的容器名字为什么是 composetest_web_1 , 和 composetest_redis_1

这个是 Docker Compose 里面的一种规则,便于标识对应的副本

例如,compose 里面对于容器会是这样的命名:

1
文件名_服务名_num

多个服务器集群的时候,这个 num 的作用就体现出来的,num 标识第几个副本

网络规则

多个容器只要是在一个局域网内,就可以互相 ping 通,相互通信,通过域名访问

例如 mysql 集群里面的服务,我们就可以访问 mysql:3306 , compose 就会给我们访问到 mysql:3306 这个服务

我们可以查看上面的 docker compose 给我们新建的自定义网络

1
docker network ls

image-20230529122115838

docker network inspect composetest_default

image-20230529122349506

发现上面的例子, web 服务和 redis 服务,是在同一个网络下的,所有可以相互通信

停止 compose

咱们可以使用 ctrl + c 停止 compose

也可以通过 docker-compose down 停止 compose

停止 compose ,那么 compose 里面涉及的服务,全部都会停止掉

docker-compose down

image-20230529122522722

1
2
3
4
5
停止 composetest_web_1
停止 composetest_redis_1
删除 composetest_web_1
删除 composetest_redis_1
移除自定义网络 composetest_default

Compose编写规则及wp实战

yaml 规则

docker-compose.yaml 是 Compose 的核心,咱们一定要学会 yaml 编写的规则

当然,咱们还是查看官方文档,compose 部分

Compose file version 3 reference

yaml 文件的结构分为三层:

  • version

版本号

  • services

服务名

  • 其他配置,如网络,挂载等公共的东西
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version:''   # 版本号
services:
服务器1 web:
当前服务的配置,这个部分就可以和DockerFile 写的差不多了
build
depends_on
...
服务2 redis:
...
服务n mongodb:

network:

volume:

...
其他配置

上面说的到版本号在哪里找呢?

进入 dockerhub 网页,docs.docker.com/compose/compose-fi…

img

官网上的这些都是可以使用的版本,如官网给出的例子:

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
version: "3.9"
services:

redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure

...

networks:
frontend:
backend:

volumes:
db-data:

services 和 其他命令都可以写什么呢?

services 下面可以写的命令非常的多,文档上也讲的非常的详细

image-20230529122959357

image-20230529123013090

image-20230529123020866

image-20230529123033858

image-20230529123042434

如上命令还是非常的多,我们一下子肯定也是记不住的,需要我们慢慢去熟悉,用的多了,写的多了,看得多了,知识慢慢的也根深蒂固了

咱们学习的方法有:

  • 多看官方文档,看官网的例子
  • 看开源项目,看看别人的 docker-compose.yaml 是如何编写的

实战 - 搭建 wp 博客

咱们来使用 docker-compose.yaml 的方式来搭建我们的个人博客,感受一下一键部署的魅力

创建工作目录

1
2
mkdir my_wordpress
cd my_wordpress

编写我们的 docker-compose.yaml 文件

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
version: "3.9"

services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress

wordpress:
depends_on:
- db
image: wordpress:latest
volumes:
- wordpress_data:/var/www/html
ports:
- "8888:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
wordpress_data: {}

image-20230529123950476

docker-compose up 一键部署服务并启动

在工作目录执行如下指令一键部署服务

1
#docker-compose up

我们也可以在让服务在后台启动

1
#docker-compose up -d

启动之后我们可以看到程序先去创建网络,创建对应的挂载卷

开始创建并启动对应的容器

  • my_wordpress_db_1
  • my_wordpress_wordpress_1

查看一下博客搭建的效果

我们可以访问博客地址:服务器的 IP:8888(直接设置就可以有了)

image-20230529123852231

nice,使用 docker-compose.yaml 搭建个人的 wordpress 博客成功!!!

wordpress 的界面是这个样子的,里面的功能很多,直接就在页面上定制化我们自己的页面,非常方便,xdm 可以尝试一波,不亏

image-20230529124013650

Docker面试题

docker总结和面试题整理

docker面试题-地鼠文档 (topgoer.cn)

  • 标题: Docker你好
  • 作者: Olivia的小跟班
  • 创建于 : 2023-04-29 15:15:18
  • 更新于 : 2023-06-24 20:37:28
  • 链接: https://www.youandgentleness.cn/2023/04/29/Docker你好/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
此页目录
Docker你好