Wednesday, November 9, 2022

Sharing secret between Gunicorn workers

 1. we found nice post from @jgleeee [link] [source code]; so we can use following to share object between workers.

  multiprocessing.Value

multiprocessing.Array

                multiprocessing.Manager

1.1. after digging, we found it's based on connection (pipe) across processes (it can operate across network). we think it's too complicated for a simple usage.


2. during digging into multiprocessing, we found shared memory which more match our scenario. [link]

2.1 based on SharedMemory we build following building block; 


# create
created = False
try:
shared, created = SharedMemory(name="_shared_name", create=True, size=size), True
except FileExistsError:
shared = SharedMemory(name="_shared_name")

# read
print(bytes(shared.buf))

# write
shared.buf[:size] = new_content[:size]

we now able to share secrets between process. but we found shared content can be simply fetched via
cat /dev/shm/_shared_name, CLEAR TEXT.

3. we need hide cleartext, choices are: 1. send across process, or 2. encrypt shared memory. obviously the choice is encryption.

3.1 need to share an encryption key between workers. this looks bring us back to memory sharing loop. but we found another way: gunicorn's pre_fork() is called before forking and in master process, post_fork() is called after forking and in worker process. so we build following building block.


def pre_fork(arbiter, worker):
__secret = getattr(arbiter, '__secret', None)
if not __secret:
arbiter.__secret = os.urandom(16)

worker.__secret = arbiter.__secret
if len(arbiter.WORKERS) + 1 == workers: # workers is final fork count
del arbiter.__secret


def post_fork(arbiter, worker):
SECRET = worker.__secret
del worker.__secret

after this, we shared static secret between workers.

3.2 connect static part and dynamic part

def has_data(obj, size):
return bytes(obj.buf) != b'\x00' * size


def set_data(obj, size):
obj.buf[:size] = aes.encrypt_cbc(data=data, key=SECRET)[:size]


def clear_data(obj, size):
obj.buf[:size] = b'\x00' * size


def get_data(obj, size):
return aes.decrypt_cbc(data=bytes(obj.buf), key=SECRET)

# ALL DONE




Sunday, August 14, 2022

OpenSSL & SM2 commands

create sm2 private key

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2 -out sm2key.pem


view sm2 key

openssl ec -in sm2key.pem -noout  -text


sign sm2 with sm3 and distid ( aka userid or uid )

openssl dgst -sm3 -hex -sign sm2key.pem -sigopt distid:userid file_to_sign


gmssl sm2 sign

./gmssl sm2sign

usage: sm2sign -key pem -pass str [-id str] [-in file] [-out file]

 

Monday, March 28, 2022

network troubleshooting with pv4.tcp_timestamp

in my work context, our company primarily develops proxy servers.

as of proxy server, we need to handle clients request, translate, transform, and then pass them to real backend servers. which we call them source server/site, or real server for more common name.


once in fault investigation, we found that our proxy server cannot connect to real server: www.cnki.net. 

in docker container which proxy server lives in, the TCP SYN packet just goes silent, which has no response completely.


in our further test, we found

1. other real server can be reached, and works normal

2. after random period, we can reach the real server for a short while.

3. in the end, we noticed the host server which ships docker container, can reach the real server correctly, and without interrupt.

4. mtr/traceroute shows TCP connectable to real server, even in container which runs proxy server.


this is weird, and we don't have any other option than utilize tcpdump and wireshark to analysis the sent packet, and try to find the differece.

and then we does that. but found no different between SYN packets. same field, same flag, even same TCP options shipped, just same everything. 


we stuck there for a while. suddenly we found an option is not very same, that was the timestamp option in TCP header.


the TSVal within container is significantly smaller than outside one.


 

we instantly reminded that linux has an option named ipv4.tcp_tw_recycle that considers timestamp value as proof whether packet is before or after recycling. the option is so buggy, that was removed from Linux 4.12 on 2017.

see also: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4396e46187ca5070219b81773c4e65088dac50cc


if one packet is considered before TIME_WAIT recycling, the linux kernel will drop that packet silently, without any notice.


the behavior is ok on internet, in beginning. but as IPv4 address is depleted, NAT gateway is all over the world, the behavior become buggy as real server sits behind the NAT gateway.

NAT gateway translate TCP packets, modify source IP to NAT gateway itself, assign random port of itself, and hides the real source IP and port. 

