0%

前章回顾

上一章使用了docker命令部署consul集群,但是每次都敲命令太麻烦了,可以使用docker-compose简化。

docker-compose简介

本章内容参考官网:https://docs.docker.com/compose/

Compose是一个用于定义和运行多容器Docker应用程序的工具。使用Compose,您可以使用YAML文件来配置应用程序的服务。然后,使用单个命令,您可以从配置中创建并启动所有服务。

docker-compose安装

进入官网,https://docs.docker.com/compose/install/,选择linux,可以找到安装命令

#运行此命令以下载Docker Compose的当前稳定版本:
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
 
#对二进制文件应用可执行权限:
sudo chmod +x /usr/local/bin/docker-compose
 
#可以移到到你需要的目录下,也可以不移动,如果移动,例如
mv /usr/local/bin/docker-compose /docker/docker-compose
 
#可以再创建一个链接就行
sudo ln -s /docker/docker-compose /usr/bin/docker-compose
 
#测试安装
$ docker-compose --version
docker-compose version 1.24.1, build 1110ad01

编写docker-compose.yml

version: '3'
services:
  #服务名
  consul_server_1:
    #容器名
    container_name: consul_server_1
    #使用的镜像
    image: consul:1.6.0
    #映射的端口
    ports:
      - "8500:8500"
    #使用的网络
    networks: 
      - consul
    #映射的目录
    volumes:
      - /docker/consul/server1/config:/consul/config
      - /docker/consul/server1/data:/consul/data
    #需要导入的环境变量
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    #是否伴随docker重启
    restart: always
    #执行的命令
    command: [
      'agent'
    ]
 
#使用自定义网络,这样每次启动就不会创建新的网络了,ip相对固定
networks:
  consul:

docker-compose启动

#启动/停止都需要进入到docker-compose.yml目录下
#启动命令-前台运行
docker-compose up
 
#启动命令-后台运行
docker-compose up -d
 
#停止命令
docker-compose stop
 
#更多命令进入官网学习:https://docs.docker.com/compose/

注意网络问题

docker-compose使用的是docker网络

#查看现有docker网络
docker network ls
 
#删除一个网络
docker network rm +name
 
#查看一个网络的详情
docker network inspect +name
 
#docker-compose网络设置
#自定义网络
networks:
  frontend:
  backend:  
 
#使用默认网络
networks:
  default:
    # Use a custom driver
    driver: custom-driver-1
 
#使用现有网络
networks:
  default:
    external:
      name: my-pre-existing-network
#更多参考官网:https://docs.docker.com/compose/networking/

修改consul另外节点的join IP

获取consul_server_1ip,可以根据启动日志,或者运行完之后执行docker exec consul_server_1 consul members查看IP
consul

consul

修改其余consul配置的join ip
consul

docker-compose.yml中增加其他节点服务,完整配置如下:

version: '3'
services:
  consul_server_1:
    container_name: consul_server_1
    image: consul:1.6.0
    ports:
      - "8500:8500"
    networks: 
      - consul
    volumes:
      - /docker/consul/server1/config:/consul/config
      - /docker/consul/server1/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_server_2:
    container_name: consul_server_2
    image: consul:1.6.0
    networks: 
      - consul
    volumes:
      - /docker/consul/server2/config:/consul/config
      - /docker/consul/server2/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_server_3:
    container_name: consul_server_3
    image: consul:1.6.0
    networks: 
      - consul
    volumes:
      - /docker/consul/server3/config:/consul/config
      - /docker/consul/server3/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_client_1:
    container_name: consul_client_1
    image: consul:1.6.0
    networks: 
      - consul
    volumes:
      - /docker/consul/client1/config:/consul/config
      - /docker/consul/client1/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
networks:
  consul:

启动,查看

consul
consul
consul

服务均正常,以后启动只需要docker-compose up -ddocker-compose stop即可,大大方便!

增加固定IP部署配置

version: '3'
services:
  consul_server_1:
    container_name: consul_server_1
    image: consul:1.6.0
    ports:
      - "8500:8500"
    networks: 
      consul:
        ipv4_address: 172.20.0.2
    volumes:
      - /docker/consul/server1/config:/consul/config
      - /docker/consul/server1/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_server_2:
    container_name: consul_server_2
    image: consul:1.6.0
    networks: 
      consul:
        ipv4_address: 172.20.0.3
    volumes:
      - /docker/consul/server2/config:/consul/config
      - /docker/consul/server2/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_server_3:
    container_name: consul_server_3
    image: consul:1.6.0
    networks: 
      consul:
        ipv4_address: 172.20.0.4
    volumes:
      - /docker/consul/server3/config:/consul/config
      - /docker/consul/server3/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
  consul_client_1:
    container_name: consul_client_1
    image: consul:1.6.0
    networks: 
      consul:
        ipv4_address: 172.20.0.5
    volumes:
      - /docker/consul/client1/config:/consul/config
      - /docker/consul/client1/data:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - TZ=Asia/Shanghai
    restart: always
    command: [
      'agent'
    ]
 
