<?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: Roy Ra</title>
    <description>The latest articles on DEV Community by Roy Ra (@sangwoo).</description>
    <link>https://dev.to/sangwoo</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%2F827181%2F400c075a-99c2-4852-b98a-336244dc20f2.jpeg</url>
      <title>DEV Community: Roy Ra</title>
      <link>https://dev.to/sangwoo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sangwoo"/>
    <language>en</language>
    <item>
      <title>Setting up Datadog APM on EKS + Fargate</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Fri, 09 Sep 2022 16:33:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/apm-with-datadog-on-eks-fargate-17i2</link>
      <guid>https://dev.to/aws-builders/apm-with-datadog-on-eks-fargate-17i2</guid>
      <description>&lt;p&gt;In this post, I will go through the steps required to integrate Datadog APM with EKS using Fargate as computing option.&lt;/p&gt;

&lt;p&gt;The process is very similar to &lt;a href="https://dev.to/aws-builders/setting-up-datadog-apm-on-ecs-fargatespring-boot-4ema"&gt;Setting up Datadog APM on ECS, Fargate(Spring Boot)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The datadog agent container must run as a sidecar of each of your application's container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing datadog agent
&lt;/h2&gt;

&lt;p&gt;We will use helm to install datadog agent into Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;First, run the command below to add datadog's repository to helm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add datadog https://helm.datadoghq.com
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we have to edit &lt;a href="https://github.com/DataDog/helm-charts/blob/main/charts/datadog/values.yaml" rel="noopener noreferrer"&gt;values.yaml&lt;/a&gt; file, which will be used as a configuration file used by datadog agent running in the cluster. The fields I overrided(edited) are as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;registry&lt;/code&gt;: Apply the value of AWS in the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;datadog.apiKey&lt;/code&gt;: Value of the API key that you created in Datadog.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;datadog.apm.portEnabled&lt;/code&gt;: Set to true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, let's run the command below to finish installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;datadog-agent &lt;span class="nt"&gt;-f&lt;/span&gt; values.yaml datadog/datadog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Configuring APM
&lt;/h2&gt;

&lt;p&gt;To integrate Datadog APM with your application, you must make sure that your application uses datadog agent when running itself. For instance, I have a Spring boot application (JVM), and corresponding dockerfile looks as below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is 1 datadog agent installed on your EKS cluster, and another datadog agent running as a process of your application to collect metrics.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;openjdk:11-jdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; JAR_FILE=build/libs/MY_APP.jar&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ${JAR_FILE} application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; application.jar extract

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:11-jdk&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;RUN  &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; dd-java-agent.jar &lt;span class="s1"&gt;'https://dtdg.co/latest-java-tracer'&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/application.jar ./application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/spring-boot-loader/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/snapshot-dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/application/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ Asia/Seoul&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-javaagent:dd-java-agent.jar", "-Ddd.profiling.enabled=true", "-XX:FlightRecorderOptions=stackdepth=256", "org.springframework.boot.loader.JarLauncher"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, &lt;code&gt;dd-java-agent.jar&lt;/code&gt; uses port 8126 of container running datadog agent to send metrics to datadog.&lt;/p&gt;

&lt;p&gt;Now all we have to do is to add another container spec to Kubernetes deployment file.&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;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;planit-deployment&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;planit&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&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;planit&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;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;revisionHistoryLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&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&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;planit&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&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;planit&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONTAINER_NAME"&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_IMAGE_NAME"&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;Always&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;8080&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;datadog-agent&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;datadog/agent:latest&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;8126&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;traceport&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;DD_KUBERNETES_KUBELET_NODENAME&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;DD_API_KEY&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_API_KEY&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;DD_SITE&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_SITE&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;DD_EKS_FARGATE&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_EKS_FARGATE&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;DD_APM_ENABLED&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_APM_ENABLED&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;DD_ENV&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_ENV&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;DD_SERVICE&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_SERVICE&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;DD_VERSION&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_VERSION&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;DD_PROFILING_ENABLED&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_PROFILING_ENABLED&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;DD_APM_IGNORE_RESOURCES&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_APM_IGNORE_RESOURCES&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;DD_LOGS_INJECTION&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secret&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;DD_LOGS_INJECTION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;datadog-secret&lt;/code&gt; is a Kubernetes secret object I created to store configuration values related to Datadog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After deploying new Kubernetes deployment, you should be able to see APM working well as below.&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%2Fuhwris1jh9t0co278vgj.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%2Fuhwris1jh9t0co278vgj.png" alt="DD APM UI"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eks</category>
      <category>fargate</category>
      <category>datadog</category>
      <category>apm</category>
    </item>
    <item>
      <title>Monitoring EKS cluster with Prometheus and Grafana</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Tue, 06 Sep 2022 09:03:04 +0000</pubDate>
      <link>https://dev.to/aws-builders/monitoring-eks-cluster-with-prometheus-and-grafana-1kpb</link>
      <guid>https://dev.to/aws-builders/monitoring-eks-cluster-with-prometheus-and-grafana-1kpb</guid>
      <description>&lt;p&gt;In this post, I will go through the minimum steps to configure Prometheus and Grafana in the existing EKS cluster, which is most likely used for monitoring and alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Node Groups
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating new node group
&lt;/h3&gt;

&lt;p&gt;Assuming that there are no pre-existing node groups, let's create one.&lt;/p&gt;

&lt;p&gt;First, let's attach IAM role to Kubernetes service account using &lt;code&gt;eksctl&lt;/code&gt;.&lt;/p&gt;

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

eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aws-node &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="s2"&gt;"AmazonEKSVPCCNIRole"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--override-existing-serviceaccounts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now let's head over to &lt;code&gt;AWS Management Console -&amp;gt; EKS -&amp;gt; Your cluster -&amp;gt; Compute -&amp;gt; Add node group&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We HAVE to use EC2 for Prometheus and Grafana, since they will both need volumes mounted to them. &lt;/p&gt;

&lt;p&gt;When creating node group, we have to attach an IAM role to EC2 worker nodes. For easy demonstration, I created a new IAM role and attached policies as below.&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%2Fp57qp24v01oy3t6kf3b1.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%2Fp57qp24v01oy3t6kf3b1.png" alt="New IAM role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When filling all the information required to create a node group, just make sure to apply values as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instance type: m5.large&lt;/li&gt;
&lt;li&gt;Subnets: Your private subnets within the VPC that EKS cluster exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can leave all the other values as it is.&lt;/p&gt;

&lt;p&gt;To confirm that your EC2 worker nodes are properly running, run the command below. The result should say that 2 pods are running.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-l&lt;/span&gt; k8s-app&lt;span class="o"&gt;=&lt;/span&gt;aws-node
NAME             READY   STATUS    RESTARTS   AGE
aws-node-6szct   1/1     Running   0          4s
aws-node-nz5cd   1/1     Running   0          8s


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  aws-ebs-csi-driver
&lt;/h3&gt;

&lt;p&gt;Prometheus and Grafana needs persistent storage attached to them, which is also called PV(Persistent Volume) in terms of Kubernetes. &lt;/p&gt;

&lt;p&gt;For stateful workloads to use Amazon EBS volumes as PV, we have to add aws-ebs-csi-driver into the cluster. &lt;/p&gt;
&lt;h3&gt;
  
  
  Associating IAM role to Service account
&lt;/h3&gt;

&lt;p&gt;Before we add aws-ebs-csi-driver, we first need to create an IAM role, and associate it with Kubernetes service account.&lt;/p&gt;

&lt;p&gt;Let's use an example policy file, which you can download using the command below.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; ebs-csi-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/docs/example-iam-policy.json


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

&lt;/div&gt;

&lt;p&gt;Now let's create a new IAM policy with that file.&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;EBS_CSI_POLICY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AmazonEBSCSIPolicy
aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--policy-name&lt;/span&gt; &lt;span class="nv"&gt;$EBS_CSI_POLICY_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://ebs-csi-policy.json
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EBS_CSI_POLICY_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-2 iam list-policies &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Policies[?PolicyName==`'&lt;/span&gt;&lt;span class="nv"&gt;$EBS_CSI_POLICY_NAME&lt;/span&gt;&lt;span class="s1"&gt;'`].Arn'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$EBS_CSI_POLICY_ARN&lt;/span&gt;
&lt;span class="c"&gt;# arn:aws:iam::12345678:policy/AmazonEBSCSIPolicy&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After that, let's attach the new policy to Kubernetes service account.&lt;/p&gt;

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

eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; ebs-csi-controller-irsa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt; &lt;span class="nv"&gt;$EBS_CSI_POLICY_ARN&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--override-existing-serviceaccounts&lt;/span&gt; &lt;span class="nt"&gt;--approve&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And now, we're ready to install aws-ebs-csi-driver!&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing aws-ebs-csi-driver
&lt;/h3&gt;

&lt;p&gt;Assuming that &lt;code&gt;helm&lt;/code&gt; is installed, let's add new helm repository as below.&lt;/p&gt;

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

helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver
helm repo update


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

&lt;/div&gt;

&lt;p&gt;After adding new helm repository, let's install aws-ebs-csi-driver with below command using helm.&lt;/p&gt;

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

helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; aws-ebs-csi-driver &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.2.4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.controller.create&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.snapshot.create&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;enableVolumeScheduling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;enableVolumeResizing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;enableVolumeSnapshot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.snapshot.name&lt;span class="o"&gt;=&lt;/span&gt;ebs-csi-controller-irsa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.controller.name&lt;span class="o"&gt;=&lt;/span&gt;ebs-csi-controller-irsa &lt;span class="se"&gt;\&lt;/span&gt;
  aws-ebs-csi-driver/aws-ebs-csi-driver


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

&lt;/div&gt;




&lt;h2&gt;
  
  
  Configuring Prometheus
&lt;/h2&gt;

&lt;p&gt;First, let's create a new Kubernetes namespace where all Prometheus-related resources will reside in.&lt;/p&gt;

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

kubectl create ns prometheus


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

&lt;/div&gt;

&lt;p&gt;We will install Prometheus using helm, so let's add the Prometheus repository to helm and install it running the command below.&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;# Add helm repositories for Prometheus&lt;/span&gt;
helm repo add kube-state-metrics https://kubernetes.github.io/kube-state-metrics
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

&lt;span class="c"&gt;# Install Prometheus&lt;/span&gt;
helm upgrade &lt;span class="nt"&gt;-i&lt;/span&gt; prometheus prometheus-community/prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; alertmanager.persistentVolume.storageClass&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gp2"&lt;/span&gt;,server.persistentVolume.storageClass&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gp2"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Few minutes after installing Prometheus, we can see all the resources created as below.&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%2Frb9dzg69egrt9a67t2ss.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%2Frb9dzg69egrt9a67t2ss.png" alt="Prometheus resources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test if they are working, let's use kubectl.&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; prometheus deploy/prometheus-server 8081:9090 &amp;amp;


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

&lt;/div&gt;

&lt;p&gt;Now let's launch web browser, and go to &lt;code&gt;localhost:8081&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The below web page should be seen if Prometheus has been successfully installed.&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%2Fmrpt9nvuvm23ro9ff0zt.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%2Fmrpt9nvuvm23ro9ff0zt.png" alt="Prometheus UI"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuring Grafana
&lt;/h2&gt;

&lt;p&gt;As the same as Prometheus, we will create a dedicated Kubernetes namespace for Grafana, and install it using helm.&lt;/p&gt;

&lt;p&gt;First let's create a new namespace.&lt;/p&gt;

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

kubectl create ns grafana


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

&lt;/div&gt;

&lt;p&gt;We also need a manifest file, which will be used to configure Grafana. Below is an example of the file, called &lt;code&gt;grafana.yaml&lt;/code&gt;.&lt;/p&gt;

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

&lt;span class="c1"&gt;# grafana.yaml&lt;/span&gt;
&lt;span class="na"&gt;datasources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datasources.yaml&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="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;datasources&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;Prometheus&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;prometheus&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://prometheus-server.prometheus.svc.cluster.local&lt;/span&gt;
        &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
        &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now let's install Grafana using helm.&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;# Add helm repository for Grafana&lt;/span&gt;
helm repo add grafana https://grafana.github.io/helm-charts

&lt;span class="c"&gt;# Install Grafana&lt;/span&gt;
helm &lt;span class="nb"&gt;install &lt;/span&gt;grafana grafana/grafana &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; grafana &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; persistence.storageClass&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gp2"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; persistence.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;adminPassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_PASSWORD'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--values&lt;/span&gt; grafana.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; service.type&lt;span class="o"&gt;=&lt;/span&gt;NodePort


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

&lt;/div&gt;

&lt;p&gt;Before exposing Grafana to the world, let's see how the Kubernetes service running Grafana is defined.&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%2Fm87qa88vsp801ijpsial.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%2Fm87qa88vsp801ijpsial.png" alt="Grafana service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that the target port is 3000, which is the port used by pods running Grafana.&lt;/p&gt;

&lt;p&gt;So we have to attach a new Security group to EC2 worker nodes, allowing inbound requests for port 3000.&lt;/p&gt;

&lt;p&gt;We can do this by creating a new Security group, and attaching it to EC2 worker nodes in the EC2 console of AWS Management Console.&lt;/p&gt;

&lt;p&gt;After applying new security group to EC2 worker nodes, let's define a new Kubernetes ingress, which will provision an ALB.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To make Kubernetes ingress to create an ALB, we have to install &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html" rel="noopener noreferrer"&gt;aws-load-balancer-controller&lt;/a&gt; first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's see how ingress looks like.&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;networking.k8s.io/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;Ingress&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;grafana-ingress&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;grafana&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/load-balancer-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana-alb&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/target-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PUBLIC_SUBNET_IDs}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/listen-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[{"HTTPS":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;443}]'&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/certificate-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ACM_CERT_ARN}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/security-groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ALB_SECURITY_GROUP_ID}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000"&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/health&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${YOUR_ROUTE53_DOMAIN}&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;grafana&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After applying new ingress and having new ALB ready, we can head over to &lt;code&gt;${YOUR_ROUTE53_DOMAIN}&lt;/code&gt; and see that Grafana is ready as below.&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%2F5f2ojiasopy0sxm7gnxl.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%2F5f2ojiasopy0sxm7gnxl.png" alt="Grafana UI"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's it! In this post, we created a new node group which will be used by Prometheus and Grafana, and installed and configured both Prometheus and Grafana.&lt;/p&gt;

&lt;p&gt;Hope this post helps you!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After &lt;a href="https://grafana.com/docs/grafana/v9.0/dashboards/export-import/" rel="noopener noreferrer"&gt;importing Grafana dashboard&lt;/a&gt; using &lt;code&gt;3119&lt;/code&gt;, I got a new dashboard as below.&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%2Frqu6uyfwdr0p8e5w2j16.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%2Frqu6uyfwdr0p8e5w2j16.png" alt="Grafana UI2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>eks</category>
      <category>kubernetes</category>
      <category>prometheus</category>
      <category>grafana</category>
    </item>
    <item>
      <title>Using ArgoCD on EKS + Fargate</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Sat, 03 Sep 2022 05:23:53 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-argocd-on-eks-fargate-47lb</link>
      <guid>https://dev.to/aws-builders/using-argocd-on-eks-fargate-47lb</guid>
      <description>&lt;p&gt;In this post, I will describe how to use ArgoCD on existing EKS cluster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I will not go through all the steps required to configure ArgoCD, but some troubles I encountered.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This &lt;a href="https://argo-cd.readthedocs.io/en/stable/getting_started/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; from ArgoCD provides all the information about how to install ArgoCD into Kubernetes cluster, and sync(deploy) applications.&lt;/p&gt;

&lt;p&gt;But I encountered some troubles when trying to access the ArgoCD API Server on EKS which is using Fargate.&lt;/p&gt;

