<?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>Forem: Adriana Villela</title>
    <description>The latest articles on Forem by Adriana Villela (@avillela).</description>
    <link>https://forem.com/avillela</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%2F523889%2F23565746-afcd-4321-8b1f-3cce8af89a76.jpeg</url>
      <title>Forem: Adriana Villela</title>
      <link>https://forem.com/avillela</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/avillela"/>
    <language>en</language>
    <item>
      <title>The Humans of OpenTelemetry: KubeCon EU 2024</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 17 Jun 2024 04:00:00 +0000</pubDate>
      <link>https://forem.com/avillela/the-humans-of-opentelemetry-kubecon-eu-2024-1hm5</link>
      <guid>https://forem.com/avillela/the-humans-of-opentelemetry-kubecon-eu-2024-1hm5</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5c7h43fsskqarwll064.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5c7h43fsskqarwll064.png" alt="The Humans of OTel Presented by The OTel End User SIG" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re back with our second edition of &lt;a href="http://localhost:1313/blog/2023/humans-of-otel/"&gt;Humans of OpenTelemetry&lt;/a&gt;, this time from KubeCon EU in Paris. Once again, &lt;a href="https://github.com/reese-lee"&gt;Reese Lee&lt;/a&gt; and I interviewed OpenTelemetry contributors and end users, and learned how they got involved with OTel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/iris-dyrmishi-b15a9a164/"&gt;Iris Dyrmishi (Miro)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/svrnm"&gt;Severin Neumann (Cisco)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kaylareopelle"&gt;Kayla Reopelle (New Relic)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mtwo"&gt;Morgan McLean (Splunk)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/henrikrexed"&gt;Henrik Rexed (Dynatrace)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ccaraman"&gt;Vijay Samuel (eBay)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danielgblanco"&gt;Daniel Gomez Blanco (Skyscanner)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dodegaard"&gt;Doug Odegaard (ServiceNow)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/adnanrahic"&gt;Adnan Rahić (Tracetest)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/musingvirtual"&gt;Rynn Mancuso (Honeycomb)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, special thanks to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/reese-lee"&gt;Reese Lee&lt;/a&gt;, my co-interviewer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/henrikrexed"&gt;Henrik Rexed&lt;/a&gt; for providing the audio and video recording equipment, and doing the initial edits of the raw footage&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jiekun"&gt;Zhu Jiekun&lt;/a&gt; for assisting with his own camera&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can watch the full recording below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?si=qiC2KfKOBmIvj0WW&amp;amp;v=bsfMECwmsm0&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--seUeapB3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ytimg.com/vi/bsfMECwmsm0/maxresdefault.jpg" height="450" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?si=qiC2KfKOBmIvj0WW&amp;amp;v=bsfMECwmsm0&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          The Humans of OTel - KubeCon EU 2024 - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          We caught up with some of the contributors, maintainers, and practitioners of OpenTelemetry at the OpenTelemetry Observatory for KubeCon EU 2024 in Paris. Th...
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--jB1YP2J6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.youtube.com/s/desktop/a258f8cf/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Thanks to everyone who has contributed to OpenTelemetry to date, and we look forward to your continued contributions in 2024 and beyond! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Transcript&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If reading is more your thing, check out the transcript of our conversations below.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1- Meet the Humans of OTel&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;IRIS DYRMISHI:&lt;/strong&gt; Well, I’m Iris Dyrmishi. I’m a senior observability engineer at Miro and my life, my professional life is all about observability. I build an observability platform that provides the tools for engineering teams at Miro to monitor, to observe and get the best of their applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEVERIN NEUMANN:&lt;/strong&gt; My name is Severin Neumann. I’m working at Cisco at the open source program office and I’m a member of the OpenTelemetry governance committee and I’m one of the co-maintainers of the OpenTelemetry documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAYLA REOPELLE:&lt;/strong&gt; My name is Kayla Reopelle. I work for New Relic and I am contributing to the OpenTelemetry Ruby project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MORGAN MCLEAN:&lt;/strong&gt; My name is Morgan McLean,I’m a director of product management at Splunk.I’ve been with OpenTelemetry since day one. I’m one of the co-founders of the project. I’m on the governance committee. Wow. What do I work on within OTel?A bit of everything. I mean early on it was literally everything. Myself and Ted and various others were doing many, many jobs. More recently I was involved in the release of traces, metrics 1.0. Logs 1.0 last year. Right now I’m working on profiling as well as OpenTelemetry’s expansion into mainframe computing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HENRIK REXED:&lt;/strong&gt; My name is Henrik Rexed. I am a cloud native advocate at Dynatrace and I’m passionate about observability, performance, and I’m trying to help the community by providing content on getting started on any solutions out there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VIJAY SAMUEL:&lt;/strong&gt; My name is Vijay Samuel and I help do architecture for the observability platform at eBay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DANIEL GOMEZ BLANCO:&lt;/strong&gt; I’m Daniel Gomez Blanco. I’m a principal engineer at Skyscanner and also member of the OpenTelemetry governance committee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOUG ODEGAARD:&lt;/strong&gt; My name is Doug Odegaard. I’m a senior solutions architect with ServiceNow Cloud Observability, which is also formerly Lightstep. I’m also a previous customer of using OpenTelemetry for several years prior to that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADNAN RAHIĆ:&lt;/strong&gt; Hey, I am Adnan. I work at Tracetest as a developer advocate which is…you can guess better than me what that is. Pretty much do a bunch of everything regarding OpenTelemetry. I’m one of the contributors for the documentation, for the blog, and the demo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RYNN MANCUSO:&lt;/strong&gt; My name is Rynn Mancuso. I work for Honeycomb.io and I am one of the maintainers of the End User SIG.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2- What does observability mean to you?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;IRIS DYRMISHI:&lt;/strong&gt; What does observability mean to me? observability to me is the biggest passion of my life and also my professional career. It is one of those areas that you are not very interested when you start your career because you don’t know anything about it. It’s not taught in school, it’s not preached by the tech communities a lot, but then you discover it and say, “Wow, this is amazing!” We’re actually making a change and we’re helping the teams make the best of their product. So yeah, that’s all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEVERIN NEUMANN:&lt;/strong&gt; I think observability is a big game changer, right? So it’s evolution from what we have done, especially APM, over the last few years. So I worked for a very long time at AppDynamics and we sold APM agents to customers and we gave them a lot of the things that observability is promising today as well. But the big change I see with observability that it’s coming down, let’s say to everybody, right? So this is making the things that we did there available for everybody. And even more, we’re moving away from this… Hey, let’s add a post compilation agent into your application to like, yeah, let’s make native observability. Let’s make this a thing that developers, that operation teams are using across all the organizations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAYLA REOPELLE:&lt;/strong&gt; So to me observability means having peace of mind. It means having something that you can rely on in order to see what happened and what went wrong. I think observability is also a way to feel more technically connected to your customers and your users, so that you can see the ways that they’re interacting with your software instead of just the ways that you might interact with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MORGAN MCLEAN:&lt;/strong&gt; I mean, observability to me transcends just the computing industry. It’s the ability to peer into something and understand how it works, what it’s doing right now, and thus if it breaks, how to fix it more quickly. Certainly when we think about telemetry in this industry, what observability classically has meant is visibility to backend infrastructure and applications kind of excitingly, I think it’s expanding right now, right? With OpenTelemetry we’re pushing into client applications, we’re pushing into mainframes, as I mentioned earlier. And so it’s really visibility into any systems that impact your business, any technical system observability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HENRIK REXED:&lt;/strong&gt; Usually when people mention of observability they say it’s a replacement of the old name monitoring. But in fact for me it’s more than monitoring, because monitoring is like, you just look at something and observability is like having enough information to understand a given situation. So if you just look at metrics then, okay, you have a guess that something is going on, but you don’t understand. So having the options to get more information like logs, events, exceptions, traces, compiling, then at the end combine all those dimensions together, then you say, okay, I got it, this is my problem and I can resolve it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VIJAY SAMUEL:&lt;/strong&gt; What does observability mean to me? I belong to what is called the site engineering organization inside of eBay, and our goal is to make sure that we can observe everything that’s going on in the site and ensure that we have high availability. So basically, observability means knowing if the site is running fine or not, because that’s why I’m there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DANIEL GOMEZ BLANCO:&lt;/strong&gt; What does observability mean to me? It’s a way for us to understand what’s happening within our systems, because we run quite a complex system, so we need to understand what goes on inside of them so we can deliver a good experience for our end users at the end of the day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOUG ODEGAARD:&lt;/strong&gt; So observability is, to me, I’ve been a full stack developer for years, and so as we observe…actually I ended up on an incident response team doing tracking of incidents, but also trying to figure out what was wrong. And it pointed out to me how much we need this, how hard it was to look at so many different screens and so forth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADNAN RAHIĆ:&lt;/strong&gt; So observability is, to me, I’ve been a full stack developer for years, and so as we observe…actually I ended up on an incident response team doing tracking of incidents, but also trying to figure out what was wrong. And it pointed out to me how much we need this, how hard it was to look at so many different screens and so forth.observability for me is the way to actually see what’s happening in your system. It’s the pinnacle of not being up the whole night trying to figure out what went wrong. And with OpenTelemetry and with the rise of tracing the last couple of years, it has hit an all time high with regards to the possibilities that we have right now. So I’m just really, really happy to be part of the project. I’m also really happy that it’s growing at that pace, that it’s growing right now, and I can’t see how that’s going to evolve within the next couple of years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RYNN MANCUSO:&lt;/strong&gt; For me, observability is about being able to ask deeper questions of our systems, being able to demand, I think more than just alerting on things that are emergencies, things we’ve seen before, but actually being able to go out into the unknown and understand how complex systems are performing.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3- What does OpenTelemetry mean to you?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;IRIS DYRMISHI:&lt;/strong&gt; OpenTelemetry is the tool that is making observability great again. I would say that observability is seeing the surge, now that OpenTelemetry is becoming so popular, it’s allowing centralization of telemetry signals, it’s allowing semantic conventions, and it’s generally helping observability teams and engineering teams take more attention to the observability and building it and making it better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEVERIN NEUMANN:&lt;/strong&gt; What does OpenTelemetry mean to me? I think it’s the vehicle for observability. It’s enabling that. And I joined OpenTelemetry community a few years back because I was curious about this idea to bring observability to everybody. And I think we are doing a really good job. And what it also means to me now is that it’s an amazing community. Right? So we’re at KubeCon here, and I meet so many people I just know from those conversations, and now I can talk to them in person. And we talk a lot about OpenTelemetry, but we also talk a lot about other things than OpenTelemetry. We talk about observability, of course, about what we think about is going to happen in a few years and all those other things, and that’s what OpenTelemetry means to me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAYLA REOPELLE:&lt;/strong&gt; So OpenTelemetry to me seems like it’s a community effort to take the best of what’s already been out there for instrumentation and collect it in one group so that everyone can benefit from it. I think that we’ve learned so much as different agent engineers, but there’s also so much to learn from users of the products themselves. And OpenTelemetry does a great job of bringing both people who are, you know, experts in observability, and experts in languages to make something that’s really great and meaningful for everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MORGAN MCLEAN:&lt;/strong&gt; I mean, OpenTelemetry is my baby. Put so much effort into creating this project. What does it mean to me? I mean, there’s the boring answer, which is it extracts signals: metrics, traces, logs, profiles, everything else from your infrastructure, from your services, from your clients, makes those observable, processable on the backend. But I think to a lot of us who’ve been in this community so long, and a lot of us like yourself and Henrik here and others who participate in the community so much, I mean, OTel is also just a really nice open source community to participate in. It’s a thing I just enjoy working on. I know that’s abstract and kind of like a sort of squishy thing to say, but I don’t know. OTel has a lot of meaning to me in many different ways. All very positive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HENRIK REXED:&lt;/strong&gt; OpenTelemetry for me, means the future. Because at the end, by having an open standard, we have the luxury to have a common standard for common format, for all the solution of the market and having that common format for all the industry and all the vendors and all the solutions, it will just open use cases. I think testing used to rely on, I don’t know, feedback from users. And now with observability data, we could be so much efficient in the way we’re testing, we could be so much efficient in replacing marketing tools, business analytics tools. I think it’s the future. And one thing that also a lot of people talk about, AI everywhere, machine learning, blah, blah, blah, but I think it’s the same thing as a Tesla. I mean, Tesla, when you drive your car, it takes decisions based on the sensors that it measures. And if you don’t have those sensors and those measurements, then you cannot have a smart… you can have the smartest systems, but without the data, you cannot take the right decisions. I think it’s an enabler also for the future implementations of modern applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VIJAY SAMUEL:&lt;/strong&gt; OpenTelemetry is the standard for observability going forward, and it’s very important because as we have gone through the journey of observability over the past few years, we have had to hunt for open standards in Prometheus and few others. Now, at least with ingestion and collection, it’s a single standard for everyone to adopt. And I think that’s pretty powerful for the long run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DANIEL GOMEZ BLANCO:&lt;/strong&gt; What does OpenTelemetry mean to me? That, I think is bringing people together, bringing everyone together under one single language and the ones that way of thinking about telemetry. I think human languages are difficult enough for us to understand each other. And I think, you know, OpenTelemetry is bringing the technology together and one single way of like, thinking about telemetry, thinking about how we observe our systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOUG ODEGAARD:&lt;/strong&gt; To me, OpenTelemetry is bringing the ability to have product teams, infrastructure teams, helping their jobs make it easier and also just improve the customer experience and just make it overall a better experience to do our jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADNAN RAHIĆ:&lt;/strong&gt; OpenTelemetry is the, I’m going to say, the future of observability. We’ve seen so many companies, many vendors move to an OpenTelemetry-first mindset, and the way that you can use OpenTelemetry to generate them, to actually gather all telemetry signals with one set of libraries, with one tool. It’s just the way it was supposed to be. You’re not locked into one tool, one vendor, one cloud provider anymore. You can do basically whatever you want, and you can use both the metrics, logs, and traces for basically anything you want to do. Really happy to see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RYNN MANCUSO:&lt;/strong&gt; OpenTelemetry is an instrumentation protocol that helps us ask more detailed questions about observability because it collects multiple signals from many flexible types of systems. Folks monitor everything from the control plane in Kubernetes all the way up to physical on-prem systems. It’s a really flexible language and it’s beautiful community of humans that came together over the pandemic to build something really special.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4- How did you get involved with OpenTelemetry?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;IRIS DYRMISHI:&lt;/strong&gt; I was working in a very fast-pacing observability team, and we were maintaining a lot of tools and we really did not have conventions there, we did not have centralizations and we really were not flexible when it came to backends and vendor agnostic in general. So we discovered this amazing tool called OpenTelemetry. We said okay, let’s give it a try. It worked great for us. And here I am today, one year later, more than one year later, and let’s say pushing the migration to OpenTelemetry in my second project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEVERIN NEUMANN:&lt;/strong&gt; How did I get involved into OpenTelemetry? So yeah, I mentioned that… so I got curious a few years ago. So I was… I was at AppDynamics working as a so-called domain architect, and I was an expert for Node.js, Python and a lot of those other languages. And there was always this conversation around like, hey, there’s this thing now called OpenTelemetry and should we not integrate this into our product? And I was like, okay, I want to learn more. Then I was like, what is a good way to learn something new about an open source technology? Yeah, get involved into that. So I was involved in JavaScript at some point, and then at some point I realized like, yeah, but if I really want to get a good view into OpenTelemetry, doing documentation is a good way into that. And that’s how I ended up being a maintainer for the documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAYLA REOPELLE:&lt;/strong&gt; I got involved in OpenTelemetry last spring when New Relic asked me to take a look at what the current status was of the OpenTelemetry Ruby project. I also work as an engineer on the New Relic Ruby agent team, and that gave me an opportunity to start to contribute to the project. And I noticed that a lot of the signals for Ruby weren’t yet stable. So a lot of my work so far has been going into trying to bring logs and metrics to stability in Ruby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MORGAN MCLEAN:&lt;/strong&gt; I was working at Google on Google’s observability products like tracing, profiling, debugging, that sort of thing. And one of the challenges we had in tracing was getting data from people’s applications. It’s really, really hard. You need integrations of hundreds of thousands of pieces of software. No one team, no one company is going to maintain that. It’s just infeasible. And so we want to do something open source. There were other open source standards. There was one that had started, I think, roughly around the same time we were doing this, called OpenTracing. We started OpenCensus.&lt;/p&gt;

&lt;p&gt;At some point, especially amongst the more social media savvy members, the team, which I am not one of, there was some contention between those projects about where people who maintain databases and language runtime things should actually spend their integration efforts, and it was limiting the success of both projects. So I was leading OpenCensus. Ted and Dan and others were leading OpenTracing. And in late 2018, early 2019, we finally sort of brought things to a head and decided to merge those into what is now called OpenTelemetry. So that’s sort of, you know, I’ve been involved since then, I’ve been…now I work at Splunk. Different company, but still on the same types of things. But that’s how my involvement started, and it’s just grown and grown and snowballed from there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HENRIK REXED:&lt;/strong&gt; When I started the adventure in observability, of course, I joined Dynatrace, and Dynatrace has their vendor agent, the OneAgent, and I saw this movement of OpenTelemetry, and coming from the performance background, I looked at it and I said, “Whoa, an open standard.” “That sounds quite exciting” because I had a performance, a gig for a customer, where I implemented like a collecting logs and processing it and putting machine learning. And I told myself at that time, it would be so wonderful to have one common standard. So then instead of doing a custom implementation, I could have something for everyone. And when I looked at the, just the definition of the project and the things behind the project, I was so excited. I said, oh, gosh, I want to be involved in the project. And that’s where I started to build content to help the community get started.&lt;/p&gt;

&lt;p&gt;I used to be a developer, but I’m a bad developer for sure. So that’s why I’m trying to help the project in other ways, in all the directions. And yeah, my goal is increase the adoption of the open standards, making sure that it’s been adopted everywhere, so then we can move forward by implementing even more exciting implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VIJAY SAMUEL:&lt;/strong&gt; I started a few years ago for two reasons. One, we were looking to introduce tracing inside the company, and at that time, OpenTracing and OpenCensus was converging into OpenTelemetry. We started evaluating OpenTelemetry for that. And given that we were moving into OpenTelemetry for tracing, I also went through the journey of migrating our metrics collection into OpenTelemetry. That’s basically how I got involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DANIEL GOMEZ BLANCO:&lt;/strong&gt; How did I get involved in OpenTelemetry? I got involved through my work at Skyscanner, as an end user. I was driving adoption and open standards for telemetry. During COVID there was a need for simplification and how we approach infrastructure, how we approach, how we collect, how we process, and how we export telemetry data, and also basically… to basically lead the adoption of open standards and their simplification effort. So as an observability lead, I got more involved in the community aspect of OpenTelemetry, decided to interact with all their end users and meeting people that want to solve the same problems and want to find a solution that works for everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOUG ODEGAARD:&lt;/strong&gt; So, OpenTelemetry, I actually, for several years, in my previous position, I was hired to actually develop observability software. I was writing my own thing, we were doing a lot of alert management and various things. It was so much work and I thought, this has got to be easier. Plus I wanted to make sure that it could be future, future proof, dare I use that term? But also extensible.&lt;/p&gt;

&lt;p&gt;And when I discovered OpenTelemetry, I was just like, oh, thank you. Because it’s something that the company could carry forward. And also we didn’t have to worry about storing the data as much. And so it’s really provided a really excellent platform so that we can focus on the task at hand versus how to do the job. So how I got involved in the project was actually first as a customer. It was about three, close to four years ago, kind of the infancy of OpenTelemetry. And I would go online, I would look at the documentation, or I would be in the code a lot, but I wanted to learn more. So I would go to a SIG call and there would be someone from Google and Microsoft and other companies, and then there was this guy from this small fintech in the US. And at first it was a little awkward, but they were so excited to have me in the call because I was an end user. And so it really was, it was a wonderful experience to begin that way, to realize that I could contribute to this rather than simply be a consumer of it. So it was great. And then I transitioned my career into working for a vendor, and we implement these systems now for customers like myself that I was years ago. So it’s kind of a pay it forward, give back type of thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADNAN RAHIĆ:&lt;/strong&gt; How did we get involved into the OpenTelemetry project? We started contributing more to the blog with you guys started contributing a bit to the docs as well. And yeah, it’s just been a whole-hearted effort in the team to always kind of dedicate a few, a few minutes of each day to check out the OpenTelemetry project, find a way to contribute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RYNN MANCUSO:&lt;/strong&gt; I got involved in the OpenTelemetry project…honestly, I was working at one observability company in marketing, and they didn’t see the point. They didn’t want me to get involved. And I really believed in open source. I’d worked in Mozilla and Wikimedia and really believed that, like, this was the way forward from a strategic perspective. So the second I could switch to a company that did let me get involved, that’s what I did. And now I’m at Honeycomb. And I’m glad to say within the first three months, I made project member and started working with the End User Working Group and worked to grow it into a SIG, into all the programs that it has today, together with others.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;5- What’s your favorite telemetry signal?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;IRIS DYRMISHI:&lt;/strong&gt; Tracing is my favorite signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEVERIN NEUMANN:&lt;/strong&gt; My favorite signal now is profiling, because I think this is really closing a big gap that was missing in observability, right? So I mentioned before, right, I come from the APM space, and now for me, APM, observability, it’s very hard to make, like, a difference here. But one thing that when I talk with people using APM products right now is they’re like, hey, where’s code level visibility with OpenTelemetry, right? My commercial agent is giving me that line of code that is breaking something. And this is what we get with profiling. And that’s why I’m really, really excited about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAYLA REOPELLE:&lt;/strong&gt; To decide a favorite signal is kind of difficult for me. I really love the power of traces. I think that traces can tell stories in ways that are very meaningful. But on the same, like, on the other hand, I’ve been so immersed in logs and trying to allow logs to have more connections to spans and traces, I definitely have a soft spot for logs as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MORGAN MCLEAN:&lt;/strong&gt; I mean, I’m partial to distributed traces because that’s where this project got its start. And I think early on, that’s where a lot of the value was. No one else was really doing standardized distributed trace collection right? There were some open source examples of it attached to, like, Zipkin and Jaeger. But I think the reason OpenTelemetry got so much traction so quickly is that it was providing that.&lt;/p&gt;

&lt;p&gt;I’m also partial to logs, which we launched last year, just because that’s one where, like, I’ve been involved in a lot of parts of OTel… But that’s one where like, I was involved in a lot of the core specification early on in driving that. And so it was really exciting to see that ship. Also, logs are just a thing that throughout my career before working on any of this, I just get frustrated with, because they’re never standardized, slow to process, they’re expensive. OTel going to bring a lot of changes there for the better for everyone who uses logs.&lt;/p&gt;

&lt;p&gt;And finally, I guess profiles, because I work on that now. When I was at Google many years ago, I launched what I think was the world’s first distributed continuous profiling product, at least publicly available one, which was Google cloud profiling, Stackdriver profiling, they still support it, I still think it’s free, it’s very powerful. But profiling has always been a bit of a niche thing. Like, I know, like at Splunk and other companies, we support it, but it’s not as well known as metrics, and traces, and logs. I think with OTel, starting later this year, we’re gonna launch like full support for profiles. That’s really gonna change. Like, we had customers at Google who would spend an hour of our profiler and save like 20, 30% of their aggregate compute because they found some really poorly optimized code really quickly. For more people to have that ability and speed things up and for developers actually to get insight into how things work, that’s super exciting. Like, the tech has been there a long time and OTel bringing this mainstream is huge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HENRIK REXED:&lt;/strong&gt; When people ask me, who is your favorite kid? Usually I say, I don’t have a favorite kid, you know. All my kids are wonderful. They all have, I don’t know, a great thing, you know, out of it. So I think I love traces because sometimes it helps you to understand where it slowed down. I love metrics because as a performance engineer, I used to use metrics a lot. And I love logs because logs at the end, there’s no sampling. So if you just do analytics on logs, wow, you are so much precise.&lt;/p&gt;

&lt;p&gt;So I don’t think I have a favorite signal. I’ll just say that depending on what I need and pick and choose, there’s clearly one signal that will help me more. There’s one thing that I’m very eager and waiting since Valencia is continuous profiling, because I love profiling and I think traces is great, but if there is a problem somewhere, profiling would be so much helpful. So I think, yeah, I don’t answer your questions, but I say, yeah, I love all the signals provided by OpenTelemetry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VIJAY SAMUEL:&lt;/strong&gt; I am thoroughly biased towards metrics. I feel metrics are the most powerful signal. As long as you are thinking through your instrumentation and making sure that you have the right granularity cardinality being sent in, to the platform, you can do powerful, powerful things with regards to anomaly detection, machine learning and many other things. So I love metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DANIEL GOMEZ BLANCO:&lt;/strong&gt; I mean, I have to say traces, because they give you the context. Traces give you the backbone correlation for all the other signals, right? But I do think that the current design of the API design of metrics is so powerful that I’m like falling in love again with metrics because of that way that we decouple instrumentation and measurement from aggregation of metrics is so powerful and so much richness to basically give us a way to describe our systems, that I’m falling back again in love with metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DOUG ODEGAARD:&lt;/strong&gt; My favorite signal, I have to say, I’m partial to traces because I’ve been doing software development for so long that that was the first thing that really turned me on to it was the ability to see that, especially because I know what it’s like, like to debug. But it’s also, I also know what it’s like in an incident to have to focus in very quickly. So yes, traces are my favorite, but I do also like to send that trace ID and span ID into the logs now. It’s kind of becoming my next favorite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADNAN RAHIĆ:&lt;/strong&gt; My favorite signal is traces. I’m going to say traces, definitely. My favorite singer is Ed Sheeran.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RYNN MANCUSO:&lt;/strong&gt; What is my favorite signal? I mean, I work for Honeycomb, so I am constitutionally obliged to say traces are my favorite signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Join us!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you have a story to share about how you use OpenTelemetry at your organization, we’d love to hear from you! Ways to share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join the &lt;a href="https://cloud-native.slack.com/archives/C01RT3MSWGZ"&gt;#otel-sig-end-user channel&lt;/a&gt; on the &lt;a href="https://communityinviter.com/apps/cloud-native/cncf"&gt;CNCF Community Slack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join our &lt;a href="http://localhost:1313/community/end-user/otel-in-practice/"&gt;OTel in Practice&lt;/a&gt; sessions&lt;/li&gt;
&lt;li&gt;Share your stories on the &lt;a href="http://localhost:1313/docs/contributing/blog/"&gt;OpenTelemetry blog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contact us on the &lt;a href="https://communityinviter.com/apps/cloud-native/cncf"&gt;CNCF Community Slack&lt;/a&gt; for any other types of sessions you’d like to see!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to follow OpenTelemetry on &lt;a href="https://fosstodon.org/@opentelemetry"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/opentelemetry/"&gt;LinkedIn&lt;/a&gt;, and share your stories using the &lt;strong&gt;#OpenTelemetry&lt;/strong&gt; hashtag!&lt;/p&gt;

&lt;p&gt;And don’t forget to subscribe to our &lt;a href="https://youtube.com/@otel-official"&gt;YouTube channel&lt;/a&gt; for more great OpenTelemetry content!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Demystifying Observability 2.0</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 10 Jun 2024 19:21:13 +0000</pubDate>
      <link>https://forem.com/avillela/demystifying-observability-20-3j18</link>
      <guid>https://forem.com/avillela/demystifying-observability-20-3j18</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gcovu8kqnoabwgbu9bj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gcovu8kqnoabwgbu9bj.png" alt="Staircase in the Sagrada Famiglia, Barcelona, Spain. Photo by Adriana Villela." width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our systems have gotten complex. Like &lt;em&gt;really&lt;/em&gt; complex. Organizations have mostly shifted from monoliths to microservices. They’ve embraced the Cloud, and with it, Kubernetes (PS: happy 10th b-day to Kubernetes!) and all sorts of other cloud native tools that help run the things that we’ve grown accustomed to having in our tech-dependent lives: access to government services, social media, airline booking, shopping, streaming services, and so on.&lt;/p&gt;

&lt;p&gt;As our systems get more and more complex, engineers need a way to understand them when things go 💩, so that services can be restored in a timely manner.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://adri-v.medium.com/list/unpacking-observability-be1835c6dd23"&gt;Observability&lt;/a&gt;, which helps with just that. Observability has been around for a while now, and it’s been really exciting to see so many organizations embarking on their respective observability journeys.&lt;/p&gt;

&lt;p&gt;Now, if you’ve been following the interwebs, you may have heard some rumblings about &lt;strong&gt;&lt;em&gt;Observability 2.0&lt;/em&gt;&lt;/strong&gt;. Cool. But what is it &lt;em&gt;really&lt;/em&gt;, and how does it differ from Observability 1.0? Well, you’ve come to the right place. Sit back, relax, and let me take you on a journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Defining Observability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we get into Observability 1.0 vs 2.0, let’s start with a definition of Observability, also known as o11y to us folks who sometimes get lazy and don’t want to write out the whole word. 🙃 (For the uninitiated: o11y == the 11 letters between “o” and “y” in “Observability”.)&lt;/p&gt;

