【容器化技术】Docker从入门到进阶,一遍文章就够了

文章目录

温馨提示:所有文章中所提及的Docker相关的案例的安装包和工具,都可以在我的网盘中直接下载:
链接:https://pan.baidu.com/s/1pgboR8O5DjZoZK9f1NvK9g
提取码:30in

1. Docker简介

在这里插入图片描述

1.1 虚拟化技术发展概述

在虚拟化技术出现之前,如果想要搭建一台Java服务器大致流程如下:

  • 购买一台硬件服务器

  • 在硬件服务器上安装配置Linux操作系统

  • 在操作系统之上配置应用运行环境JDK、Tomcat、MySQL等

  • 部署并运行应用

这种方式的缺点:

  • 部署应用非常慢

  • 应用迁移麻烦,要将应用迁移到一台新机器,又需要重来一遍

所以,随着技术发展,改进了这一问题,出现了虚拟化技术,它是一种计算机资源管理技术,它将计算机的各种硬件资源,比如硬盘、网络、CPU、内存等硬件资源,予以抽象和转换从而呈现出一套新的硬件资源环境,在这一套新的硬件环境下可以安装我们的操作系统,部署我们的应用运行环境等,它打破了计算机硬件资源不可切割的问题,使我们可以更加充分更加合理地利用计算机硬件资源,虚拟化技术又分为:硬件级虚拟化操作系统级虚拟化

1.2 硬件级虚拟化

硬件级虚拟化是运行在硬件之上的虚拟化技术,它的核心技术是Hypervisor ,Hypervisor是一种运行在基础物理服务器硬件之上的软件层,可以虚拟化硬件资源,例如CPU、硬盘、内存、网络资源等,然后我们可以通过在虚拟化出来的资源之上安装操作系统,这也就是所谓的虚拟机,像VMWare, VirtualBox等都是使用该技术,通过Hypervisor层,我们可以创建不同的虚拟机,并且每个虚拟机都是隔离独立的,这样一来,我们就可以在一台硬件服务器和操作系统之上虚拟化出多个虚拟服务器,用来部署我们的应用
在这里插入图片描述
硬件级虚拟化技术的优点:

  • 一台物理服务器可以虚拟化出多个虚拟的操作系统,让计算机资源得以充分利用

硬件级虚拟化技术的缺点:

  • 每创建一个虚拟机的时候,都会创建一个操作系统,操作系统本身就会占用很多资源,这样无疑大大的增加了硬件资源的消耗,当安装的虚拟机越多,硬件资源消耗就越多
  • 环境兼容性问题,多个虚拟机之间会有操作系统环境的差异化,例如部分虚拟机正常部署运行,部分虚拟机则有可能出现异常情况
  • 部署不便捷,随着我们的软件系统越来越庞大,需要部署的服务会越来越多,每次进行软件升级时,每台虚拟机服务器都需要进行重新部署,工作量巨大,而且出错情况增加

1.3 操作系统级虚拟化

基于硬件级虚拟化技术的缺点和不足,随着技术的发展,发展出了另一种虚拟化技术,即操作系统级虚拟化技术。操作系统级虚拟化是运行在操作系统之上的虚拟化技术,它模拟的是运行在一个操作系统上的多个不同进程,并将其封装在一个密闭的容器里面,该技术也称为容器化技术,在容器化技术领域,Docker是目前最流行的一种实现

在这里插入图片描述

1.4 Docker概述

  • Docker发布于2013年,基于LXC技术,LXC是Linux平台上的容器化技术实现。LXC是Linux Container的简写,它是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,它与宿主机使用同一个内核,性能损耗小,这种技术是Linux提供的,但是直到Docker出世,该技术才被发挥出来,官方网站:https://www.docker.com

  • Docker是一个开源的应用容器引擎,基于 Go 语言并遵从Apache2.0协议开源,项目代码托管在GitHub上进行维护 :https://github.com/docker/docker-ce

  • Docker技术让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,打包好的容器可以发布到任何流行的 Linux服务器上运行,这样就可以解决开发环境与运维环境不一致的问题了,所以容器技术解决了开发和运维之间的矛盾,让开发专注于开发,运维专注于运维,不要被环境问题所打扰

  • Docker彻底释放了虚拟化的威力,极大降低了计算机资源供应的成本,Docker重新定义了程序开发测试、交付和部署过程,Docker 提出了“构建一次,到处运行”的理念,让应用的开发、测试、部署和分发都变得前所未有的高效和轻松

  • Docker是一种轻量级的操作系统虚拟化解决方案,Docker 的基础是 Linux 容器(LXC)技术,在LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单,其资源的开销是极低的

  • Docker 自开源后受到广泛的关注,Docker最早是基于Ubuntu开发的,但后续CentOS、Debian、Fedora等主流的Linux操作系统都支持Docker

  • Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),我们用社区版就可以了

  • 简单地说,Docker是对软件和其依赖环境的标准化打包(类似于一个集装箱),应用之间相互隔离,共享一个操作系统内核(解决了资源浪费的问题),可以运行在很多主流操作系统上。但是需要注意的是Docker本身不是容器,Docker只是管理容器的引擎

1.5 容器和虚拟机的区别

容器是将代码和环境的关系打包在一起的一个集合,而虚拟机是在物理层面上,分出来的一个操作系统。多个容器可以运行在同一台物理服务器上,并共享一个操作系统的内核资源。多个虚拟机也可以运行在同一台机器上,但每个虚拟机都需要一个完整的操作系统,其对比可见下面:

特性容器虚拟机
启动秒级分钟级
硬盘空间一般为几十MB一般为10GB
性能接近原生弱于原生
系统支持量单机支持上千个容器一般几十个
操作系统与宿主机共享OS宿主机OS上运行虚拟机OS

1.6 为什么要使用Docker

作为一种新兴的虚拟化技术,Docker跟传统的硬件虚拟化方式相比具有众多的优势:

  • Docker容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多
  • Docker对系统资源的利用率很高,一台主机上可以同时运行数千个Docker容器
  • 容器除了运行其中的应用外,基本不消耗额外的系统资源,使得应用的性能很高,传统虚拟机方式运行10个完全不同的应用可能我们会起10个虚拟机来部署,而Docker 只需要启动10个隔离的应用即可
  • Docker可以更快速的交付和部署,大量地节约开发、测试、部署的时间,对开发和运维人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行
  • 更高效的虚拟化,Docker容器的运行不需要额外的Hypervisor支持,它是内核级的虚拟化,因此可以实现更高的性能和效率
  • 更轻松的迁移和扩展,Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等,这种兼容性可以让用户轻松地把一个应用程序从一个平台直接迁移到另一个平台

2. Docker安装

  • Docker从2013年3月20日发布Docker 0.1,到现在已经发布了多个版本,从2017年3月开始Docker在原来的基础上分为两个分支版本: Docker CEDocker EEDocker CE即社区免费版,可永久免费使用,Docker EE即企业版,功能更全,更强调安全,但需付费使用
  • Docker官方网站:https://www.docker.com/
  • 中文社区:https://www.docker.org.cn/index.html
  • 首先我们知道Docker并不是容器,它是一个管理容器的引擎,我们采用的Linux版本是CentOS7,学习Docker也更推荐在Linux环境下使用,目前Docker支持CentOS7及以后的版本,CentOS版:https://docs.docker.com/engine/install/centos/

2.1 在线安装

  1. Docker要求CentOS系统的内核版本高于3.10 ,验证你的CentOS版本是否支持 Docker

    uname -r
    
  2. Docker可以使用yum直接安装,首先在root账号下确保yum工具更新到最新

    yum update
    
  3. 安装前可以查看一下系统是否已经安装了Docker,如果安装过旧版本的话

    yum list installed | grep docker
    
    #如果已经安装了Dokcer则执行卸载
    yum -y remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
    
  4. 设置Dokcer的存储库,在首次安装Docker之前,需要设置Docker的存储库,之后我们就可以从存储库中安装和更新Docker

    #安装yum-utils工具,它提供了yum-config-manager工具
    yum -y install yum-utils
    
    #设置存储库,官方推荐的存储库
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
    #官方推荐的存储库,可能会有网络延迟等问题,导致下载速度较慢或超时,所以我们可以改用阿里云镜像
    yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  5. 更新yum缓存,设置完毕yum存储库后,我们需要对其缓存数据进行更新

    yum makecache fast
    
  6. 安装Docker

    yum -y install docker-ce docker-ce-cli containerd.io
    
  7. 查看Docker是否安装成功

    docker -v
    
  8. Docker系统服务相关命令

    #启动Docker服务
    systemctl start docker
    
    #停止Docker服务
    systemctl stop docker
    
    #重启Docker服务
    systemctl restart docker
    
    #设置开机启动
    systemctl enable docker
    
    #查看Dokcer运行状态
    systemctl status docker
    
    #查看Docker进程
    ps -ef | grep docker
    
    #查看Docker系统信息
    docker info
    
    #查看Docker所有帮助信息
    docker --help
    
    #查看某个commond命令的帮助信息
    docker commond --help
    

2.2 离线安装

  1. 下载 Docker 离线包

    #在这里可以选择自己需要的版本进行下载,我们这里选择x86_64中docker-20.10.18.tgz版本下载即可
    https://download.docker.com/linux/static/stable/
    

    在这里插入图片描述

    在这里插入图片描述

  2. 下载离线安装工具

    #在github中下载离线安装工具
    https://github.com/Jrohy/docker-install/
    

    在这里插入图片描述

  3. 将Docker安装包和离线安装工具,上传至CentOS7中/soft目录下并解压

    #在/soft目录中解压离线安装工具
    unzip docker-install-master.zip
    
    #将docker-20.10.18.tgz移动至解压后的离线工具docker-install-master目录中
    mv docker-20.10.18.tgz docker-install-master
    
    #解压最后一定要保证在/soft/docker-install-master目录下包含以下三个文件
    docker-20.10.18.tgz
    install.sh
    docker.bash
    
  4. 卸载之前在线安装的Docker

    yum list installed | grep docker
    
    #注意这里的卸载命令和之前在线安装卸载旧版本的命令是不一样的,新版本的docker应该使用以下方式卸载
    yum -y remove docker-ce.x86_64 \
                  docker-ce-cli.x86_64 \
                  docker-ce-rootless-extras.x86_64 \
                  docker-scan-plugin.x86_64 \
                  containerd.io.x86_64     
    
  5. 执行安装

    #进入docker-install-master目录
    cd /soft/docker-install-master
    
    #为install.sh脚本添加执行权限
    chmod +x install.sh
    或
    chmod 655 install.sh
    
    #执行脚本进行安装,其中-f指定docker的安装包名字
    ./install.sh -f docker-20.10.18.tgz
    
    #执行完上述安装命令后出现以下信息,则证明脚本安装完成
    Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
    docker 20.10.18 install success!
    
  6. 查看Docker是否安装成功

    docker -v
    
  7. Docker系统服务相关命令

    #启动Docker服务
    systemctl start docker
    
    #停止Docker服务
    systemctl stop docker
    
    #重启Docker服务
    systemctl restart docker
    
    #设置开机启动
    systemctl enable docker
    
    #查看Dokcer运行状态
    systemctl status docker
    
    #查看Docker进程
    ps -ef | grep docker
    
    #查看Docker系统信息
    docker info
    
    #查看Docker所有帮助信息
    docker --help
    
    #查看某个commond命令的帮助信息
    docker commond --help
    

3. Dokcer核心组成

Docker使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。镜像与容器的关系类似于面向对象编程中的类与对象的关系:

Docker面向对象
镜像
容器对象

3.1 核心三要素

  • Docker 包括三个核心要素镜像(Image)容器(Container)仓库(Repository)
  • 理解了这三个概念,就理解了Docker的整个生命周期,Docker 的运行离不开以上核心几个组件的支持,Docker的成功也是拜这几个组件所赐,不能错误地理解为Docker就是容器,实际上Docker不是容器,而是管理容器的引擎

3.2 镜像概念

  • Docker镜像就是一个只读的模板,可以用来创建Docker容器,例如:一个镜像可以包含一个完整的Centos操作系统环境,里面仅安装了MySql或用户需要的其它应用程序。
  • Docker提供了一个非常简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。
  • 镜像就好比是我们Java中的类,类是对象的模板

3.3 容器概念

  • 容器是从镜像创建的运行实例,它可以被启动、停止、删除,每个容器都是相互隔离的、能够保证安全,可以把容器看做一个简易版的Linux环境,包括root用户权限、进程空间、用户空间和网络空间和运行在其中的应用程序。
  • Docker利用容器来运行应用,镜像是只读的,容器在启动的时候创建一层可写层作为最上层。
  • 容器就好比我们Java中的对象,我们可以通过类,来创建多个对象的实例

3.4 仓库概念

  • 仓库是集中存放镜像文件的场所,很多时候会把仓库(Repository)仓库注册服务器(Registry) 看做同一事物,并不严格区分。但实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)
  • 仓库分为**公开仓库(Public)私有仓库(Private)**两种形式。最大的公开仓库是 Docker Hubhttps://hub.docker.com/,存放了数量庞大的镜像供用户下载,当然,用户也可以在本地网络内创建一个私有仓库
  • 当用户创建了自己的镜像之后就可以将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上拉取下来即可
  • Docker 仓库的概念跟Git类似,注册服务器也类似于GitHub、Gitee这样的托管服务

4. Docker运行机制

我们知道Docker并不是容器,Docker只是一个管理容器的引擎,Docker的底层运行原理大致是:

  1. 安装并启动Docker服务
  2. 下载镜像(如果本地仓库已经有了该镜像,则跳过下载步骤)
  3. 启动该镜像得到一个容器
  4. 容器里运行着我们想要的程序

如下图所示:

在这里插入图片描述

英文名称中文名称描述
Docker daemonDocker守护进程Docker守护进程监听Docker命令请求,并管理Docker对象,如镜像、容器、网络和卷
ClientDocker客户端Docker客户端是用户与Docker交互的主要方式,当我们使用例如docker run这样的命令时,客户端会将这些命令发送给Docker守护进程来执行这些命令
RegistryDocker注册表Docker注册表是用于存储Docker镜像的地方,其中Docker Hub是Docker为我们提供的公共注册中心,默认情况下,Docker的镜像在Docker Hub中寻找并下载。我们也可以搭建自己的注册表。当使用docker pull或docker run命令时,将从配置的注册表中提取所需的镜像,使用docker push命令时,镜像会被推送到配置的注册表中
ImagesDocker镜像Docker镜像就是一个只读的模板,可以用来创建Docker容器,例如:一个镜像可以包含一个完整的 Centos 操作系统环境,里面仅安装了 MySql或用户需要的其它应用程序,Docker提供了一个非常简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用
ContainersDocker容器容器是从镜像创建的运行实例,它可以被启动、停止、删除,每个容器都是相互隔离的、能够保证安全,可以把容器看做一个简易版的 Linux环境,包括root用户权限、进程空间、用户空间和网络空间和运行在其中的应用程序,Docker利用容器来运行应用,镜像是只读的,容器在启动的时候创建一层可写层作为最上层

4.1 HelloWorld实现

  1. 将Docker服务启动

  2. 运行官方提供的HelloWorld容器

    docker run hello-world
    
  3. 出现以下内容则证明容器创建成功

    在这里插入图片描述

4.2 Docker究竟干了什么

在上述案例中,我们执行完docker run hello-world 就能得到一个测试容器,并打印输出对应的内容,那么这个API命令究竟干了什么呢?我们参考下面这个图来说明这个过程

在这里插入图片描述

5. Docker镜像实践

  • 镜像是容器运行容器的前提,是Docker的核心之一
  • 镜像的获取有两种方式,一个是下载别人在镜像仓库中提供好的镜像,一种是自己导入或自己制作镜像

5.1 配置阿里云镜像加速器

Docker运行一个容器前需要本地有对应的镜像,如果镜像不存在本地,Docker会从镜像仓库中下载

默认是从Docker Hub公共注册表服务器中下载:https://hub.docker.com

但是这个仓库的下载速度实在是太慢了,我们需要更改一下阿里云加速器来实现提速

#进入阿里云容器镜像服务页面,使用你的淘宝账号密码登录
https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

#在镜像加速器页面中,找到加速器地址
https://d2lhm394.mirror.aliyuncs.com 
或
https://registry.docker-cn.com (官方加速)

#在/etc/docker目录下找到在daemon.json文件(没有就新建),将下面内容写入
{
  "registry-mirrors": ["https://d2lhm394.mirror.aliyuncs.com"]
}

#重启daemon
systemctl daemon-reload

#重启docker服务
systemctl restart docker

5.2 搜索镜像

语法:docker search 镜像名

docker search tomcat

在这里插入图片描述

在列出信息中,可以看到几个字段信息:

名字描述
NAME镜像的名称
DESCRIPTION镜像的描述
STARTS点星数
OFFICI是否是官方镜像

也可以从docker hub官网搜索要使用的镜像:https://hub.docker.com,如下图:

在这里插入图片描述

5.3 下载镜像

语法:docker pull 镜像名

#默认是下载最新版本的镜像,也就是latest版本
docker pull tomcat

#以上命令等价于,其中:latest就是版本
docker pull tomcat:latest

在这里插入图片描述
在这里插入图片描述

5.4 镜像的分层

  • 在上图中,我们下载镜像的时候可以看出,镜像是由许多层(layer)的文件系统叠加构成的,例如:cf92e523b49e这样的字符串是层的唯一ID,使用docker pull命令下载中会获取并输出镜像的各层信息
  • 分层中最下面是一个引导文件系统bootfs,第二层是一个root文件系统rootfs,root文件系统通常是某种操作系统的运行环境。例如:CentOS、Ubuntu,在root文件系统之上又有很多层文件系统,这些文件系统叠加在一起,构成Docker中的镜像
  • 这样做的最大好处是共享资源,当不同的镜像包括相同的层时,本地仅存储了一份内容,减少了存储空间。例如:有多个镜像都从相同的base镜像构建而来,那么本地磁盘只需保存一份base镜像,同时内存中也只需加载一份base镜像,就可以为所有容器服务了

在这里插入图片描述

5.5 显示本地已有的镜像列表

语法:docker images or docker images 镜像名

#显示全部镜像
docker images

#按需显示指定名称的镜像
dokcer images tomcat

在列出信息中,可以看到几个字段信息:

名字描述
REPOSITORY来自于哪个仓库,比如tomcat
TAG镜像的标签,比如latest代表最新的版本
IMAGE ID镜像的ID号,ID号是唯一的
CREATED镜像的创建时间
SIZE镜像的大小

5.6 Tag标签

镜像的名称实际上由两部分组成:repository:tag,其中的 tag 一般用来表示版本,默认的 taglatest,表示最近的版本,如果我们不写版本,默认下载的就是latest最新版本,镜像的名称相当于在镜像上贴的一个标签,在一个镜像上可以贴多个标签,例如:tomcat:latesttomcat:10,等价于给镜像取了不同的名字,语法:docker tag 原repository:tag 新repository:tag

#给tomcat镜像另起一个名字
docker tag tomcat:latest tomcat:10

#查看镜像,可以看到同一个镜像(ID相同)可以有多个名称
docker images tomcat

在这里插入图片描述

5.7 查看镜像详情

我们可以查看镜像的详情信息,包括镜像名字、镜像ID、镜像的作者、镜像大小、镜像各层的数字ID等,语法:docker inspect 镜像名

docker inspect tomcat

在这里插入图片描述

5.8 导出镜像文件

我们可以把镜像导出为本地文件,然后在需要镜像的其他机器中导入此镜像即可,导出镜像的语法:docker save 镜像名 > 压缩包名

#将tomcat镜像导出为tomcat.tar.gz
docker save tomcat > tomcat.tar.gz

#导出多个镜像文件
docker save tomcat hello-world > images.tar.gz

5.9 删除镜像

在删除镜像的时候需要注意的是,只有当镜像只有唯一的一个名称时才会删除镜像,否则只是删除当前的名称,语法:docker rmi 镜像名

#删除名字叫tomcat:10的镜像,注意:这里其实只是把名字删除了
docker rmi tomcat:10

#删除tomcat:latest,因为是唯一的一个名字了,所以这里才会做最终的删除
docker rmi tomcat:latest

5.10 导入镜像文件

我们可以把本地导出好的镜像文件,导入到我们当前机器的docker中,语法:docker load < 压缩包名

docker load < tomcat.tar.gz

6.Docker容器实践

  • 启动容器有两种方式,一个是将处于停止状态的容器启动,一种是基于镜像新建一个容器并直接启动
  • 容器的命令格式大致是这样的形式:docker 容器命令 [参数选项][可选] 镜像名称 or 镜像id or 容器id
  • 由于容器是整个Dokcer的核心之一,所有有关于容器的操作命令可以支持很多参数选项,它们大致分为与容器运行模式相关与容器环境配置相关与容器资源限制和安全保护相关,常用的一些选项我们在命令讲解的过程中逐一说明
  • 具体每个容器命令可以有哪些参数选项,使用docker 容器命令 --help来进行查看

6.1 创建容器

语法:docker create [参数选项] 镜像名 or 镜像id,容器创建好之后,默认处于停止状态

#创建容器
docker create tomcat
或
dokcer create 543fd2c4284f

#创建容器,并给容器取上一个名字
dokcer create --name tomcat01 tomcat
或
dokcer create --name tomcat01 543fd2c4284f

执行以上命令后会看到标准输出里打印了一个字符串,这个字符串是容器的ID

在这里插入图片描述

需要注意的是:当我们创建一个容器的时候,容器一旦被创建完成分配好容器ID后,则会一直存在于我们的容器列表中,如果我们再次使用镜像创建一个容器,不是做覆盖操作,而会再次创建一个新的容器并分配新的容器ID,我们可以简单的理解从面向对象的角度来理解,每次我们创建容器,就是从类中创建了一个新的对象

6.2 查看容器

语法:dokcer ps [参数选项]

#查看当前正在运行的容器
docker ps

#查看当前所有容器,其中-a参数代表所有容器
docker ps -a

#查看当前所有容器,仅列出其容器id,其中-q参数代表只列出容器id
docker ps -aq

在这里插入图片描述

在列出信息中,可以看到几个字段信息:

名字描述
CONTAINER ID当前容器ID,唯一
IMAGE当前容器属于哪一个镜像的实例
COMMAND容器运行时所执行的命令
CREATED容器创建的时间
STATUS容器的运行状态
PORTS容器向外暴露的访问端口
NAMES容器的名字

6.3 启动容器

语法:dokcer start 容器id or 容器名

#启动容器时,这里的容器id可以只填写容器id的前几位,但最少要有3位
docker start 6d52
或
docker start tomcat01

#查看容器是否启动成功
ps -ef | grep tomcat