&lt;p&gt;Since Fargate pods are only allowed to be launched within private subnet, we have to use Kubernetes Ingress to route requests from outside world into actual pods. And with AWS Load-Balancer-Controller add-on, when we create a Kubernetes Ingress, it will provision an AWS Application Load Balancer on behalf, accepting all the requests in the frontier.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can install AWS Load-Balancer-Controller add-on following &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we install both ArgoCD and AWS Load-Balancer-Controller add-on into EKS Cluster, we will fail to connect to ArgoCD API Server. This is because the pods running ArgoCD API Server are exposed using port 8080, and security group attached to pods do not accept inbound requests from 8080 by default. This results in ALB's health check failure on its target groups. &lt;/p&gt;

&lt;p&gt;So we have to define new security group for ArgoCD API Server pods, and apply it.&lt;/p&gt;

&lt;p&gt;To do this, you can simply follow &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html#sg-pods-example-deployment" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The new security group to attach to pods needs to meet requirements below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow inbound request on port 53(TCP) from security group of the EKS cluster.&lt;/li&gt;
&lt;li&gt;Allow inbound request on port 53(UDP) from security group of the EKS cluster.&lt;/li&gt;
&lt;li&gt;Allow inbound request on port 8080(TCP).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create new security group, and apply the yaml file below.&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="c1"&gt;# argocd-pod-sg-policy.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;vpcresources.k8s.aws/v1beta1&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;SecurityGroupPolicy&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;argo-sgp&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;argocd&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;podSelector&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;argocd-server&lt;/span&gt;
  &lt;span class="na"&gt;securityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;groupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${NEW_CREATED_SECURITY_GROUP_ID}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${EKS_CLUSTER_SECURITY_GROUP_ID}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's apply the yaml file above and create new SecurityGroupPolicy. Also, this new SecurityGroupPolicy doesn't apply to already-running pods, so we have to restart all ArgoCD API Server pods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete &lt;span class="nt"&gt;--all&lt;/span&gt; pod &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, let's define a Kubernetes Ingress, which will eventually provision a new AWS Application Load Balancer.&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;networking.k8s.io/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;Ingress&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;argocd-ingress&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;argocd&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/load-balancer-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-alb&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/target-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PUBLIC_SUBNET_IDS}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/certificate-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ACM_CERT_ARN}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/security-groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ALB_SECURITY_GROUP}&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/backend-protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/conditions.argogrpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "Content-Type", "values":["application/grpc"]}}]&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/listen-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[{"HTTPS":443}]'&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-argocd.planit-study.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&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;argogrpc&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&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;argocd-server&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the yaml file above, we used service named &lt;code&gt;argogrpc&lt;/code&gt;. &lt;br&gt;
ArgoCD API Server runs on both gRPC and HTTP/HTTPS.&lt;br&gt;
gRPC is used to serve requests from ArgoCD CLI, and HTTP/HTTPS is used for UI(web) requests. &lt;/p&gt;

&lt;p&gt;It is ideal to create new service only serving gRPC requests, so let's create one, which is also in the ArgoCD documentation.&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;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;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/backend-protocol-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP2&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;argocd-server&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;argogrpc&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;argocd&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;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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443"&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;443&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;8080&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;argocd-server&lt;/span&gt;
  &lt;span class="na"&gt;sessionAffinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&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;NodePort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can successfully access ArgoCD API Server via both CLI and Web(UI)! And that's it! Hope this post helps you :)&lt;/p&gt;

</description>
      <category>eks</category>
      <category>kubernetes</category>
      <category>argocd</category>
      <category>fargate</category>
    </item>
    <item>
      <title>Deploying simple application to EKS on Fargate.</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Fri, 26 Aug 2022 11:17:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploying-simple-application-to-eks-on-fargate-5ee2</link>
      <guid>https://dev.to/aws-builders/deploying-simple-application-to-eks-on-fargate-5ee2</guid>
      <description>&lt;p&gt;In this post, I'll describe how to deploy a server application on EKS using Fargate. I'll also go through all the problems I encountered while deploying it. This is my first time using EKS!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step (1) - Configuring credentials and environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Below are the required tools that we need to successfully deploy EKS &lt;strong&gt;in this post&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI v2: Install following &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;kubectl: Install version 1.23.6, since we will be deploying using kubernetes version 1.21&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

  curl &lt;span class="nt"&gt;-o&lt;/span&gt; kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.22.6/2022-03-09/bin/darwin/amd64/kubectl
  &lt;span class="nb"&gt;cd&lt;/span&gt; ~/.kube
  &lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./kubectl
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; ./kubectl &lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin/kubectl &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=$PATH:$HOME/bin'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bash_profile
  kubectl version &lt;span class="nt"&gt;--short&lt;/span&gt; &lt;span class="nt"&gt;--client&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;eksctl: Follow &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt; to install eksctl.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After installing all 3 tools above, create an IAM user with programming access enabled to use in your local terminal, and do some tasks. In my case, I created a temporary IAM user and attached &lt;code&gt;AdministratorAccess&lt;/code&gt; policy to it.&lt;/p&gt;

&lt;p&gt;We will also use KMS, which provides easy ways to configure and manage encryption keys used by various AWS services.&lt;/p&gt;

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

aws kms create-alias &lt;span class="nt"&gt;--alias-name&lt;/span&gt; &lt;span class="nb"&gt;alias&lt;/span&gt;/demo-eks &lt;span class="nt"&gt;--target-key-id&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;aws kms create-key &lt;span class="nt"&gt;--query&lt;/span&gt; KeyMetadata.Arn &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We will use the created CMK's ARN later on, so let's store it as an environment variable.&lt;/li&gt;
&lt;/ul&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;MASTER_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws kms describe-key &lt;span class="nt"&gt;--key-id&lt;/span&gt; &lt;span class="nb"&gt;alias&lt;/span&gt;/demo-eks &lt;span class="nt"&gt;--query&lt;/span&gt; KeyMetadata.Arn &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$MASTER_ARN&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; master_arn.txt


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

&lt;/div&gt;




&lt;h2&gt;
  
  
  Step (2) - Configuring VPC
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now we're going to create a new VPC, which will be used by EKS cluster that we will create later on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/sang-w0o/9e189c34bb25e19896703f156f0da507" rel="noopener noreferrer"&gt;eks-vpc-3az.yaml&lt;/a&gt; is a file which will be used by CloudFormation to create resources below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC.&lt;/li&gt;
&lt;li&gt;3 public subnets.&lt;/li&gt;
&lt;li&gt;3 private subnets.&lt;/li&gt;
&lt;li&gt;1 internet gateway.&lt;/li&gt;
&lt;li&gt;3 NAT gateways.&lt;/li&gt;
&lt;li&gt;3 public route tables, and 3 private route tables for each subnet, respectively.&lt;/li&gt;
&lt;li&gt;Security groups for EKS Cluster.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Let's create this VPC using the command below.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

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

  aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &lt;span class="s2"&gt;"demo-eks-vpc"&lt;/span&gt;
    &lt;span class="nt"&gt;--template-file&lt;/span&gt; &lt;span class="s2"&gt;"eks-vpc-3az.yaml"&lt;/span&gt;
    &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_NAMED_IAM


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For about 5 minutes later, we can see that a brand new VPC has been created in AWS Management Console.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigating to CloudFormation page, after selecting the "demo-eks-vpc" stack, we can see the outputs as below image in the &lt;code&gt;output&lt;/code&gt; tab.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Feytbwwx3gdoxtg9zjtqi.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%2Feytbwwx3gdoxtg9zjtqi.png" alt="CloudFormation stack outputs"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  (3) Provisioning EKS Cluster using eksctl
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting environment variables
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The ID of VPC that we created in step (2) and it's subnet IDs will be used frequently in this step, so lets' run a script as below to use it as environment variables.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;span class="c"&gt;#VPC ID&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;vpc_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-vpcs &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tag:Name,Values&lt;span class="o"&gt;=&lt;/span&gt;demo-eks-vpc | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Vpcs[].VpcId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$vpc_ID&lt;/span&gt;

&lt;span class="c"&gt;#Subnet ID, CIDR, Subnet Name&lt;/span&gt;
aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vpc_subnet.txt
aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; vpc_subnet.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;vpc_subnet.txt

