<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ondacloud</title>
        <link>https://velog.io/</link>
        <description>Devops Engineer가 목표인 학생</description>
        <lastBuildDate>Tue, 05 May 2026 03:40:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ondacloud</title>
            <url>https://velog.velcdn.com/images/on_cloud/profile/984b35c1-e19f-44b9-b21a-fdd4bb56ccb8/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ondacloud. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/on_cloud" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Kubernetes] EC2 환경에서 Legery Kubernetes 구성하기 with Amazon Linux 2023]]></title>
            <link>https://velog.io/@on_cloud/ec2-legery-kubernetes-with-al2023</link>
            <guid>https://velog.io/@on_cloud/ec2-legery-kubernetes-with-al2023</guid>
            <pubDate>Tue, 05 May 2026 03:40:22 GMT</pubDate>
            <description><![CDATA[<h1 id="overview">Overview</h1>
<p>EC2 환경에서 Kubernetes를 직접 구축하는 방식은 AWS의 Managed Service인 EKS와 달리, 모든 구성 요소를 사용자가 직접 설치하고 제어해야 한다는 특징이 있습니다. 이러한 방식은 흔히 “Legacy Kubernetes” 또는 “kubeadm 기반 Self-managed Kubernetes”라고 부르며, Control Plane과 Worker Node를 모두 EC2 인스턴스 위에서 직접 구성하는 형태입니다.</p>
<p>이 글에서는 EC2 기반 Amazon Linux 2023 환경에서 kubeadm을 활용해 Kubernetes 클러스터를 구성하는 전체 과정을 다룹니다. Control Plane EC2 인스턴스를 초기화하고, Worker Node를 클러스터에 조인시키는 기본 구조부터 시작하여, Container Runtime 설정, CNI 네트워크 플러그인 설치, kubeconfig 구성까지 단계적으로 설명합니다.</p>
<p>또한 실제 운영 환경에서 중요한 네트워크 포트 설정, Security Group 구성, 그리고 클러스터 통신 구조까지 함께 다루어 단순 설치가 아닌 “동작하는 Kubernetes 클러스터”를 만드는 것을 목표로 합니다.</p>
<p>즉, EC2 기반 Legacy Kubernetes 구축은 관리형 서비스 없이도 Kubernetes를 직접 구성하고 운영할 수 있도록 해주는 가장 기본적이면서도 핵심적인 Self-managed 클러스터 구성 방식입니다.</p>
<br>

<h1 id="setup">Setup</h1>
<h2 id="application">Application</h2>
<h3 id="mainpy">main.py</h3>
<pre><code class="language-python">from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.get(&quot;/version&quot;)
def get_version():
    return {&quot;version&quot;: &quot;v1.0.0&quot;}

@app.get(&quot;/healthcheck&quot;)
def get_healthcheck():
    return {&quot;status&quot;: &quot;ok&quot;}

if __name__ == &quot;__main__&quot;:
    uvicorn.run(app, host=&quot;0.0.0.0&quot;, port=8080)</code></pre>
<p>해당 애플리케이션은 Pod의 동작을 확인하기 위한 테스트용 코드입니다.</p>
<br>

<h3 id="requirementstxt">requirements.txt</h3>
<pre><code>fastapi==0.136.1
uvicorn==0.46.0</code></pre><br>

<h2 id="dockerfile">Dockerfile</h2>
<pre><code class="language-docker">FROM python:3.14-alpine

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app
COPY ./main.py /app/main.py
COPY ./requirements.txt /app/requirements.txt

RUN apk add --no-cache curl &amp;&amp; pip install --no-cache-dir -r requirements.txt &amp;&amp; rm -rf /root/.cache/

RUN adduser -D --disabled-password --gecos &#39;&#39; app-user
USER app-user

EXPOSE 8080
CMD [&quot;uvicorn&quot;, &quot;main:app&quot;, &quot;--host&quot;, &quot;0.0.0.0&quot;, &quot;--port&quot;, &quot;8080&quot;]</code></pre>
<br>

<h1 id="practice">Practice</h1>
<h2 id="vpc">VPC</h2>
<p>Public Subnet과 Private Subnet을 구성합니다. Availability Zone A와 C에 구성합니다.</p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/f0c0a94b-2f99-4803-9b69-94a3a74865cd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/e4ab1c7a-b1a0-4667-ad18-ff458208fd23/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/ba42c439-bb94-4909-98c1-cefccf2efd96/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/b2d56350-69d7-4192-a418-03fb075d0f18/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/0875d0ea-1a1e-498b-8c34-0f8f31f1c00e/image.png" alt=""></p>
<table>
<thead>
<tr>
<th align="center">VPC</th>
<th align="center">Subnet</th>
<th align="center">Route Table</th>
<th align="center">Gateway</th>
</tr>
</thead>
<tbody><tr>
<td align="center">demo-vpc</td>
<td align="center">demo-public-a</td>
<td align="center">demo-public-rtb</td>
<td align="center">demo-igw</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">demo-public-c</td>
<td align="center">demo-public-rtb</td>
<td align="center">demo-igw</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">demo-private-a</td>
<td align="center">demo-private-a-rtb</td>
<td align="center">demo-natgw-a</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">demo-private-c</td>
<td align="center">demo-private-c-rtb</td>
<td align="center">demo-natgw-c</td>
</tr>
</tbody></table>
<br>

<h2 id="security-group">Security Group</h2>
<h3 id="bastion">Bastion</h3>
<p><strong>Inbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SSH</td>
<td align="center">22</td>
<td align="center">0.0.0.0/0</td>
<td align="center">EC2 접근</td>
</tr>
</tbody></table>
<p><strong>Outbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SSH</td>
<td align="center">22</td>
<td align="center">0.0.0.0/0</td>
<td align="center">EC2 접근</td>
</tr>
<tr>
<td align="center">HTTP</td>
<td align="center">80</td>
<td align="center">0.0.0.0/0</td>
<td align="center">패키지 다운로드 및 외부 통신</td>
</tr>
<tr>
<td align="center">HTTPS</td>
<td align="center">443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">패키지 다운로드 및 API 통신</td>
</tr>
</tbody></table>
<br>

<h3 id="alb">ALB</h3>
<p><strong>Inbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">HTTP</td>
<td align="center">80</td>
<td align="center">0.0.0.0/0</td>
<td align="center">외부 사용자 요청 수신</td>
</tr>
</tbody></table>
<p><strong>Outbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Custom TCP</td>
<td align="center">30000-32767</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Kubernetes Nodeport Service로 트래픽 전달</td>
</tr>
</tbody></table>
<br>

<h3 id="kubernetes-master-node">Kubernetes Master Node</h3>
<p><strong>Inbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SSH</td>
<td align="center">22</td>
<td align="center">Bastion Security Group</td>
<td align="center">EC2 접근</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">6443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Kubernetes API Server 접근</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">2379 - 2380</td>
<td align="center">0.0.0.0/0</td>
<td align="center">etcd 클러스터 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">10250</td>
<td align="center">0.0.0.0/0</td>
<td align="center">kubelet API</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">179</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Calico BGP 피어링</td>
</tr>
</tbody></table>
<p><strong>Outbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">HTTP</td>
<td align="center">80</td>
<td align="center">0.0.0.0/0</td>
<td align="center">패키지 다운로드</td>
</tr>
<tr>
<td align="center">HTTPS</td>
<td align="center">443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">API 및 이미지 레지스트리 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">6443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Kubernetes API Server 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">2379 - 2380</td>
<td align="center">0.0.0.0/0</td>
<td align="center">etcd 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">10250</td>
<td align="center">0.0.0.0/0</td>
<td align="center">kubelet 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">53</td>
<td align="center">0.0.0.0/0</td>
<td align="center">DNS 질의</td>
</tr>
<tr>
<td align="center">Custom UDP</td>
<td align="center">53</td>
<td align="center">0.0.0.0/0</td>
<td align="center">DNS 질의</td>
</tr>
</tbody></table>
<br>

<h3 id="kubernetes-worker-node">Kubernetes Worker Node</h3>
<p><strong>Inbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SSH</td>
<td align="center">22</td>
<td align="center">Bastion Security Group</td>
<td align="center">EC2 접근</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">10250</td>
<td align="center">Kubernetes Master Node Security Group</td>
<td align="center">kubelet API</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">30000-32767</td>
<td align="center">ALB Security Group</td>
<td align="center">Nodeport Service 트래픽 수신</td>
</tr>
</tbody></table>
<p><strong>Outbound</strong></p>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Port Range</th>
<th align="center">Source</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">HTTP</td>
<td align="center">80</td>
<td align="center">0.0.0.0/0</td>
<td align="center">패키지 다운로드</td>
</tr>
<tr>
<td align="center">HTTPS</td>
<td align="center">443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">이미지 Pull 및 API 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">6443</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Kubernetes API Server 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">179</td>
<td align="center">0.0.0.0/0</td>
<td align="center">Calico BGP 통신</td>
</tr>
<tr>
<td align="center">Custom TCP</td>
<td align="center">53</td>
<td align="center">0.0.0.0/0</td>
<td align="center">DNS 질의</td>
</tr>
<tr>
<td align="center">Custom UDP</td>
<td align="center">53</td>
<td align="center">0.0.0.0/0</td>
<td align="center">DNS 질의</td>
</tr>
</tbody></table>
<br>

<h2 id="iam">IAM</h2>
<p>Kubernetes Node가 ECR 이미지를 Pull할 수 있도록 IAM Role을 생성하고 필요한 권한을 부여합니다.</p>
<h3 id="bastion-1">Bastion</h3>
<table>
<thead>
<tr>
<th align="center">Role Name</th>
<th align="center">Policy Name</th>
</tr>
</thead>
<tbody><tr>
<td align="center">bastion-role</td>
<td align="center">AdministratorAccess</td>
</tr>
</tbody></table>
<br>

<h3 id="kubernetes-node">Kubernetes Node</h3>
<table>
<thead>
<tr>
<th align="center">Role Name</th>
<th align="center">Policy Name</th>
</tr>
</thead>
<tbody><tr>
<td align="center">k8s-node-role</td>
<td align="center">AmazonEC2ContainerRegistryFullAccess</td>
</tr>
</tbody></table>
<br>

<h2 id="ec2">EC2</h2>
<h3 id="bastion-2">Bastion</h3>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/5af34f90-004b-4d92-af4b-10cec15737c9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/78d7ebda-048d-4978-ba1d-6e8b42d81fef/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/407f571a-7803-434c-94b6-0b15dfb7f6b4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/0fa00837-cbdb-4bb0-8d54-51e8b30e8a65/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/1461ad39-4b2b-4a11-a0e3-906e12d1290a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/b9018bb8-2df3-4db1-b908-73c27817e0d3/image.png" alt=""></p>
<p><strong>User Data</strong></p>
<pre><code class="language-shell">#!/bin/bash
dnf update -y
dnf upgrade -y
dnf install --allowerasing -y jq curl wget unzip vim

curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo ./aws/install

sed -i &quot;s|PasswordAuthentication no|PasswordAuthentication yes|g&quot; /etc/ssh/sshd_config
systemctl restart sshd
echo &#39;Skill53##&#39; | passwd --stdin ec2-user
echo &#39;Skill53##&#39; | passwd --stdin root

dnf install -y docker
systemctl enable --now docker
usermod -aG docker ec2-user
chmod 666 /var/run/docker.sock</code></pre>
<br>

<h3 id="kubernetes-master-node-1">Kubernetes Master Node</h3>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/10c3ba21-2e25-40cd-a5ac-c4db5d480720/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/78d7ebda-048d-4978-ba1d-6e8b42d81fef/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/407f571a-7803-434c-94b6-0b15dfb7f6b4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/0fa00837-cbdb-4bb0-8d54-51e8b30e8a65/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/a43a7324-1d17-408c-9bda-d0713f7d6496/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/6e25de5f-9225-4196-a9f5-e10ff4724b9a/image.png" alt=""></p>
<p><strong>User Data</strong></p>
<pre><code class="language-shell">#!/bin/bash
dnf update -y
dnf upgrade -y
dnf install --allowerasing -y jq curl wget unzip vim

curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo ./aws/install

sed -i &quot;s|PasswordAuthentication no|PasswordAuthentication yes|g&quot; /etc/ssh/sshd_config
systemctl restart sshd
echo &#39;Skill53##&#39; | passwd --stdin ec2-user
echo &#39;Skill53##&#39; | passwd --stdin root

dnf install -y docker
systemctl enable --now docker
usermod -aG docker ec2-user
chmod 666 /var/run/docker.sock</code></pre>
<br>

<h3 id="kubernetes-worker-node-1">Kubernetes Worker Node</h3>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/93622bea-e21d-4c63-8ae0-e30e4f7a66c9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/78d7ebda-048d-4978-ba1d-6e8b42d81fef/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/407f571a-7803-434c-94b6-0b15dfb7f6b4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/0fa00837-cbdb-4bb0-8d54-51e8b30e8a65/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/65263c4a-2052-48c6-a43d-26c9c0942a0e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/6e25de5f-9225-4196-a9f5-e10ff4724b9a/image.png" alt=""></p>
<p><strong>User Data</strong></p>
<pre><code class="language-shell">#!/bin/bash
dnf update -y
dnf upgrade -y
dnf install --allowerasing -y jq curl wget unzip vim

curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo ./aws/install

sed -i &quot;s|PasswordAuthentication no|PasswordAuthentication yes|g&quot; /etc/ssh/sshd_config
systemctl restart sshd
echo &#39;Skill53##&#39; | passwd --stdin ec2-user
echo &#39;Skill53##&#39; | passwd --stdin root</code></pre>
<br>

<h2 id="ecr">ECR</h2>
<p>ECR Registry를 생성합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/5946a6c5-ae09-4d78-9d1e-c037873c3af9/image.png" alt=""></p>
<br>

<p>환경변수를 정의합니다.</p>
<pre><code class="language-shell">ACCOUNT_ID=$(aws sts get-caller-identity --query &quot;Account&quot; --output text)
REGION_CODE=&quot;ap-northeast-2&quot;
ECR_NAME=&quot;demo-ecr&quot;
IMAGE_TAG=&quot;v1.0.0&quot;
ECR_URI=$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com/$ECR_NAME</code></pre>
<br>

<p>Docker 이미지를 Build 후 Push합니다.</p>
<pre><code class="language-shell">aws ecr get-login-password --region $REGION_CODE | docker login --username AWS --password-stdin &quot;$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com&quot;
docker build -t &quot;$ECR_URI:$IMAGE_TAG&quot; .
docker push &quot;$ECR_URI:$IMAGE_TAG&quot;</code></pre>
<br>

<h2 id="kubernetes">Kubernetes</h2>
<h3 id="master-node">Master Node</h3>
<p><a href="https://github.com/containerd/containerd/releases">Containerd Package</a>를 설치합니다. Containerd는 컨테이너 런타임으로, kubelet이 컨테이너를 실행할 때 사용됩니다.</p>
<pre><code class="language-shell">CONTAINERD_VERSION=$(curl -s https://api.github.com/repos/containerd/containerd/releases/latest | jq -r .tag_name)
curl -fsSLO https://github.com/containerd/containerd/releases/download/${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz
sudo tar -C /usr/local -xzf containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz
rm -rf containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz</code></pre>
<br>

<p>Containerd 서비스 파일이 위치할 디렉터리를 생성합니다.</p>
<pre><code class="language-shell">sudo mkdir -p /usr/local/lib/systemd/system</code></pre>
<br>

<p>Containerd 설정파일을 systemd에 다운로드 합니다.</p>
<pre><code class="language-shell">sudo curl -sSL https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /usr/local/lib/systemd/system/containerd.service</code></pre>
<br>

<p>systemd 설정을 다시 로드합니다.</p>
<pre><code class="language-shell">sudo systemctl daemon-reload</code></pre>
<br>

<p>Containerd를 실행합니다.</p>
<pre><code class="language-shell">sudo systemctl enable --now containerd</code></pre>
<br>

<p><a href="https://github.com/opencontainers/runc/releases">runc Package</a>를 설치합니다.</p>
<pre><code class="language-shell">RUNC_VERSION=$(curl -s https://api.github.com/repos/opencontainers/runc/releases/latest | jq -r .tag_name)
curl -sSLO https://github.com/opencontainers/runc/releases/download/${RUNC_VERSION}/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
rm -rf runc.amd64</code></pre>
<br>

<p>CNI 플러그인이 설치될 경로를 생성합니다.</p>
<pre><code class="language-shell">sudo mkdir -p /opt/cni/bin</code></pre>
<br>

<p><a href="https://github.com/containernetworking/plugins/releases">CNI Package</a>를 설치합니다.</p>
<pre><code class="language-shell">CNI_VERSION=$(curl -s https://api.github.com/repos/containernetworking/plugins/releases/latest | jq -r .tag_name)
curl -sSLO https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-amd64-${CNI_VERSION}.tgz
sudo tar -C /opt/cni/bin -xzf cni-plugins-linux-amd64-${CNI_VERSION}.tgz
rm -rf cni-plugins-linux-amd64-${CNI_VERSION}.tgz</code></pre>
<br>

<p>Kubernetes 패키지 저장소를 등록합니다.</p>
<pre><code class="language-shell">sudo tee /etc/yum.repos.d/kubernetes.repo &lt;&lt;EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.35/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.35/rpm/repodata/repomd.xml.key
EOF</code></pre>
<br>

<p>Kubernetes Package를 설치합니다.</p>
<blockquote>
<p>kubeadm: 쿠버네티스 클러스터를 생성, 설정 및 초기화하는 구축 전용 도구 <br>
kubelet: 각 노드에서 실행되며 Pod의 컨테이너 상태를 관리하고 명령을 수행 <br>
kubectl: 사용자가 클러스터에 명령을 내리고 리소스를 제어하기 위해 사용하는 명령줄 도구</p>
</blockquote>
<pre><code class="language-shell">sudo dnf install -y kubeadm kubelet kubectl</code></pre>
<br>

<p>패키지 버전을 고정할 수 있는 dnf 플러그인을 설치합니다.</p>
<pre><code class="language-shell">sudo dnf install -y &#39;dnf-command(versionlock)&#39;</code></pre>
<br>

<p>Kubernetes Package의 버전을 고정합니다. (업데이트 방지)</p>
<pre><code class="language-shell">sudo dnf versionlock add kubeadm kubelet kubectl</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/6fc54ab4-6676-4546-8179-51706a89525e/image.png" alt=""></p>
<br>

<p>swap 메모리를 비활성화합니다.</p>
<pre><code class="language-shell">sudo swapoff -a</code></pre>
<br>

<p>재부팅 후에도 swap 메모리가 비활성화 되도록 설정합니다.</p>
<pre><code class="language-shell">sudo sed -i &#39;/ swap / s/^/#/&#39; /etc/fstab</code></pre>
<br>

<p>SELinux를 비활성화 합니다. (충돌 방지)</p>
<pre><code class="language-shell">sudo setenforce 0</code></pre>
<br>

<p>SELinux를 영구적으로 Permissive 모드로 설정합니다.</p>
<pre><code class="language-shell">sudo sed -i &#39;s/^SELINUX=enforcing$/SELINUX=permissive/&#39; /etc/selinux/config</code></pre>
<br>

<p>Pod 네트워크 라우팅을 허용합니다.</p>
<pre><code class="language-shell">sudo sh -c &#39;echo &quot;net.ipv4.ip_forward=1&quot; &gt;&gt; /etc/sysctl.conf&#39;</code></pre>
<br>

<p>sysctl 명령어를 사용하여 설정을 적용합니다.</p>
<pre><code class="language-shell">sudo sysctl -p</code></pre>
<br>

<p>kubelet을 자동 실행합니다.</p>
<pre><code class="language-shell">sudo systemctl enable --now kubelet</code></pre>
<br>

<p>Path를 설정합니다. (kubeadm 실행 오류 방지)</p>
<pre><code class="language-shell">export PATH=$PATH:/sbin</code></pre>
<br>

<p>EC2 MetaData 접근 토큰을 생성하며, 토큰을 사용하여 MetaData에서 EC2의 Private IP를 가져옵니다.</p>
<pre><code class="language-shell">TOKEN=$(curl -sSX PUT &quot;http://169.254.169.254/latest/api/token&quot; -H &quot;X-aws-ec2-metadata-token-ttl-seconds: 21600&quot;)
PRIVATE_IP=$(curl -sS -H &quot;X-aws-ec2-metadata-token: $TOKEN&quot; http://169.254.169.254/latest/meta-data/local-ipv4)</code></pre>
<br>

<p>Kubernetes를 초기화 합니다. 초기화 후 나온 kubeadm join 값을 저장합니다.</p>
<ul>
<li>나중에 Worker Node에서 Master Node로 Join할 때 사용합니다.</li>
</ul>
<blockquote>
<p>만약, kubeadm join 값이 없어졌다면 아래 명령어를 사용하여 새로 발급받습니다.</p>
</blockquote>
<pre><code class="language-yaml">sudo kubeadm token create --print-join-command</code></pre>
<pre><code class="language-shell">sudo PATH=$PATH:/sbin kubeadm init --apiserver-advertise-address=$PRIVATE_IP --pod-network-cidr=192.168.0.0/16</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7c7735b5-bb59-4e08-82ff-de3146391741/image.png" alt=""></p>
<br>

<p>kubeconfig 파일을 생성합니다.</p>
<pre><code class="language-shell">mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config</code></pre>
<br>

<p><a href="https://github.com/projectcalico/calico/releases">Calico Package</a>를 설치합니다.</p>
<pre><code class="language-shell">CNI_VERSION=$(curl -s https://api.github.com/repos/projectcalico/calico/releases/latest | jq -r .tag_name)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/$CNI_VERSION/manifests/calico.yaml</code></pre>
<br>

<p>Pod의 상태가 Running인지 확인합니다.</p>
<pre><code class="language-shell">kubectl get po -n kube-system</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/ea8af855-c1a3-4eec-b042-0399ab1b0cca/image.png" alt=""></p>
<br>

<p>Node의 상태가 Ready인지 확인합니다.</p>
<pre><code class="language-shell">kubectl get no</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/fab402bc-5bb9-4064-965f-e3f542a82b13/image.png" alt=""></p>
<br>

<p><a href="https://github.com/etcd-io/etcd/releases">etcd Package</a>를 설치합니다.</p>
<pre><code class="language-shell">ETCD_VERSION=$(curl -s https://api.github.com/repos/etcd-io/etcd/releases/latest | jq -r .tag_name)
curl -sSLO https://github.com/etcd-io/etcd/releases/download/$ETCD_VERSION/etcd-$ETCD_VERSION-linux-amd64.tar.gz
tar xzf etcd-$ETCD_VERSION-linux-amd64.tar.gz
sudo mv etcd-$ETCD_VERSION-linux-amd64/etcdctl /usr/local/bin/
rm -rf etcd-$ETCD_VERSION-linux-amd64*</code></pre>
<br>

<p>etcd Pod가 Running인지 확인합니다.</p>
<pre><code class="language-shell">kubectl get po -n kube-system</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/5ba55315-23e6-4cee-a5b2-5b881a8037ac/image.png" alt=""></p>
<br>

<p>etcd에서 사용하는 인증서(SSL/TLS 키 파일)이 존재하는지 확인합니다.</p>
<pre><code class="language-shell">ls /etc/kubernetes/pki/etcd </code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/2a81dfbb-0178-457a-90df-2977842e4526/image.png" alt=""></p>
<br>

<p>Secret을 생성합니다.</p>
<pre><code class="language-shell">kubectl create secret generic demo-old-secrets --from-literal=key=topsecret</code></pre>
<br>

<p>etcd 내부 저장값을 확인합니다. 암호화가 안되어 있어 평문으로 나오고 있음을 확인할 수 있습니다.</p>
<blockquote>
<p>기본적으로 Kubernetes Secrets은 etcd에 평문(정확히는 단순 Base64 인코딩)으로 저장되어, DB 접근 권한이 있으면 누구나 내용을 확인할 수 있습니다.</p>
</blockquote>
<pre><code class="language-shell">sudo etcdctl \
   --cacert=/etc/kubernetes/pki/etcd/ca.crt \
   --cert=/etc/kubernetes/pki/etcd/server.crt \
   --key=/etc/kubernetes/pki/etcd/server.key \
   get /registry/secrets/default/demo-old-secrets | hexdump -C</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/6e84f3a1-ec3b-46dc-a9b7-8f7cc778a1fc/image.png" alt=""></p>
<br>

<p>Secret 암호화 설정 파일을 생성합니다. 이때 AES 암호화 알고리즘 중 CBC 모드를 사용하여 데이터를 암호화 합니다.</p>
<blockquote>
<p><strong>AES (Advanced Encryption Standard)</strong>
미국 표준 암호화로 대칭키 방식을 사용합니다. 데이터를 암호화할 때와 복호화할 때 동일한 키를 사용합니다.
<br>
<strong>CBC (Cipher Block Chaining)</strong>
AES는 데이터를 한 번에 처리하지 않고 일정한 크기(블록)로 나누어 처리합니다. 이때 블록들을 어떻게 서로 엮어서 암호화할지 결정하는 방식이 바로 CBC입니다.</p>
</blockquote>
<pre><code class="language-shell">cat &lt;&lt;\EOF&gt; enc.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key
              secret: ENCRYPTION_KEY
      - identity: {} 
EOF</code></pre>
<br>

<p>암호화 키를 생성합니다.</p>
<pre><code class="language-shell">ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)</code></pre>
<br>

<p>암호화키를 Secret 암호화 설정 파일에 삽입합니다.</p>
<pre><code class="language-shell">sed -i &quot;s|ENCRYPTION_KEY|$ENCRYPTION_KEY|g&quot; enc.yaml</code></pre>
<br>

<p>Secret 암호화 설정 파일이 위치할 디렉터리를 생성합니다.</p>
<pre><code class="language-shell">sudo mkdir -p /etc/kubernetes/enc/</code></pre>
<br>

<p>Secret 암호화 설정 파일을 위에서 생성한 디렉터리로 이동합니다.</p>
<pre><code class="language-shell">sudo mv enc.yaml /etc/kubernetes/enc/</code></pre>
<br>

<p>API Server의 설정 파일에서 아래 Encryption 설정을 추가합니다. 추가 후 저장하고 나가면 API Server가 자동으로 재시작됩니다.</p>
<pre><code class="language-shell">sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml</code></pre>
<blockquote>
<p>spec.containers[0].command <br></p>
</blockquote>
<pre><code class="language-yaml">- --encryption-provider-config=/etc/kubernetes/enc/enc.yaml</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/2afbf5e9-da56-448e-9cfc-a72ab4a16360/image.png" alt=""></p>
<blockquote>
<p>spec.containers[0].volumeMounts <br></p>
</blockquote>
<pre><code class="language-yaml">- name: enc
  mountPath: /etc/kubernetes/enc
  readOnly: true</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/d1119f83-dfdc-463d-a505-813ac9115430/image.png" alt=""></p>
<blockquote>
<p>spec.volumes <br></p>
</blockquote>
<pre><code class="language-yaml">- name: enc
  hostPath:
    path: /etc/kubernetes/enc
    type: DirectoryOrCreate</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7ada3b67-d516-4c82-84ef-76e132438035/image.png" alt=""></p>
<br>

<p>Secret을 생성합니다.</p>
<pre><code class="language-shell">kubectl create secret generic demo-new-secrets --from-literal=key=topsecret</code></pre>
<br>

<p>etcd 내부 저장값을 확인합니다. 암호화가 되어 있는 것을 확인할 수 있습니다.</p>
<pre><code class="language-shell">sudo etcdctl \
   --cacert=/etc/kubernetes/pki/etcd/ca.crt \
   --cert=/etc/kubernetes/pki/etcd/server.crt \
   --key=/etc/kubernetes/pki/etcd/server.key \
   get /registry/secrets/default/demo-new-secrets | hexdump -C</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7295c8ef-99ba-4ace-be7f-346db736aaa2/image.png" alt=""></p>
<br>

<p>Secret 전체에 암호화가 적용되었는지 확인합니다.</p>
<pre><code class="language-shell">kubectl get secrets --all-namespaces -o json | kubectl replace -f -</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/a77a6906-0307-4bf4-9b2e-94cd6295ea1e/image.png" alt=""></p>
<br>

<p>API Server가 실행중인지 확인합니다.</p>
<pre><code class="language-shell">ps aux | grep kube-api</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/267df636-d464-42a8-ae3d-6f7b74c65a46/image.png" alt=""></p>
<br>

<p>API Server에서 암호화가 적용되었는지 확인합니다.</p>
<pre><code class="language-shell">ps aux | grep kube-api | grep encry</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/ded77afd-62b9-4a60-878e-12515c819e5e/image.png" alt=""></p>
<br>

<h3 id="worker-node">Worker Node</h3>
<p><a href="https://github.com/containerd/containerd/releases">Containerd Package</a>를 설치합니다.</p>
<pre><code class="language-shell">CONTAINERD_VERSION=$(curl -s https://api.github.com/repos/containerd/containerd/releases/latest | jq -r .tag_name)
curl -fsSLO https://github.com/containerd/containerd/releases/download/${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz
sudo tar -C /usr/local -xzf containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz
rm -rf containerd-${CONTAINERD_VERSION#v}-linux-amd64.tar.gz</code></pre>
<br>

<p>Containerd 서비스 파일이 위치할 디렉터리를 생성합니다.</p>
<pre><code class="language-shell">sudo mkdir -p /usr/local/lib/systemd/system</code></pre>
<br>

<p>Containerd 설정파일을 systemd에 다운로드 합니다.</p>
<pre><code class="language-shell">sudo curl -sSL https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /usr/local/lib/systemd/system/containerd.service</code></pre>
<br>

<p>systemd 설정을 다시 로드합니다.</p>
<pre><code class="language-shell">sudo systemctl daemon-reload</code></pre>
<br>

<p>Containerd를 실행합니다.</p>
<pre><code class="language-shell">sudo systemctl enable --now containerd</code></pre>
<br>

<p><a href="https://github.com/opencontainers/runc/releases">runc Package</a>를 설치합니다.</p>
<pre><code class="language-shell">RUNC_VERSION=$(curl -s https://api.github.com/repos/opencontainers/runc/releases/latest | jq -r .tag_name)
curl -sSLO https://github.com/opencontainers/runc/releases/download/${RUNC_VERSION}/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
rm -rf runc.amd64</code></pre>
<br>

<p>Kubernetes 패키지 저장소를 등록합니다.</p>
<pre><code class="language-shell">sudo tee /etc/yum.repos.d/kubernetes.repo &lt;&lt;EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.35/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.35/rpm/repodata/repomd.xml.key
EOF</code></pre>
<br>

<p>Kubernetes Package를 설치합니다.</p>
<pre><code class="language-shell">sudo dnf install -y kubeadm kubelet</code></pre>
<br>

<p>패키지 버전을 고정할 수 있는 dnf 플러그인을 설치합니다.</p>
<pre><code class="language-shell">sudo dnf install -y &#39;dnf-command(versionlock)&#39;</code></pre>
<br>

<p>Kubernetes Package의 버전을 고정합니다. (업데이트 방지)</p>
<pre><code class="language-shell">sudo dnf versionlock add kubeadm kubelet</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/4f39c97b-11c2-42ec-9c95-a7d67aeb55c9/image.png" alt=""></p>
<br>

<p>swap 메모리를 비활성화합니다.</p>
<pre><code class="language-shell">sudo swapoff -a</code></pre>
<br>

<p>재부팅 후에도 swap 메모리가 비활성화 되도록 설정합니다.</p>
<pre><code class="language-shell">sudo sed -i &#39;/ swap / s/^/#/&#39; /etc/fstab</code></pre>
<br>

<p>SELinux를 비활성화 합니다. (충돌 방지)</p>
<pre><code class="language-shell">sudo setenforce 0</code></pre>
<br>

<p>SELinux를 영구적으로 Permissive 모드로 설정합니다.</p>
<pre><code class="language-shell">sudo sed -i &#39;s/^SELINUX=enforcing$/SELINUX=permissive/&#39; /etc/selinux/config</code></pre>
<br>

<p>Pod 네트워크 라우팅을 허용합니다.</p>
<pre><code class="language-shell">sudo sh -c &#39;echo &quot;net.ipv4.ip_forward=1&quot; &gt;&gt; /etc/sysctl.conf&#39;</code></pre>
<br>

<p>sysctl 명령어를 사용하여 설정을 적용합니다.</p>
<pre><code class="language-shell">sudo sysctl -p</code></pre>
<br>

<p>kubelet을 자동 실행합니다.</p>
<pre><code class="language-shell">sudo systemctl enable --now kubelet</code></pre>
<br>

<p>Worker Node를 Master Node에 Join합니다. Master Node에서 kubeadm init 후 나온 값을 넣습니다.</p>
<blockquote>
<p>만약, 해당 값을 잊어버렸다면 Master Node에서 아래 명령어 입력 후 나온 값을 넣어줍니다. <br></p>
</blockquote>
<pre><code class="language-shell">sudo kubeadm token create --print-join-command</code></pre>
<pre><code class="language-shell">sudo kubeadm join 10.0.2.135:6443 --token a5fwyw.t1nljbrukh3k6d2z \
  --discovery-token-ca-cert-hash sha256:85d09c2a09226d7c2678d178ce8c0ebcc1cd14ff0f4471bcc2b7e1e902b5bc49</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/74406609-12b2-410f-9ffd-c289932803f0/image.png" alt=""></p>
<br>

<h3 id="master-node-1">Master Node</h3>
<p>Worker Node가 Master Node에 정상적으로 Join 되었는지 확인합니다. Node의 상태가 Ready인지 확인합니다.</p>
<pre><code class="language-shell">kubectl get no</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/b3db9ada-8f17-4140-ad23-f5cf921d8be0/image.png" alt=""></p>
<br>

<p>Namespace를 생성합니다.</p>
<pre><code class="language-shell">kubectl create ns demo</code></pre>
<br>

<p>환경변수를 설정합니다.</p>
<pre><code class="language-shell">ACCOUNT_ID=$(aws sts get-caller-identity --query &quot;Account&quot; --output text)
REGION_CODE=&quot;ap-northeast-2&quot;</code></pre>
<br>

<p>ECR에서 이미지를 할당 받을 수 있도록 ECR에 대한 Secrets를 생성합니다.</p>
<pre><code class="language-shell">kubectl create secret docker-registry ecr-secret \
  --namespace=demo \
  --docker-server=$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com \
  --docker-username=AWS \
  --docker-password=$(aws ecr get-login-password --region $REGION_CODE)</code></pre>
<br>

<p>Deployment 설정 파일을 생성합니다.</p>
<pre><code class="language-shell">cat &lt;&lt;\EOF&gt; deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-deploy
  namespace: demo
  labels:
    app: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      imagePullSecrets:
      - name: ecr-secret
      containers:
      - name: demo-cnt
        image: IMAGE
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
EOF</code></pre>
<br>

<p>이미지에 대한 환경변수를 설정합니다.</p>
<pre><code class="language-shell">ECR_NAME=&quot;demo-ecr&quot;
IMAGE_TAG=&quot;v1.0.0&quot;
ECR_URI=$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com/$ECR_NAME</code></pre>
<br>

<p>이미지를 ECR Registry에 업로드된 이미지로 변경합니다.</p>
<pre><code class="language-shell">sed -i &quot;s|IMAGE|$ECR_URI:$IMAGE_TAG|g&quot; deployment.yaml</code></pre>
<br>

<p>Deployment를 배포합니다.</p>
<pre><code class="language-shell">kubectl apply -f deployment.yaml</code></pre>
<br>

<p>Service 설정 파일을 생성합니다.</p>
<pre><code class="language-shell">cat &lt;&lt;\EOF&gt; service.yaml
apiVersion: v1
kind: Service
metadata:
  name: demo-svc
  namespace: demo
spec:
  selector:
    app: demo
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30000
  type: NodePort
EOF</code></pre>
<br>

<p>Service를 배포합니다.</p>
<pre><code class="language-shell">kubectl apply -f service.yaml</code></pre>
<br>

<h3 id="worker-node-1">Worker Node</h3>
<pre><code class="language-shell">curl http://localhost:30000/version</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/90b54226-b444-45e5-b1d5-655fe9dfc0e8/image.png" alt=""></p>
<br>

<pre><code class="language-shell">curl http://localhost:30000/healthcheck</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7c075844-5abc-46f7-80e1-286e5092591b/image.png" alt=""></p>
<p>정상적으로 API 호출이 되는 것을 확인할 수 있습니다.</p>
<br>

<h2 id="alb-1">ALB</h2>
<h3 id="target-group">Target Group</h3>
<p>Target Group Port는 Service의 NodePort(30000)에 맞춰 Target Group Port를 동일하게 설정합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/eb71dd06-c952-4ad5-a17a-261819704286/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/831447bf-2c77-4da9-8011-1df4c7daba4c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/c0df27f1-2a2b-457e-9eb2-8d21524b8de0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/229b9300-7fdc-4160-b55f-eb404995d3cc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/8281ad20-695e-4a7d-b35a-e95a11b3ccbc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/96d2d46f-7bf3-43f3-9a3e-d3d04c7806d1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/60d20fe2-6832-4b46-8fea-3f548fb363ee/image.png" alt=""></p>
<br>

<h3 id="load-balancer">Load Balancer</h3>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/c76b77d5-aa62-489b-9774-08ff993366d1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/76bdbc4d-46f4-4272-b805-01a166370b69/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/66b23169-b8a2-4f9d-a0b9-02c4d9998e36/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/9180b8df-e683-4589-9b86-1a93eeb6c53a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7f3b4d69-d962-40c4-a030-760d44d16bd0/image.png" alt=""></p>
<br>

<h1 id="test">Test</h1>
<pre><code class="language-shell">ALB_NAME=&quot;demo-alb&quot;
ALB_DNS=$(aws elbv2 describe-load-balancers --names $ALB_NAME --query &quot;LoadBalancers[].DNSName&quot; --output text)</code></pre>
<pre><code class="language-shell">curl $ALB_DNS/version</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/95deed2f-08fd-490c-9637-7997abb1a768/image.png" alt=""></p>
<pre><code class="language-shell">curl $ALB_DNS/healthcheck</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/1acedc9e-54b2-4229-9e79-a112420a9152/image.png" alt=""></p>
<p>정상적으로 API 호출이 되는 것을 확인할 수 있습니다.</p>
<br>

<h1 id="result">Result</h1>
<p>이번 실습을 통해 kubeadm을 활용하여 Kubernetes 클러스터를 직접 구성하는 전체 과정을 확인할 수 있었습니다.
기존에는 관리형 서비스 없이 Kubernetes 환경을 구축하기 위해 복잡한 설정과 수동 구성이 필요했지만, kubeadm을 사용하면 Control Plane과 Worker Node를 비교적 표준화된 방식으로 손쉽게 구성할 수 있습니다.</p>
<p>또한 containerd, CNI 플러그인, kubelet 등의 구성 요소를 직접 설치하고 설정함으로써 Kubernetes의 내부 구조와 동작 방식을 깊이 이해할 수 있었습니다. 특히 etcd, API Server, 그리고 네트워크 구성 요소 간의 관계를 실습을 통해 명확히 파악할 수 있었습니다.</p>
<p>더불어 Secret의 etcd 저장 구조와 Encryption 설정을 통해 Kubernetes의 보안 메커니즘까지 확인할 수 있었으며, NodePort 기반 서비스와 외부 접근 구성을 통해 실제 서비스 배포 흐름을 경험할 수 있었습니다.</p>
<p>이러한 과정은 관리형 Kubernetes(EKS 등)를 사용할 때는 보이지 않는 내부 동작을 이해하는 데 중요한 기반이 되며, 문제 해결 능력과 운영 역량을 향상시키는 데 큰 도움이 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] CPU 아키텍처에 따른 Docker Multi Architecture 구성하기 with Amazon ECR]]></title>
            <link>https://velog.io/@on_cloud/docker-multi-architecture</link>
            <guid>https://velog.io/@on_cloud/docker-multi-architecture</guid>
            <pubDate>Wed, 15 Apr 2026 10:42:26 GMT</pubDate>
            <description><![CDATA[<h1 id="overview">Overview</h1>
<p>Docker 이미지는 기본적으로 특정 CPU 아키텍처에 따라 만들어지기 때문에(ex: x86_64, ARM64 등) 서로 다른 환경에서 동일한 이미지를 실행하려고 하면 아키텍처 호환성 문제가 생길 수 있습니다. 최근에는 클라우드 환경, IoT 디바이스, 그리고 Apple Silicon처럼 여러 가지 플랫폼이 함께 사용되는 시대여서 이런 제약이 더욱 뚜렷하게 드러납니다.</p>
<p>이 글에서는 이러한 문제를 해결할 수 있도록 Docker의 멀티 아키텍처 이미지 구성 방법을 설명합니다. buildx를 사용해 여러 플랫폼용 이미지를 빌드하는 방법부터 manifest를 활용한 통합 이미지 관리 방법까지, 실제 따라 해볼 수 있는 예제를 중심으로 다룰 예정입니다. 이를 통해 하나의 이미지 태그만으로 다양한 아키텍처를 지원하는 과정을 이해하는 것이 목표입니다.</p>
<p>즉, 하나의 이미지를 여러 아키텍처 환경에서 그대로 사용하기 위해서는 멀티 아키텍처 이미지 구성이 필요합니다.</p>
<br>

<h1 id="setup">Setup</h1>
<h2 id="application">Application</h2>
<pre><code class="language-go">package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;net/http&quot;
    &quot;runtime&quot;
)

func arch(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, &quot;Application Running\nARCH=%s\n&quot;, runtime.GOARCH)
}

func healthcheck(w http.ResponseWriter, req *http.Request) {
    fmt.Fprint(w, &quot;OK&quot;)
}

func main() {
    http.HandleFunc(&quot;/arch&quot;, arch)
    http.HandleFunc(&quot;/healthcheck&quot;, healthcheck)
    log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}</code></pre>
<p>해당 애플리케이션은 실행 중인 컨테이너의 CPU 아키텍처를 확인하기 위한 테스트용 코드입니다.</p>
<br>

<h2 id="dockerfile">Dockerfile</h2>
<pre><code class="language-docker">FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder

WORKDIR /build

ARG TARGETOS
ARG TARGETARCH
ENV CGO_ENABLED=0

COPY ./main.go ./

RUN go mod init noah.io/ark/rest &amp;&amp; go mod tidy

RUN --mount=type=cache,target=/root/.cache/go-build \
    GOOS=$TARGETOS \
    GOARCH=$TARGETARCH \
    go build -trimpath -buildvcs=false -ldflags=&quot;-s -w&quot; -o main main.go

FROM alpine:3.20

WORKDIR /app

RUN apk add --no-cache curl

COPY --from=builder /build/main .

EXPOSE 8080
ENTRYPOINT [&quot;/app/main&quot;]</code></pre>
<ul>
<li><code>--platform=$BUILDPLATFORM</code> : 빌드 환경 기준으로 builder 이미지 선택</li>
<li><code>TARGETOS</code>, <code>TARGETARCH</code> : buildx가 자동으로 전달하는 타겟 플랫폼 정보</li>
<li><code>GOOS</code>, <code>GOARCH</code> : 크로스 컴파일을 통해 다른 아키텍처용 바이너리 생성</li>
</ul>
<br>

<h1 id="practice">Practice</h1>
<blockquote>
<p>ECR에 업로드 하는 Layer는 하나의 ECR Repository에서만 동작합니다.</p>
</blockquote>
<p>Amazon ECR Registry를 생성합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/5946a6c5-ae09-4d78-9d1e-c037873c3af9/image.png" alt=""></p>
<br>

<p><a href="https://github.com/docker/buildx/releases">buildx Package</a>를 설치합니다.</p>
<ul>
<li>Amazon Linux 사용 시 buildx Package 설치를 안해도 무방합니다.<pre><code class="language-shell"># AMD64
mkdir -p ~/.docker/cli-plugins
curl -L https://github.com/docker/buildx/releases/download/v0.33.0/buildx-v0.33.0.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
</code></pre>
</li>
</ul>
<h1 id="arm64">ARM64</h1>
<p>mkdir -p ~/.docker/cli-plugins
curl -L <a href="https://github.com/docker/buildx/releases/download/v0.33.0/buildx-v0.33.0.linux-arm64">https://github.com/docker/buildx/releases/download/v0.33.0/buildx-v0.33.0.linux-arm64</a> -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx</p>
<pre><code>
&lt;br&gt;

buildx Version을 확인합니다.
```shell
docker buildx version</code></pre><br>

<p>환경변수를 입력합니다.</p>
<pre><code class="language-shell">ACCOUNT_ID=$(aws sts get-caller-identity --query &quot;Account&quot; --output text)
REGION_CODE=&quot;ap-northeast-2&quot;
ECR_NAME=&quot;demo-ecr&quot;
ECR_URI=&quot;$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com/$ECR_NAME&quot;
IMAGE_TAG=&quot;v1.0.0&quot;</code></pre>
<br>

<p>서로 다른 CPU 아키텍처 이미지를 빌드하기 위해서 tonistiigi/binfmt 컨테이너를 실행합니다.</p>
<pre><code class="language-shell">docker run --privileged --rm tonistiigi/binfmt --install all</code></pre>
<br>

<p>멀티 아키텍처 이미지를 빌드하기 위한 전용 빌더 환경을 생성합니다.</p>
<pre><code class="language-shell">docker buildx create --name multi-builder --driver docker-container --use --bootstrap</code></pre>
<br>

<p>ECR Registry에 로그인합니다.</p>
<pre><code class="language-shell">aws ecr get-login-password --region $REGION_CODE | docker login --username AWS --password-stdin &quot;$ACCOUNT_ID.dkr.ecr.$REGION_CODE.amazonaws.com&quot;</code></pre>
<br>

<p>CPU 아키텍처별로 Docker 이미지를 각각 따로 빌드합니다.</p>
<pre><code class="language-shell">docker buildx build --platform linux/amd64 -t $ECR_URI:$IMAGE_TAG-amd64 --load .
docker buildx build --platform linux/arm64 -t $ECR_URI:$IMAGE_TAG-arm64 --load .</code></pre>
<br>

<p>Amazon ECR에 Docker 이미지를 Push합니다.</p>
<pre><code class="language-shell">docker push $ECR_URI:$IMAGE_TAG-amd64
docker push $ECR_URI:$IMAGE_TAG-arm64</code></pre>
<br>

<p>Amazon ECR에 업로드한 Docker 이미지를 조회합니다. 조회 시 CPU 아키텍처별로 이미지가 업로드된 것을 확인할 수 있습니다.</p>
<pre><code class="language-shell">aws ecr describe-images --repository-name $ECR_NAME --region $REGION_CODE</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/20a35dbc-85ab-464d-b0ee-0067b7256e43/image.png" alt=""></p>
<br>

<p>CPU 아키텍처 이미지를 하나로 만들기 위해서 멀티 아키텍처 이미지로 묶습니다.</p>
<ul>
<li>manifest는 실제 이미지를 합치는 것이 아니라, 각 아키텍처 이미지를 참조하는 메타데이터입니다.<pre><code class="language-shell">docker manifest create $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-amd64 $ECR_URI:$IMAGE_TAG-arm64</code></pre>
</li>
</ul>
<br>

<p>manifest에 각 이미지의 정확한 플랫폼 정보(OS/아키텍처)를 명시해줍니다.</p>
<pre><code class="language-shell">docker manifest annotate $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-amd64 --os linux --arch amd64
docker manifest annotate $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG-arm64 --os linux --arch arm64</code></pre>
<br>

<p>멀티 아키텍처 manifest 안에 어떤 이미지들이 들어있는지 확인합니다.</p>
<pre><code class="language-shell">docker manifest inspect $ECR_URI:$IMAGE_TAG</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7011b8c1-0388-4828-9e8a-b2b6576c4a08/image.png" alt=""></p>
<br>

<p>로컬에서 만든 멀티 아키텍처 manifest를 Amazon ECR에 업로드합니다.</p>
<pre><code class="language-shell">docker manifest push $ECR_URI:$IMAGE_TAG</code></pre>
<br>

<h1 id="test">Test</h1>
<pre><code class="language-shell">docker run --platform=linux/amd64 -d -p 8080:8080 $ECR_URI:$IMAGE_TAG</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/4b104e07-09ba-4cc5-8847-0162cc7a6afc/image.png" alt=""></p>
<br>

<pre><code class="language-shell">docker run --platform=linux/arm64 -d -p 8080:8080 $ECR_URI:$IMAGE_TAG</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/b6b2117c-4930-4465-804c-23f30e0ad82b/image.png" alt=""></p>
<p>동일한 이미지 태그를 사용하더라도 <code>--platform</code> 옵션에 따라 서로 다른 아키텍처의 컨테이너가 실행되는 것을 확인할 수 있습니다.</p>
<h1 id="result">Result</h1>
<p>이번 실습을 통해 Docker에서 멀티 아키텍처 이미지를 구성하는 전체 과정을 확인할 수 있었습니다.<br>기존에는 CPU 아키텍처(x86, ARM 등)가 다른 환경에서는 동일한 이미지를 사용할 수 없었지만, buildx와 manifest를 활용하면 하나의 이미지 태그로 여러 플랫폼을 동시에 지원할 수 있습니다.</p>
<p>이러한 방식은 클라우드 환경, CI/CD 파이프라인, 그리고 다양한 디바이스를 대상으로 하는 서비스에서 매우 중요한 요소이며, 단일 이미지 태그로 다양한 실행 환경을 지원할 수 있다는 점에서 운영 효율성과 확장성을 크게 향상시킬 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Site-to-Site VPN (PSK) on Ubuntu]]></title>
            <link>https://velog.io/@on_cloud/Site-to-Site-VPN-PSK-on-VMware</link>
            <guid>https://velog.io/@on_cloud/Site-to-Site-VPN-PSK-on-VMware</guid>
            <pubDate>Wed, 15 Oct 2025 11:26:19 GMT</pubDate>
            <description><![CDATA[<p>Environment: Ubuntu 24.04 LTS</p>
<hr>
<h2 id="setting-vmnet">Setting VMnet</h2>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/1d646c11-aa2a-4845-b59e-af68a2f6754c/image.png" alt=""></p>
<hr>
<h2 id="common-setting">Common Setting</h2>
<blockquote>
<p>패키지서버에서 패키지를 다운 받아야 하기 때문에 NAT인 VMnet0를 사용합니다.</p>
</blockquote>
<pre><code class="language-shell">vim /etc/vim/vimrc
set number</code></pre>
<pre><code class="language-shell">vim /etc/netplan/config.yaml
network:
  version: 2
  ethernets:
    ens33:
     dhcp4: true</code></pre>
<pre><code class="language-shell">apt update -y
apt upgrade -y</code></pre>
<pre><code class="language-shell">apt install -y net-tools</code></pre>
<hr>
<h2 id="ip-setting">IP Setting</h2>
<blockquote>
<p>패키지 설치 후 해당 머신에 맞는 VMnet으로 변경합니다.</p>
</blockquote>
<h3 id="client_a">[Client_A]</h3>
<pre><code class="language-shell">echo &#39;client-a&#39; &gt; /etc/hostname</code></pre>
<pre><code class="language-shell">vim /etc/netplan/config.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens33:
      addresses: [192.168.10.254/24]
      routes:
        - to: default
          via: 192.168.10.1</code></pre>
<pre><code class="language-shell">netplan apply</code></pre>
<br>

<h3 id="client_b">[Client_B]</h3>
<pre><code class="language-shell">echo &#39;client-b&#39; &gt; /etc/hostname</code></pre>
<pre><code class="language-shell">vim /etc/netplan/config.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens33:
      addresses: [192.168.10.254/24]
      routes:
        - to: default
          via: 192.168.10.1</code></pre>
<pre><code class="language-shell">netplan apply</code></pre>
<br>

<h3 id="server_a">[Server_A]</h3>
<pre><code class="language-shell">echo &#39;server-a&#39; &gt; /etc/hostname</code></pre>
<pre><code class="language-shell">vim /etc/netplan/config.yaml                                                                    
network:
    version: 2
  ethernets:
        ens33:
            addresses: [192.168.10.1/24]
        ens36:
            addresses: [10.0.0.1/24]
            routes:
                - to: 10.0.1.0/24
                    via: 10.0.0.1</code></pre>
<pre><code class="language-shell">netplan apply</code></pre>
<br>

<h3 id="server_b">[Server_B]</h3>
<pre><code class="language-shell">echo &#39;server-b&#39; &gt; /etc/hostname</code></pre>
<pre><code class="language-shell">vim /etc/netplan/config.yaml
network:
    version: 2
    renderer: networkd
    ethernets:
        ens33:
            addresses: [192.168.20.1/24]
        ens36:
            addresses: [10.0.1.1/24]
            routes:
                - to: 10.0.0.0/24
                    via: 10.0.1.1</code></pre>
<pre><code class="language-shell">netplan apply</code></pre>
<hr>
<h2 id="setting-site-to-site-vpn">Setting Site-to-Site VPN</h2>
<h3 id="server_a-1">[Server_A]</h3>
<pre><code class="language-bash">apt install -y strongswan</code></pre>
<pre><code class="language-bash">systemctl enable --now ipsec</code></pre>
<pre><code class="language-bash">vim /etc/ipsec.conf    
conn s2s
    type=tunnel
    left=10.0.0.1
    leftsubnet=192.168.10.0/24
    right=10.0.1.1
    rightsubnet=192.168.20.0/24
    keyexchange=ikev2
    authby=secret
    leftauth=psk
    rightauth=psk
    auto=start
    ike=aes128-sha1-modp2048
    ikelifetime=28800
    esp=3des-md5-modp2048
    lifetime=3600
    compress=no
    keyingtries=%forever</code></pre>
<pre><code class="language-bash">vim /etc/ipsec.secrets
10.0.0.1 10.0.1.1 : PSK &quot;S2S&quot;</code></pre>
<pre><code class="language-bash">systemctl restart strongswan-starter</code></pre>
<pre><code class="language-bash">ipsec statusall</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/46ef8d8c-3dde-4bbf-9836-ec478357096a/image.png" alt=""></p>
<br>

<h3 id="server_b-1">[Server_B]</h3>
<pre><code class="language-bash">apt install -y strongswan</code></pre>
<pre><code class="language-bash">systemctl enable --now ipsec</code></pre>
<pre><code class="language-bash">vim /etc/ipsec.conf
conn s2s
    type=tunnel
    left=10.0.1.1
    leftsubnet=192.168.20.0/24
    right=10.0.0.1
    rightsubnet=192.168.10.0/24
    keyexchange=ikev2
    authby=secret
    leftauth=psk
    rightauth=psk
    auto=start
    ike=aes128-sha1-modp2048
    ikelifetime=28800
    esp=3des-md5-modp2048
    lifetime=3600
    compress=no
    keyingtries=%forever</code></pre>
<pre><code class="language-bash">vim /etc/ipsec.secrets
10.0.0.1 10.0.1.1 : PSK &quot;S2S&quot;</code></pre>
<pre><code class="language-bash">systemctl restart strongswan-starter</code></pre>
<pre><code class="language-bash">ipsec statusall</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/ff7b0476-7e03-48f8-92c5-65eb3a645531/image.png" alt=""></p>
<hr>
<h2 id="result">Result</h2>
<h3 id="client_a-1">[Client_A]</h3>
<pre><code class="language-shell">ping 192.168.20.254 -c 4</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/f397871f-15de-4d80-ab2e-fb3ba5f5e72b/image.png" alt=""></p>
<pre><code class="language-shell">tcpdump -p icmp -n</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/c719935b-637e-4df9-9f6b-e03d4f54a51f/image.png" alt=""></p>
<br>

<h3 id="client_b-1">[Client_B]</h3>
<pre><code class="language-shell">ping 192.168.10.254 -c 4</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/654c7e09-d6f3-4cfd-b590-16452a17a497/image.png" alt=""></p>
<pre><code class="language-shell">tcpdump -p icmp -n</code></pre>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/6d186de3-e1d0-4767-afc6-d2f5f7ac1b61/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] UFW]]></title>
            <link>https://velog.io/@on_cloud/Linux-UFW</link>
            <guid>https://velog.io/@on_cloud/Linux-UFW</guid>
            <pubDate>Tue, 25 Mar 2025 07:52:23 GMT</pubDate>
            <description><![CDATA[<h3 id="what-is-ufw">What is UFW?</h3>
<p>Uncomplicated Firewall의 약자로 Ubuntu 및 Debian 계열의 리눅스 배포판에서 간편하게 방화벽을 관리할 수 있도록 설계된 도구이며, Netfilter 기반의 iptables를 쉽게 다룰 수 있도록 만든 명령어</p>
<hr>
<h3 id="command">Command</h3>
<h4 id="install-package">Install Package</h4>
<pre><code class="language-shell">apt install -y ufw</code></pre>
<br>

<hr>
<h4 id="기본-명령어">기본 명령어</h4>
<pre><code class="language-shell">ufw enable</code></pre>
<p>ufw 활성화</p>
<br>

<pre><code class="language-shell">ufw disable</code></pre>
<p>ufw 비활성화</p>
<br>

<pre><code class="language-shell">ufw status verbose</code></pre>
<p>ufw 상태 확인</p>
<br>

<pre><code class="language-shell">ufw reset</code></pre>
<p>룰 초기화</p>
<br>

<hr>
<h4 id="규칙-추가">규칙 추가</h4>
<pre><code class="language-shell">ufw allow [Port]</code></pre>
<p>Port 허용</p>
<br>

<pre><code class="language-shell">ufw allow [Port]/[Protocol]</code></pre>
<p>Port, Protocol 허용</p>
<br>

<pre><code class="language-shell">ufw allow proto [Protocol] from any to any port [Port]</code></pre>
<p>Port, Protocol 허용</p>
<br>

<pre><code class="language-shell">ufw allow from [IP_Address]</code></pre>
<p>특정 IP에서 들어오는 모든 트래픽 허용</p>
<br>

<pre><code class="language-shell">ufw allow from [IP_Address]/[Prefix]</code></pre>
<p>특정 Subnet에서 들어오는 트래픽 허용</p>
<br>

<pre><code class="language-shell">ufw allow in on [Network_Interface]</code></pre>
<p>Network Interface에서 들어오는 모든 트래픽 허용</p>
<br>

<hr>
<h4 id="규칙-삭제">규칙 삭제</h4>
<pre><code class="language-shell">ufw delete allow [Port]</code></pre>
<p>Port 허용 규칙 삭제</p>
<br>

<pre><code class="language-shell">ufw delete allow from [IP_Address]</code></pre>
<p>특정 IP에서 오는 허용 규칙 삭제</p>
<br>

<pre><code class="language-shell">ufw status numbered</code></pre>
<p>번호가 있는 규칙 목록 확인</p>
<br>

<pre><code class="language-shell">ufw delete [Number]</code></pre>
<p>Number 규칙 삭제</p>
<br>

<hr>
<h4 id="트래픽-차단">트래픽 차단</h4>
<pre><code class="language-shell">ufw deny [Port]</code></pre>
<p>Port 차단</p>
<br>

<pre><code class="language-shell">ufw deny from [IP_Address]</code></pre>
<p>특정 IP에서 오는 모든 트래픽 차단</p>
<br>

<pre><code class="language-shell">ufw deny in on [Network_Interface]</code></pre>
<p>Network Interface에서 들어오는 모든 트래픽 차단</p>
<br>

<pre><code class="language-shell">ufw deny proto [Protocol] from any to any port [Port]</code></pre>
<p>Port, Protocol 차단</p>
<br>

<pre><code class="language-shell">vim /etc/ufw/before.rules
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type source-quench -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT</code></pre>
<p>ICMP 허용
ACCEPT 부분을 DROP으로 변경 시 ping 요청이 거부됨.</p>
<br>

<hr>
<h4 id="로그-관리">로그 관리</h4>
<pre><code class="language-shell">ufw logging on</code></pre>
<p>방화벽 로그 활성화</p>
<br>

<pre><code class="language-shell">ufw logging off</code></pre>
<p>방화벽 로그 비활성화</p>
<br>

<pre><code class="language-shell">ufw logging low</code></pre>
<p>최소한의 로그 기록</p>
<br>

<pre><code class="language-shell">ufw logging medium</code></pre>
<p>기본 로그 기록</p>
<br>

<pre><code class="language-shell">ufw logging high</code></pre>
<p>상세한 로그 기록</p>
<br>

<pre><code class="language-shell">ufw logging full</code></pre>
<p>모든 트래픽 로그 기록</p>
<br>

<hr>
<h4 id="기본-정책-설정">기본 정책 설정</h4>
<pre><code class="language-shell">ufw default deny incoming</code></pre>
<p>기본적으로 모든 들어오는 트래픽 차단</p>
<br>

<pre><code class="language-shell">ufw default allow outgoing</code></pre>
<p>기본적으로 모든 나가는 트래픽 허용</p>
<br>

<pre><code class="language-shell">ufw default deny outgoing</code></pre>
<p>기본적으로 모든 나가는 트래픽 차단</p>
<br>

<pre><code class="language-shell">ufw default allow incoming</code></pre>
<p>기본적으로 모든 들어오는 트래픽 허용</p>
<br>

<hr>
<h4 id="애플리케이션별-방화벽-설정">애플리케이션별 방화벽 설정</h4>
<pre><code class="language-shell">ufw app list</code></pre>
<p>UFW가 관리하는 애플리케이션 목록 확인</p>
<br>

<pre><code class="language-shell">ufw app info [Service_Name]</code></pre>
<p>Service의 방화벽 설정 확인</p>
<br>

<pre><code class="language-shell">ufw allow [Service_Name]</code></pre>
<p>Service 허용</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] NFTables]]></title>
            <link>https://velog.io/@on_cloud/Linux-NFTables</link>
            <guid>https://velog.io/@on_cloud/Linux-NFTables</guid>
            <pubDate>Tue, 25 Mar 2025 07:35:59 GMT</pubDate>
            <description><![CDATA[<h3 id="기본-개념">기본 개념</h3>
<ol>
<li><strong>Table</strong></li>
</ol>
<ul>
<li>패킷 필터링을 적용할 룰셋을 포함하는 컨테이너 역할</li>
<li>패킷을 처리할 <strong><code>Chain</code></strong>을 포함</li>
</ul>
<ol start="2">
<li><strong>(Chain)</strong></li>
</ol>
<ul>
<li>트래픽이 특정 조건을 만족할 경우 수행할 규칙(룰)의 모음</li>
<li>체인은 <code>hook</code>을 통해 커널 네트워크 스택에 연결됨</li>
</ul>
<ol start="3">
<li><strong>Rule</strong></li>
</ol>
<ul>
<li>특정 조건을 만족하는 패킷에 대해 실행할 동작을 정의</li>
</ul>
<ol start="4">
<li><strong>Set</strong></li>
</ol>
<ul>
<li>여러 개의 IP 주소, 포트 번호 등을 그룹화하여 관리 가능</li>
</ul>
<ol start="5">
<li><strong>Map</strong></li>
</ol>
<ul>
<li>특정 값과 대응되는 다른 값을 매핑하여 관리 가능</li>
</ul>
<hr>
<h3 id="nftables-chain--hook">Nftables Chain &amp; Hook</h3>
<table>
<thead>
<tr>
<th align="left">Hook Location</th>
<th align="left">Description</th>
<th align="left">Applicable Table</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>prerouting</strong></td>
<td align="left">패킷이 시스템에 도착했을 때 즉시 처리</td>
<td align="left"><strong>ip</strong>, <strong>ip6</strong>, <strong>inet</strong>, <strong>arp</strong></td>
</tr>
<tr>
<td align="left"><strong>input</strong></td>
<td align="left">시스템이 대상인 패킷을 필터링</td>
<td align="left"><strong>ip</strong>, <strong>ip6</strong>, <strong>inet</strong></td>
</tr>
<tr>
<td align="left"><strong>forward</strong></td>
<td align="left">다른 인터페이스로 전달되는 패킷 필터링</td>
<td align="left"><strong>ip</strong>, <strong>ip6</strong>, <strong>inet</strong></td>
</tr>
<tr>
<td align="left"><strong>output</strong></td>
<td align="left">시스템이 생성한 패킷 필터링</td>
<td align="left"><strong>ip</strong>, <strong>ip6</strong>, <strong>inet</strong></td>
</tr>
<tr>
<td align="left"><strong>postrouting</strong></td>
<td align="left">인터페이스를 통해 나가기 직전 패킷을 처리</td>
<td align="left"><strong>ip</strong>, <strong>ip6</strong>, <strong>inet</strong>, <strong>arp</strong></td>
</tr>
</tbody></table>
<hr>
<h3 id="command">Command</h3>
<p><strong>Install Package</strong></p>
<pre><code class="language-shell">apt install -y nftables
systemctl enable --now nftables</code></pre>
<p><strong>Basic Structure</strong></p>
<pre><code class="language-shell">nft add rule [family] [table] [chain] ip saddr [Source] sport [Source_Port] ip daddr [Destination] dport [Destination_Port] [Action]</code></pre>
<p><strong>Command</strong></p>
<pre><code class="language-shell">nft add table ip [Table_Name]</code></pre>
<p>Table 생성 (대소문자 구별함)</p>
<br>

<pre><code class="language-shell">nft add chain ip [NAT] [Chain_Name] { type [Type] hook [postrouting] priority [Priority_Number] \; }</code></pre>
<p>Additional Description </p>
<table>
<thead>
<tr>
<th align="left"><strong>Type</strong></th>
<th align="left"><strong>Description</strong></th>
<th align="left"><strong>Use Hook</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left">Filter</td>
<td align="left">방화벽 규칙 (패킷 필터링)</td>
<td align="left"><strong>INPUT</strong>, <strong>OUTPUT</strong>, <strong>FORWARD</strong></td>
</tr>
<tr>
<td align="left">NAT</td>
<td align="left">NAT 변환 (IP 주소 변경)</td>
<td align="left"><strong>PREROUTING</strong>, <strong>POSTROUTING</strong></td>
</tr>
<tr>
<td align="left">Route</td>
<td align="left">패킷 경로 변경</td>
<td align="left"><strong>PREROUTING</strong>, <strong>OUTPUT</strong></td>
</tr>
<tr>
<td align="left">Security</td>
<td align="left">보안 정책 적용</td>
<td align="left"><strong>INPUT</strong>, <strong>OUTPUT</strong>, <strong>FORWARD</strong></td>
</tr>
</tbody></table>
<br>

<table>
<thead>
<tr>
<th align="left"><strong>Priority Range</strong></th>
<th align="left"><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>-300 ~ -100</strong></td>
<td align="left">패킷을 수정하는 규칙 (ex: mangle, route)</td>
</tr>
<tr>
<td align="left"><strong>-99 ~ -1</strong></td>
<td align="left">연결 추적 및 정책 결정 관련 (ex: conntrack)</td>
</tr>
<tr>
<td align="left"><strong>0</strong></td>
<td align="left">기본 우선순위 (특별한 설정이 없으면 이 값 사용)</td>
</tr>
<tr>
<td align="left"><strong>1 ~ 99</strong></td>
<td align="left">일반적인 방화벽 및 필터 규칙</td>
</tr>
<tr>
<td align="left"><strong>100 이상</strong></td>
<td align="left">NAT 규칙 (postrouting에서 주로 사용됨)</td>
</tr>
</tbody></table>
<br>

<pre><code class="language-shell">nft list tables</code></pre>
<p>테이블 조회</p>
<br>

<pre><code class="language-shell">nft table ip [Table_Name]</code></pre>
<br>

<pre><code class="language-shell">nft add rule ip [Table_Name] [Chain_Name] ip saddr [Source_IP] ip daddr [Destination_IP] counter [Action]</code></pre>
<br>

<pre><code class="language-shell">ntf insert rule ip [Table_Name] input [Protocol] dport [Destination_Port] counter accept</code></pre>
<p>단일 Port 필터링</p>
<br>

<pre><code class="language-shell">ntf insert rule ip [Table_Name] input [Protocol] dport {[Destination_Port], [Destination_Port]} counter accept</code></pre>
<p>멀티 Port 필터링</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] Iptables]]></title>
            <link>https://velog.io/@on_cloud/Linux-Iptables</link>
            <guid>https://velog.io/@on_cloud/Linux-Iptables</guid>
            <pubDate>Mon, 24 Mar 2025 02:04:22 GMT</pubDate>
            <description><![CDATA[<h3 id="기능">기능</h3>
<ul>
<li>Packet Filtering</li>
<li>State Insepction</li>
<li>Application Gateway - OSI 7 Layer</li>
</ul>
<hr>
<h3 id="chain-기능">Chain 기능</h3>
<p><strong>Filter Rule</strong></p>
<ul>
<li>INPUT: 패킷이 서버로 들어오는 경우<img src="https://velog.velcdn.com/images/on_cloud/post/5046a3ec-898f-4c36-af5d-19504d12acbe/image.png" width="600px">
> Linux 서버가 목적지인 패킷 (패킷이 서버로 들어옴)

</li>
</ul>
<br>

<ul>
<li>OUTPUT: 서버에서 패킷이 외부로 나가는 경우<img src="https://velog.velcdn.com/images/on_cloud/post/af1d6a1b-26e6-47da-b4cd-39d50dcd3e5c/image.png" width="600px">
> Linux 서버에서 외부 목적지로 나가는 패킷 (서버의 패킷이 외부로 나감)

</li>
</ul>
<br>

<ul>
<li>FORWARD: 패킷이 서버를 지나가는 경우 (INPUT &amp; OUTPUT)<img src="https://velog.velcdn.com/images/on_cloud/post/246a2ec2-8505-40a6-bfc9-11b5aa88c442/image.png" width="600px">
> Source or Destination도 아닌 거쳐가는 경우
(NAT 네트워크 공유 기능을 위해 사용됨)

</li>
</ul>
<br>

<p><strong>NAT Rule</strong></p>
<ul>
<li>PREROUTING<ul>
<li>DNAT(Destination NAT) 룰 적용</li>
<li>패킷의 도착지 주소를 변경</li>
</ul>
</li>
<li>POSTROUTING<ul>
<li>SNAT(Source NAT) 또는 masquerad 룰 적용</li>
<li>패킷의 출발지 주소를 변경</li>
</ul>
</li>
</ul>
<hr>
<h3 id="iptables-table--chain">iptables Table &amp; Chain</h3>
<table>
<thead>
<tr>
<th align="center"><strong>Table</strong></th>
<th align="center"><strong>Description</strong></th>
<th align="left"><strong>Use Example</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="center">filter</td>
<td align="center">기본 방화벽 역할 수행</td>
<td align="left"><strong>INPUT, OUTPUT, FORWARD</strong></td>
</tr>
<tr>
<td align="center">nat</td>
<td align="center">패킷 주소 변환 (NAT)</td>
<td align="left"><strong>PREROUING, POSTROUING, OUTPUT</strong></td>
</tr>
<tr>
<td align="center">mangle</td>
<td align="center">패킷 수정</td>
<td align="left"><strong>PREROUING, INPUT, FORWARD, OUTPUT, POSTROUTING</strong></td>
</tr>
<tr>
<td align="center">raw</td>
<td align="center">키넥션 추적을 우회할 수 있도록 설정</td>
<td align="left"><strong>PREROUING,  OUTPUT</strong></td>
</tr>
<tr>
<td align="center">security</td>
<td align="center">SELinux 정책 사용</td>
<td align="left"><strong>INPUT, OUTPUT, FORWARD</strong></td>
</tr>
</tbody></table>
<hr>
<h3 id="iptables-chain--hook">Iptables Chain &amp; Hook</h3>
<table>
<thead>
<tr>
<th align="center">Chain</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>PREROUING</strong></td>
<td align="left">패킷이 목적지에 도달하기 전에 변경</td>
</tr>
<tr>
<td align="center"><strong>INPUT</strong></td>
<td align="left">로컬 시스템으로 들어오는 패킷 필터링</td>
</tr>
<tr>
<td align="center"><strong>FORWARD</strong></td>
<td align="left">다른 인터페이스로 전달되는 패킷 필터링</td>
</tr>
<tr>
<td align="center"><strong>OUTPUT</strong></td>
<td align="left">로컬에서 생성한 패킷 필터링</td>
</tr>
<tr>
<td align="center"><strong>POSTROUTING</strong></td>
<td align="left">패킷이 인터페이스를 통해 나가기 직전 변경</td>
</tr>
</tbody></table>
<hr>
<h3 id="chain-strcture">Chain Strcture</h3>
<p>iptables 구조도
<img src="https://velog.velcdn.com/images/on_cloud/post/943b5e59-e24a-4cef-af08-33a219935d27/image.png" alt=""></p>
<hr>
<h3 id="command">Command</h3>
<p><strong>Install Package</strong></p>
<pre><code class="language-shell">apt install -y iptables iptables-persistent
systemctl enable --now iptables</code></pre>
<p><strong>Basic Structure</strong></p>
<pre><code class="language-shell">iptables [Access_Option] INPUT -s [Soure] --sport [Source_Port] -d [Destination] --dport [Destination_Port] -j [Policy]</code></pre>
<p><strong>Iptables Default Policy Access Option</strong></p>
<table>
<thead>
<tr>
<th align="left">Option</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>-L (--list)</strong></td>
<td align="left">적용 되어 있는 정책 목록 출력</td>
</tr>
<tr>
<td align="left"><strong>-A (--append)</strong></td>
<td align="left">새로운 정책 추가(가장 낮은 순위로 추가됨)</td>
</tr>
<tr>
<td align="left"><strong>-I (--insert)</strong></td>
<td align="left">새로운 정책 삽입(가장 높은 순위로 삽입됨)</td>
</tr>
<tr>
<td align="left"><strong>-R (--replace)</strong></td>
<td align="left">새로운 정책 교체(기존의 정책을 교체)</td>
</tr>
<tr>
<td align="left"><strong>-D (--delete)</strong></td>
<td align="left">해당 정책을 삭제</td>
</tr>
<tr>
<td align="left"><strong>-N (--new-chain)</strong></td>
<td align="left">새로운 체인 생성(INPUT,OUTPUT,FORWARD 제외)</td>
</tr>
<tr>
<td align="left"><strong>-P (--policy)</strong></td>
<td align="left">기본 정책 변경</td>
</tr>
<tr>
<td align="left"><strong>-F (--flush)</strong></td>
<td align="left">체인에 설정된 규칙 초기화</td>
</tr>
<tr>
<td align="left"><strong>-Z</strong></td>
<td align="left">체인의 모든 규칙들의 패킷과 바이트 카운터를 0 으로 만든다.</td>
</tr>
<tr>
<td align="left"><strong>-X (--delete-chain)</strong></td>
<td align="left">비어있는 체인을 삭제</td>
</tr>
<tr>
<td align="left"><strong>-E</strong></td>
<td align="left">체인 이름 변경</td>
</tr>
</tbody></table>
<p><strong>Iptables Access Option</strong></p>
<table>
<thead>
<tr>
<th align="left">Option</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>-s (--source)</strong></td>
<td align="left">출발지 주소</td>
</tr>
<tr>
<td align="left"><strong>-d (--destination)</strong></td>
<td align="left">목적지 주소</td>
</tr>
<tr>
<td align="left"><strong>--sport</strong></td>
<td align="left">출발지 포트</td>
</tr>
<tr>
<td align="left"><strong>--dport</strong></td>
<td align="left">목적지 포트</td>
</tr>
<tr>
<td align="left"><strong>-p (--protocol)</strong></td>
<td align="left">프로토콜 (tcp, udp...)</td>
</tr>
<tr>
<td align="left"><strong>-i (--in-interface)</strong></td>
<td align="left">패킷이 들어오는 네트워크 인터페이스 (inbound interface)</td>
</tr>
<tr>
<td align="left"><strong>-o (--out-interface)</strong></td>
<td align="left">패킷이 나가는 네트워크 인터페이스 (outbound interface)</td>
</tr>
<tr>
<td align="left"><strong>-f (--fragment)</strong></td>
<td align="left">나뉜 패킷</td>
</tr>
<tr>
<td align="left"><strong>-j (--jump)</strong></td>
<td align="left">패킷 상태 제어</td>
</tr>
</tbody></table>
<p><strong>Target</strong></p>
<table>
<thead>
<tr>
<th align="left">Option</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>ACCEPT</strong></td>
<td align="left">패킷 허용</td>
</tr>
<tr>
<td align="left"><strong>DROP</strong></td>
<td align="left">패킷 버림</td>
</tr>
<tr>
<td align="left"><strong>REJECT</strong></td>
<td align="left">패킷을 버리고 이와 동시에 적절한 응답 패킷을 전송</td>
</tr>
<tr>
<td align="left"><strong>LOG</strong></td>
<td align="left">패킷을 syslog에 기록</td>
</tr>
<tr>
<td align="left"><strong>RETURN</strong></td>
<td align="left">호출 체인 내에서 패킷 처리를 지속함</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] DHCP]]></title>
            <link>https://velog.io/@on_cloud/Linux-DHCP</link>
            <guid>https://velog.io/@on_cloud/Linux-DHCP</guid>
            <pubDate>Mon, 24 Mar 2025 02:01:43 GMT</pubDate>
            <description><![CDATA[<h3 id="what-is-dhcp">What is DHCP?</h3>
<hr>
<h3 id="setting-dhcp">Setting DHCP</h3>
<p><strong>DHCP</strong>
Server</p>
<pre><code class="language-shell">apt install -u isc-dhcp-server</code></pre>
<pre><code class="language-shell">vim /etc/dhcp/dhcpd.conf

option domain-name &quot;&lt;Domain Name&gt;&quot;;
option domain-name-servers &lt;Name Server IP Address&gt;;

subnet 192.168.0.0 netmask 255.255.255.0 {
    range 192.168.0.2 192.168.0.254;
}</code></pre>
<pre><code class="language-shell">vim /etc/default/isc-dhcp-server
INTERFACESv4=&quot;ens33&quot;
INTERFACESv6=&quot;ens33&quot;</code></pre>
<pre><code class="language-shelll">ifconfig ens33 192.168.0.1</code></pre>
<pre><code class="language-shell">systemctl restart isc-dhcp-server</code></pre>
<pre><code class="language-shell">dhcp-lrease-list</code></pre>
<p>Client</p>
<pre><code class="language-shell">vim /etc/netplan/00-installer-config.yaml</code></pre>
<pre><code class="language-yaml">network:
  ethernets:
    ens33:
      dhcp4: true
  version: 2</code></pre>
<pre><code class="language-shell">netplan apply</code></pre>
<br>

<p><strong>DHCP Relay</strong>
Server</p>
<pre><code class="language-shell">apt install -y isc-dhcp-server
apt install -y isc-dhcp-relay</code></pre>
<pre><code class="language-shell">vim /etc/dhcp/dhcpd.conf

option domain-name-servers &lt;Name Server IP Address&gt;;


subnet 192.168.0.0 netmask 255.255.255.0 {
    range 192.168.0.2 192.168.0.254;
    option routers 192.168.0.1;
    interface ens33;
}</code></pre>
<pre><code class="language-shell">vim /etc/default/isc-dhcp-server
INTERFACESv4=&quot;eth1&quot;</code></pre>
<pre><code class="language-shell">systemctl restart isc-dhcp-server</code></pre>
<pre><code class="language-shell">vim /etc/default/isc-dhcp-relay
SERVICES=&quot;&lt;Network Interface IP Address&gt;&quot;
INTERFACESv4=&quot;ens33&quot;</code></pre>
<pre><code class="language-shell">systemctl restart isc-dhcp-relay</code></pre>
<p>Router</p>
<pre><code class="language-shell">vim /etc/default/isc-dhcp-relay
INTERFACES=&quot;eth1 eth2&quot;</code></pre>
<pre><code class="language-shell">systemctl restart isc-dhcp-relay</code></pre>
<hr>
<h3 id="trouble-shooting">Trouble Shooting</h3>
<blockquote>
<p>아래는 App Armor Error이다. <br> Can&#39;t open /var/lib/dhcp/dhcpd.leases for append</p>
</blockquote>
<pre><code class="language-shell">vim /etc/default/grub
GRUB_CMDLINE_LINUX=&quot;apparmor=0&quot;</code></pre>
<pre><code class="language-shell">update-grub</code></pre>
<pre><code class="language-shell">reboot</code></pre>
<pre><code class="language-shell">systemctl stop apparmor
sytsemctl disable apparmor</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] DNS]]></title>
            <link>https://velog.io/@on_cloud/Linux-DNS</link>
            <guid>https://velog.io/@on_cloud/Linux-DNS</guid>
            <pubDate>Mon, 24 Mar 2025 01:50:54 GMT</pubDate>
            <description><![CDATA[<h3 id="what-is-dns">What is DNS?</h3>
<p><code>Domain Name System</code>의 약자로 호스트의 도메인을 호스트의 네트워크 주소로 변환해주는 것</p>
<hr>
<h3 id="dns-구성-요소">DNS 구성 요소</h3>
<ol>
<li>Domain Name System</li>
<li>Name Server</li>
<li>Resolver</li>
</ol>
<hr>
<h3 id="dns-record">DNS Record</h3>
<table>
<thead>
<tr>
<th align="center">Type</th>
<th align="center">Full Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">A</td>
<td align="center">Host Record (IPv4)</td>
<td align="left">FQDN과 32 bit의 IPv4로 연결</td>
</tr>
<tr>
<td align="center">AAAA</td>
<td align="center">Host Record (IPv6)</td>
<td align="left">FQDN과 128 bit의 IPv6로 연결</td>
</tr>
<tr>
<td align="center">CNAME</td>
<td align="center">Alias Record (별칭)</td>
<td align="left">실제 도메인 이름과 연결되는 가상 도메인 이름을 정의</td>
</tr>
<tr>
<td align="center">SOA</td>
<td align="center">Start Of Authority</td>
<td align="left">권한 시작을 지정하며, 권한이 있는 서버를 가리킴</td>
</tr>
<tr>
<td align="center">NS</td>
<td align="center">Name Server</td>
<td align="left">도메인 서버 목록을 지정</td>
</tr>
<tr>
<td align="center">MX</td>
<td align="center">Mail Exchange Record</td>
<td align="left">주어진 사서함에 도달할 수 있는 라우팅 정보를 제공</td>
</tr>
<tr>
<td align="center">SRV</td>
<td align="center">Service Resources</td>
<td align="left">비슷한 TCP/IP 서비스를 제공하는 다수의 서버 위치 정보를 제공</td>
</tr>
</tbody></table>
<hr>
<h3 id="setting-dns">Setting DNS</h3>
<p><strong>Forward</strong></p>
<pre><code class="language-shell">apt install -y bind9 bind9-utils bind9-dnsutils</code></pre>
<pre><code class="language-shell">vim /etc/resolv.conf
nameserver 192.168.0.1</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf
zone  &quot;naver.com&quot;{
    type master;
    file &quot;naver.zone&quot;;
};</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf.options
dnssec-validation no;</code></pre>
<pre><code class="language-shell">cp /etc/bind/db.local /var/cache/bind/naver.zone</code></pre>
<pre><code class="language-shell">vim /var/cache/bind/naver.zone
:%s/localhost/ns.naver.com/g
       IN    A    192.168.0.1
ns    IN    A    192.168.0.1
www    IN    A    192.168.0.2</code></pre>
<pre><code class="language-shell">systemctl restart bind9</code></pre>
<br>

<p><strong>Lookup</strong></p>
<pre><code class="language-shell">apt install -y bind9 bind9-utils bind9-dnsutils</code></pre>
<pre><code class="language-shell">vim /etc/resolv.conf
nameserver 192.168.0.1</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf
zone  &quot;0.168.192.in-addr.arpa&quot;{
    type master;
    file &quot;naver.re.zone&quot;;
};</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf.options
dnssec-validation no;</code></pre>
<pre><code class="language-shell">cp /etc/bind/db.local /var/cache/bind/naver.zone</code></pre>
<pre><code class="language-shell">vim /var/cache/bind/naver.zone
:%s/localhost/ns.naver.com/g
       IN    A    192.168.0.1
ns    IN    A    192.168.0.1
www    IN    A    192.168.0.2</code></pre>
<pre><code class="language-shell">cp /etc/bind/db.local /var/cache/bind/naver.re.zone</code></pre>
<pre><code class="language-shell">vim /var/cache/bind/naver.re.zone
:%s/localhost/ns.naver.com/g
1   IN    PTR ns.naver.com.</code></pre>
<pre><code class="language-shell">systemctl restart bind9</code></pre>
<br>

<p><strong>Master &amp; Slave</strong>
Master</p>
<pre><code class="language-shell">apt install -y bind9 bind9-utils bind9-dnsutils</code></pre>
<pre><code class="language-shell">vim /etc/resolv.conf
nameserver 192.168.0.1
search example.com
domain example.com</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf
zone  &quot;example.com&quot; {
    type master;
    file &quot;example.zone&quot;;
    allow-update { any; };
    allow-transfer { 192.168.0.2; };
};

zone  &quot;0.168.192.in-addr.arpa&quot; {
    type master;
    file &quot;example.re.zone&quot;;
    allow-update { any; };
    allow-transfer { 192.168.0.2; };
};</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf.options
dnssec-validation no;</code></pre>
<pre><code class="language-shell">cp /etc/bind/db.local /var/cache/bind/example.zone</code></pre>
<pre><code class="language-shell">vim /var/cache/bind/example.zone
:%s/localhost/Master.example.com/g
Master IN    A    192.168.0.1
ns       IN    A    192.168.0.1
www       IN    A    192.168.0.2</code></pre>
<pre><code class="language-shell">cp /etc/bind/db.local /var/cache/bind/naver.re.zone</code></pre>
<pre><code class="language-shell">vim /var/cache/bind/example.re.zone
:%s/localhost/ns.naver.com/g
1  IN    PTR    ns.example.com.</code></pre>
<pre><code class="language-shell">systemctl restart bind9</code></pre>
<p>Slave</p>
<pre><code class="language-shell">apt install -y bind9 bind9-utils bind9-dnsutils</code></pre>
<pre><code class="language-shell">vim /etc/resolv.conf
nameserver 192.168.0.2
search example.com
domain example.com</code></pre>
<pre><code class="language-shell">vim /etc/bind/named.conf
zone  &quot;example.com&quot; {
    type slave;
    masters { 192.168.0.1; };
    file &quot;example.zone&quot;;
};

zone  &quot;0.168.192.in-addr.arpa&quot; {
    type slave;
    masters { 192.168.0.1; };
    file &quot;example.re.zone&quot;;
};</code></pre>
<pre><code class="language-shell">systemctl restart bind9</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] Netplan]]></title>
            <link>https://velog.io/@on_cloud/Linux-Netplan</link>
            <guid>https://velog.io/@on_cloud/Linux-Netplan</guid>
            <pubDate>Mon, 24 Mar 2025 01:27:58 GMT</pubDate>
            <description><![CDATA[<h3 id="what-is-netplan">What is Netplan?</h3>
<p>Ubuntu 17.10 (Artful Aardvark)부터 기본 네트워크 설정 도구로 도입되었습니다.
이전까지 Ubuntu는 ifupdown과 <code>/etc/network/interfaces</code> 파일을 사용했지만, 관리가 어려워지고 Cloud 환경에서 자동화된 설정이 필요해지면서 Netplan으로 전환되었습니다.</p>
<hr>
<h3 id="netplan의-사용해야-하는-이유">Netplan의 사용해야 하는 이유</h3>
<ul>
<li>YAML 기반 설정</li>
<li>자동화 및 클라우드 친화적 (서버 및 컨테이너 환경에 적합)</li>
<li>유연한 렌더러 지원 (systemd-networkd (Server) / Netowkrn Manager (Desktop)</li>
<li>빠른 적용 및 테스트 기능</li>
</ul>
<hr>
<h3 id="netplan-기본-동작-방식">Netplan 기본 동작 방식</h3>
<ol>
<li><code>/etc/netplan/*.yaml</code> | 설정 파일 작성</li>
<li><code>netplan generate</code> | 백엔드용 설정 파일 생성</li>
<li><code>netplan apply</code> | 네트워크 인터페이스 재시작 및 적용</li>
</ol>
<hr>
<h3 id="netplan-structure">Netplan Structure</h3>
<p><strong>DHCP</strong></p>
<pre><code class="language-yaml">network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: true</code></pre>
<br>

<p><strong>Static IP</strong></p>
<pre><code class="language-yaml">network:
  ethernets:
    ens33:
      addresses: 
        - &lt;IP Address&gt;/&lt;Subnet Mask&gt;
      gateway4: &lt;Gateway IP Addaress&gt;
      nameservers:
        addresses:
            - &lt;DNS Server IP Address&gt;
  version: 2</code></pre>
<br>

<p><strong>VLAN</strong></p>
<pre><code class="language-yaml">network:
  ethernets:
    ens33:
      dhcp4: no
  vlans:
    &lt;VLAN Name&gt;:
      id: &lt;VLAN Number&gt;
      link: &lt;Netowkr Interface&gt;
      addresses:
        - &lt;IP Address&gt;/&lt;Subnet Mask&gt;
      gateway4: &lt;Gateway IP Addaress&gt;
  version: 2</code></pre>
<hr>
<h3 id="routing">Routing</h3>
<p><strong>Default Routing</strong></p>
<pre><code class="language-yaml">network:
  renderer: networkd
  ethernets:
    ens33:
      addresses:
        - &lt;IP Address&gt;/&lt;Subnet Mask&gt;
      routes:
        - to: 0.0.0.0/0
          via: &lt;IP Address&gt;
  version: 2</code></pre>
<blockquote>
<p><code>via</code>: 적용할 IP 주소</p>
</blockquote>
<br>

<p><strong>Static Routing</strong></p>
<pre><code class="language-yaml">network:
  renderer: networkd
  ethernets:
    ens33:
      addresses:
        - &lt;IP Address&gt;/&lt;Subnet Mask&gt;
      routes:
        - to: &lt;IP Address&gt;/&lt;Subnet Mask&gt;
          via: &lt;IP Address&gt;
  version: 2</code></pre>
<blockquote>
<p><code>to</code>: 적용할 네트워크 대역 <br> <code>via</code>: 적용할 IP 주소</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] Lambda Cold  Start]]></title>
            <link>https://velog.io/@on_cloud/AWS-Lambda-Cold-Start</link>
            <guid>https://velog.io/@on_cloud/AWS-Lambda-Cold-Start</guid>
            <pubDate>Thu, 10 Oct 2024 22:25:20 GMT</pubDate>
            <description><![CDATA[<h2 id="overview">Overview</h2>
<p>AWS Lambda는 다양한 분야에서 사용하는 코드형 기반 서버리스 서비스입니다. 하지만 Lambda는 <strong>Cold Start</strong>라는 초기 지연 시간이 발생할 수 있는데, Cold Start는 지연시간을 발생할 수 있습니다.</p>
<h2 id="what-is-lambda-cold-start">What is Lambda Cold Start?</h2>
<p>AWS Lambda를 실행하면 바로 함수가 실행되지 않습니다.</p>
<p>Lambda 실행 시 Lambda에 올려둔 코드를 이미지 형태로 다운 받아 실행하며, 이후 컨테이너가 유지됩니다.</p>
<p>하지만 Lambda는 컨테이너 생성 후 5분동안 사용하지 않으면 사용하던 컨테이너가 중지되며, 종료됩니다.</p>
<p>이에 람다는 경우에 따라 초기 응답이 늦어지는 것을 Cold Start라고 합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/587871ec-9f52-426a-9e83-1e2a6ef07d46/image.png" alt=""></p>
<blockquote>
<p>언어 및 코드에 따라  Cold Start 시간이 다르게 나타납니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/4bcf9574-1ffa-4f09-92e6-5e70bcf9c96b/image.png" alt=""></p>
<blockquote>
<p>위 사진처럼 Python, Node.js 같은 경우 상당히 빠르지만, C#, JAVA는 느립니다.</p>
</blockquote>
<hr>
<h2 id="how-lambda-works">How Lambda Works?</h2>
<h3 id="1-lambda-event-처리-방법-work-flow">1. Lambda Event 처리 방법 (Work Flow)</h3>
<p>외부로부터 어떤 이벤트(요청)가 들어왔을  때 다음과 같은 순서로 이벤트를 처리합니다.</p>
<p><strong>1) 실행환경 (컨테이너) 준비합니다.</strong></p>
<ul>
<li>컨테이너에 코드를 다운 받습니다.</li>
<li>Lambda에 설정된 Memory, Runtime, Configuration 기반으로 환경 설정합니다.</li>
</ul>
<p><strong>2) 초기화 코드 (Init Code)를 실행합니다.</strong></p>
<p><strong>3) Lambda 함수 코드 (Event Handler)를 실행하여 이벤트를 처리합니다.</strong>
<img src="https://velog.velcdn.com/images/on_cloud/post/49e8bccf-68e8-469e-ac72-bf7006a3866d/image.png" alt=""></p>
<blockquote>
<p>1번부분에서는 비용이 발생하지 않지만 람다 컨테이너가 실행될 때까지 해당부분에서도 Latency가 발생합니다.
2, 3번은 Lambda가 실제로 실행되는 시간이므로 비용이 발생합니다.
다만, Initialization Code 부분의 경우, 첫 번째 이벤트에 대해서만 실행되는 코드이며, 두번째 호출부터는 실행되지 않습니다.
따라서 2번도 Cold Start에 포함됩니다.</p>
</blockquote>
<br>

<h3 id="2-lambda-life-cycle">2. Lambda Life Cycle</h3>
<p>Lambda는 총 3단계의 Life Cycle을 가집니다.</p>
<p><strong>1) Init 단계</strong>
위에서 언급한 Cold Start가 해당 부분에 속합니다. 새로운 실행환경(컨테이너)를 요청하여 준비된 실행환경(컨테이너)가 람다 함수 코드를 다운로드 받고, 사용자가 람다 함수를 생성했을 때 지정해둔 Runtime, Memory, Configuration 등을 세팅하는 과정입니다. 또한, 람다 함수 코드에서 init code부분(라이브러리 및 global 변수 세팅)을 실행합니다.</p>
<br>

<p><strong>2) Invoke 단계</strong>
실제 람다 함수 핸들러가 호출되어, 코드가 실행되는 단계입니다. 하나의 Request(Event)에 대한 처리가 완료되면, 람다는 다음 Request(Event)를 처리할 준비를 합니다.</p>
<br>

<p><strong>3) Shutdown 단계</strong>
람다 함수 실행을 위해 세팅되어 있던 실행환경(컨테이너)가 죽는 단계입니다. 람다 함수는 일정 기간동안 호출을 받지 않으면 런타임을 종료하고 실행환경(컨테이너)을 제거합니다.</p>
<br>

<h2 id="3-lambda-통신-과정">3. Lambda 통신 과정</h2>
<p><strong>1) Sequential</strong>
외부에서 어떠한 요청이 들어오면, Lambda는 새로운 실행환경(컨테이너)을 띄우고, 해당 실행환경에서 람다 함수의 코드를 실행할 수 있도록 이벤트를 발생시킵니다. 이 때, 하나의 컨테이너는 하나의 이벤트만 처리할 수 있으며, 해당 컨테이너가 이벤트를 처리하는 동안 freeze 상태가 됩니다.</p>
<p>이벤트 처리가 완료되면, 해당 컨테이너는 idle 상태가 됩니다.
이때, 아래 2개의 상태가 오래 지속되면 해당 컨테이너는 Shutdown 됩니다.</p>
<ul>
<li>컨테이너가 idle 상태가 오래 지속되면 Shutdown됩니다.</li>
<li>요청받은 Request(이벤트)가 오래 지속되면 폐기 후 Shutdown됩니다.</li>
</ul>
<br>

<p><strong>2) Concurrent</strong>
위에서 언급한 바와 같이, 하나의 실행환경(컨테이너)은 한 번에 하나의 Request(Event)만 처리합니다. 따라서 Request(Event)를 처리하는 동안 해당 실행환경(컨테이너)은 freeze 상태가 됩니다.</p>
<p>이 때, 만약 실행환경(컨테이너)들이 전부 freeze상태임에도 불구하고 새로운 Request(Event)가 계속해서 들어오는 경우, 람다는 새로운 실행환경(컨테이너)을 가져오도록 트리거합니다. (즉, 새로운 인스턴스를 생성합니다.)
concurrent하게 처리할 Request(Event)들에 대해 실행환경(컨테이너)는 계속해서 생성됩니다.</p>
<hr>
<h2 id="lambda-실행에-따른-문제점">Lambda 실행에 따른 문제점</h2>
<p><strong>1. 새로운 실행환경(컨테이너)을 트리거하고 준비할 때마다 Cold Start를 겪어야 합니다.</strong>
Lambda 함수마다 Concurrent 인스턴스 용량을 할당할 수 있도록 Reserved Concurrency와 Provisioned Concurrency를 설정할 수 있도록 되어있습니다.</p>
<p><strong>2. Concurrent하게 띄울 수 있는 실행환경(컨테이너) 수에 Limit이 존재합니다.</strong>
Region 별 Concurrent Instance 할당량은 1000개에서 시작합니다.
AWS Service Quotas을 통해 해당 Limit을 증가시킬 수 있습니다.)</p>
<hr>
<h2 id="cold-start-해결-방안">Cold Start 해결 방안</h2>
<ol>
<li>적절한 런타임을 선택합니다.
(언어마다 처리속도가 다르므로, 가능한 빠른 환경을 실행하는 것이 좋습니다. / Python, Node.js 등)</li>
<li>자체적인 Warm Starat로 구성하기</li>
<li>Lambda Memory 사양을 늘립니다.</li>
<li>Provisioned Concurrency를 사용합니다.</li>
<li>지속적으로 트래픽이 들어오게 설정합니다.
(5~15분이 끝나기 전인 4분 59초 ~ 14분59초에 컨테이너가 반복적으로 실행되게 구성합니다.)</li>
<li>코드를 최적화합니다.</li>
<li>초기화 코드를 최소화합니다.
(초기화 시 필요한 작업만 수행하도록 코드 구조를 최적화합니다. / 불필요한 글로벌 변수나 라이브러리 로딩을 피합니다.)</li>
<li>테스트 및 모니터링합니다. (Lambda 함수의 성능을 지속적으로 모니터링하여, Cold Start가 발생 패턴을 파악하고 이를 기반으로 최적화합니다. AWS CloudWatch를 통해 지연 시간 및 Cold Start 빈도를 확인할 수 있습니다.</li>
<li>여러 리전을 사용합니다.
(트래픽이 높은 Application의 경우, 여러 AWS Region에서 Lambda 함수를 운영하여 부하분산시키고 Cold Start 빈도를 줄입니다.)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataBase] Window 환경에서 MySQL 설치하기]]></title>
            <link>https://velog.io/@on_cloud/DataBase-Window-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-MySQL-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@on_cloud/DataBase-Window-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-MySQL-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 03 Oct 2024 18:45:15 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>MySQL Installer 페이지로 이동합니다.
<a href="https://dev.mysql.com/downloads/windows/installer/8.0.html">https://dev.mysql.com/downloads/windows/installer/8.0.html</a></p>
</li>
<li><p>Windows (x86, 32-bit), MSI Installer에서 Download를 눌러줍니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/8a200208-4529-4996-aca9-2ad4aeb49e6f/image.png" alt=""></p>
</li>
<li><p><a href="https://cdn.mysql.com//Downloads/MySQLInstaller/mysql-installer-community-8.0.36.0.msi">No thanks. just start my download</a>.를 눌러줍니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/b0dad036-560e-4104-bd04-292ebc5d20a2/image.png" alt=""></p>
</li>
<li><p>mysql-installer-community-8.0.36.0.msi를 실행합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/eab016bc-071b-4e51-ab9a-a0ee86e370d9/image.png" alt=""></p>
</li>
<li><p>Chosing a Setup Type에서 Custom을 선택 후 Next를 눌러줍니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/8aa96ee7-f6b5-4e44-9ef0-39c61beaafcc/image.png" alt=""></p>
<blockquote>
<p>Custom은 해당 사용자가 필요한 패키지만 설치합니다.</p>
</blockquote>
</li>
<li><p>[Available Products:]에서 [MySQL Servers] – [MySQL Server] – [MySQL Server 8.0] – [MySQL Server 8.0.36 – X64]를 선택하고 ▶버튼을 클릭합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/c9e1496b-2fe2-4268-bdae-acbbc7b76fc7/image.png" alt=""></p>
</li>
<li><p>위 방법과 똑같이 아래 패키지들을 선택 후 Next를 눌러줍니다.</p>
</li>
</ol>
<ul>
<li>MySQL Server 8.0.36 - x64</li>
<li>MySQL Workbench 8.0.36 - x64</li>
<li>MySQL Shell 8.0.36 - x64</li>
</ul>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/75e53692-324a-44be-b7c5-e0d791ea1e32/image.png" alt=""></p>
<ol start="8">
<li><p>Execute를 선택합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/9cb86e43-24d7-4f3c-8c71-9356ef3c6dee/image.png" alt=""></p>
</li>
<li><p>그 후 Next를 눌러 작업을 완료합니다.</p>
</li>
<li><p>Port 및 Protocol Port를 설정합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/a421df4b-34f7-46af-b3b4-5f43bb7715cc/image.png" alt=""></p>
<blockquote>
<p>MySQL Default Port는 3306입니다.</p>
</blockquote>
</li>
<li><p>Next를 선택합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/71975924-d1b2-40a2-830b-2a4cb9d06a42/image.png" alt=""></p>
</li>
<li><p>Password를 설정 후 Next 및 Execute를 선택합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/a8d2d851-e325-4e84-a3b6-c8f2f29f1afe/image.png" alt=""></p>
<blockquote>
<p>Default User는 Root입니다.</p>
</blockquote>
</li>
<li><p>모든 작업이 완료되면 Finish 및 Next를 선택합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/06179bc6-89b2-42e0-b070-b2bfd37119d8/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[VSCode] Visual Studio Code에 Spring Boot 실행하기]]></title>
            <link>https://velog.io/@on_cloud/VSCode-Visual-Studio-Code%EC%97%90-Spring-Boot-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@on_cloud/VSCode-Visual-Studio-Code%EC%97%90-Spring-Boot-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 03 Oct 2024 18:19:49 GMT</pubDate>
            <description><![CDATA[<ul>
<li>기본적으로 <a href="https://velog.io/@on_cloud/JAVA-window10-Open-JDK-%EC%84%A4%EC%B9%98">OpenJDK</a>가 설치되어 있어야 합니다.</li>
</ul>
<ol>
<li>VSCode 접근 후 아래 패키지를 다운로드합니다.</li>
</ol>
<ul>
<li>Spring Boot Extension Pack</li>
<li>thymeleaf
<img src="https://velog.velcdn.com/images/on_cloud/post/bb90cb07-2821-4454-bb05-d68a3a1cd80e/image.png" alt="">
<img src="https://velog.velcdn.com/images/on_cloud/post/18d17c87-51a0-45f8-b327-b063ed48b44d/image.png" alt=""></li>
</ul>
<p>해당 Installer는 Windows에만 해당하고,
나머지 OS에서는 VSCode, JDK, Extensions Pack을 각각 설치해야합니다.</p>
<br>

<ol start="2">
<li>[Ctrl] + [Shift] + [P] 혹은 상단 메뉴 [View] &gt; [Command Palette]를 실행하여, Spring Initializr를 입력합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/f437f852-5aee-4a44-bc32-c2c3ed8aa10b/image.png" alt=""></li>
</ol>
<p>Spring Boot Version : 3.34
Language : Java
Group ID : 원하는 그룹 ID
Artifact ID - 원하는 Artifact ID
Package Type : jar
Java Version : 사용자가 설치한 Java 버전</p>
<br>

<ol start="3">
<li>아래 Package를 찾아 설정합니다.</li>
</ol>
<ul>
<li>Spring Boot DevTools</li>
<li>Lombok</li>
<li>Spring Boot Web</li>
<li>Thymmeleaf
<img src="https://velog.velcdn.com/images/on_cloud/post/42957c11-7ccb-43aa-af90-0bf62eef6f1b/image.png" alt=""></li>
</ul>
<br>

<ol start="4">
<li>모든 작업이 완료되면 아래 사진과 같이 생성된 프로젝트를 확인하실 수 있습니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/28754574-29ab-431b-8a43-fe43147232d6/image.png" alt=""></li>
</ol>
<br>

<hr>
<p><strong>Spring Boot 실행</strong></p>
<ol>
<li><p>아래 명령어를 입력하여 생성된 Project를 Gradlew를 사용하여 Complie합니다.</p>
<pre><code>./gradlew</code></pre><blockquote>
<p>해당 명령어는 상대경로 기준으로 실행하며, 실행 시 Java를 Complie하여 Jar를 생성합니다.</p>
</blockquote>
</li>
<li><p>아래 명령어 입력 후 해당 Project를 실행합니다.</p>
<pre><code>./gradlew bootRun</code></pre><blockquote>
<p>해당 명령어는 상대경로 기준으로 실행됩니다.</p>
</blockquote>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[VSCode] Visual Studio Code에 Java 실행하기]]></title>
            <link>https://velog.io/@on_cloud/VSCode-Visual-Studio-Code%EC%97%90-Java-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@on_cloud/VSCode-Visual-Studio-Code%EC%97%90-Java-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 16 Feb 2024 11:02:46 GMT</pubDate>
            <description><![CDATA[<ul>
<li>기본적으로 <a href="https://velog.io/@on_cloud/JAVA-window10-Open-JDK-%EC%84%A4%EC%B9%98">OpenJDK</a>가 설치되어 있어야 합니다.</li>
</ul>
<ol>
<li>VSCode 접근 후 아래 패키지를 다운로드합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/c3c88b7f-524d-4f66-8cd7-c7f98b017ce8/image.png" alt=""></li>
</ol>
<p>해당 Installer는 Windows에만 해당하고,
나머지 OS에서는 VSCode, JDK, Extensions Pack을 각각 설치해야합니다.</p>
<p>VSCode 에서 제공하는 Installer <a href="https://aka.ms/vscode-java-installer-win">다운로드 링크</a></p>
<ol start="2">
<li>Java 파일을 실행합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/63b0a978-20e2-4515-bae2-92c80a96315f/image.png" alt=""></li>
</ol>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/65aa4efd-efe2-419d-aedf-9dd99021e89f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] window10 / Open JDK 설치]]></title>
            <link>https://velog.io/@on_cloud/JAVA-window10-Open-JDK-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@on_cloud/JAVA-window10-Open-JDK-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 16 Feb 2024 10:33:18 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>Open JDK 페이지로 이동합니다.
<a href="http://jdk.java.net/java-se-ri/8-MR3">http://jdk.java.net/java-se-ri/8-MR3</a></p>
</li>
<li><p><a href="https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-windows-i586-14_jan_2020.zip">Windows 10 i586 Java Development Kit</a> <a href="https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-windows-i586-14_jan_2020.zip.md5">(md5)</a> 92 MB를 클릭하여 다운로드합니다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/9729fa0f-6c8c-4156-99d2-1c6ba7805cf4/image.png" alt=""></p>
<ol start="3">
<li><p>다운받은 파일을 압축을 해제 후 폴더 이름을 java로 변경합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/5603b4bf-2f6a-469a-9ad9-e92f89d73cdc/image.png" alt=""></p>
</li>
<li><p>java 폴더를 아래 경로로 이동시켜줍니다.</p>
<pre><code>C:\</code></pre><p><img src="https://velog.velcdn.com/images/on_cloud/post/1667e40f-9c76-4f1c-a973-a6597b6809f4/image.png" alt=""></p>
</li>
<li><p>환경변수를 등록해줍니다.</p>
<pre><code>제어판\모든 제어판 항목\시스템</code></pre><p><img src="https://velog.velcdn.com/images/on_cloud/post/3f56efe1-d34e-4e83-a3da-86e22358ecb2/image.png" alt=""></p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/94f94f16-484c-4fea-aad9-7cf72219a1e7/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/c4b42ec4-40ba-4593-a635-7fe2d58fb486/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/88ebb05f-2deb-475e-8e98-60cdd56e5dcf/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/13db9884-ae86-4890-a343-9e9a469a547b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/9b83bd7b-3009-4c1d-9c6a-e53b6d9eb5b6/image.png" alt=""></p>
<ol start="6">
<li>CMD, PowerShell, GitBash등에 접속하여 실행이 되는지 테스트 합니다.</li>
</ol>
<ul>
<li>java</li>
<li>javac</li>
<li>java -version</li>
<li>javac -version
<img src="https://velog.velcdn.com/images/on_cloud/post/23002023-9e6d-47cb-8b96-c1ade86ca601/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] Nginx로 Proxy Server 구성하기]]></title>
            <link>https://velog.io/@on_cloud/Linux-Nginx-Proxy</link>
            <guid>https://velog.io/@on_cloud/Linux-Nginx-Proxy</guid>
            <pubDate>Sun, 07 Jan 2024 04:19:33 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>Nginx Package를 설치합니다.</p>
<pre><code class="language-sh">yum install -y nginx</code></pre>
</li>
<li><p>Nginx를 시작합니다.</p>
<pre><code class="language-sh">systemctl enable --now nginx</code></pre>
</li>
<li><p>Nginx.conf파일을 수정합니다.</p>
<pre><code class="language-sh">vim /etc/nginx/nginx.conf
server {
     listen       80;
     listen       [::]:80;
     server_name  &lt;WAS CIDR&gt;;
     root         /usr/share/nginx/html;
     location / {
         proxy_pass http://&lt;WAS CIDR&gt;:&lt;Port&gt;/&lt;Path&gt;;
     }
     # Load configuration files for the default server block.
     include /etc/nginx/default.d/*.conf;

     error_page 404 /404.html;
     location = /404.html {
     }</code></pre>
</li>
<li><p>Nginx를 재시작합니다.</p>
<pre><code class="language-sh">systemctl restart nginx</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[GitHub] Personal Access Token 발급 방법]]></title>
            <link>https://velog.io/@on_cloud/GitHub-Access-Token</link>
            <guid>https://velog.io/@on_cloud/GitHub-Access-Token</guid>
            <pubDate>Wed, 03 Jan 2024 10:15:04 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>오른쪽 상단에 프로필 아이콘을 클릭한 후 하단에 [Settings] 메뉴를 클릭합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/4be37b9e-90c6-4ebf-a028-6ab01f94b023/image.png" alt=""></p>
</li>
<li><p>Settings 좌측 하단 메뉴에 [Developer settings] 메뉴를 클릭합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/99e8cfec-bfb1-41ed-924d-4c110d02aa4f/image.png" alt=""></p>
</li>
<li><p>Devloper settings 화면에서 좌측 메뉴에 [Personal access tokens &gt; Tokens (classic)]을 클릭합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/76704057-65d8-4d6a-9641-49ff6440c6c8/image.png" alt=""></p>
</li>
</ol>
<ol start="4">
<li><p>오른쪽 상단에 [Generate new token] 선택박스를 클릭 후 [Generate new token (classic)]을 클릭합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/14aed796-9649-458c-ac76-afe4631dd852/image.png" alt=""></p>
</li>
<li><p>Note 및 Expiration을 입력합니다. (Note는 Token의 이름이며, Expiration은 만료일을 의미합니다.)
<img src="https://velog.velcdn.com/images/on_cloud/post/23a2db7d-5718-4de6-b526-a0913461677b/image.png" alt=""></p>
</li>
<li><p>생성된 Token을 기록 후 사용합니다.
<img src="https://velog.velcdn.com/images/on_cloud/post/c382605f-9af7-4868-8ae4-3b85a3ac84d9/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] CodeCommit 자격증명 오류(fatal : unable to access...403)]]></title>
            <link>https://velog.io/@on_cloud/AWS-CodeCommit-Error</link>
            <guid>https://velog.io/@on_cloud/AWS-CodeCommit-Error</guid>
            <pubDate>Sat, 07 Oct 2023 05:05:11 GMT</pubDate>
            <description><![CDATA[<h3 id="error">Error</h3>
<p>CodeCommit Repository를 Linux 및 Window 환경에 Pull 하려고 할 시 아래와 같은 에러가 발생합니다.</p>
<pre><code class="language-shell">fatal: unable to access &#39;https://git-codecommit.&lt;Region&gt;.amazonaws.com/v1/repos/&lt;Repository Name&gt;/&#39;: The requested URL returned error: 403</code></pre>
<p>해당 에러가 발생하는 이유는 CodeCommit에대한 Pull 자격증명 권한이 삭제되었거나 잘못되었을 때 발생하는 에러입니다.</p>
<h3 id="trouble-shooting">Trouble Shooting</h3>
<p>기존에 있던 Git Credential을 삭제한 후 다시 진행하면 정상적으로 실행됩니다.</p>
<pre><code>Window + R</code></pre><p><img src="https://velog.velcdn.com/images/on_cloud/post/aae5502f-9ef3-4439-8e96-defcd1bcee19/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/c798baaa-f932-446a-b014-37aed077378d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/a06013e7-3af2-434d-ae27-9a1a94748bc4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/9055b836-0af6-4a1e-af88-00eb396281d1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/on_cloud/post/7c3ebce8-6139-407d-9ea5-3157c7d7c4ce/image.png" alt=""></p>
<blockquote>
<p>해당 <strong>git://<a href="https://git-codecommit.ap-northeast-2.amazonaws.com">https://git-codecommit.ap-northeast-2.amazonaws.com</a></strong> 이라는 자격증명을 삭제한 후 다시 Git Credential을 진행할 시 위와 같이 해결됩니다.</p>
</blockquote>
<p>경로는 아래와 같습니다.</p>
<pre><code>제어판\사용자 계정\자격 증명 관리자</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] AWS Time Sync Service]]></title>
            <link>https://velog.io/@on_cloud/AWS-Time-Sync-Service</link>
            <guid>https://velog.io/@on_cloud/AWS-Time-Sync-Service</guid>
            <pubDate>Tue, 26 Sep 2023 01:36:28 GMT</pubDate>
            <description><![CDATA[<p><code>참고</code> <a href="https://aws.amazon.com/ko/blogs/korea/keeping-time-with-amazon-time-sync-service//">https://aws.amazon.com/ko/blogs/korea/keeping-time-with-amazon-time-sync-service//</a></p>
<h2 id="aws-time-sync-service">AWS Time Sync Service</h2>
<hr>
<p><strong>AWS Time Sync Service란?</strong>
AWS에서 제공하는 시간 동기화 서비스</p>
<p>169.254.139.123 IP 주소를 링크를 통해 서비스에 액세스 할 수 있습니다.
즉, 외부 인터넷 액세스를 구성할 필요 없이 프라이빗 서브넷 내에 안전하게 액세스할 수 있습니다.</p>
<h2 id="setting-aws-time-sync-service">Setting AWS Time Sync Service</h2>
<p><strong>Amazon Linux</strong></p>
<pre><code>sudo yum erase ntp*
sudo yum install -y chrony
sudo systemctl enable --now chronyd</code></pre><p><img src="https://velog.velcdn.com/images/on_cloud/post/03a43a48-7bd2-4f9e-9003-0f05aa376e2f/image.png" alt=""></p>
<p>또는 아래 줄을 추가하여 NTP Config를 수정하여 사용할 수 있습니다.</p>
<pre><code>server 169.254.169.123 prefer iburst</code></pre><br>

<p><strong>Window</strong></p>
<pre><code>net stop w32time
w32tm /config /syncfromflags:manual /manualpeerlist:&quot;169.254.169.123&quot;
w32tm /config /reliable:yess
net start w32time</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] AWS Domain Server IP]]></title>
            <link>https://velog.io/@on_cloud/AWS-Domain-Server-IP</link>
            <guid>https://velog.io/@on_cloud/AWS-Domain-Server-IP</guid>
            <pubDate>Tue, 26 Sep 2023 01:28:39 GMT</pubDate>
            <description><![CDATA[<p><code>참고</code> <a href="https://repost.aws/ko/knowledge-center/route-53-fix-dns-resolution-issues/">https://repost.aws/ko/knowledge-center/route-53-fix-dns-resolution-issues/</a></p>
<h2 id="aws-domain-server-ip">AWS Domain Server IP</h2>
<hr>
<p>AWS에서 DHCP Option 세트를 설정할 때 DNS Server의 IP를 지정하여야 합니다.
DNS Server IP는 <strong><span style="color: red">VPC Network 범위에 2를 더한 값</span></strong>입니다. </p>
<p>예시는 다음과 같습니다.
VPC CIDR 범위 : 10.0.0.0/16</p>
<pre><code>10.0.0.0 + 2 = 10.0.0.2</code></pre><p>따라서 VPC DNS Server IP는 10.0.0.2 주소를 가지게 됩니다.</p>
<br>

<h2 id="ec2-instance-dns">EC2 Instance DNS</h2>
<hr>
<p>EC2 Instance에서 /etc/resolve.conf에서 Domain Server IP와 같은 값인걸 확인할 수 있습니다.
(해당 EC2 Instance는 VPC CIDR 10.0.0.0/16 대역에 존재합니다.)
<img src="https://velog.velcdn.com/images/on_cloud/post/7231a31f-5921-483a-9e8b-5185826f2491/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] SSH Port 변경]]></title>
            <link>https://velog.io/@on_cloud/Linux-change-to-ssh-port</link>
            <guid>https://velog.io/@on_cloud/Linux-change-to-ssh-port</guid>
            <pubDate>Thu, 23 Mar 2023 01:39:04 GMT</pubDate>
            <description><![CDATA[<h2 id="change-to-ssh-port-in-command">change to ssh port in command</h2>
<hr>
<pre><code class="language-shell">sudo viㅡ /etc/ssh/sshd_config</code></pre>
<p>#Port 22 부분을 수정해줍니다. (주석도 지워줍니다.)</p>
<pre><code class="language-shell">Port &lt;port&gt;</code></pre>
<pre><code class="language-shell">systemctl restart sshd</code></pre>
<br>

<h2 id="shell-script">shell script</h2>
<hr>
<p><strong>Change to SSH Port in Shell Script - sed</strong></p>
<pre><code class="language-shell">sed -i &quot;s|#Port 22|Port &lt;port&gt;|g&quot; /etc/ssh/sshd_config
systemctl restart sshd</code></pre>
<p><strong>Change to SSH Port in Shell Script - echo</strong></p>
<pre><code class="language-shell">echo &quot;Port &lt;port&gt;&quot; &gt;&gt; /etc/ssh/sshd_config
systemctl restart sshd</code></pre>
]]></description>
        </item>
    </channel>
</rss>