but ipv4.tcp_tw_recycle will treat same source IP + port as same remote host, which being replaced to NAT gateway's.


here is the problem, NAT gateway will reuse same self-IP and same port for different real remote client. when linux kernel recycles TIME_WAIT sockets, the source ip + port tuple represents more than one real remote host. and the TCP timestamp clock differs between there hosts. for example, host A will use 100k as timestamp, while host B uses 800k as timestamp; once they shares same NAT gateway's IP & port, and the sockets been recycled. host A's 101k timestamp will be explicitly dropped as of the linux kernel record the last timestamp for that source IP + port as 800k.


as we discovered the root cause of this situation, which is a incorrect configuration of the real site server. normal resolution is notice the website manager who runs server behind NAT Gateway to change that setting, which is set ipv4.tcp_tw_recycle to 0.


but the site manager is unreachable, and our product should not be affected by the website setting. we found the option ipv4.tcp_timestamp in the end. when set to zero, our TCP stack dismisses the timestamp option in the outgoing TCP packets. which disables website server's recycling logic: once no timestamp can be remembered, it is unable to drop packets from that port safely.

also, we found same solution for docker containers, which use sysctl option to modify container's system configuration.


PS

after that incident, we has further investigation for mtr & traceroute which can reach the real server on same host.

found that traceroute is not utilizing linux kernel's TCP stack, instead, it uses random_req() to generate a large number. this explains why traceroute can reach the real server, while host's TCP stack cannot.



PS2

we also investigated TCP stack's timestamp generation, it's from tcp_time_stamp_raw() which invokes ktime_get_ns()

https://github.com/torvalds/linux/blob/master/kernel/time/timekeeping.c#L817

https://www.kernel.org/doc/html/latest/core-api/timekeeping.html


see also
1. we can use bpf_ktime_get_ns in userspace to get system-space's ktime_get_ns(), also noticed that `bpf_ktime_get_ns and clock_gettime(MONOTONIC) are not based on the same time source`.
https://stackoverflow.com/questions/60970877/xdp-bpf-is-there-an-user-space-alternative-to-bpf-ktime-get-ns


2. RFC that describes TCP timestamp option.

https://datatracker.ietf.org/doc/html/rfc1323

Sunday, July 25, 2021

disk tests between HDD servers

SERVER-ID 40143   

~# dd if=/dev/zero of=/root/testfile bs=4k count=10000 oflag=direct
10000+0 records in
10000+0 records out
40960000 bytes (41 MB, 39 MiB) copied, 0.446108 s, 91.8 MB/s
~# dd if=/dev/zero of=/root/testfile bs=1M count=1000 oflag=direct
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 4.92345 s, 213 MB/s
~# fio -filename=/root/test_randwrite -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=4k -size=10M -numjobs=10 -runtime=10 -group_reporting -name=mytest
mytest: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=1
...
fio-3.12
Starting 10 threads
mytest: Laying out IO file (1 file / 10MiB)
Jobs: 9 (f=9): [w(8),_(1),w(1)][55.6%][w=6224KiB/s][w=1556 IOPS][eta 00m:08s]
mytest: (groupid=0, jobs=10): err= 0: pid=24501: Sun Jul 25 18:53:39 2021
write: IOPS=1533, BW=6132KiB/s (6279kB/s)(59.9MiB/10010msec); 0 zone resets
clat (usec): min=47, max=781215, avg=6432.29, stdev=45635.14
lat (usec): min=47, max=781216, avg=6433.16, stdev=45635.14
clat percentiles (usec):
| 1.00th=[ 178], 5.00th=[ 231], 10.00th=[ 249], 20.00th=[ 277],
| 30.00th=[ 302], 40.00th=[ 326], 50.00th=[ 371], 60.00th=[ 453],
| 70.00th=[ 693], 80.00th=[ 1123], 90.00th=[ 1467], 95.00th=[ 1762],
| 99.00th=[267387], 99.50th=[362808], 99.90th=[583009], 99.95th=[658506],
| 99.99th=[759170]
bw ( KiB/s): min= 8, max= 4456, per=10.56%, avg=647.79, stdev=777.62, samples=188
iops : min= 2, max= 1114, avg=161.94, stdev=194.40, samples=188
lat (usec) : 50=0.01%, 100=0.03%, 250=10.20%, 500=52.54%, 750=9.03%
lat (usec) : 1000=5.70%
lat (msec) : 2=18.89%, 4=1.36%, 10=0.21%, 20=0.06%, 50=0.03%
lat (msec) : 100=0.07%, 250=0.72%, 500=0.96%, 750=0.18%, 1000=0.01%
cpu : usr=0.25%, sys=1.81%, ctx=30705, majf=0, minf=0
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,15346,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
WRITE: bw=6132KiB/s (6279kB/s), 6132KiB/s-6132KiB/s (6279kB/s-6279kB/s), io=59.9MiB (62.9MB), run=10010-10010msec
Disk stats (read/write):
sda: ios=0/15187, merge=0/33, ticks=0/9174, in_queue=8432, util=83.67%