&lt;span class="c"&gt;# Store VPC ID, Subnet IDs as environment variables.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet01&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PublicSubnet01/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet02&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PublicSubnet02/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet03&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PublicSubnet03/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet01&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PrivateSubnet01/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet02&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PrivateSubnet02/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet03&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ec2 describe-subnets &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$vpc_ID&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Subnets[]|.SubnetId+" "+.CidrBlock+" "+(.Tags[]|select(.Key=="Name").Value)'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/demo-eks-vpc-PrivateSubnet03/{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export vpc_ID=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;vpc_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PublicSubnet01=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet01&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PublicSubnet02=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet02&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PublicSubnet03=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PublicSubnet03&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PrivateSubnet01=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet01&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PrivateSubnet02=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet02&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export PrivateSubnet03=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet03&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/.bash_profile
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bash_profile


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;After running all the commands above, VPC ID and subnet IDs will be stored in environment variables. Also there will be a file called &lt;code&gt;vpc_subnet.txt&lt;/code&gt;, and it will look as below.&lt;/li&gt;
&lt;/ul&gt;

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

vpc-0e88a2ed7a32c0336
subnet-02b5356084f4355cb 10.11.16.0/20 demo-eks-vpc-PublicSubnet02
subnet-0ea280f1567234a3b 10.11.253.0/24 demo-eks-vpc-TGWSubnet03
subnet-0a79d22a3acf610bf 10.11.32.0/20 demo-eks-vpc-PublicSubnet03
subnet-0c96f4c64e724524b 10.11.251.0/24 demo-eks-vpc-TGWSubnet01
subnet-0d5d255e8542cf405 10.11.64.0/20 demo-eks-vpc-PrivateSubnet02
subnet-0c3c37774542ac95c 10.11.252.0/24 demo-eks-vpc-TGWSubnet02
subnet-07f97eaa984b5ced2 10.11.0.0/20 demo-eks-vpc-PublicSubnet01
subnet-0ebb353a91c36ab17 10.11.48.0/20 demo-eks-vpc-PrivateSubnet01
subnet-0e3c3abe52dbe07e8 10.11.80.0/20 demo-eks-vpc-PrivateSubnet03


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Let's also store AWS region code.&lt;/li&gt;
&lt;/ul&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;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-northeast-2 &lt;span class="c"&gt;# in my case&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Creating EKS Cluster
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cluster's name and version of Kubernetes will be used by yaml files, so let's store it as environment variables.&lt;/li&gt;
&lt;/ul&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;ekscluster_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-eks"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;eks_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.21"&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To create EKS cluster, we will use &lt;a href="https://gist.github.com/sang-w0o/e095a70170cf7e699377bde5e09f1b7b" rel="noopener noreferrer"&gt;eks-cluster-3az.yaml&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We will configure nodes to use Fargate instead of EC2, and accordin to &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate-profile.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;, Fargate needs to be provisioned within private subnets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's run the command below, to fill environment variables with actual values in eks-cluster-3az.yaml&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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; &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;gt; eks-cluster-3az.yaml
# A simple example of ClusterConfig object:
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ekscluster_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
  region: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
  version: "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;eks_version&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"

vpc:
  id: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;vpc_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
  subnets:
    private:
      PrivateSubnet01:
        az: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;a
        id: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet01&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      PrivateSubnet02:
        az: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;b
        id: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet02&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      PrivateSubnet03:
        az: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;c
        id: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet03&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

secretsEncryption:
  keyARN: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MASTER_ARN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

fargateProfiles:
  - name: demo-dev-fp
    selectors:
      - namespace: demo-dev
      - namespace: kube-system
    subnets:
      - &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet01&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      - &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet02&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      - &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PrivateSubnet03&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

cloudWatch:
  clusterLogging:
    enableTypes:
      ["api", "audit", "authenticator", "controllerManager", "scheduler"]
&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;Fargate profile is needed to run resources on Fargate.(fargateProfiles). Any resources that meet criteria defined in &lt;code&gt;fargateProfiles.selectors&lt;/code&gt; will run on Fargate.&lt;br&gt;
In the example above, we used demo-dev, the namespace that application-related resources will use, and kube-system. &lt;br&gt;
kube-system will be used to provision additional resources such as aws-load-balancer-controller which we will install later.&lt;br&gt;
If we rule out kube-system namespace, we will get an error as below when installing aws-load-balancer-controller.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ingress 0/2 nodes are available: 2 node(s) had taint {eks.amazonaws.com/compute-type: fargate}, that the pod didn't tolerate.


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

&lt;ul&gt;
&lt;li&gt;Now lets' run the command below to create cluster!&lt;/li&gt;
&lt;/ul&gt;

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

eksctl create cluster &lt;span class="nt"&gt;--config-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-cluster-3az.yaml


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Connecting to EKS cluster
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When we run &lt;code&gt;kubectl get svc&lt;/code&gt; command, we will get an output like below, which means that EKS cluster is ready!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   172.20.0.1   &amp;lt;none&amp;gt;        443/TCP   43m


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Value of PORT(S) might be different, but don't worry.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  (4) Deploying application!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First, let's create a Kubernetes namespace, where we will create resources for our application. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;In the above when we created faragte profile, we put &lt;code&gt;namespace: demo-dev&lt;/code&gt; in the &lt;code&gt;fargateProfiles.selectors&lt;/code&gt; field.&lt;/p&gt;
&lt;/blockquote&gt;

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

kubectl create ns demo-dev


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Installing AWS Load Balancer Controller add-on
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AWS Load Balancer Controller manages AWS Elastic Load Balancers(ELB) used by Kubernetes cluster. This controller provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provision new AWS ALB when Kubernetes &lt;code&gt;Ingress&lt;/code&gt; is created.&lt;/li&gt;
&lt;li&gt;Provision new AWS NLB when Kubernetes &lt;code&gt;LoadBalancer&lt;/code&gt; is created.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Before installing AWS Load Balancer Controller, we must integrate OIDC provider first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuring OIDC ID Provider(IdP) to EKS cluster allows you to use AWS IAM roles for Kubernetes service accounts, and this requires an IAM OIDC provider in the cluster. Let's run the command below to integrate OIDC into the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

