<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kafka Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/kafka/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:50:05 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>kafka Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Apache Kafka With Spring Boot and Kotlin</title>
		<link>https://blog.codersee.com/apache-kafka-with-spring-boot-and-kotlin/</link>
					<comments>https://blog.codersee.com/apache-kafka-with-spring-boot-and-kotlin/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 04 Oct 2022 05:30:21 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[kafka]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503766</guid>

					<description><![CDATA[<p>In this step-by-step guide, I will show you how to create a Kafka Producer and Consumer with Spring Boot and Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/apache-kafka-with-spring-boot-and-kotlin/">Apache Kafka With Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this step-by-step guide, I will show you how to use Apache Kafka with Spring Boot and Kotlin.</p>



<p>When you finish this article, you will know precisely:</p>



<ul class="wp-block-list">
<li>how to Produce and Consume <strong>String messages</strong>,</li>



<li>how to Publish and Consume <strong>JSON messages</strong>,</li>



<li>how to <strong>fix an infinite-loop problem</strong> with invalid JSON messages</li>
</ul>



<p>Please keep in mind, that you should have Apache Kafka <strong>up and running</strong> on your local machine. If you would like to learn how to set it up, then check out my other articles under the <a href="https://blog.codersee.com/tag/kafka/">Kafka tag</a>.</p>



<h2 class="wp-block-heading" id="h-2-create-new-project">2. Create New Project</h2>



<p>Let&#8217;s start with creating a new Spring Boot project. As always, I highly encourage you to use the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr page</a>, when creating a new one:</p>



<figure class="wp-block-image"><img fetchpriority="high" decoding="async" width="1686" height="903" src="http://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected.png" alt="The screenshot presents Spring Initializr page with selected Kafka and Spring Web dependencies and other settings." class="wp-image-5503770" srcset="https://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected.png 1686w, https://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected-300x161.png 300w, https://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected-1024x548.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected-768x411.png 768w, https://blog.codersee.com/wp-content/uploads/2022/10/spring_initializr_screenshot_with_kafka_and_spring_web_selected-1536x823.png 1536w" sizes="(max-width: 1686px) 100vw, 1686px" /></figure>



<p>To be on the same page, I&#8217;ve selected <strong>Spring Boot</strong> version <strong>2.7.4</strong> with <strong>Jar packaging</strong> and <strong>Java 17</strong>. Additionally, we will need two more dependencies:</p>



<ul class="wp-block-list">
<li><em>Spring for Apache Kafka</em>&#8211; necessary to work with our Kafka node,</li>



<li><em>Spring Web</em>&#8211; although this one is not necessary, I&#8217;ve added it so that we can expose a test REST API endpoint.</li>
</ul>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-3-publish-consume-string-messages">3. Publish/Consume String Messages</h2>



<p>As the first example, let&#8217;s take a look at how to publish and consume simple String messages.<br></p>



<h3 class="wp-block-heading" id="h-3-1-application-yaml-configuration">3.1. application.yaml Configuration</h3>



<p>After we import our Kafka Spring Boot Kotlin project into our favorite IDE (like IntelliJ, for instance), let&#8217;s navigate to the <strong>application.yaml</strong> file and put the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:8098
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    producer:
      bootstrap-servers: localhost:8098
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
</pre>



<p>As we can see, these settings are responsible for setting both Kafka consumer and producer:</p>



<ul class="wp-block-list">
<li>With<strong> bootstrap-servers</strong>, we point to the host and port, where we can reach out to our Kafka listener. Of course, depending on your node configuration this value may be different.</li>



<li>When it comes to the <strong>auto-offset-reset</strong>, it lets us specify consumer behavior when there is no initial offset in Kafka or if the current offset does not exist</li>



<li>With <strong>key-deserializer</strong> and <strong>value-deserializer</strong>, we specify the full qualifier to the deserializer we want to use. As the name suggests, we want to use the StringDeserializer for bot key and value deserialization.</li>



<li>Finally, the <strong>key-serializer</strong> and <strong>value-serializer</strong> let us do almost exactly the same, but for the serialization process.</li>
</ul>



<h3 class="wp-block-heading" id="h-3-2-create-constants-kt-file">3.2. Create Constants.kt File</h3>



<p>As the next step, let&#8217;s create <strong>Constants.kt</strong> file inside the <strong>config</strong> package:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">const val EXAMPLE_TOPIC_NAME = "someTopicOne"

const val GROUP_ID = "groupId"
</pre>



<p>As we can see, we put two String constants here with the example topic name and group ID.</p>



<p>Although this is not necessary, it will help us maintain our code a bit cleaner.</p>



<h3 class="wp-block-heading" id="h-3-3-implement-string-messages-producer">3.3. Implement String Messages Producer</h3>



<p>Nextly, let&#8217;s implement an <strong>ExampleStringProducer</strong> class inside the <strong>producer</strong> package:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Component
class ExampleStringProducer(
  private val kafkaTemplate: KafkaTemplate&lt;String, String>
) {

  fun sendStringMessage(message: String) {
    kafkaTemplate.send(EXAMPLE_TOPIC_NAME, message)
  }

}
</pre>



<p>As can be seen, in order to send messages we have to inject a <strong>KafkaTemplate</strong> class. This generic class exposes methods for executing high-level operations and as type parameters (&lt;String, String&gt; in our case), we have to specify the desired <strong>key</strong> and the <strong>value</strong> types.</p>



<p>When it comes to the <strong>sendStringMessage </strong>function, it simply takes a String argument called message and sends it to the topic- called <strong>someTopicOne</strong> in our case.</p>



<h3 class="wp-block-heading" id="h-3-4-create-messages-consumer">3.4 Create Messages Consumer</h3>



<p>With that being done, let&#8217;s implement a consumer called <strong>ExampleConsumer</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Component
class ExampleConsumer {

  private val logger = LoggerFactory.getLogger(this.javaClass)

  @KafkaListener(topics = [EXAMPLE_TOPIC_NAME], groupId = GROUP_ID)
  fun firstListener(message: String) {
    logger.info("Message received: [$message]")
  }

}
</pre>



<p>This time, in order to make our <code class="EnlighterJSRAW" data-enlighter-language="raw">firstListener</code> function a target of a Kafka message listener on the specified topics, we have to mark it with the <strong>@KafkaListener</strong> annotation. Moreover, if we would like to handle multiple topics with this function, we could simply specify their names in the <strong>topics array</strong>.</p>



<p>It&#8217;s worth mentioning that with the <code class="EnlighterJSRAW" data-enlighter-language="raw">groupId</code>, we override the group ID property for the consumer factory, <strong>but for this particular listener only</strong>.</p>



<p>And to summarize this code snippet- the incoming message will be simply logged into the output.</p>



<h3 class="wp-block-heading" id="h-3-5-expose-test-controller">3.5 Expose Test Controller</h3>



<p>Finally, let&#8217;s expose a test REST endpoint, which we will use to send messages:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
class ExampleController(
  private val exampleStringProducer: ExampleStringProducer
) {

  @PostMapping("/test")
  @ResponseStatus(HttpStatus.NO_CONTENT)
  fun sendTestMessage(
    @RequestBody requestBody: RequestBodyDto
  ) {
    exampleStringProducer.sendStringMessage(
      message = requestBody.message
    )
  }

  data class RequestBodyDto(val message: String)
}
</pre>



<p>As we can see, this handler will be responding to the <strong>POST</strong> requests to the <strong>/test</strong> endpoint.</p>



<p>As a request body, we will send the message, which will be then passed to the <strong>sendStringMessage</strong> function and if everything works fine, we should get a <strong>204 No Content</strong> response.</p>



<h3 class="wp-block-heading" id="h-3-6-test-the-endpoint">3.6 Test The Endpoint</h3>



<p>In order to test our functionality, let&#8217;s run a couple of <strong>POST</strong> requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "message": "SomeMessage"
}'
</pre>



<p>As a result, we should see our messages printed to the output, like these:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Message received: [Some Example Message]
Message received: [Another one]
</pre>



<div>&nbsp;</div>



<div>
<h2 class="article-heading-introduction">4. Kafka Spring Boot Kotlin JSON Messages</h2>
<p>With all of that being done, we know precisely how to send and retrieve String messages.</p>
<p>So as the next thing, let&#8217;s learn how to publish and consume JSON Kafka messages in our Spring Boot Kotlin project.</p>
<h3 class="article-heading-introduction">4.1. Create DTOs</h3>
<p>This time, let&#8217;s start a bit differently (and you will see why later) , by creating DTOs- <strong>ExampleDto</strong> and <strong>UserDto</strong> inside the <strong>dto</strong> package:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">// ExampleDto.kt file:
data class ExampleDto(val someMessage: String)

// UserDto.kt file:
data class UserDto(val id: Long, val name: String)
</pre>
<p>We will use them later to transport our messages.</p>
<h3 class="article-heading-introduction">4.2. Add More Constants</h3>
<p>As the next step, let&#8217;s introduce two additional topics names to the <strong>Constants.kt</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">const val EXAMPLE_TOPIC_NAME_TWO = "someTopicTwo"
const val EXAMPLE_TOPIC_NAME_THREE = "someTopicThree"
</pre>
<h3 class="article-heading-introduction">4.3. Implement JSON Messages Producer</h3>
<p>Nextly, let&#8217;s create an <strong>ExampleJsonProducer</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class ExampleJsonProducer(
  private val exampleDtoKafkaTemplate: KafkaTemplate&lt;String, ExampleDto&gt;,
  private val userDtoKafkaTemplate: KafkaTemplate&lt;String, UserDto&gt;
) {

  fun sendExampleDtoMessage(dto: ExampleDto) {
    exampleDtoKafkaTemplate.send(EXAMPLE_TOPIC_NAME_TWO, dto)
  }

  fun sendUserDtoMessage(dto: UserDto) {
    userDtoKafkaTemplate.send(EXAMPLE_TOPIC_NAME_THREE, dto)
  }
}
</pre>
<p>This time, we inject <strong>two different KafkaTemplates</strong>&#8211; each one is responsible for handling specific DTO objects. Apart from that, everything looks almost the same as in paragraph 3- with different constants used.</p>
</div>



