<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Home on Bumgu</title><link>https://blog.bumgu.com/en/</link><description>Recent content in Home on Bumgu</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Sat, 10 Oct 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://blog.bumgu.com/en/index.xml" rel="self" type="application/rss+xml"/><item><title>Vector vs OpenTelemetry Collector: Log Collector Comparison</title><link>https://blog.bumgu.com/en/post/2026/10/10/vector-otel-log-pipeline-benchmark/</link><pubDate>Sat, 10 Oct 2026 00:00:00 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2026/10/10/vector-otel-log-pipeline-benchmark/</guid><description>&lt;p>When collecting a large volume of node logs on EKS, it is difficult to decide whether to use Vector or the OpenTelemetry Collector based only on product descriptions or feature lists. I wanted to know how CPU and memory usage differ along the actual path: reading files on the node, sending them centrally, parsing them, and routing them.&lt;/p>
&lt;p>So I deployed both pipelines on Kubernetes and sent them the same kind of 1 KiB logs. To give the conclusion first: &lt;strong>in this single-node run, OTel had lower average CPU usage at 10k logs/s, while Vector used less memory.&lt;/strong> However, that does not mean either is the operational winner. The final output was a discard sink rather than a real storage backend, and from 25k onward the load entering the two collectors was itself different.&lt;/p></description></item><item><title>Reducing Persistent Disk Usage in GitHub Actions Docker Builds</title><link>https://blog.bumgu.com/en/post/2026/09/23/github-actions-buildx-disk/</link><pubDate>Wed, 23 Sep 2026 00:00:00 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2026/09/23/github-actions-buildx-disk/</guid><description>&lt;h2 id="the-problem">The Problem&lt;/h2>
&lt;p>We encountered a runner disk space shortage while building Docker images in GitHub Actions. An image with a compressed size of 1.40GB failed twice with &lt;code>no space left on device&lt;/code> in the same shared workflow, but at different points each time. At first, we thought the Dockerfile was too large or the cache had accumulated for too long. In this case, however, the cause was that a single image build result was being replicated into multiple storage locations.&lt;/p></description></item><item><title>Routing with Nginx and Envoy Gateway</title><link>https://blog.bumgu.com/en/post/2026/08/02/envoy-gateway-proxy/</link><pubDate>Sun, 02 Aug 2026 16:07:35 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2026/08/02/envoy-gateway-proxy/</guid><description>&lt;h1 id="can-nginx-regex-routing-be-moved-to-envoy-gateway">Can Nginx Regex Routing Be Moved to Envoy Gateway?&lt;/h1>
&lt;p>There was one question we had to answer first in order to migrate the edge gateway from OpenResty (nginx) to Envoy Gateway: &lt;strong>Can the regex &lt;code>location&lt;/code> settings in nginx conf be moved to the Gateway API as-is?&lt;/strong>&lt;/p>
&lt;p>Reading the documentation did not provide an answer. The Gateway API specification classifies regex path matching as Extended and &lt;strong>does not specify its priority at all&lt;/strong>. It only says that it is left to the discretion of the implementation.&lt;/p></description></item><item><title>Trying Out the Loki-VictoriaLogs Proxy</title><link>https://blog.bumgu.com/en/post/2026/04/19/loki-vl-proxy/</link><pubDate>Sun, 19 Apr 2026 18:50:12 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2026/04/19/loki-vl-proxy/</guid><description>&lt;p>I use &lt;a href="https://docs.victoriametrics.com/victorialogs/">VictoriaLogs&lt;/a> as the log store in my personal Kubernetes cluster. Compared with Loki, it uses fewer resources and is simpler to install and operate, making it suitable for small environments.&lt;/p>
&lt;p>Although Grafana officially supports VictoriaLogs as a data source, autocomplete and the Explore UX are not yet as smooth as Loki&amp;rsquo;s, so I often ended up going back to &lt;a href="https://docs.victoriametrics.com/victorialogs/querying/#web-ui">vmui&lt;/a>.&lt;br>
Then I discovered a project called &lt;a href="https://github.com/ReliablyObserve/Loki-VL-proxy">loki-vl-proxy&lt;/a>. It is a &lt;strong>proxy that uses VictoriaLogs as a backend and provides Loki API-compatible endpoints&lt;/strong>, so from Grafana&amp;rsquo;s perspective it behaves just like Loki. The idea is to store data in VL and query it using the Loki UX, so I decided to deploy it right away.&lt;/p></description></item><item><title>Building GKE with Terraform and Accessing It with Tailscale</title><link>https://blog.bumgu.com/en/post/2025/12/18/building-gke-with-terraform-access-with-tailscale/</link><pubDate>Thu, 18 Dec 2025 14:21:02 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/12/18/building-gke-with-terraform-access-with-tailscale/</guid><description>&lt;p>I had quite a bit of GCP Free trial Credit left, so I tried building GKE as if it were for a real-world environment and restricting access at the network layer through Tailscale.&lt;/p>
&lt;h1 id="1-terraform">1. Terraform&lt;/h1>
&lt;p>The directory structure is as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── main.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── modules
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   ├── gke
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   │   ├── main.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   │   └── variables.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   ├── network
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   │   ├── main.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   │   └── output.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   └── vm
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   ├── main.tf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   └── variables.tf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I modularized the configuration by defining the required resources inside &lt;code>modules&lt;/code> and creating the actual resources from the root &lt;code>main.tf&lt;/code>.&lt;/p></description></item><item><title>Contributing to the Open-Source VictoriaMetrics</title><link>https://blog.bumgu.com/en/post/2025/11/25/contirubte_victoriametrics/</link><pubDate>Tue, 25 Nov 2025 22:50:21 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/11/25/contirubte_victoriametrics/</guid><description>&lt;h1 id="the-origin">The origin&lt;/h1>
&lt;p>Last April, I replaced my company&amp;rsquo;s logging tool, Loki, with VictoriaLogs. It was a decision I was quite happy with, but one of our team&amp;rsquo;s requirements was still unmet: &amp;ldquo;include the log body in alert messages.&amp;rdquo;&lt;br>
Because of its design, VictoriaLogs returns only numeric values when an alert is triggered, so it could not include log text in the alert. I opened an &lt;a href="https://github.com/VictoriaMetrics/VictoriaLogs/issues/81">issue&lt;/a> and looked for a way to do it, but in the end the only answer was to &amp;ldquo;build it myself.&amp;rdquo;&lt;br>
As a workaround, I implemented a simple &lt;a href="https://github.com/bonzonkim/vmalert-webhook">Webhook Server&lt;/a> to fetch logs in the middle and used that.&lt;br>
Then I noticed that a few people with the same inconvenience had found my repository through searches and were forking the code.&lt;br>
Seeing that made me want to solve the problem rather than leave it as a simple patch. So I implemented the feature directly in the official VictoriaMetrics repository (specifically, in Vmalert) and contributed it.&lt;/p></description></item><item><title>Contributing to Open Source</title><link>https://blog.bumgu.com/en/post/2025/10/22/contributing-to-open-source/</link><pubDate>Wed, 22 Oct 2025 15:20:22 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/10/22/contributing-to-open-source/</guid><description>&lt;p>I often use Github Actions as my CI tool.&lt;br>
As you all know, Github Actions workflows are written in YAML, and many of us have probably received a CI job failed notification because of indentation or incorrect syntax.&lt;br>
Then I came across this post on &lt;a href="https://x.com/JLarky/status/1980024323663638773">Twitter&lt;/a>:
&lt;img src="https://blog.bumgu.com/images/post/28-contribute-opensource/1.png" alt="image">
This &lt;code>gha-ts&lt;/code> project is open source software that generates a &lt;code>.yml&lt;/code> file when you write a workflow in TypeScript.&lt;br>
It lets you generate type-safe workflows without worrying about indentation or syntax.&lt;br>
I noticed that &lt;code>setup&lt;/code> already supported node, java, python, go, and dotnet, but not bun, so I decided to add bun setup.&lt;/p></description></item><item><title>(1) A First Look at Istio</title><link>https://blog.bumgu.com/en/post/2025/09/07/play-with-istio/</link><pubDate>Sun, 07 Sep 2025 11:17:37 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/09/07/play-with-istio/</guid><description>&lt;h1 id="1-what-is-istio">1. What is Istio?&lt;/h1>
&lt;p>Many services are built using a microservices architecture (MSA).&lt;br>
MSA is particularly effective at reducing the propagation of failures by lowering dependencies between services.&lt;br>
However, as the number of services in an MSA grows, complexity also increases, making it difficult to know which services call which other services, whether communication is secure, and how to troubleshoot problems.&lt;br>
Istio is a tool that helps solve these complex problems.&lt;/p></description></item><item><title>(2) Replacing VMs by Building an In-House OpenStack - Nova, Neutron, Cinder</title><link>https://blog.bumgu.com/en/post/2025/07/06/building_private_cloud_using_openstack-2/</link><pubDate>Sun, 06 Jul 2025 12:08:08 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/07/06/building_private_cloud_using_openstack-2/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/en/post/2025/06/29/building_private_cloud_using_openstack-1/">OpenStack Setup, Part 1&lt;/a>&lt;/p>
&lt;hr>
&lt;h1 id="nova">Nova&lt;/h1>
&lt;p>In OpenStack, Nova is the core component for managing compute resources in the cloud.&lt;br>
It manages the entire lifecycle of instances, including creation, management, and deletion.&lt;br>
It is also an essential component that works with Neutron, Cinder, and other services to connect networking and storage to instances.&lt;/p>
&lt;h3 id="controller-node">Controller Node&lt;/h3>
&lt;h4 id="1-create-nova-databases-in-mariadb">1. Create Nova databases in MariaDB&lt;/h4>
&lt;ul>
&lt;li>&lt;code>create database nova_api&lt;/code>
&lt;ul>
&lt;li>Stores global information related to Nova&amp;rsquo;s API service (e.g. instance requests, cell information, high-level tracking, etc.)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>create database nova&lt;/code>
&lt;ul>
&lt;li>The database for the default cell (cell1), where data about actual instances (virtual machines) is stored. It holds local information for each cell, such as instance state, allocated resources, and events.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>create database nova_cell0&lt;/code>
&lt;ul>
&lt;li>A special-purpose database in Nova&amp;rsquo;s cell structure for instances that failed scheduling (instances that could not be assigned to any compute node).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>GRANT ALL PRIVILEGES ON nova_api.* TO &amp;#39;nova&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;NOVA_API_PASS&amp;#39;;
GRANT ALL PRIVILEGES ON nova_api.* TO &amp;#39;nova&amp;#39;@&amp;#39;%&amp;#39; IDENTIFIED BY &amp;#39;NOVA_API_PASS&amp;#39;;

