<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>dh_0e</title>
    <link>https://dh-0e.tistory.com/</link>
    <description>개발 blog</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 21:47:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>dh_0e</managingEditor>
    <image>
      <title>dh_0e</title>
      <url>https://tistory1.daumcdn.net/tistory/7026362/attach/5ec33b5064cd450890c6a657173986d5</url>
      <link>https://dh-0e.tistory.com</link>
    </image>
    <item>
      <title>[Cloud] K8s 요청 처리 단계, Service Account</title>
      <link>https://dh-0e.tistory.com/273</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Kubernetes API Server의 요청 처리 3단계&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oApF7/dJMcadvjTQ1/wdV0vnHh6gwj2KxQherwD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oApF7/dJMcadvjTQ1/wdV0vnHh6gwj2KxQherwD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oApF7/dJMcadvjTQ1/wdV0vnHh6gwj2KxQherwD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoApF7%2FdJMcadvjTQ1%2FwdV0vnHh6gwj2KxQherwD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;815&quot; height=&quot;522&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Authentication(인증 - 사용자 식별):&lt;/b&gt; HTTP 요청을 검사하여 메시지를 보낸 주체가 누구인지 식별
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSO 시스템이나 인증서 등을 통해 확인하며, 미리 구성된 인프라를 활용함 (Authenticator App)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Authorization(권한 부여 - 권한 확인):&lt;/b&gt; 인증된 사용자가 요청한 리소스에 대해 해당 작업(R/W/D 등)을 수행할 권한이 있는지 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Admission Control(승인 제어 - 유효성 검사 및 수락):&lt;/b&gt; 리소스 생성/수정/삭제 요청 시, 컨텐츠(Spec)에 잘못된 부분은 없는지 확인하고, 필요시 수정함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 플러그인은 여러 개 설치할 수 있으며, 요청은 모든 플러그인을 거쳐야 함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;ex) &quot;Pod 만드는데 CPU 10,000개 할당해 줘&quot; 같은 무리한 요청이 들어오면 거절&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD60ZH/dJMcajoK08u/wVtB7u8GqKyzLLa4VuyBH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD60ZH/dJMcajoK08u/wVtB7u8GqKyzLLa4VuyBH0/img.png&quot; data-alt=&quot;Admission Control Plugins 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD60ZH/dJMcajoK08u/wVtB7u8GqKyzLLa4VuyBH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD60ZH%2FdJMcajoK08u%2FwVtB7u8GqKyzLLa4VuyBH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;178&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Admission Control Plugins 종류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Service Account&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Service account:&lt;/b&gt; Pod가 API 서버와 통신하기 위해 사용하는 계정으로 요청 처리 과정 전반에 걸쳐 사용됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 서버에 접속하는 클라이언트는 &lt;b&gt;실제 사람(Human)&lt;/b&gt;과 &lt;b&gt;내부 Pod&lt;/b&gt;로 나뉨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daAPTm/dJMcaciUmCh/Yqjlkh02NrkdNoZNo2ET00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daAPTm/dJMcaciUmCh/Yqjlkh02NrkdNoZNo2ET00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daAPTm/dJMcaciUmCh/Yqjlkh02NrkdNoZNo2ET00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaAPTm%2FdJMcaciUmCh%2FYqjlkh02NrkdNoZNo2ET00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;916&quot; height=&quot;446&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod가 만들어지면 특정 &lt;b&gt;Service Account와 연결&lt;/b&gt;되며, Pod는 이 계정의&lt;b&gt; 토큰&lt;/b&gt;을 사용해 API 서버와 통신함&lt;/li&gt;
&lt;li&gt;Pod 생성 시 계정을 따로 지정하지 않으면 해당 namespace의 &lt;b&gt;기본 Service Account가 자동으로 부여&lt;/b&gt;됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 사진에 나와있듯이, &lt;b&gt;다른 namespace의 Service Account에 연결할 수 없음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이를 통해 각 Pod가 어떤 리소스에 접근할 수 있는지 제어할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5ei6B/dJMcafNzjQp/FJxPUc13Tjo8u7KEtYFKx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5ei6B/dJMcafNzjQp/FJxPUc13Tjo8u7KEtYFKx0/img.png&quot; style=&quot;width: 58.6926%; margin-right: 10px;&quot; data-widthpercent=&quot;59.38&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;359&quot; data-origin-width=&quot;578&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5ei6B/dJMcafNzjQp/FJxPUc13Tjo8u7KEtYFKx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5ei6B%2FdJMcafNzjQp%2FFJxPUc13Tjo8u7KEtYFKx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvp5GK/dJMcaftcEj7/OvNVdEbfaJLhdT1DKw7kwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvp5GK/dJMcaftcEj7/OvNVdEbfaJLhdT1DKw7kwK/img.png&quot; style=&quot;width: 40.1446%;&quot; data-widthpercent=&quot;40.62&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;326&quot; data-origin-width=&quot;359&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvp5GK/dJMcaftcEj7/OvNVdEbfaJLhdT1DKw7kwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvp5GK%2FdJMcaftcEj7%2FOvNVdEbfaJLhdT1DKw7kwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Example of Service Account Using in Pod YAML&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Authorization &amp;amp; RBAC (역할 기반 접근 제어)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3MvUq/dJMcafzZVq0/qZeULNYgHiwpu9E2d6DPo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3MvUq/dJMcafzZVq0/qZeULNYgHiwpu9E2d6DPo1/img.png&quot; data-alt=&quot;A리소스만 접근 가능한 것을 Role로 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3MvUq/dJMcafzZVq0/qZeULNYgHiwpu9E2d6DPo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3MvUq%2FdJMcafzZVq0%2FqZeULNYgHiwpu9E2d6DPo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;871&quot; height=&quot;450&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A리소스만 접근 가능한 것을 Role로 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;K8s는 권한을 관리하기 위해 &lt;b&gt;RBAC 플러그인&lt;/b&gt;을 주로 사용하여 Service Account에 권한을 적용함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RBAC(Role Based Access Control):&lt;/b&gt; &lt;b&gt;HTTP 메서드&lt;/b&gt;에 따라 동사(get, list, create, update 등) 단위로 권한을 나누어 줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Role(역할):&lt;/b&gt; 어떤 리소스에 대해 어떤 작업을 할 수 있는지 정의한 권한의 집합
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 특정 리소스 A만 조회 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Role Binding(역할 바인딩): &lt;/b&gt;만들어둔 Role을 &lt;b&gt;외부 사용자(User)&lt;/b&gt;나 &lt;b&gt;Pod의 계정(Service Account)&lt;/b&gt;에 연결(적용)해주는 객체&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClusterRoleBinding:&lt;/b&gt; 특정 namespace를 넘어, &lt;b&gt;Cluster 전체 범위에 Role을 바인딩&lt;/b&gt;하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTJOrk/dJMcajoK1OS/gdTl57sR3xnfy03kmQtCQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTJOrk/dJMcajoK1OS/gdTl57sR3xnfy03kmQtCQ0/img.png&quot; data-alt=&quot;HTTP 메소드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTJOrk/dJMcajoK1OS/gdTl57sR3xnfy03kmQtCQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTJOrk%2FdJMcajoK1OS%2FgdTl57sR3xnfy03kmQtCQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;298&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HTTP 메소드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ex) Role YAML file&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CdNRa/dJMcaci1ZVv/J1TRUJbjMqVOg03SynMMGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CdNRa/dJMcaci1ZVv/J1TRUJbjMqVOg03SynMMGk/img.png&quot; data-alt=&quot;service라는 리소스를 조회(get, list)할 수 있는 권한을 가진 Role&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CdNRa/dJMcaci1ZVv/J1TRUJbjMqVOg03SynMMGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCdNRa%2FdJMcaci1ZVv%2FJ1TRUJbjMqVOg03SynMMGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;331&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;service라는 리소스를 조회(get, list)할 수 있는 권한을 가진 Role&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 &lt;b&gt;Role&lt;/b&gt;과 &lt;b&gt;Role Binding&lt;/b&gt;을 통해, 특정 namespace나 리소스에 대한 접근 권한을 제어하여 결과적으로 사용자의 kubectl 명령어 실행 권한을 부여하거나 해제할 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl 명령어도 결국 http 요청이기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위와 같이 Role을 만든 뒤, 아래와 같이 &lt;b&gt;'kind: RoleBinding' &lt;/b&gt;YAML 파일을 따로 작성해서 &lt;b&gt;service-reader&lt;/b&gt;와 연결해줘야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780843239862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Service Account 생성
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa      # 신분증 이름
  namespace: foo       # 아까 Role이 있던 namespace와 동일하게 맞춤
  
# RoleBinding 생성
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-services-binding  # 이 바인딩 규칙의 이름
  namespace: foo
subjects:                     # 1. 누구에게 권한을 줄 것인가? (대상)
- kind: ServiceAccount        # 대상의 종류는 ServiceAccount!
  name: my-app-sa             # 방금 위에서 만든 그 신분증!
  namespace: foo
roleRef:                      # 2. 어떤 권한을 줄 것인가? (참조할 Role)
  kind: Role                  # 연결할 대상은 Role!
  name: service-reader        # 아까 사진에서 만든 그 권한(Role) 이름!
  apiGroup: rbac.authorization.k8s.io&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&quot;Service account(Pod)에 Role(Resources 사용 권한)을 붙여주는 것이 Role binding이다.&quot;&lt;/b&gt;&lt;/blockquote&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>clusterrolebinding</category>
      <category>K8s Admission Control</category>
      <category>K8s Authentication</category>
      <category>k8s authorization</category>
      <category>RBAC(Role Based Access Control)</category>
      <category>Role Binding</category>
      <category>Service Account</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/273</guid>
      <comments>https://dh-0e.tistory.com/273#entry273comment</comments>
      <pubDate>Mon, 8 Jun 2026 11:00:43 +0900</pubDate>
    </item>
    <item>
      <title>[Cloud] Service Routing &amp;amp; Pod Networking (CNI Plugin)</title>
      <link>https://dh-0e.tistory.com/272</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Service &amp;amp; Endpoints&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Hjts/dJMcagZZMKk/qfCG6FDl43vBpnXIaFJNn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Hjts/dJMcagZZMKk/qfCG6FDl43vBpnXIaFJNn1/img.png&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;411&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;236&quot; style=&quot;width: 54.8414%; margin-right: 10px;&quot; data-widthpercent=&quot;55.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Hjts/dJMcagZZMKk/qfCG6FDl43vBpnXIaFJNn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Hjts%2FdJMcagZZMKk%2FqfCG6FDl43vBpnXIaFJNn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3H44s/dJMcafGRnqQ/uqfdJLA9qOCd07m75YC0W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3H44s/dJMcafGRnqQ/uqfdJLA9qOCd07m75YC0W0/img.png&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;577&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.9958%;&quot; data-widthpercent=&quot;44.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3H44s/dJMcafGRnqQ/uqfdJLA9qOCd07m75YC0W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3H44s%2FdJMcafGRnqQ%2FuqfdJLA9qOCd07m75YC0W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Endpoints로 로드 밸런싱 효과를 얻을 수 있음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClusterIP(Frontend):&lt;/b&gt; 쿠버네티스 내부에서만 사용하는 가상의 IP 주소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Backend pool:&lt;/b&gt; 서비스 조건(Pod Selector)에 들어맞는 실제 파드들의 묶음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Endpoints&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스가 생성될 때 쿠버네티스가 자동으로 만들어주는 객체 (Master Node 안에 존재)&lt;/li&gt;
