镜像查看版本 查看镜像的详细信息_mysql



CMD与EntryPoint实战

        EntryPoint 与 CMD都是docker 镜像制作中的一条命令,它们在概念上可能有些相似,但在使用中,两者是有明显的区别的。比如,执行一个没有调用EntryPoint、CMD的容器会返回错误,这两条命令一般都作为容器启动的入口命令~

🎨 覆盖        

        编写Dockerfile时,一旦使用了EntryPoint、CMD命令都会覆盖之前默认的EntryPoint、CMD命令集。我们在Docker镜像运行时,也可以通过携带 “命令+参数”,覆盖CMD;如果设置了EntryPoint,这些命令集又会被当成参数,喂给EntryPoint作为参数。或是携带 ”--entrypoint“用于覆盖EntryPoint中的命令。

        如果你只希望Docker容器在运行时,只执行一个具体的程序,建议使用EntryPoint。

🎨 Shell 与 EXEC模式

        ENTRYPOINT 与 CMD指令支持两种不同的写法: shell 、 EXEC

CMD指令写法:

# EXEC FORM
CMD ["excutable","param1","param2"]
# 用于给 EntryPoint传输参数
CMD ["param1","param2"]

# shell FORM
CMD command param1 param2

ENTRYPOINT指令写法:

# EXEC FORM
ENTRYPOINT ["exutable","param1","param2"]

# Shell FORM
ENTRYPOINT command param1 param2

使用shell表示法,言外之意。这些命令终将是喂给 shell程序来执行的!

即 —— docker使用:  /bin/sh -c 的语法调用


使用EXEC语法,不会启动 /bin/sh,而是直接运行命令,该命令PID=1

        使用docker ps就可以看到实际运行的命令模式~ 

镜像查看版本 查看镜像的详细信息_mysql_02

”EXEC表示法”。

🎨 组合模式

        组合使用ENTRYPOINT 与 CMD时,ENTRYPOINT作为默认的运行命令,CMD指定运行参数。当ENTRYPOINT与CMD同时存在时,docker会把CMD中的命令都拼接在ENTRYPOINT之后,并最终执行命令~       

镜像查看版本 查看镜像的详细信息_mysql_03

实战步骤

💎 多次覆盖

        我们创建一个Dockerfile文件,指定多个CMD,如下:

镜像查看版本 查看镜像的详细信息_镜像查看版本_04

        构建镜像,查看运行结果,我们发现三条命令中,只打印了一句~

镜像查看版本 查看镜像的详细信息_mysql_05

        我们继续创建第二个 dockerfile2,指定多个EntryPoint:

镜像查看版本 查看镜像的详细信息_docker_06

        构建、运行镜像:

镜像查看版本 查看镜像的详细信息_redis_07

        

💎 参数覆盖

        我们通过指定后面启动的参数,可以覆盖CMD的指令,但却无法对ENTRYPOINT进行覆盖~

镜像查看版本 查看镜像的详细信息_mysql_08

镜像查看版本 查看镜像的详细信息_docker_09

        当我们指定 --entrypoint时,才会覆盖dockerfile中的ENTRYPOINT默认命令:

镜像查看版本 查看镜像的详细信息_docker_10

        

💎 Shell vs EXEC

        我们编写Dockerfile,让容器启动时,自动运行ping命令~

镜像查看版本 查看镜像的详细信息_docker_11

        编译运行镜像,进入镜像之中,查看进程ID,我们可以看到PID为1的进程为 /bin/sh。

镜像查看版本 查看镜像的详细信息_镜像查看版本_12

        我们新建Dockerfile4,并采用EXEC的模式:

镜像查看版本 查看镜像的详细信息_mysql_13

        编译运行镜像后,我们进入到容器中查看进程PID,此时发现PID为1的进程变为PING~

镜像查看版本 查看镜像的详细信息_docker_14

        使用entrypoint指令也是同样的结果,这里也就不再过多实验~

💎 组合

        我们新建Dockerfile5,同时设置ENTRYPOINT、CMD:

镜像查看版本 查看镜像的详细信息_mysql_15

        此时,我们编译镜像+运行镜像看看是什么效果:

镜像查看版本 查看镜像的详细信息_mysql_16

        其中CMD中的内容,作为ENTRYPOINT的参数,添加到了后面~

        又因为,我们可以在docker run启动时,通过命令行替换CMD中的内容,所以,我们又可以进行如下的执行:

