Docker 复杂安装详说
安装mysql主从复制
- 新建主服务器容器实例3307
docker run -d -p 3307:3306 --privileged=true -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql-master mysql:5.7
- 进入修改/mydata/mysql-master/conf目录下新建my.cnf
# /mydata/mysql-master/conf 为宿主机路径
cd /mydata/mysql-master/conf
vim my.cnf
# 在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
- 修改完配置后重启master实例
- docker restart mysql-master
- 进入master容器
- docker exec -it mysql-master /bin/bash
- master容器实例内创建数据同步用户
- create user 'slave'@'%' identified by '123456'; # 创建slave用户可以通过123456登录master主机
- grant replication slave, replication client on . to 'slave'@'%'; 授予权限,可拿到对应的数据
- 新建从服务器
docker run -d -p 3308:3306 --privileged=true -v /mydata/mysql-slave/log:/var/log/mysql -v /mydata/mysql-slave/data:/var/lib/mysql -v /mydata/mysql-slave/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql-slave mysql:5.7
- 进入/mydata/mysql-slave/conf 目录下新建my.cnf
# /mydata/mysql-slave/conf 为宿主机路径
cd /mydata/mysql-slave/conf
vim my.cnf
# 在my.cnf中添加以下内容
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
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
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
- 修改完后重启slave实例
- docker restart mysql-slave
- 在主数据库中查看主从同步状态
- show master status
- 进入mysql-slave容器
- docker exec -it mysql-slave /bin/bash
- mysql -u root -p
- root
- 在从数据库中配置主从复制
change master to master_host='master主机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; # 参数说明 master_host: 主数据库ip地址 master_port: 主数据库的运行端口 master_user: 在主数据库创建的用于同步数据的用户账号 master_password: 在主数据库创建的用于同步数据的用户密码 master_log_file: 指定从数据库要复制数据的日志文件,通过查看主数据的状态,读取File参数 master_log_pos: 指定从数据库从哪个位置开始复制数据,通过查看主数据状态,获取Position参数 master_connect_retry: 连接失败重试的时间间隔,单位为秒
- 在从数据库中查看主从同步状态
- show slave status \G;
- \G 是以key val键值对的形式显示
- Slave-IO-Runing 以及Slave-SQL-Runing会显示No
- 在从数据库中开启主从同步
- start slave;
- Slave-IO-Runing 以及 Slave-SQL-Runing 会显示Yes
- 主从复制测试
- 主机新建库-使用库-新建表-插入数据
- 从机使用库,查看记录
安装redis集群
大厂面试题-分布式存储案例真题
-
cluster(集群)模式-docker版 哈希槽分区进行亿级数据存储
- 面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例(抖音观看流水,转账记录,浏览记录等)
- 回答:单机单台100%不可能,肯定是分布式存储,用redis如何落地?
- 哈希取余分区
- 2亿条记录就是2亿可k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
- 优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如分3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求信息),起到负载均衡+分而治之的作用。
- 缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/?,此时地址经过某个redis机器宕机了,由于台数数量发生变化,会导致Hash取余全部重新洗牌。
- 一致性哈希算法分区
- 背景:一致性哈希在1997年由麻省理工学院提出,设计目标是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,凄然取余数就不OK了。
- 能干嘛:提出一致性hash解决方案。目的是当服务器个数发生变动时,尽量减少影响到客户端到服务器端的映射关系。
- 3大步骤
- 算法构建一致性哈希环:(所有存储节点排列在收尾相接的hash环上)一致性hash算法是对2^32次方取模,简单来说,一致性hash算法将整个哈希值空间组织成一个虚拟的圆环,0~2^32 - 1 个点组成的圆环称为hash环。
- 服务器IP节点映射:将集群中的各个IP节点映射到环上的某一个位置。具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
- key落到服务器的落键规则:当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),hash()与IP映射的哈希函数相同,然后,从hash(key)位置出发,沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值存储在该节点上。
- 优点
- 一致性哈希算法解决了哈希取余算的容错性和扩展性问题。,加入和删除节点只影响哈希环中的顺时针方向的相邻节点,对其他节点无影响。
- 缺点
- 一致性算法数据倾斜问题:一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜问题。例如系统中只有两台服务器时,由于服务器映射节点在哈希环上分布不均匀,导致被缓存的对象大部分集中缓存在某一台服务器上。
- 哈希槽分区(hash slot)
- 为什么出现: 为了解决一致性哈希算法的数据倾斜问题
- 能干什么
- 解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上方的是槽,槽中方的是数据。
- 槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动
- 哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
- 多少个hash槽
- 一个redis集群只能有16384个槽,编号0-16383(0-2^14 - 1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几,key就落入对应的槽里。slot=CRC16(key) % 16384。以槽为单位移动数据。因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
-
搭建3主3从redis集群
- 新建6个容器实例
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis --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 --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 --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 --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 --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 --cluster-enabled yes --appendonly yes --port 6386 # 参数详解 --net host #使用宿主机的IP和端口,默认 --privileged=true #获取宿主机root用户权限 -v /data/redis/share/redis-node-4:/data #容器卷,宿主机内地址:容器内地址 --cluster-enabled yse # 开启redis集群 --appendonly yes #开启持久化 --port 6386 #端口号
- 进入容器redis-node-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-replicas 1 代表master-slave之间关系为1:1, 为每个master创建一个slave节点
运行后会显示
>>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 # 为每个master节点分配槽 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 宿主机IP:6385 to 宿主机IP:6381 # 添加主从映射关系 Adding replica 宿主机IP:6386 to 宿主机IP:6382 Adding replica 宿主机IP:6384 to 宿主机IP:6383 >>> Trying to optimize slaves allocation for anti-affinity .... # 输入yes, 出现以下内容,则表示运行成功(需要开发集群总线端口,这一步不是很清楚,就把6381-6386 以及 16381-16386都开放了) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
- 链接进入6381作为切入点,查看集群状态
- redis-cli -p 6381
- cluster info
- cluster nodes
-
主从容错切换迁移案例
- 数据读写存储
# 1. 启动6台机器构成的集群并通过exec进入 docker exec -it redis-node-1 /bin/bash # 2. 对6381新增两个key redis-cli -p 6381 # 通过6381进入集群(单机连接) keys * # 查看所有键值对 set k1 v1 # 插入k1 v1,失败,k1的哈希值所所在的哈希槽对应6383,由于是单击连接,无法跳转6383,因此,插入失败 set k2 v2 # 成功 # 3. 防止路由失效加参数-c并新增两个key redis-cli -p 6381 -c set k1 v1 # 跳转至6383并插入成功 set k2 v2 # 跳转至6381并插入成功 # 4. 查看集群信息 redis-cli --cluster check 宿主机IP:6381
- 主从容错切换迁移
# 停止主机6381, 其从机6384会变为新的主机,并集继承6381之前存储的数据 docker stop redis-node-1 # 此时若主机6381恢复,其会变为6384的从机 docker start redis-node-1
-
主从扩容案例(3主3从变4主4从)
# 1. 新建6387、6388两个节点 + 新建后启动 + 查看是否是8个节点 docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis --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 --cluster-enabled yes --appendonly yes --port 6388 docker ps # 2.进入7号机内部 docker exec -it redis-node-7 /bin/bash # 3.将新增的6387节点(空槽号)作为master节点加入原集群 redis-cli --cluster add-node 宿主机IP:6387 宿主机IP:6381 # 6387 就是将要作为master新增节点 # 6381 就是原来集群里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群 # 4. 检查集群情况,第一次(没有数据也没有被分配槽位) redis-cli --cluster check 宿主机IP:6381 # 5. 重新分配槽号(重新哈希槽位) # redis-cli --cluster reshard 宿主机IP:端口号 redis-cli --cluster reshard 宿主机IP:6381 # 执行后出现以下内容 How many slots do you want to move(from 1 to 16384)? 4096 #四台master平均分配 What is the receiving node ID? node ID # 6387的节点ID, 通过查看集群信息获得 Source node #1: all # 6. 检查集群情况,第二次 redis-cli --cluster check 宿主机IP:6381 # 6387节点被分配了4096个槽位,重新分配成本太高,因此分别从其他三个master节点中分别匀出来了1364个槽位给节点6387(存在疑问,1. 若某个节点没有连续的1364个空槽位该怎么匀呢?2. 匀过去的4096个槽都是空的吗?) # 7. 为主节点6387分配从节点6388 redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID # 8. 检查集群情况
-
主从缩容案例(删除6387和6388,恢复3主3从)
# 目的:6387和6388下线(先删从节点,再删主节点) # 1. 检查集群情况获得6388的节点ID # 2. 从集群中将4号从节点6383删除 redis-cli --cluster del-node ip:从机端口 节点ID # 3. 将6387的槽号清空,重新分配,将清出来的槽号都给6381 redis-cli --cluster reshard 宿主机IP:6381 # 以6381为切入点,操作整个集群 How many slots do you want to move(from 1 to 16384)? 4096 What is the receiving node ID? node ID # 6381的节点ID, 通过查看集群信息获得 Source node #1: 6387节点ID Source node #2: done # 4. 检查集群情况第二次 redis-cli --cluster check 宿主机IP:端口 # 5. 将6387删除 redis-cli --cluster del-node ip:端口 节点ID # 6. 检查集群情况第三次
DockerFile解析
DockerFile是用来构Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的副本
官网:docs.docker.com/engine/refe…
构建三步骤:
编写docekrfile文件
docker build 命令构建镜像
docker run 依照镜像运行容器实例
DockerFile构建过程解析
- DockerFile内容基础知识
-
每条保留字指令(关键字)都必须大写,且后面要跟随至少一个参数
-
指令按照从上到下,顺序执行
-
#表示注释
-
每条指令都会创建一个新的镜像层并对镜像进行提交
-
- Docker执行dockerfile的大致流程
-
docker从基础镜像运行一个容器
-
执行一条指令并对容器作出修改
-
执行类似docker commit的操作提交一个新的镜像层
-
docker再基于刚提交的镜像运行一个新的容器
-
执行dockerfile中的下一条指令直到所有指令都完成
-
- 从应用软件的角度来看,DockerFile,Docker镜像与Docker容器分别代表软件的三个不同阶段
-
DockerFile是软件的原材料
-
Docker镜像是软件的交付品
-
Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
-
DockerFile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系中的基石。
-
DockerFile定义了进程所需的一切东西。DockerFile涉及的内容包括执行代码或是文件、环境变量、依赖包、运行环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
-
Docker镜像:在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行Docker镜像时会真正开始提供服务;
-
Docker容器,容器是直接提供服务的。
-
- DockerFile常用保留字指令
指令 | 含义 |
---|---|
FROM | 基础镜像,第一条指令必须是from |
MAINTAINER | 镜像维护者的姓名和邮箱 |
RUN | 容器构建时需要运行的命令,有shell与exec两种格式,RUN是在docker build时运行 |
EXPOSE | 当前容器对外暴露的端口 |
WORKDIR | 指在创建容器后,终端默认登录进来的工作目录,一个落脚点 |
USER | 指定镜像以什么样的用户去执行,默认root |
ENV | 用来在构建镜像过程中设置环境变量(这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样,也可在其他指令中直接使用这些环境变量,例如: ENV MY_PATH /usr/mytest WORKDIR $MY_PATH # 指定登录目录) |
ADD | 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包,拷贝+解压 |
COPY | 类似ADD, 拷贝文件和目录到镜像中。COPY src dest(容器内的指定路径,不用事先建好) |
VOLUME | 容器数据卷,用于数据保存和持久化工作 |
CMD | 指定容器启动(run)之后要做的事情。DockerFile中可以有多个CMD指令,但只有最后一个会生效,且CMD会被docker run 后面加的参数覆盖掉 |
ENTRYPOINT | 也是指定一个容器启动时要运行的命令,类似于CMD, 但ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当做参数传送给EBNTRYPOINT指令指定的程序 |
- ENTRYPOINT详解
# 命令格式 ENTRYPOINT ["executeable", "param1", "param2", ... ] # ENTRYPOINT可以和CMD一起使用,一般是**变参**才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。 # 当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令,而是将CMD的内容作为参数传递给ENTRYPOINT指令。 # 它两个组合会变成<ENTRYPOINT>"<CMD>" # 案例如下, 假设已通过DockerFile构建了nginx:test镜像 From nginx ENTRYPOINT ["nginx", "-c"] # 定参 CMD ["/etc/nginx/nginx.conf"] # CMD的职责变为给ENTRYPOINT传递参数,该参数可被docker run 后面的参数覆盖 # 具体执行情况 docker run nginx:test # docker命令1 nginx -c /etc/nginx/nginx.cnf # docker命令1 衍生出来的在run后需要执行的命令 docker run nginx:test -c /etc/nginx/new.conf # docker命令2 nginx -x /etc/nginx/new.conf # docker命令2衍生出的在run后需要执行的命令,CMD被覆盖
案例
自定义镜像mycentosjava8
要求:Centos7镜像具备vim+ifconfig+jdk8
- 编写Dockerfile文件
FROM centos:7
MAINTAINER aoke
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络ip
RUN yum -y install net-tools
# 安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar.把jdk-8u361-linux-x64.tar.gz添加到容器中,安装包必>须要和Dockerfile文件在同一位置
ADD jdk-8u361-linux-x64.tar.gz /usr/local/java/
# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_361
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
-
构建
docker build -t 新镜像名字:TAG .
-
运行
docker run -it 新镜像名:Tag
-
回顾联合文件系统
由Dockerfile构建docker镜像的过程可知,每执行一条Dockerfile中的命令,就会生成一个镜像ID, 然后由镜像生成新的容器,再执行命令再次生成新的镜像,即联合文件系统中的层次结构。
-
微服务打包成docekr镜像Dockerfile实例
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为 /tmp, 在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 运行jar包
# touch命令的作用是修改这个文件的访问时间和修改时间为当前时间,而不会修改文件的内容
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java", "-jar", "/zzyy_docker.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
虚悬镜像
是什么: 构建镜像或者删除镜像时出现错误,导致仓库名、标签都是的镜像,俗称dangling image
- Dockerfile 构建虚线镜像
vim Dockerfile
from ubuntu
CMD echo 'action is success'
docker build .
- 查看所有虚悬镜像
- docker image ls -f dangling=true
- 删除虚悬镜像
- docker image prune
Docker network
从Docker架构和运行流程来看,Docker是一个C/S模式的架构,后端是一个松耦合架构,众多模块各司其职。
Docker 运行的基本流程为:
用户是使用Docker Client与Docker Daemon建立通讯,并发送请求给后者。
Docker Daemon 作为Docker架构中的主体部分,首先提供Docker Server的功能使其可以接受Docker Client的请求。
Docker Engine 执行Docker内部的一系列工作,每一项工作都是以一个Job的形式存在。
Job的运行过程中,当需要容器镜像时,则从Docker Registry 中下载镜像,并通过镜像管理驱动Graph driver将下载镜像以Graph的形式存储。
当需要为Docker创建网络环境时,通过网络管理驱动Network driver创建并配置Docker 容器网络环境。
当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成。
Libcontainer 是一项独立的容器管理包,Network driver以及Exec driver都是通过libcontainer来实现对容器进行的的操作。
是什么
- docker服务不启动时,虚拟机默认网络情况(ifconfig 或 ip addr命令)
- ens33 : Linux宿主机地址
- lo : 本地回环链路 127.0.0.1
- virbr0 : (虚拟网桥)在CentOS的安装过程中如果有选择相关虚拟化的服务安装系统后(如Linux图形化界面等),启动网卡时会发现有一个网桥连接的私网地址的virbr0网卡(virbr0网卡,它还有一个固定的默认IP地址192.168.122.1), 是做虚拟机网桥的使用的,其作用是为连接其上的虚拟网卡提供NAT访问外网的功能。我们之前学习Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直接将libvirtd服务卸载掉:
yum remove libvirt-libs.x86_64
- docker 服务启动后,会产生一个名为docker0的虚拟网桥
- 查看docker 网络命令模式: docker network ls, 会发现Docker安装后,默认创建的三个网络
- bridge
- host
- none
- 查看docker 网络命令模式: docker network ls, 会发现Docker安装后,默认创建的三个网络
基本常用命令
# 查看网络
docker network ls
# All命令
docker network --help
# 创建网络
docker network create xx网络名字
# 查看网络数据源
docker network inspect XXX网络名字
# 删除网络
docker network rm XXX网络名字
能干嘛
- 容器间的互联和通讯以及端口映射
- 容器IP变动时候可以通过服务名直接网络通讯而不受到影响
网络模式
- 总体介绍(四大网络模式)
- bridge : 为每个容器分 配、设置IP等,并将容器连接到一个docker0。虚拟网桥,默认为该模式。 使用 --network bridge指定,默认使用docker0
- host : 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 使用 --network host指定
- none : 容器有独立的Network namespace, 但并没有对其进行任何网络设置,如分配veth pair网桥连接, IP等。使用 --network none指定
- container : 新创建的容器不会创建自己的网卡和配置自己的IP, 而是和一个指定的容器共享IP、端口范围等。 使用 --network container:NAME或者容器ID指定
- 容器实例内默认网络IP生产规则
# 新建容器,并查看容器网络信息
docker run -it --name u1 ubuntu bash
docker run -it --name u2 ubuntu bash
# 查看u1 容器信息的后20行
docker inspect u1 | tail -n 20
# 显示如下内容
"Networks": {
"bridge": { # 桥接网络
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "14239fac83ad8499244187bb4eb72364d0c8f143a730f2310ab97bb0d4c6ecb2",
"EndpointID": "96a7838a3e419f6ff1d2fcf4fd838b1c11010e6e2a91ac0c23ecd5933d3b987a",
"Gateway": "172.17.0.1", # 网关
"IPAddress": "172.17.0.2", # IP, 查看u2, IP为172.17.0.3
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02"
}
}
# 当u2宕机以后,新启动一个u3, 其IP也为172.17.0.3
- 结论:docker容器内部的ip是可能会发生改变的,即通过172.17.0.3打算访问u2, 结果访问到了u3
案例说明
- bridge
Docker 服务默认会创建一个docker0网桥(其上有一个docker0内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚 拟网卡,这就将所有容器和本地主机都放到同一物理网络,Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通讯。
Docker 使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时,Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP通信。
docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network, eht0, eth1, .... 代表网卡一,网卡二, ...., lo代表127.0.0.1,即localhost, inet addr用来表示网卡的IP地址。
网桥docker0创建一对对等虚拟设备接口,一个叫veth,另一个叫eth0, 成对匹配。
整个宿主机的网桥模式都是docker0, 类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此连通(这样一对接口叫veth pair)
每个容器实例内部也有一块网卡,每个接口叫eth0;
docker0上面的每个veth匹配某个容器实例内部的eth0, 两两配对,一一匹配
通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从整个网关下各自拿到分配的ip,此时两个容器的网络是互通的。 一点备注:ip命令与ifconfig命令的区别:zhuanlan.zhihu.com/p/349584214, ip命令安装 apt-get install -y iproute2 或 yum install iproute
- host
直接使用宿主机的IP地址与外界进行通讯,不再需要额外进行NET转换。
容器将不会获得一个独立的Network Namesapce, 而是和宿主机共用一个Network Namespace。** 容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。**
docker run --d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
WARNING: Published ports are discarded when using host network mode
# docker 启动时总是遇见上述警告
# 原因:docker 启动时指定--network=host 或者 -net=host, 如果还指定了-p映射端口,那这个时候就会有此警告。
# 并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时递增。
# 解决:解决的办法就是使用docker的其他网络模式,例如--network=bridge, 这样就可以解决问题,或者直接无视....
# 正确的命令
docker run --d --network host --name tomcat83 billygoo/tomcat8-jdk8
# 查看容器实例内部情况,由于复用宿主机的IP, 因此网关与IP都为空
docker inspect tomcat83 | tail -n 20
# 查看容器内部网络配置情况,发现与宿主机配置几乎一样
ip addr
- none
禁用网络功能,之后lo表示(就是127.0.0.1表示本地回环)
在none模式下,并不为Docker容器进行任何网络配置
也就是说,这个Docker容器没有网卡、IP、路由等信息, 只有一个lo
需要我们在即为Docker容器添加网卡,配置IP等。
- container
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围等。同样,两个容器除了网络方面,其他的如文件系统,进程列表等还是隔离的。
# 创建tomcat85 容器
docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8
# 创建tomcat86容器,并复用tomcat85的网络配置
docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8
# 错误信息
docker: Error response from daemon: conflicating options: port publishing and the container type network mode.
# 相当于tomcat85和tomcat86共用一个ip一个端口,导致端口冲突
# 以ubuntu为例
docker run -it --network container:u1 --name u3 ubuntuvimif:1.0 /bin/bash
ip addr
# 显示网络配置与u1容器一致
# 停止u1容器后,u3容器只剩下lo回环地址
- 自定义网络
- 设置自定义网络之前
# 启动两个容器,并进入查看网络,网关均为172.17.0.1, u1 ip为 172.17.0.2, u2 ip为 172.17.0.3 docker run -it --name u1 --network bridge ubuntuvimif:1.0 /bin/bash docker run -it --name u2 --network bridge ubuntuvimif:1.0 /bin/bash # 在两个容器内部,通过ping ip可以ping通,但通过ping容器名显示Unknow host
- 设置自定义网络后
# 自定义桥接网络,自定义网络默认使用的是桥接网络bridge # 新建自定义网络 docker network create zzyy_network # 新建的容器加入新建的自定义网络 docker run -it --name u1 --network zzyy_network ubuntuvimif:1.0 /bin/bash docker run -it --name u2 --network zzyy_network ubuntuvimif:1.0 /bin/bash # 进入容器互ping 测试,通过容器名可以ping通,在开发中一定要写死服务名而不要写死ip
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能ping通)
Docker-compose容器编排
是什么,能干嘛
Docker-compose 是Docker官方的开源项目,负责对Docker容器的快速编排。可以管理多个容器组成一个应用。
需要定义一个YAML格式的配置文件,docker-compose.yml, 写好多个容器之间的调用关系,然后,只需要一个命令,就能同时启动/关闭这些容器。
docker建议我们每一个容器中只运行一个服务,因为Docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来,但是这样我们又面临了一个问题?
如果我们需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样也泰不裤辣,所以docker官方给我们提供了docker-compose多服务部署工具。
例如要实现一个Web微服务项目,除了Web服务容器本身外,往往还需要再加上后盾数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包含负载均衡容器等。、
Compose 允许用户通过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建,Docker-Compose解决了容器与容器之间如何管理编排的问题。
下载Version3
# 下载(老版本)
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 添加写权限
chmod +x /usr/local/bin/docker-compose
# 查看安装是否成功
docker-compose --version
# 卸载,若使用curl进行下载,则使用以下命令卸载
rm /usr/local/bin/docker-compose
# 新版
yum install docker-compose
docker-compose核心概念
- 一文件
- docker-compose.yml
- 两要素
- 服务(service): 一个个应用容器实例,比如订单微服务,库存微服务、Mysql容器、nginx容器或者redis容器
- 工程(project): 由一组关联的应用容器组成一个完整的业务单元, 在docekr-compose.yml中定义
Compose使用的三个步骤
- 编写Dockerfile 定义各个微服务应用并构建出对应的镜像文件。
- 使用docker-compose.yml 定义一个完整的业务单元,安排好整体应用中的各个容器服务。
- 最后,执行docker-compose up 命令来启动并运行整个应用程序,完成一键部署上线。(等价于一次性运行了多个docker run ... 命令)
常用命令
命令 | 含义 |
---|---|
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 | 展示当前docker-compose编排过的容器进程 |
docker-compose logs yml里面的服务id | 查看容器输出日志 |
docker-compose config | 检查配置 |
docker-compose config -q | 检查配置,有问题才输出 |
docker-compose restart | 重启服务 |
docker-compose start | 启动服务 |
docker-compose stop | 停止服务 |
Compose编排微服务
-
不用Compose编排会存在的问题
- 容器启动的先后顺序要固定,比如先mysql+redis启动后才能启动微服务
- 需多次执行run命令
- 容器间的启停或宕机,有可能导致IP地址对应的容器实例变化,映射出错,要么生产IP写死(可以但不推荐),要么通过服务调用。
-
使用Compose编排,doker-compose.yml文件内容
version: "3"
services:
microService:
image: zzyy_docker:1.6
container:_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- atguigu_net
depends_on:
- redis
- mysql
redis: # 在microService中(yml配置中)可直接调用 redis:6379
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- atguigu_net
command: redis-server /etc/redis/redis.conf
mysql: # 在microService中(yml配置中)可直接调用 mysql:3306
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2021'
MYSQL_USER: 'zzyy'
MYSQL_PASSWORD: 'zzyy123'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- atguigu_net
command: --default-authentication-plugin=mysql_native_password # 解决外部无法访问
networks:
atguigu_net: # 相当于docker network create atguigu_net
-
步骤
- 编写Dockerfile 文件,制作镜像
- 编写docker-compose.yml文件
- docker-compose up -d # 一键编排(可使用 docker-compose config -q 检查 yml文件,无任何输出则表示没有错误)
-
优势
- 操作更简便,不用一个一个地起服务(多次run)
- 可以通过服务名:端口拉访问服务,而不用写ip
-
关停
- docker-compose stop (一键停止全部的服务)
Docker轻量级可视化工具Potainer
系统越来越大时就需要解决 监控 与 统计, 因此使用Potainer来解决docker中的该需求
-
安装
- ww.potainer.io/
- docs.potainer.io/v/ce-2.9/st…
- docker 安装命令
docker run -d -p 8000:8000 -p 9000:9000 --name potainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer # --restart=always 持久存在,docker重启之后potainer也重启
-
使用
- 访问 宿主机IP:9000 进行注册
- 选择local-connect
- Stack显示有多少组编排的容器(dcoker-compose)
- 相当于以下命令的使用:
docker system df docker system events docker system prune docker system info
Docker 容器监控之CAdvisor+InfluxDB+Granfana(CIG)
# 监控容器状态
docker stats
-
存在问题
通过docker stats命令很方便看到当前容器宿主机上所有容器的CPU,内存以及网络流量等数据,一般小公司够用了
但是,docker stats 统计结果只能是当前宿主机的全部容器,数据资料是实时的,没有地方存储,没有健康指标过线预警等功能。
-
容器展示三剑客
- CAdvisor监控收集,+InfluxDB存储数据+Granfana展示图表
- CAdvisor: 展示Host和容器两个层次的监控数据,展示历史变化数据
- InfluxDB: 分布式时序、事件、指标数据库,无需外部依赖。主要功能:(1)基于时间序列:支持与时间有关的相关函数(如最大、最小、求和等);(2)可度量性:可以实时对大数据进行计算;(3)基于事件:可支持任意的事件数据
- Grafan:开源数据监控分析可视化平台
-
compose容器编排
- 编写docker-compose.yml 文件
version: "3.1" volumes: # grafana数据挂载的容器卷 grafana_data: {} services: influxdb: image: tutum/influxdb:0.9 container_name: ms01 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: - "8080: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 user: "104" restart: always links: - influxdb:influxsrv ports: - "3000: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
- 启动docker-compose
- docker-compose config -q (测试yml文件有没有问题,没有输出就没有问题)
- docker-compose up
- 测试
-
浏览cAdvisor收集服务:http://ip:8080/
-
浏览influxdb存储服务:http://ip:8083/
-
浏览grafana展现服务:http://ip:3000 (账号密码默认admin)
-
配置grafana连接influxdb(略)
-