Hlavní navigace

Sledování činnosti systému Apache Kafka přes JMX i metriky Promethea (dokončení)

24. 6. 2021
Doba čtení: 27 minut

Sdílet

 Autor: Apache Foundation
Minule jsme si popsali základní technologie, které lze využít pro sledování činnosti Apache Kafka. Popíšeme si, které metriky je vhodné sledovat. Taktéž se zmíníme o tzv. four-letter words ZooKeepera.

Obsah

1. Sledování činnosti systému Apache Kafka přes JMX i metriky Promethea (dokončení)

2. Přístup z lokální JConsole na službu běžící na lokálním JVM

3. Přístup z lokální JConsole na vzdálený počítač

4. Jednoduché zabezpečení přístupu přes jmxremote.password

5. Omezení přístupu definované v souboru jmxremote.access

6. Chování jconsole pro uživatele s rolemi readonly a readwrite

7. Použití SSL v reálném provozu

8. Metriky, které je vhodné sledovat

9. Metriky samotného brokera

10. Metriky vztažené k producentům

11. Vytvoření jednoduchého producenta zpráv naprogramovaného v JVM jazyce

12. Metriky vztažené ke konzumentům

13. Vytvoření jednoduchého konzumenta zpráv naprogramovaného v JVM jazyce

14. Metriky virtuálního stroje Javy

15. Metriky ZooKeepera

16. Povolení „four-letter words“ ZooKeepera

17. Repositář s demonstračními příklady

18. Odkazy na relevantní články na Rootu

19. Odkazy na Internetu

1. Sledování činnosti systému Apache Kafka přes JMX i metriky Promethea (dokončení)

Na úvodní článek, v němž jsme si popsali základní technologie, které lze využít pro sledování činnosti systému Apache Kafka, dnes navážeme. Již víme, že jak Kafka brokeři, tak i ZooKeeper nabízí své metriky přes rozhraní nazvané JMX neboli Java Management Extensions. Takové metriky lze přes JMX zpracovávat několika nástroji, mezi než patří i standardní nástroj nazvaný jconsole, který je dodávaný společně s JDK (ovšem nikoli se samotným JRE, tedy s běhovým prostředím Javy). Existuje ovšem i knihovna nazvaná JMX Exporter, která dokáže tyto metriky zpřístupnit i ve formátu kompatibilním s Prometheem a nepřímo tak i s Grafanou, tedy s technologiemi, jež se v současnosti velmi často používají v praxi, například pro monitoring mikroslužeb, databází, message brokerů atd. (stručně řečeno – metriky budou dostupné přes běžné HTTP se všemi výhodami, které to přináší).

Obrázek 1: ZooKeeper i Apache Kafka běžící lokálně lze sledovat právě přes nástroj jconsole.

Poznámka: několik úvodních kapitol je sice věnováno problematice přístupu k metrikám reprezentovaným s využitím MBeans a dostupných přes JMX, ovšem popis samotných metrik je již nezávislý na tom, zda je pro přístup k nim použit JMX či kombinace Prometheus+Grafana.

Obrázek 2: Příklad „dashboardu“ vytvořeného pro účely sledování Apache Kafky v Grafaně. Důležitými metrikami jsou frekvence příchozích zpráv i celkový „lag“, tedy zpoždění konzumentů zpráv oproti producentům.

2. Přístup z lokální JConsole na službu běžící na lokálním JVM

Minule jsme si ukázali, jakým způsobem je možné se připojit nástrojem jconsole k vybranému lokálnímu virtuálnímu stroji Java, na němž běží sledovaná aplikace nabízející své metriky přes JMX. Celý postup si připomeneme na několika screenshotech, v němž je sledován běžící demonstrační příklad:

Obrázek 3: Po spuštění nástroje jconsole se zobrazí všechny nalezené aplikace běžící nad JVM. Vybereme aplikaci Main.

Obrázek 4: Povolíme připojení bez SSL (ostatně jedná se o lokální JVM).

Obrázek 5: Následně se zobrazí základní informace o sledované aplikaci (obsazení paměti apod.).

Obrázek 6: Na listu MBeans se kromě standardních MBeans objeví i jmenný prostor pojmenovaný „cz.root.app“.

3. Přístup z lokální JConsole na vzdálený počítač

Nyní si ukážeme, jak lze nakonfigurovat vzdálené připojení. Princip je jednoduchý – použijeme lokální instalaci nástroje jconsole (je součástí JDK) a připojíme se s ní k virtuálnímu stroji Javy, který ovšem běží vzdáleně (na jiném počítači). Nejprve si připravíme náš testovací příklad na vzdáleném počítači (může jít samozřejmě o virtuální počítač, pokud ovšem správně nastavíte mapování portů). Připojíme se k tomuto počítači, získáme (stáhneme) zdrojové kódy příkladu a přeložíme ho:

$ javac Main.java 

Spuštění ovšem bude vypadat odlišně, protože budeme muset povolit vzdálený přístup k JMX a taktéž budeme muset nastavit port, na němž bude JXM dostupné. Vybereme port 1099, ovšem pochopitelně je možné v případě potřeby zvolit i jiný port:

$ java -Dcom.sun.management.jmxremote.authenticate=false \
       -Dcom.sun.management.jmxremote.ssl=false \
       -Dcom.sun.management.jmxremote.port=1099 \
       Main
Poznámka: port samozřejmě může být libovolný, ovšem typicky větší než 1024 (menší čísla portů vyžadují rootovská oprávnění).

Nyní na lokálním počítači spustíme nástroj jconsole:

$ export _JAVA_OPTIONS="-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dsun.java2d.xrender=true"
$ jconsole

Samotné připojení ovšem vypadá odlišně, protože je nutné (logicky) ručně specifikovat jméno vzdáleného počítače i port, na kterém je JMX dostupné:

Obrázek 7: Specifikace jména či IP adresy vzdáleného počítače a portu, na kterém je JMX dostupné (seznam lokálních JVM tedy v tomto případě ignorujeme).

Obrázek 8: Připojování ke vzdálené JVM, resp. k aplikaci, které v rámci této JVM běží.

4. Jednoduché zabezpečení přístupu přes jmxremote.password

Řešení ukázané v předchozí kapitole je vhodné využít pouze v těch případech, kdy je nutné sledovat Kafku běžící například v testovacím prostředí. Problém tohoto řešení spočívá v tom, že kdokoli, kdo „odhadne“ port používaný JMX, se může vzdáleně připojit a sledovat metriky, popř. volat metody dostupné přes MBeans. Ukažme si proto alespoň minimální způsoby zabezpečení. První z nich spočívá v tom, že se vytvoří soubor pojmenovaný (typicky) jmxremote.password, který bude obsahovat dvojice jméno+heslo, a to v čitelné podobě (!). Tento soubor, resp. přesněji řečeno jeho ukázková varianta, je součástí instalace JRE. Například na mém systému je tento soubor umístěn v adresáři /usr/lib/jvm/java/jre/lib/management (ovšem tato cesta může být na jiných systémech odlišná, ostatně i zde je složena ze symlinků nastavovaných pomocí alternatives):

$ ls -l /usr/lib/jvm/java/jre/lib/management
total 28
-rw-r--r--. 1 root root  3998 Nov  1  2018 jmxremote.access
-r--r--r--. 1 root root  2856 Nov  1  2018 jmxremote.password.template
-rw-r--r--. 1 root root 14630 Nov  1  2018 management.properties
-r--r--r--. 1 root root  3376 Nov  1  2018 snmp.acl.template

Soubor jmxremote.password.template zkopírujte pod jménem jmxremote.password do adresáře s demonstračním příkladem. V dalším kroku nastavíme práva souboru na u+r tedy -r-------- (žádná jiná – je to kontrolováno při inicializaci JMX!). Na konec souboru lze přidat uživatelská jména a hesla:

# ----------------------------------------------------------------------
#           Template for jmxremote.password
#
# o Copy this template to jmxremote.password
# o Set the user/password entries in jmxremote.password
# o Change the permission of jmxremote.password to read-only
#   by the owner.
#
# See below for the location of jmxremote.password file.
# ----------------------------------------------------------------------
 
##############################################################
#        Password File for Remote JMX Monitoring
##############################################################
#
# Password file for Remote JMX API access to monitoring.  This
# file defines the different roles and their passwords.  The access
# control file (jmxremote.access by default) defines the allowed
# access for each role.  To be functional, a role must have an entry
# in both the password and the access files.
#
# Default location of this file is $JRE/lib/management/jmxremote.password
# You can specify an alternate location by specifying a property in
# the management config file $JRE/lib/management/management.properties
# or by specifying a system property (See that file for details).
 
 
##############################################################
#    File permissions of the jmxremote.password file
##############################################################
#      Since there are cleartext passwords stored in this file,
#      this file must be readable by ONLY the owner,
#      otherwise the program will exit with an error.
#
# The file format for password and access files is syntactically the same
# as the Properties file format.  The syntax is described in the Javadoc
# for java.util.Properties.load.
# Typical password file has multiple  lines, where each line is blank,
# a comment (like this one), or a password entry.
#
#
# A password entry consists of a role name and an associated
# password.  The role name is any string that does not itself contain
# spaces or tabs.  The password is again any string that does not
# contain spaces or tabs.  Note that passwords appear in the clear in
# this file, so it is a good idea not to use valuable passwords.
#
# A given role should have at most one entry in this file.  If a role
# has no entry, it has no access.
# If multiple entries are found for the same role name, then the last one
# is used.
#
# In a typical installation, this file can be read by anybody on the
# local machine, and possibly by people on other machines.
# For # security, you should either restrict the access to this file,
# or specify another, less accessible file in the management config file
# as described above.
#
# Following are two commented-out entries.  The "measureRole" role has
# password "QED".  The "controlRole" role has password "R&D".
#
monitorRole  nbusr123
controlRole  nbusr789

Příklad se posléze spustí s těmito volbami:

$ java -Dcom.sun.management.jmxremote.authenticate=true \
       -Dcom.sun.management.jmxremote.ssl=false \
       -Dcom.sun.management.jmxremote.password.file=jmxremote.password \
       -Dcom.sun.management.jmxremote.port=1099 \
       Main

Do připojovacího dialogu je nyní nutné jméno i heslo zapsat:

Obrázek 9: Zadání jména a hesla při připojování ke vzdálené JVM.

Obrázek 10: Stav po přihlášení ke vzdálené JVM.

Poznámka: v žádném případě se nejedná o ideální řešení a použití souboru s čitelnými hesly znamená, že není doporučováno zadávat skutečná uživatelská jména a hesla, přesněji řečeno takové kombinace, které jsou použity i jinde.

5. Omezení přístupu definované v souboru jmxremote.access

Dalším důležitým souborem, který naleznete v adresáři /usr/lib/jvm/java/jre/lib/management, je soubor nazvaný jmxremote.access:

