docker--高级

案例--mysql一主一从

  • 创建mysql3307

  • 命令:

  • docker run -p 3307:3306 --name mysql-master
    -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
    -d mysql:5.7   
    
  • 创建目录----mkdir

  • 创建/mydata/mysql-master/conf/my.cnf文件

  • [mysqld]
    ##设置server_id,同一局域网需要唯一
    server_id=101
    ##指定不需要同步的数据库名称
    binlog-ignore-db=mysql
    #开启二进制日志功能
    log-bin=mall-mysql-bin
    ##开启二进制日志使用内存大小(事务)
    binlog_cache_size=1M
    ## 设置使用二进制日志格式(mixed,startment,row)
    binlog_format=mixed
    ## 二进制日志过期清理时间,默认值为0,表示不自动清理
    expire_logs_days=7
    ##跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端赋值中断
    slave_skip_errors=1062
    
    
  • 进入mysql-master容器

  • 使用mysql -uroot -proot命令进入mysql

  • 创建用户--- create user 'slave' @'%' IDENTIFIED BY '123456';

  • 执行GRANT REPLICATION SLAVE,REPLICATION CLIENT ON . TO 'slave'@'%';

  • 创建从机

  • docker run -p 3308:3306 -name mysql-slave
    -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
    -d mysql:5.7   
    
  • 创建my.cnf文件

  • [mysqld]
    ##设置server_id,同一局域网需要唯一
    server_id=102
    ##指定不需要同步的数据库名称
    binlog-ignore-db=mysql
    #开启二进制日志功能
    log-bin=mall-mysql-bin
    ##开启二进制日志使用内存大小(事务)
    binlog_cache_size=1M
    ## 设置使用二进制日志格式(mixed,startment,row)
    binlog_format=mixed
    ## 二进制日志过期清理时间,默认值为0,表示不自动清理
    expire_logs_days=7
    ##跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端赋值中断
    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
    
  • 进入master使用show master status;命令

  • image-20230224172148857

  • 参数说明

  • image-20230224172323976

  • change master to master_host='192.168.44.130',
    master_user='slave',master_password='123456',master_port=3307,master_log_file='mall-mysql-bin.000001',master_log_pos=154,master_connect_retry=30;
    
  • 从机执行上面命令

  • image-20230224175225149

  • 没开始主从复制

  • 在从数据库开启同步

  • start slave;

  • image-20230224175408671

image-20230224175733869

面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例

回答单机单台100%不可能,肯定是分布式存储,用redis如何落地?

三种解决方案:

哈希取余分区

  • 小厂--->哈希取余分区
  • image-20230225085454792

优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证在一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求)、起到负载均衡+分而治之的作用。

缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦了,不管扩缩,每次数据变动导致节点有点变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或者故障停机的情况下,原来的取模公式就会发生变化;Hash(key)/3会变成Hash(key)/?。此时地址经过某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌

一致性Hash算法分区

  • 中厂:一致性Hash算法分区
  • 能干什么:提出一致性解决方案。目的是当服务器荷属发生变动时,将量减少影响客户端到服务器的映射关系
  • 三大步骤:构建一致性hash环,服务器ip节点映射,key落到服务器的落键规则
  • image-20230225091732380
  • image-20230225092100791
  • image-20230225092304259
  • 优点
  • image-20230225092542551
  • image-20230225092846172
  • 缺点:一致性hash算法的数据倾斜问题
  • image-20230225093105419
  • 总结:
  • image-20230225093147620

哈希槽分区

image-20230225100935753

image-20230225101330561

image-20230225101626337

3主3从redis集群配置

image-20230225101947152

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

image-20230225103346509

image-20230225103515866

  • 进入容器内部

  • redis-cli --cluster create 192.168.44.130:6381 192.168.44.130:6382 192.168.44.130:6383 192.168.44.130:6384 192.168.44.130:6385 192.168.44.130:6386 --cluster-replicas 1
    
  • image-20230225134725563

image-20230225140110398

