<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shitij Bhatnagar</title>
    <description>The latest articles on DEV Community by Shitij Bhatnagar (@shitij_bhatnagar_b6d1be72).</description>
    <link>https://dev.to/shitij_bhatnagar_b6d1be72</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2494704%2F0e505229-57cc-4618-8791-539803c12b0e.jpg</url>
      <title>DEV Community: Shitij Bhatnagar</title>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shitij_bhatnagar_b6d1be72"/>
    <language>en</language>
    <item>
      <title>Meaningful and humor books in IT</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Sat, 14 Feb 2026 16:30:33 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/meaningful-and-humor-books-in-it-4gnf</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/meaningful-and-humor-books-in-it-4gnf</guid>
      <description>&lt;p&gt;I always used to think that a book that will be meaningful could not be funny or, one that could be funny, would not have much meaning, till I got introduced to humor literature. And, the one that stayed with me the most was humor that was related to my own job/industry i.e. IT.&lt;/p&gt;

&lt;p&gt;Around 10 years ago, I came across the book called '&lt;strong&gt;The Phoenix Project&lt;/strong&gt;' - A Novel about IT, DevOps, and Helping your Business Win. By Gene Kim, Kevin Behr, George Spafford &lt;a href="https://www.amazon.in/Phoenix-Project-Helping-Business-Grayscale/dp/9355428588" rel="noopener noreferrer"&gt;link&lt;/a&gt; that talks about IT operations, Dev Ops, repeatable/predictable project delivery, managing business expectations, scaling of work and a lot more. Even though it was published many years ago, I still end up referring to it; while it has lot of meaning and lessons, the humor is amazing too and makes for a compelling read. It does not talk about core software development, but the end to end processes, critical dependencies, tight coupling / hard dependencies and solutions that one can relate to surely.&lt;/p&gt;

&lt;p&gt;For anyone with IT experience (any level or aspiring to), the events mentioned in the book could seem relatable or bring up a memory and even smiles. And who knows, you might end up even comparing yourself to some of the characters in the book :-)&lt;/p&gt;

&lt;p&gt;I hence would like to recommend this book to anyone who works in IT, irrespective whether you are in technical or functional or managerial role. Having said that, I would love to hear any similar book recommendations from anyone.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>humor</category>
      <category>learning</category>
      <category>books</category>
    </item>
    <item>
      <title>How multi-tenancy forced removal of caching in a critical API</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Fri, 30 Jan 2026 14:10:46 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/why-we-removed-caching-from-a-critical-api-312p</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/why-we-removed-caching-from-a-critical-api-312p</guid>
      <description>&lt;p&gt;When we think about data caching in an API, we automatically think about faster response time, less database calls, better overall performance - everything positive. The more critical the API (e.g. financial), the more benefits an optimization technique like caching can bring. However, in one specific scenario, &lt;strong&gt;data caching became a bottleneck&lt;/strong&gt;. Yes, that is right.&lt;/p&gt;

&lt;p&gt;This was the case when an on-premises API (note - API is REST end point, back-end Spring Boot and many other integrations and libraries combined) that is working well in production however to be more scalable, made more stable, needed to be run in high availability / multi-instance mode.&lt;/p&gt;

&lt;p&gt;When faced with a situation like this, there is generally a &lt;strong&gt;good amount of opinions&lt;/strong&gt; within a development team and option analysis, like let's use Redis/distributed caching, let's synchronize Spring cache between instances and more similar options plus another &lt;em&gt;unusual option&lt;/em&gt; - remove data caching. And after a lot of deliberation, the last option was chosen i.e. remove caching altogether from the API. There were few reasons for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronizing cache&lt;/strong&gt; across multiple service instances is error prone if done through custom mechanisms (heard of the cache invalidation problem in computer science? :-))&lt;/li&gt;
&lt;li&gt;Using a new &lt;strong&gt;distributed cache&lt;/strong&gt; like Redis would involve additional components, maintenance and possibly infrastructure (and skills too)&lt;/li&gt;
&lt;li&gt;The API was &lt;strong&gt;write heavy&lt;/strong&gt;, more data is written or sent downstream vs being read, removing caching removed additional look ups within the boundary of the application&lt;/li&gt;
&lt;li&gt;The API heart beat checks are periodic (e.g. every 30 seconds), besides that, the &lt;strong&gt;read traffic is not in significant/high use&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The API &lt;strong&gt;volume is growing gradually&lt;/strong&gt;, not exponentially (in short to medium term)&lt;/li&gt;
&lt;li&gt;We need to move the API to &lt;strong&gt;cloud&lt;/strong&gt; in 12+ months and that will eventually need some re-design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High availability is a design principle&lt;/strong&gt; that needs to be adhered to (by design, not by accident)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The trade off?&lt;/strong&gt; a miniature hit to performance in leu of high availability benefits &amp;amp; stability (that is proven through load testing, tangibly).&lt;/p&gt;

&lt;p&gt;Given the above, a decision was made to remove caching from the API. This was &lt;strong&gt;not an easy decision&lt;/strong&gt; as every developer feels emotional about the code they write :-), however in this case, this clearly was &lt;strong&gt;the pragmatic option&lt;/strong&gt;, while also an example where caching seemed like an &lt;strong&gt;existing unnecessary over engineering in an API&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: a best practice like caching also needs to be considered for fitment before applying it - whether the service is on-premises or on the cloud, considering data load, consumer size, non functional requirements, long term impacts and more.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The outcome was a leaner API service which can be run in multiple instance mode and we do not need to worry about whether the data being saved/retrieved is latest or stale. &lt;/p&gt;

&lt;p&gt;Thanks for reading thus far, if anyone has any similar experience or other experiences with design choices, would love to hear and learn from that.&lt;/p&gt;

</description>
      <category>software</category>
      <category>discuss</category>
      <category>design</category>
      <category>learning</category>
    </item>
    <item>
      <title>A Critical Gap in the Technical path, and AI cannot solve this</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Sun, 25 Jan 2026 16:42:54 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/a-critical-gap-in-the-technical-path-1fk9</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/a-critical-gap-in-the-technical-path-1fk9</guid>
      <description>&lt;p&gt;We have always learnt about the multiple career paths in IT, especially in software engineering/development. But did we ever imagine in a dynamic industry like this, what &lt;strong&gt;gaps could/would creep in silently&lt;/strong&gt; and impact the very culture of teams, departments and firms? Let's look at the career paths first and then will quickly talk about the gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Management path&lt;/strong&gt; involves people development, line management, coaching, performance management, project management, budgeting and a lot more; this path typically starts after one has spent a few years in junior/senior roles, have an understanding of how teams deliver work and then a professional makes a choice (or is forced due to circumstances or under influence) to pursue management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical path&lt;/strong&gt; involves growing into senior engineer, technical lead, staff engineer, architect and similar roles over time.&lt;/p&gt;