<div>
<h3 class="article-heading-introduction">4.4. Create JSON Messages Consumer</h3>
<p>Following, let&#8217;s add two more listeners to the <strong>ExampleConsumer</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@KafkaListener(topics = [EXAMPLE_TOPIC_NAME_TWO], groupId = GROUP_ID)
fun secondListener(message: ExampleDto) {
  logger.info("Message received: [$message]")
}

@KafkaListener(topics = [EXAMPLE_TOPIC_NAME_THREE], groupId = GROUP_ID)
fun secondListener(message: UserDto) {
  logger.info("Message received: [$message]")
}
</pre>
<p>As we can clearly see, nothing changed except the topics&#8217; names.</p>
<h3 class="article-heading-introduction">4.5. Edit ExampleController</h3>
<p>One of the last things we have to do are two more updates inside the <strong>ExampleController</strong>.</p>
<p>As the first one, we have to inject our new producer:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@RestController
class ExampleController(
  private val exampleStringProducer: ExampleStringProducer,
  private val exampleJsonProducer: ExampleJsonProducer
)
</pre>
<p>And add two more invocations inside the <strong>sendTestMessage</strong> function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">exampleJsonProducer.sendExampleDtoMessage(
  dto = ExampleDto(requestBody.message)
)
exampleJsonProducer.sendUserDtoMessage(
  dto = UserDto(
    id = Random.nextLong(0, 100),
    name = requestBody.message
  )
)
</pre>
<p>As we can see, when we query our endpoint with some payload, we expect that the provided message will be sent to our new topics. Moreover, in the case of UserDto, the id will be a randomly generated Long value.</p>
<h3 class="article-heading-introduction">4.6. application.yaml File</h3>
<p>As the last thing before testing, let&#8217;s edit the<strong> application.yaml</strong> file a bit:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:8098
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
    producer:
      bootstrap-servers: localhost:8098
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
</pre>
<p>The only values we want to change here are<strong> value-deserializer</strong> for our consumer and <strong>value-serializer</strong> for our producer. In order to work with JSON payloads, we have to utilize the <strong>JsonDeserializer</strong> and <strong>JsonSerializer</strong> here.</p>
<h3 class="article-heading-introduction">4.7. Fix an infinite-loop problem</h3>
<p>The title is a bit of a spoiler here (and the main reason we didn&#8217;t start paragraph 4 with a YAML file), but let&#8217;s run the application and query our endpoint:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request POST 'localhost:8080/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "message": "SomeMessage"
}'
</pre>
<p>After that, when we check the logs, we will see an infinite loop with a thrown exception.</p>
<p>If we stop the application (or you are a robot :D), we will see the following message (among others):</p>
<blockquote><p>This error handler cannot process &#8216;SerializationException&#8217;s directly; please consider configuring an &#8216;ErrorHandlingDeserializer&#8217; in the value and/or key deserializer</p></blockquote>
<p>Well, this message is pretty descriptive so when we apply this hint, the <strong>application.yaml</strong> file looks like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:8098
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      properties:
        spring:
          deserializer:
            value:
              delegate:
                class: org.springframework.kafka.support.serializer.JsonDeserializer
    producer:
      bootstrap-servers: localhost:8098
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
</pre>
<p>Basically, as a fix, we have to utilize the ErrorHandlingDeserializer class, which lets Spring resolve the deserialization issue the consumer&#8217;s poll() method resolves.</p>
<p>To do so with a YAML config, we have to:</p>
<ul>
<li>set <em>org.springframework.kafka.support.serializer.ErrorHandlingDeserializer</em> as a <strong>value-deserializer</strong>,</li>
<li>and add <em>properties.spring.deserializer.value.delegate</em> property delegating to <strong>JsonDeserializer</strong></li>
</ul>
<h3 class="article-heading-introduction">4.8. Fix Trusted Packages Issue</h3>
<p>And again, spoiler alert 😀</p>
<p>Let&#8217;s rerun the POST request and see what happens:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request POST 'localhost:8080/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "message": "SomeMessage"
}'
</pre>
<p>As we can clearly see, we got rid of the infinite loop, but still, we can&#8217;t read messages.</p>
<p>And this time message is pretty descriptive, as well:</p>
</div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>The class &#8216;com.codersee.kafkaexample.dto.ExampleDto&#8217; is not in the trusted packages: [java.util, java.lang]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).</p>
</blockquote>