&lt;li&gt;트래픽을 넘겨받을 &lt;b&gt;'실제 파드들의 IP 주소 목록'을 보관&lt;/b&gt;함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Endpoints Controller:&lt;/b&gt; 파드들의 상태(Ready condition)를 지속적으로 감시하여, 트래픽을 받을 준비가 된 건강한 Pod들의 IP 목록만을 Endpoints 객체에 최신 상태로 유지해 주는 관리자 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Q:&lt;/b&gt; Master Node 내부의 endpoints에는 &lt;b&gt;private 주소&lt;/b&gt;가 담겨있는데, 어떻게 이것만 보고 Pod를 찾아갈 수 있는지? 라우터가 있는 건지?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A:&lt;/b&gt; Master Node는 Endpoints라는 주소록 데이터만 보관하고 있을 뿐, 실제로 Pod를 찾아가서 트래픽을 꽂아주진 않음. 라우터 역할을 실질적으로 수행하는 것은 각 노드(Worker Node 포함)에 설치된&lt;b&gt; Kube-proxy&lt;/b&gt;와 &lt;b&gt;CNI 플러그인&lt;/b&gt;(Calico, Cilium 등)으로 다음과 같은 순서로 패킷을 전달함
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Master Node 안에서 동작하는 &lt;b&gt;Endpoint Controller&lt;/b&gt;가 Pod들의 상태를 감시하며 건강한 Pod들의 Private IP 목록을 수집하여 Endpoints라는 객체로 만들고&lt;b&gt; API 서버&lt;/b&gt;에 저장&lt;/li&gt;
&lt;li&gt;클러스터를 구성하는 모든 노드에는 &lt;b&gt;Kube-proxy&lt;/b&gt;라는 에이전트가 항상 실행되고 있음. 이 kube-proxy들이 API 서버를 계속 보고 있다가, Endpoints가 업데이트되면 그 정보를 자기 노드로 가져옴
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;kube-proxy는 자기가 속한 노드의 리눅스 커널에 다음과 같은 규칙을 작성&lt;/li&gt;
&lt;li&gt;&quot;클라이언트가 Service IP로 요청을 보내면, 방금 Master Node에서 받아온 Pod의 Private IP(Endpoints) 중 하나로 목적지를 바꿔라&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CNI&lt;/b&gt;는 &lt;b&gt;BGP 라우팅 프로토콜&lt;/b&gt;을 사용해 노드끼리 본인 노드의 Pod 대역을 알려주며 경로를 서로 학습하게 만들거나, &lt;b&gt;VXLAN&lt;/b&gt; 같은 캡슐화/터널링 기술을 써서 Private IP 패킷을 실제 물리 노드의 IP 패킷으로 감싸서 안전하게 다른 노드로 배달해 줌
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;각 노드에 존재하는 CNI(Container Network Interface) 플러그인은 Pod 네트워크를 구성하고&lt;b&gt; 라우팅 경로를 전파&lt;/b&gt;하는 등, 실질적인 라우터와 &lt;b&gt;네트워크 스위치의 역할&lt;/b&gt;을 수행함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Kube-proxy&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; 클러스터의 모든 노드에서 실행되는 &lt;b&gt;네트워크 에이전트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재는 거의 &lt;b&gt;CNI 플러그인에 대체됨&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3가지 작동 모드&lt;/b&gt;를 지원함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Iptables 모드 (기본값)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GMQ7n/dJMcaffNRuj/yfqBKz7VF46BDOK50BXY30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GMQ7n/dJMcaffNRuj/yfqBKz7VF46BDOK50BXY30/img.png&quot; data-alt=&quot;IPtables mode&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GMQ7n/dJMcaffNRuj/yfqBKz7VF46BDOK50BXY30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGMQ7n%2FdJMcaffNRuj%2FyfqBKz7VF46BDOK50BXY30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;567&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IPtables mode&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kube-proxy의 기본 동작 모드로, 리눅스 커널의 &lt;b&gt;NAT(DNAT) 기능을 활용&lt;/b&gt;함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DNAT(목적지 주소 변환):&lt;/b&gt; 클라이언트가 패킷을 ClusterIP로 보내면, iptables 규칙이 그 패킷의 목적지 IP를 서비스 IP에서&amp;nbsp;&lt;b&gt;실제 Pod의 IP&lt;/b&gt;로 바꿔치기하여 전달 (하단 그림 참고)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;NodePort&lt;/b&gt;나 &lt;b&gt;LoadBalancer&lt;/b&gt; 방식도 포트 번호를 기준으로 비슷한 규칙을 사용함&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rB86x/dJMcadWwwDK/ykRktviKraNI2LckDpDQK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rB86x/dJMcadWwwDK/ykRktviKraNI2LckDpDQK0/img.png&quot; data-alt=&quot;NodePort and LoadBalancer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rB86x/dJMcadWwwDK/ykRktviKraNI2LckDpDQK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrB86x%2FdJMcadWwwDK%2FykRktviKraNI2LckDpDQK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;296&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NodePort and LoadBalancer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 한계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 내의 서비스(Pod) 개수가 늘어날수록 iptables 규칙 목록도 선형적으로 길어져 성능이 저하됨&lt;/li&gt;
&lt;li&gt;업데이트가 발생할 때마다 전체 테이블을 새로 덮어써야 해서 시간이 오래 걸림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. IPVS(IP Virtual Server) 모드&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;683&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EmAbB/dJMcafmyNMm/R15mQPG7aX0AJoXbTkckrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EmAbB/dJMcafmyNMm/R15mQPG7aX0AJoXbTkckrK/img.png&quot; data-alt=&quot;IPVS mode&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EmAbB/dJMcafmyNMm/R15mQPG7aX0AJoXbTkckrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEmAbB%2FdJMcafmyNMm%2FR15mQPG7aX0AJoXbTkckrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;683&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;683&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IPVS mode&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 커널에 내장된 &lt;b&gt;고급 로드밸런싱 기술&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;리스트를 위에서부터 훑어보는 iptables와 달리 &lt;b&gt;해시 테이블&lt;/b&gt;을 사용하여 목적지 Pod를 즉시 찾아내기 때문에 대규모 환경에서도 속도가 매우 빠름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Service IP 주소(key) :&lt;/b&gt; &lt;b&gt;목적지 Pod&amp;nbsp; 주소 (value)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Userspace(CNI를 통한 완전 대체)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 트렌드에서는 Kube-proxy 자체를 아예 빼버리고, &lt;b&gt;Cilium&lt;/b&gt; 같은 &lt;b&gt;고급 CNI 플러그인&lt;/b&gt;이 라우팅을 직접 통제하기도 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Cilium:&lt;/b&gt; 리눅스 커널의 강력한 도구인 &lt;b&gt;eBPF&lt;/b&gt;를 사용하여 데이터 플레인 수준에서 트래픽을 처리하며, 이 역시 해시 테이블을 사용해 초고속으로 endpoints를 찾아냄&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;K8S Networking Considerations&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;K8s의 모든 Pod들은 &lt;b&gt;NAT 없이 단일 네트워크처럼 작동&lt;/b&gt;하게끔 함&lt;/li&gt;
&lt;li&gt;Pod 간 통신을 위해 구성해야 할 &lt;b&gt;5가지 핵심 요소&lt;/b&gt;가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. IPAM(IP Address Management): 클러스터 내부의 Pod들에 IP를 할당하는 메커니즘&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터를 초기화할 때 &lt;b&gt;전체 IP 대역(CIDR)&lt;/b&gt;을 설정하며, 개별 노드는 이 전체 대역 중 일부(기본적으로 /24 크기)를 쪼개어 할당받아 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CIDR(Classless Inter-Domain Routing):&lt;/b&gt; 유연하게 IP 주소를 할당하고 관리하기 위해 사용되는 &lt;b&gt;표준 IP 주소 표기 및 분류 방식&lt;/b&gt; (&quot;IP 주소/서브넷 마스크&quot;로 표기하는 분류 방식)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buMRBy/dJMcagyWYvi/dWKY787DVvXLDq2FvkXSl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buMRBy/dJMcagyWYvi/dWKY787DVvXLDq2FvkXSl1/img.png&quot; data-alt=&quot;IPAM example&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buMRBy/dJMcagyWYvi/dWKY787DVvXLDq2FvkXSl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuMRBy%2FdJMcagyWYvi%2FdWKY787DVvXLDq2FvkXSl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;863&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IPAM example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Routing Protocol: 파드의 IP 주소 간 트래픽이 올바르게 전달되도록 라우팅 경로를 전파&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대표적으로 &lt;b&gt;BGP&lt;/b&gt;가 사용되며, 이를 통해 호스트 커널의 라우팅 테이블이 업데이트됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BGP(Border Gateway Protocol)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 사용되는 네트워크 프로토콜로 &lt;b&gt;Calico&lt;/b&gt;에서 사용됨&lt;/li&gt;
&lt;li&gt;커널 라우팅 테이블을 수정하여 각 잠재적 워크로드에 대한 경로를 포함함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WQp7g/dJMcabEpV59/1wTqxJBbnv5qnJGdKzkK4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WQp7g/dJMcabEpV59/1wTqxJBbnv5qnJGdKzkK4k/img.png&quot; data-alt=&quot;Calico에서 사용되는 BGP 및 라우팅 테이블&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WQp7g/dJMcabEpV59/1wTqxJBbnv5qnJGdKzkK4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWQp7g%2FdJMcabEpV59%2F1wTqxJBbnv5qnJGdKzkK4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;481&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Calico에서 사용되는 BGP 및 라우팅 테이블&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;주의)&lt;/b&gt; IPAM example 사진과 CIDR IP가 다름&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 캡슐화와 터널링 (Encapsulation and Tunneling)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;K8s 내부 네트워크와 실제 물리 네트워크를 분리&lt;/b&gt;하기 위한 기술&lt;/li&gt;
&lt;li&gt;출발지/도착지 Pod IP가 적힌 패킷을 실제 호스트 노드의 IP 패킷으로 감싸서 전송하며, k8s CNI에서는 여러 프로토콜(VXLAN, Geneve, GRE) 중 VXLAN 프로토콜이 가장 널리 쓰임&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mnHaC/dJMcaaS4eJK/fgW3VKav2dpl2eP8Bd8kEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mnHaC/dJMcaaS4eJK/fgW3VKav2dpl2eP8Bd8kEK/img.png&quot; data-alt=&quot;K8s 패킷 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mnHaC/dJMcaaS4eJK/fgW3VKav2dpl2eP8Bd8kEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmnHaC%2FdJMcaaS4eJK%2FfgW3VKav2dpl2eP8Bd8kEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;479&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;631&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;K8s 패킷 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 워크로드 라우팅 (Workload Routability)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 클라이언트가 클러스터 내부망에 있는 Pod에 접근할 수 있게 해주는 Service Routing 메커니즘&lt;/li&gt;
&lt;li&gt;주로 &lt;b&gt;Ingress Controller&lt;/b&gt;나 &lt;b&gt;Load Balancer&lt;/b&gt;를 통해 외부&amp;nbsp; 트래픽을 받아 내부 Pod로 연결해 줌&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GfyUa/dJMcaip00wT/MNdO3MZISiMkseixtSb9o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GfyUa/dJMcaip00wT/MNdO3MZISiMkseixtSb9o0/img.png&quot; data-alt=&quot;Ingress Controller 사용 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GfyUa/dJMcaip00wT/MNdO3MZISiMkseixtSb9o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGfyUa%2FdJMcaip00wT%2FMNdO3MZISiMkseixtSb9o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;561&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ingress Controller 사용 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 네트워크 정책 (Network Policy)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod로 들어오거나(Ingress) 나가는(Egress) &lt;b&gt;트래픽의 허용 여부를 정의&lt;/b&gt;하는 &lt;b&gt;방화벽 역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;AWS의 인바운드/아웃바운드 규칙과 유사&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;825&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpt2b5/dJMcabxDrV7/nbvFQsTv47QmXlIrTcXGIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpt2b5/dJMcabxDrV7/nbvFQsTv47QmXlIrTcXGIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpt2b5/dJMcabxDrV7/nbvFQsTv47QmXlIrTcXGIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpt2b5%2FdJMcabxDrV7%2FnbvFQsTv47QmXlIrTcXGIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;575&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;825&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;podSelector: {} (적용 대상)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비어있는 것은 이 네임스페이스의 모든 Pod에 적용된다는 뜻&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ingress: (들어오는 트래픽 - Inbound)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정의된 IP 범위, protocol로 port에서 들어오는 트래픽 등을 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;egress: (나가는 트래픽 - Outbound)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정의된 namespace, label, protocol로 port의 워크로드로 전송되는 트래픽을 제한함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기본 k8s 방화벽은 너무 원시적이라, IP가 자주 바뀌는 외부 API 통신이나 복잡한 L7 제어나 거대한 클러스터 관리가 어려움&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Calico나 Cilium이 제공하는 독자적인 고급 NetworkPolicy 기능을 써야 함&lt;/b&gt; &amp;nbsp;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;3,1&quot;&gt;&lt;span&gt;&lt;b&gt;Complex condition evaluation (복잡한 조건 평가)&lt;/b&gt; &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;3,2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;기본 K8s:&lt;/b&gt; &quot;A 파드가 B 파드로 가는 것 허용&quot;처럼 아주 단순한 AND 조건 정도만 가능&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;CNI 고급 기능:&lt;/b&gt; 훨씬 복잡한 논리를 짤 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) &quot;A 파드이면서 동시에 B 라벨이 없고, 트래픽이 평일 업무 시간에만 발생할 경우 허용(OR, NOT 조건 등 활용)&quot; 같은 세밀하고 복잡한 조건을 걸어 방화벽을 통제 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span data-path-to-node=&quot;5,0&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;5,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;5,1&quot;&gt;&lt;span&gt;&lt;b&gt;Resolution of IPs based on DNS records (DNS 레코드 기반 IP 해석)&lt;/b&gt; &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;5,2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;기본 K8s:&lt;/b&gt; 외부로 나가는 트래픽(Egress)을 제어할 때, 고정된 IP 주소(CIDR)로만 허용/차단 목록을 만들 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(예: 142.250.190.46 접속 허용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;CNI 고급 기능:&lt;/b&gt; IP 대신 도메인 이름(DNS)을 통과 조건으로 쓸 수 있음 (예: *.google.com 접속 허용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 클라우드 서비스나 API들은 IP가 수시로 바뀌기 때문에, 실무에서는 IP를 직접 입력하는 대신 도메인 기반 정책이 반드시 필요함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span data-path-to-node=&quot;7,0&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;7,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;7,1&quot;&gt;&lt;span&gt;&lt;b&gt;L7 rules (host, path, etc.) (L7 애플리케이션 계층 규칙)&lt;/b&gt; &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;7,2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;기본 K8s:&lt;/b&gt; L3/L4 계층인 IP와 Port(예: TCP 80번 포트)만 보고 트래픽을 통제하기 때문에, 패킷 안에 무슨 데이터가 들었는지는 모름&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;CNI 고급 기능:&lt;/b&gt; 패킷의 속을 까서 &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;8,1,0&quot;&gt;HTTP 통신(L7 계층)의 내용까지 보고 통제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 같은 웹 서버(TCP 80)로 가는 요청이더라도, GET /api/v1/users (조회)는 허용하지만 POST /api/v1/users (생성/수정)는 차단하는 식으로 훨씬 &lt;b&gt;정교한 통제가 가능&lt;/b&gt;해짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span data-path-to-node=&quot;9,0&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;9,1&quot;&gt;&lt;span&gt;&lt;b&gt;Cluster-wide policy (클러스터 전역 정책)&lt;/b&gt; &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;10,0,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0,0&quot;&gt;기본 K8s:&lt;/b&gt; 네트워크 정책은 철저하게 네임스페이스(Namespace) 단위로만 갇혀 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-path-to-node=&quot;10,0,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;10,0,0,2&quot;&gt;&lt;span&gt;만약 &quot;모든 파드는 사내 보안망을 거쳐야 한다&quot;라는 규칙을 만들려면, 클러스터에 네임스페이스가 100개 있으면 &lt;/span&gt;&lt;b data-index-in-node=&quot;62&quot; data-path-to-node=&quot;10,0,0,2&quot;&gt;&lt;span&gt;똑같은 정책 파일을 100번 복사해서 일일이 다 넣어줘야 함&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;CNI 고급 기능:&lt;/b&gt; &lt;b data-index-in-node=&quot;11&quot; data-path-to-node=&quot;10,1,0&quot;&gt;'GlobalNetworkPolicy'&lt;/b&gt; 같은 기능을 제공하여, 클러스터 전체에 한 번만 딱 적용하면 모든 네임스페이스가 일괄적으로 따르도록 통제할 수 있어 관리자의 수고를 덜어줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CNI Plugin&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod 간 네트워크 통신을 담당하는 소프트웨어&lt;/li&gt;
&lt;li&gt;Cluster 안에 있는 어떤 Pod던지 간에 직접 통신을 할 수 있게끔 Pod에 주소를 주고, Routing table을 업데이트 해줌&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>CNI 플러그인</category>
      <category>endpoints</category>
      <category>IPAM(IP Address Management)</category>
      <category>iptables</category>
      <category>IPVS(IP Virtual Server)</category>
      <category>kube-proxy</category>
      <category>service</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/272</guid>
      <comments>https://dh-0e.tistory.com/272#entry272comment</comments>
      <pubDate>Sun, 7 Jun 2026 22:53:51 +0900</pubDate>
    </item>
    <item>
      <title>[C++] Class vs Struct (+friend), static_cast, RTTI, vector, inline 함수, virtual, wchar/wstring</title>
      <link>https://dh-0e.tistory.com/271</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Class vs Struct&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class와 Struct의 가장 큰 차이는 &lt;b&gt;기본 접근 제어자&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Class&lt;/b&gt;는 접근 지시자를 생략했을 때 &lt;b&gt;기본 접근 지시자가 private&lt;/b&gt;, &lt;b&gt;Struct는 public&lt;/b&gt;임 (생각해 보면 당연한)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-path-to-node=&quot;13,0,0&quot; data-index-in-node=&quot;0&quot;&gt;struct를 쓰는 경우 (Plain Old Data, POD):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;13,0,1&quot;&gt;
&lt;li&gt;멤버 변수만 모아둔 데이터 전송 객체(DTO) 일 때&lt;/li&gt;
&lt;li&gt;별도의 로직(함수)이 거의 없고 단순히 값을 묶어서 전달할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) Point { int x, y; }, Rect { int w, h; }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-path-to-node=&quot;13,1,0&quot; data-index-in-node=&quot;0&quot;&gt;class를 쓰는 경우:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;13,1,1&quot;&gt;
&lt;li&gt;내부에 복잡한 로직이나 함수가 들어갈 때&lt;/li&gt;
&lt;li&gt;상속이나 다형성(Virtual function)을 활용할 때&lt;/li&gt;
&lt;li&gt;private 변수를 두고 public 메서드로 접근을 제어(캡슐화) 해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C++는 Java처럼 접근 지시자의 종류가 4가지가 아니라 &lt;b&gt;default를 뺀 3가지&lt;/b&gt;(public, private, protected)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;default 느낌을 사용하고 싶으면 friend를 사용하여야 함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;friend:&lt;/b&gt; 특정 외부 함수나 클래스에게 자신의 private 및 protected 멤버에 접근할 수 있는 권한을 부여&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;일방향성 (Not Mutual):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;A가 B를 friend로 선언했다고 해서, A가 B의 비밀번호를 알 수 있는 건 아니며, B도 A를 friend로 선언해 줘야 서로 공유가 가능&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;전이되지 않음 (Not Transitive):&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;A가 B의 친구고, B가 C의 친구라고 해서 A가 C의 private 멤버에 접근할 수 없음&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,2,0&quot;&gt;상속되지 않음 (Not Inherited):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;부모의 친구라고 해서 자식인 클래스와도 친구인 것은 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780748324020&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

// ==========================================
// 1. 일방향성 (Not Mutual)
// ==========================================
class Mutual_A {
private:
    int secretA = 10;
    friend class Mutual_B; // B에게 내(A) 비밀을 볼 수 있는 권한 부여
};

class Mutual_B {
private:
    int secretB = 20;

public:
    void ShowA(Mutual_A&amp;amp; a) {
        // [성공] B는 A의 friend이므로 A의 private 접근 가능
        std::cout &amp;lt;&amp;lt; &quot;[일방향성] A의 비밀: &quot; &amp;lt;&amp;lt; a.secretA &amp;lt;&amp;lt; std::endl; 
    }
    
    // 만약 Mutual_A 클래스에서 Mutual_B의 secretB에 접근하려 한다면?
    // ❌ 컴파일 에러 발생! B는 A를 friend로 선언한 적이 없기 때문
};

// ==========================================
// 2. 전이되지 않음 (Not Transitive)
// ==========================================
class Trans_A {
private:
    int secretA = 100;
    friend class Trans_B; // B를 친구로 인정
};

class Trans_B {
private:
    friend class Trans_C; // C를 친구로 인정
};

class Trans_C {
public:
    void TryAccessA(Trans_A&amp;amp; a) {
        // ❌ [컴파일 에러] 
        // C는 B의 친구일 뿐, A의 친구가 아니므로 A의 private 접근 불가
        std::cout &amp;lt;&amp;lt; a.secretA &amp;lt;&amp;lt; std::endl; 
    }
};

// ==========================================
// 3. 상속되지 않음 (Not Inherited)
// ==========================================
class Parent {
private:
    int parentSecret = 111;
    friend class Uncle; // Uncle(아저씨)을 부모의 친구로 인정
};

class Child : public Parent {
private:
    int childSecret = 222; // 자식만의 비밀
};

class Uncle {
public:
    void CheckSecrets(Parent&amp;amp; p, Child&amp;amp; c) {
        // [성공] 부모의 친구니까 부모의 private 접근 가능
        std::cout &amp;lt;&amp;lt; &quot;[비상속성] 부모의 비밀: &quot; &amp;lt;&amp;lt; p.parentSecret &amp;lt;&amp;lt; std::endl; 

        // ❌ [컴파일 에러] 
        // 부모의 친구라고 해서 자식의 private까지 접근할 수는 없음
        // std::cout &amp;lt;&amp;lt; &quot;자식의 비밀: &quot; &amp;lt;&amp;lt; c.childSecret &amp;lt;&amp;lt; std::endl; 
    }
};

// ==========================================
// 메인 함수 실행
// ==========================================
int main() {
    // 1. 일방향성 테스트
    Mutual_A mA;
    Mutual_B mB;
    mB.ShowA(mA);

    // 3. 상속성 테스트
    Parent p;
    Child c;
    Uncle u;
    u.CheckSecrets(p, c);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;static_cast의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;C++에서 C 스타일의 강제 형변환은 매우 위험할 수 있음&lt;/li&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b&gt;static_cast&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;명시적 형변환:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &quot;데이터가 잘릴 가능성이 있다는 거 나도 알고 있어. 그래도 내 의도대로 타입을 바꿔줘.&quot;라고 명시하는 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;경고 제거:&lt;/b&gt; 컴파일러의 &quot;데이터 손실 위험&quot; 경고를 깔끔하게 없애줌&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;컴파일 타임 체크:&lt;/b&gt; C 스타일의 강제 형변환(ex: (int32)size)보다 훨씬 안전함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서로 아예 관련 없는 타입(예: 포인터를 정수로 바꾸는 등)으로 변환하려고 하면 컴파일러가 에러를 뱉어 런타임 에러를 방지함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780746869291&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lockId = static_cast&amp;lt;int32&amp;gt;(_nameTold.size());
// _nameTold가 unordered_map이라면, _nameTold.size()를 호출했을 때 반환되는 값의 타입은 size_t
// 64비트 시스템에서 size_t(8바이트)를 int32(4바이트)로 변환&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;size_t&lt;/b&gt;: 보통 64비트 시스템에서 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;5,0,0&quot;&gt;8바이트(64비트) 부호 없는 정수&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;int32&lt;/b&gt;: 이름 그대로 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;5,1,0&quot;&gt;4바이트(32비트) 부호 있는 정수&lt;/b&gt;&lt;/li&gt;
&lt;li data-path-to-node=&quot;6&quot;&gt;원래는 큰 바구니(size_t)에 담긴 물건을 작은 바구니(int32)로 옮기려고 하니, 컴파일러 입장에서는 &quot;야, 이거 데이터 넘쳐서 잘릴 수도 있는데 괜찮겠어?&quot;라고 &lt;b&gt;경고(Warning)&lt;/b&gt;를 보냄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;6&quot;&gt;&lt;b&gt;static_cast&lt;/b&gt;를 사용하면 명시적인 형변환을 통해 경고를 합리적으로 잠재우는 역할을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;RTTI (Run-Time Type Information)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로그램이 실행 중(Run-Time)에 객체의 실제 데이터 타입 정보를 알아내는 메커니즘&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 기본적으로 컴파일 타임에 모든 타입을 결정짓지만, 객체 지향의 다형성으로 인해 런타임에 객체의 실제 데이터 타입을 알아내기 힘들 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. typeid 연산자&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 정확한 타입 정보를 담고 있는 &lt;b&gt;type_info 객체&lt;/b&gt;를 반환&lt;/li&gt;
&lt;li&gt;주로 &lt;b&gt;객체의 실제 클래스 이름을 출력&lt;/b&gt;하거나, &lt;b&gt;두 객체가 같은 타입인지 비교할 때&lt;/b&gt; 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;'typeinfo'&lt;/b&gt; 헤더 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780749678777&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;typeinfo&amp;gt; // typeid를 사용하기 위해 필요

class Parent { public: virtual ~Parent() {} };
class Child : public Parent {};

int main() {
    Parent* p = new Child();
    
    // 포인터 자체의 타입은 Parent*
    std::cout &amp;lt;&amp;lt; typeid(p).name() &amp;lt;&amp;lt; std::endl;  
    
    // 포인터가 가리키는 실제 알맹이의 타입은 Child
    std::cout &amp;lt;&amp;lt; typeid(*p).name() &amp;lt;&amp;lt; std::endl; 
    
    if (typeid(*p) == typeid(Child)) {
        std::cout &amp;lt;&amp;lt; &quot;이 객체의 진짜 정체는 Child입니다!&quot; &amp;lt;&amp;lt; std::endl;
    }
    
    delete p;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. dynamic_cast&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static_cast가 컴파일 타임에 억지로 형변환을 한다면, dynamic_cast는 실행 시점(Run-Time)에 안전하게 형변환을 시도&lt;/li&gt;
&lt;li&gt;만약 부모 포인터가 가리키는 객체가 내가 원하는 자식 타입이 맞다면 변환된 포인터를 주고, 아니라면 nullptr 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780749839887&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Parent* p1 = new ChildA();
Parent* p2 = new ChildB();

// p1이 가리키는 객체가 ChildA가 맞는지 확인하면서 캐스팅
if (ChildA* childA = dynamic_cast&amp;lt;ChildA*&amp;gt;(p1)) {
    childA-&amp;gt;SpecialSkillA(); // ChildA만 가진 고유 스킬 사용
} else {
    std::cout &amp;lt;&amp;lt; &quot;ChildA가 아닙니다.&quot; &amp;lt;&amp;lt; std::endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;RTTI는 꼭 필요할 때(외부 라이브러리의 객체를 판별해야 하거나, 특정 자식 객체만의 고유한 이벤트를 반드시 강제 호출해야 할 때)만 제한적으로 사용하는 것이 좋다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Vector 초기화와 안전한 접근&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;초기화 방법:&lt;/b&gt; vector&amp;lt;int&amp;gt; vec(10, -1); (크기 10, 모든 원소를 -1로 초기화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전한 포인터 반환:&lt;/b&gt; v.data();
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;&amp;amp;v[0]&lt;/b&gt;: 만약 벡터가 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;12,0,0&quot;&gt;비어있다면(empty)&lt;/b&gt;, 0번째 원소가 존재하지 않으므로 에러(정의되지 않은 동작)가 발생할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;v.data()&lt;/b&gt;: 벡터가 비어있어도 함수 호출 자체가 안전하며, nullptr을 반환해 줌 (현대적이고 안전한 방식)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;inline (인라인 함수)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;3&quot;&gt;컴퓨터가 함수를 실행할 때 보통은 &lt;b&gt;함수가 있는 메모리 주소로 점프했다가, 실행이 끝나면 다시 돌아오는&lt;/b&gt; 과정을 거침
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;3&quot;&gt;하지만 &lt;b&gt;inline&lt;/b&gt;을 붙이면 컴파일러가 함수를 호출하는 대신, 함수의 내용물을 호출하는 자리에 그대로 복사해서 붙여 넣어 버림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;inline void Set(T* ptr) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;이 함수는 반환값이 없고(void), 아주 짧아서 호출하는 곳에 코드를 직접 박아 넣는 방식(inline)으로 최적화하겠다&quot;는 선언&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;함수 호출 오버헤드가 사라짐: &lt;/b&gt;void f(){ _ptr = ptr; }처럼 &lt;b&gt;한두 줄밖에 없는 아주 짧은 함수&lt;/b&gt;들은 호출하는 시간보다 코드 한 줄 실행하는 시간이 더 짧기 때문에 &lt;b&gt;인라인화 하는 게 성능 최적화에 유리함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;헤더 파일 정의: &lt;/b&gt;헤더(*.h) 파일에 함수를 직접 구현할 때 inline을 붙이면, &lt;b&gt;여러 소스 파일에서 include 하더라도 중복 정의 에러를 방지&lt;/b&gt;할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;컴파일러의 판단:&lt;/b&gt; 요즘 똑똑한 컴파일러들은 inline을 안 써도 알아서 처리해주기도 하지만, 개발자가 &quot;이건 진짜 짧으니까 무조건 인라인으로 해줘!&quot;라고 힌트를 주는 역할을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwiG187s0oqTAxUAAAAAHQAAAAAQxgI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;0&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Virtual 소멸자&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;0&quot;&gt;&lt;b&gt;virtual: &lt;/b&gt;&quot;실행 시점(Runtime)에 진짜 정체가 무엇인지 확인하고 동작하라&quot;는 명령&lt;/li&gt;
&lt;li data-path-to-node=&quot;0&quot;&gt;C++에서 상속 관계를 다룰 때 부모 클래스의 소멸자에 virtual을 붙이는 것은 선택이 아닌 필수에 가까움&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;정적 바인딩 (Static Binding):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴파일러가 소스 코드를 보고 &quot;포인터 타입이 Parent네? 그럼 Parent 함수를 실행해!&quot;라고 결정하는 것 (virtual이 없을 때의 기본 동작)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;0&quot;&gt;virtual이 없으면 컴파일러는 &lt;b&gt;포인터 타입만 보고&lt;/b&gt; 부모의 소멸자만 호출 (자식 객체의 메모리는 누수됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;동적 바인딩 (Dynamic Binding):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로그램이 실행 중일 때 &quot;포인터는 Parent지만, 실제 가리키는 알맹이는 Child네? 그럼 Child 함수를 실행해야지!&quot;라고 결정하는 것 (virtual을 붙였을 때 동작)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;virtual을 붙이면 실행 시점에 &lt;b&gt;가상 함수 테이블(VTable)을 확인&lt;/b&gt;하여 실제 객체의 타입(Child)에 맞는 소멸자를 찾아 호출함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1773125028528&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Parent {
public:
    ~Parent() { cout &amp;lt;&amp;lt; &quot;~Parent()&quot; &amp;lt;&amp;lt; endl; }
};

class Child : public Parent {
public:
    int* data = new int[100]; // 자식만 가진 자원
    ~Child() { 
        delete[] data; 
        cout &amp;lt;&amp;lt; &quot;~Child()&quot; &amp;lt;&amp;lt; endl; 
    }
};

// 사용 예시
Parent* p = new Child();
delete p;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;실행 결과:&lt;/b&gt; ~Parent()만 호출되고 끝&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;문제점:&lt;/b&gt; Child 객체의 메모리는 할당되었는데, ~Child() 소멸자가 호출되지 않아 내부의 data 메모리가 해제되지 않아 메모리 누수 발생&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결 방법:&lt;/b&gt; ~Parent()에 virtual 추가 (virtual ~Parent() { ... } )
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;가상 함수 테이블 확인&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;~Child() 호출&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;~Parent() 자동 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++의 기본 객체 파괴 규칙에 따라 부모의 소멸자가 자동으로 이어서 호출됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;char vs wchar, string vs wstring&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;char / string:&lt;/b&gt; 1바이트 기반의 문자열 (ASCII 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;wchar_t / wstring:&lt;/b&gt; Wide Character의 약자로, &lt;b&gt;보통 2바이트 이상을 사용&lt;/b&gt;하여 유니코드(다국어)를 표현할 때 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows API나 게임 개발에서 &lt;b&gt;한글 처리를 위해 자주 쓰임&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>C++</category>
      <category>class vs struct</category>
      <category>Friend</category>
      <category>inline 함수</category>
      <category>RTTI</category>
      <category>static_cast</category>
      <category>vector</category>
      <category>Virtual</category>
      <category>wchar/wstring</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/271</guid>
      <comments>https://dh-0e.tistory.com/271#entry271comment</comments>
      <pubDate>Sat, 6 Jun 2026 21:48:55 +0900</pubDate>
    </item>
    <item>
      <title>[Cloud] Pod(Application) Update 종류 및 실습</title>
      <link>https://dh-0e.tistory.com/270</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;무중단 Update 종류&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Blue Green Update:&lt;/b&gt; &lt;b&gt;구버전(Blue)&lt;/b&gt;과 &lt;b&gt;신버전(Green)&lt;/b&gt;을 나란히 배포하고 &lt;b&gt;신버전으로 일제히 전환&lt;/b&gt;하는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Canary Update:&lt;/b&gt; &lt;b&gt;지정한 서버&lt;/b&gt; 혹은 &lt;b&gt;특정 사용자&lt;/b&gt;에게만 &lt;b&gt;새로운 버전으로 업데이트&lt;/b&gt;를 시켰다가&lt;b&gt; 정상적인 동작을 확인한 후에 전체를 업데이트&lt;/b&gt;하는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Rolling Update:&lt;/b&gt; 전체를&lt;b&gt; 무중단으로 업데이트&lt;/b&gt;하는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 이미지의 컨테이너가 배포된 Pod는 점진적으로 추가&lt;/li&gt;
&lt;li&gt;이전 이미지의 컨테이너가 배포된 Pod는 점진적으로 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Blue Green Update&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rOCI7/dJMcaiwuLNP/TSc9RYCrBKGOKAE0Vb17dK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rOCI7/dJMcaiwuLNP/TSc9RYCrBKGOKAE0Vb17dK/img.png&quot; data-alt=&quot;구버전(Blue)과 신버전(Green)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rOCI7/dJMcaiwuLNP/TSc9RYCrBKGOKAE0Vb17dK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrOCI7%2FdJMcaiwuLNP%2FTSc9RYCrBKGOKAE0Vb17dK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;311&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;구버전(Blue)과 신버전(Green)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Load Balancer:&lt;/b&gt; 트래픽 분산 처리기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt; &lt;b&gt;빠른 Rollback&lt;/b&gt;(예전 버전으로 돌아가는 것)이 가능하며 &lt;b&gt;버전관리&lt;/b&gt;가 용이&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점:&lt;/b&gt; 시스템 &lt;b&gt;자원이 두 배로 필요&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옛날 버전을 얼마나 붙들고 있는지가 중요&lt;br /&gt;ex) 에러 나면 롤백해야 되기 때문에, 근데 그냥 갖고 있기는 비용이 나감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실습 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAvVyY/dJMcajvph7E/QuKawmJde6SF6Bxgz3DbKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAvVyY/dJMcajvph7E/QuKawmJde6SF6Bxgz3DbKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAvVyY/dJMcajvph7E/QuKawmJde6SF6Bxgz3DbKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAvVyY%2FdJMcajvph7E%2FQuKawmJde6SF6Bxgz3DbKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;936&quot; height=&quot;441&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1499&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Okxev/dJMb99T8oL5/bY2usSj1iZKQqUPf4bMev0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Okxev/dJMb99T8oL5/bY2usSj1iZKQqUPf4bMev0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Okxev/dJMb99T8oL5/bY2usSj1iZKQqUPf4bMev0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOkxev%2FdJMb99T8oL5%2FbY2usSj1iZKQqUPf4bMev0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1499&quot; height=&quot;660&quot; data-origin-width=&quot;1499&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Endpoints 구성 방식&lt;/b&gt;을 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 &lt;b&gt;최종 End Pod의 IP 주소&lt;/b&gt;가 나와있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;649&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dukYqE/dJMcac4nL0K/PSTHTKTjhFBRrSnGjkbJT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dukYqE/dJMcac4nL0K/PSTHTKTjhFBRrSnGjkbJT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dukYqE/dJMcac4nL0K/PSTHTKTjhFBRrSnGjkbJT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdukYqE%2FdJMcac4nL0K%2FPSTHTKTjhFBRrSnGjkbJT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1487&quot; height=&quot;649&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;649&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZGmiX/dJMcaaMgSzq/ZJ5zCiNlovzBM3mXeaJkU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZGmiX/dJMcaaMgSzq/ZJ5zCiNlovzBM3mXeaJkU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZGmiX/dJMcaaMgSzq/ZJ5zCiNlovzBM3mXeaJkU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZGmiX%2FdJMcaaMgSzq%2FZJ5zCiNlovzBM3mXeaJkU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1488&quot; height=&quot;640&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node 껐다 켜면 IP 주소 바뀜&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Controller manager가 바꿔줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Canary Update&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c80BsR/dJMcadWe8bC/xEzXbG60OLfB6JkfsDWOZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c80BsR/dJMcadWe8bC/xEzXbG60OLfB6JkfsDWOZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c80BsR/dJMcadWe8bC/xEzXbG60OLfB6JkfsDWOZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc80BsR%2FdJMcadWe8bC%2FxEzXbG60OLfB6JkfsDWOZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;315&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소수의 사용자(10%)만 장애를 느낄 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드 밸런서 기능을 설정하여 관리자만 10%로 빠지게 만들 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;위험을 빠르게 감지&lt;/b&gt;할 수 있는 배포전략&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 일부를 신버전으로 분산&lt;/b&gt;하여 오류 확인이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실습 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLMSCG/dJMcabjTwh6/EkhV2ZxcBA9mRRWt4VtkL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLMSCG/dJMcabjTwh6/EkhV2ZxcBA9mRRWt4VtkL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLMSCG/dJMcabjTwh6/EkhV2ZxcBA9mRRWt4VtkL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLMSCG%2FdJMcabjTwh6%2FEkhV2ZxcBA9mRRWt4VtkL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;407&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bldhfG/dJMcac4nL8I/AHmKv1sZVp5NmHc9gNi3L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bldhfG/dJMcac4nL8I/AHmKv1sZVp5NmHc9gNi3L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bldhfG/dJMcac4nL8I/AHmKv1sZVp5NmHc9gNi3L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbldhfG%2FdJMcac4nL8I%2FAHmKv1sZVp5NmHc9gNi3L1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1544&quot; height=&quot;685&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnxS7p/dJMcafUn8FD/it9EpaKbMH2WyQkzk1cCfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnxS7p/dJMcafUn8FD/it9EpaKbMH2WyQkzk1cCfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnxS7p/dJMcafUn8FD/it9EpaKbMH2WyQkzk1cCfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnxS7p%2FdJMcafUn8FD%2Fit9EpaKbMH2WyQkzk1cCfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1485&quot; height=&quot;691&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Rolling Update&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;무중단 배포 방식:&lt;/b&gt; 서비스 접속자의 연결을 끊지 않고, 구 버전 Pod를 하나씩 죽이면서 동시에 새 버전 Pod를 띄워 자연스럽게 교체하는 k8s의 기본 업데이트 전략&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배포 조건:&lt;/b&gt; Deployment의 &lt;b&gt;Pod 템플릿 정보가 변경될 때&lt;/b&gt; 자동으로 트리거 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가장 흔한 사례:&lt;/b&gt; 컨테이너의 이미지 버전을 변경할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780476822318&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이미지 버전을 nginx:1.19로 바꾸면 롤링 업데이트가 즉시 시작됨
kubectl set image deployment/my-app my-app=nginx:1.19&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;업데이트 상태 제어:&lt;/b&gt; 롤링 업데이트는 진행 속도나 상태를 명령어로 직접 제어 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;잠깐 멈춤(Pause):&lt;/b&gt; 업데이트를 진행하다가 새 버전에 에러가 발생하는 등의 이유로 Pod 교체를 멈춰야 할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다시 시작(Resume):&lt;/b&gt; 문제없음을 확인하고 남은 Pod들도 마저 교체하라고 명령할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이전으로 되돌리기(Undo):&lt;/b&gt; 새 버전에 심각한 버그가 있어서 아예 업데이트 전의 구 버전으로 롤백할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780477123507&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl rollout pause deployment/my-app		// Pause
kubectl rollout resume deployment/my-app 	// Resume
kubectl rollout undo deployment/my-app 		// Undo&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Rolling Update Scenario&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzHI9b/dJMcaaFzfmN/2sHoNNnntuZdJR2c31iBf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzHI9b/dJMcaaFzfmN/2sHoNNnntuZdJR2c31iBf0/img.png&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;693&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.0114%; margin-right: 10px;&quot; data-widthpercent=&quot;45.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzHI9b/dJMcaaFzfmN/2sHoNNnntuZdJR2c31iBf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzHI9b%2FdJMcaaFzfmN%2F2sHoNNnntuZdJR2c31iBf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NqVUR/dJMcaijfhsJ/HOU26cGWBPFIZJYrUP8Kek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NqVUR/dJMcaijfhsJ/HOU26cGWBPFIZJYrUP8Kek/img.png&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;686&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.8258%;&quot; data-widthpercent=&quot;54.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NqVUR/dJMcaijfhsJ/HOU26cGWBPFIZJYrUP8Kek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNqVUR%2FdJMcaijfhsJ%2FHOU26cGWBPFIZJYrUP8Kek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1340&quot; height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Rolling Update Scenario 1/2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIB1Sj/dJMcadvovlh/hV43YdqaF83Vws3oGmKo70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIB1Sj/dJMcadvovlh/hV43YdqaF83Vws3oGmKo70/img.png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;685&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.7465%; margin-right: 10px;&quot; data-widthpercent=&quot;55.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIB1Sj/dJMcadvovlh/hV43YdqaF83Vws3oGmKo70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIB1Sj%2FdJMcadvovlh%2FhV43YdqaF83Vws3oGmKo70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1322&quot; height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vTd2g/dJMcaar0ILD/zQQgGHYyBZrbEhLbBRv9x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vTd2g/dJMcaar0ILD/zQQgGHYyBZrbEhLbBRv9x1/img.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;700&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.0907%;&quot; data-widthpercent=&quot;44.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vTd2g/dJMcaar0ILD/zQQgGHYyBZrbEhLbBRv9x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvTd2g%2FdJMcaar0ILD%2FzQQgGHYyBZrbEhLbBRv9x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1088&quot; height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Rolling Update Scenario 3/4 시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Rolling Update 실습 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1395&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rAfW5/dJMcac4nM0n/KxmcbEGpqirX0nnkfU9afk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rAfW5/dJMcac4nM0n/KxmcbEGpqirX0nnkfU9afk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rAfW5/dJMcac4nM0n/KxmcbEGpqirX0nnkfU9afk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrAfW5%2FdJMcac4nM0n%2FKxmcbEGpqirX0nnkfU9afk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1395&quot; height=&quot;624&quot; data-origin-width=&quot;1395&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL7WgM/dJMcajbim7s/84smoLR8dMXal1nOPvB7g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL7WgM/dJMcajbim7s/84smoLR8dMXal1nOPvB7g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL7WgM/dJMcajbim7s/84smoLR8dMXal1nOPvB7g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL7WgM%2FdJMcajbim7s%2F84smoLR8dMXal1nOPvB7g1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1338&quot; height=&quot;507&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Rolling Back 실습 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RApGC/dJMcageFBoL/D5AXUqmm9Ye7c3m8xSPVf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RApGC/dJMcageFBoL/D5AXUqmm9Ye7c3m8xSPVf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RApGC/dJMcageFBoL/D5AXUqmm9Ye7c3m8xSPVf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRApGC%2FdJMcageFBoL%2FD5AXUqmm9Ye7c3m8xSPVf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1417&quot; height=&quot;617&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOWB0V/dJMcajbim7L/v1NZxXG9tNeahy08Q6m0jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOWB0V/dJMcajbim7L/v1NZxXG9tNeahy08Q6m0jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOWB0V/dJMcajbim7L/v1NZxXG9tNeahy08Q6m0jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOWB0V%2FdJMcajbim7L%2Fv1NZxXG9tNeahy08Q6m0jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;593&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D1suz/dJMcahLpMgb/pWwIkjOYb32Q4TOngxRFAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D1suz/dJMcahLpMgb/pWwIkjOYb32Q4TOngxRFAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D1suz/dJMcahLpMgb/pWwIkjOYb32Q4TOngxRFAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD1suz%2FdJMcahLpMgb%2FpWwIkjOYb32Q4TOngxRFAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1404&quot; height=&quot;589&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>Application Update</category>
      <category>Blue Green Update</category>
      <category>canary update</category>
      <category>pod update</category>
      <category>Rolling back</category>
      <category>Rolling Update</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/270</guid>
      <comments>https://dh-0e.tistory.com/270#entry270comment</comments>
      <pubDate>Wed, 3 Jun 2026 18:14:17 +0900</pubDate>
    </item>
    <item>
      <title>[Cloud] HPA (Horizontal Pod Autoscaling)</title>
      <link>https://dh-0e.tistory.com/269</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Pod의 AutoScaling 방법&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운용 중인 Pod의 서비스 상황에 따라 Pod의 수 또는 Pod에 할당된 리소스의 양을 자동으로 조정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수요변화와 서비스 품질등을 고려하여 적정하게 자동으로 스케일링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck1J4E/dJMcaciKEwS/aP6eD222dDIzz2fU0Ar7dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck1J4E/dJMcaciKEwS/aP6eD222dDIzz2fU0Ar7dk/img.png&quot; data-alt=&quot;Scale Out(In) / Scale Up(Down)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck1J4E/dJMcaciKEwS/aP6eD222dDIzz2fU0Ar7dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck1J4E%2FdJMcaciKEwS%2FaP6eD222dDIzz2fU0Ar7dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;341&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Scale Out(In) / Scale Up(Down)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;HPA(Horizontal Pod Autoscaler, 수평 스케일링)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Pod의 개수&lt;/b&gt;를 &lt;b&gt;늘리거나(out) 줄임(in)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Scale out, Scale in&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;VPA(Vertical Pod Autoscaler, 수직 스케일링)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 워크로드를 위해 이미 실행 중인 Pod에 더 많은 &lt;b&gt;자원(메모리, CPU) 할당(up) 또는 해제(down)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Scale up, Scale down&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HPA (Horizontal Pod Autoscaler)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;개념&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Application의 부하&lt;/b&gt;에 따라 Deployment나 ReplicaSet 등에 적용하여 포함된 &lt;b&gt;Pod 수를 자동으로 조정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;CPU 사용률, 메모리 사용량, 사용자 정의 메트릭 등 특정 메트릭 기반
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;메트릭:&lt;/b&gt; 시스템 상태를 수치로 나타낸 값 (예: CPU 사용률, 메모리 사용량, 초당 요청 수(RPS) 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ex) 부하가 늘어나면 Pod를 일시적으로 늘리고, 부하가 줄어들면 다시 Pod의 숫자를 줄여 서버 내 자원을 효율적으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Metric 종류&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;리소스 메트릭:&lt;/b&gt; &lt;b&gt;CPU 사용률&lt;/b&gt;과&lt;b&gt; 메모리 사용률&lt;/b&gt;을 모니터링하여 스케일링 결정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 정의 메트릭:&lt;/b&gt; &lt;b&gt;Application 요구에 맞게 정의&lt;/b&gt;한 메트릭
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) HTTP 요청 수, 응답 시간 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;외부 메트릭:&lt;/b&gt; Kubernetes 외부에서 수집된 메트릭
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 외부 API 응답 시간, 클라우드 서비스 제공자의 서비스 사용량 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck1MBR/dJMcaicwWdd/6nbLnbeKDJEUQBnE39UOKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck1MBR/dJMcaicwWdd/6nbLnbeKDJEUQBnE39UOKK/img.png&quot; data-alt=&quot;Need for HPA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck1MBR/dJMcaicwWdd/6nbLnbeKDJEUQBnE39UOKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck1MBR%2FdJMcaicwWdd%2F6nbLnbeKDJEUQBnE39UOKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;292&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Need for HPA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스 품질 향상&lt;/li&gt;
&lt;li&gt;비용 관리 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1rCpW/dJMcaaFjAzi/2PERV3B6xsEs5au54OwUK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1rCpW/dJMcaaFjAzi/2PERV3B6xsEs5au54OwUK1/img.png&quot; data-alt=&quot;Metric을 통한 HPA 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1rCpW/dJMcaaFjAzi/2PERV3B6xsEs5au54OwUK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1rCpW%2FdJMcaaFjAzi%2F2PERV3B6xsEs5au54OwUK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;463&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Metric을 통한 HPA 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;메트릭 서버:&lt;/b&gt; &lt;b&gt;Kubelet&lt;/b&gt;으로부터 메트릭을 가져옴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HPA 컨트롤러:&lt;/b&gt; &lt;b&gt;15초&lt;/b&gt;마다 메트릭 서버에서 &lt;b&gt;메트릭을 조회&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HPA 컨트롤러:&lt;/b&gt; 원하는 &lt;b&gt;복제본 수를 계산&lt;/b&gt;함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HPA 컨트롤러: API 서버&lt;/b&gt;를 통해 &lt;b&gt;관련 Deployment를 업데이트&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deployment 컨트롤러:&lt;/b&gt; 이를 처리하여&lt;b&gt; ReplicaSet을 업데이트&lt;/b&gt;, 그 결과로 Pods의 수가 변경&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/putpb/dJMcacC7Imd/QtAYWNwx2rPkfLaOgBpJbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/putpb/dJMcacC7Imd/QtAYWNwx2rPkfLaOgBpJbK/img.png&quot; data-alt=&quot;Metrics Server&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/putpb/dJMcacC7Imd/QtAYWNwx2rPkfLaOgBpJbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fputpb%2FdJMcacC7Imd%2FQtAYWNwx2rPkfLaOgBpJbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1225&quot; height=&quot;459&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Metrics Server&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Metrics Server&lt;/b&gt;는&lt;b&gt; K8s 클러스터에서&lt;/b&gt; CPU 및 메모리와 같은 메트릭 데이터를 수집하고, 클러스터 운영 및 오토 스케일링(HPA, VPA)에 필요한 실시간 모니터링 정보를 제공해 줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HPA와 VPA와 같은 오토스케일러는 &lt;b&gt;직접 Pod 상태를 보는 것이 아니라&lt;/b&gt;, &lt;b&gt;API Server를 통해 제공되는 메트릭 정보를 읽어서&lt;/b&gt; &lt;b&gt;스케일링 여부를 결정&lt;/b&gt;함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HPA 구동방법&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&quot;kubectl autoscale&quot;&lt;/b&gt;&amp;nbsp;명령을 통해&lt;b&gt; horizontalPodAutoscaler(HPA) 생성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또는&lt;b&gt; yaml 파일&lt;/b&gt;로 kubectl apply -f 해서 배포 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djDZOW/dJMcah5u4CR/tgs8wT9OrNpkCG1nmJjwok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djDZOW/dJMcah5u4CR/tgs8wT9OrNpkCG1nmJjwok/img.png&quot; data-alt=&quot;HPA yaml file&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djDZOW/dJMcah5u4CR/tgs8wT9OrNpkCG1nmJjwok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjDZOW%2FdJMcah5u4CR%2Ftgs8wT9OrNpkCG1nmJjwok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;373&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HPA yaml file&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;min, maxReplicas:&lt;/b&gt; 스케일링 시에 &lt;b&gt;최대, 최소 Replica 개수&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metrics.type:&lt;/b&gt; 어떤 타입의 metric 인지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metrics.resource&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;name: &lt;/b&gt;metric 종류&lt;/li&gt;
&lt;li&gt;&lt;b&gt;target:&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어떤 조건에서 스케일링하는지&lt;/span&gt; &amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;averageUtilization(cpu 혹은 memory 사용량)&lt;/b&gt;이 50%가 되면 Scale up&lt;/li&gt;
&lt;li&gt;현재 &lt;b&gt;resource.name&lt;/b&gt;이 cpu이므로 cpu 사용령이 50%가 되면 Scale up&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1780469694409&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource       # 메모리 조건 추가
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 60  # 메모리가 60%를 넘을 때&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU와 Memory를 모두 감시&lt;/b&gt;하기 위한 yaml 파일의 metrics&lt;/li&gt;
&lt;li&gt;위와 같이 설정하면, CPU가 50%를 넘거나, 메모리가 60%를 넘을 때 HPA 작동&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HPA 작동 방식&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;메트릭 모니터링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일정 주기(Default: 15s)&lt;/b&gt;마다 CPU 사용량, 메모리 사용량, 커스텀 메트릭 or 외부 메트릭을 모니터링&lt;/li&gt;
&lt;li&gt;Kubernetes API 서버에 등록된 &lt;b&gt;메트릭 서버&lt;/b&gt;나 &lt;b&gt;외부 모니터링 도구&lt;/b&gt;를 통해 수집
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 모니터링 도구 ex) 외부 API 응답 시간(Kubernetes와 직접 연관되지 않은 외부트래픽에 따른 값)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;임계값 평가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HPA는 &lt;b&gt;목표 메트릭 값(Target Metric)&lt;/b&gt;과 &lt;b&gt;현재 메트릭 값을 비교&lt;/b&gt;하여&lt;b&gt; 임계값 초과 여부를 평가&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex 1) 목표 CPU 사용률이 60%로 설정된 경우, 현재 CPU 사용률이 70%를 넘는다면 Scale out이 필요하다고 판단&lt;/li&gt;
&lt;li&gt;ex 2) 현재 CPU 사용률이 50%라면 Scale in이 필요하다고 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pod 스케일링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;측정된 메트릭 값이 &lt;b&gt;임계값을 초과하면&lt;/b&gt; HPA는&lt;b&gt; Pod 수를 늘려서&lt;/b&gt; &lt;b&gt;부하를 분산&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;측정된 메트릭 값이 &lt;b&gt;임계값 이하로 떨어지면&lt;/b&gt; 불필요한 &lt;b&gt;Pod 수를 줄여&lt;/b&gt; &lt;b&gt;리소스 낭비를 방지&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;목표 메트릭 값 대비 현재 메트릭 값의 비율을 계산하여 Pod 수 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1631&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNwXl/dJMb997FP6P/1Zp5lKrvnp4lECK3pC0jO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNwXl/dJMb997FP6P/1Zp5lKrvnp4lECK3pC0jO0/img.png&quot; data-alt=&quot;HPA가 Pod 수를 결정할 때 사용하는 뼈대가 되는 공식 (현재는 다양한 커스텀 Metrics 사용)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNwXl/dJMb997FP6P/1Zp5lKrvnp4lECK3pC0jO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNwXl%2FdJMb997FP6P%2F1Zp5lKrvnp4lECK3pC0jO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1631&quot; height=&quot;166&quot; data-origin-width=&quot;1631&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HPA가 Pod 수를 결정할 때 사용하는 뼈대가 되는 공식 (현재는 다양한 커스텀 Metrics 사용)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;n%의 기준점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HPA가 &quot;CPU 사용량이 50%에 도달했다&quot;라고 판단하려면 전체&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;100%가 얼마인지 아는 기준점이 필요&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deployment(php-apache)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;안의 Pod 설정 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;11&quot; data-index-in-node=&quot;49&quot;&gt;resources.requests.cpu에 할당된 값&lt;/b&gt;을 기준으로 계산
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Downward API&lt;/b&gt;로 확인할 수 있었음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clf5HB/dJMcahR8Fw9/dknZFXL9R1B4vxM3iNsWMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clf5HB/dJMcahR8Fw9/dknZFXL9R1B4vxM3iNsWMK/img.png&quot; data-alt=&quot;php-apache.yaml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clf5HB/dJMcahR8Fw9/dknZFXL9R1B4vxM3iNsWMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclf5HB%2FdJMcahR8Fw9%2FdknZFXL9R1B4vxM3iNsWMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;804&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;php-apache.yaml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;resources: 필드&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 컨테이너에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;리소스 요청 및 제한 정의&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;limits:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너가 사용할 수 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;리소스의 최대치&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;cpu: 500m&quot;&lt;/b&gt;는 컨테이너가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;최대 500밀리 코어(0.5 코어)&lt;/b&gt;의 CPU를 사용할 수 있도록 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;requests:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;기본적으로 필요한&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;리소스의 양&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;HPA는 request 값을 기준으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;CPU target 사용량(%)을 설정&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HPA 급격한 변화 방지&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biTgfZ/dJMcahR8FMD/tFpvA2ISUh4u11sniTQc21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biTgfZ/dJMcahR8FMD/tFpvA2ISUh4u11sniTQc21/img.png&quot; data-alt=&quot;spec &amp;amp;gt;&amp;amp;gt; Scale-down 동작 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biTgfZ/dJMcahR8FMD/tFpvA2ISUh4u11sniTQc21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiTgfZ%2FdJMcahR8FMD%2FtFpvA2ISUh4u11sniTQc21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1630&quot; height=&quot;429&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;spec &amp;gt;&amp;gt; Scale-down 동작 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메트릭 변동으로 인해 Pod 수가 급격하게 변동하는 것을 방지하기 위한 &lt;b&gt;scaleDwon 동작 설정&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QDybA/dJMcabxDko7/BuVP1QZocUYKZCKwWwiAm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QDybA/dJMcabxDko7/BuVP1QZocUYKZCKwWwiAm0/img.png&quot; data-alt=&quot;안정화 윈도우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QDybA/dJMcabxDko7/BuVP1QZocUYKZCKwWwiAm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQDybA%2FdJMcabxDko7%2FBuVP1QZocUYKZCKwWwiAm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;148&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;안정화 윈도우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메트릭 변동으로 인해 Pod 수가 급격하게 변동하는 것을 방지하기 위한 &lt;b&gt;시간 범위 설정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스케일링을 수행하기 전, &lt;b&gt;지정된 윈도우 동안 수집된 메트릭 값 중 가장 높은 값을 사용&lt;/b&gt;하여 스케일링 여부를 결정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;위 예시)&lt;/b&gt; 300s 동안 수집된 메트릭 값 중 가장 높은 값을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불필요한 스케일 다운을 방지&lt;/b&gt;하고, &lt;b&gt;안정성을 유지&lt;/b&gt;할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;통합 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nMQ4j/dJMcaiKgJYj/27PxOdKCHTkMkstv8SAPak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nMQ4j/dJMcaiKgJYj/27PxOdKCHTkMkstv8SAPak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nMQ4j/dJMcaiKgJYj/27PxOdKCHTkMkstv8SAPak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnMQ4j%2FdJMcaiKgJYj%2F27PxOdKCHTkMkstv8SAPak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1782&quot; height=&quot;732&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>HPA(Horizontal Pod Autoscaling)</category>
      <category>Metrics</category>
      <category>Scale in/out</category>
      <category>Scale up/down</category>
      <category>scaleDwon 정책</category>
      <category>scaleUp 정책</category>
      <category>메트릭</category>
      <category>안정화 윈도우</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/269</guid>
      <comments>https://dh-0e.tistory.com/269#entry269comment</comments>
      <pubDate>Wed, 3 Jun 2026 16:12:27 +0900</pubDate>
    </item>
    <item>
      <title>[Cloud] StatefulSet</title>
      <link>https://dh-0e.tistory.com/268</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;StatefulSet&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의:&lt;/b&gt; &lt;b&gt;&quot;상태가 있는(Stateful) 애플리케이션&quot;&lt;/b&gt;을 관리하기 위해&amp;nbsp; Pod의 순서와 고유성을 보장하며 배포하는 컨트롤러&lt;/li&gt;
&lt;li&gt;Pod들은 &lt;b&gt;동일한 컨테이너 스펙으로 생성&lt;/b&gt;되지만, 서로 교체할 수 없는 &lt;b&gt;고유한 식별자&lt;/b&gt;와 &lt;b&gt;상태&lt;/b&gt;를 가짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;재스케줄링(Pod 재생성) 시&lt;/b&gt;에도 Pod의 이름과 네트워크 식별자, 스토리지 볼륨이&lt;b&gt; 그대로 유지&lt;/b&gt;됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처(Use Cases)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터베이스 및 클러스터링 시스템:&lt;/b&gt; MySQL, MongoDB, Redis, Kafka 등 상태와 &lt;b&gt;데이터 유지가 필수적인 워크로드&lt;/b&gt;의 90% 이상에 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;워크로드:&lt;/b&gt; 컴퓨팅 리소스가 작업 완료 또는 결과 도출을 위해 수행하는 처리 작업의 유형과 양&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;안정적이고 고유한 &lt;b&gt;네트워크 식별자&lt;/b&gt;가 필요한 경우&lt;/li&gt;
&lt;li&gt;안정적인 &lt;b&gt;영구 스토리지(PV)&lt;/b&gt;가 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순차적인&lt;/b&gt; Pod의 배포, 스케일링, 삭제가 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PV&lt;/b&gt;와 궁합이 잘 맞음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스토리지(Volume) 및 스케일링 특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;volumeClaimTemplates 사용:&lt;/b&gt; 각 Pod가 생성될 때 고유한 PVC를 자동으로 생성해 개별 PV와 1:1로 매핑함&lt;/li&gt;
&lt;li&gt;Deployment는 하나의 PVC를 여러 Pod가 공유하는 방식이 흔하지만, StatefulSet은 이 템플릿을 통해 &lt;b data-index-in-node=&quot;135&quot; data-path-to-node=&quot;8,0,0&quot;&gt;생성되는 각 Pod마다 자신만의 독립적인 PVC를 자동으로 생성&lt;/b&gt;하여 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 (Headless Service)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StatefulSet은 로드밸런싱 목적인 일반 Service(ClusterIP) 대신 &lt;b&gt;Headless Service(ClusterIP: None)와 짝을 이루어 사용함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;각 Pod는 &amp;lt;Pod이름&amp;gt;.&amp;lt;Service이름&amp;gt;.&amp;lt;Namespace&amp;gt;.svc.cluster.local 형태의 &lt;b&gt;고정된 내부 DNS 주소&lt;/b&gt;를 가지게 되어, 특정 Pod(예: DB Master 노드)를 정확히 지정해 통신할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTlSMX/dJMcaiwqKYG/cv0qkVqvlRLYeqrx5lXCP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTlSMX/dJMcaiwqKYG/cv0qkVqvlRLYeqrx5lXCP1/img.png&quot; data-alt=&quot;Scale Up/Down of StatefulSet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTlSMX/dJMcaiwqKYG/cv0qkVqvlRLYeqrx5lXCP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTlSMX%2FdJMcaiwqKYG%2Fcv0qkVqvlRLYeqrx5lXCP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;927&quot; height=&quot;426&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Scale Up/Down of StatefulSet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Scale Down&lt;/b&gt;을 해도 PVC가 없어지지 않고 유지됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Scale Up&lt;/b&gt;을 했을 때 기존 PVC로 쉽게 PV와 연결할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Deployment vs StatefulSet 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1IMVO/dJMcab5ac2d/WO1coIoqutHRavfe0uV5R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1IMVO/dJMcab5ac2d/WO1coIoqutHRavfe0uV5R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1IMVO/dJMcab5ac2d/WO1coIoqutHRavfe0uV5R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1IMVO%2FdJMcab5ac2d%2FWO1coIoqutHRavfe0uV5R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;445&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Deployment&lt;/b&gt;로 만든 여러 개의 Pod들은 &lt;b&gt;고유 ID나 Pod간의&lt;/b&gt; &lt;b&gt;순서가 없으며, 임의의 이름을 할당&lt;/b&gt; 받음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Pod들은 &lt;b&gt;공유 볼륨을 사용&lt;/b&gt;할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;StatefulSet&lt;/b&gt;으로 만든 여러 개의 Pod들은 &lt;b&gt;고유 ID를 가지며, 순차적으로 생성 및 종료&lt;/b&gt;됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 Pod에 &lt;b&gt;개별적인 PV가 필요&lt;/b&gt;함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1KTaS/dJMcagSW1NK/osUyafVkf4A3iEIoyp6kKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1KTaS/dJMcagSW1NK/osUyafVkf4A3iEIoyp6kKk/img.png&quot; data-alt=&quot;Using PV in Deployment&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1KTaS/dJMcagSW1NK/osUyafVkf4A3iEIoyp6kKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1KTaS%2FdJMcagSW1NK%2FosUyafVkf4A3iEIoyp6kKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;865&quot; height=&quot;313&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Using PV in Deployment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PeyGS/dJMcagZGi08/aT98fjp5tB1JdckKgWwPNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PeyGS/dJMcagZGi08/aT98fjp5tB1JdckKgWwPNK/img.png&quot; data-alt=&quot;Using PV in StatefulSet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PeyGS/dJMcagZGi08/aT98fjp5tB1JdckKgWwPNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPeyGS%2FdJMcagZGi08%2FaT98fjp5tB1JdckKgWwPNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;892&quot; height=&quot;384&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Using PV in StatefulSet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Deployment 여러 개로 StatefulSet 기능 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1rfVs/dJMcahYBRQW/Hrklp028aKK3HBd50XF9j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1rfVs/dJMcahYBRQW/Hrklp028aKK3HBd50XF9j1/img.png&quot; data-alt=&quot;Stateful Application(Pod) without using StatefulSet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1rfVs/dJMcahYBRQW/Hrklp028aKK3HBd50XF9j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1rfVs%2FdJMcahYBRQW%2FHrklp028aKK3HBd50XF9j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;930&quot; height=&quot;476&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Stateful Application(Pod) without using StatefulSet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 Replica(Pod)들은 고유의 Service와 Deployment를 가짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Service들은 각 Pod들에게 안정된 주소를 분리시켜 제공함 &lt;b&gt;(Service x 3)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Deployment들은 각 Pod들에게 모두 다른 PVC를 제공함 &lt;b&gt;(Deployment x 3)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;공유 주소로 모든 Pod를 함께 노출하고 싶을 경우, 또 다른 서비스(사진의 quiz Service)가 필요함 &lt;b&gt;(+ 1 Service)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;만약 사진처럼 &lt;b&gt;3개의 Pod에 각각 고유한 주소와 스토리지&lt;/b&gt;를 쥐어주려면, 사진에 나온 것처럼 &lt;b&gt;3개의 Deployment&lt;/b&gt;, &lt;b&gt;3개의 개별 Service&lt;/b&gt;, &lt;b&gt;1개의 공통 Service&lt;/b&gt; 등 엄청나게 많은 리소스(YAML 파일)를 직접 관리해야 하는 복잡성이 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StatefulSet을 활용하면 이를 &lt;b&gt;단 2개의 리소스로 압축&lt;/b&gt;해 줌
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;StatefulSet:&lt;/b&gt; 사진 속의 &lt;b&gt;파란색(Deployment)&lt;/b&gt;과 &lt;b&gt;초록색(PVC)&lt;/b&gt; 상자들의 역할&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Headless Service:&lt;/b&gt; 사진 속의 &lt;b&gt;노란색(개별 Service)&lt;/b&gt; 상자들 역할&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Headless Service&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XIkIT/dJMcahK51Dh/FAqoYbHhV6CKuwvwdleem0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XIkIT/dJMcahK51Dh/FAqoYbHhV6CKuwvwdleem0/img.png&quot; data-alt=&quot;Headless Service for StatefulSet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XIkIT/dJMcahK51Dh/FAqoYbHhV6CKuwvwdleem0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXIkIT%2FdJMcahK51Dh%2FFAqoYbHhV6CKuwvwdleem0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;924&quot; height=&quot;369&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Headless Service for StatefulSet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반적인 Service&lt;/b&gt;에서는 &lt;b&gt;자신만의 고유한 IP(Cluster IP)&lt;/b&gt;를 가짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로드밸런싱:&lt;/b&gt; 외부에서 이 IP로 요청을 보내면 Service가 알아서 뒤에 있는 여러 Pod 중 무작위로 트래픽을 넘겨줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StatefulSet은 Pod들이 각각 고유 번호가 있기 때문에 &lt;b&gt;Cluster IP를 주지 않는&lt;/b&gt; &lt;b&gt;Headless Service&lt;/b&gt;를 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Service 자체의 대표 ClusetrIP(Head)를 없애버린 것&lt;/li&gt;
&lt;li&gt;대표 IP가 없는 대신, 쿠버네티스 내부의 &lt;b&gt;DNS 서버(주황색 원통)에 각 Pod들의 고유한 이름과 실제 IP 주소를 매핑&lt;/b&gt;하여 저장&lt;/li&gt;
&lt;li&gt;이 덕분에 랜덤한 Pod가 아니라, &lt;b&gt;내가 원하는 정확한 Pod를 콕 집어서 찾아갈 수 있게 됨&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;StatefulSet의 Pod들은 각각 자기만의 고유한 역할과 데이터가 있기 때문에 아무 데나 랜덤하게 접속하면 안 된다. 그래서 대표 IP를 지워버린 Headless Service를 사용해, DNS를 통해 각 Pod의 고유한 도메인 주소(FQDN)로 직접 찾아가게 만든다.&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;StatefulSet YAML&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVypFF/dJMcadWwkMX/iUi3iCaEjigZLcLBfC7MaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVypFF/dJMcadWwkMX/iUi3iCaEjigZLcLBfC7MaK/img.png&quot; data-alt=&quot;Headless Service yaml, StatefulSet yaml&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVypFF/dJMcadWwkMX/iUi3iCaEjigZLcLBfC7MaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVypFF%2FdJMcadWwkMX%2FiUi3iCaEjigZLcLBfC7MaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1692&quot; height=&quot;780&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Headless Service yaml, StatefulSet yaml&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Headless Service (왼쪽 코드)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kind: Service&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metadata.name: nginx&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;clusterIP: None (핵심)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 Service를 &lt;b&gt;Headless Service로 바꿔주는 옵션&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;자신만의 대표 IP(머리)를 가지지 않고, &lt;b&gt;뒤에 연결될 Pod들의 IP를 DNS에 묶어주는 역할만 수행&lt;/b&gt;하겠다고 선언하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;StatefulSet (중간/오른쪽 코드)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kind: StatefulSet&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;serviceName: &quot;nginx&quot; (연결 고리)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞서 만든 Headless Service와 StatefulSet을 짝지어주는 역할&lt;/li&gt;
&lt;li&gt;내가 만드는 Pod(web-0, web-1, web-2)들의 고유 도메인 주소를 &lt;b&gt;nginx라는 이름의 Headless Service DNS에 등록해 주라는 의미&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;replicas: 3&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;web-0, web-1, web-2 3개의 Pod를 &lt;b&gt;순차적&lt;/b&gt;으로 찍어냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;volumeClaimTemplates (스토리지 자동화)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전에 봤던&lt;b&gt; &quot;각 Pod마다 따로 PVC를 만들어주는 번거로운 작업&quot;을 해결&lt;/b&gt;해 주는 템플릿&lt;/li&gt;
&lt;li&gt;&lt;b&gt;storage: 1Gi&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod를 3개 만들면서, 이 템플릿을 바탕으로&lt;b&gt; 3개의 독립적인 1Gi PVC를 알아서 생성&lt;/b&gt;하고 &lt;b&gt;각각의 Pod에 1:1로 마운트 &lt;/b&gt;해줌 (mountPath: /usr/share/nginx/html)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한계점&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;스토리지(PV) 사전 준비 필수 (인프라 구성 난이도 상승)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 Pod의 스토리지는 요청된 스토리지 클래스를 기반으로 &lt;b&gt;PV Provisioner(StorageClass)에 의해 제공&lt;/b&gt;되거나, &lt;b&gt;인프라 관리자에 의해 사전 제공&lt;/b&gt;되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 삭제되지 않는 볼륨 (데이터 유지됨)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StatefulSet을 삭제하거나 Scale-down(축소)해도 StatefulSet과 관련된 볼륨은 삭제되지 않음&lt;/li&gt;
&lt;li&gt;테스트가 끝난 후 리소스를 완전히 정리할 때 관리자가 수동으로 PVC를 지워주지 않으면 클라우드 환경에서 계속 스토리지 과금이 발생할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;현재 포드의 네트워크 동일성을 책임지기 위해 Headless Service가 항상 필요함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;StatefulSet이 삭제될 때 Pod의 종료에 대한 보장을 제공하지 않음&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StatefulSet을&lt;b&gt; &quot;kubectl delete&quot;&lt;/b&gt;로 한 번에 날려버리면, Pod들이 순서를 지키지 않고 동시다발적으로 강제 종료될 위험이 있음&lt;/li&gt;
&lt;li&gt;분산 서버나 데이터베이스라면, 디스크에 안전하게 저장(Flush)할 시간이 필요하므로, 반드시&lt;b&gt; &quot;replicas: 0&quot;&lt;/b&gt;으로 먼저 &lt;b&gt;Scale-down을 지시&lt;/b&gt;하여 제일 마지막 번호의 Pod부터 0번까지 차례대로&lt;b&gt;(역순으로)&lt;/b&gt; 안전하게 마무리 작업을 하고 꺼지도록 유도해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>Headless Service</category>
      <category>statefulset</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/268</guid>
      <comments>https://dh-0e.tistory.com/268#entry268comment</comments>
      <pubDate>Wed, 3 Jun 2026 14:26:51 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Server] Packet Handler</title>
      <link>https://dh-0e.tistory.com/267</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PacketHandler&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;네트워크 통신(Session)&lt;/b&gt;과 &lt;b&gt;게임 로직(Content)&lt;/b&gt; 사이를 연결하는 추상화 클래스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에는 Session 클래스의 OnRecvPacket에서 직접 패킷을 분석하고 처리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;패킷의 종류가 늘어날수록 Session 코드가 비대해지는 것을 방지하기 위해 &lt;b&gt;패킷의 생성과 해석만을 전담&lt;/b&gt;하는 별도의 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[ GameServer ]&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ServerPacketHandler.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778310092885&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

