<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>berry_good.log</title>
        <link>https://velog.io/</link>
        <description>System Engineer의 발전기록</description>
        <lastBuildDate>Tue, 31 Mar 2026 11:35:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>berry_good.log</title>
            <url>https://velog.velcdn.com/images/berry_good/profile/89d5c989-3081-47f5-8995-219b5a7a0373/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. berry_good.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/berry_good" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Mobile-DNS 구축(feat. dnsmasq)]]></title>
            <link>https://velog.io/@berry_good/Mobile-DNS-%EA%B5%AC%EC%B6%95feat.-dnsmasq</link>
            <guid>https://velog.io/@berry_good/Mobile-DNS-%EA%B5%AC%EC%B6%95feat.-dnsmasq</guid>
            <pubDate>Tue, 31 Mar 2026 11:35:52 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>Mobile 환경 QA를 하다 보면 한 번쯤 꼭 부딪히는 문제가 있습니다.
바로 “모바일에서 staging 환경을 어떻게 자연스럽게 테스트할 것인가?” 입니다.</p>
</blockquote>
<h3 id="mobile-qa에서-겪는-현실적인-문제">Mobile QA에서 겪는 현실적인 문제</h3>
<p>PC 환경에서는 비교적 간단합니다.
<strong>/etc/hosts</strong> 파일만 수정하면 특정 도메인을 원하는 IP로 매핑해서, staging 서버로 트래픽을 보낼 수 있기 때문입니다.</p>
<p>하지만 모바일 환경은 이야기가 다릅니다.</p>
<ul>
<li>모바일 환경에서는 <strong>/etc/hosts</strong> 파일을 직접 수정하기 어려움</li>
<li>서비스 도메인을 IP로 직접 입력하거나 우회 방법을 써야 하는 번거로움 존재</li>
<li>실제 서비스 도메인을 기반으로 한 동작(쿠키, 인증 등)을 검증하기 어려움</li>
</ul>
<p>결국, “진짜 사용자 환경과 동일한 QA”를 하기가 쉽지 않습니다.</p>
<p>이 문제를 해결하기 위해 접근한 방식은 단순합니다.
모바일에서 hosts 수정은 어렵지만, Wi-Fi에 연결된 DNS 서버는 변경할 수 있습니다.</p>
<p>여기서 등장하는 게 바로 <strong>dnsmasq</strong> 입니다.</p>
<h3 id="구성-아이디어">구성 아이디어</h3>
<p>구성은 생각보다 단순합니다.</p>
<ol>
<li>간단한 DNS 서버(dnsmasq) 구축</li>
<li>특정 도메인을 staging IP로 강제 매핑</li>
<li>테스트용 Wi-Fi에서 해당 DNS를 사용하도록 설정</li>
</ol>
<p>이렇게 되면?</p>
<p>test.example.com → (DNS) → staging IP
🎯 목적</p>
<p>이 구조의 핵심 목적은 명확합니다.</p>
<p>모바일 테스트 환경에서도 실제 서비스 도메인을 그대로 사용
요청은 staging 서버로 전달
테스트 단말은 Wi-Fi 연결만으로 자동 세팅 완료</p>
<p>👉 즉,
<strong>“아무 설정 없이 QA 가능한 환경”</strong>을 만드는 것</p>
<h3 id="기대-효과">기대 효과</h3>
<p>이 방식의 장점은 꽤 강력합니다.</p>
<p>✅ 1. QA 준비 시간 단축
매번 IP 입력하거나 별도 설정할 필요 없음
Wi-Fi 연결만 하면 바로 테스트 가능
✅ 2. 실제 환경과 동일한 테스트
도메인을 그대로 사용
쿠키 / 인증 / 세션 / 리디렉션 정상 동작
✅ 3. 테스트 일관성 확보
QA 인원마다 환경 차이 없음</p>
<p>에뮬레이터나 IP 직접 입력 방식은 결국 한계가 있습니다.</p>
<p>하지만 DNS 기반으로 진행하면 가장 현실에 가까운 QA 환경을 만들 수 있어 간단하게 구축하는 방법을 소개하고자 합니다.</p>
<hr>
<h1 id="1-구성도">1. 구성도</h1>
<p><img src="https://velog.velcdn.com/images/berry_good/post/bf80d90b-2604-4161-82c5-e5ae5832f48a/image.png" alt=""></p>
<p>앞서 이야기 한 것처럼 생각보다 구축은 간단합니다.
테스트 DNS 서버에 dnsmasq 패키지를 설치한 후, QA 환경에 맞게 config 파일을 수정해주면 됩니다.</p>
<ul>
<li><strong>구축 환경</strong><ul>
<li>OS : Ubuntu 24.04</li>
<li>Dnsmasq : version 2.90 </li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-dns-서버-구축">2. DNS 서버 구축</h1>
<h2 id="21-dnsmasq-설치">2.1 dnsmasq 설치</h2>
<ul>
<li><strong>dnsmasq 설치</strong><pre><code class="language-bash">apt update
apt update dnsmasq</code></pre>
</li>
<li><strong>로컬 DNS Resolver 변경</strong></li>
</ul>
<p>대부분 리눅스에서 systemd-resolved는 기본으로 떠있고,</p>
<pre><code class="language-bash">127.0.0.53:53  ← systemd-resolved (stub resolver)</code></pre>
<p>이미 systemd-resolver가 53번 포트 점유하고 있어 dnsmasq로 Resolver를 변경합니다.</p>
<pre><code class="language-bash">vi /etc/systemd/resolved.conf