SERVER-ID 40396

~# dd if=/dev/zero of=/root/testfile bs=4k count=500 oflag=dsync
\500+0 records in
500+0 records out
2048000 bytes (2.0 MB, 2.0 MiB) copied, 71.0946 s, 28.8 kB/s
~# dd if=/dev/zero of=/root/testfile bs=1M count=50 oflag=dsync
50+0 records in
50+0 records out
52428800 bytes (52 MB, 50 MiB) copied, 6.77477 s, 7.7 MB/s~# dd if=/dev/zero of=/root/testfile bs=1M count=1000 oflag=direct
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 7.87863 s, 133 MB/s
~# dd if=/dev/zero of=/root/testfile bs=4k count=10000 oflag=direct
10000+0 records in
10000+0 records out
40960000 bytes (41 MB, 39 MiB) copied, 1.713 s, 23.9 MB/s

 

~# fio -filename=/root/test_randwrite -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=4k -size=10M -numjobs=10 -runtime=10 -group_reporting -name=mytest
mytest: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=1
...
fio-3.12
Starting 10 threads
mytest: Laying out IO file (1 file / 10MiB)
Jobs: 10 (f=10): [w(10)][100.0%][w=156KiB/s][w=39 IOPS][eta 00m:00s]
mytest: (groupid=0, jobs=10): err= 0: pid=15239: Sun Jul 25 18:54:02 2021
write: IOPS=49, BW=198KiB/s (203kB/s)(2040KiB/10292msec); 0 zone resets
clat (usec): min=193, max=10131k, avg=199604.88, stdev=1234842.93
lat (usec): min=193, max=10131k, avg=199605.71, stdev=1234842.92
clat percentiles (usec):
| 1.00th=[ 200], 5.00th=[ 215], 10.00th=[ 233],
| 20.00th=[ 269], 30.00th=[ 310], 40.00th=[ 359],
| 50.00th=[ 545], 60.00th=[ 8586], 70.00th=[ 13173],
| 80.00th=[ 27919], 90.00th=[ 76022], 95.00th=[ 92799],
| 99.00th=[ 9328133], 99.50th=[10133439], 99.90th=[10133439],
| 99.95th=[10133439], 99.99th=[10133439]
bw ( KiB/s): min= 8, max= 504, per=69.61%, avg=137.83, stdev=132.79, samples=29
iops : min= 2, max= 126, avg=34.38, stdev=33.19, samples=29
lat (usec) : 250=16.47%, 500=33.33%, 750=2.16%, 1000=0.20%
lat (msec) : 2=0.59%, 4=0.59%, 10=14.51%, 20=7.25%, 50=7.45%
lat (msec) : 100=12.94%, 250=0.98%, 500=0.20%, 750=0.39%, 1000=0.39%
cpu : usr=0.02%, sys=0.04%, ctx=1035, majf=0, minf=0
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,510,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
WRITE: bw=198KiB/s (203kB/s), 198KiB/s-198KiB/s (203kB/s-203kB/s), io=2040KiB (2089kB), run=10292-10292msec
Disk stats (read/write):
sda: ios=0/512, merge=0/42, ticks=0/14112, in_queue=6744, util=63.97%
~# uname -a
Linux WebVPN-Master-Fiber 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux

Wednesday, October 11, 2017

forward: unicode for chinese characters

source:  http://ju.outofmemory.cn/entry/53571

