murakumo 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -5
- data/etc/murakumo.server +12 -0
- data/etc/murakumo.yml.example +12 -0
- data/lib/cli/murakumo_options.rb +49 -0
- data/lib/misc/murakumo_const.rb +1 -1
- data/lib/srv/murakumo_balancer.rb +135 -0
- data/lib/srv/murakumo_cloud.rb +20 -25
- metadata +6 -5
data/README
CHANGED
@@ -27,7 +27,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
27
27
|
shell> mrkmctl -L
|
28
28
|
IP address TTL Priority Weight Activity Hostname
|
29
29
|
--------------- ------ -------- ------ -------- ----------
|
30
|
-
10.11.12.13 60 Origin
|
30
|
+
10.11.12.13 60 Origin - Active my-host
|
31
31
|
|
32
32
|
=== addition of a record
|
33
33
|
|
@@ -35,7 +35,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
35
35
|
shell> mrkmctl -L
|
36
36
|
IP address TTL Priority Weight Activity Hostname
|
37
37
|
--------------- ------ -------- ------ -------- ----------
|
38
|
-
10.11.12.13 60 Origin
|
38
|
+
10.11.12.13 60 Origin - Active my-host
|
39
39
|
10.11.12.13 300 Master 100 Active foo.bar
|
40
40
|
|
41
41
|
=== deletion of a record
|
@@ -44,7 +44,7 @@ https://bitbucket.org/winebarrel/murakumo
|
|
44
44
|
shell> mrkmctl -L
|
45
45
|
IP address TTL Priority Weight Activity Hostname
|
46
46
|
--------------- ------ -------- ------ -------- ----------
|
47
|
-
10.11.12.13 60 Origin
|
47
|
+
10.11.12.13 60 Origin - Active my-host
|
48
48
|
|
49
49
|
=== addition of a node
|
50
50
|
|
@@ -52,6 +52,6 @@ https://bitbucket.org/winebarrel/murakumo
|
|
52
52
|
shell> mrkmctl -L
|
53
53
|
IP address TTL Priority Weight Activity Hostname
|
54
54
|
--------------- ------ -------- ------ -------- ----------
|
55
|
-
10.11.12.13 60 Origin
|
56
|
-
10.11.12.14 60 Origin
|
55
|
+
10.11.12.13 60 Origin - Active my-host
|
56
|
+
10.11.12.14 60 Origin - Active other-host
|
57
57
|
|
data/etc/murakumo.server
CHANGED
@@ -77,6 +77,18 @@ max-ip-num: 8
|
|
77
77
|
#addr-includes: ^10\..*
|
78
78
|
#addr-excludes:
|
79
79
|
|
80
|
+
#balancing:
|
81
|
+
# ^app-.*: # destination alias regex
|
82
|
+
# algorithm: random # balancing algorithm
|
83
|
+
# max-ip-num: 1
|
84
|
+
# ^db-.*: # destination alias regex
|
85
|
+
# algorithm: fix_by_src # balancing algorithm
|
86
|
+
# sources: foo, bar # source alias name
|
87
|
+
# max-ip-num: 8
|
88
|
+
# ^cache-.*: # destination alias regex
|
89
|
+
# algorithm: fix_by_src2 # balancing algorithm
|
90
|
+
# sources: zoo # source alias name
|
91
|
+
|
80
92
|
#notification:
|
81
93
|
# host: my.smtp.server
|
82
94
|
# #port: 25
|
data/etc/murakumo.yml.example
CHANGED
@@ -17,6 +17,18 @@ max-ip-num: 8
|
|
17
17
|
#addr-includes: ^10\..*
|
18
18
|
#addr-excludes:
|
19
19
|
|
20
|
+
#balancing:
|
21
|
+
# ^app-.*: # destination alias regex
|
22
|
+
# algorithm: random # balancing algorithm
|
23
|
+
# max-ip-num: 1
|
24
|
+
# ^db-.*: # destination alias regex
|
25
|
+
# algorithm: fix_by_src # balancing algorithm
|
26
|
+
# sources: foo, bar # source alias name
|
27
|
+
# max-ip-num: 8
|
28
|
+
# ^cache-.*: # destination alias regex
|
29
|
+
# algorithm: fix_by_src2 # balancing algorithm
|
30
|
+
# sources: zoo # source alias name
|
31
|
+
|
20
32
|
#notification:
|
21
33
|
# host: my.smtp.server
|
22
34
|
# #port: 25
|
data/lib/cli/murakumo_options.rb
CHANGED
@@ -259,6 +259,55 @@ def murakumo_parse_args
|
|
259
259
|
end
|
260
260
|
end # {name,addr}-{includes,excludes}
|
261
261
|
|
262
|
+
# balancing
|
263
|
+
if options.config_file and (balancing = options.config_file['balancing'])
|
264
|
+
balancing.kind_of?(Hash) or parse_error('configuration of a balancing is not right')
|
265
|
+
balancing_h = options[:balancing] = {}
|
266
|
+
|
267
|
+
balancing.map {|k, v| [k.to_s.strip.downcase, v] }.each do |dest, attrs|
|
268
|
+
if dest.empty? or attrs.empty?
|
269
|
+
parse_error('configuration of a balancing is not right', dest)
|
270
|
+
end
|
271
|
+
|
272
|
+
unless attrs.kind_of?(Hash)
|
273
|
+
parse_error('configuration of a balancing is not right', dest)
|
274
|
+
end
|
275
|
+
|
276
|
+
attrs_algorithm = (attrs['algorithm'] || 'random').strip.downcase
|
277
|
+
attrs_max_ip_num = attrs['max-ip-num']
|
278
|
+
attrs_sources = (attrs['sources'] || '').strip.split(/\s*,\s*/).map {|i| i.strip }
|
279
|
+
|
280
|
+
unless %w(random fix_by_src fix_by_src2).include?(attrs_algorithm)
|
281
|
+
parse_error('configuration of a balancing is not right', dest)
|
282
|
+
end
|
283
|
+
|
284
|
+
unless attrs_max_ip_num.nil? or (/\A\d+\Z/ =~ attrs_max_ip_num.to_s and attrs_max_ip_num.to_i > 0)
|
285
|
+
parse_error('configuration of a balancing is not right', dest)
|
286
|
+
end
|
287
|
+
|
288
|
+
unless attrs_sources.empty? or attrs_sources.all? {|i| /\A[0-9a-z\.\-]+\Z/ =~ i }
|
289
|
+
parse_error('configuration of a balancing is not right', dest)
|
290
|
+
end
|
291
|
+
|
292
|
+
reg_dest = Regexp.new(dest, Regexp::IGNORECASE)
|
293
|
+
|
294
|
+
attrs_h = {
|
295
|
+
:algorithm => attrs_algorithm.to_sym,
|
296
|
+
:max_ip_num => (attrs_max_ip_num || options[:max_ip_num]).to_i
|
297
|
+
}
|
298
|
+
|
299
|
+
case attrs_algorithm
|
300
|
+
when 'random'
|
301
|
+
parse_error('configuration of a balancing is not right', dest) unless attrs_sources.empty?
|
302
|
+
when 'fix_by_src', 'fix_by_src2'
|
303
|
+
parse_error('configuration of a balancing is not right', dest) if attrs_sources.empty?
|
304
|
+
attrs_h[:sources] = attrs_sources
|
305
|
+
end
|
306
|
+
|
307
|
+
balancing_h[reg_dest] = attrs_h
|
308
|
+
end
|
309
|
+
end # balancing
|
310
|
+
|
262
311
|
end # after
|
263
312
|
|
264
313
|
error do |e|
|
data/lib/misc/murakumo_const.rb
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'misc/murakumo_const'
|
2
|
+
|
3
|
+
module Murakumo
|
4
|
+
|
5
|
+
class Balancer
|
6
|
+
|
7
|
+
def initialize(hash, address, db, logger)
|
8
|
+
@hash = hash
|
9
|
+
@address = address
|
10
|
+
@db = db
|
11
|
+
@logger = logger
|
12
|
+
end
|
13
|
+
|
14
|
+
def sort(records, max_ip_num, name)
|
15
|
+
# ハッシュが空ならランダムで
|
16
|
+
if @hash.nil? or @hash.empty?
|
17
|
+
return random(records, max_ip_num)
|
18
|
+
end
|
19
|
+
|
20
|
+
# 宛先を検索
|
21
|
+
dest, attrs = @hash.find {|k, v| k =~ name }
|
22
|
+
|
23
|
+
if dest.nil? or attrs.nil?
|
24
|
+
# 設定が見つからない場合はとりあえずランダムで
|
25
|
+
return random(records, max_ip_num)
|
26
|
+
end
|
27
|
+
|
28
|
+
algo = attrs[:algorithm]
|
29
|
+
max_ip_num = [(attrs[:max_ip_num] || max_ip_num), records.length].min
|
30
|
+
sources = attrs[:sources]
|
31
|
+
|
32
|
+
case algo
|
33
|
+
when :random
|
34
|
+
random(records, max_ip_num)
|
35
|
+
when :fix_by_src
|
36
|
+
fix_by_src(records, max_ip_num, sources)
|
37
|
+
when :fix_by_src2
|
38
|
+
fix_by_src2(records, max_ip_num, sources)
|
39
|
+
else
|
40
|
+
# 未対応のアルゴリズムの場合はとりあえずランダムで返す
|
41
|
+
@logger.warn("distribution setup which is not right: #{[dest, algo].inspect}")
|
42
|
+
random(records, max_ip_num)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# 重み付きランダム(デフォルト)
|
49
|
+
def random(records, max_ip_num)
|
50
|
+
indices = []
|
51
|
+
buf = []
|
52
|
+
|
53
|
+
# インデックスをWeight分追加
|
54
|
+
records.each_with_index do |r, i|
|
55
|
+
weight = r['weight']
|
56
|
+
weight.times { buf << i }
|
57
|
+
end
|
58
|
+
|
59
|
+
# インデックスをシャッフル
|
60
|
+
buf = buf.sort_by{ rand }
|
61
|
+
|
62
|
+
# ランダムにインデックスを取り出す
|
63
|
+
loop do
|
64
|
+
indices << buf.shift
|
65
|
+
indices.uniq!
|
66
|
+
break if (indices.size >= max_ip_num or buf.empty?)
|
67
|
+
end
|
68
|
+
|
69
|
+
# インデックスのレコードを返す
|
70
|
+
records.values_at(*indices)
|
71
|
+
end
|
72
|
+
|
73
|
+
def fix_by_src(records, max_ip_num, src_aliases)
|
74
|
+
fix_by_src0(records, max_ip_num, src_aliases) do |new_records|
|
75
|
+
# そのまま評価
|
76
|
+
new_records.slice(0, max_ip_num)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def fix_by_src2(records, max_ip_num, src_aliases)
|
81
|
+
fix_by_src0(records, max_ip_num, src_aliases) do |new_records|
|
82
|
+
# 先頭 + ランダムを返す
|
83
|
+
first = new_records.shift
|
84
|
+
[first] + new_records.slice(0, max_ip_num - 1).sort_by { rand }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# ソースで宛先を固定
|
89
|
+
def fix_by_src0(records, max_ip_num, src_aliases)
|
90
|
+
joined = src_aliases.map {|i| "'#{i.downcase}'" }.join(',')
|
91
|
+
|
92
|
+
# ソースエイリアスでIPアドレスを探す
|
93
|
+
sources = @db.execute(<<-EOS, ACTIVE).map {|i| i['ip_address'] }.sort
|
94
|
+
SELECT ip_address FROM records
|
95
|
+
WHERE name IN (#{joined}) AND activity = ?
|
96
|
+
EOS
|
97
|
+
|
98
|
+
# ソースが見つからない場合はとりあえずランダムで
|
99
|
+
if sources.empty?
|
100
|
+
@logger.warn("source is not found: #{src_aliases.join(',')}")
|
101
|
+
return random(records, max_ip_num)
|
102
|
+
end
|
103
|
+
|
104
|
+
# ソースが自分を含んでいない場合はとりあえずランダムで
|
105
|
+
unless sources.include?(@address)
|
106
|
+
@logger.warn("sources does not contain self: #{@address}")
|
107
|
+
return random(records, max_ip_num)
|
108
|
+
end
|
109
|
+
|
110
|
+
# 宛先をソート
|
111
|
+
dests = (0...records.length).map {|i| [records[i]['ip_address'], i] }.sort_by {|a, b| a }
|
112
|
+
dests_orig = dests
|
113
|
+
|
114
|
+
# 数をそろえる
|
115
|
+
if sources.length < dests.length
|
116
|
+
dests = dests.slice(0, sources.length)
|
117
|
+
elsif sources.length > dests.length
|
118
|
+
dests = dests_orig = dests * (sources.length.to_f / dests.length).ceil
|
119
|
+
dests = dests.slice(0, sources.length)
|
120
|
+
end
|
121
|
+
|
122
|
+
# 先頭を決めてローテート
|
123
|
+
first_index = sources.zip(dests).index {|s, d| s == @address }
|
124
|
+
|
125
|
+
unless first_index.zero?
|
126
|
+
dests = (dests_orig + dests_orig).slice(first_index, dests.length)
|
127
|
+
end
|
128
|
+
|
129
|
+
# 先頭インデックスからレコードを並べ直す
|
130
|
+
yield(records.values_at(*dests.map {|addr, i| i }))
|
131
|
+
end # fix_by_src0
|
132
|
+
|
133
|
+
end # Balancer
|
134
|
+
|
135
|
+
end # Murakumo
|
data/lib/srv/murakumo_cloud.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rgossip2'
|
|
3
3
|
require 'sqlite3'
|
4
4
|
|
5
5
|
require 'srv/murakumo_health_checker'
|
6
|
+
require 'srv/murakumo_balancer'
|
6
7
|
require 'misc/murakumo_const'
|
7
8
|
|
8
9
|
module Murakumo
|
@@ -97,6 +98,9 @@ module Murakumo
|
|
97
98
|
|
98
99
|
# キャッシュ
|
99
100
|
@cache = {} if options[:enable_cache]
|
101
|
+
|
102
|
+
# バランサー
|
103
|
+
@balancer = Balancer.new(@options[:balancing], @address, @db, @logger)
|
100
104
|
end
|
101
105
|
|
102
106
|
# Control of service
|
@@ -182,6 +186,10 @@ module Murakumo
|
|
182
186
|
hash[key] = @options.config_file[key]
|
183
187
|
end
|
184
188
|
end
|
189
|
+
|
190
|
+
if @options.config_file['balancing']
|
191
|
+
hash['balancing'] = @options.config_file['balancing']
|
192
|
+
end
|
185
193
|
end # 設定ファイルのみの項目
|
186
194
|
|
187
195
|
return hash
|
@@ -452,15 +460,21 @@ module Murakumo
|
|
452
460
|
return @address_records.map {|i| i.values_at('ip_address', 'ttl') }
|
453
461
|
end
|
454
462
|
|
463
|
+
# 名前は小文字に変換
|
464
|
+
name = name.downcase
|
465
|
+
|
466
|
+
# ドメインが指定されていたら削除
|
467
|
+
name.sub!(/\.#{Regexp.escape(@options[:domain])}\Z/i, '') if @options[:domain]
|
468
|
+
|
455
469
|
# 優先度の高いレコードを検索
|
456
|
-
records = shuffle_records(@address_records.select {|i| i['priority'] == MASTER })
|
470
|
+
records = shuffle_records(@address_records.select {|i| i['priority'] == MASTER }, name)
|
457
471
|
|
458
472
|
# 次に優先度の高いレコードを検索
|
459
|
-
records.concat(shuffle_records(@address_records.select {|i| i['priority'] == SECONDARY }))
|
473
|
+
records.concat(shuffle_records(@address_records.select {|i| i['priority'] == SECONDARY }, name))
|
460
474
|
|
461
475
|
# レコードが見つからなかった場合はバックアップを選択
|
462
476
|
if records.empty?
|
463
|
-
records = shuffle_records(@address_records.select {|i| i['priority'] == BACKUP })
|
477
|
+
records = shuffle_records(@address_records.select {|i| i['priority'] == BACKUP }, name)
|
464
478
|
end
|
465
479
|
|
466
480
|
# それでもレコードが見つからなかった場合はオリジンを選択
|
@@ -524,33 +538,14 @@ module Murakumo
|
|
524
538
|
private
|
525
539
|
|
526
540
|
# 乱数でレコードをシャッフルする
|
527
|
-
def shuffle_records(records)
|
541
|
+
def shuffle_records(records, name)
|
528
542
|
# レコードが1件以下の時はそのまま返す
|
529
543
|
return records if records.length <= 1
|
530
544
|
|
531
545
|
max_ip_num = [records.length, @options[:max_ip_num]].min
|
532
546
|
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
# インデックスをWeight分追加
|
537
|
-
records.each_with_index do |r, i|
|
538
|
-
weight = r['weight']
|
539
|
-
weight.times { buf << i }
|
540
|
-
end
|
541
|
-
|
542
|
-
# インデックスをシャッフル
|
543
|
-
buf = buf.sort_by{ rand }
|
544
|
-
|
545
|
-
# ランダムにインデックスを取り出す
|
546
|
-
loop do
|
547
|
-
indices << buf.shift
|
548
|
-
indices.uniq!
|
549
|
-
break if (indices.size >= max_ip_num or buf.empty?)
|
550
|
-
end
|
551
|
-
|
552
|
-
# インデックスのレコードを返す
|
553
|
-
records.values_at(*indices)
|
547
|
+
# バランサーでソートする
|
548
|
+
@balancer.sort(records, max_ip_num, name)
|
554
549
|
end
|
555
550
|
|
556
551
|
# リソースレコードのデータベース作成
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: murakumo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 8
|
10
|
+
version: 0.3.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- winebarrel
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-12-
|
18
|
+
date: 2011-12-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rubydns
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/cli/mrkmctl_options.rb
|
102
102
|
- lib/cli/murakumo.rb
|
103
103
|
- lib/srv/murakumo_health_check_notifier.rb
|
104
|
+
- lib/srv/murakumo_balancer.rb
|
104
105
|
- lib/srv/murakumo_server.rb
|
105
106
|
- lib/srv/murakumo_health_checker.rb
|
106
107
|
- lib/srv/murakumo_cloud.rb
|
@@ -140,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
141
|
requirements: []
|
141
142
|
|
142
143
|
rubyforge_project:
|
143
|
-
rubygems_version: 1.8.
|
144
|
+
rubygems_version: 1.8.1
|
144
145
|
signing_key:
|
145
146
|
specification_version: 3
|
146
147
|
summary: Murakumo is the internal DNS server which manages name information using a gossip protocol.
|