6.4 停止容器

语法:docker stop 容器id or 容器名

docker stop 6d52
或
docker stop tomcat01

6.5 删除容器

语法:docker rm [参数选项] 容器id or 容器名

#删除容器时,容器必须是停止状态,否则会报错
docker rm f2f9
或
docker rm tomcat01

#强制性删除容器
docker rm -f f2f9
或
dokcer rm -f tomcat01

6.6 通过镜像直接启动容器

Docker提供了一种直接通过镜像创建容器的时候同时启动当前容器的方式,语法:docker run [参数选项] 镜像名or镜像id ,它等同于前面我们所说的创建容器命令docker create [参数选项] 镜像名 or 镜像id和启动容器命令dokcer start 容器id or 容器名的结合,也是我们最常使用的一种方式

#通过tomcat镜像,后台启动一个tomcat容器,其中-d参数的意义是让容器启动时采用后台方式启动,默认容器启动时是采用前台启动,当我们自己挂断控制台窗口时就会停止容器
docker run -d tomcat
或
docker run -d 543fd2c4284f

#直接通过镜像启动容器时,为容器取上一个名字
docker run -d --name tomcat02 tomcat
或
docker run -d --name tomcat02 543fd2c4284f

6.7 重启容器

这个命令与启动容器命令不同的地方在于,启动命令只能启动正处于停止状态下的容器,而重启容器命令可以重启正在运行状态的容器,也能启动整处于停止状态下的容器,语法:docker restart 容器id or 容器名

#重启处于运行或停止状态的容器
docker restart 6d52
或
docker restart tomcat02

#通过镜像直接启动容器时指定容器默认自动重启,docker系统服务重启,或者操作系统重启,容器可以随系统自动启动
docker run -d --name tomcat03 --restart=always tomcat
或
docker run -d --name tomcat03 --restart=always 543fd2c4284f

6.8 进入容器

当容器进行-d后台启动的时候,用户无法看到容器中的信息,也无法进行操作,这个时候需要进入容器进行操作,官方有两个命令用于进入容器,attchexec,推荐使用exec,语法:docker exec [参数选项] 容器id or 容器名 需要执行的命令

#进入容器,运行指定命令,其中i表示交互式的,也就是保持标准输入流打开,t表示虚拟控制台,分配到一个虚拟控制台
docker exec -it 82c045 pwd
docker exec -it 82c045 lsdocker exec -it tomcat02 pwd
docker exec -it tomcat02 ls

#进入容器,并启动bash命令行,输入exit可以退出命令行
docker exec -it 82c045 bashdocker exec -it tomcat02 bash

6.9 查看容器日志

语法:docker logs [参数选项] 容器id or 容器名

#查看容器中的过往日志
docker logs 82c045
或
docker logs tomcat02

#查看容器中的实时日志,-f代表显示滚动日志
docker logs -f 82c045
或
docker logs -f tomcat02

6.10 复制文件

Docker支持在容器和宿主机之间复制文件,语法格式:docker cp 容器名 or 容器id:要拷贝的容器中的文件或目录 拷贝到的宿主机目录 或者 docker cp 要拷贝的宿主机文件或目录 容器名 or 容器id:拷贝到的容器中的目录

#将tomcat02容器中的server.xml配置文件拷贝到宿主机中的/root目录下
docker cp tomcat02:/usr/local/tomcat/conf/server.xml /root
或
docker cp 82c045:/usr/local/tomcat/conf/server.xml /root

#我们将拷贝下来的server.xml文件中的端口号修改为9090
vim server.xml

<Connector port="9090" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

#将宿主机中/root目录下的server.xml文件拷贝到tomcat02容器中
docker cp /root/server.xml tomcat02:/usr/local/tomcat/conf
或
docker cp /root/server.xml 82c045:/usr/local/tomcat/conf

#重新启动tomcat02容器,并查看日志情况
docker restart tomcat02
docker logs tomcat02

6.11 查看容器元信息

查看某容器的具体信息,会以 json 格式返回包括容器 ID、创建时间、路径、状态、镜像、配置等在内的各项信息,语法:docker inspect 容器id or 容器名

docker inspect 82c045
或
docker inspect tomcat02

6.12 容器的组合命令

在Docker中,我们可以对命令进行组合从而达到我们预期的效果,这里列出一些高频使用的组合命令

#停用全部运行中的容器
docker stop $(docker ps -q)

#删除全部容器
docker rm $(docker ps -aq)

#停用并删除所有容器
docker stop $(docker ps -q) & docker rm -f $(docker ps -aq)

6.13 容器端口映射

在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器的网络应用和服务的,当容器中运行一些网络应用,要让外部访问这些应用时,可以通过-P-p 参数来指定端口映射。当使用大写的-P时,Docker会随机映射一个49000~49900的端口到内部容器中,当使用小写的-p时,我们可以自定义映射端口到内部容器中,语法:docker run -d -P or -p [宿主机开放ip]宿主机端口号:容器映射端口号 镜像名 or 镜像id,这里我们以tomcat为例,来为大家进行说明,步骤如下:

  1. 启动tomcat容器,并指定端口映射:

    #启动一个tomcat,自动分配宿主机和容器的端口映射
    docker run -d -P tomcat
    或
    docker run -d -P 543fd2c4284f
    
    #启动一个tomcat,自定义宿主机和容器的端口映射
    docker run -d -p 8080:8080 tomcat
    或
    docker run -d -p 8080:8080 543fd2c4284f
    
    #启动一个tomcat,自定义宿主机和容器的端口映射,并限制ip地址为127.0.0.1的机器才可以访问
    docker run -d -p 127.0.0.1:8080:8080 tomcat
    或
    docker run -d -p 127.0.0.1:8080:8080 543fd2c4284f
    
  2. 查看端口映射关系

    #主要关注PORTS这一栏,这里指的是任意IP地址都可以访问宿主机的8080端口号来访问容器内部的tomcat的8080端口0.0.0.0:8080->8080/tcp
    docker ps 
    

    在这里插入图片描述

  3. 我们可以在windows的浏览器上输入测试,出现tomcat的首页则成功

    http://192.168.177.128:8080
    

    在这里插入图片描述

    有两个地方值得注意:

    3.1 如果输入浏览器地址时,发现浏览器一直处于转圈的一个加载状态,过一会出现404,说明是linux宿主机的防火墙未关闭

    #查看防火墙状态
    systemctl status firewalld 
    
    #关闭防火墙
    systemctl stop firewalld
    
    #让防火墙禁用
    systemctl disable firewalld
    

    3.2 如果出现页面,但页面显示的是tomcat404,则说明是tomcat的首页没有正确配置,我们可以进入docker容器配置tomcat首页

    在这里插入图片描述

    #进入容器
    docker exec -it 1d812 bash
    
    #查看tomcat在容器中的哪个目录
    whereis tomcat
    
    #进入tomcat目录
    cd /usr/local/tomcat
    
    #进入webapps.dist中的ROOT文件夹拷贝到webapps下,刷新页面即可
    cp -r webapps.dist/ROOT webapps
    

7. Docker数据管理

在使用Docker时,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及容器的数据管理操作,容器中的管理数据主要有两种方式:目录挂载数据卷挂载

7.1 目录挂载

目录挂载就是我们将宿主机的真实文件路径和容器中的路径做一个映射,当宿主机的数据发生变化会同步到容器中,反之,当容器中的数据发生变化,也会直接同步到宿主机的挂载目录中。在Docker中,在启动容器时,我们可以通过配置-v 参数选项来启用挂载宿主机目录功能,语法:docker run -v 宿主机目录:容器目录 镜像名 or 镜像id

我们这里以Tomcat为例:

  1. 在宿主机中创建mydata/tomcat目录

    mkdir -p /mydata/tomcat
    
  2. 启动一个tomcat容器,将宿主机的/mydata/tomcat目录挂载到容器的/usr/local/tomcat/webapps/ROOT

    docker run -d -p 8080:8080 -v /mydata/tomcat/:/usr/local/tomcat/webapps/ROOT tomcat
    
  3. 在/mydata/tomcat目录下建立test.html

    vim /mydata/tomcat/test.html
    

    填入以下内容:

    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
      	<p>测试目录挂载</p>
      </body>
    </html>
    
  4. 用浏览器访问测试

    http://192.168.177.128:8080/test.html
    

    在这里插入图片描述

  5. 修改/mydata/tomcat/test.html的内容

    vim /mydata/tomcat/test.html
    
    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
      	<p>测试目录挂载修改</p>
      </body>
    </html>
    
  6. 再次用浏览器访问观察变化

    http://192.168.177.128:8080/test.html
    

    在这里插入图片描述

  7. 我们也可以进入容器观察挂载情况

    #进入容器
    docker exec -it d55b27 bash
    
    #切换到tomcat的ROOT目录下
    cd /usr/local/tomcat/webapps/ROOT
    

    在这里插入图片描述

7.2 数据卷挂载

数据卷是一个可供一个或多个容器使用的特殊目录,它将宿主机目录直接映射进容器,在容器之间共享和重用,默认会一直存在,即使容器被删除。数据卷目录挂载的区别在于数据卷是受控存储,是由Docker引擎进行管理维护的。因此使用数据卷,你可以不必处理 uid、SELinux 等各种权限问题,Docker引擎在建立卷时会自动添加安全规则,以及根据挂载点调整权限。并且可以统一列表、添加、删除。而目录挂载那就没人管了,属于用户自行维护,就必须手动处理所有权限问题。特别是在CentOS 上,很多时候碰到 Permission Denied(权限被拒绝),就是因为没有使用数据卷,而是目录挂载,Docker提供了 volume子命令来管理数据卷

我们这里以Tomcat为例:

  1. 新建数据卷

    docker volume create tomcat-vol
    
    #创建完毕后,查看/var/lib/docker/volumes路径下,会发现所创建的数据卷位置
    cd /var/lib/docker/volumes
    

    在这里插入图片描述

  2. 查看数据卷

    #列出所有数据卷
    docker volume ls
    
    #查看指定数据卷的详情
    docker inspect tomcat-vol
    
  3. 启动Tomcat容器,并挂载数据卷

    #--mount选项用于进行数据卷的挂载,其中source是指定数据卷的名字,target指定容器的挂载目录
    docker run -d -p 8080:8080 --mount source=tomcat-vol,target=/usr/local/tomcat/webapps/ROOT tomcat
    
    #以上命令也可以采用目录挂载的方式进行简写
    docker run -d -p 8080:8080 -v tomcat-vol:/usr/local/tomcat/webapps/ROOT tomcat
    
  4. 在数据卷目录中添加test.html

    #切换到数据卷目录
    cd /var/lib/docker/volumes/tomcat-vol/_data
    
    #添加test.html
    vim test.html
    

    添加内容如下:

    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
      	<p>测试数据卷挂载</p>
      </body>
    </html>
    
  5. 用浏览器访问测试

    http://192.168.177.128:8080/test.html
    

    在这里插入图片描述

  6. 删除数据卷

    docker volume rm tomcat-vol
    

    删除数据卷时会出现以下异常:原因是正在被容器使用的数据卷是无法删除的!

    在这里插入图片描述

    我们可以先删除容器,再尝试删除数据卷

    #查询容器列表
    docker ps
    
    #删除容器
    docker rm -f 3b6c856e1ae7
    
    #删除数据卷
    docker volume rm tomcat-vol
    
  7. 清理无用数据卷

    #没有被挂载的数据卷执行此命令会被删除掉
    docker volume prune
    

7.3 Commit命令

Docker提供了一个比较特殊的命令commit,该命令可以将设置好的容器数据状态保存为一个新的镜像,语法:docker commit 镜像id or 镜像名 新镜像名

我们以仍然Tomcat为例:

  1. 启动一个新的Tomcat容器

    docker run -d -p 8080:8080 tomcat
    
  2. 进入容器,在/usr/local/tomcat/webapps下创建ROOT目录

    #进入容器
    docker exec -it a48c66 bash 
    
    #进入webapps目录
    cd /usr/local/tomcat/webapps
    
    #创建ROOT目录
    mkdir ROOT
    
    #退出容器
    exit
    
  3. 将宿主机的/mydata/tomcat/test.html文件复制容器中

    docker cp /mydata/tomcat/test.html a48c66:/usr/local/tomcat/webapps/ROOT/test.html
    
  4. 访问如下地址看到页面

    http://192.168.177.128:8080/test.html
    

    在这里插入图片描述

  5. 使用commit保存修改后的容器作为新的镜像

    docker commit a48c66 my_tomcat
    或
    docker commit a48c66 my_tomcat:latest
    
  6. 从新镜像中启动容器

    #先停止并删除掉之前的容器
    docker rm -f a48c66
    
    #从保存的新镜像中启动容器
    docker run -d -p 8080:8080 my_tomcat
    
  7. 再次访问页面,之前commit保存的新镜像已经保留了我们之前复制进去的页面

    http://192.168.177.128:8080/test.html
    

    在这里插入图片描述

    如果我们希望把一个运行着的容器,由于里面改了一些数据或配置,并希望把该容器保存起来,可以使用上面这种做法,得到一个新镜像,下次用该新镜像运行新的容器,这是一种数据持久化的特殊做法,也是一种自定义一个镜像的方式之一

8. 本地镜像上传仓库

  • 我们知道仓库是集中存放镜像的地方,仓库又分公共仓库私有仓库
  • 最大的公开仓库是 Docker Hub国内第三方仓库,例如:腾讯云、华为云、阿里云等
  • 私有仓库又分为本地私有仓库国内第三方仓库,例如:腾讯云、华为云、阿里云等
  • 有时候容易把仓库注册服务器混淆实际上注册服务器是存放仓库的具体服务器, 一个注册服务器上可以有多个仓库,而每个仓库下面可以有多个镜像,仓库可以被认为是一个具体的项目或目录,例如对于仓库地址 private-docker.com/ubuntu 来说, private-docker.com 是注册服务器地址, ubuntu是仓库名

8.1 本地镜像上传Docker Hub

我们以之前自己commit保存的my_tomcat镜像为例,上传本地的镜像到官方提供的Docker Hub中

  1. 注册

    #在如下网站注册下得到docker id和密码
    https://hub.docker.com/ 
    

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  2. 在本地虚拟机登陆hub服务器

    #输入如下命令,并根据提示输入docker id和密码即可
    docker login
    

    在这里插入图片描述

  3. 修改镜像名称,在推送之前,你要确保镜像不能随意取名,需要遵循docker hub的命名规则

    #其中docker.io是注册服务器地址,gudashi是我们的dokcer id,my_tomcat是我们的仓库名
    docker tag my_tomcat docker.io/gudashi/my_tomcat
    
    #因为默认使用的注册服务器就是dokcer.io所以这里也可以省略不写
    docker tag my_tomcat gudashi/my_tomcat
    
  4. 推送镜像

    #推送过程可能因为网络原因会有失败的情况,可以反复尝试一下
    docker push docker.io/gudashi/my_tomcat
    或
    docker push gudashi/my_tomcat
    
  5. 验证是否成功

    登陆如下网站,点击Repositories菜单,此时已经出现了我们的镜像
    https://hub.docker.com/
    

    在这里插入图片描述

  6. 下载镜像

    #删掉之前本地的镜像
    docker rmi gudashi/my_tomcat
    或
    docker rmi docker.io/gudashi/my_tomcat
    
    #执行下载
    docker pull docker.io/gudashi/my_tomcat
    或
    docker pull gudashi/my_tomcat
    

8.2 本地镜像上传私有仓库

私有仓库其本质就是我们启动一个docker的镜像仓库容器,通过这个容器服务来帮助我们管理镜像,我们以之前自己commit保存的my_tomcat镜像为例,上传本地的私有仓库中

  1. 下载registry镜像

    docker pull registry
    
  2. 启动镜像

    #启动容器,-d后台启动 -p端口映射(端口号默认为5000) -v目录挂载
    docker run -d -p 5000:5000 -v /mydata/registry:/var/lib/registry registry
    
  3. 查看镜像

    #在浏览器访问如下地址,此时仓库里是空的
    http://192.168.177.128:5000/v2/_catalog
    

    在这里插入图片描述

  4. 修改镜像名称,在推送之前,仍然要确保镜像不能随意取名,需要遵循命名规则

    #其中127.0.0.1:5000是注册服务器地址,这里设置成我们本机地址,gucaini是我们的dokcer id,my_tomcat是我们的仓库名
    docker tag my_tomcat 127.0.0.1:5000/gucaini/my_tomcat
    
  5. 推送镜像

    docker push 127.0.0.1:5000/gucaini/my_tomcat
    
  6. 再次查看镜像

    #在浏览器访问如下地址,此时仓库里已经有了我们推送上去的镜像
    http://192.168.177.128:5000/v2/_catalog
    

    在这里插入图片描述

    我们也可以在挂载的目录中找到我们的镜像文件

    cd /mydata/registry/docker/registry/v2/repositories
    

    在这里插入图片描述

  7. 下载镜像

    #修改/etc/docker/daemon.json,让docker信任私有仓库地址
    vim /etc/docker/daemon.json
    {
      "registry-mirrors": ["https://d2lhm394.mirror.aliyuncs.com"],
      "insecure-registries": ["192.168.177.128:5000"]
    }
    
    #然后重启docker
    systemctl restart docker
    
    #重启registry容器
    docker start e2124c23ba8a
    
    #删掉之前本地的镜像
    docker rmi 127.0.0.1:5000/gucaini/my_tomcat
    
    #执行下载
    docker pull 192.168.177.128:5000/gucaini/my_tomcat
    

8.3 本地镜像上传阿里云

国内很多厂商提供了第三方仓库可以帮助我们统一管理我们的镜像,这里我们以阿里云为例,用我们以之前自己commit保存的my_tomcat镜像为例,上传本地的镜像到阿里云中

  1. 进入阿里云容器镜像服务

    https://cr.console.aliyun.com/cn-hangzhou/instances
    

    在这里插入图片描述

  2. 创建个人实例

    在实例列表中,选择个人实例,进行创建

    在这里插入图片描述

  3. 设置Registry登录密码

    在这里插入图片描述

    在这里插入图片描述

  4. 创建命名空间

    在个人实例的仓库管中找到命名空间进行创建,命名空间类似于我们Java中的包,其主要目的是区分不同的环境,统一管理,推荐使用公司、组织、或者个人用户来命名,例如:aliyun、gucaini,不推荐使用对应的一个模块或系统,例如:tomcat、mysql

    在这里插入图片描述

  5. 设置命名空间仓库的类型

    命名空间创建完毕之后,可以选择在该命名空间下创建的仓库类型,默认为私有

    在这里插入图片描述

  6. 创建镜像仓库

    选择镜像仓库进行仓库创建,填写仓库基本信息,在代码源中选择本地仓库,然后执行创建

    在这里插入图片描述

    在这里插入图片描述

  7. 完成创建

    完成仓库创建后,会出现下图所示界面,该界面有具体的操作指南,我们根据指南就可进行本地镜像推送到阿里云私有镜像仓库中

    在这里插入图片描述

  8. 在本地虚拟机登陆阿里云服务器

    #输入如下命令进行登录,--username为你自己的阿里云账号,提示输入密码时,是你自己开通服务时所设置的密码
    docker login --username=skyzdq123@163.com registry.cn-hangzhou.aliyuncs.com
    

    在这里插入图片描述

  9. 修改镜像名称,在推送之前,你要确保镜像不能随意取名,需要遵循阿里云的命名规则

    #其中registry.cn-hangzhou.aliyuncs.com是注册服务器地址,gucaini是我们的命名空间,tomcat是我们的仓库名
    docker tag my_tomcat registry.cn-hangzhou.aliyuncs.com/gucaini/tomcat
    
  10. 推送镜像

    docker push registry.cn-hangzhou.aliyuncs.com/gucaini/tomcat
    
  11. 查看镜像

    推送完毕后,可在镜像版本中查看我们所推送的镜像,如下图

    在这里插入图片描述

  12. 下载镜像

    #先删除本地镜像
    docker rmi registry.cn-hangzhou.aliyuncs.com/gucaini/tomcat
    
    #下载镜像
    docker pull registry.cn-hangzhou.aliyuncs.com/gucaini/tomcat
    

9. 常用软件基础安装

Docker可以安装几乎比较全面的主流软件,我们一一例举显然是不太现实的,这里我们以4个常用软件作为基础软件安装教学,它们分别是:MySQL、Redis、RabbitMQ、ElasticSearch

9.1 MySQL基础安装