关于unicode汉字范围正则的写法
Chronos 2013-10-21 7952 阅读
正则 范围 unicode 汉字
原来我使用的一直是 \u4e00-\u9fa5 ,今天在匹配中文标点的时候匹配不上,就查了一下相关资料,原来unicode跟中文有关的范围还有好几个。

字符范围表
1.标准CJK文字

范围:\u3400-\u4DB5,\u4E00-\u9FA5,\u9FA6-\u9FBB,\uF900-\uFA2D,\uFA30-\uFA6A,\uFA70-\uFAD9 说明:一共有好几个范围,除 \u4e00-\u9fa5外 都不是很常用 参考地址:http://www.unicode.org/Public/UNIDATA/Unihan.html

2.全角ASCII、全角中英文标点、半宽片假名、半宽平假名、半宽韩文字母

范围:\uFF00-\uFFEF 参考地址:http://www.unicode.org/charts/PDF/UFF00.pdf

3.CJK部首补充

范围:\u2E80-\u2EFF 参考地址:http://www.unicode.org/charts/PDF/U2E80.pdf

4.CJK标点符号

范围:\u3000-\u303F 参考地址:http://www.unicode.org/charts/PDF/U3000.pdf

5.CJK笔划

范围:\u31C0-\u31EF 参考地址:http://www.unicode.org/charts/PDF/U31C0.pdf

6.康熙部首

范围:\u2F00-\u2FDF 参考地址:http://www.unicode.org/charts/PDF/U2F00.pdf

7.汉字结构描述字符

范围:\u2FF0-\u2FFF 参考地址:http://www.unicode.org/charts/PDF/U2FF0.pdf

8.注音符号

范围:\u3100-\u312F 参考地址:http://www.unicode.org/charts/PDF/U3100.pdf

9.注音符号(闽南语、客家语扩展)

范围:\u31A0-\u31BF 参考地址:http://www.unicode.org/charts/PDF/U31A0.pdf

10.日文平假名

范围:\u3040-\u309F 参考地址:http://www.unicode.org/charts/PDF/U3040.pdf

11.日文片假名

范围:\u30A0-\u30FF 参考地址:http://www.unicode.org/charts/PDF/U30A0.pdf

12.日文片假名拼音扩展

范围:\u31F0-\u31FF 参考地址:http://www.unicode.org/charts/PDF/U31F0.pdf

13.韩文拼音

范围:\uAC00-\uD7AF 参考地址:http://www.unicode.org/charts/PDF/UAC00.pdf

14.韩文字母

范围:\u1100-\u11FF 参考地址:http://www.unicode.org/charts/PDF/U1100.pdf

15.韩文兼容字母

范围:\u3130-\u318F 参考地址:http://www.unicode.org/charts/PDF/U3130.pdf

16.易经六十四卦象

范围:\u4DC0-\u4DFF 参考地址:http://www.unicode.org/charts/PDF/U4DC0.pdf

17.彝文音节

范围:\uA000-\uA48F 参考地址:http://www.unicode.org/charts/PDF/UA000.pdf

18.彝文部首

范围:\uA490-\uA4CF 参考地址:http://www.unicode.org/charts/PDF/UA490.pdf

19.盲文符号

范围:\u2800-\u28FF 参考地址:http://www.unicode.org/charts/PDF/U2800.pdf

20.CJK字母及月份

范围:\u3200-\u32FF 参考地址:http://www.unicode.org/charts/PDF/U3200.pdf

21.CJK特殊符号(日期合并)

范围:\u3300-\u33FF 参考地址:http://www.unicode.org/charts/PDF/U3300.pdf

22.装饰符号(非CJK专用)

范围:\u2700-\u27BF 参考地址:http://www.unicode.org/charts/PDF/U2700.pdf

23.杂项符号(非CJK专用)

范围:\u2600-\u26FF 参考地址:http://www.unicode.org/charts/PDF/U2600.pdf

24.中文竖排标点

范围:\uFE10-\uFE1F 参考地址:http://www.unicode.org/charts/PDF/UFE10.pdf

25.CJK兼容符号(竖排变体、下划线、顿号)

范围:\uFE30-\uFE4F 参考地址:http://www.unicode.org/charts/PDF/UFE30.pdf