$ ls -l /usr/lib/jvm/java/jre/lib/management
total 28
-rw-r--r--. 1 root root  3998 Nov  1  2018 jmxremote.access
-r--r--r--. 1 root root  2856 Nov  1  2018 jmxremote.password.template
-rw-r--r--. 1 root root 14630 Nov  1  2018 management.properties
-r--r--r--. 1 root root  3376 Nov  1  2018 snmp.acl.template

V tomto souboru jsou pro jednotlivé uživatele definovány přístupové role, které jsou pouze dvě: readonly a readwrite. Ukázková varianta tohoto souboru vypadá takto:

######################################################################
#     Default Access Control File for Remote JMX(TM) Monitoring
######################################################################
#
# Access control file for Remote JMX API access to monitoring.
# This file defines the allowed access for different roles.  The
# password file (jmxremote.password by default) defines the roles and their
# passwords.  To be functional, a role must have an entry in
# both the password and the access files.
#
# The default location of this file is $JRE/lib/management/jmxremote.access
# You can specify an alternate location by specifying a property in
# the management config file $JRE/lib/management/management.properties
# (See that file for details)
#
# The file format for password and access files is syntactically the same
# as the Properties file format.  The syntax is described in the Javadoc
# for java.util.Properties.load.
# A typical access file has multiple lines, where each line is blank,
# a comment (like this one), or an access control entry.
#
# An access control entry consists of a role name, and an
# associated access level.  The role name is any string that does not
# itself contain spaces or tabs.  It corresponds to an entry in the
# password file (jmxremote.password).  The access level is one of the
# following:
#       "readonly" grants access to read attributes of MBeans.
#                   For monitoring, this means that a remote client in this
#                   role can read measurements but cannot perform any action
#                   that changes the environment of the running program.
#       "readwrite" grants access to read and write attributes of MBeans,
#                   to invoke operations on them, and optionally
#                   to create or remove them. This access should be granted
#                   only to trusted clients, since they can potentially
#                   interfere with the smooth operation of a running program.
#
# The "readwrite" access level can optionally be followed by the "create" and/or
# "unregister" keywords.  The "unregister" keyword grants access to unregister
# (delete) MBeans.  The "create" keyword grants access to create MBeans of a
# particular class or of any class matching a particular pattern.  Access
# should only be granted to create MBeans of known and trusted classes.
#
# For example, the following entry would grant readwrite access
# to "controlRole", as well as access to create MBeans of the class
# javax.management.monitor.CounterMonitor and to unregister any MBean:
#  controlRole readwrite \
#              create javax.management.monitor.CounterMonitorMBean \
#              unregister
# or equivalently:
#  controlRole readwrite unregister create javax.management.monitor.CounterMBean
#
# The following entry would grant readwrite access as well as access to create
# MBeans of any class in the packages javax.management.monitor and
# javax.management.timer:
#  controlRole readwrite \
#              create javax.management.monitor.*,javax.management.timer.* \
#              unregister
#
# The \ character is defined in the Properties file syntax to allow continuation
# lines as shown here.  A * in a class pattern matches a sequence of characters
# other than dot (.), so javax.management.monitor.* matches
# javax.management.monitor.CounterMonitor but not
# javax.management.monitor.foo.Bar.
#
# A given role should have at most one entry in this file.  If a role
# has no entry, it has no access.
# If multiple entries are found for the same role name, then the last
# access entry is used.
#
#
# Default access control entries:
# o The "monitorRole" role has readonly access.
# o The "controlRole" role has readwrite access and can create the standard
#   Timer and Monitor MBeans defined by the JMX API.
 
monitorRole   readonly
controlRole   readwrite \
              create javax.management.monitor.*,javax.management.timer.* \
              unregister

Tento soubor si opět můžete zkopírovat do adresáře s demonstračním příkladem a specifikovat ho při spuštění:

$ java -Dcom.sun.management.jmxremote.authenticate=true
       -Dcom.sun.management.jmxremote.ssl=false
       -Dcom.sun.management.jmxremote.password.file=jmxremote.password
       -Dcom.sun.management.jmxremote.access.file=jmxremote.access
       -Dcom.sun.management.jmxremote.port=1099
       Main

6. Chování jconsole pro uživatele s rolemi readonly a readwrite

Vzhledem k tomu, že v souboru s hesly již máme specifikována dvě jména:

monitorRole  nbusr123
controlRole  nbusr789

… s příslušnými rolemi nastavenými v souboru jmxremote.access:

monitorRole   readonly
controlRole   readwrite

… si můžeme odzkoušet chování v jconsoli po přihlášení těchto uživatelů.

Nejprve pro uživatele s rolí readonly:

Obrázek 11: Přihlášení uživatele s rolí readonly.

Obrázek 12: Všechny atributy dostupné přes MBeans jsou čitelné.

Obrázek 13: Dostupná (resp. přesněji řečeno viditelná) je i metoda definovaná v MBeanu.

Obrázek 14: Ovšem ve skutečnosti ji nemůžeme zavolat, což přesně odpovídá vybrané roli.

Následně provedeme tytéž operace, ovšem pro uživatele s rolí readwrite:

Obrázek 15: Přihlášení uživatele s rolí readwrite.

Obrázek 16: Dostupná (resp. viditelná) je i metoda definovaná v MBeanu.

Obrázek 17: Tuto metodu lze nyní bez problémů zavolat, což opět přesně odpovídá zvolené roli.

7. Použití SSL v reálném provozu

