docker搭建canal-server和canal-admin

一、搭建有binlog功能的mysql

1.1 先建两文件夹

我是使用docker来搭建,在服务器上建这么两个目录,用来挂载docker里面的mysql的配置文件,和mysql的数据存储, 后面docker run要用

 /etc/mysql   ##挂载mysql的配置文件
 /opt/mysql   ##挂载mysql的数据存储

1.2 拉取docker 镜像

docker pull mysql:5.7

1.3 运行镜像

docker run -itd --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=www123456 -v /etc/mysql:/etc/mysql -v /opt/mysql:/var/lib/mysql -v /etc/localtime:/etc/localtime mysql:5.7

1.4 查看挂载情况

查看挂载情况,里面可以看到详细的挂载情况,可以看到我们的两个目录是被正确挂载了

docker inspect 容器名或者id

docker搭建canal-server和canal-admin_第1张图片

1.5 编辑mysql的配置文件

直接在服务器上编辑mysql的配置文件(和docker容器里面的文件相互挂载了),里面有可能是空的,直接往里面加就行了

vim /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
log-bin=mysql-bin
binlog-format=ROW
server_id=1

1.6 重启mysql

docker restart 容器名或者id

1.7 查看数据库的binlog是否开启

用mysql工具查看数据库的binlog是否开启

show variables like '%log_bin%';

docker搭建canal-server和canal-admin_第2张图片

执行一条建表语句看看有没有binlog存在服务器上面

确实有,在mysql里面看看日志,到这里mysql就配置好了!

docker搭建canal-server和canal-admin_第3张图片

1.8 创建canal用户并授权

mysql> CREATE USER canal IDENTIFIED BY 'canal';
mysql> GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
mysql> FLUSH PRIVILEGES;
mysql> show grants for 'canal'@'%';
+----------------------------------------------------------------------------+
| Grants for canal@%%                                                        |
+----------------------------------------------------------------------------+
| GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `canal`@`%` |
+----------------------------------------------------------------------------+
1 row in set (0.00 sec)

1.9 创建woods_test库

create database woods_test;

二、搭建canal-server

注意

从这开始,就开始经历很多的坑,,,可能是对docker网络这部分不太熟的原因。

最开始的想法

我最开始的想法是在一台服务器上的docker里面,运行一个mysql容器,运行一个canal-server容器,然后本地起一个java的canal客户端连接canal-server,监听数据库的增量binlog日志。思路看起来没啥问题。

开始探索

以一条docker命令启动
# bash run.sh -e canal.auto.scan=false -e canal.destinations=woods_test -e canal.instance.master.address=172.17.0.3:3306 -e canal.instance.dbUsername=canal -e canal.instance.dbPassword=canal -e canal.instance.connectionCharset=UTF-8 -e canal.instance.tsdb.enable=true -e canal.instance.gtidon=false

显示启动成功
docker搭建canal-server和canal-admin_第4张图片

Java客户端代码, 其实就是官网的demo

public static void main(String args[]) {
        // 创建链接
        String hostIp = AddressUtils.getHostIp();
        System.out.println(hostIp);
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(
                "42.193.12.204", //canal-server的ip地址
                11111),
                "woods_test",
                "canal",
                "canal");
        int batchSize = 1000;
        int emptyCount = 0;
        try {
            connector.connect();
            connector.subscribe(".*\\..*");
            connector.rollback();
            int totalEmptyCount = 120;
            while (emptyCount < totalEmptyCount) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    emptyCount++;
                    System.out.println("empty count : " + emptyCount);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                } else {
                    emptyCount = 0;
                    // System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
                    printEntry(message.getEntries());
                }

                connector.ack(batchId); // 提交确认
                // connector.rollback(batchId); // 处理失败, 回滚数据
            }

            System.out.println("empty too many times, exit");
        } finally {
            connector.disconnect();
        }
    }

    private static void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                continue;
            }

            RowChange rowChage = null;
            try {
                rowChage = RowChange.parseFrom(entry.getStoreValue());
            } catch (Exception e) {
                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                        e);
            }

            EventType eventType = rowChage.getEventType();
            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                    entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                    eventType));

            for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                } else {
                    System.out.println("-------> before");
                    printColumn(rowData.getBeforeColumnsList());
                    System.out.println("-------> after");
                    printColumn(rowData.getAfterColumnsList());
                }
            }
        }
    }

    private static void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }

上面的Java客户端是怎么起都启动不起来,一直报这个错

Exception in thread "main" com.alibaba.otter.canal.protocol.exception.CanalClientException: failed to subscribe with reason: something goes wrong with channel:[id: 0x70fe412e, /118.112.75.194:7136 => /172.17.0.4:11111], exception=com.alibaba.otter.canal.server.exception.CanalServerException: destination:example should start first
tail -fn 100 /home/admin/canal-server/logs/example/example.log  //查日志

去docker容器里面查看日志找原因,发现是canal1.16版本的bug,使得canal-server会去查一张不存在的表,所以这里我修改了一下run.sh里面最后的cmd命令,指定了canal-server的版本

cmd="docker run -d -it -h $LOCALHOST $CONFIG --name=canal-server $VOLUMNS $NET_MODE $PORTS $MEMORY canal/canal-server:v1.1.4"

再次启动java的canal-client,可以看到连接成功,正在定时获取binlog,如果监听的库有表的插入或更新,客户端就会打印出来

docker搭建canal-server和canal-admin_第5张图片

编写docker-compose.yml来启动
version: '2'

services:
  canal-server:
    network_mode: "bridge"  #因为我mysql是在这个默认的网络下面,所以我的canal-server也加入这个网络
    image: canal/canal-server:v1.1.4 #最新的可能有bug,就用这个1.14吧
    container_name: canal-server
    ports:
      - 11111:11111
    environment:
      - canal.auto.scan=false
      - canal.destinations=woods_test
      - canal.instance.master.address=172.17.0.3:3306  #要监听的mysql地址
      - canal.instance.dbUsername=canal
      - canal.instance.dbPassword=canal
      - canal.instance.connectionCharset=UTF-8
      - canal.instance.tsdb.enable=true 
      - canal.instance.gtidon=false
      - canal.instance.filter.regex=.*\\..*  #监听规则
    volumes:
      - ./canal-server/conf/:/admin/canal-server/conf/ #挂载文件至宿主机
      - ./canal-server/logs/:/admin/canal-server/logs/

启动java客户端后同样可以正确监听到woods_test数据库的变化。success!

三、搭建canal-admin

通过docker-compose 启动 canal-admin

version: '2'

services:
  canal-admin:
    network_mode: "bridge"  #因为我mysql和canal-server是在这个默认的网络下面,所以我的canal-admin也加入这个网络
    image: canal/canal-admin:v1.1.4
    container_name: canal-admin
    ports:
      - 8089:8089
    environment:
      - server.port=8089
      - canal.adminUser=admin
      - canal.adminPasswd=admin

登录 ip:8089 canal-admin 管理界面

docker搭建canal-server和canal-admin_第6张图片

手动新建server

ip就填canal-server的ip,端口就默认的那几个

docker搭建canal-server和canal-admin_第7张图片

手动新建instance

改一下canal.instance.master.address就行,你的mysql IP地址

docker搭建canal-server和canal-admin_第8张图片

上面两步完成后,使用java的canal客户端进行连接,可以看到定时获取binlog的日志打印,如果监控的库有更新插入,则会有相应的日志打印出来,要注意的问题是,使用了admin客户端和server连接后,我们的java客户端在连接的时候不能使用之前用的明文的密码canal进行连接,需要使用密文 E3619321C1A937C46A0D8BD1DAC39F93B27D4458,或者你去mysql里查询

select password('canal');  ## *E3619321C1A937C46A0D8BD1DAC39F93B27D4458  用不要星号的后面就行
##java客户端连接  就变成这样了
 // 创建链接
        String hostIp = AddressUtils.getHostIp();
        System.out.println(hostIp);
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(
                "42.193.12.204",
                11111),
                "woods_test",
                "canal",
                "E3619321C1A937C46A0D8BD1DAC39F93B27D4458");
        int batchSize = 1000;
        int emptyCount = 0;

完结,撒花!

你可能感兴趣的:(canal,docker,容器)