MySQL 是全球最流行的开源关系型数据库之一,由于其具有高性能、成熟可靠、高适应性 易用性而得到广泛应用

  1. 下载MySQL镜像

    #我们可以在Docker Hub中找到MySQL镜像
    https://hub.docker.com/_/mysql
    
    #拉取MySQL镜像,默认拉取的版本是latest的,这里下载的是8.0版本的MySQL镜像
    docker pull mysql
    
    #也可以指定版本拉取,这里下载的是5.7版本的MySQL镜像
    docker pull mysql:5.7.40
    

    在这里插入图片描述

  2. 快速启动一个MySQL容器

    #其中-d代表后台启动,-p代表端口映射,--name是容器的名称,-e是指定容器启动时所需要配置的环境变量,例如:MYSQL_ROOT_PASSWORD就是指定数据库的密码
    docker run -d -p 3306:3306 --name my_mysql -e MYSQL_ROOT_PASSWORD=123456 mysql
    
    #查看容器是否启动
    docker ps
    
    #查看容器启动时的日志
    docker logs b87dd
    
  3. 进入容器,并登陆MySQL客户端

    docker exec -it b87dd bash
    
    #进入容器后输入MySQL客户端连接命令,其中-u是数据库用户名,默认为root,-p是数据库密码
    mysql -uroot -p123456
    

    在这里插入图片描述

  4. 进行数据库操作

    #查看所有的库
    show databases;
    
    #创建一个数据库
    create database docker;
    
    #切换数据库
    use docker;
    
    #查看所有表
    show tables;
    
    #创建一张表
    create table t_user(
        id int primary key,
    	user_name varchar(20),
        passsword varchar(50)
    );
    
    #插入数据
    insert into t_user values(1,'Tom','123456');
    
    #查询表
    select * from t_user;
    
  5. 授权远程登录访问:

    #修改远程用户访问密码,其中root@'%'中的root表示用户名,%表示任意ip地址
    alter user 'root'@'%' identified with mysql_native_password by '123456';
    
    #授权访问,其中*.* 的第一个*表示所有数据库名,第二个*表示所有的数据库表
    grant all privileges on *.* to 'root'@'%';
    
    #刷新权限
    flush privileges;
    
  6. 使用SQLyog远程连接MySQL

    主机地址:192.168.177.128
    用户名:root
    密码:123456
    端口:3306
    

    在这里插入图片描述

    在这里插入图片描述

  7. 删除容器,并重新启动一个MySQL容器,观察现象

    #删除容器
    docker rm -f b87dd
    
    #启动一个新容器
    docker run -d -p 3306:3306 --name my_mysql -e MYSQL_ROOT_PASSWORD=123456 mysql
    
    #查看新容器的进程
    docker ps
    
    #进入新容器
    docker exec -it 486042 bash
    
    #连接MySQL客户端
    mysql -uroot -p123456
    
    #查看数据库
    show databases;
    
  8. 再次尝试用SQLyog进行连接,观察现象

    在这里插入图片描述

    结论:启动新容器后,原有MySQL容器的数据和配置就失效了,容器是瞬态的,所以应当给MySQL的配置数据做持久化。除此之外,如果希望自定义MySQL的配置,例如:指定字符集,配置慢查询日志等功能,也应该将其核心配置文件my.cnf做持久化保存。MySQL的系统日志最好也做持久化保存

  9. 使用数据持久化方式创建MySQL容器

    我们知道,Docker在进行数据管理的时候,支持两种方式,一种是目录挂载,一种是数据卷挂载。本案例中,我们采用目录挂载方式做演示,目录挂载方式需要注意特别是在CentOS 上,很多时候碰到 Permission Denied(权限被拒绝),就是因为没有使用数据卷,而是目录挂载,例如:MySQL中使用目录挂载方式,就会出现chown: changing ownership of '/var/lib/mysql/': Permission denied 这样的错误,所以此处我们需要自行处理掉这个问题,或者干脆使用数据卷的方式

    #启动MySQL容器
    docker run --name=my_mysql --privileged=true --restart=always \
    -v /mydata/mysql/data:/var/lib/mysql \
    -v /mydata/mysql/conf:/etc/mysql/conf.d \
    -v /mydata/mysql/logs:/var/log/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -d -p 3306:3306 mysql
    
    #以上命令解析
    --name:容器的名称
    
    --privileged=true:为了防止出现Centos7安全Selinux禁止了一些安全权限
    
    --restart=always:开机自动重启MySQL容器
    
    -v:目录挂载
    -v /mydata/mysql/data:/var/lib/mysql
    将宿主机的/mydata/mysql/data目录挂载到容器中的/var/lib/mysql目录中(这是MySQL容器默认存储数据文件的目录)
    -v /mydata/mysql/conf:/etc/mysql/conf.d
    将宿主机的/mydata/mysql/conf目录挂载到容器中的/etc/mysql/conf.d目录(这是MySQL容器默认的配置文件目录)
    -v /mydata/mysql/logs:/var/log/mysql 
    将宿主机的/mydata/mysql/conf目录挂载到容器中的/etc/mysql/conf.d目录(这是MySQL容器默认的配置文件目录)
    
    -e:指定环境变量,其中MYSQL_ROOT_PASSWORD指定数据库的密码
    
    -d:后台启动
    
    -p:端口映射
    
  10. 添加MySQL自定义配置

    #在宿主机中切换到/mydata/mysql/conf目录下
    cd /mydata/mysql/conf
    
    #添加my.cnf数据库核心配置文件
    vim my.cnf
    
    #添加以下内容
    [client]
    default-character-set=utf8mb4
    
    [mysqld]
    character-set-server=utf8mb4
    autocommit=1
    
    #[clinet]代表配置MySQL客户端连接时的相关配置
    default-character-set:设置MySQL客户端连接时的字符集
    
    #[mysqld]代表MySQL服务端的相关配置
    character-set-server:设置服务端的字符集
    autocommit:开启自动提交
    
    #配置完毕后重启容器
    docker restart my_mysql
    
  11. 进入容器,重新尝试之前的数据操作,远程授权,并删掉容器,重新以目录挂载方式启动,观察现象

    在这里插入图片描述
    在这里插入图片描述

    结论:启动新容器后,原有MySQL容器的数据由于做了目录挂载进行了持久化,数据被保留了。由于自定义了MySQL的配置,现在插入数据也支持中文插入。远程授权访问的设置也被完整的保留了下来

9.2 Redis基础安装

Redis是一个基于内存的数据结构存储服务器,常被用作数据库、缓存、消息队列,是目前最主流的Nosql数据库之一

  1. 下载Redis镜像

    #我们可以在Docker Hub中找到Redis镜像
    https://hub.docker.com/_/redis
    
    #拉取Redis镜像,默认拉取的版本是latest的,注意官方latest版本默认是7.0版本,但由于我们配置了阿里云镜像加速器,默认下载下来的版本还是6.0的版本
    docker pull redis
    
    #也可以指定版本拉取,这里下载的是7.0版本的Redis镜像
    docker pull redis:7.0.5
    

    在这里插入图片描述

  2. 快速启动一个Redis容器

    docker run -d -p 6379:6379 --name my_redis redis:7.0.5
    
  3. 进入Redis客户端工具,进行测试

    #进入容器
    docker exec -it my_redis bash
    
    #查看Redis的版本
    redis-server -v
    
    #进入Redis命令窗口
    redis-cli
    
    #输入以下命令
    set test 1
    get test
    

    在这里插入图片描述

  4. 创建用于持久化Redis数据的数据卷

    显然快速启动Redis的方式,并不是我们想要的方式,在前面MySQL中,我们使用了目录挂载方式来进行数据持久化。Redis因本身是一个内存结构的数据库,但如果容器关闭或宿主机重启了,就会丢失内存数据,但其内部也做了数据持久化方案,通常是使用RDBAOF的方式进行持久化,为了保证数据不丢失,也应当进行持久化工作。同时Redis本身支持很多自定义配置,这些配置都是记录在其核心配置文件redis.conf中,所以此文件,我们也应当进行持久化。本例中我们采用数据卷的方式来进行持久化

    #新建两个数据卷,一个用于存储redis配置文件数据,一个用于存储redis的持久化数据
    docker volume create redis-config
    docker volume create redis-data
    
    #查看数据卷详情
    docker inspect redis-config
    docker inspect redis-data
    
    #切换到/var/lib/docker/volumes/redis-config/_data目录中,放入我们准备好的redis.conf文件
    cd /var/lib/docker/volumes/redis-config/_data
    
    #编辑redis.conf配置文件,修改如下配置
    protected-mode no (改为不保护,否则远程访问不了)
    bind 127.0.0.1 (注释掉,否则只能本机ip访问)
    requirepass 设置密码
    

    一个比较大的坑:在redis.conf核心配置文件中有一个配置daemonize no,此配置的含义是Redis在运行的时候是否进行后台启动,在不使用Docker的情况下,原生安装Redis软件我们通常会把此配置改为daemonize yes,使Redis以后台方式进行启动,但在使用Docker的情况下,此配置修改之后就会导致Redis容器无法正常启动,因为Docker在启动容器时本身有一个选项-d就支持后台启动,这就会与该配置产生冲突。

  5. 使用数据持久化方式创建Redis容器

    docker run --name my_redis --restart=always \
    -v redis-data:/data \
    -v redis-config:/usr/local/etc/redis \
    -d -p 6379:6379 \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #以上命令解析
    --name:容器的名称
    
    --restart=always:开机自动重启Redis容器
    
    -v:目录挂载
    -v redis-data:/data
    将宿主机的数据卷redis-data挂载到容器中的/data目录中(这是Redis容器默认存储数据文件的目录)
    -v redis-config:/usr/local/etc/redis \
    将宿主机的数据卷redis-config目录挂载到容器中的/usr/local/etc/redis目录(这是官方推荐我们挂载配置文件的目录)
    
    -d:后台启动
    
    -p:端口映射
    
    redis-server /usr/local/etc/redis/redis.conf: 在Redis容器启动的时候执行一个命令,该命令指定了我们自定义的配置文件来启动Redis
    
  6. 进入容器,效验我们自定义的配置是否生效

    #进入容器
    docker exec -it my_redis bash
    
    #登录redis命令客户端
    redis-cli
    
    #输入密码
    auth 123456
    

    在这里插入图片描述

  7. 使用可视化工具连接Redis

    Another Redis Desktop Manager是国内开源免费的一个用于可视化操作Redis的热门桌面工具,GitHub地址:https://github.com/qishibo/AnotherRedisDesktopManager,如能正常连接,说明Redis允许远程访问的配置也生效了

    在这里插入图片描述

  8. 测试Redis持久化

    Redis默认开启了RDB持久化方式,我们往Redis里写入数据,然后在客户端中执行save命令进行内存数据强制性保存,观察数据卷目录的持久化情况。之后删掉容器,重新启动一个新的Redis容器,观察数据是否恢复

    #进入容器
    docker exec -it my_redis bash
    
    #登录redis命令客户端
    redis-cli
    
    #输入密码
    auth 123456
    
    #输入命令
    set test 1
    get test
    save
    
    #删掉容器
    docker rm -f my_redis
    
    #重新创建新的容器
    docker run --name my_redis --restart=always \
    -v redis-data:/data \
    -v redis-config:/usr/local/etc/redis \
    -d -p 6379:6379 \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    

    在这里插入图片描述

    在这里插入图片描述

  9. 开启RDB备份和AOF备份

    • Redis提供两种机制对数据进行持久化,便于恢复数据。一种是RDB方式、另一种是AOF方式

    • RDB是默认开启的,是指在指定的时间间隔内将内存中的数据集快照写入磁盘,数据恢复时将快照文件直接再读到内存

    • AOF是指Redis每次接收到一条改变数据的命令时,它将把该命令写到一个AOF文件中(只记录写操作,不记录读操作),当Redis重启时,它通过执行AOF文件中所有的命令来恢复数据

    • 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效,RDB的缺点是最后一次持久化后的数据可能丢失

    • AOF这种方式的持久化让Redis的数据不会丢失,AOF需要手动开启,AOF可以将Redis执行的每一条写命令追加到硬盘文件中,这一过程会降低Redis的性能,但大部分情况下这个影响是能够接受的

    • RDB和AOF两种持久化方式是可以共存的

    #编辑redis.conf文件
    cd /var/lib/docker/volumes/redis-config/_data
    vim redis.conf
    
    #配置文件搜索SNAPSHOTTING部分,设置RDB的自动备份方式,配置格式:save <seconds> <changes>
    save 900 1
    save 300 10
    save 60 10000
    
    #配置文件搜索APPEND ONLY MODE部分,设置AOF的备份,AOF的相关配置选项如下
    appendonly  默认是no,改成yes即开启了aof持久化
    appendfilename 指定AOF文件名,默认文件名为appendonly.aof
    appendfsync 配置向aof文件写命令数据的策略:
    no:不主动进行同步操作,而是完全交由操作系统来做(即每30秒一次),比较快但不是很安全
    always:每次执行写入都会执行同步,慢一些但是比较安全
    everysec:每秒执行一次同步操作,比较平衡,介于速度和安全之间
    
    #配置完毕之后,重启Redis容器
    docker restart my_redis
    
  10. 测试RDB自动备份和AOF备份

    #进入容器
    docker exec -it my_redis bash
    
    #登录redis命令客户端
    redis-cli
    
    #输入密码
    auth 123456
    
    #输入命令
    set test1 2
    
    #在宿主机查看数据卷目录
    cd /var/lib/docker/volumes/redis-data/_data
    

    在这里插入图片描述

9.3 RabbitMQ基础安装

RabbitMQ是部署最广泛的开源消息中间件之一,RabbitMQ在全球的小型初创企业和大型企业中都有使用。

  1. 下载RabbitMQ镜像

    #我们可以在Docker Hub中找到RabbitMQ镜像
    https://hub.docker.com/_/rabbitmq
    
    #拉取Rabbit镜像,默认拉取的版本是latest的,默认是3.11的版本,但此版本不包含RabbitMQ的可视化管理端工具
    docker pull rabbitmq
    
    #拉取Rabbit镜像,且包含RabbitMQ的可视化管理端工具
    docker pull rabbitmq:management
    

    在这里插入图片描述

  2. 以持久化的方式启动RabbitMQ容器

    前面两个案例中,我们都做了持久化启动的详细讲解,本例中,我们就直接以目录挂载持久化方式进行容器的启动

    docker run --name my_rabbitmq --restart=always \
    --hostname=rabbitmq \
    -v /mydata/rabbitmq:/var/lib/rabbitmq \
    -p 5672:5672 \
    -p 15672:15672 \
    -d rabbitmq:management
    
    #以上命令解析
    --name:容器的名称
    
    --restart=always:开机自动重启RabbitMQ容器
    
    --hostname=rabbitmq:这个配置是需要特别注意的,在官方文档中,告诉我们,RabbitMQ它根据所谓的“节点名”来存储数据,默认为主机名。这意味着在Docker中,我们应该为每个守护进程显式地指定 ——hostname,这样我们就不会得到一个随机的主机名。如果不指定,我们做RaabitMQ的数据持久化将不会生效
    
    -v:目录挂载
    -v /mydata/rabbitmq:/var/lib/rabbitmq:目录挂载,将宿主机的/mydata/rabbitmq目录挂载到容器中的/var/lib/rabbitmq目录中(这是rabbitmq容器默认存储数据文件的目录)
    
    -p:端口映射
    -p 5672:5672:这是rabbitMQ服务进程的默认端口号
    -p 15672:15672:这是rabbitMQ可视化管理端工具的默认端口号
    
    -d:后台启动
    
  3. 访问RabbitMQ可视化管理端

    我们可以在Windows浏览器中,输入如下地址来访问RabbitMQ可视化管理端

    http://192.168.177.128:15672
    

    在这里插入图片描述

  4. 登录可视化管理端

    成功访问可视化管理端口后,默认的登录名和密码都是guest,如果想要修改默认用户名和密码,可以通过启动容器时添加RABBITMQ_DEFAULT_USERRABBITMQ_DEFAULT_PASS环境变量来实现。

    docker run --name my_rabbitmq --restart=always \
    --hostname=rabbitmq \
    -v /mydata/rabbitmq:/var/lib/rabbitmq \
    -p 5672:5672 \
    -p 15672:15672 \
    -e RABBITMQ_DEFAULT_USER=admin \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    -d rabbitmq:management
    

    在这里插入图片描述

  5. 添加一个交换机测试下

    在这里插入图片描述

  6. 测试持久化

    我们删掉正在运行的RabbitMQ容器,重新启动一个新的容器。启动成功后再次访问客户端工具,看一下之前创建的交换机是否正确被持久化。同时我们也可以观察宿主机目录/mydata/rabbitmq的情况

    #删掉之前的rabbitMQ容器
    docker rm -f my_rabbitmq
    
    #启动新的容器
    docker run --name my_rabbitmq --restart=always \
    --hostname=rabbitmq \
    -v /mydata/rabbitmq:/var/lib/rabbitmq \
    -p 5672:5672 \
    -p 15672:15672 \
    -d rabbitmq:management
    
    #查看宿主机挂载目录
    cd /mydata/rabbitmq
    

9.4 ElasticSearch基础安装

ElasticSearch是一个基于Apache Lucene作为其核心来实现目前最为主流的搜索引擎之一,它通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

  1. 下载ElasticSearch镜像

    ElasticSearch的更新频率是特别快的,有可能几个小时就会有更新,所以官方建议在下载镜像时,最好要加上tag标签指定版本,目前ElasticSearch的最新版本是8.0+,但实际公司中用的比较多的版本是6.0+和7.0+的版本,我们这里以7.0+版本为例

    #我们可以在Docker Hub中找到ElasticSearch镜像
    https://hub.docker.com/_/elasticsearch
    
    #拉取ElasticSearch镜像,默认拉取的版本是latest的,官方强烈不推荐这么干
    docker pull elasticsearch
    
    #拉取ElasticSearch镜像,指定版本为7.17.6
    docker pull elasticsearch:7.17.6
    

    在这里插入图片描述

  2. 准备持久化目录

    本例中,我们仍然以目录挂载持久化方式进行容器的启动,不同的是,在启动前,我们需要自己去创建一下挂载目录,这样做的目的是为了手动更改一下目录的访问权限,在ES的官方中提到,如果我们在Docker中尝试以挂载目录的方式来持久化数据,那么需要对持久化目录的访问权限进行授权,因为Docker中的ES默认是以elasticsearch用户身份运行的,如果宿主机目录是以root用户身份创建的,那么就会导致权限不足,从而启动失败

    #在宿主机中创建持久化目录
    mkdir -p /mydata/es/plugins
    mkdir -p /mydata/es/data
    
    #目录解释
    plugins:用于存储es的插件
    data:用于存储es的持久化数据
    
    #更改文件权限
    chmod -R 777 /mydata/es
    
  3. 以持久化方式启动ElasticSearch容器

    本例中,我们就直接以目录挂载持久化方式进行容器的启动

    docker run --name my_es --restart=always \
    -e "discovery.type=single-node" \
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
    -v /mydata/es/plugins:/usr/share/elasticsearch/plugins \
    -v /mydata/es/data:/usr/share/elasticsearch/data \
    -p 9200:9200 \
    -p 9300:9300 \
    -d elasticsearch:7.17.6
    
    #以上命令解析
    --name:容器的名称
    
    --restart=always:开机自动重启ES容器
    
    -e:指定环境变量
    -e "discovery.type=single-node" :es在启动的时候默认是需要以集群的方式来运行的,如果我们期望以单机版的方式来启动es,则必须加上此环境变量,告诉容器我们运行的模式是单节点模式
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m":es容器默认设置的JVM内存大小是"-Xms512m -Xmx512m",我们可以指定环境变量来调整JVM的内存大小
    
    -v:挂载目录
    -v /mydata/es/plugins:/usr/share/elasticsearch/plugins:目录挂载,将宿主机的/mydata/es/plugins目录挂载到容器中的/usr/share/elasticsearch/plugins目录中(这是es容器默认存储插件的目录)
    -v /mydata/es/data:/usr/share/elasticsearch/data:目录挂载,将宿主机的/mydata/es/data目录挂载到容器中的/usr/share/elasticsearch/data目录中(这是es容器默认存储数据的目录)
    
    -p:端口映射
    -p 9200:9200:9200端口是http协议的,用于操作访问es的数据端口
    -p 9300:9300:9300端口是tcp通讯端口,用于ES集群节点之间通讯使用端口
    
    -d:后台启动
    
  4. 查看ElasticSearch版本信息

    在我们的Windows浏览器中访问如下地址,出现版本信息则说明启动成功

    http://192.168.177.128:9200 
    

    在这里插入图片描述

  5. 下载Kibana镜像

    Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的。你可以用Kibana搜索、查看存放在Elasticsearch中的数据。Kibana与Elasticsearch的交互方式是各种不同的图表、表格、地图等,直观的展示数据,从而达到高级的数据分析与可视化的目的。简单的说,Elasticsearch本身只是一个搜索引擎存储数据的地方,好比我们的MySQL服务,如果我们希望操作Elasticsearch最简单的方式就是直接通过HTTP请求来进行方法,但这非常的不直观,借助Kibana可以让我们的操作和数据的查看更为直观

    #我们可以在Docker Hub中找到kibana镜像
    https://hub.docker.com/_/kibana
    
    #拉取Kibana镜像,需要与我们的ES版本对应
    docker pull kibana:7.17.6
    

    在这里插入图片描述

  6. 启动Kibana容器

    docker run --name my_kibana --restart=always \
    -e ELASTICSEARCH_HOSTS=http://192.168.177.128:9200 \
    -p 5601:5601 \
    -d kibana:7.17.6
    
    #以上命令解析
    --name:容器的名称
    
    --restart=always:开机自动重启kibana容器
    
    -e:指定环境变量
    -e ELASTICSEARCH_HOSTS=http://192.168.177.128:9200:指定ES的服务端地址和端口号
    
    -p:端口映射
    
    -d:后台启动
    
  7. 访问Kibana

    在Windows浏览器中输入一下地址对kibana的可视化界面进行访问

    http://192.168.177.128:5601/app/kibana
    

    在这里插入图片描述

  8. 测试ES

    本篇中,我们以Docker作为教学基础,不对ES做过多教学,这里我们进行基本ES访问测试即可。在kibana中我们找到Dev Tools工具,对ES进行操作访问

    在这里插入图片描述

    在这里插入图片描述

    在该界面,我们执行以下命令进行测试

    <!-- 索引一个文档,PUT /索引名/类型名/文档id {请求体} -->
    PUT /phone/_doc/1 
    {
      "name" : "xiao mi shou ji",
      "price" : 2998.00,
      "store" : 5000
    }
    
    PUT /phone/_doc/2
    {
      "name" : "hua wei shou ji",
      "price" : 3998.00,
      "store" : 8000
    }
    
    <!-- 查询索引 -->
    GET /phone/_doc/1
    GET /phone/_doc/2
    
    <!-- 返回值内容 -->
    {
      "_index" : "phone", //在哪个索引
      "_type" : "_doc", //在哪个类型
      "_id" : "1", //记录id
      "_version" : 1, //版本号
      "_seq_no" : 0, //并发控制字段,每次更新都会+1,用来做乐观锁
      "_primary_term" : 1, //同上,主分片重新分配,如重启,就会变化
      "found" : true,
      "_source" : {
        "name" : "xiao mi shou ji",
        "price" : 2998.0,
        "store" : 5000
      }
    }
    
  9. 分词器

    在ES中所有的语言分词器,默认使用的都是Standard Analyzer,但是这些分词器针对于中文的分词,并不友好。比如中华人民共和国国歌,不能简单的分成一个一个的词,也不能粗暴的分成中华人民共和国和国歌,而中华人民共和国国歌等都是一个词。因此常见的分词算法就是拿一个标准的词典,关键词都在这个词典里面,然后按照几种规则去查找有没有关键词。为此需要安装中文的分词器。我们可以进行一个测试

    <!-- 使用标准分词器 -->
    GET _analyze
    {
      "analyzer":"standard",
      "text":"中华人民共和国国歌"
    }
    
  10. 下载IK分词器

    ElasticSearch官方自带elasticsearch-analysis-ik分词器,并且支持自定义词典,源代码托管在github上:https://github.com/medcl/elasticsearch-analysis-ik,是ElasticSearch中国社区负责维护的项目,我们可以进行在如下地址进行对应版本的下载,版本一定要和ElasticSearch所匹配

    https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.17.6
    
  11. 安装IK分词器

    在前面安装的启动ElasticSearch容器时,我们已经将ElasticSearch容器的/usr/share/elasticsearch/plugins目录,映射到宿主机的/mydata/es/plugins目录下,所以比较方便的做法就是下载/elasticsearch-analysis-ik-7.17.6.zip文件,然后解压到该文件夹下即可。安装完毕后,需要重启ElasticSearch容器

    #在/mydata/es/plugins/目录下创建ik目录
    cd /mydata/es/plugins
    mkdir ik
    
    #将elasticsearch-analysis-ik-7.17.6.zip上传到/mydata/es/plugins/ik目录下,并解压
    cd /mydata/es/plugins/ik
    unzip elasticsearch-analysis-ik-7.17.6.zip
    
    #重启ES容器
    docker restart my_es
    
  12. 测试分词器

    在kibana中输入以下测试案例,IK分词安装后有两种分词策略:ik_max_wordik_smart
    ik_max_word:细力度分割策略

    ik_smart:粗粒度分割策略

    <!-- 细粒度分割策略 -->
    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text": ["中华人民共和国国歌"]
    }
    
    <!-- 粗粒度度分割策略 -->
    GET _analyze
    {
      "analyzer": "ik_smart",
      "text": ["中华人民共和国国歌"]
    }
    

