一、Docker基础概念介绍
1.1、基本概念
1.1.1、应用场景:
我们写的代码会涉及到到好几个环境
开发环境
测试环境
生产环境
在不同环境跑代码,可能会导致代码“水土不服”的问题,如何解决软件跨环境问题,docker就由此而生。
1.1.2、Docker概念:
Docker是一个基于Go语言实现的开源的应用容器引擎,诞生于2013年初。Docker可以让开发者打包他们的应用以及依赖包到个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上。容器之间是完全使用沙箱机制,相互隔离并且容器性能开销极低。官网https://www.docker.com。
1.1.3、Docker架构
- 镜像(Image) : Docker镜像(Image),就相当于是一个root文件系统。如官方镜像ubuntu:6.04就包含了完整的一套Ubuntu6.04最小系统的root文件系统。
- 容器(Container):镜像(lmage) 和容器(Container)的关系,就像是面向对象程序设计中的类和对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- 客户端(Client):客户端通过命令行或者其他工具使用Docker SDK与 Docker 的守护进程通信。
- 主机(Host):一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
- Machine :一个简化Docker安装的命令行工具,通过简单的命令行即可在相应的平台上安装Docker。
- 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
1.1.4、Docker运行的基本流程
Docker是一个C/S模式的架构,后端是一个松耦合架构, 众多模块各司其职,互不影响。
运行的基本流程为:
- 用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者(比如 docker build、docker run、docker pull等命令)。
- Docker Daemon作为Docker架构中的主体部分,首先提供Docker Server的功能使其可以接受Docker Client的请求。
- Docker Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
- Job的运行过程中,当需要容器镜像时,则从Docker Registy中下载镜像,并通过镜像管理驱动Graph driver将下载镜像以Graph的形式存储。
- 当需要为Docker创建网络环境时,通过网络管理驱动Network driver创建并配置Docker容器网络环境。
- 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成。
- Libcontainer是一项独立的容器管理包,Network driver以及Exec driver都是通过Libcontainer来实现具体对容器进行的操作。
docker网络层
1.2、安装配置
1.2.1、安装
安装教程
不同系统有不同安装教程。
注意: 配置镜像源时候选择国内的镜像源
1.2.2、配置阿里云镜像加速器
用自己的账号登录获取
1.2.3、hello world 解析
docker run hello-world
执行原理
二、docker常用命令
2.1、帮助启动类命令
- 启动docker:
systemctl start docker
- 停止docker:
systemctl stop docker
- 重启docker:
systemctl restart docker
- 查看docker状态:
systemctl status docker
- 开机启动:
systemctl enable docker
- 查看docker概要信息:
docker info
- 查看docker容器状态信息:
docker stats
- 查看docker总体帮助文档:
docker -help
- 查看docker命令帮助文档:
docker 具体命令 --help
2.2、镜像类命令
2.2.1、基础命令
docker images
:列出本地主机上的镜像。- OPTIONS
-a
:列出本地所有的镜像(含历史映像层)-q
:只显示镜像ID
- 镜像列表的表头含义
3. REPOSITORY:表示镜像的仓库源。
4. TAG:镜像的标签版本号。同一个仓库源可以有多个tag版本。REPOSITORY:TAG
定义不同镜像,不写默认镜像:latest
表示最新版本。
5. IMAGE ID:镜像ID。
6. CREATED:镜像创建时间。
7. SIZE:镜像大小。
- OPTIONS
docker search
:某个xxx镜像名字。- OPTIONS
--limit
:只列出N个镜像,默认25个
- 镜像列表的表头含义
- NAME 镜像名称
- DESCRIPTION 镜像说明
- STARS 点赞数量
- OFFICIAL 是否是官方的
- AUTOMAT TED 是否是自动构建的
- OPTIONS
docker pull
:某个xxx镜像名字。- 命令格式
docker pull image
:拉取最新版本的镜像。docker pull image:TAG
:拉取指定tag版本的镜像。
- 命令格式
docker system df
:查看镜像/容器/数据卷所占的空间。docker rmi 某个xxx镜像名字/ID
:删除此镜像。- OPTIONS
-f
:强制删除。(在镜像被使用的情况下)删除多个镜像
:docker rmi 某个xxx镜像1名字/ID 某个xxx镜像2名字/ID ....
删除全部
:docker rmi $(docker images -qa)
- OPTIONS
docker save -o <压缩包文件名.tar> <镜像名>
:导出镜像到压缩包。- OPTIONS
-o
:指定导出镜像的输出文件路径和名称。-q
:静默模式,只输出导出镜像的文件路径和名称。-t
:指定要导出的镜像的名称。
- 另一个导出镜像命令:
docker save 镜像名:tag | gzip > 文件名.tar.gz
- OPTIONS
docker load -i <压缩包文件名.tar>
:导入压缩包为镜像。- OPTIONS
-t
: 自定义名称docker load -i <压缩包文件名.tar> -t 自定义名称
-i
:指定要导入的镜像文件路径和名称。-q
:静默模式,只输出导入镜像的 ID。--input-format
:指定导入镜像的格式,默认为 “docker”。
- OPTIONS
补充概念
-
Docker镜像的自动构建和非自动构建有以下区别:
- 自动构建:自动构建是指使用CI/CD工具(如Jenkins、Travis CI、GitLab CI等)或容器注册表(如Docker Hub、GitHub Container Registry等)的自动化流程,从代码仓库中获取源代码,并自动构建Docker镜像。这意味着每当代码仓库发生改变时,都会触发自动构建流程,重新构建镜像。自动构建可以根据代码库的变化自动更新并发布最新版本的镜像。
2.非自动构建:非自动构建是指手动执行构建过程,没有与代码仓库的变化进行关联。你需要手动执行构建命令,将代码从代码仓库中拉取并转换为Docker镜像。
- 自动构建:自动构建是指使用CI/CD工具(如Jenkins、Travis CI、GitLab CI等)或容器注册表(如Docker Hub、GitHub Container Registry等)的自动化流程,从代码仓库中获取源代码,并自动构建Docker镜像。这意味着每当代码仓库发生改变时,都会触发自动构建流程,重新构建镜像。自动构建可以根据代码库的变化自动更新并发布最新版本的镜像。
-
关于两者的区别,以下是一些主要考虑因素:
- 自动化程度:自动构建提供了更高的自动化程度,如根据代码变更自动触发构建流程。而非自动构建需要手动执行构建命令。
实时性:自动构建可以在代码更改后立即构建和部署镜像,保持镜像的实时性。非自动构建需要手动触发构建流程,可能导致一些时间上的延迟。 - 过程复杂性:自动构建需要一些配置和集成工作,如设置webhooks、构建触发条件等。非自动构建相对简单,只需手动执行构建命令。
- 部署频率:如果代码仓库中的变化频繁且需要频繁地构建和部署镜像,自动构建是更好的选择。如果代码仓库变化较少,并且不需要频繁构建和发布镜像,则非自动构建可以满足需求。
- 自动化程度:自动构建提供了更高的自动化程度,如根据代码变更自动触发构建流程。而非自动构建需要手动执行构建命令。
-
自动构建适合需要实时构建和部署的场景,尤其是对于持续集成和持续交付的需求;而非自动构建则适用于不需要频繁构建和部署的场景,或者只需进行间断性的镜像构建。
2.2.2、镜像相关概念
1.虚悬镜像
仓库名、标签都是<none>
的镜像,俗称虚悬镜像dangling image
一般没啥太大用处,删掉就行。
2.3、容器类命令
2.3.1、基础命令
-
新建+启动容器(
docker run --help
)- 命令格式 :
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
- OPTIONS
--name="容器新名字"
: 为容器指定一个名称。-d
:后台运行容器并返回容器ID,也即启动守护式容器(后台运行)。-i
: interactive,以交互模式运行容器,通常与-t
同时使用。-t
: tyy,为容器重新分配一个伪输入终端,通常与-i
同时使用,也即启动交互式容器(前台有伪终端,等待交互)。-v
:映射文件夹-P
:随机端口映射,大写P。-p
:指定端口映射,小写p。
- 启动交互式容器(前台命令行)
docker run - it ubuntu /bin/bash
: 以交互模式启动一个容器,在容器内执行/bin/bash
命令。docker run - it ubuntu bash
: 以交互模式启动一个容器,在容器内执行bash
命令。
- 命令格式 :
-
列出当前所有正在运行的容器
- 命令格式 :
docker ps [OPTIONS]
- OPTIONS
-a
:列出当前所有正在运行的容器 + 历史上运行过的。-|
:显示最近创建的容器。-n n
:显示最近n个创建的容器。-q
:静默模式,只显示容器编号。
- 命令格式 :
-
退出容器
exit
: run进去容器,exit退出, 容器停止。ctrl+p+q
: run进去容器,ctr1+p+q退出, 容器不停止。
-
启动已停止运行的容器 :
docker start容器ID/容器名
-
重启容器 :
docker restart容器ID/容器名
-
停止容器 :
docker stop容器ID/容器名
-
强制停止容器 :
docker kill容器ID/容器名
-
删除已停止的容器
- 命令格式 :
docker rm 容器ID/名称
- 命令举例(删除全部容器)
docker rm -f $(docker ps -a -q)
docker ps-a -q| xargs docker rm
- OPTIONS
-f
:强制删除。
- 命令格式 :
-
重要内容
-
守护式容器
- 问题:
docker run -d centos
执行,然后docker ps -a
进行查看,会发现容器已经退出很重要的要说明的一点,**Docker容器后台运行,就必须有一个前台进程,容器运行的命令如果不是那些一直挂起的命令(比如运行top无识别结果,就是会自动退出的)。 - 前台交互式启动:docker run -it redis:6.0.8 。
- 后台守护式启动:docker run -d redis:6.0.8。
- 问题:
-
查看容器ID :
docker logs 容器ID/名字
-
查看容器内运行进程:
docker top 容器ID/名字
-
查看容器内部细节:
docker inspect 容器ID/名字
-
进入正在运行的容器并以命令行交互
- 命令
docker exec -it 容器ID/名称 bashShell
docker attach 容器ID/名称
- 区别
attach
:直接进入容器启动命令的终端,不会启动新的进程,用exit退出,会导致容器的停止。exec
:是在容器中打开新的终端,并且可以启动新的进程,用exit退出,不会导致容器的停止。
- 命令
-
从容器内拷贝文件到主机上(容器 --> 主机)
- 命令格式:
docker cp 容器ID:容器内路径 目的主机路径
- 命令格式:
-
导入导出容器
export
导出容器的内容流作为一个tar归档文件[对应import命令]- 命令格式:
docker export ddb1ae909a66 > xxxx. tar
- 命令格式:
import
从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]- 命令格式:
cat文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
- 命令格式:
2.3.2、端口映射关系
端口映射支持格式
ip:hostport:containerport #指定ip、指定宿主机port、指定容器port
ip::containerport #指定ip、未指定宿主机port(随机)、指定容器port
hostport:containerport #未指定ip、指定宿主机port、指定容器port
端口的映射有以下五种方法:
- 1.将容器暴露的所有端口,都随机映射到宿主机上。不推荐使用)
docker run -P -it ubuntu /bin/bash - 2.将容器指定端口随机映射到宿主机一个端口上。
docker run -P 80 -it ubuntu /bin/bash
以上指令会将容器的80端口随机映射到宿主机的一个端口上。 - 3.将容器指定端口指定映射到宿主机的一个端口上。
docker run -p 8000:80 -it ubuntu /bin/bash
以上指令会将容器的80端口映射到宿主机的8000端口上。 - 4.将容器ip和端口,随机映射到宿主机上。
docker run -P 192.168.0.100::80 -it ubuntu /bin/bash
以上指令会将容器的ip192.168.0.100和80端口,随机映射到宿主机的一个端口上。 - 5.将容器ip和端口,指定映射到宿主机上。
docker run -p 192.168.0.100:8000:80 -it ubuntu /bin/bash
以上指令会将容器的ip192.168.0.100和80端口,映射到宿主机的8000端口。
查看映射端口配置
docker port container_ID
结果
80/tcp -> 0.0.0.0:800
三、Docker镜像
3.1、概念
3.1.1、基本概念
是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来 一个对象)。
3.1.2、镜像分层
在下载的过程中,注意到docker的镜像是在一层一层的在下载。
3.1.3、联合文件系统(UnionFS)
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual
filesystem)。Union 文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种且体的应用镜像。
特性: 一次同时加载多个文件系统,但从外面看起来,只能看到-一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
3.1.4、镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统是UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。 这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system),在bootfs之上。包含的就是典型Linux系统中的/dev、/proc、/bin、/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
3.1.5、镜像注意
Docker镜像层都是只读的,容器层是可写的当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层“。
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层” 之下的都叫“镜像层”。所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
3.2、Docker commit
docker commit
提交容器副本使之成为一个新的镜像- 命令格式
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名/版本号]
- 命令格式
3.3、本地镜像发布到阿里云
3.3.1、流程
3.3.2、创建仓库镜像
- 选择控制台,进入容器镜像服务中心
- 选择个人实例
- 创建命名空间
- 创建仓库镜像
- 进入管理界面获得脚本
- 拉取镜像
3.4、docker私有库
3.4.1、概念
Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。
3.4.2、操作
- 下载镜像Docker Registry。
docker pull registry
- 运行私有库Registry,相当于本地有个私有Docker hub。
默认情况,仓库被创建在容器的var/lib/registry
目录下,建议自行用容器卷映射,方便于宿主机联调
docker run -d -p 5000:5000 -v /xxx/xxx/:/tmp/registry --privileged=true registry
其他操作
- curl验证私服库上有什么镜像。
curl -XGET 本地服务器ip:5000/v2/_catalog
- 将新镜像xxx:1.2修改符合私服规范的Tag。
docker tag 镜像:Tag Host:Port/Repository:Tag
docker tag xxx:1.2 ip:端口/xxx:1.2
- 修改配置文件使之支持http。
给/etc/docker/daemon.json
配置文件增加下面那条配置。docker默认不允许http方式推送镜像,通过此配置选项来取消这个限制。修改完后重启docker。
注意: egistry-mirrors配置的是国内阿里提供的,不用加速的话访问官网的会很慢。2个配置中间有个逗号;'别漏了,这个配置是json格式的。 - push推送到私服库。
docker push ip:端口/xxx:1.2
四、Docker数据卷
4.1、注意
4.1.1、容器卷必带参数
--privileged = true
原因: docker 挂在主机目录访问 出现 cannot open directory .:Permission denied
,在挂在目录后面加一个--privileged = true
。
4.2、概念
容器数据卷,卷就是目录或者文件,存在于一个或者多个容器中,但不属于联合文件系统,因此能绕过Union File System提供一些用于持续存储或者共享数据的特性。设计的目的就是用于数据的持久化,其完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
可以将容器中的 数据备份+ 持久化 到本地的宿主机中
4.3、特点
- 数据卷可以在容器之间共享或重用数据。
- 容器卷的更改可以实时生效(即使docker在关闭情况下,主机修改,dockers重启也能获得)。
- 容器卷的更改不会包含到镜像的更新中。
- 数据卷的生命周期一直持续到没有容器使用它为止。
4.4、操作实例
4.4.1、命令演示
1.创建命令
docker run -it --privileged=true -v /宿主机绝对路径:/容器内目录 镜像名
2.查看是否挂载成功
docker inspect 容器ID
4.5、容器卷读写规则
4.5.1、读写(默认)
docker run -it --privileged=true -v /宿主机绝对路径:/容器内目录:rw 镜像名
设置可读写(不写默认就是)。
4.5.2、只读
docker run -it --privileged=true -v /宿主机绝对路径:/容器内目录:ro 镜像名
read only设置只读,容器内只能读不能写。
4.6、容器卷的继承
- 容器1完成和宿主机的映射
docker run -it --privileged=true -v /宿主机绝对路径:/容器内目录 --name u1 镜像名
- 容器2继承容器1的卷规则
docker run -it --privileged=true -volumes-from u1 --name u2 镜像名
继承关系只是继承挂载路径和其规则,u1删除了并不影响u2。
五、docker常规软件安装
5.1、tomcat
docker run -d -p 8080:8080 tomcat
- 测试访问如果404,可能原因
- 没有映射端口或者米有关闭防火墙
- 进入容器,将
webapps.dist
改成webapps
- 免修改版说明
docker pull billygoo/tomcat8-jdk8
docker run -d -p 8080:8080 --name 容器名字 billygoo/tomcat8-jdk8
5.2、MySQL
docker run -d -p 3306:3306 --name=mysql --privileged=true -v /xxx/mysql/log:/var/log/mysql -v /xxx/mysql/data:/var/lib/mysql -v /xxxx/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
- 解决MySQL乱码问题
- 在
/xxxx/mysql/conf
下 新建my.cof - 写入配置
- 在
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
- 检查编码:进入容器 输入
SHOW VARIABLES LIKE 'character%'
,检查是否全是utf8
5.3、Redis
- 需要从之前的安装的Redis中获取其配置文件
redis.conf
,放到对应的数据卷下。 - 修改配置文件
- 允许Redis的外地连接
注释掉配置文件中的 # bind 127.0.0.1
- 将
daemonize no
改为daemonize yes
,因为该配置和docker run
中的-d
命令冲突,导致容器一直启动失败。 - 开启redis持久化 改配置为
appendonly yes
。(可选)
- 允许Redis的外地连接
docker run -d -p 6379:6379 --name=redis --privileged=true -v /xxxx/redis/redis.conf:/etc/redis/redis.conf -v /xxxx/redis/data:/data redis:6.0.8 redis-server /etc/redis/redis.conf
六、Docker复杂操作
6.1、MySQL主从复制
6.1.1、原理
6.1.2、步骤
1.新建主服务器容器实例3307
docker run -d -p 3307:3306 --name mysql-master -v /xxx/mysql-master/log:/var/log/mysql -v /xxx/mysql-master/data:/var/lib/mysql -v /xxxx/mysql-master/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7
2. 进入配置/xxxx/mysql-master/conf
目录下新建my.cnf
cd /xxxx/mysql-master/conf
vim my.cnf
将下面配置写进去
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致。
slave_skip_errors=1062
3. 修改配置后重启master实例
docker restart mysql-master
4. 进入mysql-master
容器
docker exec -it mysql-master /bin/bash
mysql -uroot -proot
5. master
容器实例内创建数据同步用户
CREATE USER 'slave'@'%' IDENTIFIED BY '123456' #建立用户
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'slave'@'%' # 授权
6. 新建主服务器容器实例3308
docker run -d -p 3307:3306 --name mysql-slave -v /xxx/mysql-slave/log:/var/log/mysql -v /xxx/mysql-slave/data:/var/lib/mysql -v /xxxx/mysql-slave/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7
7. 进入配置/xxxx/mysql-slave/conf
目录下新建my.cnf
cd /xxxx/mysql-slave/conf
vim my.cnf
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致。
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
8.修改配置后重启slave实例
docker restart mysql-slave
9.在主数据库中查看主从同步状态
进入主机实例内部,执行
show master status
10. 进入mysql-slave
容器
docker exec -it mysql-slave /bin/bash
mysql -uroot -proot
11.在从数据库中配置中从复制
change master to master_host ='宿主机IP‘, master_user='slave',master_password='123456',master_port=3307,master_log_file='mall-mysql-bin.000001',master_log_pos=617,master_connect_retry=30;
参数说明:
1. master_host
:主数据库IP地址。
2. master_port
:主数据库运行端口。
3. master_user
:在主数据库创建的用于同步数据的用户账号。
4. master_password
:在主数据库创建的用于同步数据的用户密码。
5. master_log_file
:指定从数据库要复制的日志文件,通过查看主数据库的状态,获取File参数。
6. master_log_pos
:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取position参数
7. master_connect_retry
:连续失败的重试时间间隔,单位是秒。
12.在从数据库中查看主从同步状态
show slave status \G;
13.在主数据库中开启主从同步
start slave
14.查看从数据库状态
show slave status \G;
15.主从复制测试
6.2、Redis集群
6.2.1、Cluster(集群)模式–docker版
哈希槽分区进行亿级数据存储。
6.2.2、分区原理
1.哈希取余分区
- 原理
2亿条数据就是两亿个(k,v),我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式hash(key)%N
个机器台数,计算出哈希值,决定数据映射到哪个节点上。 - 优点
简单,直接有效,只需要预估好数据规划好节点,例如3台,就能保证一段时间的数据支撑。使用Hash算法让固定一部分请求落到一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求信息),起到了负载均衡+分而治之的作用 - 缺点
原来规划好的节点,进行扩容或者缩容就会出现问题。在扩容缩容下,每次数据变动导致节点有变动,映射关系需要重新计算,在服务器个数固定不变时,没有问题,当服务器需要弹性扩容或者服务器故障宕机的情况下,原来的取模公式就会发生变化。此时地址经过某个redis机器宕机,由于服务器数量的变化,会导致hash取余全部数据重新洗牌。
2.一致性哈希算法分区
-
背景
一致性算法在1997年由麻省理工学院中提出的,为了解决分布式缓存数据变动和映射问题(某个机器宕机,分母改变,自然取余会出现问题) -
步骤
- 算法构建一致性哈希环。
- 服务器IP节点映射。
- key落到服务器的落键原则。
-
一致性哈希环的创建
- 一致性哈希算法必然有个Hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个Hash空间[0,232-1 ],这是一个线性空间,但在算法中,通过适当的逻辑控制将它的首尾相连(0 = 232),这样让它逻辑上形成一个环形空间。
- 它是按照取模的方法,
哈希取余分区
是对节点(服务器)的数量进行取模。而一致性Hash算法是对 232 取模。一致性Hash算法将整个哈希值空间组成一个虚拟的圆环,如假设某哈希函数H的值空间为0 – 232-1(哈希值是一个32位无符号整形)。 - 如图哈希环:整个空间按照顺时针方向组织,圆环的正上方的点代表0,0点右侧第一个点代表1,以此类推直到232-1,也就是说0点左侧对的第一个点代表 232-1,0和 232-1在零点中方向重合,把这个由 232个点组成的圆环称为Hash环。
-
服务器IP节点映射
- 将集群中的ip节点映射到环上的某个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希闭环上的位置。例如:4个节点NodeA、B、C、D,经过IP地址的哈希计算(hash(ip)),使用IP地址哈希后再环空间的位置如下:
- 将集群中的ip节点映射到环上的某个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希闭环上的位置。例如:4个节点NodeA、B、C、D,经过IP地址的哈希计算(hash(ip)),使用IP地址哈希后再环空间的位置如下:
-
key落到服务器的落键规则
- 当需要存储一个kv键值对时,首先计算key的hash值,hash(key),将整个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一个遇到的服务器就是其应该定位的服务器,并将该键值对存储在该节点上。
- 例如,ObjA、ObjB、ObjC、ObjD四个数据对象,经过哈希计算后,在环空间上的位置如下,根据一致性Hash算法,数据A会被定位到NodeA上,B到NodeB上,以此类推。
-
优点
- 容错性高。假设Node C 宕机,可以可以看到对象ABD不受影响,只有C对象被重新定位到Node D。一般的马仔一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其他闭环控件中前一台服务器(沿着逆时针方向行走遇到的第一台服务器)之间数据,其他不会受到影响。(C宕机只会影响BC之间的数据,这些数据会转移到D中)
- 扩展性。数据量增加后,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致Hash取余全部数据重新洗牌。
- 容错性高。假设Node C 宕机,可以可以看到对象ABD不受影响,只有C对象被重新定位到Node D。一般的马仔一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其他闭环控件中前一台服务器(沿着逆时针方向行走遇到的第一台服务器)之间数据,其他不会受到影响。(C宕机只会影响BC之间的数据,这些数据会转移到D中)
-
缺点
- 存在数据倾斜问题。如果节点太少,容易因为节点分布不均匀造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)。如下
- 存在数据倾斜问题。如果节点太少,容易因为节点分布不均匀造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)。如下
3.哈希槽分区
- 概念
- 哈希槽实质是一个数组,数组[0,214-1]形成的hash slot控件。用于解决均匀分配问题,在数据和节点之间又加入一层,把这层命名为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。
- 槽解决的是粒度问题,相当于把粒度变大,这样便于数据移动。
- 哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
- 一个集群只能有16384个槽(0-16383 --》0 - 214-1)。redis的集群默认是16384个槽(原理)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点,集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几,key就落入对应的槽里。
slot = CRC16(key) % 16384
。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,数据移动问题就解决了。
- 计算方式
- Redis集群内置了16384个哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-value时,redis先对key使用CRC16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希草,也就是映射到某个节点上。如下代码和图。
- Redis集群内置了16384个哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-value时,redis先对key使用CRC16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希草,也就是映射到某个节点上。如下代码和图。
@Test
public void test(){
System.out.println(SlotHash.getSlot("A"));//6373
System.out.println(SlotHash.getSlot("B"));//10374
System.out.println(SlotHash.getSlot("C"));//14503
System.out.println(SlotHash.getSlot("hello"));//866
}
6.2.3、3主3从redis集群配置
1. 流程图
2.步骤
- 新建6个docker容器实例
--net host
:使用宿主机的IP和端口(默认)
--cluster-enabled
:集群开关
--appendonly
: 持久化开关
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
- 进入1号容器(随便容器都可以),为6台机器构建集群关系
docker exec -it redis-node-1 /bin/bash
执行下面命令
redis-cli --cluster create ip:6381 ip:6382 ip:6383 ip:6384 ip:6385 ip:6386 --cluster-replicas 1
--cluster create
:构建集群
--cluster-replicas 1
:表示为每个master创建一个slave
- 链接6381作为切入点,查看集群状态
redis-cli -p 6381
cluster info
cluster nodes
cluster info
:查看集群信息
cluster nodes
:查看集群节点
集群信息
节点信息
注意:主从关系是redis集群自动设置,每次设置不一定一样。
6.2.4、主从容错切换迁移配置
1.流程图
2.步骤
- 数据读写存储
- 启动6个机器构成的集群,通过exec进入
docker exec -it redis-node-1 /bin/bash
当用单机版命令redis-cli -p 6381
连接redis时,出现存储错误,哈希槽值大于6381的值。
故:集群环境下不能使用单机命令连接redis,要是用 redis-cli -p 6381 -c
命令连接(-c是优化路由,集群环境连接)
- 查看集群状态
在容器中执行
redis-cli --cluster check ip:6381
- 容错切换迁移
- 主机3681和从机切换,停掉主机6381
docker stop redis-node-1
- 再次查看集群信息
docker exec -it redis-node-2 /bin/bash
redis-cli -p 6382 -c
cluster nodes
6381宕机,其从机6384变成主机。 - 重启1号机,查看集群状态
docker start redis-node-1
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381 -c
cluster nodes
1号机变成从机,4号机变成主机。 - 还原之前的3主3从架构关系
- 先启动1号机
docker start redis-node-1
- 再停掉4号机
docker stop redis-node-4
- 再启动4号机
docker start redis-node-4
- 先启动1号机
- 查看集群状态
cluster nodes
- 主机3681和从机切换,停掉主机6381
6.2.5、主从扩容
1、流程图
2、步骤
- 新建6387、6388两个节点+新建后启动+查看是否8节点
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
- 进入7号机器
docker exec -it redis-node-7 /bin/bash
- 将新增的6387节点(空槽号)作为master节点加入原集群
reids-cli --cluster add-node ip:6387 ip:6381
--cluster add-node ip:6387 ip:6381
:6387是新增的master节点,6381是原来集群节点里面的“领路节点”
- 检查集群情况第1次
redis-cli --cluster check ip:6381
- 重新分派槽号
redis-cli --cluster reshard ip:6381
- 输入指令后需要填写分的槽数,这里平均分 ,写4096
- 写完数量,还需要指定分的节点的ID,复制6387的ID即可
- 说明:重新分槽成本太高,所以三个节点各自分出一点给新的
- 检查集群情况第2次
redis-cli --cluster check ip:6381
- 为主节点6387分配从节点6388
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
- 检查集群情况第3次
redis-cli --cluster check ip:6381
6.2.6、主从缩容
1.流程图
2.步骤
- 清除6388节点(是主节点6387的从节点)
redis-cli --cluster del-node ip:6388 主节点ID
。 - 将6387 的槽号清空,重新分配。(本次将所有槽给了6381)。
redis-cli --cluster reshard ip:6381
- 检查集群情况
redis-cli --cluster check ip:6381
- 清除6387节点(是主节点)
redis-cli --cluster del-node ip:63878 主节点ID
。
七、Docker File
7.1、简介
7.1.1、概念
是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
官网
7.1.2、构建步骤
- 编写Dockerfile文件
docker build
命令构建镜像docker run
依镜像运行容器实例
7.2、构建过程解析
7.2.1、DockerFile基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行。
#
表示注释。- 每条指令都会创建一个新的镜像层并对镜像进行提交。
7.2.2、Docker执行DockerFile的大致流程
- docker从基础镜像运行一个容器。
- 执行一条命令对容器作出修改。
- 执行类似的
docker commit
的操作提交一个新的镜像层。 - docker再基于刚提交的镜像运行一个新的容器。
- 执行
dockerfile
中的下一条指令直到所有指令都执行完成。
7.3、DockerFile常用的保留字指令
7.3.1、常用保留字介绍
FROM
:基础镜像,当前新镜像是基于哪个镜像,指定一个已经存在的镜像作为模版,DockerFile的第一条命令必须是FROMMAINTAINER
:镜像维护者的姓名和邮箱地址RUN
:容器构建时需要运行的命令,RUN是在docker build
时运行的- shell格式:
RUN <命令行指令>
等同于在终端操作 shell命令。例如RUN yum -y install vim
。 - exec格式 :
RUN ["可执行文件","参数1","参数2"]
。例如RUN ["./test.php","dev","offline"]
等价于RUN ./test.php dev offline
。
- shell格式:
EXPOSE
:当前容器对外暴露出的端口。WORKDIR
:指定 在创建容器后,终端默认登录进来的工作目录,一个落脚点。USER
:该用户以什么样的用户去执行,如果不指定,默认ROOT。ENV
:用来在构建镜像的过程中设置环境变量。- 例如
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何RUN指令中使用,相当于命令前面指定了环境变量前缀,也可以在其他指令中直接使用这些环境变量。如WORKDIR $MY_PATH
- 例如
ADD
:将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包。COPY
: 类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源路径>
的文件/目录复制到新的一层镜像内的<目标路径>
COPY src dest
COPY ["src","dest"]
<src 源路径>
:源文件或者源目录。<dest 目标路径>
:容器内的指定路径,该路径不用事先建好,路径不存在会创建。
VOLUME
:容器卷,用于数据保存和持久化工作。CMD
:指定容器启动后干的事。命令格式与RUN相似。- 命令格式与RUN相似。但CMD四在docker run 时运行,RUN实在docker build 时运行。
- shell 格式:CMD <命令>
- exec 格式:CMD [“可执行文件”,“参数1”,“参数2”]
- 参数列表 格式:CMD [“参数1”,“参数2”]。在指定了命令
ENTRYPOINT
指令后,用CMD
指定具体的参数。
- Dockerfile中可以有多个CMD命令,但只有最后一个生效,cmd会被docker run之后的参数替换。
- 命令格式与RUN相似。但CMD四在docker run 时运行,RUN实在docker build 时运行。
ENTRYPOINT
: 指定一个容器启动时要运行的命令。类似于cmd指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且类似于CMD命令行参数送给ENTRYPOINT指令指定的程序。- 注意
ENTRYPOINT
可以和CMD
一起用,一般是变更命令才使用CMD
,这里的CMD等于在给ENTRYPOINT
传参。- 当指定了
ENTRYPOINT
后,CMD
的含义就发生了变化,不再是直接运行其命令而是将CDM
的内容作为参数传递给ENTRYPOINT
指令,组合变成<ENTRYPOINT> “<CMD>”
- 命令格式
ENTRYPOINT ["executable","<param1>","<param2>"
- 示例:构建nginx
FROM nginx ENTRYPOINT ["nginx","-c"] CDM ["/etc/nginx/nginx.conf]"
- 注意
7.3.2、Entrypoint和Cmd区别
Entrypoint和Cmd都是用于指定容器启动时要运行的命令,主要区别在于它们的作用和使用方式。
-
Entrypoint是指定容器启动时要执行的可执行文件或脚本,并且该命令在运行容器时不能被覆盖。Entrypoint可以看作是容器的默认执行命令,它会在运行容器时自动执行。如果在运行容器时指定了其他命令,则这些命令将作为Entrypoint命令的参数传递给容器。例如,在Dockerfile中指定Entrypoint为一个可执行文件,如下所示:
ENTRYPOINT ["./app"]
。在运行容器时可以使用以下命令:docker run myapp arg1 arg2
,这样将运行容器,自动执行Entrypoint命令"./app",并将"arg1"和"arg2"作为参数传递给它。 -
Cmd是指定容器启动时要执行的默认命令或参数。它可以被覆盖,如果在运行容器时指定了其他命令,则这些命令将替换掉Cmd命令。例如,在Dockerfile中指定Cmd为一个默认参数,如下所示:
CMD ["--port","80"]
。在运行容器时可以使用以下命令:docker run myapp --port 8080
,这样将运行容器,并使用"–port 8080"替换掉默认的Cmd命令参数"–port 80"。
7.4、案例解析
7.4.1、构建一个具备vim+ipconfig+jdk8的Centos7
1.编写Dockerfile文件(大写字母D)
#基础镜像,继承于centos
FROM centos
#指定作者
MAINTAINER xxx<xxx@xxx.com>
#指定目录
ENV MYPATH /usr/local
#进入命令
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#按装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
#gcc的一个加强安装包
RUN yum -y install glibc.i686
#创建一个目录
RUN mkdir /usr/local/java
#ADD是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success-------ok"
CMD /bin/bash
2.构建
在Dockerfile目录下,输入命令。
docker build -t 新镜像名字:TAG .
成功
7.5、虚悬镜像
7.5.1、概念
仓库名、标签都是<none>
的镜像,俗称dangling image(虚悬镜像)。
建议删除,这个东西并没有什么用。
7.5.2、生成虚悬镜像
from ubuntu
docker build .
7.5.3、关于虚悬镜像命令
- 查看虚悬镜像
docker image ls -f dangling=true
- 删除所有虚悬镜像
docker image prune
八、docker微服务
8.1、案例解析
- 通过IDEA新建一个 普通微服务模块(简单的微服务模块)。
- 模块打包传到服务器上。
- 编写DockerFile
#基础镜像使用java
FROM java:8
#作者
MAINTAINER xxxxx
# VOLUME指定临时文件目录为/tmp,在主机 /var/lib/docker 目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
#将jar包添加到容器中并更名为xxx_docker. jar
ADD docke_boot-0.0.1-SNAPSHOT.jar xxx_docker.jar
#运行jar包
RUN bash .C 'touch/zzy_docker.jar'
ENTRYPOINT ["java","-jar","/xxx_docker.jar"]
#暴露8080端口作为微服务
EXPOSE 8080
- 将jar包和Dockerfile文件放到一个目录下
- 构建镜像。
docker build -t xxx_docker.jar .
- 测试访问
curl 127.0.0.1:8080/test/test1
8.2、可能报错
8.2.1、防火墙报错
解决:
- 关闭防火墙
systemctl stop firewalld
- 重启docker
systemctl restart docker
九、docker网络
9.1、概念
9.1.1、docker网络作用
- 容器间的互联和通信以及端口映射。
- 容器IP变动时候可以通过服务名直接网络通信而不受到影响。
9.1.2、docker网络情况
1.不启动(默认)
docker不启动输入命令报错。
宿主机的网络情况
- ens33:centos7版本后普遍默认的网卡是ensxx。
- lo:环回接口(loopback)
- virbr0:virbr0是虚拟网桥网卡,virbr是虚拟网桥(Virtual Bridge)。在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的
virbr0
网卡(virbr0网卡:它还有一个固定的默认IP地址192 168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供NAT访问外网的功能。
2.启动后
产生了个名为docker0的虚拟网桥,通过此网桥与宿主机或者容器间进行通信。
9.2、基本命令
命令docker network COMMAND
查询网络相关命令
docker network ls
:查看网络模式
docker 默认的网桥模式docker network connect
:连接docker network create
:创建网络模式docker network disconnect
:中断docker network prune
:删除所有无效网络docker network rm xxx网络名称
:删除网络模式-
docker network inspect xxx网络名称
:查看网络数据源
9.3、网络模式简介
bridge
:为每一个容器分配、设置IP等,并将容器连接到一个docker0,虛拟网桥,默认为该模式。使用--network bridge
指定(默认使用dicker0)。host
:容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。使用--network host
指定。none
:容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair
和网桥连接,IP等。使用--network none
指定。container
:新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP端口范围。使用--network container:NAME或者容器ID
指定。
9.4、网络模式案例
9.4.1、底层IP和容器映射变化
概念
- 在Docker网络中,有两个关键的概念:底层IP和容器映射。
- 底层IP:每个Docker容器都会分配一个底层IP地址,这个地址是在Docker宿主机上的虚拟网络接口上分配的。底层IP是容器内部使用的IP地址,用于容器内部进程之间的通信。
- 容器映射:容器映射是指将容器的端口映射到宿主机的端口,以便容器可以通过宿主机的网络接口与外部进行通信。当我们在创建容器时使用
-p
或--publish
参数指定端口映射规则时,Docker会将容器内部的端口映射到宿主机上的一个随机端口或指定的端口上。
- 具体来说,当创建一个Docker容器并指定端口映射规则时,Docker会在宿主机上创建一个转发规则,将宿主机上的指定端口与容器内部的端口进行绑定。这样,当外部系统通过宿主机的IP地址和绑定的端口访问时,请求会被转发到容器内部的对应端口上。
- 底层IP是容器内部使用的IP地址,用于容器内部进程之间的通信。而容器映射是将容器的端口映射到宿主机的端口,以便容器可以与外部网络进行通信。通过这种方式,Docker网络提供了灵活且可定制的网络配置选项,使得容器可以轻松地与其他容器和外部系统进行通信。
实例
docker inspect u1 | tail -n 20
docker inspect u2 | tail -n 20
对比两个,发现Ip不一样
当删除U2后,再创建U3时,U3的IP和删除前的U2一样。所以,底层的网络是会变动的,当写死IP后,万一服务器宕机或者被删,会造成很严重后果。 所以,需要通过网络规划好网络服务。
9.4.2、bridge默认网络模式(桥接模式)
1.概念
Docker服务默认会创建一个docker0网桥(其上有一个docker0
内部接口),该桥接网络的名称为docker0
,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker默认指定了docker0
接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
#查看bridge网络的详细信息,并通过grep获取名称项
docker network inspect bridge | grep name
ifconfig | grep docker
ip addr # 查看网络
2.网络模型
- Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(
docker0
),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP
,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP
直接通信。 docker run
的时候,没有指定network
的话默认使用的网桥模式就是bridge
,使用的就是docker0
。在宿主机ifconfg
,就可以看到docker0
和自己create
的network
,eth0
,eth1
,eth2
…代表网卡一,网卡二,网卡三…lo
代表127.0.0.1
,即localhost
,inet addr
用来表示网卡的IP地址。- 网桥
docker0
创建一 对对等虚拟设备接口一个叫veth
,另一个叫eth0
,成对匹配。- 整个宿主机的网桥模式都是
docker0
,类似一个交换机有一堆接口,每个接口叫veth
,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair
) ; - 每个容器实例内部也有一块网卡,每个接口叫
eth0
; docker0
上面的每个veth
匹配某个容器实例内部的eth0
,两两配对,一一匹配。
- 整个宿主机的网桥模式都是
- 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
3.两两匹配验证
- 宿主机查看网络,发现有个
23的veth
对应22的etho
ip addr # 查看网络
2. 容器查看网络,发现有个22的etho
对应23的veth
ip addr # 查看网络
9.4.3、host网络模式(主机模式)
1.概念
直接使用宿主机的IP地址与外界进行通信,不再需要额外进行NAT转换。
网络信息:docker inspect 容器名(host网络模式)
2.网络模型
容器将不会获得一个独立的Network Namespace
,而是和宿主机共用一个Network Namespace
。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
3.举例验证
- 问题
docker run -d -p 8083:8080 --network host --name tomcat83 test/tomcat8-jdk8
执行上述代码会有以下警告
- 原因:docker启动时指定
--network=hos
t或-net=host
,如果还指定了-p
映射端口,那这个时候就会有此警告,
并且-p
设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。 - 解决:
- 就是使用docker的其他网络模式,例
--network=bridge
。 - 直接无视。
- 就是使用docker的其他网络模式,例
9.4.4、none网络模式
1.概念
禁用网络功能,只有Io
标识(就是127.0.0.1
表示本地回环)。
2.网络模型
- 在none模式下,并不为Docker容器进行任何网络配置。
- 也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo。
- 需要我们自己为
Docker
容器添加网卡、配置IP等。
3.案例
查看网络信息:docker inspect 容器名
容器内部查看网络信息:ip addr
9.4.4、container网络模式
1.概念
新创建的容器不会创建自己的网卡和配置自己的IP, 而是和一个指定的容器共享IP、 端口范围等。
2. 网络模型
新建的容器和己经存在的一个容器共享一个网络ip配置 而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样。两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
3.案例
- 问题
docker run -d -p 8085:8080 --name tomcat85 xxx/tomcat8-jdk8
docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 xxx/tomcat8-jdk8
报错:
原因:相当于tomcat86
和tomcat85
公用同一个ip同-一个端口,导致端口冲突。
- 小案例
搭建Alpine环境(Alpine操作系统是一个面向安全的轻型Linux发行版,下面有简介):
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
验证环境:
alpine1
alpine2
关闭alpine1 后测试alpine2:
发现共享源消失
- Alpine介绍
Alpine Linux是一款独立的、非商业的通用Linux发行版,专为追求安全性、简单性和资源效率的用户而设计。可能很多人没听说过这个Linux发行版本,但是经常用Docker的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到6M的大小,所以特别适合容器打包。
9.4.6、自定义网络模式
1. 概念
2. 没用自定义网络前
docker run -d -p 8081:8080 --name tomcat81 xxx/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 xxx/tomcat8-jdk8
成功启动并用docker exec
进入各自容器实例内部
tomcat81:
tomcat82:
在tomcat82 ping
tomcat81的ip或者tomcat81 ping
tomcat82的ip,没有任何问题。
在tomcat81
和tomcat82
直接ping对方服务名,报错
3. 使用自定义网络后
自定义桥接网络,自定义网络默认使用的是桥接网络bridge
- 新建自定义网络
docker network create my_network
- 新建容器加入自定义网络
docker run -d -p 8081:8080 --network my_network --name tomcat81 xxx/tomcat8-jdk8
docker run -d -p 8082:8080 --network my_network --name tomcat82 xxx/tomcat8-jdk8
- 互相ping测试口
tomcat81 :
tomcat82 :
9.4.7、总结
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
9.5、可能出现问题
9.5.1、docker启动的容器服务均无法访问
问题
docker启动的容器服务均无法访问,容器启动后浏览器访问一直提示网络连接错误。
启动容器时有以下提示
WARNING: IPv4 forwarding is disabled. Networking will not work.
解决方案
在sysctl.conf文件中追加以下
# 1代表启用
net.ipv4.ip_forward=1
然后使用以下命令重启服务器网络
systemctl restart network
再次通过浏览器访问docker容器相关服务,一切正常
问题原因
没有开启转发,网桥配置完后,需要开启转发,不然容器启动后,就会没有网络
9.5.2、docker启动的容器服务均无法访问
十、docker-compose容器编排
10.1、基础
10.1.1、概念
Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成个应用。你需要定义一个YAML格式的配置文件docker-compose.yml
,写好多个容器之间的调用关系。然后,只要一个命令, 就能同时启动关闭这些容器。
10.1.2、作用
- 解决容器启动顺序和加载条件。
10.1.3、核心概念
1.一文件
指的是docker-compose.yml
2.两要素
- 服务(service):一个应用容器实例就是一个服务。
- 工程(project):由一组关联的应用容器组成的一个完整业务单元,在
docker-compose.yml
文件中定义。
10.2、下载安装
10.2.1、下载
10.2.2、安装
centos安装
curl -L https://github.com/docker/compose/releases/download/v2.14.1/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
# 或者:
curl -L http://mirror.azure.cn/docker-toolbox/linux/compose/v2.14.1/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# 卸载
sudo rm /usr/1oca1/bin/docker-compose
ubuntu安装
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
10.3、操作步骤
- 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件。
- 使用
docker-compose.yml
定义一个完整业务单元,安排好整体应用中的各个容器服务。 - 执行
docker-compose up
命令来启动并运行整个应用程序,完成一键部署上线。
10.4、常用命令
系统命令
docker-compose -h
: 查看帮助。
docker-compose up
:启动所有docker-compose服务。
docker-compose up -d
:启动所有docker-compose服务并后台运行。
docker-compose down
:停并删除容器、网络、卷、镜像。
docker-compose exec yml 里面的服务id
:进入容器实例内部 docker-compose exec docker-compose .yml文件中写的服务id /bin/bash
。
docker-compose ps
:展示当前docker-compose编排过的运行的所有容器。
docker-compose top
:展示当前docke-compose编排过的容器进程。
docker-compose logs yml里面的服务id
:查看容器输出日志。
dokcer-compose config
:检查配置。
dokcer-compose config -q
:检查配置,有问题才有输出。
docker-compose restart
:重启服务。
docker-compose start
:启动服务。
docker-compose stop
:停止服务。
yml指令
environment
可以用来设置容器环境变量。它可以用于配置应用程序所需的各种环境变量,如数据库连接字符串、API密钥等。通过在docker-compose.yml
文件中使用environment
选项,您可以将键值对形式的环境变量添加到容器中。
version: '3'
services:
my-app:
image: my-image
environment:
- DATABASE_URL=mysql://user:password@db:3306/mydb
- API_KEY=abc123
command
用于在容器启动时运行特定的命令。您可以使用command
选项来覆盖Docker
镜像中定义的默认命令。这在需要在容器启动时运行特定的脚本或命令时非常有用。
version: '3'
services:
my-app:
image: my-image
command: python script.py
entrypoint
类似于command
选项,用于定义容器启动时运行的命令或脚本。不同之处在于entrypoint
的值会被作为容器的默认入口点,在其后面可以附加命令参数。与command
选项不同,entrypoint
选项一般用于定义服务所需的主要命令,而command
选项用于提供额外的参数。
version: '3'
services:
my-app:
image: my-image
entrypoint: ["python", "app.py"]
使用场景
environment
选项适用于需要将环境变量传递给应用程序的情况,例如配置数据库连接字符串或设置API密钥。command
选项适用于覆盖Docker镜像中默认的启动命令,例如运行特定的脚本或指定不同的启动参数。entrypoint
选项适用于定义容器的默认入口点,在其后可以添加命令参数,例如指定要运行的主要应用程序和其它参数。
10.5、编排服务实例
- 将项目打包成.jar包
- 编写Dockerfile文件,构建镜像
#基础镜像使用java
FROM java:8
#作者
MAINTAINER xxx
# VOLUME指定临时文件目录为/tmp, 在主机 /var/ib/docker 月录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
#将jar包添加到容器中并更名为zzyy_ dockerjar
ADD docker_boot-0.0.1-SNAPSHOT.jar xxx_docker.jar
#运行jar包
RUN bash -c 'touch xxx_docker.jar'
ENTRYPOINT ["ava","jar","xxx_docker.jar"]
#暴露8080端口作为微服务
EXPOSE 8080
docker build -t xxx_docker:1.6
1.用compose之前
1.创建数据库实例,并且创建数据表
docker run - P 3306:3306 --name mysql57 --privileged=true -v /xxx/mysgl/conf:/etc/mysql/conf.d -v /xxx/mysql/logs:/logs -v /xxx/mysql/data:/var/lib/mysql -e MYSQL_R0OT_PASSWORD=123456 -d mysql:5.7
- 创建redis实例
docker run -p 6379:6379 --name redis608 --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf
- 微服务启动
docker run -d -p 8080:8080 xxx_docker
- 分析痛点
- 先后顺序要求固定,先mysql+redis才能微服务访问成功。
- 多个run命令…
- 容器间的启停或宕机,有可能导致IP地址对应的容器实例变化,映射出错,要么生产IP写死(可以但是不推荐),要么通过服务调用。
2.用compose之后
- 编写docker-compose.yml文件
version: "3" #compose版本号
services: #服务容器实例,下面写服务名
microService: # 服务名 自定义即可
image: xxx_docker:1.6 # 镜像名:版本号,还可以用build构建
container_name: ms01 # 容器名 --name 不加容器名会在显示的时候自动加前后缀 当前目录_服务名_数字标号
ports: # 端口映射 -p
- "8080:8080"
volumes: # 容器数据卷 -v
- /app/microSerice:/data
networks: #网络选择 --network
- xx_net # 最底下有定义
depends_on: # 依赖项,依赖于下面的项目 项目名即服务名
- redis
- mysql
redis:
image: redis:6.0.8
#container_name: redis # 容器名 --name 不加容器名会在显示的时候自动加前后缀 当前目录_redis_1
ports:
- "6379:6379"
volumes:
- /app/redis/data:/data
networks:
- xxx_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysq:5.7
#container_name: redis # 容器名 --name 不加容器名会在显示的时候自动加前后缀 当前目录_redis_1
networks:
- xxx_net
ports:
- "3306:3306"
environment: # 环境配置
MYSQL_ROOT_PASSWORD: '123456'
# MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
# MYSQL_DATABASE: 'db2021"
# MYSQL_USER: "root"
# MYSQL_PASSWORD: "123456"
volumes:
- $PWD/mysql/db:/var/lib/mysql
- $PWD/mysql/conf:/etc/mysql/conf
- $PWD/mysql/init:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问问题
networks:
xx_net: #网络模式名字,查看显示的时候会在前面添加前缀 当前目录_xx_net
- 修改服务的配置文件,将写死的ip替换成服务名
spring:
datasource:0
url: jdbc:mysql://mysql:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL-false
#url: jdbc:mysql://192.168.111.168:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL-false
redis:
#host:192.168.111.168
host:redis
- 打包服务,上传服务器,重新构建镜像
docker build -t xxx_docker:1.6
- 执行
dokcer-compose config -q
命令,检查docker-compose
语法是否有问题,没输出则没问题。 - 执行
docker-compose up -d
命令 - 关停 使用
docker-compose stop
十一、docker轻量级可视化工具Portainer
11.1、概念
Portainer是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
11.2、安装步骤
11.2.1、官网
11.2.2、步骤
- docker命令安装口
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v xxx/xxx/portainer_data:/data portainer/portainer
-v /var/run/docker.sock:/var/run/docker.sock
运行Docker的目录映射,必须要有才能用Portainer操作Docker命令
-
第一次登录需创建admin并且设置密码,
访问地址:x.x.x.xxx:9000
-
设置完成后,首次登陆。
-
选择local选项卡后本地docker详细信息展示。
点击进入
注意:Stack表示docker-compose编排实例 -
图形展示,对应命令。
首页的展示:docker system df
11.3、常用操作
11.3.1、查询操作
- docker整体概览
docker system df
- 查看所有容器实例
docker ps
- 查看容器实例详情
每一个小图标代表一个操作
11.3.2、安装操作
需要可以自己添加
十二、docker容器监控(重量级)CAdvisor+InfkuxDB+Granfana
12.1、基础概念
12.1.1、docker states
- 通过dockerstats命令可以很方便的看到当前宿主机上所有容器的CPU、内存以及网络流量等数据。
- 但是docker stats统计结果只能是当前宿主机的全部容授,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能
12.1.2、概念
CAdvisor监控收集
+ InfluxDB存储数据
+ Granfana展示图表
1.CAdvisor
- CAdvisor是一个容器资源监控工具包括容器的内存、CPU、网络I0、磁盘I0等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据,而且只是针对单物理机。
- 不过,CAdvisor提供 了很多数据集成接口,支持InfluxDB、Redis、Kafka、Elasticsearch等集成,可以加上对应配置将监控数据发往这些数据库存储起来。
- CAdvisor功能主要有两点:
- 展示Host和容器两个层次的监控数据。
- 展示历史变化数据。
2.InfluxDB
- InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖。
- CAdvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一 个时序数据库,专门用于存储时序相关数据,很适合存储CAdvisor的数据。而且,CAdvisor本身已经提供了InfluxDB的集成方法, 启动容器时指定配置即可。
- InfluxDB主要功能:
- 基于时间序列,支持与时间有关的相关函数(如最大、最小、求和等)。
- 可度量性:你可以实时对大量数据进行计算。
- 基于事件:它支持任意的事件数据。
12.2、搭建步骤(compose)
1. 新建docker-compose.yml
version: '3.1'
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor
ports:
- "8083:8083"
- "8086:8086"
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
restart: always
ports:
- "8082:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
grafana:
user: "104"
image: grafana/grafana
restart: always
links:
- influxdb:influxsrv
ports:
- "3001:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root
cadvisor备注:
-storage_driver相关参数:
-storage_driver driver
要使用的存储驱动程序。数据总是短时缓存在内存中,这控制了将数据推送到本地缓存之外的位置。空意味着没有。选项包括:<empty>,bigquery,elasticsearch,influxdb,kafka,redis,statsd,stdout
-storage_driver_buffer_duration duration
存储驱动程序中的写操作将在此期间进行缓冲,并作为单个事务提交给非内存后端(默认为1m0s)
-storage_driver_db string
数据库名称(默认为“ cadvisor”)
-storage_driver_es_enable_sniffer
默认情况下,ElasticSearch使用嗅探过程自动查找集群中的所有节点
-storage_driver_es_host string
ElasticSearch主机:端口(默认为“ http:// localhost:9200”)
-storage_driver_es_index string
ElasticSearch索引名称(默认为“cadvisor”)
-storage_driver_es_type string
ElasticSearch类型名称(默认为“ stats”)
-storage_driver_host string
数据库主机:端口(默认为“ localhost:8086”)
-storage_driver_influxdb_retention_policy string
保留政策
-storage_driver_kafka_broker_list string
kafka经纪人csv(默认为“ localhost:9092”)
-storage_driver_kafka_ssl_ca string
TLS客户端身份验证的可选证书颁发机构文件
-storage_driver_kafka_ssl_cert string
TLS客户端身份验证的可选证书文件
-storage_driver_kafka_ssl_key string
TLS客户端身份验证的可选密钥文件
-storage_driver_kafka_ssl_verify
验证ssl证书链(默认为true)
-storage_driver_kafka_topic string
kafka主题(默认为“统计信息”)
-storage_driver_password string
数据库密码(默认为“ root”)
-storage_driver_secure
与数据库使用安全连接
-storage_driver_table string
表名(默认为“统计信息”)
-storage_driver_user string
数据库用户名(默认为“ root”)
-storage_duration duration
保存数据的时间(默认值:2分钟)
2. 检查语法是否有问题
docker-compose config -q
没有输出则正确
3. 启动
- 后台启动
docker-compose up -d
- 前台启动
docker-compose up
3. 启动
- 检查
docker ps
4.测试
-
浏览cAdvisor收集服务:http://ip:8082/
首次访问会较慢(无法访问请看下面的出现问题
)
-
浏览influxdb存储服务,http://ip:8083/
-
浏览grafana展现服务,http://ip:3001
admin
admin
进入
出现问题
cadivisor报如下错
去官网(或者CSDN加速的镜像官网)看发现官网给的是gcr.io/cadvisor/cadvisor:$VERSION
镜像,但是镜像拉不下来,可以挂梯子试试。
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.44.0
#image: google/cadvisor:latest
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
restart: always
ports:
- "8081:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
然后重启,查看日志,发现
这只是个warning,忽略即可
5. 添加panel
1. 配置数据源
2. 配置细节

成功
3. 配置面板
- 创建面板
- 创建面板(曲线,图形)
随便选一个
4. 填充数据
十三、dockerDocker使用规范建议
- 尽量使用最近1-2年的新的稳定的docker版本
- 不要去安装今年前很老的版本,大量的bug已经被新版本更新解决掉了
- 尽量不要去创建非常大的镜像,比如5G10G以上的
- 镜像要尽量轻量化,去除不必要的软件,数据等
- 容器内挂载宿主机配置,使用只读
- 容器需要-v 宿主机的配置文件,尽量使用ro只读
- 数据要挂载宿主机物理硬盘或存储节点上
- 不要直接在容器里run,避免容器宕机引起数据丢失
- 应用日志一定要挂到宿主机上
- 不要直接打印到容器内,避免只能docker logs方式查看,避免去vulume目录里查看日志
- 不要只使用latest标签。
- Tag要有个管理标准,可以根据tag查找对应版本
- 不要使用容器ip,配置里更不能写死(默认172.17.0.x)
- 容器重启后,ip很可能会变
- 尽量不要在单容器内跑多进程
- 容器不是虚拟机,尽量做到1个容器,1个进程
- 跨环境镜像保持一致
- 不论是测试,UAT,生产环境,尽量保持同一个镜像,不要变更,环境变更只需要变更环境变量参数做区分
- 一定监控docker容器,及时发现问题
- 建议使用prometheus监控容器
- 一定要限制docker容器的资源,尤其是CPU、内存、硬盘空间,甚至是网络等,避免侵占宿主机的硬件资源