networks:
  consul:
    ipam:
      config:
        - subnet: 172.20.0.0/16

设置网络模式

NAT模式

设置固定ip

#编辑以下文件
vi /etc/sysconfig/network-scripts/ifcfg-ens33
 
#修改以下内容,注意属性BOOTPROTO,ONBOOT,IPADDR,NETMASK,GATEWAY
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=e27ede3e-740b-4241-adf1-e979fd2788e9
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.41.128
NETMASK=255.255.255.0
GATEWAY=192.168.41.2
 
#重启网络
service network restart

设置nameserver

#编辑/etc/resolv.conf,随便增加一些国内DNS服务器IP,例如:
nameserver 114.114.114.114
nameserver 119.29.29.29
nameserver 114.114.114.114
nameserver 223.5.5.5
nameserver 8.8.8.8
 
#重启网络服务,注意和上一步重启网络要分开执行,不然本文件内容会因为上一步重启而清空
service network restart

拉取consul镜像

docker pull consul:1.6.0

本地创建映射目录及配置文件

#创建server1对应的目录
mkdir /docker/consul/server1
mkdir /docker/consul/server1/data
mkdir /docker/consul/server1/config
 
#创建server2对应的目录
mkdir /docker/consul/server2
mkdir /docker/consul/server2/data
mkdir /docker/consul/server2/config
 
#创建server3对应的目录
mkdir /docker/consul/server3
mkdir /docker/consul/server3/data
mkdir /docker/consul/server3/config
 
#consul容器默认config路径在/consul/config,data路径在/consul/data
#使用docker -v映射,启动可以使用本地config,data也可以持久化到本地
// 创建server1配置文件config.json
// 注意配置ui集群页面,client_addr对应-client,bootstrap
 
{
    "datacenter":"dc1",
    "primary_datacenter":"dc1",
    "server":true,
    "ui":true,
    "client_addr":"0.0.0.0",
    "bootstrap":true,
    "enable_script_checks":false,
    "node_name":"consul-server-1",
    "enable_local_script_checks":true
}
 
// 拷贝同样的配置到/docker/consul/server2,/docker/consul/server3目录下,注意去掉ui和bootstrap属性,注意修改节点名node_name,不能冲突
// client_addr不写的话,外面服务访问不了容器里的consul服务

启动容器

#docker启动命令
 
docker run -d -e CONSUL_BIND_INTERFACE=eth0 -v /docker/consul/server1/config:/consul/config -v /docker/consul/server1/data:/consul/data  -p 8500:8500  --name=consul consul:1.6.0 agent
 
#前面的不解释了,-v是将本地目录映射到容器内,容器内的路径是刚刚config.json配置中指定的,启动consul时指定配置文件,实际这个配置文件在本地/docker/consul/server1/config/config.json

访问本地8500端口,可以看到页面

consul

启动第二、第三个server加入到集群

#第一步:通过consul命令查看现有server信息
docker exec consul_server_1 consul members

consul

//第二步:修改server2和server3的配置文件,增加以下内容
    "start_join":[
        "172.17.0.4"
    ],
    "retry_join":[
        "172.17.0.4"
    ]
//注意保持json格式正确
//也可以在docker启动命令后加 -join=172.17.0.4 -retry-join=172.17.0.4,结果一样
#第三步:使用docker命令启动server2和server3

#启动第2个server节点
docker run -d -e CONSUL_BIND_INTERFACE=eth0 -v /docker/consul/server2/config:/consul/config -v /docker/consul/server2/data:/consul/data --name=consul_server_2 consul:1.6.0 agent
 
 
#启动第3个server节点
docker run -d -e CONSUL_BIND_INTERFACE=eth0 -v /docker/consul/server3/config:/consul/config -v /docker/consul/server3/data:/consul/data --name=consul_server_3 consul:1.6.0 agent

查看ui

consul

增加client类型consul

// 复制一个config.json到/docker/consul/client1目录下
// 修改client1的配置文件,具体如下
 
{
    "datacenter":"dc1",
    "primary_datacenter":"dc1",
    "server":false,
    "client_addr":"0.0.0.0",
    "enable_script_checks":false,
    "node_name":"consul-client-1",
    "enable_local_script_checks":true,
    "start_join":[
        "172.17.0.4"
    ],
    "retry_join":[
        "172.17.0.4"
    ]
}
 
// 注意点:指明了server为false
# docker启动
docker run -d -e CONSUL_BIND_INTERFACE=eth0 -v /docker/consul/client1/config:/consul/config -v /docker/consul/client1/data:/consul/data --name=consul_client_1 consul:1.6.0 agent

client加入之后可以通过命令consul members查看
consul

可以看到我们刚刚启动的consul_client_1这个节点。