镜像查看版本 查看镜像的详细信息_mysql_17


Dockerfile搭建Mysql主从集群

build功能

        在 docker-cmpose.yml文件中,使用build选项 编译镜像:

# 方式一
services:
  frontend:
    image: awesome/webapp
    build: ./webapp
# 注解:
该镜像的构建,是由 "awesome"的子目录“webapp"决定。
如果该文件(webapp) 缺少dockerfile 就会发生报错


# 方式二
  backend:
    image: awesome/database
    build: 
      context: ./backend
      dockerfile: ./backend.dockerfile

# 注解:
"./backend"作为镜像构建的上下文,这个"./backend"需要是一个子目录
"./backend.dockerfile" 与之是同级目录,其路径与"./backend"是相关的都是子目录

Mysql主从同步原理

什么是Mysql的主从同步? 

        所谓主从 —— 即一个主节点,多个从节点的模式。在Mysql中,主节点的Mysql服务器上的数据可以通过一定的方式,“同步复制”给其他从节点,从而保证主、从节点数据的一致性。

异步复制的方式,这样的好处在于,从节点不用频繁地找主节点更新拷贝新数据,数据的更新可以放在连接上。

为什么需要Mysql主从同步?

🎯 读写分离,性能提升: 让主库负责写,从库负责读。这样当主库进行写触发锁机制时,因为有从库的存在,也不会停止提供读服务。

🎯 数据实时备份: 主数据库实时保存,当主节点突然宕机、挂掉,主库可以去从库哪里找到历史数据~

🎯 高可用: 某个节点发送故障,仍然有其他节点提供服务~不会致使整个服务瘫痪~

主从同步架构

        在主从复制中存在3个线程用来执行这个过程,一个是"binlog dump thread",该线程位于master节点上,另外两个线程分别为 I/O 、SQL线程,它们分别存在于从节点上。

镜像查看版本 查看镜像的详细信息_mysql_18

同步过程:

🎃 当master接收到一个写请求(增\删除\改),这些操作都会被记录进 binlog 之中.

🎃 master节点会为每一个 slave节点(前提是,slave节点连接到了master节点上),分别创建一个 线程(binlog dump thread),并将binlog中的内容通过线程发送给各slave节点。 

🎃 binlog dump thread线程会 "互斥地"(加锁)读取master节点上的binlog日志,并将该日志信息发送到 slave节点的 I/O上

🎃 slave节点的 I/O接收到binlog日志信息后,会将其存放到本节点上的 relaylog中~

🎃 slave启用SQL THREAD,前去读取relaylog中的内容,将其具体解析成执行的SQL,并执行这些SQL,实现某种意义上的还原~

        从而实现一种 主从节点数据上,一致性的现象。

Binlog

        Binlog本质上虽然是一个二进制文件,但其内部存储的是一个一个的事件~ 所谓的事件就是指使用数据库过程中产生的各个SQL 指令: INSERT、UPDATE、DELETE等等。

        主库每提交一次”事务“(即,一组持有原子性、持久性、隔离性、一致性语句的逻辑),都会把数据进行变更,记录到一个 二进制文件之中 —— binlog。

参数值

含义

缺点

Statement

记录原始的SQL语句

SQL中包含了每次执行结果不一致的函数、触发器时,同步数据时会造成不一致

row

记录了数据被更改的具体值

每条数据的更改被详细记录,如整表删除,alter表等操作涉及的数据行都会记录,ROW格式会产生大量日志。

mixed

混合模式以上两种格式的混合版

无法对误操作数据进行单独恢复。

主从同步方式

💰 全同步方式: 

        当主库处理执行完一个事务之后,要求所有的从库也必须执行完该事务,才可以继续返回处理客户端的请求。这样虽然能够极大程度上保障主从节点数据的一致性,但却带来的是请求处理性能的损耗,从库宕机也会对主库产生影响。

镜像查看版本 查看镜像的详细信息_镜像查看版本_19

💰 异步同步方式: 

        这是Mysql默认采取的主从同步方式。主库在执行完客户端提交的事务之后,立即返回结果给客户端,并不关心从库,是否已经同步完成 新增数据信息~

        所以,这必然会在某一个周期时间内,主从库数据会产生不一致的问题。而且,一旦主库宕机挂掉,此时的binlog可能还没有发送新提交事务的信息,就会产生数据丢失问题。由此,异步同步方式虽然性能上比 同步方式下更优,但是数据安全、一致性问题上显得欠缺~

