Docker elasticsearch 集群搭建记录
.env
1 | PRIVATE_REPO=34.0.7.183:5000 |
docker-compose.yml
1 | version: '2.2' |
问题
- 挂载的日志和数据文件的权限
vm.max_map_count数目的设置- mac 环境下注意配置 docker 的内存大小设置
env.init
1 |
|
1 | PRIVATE_REPO=34.0.7.183:5000 |
1 | version: '2.2' |
vm.max_map_count 数目的设置1 | #!/usr/bin/env bash |
使用 spring data elasticsearch 来连接使用 elasticsearch, 配置如下:
1 | spring: |
已经确认 elasticsearch 的 9300 和 9200 端口无任何问题,均可进行连接
可是在启动项目是报出如下错误:
1 | 2019-01-16 17:17:35.376 INFO 36410 --- [ main] o.elasticsearch.plugins.PluginsService : no modules loaded |
连接被拒绝???
发现无法进行 elasticsearch 的健康检查,于是想到我使用了 actuator 进行端点健康监控
经过调试发现如下代码为返回数据:
ElasticsearchRestHealthIndicator 类中
1 | @Override |
new Request("GET", "/_cluster/health/") 正是 elasticsearch 健康的请求,但是没有看到 host 和 port
于是用抓包工具发现其请求的是 127.0.0.1:9200
那这肯定是 springboot 的默认配置了
查看 spring-boot-autoconfigure-2.1.2.RELEASE.jar
找到 elasticsearch 的配置 org.springframework.boot.autoconfigure.elasticsearch
在找到类 RestClientProperties
看到如下源码:
1 | @ConfigurationProperties(prefix = "spring.elasticsearch.rest") |
Collections.singletonList("http://localhost:9200")); 没错了,这就是错误的起因
顺藤摸瓜, 根据 spring.elasticsearch.rest 的配置,配置好 uris 即可
于是进行如下配置:
1 | spring: |
集群中的多个节点就写多个
启动,没有出现错误
还有一种方式也可以解决,但是并不是一种好的解决方式,那就是关闭 actuator 对 elasticsearch 的健康检查
1 | management: |
1 | compile ('com.dangdang:elastic-job-lite-core:2.1.5') |
1 | *************************** |
一开始我以为是搭建的 zookeeper 环境有问题,但是用其他工具可以连接的上
又怀疑是 zookeeper 的版本问题,查看了 com.dangdang:elastic-job-common-core:2.1.5 , 发现其依赖的 zookeeper 版本是 org.apache.zookeeper:zookeeper:3.5.3-beta
于是又用 docker 搭建了个 3.5.3-beta 的版本的 zookeeper 单机版
结果问题依旧…….
中间查找问题花费了很长的时间…..
后来把官方的 demo clone 到本地跑次看看,官方的 demo 仅仅依赖一个包 com.dangdang:elastic-job-lite-core:2.1.5
发现这个 demo 没有问题,可以连接的上 zookeeper
对比发现2个项目的依赖版本号不一致