/*------------------------
	ServerPacketHandler
------------------------*/

enum { S_TEST = 1 };

struct BuffData {
	uint64 buffId;
	float remainTime;
};

class ServerPacketHandler {
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static SendBufferRef Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector&amp;lt;BuffData&amp;gt; buffs);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ServerPacketHandler.cpp&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778310129083&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;ServerPacketHandler.h&quot;
#include &quot;BufferReader.h&quot;
#include &quot;BufferWriter.h&quot;

/*------------------------
	ServerPacketHandler
------------------------*/

void ServerPacketHandler::HandlePacket(BYTE* buffer, int32 len) {
	BufferReader br(buffer, len);

	PacketHeader header;
	br.Peek(&amp;amp;header);

	switch (header.id)
	{
	default:
			break;
	}
}

SendBufferRef ServerPacketHandler::Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector&amp;lt;BuffData&amp;gt; buffs) 
{
	SendBufferRef sendBuffer = MakeShared&amp;lt;SendBuffer&amp;gt;(4096);

	BufferWriter bw(sendBuffer-&amp;gt;Buffer(), sendBuffer-&amp;gt;AllocSize());

	// Reserve로 header가 들어갈 공간 미리 확보
	PacketHeader* header = bw.Reserve&amp;lt;PacketHeader&amp;gt;();

	// id(uint64), 체력(uint32), 공격력(uint16)
	bw &amp;lt;&amp;lt; id &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; attack;

	// 가변 데이터
	bw &amp;lt;&amp;lt; static_cast&amp;lt;uint16&amp;gt;(buffs.size());

	for (BuffData&amp;amp; buff : buffs)
	{
		bw &amp;lt;&amp;lt; buff.buffId &amp;lt;&amp;lt; buff.remainTime;
	}

	sendBuffer-&amp;gt;SetWriteSize(bw.WriteSize());

	header-&amp;gt;size = bw.WriteSize();
	header-&amp;gt;id = S_TEST;	 // 1: Test Msg

	return sendBuffer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;송신(Send) 로직&lt;/b&gt; (ServerPacketHandler::MAKE_S_TEST)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;헤더 예약(Reserve):&lt;/b&gt; Reserve를 통해 패킷의 맨 앞부분(PacketHeader) 공간을 미리 비워둠&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 삽입&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고정 데이터:&lt;/b&gt; id, hp, attack과 같은 고정 크기 자료형을 버퍼에 삽입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가변 데이터:&lt;/b&gt; 보낼 리스트(buffs)의 개수를 기록하고 for 루프를 돌며 각 요소의 상세 데이터(buffId, remainTime)를 순차적으로 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;헤더 완성:&lt;/b&gt; 최종적으로 기록된 전체 바이트 크기를 계산하여 아까 비워둔 헤더의 size 필드와 패킷 id를 채워 넣음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[ DummyClient ]&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ClientPacketHandler.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778310191548&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

/*------------------------
	ClientPacketHandler
------------------------*/

enum 
{ 
	S_TEST = 1
};

class ClientPacketHandler {
	
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static void Handle_S_TEST(BYTE* buffer, int32 len);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ClientPacketHandler.cpp&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778310224730&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;ClientPacketHandler.h&quot;
#include &quot;BufferReader.h&quot;

/*------------------------
	ClientPacketHandler
------------------------*/

void ClientPacketHandler::HandlePacket(BYTE* buffer, int32 len) {
	BufferReader br(buffer, len);

	PacketHeader header;
	br &amp;gt;&amp;gt; header;

	// switch case보다 효율적인 방법이 있음. Map?
	switch (header.id)
	{ 
		case S_TEST:
			Handle_S_TEST(buffer, len);
			break;
	}

	
}

// 패킷 설계 TEMP
struct BuffData
{
	uint64 buffId;
	float remainTime;
};

struct S_TEST
{
	uint64 id;
	uint32 hp;
	uint16 attack;
	// 가변 데이터
	// 1) 문자열 (ex. name)
	// 2) 그냥 바이트 배열 (ex. 길드 img)
	// 3) 일반적인 리스트
	vector&amp;lt;BuffData&amp;gt; buffs;
};

void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len) {
	BufferReader br(buffer, len);

	PacketHeader header;
	br &amp;gt;&amp;gt; header;

	uint64 id;
	uint32 hp;
	uint16 attack;
	br &amp;gt;&amp;gt; id &amp;gt;&amp;gt; hp &amp;gt;&amp;gt; attack;

	cout &amp;lt;&amp;lt; &quot;ID: &quot; &amp;lt;&amp;lt; id &amp;lt;&amp;lt; &quot; HP: &quot; &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; &quot; ATT: &quot; &amp;lt;&amp;lt; attack &amp;lt;&amp;lt; endl;

	vector&amp;lt;BuffData&amp;gt; buffs;
	uint16 buffSize;
	br &amp;gt;&amp;gt; buffSize;

	buffs.resize(buffSize);
	for (int i = 0; i &amp;lt; buffSize; i++) {
		br &amp;gt;&amp;gt; buffs[i].buffId &amp;gt;&amp;gt; buffs[i].remainTime;
	}

	cout &amp;lt;&amp;lt; &quot;BufSize: &quot; &amp;lt;&amp;lt; buffSize &amp;lt;&amp;lt; endl;
	for (int i = 0; i &amp;lt; buffSize; i++) {
		cout &amp;lt;&amp;lt; &quot;BuffInfo: &quot; &amp;lt;&amp;lt; buffs[i].buffId &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; buffs[i].remainTime &amp;lt;&amp;lt; endl;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수신 로직&lt;/b&gt; (ClientPacketHandler::Handle_S_TEST)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고정 데이터 추출:&lt;/b&gt; BufferReader를 이용해 헤더 이후의 고정 데이터(id, hp, attack)를 먼저 읽어옴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가변 데이터 복원:&lt;/b&gt; 버퍼에서 가변 데이터의 개수(buffSize)를 먼저 읽음&lt;/li&gt;
&lt;li&gt;루프를 돌며 버퍼의 데이터를 객체에 복원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BufferWriter.h / GameSession.h / GameSession.cpp / DummyClient.cpp / GameServer.cpp 수정&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1778312123802&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;PacketHandler &amp;middot; znfnfns0365/CPP_Server_Practice@e7a287d&quot; data-og-description=&quot;+ vector&amp;lt;BuffData&amp;gt; buffs{BuffData{100, 1.2f}, BuffData{200, 3.4}, BuffData{300, 1.9}};&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e7a287d69431aabab53f165efd9c2a55d4fc6be5#diff-ba2d3af3ee7ffeceef756ce7d7ac8d24d80a0026a9a70b7103d6d386c483dcb3L119-R123&quot; data-og-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e7a287d69431aabab53f165efd9c2a55d4fc6be5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BSEaF/dJMb82eRlXJ/UevySRd1yqFiLrSs0WdYy1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cV6Esk/dJMb87gaPCO/kAPm7Eb2HJD2MSHGWddHUK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e7a287d69431aabab53f165efd9c2a55d4fc6be5#diff-ba2d3af3ee7ffeceef756ce7d7ac8d24d80a0026a9a70b7103d6d386c483dcb3L119-R123&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e7a287d69431aabab53f165efd9c2a55d4fc6be5#diff-ba2d3af3ee7ffeceef756ce7d7ac8d24d80a0026a9a70b7103d6d386c483dcb3L119-R123&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BSEaF/dJMb82eRlXJ/UevySRd1yqFiLrSs0WdYy1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cV6Esk/dJMb87gaPCO/kAPm7Eb2HJD2MSHGWddHUK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PacketHandler &amp;middot; znfnfns0365/CPP_Server_Practice@e7a287d&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;+ vector&amp;lt;BuffData&amp;gt; buffs{BuffData{100, 1.2f}, BuffData{200, 3.4}, BuffData{300, 1.9}};&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BufferWriter.h:&lt;/b&gt; 오류 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameSession.h / GameSession.cpp:&lt;/b&gt; 서버에서 패킷 수신 시 ServerPacketHandler::HandlePacket() 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DummyClient.cpp:&lt;/b&gt; 클라이언트에서 패킷 수신 시 ClientPacketHandler::HandlePacket() 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameServer.cpp:&lt;/b&gt; ServerPacketHandler::Make_S_TEST()로 패킷 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTrDUb/dJMcahYEsmJ/cCVWYCPbYbBXfVOWMhBX30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTrDUb/dJMcahYEsmJ/cCVWYCPbYbBXfVOWMhBX30/img.png&quot; data-alt=&quot;Client / Server 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTrDUb/dJMcahYEsmJ/cCVWYCPbYbBXfVOWMhBX30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTrDUb%2FdJMcahYEsmJ%2FcCVWYCPbYbBXfVOWMhBX30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;580&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client / Server 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>C++ Server/Framework</category>
      <category>Packet Handler</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/267</guid>
      <comments>https://dh-0e.tistory.com/267#entry267comment</comments>
      <pubDate>Sat, 9 May 2026 17:51:39 +0900</pubDate>
    </item>
    <item>
      <title>[Cloud] Kubernetes Volumes III (Downward API, Job, CronJob, DaemonSet)</title>
      <link>https://dh-0e.tistory.com/266</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Downward API&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod가 자기 자신의 정보(이름, IP, label 등)를 ConfigMap이나 Secret에 미리 정의하지 않고도&amp;nbsp;&lt;b&gt;런타임에 가져올 수 있게 해주는 기능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 개념:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;k8s 마스터 서버를 통하지 않고, &lt;b&gt;환경 변수&lt;/b&gt;나 &lt;b&gt;볼륨 파일&lt;/b&gt;을 통해 &lt;b&gt;포드/컨테이너의 메타데이터를 주입&lt;/b&gt;하는 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환경 변수:&lt;/b&gt; 개별적인 값을 하나씩 변수에 할당할 때 사용 (가장 흔한 방식)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;볼륨 마운트:&lt;/b&gt; 정보를 파일 형태로 제공하여 label이나 annotation처럼 내용이 수시로 변할 수 있는 정보를 실시간으로 반영하기에 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;응용 프로그래머&lt;/b&gt;들이&lt;/span&gt;&amp;nbsp;자주 사용함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ex1)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Redis, MongoDB 같은 분산 시스템을 구축할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ex2)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모니터링을 위해 여러 Pod에서 로그를 수집할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ex3)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;JVM이나 Node.js에서 리소스 제한에 따른 동적 메모리 설정을 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU7ek2/dJMcad2UuuM/qX7ueLQAa4ZKRIdOokVTn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU7ek2/dJMcad2UuuM/qX7ueLQAa4ZKRIdOokVTn0/img.png&quot; data-alt=&quot;Downward API를 통해 전달할 수 있는 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU7ek2/dJMcad2UuuM/qX7ueLQAa4ZKRIdOokVTn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU7ek2%2FdJMcad2UuuM%2FqX7ueLQAa4ZKRIdOokVTn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1006&quot; height=&quot;519&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Downward API를 통해 전달할 수 있는 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;metadata.labels, metadata.annotations는 볼륨 파일로만 접근 가능한 이유&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;labels, annotations는 &lt;b&gt;Key-Value 쌍의 집합&lt;/b&gt;이기 때문에, 환경 변수 하나에 이 수많은 목록을 다 집어넣기 힘듦&lt;/li&gt;
&lt;li&gt;labels는&lt;b&gt; Pod가 실행 중에도 바뀔 수 있음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환경 변수:&lt;/b&gt; 한 번 실행되면, 프로세스를 재시작하지 않는 이상 바꿀 수 없음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;볼륨 파일:&lt;/b&gt; 쿠버네티스가 파일 내용을 실시간으로 업데이트해 줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Using Downward API with Volume Mount&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bt6MgG/dJMcabcYyAC/L8zkvCqkXfKVsUZWt3b0Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bt6MgG/dJMcabcYyAC/L8zkvCqkXfKVsUZWt3b0Y0/img.png&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;350&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.6161%; margin-right: 10px;&quot; data-widthpercent=&quot;NaN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bt6MgG/dJMcabcYyAC/L8zkvCqkXfKVsUZWt3b0Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbt6MgG%2FdJMcabcYyAC%2FL8zkvCqkXfKVsUZWt3b0Y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HNUly/dJMcabcYyAY/IOd7y1GapuNurHDcFNXuNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HNUly/dJMcabcYyAY/IOd7y1GapuNurHDcFNXuNk/img.png&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;375&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.2211%;&quot; data-widthpercent=&quot;NaN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HNUly/dJMcabcYyAY/IOd7y1GapuNurHDcFNXuNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHNUly%2FdJMcabcYyAY%2FIOd7y1GapuNurHDcFNXuNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;394&quot; height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Pod Field(only Volume Mount)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;labels, annotations로 metadata.labels, metadata.annotations 환경 변수로 설정&lt;/li&gt;
&lt;li&gt;&quot;kubectl exec -it 'pod 이름' -- /bin/bash&quot;로 Pod 안에 들어가서 Volume Mount 경로에 있는 파일 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Using Downward API with Environment Variables (1)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csp01S/dJMcabjOgmY/O8OCUpUZxGpn3wRPUYJ7Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csp01S/dJMcabjOgmY/O8OCUpUZxGpn3wRPUYJ7Fk/img.png&quot; data-alt=&quot;Pod Field(Both)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csp01S/dJMcabjOgmY/O8OCUpUZxGpn3wRPUYJ7Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcsp01S%2FdJMcabjOgmY%2FO8OCUpUZxGpn3wRPUYJ7Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;283&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Pod Field(Both)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt; spec.nodeName을 NODE_NAME에 저장하는 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;metadata.name, metadata.namespace, metadata.uid, spec.serviceAccountName, status.hostIP, status.podIP 들은 Volume mount로도 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&quot;kubectl exec -it 'pod 이름' -- env&quot;로 환경변수 확인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Using Downward API with Environment Variables (2)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EGMCG/dJMcaaFcsX6/K5DJyKN59VrAb78vfTQJ61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EGMCG/dJMcaaFcsX6/K5DJyKN59VrAb78vfTQJ61/img.png&quot; data-alt=&quot;Container Field(Both)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EGMCG/dJMcaaFcsX6/K5DJyKN59VrAb78vfTQJ61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEGMCG%2FdJMcaaFcsX6%2FK5DJyKN59VrAb78vfTQJ61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;466&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Container Field(Both)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너 필드 메타데이터(requests.cpu, requests.memory, limits.cpu)는 환경 변수, 볼륨 파일 모두 가능&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&quot;kubectl exec -it 'pod 이름' -- env&quot;로 환경변수 확인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Downward API는 Pod 내의 컨테이너가 자신이 실행 중인 K8s 환경을 스스로 인식할 수 있게 해주는 가장 가볍고 안전한 방법&lt;/b&gt;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Job, CronJob&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;필요 상황:&lt;/b&gt; Pod를 한 번만 돌리고 사라지게 하고 싶을 때 (ex. 백업 한 번 하고 잔여 파일 삭제)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Job:&lt;/b&gt; 일회성 작업을 수행하기 위한 리소스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CronJob:&lt;/b&gt; 주기적인 작업을 수행하기 위한 리소스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Job&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vgfHH/dJMcabYonyW/7g6vPltDuEnD5VIcFiXxRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vgfHH/dJMcabYonyW/7g6vPltDuEnD5VIcFiXxRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vgfHH/dJMcabYonyW/7g6vPltDuEnD5VIcFiXxRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvgfHH%2FdJMcabYonyW%2F7g6vPltDuEnD5VIcFiXxRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;521&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성공적인 실행 보장:&amp;nbsp;&lt;/b&gt;지정된 작업이 성공적으로 완료될 때까지 Pod를 재시작&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동시성 설정:&amp;nbsp;&lt;/b&gt;동시에 실행할 수 있는 Pod 수 조정 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완료 상태 관리:&amp;nbsp;&lt;/b&gt;작업 완료 후 상태 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 처리 Batch 작업&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;백업 및 복원 작업&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI/ML Training(학습) Task&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DB Migration&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VOWvL/dJMcaiQHyyZ/6VGMpYtDaueM3eKWETDW0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VOWvL/dJMcaiQHyyZ/6VGMpYtDaueM3eKWETDW0K/img.png&quot; data-alt=&quot;Job 구조 (시험 X)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VOWvL/dJMcaiQHyyZ/6VGMpYtDaueM3eKWETDW0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVOWvL%2FdJMcaiQHyyZ%2F6VGMpYtDaueM3eKWETDW0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1033&quot; height=&quot;445&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Job 구조 (시험 X)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;restartPolicy:&amp;nbsp;&lt;/b&gt;실패 시 컨테이너를 다시 살릴 것인가, 아니면 포드를 새로 만들 것인가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;OnFailure:&lt;/b&gt; 실패 시 재시작 (컨테이너 수준의 재시작)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Never:&lt;/b&gt; 실패 시 포드 새로 생성 (Pod 수준의 재시작)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;backoffLimit:&lt;/b&gt; Job 컨트롤러가 &quot;이 작업은 가망이 없다&quot;라고 판단하고 포기하기 전까지의 재시도 횟수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;restartPolicy: Never인 경우:&lt;/b&gt; 실패한 Pod의 개수가 backoffLimit에 도달하면 종료,&amp;nbsp;Job 실패 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;restartPolicy: OnFailure인 경우:&lt;/b&gt; 컨테이너의 재시작 횟수가 backoffLimit에 도달하면 해당 Pod를 종료, Job 실패 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CronJob&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CC6pZ/dJMcagk4oyb/IMqcZk22kijGfRx6TwkZXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CC6pZ/dJMcagk4oyb/IMqcZk22kijGfRx6TwkZXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CC6pZ/dJMcagk4oyb/IMqcZk22kijGfRx6TwkZXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCC6pZ%2FdJMcagk4oyb%2FIMqcZk22kijGfRx6TwkZXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;356&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Job 리소스에 스케줄을 추가하여 &lt;b&gt;주기적으로 실행되는 Job&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Linux의 crontab 기능과 유사하게 특정 시간 및 주기에 맞춰 작업 수행 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정기적인 데이터 백업&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그 파일 정리&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정해진 시간에 반복적으로 수행되는 작업&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지속적으로 주기적인 작업 실행&lt;/li&gt;
&lt;li&gt;일정에 맞게 Job을 생성하도록&lt;b&gt; 실행 주기&lt;/b&gt; 설정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6n7n/dJMcahj1YIA/pEWodzXICLKK9SqxQ6kIV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6n7n/dJMcahj1YIA/pEWodzXICLKK9SqxQ6kIV1/img.png&quot; data-alt=&quot;CronJob 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6n7n/dJMcahj1YIA/pEWodzXICLKK9SqxQ6kIV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6n7n%2FdJMcahj1YIA%2FpEWodzXICLKK9SqxQ6kIV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1049&quot; height=&quot;355&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CronJob 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Scheduling:&lt;/b&gt; * * * * * (분 시 일 월 요일) 형식을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Job &amp;amp; CronJob이 실패했을 때 어떻게 될 것인가?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DaemonSet&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 내의 모든(혹은 특정 조건에 맞는) &lt;b&gt;노드에 Pod를 딱 하나씩 배치&lt;/b&gt;하는 리소스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node당 딱 하나만 만들어서 백그라운드에서 돌면서 뭔가 해야 할 일이 주어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동작 방식&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 노드가 클러스터에 추가되면 DaemonSet Pod가 자동으로 그 노드에 생성됨&lt;/li&gt;
&lt;li&gt;노드가 삭제되면 해당 Pod는 &lt;b&gt;Garbage Collection&lt;/b&gt;으로 작동&lt;/li&gt;
&lt;li&gt;RollingUpdate를 통해 노드별로 순차적인 업데이트가 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 사례&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로그 수집기:&lt;/b&gt; 각 노드에서 발생하는 로그를 수집하여 중앙으로 전송&lt;/li&gt;
&lt;li&gt;&lt;b&gt;노드 모니터링:&lt;/b&gt; 각 노드의 리소스 상태를 감시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 플러그인:&lt;/b&gt; 쿠버네티스 네트워킹을 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Cloud/Kubernetes</category>
      <category>cronjob</category>
      <category>DaemonSet</category>
      <category>downward api</category>
      <category>Job</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/266</guid>
      <comments>https://dh-0e.tistory.com/266#entry266comment</comments>
      <pubDate>Fri, 8 May 2026 14:08:57 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Server] Buffer Helper (Reader, Writer)</title>
      <link>https://dh-0e.tistory.com/265</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Buffer Helper&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기존 방식:&lt;/b&gt; memcpy를 사용하여 구조체나 변수를 직접 버퍼에 복사
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가독성 저하:&lt;/b&gt; buffer + offset 계산이 반복되어 코드가 지저분해짐&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실수 가능성:&lt;/b&gt; 데이터 크기 계산을 Send 할 때마다 하기 때문에 계산 실수 발생 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가변 패킷 처리:&lt;/b&gt; 문자열이나 리스트 등 크기가 변하는 데이터를 다루기 까다로움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Buffer Helper:&lt;/b&gt; 이를 해결하기 위해&lt;b&gt; C++의 연산자 오버로딩&lt;/b&gt;과 &lt;b&gt;템플릿을 활용&lt;/b&gt;해 &lt;b&gt;Stream 방식&lt;/b&gt;으로 데이터를 밀어 넣고 빼낼 수 있는 클래스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;커서 관리의 자동화:&lt;/b&gt; _pos 멤버 변수를 통해 현재 읽기/쓰기 위치를 내부적으로 관리하므로, 개발자가 외부에서 인덱스를 관리할 필요가 없음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 메모리 접근:&lt;/b&gt; BYTE* 포인터를 직접 다루기 때문에 추가적인 복사 비용을 최소화하면서도 인터페이스는 추상화되어 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가변 데이터 대응:&lt;/b&gt; Write(void* src, uint32 len) 함수를 통해 구조체뿐만 아니라 일반적인 바이너리 데이터나 가변 길이 문자열도 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/153bh/dJMcahdgM09/QLPMBvXF0uUpfDYPBsb7Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/153bh/dJMcahdgM09/QLPMBvXF0uUpfDYPBsb7Ok/img.png&quot; data-alt=&quot;Before / After&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/153bh/dJMcahdgM09/QLPMBvXF0uUpfDYPBsb7Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F153bh%2FdJMcahdgM09%2FQLPMBvXF0uUpfDYPBsb7Ok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;617&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Before / After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BufferWriter&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 버퍼에 쓰는 역할로,&lt;b&gt; 직관적인 패킷 생성&lt;/b&gt;을 담당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;연산자 오버로딩(&amp;lt;&amp;lt;):&lt;/b&gt; &quot;bw &amp;lt;&amp;lt; id &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; attack;&quot;와 같이 cout을 쓰듯 직관적으로 데이터 삽입 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Reserve 패턴&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷의 총크기는 모든 데이터를 Write 해야 알 수 있는 경우가 많음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Reserve&amp;lt;PacketHeader&amp;gt;()&lt;/b&gt;를 통해 헤더 공간을 미리 비워두고, 데이터를 다 채운 뒤&lt;b&gt; 나중에 헤더 정보를 업데이트&lt;/b&gt;할 수 있게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;템플릿 활용:&lt;/b&gt; 임의의 타입(T)에 대해 크기를 자동으로 계산하여 복사함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BufferWrtier.h&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778206115519&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
/*-------------------------
	BufferWriter
-------------------------*/

class BufferWriter {
public:
	BufferWriter();
	BufferWriter(BYTE* buffer, uint32 size, uint32 pos = 0);
	~BufferWriter();

	BYTE* Buffer() { return _buffer; }
	uint32 Size() { return _size; }
	uint32 WriteSize() { return _pos; }
	uint32 FreeSize() { return _size - _pos; }

	template &amp;lt;typename T&amp;gt;
	bool Write(T* src) {
		return Write(src, sizeof(T));
	}
	bool Write(void* src, uint32 len);

	// packetHeader를 마지막에 채울 수 있기 때문에 이를 편하게 하기 위해
	template &amp;lt;typename T&amp;gt;
	T* Reserve();

	template &amp;lt;typename T&amp;gt;
	BufferWriter&amp;amp; operator&amp;lt;&amp;lt;(const T&amp;amp; src);

	template &amp;lt;typename T&amp;gt;
	BufferWriter&amp;amp; operator&amp;lt;&amp;lt;(T&amp;amp;&amp;amp; src);
	// &amp;lt;&amp;lt;로 데이터를 밀어넣는 연산자 만듬

private:
	BYTE* _buffer = nullptr;
	uint32 _size = 0;
	uint32 _pos = 0;
};

template &amp;lt;typename T&amp;gt;
T* BufferWriter::Reserve() {
	if (FreeSize() &amp;lt; sizeof(T))
		return nullptr;

	T* ret = reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]);
	_pos += sizeof(T);
	return ret;
}