镜像查看版本 查看镜像的详细信息_mysql_20

💰 半同步方式: 

反馈机制。

        在Mysql5.7版本中,又新增一个参数: "rpl_semi_sync_master_wait_for_slave_count"。这个参数是用来干嘛的呢 ? 你可以把它类似于一种投票机制,默认设置为1。也就是说,一旦一个从库进行了响应,那么就可以告诉主节点,可以返回给客户端了。 当这个参数设置得越大,也就说明需要从库进行确认的个数越多,更大程度上地提升数据一致性的强度,但也会增加咱们主库等待ACK响应的时延~

镜像查看版本 查看镜像的详细信息_mysql_21

       不过,半同步方式也存在一系列的问题:

⌛ 性能较低: 异步复制一旦客户端进行commit提交,立马就能得到返回响应。但,半同步复制则需要等待至少一个库发送 确认收到relaylog后,才能进行返回客户端。

⌛ 主库等待的最大时长可以进行配置的,一旦超过了配置的时间,半同步复制就会演变为异步复制,异步复制的问题也会显现~

⌛ 半同步复制还会存在幻读问题!!

所谓幻读,其本质就是一种 “不可重复读“问题的一种~

主要针对的是在执行了Insert 插入语句后,可能导致的事务前后查询数据 不一致的问题

💰 增强半同步方式: 

        看这个方式的名字也就知晓,这是半同步复制的一种改进,原理上几乎与半同步复制一样,但解决了其遗留的幻读问题!

镜像查看版本 查看镜像的详细信息_redis_22

        其核心在于:

        主库配置了新的参数 "rpl_semi_sync_mater_wait_point=AFTER_SYNC"。现在的主库不再将写入binlog中的内容,立即同步给 ”存储引擎“,而是直到收到Slave的relay log的ACK后,才能进行提交存储引擎,完成向客户端的请求反馈、处理。

💰 组复制:

        Mysql官方在5.7.17版本中,正式推出组复制(Mysql Group Replication) —— MGR。

        由若干个节点共同组织成一个组,一个事务的提交,必须经由绝大多数组内节点的确认  —— (N / 2 + 1)。例如,如果是由3个Mysql服务器共同组成的组复制,在事务提交的过程中,至少需要2个节点决议,是否通过这个提交决议~

        引入组复制,根本上是为解决传统异步复制、半同步复制存在的数据不一致性的问题。

        不过,MGR的解决方案是也有一定的局限,如仅由Innodb表能够支持,并且对表的结构也有一顶的要求……

镜像查看版本 查看镜像的详细信息_镜像查看版本_23

Mysql主从形式

👑 一主一从(多):

镜像查看版本 查看镜像的详细信息_容器_24

镜像查看版本 查看镜像的详细信息_镜像查看版本_25

👑 双主复制:

镜像查看版本 查看镜像的详细信息_redis_26

        每个master都是对端master的slave,任何一方做变更,都会复制应用到另一方。 

👑 级联复制:

镜像查看版本 查看镜像的详细信息_镜像查看版本_27

        级联复制下,部分slave的数据同步不跟随主节点,而是连接的从节点。通过增加replication层,可以缓解主节点replication产生的性能损耗,对数据一致性也没什么负面影响,但数据同步时的时延性会增加~ 

Mysql主从集群搭建

搭建步骤:

⛳ 创建主库,并在主库中创建单独的Mysql用户,用于数据的同步,授予该用户所有权限。

⛳ 创建从库,配置从库的数据,同步到主库。

⛳ 启动从库,开始同步。

配置Mysql文件 + Dockerfile:

        咱们选择的主从模式是一主多从,创建一个主节点两个从节点:

镜像查看版本 查看镜像的详细信息_mysql_28

        创建主库的配置sql脚本:

# 创建新用户
CREATE USER 'root'@'%' IDENTIFIED BY 'root';
# 让"root"授予访问 slave1 slave2 的所有权限
grant replication slave1,replication slave2 on *.* to 'root'@'%';
flush privileges;

        创建从库的配置sql脚本: 