主从容错切换迁移案例

    1. 数据读写存储
    2. image-20230225143812639
    3. 原因通过hash算法当前key的槽不在6381
    4. 解决办法,连接时加上-c参数
    5. image-20230225144124622
    6. 检查集群:
    7. redis-cli --cluster check 192.168.44.130:6381
    8. image-20230225144950487
  • 容错切换迁移

  1. 扩容,原来的6台机器变成8台

  2. 新增主机6387无槽号

  3. 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
    
  4. 进入容器--docker exec -it redis-node-7 /bin/bash

  5. 将该节点加入集群----redis-cli --cluster add-node 192.168.44.130:6387 192.168.44.130:6381

  6. 6381是6388的领路人

  7. image-20230225161955180

  8. image-20230225162744327

  9. 重新分配卡槽

  10. redis-cli --cluster reshard IP地址:端口号

  11. redis-cli --cluster reshard 192.168.44.130:6381

  12. image-20230225163939560

  13. image-20230225164441968

  14. image-20230225164504543

  15. 为6387分配从节点6388

  16. 命令:redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID

  17. redis-cli --cluster add-node 192.168.44.130:6388 
    192.168.44.130:6387 --cluster-slave --cluster-master-id 11bb4628607887d3a992221c5bccedd6b8c48f32
    
  18. image-20230225170244714

  • 缩容

    1. 先删除从节点
    2. 重新分配槽点(原路返回,还是只给一个?)
    3. 删除主节点
    4. 恢复3主3从

    删除从节点

    命令:redis-cli --cluster del-node ip:从机端口 从机6388节点Id

    redis-cli --cluster del-node 192.168.44.130:6388 f0e50b122664267465f157f5aa76f29a5383f938 
    

    image-20230225173359699

    重新分配槽点

    redis-cli --cluster reshard 192.168.44.130:6381

    image-20230225174311234

    检查情况---
    image-20230225174414955

​ 删除主节点

image-20230225174510304

查看结果

image-20230225174600198

dockerfile

构建三步骤:编写dockerfile文件 ----->docker build 命令构建镜像 ------->docker run依镜像运行容器实例

image-20230225180003110

image-20230225180115780

  • RUN
  • image-20230228121138997
  • image-20230228121506807

image-20230228133204863

image-20230228133351694

构建自己的helloworld镜像

image-20230306111034074

image-20230306112325312

scratch镜像和ADD指令

scratch镜像

​ 在构建自己的镜像之前,首先要了解一个特殊的镜像 scratch。 scratch 镜像是一个空镜像,是所有镜像的 Base Image(相当于面向对象编程中的 Object 类)。scratch 镜像只能在 Dockerfile 中被继承,不能通过 pull 命令拉取,不能 run,也没有 tag。 并且它也不会生成镜像中的文件系统层。在 Docker 中,scratch 是一个保留字,用户不能作 为自己的镜像名称使用。

ADD镜像

【语法 1】ADD

【语法 2】ADD ["", ""] # 路径中存在空格时使用双引号引起来

【解析】该指令将复制当前宿主机中指定文件 src 到容器中的指定目录 dest 中。src 可以是

宿主机中的绝对路径,也可以时相对路径。但相对路径是相对于 docker build 命令所指定的

路径的。src 指定的文件可以是一个压缩文件,压缩文件复制到容器后会自动解压为目录;

src 也可以是一个 URL,此时的 ADD 指令相当于 wget 命令;src 最好不要是目录,其会将该

目录中所有内容复制到容器的指定目录中。dest 是一个绝对路径,其最后面的路径必须要加

上斜杠,否则系统会将最后的目录名称当做是文件名的。

构建centos镜像

image-20230306134724091

image-20230306134841923

image-20230306134927067

image-20230306135545800

·image-20230306135731928

#Dockerfile
FROM centos:7
MAINTAINER yhd 1451053372@qq.com
LABEL version="1.0" description="this is a centos7 image made in yhd" author="TOM" email="1451053372@qq.com"
ENV WORKPATH /usr/local
WORKDIR $WORKPATH
RUN yum install -y wget vim net-tools
CMD /bin/bash

悬虚镜像

image-20230306142335410

image-20230306142406075

发现出现悬虚镜像

如何删除悬虚镜像

docker rm -f 镜像id

docker system prune 删除四类无用文件,其中就包括悬虚镜像

