Docker你好
题记
本文用于记录docker的相关学习。
Docker是什么
Docker网址:docker
docker对进程进行封装隔离,属于操作系统层面的虚拟化技术
由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器
docker 应用场景
- 自动化测试和持续集成、发布
- Web 应用的自动化打包和发布
- 后台应用易部署
docker 的优势
- 快速, 一致的交付应用程序
- 可移植,可扩展
- 轻巧,快速,经济,高效,压榨
linux
自身资源
Docker 能做什么?
先来说说Docker和虚拟机有啥不一样的
以前的虚拟机这样的,系统占用资源大,很多步骤是冗余的,并且启动还很慢,不能忍
现在的 Docker 是这个样子的,
容器之间互相隔离,互补干扰,一起运行在同一个操作系统上,最大化使用操作系统资源
Docker 技术和虚拟机技术的不同?
- 每个容器间都是相互隔离的,他们有属于自己的文件系统,相互不会有影响
- 容器没有自己的内核,没有自己的硬件,容器内的应用是直接运行在宿主机的内核中
- 传统的虚拟机是虚拟出一个硬件,运行完成的操作系统,在其上面运行应用
那么 Docker 具体能做什么?
做 DevOps
做 DevOps 有如下几个提升点:
- 应用可以更快捷的部署和交付
以前麻烦的安装步骤一去不复返,使用 Docker 容器化后,打包镜像发布测试,一键部署及运行
- 可以更方便的升级和扩容
使用 Docker,将项目打包成镜像,升级方便,扩容方便
- 开发,运维,测试都会更简单
再也不用担心开发环境,测试环境,运维环境不一致的情况了
- 更高效的利用资源
Docker 是运行在宿主机的内核中,可以在这台物理主机上部署多个Docker实例
Docker的组成
Docker 使用客户端 - 服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器
Docker的三个基本概念:
- 镜像
相当于是一个 root 文件系统,类似于一个模板,这是静态的
- 容器
相当于从模板拉出来的一个实例,容器通过镜像来创建,我们可以对他做创建,启动,停止,暂停,删除等操作
- 仓库
用来保存镜像的,可以看做是一个代码控制中心
Docker的安装和使用
day9 wsl2和docker的安装与迁移 |青训营笔记 - 掘金 (juejin.cn)
docker run 的流程
- docker run 现在本地找对应的镜像,若有则直接运行
- 若没有就去 docker hub 上下载,若有就下载到本地后运行
- 若没有就直接报错
Docker的底层原理
Docker 是如何工作的?
docker 是一个 C/S 模型,docker 的后台守护进行运行在主机上,客户端和服务端通过套接字 Socket 通信
docker 服务端收到 docker 客户端的指令时,则执行该指令
为什么 Docker 比 虚拟机快呢?
在网络上找了一张图,咱们对比一下就明确了
如图,Docker 比虚拟机快的原因如下:
- docker 比虚拟机的抽象层更少
- docker 利用的是宿主机的内核,而虚拟机是需要新建一个 OS
基于如上2点,虚拟机启动时,会加载操作系统,启动慢,时间基本上是分钟级的
docker 启动的时候,不需要加载操作系统内核,因此快,时间基本上是秒级的
Docker的常用命令
基本帮助命令
1 | # 查看 docker 的基本版本信息 |
咱们可以看官方的帮助文档:Reference documentation
镜像命令
docker images 查看镜像
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
查看本机上的镜像:
关键字 | 解释 |
---|---|
REPOSITORY | 仓库源 |
TAG | 镜像标签 |
IMAGE ID | 镜像 ID |
CREATED | 创建时间 |
SIZE | 镜像大小 |
可选参数:
1 | Options: |
docker search 搜索镜像
搜索redis为例
- NAME(名称):镜像的名称或仓库名称。
- DESCRIPTION(描述):镜像的描述或说明,通常包含有关镜像内容和用途的信息。
- STARS(收藏数):镜像的收藏数,表示该镜像受欢迎程度的指标。
- OFFICIAL(官方镜像):如果该镜像是官方维护的镜像,则显示为”OK”,表示来自Redis官方或Docker官方的镜像。
- AUTOMATED(自动构建):如果该镜像是通过自动化流程进行构建的,则显示为”OK”,表示镜像是通过自动构建过程生成的。
加上参数
过滤 STARS 大于 2000 的 镜像
我们也可以在 docker hub 上面直接在页面上搜索镜像
docker pull下载镜像
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
下载 redis 镜像为例
因此上述的下载操作:docker pull redis
与 docker pull docker.io/library/redis:latest
一致
dockerhub 上面 可以查看到 redis 支持的版本
我们下载一个 6 版本的 redis
可以看到 下载 版本 6 的 redis 的时候,没有分层下载了,说明在上述看到的分层下载他们是共用的
查看刚才的安装的镜像
docker rmi 删除镜像
1 | 删除单个镜像 docker rmi -f 容器ID |
容器命令
容器是基于镜像创建的,我们来下载一个 ubuntu 镜像
1 | docker pull ubuntu |
docker run 新建并启动容器
docker run [参数] 镜像名字 [指令] [参数]
常用参数说明:
1 | --name="xxx" # 运行容器的名字 |
启动 容器里面的 ubuntu,通过主机名字,我们已经可以看出来主机切换了
退出容器
- 键入 exit 命令,容器会退出
- 使用快捷键 Ctrl + P + Q ,回到主机,容器不会退出
docker ps 查看容器
docker ps [OPTIONS]
可选参数:
1 | # 查看正在运行的容器 |
docker rm 删除容器
1 | docker rm 容器ID # 删除未运行的容器 |
start,restart,stop,kill 开启,重启,停止,强制停止容器
1 | docker start 容器ID |
常用其他命令
docker run -d 后台启动容器
1 | # 后台启动一个 ubuntu |
发现没有容器正在运行
原因如下:
- docker 后台启动服务,需要有一个前台的进程,否则 docker 发现没有应用,则会将该服务停止
我们主动加一个前台进程,看看效果
1 | 临时加上一个前台进程 |
可以看出,docker ps 命令已经可以查看到正在运行的容器了,OK
docker logs 查看日志
docker logs [参数] 容器 ID
1 | Options: |
查看上述容器的日志
docker top 查看容器中进程信息
docker top 容器 ID
docker inspect 查看镜像元数据
docker inspect 容器 ID
输出信息中省略了大量信息
docker exec 进入当前运行的容器
docker exec [参数] 容器 ID 指令 [指令的参数]
docker attach 进入容器中正在执行的程序
docker attach 容器 ID
docker exec 和 docker attach 的区别
docker exec:进入容器,会新开一个终端,可以正常操作
docker attach:进入容器正在执行的终端,不会启动新的终端
docker cp 将容器内文件拷贝到主机内
docker cp 容器 ID: 容器文件路径 主机路径
docker stats:查看docker内服务内存状态
Docker部署nginx
搜索 nginx 镜像
- 使用 docker search nginx
- 或者在 dockerhub 上搜索 nginx,具体的版本和详细信息会更加全面,一般使用官方的
拉取 nginx 镜像
拉取 nginx 镜像,我们这里就拉取最新版本的 nginx
我之前就有了nginx镜像,所以是这样的
创建并运行容器
- 新建一个容器命名为 nginx1
- nginx 默认端口是 80,将 docker 容器中的 80 端口映射程 主机中的 8888 端口
- 设置后台运行 nginx 容器
验证
使用 curl 命令,访问一下 主机的 8888 端口,查看是否访问 OK
咱们也可以进入到 nginx docker 容器中,直接访问 80 端口
小结
因为我们在创建 nginx1 容器的时候,将主机的 8888 端口,映射到了 容器 nginx1 的 80 端口,因此可以访问主机的 8888 端口来访问到 nginx1 容器中的 80 端口
此时,可以访问我的阿里云服务器的 8888 端口,实际是可以访问到我的 nginx1 容器中的 nginx 服务器
docker run –-help
用法: docker run [参数] 镜像 [命令] [命令的参数列表…]
Run a command in a new container
参数:
1 | -a, –attach list Attach to STDIN, STDOUT or STDERR |
安装portainer
1.拉取portainer镜像
2.运行portainer镜像
3.访问portainer可视化界面
这个就是本地的docker
portainer其他操作可参考这篇文章:Docker可视化界面portainer的安装与使用
镜像相关原理
镜像是什么?
镜像是一种轻量级的,可执行的独立的软件包。
镜像用来打包软件的运行环境和基于运行环境开发的软件,它包含运行某些软件所需要的所有内容,例如:代码,运行时库,环境变量和配置文件等等
所有的应用,可以直接打包 docker 镜像,一键部署,一键运行
得到镜像方式有哪些?
- 直接拷贝其他 docker 镜像
- 自己制作一个镜像 DockerFile
- 从远程仓库下载,如 dockerhub
Docker 镜像的加载原理
UnionFS,是联合文件系统,还记的我们 docker 学习二 里面安装 redis 的时候,出现的分层下载吗
这就是联合文件系统
1 | UnionFS 联合文件系统,是一种分层,轻量级并且高性能的文件系统 |
特性:
联合文件系统一次同时加载多个文件系统,联合加载会把各层的文件系统叠加起来,最终的文件系统会包含所有底层的文件和目录
Docker 的镜像加载原理是什么呢?
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 看看效果
Docker 是按照层级进行下载,之前下载过的层级就不会再次下载了,我们可以通过 docker inspect 查看 redis 的细节
1 | # docker inspect redis:latest |
如上结果我们可以看到,redis 的层级与我们 pull 拉取镜像时候的层级一致
那么 Docker 为什么要采用分层下载呢?
主要是为了共享资源
例如我们的多个镜像都是从基础镜像构建而来,那么宿主机只需要在机器上保留一份基础镜像即可,并且内存中也只需要加载一份基础镜像,就可以为所有需要的容器服务,而且镜像的每一层也都是可以共享的
我们可以这样来理解:
所有的 Docker 镜像都来源于基础镜像,我们增加或修改镜像内容的时候,就会在当前镜像层上面,新建一个镜像层,这就例如 windows 里面的一个安全补丁
如上面图例:
我们在镜像的第一层,放置 file1,file2,镜像的第二层放置 file3,file4,镜像的第三层放置 file3.1 (file3.1 是 file3 的一个新版本)
那么,我们在打包镜像的时候,就会合并出 一个镜像里面,有 4 个文件,此处也就是 4 个 layer
当我们下载这个最终合并的镜像时,就会一次下载上述的 4 个 layer
Docker 镜像的特点:
Docker 镜像默认都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部
上面说的这一层就是咱们的容器层,容器层下面是镜像层,如下图所示
如何提交咱们的镜像
Docker 提交原理和命令与 Git 类似
docker commit
提交当前容器,成为一个新的版本
我们一般会这样使用
1 | docker commit -m="描述信息" -a="作者" 容器ID 目标镜像名:[TAG] |
举个例子:
如果我们想要保存自己的容器当前状态,咱们就可以通过 commit 来提交,获得一个想要的镜像,这就有点像使用虚拟机打快照一样
容器数据卷
什么是容器数据卷
思考一个问题,我们为什么要使用 Docker?
主要是为了可以将应用和环境进行打包成镜像,一键部署。
再思考一个问题,容器之间是相互隔离的,如果我们在容器中部署类似 mysql 这样的组件,如果把该容器删除掉,那么 mysql 的数据也会被删掉了,数据丢失了,咱们删库跑路真刺激
事实上,我们可不能让这么有风险的事情存在,因此有了卷技术
卷技术是容器之间可以共享数据的技术,Docker 容器中产生数据,将数据同步到本地
例如咱们将 Docker mysql 容器中的 /usr/mysql 目录挂载到宿主机的 /home/mysql 目录
使用卷技术,我们就可以让数据得以持久化
实际上操作起来就是挂载目录,将 Docker 容器里面的目录,挂载到宿主机上的某个目录,这就可以将数据持久化和同步了, Docker 容器间的数据共享仍然是这样做的
咱们如何使用数据卷?
mysql 实战一波
咱们再来一个实战,我们一起来看看数据卷如何使用
下载 5.7 版本的 mysql docker 镜像,也可以下载其他版本,这个没有关系
启动镜像,直接使用 -v 来挂载目录
使用方式
1 | docker run -it -v 主机目录:容器的目录 |
开始启动镜像
咱们可以参考 dockerhub 上的文档
1 | 解释一下上述命令 |
咱们可以通过 window 的 workbench 来远程连接一下 mysql
我的是云服务器,因此输入云服务器的地址,端口填入 8888 端口
默认用户名是 root , 密码是 123456
测试连接 ok ,我们可以来进入数据库
咱们在 workbench 中新建一个数据库
看看这个数据库是否会在我们的宿主机上面有同步
果然是有的,再次 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只读,只能宿主机才能写
查看数据挂载卷
查看挂载具体目录
数据卷容器
Dockerfile
Dockerfile 就是用来构建 docker 镜像的构建文件,关于 Dockerfile 详细的我们在后面一期说到,此处先用用
他是一个命令脚本,通过这个脚本可以生成我们想要的镜像,镜像是分层的,一层一层的,脚本也是一个一个的命令,每个命令就是一层
我们可以来看一个小小的例子
自己写一个 Dockerfile 来构建自己的镜像,我们以 ubuntu 为例子
写一个简单的 dockerfile1
1 | vim dockerfile1 |
解释一下:
- FROM
来源基础镜像为 ubuntu
- VOLUME
挂载,可以匿名挂载,也可以是具名挂载 ,默认会挂载到 docker 专门挂载的目录
- CMD
指定可以使用命令
构建我们自己的镜像
1 | docker build -f dockerfile1 -t xiaomotong/ubuntu . |
通过上述我们可以看到 docker 构建镜像的时候是一层一层的,一个命令一个命令的执行的,一个命令就是一层
- docker build
构建我们自己的镜像
- -f
指定 dockerfile 的文件
- -t
目标,即我们 docker 镜像的名字
后面跟着生成镜像的位置
通过我们构建的镜像的创建并启动容器
1 | docker images |
执行完上述命令之后,我们可以看到容器里面目录如下:
从图中我们可以看到 volume1 和 volume2,这就是我们刚才构建容器时候做的匿名挂载,那么我们使用 docker inspect 命令来看看这俩挂载卷具体是挂载到宿主机的哪个位置,并测试一个同步数据
1 | docker inspect b29995f4178d |
通过 docker inspect 可以看出 volume1 和 volume2 具体是挂载到宿主机的目录为 /var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data
, 和 var/lib/docker/volumes/975ae74c8716f5e85ccf784c716291cffda2158baf6b3f9e145ffc1ea353cb7b/_data
咱们在容器挂载中创建一个文件,测试是否可以同步数据
在容器中的 volume1 中创建一个文件 xiaomotong.txt,写入字符串 hello world
1 | root@b29995f4178d:/# cd volume1 |
查看宿主机对应的挂载目录,确认是否同步数据
1 | root@iZuf66y3tuzn4wp3h02t7pZ:/var/lib/docker/volumes/a1fd1edec5784f1153a318003bba4279b86fd2dd71b401be5864ed9b868d7332/_data# ls |
果然同步 ok,nice
那么我们有没有想过,现在是容器和宿主机之间同步数据,可是容器和容器之间是如何同步数据的呢?
数据卷容器
数据卷容器,例如容器 1 通过指令 --volumes-from
挂载到容器 2 上的某个目录,那么容器 2 就是父容器,这个时候就实现了两个容器之间数据同步,容器 2 就是数据卷容器
来实战一个小例子:
用刚才我们制作的镜像,创建 2 容器,先创建 docker2 ,再创建docker1,并且 docker1 挂载到 docker2 上,并在 docker2 挂载的目录,volume1 里面创建一个文件,xiaomotong.txt,我们验证 docker1 volume1 里面是否也有这个文件
1 | docker images |
b26faaedefac 是我们自己制作的镜像的 ID
主要是使用 --volumes-from
指令,我们就可以将两个容器进行挂载
docker1
1 | root@3ed3ca51118f:/volume1# ls |
docker2
1 | root@e9e1a0c46331:/volume1# ls |
果然,两个容器互相同步数据了
上述的例子,不仅仅是两个容器之间挂载,进行同步数据,多个容器挂载也是同样的道理,例如再来一个容器 docker3 挂载到 docker2 上,也是一样的效果
那么他们都是如何同步数据的呢?
容器间同步数据的原理是通过拷贝的方式,例如在 docker2 上面的挂载上创建了一个文件 2,这个时候 docker1 挂载了 docker2,也就是说 docker2 是父容器,那么 docker1 就会将文件 2 拷贝到自己对应的挂载中
反之,如果 docker1 在自己的挂载中创建了文件 1,那么文件 1 也会被 docker2 拷贝到自己的挂载中
若这个时候,我们删除 docker2 ,那么 docker1 挂载中的文件会丢失吗?
答案是不会的,因为他们是通过拷贝的方式,并不是共享一个副本
那么我们回顾一下上一期我们创建 mysql 容器的时候,若我们创建多个,那么我们是不是就很容易让他们数据同步,且删除其中一个容器,数据也不会丢失了
例如:
1 | 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 |
Dockerfile编写和实战
我们开始来一起学习 DockerFile 的知识点
DcokerFile 是用来构建 docker 镜像的文件,是一个命令参数脚本
一般 docker 镜像的构建步骤:
1、编写一个 dockerfile 文件
2、docker build 构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(咱们可以发布到 DockerHub,也可以发布到阿里云上面)
我们来看看官方的镜像是咋玩的
例如我们在 DockerHub 上搜索 ubuntu ,看看官网的 DockerFile 是啥样子的
点击链接我们会进入到 git 仓库上,也是 DockerFile
咱们看到就 3 行 Docker 命令,是官方做成镜像的 DockerFile,所以这个官方的 ubuntu 镜像是非常简单的,阉割版本的,甚至连 clear 命令都没有,ll 命令也没有
很多的官方镜像包都是非常简单的,很多功能都是没有的,我们通常会自己搭建自己的镜像来满足我们的各种需求
DockerFile 的构建过程
官方能构建镜像,我们也可以自己的镜像
DockerFile 的基础知识:
- 每个 DockerFile 的保留字(指令),都必须是大写的
- DockerFile 脚本执行是按照顺序执行的
- #表示注释
- 每一个指令都会创建提交一个新的镜像层,并提交
可以在网络上找到这样的图片,可以看到镜像是一层一层的,可以在浏览器上搜索到 DockerFile 里面的指令解释
dockerfile 是面向开发的,我们以后在做项目的时候,是直接发布一个镜像,交付的是一个镜像,就需要编写 DockerFile 文件,这个文件非常的简单!
咱们必须要掌握 Docker 镜像,逐渐成为企业交付的标准了。
咱们学习的过程是先会使用别人的东西,再去研究别人是怎么写的,进而我们也学会如何去写,去开发
例如:
我们先学习使用了,
Docker Images:通过 DockerFile 构建生产的镜像,最终发布和运行的产品
Docker 容器:容器服务就是镜像运行起来的服务器
现在我们开始详细学习 DockerFIle:构建文件,定义了一切的步骤,这是源代码
DockerFile 的指令
图片来源于网络,我们一一解释一波
FROM
基础的镜像,一切都是从这里开始的
MAINTAINER(已过时,推荐使用LABEL
)
指明镜像是谁写的,写下自己的姓名和邮箱
RUN
镜像构建的时候需要运行的命令
ADD
加入某些配置,例如加入 mysql 的压缩包,添加内容
WORKDIR
镜像的工作目录
VOLUME
挂载目录
EXPOSE
暴露端口 和 -p 是一个效果
CMD
指定这个容器启动的时候执行的命令,只会是最优一个指令进行生效,会被替代
ENTRYPOINT
指定这个容器启动的时候执行的命令,可以追加
ONBUILD
当构建一个被继承的 DockerFIle ,这个时候就会运行 ONBUILD 的指令,触发相应的动作
COPY
与 ADD 类似,此命令是将文件拷贝到镜像中
ENV
构建的时候设置环境变量
乍一看感觉 CMD 和 ENTRYPOINT 功能好像差不多,但是还是不太清楚具体区别在哪里,文章末尾会有详细说明
实战
我们自己来做一个自定一个 ubuntu
镜像
官方的 ubuntu
是阉割版本的,很多工具和命令都是不支持的,那么我们就自己加进去,自给自足
自己写一个 DockerFile
这里需要注意的是,基本上 99% 的镜像,都是基于这个基础镜像 scratch,我们可以看到官方的 DockerFIle 也是基于这个镜像来玩的
docker-brew-ubuntu-core/Dockerfile
那么我们可以基于这个 ubuntu 来进行自定义,加入一些我们需要的工具,如 vim
,ifconfig
等
1 | FROM ubuntu |
开始构建
docker build -f dockerfile2 -t xmtubuntu .
Windows下在dockerfile文件的目录下运行这个命令
如果不在 DockerFile 中写入 apt-get update 更新源,会出现下面这个问题,这个要注意
执行上述命令,会看到如下打印信息,最终会看到 Successfully
,即为构建成功
验证结果
docker images查看我们的镜像
docker history 查看我们镜像的构建过程
xmtubuntu 这个镜像的构建过程我们可以清晰的看出都执行了哪些步骤,当然,同样的方式,我们也可以看看官方的镜像是如何构建的,我们来看看官方 ubuntu 的
官方的就很简单,阉割了很多东西,我们可以看出官方的 ubuntu 就 2 个步骤,第一个是加入 ubuntu
压缩包,第二个就是 /bin/bash
我们查看我们的自定义镜像 xmtubuntu
CMD 和 ENTRYPOINT 的区别
CMD
指定这个容器启动的时候执行的命令,只会是最优一个指令进行生效,会被替代
ENTRYPOINT
指定这个容器启动的时候执行的命令,可以追加
如何理解呢?我们来做一个对比试验就可以很好的理解上述的解释说明,docker 里面有很多命令会有这样的微小区别,我们可以举一反三,慢慢深入学习
CMD 的例子
写一个简单的 DockerFile 文件名为 dockerfile-cmd
1 | FROM xmtubuntu |
构建镜像
创建并启动容器
docker run 容器id
,可以看到如下效果
我们尝试在启动容器时候追加命令
docker run 容器id -l
,就会有如下报错
原因如下:
使用 CMD
指令是(例如我们的例子是 ls -a
),我们在启动容器的时候,后面追加的命令(-l
)会把 ls -a
替换掉,由于 -l
不是一个命令,因此报错
ENTRYPOINT 的例子
写一个简单的 DockerFile 文件名为 dockerfile-entrypoint
1 | FROM xmtubuntu |
构建镜像,创建并启动容器和 CMD 的例子一模一样,咱们直接启动容器的效果和 CMD 的例子也是一模一样,我们直接来看启动容器并追加参数的例子
可以看出使用 ENTRYPOINT 是可以在后面追加参数的,使用 CMD 若指令后面追加参数,那么会覆盖 CMD 指定的指令
那么,对于以后遇到相关的指令,我们也可以举一反三,做对比试验,这样我们就会理解的更加清楚
如何发布我们的镜像
1、登录 dockerhub
没有注册的 xdm 可以注册一个,hub.docker.com/
1 | docker login -u xxxx用户名 |
2、修改我们的镜像 tag
1 | docker tag 我们的镜像id 我们的docker用户名/镜像名字:版本 |
3、将镜像推到我们自己的仓库中
发布镜像的时候,也是按照一层一层的提交的
最后补充一个网络上找到的图片,现在看这张图就能更清晰的明白其中的原理了
Docker网络
开始理解 docker
一开始,咱们思考一下,宿主机怎么和容器通信呢?
说容器之间是相互隔离的,那么他们是否可以通信?又是如何通信的呢?
开始探索
我们先来看看咱环境中的镜像都有些啥,有 xmtubuntu
再来看看宿主机的网卡信息
ip addr
来查看咱们宿主机的网卡信息
我们发现有一个 docker0
,是因为我们的宿主机上面安装了 docker 的服务,docker 会给我生成一个虚拟网卡,图中的这个 docker0
就是虚拟网卡信息
创建并启动一个 docker 命名为 ubuntu1
1 | docker run -it --name ubuntu1 -P xmtubuntu |
查看一下宿主机网卡信息
查看宿主机的网卡信息
再查看 ubuntu1
的网卡信息,docker 也会默认给我们的容器分配 ip
地址
可以发现宿主机的网卡信息 docker0 下面多了 117: veth838e165@if116:,ubuntu1 的网卡信息上也正好有 116: eth0@if117
我们发现这些 veth 的编号是成对出现的,咱们的宿主机就可以和 ubuntu1 进行通信了
使用宿主机(docker0)和 ubuntu1 互相 ping
docker0 ping ubuntu1 ok
ubuntu1
pingdocker0
,同样的 ok
咱们可以尝试再创建并启动一个 docker 命名为 ubuntu2,方法和上述完全一致
1 | docker run -it -P --name ubuntu2 xmtubuntu |
进入容器,使用 ip a
查看到 ubuntu2 的网卡信息。
进入容器,使用 ip a 查看到 ubuntu2 的网卡信息
宿主机上面查看网信息
宿主机上面又多了一个 veth , 119: veth0b29558@if118
ubuntu2 上的网卡信息是 118: eth0@if119,他们同样是成对出现的,小伙伴看到这里应该明白了吧
ubuntu1 ping ubuntu2 呢?
ubuntu1 对应 172.18.0.2
ubuntu2 对应 172.18.0.3
1 | docker exec -it ubuntu1 ping 172.18.0.3 |
仍然是可以通信,非常 nice
原理是什么?
上述的探索,我们发现宿主机创建的容器,都可以直接 ping 通宿主机,那么他们的原理是啥呢?
细心的 xdm 应该可以看出来,上述的例子中,veth 是成对出现的,上述宿主机和容器能够进行网络通信,得益于这个技术 veth-pair
veth-pair
veth-pair 是一对虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正是因为这个特性,veth-pair 在此处就是充当了一个桥梁,连接各种虚拟设备
通过上图我们可以得出如下结论:
- ubuntu1 和 ubuntu2 他们是公用一个路由器,也就是 docker0,ubuntu1 能 ping 通 ubuntu2 是因为 docker0 帮助其转发的
- 所有的容器在不指定路由的情况下,都是以 docker0 作为路由,docker 也会给我们的容器分配一个可用的 ip
- docker0 是在宿主机上面安装 docker 服务就会存在的
那么通过上图我们就知道,容器和宿主机之前是通过桥接的方式来打通网络的。
Dcoker 中所有的网络接口都是虚拟的,因为虚拟的转发效率高呀,当我们删除某一个容器的时候,这个容器对应的网卡信息,也会被随之删除掉
那么我们可以思考一下,如果都是通过找 ip 地址来通信,如果 ip 变化了,那么我们岂不是找不到正确的容器了吗?我们是否可以通过服务名来访问容器呢?
–link
当然是可以的,当我们在创建和启动容器的时候加上–link 就可以达到这个效果
我们再创建一个容器 ubuntu3,让他 link 到 ubuntu2
1 | docker run -it --name ubuntu3 -P --link ubuntu2 xmtubuntu |
1 | # docker exec -it ubuntu3 ping ubuntu2 |
很明显,我们可以到看到 ubuntu3 可以通过服务名 ubuntu2 直接和 ubuntu2 通信,但是反过来是否可以呢?
1 | docker exec -it ubuntu2 ping ubuntu3 |
不行?这是为什么呢?
我们来查看一下 ubuntu3 的本地 /etc/hosts 文件就清楚了
看到这里,这就清楚了 link 的原理了吧,就是在自己的 /etc/hosts 文件中,加入一个 host 而已,这个知识点我们可以都知悉一下,但是这个 link 还是好搓,不好,他需要在创建和启动容器的时候使用,用起来不方便
那么我们有没有更好的办法的呢?
自定义网络
可以使用 docker network ls
查看宿主机 docker 的网络情况
网络模式
bridge
桥接,docker0 默认使用 bridge 这个名字
host
和宿主机共享网络
none
不配置网络
container
容器网络连通,这个模式用的非常少,因为局限性很大
现在咱们可以自定义个网络,来连通两个容器
自定义网络
自定义应该mynet网络
1 | docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet |
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 | docker run -it -P --name ubuntu1 --net mynet xmtubuntu |
此时我们可以查看一下宿主机的网卡信息,并验证两个容器直接通过容器名字是否可以通信
我们思考一下自定义网络的好处
咱们自定义 docker 网络,已经帮我们维护好了对应关系,这样做的好处是容器之间可以做到网络隔离,
例如
一堆 redis 的容器,使用 192.168.0.0/16 网段,网关是 192.168.0.1
一堆 mongodb 的容器,使用 192.167.0.0/16 网段,网关是 192.167.0.1
这样就可以做到子网很好的隔离开来,不同的集群使用不同的子网,互不影响
那么子网间是否可以打通呢?
网络连通
两个不同子网内的容器如何连通了呢?
我们绝对不可能让不同子网的容器不通过路由的转发而直接通信,这是不可能的,子网之间是相互隔离的
但是我们有办法让 ubuntu3 这个容器通过和 mynet 打通,进而转发到 ubuntu1 或者 ubuntu2 中就可以了
打通子网
我们查看 docker network 的帮助手册
1 | docker network -h |
可以使用 docker network connect
命令实现,在查看一下帮助文档
1 | docker network connect -h |
开始打通
1 | docker network connect mynet ubuntu3 |
这个时候我们可以查看一下 mynet 网络的详情
可以看到在 mynet 网络上,又增加了一个容器,ip 是 192.168.0.4
没错,docker 处理这种网络打通的事情就是这么简单粗暴,直接在 ubuntu3 容器上增加一个虚拟网卡,让 ubuntu3 能够和 mynet 网络打通
宿主机当然也相应的多了一个 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 | version: "3.9" # optional since v1.27.0 |
- services
指定服务
- volumes
指定挂载卷
通过官方文档的上述说明,我们可以知道 Compose 有这么 2 个重要的概念:
- services 服务,就是容器和相关的应用
- 项目,就是一组关联的容器
Compose 安装
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 | docker-compose version |
Compose 官方案例体验
咱们安装好了 docker-compose ,我们一起来体验一下官方的 例子,先会使用,再来研究
准备环境和代码
1、创建 compose 测试目录,自己可以在任意目录下执行如下指令
1 | mkdir composetest |
2、编写 app.py 文件
app.py
1 | import time |
该 py 文件的功能就是,注册了一个路由为 /,我们访问服务器的 / 时,程序会去读取 redis 的计数器,来确认这个网站是访问了第几次了
3、创建一个文件 requirements.txt,用于之后的安装
requirements.txt
1 | flask |
创建 DockerFile 文件
写 Dockerfile 文件
Dockerfile
1 | syntax=docker/dockerfile:1 |
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 | version: "3.9" |
这个 compose 文件定义了 2 个服务
- web 服务,暴露的是 5000 端口
- redis
构建和运行我们的 Compose
运行指令前,我们来查看一下我们的 compose 测试目录都有些啥了:
开始构建
1 | docker compose up |
这个是因为我已经构建过一次了。
实际上的过程是这样的:
可以看到执行指令 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/ |
果然 ok,官方的 compose 体验 no problem ,nice
查看一下镜像
使用 docker images 查看一下镜像,发现多了 composetest_web,python,redis alpine 版本 ,这些也都是 compose 自动为我们做的,非常方便(哥们没有python呢?,可好像不影响)
查看一下网络
1 | docker network ls |
compose 构建的时候,一开始就会为我们创建一个网络
疑问?
细心的朋友发现了,我们的容器名字为什么是 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 |
docker network inspect composetest_default
发现上面的例子, web 服务和 redis 服务,是在同一个网络下的,所有可以相互通信
停止 compose
咱们可以使用 ctrl + c 停止 compose
也可以通过 docker-compose down 停止 compose
停止 compose ,那么 compose 里面涉及的服务,全部都会停止掉
docker-compose down
1 | 停止 composetest_web_1 |
Compose编写规则及wp实战
yaml 规则
docker-compose.yaml 是 Compose 的核心,咱们一定要学会 yaml 编写的规则
当然,咱们还是查看官方文档,compose 部分
Compose file version 3 reference
yaml 文件的结构分为三层:
- version
版本号
- services
服务名
- 其他配置,如网络,挂载等公共的东西
1 | version:'' # 版本号 |
上面说的到版本号在哪里找呢?
进入 dockerhub 网页,docs.docker.com/compose/compose-fi…
官网上的这些都是可以使用的版本,如官网给出的例子:
1 | version: "3.9" |
services 和 其他命令都可以写什么呢?
services 下面可以写的命令非常的多,文档上也讲的非常的详细
如上命令还是非常的多,我们一下子肯定也是记不住的,需要我们慢慢去熟悉,用的多了,写的多了,看得多了,知识慢慢的也根深蒂固了
咱们学习的方法有:
- 多看官方文档,看官网的例子
- 看开源项目,看看别人的 docker-compose.yaml 是如何编写的
实战 - 搭建 wp 博客
咱们来使用 docker-compose.yaml 的方式来搭建我们的个人博客,感受一下一键部署的魅力
创建工作目录
1 | mkdir my_wordpress |
编写我们的 docker-compose.yaml 文件
1 | version: "3.9" |
docker-compose up 一键部署服务并启动
在工作目录执行如下指令一键部署服务
1 | #docker-compose up |
我们也可以在让服务在后台启动
1 | #docker-compose up -d |
启动之后我们可以看到程序先去创建网络,创建对应的挂载卷
开始创建并启动对应的容器
- my_wordpress_db_1
- my_wordpress_wordpress_1
查看一下博客搭建的效果
我们可以访问博客地址:服务器的 IP:8888(直接设置就可以有了)
nice,使用 docker-compose.yaml 搭建个人的 wordpress 博客成功!!!
wordpress 的界面是这个样子的,里面的功能很多,直接就在页面上定制化我们自己的页面,非常方便,xdm 可以尝试一波,不亏
Docker面试题
- 标题: 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 进行许可。