&lt;p&gt;And then of course the new path i.e. &lt;strong&gt;Hybrid&lt;/strong&gt; - that has roles like Engineering manager, Technical manager every since IT companies starting asking managerial roles to be more hands-on (&lt;em&gt;without thinking about its impact or success rate&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Let me talk about the gaps that I am seeing as critical (or becoming worrisome) for past few years. This relates to some behaviors that I have noticed in past few years in multiple organizations and relates to technical path, the following are only few examples or behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Engineers &lt;strong&gt;only focused on their individual contribution&lt;/strong&gt;, not sharing anything much in teams or outside team (this might appear like a behavior that has always been there, but I feel it is a bit compounded in past few years)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Engineers who are &lt;strong&gt;high headed&lt;/strong&gt; and tend to shrug off others (there are always a few in every team, at least one - in my experience :-))&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Senior Engineers / Tech leads (at least 10 years+ experienced) being &lt;strong&gt;immature&lt;/strong&gt; in their oral and written communications e.g. not considering what impact their words or gestures may have&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Engineers at all levels (juniors, seniors, leaders) &lt;strong&gt;not joining meetings on time&lt;/strong&gt; :-), yes, this happens more often than we think it does; including engineers not opening their meeting codes on time while interviewing candidates (i.e. making candidates wait)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Engineers growing into tech leads/engineering managers &lt;strong&gt;with line management&lt;/strong&gt; however not having an eye for observing individual or team behaviors and too focused on just the technical stuff (guess who loses out - everyone)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Engineering leaders (managers, VPs &amp;amp; more) losing touch of technical reality due to not being hands-on for some time but make up by being good people leaders OR in some cases, being forced to be hands-on/coding, so not able to properly focus on people aspects (basically, a mix up)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can go on, but I would take a pause here, because these are just my observations and not necessarily what anyone else may have seen in the technical paths. These observations made me ponder about why this is happening (in pockets or in plenty), because this neither looks healthy nor it is something that I would want the next generation to follow.&lt;/p&gt;

&lt;p&gt;In my humble view, I felt the following could be some of the reasons (and I am not talking of generation gap here), there would surely be a lot more to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Engineers not receiving adequate &lt;strong&gt;direction&lt;/strong&gt; in their careers that it is not just producing software (the 'what' part of work), but also behaviors and maturity (the 'how' part of work) that matters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A culture of &lt;strong&gt;not troubling&lt;/strong&gt; 'technically strong engineers', worrying they will leave :-) (and letting work environment get toxic)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance assessments that &lt;strong&gt;emphasize that only technical delivery matters&lt;/strong&gt;, behaviors do not matter 'as much' comparatively&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of gut&lt;/strong&gt; in line managers and leaders to take 'tough calls', to coach others, even if it's difficult&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, I would take a pause here, but one thing that clearly stands out to me in this assessment is that while technical delivery happens, individual contributors do their work, teams deliver.. but people don't grow in stature, team's don't gain &lt;strong&gt;emotional intelligence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Does all of this come down to just the leader of the team / project / department (stuff like this does not happen in a day, it builds over time)?. May be I am wrong, but if the leader is not focusing enough on the soft aspects (not just technical aspects), &lt;strong&gt;how would a correction happen?&lt;/strong&gt; And if leaders/managers are thrown into coding left right center, who will take care of the softer issues, AI?&lt;/p&gt;

&lt;p&gt;I would appreciate if anyone could share their experiences, suggestions or any comments.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>software</category>
      <category>learning</category>
      <category>career</category>
    </item>
    <item>
      <title>Do we need junior developers? - https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers-2k2l</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Sat, 17 Jan 2026 13:15:46 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers--h43</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers--h43</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/shitij_bhatnagar_b6d1be72" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2494704%2F0e505229-57cc-4618-8791-539803c12b0e.jpg" alt="shitij_bhatnagar_b6d1be72"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers-2k2l" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Do we need Junior developers?&lt;/h2&gt;
      &lt;h3&gt;Shitij Bhatnagar ・ Jan 17&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#career&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Do we need Junior developers?</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Sat, 17 Jan 2026 13:11:01 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers-2k2l</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/do-we-need-junior-developers-2k2l</guid>
      <description>&lt;p&gt;Do we need junior developers, freshers, early aspirants in Software Industry (and any other industry)? my take - &lt;strong&gt;Yes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Doesn't the above take seem contrary to the prevalent news about AI taking over junior or senior jobs, why so much narrative about every day work going away to robots (like shown in the movie '&lt;em&gt;I Robot&lt;/em&gt;' released in 2005) - when retail/daily Robotics is at least a decade away and Artificial Intelligence (particularly Agentic AI) is still shaping up?&lt;/p&gt;

