consul 配置---K/V存储及ACL

consul 配置—K/V存储及ACL

标签(空格分隔): consul


  • Consul简介
  • 安装及运行
  • K/V存储
  • Java案例(基于Spring boot)
  • ACL配置
  • 小结

1.Consul简介

1.1 consul的作用

  • 服务发现

    • Consul clients提供服务(例如API)
    • 其他的client发现服务的提供者(通过DNS或http,应用可以轻松的发现他们所依赖的服务)
  • 健康检查
  • Key-Value存储操作
    • 动态配置
    • leader选举
    • feature flagging
    • coordination
  • 多数据中心(DC)
    • 用于容灾

1.2 consul的基本架构

  • 一个consul节点对应一个consul agent

    • agent会负责节点的健康检查和节点上的服务的健康检查
  • agent分为server和client
    • server

      • 存储数据
      • 复制数据
      • servers们自己选择一个leader
      • 每个数据中心推荐有3或5个server(官方推荐,但是只用单节点做server也是可以的,只是可能会数据丢失,两个节点的话,一个挂了,还是无法选举出leader,所以推荐3-5个)
    • client
      • 个人感觉client只是作为一个与server交互的agent而已

2.安装及运行

安装

启动

  • 以server模式启动

    • consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node=service-center -bind=127.0.0.1 -client 0.0.0.0 -ui -config-dir ~/.consul_config/
  • 以client模式启动节点(非server都是client)
    • consul agent -data-dir /tmp/consul -node=qianweifeng-mac -bind=192.168.15.16 -join 192.168.15.19
  • 集群模式
    • consul的集群搭建相对容易,一般需要用3-5个server节点来搭建一个集群,启动方式就想上面说的server启动方式一致,就是在加上 -join ip地址 即可。至于3-5个节点,之前也说过,官方推荐,确实根据consul的leader推举机制来说,最少需要3个节点。
    • 举个小例子:

      现有节点A,B ,我们需要组建集群,那么自然是start A, start B, A join B (反过来也一样),如此,A,B就相互认识了。

      此时有一个节点C要加入集群,我们不需要 C join A, C join B, 只需要选择A,B中的一个进行join就可以了。

      其中的原理是给予gossip协议完成的

关键指令介绍

agent 相当于是一个客户端,我觉得consul是没有意义明确的客户端与服务端的,只是节点启动的模式不同,而不是由运行于该节点之上的程序所决定的,所以agent相当于是一个客户端代理,通过它我们可以与consul集群进行交流。

-server 以server模式启动节点,相对的还要-client, 不过如果不指定-server,默认就是-client

-bootstrap-expect 3 这个东西是告诉consul集群,我们预计有3个server节点,该参数是为了延迟日志复制的启动直到我们指定数量的server节点成功的加入后启动。

-data-dir 指定agent储存状态的数据目录,比较重要

-config-dir 指定一个要装载的配置文件,可以是一个文件,也可以是一个目录下的所有文件,文件以.json后缀

-node 该节点在集群中的名称,具有唯一性,推荐直接用机器ip,便于区分辨别

-bind 说明该agent绑定的地址,通常情况是绑定本机

-join 加入集群

-rejoin 效果同上不过是在退出集群后再加入进去用的

特别注意:

-client 0.0.0.0 -ui 这个东西是为了能远程访问WEB UI界面,高版本的consul在远程访问WEB UI界面上似乎有些问题,也可能是我哪里没有配置好,所以访问不了。

使用这个指令后就可以远程访问了(目测这个指令的意思是,允许任何IP的客户端来访问WEB UI)

3.K/V存储

关于K/V的作用在上一节已经说明了,这里来讲讲其具体应用。

这个东西的话用来做动态配置比较多,比如我们有一些数据库配置,如果配置在本地,那么每次启动服务时还需要注意这些配置启动的正不正确,上传到测试环境时,也需要修改这些配置。但是如果我们用了K/V,我们要做的只是从不同的K/V拉去配置,而不需要修改代码。更重要的是,有一些非启动阶段读取的配置(也就是用到在读取的配置),可以做到动态更改,而不需要重启程序。