eksctl utils associate-iam-oidc-provider &lt;span class="nt"&gt;--cluster&lt;/span&gt; demo-eks &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;span class="c"&gt;# 2022-08-23 16:04:39 [ℹ]  will create IAM Open ID Connect provider for cluster "demo-eks" in "ap-northeast-2"&lt;/span&gt;
&lt;span class="c"&gt;# 2022-08-23 16:04:40 [✔]  created IAM Open ID Connect provider for cluster "demo-eks" in "ap-northeast-2"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now we're ready to install AWS Load Balancer Controller add-on! Install by following steps described in &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying application
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In my case, I was using an application which used port number 8080 by default. I didn't want to change default this port number, so I created a simple yaml file(&lt;a href="https://gist.github.com/sang-w0o/6651690bd6d16c8d21aff95604b39f24" rel="noopener noreferrer"&gt;deployment.yaml&lt;/a&gt;) for Kubernetes Deployment, and also created a yaml file for Kubernetes Service as below.&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;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;demo-dev-svc&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;demo-dev&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&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;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;80&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;8080&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;NodePort&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;As you can see, type of service is &lt;code&gt;NodePort&lt;/code&gt;, and the type should be &lt;code&gt;NodePort&lt;/code&gt; when using Fargate on EKS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, the service will receive requests from port number 80, and forward the request to pods using pod's port number 8080.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lastly, let's create a Kubernetes Ingress, which will eventually create an AWS ALB with the help of AWS Load Balancer Controller add-on.&lt;/p&gt;&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;networking.k8s.io/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;Ingress&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;demo-dev-ingress&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;demo-dev&lt;/span&gt;
&lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/load-balancer-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-dev-lb&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/target-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PUBLIC_SUBNET_IDS}&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/listen-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[{"HTTPS":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;443}]'&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/certificate-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ACM_CERT_ARN}&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/security-groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${SECURITY_GROUP_FOR_ALB}&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080"&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/actuator/health&lt;/span&gt;
  &lt;span class="na"&gt;alb.ingress.kubernetes.io/success-codes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb-url.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;service&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;demo-dev-svc&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In the spec, we will make ALB to forward all request to &lt;code&gt;demo-dev-svc&lt;/code&gt; Kubernetes service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can find about annotations in &lt;a href="https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/ingress/annotations/" rel="noopener noreferrer"&gt;this document&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Note that &lt;code&gt;alb.ingress.kubernetes.io/target-type&lt;/code&gt; should be set to &lt;code&gt;ip&lt;/code&gt; when using Fargate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also the ALB provisioned when creating this Ingress should receive traffic from the internet, so it should be placed in public subnets(&lt;code&gt;alb.ingress.kubernetes.io/subnets: ${PUBLIC_SUBNET_IDS}&lt;/code&gt;), and it should be internet-facing(&lt;code&gt;alb.ingress.kubernetes.io/scheme: internet-facing&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now when we apply new Ingress to Kubernetes, new ALB will be created with appropriate listener rule configured.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But when we view the health check of ALB on target groups, it will fail, saying &lt;code&gt;504 gateway timeout&lt;/code&gt;.&lt;br&gt;
since default security group attached to Fargate pods do not allow inbound requests on port 8080.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resolving issue: 504 Gateway Timeout
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This is because default security group attached to Fargate pods does not allow inbound requests on port 8080.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;So we need to configure custom security groups to Fargate pods, which is described in &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html#sg-pods-example-deployment" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;According to the documentation above, new security group must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow inbound request on port 53(TCP) from security group of the EKS cluster.&lt;/li&gt;
&lt;li&gt;Allow inbound request on port 53(UDP) from security group of the EKS cluster.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Let's create a new security group, and apply the below yaml file.&lt;/p&gt;&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;vpcresources.k8s.aws/v1beta1&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;SecurityGroupPolicy&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;dev-sgp&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;demo-dev&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;podSelector&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&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&lt;/span&gt;
&lt;span class="na"&gt;securityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;groupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${NEW_CREATED_SECURITY_GROUP_ID}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${EKS_CLUSTER_SECURITY_GROUP_ID}&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When we apply the yaml file, we must restart all pods since the new security group will not effect already running pods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now when we check the ALB health check, we can see that health checks on target groups are successfully done, and all targets are healthy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;That's it! In this post, I didn't want to focus on any Kubernetes fundamentals, but wanted to share how I troubleshooted issues that I encountered in my deployment process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, there are tons of documents to see when we encounter some issues when deploying Kubernetes resources. I wanted to narrow down these documents to what we really have to see, which is why I embedded so many links to documentations in this post.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Extra
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When we log in to AWS Management Console with root account and visit EKS service page, we will see a warning as below.&lt;/li&gt;
&lt;/ul&gt;

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

Your current user or role does not have access to Kubernetes objects on this EKS cluster This may be due to the current user or role not having Kubernetes RBAC permissions to describe cluster resources or not having an entry in the cluster’s auth config map.


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;If you want to view your resources on your EKS Cluster, run the command below.&lt;/li&gt;
&lt;/ul&gt;

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

kubectl edit configmap aws-auth &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;And add below fields to the same depth as &lt;code&gt;mapRoles&lt;/code&gt;.&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;mapUsers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;- userarn: arn:aws:iam::[account_id]:root&lt;/span&gt;
  &lt;span class="s"&gt;groups:&lt;/span&gt;
  &lt;span class="s"&gt;- system:masters&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;After save and quit, you will be able to view all resources within your EKS cluster.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>kubernetes</category>
      <category>eks</category>
      <category>fargate</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Using DynamoDB on Spring Boot (feat.Kotlin)</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Fri, 22 Jul 2022 04:46:28 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-dynamodb-on-spring-boot-featkotlin-3674</link>
      <guid>https://dev.to/aws-builders/using-dynamodb-on-spring-boot-featkotlin-3674</guid>
      <description>&lt;p&gt;In this post, I will talk about how to use Amazon DynamoDB with Spring Boot application made with Kotlin.&lt;/p&gt;

&lt;p&gt;The codes written here are all stored in this &lt;a href="https://github.com/Example-Collection/Spring-Boot-DynamoDB-Example"&gt;Github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Situation
&lt;/h3&gt;

&lt;p&gt;Here we will think about a situation where we have to store posts created. Below is the example scheme of data that we will store in DynamoDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"roy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Title of this post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content of this post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-07-16T14:07:31.000Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And below are our application's requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get a specific post using &lt;code&gt;post_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search posts with &lt;code&gt;title&lt;/code&gt; and sort it using &lt;code&gt;created_at&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search posts by written by a specific user(&lt;code&gt;user_id&lt;/code&gt;), and sort it using &lt;code&gt;created_at&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To meet the application's requirements, we can configure this DynamoDB table as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary Key: post_id(partition key)&lt;/li&gt;
&lt;li&gt;Global Secondary Indexes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;post_user_id_created_at_idx&lt;/code&gt;: HASH: &lt;code&gt;user_id&lt;/code&gt;, RANGE: &lt;code&gt;created_at&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;post_title_created_at_idx&lt;/code&gt;: HASH: &lt;code&gt;title&lt;/code&gt;, RANGE: &lt;code&gt;created_at&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;To demonstrate DynamoDB without actually using it, we will run a docker container with a DynamoDB image.&lt;/p&gt;

&lt;p&gt;(1) Clone this &lt;a href="https://github.com/Example-Collection/Spring-Boot-DynamoDB-Example"&gt;repository&lt;/a&gt; to your local machine and &lt;em&gt;change directory&lt;/em&gt; into it.&lt;/p&gt;

&lt;p&gt;(2) Run docker container using below commands.&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;# Run&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;  
&lt;span class="c"&gt;# Remove&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the "Run" command, it will run a docker container acting as DynamoDB in &lt;code&gt;localhost:54000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(3) Configure DynamoDB table with &lt;code&gt;scripts/create-dynamodb-table.sh&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="c"&gt;# Add permission&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./scripts/create-dynamodb-table.sh
&lt;span class="c"&gt;# Execute&lt;/span&gt;
./scripts/create-dynamodb-table.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will configure the table needed.&lt;br&gt;&lt;br&gt;
&lt;code&gt;create-dynamodb-table.sh&lt;/code&gt; contains all the commands required to create a DynamoDB table with aws-cli, and all global secondary indexs are defined in &lt;code&gt;scripts/gsi.json&lt;/code&gt; file.&lt;br&gt;
&lt;code&gt;create-dynamodb-table.sh&lt;/code&gt; reads information about global secondary indexes from &lt;code&gt;gsi.json&lt;/code&gt;, and configures it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring DynamoDB in Spring Boot Application
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Adding dependencies
&lt;/h4&gt;

&lt;p&gt;First we have to add required dependencies to our code.&lt;br&gt;
Assuming using Gradle, let's add two dependencies as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="k"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//..&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"com.amazonaws:aws-java-sdk-dynamodb:1.12.258"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"io.github.boostchicken:spring-data-dynamodb:5.2.5"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;spring-data-dynamodb is not an official library maintained by AWS nor Spring team. It originated from &lt;a href="https://github.com/michaellavelle/spring-data-dynamodb"&gt;michaellavelle/spring-data-dynamodb&lt;/a&gt;, and was forked and maintained at &lt;a href="https://github.com/derjust/spring-data-dynamodb"&gt;derjust/spring-data-dynamodb&lt;/a&gt; to support Spring Boot versions up to 2.1.x. Now it is being maintained at &lt;a href="https://github.com/boostchicken/spring-data-dynamodb"&gt;boostchicken/spring-data-dynamodb&lt;/a&gt;, supporting Spring Boot versions up to 2.2.x.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  DynamoDBConfig.kt
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;DynamoDBConfig&lt;/code&gt; class defines configurations to use DynamoDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableDynamoDBRepositories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePackages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"com.example.post.domain"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.dynamodb.endpoint}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.accessKey}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.secretKey}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.region}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Primary&lt;/span&gt;
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;dynamoDBMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amazonDynamoDB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDB&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBMapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amazonDynamoDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBMapperConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;amazonDynamoDB&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;awsCredentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BasicAWSCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;awsCredentialsProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AWSStaticCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsCredentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;endpointConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AwsClientBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EndpointConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AmazonDynamoDBClientBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withEndpointConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpointConfiguration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;awsCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BasicAWSCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secretKey&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, we are configuring AWS credentials, and registering &lt;code&gt;AmazonDynamoDB&lt;/code&gt; as a Spring Bean.&lt;/p&gt;