V reálném provozu je navíc nutné zajistit, aby se jméno a heslo přenášelo přes zabezpečený komunikační kanál. Ten zajišťuje SSL (Secure Sockets Layer). Nejprve je nutné nástrojem keytool s přepínačem -genkeypair (ten je dodáván společně s JDK) vygenerovat dvojici klíčů. Následně si vyžádat certifikát od některé důvěryhodné certifikační autority. Tento certifikát se naimportuje, a to opět nástrojem keytool (přepínač –import). Cesty k souboru s klíči a certifikátem se použijí při spuštění aplikace (volby javax.new.ssl…).

Poznámka: využít lze i soubor s klíči dostupný v celém systému (typicky uložen v /etc).

Vlastní spuštění sledované aplikace, která má přes JMX poskytovat metriky, nyní proběhne takto:

$ java -Dcom.sun.management.jmxremote.authenticate=true\
       -Dcom.sun.management.jmxremote.ssl=true \
       -Dcom.sun.management.jmxremote.password.file=jmxremote.password \
       -Dcom.sun.management.jmxremote.port=1099 \
       -Dcom.sun.management.jmxremote.ssl.need.client.auth=true \
       -Djavax.net.ssl.keyStore=/home/${USER}/.keystore
       -Djavax.net.ssl.keyStorePassword=${PASSWORD}
       -Djava.security.manager
       -Djava.security.policy=jmx.policy
       Main
Poznámka: pochopitelně vše bude funkční jen za předpokladu, že uživatel má v systému dostupnou svoji část klíče.

8. Metriky, které je vhodné sledovat

Již minule jsme si ve stručnosti řekli, že Apache Kafka, resp. přesněji řečeno její jednotlivé části (tedy typicky ZooKeeper a jednotliví brokeři) vytváří a „vystavují“ poměrně velké množství metrik. Ty je možné rozdělit do čtyř skupin:

  1. Metriky samotného brokera
  2. Metriky vztažené k producentům
  3. Metriky vztažené ke konzumentům
  4. Metriky ZooKeepera (což je samostatný proces)

Jednotlivé důležité metriky budou zmíněny v navazujících kapitolách.

Obrázek 18: MBeans poskytované Kafka brokerem.

Praktická poznámka: pokud kliknete na nějaký číselný atribut, dokáže jconsole zobrazit graf s vývojem hodnoty tohoto atributu v čase. Nejedná se sice o nahrazení Grafany, ovšem i tak je to velmi praktická výpomoc.

9. Metriky samotného brokera

Důležité metriky, které se vztahují přímo k činnosti brokeru (každý běží ve vlastní JVM), jsou vypsány v následující tabulce:

# Jméno metriky Označení MBeanu Stručný popis metriky
1 UnderReplicatedPartitions kafka.server:type=Replica­Manager,name=UnderReplica­tedPartitions zvyšuje se nad nulu, pokud není nějaký broker dostupný (ideálně se zde očekává nulová hodnota)
2 IsrShrinksPerSec kafka.server:type=Replica­Manager,name=IsrShrinksPer­Sec změna v počtu replik, které jsou synchronizovány, nutno sledovat společně s IsrExpandsPerSec
3 IsrExpandsPerSec kafka.server:type=Replica­Manager,name=IsrExpandsPer­Sec tato hodnota typicky následuje (s časovým zpožděním) předchozí hodnotu. Nutno sledovat rozdíly, tj. stav, kdy se počet replik opět nezvýší na původní hodnotu.
4 ActiveControllerCount kafka.controller:type=Kaf­kaController,name=ActiveCon­trollerCount počet aktivních controllerů (což je typicky první uzel, který je spuštěn). Pouze jediný broker by měl mít hodnotu nastavenou na 1, ostatní na nulu (jiné hodnoty značí možné problémy se ZooKeeperem).
5 OfflinePartitionsCount kafka.controller:type=Kaf­kaController,name=Offline­PartitionsCount počet oddílů bez leadera. Nenulová hodnota značí potenciální problém.
6 LeaderElectionRateAndTimeMs kafka.controller:type=Con­trollerStats,name=LeaderE­lectionRateAndTimeMs čas, který byl stráven změnou leadera v případě, že předchozí leader je z nějakého důvodu nedostupný. Typické hodnoty jsou v řádu milisekund.
7 TotalTimeMs kafka.network:type=Reques­tMetrics,name=TotalTimeMs čtyři metriky s časy, které byly stráveny čekáním požadavků ve frontě, v leaderu, čekáním na další požadavek atd. Většinou nemá smysl sledovat absolutní hodnoty, ale spíše velikosti změn.
8 PurgatorySize kafka.server:type=DelayedO­perationPurgatory,name=Pur­gatorySize čas strávený vyřízením požadavku. Jedná se o dvě metriky: jedna pro čekající požadavky (nejsou k dispozici data), druhá pro požadavky čekající na ack
9 BytesInPerSec kafka.server:type=BrokerTo­picMetrics,name=BytesInPer­Sec tato metrika sleduje síťové přenosy, konkrétně počet bajtů přijatých za sekundu (síť může být úzkým hrdlem, ovšem méně často, než diskové operace)
10 BytesOutPerSec kafka.server:type=BrokerTo­picMetrics,name=BytesOutPer­Sec tato metrika taktéž sleduje síťové přenosy, konkrétně počet bajtů odeslaných za sekundu
10 RequestsPerSecond kafka.network:type=Reques­tMetrics,name=RequestsPer­Sec počet požadavků od producentů a konzumentů. Vysoké hodnoty (nebo jejich změna) může znamenat připojení špatně naprogramovaného klienta.
Poznámka: poslední metriku lze „zlepšit“ tím, že se povolí blokové operace jak na producentech, tak i na konzumentech (samozřejmě jen tam, kde to má smysl).