10. Dockerfile自定义镜像

Dockerfile 是一个文本格式的配置文件, 用户可以使用 Dockerfile 来快速创建自定义的镜像,Dockerfile文件是由一行行命令语句组成,用来设置镜像的构建流程,基于这些命令即可以构建一个镜像

10.1 基本结构

一般的来说,Dockerfile主体内容分为四部分:基础镜像信息维护者/作者信息镜像操作指令容器启动时执行指令
比如下面就是一个Dockefile文件样例:

#选择基础镜像
FROM centos:7
#指定维护者/作者信息
MAINTAINER gucaini
#设置环境变量
ENV JAVA_HOME=/usr/local/java
#tomcat文件添加到镜像的 /usr/local/目录下,ADD指令会自动解压文件
ADD apache-tomcat-9.0.64.tar.gz /usr/local/
#在当前镜像基础上执行指定命令,并并提交为新的镜像层
RUN mv apache-tomcat-9.0.64 tomcat9
#声明镜像内服务监听的端口,供互联系统使用,在启动容器时需要通过-p映射端口
EXPOSE 8080
#指令用来指定启动容器时默认执行的命令
CMD ["catalina.sh", "run"]

10.2 常用指令说明

Dockerfile中指令包括配置指令操作指令,这里我们列举一些常用指令进行说明:

分类指令说明
配置指令FROM指定所创建镜像的基础镜像
配置指令MAINTAINER指定维护者/作者信息
配置指令LABEL为生成的镜像添加源数据标签信息
配置指令EXPOSE声明镜像内服务监听的端口
配置指令ENV指定环境变量
配置指令ENTRYPOINT指定镜像的默认入口命令
配置指令VOLUME创建一个数据卷挂载点
配置指令WORKDIR配置工作目录
配置指令ONBUILD创建子镜像时指定自动执行的操作指令
操作指令RUN运行指定命令
操作指令CMD启动容器时指定默认执行的命令
操作指令ADD添加内容到镜像
操作指令COPY复制内容到镜像
  • FROM:

    指定所创建镜像的基础镜像,是最重要的指令之一,格式为:FROM 镜像名:镜像标签 [AS 别名],任何 Dockerfile 中第一条指令必须为FROM指令。 并且, 如果在同一个Dockerfile中创建多个镜像时, 可以使用多个FROM 指令

    #默认在构建镜像时会把此镜像最为基础镜像,如果本地没有此镜像,会到远程仓库中拉取
    FROM centos:7
    
    #取上别名
    FROM centos:7 AS my_centos
    
  • MAINTAINER:

    指定维护者/作者信息,Dockerfile不限制MAINTAINER出现的位置,但是推荐放到FROM指令之后,格式为:MAINTAINER 作者名

    #指定作者名字
    MAINTAINER gucaini
    
  • LABEL:

    可以为生成的镜像添加元数据标签信息。一个Dockerfile可以写多个LABEL,但是不推荐这么做,Dockerfile每一条指令都会生成一层镜像,如果LABEL太长可 以使用\符号换行。格式为:LABEL key1=value1 key2=value2...

    #指定镜像的源数据信息,包括版本,创建时间,作者等
    LABEL version=1.0 date="2023-01-01" \
    	author="gucaini"
    
  • EXPOSE:

    声明镜像内服务监听的端口,注意该指令只是起到声明作用, 并不会自动完成端口映射,可以一次性指定多个端口。格式为:EXPOSE 端口1 端口2...

    #声明容器内开放的端口,方便做端口映射
    EXPOSE 8080 9090
    
  • ENV:

    指定环境变量,在镜像启动的容器中也会存在,格式为:ENV key=value

    #指定JDK环境变量
    ENV JAVA_HOME=/usr/local/java
    ENV PATH=$JAVA_HOME/bin:$PATH
    
  • ENTRYPOINT:

    指定镜像的默认入口命令, 该入口命令会在启动容器时作为根命令执行, 所有传人值作为该命令的参数。格式为:ENTRYPOINT ["命令","参数1","参数2"...]

    #容器启动时,执行echo这个命令,打印hello docker
    ENTRYPOINT ["/bin/echo","hello docker"]
    
  • VOLUME:

    创建一个数据卷挂载点,可以挂载宿主机上的卷或者其他容器上的卷,格式为:VOLUME [挂载目录]

    #容器启动时,自动挂载容器内部的/var/lib/mysql数据目录
    VOLUME ["/var/lib/mysql"]
    
  • WORKDIR:

    为后续的RUN、CMD、ENTRYPOINT指令配置工作目录,只会影响当前WORKDIR之后的指令。如不指定,默认的工作目录是在容器镜像中的根目录/中执行的,可以使用多个WORKDIR,命令格式为:WORKDIR 绝对路径

    #将容器的工作目录切换到/a目录中,并运行pwd命令
    WORKDIR /a
    RUN pwd
    
  • ONBUILD:

    指定当基于所生成镜像创建子镜像时,自动执行的操作指令,命令格式为:ONBUILD 指令

    #使用如下的Dockerfile创建父镜像ParentImag,指定ONBUILD指令
    .....
    ONBUILD RUN ls
    ONBUILD RUN pwd
    ....
    
    #当创建子镜像ChildImage时,会先执行ParentImag镜像中配置的ONBUILD指令
    FROM ParentImag
    ....
    
    #等价于在ChildImage的Dockerfile中添加了如下指令
    RUN ls
    RUN pwd
    
  • RUN:

    运行指定命令,命令格式为:RUN 命令 或 RUN ["执行脚本","参数1","参数2"],注意后者指令会被解析为 JSON 数组,因此必须用双引号、前者默认将在 shell 终端中运行命令,即/bin/sh -c 后者则使用 exec 执行,不会启动 shell 环境。

    #使用shell终端运行pwd命令
    RUN pwd
    
    #使用其他类型终端执行命令
    RUN ["/bin/bash","-c","echo hello"]
    
  • CMD:

    用来指定启动容器时默认执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条命令,只有最后一条会被执行,命令格式为:CMD ["执行脚本","参数1","参数2"] 或 CMD 命令 参数1 参数2 或 CMD ["参数1","参数2"]

    #容器启动时,执行Tomcat的启动脚本
    CMD ["catalina.sh", "run"]
    
    #容器启动时,执行Tomcat的启动脚本
    CMD /usr/local/apache-tomcat-9.0.64/bin/catalina.sh run
    
    #如果dokcerfile存在ENTRYPOINT指令也存在CMD指令,则CMD指令是提供给ENTRYPOINT指令的默认参数
    ENTRYPOINT ["/bin/echo"]
    CMD ["hello docker"]
    
  • ADD:

    添加内容到镜像,命令格式为:ADD 宿主机文件或目录路径[URL] 镜像内的文件或目录路径,宿主机文件或目录路径可以是当前Dockerfile文件所在的相对路径或绝对路径,也可以是一个URL远程资源地址。镜像内的文件或目录路径可以是镜像内绝对路径,也可以是相对于WORKDIR的相对路径

    #tomcat文件添加到镜像的/usr/local/目录下,ADD指令会自动解压文件
    ADD apache-tomcat-9.0.64.tar.gz /usr/local/
    
  • COPY:

    复制内容到镜像,命令格式:COPY 宿主机文件或目录路径 镜像内的文件或目录路径,这个命令和ADD指令比较相似,算是一迷你版的ADD指令,不同的是COPY指令只能复制宿主机的文件或目录,不能从URL远程资源地址中复制,同时,ADD指令会自动对归档压缩文件进行解压,而COPY指令则不会

    #tomcat目录复制到镜像的/usr/local/目录下
    COPY apache-tomcat-9.0.64 /usr/local/
    

10.3 自定义jdk镜像

我们来尝试自己制作一个Centos7的镜像,里面包含了jdk

  1. 在/mydata目录下建立docker/jdk文件夹,并将jdk压缩包上传至该文件夹

    mkdir -p /mydata/docker/jdk
    
  2. 创建并编写Dockerfile文件

    #进入/mydata/dokcer/jdk目录
    cd /mydata/dokcer/jdk
    
    #创建Dockerfile
    vim Dockerfile
    
    #编写内容
    FROM centos:7
    MAINTAINER gucaini
    ADD jdk-8u121-linux-x64.tar.gz /usr/local/java
    ENV JAVA_HOME=/usr/local/java/jdk1.8.0_121
    ENV PATH=$JAVA_HOME/bin:$PATH
    CMD ["java","-version"]
    
  3. 构建镜像

    语法:docker build [选项] Dockerfile路径

    #在Dockerfile所在的目录下执行以下命令,注命令末尾的"."不可省略
    docker build -t my_jdk .
    
    #以上命令解释
    docker build:构建镜像
    -t my_jdk选项:指定构建镜像的tag名字
    .:当前Dockerfile所在的路径
    
  4. 运行镜像

    docker run my_jdk
    

    在这里插入图片描述

10.4 自定义tomcat镜像

我们在我们制作的jdk镜像的基础上来制作tomcat镜像

  1. 在/mydata目录下建立docker/tomcat文件夹,并将tomcat压缩包上传至该文件夹

    mkdir -p /mydata/docker/tomcat
    
  2. 创建并编写Dockerfile文件

    #进入/mydata/dokcer/tomcat目录
    cd /mydata/dokcer/jdk
    
    #创建Dockerfile
    vim Dockerfile
    
    #编写内容
    FROM my_jdk
    MAINTAINER gucaini
    ADD apache-tomcat-9.0.64.tar.gz /usr/local/
    ENV CATALINA_HOME=/usr/local/apache-tomcat-9.0.64
    ENV PATH=$CATALINA_HOME/bin:$PATH
    EXPOSE 8080
    CMD ["catalina.sh","run"]
    
  3. 构建镜像

    #在Dockerfile所在的目录下执行以下命令,注命令末尾的"."不可省略
    docker build -t my_tomcat:9 .
    
    #以上命令解释
    docker build:构建镜像
    -t my_tomcat:9选项:指定构建镜像的tag名字
    .:当前Dockerfile所在的路径
    
  4. 运行镜像

    docker run -d -p 8080:8080 my_tomcat:9
    
  5. 在浏览器访问以下地址

    http://192.168.177.128:8080/
    

    在这里插入图片描述

10.5 多个镜像构建

Dockerfile文件支持在一个文件中,指定多个镜像的构建指令,他们以FROM指令来区分,例如:我们可以在一个Dockerfile文件中即构建jdk镜像,又构建Tomcat镜像

  1. 删除jdk和tomcat自定义镜像

    docker rmi my_jdk
    docker rmi my_tomcat:9
    
  2. 在/mydata/docker目录下编写Dockerfile文件

    #进入/mydata/dokcer目录
    cd /mydata/dokcer
    
    #创建Dockerfile
    vim Dockerfile
    
    #编写内容,我们可以在一个Dockerfile文件中编写多个自定义镜像的构建,通过在FROM指令后面加上AS来区分各个镜像,并为它们加上别名
    FROM centos:7 AS centos7
    MAINTAINER gucaini
    ADD jdk/jdk-8u121-linux-x64.tar.gz /usr/local/java
    ENV JAVA_HOME=/usr/local/java/jdk1.8.0_121
    ENV PATH=$JAVA_HOME/bin:$PATH
    CMD ["java","-version"]
    
    FROM my_jdk as jdk8
    MAINTAINER gucaini
    ADD tomcat/apache-tomcat-9.0.64.tar.gz /usr/local/
    ENV CATALINA_HOME=/usr/local/apache-tomcat-9.0.64
    ENV PATH=$CATALINA_HOME/bin:$PATH
    EXPOSE 8080
    CMD ["catalina.sh","run"]
    
  3. 构建镜像

    #在Dockerfile所在的目录下执行以下命令,注命令末尾的"."不可省略
    docker build -t my_jdk --target centos7 .
    docker build -t my_tomcat:9 --target jdk8 .
    
    #以上命令解释
    docker build:构建镜像
    -t 选项:指定构建镜像的tag名字
    --target 选项:指定要当前要构建的是Dockerfile文件中的哪一个自定义镜像的别名
    .:当前Dockerfile所在的路径
    
  4. 运行镜像

    docker run -d -p 8080:8080 my_tomcat:9
    
  5. 在浏览器访问以下地址

    http://192.168.177.128:8080/
    

    在这里插入图片描述

10.6 部署SpringBoot项目

我们在学习了Dockerfile之后,可以尝试部署一个单体SpringBoot项目到Docker容器中,项目的源码和打包后的文件都附在了资料中,首先我们需要将SpringBoot项目打包,然后将打包好的SpringBoot项目制作Docker镜像,镜像制作完毕之后,启动MySQL数据库容器,将SQL脚本导入,最后启动SpringBoot容器

  1. 将项目进行打包(也可以使用打包好的jar包)

    将项目pom.xml文件中的打包方式修改为jar

    <groupId>com.java</groupId>
    <artifactId>JXC</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    

    将项目的数据库连接进行修改,此处url的数据库的连接地址修改为你的虚拟机ip地址

    在这里插入图片描述

    点击右上角Maven按钮,选择Lifecycle->package进行打包,出现BUILD SUCCESS即为打包成功

    在这里插入图片描述

    在这里插入图片描述

  2. 上传打包好的jar包

    target目录下找到打包好的JXC.jar,鼠标右键,选择Open In->Explorer就可以找到我们磁盘上的JXC.jar包了

    在这里插入图片描述

    在这里插入图片描述

  3. 上传JXC.jar到宿主机中

    创建/mydata/docker/springboot文件夹,并将JXC.jar上传到文件夹中

    #创建文件夹
    mkdir -p /mydata/docker/springboot
    
  4. 创建并编写Dockerfile文件

    #进入/mydata/dokcer/spirngboot目录
    cd /mydata/dokcer/springboot
    
    #创建Dockerfile
    vim Dockerfile
    
    #编写内容
    FROM my_jdk
    MAINTAINER gucaini
    COPY JXC.jar /usr/local/
    WORKDIR /usr/local/
    RUN chmod +x /usr/local/JXC.jar
    EXPOSE 80
    CMD ["java","-jar","JXC.jar"]
    
  5. 构建镜像

    #在Dockerfile所在的目录下执行以下命令,注命令末尾的"."不可省略
    docker build -t springboot_jxc .
    
  6. 启动MySQL容器

    #启动MySQL容器
    docker run --name=my_mysql --privileged=true --restart=always \
    -v /mydata/mysql/data:/var/lib/mysql \
    -v /mydata/mysql/conf:/etc/mysql/conf.d \
    -v /mydata/mysql/logs:/var/log/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -d -p 3306:3306 mysql
    
    #以上命令解析
    --name:容器的名称
    
    --privileged=true:为了防止出现Centos7安全Selinux禁止了一些安全权限
    
    --restart=always:开机自动重启MySQL容器
    
    -v:目录挂载
    -v /mydata/mysql/data:/var/lib/mysql
    将宿主机的/mydata/mysql/data目录挂载到容器中的/var/lib/mysql目录中(这是MySQL容器默认存储数据文件的目录)
    -v /mydata/mysql/conf:/etc/mysql/conf.d
    将宿主机的/mydata/mysql/conf目录挂载到容器中的/etc/mysql/conf.d目录(这是MySQL容器默认的配置文件目录)
    -v /mydata/mysql/logs:/var/log/mysql 
    将宿主机的/mydata/mysql/conf目录挂载到容器中的/etc/mysql/conf.d目录(这是MySQL容器默认的配置文件目录)
    
    -e:指定环境变量,其中MYSQL_ROOT_PASSWORD指定数据库的密码
    
    -d:后台启动
    
    -p:端口映射
    
  7. 导入数据库数据

    在Windows中使用SQLyog连接数据库,并将jxc.sql文件中的数据库语句进行导入,出现数据库db_jxc和具体的表即导入成功

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  8. 启动SpringBoot容器

    docker run --name=jxc -d -p 80:80 springboot_jxc
    
  9. 进行访问测试

    在Windows浏览器中访问如下地址,访问后如出现如下登录页面,则部署成功,登录账号:admin,登录密码:admin123

    http://192.168.177.128:80/login.html
    

    在这里插入图片描述

    在这里插入图片描述

11. Docker网络

容器和容器之前是相互隔离的,外界无法直接访问,那么Docker是如何做到容器和宿主机,容器和容器之前的网络通信呢?首先我们先来看一下宿主机的网络情况,执行ifconfig,可以得到下图内容:

  • 其中ens33是我们宿主机的网卡
  • lo是本机回环地址
  • virbr0是我们在创建CentOS7虚拟机时如果在安装过程中选择过虚拟化的服务安装,启动网卡时就会帮我们创建这个网卡,它的作用是做虚拟机网桥使用的,其作用是让我们的虚拟机可以访问互联网
  • 我们着重要关注的是docker0,Docker默认启动时会在宿主机上建立一个虚拟网桥docker0,当容器启动的时候,每一个容器会分配一个虚拟IP地址,宿主机通过虚拟网桥转发容器进行通信,容器也通过虚拟网桥转发宿主机进行通信

在这里插入图片描述

11.1 Docker的网络模式

我们学习Docker网络,实际就是学习docker0这个虚拟网桥相关的知识,Docker一般来说有5种网络模式,它们分别是:bridge模式、host模式、none模式、container模式、自定义模式。Docker默认提供了3种网络模式,我们可以通过docker network ls来查看,生成容器时不指定网络模式下默认使用bridge模式

在这里插入图片描述

11.2 bridger模式

我们知道Docker服务启动时会首先在主机上自动创建一个 docker0虚拟网桥, 网桥可以理解为一个交换机, 负责挂载其上的接口之间进行包转发。同时,Docker随机分配一个本地未占用的私有网段地址给 docker0接口,一般为172.17.0.0/16 网段, 掩码为 255.255.0.0。 此后每启动一个新的容器,就会为容器自动分配一个该网段的地址,同时会创建了一对 veth pair互联接口。 当向任一个接口发送包时, 另外一个接口自动收到相同的包。 互联接口的一端位于容器内, 即 eth0 另一端在本地并被挂载到 docker0网桥, 名称以 veth 开头(例如 vethAQI2QT) 。 通过这种方式,主机可以与容器通信, 容器之间也可以相互通信。 如此一来, Docker就创建了在主机和所有容器之间一个虚拟共享网络。这种模式就是bridger模式,也是默认的模式

在这里插入图片描述

  1. 我们在启动容器时可以添加--network参数来指定容器以何种网络模式运行,如果我们不写--network那么默认就是bridger模式。可以通过-P -p参数来指定端口映射,我们可以启动一个tomcat容器来观察

    #启动tomcat01容器
    docker run --name tomcat01 -d -p 8081:8080 my_tomcat:9
    
  2. 启动容器后,我们再次使用ifconfig查看网络情况,如下图,可以看到在我们的网络信息中,多了一个veth19884e7这么一个网卡,这就是每个容器的互连接口,在宿主机的网络信息中以 veth 开头

    在这里插入图片描述

  3. 我们再查看一下Docker容器中的网络情况,可以看到容器内Docker为我们分配了在172.17.0.0/16 网段的一个地址做为容器内部的IP地址,容器内部的互连接口就是eth0

    docker inspect tomcat01
    

    在这里插入图片描述

  4. 在宿主机中,我们尝试ping一下容器的ip,发现是可以进行访问的,说明docker0虚拟网桥打通了宿主机和容器内部的网络

    ping 172.17.0.2
    

    在这里插入图片描述

  5. 我们再启动一个新的tomcat,看一看容器与容器之间是否可以连通

    #启动tomcat02容器
    docker run --name tomcat02 -d -p 8082:8080 my_tomcat:9
    
    #进入tomcat02容器
    docker exec -it tomcat02 bash
    
    #容器内部默认是没有安装ping命令的,我们先安装一下
    yum install -y iputils-ping
    
    #从tomcat02容器访问tomcat01容器
    ping 172.17.0.2
    

    在这里插入图片描述

  6. 这就是bridger网络模式,其特点是网络隔离性好,会占用宿主机端口,只占用一个真实ip,适用于大多数的场景,也是我们用的最多的网络模式

11.3 host模式

如果启动容器的时候使用--network=host,就开启了host模式,那么这个容器将不会获得一个独立的Network Namespace,即不要创建容器内的网络,而是和宿主机共用一个本地网络,容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口,如下图,容器1将不在通过docker0,而是直接共用宿主机的网卡。

在这里插入图片描述

  1. 我们再以host模式来启动一个tomcat容器,需要注意的是,当使用host模式时,将不用指定-P-p端口映射

    #启动tomcat03容器
    docker run --name tomcat03 --network=host -d my_tomcat:9
    
  2. 我们查看一下容器的网络情况,网络模式切换为了host,并且容器内的ip和网关都为空了

    docker inspect tomcat03
    

    在这里插入图片描述

  3. 我们进入到容器中,查看容器中的网络情况,我们发现目前容器中显示的网络信息和宿主机中一致

    #进入tomcat03容器
    docker exec -it tomcat03 bash
    
    #容器内部默认是没有安装ifconfig命令的,我们先安装一下
    yum install -y net-tools
    
    #查看网络情况
    ifconfig
    

    在这里插入图片描述

  4. 在浏览器中访问8080端口,可以正常访问到tomcat,说明容器确实使用的是宿主机的IP和端口号

    http://192.168.177.128:8080
    

    在这里插入图片描述

  5. 这就是host网络模式,网络隔离性较差,会占用宿主机端口,只占用一个真实ip,会出现端口冲突,性能最好。能确保所有容器端口不冲突且都需要对外暴露时可以使用

11.4 none模式

如果启动容器的时候使用--network=none,就开启了none模式,这种模式下Docker容器拥有自己的NetWork Namespace,但是并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、ip、路由等信息,需要我们自己为Docker容器添加网卡、配置ip等。