change master to master_host='mysql-server';
# 填写主节点的用户信息
master_user='root',master_password='root',master_port=3306;
start slave;

        进入"mysql-cluster-master"目录下,创建并配置主库Dockerfile文件 —— "Dockerfile-master":

         这里本质上就是将Mysql初始化库的启动命令更换为了咱们写的sql语句。并且进行了时间同步,可以看到Linux系统中有许多地区、城市的时间信息~

镜像查看版本 查看镜像的详细信息_mysql_29

FROM mysql:5.7
RUN ls /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 
COPY ./master/master.sql /docker-entrypoint-initdb.d

        还有Dockerfile-slave:

FROM mysql:5.7
RUN ls /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 
COPY ./slave/slave.sql /docker-entrypoint-initdb.d

        

编写docker-compose.yml 统一编排容器

        我们进入到 "/root/mysql-cluster/",创建.yml文件:

version: "3.6"
services:
  mysql-master:
    build: 
      context: ./ 
      dockerfile: ./master/Dockerfile-master
    image: mysqlmaster:v1.0
    restart: always
    container_name: mysql-master
    volumes: 
      - ./mastervarlib:/var/lib/mysql
    ports: 
      - 8080:3306
    environment: 
      MYSQL_ROOT_PASSWORD: root
    privileged: true
    command: ['--server-id=1',
            '--log-bin=master-bin',
            '--binlog-ignore-db=mysql',
            '--binlog_cache_size=256M',
            '--binlog_format=mixed',
            '--lower_case_table_names=1',
            '--character-set-server=utf8',
            '--collation-server=utf8_general_ci']

  mysql_slave:
    build: 
      context: ./ 
      dockerfile: ./slave/Dockerfile-slave
    image: mysqlslave:v1.0
    restart: always
    container_name: mysql-slave
    volumes:
      - ./slavevarlib:/var/lib/mysql
    ports:
      - 8081:3306
    environment:
      - MYSQL_ROOT_PASSWORD=root
    privileged: true
    command: ['--server-id=2',
              '--relay-log=slave-relay',
              '--lower_case_table_names=1',
              '--character-set-server=utf8',
              '--collation-server=utf8_general_ci']  
    depends_on:
      - mysql-master

  mysql_slave2:
    # build: 
    #   context: ./ 
    #   dockerfile: ./slave/Dockerfile-slave
    image: mysqlslave:v1.0
    restart: always
    container_name: mysql-slave2
    volumes:
      - ./slavevarlib2:/var/lib/mysql
    ports:
      - 8082:3306
    environment:
      - MYSQL_ROOT_PASSWORD=root
    privileged: true
    command: ['--server-id=3',
              '--relay-log=slave-relay',
              '--lower_case_table_names=1',
              '--character-set-server=utf8',
              '--collation-server=utf8_general_ci']  
    depends_on:
      - mysql-master

镜像查看版本 查看镜像的详细信息_docker_30

        我们使用 "docker compose config" 检查.yml是否编写规范~

镜像查看版本 查看镜像的详细信息_mysql_31

        没问题后,我们就可以选择构建镜像了:

镜像查看版本 查看镜像的详细信息_mysql_32

        启动服务进行测试,这些容器能够正常启动~

镜像查看版本 查看镜像的详细信息_docker_33

        连接上主库,查看数据库~

镜像查看版本 查看镜像的详细信息_docker_34

                查看数据库角色、同步状态、连接上的从库信息等~

SHOW MASTER STATUS\G
SHOW SLAVE STATUS\G

master:

镜像查看版本 查看镜像的详细信息_redis_35

slave:       

镜像查看版本 查看镜像的详细信息_mysql_36

        在主库上创建数据:

镜像查看版本 查看镜像的详细信息_docker_37

        查看从库上数据的同步写入~

镜像查看版本 查看镜像的详细信息_镜像查看版本_38

        我们可以瞧见,最终完成了同步~


Dockerfile搭建Redis主从集群

修改redis.conf文件

        我们可以在Windows上先下载redis7.x的源码: Download | Redis

镜像查看版本 查看镜像的详细信息_镜像查看版本_39

        亦或是在linux机器下,使用wget命令: 

        https://codeload.github.com/redis/redis/tar.gz/refs/tags/7.2.4

镜像查看版本 查看镜像的详细信息_镜像查看版本_40

        准备目录,将下载的redis源码放在新建目录中:

镜像查看版本 查看镜像的详细信息_redis_41

        找到redis中的配置文件模板,完成以下redis.conf内容的修改,把该文件继续放在新建目录("/data/wgzzs/rediscluster/redis"):

镜像查看版本 查看镜像的详细信息_容器_42

镜像查看版本 查看镜像的详细信息_mysql_43

编写docker-file 

        在"/data/wgzzs/rediscluster/redis"编写Dockerfile-redis文件,用于构建自己的redis镜像~

镜像查看版本 查看镜像的详细信息_mysql_44

        通过"docker build -t"来构建镜像:

镜像查看版本 查看镜像的详细信息_mysql_45

镜像查看版本 查看镜像的详细信息_docker_46

        启动一个容器测试看看能否正常运行~

镜像查看版本 查看镜像的详细信息_docker_47

        清理容器资源,之后会启动redis集群,需要的服务资源也会增多~     

镜像查看版本 查看镜像的详细信息_容器_48

进行容器编排 —— 编写docker-compose.yml 

        我们需要启动多个容器而不再是一个了!又因为咱们的redis容器需要自定义所以采用了dockerfile编写,现在需要启动多个容器则需要用到 —— 容器编排~

镜像查看版本 查看镜像的详细信息_镜像查看版本_49

        “docker compose config”检查编写符合规范~ 

镜像查看版本 查看镜像的详细信息_镜像查看版本_50

        执行完成构建镜像:

镜像查看版本 查看镜像的详细信息_redis_51

值得注意的是:当我们设置.context时,就是以这个路径去寻找相对路径下的文件

镜像查看版本 查看镜像的详细信息_镜像查看版本_52

        启动服务,查看服务状态:

镜像查看版本 查看镜像的详细信息_redis_53

        查看redis07打印的日志,是否正确 —— 哈希槽已经被各个节点支配~

镜像查看版本 查看镜像的详细信息_镜像查看版本_54

        我们再次进入redis01 ~ redis07任意一个容器中,检查容器功能是否正常~

# 进入容器
docker exec -it redis01 bash

# -c 插入key值时,会自动重定向 映射的slots分片
/redis/redis-cli -c -a 123456

        查看集群节点详情信息~

镜像查看版本 查看镜像的详细信息_镜像查看版本_55

        我们可以插入一些 {Key,Value}~

镜像查看版本 查看镜像的详细信息_镜像查看版本_56

         完成资源释放,咱们使用Dockerfile搭建的Redis集群也就得到了一个圆满的成功~

镜像查看版本 查看镜像的详细信息_docker_57


Dockerfile结合Docker-Compose搭建C++微服务

构建C++微服务

        之前我们在容器中搭建的C++程序,一旦运行完成后就会自动退出~我们现在打算使用Dockerfile构建一个长时间运行的 C++服务器。

        这个服务器的功能很简单,就是一问一答,返回一个nginx经典的html~        

        构建相应目录:

镜像查看版本 查看镜像的详细信息_容器_58

        “/data/wgzzs/webcpp/cppweb”中,编写源代码main.cpp:

镜像查看版本 查看镜像的详细信息_镜像查看版本_59

        这只是一个简短的C-S客户端模型,用来给客户端返回前端编写的html文件(这个文件我们借用的是Nginx页面)~

#include <iostream>
#include <cstring>
#include <cassert>
#include <fstream>
#include <string>

#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
using namespace std;

struct pthread_data
{
    struct sockaddr_in client_addr;
    int clientfd;
};

void InitNet(struct sockaddr_in &local, int &sock_fd)
{
    // init addr
    memset(&local, 0, sizeof local);
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htons(INADDR_ANY);
    local.sin_port = htons(8011);

    // socket()
    int opt = 1;
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    // reuse addr
    assert(sock_fd >= 0);
    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // bind()
    if (bind(sock_fd, (struct sockaddr *)&local, sizeof local) < 0)
    {
        perror("bind error");
        cout << strerror(errno) << endl;
    }
    // listen()
    assert(listen(sock_fd, 5) == 0);

    cout << "WebCpp-Server Created..." << endl;
}