## resolved.conf 
#Cache=no-negative
#CacheFromLocalhost=no
DNSStubListener=no
#DNSStubListenerExtra=</code></pre>
<pre><code class="language-bash">rm /etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
systemctl restart systemd-resolved
systemctl restart dnsmasq</code></pre>
<ul>
<li><strong>서비스 체크</strong><pre><code class="language-bash">netstat -nltp</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/e977d557-0b1d-484e-8aea-84dc8ae8e482/image.png" alt=""></li>
</ul>
<h2 id="22-dnsmasq-설정-변경">2.2 dnsmasq 설정 변경</h2>
<ul>
<li><strong>config 설정 변경</strong><pre><code class="language-bash">vi /etc/dnsmasq.conf
</code></pre>
</li>
</ul>
<h2 id="dnsmasqconf">dnsmasq.conf</h2>
<p>interface=ens3 # 각 서버 네트워크 인터페이스에 맞게 조정
bind-interfaces
log-queries
log-facility=/var/log/dnsmasq.log
local-service</p>
<h1 id="상위-dns-서버-지정-dnsmasq가-모르는-도메인은-여기로-물어봄">상위 DNS 서버 지정 (dnsmasq가 모르는 도메인은 여기로 물어봄)</h1>
<p>server=1.1.1.1
server=8.8.8.8</p>
<h1 id="로컬-도메인-우선-참조">로컬 도메인 우선 참조</h1>
<p>domain-needed
bogus-priv</p>
<pre><code>
- **resolver 설정 변경**
``` bash
vi /etc/resolv.conf
nameserver 127.0.0.1
#nameserver 1.1.1.1 # 기존 nameserver 주석처리
search openstacklocal</code></pre><ul>
<li><strong>dnsmasq 재시작</strong><pre><code class="language-bash">systemctl restart dnsmasq.service
systemctl status dnsmasq.service</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/57e2d2af-5554-4328-81ae-4f9645515b74/image.png" alt=""></li>
</ul>
<hr>
<h1 id="3-dns-사용-방법">3. DNS 사용 방법</h1>
<ul>
<li><p><strong>host 설정</strong></p>
<ul>
<li>/etc/hosts 에서 host 설정 진행</li>
<li>CNAME으로 위임된 Domain은 CNAME까지 hosts 설정
<img src="https://velog.velcdn.com/images/berry_good/post/339139da-bc9f-439e-8ba2-ca771388d39b/image.png" alt=""></li>
</ul>
</li>
<li><p><strong>host 설정 후 dnsmasq 재시작</strong></p>
<pre><code class="language-bash">systemctl restart dnsmasq.service</code></pre>
</li>
</ul>
<h2 id="31-ios-wi-fi-설정">3.1 iOS Wi-Fi 설정</h2>
<ul>
<li><strong>Wi-Fi 연결 후 설정 진입</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/656972ed-0b4f-426a-9ab1-56f1d71457b6/image.png" alt=""></li>
<li><strong>DNS 구성 클릭</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/15231e08-3717-463b-afb8-be4386d87e8a/image.png" alt=""></li>
<li><strong>DNS: 수동, DNS 서버: DNS 서버 IP입력</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/f94b22bb-1a9a-4176-b933-27b97154b65d/image.png" alt=""></li>
</ul>
<h2 id="32-android-설정">3.2 Android 설정</h2>
<ul>
<li><strong>Wi-Fi 연결 후 더보기 열기</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/6e95bb34-dcef-4291-9037-06a2df05316d/image.png" alt=""></li>
<li><strong>DNS: 고정, DNS1: 서버 IP입력</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/a03758a0-a42c-4407-9908-8e40d417b22c/image.png" alt=""></li>
</ul>
<h2 id="33-정상-질의-확인">3.3 정상 질의 확인</h2>
<ul>
<li><strong>dnsmasq.log 확인</strong><pre><code class="language-bash">tail -f /var/log/dnsmasq.log</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/8f3979f7-4338-4f25-af64-b602dacfe474/image.png" alt=""></li>
</ul>
<hr>
<p>모바일 환경에서는 단순한 설정 하나도 제약이 많지만,
DNS를 활용하면 오히려 더 유연하고 강력한 테스트 환경을 구성할 수 있습니다.</p>
<p>이번 구성을 통해 Wi-Fi 연결만으로 staging 환경을 자연스럽게 검증할 수 있는 QA 환경을 만들 수 있었고, 실제 사용자와 동일한 조건에서 테스트할 수 있다는 점에서 큰 의미가 있었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] Single User Mode 부팅 방법]]></title>
            <link>https://velog.io/@berry_good/Linux-Single-User-Mode-%EB%B6%80%ED%8C%85-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@berry_good/Linux-Single-User-Mode-%EB%B6%80%ED%8C%85-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 03 Feb 2026 05:06:32 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>리눅스를 사용하며 root 비밀번호를 잃어버리거나 인스턴스(VM)이 정상적으로 부팅이 되지 않을 때가 있습니다. 저 역시 최근 PAM 정책 설정을 Ansible로 진행하다가 Script 오타로 인해 SSH 접근이 막혀 곤란했던 경험이 있습니다.
이런 곤란한 경험을 조금이라도 줄여주는 Single User Mode 부팅 방법을 설명드리겠습니다.</p>
</blockquote>
<h1 id="1-single-user-mode란">1. Single User Mode란?</h1>
<ul>
<li><p><strong>Single User Mode?</strong></p>
<ul>
<li>한 명의 사용자(root)만 접속 가능한 최소 환경으로 OS를 부팅하는 방식</li>
<li>네트워크, 대부분의 서비스, 데몬을 띄우지 않음</li>
<li>윈도우의 안전모드와 비슷한 방식</li>
<li>리눅스의 경우 <strong>GRUB</strong>라는 부트로더를 통해 부팅 진행</li>
<li><strong>runlevel 1</strong>로 실행</li>
</ul>
</li>
<li><p><strong>사용 사례?</strong></p>
<ul>
<li>root 비밀번호 분실</li>
<li>파일 시스템(fstab 등) 오류 복구</li>
<li>네트워크 없이 로컬에서 강제 복구</li>
</ul>
</li>
</ul>
<h1 id="2-grub부트로더란">2. GRUB(부트로더)란?</h1>
<p>간단하게 부트로더 및 서버 부팅 흐름에 대해 설명하겠습니다
(알면 좋음)</p>
<ul>
<li><strong>서버 부팅 흐름</strong>
  <img src="https://velog.velcdn.com/images/berry_good/post/b0a959ad-772d-4563-a033-21c40176055f/image.png" alt=""></li>
</ul>
<p>위에서 보이는 그림 중 Boot Loader에 대해 알아보고 가겠습니다~!</p>
<ul>
<li><strong>GRUB</strong>?<ul>
<li>Linux에서 가장 많이 쓰이는 부트로더</li>
<li>OS/커널 로딩<ul>
<li>디스크에서 Linux 커널과 initramfs(Initial RAM File System)를 직접 읽어 RAM에 적재</li>
</ul>
</li>
<li>커널 실행 후 제어권을 커널에 넘김</li>
</ul>
</li>
</ul>
<h1 id="3-single-mode-부팅-방법">3. Single Mode 부팅 방법</h1>
<p>위의 내용들을 잘 생각 하면서, Single Mode로 부팅하는 방법을 알아보겠습니다.</p>
<h2 id="31-grub-진입">3.1 GRUB 진입</h2>
<p>진입하는 방법은 OS에 따라 다르지만
ctrl + X, ESC, Shift 키 등 다양합니다.
전원을 넣은 즉시 연속적으로 클릭하여 GRUB에 진입합니다.
<img src="https://velog.velcdn.com/images/berry_good/post/aec27337-84c2-440f-8aa1-4d9961132fed/image.png" alt=""></p>
<h2 id="32-grub-kernel-parameter-수정">3.2 GRUB Kernel Parameter 수정</h2>
<p>부팅 할 OS 선택 후 <strong>E</strong> 키를 눌러 커널 진입
<img src="https://velog.velcdn.com/images/berry_good/post/2b12a929-b9e9-4c19-ad10-bc82e1b90f3e/image.png" alt="">
화면을 내리다 보면 해당 파라메터들이 보입니다. </p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/e195c7a5-f07e-452f-ab9f-cfd083f990a5/image.png" alt=""></p>
<h2 id="33-시스템-재시작">3.3 시스템 재시작</h2>
<p>ro 부분을 rw init=/bin/bash로 변경 이후 Ctrl + X를 눌러 재부팅을 진행해 줍니다.
<img src="https://velog.velcdn.com/images/berry_good/post/d4b6661c-d583-470a-a39d-d10ce3b61b23/image.png" alt="">
재부팅이 된다면 아래와 같이 Single Mode에서 패스워드 재시작 및 파일시스템 재마운트를 진행해주시면 됩니다.
<img src="https://velog.velcdn.com/images/berry_good/post/f21044fe-58e9-47c1-81f9-7c8c490e7089/image.png" alt=""></p>
<h2 id="34-간단한-방법recovery-mode">3.4 간단한 방법(Recovery Mode)</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/1f1604ce-61ee-43a8-a0a4-c18564dbe33f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/38a05bb3-dadc-4d89-b192-543963574ecd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/b0982c8b-24f6-45e8-b157-8f67e4f66170/image.png" alt="">
<img src="https://velog.velcdn.com/images/berry_good/post/e4336a09-ad75-49ff-9f01-6cb7c1bbd817/image.png" alt=""></p>
<h1 id="4-grub-보안">4. GRUB 보안</h1>
<blockquote>
<p>앞서 말씀드린 방법을 사용하여 AWS, GCP 등 CSP 계정을 탈취 당하였을때 서버 내부의 정보들이 해킹당할 위험이 있을텐데요 이 위험을 방지하기 위해 GRUB에 암호를 설정하여
비인가 사용자의 접근이나 커널 매개변수 조작을 방지할 수 있습니다.</p>
</blockquote>
<h2 id="41-grub-비밀번호-생성">4.1 GRUB 비밀번호 생성</h2>
<p>  <code>sudo grub-mkpasswd-pbkdf2</code>
    <img src="https://velog.velcdn.com/images/berry_good/post/06c9cc5c-dd16-475c-bd41-6e752a10b6fe/image.png" alt=""></p>
<p>패스워드 입력 후 결과 값을 <strong>복사</strong>해둡니다.</p>
<h2 id="42-grub-커스텀-설정-파일-수정">4.2 GRUB 커스텀 설정 파일 수정</h2>
<p><code>sudo vi /etc/grub.d/40_custom</code></p>
<pre><code class="language-bash">set superusers=&quot;admin&quot;
password_pbkdf2 admin [비밀번호_해시]</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/80f57be4-a14f-470d-8b71-994c49601774/image.png" alt=""></p>
<h2 id="43-설정-업데이트-및-적용">4.3 설정 업데이트 및 적용</h2>
<p><code>sudo update-grub</code>
<img src="https://velog.velcdn.com/images/berry_good/post/cce4befd-cc62-4176-905c-7d121e94cb08/image.png" alt=""></p>
<h2 id="44-설정-적용-확인">4.4 설정 적용 확인</h2>
<p>앞서 말씀드린대로 다시 재부팅을 실행하고 GRUB에 진입하려고 할 때 아래와 같이 username과 password를 입력하는 창이 나오게 됩니다.
<img src="https://velog.velcdn.com/images/berry_good/post/ad5710cd-30e1-49ee-800c-5aaf5b138bea/image.png" alt=""></p>
<h2 id="45-실무-적용">4.5 실무 적용</h2>
<blockquote>
<p>만약 위의 설정처럼 적용하게 된다면 서버가 오류로 인해 재부팅이 될 경우 GRUB 화면에서 멈춰 Kernel을 로드하지 못해 서버가 정상적으로 재부팅 되지 않을 것입니;다.
실무에서는 부팅 메뉴 편집 제한을 통해 GRUB 보안을 강화합니다.</p>
</blockquote>
<ul>
<li><p>설정 파일 수정</p>
<p><code>sudo vi /etc/grub.d/10_linux</code></p>
<pre><code class="language-bash"># 수정 전
CLASS=&quot;--class gnu-linux ...&quot;

# 수정 후
CLASS=&quot;--class gnu-linux --class gnu --class os --unrestricted&quot;</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/2af1e448-0e55-4e5d-b102-8b198c3792cf/image.png" alt=""></p>
<ul>
<li><p>설정 업데이트 및 적용</p>
<p><code>sudo update-grub</code></p>
</li>
</ul>
<p>해당 설정을 적용하게 된다면, GRUB 수정을 진행하려고 할 때 아까와 같이 super user의 ID와 PW를 요구하게 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CDN] Multi DRM이란?]]></title>
            <link>https://velog.io/@berry_good/CDN-Multi-DRM%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@berry_good/CDN-Multi-DRM%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Mon, 24 Nov 2025 12:29:56 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>이전 포스팅에서 잠깐 언급했던 DRM에 대해 이번 글에서 좀 더 자세히 소개해보려고 합니다!
요즘은 Netflix, Disney+, Coupang Play 등 다양한 OTT 서비스를 통해 디지털 콘텐츠를 시청하는데요, 이때 콘텐츠 보안은 매우 중요한 요소입니다. 콘텐츠가 유출될 경우 서비스 제공자뿐 아니라 제작사에도 큰 피해가 발생할 수 있기 때문이죠.
이러한 이유로 많은 기업들은 Multi DRM 솔루션을 도입해 콘텐츠 유출을 방지하고, 보다 안전하게 콘텐츠를 제공하고 있습니다.
이번 포스팅에서는 Multi DRM이 무엇인지, 그리고 어떤 역할을 하는지 쉽게 이해할 수 있도록 설명해드리겠습니다.</p>
</blockquote>
<h1 id="1-drm이란">1. DRM이란?</h1>
<ul>
<li><p><strong>DRM(Digital Rights Management)?</strong></p>
<ul>
<li>각종 디지털 콘텐츠의 불법적인 사용을 제한하고, 승인된 사용자의 콘텐츠 사용을 저작권 소유자의 의도에 따라 제어하는 기술</li>
<li>DRM은 콘텐츠를 암호화하고, 이용권(라이선스)을 인증된 사용자·기기에게만 발급해 합법적 범위 내에서만 재생 및 이용을 허용</li>
</ul>
</li>
<li><p><strong>DRM의 중요 기능</strong></p>
<ul>
<li><strong>접근 제어</strong>
인증된 사용자나 유효한 라이선스를 가진 경우에만 콘텐츠 접근을 허용</li>
<li><strong>복사 방지</strong>
콘텐츠 파일 자체를 무단으로 복사하거나 저장하는 것을 방지</li>
<li><strong>사용기간 / 횟수 제한</strong>
콘텐츠를 특정 기간 동안만 사용하게 하거나, 정해진 횟수만큼만 재생/열람할 수 있게 제한</li>
<li><strong>스크린샷 제한</strong>
화면 캡처(스크린샷) 기능을 막아 콘텐츠 유출을 방지</li>
<li><strong>워터마크/핑거프린트</strong>
콘텐츠에 사용자의 고유 식별 정보나 소유권 정보를 삽입하여 불법 유통 시 출처를 추적할 수 있게 함</li>
</ul>
</li>
<li><p><strong>DRM의 작동 방식</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/8f4899b5-4844-404a-9ec6-808ff5b64c01/image.png" alt=""></p>
</li>
</ul>
<p>위 그림은 DRM인증을 받는 플로우를 간단하게 Sequence 다이어그램으로 표현한 것입니다.
DRM 인증서버 중간에 Video Service(백엔드)가  결합되어 있다는 가정하에 설명드리겠습니다.</p>
<p><strong>1. 콘텐츠 패키징 및 암호화</strong></p>
<ul>
<li>원본 콘텐츠를 &quot;콘텐츠 키&quot;로 암호화 하여 키 없이는 내용을 볼 수 없게 만듭니다.</li>
</ul>
<p><strong>2. 사용자 인증 및 Token 요청</strong></p>
<ul>
<li>사용자가 콘텐츠 재생을 요청하면(재생 버튼 클릭) DRM 모듈을 통해 Video Service에 토큰 발급을 요청합니다.</li>
<li>이 때 사용자의 인증정보, 장치 정보 등 다양한 정보가 전송됩니다.</li>
</ul>
<p><strong>3. 라이선스 발급 및 전송</strong></p>
<ul>
<li>DRM 서버에서는 사용자 인증 정보 및 Token 정보를 확인하여 해당 사용자, Token이 유효한지 판단하고 유효하다면 라이선스를 발급해줍니다.</li>
</ul>
<p><strong>4. 복호화 및 콘텐츠 재생</strong></p>
<ul>
<li>사용자는 발급받은 라이선스를 이용하여 암호화된 콘텐츠를 복호화 하고, 콘텐츠를 재생합니다.</li>
</ul>
<p>이렇게 4단계로 암호화된 콘텐츠를 복호화 하여 콘텐츠를 재생 합니다.</p>
<hr>
<h1 id="2-multi-drm이란">2. Multi DRM이란?</h1>
<p>앞서 말씀드린것처럼 복잡한 암호화 / 복호화 과정을 거쳐야만 콘텐츠를 재생할 수 있는 DRM은 운영체제(Android, iOS 등)와 브라우저(MS Edge, Chrome 등)마다 지원 방식이 서로 다릅니다.
또한 휴대폰, PC, 태블릿, TV 등 재생 환경이 매우 다양하기 때문에, 모든 기기에서 안정적으로 콘텐츠를 보호하고 재생하기 위해 <strong>Multi DRM</strong> 솔루션을 사용하는 것이 일반적입니다.</p>
<ul>
<li><p><strong>Multi DRM</strong>?</p>
<ul>
<li>Multi-DRM은 하나의 서비스에서 Widevine, PlayReady, FairPlay 같은 서로 다른 DRM들을 한꺼번에 관리·제공해서, 어떤 기기·브라우저에서도 동일한 콘텐츠를 안전하게 재생할 수 있게 만드는 통합 DRM 시스템</li>
</ul>
</li>
<li><p><strong>Multi DRM 솔루션 종류</strong></p>
<ul>
<li><strong>Google Widevine</strong>: 안드로이드 및 크롬 브라우저에서 사용</li>
<li><strong>Apple FairPlay</strong>: iOS 및 Safari 브라우저에서 사용</li>
<li><strong>Microsoft PlayReady</strong>: 윈도우 및 엣지 브라우저에서 사용</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/406d9faa-05a9-4968-9e63-b2578157ee1a/image.png" alt=""></p>
<ul>
<li><p><strong>Multi DRM의 장점</strong></p>
<ul>
<li>다양한 기기·브라우저에 대한 광범위한 호환성 확보</li>
<li>콘텐츠 보안 정책의 통합 관리</li>
<li>빠른 기술 적용 및 확장성</li>
<li>관리·운영·모니터링의 단일화</li>
</ul>
</li>
<li><p><strong>Multi DRM의 지원환경</strong>
<a href="https://docs.doverunner.com/ko/content-security/multi-drm/getting-started/supported-env/">DoveRunner / Multi DRM 지원환경</a></p>
</li>
</ul>
<p>멀티 DRM의 지원 환경은 위 페이지에서 확인 가능합니다(DoveRunner사)</p>
<hr>
<h1 id="3--multi-drm의-간단한-예제">3.  Multi DRM의 간단한 예제</h1>
<p>지금까지 Multi DRM에 대해 알아보았으니 간단하게 DRM 인증 과정 예제를 보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/cf175d47-1219-40d2-9be3-101bce2ce89b/image.png" alt=""></p>
<p>위 그림은 Multi DRM(DoveRunner 사)이 적용된 샘플 예제입니다.</p>
<p><strong>1. 플레이어(Client)에서 Token 발급 요청 → manifest.mpd</strong>
사용자는 Video Service(백엔드)로 Token 발급을 요청합니다.
(manifest.mpd)
해당 파일 안에는 </p>
<blockquote>
<p>cenc(여러 DRM을 공통 포맷으로 암호화하는 표준)
해상도 값
DRM 정보 등이 존재합니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/berry_good/post/a48542ae-fe5d-4b0c-a327-8a82c563fea0/image.png" alt="">
<strong>2. 라이선스 키 요청 → LicenseManager.do</strong>
발급받은 토큰을 활용하여 DRM Server에 라이선스를 요청합니다.</p>
<p><strong>3. 영상 재생 → mpd.m4s</strong> 
마지막으로 실제 영상 데이터가 담긴 비디오, 오디오 Segments를 재생합니다.</p>
<hr>
<p>이렇게 DRM, Multi DRM에 대해 알아보았는데요
DRM과 Multi DRM은 단순한 암호화 기술을 넘어, 다양한 기기와 플랫폼에서 콘텐츠를 안전하게 보호하고 원활하게 재생할 수 있도록 하는 필수 기능입니다. 앞으로도 스트리밍 환경과 기기 다양성이 늘어날수록, Multi DRM의 중요성은 계속 커질 것입니다.</p>
<hr>
<h3 id="참고문헌">참고문헌</h3>
<p><a href="https://doverunner.com/kr/blogs/drm-introduction-application-guide/">https://doverunner.com/kr/blogs/drm-introduction-application-guide/</a>
<a href="https://docs.doverunner.com/ko/content-security/multi-drm/license/license-token/">https://docs.doverunner.com/ko/content-security/multi-drm/license/license-token/</a>
<a href="https://docs.doverunner.com/ko/content-security/multi-drm/getting-started/supported-env/">https://docs.doverunner.com/ko/content-security/multi-drm/getting-started/supported-env/</a>
<a href="https://www.cdnetworks.com/ko/glossary/digital-rights-management/">https://www.cdnetworks.com/ko/glossary/digital-rights-management/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[VPN 보안 및 TCP 연동(feat. openvpn) ]]></title>
            <link>https://velog.io/@berry_good/VPN-%EB%B3%B4%EC%95%88-%EB%B0%8F-TCP-%EC%97%B0%EB%8F%99feat.-openvpn</link>
            <guid>https://velog.io/@berry_good/VPN-%EB%B3%B4%EC%95%88-%EB%B0%8F-TCP-%EC%97%B0%EB%8F%99feat.-openvpn</guid>
            <pubDate>Mon, 17 Nov 2025 11:10:26 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>VPN 운영 중 다음과 같은 애로사항이 있었습니다.</p>
</blockquote>
<ol>
<li>접속 로그는 남지만 실시간 접속 현황을 확인하기 어려움</li>
<li>UDP Inbound/Outbound가 차단된 환경에서는 VPN 사용이 어려움
이러한 문제를 해결하기 위해 기존 VPN에 TCP 연동을 추가하고, 보안 강화를 위해 
접속 알림 기능을 구성하였습니다.</li>
</ol>
<blockquote>
<p>해당 글은 VPN 환경이 구성되었다는 전제 하에 작성된 글이기 때문에 
VPN 구축에 대해 궁금하신 분들은 아래 글에서 구축 방법을 확인하고 오시는것도 도움이 될 것 같습니다!</p>
</blockquote>
<blockquote>
<p><a href="https://velog.io/@berry_good/VPN-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95feat.-openvpn">VPN 서버 구축(feat. openvpn)</a></p>
</blockquote>
<h1 id="1-vpn-tcp-연동">1. VPN TCP 연동</h1>
<ul>
<li><p><strong>구축 배경</strong></p>
<ul>
<li>일부 네트워크망(Wi-Fi, 관공서망, 해외망 등)에서 VPN 접속이 불가능한 사례 존재</li>
<li>OpenVPN의 기본 통신 프로토콜은 UDP이기 때문에, UDP 차단 환경에서는 연결이 이루어지지 않음</li>
<li>따라서 TCP 연결을 구현하여 UDP가 차단되는 일부 망에서 TCP를 통한 VPN 연결을 지속하고자 함</li>
</ul>
</li>
<li><p>VPN TCP config 파일 생성</p>
<pre><code class="language-bash">cd /etc/openvpn/server
cp server.conf server-tcp.conf</code></pre>
</li>
<li><p>server-tcp.conf 수정</p>
<pre><code class="language-bash"># server-tcp.conf
# 포트변경
port 443

# TCP 연결
proto tcp
;proto udp

#가상 네트워크 인터페이스 변경
dev tun1

# TCP, UDP 서브네팅
server 10.9.0.0 255.255.255.0

# TCP, UDP 로깅 변경
status /var/log/openvpn/openvpn-status-tcp.log

# By default, log messages will go to the syslog (or
# on Windows, if running as a service, they will go to
# the &quot;\Program Files\OpenVPN\log&quot; directory).
# Use log or log-append to override this default.
# &quot;log&quot; will truncate the log file on OpenVPN startup,
# while &quot;log-append&quot; will append to it.  Use one
# or the other (but not both).
log         /var/log/openvpn/openvpn-tcp.log
log-append  /var/log/openvpn/openvpn-tcp.log

# UDP에서 사용하는 옵션 제거
;explicit-exit-notify 1</code></pre>
</li>
<li><p>systemctl을 이용하여 VPN-TCP 서비스 등록 및 서비스 시작</p>
<pre><code class="language-bash">systemctl enable openvpn-server@server-tcp
systemctl start openvpn-server@server-tcp.service
systemctl status openvpn-server@server-tcp.service
</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/f7d1fc51-6d79-4748-9f6f-13d4b11c22a5/image.png" alt="">
서비스 Active 확인
<img src="https://velog.velcdn.com/images/berry_good/post/d9ec29ab-56a9-40b0-9abe-8909e494b211/image.png" alt=""></p>
</li>
<li><p>TCP, UDP 포트 확인</p>
<pre><code class="language-bash">netstat -nlutp</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/5d325322-9049-40b5-bef7-6326ef9e7602/image.png" alt=""></p>
</li>
<li><p>TCP 프로토콜 전용 .ovpn 생성</p>
<ul>
<li>TCP로 통신하기 위해서는 새로운 .ovpn 파일이 필요</li>
<li>기존 base.conf 파일을 cp해 새로운 config 파일 생성</li>
</ul>
<pre><code class="language-bash">cp base.conf base-tcp.conf</code></pre>
<ul>
<li>base-tcp.conf<pre><code class="language-bash">;dev tap
dev tun1
# Windows needs the TAP-Win32 adapter name
# from the Network Connections panel
# if you have more than one.  On XP SP2,
# you may need to disable the firewall
# for the TAP adapter.
;dev-node MyTap
</code></pre>
</li>
</ul>
<h1 id="are-we-connecting-to-a-tcp-or">Are we connecting to a TCP or</h1>
<h1 id="udp-server--use-the-same-setting-as">UDP server?  Use the same setting as</h1>
<h1 id="on-the-server">on the server.</h1>
<p>proto tcp
;proto udp</p>
<h1 id="the-hostnameip-and-port-of-the-server">The hostname/IP and port of the server.</h1>
<h1 id="you-can-have-multiple-remote-entries">You can have multiple remote entries</h1>
<h1 id="to-load-balance-between-the-servers">to load balance between the servers.</h1>
<p>remote my-server-2 443    #서버 IP 입력
;remote my-server-2 1194</p>
<pre><code>


</code></pre></li>
</ul>
<h1 id="2-vpn-로그인-알림-설정with-slack">2. VPN 로그인 알림 설정(with Slack)</h1>
<ul>
<li><strong>구축 배경</strong><ul>
<li>보안 강화 및 무단 접속 탐지</li>
<li>접속 이력의 투명성 및 감사 로그 확보</li>
<li>운영 및 장애 대응 편의성</li>
</ul>
</li>
</ul>
<blockquote>
<p>해당 설정은 OpenVPN에서 기본적으로 제공하는 OpenVPN Client Script Hooks 기능을 사용하여 구성하였습니다.</p>
</blockquote>
<ul>
<li><p>Client 접속 성공 시 알림 스크립트 작성</p>
<pre><code class="language-bash">#!/bin/bash

# ===============================================
# FileName: client-connect.sh
# Description: VPN 접속 시 user,ip,protocol,time 등의 정보를
# Slack에 전송해주는 스크립트
# server.conf, server-tcp.conf 에서 해당 스크립트 실행
# Author: Chanyoung, Han
# ver: 1.0.0
# Last Modified: 2025-11-07
# ===============================================

SLACK_WEBHOOK_URL=&quot;WEBHOOKS URL&quot;
USER=&quot;$common_name&quot;
IP=&quot;$ifconfig_pool_remote_ip&quot;
SRC_IP=&quot;$trusted_ip&quot;
PROTO=&quot;$PROTO&quot;
TIME=&quot;$(date &#39;+%Y-%m-%d %H:%M:%S&#39;)&quot;

MESSAGE=&quot;🟢  *VPN Connected*  
*User:* ${USER}  
*Protocol:* ${PROTO}
*VPN IP:* ${IP}  
*Source IP:* ${SRC_IP}  
*Time:* ${TIME}&quot;

PAYLOAD=$(cat &lt;&lt;EOF
    {
    &quot;channel&quot;: &quot;#channel_name&quot;,
    &quot;username&quot;: &quot;VPN&quot;,
    &quot;text&quot;: &quot;${MESSAGE}&quot;,
    &quot;icon_emoji&quot;: &quot;:openvpn:&quot;
    }
EOF
)

curl -X POST -H &quot;Content-type: application/json&quot; --data &quot;$PAYLOAD&quot; &quot;$SLACK_WEBHOOK_URL&quot;
</code></pre>
</li>
<li><p>Client 접속 해제 시 알림 스크립트 작성</p>
<pre><code class="language-bash">#!/bin/bash

# ===============================================
# FileName: client-disconnect.sh
# Description: VPN 접속해제 시 user,ip,protocol,time 등의 정보를
# Slack에 전송해주는 스크립트
# server.conf, server-tcp.conf 에서 해당 스크립트 실행
# Author: Chanyoung, Han
# ver: 1.0.0
# Last Modified: 2025-11-07
# ===============================================

SLACK_WEBHOOK_URL=&quot;WEBHOOKS URL&quot;
USER=&quot;$common_name&quot;
IP=&quot;$ifconfig_pool_remote_ip&quot;
SRC_IP=&quot;$trusted_ip&quot;
PROTO=&quot;$PROTO&quot;
TIME=&quot;$(date &#39;+%Y-%m-%d %H:%M:%S&#39;)&quot;

MESSAGE=&quot;🔴 *VPN DisConnected*  
*User:* ${USER}  
*Protocol:* ${PROTO}
*VPN IP:* ${IP}  
*Source IP:* ${SRC_IP}  
*Time:* ${TIME}&quot;

PAYLOAD=$(cat &lt;&lt;EOF
    {
    &quot;channel&quot;: &quot;#channel_name&quot;,
    &quot;username&quot;: &quot;VPN&quot;,
    &quot;text&quot;: &quot;${MESSAGE}&quot;,
    &quot;icon_emoji&quot;: &quot;:openvpn:&quot;
    }
EOF
)

curl -X POST -H &quot;Content-type: application/json&quot; --data &quot;$PAYLOAD&quot; &quot;$SLACK_WEBHOOK_URL&quot;</code></pre>
</li>
<li><p>Scripts 권한 부여</p>
<pre><code class="language-bash">chmod +x client-connect.sh
chmod +x client-disconnect.sh</code></pre>
</li>
<li><p>server.conf 변경</p>
<ul>
<li>해당 내용 추가 후 저장<pre><code class="language-bash"># VPN Login Configs
client-connect /etc/openvpn/client-connect.sh
client-disconnect /etc/openvpn/client-disconnect.sh
script-security 2   # 보안 Level 조정(Scripts 실행 허용)
setenv PROTO UDP    # TCP일때는 PROTO TCP
</code></pre>
</li>
</ul>
<pre><code></code></pre></li>
<li><p>시스템 재시작</p>
<pre><code class="language-bash">systemctl restart openvpn-server@server.service</code></pre>
</li>
<li><p>알림 결과
<img src="https://velog.velcdn.com/images/berry_good/post/3b511099-ad3a-4e7a-8fef-79e81d3c1cbb/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CDN] GPU 트랜스코딩에 하드웨어 가속 얹어보기(feat. NVIDIA) - 2탄]]></title>
            <link>https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA-2%ED%83%84</link>
            <guid>https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA-2%ED%83%84</guid>
            <pubDate>Mon, 29 Sep 2025 11:45:28 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>저번 1탄에서는 트랜스코딩의 기본 개념과 테스트 설계에 대해 설명해 보았습니다.
이번 2탄에는 실제 서버에서 트랜스코딩 환경을 구축해보고 테스트 결과를 보겠습니다.
트랜스코딩에 대해 궁금하신 분은 아래 링크를 통해 내용을 보고 오시는 것을 추천드립니다!</p>
</blockquote>
<blockquote>
<p><a href="https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA">GPU 트랜스코딩에 하드웨어 가속 얹어보기(feat. NVIDIA) - 1탄</a></p>
</blockquote>
<h1 id="1-트랜스코딩-환경-구축">1. 트랜스코딩 환경 구축</h1>
<ul>
<li><strong>구축 환경</strong><ul>
<li>OS : Ubuntu 22.04</li>
<li>GPU : Quadro RTX 4000(Turing 아키텍처)</li>
<li>ffmpeg : 4.4.2</li>
</ul>
</li>
</ul>
<h2 id="11-nvidia-driver-설치">1.1 NVIDIA-Driver 설치</h2>
<p><strong>NVIDIA Driver란?</strong> 
운영체제(OS)와 GPU 하드웨어를 연결해주는 소프트웨어
GPU를 인식하고, 연산·그래픽·인코딩 같은 기능을 사용할 수 있게 해준다</p>
<ul>
<li>기존 NVIDIA Driver 삭제<pre><code class="language-bash">apt purge &#39;^nvidia-.*&#39;
apt autoremove --purge
apt clean</code></pre>
</li>
<li>NVIDIA Driver 설치<pre><code class="language-bash">apt install ubuntu-drivers-common
ubuntu-drivers devices</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/a3b01d57-b4ac-4378-b32b-62a8cd99d112/image.png" alt="">
  명령어 이후 나오는 <strong>recommended</strong>를 설치하시면 됩니다!<pre><code class="language-bash">  apt-get install nvidia-driver-575
  nvidia-smi</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/35cb2593-0fa1-4710-a440-26328f4e969c/image.png" alt=""></li>
</ul>
<h2 id="12-cuda-toolkit-설치">1.2 CUDA Toolkit 설치</h2>
<p><strong>CUDA Toolkit 이란?</strong>
NVIDIA GPU를 활용해 병렬 계산을 할 수 있게 해주는 플랫폼 &amp; 개발 툴킷.
인코딩, 디코딩에는 쓰이지 않지만, FFmpeg CUDA 필터(scale_cuda, hwupload_cuda 등 GPU 연산용 필터)를 쓰려면 CUDA Toolkit이 필요</p>
<ul>
<li><p>NVIDIA Driver와 호환되는 Cuda 확인
<a href="https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html">Cuda 호환성 확인</a>
<img src="https://velog.velcdn.com/images/berry_good/post/2fbf0733-4f2c-4479-abf7-d964b87e23a0/image.png" alt="">
위에 보이시는것처럼 제 NVIDIA-Driver는 575.57.08이기 때문에
CUDA 12.9 Update 1을 설치 진행하겠습니다.</p>
</li>
<li><p>CUDA 설치 진행
<a href="https://developer.nvidia.com/cuda-toolkit-archive">CUDA ToolKit Archive</a></p>
<ul>
<li><p>OS, Architecture 등 자신의 환경에 맞는 CUDA 선택
<img src="https://velog.velcdn.com/images/berry_good/post/d317128f-b420-4e66-9bbf-826b2e6107ec/image.png" alt=""></p>
</li>
<li><p>하기 명령어대로 설치 진행
<img src="https://velog.velcdn.com/images/berry_good/post/ea366d72-411f-41a4-ae27-04aa7b59e2ad/image.png" alt=""></p>
<pre><code class="language-bash">wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda-repo-ubuntu2204-12-9-local_12.9.1-575.57.08-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2204-12-9-local_12.9.1-575.57.08-1_amd64.deb
sudo cp /var/cuda-repo-ubuntu2204-12-9-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-9</code></pre>
</li>
<li><p>환경변수 추가 및 저장</p>
<pre><code class="language-bash">vi /etc/profile
</code></pre>
</li>
</ul>
<p>export PATH=$PATH:/usr/local/cuda/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64
export CUDADIR=/usr/local/cuda</p>
<h1 id="환경변수-저장">환경변수 저장</h1>
<p>source /etc/profile</p>
<pre><code>- CUDA 설치 확인
```bash
nvcc -V</code></pre><p><img src="https://velog.velcdn.com/images/berry_good/post/1c0172a3-38b4-4da9-b488-fdcdc4922147/image.png" alt=""></p>
</li>
</ul>
<h2 id="13-nv-codec-headers-설치">1.3 nv-codec-headers 설치</h2>
<p><strong>nv-codec-headers 이란?</strong>
FFmpeg이 NVENC/NVDEC API를 호출할 수 있게 해주는 헤더 파일 모음</p>
<ul>
<li>nv-codec-headers 설치 진행 <pre><code class="language-bash">git clone https://github.com/FFmpeg/nv-codec-headers.git
cd nv-codec-headers
git checkout n11.1.5.1
make &amp;&amp; sudo make install</code></pre>
</li>
</ul>
<p>왜 최신 버전이 아니라 11.1.5.1로 설치하는가? 라고 말하면 ffmpeg 4.4.2 에 맞는 header가 저것이기 때문... 12버전으로 설치하는 순간 바로 ERROR 로그가 우수수....</p>
<hr>
<p>여기까지가 ffmpeg을 설치하기 위한 사전 과정이였습니다.</p>
<p>이쯤에서 느끼는.. 패키지 매니저의 중요성....
apt install만 하면 설치되던것이 버전 맞추고 설정 해야하고... 생각보다 중요한 부분이였네요 ^^,,</p>
<hr>
<h2 id="14-ffmpeg-설치442">1.4 ffmpeg 설치(4.4.2)</h2>
<p>Ubuntu 22에서 공식적으로 지원하는 ffmpeg은 4.4.2 입니다.
ffmpeg은 7버전까지 나왔지만 디코딩, 스케일링 가속은 4.4.2 버전에서도 가능해서 OS가 지원하는 최고 버전으로 설치 진행하였습니다.</p>
<p>추후 언급될 <strong>scale_npp</strong> 옵션을 사용하기 위해 github에서 소스를 다운받은 뒤 직접 컴파일 하여 ffmpeg을 사용하겠습니다.
<a href="https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/ffmpeg-with-nvidia-gpu/index.html">ffmpeg 하드웨어 가속 참고문서</a>
<a href="https://ffmpeg.org/index.html#news">ffmpeg 업데이트 News</a></p>
<ul>
<li>필수 패키지 설치<pre><code class="language-bash">sudo apt-get install build-essential yasm cmake libtool libc6 libc6-dev unzip wget libnuma1 libnuma-dev</code></pre>
</li>
<li>ffmpeg git repo 다운로드<pre><code class="language-bash">git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg/
cd ffmpeg
git checkout n4.4.2</code></pre>
</li>
<li>ffmpeg Configure<pre><code class="language-bash">./configure \
  --enable-nonfree \
  --enable-nvenc \
  --enable-cuda-nvcc \
  --enable-libnpp \
  --extra-cflags=&quot;-I/usr/local/cuda/include -I/usr/local/include&quot; \
  --extra-ldflags=-L/usr/local/cuda/lib64 \
  --nvccflags=&quot;-gencode arch=compute_75,code=sm_75&quot; \
  --enable-gpl \
  --enable-libass \
  --enable-libbluray \
  --enable-libfontconfig \
  --enable-libfribidi \
  --enable-libfreetype \
  --enable-libvidstab \
  --enable-libx264 \
  --enable-libx265 \
  --enable-libvpx \
  --enable-libfdk-aac \
  --enable-libopus \
  --enable-libmp3lame \
  --enable-libxvid \
  --enable-libzmq \
  --enable-libzimg \
  --enable-libaom \
  --enable-libshine \
  --enable-libopenjpeg \
  --enable-libwebp \
  --enable-libsoxr \
  --enable-libspeex \
  --enable-libopenh264 \
  --enable-libmfx</code></pre>