template &amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(const T&amp;amp; src) {
	*reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]) = src;
	// ::memcpy 써도 됨
	_pos += sizeof(T);
	return *this;
}

template &amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(T&amp;amp;&amp;amp; src) {
	*reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]) = std::move(src);
	_pos += sizeof(T);
	return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BufferWrtier.cpp&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778206141428&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;BufferWriter.h&quot;

/*-------------------------
	BufferWriter
-------------------------*/

BufferWriter::BufferWriter() {}

BufferWriter::BufferWriter(BYTE* buffer, uint32 size, uint32 pos) : _buffer(buffer), _size(size), _pos(pos) {}

BufferWriter::~BufferWriter() {}

bool BufferWriter::Write(void* src, uint32 len) {
	if (FreeSize() &amp;lt; len)
		return false;

	::memcpy(&amp;amp;_buffer[_pos], src, len);
	_pos += len;
	return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BufferReader&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수신된 버퍼에서 데이터를 순차적으로 읽어와 &lt;b&gt;안전한 패킷 파싱&lt;/b&gt;을 담당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;연산자 오버로딩(&amp;gt;&amp;gt;):&lt;/b&gt; &quot;br &amp;gt;&amp;gt; header &amp;gt;&amp;gt; id;&quot;와 같이 cin을 쓰듯 직관적으로 데이터 파싱 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Peek 기능:&lt;/b&gt; 데이터를 읽어서 커서(pos)를 옮기지 않고 &lt;b&gt;내용만 확인&lt;/b&gt;하고 싶을 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BufferReader.h&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778206544013&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

/*-------------------------
	BufferReader
-------------------------*/

class BufferReader {
public:
	BufferReader();
	BufferReader(BYTE* buffer, uint32 size, uint32 pos = 0);
	~BufferReader();

	BYTE* Buffer() { return _buffer; }
	uint32 Size() { return _size; }
	uint32 ReadSize() { return _pos; }
	uint32 FreeSize() { return _size - _pos; }

	// 데이터를 옅보고 싶지만, 커서는 옮기고 싶지 않을 때
	template &amp;lt;typename T&amp;gt;
	bool Peek(T* dest) {
		return Peek(dest, sizeof(T));
	}
	bool Peek(void* dest, uint32 len);

	template &amp;lt;typename T&amp;gt;
	bool Read(T* dest) {
		return Read(dest, sizeof(T));
	}
	bool Read(void* dest, uint32 len);

	template &amp;lt;typename T&amp;gt;
	BufferReader&amp;amp; operator&amp;gt;&amp;gt;(OUT T&amp;amp; dest);
	// &amp;gt;&amp;gt;로 데이터를 꺼내쓰는 연산자 만듬

private:
	BYTE* _buffer = nullptr;
	uint32 _size = 0;
	uint32 _pos = 0;
};

template &amp;lt;typename T&amp;gt;
inline BufferReader&amp;amp; BufferReader::operator&amp;gt;&amp;gt;(OUT T&amp;amp; dest) {
	dest = *reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]);
	_pos += sizeof(T);
	return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BufferReader.cpp&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778206521456&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;BufferReader.h&quot;