void readfile(string *body)
{
    ifstream ifs("index.html", std::ios::binary);
    if (ifs.is_open() == false)
    {
        perror("readfile");
        return;
    }

    size_t fsize = 0;

    ifs.seekg(0, ifs.end);
    fsize = ifs.tellg();
    ifs.seekg(0, ifs.beg);
    if (fsize > 0)
    {
        body->resize(fsize);
        ifs.read(&(*body)[0], fsize);
    }
    ifs.close();
}

void *serverHandler(void *args)
{
    struct pthread_data *pdata = (struct pthread_data *)args;
    int conn_fd = pdata->clientfd;
    std::cout << "Handler Task: " << pdata->clientfd << endl;
    char request[1024];
    int len = recv(conn_fd, request, 1024, 0);
    assert(len >= 0);
    
    // cout << "Message From Cli: " << request << endl;
    // char header[128] = "HTTP/1.1 200 ok\r\n";
    // char body[128] = "connection:close\r\n";
    // char blank_row[4] = "\r\n";
    char response_head[128] = "HTTP/1.1 200 ok\r\nconnection:close\r\n\r\n";

    // 首行+报头
    int s = send(conn_fd, response_head, strlen(response_head), 0);
    assert(s > 0);
    // 正文
    string body;
    readfile(&body);
    if(body.size() > 0) assert(send(conn_fd, body.c_str(), body.size(), 0) > 0);
    close(conn_fd);
    return nullptr;
}

int main()
{
    int sock_fd;
    struct sockaddr_in local;
    InitNet(local, sock_fd);
    // 作为监听
    while (1)
    {
        struct sockaddr_in client;
        int client_len = sizeof(client);
        int conn_fd = accept(sock_fd, (struct sockaddr *)&client, (socklen_t *)&client_len);

        // 分配线程去 完成任务
        struct pthread_data data;
        data.client_addr = client;
        data.clientfd = conn_fd;
        assert(data.clientfd >= 1);
        cout << "Get a new Link:" << data.clientfd << "~~" << endl;
        pthread_t pt;
        pthread_create(&pt, NULL, serverHandler, (void *)&data);
    }

    return 0;
}

        启动服务器,我们来看看效果~

镜像查看版本 查看镜像的详细信息_docker_60

        服务器访问效果:

镜像查看版本 查看镜像的详细信息_容器_61

镜像查看版本 查看镜像的详细信息_mysql_62

        结果是,我们的程序能够正确地执行,达到我们想要的效果~

编写C++ Dockerfile应用

        我们现在要做的,就是将这份程序制作成镜像,并让它在咱们的容器中运行。

        进入 “/data/wgzzs/webcpp/cppweb” 目录,编写Dockerfile~

镜像查看版本 查看镜像的详细信息_容器_63

         进入 "/data/wgzzs/webcpp/nginx目录,编写bit.conf,用于使用nginx作为负载均衡器的功能~

镜像查看版本 查看镜像的详细信息_docker_64

        我们还需要继续编写nginx的Dockerfile:

镜像查看版本 查看镜像的详细信息_redis_65

     

C++微服务容器编排

        进入"/data/wgzzs/webcpp"目录,编写docker-compose.yml文件:

version: "3.6"
services: 
  mywebcpp1:
    image: mywebcpp:v1.0
  build:
    context: ./cppweb
  mywebcpp2:
    image: mywebcpp:v1.0
  mywebcpp3:
    image: mywebcpp:v1.0

  web:
    image: mynginx:v1.0
    build:
      context: ./nginx
    ports: 
    - 8112:80
    depends_on:
      mywebcpp1:
        condition: service_started
      mywebcpp2:
        condition: service_started
      mywebcpp3:
        condition: service_started

镜像查看版本 查看镜像的详细信息_redis_66

        构建镜像: 

镜像查看版本 查看镜像的详细信息_镜像查看版本_67

        启动服务,从nginx的日志来看,启动是没有任何问题的~

镜像查看版本 查看镜像的详细信息_容器_68

镜像查看版本 查看镜像的详细信息_mysql_69

         访问nginx服务器6次,我们看看nginx是否能够将连接打在其他cpp服务器中~        

镜像查看版本 查看镜像的详细信息_mysql_70

        我们都可以得知,连接请求被负载到了不同的服务器容器中~

镜像查看版本 查看镜像的详细信息_docker_71

        清理资源,完成咱们最后的收尾工作~        

镜像查看版本 查看镜像的详细信息_mysql_72


本篇到此结束,感谢你的阅读

祝你好运,向阳而生~