본문 바로가기

빅데이터/Kafka

Kafka-client client.dns.lookup 옵션 정리

Kafka-client 2.1.0 이후 버젼에서는 client.dns.lookup옵션을 사용하여 dns관련 설정을 사용할 수 있습니다. 오늘 포스팅에서는 해당 옵션이 어떤 역할을 하고 어떻게 동작하는지 알아보도록 하겠습니다.

먼저, apache kafka document의 설명을 보도록 하겠습니다.

client.dns.lookup document

client.dns.lookup: Controls how the client uses DNS lookups. If set to use_all_dns_ips then, when the lookup returns multiple IP addresses for a hostname, they will all be attempted to connect to before failing the connection. Applies to both bootstrap and advertised servers. If the value is resolve_canonical_bootstrap_servers_only each entry will be resolved and expanded into a list of canonical names.

- Type: string
- Default: default
- Valid Values: [default, use_all_dns_ipsresolve_canonical_bootstrap_servers_only]
- Importance: medium

 

client.dns.lookup옵션은 Kafka client를 위한 DNS lookup옵션입니다. 만약 use_all_dns_ips 옵션을 사용하면 bootstrap의 dns domain에 접속시 multiple ip가 등록된 hostname이라면 모든 ip에 대해 connection을 시도하게 됩니다. 만약 resolve_canonical_bootstrap_servers_only옵션을 사용한다면 해당 hostname의 canonical name을 모두 가져와서 bootstrap에 등록하게 됩니다.

client.dns.lookup = default

default 옵션은 kafka client의 기본 동작 옵션입니다. bootstrap으로 설정한 여러개의 ip(또는 domain name)을 하나씩 호출하면서 유효한 kafka broker에 대해 연결 및 advertised.listener를 return받아 통신을 시작하게 됩니다.

다만 이 동작에 이슈가 있는데 bootstrap에 domain으로 등록할때 첫번째로 호출되는 첫번째 ip에 대해서만 connection을 시도한다는 것입니다. 이러한 동작방식은 1 domain name에 n ip(multiple ip)가 등록되어 있을 경우 나머지 ip는 무시되어 동작하게 됩니다.

// index 0번 ip를 사용하는 default 옵션
InetAddress.getAllByName(hostname)[0]

그러므로 기본(default)옵션을 사용할 때는 각 ip를 comma separate로 풀어서 bootstrap server에 등록하도록 합니다.

configs.put("bootstrap.servers", "test-broker01:9092,test-broker02:9092,test-broker03:9092");
configs.put("client.dns.lookup", "default");

client.dns.lookup = use_all_dns_ips

use_all_dns_ips 옵션은 default 동작을 보완하기 위해 나왔습니다. KIP-302에서 논의된 이 옵션은 kafka-client 2.1.0에서 최초 적용되었습니다. 기존에 적용되었던 옵션으로는 bootstrap에 등록된 domain name의 첫번째 ip만 사용하였으나 개선되 코드를 통해 모든 ip에 대해 connection 요청을 하게 되었습니다.

// hostname의 모든 ip return받아서 connection요청
InetAddress.getAllByName(hostname)

위와 같은 변경사항을 통해 Kafka client는 1개의 Domain name에 multiple broker ip가 등록된 경우 유연하게 접근할 수 있게 되었습니다. 예를 들자면 broker의 개수가 변경될때 마다 kafka-client의 bootstrap server의 ip 리스트를 수정할 필요가 없다는 점이죠.

이 옵션을 사용하는 상세 예제는 아래 포스트에서 확인하실 수 있습니다. 

 

카프카 클러스터 클러스터ip DNS 연동방법. use_all_dns_ips 사용(in AWS, route53)

Kafka-client(consumer, producer)를 사용하기 위해서는 다양한 설정이 필요하지만 카프카 브로커와 통신하기 위해서는 bootstrap.servers 옵션은 반드시 필요한 옵션중 하나입니다. Bootstrap.servers 이 옵션은..

blog.voidmainvoid.net

client.dns.lookup = resolve_canonical_bootstrap_servers_only

