目录
1.3.1.1我们假设现在需要从应用市场中下载nginx软件
1.3.1.2我们假设现在需要从docker-cli打包软件到dockerhost主机中
1.3.1.3我们假设现在想要将自己打包的软件包上传到软件商城。
步骤4:尝试完成对容器的删除(这个实验中可以不用删除,我们只学命令)
第一步:docker ps查看正在运行的容器,找到容器的id
第二步:docker stop 容器的id来停止正在运行的容器
第三步:重新docker ps验证停止的容器是否还在运行或者使用docker ps -a查看刚刚停止的容器的Status是否为Exited退出状态
第一步:docker ps -a找到我们STATUS为Exited的已经退出的容器的id
第一步:验证编辑过数据的nginx掉了之后开启新的nginx容器,界面会发生改变且数据无法保存。
步骤3:人为的删除该容器模拟出容器炸了的场景,重新创建一个新容器查看编辑过的主页面是否还存在
第二步:使用目录挂载功能挂载nginx的主界面到docker外部主机
第三步:运行一个容器,同时完成对主页的目录挂载和卷映射,并验证
实验:安装mysql和wordpress博客,并且我们写的博客都能存放到mysql中
综合实验:使用docker compose完成对于wordpress、mysql的一键启动
第二步:将我们原来手动在docker配置的网络、卷、容器全部删除干净
第三步:批量启动容器,我们只需要up yaml类型的文件即可
1.docker的基础知识
1.1docker简介
Docker 是一个开源的应用容器引擎,它允许开发者将他们的应用以及依赖打包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。无论你身处计算机甚至不处在计算机岗,你都要起码的了解docker的基础知识。正好,2024.10.1日我提笔开始写,结合了B站的某些博主的讲解,而现在的时间点正好是docker解封后有段日子了,大家也可以登录进dockerhub进行社区的访问和下载资源了。本次,害羞的白菜用一篇文章带你通关docker!
1.2docker存在的原因
假设现在的你是一个全栈工程师,你现在把做完的项目打包给客户,但是现在存在这么几个问题:
环境不一致:客户的生产环境可能与我的开发环境不同,导致在我这里运行良好的代码在客户那里出现各种问题,如依赖库版本不匹配、缺少系统包或配置差异等。
依赖管理:项目可能依赖于特定的库或框架版本,而这些依赖在客户的服务器上可能未安装或版本不兼容。
配置差异:项目可能需要特定的配置文件或环境变量,这些在客户的服务器上可能需要手动设置。
部署复杂性:部署过程可能涉及多个步骤,包括代码传输、依赖安装、数据库迁移等,这些步骤在不同环境中可能需要不同的命令或脚本。
扩展性和伸缩性:如果客户需要在多个服务器上运行应用,或者需要根据负载动态扩展应用实例,传统的部署方式可能难以满足需求。
版本控制:在多个环境(开发、测试、生产)之间迁移时,可能难以保持应用状态的一致性,难以回滚到以前的版本。
安全性:在不同环境中复制和执行代码可能引入安全风险,特别是如果代码中包含敏感信息或需要隔离不同用户的数据。
资源利用率:在客户的服务器上,可能难以优化资源使用,导致应用运行效率低下或成本过高。
维护和更新:随着时间的推移,项目可能需要更新和维护,这在传统部署模式下可能涉及复杂的版本管理和兼容性问题。
文档和培训:可能需要为客户的运维团队提供详细的部署文档和培训,以确保他们能够正确部署和维护应用。
这么一看问题还是不少的,谁也不敢保证一个上述问题在与客户交付的时候都不会出现,那么使用docker就可以完美的解决上述的所有问题:
环境不一致:Docker通过容器化技术,将应用及其依赖打包在容器中,确保了不同环境之间的一致性。
依赖管理:Docker允许您在Dockerfile中声明应用所需的依赖,确保了依赖的一致性和可管理性。
配置差异:通过Docker容器,您可以为应用配置环境变量和配置文件,这些配置可以在不同的容器之间保持一致。
部署复杂性:Docker简化了部署流程,使得应用的部署变得快速和标准化。
扩展性和伸缩性:Docker容器可以快速启动和停止,适合动态扩容和缩容,提高了应用的伸缩性。
版本控制:Docker镜像可以进行版本控制,您可以构建、标记和分发特定版本的镜像。
安全性:Docker提供了容器级别的隔离,增强了应用的安全性,并且可以通过安全策略进一步加强保护。
资源利用率:Docker容器共享宿主机的内核,启动速度快,资源利用率高,有助于优化资源使用。
维护和更新:Docker允许您轻松更新容器镜像,并且可以快速回滚到以前的版本。
文档和培训:Docker的普遍使用意味着有大量的文档和社区支持,有助于学习和维护。
对于上述的解决方法我们在文章的一开始就讲解出来可能对于新手不友好,但是我会慢慢给大家解决,需要明白的仅有一句话:就是因为docker支持跨平台的快速运行应用,而且只是用甚少的命令就可以实现,无视平台(Ubautu、centos、debian等等系统都可以使用)且不需要像传统的交付一样需要写出“长篇大论”的报告,就像在浏览器中下载软件一样直接安装在电脑上一样方便,docker也可以实现对于项目的快速打包、构建和运行。
当你想要把软件包上传给客户的时候又开始犯难,况且还存在多个版本之间的管理问题,这就有了dockerhub这个平台,我们可以将写完的软件包一键分享到docker自己的应用商店dockerhub中,客户可以直接在dockerhub直接下载你分享的软件。
1.3docker如何进行工作?
我们首先要在某一台主机上安装docker进程,我们把安装了docker进程的主机叫做docker主机(下图居中),也称为docker-host。同时,docker这个进程还提供了一个客户端(下图居左),简称为docker-cli命令行程序,用来操作docker主机的后台进程,给主机发送docker命令。而在docker的应用市场中的应用(下图居右)我们称之为“镜像”。
1.3.1docker完整的拉镜像的流程(原理)
1.3.1.1我们假设现在需要从应用市场中下载nginx软件
第一步:下载镜像
在docker-cli端中发送下载命令docker pull 镜像名称,该命令会发给docker Deamon进程,进程拿到这条命令会知道“客户机想要下载软件了”这条命令,该进程就会开始工作,跑到应用市场中找到docker-cli中请求的应用名称的应用,去下载该镜像到docker本机。这时我们的本机就存在的docker镜像了。
docker pull nginx
第二步:启动镜像
使用docker run 镜像名称来启动相关的镜像文件,即使我们的镜像没有下载到dockerhost主机,我们可以称“一步docker run命令其实就包含了docker pull命令”。我们将用镜像启动的文件称之为“容器”。类似的我们就可以启动多个容器而每一个容器都可以代表我们运行中的应用。
docker run 镜像名称
1.3.1.2我们假设现在需要从docker-cli打包软件到dockerhost主机中
第一步:打包软件包
使用docker build 自定义的软件包名这条命令来将我们的软件包进行打包发到dockerhost主机的dockerDeamon进程来制作软件的镜像。该守护进程就会帮助你制作镜像到dockerhost主机。
docker build -t 自定义的软件包名
1.3.1.3我们假设现在想要将自己打包的软件包上传到软件商城。
第一步:打包软件包
完成docker build 自定义的软件包来完成对于镜像文件的打包。
docker build 自定义的软件包名
第二步:推送到软件商城
使用docker pull 自定义软件包的名称这条命令给到dockerhost主机的dockerDeamon进程,这样该进程就会将我们打包好的镜像推送到软件商城中。我们的张三用户将docker推送到软件商城之后李四、王五等等用户只要登陆上软件商城并且搜索到张三的项目,都可以进行下载。
docker pull 自定义的软件包的名称
我们发现基本上docker的命令的步骤都不会特别的多,这也是我上文所说的不需要“长篇大论”。
1.4镜像及容器
好多童鞋都会混淆的一个非常重要的知识点就是:何为镜像?何为容器?
一句话概括:镜像就是软件包,容器就是用软件包启动的应用,每一个运行中的应用又都可以称为一个容器。
1.4.1容器
如果我们使用传统的部署方法,就是App直接运行在操作系统上(类似Windows)运行,这样的好处就是应用可以直接与操作系统的内核进行交互。但如果我们的一个应用发生了内存泄漏(内存泄漏是指程序在申请内存后,无法在不需要时正确释放,导致随着时间的推移,可用内存越来越少的现象。)而别的应用再使用内存的时候发现没有空间,则也会停止最后,所有的应用都会停止导致系统上没有应用可以正常的运行。这样的情况我们该如何解决呢?
使用虚拟化技术可以完美的解决上述的问题,我们在操作系统上搭建虚拟机,而虚拟机内又有不同的操作系统可以使用,就相当于一个客户端了。但是这样做有一个缺点,每个虚拟机的空间都非常大,更别谈什么在虚拟机上再装载应用了,除非你有“钞能力”。
我们可以再从虚拟化部署进阶到容器化的部署从而解决虚拟机过于笨重的问题。我们直接在某一个操作系统上安装容器的运行时环境,利用容器的技术 一个一个的启动不同的容器,每一个应用都运行在自己的容器内部,该应用的容器就包含了应用运行的完整环境。而且多个容器之间是相互隔离的关系,如果发生内存泄漏的问题仅会在一个容器内泄漏,最差的情况就是该容器不可用,那我们大不了再开一个容器重新运行应用即可,而且一个容器的空间比虚拟机要小得太多。
综上所述,容器化部署对于传统部署的优点集中在:
- 轻量便捷
- 共享操作系统内核
- 每个容器都是拥有自己的内存、cpu、进程空间等。相互之间互不干扰。
- 容器可以实现高密度的部署,一个虚拟机的空间就可以部署几十个容器甚至几百个。容器占用的资源也非常少,单位空间内容器的数量要比虚拟机的数量多的多
1.5docker的安装(上“云”安装)
第一步
我们首先需要有一个云服务器,我使用的是阿里云服务器(具体的部署一个云服务器的步骤我就不展示了)国内热门的云服务器有阿里云、腾讯云、百度云和华为云,我建议是使用阿里云,因为每人有三个月的免费体验期。
我仅提醒一件事,我们实验的时候最好把端口全部打开,虽然这是一种不安全的行为,但是还是建议大家打开,避免以后出现问题找不到原因结果是云服务器端的端口问题导致。
第二步
连接云服务器,本人云服务器的ip地址为123.57.240.242,我们可以使用FinalShell软件进行远程连接,输入账号密码即可进入
第三步
安装docker,打开docker的首页的学习文档(点我进入)
在弹出的对话框中选择Manuals选项(点我进入)
在弹出的页面中的左侧依次选择Docker Engine-->Install-->CentOS选项
复制Uninstall Old Version内的命令,移除系统内的旧版本Docker
粘贴到虚拟机上如下图所示(这里因为我们是新开的机器所以没有之前版本的docker)
第四步
下载新的yum-util工具类并使用yum-util工具类下载docker的下载地址源(也不需要我们自己干,第三步最后的页面内下滑即可找到最新的util单元,我们只管复制代码即可)
我给大家网上找到了阿里云的国内docker下载源,大家先用我这个吧,因为国外的docker下载源确实慢。。。。。。
sudo yum install -y yum-utils
sudo yum-config-manager
--add-repo
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
下载完成后的界面如下(阿里云的服务器好像是已经给你配置好了,应该不用下载,大家如果保险一点下载上也是没问题的)
第五步
接下来我们安装docker引擎(点我进入)
我将代码提取出来大家可以直接复制我的
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
下面是对于我们下载的内容的简单解释
docker-ce:docker的引擎
docker-ce-cli:我们先前讲推送时讲到的docker客户端命令行
containerd.io:containerd:一个行业标准的容器运行时,它被设计为Docker和其他CRI兼容的客户端的守护进程。containerd负责启动和运行容器。
Docker Buildx Plugin:docker buildx是一个Docker CLI插件,用于构建多平台的容器镜像。它允许您构建可在不同操作系统和架构上运行的镜像。
Docker Compose Plugin:docker-compose是一个用于定义和运行多容器Docker应用程序的工具。它使用YAML文件来配置应用程序的服务,然后使用一个简单的命令来启动和停止所有服务。
安装完成之后如下图所示(大概400多M没到500M,所以会有点慢。。。)
1.6docker启动
systemctl start docker
上述命令要求我们在执行完1.5的步骤之后才可以使用,回车后docker算是在我们的云服务器中开启成功了
什么都不弹就是最好的结果,表示我们成功启动,但是我们重启云之后我们的docker却没有起来,我们需要额外添加一条命令来完成docker程序的启动
systemctl enable docker
使用上述命令我们就能在云开机之后docker也跟着启动了。
1.7配置docker的加速源
在启动docker之后我们如果这个时候就想拉镜像的话还是比较慢的,建议大家配置一个国内的加速源,这样能在docker源配置到国内的基础上再次加速下载速度,命令如下
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
验证一下我们换加速源的配置是否成功的方法:
随意使用一个docker命令看看是否会报错
docker ps
我们发现没有报错,说明我们的国内加速源配置成功
如果大家对于将docker源配置到国内和将加速源配置到国内有点混淆的话可以看我下面的话
Docker的加速源地址:通常指的是将Docker的镜像仓库地址指向一个地理位置更靠近你、网络连接更快的镜像源。这样在拉取Docker镜像时,可以减少延迟,提高下载速度。这些加速源地址通常是镜像的缓存服务器,它们会存储常用的镜像供用户快速下载。配置方法一般是在Docker的配置文件/etc/docker/daemon.json中添加对应的加速源地址。
国内的Docker加速器:这通常是指国内的一些云服务商提供的服务,它们不仅提供镜像加速,还可能包括构建、存储和分发镜像的完整服务。这些服务通常会提供一个专属的加速器地址,你需要在Docker的配置中指定这个地址来使用加速服务。例如,阿里云、腾讯云等都提供了自己的Docker镜像加速服务。
想象一下,Docker镜像就像是一本书,而Docker镜像的源(Registry)就像是分布在全球各地的图书馆。当你想要阅读一本书时,你可以选择去最近的图书馆借阅。
- 将镜像的源配置到国内:这就像是你选择去一个离家近的图书馆借书。比如,你住在上海,而最近的图书馆是上海图书馆。如果你想要借的书在北京图书馆,那么你需要跨越很长的距离去取书,这会花费很多时间和努力。将镜像的源配置到国内,就像是选择一个地理位置更近的图书馆,比如上海图书馆,这样你去借书的路程就会短很多,速度也会快很多。
- 将镜像的加速源配置到国内:这就像是你选择通过快递服务来借书。即使你想借的书还是在北京的图书馆,但是通过快递服务(加速器),图书馆会将书快递到你家,这样你就不需要亲自去北京取书了。而且,如果你选择的快递服务在上海有一个分拣中心,它还可以先将书送到上海的分拣中心,然后再送到你家,这样速度会更快,因为它减少了从北京到上海的直接运输距离。
总结一下:
- 将镜像的源配置到国内:就像是选择了一个地理位置更近的图书馆,减少了你去借书的距离。
- 将镜像的加速源配置到国内:就像是使用了快递服务,即使书不在你的城市,也能快速地送到你手中。
2.docker命令
假设我们需要完成一个实验,实验的内容是在含有docker的云服务器内启动一个nginx服务,并将首页改为自己的页面发布出去,让所有的人都可以使用它,这个实验几乎涵盖了我们docker的所有命令,我们可以边讲实验边贯穿docker命令。
2.1实验的整体步骤
实验整体的步骤我们可以划为五大部分:
从应用商店下载镜像到云服务器主机-->
在云服务器上启动一个容器-->
修改nginx的主界面使实验具有鲜明性-->
保存我们做好的镜像-->
将保存的镜像发布到社区
2.1.1从应用商店下载镜像到云服务器主机
步骤1:检查应用商城中是否有这个容器
docker search nginx
上面的图是我从网上找的,现在我也试了,还是连不上,但是应该不耽误我们的实验
(经过我3个余小时的鏖战我终于找到了原因:docker search 是使用外网访问dockerhub的热门资源,而docker pull是从我们企业的镜像中拉取我们最新的源,所以我们的docker pull能好使但是docker search不好使),同时,我也可以解释一下上图docker search的每列的含义
第一列:表示我们镜像的名称
第二列:表示我们对于镜像的描述
第三列:STARS表示镜像在Docker Hub上获得的星标数,反映了该镜像的受欢迎程度。
第四列:offical如果为ok的话表示是官方发布的容器,否则则为第三方下载的容器
步骤2:存在容器则进行下载
docker pull 镜像名称
使用上述命令即可完成镜像的下载
步骤3:下载完成镜像之后检查一下是否下载完成
docker images
通过上述命令来验证我们的镜像文件是否下载完成。我们发现我们下拉的nginx已经成功完成了,并且nginx的版本还是当前最新的版本(latest)
REPOSITORY:镜像的名称,这里是nginx
TAG:镜像的标签 ,一般代表镜像的版本,而latest表示是当前最新版本的镜像
IMAGE ID:镜像的id,一个容器内部的镜像的id是不会重复的,一般我们取前三位就能识别是哪一个镜像。
CREATED:该镜像被创建的时间
SIZE:镜像大小
但是这样就存在了一个问题:我们只能下载最新版本的镜像吗?如我想要下载一个指定版本的镜像应该如何操作呢?
这时我们需要明确一个概念:容器的完整名称=容器名称:TAG 例如:nginx:1.26.0
步骤4:尝试完成对容器的删除(这个实验中可以不用删除,我们只学命令)
通过上一步我们docker image看到我们已经下载的镜像和镜像id,我们可以使用一下方法完成对镜像的删除
docker rmi 镜像名称
docker rmi 镜像名称:版本号
docker rmi 镜像id
docker rmi 镜像id前三位
注意:删除镜像的时候需要保证镜像没有在运行,否则你可以使用docker rmi -f命令强制执行删除操作。或者你可以先docker stop 容器的id之后再执行docker rmi 容器的id即可完成删除
docker rmi -f 镜像名称
docker rmi -f 镜像名称:版本号
docker rmi -f 镜像id
docker rmi -f 镜像id前三位
如果我们想要删除容器的话就需要使用docker rm 容器id
docker rm 容器id
run命令的其他选项(后期我们会大量的使用run命令,所以大家在这一块一定好好看)
docker run的语法结构如下
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
- [OPTIONS]:这是可选的参数,用于配置容器的创建和运行。例如,您可以指定端口映射、环境变量、卷挂载等。
- IMAGE:指定要用于创建容器的镜像名称。
- [COMMAND]:这是在容器启动时运行的命令。如果未指定,Docker 将使用镜像中定义的默认命令。
- [ARG...]:这是传递给命令的参数。
接下来我们可以详细的聊聊[OPTIONS]这一部分的内容,常用的选项如下
-d:以守护进程模式运行容器(后台运行)
--name="容器名":为容器指定一个名称,指定完名称之后我们指定的名称就可以代替id去使用,指定的容器的名称不可以重复!!
接下来我们使用一条语句来完成:后台启动一个新的nginx镜像并且将nginx的自定义名称设置为My_nginx
docker run -d --name my_nginx nginx
如果感觉太长我教大家这么记忆:
选项找最近的答案,但是选项之间使用空格书写,我们的--name选项离他最近的答案就是my_nainx而我们的-d选项又因my_nginx被--name拿走了仅剩下nginx所以只能和nginx匹配,既“后台运行nginx镜像的实例化容器,该容器的名称为my_nginx”
做到了这一步,大家可能会想:我们已经运行了一个容器了,能不能上网试试看能不能显示出nginx主页呢?我们又将主机的网址在浏览器中输入,发现拒绝访问,这又是为什么呢?
原因在于每一个容器都是相互隔离的在安装了docker的主机上运行,互不干扰,而且每个容器内部都有独立的文件系统,我们在主机的浏览器上进行访问只能访问到主机上的端口而不是docker内部容器的端口,所以会造成这种情况,解决的方法就是“设置端口映射”,由下图可知我们想要通过外部的90端口映射到内部容器的80端口,因为80端口是nginx的默认开放端口,而90端口我们任意设置,只要与其他服务不冲突即可。
我们删掉原来的a7c容器重新运行一个带有端口映射的容器即可
docker run -d --name my_nginx -p 90:80 nginx
完成之后我们再次访问IP:90查看我们的服务是否开启,nginx的主页成功开启,试验成功。
注意:
如果我们的实验没有成功的话可能是云服务器的安全组有问题,我们将安全组打开外部的90端口即可。
如果我们再开容器的话内部新容器仍然可以用80端口(因为容器之间是隔离的),但是我们docker主机的容器就不能再用90了,因为外部主机的90端口只能用一个,而且已经做了和原先旧的容器的端口映射,所以外部只能换成别的端口。
2.1.2在云服务器上启动一个容器
docker run 容器名称
docker run 容器名称:版本号
docker run 容器id
docker run 容器id前三位
后续很多方法都可以使用上述四种的其一来实现,效果是一样的,我们后面的文章为了简略,仅使用docker 命令 容器名称来进行实验
我们发现启动之后的容器仅在前端运行,非常影响我们后续的操作,有没有一种方法能让我们的容器“滚”去后端呢?这当然不是和Linux中的‘&’一样,虽然思想是一样的,都想放到后台。这可以用docker自带一个选项专门针对前端运行跳到后端这种情况,我们先Ctrl+C把这个前端窗口结束再使用-d选项
成功执行的话会给我们返回一个完成的镜像id,没错,上面的id才是镜像的完整id,就连我们images查看的镜像的id也仅仅是完整id的一部分。如果我们images之后没有查看到相应的镜像直接运行的话也是可以的,接下来我举个mysql的例子(因为我们的docker images中并没有mysql的镜像,即使这样,我们也是可以正常运行mysql的),如下图,最终也是会正常给你返回一个mysql的版本以及该镜像的完整id
我们可以使用docker ps命令查看正在运行的容器
每一列的解释如下:
- CONTAINER ID:容器的唯一标识符。在这个例子中,容器ID是 b206bc6d1a74。
- IMAGE:容器使用的镜像名称。这里显示的是 zealous_saha/nginx,这可能意味着使用的是一个名为 zealous_saha 的用户上传的 nginx 镜像。
- COMMAND:容器启动时执行的命令。这里显示的是 "/docker-entrypoint.",这通常是 Docker 容器启动时执行的入口脚本,用于初始化容器环境。
- CREATED:容器创建的时间。这里显示的是 12 minutes ago,意味着这个容器是在12分钟前创建的。
- STATUS:容器的当前状态。这里显示的是 Up 12 minutes,表示容器已经运行了12分钟。
- PORTS:容器暴露的端口。这里显示的是 80/tcp,意味着容器的80端口已经被映射到宿主机的某个端口上,并且使用的是TCP协议。
- NAMES:容器的名称。默认是随机的名称。
注意
1.docker ps 默认只显示正在运行的容器,所以这个容器目前是处于运行状态的。如果容器已经停止,状态会显示为 Exited 或者其他状态。想要查看其他状态的容器。
2.docker run 命令实际上是创建并运行一个新的容器,而不是直接运行镜像。镜像是容器运行的基础,但镜像本身不会执行任何操作。容器是镜像的运行实例,可以将镜像类比成“类”而容器就是类的实例化
2.1.2.1常见的查阅命令之间的区别
运行完容器之后我们想查阅容器,可以使用docker ps命令进行查看,那么现在就有小伙伴迷惑了:我们之前学过docker image命令,现在又学了docker ps命令,二者是一样的吗?
答案是否定的,具体的区别如下:
docker ps:这个命令用于列出当前正在运行的容器。默认情况下,它只显示正在运行的容器。
docker images:这个命令用于列出本地存储的镜像。
此外,还有docker images -a和docker ps -a命令,这两个命令可以和上面的命令对照
docker ps -a:这个命令与 docker ps 类似,但是 -a 或 --all 参数会显示所有容器,包括停止的和正在运行的。
docker images -a:这个命令与 docker images 类似,但是 -a 或 --all 参数会显示所有镜像,包括中间层镜像(intermediate image layers)。
总结区别:
- docker ps 与 docker ps -a 的区别在于,前者只显示正在运行的容器,后者显示所有容器。
- docker images 与 docker images -a 的区别在于,前者只显示最终的镜像,后者显示所有镜像,包括用于构建其他镜像的中间层镜像。
2.1.2.2尝试停止正在运行的容器
第一步:docker ps查看正在运行的容器,找到容器的id
第二步:docker stop 容器的id来停止正在运行的容器
第三步:重新docker ps验证停止的容器是否还在运行或者使用docker ps -a查看刚刚停止的容器的Status是否为Exited退出状态
2.1.2.3尝试启动已经停止的容器
第一步:docker ps -a找到我们STATUS为Exited的已经退出的容器的id
第二步:docker start 容器的id
第三步:docker ps 验证启动的容器
2.1.2.4尝试重启容器
无论你的容器是启动还是关闭都可以使用docker restart 容器id 命令来完成对容器的重启,只要你的容器id没有错误即可,容器id这个位置也可以替换成名称或者id的前三位,只要可以区分出即可。
2.1.2.5尝试验证容器的状态
我们如果想要知道容器的状态可以使用docker stats 容器id这种方法来验证我们容器的状态,不论是否正在运行的容器都可以进行验证。但是这会在一个新的会话中显示。
docker stats 容器id
NAME:容器的名称。
CPU %:容器使用的 CPU 百分比。
MEM USAGE:容器当前使用的内存量。
LIMIT:容器可以使用的内存上限。
MEM %:容器使用的内存百分比,相对于内存限制。
NET I/O:容器的网络输入/输出数据量。
BLOCK I/O:容器的块设备输入/输出数据量。
PIDS:容器内部当前运行的进程数。
2.1.2.6尝试查看容器的日志
我们可以使用docker logs 容器的id这条命令查看容器的日志。
docker logs 容器的id
2.1.3修改nginx的主界面使实验具有鲜明性
docker主机内我们可以安装多个容器,假设每个容器都有一个nginx我们已知nginx的默认主界面在/usr/share/nginx/html中,我们怎么就能确定能编辑我们想要的一台容器内的nginx呢?使用传统的cd到/usr/share/nginx/html中吗?显然不可能!
我们还有一个没有学到的命令:docker exec命令,该命令就是完成我们主机与一台或者多台容器内的软件进行交互的命令。接下来我们可以这样(这部分也很重要,这里敲黑板!)
docker exec -it my_nginx /bin/bash
- docker exec:Docker 命令,用于在已经运行的容器中执行命令。
- -it:这是两个参数的组合:
- -i 参数(或 --interactive)保证容器的标准输入(STDIN)是开放的,并能与用户进行交互。
- -t 参数(或 --tty)分配一个伪终端(pseudo-TTY)到容器中。
- my_nginx:指定要执行命令的容器的名称。
- /bin/bash:这是要在容器中执行的命令,以何种方式打开这个新的会话,输入/bin/bash即启动一个 Bash shell。
我们发现现已成功进入了一台容器的内部了,这样我们就能完成对这一台容器的nginx的主页进行编辑。接下来我们进入nginx的html就方便多了(注意我们现在的位置是在3c9这个容器的内部,如上图)现在我们可以对html网页进行编辑(如下图)。
更逆天的来了,vim命名没找到?我又尝试了vi基础命令,但是这个容器还是没找到,这可让我们怎么办哪???!写不进入命令就无法改网页了。
揭晓答案,原因是我们的容器内部极度的“轻量化”,导致基础命令对于容器来说基本没有几个能用的,所以我们会看到command not found没找到命令的这种情况,解决方法也很简单,我们可以使用内核上的命令echo结合重定向完成对代码的写入(重定向不明白的去我这篇重定向看细节)
2.1.4保存我们做好的镜像
第一步:先提交成一个新的镜像
docker commit -m "update index.html" my_nginx my_nginx:1.0
从名为 my_nginx 的 Docker 容器创建一个新的镜像,并为其打上标签 my_nginx:1.0。下面是命令参数的解释:
- docker commit:Docker 命令,用于从更改过的容器创建一个新的镜像。
- -m "update index.html":-m 参数后面跟随的是一个提交信息,这里写的是 "update index.html"。这是一个简短的描述,说明了为何要创建这个新的镜像。
- my_nginx:指定要用来创建新镜像的容器名称。
- my_nginx:1.0:指定新创建的镜像的名称和标签。在这个例子中,镜像名称是 my_nginx,标签是 1.0。
执行这个命令后,Docker 会基于当前 my_nginx 容器的状态创建一个新的镜像,并将其标记为 my_nginx:1.0。这个新镜像包含了容器的所有文件系统更改,例如文件的添加、修改或删除。
这样我们再使用docker images 命令的时候就能查看到我们自己创建的镜像文件my_nginx了.
第二步:将提交的新镜像保存为tar包
docker save -o my_nginx.tar my_nginx:1.0
命令 docker save -o my_nginx.tar my_nginx:1.0 用于将名为 my_nginx:1.0 的 Docker 镜像保存到一个 tar 归档文件中。下面是命令参数的解释:
- docker save:Docker 命令,用于保存一个或多个镜像到一个 tar 归档文件中。
- -o my_nginx.tar:-o 参数后面跟随的是要输出的文件名。这里指定输出文件名为 my_nginx.tar。
- my_nginx:1.0:指定要保存的镜像的名称和标签。在这个例子中,镜像名称是 my_nginx,标签是 1.0。
执行这个命令后,Docker 会将 my_nginx:1.0 镜像保存到当前目录下的 my_nginx.tar 文件中。这个文件包含了镜像的所有层和元数据。这个包我们就可以传输到移动存储设备上供别人使用了
第三步:从别人的视角使用我的tar镜像包
别人在拿到你的tar格式的镜像包应该如何去使用呢?docker load命令可以完美的解决这一问题,前提是你原来没有再安装过tar格式的镜像包(不能重复多次安装同一个镜像包,因为里面的容器id会重复导致报错!)
docker load -i 镜像包名.tar
- docker load:Docker 命令,用于从 tar 文件加载镜像。
- -i 或 --input:指定输入文件的参数,后面跟随的是 tar 文件的路径。
- 镜像包名.tar:指定要加载的 tar 文件的名称。
2.1.5将保存的镜像发布到社区
上传到社区这一步我们就不详细讲了,一是因为现在国内dockerhub进不去,二是实用性确实不是很大。。。。。。
- 登录 Docker Hub:首先,你需要使用 docker login 命令登录 Docker Hub。
docker login
输入你的 Docker Hub 用户名和密码。
- 打标签:使用 docker tag 命令给镜像打上标签。格式为 docker tag /:。
例如,如果你的镜像名称是 my_nginx,你的 Docker Hub 用户名是 yourusername,你想要推送到 my_nginx 仓库的 1.0 标签,你可以这样做:
docker tag my_nginx yourusername/my_nginx:1.0
- 推送镜像:使用 docker push 命令将镜像推送到 Docker Hub。
docker push yourusername/my_nginx:1.0
这个命令会将名为 yourusername/my_nginx:1.0 的镜像上传到 Docker Hub。
请确保你已经有一个 Docker Hub 账户,并且仓库名(在这个例子中是 my_nginx)是正确的。如果你要推送的仓库还不存在,Docker Hub 会自动为你创建它。如果仓库已经存在,这个命令会将新的标签添加到仓库中。
如果你想要推送最新版本的镜像,并且不关心标签,可以使用 latest 标签:
docker tag my_nginx yourusername/my_nginx:latest docker push yourusername/my_nginx:latest
这样,你的镜像就会被标记为最新版本,并上传到 Docker Hub。
2.2总结
目前我们docker的基础命令就讲完了,主要包括如下几条,看看大家掌握了没有
镜像
- 检索: docker search
- 下载: docker pull
- 列表: docker images
- 删除: docker rmi
- 命名: docker tag
- 保存: docker save
- 加载: docker load
容器
- 运行: docker run
- 查看: docker ps
- 停止: docker stop
- 启动: docker start
- 重启: docker restart
- 状态: docker stats
- 日志: docker logs
- 进入: docker exec
- 删除: docker rm
分享
- 提交: docker commit
- 推送: docker push
- 登录: docker login
需要注意的是:
- 配置端口映射的时候要保证docker主机的端口在安全组中是打开的状态
- 创建多个容器且每个容器都有相同的服务(例如nginx)那么nginx的所起的名称也不能是一样的。
- 使用 docker search 检索镜像时,注意检查镜像的星级和下载次数,这可以帮助你评估镜像的受欢迎程度和可靠性。
- 在使用 docker pull 下载镜像时,仅下载需要的镜像,以节省本地存储空间,并考虑使用特定标签的镜像以确保版本一致性。
- 使用 docker images 查看镜像列表时,定期清理未使用的镜像,以释放空间,并注意镜像大小,优化你的Dockerfile以减小镜像大小。
- 使用 docker rmi 删除镜像前,确保容器不在使用要删除的镜像,否则可能导致错误。
- 使用 docker tag 命名镜像时,提供有意义的标签,以便于识别和管理,避免使用过长的标签。
- 使用 docker save 保存镜像时,确保包含所有层,并考虑压缩保存的文件,以便于传输和存储。
- 使用 docker load 加载镜像时,确保加载的文件完整无损,并检查加载后的镜像是否与预期一致。
容器使用注意事项:
- 在使用 docker run 运行容器时,明确资源限制,如内存和CPU,以避免影响宿主机性能,并使用合适的安全选项,如 --read-only,以增强容器安全性。
- 使用 docker ps 查看容器时,使用 -a 参数查看所有容器,包括停止的容器,并定期检查容器列表,以识别和停止未使用的容器。
- 使用 docker stop 停止容器前,确保在停止容器前保存了必要的数据,并注意停止容器可能不会立即释放所有资源。
- 使用 docker start 启动容器时,确保容器依赖的服务已经启动,并检查容器状态,确保其按预期启动。
- 使用 docker restart 重启容器时,考虑容器中的应用程序是否支持热重启,并避免频繁重启,可能会影响服务稳定性。
- 使用 docker stats 查看容器状态时,定期监控容器资源使用情况,以优化资源分配,并注意异常的资源使用。
- 使用 docker logs 查看日志时,使用 -f 跟随日志输出,以便实时监控,并合理配置日志级别和格式。
- 使用 docker exec 进入容器时,仅在必要时进入容器,以避免对运行中的应用程序造成干扰,并使用正确的用户权限执行命令。
- 使用 docker rm 删除容器时,确保容器已经停止,以避免数据丢失,并清理与容器相关的卷和网络资源。
3.docker存储
通过上节课docker exec -it 的知识我们知道了如果我们想要修改某个容器内部的nginx的启动主界面,需要进入到容器内的对应文件位置上,而且不能使用一般的文本查看工具,这种方式十分麻烦。因为容器有自己的文件系统并且运行在自己的系统内,那么数据理应存在自己容器内部。如果某天我们的容器因为某种原因掉了无法恢复,我们再启动一个容器之后,容器是起来了,但我们配置的文件、数据呢?
有没有一种方法将我们容器内部的数据存储出来放在容器外呢?显然是有的。为了我们的实验,我们先把所有的的容器全部删除(无论运行与否)。教大家一种新的方法:
docker rm -f $(docker ps -aq)
- docker rm:这是删除容器的命令。
- -f:这是强制删除的选项,即使容器正在运行也会被删除。
- $(docker ps -aq):这是一个命令替换,它会先执行docker ps -aq命令,该命令会列出所有正在运行的容器ID,然后这些ID的值会被传递给docker rm -f命令,从而运行删除。
实验开始:
第一步:验证编辑过数据的nginx掉了之后开启新的nginx容器,界面会发生改变且数据无法保存。
步骤1:开启一个名称为a1的新容器,使用主机的91端口
docker run -d -p 91:80 --name a1 nginx
步骤2:进入该容器的主界面
docker exec -it nginx容器id的前三位 bash
cd /usr/share/nging/html
echo 1111>index.html
重新刷新界面就可以看到新的网页了。
步骤3:人为的删除该容器模拟出容器炸了的场景,重新创建一个新容器查看编辑过的主页面是否还存在
退出容器
exit
删除我们编辑过默认界面的容器
docker rm -f 容器的id前三位
重启一个容器还是使用91端口,名称还是a1
docker run -d -p 91:81 --name a1 nginx
刷新我们刚刚编辑过1111的网页查看1111是否还存在于网页之上
我们重启的容器,刷新界面之后和我们刚刚编辑过的1111并不是一个界面。进一步验证了容器内都存有各自的文件系统且容器相互隔离,碰到这样的情况又该如何处理呢?
目录挂载可以解决如上问题
存储方式之一:目录挂载
我们可以在docker的外部主机上面新建一个目录,专门挂载我们某一个容器内部的数据,如果我们的容器崩溃了,但是挂载点目录内的数据仍然存在,二者相当于形成了一个关联关系,而且docker主机还可以使用相关的linux工具,比轻量化的容器也方便了不少。
所以我们仅修改外部的挂载目录就可以映射到内部的目录了,非常的便捷。
第二步:使用目录挂载功能挂载nginx的主界面到docker外部主机
使用的docker选项是-v,而且是需要在运行容器的时候就要声明出来
我们删掉原来的容器重新试一试添加了目录挂载的新容器
docker rm -f 原容器id的前三位
docker run -d -p 91:80 -v /app/nghtml:/usr/share/nginx/html --name app01 nginx
这也将会是我们以后运行新容器的“母式”,就相当于我们上学时老师给我们讲的“母题”一样,后期仅会根据客户的需求在这条运行命令上做增删改(我整理的如下)。
docker run -d -p docker主机端口号:想要使用容器的内部的端口号 -v docker外部主机的某个目录(如果没有会自动创建):容器内部某个软件的目录(可能是容器内某个软件的工作目录也可能是容器的其他目录) --name 想要起的名称(不能重复) 运行哪个镜像
默认我们的主机挂载目录是没有东西的
现在我们重新刷新界面看看我们的nginx有什么变化
我们发现没有了nginx的默认的html网页,原因是因为我们的主机挂载点目录没有内容,nginx默认页是以主机内的目录内容为准。所以会出现403拒绝访问(即使我们跑到对应容器的nginx的软件内手动找html文件,虽然看到了nginx的index.html还存在,但是就是给你弹拒绝访问403)。
我们vim一个index.html,里面写上“你好”再来刷新看看nginx的主页有没有内容吧!
成功,出现乱码的原因是我们的nginx.conf的配置文件没有更改utf-8编码,这里仅为实验所以用不到,我们暂时不用去管
我们现在来模拟一下“炸”容器的场景:再次删除我们已经挂载到/app/nghtml目录下的那个容器。
docker -rm -f 613
ls /app
我们创建的/app/nghtml仍然存在,这就说明我们的容器爆炸并不会影响挂载目录内的数据,我们直接进入容器的内部修改html文件回到挂载点目录后再次查看html文件文件也会被修改,二者是同步的关系只不过删除了容器挂载点不会消失而已。
同理,不光光是我们的nginx的主界面的挂载,nginx的配置文件我们也是使用的最多的,能不能将nginx.conf也挂载到docker外部主机的某个目录下呢?明确的告诉你,能!但不是使用目录挂载的方式,而是卷映射!
回想一下,我们在对html进行目录挂载的时候为什么可以成功,而对nginx.conf却不可以使用目录挂载呢?因为如果我们还是以外部docker主机的目录内的内容为准的话,主界面请求不到资源倒是没问题,最多就是一个403的报错,但如果我们的配置文件使用目录挂载的话那默认也会是空的,相当于没有nginx.conf这个文件,所以我们甚至连nginx都不能成功启动。
有没有一种方式虽然我们执行了目录挂载但是目录挂载内的文件夹要和容器内部的文件保持一致的方式(外部依照内部的文件而不是默认里面仅有空的内容),这时就可以使用到卷映射的方式来解决上述问题。
存储方式之二:卷映射
卷映射的概念
卷映射(Volume Mount)是Docker中的一种数据持久化机制,它允许您将数据卷(Volume)挂载到容器内部,以便在容器之间共享数据或持久化数据。
目录挂载和卷映射二者的相同点和不同点:
相同点:
数据持久化:两者都可以用于持久化容器内的数据,即使容器被删除,数据也不会丢失。
可访问性:通过它们挂载的文件或目录都可以在容器内部被访问。
宿主机与容器间的交互:都可以实现宿主机与容器之间的文件交互。
不同点:
管理方式:
目录挂载:由宿主机的文件系统直接管理,Docker不负责管理挂载的目录。
卷映射:由Docker管理,存储在Docker的专用目录中,可以在Docker守护进程的控制下进行备份、迁移和共享。
宿主机依赖性:
目录挂载:依赖于宿主机上的文件系统结构,必须提供宿主机上的绝对路径。
卷映射:不依赖于宿主机的文件系统,Docker会在内部创建和管理卷。
性能:
目录挂载:通常性能更好,因为它直接操作宿主机的文件系统。
卷映射:可能会有额外的性能开销,因为Docker需要管理卷的存储和同步。
安全性:
目录挂载:可能会带来安全风险,因为容器内部的进程可以影响宿主机的文件系统。
卷映射:提供了更好的隔离性,卷的内容不会影响宿主机。
共享性:
目录挂载:不适合在多个容器间共享,因为它们是绑定到特定宿主机路径的。
卷映射:可以轻松地在多个容器甚至多个Docker主机间共享。
生命周期:
目录挂载:与宿主机上的文件和目录的生命周期相关联。
卷映射:与Docker卷的生命周期相关联,独立于容器的生命周期。
使用场景:
目录挂载:适合开发和测试环境,或者当需要容器访问宿主机上的特定文件或目录时。
卷映射:适合生产环境,需要持久化配置文件、日志文件或应用程序数据时。
Docker命令:
目录挂载:使用 -v 或 --volume 标志,后面跟宿主机路径和容器内路径。
卷映射:使用 -v 或 --volume 标志,后面跟卷名和容器内路径。
做个比喻吧:假设你是一个厨师,你的厨房(Docker容器)需要一些食材(数据或配置文件)来烹饪(运行应用程序)。
- 目录挂载:
- 就像你直接从冰箱(宿主机文件系统)中拿食材放到厨房里。
- 如果冰箱的某个区域(挂载点)是空的,你拿到的也是空的。
- 如果冰箱里面有预先准备好的食材,你就可以直接用。
- 卷映射:
- 就像你使用一个便携式食品储藏室(Docker卷)。
- 这个储藏室是独立的,你可以在不同的厨房(容器)之间移动它,并且它里面可以预先准备好食材(数据持久化)。
- 即使你换到另一个厨房,储藏室里的东西也不会丢失,而且你可以在不同的厨房间共享这个储藏室。
docker中实现卷映射的命令
docker run -d -p docker主机端口号:想要使用容器的内部的端口号 -v 新建的卷名:容器内部的目录(可能是容器内某个软件的工作目录也可能是容器的其他目录) --name 想要起的名称(不能重复) 运行哪个镜像
二者的命令区别我们上面也讲了,我再提取出来强调一下吧
目录挂载:使用 -v 或 --volume 标志,后面跟宿主机路径和容器内路径。
卷映射:使用 -v 或 --volume 标志,后面跟卷名和容器内路径。
目录和卷名的区分:目录是以./或/开头的而剩余的所有情况冒号前都是卷名,这里不要混淆二者命令上的区别
第三步:运行一个容器,同时完成对主页的目录挂载和卷映射,并验证
步骤1:运行一个容器
'我们再将第二步创建的容器删除,以防对我们现在的实验造成影响。
docker rm -f d9e
重新运行一个容器,对其运行方式、端口、存储方式、名称和使用的镜像进行声明
docker run -d -p 91:80 -v /app/nghtml:/usr/share/nginx/html -v ngconf:/etc/nginx --name app01 nginx
需要注意的是,我们的卷映射的位置docker统一放在了/var/lib/docker/volumes/文件夹下,文件夹内有我们创建的卷名ngconf
我们一步一步的进入就可以看到和nginx一样的配置目录了
步骤2:验证
重新刷新浏览器,因为我们目录挂载的位置和我们第二步做的那个容器的位置一样,所以我们还是会看到乱码的“你好”。
总结:
1.一句话理解目录挂载和卷映射
目录挂载是外-->内,卷映射是内-->外(这里的“外”指的是宿主机的文件系统,“内”指的是Docker容器的文件系统)
2.docker中对于卷的常见操作命令
显示所有创建的卷名
docker volume ls
创建卷
docker volume create 卷名
查看卷内信息
docker volume inspect 卷名
3.存储特性
无论是卷映射还是目录挂载我们删除容器的时候它们仍然会在我们的docker宿主机上存在
4.docker网络
本章独立于前述的所有章节,我们从一个问题开始:我们知道,外部用户需要访问某一台容器的时候需要进入宿主机的某个端口,且宿主机的端口与容器内部需要做端口映射,这样外面的人就可以访问进来了,那如何实现容器和容器之间通过IP:端口的形式访问呢?
我们先使用docker rm -f $(docker ps -aq)删除所以曾经创建好的容器,方便我们的实验。接下来创建两个不同端口的nginx容器
docker run -d -p 90:80 --name a1 nginx
docker run -d -p 91:80 --name a2 nginx
4.1如何完成a1访问a2呢(容器互访)?
第一步:查看容器细节
docker inspect 容器名称
docker inspect a1
使用该命令会弹出一堆内容,我们直接下滑到最后,有个IPAddress,这个就是容器的ip地址,同理我们再查看另一个容器的IP地址
docker inspect a2
拿到了第二个容器的ip,这样我们两台容器的ip都知道了(a1:172.17.0.2 a2:172.17.0.3)这个时候就可以完成互访了。
第二步:进入容器内部
如果我们想要使用a1访问a2的话我们要进入a1的内部
docker exec -it a1 bash
第三步:开始访问
我们使用curl连接命令连接到a2容器上,注意不要忘记容器的端口号(容器的端口号是开放80的而不是docker宿主机的端口号91!)
curl http://172.17.0.3:80
这样我们就拿到了a2的nginx的主界面,和浏览器的界面代码一模一样
实现原理
我们使用ip a的时候会发现存在一个docker 0 的IP地址,在Docker中,docker0是一个虚拟网桥,它为宿主机上运行的容器提供了一个虚拟的网络环境。当Docker启动时,它会自动创建这个网桥,并为它分配一个IP地址,通常在172.17.0.0/16网段内。这个地址作为默认网关,允许容器内部的所有网络流量通过它进行路由。容器会将自己的网关设置为docker0的IP地址,从而能够与宿主机和其他容器通信。这就像在宿主机内部创建了一个虚拟的局域网,其中docker0扮演着网关的角色,而容器则是连接到这个网关的设备。通过这种方式,Docker确保了容器之间的网络隔离,同时允许它们通过宿主机安全地访问外部网络。
4.2创建自定义网络从而方便容器之间使用域名进行互访
上述使用ip访问的方式也是存在缺点的,ip可能会变化且不好记忆。为此,我们可以创建一个自定义的网络,让我们的容器连接到docker网关的同时连接到自定义的网络,因为docker 0网络是不能设置域名的,我们可以给我们的自定义网络设置域名,方便我们的管理和记忆。
第一步:删除原来的容器
docker rm -f $(docker ps -aq)
第二步:创建一个网络
docker network create 网络名称
创建完网络之后我们可以进行验证
docker network ls
在Docker中,bridge、host、和none是三种不同的网络驱动模式,它们决定了容器如何与外部网络通信:
bridge(桥接):
默认网络模式。
Docker会创建一个虚拟的docker0网桥,新启动的容器会连接到这个网桥上。
容器将获得一个从特定IP地址范围分配的IP地址,并且可以与其他容器以及外部网络通信,但前提是需要进行端口映射。
容器之间可以通过各自的网络接口相互通信,但与外部通信需要通过网桥。
host(宿主机):
容器将不会获得自己的网络命名空间,而是直接使用宿主机的网络栈。
容器将共享宿主机的网络,包括IP地址、端口等。
容器内部的进程可以看到宿主机上的所有网络接口,并且可以直接与外部网络通信,就像宿主机上的进程一样。
这种模式下,容器之间的网络隔离被完全消除,容器间网络通信与宿主机相同。
none(无网络):
容器将拥有自己的网络命名空间,但Docker不会为它进行任何网络配置。
容器将不会有任何网络接口,除非手动添加。
这种模式通常用于自定义网络配置或者在容器内部使用特定网络工具。
第三步:将新创建的容器加入到自定义的网络中
docker run -d -p 90:80 --network mynet --name a1 nginx
docker run -d -p 91:80 --network mynet --name a2 nginx
第四步:进入a1容器访问a2容器
docker exec -it a1 bash
curl http://a2:80
注意:
1.自定义网络的ip和docker 0的IP不是一样的
2.使用自定义网络的容器相互访问的格式为
curl http://想要访问的容器的名称:容器的开放的端口
容器名就是容器之间互相访问的域名,不需要和docker 0 一样使用ip了
实验:使用docker创建一个Redis的主从同步集群
因为我们设置了主从关系,所以从机需要同步主机的数据,这样主机新增的数据从机都会同步,实现读写分离,读请求放到Master主机上,而读请求放到Slave从机上从而实现读写分离。
二者作为主从的前提是:分布在同一个网络,容器开放的服务需要是Redis服务端口,且为了保证数据的持久化最好在运行容器的时候做上目录挂载保证数据不丢失。而2号Redis机要配置自己为1号Redis的从机,Master主机要知道自己是2号Redis的主机。
我们先下载redis,我使用的版本是bitnami/redis因为这个版本对新手比较友好
docker run -d -p 6379:6379 -v /app/rd1:/bitnami/redis/data -e REDIS_REPLICATION_MODE=master -e REDIS_PASSWORD=123456 --network mynet --name redis01 bitnami/redis
这条 docker run 命令用于在 Docker 中启动一个 Redis 容器,具体参数解释如下:
-d:以守护进程模式运行容器(后台运行)。
-p 6379:6379:将容器内的 6379 端口映射到宿主机的 6379 端口。这样,宿主机和其他机器可以通过宿主机的 6379 端口访问容器中运行的 Redis 服务。
-v /app/rd1:/bitnami/redis/data:挂载宿主机的 /app/rd1 目录到容器内的 /bitnami/redis/data 目录。这是一个数据卷挂载,用于持久化 Redis 数据,确保容器重启或删除后数据不会丢失。
-e REDIS_REPLICATION_MODE=master:设置环境变量 REDIS_REPLICATION_MODE 为 master,表示这个 Redis 容器将以主节点模式运行。
-e REDIS_PASSWORD=123456:设置环境变量 REDIS_PASSWORD 为 123456,为 Redis 设置密码保护。这样,客户端连接 Redis 时需要提供密码。
--network mynet:指定容器加入到名为 mynet 的自定义网络。这个网络必须是之前已经创建好的。
--name redis01:给容器指定一个名称 redis01,便于后续管理。
bitnami/redis:指定要使用的 Docker 镜像,这里是 Bitnami 官方提供的 Redis 镜像。
总结来说,这个命令是在 Docker 中启动一个设置了密码保护、以主节点模式运行的 Redis 容器,并且将容器的 6379 端口映射到宿主机的 6379 端口,同时将宿主机的 /app/rd1 目录挂载到容器内用于数据持久化,容器加入到自定义网络 mynet 中,容器名称为 redis01。
下载完一个之后我们验证一下redis是否真的在运行了
并没有运行,现在正在运行的容器只有nginx,我们使用docker images发现redis确实被下载下来了但是容器就是运行不起来,这时候建议大家查看日志的报错信息
docker logs 镜像的完整id(就是下载完成镜像之后后面一长串的完整id)
我们找到日志的最后一行Can’t......发现是因为权限拒绝导致的无法运行Redis容器,这样就好说了,我们将该文件的权限设置为最大即可。 运行程序需要进入到挂载目录的位置,我们修改挂载目录文件的权限
chmod -R 777 /app/rd1
修改完成之后重启redis01
docker restart redis01
重启完成之后再来验证一遍redis是否已经成功启动
docker ps
redis已经成功的被拉起来了。因为权限的问题我们一会再拉一个redis02的时候文件配置就不用写了我们现在自己先配置上
mkdir rd2
chmod -R 777 rd2
文件创建完成之后我们启动第二个redis
docker run -d -p 6380:6379 -v /app/rd2:/bitnami/redis/data -e REIDS_REPLICATION_MODE=slave -e REDIS_MASTER_HOST=redis01 -e REDIS_MASTER_PORT_NUMBER=6379 -e REDIS_MASTER_PASSWORD=123456 -e REDIS_PASSWORD=123456 --network mynet --name redis02 bitnami/redis
解释:
-d:以守护进程模式运行容器(即在后台运行)。
-p 6380:6379:将容器内的 6379 端口映射到宿主机的 6380 端口。这样,您可以通过宿主机的 6380 端口访问容器中运行的 Redis 服务。
-v /app/rd2:/bitnami/redis/data:将宿主机的 /app/rd2 目录挂载到容器内的 /bitnami/redis/data 目录。这用于数据持久化。
-e REIDS_REPLICATION_MODE=slave:设置环境变量 REDIS_REPLICATION_MODE 为 slave,表示这个 Redis 容器将以从节点模式运行。(注意:环境变量名中有一个拼写错误,应为 REDIS_REPLICATION_MODE 而不是 REIDS_REPLICATION_MODE)。
-e REDIS_MASTER_HOST=redis01:设置环境变量 REDIS_MASTER_HOST 为 redis01,指定主节点的主机名。
-e REDIS_MASTER_PORT_NUMBER=6379:设置环境变量 REDIS_MASTER_PORT_NUMBER 为 6379,指定主节点的端口号。
-e REDIS_MASTER_PASSWORD=123456:设置环境变量 REDIS_MASTER_PASSWORD 为 123456,指定连接主节点时需要使用的密码。
-e REDIS_PASSWORD=123456:设置环境变量 REDIS_PASSWORD 为 123456,为从节点设置密码。
--network mynet:指定容器加入到名为 mynet 的自定义网络。
--name redis02:给容器指定一个名称 redis02。
bitnami/redis:指定要使用的 Docker 镜像,这里是 Bitnami 官方提供的 Redis 镜像。
总结来说,这个命令是在 Docker 中启动一个作为 Redis 从节点的容器,并且将容器的 6379 端口映射到宿主机的 6380 端口。容器将尝试连接到名为 redis01 的主节点,使用端口 6379 和密码 123456 进行认证。此外,容器的 /app/rd2 目录被挂载到容器内用于数据持久化,并且容器加入到自定义网络 mynet 中,容器名称为 redis02。
5.容器统一管理Docker Compose
5.1何为Docker Compose?
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它使用 YAML 文件来配置您的应用服务,然后使用一个简单的命令行接口来启动和停止整个应用程序。Docker Compose 特别适合开发、测试、部署多容器应用。
做个关于它的比喻方便大家去理解
想象一下,一个乐队中有各种各样的音乐家,每个音乐家都演奏不同的乐器,他们就像是Docker中的不同容器,每个容器运行着不同的服务,比如数据库、网站服务器、缓存服务等。
而乐队指挥(Docker Compose)的角色就是确保所有音乐家(容器)能够和谐地一起演奏。指挥会告诉每个音乐家(容器)什么时候开始演奏(启动),什么时候结束(停止),以及他们应该如何与其他音乐家(容器)协作。
5.2使用Docker Compose的前提
我们必须提前写好一个.yaml格式的文件,该文件中存储了启动的容器的集合,我们统一对该文件进行操作就相当于统一的管理我们的容器了(可以对yaml文件内的所有容器进行管理也可以对yaml文件的部分容器进行管理),具体的管理方式如下,我按照命令分了几类,方便大家查看
基本命令
- docker-compose up:启动所有服务,如果我们up后面还根内容的话表示我们启动部分容器的服务,例如docker-compose up web db
- docker-compose up -d:在后台启动所有服务。
- docker-compose up --build:在启动服务前重新构建服务。
- docker-compose down:停止并删除容器、网络、卷、镜像。
- docker-compose stop:停止所有服务,如果我们stop后面还根内容的话表示我们停止部分容器的服务,例如docker-compose stop web
- docker-compose start:启动已经停止的服务。
- docker-compose restart:重启所有服务。
- docker-compose down --volumes:停止服务并删除数据卷。
服务管理
- docker-compose start [服务名...]:启动指定的服务。
- docker-compose stop [服务名...]:停止指定的服务。
- docker-compose restart [服务名...]:重启指定的服务。
- docker-compose rm [服务名...]:删除指定的服务的容器。
- docker-compose scale [服务名]=[数量]:设置指定服务的容器数量。
配置和状态
- docker-compose config:验证并查看 Compose 文件配置。
- docker-compose pull:拉取服务依赖的镜像。
- docker-compose build:构建或重建服务依赖的镜像。
- docker-compose status:显示服务的状态。
日志和调试
- docker-compose logs:查看服务的日志,如果我们logs后面还根内容的话表示我们查看部分容器的日志,例如docker-compose logs db
- docker-compose logs -f:实时查看服务的日志流。
- docker-compose exec [服务名] [命令]:在指定服务的容器中执行命令。
帮助和信息
- docker-compose --version 或 docker-compose -v:查看 Docker Compose 的版本。
- docker-compose help:查看命令的帮助信息。
其他命令
- docker-compose pause [服务名...]:暂停服务中的所有容器。
- docker-compose unpause [服务名...]:恢复服务中的所有容器。
- docker-compose run [服务名] [命令]:在指定服务中运行一次性命令。
网络管理
- docker-compose network ls:列出所有网络。
- docker-compose network create [网络名]:创建一个新的网络。
- docker-compose network rm [网络名...]:删除一个或多个网络。
卷管理
- docker-compose volumes:列出所有卷。
- docker-compose volume ls:列出所有卷。
- docker-compose volume create [卷名]:创建一个新的卷。
- docker-compose volume rm [卷名...]:删除一个或多个卷。
实验:安装mysql和wordpress博客,并且我们写的博客都能存放到mysql中
第一步:安装mysql
安装前还是需要删除我们原来的容器
docker rm -f $(docker ps -aq)
创建一个自定义网络,方便mysql和wordpress共处于一个net中
docker network create blog
docker network ls
下载关于mysql的镜像并做好配置
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -v mysql-data:/var/lib/mysql -v /app/myconf:/etc/mysql/conf.d --restart always --name mysql --network blog mysql:8.0
解释如下
-d:以守护进程模式在后台运行容器。
-p 3306:3306:将容器内的 3306 端口映射到宿主机的 3306 端口,这样外部可以访问容器中运行的 MySQL 服务。
-e MYSQL_ROOT_PASSWORD=123456:设置环境变量 MYSQL_ROOT_PASSWORD 为 123456,这是 MySQL 的 root 用户密码。
-e MYSQL_DATABASE=wordpress:设置环境变量 MYSQL_DATABASE 为 wordpress,这会在 MySQL 启动时自动创建一个名为 wordpress 的数据库。
-v mysql-data:/var/lib/mysql:创建并挂载一个名为 mysql-data 的数据卷到容器的 /var/lib/mysql 目录,用于持久化 MySQL 数据。
-v /app/myconf:/etc/mysql/conf.d:将宿主机的 /app/myconf 目录挂载到容器的 /etc/mysql/conf.d 目录,允许自定义 MySQL 配置文件。
--restart always:设置重启策略为 always,这意味着如果容器退出,Docker 将尝试无限次重启它。
--name mysql:给容器指定一个名称 mysql。
--network blog:指定容器加入到名为 blog 的网络,这个网络需要事先创建好。
mysql:8.0:指定要使用的 MySQL 镜像版本为 8.0。
执行完上述的命令后我们docker ps验证一下mysql有没有运行,发现mysql成功运行了
第二步:下载wordpress
docker run -d -p 8080:80
-e WORDPRESS_DB_HOST=mysql
-e WORDPRESS_DB_USER=root
-e WORDPRESS_DB_PASSWORD=123456
-e WORDPRESS_DB_NAME=wordpress
-v wordpress-data:/var/www/html
--restart always
--name wordpress-app
--network blog
wordpress:latest
命令的解释如下
-d:以守护进程模式运行容器。
-p 8080:80:将容器的 80 端口映射到宿主机的 8080 端口。
-e WORDPRESS_DB_HOST=mysql:设置环境变量,指定 WordPress 连接的数据库主机名。
-e WORDPRESS_DB_USER=root:设置环境变量,指定数据库用户。
-e WORDPRESS_DB_PASSWORD=123456:设置环境变量,指定数据库密码。
-e WORDPRESS_DB_NAME=wordpress:设置环境变量,指定 WordPress 使用的数据库名。
-v wordpress-data:/var/www/html:挂载一个名为 wordpress-data 的数据卷到 WordPress 的网页目录,用于数据持久化。
--restart always:设置容器的重启策略为始终重启。
--name wordpress-app:给容器指定一个名称。
--network blog:将容器连接到名为 blog 的自定义网络。
wordpress:latest:指定使用 WordPress 的官方镜像的最新版本。
第三步:验证
我们输入宿主机的ip:8080查看wordpress是否被访问到了,输入了我的ip123.57.240.242:8080也是成功的进入到wordpress的主页了。
我们继续选择简体中文,看看这个wordpress网站吧
在欢迎界面做好如下配置,用户名我建议为root,密码改成123456,点击下方的“安装wordpress”即可完成安装
我们也可以进入到后台进行管理,后台的网址为
配置wordpress的docker主机ip:8080/wp-login.php
输入我们刚刚设置的账号(root)密码(123456)即可进入管理员后台。可以自己完成一篇文章的创作
我自己的创作如下,点击发布即可
还有很多好玩的内容供大家探索这里我就不展示了,我们接下来将这些配置写到docker compose文件中完成一键启动,这样如果别的机器想要启动wordpress就不用大费周章的重新去写配置文件了
综合实验:使用docker compose完成对于wordpress、mysql的一键启动
.yaml文件
在yaml内配置好我们需要使用的容器信息来完成接下来的实验,我们先创建一个名为compose.yaml的文件,那么问题就来了
yaml文件怎么写?
我们第一次谈及yaml文件的时候一般都是在说可以一键启动,可以配置状态、日志, 但是真正到我们写的时候应该如何去写确实优点犯难。今天我先给大家简述一下yaml文件的基本写法。
一个yaml最基础的应该包括四个键,name键、service键、network键、volumes键。
name键
name引导的是后续的三个键(service键、network键、volumes键)所以说一个yaml文件内可以定义多个name键,name键的主要作用就是标识和映射
service键:
- 容器编排:在Docker Compose中,services是YAML文件的主要部分,用来定义一个或多个服务(容器)。每个服务定义了如何构建、启动和运行一个容器。
- 微服务架构:在微服务架构中,service键可能用来描述一个特定的服务,包括它的依赖、配置和端点。
- 云服务:在云服务配置中,service键可能用来指定一个特定的服务实例,如数据库服务、消息队列服务等。
network键:
- 容器网络:在Docker Compose中,networks键用来定义网络配置,允许服务之间或与外部网络进行通信。它可以定义服务如何连接到网络,以及如何配置网络的IP地址、子网等。
- 虚拟网络:在虚拟化环境中,network键可能用来描述虚拟网络的配置,包括网络地址、网关、子网等。
- 云网络:在云服务配置中,network键可能用来定义云资源的网络设置,如虚拟私有云(VPC)配置。
volumes键:
- 容器卷:在Docker Compose中,volumes键用来定义卷,这些卷可以被一个或多个服务使用,用于持久化数据或共享数据。卷可以是匿名卷、命名卷或绑定挂载。
- 存储卷:在虚拟化或云服务配置中,volumes键可能用来描述存储卷的配置,包括卷的大小、类型、访问权限等。
- 数据持久化:在应用程序配置中,volumes键可能用来指定数据存储的位置,确保应用程序数据的持久化和备份。
第一步:开始写.yaml文件
Windows桌面新建一个任意名称的yaml文件,我使用的名字是compose.yaml,双击使用文本文档打开进行编写。
name: myblog # 指定名称键,一个yaml文件中可以有多个这样的名称键
services: # 定义服务列表
mysql: # 服务名称,这里定义了一个名为 mysql 的服务,服务名可以自定义名称,这里为了好记写的mysql,下面的wordpress的服务键也可以改成自己喜欢的名字
container_name: mysql # 为容器指定一个名称
image: mysql:8.0 # 使用 MySQL 的官方镜像,版本为 8.0
ports: # 定义端口映射
- "3306:3306" # 将容器内的 3306 端口映射到宿主机的 3306 端口
environment: # 定义环境变量
MYSQL_ROOT_PASSWORD: 123456 # 设置 MySQL 的 root 用户密码
MYSQL_DATABASE: wordpress # 创建一个名为 wordpress 的数据库
volumes: # 定义卷挂载
- mysql-data:/var/lib/mysql # 挂载一个名为 mysql-data 的数据卷到 MySQL 数据目录
- /app/myconf:/etc/mysql/conf.d # 挂载宿主机的配置目录到容器的 MySQL 配置目录
restart: always # 设置容器的重启策略为 always,即总是重启
networks: # 定义服务所在的网络
- blog # 服务将连接到名为 blog 的网络
wordpress: # 定义了一个名为 wordpress 的服务
image: wordpress # 使用 WordPress 的官方镜像
ports: # 定义端口映射
- "8080:80" # 将容器内的 80 端口映射到宿主机的 8080 端口
environment: # 定义环境变量
WORDPRESS_DB_HOST: mysql # 设置 WordPress 数据库主机为 mysql 服务
WORDPRESS_DB_USER: root # 设置 WordPress 数据库用户为 root
WORDPRESS_DB_PASSWORD: 123456 # 设置 WordPress 数据库密码
WORDPRESS_DB_NAME: wordpress # 设置 WordPress 使用的数据库名称
volumes: # 定义卷挂载
- wordpress:/var/www/html # 挂载一个名为 wordpress 的数据卷到 WordPress 的网页目录
restart: always # 设置容器的重启策略为 always
networks: # 定义服务所在的网络
- blog # 服务将连接到名为 blog 的网络
depends_on: # 定义服务的启动依赖
- mysql # 指定 wordpress 服务在 mysql 服务启动后启动
volumes: # 定义数据卷
mysql-data: # 定义一个名为 mysql-data 的数据卷
wordpress: # 定义一个名为 wordpress 的数据卷
networks: # 定义网络
blog: # 定义一个名为 blog 的网络
以上就是我写的yaml文件,每一行基本上都添加了注释,希望大家可以明白
第二步:将我们原来手动在docker配置的网络、卷、容器全部删除干净
docker rm -f $(docker ps -aq)
docker volume rm mysql-data wordpress-data wordpress-data
docker network rm blog
以上是删除部分
docker ps
docker network ls
docker volumes ls
以上是验证部分
删除完成之后我们的docker主机应该如上图所示,现在我们了compose文件,在我们没有运行这些服务的docker上简直不要太简单
第三步:批量启动容器,我们只需要up yaml类型的文件即可
docker compose -f 我们编写好的yaml类型的文件 up -d
- -f:指定运行的yaml类型的文件名称
- up:指定文件的操作方式,up是上线、start是启动、stop是停止、restart是重启
- -d:compose上线的文件后台运行,-d选项一般搭配up、start和restart一起使用
执行指令之后弹出的窗口如下:
如果你不相信的话重新进入浏览器输入IP:8080进行查看即可
yaml文件在书写时候的注意事项
1.键值对
这是我们第一次写yaml类型的文件,有非常多的问题我需要向大家说明,yaml是对格式要求及其严格的一种文件格式,我曾经写的时候因为一个空格的问题导致yaml文件产生了严重的报错从而导致无法成功上线,我先给大家看看因为格式而弹出的报错信息
上图是因为我们yaml文件的格式错误导致的无法上线从而弹出的报错,大家如果没遇到最好先混个眼熟,就怕以后写yaml文件真的遇见了就别慌了,乖乖改格式就好了。可以给大家揭晓一下关于空格的正确格式和错误格式(写错了在bash文件中颜色是不会发生改变的)
错误格式:
正确格式
发现了吗?如果我们少写了一个空格就会导致yaml文件无法上下或运行,可以通过判断代码的颜色看自己的代码有没有报错,正确的代码键是蓝色的,错误的代码键是原色,和未响应一样。
2.缩进:
- YAML 文件使用空格进行缩进,不能使用制表符(Tab)。
- 同一级别的缩进必须是一致的,通常是两个或四个空格。
3.表示方法
对于服务键可以使用数组或者键值的方式进行表示
数组方法:
volumes: # 定义卷挂载
- mysql-data:/var/lib/mysql
键值方法:
environment: # 定义环境变量
WORDPRESS_DB_HOST: mysql
4.关于上线之后
只要我们的服务拉起来了之后,原来对docker的操作现在对于服务内的镜像仍然好使,你就把使用yaml文件拉起来的镜像看成小学里转学过来的同学,来了就一块受管理,日志文件、删除新增查找等操作都可以正常进行
其他需要注意的问题
空格敏感:
YAML 对空格非常敏感,多余的空格可能会导致解析错误。
注释:
使用 # 添加注释,但只能在行的末尾或完全空的行使用。
不能在键值对的值内部使用 # 进行注释。
字符串:
可以使用单引号(')或双引号(")来定义字符串,但要保持一致。
双引号字符串中可以包含特殊字符,如换行符和制表符。
布尔值:
布尔值应该使用 true 或 false,并且是大小写敏感的。
列表:
列表中的每个元素都应该以短横线(-)开头,后面跟一个空格。
映射:
映射是键值对的集合,每个键后面都应该跟一个冒号和空格,然后是值。
文件编码:
文件应该是 UTF-8 编码,避免使用其他编码。
特殊字符:
如果值中包含特殊字符,如 &, *, !, '|', , #, @, ^, ~, %, ?, :, =, /, ", ', ,, {, }, [, ] 等,可能需要使用引号括起来。
多文档:
如果文件中包含多个文档,每个文档之间应该用 --- 分隔。
缩进块:
对于更复杂的数据结构,可以使用 | 来保留换行符,或者使用 > 来折叠换行符。
浮点数:
浮点数应该使用小数点(.)作为分隔符。
使用流风格:
YAML 支持流风格(Flow Style),在某些情况下,使用大括号 {} 或方括号 [] 可以更清晰地表示数据结构。
工具验证:
使用 YAML 验证工具来检查文件的语法是否正确。
一致性:
在团队中保持一致的 YAML 编写风格。
版本控制:
将 YAML 文件放入版本控制系统中,以便跟踪更改。
避免不必要的复杂性:
尽量保持 YAML 文件的简洁和清晰,避免不必要的复杂性。
最后我们验证一下上线之后的docker后的相关工具是否起来了
试验成功,不管是网络还是卷还是容器都有了!
因为我们这也是第一次写yaml文件,我们所用到的知识知识yaml的冰山一角,碍于国外站点docker hub现在进不去,yaml的所有的配置文件都在那里面,我给大家整理了几个网站,方便大家茶余饭后稍微了解一下yaml。
以上就是我们docker的整个章节了,非常感谢大家能看到最后!后续我也会继续更新博文,走波关注不迷路~