#include &quot;BufferWriter.h&quot;

/*-------------------------
	BufferReader
-------------------------*/

BufferReader::BufferReader() {}

BufferReader::BufferReader(BYTE* buffer, uint32 size, uint32 pos) : _buffer(buffer), _size(size), _pos(pos) {}

BufferReader::~BufferReader() {}

bool BufferReader::Peek(void* dest, uint32 len) {
	if (FreeSize() &amp;lt; len)
		return false;

	::memcpy(dest, &amp;amp;_buffer[_pos], len);
	return true;
}

bool BufferReader::Read(void* dest, uint32 len) {
	if (Peek(dest, len) == false)
		return false;

	_pos += len;
	return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연산자 오버로딩에 사용된 &lt;b&gt;reinterpret_cast&lt;/b&gt;는 &lt;b&gt;memcpy&lt;/b&gt;로 바꿔도 상관없음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오히려 안정성과 정렬 문제 예방면에서 더 좋음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1778206857973&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 방식
dest = *reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]);

// memcpy 방식 (성능 차이 없음, 더 안전함)
::memcpy(&amp;amp;dest, &amp;amp;_buffer[_pos], sizeof(T));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SendBuffer.h /&amp;nbsp; SendBuffer.cpp / DummyClient.cpp / GameServer.cpp 수정&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1778206930162&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Buffer Helper(BufferReader, BufferWriter) &amp;middot; znfnfns0365/CPP_Server_Practice@e33f461&quot; data-og-description=&quot;+ BufferReader::BufferReader(BYTE* buffer, uint32 size, uint32 pos) : _buffer(buffer), _size(size), _pos(pos) {}&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e33f461c24843e60d7a06c3e558c86caa953157b#diff-addfad43d7054c578270047e94b7a1cecec94bd7ff585542a95ff8bd097d3067R1-R63&quot; data-og-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e33f461c24843e60d7a06c3e558c86caa953157b&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cphol7/dJMb8SpL9Rp/gq7CWDVuhEakkQBF8x0tKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dknQdw/dJMb8XkjAcY/7ivtmu74hTCMJH3XxigK2K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e33f461c24843e60d7a06c3e558c86caa953157b#diff-addfad43d7054c578270047e94b7a1cecec94bd7ff585542a95ff8bd097d3067R1-R63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/e33f461c24843e60d7a06c3e558c86caa953157b#diff-addfad43d7054c578270047e94b7a1cecec94bd7ff585542a95ff8bd097d3067R1-R63&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cphol7/dJMb8SpL9Rp/gq7CWDVuhEakkQBF8x0tKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dknQdw/dJMb8XkjAcY/7ivtmu74hTCMJH3XxigK2K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Buffer Helper(BufferReader, BufferWriter) &amp;middot; znfnfns0365/CPP_Server_Practice@e33f461&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;+ BufferReader::BufferReader(BYTE* buffer, uint32 size, uint32 pos) : _buffer(buffer), _size(size), _pos(pos) {}&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SendBuffer.h:&lt;/b&gt; AllocSize() 추가(SendBuffer Pooling 안 한 상태라 의미 없음), 자료형 uint32로 통일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SendBuffer.cpp:&lt;/b&gt; CopyData()의 기능을 BufferWriter에서 해주기 때문에 삭제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DummyClient.cpp:&lt;/b&gt; BufferReader로 패킷 파싱
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;header.size - sizeeof(PacketHeader) - 8 - 4 - 2:&lt;/b&gt; 가변 길이는 나중에 패킷 설계에서 설정해줘야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameServer.cpp:&lt;/b&gt; BufferWriter로 패킷 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Reserve&lt;/b&gt;로 packetHeader 공간 미리 확보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;sendBuffer&amp;rarr;SetWriteSize(bw.WriteSize())&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Session::RegisterSend()에서 &lt;b&gt;wsaBuf.len에 sendBuffer&amp;rarr;WriteSize()를 넣고 있기 때문&lt;/b&gt;에, &lt;br /&gt;bufferWriter의 &lt;b&gt;_pos&lt;/b&gt;를 sendBuffer의 &lt;b&gt;_writeSize&lt;/b&gt;에 넣어줘야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzIQgD/dJMcabqBT6h/d4wK0XCYjVtKwZCbGcPBlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzIQgD/dJMcabqBT6h/d4wK0XCYjVtKwZCbGcPBlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzIQgD/dJMcabqBT6h/d4wK0XCYjVtKwZCbGcPBlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzIQgD%2FdJMcabqBT6h%2Fd4wK0XCYjVtKwZCbGcPBlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;605&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>C++ Server/Framework</category>
      <category>Buffer Helper</category>
      <category>BufferReader</category>
      <category>BufferWriter</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/265</guid>
      <comments>https://dh-0e.tistory.com/265#entry265comment</comments>
      <pubDate>Fri, 8 May 2026 11:59:35 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Server] PacketSession</title>
      <link>https://dh-0e.tistory.com/264</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PacketSession&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Session 클래스(TCP 소켓의 기본 송수신을 담당)를 상속&lt;/b&gt;받아 &lt;b&gt;패킷 조립 기능&lt;/b&gt;을 얹은 클래스&lt;/li&gt;