在这里插入图片描述

  1. 我们再以none模式来启动一个tomcat容器

    #启动tomcat04容器
    docker run --name tomcat04 --network=none -d my_tomcat:9
    
  2. 我们查看一下容器的网络情况,网络模式切换为了none,并且容器内的ip和网关都为空了

    docker inspect tomcat04
    

    在这里插入图片描述

  3. 我们进入到容器中,无法安装ifconfig命令,因为容器处于无网状态

    #进入tomcat04容器
    docker exec -it tomcat04 bash
    
    #容器内部默认是没有安装ifconfig命令的,执行时命令时出错,容器无网
    yum install -y net-tools
    
  4. 我们知道在Linux系统中安装软件的方式还有一种是使用npm的方式,默认tomcat容器中的Linux系统中是包含npm命令的,所以我们可以通过在宿主机中下载好一个net-toolsnpm包,拷贝到容器中,然后进行ifconfig命令的安装

    #上传net-tools-2.0-0.25.20131004git.el7.x86_64.rpm到/root目录下,并拷贝到容器中/usr/local中
    docker cp /root/net-tools-2.0-0.25.20131004git.el7.x86_64.rpm tomcat04:/usr/local
    
    #进入tomcat04容器
    docker exec -it tomcat04 bash
    
    #执行安装
    rpm -ivh net-tools-2.0-0.25.20131004git.el7.x86_64.rpm
    
  5. 在容器中再次查看网络情况,此时我们发现只有lo是本机回环地址

    ifconfig
    

    在这里插入图片描述

  6. 这就是none网络模式,默认创建的容器是一个无网容器,其网卡、ip、路由等信息,一切由运维人员来自定义。例如使用Open vSwitch来自定义网络,或是借助Openstack、Kubenetes等方式来自动化配置网络。

11.5 container模式

如果启动容器的时候使用--network=container:容器名,就开启了container模式,这种模式下新创建的Docker容器不会创建自己的网卡、配置自己的IP等。而是和一个已经存在的容器共享一个Network Namespace,而不再和宿主机共享,是和已存在的容器共享网卡、端口、IP等。两个容器之间除了网络方面,其他的例如文件、进程列表等还是相互隔离的。

在这里插入图片描述

  1. 我们先以bridge模式来启动一个redis容器,并暴露其端口号为8080

    #启动centos7容器,并暴露端口为8080
    docker run --name redis -p 8080:8080 -d redis:7.0.5
    
  2. 我们查看一下redis容器的网络情况,网络模式为bridge,容器IP为172.17.0.2

    docker inspect redis
    

    在这里插入图片描述

  3. 我们再以container模式来启动一个tomcat容器,绑定到redis容器

    #启动tomcat05容器
    docker run --name tomcat05 --network=container:redis -d my_tomcat:9
    
  4. 我们查看一下tomcat容器的网络情况,网络信息都是空白

    docker inspect tomcat05
    

    在这里插入图片描述

  5. 然后我们在浏览器中访问8080端口,可以正常访问到tomcat,说明容器确实使用的redis容器的IP和端口号等

    http://192.168.177.128:8080
    

    在这里插入图片描述

  6. 这就是container网络模式,网络隔离性较好,会占用容器端口,会出现端口冲突,性能差。可能只有在开发类似网关应用时可以考虑使用

11.6 自定义模式

之前在bridger模式中,我们启动了两个tomcat容器,tomcat01和tomcat02。之后我们进入tomcat02容器中安装ping命令来尝试访问tomcat01容器的IP地址,发现是可以互通的,但是会有一些问题。在说明自定义模式之前,我们先来看下面一个现象

  1. 我们先重新启动bridger模式中的两个tomcat容器

    docker start tomcat01
    docker start tomcat02
    
    #如果容器已经删除,可以使用一下命令快速创建
    docker run --name tomcat01 -d -p 8081:8080 my_tomcat:9
    docker run --name tomcat02 -d -p 8082:8080 my_tomcat:9
    
  2. 然后我们分别查看两个容器的ip地址

    docker inspect tomcat01
    docker inspect tomcat02
    

    在这里插入图片描述

    在这里插入图片描述

  3. 进入tomcat02容器,并尝试访问tomcat01容器的ip

    #进入tomcat02容器
    docker exec -it tomcat02 bash
    
    #从tomcat02容器访问tomcat01容器
    ping 172.17.0.2
    
    #如果是新创建的两个容器,没有ping命令的话,就安装一下
    yum install -y iputils-ping
    

    在这里插入图片描述

  4. 然后我们停止tomcat01容器,再重新启动一个tomcat06容器,并查看网络信息

    #停止tomcat01容器
    docker stop tomcat01
    
    #启动tomcat06容器
    docker run --name tomcat06 -d -p 8083:8080 my_tomcat:9
    
    #查看tomcat06容器的网络信息
    docker inspect tomcat06
    

    在这里插入图片描述

  5. 重启tomcat01容器,并查看网络信息

    #启动tomcat01容器
    docker start tomcat01
    
    #查看tomcat01容器的网络信息
    docker inspect tomcat01
    

    在这里插入图片描述

    通过以上现象,我们得出一个结论:在默认的bridger桥接模式下,容器可以通过容器ip进行互联通信,但是容器被分配的ip地址会随着容器的启动顺序而变化,也就是说,容器启动后的ip是不固定的。那么这就会有问题了,假设我们现在部署了一个SpringBoot程序,该程序是用docker进行部署的,并且需要连接同样由docker启动的MySQL数据库,如果我们在SpringBoot程序中使用容器ip地址进行连接,那么当MySQL数据库容器的ip发生变化时,就连接不上了!

    这就好比在微服务部署的场景下,注册中心是使用服务名来识别微服务的,而我们上线部署的时候微服务对应的ip地址可能会改动,所以我们需要使用容器名来配置容器间的网络连接,而不是使用容器ip连接

官方推荐我们使用自定义模式来创建网络,使用docker network create命令创建一个自定义桥接网络,在启动容器的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以通过容器名来相互访问了。

  1. 首先,我们先进入tomcat02容器中,看看能否直接用容器名来访问

    #进入tomcat02容器
    docker exec -it tomcat02 bash
    
    #使用容器名从tomcat02容器访问tomcat01容器
    ping tomcat01
    

    在这里插入图片描述

  2. 创建自定义桥接网络,语法:docker network create 网络名 或者 docker network create 网络名 --subnet=172.18.0.0/24 指定网段

    #创建自定义桥接网络
    docker network create my_network
    
    #查看网络
    dokcer network ls
    

    在这里插入图片描述

  3. 通过自定义网络重新创建tomcat01和tomcat02容器

    #删掉之前的容器
    docker rm -f tomcat01
    docker rm -f tomcat02
    
    #通过自定义网络创建容器
    docker run --name tomcat01 --network my_network -d -p 8081:8080 my_tomcat:9
    docker run --name tomcat02 --network my_network -d -p 8082:8080 my_tomcat:9
    
  4. 查看两个容器的网络信息,目前使用的网络是我们自定义的网络名

    docker inspect tomcat01
    docker inspect tomcat02
    

    在这里插入图片描述

  5. 我们进入tomcat02容器,通过容器名来尝试访问tomcat01容器

    #进入tomcat02容器
    docker exec -it tomcat02 bash
    
    #使用容器名从tomcat02容器访问tomcat01容器
    ping tomcat01
    
    #如果是新创建的两个容器,没有ping命令的话,就安装一下
    yum install -y iputils-ping
    

    在这里插入图片描述

  6. 这就是自定义网络模式,默认使用的是bridger网桥模式,只不过是我们自己创建和定义的网络,其特点在相同网桥下的容器可以通过容器名来互相访问,共享网络,我们无需再去担心容器启动时IP地址等信息发生变化,自定义模式本身就维护好了主机名和IP的对应关系。

11.7 同宿主机网络通信的方案

在了解了Docker网络后,在同一台宿主机中,容器之间的网络通信方案就有以下几种:

  • 通过容器本身的ip相互直接访问对方(不推荐)

    例如:Tomcat互访、Redis互访、MySQL互访,这种方式会导致ip地址的硬编码写死,不方便迁移,容器重启后,ip可能会发生变化,容器启动时是随机生成的一个ip,通过容器ip访问不是一个好的方案,不推荐使用

  • 通过宿主机的ip:port访问(可以使用)
    通过宿主机的ip:port访问,需要将宿主机的端口与容器的端口进行映射

  • 通过link建立连接(docker老版本使用,官方已经不推荐使用)

    运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。缺点是:通过link建立连接的容器,被链接的容器能ping通源容器,反过来不行

  • 通过自定义网络通信(官方推荐)

    使用docker network命令创建一个自定义桥接网络,在docker run的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以相互访问

11.8 跨宿主机网络通信的方案

从Docker 1.7.0 版本开始,Docker官方正式把网络的功能实现以插件化的形式剥离出来,允许用户通过指令来选择不同的后端实现,剥离出来的独立容器网络项目即为libnetwork项目,Docker 希望将来能为不同类型的容器定义统一规范的网络层标准,支持多种操作系统平台,这也是Docker希望构建强大容器生态系统的积极的尝试。简单来说,就是Docker官方把多台宿主机的网络支持剥离出来,交给了用户自己来定义,你可以使用Docker官方为我们提供的原生网络方案,也可以使用一些优秀的第三方开源产品来实现网络方案。

libnetwork容器网络模型( Container Networking Model, CNM )十分简洁和抽象,可以让其上层使用网络功能的容器不关心底层的网络实现,网络模型包括三种基本元素

  • 沙盒(Sandbox):代表一个具体的容器中的网络命名空间
  • 接入点(Endpoint):代表网络上可以挂载容器的接口,会分配IP地址
  • 网络(Network):可以连通多个接入点的一个子网

在这里插入图片描述

跨宿主机通信目前有如下几种主流方法:

  • 使用Overlay覆盖模式打通网路

    Overlay覆盖网络会在多个宿主机之间创建一个分布式的网络。这个网络会覆盖宿主机特有的网络,并允许容器连接它(包括集群服务中的容器)来安全通信。这是Docker官方提供的一个原生方案

  • 使用Open vSwitch虚拟交换机实现打通网络

    Open vSwitch是第三方产品,是一个虚拟交换软件,主要用于虚拟机环境,提供网络的虚拟化

  • 使用Flannel来打通网络

    Flannel是一个网络规划服务软件,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信

  • 借助OpenstackKubenetes等方式打通网络

    OpenstackKubenetes无疑是当今云计算领域的两大巨头,它们都是开源管理技术。其内部提供了完善的虚拟技术的网络解决方案

我们以Docker官方的原生方案Overlay来实现跨宿主机网络通信,Overlay在IP地址可以互相访问的多个宿主机之间搭建隧道,从而让容器可以互相访。要实现Overlay网络我们需要满足以下条件:

  • 保证Docker的版本在1.9.0以上,Overlay方案是在Docker 1.9.0之后才推出的方案

  • 需要有一个key-value式的键值数据库将各个宿主机的中的容器网络信息进行注册,从而实现跨主机的通信。Docker支持Consul、Etcd、ZooKeeper等服务注册发现工具,官方推荐我们使用Consul

  • 每台宿主机必须具有唯一的主机名,因为key-value式的键值数据库使用主机名来标识每台宿主机成员

  • Linux内核的版本大于3.12,因为overlay模式底层是基于VXLAN这种技术来实现的,而在Linux内核3.12以后才开始提供,如果低于这个版本,可能会出现无法通信的情况

Overlay网络原理:当我们使用Overlay网络的时候,每台宿主机会将自己的网络信息注册到Consul之中,当我们在某一台机器创建Overlay网络的时候,会将网络信息也同步到Consul之中,加入到Consul管理的其他宿主机将同步这个网络。同时,当创建Overlay网络的时候,Docker会帮我们创建2个网桥,overlay是我们自己创建的覆盖网络网桥,docker_gwbridge是docker帮我们创建的网桥,它会在这种模式中,代替docker0,用于容器与当前宿主机通信。当容器1向容器2通信时,会通过容器的eth0发送数据包到node1宿主机的overlay网桥中,overlay网桥使用VETP将数据包进行封装,然后将封装好的VXLAN格式的数据包交给node1宿主机的eth0,随后传递给node2宿主机的eth0。node2宿主机接收到该数据后同样使用overlay网桥的VETP将数据解析,然后发送给容器2的eth0从而实现跨主机通信

在这里插入图片描述

下面我们以两台分别以两台虚拟机node1和node2为例来演示这个案例:

要确保node1和node2两台虚拟机的网络是互通的
node1:192.168.177.128
node2:192.168.177.129

  1. 配置每台虚拟机的主机名

    #在192.168.177.128虚拟机执行
    vim /etc/hostname
    
    #输入以下内容,并保存
    node1
    
    #在192.168.177.129虚拟机执行
    vim /etc/hostname
    
    #输入以下内容,并保存
    node2
    
  2. 配置每台虚拟机的hosts映射

    #在192.168.177.128虚拟机执行
    vim /etc/hosts
    
    #输入以下内容,并保存
    192.168.177.128 node1
    192.168.177.128 node2
    
    #在192.168.177.129虚拟机执行
    vim /etc/hosts
    
    #输入以下内容,并保存
    192.168.177.128 node1
    192.168.177.128 node2
    
    #操作完毕之后分别重启两台虚拟机
    reboot
    
  3. 分别查看每台虚拟机的主机名

    hostname -f
    

    在这里插入图片描述

    在这里插入图片描述

  4. 查看Linux内核,并配置内核升级仓库(如果内核版本高于3.12则不用执行4-6步骤)

    #查看Linux的内核版本
    uname -r
    
    #最新的Linux内核版本地址
    https://www.kernel.org/
    
    #配置ELRepo仓库,用于升级内核
    rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
    yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
    
    #仓库启用后,可以使用下面的命令列出可用的内核相关包
    yum --disablerepo="*" --enablerepo="elrepo-kernel" list available
    

    在这里插入图片描述

  5. 安装内核

    在列出的内核相关包中lt为长期维护版本,ml为稳定版本,从上图可知,目前最新的稳定版本内核是kerner-ml.x86_64

    #安装kernel-ml.x86_64和kernel-ml-devel.x86_64
    yum -y --enablerepo=elrepo-kernel install kernel-ml.x86_64 kernel-ml-devel.x86_64
    
  6. 设置 GRUB 默认的内核版本

    我们再次查看一下Linux的内核版本,发现内核的版本还是之前的3.10的版本,其原因是Linux启动的时候还是默认选择了老的内核,这里需要配置一下GRUB,重新设置一下启动时的默认内核

    #查看内核
    uname -r
    
    #查看当前都有哪些启动项
    cat /boot/grub2/grub.cfg |grep menuentry
    
    #设置默认选项
    grub2-set-default 'CentOS Linux (6.1.9-1.el7.elrepo.x86_64) 7 (Core)'
    
    #重启Linux
    reboot
    

    在这里插入图片描述

    在这里插入图片描述

    至此,我们的内核升级就完成了,注意将node1和node2宿主机节点都完成内核升级

  7. 在node1中安装Consul注册中心

    #拉取Consul注册中心镜像
    docker pull consul
    
    #启动Consul容器
    docker run --name consul --restart=always -p 8500:8500 -d consul
    
  8. 访问consul,看是否启动成功

    http://192.168.177.128:8500
    

    在这里插入图片描述

  9. 配置node1的daemon.json文件

    #打开node1的daemon.json文件
    vim /etc/docker/daemon.json
    
    #在该文件中,添加如下两行配置
    {
      "cluster-store":"consul://192.168.177.128:8500",
      "cluster-advertise":"192.168.177.128:2376"
    }
    #以上配置中cluster-store是配置Consul注册中心集群的地址
    #cluster-advertise,是广播通信地址和端口
    
    #重启daemon
    systemctl daemon-reload
    
    #重启docker服务
    systemctl restart docker
    
  10. 配置node2的daemon.json文件

    #打开node2的daemon.json文件
    vim /etc/docker/daemon.json
    
    #在该文件中,添加如下两行配置
    {
      "cluster-store":"consul://192.168.177.128:8500",
      "cluster-advertise":"192.168.177.129:2376"
    }
    #注意,这里的cluster-advertise是暴露129这台机器的广播IP地址,需要修改IP为129节点地址
    
    #重启daemon
    systemctl daemon-reload
    
    #重启docker服务
    systemctl restart docker
    
  11. 使用自定义模式创建网络

    前面介绍过Docker中的自定义网络模式,可以使用docker network create命令创建,默认创建出的自定义网络的模式是bridger桥接模式。实际上,在自定义模式中,我们还可以指定创建的自定网络模式为overlay覆盖模式,在node1中创建自定义overlay网络

    #创建自定义网络,--driver overlay指定创建模式为覆盖模式,如果不指定,默认就是bridger桥接
    docker network create --driver overlay my_overlay_work
    
    #同时在node1和node2查看网络,可以看到新网络my_overlay_work
    docker network ls
    

    在这里插入图片描述

  12. 在node1中创建tomcat01容器

    #删除掉之前的tomcat01容器
    docker rm -f tomcat01
    
    #创建tomcat01容器,并连接到my_overlay_work网络上
    docker run --name tomcat01 --network my_overlay_work -d -p 8081:8080 my_tomcat:9
    
  13. 在node2中创建tomcat02容器

    #删除掉之前的tomcat02容器
    docker rm -f tomcat02
    
    #创建tomcat02容器,并连接到my_overlay_work网络上
    docker run --name tomcat02 --network my_overlay_work -d -p 8082:8080 my_tomcat:9
    
  14. 进入node2中的tomcat02容器,通过容器名来尝试访问node1中的tomcat01容器

    #进入node2中的tomcat02容器
    docker exec -it tomcat02 bash
    
    #使用容器名从node2中的tomcat02容器访问node1的tomcat01容器
    ping tomcat01
    
    #如果是新创建的两个容器,没有ping命令的话,就安装一下
    yum install -y iputils-ping
    

    在这里插入图片描述

    至此,我们完成了跨宿主机的网络通信

12. 常用软件高可用安装

前面,我们对常用软件的基础安装做了了解和学习,现在我们对常用软件的高可用安装来进行学习。在日常企业工作中,我们所部署的大量基础软件为避免出现单点故障问题都需要进行高可用来保证程序正常对外提供服务。通常的做法是在同一台宿主机中启动多态实例,或在多台宿主机中部署多个实例来达到集群和分布式的效果。这里我们以一台宿主机为例,以**伪分布式(伪集群)**的方式来构建常用软件的高可用方式,仅方便学习。

12.1 MySQL主从复制搭建

所谓MySQL主从,就是建立两个完全一样的数据库,一个是主库,一个是从库,主库对外提供读写的操作,从库对外提供读的操作,大部分系统的访问模型是读多写少,读写请求量的差距可能比较大,所以我们可以通过一主多从的方式,主库只负责写入和部分核心逻辑的查询,多个从库只负责查询,提升查询性能,降低主库压力,当主库宕机时,从库可以切成主库,保证服务的高可用,然后从库也可以做数据的容灾备份,总结下有这些优势:读写分离高可用数据备份

下图是MySQL主从复制的原理图:

在这里插入图片描述

MySQL的主从复制将经过如下步骤:

  1. Master主服务器上的数据发生改变时(UPDATE、INSERT、DELETE),则将其改变写入二进制事件日志文件Binary logbinlog)中
  2. Slave从服务器会在一定时间间隔内对Master主服务器上的二进制日志进行探测,探测其是否发生过改变,如果探测到Master主服务器的二进制事件日志发生了改变,则开始一个I/O thread线程请求Master二进制事件日志
  3. 同时Master主服务器为每个I/O thread线程启动一个Log dump thread线程,用于向其发送二进制事件日志
  4. Slave从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件Relay log
  5. Slave从服务器将启动SQL thread线程从中继日志中读取二进制日志,在本地重放(回放),使得其数据和主服务器保持一致
  6. 最后I/O threadSQL thread将进入睡眠状态,等待下一次被唤醒