&lt;p&gt;The “classic” definition of Observability comes from &lt;a href="https://en.wikipedia.org/wiki/Observability"&gt;control theory&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Observability is a measure of how well internal states of a system can be inferred from knowledge of its external outputs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;– Rudolf E. Kálmán&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://hazelweakly.me/blog/redefining-observability/#observability:-control-theory"&gt;This definition&lt;/a&gt; was popularized by &lt;a href="https://x.com/mipsytipsy"&gt;Charity Majors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I love this definition, and I’ve used it for many years, including in my very &lt;a href="https://storiesfromtheherd.com/unpacking-observability-a-beginners-guide-833258a0591f"&gt;first blog post on Observability&lt;/a&gt;, and more recently, in my &lt;a href="https://learning.oreilly.com/videos/fundamentals-of-observability/0636920926597/"&gt;O’Reilly Observability video course&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That being said, there’s a refinement to the definition of Observability that I’ve been embracing of late, which was coined by my good friend, &lt;a href="https://www.linkedin.com/in/hazelweakly/"&gt;Hazel Weakly&lt;/a&gt;, who has an &lt;a href="https://hazelweakly.me/blog/redefining-observability"&gt;amazing blog post on redefining Observability&lt;/a&gt;. (Hazel is also incredibly smart and super astute and you should totally &lt;a href="https://www.linkedin.com/in/hazelweakly/"&gt;follow her on LinkedIn&lt;/a&gt;):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Observability is the process through which one develops the ability to ask meaningful questions, get useful answers, and act effectively on what you learn.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;– Hazel Weakly&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s so simple, and so elegant, and I love it. Also, it applies to both Observability 1.0 and 2.0, and does not hold us back from continuing to refine Observability.&lt;/p&gt;

&lt;p&gt;Okay, now that we’ve gotten the basics out of the way, let’s tackle this 1.0 vs. 2.0 business.&lt;/p&gt;

&lt;p&gt;I set out to write this piece because I’ve found myself talking a lot about Observability 2.0 recently, including last week on &lt;a href="https://www.youtube.com/live/WiFoSr54kjM?si=5edzpyd33RKcp2te"&gt;Whitney Lee’s Enlightning show&lt;/a&gt;, and in an &lt;a href="https://www.linkedin.com/posts/adrianavillela_observability-otel-bouldering-activity-7204876506731335680-ACL2"&gt;upcoming episode of The Cloud Gambit&lt;/a&gt;. After all this talking about it, I wanted a place to jot down my thoughts, and to also share them with y’all. I honestly thought it would be a straight regurgitation of what I’d already said. But then I asked Hazel to look over this piece, and her feedback encouraged me to think about this further, thereby refining some of my understanding and thoughts around this. Which is awesome, because it’s so fitting, given that I’m talking about the evolution of our understanding of Observability!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwrijtv9mgxziolznpqo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwrijtv9mgxziolznpqo.jpg" alt="Summary of Observability 1.0 vs 2.0 from my appearance on Enlightning with Whitney Lee." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Observability 1.0&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When Observability burst onto the scene, it was still a very APM-dominated world. Many APM vendors, sensing that Observability was becoming an Actual Thing, pivoted to Observability. This pivot, however, was mostly in name only, in much the same way that many organizations pivoted from Ops to DevOps (or SRE or Platform Engineering) in name only. New name, but business as usual. And perhaps we can’t blame them for that. These are paradigm shifts and paradigm shifts are often hard to swallow. You’ve gotta start somewhere, and maybe a name change is as good a place as any.&lt;/p&gt;

&lt;p&gt;So, time for the big reveal…&lt;strong&gt;&lt;em&gt;Observability 1.0 is APM.&lt;/em&gt;&lt;/strong&gt; But more specifically, what is Observability 1.0? Observability 1.0 is focused on:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1- Yow you operate your code&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This means that it’s more of an Ops concern, and not so much of an Everyone concern.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2- Known unknowns&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Also known as “predicable shit happens”. We know the usual things that go wrong with our systems, and &lt;a href="https://faun.pub/observability-mythbusters-observability-anti-patterns-2b3062405b54"&gt;we put dashboards in place to represent all of things that we know can go wrong with our systems&lt;/a&gt; (and for which we know the fixes), so that we can keep an eye on things if they go sideways.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3- Multiple sources of truth&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;These “sources of truth” are &lt;a href="https://medium.com/dzerolabs/observability-journey-understanding-logs-events-traces-and-spans-836524d63172"&gt;traces, metrics, and logs&lt;/a&gt;, also known as “The Three Pillars”. I actually hate that term, because it implies that these things are siloed from one another (more on that later). I much prefer the term “signal”. A signal is anything that gives you data.&lt;/p&gt;

&lt;p&gt;I suppose that the whole Three Pillars thing kind of makes sense for Observability 1.0, where traces, metrics, and logs were often not correlated. This is especially true since, in the early days of Observability, we didn’t really have a common language for even talking about these signals. Each vendor had their own standard, and that may or may not have included a way to correlate the three signals.&lt;/p&gt;

&lt;p&gt;I also want to add that there was much more of an emphasis on logs and metrics, because that’s just something that developers and operators are familiar with. Traces have been around, but were not very widely used.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Observability 2.0&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So now that we know what Observability 1.0 is all about, let’s look at how it differs from Observability 2.0.&lt;/p&gt;

&lt;p&gt;First things first. Credit where credit is due. The term “Observability 2.0” was coined by &lt;a href="https://x.com/mipsytipsy"&gt;Charity Majors&lt;/a&gt;. Observability 2.0 is the acknowledgment that Observability, like all things tech and non-tech, continues to evolve. The evolution to Observability 2.0 is the recognition that we made a decent stab at Observability (i.e. 1.0), but unfortunately, it didn’t really fulfill the promise of the definition of Observability that we saw earlier on. No problem, because things are constantly evolving.&lt;/p&gt;

&lt;p&gt;So what makes Observability 2.0 different from 1.0? It has the following characteristics:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1- It’s focused not only on how you operate your code, but also on how you develop your code&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This means that Observability is part of the &lt;a href="https://en.wikipedia.org/wiki/Systems_development_life_cycle"&gt;systems development lifecycle (SDLC)&lt;/a&gt;, and is therefore a concern of developers, QAs, and SREs. How?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://youtube.com/shorts/GIF61VCIqQI?feature=share"&gt;Developers instrument their code&lt;/a&gt;&lt;/strong&gt; so that they can troubleshoot it during development. 🤯 Instrumentation is the process of adding code to software to generate telemetry signals for Observability purposes. Software engineers already rely on logging for troubleshooting (hello, “print” statements?), so why not add traces and metrics into the mix?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quality Assurance (QA) analysts leverage instrumented code during testing&lt;/strong&gt;. When they encounter a bug, QAs can use telemetry data to enable them to troubleshoot code and file more detailed bug reports to developers. Or, if they’re unable to troubleshoot the code with the telemetry provided, it means that the system has not been sufficiently instrumented. Again, they go back to developers with that information so that developers can add more instrumentation to the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QAs further take advantage of instrumented code by &lt;a href="https://thenewstack.io/trace-based-testing-the-next-step-in-observability/"&gt;creating trace-based tests&lt;/a&gt; (TBT) for integration testing.&lt;/strong&gt; In a nutshell, TBT leverages traces to create integration tests. For anyone interested in seeing TBT in action, the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/test"&gt;OpenTelemetry Demo leverages TBT&lt;/a&gt; using the opens source version of &lt;a href="https://tracetest.io/"&gt;Tracetest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SREs leverage instrumented code to create &lt;a href="https://thenewstack.io/translating-failures-into-service-level-objectives/"&gt;service-level objectives (SLOs)&lt;/a&gt;&lt;/strong&gt;. SLOs help us answer the question, “What is the reliability goal of this service?” SLO are based on &lt;a href="https://youtu.be/Mgzt4bq0JU4?si=lXigHRUPmoiMtd7Q"&gt;Service Level Indicators&lt;/a&gt; (SLIs), which are themselves based on metrics. Metrics that were instrumented by your developer! 🤯 SREs can create alerts based on these SLOs, so that when an SLO is breached, they’re notified right away. Furthermore, since the SLO is ultimatley tied a metric (via an SLI), which was correlated to a trace (more on signal correlation shortly), the SRE knows where to start looking when an issue arises in production.&lt;/p&gt;

&lt;p&gt;**&lt;a href="https://thenewstack.io/how-to-observe-your-ci-cd-pipelines-with-opentelemetry/"&gt;CI/CD pipelines are instrumented&lt;/a&gt;. **CI/CD pipelines are the backbone of modern SDLC. They are responsible for packaging and delivering code to production in a timely manner. When they fail, we can’t get code into production, which means angry users. Nobody likes angry useres. Ever. Therefore, having observable CI/CD pipelines allows us to address pipeline failures in a more timely manner to help alleviate software delivery bottlenecks.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2- It’s focused on unknown unknowns&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Also known as “unpredictable shit happens”. Let’s face it, you can’t know every problem that there’s ever going to be. This is especially true in the world of microservices, where services interact with each other in such weird and unpredictable ways because…well, we users tend to use systems in very weird and unpredictable ways! 🤯 &lt;a href="https://faun.pub/observability-mythbusters-observability-anti-patterns-2b3062405b54"&gt;Traditional dashboards can’t save you&lt;/a&gt;, but &lt;a href="https://youtu.be/5maHQiElGgY?si=FF7ZO8QN6ivnt_BZ"&gt;SLO-based alerts can&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3- It’s focused on a single source of truth: events&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Wait…what? What about traces, metrics, and logs? Well, traces, metrics, and logs all types of &lt;em&gt;events&lt;/em&gt;. An event is information about a thing that happened. They are structured (think JSON-like), and timestamped. &lt;strong&gt;&lt;em&gt;Traces, metrics, and logs are therefore different types of events that serve different and important purposes, each contributing to the Observability story.&lt;/em&gt;&lt;/strong&gt; Furthermore, they’re all &lt;em&gt;correlated&lt;/em&gt;. Instead of Three Pillars, they’re more like &lt;a href="https://thenewstack.io/modern-observability-is-a-single-braid-of-data/"&gt;the three strands that make up a braid&lt;/a&gt; (shoutout to my teammate &lt;a href="https://twitter.com/tedsuo"&gt;Ted Young&lt;/a&gt; for this analogy).&lt;/p&gt;

&lt;p&gt;In addition, we now have a common standard for defining and correlating traces, metrics, and logs: &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt;. Most Observability vendors are all in on OpenTelemetry, which means that it has become the de-facto standard for instrumenting code (and also the second most popular CNCF project in terms of contributions 🎉). It also means that these vendors all ingest the same data, and it’s up to how those vendors render the data that differentiates them from one other.&lt;/p&gt;

&lt;p&gt;I also want to add that in this Observability story, we place traces front and center, since they help give us that end-to-end picture of what happens when someone does a thing to a system, with metrics and logs serving as supporting actors which add useful details to that picture. And of course, everything correlated.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Final thoughts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Observability has come a long way from its early days, and Observability 2.0 is the acknowledgement that Observability is evolving, and most importantly, that we’re getting closer and closer to fulfilling the promise of Observability itself.&lt;/p&gt;

&lt;p&gt;I can’t wait to see what the future has in store!&lt;/p&gt;

&lt;p&gt;Now, please enjoy this photo of my rat Katie, enjoying some hangtime in the pocket of my husband’s bathrobe. 💜&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13owuaumaef944skwdd4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13owuaumaef944skwdd4.png" alt="Katie rat enjoying some pocket hangtime. Photo by Adriana Villela" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until next time, peace, love, and code. ✌️💜👩‍💻&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>cloudnative</category>
      <category>apm</category>
    </item>
    <item>
      <title>Things You Might Not Have Known About the OpenTelemetry Operator</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Thu, 25 Apr 2024 04:00:00 +0000</pubDate>
      <link>https://forem.com/avillela/opentelemetry-operator-qa-1hmh</link>
      <guid>https://forem.com/avillela/opentelemetry-operator-qa-1hmh</guid>
      <description>&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-operator"&gt;OpenTelemetry (OTel) Operator&lt;/a&gt; is a &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/"&gt;Kubernetes Operator&lt;/a&gt; that manages OTel for you in your Kubernetes cluster to make life a little easier. It does the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages deployment of the &lt;a href="http://localhost:1313/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt;, supported by the &lt;code&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-operator?tab=readme-ov-file#getting-started"&gt;OpenTelemetryCollector&lt;/a&gt;&lt;/code&gt; &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/"&gt;custom resource (CR)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Manages the configuration of a fleet of OpenTelemetry Collectors via &lt;a href="https://opentelemetry.io/docs/specs/opamp/"&gt;OpAMP&lt;/a&gt; integration, supported by the &lt;code&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/docs/api.md#opampbridge"&gt;OpAMPBridge&lt;/a&gt;&lt;/code&gt; custom resource.&lt;/li&gt;
&lt;li&gt;Provides
&lt;a href="https://github.com/open-telemetry/opentelemetry-operator/tree/main/cmd/otel-allocator"&gt;integration with the Prometheus Operator's &lt;code&gt;PodMonitor&lt;/code&gt; and &lt;code&gt;ServiceMonitor&lt;/code&gt; CRs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Injects and configures &lt;a href="https://www.honeycomb.io/blog/what-is-auto-instrumentation"&gt;auto-instrumentation&lt;/a&gt; into your pods, supported by the &lt;code&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-operator?tab=readme-ov-file#opentelemetry-auto-instrumentation-injection"&gt;Instrumentation&lt;/a&gt;&lt;/code&gt; custom resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I first used the Operator last year when I used &lt;a href="https://kratix.io"&gt;Kratix&lt;/a&gt; to &lt;a href="https://www.syntasso.io/post/guest-blog-lightstep-and-kratix"&gt;demonstrate how a platform team can make the OTel Operator available to developers, so that they can self-provision a pre-configured OTel Collector in their Kubernetes clusters&lt;/a&gt;. More recently, I’ve had a chance to get to know the Operator a bit better, especially while putting together my &lt;a href="https://learning.oreilly.com/course/fundamentals-of-observability/0636920926597/"&gt;O’Reilly video course on Observability with OpenTelemetry&lt;/a&gt;, and in the recent &lt;a href="https://youtu.be/LJd1pJ0k28g?si=y0vv0EhFll606S1E"&gt;KubeCon EU talk that I did with Reese Lee&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve learned some really cool things, and I thought it might be helpful to share some little OTel Operator goodies that I’ve picked up along the way, in the form of a Q&amp;amp;A.&lt;/p&gt;

&lt;p&gt;Please note that this post assumes that you have some familiarity with OpenTelemetry, the &lt;a href="http://localhost:1313/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt;, the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator"&gt;OpenTelemetry Operator&lt;/a&gt; (including the &lt;a href="https://adri-v.medium.com/prometheus-opentelemetry-better-together-41dc637f2292"&gt;Target Allocator&lt;/a&gt;), and &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Q&amp;amp;A
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Q1: Does the Operator support multiple Collector configuration sources?
&lt;/h4&gt;

&lt;p&gt;Short answer: no.&lt;/p&gt;

&lt;p&gt;Longer answer: OTel Collector can be fed more than one Collector config YAML file. That way, you can keep your base configurations in, say, &lt;code&gt;otelcol-config.yaml&lt;/code&gt;, and overrides or additional configurations of the base can go in, for example, &lt;code&gt;otelcol-config-extras.yaml&lt;/code&gt;. See an example of this in the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/06f020c97f78ae9625d3a4a5d1107c55045c567f/docker-compose.yml#L665-L668"&gt;OTel Demo’s Docker compose file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, while the OTel Collector supports multiple Collector configuration files, the Collector managed by the OTel Operator does not.&lt;/p&gt;

&lt;p&gt;To get around this, you could merge the multiple Collector configs through some external tool beforehand. For example, if you &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-operator"&gt;were deploying the Operator via Helm&lt;/a&gt;, you could technically &lt;a href="https://stackoverflow.com/a/56653384"&gt;pass it multiple Collector config files using multiple --values flags&lt;/a&gt; and let &lt;a href="https://helm.sh"&gt;Helm&lt;/a&gt; do the merging for you.&lt;/p&gt;

&lt;p&gt;For reference, &lt;a href="https://cloud-native.slack.com/archives/C033BJ8BASU/p1709321896612279"&gt;check out this thread in the #otel-operator CNCF Slack channel&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q2: How can I securely reference access tokens in the Collector’s configuration?
&lt;/h4&gt;

&lt;p&gt;In order to send OpenTelemetry data to an Observability backend, you must define at least one &lt;a href="https://opentelemetry.io/docs/collector/configuration/#exporters"&gt;exporter&lt;/a&gt;. Whether you use &lt;a href="https://opentelemetry.io/docs/specs/otel/protocol/"&gt;OTLP&lt;/a&gt; or &lt;a href="https://opentelemetry.io/docs/specs/otel/protocol/"&gt;some proprietary vendor format&lt;/a&gt;, most exporters typically require that you specify an endpoint and an access token when sending data to a vendor backend.&lt;/p&gt;

&lt;p&gt;When using the OpenTelemetry Operator to manage the OTel Collector, the OTel Collector config YAML is defined in the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator?tab=readme-ov-file#getting-started"&gt;OpenTelemetryCollector&lt;/a&gt; CR. This file should be version-controlled and therefore shouldn’t contain any sensitive data, including access tokens stored as plain text.&lt;/p&gt;

&lt;p&gt;Fortunately, the &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; CR gives us a way to reference that value as a secret. Here’s how you do it:&lt;/p&gt;

&lt;p&gt;1- Create a Kubernetes secret for your access token. Remember to &lt;a href="https://www.base64encode.org/"&gt;base-64 encode&lt;/a&gt; the secret.&lt;/p&gt;

&lt;p&gt;2- &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/#using-a-secret"&gt;Expose the secret as an environment variable&lt;/a&gt; by adding it to the &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; CR’s &lt;code&gt;&lt;a href="https://github.com/avillela/otel-target-allocator-talk/blob/21e9643e28165e39bd79f3beec7f2b1f989d87e9/src/resources/02-otel-collector-ls.yml#L16-L21"&gt;env section&lt;/a&gt;&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LS_TOKEN&lt;/span&gt;
      &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LS_TOKEN&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;otel-collector-secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Reference the &lt;em&gt;environment variable&lt;/em&gt; in your &lt;a href="https://github.com/avillela/otel-target-allocator-talk/blob/21e9643e28165e39bd79f3beec7f2b1f989d87e9/src/resources/02-otel-collector-ls.yml#L43-L47"&gt;exporter definition&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;otlp/ls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ingest.lightstep.com:443"&lt;/span&gt;
     &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lightstep-access-token"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${LS_TOKEN}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more info, check out my full example &lt;a href="https://github.com/avillela/otel-target-allocator-talk/blob/main/src/resources/02-otel-collector-ls.yml"&gt;here&lt;/a&gt;, along with full instructions &lt;a href="https://github.com/avillela/otel-target-allocator-talk/tree/main?tab=readme-ov-file#3b--kubernetes-deployment-servicenow-cloud-observability-backend"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q3: Is the Operator version at parity with the Collector version?
&lt;/h4&gt;

&lt;p&gt;For every Collector release, there is an Operator release which provides support for that Collector version. For example, at the time of this writing, the latest Operator version is &lt;code&gt;0.98.0&lt;/code&gt;. Thus, the the default image of the Collector used by the Operator is version &lt;code&gt;0.98.0&lt;/code&gt; of the &lt;a href="https://dev.to/blog/2024/otel-collector-anti-patterns/#3--not-using-the-right-collector-distribution-or-not-building-your-own-distribution"&gt;core distribution&lt;/a&gt; (as opposed to the contrib distribution).&lt;/p&gt;

&lt;h4&gt;
  
  
  Q4: Can I override the base OTel Collector image?
&lt;/h4&gt;

&lt;p&gt;Yes!&lt;/p&gt;

&lt;p&gt;As we saw earlier, the &lt;a href="https://github.cm/open-telemetry/open-telemetry-collector"&gt;core distribution&lt;/a&gt; is the default Collector distribution used by the &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; CR. The Core distribution is a bare-bones distribution of the Collector for OTel developers to develop and test. It contains a base set of components–i.e. &lt;a href="https://opentelemetry.io/docs/collector/configuration/#service-extensions"&gt;extensions&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/configuration/#connectors"&gt;connectors&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/configuration/#receivers"&gt;receivers&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/configuration/#processors"&gt;processors&lt;/a&gt;, and &lt;a href="https://opentelemetry.io/docs/collector/configuration/#exporters"&gt;exporters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want access to more components than the ones offered by core, you can use the Collector's &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-k8s"&gt;Kubernetes Distribution&lt;/a&gt; instead. This distribution is made specifically to be used in a Kubernetes cluster to monitor Kubernetes and services running in Kubernetes. It contains a subset of components from&lt;br&gt;
&lt;a href="https://github.com/open-telemetry/opentelemetry-collector"&gt;OpenTelemetry Collector Core&lt;/a&gt; and &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib"&gt;OpenTelemetry Collector Contrib&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to use specific Collector components, you can build your own distribution using the &lt;a href="https://opentelemetry.io/docs/collector/custom-collector/"&gt;OpenTelemetry Collector Builder&lt;/a&gt; (OCB), and include only the components that you need.&lt;/p&gt;

&lt;p&gt;Either way, the OpenTelemetryCollector CR allows you to override the default Collector image with one that better suits your needs by &lt;code&gt;adding spec.image&lt;/code&gt; to your &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; YAML. In addition, you can also specify the number of Collector replicas you wish to spin up, by adding &lt;code&gt;spec.replicas&lt;/code&gt; (this is totally independent of whether or not you override the Collector image). Your code would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenTelemetryCollector&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;otelcol&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mynamespace&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;statefulset&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;my_collector_image&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;number_of_replicas&amp;gt;&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;my_collector_image&amp;gt;&lt;/code&gt; is the name of a valid Collector image from a container repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;number_of_replicas&amp;gt;&lt;/code&gt; is the number of pod instances for the underlying OpenTelemetry Collector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that if you're pulling a Collector image from a private container registry, you'll need to use &lt;code&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/"&gt;imagePullSecrets&lt;/a&gt;&lt;/code&gt;. Since private container registries require authentication, this will enable you to authenticate against that private registry. For more info on how to use &lt;code&gt;imagePullSecrets&lt;/code&gt; for your Collector image, check out the instructions &lt;a href="https://github.com/open-telemetry/opentelemetry-operator?tab=readme-ov-file#using-imagepullsecrets"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more info, check out the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/docs/api.md#opentelemetrycollector"&gt;OpenTelemetryCollector CR API docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q5: Does the Target Allocator work for all deployment types?
&lt;/h4&gt;

&lt;p&gt;No. The Target Allocator only works for &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/"&gt;StatefulSet&lt;/a&gt;, and &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet&lt;/a&gt; (&lt;a href="https://github.com/open-telemetry/opentelemetry-operator/pull/2430#discussion_r1420495631"&gt;newly-introduced&lt;/a&gt;). More info &lt;a href="https://github.com/open-telemetry/opentelemetry-operator/blob/aed905c2c3c0aa3fb608a79c2e4d0e7b73dff980/apis/v1beta1/collector_webhook.go#L328"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q6: If I’m using Operator’s Target Allocator for Prometheus service discovery, do I need &lt;code&gt;PodMonitor&lt;/code&gt; and &lt;code&gt;ServiceMonitor&lt;/code&gt; CRs installed in my Kubernetes cluster?
&lt;/h4&gt;

&lt;p&gt;Yes, you do. These CRs are bundled with the &lt;a href="https://github.com/prometheus-operator/prometheus-operator"&gt;Prometheus Operator&lt;/a&gt;; however, they can be installed standalone, which means that you don’t need to install the Prometheus Operator just to use these two CRs with the Target Allocator.&lt;/p&gt;

&lt;p&gt;The easiest way to install the &lt;code&gt;&lt;a href="https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.PodMonitor"&gt;PodMonitor&lt;/a&gt;&lt;/code&gt; and &lt;code&gt;&lt;a href="https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.ServiceMonitor"&gt;ServiceMonitor&lt;/a&gt;&lt;/code&gt; CRs is to grab a copy of the individual &lt;a href="https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack/charts/crds/crds/crd-podmonitors.yaml"&gt;PodMonitor YAML&lt;/a&gt; and &lt;a href="https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack/charts/crds/crds/crd-servicemonitors.yaml"&gt;ServiceMonitor YAML&lt;/a&gt; &lt;a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/"&gt;custom resource definitions (CRDs)&lt;/a&gt;, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.71.2/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml

kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.71.2/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out my example of the OpenTelemetry Operator’s Target Allocator with &lt;code&gt;ServiceMonitor&lt;/code&gt; &lt;a href="https://github.com/avillela/otel-target-allocator-talk/tree/main?tab=readme-ov-file#3b--kubernetes-deployment-servicenow-cloud-observability-backend"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q7: Do I need to create a service account to use the Target Allocator?
&lt;/h4&gt;

&lt;p&gt;No, but you do need to do a bit of extra work. So, here’s the deal…although you need a &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/"&gt;service account&lt;/a&gt; to use the Target Allocator, you don’t have to create your own.&lt;/p&gt;

&lt;p&gt;If you enable the Target Allocator and don’t create a service account, one is automagically created for you. This service account’s default name is a concatenation of the Collector name (&lt;code&gt;metadata.name&lt;/code&gt; in the &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; CR) and &lt;code&gt;-collector&lt;/code&gt;. For example, if your Collector is called &lt;code&gt;mycollector&lt;/code&gt;, then your service account would be called &lt;code&gt;mycollector-collector&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By default, this service account has no defined policy, you’ll still need to create your own &lt;code&gt;&lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole"&gt;ClusterRole&lt;/a&gt;&lt;/code&gt; and &lt;code&gt;&lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding"&gt;ClusterRoleBinding&lt;/a&gt;&lt;/code&gt;, and associate the &lt;code&gt;ClusterRole&lt;/code&gt; to the &lt;code&gt;ServiceAccount&lt;/code&gt; via &lt;code&gt;ClusterRoleBinding&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator/tree/main/cmd/otel-allocator#rbac"&gt;Target Allocator readme&lt;/a&gt; for more on Target Allocator RBAC configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q8: Can I override the Target Allocator base image?
&lt;/h4&gt;

&lt;p&gt;Just like you can override the Collector base image (as we saw in Q4), you can also override the Target Allocator’s base image.&lt;/p&gt;

&lt;p&gt;Please keep in mind that &lt;a href="https://cloud-native.slack.com/archives/C033BJ8BASU/p1709128862949249?thread_ts=1709081221.484429&amp;amp;cid=C033BJ8BASU"&gt;it’s usually best to keep the Target Allocator and OTel operator versions the same&lt;/a&gt;, to avoid any compatibility issues. If you do choose to override the Target Allocator’s base image, you can do so by adding &lt;code&gt;spec.targetAllocator.image&lt;/code&gt; in the &lt;code&gt;OpenTelemetryCollector&lt;/code&gt; CR. You can also specify the number of replicas by adding &lt;code&gt;spec.targetAllocator.replicas&lt;/code&gt; (this is totally independent of whether or not you override the TA image). Your code would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenTelemetryCollector&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;otelcol&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mynamespace&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;statefulset&lt;/span&gt;
  &lt;span class="na"&gt;targetAllocator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;ta_image_name&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;number_of_replicas&amp;gt;&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;ta_image_name&amp;gt;&lt;/code&gt; is a valid Target Allocator image from a container repository.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;number_of_replicas&amp;gt;&lt;/code&gt; is the number of pod instances for the underlying Target Allocator&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Q9: If it’s not recommended that you override the Target Allocator base image, then why would you want to?
&lt;/h4&gt;

&lt;p&gt;One use case might be &lt;a href="https://cloud-native.slack.com/archives/C033BJ8BASU/p1713894678225579"&gt;if you need to host a mirror of the Target Allocator image in your own private container registry for security purposes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you do need to reference a Target Allocator image from a private registry, you’ll need to use &lt;code&gt;imagePullSecrets&lt;/code&gt;. To use &lt;code&gt;imagePullSecrets&lt;/code&gt; with the OTel Operator, check out the instructions &lt;a href="https://github.com/open-telemetry/opentelemetry-operator?tab=readme-ov-file#using-imagepullsecrets"&gt;here&lt;/a&gt;. Note that you don’t need to create a &lt;code&gt;serviceAccount&lt;/code&gt; for the Target Allocator, since once is already created for you automagically if you don’t create one yourself (see Q6).&lt;/p&gt;

&lt;p&gt;For more info, check out the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/docs/api.md#opentelemetrycollectorspectargetallocator"&gt;Target Allocator API docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Q10: Is there a version lag between the OTel Operator auto-instrumentation and auto-instrumentation of supported languages?
&lt;/h4&gt;

