Docker笔记-进阶

119 阅读32分钟

Docker 复杂安装详说

安装mysql主从复制

  1. 新建主服务器容器实例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
  1. 进入修改/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
  1. 修改完配置后重启master实例
    • docker restart mysql-master
  2. 进入master容器
    • docker exec -it mysql-master /bin/bash
  3. master容器实例内创建数据同步用户
    • create user 'slave'@'%' identified by '123456'; # 创建slave用户可以通过123456登录master主机
    • grant replication slave, replication client on . to 'slave'@'%'; 授予权限,可拿到对应的数据
  4. 新建从服务器
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
  1. 进入/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
  1. 修改完后重启slave实例
    • docker restart mysql-slave
  2. 在主数据库中查看主从同步状态
    • show master status
  3. 进入mysql-slave容器
    • docker exec -it mysql-slave /bin/bash
    • mysql -u root -p
    • root
  4. 在从数据库中配置主从复制
    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: 连接失败重试的时间间隔,单位为秒
    
  5. 在从数据库中查看主从同步状态
    • show slave status \G;
    • \G 是以key val键值对的形式显示
    • Slave-IO-Runing 以及Slave-SQL-Runing会显示No
  6. 在从数据库中开启主从同步
    • start slave;
    • Slave-IO-Runing 以及 Slave-SQL-Runing 会显示Yes
  7. 主从复制测试
    • 主机新建库-使用库-新建表-插入数据
    • 从机使用库,查看记录

安装redis集群

大厂面试题-分布式存储案例真题

  1. cluster(集群)模式-docker版 哈希槽分区进行亿级数据存储

    • 面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例(抖音观看流水,转账记录,浏览记录等)
    • 回答:单机单台100%不可能,肯定是分布式存储,用redis如何落地?
    1. 哈希取余分区
      • 2亿条记录就是2亿可k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
      • 优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如分3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求信息),起到负载均衡+分而治之的作用。
      • 缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/?,此时地址经过某个redis机器宕机了,由于台数数量发生变化,会导致Hash取余全部重新洗牌。
    2. 一致性哈希算法分区
      • 背景:一致性哈希在1997年由麻省理工学院提出,设计目标是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,凄然取余数就不OK了。
      • 能干嘛:提出一致性hash解决方案。目的是当服务器个数发生变动时,尽量减少影响到客户端到服务器端的映射关系。
      • 3大步骤
        1. 算法构建一致性哈希环:(所有存储节点排列在收尾相接的hash环上)一致性hash算法是对2^32次方取模,简单来说,一致性hash算法将整个哈希值空间组织成一个虚拟的圆环,0~2^32 - 1 个点组成的圆环称为hash环。
        2. 服务器IP节点映射:将集群中的各个IP节点映射到环上的某一个位置。具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
        3. key落到服务器的落键规则:当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),hash()与IP映射的哈希函数相同,然后,从hash(key)位置出发,沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值存储在该节点上。
      • 优点
        • 一致性哈希算法解决了哈希取余算的容错性和扩展性问题。,加入和删除节点只影响哈希环中的顺时针方向的相邻节点,对其他节点无影响。
      • 缺点
        • 一致性算法数据倾斜问题:一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜问题。例如系统中只有两台服务器时,由于服务器映射节点在哈希环上分布不均匀,导致被缓存的对象大部分集中缓存在某一台服务器上。
    3. 哈希槽分区(hash slot)
      • 为什么出现: 为了解决一致性哈希算法的数据倾斜问题
      • 能干什么
        1. 解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上方的是槽,槽中方的是数据。
        2. 槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动
        3. 哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
      • 多少个hash槽
        • 一个redis集群只能有16384个槽,编号0-16383(0-2^14 - 1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几,key就落入对应的槽里。slot=CRC16(key) % 16384。以槽为单位移动数据。因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
  2. 搭建3主3从redis集群

    1. 新建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 #端口号
    
    1. 进入容器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.
    
    1. 链接进入6381作为切入点,查看集群状态
      • redis-cli -p 6381
      • cluster info
      • cluster nodes
  3. 主从容错切换迁移案例

    1. 数据读写存储
    # 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
    
    1. 主从容错切换迁移
    # 停止主机6381, 其从机6384会变为新的主机,并集继承6381之前存储的数据
    docker stop redis-node-1
    
    # 此时若主机6381恢复,其会变为6384的从机
    docker start redis-node-1
    
  4. 主从扩容案例(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. 检查集群情况
    
  5. 主从缩容案例(删除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…

构建三步骤:

  1. 编写docekrfile文件

  2. docker build 命令构建镜像

  3. docker run 依照镜像运行容器实例

DockerFile构建过程解析

  1. DockerFile内容基础知识
    • 每条保留字指令(关键字)都必须大写,且后面要跟随至少一个参数

    • 指令按照从上到下,顺序执行

    • #表示注释

    • 每条指令都会创建一个新的镜像层并对镜像进行提交

  2. Docker执行dockerfile的大致流程
    • docker从基础镜像运行一个容器

    • 执行一条指令并对容器作出修改

    • 执行类似docker commit的操作提交一个新的镜像层

    • docker再基于刚提交的镜像运行一个新的容器

    • 执行dockerfile中的下一条指令直到所有指令都完成

  3. 从应用软件的角度来看,DockerFile,Docker镜像与Docker容器分别代表软件的三个不同阶段
    • DockerFile是软件的原材料

    • Docker镜像是软件的交付品

    • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例

    • DockerFile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系中的基石。

    • DockerFile定义了进程所需的一切东西。DockerFile涉及的内容包括执行代码或是文件、环境变量、依赖包、运行环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。

    • Docker镜像:在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行Docker镜像时会真正开始提供服务;

    • Docker容器,容器是直接提供服务的。

  4. 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 运行的基本流程为:

  1. 用户是使用Docker Client与Docker Daemon建立通讯,并发送请求给后者。

  2. Docker Daemon 作为Docker架构中的主体部分,首先提供Docker Server的功能使其可以接受Docker Client的请求。

  3. Docker Engine 执行Docker内部的一系列工作,每一项工作都是以一个Job的形式存在。

  4. Job的运行过程中,当需要容器镜像时,则从Docker Registry 中下载镜像,并通过镜像管理驱动Graph driver将下载镜像以Graph的形式存储。

  5. 当需要为Docker创建网络环境时,通过网络管理驱动Network driver创建并配置Docker 容器网络环境。

  6. 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成。

  7. 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 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

官网:docs.docker.com/compose/com…

下载地址 :docs.docker.com/compose/ins…

# 下载(老版本)
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中的该需求

  • 安装

    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
    • 测试