下面我们以一主一从伪集群的方式来搭建数据库来进行演示,即在一台宿主机中即安装主库,也安装从库实现主从复制。其中主库的端口号为3306,从库的端口号为3307,这里我们以bridger网络摸式来进行演示

  1. 在宿主机中分别启动两个数据库容器

    #启动MySQL主库容器
    docker run --name=mysql_master --privileged=true --restart=always \
    -v /mydata/mysql/master/data:/var/lib/mysql \
    -v /mydata/mysql/master/conf:/etc/mysql/conf.d \
    -v /mydata/mysql/master/logs:/var/log/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -d -p 3306:3306 mysql
    
    #启动MySQL从库容器
    docker run --name=mysql_slave --privileged=true --restart=always \
    -v /mydata/mysql/slave/data:/var/lib/mysql \
    -v /mydata/mysql/slave/conf:/etc/mysql/conf.d \
    -v /mydata/mysql/slave/logs:/var/log/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -d -p 3307:3306 mysql
    
    #注意两个库的数据目录的挂载路径:主库在/mydata/mysql/master/目录下,从库在/mydata/mysql/slave/目录下
    
  2. 添加MySQL主库的自定义配置

    #在宿主机中切换到/mydata/mysql/master/conf目录下
    cd /mydata/mysql/master/conf
    
    #添加my.cnf数据库核心配置文件
    vim my.cnf
    
    #添加以下内容
    [client]
    default-character-set=utf8mb4
    
    [mysqld]
    character-set-server=utf8mb4
    autocommit=1
    server-id=3306
    log-bin=mysql-bin
    
    #[clinet]代表配置MySQL客户端连接时的相关配置
    default-character-set:设置MySQL客户端连接时的字符集
    
    #[mysqld]代表MySQL服务端的相关配置
    character-set-server:设置服务端的字符集
    autocommit:开启自动提交
    server-id:表示server编号,编号要唯一
    log-bin:表示启用二进制日志,并设置日志文件的名字,可以用于主从服务器之间的数据同步
    
    #配置完毕后重启容器
    docker restart mysql_master
    
  3. 添加MySQL从库的自定义配置

    #在宿主机中切换到/mydata/mysql/slave/conf目录下
    cd /mydata/mysql/slave/conf
    
    #添加my.cnf数据库核心配置文件
    vim my.cnf
    
    #添加以下内容
    [client]
    default-character-set=utf8mb4
    
    [mysqld]
    character-set-server=utf8mb4
    autocommit=1
    server-id=3307
    log-bin=mysq-slave-bin
    read_only=1
    
    #[clinet]代表配置MySQL客户端连接时的相关配置
    default-character-set:设置MySQL客户端连接时的字符集
    
    #[mysqld]代表MySQL服务端的相关配置
    character-set-server:设置服务端的字符集
    autocommit:开启自动提交
    server-id:表示server编号,编号要唯一
    log-bin:表示启用二进制日志,并设置日志文件的名字,以防Slave作为其他数据库实例的Master时使用
    read_only:表示只读
    
    #配置完毕后重启容器
    docker restart mysql_slave
    
  4. 分别进入主从MySQL数据库进行授权远程登录访问

    #进入容器
    docker exec -it mysql_master bash
    docker exec -it mysql_slave bash
    
    #进入容器后输入MySQL客户端连接命令,其中-u是数据库用户名,默认为root,-p是数据库密码
    mysql -uroot -p123456
    
    #修改远程用户访问密码,其中root@'%'中的root表示用户名,%表示任意ip地址
    alter user 'root'@'%' identified with mysql_native_password by '123456';
    
    #授权访问,其中*.* 的第一个*表示所有数据库名,第二个*表示所有的数据库表
    grant all privileges on *.* to 'root'@'%';
    
    #刷新权限
    flush privileges;
    
  5. 在主服务器上创建复制数据的账号并授权

    #进入容器
    docker exec -it mysql_master bash
    
    #进入容器后输入MySQL客户端连接命令,其中-u是数据库用户名,默认为root,-p是数据库密码
    mysql -uroot -p123456
    
    #在主服务器上创建复制数据的账号和本地访问密码
    create user 'copy'@'%' identified by '123456';
    
    #修改复制数据账号的远程访问密码,其中copy@'%'中的copy表示用户名,%表示任意ip地址
    alter user 'copy'@'%' identified with mysql_native_password by '123456';
    
    #在主服务器上对复制数据账号授权
    grant replication slave on *.* to 'copy'@'%';
    
    #刷新权限
    flush privileges;
    
    #在主服务器上执行命令,查看主服务器状态
    show master status;
    
    #如果主服务状态不是初始状态,建议重置状态,初始状态:日志文件是:mysql-bin.000001,偏移坐标:156
    reset master;
    
  6. 在从服务器设置主库

    #进入容器
    docker exec -it mysql_slave bash
    
    #进入容器后输入MySQL客户端连接命令,其中-u是数据库用户名,默认为root,-p是数据库密码
    mysql -uroot -p123456
    
    #查看从服务器状态
    show slave status;
    
    #如果从服务器不是初始状态,重置一下(什么也没有就是初始状态)
    stop slave;   --停止复制
    reset slave;  --重置从机器状态
    
    #设置master
    change master to master_host='192.168.177.128',master_user='copy',master_port=3306,master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=156;
    
    #执行开始复制命令
    start slave;
    
  7. 检查从服务器复制功能状态

    #如果Slave_IO_Running和Slave_SQL_Running均为YES,则表示主从关系正常
    show slave status \G
    

    在这里插入图片描述

  8. 在主服务器上创建数据库、表、数据,然后在从服务器上查看是否已经复制

    在主库执行以下数据库语句:

    #创建一个数据库
    create database docker;
    
    #切换数据库
    use docker;
    
    #创建一张表
    create table t_user(
        id int primary key,
    	user_name varchar(20),
        passsword varchar(50)
    );
    
    #插入数据
    insert into t_user values(1,'Tom','123456');
    
    #查询表
    select * from t_user;
    

    在从库执行以下数据库语句:

    #查看数据库是否复制
    show databases;
    
    #切换数据库
    use docker;
    
    #查看所有表
    show tables;
    
    #查询表
    select * from t_user;
    

12.2 Redis哨兵集群搭建

Redis也提供了一主多从架构,主库对外提供读写的操作,从库对外提供读的操作。主库把写入的数据异步复制到从机器,当主库宕机时,从库可以切成主库,保证服务的高可用,然后从库也可以做数据的容灾备份。但是当主库发生故障时,需将其中一台从库手动切换成主库,这种处理也叫冷处理(人工处理)。这种处理方式并不智能,要实现自动化处理,这就需要Sentinel哨兵实现故障自动转移。Sentinel哨兵是Redis官方提供的高可用方案,使用Sentinel哨兵可以监控多个Redis服务实例的运行情况。它的基本工作流程如下:

在这里插入图片描述

  1. Sentinel哨兵用来监视Redis的主从服务器,它会不断检查主库和从库是否正常
  2. 但是如果Sentinel挂了,也无法监控,所以需要多个哨兵组成Sentinel网络,监控同一个Redis主从集群的各个Sentinel哨兵会相互通信,组成一个分布式的Sentinel哨兵网络,互相交换彼此关于被监控Redis服务器的信息
  3. 当一个Sentinel哨兵认为被监控的Redis服务器出现故障时,它会向网络中的其它Sentinel哨兵进行确认,判断该服务器是否真的已故障
  4. 如果故障的Redis为主库,那么Sentinel哨兵网络将对故障的主库服务器进行自动故障转移,将某个Redis从库提升为新的主库,并让其它Redis从库转移到新的主库下,以此来让整个主从模式重新回到正常状态
  5. 待出现故障旧的主库重新启动上线时,Sentinel哨兵会让它变成一个Redis从库,并挂到新的Redis主库,所以哨兵是自动实现故障转移,不需要人工干预,是一种高可用的集群方案

通过了解,我们得知,Redis的哨兵集群模式=Redis主从复制+哨兵集群组成,所以实际上,我们如果要搭建哨兵集群就要分为两个方面来搭建,首先是搭建Redis主从复制部分,我们以一主两从伪集群的方式来搭建。其次是Sentinel哨兵集群部分,我们以三个哨兵、伪集群的方式来搭建,其中主库的端口号为6379,两个从库的端口号分别为6380,6381。三个Sentinel的端口号分别为26379,26380,26381。这里以host网络模式进行演示

  1. 分别创建Redis主库和从库的数据持久化目录和配置文件目录

    #创建三台Redis的数据持久化目录
    mkdir -p /mydata/redis/6379/data /mydata/redis/6380/data /mydata/redis/6381/data
    
    #创建三台Redis的配置文件目录
    mkdir -p /mydata/redis/6379/config /mydata/redis/6380/config /mydata/redis/6381/config
    
  2. 将核心配置文件redis.conf分别放入三台Redis的配置文件目录,并修改

    #在/mydata/redis/6379/config放入redis.conf配置文件并修改如下内容
    protected-mode no (改为不保护,否则远程访问不了)
    bind 127.0.0.1 (注释掉,否则只能本机ip访问)
    requirepass 设置密码
    
    配置文件搜索SNAPSHOTTING部分,设置RDB的自动备份方式,配置格式:save <seconds> <changes>
    save 900 1
    save 300 10
    save 60 10000
    
    配置文件搜索APPEND ONLY MODE部分,开启aof
    appendonly (默认是no,改成yes即开启了aof持久化)
    
    #在/mydata/redis/6380/config和/mydata/redis/6381/config放入redis.conf配置文件并修改如下内容
    protected-mode no (改为不保护,否则远程访问不了)
    bind 127.0.0.1 (注释掉,否则只能本机ip访问)
    requirepass 设置密码
    port 6380/6381 (修改从库端口号,否则在使用host模式时端口会冲突)
    
    配置文件搜索SNAPSHOTTING部分,设置RDB的自动备份方式,配置格式:save <seconds> <changes>
    save 900 1
    save 300 10
    save 60 10000
    
    配置文件搜索APPEND ONLY MODE部分,开启aof
    appendonly yes (默认是no,改成yes即开启了aof持久化)
    
    配置文件搜索REPLICATION部分,配置主从复制
    replicaof 192.168.177.128 6379 (主库的地址)
    masterauth 主库密码
    
  3. 创建Redis主库和两个从库容器

    #创建6379容器
    docker run --name redis6379 --restart=always \
    -v /mydata/redis/6379/data:/data \
    -v /mydata/redis/6379/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建6380容器
    docker run --name redis6380 --restart=always \
    -v /mydata/redis/6380/data:/data \
    -v /mydata/redis/6380/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建6381容器
    docker run --name redis6381 --restart=always \
    -v /mydata/redis/6381/data:/data \
    -v /mydata/redis/6381/config/:/usr/local/etc/redis \
    -d --network=host  \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
  4. 查看三个redis服务的角色

    #登录6379主库redis客户端
    docker exec -it redis6379 redis-cli -p 6379
    #输入密码
    auth 123456
    #查看角色
    info replication
    
    #登录6380从库redis客户端
    docker exec -it redis6380 redis-cli -p 6380
    #输入密码
    auth 123456
    #查看角色
    info replication
    
    #登录6381从库redis客户端
    docker exec -it redis6381 redis-cli -p 6381
    #输入密码
    auth 123456
    #查看角色
    info replication
    

    在这里插入图片描述

    在这里插入图片描述

  5. 测试主从复制,至此主从复制搭建完毕

    #在6379主库写入数据
    set test 1
    
    #在6380从库查看数据
    get test
    

    在这里插入图片描述

    在这里插入图片描述

  6. 开始搭建哨兵,首先将sentinel.conf哨兵配置文件分别放入三台Redis的配置文件目录,并修改

    #在/mydata/redis/6379/config放入sentinel.conf配置文件并修改如下内容
    sentinel monitor mymaster 192.168.177.128 6379 2(设置监控的主库地址,其中末尾的2代表的是投票数)
    sentinel auth-pass mymaster 123456(由于redis设置过密码,所以这里需要配置下)
    
    #在/mydata/redis/6380/config放入sentinel.conf配置文件并修改如下内容
    port 26380 (修改端口号,默认为26379)
    sentinel monitor mymaster 192.168.177.128 6379 2(设置监控的主库地址,其中末尾的2代表的是投票数)
    sentinel auth-pass mymaster 123456(由于redis设置过密码,所以这里需要配置下)
    
    #在/mydata/redis/6381/config放入sentinel.conf配置文件并修改如下内容
    port 26381 (修改端口号,默认为26379)
    sentinel monitor mymaster 192.168.177.128 6379 2(设置监控的主库地址,其中末尾的2代表的是投票数)
    sentinel auth-pass mymaster 123456(由于redis设置过密码,所以这里需要配置下)
    
  7. 创建三个哨兵容器

    哨兵的创建仍然是使用redis镜像,只是在镜像启动后执行的脚本改为redis-sentinel,指向的配置文件也指向sentinel.conf

    #创建26379哨兵容器
    docker run --name sentinel26379 --restart=always \
    -v /mydata/redis/6379/config/:/usr/local/etc/redis \
    -d --network=host  \
    redis:7.0.5 redis-sentinel /usr/local/etc/redis/sentinel.conf
    
    #创建26380哨兵容器
    docker run --name sentinel26380 --restart=always \
    -v /mydata/redis/6380/config/:/usr/local/etc/redis \
    -d --network=host  \
    redis:7.0.5 redis-sentinel /usr/local/etc/redis/sentinel.conf
    
    #创建26381哨兵容器
    docker run --name sentinel26381 --restart=always \
    -v /mydata/redis/6381/config/:/usr/local/etc/redis \
    -d --network=host  \
    redis:7.0.5 redis-sentinel /usr/local/etc/redis/sentinel.conf
    
  8. 进入任意一个哨兵容器,查看它监控的Redis主从容器和其他哨兵容器

    #进入容器命令行
    docker exec -it sentinel26379 redis-cli -p 26379
    
    #查看主容器
    sentinel master mymaster
    
    #查看从容器
    sentinel slaves mymaster
    
    #查看其他哨兵容器
    sentinel sentinels mymaster
    
  9. 停止Redis主库,观察主库重新选举日志

    #停止主库
    docker stop redis6379
    
    #在哨兵日志中查看选举切换日志,默认为30秒
    docker logs sentinel26379
    

    在这里插入图片描述

  10. 查看6380和6381容器的角色变化

    #登录6380客户端
    docker exec -it redis6380 redis-cli -p 6380
    #输入密码
    auth 123456
    #查看角色
    info replication
    
    #登录6381客户端
    docker exec -it redis6381 redis-cli -p 6381
    #输入密码
    auth 123456
    #查看角色
    info replication
    

    在这里插入图片描述

    在这里插入图片描述

  11. 重新启动6379容器,不会把6379容器切换成主库,而是作为从库

    #重启6379容器
    docker start redis6379
    
    #登录6379客户端
    docker exec -it redis6379 redis-cli -p 6379
    
    #输入密码
    auth 123456
    
    #查看角色
    info replication
    

    在这里插入图片描述

12.3 Redis分片集群搭建

使用哨兵模式可以达到Redis高可用目的,但是此时每个Redis库存有集群中的所有数据,从而导致集群的总数据存储量受限于可用存储内存最小的节点,形成了木桶效应。因此在Redis3.0版本开始提供了Redis Cluster集群功能,集群的特点在于拥有和单机实例一样的性能,同时提供了数据分片存储的支持,以及在主Redis数据库故障后自动故障转移恢复的支持。 哨兵集群和分片集群是两个独立的功能,当不需要对数据进行分片使用哨兵就够了,如果要进行水平扩容Redis Cluster是一个比较好的方式,它的基本拓扑结构如下:

在这里插入图片描述

  • Redis的数据分区:

    一个Redis Cluster由多个Redis节点构成,不同节点组的Redis数据没有交集,也就是每个组节组对应数据的一个分片,节点组内部分为主备两类节点,对应Master和Slave节点,两者数据准实时一致,通过异步化的主备复制机制来保证。一个节点组有且只有一个Master节点,同时可以有0个到多个Slave节点,在这个节点组中只有Master节点对用户提供些服务,Slave节点用于备份数据。

  • Redis Cluster数据分片概念:

    Redis Cluster提出哈希槽slot的概念,来实现数据分片,每个存入Redis的key通过CRC16函数(一种哈希函数)后对16384取模来决定将key放置哪个槽,集群的每个节点负责一部分哈希槽,key的槽位计算公式为:slot number = crc16(key) % 16384,举个例子说明:比如当前集群有3个节点组,那么节点组 A 包含 0 到 5500号哈希槽,节点组 B 包含5501 到 11000 号哈希槽,节点组 C 包含11001 到 16383号哈希槽,假设我们现在有一个key想存入分片集群,那么会首先进行计算:235 = crc16 (k1) % 16384,此时结果为235,那么就会将数据存入节点组A。

  • 哈希槽的不变性:

    哈希槽的个数固定是2的14次方,也就是16384,它是不可改变的。这种结构很容易添加或者删除节点组,比如:如果我想新添加一个节点组D, 我需要从节点组 A, B, C中分得部分哈希槽到D上。如果我想移除节点A,需要将A中的槽移到B和C节点组上,然后将没有任何槽的A节点组从集群中移除即可。由于从一个节点组将哈希槽移动到另一个节点组并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。这非常方便的进行了内存空间的横向扩容,由于哈希槽恒定不变,当节点组的数量增加,每个节点组所分配的哈希槽范围就缩小了,能够存储当前哈希槽范围内的数据变相的就增多了。

  • 高可用性:

    通常情况下,一个主节点下至少部署一个从节点,比如有A,B,C三个节点的集群,在没有从节点复制模型的情况下,如果节点B故障了,那么整个集群就会因为缺少5501-11000这个范围的哈希槽而不可用。如果在集群创建的时候我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个Master节点和三个Slave节点组成,这样在节点B故障后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用,不过当B和B1都失败后,集群是不可用的。

我们以三主三从伪集群的方式来搭建Redis分片集群。其中3个节点组的Master节点端口号分别为:7000、7001、7002。Slave节点端口号分别为:7003、7004、7005。这里以host网络模式进行演示

  1. 分别创建Redis各节点的数据持久化目录和配置文件目录

    #创建六台Redis的数据持久化目录
    mkdir -p /mydata/redis/7000/data /mydata/redis/7001/data /mydata/redis/7002/data /mydata/redis/7003/data /mydata/redis/7004/data /mydata/redis/7005/data
    
    #创建六台Redis的配置文件目录
    mkdir -p /mydata/redis/7000/config /mydata/redis/7001/config /mydata/redis/7002/config /mydata/redis/7003/config /mydata/redis/7004/config /mydata/redis/7005/config
    
  2. 将核心配置文件redis.conf分别放入六台Redis的配置文件目录,并修改

    port 7000/7001/7002/7003/7004/7005 (端口号修改)
    protected-mode no (改为不保护,否则远程访问不了)
    bind 127.0.0.1 (注释掉,否则只能本机ip访问)
    cluster-enabled yes (启用集群模式)
    cluster-config-file nodes-7000.conf/nodes-7001.conf/nodes-7002.conf/nodes-7003.conf/nodes-7004.conf/nodes-7005.conf(它由Redis节点自行创建的集群文件,确保没有重叠的集群配置文件名)
    cluster-node-timeout 15000 (集群的故障超时时间)
    
    配置文件搜索SNAPSHOTTING部分,设置RDB的自动备份方式,配置格式:save <seconds> <changes>
    save 900 1
    save 300 10
    save 60 10000
    
    配置文件搜索APPEND ONLY MODE部分,开启aof
    appendonly yes (默认是no,改成yes即开启了aof持久化)
    
  3. 启动六台Redis容器

    #创建7000容器
    docker run --name redis7000 --restart=always \
    -v /mydata/redis/7000/data:/data \
    -v /mydata/redis/7000/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建7001容器
    docker run --name redis7001 --restart=always \
    -v /mydata/redis/7001/data:/data \
    -v /mydata/redis/7001/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建7002容器
    docker run --name redis7002 --restart=always \
    -v /mydata/redis/7002/data:/data \
    -v /mydata/redis/7002/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建7003容器
    docker run --name redis7003 --restart=always \
    -v /mydata/redis/7003/data:/data \
    -v /mydata/redis/7003/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建7004容器
    docker run --name redis7004 --restart=always \
    -v /mydata/redis/7004/data:/data \
    -v /mydata/redis/7004/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
    #创建7005容器
    docker run --name redis7005 --restart=always \
    -v /mydata/redis/7005/data:/data \
    -v /mydata/redis/7005/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    
  4. 执行命令配置集群,进入容器执行集群配置命令,任意一台即可,这里以端口号为7000的为例

    docker exec -it redis7000 redis-cli --cluster create \
    192.168.177.128:7000 192.168.177.128:7001 \
    192.168.177.128:7002 192.168.177.128:7003 \
    192.168.177.128:7004 192.168.177.128:7005 \
    --cluster-replicas 1
    
    #以上参数解释
    --cluster create 代表指定创建集群的redis容器
    --cluster-replicas 1 代表副本的数量为1,即6台服务器最后会变为3主3从
    
  5. 配置信息确认

    redis-cli会输出一份预想中的配置信息需要确认,包含分配的slots哈希槽,可以输入yes,redis-cli就会将这份配置应用到集群当中,让各个节点开始互相通讯。当显示All 16384 slots covered 这表示集群中的16384个槽都有至少一个主节点在处理,每个主下面有一个备用的从节点,集群运作正常

    在这里插入图片描述

  6. 查看集群信息

    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #查看集群信息
    cluster info
    
    #查看集群节点
    cluster nodes
    
    #查看槽位分配
    docker exec -it redis7000 redis-cli --cluster check 192.168.177.128:7000
    

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  7. 测试集群

    #使用redis-cli连接任意一台Redis,这里以7000为例
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #分别执行以下命令观察结果
    set a a
    
    set b b
    
    set c c
    

    在这里插入图片描述

    当对某个key进行存储时,会自动进行哈希槽计算得出对应的slot槽位,然后重定向给对应的Master节点进行处理。同样的,当对某个key进行数据访问的时候,也会先计算key所对应的slot槽位,然后重定向到对应的Mater节点进行访问

  8. 故障测试

    停掉一个Master主节点后,该节点组下的Slave节点会提升为Master主节点,再次启动原来的Master主节点,它将沦为Slave从节点,这里我们把7000停掉来进行测试

    #停止Redis7000容器
    docker stop redis7000
    
    #再次启动Redis7000容器
    docker start redis7000
    
    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #查看集群节点,7000成了slave,它的master是7004
    cluster nodes
    

    在这里插入图片描述

  9. 密码的设置

    在搭建分片集群时,不要在一开始就为每个Redis节点设置密码,而是需要在集群搭建完毕之后,再统一为整个集群设置访问密码

    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #设置密码
    config set requirepass '123456'
    
    #设置从节点连接主节点的密码
    config set masterauth '123456'
    
    #退出客户端,重新连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #此时需要输入密码
    auth 123456
    
  10. 添加新的主节点,添加一个7006的新节点,加入到整个集群中

    创建数据持久化目录和配置文件

    #创建数据持久化目录和配置文件目录
    mkdir -p /mydata/redis/7006/data
    mkdir -p /mydata/redis/7006/config
    
    #拷贝一份7000节点的redis.conf文件到/mydata/redis/7006/config目录下
    cp /mydata/redis/7000/config/redis.conf /mydata/redis/7006/config/redis.conf
    
    #修改/mydata/redis/7006/config/redis.conf如下内容
    vim /mydata/redis/7006/config/redis.conf
    
    port 7006 (端口号修改)
    cluster-config-file nodes-7006.conf(它由Redis节点自行创建的集群文件,确保没有重叠的集群配置文件名)
    

    启动新的容器

    #启动7006容器
    docker run --name redis7006 --restart=always \
    -v /mydata/redis/7006/data:/data \
    -v /mydata/redis/7006/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    

    加入集群

    #使用redis-cli连接任意一台节点客户端,这里以7000为例,添加7006节点到集群,第一个参数是要添加的节点,第二个参数是集群中任意一个主节点
    docker exec -it redis7000 redis-cli --cluster add-node 192.168.177.128:7006 192.168.177.128:7002
    

    查看集群情况

    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #查看集群信息
    cluster info
    
    #查看集群节点
    cluster nodes
    
    #查看槽位分配
    docker exec -it redis7000 redis-cli --cluster check 192.168.177.128:7006
    

    新节点7006现在已经加入了集群,成为集群的一份子,可以对客户端的命令请求进行重定向,且新添加的节点是一个主节点,但是和其他主节点相比:1.新节点7006没有包含任何哈希槽;2.集群需要将某个从节点升级为新的主节点时,这个新节点不会被选中;3.将集群中的某些哈希槽移动到新节点中,新节点才会成为真正的主节点

  11. 集群重新分片

    重新分片操作本质上就是将某些节点上的哈希槽移动到另外一些节点上面,和创建集群一样, 重新分片也可以使用 redis-cli 程序来执行,执行以下命令可以开始一次重新分片操作

    #192.168.72.128:7002是集群中任意一个主节点
    docker exec -it redis7000 redis-cli --cluster reshard 192.168.177.128:7002
    

    提示移动多少个槽( 从0 到 16384)?我们原来是3个Master,后面添加一个M,aster 7006,就是四个Master了。16384个槽4个节点分,是4096个,我们这里输入4096表示想移动的槽的个数

    在这里插入图片描述

    提示要把移动的槽给哪个节点,要求输入节点ID,7006节点的ID是414ab04c83bc4d9fd7837b004fc5cfe6f330fffb拷贝粘贴过来

    在这里插入图片描述

    提示从哪些节点分哈希槽过来,输入all表示全部节点

    在这里插入图片描述

    提示是否确定执行拟定的重新分片计划 ,输入yes

    在这里插入图片描述

    在重新分片结束后可检查集群情况,至此完成了对进群的重新分片操作

    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #查看集群信息
    cluster info
    
    #查看集群节点
    cluster nodes
    
    #查看槽位分配
    docker exec -it redis7000 redis-cli --cluster check 192.168.177.128:7006
    
  12. 添加新的从节点,添加一个7007的新节点,加入到整个集群中,并作为7006主节点的从节点

    创建数据持久化目录和配置文件

    #创建数据持久化目录和配置文件目录
    mkdir -p /mydata/redis/7007/data
    mkdir -p /mydata/redis/7007/config
    
    #拷贝一份7000节点的redis.conf文件到/mydata/redis/7007/config目录下
    cp /mydata/redis/7000/config/redis.conf /mydata/redis/7007/config/redis.conf
    
    #修改/mydata/redis/7007/config/redis.conf如下内容
    vim /mydata/redis/7007/config/redis.conf
    
    port 7007 (端口号修改)
    cluster-config-file nodes-7007.conf(它由Redis节点自行创建的集群文件,确保没有重叠的集群配置文件名)
    

    启动新的节点

    #启动7007容器
    docker run --name redis7007 --restart=always \
    -v /mydata/redis/7007/data:/data \
    -v /mydata/redis/7007/config/:/usr/local/etc/redis \
    -d --network=host \
    redis:7.0.5 redis-server /usr/local/etc/redis/redis.conf
    

    加入集群,并设置挂载主节点

    #使用redis-cli连接任意一台节点客户端,这里以7000为例,添加7006节点到集群,第一个参数是要添加的节点,第二个参数是集群中任意一个主节点,414ab04c83bc4d9fd7837b004fc5cfe6f330fffb是要挂载的主节点7006的节点ID
    docker exec -it redis7000 redis-cli --cluster add-node 192.168.177.128:7007 192.168.177.128:7002 --cluster-slave --cluster-master-id 414ab04c83bc4d9fd7837b004fc5cfe6f330fffb
    

    检查集群情况

    #使用redis-cli连接任意一台Redis,这里以7000为例,-c表示以集群模式连接
    docker exec -it redis7000 redis-cli -c -p 7000
    
    #查看集群信息
    cluster info
    
    #查看集群节点
    cluster nodes
    
    #查看槽位分配
    docker exec -it redis7000 redis-cli --cluster check 192.168.177.128:7007
    