&lt;p&gt;If there is a lag, it's minimal, as maintainers try to keep these up to date for each release cycle. Keep in mind that there are breaking changes in some semconvs and the team is trying to avoid breaking users' code. More info &lt;a href="https://cloud-native.slack.com/archives/C033BJ8BASU/p1713894678225579"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Hopefully this has helped to demystify the OTel Operator a bit more. There’s definitely a lot going on, and the OTel Operator can certainly be a bit scary at first, but understanding some of the basics will get you well on your way to mastering this powerful tool.&lt;/p&gt;

&lt;p&gt;If you have any questions about the OTel Operator, I highly recommend that you post questions on the &lt;a href="https://cloud-native.slack.com/archives/C033BJ8BASU"&gt;#otel-operator&lt;/a&gt; channel on the &lt;a href="https://communityinviter.com/apps/cloud-native/cncf"&gt;CNCF Slack&lt;/a&gt;. Maintainers and contributors are super friendly, and have always been more than willing to answer my questions! You can also &lt;a href="https://bento.me/adrianamvillela"&gt;hit me up&lt;/a&gt;, and I'll try my best to answer your questions, or to direct you to folks who have the answers!&lt;/p&gt;

&lt;p&gt;And now I'll leave you with a photo of my rat, Katie Jr.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7v2xjg15q41cgjdw117.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7v2xjg15q41cgjdw117.png" alt="Brown fancy rat being held by her human friend. Photo by Adriana Villela" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until next time, peace, love, and code. ☮️❤️👩‍💻&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>softwareengineering</category>
      <category>observability</category>
      <category>oteloperator</category>
    </item>
    <item>
      <title>OpenTelemetry Collector Anti-Patterns</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 26 Feb 2024 20:17:34 +0000</pubDate>
      <link>https://forem.com/avillela/opentelemetry-collector-anti-patterns-42be</link>
      <guid>https://forem.com/avillela/opentelemetry-collector-anti-patterns-42be</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2A0tjDJ-D89kqdweBuCRbsiA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2A0tjDJ-D89kqdweBuCRbsiA.jpeg" alt="House on stilts against ocean and mountain backdrop. Photo by Adriana Villela"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/docs/collector"&gt;OpenTelemetry Collector&lt;/a&gt; is one of my favorite OpenTelemetry (OTel) components. It’s a flexible and powerful data pipeline which allows you to ingest OTel data from one or more sources, transform it (including batching, filtering, and masking), and export it to one or more observability backends for analysis. It’s vendor-neutral. It’s extensible, meaning that you can create your own custom components for it. What’s there not to like?&lt;/p&gt;

&lt;p&gt;Unfortunately, as it happens with many tools out there, it is also very easy to fall into some bad habits. Today, I will dig into five OpenTelemetry Collector antipatterns, and how to avoid them. Let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Antipatterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1- Improper use of Collector deployment modes
&lt;/h3&gt;

&lt;p&gt;It’s not just enough to use a Collector. It’s also about &lt;em&gt;how&lt;/em&gt; your Collectors are deployed within your organization. That’s right - Collector*s*, plural. Because one is often not enough.&lt;/p&gt;

&lt;p&gt;There are two deployment modes for Collectors: agent mode and gateway mode, and both are needed.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/docs/collector/deployment/agent/"&gt;agent mode&lt;/a&gt;, the Collector sits next to the application or on the same host as the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F0%2AlrTSayzbpvzZFGK9" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F0%2AlrTSayzbpvzZFGK9" alt="OTel Collector Agent Mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/docs/collector/deployment/gateway/"&gt;gateway mode&lt;/a&gt;, telemetry data is sent to a load balancer, which then determines how to distribute the load amongst a pool of Collectors. Because you have a pool of Collectors, should one Collector in that pool fail, one of the other Collectors in the pool can take over. This keeps data flowing to your destination sans disruptions. Gateway mode is commonly deployed per cluster, data center, or region.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F0%2A1giiBuQUPQpb2WIm" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F0%2A1giiBuQUPQpb2WIm" alt="OTel Collector Agent Mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So which should you use? Both agent and gateway.&lt;/p&gt;

&lt;p&gt;If you’re collecting telemetry data for your application, place a Collector agent alongside your application. If you’re collecting data for infrastructure, place a Collector agent alongside your infrastructure. Whatever you do, don’t collect telemetry for all of your infrastructure and applications using a single Collector. That way, if one Collector fails, the rest of your telemetry collection is unaffected.&lt;/p&gt;

&lt;p&gt;The telemetry from your Collector agents can then be sent to a Collector gateway. Because the gateway sits behind a load balancer, you don’t have a single point of failure for exporting telemetry data, typically to your observability backend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bottom line:&lt;/em&gt; Having the right Collector deployment configuration to send data to your observability backend ensures higher availability of your telemetry collection infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  2- Not monitoring your Collectors
&lt;/h3&gt;

&lt;p&gt;Deploying multiple Collector agents and a Collector gateway is great, but it’s not good enough. Wouldn’t it be nice to know when one of your Collectors is malfunctioning, or when data is being dropped? That way, you can take action before things start to escalate. This is where monitoring your Collectors can be very useful.&lt;/p&gt;

&lt;p&gt;But how does one monitor a Collector? The OTel Collector already emits &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/monitoring.md" rel="noopener noreferrer"&gt;metrics for the purposes of its own monitoring&lt;/a&gt;. These can then be sent to your Observability backend for monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  3- Not using the right Collector Distribution (or not building your own distribution)
&lt;/h3&gt;

&lt;p&gt;There are two official distributions of the OpenTelemetry Collector: &lt;a href="https://github.com/open-telemetry/opentelemetry-collector" rel="noopener noreferrer"&gt;Core&lt;/a&gt;, and &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib" rel="noopener noreferrer"&gt;Contrib&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Core distribution is a bare-bones distribution of the Collector for OTel developers to develop and test. It contains a base set of &lt;a href="https://dev.to/docs/collector/configuration/#service-extensions"&gt;extensions&lt;/a&gt;, &lt;a href="https://dev.to/docs/collector/configuration/#connectors"&gt;connectors&lt;/a&gt;, &lt;a href="https://dev.to/docs/collector/configuration/#receivers"&gt;receivers&lt;/a&gt;, &lt;a href="https://dev.to/docs/collector/configuration/#processors"&gt;processors&lt;/a&gt;, and&lt;br&gt;
&lt;a href="https://dev.to/docs/collector/configuration/#exporters"&gt;exporters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Contrib distribution is for non-OTel developers to experiment and learn. It also extends the Core distribution, and includes components created by third-parties (including vendors and individual community members), that are useful to the OpenTelemetry community at large.&lt;/p&gt;

&lt;p&gt;Neither Core nor Contrib alone are meant to be part of your production workload. Using just Core by itself is too bare-bones and wouldn’t suit an organization’s needs. (Though its components are absolutely needed!) And although many OpenTelemetry practitioners, deploy Contrib in their respective organizations, it has many components, and you likely won’t need every single exporter, receiver, processor, connector, and extension. That would be overkill, and your Collector instance ends up needlessly bloated, potentially increasing the attack surface.&lt;/p&gt;

&lt;p&gt;But how do you pick and choose the components that you need? The answer is to build your own distribution, and you can do that using a tool called the &lt;a href="https://dev.to/docs/collector/custom-collector/"&gt;OpenTelemetry Collector Builder&lt;/a&gt; (OCB). In addition, at some point, you may need to create your own custom Collector component, such as a processor or exporter. The OCB allows you to integrate your custom components AND pick and choose the Contrib components that you need.&lt;/p&gt;

&lt;p&gt;It is also worth mentioning that some vendors build their own&lt;br&gt;
&lt;a href="https://dev.to/ecosystem/distributions/"&gt;Collector distributions&lt;/a&gt;. These are OTel Collector distributions that are curated to Collector components that are specific to that vendor. They may be a combination of custom, vendor-developed components, and curated Collector Contrib components. Using vendor-specific distributions ensures that you are using just the Collector components that you need, again reducing overall bloat.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bottom line:&lt;/em&gt; Using the right distribution reduces bloat and allows you to include only the Collector components that you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  4- Not updating your Collectors
&lt;/h3&gt;

&lt;p&gt;This one’s short and sweet. Keeping software up-to-date is important, and the Collector is no different! By regularly updating the Collector, it allows you to stay up-to-date with the latest version so that you can take advantage of new features, bug fixes, performance improvements, and security fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5- Not using the OpenTelemetry Collector where appropriate
&lt;/h3&gt;

&lt;p&gt;OpenTelemetry allows you to send telemetry signals from your application to an observability backend in one of two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/docs/collector/deployment/no-collector/"&gt;Directly from the application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/docs/collector/"&gt;Via the OpenTelemetry Collector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sending telemetry data “direct from application” for non-production systems is all well and good if you’re getting started with OpenTelemetry, but it is neither suited nor recommended to use this approach for production systems. Instead, the &lt;a href="https://dev.to/docs/collector/#when-to-use-a-collector"&gt;OpenTelemetry docs recommend using the OpenTelemetry Collector&lt;/a&gt;. How come?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/docs/collector/#when-to-use-a-collector"&gt;Per the OTel Docs&lt;/a&gt;, the Collector “allows your service to offload data quickly and the collector can take care of additional handling like retries, batching, encryption or even sensitive data filtering.”&lt;/p&gt;

&lt;p&gt;Check out some additional Collector benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Collectors can enhance the quality of the telemetry emitted by an application while also minimizing costs.&lt;/strong&gt; For example: sampling spans to reduce costs, enriching telemetry with extra metadata, and generating new telemetry, such as metrics derived from spans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a Collector to ingest telemetry data makes it easy to change to a new backend or export the data in a different format.&lt;/strong&gt; If we want to change how telemetry is being processed or exported, that change happens in one place (the Collector!), as opposed to making the same change for multiple applications in your organization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collectors allow you to receive data of various formats and translate to the desired format for export.&lt;/strong&gt; This can be very handy when transitioning from some other telemetry solution to OTel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collectors allow you to ingest non-application telemetry.&lt;/strong&gt; This includes logs and non-app metrics from infrastructure like Azure, Prometheus, and Cloudwatch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said, there are some use-cases where folks don't want or can't use a Collector. For instance, when collecting data at the edge from IOT devices, it might be better to send data directly to their observability backend instead of a local Collector, given that resources on that edge might be limited.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bottom line:&lt;/em&gt; As a general rule, using the OpenTelemetry Collector gives you additional flexibility for managing your telemetry data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The OpenTelemetry Collector is a powerful and flexible tool for ingesting, manipulating, and exporting OpenTelemetry data. By using it to its full potential and by avoiding these five pitfalls, your organization can be well on its way towards achieving observability greatness.&lt;/p&gt;

&lt;p&gt;Now, please enjoy this lovely photo of my rats Buffy, Mookie, and Katie.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2AuQMUc8vjHDaamdzrEMgHoA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2AuQMUc8vjHDaamdzrEMgHoA.png" alt="3 fancy rats peering out of a red house. Photo by Ian Maxwell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until next time, peace, love, and code! ☮️❤️👩‍💻&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opensource</category>
      <category>opentelemetry</category>
      <category>sitereliabilityengineering</category>
    </item>
    <item>
      <title>Tracetest in Action: Running Trace-Based Tests on the OpenTelemetry Demo App with Nomad</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Tue, 28 Feb 2023 15:00:00 +0000</pubDate>
      <link>https://forem.com/lightstep/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad-5dg0</link>
      <guid>https://forem.com/lightstep/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad-5dg0</guid>
      <description>&lt;p&gt;The concept of Trace-based testing (TBT) was first presented by my awesome colleague, &lt;a href="https://twitter.com/tedsuo" rel="noopener noreferrer"&gt;Ted Young&lt;/a&gt;, at &lt;a href="https://www.youtube.com/watch?v=NU-fTr-udZg" rel="noopener noreferrer"&gt;KubeCon North America 2018&lt;/a&gt;. The idea behind it was simple, yet elegant: if you’re already creating distributed &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/" rel="noopener noreferrer"&gt;Traces&lt;/a&gt; in your application code, why not use this same Trace data to create test assertions to validate your end-to-end system flow?&lt;/p&gt;

&lt;p&gt;Back in 2018, Trace-based testing was just an idea. Fast-forward to today: TBT is now a reality, thanks to Trace standardization à la &lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTel) and Trace-based testing tools like &lt;a href="https://tracetest.io" rel="noopener noreferrer"&gt;Tracetest&lt;/a&gt;, &lt;a href="https://gethelios.dev" rel="noopener noreferrer"&gt;Helios&lt;/a&gt;, and &lt;a href="https://github.com/aspecto-io/malabi" rel="noopener noreferrer"&gt;Malabi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I first heard about TBT in the summer of 2022, when I was writing a piece on how &lt;a href="https://lightstep.com/blog/observability-mythbusters-not-only-for-sre" rel="noopener noreferrer"&gt;Observability is useful for QAs&lt;/a&gt;. After writing the piece, I wanted to get my hands dirty and try this Trace-based testing thing for myself. The tool that I chose for my initial TBT explorations was Tracetest, which is open-source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracetest Overview
&lt;/h2&gt;

&lt;p&gt;Tracetest came out in May 2022, and I first got my hands on Tracetest &lt;a href="https://github.com/lightstep/tracetest-nomad/tree/main/jobspec" rel="noopener noreferrer"&gt;back in June 2022&lt;/a&gt;, so it was still pretty fresh! At the time, it integrated only with Jaeger. More specifically, it used the Jaeger API to pull OTel Traces from Jaeger to register them into Tracetest. You then used the UI to create trace-based tests.&lt;/p&gt;

&lt;p&gt;Tracetest has come a long way since those early days! It now has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://docs.tracetest.io/cli/configuring-your-cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A quickstart installer so that you can get a full-fledged example up and running on an existing Kubernetes cluster (Very slick and easy to get going, I might add!)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tracetest.io/integrations" rel="noopener noreferrer"&gt;Integrations&lt;/a&gt; with a number of Observability back-ends and other tools, including &lt;a href="https://app.lightstep.com" rel="noopener noreferrer"&gt;Lightstep&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A declarative approach to defining Trace-based tests via YAML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got to play around with these newer features last December, after a months-long hiatus, and it was really cool to see the evolution of the product. If you follow &lt;a href="https://linktr.ee/adriana_villela" rel="noopener noreferrer"&gt;my work&lt;/a&gt;, you’ll know that I play in both the Kubernetes and Nomad worlds. Today, I’ll be taking you on a quick little guided tour of Tracetest, using Traces from the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OpenTelemetry Demo App&lt;/a&gt; to give you a feel for how it works. The whole setup will be running on HashiCorp Nomad. \&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I will show you how to install Tracetest (v0.9.1) and the OTel Demo App (v1.1.0) on Nomad running locally using &lt;a href="https://github.com/servian/hashiqube" rel="noopener noreferrer"&gt;HashiQube&lt;/a&gt;. I will then show you how to create and run a simple test with Tracetest, using a couple of Traces from the OTel Demo App.&lt;/p&gt;

&lt;p&gt;Although the versions of both Tracetest and the OTel Demo App used in this tutorial are slightly older than what’s out there, you should still get a decent idea of how everything works. The OTel Demo App is configured to send Traces and Metrics to Lightstep via the &lt;a href="https://opentelemetry.io/docs/collector/" rel="noopener noreferrer"&gt;OTel Collector&lt;/a&gt;. The Collector is also configured to send Traces to Tracetest. This configuration is based on guidance from the &lt;a href="https://docs.tracetest.io/configuration/connecting-to-data-stores/lightstep/" rel="noopener noreferrer"&gt;official Tracetest docs&lt;/a&gt;. You can see the full Collector configuration used in this example &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/tracetest/jobspec/otel-collector.nomad#L93-L163" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please note that there are no official Nomad jobspecs for either Tracetest or the OTel Demo App, so I went ahead and did the conversions myself from Kubernetes manifests to Nomad jobspecs. You can check out the jobspecs in &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;. If you’re curious as to how I went about the Kubernetes-to-Nomad conversion, you can &lt;a href="https://medium.com/dev-genius/how-to-convert-kubernetes-manifests-into-nomad-jobspecs-7a58d2fa07a0" rel="noopener noreferrer"&gt;check out my blog post on this topic&lt;/a&gt;. I also have a blog post dedicated to running the OTel Demo App on Nomad. If this tickles your fancy, you can check it out &lt;a href="https://medium.com/@adri-v/running-the-opentelemetry-demo-app-on-hashicorp-nomad-a3e21e35369d" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assumptions
&lt;/h3&gt;

&lt;p&gt;Before we move on, I am assuming that you have a basic understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nomad. If not, mosey on over to my &lt;a href="https://adri-v.medium.com/just-in-time-nomad-80f57cd403ca" rel="noopener noreferrer"&gt;Nomad intro post&lt;/a&gt;. &lt;a href="https://danielabaron.me/blog/nomad-tips-and-tricks/" rel="noopener noreferrer"&gt;This blog post&lt;/a&gt; by &lt;a href="https://danielabaron.me/" rel="noopener noreferrer"&gt;Daniela Baron&lt;/a&gt; is also great.&lt;/li&gt;
&lt;li&gt;Observability (o11y) and &lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTel). If not, mosey on over to my &lt;a href="https://storiesfromtheherd.com/unpacking-observability-the-observability-stack-93d4733e2a72" rel="noopener noreferrer"&gt;Observability &amp;amp; OTel post&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pre-Requisites
&lt;/h3&gt;

&lt;p&gt;In order to run the example in this tutorial, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; (version 2.3.1 at the time of this writing) - to provision HashiQube&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; (version 20.10.21 at the time of this writing) - we’ll be running HashiQube on Docker, using the &lt;a href="https://developer.hashicorp.com/vagrant/docs/providers/docker" rel="noopener noreferrer"&gt;Vagrant Docker Provider&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also need to make sure that your Docker Desktop resource settings are set to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPUs: 3&lt;/li&gt;
&lt;li&gt;Memory: 9.5GB&lt;/li&gt;
&lt;li&gt;Swap: 3GB&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tutorial Repos
&lt;/h3&gt;

&lt;p&gt;Below are the repos that we’ll be using for today’s tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/avillela/hashiqube" rel="noopener noreferrer"&gt;My modified HashiQube Repo&lt;/a&gt; (fork of &lt;a href="https://github.com/servian/hashiqube" rel="noopener noreferrer"&gt;servian/hashiqube&lt;/a&gt;). If you’re curious, you can see what modifications I’ve made &lt;a href="https://github.com/avillela/hashiqube#about" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;My &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;Nomad Conversions&lt;/a&gt; repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 1: Provision Nomad, Consul, and Vault using HashiQube
&lt;/h3&gt;

&lt;p&gt;In this section, we’ll be provisioning a local Hashi environment (including Nomad, Consul, and Vault) instance using &lt;a href="https://github.com/avillela/hashiqube" rel="noopener noreferrer"&gt;HashiQube&lt;/a&gt;. Then, we’ll install &lt;a href="https://tracetest.io" rel="noopener noreferrer"&gt;Tracetest&lt;/a&gt; and the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OTel Demo App&lt;/a&gt; on &lt;a href="https://nomadproject.io" rel="noopener noreferrer"&gt;Nomad&lt;/a&gt;. Feel free to skip this section if you already have a working Hashi environment with Nomad, Consul, and Vault.&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Update /etc/hosts
&lt;/h4&gt;