UI中在service中看不到client
consul

但是可以在node中看到
consul

查看容器中运行的数据是否保存在本地

consul
成功部署完成!

参考链接

启动及常用配置项

解压后,在目录下打开cmd窗口,运行

consul agent -dev -ui -node=nodeName

-data-dir

  • 作用:指定agent储存状态的数据目录
  • 这是所有agent都必须的
  • 对于server尤其重要,因为他们必须持久化集群的状态

-config-dir

  • 作用:指定service的配置文件和检查定义所在的位置
  • 通常会指定为”某一个路径/consul.d”(通常情况下,.d表示一系列配置文件存放的目录)

-config-file

  • 作用:指定一个要装载的配置文件
  • 该选项可以配置多次,进而配置多个配置文件(后边的会合并前边的,相同的值覆盖)

-dev

  • 作用:创建一个开发环境下的server节点
  • 该参数配置下,不会有任何持久化操作,即不会有任何数据写入到磁盘
  • 这种模式不能用于生产环境(因为第二条)

-node

  • 作用:指定节点在集群中的名称
  • 该名称在集群中必须是唯一的(默认采用机器的host
  • 推荐:直接采用机器的IP

-bind

  • 作用:指明节点的IP地址
  • 有时候不指定绑定IP,会报Failed to get advertise address: Multiple private IPs found. Please configure one. 的异常

-server

  • 作用:指定节点为server
  • 每个数据中心(DC)的server数推荐至少为1,至多为5
  • 所有的server都采用raft一致性算法来确保事务的一致性和线性化,事务修改了集群的状态,且集群的状态保存在每一台server上保证可用性
  • server也是与其他DC交互的门面(gateway)

-bootstrap-expect

  • 作用:该命令通知consul server我们现在准备加入的server节点个数,该参数是为了延迟日志复制的启动直到我们指定数量的server节点成功的加入后启动。

-join

  • 作用:将节点加入到集群

-datacenter(老版本叫-dc,-dc已经失效)

  • 作用:指定机器加入到哪一个数据中心中

-client

  • 作用:指定节点为client,指定客户端接口的绑定地址,包括:HTTP、DNS、RPC
  • 默认是127.0.0.1,只允许回环接口访问
  • 若不指定为-server,其实就是-client

测试

浏览器访问http://127.0.0.1:8500
consul

启动命令

docker run "image"
参数 用途
-it 交互式启动
-d 后台启动
-p80:80 端口映射
--name 容器名称
--cpus 限制cpu使用个数
--cpuset-cpus 限制cpu使用的核,例如0-3,限制使用0-3核CPU
-m 限制内存使用,例如2048M
--restart 自动重启策略

对指定容器执行bash

docker exec -it 9df70f9a0714 /bin/bash

删除镜像

docker rmi 镜像名/镜像ID

构建镜像

FROM myjdk1.8
MAINTAINER author
 
ADD haolin.jar /root
ADD start.sh /root
RUN chmod +x /root/start.sh
 
EXPOSE 80
ENV LANG C.UTF-8
CMD /root/start.sh
#! /bin/bash
echo "building docker image..."
docker build -f /docker/bbs/Dockerfile -t author/bbs .
 
echo "stop and remove old docker container"
if [ `docker ps -a|grep bbs|awk '{print $1}'` != '' ]; then
        docker stop bbs
        docker rm bbs
fi
 
echo "start..."
docker run -d -p 80:80 --name bbs --restart=always author/bbs:latest
 
echo "buid success"

首先开启gateway debug日志,可以看到:

debug

debug日志中是可以打印请求所命中的路由id的,Route matched: hello-route

然后我在想怎么在程序中获取到这个id呢?因为如果在程序中可以获取到这个id,我就可以按统计每个路由所被访问的次数。网上找了一些博客,都没有提到这个问题,所以只能自己想了。

首先我从打印日志的类开始搜,打开源码RoutePredicateHandlerMapping,找到打印日志的代码。

protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
    if (this.managmentPort != null && exchange.getRequest().getURI().getPort() == this.managmentPort) {
        return Mono.empty();
    } else {
        exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName());
        return this.lookupRoute(exchange).flatMap((r) -> {
            exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
            if (this.logger.isDebugEnabled()) {
                // 可以看到这里对应的r就是我们要获取的路由。因为从打印的日志中可以看出
                this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
            }

            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
            return Mono.just(this.webHandler);
        }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
            exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
            }

        })));
    }
}
Route{id='hello-route', uri=http://127.0.0.1:8763, order=0, predicate=org.springframework........

我们只要获取到这个Route对象就可以了。那么源码中这个对象放哪儿了呢?

exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);

所以我们只需要在exchange中根据ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,这个keyAttributes获取就可以了。

下面编写一个Global Filter试试吧。

public class GatewayLogFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        System.out.println(route.getId());
        return chain.filter(exchange);
    }

}