docker image prune 用于删除悬虚镜像

CMD&ENTRYPOINT

CMD

  • CMD ["EXECUTABLE","PARAM1","PARAM2", ...]

  • 在容器启动后,即在执行完 docker run 后会立即调用执行"EXECUTABLE"指定的可执行文件,并使用后面第二、三等参数作为应用程序的运行参数。

  • CMD command param1 param2, ...

  • 这里的 command 就是 shell 命令。在容器启动后会立即运行指定的 shell 命令。

  • CMD ["PARAM1","PARAM2", ...]

  • 提供给 ENTERYPOINT 的默认参数。

举例:

#Dockerfile
FROM centos:7
CMD cal
#Dockerfile2
FROM centos:7
CMD ["cal"]
#Dockerfile3
FROM centos:7
CMD ["/bin/bash","-c","cal"]

tips:当文件名不是Dockerfile时,docker build需要加上-f指定文件名

在build命令 .指定文件路径,.为当前路径可以替换

image-20230307101215750

ENTRYPOINT

  • ENTRYPOINT ["EXECUTABLE","PARAM1","PARAM2", ...]

  • 在容器启动过程中,即在执行 docker run 时,会调用执行"EXECUTABLE"指定的应用

    程序,并使用后面第二、三等参数作为应用程序的运行参数

  • ENTRYPOINT command param1 param2, ...

  • 这里的 command 就是 shell 命令。在容器启动过程中,即在执行 docker run 时,会

    运行指定的 shell 命令。

#Dockerfile4
FROM centos:7
ENTRYPOINT cal

image-20230307102404430

#Dockerfile4
FROM centos:7
ENTRYPOINT ["cal"]

image-20230307102545858

FROM centos:7
ENTRYPOINT ["/bin/bash","-c","cal"]

image-20230307103131154

CMD&ENTRYPOINT联用

FROM centos:7
CMD ["hello world"]
ENTRYPOINT ["echo"]

image-20230307103748881

Dockerfile中的[command]或["EXECUTABLE"]如果是通过CMD指定的,则该镜像的启动命令docker run 中是不能添加参数[ARG]的,因为dockerfile中CMD是可以被[COMMAND]替代的,如果命令中的IMAGE后任有内容,此时对于docker daemon来说,其首先认为时替代用的[COMMAND],如果有两个或两个以上的内容,后面的内容才会认为时[ARG]。所以,添加的-y会报错,因为没有-y这个[COMMAND]

