<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WN</title>
    <description>The latest articles on DEV Community by WN (@wn).</description>
    <link>https://dev.to/wn</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%2F1061114%2F4be265e3-a33f-423d-a3ac-02786175900e.png</url>
      <title>DEV Community: WN</title>
      <link>https://dev.to/wn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wn"/>
    <language>en</language>
    <item>
      <title>Use Chatgpt to create a webhook microservice</title>
      <dc:creator>WN</dc:creator>
      <pubDate>Sat, 29 Apr 2023 04:28:16 +0000</pubDate>
      <link>https://dev.to/wn/use-chatgpt-to-create-a-webhook-microservice-31ha</link>
      <guid>https://dev.to/wn/use-chatgpt-to-create-a-webhook-microservice-31ha</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;ChatGPT is a large language model that has been trained on a massive dataset of human language, allowing it to understand and generate natural language. One of the most exciting applications of ChatGPT is in programming, where it can automate the process of generating code. In this article, we will explore how good ChatGPT is at writing code and the potential benefits and limitations of using ChatGPT to generate programming code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's get started
&lt;/h2&gt;

&lt;p&gt;Let's try with a really long and complex reqirement prompt.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please act as a senior backend developer and write a webhook microservice in Java using the Spring Boot Framework. Please do not skip any implementations inside method. The program should be easy to maintain with high readability and expandability. No TODO should be written in the code.&lt;/p&gt;

&lt;p&gt;The microservice will be a webhook service that will be managed by Gradle. The API endpoints will follow the design standards of a REST API. The microservice will have two entities: Session and History. The Session entity will represent a webhook session, which will have a unique ID in UUID format. The History entity will represent a request made to this webhook session,including the request time, URL, and HTTP method. The History entity will have a many-to-one relationship with the Session entity.&lt;/p&gt;

&lt;p&gt;The microservice will expose the following endpoints:&lt;/p&gt;

&lt;p&gt;A "webhook" endpoint in POST method to create a new webhook session. This endpoint will generate a new UUID.&lt;br&gt;
A "webhook/{uuid}/config" endpoint to allow the user to configure what response should be returned in the "webhook" endpoint based on the regular expression string. This endpoint will accept a regular expression string and a jsonNode. A session can have multiple pairs sof regular expression and response.&lt;br&gt;
A "webhook/{uuid}" endpoint to receive HTTP requests, save them into MongoDB, and return a response matched the first regular expression based on the request URL in the "configure" endpoint in JSON. This endpoint will accept any kind of HTTP method. If the user does not configure the response, the default JSON response will be "{'status': 'success'}".&lt;br&gt;
A "webhook/{uuid}/history" endpoint to allow the user to search all webhook records.&lt;/p&gt;

&lt;p&gt;Use H2 Database.&lt;/p&gt;

&lt;p&gt;A schueduler should be set to housekeep the data periodically. A webhook session should be kept for one week only, and all child histories under the session should be removed as well.&lt;/p&gt;

&lt;p&gt;Please follow the programming style of Domain Driven Design and Test Driven Design. Using Lombok and annotation is highly encouraged.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Sure, here's an implementation of the webhook microservice in Java using the Spring Boot framework:&lt;/p&gt;

&lt;p&gt;Session Entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@Data
@NoArgsConstructor
public class Session {

    @Id
    private UUID id;
    @OneToMany(mappedBy = "session", cascade = CascadeType.ALL)
    private List&amp;lt;History&amp;gt; histories;