<p>Similarly, to fix this issue we have to add new property- <strong><em>properties.spring.json.trusted.packages</em></strong>&#8211; and either point to the package, where our DTOs live or set a wildcard.</p>



<p>In my case, the final <strong>application.yaml file</strong> looks, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:8098
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      properties:
        spring:
          json:
            trusted:
              packages: "com.codersee.kafkaexample.dto"
          deserializer:
            value:
              delegate:
                class: org.springframework.kafka.support.serializer.JsonDeserializer
    producer:
      bootstrap-servers: localhost:8098
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
</pre>



<h3 class="wp-block-heading" id="h-4-9-final-testing">4.9. Final Testing</h3>



<p>Finally, we can rerun our beloved Spring Boot Kafka with Kotlin application and try the request once again:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "message": "SomeMessage"
}'
</pre>



<p>As a result, we should see the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Message received: [SomeMessage]
Message received: [ExampleDto(someMessage=SomeMessage)]
Message received: [UserDto(id=26, name=SomeMessage)]
</pre>



<p>As we can see, all 3 Apache Kafka producers and consumers are working, as expected (and if no, then let me know in the comment section).</p>



<h2 class="wp-block-heading" id="h-5-apache-kafka-with-spring-boot-and-kotlin-summary">5. Apache Kafka With Spring Boot and Kotlin Summary</h2>



<p>And that would be all for this article about how to work with <strong>Apache Kafka With Spring Boot and Kotlin</strong>. If you&#8217;d like to see the whole project source code, then visit <a href="https://github.com/codersee-blog/spring-boot-kotlin-apache-kafka" target="_blank" rel="noopener">this GitHub repository</a>.</p>



<p>I really hope that you enjoyed this step-by-step guide and will be delighted if you would like to share your feedback with me and the others <strong>in the comments section below.</strong></p>