&lt;h4&gt;
  
  
  Post.kt
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Post&lt;/code&gt; class represents the entities which will be saved in &lt;code&gt;posts&lt;/code&gt; DynamoDB table. Let's define field, add appropriate annotations based on requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DynamoDBTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBHashKey&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBTyped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBMapperFieldModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexRangeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&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;h4&gt;
  
  
  PostRepository.kt
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;PostRepository&lt;/code&gt; interface is where you declare &lt;em&gt;spring-data-jpa-styled&lt;/em&gt; methods to query items from DynamoDB table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EnableScan&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PostRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findByUserIdOrderByCreatedAtAsc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findByTitleOrderByCreatedAtDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing read, write and resolving issues
&lt;/h3&gt;

&lt;p&gt;Above code works fine when we try to insert new item into DynamoDB table using &lt;code&gt;PostRepository.save()&lt;/code&gt;. However, when we call &lt;code&gt;PostRepository.findByUserIdOrderByCreatedAtAsc()&lt;/code&gt;, it throws an error saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.NoSuchMethodException: com.example.post.domain.Post.&amp;lt;init&amp;gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This means that we have to add default constructor for &lt;code&gt;Post&lt;/code&gt; class, so let's simply add default values for each properties to implement this.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DynamoDBTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBHashKey&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBTyped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBMapperFieldModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexRangeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&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 when we invoke the repository method again, we get different error saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.NullPointerException: null
    at com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$MethodReflect.set(StandardBeanProperties.java:133) ~[aws-java-sdk-dynamodb-1.12.258.jar:na]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error occurs because spring-data-dynamodb first creates &lt;code&gt;Post&lt;/code&gt; instance using default constructor, and sets each values using &lt;em&gt;setters&lt;/em&gt;. Since every properties of &lt;code&gt;Post&lt;/code&gt; class is declared as &lt;code&gt;val&lt;/code&gt;, there are no &lt;em&gt;setters&lt;/em&gt; created. Let's simply declare all properties with &lt;code&gt;var&lt;/code&gt; instead of &lt;code&gt;val&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DynamoDBTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBHashKey&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBTyped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBMapperFieldModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexRangeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&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;Now, when we call the repository method, we get different error saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.IllegalArgumentException: argument type mismatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this error message is quite unkind, if we think carefully, the only property that has different type from Kotlin code and DynamoDB is &lt;code&gt;createdAt&lt;/code&gt;. This field's Kotlin type is &lt;code&gt;LocalDateTime&lt;/code&gt;, while DynamoDB attribute type is &lt;code&gt;S&lt;/code&gt;, indicating string.&lt;/p&gt;

&lt;p&gt;So let's simply remove the &lt;code&gt;@field:DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S)&lt;/code&gt; annotation from &lt;code&gt;createdAt&lt;/code&gt; property, and invoke the repository method again.&lt;/p&gt;

&lt;p&gt;Now we get another error, saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: Post[created_at]; only scalar (B, N, or S) type allowed for key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this happening? We previously defined that &lt;code&gt;createdAt&lt;/code&gt;'s field type is &lt;code&gt;S&lt;/code&gt; using &lt;code&gt;@DynamoDBTyped&lt;/code&gt; annotation, but error message is indicating that we should define DynamoDB type for &lt;code&gt;createdAt&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;The problem is that while &lt;code&gt;Date&lt;/code&gt; can be automatically converted to &lt;code&gt;S&lt;/code&gt; in spring-data-dynamodb, &lt;code&gt;LocalDateTime&lt;/code&gt; cannot. So we have to declare a &lt;em&gt;converter&lt;/em&gt; for this field which is responsible of conversion between &lt;code&gt;Date&lt;/code&gt; and &lt;code&gt;LocalDateTime&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Getting back to &lt;code&gt;DynamoDBConfig&lt;/code&gt; class, let's add this converter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableDynamoDBRepositories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePackages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"com.example.post.domain"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.dynamodb.endpoint}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.accessKey}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.secretKey}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${amazon.aws.region}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTimeConverter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBTypeConverter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInstant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ZoneOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;unconvert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInstant&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;atZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimeZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDefault&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toZoneId&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toLocalDateTime&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;span class="c1"&gt;// Spring Beans..&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, we have to make &lt;code&gt;createdAt&lt;/code&gt; property to use this converter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DynamoDBTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Other properties..&lt;/span&gt;

    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBTypeConverted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Companion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTimeConverter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;DynamoDBIndexRangeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globalSecondaryIndexNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"post_user_id_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"post_title_created_at_idx"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&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;Boom! Now we can use repository methods successfully! &lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;It's been a quite long journey, resolving all the new errors upcoming. But this is the fun of developing, isn't it?&lt;/p&gt;

&lt;p&gt;By the way if we have to configure primary key of DynamoDB table using both partition key and sort key, we need to configure domain class(in this example, &lt;code&gt;Post&lt;/code&gt; class) more tha n just adding properties and applying &lt;code&gt;@DynamoDBHashKey&lt;/code&gt; and &lt;code&gt;@DynamoDBRangeKey&lt;/code&gt; annotation for each property. I will discuss how to implement this in the later post.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Beginner's guide to AWS Solutions Architect Associate(SAA) certification.</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Wed, 13 Jul 2022 16:06:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/beginners-guide-to-aws-saa-exam-fcn</link>
      <guid>https://dev.to/aws-builders/beginners-guide-to-aws-saa-exam-fcn</guid>
      <description>&lt;p&gt;In this post, I would like to share a little bit of story about how I prepared for AWS Solutions Architect Associate exam from a &lt;em&gt;beginner&lt;/em&gt;'s perspective.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The exam that I took (SAA-C02) is retiring on August 29, 2022. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Preparing
&lt;/h3&gt;

&lt;p&gt;This exam exists to verify your knowledge about general services provided by AWS, and how and when to use these services in various situations. As you can find many blog posts or videos explaining what kinds of questions appear in this exam, I will not discuss it here.&lt;/p&gt;

&lt;p&gt;I prepared for this exam only using the suggested learning path provided by &lt;a href="https://cloudacademy.com/learning-paths/aws-solutions-architect-associate-certification-preparation-for-aws-2021-1-2977/"&gt;CloudAcademy&lt;/a&gt;. The lectures covered many services provided by AWS, mostly focused on the services that are most likely to appear in the exam. &lt;/p&gt;

&lt;p&gt;Going straightforward, I will discuss the benefits and drawbacks of preparing for this exam using CloudAcademy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Benefits of preparing using CloudAcademy
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;All lectures cover one or more services provided by AWS, but also discuss enough, meaningful context. For example, if we have to learn about multi-tiered architecture, the lecture covers why we need it, various situations of when to implement it, and how to implement it using AWS services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each category(security, management, etc.) has hands on lab sessions, which allows you to actually use some of the services covered in the session, and having hands-on experience really helps you keep those services in mind much longer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each category has quizzes, which asks you about the services learned. It really helps to keep the knowledges in your mind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last and best is that at the very end, there are example questions provided by CloudAcademy team, which is really similar from questions that appear in the SAA exam. Also, there seems to be 300+ number of questions, so you can retake the exam as much as you want, and know your vulnerabilities.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Drawbacks of preparing using CloudAcademy
&lt;/h4&gt;

&lt;p&gt;There was only one drawback of using CloudAcademy, and it was that the questions didn't cover some of the services that appeared in my exam. For example, the SAA exam I took had some questions about Step Functions, Rekognition, Transcribe, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taking the exam
&lt;/h3&gt;