&lt;p&gt;There is some truth (early strides like use of chatbots for IT support), but lot of hype to aid capitalism, regrettably; still the news circulates on and professionals get effected because taking this narrative path helps justify role reductions or reorgs or strategizing, which leads to cost savings, then leads to higher share prices or opportunity budget that can be deployed elsewhere - &lt;em&gt;just an inference&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;However, even if there is some truth, does it mean the industry does not require junior engineers/professionals? This has never been true, nor would it ever be, in my humble opinion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entry level / junior professionals&lt;/strong&gt; - you are the next generation, the next force that pushes the economic pedestal forward for your country and industry, you have the energy and perspective, you have the internet and lot of free resources to upskill and you can understand industry trends well (or learn).. simply said, &lt;strong&gt;you are smart, vocal, capable and well armed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First some historical reference:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During the economic downturn events (2000 dot com burst, 2008-09 financial crisis, Covid-19), for freshers/juniors (even seniors), I cannot deny that struggle was there, I am aware of many technologists to moved to marketing/data processing/front office/other roles to keep the house running; due to lay offs many professionals had to stay out of job for few months, but then eventually (even after an year), people got the relevant jobs - may not be in big companies, but in small ones, midsized ones, working as interns/trainees, faculties, part-time engineers and then &lt;strong&gt;eventually landing&lt;/strong&gt; the kind of jobs they wanted or aspired to. It took time, but it happened; career toll? it took some months or year out of a career trajectory, but few months or year out of a 35-40 year career, can be caught up - looking at the long term.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: majority of working people I have known over the past 25 years are still employed, but they did need to pick up new skills or move into different roles&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;To the point about AI/automation impacting need for junior engineers:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before AI, let's talk about previous automation spikes and what they did to IT jobs and need for juniors. Innovations like &lt;strong&gt;software test automation&lt;/strong&gt;, &lt;strong&gt;robotic process automation&lt;/strong&gt; (RPA), &lt;strong&gt;AI chatbots&lt;/strong&gt; in support/customer facing phases forced professionals to pick new skills rather than eliminate their jobs - for majority of the roles (but some were effected e.g. reduction in IT support personnel head count or manual software testers asked to become test engineers etc.); again, only the professionals who did not adapt to new skills or aligned with new realities (incl. to increased productivity or performance expectations) were effected - and that too not on day one, but over a tenure. &lt;strong&gt;The need for entry level / juniors did not go away, junior hiring got delayed or recruitment numbers got reduced temporarily but definitely picked up later&lt;/strong&gt; - let's call this the &lt;strong&gt;'delay' effect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI&lt;/strong&gt; is another form of automation that is &lt;strong&gt;much smarter&lt;/strong&gt; than its predecessors. The &lt;strong&gt;first impact or theme&lt;/strong&gt;, at least at this stage with Generative AI (Chat GPT, Gemini, Claude and others) is that with these chat tools at hand, people need to be a lot more productive and should be multipliers. So, if you see, the business demands are what? -&amp;gt; 'increased productivity or performance expectations'. I find this as a common theme repeated over and over again irrespective of the automation spike at play.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Another theme&lt;/strong&gt; that AI has actually expedited (it was already growing) is a shift towards leaner organization hierarchy, increasing technical skills, currently trending as the need for more engineering skills and reduction in facilitation oriented roles. Whether we like this or not, this is happening and we must prepare/adapt. So, what could juniors/entry level staff do? Do more side / live projects to be job ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yet another theme&lt;/strong&gt; that will start taking shape is when Agentic AI becomes production ready, it could further impact facilitator roles in companies if the privacy and security concerns of Agentic AI are taken care of. But who would configure Agentic AI? of course, developers. And what could facilitator oriented roles do? Mesh more business or other skills relevant to the job or industry&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI will impact different IT companies in different ways&lt;/strong&gt;, depending on their business models, segregation of roles, customer profiles, funding and more. But some things are less likely to change, in my humble opinion, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need for engineers who are productive and multipliers (right attitude)&lt;/li&gt;
&lt;li&gt;Need for engineers to oversee output of AI tools, this would be a technical function&lt;/li&gt;
&lt;li&gt;Need for project managers to manage projects and programmes&lt;/li&gt;
&lt;li&gt;Need for production support staff to man IT systems and tackle incidents/problems (if I were an IT production manager, I would not trust a solution from AI - at least at this point, I need to talk to a man)&lt;/li&gt;
&lt;li&gt;Need for engineers to mentor other engineers, otherwise, the human connect will vanish&lt;/li&gt;
&lt;li&gt;Need for Engineering leaders, not just talkers but, those who can walk the talk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need for multiple skills&lt;/strong&gt; - degrees would matter less, skills would matter more. And, this is not a new theme, its been there since a few decades at least in IT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I would have done if I were a junior developer or fresher today&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being an entry level / junior is never easy in an unsettling economic or industry stage, like in IT. Yet, if I were there today, this is what I would have done:&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Talked to experienced engineers&lt;/strong&gt; (find on LinkedIn, Medium, dev.to etc.), taken their guidance for realistic projects and done practice&lt;br&gt;
2) &lt;strong&gt;Checked college/university alumni&lt;/strong&gt; and sought support (kept ego side)&lt;br&gt;
3) &lt;strong&gt;Volunteered&lt;/strong&gt; for freelance projects&lt;br&gt;
4) Got into any &lt;strong&gt;IT role&lt;/strong&gt; &lt;em&gt;if possible&lt;/em&gt; (trainee, intern, tester, engineer, support) in any IT organization / get foot in the door&lt;br&gt;
5) &lt;strong&gt;Created a smart CV&lt;/strong&gt; covering projects, skills, volunteering, education&lt;br&gt;
6) Done &lt;strong&gt;Network Network Network&lt;/strong&gt; - one of the lifelines of career&lt;br&gt;
7) Focused on learning and &lt;strong&gt;professional certification&lt;/strong&gt;&lt;br&gt;
8) &lt;strong&gt;Taken career guidance&lt;/strong&gt; from experienced IT folks&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At the end&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hope the articles resonates with someone, I would be more than happy to hear any criticism or views that would widen my own and everyone's understanding and to help each other. And if you ask me about AI impact, I believe the senior IT professionals are at a much higher risk than juniors.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Simplified: Spring Boot with Docker (Part 3)</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Sun, 23 Feb 2025 10:00:11 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-3-53ip</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-3-53ip</guid>
      <description>&lt;p&gt;We covered the 'Multiple Services Instances' angle of Spring Boot and Docker in the &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-2-55gk"&gt;Part 2&lt;/a&gt; article of this series.&lt;/p&gt;

&lt;p&gt;In this Part 3 article, we shall see how to manage dependencies between services, recovering from start failures, following a start up sequence through Docker Compose in an application that has multiple services. There are numerous examples of such an arrangement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microservice that talks to a database (another service)&lt;/li&gt;
&lt;li&gt;Single Page application (SPA) that talks to microservice in background&lt;/li&gt;
&lt;li&gt;Necessary service bindings (e.g. distributed logging)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Pre-requisites:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To experience the journey as outlined in the article, it is recommended to have the pre-requisites in place before getting on with the technical steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading: Basic awareness about Containerization &amp;amp; Docker&lt;/li&gt;
&lt;li&gt;Tool: Installed Docker Desktop – contains multiple Docker services&lt;/li&gt;
&lt;li&gt;Tool: Familiarity with Java (preferably 8 onwards), Spring Boot 3.4.x&lt;/li&gt;
&lt;li&gt;Tool: Installed JDK 17 &amp;amp; Maven (build tool)&lt;/li&gt;
&lt;li&gt;Hardware: Recommended to have at least 08 GB RAM (more the merrier)&lt;/li&gt;
&lt;li&gt;Reading: Understood the &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/spring-boot-befriends-docker-part-1-1kpf"&gt;Part 1&lt;/a&gt; &amp;amp; &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-2-55gk"&gt;Part 2&lt;/a&gt; articles (Basics &amp;amp; Docker Compose)&lt;/li&gt;
&lt;li&gt;Code: We shall use the 'Referral' Spring Boot application available at this &lt;a href="https://github.com/shitijbhatnagar/Referral" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt; and Docker setup used in Part 1 &amp;amp; 2 article&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Context:
&lt;/h2&gt;

&lt;p&gt;We have a Spring Boot microservice called '&lt;strong&gt;Referral&lt;/strong&gt;'. This service helps persist &amp;amp; retrieve referral (any person) information in a back-end database - H2 (in memory) or external (PostgreSQL on-prem or container) and each of these 03 storage options can be enabled by choosing the active spring profile i.e. &lt;strong&gt;local&lt;/strong&gt; (H2), &lt;strong&gt;dev&lt;/strong&gt; (PostgreSQL on-prem) or &lt;strong&gt;dockerize&lt;/strong&gt; (PostgreSQL on Docker container). Note that the default spring profile is local.&lt;/p&gt;

&lt;p&gt;Let's look at the operations we can perform with the &lt;strong&gt;Referral application&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET /referrals/status&lt;/strong&gt; to get the status of the service (&lt;em&gt;alternative is Actuator but that is not enabled right now&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GET /referrals&lt;/strong&gt; to retrieve all the referrals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST /referrals&lt;/strong&gt; to add a referral [JSON request like { "givenName": "Marty" }]&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Objectives we want to accomplish:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the Referral application to Docker (think &lt;em&gt;service&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Keep multiple instances of the API / service (think &lt;em&gt;replicas&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Have request distribution to the Referral application instances (think &lt;em&gt;another service i.e NGINX&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Referral application to connect with PostgreSQL running in container (think &lt;em&gt;another service i.e. PostgreSQL&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;When using 'dockerize' spring profile, the database connectivity information &amp;amp; schema setups are &lt;strong&gt;&lt;u&gt;not provided&lt;/u&gt;&lt;/strong&gt; in the application properties but are defined externally in the Docker Compose YML (we shall touch on this later in the article)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Steps:
&lt;/h2&gt;