</li>
<li>ffmpeg 컴파일<pre><code class="language-bash">make -j 8</code></pre>
</li>
<li>ffmpeg 설치<pre><code class="language-bash">sudo make install</code></pre>
</li>
<li>ffmpeg 버전 확인<pre><code class="language-bash">ffmpeg -version</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/3a00afec-5caf-4722-8f61-e3432c2aae43/image.png" alt=""></li>
</ul>
<hr>
<h1 id="2-gpu-트랜스코딩-테스트">2. GPU 트랜스코딩 테스트</h1>
<p>이제 모든 환경이 준비되었으니 테스트를 진행해보겠습니다.</p>
<blockquote>
<p>모든 테스트는 같은 환경(서버)에서 진행되었습니다.</p>
</blockquote>
<h2 id="21-gpu-pipeline-vs-cpu--gpu-트랜스코딩단일파일">2.1 GPU Pipeline vs CPU + GPU 트랜스코딩(단일파일)</h2>
<pre><code class="language-bash"># CPU + GPU 트랜스코딩 명령어
time ffmpeg -y -i input.mp4 -vf &quot;scale=1280:720&quot; -c:a copy -c:v h264_nvenc -b:v 5M output.mp4
#

# GPU pipeline 트랜스코딩 명령어
time ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 -vf &quot;scale_npp=1280:720&quot; -c:a copy -c:v h264_nvenc -b:v 5M output.mp4</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/8e8b3392-eca8-4490-89ae-ce3225827fec/image.png" alt=""></p>
<h3 id="테스트-결과">테스트 결과</h3>
<p><img src="https://velog.velcdn.com/images/berry_good/post/2e3c1610-c2ab-42f8-92fd-6390fc1d6d50/image.png" alt=""></p>
<p>GPU Pipeline으로 단일 파일을 트랜스코딩 한 결과가 약 2배 이상 빠른것으로 테스트 결과가 나왔습니다.</p>
<hr>
<h2 id="22-gpu-pipeline-vs-cpu--gpu-트랜스코딩다중파일-3ea">2.2 GPU Pipeline vs CPU + GPU 트랜스코딩(다중파일 3EA)</h2>
<ul>
<li><strong>CPU + GPU 동시 트랜스코딩 스크립트</strong>
&quot;해당 스크립트는 동시 트랜스코딩 성능을 측정하기 위해 작성하였습니다.&quot;<pre><code class="language-bash">#!/bin/bash
</code></pre>
</li>
</ul>
<h1 id="3개의-ffmpeg-인코딩-작업을-동시에-실행하고">3개의 ffmpeg 인코딩 작업을 동시에 실행하고,</h1>
<h1 id="각-실행의-출력-및-time-결과를-ffmpeg_time2log에-저장">각 실행의 출력 및 time 결과를 ffmpeg_time2.log에 저장</h1>
<p>for i in {1..3}; do 
    echo &quot;=== Run $i ===&quot; &gt;&gt; ffmpeg_time2.log
    { time ffmpeg -y -i input.mp4 <br>        -vf &quot;scale=1280:720&quot; <br>        -c:a copy <br>        -c:v h264_nvenc -b:v 5M <br>        output_$i.mp4 ; } &gt;&gt; ffmpeg_time2.log 2&gt;&amp;1 &amp;
    echo &quot;&quot; &gt;&gt; ffmpeg_time2.log
done</p>
<p>wait
echo &quot;모든 인코딩 작업이 완료되었습니다.&quot;</p>
<pre><code>
- **GPU Pipeline 동시 트랜스코딩 스크립트**
&quot;해당 스크립트는 동시 트랜스코딩 성능을 측정하기 위해 작성하였습니다.&quot;