改进后的匹配表达式
[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF] (注:这条基本能满足要求)
[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF\u2F00-\u2FDF\u2FF0-\u2FFF\u3100-\u312F\u31A0-\u31BF\u3040-\u309F\u30A0-\u30FF\u31F0-\u31FF\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F\u4DC0-\u4DFF\uA000-\uA48F\uA490-\uA4CF\u2800-\u28FF\u3200-\u32FF\u3300-\u33FF\u2700-\u27BF\u2600-\u26FF\uFE10-\uFE1F\uFE30-\uFE4F] (注:这是完整版本)

Monday, August 21, 2017

recursive dns resolution

recursive dns resolution, [gist link], requirements:

  • dns
  • clientsubnetoption


# -*- coding: utf8 -*-
"""@author: boyxuper@date: 2017/8/21 16:07"""import random
import socket
from collections import defaultdict
from itertools import chain

import clientsubnetoption
import dns
import dns.name
import dns.message
import dns.rdatatype
import dns.resolver

_logger = lambda *args: None

def logger_full(fmt, *args):
    if not args:
        print fmt
        return
    print fmt % args

socket.inet_pton = lambda _, p: socket.inet_aton(p)
socket.inet_ntop = lambda _, n: socket.inet_ntoa(n)


def retry(n=3, exc_list=()):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            tried = 1            while tried <= n:
                try:
                    return fn(*args, **kwargs)
                except exc_list as err:
                    print 'RETRY %s/%s, [%s]' % (tried, n, err)
                    tried += 1            else:
                raise err
        return wrapper
    return decorator


@retry(n=5, exc_list=(dns.exception.Timeout, dns.resolver.NoAnswer, ))
def query_dns0(domain, ns_ip, client_ip=None, **kwargs):
    kwargs.setdefault('rdtype', dns.rdatatype.A)
    message = dns.message.make_query(domain, **kwargs)
    if client_ip:
        message.use_edns(options=[clientsubnetoption.ClientSubnetOption(client_ip)])

    if callable(ns_ip):
        ns_ip = ns_ip()
    return dns.query.udp(message, ns_ip, timeout=TIMEOUT), ns_ip


# only authority can be cachedKNOWN_AUTHORITIES = defaultdict(list, **{
    '.': ['192.5.5.241', '199.7.83.42', '192.58.128.30', '192.36.148.17'],
    'net.': [
        '192.48.79.30', '192.35.51.30', '192.52.178.30', '192.5.6.30', 
        '192.26.92.30', '192.41.162.30', '192.12.94.30', '192.54.112.30', 
        '192.31.80.30', '192.43.172.30', '192.33.14.30', '192.55.83.30', '192.42.93.30'],
    'com.': ['192.33.14.30', '192.12.94.30', '192.55.83.30', '192.52.178.30',
             '192.26.92.30', '192.48.79.30', '192.5.6.30', '192.43.172.30',
             '192.42.93.30', '192.31.80.30', '192.54.112.30', '192.35.51.30', '192.41.162.30'],
    'cn.': [
        '203.119.29.1', '203.119.27.1', '203.119.28.1',
        '203.119.26.1', '202.112.0.44', '203.119.25.1'],
})
NS_IPS = defaultdict(set)
TIMEOUT = 2

def locate_nearest_authority(dns_name):
    """    :type dns_name: dns.name.Name    """    for depth in range(len(dns_name), 0, -1):
        _, sub = dns_name.split(depth)
        if sub.to_text() in KNOWN_AUTHORITIES:
            return sub, KNOWN_AUTHORITIES[sub.to_text()]

    assert False, 'impossible'

def is_ip(s):
    try:
        socket.inet_aton(s)
    except:
        return False    else:
        return True

def _authority_iterator(authority_ips, client_ip, logger=_logger):
    pos = random.randint(0, len(authority_ips))

    # for short ips    while True:
        non_ips = []
        for name in chain(authority_ips[pos:], authority_ips[:pos]):
            if is_ip(name):
                yield name
            else:
                non_ips.append(name)

        for name in non_ips:
            if name in NS_IPS:
                server_ips = list(NS_IPS[name])
            else:
                _, server_ips, _ = resolve_A(name, client_ip=client_ip, logger=logger)
                NS_IPS[name] = set(server_ips)

            yield server_ips[pos % len(server_ips)]