12.4 RabbitMQ默认集群

RabbitMQ的集群分两种模式,一种是默认集群模式,一种是镜像集群模式。默认集群模式也叫普通集群模式或者内置集群模式,在RabbitMQ集群中所有的节点被归为两类:磁盘节点内存节点,磁盘节点会把集群的所有信息(比如交换机、绑定关系、队列等信息)持久化到磁盘中,而内存节点只会将这些信息保存到内存中,如果该节点宕机或重启,内存节点的数据会全部丢失,而磁盘节点的数据不会丢失。RabbitMQ默认集群模式,只会把交换机、队列、虚拟主机等元数据信息在各个节点同步,而具体队列中的消息内容不会在各个节点中同步,如下图:

在这里插入图片描述

  1. 当用户访问其中任何一个RabbitMQ节点时,查询到的队列、交换机、虚拟主机、绑定关系等信息都是相同的
  2. 集群中队列的具体信息数据只在队列的拥有者节点保存,其他节点只知道队列的元数据和指向该节点的指针,所以当消费者消费时连接了另一个节点并访问不属于该节点队列的消息时,会通过元数据定位到队列数据所在的节点然后访问队列拉取数据发送给消费者
  3. 默认集群可以提高RabbitMQ的消息吞吐能力、节省存储空间、提高性能,如果消息需要复制到集群中每个节点,网络开销不可避免,持久化消息还需要写磁盘,占用磁盘空间等
  4. 默认集群无法保证高可用,因为一旦一个RabbitMQ节点挂了,消息就没法访问了,如果当前节点是磁盘节点(做了持久化),那么等 RabbitMQ 实例恢复后,就可以继续访问了。如果当前节点是内存节点没做持久化,那么消息就丢了
  5. 默认集群要求至少有一个磁盘节点,其他节点可以是内存节点。当节点加入或者离开集群时,必须要将变更通知到至少一个磁盘节点。如果集群中唯一的一个磁盘节点挂掉的话,集群仍然可以保持运行,但是无法进行其他操作,直到磁盘节点恢复。为了确保集群信息的可靠性,或者在不确定使用磁盘节点还是内存节点的时候,建议直接使用磁盘节点

接下来我们就在同一宿主机中通过Docker搭建3个节点的RabbitMQ的默认集群,并全部采用磁盘节点。端口分别为5672、5673、5674。需要值得注意的是,在同一宿主机创建RabbitMQ集群,不能使用Docker默认的bridger网络模式,默认的bridger网络模式只支持暴露端口通过ip和端口号进行通讯,而RabbitMQ节点则是通过hostname节点名来加入集群,所以这里我们应当使用自定义网络模式,从而让各个节点可以通过节点名来进行访问(这一点我们在搭建单体RabbitMQ时也说明了此参数用于来指定节点名,从而才能进行持久化)

  1. 创建自定义网络

    docker network create rabbit_network
    
  2. 创建第5672、5673、5674节点容器

    #创建5672节点
    docker run --name my_rabbitmq1 --restart=always \
    --hostname=rabbit1 \
    --network rabbit_network \
    -v /mydata/rabbitmq-cluster:/var/lib/rabbitmq \
    -p 5672:5672 \
    -p 15672:15672 \
    -e RABBITMQ_DEFAULT_USER=admin \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    -d rabbitmq:management
    
    #创建5673节点
    docker run --name my_rabbitmq2 --restart=always \
    --hostname=rabbit2 \
    --network rabbit_network \
    -v /mydata/rabbitmq-cluster:/var/lib/rabbitmq \
    -p 5673:5672 \
    -p 15673:15672 \
    -e RABBITMQ_DEFAULT_USER=admin \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    -d rabbitmq:management
    
    #创建5674节点
    docker run --name my_rabbitmq3 --restart=always \
    --hostname=rabbit3 \
    --network rabbit_network \
    -v /mydata/rabbitmq-cluster:/var/lib/rabbitmq \
    -p 5674:5672 \
    -p 15674:15672 \
    -e RABBITMQ_DEFAULT_USER=admin \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    -d rabbitmq:management
    
    #这里有几点需要注意:
    --hostname (必须要指定)
    -v /mydata/rabbitmq-cluster:/var/lib/rabbitmq(三个挂载的目录都是一致的,这是因为RabbitMQ底层是通过 Erlang语言来实现的,所以会使用Erlang系统连接RabbitMQ节点,在连接过程中需要正确的Erlang Cookie和节点名称,Erlang节点通过交换Erlang Cookie以获得认证,erlang.cookie是erlang实现分布式的必要文件,erlang分布式的每个节点上要保持相同的erlang.cookie文件)
    
  3. 分别进入5673、5674节点容器,加入5672节点形成集群

    #将第5673节点加入到集群中
    docker exec -it my_rabbitmq2 bash
    rabbitmqctl stop_app
    rabbitmqctl reset
    rabbitmqctl join_cluster rabbit@rabbit1
    rabbitmqctl start_app
    
    #将第5674节点加入到集群中
    docker exec -it my_rabbitmq3 bash
    rabbitmqctl stop_app
    rabbitmqctl reset
    rabbitmqctl join_cluster rabbit@rabbit1
    rabbitmqctl start_app
    
    #以上命令中:
    rabbitmqctl stop_app (停止RabbitMQ应用)
    rabbitmqctl reset (用于清空节点的元数据和状态信息,以便于同步集群中的数据信息)
    rabbitmqctl start_app(启动RabbitMQ应用)
    rabbitmqctl join_cluster rabbit@rabbit1(加入到指定节点中形成集群,默认为磁盘节点,如果希望作为内存节点加入,可以使用 join_cluster --ram rabbit@rabbit1)
    
  4. 通过Web控制台页面查看集群状况

    在Windows浏览器中,访问如下地址,用户名admin,密码123456,查看集群情况,3个节点信息都出现就证明默认集群搭建完毕

    http://192.168.177.128:15672
    

    在这里插入图片描述

  5. 测试集群消息发送

    前面我们在创建RabbitMQ容器的时候分别都创建了Web控制台,其中15672对应5672,15673对应5673,15674对应5674。接下来,我们在15672中创建一个test队列

    在这里插入图片描述

    然后创建交换机testExchange并绑定关系

    在这里插入图片描述

    在这里插入图片描述

    分别查看15673、15674也有了对应的队列和交换机的元数据信息

    在这里插入图片描述

    在这里插入图片描述

    向15672控制台的交互机发送一条消息test message

    在这里插入图片描述

    每个节点的test队列都能收到消息

    在这里插入图片描述

    在这里插入图片描述

    我们可以在任何节点消费此消息,例如我们在15673控制台手动消费消息,此时由于消息是存储在5672节点的,15673控制台对应的是5673节点,并没有该消息,5673节点的RabbitMQ会主动向5672节点进行消息的拉取并消费,当消息被消费时,其他节点将不会存在该消息

    在这里插入图片描述

    在这里插入图片描述

  6. 测试集群宕机

    下面我们再通过15672控制台发送一条消息test message2

    在这里插入图片描述

    确保15673和15674都有了消息之后,将15672所对应的5672节点容器手动停止掉,再使用15673控制台进行消息的消费观察现象

    docker stop my_rabbitmq1
    

    此时消息直到5672节点恢复之前,消息都将无法被消费

    在这里插入图片描述

    默认集群中,如果每个节点都是磁盘节点的话,只能保证消息的可靠性,也就是消息不丢失。但无法保证集群的高可用

12.5 RabbitMQ镜像集群

由于默认集群无法保证高可用,所以可以采用镜像集群镜像集群基于默认集群加上一定的配置得来的。镜像集群是把所有的队列数据完全同步,包括元数据信息和消息数据信息,当然这对性能肯定会有一定影响,但能保证数据的可靠性和集群的高可用,实现镜像集群也非常简单,它是在默认集群基础之上搭建而成的

在这里插入图片描述

  1. 镜像集群只需要进入默认集群中的某一个节点容器内部执行命令就可以进行配置

    命令格式:rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]

    set_policy:设置集群策略

    -p Vhost:可选参数,针对指定vhost下的queue进行设置,可以不填,也可以默认填个’/’

    Name:策略的名称,名称可以随意拟定

    Pattern:队列的匹配模式,可以使用正则表达式,满足正则表达式的队列才会被设置上当前的策略

    Definition:定义镜像队列的规则,是json字符串,分为三个部分:ha-mode,ha-params,ha-sync-mode

    • ha-mode:指定镜像队列的模式,可选值为all/exactly/nodes,其中all表示在集群中所有的节点上进行镜像,exactly表示在指定个数的节点上进行镜像,nodes表示在指定的节点上进行镜像
    • ha-params:ha-mode中exactlynodes模式的参数,表示节点数或节点名称
    • ha-sync-mode:进行队列中消息的同步方式,有效值为automatic(自动)和manual(手动)

    Priority:可选参数,策略的优先级,可以不填

  2. 进入5672节点容器,进行配置,配置所有队列都进行镜像配置

    #进入5672节点容器内
    docker exec -it my_rabbitmq1 bash
    
    #设置策略匹配所有名称的队列都进行镜像配置
    rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
    
    #如果要配置指定名称的队列进行镜像配置,可采用策略匹配所有名称是amp开头的队列都存储在2个节点上的命令如下
    rabbitmqctl set_policy ha-amp "^amp*" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
    
    #查询策略
    rabbitmqctl list_policies
    
    #删除策略
    rabbitmqctl clear_policy 策略名
    
  3. 配置完毕之后,我们可以通过15672的控制台来查看一下情况

    交换机已经全部同步为ha-all我们指定的镜像策略

    在这里插入图片描述

    队列也已经全部同步为ha-all我们指定的镜像策略

    在这里插入图片描述

  4. 测试集群宕机

    下面我们通过15672控制台发送一条消息test message3

    在这里插入图片描述

    确保15673和15674都有了消息之后,将15672所对应的5672节点容器手动停止掉,再使用15673控制台进行消息的消费观察现象

    docker stop my_rabbitmq1
    

    此时5673和5674节点仍然可以进行消息的消费

    在这里插入图片描述

    再次重启5672节点,消息已经被消费了,队列不再有信息

    在这里插入图片描述

12.6 ElasticSearch集群

假设有一个索引有10亿个文档,占用1TB的磁盘空间,而任意一个节点都没有这样大的磁盘空间,或者单个节点处理搜索请求响应太慢了,为了解决这个问题,ElasticSearch提供了将索引划分为多份的功能,每一份就叫做一个分片。当建立一个索引的时候,可以指定想要分配的分片数量,每个分片本身也是一个功能完善并且独立的索引,这个索引可以被放置到集群中的任何节点上,有了分片,一方面允许你水平分割/扩展你的内容容量,另一方面允许你在分片之上进行分布式并行操作,从而提高性能和吞吐量。当某个分片节点出现故障,该分片上的数据应该在另一个节点上有备份,这样才能保证不会出现找不到数据的情况,因此Elasticsearch允许你创建分片的一份或者多份拷贝,这些拷贝叫做分片的副本,副本的存在提高了节点出现故障时的集群高可用性。因此,副本分片不会与主分片在同一个节点上,如下图:

在这里插入图片描述

  • 一个索引可以被分为多个分片,可以被复制0~N次,分片数和副本数可以在索引创建的时候指定,在索引创建之后,副本数可以改变,但分片数不能再改变
  • 创建索引时,可以指定主分片数,默认主分片数是5个(可以修改),默认副分片数是1个(可以修改),需要注意:
    • 主分片设置过少,比如只有一个分片,当数据增长比较快时,无法水平扩展
    • 主分片设置过多,那么单个分片容量小,导致一个节点上有太多分片,影响性能
    • 副本分片数过多,会降低集群写入性能
  • 当我们创建文档时,ElasticSearch是根据这个公式决定的:shard = hash(文档id) % 主分片数 ,为了保证文档能正确路由,主分片个数不能随便修改,因为如果主分片个数发生变化了,那么所有之前路由值都会无效,文档也找不到了
  • 每个节点都能接收请求,每个节点接收到请求后都能把该请求路由到有对应数据的其他节点上,接收原始请求的节点负责采集数据并返回给客户端

接下来,我们在一台宿主机中搭建3个节点的ElasticSearch伪集群,端口号分别为9200,9201,9202,并安装head插件查看集群情况,安装Kibana操作ElasticSearch,在安装之前需要确保单台宿主机的内存不低于2G

  1. 准备持久化目录

    我们需要自己去创建一下挂载目录,这样做的目的是为了手动更改一下目录的访问权限,在ES的官方中提到,如果我们在Docker中尝试以挂载目录的方式来持久化数据,那么需要对持久化目录的访问权限进行授权,因为Docker中的ES默认是以elasticsearch用户身份运行的,如果宿主机目录是以root用户身份创建的,那么就会导致权限不足,从而启动失败

    #在宿主机中分别创建9200、9201、9202持久化目录,设置权限为777最大权限
    mkdir -p /mydata/es-cluster/9200/plugins /mydata/es-cluster/9201/plugins /mydata/es-cluster/9202/plugins
    mkdir -p /mydata/es-cluster/9200/data /mydata/es-cluster/9201/data /mydata/es-cluster/9202/data
    
    #设置目录权限为777
    chmod -R 777 /mydata/es-cluster/
    
    #目录解释
    plugins:用于存储es的插件
    data:用于存储es的持久化数据
    
  2. 创建自定义模式的网络

    在ElasticSearch集群中,各个节点之间的通信是通过节点名字来进行通信的,为了保证可以节点名称访问具体的容器网络,这里就必须使用自定义模式网络

    docker network create es_network
    
  3. 调整vm.max_map_count大小

    vm.max_map_count参数,是一个Linux系统的调优参数,该参数可以限制一个进程可以拥有的VMA(虚拟内存区域)的数量,默认值是65530~65536这个区间。由于我们现在是在一台宿主机中搭建3个节点的ElasticSearch容器节点,ElasticSearch本身是需要占用较大的虚拟内存。为了防止出现类似如下的错误,我们需要将默认Linux中的虚拟内存的大小调大,建议是调整为默认大小的4倍

    max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
    

    查看当前vm.max_map_count的大小

    sysctl -a | grep vm.max_map_count
    

    在这里插入图片描述

    调整vm.max_map_count的大小

    #打开系统配置文件
    vim /etc/sysctl.conf
    
    #添加如下内容
    vm.max_map_count=262144
    
    #执行命令,让其生效
    sysctl -p
    

    在这里插入图片描述

  4. 分别创建3个ElasticSearch节点的Docker容器

    #9200容器
    docker run --name es1 --restart=always \
    --network es_network \
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
    -e cluster.name=es-cluster \
    -e node.name=es1 \
    -e node.master=true \
    -e network.host=0.0.0.0 \
    -e discovery.seed_hosts=es1,es2,es3 \
    -e cluster.initial_master_nodes=es1 \
    -v /mydata/es-cluster/9200/plugins:/usr/share/elasticsearch/plugins \
    -v /mydata/es-cluster/9200/data:/usr/share/elasticsearch/data \
    -p 9200:9200 \
    -p 9300:9300 \
    -d elasticsearch:7.17.6
    
    #9201容器
    docker run --name es2 --restart=always \
    --network es_network \
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
    -e cluster.name=es-cluster \
    -e node.name=es2 \
    -e node.master=true \
    -e network.host=0.0.0.0 \
    -e discovery.seed_hosts=es1,es2,es3 \
    -e cluster.initial_master_nodes=es1 \
    -v /mydata/es-cluster/9201/plugins:/usr/share/elasticsearch/plugins \
    -v /mydata/es-cluster/9201/data:/usr/share/elasticsearch/data \
    -p 9201:9200 \
    -p 9301:9300 \
    -d elasticsearch:7.17.6
    
    #9202容器
    docker run --name es3 --restart=always \
    --network es_network \
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
    -e cluster.name=es-cluster \
    -e node.name=es3 \
    -e node.master=true \
    -e network.host=0.0.0.0 \
    -e discovery.seed_hosts=es1,es2,es3 \
    -e cluster.initial_master_nodes=es1 \
    -v /mydata/es-cluster/9202/plugins:/usr/share/elasticsearch/plugins \
    -v /mydata/es-cluster/9202/data:/usr/share/elasticsearch/data \
    -p 9202:9200 \
    -p 9302:9300 \
    -d elasticsearch:7.17.6
    
    #以上环境变量解释命令解析
    -e ES_JAVA_OPTS="-Xms256m -Xmx256m":es容器默认设置的JVM内存大小是"-Xms512m -Xmx512m",我们可以指定环境变量来调整JVM的内存大小
    -e cluster.name=es-cluster:集群名字,同一个集群该值必须相同
    -e node.name=es1:该节点的名字
    -e node.master=true:该节点是否有机会成为master节点
    -e network.host=0.0.0.0:打开网络,让其他主机可访问
    -e discovery.seed_hosts=es1,es2,es3:集群中所有节点的地址列表
    -e cluster.initial_master_nodes=es1:集群初始时的候选主节点
    
  5. 查看集群启动结果

    集群启动之后,需要等待一小会儿时间,ES的启动会比较慢,确保全部启动完毕之后,可以在Windows浏览器中通过如下地址观察每个节点的情况和集群情况

    查看每个节点的启动情况
    http://192.168.177.128:9200
    http://192.168.177.128:9201
    http://192.168.177.128:9202
    
    #查看集群的情况
    http://192.168.177.128:9200/_cat/nodes
    

    在这里插入图片描述

  6. 安装elasticsearch-head插件
    elasticsearch-head 项目提供了一个直观的界面,可以很方便地查看集群、分片、数据等等。elasticsearch-head本身是一个由JS开发的前端项目,被托管在GitHub上:https://github.com/mobz/elasticsearch-head,它的最简单的安装方式是通过下载插件直接安装在谷歌浏览器上,插件被存放在源码中的crx目录中,直接下载即可,下载后的文件为es-head.crx,将此文件后缀改为.zip,即es-head.zip,然后就可以解压此文件了。

    紧接着在谷歌浏览器中选择“更多工具”–“扩展程序”

    在这里插入图片描述

    确保右上角的开发者模式处于“打开状态”,然后点击“加载已解压的扩展程序”,选择我们前面解压的插件目录即安装完毕

    在这里插入图片描述

    在浏览器中点击 elasticsearch-head 插件打开 head 界面,并连接 http://192.168.177.181:9200/

  7. 访问elasticsearch-head页面

    插件安装完毕后,点击谷歌浏览器扩展程序图片,即可看到elasticsearch-head插件,点击插件即可进入页面

    在这里插入图片描述

    在页面中填写如下地址连接ES集群

    http://192.168.177.128/9200
    

    在这里插入图片描述

    标有★符号的是Master主节点,标有●的是从节点

    被粗实体边框包裹的分片代表的是主分片,被细实体边框包裹的分片代表的是副本分片,数字代表的是当前分片编号

  8. 安装Kibana

    在集群中,安装Kibana有少许不同,首先要确保Kibana也处于ES集群的自定义网络中,其次在配置连接的时候我们需要指定3个节点的地址

    docker run --name my_kibana --restart=always \
    --network es_network \
    -e ELASTICSEARCH_HOSTS='["http://es1:9200","http://es2:9200","http://es3:9200"]' \
    -p 5601:5601 \
    -d kibana:7.17.6
    
  9. 访问Kibana

    在Windows浏览器中输入一下地址对Kibana的可视化界面进行访问

    http://192.168.177.128:5601/app/kibana
    
  10. 测试分片

    <!-- 创建索引,命名为phone-->
    PUT /phone
    {
      "settings": {
        "number_of_shards": 3, 
        "number_of_replicas": 1
      }
    }
    
    /* 
    以上参数解释:
    number_of_shards:分片数量,默认值是5
    number_of_replicas:副本数量,默认值是1
    我们有三个节点,可以在每个节点上都创建一个主分片,每个分片都创建一个副本分片
    */
    

    在这里插入图片描述

    es1节点为主节点,其中2号分片为主分片,1号分片为副本分片

    es2节点为从节点,其中的0号分片为主分片,2号分片为副本分片

    es3节点为从节点,其中的1号分片为主分片,0号分片为副本分片

    ES集群会保证主分片和副本分片不会在同一个节点中,主分片可以在同一个节点中

  11. 测试集群宕机

    我们停掉es1节点,观察情况

    docker stop es1
    

    在这里插入图片描述

    通过Head插件观察集群情况,我们发现原本es1节点是主节点,宕机之后es2被选举为了新的主节点

    原本es1节点中中2号分片是主分片,此时宕机之后,将es2节点中的2号副本分片提升为了主分片

    同时健康值变为yellow黄色(集群本身是Green绿色,表示健康状态),这种情况代表的是亚健康状态,指主分4.片全部可用,部分副本分片不可用

    如果只有1个主分片不设置副本分片的话,有主分片所在的节点宕机之后,整个集群会出现Red红色,表示不健康状态,指的就是部分主分片不可用了

    然后我们恢复es1节点,观察情况

    docker start es1
    

    在这里插入图片描述

    通过Head插件观察集群情况,我们发现原本es1节点现在是从节点

    es1节点中的两个分片现在都是副本分片,集群情况变为Green绿色,表示健康状态

    集群目前满足主分片和副本分片不在同一个节点中,允许多个主分片在同一个节点,在一些特定情况,如果不满足这个情况,ES集群内部会重新分片

  12. 安装ik分词器

    前面单机模式中我们已经了解过ik分词器,它是为了对中文分词的支持,本例中我们为3台节点分别也安装一下ik分词器

    #分别在各节点plugins目录下创建ik分词器目录
    mkdir /mydata/es-cluster/9200/plugins/ik /mydata/es-cluster/9201/plugins/ik /mydata/es-cluster/9202/plugins/ik
    
    #将之前单机版/mydata/es/plugins/ik目录下的elasticsearch-analysis-ik-7.17.6.zip分别拷贝到三个节点ik目录中
    cp /mydata/es/plugins/ik/elasticsearch-analysis-ik-7.17.6.zip /mydata/es-cluster/9200/plugins/ik
    
    cp /mydata/es/plugins/ik/elasticsearch-analysis-ik-7.17.6.zip /mydata/es-cluster/9201/plugins/ik
    
    cp /mydata/es/plugins/ik/elasticsearch-analysis-ik-7.17.6.zip /mydata/es-cluster/9202/plugins/ik
    
    #分别并解压
    cd /mydata/es-cluster/9200/plugins/ik
    unzip elasticsearch-analysis-ik-7.17.6.zip
    
    cd /mydata/es-cluster/9201/plugins/ik
    unzip elasticsearch-analysis-ik-7.17.6.zip
    
    cd /mydata/es-cluster/9202/plugins/ik
    unzip elasticsearch-analysis-ik-7.17.6.zip
    
    #重启ES容器
    docker restart es1 es2 es3
    
  13. 测试分词

    <!-- 细粒度分割策略 -->
    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text": ["中华人民共和国国歌"]
    }
    
    <!-- 粗粒度度分割策略 -->
    GET _analyze
    {
      "analyzer": "ik_smart",
      "text": ["中华人民共和国国歌"]
    }
    