Obrázek 19: MBeans dostupné v prostoru kafka.server.

Obrázek 20: MBeans dostupné v prostoru kafka.controller.

Obrázek 21: MBeans dostupné v prostoru kafka.network.

10. Metriky vztažené k producentům

Každý producent zpráv, který je naprogramován v Javě, Scale, Clojure atd. (tedy v jazyku běžícím nad JVM) a který komunikuje s Apache Kafkou, nabízí následující důležité metriky, které lze přes JMX sledovat:

# Jméno metriky Označení MBeanu Stručný popis metriky
1 compression-rate-avg kafka.producer:type=producer-metrics,client-id=([-.w]+) míra komprimace zpráv posílaných producentem
2 response-rate kafka.producer:type=producer-metrics,client-id=([-.w]+) rychlost odpovědí od brokera, ke kterému je producent připojen (ovlivněno volbou request.required.acks, měla by být víceméně stabilní)
3 request-rate kafka.producer:type=producer-metrics,client-id=([-.w]+) rychlost požadavků producenta, lze omezit pomocí technologie rate-limit
4 request-latency-avg kafka.producer:type=producer-metrics,client-id=([-.w]+) časové zpoždění mezi posláním zprávy a odpovědí brokeru (měla by být stabilní)
5 outgoing-byte-rate kafka.producer:type=producer-metrics,client-id=([-.w]+) měří míru využití sítě, resp. rychlost přenosu dat
6 io-wait-time-ns-avg kafka.producer:type=producer-metrics,client-id=([-.w]+) čekání způsobené tím, že producent posílá více zpráv, než broker dokáže zpracovat (hodnota by měla být co nejnižší)
7 batch-size-avg kafka.producer:type=producer-metrics,client-id=([-.w]+) velikost bloku, který se posílá (16 kB je výchozí hodnota), pokud producent čeká na brokera

Obrázek 22: Sledování metrik producenta zpráv.

Obrázek 23: Graf zobrazený u vybrané metriky.

11. Vytvoření jednoduchého producenta zpráv naprogramovaného v JVM jazyce

Metriky související s producenty zpráv jsou (pochopitelně) nabízeny těmi producenty, které jsou naprogramovány v nějakém jazyce, který generuje bajtkód zpracovávaný virtuálním strojem Javy. Pro zajímavost si tedy vytvoříme producenta nikoli přímo v Javě (takový producent je použitý v každém tutoriálu), ale v jazyce Clojure. Využijeme přitom informace, které jsou zmíněny v článku Pokročilý streaming založený na projektu Apache Kafka, jazyku Clojure a knihovně Jackdaw (streamy a kolony).

Do projektového souboru se doplní závislost na knihovně jackdaw:

(defproject produce-messages-1 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [fundingcircle/jackdaw "0.7.6"]]
  :main ^:skip-aot produce-messages-4.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Samotný producent zpráv generuje 1000 zpráv s frekvencí přibližně jedna zpráva za sekundu:

(ns produce-messages-4.core
  (:require [jackdaw.client :as jc]
            [clojure.pprint :as pp]))
 
(def producer-config
  {"bootstrap.servers" "localhost:9092"
   "key.serializer" "org.apache.kafka.common.serialization.StringSerializer"
   "value.serializer" "org.apache.kafka.common.serialization.StringSerializer"
   "acks" "all"
   "client.id" "foo"})
 
(defn -main
  [& args]
  (with-open [producer (jc/producer producer-config)]
    (doseq [i (range 1 1000)]
      (Thread/sleep 1000)
      (let [key (str i)
            value (str "Message #" i)]
        (println "Publishing message with key '" key "' and value '" value "'")
        (let [record-metadata (jc/produce! producer {:topic-name "test3"} key value)]
          (pp/pprint @record-metadata)))
      )))

Tento projekt naleznete na adrese https://github.com/tisnik/message-queues-examples/tree/master/kafka/clo­jure/produce-messages-4.

Poznámka: i když se jedná o klienta naprogramovaného v Clojure a používajícího specializovanou knihovnu, interně je využit standardní konektor k Apache Kafce.

12. Metriky vztažené ke konzumentům

Další důležité metriky, o nichž se v dnešním článku zmíníme, jsou metriky vztažené ke konzumentům. Opět platí, že tyto metriky jsou dostupné ve chvíli, kdy je klient naprogramován v Javě nebo v jiném jazyku, který produkuje bajtkód zpracovávaný virtuálním strojem Javy:

# Jméno metriky Označení MBeanu Stručný popis metriky
1 records-lag kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+),topic=([-.w]+),partition=([-.w]+) jedna z nejdůležitějších metrik vůbec – ukazuje, jak opožděn je konzument zpráv oproti nejnovější zprávě v tématu
2 records-lag-max kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+),topic=([-.w]+),partition=([-.w]+) největší hodnota získaná předchozí metrikou za dobu života konzumenta
3   kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+) dtto, ovšem neomezeno na klienta a téma
4 bytes-consumed-rate kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+),topic=([-.w]+) rychlost přenosu dat ke konzumentovi (měří tedy rychlost přenosu po síti)
5   kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+) dtto, opět bez omezení
6 records-consumed-rate kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+),topic=([-.w]+) opět měření rychlosti konzumace zpráv, ovšem měřeno v počtu zpráv (ty nemusí mít konstantní velikost)
7   kafka.consumer:type=consumer-fetch-manager-metrics,client-id=([-.w]+) dtto, opět bez omezení
8 fetch-rate kafka.consumer:type=consumer-fetch-manager-metrics,client_id=([-.w]+) rychlost konzumace zpráv, pokud se blíží k nule, může to znamenat problémy na straně konzumenta (například pomalá databáze atd.)