4.Java案例(基于Spring boot)

这里就动态配置,给出一个基于Spring Boot的具体案例

1.maven依赖

<!--consul-->
        <dependency>
            <groupId>com.orbitz.consul</groupId>
            <artifactId>consul-client</artifactId>
            <version>0.10.0</version>
        </dependency>
        <!-- consul需要的包 -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>2.22.2</version>
        </dependency>
         <!-- 动态配置的实现 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- archaius -->
        <dependency>
            <groupId>com.netflix.archaius</groupId>
            <artifactId>archaius-core</artifactId>
            <version>0.6.6</version>
        </dependency>
        <!-- 动态配置,archaius底层 -->
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.8</version>
        </dependency>
         <!-- 这个只是一个比较easy的consul client api,与第一个选一个用即可 -->
        <dependency>
            <groupId>com.github.dcshock</groupId>
            <artifactId>consul-rest-client</artifactId>
            <version>0.7</version>
        </dependency>
package com.config.center.config;

import com.config.center.common.CommonConstants;
import com.config.center.consul.ConsulInfo;
import com.config.center.consul.ConsulPropertySource;
import com.orbitz.consul.AgentClient;
import com.orbitz.consul.Consul;
import com.orbitz.consul.NotRegisteredException;
import consul.ConsulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.ConfigurableEnvironment;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URI;

/**
 *
 * @author qwf
 * @version 2015-08-17
 */
@Configuration
public class ConsulConfig {
    @Autowired
    private ConfigurableEnvironment env;

    @PostConstruct
    public void init() throws ConsulException, IOException, NotRegisteredException {
        ConsulInfo info = getConsulInfo();
        if (info != null) {
            env.getPropertySources().addFirst(new ConsulPropertySource("consul", info));
        }
    }

    /***
     * 注册服务+健康检查
     * 这里用了两种不同的consul client
     * 因为一种对于KeyClient的处理相对方便,但是对于check的注册支持却不行,只能支持script检查
     * 另一种对于KeyClient的处理相对麻烦,但是对于注册以及check的定义相对方便
     * @return
     * @throws ConsulException
     * @throws IOException
     * @throws NotRegisteredException
     */
    public ConsulInfo getConsulInfo() throws ConsulException, IOException, NotRegisteredException {
        //tag 来自于启动参数 -Dservice.tag=dev
        String tag = env.getProperty(CommonConstants.CONSUL_TAG);
        if (StringUtils.isBlank(tag)) {
            throw new RuntimeException("not specified " + CommonConstants.CONSUL_TAG);
        }

        if ("local".equals(tag)) {
            return null;
        }
        String service = CommonConstants.CONSUL_SERVICE_NAME;
        Consul consul = Consul.builder().build();            //建立consul实例
        AgentClient agentClient = consul.agentClient();
        agentClient.register(8800, URI.create("http://localhost:8800/health").toURL(), 3, service, "config-center", tag);
//        consul.agent().register(new ServiceProvider("config-center",
//                service, 8800,
//                new String[]{tag}));
//        consul.agent().checkRegister(new AgentCheck("id", service, "", "http://localhost:8800/health", "10s", "15s"));
//        Consul consul = new Consul("http://localhost", 8500);
//        KeyValue keyValue = new KeyValue(consul);
        String keyName = String.format("service/%s/%s/config", service, tag);

        return new ConsulInfo(keyName, null);
    }

    /**
     * IMPORTANT:
     * 保证consul配置获取第一个被加载,防止spring启动的时候从本地加载配置
     * 后期添加配置,如果依赖environment,也需要denpendsOn这个
     */
    @Bean
    public Object trick() {
        System.out.println("trick");
        return "trick";
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}
package com.config.center.consul;

import com.netflix.config.PollResult;
import com.netflix.config.PolledConfigurationSource;
import com.orbitz.consul.Consul;
import com.orbitz.consul.KeyValueClient;
import org.apache.http.util.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * ConsulConfigurationSource
 *
 * @author qwf
 * @version 2016-08-17
 */
public class ConsulConfigurationSource implements PolledConfigurationSource {