```bash
#!/bin/bash

# 3개의 ffmpeg 인코딩 작업을 동시에 실행하고,
# 각 실행의 출력 및 time 결과를 ffmpeg_time.log에 저장

for i in {1..3}; do
    echo &quot;=== Run $i ===&quot; &gt;&gt; ffmpeg_time.log
    { time ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 \
        -vf &quot;scale_npp=1280:720&quot; \
        -c:a copy \
        -c:v h264_nvenc -b:v 5M \
        output_$i.mp4 ; } &gt;&gt; ffmpeg_time.log 2&gt;&amp;1 &amp;
    echo &quot;&quot; &gt;&gt; ffmpeg_time.log
done

wait
echo &quot;모든 인코딩 작업이 완료되었습니다.&quot;</code></pre><h3 id="테스트-결과-1">테스트 결과</h3>
<p><img src="https://velog.velcdn.com/images/berry_good/post/08e5114f-e6cb-4135-a60d-0abe106b1340/image.png" alt=""></p>
<p>이전 테스트에 비해 속도가 느려졌지만, 여전히 GPU Pipeline 구성이 더 빠른것을 확인할 수 있습니다.</p>
<hr>
<h2 id="23-gpu-pipeline-vs-cpu--gpu-트랜스코딩다중파일-6ea">2.3 GPU Pipeline vs CPU + GPU 트랜스코딩(다중파일 6EA)</h2>
<p>스크립트는 2.2 스크립트에서 for문을 조정하면 됩니다!</p>
<h3 id="테스트-결과-2">테스트 결과</h3>
<p><img src="https://velog.velcdn.com/images/berry_good/post/73ffc60c-e221-43be-9cca-b25f78cd90a0/image.png" alt=""></p>
<hr>
<h2 id="3-테스트-종합">3. 테스트 종합</h2>
<p>디코딩 + 스케일링을 사용하여 실제 모니터링을 진행했을때
ENC, DEC에서 GPU를 사용하는 것을 확인 할 수 있었습니다.
<img src="https://velog.velcdn.com/images/berry_good/post/b79c694b-c9ba-483d-8358-79e910e5df71/image.png" alt=""></p>
<p>또한 테스트를 종합해볼때 6EA 이상 다중 트랜스코딩을 진행할때 GPU Pipeline 트랜스코딩의 성능이 <strong>줄어드는</strong> 결과가 나왔습니다.</p>
<p>왜 이러한 결과가 나왔을까요?</p>
<ul>
<li><strong>GPU 리소스 경쟁</strong><ul>
<li>NVENC/NVDEC/커널 연산 등 GPU 내부 리소스는 한정됨.</li>
<li>NVDEC 디코딩, NPP 스케일링, NVENC 인코딩이 모두 동시에 여러 스트림을 처리할 때 GPU 내부 메모리, CUDA 코어, 엔진이 경쟁 → 스케줄링 대기 발생.</li>
<li>1~2개는 GPU가 충분히 커버하지만 3개, 6개 이상부터는 큐 대기 및 context switch가 발생 → 성능 하락.</li>
</ul>
</li>
</ul>
<blockquote>
<p>따라서 자신의 서버, GPU의 성능을 정확히 파악하여 특정 Queue 개수를 제한하여 트랜스코딩 성능을 향상하는 것이 중요합니다!</p>
</blockquote>
<hr>
<p>참고문헌
<a href="https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/ffmpeg-with-nvidia-gpu/index.html">https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/ffmpeg-with-nvidia-gpu/index.html</a>
<a href="https://ffmpeg.org/index.html#news">https://ffmpeg.org/index.html#news</a>
<a href="https://github.com/m-ab-s/media-autobuild_suite/issues/2522">https://github.com/m-ab-s/media-autobuild_suite/issues/2522</a>
<a href="https://developer.nvidia.com/blog/enabling-customizable-gpu-accelerated-video-transcoding-pipelines/">https://developer.nvidia.com/blog/enabling-customizable-gpu-accelerated-video-transcoding-pipelines/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CDN] GPU 트랜스코딩에 하드웨어 가속 얹어보기(feat. NVIDIA) - 1탄]]></title>
            <link>https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA</link>
            <guid>https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA</guid>
            <pubDate>Sat, 06 Sep 2025 09:36:25 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>최근에 운영 중인 서버의 OS 업그레이드 작업을 맡게 되었습니다. 이 서버는 주로 FFmpeg을 이용한 동영상 트랜스코딩을 담당하고 있었는데, OS 버전이 올라가면서 기존에 사용하던 FFmpeg 패키지와의 호환성 문제로 인해 FFmpeg 패키지도 최신 버전으로 업그레이드를 진행했습니다.
업그레이드 검토 중 문서를 살펴보다 보니 FFmpeg이 생각보다 꽤 많은 기능이 추가되어 있었고 문득 이런 생각이 들었습니다.
“지금 이 서버가 내는 성능(속도)이 과연 최선일까?”
그래서 단순히 업그레이드에서 끝내지 않고, 실제로 성능 테스트와 튜닝을 진행해 보기로 했습니다. 이번 글에서는 그 과정과 성능 최적화 테스트 결과를 공유하고자 합니다.</p>
</blockquote>
<hr>
<h1 id="1-트랜스코딩이란">1. 트랜스코딩이란?</h1>
<ul>
<li>트랜스코딩(Transcoding)은 원본 미디어 파일을 다른 형식이나 설정으로 변환하는 과정입니다.</li>
<li>왜 트랜스코딩 과정을 거치지?<ul>
<li><strong>호환성 확보</strong> → 모든 장치가 미디어 형식(코덱)을 지원하지 않기 때문에 미디어 파일을 재생할 수 있는 호환 장치의 수를 최대화하기 위해</li>
<li><strong>파일 크기 최적화</strong> → 고화질의 원본은 크기가 매우 커 공간 많이 차지해 스트리밍 시 버퍼링 문제를 일으킬 가능성이 많아 크기 최적화을 진행</li>
<li><strong>품질 조정</strong> → 사용자의 네트워크 환경에 따라 다양한 해상도로 재생하기 위해 1080P, 720P 등 다양한 품질로 미디어 파일을 변환</li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-트랜스코딩-진행-로직">2. 트랜스코딩 진행 로직</h1>
<p><img src="https://velog.velcdn.com/images/berry_good/post/7588672c-21d1-4e75-b5b1-886d8cc9e390/image.png" alt="">
트랜스코딩에 대해 개념을 알았으니 이제 미디어 파일(동영상)이 어떻게 트랜스코딩 되어 사용자들에게 보여지는지 과정을 알아보겠습니다</p>
<ul>
<li><strong>Input File</strong><ul>
<li>원본 미디어 파일을 트랜스코딩 하기 위해 준비하는 작업</li>
<li>ex) test.mp4, sample.mkv</li>
</ul>
</li>
<li><strong>Demuxing</strong><ul>
<li>입력 파일에서 비디오, 오디오, 자막 등 여러 데이터 스트림을 분리하는 과정</li>
<li>ex) sample.mkv 파일에서 H.265 비디오 스트림과 DTS 오디오 스트림을 분리</li>
</ul>
</li>
<li><strong>Decoding</strong><ul>
<li>분리된 데이터 스트림의 압축을 해제하여 원시 데이터로 만드는 과정</li>
<li>ex) 압축된 비디오 스트림을 RGB나 YUV와 같은 원시 비디오 데이터로 변환</li>
</ul>
</li>
<li><strong>Filters &amp; Processing</strong><ul>
<li>디코딩 된 원시 데이터(영상/음성)를 원하는 형태로 가공</li>
<li>ex) 해상도 변경 : 4K원본 비디오를 1080P 해상도로 줄인다
로고 추가 : 비디오 오른쪽 하단에 회사 로고 삽입</li>
</ul>
</li>
<li><strong>Encoding</strong><ul>
<li>필터링된 원시 데이터를 원하는 코덱을 사용해 다시 압축</li>
<li>ex) H.265 → H.264로 변환, DTS → AAC 변환, Bitrate 변환</li>
</ul>
</li>
<li><strong>Muxing</strong><ul>
<li>새롭게 인코딩 된 비디오와 오디오 스트림을 하나의 파일에 합치는 과정</li>
<li>ex) H.264 비디오 스트림 + AAC 오디오 스트림 = final.mp4</li>
</ul>
</li>
<li><strong>Output File</strong><ul>
<li>위의 모든 과정을 거쳐 최종적으로 생성된 파일</li>
<li>ex) final.mp4 / 1080p, 5M Bitrate, .mp4</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3-gpu-하드웨어-가속이란">3. GPU 하드웨어 가속이란?</h1>
<ul>
<li>GPU 하드웨어 가속은 미디어 처리(인코딩, 디코딩 등)에서 GPU의 병렬 처리 능력을 활용하여 작업 성능을 극적으로 높이는 기술입니다.</li>
</ul>
<p>앞서 이야기 했던 Decoding, Filters &amp; Processing, Encoding을 CPU 대신 GPU를 이용하여 동영상 트랜스코딩의 속도를 획기적으로 향상시킬 수 있습니다.</p>
<ul>
<li>어떻게?<ul>
<li>추후에 기술할 NVIDIA의 GPU에는 전용 디코더(NVDEC)과 인코더(NVENC)이 내장되어 있어 이 전용 GPU가 압축된 스트림을 처리합니다.</li>
<li>디코더가 만든 프레임은 GPU 메모리에 올라갑니다. 이 상태에서 GPU 필터(scale_npp)로 처리하고 다시 인코더로 넘겨 GPU Pipeline을 만듭니다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="4-테스트-설계gpu-pipeline-구성">4. 테스트 설계(GPU Pipeline 구성)</h1>
<p>먼저 기존의 트랜스코딩 로직을 분석해 보았습니다.</p>
<ul>
<li><p>기존 트랜스코딩 로직
<img src="https://velog.velcdn.com/images/berry_good/post/3fb14fce-43fa-4273-b9a7-6914fd5033bf/image.png" alt="">
기존 트랜스코딩 로직에서는 Encoding 과정에서만 GPU를 사용하여 미디어를 변환하고 있었습니다.
실제 서버에서 확인했을경우 아래의 사진처럼 decoding에는 GPU를 하나도 쓰지 않는것을 확인 할 수 있었습니다.
<img src="https://velog.velcdn.com/images/berry_good/post/2fadf8c8-a6ca-4408-b9e7-ee4940dd65e8/image.png" alt=""></p>
</li>
<li><p><strong>GPU Pipeline 로직</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/7f542c9e-95e8-4752-bdc8-dfb26bed6214/image.png" alt="">
기존 CPU로 처리하던 Decoding, Filters &amp; Processing 과정을 GPU로 처리하여 하나의 GPU Pipeline을 만들어 속도를 획기적으로 높일 수 있습니다.</p>
</li>
</ul>
<p>간단하게 비교표를 만들어 보았습니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>인코딩만 GPU 사용</th>
<th>전체 파이프라인 GPU 사용</th>
</tr>
</thead>
<tbody><tr>
<td>처리 과정</td>
<td>CPU (디코딩/필터링) → GPU (인코딩)</td>
<td>GPU (디코딩 → 필터링 → 인코딩)</td>
</tr>
<tr>
<td>데이터 흐름</td>
<td>CPU-메모리-GPU 간 데이터 전송 발생</td>
<td>GPU 내부에서 데이터 처리 (전송 없음)</td>
</tr>
<tr>
<td>PCIe 대역폭 소모</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>CPU 사용량</td>
<td>중간~높음 (디코드·필터에 CPU 사용)</td>
<td>매우 낮음 (대부분 GPU 사용)</td>
</tr>
<tr>
<td>지연(Latency)</td>
<td>중간 — 복사 지연 존재</td>
<td>낮음 — 파이프라인 오버랩으로 latency 감소</td>
</tr>
<tr>
<td>동시 스트림(Throughput)</td>
<td>한계가 빠르게 옴</td>
<td>훨씬 높음 (병렬 처리·재사용 가능)</td>
</tr>
<tr>
<td>1→N(멀티 해상도) 효율</td>
<td>디코드/업로드가 중복될 수 있음</td>
<td>디코드 한 번으로 여러 해상도 생성 가능(재사용)</td>
</tr>
</tbody></table>
<p>위 내용을 간단하게 정리해보면</p>
<p><strong>데이터 전송 병목 현상을 완전히 제거하여 CPU-GPU 간 데이터 전송 없이 모든 연산이 GPU 내부에서 고속으로 이루어지므로, 트랜스코딩 속도가 획기적으로 향상됩니다.</strong></p>
<p>해당 로직을 바탕으로 패키지를 설치해보고 테스트를 진행해보도록 하겠습니다.</p>
<hr>
<h3 id="참고문헌">참고문헌</h3>
<p><a href="https://www.cdnetworks.com/ko/blog/media-delivery/what-is-transcoding/">https://www.cdnetworks.com/ko/blog/media-delivery/what-is-transcoding/</a>
<a href="https://towardsdatascience.com/breaking-the-bottleneck-gpu-optimised-video-processing-for-deep-learning/">https://towardsdatascience.com/breaking-the-bottleneck-gpu-optimised-video-processing-for-deep-learning/</a></p>
<hr>
<p><a href="https://velog.io/@berry_good/CDN-GPU-%ED%8A%B8%EB%9E%9C%EC%8A%A4%EC%BD%94%EB%94%A9%EC%97%90-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EA%B0%80%EC%86%8D-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0feat.-NVIDIA-2%ED%83%84">[CDN] GPU 트랜스코딩에 하드웨어 가속 얹어보기(feat. NVIDIA) - 2탄</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CDN] HLS 프로토콜이란? ]]></title>
            <link>https://velog.io/@berry_good/HLS-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@berry_good/HLS-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 30 Jul 2025 11:44:11 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>실무를 수행하며 HLS라는 단어를 처음 들어봤습니다. CDN 업무를 수행하며 OVP(Online Video Platform)을 운영하고 있는데요 처음에 HLS에 대한 정보도 부족하고 어떻게 실제 작동하는지에 대한 지식이 부족해 어려움을 겪었습니다. 지금은 어느정도 HLS에 대해 이해하고 있지만 알고 있는 내용을 한번 정리해 보기 위해 이 글을 씁니다.</p>
</blockquote>
<h1 id="1-hls란">1. HLS란?</h1>
<ul>
<li><p>HTTP Live Streaming(HLS)는 Apple에서 개발한 미디어 스트리밍 프로토콜로 비디오 콘텐츠를 인터넷을 통해 실시간 또는 On-demand로 스트리밍 하는데 사용됩니다. 현재 널리 사용되는 <strong>A</strong>daptive <strong>B</strong>itrate St<strong>R</strong>eaming(ABR) 입니다.</p>
</li>
<li><p>HLS의 특징으로는</p>
<ul>
<li>HTTP 기반의 스트리밍</li>
<li>적응형 비트레이트(Adaptive Bitrate) 지원</li>
<li>파일 분할 및 플레이리스트 구조</li>
<li>높은 호환성 및 접근성</li>
<li>CDN과의 통합 용이</li>
<li>VOD, Live 모두 사용 가능</li>
</ul>
<p>이 있습니다.</p>
</li>
</ul>
<h1 id="2-hls의-구조">2. HLS의 구조</h1>
<ul>
<li><strong>Master Playlist(.m3u8)</strong><ul>
<li>여러 개의 미디어 플레이리스트를 참조하는 최상단 m3u8, 서로 다른 해상도, 비트레이트를 포함합니다.</li>
</ul>
</li>
<li><strong>Media Playlist(.m3u8)</strong><ul>
<li>각 세그먼트들의 위치(URL), 순서, 길이 등의 정보를 담고 있는 메타데이터 파일 입니다.</li>
</ul>
</li>
<li><strong>Media Segment(ts)</strong><ul>
<li>전체 영상이 수 초 단위로(보통 2~10초) 분할된 미디어 조각입니다. MPEG-TS 포맷을 주로 사용합니다.</li>
</ul>
</li>
</ul>
<p>그림을 보면 조금 더 이해가 쉬워 그림을 한번 봐볼까요?
<img src="https://velog.velcdn.com/images/berry_good/post/e5470537-952e-4fdd-be34-f273cf6d08cd/image.png" alt=""></p>
<p>위의 그림처럼 <strong>Master Playlist</strong> 안에는 다양한 품질의 <strong>Media Playlist</strong>가 있으며 그 안에는 미디어 조각들인 <strong>Media Segment</strong>가 존재합니다. </p>
<p>예시를 보며 조금 더 자세히 알아보겠습니다.</p>
<p>실제 HLS 영상을 분석할때, Chrome 개발자 도구를 사용해서 Master Playlist, Media Playlist, Media Segment를 다운로드 하는 것을 볼 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/abcc0e4d-b4c8-4704-a894-f9b0f3b6dfcd/image.png" alt=""></p>
<p>먼저 <strong>Master Playlist</strong>의 내용을 보겠습니다.
(여기에서는 playlist.m3u8)</p>
<pre><code>#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=3513750,RESOLUTION=1920x1080,FRAME-RATE=25.000,NAME=&quot;1080P&quot;,CLOSED-CAPTIONS=NONE
https://test/good_1080P/v/chunklist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1241250,RESOLUTION=1280x720,FRAME-RATE=25.000,NAME=&quot;720P&quot;,CLOSED-CAPTIONS=NONE
https://test/good_720P/v/chunklist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=351250,RESOLUTION=640x360,FRAME-RATE=25.000,NAME=&quot;360P&quot;,CLOSED-CAPTIONS=NONE
https://test/good_360P/v/chunklist.m3u8</code></pre><p><img src="https://velog.velcdn.com/images/berry_good/post/79ad1171-ac7f-4764-99b7-70f6e7ad9c4e/image.png" alt=""></p>
<p>앞서 설명드렸던 것처럼 다양한 품질의 Media Playlist들의 URL을 가지고 있는것을 알 수 있습니다.</p>
<p>이 URL을 바탕으로 사용자들의 대역폭(Bandwidth)를 측정하여 대역폭에 맞는 동영상을 재생하게 됩니다.</p>
<blockquote>
<p>그러면 사용자의 대역폭은 어떻게 알 수 있을까요?
➡️ 플레이어(HLS.js, Safari 등)가 일부 세그먼트를 다운로드해보고, 그 속도와 크기를 기반으로 실제 네트워크 대역폭을 계산해서 알맞은 해상도를 선택합니다.</p>
</blockquote>
<p>위의 Master Playlist의 태그의 의미는
<strong>#EXTM3U</strong> : 파일이 M3U8 형식(HLS playlist)임을 나타내는 태그
<strong>#EXT-X-STREAM-INF</strong> : 미디어 플레이리스트에 대한 정보를 제공하는 태그</p>
<hr>
<p>두번째로 Media Playlist 입니다.
(여기서는 chunklist.m3u8)</p>
<pre><code>#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
/test/good_1080P/v/0.ts
#EXTINF:10.0,
/test/good_1080P/v/1.ts
#EXTINF:10.0,
/test/good_1080P/v/2.ts
#EXTINF:7.28,
/test/good_1080P/v/3.ts
#EXT-X-ENDLIST</code></pre><p><img src="https://velog.velcdn.com/images/berry_good/post/33024564-eebe-48f4-b1b7-a79cabdb1f0c/image.png" alt=""></p>
<p>Media Playlist안에는 전체 영상에서 특정 시간 초로 분리된 미디어 조각들의 정보가 있습니다.
이 미디어 조각들을 다운로드 하며 영상을 재생하는 구조로 설명할 수 있습니다.</p>
<p>위의 Media Playlist의 태그의 의미는</p>
<p><strong>#EXTM3U</strong> : 파일이 M3U8 형식(HLS playlist)임을 나타내는 태그
<strong>#EXT-X-VERSION:3</strong> : HLS 버전 3 사용
<strong>#EXT-X-TARGETDURATION:10</strong> : 가장 긴 세그먼트의 재생 시간 (초) 기준값
<strong>#EXT-X-MEDIA-SEQUENCE:0</strong> :     첫 세그먼트의 순번 (시작 인덱스)
<strong>#EXTINF:10.0</strong> : 다음 세그먼트의 재생 시간 (초 단위)
<strong>#EXT-X-ENDLIST</strong> : 더 이상 세그먼트가 없음을 의미 (VOD 완료 표시)
입니다.</p>
<hr>
<p>마지막으로 Media Segmnet입니다.
(여기에서는 0.ts)</p>
<p>이 Segment 는 실제 미디어이기 때문에 실제 브라우저 창에 0.ts의 요청 URL을 입력하게 되면 10초로 분리 된 동영상이 다운로드 됩니다.
<img src="https://velog.velcdn.com/images/berry_good/post/04b064b1-82e0-44b2-89b8-a0f924b8eee0/image.png" alt=""></p>
<p>실제 .ts 파일을 URL에 입력했을 경우
<img src="https://velog.velcdn.com/images/berry_good/post/d846623c-5ac8-4c3c-91f5-421fadbe7925/image.png" alt=""></p>
<p>위의 그림과 같이 조각난 동영상의 일부가 다운로드 되게 됩니다.
이 동영상들을 조합하게 된다면 하나의 완전한 동영상이 완성되게 됨으로서 스크린 녹화를 하지 않고도 원본 동영상을 얻을 수 있습니다.
이러한 악용사례를 방지하기 위해 <strong>DRM</strong> 솔루션들이 있는데, 그 부분은 다음번에 다뤄보겠습니다.</p>
<hr>
<h1 id="3-hls의-동작방식">3. HLS의 동작방식</h1>
<p>그러면 HLS는 어떻게 동작할까요?</p>
<p><strong>1. 인코딩 &amp; 패키징</strong></p>
<ul>
<li>원본 미디어 파일을 여러 품질(해상도/비트레이트)로 인코딩합니다.</li>
<li>인코딩된 파일들을 짧은 세그먼트(기본 6초, 보통 2~10초)로 분할(Segmenting)합니다.</li>
<li>각 품질마다 개별 Playlist 생성</li>
<li>Master Playlist에 각각의 M3U8들을 링크로 연결</li>
</ul>
<p><strong>2. 전송</strong></p>
<ul>
<li>모든 세그먼트와 플레이리스트 파일들을 HTTP 서버에 업로드</li>
<li>Master Playlist를 받아 적절한 품질의 M3U8을 선택하고 스트리밍을 시작</li>
</ul>
<p><strong>3. 재생</strong></p>
<ul>
<li>클라이언트는 M3U8을 파싱해 세그먼트를 순차적으로 요청하며 다운로드하고 재생</li>
<li>네트워크 상황에 따라 다른 해상도의 스트림으로 자동 전환</li>
</ul>
<p>이러한 순서로 동작하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/5ee838de-c676-4367-aa05-37d58a90d3f4/image.png" alt=""></p>
<p>그림으로 보면 조금 이해하기 쉬울것 같습니다!</p>
<p>HLS는 HTTP 형식이기 때문에 앞서 말한것 처럼 CDN 적용이 가능하여 아직까지도 많은 사랑을 받고 있습니다!</p>
<hr>
<h3 id="참고문헌">참고문헌</h3>
<p><a href="https://developer.apple.com/library/archive/referencelibrary/GettingStarted/AboutHTTPLiveStreaming/about/about.html">https://developer.apple.com/library/archive/referencelibrary/GettingStarted/AboutHTTPLiveStreaming/about/about.html</a>
<a href="https://www.cloudflare.com/ko-kr/learning/video/what-is-http-live-streaming/">https://www.cloudflare.com/ko-kr/learning/video/what-is-http-live-streaming/</a>
<a href="https://developer.apple.com/documentation/http-live-streaming">https://developer.apple.com/documentation/http-live-streaming</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[fail2ban으로 공격자의 IP 차단하기 + Slack 연동]]></title>
            <link>https://velog.io/@berry_good/fail2ban%EC%9C%BC%EB%A1%9C-%EA%B3%B5%EA%B2%A9%EC%9E%90%EC%9D%98-IP-%EC%B0%A8%EB%8B%A8%ED%95%98%EA%B8%B0-Slack-%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@berry_good/fail2ban%EC%9C%BC%EB%A1%9C-%EA%B3%B5%EA%B2%A9%EC%9E%90%EC%9D%98-IP-%EC%B0%A8%EB%8B%A8%ED%95%98%EA%B8%B0-Slack-%EC%97%B0%EB%8F%99</guid>
            <pubDate>Mon, 21 Jul 2025 11:59:49 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>최근 한 기업이 랜섬웨어 공격으로 인해 업무에 큰 차질을 겪었다는 보안 기사를 접했습니다. 해킹 그룹이 Brute Force 기법을 이용해 SSL-VPN에 무차별 로그인 시도를 한 끝에 내부망을 장악한 것이 원인이었습니다. 이 기사를 보고 저희가 운영 중인 VPN도 동일한 위협에 노출될 수 있다는 우려가 생겼고, 이에 따라 로그인 시도를 제한하는 방안을 검토하게 되었습니다.</p>
</blockquote>
<h1 id="1-fail2ban이란">1. fail2ban이란?</h1>
<ul>
<li><p>Fail2Ban은 서버 보안을 강화하기 위해 널리 사용되는 <strong>침입 방지 도구(IPS, Intrusion Prevention System)</strong>입니다. 주로 SSH, FTP, SMTP, Apache, Nginx, VPN 등 외부 접근이 가능한 서비스에서 발생하는 비정상적인 로그인 시도를 자동으로 탐지하고 차단하는 데 사용합니다.</p>
</li>
<li><p><strong>fail2ban의 주요 기능</strong></p>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>로그 분석</td>
<td><code>/var/log/secure</code>, <code>/var/log/auth.log</code> 등 로그 파일을 실시간 감시</td>
</tr>
<tr>
<td>IP 차단</td>
<td>공격으로 의심되는 IP를 iptables, firewalld, nftables, 또는 route 명령어로 차단</td>
</tr>
<tr>
<td>다중 jail 지원</td>
<td>각 서비스별로 감시 규칙(jail)을 개별 설정 가능</td>
</tr>
<tr>
<td>차단 해제</td>
<td>일정 시간이 지나면 자동으로 차단 해제 (또는 수동 해제 가능)</td>
</tr>
<tr>
<td>Slack, 이메일 알림</td>
<td>공격 탐지 시 관리자에게 알림 전송 가능</td>
</tr>
</tbody></table>
</li>
</ul>
<h1 id="2-fail2ban-기본-환경-구성">2. fail2ban 기본 환경 구성</h1>
<ul>
<li>fail2ban 설치<pre><code class="language-bash">apt-get install fail2ban -y</code></pre>
</li>
<li>fail2ban 프로세스 시작<pre><code class="language-bash">systemctl enable fail2ban
systemctl start fail2ban</code></pre>
</li>
<li>fail2ban 프로세스 상태 확인<pre><code class="language-bash">systemctl status fail2ban.service</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/69937881-fa7e-4e0c-9807-49367b732d17/image.png" alt=""></li>
</ul>
<h1 id="3-fail2ban-기본-구조">3. fail2ban 기본 구조</h1>
<p>먼저 fail2ban의 기본 구조를 살펴 보겠습니다.
가장 중점적으로 볼 부분은 </p>
<ul>
<li><strong>action.d/</strong><ul>
<li>액션 정의 디렉토리</li>
<li>fail2ban이 특정 조건을 만족할 때 어떤 Action을 행할지 정의한 템플릿이 들어있는 폴더</li>
<li>iptables-common.conf, ufw.conf 등 다양한 템플릿이 기본적으로 내장되어 있음</li>
</ul>
</li>
<li><strong>filter.d/</strong><ul>
<li>로그 필터 정의 디렉토리</li>
<li>로그에서 공격을 탐지하기 위한 정규식 필터가 있는 폴더</li>
<li>sshd.conf, gitlab.conf 등 다양한 정규식 필터가 있음</li>
</ul>
</li>
<li><strong>jail.local</strong><ul>
<li>사용자 정의 Jail 설정 파일</li>
<li>기존에 설정되어 있는 jail.conf를 덮어쓰는 우선순의 파일(해당 파일은 생성해야함)</li>
</ul>
</li>
</ul>
<p>입니다. 이 3가지의 구조로 기본적인 IP 차단 룰을 구현 할 수 있기 때문에 나머지 부분은  참고해주시면 됩니다.</p>
<pre><code>/etc/fail2ban
├── action.d/               # 액션 정의 파일들 (예: IP 차단 방식)
├── fail2ban.conf           # fail2ban 기본 설정 파일
├── fail2ban.d/             # fail2ban 설정 조각(디렉토리 override용)
├── filter.d/               # 로그 필터 정의 (regex 기반)
├── jail.conf               # 기본 jail 설정 파일
├── jail.d/                 # 개별 jail 설정 파일 모음
├── jail.local              # 사용자 정의 jail 설정 파일 (우선 적용됨)
├── paths-arch.conf         # Arch 리눅스용 로그 경로 설정
├── paths-common.conf       # 공통 로그 경로 설정
├── paths-debian.conf       # Debian 계열용 로그 경로 설정
├── paths-opensuse.conf     # OpenSUSE용 로그 경로 설정</code></pre><h1 id="4-vpn-환경에-fail2ban-적용">4. VPN 환경에 fail2ban 적용</h1>
<p>기존에 운영하던 VPN에 해당 프로세스를 적용한 뒤, 고도화로 내용으로 Slack과 연동을 진행해 보겠습니다
VPN 구축이 궁금하신 분은 아래의 링크 참고 부탁드립니다</p>
<h3 id="vpn-서버-구축기"><a href="https://velog.io/@berry_good/VPN-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95feat.-openvpn">VPN 서버 구축기</a></h3>
<ul>
<li><p>fail2ban 정책 생성</p>
<pre><code class="language-bash">cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local</code></pre>
<ul>
<li>jail.local 파일 최하단에 VPN 내용 삽입<pre><code class="language-bash">[vpn]
enabled = true
filter = vpn-auth
port = 1111
protocol = udp   # 프로토콜 default는 TCP
logpath = /var/log/로그경로.log 
maxretry = 5     # 최대 시도 횟수
bantime = 600    # 차단 시간
findtime = 300   # 탐지 범위
action = %(action_)s   # 차단 후 Action
     slack-notify</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/5b075b81-4418-4e19-acfa-474c12f563c7/image.png" alt=""></li>
</ul>
</li>
<li><p>기존 sshd fail2ban disable</p>
<pre><code class="language-bash">vi /etc/fail2ban/jaid.d/defaults-debian.conf</code></pre>
<pre><code class="language-bash">[sshd]
enabled = false</code></pre>
<p>sshd도 같이 차단 정책을 걸고 싶으면 해당 부분은 true로 설정하시면 됩니다.</p>
</li>
</ul>
<ul>
<li><p>로그 포맷 설정 </p>
<pre><code class="language-bash">vi /etc/fail2ban/filter.d/vpn-auth.conf</code></pre>
<pre><code class="language-bash">[Definition]
maxlines = 10

prefregex = ^\s*(?:(?:[A-Za-z]{3}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+[\w\d\.-]+\s+)?(?:vpn\[\d+\]:)?\s*)PLUGIN AUTH-PAM: BACKGROUND: user &#39;(?P&lt;user&gt;[^&#39;]+)&#39; failed to authenticate: Authentication failure

failregex = ^(?:&lt;F-NOFAIL&gt;IP:Port SENT CONTROL \[UNDEF\]: &#39;AUTH_FAILED&#39; \(status=1\)&lt;/F-NOFAIL&gt;\s+)?&lt;HOST&gt;:\d+ SENT CONTROL \[UNDEF\]: &#39;AUTH_FAILED&#39; \(status=1\)$

ignoreregex =
</code></pre>
<ul>
<li>prefregex<ul>
<li>로그 메시지 앞부분의 공통된 패턴을 정의하기 위한 정규식</li>
</ul>
</li>
<li>failregex<ul>
<li>차단 대상이 되는 로그 메시지를 감지하기 위한 정규식</li>
<li>해당 정규식에 매칭되는 로그가 탐지되면, fail2ban은 해당 로그의 IP를 추출하여 차단 대상으로 지정</li>
<li>ignoreregex<ul>
<li>예외 처리 패턴, 무시할 로그 패턴</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>저는 로그인을 시도한 IP와 user명을 Slack으로 알림 보내기 위해 <strong>failregex</strong>와 같이 <strong>prefregex</strong>를 사용하였습니다. 각 환경에 맞게 정규식을 조정하시면 됩니다.</p>
</li>
</ul>
<ul>
<li><p>IP 차단 후 Action 설정 편집</p>
<pre><code class="language-bash">vi /etc/fail2ban/action.d/slack-notify.conf</code></pre>
<pre><code class="language-bash">[Definition]
actionstart =
actionstop =
actioncheck =

# &#39;actionban&#39;은 IP가 차단될 때 실행될 명령어
# 스크립트에 다음 인자들을 전달합니다:
# 1. &lt;ip&gt;: 차단될 IP 주소 (Fail2Ban이 자동으로 제공)
# 2. BANNED: 조치 타입
# 3. &lt;name&gt;: 조치를 유발한 Jail 이름 (Fail2Ban이 자동으로 제공)
# 4. &lt;F-USER&gt;: 필터에서 캡처된 사용자 ID
actionban = /etc/fail2ban/slack-notify.sh &lt;ip&gt; BANNED &lt;name&gt; &lt;F-USER&gt;

# &#39;actionunban&#39;은 IP 차단이 해제될 때 실행될 명령어
# 동일한 인자들을 전달합니다.
actionunban = /etc/fail2ban/slack-notify.sh &lt;ip&gt; UNBANNED &lt;name&gt; &lt;F-USER&gt;</code></pre>
</li>
<li><p>Slack 알림 스크립트 생성</p>
<pre><code class="language-bash">vi /etc/fail2ban/slack-notify.sh</code></pre>
<pre><code class="language-bash">#!/bin/bash
# Slack 웹훅 URL WEBHOOK_URL=&quot;https://hooks.slack.com/test&quot; # 이 부분을 본인의 웹훅 URL로 변경해주세요!
# Fail2Ban으로부터 전달받는 인자들
HOST=&quot;$1&quot;    # 차단되거나 해제된 IP 주소 (Fail2Ban이 차단하는 대상)
ACTION=&quot;$2&quot;  # &#39;BANNED&#39; 또는 &#39;UNBANNED&#39;
JAIL=&quot;$3&quot;    # 조치가 발생한 Fail2Ban Jail 이름
USERID=&quot;$4&quot;  # 필터에서 캡처된 사용자 ID

# Slack 알림을 보낼 채널 설정
CHANNEL=&quot;#test&quot; 

if [[ &quot;$ACTION&quot; == &quot;BANNED&quot; ]]; then
  COLOR=&quot;#ff0000&quot;
elif [[ &quot;$ACTION&quot; == &quot;UNBANNED&quot; ]]; then
  COLOR=&quot;#36a64f&quot;
else
  COLOR=&quot;#cccccc&quot;
fi

# 메시지 내용
TEXT=&quot;*IP 차단 알림*
*조치:* ${ACTION}
*IP 주소:* ${HOST}
*사용자 ID:* ${USERID}
*시간:* $(date +&#39;%Y-%m-%d %H:%M:%S %Z&#39;)&quot;

# JSON 페이로드 생성
PAYLOAD=$(cat &lt;&lt;EOF
{
  &quot;channel&quot;: &quot;${CHANNEL}&quot;,
  &quot;username&quot;: &quot;test&quot;,
  &quot;icon_emoji&quot;: &quot;:happy:&quot;,
  &quot;attachments&quot;: [
    {
      &quot;color&quot;: &quot;${COLOR}&quot;,
      &quot;text&quot;: &quot;${TEXT}&quot;
    }
  ]
}
EOF
)

# Slack으로 메시지 전송
curl -X POST -H &quot;Content-type: application/json&quot; --data &quot;${PAYLOAD}&quot; &quot;${WEBHOOK_URL}&quot;</code></pre>
</li>
<li><p>fail2ban 재시작 및 로그 확인</p>
<pre><code class="language-bash">systemctl restart fail2ban.service
tail -50 /var/log/fail2ban.log</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/800a014b-38dc-4cc4-9f2b-156fe7563796/image.png" alt=""></p>
</li>
<li><p>Jail List 확인</p>
<pre><code class="language-bash">fail2ban-client status</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/968c9306-0753-4f0a-ac3b-a6935ad4559d/image.png" alt=""></p>
</li>
<li><p>IP 차단 테스트</p>
<pre><code class="language-bash">fail2ban-client status {JAIL-NAME}</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/276ddc14-3550-4bcb-9fa2-5b88ab4d2c90/image.png" alt=""></p>
</li>
<li><p>차단 되었을 경우
<img src="https://velog.velcdn.com/images/berry_good/post/e908bbac-4dfb-47f4-8e24-1aae1839a870/image.png" alt="">
<img src="https://velog.velcdn.com/images/berry_good/post/85ec916b-e3a6-42d9-8c17-a0b27653f424/image.png" alt=""></p>
</li>
<li><p>차단 해제</p>
<pre><code class="language-bash">fail2ban-client set &lt;jail-name&gt; unbanip &lt;IP&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/ba608aca-3815-4656-a18a-d30378179b10/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vault Cluster 구축기(feat. Terraform)]]></title>
            <link>https://velog.io/@berry_good/Vault-Cluster-%EA%B5%AC%EC%B6%95%EA%B8%B0feat.-Terraform</link>
            <guid>https://velog.io/@berry_good/Vault-Cluster-%EA%B5%AC%EC%B6%95%EA%B8%B0feat.-Terraform</guid>
            <pubDate>Sat, 28 Jun 2025 06:13:48 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>올해 신규 보안 프로젝트의 일환으로 Vault Cluster를 구축해 보았습니다.
기술 관련 오픈채팅방을 종종 살펴보면서 Vault를 사용하는 실무자가 의외로 많다고 생각했었는데 이번 프로젝트를 통해 Vault에 대한 실질적인 지식을 쌓을 수 있는 좋은 기회가 되었습니다.</p>
</blockquote>
<blockquote>
<p><strong>Why? 왜 Vault를 사용했는가?</strong>
많은 실무자들이 AWS를 활용하면서 동시에 AWS CLI도 같이 사용합니다. 저 역시 실무에서 AWS CLI를 사용하는데, 이때 /root/.aws 디렉터리에 AWS Secret Key가 평문으로 저장되어 있다는 점이 보안상 큰 취약점이라고 판단했습니다. 그래서 Vault를 활용하면 Secret Key를 동적으로 발급받을 수 있으며 이 키는 일정 시간이 지나면 만료가 되어 보안 수준을 한 층 높일 수 있다고 생각했습니다.   </p>
</blockquote>
<h1 id="1-vault란">1. Vault란?</h1>
<ul>
<li><p><strong>Vault</strong>는 UI, CLI 또는 HTTP API를 사용하여 애플리케이션이나 인프라에서 사용되는 민감 정보(Secrets) ex) API 키, 비밀번호, 인증서, 데이터베이스 자격 증명 등을 안전하게 저장하고 제어할 수 있게 도와주는 비밀 관리 시스템(Secret Management System) 입니다.</p>
</li>
<li><p><strong>Vault의 기능?</strong></p>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Secret 저장</strong></td>
<td>민감한 정보를 중앙에서 안전하게 저장하고 액세스를 제어할 수 있음</td>
</tr>
<tr>
<td><strong>동적 자격증명(Dynamic Secrets)</strong></td>
<td>요청 시마다 짧은 TTL(Time-To-Live)을 가진 임시 자격증명을 생성<br>예: DB 계정, AWS IAM credentials 등</td>
</tr>
<tr>
<td><strong>암호화 서비스(Encryption as a Service)</strong></td>
<td>Vault를 통해 데이터를 직접 암복호화 할 수 있음 (앱에 키를 저장할 필요 없음)</td>
</tr>
<tr>
<td><strong>정책 기반 접근 제어</strong></td>
<td>사용자/서비스의 권한을 세밀하게 제어 가능 (ACL 정책)</td>
</tr>
<tr>
<td><strong>감사 로그(Audit Logs)</strong></td>
<td>누가 어떤 Secret에 접근했는지 기록을 남겨 보안 감사에 유용</td>
</tr>
<tr>
<td><strong>멀티 백엔드 저장소 지원</strong></td>
<td>AWS S3, 파일 시스템, Consul, PostgreSQL 등 다양한 스토리지 백엔드를 사용 가능</td>
</tr>
</tbody></table>
</li>
</ul>
<ul>
<li>Vault의 장점?<ul>
<li>민감한 정보들이 소스코드, 환경변수, 파일 등에 직접 노출되지 않음</li>
<li>자격증명이 자동으로 만료되고 폐기되므로, 유출 시 피해를 최소화할 수 있음</li>
<li>자격 증명별 권한과 경로를 매우 세밀하게 정의 가능</li>
<li>감사로그(Audit Logs)를 지원하여 비밀에 접근하는지를 기록 가능</li>
</ul>
</li>
</ul>
<h1 id="2-vault-cluster-구성">2. Vault Cluster 구성</h1>
<ul>
<li><p>Vault Cluster의 필요성</p>
<ul>
<li>HA 구성 가능 → 하나의 서비스가 중단 되더라도 장애 X (무중단 운영 가능)</li>
<li>Auto Failover → 운영자가 개입 하지 않고도 follwer Node가 Leader Node로 자동 승격</li>
<li>데이터 내구성 → 저장된 Secrets 및 정보가 사라져도 복제된 다른 Node에서 정보 받아옴</li>
</ul>
</li>
<li><p>Vault Cluster 구성 요소?</p>
<ul>
<li>최소 3EA Node로 구성 (1개의 Active Node, 2개의 Stanby Node)</li>
<li><strong>Active Node</strong>: 클러스터에서 실제로 요청을 처리하는 주 노드입니다. 이 노드는 Vault의 모든 API 요청을 처리하며, 읽기 및 쓰기 작업을 수행할 가능</li>
<li><strong>Standby Node</strong>: 주로 Active Node의 상태를 복제하고 있습니다. Standby Node는 요청을 처리하지 않으며, Active Node에 장애가 발생할 경우 자동으로 Active Node 역할을 맡는다.</li>
</ul>
</li>
<li><p>Raft Storage 구성도</p>
<blockquote>
<p>암호화된 데이터를 보관하는 여러가지의 Storage 중 HA를 지원하는 Raft Storage를 선택했습니다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/berry_good/post/bc0c0921-d50d-4c48-8849-291a28119f0c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/8035b381-f55c-4401-91a9-d811222317fd/image.png" alt="">
Raft Storage는 Raft Consensus 알고리즘 사용하여 Leader 선출하는데 자세한 내용은 Google에... 질문하시면 더 자세한 정보를 얻을 수 있습니다!</p>
</li>
</ul>
<h1 id="3-vault-cluster-구축">3. Vault Cluster 구축</h1>
<h2 id="31-vault-설치">3.1 Vault 설치</h2>
<ul>
<li>GPG, wget 설치<pre><code class="language-bash">sudo apt update &amp;&amp; sudo apt install gpg wget</code></pre>
</li>
<li>Keyring 다운로드<pre><code class="language-bash">wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg</code></pre>
</li>
<li>Keyring 확인<pre><code class="language-bash">gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/7ecc6fbb-5c53-4b5c-aeb3-15a7d3c95833/image.png" alt=""></li>
<li>Vault 설치<pre><code class="language-bash">sudo apt update &amp;&amp; sudo apt install vault</code></pre>
</li>
<li>Vault Version 확인<pre><code class="language-bash">vault -version</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/cf980e60-16df-4eeb-9d85-ab98ec7c85c5/image.png" alt=""></li>
<li>Vault 시작<pre><code class="language-bash">systemctl enable vault.service
systemctl start vault.service</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/00e2a681-750e-46d0-b5dc-654b9e1f9546/image.png" alt=""></li>
</ul>
<blockquote>
<p>같은 방식으로 3EA 서버에 Vault 세팅 진행하면 됩니다</p>
</blockquote>
<h2 id="32-vault-cluster-구성">3.2 Vault Cluster 구성</h2>
<ul>
<li>데이터 폴더 생성 - Vault 1, Vault 2, Vault 3 서버<pre><code class="language-bash">mkdir -p /data/vault</code></pre>
</li>
<li>인증서 폴더 생성 - Vault 1, Vault 2, Vault 3 서버<pre><code class="language-bash">mkdir -p /data/vault/tls</code></pre>
</li>
<li><strong>Vault User에게 소유권 부여</strong> - Vault1, Vault2, Vault3<pre><code class="language-bash">chown -R vault:vault /data/vault</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/a5f80ccc-b6c3-41f3-8fc5-b894116c8300/image.png" alt=""></li>
<li>인증서 구성 파일 옮기기<pre><code class="language-bash">cd /data/vault/tls</code></pre>
<ul>
<li>cert.pem</li>
<li>chain.pem</li>
<li>fullchain.pem</li>
<li>privkey.pem</li>
</ul>
</li>
</ul>
<ul>
<li><p>Vault 환경 구성 진행    </p>
<pre><code class="language-bash">vi /etc/vault.d/vault.hcl</code></pre>
<ul>
<li>vault.hcl 파일 수정 - Vault1, Vault2, Vault3<pre><code class="language-hcl"># Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
</code></pre>
</li>
</ul>
<h1 id="full-configuration-options-can-be-found-at-httpsdeveloperhashicorpcomvaultdocsconfiguration">Full configuration options can be found at <a href="https://developer.hashicorp.com/vault/docs/configuration">https://developer.hashicorp.com/vault/docs/configuration</a></h1>
<p>ui = true  # UI 모드 Enable</p>
<p>#mlock = true
#disable_mlock = true</p>
<p>#storage &quot;file&quot; {</p>
<h1 id="path--optvaultdata">path = &quot;/opt/vault/data&quot;</h1>
<p>#}</p>
<p>#storage &quot;consul&quot; {</p>
<h1 id="address--1270018500">address = &quot;127.0.0.1:8500&quot;</h1>
<h1 id="path-----vault">path    = &quot;vault&quot;</h1>
<p>#}</p>
<h1 id="http-listener">HTTP listener</h1>
<p>#listener &quot;tcp&quot; {</p>
<h1 id="address--1270018200">address = &quot;127.0.0.1:8200&quot;</h1>
<h1 id="tls_disable--1">tls_disable = 1</h1>
<p>#}</p>
<p>cluster_addr = &quot;<a href="https://sample1.domain.com:8201&quot;">https://sample1.domain.com:8201&quot;</a>
api_addr = &quot;<a href="https://sample1.domain.com:8200&quot;">https://sample1.domain.com:8200&quot;</a></p>
<h1 id="메모리-부족-오류-방지">메모리 부족 오류 방지</h1>
<p>disable_mlock = true</p>
<h1 id="https-listener">HTTPS listener</h1>
<h1 id="https-구성">HTTPS 구성</h1>
<p>listener &quot;tcp&quot; {
 address = &quot;0.0.0.0:8200&quot;
 cluster_address = &quot;0.0.0.0:8201&quot;
 tls_cert_file      = &quot;/data/vault/tls/fullchain.pem&quot;
 tls_key_file       = &quot;/data/vault/tls/privkey.pem&quot;
 tls_client_ca_file = &quot;/data/vault/tls/chain.pem&quot;
 tls_disable = 0
}</p>
<h1 id="storage-구성-각-node_id는-달라야함">Storage 구성 각 <strong>node_id는 달라야함!!</strong></h1>
<p>storage &quot;raft&quot; {
 path = &quot;/data/vault&quot;
 node_id = &quot;raft_node_1&quot; </p>
<p> retry_join {
   leader_api_addr = &quot;<a href="https://sample1.domain.com:8200&quot;">https://sample1.domain.com:8200&quot;</a>
   leader_tls_servername   = &quot;sample1.domain.com&quot;
   leader_ca_cert_file     = &quot;/data/vault/tls/chain.pem&quot;
   leader_client_cert_file = &quot;/data/vault/tls/fullchain.pem&quot;
   leader_client_key_file  = &quot;/data/vault/tls/privkey.pem&quot;
 }
 retry_join {
   leader_api_addr = &quot;<a href="https://sample2.domain.com&quot;">https://sample2.domain.com&quot;</a>
   leader_tls_servername   = &quot;sample2.domain.com&quot;
   leader_ca_cert_file     = &quot;/data/vault/tls/chain.pem&quot;
   leader_client_cert_file = &quot;/data/vault/tls/fullchain.pem&quot;
   leader_client_key_file  = &quot;/data/vault/tls/privkey.pem&quot;
 }
 retry_join {
   leader_api_addr = &quot;<a href="https://sample3.domain.com:8200&quot;">https://sample3.domain.com:8200&quot;</a>
   leader_tls_servername   = &quot;sample3.domain.com&quot;
   leader_ca_cert_file     = &quot;/data/vault/tls/chain.pem&quot;
   leader_client_cert_file = &quot;/data/vault/tls/fullchain.pem&quot;
   leader_client_key_file  = &quot;/data/vault/tls/privkey.pem&quot;
 }
}</p>
<h1 id="enterprise-license_path">Enterprise license_path</h1>
<h1 id="this-will-be-required-for-enterprise-as-of-v18">This will be required for enterprise as of v1.8</h1>
<p>#license_path = &quot;/etc/vault.d/vault.hclic&quot;</p>
<h1 id="example-aws-kms-auto-unseal">Example AWS KMS auto unseal</h1>
<p>#seal &quot;awskms&quot; {</p>
<h1 id="region--us-east-1">region = &quot;us-east-1&quot;</h1>
<h1 id="kms_key_id--replace-me">kms_key_id = &quot;REPLACE-ME&quot;</h1>
<p>#}</p>
<h1 id="example-hsm-auto-unseal">Example HSM auto unseal</h1>
<p>#seal &quot;pkcs11&quot; {</p>
<h1 id="lib-------------usrvaultliblibcryptoki2_64so">lib            = &quot;/usr/vault/lib/libCryptoki2_64.so&quot;</h1>
<h1 id="slot------------0">slot           = &quot;0&quot;</h1>
<h1 id="pin-------------aaaa-bbbb-cccc-dddd">pin            = &quot;AAAA-BBBB-CCCC-DDDD&quot;</h1>
<h1 id="key_label-------vault-hsm-key">key_label      = &quot;vault-hsm-key&quot;</h1>
<h1 id="hmac_key_label--vault-hsm-hmac-key">hmac_key_label = &quot;vault-hsm-hmac-key&quot;</h1>
<p>#}</p>
<pre><code>
</code></pre></li>
</ul>
<blockquote>
<p>실제 상용 서버에 구성하기 때문에 보안향상을 위해 TLS을 사용하여 구성 진행하였습니다. 또한 각 Vault 서버별로 node ID를 다르게 구성 하셔야 합니다.</p>
</blockquote>
<ul>
<li>Vault 데몬 재시작 - Vault1, Vault2, Vault3<pre><code class="language-bash">vi /etc/profile
export VAULT_ADDR=&quot;https://sample1.domain.com:8200&quot; or sample2. sample3(각 서버에 맞게)
source /etc/profile</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/7d497e76-4145-4873-805d-238b84c20296/image.png" alt=""></li>
</ul>
<ul>
<li>Vault 초기화 - Vault1(Leader Node)<pre><code class="language-bash">vault operator init</code></pre>
</li>
</ul>
<blockquote>
<p>초기화를 진행하게 되면, Unseal Key가 나오는데 5개 중 3개를 입력해야지 Seal이 해제 됩니다. </p>
</blockquote>
<ul>
<li>Leader Node에서 Cluster 적용 확인<pre><code class="language-bash">vault operator raft list-peers</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/fb0eec31-4b26-4133-b037-e43d677819ac/image.png" alt=""></li>
</ul>
<h1 id="4-terraform과-연동">4. Terraform과 연동</h1>
<blockquote>
<p>Vault Cluster 활용법으로 Terraform과 연동 진행해 보았습니다.
앞서 언급한것처럼 기존에는 평문으로 AWS Token을 저장하여 Terraform을 사용하였는데, 보안 향상을 위해 Terraform Provider에서 Token을 주입해 AWS 리소스들을 다룰 수 있습니다.</p>
</blockquote>
<blockquote>
<p>Terraform에서는 임시 AWS Secret을 동적으로 provider에 주입하여 사용!
(단, Secret인증은 30분간 유효)</p>
</blockquote>
<ol>
<li>vault_token으로 Vault에 연결 (provider &quot;vault&quot;)</li>
<li>Vault에서 AWS 자격증명 가져오기 (data &quot;vault_aws_access_credentials&quot;)</li>
<li>AWS provider에 동적으로 그 자격증명 주입</li>
<li>이제 Terraform이 AWS 자원 생성/조회 가능</li>
</ol>
<h2 id="41-terraform-연동을-위한-구성-gslb--tls">4.1 Terraform 연동을 위한 구성 GSLB + TLS</h2>
<ul>
<li>GSLB 구성 이유?<ul>
<li>Terraform은 Vault 클러스터의 모든 노드 주소를 인식하지 못하므로, 장애가 발생한 노드로 요청이 전달되면 Vault로부터 응답을 받지 못할 수 있다.</li>
<li>이러한 문제를 방지하기 위해, GSLB의 헬스 체크 기능을 통해 장애 노드를 자동으로 감지하고 정상 노드로 트래픽을 우회시킨다</li>
</ul>
</li>
</ul>
<ul>
<li><p>GSLB 구성이 안 되어 있을 경우
<img src="https://velog.velcdn.com/images/berry_good/post/dbf670e0-a5b3-462d-ba5f-4b5cab0a86ca/image.png" alt="">
Vault 2번으로 Terraform은 주소를 알고 있지만 장애가 발생하여 HA 구성을 통해 Vault 1번으로 Active Node로 전환 되었을 때 Terraform과 Vault Cluster는 통신이 불가합니다.</p>
</li>
<li><p>GSLB 구성이 되어 있을 경우
<img src="https://velog.velcdn.com/images/berry_good/post/74200cad-c1e3-48e3-b940-8915b42c8c24/image.png" alt="">
GSLB를 통해 어디로 통신이 되든 Stanby Node는 Active Node로 요청을 Redirect 하기 때문에, 만약 Vault 2번이 장애가 나도 GSLB는 장애가 난 Node에게는 요청을 보내지 않기 때문에 서로 통신이 가능합니다.</p>
<p>단) GSLB는 sample.domain.com으로 요청을 받아야 합니다.
각 Node들은 고유의 도메인을 가지고 있으며(sample1, sample2, sample3) sample.domain.com 으로 요청을 받아 요청을 Balancing 해주게 됩니다.</p>
</li>
</ul>
<h2 id="42-terraform-서버-수정">4.2 Terraform 서버 수정</h2>
<ul>
<li>Vault Token 저장<pre><code class="language-bash">vi /etc/profile
export TF_VAR_vault_token=&quot;{Vault Token}”
source /etc/profile</code></pre>
</li>
<li>provider.tf 교체 및 재작성<ul>
<li>vi provider.tf</li>
</ul>
</li>
</ul>
<pre><code class="language-hcl">variable &quot;vault_token&quot; {
  type = string
  sensitive = true # 토큰 값을 보호하기 위해 sensitive로 설정
}

provider &quot;vault&quot; {
  address = &quot;https://sample.domain.com:8200&quot; # Vault 서버의 주소
  token   = sensitive(var.vault_token)          # Vault Token
}

data &quot;vault_aws_access_credentials&quot; &quot;example&quot; {
  backend = &quot;aws-test&quot; # 원하는 secret 설정
  role    = &quot;terraform&quot;  # Vault에서 설정한 역할 이름
}

provider &quot;aws&quot; {
  access_key = data.vault_aws_access_credentials.example.access_key
  secret_key = data.vault_aws_access_credentials.example.secret_key
  region     = &quot;ap-northeast-2&quot;
}</code></pre>
<ul>
<li>Terraform - Vault 연동 테스트<pre><code class="language-bash">terraform init
terraform plan</code></pre>
</li>
</ul>
<blockquote>
<p>보안을 위해 몇가지 빠진 부분이 있지만, Vault Cluster 구축 및 Terraform과 연동을 위한 분들께 도움이 되었으면 좋겠습니다..!</p>
</blockquote>
<h3 id="참고문헌">참고문헌</h3>
<p><a href="https://developer.hashicorp.com/vault/tutorials/getting-started/getting-started-install">Install Vault | Vault | HashiCorp Developer</a>
<a href="https://developer.hashicorp.com/vault/tutorials/raft/raft-deployment-guide">Vault with integrated storage deployment guide | Vault | HashiCorp Developer</a>
<a href="https://developer.hashicorp.com/vault/tutorials/day-one-raft/raft-deployment-guide">Vault with integrated storage deployment guide | Vault | HashiCorp Developer</a>
<a href="https://developer.hashicorp.com/vault/tutorials/day-one-raft/raft-reference-architecture">Vault with integrated storage reference architecture | Vault | HashiCorp Developer</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Hardware] RAID란? RAID의 구성방식과 종류]]></title>
            <link>https://velog.io/@berry_good/Hardware-RAID%EB%9E%80-RAID%EC%9D%98-%EA%B5%AC%EC%84%B1%EB%B0%A9%EC%8B%9D%EA%B3%BC-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@berry_good/Hardware-RAID%EB%9E%80-RAID%EC%9D%98-%EA%B5%AC%EC%84%B1%EB%B0%A9%EC%8B%9D%EA%B3%BC-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Tue, 29 Apr 2025 12:33:23 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>이번주 센터 업무를 진행하면서 서버 폐기, OS 재설치 등 오랜만에 하드웨어 관련업무를 진행했습니다.
OS 설치를 하며 RAID를 구성하던 중 기존에는 OS파티션은 RAID 1 데이터파티션은 RAID 6로 구성했었는데 왜 그렇게 구성을 진행했는지, 그리고 각 RAID의 차이점을 알아보기 위해 이 글을 작성합니다.</p>
</blockquote>
<h1 id="1-raid란">1. RAID란?</h1>
<ul>
<li><p><strong>RAID(Redundant Array of Independent Disks)</strong>는 여러 개의 하드디스크 드라이브(HDD) 또는 SSD를 하나의 논리적 장치처럼 구성하여 데이터 보호, 성능 향상 또는 두 가지 모두를 목표로 하는 기술</p>
</li>
<li><p><strong>RAID의 장점</strong></p>
<ul>
<li>성능향상</li>
<li>데이터 보호 및 내결함성 제공</li>
<li>서비스 연속성 유지</li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-raid의-구성방식">2. RAID의 구성방식</h1>
<p>RAID를 구성하는 방식에는 주로 3가지 방식이 사용됩니다.</p>
<ul>
<li><p><strong>Striping(스트라이핑)</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/5c276601-2545-4edf-aff6-db2a491e2f3f/image.png" alt=""></p>
<ul>
<li>데이터를 여러 디스크에 블록 단위로 나누어 분산 저장하는 기술</li>
<li>각 디스크에 데이터의 일부(스트립)를 동시에 기록하여 읽기/쓰기 성능을 크게 향상</li>
<li>RAID 0, RAID 5, RAID 6 등에서 사용</li>
<li>디스크 중 하나라도 고장 나면 전체 데이터가 손실될 수 있음</li>
</ul>
</li>
<li><p><strong>Mirroring(미러링)</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/02be3930-126d-4f38-914a-23b0f5670fef/image.png" alt=""></p>
<ul>
<li><p>동일한 데이터를 두 개 이상의 디스크에 복제하여 저장하는 방식</p>
</li>
<li><p>한 디스크에 장애가 발생해도 다른 디스크에 동일한 데이터가 있어 데이터 손실 없이 복구 가능</p>
</li>
<li><p>RAID 1, RAID 10 등에서 사용</p>
</li>
<li><p>저장 효율이 50%로 떨어져, 디스크 용량이 두 배로 필요</p>
</li>
<li><p><strong>Parity (패리티)</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/3ecb6775-f5b4-421f-bc80-162913c1afc5/image.png" alt=""></p>
<ul>
<li>데이터 보호를 위해 추가로 계산된 정보를 저장하는 방식</li>
<li>XOR 연산 등으로 생성된 패리티 비트를 별도의 디스크(혹은 분산) 저장하여, 한 개 또는 두 개의 디스크 장애 시에도 데이터 복구가 가능</li>
<li>RAID 3, RAID 4, RAID 5(분산 패리티), RAID 6(이중 패리티) 등에서 사용</li>
<li>패리티 정보는 전용 디스크에 저장하거나<strong>(전용 패리티)</strong>, 모든 디스크에 분산 저장<strong>(분산 패리티)</strong>할 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3-raid의-종류">3. RAID의 종류</h1>
<h2 id="raid-0---striping스트라이핑">RAID 0 - Striping(스트라이핑)</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/a7777ce8-6375-4218-9da9-1bb13fb4e936/image.png" alt=""></p>
<ul>
<li><strong>Striping(스트라이핑)</strong>이라고도 불리며, 데이터를 여러 디스크에 번갈아 가며 분산 저장하는 방식 </li>
<li>최소 2개 이상의 디스크가 필요하며, 중복성이나 오류 검출 기능이 없어 데이터 안정성은 가장 낮습니다.</li>
</ul>
<ul>
<li><strong>동작원리?</strong>
데이터는 일정한 크기의 블록(스트라이프)으로 나뉘어 순차적으로 각 디스크에 기록됩니다. 예를 들어, 2개의 디스크로 구성된 RAID 0 어레이에서 데이터 A는 디스크 1에, 데이터 B는 디스크 2에, 데이터 C는 디스크 1에, 데이터 D는 디스크 2에 기록되는 방식입니다. </li>
</ul>
<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 0</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>4TB</strong></p>
</blockquote>
<h2 id="raid-1---mirroring미러링">RAID 1 - Mirroring(미러링)</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/ab571034-5ebd-4773-a35b-87a3a3e2c1d3/image.png" alt=""></p>
<ul>
<li><p><strong>Mirroring(미러링)</strong>이라고 불리며, 데이터를 두 개 이상의 디스크에 완전히 동일하게 복제하여 저장하는 방식 
최소 2개의 디스크가 필요하며, 높은 데이터 안정성을 제공</p>
</li>
<li><p><strong>동작원리?</strong>
하나의 디스크에 데이터를 기록하면, 동시에 다른 모든 디스크에도 동일한 데이터가 기록됩니다. 따라서 모든 디스크는 동일한 데이터의 복사본을 갖게 됩니다</p>
</li>
</ul>
<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 1</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>2TB</strong></p>
</blockquote>
<h2 id="raid-2">RAID 2</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/e4295143-89cd-47aa-b57f-cce5ad455d25/image.png" alt=""></p>
<ul>
<li><p><strong>Bit-level Striping(비트 레벨 스트라이핑) + Hamming Code(ECC) 패리티</strong></p>
</li>
<li><p>디스크 최소 3개 이상 필요</p>
</li>
<li><p>데이터는 비트 단위로 스트라이핑되어 여러 데이터 디스크에 분산 저장되고, 오류 검출 및 수정을 위한 해밍 코드 패리티 정보는 별도의 디스크에 기록됩니다.</p>
</li>
<li><p>데이터를 비트 단위로 나누어 각 데이터 디스크에 순차적으로 저장하고, 이 데이터 블록에 대한 해밍 코드 패리티를 생성하여 전용 패리티 디스크에 저장</p>
</li>
<li><p><strong>실무에서는 거의 사용되지 않음</strong></p>
</li>
<li><p><strong>동작원리?</strong>
데이터는 비트 단위로 나뉘어 여러 데이터 디스크에 순차적으로 분산 저장 동시에, 이 데이터 블록에 대한 해밍 코드 패리티 정보가 생성되어 별도의 패리티 디스크 세트에 기록</p>
</li>
</ul>
<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 2</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ 데이터 Disk 2EA, ECC(오류검출) 2EA일 경우
<strong>2TB</strong>
➡️ 데이터 Disk 3EA, ECC(오류검출) 1EA일 경우
<strong>3TB</strong></p>
</blockquote>
<h2 id="raid-3">RAID 3</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/fef06759-db7c-43a8-95c5-85abcc22efb0/image.png" alt=""></p>
<ul>
<li><strong>바이트 레벨 스트라이핑(Byte-level Striping)</strong> + <strong>전용 패리티 디스크</strong></li>
<li>데이터를 바이트 단위로 스트라이핑</li>
<li>디스크 최소 3개 이상 필요</li>
<li><strong>동작원리?</strong>
데이터는 바이트 단위로 분할되어 여러 데이터 디스크에 순차적으로 기록됩니다. 각 스트라이프에 대한 패리티(Parity) 정보는 별도의 전용 디스크에 저장된다.</li>
</ul>
<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 3</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>3TB</strong></p>
</blockquote>
<h2 id="raid-4">RAID 4</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/f37e3635-1168-49fb-9165-079b1996be7a/image.png" alt=""></p>
<ul>
<li><strong>블록 레벨 스트라이핑(Block-level Striping)</strong> + <strong>전용 패리티 디스크</strong></li>
<li>디스크 최소 3개 이상 필요</li>
<li><strong>성능 이슈로 인해 실무에서는 잘 쓰이지 않는다.</strong></li>
<li><strong>동작원리?</strong>
데이터는 블록 단위로 분할되어 여러 데이터 디스크에 순차적으로 기록됩니다. 각 스트라이프에 대한 패리티 정보는 별도의 전용 패리티 디스크에 저장<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 4</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>3TB</strong></p>
</blockquote>
</li>
</ul>
<h2 id="raid-5">RAID 5</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/08929aa8-82db-4315-bfc3-451b50852a60/image.png" alt=""></p>
<ul>
<li><strong>블록 레벨 스트라이핑(Block-level Striping)</strong> + <strong>분산 패리티(Distributed Parity)</strong></li>
<li>디스크 최소 3개 이상 필요</li>
<li>데이터와 패리티 정보는 모든 디스크에 분산되어 저장</li>
<li>동작원리?
데이터를 블록 단위로 나누어 여러 데이터 디스크에 스트라이핑하고, 각 스트라이프에 대한 패리티 정보를 생성, 패리티 블록은 특정 전용 디스크가 아닌 각 디스크에 순차적으로 분산<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 5</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>3TB</strong></p>
</blockquote>
</li>
</ul>
<h2 id="raid-6">RAID 6</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/62629ed7-f962-4ebf-a2fe-6669c07b371b/image.png" alt=""></p>
<ul>
<li><strong>블록 레벨 스트라이핑(Block-level Striping)</strong> + <strong>이중 분산 패리티(Dual Distributed Parity)</strong></li>
<li>데이터와 두 개의 독립적인 패리티 정보가 모든 디스크에 분산되어 저장</li>
<li>최소 4개 이상의 디스크 필요</li>
<li><strong>동작원리?</strong>
데이터를 블록 단위로 나누어 여러 데이터 디스크에 스트라이핑하고, 각 스트라이프에 대해 두 개의 서로 다른 패리티 블록을 생성<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 6</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>2TB</strong></p>
</blockquote>
</li>
</ul>
<h2 id="raid-10-raid-10">RAID 10 (RAID 1+0)</h2>
<p><img src="blob:https://velog.io/bf12f2a9-5898-48ec-8b5f-cea4195cd2e7" alt="업로드중.."></p>
<ul>
<li><strong>RAID 1(미러링)</strong> + <strong>RAID 0(스트라이핑)</strong></li>
<li>최소 4개의 디스크 필요</li>
<li>먼저 데이터를 여러 디스크 쌍으로 미러링하고(RAID 1), 이 미러링된 디스크 쌍들을 스트라이핑(RAID 0)</li>
<li><strong>동작원리?</strong>
데이터를 먼저 두 개씩 짝지어 미러링하여 데이터의 복사본을 만들고, 이렇게 구성된 미러링된 디스크 쌍들을 하나의 그룹으로 묶어 RAID 0 방식으로 스트라이핑</li>
</ul>
<blockquote>
<p>[예시]
1TB 의 디스크 4개를 <strong>RAID 10</strong>으로 구성하게 된다면 총 사용할 수 있는 용량은?
➡️ <strong>2TB</strong></p>
</blockquote>
<hr>
<h1 id="4-raid-정리">4. RAID 정리</h1>
<p>앞서 말한 모든 내용을 간단하게 표로 정리해 보겠습니다.</p>
<table>
<thead>
<tr>
<th>RAID 레벨</th>
<th>최소 디스크 수</th>
<th>저장 용량 효율</th>
<th>내결함성 (허용 장애 수)</th>
<th>주요 특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>RAID 0</strong></td>
<td>2개</td>
<td>100%</td>
<td>❌ 없음 (1개만 고장나도 데이터 손실)</td>
<td>스트라이핑만 적용 → 속도 빠름, 복구 불가</td>
</tr>
<tr>
<td><strong>RAID 1</strong></td>
<td>2개</td>
<td>50%</td>
<td>✅ 1개 (미러링된 디스크)</td>
<td>미러링 → 안정성 높음, 용량 절반만 사용</td>
</tr>
<tr>
<td><strong>RAID 2</strong></td>
<td>3개 이상</td>
<td>↓ 낮음</td>
<td>✅ 다수 (Hamming Code 기반)</td>
<td>바이트 단위 스트라이핑 + ECC / 거의 사용 안 함</td>
</tr>
<tr>
<td><strong>RAID 3</strong></td>
<td>3개</td>
<td>(N-1)/N</td>
<td>✅ 1개</td>
<td>바이트 단위 스트라이핑 + 전용 패리티 디스크</td>
</tr>
<tr>
<td><strong>RAID 4</strong></td>
<td>3개</td>
<td>(N-1)/N</td>
<td>✅ 1개</td>
<td>블록 단위 스트라이핑 + 전용 패리티 디스크 (병목 발생)</td>
</tr>
<tr>
<td><strong>RAID 5</strong></td>
<td>3개</td>
<td>(N-1)/N</td>
<td>✅ 1개</td>
<td>블록 단위 스트라이핑 + 분산 패리티, 성능 균형</td>
</tr>
<tr>
<td><strong>RAID 6</strong></td>
<td>4개</td>
<td>(N-2)/N</td>
<td>✅ 2개</td>
<td>RAID 5 + 추가 패리티, 고신뢰성</td>
</tr>
<tr>
<td><strong>RAID 10</strong></td>
<td>4개 (짝수 개수)</td>
<td>50%</td>
<td>✅ 1개 이상 (미러링마다 1개 허용)</td>
<td>RAID 1 + 0: 속도+안정성 우수, 고비용</td>
</tr>
</tbody></table>
<hr>
<p>참고: <a href="https://www.prepressure.com/library/technology/raid">https://www.prepressure.com/library/technology/raid</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Hardware] CPU, Core, Thread의 개념]]></title>
            <link>https://velog.io/@berry_good/Hardware-CPU-Core-Thread%EC%9D%98-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@berry_good/Hardware-CPU-Core-Thread%EC%9D%98-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 15 Apr 2025 11:18:53 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>저번 글에는 CPU의 Architecture에 대해 알아보았습니다
그러면 실제 서버나 노트북을 구매할때 어떤 CPU를 구입해야 운영하는 서비스에 알맞은 성능을 발휘 할 수 있는지 알려면, CPU 구성요소에 대해 조금 더 알아보며 명령어까지 학습하면 좋을 것 같습니다.</p>
</blockquote>
<hr>
<h1 id="1-cpu란">1. CPU란?</h1>
<ul>
<li><p><strong>CPU</strong> : 컴퓨터에서 모든 연산과 명령을 처리하는 핵심 장치</p>
</li>
<li><p><strong>CPU의 기능?</strong></p>
<ul>
<li>명령어를 해석하고 실행</li>
<li>산술 연산, 논리 연산, 입출력 제어</li>
</ul>
</li>
<li><p>물리적인 CPU 개수 확인 명령어</p>
<pre><code class="language-bash">grep &quot;physical id&quot; /proc/cpuinfo | sort -u | wc -l</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/8e4adf74-d19e-4d04-b2ac-0aed7c5c82f9/image.png" alt=""></p>
</li>
</ul>
<p>위 사진에서 &quot;물리적인&quot; CPU의 갯수는 1개 입니다.</p>
<h1 id="2-core란">2. Core란?</h1>
<ul>
<li><p><strong>Core</strong> : CPU 내부의 처리 유닛으로, 하나의 코어가 하나의 명령어 흐름을 처리할 수 있으며, 하나의 물리적인 칩 아에 여러개의 Core가 존재 가능합니다.</p>
</li>
<li><p><strong>Core의 기능?</strong></p>
<ul>
<li>각 Core는 독립적으로 명령어 처리 가능</li>
<li>멀티코어라면 동시에 여러 작업을 병렬로 처리 가능</li>
</ul>
</li>
<li><p>Core의 개수 확인 명령어</p>
<pre><code class="language-bash">grep &quot;core id&quot; /proc/cpuinfo | sort | uniq | wc -l</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/ce63103d-1906-4bea-a0d2-64894b6488b6/image.png" alt=""></p>
</li>
</ul>
<p>위 사진에서 확인한 Core의 개수는 4개 입니다.</p>
<h1 id="3-thread란">3. Thread란?</h1>
<ul>
<li><p><strong>Thread</strong> : Core가 동시에 처리할 수 있는 작업 흐름 단위</p>
</li>
<li><p><strong>Thread의 기능?</strong></p>
<ul>
<li>Core 내에서 동시성 향상</li>
<li>Thread는 하나의 프로그램 내부에서 실행되는 작은 단위</li>
</ul>
</li>
<li><p>Thread의 개수 확인 명령어</p>
<pre><code class="language-bash">grep &quot;processor&quot; /proc/cpuinfo | wc -l</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/4bbc7e21-05a6-42f4-ac4a-9ec321d3c0d0/image.png" alt=""></p>
</li>
</ul>
<p>위 사진에서 확인한 Thread의 개수는 8개 입니다.</p>
<blockquote>
<p>1개의 물리 Core에서 여러개의 Thread를 쓸 수 있는 이유는
<strong>Hyper-Threading == SMT(Simultaneous Multi-Threading)</strong>이 있어 가능합니다!
<strong>Intel</strong> → Hyper-Threading
<strong>AMD</strong> → SMT(Simultaneous Multi-Threading)</p>
</blockquote>
<hr>
<h1 id="4-cpu-vs-core-vs-thread">4. CPU vs Core vs Thread</h1>
<p>위의 내용을 종합해 보자면
물리적인 CPU는 1개, Core는 4개 Thread는 8개이므로 
1개의 Core 당 2개의 Thread가 가용하다고 볼 수 있습니다.</p>
<p>한번에 볼 수 있는 명령어는</p>
<pre><code class="language-bash">lscpu | egrep &quot;Socket|Core|Thread&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/f0fa83e2-033f-4c66-a518-57b06d56159f/image.png" alt=""></p>
<ul>
<li>CPU, Core, Thread를 비교한 표를 아래에 적어두겠습니다.</li>
</ul>
<table>
<thead>
<tr>
<th><strong>특징</strong></th>
<th><strong>CPU</strong></th>
<th><strong>Core</strong></th>
<th><strong>Thread</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>정의</strong></td>
<td>컴퓨터의 두뇌, 명령어 해석 및 실행의 핵심 하드웨어 장치</td>
<td>CPU 내부에 있는 독립적인 연산 처리 장치 (물리적 단위)</td>
<td>소프트웨어적인 작업 흐름 또는 실행 단위 (논리적 단위)</td>
</tr>
<tr>
<td><strong>역할</strong></td>
<td>명령어 처리, 데이터 연산, 시스템 제어, 주소 지정 등</td>
<td>독립적인 명령어 스트림 실행, 병렬 처리</td>
<td>코어 내에서 동시성 향상, 멀티태스킹 성능 향상</td>
</tr>
<tr>
<td><strong>물리적/논리적</strong></td>
<td>물리적인 하드웨어 칩</td>
<td>물리적인 하드웨어 단위</td>
<td>논리적인 실행 단위 (하드웨어적으로 지원될 수 있음)</td>
</tr>
<tr>
<td><strong>독립성</strong></td>
<td>전체 시스템의 작동을 총괄</td>
<td>각 코어는 독립적으로 작업 수행 가능</td>
<td>하나의 코어 내에서 여러 스레드가 번갈아 가며 또는 동시에 실행</td>
</tr>
<tr>
<td><strong>성능 영향</strong></td>
<td>전체 시스템 성능의 가장 중요한 요소</td>
<td>멀티태스킹 및 병렬 처리 성능에 큰 영향</td>
<td>코어의 효율성을 높여 전반적인 처리량 향상에 기여</td>
</tr>
<tr>
<td><strong>수량</strong></td>
<td>일반적으로 시스템에 1개</td>
<td>CPU 칩 내에 1개 이상 존재 가능 (멀티 코어)</td>
<td>코어당 1개 또는 2개 이상 존재 가능 (하이퍼스레딩/SMT 지원 시)</td>
</tr>
</tbody></table>
<blockquote>
<p>위 CPU를 그림으로 그려보자면 아래의 사진처럼 구성되있음을 알 수 있습니다.</p>
</blockquote>
<ul>
<li><strong>예시 사진</strong>
<img src="https://velog.velcdn.com/images/berry_good/post/c85f102c-bcb9-489d-bcc1-3441bc819e06/image.png" alt=""></li>
</ul>
<hr>
<h1 id="5-cpu의-성능">5. CPU의 성능</h1>
<p>그러면 궁금증이 생깁니다. Core수가 많고, Thread가 많으면 무조건 좋은 CPU일까요?</p>
<blockquote>
<p>반은 맞고 반은 틀린 이야기 입니다.</p>
</blockquote>
<p>CPU의 성능을 결정하는 요소는 많은 것들이 있는데, 그 중 대표적인 몇가지만 알아보겠습니다.</p>
<ul>
<li>Core</li>
<li>Thread</li>
<li>클럭(Clock) Speed</li>
<li>IPC</li>
<li>Cache Memory</li>
</ul>
<p>Core와 Thread는 앞에서 서술했기 때문에, Clock과 Cache Memory에 대해 알아보겠습니다</p>
<h3 id="clock">Clock?</h3>
<ul>
<li>CPU가 초당 몇 번의 명령어 사이클(Clock Cycle)을 실행할 수 있는지를 나타낸다.</li>
<li>단위는 Hz(헤르츠)로 표현한다.</li>
<li>1Hz(헤르츠)는 1초에 1번의 Clock Cycle을 의미한다.</li>
</ul>
<p>현재 서버의 Clock을 확인하려면 </p>
<pre><code class="language-bash">lscpu</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/25268c64-7abb-47d6-be99-001cf3178270/image.png" alt=""></p>
<p>위의 사진처럼 2400MHz = 2.4GHz 초당 약 24억 Cycle을 돌 수 있습니다.</p>
<blockquote>
<p><strong>Clock Speed ↑ / 성능 ↑</strong></p>
</blockquote>
<h3 id="ipc">IPC?</h3>
<ul>
<li>IPC(Instructions Per Cycle)는 1클럭 사이클당 처리할 수 있는 명령어 수를 의미한다.</li>
<li>IPC = 초당 실행된 명령어 수 / 초당 클럭 사이클 수</li>
</ul>
<p>위의 언급된 것처럼 Clock Speed가 빠르면 무조건 좋을까요?
아래의 예시를 보며 판단해보시죠!</p>
<ul>
<li>예시</li>
<li><em>CPU A*</em>: 4GHz, IPC 1.0 → 4G instr/sec</li>
<li><em>CPU B*</em>: 3GHz, IPC 2.0 → 6G instr/sec</li>
</ul>
<p>A가 B보다 Clock Speed는 좋지만 IPC가 낮아 <strong>CPU B</strong>가 더 우수한 성능을 보이며
IPC가 높을 수록 성능이 좋은 것을 알 수 있습니다!</p>
<blockquote>
<p><strong>IPC ↑ / 성능 ↑</strong></p>
</blockquote>
<h3 id="cache-memory">Cache Memory?</h3>
<ul>
<li>CPU 내부 또는 가까이에 위치한 임시 저장 공간</li>
<li>자주 사용하는 데이터나 명령어를 미리 저장해두고, CPU가 빠르게 접근하도록 합니다.</li>
</ul>
<p>Cache Memory도 계층적 구조로 나뉘게 됩니다. </p>
<table>
<thead>
<tr>
<th><strong>계층</strong></th>
<th><strong>위치</strong></th>
<th><strong>용량</strong></th>
<th><strong>속도</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>L1 Cache</td>
<td>CPU Core 내</td>
<td>수십 KB</td>
<td>매우 빠름 (1~2ns)</td>
<td>데이터/명령어 분리됨 (L1d, L1i)</td>
</tr>
<tr>
<td>L2 Cache</td>
<td>CPU Core/Die</td>
<td>수백 KB ~ 1MB</td>
<td>빠름 (3~10ns)</td>
<td>통합 캐시</td>
</tr>
<tr>
<td>L3 Cache</td>
<td>CPU Core 외부</td>
<td>수 MB ~ 수십 MB</td>
<td>느림 (10~20ns)</td>
<td>모든 코어가 공유</td>
</tr>
</tbody></table>
<p>L1 → L2 → L3 → 주기억장치 순으로 탐색하며 Cache Hit율이 올라갈 수록 성능은 향상됩니다.
<img src="https://velog.velcdn.com/images/berry_good/post/afc9c7c6-1f76-4f7b-9194-9a1562b8f622/image.png" alt=""></p>
<p>위의 그림을 보면
L1d cache(데이터 캐시) : 32KiB
L1i cache(명령어 캐시) : 32KiB
L2 cache : 256KiB
L3 cache : 12288KiB</p>
<p>을 알 수 있습니다!</p>
<p>마찬가지로 Cache 크기가 커지면 성능도 좋아지겠죠?</p>
<blockquote>
<p><strong>Cache Memory ↑ / 성능 ↑</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Hardware] CPU Architecture]]></title>
            <link>https://velog.io/@berry_good/Hardware-CPU-Architecture</link>
            <guid>https://velog.io/@berry_good/Hardware-CPU-Architecture</guid>
            <pubDate>Thu, 20 Mar 2025 09:36:19 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>서비스를 운영하면서 java 버전을 업그레이드 해달라는 개발팀의 요청이 있었습니다. 특정 버전의 java를 찾으면서 당황했는데 그 이유는...?  </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/berry_good/post/8efd3d01-258a-4e98-a992-46067df5cd83/image.png" alt=""></p>
<blockquote>
<p>위와 같이 여러개의 Architecture가 있었고, 현재 운영중인 서버 Architecture에 맞춰  java 버전 업그레이드는 진행했지만 정확히 CPU Architecture가 무엇인지 모르는 나 자신을 발견하여... 자세히 알아보기로 했습니다...ㅎ
<del>(AMD에 주식 투자하는건 안비밀)</del></p>
</blockquote>
<h1 id="1-cpu-architecture란">1. CPU Architecture란?</h1>
<p>중앙처리장치(CPU)의 설계 방식과 <strong>명령어 집합(Instruction Set Architecture, ISA)</strong>을 정의하는 구조를 의미합니다.</p>
<ul>
<li><strong>ISA(Instruction Set Architecture)?</strong>
CPU가 이해하고 실행할 수 있는 명령어의 집합 (예: x86, ARM)</li>
</ul>
<p>그런데! 같은 ISA라도 각 벤더사에 따라 성능이 달라지게 됩니다.
ex) Intel x86 vs AMD x86</p>
<p>그 이유는 각 벤더사마다 <strong>Microarchitecture (미세 아키텍처)</strong> 가 다르기 때문에 성능에도 차이가 발생하는 것입니다.</p>
<ul>
<li><strong>Microarchitecture?</strong>
ISA를 구현하는 방식 (예: Intel Core, AMD Zen)</li>
</ul>
<hr>
<h1 id="2-cpu-architecture의-종류">2. CPU Architecture의 종류</h1>
<p>그러면 현재 사용되는 Architecture의 종류들은 대표적으로 어떤 것이 있을까요? </p>
<ul>
<li>CISC(Complex Instruction Set Computing)
복잡한 명령어를 사용하여 한 번의 명령어로 여러 작업을 수행
(예: x86, x86-64{amd64}, x64)</li>
</ul>
<ul>
<li>RISC (Reduced Instruction Set Computing)
단순하고 짧은 명령어를 사용하여 실행 속도를 높임
(예: ARM, RISC-V)</li>
</ul>
<p>아래 표는 각 Architecture 별 자주 사용되는 종류를 정리한 표입니다.</p>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th><strong>x86 (CISC)</strong></th>
<th><strong>ARM (RISC)</strong></th>
<th><strong>RISC-V (RISC)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>설계 철학</strong></td>
<td>복잡한 명령어 사용</td>
<td>단순한 명령어 사용</td>
<td>오픈소스 RISC 구조</td>
</tr>
<tr>
<td><strong>전력 효율</strong></td>
<td>낮음 (고전력)</td>
<td>높음 (저전력)</td>
<td>매우 높음</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>고성능</td>
<td>중간~고성능</td>
<td>확장 가능</td>
</tr>
<tr>
<td><strong>가격</strong></td>
<td>높음</td>
<td>낮음</td>
<td>낮음</td>
</tr>
<tr>
<td><strong>사용 환경</strong></td>
<td>데스크톱, 서버, 게이밍 PC</td>
<td>스마트폰, 태블릿, IoT</td>
<td>임베디드, 서버, AI</td>
</tr>
<tr>
<td><strong>대표 CPU</strong></td>
<td>Intel Core, AMD Ryzen</td>
<td>Apple M3, Qualcomm Snapdragon</td>
<td>SiFive, StarFive</td>
</tr>
</tbody></table>
<hr>
<h1 id="3-cpu-architecture별-사용-사례">3. CPU Architecture별 사용 사례</h1>
<h4 id="데스크톱--게이밍-pc"><strong>데스크톱 &amp; 게이밍 PC</strong></h4>
<ul>
<li>사용 아키텍처: <strong>x86-64 (CISC)</strong></li>
<li>사용 CPU: <strong>Intel Core, AMD Ryzen</strong></li>
<li>특징: 강력한 연산 능력, 고성능 그래픽 지원.</li>
</ul>
<h4 id="스마트폰--태블릿"><strong>스마트폰 &amp; 태블릿</strong></h4>
<ul>
<li>사용 아키텍처: <strong>ARM (RISC)</strong></li>
<li>사용 CPU: <strong>Apple M3, Snapdragon, MediaTek, Exynos</strong></li>
<li>특징: 저전력 &amp; 배터리 효율 최적화.</li>
</ul>
<h4 id="서버--데이터-센터"><strong>서버 &amp; 데이터 센터</strong></h4>
<ul>
<li>사용 아키텍처: <strong>x86-64 (CISC), ARM (RISC)</strong></li>
<li>사용 CPU: <strong>Intel Xeon, AMD EPYC, AWS Graviton</strong></li>
<li>특징: 고성능 병렬 처리, 클라우드 컴퓨팅 지원.</li>
</ul>
<h4 id="인공지능--머신러닝"><strong>인공지능 &amp; 머신러닝</strong></h4>
<ul>
<li>사용 아키텍처: <strong>ARM (RISC), RISC-V</strong></li>
<li>사용 CPU: <strong>NVIDIA Grace, Google TPU, Apple Neural Engine</strong></li>
<li>특징: AI 연산 가속, 저전력 최적화.</li>
</ul>
<h4 id="임베디드-시스템--iot"><strong>임베디드 시스템 &amp; IoT</strong></h4>
<ul>
<li>사용 아키텍처: <strong>ARM (RISC), RISC-V</strong></li>
<li>사용 CPU: <strong>Raspberry Pi, ESP32, STM32</strong></li>
<li>특징: 소형 &amp; 저전력 최적화.</li>
</ul>
<hr>
<p>여담으로 여러분이 사용하고 있는 CPU의 Architecure를 확인해보며 개념을 이해하면 더 좋을 것 같습니다.</p>
<ul>
<li><p>노트북 Architecture 확인 방법
시스템 → 정보 → 장치사양
<img src="https://velog.velcdn.com/images/berry_good/post/051eb381-3218-414e-a2ae-e5ffa2441730/image.png" alt="">
<img src="https://velog.velcdn.com/images/berry_good/post/b5f7a77d-ba15-4119-a69a-37ec8f70d8b7/image.png" alt="">
노트북은 x64 Architecture를 사용하고 있네요!!</p>
</li>
<li><p>서버(Linux) Architecture 확인방법</p>
<pre><code class="language-bash">uname -m</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/4c83ebda-3323-4edf-8f31-1fd0f1d0f0fb/image.png" alt="">
서버는 x86_64를 사용하고 있었습니다! : )</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 파일/폴더 권한 설정]]></title>
            <link>https://velog.io/@berry_good/Linux-%ED%8C%8C%EC%9D%BC%ED%8F%B4%EB%8D%94-%EA%B6%8C%ED%95%9C-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@berry_good/Linux-%ED%8C%8C%EC%9D%BC%ED%8F%B4%EB%8D%94-%EA%B6%8C%ED%95%9C-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 13 Mar 2025 10:17:49 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>Linux를 사용하면서 파일 권한 문제로 인해 프로세스가 정상적으로 동작하지 않거나, 적절한 권한을 부여해야 하지만, 모든 권한을 부여하는 상황이 있을 것 입니다. 
이 기회에 파일 권한에 대한 내용을 완벽히 이해하여 각 조건에 맞는 권한을 부여하는 능력을 키워보겠습니다.</p>
</blockquote>
<h2 id="🖥️-테스트-환경">🖥️ 테스트 환경</h2>
<ul>
<li>Linux</li>
<li>Ubuntu 22.04 LTS</li>
</ul>
<h1 id="1-파일-권한의-구성-요소">1. 파일 권한의 구성 요소</h1>
<ul>
<li>읽기(<strong>R</strong>ead)</li>
<li>쓰기(<strong>W</strong>rite)</li>
<li>실행(E<strong>x</strong>cute)</li>
<li>없음(No Access)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/4e495811-99e0-4240-94e1-a8b71f9b1202/image.png" alt=""></p>
<p>리눅스에서는 파일 권한이 <strong>10</strong>개의 문자로 구성된 표기로 나타납니다.</p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/4ae391c6-bbb6-460f-a365-a4c2e0b778ee/image.png" alt=""></p>
<h2 id="권한-확인-명령어">권한 확인 명령어</h2>
<pre><code class="language-bash">ls -al</code></pre>
<h3 id="예시">예시</h3>
<p><img src="https://velog.velcdn.com/images/berry_good/post/78ea0948-da9a-4239-b7b7-a94a4e6f120e/image.png" alt=""></p>
<p>아래와 같이 권한이 나올때는</p>
<pre><code class="language-bash">-rw-r--r--</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/8c6d77ce-81e6-4a3b-a6b6-dcfa6888c1c4/image.png" alt=""></p>
<p>일반파일 + <strong>읽기 쓰기</strong> 가능 (소유자) + <strong>읽기</strong> 가능 (그룹) + <strong>읽기</strong> 가능(기타 사용자) </p>
<h1 id="2-숫자-형태의-권한">2. 숫자 형태의 권한</h1>
<p>위에는 *<em>r , w , x , - *</em> 의 4가지 형태로 10자리 표현이 되었다면, 숫자 형태는 3자리로 표현됩니다
<img src="https://velog.velcdn.com/images/berry_good/post/9de28794-0454-45ea-8d29-a5a98c0072fe/image.png" alt=""></p>
<p><strong>r</strong> : 4 , <strong>w</strong> : 2 <strong>x</strong> : 1 로 표현이 되는데, 아래 예시를 보며 기호 → 숫자로 변환해 보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/berry_good/post/65fb08ae-32b9-44aa-813a-3836fafa05fc/image.png" alt=""></p>
<h1 id="3-파일-권한-변경-명령어">3. 파일 권한 변경 명령어</h1>
<h2 id="chmod">chmod</h2>
<p>기호를 통해 특정 권한을 부여하거나 제거 할 수 있습니다.
<img src="https://velog.velcdn.com/images/berry_good/post/76f42a2f-7a91-4aa8-8347-06806754b96c/image.png" alt=""></p>
<h3 id="예시-1">예시</h3>
<pre><code class="language-bash">chmod u+x test.sh  # 1번  소유자(user)에게 실행(x) 권한 추가
chmod u-w test.sh  # 2번 그룹(group)에서 쓰기(w) 권한 제거
chmod o=x test.sh  # 3번 기타 사용자(others)에게 읽기(r)만 부여
chmod ugo=rwx test.sh  # 4번 모든 사용자에게 읽기, 쓰기, 실행 권한 부여</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/12972ea8-ef4f-49a0-899e-ad946bc26fe5/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[VPN 서버 구축(feat. openvpn)]]></title>
            <link>https://velog.io/@berry_good/VPN-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95feat.-openvpn</link>
            <guid>https://velog.io/@berry_good/VPN-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95feat.-openvpn</guid>
            <pubDate>Tue, 25 Feb 2025 10:33:30 GMT</pubDate>
            <description><![CDATA[<h1 id="0-도입">0. 도입</h1>
<blockquote>
<p>실무를 하면서 VPN을 구축할 일이 생겼습니다. 
원격으로 상용서버를 접속 할 때 Any IP를 오픈하면 안되니 하나의 접속 포인트에서 상용서버들을 접속하는게 보안상 안전할 것입니다.</p>
</blockquote>
<p>먼저 VPN 서버를 구축 하기 전에 VPN이 뭔지 간단하게 개념을 짚고 넘어가겠습니다.</p>
<h2 id="🤔vpn이란">🤔VPN이란?</h2>
<p>VPN(가상 사설 네트워크, Virtual Private Network)은 공용 네트워크를 통해 데이터를 전송할 때, 사용자의 연결을 암호화하고 보안을 강화해 주는 기술입니다. 이를 통해 인터넷 연결이 보다 안전해지고, 사용자의 프라이버시를 보호할 수 있습니다.</p>
<h2 id="👍vpn의-장점">👍VPN의 장점?</h2>
<ol>
<li>데이터 암호화</li>
<li>익명성 보호</li>
<li>지역 차단 우회</li>
<li>안전한 원격 접속</li>
</ol>
<hr>
<h1 id="1-openvpn-및-easy-rsa-설치">1. OpenVPN 및 Easy-RSA 설치</h1>
<ul>
<li>OpenVPN, Easy-RSA 설치<pre><code class="language-bash">sudo apt-get install update
sudo apt-get install openvpn easy-rsa</code></pre>
</li>
<li>폴더 생성 및 심볼릭 링크 생성<pre><code class="language-bash"># 폴더 생성
mkdir ~/easy-rsa
# 심볼릭 링크 생성
ln -s /usr/share/easy-rsa/* ~/easy-rsa/</code></pre>
<ul>
<li>결과화면
<img src="https://velog.velcdn.com/images/berry_good/post/64a80242-5840-4a38-8b46-e549558a4aff/image.png" alt=""></li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-openvpn용-pki-만들기">2. OpenVPN용 PKI 만들기</h1>
<blockquote>
<p><strong>PKI란?</strong> --&gt; <strong>PKI</strong>(Public Key Infrastructure)는 공개 키 암호화 기술을 활용하여 네트워크 상에서 보안성과 신뢰성을 제공하는 체계</p>
</blockquote>
<ul>
<li><p>vars 파일 생성 및 수정</p>
<pre><code class="language-bash"># 폴더로 이동
cd easy-rsa

# vars 파일 생성
vi vars

# 세팅 값 붙여넣기
set_var EASYRSA_ALGO &quot;ec&quot;
set_var EASYRSA_DIGEST &quot;sha512&quot;</code></pre>
<ul>
<li>결과화면
<img src="https://velog.velcdn.com/images/berry_good/post/da2ea99e-8e39-4c55-9f24-1ebe8085a161/image.png" alt=""></li>
</ul>
</li>
<li><p>PKI 생성</p>
<pre><code class="language-bash">  ./easyrsa init-pki</code></pre>
</li>
</ul>
<hr>
<h1 id="3-openvpn용-서버-인증서-요청-및-개인키-생성">3. OpenVPN용 서버 인증서 요청 및 개인키 생성</h1>
<ul>
<li><p>인증서 생성</p>
<pre><code class="language-bash">cd /easy-rsa

./easyrsa build-ca nopass
-&gt; Enter
./easyrsa gen-req server nopass
-&gt; Enter</code></pre>
<ul>
<li>결과
<img src="https://velog.velcdn.com/images/berry_good/post/066bf39a-4e9b-4ca0-a2cf-7fb626d94e1d/image.png" alt=""><img src="https://velog.velcdn.com/images/berry_good/post/d89d1bb2-7fde-4284-aaa2-58085a4d8642/image.png" alt=""><pre><code class="language-bash">sudo cp /home/ubuntu/easy-rsa/pki/private/server.key /etc/openvpn/server/</code></pre>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="4-openvpn-서버-인증서-요청-서명-및-암호화-자료-구성">4. OpenVPN 서버 인증서 요청 서명 및 암호화 자료 구성</h1>
<ul>
<li><p>인증서 서명 및 암호화 자료 구성</p>
<pre><code class="language-bash">sudo cp ~/easy-rsa/pki/private/server.key /etc/openvpn/server/

# 인증서 서명
./easyrsa sign-req server server
-&gt; yes

sudo cp ~/easy-rsa/pki/issued/server.crt /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/ca.crt /etc/openvpn/server/</code></pre>
<ul>
<li><img src="https://velog.velcdn.com/images/berry_good/post/8a7bcbc8-6f4f-4511-a417-3364721ce271/image.png" alt=""></li>
</ul>
</li>
<li><p>Diffie-Hellman key 생성 및 ta.key 생성</p>
<pre><code class="language-bash"># Diffie-Hellman key 생성
./easyrsa gen-dh

# ta.key 생성
sudo openvpn --genkey --secret ta.key

# ta.key, dh.pem 파일 복사
sudo cp ~/easy-rsa/ta.key /etc/openvpn/server/
sudo cp ~/easy-rsa/pki/dh.pem /etc/openvpn/server/</code></pre>
<hr>
<h1 id="5-클라이언트-인증서-및-키-쌍-생성">5. 클라이언트 인증서 및 키 쌍 생성</h1>
</li>
<li><p>VPN Clinet 계정 만들기</p>
<pre><code class="language-bash"># 폴더 생성 및 권한 부여
mkdir -p ~/client-configs/keys
chmod -R 777 ~/client-configs

# 계정 인증서 생성
./easyrsa gen-req 유저명 nopass
-&gt; Enter</code></pre>
<ul>
<li><img src="https://velog.velcdn.com/images/berry_good/post/7fc7db7d-1fb6-4985-bf49-d987bffb299b/image.png" alt=""></li>
</ul>
</li>
<li><p>파일 복사</p>
<pre><code class="language-bash"># 키 파일 이동
sudo cp pki/private/유저명.key ~/client-configs/keys/

# 인증서 생성
./easyrsa sign-req client 유저명
-&gt; yes

sudo cp pki/issued/유저명.crt ~/client-configs/keys/</code></pre>
</li>
<li><p>ta.key 복사</p>
<pre><code class="language-bash">sudo cp ~/easy-rsa/ta.key ~/client-configs/keys/
sudo cp ta.key /etc/openvpn/server
sudo cp /etc/openvpn/server/ca.crt ~/client-configs/keys/

# 권한 부여
sudo chmod -R 777 ~/client-configs</code></pre>
<h1 id="6-openvpn-설정">6. OpenVPN 설정</h1>
</li>
<li><p>Server.conf 수정</p>
<pre><code class="language-bash"># 파일 이동
sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf  /etc/openvpn/server

# Server.conf 수정
sudo vi /etc/openvpn/server/server.conf</code></pre>
</li>
<li><p>server.conf</p>
<blockquote>
<p>일부 설정은 제외 하였습니다.</p>
</blockquote>
<pre><code class="language-bash">
  ;local a.b.c.d

  port 1194

  # TCP or UDP server?
  ;proto tcp
  proto udp

  dev tun

  ca /etc/openvpn/server/ca.crt
  cert /etc/openvpn/server/server.crt
  key /etc/openvpn/server/server.key  # This file should be kept secret

  dh /etc/openvpn/server/dh.pem
  topology subnet

  server 10.8.0.0 255.255.255.0

  ifconfig-pool-persist /var/log/openvpn/ipp.txt

  ;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100

  ;server-bridge

  push &quot;route 192.168.10.0 255.255.255.0&quot;
  push &quot;route 192.168.20.0 255.255.255.0&quot;

  ;client-config-dir ccd
  ;route 192.168.40.128 255.255.255.248
  ;client-config-dir ccd
  ;route 10.9.0.0 255.255.255.252
  ;learn-address ./script

  push &quot;redirect-gateway def1 bypass-dhcp&quot;

  push &quot;dhcp-option DNS 8.8.8.8&quot;
  push &quot;dhcp-option DNS 1.1.1.1&quot;

  ;client-to-client

  ;duplicate-cn

  keepalive 10 120

  tls-auth ta.key 0
  key-direction 0
  cipher AES-256-GCM
  auth SHA256

  ;compress lz4-v2
  ;push &quot;compress lz4-v2&quot;

  ;comp-lzo

  ;max-clients 100

  user nobody
  group nogroup

  persist-key
  persist-tun
  status /var/log/openvpn/openvpn-status.log

  log         /var/log/openvpn/openvpn.log
  log-append  /var/log/openvpn/openvpn.log

  verb 3

  ;mute 20

  explicit-exit-notify 1

  #### OTP 설정 ####
  reneg-sec 0
  plugin openvpn-plugin-auth-pam.so &quot;openvpn login USERNAME password PASSWORD &#39;verification code&#39; OTP&quot;
  verify-client-cert none
  username-as-common-name</code></pre>
</li>
</ul>
<hr>
<h1 id="7-openvpn-서버-네트워킹-및-방화벽-구성-변경">7. OpenVPN 서버 네트워킹 및 방화벽 구성 변경</h1>
<ul>
<li><p>네트워크 구성 변경</p>
<pre><code class="language-bash">sudo vi /etc/sysctl.conf

# 주석 해제
net.ipv4.ip_forward=1</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/cf32b2af-2f65-49f4-af8e-1314bf0711b9/image.png" alt=""></p>
<ul>
<li>결과확인<pre><code>sudo sysctl -p</code></pre></li>
<li><img src="https://velog.velcdn.com/images/berry_good/post/ae5d0a50-5004-49bf-85fa-f62fe1d35821/image.png" alt=""></li>
</ul>
</li>
<li><p>네트워크 NAT 설정</p>
<ul>
<li>네트워크 인터페이스 조회<pre><code class="language-bash">ip route list default</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/c0711877-92a8-4769-8244-36d643e7c32d/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li><p>자신의 인터페이스 nat 설정</p>
<pre><code class="language-bash">vi /etc/iptables/rules.v4

# 추가
##### NAT #####
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o ens3 -j MASQUERADE
COMMIT</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/bf301289-4172-404f-a527-25cfa481916f/image.png" alt=""></p>
<ul>
<li>결과확인<pre><code class="language-bash">iptables -t nat -L</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/1e0bddda-6c5e-48f4-ad26-4458c84d2acd/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>적용<pre><code>iptables-restore &lt; /etc/iptables/rules.v4</code></pre></li>
</ul>
<h1 id="8openvpn-시작-및-client-구성">8.OpenVPN 시작 및 Client 구성</h1>
<ul>
<li>OpenVPN 시작<pre><code class="language-bash">sudo systemctl -f enable openvpn-server@server.service
sudo systemctl start openvpn-server@server.service</code></pre>
</li>
<li>VPN 상태 확인<pre><code class="language-bash">systemctl status openvpn-server@server</code></pre>
<img src="https://velog.velcdn.com/images/berry_good/post/b19ad390-e655-47a0-bbfe-999fa40a9d2e/image.png" alt=""></li>
</ul>
<ul>
<li><p>Client 추가 구성</p>
<pre><code class="language-bash"># 폴더 생성
mkdir -p ~/client-configs/files

# 샘플 파일 복사
sudo cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf

sudo vi ~/client-configs/base.conf</code></pre>
</li>
</ul>
<blockquote>
<p>자신의 환경에 맞게 base.conf를 수정하시면 됩니다!</p>
</blockquote>
<ul>
<li><p>Simple Script(make_config.sh) 작성</p>
<pre><code class="language-bash">vi ~/client-configs/make_config.sh

# make_config.sh

#!/bin/bash

# First argument: Client identifier

KEY_DIR=/home/ubuntu/client-configs/keys
OUTPUT_DIR=/home/ubuntu/client-configs/files
BASE_CONFIG=/home/ubuntu/client-configs/base.conf

cat ${BASE_CONFIG} \
      &lt;(echo -e &#39;&lt;ca&gt;&#39;) \
      ${KEY_DIR}/ca.crt \
      &lt;(echo -e &#39;&lt;/ca&gt;\n&lt;cert&gt;&#39;) \
      ${KEY_DIR}/${1}.crt \
      &lt;(echo -e &#39;&lt;/cert&gt;\n&lt;key&gt;&#39;) \
      ${KEY_DIR}/${1}.key \
      &lt;(echo -e &#39;&lt;/key&gt;\n&lt;tls-auth&gt;&#39;) \
      ${KEY_DIR}/ta.key \
      &lt;(echo -e &#39;&lt;/tls-auth&gt;&#39;) \
      &gt; ${OUTPUT_DIR}/${1}.ovpn</code></pre>
</li>
<li><p>Config file 권한부여</p>
<pre><code class="language-bash">sudo chmod 777 ~/client-configs/make_config.sh</code></pre>
</li>
</ul>
<h1 id="9-ovpn-파일vpn-접속에-필요한-파일-생성">9. ovpn 파일(vpn 접속에 필요한 파일) 생성</h1>
<ul>
<li><p>.ovpn 생성</p>
<pre><code class="language-bash">cd /home/ubuntu/client-configs

./make_confing.sh 계정명
ex) ./make_config.sh asher</code></pre>
</li>
<li><p>생성확인(파일 위치)</p>
<pre><code class="language-bash">cd ~/client-configs/files
ll</code></pre>
</li>
</ul>
<h1 id="10-vpn-접속">10. VPN 접속</h1>
<ul>
<li><p>OpenVPN 다운로드 
<a href="https://openvpn.net/community-downloads/">OpenVPN 바로가기</a>
<a href="https://openvpn.net/community-downloads/">https://openvpn.net/community-downloads/</a></p>
</li>
<li><p>.ovpn 파일 위치 변경
C:\Program Files\OpenVPN\config 에 ovpn 이동</p>
</li>
<li><p>OpenVPN 접속
<img src="https://velog.velcdn.com/images/berry_good/post/60a4af5e-f3f9-40f4-804a-72f2afb6923a/image.png" alt=""></p>
</li>
<li><p>결과화면
<img src="https://velog.velcdn.com/images/berry_good/post/f97e1306-1c24-4200-a95e-90578b2d9191/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] Container ←→ Host 파일 복사]]></title>
            <link>https://velog.io/@berry_good/Docker-Container-Host-%ED%8C%8C%EC%9D%BC-%EB%B3%B5%EC%82%AC</link>
            <guid>https://velog.io/@berry_good/Docker-Container-Host-%ED%8C%8C%EC%9D%BC-%EB%B3%B5%EC%82%AC</guid>
            <pubDate>Mon, 03 Jun 2024 10:43:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Container와 Host간의 파일 복사에 대해 알아보자</p>
</blockquote>
<p>Docker를 사용하다 보면, 
<strong>Container → Host</strong>, <strong>Host → Container</strong>로 파일을 복사 할 일이 생긴다.
scp를 이용해서 파일을 복사할 수 있지만, 더 쉽고 간편한 방법이 있어 소개한다.</p>
<h2 id="container-→-host-파일-복사">Container → Host 파일 복사</h2>
<pre><code class="language-bash">docker cp [컨테이너 이름]:[컨테이너 내부경로] [host 파일 경로]
docker cp nginx:/home/asher/test.txt /home/ubuntu</code></pre>
<p>현재 컨테이너에 test.txt 파일이 있다 이 파일을 host 경로로 이동하면</p>
<ul>
<li>이동 전
<img src="https://velog.velcdn.com/images/berry_good/post/d3beaabf-6ba8-400a-949c-e6e0d3a0a462/image.png" alt=""></li>
<li>이동 후
<img src="https://velog.velcdn.com/images/berry_good/post/2eb36c7c-32e8-44d3-b46f-596c2f59d80f/image.png" alt=""></li>
</ul>
<p>Successfully라는 명령어가 나오며 옮겨진 것을 확인 할 수 있다.</p>
<h2 id="host-→-container-파일-복사">Host → Container 파일 복사</h2>
<pre><code class="language-bash">docker cp [host 파일경로] [컨테이너 이름]:[컨테이너 내부경로]
docker cp /home/ubuntu/test2.txt nginx:/home/asher</code></pre>
<ul>
<li>이동 전
<img src="https://velog.velcdn.com/images/berry_good/post/e673cd9e-f2c1-474f-8aa3-819a6b771b3c/image.png" alt=""></li>
<li>이동 후
<img src="https://velog.velcdn.com/images/berry_good/post/68ce585f-1ac3-4c8c-9433-2b5322d0e72b/image.png" alt=""><img src="https://velog.velcdn.com/images/berry_good/post/0348191f-e9b6-498a-aa2b-7ac3a7ca508d/image.png" alt="">
마찬가지로 Successfully라는 명령어가 나오며 정상적으로 파일 복사가 진행되었다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] Docker, Docker Compose 설치 방법(Linux)]]></title>
            <link>https://velog.io/@berry_good/Docker-Docker-Compose-%EC%84%A4%EC%B9%98-%EB%B0%A9%EB%B2%95Linux</link>
            <guid>https://velog.io/@berry_good/Docker-Docker-Compose-%EC%84%A4%EC%B9%98-%EB%B0%A9%EB%B2%95Linux</guid>
            <pubDate>Mon, 27 May 2024 14:03:16 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-설치하기">Docker 설치하기</h1>
<blockquote>
<p>Docker를 처음 접하시는 분들을 위해
Linux에서 Docker를 설치하는 방법을 작성해봅니다!</p>
</blockquote>
<h3 id="1-기존-도커-커뮤니티-에디션-및-도커-엔진-제거">1. 기존 도커 커뮤니티 에디션 및 도커 엔진 제거</h3>
<pre><code class="language-bash">sudo apt-get remove docker docker-engine docker.io containerd runc</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/ff88ebf7-4d96-4038-b8c8-77347e91e63f/image.png" alt=""></p>
<h3 id="2-도커-설치에-필요한-패키지-설치">2. 도커 설치에 필요한 패키지 설치</h3>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl \
gnupg-agent software-properties-common</code></pre>
<h3 id="3-도커-인증-키-추가">3. 도커 인증 키 추가</h3>
<pre><code class="language-bash">curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/d5d074e1-4c49-4c32-a1ff-b596c0798902/image.png" alt=""></p>
<ul>
<li><p>인증 키 추가 확인</p>
<pre><code class="language-bash">sudo apt-key fingerprint 0EBFCD88</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/03c52e7d-f28a-4e3b-a977-bbe3f85f5a26/image.png" alt=""></p>
<h3 id="4-도커-저장소-추가">4. 도커 저장소 추가</h3>
<pre><code class="language-bash">sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/0cf70925-1087-4d64-a8e1-a517185090bb/image.png" alt=""></p>
<h3 id="5-패키지-업데이트-및-도커-설치">5. 패키지 업데이트 및 도커 설치</h3>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io</code></pre>
<h3 id="6-도커-버전-확인">6. 도커 버전 확인</h3>
<pre><code class="language-bash">docker version</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/13570041-07a8-45db-916c-4f6e84144f26/image.png" alt=""></p>
</li>
<li><p>Docker 상태 확인</p>
<pre><code class="language-bash"># OS부팅 시 자동 시작
sudo systemctl enable docker
sudo systemctl status docker</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/345820e1-912a-4772-8e41-f10e52106d23/image.png" alt=""></p>
</li>
</ul>
<h1 id="docker-compose-설치">Docker Compose 설치</h1>
<h3 id="1-docker-compose-설치">1. Docker Compose 설치</h3>
<pre><code class="language-bash">sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/945735f6-aecf-4347-8893-89e48af1320e/image.png" alt=""></p>
<h3 id="2-docker-compose-실행-권한-부여">2. Docker Compose 실행 권한 부여</h3>
<pre><code class="language-bash">sudo chmod +x /usr/local/bin/docker-compose</code></pre>
<h3 id="3-docker-compose-버전-확인">3. Docker Compose 버전 확인</h3>
<pre><code class="language-bash">docker compose version</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/dcb0b304-0f59-4aa1-b374-65b0fbb2bb80/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[APM] 2Tier 기반 APM(Apache, PHP, MySQL) 구축 2탄]]></title>
            <link>https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-2%ED%83%84</link>
            <guid>https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-2%ED%83%84</guid>
            <pubDate>Wed, 14 Feb 2024 12:12:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>APM 구축하기 2탄 입니다.
1탄이 궁금하신 분들은
<a href="https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-1%ED%83%84">[APM] 2Tier 기반 APM(Apache, PHP, MySQL) 구축 1탄</a> 여기를 클릭해주세요!</p>
</blockquote>
<h2 id="구축방법">구축방법</h2>
<ul>
<li><p>php 설치
<code>sudo apt install php php-mysql</code></p>
</li>
<li><p>보안그룹 Open</p>
<ul>
<li>Web-Server 2 대 -&gt; DB 2대 Inbound Traffic 허용
<img src="https://velog.velcdn.com/images/berry_good/post/2d844498-026c-4fc3-ab28-4527004f0824/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>Test PHP 설정</strong></p>
<pre><code class="language-shell">    // PHP 저장 파일 폴더로 이동
        cd /var/www/html

    // test.php 파일 작성

    sudo vi test.php

    // test.php

    &lt;?php

          error_reporting(E_ALL);

          ini_set(&quot;display_errors&quot;, 1);

          date_default_timezone_set(&#39;Asia/Seoul&#39;);

          $conn = mysqli_connect( &quot;공인 IP&quot;, &quot;chan&quot;, &quot;7975&quot;, &quot;hi&quot;, &quot;3306&quot; );
                                  #DB IP   Username   PW     DB    PORT

          if( empty( $conn ) == true ) {

                echo ( &quot;#############&quot; );

                    echo ( &quot;&lt;/br&gt; default DBMS 접속 호스트 정보가 정확하지 않습니다. &lt;/br&gt;\n\n&quot; );

                    exit ( &quot;#################&quot; );

          } else {

                echo ( &quot;#######&quot; );

                    echo ( &quot;&lt;/br&gt; default DBMS 접속에 성공하였습니다. &lt;/br&gt;\n\n&quot; );

                    echo ( &quot;-----------------------------------------------------------------------------&quot; );

                    echo ( &quot;&lt;pre&gt;&quot; );

                    print_r ( $conn );

                    echo ( &quot;&lt;/pre&gt;&quot; );

                    exit ( &quot;##########################&quot; );

          }


</code></pre>
</li>
</ul>
<pre><code>        mysqli_close( $conn );

    ?&gt;
 ```