&lt;p&gt;I took the exam from home, and there was a human test proctor for me, directing a lot of things such as "clean up all the things under your desk" and more. It took almost 30 minutes for me to get my environment ready to take the exam.&lt;/p&gt;

&lt;p&gt;I got two warnings to not cover my mouth during the exam, and everything else went smooth. I didn't need extra 30 minutes for the exam, and I solved and submitted my answers in almost 90 minutes. &lt;/p&gt;

&lt;p&gt;I think the best factor that speeded up my time is that I took the exam questions provided by CloudAcademy for almost 10+ times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;All the processes of preparing for SAA really helped me a lot about expanding my knowledge of AWS, and how to reduce cost, design various systems while maintaining high availability.&lt;/p&gt;

&lt;p&gt;The exam didn't require that much of memorization, I just needed to be fully aware about what each service provides, how it gives benefits to customers.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>saa</category>
      <category>beginners</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Setting up Datadog APM on ECS, Fargate(Spring Boot)</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Tue, 07 Jun 2022 01:35:53 +0000</pubDate>
      <link>https://dev.to/aws-builders/setting-up-datadog-apm-on-ecs-fargatespring-boot-4ema</link>
      <guid>https://dev.to/aws-builders/setting-up-datadog-apm-on-ecs-fargatespring-boot-4ema</guid>
      <description>&lt;p&gt;Hello this is Roy, and today I want to write a short blog post about enabling Datadog APM at Spring Boot application running on Fargate, along with ECS.&lt;/p&gt;

&lt;p&gt;APM helps you deep dive into your application, allowing you to see all the metrics and what kind of tasks were executed(also in which order) to handle a request.&lt;/p&gt;

&lt;p&gt;For example, if you were to use APM on server applications, which is also in this case, you can see how much time it took to execute queries to database, how long it took to process associated codes, and also the latency time, which is a time indicating how long it took for the server to process the request made from a client.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For container metrics, you can reference this nice &lt;a href="https://docs.datadoghq.com/integrations/ecs_fargate/?tab=fluentbitandfirelens"&gt;documentation&lt;/a&gt; from Datadog itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I was using Amazon ECS with Fargate, I was confused if I had to see APM configuration for &lt;a href="https://docs.datadoghq.com/agent/amazon_ecs/apm/?tab=ec2metadataendpoint"&gt;ECS&lt;/a&gt; or for &lt;a href="https://docs.datadoghq.com/integrations/ecs_fargate/?tab=logdriver"&gt;Fargate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After reading all the relevant docs, I ended up noticing that there were three essential steps to enable Datadog APM on my environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;code&gt;dd-java-agent.jar&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Assign this downloaded jar file to &lt;code&gt;-javaagent&lt;/code&gt; flag when running the application jar file.&lt;/li&gt;
&lt;li&gt;Set appropriate environment variables for Datadog.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;For step 3, you can configure it in your task definition file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my case, my previous Dockerfile for containerizing my Spring Boot application looked as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;openjdk:11-jdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; JAR_FILE=build/libs/MY_APPLICATION.jar&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ${JAR_FILE} application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; application.jar extract

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:11-jdk&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/spring-boot-loader/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/snapshot-dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/application/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ Asia/Seoul&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "org.springframework.boot.loader.JarLauncher"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I was wondering where I was supposed to download the &lt;code&gt;dd-java-agent.jar&lt;/code&gt; file and assign it to &lt;code&gt;-javaagent&lt;/code&gt; flag, I found that Dockerfile was the appropriate place in my case.&lt;/p&gt;

&lt;p&gt;So my Dockerfile changed as below, and as you can see it downloads &lt;code&gt;dd-java-agent.jar&lt;/code&gt; file, and assigns it to &lt;code&gt;-javaagent&lt;/code&gt; flag when running my application's jar file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;openjdk:11-jdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; JAR_FILE=build/libs/MY_APPLICATION.jar&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ${JAR_FILE} application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; application.jar extract

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:11-jdk&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; application&lt;/span&gt;
&lt;span class="k"&gt;RUN  &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; dd-java-agent.jar &lt;span class="s1"&gt;'https://dtdg.co/latest-java-tracer'&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/application.jar ./application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/spring-boot-loader/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/snapshot-dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder application/application/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ Asia/Seoul&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-javaagent:dd-java-agent.jar", "-Ddd.profiling.enabled=true", "-XX:FlightRecorderOptions=stackdepth=256", "org.springframework.boot.loader.JarLauncher"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! After deploying new tasks, I was able to see my application on Datadog APM tab, and was able to see all the events made, all the details and process time of it, how long it took to process a request and much more.&lt;/p&gt;

&lt;p&gt;I spent around 3 days configuring this, and I am hoping that this blog will help you if you are in the same situation as I am.&lt;/p&gt;

&lt;p&gt;Thank you, and any feedbacks or comments are always appreciated.&lt;/p&gt;