<p>Take care and have a great day! 😀</p>
<p>The post <a href="https://blog.codersee.com/apache-kafka-with-spring-boot-and-kotlin/">Apache Kafka With Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/apache-kafka-with-spring-boot-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>How To Set Up Kafka Without Zookeeper using Docker Compose?</title>
		<link>https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/</link>
					<comments>https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 29 Sep 2022 06:00:51 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[kafka]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503746</guid>

					<description><![CDATA[<p>In this article, I would like to show you how to set up Apache Kafka Without Zookeeper using Docker Compose.</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/">How To Set Up Kafka Without Zookeeper using Docker Compose?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this last of the 3 articles about setting up Apache Kafka, I would like to show you how to set up Apache Kafka <strong>Without Zookeeper</strong> using Docker Compose.</p>



<p>After reading it, you will know precisely:</p>



<ul class="wp-block-list">
<li>what does the Docker Compose configuration look like to set up <strong>3 Kafka Brokers</strong>,</li>



<li>what <strong>environment variables</strong> we have to set,</li>



<li>and what <strong>workaround</strong> we have to do in order to make it run.</li>
</ul>



<p>If after finishing it, you will still be eager to broaden your knowledge, then you can find the previous two articles right here:</p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/">How To Deploy Multiple Kafka Brokers With Docker Compose?</a></li>



<li><a href="https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/" target="_blank" rel="noopener">How To Set Up Apache Kafka With Docker?</a></li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>In my articles, I focus on the practice and getting things done. <br><br>However, if you would like to get a strong understading of DevOps concepts, which are more and more in demand nowadays, then I highly recommend to check out KodeKloud courses, like this <a href="https://shrsl.com/46wvo" rel="sponsored nofollow">Docker</a> and <a href="https://shareasale.com/r.cfm?b=2183704&amp;u=3507867&amp;m=132199&amp;urllink=&amp;afftrack=" rel="sponsored nofollow">Kubernetes</a> learning paths. </cite></blockquote>



<h2 class="wp-block-heading" id="h-2-apache-kafka-without-zookeeper">2. Apache Kafka Without Zookeeper</h2>



<p>Before we dive into the practice part, let&#8217;s take a second to understand the motivation behind the Zookeeper removal (I&#8217;ve already written about Zookeeper responsibilities in the first part, so I&#8217;ll skip it here).</p>



<p>To put it simply, the motivation behind it can be summarized with these points:</p>



<ul class="wp-block-list">
<li>simplifying the deployment and configuration process (Zookeeper is a separate system, with its own syntax, configs, and deployment patterns),</li>



<li>scalability improvement,</li>



<li>enabling support for more partitions.</li>
</ul>



<p>The whole change has been introduced for the first time as a <a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-500%3A+Replace+ZooKeeper+with+a+Self-Managed+Metadata+Quorum" target="_blank" rel="noopener">KIP-500</a> (Kafka Improvement Proposal) and has been offered as an early access version with the <strong>2.8.0 release</strong>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>If you would like to understand deeply the reasons and arguments behind the Zookeeper removal, I highly encourage you to visit the KIP-500 ticket page linked above.</p>
</blockquote>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-3-kafka-without-zookeeper-docker-compose-file">3. Kafka Without Zookeeper Docker Compose File</h2>



<p>With that being said, let&#8217;s take a look at the <strong>docker-compose.yml</strong> file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: '3'
services:
  kafka1:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka1
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_LISTENERS: PLAINTEXT://kafka1:9092,CONTROLLER://kafka1:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka1:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
      KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka1:9093,2@kafka2:9093,3@kafka3:9093'
      KAFKA_PROCESS_ROLES: 'broker,controller'
    volumes:
      - ./run_workaround.sh:/tmp/run_workaround.sh
    command: "bash -c '/tmp/run_workaround.sh &amp;&amp; /etc/confluent/docker/run'"
  kafka2:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka2
    environment:
      KAFKA_NODE_ID: 2
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_LISTENERS: PLAINTEXT://kafka2:9092,CONTROLLER://kafka2:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka2:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
      KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka1:9093,2@kafka2:9093,3@kafka3:9093'
      KAFKA_PROCESS_ROLES: 'broker,controller'
    volumes:
      - ./run_workaround.sh:/tmp/run_workaround.sh
    command: "bash -c '/tmp/run_workaround.sh &amp;&amp; /etc/confluent/docker/run'"
  kafka3:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka3
    environment:
      KAFKA_NODE_ID: 3
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_LISTENERS: PLAINTEXT://kafka3:9092,CONTROLLER://kafka3:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka3:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
      KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka1:9093,2@kafka2:9093,3@kafka3:9093'
      KAFKA_PROCESS_ROLES: 'broker,controller'
    volumes:
      - ./run_workaround.sh:/tmp/run_workaround.sh
    command: "bash -c '/tmp/run_workaround.sh &amp;&amp; /etc/confluent/docker/run'"
</pre>



<p>Don&#8217;t worry if you feel overwhelmed at this point, I will walk you step by step through the configuration values in the following paragraphs.</p>



<p>Nevertheless, don&#8217;t run the <code class="EnlighterJSRAW" data-enlighter-language="raw">docker compose up</code> command yet, we will need one more file to run it 🙂</p>



<h2 class="wp-block-heading" id="h-4-add-necessary-workarounds">4. Add Necessary Workarounds</h2>



<p>As the next step, we need to run a few commands when containers start.</p>



<p>In my example, I will call this file <strong>run_workaround.sh</strong>. And although the name is not important, it has to match values specified when mounting volumes and running the command from within the containers:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/bin/sh

sed -i '/KAFKA_ZOOKEEPER_CONNECT/d' /etc/confluent/docker/configure

sed -i 's/cub zk-ready/echo ignore zk-ready/' /etc/confluent/docker/ensure

echo "kafka-storage format --ignore-formatted -t NqnEdODVKkiLTfJvqd1uqQ== -c /etc/kafka/kafka.properties" >> /etc/confluent/docker/ensure
</pre>



<p>As we can see, the above script contains exactly 3 commands.</p>



<p>The first one is responsible for turning off the check for the <code class="EnlighterJSRAW" data-enlighter-language="raw">KAFKA_ZOOKEEPER_CONNECT</code> parameter. Without this line, the error will be thrown: C<em>ommand [/usr/local/bin/dub ensure KAFKA_ZOOKEEPER_CONNECT] FAILED !</em></p>



<p>The second one ignores the cub zk-ready. Without it, everything will end up with another exception: <em>zk-ready: error: too few arguments</em>.</p>



<p>And finally, the last line is a KRaft required step, which formats the storage directory with a new cluster identifier. Please keep in mind that the specified UUID value (<code class="EnlighterJSRAW" data-enlighter-language="raw">NqnEdODVKkiLTfJvqd1uqQ==</code> in my case) has to be <strong>16 bytes</strong> of a <strong>base64-encoded UUID</strong>.</p>



<h2 class="wp-block-heading" id="h-5-docker-compose-instructions">5. Docker Compose Instructions</h2>



<p>With all of the above being said, we can get back to the <strong>docker-compose.yml</strong> file.</p>



<h3 class="wp-block-heading" id="h-5-1-volumes-and-commands">5.1. Volumes and Commands</h3>



<p>Although I will skip the things like the <em>services</em>, <em>images</em>, or <em>container</em> names (I&#8217;ve covered them in the previous articles), let&#8217;s take a look at these 3 lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">volumes:
  - ./run_workaround.sh:/tmp/run_workaround.sh
command: "bash -c '/tmp/run_workaround.sh &amp;&amp; /etc/confluent/docker/run'"
</pre>



<p><em>Volumes</em> are the preferred mechanism for persisting data <strong>generated by and used</strong> by Docker containers. With the above setting, we inform Docker that we want the <strong>run_workaround.sh </strong>file to be mounted inside the <strong>tmp</strong> directory inside the container with the same name. Of course, we could change the name of this file inside, but then, the change should also be reflected in the command.</p>



<p>When it comes to the <em>command</em>, it simply <strong>overrides the default command</strong>. With the above config, we instruct Docker to run our container with our custom script.</p>



<h2 class="wp-block-heading" id="h-6-apache-kafka-brokers-environment-variables">6. Apache Kafka Brokers Environment Variables</h2>



<p>Finally, let&#8217;s go through all the environments we had to set in order to make everything work.</p>



<h3 class="wp-block-heading" id="h-6-1-kafka-node-id-environment-variable">6.1. KAFKA_NODE_ID Environment Variable</h3>



<p>As the name suggest, the <code class="EnlighterJSRAW" data-enlighter-language="raw">KAFKA_NODE_ID</code> variable is used to specify a unique identifier of our node in the cluster.</p>



<h3 class="wp-block-heading" id="h-6-2-kafka-listeners-and-protocol-mapping">6.2. Kafka Listeners and Protocol Mapping</h3>



<p>Nextly, let&#8217;s see the settings responsible for appropriate hosts and ports mapping:</p>



<ul class="wp-block-list">
<li><strong>KAFKA_LISTENER_SECURITY_PROTOCOL_MAP</strong> &#8211; with this one, we simply define the key-value pairs of listeners&#8217; names with security protocols used with them</li>



<li><strong>KAFKA_LISTENERS</strong> &#8211; with this variable, we define all exposed listeners</li>



<li><strong>KAFKA_ADVERTISED_LISTENERS</strong> &#8211; this one, on the other hand, contains all listeners used by clients</li>
</ul>



<p>It&#8217;s worth mentioning here that when working with Docker Compose, the container name becomes a hostname- like <em>kafka1</em>, <em>kafka2</em>, and <em>kafka3</em> in our examples.</p>



<h3 class="wp-block-heading" id="h-6-3-kafka-advertised-listeners-setting">6.3. KAFKA_ADVERTISED_LISTENERS Setting</h3>



<p>As the next step, let&#8217;s see the <code class="EnlighterJSRAW" data-enlighter-language="raw">KAFKA_ADVERTISED_LISTENER</code>. This one, simply allows us to specify a list of coma-separated listeners&#8217; names used by the controller. It&#8217;s necessary when running in a KRaft mode.</p>



<p>Additionally, none of the values specified here can be put inside the advertised listeners covered in the previous paragraph. Otherwise, the following error will be thrown:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>The advertised.listeners config must not contain KRaft controller listeners from controller.listener.names when process.roles contains the broker role because Kafka clients that send requests via advertised listeners do not send requests to KRaft controllers &#8212; they only send requests to KRaft brokers.</p>
</blockquote>



<h3 class="wp-block-heading" id="h-6-4-kafka-controller-quorum-voters">6.4. KAFKA_CONTROLLER_QUORUM_VOTERS</h3>



<p>As the next one comes the <code class="EnlighterJSRAW" data-enlighter-language="raw">KAFKA_CONTROLLER_QUORUM_VOTERS</code> environment variable. And this one allows us to specify the set of voters in our node. When it comes to the format, values have to be put in the following order: <code class="EnlighterJSRAW" data-enlighter-language="raw">{id}@{host}:{port}</code> .</p>



<p>So, in our case, we do specify <code class="EnlighterJSRAW" data-enlighter-language="raw">9093</code> of our nodes, because this is the port exposed as a controller.</p>



<h3 class="wp-block-heading" id="h-6-5-kafka-process-roles-variable">6.5. KAFKA_PROCESS_ROLES Variable</h3>



<p>And the last one- the <code class="EnlighterJSRAW" data-enlighter-language="raw">KAFKA_PROCESS_ROLES</code> variable lets us specify the role of the particular broker. The value can be set to either <strong>broker</strong>, <strong>controller</strong>, or <strong>both of them</strong> (just like above).</p>



<h2 class="wp-block-heading" id="h-7-testing">7. Testing</h2>



<p>And finally, we can proceed to the part where we will verify if everything is working, as expected. In my example, I will show you how to use Console Producer/Consumer, but feel free to connect with anything you like.</p>



<p>Firstly, let&#8217;s start our services with the <code class="EnlighterJSRAW" data-enlighter-language="raw">docker compose up</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">The broker has been unfenced. Transitioning from RECOVERY to RUNNING.</pre>



<p>After some time, we should see the above for each of our containers.</p>



<p>Additionally, we can validate with a <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</code> command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CONTAINER ID IMAGE                       COMMAND                CREATED            STATUS            PORTS    NAMES
dc3966ba58a4 confluentinc/cp-kafka:7.2.1 "bash -c '/tmp/run_w…" About a minute ago Up About a minute 9092/tcp kafka3
10242d9d1e6a confluentinc/cp-kafka:7.2.1 "bash -c '/tmp/run_w…" About a minute ago Up About a minute 9092/tcp kafka2
c404c866cde6 confluentinc/cp-kafka:7.2.1 "bash -c '/tmp/run_w…" About a minute ago Up About a minute 9092/tcp kafka1</pre>



<p>As we can clearly see, every container is up and running. Moreover, the desired <code class="EnlighterJSRAW" data-enlighter-language="raw">9092</code> port is exposed for each of them.</p>



<p>When it comes to the testing strategy, I would like to connect to each broker separately and perform a different action:</p>



<ul class="wp-block-list">
<li>firstly, connect to the <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka1</code> and create a new topic called <code class="EnlighterJSRAW" data-enlighter-language="raw">exampleTopic</code></li>



<li>secondly, to <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka2</code> and produce some messages</li>



<li>and finally, read all of the messages from within the <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka3</code></li>
</ul>



<p>To do so, let&#8217;s start with the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it kafka1 /bin/bash
kafka-topics --bootstrap-server kafka1:9092 --create --topic exampleTopic

# Output:
Created topic exampleTopic.

# To exit the container, please specify exit</pre>



<p>As we can see, the <code class="EnlighterJSRAW" data-enlighter-language="raw">exampleTopic</code> was created successfully.</p>



<p>So as the next thing, let&#8217;s produce some messages from <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka2</code> broker:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it kafka2 /bin/bash
kafka-console-producer --bootstrap-server kafka2:9092 --topic exampleTopic

>codersee
>rules 

# To exit the producer, please hit Ctrl + D. Then to exit the container please type 'exit'.</pre>



<p>No errors have been thrown, which let us assume that everything is good so far.</p>



<p>Nevertheless, to fully make sure, let&#8217;s consume these messages:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it kafka3 /bin/bash
kafka-console-consumer --bootstrap-server kafka3:9092 --topic exampleTopic --from-beginning

# Output:
codersee
rules

# To exit the consumer, please hit Ctrl + C. As the output, we should see:
^CProcessed a total of 2 messages

# And again, to exit the container please type 'exit'.</pre>



<p>As we can see, everything worked as expected and our brokers are correctly communicating within the cluster.</p>



<h2 class="wp-block-heading" id="h-8-kafka-without-zookeeper-using-docker-compose-summary">8. Kafka Without Zookeeper Using Docker Compose Summary</h2>



<p>And that&#8217;s all for today&#8217;s article on how to<strong> set up Kafka Without Zookeeper using Docker Compose</strong>.</p>



<p>I really hope that you enjoyed this and my previous articles in an Apache Kafka series and I will be forever thankful if you would like to share your feedback with me in the comments section below (positive and negative, as well).</p>



<p>Take care and have a great week! 🙂</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/">How To Set Up Kafka Without Zookeeper using Docker Compose?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>How To Deploy Multiple Kafka Brokers With Docker Compose?</title>
		<link>https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/</link>
					<comments>https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 26 Sep 2022 06:00:31 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[kafka]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503730</guid>

					<description><![CDATA[<p>In this step-by-step article, I will teach you how to set up multiple Kafka Brokers using Docker Compose, aka multi-node cluster.</p>
<p>The post <a href="https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/">How To Deploy Multiple Kafka Brokers With Docker Compose?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>Hello, in this step-by-step guide, I will show you how to set up multiple Kafka brokers with Docker Compose 🙂</p>



<p>In this article, together we will:</p>



<ul class="wp-block-list">
<li>see the minimum Docker Compose config required to spin up <strong>3 Kafka brokers</strong>,</li>



<li>take a closer look at the <strong>configuration</strong>,</li>



<li>get information about active brokers with the <strong>zookeeper-shell</strong> command,</li>



<li>verify whether everything is working as expected with <strong>Console Producer and Consumer tools</strong>.</li>
</ul>



<p>Unlike the previous article about Kafka, I would like to start this one by showing the configuration you&#8217;ll need to set up 3 Kafka brokers. After that, we will go through it as thoroughly as possible to understand it even better and in the end, we will learn how to verify and test our config.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>In my articles, I focus on the practice and getting things done. <br><br>However, if you would like to get a strong understading of DevOps concepts, which are more and more in demand nowadays, then I highly recommend to check out KodeKloud courses, like this <a href="https://shrsl.com/46wvo" rel="sponsored nofollow">Docker</a> and <a href="https://shareasale.com/r.cfm?b=2183704&amp;u=3507867&amp;m=132199&amp;urllink=&amp;afftrack=" rel="sponsored nofollow">Kubernetes</a> learning paths. </cite></blockquote>



<h2 class="wp-block-heading" id="h-2-multiple-kafka-brokers-docker-compose">2. Multiple Kafka Brokers Docker Compose</h2>



<p>With that being said, let&#8217;s see the <strong>docker-compose.yaml</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.2.1
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
  kafka1:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka1
    ports:
      - "8097:8097"
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: EXTERNAL://localhost:8097,INTERNAL://kafka1:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
  kafka2:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka2
    ports:
      - "8098:8098"
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 2
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: EXTERNAL://localhost:8098,INTERNAL://kafka2:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
  kafka3:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka3
    ports:
      - "8099:8099"
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 3
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: EXTERNAL://localhost:8099,INTERNAL://kafka3:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
</pre>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h3 class="wp-block-heading" id="h-2-1-docker-images-versions-and-containers-names">2.1. Docker Images Versions and Containers Names</h3>



<p>Firstly, let&#8217;s take a look at docker images versions and containers names:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.2.1
    container_name: zookeeper
...
  kafka1:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka1
...
  kafka2:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka2
...
  kafka3:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka3
</pre>



<p>As we can see, in our example we will use <strong>Confluent Community Docker Image for Apache Kafka</strong> and <strong>Confluent Docker Image for Zookeeper</strong>&#8211; both in version <strong>7.2.1</strong>.</p>



<p>Additionally, we explicitly set names for our containers, which are discoverable at a hostname identical to the container name. Simply put, container exposed port <code class="EnlighterJSRAW" data-enlighter-language="raw">9092</code> of&nbsp;<code class="EnlighterJSRAW" data-enlighter-language="raw">kafka1</code> will be visible for other containers at <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka1:9092</code>.</p>



<h3 class="wp-block-heading" id="h-2-2-containers-ports">2.2. Containers Ports</h3>



<p>Nextly, let&#8217;s have a look at the exposed ports:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...
  kafka1:
    ports:
      - "8097:8097"
...
  kafka2:
    ports:
      - "8098:8098"
...
  kafka3:
    ports:
      - "8099:8099"
</pre>



<p>As can be seen, we did it for each service except the <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper</code>.</p>



<p>You might already know this, but <code class="EnlighterJSRAW" data-enlighter-language="raw">ports</code> is responsible for exposing container ports to the host machine. Simply put, when running docker-compose on our computer with a <code class="EnlighterJSRAW" data-enlighter-language="raw">123:456</code> setting, we will be able to access a container&#8217;s <code class="EnlighterJSRAW" data-enlighter-language="raw">456</code> port through <code class="EnlighterJSRAW" data-enlighter-language="raw">localhost:123</code>.</p>



<h3 class="wp-block-heading" id="h-2-3-docker-compose-depends-on">2.3. Docker Compose Depends On</h3>



<p>The last thing, I would like to cover before we head to the environment settings of our Kafka brokers is the <code class="EnlighterJSRAW" data-enlighter-language="raw">depends_on</code> .</p>



<p>As we can see, we put it for each Kafka service specifying the <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper</code> service as an argument. With this one, we simply make sure that when running a <code class="EnlighterJSRAW" data-enlighter-language="raw">docker compose up</code> command, the <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper</code> service will be started before the brokers.</p>



<h2 class="wp-block-heading" id="h-3-multiple-kafka-brokers-environment-variables">3. Multiple Kafka Brokers Environment Variables</h2>



<p>If you&#8217;ve seen my previous article on <a href="https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/" target="_blank" rel="noopener">how to set up a single-node cluster</a> with docker-compose, then you will notice that it looks a bit different. The main reason behind this is that with multiple Kafka brokers in our node, they have to know how to communicate with each other.</p>



<h3 class="wp-block-heading" id="h-3-1-zookeeper-client-port">3.1. Zookeeper Client Port</h3>



<p>As the first step, let&#8217;s take a look at the <strong>ZOOKEEPER_CLIENT_PORT</strong>.</p>



<p>To put it simply, this one instructs Zookeeper where to listen for connections by clients- Kafka brokers in our example.</p>



<h3 class="wp-block-heading" id="h-3-2-kafka-broker-id">3.2. Kafka Broker ID</h3>



<p>Following, check the <strong>KAFKA_BROKER_ID</strong> variable.</p>



<p>By default, when we don&#8217;t set this one, the Zookeeper will automatically assign a unique broker identifier. Nevertheless, if we would like to specify it explicitly, then we can take care of it with this environment variable. Of course, please keep in mind that this value <strong>has to be unique</strong> across our Kafka brokers.</p>



<h3 class="wp-block-heading" id="h-3-3-kafka-zookeeper-connect">3.3. Kafka Zookeeper Connect</h3>



<p>As the next thing, we set the <strong>KAFKA_ZOOKEEPER_CONNECT</strong> in each service to point to the Zookeeper.</p>



<p>As the value, we have to set our Zookeeper container name with a port set with the ZOOKEEPER_CLIENT_PORT directive.</p>



<h3 class="wp-block-heading" id="h-3-4-kafka-listener-settings">3.4. Kafka Listener Settings</h3>



<p>Finally, let&#8217;s see the rest of the environment variables responsible for communication:</p>



<ul class="wp-block-list">
<li>KAFKA_LISTENER_SECURITY_PROTOCOL_MAP</li>



<li>KAFKA_ADVERTISED_LISTENERS</li>



<li>KAFKA_INTER_BROKER_LISTENER_NAME</li>
</ul>



<p>Unlike the previous ones, I believe it has more sense to take a look at all of them at once to get a better understanding.</p>



<p>With a <strong>KAFKA_ADVERTISED_LISTENERS</strong>, we set a comma-separated list of listeners with their host/IP and port. This value <strong>must be set</strong> in a multi-node environment because otherwise, clients will attempt to connect to the internal host address. Please keep in mind that the listeners&#8217; values (EXTERNAL and INTERNAL in our case), <strong>must be unique here</strong>.</p>



<p>When it comes to the naming here, we can specify our own values, just as I did with EXTERNAL/INTERNAL. Nevertheless, in such a case, we have to specify the correct mapping with <strong>KAFKA_LISTENER_SECURITY_PROTOCOL_MAP</strong>:</p>



<ul class="wp-block-list">
<li>EXTERNAL will be mapped to PLAINTEXT protocol,</li>



<li>INTERNAL will be mapped the same way, as well</li>
</ul>



<p>If you are wondering why have we done it, then the reason is mentioned above- listeners&#8217; values <strong>have to be unique</strong>, so we can&#8217;t specify <code class="EnlighterJSRAW" data-enlighter-language="raw">PLAINTEXT://localhost:8099,PLAINTEXT://kafka3:9092</code> .</p>



<p>Finally, with <strong>KAFKA_INTER_BROKER_LISTENER_NAME</strong> we explicitly specify which listener to use for inter-broker communication.</p>



<h2 class="wp-block-heading" id="h-4-multiple-kafka-with-docker-compose-testing">4. Multiple Kafka With Docker Compose Testing</h2>



<p>With all of that being said, we can finally make use of our docker-compose.yaml file.</p>



<p>To do so, let&#8217;s build and run our services with the following command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker compose up</pre>



<p>Depending on our machine, it may take a while until everything is up and running.</p>



<p>In the end, we should see the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...Recorded new controller, from now on will use broker...</pre>



<h3 class="wp-block-heading" id="h-4-1-verify-all-containers-running">4.1. Verify All Containers Running</h3>



<p>As the next step, let&#8217;s open up a new terminal window and list up and running containers with <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Output:
CONTAINER ID IMAGE                           COMMAND                CREATED        STATUS        PORTS                            NAMES
cf892aeb5793 confluentinc/cp-kafka:7.2.1     "/etc/confluent/dock…" 54 minutes ago Up 54 minutes 0.0.0.0:8098->8098/tcp, 9092/tcp kafka2
a10bef7b6a54 confluentinc/cp-kafka:7.2.1     "/etc/confluent/dock…" 54 minutes ago Up 54 minutes 0.0.0.0:8099->8099/tcp, 9092/tcp kafka3
46c9ca4862b6 confluentinc/cp-kafka:7.2.1     "/etc/confluent/dock…" 54 minutes ago Up 54 minutes 0.0.0.0:8097->8097/tcp, 9092/tcp kafka1
eef5ebec8519 confluentinc/cp-zookeeper:7.2.1 "/etc/confluent/dock…" 54 minutes ago Up 54 minutes 2181/tcp, 2888/tcp, 3888/tcp     zookeeper</pre>



<p>As we can clearly see, all Kafka containers have their ports exposed to our host and Zookeeper is reachable through <code class="EnlighterJSRAW" data-enlighter-language="raw">2181</code>, as well.</p>



<h3 class="wp-block-heading" id="h-4-2-verify-active-kafka-brokers-using-zookeeper-shell">4.2. Verify Active Kafka Brokers Using zookeeper-shell</h3>



<p>Following, let&#8217;s run a <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper-shell</code> on the <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper</code> container:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it zookeeper /bin/zookeeper-shell localhost:2181

# Output:
Connecting to localhost:2181
Welcome to ZooKeeper!
JLine support is disabled

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
</pre>



<p>What&#8217;s important to mention here is that we run a <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper-shell</code> command <strong>from within the container</strong>. That&#8217;s the reason why the <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper_host</code> value is set to <code class="EnlighterJSRAW" data-enlighter-language="raw">localhost</code> and accessible, even though we haven&#8217;t exposed it.</p>



<p>Thanks to the <code class="EnlighterJSRAW" data-enlighter-language="raw">-it</code> flags, we can use the shell from our terminal.</p>



<h3 class="wp-block-heading" id="h-4-3-get-ids-of-currently-active-brokers">4.3. Get IDs of Currently Active Brokers</h3>



<p>So, to get the identifiers of currently active Kafka Brokers, we can use the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ls /brokers/ids

# Output:
[1, 2, 3]
</pre>



<p>The above output clearly indicates that everything is working, as expected.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Note: to close the zookeeper-shell, please hit <strong>Ctrl + C </strong>(and please do it before heading to the Producer/Consumer parts)</p>
</blockquote>



<h2 class="wp-block-heading" id="h-5-publish-messages-with-console-producer">5. Publish Messages With Console Producer</h2>



<p>After we know all of the above, we can finally send messages using the Console Producer.</p>



<p>As the first step, let&#8217;s run bash from a randomly picked Kafka broker container:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it kafka2 /bin/bash
</pre>



<p>Nextly, let&#8217;s create a new topic called <code class="EnlighterJSRAW" data-enlighter-language="raw">randomTopic</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">kafka-topics --bootstrap-server localhost:9092 --create --topic randomTopic

# Output:
Created topic randomTopic.
</pre>



<p>Please note, that given the <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka-topics</code> script is run inside of the container, we point to the <code class="EnlighterJSRAW" data-enlighter-language="raw">9092</code> port of <code class="EnlighterJSRAW" data-enlighter-language="raw">localhost</code>.</p>



<p>Following, we can produce some messages to our new topic: <code class="EnlighterJSRAW" data-enlighter-language="raw"></code></p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">kafka-console-producer --bootstrap-server localhost:9092 --topic randomTopic 

> lorem
> ipsum

# When we finish, we have to hit ctrl + d</pre>



<p>Of course, we could do that from any other broker container (and in order to exit bash, we need to type <code class="EnlighterJSRAW" data-enlighter-language="raw">exit</code> in the command prompt).</p>



<h2 class="wp-block-heading" id="h-6-read-messages-with-console-consumer">6. Read Messages With Console Consumer</h2>



<p>As the last step, let&#8217;s connect to another Apache Kafka broker and read previously sent messages:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec -it kafka3 /bin/bash

kafka-console-consumer --bootstrap-server localhost:9092 --topic randomTopic --from-beginning

# Output:
lorem
ipsum
</pre>



<p>As we can clearly see, the output contains all messages, we&#8217;ve sent using Console Producer.</p>



<h2 class="wp-block-heading" id="h-7-summary">7. Summary</h2>



<p>And that would be all for this article on how to set up multiple Apache Kafka brokers using Docker Compose and produce/consume messages. As a follow-up, I would recommend checking out <a href="https://www.confluent.io/blog/kafka-client-cannot-connect-to-broker-on-aws-on-docker-etc/" target="_blank" rel="noopener">this Confluent article</a> about connecting to Kafka with different configurations.</p>



<p>Finally, let me know your thoughts in the comment section bellow, dear friend! 😀</p>
<p>The post <a href="https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/">How To Deploy Multiple Kafka Brokers With Docker Compose?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How To Set Up Apache Kafka With Docker?</title>
		<link>https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/</link>
					<comments>https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 20 Sep 2022 05:00:52 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[kafka]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503717</guid>

					<description><![CDATA[<p>In this step by step guide, I will show you how to set up Apache Kafka with Docker Compose from scratch and test it with a command line.</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/">How To Set Up Apache Kafka With Docker?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>If you would like to learn how to set up<strong> Apache Kafka</strong> using<strong> Docker Compose</strong> then you&#8217;ve come to the right place 🙂</p>



<p>In this article, together we will:</p>



<ul class="wp-block-list">
<li>see the bare minimum Docker Compose configuration we must provide to spin up an environment with <strong>exactly one Kafka broker and Zookeeper instance</strong>,</li>



<li>take a closer look at the required configurations,</li>



<li>verify our setup using Console Producer and Consumer tools.</li>
</ul>



<p>If you will enjoy this tutorial, then I highly encourage you to check out the continuations:</p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/how-to-deploy-multiple-kafka-brokers-with-docker-compose/">How To Deploy Multiple Kafka Brokers With Docker Compose?</a></li>



<li><a href="https://blog.codersee.com/how-to-set-up-kafka-without-zookeeper-using-docker-compose/">How To Set Up Kafka Without Zookeeper using Docker Compose</a></li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>In my articles, I focus on the practice and getting things done. <br><br>However, if you would like to get a strong understading of DevOps concepts, which are more and more in demand nowadays, then I highly recommend to check out KodeKloud courses, like this <a href="https://shrsl.com/46wvo" rel="sponsored nofollow">Docker</a> and <a href="https://shareasale.com/r.cfm?b=2183704&amp;u=3507867&amp;m=132199&amp;urllink=&amp;afftrack=" rel="sponsored nofollow">Kubernetes</a> learning paths. </cite></blockquote>



<h2 class="wp-block-heading" id="h-2-what-is-apache-kafka">2. What is Apache Kafka?</h2>



<p>Before we start, I feel obliged to provide at least a brief explanation of what Apache Kafka and Zookeeper are. Please skip the following two paragraphs if you want to get straight to the practice part of this tutorial.</p>



<p>Well, Apache Kafka by definition is an open-source distributed event streaming platform. Simply put, it&#8217;s a platform widely used to work with real-time streaming data pipelines and to integrate applications to work with such streams. It&#8217;s mainly responsible for:</p>



<ul class="wp-block-list">
<li>publishing/subscribing to the stream of records,</li>



<li>their real-time processing,</li>



<li>and their ordered storage</li>
</ul>



<p>But let&#8217;s just be honest- it&#8217;s almost impossible to describe Kafka in a couple of sentences and I highly encourage you to visit the Apache Intro page after this tutorial <a href="https://kafka.apache.org/intro" target="_blank" rel="noopener">right here</a>.</p>



<h2 class="wp-block-heading" id="h-3-what-is-apache-zookeeper">3. What is Apache ZooKeeper?</h2>



<p>Or the question should rather be why do I even need another service to work with Apache Kafka?</p>



<p>Just like a zookeeper in a zoo takes care of the animals, Apache Zookeeper &#8220;takes care&#8221; of all the Kafka brokers in a cluster. It&#8217;s mainly responsible for:</p>



<ul class="wp-block-list">
<li>tracking the cluster state- like which brokers are a part of the cluster, sending notifications to Kafka in case of changes</li>



<li>topics configuration</li>



<li>controller election</li>



<li>access control lists and quotas</li>
</ul>



<p>Nevertheless, please keep in mind that the Kafka team is constantly working on <strong>replacing the ZooKeeper with Apache Kafka Raft (KRaft)</strong>. To be more specific- the 2.8.0 version introduced early access to this functionality, but at the moment of publishing this article, it&#8217;s still not production-ready.</p>



<figure class="wp-block-image aligncenter"><a href="https://codersee.com/newsletter/"><img decoding="async" width="1024" height="576" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" class="wp-image-3002956" srcset="https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-300x169.png 300w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-768x432.png 768w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter.png 1440w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<h2 class="wp-block-heading" id="h-4-bare-minimum-kafka-docker-compose">4. Bare Minimum Kafka Docker Compose</h2>



<p>With all of that being said, let&#8217;s have a look at the bare minimum version of the docker-compose.yml file. This configuration is necessary to spin up exactly 1 Kafka and Zookeeper instance:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.2.1
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
  kafka:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka
    ports:
      - "8098:8098"
    depends_on:
      - zookeeper
    environment:
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:8098
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1</pre>



<p>Again, before we head to the next commands, let&#8217;s take a couple of minutes to understand, what exactly is going on here.</p>



<h3 class="wp-block-heading" id="h-4-1-docker-images-version-and-containers">4.1. Docker Images Version And Containers</h3>



<p>Firstly, let&#8217;s take a look at this piece of code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.2.1
    container_name: zookeeper
...   
  kafka:
    image: confluentinc/cp-kafka:7.2.1
    container_name: kafka</pre>



<p>As we can see, with this config we will spin up two services named <code class="EnlighterJSRAW" data-enlighter-language="raw">kafka</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">zookeeper</code>. The <code class="EnlighterJSRAW" data-enlighter-language="raw">container_name</code> will set specified names for our containers, instead of letting Docker generate them. Finally, we will use the 7.2.1 versions of <em>Confluent Community Docker Image for Apache Kafka</em> and <em>Confluent Docker Image for Zookeeper</em>.</p>



<h3 class="wp-block-heading" id="h-4-2-additional-kafka-container-configs">4.2. Additional Kafka Container Configs</h3>



<p>As the next step, let&#8217;s have a quick look at these four lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ports:
  - "8098:8098"
depends_on:
  - zookeeper</pre>



<p>As the name suggests, the <strong>ports&nbsp;</strong>command is responsible for exposing ports, so with this setting, port 8098 of our container will be accessible through the 8098 port of the host machine. Additionally, the <strong>depends_on</strong> will make sure to start the zookeeper container before the kafka.</p>



<h2 class="wp-block-heading" id="h-5-required-settings">5. Required Settings</h2>



<p>As the next step, let&#8217;s take a closer look at the required settings for both Kafka and Zookeeper in our Docker Compose settings.</p>



<h3 class="wp-block-heading" id="h-5-1-required-zookeeper-settings">5.1. Required Zookeeper Settings</h3>



<p>As we can see in our example, the only environment we specified is <strong>ZOOKEEPER_CLIENT_PORT</strong>. This one instructs Zookeeper where it should listen for connections by clients- Kafka in our case.</p>



<p>Additionally, when running in clustered mode, we have to set the <strong>ZOOKEEPER_SERVER_ID</strong> (but it&#8217;s not the case here).</p>



<h3 class="wp-block-heading" id="h-5-2-required-confluent-kafka-settings">5.2. Required Confluent Kafka Settings</h3>



<p>When it comes to the Confluent Kafka Docker image, here is the list of the required environment variables:</p>



<ul class="wp-block-list">
<li><strong>KAFKA_ZOOKEEPER_CONNECT</strong> &#8211; instructing Kafka where it can find the Zookeeper</li>



<li><strong>KAFKA_ADVERTISED_LISTENERS</strong> &#8211; specifying the advertised hostname, which clients can reach out to. Moreover, this value is sent to the Zookeeper</li>
</ul>



<p>Please keep in mind, that in order to run only one instance of Kafka (aka single-node cluster), we have to additionally specify <strong>KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR</strong> set to <strong>1</strong>. Its default value is 3 and if we don&#8217;t do that, we will get into this error: <em>org.apache.kafka.common.errors.InvalidReplicationFactorException: Replication factor: 3 larger than available brokers: 1</em></p>



<h2 class="wp-block-heading" id="h-6-verify-kafka-docker-compose-config">6. Verify Kafka Docker Compose Config</h2>



<p>With all of that being said, we can finally check out if everything is working, as expected.</p>



<h3 class="wp-block-heading" id="h-6-1-start-containers">6.1. Start Containers</h3>



<p>As the first step, we have to start our containers:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker compose up -d</pre>



<p>The <strong>-d</strong> flag instructs the docker to run containers in a detached mode.</p>



<p>Of course, we can verify everything by running the <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</code> command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># example output:
CONTAINER ID IMAGE                           COMMAND                CREATED        STATUS       PORTS                            NAMES
38b41bc43676 confluentinc/cp-kafka:7.2.1     "/etc/confluent/dock…" 42 seconds ago Up 4 seconds 0.0.0.0:8098->8098/tcp, 9092/tcp kafka
45484184f330 confluentinc/cp-zookeeper:7.2.1 "/etc/confluent/dock…" 42 seconds ago Up 5 seconds 2181/tcp, 2888/tcp, 3888/tcp     zookeeper

</pre>



<h3 class="wp-block-heading" id="h-6-2-create-kafka-topic">6.2. Create Kafka Topic</h3>



<p>Nextly, let&#8217;s create a new Kafka topic:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec kafka kafka-topics --bootstrap-server localhost:8098 --create --topic example</pre>



<p>As a word of explanation, the <strong>kafka-topics</strong> is a shell script that allows us to execute a <strong>TopicCommand</strong> tool. It&#8217;s a command-line tool that can manage and list topics in a Kafka cluster. Additionally, we have to specify the listener hostname (specified with KAFKA_ADVERTISED_LISTENERS) with <strong>&#8211;bootstrap-server</strong>.</p>



<p>When the topic is created we should see the following message:<strong> &#8220;Created topic example.&#8221;</strong></p>



<h3 class="wp-block-heading" id="h-6-3-run-kafka-producer">6.3. Run Kafka Producer</h3>



<p>Following, let&#8217;s run a console producer with the <strong>kafka-console-producer:</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec --interactive --tty kafka kafka-console-producer --bootstrap-server localhost:8098 --topic example</pre>



<p>Please keep in mind that this command will start the producer and it will wait for our input (and you should notice the <code class="EnlighterJSRAW" data-enlighter-language="raw">&gt;</code> sign). Please specify a couple of messages and hit <strong>Ctrl + D</strong> after you finish:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>lorem
>ipsum
# hit Ctrl + D</pre>



<h3 class="wp-block-heading" id="h-6-4-run-kafka-consumer">6.4. Run Kafka Consumer</h3>



<p>As the last step, let&#8217;s run a console consumer with the <strong>kafka-console-consumer </strong>command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker exec --interactive --tty kafka kafka-console-consumer --bootstrap-server localhost:8098 --topic example --from-beginning</pre>



<p>This time, we should see that our messages are printed to the output successfully (and to finish please hit <strong>Ctrl+ C</strong>):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">lorem
ipsum</pre>



<h2 class="wp-block-heading" id="h-7-kafka-with-docker-compose-summary">7. Kafka With Docker Compose Summary</h2>



<p>And that would be all for this step-by-step guide on <strong>How To Set Up Apache Kafka With Docker Compose</strong>.</p>



<p>Let me know about your thoughts in the comment section, or reach out through the <a href="https://codersee.com/contact/">contact</a> form- I highly appreciate your thoughts, comments and feedback.</p>



<p>Take care brother/sister!</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/">How To Set Up Apache Kafka With Docker?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/how-to-set-up-apache-kafka-with-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-05-14 19:29:02 by W3 Total Cache
-->