- 결과 확인
    ![](https://velog.velcdn.com/images/berry_good/post/25804835-0dcc-46c1-a531-4004a03223c0/image.png)</code></pre><p>  ❗<strong>DBMS 접속이 안될 때</strong></p>
<pre><code>- 보안그룹이 제대로 열려있는지 WEB → DB 확인!
- MySQL 재시작 해보자….
`sudo service mysql restart`
- PHP 문법이 제대로 맞는지 확인!</code></pre><ul>
<li><strong>signup_action.php 작성 및 DB 적재 확인</strong></li>
</ul>
<pre><code>```shell
// 폴더로 이동
cd /var/www/html

// signup_action 열기
sudo vi signup_action.php

// signup_action.php
&lt;?php
$name=$_POST[&#39;name&#39;];
$email=$_POST[&#39;email&#39;];
$num=$_POST[&#39;num&#39;];
$con = mysqli_connect(&quot;공인IP&quot;,&quot;chan2&quot;,&quot;7975&quot;,&quot;hi&quot;) or die (&quot;Can&#39;t access DB&quot;);
$query = &quot;insert into exam (name,email,num) values(&#39;&quot;.$name.&quot;&#39;,&#39;&quot;.$email.&quot;&#39;,&#39;&quot;.$num.&quot;&#39;)&quot;;
$resut=mysqli_query($con,$query);
if(!$result)
{?&gt;
    &lt;script&gt; alert(&#39;회원가입이 완료되었습니다.&#39;); location.href=&quot;..&quot;; &lt;/script&gt;
&lt;?php
} else {?&gt;
    &lt;script&gt; alert(&#39;회원가입에 실패했습니다.\n다시 시도해 주세요.&#39;); location.href=&quot;..&quot;; &lt;/script&gt;
&lt;?php } ?&gt;
```

- 결과확인

![](https://velog.velcdn.com/images/berry_good/post/7c9b6bc0-4000-4379-a715-e551b7f2a44b/image.png)</code></pre><ul>
<li><p>LB 생성 및 보안 그룹 연결</p>
<ul>
<li>LB 생성
<img src="https://velog.velcdn.com/images/berry_good/post/b578e06a-374b-42fb-89c5-36e1ef5f15f4/image.png" alt=""></li>
<li>인스턴스에 LB 보안그룹 연결</li>
<li><blockquote>
<p>어떤 CSP던 상관 없습니다</p>
</blockquote>
</li>
</ul>
</li>
<li><p><strong>DB Replication (양방향)</strong></p>
<ul>
<li><p>mysqld.cnf 수정 + 보안그룹 OPEN</p>
<pre><code class="language-bash">  // **DB1 Instance**
  // 폴더이동 
  cd /etc/mysql/mysql.conf.d

  // mysqld.cnf 수정
  sudo vi mysqld.cnf

  # 서버 ID
  server-id = 1
  # Log 저장 위치
  log_bin = /var/log/mysql/mysql-bin.log
  # Replication 할 DB
  binlog_do_db = hi

  //mysql 재시작

  sudo service mysql restart</code></pre>
<ul>
<li>결과확인
<img src="https://velog.velcdn.com/images/berry_good/post/0cb5e3a9-e053-431d-842d-8519927ca0ae/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>    ```bash
    // **DB2 Instance**
    // 폴더이동 
    cd /etc/mysql/mysql.conf.d

    // mysqld.cnf 수정
    sudo vi mysqld.cnf

    # 서버 ID
    server-id = 2
    # Log 저장 위치
    log_bin = /var/log/mysql/mysql-bin.log
    relay-log = /var/log/mysql/mysql-relay-bin.log
    # Replication 할 DB
    binlog_do_db = hi

    //mysql 재시작

    sudo service mysql restart
    ```

    - 결과확인      
![](https://velog.velcdn.com/images/berry_good/post/1e704c2d-0ad8-4966-9f43-1a2b57643915/image.png)

- MASTER, SLAVE 정보 확인 및 설정
    - MySQL 접속 (DB 1)
        `SHOW MASTER STATUS;`

    ![](https://velog.velcdn.com/images/berry_good/post/4b8db1b8-8e14-4dfa-8527-03d8b1439c3c/image.png)


    - MASTER - SLAVE 양방향 설정 DB1, DB2 둘 다

    ```bash

    // DB2에 적용
    CHANGE MASTER TO 
    MASTER_HOST=&#39;DB 사설 IP 1&#39;, 
    MASTER_PORT=3306,
    MASTER_USER=&#39;chan&#39;, 
    MASTER_PASSWORD=&#39;7975&#39;, 
    MASTER_LOG_FILE=&#39;mysql-bin.000001&#39;, 
    MASTER_LOG_POS=  1525;

    LOG_POS, LOG_FILE 잘 확인
    IP는 사설 IP로 적용

    // DB1에 적용
    CHANGE MASTER TO 
    MASTER_HOST=&#39;DB 사설 IP 2&#39;,
    MASTER_PORT=3306,
    MASTER_USER=&#39;chan2&#39;, 
    MASTER_PASSWORD=&#39;7975&#39;, 
    MASTER_LOG_FILE=&#39;mysql-bin.000002&#39;, 
    MASTER_LOG_POS=  444;
    ```


- Slave 상태 확인

    `SHOW SLAVE STATUS\G;`
    ![](https://velog.velcdn.com/images/berry_good/post/9bbd713c-2a54-4ba8-b21e-a015c4f7d294/image.png)![](https://velog.velcdn.com/images/berry_good/post/7954f41e-c3b7-4240-8d73-8377903a3eec/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[APM] 2Tier 기반 APM(Apache, PHP, MySQL) 구축 1탄]]></title>
            <link>https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-1%ED%83%84</link>
            <guid>https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-1%ED%83%84</guid>
            <pubDate>Wed, 03 Jan 2024 11:57:52 GMT</pubDate>
            <description><![CDATA[<h2 id="🤔apm이란">🤔APM이란?</h2>
<blockquote>
<p>APM?</p>
</blockquote>
<p>Apache + PHP + MySQL을 합쳐 부르는 말이다.</p>
<h2 id="기술스택">기술스택</h2>
<blockquote>
</blockquote>
<ul>
<li>Apache 2.4.14</li>
<li>MySQL 8.0.35</li>
<li>PHP 7.4.3</li>
<li>Ubuntu 20.04.1</li>
<li>Web-Server Instance 2대</li>
<li>DB Instance 2대</li>
<li>LoadBalancer 1대</li>
</ul>
<h2 id="🏗️architecture">🏗️Architecture</h2>
<p><img src="https://velog.velcdn.com/images/berry_good/post/81190a39-876d-47c7-a0a8-7a5ad4fc7a93/image.png" alt=""></p>
<h2 id="구축방법">구축방법</h2>
<blockquote>
<p>참고로 제가 구축하는 환경은 Cloud 환경입니다.</p>
</blockquote>
<h4 id="apache2-설치web-server-instance-생성-후">Apache2 설치(Web-Server Instance 생성 후)</h4>
<ul>
<li><p>패키지 업데이트
<code>sudo apt-get update</code></p>
</li>
<li><p>Apache2 설치
<code>sudo apt-get install apache2</code></p>
</li>
<li><p>Apache2 상태 확인
<code>systemctl status apache2</code></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/99c8b4a2-2fb0-4702-b8c2-4b779464d28f/image.png" alt=""></p>
<ul>
<li>공인 IP 접속 -&gt; 정상 작동 확인<pre><code>공인 IP:80 ex) 1.255.428.17:80</code></pre><img src="https://velog.velcdn.com/images/berry_good/post/03c7c228-8340-4362-a677-2b7db140f652/image.png" alt=""></li>
</ul>
<blockquote>
<p>위와 똑같은 방식으로 Web-Server 1대 더 만들어 주시면 됩니다.</p>
</blockquote>
<h4 id="mysql-설치db-instance-생성-후">MySQL 설치(DB Instance 생성 후)</h4>
<ul>
<li>apt 패키지 업데이트
<code>sudo apt-get update</code>   </li>
</ul>
<ul>
<li>MySQL-Server 설치
<code>sudo apt-get install mysql-server</code></li>
</ul>
<ul>
<li>MySQL-Server 상태 확인
<code>systemctl status mysql</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/0daa624f-8c68-4100-a360-636f9db04872/image.png" alt=""></p>
<ul>
<li><p><strong>DB 사용자 추가 및 권한 할당</strong></p>
<ul>
<li><p>user 권한 검색</p>
<pre><code class="language-sql">use mysql;
select host, user from user;</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/berry_good/post/cc4ba0d1-a9c4-40be-be64-6ab440d0ba6d/image.png" alt=""></p>
</li>
</ul>
<pre><code>- User 추가</code></pre><pre><code class="language-sql">    // 사용자 추가 및 PW 설정
    create user &#39;[ID입력]&#39;@&#39;%&#39; identified by &#39;[PW 입력]&#39;;

    // 변경사항 즉시 반영
    FLUSH privileges;</code></pre>
<pre><code>![](https://velog.velcdn.com/images/berry_good/post/1d0cbf18-fd77-4cf0-9cf5-d0d282d25479/image.png)

- 권한부여</code></pre><pre><code class="language-sql">    // chan2 User에게 모든 권한 부여
    GRANT ALL PRIVILEGES ON *.* TO &#39;chan2&#39;@&#39;%&#39;;

    // 변경사항 즉시 반영
    FLUSH privileges;</code></pre>
<p>  <img src="https://velog.velcdn.com/images/berry_good/post/2040f5ad-38a9-4011-9c19-3a776eb1561d/image.png" alt=""></p>
<ul>
<li><p>외부접속 허용
<code>sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf</code> </p>
<ul>
<li>bind-address = 0.0.0.0으로 변경
<img src="https://velog.velcdn.com/images/berry_good/post/951f409b-52df-4682-bb9b-b0ad881e54e9/image.png" alt=""></li>
</ul>
</li>
<li><p>Mysql 재시작
<code>sudo service mysql restart</code></p>
</li>
</ul>
<ul>
<li><p>DB 스키마 생성</p>
<ul>
<li><p>MySQL 접속 및 DB생성</p>
<pre><code class="language-sql">mysql -u chan2 -p
create database hi;
show databases;</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/eebfc9e0-1903-4e64-80a4-d3b45394e489/image.png" alt=""></p>
</li>
<li><p>DB 테이블 생성</p>
<pre><code class="language-sql">CREATE TABLE exam (
id int NOT NULL AUTO_INCREMENT,
name varchar(30),
email varchar(40),
num varchar(30),
PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;</code></pre>
<p><img src="https://velog.velcdn.com/images/berry_good/post/4fba32ec-8d10-41b3-a747-3ef50cb33e31/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>위와 같은 방식으로 DB 1대 더 만들어 주시면 됩니다.
구축을 완료했다면, 현재</p>
</blockquote>
<ul>
<li>Web-Server 2대</li>
<li>DB 2대
총 4대의 Instance가 생성이 되어야 합니다.</li>
</ul>
<hr>
<blockquote>
<p>2탄에서 계속...
<a href="https://velog.io/@berry_good/APM-2Tier-%EA%B8%B0%EB%B0%98-APMApache-PHP-MySQL-%EA%B5%AC%EC%B6%95-2%ED%83%84">[APM] 2Tier 기반 APM(Apache, PHP, MySQL) 구축 2탄</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>