    public static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigurationSource.class);
    private ConsulInfo consulInfo;

    public ConsulConfigurationSource(ConsulInfo consulInfo) {
        this.consulInfo = consulInfo;
    }

/**
*动态拉去配置,默认60s拉一次
*/
    @Override
    public PollResult poll(boolean initial, Object checkPoint) throws Exception {
        String str = null;
        try {
            Consul consul = Consul.builder().build();
            KeyValueClient kvClient = consul.keyValueClient();
            str = kvClient.getValueAsString(consulInfo.getKeyName()).get();
        } catch (Throwable e) {
            LOGGER.error("get property from consul error", e);
        }
        Map<String, Object> map = new HashMap<>();
        if (TextUtils.isBlank(str)) {
            return PollResult.createFull(map);
        } else {
            Properties properties = new Properties();
            properties.load(new StringReader(str));
            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                map.put(entry.getKey().toString(), entry.getValue());
            }

            return PollResult.createFull(map);
        }
    }
}
package com.config.center.consul;

import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicConfiguration;
import com.netflix.config.FixedDelayPollingScheduler;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;

/**
 * ConsulPropertySource
 *
 * @author qwf
 * @version 2015-08-17
 */
public class ConsulPropertySource extends MapPropertySource {
    private DynamicConfiguration configuration;

    public ConsulPropertySource(String name, ConsulInfo consulInfo) {
        super(name, new HashMap<String, Object>());

        FixedDelayPollingScheduler scheduler = new FixedDelayPollingScheduler();
        scheduler.setIgnoreDeletesFromSource(true);
        DynamicConfiguration configuration = new DynamicConfiguration(
                new ConsulConfigurationSource(consulInfo), scheduler);
        ConfigurationManager.install(configuration);
        this.configuration = configuration;
    }

    @Override
    public Object getProperty(String name) {
        return configuration.getProperty(name);
    }
}
package com.config.center.consul;

import com.orbitz.consul.KeyValueClient;
import consul.Consul;
import consul.KeyValue;

/**
 * ConsulInfo
 *
 * @author qwf
 * @version 2015-08-17
 */
public class ConsulInfo {
    private String keyName;
    private KeyValue keyValue;

    public ConsulInfo(String keyName, KeyValue keyValue) {
        this.keyName = keyName;
        this.keyValue = keyValue;
    }

    public String getKeyName() {
        return keyName;
    }

    public void setKeyName(String keyName) {
        this.keyName = keyName;
    }

    public KeyValue getKeyValue() {
        return keyValue;
    }

    public void setKeyValue(KeyValue keyValue) {
        this.keyValue = keyValue;
    }
}

配置如上即可,代码比较简单。

需要注意的是启动时保整本地的consul agent连接到集群server,以及指定一个consul tag,如果tag为“local”,那就不从consul读配置

运行后即可看到动态配置的效果。


4.ACL配置

ACL即为“Access Control List”,用来给consul做一些权限控制的(一定程度上弥补了没有登录校验的场景)

网上ACL的教程比较少,基本就是介绍下ACL是什么,官方也没有给出具体配置,只是讲了各个属性的作用

推荐http://www.tuicool.com/articles/2InMV36这篇文章讲的还可以,不过精华是里面引用的那篇日文文章。

那直接进入主题

consul的acl配置

经过老夫的对文档的查阅以及多次测试,得出的结论:

1.启动consul的all模式时,配置文件:

{

“acl_datacenter”: “datacenter-tag”,

“acl_master_token”: “xxxxxxxxxx”, //这个token就是上面的种子token用来生成其余token

“acl_default_policy”: “deny” //开启acl

}