GRANT ALL PRIVILEGES ON nova.* TO &amp;#39;nova&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;NOVA_PASS&amp;#39;;
GRANT ALL PRIVILEGES ON nova.* TO &amp;#39;nova&amp;#39;@&amp;#39;%&amp;#39; IDENTIFIED BY &amp;#39;NOVA_PASS&amp;#39;;

GRANT ALL PRIVILEGES ON nova_cell0.* TO &amp;#39;nova&amp;#39;@&amp;#39;localhost&amp;#39; IDENTIFIED BY &amp;#39;NOVA_CELL0_PASS&amp;#39;;
GRANT ALL PRIVILEGES ON nova_cell0.* TO &amp;#39;nova&amp;#39;@&amp;#39;%&amp;#39; IDENTIFIED BY &amp;#39;NOVA_CELL0_PASS&amp;#39;;
&lt;/code>&lt;/pre>&lt;h4 id="2-create-the-nova-user-and-grant-permissions">2. Create the Nova user and grant permissions&lt;/h4>
&lt;ul>
&lt;li>Create the user
&lt;ul>
&lt;li>&lt;code>openstack user create --domain default --password-prompt nova&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Grant administrator privileges
&lt;ul>
&lt;li>&lt;code>openstack role add --project service --user nova admin&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Create the Nova service entity
&lt;ul>
&lt;li>&lt;code>openstack service create --name nova --description &amp;quot;Openstack Compute&amp;quot; compute&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="3-create-nova-api-service-endpoints">3. Create Nova API service endpoints&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ openstack endpoint create --region RegionOne &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> compute public http://controller:8774/v2.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Field | Value |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| enabled | True |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| id | 08ab870ef54647399f31472a22e3ec23 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| interface | public |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region_id | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_id | 25ca5a23aa5b4375889d931e11467e61 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_name | nova |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_type | compute |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| url | http://controller:8774/v2.1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ openstack endpoint create --region RegionOne &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> compute internal http://controller:8774/v2.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Field | Value |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| enabled | True |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| id | 59b80313047f41eb89e429600eff9a71 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| interface | internal |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region_id | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_id | 25ca5a23aa5b4375889d931e11467e61 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_name | nova |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_type | compute |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| url | http://controller:8774/v2.1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ openstack endpoint create --region RegionOne &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> compute admin http://controller:8774/v2.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Field | Value |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| enabled | True |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| id | 12a2aab58e0f4a87b1f41e6e6486ef5c |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| interface | admin |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| region_id | RegionOne |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_id | 25ca5a23aa5b4375889d931e11467e61 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_name | nova |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| service_type | compute |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| url | http://controller:8774/v2.1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>+--------------+----------------------------------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="4-install-nova-packages">4. Install Nova packages&lt;/h4>
&lt;p>&lt;code>api install nova-api nova-conductor nova-novncproxy nova-scheduler -y&lt;/code>&lt;/p></description></item><item><title>(1) Replacing VMs by Building an In-House OpenStack - Keystone, Glance, Placement</title><link>https://blog.bumgu.com/en/post/2025/06/29/building_private_cloud_using_openstack-1/</link><pubDate>Sun, 29 Jun 2025 12:13:11 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/06/29/building_private_cloud_using_openstack-1/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/en/post/2025/07/06/building_private_cloud_using_openstack-2/">OpenStack Setup, Part 2&lt;/a>&lt;/p>
&lt;hr>
&lt;h1 id="background">Background&lt;/h1>
&lt;p>Our VMs were no longer maintained and the license had expired, so I decided to replace them and started building OpenStack.&lt;br>
I had previously tried to &lt;del>take the easy way out&lt;/del> install everything with &lt;a href="https://docs.openstack.org/openstack-helm/latest/">OpenStack Helm&lt;/a>, but it didn&amp;rsquo;t go well and the effort petered out&amp;hellip;&lt;br>
At the Cloud bro seminar on June 4, I asked Seongsu Cho from NHN Cloud about it. He advised me not to try installing everything at once with Helm, but to follow the guide, install components one by one, and troubleshoot along the way. So I started from scratch, one component at a time.&lt;/p></description></item><item><title>Kubernetes Cluster Version Upgrade</title><link>https://blog.bumgu.com/en/post/2025/06/22/kube-cluster-upgrade/</link><pubDate>Sun, 22 Jun 2025 13:25:06 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/06/22/kube-cluster-upgrade/</guid><description>&lt;h1 id="kubernetes-cluster-upgrade">Kubernetes Cluster Upgrade&lt;/h1>
&lt;p>&lt;strong>Server: Vagrant VM (Ubuntu 22.04)&lt;/strong>&lt;br>
&lt;strong>Current Kubeadm, Kubectl, Kubelet: 1.32.6&lt;/strong>&lt;br>
&lt;strong>Kubeadm, Kubectl, Kubelet to upgrade: 1.33&lt;/strong>&lt;/p>
&lt;p>Assuming the cluster is currently in production, I&amp;rsquo;ll perform the upgrade while a simple API server is running.&lt;/p>
&lt;p>Kubernetes YAML, source code, Vagrantfile, etc.: &lt;a href="https://github.com/bonzonkim/kube-cluster-upgrade">GitHub&lt;/a>&lt;/p>
&lt;h1 id="current-state">Current state&lt;/h1>
&lt;p>&lt;code>GET :8080/hello&lt;/code>&lt;/p>
&lt;pre tabindex="0">&lt;code>method: GET 
Response:
{
Message: Hello world!
}
&lt;/code>&lt;/pre>&lt;p>&lt;code>GET :8080/bye&lt;/code>&lt;/p>
&lt;pre tabindex="0">&lt;code>method: GET 
Response:
{
Message: Bye world!
}
&lt;/code>&lt;/pre>&lt;h2 id="1-control-plane---kubeadm">1. Control plane - Kubeadm&lt;/h2>
&lt;pre tabindex="0">&lt;code>apt update
apt-cache madison kubeadm
&lt;/code>&lt;/pre>&lt;p>If the command above only shows version 1.32.x, update the apt repository.&lt;/p></description></item><item><title>Log Aggregation with Kafka</title><link>https://blog.bumgu.com/en/post/2025/05/06/log-aggregation-with-kafka/</link><pubDate>Tue, 06 May 2025 15:52:28 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/05/06/log-aggregation-with-kafka/</guid><description>&lt;p>Kafka is an MQ (Message Queue).&lt;br>
The original log collection pipeline was simple, as shown here:&lt;br>
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/1.png" alt="image">
The problem was that we had more than one or two servers.&lt;br>
As all the logs were inserted into VictoriaLogs simultaneously, bottlenecks caused processing delays and some logs were lost.&lt;br>
So we introduced Kafka as a Log Aggregator to centralize the logs.
It can process large volumes of logs quickly and reliably, and preserve data safely without log loss even if a failure occurs. This led us to consider this structure:&lt;br>
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/2.png" alt="image">
However, &lt;strong>unlike AMQP, Kafka is a Pub/Sub model where consumers subscribe to topics and pull messages&lt;/strong>, so the final structure became the following:&lt;br>
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/3.png" alt="image">&lt;/p></description></item><item><title>Building a Blog with Hugo</title><link>https://blog.bumgu.com/en/post/2025/04/22/making_blog_with_hugo/</link><pubDate>Tue, 22 Apr 2025 20:42:28 +0900</pubDate><guid>https://blog.bumgu.com/en/post/2025/04/22/making_blog_with_hugo/</guid><description>&lt;p>I was familiar with Markdown, so I used &lt;a href="https://velog.io/@kellyb9/posts">Velog&lt;/a> as my blog, but I decided to host a blog with &lt;strong>Hugo + GitHub Pages&lt;/strong>.&lt;br>
Here I&amp;rsquo;ll document the deployment process.&lt;/p>
&lt;h1 id="1-hugo">1. Hugo?&lt;/h1>
&lt;p>&lt;a href="https://gohugo.io/">Hugo&lt;/a> is an open-source SSG (Static Site Generator) written in Go.&lt;br>
I considered using &lt;a href="https://astro.build/">Astro&lt;/a>, based on JS/TS, but chose lightweight Hugo.&lt;br>
Hugo allows CSS and HTML customization through &lt;code>theme-override&lt;/code>, and its &lt;code>GoTemplate&lt;/code> supports constructs such as &lt;code>if&lt;/code> and &lt;code>for&lt;/code> loops.&lt;/p></description></item><item><title>Let's Try MCP (Model Context Protocol)</title><link>https://blog.bumgu.com/en/post/2025/03/01/try_mcp/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2025/03/01/try_mcp/</guid><description>&lt;p>I attended Docker Community Day, held on February 27.
One of the talks was by Geuncheol Yeom, an SRE at Karrot. It seemed like an interesting topic, so I gave it a quick try.&lt;/p>
&lt;hr>
&lt;h4 id="what-is-mcp">What is MCP?&lt;/h4>
&lt;blockquote>
&lt;p>Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM (Large Language Model) applications and external data sources and tools. Whether you are building an AI-powered IDE, improving a chat interface, or creating a custom AI workflow, MCP provides a standardized way to connect LLMs to the context they need.&lt;/p></description></item><item><title>Go - Functional Options Pattern</title><link>https://blog.bumgu.com/en/post/2025/02/16/go-functional-options/</link><pubDate>Sun, 16 Feb 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2025/02/16/go-functional-options/</guid><description>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">package&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> &lt;span style="color:#e6db74">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">Server&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">maxConn&lt;/span>		&lt;span style="color:#66d9ef">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">tls&lt;/span>			&lt;span style="color:#66d9ef">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">id&lt;/span>			&lt;span style="color:#66d9ef">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">newServer&lt;/span>(&lt;span style="color:#a6e22e">maxConn&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#a6e22e">tls&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span>, &lt;span style="color:#a6e22e">id&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>) &lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">Server&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">Server&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">maxConn&lt;/span>:	&lt;span style="color:#a6e22e">maxConn&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">tls&lt;/span>:		&lt;span style="color:#a6e22e">tls&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">id&lt;/span>:			&lt;span style="color:#a6e22e">id&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">s&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">newServer&lt;/span>(&lt;span style="color:#ae81ff">100&lt;/span>, &lt;span style="color:#66d9ef">false&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;server1&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">s&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here is a function that starts a server.
When calling &lt;code>newServer()&lt;/code>, you must pass arguments such as &lt;code>maxConn&lt;/code>, &lt;code>tls&lt;/code>, and &lt;code>id&lt;/code>.
There are only three for now, so it is hard to forget them, but with 10 or 20 arguments you may start forgetting some and the program will fail to run.&lt;/p></description></item><item><title>Resolving a TLS Error in Etcd Monitoring</title><link>https://blog.bumgu.com/en/post/2025/01/18/etcd_monitoring_tls_error/</link><pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2025/01/18/etcd_monitoring_tls_error/</guid><description>&lt;p>Server OS: Ubuntu 22.04
Kubernetes environment: Kubespray&lt;br>
Prometheus: &lt;a href="https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack/65.1.1">kube-prometheus-stack&lt;/a> v65.1.1&lt;/p>
&lt;h1 id="1-overview">1. Overview&lt;/h1>
&lt;p>The etcd service itself was installed directly on the Kubernetes nodes (on the host, outside Pods), rather than running in a Pod; the Grafana etcd dashboard is only for viewing metrics. Because etcd runs outside the cluster&amp;rsquo;s Pod network, Prometheus did not have a scrape target for it by default. I added &lt;code>&amp;lt;etcd-server&amp;gt;:2379/metrics&lt;/code> as a target, but scraping failed because Prometheus could not access etcd due to a TLS issue.&lt;/p></description></item><item><title>Go - The Difference Between http.HandleFunc and http.HandlerFunc</title><link>https://blog.bumgu.com/en/post/2025/01/07/http_handlefunc_handlerfunc/</link><pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2025/01/07/http_handlefunc_handlerfunc/</guid><description>&lt;h3 id="1-httphandlefunc">1. http.HandleFunc&lt;/h3>
&lt;ul>
&lt;li>&lt;code>http.HandleFunc&lt;/code> is a &lt;strong>function&lt;/strong>.&lt;/li>
&lt;li>It registers a handler to process requests for a particular path.&lt;/li>
&lt;li>The registered function follows the &lt;code>func(http.ResponseWriter, *http.Request)&lt;/code> signature.&lt;/li>
&lt;/ul>
&lt;h4 id="1-1-usage-example">1-1. Usage example&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">package&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#e6db74">&amp;#34;encoding/json&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#e6db74">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#e6db74">&amp;#34;net/http&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">Response&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">Message&lt;/span>		&lt;span style="color:#66d9ef">string&lt;/span> &lt;span style="color:#e6db74">`json:&amp;#34;message&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">helloHandler&lt;/span>(&lt;span style="color:#a6e22e">w&lt;/span> &lt;span style="color:#a6e22e">http&lt;/span>.&lt;span style="color:#a6e22e">ResponseWriter&lt;/span>, &lt;span style="color:#a6e22e">r&lt;/span> &lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">http&lt;/span>.&lt;span style="color:#a6e22e">Request&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">w&lt;/span>.&lt;span style="color:#a6e22e">Header&lt;/span>().&lt;span style="color:#a6e22e">Set&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Content-Type&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;application/json&amp;#34;&lt;/span>) &lt;span style="color:#75715e">// Set the response header
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>	&lt;span style="color:#a6e22e">w&lt;/span>.&lt;span style="color:#a6e22e">WriteHeader&lt;/span>(&lt;span style="color:#a6e22e">http&lt;/span>.&lt;span style="color:#a6e22e">StatusOK&lt;/span>) &lt;span style="color:#75715e">// Set the status code in the header
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">response&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">Response&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">Message&lt;/span>:	&lt;span style="color:#e6db74">&amp;#34;hello world&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">json&lt;/span>.&lt;span style="color:#a6e22e">NewEncoder&lt;/span>(&lt;span style="color:#a6e22e">w&lt;/span>).&lt;span style="color:#a6e22e">Encode&lt;/span>(&lt;span style="color:#a6e22e">response&lt;/span>); &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Errorf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;failed to encode json reponse&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">http&lt;/span>.&lt;span style="color:#a6e22e">HandleFunc&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;/&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">helloHandler&lt;/span>) &lt;span style="color:#75715e">// Register the handler for the &amp;#39;/&amp;#39; route
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Server start on port :8080&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">http&lt;/span>.&lt;span style="color:#a6e22e">ListenAndServe&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;:8080&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">nil&lt;/span>); &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		panic(&lt;span style="color:#e6db74">&amp;#34;failed to start server&amp;#34;&lt;/span>)		
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://blog.bumgu.com/images/post/17-go-http/1.png" alt="">&lt;/p></description></item><item><title>kube-prometheus-stack: Managing Scrape Targets with an External Secret</title><link>https://blog.bumgu.com/en/post/2024/12/22/kube_prometheus_stack_scrape_target_with_secret/</link><pubDate>Sun, 22 Dec 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/12/22/kube_prometheus_stack_scrape_target_with_secret/</guid><description>&lt;p>As you add targets directly to &lt;code>additionalScrapeConfigs&lt;/code>, the &lt;code>values.yaml&lt;/code> file can grow to 1,000 or 2,000 lines and become difficult to manage.&lt;/p>
&lt;p>So I changed the setup to keep Scrape Targets in an external Secret and manage them by referencing that Secret.&lt;/p>
&lt;h1 id="1-create-the-existing-scrapetarget-in-a-file">1. Create the existing ScrapeTarget in a file&lt;/h1>
&lt;p>Write the configuration that was previously in &lt;code>prometheus.prometheusSpec.additionalScrapeConfigs&lt;/code> in &lt;code>values.yaml&lt;/code> to a separate file.&lt;/p>
&lt;ul>
&lt;li>prometheus-scrape-target.yaml&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#f92672">job_name&lt;/span>: &lt;span style="color:#ae81ff">server1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">static_configs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">targets&lt;/span>: [&lt;span style="color:#e6db74">&amp;#34;1.1.1.1:9100&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">alias&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;server1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">job&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;node-exporter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">component&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;linux-server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#f92672">job_name&lt;/span>: &lt;span style="color:#ae81ff">server2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">static_configs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">targets&lt;/span>: [&lt;span style="color:#e6db74">&amp;#34;1.1.1.2:9100&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">alias&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;server2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">job&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;node-exporter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">component&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;linux-server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="2-generate-a-secret-manifest-from-the-file">2. Generate a Secret manifest from the file&lt;/h1>
&lt;p>&lt;code>kubectl create secret generic --from-file=prometheus-scrape-target.yaml --dry-run=client -o yaml &amp;gt; additional-scrape-target.yaml&lt;/code>
The command above generates a Secret manifest YAML file that can create a Kubernetes Secret based on the file we just wrote.&lt;/p></description></item><item><title>Grafana, Loki ERROR - Failed to load the data source configuration for loki: Unable to fetch alert rules. Is the loki data source properly configured?</title><link>https://blog.bumgu.com/en/post/2024/12/08/grafana_loki_error/</link><pubDate>Sun, 08 Dec 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/12/08/grafana_loki_error/</guid><description>&lt;p>I deployed Loki on Kubernetes and connected it to Grafana as a Data Source, and it had been working well. However, on the Alerting rulers page, I saw an error message like &lt;code>Failed to load the data source configuration for loki: Unable to fetch alert rules. Is the loki data source properly configured?&lt;/code>.&lt;/p>
&lt;p>The Loki Helm Chart I use is version 6.22 from &lt;a href="https://artifacthub.io/packages/helm/grafana/loki">here&lt;/a>.&lt;/p>
&lt;hr>
&lt;h1 id="1-existing-values">1. Existing values&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">rulerConfig&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">storage&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">local&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">local&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">directory&lt;/span>: &lt;span style="color:#ae81ff">/etc/loki/rules&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">rule_path&lt;/span>: &lt;span style="color:#ae81ff">/rules&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">enable_api&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">alertmanager_url&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;&amp;lt;alertmanager_url&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>/etc/loki/rules&lt;/code> is read-only because it is the directory where alert rules are stored persistently. Rules created by clients such as Grafana are temporarily stored in the directory specified by &lt;code>rule_path&lt;/code>, so that directory needs to be writable. Therefore, we need to mount &lt;code>rule_path&lt;/code> as a volume.&lt;/p></description></item><item><title>Installing MetalLB When Using Kubespray</title><link>https://blog.bumgu.com/en/post/2024/09/22/kubespray_metallb_install/</link><pubDate>Sun, 22 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/09/22/kubespray_metallb_install/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/en/post/2024/09/15/kubernetes_clustering_with_kubespray/">Last time&lt;/a>, when I used &lt;code>kubespray&lt;/code> and &lt;code>ansible&lt;/code> to build a Kubernetes cluster, the final MetalLB installation failed. Today I&amp;rsquo;ll install it.&lt;/p>
&lt;h3 id="1-install-metallb">1. Install MetalLB&lt;/h3>
&lt;p>I followed the &lt;a href="https://metallb.universe.tf/installation/">official documentation&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run the command above to install it.&lt;/p>
&lt;h4 id="1-1-modify-the-kube-proxy-configmap">1-1. Modify the kube-proxy ConfigMap&lt;/h4>
&lt;p>In IPVS mode, &lt;code>MetalLB&lt;/code> performs load balancing by strictly controlling ARP responses. When strictARP is enabled in this mode, kube-proxy ensures that only the explicitly designated node handles ARP responses for an IP assigned to a node.&lt;/p></description></item><item><title>Connecting to a Database with Go and GORM</title><link>https://blog.bumgu.com/en/post/2024/09/18/go_gorm_db/</link><pubDate>Wed, 18 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/09/18/go_gorm_db/</guid><description>&lt;p>Recently, I&amp;rsquo;ve been studying Go to build a custom exporter for my company&amp;rsquo;s service application.
I tried connecting to a database and inserting data with &lt;code>GORM&lt;/code>, the Go ecosystem&amp;rsquo;s ORM.&lt;/p>
&lt;hr>
&lt;h3 id="1-setup">1. Setup&lt;/h3>
&lt;h4 id="1-1-set-up-go">1-1. Set up Go&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>go mod init // Initialize the project
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get -u gorm.io/gorm // Download the GORM package
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get -u gorm.io/driver/mysql // Download the MySQL driver
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="1-2-run-mariadb-with-docker">1-2. Run MariaDB with Docker&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>docker run -d -p 3306:3306 --name maria &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-e MYSQL_ALLOW_EMPTY_PASSWORD&lt;span style="color:#f92672">=&lt;/span>true mariadb:10.9
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-connect-to-the-database">2. Connect to the database&lt;/h3>
&lt;p>Now let&amp;rsquo;s connect GORM to the database.&lt;/p></description></item><item><title>Building a Kubernetes Cluster with Kubespray</title><link>https://blog.bumgu.com/en/post/2024/09/15/kubernetes_clustering_with_kubespray/</link><pubDate>Sun, 15 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/09/15/kubernetes_clustering_with_kubespray/</guid><description>&lt;p>Let&amp;rsquo;s build a Kubernetes cluster using &lt;code>Ansible&lt;/code> and &lt;code>Kubespray&lt;/code>.&lt;/p>
&lt;hr>
&lt;h3 id="1-create-vms-using-vagrant">1. Create VMs using Vagrant&lt;/h3>
&lt;p>I referred to &lt;a href="https://github.com/techiescamp/kubernetes-certification-guide">kubernetes-certification-guide&lt;/a> for the Vagrant configuration file.&lt;/p>
&lt;p>&lt;strong>setting.yaml&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># settings.yaml&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># box_name: &amp;#34;sloopstash/ubuntu-22-04&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">box_name&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;bento/ubuntu-22.04&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># box_version: &amp;#34;2.1.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">vm&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;controlplane&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ip&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;192.168.64.163&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">memory&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2048&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cpus&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;node01&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ip&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;192.168.64.164&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">memory&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2048&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cpus&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;node02&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ip&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;192.168.64.165&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">memory&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2048&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cpus&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Vagrantfile&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="display:flex;">&lt;span>require &lt;span style="color:#e6db74">&amp;#39;yaml&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>settings &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">YAML&lt;/span>&lt;span style="color:#f92672">.&lt;/span>load_file(&lt;span style="color:#66d9ef">File&lt;/span>&lt;span style="color:#f92672">.&lt;/span>join(&lt;span style="color:#66d9ef">File&lt;/span>&lt;span style="color:#f92672">.&lt;/span>dirname(__FILE__), &lt;span style="color:#e6db74">&amp;#39;settings.yaml&amp;#39;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">Vagrant&lt;/span>&lt;span style="color:#f92672">.&lt;/span>configure(&lt;span style="color:#e6db74">&amp;#34;2&amp;#34;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span> &lt;span style="color:#f92672">|&lt;/span>config&lt;span style="color:#f92672">|&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>box &lt;span style="color:#f92672">=&lt;/span> settings&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;box_name&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># config.vm.box_version = settings[&amp;#39;box_version&amp;#39;]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>box_check_update &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> settings&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;vm&amp;#39;&lt;/span>&lt;span style="color:#f92672">].&lt;/span>each &lt;span style="color:#66d9ef">do&lt;/span> &lt;span style="color:#f92672">|&lt;/span>vm_config&lt;span style="color:#f92672">|&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>define vm_config&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span> &lt;span style="color:#f92672">|&lt;/span>vm&lt;span style="color:#f92672">|&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>hostname &lt;span style="color:#f92672">=&lt;/span> vm_config&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>network &lt;span style="color:#e6db74">&amp;#34;public_network&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">ip&lt;/span>: vm_config&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ip&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>synced_folder &lt;span style="color:#e6db74">&amp;#34;.&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;/vagrant&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">disabled&lt;/span>: &lt;span style="color:#66d9ef">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>provider &lt;span style="color:#e6db74">&amp;#34;vmware_fusion&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span> &lt;span style="color:#f92672">|&lt;/span>vb&lt;span style="color:#f92672">|&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vb&lt;span style="color:#f92672">.&lt;/span>gui &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vb&lt;span style="color:#f92672">.&lt;/span>memory &lt;span style="color:#f92672">=&lt;/span> vm_config&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;memory&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vb&lt;span style="color:#f92672">.&lt;/span>cpus &lt;span style="color:#f92672">=&lt;/span> vm_config&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;cpus&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm&lt;span style="color:#f92672">.&lt;/span>vm&lt;span style="color:#f92672">.&lt;/span>provision &lt;span style="color:#e6db74">&amp;#34;shell&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">inline&lt;/span>: &lt;span style="color:#e6db74">&amp;lt;&amp;lt;-SHELL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&lt;/span> apt update
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> apt upgrade &lt;span style="color:#f92672">-&lt;/span>y
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> apt install &lt;span style="color:#f92672">-&lt;/span>y wget vim net&lt;span style="color:#f92672">-&lt;/span>tools gcc make tar git unzip sysstat tree
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> echo &lt;span style="color:#e6db74">&amp;#34;192.168.201.10 controlplane&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#e6db74">/etc/&lt;/span>hosts
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> echo &lt;span style="color:#e6db74">&amp;#34;192.168.201.11 node01&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#e6db74">/etc/&lt;/span>hosts
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> echo &lt;span style="color:#e6db74">&amp;#34;192.168.201.12 node02&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#e6db74">/etc/&lt;/span>hosts
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">SHELL&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># vm.vm.provision &amp;#34;shell&amp;#34;, path: &amp;#34;scripts/common.sh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Create the VMs with the &lt;code>vagrant up&lt;/code> command.
After creation, connect and check with &lt;code>vagrant ssh &amp;lt;vm name&amp;gt;&lt;/code>.
You can also connect with &lt;code>ssh vagrant@&amp;lt;ip&amp;gt;&lt;/code>. The initial password for the &lt;code>vagrant&lt;/code> user is &lt;code>vagrant&lt;/code>.&lt;/p></description></item><item><title>Building a k3s Cluster on Debian</title><link>https://blog.bumgu.com/en/post/2024/06/30/building-a-k3s-cluster-on-debian/</link><pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/06/30/building-a-k3s-cluster-on-debian/</guid><description>&lt;p>I originally built a k8s cluster (one master and two workers) for testing at work, but it ended up being used in production. The servers are running Ubuntu Desktop, and because intermittent network disconnections have been occurring, I&amp;rsquo;m considering rebuilding it, so I did this exercise.&lt;/p>
&lt;p>&lt;a href="https://www.debian.org/releases/stable/debian-installer/">Download the official ISO&lt;/a>&lt;/p>
&lt;p>From the link above, I chose the desired architecture under the netinst CD image, downloaded it, and installed it in a VM.&lt;br>
&lt;code>Local environment: Mac OS Sonoma 14.5 | Macbook Air M1 | 16G RAM&lt;/code>&lt;br>
&lt;code>VM installation environment: Debian 12.6 | 2 cores | 4G RAM&lt;/code>&lt;/p></description></item><item><title>Issuing a Gmail OAuth2 Token (Bun, Elysia)</title><link>https://blog.bumgu.com/en/post/2024/06/19/gmail_oauth_with_bun_elysia/</link><pubDate>Wed, 19 Jun 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/06/19/gmail_oauth_with_bun_elysia/</guid><description>&lt;h1 id="1-what-is-bun">1. What is Bun?&lt;/h1>
&lt;p>&lt;code>bun&lt;/code> is a JavaScript runtime like &lt;code>node.js&lt;/code>. You can find more information and installation instructions at &lt;a href="https://bun.sh/">bun.sh&lt;/a>. In this post, I&amp;rsquo;ll use the &lt;code>elysia&lt;/code> framework, similar to &lt;code>express&lt;/code> in &lt;code>node.js&lt;/code>, and walk through issuing a Google Gmail OAuth2 token.&lt;/p>
&lt;h1 id="2-create-an-elysia-app">2. Create an Elysia app&lt;/h1>
&lt;p>Let&amp;rsquo;s create an &lt;code>elysia&lt;/code> app. You can create one with the command
&lt;code>bun create elysia ${app name}&lt;/code>&lt;/p>
&lt;p>&lt;img src="https://velog.velcdn.com/images/kellyb9/post/6196cf35-57fc-4c79-ab03-cbb5648e4fb8/image.png" alt="">&lt;/p>
&lt;p>If you look at the &lt;code>src/index.ts&lt;/code> file:&lt;/p></description></item><item><title>AWS Summit Seoul 2024: A Retrospective</title><link>https://blog.bumgu.com/en/post/2024/05/18/aws_summit_seoul_2024/</link><pubDate>Sat, 18 May 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/05/18/aws_summit_seoul_2024/</guid><description>&lt;p>I attended AWS Summit Seoul 2024, held on May 16 and 17. I only went on May 17, and this is my recap.&lt;/p>
&lt;p>&lt;img src="https://velog.velcdn.com/images/kellyb9/post/78a1b38b-c16b-4fa6-ab11-e120b9a1aa70/image.JPG" alt="">&lt;/p>
&lt;p>After scanning the QR code I received by email to enter, I was given a name tag. Although I work in SRE, DevOps, and systems engineering, I listed my role as systems engineer, so that is what appeared on it.&lt;/p>
&lt;h1 id="930-keynote">9:30 Keynote&lt;/h1>
&lt;p>The keynote began at 9:30 in the auditorium on the third floor. We could watch videos and hear talks by the AWS Senior Tech Evangelist, Infralab CTO Dongwook Lee, Sendbird CTO Jeongjin Koo, and AWS&amp;rsquo;s Werner Vogels and Matt Wood.
One of the key points was &lt;code>Frugal Architect&lt;/code>. Cloud environments remove the hardware constraints that existed on premises and make it easy to build, with high scalability and flexibility. However, costs can be higher than expected, so an architecture that takes all of this into account and sets constraints is called &lt;code>Frugal Architecture&lt;/code>. I thought this was an important point for a junior engineer to keep in mind. (Although our company is on premises.)
&lt;img src="https://velog.velcdn.com/images/kellyb9/post/0528b37b-98ef-47f6-bfde-733187839b52/image.jpg" alt="">&lt;/p></description></item><item><title>Persistent Data (PV) with NFS in an On-Premises Kubernetes Environment</title><link>https://blog.bumgu.com/en/post/2024/04/28/kubernetes_nfs_pv/</link><pubDate>Sun, 28 Apr 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/04/28/kubernetes_nfs_pv/</guid><description>&lt;h1 id="master-server">Master Server&lt;/h1>
&lt;h2 id="1-install-the-nfs-server-on-the-master-server">1. Install the NFS server on the master server&lt;/h2>
&lt;ul>
&lt;li>Install NFS (Network File System)
&lt;code>apt-get install nfs-kernel-server&lt;/code>&lt;/li>
&lt;li>Start the NFS server
&lt;code>systemctl start nfs-server&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="2-specify-the-shared-directory">2. Specify the shared directory&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Create the directory to share
&lt;code>mkdir /data/nfsk8s&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Specify the shared directory
&lt;code>vim /etc/expoerts&lt;/code>
&lt;strong>Add &lt;code>&amp;lt;directory path&amp;gt; &amp;lt;\IP&amp;gt;(\&amp;lt;permissions&amp;gt;)&lt;/code> to the bottom of the file.&lt;/strong>
&lt;strong>Do not add spaces when entering permissions: (ro, rw) X → (ro,rw) O&lt;/strong>
&lt;strong>Apply permissions to all IPs&lt;/strong>
&lt;code>/data/nfsk8s *(rw,sync,no_root_squash)&lt;/code>&lt;/p></description></item><item><title>Kubernetes Errors I Encountered</title><link>https://blog.bumgu.com/en/post/2024/04/25/kuberntes_errors_that_i_faced/</link><pubDate>Thu, 25 Apr 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/04/25/kuberntes_errors_that_i_faced/</guid><description>&lt;p>When setting up a cluster with &lt;code>kubeadm&lt;/code> in an on-premises environment to build a monitoring server (prometheus, zabbix), I ran into several errors. Here I&amp;rsquo;ve collected them.&lt;/p>
&lt;h1 id="1">1.&lt;/h1>
&lt;pre tabindex="0">&lt;code>Unable to connect to the server: x509:
certificate signed by unknown authority
(possibly because of &amp;#34;crypto/rsa: verification error&amp;#34; while trying to verify candidate authority certificate &amp;#34;kubernetes&amp;#34;)
&lt;/code>&lt;/pre>&lt;p>This error can occur when running &lt;code>kubeadm init&lt;/code> or &lt;code>kubeadm join&lt;/code>. It usually happens when using an old or incorrectly configured config.&lt;/p></description></item><item><title>Docker - Creating an Image: Install Git on Ubuntu</title><link>https://blog.bumgu.com/en/post/2024/03/18/docker-ubuntu-git-install/</link><pubDate>Mon, 18 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/03/18/docker-ubuntu-git-install/</guid><description>&lt;p>Let&amp;rsquo;s use Docker to create an Ubuntu image with git installed.&lt;/p>
&lt;h1 id="1-run-vanilla-ubuntu">1. Run Vanilla Ubuntu&lt;/h1>
&lt;p>Let&amp;rsquo;s run an Ubuntu image with nothing installed.&lt;/p>
&lt;p>&lt;code>docker run -it --name velog-docker ubuntu:latest&lt;/code>&lt;br>
This means run a container using the image from the &lt;code>latest&lt;/code> tag of the Ubuntu repository, and name it &lt;code>velog-docker&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>docker run -it --name velog-docker ubuntu:latest
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Unable to find image &lt;span style="color:#e6db74">&amp;#39;ubuntu:latest&amp;#39;&lt;/span> locally
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>latest: Pulling from library/ubuntu
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>f4bb4e8dca02: Already exists
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Digest: sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Status: Downloaded newer image &lt;span style="color:#66d9ef">for&lt;/span> ubuntu:latest
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@edd8a22995f6:/#
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I had run it before, so it showed &lt;code>Already exists&lt;/code>.
I connected to Ubuntu and opened bash.
Then, running the &lt;code>git --version&lt;/code> command reports that it can&amp;rsquo;t find the &lt;code>git&lt;/code> command. That&amp;rsquo;s expected, since it hasn&amp;rsquo;t been installed yet.
&lt;img src="https://blog.bumgu.com/images/post/7-docker-ubuntu-git/1.png" alt="">&lt;/p></description></item><item><title>Practicing Kubernetes with Minikube</title><link>https://blog.bumgu.com/en/post/2024/03/10/kubernetes-practice/</link><pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/03/10/kubernetes-practice/</guid><description>&lt;p>I followed the guide by &lt;a href="https://subicura.com/k8s/guide/#%E1%84%8B%E1%85%AF%E1%84%83%E1%85%B3%E1%84%91%E1%85%B3%E1%84%85%E1%85%A6%E1%84%89%E1%85%B3-%E1%84%87%E1%85%A2%E1%84%91%E1%85%A9">subicura&lt;/a> to do this practice.&lt;/p>
&lt;p>If you&amp;rsquo;ve installed &lt;code>minukube&lt;/code> and &lt;code>kubectl&lt;/code>, you can start right away.&lt;/p>
&lt;h2 id="what-is-minikube">What is Minikube?&lt;/h2>
&lt;blockquote>
&lt;p>miniKube is a tool that quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. In other words, minikube makes it easy to create a Kubernetes cluster locally. It can even manage multiple clusters.
To run a Kubernetes cluster, you need to install at least the scheduler, controller, api-server, etcd, kubelet, and kube-proxy. Depending on your needs, you may also need to install DNS, an ingress controller, a storage class, and so on. Installing Kubernetes is an important process, but when you&amp;rsquo;re first learning, it&amp;rsquo;s more important to learn how to use it in practice than to focus on installation.
Minikube is a tool that makes this setup easy and quick. It can be used on Windows, macOS, and Linux, supports various virtual environments, and runs without problems in most environments.
&lt;a href="https://velog.io/@yoojinjangjang/Minikube">Source&lt;/a>&lt;/p></description></item><item><title>Next.js-like Routing in React with vite-plugin-next-react-router</title><link>https://blog.bumgu.com/en/post/2024/02/26/vite_plugin_next_react_router/</link><pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/02/26/vite_plugin_next_react_router/</guid><description>&lt;p>While taking a course, I ran into an error because &lt;code>vite-plugin-next-react-router&lt;/code> no longer worked: its installed version differed from the version at the time the course was recorded. I resolved the error by referring to the &lt;a href="https://github.com/zoubingwu/vite-plugin-next-react-router">official GitHub repository&lt;/a> and &lt;a href="https://velog.io/@cjy921004/vite-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-vite-plugin-next-router-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">this blog post&lt;/a>.&lt;/p>
&lt;hr>
&lt;h1 id="1-create-a-vite-project">1. Create a Vite project&lt;/h1>
&lt;p>Create a Vite project with &lt;code>yarn create vite&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>➜ yarn create vite
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>yarn create v1.22.19
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">(&lt;/span>node:11216&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#f92672">[&lt;/span>DEP0040&lt;span style="color:#f92672">]&lt;/span> DeprecationWarning: The &lt;span style="color:#e6db74">`&lt;/span>punycode&lt;span style="color:#e6db74">`&lt;/span> module is deprecated. Please use a userland alternative instead.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">(&lt;/span>Use &lt;span style="color:#e6db74">`&lt;/span>node --trace-deprecation ...&lt;span style="color:#e6db74">`&lt;/span> to show where the warning was created&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>1/4&lt;span style="color:#f92672">]&lt;/span> 🔍 Resolving packages...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>2/4&lt;span style="color:#f92672">]&lt;/span> 🚚 Fetching packages...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>3/4&lt;span style="color:#f92672">]&lt;/span> 🔗 Linking dependencies...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>4/4&lt;span style="color:#f92672">]&lt;/span> 🔨 Building fresh packages...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>success Installed &lt;span style="color:#e6db74">&amp;#34;create-vite@5.1.0&amp;#34;&lt;/span> with binaries:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - create-vite
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - cva
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>✔ Project name: … vite-practice
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>✔ Select a framework: › React
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>✔ Select a variant: › TypeScript
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Scaffolding project in /Users/location/toyour/project/vite-practice...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Done. Now run:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cd vite-practice
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yarn
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yarn dev
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>✨ Done in 13.75s.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enter a project name and choose the options that suit your needs. I chose React and TypeScript.&lt;/p></description></item><item><title>Applying SSG to a Next.js Project</title><link>https://blog.bumgu.com/en/post/2024/02/11/nextjs_ssg/</link><pubDate>Sun, 11 Feb 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/02/11/nextjs_ssg/</guid><description>&lt;p>My &lt;a href="https://bumgu.com">website&lt;/a> is built with Next.js.
Although the &lt;code>Project&lt;/code> and &lt;code>Career&lt;/code> tabs here are small web pages, I wanted to try pre-rendering them with SSG, so I gave it a try.&lt;/p>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/5-nextjs-ssg/1.png" alt="">
I&amp;rsquo;ll apply it to this page.&lt;/p>
&lt;ul>
&lt;li>Development environment
Next.js 13
TypeScript&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="1-existing-code">1. Existing code&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-ts" data-lang="ts">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#a6e22e">thumbnailLively&lt;/span> &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#39;../public/images/works/Lively_main.png&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#a6e22e">thumbnailWhatCandy&lt;/span> &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#39;../public/images/works/whatCandy.png&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#a6e22e">thumbnailSpotify&lt;/span> &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#39;../public/images/works/know-your-spotify.png&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">Projects&lt;/span> &lt;span style="color:#f92672">=&lt;/span> () &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Container&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Heading&lt;/span> &lt;span style="color:#a6e22e">as&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;h3&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">fontSize&lt;/span>&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">20&lt;/span>} &lt;span style="color:#a6e22e">mb&lt;/span>&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#ae81ff">4&lt;/span>}&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Projects&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Heading&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Layout&lt;/span> &lt;span style="color:#a6e22e">title&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;works&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">ProjectGridItem&lt;/span> &lt;span style="color:#a6e22e">title&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Lively&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">thumbnail&lt;/span>&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#a6e22e">thumbnailLively&lt;/span>} &lt;span style="color:#a6e22e">gitRepo&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Lively&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Description&lt;/span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">ProjectGridItem&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">ProjectGridItem&lt;/span> &lt;span style="color:#a6e22e">title&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;what candy&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">thumbnail&lt;/span>&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#a6e22e">thumbnailWhatCandy&lt;/span>} &lt;span style="color:#a6e22e">gitRepo&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;what-candy&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Description&lt;/span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">ProjectGridItem&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">ProjectGridItem&lt;/span> &lt;span style="color:#a6e22e">title&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;know your Spotify&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">thumbnail&lt;/span>&lt;span style="color:#f92672">=&lt;/span>{&lt;span style="color:#a6e22e">thumbnailSpotify&lt;/span>} &lt;span style="color:#a6e22e">gitRepo&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;know-your-spotify&amp;#34;&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Description&lt;/span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">ProjectGridItem&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Section&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Layout&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">Container&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> );
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>};
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Previously, I passed prop values to components and rendered them as shown above.&lt;/p></description></item><item><title>Using the Spotify API (Node.js, Express, React)</title><link>https://blog.bumgu.com/en/post/2024/01/31/spotify_api/</link><pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/01/31/spotify_api/</guid><description>&lt;p>&lt;a href="https://github.com/bonzonkim/spotify-ranking-app">GitHub link&lt;/a>&lt;/p>
&lt;p>I originally planned to implement this with Next.js, but since this was my first time implementing OAuth2.0-based login and I was not very experienced with Next.js, I built it with Node.js + Express + React instead.&lt;/p>
&lt;h1 id="1-spotifys-login-flow">1. Spotify&amp;rsquo;s login flow&lt;/h1>
&lt;p>Spotify uses the OAuth2.0 framework.&lt;/p>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/3-spotify_api/0.png" alt="">
First, here is what we need to use the API:&lt;/p>
&lt;ul>
&lt;li>&lt;code>code&lt;/code>: Used to exchange for an &lt;code>access_token&lt;/code> and &lt;code>refresh_token&lt;/code>&lt;/li>
&lt;li>&lt;code>access_token&lt;/code>: The token required to retrieve the actual information&lt;/li>
&lt;li>&lt;code>refresh_token&lt;/code>: Used to obtain a new &lt;code>access_token&lt;/code> when it expires&lt;/li>
&lt;li>&lt;code>client_id&lt;/code>: The identifier for the Spotify Developer app (issued to the developer)&lt;/li>
&lt;li>&lt;code>client_secret&lt;/code>: The secret key for the Spotify Developer app (issued to the developer)&lt;/li>
&lt;/ul>
&lt;p>The flow is:&lt;/p></description></item><item><title>Data Fetching with CSR and SSR</title><link>https://blog.bumgu.com/en/post/2024/01/26/csr_ssr_data_fetching/</link><pubDate>Fri, 26 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/01/26/csr_ssr_data_fetching/</guid><description>&lt;p>While developing, you&amp;rsquo;ll encounter both &lt;code>CSR&lt;/code> and &lt;code>SSR&lt;/code>. In this post, I&amp;rsquo;ll show how to use these two approaches in Next.js to make a GET request and receive JSON data.&lt;/p>
&lt;h1 id="1-the-difference-between-csr-and-ssr">1. The difference between CSR and SSR&lt;/h1>
&lt;h4 id="csr-client-side-rendering">CSR (Client-Side Rendering)&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>Concept
When a website initially loads, the server sends only HTML and basic JavaScript code to the client; the content that will actually be visible is initially empty.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>How it works
When the JavaScript code runs on the client, the client requests and receives the necessary data from the server, then uses that data to build and render the page dynamically.&lt;/p></description></item><item><title>About Next.js getStaticProps()</title><link>https://blog.bumgu.com/en/post/2024/01/23/getstaticprops/</link><pubDate>Tue, 23 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/en/post/2024/01/23/getstaticprops/</guid><description>&lt;p>Hello. In this post, we&amp;rsquo;ll take a look at pre-rendering, one of Next.js&amp;rsquo;s defining features.&lt;/p>
&lt;h1 id="1-pre-rendering">1. Pre-rendering&lt;/h1>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/1-getStaticProps/1.png" alt="">&lt;/p>
&lt;blockquote>
&lt;p>Before talking about fetching data, let&amp;rsquo;s discuss one of the most important concepts in Next.js: pre-rendering.
By default, Next.js pre-renders every page. This means that instead of having client-side JavaScript do all the work, Next.js generates each page&amp;rsquo;s HTML in advance. Pre-rendering can improve performance and SEO.
Each generated HTML page is associated with the minimum JavaScript code needed for that page. When the page loads in the browser, that JavaScript runs and makes the page fully interactive. (This process is called hydration.)&lt;/p></description></item></channel></rss>