이 옵션은 default나 use_all_dns_ips 옵션과는 약간은 결이 다릅니다. 이 옵션은 Kerberos의 SASL 인증을 통한 Kafka 연동을 위해 KIP-235에서 제안되었습니다. 이를 해결하기 위해 사용된 resolve_canonical_bootstrap_servers_only옵션을 사용하게 되면 KafkaConsumer(또는 Producer)가 최초 인스턴스 생성시 bootsrap의 모든 ip에 대해 reverse lookup을 수행하게 됩니다.

org.apache.kafka.clients.ClientUtils.java

if (clientDnsLookup == ClientDnsLookup.RESOLVE_CANONICAL_BOOTSTRAP_SERVERS_ONLY) {
InetAddress[] inetAddresses = InetAddress.getAllByName(host);
for (InetAddress inetAddress : inetAddresses) {
    String resolvedCanonicalName = inetAddress.getCanonicalHostName();
    InetSocketAddress address = new InetSocketAddress(resolvedCanonicalName, port);
    if (address.isUnresolved()) {
        log.warn("Couldn't resolve server {} from {} as DNS resolution of the canonical hostname {} failed for {}", url, CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, resolvedCanonicalName, host);
    } else {
        addresses.add(address);
    }
}

 

이 옵션은 kerberos 인증을 사용할 때 이 옵션을 사용하여 domain관련 오류를 피할 수 있습니다.

그런데 만약 1 domain name에 multiple ip가 등록된 bootstrap을 등록하고 이 옵션을 사용하면 어떻게 될까요? KafkaConsumer의 인스턴스 생성시 bootstrap의 해당 domain name의 multiple ip를  풀어서 bootstrap server 리스트에 등록하게 됩니다. use_all_dns_ips 옵션과 유사하게 동작하게 됩니다. 다만 언제 해당 domain의 ip들을 lookup하느냐 차이입니다.


옵션별 동작방식

위 옵션별로 bootstrap과 resolve시에 어떻게 등록되는지 살펴보도록 하겠습니다. 아래와 같이 domain이 등록되어 있다고 가정해 봅시다.

먼저, 아래와 같이 default 옵션이 설정된 경우를 살펴봅시다.

configs.put("bootstrap.servers", "kafka.voidmainvoid.net:9092");
configs.put("client.dns.lookup", "default");

위와 같은 config로 설정된 경우 첫번째 index ip에 대해서 인스턴스가 생성될 때 bootstrap에 등록되게 되고, polling과 같은 실 동작시에도 첫번째 ip로만 호출합니다. 

Bootstrap :  kafka.voidmainvoid.net/54.180.98.4:9092
Resolve ip : [kafka.voidmainvoid.net/54.180.98.4]

이러한 동작은 해당 ip의 broker가 불의의 사고로 죽었을 때 다른 ip에 붙지 못하고 계속 첫번째 index의 ip만 호출하게 되므로 정상적인 동작이 아닙니다.

두번째는 use_all_dns_ips 옵션을 사용한 경우입니다.

configs.put("bootstrap.servers", "kafka.voidmainvoid.net:9092");
configs.put("client.dns.lookup", "use_all_dns_ips");

이 경우 bootstrap에서는 해당 domain name을 그대로 가져가면서 클라이언트 인스턴스가 생성됩니다. 다만, 추후 polling과 같은 동작이 일어날 때 서버와 connection을 맺을 때 해당 domain name을 모두 resolve하여 connection시도를 합니다.

Bootstrap :  kafka.voidmainvoid.net/54.180.98.4:9092
Resolve ip : [kafka.voidmainvoid.net/54.180.98.4, kafka.voidmainvoid.net/15.164.97.6, kafka.voidmainvoid.net/54.180.93.146]

만약 54.180.98.4 broker가 down되더라도 다른 ip의 broker에 connection을 성공적으로 시도하여 connection을 유지하게 됩니다.

 

세번째는 resolve_canonical_bootstrap_servers_only 옵션을 사용한 경우입니다.

configs.put("bootstrap.servers", "kafka.voidmainvoid.net:9092");
configs.put("client.dns.lookup", "resolve_canonical_bootstrap_servers_only");

이 경우 최초 인스턴스 생성시 domain name에 대해 모든 ip를 가져와서 bootstrap에 등록하게 됩니다.

Bootstrap :  ec2-54-180-98-4.ap-northeast-2.compute.amazonaws.com/54.180.98.4:9092,ec2-15-164-97-6.ap-northeast-2.compute.amazonaws.com/15.164.97.6:9092,ec2-54-180-93-146.ap-northeast-2.compute.amazonaws.com/54.180.93.146:9092
Resolve ip : [kafka.voidmainvoid.net/54.180.98.4]

마치 host name의 모든 ip를 bootsrap에 풀어서 등록한것과 같은 효과가 되었습니다. 위와 같이 1 domain name에 multiple ip를 사용할 경우 사실상 use_all_dns_ips와 비슷한 동작을 한다고 볼 수 있습니다. 

 

결론

위 옵션을 통해 Kafka client는 매우 큰 강점을 가지게 되었습니다.

첫번째로 DNS를 통한 broker ip등록을 통해 Kafka client들을 재배포하지 않고 broker의 유동적인 ip를 등록할 수 있게 되었습니다.
두번째로 이러한 ip확장성을 활용하여 Kafka cluster DR(
disaster recovery)에도 응용할 수 있습니다. 
세번째로 Kerberos 인증시 에러를 회피할 수 있습니다.

이러한 강점을 통해 Kafka관련 생태계를 재정리하고 유지보수의 효율성을 높였습니다. 

 

의견

client.dns.lookup 옵션을 이해 하는데 매우 많은 시간이 걸렸습니다. 왜냐면 default를 제외한 2개의 동작이 테스트환경(1 domain name to multiple ip)에서 완벽하게 동일하게 동작했기 때문입니다. KIP를 읽어보더라도 뜬금없이 kerberos관련 이야기가 나오기도 해서 더욱 복잡하게 느껴졌었습니다.

그러나 Kafka source 전체를 찬찬히 살펴보고 sample application을 개발하여 궁금한 지점마다 break point를 걸어 각 동작이 어떤 흐름으로 돌아가는지 확인하고 보니 전체적인 흐름과 사용하는 이유, 왜 똑같은 동작이 발생했는지 이해할수 있게 되었습니다.

그리고 각 option별 KIP, email discussion thread, vote thread를 살펴봄으로서 어떤방식으로 option 이름이 선택되었는지에 대한 내막도 알게되었습니다. 특히 email discussion thread은 매우 인상깊었습니다. Apache kafka Committer들이 왜 이렇게 client.dns.lookup 옵션에서 3개의 option으로 나누게 되었는지에 대해 자세히 나와 있었기 때문입니다.(원래는 2개의 option으로 4개의 dns 개별 동작을 제공하려고 했었음)

Confluent Engineer Rajini Sivaram의 discussion thread 중 하나

위와 같이 공개된 내용을 통해 개발자들은 왜 이러한 옵션이 신규 버젼에 출시되었고 언제 이런 옵션을 사용해야 하는지도 공감할 수 있습니다.

Discussion 문서를 읽으면서 이게 바로 open source지! 라는 생각이 들었습니다. 말그대로 '모든 내용'이 open되어 있었기 때문입니다. 개인적으로 이 옵션에 대해 조사하는 모든 과정이 흥미로웠으며, 오픈소스에 대한 존경심도 함께 들게 되는 순간이였습니다.

 

이 글을 보는 여러분들도 open source의 사용방법이나 신규 옵션들에 대해 조사할 때 이러한 내막을 더욱 면밀히 조사하길 추천드립니다. 그렇게 한다면 더욱 높은 이해도로 open source를 활용할 수 있게되어 더욱 알맞은 옵션을 통해 안정적이고 완벽한 시스템을 구축할 수 있게 될 것입니다.

 

끝가지 읽어주셔서 감사합니다.

태그