13. Docker容器编排

编排功能,是复杂系统是否具有灵活可操作性的关键。特别在Docker应用场景中,编排意味着用户可以灵活地对各种容器资源实现定义和管理。例如:在基于微服务架构的应用系统一般都包含若干个微服务,每个微服务一般都会集群部署多个实例,如果每个微服务都要手动启动与停止,那么效率非常低、工作量也非常大。或是有一些微服务和应用之间有部署的先后关系,当前一个有依赖性的服务还没有完成部署时,另一个有依赖性的服务先完成了部署就会导致部署失败。那么使用 Docker Compose可以轻松、高效地管理和编排多个容器。

Docker Compose 作为Docker官方编排工具,其重要性不言而喻,它可以让用户通过编写一个简单的模板文件(yaml格式),然后使用一个命令即可创建并启动配置文件中所配置的所有服务,实现快速地创建和管理基于Docker容器的应用集群。。

Docker Compose 作为Docker官方编排工具,其重要性不言而喻,它可以让用户通过编写一个简单的模板文件(yaml格式),然后使用一个命令即可创建并启动配置文件中所配置的所有服务,实现快速地创建和管理基于Docker容器的应用集群。

13.1 Docker-Compose安装

  1. 下载Compose二进制安装包(可以使用我们下载好的):

    下载地址:https://github.com/docker/compose/releases,我们下载Linux版本

    在这里插入图片描述

  2. 上传这些二进制文件到执行路径到/usr/local/bin/目录下,并添加执行权限即可

    #进入/usr/local/bin目录
    cd /usr/local/bin
    
    #修改文件名字
    mv docker-compose-linux-x86_64 docker-compose
    
    #添加执行权
    chmod +x docker-compose
    
  3. 测试安装结果

    #打印docker-compose版本号
    docker-compose --version
    

    在这里插入图片描述

13.2 Docker-Compose模板文件

模板文件是使用Compose的核心,涉及的指令也比较多,但大部分的指令与docker命令创建或启动容器时所填写的参数类似。默认的模板文件名称为docker-compose.yml,格式为YAML格式,目前最新的版本为v3

版本v1的Compose文件结构十分简单,每个顶级元素为服务名,次级元素为服务容器的配置信息,例如:

tomcat:
	image: my_tomcat:9
	ports:
		- "8080:8080"
	volumes:
		- "/mydata/tomcat"

版本v2和版本v3扩展了Compose的语法,同时尽量保持跟旧版本的兼容,除了可以声明网络和存储外,最大的不同一个是添加了版本信息,另一个是需要将所有的服务放到services元素下面,例如我们将上面的例子改为版本v3:

version:"3"
services:
	tomcat:
		image: my_tocamt:9
		ports:
			- "8080:8080"
		volumes:
			- "/mydata/tomcat"

注意:每个服务的编排都必须通过image指令指定镜像 或 build指令通过Dockerfile构建镜像,如果使用build指令,在Dockerfile中设置的一系列指令,例如CMD,EXPOSE都会自动被执行,无需再次设置

Compose模板文件常用指令列表参考如下表格内容:(这里只列出一些我认为很常用的)

指令功能
image指定镜像名或镜像id,如果镜像在本地不存在,会尝试去拉取这个镜像
ports暴露容器的端口,可指定多个。
volumes数据卷或数据目录的挂载设置,可指定多个
build指定Dockerfile所在路径,会自动构建这个镜像,并使用这个镜像
container_name指定容器名称,默认将会使用"项目名称_服务名称_序号"的格式,如果我们自己指定了容器名,该服务将无法进行集群扩展,因为Docker不允许多个容器实例名重复
command覆盖容器启动后默认执行的命令
depends_on指定多个服务之间的依赖关系,启动时,会先启动被依赖服务
dns自定义DNS服务器,可以指定多个
dns_serach配置DNS搜索域,可以指定多个
dockerfile指定额外的编译镜像的Dockerfile文件,该指令不能跟image同时使用
entrypoint覆盖容器中默认的入口命令,也会取消镜像中指定的入口命令和默认启动命令
pid跟宿主机共享进程命名空间,打开该选项容器之间,以及容器额宿主机之间可以通过进程ID来互相访问和操作
expose指定容器暴露的端口
extends基于其他模板文件进行扩展,可以有多个yaml文件,他们之间可以有继承关系
restart指定重启策略,可以为no(不重启),always(总是),on-failure(失败时),unless-stopped(停止时)
ulimits指定容器的限制值,例如,指定最大进程数
network_mode设置网络模式,bridger、host、none、containner
networks指定加入的网络名
environment设置环境变量

13.3 Docker-Compose命令

Docker-Compose的命令与docker命令极为相似, 用法上没有太大区别,其基本格式是docker-compose 命令 参数,下面列出一些常用命令:

查看帮助命令:

docker-compose --help

效验和查看Compose文件的配置信息

docker-compose config 

创建并启动容器

docker-compose up -d --scale 服务名=数字
d表示后台运行,scale是表示对应的服务同时启动几个容器

停止容器,但不删除

docker-compose stop

启动容器

docker-compose start

停止并删除容器

docker-compose down

13.4 Docker-Compose部署

我们以之前的SpirngBoot项目部署例子来进行改造实验,目前我们需要在同一个宿主机中实现的一个MySQL数据库和两个SpirngBoot程序的实例部署:

  1. 创建用于存储yml文件的目录

    #进入到/mydata目录
    cd /mydata
    
    #创建compose目录
    mkdir compose
    
  2. 编写docker-compose.yml文件

    #在/mydata/compose目录下编写
    vim docker-compose.yml
    
    version: "3"
    services:
    	mysql:
    		container_name: my_mysql
    		image: mysql
    		privileged: true
    		restart: always
    		volumes:
    			- "/mydata/mysql/data:/var/lib/mysql"
    			- "/mydata/mysql/conf:/etc/mysql/conf.d"
    			- "/mydata/mysql/logs:/var/log/mysql"
    		ports:
    			- "3306:3306"
    		expose:
    			- "3306"
    		environment: 
    			- MYSQL_ROOT_PASSWORD=123456
    		networks:
    			- my_network
    	springboot:
    		image: springboot_jxc
    		ports:
    			- "80-81:80"
    		depends_on:
    			- mysql
    		restart: always
    		networks:
    			- my_network
    networks:
    	my_network:               
    

    以上配置文件注释版讲解:

    #指定yaml版本v3
    version: "3"
    #服务配置
    services:
    	#第一个服务mysql
    	mysql:
    		#容器名,如果指定了,就不能部署多个容器
    		container_name: my_mysql
    		#镜像名
    		image: mysql
    		#为了防止出现Centos7安全Selinux禁止了一些安全权限
    		privileged: true
    		#自动重启容器
    		restart: always
    		#数据挂载
    		volumes:
    			- "/mydata/mysql/data:/var/lib/mysql"
    			- "/mydata/mysql/conf:/etc/mysql/conf.d"
    			- "/mydata/mysql/logs:/var/log/mysql"
    		#映射端口
    		ports:
    			- "3306:3306"
    		#暴露端口
    		expose:
    			- "3306"
    		#环境配置
    		environment: 
    			- MYSQL_ROOT_PASSWORD=123456
    		#加入的自定义网络
    		networks:
    			- my_network
    	#第二个服务springboot
    	springboot:
    		#镜像名,我们之前自己通过Dockerfile制作的JXC项目的镜像
    		image: springboot_jxc
    		#映射端口,"80-81:80"这种写法会在启动多个实例的时候,自动为我们分配端口在80-81这个区间的容器映射
    		ports:
    			- "80-81:80"
    		#依赖某个服务
    		depends_on:
    			- mysql
    		#自动重启
    		restart: always
    		#加入的自定义网络
    		networks:
    			- my_network
    #自定义网络配置
    networks:
    	#自定义网络名
    	my_network:
    
  3. 启动服务

    #在/mydata/compose目录下执行
    docker-compose up -d --scale springboot=2
    
    -d:后台启动容器
    --scale:启动多个实例
    
  4. 进行访问测试

    在Windows浏览器中分别访问8081两个端口号的实例,访问如下地址,访问后如出现如下登录页面,则部署成功,登录账号:admin,登录密码:admin123

    http://192.168.177.128:80
    http://192.168.177.128:81
    

    在这里插入图片描述

    在这里插入图片描述

  5. 关闭服务,并删除容器

    #在/mydata/compose目录下执行
    docker-compose down
    

14. Docker容器管理

目前,已经有若干开源项目试图实现强大和便捷的Docker管理工具,包括Portainer、Panamax、Seagull等。本例,我们以Portainer为例为大家讲解如何安装使用Docker的容器管理工具。

Portainer是一个Docker官方推荐的轻量级的docker管理工具,它具有可视化ui界面,只需简单的TCP连接就能实现对Docker容器的全面管理和监控。官方网站为:https://portainer.io,该项目最早于2013年12月发起,主要基于HTML/JS语言实现。

14.1 Portainer安装

  1. 下载Portainer镜像

    #我们可以在Docker Hub中找到MySQL镜像
    https://hub.docker.com/r/portainer/portainer
    
    #拉取portainer镜像,默认拉取的版本是latest的
    docker pull portainer/portainer-ce
    
  2. 创建数据卷

    docker volume create portainer_data
    
  3. 启动容器

    docker run --name portainer -p 9000:9000 \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data \
    -d portainer/portainer-ce
    
    #以上命令解析
    --name:容器的名称
    
    -p:端口映射
    
    -v:目录挂载
    -v /var/run/docker.sock:/var/run/docker.sock
    将宿主机的/var/run/docker.sock目录挂载到容器中的/var/run/docker.sock目录中(该目录是docker的套接字目录,主要就是发送指令给docker的守护进程的)
    -v portainer_data:/data
    将宿主机的portainer_data数据卷挂载到容器中的/data目录(这是portainer容器默认的数据目录)
    
    -d:后台启动
    
  4. 创建管理员账号

    容器启动完毕之后,我们可以在Windows浏览器中输入以下地址访问管理平台

    http://192.168.177.128:9000
    

    在这里插入图片描述

    我们需要管理员账号的创建,如果5分钟不创建该账号,Portainner会出于安全考虑而自动关闭容器,常见完毕之后就会进入下面的页面,那么整个Portainer就安装完毕了

    在这里插入图片描述

14.2 Portainer本地连接

Portainer安装完毕之后,我们就可以开始进行使用了,Portainer支持很多种管理和监控方式,这里我们以LocalRemote两种方式为例。Local指的是管理当前安装Portainer宿主机的Docker环境,而Remote指的是管理其他宿主机的Docker环境。

  1. 访问本地Docker

    选择Local ->点击Connect即可进行连接。

    在这里插入图片描述

    连接后就会进入如下列表界面,该列表会显示出所有正处于Portainner管理的机器信息

    在这里插入图片描述

  2. 管理本地Docker

    在列表中,点击local本地机器,就可以进入当前机器的管理页面,该页面可对当前选定机器的栈,容器,镜像,数据卷,网络进行管理

    在这里插入图片描述

    在这里插入图片描述

14.3 Portainer远程连接

Portainer连接其他机器需要使用tcp协议,端口号默认为2375。我们以192.168.177.129机器node2为例来进行演示

  1. 在node2机器中修改docker.service文件,开放2375端口

    #打开node2的docker.service文件
    vim /usr/lib/systemd/system/docker.service
    
    #找到ExecStart字段修改如下
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
    
    #重启daemon
    systemctl daemon-reload
    
    #重启docker服务
    systemctl restart docker
    
  2. 添加远程机器管理

    回到Portainer页面,在左侧导航栏选择Endpoint,在打开的页面中选择Add endpoint选项

    在这里插入图片描述

    在弹出页面中选择Docker,并填写机器名remote和机器地址192.168.177.129:2375,之后点击下方Add endpoint按钮进行添加

    在这里插入图片描述

  3. 查看添加的远程机器

    添加完毕后,点击左侧导航栏选择Home,就可以看到如下页面,出现remote说明node2机器的Docker也被Portainner进行管理了

    在这里插入图片描述

    点击remote机器就可以查看node2机器中Docker的栈,容器,镜像,数据卷,网络情况了,至此我们就完成了对远程Docker机器的连接

    在这里插入图片描述

14.4 Portainner镜像管理

我们以本地node1上的Docker为例,演示Portainner对镜像的操作

  1. 进入镜像管理页面

    选择左侧导航栏的Home按钮,进入以下主机列表页面,然后选择local本地机器

    在这里插入图片描述

    之后检查左侧导航栏Home下的机器是否正确切换到LOCAL字样,我们可以选择左侧导航栏的Images或是点击当前列表页中的Images图标进入镜像管理页面

    在这里插入图片描述

    在这里插入图片描述

  2. 单个镜像删除

    我们在镜像列表页选择需要删除的惊险,点击Remove即可进行删除,例如这里我们删除hello-world:latest,如下图所示

    在这里插入图片描述

  3. 多个镜像删除

    如果我们需要删除的镜像包含多个tags标签,那么直接点解Remove删除按钮是不被允许的,例如我们这里的Tomcat镜像,有3个Tags,那么我们需要点击该镜像,进入到如下页面进行逐一删除

    在这里插入图片描述

    在这里插入图片描述

  4. 从DockerHub拉取镜像

    要拉取镜像,我需要在该页面的上方选择Registry仓库,填写要拉取的镜像,然后点击Pull the image拉取镜像

    在这里插入图片描述

  5. 从私有仓库拉取镜像

    这里我们以阿里云为例,从私有仓库拉取镜像。找到左侧导航栏,选择Registrles,然后点击Add registry添加注册仓库

    在这里插入图片描述

    登录自己的阿里云镜像仓库,地址https://cr.console.aliyun.com/cn-hangzhou/instances,找到自己的镜像仓库,查看自己仓库的信息

    在这里插入图片描述

    回到Portainner页面,填写如下仓库信息,然后添加该仓库

    在这里插入图片描述

    在这里插入图片描述

    添加完自己的镜像仓库后,点击左侧导航栏选择images,在该页面的上方选择Registry仓库,填写要拉取的镜像,然后点击Pull the image拉取镜像

    在这里插入图片描述

  6. 镜像的导出

    在镜像页面,选择需要导出的镜像,点击Export按钮,例如这里我们导出hello-world:latest,导出后浏览器会自动进行下载

    在这里插入图片描述

  7. 镜像的导入

    我们把之前导出的镜像进行导入,再导入之前,先将hello-world:latest镜像手动删除掉。然后点击Import按钮找到之前导出的镜像进行导入即可

    在这里插入图片描述

    在这里插入图片描述

  8. 使用Dockerfile构建镜像

    在镜像页面,选择Build a new image 来构建镜像,再弹出的新页面中,编写构建镜像的镜像名和Dockerfile文件

    在这里插入图片描述

    在使用Portainner进行Dockerfile构建镜像时,Portainner官方文档专门有说明,Dockerfile里的ADD COPY指令不允许使用相对路径,而应该采用URL远程资源地址进行资源的下载。这里我们制作一个包含JDK17的centos7镜像,填下内容如下

    FROM centos:7
    MAINTAINER gucaini
    ADD https://download.oracle.com/java/17/archive/jdk-17.0.6_linux-x64_bin.tar.gz /usr/local/java
    ENV JAVA_HOME=/usr/local/java/jdk-17.0.6
    ENV PATH=$JAVA_HOME/bin:$PATH
    CMD ["java","-version"]
    

    在这里插入图片描述

    填写完毕之后点击下方Build the image,进行构建,构建结果,可以通过切换到Output页面来查看

    在这里插入图片描述

    在这里插入图片描述

14.5 Portainner容器管理

我们以本地node1上的Docker为例,演示Portainner对容器的操作

  1. 进入容器管理页

    选择左侧导航栏的Containers进入容器管理页

    在这里插入图片描述

    进入容器管理页后,会出现容器的操作功能按钮以及当前宿主机中的Docker容器列表,其中容器的操作功能按钮各个功能含义如下:

    按钮键功能含义
    Start启动一个停止中的容器
    Stop停止一个正在运行的容器
    Kill与Stop的作用相似,区别在于Stop是优雅关闭容器,而Kill是强制关闭
    Restart重启一个容器
    Pause暂停一个容器中的所有进程
    Resume恢复一个容器中的所有进程
    Remove删除一个容器
    Add container添加一个新的容器

    Docker容器列表中每一个属性的作用及含义如下:

    列表属性功能含义
    Name容器名
    State容器的运行状态,Running是正在运行,Stop是停止
    Quick actions可以查看容器的日志、容器的详情、容器的运行情况、进入容器命令行
    Stack堆栈名称,这里指的是使用类似Docker-Compose容器编排来创建容器时的堆栈名称
    Image镜像名
    Created容器创建的时间
    Published Ports端口映射列表
    Ownership容器的所有权
  2. 创建一个容器

    这里以启动一个MySQL容器为例,我们在容器列表页点击Add container,首先填写容器名my_mysql,选择镜像仓库服务器DockerHub,填写镜像名mysql:latest,如下:

    在这里插入图片描述

    接下来,我们添加端口映射,选择publish a new network port,然后填写宿主机和容器的端口映射配置3306:3306,如下图:

    在这里插入图片描述

    在下方容器设置中,找到Volumes标签页配置数据挂载,点击map additional volume添加一组新的数据挂载配置项,其中Volume配置的是数据卷挂载,Bind配置的是目录挂载,这里使用Bind目录挂载,分别填下如下挂载路径

    /mydata/mysql/data:/var/lib/mysql
    /mydata/mysql/conf:/etc/mysql/conf.d
    /mydata/mysql/logs:/var/log/mysql
    

    在这里插入图片描述

    在下方容器设置中,找到Env标签页配置环境变量,点击add environment variable添加一组新的环境变量配置项,其中name配置的是环境变量的名字,value配置的是环境变量的值,填下如下内容:

    MYSQL_ROOT_PASSWORD=123456
    

    在这里插入图片描述

    接着找到Restart policy标签页配置重启策略,点击Always,设置为总是自动重启,如下图:

    在这里插入图片描述

    再接着找到Runtime & Resource标签页配置运行时资源,打开Privileged mode特权模式,如下图:

    在这里插入图片描述

    最后点击页面中的Deploy the container按钮进行容器的创建

    在这里插入图片描述

    容器创建成功后,会自动跳转到容器列表页进行展示,如下图:

    在这里插入图片描述

  3. 使用Compose容器编排来创建容器

    在Portainner中,可以非常方便的使用容器堆栈的方式来创建容器,它相当于Docker-Compose容器编排。这里仍然SpringBoot程序部署为例,在左侧导航栏中找到Stacks按钮进入堆栈列表,填写堆栈名称,以及编写docker-compose.yml文件,如下图:

    version: "3"
    services:
      mysql:
        container_name: my_mysql
        image: mysql
        privileged: true
        restart: always
        volumes:
          - "/mydata/mysql/data:/var/lib/mysql"
          - "/mydata/mysql/conf:/etc/mysql/conf.d"
          - "/mydata/mysql/logs:/var/log/mysql"
        ports:
          - "3306:3306"
        expose:
          - "3306"
        environment:
          - MYSQL_ROOT_PASSWORD=123456
        networks:
          - my_network
      springboot:
        image: springboot_jxc
        deploy:
          replicas: 2
        ports:
          - "80-81:80"
        depends_on:
          - mysql
        restart: always
        networks:
          - my_network
    networks:
      my_network:
    

在这里插入图片描述

在这里插入图片描述

编写完毕之后,点击Deploy the stack按钮进行容器的创建,如下图:

在这里插入图片描述

部署完毕之后,我们可以通过列表查看详情,至此完成了Compose的部署:

在这里插入图片描述

进行访问测试:

在Windows浏览器中分别访问8081两个端口号的实例,访问如下地址,访问后如出现如下登录页面,则部署成功,登录账号:admin,登录密码:admin123

http://192.168.177.128:80
http://192.168.177.128:81

在这里插入图片描述

  • 43
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值