@retry(n=5, exc_list=(dns.exception.Timeout, dns.resolver.NoAnswer, ))
def query_dns(dns_name, client_ip=None, logger=_logger, **kwargs):
    sub, authority_ips = locate_nearest_authority(dns_name)
    iterator = _authority_iterator(authority_ips, client_ip, logger=_logger)

    logger('querying %s @NS"%s": %r', dns_name, sub, authority_ips)
    response, ns_ip = query_dns0(dns_name, ns_ip=iterator.next, client_ip=client_ip, **kwargs)
    resp_code = response.rcode()
    if resp_code != dns.rcode.NOERROR:
        if resp_code == dns.rcode.NXDOMAIN:
            raise Exception('%s does not exist on %s.' % (sub, ns_ip))
        else:
            raise Exception('Error %s' % dns.rcode.to_text(resp_code))

    return response


def resolve_A(domain, client_ip, logger=_logger):
    """     :return: answers        response.authority.__len__() == 1    response.authority.name == {Name}a.shifen.com.    response.authority.items:        0 = {NS} ns2.a.shifen.com.        1 = {NS} ns3.a.shifen.com.        2 = {NS} ns4.a.shifen.com.        3 = {NS} ns1.a.shifen.com.        4 = {NS} ns5.a.shifen.com.    """    if not domain.endswith('.'): domain += '.'
    dns_name = dns.name.from_text(domain)

    while True:
        response = query_dns(dns_name, client_ip=client_ip, logger=logger)
        # logger(response)
        instant_ips = defaultdict(list)
        final_answer = None
        # authority contains NS should be processed last, so it can leverage the instant IPs        for answer in chain(response.additional, response.answer, response.authority):
            answer_name = answer.name.to_text()

            for item in answer.items:
                item_text = item.to_text()
                if item.rdtype == dns.rdatatype.A:
                    if answer_name == domain: final_answer = answer
                    instant_ips[answer_name].append(item_text)
                elif item.rdtype == dns.rdatatype.NS:
                    if domain not in instant_ips:
                        logger('got NS: %s -> %s', answer_name, item_text)

                    if item_text in instant_ips:
                        authority_ips = instant_ips[item_text]
                        NS_IPS[item_text].update(authority_ips)
                    else:
                        authority_ips = [item_text]

                    KNOWN_AUTHORITIES[answer_name].extend(authority_ips)
                elif item.rdtype == dns.rdatatype.CNAME:
                    logger('CNAME: %s -> %s', domain, item_text)
                    domain, dns_name = item_text, dns.name.from_text(item_text)

        if domain in instant_ips:
            return domain, instant_ips[domain], final_answer.ttl


