<?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/</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/index.xml" rel="self" type="application/rss+xml"/><item><title>Vector vs OpenTelemetry Collector: 로그 수집기 비교</title><link>https://blog.bumgu.com/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/post/2026/10/10/vector-otel-log-pipeline-benchmark/</guid><description>&lt;p>EKS에서 노드 로그를 많이 수집할 때 Vector를 쓸지 OpenTelemetry Collector를 쓸지 제품 소개나 기능 목록만으로 결정하기는 어렵습니다. 노드에서 파일을 읽고, 중앙으로 보내고, 파싱하고, 라우팅하는 실제 경로에서 CPU와 메모리가 어떻게 달라지는지가 궁금했습니다.&lt;/p>
&lt;p>그래서 Kubernetes에 두 파이프라인을 올려 같은 형태의 1 KiB 로그를 흘려보냈습니다. 결론부터 말하면 이번 단일 노드 실행에서는 &lt;strong>10k logs/s에서 OTel 쪽 평균 CPU가 낮았고, Vector 쪽 메모리 사용량이 적었습니다.&lt;/strong> 다만 이 문장은 운영 승자를 뜻하지 않습니다. 마지막 출력이 실제 저장소가 아닌 폐기 sink였고, 25k부터는 두 수집기에 들어간 부하 자체가 달랐습니다.&lt;/p></description></item><item><title>GitHub Actions Docker 빌드에서 사라지지 않는 디스크 사용량 줄이기</title><link>https://blog.bumgu.com/post/2026/09/23/github-actions-buildx-disk/</link><pubDate>Wed, 23 Sep 2026 00:00:00 +0900</pubDate><guid>https://blog.bumgu.com/post/2026/09/23/github-actions-buildx-disk/</guid><description>&lt;h2 id="문제">문제&lt;/h2>
&lt;p>GitHub Actions에서 Docker 이미지를 빌드하다가 러너 디스크가 부족해지는 문제를 확인했습니다. 압축 크기 1.40GB인 이미지가 같은 공유 워크플로에서 &lt;code>no space left on device&lt;/code>로 두 번 실패했는데, 두 번의 실패 지점이 서로 달랐습니다. 처음에는 Dockerfile이 너무 크거나 캐시가 오래 쌓였다고 생각했습니다. 하지만 이번 경우에는 한 번의 이미지 빌드 결과가 서로 다른 저장소에 여러 번 복제되는 구조가 원인이었습니다.&lt;/p>
&lt;p>이 글에서는 기존 Buildx 빌드와 push 흐름에서 어떤 데이터가 어디에 저장되는지 정리하고, &lt;code>load: true&lt;/code>를 제거해 로컬 Docker 데몬 사본을 없애는 방법을 설명합니다.&lt;/p></description></item><item><title>Nginx와 Envoy Gateway의 라우팅</title><link>https://blog.bumgu.com/post/2026/08/02/envoy-gateway-proxy/</link><pubDate>Sun, 02 Aug 2026 16:07:35 +0900</pubDate><guid>https://blog.bumgu.com/post/2026/08/02/envoy-gateway-proxy/</guid><description>&lt;h1 id="nginx-정규식-라우팅을-envoy-gateway로-옮길-수-있을까">nginx 정규식 라우팅을 Envoy Gateway로 옮길 수 있을까&lt;/h1>
&lt;p>엣지 게이트웨이를 OpenResty(nginx)에서 Envoy Gateway로 전환하려면 먼저 답해야 하는
질문이 하나 있었습니다. &lt;strong>nginx conf에 있는 정규식 location 설정을 Gateway API로
그대로 옮길 수 있는가.&lt;/strong>&lt;/p>
&lt;p>문서를 읽는 것으로는 답이 나오지 않았습니다. Gateway API 스펙이 정규식 경로 매칭을
Extended 등급으로 두고 &lt;strong>우선순위를 아예 규정하지 않기&lt;/strong> 때문입니다. 구현체 재량이라고만
적혀 있습니다.&lt;/p>
&lt;p>그래서 kind에 Envoy Gateway를 올려 직접 측정했습니다. 이 글은 그 기록입니다.
모든 매니페스트를 포함해 그대로 재현할 수 있게 썼습니다.&lt;/p></description></item><item><title>Loki-VictoriaLogs Proxy 사용해보기</title><link>https://blog.bumgu.com/post/2026/04/19/loki-vl-proxy/</link><pubDate>Sun, 19 Apr 2026 18:50:12 +0900</pubDate><guid>https://blog.bumgu.com/post/2026/04/19/loki-vl-proxy/</guid><description>&lt;p>개인 쿠버네티스 클러스터에서는 로그 저장소로 &lt;a href="https://docs.victoriametrics.com/victorialogs/">VictoriaLogs&lt;/a>를 사용하고 있습니다. Loki에 비해 리소스 사용량이 적고 설치/운영이 단순해 소규모 환경에 적합하기 때문입니다.&lt;/p>
&lt;p>다만 Grafana에서 VictoriaLogs 데이터소스를 공식 지원하긴 하나, 자동완성이나 Explore UX가 아직 Loki만큼 매끄럽진 않아서 결국 &lt;a href="https://docs.victoriametrics.com/victorialogs/querying/#web-ui">vmui&lt;/a>로 돌아가곤 했습니다.&lt;br>
그러던 중 &lt;a href="https://github.com/ReliablyObserve/Loki-VL-proxy">loki-vl-proxy&lt;/a>라는 프로젝트를 발견했습니다. &lt;strong>VictoriaLogs를 백엔드로 두고 Loki API와 호환되는 엔드포인트를 제공하는 Proxy&lt;/strong> 여서, Grafana 입장에선 그냥 Loki를 쓰는 것처럼 동작합니다. 저장은 VL로 하고, 조회는 Loki UX로 하겠다는 발상이라 바로 배포해보기로 했습니다.&lt;/p>
&lt;h2 id="아키텍처">아키텍처&lt;/h2>
&lt;p>loki-vl-proxy는 이름 그대로 &lt;strong>읽기 전용 프록시&lt;/strong>입니다. &lt;code>push&lt;/code>(수집) 경로는 막혀있고, Loki의 query API만 VictoriaLogs로 중계합니다. 즉 로그 수집은 기존처럼 &lt;code>vlagent&lt;/code>/OTLP/Loki push → VL 로 그대로 두고, &lt;strong>조회 계층에만 Loki 호환 레이어&lt;/strong>를 끼워 넣는 구조입니다.&lt;/p></description></item><item><title>Terraform으로 GKE를 구축하고 Tailscale로 접근하기</title><link>https://blog.bumgu.com/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/post/2025/12/18/building-gke-with-terraform-access-with-tailscale/</guid><description>&lt;p>GCP Free trial Credit이 많이 남아서 실무라 가정하고 GKE를 구축하고, Tailscale을 통해 네트워크단에서 접근을 제한하는 작업을 해보았습니다.&lt;/p>
&lt;h1 id="1-terraform">1. Terraform&lt;/h1>
&lt;p>폴더구조는 아래와 같습니다.&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>&lt;code>modules&lt;/code>안에서 필요한 리소스를 정의해놓고 root의 &lt;code>main.tf&lt;/code>에서 실제 리소스를 생성하는 식으로 모듈화 하였습니다.&lt;/p></description></item><item><title>오픈소스 VictoriaMetrics 에 기여하기</title><link>https://blog.bumgu.com/post/2025/11/25/contirubte_victoriametrics/</link><pubDate>Tue, 25 Nov 2025 22:50:21 +0900</pubDate><guid>https://blog.bumgu.com/post/2025/11/25/contirubte_victoriametrics/</guid><description>&lt;h1 id="발단">발단&lt;/h1>
&lt;p>지난 4월, 회사 로깅 툴을 Loki에서 VictoriaLogs로 교체했다. 꽤 만족스러운 결정이었지만, 팀의 요구사항인 &amp;ldquo;알람 메시지에 로그 본문 포함&amp;quot;이 안 된다는 것이었다.&lt;br>
VictoriaLogs는 구조적으로 알람 트리거 시 숫자 값만 반환하기 때문에 로그 텍스트를 알람에 실을 수 없었다. &lt;a href="https://github.com/VictoriaMetrics/VictoriaLogs/issues/81">issue&lt;/a>도 올려보고 방법을 찾아봤지만, 결국 답은 &amp;lsquo;직접 만들기&amp;rsquo;뿐이었다.&lt;br>
아쉬운 대로 간단한 &lt;a href="https://github.com/bonzonkim/vmalert-webhook">Webhook Server&lt;/a>를 구현해 중간에서 로그를 긁어오도록 처리하고 사용했다.&lt;br>
그런데 나와 같은 불편함을 겪던 몇몇 사람들이 검색을 통해 내 레포지터리를 발견하고 코드를 Fork 해가는 것이었다.&lt;br>
그 모습을 보고 단순히 땜질 처방으로 남겨두기보다 해결하고 싶어졌다. 그렇게 나는 VictoriaMetrics 공식 레포지터리에(정확히는 그 안의 Vmalert) 해당 기능을 직접 구현해 Contribution하게 되었다.&lt;/p></description></item><item><title>오픈소스 기여하기</title><link>https://blog.bumgu.com/post/2025/10/22/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC%ED%95%98%EA%B8%B0/</link><pubDate>Wed, 22 Oct 2025 15:20:22 +0900</pubDate><guid>https://blog.bumgu.com/post/2025/10/22/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC%ED%95%98%EA%B8%B0/</guid><description>&lt;p>저는 CI tool로 Github Actions를 자주 사용합니다.&lt;br>
다들 아시다시피, Github Actions는 yaml기반으로 workflow파일을 작성하는데 그러다보면 인덴트, 혹은 잘못된 문법으로 CI job failed 알람을 받아본 경험이 많을것 입니다.&lt;br>
그러다가 &lt;a href="https://x.com/JLarky/status/1980024323663638773">트위터&lt;/a> 에서
&lt;img src="https://blog.bumgu.com/images/post/28-contribute-opensource/1.png" alt="image">
이런 포스트를 발견했습니다.&lt;br>
이 &lt;code>gha-ts&lt;/code> 프로젝트는 TypeScript 로 workflow파일을 작성하면 &lt;code>.yml&lt;/code>파일을 생성해주는 오픈소스입니다.&lt;br>
인덴트나 문법 걱정없이 type safe한 workflow를 생성할 수 있습니다.&lt;br>
그런데 &lt;code>setup&lt;/code>에 node, java, python, go, dotnet은 존재하는데 bun이 없는걸 보고 bun setup을 추가하기로 마음 먹었습니다.&lt;/p>
&lt;hr>
&lt;h1 id="코드">코드&lt;/h1>
&lt;p>우선 &lt;a href="https://github.com/JLarky/gha-ts">Repository&lt;/a> 를 클론받고 브랜치를 생성했습니다.
&lt;code>/src/actions/setup.ts&lt;/code> 파일에 모든 &lt;code>setup&lt;/code> 함수와 인터페이스 등이 선언되어있어서 이 파일에 코드를 작성했습니다.&lt;/p></description></item><item><title>(1)Istio 맛보기</title><link>https://blog.bumgu.com/post/2025/09/07/play-with-istio/</link><pubDate>Sun, 07 Sep 2025 11:17:37 +0900</pubDate><guid>https://blog.bumgu.com/post/2025/09/07/play-with-istio/</guid><description>&lt;h1 id="1-istio">1. Istio?&lt;/h1>
&lt;p>많은 서비스들이 마이크로서비스아키텍처(MSA)로 구성 되어있습니다.&lt;br>
MSA는 각 서비스 간의 의존도를 낮추어 장애 전파를 낮추는데 특히 효과적 입니다.&lt;br>
하지만 MSA에서 서비스가 많아지면 많아질수록 복잡도가 증가하고, 어떤 서비스가 어떤 서비스를 호출하는지, 통신은 안전한지, 트러블 슈팅이 힘들어지는 문제 등이 있습니다.&lt;br>
이러한 복잡한 문제를 해결하는데 도움을 주는 도구가 Istio입니다.&lt;/p>
&lt;p>Istio는 Service Mesh의 대표적인 오픈소스 소프트웨어 입니다.&lt;br>
마이크로 서비스 간의 통신을 제어하고, 가시성을 제공하며 보안을 강화합니다.&lt;br>
마치 교통경찰이 수신호를 하여 교통흐름을 통제하고 있다고 생각하면 됩니다.&lt;/p></description></item><item><title>(2)사내 Openstack 구축으로 VM 대체하기 - nova, neutron, cinder</title><link>https://blog.bumgu.com/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/post/2025/07/06/building_private_cloud_using_openstack-2/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/post/2025/06/29/building_private_cloud_using_openstack-1/">Openstack 구축 1편&lt;/a>&lt;/p>
&lt;hr>
&lt;h1 id="nova">Nova&lt;/h1>
&lt;p>Openstack에서 Nova는 클라우드 환경의 Compute 리소스를 관리하는 핵심 컴포넌트 이다.&lt;br>
인스턴스의 생성, 관리, 삭제 등 라이프사이클 전체를 담당한다.&lt;br>
또한 neutron, cinder 등과 연계하여 인스턴스에 네트워크와 스토리지를 연결하는 필수 컴포넌트 이다.&lt;/p>
&lt;h3 id="controller-node">Controller Node&lt;/h3>
&lt;h4 id="1-mariadb에-nova-데이터베이스-생성">1. MariaDB에 Nova 데이터베이스 생성&lt;/h4>
&lt;ul>
&lt;li>&lt;code>create database nova_api&lt;/code>
&lt;ul>
&lt;li>Nova의 API 서비스와 관련된 글로벌 정보(예: 인스턴스 요청, 셀 정보, 고수준 트래킹 등)를 저장&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>create database nova&lt;/code>
&lt;ul>
&lt;li>실제 인스턴스(가상 머신)와 관련된 데이터를 저장하는 기본 셀(cell1)의 데이터베이스. 인스턴스의 상태, 할당된 자원, 이벤트 등 각 셀의 로컬 정보를 보관&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>create database nova_cell0&lt;/code>
&lt;ul>
&lt;li>Nova의 셀 구조에서 스케줄링에 실패한 인스턴스(어떤 컴퓨트 노드에도 할당되지 못한 인스턴스)를 저장하는 특수한 용도의 데이터베이스.&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-nova-유저-생성-및-권한-부여">2. Nova 유저 생성 및 권한 부여&lt;/h4>
&lt;ul>
&lt;li>유저 생성
&lt;ul>
&lt;li>&lt;code>openstack user create --domain default --password-prompt nova&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>관리자 권한 부여
&lt;ul>
&lt;li>&lt;code>openstack role add --project service --user nova admin&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Nova 서비스 엔티티 생성
&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-nova-api-서비스-엔드포인트-생성">3. Nova API 서비스 엔드포인트 생성&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-nova-패키지-설치">4. Nova 패키지 설치&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)사내 Openstack 구축으로 VM 대체하기 - keystone, glance, placement</title><link>https://blog.bumgu.com/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/post/2025/06/29/building_private_cloud_using_openstack-1/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/post/2025/07/06/building_private_cloud_using_openstack-2/">Openstack 구축 2편&lt;/a>&lt;/p>
&lt;hr>
&lt;h1 id="발단">발단&lt;/h1>
&lt;p>VM이 더이상 유지보수도 안되고 라이센스도 끝나서 대체 하기로 마음을 먹고 Openstack 구축을 시작했다.&lt;br>
이전에 &lt;a href="https://docs.openstack.org/openstack-helm/latest/">Openstack Helm&lt;/a> 으로 &lt;del>날먹&lt;/del> 해보려했으나 잘 되지 않아서 흐지부지 됐었으나..
6월4일에 열렸던 &lt;a href="www.cloudbro.ai">Cloud bro&lt;/a> 세미나에서 NHN Cloud 조성수님에게 여쭤봤다가 처음부터 helm으로 한번에 설치하려하지 말고 가이드보고 컴포넌트별로 설치하며 트러블 슈팅을 해보란 말씀에 하나하나씩 처음부터 설치를 해봤다.&lt;/p>
&lt;h1 id="환경">환경&lt;/h1>
&lt;ul>
&lt;li>OS: Ubuntu 24.04 3대&lt;/li>
&lt;li>1번서버 : Controller, Network, Compute&lt;/li>
&lt;li>2번서버 : Compute&lt;/li>
&lt;li>3번서버 : Compute&lt;/li>
&lt;/ul>
&lt;p>자원의 한계로 사내에 남는 서버를 주워서 구축했다&amp;hellip; 1번서버야 미안해&lt;/p></description></item><item><title>Kubernetes Cluster 버전 업그레이드</title><link>https://blog.bumgu.com/post/2025/06/22/kube-cluster-upgrade/</link><pubDate>Sun, 22 Jun 2025 13:25:06 +0900</pubDate><guid>https://blog.bumgu.com/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>현재 Kubeamd, Kubectl, Kubelet: 1.32.6&lt;/strong>&lt;br>
&lt;strong>업그레이드 할 Kubeamd, Kubectl, Kubelet: 1.33&lt;/strong>&lt;/p>
&lt;p>현재 운영중이라는 가정하에 간단한 API 서버를 실행중 업그레이드를 수행한다.&lt;/p>
&lt;p>Kubernetes yaml, 소스코드, Vagrantfile 등 &lt;a href="https://github.com/bonzonkim/kube-cluster-upgrade">Github&lt;/a>&lt;/p>
&lt;h1 id="현재">현재&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>위 명령어 실행 시 1.32.x 버전만 보인다면 apt 저장소 업데이트가 필요합니다.&lt;/p></description></item><item><title>Kafka를 이용한 Log Aggregation</title><link>https://blog.bumgu.com/post/2025/05/06/log-aggregation-with-kafka/</link><pubDate>Tue, 06 May 2025 15:52:28 +0900</pubDate><guid>https://blog.bumgu.com/post/2025/05/06/log-aggregation-with-kafka/</guid><description>&lt;p>Kafka는 MQ(Message Queue)다.&lt;br>
기존 로그 수집 파이프라인은&lt;br>
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/1.png" alt="image">
이렇게 간단했으나, 문제는 서버가 한두대가 아니라는 것이다.&lt;br>
모든 로그가 동시 다발적으로 VictoriaLogs에 로그를 Insert하게 되면서 병목현상이 생겨 데이터 처리 지연, 일부 로그 유실이 발생하였다.&lt;br>
그래서 로그 중앙 집중화를 위해 Log Aggregator로 Kafka를 도입하게 되었다.
대용량 로그를 빠르고 안정적으로 처리할 수 있으며, 장애 발생 시에도 로그 유실이 없어 데이터를 안전하게 보존할 수 있다. 그래서
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/2.png" alt="image">
이런 구조를 생각하게 되었으나&lt;br>
Kafka는 &lt;strong>AMQP와 달리 Consumer가 topic을 구독(sub)하여 소비하는 Pub/Sub모델이며 Consumer가 Pull하는 방식&lt;/strong>이기 때문에 아래와 같은 구조가 완성되었다.
&lt;img src="https://blog.bumgu.com/images/post/23-kafka-log-aggregator/3.png" alt="image">&lt;/p></description></item><item><title>Hugo를 사용해 블로그 만들기</title><link>https://blog.bumgu.com/post/2025/04/22/making_blog_with_hugo/</link><pubDate>Tue, 22 Apr 2025 20:42:28 +0900</pubDate><guid>https://blog.bumgu.com/post/2025/04/22/making_blog_with_hugo/</guid><description>&lt;p>마크다운이 익숙해서 &lt;a href="https://velog.io/@kellyb9/posts">Velog&lt;/a>를 블로그로 사용하다가 &lt;strong>Hugo + GitHub Pages&lt;/strong> 사용해 블로그를 호스팅했습니다.&lt;br>
배포했던 과정을 글로 남깁니다.&lt;/p>
&lt;h1 id="1-hugo">1. Hugo?&lt;/h1>
&lt;p>&lt;a href="https://gohugo.io/">Hugo&lt;/a>는 Go로 작성된 오픈소스 SSG(Static Site Generator) 입니다.&lt;br>
JS/TS 기반의 &lt;a href="https://astro.build/">Astro&lt;/a>를 사용할까 하다가 가벼운 Hugo를 선택했습니다.&lt;br>
Hugo는 내부적으로 &lt;code>theme-override&lt;/code>를 사용해 css와 html 커스텀이 가능하고, &lt;code>GoTemplate&lt;/code>를 사용해 &lt;code>if&lt;/code>문과 &lt;code>for&lt;/code>문같은 구현이 가능합니다.&lt;/p>
&lt;h1 id="2-hugo-설치">2. Hugo 설치&lt;/h1>
&lt;p>&lt;a href="https://gohugo.io/installation/">설치 공식문서&lt;/a>&lt;/p>
&lt;ul>
&lt;li>mac: &lt;code>brew install hugo&lt;/code>&lt;/li>
&lt;li>Linux: &lt;code>sudo snap install hugo&lt;/code>&lt;/li>
&lt;li>Windows
&lt;ul>
&lt;li>chocolately: &lt;code>choco install hugo-extended&lt;/code>&lt;/li>
&lt;li>scoop: &lt;code>scoop install hugo-extended&lt;/code>&lt;/li>
&lt;li>winget: &lt;code>winget install Hugo.Hugo.Extended&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Go로 설치도 가능합니다.&lt;/p></description></item><item><title>MCP(Model Context Protocol)를 사용해보자</title><link>https://blog.bumgu.com/post/2025/03/01/try_mcp/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2025/03/01/try_mcp/</guid><description>&lt;p>2월 27일 열린 Docker Community Day를 다녀왔습니다.
연사자분중 당근마켓 SRE 염근철님의 발표주제였는데요, 재밌는 주제인것 같아 간단하게 사용을 해보았습니다.&lt;/p>
&lt;hr>
&lt;h4 id="mcp란">MCP란?&lt;/h4>
&lt;blockquote>
&lt;p>Model Context Protocol(MCP)은 LLM(Large Language Model) 애플리케이션과 외부 데이터 소스 및 도구들 간의 원활한 통합을 가능하게 하는 개방형 프로토콜입니다. AI 기반 IDE를 구축하든, 채팅 인터페이스를 개선하든, 혹은 커스텀 AI 워크플로우를 만들든 관계없이, MCP는 LLM이 필요로 하는 컨텍스트와 연결하기 위한 표준화된 방법을 제공합니다.&lt;/p>
&lt;/blockquote>
&lt;h4 id="mcp-구조">MCP 구조&lt;/h4>
&lt;p>MCP follows a client-server architecture where:&lt;/p>
&lt;ul>
&lt;li>Hosts: LLM 애플리케이션 (Claude 데스크탑 혹은 IDE)&lt;/li>
&lt;li>Clients: 호스트 애플리케이션 안에서 서버와 1:1 연결을 유지합니다.&lt;/li>
&lt;li>Servers: 클라이언트에게 context, tools, prompts를 제공합니다.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/20-MCP/1.png" alt="">&lt;/p></description></item><item><title>Go - Functional Options 패턴</title><link>https://blog.bumgu.com/post/2025/02/16/go-functional-options/</link><pubDate>Sun, 16 Feb 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/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>위와 같이 서버를 실행하는 함수가 있습니다.
&lt;code>newServer()&lt;/code>를 호출할 때는 인자값으로 &lt;code>maxConn&lt;/code>, &lt;code>tls&lt;/code>, &lt;code>id&lt;/code>등을 &lt;strong>필수&lt;/strong>로 넘겨줘야 합니다.
지금은 3개밖에 안받으니 까먹을일 없겠지만 만약 인자가 10개, 20개라면 하나둘씩 까먹기 시작하고 실행에 실패할 것입니다.&lt;/p>
&lt;h3 id="opts-구조체">Opts 구조체&lt;/h3>
&lt;p>기존 &lt;code>Server&lt;/code> 구조체에 가지고 있던 속성들을 &lt;code>Opts&lt;/code>라는 구조체로 옮기고, &lt;code>Server&lt;/code>는 &lt;code>Opts&lt;/code>구조체를 가지겠습니다.&lt;/p></description></item><item><title>Etcd 모니터링 tls 에러 해결</title><link>https://blog.bumgu.com/post/2025/01/18/etcd_monitoring_tls_error/</link><pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2025/01/18/etcd_monitoring_tls_error/</guid><description>&lt;p>Server OS : Ubuntu 22.04
Kubernetes 구축환경 : 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-개요">1. 개요&lt;/h1>
&lt;p>Grafana etcd 대쉬보드가 Pod로 띄워지지 않고 쿠버네티스 서버에 직접 설치되어서 모니터링이 안되고 있던 상황
타겟에 &lt;code>&amp;lt;etcd-server&amp;gt;:2379/metrics&lt;/code>를 추가했으나 tls문제로 etcd에 접근하지 못해 수집이 안되고있었다.&lt;/p>
&lt;h3 id="2-etcd-실행-플래그-확인">2. etcd 실행 플래그 확인&lt;/h3>
&lt;p>&lt;code>etcd&lt;/code>가 실행되고있는 쿠버네티스 노드에 접근해서 &lt;code>ps aux | grep etcd&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>$ ps aux | grep etcd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root &lt;span style="color:#ae81ff">9330&lt;/span> 2.9 1.1 &lt;span style="color:#ae81ff">11494180&lt;/span> &lt;span style="color:#ae81ff">180124&lt;/span> ? Ssl &lt;span style="color:#ae81ff">2024&lt;/span> 3132:04 /usr/local/bin/etcd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root &lt;span style="color:#ae81ff">10329&lt;/span> 4.7 6.3 &lt;span style="color:#ae81ff">2480192&lt;/span> &lt;span style="color:#ae81ff">1041808&lt;/span> ? Ssl &lt;span style="color:#ae81ff">2024&lt;/span> 5166:20 kube-apiserver --advertise-address&lt;span style="color:#f92672">=&lt;/span>&amp;lt;server ip&amp;gt; --allow-privileged&lt;span style="color:#f92672">=&lt;/span>true --anonymous-auth&lt;span style="color:#f92672">=&lt;/span>True --apiserver-count&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span> --authorization-mode&lt;span style="color:#f92672">=&lt;/span>Node,RBAC --bind-address&lt;span style="color:#f92672">=&lt;/span>0.0.0.0 --client-ca-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/ca.crt --default-not-ready-toleration-seconds&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">300&lt;/span> --default-unreachable-toleration-seconds&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">300&lt;/span> --enable-admission-plugins&lt;span style="color:#f92672">=&lt;/span>NodeRestriction --enable-aggregator-routing&lt;span style="color:#f92672">=&lt;/span>False --enable-bootstrap-token-auth&lt;span style="color:#f92672">=&lt;/span>true --endpoint-reconciler-type&lt;span style="color:#f92672">=&lt;/span>lease --etcd-cafile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/ca.pem --etcd-certfile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/node-controlplane.pem --etcd-compaction-interval&lt;span style="color:#f92672">=&lt;/span>5m0s --etcd-keyfile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/node-controlplane-key.pem --etcd-servers&lt;span style="color:#f92672">=&lt;/span>https://&amp;lt;etcd cluster ip1&amp;gt;:2379,https://&amp;lt;etcd cluster ip2&amp;gt;:2379,https://&amp;lt;etcd cluster ip3&amp;gt;:2379 --event-ttl&lt;span style="color:#f92672">=&lt;/span>1h0m0s --kubelet-client-certificate&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/apiserver-kubelet-client.crt --kubelet-client-key&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/apiserver-kubelet-client.key --kubelet-preferred-address-types&lt;span style="color:#f92672">=&lt;/span>InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP --profiling&lt;span style="color:#f92672">=&lt;/span>False --proxy-client-cert-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/front-proxy-client.crt --proxy-client-key-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/front-proxy-client.key --request-timeout&lt;span style="color:#f92672">=&lt;/span>1m0s --requestheader-allowed-names&lt;span style="color:#f92672">=&lt;/span>front-proxy-client --requestheader-client-ca-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/front-proxy-ca.crt --requestheader-extra-headers-prefix&lt;span style="color:#f92672">=&lt;/span>X-Remote-Extra- --requestheader-group-headers&lt;span style="color:#f92672">=&lt;/span>X-Remote-Group --requestheader-username-headers&lt;span style="color:#f92672">=&lt;/span>X-Remote-User --secure-port&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">6443&lt;/span> --service-account-issuer&lt;span style="color:#f92672">=&lt;/span>https://kubernetes.default.svc.cluster.local --service-account-key-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/sa.pub --service-account-lookup&lt;span style="color:#f92672">=&lt;/span>True --service-account-signing-key-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/sa.key --service-cluster-ip-range&lt;span style="color:#f92672">=&lt;/span>10.233.0.0/18 --service-node-port-range&lt;span style="color:#f92672">=&lt;/span>30000-32767 --storage-backend&lt;span style="color:#f92672">=&lt;/span>etcd3 --tls-cert-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/apiserver.crt --tls-private-key-file&lt;span style="color:#f92672">=&lt;/span>/etc/kubernetes/ssl/apiserver.key
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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>--etcd-cafile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/ca.pem --etcd-certfile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/node-controlplane.pem --etcd-keyfile&lt;span style="color:#f92672">=&lt;/span>/etc/ssl/etcd/ssl/node-controlplane-key.pem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>이부분이 &lt;code>etcd&lt;/code>가 요구하는 인증서 정보이다.&lt;/p></description></item><item><title>Go - http.HandleFunc와 http.HandlerFunc의 차이</title><link>https://blog.bumgu.com/post/2025/01/07/http_handlefunc_handlerfunc/</link><pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/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>는 &lt;strong>함수&lt;/strong>입니다.&lt;/li>
&lt;li>특정 경로에 대해 요청을 처리할 핸들러를 등록합니다.&lt;/li>
&lt;li>등록된 함수는 &lt;code>func(http.ResponseWriter, *http.Request)&lt;/code>시그니처를 따릅니다.&lt;/li>
&lt;/ul>
&lt;h4 id="1-1-사용-예제">1-1. 사용 예제&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">// 응답 헤더 설정
&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">// 헤더에 Status코드 설정
&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">// &amp;#39;/&amp;#39; 경로에 핸들러 등록
&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: scrape target을 외부 secret으로 관리</title><link>https://blog.bumgu.com/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/post/2024/12/22/kube_prometheus_stack_scrape_target_with_secret/</guid><description>&lt;p>&lt;code>additionalScrapeConfigs&lt;/code> 에 직접 target을 작성하다보면 1000줄, 2000줄이 넘어가 &lt;code>values.yaml&lt;/code>파일이 길어져 관리가 힘들어집니다..&lt;/p>
&lt;p>그렇기 때문에 Scrape Targets을 외부 Secret으로 빼고 Secret을 참조하는방식으로 관리하도록 변경했습니다.&lt;/p>
&lt;h1 id="1-기존-scrapetarget을-파일로-생성">1. 기존 ScrapeTarget을 파일로 생성&lt;/h1>
&lt;p>기존에 &lt;code>values.yaml&lt;/code>의 &lt;code>prometheus.prometheusSpec.additionalScrapeConfigs&lt;/code>에 적혀있던 설정을 파일로 따로 작성합니다.&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-파일-기준-secret-manifest-생성">2. 파일 기준 Secret manifest 생성&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>
위 명령어는 방금 작성한 파일을 기준으로 쿠버네티스 시크릿을 생성할 수 있는 secret manifest yaml파일을 생성합니다.&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/post/2024/12/08/grafana_loki_error/</link><pubDate>Sun, 08 Dec 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/12/08/grafana_loki_error/</guid><description>&lt;p>Loki를 Kubernetes에 배포하고 Grafana에 Data Source로 연결해서 잘 사용하고 있었으나, Alerting rulers 페이지에서 &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>사용하고 있는 Loki Helm Chart는 &lt;a href="https://artifacthub.io/packages/helm/grafana/loki">여기&lt;/a> 의 6.22 버전을 사용하고 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="1-기존-values">1. 기존 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>는 Alerts 룰을 영구적으로 저장하는 디렉토리이기 때문에 읽기전용이고 클라이언트(Grafana 등)에서 생성한 룰은 &lt;code>rule_path&lt;/code>에 지정한 디렉토리에 임시로 저장되기 때문에 쓰기가 가능합니다. 그렇기 때문에 &lt;code>rule_path&lt;/code>를 마운트 해줘야 합니다.&lt;/p></description></item><item><title>Kubespray 사용 시 Metallb 설치</title><link>https://blog.bumgu.com/post/2024/09/22/kubespray_metallb_install/</link><pubDate>Sun, 22 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/09/22/kubespray_metallb_install/</guid><description>&lt;p>&lt;a href="https://blog.bumgu.com/post/2024/09/15/kubernetes_clustering_with_kubespray/">지난번 &lt;/a> &lt;code>kubespray&lt;/code> 와 &lt;code>ansible&lt;/code>을 활용해서 쿠버네티스 클러스터링을 할 때, 마지막 metallb 설치에 실패했는데, 오늘 설치해보겠습니다.&lt;/p>
&lt;h3 id="1-metallb-설치">1. MetalLB 설치&lt;/h3>
&lt;p>&lt;a href="https://metallb.universe.tf/installation/">공식문서&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>으로 설치합니다.&lt;/p>
&lt;h4 id="1-1-kube-proxy-configmap-수정">1-1. kube-proxy configMap 수정&lt;/h4>
&lt;p>&lt;code>MetalLB&lt;/code>는 IPVS 모드에서 ARP 응답을 엄격하게 제어하여 로드밸런싱을 수행합니다. 이 모드에서 strictARP를 활성화하면, kube-proxy는 노드가 할당된 IP에 대한 ARP 응답을 명확하게 지정된 노드에서만 처리하도록 합니다.&lt;/p>
&lt;p>&lt;code>kubectl edit cm -n kube-system kube-proxy&lt;/code>로 편집을 열고
&lt;code>strictARP: false&lt;/code> 부분을 &lt;code>strictARP: true&lt;/code>로 수정해줍니다.&lt;/p></description></item><item><title>Go와 GORM으로 DB연결해보기</title><link>https://blog.bumgu.com/post/2024/09/18/go_gorm_db/</link><pubDate>Wed, 18 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/09/18/go_gorm_db/</guid><description>&lt;p>최근 회사 서비스 어플리케이션 커스텀 Exporter를 만들기 위해 Go를 공부하고 있습니다.
Go진영 ORM인 &lt;code>GORM&lt;/code>으로 DB연결후 데이터 삽입까지 간단히 해봤습니다.&lt;/p>
&lt;hr>
&lt;h3 id="1-세팅">1. 세팅&lt;/h3>
&lt;h4 id="1-1-go-세팅">1-1. 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 // 프로젝트 초기화
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get -u gorm.io/gorm // gorm 패키지 다운
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get -u gorm.io/driver/mysql // mysql 드라이버 다운
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="1-2-docker로-mariadb-띄우기">1-2. Docker로 Mariadb 띄우기&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-db-연결">2. DB 연결&lt;/h3>
&lt;p>이제 GORM을 연결해 DB연결을 해보겠습니다.&lt;/p>
&lt;ul>
&lt;li>database 생성
&lt;code>create database test;&lt;/code>&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-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;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#e6db74">&amp;#34;log&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:#e6db74">&amp;#34;gorm.io/driver/mysql&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#e6db74">&amp;#34;gorm.io/gorm&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>&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">dsn&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;root@tcp(127.0.0.1:3306)/test&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:#a6e22e">db&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">gorm&lt;/span>.&lt;span style="color:#a6e22e">Open&lt;/span>(&lt;span style="color:#a6e22e">mysql&lt;/span>.&lt;span style="color:#a6e22e">Open&lt;/span>(&lt;span style="color:#a6e22e">dsn&lt;/span>), &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">gorm&lt;/span>.&lt;span style="color:#a6e22e">Config&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:#66d9ef">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#a6e22e">log&lt;/span>.&lt;span style="color:#a6e22e">Fatalf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Error opening GORM connection %v&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 style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Successfully connected GORM : &amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">db&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;/p></description></item><item><title>Kubespray로 k8s클러스터 구축</title><link>https://blog.bumgu.com/post/2024/09/15/kubernetes_clustering_with_kubespray/</link><pubDate>Sun, 15 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/09/15/kubernetes_clustering_with_kubespray/</guid><description>&lt;p>&lt;code>Ansible&lt;/code>, &lt;code>Kubespray&lt;/code>를 이용해 k8s클러스터 구축을 해보겠습니다.&lt;/p>
&lt;hr>
&lt;h3 id="1-vagrant를-사용해-vm-생성">1. Vagrant를 사용해 VM 생성&lt;/h3>
&lt;p>Vagrant 설정파일은 &lt;a href="https://github.com/techiescamp/kubernetes-certification-guide">kubernetes-certification-guide&lt;/a> 를 참고했습니다.&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>&lt;code>vagrant up&lt;/code>을 명령어를 통해 VM을 생성합니다.
생성이 된후엔 &lt;code>vagrant ssh &amp;lt;vm name&amp;gt;&lt;/code>으로 접속해서 확인해봅니다.
&lt;code>ssh vagrant@&amp;lt;ip&amp;gt;&lt;/code>로 접속해도 됩니다. &lt;code>vagrant&lt;/code>유저의 초기 비밀번호는 &lt;code>vagrant&lt;/code> 입니다.&lt;/p></description></item><item><title>Debian에서 k3s 클러스터 구축해보기</title><link>https://blog.bumgu.com/post/2024/06/30/debian%EC%97%90%EC%84%9C-k3s-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95%ED%95%B4%EB%B3%B4%EA%B8%B0/</link><pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/06/30/debian%EC%97%90%EC%84%9C-k3s-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95%ED%95%B4%EB%B3%B4%EA%B8%B0/</guid><description>&lt;p>회사에서 테스트용으로 구축했다가 어쩌다보니 현재 실사용하고있는 k8s클러스터(마스터 1, 워커 2) 서버가 우분투 데스크탑인데다가, 간헐적인 네트워크 끊김 문제가 생겨서 새로 구축할 생각이라 실습을 진행했습니다.&lt;/p>
&lt;p>&lt;a href="https://www.debian.org/releases/stable/debian-installer/">공식 페이지 iso 파일 다운로드&lt;/a>&lt;/p>
&lt;p>위 링크에서 netinst CD image 에서 원하는 아키텍쳐를 선택해 다운받고 vm에 설치했습니다.&lt;br>
&lt;code>로컬 환경 : Mac OS Sonoma 14.5 | Macbook Air M1 | 16G RAM&lt;/code>&lt;br>
&lt;code>vm 설치 환경 : Debian 12.6 | 2코어 | 4G RAM&lt;/code>&lt;/p>
&lt;hr>
&lt;h1 id="1-debian-설치">1. Debian 설치&lt;/h1>
&lt;p>Debian 은 처음이기에 나오는 설명들을 하나하나 읽으며 진행했습니다,
Software Installation 탭에서는 기본으로 체크되어있는
&lt;code>SSH server, standart system utilities&lt;/code>만 체크 한후 설치했고, 파티션은 &lt;code>/var, /tmp, /home&lt;/code>등으로 나누는 사항을 체크했습니다.&lt;/p></description></item><item><title>gmail OAuth2 토큰 발급(bun, elysia)</title><link>https://blog.bumgu.com/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/post/2024/06/19/gmail_oauth_with_bun_elysia/</guid><description>&lt;h1 id="1-bun이란">1. bun이란?&lt;/h1>
&lt;p>&lt;code>bun&lt;/code>은 &lt;code>node.js&lt;/code>와 같은 자바스크립트 런타임입니다. 자세한 내용과 설치는 &lt;a href="https://bun.sh/">bun.sh&lt;/a> 에서 확인 하실 수 있습니다. &lt;code>node.js&lt;/code>의 &lt;code>express&lt;/code>와 같은 &lt;code>elysia&lt;/code>라는 프레임워크를 사용해서 google gmail OAuth2토큰을 발급 받는 과정까지 포스팅 하도록 하겠습니다.&lt;/p>
&lt;h1 id="2-elysia-앱-생성">2. elysia 앱 생성&lt;/h1>
&lt;p>&lt;code>elysia&lt;/code>앱을 생성해보겠습니다 생성은
&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>&lt;code>src/index.ts&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-typescript" data-lang="typescript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">import&lt;/span> { &lt;span style="color:#a6e22e">Elysia&lt;/span> } &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#34;elysia&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">const&lt;/span> &lt;span style="color:#a6e22e">app&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#a6e22e">Elysia&lt;/span>().&lt;span style="color:#66d9ef">get&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;/&amp;#34;&lt;/span>, () &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Hello Elysia&amp;#34;&lt;/span>).&lt;span style="color:#a6e22e">listen&lt;/span>(&lt;span style="color:#ae81ff">3000&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:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">`🦊 Elysia is running at &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>&lt;span style="color:#a6e22e">app&lt;/span>.&lt;span style="color:#a6e22e">server&lt;/span>&lt;span style="color:#f92672">?&lt;/span>.&lt;span style="color:#a6e22e">hostname&lt;/span>&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">:&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>&lt;span style="color:#a6e22e">app&lt;/span>.&lt;span style="color:#a6e22e">server&lt;/span>&lt;span style="color:#f92672">?&lt;/span>.&lt;span style="color:#a6e22e">port&lt;/span>&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">`&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>3000번포트로 리스닝 중인것을 확인 할 수 있습니다.
&lt;code>bun run dev&lt;/code>명령어로 실행하고 &lt;code>localhost:3000&lt;/code>을 접속해보면
&lt;img src="https://velog.velcdn.com/images/kellyb9/post/4851b823-393e-42eb-a4bb-2a5f4ea91ab1/image.png" alt="">
이와 같이 작동중인것을 확인 할 수 있습니다.&lt;/p></description></item><item><title>AWS Summit Seoul 2024 후기</title><link>https://blog.bumgu.com/post/2024/05/18/aws_summit_seoul_2024/</link><pubDate>Sat, 18 May 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/05/18/aws_summit_seoul_2024/</guid><description>&lt;p>5월16일, 5월17일에 열린 AWS Summit Seoul 2024 에 다녀왔습니다.
저는 5월 17일만 다녀왔는데 다녀온 후기입니다&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>메일로 받은 QR코드를 찍고 입장하면 네임택을 줍니다. SRE, 데브옵스 그리고 시스템엔지니어까지 하고있지만 저는 시스템엔지니어로 role을 적어서 그렇게 나왔습니다.&lt;/p>
&lt;h1 id="930-키노트-연설">9:30 키노트 연설&lt;/h1>
&lt;p>9시 30분부터 키노트 연설이 시작됩니다. 3층의 오디토리움에서 진행되는데 영상과 AWS 수석테크 에반젤리스트, 인프랩 CTO 이동욱님, 센드버드 CTO 구정진님 그리고 AWS의 CTO Werner Vogels, Matt Wood의 연설을 들을 수 있었습니다.
키포인트로 잡은 것중 하나가 &lt;code>Frugal Architect&lt;/code>(검소한 아키텍트) 였습니다. 온프레미스 환경에 존재했던 하드웨어 제약을 없애고 클라우드 환경에서 구축이 쉬워지고, 확장성과 유연성이 높은만큼 비용이 생각 보다 클 수 있는데 그러한 점을 모두 고려해 제약조건을 둔 아키텍쳐를 &lt;code>Frugal Architecture&lt;/code>라고 합니다. 주니어 엔지니어로써 인지해야할 포인트라고 생각했습니다. (우리회사는 온프레미스환경이지만요)
&lt;img src="https://velog.velcdn.com/images/kellyb9/post/0528b37b-98ef-47f6-bfde-733187839b52/image.jpg" alt="">&lt;/p></description></item><item><title>온프레미스 Kubernetes 환경에서 nfs로 데이터 영속성(pv) 설정</title><link>https://blog.bumgu.com/post/2024/04/28/kubernetes_nfs_pv/</link><pubDate>Sun, 28 Apr 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/04/28/kubernetes_nfs_pv/</guid><description>&lt;h1 id="마스터-서버">마스터 서버&lt;/h1>
&lt;h2 id="1-마스터-서버-nfs-서버-설치">1. 마스터 서버 NFS 서버 설치&lt;/h2>
&lt;ul>
&lt;li>nfs (Network File System) 설치
&lt;code>apt-get install nfs-kernel-server&lt;/code>&lt;/li>
&lt;li>nfs 서버 기동
&lt;code>systemctl start nfs-server&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="2-공유-디렉토리-지정">2. 공유 디렉토리 지정&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>지정할 공유 디렉토리 생성
&lt;code>mkdir /data/nfsk8s&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>공유 디렉토리 지정
&lt;code>vim /etc/expoerts&lt;/code>
&lt;strong>맨 아래줄에 &amp;lt;디렉토리 경로&amp;gt; &amp;lt;\IP&amp;gt;(&amp;lt;권한&amp;gt;) 항목 추가&lt;/strong>
&lt;strong>권한 삽입 시 띄어쓰기 하지말것 (ro, rw) X → (ro,rw) O&lt;/strong>
&lt;strong>모든 IP에 대해 권한 적용&lt;/strong>
&lt;code>/data/nfsk8s *(rw,sync,no_root_squash)&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>이후 nfs 재시작
&lt;code>systemctl restart nfs-server&lt;/code>&lt;/p>
&lt;p>nfs 옵션&lt;/p></description></item><item><title>내가 겪은 Kubernetes 오류들</title><link>https://blog.bumgu.com/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/post/2024/04/25/kuberntes_errors_that_i_faced/</guid><description>&lt;p>모니터링 서버(prometheus, zabbix)를 구축하기 위해 온프레미스 환경에서 &lt;code>kubeadm&lt;/code>을 사용해 클러스터를 구성 할 때 제가 겪은 오류들을 정리합니다.&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>&lt;code>kubeadm init&lt;/code> 혹은 &lt;code>kubeadm join&lt;/code>을 할때 생기는 오류 입니다. 주로 이전에 사용하거나 잘못된 config를 사용할때 발생합니다.&lt;/p>
&lt;p>&lt;code>rm -rf ~/.kube&lt;/code>&lt;br>
&lt;code>mkdir ~/.kube&lt;/code>&lt;br>
&lt;code>cp -i /etc/kubernetes/admin.conf ~/.kube/config&lt;/code>&lt;br>
&lt;code>chown $(id -u):$(id -g) ~/.kube/config&lt;/code>&lt;/p></description></item><item><title>Docker - Ubuntu에 git 설치: 이미지 만들기</title><link>https://blog.bumgu.com/post/2024/03/18/docker-ubuntu-git-install/</link><pubDate>Mon, 18 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/03/18/docker-ubuntu-git-install/</guid><description>&lt;p>Docker를 이용해 git 이 설치된 우분투 이미지를 만들어보겠습니다&lt;/p>
&lt;h1 id="1-vanilia-ubuntu-실행">1. Vanilia Ubuntu 실행&lt;/h1>
&lt;p>아무것도 설치되어 있지 않은 우분투 이미지를 실행하겠습니다.&lt;/p>
&lt;p>&lt;code>docker run -it --name velog-docker ubuntu:latest&lt;/code>&lt;br>
&lt;code>velog-docker&lt;/code>라는 이름으로 만들고, ubuntu레포지터리의 latest태그를 사용해 이미지를 이용해 컨테이너를 실행한다는 뜻 입니다.&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>저는 이전에 실행해본적이 있어서 &lt;code>Already exists&lt;/code>가 떴습니다.
ubuntu에 접속해 bash 에 접속했습니다.
이후 &lt;code>git --version&lt;/code> 명령어를 실행하면 &lt;code>git&lt;/code>커맨드를 찾을 수 없다고 뜹니다. 아직 설치를 안했기 때문에 당연합니다.
&lt;img src="https://blog.bumgu.com/images/post/7-docker-ubuntu-git/1.png" alt="">&lt;/p></description></item><item><title>Minikube를 이용하여 쿠버네티스 실습해보기</title><link>https://blog.bumgu.com/post/2024/03/10/kubernetes-practice/</link><pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/03/10/kubernetes-practice/</guid><description>&lt;p>&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> 님의 글을 보고 실습을 진행 했습니다.&lt;/p>
&lt;p>&lt;code>minukube&lt;/code>와 &lt;code>kubectl&lt;/code>을 설치 했다면 바로 실습을 진행 할 수 있습니다.&lt;/p>
&lt;h2 id="minikube-란">Minikube 란&lt;/h2>
&lt;blockquote>
&lt;p>miniKube는 macOs, Linux 및 Windows에서 로컬 Kubernetes 클러스터를 빠르게 설정해주는 도구이다. 즉, miniKube를 이용하면 손쉽게 로컬에서 쿠버네티스 클러스터를 만들 수 있다. 심지어 여러 클러스터를 관리하는 것도 가능하다.
쿠버네티스 클러스터를 실행하려면 최소한 sceduler, controller, api-server, etcd, kubelet, kube-proxy를 설치해야 하고 필요에 따라 dns, ingress controller, storage class등을 설치해야 한다. 쿠버네티스는 설치 또한 중요한 과정이지만 처음 공부할땐 설치보단 실질적인 사용법을 익히는 게 중요하다.
이러한 설치를 쉽고 빠르게 하기 위한 도구가 minikube이다. minikube는 windows, macOs, linux에서 사용할 수 있고 다양한 가상 환경을 지원하여 대부분의 환경에서 문제없이 동작한다.
&lt;a href="https://velog.io/@yoojinjangjang/Minikube">출처&lt;/a>&lt;/p></description></item><item><title>React의 라우팅을 Next.js 처럼 - vite-plugin-next-react-router</title><link>https://blog.bumgu.com/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/post/2024/02/26/vite_plugin_next_react_router/</guid><description>&lt;p>강의를 듣다 &lt;code>vite-plugin-next-react-router&lt;/code>의 설치가 강의녹화 시점과 현재의 버전이 달라 동작이 안되는 에러가 발생했습니다. &lt;a href="https://github.com/zoubingwu/vite-plugin-next-react-router">공식깃허브&lt;/a>와 &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">블로그&lt;/a>를 참고하여 에러 해결을 진행했습니다.&lt;/p>
&lt;hr>
&lt;h1 id="1-vite-프로젝트-생성">1. vite 프로젝트 생성&lt;/h1>
&lt;p>&lt;code>yarn create vite&lt;/code>로 vite 프로젝트를 생성 합니다.&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>프로젝트명을 입력하고 상황에 맞게 선택하면 됩니다. 저는 React와 Typescript를 선택했습니다.&lt;/p></description></item><item><title>Next.js 프로젝트에 SSG 적용하기</title><link>https://blog.bumgu.com/post/2024/02/11/nextjs_ssg/</link><pubDate>Sun, 11 Feb 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/02/11/nextjs_ssg/</guid><description>&lt;p>제 &lt;a href="https://bumgu.com">홈페이지&lt;/a>는 next.js로 만들어져 있습니다.
여기의 &lt;code>Project&lt;/code>탭과 &lt;code>Career&lt;/code>탭을 작은 웹 페이지지만 SSG로 pre-rendering해보고 싶어서 하게 되었습니다.&lt;/p>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/5-nextjs-ssg/1.png" alt="">
이 페이지에 적용 해보겠습니다.&lt;/p>
&lt;ul>
&lt;li>개발환경
Next.js 13
Typescript&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="1-기존의-코드">1. 기존의 코드&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:#960050;background-color:#1e0010">설명&lt;/span> &lt;span style="color:#f92672">~~~&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:#960050;background-color:#1e0010">설명&lt;/span> &lt;span style="color:#f92672">~~~&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:#960050;background-color:#1e0010">설명&lt;/span> &lt;span style="color:#f92672">~~~&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>기존엔 이와 같이 컴포넌트에 props 값을 넘겨주고 렌더링 했습니다.&lt;/p></description></item><item><title>Spotify API 사용 (Node.js, express, React)</title><link>https://blog.bumgu.com/post/2024/01/31/spotify_api/</link><pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/01/31/spotify_api/</guid><description>&lt;p>&lt;a href="https://github.com/bonzonkim/spotify-ranking-app">깃허브 링크&lt;/a>&lt;/p>
&lt;p>원래 Next.js로 구현해보려 했으나 OAuth2.0기반 로그인도 처음 구현해보는데, Next.js의 숙련도도 부족해 Node.js + Express + React로 구현했습니다.&lt;/p>
&lt;h1 id="1-spotify의-로그인-방식">1. Spotify의 로그인 방식&lt;/h1>
&lt;p>Spotify 는 OAuth2.0 프레임워크를 사용합니다.&lt;/p>
&lt;p>&lt;img src="https://blog.bumgu.com/images/post/3-spotify_api/0.png" alt="">
먼저 API를 사용하기위해 필요한건&lt;/p>
&lt;ul>
&lt;li>&lt;code>code&lt;/code> : &lt;code>access_token&lt;/code>, &lt;code>refresh_token&lt;/code> 과 교환하기위함&lt;/li>
&lt;li>&lt;code>access_token&lt;/code> : 실제 정보를 받기 위해 필요한 토큰&lt;/li>
&lt;li>&lt;code>refresh_token&lt;/code> : &lt;code>access_token&lt;/code>이 만료되면 다시 받기위한 토큰&lt;/li>
&lt;li>&lt;code>client_id&lt;/code> : 스포티파이 디벨로퍼 앱의 식별번호 (개발자에게 부여)&lt;/li>
&lt;li>&lt;code>client_secret&lt;/code> : 스포티파이 디벨로퍼 앱의 시크릿 번호 (개발자에게 부여)&lt;/li>
&lt;/ul>
&lt;p>흐름은
1.&lt;code>client_secret&lt;/code>, &lt;code>client_id&lt;/code> (스포티파이 개발자 대쉬보드에서 발급)
2.&lt;code>code&lt;/code>발급
3.&lt;code>access_token&lt;/code>,&lt;code>refresh_token&lt;/code> 발급
4.&lt;code>access_token&lt;/code>을 이용해 API사용 가능
5.&lt;code>access_token&lt;/code>만료 시 &lt;code>refresh_token&lt;/code>을 이용해 다시 발급
이후 다시 4번 &amp;gt; 5번 &amp;gt; 4번 &amp;gt; 5번 &amp;hellip; 이 됩니다.&lt;/p></description></item><item><title>CSR, SSR의 데이터 패칭</title><link>https://blog.bumgu.com/post/2024/01/26/csr_ssr_data_fetching/</link><pubDate>Fri, 26 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/01/26/csr_ssr_data_fetching/</guid><description>&lt;p>개발하다보면 만나게 되는 &lt;code>CSR&lt;/code>, &lt;code>SSR&lt;/code> 이 두가지의 방식으로 next.js에서 get요청으로 json 데이터를 받아오는걸 해보겠습니다.&lt;/p>
&lt;h1 id="1-csr-ssr의-차이">1. CSR, SSR의 차이&lt;/h1>
&lt;h4 id="csr-client-side-rendering">CSR (Client-Side Rendering)&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>개념
웹의 초기 로딩시, 서버는 HTML과 기본적인 자바스크립트 코드만 클라이언트에게 보내며, 실제로 보이는 내용은 비어있는 상태.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>동작 방식
클라이언트에서 자바스크립트 코드가 실행되면, 클라이언트는 서버에 필요한 데이터를 요청해 받아온 후, 그 데이터를 이용해 동적으로 화면을 구성하고 렌더링함.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="ssrserver-side-rendering">SSR(Server-Side Rendering)&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>개념
웹의 초기 로딩시, 서버에서 초기 HTML을 완성하여 클라이언트에게 전달하므로, 초기 로딩 시 이미 완성된 HTML이 화면에 나타남.&lt;/p></description></item><item><title>Next.js getStaticProps()에 대해서</title><link>https://blog.bumgu.com/post/2024/01/23/getstaticprops/</link><pubDate>Tue, 23 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.bumgu.com/post/2024/01/23/getstaticprops/</guid><description>&lt;p>안녕하세요. Next.js의 특징중 하나인 pre-rendering에 대해 알아보겠습니다.&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>데이터 불러오기에 대해 이야기하기 전에 Next.js에서 가장 중요한 개념 중 하나에 대해 이야기해 보겠습니다: 프리 렌더링.
기본적으로 Next.js는 모든 페이지를 미리 렌더링합니다. 즉, 클라이언트 측 자바스크립트가 모든 작업을 수행하는 대신 Next.js가 각 페이지의 HTML을 미리 생성합니다. 사전 렌더링은 성능과 SEO를 개선할 수 있습니다.
생성된 각 HTML은 해당 페이지에 필요한 최소한의 자바스크립트 코드와 연결됩니다. 브라우저에서 페이지가 로드되면 해당 JavaScript 코드가 실행되어 페이지가 완전한 대화형 페이지로 만들어집니다. (이 프로세스를 하이드레이션이라고 합니다.)&lt;/p></description></item></channel></rss>