这三个是必要的base属性。

  • acl_default_policy:不配置rule规则的情况下对于默认行为的处理(可配置为allow,deny)

    对于这个,解释一下consul的白名单与黑名单

    当acl_default_policy为deny是,默认的api(写)行为都会被阻止,我们可以配置其子项,让比如配置key可写,这样在全部deny的情况下,出现了一个默认可写的行为,这就是白名单

    黑名单反之,当acl_default_policy为allow时,默认行为都是允许的,我们可以配置子项使它某些行为为deny,这就是黑名单的概念

    若要开启all,acl_default_policy需要设置成deny

  • acl_master_token:这个是自己指定的,随便来一串字符串就可以了
  • acl_datacenter:all作用的数据中心

这样配置就完成了初始工作,在启动consul节点时需要加入该配置 -config-dir [配置文件的路径/或者配置文件的父目录]

此时我们访问consul的web ui时,(如果不设置token的情况)是作为陌生人的模式访问的,默认情况(不设置白名单),我们只能看到services,node里的内容,key/value是屏蔽的(无法读写),acl没有权限

2.创建子token

我们不能一直用最高权限的master token吧

curl -H “X-Consul-Token: secret” -X PUT -d ‘{“Name”: “dc1”, “Type”: “management”}’ http://192.168.15.19:8500/v1/acl/create?token=xxxxxxxx

Name:token的name

Type:token的类型,有client跟management

token:我们上面说到的master token

会返回类似{“ID”:”54776fd0-510a-875b-8e50-83e8cda3f5d6”}

这个ID就是我们需要的子token

在配置中加入这个token,即可拥有相关的权限,需要注意的是,token不要加错,加错了会很麻烦,比如403forbidden,而且这个是浏览器清缓存有没用的东西,老夫暂时还没搞清楚要咋整(似乎把浏览器的缓存清理掉就能行)。

在说另一种acl方式,这种方式更为直接,如果token不对直接连web ui都访问不了,而且特点是屏蔽的陌生人token

我觉得这种方式是有缺陷的,因为官方并没有类似的说明,而且我个人也认为本身acl就不应该连web ui都控制掉

这种方式下,我们启动acl,此时是无法访问web ui的,因为陌生人模式被“关闭了”,我们需要在此时生成一个子token,然后节点重启,进入非acl模式,此时的配置中加入该token,然后在重启节点,进入acl模式

经测验,这种方式的acl_config.json与上面的不一样

{

“acl_datacenter”: “datacenter-tag”,

“acl_master_token”: “xxxxxxxxxx”, //这个token就是上面的种子token用来生成其余token

“acl_token”: “b9exxxxx-xxxx-xxxx-xxx-xxxxxxxxxx291ba”,//通过种子token生成的token

“acl_default_policy”: “deny” //开启acl

}

上面只需要3个变量,这种方式4个都需要

不过acl_token 似乎是随意的

设置了token的情况

不设置token的情况


5.小结

说了这么多,最后做个总结。

1.server节点在推出重启后 peers.json会被置null(该文件一般在/tmp/consul下,启动时指定的目录),原本应该是类似 [“192.168.37.42:8300”, “192.168.37.111:8300”,”192.168.37.246:8300”] 这样的json数据(server节点数据),这种情况下在重新启动server节点后会出现No cluster leader这样的错误,consul info 查看节点信息,会发现所有的server节点都是follower,无法选举出leader

解决方案:将[“120.27.37.42:8300”, “121.42.185.111:8300”,”115.28.154.246:8300”]信息回复到peers.json中,再重新启动节点(-rejoin)

2.web ui的访问,如果不能访问,可以查看下启动时是否加了-client 0.0.0.0 -ui 的启动命令。因为高版本的consul似乎在默认情况下,不允许web ui的远程访问(可能是我哪里配置有问题吧),不过加了-client 0.0.0.0 -ui,所有机器都能访问到web ui界面,剩下的就是有没有权限的问题了。


时间: 2024-10-06 18:48:11

consul 配置---K/V存储及ACL的相关文章

Apache Accumulo 1.8.0 发布,K/V 存储方案