Obrázek 24: Sledování metrik konzumenta zpráv.

Obrázek 25: Graf zobrazený u vybrané metriky.

13. Vytvoření jednoduchého konzumenta zpráv naprogramovaného v JVM jazyce

Opět si ukážeme jednoduchého konzumenta zpráv, kterého lze spustit a sledovat jeho metriky s využitím MBeans a technologie JMX. Zdrojový kód tohoto demonstračního příkladu, který je naprogramován v Clojure, je dostupný na adrese https://github.com/tisnik/message-queues-examples/tree/master/kafka/clo­jure/consume-messages-3. Po vytvoření Clojure projektu se do projektového souboru project.clj přidala knihovna jackdaw:

(defproject consume-messages-2 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [fundingcircle/jackdaw "0.7.6"]]
  :main ^:skip-aot consume-messages-3.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Samotný konzument zpracovává zprávy v nekonečné smyčce, takže po zpracování celého tématu čeká na příchod nových zpráv:

(ns consume-messages-3.core
  (:require [jackdaw.client :as jc]
            [jackdaw.client.log :as jl]
            [clojure.pprint :as pp]))
 
(def consumer-config
  {"bootstrap.servers" "localhost:9092"
   "key.deserializer" "org.apache.kafka.common.serialization.StringDeserializer"
   "value.deserializer" "org.apache.kafka.common.serialization.StringDeserializer"
   "auto.offset.reset" "earliest"
   "group.id"  "group-A"})
 