Dockerfile中的[command]或["EXECUTABLE"]如果是通过ENTRYPOINT指定的,则该镜像的启动命令docker run 中是可以添加参数[ARG]的,因为dockerfile中ENTRYPOINT不能被[COMMAND]替代的,如果命令中的IMAGE后任有内容,此时对于docker daemon来说,其只能是[ARG]。docker demon对于ENTRYPOINT指定的[command]与["ENTRYPOINT]的吃力方式是不同的,如果是shell,daemon会直接运行,而不会与docker run中的[ARG]拼接,如果是["EXECUTABLE"]指定的命令,daemon则会先与docker run中的[ARG]进行拼接然后再运行拼接后的结果。

结论:无论是CMD还是ENTRYPOINT,使用["EXECUTABLE"]方式的通用性会更强些。

ADD&COPY

  • ADD
  • ADD ["", ""] # 路径中存在空格时使用双引号引起来
  • 该指令将复制当前宿主机中指定文件 src 到容器中的指定目录 dest 中。src 可以是宿主机中的绝对路径,也可以时相对路径。但相对路径是相对于 docker build 命令所指定的路径的。src 指定的文件可以是一个压缩文件,压缩文件复制到容器后会自动解压为目录;src 也可以是一个 URL,此时的 ADD 指令相当于 wget 命令;src 最好不要是目录,其会将该目录中所有内容复制到容器的指定目录中。dest 是一个绝对路径,其最后面的路径必须要加上斜杠,否则系统会将最后的目录名称当做是文件名的。
  • COPY
  • 【说明】功能与 ADD 指令相同,只不过 src 不能是 URL。若 src 为压缩文件,复制到容器后
  • 不会自动解压.

onbuild

  • 【语法】ONBUILD [INSTRUCTION]

  • 【解析】该指令用于指定当前镜像的子镜像进行构建时要执行的指令。

    #Dockerfile
    FROM centos:7
    ENV WORKPATH /usr/local
    WORKDIR $WORKPATH
    ONBUILD RUN yum install -y wget
    CMD /bin/bash
    

    image-20230307112610853

    #Dockerfile2
    FROM parent:1.0
    
    

image-20230307112717685

可以构建出新的镜像的方式有:

  • docker build
  • docker commit
  • docker import(注意,docker load 并没有构建出新的镜像,其与原镜像是同一个镜像)
  • docker compose
  • docker hub 中完成 Automated Builds

制作自己项目的镜像

FROM openjdk:8u102
LABEL author="yhd" email="1451053372@qq.com" version="1.0"
#复制原jar包到根目录
COPY demo.jar hd.jar
#执行java-jar
ENTRYPOINT ["java","-jar","hd.jar"]
EXPOSE 8080

  • image-20230307232320475

image-20230307232336325

镜像生成过程

FROM centos:7
LABEL auth="Tom"
COPY hello.log /var/log/
RUN yum -y install vim
CMD /bin/bash
  • 镜像的生成过程
  • 为了了解什么是 build cache,理解镜像构建过程中 build cache 机制,需要先了解镜像的生成过程。Docker 镜像的构建过程,大量应用了镜像间的父子关系。即下层镜像是作为上层镜像的父镜像出现,下层镜像是作为上层镜像的输入出现;上层镜像是在下层镜像的基础之上变化而来。下面将针对上面的例子逐条指令的分析镜像的构建过程。
  • 前提知识
  • 每一个镜像都由镜像文件系统和json文件组成,镜像id由这两个通过sha256算法生成,也就是说如果产生新的镜像id,肯定是文件系统或者json发生该改变
  • FROM centos:7
  • ​ FROM 指令是 Dockerfile 中唯一不可缺少的指令,它为最终构建出的镜像设定了一个基础镜像(Base Image)。该语句并不会产生新的镜像层,它是使用指定的镜像作为基础镜像层的。docker build 命令解析 Dockerfile 的 FROM 指令时,可以立即获悉在哪一个镜像基础上完成下一条指令镜像层构建。对于本例,Docker Daemon 首先从 centos:7 镜像的文件系统获取到该镜像的 ID,然后再根据镜像 ID 提取出该镜像的 json 文件内容,以备下一条指令镜像层构建时使用。
  • LABEL auth="Tom"
  • ​ LABEL 指令仅修改上一步中提取出的镜像 json 文件内容,在 json 中添加 LABEL auth="Tom",无需更新镜像文件系统。但也会生成一个新的镜像层,只不过该镜像层中只记录了 json 文件内容的修改变化,没有文件系统的变化。如果该指令就是最后一条指令,那么此时形成的镜像的文件系统其实就是原来 FROM 后指定镜像的文件系统,只是 json 文件发生了变化。但由于 json 文件内容发生了变化,所以产生了新的镜像层。
  • COPY hello.log /var/log/
  • ​ COPY 指令会将宿主机中的指定文件复制到容器中的指定目录,所以会改变该镜像层文件系统大小,并生成新的镜像层文件系统内容。所以 json 文件中的镜像 ID 也就发生了变化,产生了新的镜像层。
  • RUN yum -y install vim
  • ​ RUN 指令本身并不会改变镜像层文件系统大小,但由于其 RUN 的命令是 yum install,而该命令运行的结果是下载并安装一个工具,所以导致 RUN 命令最终也改变了镜像层文件系统大小,所以也就生成了新的镜像层文件系统内容。所以 json 文件中的镜像 ID 也就发生了变化,产生了新的镜像层。
  • CMD /bin/bash
  • ​ 对于 CMD 或 ENTRYPOINT 指令,其是不会改变镜像层文件系统大小的,因为其不会在docker build 过程中执行。所以该条指令没有改变镜像层文件系统大小。但对于 CMD 或 ENTRYPOINT 指令,由于其是将来容器启动后要执行的命令,所以会将该条指令写入到 json 文件中,会引发 json 文件的变化。所以 json 文件中的镜像 ID 也就发生了变化,产生了新的镜像层。

build cache

修改dockerfile

FROM centos:7
LABEL auth="Tom"
COPY hello.log /var/log/
RUN yum -y install vim
CMD /bin/bash
EXPOSE 9000

image-20230308101611451

删除镜像时,只有当最后一个镜像被删除时,build cache才会被删除

  • build cache 机制

  • Docker Daemnon 通过 Dockerfile 构建镜像时,当发现即将新构建出的镜像(层)与本地已存在的某镜像(层)重复时,默认会复用已存在镜像(层)而不是重新构建新的镜像(层),这种机制称为 docker build cache 机制。该机制不仅加快了镜像的构建过程,同时也大量节省了Docker 宿主机的空间。docker build cache 并不是占用内存的 cache,而是一种对磁盘中相应镜像层的检索、复用机制。所以,无论是关闭 Docker 引擎,还是重启 Docker 宿主机,只要该镜像(层)存在于本地,那么就会复用。

  • build cache 失效

    docker build cache 在以下几种情况下会失效。

    Dockerfile 文件发生变化

    ​ 当 Dockerfile 文件中某个指令内容发生变化,那么从发生变化的这个指令 层开始的所有镜像层 cache 全部失效。即从该指令行开始的镜像层将构建出新的镜像层,而不再使用 build cache,即使后面的指令并未发生化。因为镜像关系本质上是一种树状关系,只要其上层节点变了,那么该发生变化节点的所有下层节点也就全部变化了。

    ADD COPY 指令内容变化

    ​ Dockerfile 文件内容没有变化,但 ADD 或 COPY 指令所复制的文件内容发生了变化,同样会使从该指令镜像层开始的后面所有镜像层的 build cache 失效。

    RUN 指令外部依赖变化

    ​ 与 ADD/COPY 指令相似。Dockerfile 文件内容没有变化,但 RUN 命令的外部依赖发生了变化,例如本例中要安装的 vim 软件源发生了变更(版本变化、下载地址变化等),那么从发生变化的这个指令层开始的所有镜像层 cache 全部失效。

    指定不使用 build cache

    有些时候为了确保在镜像构建过程中使用到新的数据,在镜像构建 docker build 时,通

    过--no-cache 选项指定不使用 build cache。

    image-20230308104509858

    清理 dangling build cache

    ​ dangling build cache,即悬虚 build cache,指的是无法使用的 build cache。一般为悬虚镜像 dangling image 所产生的 build cache。通过 docker system prune 命令可以清除。

    image-20230308104547176

dockerfile持久化

#Dockerfile
FROM centos:7
VOLUME /opt/xxx /opt/ooo
CMD /bin/bash

构建镜像 build

使用 Dockerfile 构建镜像 volscon:latest。

image-20230308111555678

有个疑问?我们没有指定宿主机的数据卷

概念:持久化中容器内叫做挂载点,宿主机叫数据卷

通过查看docker inspect发现数据卷时docker daemo自动生成的;

总结

定制镜像持久化

​ 理解定制镜像持久化方式针对的应用场景:对 UnionFS 产生一次性变化,且这种变化易于持久化为镜像。不适合变更非常频繁的实时性场景。

数据卷持久化

需要理解两点:

​ 数据卷属于宿主机文件系统,与 UnionFS 是挂载关系,与容器挂载点是硬链接关系,容器的存在与否与数据卷没有关系

​ 数据卷持久化可以做到对容器变更的实时性持久化,不会出现数据丢失的情况

Dockerfile 持久化

理解 docker run –v 方式与 Dockerfile 的 VOLUME 指令方式指定数据卷的区别:

​ docker run –v 方式是针对已经存在的镜像的,而 Dockerfile 的 VOLUME 指令方式是针对

准备创建的镜像的。

​ docker run –v 方式可以指定宿主机中的数据卷目录,而 Dockerfile 的 VOLUME 指令方式无法指定宿主机中的数据卷目录,其是 Docker Daemon 自动分配的。所以对于用户来说,通过 docker run –v 方式来指定数据卷会更为清晰明了些

posted @   我是一个大废物  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示