    public Session(UUID id) {
        this.id = id;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;History Entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@Data
@NoArgsConstructor
public class History {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String url;
    private String method;
    private LocalDateTime requestTime;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "session_id")
    private Session session;

    public History(String url, String method, LocalDateTime requestTime, Session session) {
        this.url = url;
        this.method = method;
        this.requestTime = requestTime;
        this.session = session;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SessionRepository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Repository
public interface SessionRepository extends JpaRepository&amp;lt;Session, UUID&amp;gt; {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HistoryRepository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Repository
public interface HistoryRepository extends JpaRepository&amp;lt;History, Long&amp;gt; {

    List&amp;lt;History&amp;gt; findBySessionId(UUID sessionId);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WebhookController:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RequestMapping("/webhook")
public class WebhookController {

    @Autowired
    private SessionRepository sessionRepository;
    @Autowired
    private HistoryRepository historyRepository;
    @Autowired
    private Scheduler scheduler;

    @PostMapping
    public ResponseEntity&amp;lt;UUID&amp;gt; createSession() {
        UUID sessionId = UUID.randomUUID();
        Session session = new Session(sessionId);
        sessionRepository.save(session);
        scheduler.scheduleSessionDeletion(sessionId);
        return ResponseEntity.ok(sessionId);
    }

    @PostMapping("/{uuid}/config")
    public ResponseEntity&amp;lt;Void&amp;gt; configureSession(@PathVariable("uuid") UUID sessionId,
                                                  @RequestParam("regex") String regex,
                                                  @RequestBody JsonNode response) {
        Optional&amp;lt;Session&amp;gt; optionalSession = sessionRepository.findById(sessionId);
        if (optionalSession.isPresent()) {
            Session session = optionalSession.get();
            session.addConfiguration(regex, response);
            sessionRepository.save(session);
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    @RequestMapping(value = "/{uuid}", method = {RequestMethod.GET, RequestMethod.POST})
    public ResponseEntity&amp;lt;JsonNode&amp;gt; handleRequest(@PathVariable("uuid") UUID sessionId,
                                                   @RequestBody(required = false) String requestBody,
                                                   HttpServletRequest request) {
        Optional&amp;lt;Session&amp;gt; optionalSession = sessionRepository.findById(sessionId);
        if (optionalSession.isPresent()) {
            Session session = optionalSession.get();
            JsonNode response = session.getResponse(request.getRequestURI());
            History history = new History(request.getRequestURI(), request.getMethod(),
                    LocalDateTime.now(), session);
            historyRepository.save(history);
            return ResponseEntity.ok(response);
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    @GetMapping("/{uuid}/history")
    public ResponseEntity&amp;lt;List&amp;lt;History&amp;gt;&amp;gt; getSessionHistory(@PathVariable("uuid") UUID sessionId) {
        List&amp;lt;History&amp;gt; histories = historyRepository.findBySessionId(sessionId);
        return ResponseEntity.ok(histories);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SessionService:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class SessionService {

    @Autowired
    private SessionRepository sessionRepository;

    public Session createSession() {
        UUID sessionId = UUID.randomUUID();
        Session session = new Session(sessionId);
        sessionRepository.save(session);
        return session;
    }

    public Optional&amp;lt;Session&amp;gt; getSession(UUID sessionId) {
        return sessionRepository.findById(sessionId);
    }

    public void configureSession(Session session, String regex, JsonNode response) {
        session.addConfiguration(regex, response);
        sessionRepository.save(session);
    }

    public JsonNode handleRequest(Session session, String uri) {
        return session.getResponse(uri);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SessionScheduler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component
public class SessionScheduler {

    @Autowired
    private SessionRepository sessionRepository;
    @Autowired
    private HistoryRepository historyRepository;

    @Scheduled(cron = "0 0 0 * * *")
    public void deleteExpiredSessions() {
        LocalDateTime expirationTime = LocalDateTime.now().minusWeeks(1);
        List&amp;lt;Session&amp;gt; expiredSessions = sessionRepository.findByCreatedBefore(expirationTime);
        for (Session session : expiredSessions) {
            historyRepository.deleteBySessionId(session.getId());
            sessionRepository.delete(session);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this implementation meets your requirements. Let me know if you have any questions or need further assistance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reviewing the Generated Code
&lt;/h2&gt;

&lt;p&gt;I found that the generated code is syntactically correct, but some methods are missing in the code. That's why it's important to review the generated code carefully and thoroughly, as it's very possible that the code may contain errors, especailly asking ChatGpt to write the entire program.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;Error message is prompted at start up: "Caused by: org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException: Could not determine recommended JdbcType for com.fasterxml.jackson.databind.JsonNode", so I ask ChatGpt to debug. ChatGpt succesfully points out that the JsonNode field in Class "Configuration" cannot be determined.&lt;/p&gt;

&lt;p&gt;So I request ChatGpt to fix the missing AttributeConverter for JsonNode, and the response is great.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Converter
public class JsonNodeConverter implements AttributeConverter&amp;lt;JsonNode, String&amp;gt; {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(JsonNode jsonNode) {
        try {
            return objectMapper.writeValueAsString(jsonNode);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("Failed to convert JSON node to string", e);
        }
    }

    @Override
    public JsonNode convertToEntityAttribute(String json) {
        try {
            return objectMapper.readTree(json);
        } catch (IOException e) {
            throw new IllegalArgumentException("Failed to convert string to JSON node", e);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that a shorter, well-defined and specific requirement prompt may be more effective in generating accurate code. Conversely, a longer requirement prompt is more challenging for ChatGPT to fully understand and may result in less accurate or less efficient code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;I do believe that ChatGPT's coding capabilities are constantly evolving through ongoing training and optimization. While at present, ChatGPT's coding capabilities may be limited to simpler tasks and it may struggle with more complex programming tasks.&lt;/p&gt;

&lt;p&gt;Good at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating code snippets for specific tasks or functions&lt;/li&gt;
&lt;li&gt;Providing explanations for programming concepts or terms&lt;/li&gt;
&lt;li&gt;Identifying common syntax errors or bugs in simple code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bad at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging complex programs with multiple layers of logic&lt;/li&gt;
&lt;li&gt;Writing entire programs from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ChatGPT can be a helpful tool for programmers, but it cannot fully replace human programmers. Instead, it can be used as a complementary tool for generating code, answering questions, and providing support for programming tasks.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Monitoring Your Spring Boot Application with Custom Metrics and Prometheus</title>
      <dc:creator>WN</dc:creator>
      <pubDate>Thu, 13 Apr 2023 13:21:25 +0000</pubDate>
      <link>https://dev.to/wn/monitoring-your-spring-boot-application-with-custom-metrics-and-prometheus-36h9</link>
      <guid>https://dev.to/wn/monitoring-your-spring-boot-application-with-custom-metrics-and-prometheus-36h9</guid>
      <description>&lt;h2&gt;
  
  
  1. Background
&lt;/h2&gt;

&lt;p&gt;In a microservice architecture, applications are composed of multiple independent services that work together to provide a larger functionality. While this approach offers benefits such as scalability and flexibility, it also introduces challenges in terms of monitoring and managing the application.&lt;/p&gt;

&lt;p&gt;Integrating Prometheus with your microservices on Kubernetes can help address some of these challenges by providing the ability to monitor, alert, and visualize metrics across multiple services. This can help you identify and troubleshoot issues that span multiple services, which can be difficult in a distributed system.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Objective
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To expose metrics to integrate Prometheus by Spring Boot Actuator&lt;/li&gt;
&lt;li&gt;To Define custom metrics with Micrometer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will run a demo Spring Boot application on Kubernetes with Istio.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Enabling Spring Boot Actuator Endpoints
&lt;/h2&gt;

&lt;p&gt;Spring Boot Actuator is a great tool when deploying a Spring Boot application on Kubernetes as it provides built-in support for health checks, metrics, tracing, and security, making it a lot easier to operate and maintain the application.&lt;/p&gt;

&lt;p&gt;Add Spring Actuator Dependencies to project&lt;/p&gt;

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

implementation 'org.springframework.boot:spring-boot-starter-actuator'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Just add following properties to your application.yml file to expose an endpoint at /actuator/prometheus to present a Prometheus scrape with the appropriate format.&lt;/p&gt;

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

management:
  endpoints:
    enabled-by-default: true
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Try to access the prometheus metrics endpoint (&lt;a href="http://localhost:8080/actuator/prometheus" rel="noopener noreferrer"&gt;http://localhost:8080/actuator/prometheus&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Output&lt;/p&gt;

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

# HELP executor_active_threads The approximate number of threads that are actively executing tasks
# TYPE executor_active_threads gauge
executor_active_threads{name="applicationTaskExecutor",} 0.0
# HELP jvm_memory_usage_after_gc_percent The percentage of long-lived heap pool used after the last GC event, in the range [0..1]
# TYPE jvm_memory_usage_after_gc_percent gauge
jvm_memory_usage_after_gc_percent{area="heap",pool="long-lived",} 0.0034471343796665006
# HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine
# TYPE jvm_classes_loaded_classes gauge
jvm_classes_loaded_classes 7787.0
# HELP jvm_gc_pause_seconds Time spent in GC pause
# TYPE jvm_gc_pause_seconds summary
jvm_gc_pause_seconds_count{action="end of minor GC",cause="G1 Evacuation Pause",} 1.0
jvm_gc_pause_seconds_sum{action="end of minor GC",cause="G1 Evacuation Pause",} 0.101
# HELP jvm_gc_pause_seconds_max Time spent in GC pause
# TYPE jvm_gc_pause_seconds_max gauge
jvm_gc_pause_seconds_max{action="end of minor GC",cause="G1 Evacuation Pause",} 0.0
# HELP disk_total_bytes Total space for path
# TYPE disk_total_bytes gauge
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Creating Your Custom Prometheus Metric
&lt;/h2&gt;

&lt;p&gt;Add Micrometer Prometheus Dependencies to project&lt;/p&gt;

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

runtimeOnly 'io.micrometer:micrometer-registry-prometheus'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There are 4 core metric types in Prometheus. In this article, we focus on "Counter" and "Gauge". A Counter is used to count the number of occurrences of an event, here is an example of how to define a Counter. &lt;/p&gt;

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

@RestController
@RequestMapping("/api")
public class NewsController {

    private Counter counter;

    public NewsController(MeterRegistry registry) {
        // counter
        this.counter = Counter.builder("news_fetch_request_total").
                tag("version", "v1").
                description("News Fetch Count").
                register(registry);
    }

    @GetMapping("/news")
    public List&amp;lt;News&amp;gt; getNews() {
        counter.increment();
        return List.of(new News("Good News!"), new News("Bad News!"));
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We define a Counter named "news_fetch_request_total" and register it with the MeterRegistry. We also define an api endpoint getNews() that increments the counter each time it is called.&lt;/p&gt;

&lt;p&gt;Try to access &lt;a href="http://localhost:8080/api/news" rel="noopener noreferrer"&gt;http://localhost:8080/api/news&lt;/a&gt;, it should return the following response.&lt;/p&gt;

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

[{"title":"Good News!"},{"title":"Bad News!"}]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Try to access the prometheus metrics endpoint again and search "news_fetch_request_total", and found that the value is 1.0 now.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjnnmyya6v68pykml178.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjnnmyya6v68pykml178.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And a Gauge is used to measure the value of a particular variable. We define a Gauge named "temperature" and register it with the MeterRegistry as well.&lt;/p&gt;

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

        Gauge.builder("temperature", () -&amp;gt; {
                    double min = 10, max = 30;
                    return min + new Random().nextDouble() * (max - min);
                }).
                tag("version", "v1").
                description("Temperature Record").
                register(registry);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Building and deploying a Docker image to a Kubernetes cluster
&lt;/h2&gt;

&lt;p&gt;Create a Dockerfile to dockerize the Gradle project.&lt;/p&gt;

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

FROM openjdk:17-alpine3.14
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run the following command to build and tag the Docker image.&lt;/p&gt;

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

docker build --build-arg JAR_FILE=build/libs/*.jar -t myorg/myapp .


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following annotations to deployment file to tell prometheus where to scrape metrics from the service.&lt;/p&gt;

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

annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "8080"
  prometheus.io/path: "/actuator/prometheus"    


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's deploy it on Kubernetes cluster.&lt;/p&gt;

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-v1
  labels:
    app: myapp
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
      version: v1
  template:
    metadata:
      labels:
        app: myapp
        version: v1
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
        prometheus.io/path: "/actuator/prometheus"    
    spec:
      containers:
      - name: details
        image: docker.io/myorg/myapp
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        securityContext:
          runAsUser: 1000


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Result
&lt;/h2&gt;

&lt;p&gt;If you are working with Istio, you can use the istioctl command to forward the port, enabling the access on localhost.&lt;/p&gt;

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

istioctl dashboard prometheus


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Custom metric "temperature" is found, yeah!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtgsvtfnujq04lowp391.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtgsvtfnujq04lowp391.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the help of the Micrometer Prometheus and the Spring Boot Actuator, implementing custom metrics in your application is really straightforward. Hope this tutorial has been helpful in getting you started with creating custom Prometheus metrics in your Spring Boot application!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting Started with Docker, Kubernetes, and Istio on Windows</title>
      <dc:creator>WN</dc:creator>
      <pubDate>Mon, 10 Apr 2023 14:19:19 +0000</pubDate>
      <link>https://dev.to/wn/getting-started-with-docker-kubernetes-and-istio-on-windows-3b2m</link>
      <guid>https://dev.to/wn/getting-started-with-docker-kubernetes-and-istio-on-windows-3b2m</guid>
      <description>&lt;h2&gt;
  
  
  Objective
&lt;/h2&gt;

&lt;p&gt;To provide a comprehensive guide for beginners on how to Set up a local development environment using Docker, Kubernetes, and Istio. This article will cover the necessary steps to install and familiarize oneself with these technologies.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Installing Docker on Windows
&lt;/h2&gt;

&lt;p&gt;Docker is a containerization platform used to build, ship, and run applications. &lt;/p&gt;

&lt;p&gt;Go to the Docker website (&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;https://www.docker.com/products/docker-desktop&lt;/a&gt;) and click on the "Download for Windows" button.&lt;br&gt;
Once the download is complete, double-click the downloaded file to start the installation process.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxw5pn32lilhd8enzzyg.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxw5pn32lilhd8enzzyg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the on-screen instructions to install Docker Desktop on your Windows machine.&lt;/p&gt;

&lt;p&gt;During the installation process, you may be prompted to enable Hyper-V and Containers features on your system. Follow the prompts to enable these features.&lt;/p&gt;

&lt;p&gt;Once the installation is complete, Docker Desktop should launch automatically.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz26o4p553u822f9glugf.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz26o4p553u822f9glugf.png"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Enabling Kubernetes in Docker Desktop
&lt;/h2&gt;

&lt;p&gt;Open Docker Desktop and click on the settings icon (gear icon) in the top-right corner of the window.&lt;/p&gt;

&lt;p&gt;Click on the "Kubernetes" tab in the settings window.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3baeq07v4hee5jy0z6r.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3baeq07v4hee5jy0z6r.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check "Enable Kubernetes" and "Show system containers" to enable the Kubernetes feature in Docker Desktop.&lt;/p&gt;

&lt;p&gt;After Kubernetes is enabled, you can use the "kubectl" command-line tool to manage your Kubernetes clusters.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Installing Istio on Windows
&lt;/h3&gt;

&lt;p&gt;Go to the Istio release page (&lt;a href="https://github.com/istio/istio/releases" rel="noopener noreferrer"&gt;https://github.com/istio/istio/releases&lt;/a&gt;) to download the installation file for   window&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0zxaty33ssta5qrc24k.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0zxaty33ssta5qrc24k.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;!-- The installation directory contains:&lt;/p&gt;

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

/samples - Sample applications, will be used in this tutorial
/bin - The istioctl client binary


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add &lt;code&gt;istioctl&lt;/code&gt; to Path Environment Variable&lt;br&gt;
For example, if the package is istio-1.17.2, then add &lt;code&gt;\istio-1.17.2\bin&lt;/code&gt; Directory to Path Environment Variable&lt;/p&gt;

&lt;p&gt;Run the following command to check if Istioctl is in the system PATH or not.&lt;/p&gt;

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

istioctl version


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output&lt;/p&gt;

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

client version: 1.17.2
control plane version: 1.17.2
data plane version: 1.17.2 (8 proxies)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run the following command to install Istio.&lt;/p&gt;

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

$ istioctl install --set profile=demo


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The "demo" profile is intended for evaluating a broad set of Istio features. For development purposes, it is a good option to start with.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Enable Kiali (Optional)
&lt;/h3&gt;

&lt;p&gt;Kiali is a web-based dashboard that provides an overview of the microservices within an Istio service mesh. It allows you to view the topology of your microservices, see the traffic flow between them, and monitor the health of the services. &lt;/p&gt;

&lt;p&gt;Istio provides a basic sample installation to quickly get Kiali up and running:&lt;/p&gt;

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

$ kubectl apply -f /samples/addons/kiali.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will deploy Kiali into your cluster. This is intended for demonstration only, and is not tuned for performance or security.&lt;/p&gt;

&lt;p&gt;Once the installation is complete, run the following command to open Kiali web UI:&lt;/p&gt;

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

istioctl dashboard kiali


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open a web browser and navigate to the Kiali dashboard by entering the following URL: &lt;a href="http://localhost:20001/kiali" rel="noopener noreferrer"&gt;http://localhost:20001/kiali&lt;/a&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjrmbt3y2lmwckxzxbev.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjrmbt3y2lmwckxzxbev.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://istio.io/latest/docs/ops/integrations/kiali/#installation" rel="noopener noreferrer"&gt;https://istio.io/latest/docs/ops/integrations/kiali/#installation&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Creating a sample application
&lt;/h3&gt;

&lt;p&gt;The default Istio installation uses automatic sidecar injection. Label the namespace that will host the application with istio-injection=enabled:&lt;/p&gt;

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

kubectl label namespace default istio-injection=enabled


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Deploy the sample "bookinfo" project using the kubectl command:&lt;/p&gt;

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

kubectl apply -f /samples/bookinfo/platform/kube/bookinfo.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Confirm all services and pods are correctly defined and running:&lt;/p&gt;

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

kubectl get pods


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output&lt;/p&gt;

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

NAME                             READY   STATUS    RESTARTS        AGE
details-v1-5ffd6b64f7-czr7w      2/2     Running   2 (6h40m ago)   10h
productpage-v1-979d4d9fc-lc59r   2/2     Running   2 (6h40m ago)   10h
ratings-v1-5f9699cfdf-ppf74      2/2     Running   2 (6h40m ago)   10h
reviews-v1-569db879f5-cj2xr      2/2     Running   2 (6h40m ago)   10h
reviews-v2-65c4dc6fdc-kjmnm      2/2     Running   2 (6h40m ago)   10h
reviews-v3-c9c4fb987-2hg5x       2/2     Running   2 (6h40m ago)   10h


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you previously installed Kiali, you can also click on the "Workloads" tab in the Kiali dashboard to see a list of all the workloads, including Pods, and their status.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0l82a60tkjmwtagfguw.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0l82a60tkjmwtagfguw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm that the Bookinfo application is running, send a request to it by a curl command from some pod, for example from ratings:&lt;/p&gt;

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

kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | findstr /r "&amp;lt;title&amp;gt;.*&amp;lt;/title&amp;gt;"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output&lt;/p&gt;

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

    &amp;lt;title&amp;gt;Simple Bookstore App&amp;lt;/title&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Using a Gateway to Expose Your App
&lt;/h2&gt;

&lt;p&gt;Now that the Bookinfo services are up and running, you need to make the application accessible from outside of your Kubernetes cluster, e.g., from a browser. &lt;/p&gt;

&lt;p&gt;Run the following command to apply a gateway used for this purpose.&lt;/p&gt;

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

kubectl apply -f /samples/bookinfo/networking/bookinfo-gateway.yaml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run the following command to get the external IP address of the Istio ingress gateway:&lt;/p&gt;

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

kubectl get svc istio-ingressgateway -n istio-system


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note the "EXTERNAL-IP" value for the "istio-ingressgateway" service. This is the IP address that you will use to access the Bookinfo application externally.&lt;/p&gt;

&lt;p&gt;For example, if the external IP address is 192.168.99.100, you would enter the following URL: &lt;a href="http://192.168.99.100/productpage" rel="noopener noreferrer"&gt;http://192.168.99.100/productpage&lt;/a&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvr0rek2h1hth9k2652t.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvr0rek2h1hth9k2652t.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visit the Istio documentation website (&lt;a href="https://istio.io/latest/docs/" rel="noopener noreferrer"&gt;https://istio.io/latest/docs/&lt;/a&gt;) to learn more about Istio's powerful features and best practices. Happy hacking!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>istio</category>
    </item>
  </channel>
</rss>