(defn -main
  [& args]
  (with-open [consumer (-> (jc/consumer consumer-config)
                           (https://github.com/tisnik/message-queues-examples/tree/master/kafka/clojure/consume-messages-3))]
    (doseq [{:keys [key value partition timestamp offset]} (jl/log consumer 10)]
      (println "key: " key)
      (println "value: " value)
      (println "partition: " partition)
      (println "timestamp: " timestamp)
      (println "offset: " offset))))

Obrázek 26: Sledování vybraných metrik tohoto konzumenta zpráv.

14. Metriky virtuálního stroje Javy

V některých situacích může být důležité sledovat i vybrané metriky poskytované samotným virtuálním strojem Javy (JVM). Kritickým místem bývá správce paměti, který sice běží na pozadí (v samostatných vláknech), ovšem při „úklidu“ Old Generation budou ostatní vlákna brokera či ZooKeepera na určitý okamžik pozastaveny. Kafka používá správce paměti G1, takže se vyplatí sledovat tyto metriky:

# Jméno metriky Označení MBeanu Stručný popis metriky
1 CollectionCount java.lang:type=GarbageCollector,name=G1 Young Generation celkový počet úklidů Young generation (bývá velký, ovšem čas úklidu malý)
2 CollectionTime java.lang:type=GarbageCollector,name=G1 Young Generation celkový čas strávený úklidem Young generation
3 CollectionCount java.lang:type=GarbageCollector,name=G1 Old Generation celkový počet úklidů Old generation (bývá malý, ovšem čas úklidu delší)
4 CollectionTime java.lang:type=GarbageCollector,name=G1 Old Generation celkový čas strávený úklidem Old generation
Poznámka: základní informace o správě paměti v JVM jsme si vysvětlili v článcích Monitorování procesů a správa paměti v JDK 6 a JDK 7 (2) a Monitorování procesů a správa paměti v JDK 6 a JDK 7 (4).

Obrázek 27: Sledování metrik správce paměti Kafka brokeru.

Obrázek 28: Sledování metrik správce paměti Kafka brokeru.

15. Metriky ZooKeepera

Většina metrik ZooKeepera je dostupná přes takzvané „four-letter words“ zmíněné v navazující kapitole. Sledovat je vhodné zejména následující metriky:

# Jméno metriky Stručný popis metriky
1 outstanding_requests počet požadavků, které nebyly vyřízeny (mělo by se blížit k nule)
2 avg_latency čas (vyjádřený v milisekundách) na vyřízení požadavku ZooKeeperem
3 num_alive_connections počet klientů, kteří jsou v daný okamžik k ZooKeeperu přihlášeni
4 followers počet aktivních followerů (bez leadera)
5 pending_syncs ZooKeeper musí ukládat transakční logy na disk, zde je zobrazen počet čekajících zápisů (čím menší, tím lépe)
6 open_file_descriptor_count počet otevřených souborů se stavovými informacemi (musí být menší, než povolený počet deskriptorů nastavený v systému)

Obrázek 29: Metriky ZooKeepera.

16. Povolení „four-letter words“ ZooKeepera

V předchozí kapitole jsme se zmínili o tzv. „four-letter words“. Jedná se o příkazy (každý se skládá ze čtyř písmen – odtud jejich název), které je možné ZooKeeperu posílat přes telnet či nc a získávat tak další informace. Ovšem jednotlivé příkazy je nejprve zapotřebí povolit, a to buď specifikací jednotlivých příkazů, nebo lze využít žolíkového znaku *:

$ export KAFKA_OPTS="-Dzookeeper.4lw.commands.whitelist=*"
$ ./zookeeper.sh

Po tomto spuštění ZooKeepera se můžeme k jeho instanci připojit telnetem:

$ telnet localhost 2181
 
Trying ::1...
Connected to localhost.
Escape character is '^]'.

Dostupné jsou příkazy popsané zde. Zajímat nás bude příkaz mntr. Ten vypíše základní metriky ZooKeepera a ukončí připojení:

MIF temata

mntr
 
zk_version      3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT
zk_avg_latency  0
zk_max_latency  0
zk_min_latency  0
zk_packets_received     1
zk_packets_sent 0
zk_num_alive_connections        1
zk_outstanding_requests 0
zk_server_state standalone
zk_znode_count  5
zk_watch_count  0
zk_ephemerals_count     0
zk_approximate_data_size        44
zk_open_file_descriptor_count   125
zk_max_file_descriptor_count    4096
 
Connection closed by foreign host.

Obrázek 30: Další ukázka „dashboardu“ zobrazujícího vybrané metriky Apache Kafky.

17. Repositář s demonstračními příklady

Zdrojové soubory naprogramované v Javě a použité v předchozím i v dnešním článku byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/pre­sentations/. V případě, že nebudete chtít klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé soubory, které naleznete v následující tabulce:

# Příklad/soubor Stručný popis Cesta
1 example1/StatusMBean.java rozhraní s popisem MBeans https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example1/StatusMBean.ja­va
2 example1/Status.java implementace rozhraní StatusMBean https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example1/Status.java
3 example1/Main.java kód prvního demonstračního příkladu s inicializací JMX https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example1/Main.java
       
4 example2/StatusMBean.java rozhraní s popisem MBeans https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example2/StatusMBean.ja­va
5 example2/Status.java implementace rozhraní StatusMBean https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example2/Status.java
6 example2/Main.java kód druhého demonstračního příkladu s inicializací JMX https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example2/Main.java
       
7 example3/StatusMBean.java rozhraní s popisem MBeans https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example3/StatusMBean.ja­va
8 example3/Status.java implementace rozhraní StatusMBean https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example3/Status.java
9 example3/Main.java kód třetího demonstračního příkladu s inicializací JMX https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example3/Main.java
10 example3/config.yaml prázdný konfigurační soubor používaný knihovnou JMX exporter https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example3/
       
11 example4/StatusMBean.java rozhraní s popisem MBeans https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/StatusMBean.ja­va
12 example4/Status.java implementace rozhraní StatusMBean https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/Status.java
13 example4/Main.java kód čtvrtého demonstračního příkladu s inicializací JMX https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/Main.java
14 example4/config.yaml prázdný konfigurační soubor používaný knihovnou JMX exporter https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/
15 example4/jmxremote.password soubor s hesly uloženými v plaintextu https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/jmxremote.pas­sword
16 example4/jmxremote.access soubor s mapováním uživatele na role https://github.com/tisnik/pre­sentations/blob/master/mbe­ans/example4/jmxremote.ac­cess
Poznámka: výše uvedené zdrojové kódy jsou zpětně kompatibilní až minimálně do verze Java 7.

18. Odkazy na relevantní články na Rootu

  1. Použití nástroje Apache Kafka v aplikacích založených na mikroslužbách
    https://www.root.cz/clanky/pouziti-nastroje-apache-kafka-v-aplikacich-zalozenych-na-mikrosluzbach/
  2. Apache Kafka: distribuovaná streamovací platforma
    https://www.root.cz/clanky/apache-kafka-distribuovana-streamovaci-platforma/
  3. Pokročilý streaming založený na Apache Kafce, jazyku Clojure a knihovně Jackdaw
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-apache-kafce-jazyku-clojure-a-knihovne-jackdaw/
  4. Pokročilý streaming založený na Apache Kafce, jazyku Clojure a knihovně Jackdaw (2. část)
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-apache-kafce-jazyku-clojure-a-knihovne-jackdaw-2-cast/
  5. Pokročilý streaming založený na projektu Apache Kafka, jazyku Clojure a knihovně Jackdaw (streamy a kolony)
    https://www.root.cz/clanky/pokrocily-streaming-zalozeny-na-projektu-apache-kafka-jazyku-clojure-a-knihovne-jackdaw-streamy-a-kolony/
  6. Vývoj služeb postavených na systému Apache Kafka v jazyku Go
    https://www.root.cz/clanky/vyvoj-sluzeb-postavenych-na-systemu-apache-kafka-v-jazyku-go/
  7. Práce s Kafkou z příkazové řádky: nástroje Kafkacat a Kcli
    https://www.root.cz/clanky/prace-s-kafkou-z-prikazove-radky-nastroje-kafkacat-a-kcli/
  8. Apache ActiveMQ – další systém implementující message brokera
    https://www.root.cz/clanky/apache-activemq-dalsi-system-implementujici-message-brokera/#k12
  9. Sledování činnosti systému Apache Kafka přes JMX i metriky Promethea
    https://www.root.cz/clanky/sledovani-cinnosti-systemu-apache-kafka-pres-jmx-i-metriky-promethea/

19. Odkazy na Internetu

  1. The Java™ Tutorials – Introducing MBeans
    https://docs.oracle.com/ja­vase/tutorial/jmx/mbeans/in­dex.html
  2. Standard MBeans
    https://docs.oracle.com/ja­vase/tutorial/jmx/mbeans/stan­dard.html
  3. JMX Exporter
    https://github.com/promet­heus/jmx_exporter
  4. Monitor Apache Kafka with Prometheus and Grafana
    https://computingforgeeks.com/monitor-apache-kafka-with-prometheus-and-grafana/
  5. Kafka Monitoring Using Prometheus
    https://www.metricfire.com/blog/kafka-monitoring-using-prometheus/
  6. Collecting Kafka Performance Metrics with OpenTelemetry
    https://www.splunk.com/en_us/blog/de­vops/monitoring-kafka-performance-metrics-with-splunk-infrastructure-monitoring.html
  7. Monitoring Kafka performance metrics
    https://www.datadoghq.com/blog/mo­nitoring-kafka-performance-metrics/
  8. Kcli: is a kafka read only command line browser.
    https://github.com/cswank/kcli
  9. Kcli: a kafka command line browser
    https://go.libhunt.com/kcli-alternatives
  10. Awesome Go
    https://github.com/avelino/awesome-go
  11. Real-Time Payments with Clojure and Apache Kafka (podcast)
    https://www.evidentsystem­s.com/news/confluent-podcast-about-apache-kafka/
  12. Microservices: The Rise Of Kafka
    https://movio.co/blog/microservices-rise-kafka/
  13. Building a Microservices Ecosystem with Kafka Streams and KSQL
    https://www.confluent.io/blog/building-a-microservices-ecosystem-with-kafka-streams-and-ksql/
  14. An introduction to Apache Kafka and microservices communication
    https://medium.com/@ulymarins/an-introduction-to-apache-kafka-and-microservices-communication-bf0a0966d63
  15. kappa-architecture.com
    http://milinda.pathirage.org/kappa-architecture.com/
  16. Questioning the Lambda Architecture
    https://www.oreilly.com/i­deas/questioning-the-lambda-architecture
  17. Lambda architecture
    https://en.wikipedia.org/wi­ki/Lambda_architecture
  18. Kafka – ecosystem (Wiki)
    https://cwiki.apache.org/con­fluence/display/KAFKA/Eco­system
  19. The Kafka Ecosystem – Kafka Core, Kafka Streams, Kafka Connect, Kafka REST Proxy, and the Schema Registry
    http://cloudurable.com/blog/kafka-ecosystem/index.html
  20. A Kafka Operator for Kubernetes
    https://github.com/krallistic/kafka-operator
  21. Kafka Streams
    https://cwiki.apache.org/con­fluence/display/KAFKA/Kaf­ka+Streams
  22. Kafka Streams
    http://kafka.apache.org/do­cumentation/streams/
  23. Kafka Streams (FAQ)
    https://cwiki.apache.org/con­fluence/display/KAFKA/FAQ#FAQ-Streams
  24. Event stream processing
    https://en.wikipedia.org/wi­ki/Event_stream_processing
  25. Part 1: Apache Kafka for beginners – What is Apache Kafka?
    https://www.cloudkarafka.com/blog/2016–11–30-part1-kafka-for-beginners-what-is-apache-kafka.html
  26. What are some alternatives to Apache Kafka?
    https://www.quora.com/What-are-some-alternatives-to-Apache-Kafka
  27. What is the best alternative to Kafka?
    https://www.slant.co/opti­ons/961/alternatives/~kaf­ka-alternatives
  28. A super quick comparison between Kafka and Message Queues
    https://hackernoon.com/a-super-quick-comparison-between-kafka-and-message-queues-e69742d855a8?gi=e965191e72d0
  29. Kafka Queuing: Kafka as a Messaging System
    https://dzone.com/articles/kafka-queuing-kafka-as-a-messaging-system
  30. Apache Kafka Logs: A Comprehensive Guide
    https://hevodata.com/learn/apache-kafka-logs-a-comprehensive-guide/
  31. Microservices – Not a free lunch!
    http://highscalability.com/blog/2014/4/8/mi­croservices-not-a-free-lunch.html
  32. Microservices, Monoliths, and NoOps
    http://blog.arungupta.me/microservices-monoliths-noops/
  33. Microservice Design Patterns
    http://blog.arungupta.me/microservice-design-patterns/
  34. REST vs Messaging for Microservices – Which One is Best?
    https://solace.com/blog/experience-awesomeness-event-driven-microservices/
  35. Kappa Architecture Our Experience
    https://events.static.linux­found.org/sites/events/fi­les/slides/ASPgems%20-%20Kappa%20Architecture.pdf
  36. Apache Kafka Streams and Tables, the stream-table duality
    https://towardsdatascience.com/apache-kafka-streams-and-tables-the-stream-table-duality-ee904251a7e?gi=f22a29cd1854
  37. Alertmanager
    https://prometheus.io/doc­s/alerting/alertmanager/
  38. Grafana support for Prometheus
    https://prometheus.io/doc­s/visualization/grafana/
  39. Prometheus: from metrics to insight
    https://prometheus.io/
  40. Java Management Extensions (JMX)
    https://en.wikipedia.org/wi­ki/Java_Management_Extensi­ons
  41. JConsole
    https://en.wikipedia.org/wi­ki/JConsole
  42. Using JConsole
    https://docs.oracle.com/ja­vase/8/docs/technotes/gui­des/management/jconsole.html
  43. Overview of Java SE Monitoring and Management
    https://docs.oracle.com/en/ja­va/javase/15/management/o­verview-java-se-monitoring-and-management.html
  44. Using jconsole (JMX) with SSL encryption
    https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/configu­ration/secureJconsoleSSL.html
  45. Step by step instructions for securing JMX authentication for nodetool utility, OpsCenter and JConsole
    https://support.datastax.com/hc/en-us/articles/204226179-Step-by-step-instructions-for-securing-JMX-authentication-for-nodetool-utility-OpsCenter-and-JConsole
  46. Enabling remote JMX with password authentication only
    https://docs.oracle.com/ja­vadb/10.10.1.2/adminguide/rad­minjmxenablepwd.html
  47. Enabling remote JMX with password authentication and SSL
    https://docs.oracle.com/ja­vadb/10.10.1.2/adminguide/rad­minjmxenablepwdssl.html

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.