Apache Accumulo 1.8.0 发布了,Apache Accumulo 是一个可靠的.可伸缩的.高性能的排序分布式的 Key-Value 存储解决方案,基于单元访问控制以及可定制的服务器端处理(腾云科技ty300.com).使用 Google BigTable 设计思路(基础教程qkxue.net),基于 Apache Hadoop.Zookeeper 和 Thrift 构建. 主要改进: Speed up WAL roll oversUser level API for RFile

数据库k/v存储模型浅析——Hash,B树,LSM

1.基于哈希的存储引擎 常见模型是BitCask 并发下的数据库文件读写: 本来想使用FileLock,但是后来发现 FileLock是进程间的,并不能用于同一个JVM多个线程之间的同步: File locks are held on behalf of the entire Java virtual machine.* They are not suitable for controlling access to a file by multiple* threads within the s

C#泛型集合之Dictionary&lt;k, v&gt;使用技巧

1.要使用Dictionary集合,需要导入C#泛型命名空间 System.Collections.Generic(程序集:mscorlib) 2.描述 1).从一组键(Key)到一组值(Value)的映射,每一个添加项都是由一个值及其相关连的键组成 2).任何键都必须是唯一的 3).键不能为空引用null(VB中的Nothing),若值为引用类型,则可以为空值 4).Key和Value可以是任何类型(string,int,custom class 等) 3.创建及初始化 Dictionary<

Java集合源码分析(七)HashMap&lt;K, V&gt;

一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Coll

C#泛型集合—Dictionary&lt;K,V&gt;使用技巧

[csharp] view plaincopy 1.要使用Dictionary集合,需要导入C#泛型命名空间 System.Collections.Generic(程序集:mscorlib) 2.描述  1).从一组键(Key)到一组值(Value)的映射,每一个添加项都是由一个值及其相关连的键组成  2).任何键都必须是唯一的  3).键不能为空引用null(VB中的Nothing),若值为引用类型,则可以为空值  4).Key和Value可以是任何类型(string,int,custom c

zabbix_sender主动上传k/v监控nginx日志状态码

目前的zabbix监控了900台左右服务器,大概有11万items,zabbix在工作时根据agent的工作特点分为主动模式和被动模式,实际上一般开启兼容模式.在items多了后,有一些速度慢的items如果不采用主动模式,会把server端拖死,而zabbix_sender其实是一种变相的主动模式,配合计划任务,主动将k/v上传到zabbix,现将nginx日志zabbix_sender实现状态码的监控抛砖引玉做下介绍. 一.agent端编写脚本和计划任务 需求是监控nginx日志的200.4

Cocos2d-x3.0模版容器详解之——cocos2d::Vector&lt;T&gt;, cocos2d::Map&lt;K,V&gt;, cocos2d::Value

Cocos2d-x3.0模版容器详解之一:cocos2d::Vector<T>  http://www.cocoachina.com/bbs/read.php?tid=199793Cocos2d-x3.0模版容器详解之二:cocos2d::Map<K,V>  http://www.cocoachina.com/bbs/read.php?tid=199916Cocos2d-x3.0模版容器详解之三:cocos2d::Value  http://www.cocoachina.com/b

cocos基础教程(4)数据结构介绍之cocos2d::Map&lt;K,V&gt;

1.概述 cocos2d::Map<K,V> 是一个内部使用了 std::unordered_map的关联容器模版. std::unordered_map 是一个存储了由key-value键值对组合成构成的关联性容器,允许基于键对单个元素进行快速检索. 2.模版参数 K - key value的类型. map中元素都由它的 key值作为唯一标识. V - mapped value的类型. T 必须是一个指向 cocos2d::Object 子类对象的指针. 3.内存管理 如果你在栈上声明了一个

Cocos2d-x3.0模版容器具体解释之二:cocos2d::Map&amp;lt;K,V&amp;gt;

1.概述: 版本号: v3.0 beta 语言: C++ 定义在 "COCOS2DX_ROOT/cocos/base" 路径下的 "CCMap.h" 的头文件里. template <class K, class V> class CC_DLL Map; cocos2d::Map<K,V> 是一个内部使用了 std::unordered_map的关联容器模版. std::unordered_map 是一个存储了由key-value键值对组合成