&lt;li&gt;TCP 특성상 패킷이 모두 도착했다는 보장을 할 수가 없는 &lt;b&gt;TCP 데이터 경계 문제(TCP Boundary Problem)를 해결&lt;/b&gt;함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP는 스트림(Stream) 기반이라 데이터의 경계가 없음&lt;/li&gt;
&lt;li&gt;패킷이 쪼개져서 오기도 하고(Fragmentation), 뭉쳐서 오기도 함(Sticky Packets)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1778118587766&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct PacketHeader {
	uint16 size;
	uint16 id;
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;size(2Byte) + id(2Byte)로 구성된 헤더&lt;/b&gt;를 만듦
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;size:&lt;/b&gt; 패킷 전체 크기 (header + data)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;id:&lt;/b&gt; 프로토콜 ID (ex. 1=로그인, 2=회원가입)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OnRecv (final)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 I/O 스레드에서 실제 데이터를 수신했을 때 호출되는 함수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패킷 쪼개짐/뭉침 문제를 모두 처리&lt;/li&gt;
&lt;li&gt;받은 데이터 버퍼를 읽으면서 헤더의 size만큼 데이터가 다 모였는지 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다 모이지 않았다면 다음 수신을 기다림&lt;/li&gt;
&lt;li&gt;다 모였다면 온전한 하나의 패킷을 잘라내어 OnRecvPacket() 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;final:&lt;/b&gt; 하위 클래스에서 실수로 재정의하는 것을 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OnRecvPacket (abstract)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OnRecv에서 &lt;b&gt;온전한 1개의 패킷이 조립&lt;/b&gt;되었을 때마다 호출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스를 상속받을 실제 게임 세션에서 컨텐츠 로직을 구현하는 곳&lt;b&gt; (원래 OnRecv 역할)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;넘어온 데이터가 무조건 결함 없는 1개의 완벽한 패킷이기 때문에, 헤더의 id를&amp;nbsp; 읽고 컨텐츠 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GetPacketSessionRef&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티스레드 환경에서 세션 객체의 생명 주기를 안전하게 관리하기 위해 shared_from_this 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Session.h&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778119528381&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*--------------------
	PacketSession
--------------------*/
// TCP 특성상 패킷이 모두 도착했다는 보장을 할 수가 없어서 이를 처리하는 세션

struct PacketHeader {
	uint16 size;
	uint16 id;
};

// [size(2Byte)][id(2Byte)][data...]
// size: 패킷 전체 크기 (header + data)
// id: 프로토콜 ID (ex. 1=로그인, 2=회원가입)

class PacketSession : public Session {
public:
	PacketSession();
	virtual ~PacketSession();

	PacketSessionRef GetPacketSessionRef() { return static_pointer_cast&amp;lt;PacketSession&amp;gt;(shared_from_this()); }

protected:
	virtual int32 OnRecv(BYTE* buffer, int32 len) final;
	virtual int32 OnRecvPacket(BYTE* buffer, int32 len) abstract;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Session.cpp&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778119491259&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*--------------------
	PacketSession
--------------------*/

PacketSession::PacketSession() {}

PacketSession::~PacketSession() {}

int32 PacketSession::OnRecv(BYTE* buffer, int32 len) {
	int32 processLen = 0;
	while (true) {
		int32 dataSize = len - processLen;
		// 최소한 헤더는 파싱할 수 있는지
		if (dataSize &amp;lt; sizeof(PacketHeader))
			break;

		PacketHeader header = *(reinterpret_cast&amp;lt;PacketHeader*&amp;gt;(&amp;amp;buffer[processLen]));
		// 헤더에 기록된 패킷 크기를 파싱할 수 있는지
		if (dataSize &amp;lt; header.size)
			break;

		// 패킷 조립 성공
		OnRecvPacket(&amp;amp;buffer[processLen], header.size);

		processLen += header.size;
	}
	return processLen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DummyClient.cpp / GameServer.cpp / GameSession.h / GameSession.cpp / Types.h 수정&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1778119620981&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;PacketSession, Disconnect Logic Debug &amp;middot; znfnfns0365/CPP_Server_Practice@c305ec7&quot; data-og-description=&quot;+ MakeShared&amp;lt;ClientService&amp;gt;(NetAddress(L&amp;quot;127.0.0.1&amp;quot;, 7777), MakeShared&amp;lt;IocpCore&amp;gt;(), MakeShared&amp;lt;ServerSession&amp;gt;, 1000);&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/c305ec7186fbf338703e699c8b5525142ebdce2c&quot; data-og-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/c305ec7186fbf338703e699c8b5525142ebdce2c&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ckLsKY/dJMb8XR9NNo/eYuJ6RkkNO55vHdAKgHeZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dcBE32/dJMb8Rj52Wg/LpwUQhqOAo7U4vlB8kN9C0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/c305ec7186fbf338703e699c8b5525142ebdce2c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/znfnfns0365/CPP_Server_Practice/commit/c305ec7186fbf338703e699c8b5525142ebdce2c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ckLsKY/dJMb8XR9NNo/eYuJ6RkkNO55vHdAKgHeZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dcBE32/dJMb8Rj52Wg/LpwUQhqOAo7U4vlB8kN9C0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PacketSession, Disconnect Logic Debug &amp;middot; znfnfns0365/CPP_Server_Practice@c305ec7&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;+ MakeShared&amp;lt;ClientService&amp;gt;(NetAddress(L&quot;127.0.0.1&quot;, 7777), MakeShared&amp;lt;IocpCore&amp;gt;(), MakeShared&amp;lt;ServerSession&amp;gt;, 1000);&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DummyClient.cpp&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OnRecv를 OnRecvPacket으로 바꾸고, OnRecv에서 받은 패킷을 복사, 출력&lt;/li&gt;
&lt;li&gt;클라이언트 1000개를 만들어 서버 연결 시도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameServer.cpp / GameSession.cpp:&lt;/b&gt; Broadcast를 GameServer에서 주기적으로(250ms) 보내도록 수정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameSession.h:&lt;/b&gt; GameSession이 PacketSession을 상속, OnRecv가 아닌 OnRecvPacket을 재정의 하도록 수정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Types.h:&lt;/b&gt; PacketSessionRef 선언&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Session.cpp 수정 (Line 43~43 &amp;rarr; Line 230~232)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원래 코드에서 Client를 1000개로 늘려서 실행 중 Client를 접속 해제(종료)하면 CRASH 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FNJIK/dJMcad2V772/E2K1Db7xegpuvnLl8Kx45K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FNJIK/dJMcad2V772/E2K1Db7xegpuvnLl8Kx45K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FNJIK/dJMcad2V772/E2K1Db7xegpuvnLl8Kx45K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFNJIK%2FdJMcad2V772%2FE2K1Db7xegpuvnLl8Kx45K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1338&quot; height=&quot;639&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이유:&lt;/b&gt; 원래 코드의 로직으로 GameSession::OnRecvPacket에서 Send 처리 중 클라이언트가 접속 해제 시
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt; Session::RegisterSend()&lt;/b&gt;에서 WSASend 중 HandleError 발생&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Session::HandleError()&lt;/b&gt;에서 Disconnect(L&quot;HandleError&quot;); 호출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Session::Disconnect()&lt;/b&gt;에서 OnDisconnected() 호출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameSession::OnDisconnected()&lt;/b&gt;에서 세션 삭제
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Send 처리 중 세션을 삭제한다는 뜻 &amp;rarr; &lt;b&gt;CRASH (Dangling Pointer) 발생&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w8q1h/dJMcac33Hr5/8GmtASvqf85aO2KiOoPvOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w8q1h/dJMcac33Hr5/8GmtASvqf85aO2KiOoPvOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w8q1h/dJMcac33Hr5/8GmtASvqf85aO2KiOoPvOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw8q1h%2FdJMcac33Hr5%2F8GmtASvqf85aO2KiOoPvOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1502&quot; height=&quot;313&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같이 &lt;b&gt;OnDisconnected() 호출 부분&lt;/b&gt;을&lt;b&gt; ProcessDisconnect()로 옮겨서&lt;/b&gt; GameSession이 소멸된 이후에 세션을 삭제하게끔 만들어줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;기존 (위험):&lt;/b&gt; Send 하다가 에러 발생 &amp;rarr; 그 즉시 ReleaseSession 호출 &amp;rarr; 메모리 해제&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;수정 (안전):&lt;/b&gt; 에러 발생 &amp;rarr; RegisterDisconnect (끊어달라고 통보만 함) &amp;rarr; 나중에 IOCP가 완료 신호를 주면 ProcessDisconnect에서 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vdhya/dJMcabD6K1U/FLJgjgQTcizvU4P4p6Nsj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vdhya/dJMcabD6K1U/FLJgjgQTcizvU4P4p6Nsj0/img.png&quot; data-alt=&quot;클라이언트 세션 1000개가 잘 소멸됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vdhya/dJMcabD6K1U/FLJgjgQTcizvU4P4p6Nsj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvdhya%2FdJMcabD6K1U%2FFLJgjgQTcizvU4P4p6Nsj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;612&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라이언트 세션 1000개가 잘 소멸됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>C++ Server/Framework</category>
      <category>Dangling Pointer</category>
      <category>packetSession</category>
      <author>dh_0e</author>
      <guid isPermaLink="true">https://dh-0e.tistory.com/264</guid>
      <comments>https://dh-0e.tistory.com/264#entry264comment</comments>
      <pubDate>Thu, 7 May 2026 11:37:28 +0900</pubDate>
    </item>
  </channel>
</rss>