&lt;p&gt;In order to meet the objectives, let's move one step at a time as there is a lot to accomplish here.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Understand Services &amp;amp; inter-dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The traffic to 'Referral' application instances will channeled though the web server NGINX (similar approach to Part 2 article). The 'Referral' application instances shall integrate with PostgreSQL running in another container. So, we see at least 03 entities with their inter-dependencies as below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NGINX&lt;/strong&gt; (depends on Referral)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Referral&lt;/strong&gt; (depends on PostgreSQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; (with necessary database schema)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What does 'Referral Controller' do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's look at the below code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.sb.mybatis.postgre.web;

import com.sb.mybatis.postgre.dto.ReferralDTO;
import com.sb.mybatis.postgre.service.ReferralService;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/referrals")
public class ReferralController {

    @Autowired
    private ReferralService referralService;

    @GetMapping("/status")
    public String getStatus() {
        log.info("ReferralController.getStatus() is invoked");
        return "Referral Service is Up";
    }

    @GetMapping
    public List&amp;lt;ReferralDTO&amp;gt; getTransactions() throws Exception {
        log.info("ReferralController.getTransactions() is invoked");
        return referralService.findAllReferralRecords();
    }

    @PostMapping
    public ReferralDTO createTransaction(@Valid @RequestBody ReferralDTO referralDTO) throws Exception {
        log.info("ReferralController.createTransactions() is invoked");
        if(referralService.insertReferral(referralDTO) == 1) {
            //Success
            return referralDTO;
        }
        else throw new Exception("Error in creating new Referral record");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/shitijbhatnagar/Referral/blob/master/src/main/java/com/sb/mybatis/postgre/web/ReferralController.java" rel="noopener noreferrer"&gt;&lt;em&gt;ReferralController&lt;/em&gt;&lt;/a&gt; allows us to create/retrieve Referral records, provide service status and at the same time also add a simple message in the application logs that can be verified (would be referred later in the article).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Create Referral Executable Jar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First and foremost, please clone the &lt;strong&gt;Referral&lt;/strong&gt; spring boot application locally and run the command &lt;code&gt;mvn clean install&lt;/code&gt; to have the executable jar file ready (name like Referral-0.0.1-SNAPSHOT.jar).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Create Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to create a Docker image for the Referral application, we first need to create a &lt;a href="https://github.com/shitijbhatnagar/Referral/blob/master/src/env/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM openjdk:17
EXPOSE 8081
ARG JAR_FILE=Referral-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} Referral-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java","-Dspring.profiles.active=dockerize","-jar","/Referral-0.0.1-SNAPSHOT.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Provision of the spring profile 'dockerize' in the ENTRYPOINT ensures this profile is picked up in the eventual image creation&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Create 'Referral' Docker image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the Docker image, let's execute the command &lt;code&gt;docker build -t sb-referral-service:V1 .&lt;/code&gt; to create the docker image for the Referral application.&lt;/p&gt;

&lt;p&gt;A successful execution should look like below, indicating that the Docker image 'sb-referral-service:V1' is built.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfidvxg6izls69608oan.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfidvxg6izls69608oan.JPG" alt="Successful Image creation" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Create NGINX.conf (web server configuration)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similar to the Part 2 article, let's create a NGINX configuration file which should indicate that all requests to &lt;em&gt;sb-referral-service&lt;/em&gt; should be channeled through the NGINX service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user  nginx;

events {
    worker_connections   10;
}
http {
        server {
              listen 8000;
              location / {
                proxy_pass http://sb-referral-service:8081;
              }
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Create PostgreSQL DB Schema creation script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As mentioned previously in the Step 'Understand Services &amp;amp; inter-dependencies', we would like to ensure that the PostgreSQL DB in Docker should have the necessary database schema. If this is not the case, the Referral application will not work because Spring Boot is not instructed to initiate schema creation in 'dockerize' mode/profile.&lt;/p&gt;

&lt;p&gt;Let's create an SQL script by the name 'create-db.sql' with content as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create table if not exists T_REFERRAL
(
    id varchar(255) primary key,
    name varchar(255) NOT NULL
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;'create-db.sql' needs to be part of the PostgreSQL configuration in Docker Compose YML file&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Define Service Orchestration via Docker Compose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once we have the Referral image, database schema creation SQL script and the NGINX configuration files in place, the next step is to define how the different services will be associated, sequenced (in start up) and recover (in case of any dependent service failure). Let's have a look at the Docker Compose YML below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - sb-referral-service
    ports:
      - '8000:8000'

  sb-referral-service:
    image: 'sb-referral-service:V1'
    restart: on-failure
    expose:
      - '8081'
    build:
      context: .
    deploy:
      replicas : 2
    depends_on:
      - db
    environment:
      - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/test
      - SPRING_DATASOURCE_USERNAME=test
      - SPRING_DATASOURCE_PASSWORD=test
      - SPRING_JPA_HIBERNATE_DDL_AUTO=none

  db:
    image: 'postgres:13.1-alpine'
    container_name: db
    environment:
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test
    volumes:
      - ./db:/var/lib/postgresql/data
      - ./create-db.sql:/docker-entrypoint-initdb.d/create_database.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the YML file closely, we find 03 services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NGINX&lt;/strong&gt;: the web server, that will be setup by Docker by utilizing the NGINX.conf file and that has dependency on 'sb-referral-service'&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sb-referral-service&lt;/strong&gt;: the Referral application, that will have 02 instances (&lt;em&gt;replicas&lt;/em&gt;), will be restarted by Docker if it's previous attempt was unsuccessful (&lt;em&gt;restart: on-failure&lt;/em&gt;) and is dependent on 'db'&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;db&lt;/strong&gt;: the PostgreSQL server, that will use the create-db.sql script and use the provided credentials for 'test' database&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice the provision of the Spring Boot to database connectivity information in the 'environment' section under 'sb-referral-service' and the atypical spring datasource url as &lt;code&gt;jdbc:postgresql://db:5432/test&lt;/code&gt; - that is because the database is running in a container&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Starting the Services in Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To initiate creation of the services in Docker, we need to execute the command &lt;code&gt;docker compose up&lt;/code&gt; (please ensure create-db.sql and nginx.conf files are also present in the same folder) and we should see a message like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qx648jfbnfby6s21bea.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qx648jfbnfby6s21bea.JPG" alt="docker compose up" width="800" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the services are started successfully, we should be able to see them in Docker Desktop like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lxheo3xaqimsxj28euh.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lxheo3xaqimsxj28euh.JPG" alt="container view" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Pre-Checks before consuming the services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes re-starts can be noticed in the upstream services (in this case, sb-referral-service) if the downstream services (in this case, db) are still getting setup while the upstream is trying to connect to the downstream. So, instead of rushing to consume the Referral application after seeing 05 containers visible in the Docker Desktop Dashboard, we can do some pre-checks also.&lt;/p&gt;

&lt;p&gt;So, before we attempt to start using the API/end points of Referral application at the URL &lt;a href="http://localhost:8000/referrals" rel="noopener noreferrer"&gt;http://localhost:8000/referrals&lt;/a&gt; (note the port 8000 is of NGINX), we can additionally verify the following messages from the command prompt / log messages displayed by 'docker compose up' command:&lt;/p&gt;

&lt;p&gt;Successful PostgreSQL setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqi4b83rhjk2svai8r48.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqi4b83rhjk2svai8r48.JPG" alt="db ready" width="800" height="15"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Successful Referral application setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw878h44t5lpjxyxu9e5n.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw878h44t5lpjxyxu9e5n.JPG" alt="app ready" width="648" height="22"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because the 'depends on' (in Docker Compose YML) only waits for the other container to be up (and not for its contained application to be fully running), it is advisable to additionally verify the applications/services before consuming them.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Consuming the 'Referral' application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that the services are running in Docker, let's try consuming them, this time from Postman.&lt;/p&gt;

&lt;p&gt;When we execute GET &lt;a href="http://localhost:8000/referrals/status" rel="noopener noreferrer"&gt;http://localhost:8000/referrals/status&lt;/a&gt;, we should get a successful message about service status, like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c68v022xhwkva53u8yi.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c68v022xhwkva53u8yi.JPG" alt="app service status" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we execute GET &lt;a href="http://localhost:8000/referrals" rel="noopener noreferrer"&gt;http://localhost:8000/referrals&lt;/a&gt;, we should receive zero Referral records, because none exist, like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbme42dnbish8i816jcbm.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbme42dnbish8i816jcbm.JPG" alt="empty referrals" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we execute POST &lt;a href="http://localhost:8000/referrals" rel="noopener noreferrer"&gt;http://localhost:8000/referrals&lt;/a&gt;, we should see a success message with the Referral information, like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl0c9j3avvuhpb3di4y7.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl0c9j3avvuhpb3di4y7.JPG" alt="create referral success" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, again when we execute GET &lt;a href="http://localhost:8000/referrals" rel="noopener noreferrer"&gt;http://localhost:8000/referrals&lt;/a&gt; to see if we see the newly created Referral in the fetch operation, we receive the Referral record(s), like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foy8wzo5wrcq0sfh83b4t.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foy8wzo5wrcq0sfh83b4t.JPG" alt="list referrals" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to the above, let's also verify the Referral application logs. For the same, we can go to the individual sb-referral-service instances/containers and go to Logs option, we should see below (based on the above success).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sctjh0higd2bbs82m9u.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sctjh0higd2bbs82m9u.JPG" alt="service log 1" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzh9qkazfysy53q3ctob.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzh9qkazfysy53q3ctob.JPG" alt="service log 2" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that as per the above two logs, Referral services instances received different requests (from Postman), which also proves that NGINX is rotating the requests between the instances successfully&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Destroy the containers / services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As our work finishes, we can finish the containers using the command &lt;code&gt;docker compose down&lt;/code&gt; and we should see a confirmation, like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F78zla375nr513ohizeys.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F78zla375nr513ohizeys.JPG" alt="docker compose down" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, we saw how Docker (and Docker Compose) allowed us to comfortably build a service orchestration that is typically only possible in a test environment - on our development machine and how we can use it - covering multiple services (including database), service to service communication, service start up sequencing and recovery.&lt;/p&gt;

&lt;p&gt;Would appreciate any feedback for improvement on the approach and content.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/52699899/depends-on-doesnt-wait-for-another-service-in-docker-compose-1-22-0" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/52699899/depends-on-doesnt-wait-for-another-service-in-docker-compose-1-22-0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>java</category>
      <category>springboot</category>
      <category>docker</category>
    </item>
    <item>
      <title>Simplified: Spring Boot with Docker (Part 2)</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Fri, 14 Feb 2025 17:11:01 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-2-55gk</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/simplified-spring-boot-with-docker-part-2-55gk</guid>
      <description>&lt;p&gt;We covered the 'Getting Started' angle of Spring Boot and Docker in the &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/spring-boot-befriends-docker-part-1-1kpf"&gt;Part 1&lt;/a&gt; article of this series.&lt;/p&gt;

&lt;p&gt;In this Part 2 article, we shall see how to span up multiple instances of a Spring Boot REST service using containers on a developer machine itself. The need to have multiple service instances can arise due to a number of reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Observe REST service behavior in multi-instance setup&lt;/li&gt;
&lt;li&gt;Simulate a test environment setup locally&lt;/li&gt;
&lt;li&gt;Experimentation / Getting hands dirty before moving to more complicated scenarios (like proving service to service connectivity, orchestration expansion &amp;amp; more)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the Part 3 article of the series, we shall bring in more details around inter-service hard dependencies, orchestration methods (&amp;amp; more) however here, we shall cover multi-instance possibilities in Docker.&lt;/p&gt;




&lt;p&gt;Pre-requisites:&lt;br&gt;
To experience the journey as outlined in the article, it is recommended to have the pre-requisites in place before getting on with the technical steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Reading: Basic awareness about Containerization &amp;amp; Docker&lt;/li&gt;
&lt;li&gt;  Tool: Installed Docker Desktop – contains multiple Docker services&lt;/li&gt;
&lt;li&gt;  Tool: Familiarity with Java (preferably 8 onwards), Spring Boot 3.4.x&lt;/li&gt;
&lt;li&gt;  Tool: Installed JDK 17 &amp;amp; Maven (build tool)&lt;/li&gt;
&lt;li&gt;  Hardware: Recommended to have at least 08 GB RAM (more the merrier)&lt;/li&gt;
&lt;li&gt;      Reading: Understood the &lt;a href="https://dev.to/shitij_bhatnagar_b6d1be72/spring-boot-befriends-docker-part-1-1kpf"&gt;Part 1&lt;/a&gt; article (Basics)&lt;/li&gt;
&lt;li&gt;      Reuse: We shall re-use the 'Tower' Spring Boot service and Docker setup used in Part 1 article&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;So, in Part 1, we saw how to deploy the 'tower' service in Docker and consume it through the application URL &lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if we want to have multiple instances of the 'tower' service aka run 'tower' service in multiple containers, what are the different options to do so in Docker, let's explore the same below.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Objective&lt;/strong&gt;: We should have multiple instances of the 'tower' service, each ready to serve clients&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A&lt;/strong&gt;: Execute multiple 'docker run' commands&lt;/p&gt;

&lt;p&gt;For creating multiple containers, we can first try to run the 'docker run' command (same as in Part 1 article) multiple times - this might seem like first no-brainer option to exercise. Let's have a look-&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Command 1&lt;/em&gt;: docker run -p 8085:8085 sb-tower-service:V1&lt;br&gt;
&lt;em&gt;Command 2&lt;/em&gt;: docker run -p 8085:8085 sb-tower-service:V1&lt;/p&gt;

&lt;p&gt;When the first command is executed, the 'tower' service comes up successfully (accessible at &lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt;) and we can verify the service (image below). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzs4g7ypqip8h2zu3av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzs4g7ypqip8h2zu3av.png" alt="Service Verification" width="405" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However when we execute the second command, instead of a new container getting setup, we see an error message like below. This is because the host port 8085 is already occupied by the first 'tower' service and cannot be re-used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec7y4h8t57vfgfkx0meb.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec7y4h8t57vfgfkx0meb.JPG" alt="Docker run command execution error" width="800" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we realize that we cannot plainly apply the Part 1 article approach here for creating multiple containers for the same service and need to look for a different option.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Option B&lt;/strong&gt;: Execute multiple 'docker run' commands but with different host port&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Command 1&lt;/em&gt;: docker run -p 8085:8085 sb-tower-service:V1&lt;br&gt;
&lt;em&gt;Command 2&lt;/em&gt;: docker run -p &lt;strong&gt;8086&lt;/strong&gt;:8085 sb-tower-service:V1 (&lt;em&gt;notice the different port 8086&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;When the first command is executed, the 'tower' service comes up successfully (accessible at &lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt;) and we can verify the service (image below). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzs4g7ypqip8h2zu3av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzs4g7ypqip8h2zu3av.png" alt="Service Verification" width="405" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the second command is executed, the second 'tower' service also gets started successfully (accessible at &lt;a href="http://localhost:8086/tower" rel="noopener noreferrer"&gt;http://localhost:8086/tower&lt;/a&gt;) and we are able to verify the service (image below)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjn87hupiv870q28cf0ak.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjn87hupiv870q28cf0ak.JPG" alt="Service Verification" width="412" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, while we are able to have multiple instances of the 'tower' service running in Docker, we have a problem :) i.e. we have two URLs for the same service. This approach is not extensible (i.e. if we create 05 instances, we shall have 05 application URLs) and we cannot have tests written for multiple URLs every time except for specific scenarios like &lt;a href="https://en.wikipedia.org/wiki/A/B_testing" rel="noopener noreferrer"&gt;A/B testing&lt;/a&gt; but again, we would typically not do this kind of testing on local machine - &lt;em&gt;generally&lt;/em&gt;.&lt;/p&gt;



&lt;p&gt;Before moving to further options, we need to understand another tool called &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker Compose&lt;/strong&gt; is a docker utility that allows us to describe service(s) run configuration in an YML file (in addition to Dockerfile) and then &lt;em&gt;instead of using 'docker run' command to create a container&lt;/em&gt;, we can use command '&lt;strong&gt;docker compose up&lt;/strong&gt;' to execute the run configuration to create the container(s). Likewise we can use '&lt;strong&gt;docker compose down&lt;/strong&gt;' to dismantle the running containers that were created previously. One point to note is that Docker Compose does not create an image of a service automatically, so to use the 'tower' service in Docker Compose, we need to have an existing image (like we created in Part 1 already) named &lt;em&gt;sb-tower-service:V1&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The real benefit of Docker Compose is that we can execute a number of scenarios like creating multiple service instances, creating dependency between services etc. quite easily without going into multi node and network setups &lt;em&gt;explicitly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For a better understanding, let's experience the &lt;strong&gt;Option B&lt;/strong&gt; &lt;strong&gt;again&lt;/strong&gt; using docker compose tool. For the same, we need to create a docker-compose.yml file with the following content:&lt;/p&gt;

&lt;p&gt;docker-compose.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  tower-services: 
    image: sb-tower-service:V1
    ports:
    - "8085-8086:8085" 
    deploy:
      replicas : 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the docker compose content below-&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tower-services&lt;/em&gt; - this is the service name 'tower-services'&lt;br&gt;
&lt;em&gt;sb-tower-service:V1&lt;/em&gt; - this is the docker image name&lt;br&gt;
&lt;em&gt;8085-8086:8085&lt;/em&gt; - instructs Docker to use a host port range (8085-8086)&lt;br&gt;
&lt;em&gt;replicas : 2&lt;/em&gt; - instructs Docker to create 02 service instances&lt;/p&gt;

&lt;p&gt;Then, we execute the command &lt;strong&gt;docker compose up&lt;/strong&gt; and the 02 'tower' instances come up using port 8085 &amp;amp; 8086 (image below). The services are verifiable in the same way as done earlier in Option B.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntxyf4xm2xtmc79rrnfb.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntxyf4xm2xtmc79rrnfb.JPG" alt="Successful docker compose start" width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we can exit the docker compose by executing the command &lt;strong&gt;docker compose down&lt;/strong&gt; and the 'tower' instances are removed (image below).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqluvpeq4zzsaiflibntf.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqluvpeq4zzsaiflibntf.JPG" alt="Successful docker compose stop" width="800" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: Docker also creates/destroys a Docker network by the name 'tower_default' automatically for supporting network/communication&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Option C: Setup multiple service instances on same host port (using Docker Compose &amp;amp; front-proxy)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have understood Docker Compose, let's try to accomplish &lt;br&gt;
our objective using a simple arrangement. This arrangement would have an additional service, NGINX web server (open source) to front-end the client calls, while in the background it would send the client requests to multiple 'tower' service instances (using round robin allocation). This way, the client only gets a single application URL (e.g. &lt;a href="http://localhost:8000/tower" rel="noopener noreferrer"&gt;http://localhost:8000/tower&lt;/a&gt;) to access even though there are multiple service instances.&lt;/p&gt;

&lt;p&gt;The configuration for &lt;strong&gt;Option C&lt;/strong&gt; consists of additional artefacts-&lt;br&gt;
1) docker-compose.yml - provides the 'tower' &amp;amp; NGINX services info&lt;br&gt;
2) nginx.conf - NGINX server config file including association with the tower services/instances&lt;/p&gt;

&lt;p&gt;&lt;em&gt;docker-compose.yml&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  tower-services:
    image: sb-tower-service:V1
    expose:
      - '8085'
    deploy:
      replicas : 2

  nginx:
      image: nginx:latest
      volumes:
         - ./nginx.conf:/etc/nginx/nginx.conf:ro
      depends_on:
         - tower-services
      ports:
         - '8000:8000'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the docker compose content below-&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tower-services&lt;/em&gt; - this is the service name 'tower-services'&lt;br&gt;
&lt;em&gt;sb-tower-service:V1&lt;/em&gt; - this is the docker image name&lt;br&gt;
&lt;em&gt;expose 8085&lt;/em&gt; - instructs Docker to use the port 8085 in REST service&lt;br&gt;
&lt;em&gt;replicas : 2&lt;/em&gt; - instructs Docker to create 02 REST service instances&lt;br&gt;
&lt;em&gt;nginx _ - this is the NGINX web server &amp;amp; service name&lt;br&gt;
depends_on&lt;/em&gt; - instructs that the NGINX depends on 'tower-services'&lt;br&gt;
&lt;em&gt;volumes &amp;amp; nginx.conf&lt;/em&gt; - instructs a volume and NGINX config to refer&lt;br&gt;
&lt;em&gt;ports '8000:8000'&lt;/em&gt; - instructs docker to ensure port 8000 for NGINX&lt;/p&gt;

&lt;p&gt;&lt;em&gt;nginx.conf&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user  nginx;
events {
    worker_connections   10;
}
http {
        server {
              listen 8000;
              location / {
                proxy_pass http://tower-services:8085;
              }
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the nginx.conf content below-&lt;/p&gt;

&lt;p&gt;&lt;em&gt;worker_connections 10&lt;/em&gt; - instructs NGINX to use up to 10 worker connections when talking to the 'tower' instances&lt;br&gt;
&lt;em&gt;server listen 8000&lt;/em&gt; - instructs NGINX to use 8000 as the listening port; note the same port number is mapped in docker compose yml also&lt;br&gt;
&lt;em&gt;proxy_pass &lt;a href="http://tower-services:8085" rel="noopener noreferrer"&gt;http://tower-services:8085&lt;/a&gt;&lt;/em&gt; - instructs NGINX to pass on the requests received on port 8000 to the service 'tower-services' listening on port 8085.&lt;/p&gt;

&lt;p&gt;So now our expectation is that &lt;em&gt;when the above configurations are executed&lt;/em&gt;, the clients would should be able to hit the application URL &lt;a href="http://localhost:8000/tower" rel="noopener noreferrer"&gt;http://localhost:8000/tower&lt;/a&gt; (external URL) and this should internally talk to &lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt; (internal URL) however the internal URL will not be accessible to clients.&lt;/p&gt;

&lt;p&gt;Now it's time to let Docker to the above setup for us. When the command &lt;strong&gt;docker compose up&lt;/strong&gt; is executed, the NGINX server and the 02 'tower' service instances get successfully created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuk4y3z2sgtk0x1tduxh.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuk4y3z2sgtk0x1tduxh.JPG" alt="Successful docker compose including NGINX" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are also able to verify the service through a CURL command hitting &lt;a href="http://localhost:8000/tower" rel="noopener noreferrer"&gt;http://localhost:8000/tower&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyc3lnxfz5rt68kb80fw.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyc3lnxfz5rt68kb80fw.JPG" alt="Successful NGINX service verification" width="407" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also see the application log in one of the 'tower' service instances.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz58l4a7s3ko2ljqqdnf9.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz58l4a7s3ko2ljqqdnf9.JPG" alt="Service log" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally with this proving complete, we close the Docker compose service by running the command &lt;strong&gt;docker compose down&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdor5yb8fb9jgb3dl3wj.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdor5yb8fb9jgb3dl3wj.JPG" alt="Docker compose down" width="800" height="70"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;em&gt;Additional Note&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;1) Scaling possibility in &lt;strong&gt;Option C&lt;/strong&gt;: We can over-ride the replica count 2 mentioned in the docker compose yml by providing that value in the docker compose up command e.g. 'docker-compose up --scale tower-services=5'. This would end up creating 05 instances of the Spring Boot service instead of 2.&lt;/p&gt;

&lt;p&gt;2) Other aspects of &lt;strong&gt;Option C&lt;/strong&gt; for multi-instance service setup&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Benefits&lt;/em&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all configuration stays in docker compose yml &amp;amp; NGINX conf files&lt;/li&gt;
&lt;li&gt;possible to scale the no of instances above the 'replica' count mentioned in the docker compose yml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Drawbacks&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker compose file becomes more complicated with NGINX added (but still is one-time effort)&lt;/li&gt;
&lt;li&gt;cannot scale dynamically / additionally after services have started (e.g. cannot add more instances post start up)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We saw how multiple instances of same Spring Boot REST application can be setup using Docker to service client requests while utilizing additional services like NGINX. In the Part 3 of the article series, we shall try to cover additional use cases like hard dependencies between services (e.g. web dependent on database), start up sequencing (&amp;amp; known issues), more orchestration scenarios (like Docker Swarm or K8s) &amp;amp; more.&lt;/p&gt;

&lt;p&gt;Would appreciate any feedback for improvement on the approach and content.&lt;/p&gt;




&lt;p&gt;References:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nutrient.io/blog/how-to-use-docker-compose-to-run-multiple-instances-of-a-service-in-development/" rel="noopener noreferrer"&gt;https://www.nutrient.io/blog/how-to-use-docker-compose-to-run-multiple-instances-of-a-service-in-development/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/64819934/running-multiple-services-on-same-port-in-docker-compose" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/64819934/running-multiple-services-on-same-port-in-docker-compose&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>java</category>
      <category>docker</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Simplified: Spring Boot with Docker (Part 1)</title>
      <dc:creator>Shitij Bhatnagar</dc:creator>
      <pubDate>Tue, 11 Feb 2025 11:33:11 +0000</pubDate>
      <link>https://dev.to/shitij_bhatnagar_b6d1be72/spring-boot-befriends-docker-part-1-1kpf</link>
      <guid>https://dev.to/shitij_bhatnagar_b6d1be72/spring-boot-befriends-docker-part-1-1kpf</guid>
      <description>&lt;p&gt;&lt;strong&gt;Spring Boot&lt;/strong&gt; is a popular framework for building microservices in Java. Spring Boot helps make building REST services faster because of a lot of auto configurations and in-build dev tools. Even the simplest Spring Boot REST application comes with an inbuilt Tomcat server to host the service, which implies faster local testing and validation.&lt;/p&gt;

&lt;p&gt;However, sometimes, there is a need to test a &lt;strong&gt;containerized&lt;/strong&gt; (or multi-instance) arrangement for a REST service for specific use cases &lt;strong&gt;early in the development life cycle&lt;/strong&gt; e.g. before pushing the service to higher test environments. Also, while a test environment could support containerization (e.g. in PaaS), the &lt;strong&gt;same may not be easily possible on a developer’s machine&lt;/strong&gt; – whether for technical proving (single container, multiple containers) or for replicating more complicated scenarios on local (e.g. service talking to a real database that’s generally only possible in an integration test environment).&lt;/p&gt;

&lt;p&gt;That is where a &lt;strong&gt;containerization tool&lt;/strong&gt; can come handy to provide a similar experience to a developer, e.g. by allowing the REST service to run in a container on the developer machine, without the overhead of additional hardware, operating system etc. Once a REST service runs in a container platform locally, it is possible to load additional services (like other REST services or DB etc.) in additional containers like an ecosystem. &lt;strong&gt;Docker&lt;/strong&gt; is one of the containerization platforms that makes this possible.&lt;/p&gt;

&lt;p&gt;This article &lt;strong&gt;intents&lt;/strong&gt; to share a simple use case of running a Spring Boot REST service with Docker on a developer’s machine, which should be easily replicable. This is the &lt;strong&gt;Part 1&lt;/strong&gt; of this article series. Next parts shall bring in more details around multiple-container scenarios however to start with here, we shall cover a single container use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-requisites:&lt;/strong&gt;&lt;br&gt;
To experience the journey as outlined in the article, it is recommended to have the pre-requisites in place before getting on with the technical steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Reading: Basic awareness about Containerization &amp;amp; Docker&lt;/li&gt;
&lt;li&gt;  Tool: Installed Docker Desktop – contains multiple Docker services&lt;/li&gt;
&lt;li&gt;  Tool: Familiarity with Java (preferably 8 onwards), Spring Boot 3.4.x&lt;/li&gt;
&lt;li&gt;  Tool: Installed JDK 17 &amp;amp; Maven (build tool)&lt;/li&gt;
&lt;li&gt;  Hardware: Recommended to have at least 08 GB RAM (more the merrier)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create Service&lt;/strong&gt;&lt;br&gt;
We need to have a REST service that spans up fast and can be verified quickly. To do so, the first step is to create a new skeleton Spring boot service (using Spring Initializer) or as an even faster alternative, please download an already prepared Spring Boot REST service from &lt;a href="https://github.com/shitijbhatnagar/tower/tree/learner_1" rel="noopener noreferrer"&gt;https://github.com/shitijbhatnagar/tower/tree/learner_1&lt;/a&gt; (unusual name ‘tower’, I know).&lt;/p&gt;

&lt;p&gt;As we download and setup the code (e.g. using IntelliJ), let’s have a look at the main Rest Controller (TowerController.java) and note that all it does is to provide a happy path response to the HTTP GET call and log a message (by default goes to console).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q4doz22npsjbqadnwce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q4doz22npsjbqadnwce.png" alt="Rest Controller code" width="602" height="341"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Build and Execute Service (on local)&lt;/strong&gt;&lt;br&gt;
We need to build/compile the service first, using the command &lt;strong&gt;mvn clean install&lt;/strong&gt;. Once successfully through, the Spring Boot executable jar file should be created in the ‘target’ folder. The next step is to run the service using the command &lt;strong&gt;mvn spring-boot:run&lt;/strong&gt; post which, the REST service should start successfully and we should see an output as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5prlmdy4in61hk9ug7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5prlmdy4in61hk9ug7s.png" alt="Running service confirmation" width="602" height="93"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Verify Service (on local)&lt;/strong&gt; &lt;br&gt;
The REST service should be accessible (via curl/Postman/browser) by hitting the application URL &lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn28pqyho1xh7rf9t6r8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn28pqyho1xh7rf9t6r8u.png" alt="Successful service response" width="405" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, note that when the application URL is accessed, a new log message should be visible in the Spring boot application log (aka console) as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjw6c2vm4dmba823qtyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjw6c2vm4dmba823qtyt.png" alt="Successful service consumption log" width="602" height="12"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus far, we have setup the simple REST service and proven that it works on developer machine, so the next step is to focus on Docker. In preparation, let us create a new folder/directory (we can call it ‘&lt;strong&gt;tower&lt;/strong&gt;’ folder) and copy the Spring Boot executable jar file there. Additional files and artefacts will also be kept in the same folder for easy reference.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Prepare Dockerfile (for a Docker image)&lt;/strong&gt;&lt;br&gt;
A docker image is a blueprint (via metadata) of what component or service a Docker container will execute. This meta data is captured inside a file called the ‘Dockerfile’ and the docker image is later created using this Dockerfile. &lt;/p&gt;

&lt;p&gt;Below are the contents of a Dockerfile that mentions the specific JDK to be used, which port the service will be exposed on, the executable Spring Boot JAR (aka REST service) and how it will be executed inside the container. Please create/save the Dockerfile in the &lt;strong&gt;tower&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbckzqqc33tswnjo1pcai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbckzqqc33tswnjo1pcai.png" alt="Dockerfile" width="436" height="84"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Prepare Docker image&lt;/strong&gt;&lt;br&gt;
Once the Dockerfile is ready, it can be used to create a docker image using the following command on the command prompt:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker build -t sb-tower-service:V1 .&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let’s break down the command below&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;docker build -t&lt;/em&gt; is the command to build a new docker/container image with a name:tag&lt;br&gt;
&lt;em&gt;sb-tower-service&lt;/em&gt; is the name of the docker image&lt;br&gt;
&lt;em&gt;V1&lt;/em&gt; is the tag associated with the docker image&lt;/p&gt;

&lt;p&gt;Please ensure the &lt;strong&gt;Docker Desktop tool/service is running&lt;/strong&gt; prior to building a docker image, otherwise, the image creation will not succeed. Now, once the previous command is executed, an output similar to below should be seen on the command prompt/console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ww3qeess4hqhwbzxcjz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ww3qeess4hqhwbzxcjz.png" alt="Successful docker image build and publishing" width="602" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The newly created docker should also be visible in the Docker Desktop tool (Dashboard -&amp;gt; Images).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3m1we2y8qk79anfowtd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3m1we2y8qk79anfowtd.png" alt="Freshly built docker image visible in Docker Desktop" width="602" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An alternative method to check about docker image creation is to execute the command ‘docker images’ on command prompt and it should show the newly created image (i.e. ‘&lt;em&gt;sb-tower-service&lt;/em&gt;’) along with other images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy9vlwzp0oyru4yo8wt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy9vlwzp0oyru4yo8wt2.png" alt="Freshly built docker image visible in docker images listing" width="558" height="104"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Start Docker container&lt;/strong&gt;&lt;br&gt;
Once the docker image is created (built and available), it can be used to then create a container (that hosts a running instance of the ‘tower’ REST service). Execute the following command on the command prompt:&lt;br&gt;
&lt;strong&gt;docker run -p 8085:8085 sb-tower-service:V1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let’s break down the command below&lt;/em&gt;&lt;br&gt;
&lt;em&gt;docker run&lt;/em&gt; – this instructs docker to run a new container with an indicated image&lt;br&gt;
&lt;em&gt;-p&lt;/em&gt; indicates port mapping (host port to container port)&lt;br&gt;
&lt;em&gt;8085:8085&lt;/em&gt; - indicates that requests on the host port 8085 (where you can traffic) should be sent to the container’s port 8085 (where container is listening) for servicing&lt;br&gt;
&lt;em&gt;sb-tower-service:V1&lt;/em&gt; – this is the docker image with its tag that was created previously&lt;/p&gt;

&lt;p&gt;After executing the command, we should see an output similar to below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiduj0okzy2s9nht6zb3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiduj0okzy2s9nht6zb3u.png" alt="REST service executed inside docker container" width="602" height="296"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The container (provided a random name by Docker) should be visible in Docker Desktop also, similar to below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgk0n4x6ktqd265bo37n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgk0n4x6ktqd265bo37n.png" alt="Docker Desktop (Dashboard -&amp;gt; Containers) showing the container in running state" width="602" height="185"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Verify the REST service (running in Docker)&lt;/strong&gt;&lt;br&gt;
Try hitting the application URL again via CURL/Postman/browser (&lt;a href="http://localhost:8085/tower" rel="noopener noreferrer"&gt;http://localhost:8085/tower&lt;/a&gt;) and we should see an output like below; the only &lt;strong&gt;difference&lt;/strong&gt; is that this time, the service response is coming from the service instance &lt;strong&gt;inside Docker container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtlnx50kgz4yce3httbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtlnx50kgz4yce3httbr.png" alt="Successful service response (from Docker)" width="405" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, let’s observe the logs generated by the application by clicking on the container name (in Docker Desktop) and go to Logs tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi5m5fguhdbv2sri6xt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi5m5fguhdbv2sri6xt2.png" alt="Successful service consumption log (from Docker)" width="602" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We should be able to see the message “Tower Service is Up” in the container logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We saw how a simple Spring Boot REST service can be containerized using Docker on a developer desktop – no super heavy hardware requirements, no new equipment - with minimum setup. In the Part 2 of the article series, we shall try to cover additional use cases like multi-instances (of same service) and more.&lt;/p&gt;

&lt;p&gt;It is also worth nothing that this article focussed more on ‘running commands’ for a closer understanding/experience while there are plug-ins and utilities that can automate some of this.&lt;/p&gt;

&lt;p&gt;Would appreciate any feedback for improvement on the approach and content.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>springboot</category>
      <category>beginners</category>
      <category>java</category>
    </item>
  </channel>
</rss>