</description>
      <category>datadog</category>
      <category>apm</category>
      <category>ecs</category>
      <category>fargate</category>
    </item>
    <item>
      <title>Configuring Minimum-Secure Web Application with Amazon VPC, EC2, and RDS</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Mon, 18 Apr 2022 12:35:15 +0000</pubDate>
      <link>https://dev.to/aws-builders/configuring-private-web-application-with-amazon-vpc-ec2-and-rds-5eii</link>
      <guid>https://dev.to/aws-builders/configuring-private-web-application-with-amazon-vpc-ec2-and-rds-5eii</guid>
      <description>&lt;p&gt;There are several key points that we should consider when making web applications on the cloud.(on-premises too.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;High Availability&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Durability&lt;/li&gt;
&lt;li&gt;And much more..&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we will typically focus on &lt;code&gt;Security&lt;/code&gt;, by making a simple infrastructure for web application on AWS.&lt;br&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%2Fhrf0fny08f18b005wa96.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%2Fhrf0fny08f18b005wa96.png" alt="Simple Web Application Architecture Overview"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The EC2 instance inside the public subnet is a web server, accepting traffics from the internet. RDS instance in the private subnet is attended to be only used by the EC2 instance in the public subnet. It only needs to be able to connect to EC2, but not the internet. So we will create a private subnet and put RDS instance inside it, preventing access from the internet at the root.&lt;/p&gt;

&lt;p&gt;We will proceed with this implementation in the following order.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating VPC and subnets (public, private)&lt;/li&gt;
&lt;li&gt;More VPC configurations&lt;/li&gt;
&lt;li&gt;Creating and configuring EC2 instance as a web server application&lt;/li&gt;
&lt;li&gt;Creating RDS instance&lt;/li&gt;
&lt;li&gt;Validating implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating VPC and subnets (public, private)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First and foremost, we must create a VPC where our AWS resources will reside in. So let's create a VPC named &lt;code&gt;Demo-VPC&lt;/code&gt;, with IPv4 CIDR set as &lt;code&gt;10.0.0.0/16&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After that, we have to create two subnets of that VPC, where one is public and the other is private. For the public subnet, let's set IPv4 CIDR as &lt;code&gt;10.0.1.0/24&lt;/code&gt;, and for the private subnet, let's set IPv4 CIDR as &lt;code&gt;10.0.2.0/24&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We also have to create another subnet in another availability zone within the same region than two other subnets we previously created. We have to create this subnet since &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html" rel="noopener noreferrer"&gt;RDS requires two different subnets created in different availability zones&lt;/a&gt;. Let's set its IPv4 CIDR as &lt;code&gt;10.0.3.0/24&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More VPC configurations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For our VPC to connect with internet, we have to create and attach an &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html" rel="noopener noreferrer"&gt;Internet Gateway&lt;/a&gt; to our VPC. Simply head over to VPC console, and create and attach the Internet Gateway to the VPC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After that, we have to edit Route Tables for this VPC, to allow all traffics outside of this VPC to be delivered over the internet. Go to VPC console, Route tables section and let's add another rule like below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Destination: &lt;code&gt;0.0.0.0/0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Target: Internet Gateway we created. (In the form of &lt;code&gt;igw-XX&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating and configuring EC2 instance as an web server application
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now we have to create an EC2 instance which will reside in the public subnet, so that it can allow and communicate with traffic over internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Choose the correct VPC and public subnet we created, and for the Security Group, we should add two inbound rules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: &lt;code&gt;SSH&lt;/code&gt;, Source: &lt;code&gt;0.0.0.0/0&lt;/code&gt;: To allow ssh access to EC2 instance. It is obviously not recommended to allow ssh access from everywhere, but we will set it as &lt;code&gt;0.0.0.0/0&lt;/code&gt; to simplify the process.&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;Custom TCP&lt;/code&gt;, Port: &lt;code&gt;8080&lt;/code&gt;, Source: &lt;code&gt;0.0.0.0/0&lt;/code&gt;: Our sample server application will use port 8080, so we will pretend as if port 8080 is for HTTP or HTTPS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Take a note of the Security Group ID created, since we have to use it later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating RDS instance
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Before creating RDS, we have to create another Security Group for it. Inbound rule to add is as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: &lt;code&gt;MySQL/Aurora&lt;/code&gt;, Source: &lt;code&gt;sg-xxx&lt;/code&gt;(Security Group ID we created for EC2 instance.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;On the configuration page, we have to check that this RDS will reside in private subnet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VPC&lt;/code&gt;: VPC we created.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Public Access&lt;/code&gt;: No&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VPC Security Group&lt;/code&gt;: Security Group we just created for RDS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;After creating the instance, navigate to &lt;code&gt;RDS -&amp;gt; Databases&lt;/code&gt; and select the instance we just created. We can see the &lt;code&gt;Endpoint &amp;amp; Port&lt;/code&gt; section. Note that in &lt;code&gt;Security&lt;/code&gt; section, &lt;code&gt;Publicly accessible&lt;/code&gt; is marked as &lt;strong&gt;No&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validating implementation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that we have implemented every requirements we first saw, it's time to validate it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;First, let's see if we can access the database from our local machine, using the specified RDS endpoint assigned by AWS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2F5ipwnsnsppz389fd8hwd.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%2F5ipwnsnsppz389fd8hwd.png" alt="RDS Connectivity Validation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;As we can see, it failed since RDS resides in the private subnet of VPC, blocking all traffics originated from the internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now let's see if our server application on EC2 is working as expected. I have created two endpoints in this &lt;a href="https://github.com/sang-w0o/Public-Spring-Server-Demo" rel="noopener noreferrer"&gt;sample code&lt;/a&gt;, one for creating a user and the other for reading user information by an ID.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As in the two screenshots below, it is proven that EC2 instance can communicate with RDS instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating User API - this will perform &lt;code&gt;INSERT&lt;/code&gt; query on RDS.&lt;br&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%2Fjlr2vulyzyg6m5wvxc78.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%2Fjlr2vulyzyg6m5wvxc78.png" alt="Creating User API"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reading User API - this will perform &lt;code&gt;SELECT&lt;/code&gt; query on RDS.&lt;br&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%2Foh3ckj1oy0qzfa03us88.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%2Foh3ckj1oy0qzfa03us88.png" alt="Reading User API"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wrapping up, we successfully implemented a web application which meets the minimum security criteria. It is always important to think in the perspective of security, when designing and implementing software architecture, and blocking access from the internet at the root using private subnets in VPC can be the first step to implement it.&lt;/p&gt;

&lt;p&gt;Thank you for viewing this article. If you have any questions or feedbacks, please leave a comment or contact me!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>rds</category>
      <category>vpc</category>
    </item>
    <item>
      <title>Consuming SQS Messages with Lambda(TypeScript)</title>
      <dc:creator>Roy Ra</dc:creator>
      <pubDate>Mon, 04 Apr 2022 07:41:46 +0000</pubDate>
      <link>https://dev.to/aws-builders/consuming-sqs-messages-with-lambdatypescript-1gck</link>
      <guid>https://dev.to/aws-builders/consuming-sqs-messages-with-lambdatypescript-1gck</guid>
      <description>&lt;p&gt;Amazon SQS(Simple Queue Service) is a fully managed message queuing service by AWS that enables you to lower the degree of coupling between services(ex. mircoservices), distributed systems, and serverless applications. &lt;/p&gt;

&lt;p&gt;There are a lot of ways to produce message to SQS, but in this post we will focus on consuming(receiving) these messages with a lambda function which is written in TypeScript.&lt;/p&gt;

&lt;p&gt;Let's assume we already have our SQS ready, and we only need to write the lambda code to consume the messages in this queue.&lt;/p&gt;

&lt;p&gt;Below are the technical stacks we will use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.serverless.com/framework/docs/providers/aws/guide/intro"&gt;Serverless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will go through this implementation in three sections,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating IAM User for managing Lambda.&lt;/li&gt;
&lt;li&gt;Writing some codes.&lt;/li&gt;
&lt;li&gt;Deploying, and connecting with SQS!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating IAM User for managing Lambda.
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We can easily deploy Lambda functions using Serverless framework. We'll see the deployment script later, but let's focus on the IAM user who will manage the Lambda function we make here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Serverless deploys our Lambda function, which is zipped and stored to S3, and all of the logs created by the Lambda function will remain in CloudWatch.&lt;/p&gt;

&lt;p&gt;So we need permissions for below AWS Services.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;CloudWatch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But since this Lambda function will receive and consume messages from SQS, this IAM user also needs a permission to receive message from SQS, which is &lt;code&gt;sqs:ReceiveMessage&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing some codes!
&lt;/h2&gt;

&lt;p&gt;Let's start coding the Lambda function. &lt;br&gt;
Here, we want our Lambda function to receive message from SQS, and send that message to a specific channel in our Slack workspace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In case you don't know how to make slack apps, you can create a Slack App and get &lt;code&gt;OAuth Token&lt;/code&gt; and &lt;code&gt;clientSecret&lt;/code&gt; by following this great article &lt;a href="https://slack.com/help/articles/115005265703-Create-a-bot-for-your-workspace"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that you have your &lt;code&gt;OAuth Token&lt;/code&gt; and &lt;code&gt;clientSecret&lt;/code&gt;, let's start coding!&lt;/p&gt;

&lt;p&gt;First we have to add dependencies below to our fresh new project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@slack/bolt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.11.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to write a handler function, which will handle the event(receiving message).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OAUTH_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slack app oauth token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SIGNING_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slack app signing secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHANNEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slack channel name including #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;consumeSqsMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SQSEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;signingSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SIGNING_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&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;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendMessageToSlack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendMessageToSlack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CHANNEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sent!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to send message. error: &lt;/span&gt;&lt;span class="dl"&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;error&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="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;consumeSqsMessage&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying, and connecting with SQS!
&lt;/h2&gt;

&lt;p&gt;Now that we have our Lambda function ready, lets' take a look at the serverless deployment script.&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;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;ServiceName&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-plugin-typescript&lt;/span&gt;

&lt;span class="na"&gt;provider&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;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs14.x&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;aws region&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;notifySQSMessageToSlack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/functions.consumeSqsMessage&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the timeout value of Lambda functions should be lower than or equal to the visibility timeout of SQS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have our Serverless Deployment script ready, we can deploy this to AWS Lambda using &lt;a href="https://github.com/serverless/github-action"&gt;this script&lt;/a&gt; with Github Actions.&lt;/p&gt;

&lt;p&gt;Finally, all we have to do is configure SQS to handle this Lambda function whenever a new message comes in.&lt;br&gt;
We can do this very easily, just go to SQS Console and find &lt;code&gt;Lambda triggers&lt;/code&gt; tab below! Click &lt;code&gt;Configure Lambda function trigger&lt;/code&gt;, and we'll see the Lambda function we just deployed.&lt;/p&gt;

&lt;p&gt;That's it! We just made a lambda function which consumes messages from SQS, and sends it to a Slack channel.&lt;/p&gt;

&lt;p&gt;Thank you for viewing this article. If you have any questions or feedbacks, please leave a comment or contact me! &lt;/p&gt;

</description>
      <category>aws</category>
      <category>typescript</category>
      <category>lambda</category>
      <category>sqs</category>
    </item>
  </channel>
</rss>