if __name__ == '__main__':
    log = _logger
    log = logger_full
    print resolve_A('www.baidu.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('www.taobao.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('www.google.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('dl.tiku.zhan.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('ns3.dnsv4.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('www.facebook.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('api.taoqian123.com', client_ip='8.8.8.8', logger=log)
    print resolve_A('hotsoon.snssdk.com', client_ip='66.103.188.117', logger=log)

Thursday, April 6, 2017

kubernetes 1.6环境下使用storageClass与ceph rbd整合

在每个k8s node上要安装ceph-common,确保有rbd命令
安装secret(集群级别)
apiVersion: v1
kind: Secret
metadata: name: ceph-secret
type: kubernetes.io/rbd   #非常重要,如果想让storageclass识别必须加这个,文档示例上没写,但是example里写了
data: key: QVFDZ2ZOOVkza3VyR3hBQXNYYmx6Mi9xVlBZNzN0VWZvMUlFRlE9PQ==  # ceph auth get-key client.admin | base64

安装storageClass(集群级别)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata: name: fast
provisioner: kubernetes.io/rbd
parameters: monitors: 10.1.4.208:6789
  adminId: admin
  adminSecretName: ceph-secret
  pool: rbd
  userId: admin
  userSecretName: ceph-secret

安装PVC(namespace级别)
  1. 向配置的stroageClass申请PV
  2. 绑定后PV相当于卷,pod挂载时是挂载对PVC的引用
  3. 目前只支持指定大小,实际上代码修改后可以支持对fsType等的指定(目前不支持)
  4. 挂载方式是使用docker HostConfig.Binds来挂载,所以
    1. 在node上看mount会看到:/dev/rbd0 on /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/rbd-image-kubernetes-dynamic-pvc-59474c44-1a77-11e7-8b1a-fa163e0dfa6d type ext4 (rw,relatime,stripe=4096,data=ordered)
    2. HostConfig.Binds:"/var/lib/kubelet/pods/b84a50db-1aa7-11e7-a0c8-fa163e0dfa6d/volumes/kubernetes.io~rbd/pvc-3c34a5f2-1a95-11e7-a0c8-fa163e0dfa6d:/mnt/rbd-rox"
kind: PersistentVolumeClaim
apiVersion: v1
metadata: name: pvc-rox
spec: accessModes:  - ReadOnlyMany
  resources: requests: storage: 100Mi
  storageClassName: fast

安装pod:
apiVersion: v1
kind: Pod
metadata: name: rbd-test5
spec: containers:  - name: rbd-rw
      image: docker.cloudin.com/cloudin/alpine:latest
      command: ["/bin/sleep", "10000"]
      volumeMounts:  - mountPath: "/mnt/rbd"  name: rbd-rox  # 也可以配置subPath,使用该pvc中的一个子目录
  volumes:  - name: rbd-rox
      persistentVolumeClaim: claimName: pvc-rox


疑难问题:
  1. 检查锁定的image: rbd lock list aaa.img --pool kube
  2. 检查当前节点的rbd map信息rbd showmapped
  3. osd节点支持使用目录(系统原生fs)或块设备来创建,如果原生fs为ext4时,会有可能出现:File name too long
    1. 配置调整ceph.conf
      1. osd max object name len = 256
      2. osd max object namespace len = 64
  4. rbd
    1. 稳定性依赖于ceph
    2. 仅支持rox / rwo
    3. 需要rwx就上ceph fs (文件系统级互斥锁)
  5. ceph:rbd image有4个 features,layering, exclusive-lock, object-map, fast-diff, deep-flatten 因为目前内核仅支持layering
    1. 修改默认配置 每个ceph node的/etc/ceph/ceph.conf 添加一行 rbd_default_features = 1 这样之后创建的image 只有这一个feature
      1. 验证方式:ceph --show-config|grep rbd|grep features
      2. rbd_default_features = 1
    2. ceph osd crush show-tunables -f json-pretty
      1. ceph osd crush tunables legacy
  6. k8s < 1.6是不支持在pvc中使用storageClassName的
  7. 对于k8s 1.6,docker版本最好是1.12,在centos7是存在于默认yum源中的
  8. 如果使用kubeadm部署的k8s,controll-manager会在容器内运行
    1. failed to create rbd image: executable file not found in $PATH
    2. 需要在容器内安装rbd或改为系统环境运行
  9. 跟踪日志:journalctl -fu kubelet / journalctl -f
  10. dowload_image.sh
    1. docker pull zeewell/$1
      docker tag zeewell/$1 gcr.io/google_containers/$1
  11. 需要下载的镜像
    1. bash download.sh etcd-amd64:3.0.17
      bash download.sh kube-controller-manager-amd64:v1.6.0
      bash download.sh kube-proxy-amd64:v1.6.0
      bash download.sh k8s-dns-sidecar-amd64:1.14.1
      bash download.sh k8s-dns-kube-dns-amd64:1.14.1
      bash download.sh k8s-dns-dnsmasq-nanny-amd64:1.14.1
  12. vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
    1. 注释掉 #Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
    2. KUBELET_KUBECONFIG_ARGS需要添加 --cgroup-driver=systemd

refs:
  1. http://foxhound.blog.51cto.com/1167932/1899545
  2. http://docs.ceph.com/docs/master/start/quick-ceph-deploy/
  3. http://docs.ceph.com/docs/master/install/manual-deployment/
  4. https://github.com/kubernetes/kubernetes/tree/master/examples/persistent-volume-provisioning/rbd
  5. http://www.tuicool.com/articles/vQr6zaV
  6. http://www.tuicool.com/articles/feyiMr6
  7. http://tonybai.com/2016/11/07/integrate-kubernetes-with-ceph-rbd/
  8. http://webcache.googleusercontent.com/search?q=cache:XESMwMuMZTEJ:lists.ceph.com/pipermail/ceph-users-ceph.com/2015-July/002815.html+&cd=1&hl=en&ct=clnk
  9. https://kubernetes.io/docs/concepts/storage/persistent-volumes/