看到 demo 里依赖的 org.apache.curator:curator-framework 和 org.apache.curator:curator-recipes 都是 2.10.0, 而我引入的版本却是gradle 上的最新版 4.0.1, 而且也能看到2者的 zookeeper 的版本也不一致,一个是 3.4.6,一个是 3.5.3-beta
问题所在找到了
解决问题
1 | compile ('com.dangdang:elastic-job-lite-core:2.1.5') |
手动声明版本为 2.10.0
问题解决,但是为什么 gradle 会造成这样的问题? 为什么传递依赖时, gradle 会去找最新的依赖版本? 这些问题我还没搞清楚….
日后搞清楚了,或者有眉目了,再来更新这篇文章.
1 | LocalDateTime |
例如下属代码导致 images 里的 DataImage 对象里的 stake 对象的数量改变
1 | Map<String,List<HighwayStake>> roadStakeMap = images.stream() |
因为对 dataImage 的 stakes 集合进行了合并,将 map 操作改为 复制一个新的 list , 而不是操作原来的 stakes
1 | Map<String,List<HighwayStake>> roadStakeMap = images.stream() |
上述的问题实际上是一个 list 的拷贝,而且是 浅度复制
new ArrayList<>(list) 和 Collections.copy(dest,src) 都是浅度复制
下面代码是一个靠谱的 深度拷贝, 需要 T 实现序列化接口
1 | /** |
reduce 有三种方法可以使用:
Optional<T> reduce(BinaryOperator<T> accumulator)T reduce(T identity, BinaryOperator<T> accumulator)<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)第一种传入二元运算表达式,第二种是带初始值的二元运算表达式,这里着重记录下第三种的使用方式
第三种第一个参数方法的返回值类型,
第二个参数是一个二元运算表达式,这个表达式的第一个参数是方法的返回值,也就是方法的第一个参数,第二个参数是 Stream 里的值
第三个参数也是一个二元运算表达式,表达式的2个参数都是方法返回值的类型,用于对返回值类型的操作
第三个参数在非并发的情况下返回任何类型(甚至是 null)都没有影响,因为在非并发情况下,第三个二元表达式根本不会执行
那么第三个二元表达式用在并发的情况下,在并发的情况下,第二个二元表达式的第一个参数始终是方法的第一个类型,第三个三元表达式用于将不同线程操作的结果汇总起来
区别在于, map() 返回自定义对象, 而 flatMap() 返回 Stream 流对象
最近在 lamda 的 stream 进行 list 去重复的时候,发现没有生效
代码如下:
1 | Map<String, Map<String, List<FollowAnalysisPojo>>> maps = allList |
实体类:
1 | @Data |
上面的代码是想做 先对查询出来的数据进行去重复的操作,然后在按照被跟车牌和跟踪车牌进行分组操作
有点需要说明的是 parallelStream() 比我们常用的 stream() 是并行多管操作,速度上更快
然后发现的问题是并没有去重复,当时也在奇怪 distinct() 里并没有任何参数来指定如何使用规则来去重复
重写List中实体类的 equals() 方法
1 | @Data |
这样我们就按照我自定义的规则进行去重复了
运行了一下,发现还是不起作用
debug了一下,发现根本没有执行重写的 equals 方法
原来还需要重写 hashCode() 方法
在 equals() 方法 执行前会先执行 hashCode() 方法
1 | @Data |
这样就可以了。
如果我们不重写方法,有没有办法按照List中bean的某个属性来去重复呢?答案是有的,利用的是 stream 的 reduce,用一个set 来存放 key,代码如下:
1 | List<JSONObject> result = trails.stream() |
1 | Integer[] xs = new Integer[]{3, 4}; |
比如: List<List
1 | // 第一种 |
1 | List<Demo> demos = list.stream().flatMap(Collection::stream).collect(Collectors.toList()); |
docker exec -it name /bin/sh 失败,
查看容器 inspect 报错信息如下:
1 | pc error: code = 2 desc = oci runtime error: exec failed: |
1 | yum downgrade docker docker-client docker-common |
我们在 docker-compose 一条命令就启动我们的多个容器时,需要考虑到容器之间的启动顺序问题…..
比如有的服务依赖数据库的启动, service 依赖 eureka 的启动完成
docker compose 里有 depends_on 配置,但是他不能等上一个容器里的服务完全启动完成,才启动下一个容器,这仅仅定义了启动的顺序, 那么这就会导致很多问题的发生
比如应用正在等待数据库就绪,而此时数据库正在初始化数据, 导致无法连接退出等等
地址 : https://docs.docker.com/compose/startup-order/
官方的思路是使用一个脚本,轮询给定的主机和端口,直到它接受 TCP 连接
个人感觉这种方式不是很好
还有几个开源的工具解决方法, 这些是一些小型脚本,和上面的原理类似:
这些工具也能解决问题,但有很大的局限性: 需要重新定义 command , 在执行完自己的脚本后在执行容器里的启动脚本
如果不知道容器的启动脚本或者容器的启动脚本很长,并且带有参数,那将非常头疼
查看容器的启动脚本:
1 | docker ps --no-trunc --format="table {{.ID}}\t{{.Command}}:" |
或者
1 | docker inspect container |
比如下面的配置
1 | server: |
server 使用了健康检查 healthcheck
test : 命令,必须是字符串或列表,如果它是一个列表,第一项必须是 NONE,CMD 或 CMD-SHELL ;如果它是一个字符串,则相当于指定CMD-SHELL 后跟该字符串, 例如: test: ["CMD", "curl", "-f", "http://localhost"] 或者 test: ["CMD-SHELL", "curl -f http://localhost || exit 1"] 或者 test: curl -f https://localhost || exit 1interval: 每次执行的时间间隔timeout: 每次执行时的超时时间,超过这个时间,则认为不健康retries: 重试次数,如果 retries 次后都是失败,则认为容器不健康start_period: 启动后等待多次时间再做检查, version 2.3 版本才有interval, timeout, start_period 格式如下:
1 | 2.5s |
健康状态返回 0 (health) 1 (unhealth) 2(reserved)
test 命令的通用是 'xxxx && exit 0 || exit 1' , 2 一般不使用
admin depends_on server ,且条件是 service_healthy ,即容器为健康状态,即 9368 端口开启
将 spring cloud 项目部署到 docker 容器中后,虽然可以配置容器的端口映射到宿主机的端口
但是在 eureka 界面显示的instance id 是一串随机的字符串,类似于 d97d725bf6ae 这样的
但是,事实上,我们想让他显示出 IP ,这样我们可以直接点击而打开 info 端点信息
修改 3 处配置项:
1 | eureka: |
eureka.instance.prefer-ip-address 配置为 true , 表示 instance 使用 ip 配置eureka.instance.prefer-ip-address 配置当前 instance 的物理 IPeureka.instance.prefer-instance-id 界面上的 instance-id 显示为 ip + 端口通常情况下,我们使用 springcloud 都会有很多的服务需要部署,就会产生很多的容器,这么多的容器再使用 docker 一个个操作就显得很复杂
这时候需要一个编排工具,于是我们就使用 docker-compose 来部署 springcloud 服务
1 | spring: |
使用 docker-compose 我们放弃使用 ip 来进行容器间的相互通信,继而使用 hostname,这就相当于在 /etc/hosts 添加了一条记录
1 | eureka: |
1 | eureka: |
1 | server: |
service 模块 links server 模块,再起个别名 traffic-service-eureka ,因为我配置文件里配置的是 traffic-service-eureka,
这样 service 模块就可以通过 server 或者 traffic-service-eureka 来访问 server 了
另外,配置的 hostname,可以进入 容器中查看 /etc/hosts 该配置会在 文件中生成一个容器的 ip 和 hostname 的记录
详见 : http://blog.joylau.cn/2018/12/19/Docker-Compose-StartOrder/
查看路由表: netstat -nr
添加路由: sudo route add 34.0.7.0 34.0.7.1
删除路由: sudo route delete 0.0.0.0
清空路由表: networksetup -setadditionalroutes "Ethernet", “Ethernet” 指定路由走哪个设备(查看当前的设备可以使用这个命令 networksetup -listallnetworkservices
清空路由表: sudo route flush , 是否有效没测试过,通过 man route 看到的,等哪天试过了,再来更新这个内容是否有效
我这里的使用场景是无线接外网, USB 网卡接内网,无线路由的网关是 192.168.0.1, USB 网卡的网关是 34.0.7.1
删除默认路由: sudo route delete 0.0.0.0
添加默认路由走无线网卡: sudo route add 0.0.0.0 192.168.0.1
内网走 USB 网卡: sudo route add 34.0.7.0 34.0.7.1
调整网络顺序,网络属性里面的多个网卡的优先级顺序问题。基本原则是哪个网卡访问互联网,他的优先级就在上面就可以了
有个问题没搞明白, 按逻辑说这样添加的静态路由是临时的,在重启后会消失失效,可实际上我重启了之后并没有失效
networksetup mac 自带的工具,升级到最新的Sierra后拥有,是个“系统偏好设置”中网络设置工具的终端版
networksetup –help 可以查看具体的帮助
添加静态永久路由: networksetup -setadditionalroutes "USB 10/100/1000 LAN" 10.188.12.0 255.255.255.0 192.168.8.254
“USB 10/100/1000 LAN” 指定路由走哪个设备(查看当前的设备可以使用这个命令 networksetup -listallnetworkservices
netstat -nr 查看路由表