Docker学习笔记
Docker学习笔记
1 初识Docker
1.1 应用部署的环境问题
微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。
- 分布式系统中,依赖的组件非常多,不同组件之间部署时往往会产生一些冲突。
- 在数百上千台服务中重复部署,环境不一定一致,会遇到各种问题
大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:
依赖关系复杂,容易出现兼容性问题
开发、测试、生产环境有差异
例如一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要的函数库、依赖项各不相同,甚至会有冲突。给部署带来了极大的困难。
Docker解决依赖兼容问题
Docker为了解决依赖的兼容问题的,采用了两个手段:
将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包
将每个应用放到一个隔离容器去运行,避免互相干扰
这样打包好的应用包中,既包含应用本身,也保护应用所需要的Libs、Deps,无需再操作系统上安装这些,自然就不存在不同应用之间的兼容问题了。
Docker解决操作系统环境差异
要解决不同操作系统环境差异问题,必须先了解操作系统结构。以一个Ubuntu操作系统为例,结构如下:
结构包括:
- 计算机硬件:例如CPU、内存、磁盘等
- 系统内核:所有Linux发行版的内核都是Linux,例如CentOS、Ubuntu、Fedora等。内核可以与计算机硬件交互,对外提供内核指令,用于操作计算机硬件。
- 系统应用:操作系统本身提供的应用、函数库。这些函数库是对内核指令的封装,使用更加方便。
应用于计算机交互的流程如下:
1)应用调用操作系统应用(函数库),实现各种功能
2)系统函数库是对内核指令集的封装,会调用内核指令
3)内核指令操作计算机硬件
Ubuntu和CentOS都是基于Linux内核,无非是系统应用不同,提供的函数库有差异:
此时,如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或者不匹配,就会报错了:
Docker如何解决不同系统环境的问题?
- Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包
- Docker运行到不同操作系统时,直接基于打包的函数库,借助于操作系统的Linux内核来运行
如图:
小结
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?
Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
Docker应用运行在容器中,使用沙箱机制,相互隔离
Docker如何解决开发、测试、生产环境有差异的问题?
- Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
Docker是一个快速交付应用、运行应用的技术,具备下列优势:
- 可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
- 运行时利用沙箱机制形成隔离容器,各个应用互不干扰
- 启动、移除都可以通过一行命令完成,方便快捷
1.2 Docker和虚拟机的区别
Docker可以让一个应用在任何操作系统中非常方便的运行。而以前我们接触的虚拟机,也能在一个操作系统中,运行另外一个操作系统,保护系统中的任何应用。
两者有什么差异呢?
虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。
Docker仅仅是封装函数库,并没有模拟完整的操作系统,如图:
对比来看:
特性 | Docker | 虚拟机 |
---|---|---|
性能 | 接近原生 | 性能较差 |
硬盘占用 | 一般为 MB | 一般为GB |
启动 | 秒级 | 分钟级 |
小结——Docker和虚拟机的差异
docker是一个系统进程;虚拟机是在操作系统中的操作系统
docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般
1.3 Docker架构
1.3.1 镜像和容器
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。
一切应用最终都是代码组成,都是硬盘中的一个个的字节形成的文件。只有运行时,才会加载到内存,形成进程。而镜像,就是把一个应用在硬盘上的文件、及其运行环境、部分系统函数库文件一起打包形成的文件包。这个文件包是只读的。容器,就是将这些文件中编写的程序、函数加载到内存中允许,形成进程,只不过要隔离起来。因此一个镜像可以启动多次,形成多个容器进程。
例如你下载了一个QQ,如果我们将QQ在磁盘上的运行文件及其运行的操作系统依赖打包,形成QQ镜像。然后你可以启动多次,双开、甚至三开QQ,跟多个妹子聊天。
1.3.2 DockerHub
开源应用程序非常多,打包这些应用往往是重复的劳动。为了避免这些重复劳动,人们就会将自己打包的应用镜像,例如Redis、MySQL镜像放到网络上,共享使用,就像GitHub的代码共享一样。
DockerHub
:DockerHub是一个官方的Docker镜像的托管平台。**这样的平台称为Docker Registry
**。
我们一方面可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像:
1.3.3 Docker架构
我们要使用Docker来操作镜像、容器,就必须要安装Docker。
Docker是一个CS架构的程序,由两部分组成:
服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。
如图:
1.4 安装Docker
企业部署一般都是采用Linux操作系统,而其中又数CentOS发行版占比最多,因此我们在CentOS下安装Docker。
常用命令
1 | systemctl start docker # 启动docker服务 |
部分截图
2 Docker基本操作
2.1 镜像操作
2.1.1 镜像名称
镜像名称一般分两部分组成:[repository]:[tag]
。
在没有指定tag时,默认是latest
,代表最新版本的镜像
这里的mysql就是repository,5.7就是tag,合一起就是镜像名称,代表5.7版本的MySQL镜像。
2.1.2 镜像命令
常见的镜像操作命令如图:
2.1.3 拉取和查看镜像
- 首先去镜像仓库搜索nginx镜像,比如DockerHub:
- 根据查看到的镜像名称,拉取自己需要的镜像,通过命令
1 | docker pull nginx # 拉取镜像 |
- 查看拉取到的镜像,通过命令:
1 | docker images # 查看镜像 |
2.1.4 保存和导入镜像
- 利用
docker xx --help
命令可以查看docker save和docker load的语法:
1 | docker save --help |
可以知道命令格式为:
1 | docker save -o nginx.tar nginx:latest |
- 保存镜像到本地,通过命令:
1 | docker save -o nginx.tar nginx:latest |
- 删除镜像,通过命令:
1 | docker rmi nginx:latest # 删除镜像 |
- 导入本地镜像,通过命令:
1 | docker load -i nginx.tar |
2.1.5 构建镜像
docker build
命令用于根据 Dockerfile 构建 Docker 镜像。它将 Dockerfile 中定义的所有指令(如 FROM
、RUN
、COPY
等)逐步执行,生成一个新的镜像。这个命令是 Docker 工作流中的关键部分,用于创建符合需求的自定义镜像。
1 | docker build [OPTIONS] PATH | URL | - |
PATH
:构建上下文的根目录。Docker 引擎在构建镜像时会将这个目录中的所有文件作为上下文进行处理。默认情况下,Dockerfile 应该位于构建上下文的根目录。如果不在,需要使用-f
。URL
:可以指定一个远程 Git 仓库 URL,Docker 会从中获取 Dockerfile。-
:从标准输入读取 Dockerfile。
常用选项:
-t
,--tag
:为构建的镜像指定一个标签(名称和可选的标签)。格式是name:tag
,例如myapp:latest
。如果省略tag
,默认使用latest
标签。-f
,--file
:指定 Dockerfile 的路径。默认情况下,Dockerfile 应该位于构建上下文的根目录。使用此选项可以指定其他路径或文件名。--build-arg
:设置构建时的环境变量(构建参数)。可以多次使用来设置多个参数。--no-cache
:不使用缓存,强制 Docker 从头开始构建镜像。--rm
:构建完成后移除临时构建容器。默认启用,可以用--rm=false
禁用。--target
:指定多阶段构建中的目标阶段。
示例
- 基本构建:
假设有以下目录结构:
1 | /myproject |
如果在 /myproject
目录下运行以下命令:
1 | docker build -t myapp:latest . |
.
表示 Docker 构建上下文是/myproject
目录。- Docker 将在
/myproject
目录中找到 Dockerfile,并将该目录中的所有文件(Dockerfile
、app.js
、package.json
)作为构建上下文的一部分发送给 Docker 引擎。
- 如果 Dockerfile 位于构建上下文的子目录中:
1 | /home/user/myproject |
可以运行:
1 | docker build -f /home/user/myproject/docker/Dockerfile -t myapp:latest /home/user/myproject |
-f /home/user/myproject/docker/Dockerfile
指定 Dockerfile 的路径。/home/user/myproject
(或者用.
)是构建上下文的路径,Docker 将将这个目录中的所有文件作为构建上下文的一部分,包括 Dockerfile 所在的子目录。
- 使用构建参数
1 | # syntax=docker/dockerfile:1 |
1 | docker build --build-arg ENVIRONMENT=development -t myapp:dev . |
这个命令将构建镜像并将 ENVIRONMENT
参数设置为 development
,影响 Dockerfile 中的 ENV NODE_ENV=$ENVIRONMENT
。
2.2 容器操作
2.2.1 概述
容器操作的命令如图:
容器保护三个状态:
- 运行:进程正常运行
- 暂停:进程暂停,CPU不再运行,并不释放内存
- 停止:进程终止,回收进程占用的内存、CPU等资源
其中的命令含义为:
docker run
:创建并运行一个容器,处于运行状态docker pause
:让一个运行的容器暂停docker unpause
:让一个容器从暂停状态恢复运行docker stop
:停止一个运行的容器docker start
:让一个停止的容器再次运行docker rm
:删除一个容器
2.2.2 创建并运行一个容器
去docker hub查看Nginx的容器运行命令:
1 | docker run --name some-nginx -d -p 8080:80 some-content-nginx |
命令解读:
- docker run:创建并运行一个容器
- –name : 给容器起一个名字,比如叫做serv
- -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
- -d:后台运行容器
- nginx:镜像名称,例如nginx
这里的-p
参数,是将容器端口映射到宿主机端口。
默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx。
现在,将容器的80与宿主机的8080关联起来,当我们访问宿主机的8080端口时,就会被映射到容器的80,这样就能访问到nginx了:
创建并运行
1 | docker ps # 查看正在运行的容器 |
访问页面:注意是8080端口
日志跟踪
1 | docker logs serv # 打印全部日志,参数是容器名称 |
2.2.3 进入容器和修改文件
需求:进入Nginx容器,修改HTML文件内容,添加“Hello World”
提示:进入容器要用到docker exec
命令。
- 进入容器。进入我们刚刚创建的nginx容器的命令为:
1 | docker exec -it serv bash |
docker exec
:进入容器内部,执行一个命令。exec命令可以进入容器修改文件,但是在容器内修改文件是不推荐的-it
: 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互serv
:要进入的容器的名称bash
:进入容器后执行的命令,bash是一个linux终端交互命令
- 进入nginx的HTML所在目录
/usr/share/nginx/html
容器内部会模拟一个独立的Linux文件系统,看起来如同一个linux服务器一样:
nginx的环境、配置、运行文件全部都在这个文件系统中,包括我们要修改的html文件。
查看DockerHub网站中的nginx页面,可以知道nginx的html目录位置在/usr/share/nginx/html
我们执行命令,进入该目录:
1 | cd /usr/share/nginx/html |
查看目录下文件:
- 修改index.html的内容
**容器内没有vi命令(nginx容器)**,无法直接修改,我们用下面的命令来修改:
1 | sed -i -e 's#Welcome to nginx#Hello World#g' -e 's#<head>#<head><meta charset="utf-8">#g' index.html |
再次访问页面:
- 停止容器,并显示所有容器的状态(包括停止运行的容器):
1 | docker stop serv # 停止运行serv容器 |
- 删除容器:
1 | docker rm -f serv # 强制删除正在运行的容器 |
2.3 容器数据管理
在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部。并且因为没有编辑器,修改文件也很麻烦。
这就是因为容器与数据(容器内文件)耦合带来的后果。要解决这个问题,必须将数据与容器解耦,这就要用到数据卷了。
为了持久化数据和与宿主机共享文件,Docker 提供了数据卷和绑定挂载两种方式。
2.3.1 数据卷挂载
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。
- 数据卷是由 Docker 管理的独立存储,可以在多个容器之间共享。
- 数据卷不会随着容器的删除而删除,适用于需要长期保存的数据。
- 通过
-v
选项创建和挂载数据卷。
1 | docker run -d -v myvolume:/app/data my-spring-boot-app |
一旦完成数据卷的挂载,操作宿主机的/var/lib/docker/volumes/html
目录,就等于操作容器内的/usr/share/nginx/html
目录。
① 操作命令
数据卷操作的基本语法如下:
1 | docker volume [COMMAND] |
docker volume
命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:
create
创建一个volumeinspect
显示一个或多个volume的信息docker volume inspect aaa # 查看aaa数据卷的信息
1
2
3
4
5
- `ls` 列出所有的volume
- ```sh
docker volume ls
prune
删除未使用的volumedocker volume prumn
1
2
3
4
5
- `rm` 删除一个或多个指定的volume
- ```sh
docker volume rm aaa
示例
② 数据卷挂载案例
需求:创建一个nginx容器,修改容器内的html目录内的index.html内容
分析:上个案例中,我们进入nginx容器内部,已经知道nginx的html目录所在位置/usr/share/nginx/html
,我们需要把这个目录挂载到html这个数据卷上,方便操作其中的内容。
提示:运行容器时使用 -v 参数挂载数据卷
步骤
- 创建容器并挂载数据卷到容器内的HTML目录
1 | docker run --name serv -v html:/usr/share/nginx/html -p 8080:80 -d nginx |
-v html:/usr/share/nginx/html
:把html数据卷挂载到容器内的/usr/share/nginx/html
这个目录中
- 进入html数据卷所在位置,并修改HTML内容
1 | # 查看html数据卷的位置 |
修改index.html
文件:
访问页面:
2.3.2 绑定挂载
① 操作命令
- 绑定挂载将宿主机上的目录或文件挂载到容器中。
- 容器对绑定挂载点的修改会直接反映在宿主机上。
- 通过
-v
选项指定宿主机路径和容器路径。
1 | docker run -d -v /path/on/host:/app/data my-spring-boot-app |
即容器不仅仅可以挂载数据卷,也可以直接挂载到宿主机目录或者文件上。关联关系如下:
- 带数据卷模式:宿主机目录 –> 数据卷 —> 容器内目录
- 直接挂载模式:宿主机目录/文件 —> 容器内目录/文件
语法:目录挂载与数据卷挂载的语法是类似的:
-v [宿主机目录]:[容器内目录]
-v [宿主机文件]:[容器内文件]
② 绑定挂载案例
需求:创建并运行一个MySQL容器,将宿主机目录直接挂载到容器
实现步骤
- 在mysql.tar文件上传到虚拟机,通过load命令加载为镜像
1 | docker load -i mysql.tar |
- 创建目录
/tmp/mysql/data
1 | mkdir -p mysql/data |
- 创建目录
/tmp/mysql/conf
,将hmy.cnf
文件上传到/tmp/mysql/conf
1 | mkdir -p mysql/conf |
去DockerHub查阅资料,创建并运行MySQL容器,要求:
挂载/tmp/mysql/data到mysql容器内数据存储目录
挂载/tmp/mysql/conf/hmy.cnf到mysql容器的配置文件
设置MySQL密码
1 | docker run \ |
执行结果
测试连接数据库:
总结
数据卷挂载与目录直接挂载的比较
- 数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
- 目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看
3 Dockerfile自定义镜像
常见的镜像在DockerHub就能找到,但是我们自己写的项目就必须自己构建镜像了。
而要自定义镜像,就必须先了解镜像的结构才行。
3.1 镜像结构
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
我们以MySQL为例,来看看镜像的组成结构:
镜像是分层结构,每一层称为一个Layer:
BaseImage层:包含基本的系统函数库、环境变量、文件系统
Entrypoint:入口,是镜像中应用启动的命令
其它:在BaseImage基础上添加依赖、安装程序、完成整个应用的安装和配置
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。
我们要构建镜像,其实就是实现上述打包的过程。
3.2 Dockerfile
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。我们只需要告诉Docker,我们的镜像的组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么,将来Docker会帮助我们构建镜像。
而描述上述信息的文件就是Dockerfile文件。
Dockerfile就是一个文本文件,其中包含一个个的**指令(Instruction)**,用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
1 | # 使用基础镜像 |
各部分解释:
3.2.1 FROM
作用:这个指令指定了构建镜像的起始点,所有的构建操作都会基于这个基础镜像进行。base-image 是基础镜像的名称,tag 是标签(如版本号)。
例如:
1 | FROM openjdk:8-jdk-alpine |
注意点:
- 不能直接指定多个基础镜像,但可以通过多阶段构建(multi-stage build)来实现从多个镜像中获取不同的内容。多阶段构建的主要目的是优化镜像大小和提高构建过程的灵活性。
1 | # 第一阶段:构建阶段,使用 Node.js 镜像 |
- 选择基础镜像是非常重要的一步,它会影响镜像的大小、启动时间、性能、安全性等因素。
采用操作系统作为基础镜像:
优点:
- 灵活性:你可以完全控制基础镜像中的软件包安装和配置。例如,可以安装特定版本的 Java 或其他依赖。
- 定制化:可以根据需要添加额外的工具和库,完全按照需求定制镜像。
- 一致性:使用和生产环境相同的操作系统(如 CentOS),可以确保更一致的运行环境。
缺点:
- 复杂性:需要手动安装所有必要的依赖(如 OpenJDK),增加了构建复杂性。
- 镜像大小:操作系统镜像通常比专用镜像大,会导致生成的 Docker 镜像较大。
1 | FROM centos:y |
采用open jdk作为基础镜像:
优点:
- 简化构建:已经包含了必要的 Java 环境,不需要手动安装,构建过程更简单。
- 镜像大小较小:专用的 JDK 镜像通常更小,因为它们去除了不必要的操作系统工具和库。
- 社区支持:官方的 OpenJDK 镜像通常维护良好,有定期的安全更新和优化。
缺点:
- 灵活性较低:定制化选项较少,无法控制基础镜像的细节配置。
- 特定场景受限:如果需要安装额外的操作系统依赖,可能需要额外步骤和配置。
1 | FROM openjdk:1.8 |
3.2.2 ENV
作用:在构建镜像时设置环境变量,这些变量在容器运行时可用。
例如:
1 | ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk |
示例
1 | # 基础镜像 |
于是可以在运行容器时覆盖环境变量,以便在不同环境下使用不同的配置(使用-e
)。例如:
1 | docker run -d -p 8080:8080 -e SERVER_PORT=9590 -e APP_NAME=myapp -e APP_VERSION=2.0.0 my-spring-boot-app |
此外,Spring Boot 应用程序使用这些环境变量来配置应用名称和版本,可以在 application.properties
文件中引用这些环境变量:
1 | # ${key:defaultValue} |
1 |
|
3.2.3 WORKDIR
作用:指定后续指令(如 RUN
、CMD
、COPY
)的工作目录。如果目录不存在,将会创建。
例如:
1 | WORKDIR /app |
注意:工作目录(WORKDIR
)是容器内的一个目录,而不是宿主机上的目录。这个目录路径相对于容器的文件系统。因此,当你在 Dockerfile 中使用 WORKDIR /app
指令时,你是在容器内部创建或切换到 /app
目录。
容器文件系统的图示:
1 | 容器文件系统 |
3.2.4 COPY
作用:将构建上下文中的文件或目录复制到镜像中的指定路径。
1 | COPY target/your-application.jar /app/ |
示例:
1 | FROM openjdk:1.8 |
3.2.5 ADD
作用:类似于 COPY
,但 ADD
还支持从 URL 下载文件并自动解压归档文件。
1 | ADD http://example.com/file.zip /app/ |
3.2.6 RUN
作用:运行命令安装软件包和依赖、修改文件等。每个 RUN
指令都会创建一个新的镜像层。
1 | RUN npm install --production |
示例:详见3.2.1节
3.2.7 CMD
作用:CMD
指令用于为启动容器时的默认命令提供默认参数。Dockerfile 中可以有多个 CMD
指令,但只有最后一个 CMD
指令会生效。
语法和示例:
1 | CMD ["executable", "param1", "param2", ...] |
3.2.8 ENTRYPOINT
作用:定义容器启动时的命令,不会被 CMD
指令覆盖。可以与 CMD
配合使用。
语法和示例:
1 | ENTRYPOINT ["executable", "param1", "param2", ...] |
当 ENTRYPOINT
和 CMD
一起使用时,CMD
提供默认参数,ENTRYPOINT
指定要运行的主程序。这种方式可以使得容器在运行时具有更灵活的参数化特性。
1 | FROM openjdk:8-jre |
3.2.9 EXPOSE
作用:声明容器将要监听的端口。这个指令不会实际暴露端口,而是用于文档说明,告知容器使用者和其他人哪些端口应该被映射。
1 | EXPOSE 8080 |
3.2.10 VOLUME
作用:创建挂载点并挂载卷。指定容器内的目录作为数据卷,这些目录将可以与主机系统的目录挂载,便于持久化存储和数据共享。
1 | VOLUME ["/app/data"] |
示例
1 | # 使用官方的 Node.js 镜像作为基础镜像 |
当构建并运行使用了 VOLUME
指令的容器时,Docker 会自动处理数据卷。可以通过以下命令运行构建的镜像:
1 | docker build -t myapp . |
在运行容器后,数据将保存在 /app/data
目录内的卷中。如果希望将这个数据卷挂载到宿主机的某个目录,可以在 docker run
命令中使用 -v
选项:
1 | docker run -d -p 3000:3000 -v /my/local/data:/app/data --name myapp-container myapp |
该命令将宿主机的 /my/local/data
目录挂载到容器的 /app/data
目录。数据将存储在宿主机上,并且可以在容器重启或删除后保留。
3.3 构建一个Java项目
需求:基于Centos镜像构建一个新镜像,运行一个java项目
步骤
新建一个空文件夹docker-demo
拷贝课前资料中的
docker-demo.jar
,jdk8.tar.gz
,Dockerfile
文件到docker-demo这个目录,其中Dockerfile文件的内容如下:
1 | # 指定基础镜像 |
- 进入docker-demo,将准备好的docker-demo上传到虚拟机任意目录,然后进入docker-demo目录下
- 运行命令:构建镜像
1 | docker build -t javaweb:1.0 . |
- 查看镜像:
- 启动该镜像为容器:
- 访问启动的项目
XXXX:8090/hello/count
:
4 Docker Compose
4.1 概述
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。
Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。格式如下:
1 | version: "3.8" |
上面的Compose文件就描述一个项目,其中包含两个容器:
- mysql:一个基于
mysql:5.7.25
镜像构建的容器,并且挂载了两个目录 - web:一个基于
docker build
临时构建的镜像容器,映射端口是8090
其实DockerCompose文件可以看做是将多个docker run命令写到一个文件,只是语法稍有差异。
4.2 docker-compose.yml
Docker Compose 文件(通常命名为 docker-compose.yml
)是一个 YAML 文件,用于定义和配置 Docker 容器的服务、网络和卷。这个文件允许用代码来管理多个 Docker 容器,并将它们组合成一个应用程序栈。通常放在项目的根目录中。
1 | my-project/ |
以下是 Docker Compose 文件的组成部分及其详细注释示例:
1 | version: '3.8' # 指定 Docker Compose 文件的版本 |
4.3 部署微服务集群
需求:将之前学习的cloud-demo微服务集群利用DockerCompose部署
实现步骤:
- 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件
- 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名
- 使用maven打包工具,将项目中的每个微服务都打包为app.jar
- 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中
- 将cloud-demo上传至虚拟机,利用 docker-compose up -d 来部署
4.3.1 Compose文件
项目结构:
1 | cloud-demo/ |
- 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件,而且每个微服务都准备了一个独立的目录:
docker-compose.yml
文件内容如下:
1 | version: "3.2" |
可以看到,其中包含5个service服务:
nacos
:作为注册中心和配置中心image: nacos/nacos-server
: 基于nacos/nacos-server镜像构建environment
:环境变量MODE: standalone
:单点模式启动
ports
:端口映射,这里暴露了8848端口
mysql
:数据库image: mysql:5.7.25
:镜像版本是mysql:5.7.25environment
:环境变量MYSQL_ROOT_PASSWORD: 123
:设置数据库root账户的密码为123
volumes
:数据卷挂载,这里挂载了mysql的data、conf目录,其中有提前准备好的数据
userservice
、orderservice
、gateway
:都是基于Dockerfile
临时构建的
- 查看mysql目录,可以看到其中已经准备好了cloud_order、cloud_user表:
- 查看微服务目录,可以看到都包含Dockerfile文件,内容如下:
1 | FROM java:8-alpine |
4.3.2 修改微服务配置
因为微服务将来要部署为docker容器,而容器之间互联不是通过IP地址,而是通过容器名。这里我们将order-service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。
如下所示:
1 | spring: |
4.3.3 打包
接下来需要将我们的每个微服务都打包。因为之前查看到Dockerfile中的jar包名称都是app.jar
,因此我们的每个微服务都需要用这个名称。
可以通过修改pom.xml中的打包名称来实现,每个微服务都需要修改:
1 | <build> |
4.3.4 拷贝jar包到部署目录
编译打包好的app.jar
文件,需要放到Dockerfile的同级目录中。
4.3.5 部署
最后,我们需要将文件整个cloud-demo文件夹上传到远端服务器中,由DockerCompose部署。
然后进入cloud-demo
目录,然后运行下面的命令:
1 | docker-compose up -d |
4.4 操作命令
暂留
5 Docker镜像仓库
镜像仓库( Docker Registry )有公共的和私有的两种形式:
公共仓库:例如Docker官方的 Docker Hub,国内也有一些云服务商提供类似于 Docker Hub 的公开服务,比如 网易云镜像服务、DaoCloud 镜像服务、阿里云镜像服务等。
除了使用公开仓库外,用户还可以在本地搭建私有 Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。
6 Docker网络
Docker 网络设置了容器如何与其他容器和外部服务通信。为了获得网络访问,容器需要是 Docker 网络的一部分。容器可以通信的方式取决于它的网络连接。Docker 提供了五种标准网络模式来执行核心网络功能:Bridge(桥接)、Host(主机)、Overlay(重叠)、Macvlan、None。
6.1 Bridge
Docker的Bridge网络是一种默认的网络模式,适用于单个Docker主机上的容器间通信。每个容器连接到一个虚拟网桥(bridge
),容器之间可以通过该桥进行通信。
网桥是一种虚拟网络设备,所以具备虚拟网络设备的所有特性,比如可以配置IP、MAC等,除此之外,网桥还是一个二层交换机,具有交换机所有的功能。
6.1.1 工作原理
- 默认Bridge网络:
- Docker安装时会创建一个默认的Bridge网络,名称通常是
bridge
。 - 容器连接到默认的Bridge网络,可以使用容器名互相通信。
- Docker安装时会创建一个默认的Bridge网络,名称通常是
- 自定义Bridge网络:
- 通过创建自定义的Bridge网络,可以更灵活地管理容器间的通信和隔离。
6.1.2 创建和使用自定义Bridge网络
- 创建自定义bridge网络:
1 | docker network create my_bridge_network |
- 查看所有网络:
1 | [root@VM-0-16-centos hongyi]# docker network ls |
- 启动容器并连接到自定义Bridge网络:
1 | docker run -d --name container1 --network my_bridge_network nginx |
- 验证容器间通信(直接使用容器名通信):
1 | docker exec container2 ping container1 |
6.1.3 查看网络详情
- 使用以下命令可以查看Bridge网络的详细信息:
1 | docker network inspect my_bridge_network |
1 | [ |
6.1.4 网络流程
容器连接到docker0网桥设备,并分配一个IP地址。
- Docker启动时,会在主机操作系统上创建一个虚拟网桥(
docker0
)。 - 当我们创建一个容器时,Docker会为该容器分配一个唯一的虚拟网络接口(
veth pair
)。 - 一个端点连接到容器的虚拟网络接口,而另一个端点连接到虚拟网桥(
docker0
)。 - 容器和主机操作系统之间的通信通过虚拟网桥进行转发,使得容器可以与其他容器或主机上的服务进行通信。
在Bridge模式下,Docker会为每个容器分配一个IP地址,并使用桥接技术将容器的网络流量转发到正确的目标。这样,容器之间可以通过IP地址直接进行通信,并且容器与主机操作系统之间也可以进行通信。
6.2 Host
6.2.1 工作原理
Host网络模式将容器直接连接到宿主机的网络上,容器将使用宿主机的IP地址。这意味着容器中的应用程序将共享宿主机的网络栈,与宿主机的网络配置完全一致,共享主机的IP地址。这种模式在需要最大网络性能和最低网络延迟的情况下特别有用。
6.2.2 操作
不能自定义host网络。
- 启动容器时使用Host网络模式
1 | docker run -d --network host --name my_nginx nginx |
- 查看Host网络中的容器
1 | docker network inspect host |
1 | [ |
注意
- 网络隔离:Docker默认使用的是bridge模式,每个容器都有自己的虚拟网络接口和IP地址。而host模式则取消了这种隔离,容器直接使用宿主机的网络栈,因此它们共享同一个网络接口和IP地址。
- 端口映射:在host模式下,容器可以通过监听宿主机的某个端口来提供网络服务,而不需要进行端口映射。例如,如果容器监听宿主机的80端口,那么通过宿主机的IP地址就可以直接访问该容器提供的服务。
- 网络性能:由于容器与宿主机共享网络栈,所以在host模式下,容器的网络性能通常比bridge模式要好。这是因为在bridge模式下,容器之间的网络通信需要经过虚拟网桥进行转发,而在host模式下,容器直接使用宿主机的网络栈,减少了网络转发的开销。
- 安全性考虑:host模式将容器与宿主机的网络完全融合在一起,容器可以直接访问宿主机上的网络服务。这在某些情况下可能会带来安全风险,因为容器可以通过攻击宿主机上的网络服务或操纵宿主机的网络栈来进行恶意行为。因此,在使用host模式时需要特别注意网络安全的问题。
6.3 Overlay
Overlay网络是一种网络驱动,旨在支持跨多个Docker主机的容器通信。这使得它特别适用于Docker Swarm集群或其他分布式容器编排系统,其中容器可能运行在不同的主机上。Overlay网络通过在底层物理网络上创建虚拟网络来实现容器之间的通信。
6.4 Macvlan
6.4.1 概述
Docker的macvlan
网络是一种网络驱动,允许容器在宿主机上分配独立的MAC地址和IP地址,使其在网络上表现得像是物理网络设备。这种网络模式适用于需要与宿主机网络隔离,但仍在同一物理网络上运行的容器。macvlan
网络模式可以用于需要容器拥有自己的网络身份或直接与外部网络设备通信的场景。
特点:
- 独立的MAC地址:每个容器可以拥有一个独立的MAC地址,使得容器在网络上表现得像一个独立的网络设备。
- 独立的IP地址:容器可以拥有独立的IP地址,允许容器直接与外部网络进行通信。
- 与宿主机隔离:
macvlan
网络将容器的网络栈与宿主机网络栈隔离,提供一定程度的网络隔离。
6.4.2 操作
创建macvlan
网络:
1 | docker network create -d macvlan \ |
-d macvlan
:指定网络驱动为macvlan
。--subnet
:指定网络的子网。--gateway
:指定网络的网关。-o parent
:指定宿主机上的物理网络接口(例如eth0
)。my_macvlan_network
:自定义的网络名称。
运行容器并连接到 macvlan
网络:
1 | docker run -d --name my_container --network my_macvlan_network --ip 192.168.1.100 nginx |
--name
:指定容器的名称。--network
:指定容器使用的网络。--ip
:指定容器的IP地址(必须在子网范围内)。nginx
:要运行的镜像。
查看网络:
1 | docker network inspect my_macvlan_network |
1 | [ |
6.4.3 与宿主机通信
要与使用 macvlan
网络的容器通信,可以使用宿主机的物理网络接口配置。
在宿主机上配置路由,以允许宿主机和 macvlan
网络上的容器通信。例如:
1 | ip route add 192.168.1.0/24 dev eth0 |
这条命令将 192.168.1.0/24
网络的流量路由到宿主机的 eth0
接口。
6.5 None
Docker 的 none
网络模式是 Docker 提供的一种特殊网络驱动,它用于创建没有网络连接的容器。在 none
网络模式下,容器不连接到任何网络,因此容器内的应用无法访问外部网络,也无法与宿主机或其他容器通信。这种网络模式适用于对网络隔离有特殊要求的场景,或者容器仅用于特定的内部操作,不需要网络连接。
- 启动容器时使用
none
网络模式
1 | docker run -d --name my_container --network none nginx |
- 查看容器网络配置:
1 | docker inspect my_container |
1 | [ |
在 NetworkSettings
部分,可以看到 none
网络的配置没有 IPAddress
或 Gateway
,这表明容器没有网络连接。