&lt;p&gt;We use the &lt;a href="https://traefik.io" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt; load-balancer to expose our services, which we access as subdomains of &lt;code&gt;localhost&lt;/code&gt;. In order ensure that we can access our Traefik-exposed services (and also the Traefik dashboard itself, you’ll need to add the following entries to &lt;code&gt;/etc/hosts&lt;/code&gt; on your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1 traefik.localhost
127.0.0.1 otel-demo.localhost
127.0.0.1 tracetest.localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more on Traefik load-balancing on Nomad, check out &lt;a href="https://medium.com/dev-genius/running-hashiqube-using-the-vagrant-docker-provider-3e551c0eca97" rel="noopener noreferrer"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2- Provision a Local Hashi Environment with HashiQube
&lt;/h4&gt;

&lt;p&gt;Start HashiQube by following the detailed instructions &lt;a href="https://github.com/avillela/hashiqube#quickstart" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can now access the apps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vault: &lt;a href="http://localhost:8200" rel="noopener noreferrer"&gt;http://localhost:8200&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Nomad: &lt;a href="http://localhost:4646" rel="noopener noreferrer"&gt;http://localhost:4646&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Consul: &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Traefik: &lt;a href="http://traefik.localhost" rel="noopener noreferrer"&gt;http://traefik.localhost&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 2: Deploying Tracetest and the OTel Demo App on Nomad
&lt;/h3&gt;

&lt;p&gt;We’re ready to deploy the OTel Demo App and Tracetest on Nomad!&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Add Lightstep Access Token to Vault
&lt;/h4&gt;

&lt;p&gt;As I mentioned earlier, we’ll be sending our Traces to both Lightstep and Tracetest. In order to send Traces to Lightstep, you’ll need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token" rel="noopener noreferrer"&gt;Lightstep Access Token&lt;/a&gt;. (Make sure that you &lt;a href="https://app.lightstep.com/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; for a &lt;a href="https://app.lightstep.com/" rel="noopener noreferrer"&gt;Lightstep&lt;/a&gt; account first, if you don’t already have one.)&lt;/li&gt;
&lt;li&gt;Configure Vault by following the instructions &lt;a href="https://github.com/avillela/hashiqube#vault-setup" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add your Lightstep Access Token to Vault by running the command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault kv put kv/otel/o11y/lightstep &lt;span class="nv"&gt;ls_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;LS_TOKEN&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;LS_TOKEN&amp;gt;&lt;/code&gt; is your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token" rel="noopener noreferrer"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2- Deploy the OTel Demo App and Tracetest
&lt;/h4&gt;

&lt;p&gt;First, let’s clone the repo, and go to our working directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/avillela/nomad-conversions.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nomad-conversions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s deploy the services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable memory over-subscription&lt;/span&gt;
nomad operator scheduler set-config &lt;span class="nt"&gt;-memory-oversubscription&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="c"&gt;# Tracetest deploy&lt;/span&gt;
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/traefik.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/postgres.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/tracetest.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/otel-collector.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/go-server.nomad
&lt;span class="c"&gt;# OTel Demo App deploy&lt;/span&gt;
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/redis.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/ffspostgres.nomad

nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/adservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/cartservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/currencyservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/emailservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/featureflagservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/paymentservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/productcatalogservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/quoteservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/shippingservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/checkoutservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/recommendationservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontend.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/loadgenerator.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontendproxy.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/grafana.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/jaeger.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/prometheus.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; We’ve enabled memory &lt;a href="https://developer.hashicorp.com/nomad/docs/commands/operator/scheduler/set-config#memory-oversubscription" rel="noopener noreferrer"&gt;oversubscription&lt;/a&gt; in Nomad. This is a one-time setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we’re running the jobs in &lt;a href="https://developer.hashicorp.com/nomad/docs/commands/job/run#detach" rel="noopener noreferrer"&gt;detached mode&lt;/a&gt;, Nomad won’t wait to start the next job until the current one has deployed successfully. This means that your output will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job registration successful
Evaluation ID: d3eaa396-954e-241f-148d-6720c35f34bf
Job registration successful
Evaluation ID: 6bba875d-f415-36b7-bfeb-2ca4b9982acb
Job registration successful
Evaluation ID: 16dc8ef8-5e26-68f4-89b6-3d96b348775b
Job registration successful
Evaluation ID: 34de0532-a3b5-8691-bf18-51c0cc030573
Job registration successful
Evaluation ID: 7310e6a2-9945-710b-1505-c01bd58ccd35
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Friendly reminder that the &lt;code&gt;Evaluation ID&lt;/code&gt; values will be different on your machine.&lt;/p&gt;

&lt;h4&gt;
  
  
  3- See it in Nomad
&lt;/h4&gt;

&lt;p&gt;As things are deploying, you can mosey on over to the Nomad UI at &lt;a href="http://localhost:4646" rel="noopener noreferrer"&gt;http://localhost:4646&lt;/a&gt; to see how things are coming along:&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%2Fqnnviyj9nx5c0envpzxl.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%2Fqnnviyj9nx5c0envpzxl.png" alt="Screen capture of the HashiCorp Nomad UI showing the OTel Demo App and Tracetest services starting up" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take some time for all of the services to come up (sometimes up to 30 minutes), depending on network speed (especially since Nomad needs to download the images and initialize the services) and system resources, so be patient! Since some services depend on other services in order to run, you may see services in limbo or some going up and down for a while, per the above screen capture. DON’T PANIC! IT WILL ALL BE OKAY!!&lt;/p&gt;

&lt;p&gt;Once all of the jobs are up and running, you’ll see everything look green, like this:&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%2Flbhqncw8z44gzkl3qe5x.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%2Flbhqncw8z44gzkl3qe5x.png" alt="Screen capture of the HashiCorp Nomad UI showing the OTel Demo App and Tracetest services started" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also head on over to Consul at &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt; to see the health of the services:&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%2F4ne4u1pqf6o6ic1qdlyc.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%2F4ne4u1pqf6o6ic1qdlyc.png" alt="Screen capture of services running in Consul" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, unhealthy services show up at the top, with a red “x” next to them. Since we don’t see any nasty red “x”s in the above screen shot, we know that our services are lookin’ good!&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Access the OTel Demo App
&lt;/h4&gt;

&lt;p&gt;To make sure that the OTel Demo App is up and running, you can access it at: &lt;a href="http://otel-demo.localhost" rel="noopener noreferrer"&gt;http://otel-demo.localhost&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%2Ftf96aebpmgmehcypcygx.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%2Ftf96aebpmgmehcypcygx.png" alt="Screen capture of the OTel Demo app running at http://otel-demo.localhost" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  5- Access Tracetest
&lt;/h4&gt;

&lt;p&gt;You should also be able to access the Tracetest UI at: &lt;a href="http://tracetest.localhost" rel="noopener noreferrer"&gt;http://tracetest.localhost&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%2Fpwp2gxv70td3eruob9ra.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%2Fpwp2gxv70td3eruob9ra.png" alt="Tracetest user interface running at http://tracetest.localhost" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: Creating &amp;amp; Running Tests in Tracetest
&lt;/h3&gt;

&lt;p&gt;Now that we have the OTel Demo App and Tracetest up and running, we can finally create our tests in Tracetest!&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Install the Tracetest CLI
&lt;/h4&gt;

&lt;p&gt;The Tracetest CLI allows you to interact with Tracetest programmatically. You can install the Tracetest CLI on Mac via Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kubeshop/tracetest/tracetest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows and Linux, check out the instructions &lt;a href="https://docs.tracetest.io/getting-started/installation#install-the-tracetest-cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s make sure that the CLI is installed properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tracetest version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v0.9.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2- Configure the Tracetest CLI
&lt;/h4&gt;

&lt;p&gt;Now that the CLI is installed, let’s configure the Tracetest CLI so that it knows what Tracetest installation it needs to talk to. Make sure that you’re still in the &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&lt;/a&gt; root directory, and then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;tracetest
tracetest configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a file called &lt;code&gt;config.yaml&lt;/code&gt; in the directory from which you ran &lt;code&gt;tracetest configure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you run the above command, you will be prompted for the Tracetest server URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter your Tracetest server URL [http://tracetest.localhost]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that it’s already pre-populated with &lt;code&gt;http://tracetest.localhost&lt;/code&gt;. This is because there’s already a &lt;code&gt;config.yaml&lt;/code&gt; file in our &lt;code&gt;tracetest&lt;/code&gt; directory. If you don’t type anything in and hit return, the CLI will just use the value already in &lt;code&gt;config.yaml&lt;/code&gt;. If you want to use a different URL, make sure that you prefix that URL with &lt;code&gt;http://&lt;/code&gt; (or &lt;code&gt;https://&lt;/code&gt; if you’re hosting a secure instance).&lt;/p&gt;

&lt;p&gt;Next, you’ll be prompted on whether or not you wish to enable analytics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enable analytics? [Y/n]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you hit return here, it will enable analytics by default. Type &lt;code&gt;n&lt;/code&gt; to disable analytics.&lt;/p&gt;

&lt;p&gt;Now, let’s open up &lt;code&gt;config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scheme: http
endpoint: tracetest.localhost
analyticsEnabled: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3- Create a Trace-based test in Tracetest
&lt;/h4&gt;

&lt;p&gt;Now we’re ready to create a test! We can do this in one of two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the UI (check out the Tracetest docs &lt;a href="https://docs.tracetest.io/web-ui/creating-tests" rel="noopener noreferrer"&gt;here&lt;/a&gt; for details)&lt;/li&gt;
&lt;li&gt;From a YAML file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m a big fan of doing things programmatically, so let’s create our test from a YAML file. &lt;a href="https://docs.tracetest.io/web-ui/creating-tests" rel="noopener noreferrer"&gt;Per the Tracetest docs&lt;/a&gt;, you can create tests from HTTP requests, &lt;a href="https://grpc.io" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; requests, &lt;a href="https://curl.se" rel="noopener noreferrer"&gt;cURL&lt;/a&gt; commands, and more. In our case, we’re creating our tests from a gRPC request, so our Tracetest test definition file YAML looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type: Test
spec:
 id: &amp;lt;test_id&amp;gt;
 name: &amp;lt;test_name&amp;gt;
 description: &amp;lt;test_description&amp;gt;
 trigger:
   type: grpc
   grpc:
     protobufFile: |
        &amp;lt;protobuf_file_contents_here&amp;gt;
     address: &amp;lt;grpc_endpoint_address&amp;gt;
     method: &amp;lt;grpc_method_name&amp;gt;
 specs:
 - selector: span[tracetest.span.type="general" name="&amp;lt;operation_name&amp;gt;"]
   assertions:
   - attr:&amp;lt;attribute_name&amp;gt; &amp;lt;operator&amp;gt; &amp;lt;value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_id&amp;gt;&lt;/code&gt; is a unique test identifier. If you create your test via the UI, a unique ID is assigned. If you create a test from a YAML file, you can assign your own ID. For this example, let’s name our ID &lt;code&gt;​tt-recsvc-01&lt;/code&gt;. As long as it’s unique, you should be good.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_name&amp;gt;&lt;/code&gt; is the name of your test. Use something short and meaningful.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_description&amp;gt;&lt;/code&gt; is short description of your test.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trigger.type&lt;/code&gt; is &lt;code&gt;grpc&lt;/code&gt;, since we’re creating a test from a gRPC request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trigger.grpc.protobufFile&lt;/code&gt; should be the &lt;strong&gt;&lt;em&gt;complete contents&lt;/em&gt;&lt;/strong&gt; of your &lt;a href="https://en.wikipedia.org/wiki/Protocol_Buffers" rel="noopener noreferrer"&gt;protobuf file&lt;/a&gt;. In our case, we’re going to embed the contents of &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OTel Demo App&lt;/a&gt;’s &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/v1.1.0/pb/demo.proto" rel="noopener noreferrer"&gt;demo.proto&lt;/a&gt; file in this field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;grpc_endpoint_address&amp;gt;&lt;/code&gt; is the address at which the gRPC endpoint is served. In this case, we’re using &lt;a href="https://developer.hashicorp.com/consul/docs/discovery/dns" rel="noopener noreferrer"&gt;Consul DNS&lt;/a&gt; to expose the service’s address. If we look at the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/recommendationservice.nomad" rel="noopener noreferrer"&gt;Recommendation Service’s Nomad jobspec&lt;/a&gt;, you’ll see that the name of the gRPC service is &lt;code&gt;recommendationservice&lt;/code&gt;. So when we query it in &lt;a href="https://consul.io" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;, it should be accessible at this address &lt;code&gt;recommendationservice.service.consul&lt;/code&gt;. We can test this by logging into the HashiQube image. Do this by going to the root directory of HashiQube repo, and typing &lt;code&gt;vagrant ssh&lt;/code&gt;. Once you’re in the HashiQube Vagrant box, run this command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dig +short recommendationservice.service.consul
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;172.17.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get the port number from the Recommendation Service jobspec’s &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/otel-demo-app/jobspec/recommendationservice.nomad#L18" rel="noopener noreferrer"&gt;port definition stanza&lt;/a&gt;. In our case, the port number is &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/otel-demo-app/jobspec/recommendationservice.nomad#L18" rel="noopener noreferrer"&gt;9001&lt;/a&gt;. Note that we need to use a static port number in order for this setup to work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;grpc_method_name&amp;gt;&lt;/code&gt; is the name of the gRPC method that we’re testing. It follows the naming convention &lt;code&gt;&amp;lt;package_name&amp;gt;.&amp;lt;service_name&amp;gt;.&amp;lt;method_name&amp;gt;&lt;/code&gt;. Where &lt;code&gt;&amp;lt;package_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L19" rel="noopener noreferrer"&gt;hipstershop&lt;/a&gt;, &lt;code&gt;&amp;lt;service_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L58" rel="noopener noreferrer"&gt;RecommendationService&lt;/a&gt;, and &lt;code&gt;&amp;lt;method_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L59" rel="noopener noreferrer"&gt;ListRecommendations&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec&lt;/code&gt; is where we define our test assertions. We need to first select our target Span(s), and then we define what assertions to apply to the Span(s).  In our case, we are selecting a span named &lt;code&gt;get_product_list&lt;/code&gt;, and we’re asserting that the value of the attribute &lt;code&gt;app.filtered_products.count&lt;/code&gt; is set to &lt;code&gt;9&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all that in mind, our YAML should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type: Test
spec:
id: ​tt-recsvc-01
name: RecommendationService test
description: Sample test file for the OTel Demo App RecommendationService
trigger:
  type: grpc
  grpc:
    protobufFile: |
      // Copyright 2020 Google LLC
      //
      // Licensed under the Apache License, Version 2.0 (the "License");
      // you may not use this file except in compliance with the License.
      // You may obtain a copy of the License at
      //
      //      http://www.apache.org/licenses/LICENSE-2.0
      //
      // Unless required by applicable law or agreed to in writing, software
      // distributed under the License is distributed on an "AS IS" BASIS,
      // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      // See the License for the specific language governing permissions and
      // limitations under the License.

      syntax = "proto3";

      import "google/protobuf/timestamp.proto";

      package hipstershop;

      option go_package = "genproto/hipstershop";

      // -----------------Cart service-----------------

      service CartService {
          rpc AddItem(AddItemRequest) returns (Empty) {}
          rpc GetCart(GetCartRequest) returns (Cart) {}
          rpc EmptyCart(EmptyCartRequest) returns (Empty) {}
      }

      message CartItem {
          string product_id = 1;
          int32  quantity = 2;
      }

      message AddItemRequest {
          string user_id = 1;
          CartItem item = 2;
      }

      message EmptyCartRequest {
          string user_id = 1;
      }

      message GetCartRequest {
          string user_id = 1;
      }

      message Cart {
          string user_id = 1;
          repeated CartItem items = 2;
      }

      message Empty {}

      // ---------------Recommendation service----------

      service RecommendationService {
        rpc ListRecommendations(ListRecommendationsRequest) returns (ListRecommendationsResponse){}
      }

      message ListRecommendationsRequest {
          string user_id = 1;
          repeated string product_ids = 2;
      }

      message ListRecommendationsResponse {
          repeated string product_ids = 1;
      }

      // ---------------Product Catalog----------------

      service ProductCatalogService {
          rpc ListProducts(Empty) returns (ListProductsResponse) {}
          rpc GetProduct(GetProductRequest) returns (Product) {}
          rpc SearchProducts(SearchProductsRequest) returns (SearchProductsResponse) {}
      }

      message Product {
          string id = 1;
          string name = 2;
          string description = 3;
          string picture = 4;
          Money price_usd = 5;

          // Categories such as "clothing" or "kitchen" that can be used to look up
          // other related products.
          repeated string categories = 6;
      }

      message ListProductsResponse {
          repeated Product products = 1;
      }

      message GetProductRequest {
          string id = 1;
      }

      message SearchProductsRequest {
          string query = 1;
      }

      message SearchProductsResponse {
          repeated Product results = 1;
      }

      // ---------------Shipping Service----------

      service ShippingService {
          rpc GetQuote(GetQuoteRequest) returns (GetQuoteResponse) {}
          rpc ShipOrder(ShipOrderRequest) returns (ShipOrderResponse) {}
      }

      message GetQuoteRequest {
          Address address = 1;
          repeated CartItem items = 2;
      }

      message GetQuoteResponse {
          Money cost_usd = 1;
      }

      message ShipOrderRequest {
          Address address = 1;
          repeated CartItem items = 2;
      }

      message ShipOrderResponse {
          string tracking_id = 1;
      }

      message Address {
          string street_address = 1;
          string city = 2;
          string state = 3;
          string country = 4;
          string zip_code = 5;
      }

      // -----------------Currency service-----------------

      service CurrencyService {
          rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
          rpc Convert(CurrencyConversionRequest) returns (Money) {}
      }

      // Represents an amount of money with its currency type.
      message Money {
          // The 3-letter currency code defined in ISO 4217.
          string currency_code = 1;

          // The whole units of the amount.
          // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar.
          int64 units = 2;

          // Number of nano (10^-9) units of the amount.
          // The value must be between -999,999,999 and +999,999,999 inclusive.
          // If `units` is positive, `nanos` must be positive or zero.
          // If `units` is zero, `nanos` can be positive, zero, or negative.
          // If `units` is negative, `nanos` must be negative or zero.
          // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.
          int32 nanos = 3;
      }

      message GetSupportedCurrenciesResponse {
          // The 3-letter currency code defined in ISO 4217.
          repeated string currency_codes = 1;
      }

      message CurrencyConversionRequest {
          Money from = 1;

          // The 3-letter currency code defined in ISO 4217.
          string to_code = 2;
      }

      // -------------Payment service-----------------

      service PaymentService {
          rpc Charge(ChargeRequest) returns (ChargeResponse) {}
      }

      message CreditCardInfo {
          string credit_card_number = 1;
          int32 credit_card_cvv = 2;
          int32 credit_card_expiration_year = 3;
          int32 credit_card_expiration_month = 4;
      }

      message ChargeRequest {
          Money amount = 1;
          CreditCardInfo credit_card = 2;
      }

      message ChargeResponse {
          string transaction_id = 1;
      }

      // -------------Email service-----------------

      service EmailService {
          rpc SendOrderConfirmation(SendOrderConfirmationRequest) returns (Empty) {}
      }

      message OrderItem {
          CartItem item = 1;
          Money cost = 2;
      }

      message OrderResult {
          string   order_id = 1;
          string   shipping_tracking_id = 2;
          Money shipping_cost = 3;
          Address  shipping_address = 4;
          repeated OrderItem items = 5;
      }

      message SendOrderConfirmationRequest {
          string email = 1;
          OrderResult order = 2;
      }


      // -------------Checkout service-----------------

      service CheckoutService {
          rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
      }

      message PlaceOrderRequest {
          string user_id = 1;
          string user_currency = 2;

          Address address = 3;
          string email = 5;
          CreditCardInfo credit_card = 6;
      }

      message PlaceOrderResponse {
          OrderResult order = 1;
      }

      // ------------Ad service------------------

      service AdService {
          rpc GetAds(AdRequest) returns (AdResponse) {}
      }

      message AdRequest {
          // List of important key words from the current page describing the context.
          repeated string context_keys = 1;
      }

      message AdResponse {
          repeated Ad ads = 1;
      }

      message Ad {
          // url to redirect to when an ad is clicked.
          string redirect_url = 1;

          // short advertisement text to display.
          string text = 2;
      }

      // ------------Feature flag service------------------

      service FeatureFlagService {
        rpc GetFlag(GetFlagRequest) returns (GetFlagResponse) {}
        rpc CreateFlag(CreateFlagRequest) returns (CreateFlagResponse) {}
        rpc UpdateFlag(UpdateFlagRequest) returns (UpdateFlagResponse) {}
        rpc ListFlags(ListFlagsRequest) returns (ListFlagsResponse) {}
        rpc DeleteFlag(DeleteFlagRequest) returns (DeleteFlagResponse) {}
      }

      message Flag {
        string name = 1;
        string description = 2;
        bool enabled = 3;
        google.protobuf.Timestamp created_at = 4;
        google.protobuf.Timestamp updated_at = 5;
      }

      message GetFlagRequest {
        string name = 1;
      }

      message GetFlagResponse {
        Flag flag = 1;
      }

      message CreateFlagRequest {
        string name = 1;
        string description = 2;
        bool enabled = 3;
      }

      message CreateFlagResponse {
        Flag flag = 1;
      }

      message UpdateFlagRequest {
        string name = 1;
        bool enabled = 2;
      }

      message UpdateFlagResponse {}

      message ListFlagsRequest {}

      message ListFlagsResponse {
        repeated Flag flag = 1;
      }

      message DeleteFlagRequest {
        string name = 1;
      }

      message DeleteFlagResponse {}
    address: recommendationservice.service.consul:9001
    method: hipstershop.RecommendationService.ListRecommendations
specs:
 - selector: span[name="get_product_list"]
   assertions:
   - attr:app.filtered_products.count  =  9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this file as &lt;code&gt;recsvc-test.yml&lt;/code&gt; in the &lt;a href="https://github.com/avillela/nomad-conversions/tree/main/tracetest/tests" rel="noopener noreferrer"&gt;tracetest/tests&lt;/a&gt; directory of &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Run the Trace-based test
&lt;/h4&gt;

&lt;p&gt;With our test YAML in hand, we can run our test! Make sure that you’re running this from &lt;a href="https://github.com/avillela/nomad-conversions/tree/main/tracetest" rel="noopener noreferrer"&gt;tracetest&lt;/a&gt; directory of &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&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;tracetest test run --definition tests/recsvc-test.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ RecommendationService test (http://tracetest.localhost/test/avtest4567/run/1/test)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s check out our test in the Tracetest UI by going to &lt;a href="http://tracetest.localhost" rel="noopener noreferrer"&gt;http://tracetest.localhost&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%2Fm0n4b1z43aoue48cv3n7.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%2Fm0n4b1z43aoue48cv3n7.png" alt="Screen capture of the results of running the RecommendationService Test in the Tracetest UI" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on the &lt;code&gt;&amp;gt;&lt;/code&gt; next to the test definition to expand it:&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%2Fqs2mdxf5mcnywfbh2p6b.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%2Fqs2mdxf5mcnywfbh2p6b.png" alt="Screen capture of expanding the RecommendationService Test in the Tracetest UI" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the UI shows that it ran v1 of the test. If we make any changes to the test definition, Tracetest bumps the test version; however, if we were to change only the test ID and re-run the file, it would show up as an entirely different test in Tracetest, and not a different version of the same test.&lt;/p&gt;

&lt;p&gt;Let’s explore our test definition in the UI, by clicking in the area highlighted 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%2Fvyqpcguldftc93fj1616.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%2Fvyqpcguldftc93fj1616.png" alt="Selecting your test from the Tracetest UI" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Will take us to the test trigger definition screen:&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%2Ff1nxkxbgy56st71rxnqy.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%2Ff1nxkxbgy56st71rxnqy.png" alt="Tracetest test trigger definition screen" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This might seem familiar - it’s the information that we entered in our test YAML. Tracetest runs the API endpoint and then checks to see if there are any Traces associated with this with this endpoint, and lets you create tests against the traces. Test specs are defined in the  &lt;code&gt;Test&lt;/code&gt; tab, located at the top middle section of the screen:&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%2Flko4cj1zt1vke5ahwrce.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%2Flko4cj1zt1vke5ahwrce.png" alt="Test tab" width="800" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on it brings us to the test spec definition screen:&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%2Fwnjlsr9cyypsfkfmagdi.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%2Fwnjlsr9cyypsfkfmagdi.png" alt="Tracetest test spec definition screen" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let’s head to the top left-hand side of the screen, and click on the purple circle with &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&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%2Fygkj38ae8bsnivb3zlua.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%2Fygkj38ae8bsnivb3zlua.png" alt="Expanding the test spec definition screen to reveal span details" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reveals a side pane with all sorts of information about the selected Span. In our case, we’ve selected the &lt;code&gt;Tracetest trigger&lt;/code&gt; Span (top box), so our screen looks something like this:&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%2Fsdnyz841wx9vttlh52c7.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%2Fsdnyz841wx9vttlh52c7.png" alt="Tracetest test definition screen with the expanded span info" width="800" height="942"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the &lt;code&gt;Tracetest trigger&lt;/code&gt; box has a slight blue highlight. But we’re not interested in this &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans" rel="noopener noreferrer"&gt;Span&lt;/a&gt;. We’re actually interested in the &lt;code&gt;get_product_list&lt;/code&gt; &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans" rel="noopener noreferrer"&gt;Span&lt;/a&gt;, so let’s click on that Span:&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%2Foidcpbnnv25w64nye19p.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%2Foidcpbnnv25w64nye19p.png" alt="Tracetest test definition screen get_product_list test" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how it has an attribute called &lt;code&gt;app.filtered_products.count&lt;/code&gt;, and that its value is &lt;code&gt;9&lt;/code&gt;…which is the value that we’re checking for in our assertion! Ta-da! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 4: See it Lightstep!
&lt;/h3&gt;

&lt;p&gt;Remember that our OTel Collector is configured to send Traces to both Tracetest and Lightstep, so we should see the same Trace data in both.&lt;/p&gt;

&lt;p&gt;I went into my Lightstep account and created a &lt;a href="https://docs.lightstep.com/docs/use-notebooks" rel="noopener noreferrer"&gt;Notebook&lt;/a&gt; that looked like this:&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%2F3ops0n17vbspn9yzb8uo.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%2F3ops0n17vbspn9yzb8uo.png" alt="Lightstep Notebook screen for Tracetest" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And clicked on one of the matching traces returned, which led me to the Trace diagram 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%2Fzw3sifq7yjwnlbmztrv0.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%2Fzw3sifq7yjwnlbmztrv0.png" alt="Flame graph of get_product_list trace" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we have our &lt;code&gt;get_product_list&lt;/code&gt; trace, which has an attribute called &lt;code&gt;app.filtered_products.count&lt;/code&gt; with a value of &lt;code&gt;9&lt;/code&gt;. Yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Tracetest is a great tool for unlocking the potential of Trace-based testing. Although the example in this tutorial was fairly simple, I hope that it gives you an appreciation for some of the cool stuff that Tracetest can do. I definitely plan to keep my eye out as this tool improves, and &lt;a href="https://thenewstack.io/our-2023-site-reliability-engineering-wish-list/" rel="noopener noreferrer"&gt;I am excited to see where Trace-based testing takes us in 2023&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;For more on the ins and outs of Tracetest, be sure to check out the official docs &lt;a href="https://docs.tracetest.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For you visual learners, I’ve put together a quick little four-minute video highlighting the main things covered in this post. Be sure to check it out &lt;a href="https://youtu.be/EBWf2aaR9wk" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That was definitely a lot to take in, so give yourself a pat on the back for getting through this! Now please enjoy this lovely photo of Mookie the rat being cradled in my husband’s arms.&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%2Fa56lh0yna3f1dhue6kuc.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%2Fa56lh0yna3f1dhue6kuc.JPG" alt="Hello from Mookie the rat!" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code!&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%2F8u22sqhqosy1yhe0r7rr.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%2F8u22sqhqosy1yhe0r7rr.png" alt="Peace love code" width="800" height="128"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This post was originally published on the &lt;a href="https://tracetest.io/blog/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad" rel="noopener noreferrer"&gt;Tracetest blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bug</category>
      <category>n8nbrightdatachallenge</category>
      <category>challenge</category>
    </item>
    <item>
      <title>Running Tracetest and the OpenTelemetry Demo App on HashiCorp Nomad</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Wed, 18 Jan 2023 23:17:51 +0000</pubDate>
      <link>https://forem.com/avillela/running-tracetest-and-the-opentelemetry-demo-app-on-hashicorp-nomad-44el</link>
      <guid>https://forem.com/avillela/running-tracetest-and-the-opentelemetry-demo-app-on-hashicorp-nomad-44el</guid>
      <description>&lt;p&gt;This quick little video gives a high-level overview of &lt;br&gt;
running Tracetest and the OpenTelemetry (OTel) Demo App on HashiCorp Nomad. In this demo, I run tests against traces generated by a couple of gRPC services in the OTel Demo App. &lt;/p&gt;

&lt;p&gt;I run Nomad locally using &lt;a href="https://github.com/avillela/hashiqube"&gt;HashiQube&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>nomad</category>
      <category>tracetest</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>How to Convert Kubernetes Manifests into Nomad Jobspecs</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 19 Dec 2022 18:37:16 +0000</pubDate>
      <link>https://forem.com/avillela/how-to-convert-kubernetes-manifests-into-nomad-jobspecs-2d9e</link>
      <guid>https://forem.com/avillela/how-to-convert-kubernetes-manifests-into-nomad-jobspecs-2d9e</guid>
      <description>&lt;p&gt;Ever since I started &lt;a href="https://medium.com/@adri-v/list/justintime-nomad-cc5d249a172b"&gt;exploring Nomad&lt;/a&gt;, one of the things that I’ve enjoyed doing is taking &lt;a href="https://www.baeldung.com/ops/docker-compose"&gt;Docker Compose files&lt;/a&gt; and &lt;a href="https://stackoverflow.com/a/55132302"&gt;Kubernetes manifests&lt;/a&gt;, and translating them into &lt;a href="https://nomadproject.io"&gt;HashiCorp Nomad&lt;/a&gt; &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification"&gt;jobspec&lt;/a&gt;. I did it for &lt;a href="https://medium.com/faun/just-in-time-nomad-running-temporal-on-nomad-5fee139f37ea"&gt;Temporal back in March 2022,&lt;/a&gt; and also for an &lt;a href="https://github.com/lightstep/tracetest-nomad"&gt;early version of Tracetest, back in the summer of 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my latest Nomadification Project (TM), I got the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to run on Nomad (with &lt;a href="https://medium.com/@adri-v/list/hashiqube-bfdcb9c84e10"&gt;HashiQube&lt;/a&gt;, &lt;em&gt;of course&lt;/em&gt;). To do this, I used the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;OpenTelemetry Demo App Helm Chart&lt;/a&gt; as my guide. In doing this, and other Nomadifications, I realized that I’ve never gone through the process of explaining the conversion process from Kubernetes manifests to Nomad jobspecs.&lt;/p&gt;

&lt;p&gt;So, as you may have guessed, today, I will go through the process of converting Kubernetes manifests to Nomad jobspecs, so that if you ever find yourself in a situation whereby you’re thinking, “Gee, it would be nice to see this Kubernetes stuff running on Nomad,” you now have a process!&lt;/p&gt;

&lt;p&gt;I’ll use examples from the work I did recently in converting the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;OpenTelemetry Demo App Helm Charts&lt;/a&gt; into Nomad jobspecs to illustrate the process.&lt;/p&gt;

&lt;p&gt;Are you ready? Let’s do this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifests and Helm Charts and Jobspecs…oh my!
&lt;/h2&gt;

&lt;p&gt;While I like working with both Kubernetes and Nomad alike, there is one thing that I find exceedingly irritating in Kubernetes Land: the fact that a Kubernetes manifest for an app deployment is made up of a scavenger hunt of YAML definitions of various Kubernetes objects. Nomad, however, takes a different approach, using a single &lt;a href="https://github.com/hashicorp/hcl"&gt;HashiCorp Configuration Language (HCL)&lt;/a&gt; &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification"&gt;jobspec&lt;/a&gt; file as a one-stop shop for defining your app. I personally find Nomad HCL a lot easier to manage, since there are fewer moving parts, and when it comes to converting Kubernetes manifests to Nomad jobspecs, I find that having a single file to work with makes things a lot simpler. &lt;/p&gt;

&lt;p&gt;In order to convert a Kubernetes manifest into a Nomad jobspec, we first need to start with a basic Nomad jobspec. This will serve as a template for deploying our application in Nomad.&lt;/p&gt;

&lt;p&gt;Let’s start with our template jobspec below. Please bear in mind that this is a starting point for our conversion. After all, some services are more complex than others, so while for some services, we need all of the components below to be included in our jobspec, for other services, we may end up with a more pared down version of the jobspec.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;service_name&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service"&lt;/span&gt;
  &lt;span class="nx"&gt;datacenters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;service_name&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"host"&lt;/span&gt;

      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;port_number&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;service_name&amp;gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tags_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;service_check_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;service_name&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;

      &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;image_name&amp;gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;image_pull_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"25m"&lt;/span&gt;
        &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;args_go_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="nx"&gt;delay&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"15s"&lt;/span&gt;
        &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
        &lt;span class="nx"&gt;mode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"delay"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;env_vars_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;      

      &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&amp;lt;env_vars_derived_from_consul&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;        &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local/env"&lt;/span&gt;
        &lt;span class="nx"&gt;env&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
        &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;650&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great…so now we’ve got our jobspec template. Yay! But we need to fill in the blanks, don’t we? So...where do we start?&lt;/p&gt;

&lt;p&gt;Since we’re going from Kubernetes to Nomad, we need to look at the application’s Kubernetes manifest. Fortunately, we can grab this info easily from the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts"&gt;OTel Helm Charts Repo&lt;/a&gt;, which, as you may have guessed, has a &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo"&gt;Helm Chart for the OTel Demo App&lt;/a&gt;. It also contains the rendered YAML manifests available to us &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo/examples/default/rendered/component.yaml"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The OpenTelemetry Demo App is made up of a number of services. The process of converting the Kubernetes manifest of each service to its corresponding Nomad jobspec is very similar, so in the interest of not boring you to death, I’ll be choosing one service to illustrate the conversion: the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conversion Process
&lt;/h2&gt;

&lt;p&gt;With the Nomad jobspec template and Kubernetes manifest in hand, we are ready to begin the conversion!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; You can find the repo with all of the OpenTelemetry Demo App jobspec files &lt;a href="https://github.com/avillela/nomad-conversions/tree/main/otel-demo-app"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1- Grab the Kubernetes manifests
&lt;/h3&gt;

&lt;p&gt;As I mentioned earlier, the rendered YAML manifests for the OpenTelemetry Demo App are available to us here. Since, for the purposes of this tutorial, we only care about the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;’s Kubernetes manifest, I’ve gone ahead and grabbed the manifest pertaining to the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;, which is made up of a &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployment&lt;/a&gt; and a &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Service&lt;/a&gt;, as per below.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Source: opentelemetry-demo/templates/component.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-featureflagservice&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;helm.sh/chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-demo-0.14.3&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2.1"&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/part-of&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-demo&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Helm&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ghcr.io/open-telemetry/demo:v1.2.1-featureflagservice'&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50053&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_SERVICE_NAME&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.labels['app.kubernetes.io/component']&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_NAMESPACE&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.namespace&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_NODE_NAME&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spec.nodeName&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_POD_NAME&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.name&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50053"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FEATURE_FLAG_SERVICE_PORT&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_EXPORTER_OTLP_TRACES_PROTOCOL&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecto://ffs:ffs@example-ffspostgres:5432/ffs&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://example-otelcol:4317&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service.name=$(OTEL_SERVICE_NAME),k8s.namespace.name=$(OTEL_K8S_NAMESPACE),k8s.node.name=$(OTEL_K8S_NODE_NAME),k8s.pod.name=$(OTEL_K8S_POD_NAME)&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;175Mi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L121-L147"&gt;Service YAML&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Source: opentelemetry-demo/templates/component.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-featureflagservice&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;helm.sh/chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-demo-0.14.3&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2.1"&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/part-of&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-demo&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Helm&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50053&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50053&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yikes! This all looks pretty overwhelming, doesn’t it? Fortunately, it’s not as scary as it looks. Don’t worry…I’ll guide you along. Let’s keep going.&lt;/p&gt;

&lt;h3&gt;
  
  
  2- Prepare the jobspec
&lt;/h3&gt;

&lt;p&gt;With our Kubernetes YAMLs in hand, let’s go back to our jobspec template from earlier, and fill in some blanks. Since we know that we’re working with the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;, I’ve gone ahead and replaced &lt;code&gt;&amp;amp;lt;service_name&amp;gt;&lt;/code&gt; with &lt;code&gt;featureflagservice&lt;/code&gt;, which means that now our template looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service"&lt;/span&gt;
  &lt;span class="nx"&gt;datacenters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"host"&lt;/span&gt;

      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;port_number&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;service_name&amp;gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tags_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;service_check_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;

      &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;image_name&amp;gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;image_pull_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"25m"&lt;/span&gt;
        &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;args_go_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;entrypoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;entrypoints_go_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;port_name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="nx"&gt;delay&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"15s"&lt;/span&gt;
        &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
        &lt;span class="nx"&gt;mode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"delay"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;env_vars_here&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;      

      &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&amp;lt;env_vars_derived_from_consul&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;        &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local/env"&lt;/span&gt;
        &lt;span class="nx"&gt;env&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
        &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;650&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; You could technically give different names to your &lt;code&gt;job&lt;/code&gt;, &lt;code&gt;task&lt;/code&gt; and &lt;code&gt;group&lt;/code&gt;, such as &lt;code&gt;featureflagservice-job&lt;/code&gt;, &lt;code&gt;featureflagservice-task&lt;/code&gt; and &lt;code&gt;featureflagservice-group&lt;/code&gt; (or really anything you want), but for the sake of simplicity (with a sprinkling of lack of originality), I decided to give them all the same name: &lt;code&gt;featureflagservice&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some useful terminology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;job&lt;/code&gt; is the unit of control. The job is the thing that you start, stop, and update. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;group&lt;/code&gt; is the unit of scale. The group defines how many instances you are running. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;task&lt;/code&gt; is the unit of work. The task is what you actually want to run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3- Port definitions
&lt;/h3&gt;

&lt;p&gt;The next set of blanks that we need to fill in are in the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/network"&gt;network&lt;/a&gt; stanza. More specifically, the &lt;code&gt;&amp;amp;lt;port_name&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;amp;lt;port_number&amp;gt;&lt;/code&gt; values in the &lt;code&gt;port&lt;/code&gt; stanza.&lt;/p&gt;

&lt;p&gt;If we look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;’s &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L121-L147"&gt;Service YAML&lt;/a&gt; above, you’ll notice that it exposes two ports: &lt;code&gt;50053&lt;/code&gt; (gRPC) and &lt;code&gt;8081&lt;/code&gt; (HTTP), per &lt;code&gt;spec -&amp;gt; ports -&amp;gt; targetPort&lt;/code&gt;. Let’s plug these into our jobspec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"host"&lt;/span&gt;

  &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50053&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see per the snippet above, we labeled (named) our ports &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;grpc&lt;/code&gt;. These labels will allow us to refer to those ports by a human-friendly label, rather than by number. Which means that if one or both of the port numbers change, we only need to make the change in one place. And spoiler alert: we will be referring to those ports elsewhere in the jobspec. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Feel free to label your ports anything you want–just make sure that it’s reasonably descriptive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4- Service Definition
&lt;/h3&gt;

&lt;p&gt;Now that we’ve defined our ports, we need to register our services, which is done by way of the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; stanza. Since we have two ports in the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/network"&gt;network&lt;/a&gt; stanza above, we need to define two &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt;s: one per port.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; definition for the &lt;code&gt;http&lt;/code&gt; port looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice-http"&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.http.routers.otel-collector-http.rule=Host(`featureflag.localhost`)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.http.routers.otel-collector-http.entrypoints=web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.http.routers.otel-collector-http.tls=false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.enable=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10s"&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5s"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; is registered to &lt;a href="https://consul.io"&gt;Consul&lt;/a&gt;. Although we don’t explicitly say so, it’s the equivalent of adding a &lt;code&gt;provider = "consul"&lt;/code&gt; attribute to the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; stanza. &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service#provider"&gt;You can register your services to either Nomad or Consul&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;port&lt;/code&gt; attribute is the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/network"&gt;network&lt;/a&gt; port label to which the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; applies.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; is called &lt;code&gt;featureflagservice-http&lt;/code&gt;. Again, you can call it whatever you want, though a descriptive name is always helpful.&lt;/li&gt;
&lt;li&gt;We’re exposing this &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; to the outside world via &lt;a href="https://traefik.io"&gt;Traefik&lt;/a&gt;, and the service is accessible via the URL: &lt;a href="http://featureflag.localhost"&gt;http://featureflag.localhost&lt;/a&gt; (since I’m &lt;a href="https://opentelemetry.io/blog/2022/otel-demo-app-nomad/"&gt;running this locally, using HashiQube&lt;/a&gt;). Keep in mind that you also need to deploy the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/traefik.nomad"&gt;Traefik jobspec&lt;/a&gt; alongside the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/featureflagservice.nomad"&gt;featureflagservice jobspec&lt;/a&gt; in order to expose this service to the outside world. To learn more about running Traefik on Nomad, check out &lt;a href="https://medium.com/@adri-v/just-in-time-nomad-running-traefik-on-hashiqube-7d6dfd8ef9d8"&gt;this post&lt;/a&gt; and &lt;a href="https://medium.com/dev-genius/running-hashiqube-using-the-vagrant-docker-provider-3e551c0eca97"&gt;this post&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/check"&gt;check&lt;/a&gt; stanza runs a health check on the service  Since the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; is registered to Consul, the health check runs on Consul. The above health check is configured to run every 10 seconds, and is given 5 seconds for the health check query to succeed. Health checks in Nomad are similar to &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command"&gt;Kubernetes liveness probes&lt;/a&gt;. Setting the &lt;code&gt;on_update&lt;/code&gt; attribute creates something closer to a &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes"&gt;Kubernetes readiness probe&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The service for the &lt;code&gt;grpc&lt;/code&gt; port looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice-grpc"&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;

  &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10s"&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5s"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since we’re not exposing any outside services, we don’t need the &lt;code&gt;tags&lt;/code&gt; attribute with the Traefik configurations.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;port&lt;/code&gt; attribute refers to the &lt;code&gt;grpc&lt;/code&gt; port that we defined in the &lt;code&gt;network&lt;/code&gt; stanza earlier.&lt;/li&gt;
&lt;li&gt;We’re doing the same health check that we did for the &lt;code&gt;http&lt;/code&gt; port.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For additional examples health checks, check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A gRPC health check in the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/recommendationservice.nomad#L25-L30"&gt;recommendationservice jobspec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A command-based health check to check database connectivity in the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/ffspostgres.nomad#L63-L74"&gt;ffspostgres jobspec&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5- Task Definition
&lt;/h3&gt;

&lt;p&gt;Okay…now we’re ready to define our &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;task&lt;/a&gt;. Since we’re running a containerized workload, our task uses the &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;Docker driver&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Config Stanza
&lt;/h4&gt;

&lt;p&gt;Since we’re using the &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;Docker driver&lt;/a&gt;, we need to provide the following information to Nomad via the &lt;code&gt;config&lt;/code&gt; stanza:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name of the Docker image.&lt;/strong&gt; We get this information from &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L858"&gt;spec -&amp;gt; template -&amp;gt; spec -&amp;gt; containers -&amp;gt; image&lt;/a&gt; in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;. In this case, the image name is &lt;code&gt;ghcr.io/open-telemetry/demo:v1.2.1-featureflagservice&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ports being used by the Docker image.&lt;/strong&gt; We get this information from &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L860-L865"&gt;spec -&amp;gt; template -&amp;gt; spec -&amp;gt; containers -&amp;gt; ports&lt;/a&gt; in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;. In this case, the image requires ports &lt;code&gt;50053&lt;/code&gt; and &lt;code&gt;8081&lt;/code&gt;, which we named &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;grpc&lt;/code&gt;, respectively, in our jobspec’s &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/network"&gt;network&lt;/a&gt; stanza
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-featureflagservice&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featureflagservice&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ghcr.io/open-telemetry/demo:v1.2.1-featureflagservice'&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This translates to the &lt;code&gt;config&lt;/code&gt; stanza of the &lt;code&gt;featureflagservice&lt;/code&gt; &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;task&lt;/a&gt; looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ghcr.io/open-telemetry/demo:v1.2.1-featureflagservice"&lt;/span&gt;
  &lt;span class="nx"&gt;image_pull_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"25m"&lt;/span&gt;
  &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since there are no &lt;a href="https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#define-a-command-and-arguments-when-you-create-a-pod"&gt;args&lt;/a&gt; present in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;, we’re omitting &lt;code&gt;args&lt;/code&gt; from this jobspec. If you’d like to see an example of a jobspec that uses &lt;code&gt;args&lt;/code&gt;, check out the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/prometheus.nomad#L27-L40"&gt;Prometheus jobspec&lt;/a&gt;, and its corresponding &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/2e467f23fdb6a236abc7effb037ef9330bbbb9f1/charts/opentelemetry-demo/examples/default/rendered/prometheus/deploy.yaml#L38-L46"&gt;Deployment YAML&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Since there is no &lt;a href="https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#define-a-command-and-arguments-when-you-create-a-pod"&gt;command&lt;/a&gt; present in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;, we’re omitting the &lt;code&gt;entrypoint&lt;/code&gt; from this jobspec.  If you’d like to see an example of a jobspec that uses an &lt;code&gt;entrypoint&lt;/code&gt;, check out the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/otel-collector.nomad#L54-L58"&gt;OTel Collector jobspec&lt;/a&gt;, and its corresponding &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/opentelemetry-collector/deployment.yaml#L41-L46"&gt;Deployment YAML&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker#infra_image_pull_timeout"&gt;image_pull_timeout&lt;/a&gt; is set to 25 minutes. This is an optional value, and if you leave it out, it defaults to 5 minutes. I set it to a high value because sometimes you just never know if your network decides to give you the finger, and &lt;a href="https://danielabaron.me/blog/nomad-tips-and-tricks/#increase-docker-pull-timeout"&gt;I don’t want the job to fail because it wasn’t able to pull the image&lt;/a&gt; within the allotted time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Env Stanza
&lt;/h4&gt;

&lt;p&gt;We’re not quite done with configuring our &lt;code&gt;featureflagservice&lt;/code&gt; &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;task&lt;/a&gt;. If you look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;, you’ll notice that there are a number of environment variables under the &lt;code&gt;env&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_SERVICE_NAME&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.labels['app.kubernetes.io/component']&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_NAMESPACE&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.namespace&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_NODE_NAME&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spec.nodeName&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_K8S_POD_NAME&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.name&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50053"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FEATURE_FLAG_SERVICE_PORT&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_EXPORTER_OTLP_TRACES_PROTOCOL&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecto://ffs:ffs@example-ffspostgres:5432/ffs&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://example-otelcol:4317&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service.name=$(OTEL_SERVICE_NAME),k8s.namespace.name=$(OTEL_K8S_NAMESPACE),k8s.node.name=$(OTEL_K8S_NODE_NAME),k8s.pod.name=$(OTEL_K8S_POD_NAME)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can ignore the ones that start with &lt;code&gt;OTEL_K8S_&lt;/code&gt;, as they are Kubernetes-specific; however we do care about these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OTEL_SERVICE_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FEATURE_FLAG_SERVICE_PORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OTEL_EXPORTER_OTLP_TRACES_PROTOCOL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DATABASE_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how do we configure these in Nomad? Through the &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;task&lt;/a&gt;’s &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/env"&gt;env&lt;/a&gt; stanza. Which means that our environment variables look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${NOMAD_PORT_grpc}"&lt;/span&gt;
  &lt;span class="nx"&gt;FEATURE_FLAG_SERVICE_PATH_ROOT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/feature&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;FEATURE_FLAG_SERVICE_PORT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${NOMAD_PORT_http}"&lt;/span&gt;
  &lt;span class="nx"&gt;OTEL_EXPORTER_OTLP_TRACES_PROTOCOL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;
  &lt;span class="nx"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service.name=featureflagservice"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rather than hard-coding the value of &lt;code&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/code&gt; to &lt;code&gt;50053&lt;/code&gt; and &lt;code&gt;8081&lt;/code&gt;, we’re using &lt;code&gt;NOMAD_PORT_grpc&lt;/code&gt; and &lt;code&gt;NOMAD_PORT_http&lt;/code&gt;. These are actually &lt;a href="https://developer.hashicorp.com/nomad/docs/runtime/environment"&gt;runtime environment variable&lt;/a&gt;, which follow the &lt;code&gt;NOMAD_PORT_&amp;amp;lt;label&amp;gt;&lt;/code&gt; naming convention. This prevents you from hard-coding the port number, which comes in handy if the port number changes in the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/network"&gt;network&lt;/a&gt; stanza for whatever reason, as you only need to change the number in one spot.&lt;/li&gt;
&lt;li&gt;If you look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;, you’ll notice that &lt;code&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt; is set to &lt;code&gt;service.name=$(OTEL_SERVICE_NAME),k8s.namespace.name=$(OTEL_K8S_NAMESPACE),k8s.node.name=$(OTEL_K8S_NODE_NAME),k8s.pod.name=$(OTEL_K8S_POD_NAME)&lt;/code&gt;. But I only set &lt;code&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt; to &lt;code&gt;service.name=featureflagservice&lt;/code&gt;. Why? Well, because the other attributes in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt; were Kubernetes-related, so I left them out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Template Stanza
&lt;/h4&gt;

&lt;p&gt;Wait…but why are &lt;code&gt;DATABASE_URL&lt;/code&gt; and &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; missing?? Well, if you look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/6fdb0d3d0e22e550c30c0d5c61f428bbe8ed3aa1/charts/opentelemetry-demo/examples/default/rendered/component.yaml#L829-L901"&gt;Deployment YAML&lt;/a&gt;, you’ll notice that the values for the above two environment variables are &lt;code&gt;ecto://ffs:ffs@example-ffspostgres:5432/ffs&lt;/code&gt; and &lt;code&gt;http://example-otelcol:4317&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;p&gt;Which begs the question...how does this translate to Nomad-speak? &lt;code&gt;example-ffspostgres&lt;/code&gt; and &lt;code&gt;example-otelcol&lt;/code&gt;, are the service names in Kubernetes for &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; and the &lt;a href="https://opentelemetry.io/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt;, respectively, so if we tried to use those same names in our jobspec definition, you’d get a big ‘ole nasty error from Nomad.&lt;/p&gt;

&lt;p&gt;We could use the IP addresses of those services, but that’s not such a great idea, because IP addresses for services are bound to change, so if and when that address changes, your jobspec will fail to deploy.&lt;/p&gt;

&lt;p&gt;What we need is a way to dynamically get a service’s IP address, given the service’s name. This is where &lt;a href="https://consul.io"&gt;Consul&lt;/a&gt; comes in. Among other things, &lt;a href="https://developer.hashicorp.com/consul/docs/concepts/service-discovery"&gt;Consul offers service discovery&lt;/a&gt;, which does exactly what we need.&lt;/p&gt;

&lt;p&gt;To use Consul service-discovery, we need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name of the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt; that we’re referencing&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/template"&gt;Nomad template stanza&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Nomad &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/template"&gt;template&lt;/a&gt; stanza is very reminiscent of a &lt;a href="https://kubernetes.io/docs/concepts/configuration/configmap/"&gt;Kubernetes ConfigMap&lt;/a&gt;. Per the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/template"&gt;Nomad docs&lt;/a&gt;, templates let you “ship configuration files that are populated from environment variables, Consul data, Vault secrets, or just general configurations within a Nomad task.” In our case, we’re using a template to query Consul services, so that we can find the IP address and port number of these services, so that we can populate our  to populate our two remaining environment variables,  &lt;code&gt;DATABASE_URL&lt;/code&gt; and &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt;. The code for that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{{ range service "ffspostgres-service" }}
DATABASE_URL = "ecto://ffs:ffs@{{ .Address }}:{{ .Port }}/ffs"
{{ end }}

{{ range service "otelcol-grpc" }}
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "http://{{ .Address }}:{{ .Port }}"
{{ end }}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;  &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local/env"&lt;/span&gt;
  &lt;span class="nx"&gt;env&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/template"&gt;template&lt;/a&gt; stanza is defined inside the &lt;a href="https://developer.hashicorp.com/nomad/docs/drivers/docker"&gt;task&lt;/a&gt; stanza.&lt;/li&gt;
&lt;li&gt;The lines &lt;code&gt;destination = "local/env"&lt;/code&gt; and &lt;code&gt;env = true&lt;/code&gt; tell Nomad that these are environment variables&lt;/li&gt;
&lt;li&gt;The line &lt;code&gt;{{ range service "ffspostgres-service" }}&lt;/code&gt; tells Nomad to look for a service in Consul called &lt;code&gt;ffspostgres-service&lt;/code&gt;. Once it finds the service name, we can pull the service’s IP address and port number using &lt;code&gt;{{ .Address }}&lt;/code&gt; and &lt;code&gt;{{ .Port }}&lt;/code&gt;, respectively.&lt;/li&gt;
&lt;li&gt;Similarly, the line &lt;code&gt;{{ range service "otelcol-grpc" }}&lt;/code&gt; tells Nomad to look for a service called &lt;code&gt;otelcol-grpc&lt;/code&gt;. Once it finds the service name, we can pull the service’s IP address and port number using &lt;code&gt;{{ .Address }}&lt;/code&gt; and &lt;code&gt;{{ .Port }}&lt;/code&gt;, respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But wait...where the heck do these service names come from?? Well, remember when we defined services in step 4 above, we gave each of our services a name?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ffspostgres-service&lt;/code&gt; is the name of the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/ffspostgres.nomad#L59-L74"&gt;PostgreSQL service&lt;/a&gt;. You can check out the Nomad service definition &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/ffspostgres.nomad#L59-L74"&gt;here&lt;/a&gt;. (&lt;strong&gt;Aside:&lt;/strong&gt; Take note of the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/service"&gt;service&lt;/a&gt;’s &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/ffspostgres.nomad#L63-L74"&gt;command-based health check to check database connectivity&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;otelcol-grpc&lt;/code&gt; is the name of the &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/otel-collector.nomad#L190-L199"&gt;gRPC service of the OpenTelemetry Collector&lt;/a&gt;. You can check out the service definition &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/otel-collector.nomad#L190-L199"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more info on Consul service discovery, check out &lt;a href="https://discuss.hashicorp.com/t/i-dont-understand-networking-between-services/24470/3"&gt;this HashiCorp discussion forum&lt;/a&gt;. In addition, Nomad now has native service discovery sans Consul. For more info, check out docs &lt;a href="https://developer.hashicorp.com/nomad/docs/networking/service-discovery"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For an example of using the &lt;code&gt;template&lt;/code&gt; stanza for configuration files, check out the OpenTelemetry Collector’s jobspec &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed12ec3d4092a7816aadd2d761a98f9ef51dfb74/otel-demo-app/jobspec/otel-collector.nomad#L89-L143"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Restart Rules
&lt;/h4&gt;

&lt;p&gt;Unlike Docker Compose, you can’t specify dependency between services in Nomad. So, in order to ensure that Service X doesn’t die on you because it’s dependent on Service Y, which hasn’t started yet, you can put a restart policy into place. Below is the restart policy that I configured for &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="nx"&gt;delay&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"15s"&lt;/span&gt;
  &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
  &lt;span class="nx"&gt;mode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"delay"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/restart"&gt;restart&lt;/a&gt; policy states that Nomad will try to restart the job 10 times in the span of 2 minutes. It will wait 15 seconds between restarts. If, after 10 attempts at a restart. By default, if the job still hasn’t started successfully, Nomad will fail the deployment and the job will be dead. This is dictated by the &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/restart#mode-values"&gt;mode&lt;/a&gt; attribute, which defaults to &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/restart#fail"&gt;fail&lt;/a&gt;. That’s not what we want, so instead we must set our &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/restart#mode-values"&gt;mode&lt;/a&gt; to &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/restart#delay-1"&gt;delay&lt;/a&gt;. This tells Nomad to restart the job another 10 times. This cycle continues on until the job finally starts up successfully.&lt;/p&gt;

&lt;h4&gt;
  
  
  Resource Allocations
&lt;/h4&gt;

&lt;p&gt;If you follow my &lt;a href="https://adri-v.medium.com/list/justintime-nomad-cc5d249a172b"&gt;writings on Nomad&lt;/a&gt;, you’ll know that I am a HUGE fan of using &lt;a href="https://rubiksqube.com/#/"&gt;HashiQube&lt;/a&gt; for running a Hashi environment on my local machine. This, of course, means that I have way less computing power than if I was, say, running this in Nomad in a datacenter. Which means that I have to be very mindful of the resources that I use, both for CPU and memory.&lt;/p&gt;

&lt;p&gt;To get the correct values for CPU and memory usage, I had to play around a little. First, I started by deploying the jobspecs without any resource allocations, and checked out the jobs in Nomad to see if I either over-allocated or under-allocated resources.&lt;/p&gt;

&lt;p&gt;For memory utilization, I looked at the resources consumed under the service’s allocation dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hxQaDWHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/0%2A625G90BvTghQm4IB" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hxQaDWHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/0%2A625G90BvTghQm4IB" alt="Screen capture of the Nomad UI, showing CPU and memory utilization for a given allocation in Nomad. The graphs for CPU and memory utilization have a red box around them." title="CPU and memory utilization on Nomad" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look at the above screen capture for the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;, you can see that I’m using about 60% of the memory that I allocated to this jobspec, which is pretty decent. If I deploy a service and see that it’s getting close to 100% memory usage (anything at 80% or above), I bump up the amount of memory used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.hashicorp.com/nomad/tutorials/manage-jobs/jobs-utilization"&gt;If you prefer the command line&lt;/a&gt;, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ALLOCATION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;nomad job allocs &lt;span class="nt"&gt;-json&lt;/span&gt; featureflagservice | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[0].ID'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
nomad alloc status &lt;span class="nt"&gt;-stats&lt;/span&gt; &lt;span class="nv"&gt;$ALLOCATION_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
Task "featureflagservice" is "running"
Task Resources:
CPU       Memory           Disk     Addresses
0/55 MHz  151 MiB/250 MiB  300 MiB  

Memory Stats
Cache  Swap  Usage
0 B    0 B   151 MiB

CPU Stats
Percent  Throttled Periods  Throttled Time
2.89%    0                  0
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the printout above CPU utilization is at 0 out of 55 MHz, and memory utilization is at 151 MiB out of 250 MiB.&lt;/p&gt;

&lt;p&gt;For CPU utilization, I look at Nomad’s &lt;a href="https://www.hashicorp.com/blog/see-your-entire-cluster-at-once-with-nomad-s-topology-visualization"&gt;Topology dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9rlmH_nX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/0%2AJLgBQorpXm-bWtM-" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9rlmH_nX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/0%2AJLgBQorpXm-bWtM-" alt="Screen capture of the Nomad UI showing the Topology view. This shows 75% memory used out of the 9.28GB RAM allocated to the Nomad, and 60% compute power out of the 2 GHz compute power allocated to Nomad." title="Nomad UI Topology view" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can see that for all of my services, I am using a grand total of 1.21 GHz of CPU for all of my jobspecs (all OTel Demo App jobspecs), out of my allotted 2 GHz (if you’re curious, I configured this setting &lt;a href="https://github.com/avillela/hashiqube/blob/9dd8e08febcb295b44d2a834e9f5e024841a9d00/hashicorp/nomad.sh#L59-L60"&gt;here&lt;/a&gt; in HashiQube). By looking at my service’s CPU utilization from the allocation’s Resource Utilization dashboard, and by looking at how much compute power I have from the Topology dashboard, I can play around with the CPU utilization to reach a value that won’t exhaust my allocated resources. As a general rule of thumb, I like to make sure that all of my services are using 60-75% of the allotted resources.&lt;/p&gt;

&lt;p&gt;So, with all that in mind, below are my &lt;a href="https://developer.hashicorp.com/nomad/docs/job-specification/resources"&gt;resources&lt;/a&gt; settings for the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/featureflagservice"&gt;featureflagservice&lt;/a&gt;, where CPU is measured in GHz, and memory is measured in MiB (&lt;a href="https://www.majordifferences.com/2018/03/differences-between-megabyte-and.html"&gt;mebibytes&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6- The Final Product!
&lt;/h3&gt;

&lt;p&gt;Now that we’ve got all of our pieces in place, our final jobspec looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service"&lt;/span&gt;
  &lt;span class="nx"&gt;datacenters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dc1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"host"&lt;/span&gt;

      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50053&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice-http"&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
      &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"traefik.http.routers.featureflagservice.rule=Host(`feature.localhost`)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"traefik.http.routers.featureflagservice.entrypoints=web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"traefik.http.routers.featureflagservice.tls=false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"traefik.enable=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10s"&lt;/span&gt;
        &lt;span class="nx"&gt;timeout&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5s"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice-grpc"&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;

      &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10s"&lt;/span&gt;
        &lt;span class="nx"&gt;timeout&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5s"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt;

      &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"otel/demo:v1.1.0-featureflagservice"&lt;/span&gt;
        &lt;span class="nx"&gt;image_pull_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10m"&lt;/span&gt;
        &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="nx"&gt;delay&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"15s"&lt;/span&gt;
        &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2m"&lt;/span&gt;
        &lt;span class="nx"&gt;mode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"delay"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;FEATURE_FLAG_GRPC_SERVICE_PORT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${NOMAD_PORT_grpc}"&lt;/span&gt;
        &lt;span class="nx"&gt;FEATURE_FLAG_SERVICE_PATH_ROOT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/feature&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;FEATURE_FLAG_SERVICE_PORT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${NOMAD_PORT_http}"&lt;/span&gt;
        &lt;span class="nx"&gt;OTEL_EXPORTER_OTLP_TRACES_PROTOCOL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grpc"&lt;/span&gt;
        &lt;span class="nx"&gt;OTEL_SERVICE_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"featureflagservice"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{{ range service "ffspostgres-service" }}
DATABASE_URL = "ecto://ffs:ffs@{{ .Address }}:{{ .Port }}/ffs"
{{ end }}

{{ range service "otelcol-grpc" }}
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "http://{{ .Address }}:{{ .Port }}"
{{ end }}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;        &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local/env"&lt;/span&gt;
        &lt;span class="nx"&gt;env&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;
        &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ta-da!! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Whew! We covered a lot today! At the end of the day, I hope that this shows you that converting a Kubernetes manifest to a Nomad jobspec is not rocket science! It just takes a little bit of knowledge and patience.&lt;/p&gt;

&lt;p&gt;Although this was by no means an exhaustive conversion, I hope that this little tutorial has given you the confidence to go from, “I wish that there was an example of how to run this on Nomad,” to, “I can get this to run in Nomad myself!”&lt;/p&gt;

&lt;p&gt;I shall now reward you with a picture of Phoebe and our dearly departed Bunny, peering out of their cage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ct9P-q8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/v1/images/image4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ct9P-q8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/v1/images/image4.jpg" alt="Two rats peering out of a cage: a white rat on the left, and a light brown and white rat on the right." title="Bunny (left) and Phoebe (right) say hello!" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IORd3AIW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/v1/images/image5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IORd3AIW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/v1/images/image5.png" alt="Peace sign, heart, and terminal" title="Peace, love, and code" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Got questions about Observability and/or OpenTelemetry? Want to collaborate on the OTel Demo App for Nomad? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://hachyderm.io/@adrianamvillela"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>hashicorp</category>
      <category>tutorial</category>
      <category>kubernetes</category>
      <category>nomad</category>
    </item>
    <item>
      <title>Running the OpenTelemetry Demo App on HashiCorp Nomad</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Thu, 15 Dec 2022 23:59:12 +0000</pubDate>
      <link>https://forem.com/avillela/running-the-opentelemetry-demo-app-on-hashicorp-nomad-4e1b</link>
      <guid>https://forem.com/avillela/running-the-opentelemetry-demo-app-on-hashicorp-nomad-4e1b</guid>
      <description>&lt;p&gt;Y’all...I’m so excited, because I finally got to work on an item on my tech bucket list. Last week, I began the process of translating &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry (OTel) Demo App&lt;/a&gt;’s &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;Helm Charts&lt;/a&gt; to &lt;a href="https://hashicorp.com"&gt;HashiCorp&lt;/a&gt; &lt;a href="https://nomadproj.io"&gt;Nomad&lt;/a&gt; job specs.&lt;/p&gt;

&lt;p&gt;Today I’ll be talking about how to run the OpenTelemetry Demo App on Nomad, using my favorite Hashi-in-a-box tool, &lt;a href="https://rubiksqube.com/#/"&gt;HashiQube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s do this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Assumptions
&lt;/h3&gt;

&lt;p&gt;Before we move on, I am assuming that you have a basic understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nomadproject.io"&gt;Nomad&lt;/a&gt;&lt;/strong&gt;. If not, head on over to my &lt;a href="https://adri-v.medium.com/just-in-time-nomad-80f57cd403ca"&gt;Nomad intro post&lt;/a&gt;. &lt;a href="https://danielabaron.me/blog/nomad-tips-and-tricks/"&gt;This blog post&lt;/a&gt; by
&lt;a href="https://danielabaron.me"&gt;Daniela Baron&lt;/a&gt; is also great.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability (o11y) and &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; (OTel)&lt;/strong&gt;. If not, check out this &lt;a href="https://dev.to/docs/concepts/observability-primer/"&gt;Observability primer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pre-Requisites
&lt;/h3&gt;

&lt;p&gt;In order to run the example in this tutorial, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docker.com"&gt;Docker&lt;/a&gt; (version 20.10.21 at the time of this writing)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vagrantup.com"&gt;Vagrant&lt;/a&gt; (version 2.3.1 at the time of this writing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tutorial Repos
&lt;/h3&gt;

&lt;p&gt;Below are the repos that we’ll be using for today’s tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My modified &lt;a href="https://github.com/avillela/hashiqube"&gt;HashiQube Repo&lt;/a&gt; (fork of &lt;a href="https://github.com/servian/hashiqube"&gt;servian/hashiqube&lt;/a&gt;). If you’re curious, you can see what modifications I’ve made &lt;a href="https://github.com/avillela/hashiqube"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;My &lt;a href="https://github.com/avillela/nomad-conversions"&gt;Nomad Conversions&lt;/a&gt; repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HashiQube Setup
&lt;/h3&gt;

&lt;p&gt;Before you start, just a friendly reminder that HashiQube by default runs &lt;a href="https://nomadproject.io"&gt;Nomad&lt;/a&gt;, &lt;a href="http://vaultproject.io"&gt;Vault&lt;/a&gt;, and &lt;a href="https://consul.io"&gt;Consul&lt;/a&gt; on Docker. In addition, we’ll be deploying 21 job specs to Nomad. This means that we’ll need a decent amount of CPU and RAM, so&lt;br&gt;
please make sure that you have enough resources allocated in your Docker desktop. For reference, I’m running an M1 Macbook Pro with 8 cores and 32 GB RAM. My Docker Desktop Resource settings are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPUs:&lt;/strong&gt; 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; 9.5GB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swap:&lt;/strong&gt; 3GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a screenshot of my Docker Preferences Resources settings, if you need a visual:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cHBsFVcu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/docker-desktop-resources-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cHBsFVcu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/docker-desktop-resources-config.png" alt="Screen capture of Docker Desktop resources preferences" title="Screen capture of Docker Desktop resources preferences" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more, check out the Docker docs on how to change your resources settings for &lt;a href="https://docs.docker.com/desktop/settings/mac/"&gt;Mac&lt;/a&gt;, &lt;a href="https://docs.docker.com/desktop/settings/windows/"&gt;Windows&lt;/a&gt;, and &lt;a href="https://docs.docker.com/desktop/settings/linux/"&gt;Linux&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  1- Update /etc/hosts
&lt;/h4&gt;

&lt;p&gt;We use the &lt;a href="https://traefik.io"&gt;Traefik&lt;/a&gt; load-balancer to expose our services, which we access as subdomains of localhost. In order ensure that we can access our Traefik-exposed services (and also the Traefik dashboard itself, you’ll need to add the following entries to &lt;code&gt;/etc/hosts&lt;/code&gt; on your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1   traefik.localhost
127.0.0.1   otel-demo.localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2- Provision a Local Hashi Environment with HashiQube
&lt;/h4&gt;

&lt;p&gt;Start HashiQube by following the detailed instructions&lt;br&gt;
&lt;a href="https://github.com/avillela/hashiqube#quickstart"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Be sure to check out the &lt;a href="https://github.com/avillela/hashiqube#gotchas"&gt;Gotchas&lt;/a&gt; section, if you get stuck.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once everything is up and running (this will take several minutes, by the way), you’ll see this in the tail-end of the startup sequence, to indicate that you are good to go:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L6eJWSiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/hashiqube-startup-sequence.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L6eJWSiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/hashiqube-startup-sequence.png" alt="Screen capture of the tail-end of the HashiQube startup sequence, displaying the URLs for Nomad, Nomad Documentation, Traefik, and Traefik documentation" title="Screen capture of the tail-end of the HashiQube startup sequence" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now access the apps using the URLs below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vault:&lt;/strong&gt; &lt;a href="http://localhost:8200"&gt;http://localhost:8200&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nomad:&lt;/strong&gt; &lt;a href="http://localhost:4646"&gt;http://localhost:4646&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consul:&lt;/strong&gt; &lt;a href="http://localhost:8500"&gt;http://localhost:8500&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traefik:&lt;/strong&gt; &lt;a href="http://traefik.localhost"&gt;http://traefik.localhost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don’t forget to download and install the&lt;br&gt;
&lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwww.nomadproject.io%2Fdownloads"&gt;Nomad CLI&lt;/a&gt;&lt;br&gt;
and the &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwww.vaultproject.io%2Fdownloads"&gt;Vault CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you need to SSH into HashiQube, open up a new terminal window on your host machine and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3- Deploy the OTel Demo App
&lt;/h4&gt;

&lt;p&gt;We’re finally ready to deploy the OTel Demo App!&lt;/p&gt;

&lt;p&gt;First, let’s clone the repo, and go to our working directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/avillela/nomad-conversions.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nomad-conversions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s enable&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/nomad/docs/commands/operator/scheduler/set-config#memory-oversubscription"&gt;Memory Oversubscription&lt;/a&gt; in Nomad. This is a one-time setting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad operator scheduler set-config &lt;span class="nt"&gt;-memory-oversubscription&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Memory Oversubscription allows Nomad to use more memory than is allotted to the job. For example, consider this setting in the &lt;code&gt;resources&lt;/code&gt; stanza:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;
   &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
   &lt;span class="nx"&gt;memory_max&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve allocated 55Mz of processing power to our job (&lt;code&gt;cpu&lt;/code&gt; setting), along with 1024MB RAM (&lt;code&gt;memory&lt;/code&gt; setting). In this case, when Memory Oversubscription is enabled, and the job requires more memory than the allotted 1024MB, Nomad will allocate as much as 2048MB RAM to the job (&lt;code&gt;memory_max&lt;/code&gt; setting). Note that if Memory Oversubscription is not enabled,Nomad will ignore the &lt;code&gt;memory_max&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;Next, let’s deploy the services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/traefik.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/redis.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/ffspostgres.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/otel-collector.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/adservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/cartservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/currencyservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/emailservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/featureflagservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/paymentservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/productcatalogservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/quoteservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/shippingservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/checkoutservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/recommendationservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontend.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/loadgenerator.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontendproxy.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/grafana.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/jaeger.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/prometheus.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we’re running the jobs in &lt;a href="https://developer.hashicorp.com/nomad/docs/commands/job/run#detach"&gt;detached mode&lt;/a&gt;, Nomad won’t wait to start the next job until the current one has deployed successfully. This means that your output will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job registration successful
Evaluation ID: d3eaa396-954e-241f-148d-6720c35f34bf
Job registration successful
Evaluation ID: 6bba875d-f415-36b7-bfeb-2ca4b9982acb
Job registration successful
Evaluation ID: 16dc8ef8-5e26-68f4-89b6-3d96b348775b
Job registration successful
Evaluation ID: 34de0532-a3b5-8691-bf18-51c0cc030573
Job registration successful
Evaluation ID: 7310e6a2-9945-710b-1505-c01bd58ccd35
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reminder that the &lt;code&gt;Evaluation ID&lt;/code&gt; values will be different on your machine.&lt;/p&gt;

&lt;h4&gt;
  
  
  4- See it in Nomad!
&lt;/h4&gt;

&lt;p&gt;As things are deploying, you can mozy on over to the Nomad UI at&lt;br&gt;
&lt;a href="http://localhost:4646"&gt;http://localhost:4646&lt;/a&gt; to see how things are coming along:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bn4KH70e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/nomad-jobs-startup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bn4KH70e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/nomad-jobs-startup.png" alt="Screen capture of jobs view in Nomad. Some jobs have started, and others are still starting up." title="Screen capture of jobs view in Nomad. Some jobs have started, and others are still starting up" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take some time for all of the services to come up (sometimes up to 10 minutes), especially since Nomad needs to download the images and initialize the services, so be patient! Since some services depend on other services in order to run, you may see services in limbo or some going up and down for a while, per the above screen capture. DON’T PANIC! IT WILL ALL BE OKAY!!&lt;/p&gt;

&lt;p&gt;Once all of the jobs are up and running, you’ll see everything look green, like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x84g_MzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/nomad-started.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x84g_MzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/nomad-started.png" alt="Screen capture of jobs view in Nomad, with all jobs started" title="Screen capture of jobs view in Nomad, with all jobs started" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also head on over to Consul at &lt;a href="http://localhost:8500"&gt;http://localhost:8500&lt;/a&gt; to see the health of the services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4I9AeOh8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/consul-service-health.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4I9AeOh8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/consul-service-health.png" alt="Screen capture of Consul service health. All services healthy." title="Screen capture of Consul service health. All services healthy." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, unhealthy services show up at the top, with a red “x” next to them. Since we don’t see any nasty red “x”s in the above screen shot, we know that our services are lookin’ good!&lt;/p&gt;
&lt;h4&gt;
  
  
  5- Access the OTel Demo App
&lt;/h4&gt;

&lt;p&gt;The OTel Demo App uses &lt;a href="https://www.envoyproxy.io"&gt;Envoy&lt;/a&gt; to expose a number of front-end services: the Webstore, &lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt;, &lt;a href="https://grafana.com/"&gt;Grafana&lt;/a&gt;, Load Generator, and Feature Flag. These are all managed by the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/frontendproxy.nomad"&gt;frontendproxy&lt;/a&gt; service. Traefik makes the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/frontendproxy.nomad"&gt;frontendproxy&lt;/a&gt; service available via the &lt;code&gt;otel-demo.localhost&lt;/code&gt; address.&lt;/p&gt;

&lt;p&gt;This is configured via the code snippet below, in the &lt;code&gt;service&lt;/code&gt; stanza of &lt;a href="https://github.com/avillela/nomad-conversions/blob/cefe9b9b12d84fb47be8aa5fc67b1b221b7b599b/otel-demo-app/jobspec/frontendproxy.nomad#L19-L24"&gt;frontendproxy.nomad&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;        &lt;span class="s2"&gt;"traefik.http.routers.frontendproxy.rule=Host(`otel-demo.localhost`)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.http.routers.frontendproxy.entrypoints=web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.http.routers.frontendproxy.tls=false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"traefik.enable=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;Host&lt;/code&gt; is set to &lt;code&gt;otel-demo.localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The services are accessed via the URLs below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webstore:&lt;/strong&gt; &lt;a href="http://otel-demo.localhost/"&gt;http://otel-demo.localhost/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--68mHDD-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/otel-demo-app-ui.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--68mHDD-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/otel-demo-app-ui.jpg" alt="Screen capture of the Demo App Webstore UI" title="Screen capture of the Demo App Webstore UI" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and explore the amazing selection of telescopes and accessories, and buy a few. 😉🔭&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jaeger UI:&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://otel-demo.localhost/jaeger/ui/"&gt;http://otel-demo.localhost/jaeger/ui/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f5Tepvhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/jaeger.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f5Tepvhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/jaeger.png" alt="Screen capture of the Jaeger UI" title="Screen capture of the Jaeger UI" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screen capture above, we can see a sample Trace from the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/checkoutservice.nomad"&gt;checkoutservice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grafana:&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://otel-demo.localhost/grafana/"&gt;http://otel-demo.localhost/grafana/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qLVDcNn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/grafana-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qLVDcNn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/grafana-1.png" alt="Screen capture of one of the OpenTelemetry dashboard on Grafana" title="Screen capture of one of the OpenTelemetry dashboard on Grafana" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fUfFAm_z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/grafana-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fUfFAm_z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/grafana-2.png" alt="Screen capture of the recommendationservice metrics dashboard on Grafana" title="Screen capture of the recommendationservice metrics dashboard on Grafana" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Demo App comes bundled with a two Grafana dashboards, which showcase Metrics emitted with OpenTelemetry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature Flags UI:&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://otel-demo.localhost/feature/"&gt;http://otel-demo.localhost/feature/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--61TQXN4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/featureflag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--61TQXN4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/featureflag.png" alt="Screen capture of featureflagservice UI" title="Screen capture of featureflagservice UI" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load Generator UI:&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://otel-demo.localhost/loadgen/"&gt;http://otel-demo.localhost/loadgen/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2PzqS5o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/loadgen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2PzqS5o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/loadgen.png" alt="Screen capture of loadgenerator UI" title="Screen capture of loadgenerator UI" width="800" height="965"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;While I think I’ve managed to iron out a lot of the kinks as far as running the OTel Demo App in Nomad, I have run into a few hiccups when deploying the services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Services sometimes can’t connect to the Collector
&lt;/h3&gt;

&lt;p&gt;Although all of the services appear to start up properly, in some cases, some services appear to be unable to connect to the OTel Collector. I haven’t quite figured out why this is happening, so for now, I just restart &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/otel-collector.nomad"&gt;otel-collector.nomad&lt;/a&gt;. If things are looking a little weird in the Webapp UI (like missing products or currency), I also restart &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/frontend.nomad"&gt;frontend.nomad&lt;/a&gt;. Usually a good indicator that services aren’t sending telemetry to the Collector is to look at the number of services showing up in Jaeger. You should see 14 services, including the &lt;code&gt;jaeger-query&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DImHcKEF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/jaeger-service-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DImHcKEF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/jaeger-service-list.png" alt="Screen capture of Jaeger service list drop-down" title="Screen capture of Jaeger service list drop-down" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Low memory on host machine
&lt;/h3&gt;

&lt;p&gt;Yup…as beefy as my machine is, I do also sometimes run low on memory on my host machine. It probably doesn’t help that I have a zillion tabs open in Chrome and Safari. Plus, let’s face it: HashiQube + 21 jobs in Nomad can be a bit memory intensive. I’ve made a few tweaks to the memory settings in HashiQube and Docker to try to minimize memory issues, but in case the Memory Monster gets you, I suggest closing browsers and other apps, and re-opening them to free up some memory. And if this does happen to you, please let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  A Work in Progress
&lt;/h2&gt;

&lt;p&gt;Please bear in mind that this project is a work in progress. If you have any suggestions for improvement, or would like to collaborate further on the Nomad jobspecs, please &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;hit me up&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Well, there you have it, folks! You now have an example of how to deploy &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/main/docs/kubernetes_deployment.md"&gt;OpenTelemetry Demo App&lt;/a&gt; (a multi-micro-service app running OpenTelemetry) to HashiCorp Nomad. Main highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We used &lt;a href="https://github.com/avillela/hashiqube"&gt;HashiQube&lt;/a&gt; to stand up a local HashiCorp environment in Docker via Nomad so that we could run the OTel Demo App in Nomad using &lt;a href="https://traefik.io"&gt;Traefik&lt;/a&gt; as our load balancer.&lt;/li&gt;
&lt;li&gt;We saw the OTel Demo App in action, by accessing the following services exposed through the
&lt;a href="https://github.com/avillela/nomad-conversions/blob/cefe9b9b12d84fb47be8aa5fc67b1b221b7b599b/otel-demo-app/jobspec/frontendproxy.nomad"&gt;frontendproxy&lt;/a&gt;: &lt;a href="http://otel-demo.localhost/"&gt;Webstore&lt;/a&gt;, &lt;a href="http://otel-demo.localhost/grafana/"&gt;Grafana&lt;/a&gt;, &lt;a href="http://otel-demo.localhost/jaeger/ui"&gt;Jaeger&lt;/a&gt;, &lt;a href="http://otel-demo.localhost/feature/"&gt;Feature Flags UI&lt;/a&gt;,and the &lt;a href="http://otel-demo.localhost/loadgen/"&gt;Load Generator UI&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before I wrap this up, I do want to give a HUGE shoutout to &lt;a href="https://www.linkedin.com/in/luizaoqui/"&gt;Luiz Aoqui&lt;/a&gt; of HashiCorp, who helped me tweak my Nomad jobspecs, and to&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/riaannolan/"&gt;Riaan Nolan&lt;/a&gt;, for his continued work on HashiQube.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;/strong&gt; Both &lt;a href="https://oncallmemaybe.com/episodes/opentelemetry-nomad-with-luiz-aoqui-of-hashicorp"&gt;Luiz&lt;/a&gt; and &lt;a href="https://oncallmemaybe.com/episodes/adventures-in-open-source-software-with-riaan-nolan-of-servian"&gt;Riaan&lt;/a&gt; were my guests on the &lt;a href="https://oncallmemaybe.com"&gt;On-Call Me Maybe Podcast&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will now leave you with a picture of Phoebe the rat, peering out of a pink basket. Doesn’t she look cute? 🥰&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--niZOrT1U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/phoebe-basket.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--niZOrT1U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://opentelemetry.io/blog/2022/otel-demo-app-nomad/phoebe-basket.jpg" alt="Light brown and white rat peering out of a pink wicker basket" title="Phoebe the rat peers out of a pink wicker basket" width="800" height="860"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Have questions about the OTel Demo App on Nomad? Feel free to connect through &lt;a href="https://hachyderm.io/@adrianamvillela"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;The OpenTelemetry community is always looking for contributions! &lt;a href="https://github.com/open-telemetry/community"&gt;Join us&lt;/a&gt;! If you're on Mastodon, be sure&lt;br&gt;
to follow [OpenTelemetry on Mastodon (&lt;a href="https://fosstodon.org/@opentelemetry"&gt;https://fosstodon.org/@opentelemetry&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opentelemetry</category>
      <category>hashicorp</category>
      <category>nomad</category>
    </item>
    <item>
      <title>Three Terraform Mistakes, and How to Avoid Them</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 21 Nov 2022 15:56:17 +0000</pubDate>
      <link>https://forem.com/lightstep/three-terraform-mistakes-and-how-to-avoid-them-88a</link>
      <guid>https://forem.com/lightstep/three-terraform-mistakes-and-how-to-avoid-them-88a</guid>
      <description>&lt;p&gt;In my &lt;a href="https://lightstep.com/blog/observability-as-code-with-kubernetes-and-lightstep"&gt;last blog post&lt;/a&gt;, I talked about how &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt; and I used &lt;a href="https://terraform.io"&gt;Terraform&lt;/a&gt; to show off &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code"&gt;Observability-Landscape-as-Code&lt;/a&gt; in practice, leveraging the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to do so. The Demo App showcases instrumentation of &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; of different services written in different languages using &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; (OTel). Our Terraform code did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Deployed the Demo App to &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deployed &lt;a href="https://opentelemetry.io/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt; to &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;, and configured it to send Traces and Metrics to &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Created dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I’m a fan of beautiful code, so we organized our code using &lt;a href="https://developer.hashicorp.com/terraform/language/modules"&gt;Terraform Modules&lt;/a&gt;. We used a module for provisioning the Kubernetes cluster, one for deploying the OTel Demo App and the OTel Collector, and one for creating the Lightstep dashboards.&lt;/p&gt;

&lt;p&gt;We also leveraged the following Terraform &lt;a href="https://developer.hashicorp.com/terraform/language/providers"&gt;Providers&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs"&gt;Google Cloud Provider&lt;/a&gt; for spinning up a Kubernetes Cluster in &lt;a href="https://cloud.google.com/"&gt;Google Cloud Platform (GCP)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs"&gt;Kubernetes Provider&lt;/a&gt; and the &lt;a href="https://registry.terraform.io/providers/hashicorp/helm/latest/docs"&gt;Helm Provider&lt;/a&gt; for deploying the app to Kubernetes&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Provider&lt;/a&gt; for creating the dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All good, right? Except for one teeeensy little problem...the last time I’d touched Terraform was in early 2021, and even then, I was just tweaking code. So I kinda had to teach myself Terraform all over again. And I hit up a few snags along the way. Cue. The. Panic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhfmitip26xra66pnx08.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhfmitip26xra66pnx08.jpg" alt='Home Alone meme: Kevin with hands to his face screaming. Text: "OMG Terraform is mad at me"' title="Home Alone meme: Kevin with hands to his face screaming. Text: 'OMG Terraform is mad at me'. URL: https://imgflip.com/i/709pup" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, Google came through, and we were able to resolve the issues. In today’s blog post, I will cover THREE Terraform gotchas that Ana and I hit, and how we solved them, so that you will hopefully be spared our utter despair and panic. 😅&lt;/p&gt;

&lt;p&gt;Let’s do this!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt;  If you want to follow along to see the full Terraform source code, you can check it out &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo"&gt;here&lt;/a&gt;. Even though the source code is specific to the Observability-Landscape-as-Code use case, the main Terraform concepts in this blog post can be ported over to other scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha #1: The Chicken-and-Egg Scenario
&lt;/h2&gt;

&lt;p&gt;After creating a Kubernetes cluster, we needed to create a Kubernetes resource before we could apply the Helm chart to install the OpenTelemetry demo app. The &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;Demo App’s Helm Chart&lt;/a&gt; deploys an OpenTelemetry Collector. We wanted to configure the Collector to send OTel data to Lightstep. To do so, you need to add a &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;, which is stored as a &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Kubernetes secret&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can learn more about the specifics of this setup &lt;a href="https://medium.com/dev-genius/running-opentelemetry-demo-app-in-kubernetes-95dccd613e0b"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To create the secret in Kubernetes before running the Helm Chart, we used the &lt;a href="https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs"&gt;Kubernetes Provider&lt;/a&gt;. In order to use this provider, Terraform needs to know information about your cluster, so that it knows what cluster to apply the manifest to. To do this, I needed to store the cluster  information in the &lt;code&gt;data&lt;/code&gt; stanza, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_client_config"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_container_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"primary"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
 &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;host&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://${data.google_container_cluster.primary.endpoint}"&lt;/span&gt;
 &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_client_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
 &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_container_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;master_auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy peasey, right? Unfortunately, when I ran &lt;code&gt;terraform apply&lt;/code&gt;, I kept getting the following errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Invalid template interpolation value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Attempt to index null value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, Terraform was trying to evaluate the contents of the &lt;code&gt;data&lt;/code&gt; stanza (which were null) before it had any information about the Kubernetes cluster. Which of course it didn’t, because the cluster didn’t yet exist!! Hence the null contents. &lt;/p&gt;

&lt;p&gt;I frantically Googled this one for a while, spinning my wheels. And then, the “aha” moment  hit me, when I saw somewhere in one of my searches that I could use the  &lt;code&gt;depends_on&lt;/code&gt; attribute in the &lt;code&gt;data&lt;/code&gt; stanza. So I added &lt;code&gt;depends_on = [module.k8s_cluster_create]&lt;/code&gt; to both my &lt;code&gt;data&lt;/code&gt; stanzas, which basically says, “Hey buddy, don’t try to evaluate this until AFTER the &lt;code&gt;k8s_cluster_create module&lt;/code&gt; (i.e. the module in which the Kubernetes cluster is created) is run. So now, after adding &lt;code&gt;depends_on&lt;/code&gt;, my &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L32-L49"&gt;providers.tf (lines 32-49)&lt;/a&gt; code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_client_config"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_container_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"primary"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
 &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;host&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://${data.google_container_cluster.primary.endpoint}"&lt;/span&gt;
 &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_client_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
 &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_container_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;master_auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after making that change, all was well with the world. Huzzah!&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #2: Using Modules with depends_on
&lt;/h2&gt;

&lt;p&gt;While the above problem went away, I then found myself face-to-face with yet another conundrum. When I initially wrote my Terraform code, everything was in one big file, and it worked just fine. So OF COURSE I just assumed that when I prettified my code and moved things into modules, I could just get away defining my &lt;a href="https://developer.hashicorp.com/terraform/language/providers"&gt;Providers&lt;/a&gt; in the Modules themselves. Well, you can. That is…if you don’t use the &lt;code&gt;depends_on&lt;/code&gt; attribute in your Module call.&lt;/p&gt;

&lt;p&gt;So basically, when I tried to say that the Module &lt;code&gt;lightstep_dashboards&lt;/code&gt; depended on &lt;code&gt;k8s_cluster_create&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"k8s_cluster_create"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/k8s"&lt;/span&gt;

   &lt;span class="nx"&gt;cluster_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
   &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
   &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
   &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;
   &lt;span class="nx"&gt;subnet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"deploy_otel_demo_app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/otel_demo_app"&lt;/span&gt;

   &lt;span class="nx"&gt;otel_demo_namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;otel_demo_namespace&lt;/span&gt;
   &lt;span class="nx"&gt;ls_access_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ls_access_token&lt;/span&gt;
   &lt;span class="nx"&gt;cluster_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
   &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
   &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
   &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;
   &lt;span class="nx"&gt;subnet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"lightstep_dashboards"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/lightstep"&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

   &lt;span class="nx"&gt;lightstep_project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ls_project&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I kept getting this error when I ran &lt;code&gt;terraform apply&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Module is incompatible with count, for_each and depends_on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error happens when the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Module&lt;/a&gt; contains a &lt;code&gt;provider&lt;/code&gt; block and the Module that you’re trying to call is using &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;depends_on&lt;/code&gt;, and/or &lt;code&gt;for_each&lt;/code&gt;. Why? Because &lt;code&gt;provider&lt;/code&gt; blocks inside a &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Module&lt;/a&gt; are not allowed when your Module call is using &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;depends_on&lt;/code&gt;, and/or &lt;code&gt;for_each&lt;/code&gt;. You can read up more on this &lt;a href="https://github.com/hashicorp/terraform/issues/31081#issuecomment-1131908824"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Well, it turns out that correct practice is to define your &lt;code&gt;provider&lt;/code&gt; block in the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#the-root-module"&gt;Root Module&lt;/a&gt;, as Providers are automagically passed down to the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Modules&lt;/a&gt;. So to make the above error go away, I &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/providers.tf"&gt;moved all of my Provider definitions to the Root Module&lt;/a&gt;, and was able to &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/main.tf"&gt;keep depends_on in my Module call&lt;/a&gt;. If I didn’t have any dependencies, I could’ve left out the &lt;code&gt;depends_on&lt;/code&gt; block, but I wouldn’t really be following the recommended  practice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE&lt;/em&gt;&lt;/strong&gt;: You can learn more about Providers and Modules &lt;a href="https://developer.hashicorp.com/terraform/language/modules/develop/providers"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha #3: Referencing a non-TF provider in a module
&lt;/h2&gt;

&lt;p&gt;Two problems down. Awesome! Unfortunately, my problems were not over. I continued to anger the Module Gods, because I encountered yet another issue when I moved my non-modularized code into Modules. This time, it had to do with using the &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Provider&lt;/a&gt;. You see, this Provider comes from a third-party (i.e. not HashiCorp), which in this case is Lightstep. Lightstep is what is known as a &lt;a href="https://developer.hashicorp.com/terraform/registry/providers#provider-tiers-namespaces"&gt;Partner Provider&lt;/a&gt;.  This means that in the &lt;a href="https://registry.terraform.io/browse/providers"&gt;Provider Registry&lt;/a&gt;, the Provider is named &lt;code&gt;lightstep/lightstep&lt;/code&gt;, where the first &lt;code&gt;lightstep&lt;/code&gt; means that the Provider is created and maintained by Lightstep, and the second &lt;code&gt;lightstep&lt;/code&gt; is the actual Provider name. For comparison, the &lt;code&gt;hashicorp/google&lt;/code&gt; provider is an &lt;a href="https://developer.hashicorp.com/terraform/registry/providers#provider-tiers-namespaces"&gt;Official Provider&lt;/a&gt;, because it is created and maintained by HashiCorp.&lt;/p&gt;

&lt;p&gt;Now here’s the odd part. When I tried to run &lt;code&gt;terraform init&lt;/code&gt;, I was graced with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Failed to query available provider packages

Could not retrieve the list of available versions for provider hashicorp/lightstep 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um...what? This did not compute, because in my &lt;code&gt;providers.tf&lt;/code&gt; file, I &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L12-L15"&gt;CLEARLY said that the Provider name was lightstep/lightstep&lt;/a&gt;, so where oh where was it getting this &lt;code&gt;hashicorp/lightstep&lt;/code&gt; business from?? LOOK ⬇️⬇️⬇️&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
   &lt;span class="nx"&gt;lightstep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lightstep/lightstep"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;=1.70.0"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Google gods, help meeeeee!!&lt;/p&gt;

&lt;p&gt;Well, it turns out that when using a Partner Provider in a Module, Terraform assumes the Provider is an Official Provider, and is therefore automagically given a &lt;code&gt;hashicorp&lt;/code&gt; suffix when passing it down to the Module. So Terraform basically thought that the Provider was called &lt;code&gt;hashicorp/lightstep&lt;/code&gt;, even though I &lt;em&gt;clearly&lt;/em&gt; defined it correctly in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L12-L15"&gt;Providers section of the Root Module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To fix this issue, I ended up having to define a &lt;code&gt;required_providers&lt;/code&gt; stanza in the Root Module, as I had already done, AND I also had to add a &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/modules/lightstep/providers.tf"&gt;required_providers stanza to my Child Module&lt;/a&gt;, as per the snippet  below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;lightstep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lightstep/lightstep"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;=1.70.0"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, my &lt;code&gt;terraform init&lt;/code&gt; stopped screaming at me!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5m8nvnqxduyciiazolf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5m8nvnqxduyciiazolf.jpg" alt='Jimmy Fallon in front of blue curtain saying "Phew!"' title="Jimmy Fallon in front of blue curtain saying 'Phew!' Image source: https://images.app.goo.gl/7XyDJJ88DHzcXhrJ7" width="479" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Today we learned that Terraform can be a wee finicky. We learned that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;code&gt;depends_on&lt;/code&gt; to the &lt;code&gt;data&lt;/code&gt; stanza used to capture your Kubernetes cluster configuration data ensures that Terraform doesn’t try to evaluate the &lt;code&gt;data&lt;/code&gt; stanza until AFTER the cluster is created, thereby avoiding some serious Terraform Anger™.&lt;/li&gt;
&lt;li&gt;If you want to use &lt;code&gt;depends_on&lt;/code&gt; in a Module call, the Provider configuration must be done in the Root Module. Also, it’s the recommended practice even if you don’t want to use &lt;code&gt;depends_on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you have a Module that references a Partner Provider, you need to define a &lt;code&gt;required_providers&lt;/code&gt; stanza in both the Root Module and the Child Module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope that these tips prevent you from experiencing Terraform Anguish™  next time you find yourself Terraformin’. And now, I shall reward you with a picture of my rat Mookie, who is seen below peering out of an authentic &lt;a href="https://cheesehead.com"&gt;Wisconsin Cheese Head&lt;/a&gt; hat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h_glAxog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/4WjpdboNgYiF77IJZvfCn5/7a6221a74ab020611ccb138fac30aa87/mookie-cheese-hat.JPG%3Fw%3D3520%26h%3D1980%26q%3D50%26fm%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h_glAxog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/4WjpdboNgYiF77IJZvfCn5/7a6221a74ab020611ccb138fac30aa87/mookie-cheese-hat.JPG%3Fw%3D3520%26h%3D1980%26q%3D50%26fm%3Dwebp" alt="Mookie the rat peering out of a Cheese Head hat. Image by Adriana Villela" title="Mookie the rat peering out of a Cheese Head hat. Image by Adriana Villela" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about Terraform or Observability-Landscape-as-Code? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt;, &lt;a href="https://hachyderm.io/web/@adrianamvillela#"&gt;Mastodon&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>terraform</category>
      <category>tech</category>
      <category>hashicorp</category>
    </item>
    <item>
      <title>Observability-Landscape-as-Code in Practice</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Tue, 15 Nov 2022 15:47:02 +0000</pubDate>
      <link>https://forem.com/lightstep/observability-landscape-as-code-in-practice-4co3</link>
      <guid>https://forem.com/lightstep/observability-landscape-as-code-in-practice-4co3</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;with &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you follow &lt;a href="https://lightstep.com/blog/authors/adriana-villela"&gt;Adriana's writings on Observability&lt;/a&gt;, you may recall a post from back in June introducing the concept of &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code"&gt;Observability-Landscape-as-Code (OLaC)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An Observability Landscape is made up of the following pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application instrumentation&lt;/li&gt;
&lt;li&gt;Collecting and storing application telemetry&lt;/li&gt;
&lt;li&gt;An Observability back-end&lt;/li&gt;
&lt;li&gt;A set of meaningful SLOs&lt;/li&gt;
&lt;li&gt;Alerts for on-call Engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4ZXBvcXtkWVjEBnYNP1pEf/d2c9262fe98996e4ae9f875a43e1514b/o11y-landscape.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4ZXBvcXtkWVjEBnYNP1pEf/d2c9262fe98996e4ae9f875a43e1514b/o11y-landscape.png" alt="The Observability landscape is made up of Collecting and storing application telemetry, An Observability back-end, A set of meaningful SLOs, Alerts for on-call Engineers" title="The Observability landscape is made up of Collecting and storing application telemetry, An Observability back-end, A set of meaningful SLOs, Alerts for on-call Engineers. Image by Adriana Villela."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keeping that in mind, OLaC is simply the codification of your Observability Landscape, thereby ensuring consistency, maintainability, and reproducibility.&lt;/p&gt;

&lt;p&gt;That’s all well and good, but how about seeing this thing in action? Well, my friend, you’ve come to the right place, because today, you get to see a tutorial featuring a number of OLaC practices in action!&lt;/p&gt;

&lt;p&gt;Today, &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt; (my fellow DevRel and partner in crime) and I will be highlighting the following aspects of OLaC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#instrument-your-code"&gt;Application instrumentation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How?&lt;/strong&gt; À la &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;, via the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;, which contains examples of &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; instrumentation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#use-the-otel-collector--codify-its-deployment"&gt;Collecting &amp;amp; storing application telemetry&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How:&lt;/strong&gt; &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OpenTelemetry Collector&lt;/a&gt; is deployed via code (Helm chart), alongside the various services that make up the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#codify-observability-back-end-configuration"&gt;Codifying your Observability back-end configuration&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How:&lt;/strong&gt; Using the &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Terraform Provider&lt;/a&gt; to create dashboards in &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We wanted to showcase OLaC principles with a real-life example using modern cloud-native tooling...Which means using &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; for our cloud infrastructure with &lt;a href="https://cloud.google.com/gcp"&gt;Google Cloud&lt;/a&gt;’s Kubernetes offering, &lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE&lt;/a&gt;. Now, since we are good practitioners of OLaC and SRE, we won’t just be setting things up through the clickity click of a UI. No sirreee. Instead, we’ll be #automatingAllTheThings using &lt;a href="https://hashicorp.com"&gt;HashiCorp&lt;/a&gt; &lt;a href="https://terraform.io"&gt;Terraform&lt;/a&gt;. Terraform allows us to do infrastructure-as-code (IaC), and gives us tons of added benefits like better control over our resources and standardization. These are key principles in OLaC and IaC.&lt;/p&gt;

&lt;p&gt;We will be deploying &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to our cluster. The Demo App has been instrumented using &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;, and will send &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; through the &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OpenTelemetry Collector&lt;/a&gt; to Lightstep.&lt;/p&gt;

&lt;p&gt;Are you ready??? Let’s get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  Tutorial
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.lightstep.com/signup/developer?signup_source=docs"&gt;Lightstep account&lt;/a&gt; so you can see application Traces, and Metrics dashboards&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt; for the &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt; project you would like to use&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-api-keys"&gt;Lightstep API key&lt;/a&gt; for creating dashboards in &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/downloads"&gt;Terraform CLI&lt;/a&gt; to run the Terraform scripts&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://cloud.google.com/gcp"&gt;Google Cloud&lt;/a&gt; account, so you can create a Kubernetes cluster (&lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk/docs/install-sdk"&gt;gcloud CLI&lt;/a&gt; to interact with Google Cloud&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/"&gt;kubectl&lt;/a&gt; to interact with Kubernetes&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In today’s tutorial, we’ll be running Terraform code to showcase OLaC in action. For convenience, the main components have been broken up into separate &lt;a href="https://www.terraform.io/language/modules"&gt;Terraform modules&lt;/a&gt; that will do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Kubernetes cluster (GKE) in Google Cloud using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs"&gt;Google Terraform Provider&lt;/a&gt;. This is defined in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/k8s"&gt;k8s&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;li&gt;Deploy the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to the cluster using the &lt;a href="https://registry.terraform.io/providers/hashicorp/helm/latest/docs"&gt;Helm Terraform Provider&lt;/a&gt;. This is defined &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/otel_demo_app"&gt;otel_demo_app&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;li&gt;Create Metrics dashboards in &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Terraform Provider&lt;/a&gt;. This is defined in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/lightstep"&gt;lightstep&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full code listing for this tutorial can be found &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/k8s-cluster-with-otel-demo"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1- Clone the example repo
&lt;/h3&gt;

&lt;p&gt;Let's start by cloning the example repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lightstep/unified-observability-k8s-kubecon.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2- Initialize Sub-Modules
&lt;/h3&gt;

&lt;p&gt;This project makes use of a few &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules"&gt;Git submodules&lt;/a&gt;, so in order to ensure that things work nicely,  you’ll need to pull them in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;unified-observability-k8s-kubecon
git submodule init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git submodule update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3- Google Cloud Login
&lt;/h3&gt;

&lt;p&gt;Before we can create a GKE cluster you must authenticate your Google Cloud account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth application-default login &lt;span class="nt"&gt;--no-launch-browser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be presented with a link which you need to open up in a browser, to authenticate your Google ID. Once you are authenticated, the browser will display an authorization token for you to paste in the command line, as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7aJFKi4LN8L4N6fgcDbauS/5fd36e469285896c26ab5624de73151d/gcloud-auth-prompt.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7aJFKi4LN8L4N6fgcDbauS/5fd36e469285896c26ab5624de73151d/gcloud-auth-prompt.png" alt="gcloud auth prompt" title="gcloud auth prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4- Create terraform.tfvars
&lt;/h3&gt;

&lt;p&gt;Now that you’re authenticated, let’s get ready to Terraform! Before you can do that, we need to create a &lt;code&gt;terraform.tfvars&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Lucky for you, we have a handy-dandy template that you can use to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;k8s-cluster-with-otel-demo/terraform
&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.template terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next populate the following values in the file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_gcp_project&amp;gt;&lt;/code&gt;: The name of your Google Cloud project. Don’t know your project name? No problem! Just run &lt;code&gt;gcloud config get-value project&lt;/code&gt; to find out what it is!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_gke_cluster_name&amp;gt;&lt;/code&gt;: The name you wish to give your GKE cluster. Make sure it follows Kubernetes cluster naming conventions (i.e. no underscores &lt;code&gt;_&lt;/code&gt; or special characters).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_access_token&amp;gt;&lt;/code&gt;: Your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;. This is used to send Traces to your &lt;a href="https://docs.lightstep.com/docs/create-projects-for-your-environments"&gt;Lightstep Project&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_api_key&amp;gt;&lt;/code&gt;: Your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-api-keys"&gt;Lightstep API key&lt;/a&gt;. This is used to create our Metrics dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_org_name&amp;gt;&lt;/code&gt;: Your Lightstep organization name. Not sure what your organization is called? No problem! Log into Lightstep,and click on the person icon on the bottom left of your screen. This will pop up a little menu. The organization name can be found under the “Account Management” heading, like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/62bgzto893vBqq06so4A24/38ae8e8820b66d76df610cb9335888c7/ls_org_name.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/62bgzto893vBqq06so4A24/38ae8e8820b66d76df610cb9335888c7/ls_org_name.png" alt="Finding your Lightstep organization name" title="Finding your Lightstep organization name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that my organization is called “LightStep”. Yours will be different. Note also that Organization names are case-sensitive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightsep_project_name&amp;gt;&lt;/code&gt;: The name of the &lt;a href="https://docs.lightstep.com/docs/create-projects-for-your-environments"&gt;Lightstep Project&lt;/a&gt; where the Metrics dashboards will be displayed.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;terraform.tfvars&lt;/code&gt; is in &lt;code&gt;.gitignore&lt;/code&gt; and won't be put into version control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5- Run Terraform
&lt;/h3&gt;

&lt;p&gt;This step will initialize Terraform (install providers locally), and then will apply the Terraform plan. &lt;/p&gt;

&lt;p&gt;It will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Deploy the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; using the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;OpenTelemetry Demo Helm Chart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before running the commands below, make sure that you’re already in the &lt;code&gt;k8s-cluster-with-otel-demo/terraform&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform apply &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that this step may take up to 30 minutes, depending on GKE’s disposition. Be patient. 😄&lt;/p&gt;

&lt;h3&gt;
  
  
  6- Update your kubeconfig
&lt;/h3&gt;

&lt;p&gt;Now that the cluster is created, you can add it to your &lt;code&gt;kubeconfig&lt;/code&gt; file! By default, the file is saved at &lt;code&gt;$HOME/.kube/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before you can update your &lt;code&gt;kubeconfig&lt;/code&gt;, you first need to make sure that you have the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#install_plugin"&gt;gke-gcloud-auth-plugin&lt;/a&gt; installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud components &lt;span class="nb"&gt;install &lt;/span&gt;gke-gcloud-auth-plugin
gke-gcloud-auth-plugin &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export USE_GKE_GCLOUD_AUTH_PLUGIN=True"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add the cluster to &lt;code&gt;kubeconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials &lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; kubernetes_cluster_name&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; region&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets the &lt;code&gt;kubernetes_cluster_name&lt;/code&gt; and &lt;code&gt;region&lt;/code&gt; output values from Terraform (that’s the &lt;code&gt;terraform output -raw&lt;/code&gt; stuff), and plunks those into your &lt;code&gt;gcloud container clusters get-credentials&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Or, if you closed the terminal in which you were running Terraform and lost your output values, you can also do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials &amp;lt;cluster_name&amp;gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;cluster_name&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;region&amp;gt;&lt;/code&gt; correspond to the values you entered in Step 3 in your &lt;code&gt;terraform.tfvars&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  7- Check out the OTel Demo app
&lt;/h3&gt;

&lt;p&gt;Now that you’ve got the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; deployed to your cluster, let’s take a look at it! First, let’s peek into Kubernetes to see what’s up.&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;kubectl get ns&lt;/code&gt;, you’ll notice that there’s now a new namespace called &lt;code&gt;otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7Amz3cvi7YGA9sW4ZyWDID/9d040359f783953cdec7443c6b9d6a3c/kubectl-get-ns.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7Amz3cvi7YGA9sW4ZyWDID/9d040359f783953cdec7443c6b9d6a3c/kubectl-get-ns.png" alt='Results of running "kubectl get ns"' title="Results of running 'kubectl get ns'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where we deployed the OTel Demo app. Let’s look into this namespace to see what we’ve created. First, let’s look at the pods with &lt;code&gt;kubectl get pods -ns otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3Vjy8eS8TcD7DJzIO1BMCm/c99bb85e247233c9b70d74afa7a7dd3c/kubectl-get-pods.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3Vjy8eS8TcD7DJzIO1BMCm/c99bb85e247233c9b70d74afa7a7dd3c/kubectl-get-pods.png" alt='Results of running "kubectl get pods"' title="Results of running 'kubectl get pods'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how we deployed a bunch of different services that make up the OTel Demo App, including &lt;code&gt;adservice&lt;/code&gt;, &lt;code&gt;cartservice&lt;/code&gt;, &lt;code&gt;recommendationservice&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;We also deployed an &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OTel Collector&lt;/a&gt;. Its configuration YAML is stored in a configmap. We can take a peek by running &lt;code&gt;kubectl describe configmap otel-demo-app-otelcol -n otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7nzEZ5yat0sF2rgIpS4lGT/aa3b72dd3b8d32a023854f3a3512468b/kubectl-get-configmap.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7nzEZ5yat0sF2rgIpS4lGT/aa3b72dd3b8d32a023854f3a3512468b/kubectl-get-configmap.png" alt='Results of running "kubectl describe configmap"' title="Results of running 'kubectl describe configmap'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that we also reference a variable called &lt;code&gt;${LS_TOKEN}&lt;/code&gt; which represents your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;, which you set in &lt;code&gt;terraform.tfvars&lt;/code&gt;. But where is it? The secret is mounted to the OTel Collector container instance as a secret called &lt;code&gt;otel-collector-secret&lt;/code&gt;. Let’s take a look at the secret by running &lt;code&gt;kubectl describe secret otel-collector-secret -n otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3t5WRDOBgaGumEOr7q6xqj/1444778683e1cb6b418e7fcae350ff54/kubectl-get-secret.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3t5WRDOBgaGumEOr7q6xqj/1444778683e1cb6b418e7fcae350ff54/kubectl-get-secret.png" alt='Results of running "kubectl describe secret"' title="Results of running 'kubectl describe secret'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All this magic happens in &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/configs/otel-demo-app-values.yaml"&gt;otel-demo-app-values-ls.yaml&lt;/a&gt;. This is a version of &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo/values.yaml"&gt;values.yaml&lt;/a&gt; from the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo"&gt;OTel Demo App Helm Chart&lt;/a&gt; with updates to the Collector configs so that we can configure the OTel Collector to send Traces to Lightstep.&lt;/p&gt;

&lt;h3&gt;
  
  
  8 - Run the OTel Demo App
&lt;/h3&gt;

&lt;p&gt;Okay...enough Kubernetes talk. Let’s look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;! You can access the Demo App by Kubernetes port-forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; otel-demo svc/otel-demo-app-frontend 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the front-end, go to &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3SnG3kY8p1OZ6UW31ZqXNz/dde580e4af8afc15518880721fc9c30a/otel-demo-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3SnG3kY8p1OZ6UW31ZqXNz/dde580e4af8afc15518880721fc9c30a/otel-demo-ui.png" alt="OTel Demo App UI running on http://localhost:8080" title="OTel Demo App UI running on http://localhost:8080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and explore the amazing selection of telescopes and accessories, and buy a few. 😉🔭&lt;/p&gt;

&lt;h3&gt;
  
  
  9- See Traces in Lightstep
&lt;/h3&gt;

&lt;p&gt;We can now pop over to Lightstep and check out some &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt;. Let’s do this by creating a &lt;a href="https://docs.lightstep.com/docs/use-notebooks"&gt;Notebook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, click on the little page icon on the left nav bar (highlighted in blue, below). That will bring up this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5Nckr9lNvszGFsgzjwOqog/89d5b91a62ed12dda09354a3232b914b/ls-create-notebook.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5Nckr9lNvszGFsgzjwOqog/89d5b91a62ed12dda09354a3232b914b/ls-create-notebook.png" alt="Lightstep create blank notebook page" title="Lightstep create blank notebook page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we build our query for our Traces. Let’s look at the traces from the &lt;code&gt;recommendationservice&lt;/code&gt;. We’ll do by entering &lt;code&gt;recommendationservice&lt;/code&gt; in the field next to “All telemetry”. Because this is a service, select the second value from the drop-down, which says, “Use ‘recommendationservice’ as service value”, as per below:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2wfmzZ62GwRMUfnnTsHRky/a81cd0f4c8eaf7d44239091d455b95a2/ls-create-notebook-2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2wfmzZ62GwRMUfnnTsHRky/a81cd0f4c8eaf7d44239091d455b95a2/ls-create-notebook-2.png" alt="Creating a notebook for the OTel Demo App's recommendationservice" title="Creating a notebook for the OTel Demo App's recommendationservice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you select that value, you’ll see a chart like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2ii2nk7J8eoJpyB2Ehhbt1/a868be73b4bd237f66675a675ff9266d/ls-create-notebook-3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2ii2nk7J8eoJpyB2Ehhbt1/a868be73b4bd237f66675a675ff9266d/ls-create-notebook-3.png" alt="Results of recommendationservice Trace query" title="Results of recommendationservice Trace query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The little green dots represent trace exemplars from that Service. Hover over one of them to see for yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1raoM2PdyFQDe1jmMa5Hoj/58a060ee6085cfd3ad6d2ef134b8170f/ls-create-notebook-4.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1raoM2PdyFQDe1jmMa5Hoj/58a060ee6085cfd3ad6d2ef134b8170f/ls-create-notebook-4.png" alt="Trace exemplars for recommendationservice in Lightstep notebook" title="Trace exemplars for recommendationservice in Lightstep notebook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on one of these dots, you’ll get taken to the Trace view. Before you click, be sure to save your Notebook first (don’t worry, you’ll get a reminder before you navigate away from the page)!&lt;/p&gt;

&lt;p&gt;Here’s the Trace view we see when we click on the &lt;code&gt;get_product_list&lt;/code&gt; dot (Operation) above::&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/4abac571d5aa0924f0d2b9be9b4a691a/ls-trace-view.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/4abac571d5aa0924f0d2b9be9b4a691a/ls-trace-view.png" alt="OTel Demo App's recommendationservice Trace view" title="OTel Demo App's recommendationservice Trace view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty cool, amirite?&lt;/p&gt;

&lt;h3&gt;
  
  
  10- See Kubernetes Metrics in Lightstep
&lt;/h3&gt;

&lt;p&gt;But wait...there’s more! Not only can we see Traces in Lightstep, we can also see &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Remember when you ran &lt;code&gt;terraform apply&lt;/code&gt;? Well, not only did it create a Kubernetes cluster, deploy the OTel Demo App (and OTel Collector), it also created some handy-dandy Metrics dashboards for us.&lt;/p&gt;

&lt;p&gt;You can check out the newly-created Metrics dashboards by going to the Dashboards icon (the icon with 4 little squares) on the left navigation bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" alt="Lightstep dashboard list for OTel Demo App" title="Lightstep dashboard list for OTel Demo App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, let’s check out the &lt;strong&gt;Kubernetes / Compute Resources / Cluster&lt;/strong&gt; dashboard. This dashboard lets you see the state of your cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/55fX1xA5ytWrDTx7iLsT5H/1e6492c8869770997524eab23c60ac15/kubernetes-cluster-dashboards.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/55fX1xA5ytWrDTx7iLsT5H/1e6492c8869770997524eab23c60ac15/kubernetes-cluster-dashboards.png" alt="Kubernetes Compute Resources Cluster Dashboard" title="Kubernetes Compute Resources Cluster Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then have various other Metrics called &lt;strong&gt;Kubernetes Workload Metrics&lt;/strong&gt;. These are the dashboards with names that start with “&lt;strong&gt;Kubernetes / Compute Resources / Workload&lt;/strong&gt;”. These dashboards are specific to the services you are running. They take into account the Kubernetes Workloads in your various namespaces, using &lt;a href="https://github.com/kubernetes/kube-state-metrics"&gt;kube-state-metrics&lt;/a&gt;. For a closer look, check out &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/modules/lightstep/otel_demo_app_k8s_dashboard.tf"&gt;otel_demo_app_k8s_dashboard.tf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We used &lt;a href="https://github.com/lightstep/prometheus-k8s-opentelemetry-collector"&gt;Lightstep’s Prometheus Kubernetes OpenTelemetry Collector&lt;/a&gt; to get these Metrics into Lightstep. This Helm chart is inspired by &lt;a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack"&gt;kube-prometheus-stack&lt;/a&gt;, but with one crucial difference -- no Prometheus! We’re able to use recent enhancements to the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator"&gt;OpenTelemetry Operator for Kubernetes&lt;/a&gt; such as support for Service Monitors in order to scrape Prometheus metrics from pods, system components, and more.&lt;/p&gt;

&lt;p&gt;The best part is that you don’t need to install and maintain a &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; instance to be able to run it! But wait…there’s more! You also don’t need to use Lightstep as your Observability back-end in order to take advantage of this special Collector! How cool is that??&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can learn more about the &lt;a href="https://github.com/lightstep/prometheus-k8s-opentelemetry-collector"&gt;Prometheus Kubernetes OpenTelemetry Collector&lt;/a&gt; by checking out the docs &lt;a href="https://docs.lightstep.com/docs/otel-collector-k8s-quick-start"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, the &lt;strong&gt;Kubernetes / Compute Resources / Workload / otel-demo-app-cartservice&lt;/strong&gt; dashboard displays metrics for the OTel Demo App’s cartservice. In it we can see how our containers and pods are doing based on Metrics such as those for CPU and Memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/57ppDqZqiXLZ6r0MeaoVmg/cd717d5ed0603df5adcf2eb5c1811c83/workload-cart-service-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/57ppDqZqiXLZ6r0MeaoVmg/cd717d5ed0603df5adcf2eb5c1811c83/workload-cart-service-dashboard.png" alt="Workload dashboard for OTel Demo App's cartservice" title="Workload dashboard for OTel Demo App's cartservice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  11- See Application Metrics in Lightstep
&lt;/h3&gt;

&lt;p&gt;Ah...but we’re not done with Metrics just yet! If you go back to the dashboard view and scroll to the very end of the list, you’ll see the &lt;strong&gt;OTel Demo App - Application Metrics&lt;/strong&gt; dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" alt="Lightstep dashboard list for OTel Demo App" title="Lightstep dashboard list for OTel Demo App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on it to take a quick little peek!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3FwKSbXFAuij68d3UinVqV/443fed4e4d4129d5e91ebc41ca5a02e0/otel-demo-app-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3FwKSbXFAuij68d3UinVqV/443fed4e4d4129d5e91ebc41ca5a02e0/otel-demo-app-dashboard.png" alt="OTel Demo App Metrics Dashboard" title="OTel Demo App Metrics Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The latest version of the OTel Demo App emits both auto-instrumented and manually-instrumented Metrics. In today’s demo, we wanted to highlight some of the &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; from the &lt;code&gt;recommendationservice&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, we have the auto-instrumented Python Metrics, which are captured from the Python runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.cpu_time&lt;/code&gt;: Track the amount of time being spent in different states of the CPU. This includes user (time running application code) and system (time spent in the operating system). This metric is represented as total elapsed time in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.memory&lt;/code&gt;: Memory utilization &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.gc_count&lt;/code&gt;: Number of times the garbage collector has been called.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also have one manually-instrumented Metric:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app_recommendations_counter&lt;/code&gt;: Cumulative count of the number of recommended products per service call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more on the &lt;code&gt;recommendationservice&lt;/code&gt; &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt;, check out &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/main/docs/services/recommendationservice.md"&gt;this doc&lt;/a&gt;. For more on &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; captured by other services, check out the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/docs/services"&gt;OTel Demo App service docs&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  12- Teardown
&lt;/h3&gt;

&lt;p&gt;If you’re no longer using this environment, don’t forget to tear down its resources, to avoid running up a huge cloud bill. You’re welcome. 😉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step can take up to 30 minutes, so please be patient! Also, you’ll probably notice that on first run, you’ll see the following error:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/iWJTisAFXDNxnrlyf16n5/b9da920442c86ddca0ae7bad01b5b82b/tf-destroy-error.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/iWJTisAFXDNxnrlyf16n5/b9da920442c86ddca0ae7bad01b5b82b/tf-destroy-error.png" alt="Terraform destroy error" title="Terraform destroy error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Don’t panic!&lt;/em&gt;&lt;/strong&gt; If you run &lt;code&gt;terraform destroy -auto-approve&lt;/code&gt; again, it will finish nukifying all the things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Today we got to see some aspects of Observability-Landscape-as-Code (OLaC) in practice! Specifically, we looked at the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application instrumentation with &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Collecting and storing application telemetry via the OTel Collector&lt;/li&gt;
&lt;li&gt;Configuring an Observability back-end (i.e. &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;) through code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We showcased this by using Terraform to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy the OpenTelemetry Demo App to Kubernetes.&lt;/strong&gt; The Otel Demo App showcases the &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; instrumentation of different services in different languages using OpenTelemetry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy an OpenTelemetry Collector to Kubernetes (part of the Demo App deployment).&lt;/strong&gt; The Collector is used to send application Traces and Metrics to Lightstep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Lightstep dashboards.&lt;/strong&gt; The Lightstep Terraform provider allowed us to codify this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codifying our Observability Landscape means that we can tear down and recreate our application, Collector, and dashboards as needed, knowing that we’ll have consistency across the board every single time. Plus, it means that we can version control it, so that it’s not lost in the ether somewhere, or sitting in a secret server under Bob’s desk. Bonus!&lt;/p&gt;

&lt;p&gt;Hopefully this gives you a nice little flavour of the power of OLaC, and will inspire you to go out there and start OLaC-ing too! (I just made up a new verb. You’re welcome.)&lt;/p&gt;

&lt;p&gt;Whew! That was a lot to think about and take in! Give yourself a pat on the back, because we’ve covered a LOT! Now, please enjoy this picture of Adriana’s rat, Bunny, enjoying an almond!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2xH9Iu8LOCbVRvTudg80Zo/37dc2c3d97529ed22ca16739467d05ec/bunny-the-rat.JPEG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2xH9Iu8LOCbVRvTudg80Zo/37dc2c3d97529ed22ca16739467d05ec/bunny-the-rat.JPEG" alt="Bunny the rat enjoying an almond treat" title="Bunny the rat enjoying an almond treat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; is always looking for feedback and contributors. Please consider joining the &lt;a href="https://github.com/open-telemetry/community/blob/main/community-membership.md#member"&gt;OTel Community&lt;/a&gt; to help make OpenTelemetry AWESOME!&lt;/p&gt;




&lt;p&gt;Got questions about Observability-Landscape-as-Code? Talk to us! Feel free to connect with us through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect with Adriana on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Connect with Ana on &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;LinkedIn&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>kubernetes</category>
      <category>terraform</category>
      <category>observability</category>
    </item>
    <item>
      <title>KubeCon North America 2022: A Retrospective</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Wed, 09 Nov 2022 20:15:53 +0000</pubDate>
      <link>https://forem.com/lightstep/kubecon-north-america-2022-a-retrospective-4i4d</link>
      <guid>https://forem.com/lightstep/kubecon-north-america-2022-a-retrospective-4i4d</guid>
      <description>&lt;p&gt;&lt;strong&gt;with &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;Ana Margarita Medina&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello fellow Kubernetes nerds! 🤓 KubeCon North America is now behind us, so it’s time for a conference retrospective! Now, y’all must be thinking, “Awww, not another one! Noooo...” Ah, but ‘tis not just any KubeCon wrap-up, because both &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana&lt;/a&gt; and I will be sharing our perspectives, and we’re awesome and y’all love us. Amirite? Course I am. Without further ado...&lt;/p&gt;

&lt;h2&gt;
  
  
  Adriana’s Point-of-View
&lt;/h2&gt;

&lt;p&gt;If there is any conference that is on My Conference Bucket List™, it is most definitely KubeCon. I have a love-hate relationship with &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; (don’t we all, though?), and &lt;a href="https://adri-v.medium.com/list/kubernetes-090db256e52b"&gt;have spent hours trying to understand its wiley ways&lt;/a&gt;, and cursing at my terminal at yet another &lt;a href="https://stackoverflow.com/questions/41604499/my-kubernetes-pods-keep-crashing-with-crashloopbackoff-but-i-cant-find-any-lo"&gt;CrashLoopBackOff&lt;/a&gt;. So to go to KubeCon and nerd out on Kubernetes just sounded awesome to me. &lt;/p&gt;

&lt;p&gt;I finally got my chance by attending &lt;a href="https://events.linuxfoundation.org/kubecon-cloudnativecon-north-america/"&gt;KubeCon North America 2022&lt;/a&gt;, which took place in Detroit, MI.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3jXTMhBsdJ7xyKsKMLUQsd/129dd3bb67c4fce33e65259c928f0082/detroit-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3jXTMhBsdJ7xyKsKMLUQsd/129dd3bb67c4fce33e65259c928f0082/detroit-collage.JPG" alt="Collage of various downtown Detroit landmarks" title="Collage of various downtown Detroit landmarks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was super excited to attend the conference for a few reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It would be the first time I’d be getting to meet my Lightstep DevRel team, and find out if I was the shortest of the bunch. Spoiler alert: Ana and I are the shortest at around 5’3 (161cm for my Metric peeps). Latina genes. I roll with it.&lt;/li&gt;
&lt;li&gt;It was my conference début as a DevRel. Y’all, this is my first DevRel role, if you can believe it - 6 months in and lovin’ it!&lt;/li&gt;
&lt;li&gt;Last time I attended a conference was &lt;a href="https://devopsdays.org/events/2018-toronto/welcome/"&gt;DevOps Days Toronto, in 2018&lt;/a&gt;. The highlight of that was getting &lt;a href="https://nicolefv.com/"&gt;Dr. Nicole Forsgren&lt;/a&gt; and &lt;a href="https://research.google/people/106958/"&gt;Jez Humble&lt;/a&gt; (both of &lt;a href="https://www.devops-research.com/research.html"&gt;DORA&lt;/a&gt; fame) to sign a hard copy of their book, &lt;a href="https://www.amazon.ca/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339/ref=asc_df_1942788339/?tag=googleshopc0c-20&amp;amp;linkCode=df0&amp;amp;hvadid=293004119900&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=13450460705037761726&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9000918&amp;amp;hvtargid=pla-446149606248&amp;amp;psc=1"&gt;Accelerate&lt;/a&gt;, so not too shabby. PS: Get the book. Bonus: if you get the audiobook, it’s read by Nicole. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, enough rambling. Here’s my KubeCon recap, newbie edition! &lt;/p&gt;

&lt;p&gt;As a COVID-cautious gal, I was pleased to see that the &lt;a href="https://www.cncf.io/"&gt;CNCF&lt;/a&gt; had various COVID precautions in place including vaccine requirement or a negative COVID test to pick your badge and masking was required and enforced. various COVID precautions in place. For starters, you needed proof of vaccination or proof of a negative COVID test in order to pick your badge. Inside the venue, masking was required and enforced. I always mask indoors (no exceptions), so I definitely felt more at ease among the droves of humans, knowing that we had those protections in place. The other thing that I very much appreciated is that the CNCF provided freebie &lt;a href="https://www.globalpointofcare.abbott/en/product-details/navica-binaxnow-covid-19-us.html"&gt;rapid COVID tests&lt;/a&gt; proctored through &lt;a href="https://www.emed.com"&gt;eMed&lt;/a&gt;, so I was able to test daily while I was there, for extra peace of mind.&lt;/p&gt;

&lt;p&gt;Although KubeCon itself didn’t start until Wednesday, October 26th, I flew in on Sunday night so that I could catch &lt;a href="https://events.linuxfoundation.org/open-observability-day-north-america/program/schedule/"&gt;Open Observability Day&lt;/a&gt; on Monday, and &lt;a href="https://www.eventbrite.com/e/otel-unplugged-kubeconcloudnativecon-detroit-2022-tickets-427595037267?utm_source=o11y.news&amp;amp;utm_medium=email"&gt;OTel Unplugged&lt;/a&gt; on Tuesday.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 1: Open Observability Day
&lt;/h3&gt;

&lt;p&gt;I popped in and out of &lt;a href="https://events.linuxfoundation.org/open-observability-day-north-america/program/schedule/"&gt;Open Observability Day&lt;/a&gt;, and of the talks that I caught, there were two talks that I really enjoyed. One was &lt;a href="https://www.youtube.com/watch?v=p0oHZl3M51A&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=8"&gt;OTel Me How to Build a Data Pipeline for Observability, by Daniel Kim and Reese Lee&lt;/a&gt;. Even though I was already familiar with the content, I really appreciated Daniel and Reese’s super energetic presentation style. It was definitely a welcome pick-me-up to help fight post-lunch food coma. The other talk I really enjoyed was &lt;a href="https://www.youtube.com/watch?v=ieDvAiWxlt0&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=9"&gt;What Can eBPF Actually do for Modern-Day Observability? by Ori Shussman&lt;/a&gt;. In it, he talks about how &lt;a href="https://www.tigera.io/learn/guides/ebpf/"&gt;eBPF&lt;/a&gt; lets us see data that is otherwise not visible. For example, eBPF is useful for providing greater insight into &lt;a href="https://grpc.io"&gt;gRPC&lt;/a&gt; calls, which are notoriously difficult to observe. 🤯I also wanted to give a special shout-out to &lt;a href="https://www.youtube.com/watch?v=1v8aL9Jxce8&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=13"&gt;Opening the Door to Observability, by Libby Meren&lt;/a&gt;. Although I missed this talk, this topic is very near and dear to my heart, because in my previous role, I had to spend a chunk of time trying to get buy-in on doing Observability The Right Way ™.&lt;/p&gt;

&lt;p&gt;Interested in checking out the other talks? Y’all can check out videos for them &lt;a href="https://www.youtube.com/watch?v=Q5Vf8bpTDlI&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5toOUhVqcmAjFAE5eAqpOv/28020dadb6092456f957ac958d391e23/open-o11y-day.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5toOUhVqcmAjFAE5eAqpOv/28020dadb6092456f957ac958d391e23/open-o11y-day.png" alt="A presentation in progress at Open Observability Day" title="A presentation in progress at Open Observability Day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later that afternoon, I got to meet my Lightstep DevRel fam in real life, and it was AWESOME! Unfortunately, we never took a group selfie. 😿&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 2: OTel Unplugged
&lt;/h3&gt;

&lt;p&gt;I spent Day 2 at the &lt;a href="https://www.weddingwire.com/biz/colony-club-detroit-detroit/68e394fe233e346c.html"&gt;Colony Club&lt;/a&gt; to attend &lt;a href="https://www.eventbrite.com/e/otel-unplugged-kubeconcloudnativecon-detroit-2022-tickets-427595037267?utm_source=o11y.news&amp;amp;utm_medium=email"&gt;OTel Unplugged&lt;/a&gt;. This event was sponsored by &lt;a href="https://lightstep.com"&gt;Lightstep&lt;/a&gt;, &lt;a href="https://honeycomb.io"&gt;Honeycomb&lt;/a&gt;, &lt;a href="https://newrelic.com"&gt;New Relic&lt;/a&gt;, &lt;a href="https://www.splunk.com"&gt;Splunk&lt;/a&gt;, &lt;a href="https://www.dynatrace.com"&gt;Dynatrace&lt;/a&gt;, &lt;a href="https://www.crowdstrike.com"&gt;Crowdstrike&lt;/a&gt;, and &lt;a href="https://www.nginx.com"&gt;NGINX&lt;/a&gt;. I came into the event not knowing what to expect. I can sometimes clamp up when I’m around folks that I don’t know, but because I was helping with the event check-in, I got to say hello to a number of the attendees, which helped break the ice. And it turns out that there were a lot of names that I recognized from my work in the OTel community, and it was nice to connect in person with folks whom I’d only previously met through Slack or Zoom.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6eQFxw1380BEjAClwSSoMK/620fb02724bfe3101f50cc00dcce50ed/otel-unplugged-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6eQFxw1380BEjAClwSSoMK/620fb02724bfe3101f50cc00dcce50ed/otel-unplugged-collage.JPG" alt="Collage of OTel Unplugged highlights" title="Collage of OTel Unplugged highlights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: Big shout-out to the venue, which was gorgeous, and the staff were super friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 3: KubeCon!
&lt;/h3&gt;

&lt;p&gt;Finally, the main event! I’d been to the conference center on Monday to pick up my badge, but the volume of people that day didn’t even compare to how busy things got on Wednesday. It was a whirlwind of a day, and I spent some of my time at the Lightstep booth. The Lightstep crew did demos of the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OTel Demo App&lt;/a&gt; to illustrate &lt;a href="https://lightstep.com/blog/observability-as-code-with-kubernetes-and-lightstep"&gt;Observability-Landscape-as-Code in action&lt;/a&gt;, which &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Ana&lt;/a&gt; and I poured our blood, terror, terror, sweat, and (happy?) tears into it before KubeCon. It’s a great little demo, if I do say so myself, and I definitely recommend that you check it out. (Shameless plug, I know. Sorrynotsorry.) &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5lI6chZQiVWri0NpfC5Ok2/5e96c01e39b9818c35327defbbbcbf56/ls-booth-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5lI6chZQiVWri0NpfC5Ok2/5e96c01e39b9818c35327defbbbcbf56/ls-booth-collage.JPG" alt="Collage of the Lightstep booth at KubeCon" title="Collage of the Lightstep booth at KubeCon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The life-sized game of &lt;a href="https://en.wikipedia.org/wiki/Jenga"&gt;Jenga&lt;/a&gt; at our booth was a huge draw, and there were a lot of adventurous folks who tried their hand at it. The pic of Ana and me below shows how tall the tower got. What you don’t see is that, shortly after the pic was taken, one of us knocked the tower down. Oops. 😳&lt;/p&gt;

&lt;p&gt;Our team also had KubeCon-Detroit-themed stickers made especially for this event. Ana took things to the next level and did her nails to match our stickers. And I don’t know if you can see it in any of the photos below, but she also wore Kubernetes-themed earrings too. She’s very extra, and I love it! The stickers were part of a scavenger hunt that revealed the location of a pizza party hosted by Lightstep on Thursday night.&lt;/p&gt;

&lt;p&gt;Ana and I also spent some time roaming the exhibitors’ area, grabbing some sweet swag (I nabbed a sweet OTel t-shirt and the cutest little &lt;a href="https://tracetest.io"&gt;Tracetest&lt;/a&gt; plushie, whom I named Tracey), meeting new and interesting people, and meeting some familiar faces in real life. (Hi &lt;a href="https://twitter.com/a_bangser"&gt;Abby&lt;/a&gt;!!) Ah, the life of a DevRel. So. Much. Fun. 💜💜💜&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1f3a4StmUJTosYBaEK1nbI/7ce98a53eda032fedb80d33c461fcf2d/kubecon-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1f3a4StmUJTosYBaEK1nbI/7ce98a53eda032fedb80d33c461fcf2d/kubecon-collage.JPG" alt="Collage of KubeCon highlights" title="Collage of KubeCon highlights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, I wasn’t able to stay for the full conference, but from what I did experience, it was lots of fun. This most definitely will not be my last KubeCon, and I most definitely can’t wait for the next KubeCon! Bring it!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6fr8XpNHdr8uZ9mCqDopYu/a63aa189b672fae018eab03b410858ab/bring-it.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6fr8XpNHdr8uZ9mCqDopYu/a63aa189b672fae018eab03b410858ab/bring-it.png" alt="Bring it On meme" title="Bring it On meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ana’s Point-of-View
&lt;/h2&gt;

&lt;p&gt;Ana here! I wanted to provide my perspective from attending four KubeCons in the past. This is my second KubeCon through COVID, and I appreciate that the CNCF has enforced COVID protocols for their events. As someone who caught COVID during KubeCon EU València, I was extra careful this time around, and tested daily. I’m still testing and COVID negative, so thanks, CNCF! &lt;/p&gt;

&lt;p&gt;On Monday, I was scheduled to go to the Contributor Summit, but I lost my voice the week before on Thursday, and I still hadn’t recovered it by Monday morning.🥲 I had talks and booth duty during the coming week, so instead I did something more easygoing and I just helped my fellow Lightsteppers get ready for KubeCon and did final touches on my slides. &lt;/p&gt;

&lt;p&gt;One thing that was really hard this year was that there were too many (or what felt to me like too many) co-located/Day 0 events. It’s great to see the ecosystem continuing to grow, but it definitely makes it very hard to decide which events to go to, for an opportunity to learn from the community. I know I struggled with this, so I do hope that we try to unify topics a bit more next time. &lt;/p&gt;

&lt;p&gt;On Tuesday, I split my time between &lt;a href="https://kccncna2022.sched.com/event/1AOma/chaos-day-hosted-by-harness-additional-in-person-registration-fee-100"&gt;Chaos Day&lt;/a&gt;, &lt;a href="https://community.cncf.io/events/details/cncf-keptn-community-presents-keptn-community-day-kubecon-cloudnativecon-detroit/"&gt;Keptn Community Day&lt;/a&gt;, and of course, the hallway track of the co-located events. From working in the Chaos Engineering community the last 4+ years, I loved to see the &lt;a href="https://twitter.com/Ana_M_Medina/status/1584958979767402496"&gt;Cloud Native Chaos Engineering community growth&lt;/a&gt;, I also loved talking to folks about the future of &lt;a href="https://keptn.sh/"&gt;Keptn&lt;/a&gt;, and giving a talk on how to achieve more reliability with this CNCF sandbox project and the other OSS tools. &lt;/p&gt;

&lt;p&gt;As Wednesday kicked off, it was great to feel the buzz and excitement of folks so eager to learn and connect. From Day 1’s keynote, I really enjoyed the reminder that companies benefiting from Kubernetes and the CNCF ecosystem should be getting involved, giving back, and mentoring others. There was a lot of love to maintainers and contributors in the ecosystem, and as someone involved in the &lt;a href="https://github.com/kubernetes/sig-release"&gt;Kubernetes Release Team&lt;/a&gt; for Kubernetes v1.25 and v1.26, it was a huge honor to see my face up in there with the rest of the team! 💙&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/676f9AHqWgYElocXjzquV6/b9ed8b78c35061d8b6085257554dad6f/release_team.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/676f9AHqWgYElocXjzquV6/b9ed8b78c35061d8b6085257554dad6f/release_team.jpg" alt="Slide of the Kubernetes release team" title="Slide of the Kubernetes release team. Photo credit: https://twitter.com/LeonardPahlke/status/1585273031680823298/photo/1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also took some time to walk the show floor, buy some CNCF swag and check out the vendor swag too. &lt;/p&gt;

&lt;p&gt;Thursday was another busy day of running around with the community. I also got to record some Cloud Native in Spanish content with some other Latinxs in the space. In addition to that, Lightstep hosted two events, one of which was our secret pizza party. It was fun to take over Detroit’s famous alley, &lt;a href="https://www.thebelt.org/about-the-belt"&gt;The Belt&lt;/a&gt;, to hang out and talk about SRE. Thanks to everyone who stopped by! #DetroitSRECity 🤘&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/11r0SAW3PyUozU9xiazURY/a2d50c4df2b0ca244f33a2eb9f1282c9/the_belt_collage.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/11r0SAW3PyUozU9xiazURY/a2d50c4df2b0ca244f33a2eb9f1282c9/the_belt_collage.png" alt="Collage of highlights from The Belt" title="Collage of highlights from The Belt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the other places I spent a lot of time at at KubeCon NA was the CNCF Project Pavillion. I was very happy to see that it was a bit larger than the area we had during KubeCon EU, but I still wish that it was bigger and wasn’t so tucked away in the corner. A number of booths were showcasing their projects with demos through the week, hosted Q&amp;amp;A time, and gave away swag. If you are still trying to understand the Cloud Native Ecosystem, you can look at this &lt;a href="https://landscape.cncf.io/"&gt;very extensive map&lt;/a&gt; of the landscape and projects under the CNCF, some of which are more advanced than others. Of course I’m biased, but I’m really excited for the work that &lt;a href="https://keptn.sh/"&gt;Keptn&lt;/a&gt; is doing in helping developers have more control over their application lifecycle. I’m also very excited to see where &lt;a href="https://backstage.io/"&gt;Backstage&lt;/a&gt; goes and how other CNCF projects can integrate with their service catalog. &lt;/p&gt;

&lt;p&gt;On Friday, I got a chance to hang out some more with my community friends and give a talk on the future of Keptn with our friends at &lt;a href="https://www.dynatrace.com"&gt;Dynatrace&lt;/a&gt;. I am very excited to see where this project goes and what work we continue to do with OpenTelemetry to make it easier to observe our deployments and their reliability. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4siIu0JPRwrKyVvUUEa31/4035b01d421b4d2897f2e234640e8c06/keptn_friday_talk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4siIu0JPRwrKyVvUUEa31/4035b01d421b4d2897f2e234640e8c06/keptn_friday_talk.jpg" alt="Ana at the Keptin Friday talk with some of the Keptin crew" title="Ana at the Keptin Friday talk with some of the Keptin crew"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Aaaand...that’s a wrap! KubeCon was a ton of fun for both Ana and me. We loved having the  Lightstep DevRel team in person for the first time, we loved connecting with our various CNCF communities, Lightstep customers, and it was great fun to see all the cool tech out there. Whether you’re a KubeCon newbie like me, or KubeCon veteran like Ana, it was great to see the CNCF community continuing to come together, grow, and prosper. We are definitely looking forward to KubeCon EU in 2023!&lt;/p&gt;

&lt;p&gt;Now, please enjoy this photo of Adriana’s rat, Phoebe, keeping her company as she does her KubeCon expense report.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4wTOgMiV7u4a1XLElOM483/47fefc06e936287fb2c86cacae2b8c11/phoebe-computer.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4wTOgMiV7u4a1XLElOM483/47fefc06e936287fb2c86cacae2b8c11/phoebe-computer.jpg" alt="Phoebe the rat helps Adriana with KubeCon expenses" title="Phoebe the rat helps Adriana with KubeCon expenses"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about Observability? Talk to us! Feel free to connect with us through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect with Adriana up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Connect with Ana on &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;LinkedIn&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>kubecon</category>
      <category>devrel</category>
      <category>conference</category>
    </item>
    <item>
      <title>Running the OpenTelemetry Demo App in Kubernetes</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Fri, 28 Oct 2022 15:30:00 +0000</pubDate>
      <link>https://forem.com/avillela/running-the-opentelemetry-demo-app-in-kubernetes-1j51</link>
      <guid>https://forem.com/avillela/running-the-opentelemetry-demo-app-in-kubernetes-1j51</guid>
      <description>&lt;p&gt;If you’re new to OpenTelemetry and want to see it in action in a real-life example, the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo app&lt;/a&gt; is a great way to get started quickly. In one of my &lt;a href="https://lightstep.com/blog/observability-mythbusters-how-hard-is-it-to-get-started-with-opentelemetry"&gt;previous blog posts&lt;/a&gt;, I showed you how to get the demo app up and running and sending Traces to &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;. That’s all well and good, but we all know that in “The Real World”, we’re not running our containerized workloads locally with Docker Compose. Instead, we’re running them using container orchestrators such as &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; and &lt;a href="https://nomadproject.io"&gt;Nomad&lt;/a&gt;. Keeping that in mind, wouldn’t it be so very very nice if we could run the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo app&lt;/a&gt; in Kubernetes? And wouldn’t it be super extra nice if we could send those Traces to Lightstep?&lt;/p&gt;

&lt;p&gt;Well, look no further, my friend, because today, that is exactly what we’re going to do!&lt;/p&gt;

&lt;p&gt;Are you ready? Let’s do this!&lt;/p&gt;

&lt;h1&gt;
  
  
  Tutorial
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://app.lightstep.com/signup/developer?signup_source=docs"&gt;A Lightstep account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt; for the Lightstep project you would like to use&lt;/li&gt;
&lt;li&gt;A Kubernetes cluster&lt;/li&gt;
&lt;li&gt;&lt;a href="https://helm.sh/docs/intro/install/"&gt;Helm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The version of the OpenTelemetry Demo App used at the time of this writing was version &lt;code&gt;0.9.6&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h3&gt;
  
  
  1- Initialize Helm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add open-telemetry https://github.com/open-telemetry/opentelemetry-helm-charts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2- Create the values YAML file
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;OTel Demo App Helm Chart&lt;/a&gt; expects a &lt;code&gt;values.yaml&lt;/code&gt; file. Now, the one that comes out-of-the-box with the chart is all well and good, but it sends Traces to Jaeger. In our case, since we want to send Traces to Lightstep, we’ll need to modify it a tad. Let’s start by creating our own values YAML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;values-ls.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then open &lt;code&gt;values-ls.yaml&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;otel-demo-ls
&lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; values-ls.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
opentelemetry-collector:
  nameOverride: otelcol
  mode: deployment
  extraEnvs:
    - name: LS_TOKEN
      valueFrom:
        secretKeyRef:
          key: LS_TOKEN
          name: otel-collector-secret
  resources:
    limits:
      memory: 100Mi
  config:
    exporters:
      otlp/ls:
        endpoint: ingest.lightstep.com:443
        timeout: 30s
        tls:
          insecure_skip_verify: true
        headers:
          "lightstep-access-token": "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LS_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"

      otlp:
        endpoint: '{{ .Release.Name }}-jaeger:4317'
        tls:
          insecure: true

      logging:
        logLevel: debug

    processors:
      resource:
        attributes:
        - key: service
          from_attribute: service.name
          action: insert
      resource/add_container:
        attributes:
        - key: container
          from_attribute: service.name
          action: insert

    service:
      pipelines:
        metrics:
          exporters:
            - logging
            - otlp/ls
          processors:
            - resource
        traces:
          exporters:
            - logging
            - otlp
            - otlp/ls
          processors:
            - resource/add_container
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I had originally left out Jaeger and Prometheus configurations for the sake of keeping things simple. If you'd like to see an example of with both, check out this &lt;a href="https://gist.github.com/lakamsani/c329f211bf2a6f3d1c685ae02d4ff828"&gt;here&lt;/a&gt;. (Thank you &lt;a href="https://medium.com/@lakamsani"&gt;Vamsee Lakamsani&lt;/a&gt; for the share!)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your file should look like &lt;a href="https://gist.github.com/avillela/ecc6929f60c563febadb305edf006cf4"&gt;this one&lt;/a&gt;. Okay...so what exactly did we do?? So glad you asked!&lt;/p&gt;

&lt;p&gt;Well, in a nutshell, we’re overriding the &lt;code&gt;values.yaml&lt;/code&gt; in the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo/values.yaml"&gt;OpenTelemetry Demo App Chart&lt;/a&gt;. But we’re only overriding the bits that we need to override - specifically, the OTel Collector configuration, so we can send Traces to Lightstep. It looks an awful lot like the &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/collector/vanilla/collector.yaml"&gt;Collector config YAML&lt;/a&gt; that we know and love. But not quite. Because it’s just a partial config of the Collector config. You see, &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo/Chart.yaml"&gt;Demo App Chart uses the OpenTelemetry Collector Helm Chart as a subchart&lt;/a&gt;. All the configuration that the Collector Chart exposes is available to us in the Demo Chart. So when you’re populating &lt;a href="https://gist.github.com/avillela/ecc6929f60c563febadb305edf006cf4"&gt;your own version of values.yaml&lt;/a&gt;, all you need to do is include the Collector configs that you wish to modify or add to. In our case, we’re doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure a new exporter, &lt;code&gt;otlp/ls&lt;/code&gt;, which allows us to send traces to Lightstep&lt;/li&gt;
&lt;li&gt;Add the new exporter to our &lt;code&gt;metrics&lt;/code&gt; and &lt;code&gt;traces&lt;/code&gt; pipelines&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;logging&lt;/code&gt; exporter to use the &lt;code&gt;debug&lt;/code&gt; log level.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll notice that in configuring the &lt;code&gt;otlp/ls&lt;/code&gt; exporter, we’re setting the following header value: &lt;code&gt;"lightstep-access-token": "${LS_TOKEN}"&lt;/code&gt;. But where in Space does &lt;code&gt;${LS_TOKEN}&lt;/code&gt; come from?? Great question! Which brings me to the second noteworthy section.&lt;/p&gt;

&lt;p&gt;You may have noticed the &lt;code&gt;extraEnvs&lt;/code&gt; section in our &lt;code&gt;opentelemetry-collector&lt;/code&gt; config. Well, this is where we can configure environment variables that are mounted to our Collector pod in Kubernetes. We don’t wish to expose our secret in &lt;code&gt;values-ls.yaml&lt;/code&gt;, as that would be a security no-no. Instead, we reference a secret called &lt;code&gt;otel-collector-secret&lt;/code&gt;, which is mounted as the environment variable, &lt;code&gt;LS_TOKEN&lt;/code&gt;. Ta-da! 🎉&lt;/p&gt;

&lt;p&gt;PS: We’ll create the secret in the next step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; I am fully aware of the fact that you won’t want to use Kubernetes secrets In Real Life to store your Lightstep Access Token, as they are only &lt;a href="https://en.wikipedia.org/wiki/Base64"&gt;base64-encoded&lt;/a&gt;. Instead, you’ll want to store your secrets in a secrets manager, such as one that comes with your Cloud provider (e.g. &lt;a href="https://medium.com/dzerolabs/kubernetes-saved-today-f-cked-tomorrow-a-rant-azure-key-vault-secrets-%C3%A0-la-kubernetes-fc3be5e65d18"&gt;Azure Key Vault&lt;/a&gt;, &lt;a href="https://cloud.google.com/secret-manager"&gt;Google Secret Manager&lt;/a&gt;), or &lt;a href="https://vaultproject.io"&gt;HashiCorp Vault&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3- Deploy the app
&lt;/h3&gt;

&lt;p&gt;Now that we know what’s up, let’s deploy the app to Kubernetes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_LS_TOKEN&amp;gt;"&lt;/span&gt;

kubectl create ns otel-demo
kubectl create secret generic otel-collector-secret &lt;span class="nt"&gt;-n&lt;/span&gt; otel-demo &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;LS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$LS_TOKEN&lt;/span&gt;

helm upgrade my-otel-demo open-telemetry/opentelemetry-demo &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;path-to-values-ls-file&amp;gt;/values-ls.yaml &lt;span class="nt"&gt;-n&lt;/span&gt; otel-demo &lt;span class="nt"&gt;--install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;path-to-values-ls-file&amp;gt;&lt;/code&gt; is the path in which your newly-created &lt;code&gt;values-ls.yaml&lt;/code&gt; is located.&lt;/p&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;&amp;lt;YOUR_LS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  4- Access the OTel Demo App
&lt;/h3&gt;

&lt;p&gt;You can access the Demo App by Kubernetes port-forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; otel-demo svc/otel-demo-app-frontend 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the front-end, go to &lt;a href="http://localhost:8080:"&gt;http://localhost:8080:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j6xfcda0e9azwmscjh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j6xfcda0e9azwmscjh8.png" alt="Screen capture of the OpenTelemetry Demo App UI" title="OpenTelemetry demo app UI" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5- See traces in Lightstep
&lt;/h3&gt;

&lt;p&gt;We can now pop over to Lightstep and check out some Traces. Let's do this by creating a &lt;a href="https://docs.lightstep.com/docs/use-notebooks"&gt;Notebook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, click on the little page icon on the left nav bar (highlighted in blue, below). That will bring up this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqb9itss106wvlbdakd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqb9itss106wvlbdakd6.png" alt="Creating a Lightstep Notebook" title="Creating a Lightstep Notebook" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we build our query for our Traces. Let's look at the traces from the &lt;code&gt;recommendationservice&lt;/code&gt;. We'll do by entering &lt;code&gt;recommendationservice&lt;/code&gt; in the field next to "All telemetry". Because this is a service, select the second value from the drop-down, which says, "Use 'recommendationservice' as service value", as per below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6oj88vph00umw3qqdo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6oj88vph00umw3qqdo7.png" alt="Creating a Lightstep notebook for the recommendationservice traces" title="Creating a Lightstep notebook for the recommendationservice traces" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you select that value, you'll see a chart like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiyar05fkxtatt6avxbgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiyar05fkxtatt6avxbgd.png" alt="Output of Notebook for recommendationservice traces" title="Output of Notebook for recommendationservice traces" width="800" height="725"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The little green dots represent trace exemplars from that Service. Hover over one of them to see for yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbse4aug9v9182lnf7ik9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbse4aug9v9182lnf7ik9.png" alt="Screen shot of Trace exemplars" title="Screen shot of Trace exemplars" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on one of these dots, you'll get taken to the Trace view. Before you click, be sure to save your Notebook first (don't worry, you'll get a reminder before you navigate away from the page)!&lt;/p&gt;

&lt;p&gt;Here's the Trace view we see when we click on the &lt;code&gt;get_product_list&lt;/code&gt; dot (Operation) above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8kfvdml0cfj947oeyfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8kfvdml0cfj947oeyfb.png" alt="Trace view for recommendationservice" title="Trace view for recommendationservice" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Today we upped our &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; game, and moved from running it à la &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/main/docker-compose.yml"&gt;Docker Compose&lt;/a&gt;, to deploying it to Kubernetes. We did this, thanks to the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts"&gt;OpenTelemetry Demo App Helm Chart&lt;/a&gt;, and we used our &lt;a href="https://gist.github.com/avillela/ecc6929f60c563febadb305edf006cf4"&gt;own version of values.yaml&lt;/a&gt; so that we could send Traces and Metrics to Lightstep.&lt;/p&gt;

&lt;p&gt;This should give you a nice feel for running a full-fledged OTel-instrumented app in Kubernetes!&lt;/p&gt;

&lt;p&gt;And now, I shall reward you with a picture of Ollie Octopus painting, drawn by my &lt;a href="https://medium.com/r/?url=https%3A%2F%2Finstagram.com%2Fold_fashion_glazed"&gt;superly-talented 14-year-old daughter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5pka3rqm3ybc9jnr249.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5pka3rqm3ybc9jnr249.png" alt="Drawing of Ollie Octopus painting a picture." title="Drawing of Ollie Octopus painting a picture. Drawing by https://instagram.com/old_fashion_glazed" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; is always looking for feedback and contributors. Consider &lt;a href="https://github.com/open-telemetry/community/blob/main/community-membership.md#member"&gt;joining the OTel Community&lt;/a&gt; to help make OpenTelemetry AWESOME!&lt;/p&gt;




&lt;p&gt;Got questions about today’s blog post? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opentelemetry</category>
      <category>observability</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
