<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>넷오빠</title>
    <link>https://netoppa.tistory.com/</link>
    <description>넷옵스 아키텍터를 꿈꾸는 네트워크 엔지니어입니다. 
MSA Devops와 Network 인프라 기술 블로그입니다. 
</description>
    <language>ko</language>
    <pubDate>Tue, 16 Jun 2026 12:40:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>넷오빠</managingEditor>
    <image>
      <title>넷오빠</title>
      <url>https://tistory1.daumcdn.net/tistory/7882478/attach/0bf15438dbf9465688c9f386a8194b3f</url>
      <link>https://netoppa.tistory.com</link>
    </image>
    <item>
      <title>[7주차] RKE2 &amp;amp; Cluster API</title>
      <link>https://netoppa.tistory.com/54</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 포스팅은 Cloud@net 가시다님의 스터디 강의 내용을 요약한 자료입니다.&lt;br /&gt;무단 베포를 금지합니다.&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;&lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt;&amp;nbsp;RKE2 소개&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소개 : Rancher 에서 개발한 엔터프라이즈 K8S 배포판, 미국 연방 정부 부문의 보안 및 규정 준수 - &lt;a href=&quot;https://docs.rke2.io/&quot;&gt;Docs&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영자의 개입을 최소화하면서&amp;nbsp;클러스터가 CIS Kubernetes Benchmark&amp;nbsp;&lt;a href=&quot;https://docs.rke2.io/security/cis_self_assessment17&quot;&gt;v1.7&lt;/a&gt;&amp;nbsp;또는&amp;nbsp;&lt;a href=&quot;https://docs.rke2.io/security/cis_self_assessment18&quot;&gt;v1.8 을 통과할 수 있도록&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;https://docs.rke2.io/security/hardening_guide&quot;&gt;기본 설정 및 구성 옵션을&lt;/a&gt; 제공합니다 .
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.rke2.io/security/fips_support&quot;&gt;FIPS 140-2 규정 준수를&lt;/a&gt;&amp;nbsp;지원합니다.&lt;/li&gt;
&lt;li&gt;빌드 파이프라인에서&amp;nbsp;&lt;a href=&quot;https://github.com/aquasecurity/trivy&quot;&gt;trivy를&lt;/a&gt; 사용하여 구성 요소의 CVE를 정기적으로 검사합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RKE2는 컨트롤 플레인 구성 요소를 kubelet이 관리하는 정적 Pod로 실행합니다. 내장 컨테이너 런타임은 containerd입니다.&lt;/li&gt;
&lt;li&gt;이 배포판은 독립 실행형으로 실행되거나 &lt;a href=&quot;https://www.rancher.com/&quot;&gt;Rancher&lt;/a&gt;에 통합되어 실행될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 : &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.rke2.io/architecture&quot;&gt;https://docs.rke2.io/architecture&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771762398129&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Anatomy of a Next Generation Kubernetes Distribution | RKE2&quot; data-og-description=&quot;Architecture Overview&quot; data-og-host=&quot;docs.rke2.io&quot; data-og-source-url=&quot;https://docs.rke2.io/architecture&quot; data-og-url=&quot;https://docs.rke2.io/architecture&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/blnMua/dJMb87NTa70/xtT30fViPcLKUmWkO8SpXK/img.png?width=1016&amp;amp;height=371&amp;amp;face=0_0_1016_371&quot;&gt;&lt;a href=&quot;https://docs.rke2.io/architecture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.rke2.io/architecture&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/blnMua/dJMb87NTa70/xtT30fViPcLKUmWkO8SpXK/img.png?width=1016&amp;amp;height=371&amp;amp;face=0_0_1016_371');&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;Anatomy of a Next Generation Kubernetes Distribution | RKE2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Architecture Overview&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.rke2.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baekBk/dJMcadntkAS/u6L9P4Ru9Pfmpu5lmGokgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baekBk/dJMcadntkAS/u6L9P4Ru9Pfmpu5lmGokgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baekBk/dJMcadntkAS/u6L9P4Ru9Pfmpu5lmGokgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaekBk%2FdJMcadntkAS%2Fu6L9P4Ru9Pfmpu5lmGokgK%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;1016&quot; height=&quot;371&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;371&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/&quot;&gt;RKE2는 경량 Kubernetes&lt;/a&gt;&amp;nbsp;배포판인&amp;nbsp;&lt;a href=&quot;https://k3s.io/&quot;&gt;K3s를&lt;/a&gt;&amp;nbsp;개발하고 유지 관리하면서 얻은 교훈을 바탕으로,&amp;nbsp;&lt;a href=&quot;https://k3s.io/&quot;&gt;K3s&lt;/a&gt;의 사용 편의성을 유지하면서도 엔터프라이즈 환경에 적합한 배포판을 구축한 제품입니다.&lt;/li&gt;
&lt;li&gt;즉, RKE2는 가장 간단하게 말하면&amp;nbsp;&lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt;&amp;nbsp;클러스터에 참여할 모든 노드에 설치 및 구성해야 하는 &lt;b&gt;단일 바이너리 파일&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;일단 시작되면 RKE2는 각 &lt;b&gt;노드의 역할&lt;/b&gt;에 맞는 &lt;b&gt;에이전트를 부트스트랩&lt;/b&gt;하고 관리하는 동시에 네트워크에서 &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;&lt;span style=&quot;background-color: #9feec3;&quot; data-token-index=&quot;0&quot;&gt;&amp;nbsp;RKE2 구성요소&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://k3s.io/&quot;&gt;K3S&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/k3s-io/helm-controller&quot;&gt;Helm Controller&lt;/a&gt; : &lt;b&gt;GitOps 스타일 경량 배포기,&lt;/b&gt; A simple way to manage helm charts with Custom Resource Definitions in k8s - &lt;a href=&quot;https://docs.rke2.io/add-ons/helm&quot;&gt;Docs&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RKE2 클러스터가 올라올 때 필수 Helm 차트를 자동으로 설치&amp;middot;유지하는 내장 배포 컨트롤러.&lt;/li&gt;
&lt;li&gt;manifests 디렉토리 감시 방식 : /var/lib/rancher/rke2/server/manifests 디렉토리에 (HelmChart) YAML 파일만 떨어뜨리면 Helm Controller에 의해 자동으로 Helm chart를 (다운로드)/설치/업데이트.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771762488758&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tree /var/lib/rancher/rke2/server/manifests
├── rke2-canal-config.yaml
├── rke2-canal.yaml
├── rke2-coredns-config.yaml
├── rke2-coredns.yaml
├── rke2-metrics-server.yaml
└── rke2-runtimeclasses.yaml&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;용도 비교 : Helm Ctrl &lt;span data-token-index=&quot;1&quot;&gt;부트스트랩 &lt;/span&gt;(부팅 시 &lt;span data-token-index=&quot;3&quot;&gt;필수 애드온&lt;/span&gt; 자동 설치) vs Argo CD &lt;span data-token-index=&quot;5&quot;&gt;GitOps &lt;/span&gt;(&lt;span data-token-index=&quot;7&quot;&gt;애플리케이션&lt;/span&gt; 배포).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Kubernetes 구성요소 기능 요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;402&quot; data-start=&quot;364&quot; data-ke-size=&quot;size23&quot;&gt;1-1. API Server (kube-apiserver)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;604&quot; data-start=&quot;403&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;433&quot; data-start=&quot;403&quot;&gt;&lt;b&gt;쿠버네티스의 관문(Front Door)&lt;/b&gt; 역할&lt;/li&gt;
&lt;li data-end=&quot;501&quot; data-start=&quot;434&quot;&gt;kubectl, Controller, Scheduler 등 &lt;b&gt;모든 컴포넌트가 API Server를 통해 통신&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;542&quot; data-start=&quot;502&quot;&gt;클러스터 상태 조회/변경 요청을 받아서 검증 후 저장(etcd 반영)&lt;/li&gt;
&lt;li data-end=&quot;604&quot; data-start=&quot;543&quot;&gt;인증(Authentication), 인가(Authorization), Admission(정책 적용)도 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;699&quot; data-start=&quot;644&quot; data-ke-size=&quot;size23&quot;&gt;1-2. Controller Manager (kube-controller-manager)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;921&quot; data-start=&quot;700&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;700&quot;&gt;클러스터가 &lt;b&gt;원하는 상태(Desired State)&lt;/b&gt; 를 유지하도록 계속 감시/조정&lt;/li&gt;
&lt;li data-end=&quot;892&quot; data-start=&quot;751&quot;&gt;예:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;892&quot; data-start=&quot;758&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;793&quot; data-start=&quot;758&quot;&gt;Deployment Controller &amp;rarr; 파드 개수 맞추기&lt;/li&gt;
&lt;li data-end=&quot;824&quot; data-start=&quot;796&quot;&gt;Node Controller &amp;rarr; 노드 상태 확인&lt;/li&gt;
&lt;li data-end=&quot;859&quot; data-start=&quot;827&quot;&gt;ReplicaSet Controller &amp;rarr; 복제본 유지&lt;/li&gt;
&lt;li data-end=&quot;892&quot; data-start=&quot;862&quot;&gt;Job Controller &amp;rarr; 배치 작업 완료 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;999&quot; data-start=&quot;962&quot; data-ke-size=&quot;size23&quot;&gt;1-3. Scheduler (kube-scheduler)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1150&quot; data-start=&quot;1000&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1032&quot; data-start=&quot;1000&quot;&gt;새로 생성될 Pod를 &lt;b&gt;어느 노드에 배치할지 결정&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1103&quot; data-start=&quot;1033&quot;&gt;CPU/메모리 여유, taint/toleration, affinity/anti-affinity, topology 등을 고려&lt;/li&gt;
&lt;li data-end=&quot;1150&quot; data-start=&quot;1104&quot;&gt;실제 실행은 kubelet이 하고, scheduler는 &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;h3 data-end=&quot;1214&quot; data-start=&quot;1185&quot; data-ke-size=&quot;size23&quot;&gt;1-4. Proxy (kube-proxy)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1349&quot; data-start=&quot;1215&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1243&quot; data-start=&quot;1215&quot;&gt;각 노드에서 &lt;b&gt;Service 네트워킹&lt;/b&gt; 구현&lt;/li&gt;
&lt;li data-end=&quot;1292&quot; data-start=&quot;1244&quot;&gt;Service IP(ClusterIP)로 들어온 트래픽을 &lt;b&gt;실제 Pod로 전달&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1326&quot; data-start=&quot;1293&quot;&gt;보통 iptables 또는 ipvs 기반 룰 생성&lt;/li&gt;
&lt;li data-end=&quot;1349&quot; data-start=&quot;1327&quot;&gt;로드밸런싱(간단한 분산) 역할도 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1421&quot; data-start=&quot;1393&quot; data-ke-size=&quot;size23&quot;&gt;1-5. Kubelet (kubelet)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1579&quot; data-start=&quot;1422&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1445&quot; data-start=&quot;1422&quot;&gt;노드 에이전트 (각 워커 노드에 실행)&lt;/li&gt;
&lt;li data-end=&quot;1498&quot; data-start=&quot;1446&quot;&gt;API Server에서 할당된 PodSpec을 보고 &lt;b&gt;실제로 컨테이너 실행 상태 유지&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1554&quot; data-start=&quot;1499&quot;&gt;container runtime(containerd 등)와 연동하여 컨테이너 생성/삭제/헬스체크&lt;/li&gt;
&lt;li data-end=&quot;1579&quot; data-start=&quot;1555&quot;&gt;Pod 상태를 API Server에 보고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1636&quot; data-start=&quot;1626&quot; data-ke-size=&quot;size26&quot;&gt;1-6. etcd&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1790&quot; data-start=&quot;1637&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1666&quot; data-start=&quot;1637&quot;&gt;쿠버네티스의 &lt;b&gt;분산 Key-Value 저장소&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1708&quot; data-start=&quot;1667&quot;&gt;클러스터의 모든 상태 정보(리소스 정의, 설정, 메타데이터 등)를 저장&lt;/li&gt;
&lt;li data-end=&quot;1754&quot; data-start=&quot;1709&quot;&gt;Control Plane의 &amp;ldquo;단일 진실 공급원(Source of Truth)&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;1790&quot; data-start=&quot;1755&quot;&gt;고가용성(HA) 구성 시 매우 중요하며 백업/복구 전략 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1839&quot; data-start=&quot;1829&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;1839&quot; data-start=&quot;1829&quot; data-ke-size=&quot;size26&quot;&gt;1-7. runc&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1987&quot; data-start=&quot;1840&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1896&quot; data-start=&quot;1840&quot;&gt;OCI(Open Container Initiative) 표준 기반의 &lt;b&gt;저수준 컨테이너 실행기&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1946&quot; data-start=&quot;1897&quot;&gt;실제로는 컨테이너 프로세스를 Linux namespace/cgroup으로 격리해 실행&lt;/li&gt;
&lt;li data-end=&quot;1987&quot; data-start=&quot;1947&quot;&gt;사용자가 직접 다루기보다는 보통 containerd가 내부적으로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;2059&quot; data-start=&quot;2037&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;2059&quot; data-start=&quot;2037&quot; data-ke-size=&quot;size26&quot;&gt;1-8. containerd / CRI&lt;/h2&gt;
&lt;h3 data-end=&quot;2074&quot; data-start=&quot;2060&quot; data-ke-size=&quot;size23&quot;&gt;containerd&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2158&quot; data-start=&quot;2075&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2089&quot; data-start=&quot;2075&quot;&gt;고수준 컨테이너 런타임&lt;/li&gt;
&lt;li data-end=&quot;2127&quot; data-start=&quot;2090&quot;&gt;이미지 pull, 스냅샷, 컨테이너 lifecycle 관리 담당&lt;/li&gt;
&lt;li data-end=&quot;2158&quot; data-start=&quot;2128&quot;&gt;내부적으로 runc를 사용해 실제 컨테이너 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2197&quot; data-start=&quot;2160&quot; data-ke-size=&quot;size23&quot;&gt;1-9. CRI (Container Runtime Interface)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2295&quot; data-start=&quot;2198&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2232&quot; data-start=&quot;2198&quot;&gt;쿠버네티스가 런타임과 통신하기 위한 &lt;b&gt;표준 인터페이스&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2273&quot; data-start=&quot;2233&quot;&gt;kubelet은 CRI를 통해 containerd 같은 런타임을 제어&lt;/li&gt;
&lt;li data-end=&quot;2295&quot; data-start=&quot;2274&quot;&gt;덕분에 런타임 교체/호환성이 좋아짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2420&quot; data-start=&quot;2381&quot; data-ke-size=&quot;size26&quot;&gt;1-10. CNI (Container Network Interface)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2541&quot; data-start=&quot;2421&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2452&quot; data-start=&quot;2421&quot;&gt;Pod 네트워크를 구성하기 위한 &lt;b&gt;플러그인 표준&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2497&quot; data-start=&quot;2453&quot;&gt;Pod에 IP 부여, 네트워크 연결, 라우팅/오버레이, 정책 연계 등을 담당&lt;/li&gt;
&lt;li data-end=&quot;2541&quot; data-start=&quot;2498&quot;&gt;&amp;ldquo;쿠버네티스 기본 기능&amp;rdquo;이 아니라 CNI 플러그인이 실제 네트워크를 구현함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3601&quot; data-start=&quot;3588&quot; data-ke-size=&quot;size26&quot;&gt;1-11. CoreDNS&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3726&quot; data-start=&quot;3602&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3618&quot; data-start=&quot;3602&quot;&gt;클러스터 내부 DNS 서버&lt;/li&gt;
&lt;li data-end=&quot;3679&quot; data-start=&quot;3619&quot;&gt;Service 이름(my-service.default.svc.cluster.local)을 IP로 변환&lt;/li&gt;
&lt;li data-end=&quot;3704&quot; data-start=&quot;3680&quot;&gt;Pod가 다른 서비스 찾을 때 핵심 역할&lt;/li&gt;
&lt;li data-end=&quot;3726&quot; data-start=&quot;3705&quot;&gt;필요 시 외부 DNS 포워딩도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3812&quot; data-start=&quot;3772&quot; data-ke-size=&quot;size26&quot;&gt;1-12. ngress NGINX Controller / Traefik&lt;/h2&gt;
&lt;p data-end=&quot;3880&quot; data-start=&quot;3813&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 &lt;b&gt;외부 HTTP/HTTPS 요청을 클러스터 내부 서비스로 라우팅&lt;/b&gt;하는 Ingress Controller입니다.&lt;/p&gt;
&lt;h3 data-end=&quot;3910&quot; data-start=&quot;3882&quot; data-ke-size=&quot;size23&quot;&gt;Ingress NGINX Controller&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3982&quot; data-start=&quot;3911&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3929&quot; data-start=&quot;3911&quot;&gt;NGINX 기반, 가장 보편적&lt;/li&gt;
&lt;li data-end=&quot;3951&quot; data-start=&quot;3930&quot;&gt;기능/레퍼런스 많고 운영 사례 풍부&lt;/li&gt;
&lt;li data-end=&quot;3982&quot; data-start=&quot;3952&quot;&gt;세밀한 튜닝과 annotation 기반 설정이 강점&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; &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;Process Lifecycle&lt;/span&gt;&lt;/b&gt;&lt;/h2&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;&lt;span data-token-index=&quot;0&quot;&gt;Content Boostrap&lt;/span&gt; &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;RKE2 런타임 이미지로 부터 sources binaries and manifests 추출하여, 서버 노드와 에이전트 노드를 모두 실행.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(참고) rke2-runtime 컨테이너 내부에 파일 확인 : 바이너리, HelmChart&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771763217558&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;crictl images
IMAGE                                           TAG                            IMAGE ID            SIZE
docker.io/rancher/rke2-runtime                  v1.34.3-rke2r3                 30afde048693a       91.6MB

tree
├── bin
│   ├── containerd
│   ├── containerd-shim-runc-v2
│   ├── crictl
│   ├── ctr
│   ├── kubectl
│   ├── kubelet
│   └── runc
├── charts
│   ├── harvester-cloud-provider.yaml
│   ├── harvester-csi-driver.yaml
│   ├── rancher-vsphere-cpi.yaml
│   ├── rancher-vsphere-csi.yaml
│   ├── rke2-calico-crd.yaml
│   ├── rke2-calico.yaml
│   ├── rke2-canal.yaml
│   ├── rke2-cilium.yaml
│   ├── rke2-coredns.yaml
│   ├── rke2-flannel.yaml
│   ├── rke2-ingress-nginx.yaml
│   ├── rke2-metrics-server.yaml
│   ├── rke2-multus.yaml
│   ├── rke2-runtimeclasses.yaml
│   ├── rke2-snapshot-controller-crd.yaml
│   ├── rke2-snapshot-controller.yaml
│   ├── rke2-snapshot-validation-webhook.yaml
│   ├── rke2-traefik-crd.yaml
│   └── rke2-traefik.yaml&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RKE2 런타임 이미지는 /var/lib/rancher/rke2/agent/images/*.tar 에서 스캔하며(태그는 rke2 --version 출력), 이를 찾을 수 없는 경우 DockerHub 에서 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (참고) /var/lib/rancher/rke2/agent/images/ 에 runtime-image.txt 파일 확인&lt;/p&gt;
&lt;pre id=&quot;code_1771763331338&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tree /var/lib/rancher/rke2/agent/images/
├── etcd-image.txt
├── kube-apiserver-image.txt
├── kube-controller-manager-image.txt
├── kube-proxy-image.txt
├── kube-scheduler-image.txt
└── runtime-image.txt

cat /var/lib/rancher/rke2/agent/images/runtime-image.txt 
index.docker.io/rancher/rke2-runtime:v1.34.3-rke2r3&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;그런 다음 RKE2는 런타임 이미지에서 /bin/을 추출하여 /var/lib/rancher/rke2/data/${RKE2_DATA_KEY}/bin 으로 평탄화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 ${RKE2_DATA_KEY}는 이미지를 식별(버전)하는 고유 문자열을 나타냅니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&amp;nbsp;/var/lib/rancher/rke2/data/&amp;nbsp;확인&lt;/p&gt;
&lt;pre id=&quot;code_1771763348980&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tree /var/lib/rancher/rke2/data/
└── v1.34.3-rke2r3-5b8349de68df
    ├── bin
    │   ├── containerd
    │   ├── containerd-shim-runc-v2
    │   ├── crictl
    │   ├── ctr
    │   ├── kubectl
    │   ├── kubelet
    │   └── runc
    └── charts
        ├── harvester-cloud-provider.yaml
        ├── harvester-csi-driver.yaml
        ├── rancher-vsphere-cpi.yaml
        ├── rancher-vsphere-csi.yaml
        ├── rke2-calico-crd.yaml
        ├── rke2-calico.yaml
        ├── rke2-canal.yaml
        ├── rke2-cilium.yaml
        ├── rke2-coredns.yaml
        ├── rke2-flannel.yaml
        ├── rke2-ingress-nginx.yaml
        ├── rke2-metrics-server.yaml
        ├── rke2-multus.yaml
        ├── rke2-runtimeclasses.yaml
        ├── rke2-snapshot-controller-crd.yaml
        ├── rke2-snapshot-controller.yaml
        ├── rke2-snapshot-validation-webhook.yaml
        ├── rke2-traefik-crd.yaml
        └── rke2-traefik.yaml&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RKE2가 예상대로 작동하려면 런타임 이미지는 최소한 다음을 제공해야 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;containerd (&lt;/b&gt;&lt;a href=&quot;https://github.com/kubernetes/cri-api&quot;&gt;CRI&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;containerd-shim&lt;/b&gt; (shims wrap&amp;nbsp;runc&amp;nbsp;tasks and do not stop when&amp;nbsp;containerd&amp;nbsp;does)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;containerd-shim-runc-v1&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;containerd-shim-runc-v2&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubelet&lt;/b&gt; (쿠버네티스 노드 에이전트)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;runc&lt;/b&gt; (OCI 런타임)&lt;/li&gt;
&lt;/ul&gt;
&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;**ctr**containerd (저수준 유지보수 및 점검)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;crictl&lt;/b&gt; (낮은 수준의 CRI 유지 관리 및 점검)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubectl&lt;/b&gt; (쿠버네티스 클러스터 유지 관리 및 점검)&lt;/li&gt;
&lt;li&gt;**socat**containerd ( 포트 포워딩&amp;nbsp;에 필요함 )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;바이너리 파일 압축 해제가 완료되면 RKE2는 이미지에서 차트를 추출하여 해당&amp;nbsp;/var/lib/rancher/rke2/server/manifests디렉터리에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1771763393017&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tree /var/lib/rancher/rke2/server/manifests
├── rke2-canal-config.yaml
├── rke2-canal.yaml
├── rke2-coredns-config.yaml
├── rke2-coredns.yaml
├── rke2-metrics-server.yaml
└── rke2-runtimeclasses.yaml&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;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; &lt;span data-token-index=&quot;0&quot;&gt;Initialize Server&lt;/span&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베디드 K3s 엔진 서버에는 특수 에이전트 프로세스가 포함되어 있어 노드 컨테이너 런타임 &lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;1&quot;&gt;node container runtime&lt;/span&gt; 이 시작될 때까지 다음 시작이 연기됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고)&amp;nbsp;/var/lib/rancher/rke2/agent/pod-manifests/&amp;nbsp;static&amp;nbsp;pod&lt;/p&gt;
&lt;pre id=&quot;code_1771763432165&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls -ltr /var/lib/rancher/rke2/agent/pod-manifests/
total 32
-rw-r--r--. 1 root root 3279 Feb 14 16:32 etcd.yaml
-rw-r--r--. 1 root root 2325 Feb 14 16:32 kube-proxy.yaml
-rw-r--r--. 1 root root 9337 Feb 14 16:33 kube-apiserver.yaml
-rw-r--r--. 1 root root 3724 Feb 14 16:33 kube-scheduler.yaml
-rw-r--r--. 1 root root 6325 Feb 14 16:33 kube-controller-manager.yaml&lt;/code&gt;&lt;/pre&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;Prepare Components&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;kube-apiserver
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kube-apiserver 이미지가 아직 존재하지 않는 경우, Goroutine을 회전시켜 &lt;b&gt;etcd&lt;/b&gt;를 기다린 다음 /var/lib/rancher/rke2/agent/pod-manifests/에 정적 포드 정의 static pod definition 를 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube-controller-manager
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-controller-manager 이미지가 아직 존재하지 않는 경우, goroutine을 회전시켜 &lt;b&gt;kube-apiserver&lt;/b&gt;를 기다린 다음 /var/lib/rancher/rke2/agent/pod-manifests/에 정적 포드 정의를 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube-scheduler
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-scheduler 이미지가 아직 없는 경우, 고루틴을 회전시켜 kube-apiserver를 기다린 다음 /var/lib/rancher/rke2/agent/pod-manifests/에 정적 포드 정의를 작성합니다.&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;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; &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;Start Cluster&lt;/span&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spin up an &lt;b&gt;HTTP server&lt;/b&gt; in a goroutine to &lt;b&gt;listen&lt;/b&gt; for other cluster &lt;b&gt;servers/agents&lt;/b&gt; then &lt;b&gt;initialize/join the cluster.&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;etcd
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 존재하지 않는 경우 etcd 이미지를 가져와서 Goroutine을 회전시켜 &lt;b&gt;kubelet&lt;/b&gt;을 기다린 다음 /var/lib/rancher/rke2/agent/pod-manifests/에 정적 포드 정의를 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;helm-controller
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kube-apiserver&lt;/b&gt;가 준비될 때까지 기다린 후 Goroutine을 회전시켜 내장된 헬름 컨트롤러 embedded helm-controller 를 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; rke2-server 프로세스&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771763476698&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
pstree -al
  ├─rke2
  │   ├─containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
  │   │   └─12*[{containerd}]
  │   ├─kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins --file-check-frequency=5s --sync-frequency=30s --config-dir=/var/lib/rancher/rke2/agent/etc/kubelet.conf.d --containerd=/run/k3s/containerd/containerd.sock --hostname-override=k8s-node1 --kubeconfig=/var/lib/rancher/rke2/agent/kubelet.kubeconfig --node-ip=192.168.10.11 --node-labels= --read-only-port=0
  │   │   └─14*[{kubelet}]
  │   └─12*[{rke2}]

systemctl status rke2-server.service --no-pager
● rke2-server.service - Rancher Kubernetes Engine v2 (server)
     Loaded: loaded (/usr/lib/systemd/system/rke2-server.service; enabled; preset: disabled)
     Active: active (running) since Sat 2026-02-14 16:33:49 KST; 1h 37min ago
       Docs: https://github.com/rancher/rke2#readme
    Process: 6346 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 6347 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 6348 (rke2)
      Tasks: 146
     Memory: 1.1G
        CPU: 5min 15.217s
     CGroup: /system.slice/rke2-server.service
             ├─6348 &quot;/usr/bin/rke2 server&quot;
             ├─6367 containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
             ├─6426 kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins --file-check-frequency=5s --sync-frequency&amp;hellip;
             ├─6483 /var/lib/rancher/rke2/data/v1.34.3-rke2r3-5b8349de68df/bin/containerd-shim-runc-v2 -namespace k8s.io &amp;hellip;
             ├─6484 /var/lib/rancher/rke2/data/v1.34.3-rke2r3-5b8349de68df/bin/containerd-shim-runc-v2 -namespace k8s.io &amp;hellip;
             ...

# systemd unit 파일 확인
cat /usr/lib/systemd/system/rke2-server.service
[Unit]
Description=Rancher Kubernetes Engine v2 (server)
Documentation=https://github.com/rancher/rke2#readme
Wants=network-online.target
After=network-online.target
Conflicts=rke2-agent.service

[Install]
WantedBy=multi-user.target

[Service]
Type=notify
EnvironmentFile=-/etc/default/%N
EnvironmentFile=-/etc/sysconfig/%N
EnvironmentFile=-/usr/lib/systemd/system/%N.env
KillMode=process   # &quot;나만 죽이고, 내가 띄운 애들까지는 systemd가 건드리지 마&quot; - RKE2, containerd 같은 애들은 내부적으로 여러 자식 프로세스 shim, runc, container 프로세스를 관리함. 물론 완전 무중단은 아니지만, 충격 최소화
Delegate=yes       # &quot;cgroup(리소스 제어)는 내가 직접 관리할게&quot; - cgroup(리눅스 리소스 그룹) 관리를 systemd가 아니라 자기 자신이 직접 하도록 위임(delegate) 하겠다는 의미.
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/rke2 server
ExecStopPost=-/bin/sh -c &quot;systemd-cgls /system.slice/%n | grep -Eo '[0-9]+ (containerd|kubelet)' | awk '{print $1}' | xargs -r kill&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;Initialize Agent&lt;/span&gt; &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;kubelet : Spawn and supervise the kubelet process.&lt;/li&gt;
&lt;li&gt;kubelet이 종료되면 rke2가 재시작을 시도합니다. kubelet이 작동하면 사용 가능한 모든 정적 포드가 시작됩니다.&lt;/li&gt;
&lt;li&gt;서버의 경우, etcd와 kube-apiserver가 연속적으로 시작되어 정적 포드를 통해 시작된 나머지 구성 요소들이 kube-apiserver에 연결되어 처리를 시작할 수 있게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Server Charts&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;On server nodes, the&amp;nbsp;helm-controller&amp;nbsp;can now apply to the cluster any charts found&amp;nbsp;/var/lib/rancher/rke2/server/manifests.&lt;/li&gt;
&lt;li&gt;rke2*-**canal.&lt;b&gt;yaml or rke2-&lt;b&gt;cilium&lt;/b&gt;.yaml or rke2-&lt;b&gt;calico&lt;/b&gt;.yaml or rke2-f&lt;/b&gt;lannel.**yaml or rke2-**multus.*yaml (daemonset, bootstrap)&lt;/li&gt;
&lt;li&gt;rke2-&lt;b&gt;coredns&lt;/b&gt;.yaml (deployment, bootstrap)&lt;/li&gt;
&lt;li&gt;rke2-ingress-nginx.yaml and/or rke2-traefik.yaml and rke2-traefik-crd.yaml (deployment)&lt;/li&gt;
&lt;li&gt;rke2-metrics-server.yaml (deployment)&lt;/li&gt;
&lt;li&gt;rke2-runtimeclasses.yaml (deployment)&lt;/li&gt;
&lt;li&gt;rke2-snapshot-controller-crd.yaml, rke2-snapshot-controller.yaml and rke2-snapshot-validation-webhook.yaml (deployment)&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;b&gt;Daemon Process&lt;/b&gt; : 이제 RKE2 프로세스는 SIGTERM 또는 SIGKILL을 받거나 컨테이너 프로세스가 종료될 때까지 무기한으로 실행됨.&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; 서버 노드 부팅 시 과정&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1787&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQfwS/dJMcahcmIq6/aoVOANLq76c7paKG9dPCB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQfwS/dJMcahcmIq6/aoVOANLq76c7paKG9dPCB0/img.png&quot; data-alt=&quot;출처 : https://sirzzang.github.io/kubernetes/Kubernetes-RKE2-00/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQfwS/dJMcahcmIq6/aoVOANLq76c7paKG9dPCB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQfwS%2FdJMcahcmIq6%2FaoVOANLq76c7paKG9dPCB0%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;2048&quot; height=&quot;1787&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1787&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://sirzzang.github.io/kubernetes/Kubernetes-RKE2-00/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 에이전트 노드 부팅 시 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GzPWY/dJMcachQnp8/f2XNmhCaGVm8KP4FrZnY5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GzPWY/dJMcachQnp8/f2XNmhCaGVm8KP4FrZnY5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GzPWY/dJMcachQnp8/f2XNmhCaGVm8KP4FrZnY5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGzPWY%2FdJMcachQnp8%2Ff2XNmhCaGVm8KP4FrZnY5k%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;1548&quot; height=&quot;856&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;856&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;RKE2 실습&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #9d9d9d; color: #ffffff;&quot; data-token-index=&quot;0&quot;&gt;실습 환경 배포&lt;/span&gt;&lt;/div&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; Vagrantfile : Rocky Linux 9 &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771763647309&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Base Image  https://portal.cloud.hashicorp.com/vagrant/discover/bento/rockylinux-9
BOX_IMAGE = &quot;bento/rockylinux-9&quot; # &quot;bento/rockylinux-10.0&quot;
BOX_VERSION = &quot;202510.26.0&quot;
N = 2 # max number of Node

Vagrant.configure(&quot;2&quot;) do |config|

# Nodes 
  (1..N).each do |i|
    config.vm.define &quot;k8s-node#{i}&quot; do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider &quot;virtualbox&quot; do |vb|
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--groups&quot;, &quot;/RKE2-Lab&quot;]
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--nicpromisc2&quot;, &quot;allow-all&quot;]
        vb.name = &quot;k8s-node#{i}&quot;
        vb.cpus = 4
        vb.memory = 4096
        vb.linked_clone = true
      end
      subconfig.vm.host_name = &quot;k8s-node#{i}&quot;
      subconfig.vm.network &quot;private_network&quot;, ip: &quot;192.168.10.1#{i}&quot;
      subconfig.vm.network &quot;forwarded_port&quot;, guest: 22, host: &quot;6000#{i}&quot;, auto_correct: true, id: &quot;ssh&quot;
      subconfig.vm.synced_folder &quot;./&quot;, &quot;/vagrant&quot;, disabled: true
      subconfig.vm.provision &quot;shell&quot;, path: &quot;init_cfg.sh&quot; , args: [ N ]
    end
  end

end&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; init_cfg.sh &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771763662166&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;


echo &quot;[TASK 1] Change Timezone and Enable NTP&quot;
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo &quot;[TASK 2] Disable firewalld and selinux&quot;
systemctl disable --now firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo &quot;[TASK 3] Disable and turn off SWAP &amp;amp; Delete swap partitions&quot;
swapoff -a
sed -i '/swap/d' /etc/fstab
sfdisk --delete /dev/sda 2 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
partprobe /dev/sda &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 4] Config kernel &amp;amp; module&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/modules-load.d/k8s.conf
overlay
br_netfilter
#vxlan
EOF
modprobe overlay &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
modprobe br_netfilter &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
#modprobe vxlan &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

cat &amp;lt;&amp;lt; EOF &amp;gt;/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 5] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
for (( i=1; i&amp;lt;=$1; i++  )); do echo &quot;192.168.10.1$i k8s-node$i&quot; &amp;gt;&amp;gt; /etc/hosts; done


echo &quot;[TASK 6] Install Helm&quot;
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | DESIRED_VERSION=v3.20.0 bash &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 7] Setting SSHD&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF
systemctl restart sshd &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 8] Install packages&quot;
dnf install -y conntrack python3-pip git &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 9] NetworkManager to ignore calico/flannel related network interfaces&quot;
# https://docs.rke2.io/known_issues#networkmanager
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/NetworkManager/conf.d/k8s.conf
[keyfile]
unmanaged-devices=interface-name:flannel*;interface-name:cali*;interface-name:tunl*;interface-name:vxlan.calico;interface-name:vxlan-v6.calico;interface-name:wireguard.cali;interface-name:wg-v6.cali
EOF
systemctl reload NetworkManager


echo &quot;[TASK 11] Install K9s&quot;
CLI_ARCH=amd64
if [ &quot;$(uname -m)&quot; = &quot;aarch64&quot; ]; then CLI_ARCH=arm64; fi
wget -P /tmp https://github.com/derailed/k9s/releases/latest/download/k9s_linux_${CLI_ARCH}.tar.gz  &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
tar -xzf /tmp/k9s_linux_${CLI_ARCH}.tar.gz -C /tmp
chown root:root /tmp/k9s
mv /tmp/k9s /usr/local/bin/
chmod +x /usr/local/bin/k9s


echo &quot;[TASK 12] ETC&quot;
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc


echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771763675784&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir k8s-rke2
cd k8s-rke2

curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-rke2/Vagrantfile
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-rke2/init_cfg.sh

vagrant up
vagrant status&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;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;RKE2 서버 노드 설치 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.rke2.io/install/quickstart&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;, Rocky Linux 9 사용 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 스크립트&lt;/p&gt;
&lt;pre id=&quot;code_1771763711268&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh

set -e

if [ &quot;${DEBUG}&quot; = 1 ]; then
    set -x
fi

# Usage:
#   curl ... | ENV_VAR=... sh -
#       or
#   ENV_VAR=... ./install.sh
#

# Environment variables:
#
#   - INSTALL_RKE2_CHANNEL
#     Channel to use for fetching rke2 download URL.
#     Defaults to 'stable'.
#
#   - INSTALL_RKE2_METHOD
#     The installation method to use.
#     Default is on RPM-based systems is &quot;rpm&quot;, all else &quot;tar&quot;.
#
#   - INSTALL_RKE2_TYPE
#     Type of rke2 service. Can be either &quot;server&quot; or &quot;agent&quot;.
#     Default is &quot;server&quot;.
#
#   - INSTALL_RKE2_EXEC
#     This is an alias for INSTALL_RKE2_TYPE, included for compatibility with K3s.
#     If both are set, INSTALL_RKE2_TYPE is preferred.
#
#   - INSTALL_RKE2_VERSION
#     Version of rke2 to download.
#
#   - INSTALL_RKE2_RPM_RELEASE_VERSION
#     Version of the rke2 RPM release to install.
#     Format would be like &quot;1.el7&quot; or &quot;2.el8&quot;
#
#   - INSTALL_RKE2_TAR_PREFIX
#     Installation prefix when using the tar installation method.
#     Default is /usr/local, unless /usr/local is read-only or has a dedicated mount point,
#     in which case /opt/rke2 is used instead.
#
#   - INSTALL_RKE2_COMMIT
#     Commit of RKE2 to download from temporary cloud storage.
#     For developer &amp;amp; QA use only
#
#   - INSTALL_RKE2_AGENT_IMAGES_DIR
#     Installation path for airgap images when installing from CI commit
#     Default is /var/lib/rancher/rke2/agent/images
#
#   - INSTALL_RKE2_ARTIFACT_URL
#     URL prefix for RKE2 release artifacts.
#     Default is https://github.com/rancher/rke2/releases/download
#
#   - INSTALL_RKE2_ARTIFACT_PATH
#     If set, the install script will use the local path for sourcing the rke2.linux-$SUFFIX and sha256sum-$ARCH.txt files
#     rather than the downloading the files from the internet.
#     Default is not set.
#
#   - INSTALL_RKE2_SKIP_RELOAD
#     If set, the install script will skip reloading systemctl daemon after the tar has been extracted and systemd units
#     have been moved.
#     Default is not set.
#
#   - INSTALL_RKE2_SKIP_FAPOLICY
#     If set, the install script will skip adding fapolicy rules
#     Default is not set.


# info logs the given argument at info log level.
info() {
    echo &quot;[INFO] &quot; &quot;$@&quot;
}

# warn logs the given argument at warn log level.
warn() {
    echo &quot;[WARN] &quot; &quot;$@&quot; &amp;gt;&amp;amp;2
}

# fatal logs the given argument at fatal log level.
fatal() {
    echo &quot;[ERROR] &quot; &quot;$@&quot; &amp;gt;&amp;amp;2
    if [ -n &quot;${SUFFIX}&quot; ]; then
        echo &quot;[ALT] Please visit 'https://github.com/rancher/rke2/releases' directly and download the latest rke2.${SUFFIX}.tar.gz&quot; &amp;gt;&amp;amp;2
    fi
    exit 1
}

# check_target_mountpoint return success if the target directory is on a dedicated mount point
check_target_mountpoint() {
    mountpoint -q &quot;${INSTALL_RKE2_TAR_PREFIX}&quot;
}

# check_target_ro returns success if the target directory is read-only
check_target_ro() {
    touch &quot;${INSTALL_RKE2_TAR_PREFIX}&quot;/.rke2-ro-test &amp;amp;&amp;amp; rm -rf &quot;${INSTALL_RKE2_TAR_PREFIX}&quot;/.rke2-ro-test
    test $? -ne 0
}


# setup_env defines needed environment variables.
setup_env() {
    STORAGE_URL=&quot;https://rke2-ci-builds.s3.dualstack.us-east-1.amazonaws.com&quot;
    DEFAULT_TAR_PREFIX=&quot;/usr/local&quot;
    # --- bail if we are not root ---
    if [ ! $(id -u) -eq 0 ]; then
        fatal &quot;You need to be root to perform this install&quot;
    fi

    # --- make sure artifact url prefix has a value
    if [ -z &quot;${INSTALL_RKE2_ARTIFACT_URL}&quot; ]; then
        INSTALL_RKE2_ARTIFACT_URL=&quot;https://github.com/rancher/rke2/releases/download&quot;
    fi

    # --- make sure install channel has a value
    if [ -z &quot;${INSTALL_RKE2_CHANNEL}&quot; ]; then
        INSTALL_RKE2_CHANNEL=&quot;stable&quot;
    fi

    # --- make sure install type has a value
    if [ -z &quot;${INSTALL_RKE2_TYPE}&quot; ]; then
        INSTALL_RKE2_TYPE=&quot;${INSTALL_RKE2_EXEC:-server}&quot;
    fi

    # --- use rpm install method if available by default
    if [ -z &quot;${INSTALL_RKE2_ARTIFACT_PATH}&quot; ] &amp;amp;&amp;amp; [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ] &amp;amp;&amp;amp; [ -z &quot;${INSTALL_RKE2_METHOD}&quot; ] &amp;amp;&amp;amp; command -v yum &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
        INSTALL_RKE2_METHOD=&quot;rpm&quot;
    fi

    # --- install tarball to /usr/local by default, except if /usr/local is on a separate partition or is read-only
    # --- in which case we go into /opt/rke2.
    if [ -z &quot;${INSTALL_RKE2_TAR_PREFIX}&quot; ]; then
        INSTALL_RKE2_TAR_PREFIX=${DEFAULT_TAR_PREFIX}
        if check_target_mountpoint || check_target_ro; then
            INSTALL_RKE2_TAR_PREFIX=&quot;/opt/rke2&quot;
            warn &quot;${DEFAULT_TAR_PREFIX} is read-only or a mount point; installing to ${INSTALL_RKE2_TAR_PREFIX}&quot;
        fi
    fi

    if [ -z &quot;${INSTALL_RKE2_AGENT_IMAGES_DIR}&quot; ]; then
        INSTALL_RKE2_AGENT_IMAGES_DIR=&quot;/var/lib/rancher/rke2/agent/images&quot;
    fi
}

# check_method_conflict will exit with an error if the user attempts to install
# via tar method on a host with existing RPMs.
check_method_conflict() {
     . /etc/os-release
    case ${INSTALL_RKE2_METHOD} in
    yum | rpm | dnf)
        if [ &quot;${ID_LIKE%%[ ]*}&quot; = &quot;suse&quot; ]; then
            if [ -f ${INSTALL_RKE2_TAR_PREFIX}/bin/rke2 ]; then
                fatal &quot;Cannot perform ${INSTALL_RKE2_METHOD:-rpm} install on host with existing tarball installation - please run rke2-uninstall.sh first&quot;
            fi
        fi
        return
        ;;
    *)
        if rpm -q rke2-common &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
            fatal &quot;Cannot perform ${INSTALL_RKE2_METHOD:-tar} install on host with existing RKE2 RPMs - please run rke2-uninstall.sh first&quot;
        fi
        ;;
    esac
}

# setup_arch set arch and suffix,
# fatal if architecture not supported.
setup_arch() {
    case ${ARCH:=$(uname -m)} in
    x86_64|amd64)
        ARCH=amd64
        SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-${ARCH}
        ;;
    aarch64|arm64)
        ARCH=arm64
        SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-${ARCH}
        ;;
    s390x)
        ARCH=s390x
        SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-${ARCH}
        ;;
    *)
        fatal &quot;unsupported architecture ${ARCH}&quot;
        ;;
    esac
}

# verify_downloader verifies existence of
# network downloader executable.
verify_downloader() {
    cmd=&quot;$(command -v &quot;${1}&quot;)&quot;
    if [ -z &quot;${cmd}&quot; ]; then
        return 1
    fi
    if [ ! -x &quot;${cmd}&quot; ]; then
        return 1
    fi

    # Set verified executable as our downloader program and return success
    DOWNLOADER=${cmd}
    return 0
}

# verify_fapolicyd verifies existence of
# fapolicyd and fagenrules executables and make sure that fapolicyd service is running.
verify_fapolicyd() {
    cmd=&quot;$(command -v &quot;fapolicyd&quot;)&quot;
    if [ -z &quot;${cmd}&quot; ]; then
        return 1
    fi
    cmd=&quot;$(command -v &quot;fagenrules&quot;)&quot;
    if [ -z &quot;${cmd}&quot; ]; then
        return 1
    fi
    systemctl is-active --quiet fapolicyd || return 1

    return 0
}

# setup_tmp creates a temporary directory
# and cleans up when done.
setup_tmp() {
    TMP_DIR=$(mktemp -d -t rke2-install.XXXXXXXXXX)
    TMP_CHECKSUMS=${TMP_DIR}/rke2.checksums
    TMP_TARBALL=${TMP_DIR}/rke2.tarball
    TMP_AIRGAP_CHECKSUMS=${TMP_DIR}/rke2-images.checksums
    TMP_AIRGAP_TARBALL=${TMP_DIR}/rke2-images.tarball
    cleanup() {
        code=$?
        set +e
        trap - EXIT
        rm -rf &quot;${TMP_DIR}&quot;
        exit $code
    }
    trap cleanup INT EXIT
}

# --- use desired rke2 version if defined or find version from channel ---
get_release_version() {
    if [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ] &amp;amp;&amp;amp; [ -z &quot;${INSTALL_RKE2_VERSION}&quot; ]; then
        info &quot;finding release for channel ${INSTALL_RKE2_CHANNEL}&quot;
        INSTALL_RKE2_CHANNEL_URL=${INSTALL_RKE2_CHANNEL_URL:-'https://update.rke2.io/v1-release/channels'}
        version_url=&quot;${INSTALL_RKE2_CHANNEL_URL}/${INSTALL_RKE2_CHANNEL}&quot;
        case ${DOWNLOADER} in
        *curl)
            version=$(${DOWNLOADER} -w &quot;%{url_effective}&quot; -L -s -S &quot;${version_url}&quot; -o /dev/null | sed -e 's|.*/||')
            ;;
        *wget)
            version=$(${DOWNLOADER} -SqO /dev/null &quot;${version_url}&quot; 2&amp;gt;&amp;amp;1 | grep -i Location | sed -e 's|.*/||')
            ;;
        *)
            fatal &quot;Unsupported downloader executable '${DOWNLOADER}'&quot;
            ;;
        esac
        INSTALL_RKE2_VERSION=&quot;${version}&quot;
    fi
}

# check_download performs a HEAD request to see if a file exists at a given url
check_download() {
    case ${DOWNLOADER} in
    *curl)
        curl -o &quot;/dev/null&quot; -fsLI -X HEAD &quot;$1&quot;
        ;;
    *wget)
        wget -q --spider &quot;$1&quot;
        ;;
    *)
        fatal &quot;downloader executable not supported: '${DOWNLOADER}'&quot;
        ;;
    esac
}

# download downloads a file from a url using either curl or wget
download() {
    if [ $# -ne 2 ]; then
        fatal &quot;download needs exactly 2 arguments&quot;
    fi

    case ${DOWNLOADER} in
    *curl)
        curl -o &quot;$1&quot; -fsSL &quot;$2&quot;
        ;;
    *wget)
        wget -qO &quot;$1&quot; &quot;$2&quot;
        ;;
    *)
        fatal &quot;downloader executable not supported: '${DOWNLOADER}'&quot;
        ;;
    esac

    # Abort if download command failed
    if [ $? -ne 0 ]; then
        fatal &quot;download failed&quot;
    fi
}

# download_checksums downloads hash from github url.
download_checksums() {
    version_urlsafe=&quot;$(echo ${INSTALL_RKE2_VERSION} | sed 's/\+/%2B/g')&quot;
    if [ -n &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        CHECKSUMS_URL=${STORAGE_URL}/rke2.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.gz.sha256sum
    else
        CHECKSUMS_URL=${INSTALL_RKE2_ARTIFACT_URL}/${version_urlsafe}/sha256sum-${ARCH}.txt
    fi
    info &quot;downloading checksums at ${CHECKSUMS_URL}&quot;
    download &quot;${TMP_CHECKSUMS}&quot; &quot;${CHECKSUMS_URL}&quot;
    CHECKSUM_EXPECTED=$(grep &quot;rke2.${SUFFIX}.tar.gz&quot; &quot;${TMP_CHECKSUMS}&quot; | awk '{print $1}')
}

# download_tarball downloads binary from github url.
download_tarball() {
    version_urlsafe=&quot;$(echo ${INSTALL_RKE2_VERSION} | sed 's/\+/%2B/g')&quot;
    if [ -n &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        TARBALL_URL=${STORAGE_URL}/rke2.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.gz
    else
        TARBALL_URL=${INSTALL_RKE2_ARTIFACT_URL}/${version_urlsafe}/rke2.${SUFFIX}.tar.gz
    fi
    info &quot;downloading tarball at ${TARBALL_URL}&quot;
    download &quot;${TMP_TARBALL}&quot; &quot;${TARBALL_URL}&quot;
}

# download_dev_rpm downloads dev rpm from remote repository
download_dev_rpm() {
    if [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        fatal 'Development rpm requires a commit hash'
    fi

    DEST=&quot;${1}&quot;
    RPM_FILE=$(basename &quot;${DEST}&quot;)
    RPM_URL=&quot;${STORAGE_URL}/${RPM_FILE}&quot;

    info &quot;downloading rpm at ${RPM_URL}&quot;
    download &quot;${DEST}&quot; &quot;${RPM_URL}&quot;
}

# stage_local_checksums stages the local checksum hash for validation.
stage_local_checksums() {
    info &quot;staging local checksums from ${INSTALL_RKE2_ARTIFACT_PATH}/sha256sum-${ARCH}.txt&quot;
    cp -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/sha256sum-${ARCH}.txt&quot; &quot;${TMP_CHECKSUMS}&quot;
    CHECKSUM_EXPECTED=$(grep &quot;rke2.${SUFFIX}.tar.gz&quot; &quot;${TMP_CHECKSUMS}&quot; | awk '{print $1}')
    if [ -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.zst&quot; ]; then
        AIRGAP_CHECKSUM_EXPECTED=$(grep &quot;rke2-images.${SUFFIX}.tar.zst&quot; &quot;${TMP_CHECKSUMS}&quot; | awk '{print $1}')
    elif [ -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.gz&quot; ]; then
        AIRGAP_CHECKSUM_EXPECTED=$(grep &quot;rke2-images.${SUFFIX}.tar.gz&quot; &quot;${TMP_CHECKSUMS}&quot; | awk '{print $1}')
    fi
}

# stage_local_tarball stages the local tarball.
stage_local_tarball() {
    info &quot;staging tarball from ${INSTALL_RKE2_ARTIFACT_PATH}/rke2.${SUFFIX}.tar.gz&quot;
    cp -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2.${SUFFIX}.tar.gz&quot; &quot;${TMP_TARBALL}&quot;
}

# stage_local_airgap_tarball stages the local checksum hash for validation.
stage_local_airgap_tarball() {
    if [ -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.zst&quot; ]; then
        info &quot;staging zst airgap image tarball from ${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.zst&quot;
        cp -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.zst&quot; &quot;${TMP_AIRGAP_TARBALL}&quot;
        AIRGAP_TARBALL_FORMAT=zst
    elif [ -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.gz&quot; ]; then
        info &quot;staging gzip airgap image tarball from ${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.gz&quot;
        cp -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.gz&quot; &quot;${TMP_AIRGAP_TARBALL}&quot;
        AIRGAP_TARBALL_FORMAT=gz
    fi
}

# verify_tarball verifies the downloaded installer checksum.
verify_tarball() {
    info &quot;verifying tarball&quot;
    CHECKSUM_ACTUAL=$(sha256sum &quot;${TMP_TARBALL}&quot; | awk '{print $1}')
    if [ &quot;${CHECKSUM_EXPECTED}&quot; != &quot;${CHECKSUM_ACTUAL}&quot; ]; then
        fatal &quot;download sha256 does not match ${CHECKSUM_EXPECTED}, got ${CHECKSUM_ACTUAL}&quot;
    fi
}

# unpack_tarball extracts the tarball, correcting paths and moving systemd units as necessary
unpack_tarball() {
    info &quot;unpacking tarball file to ${INSTALL_RKE2_TAR_PREFIX}&quot;
    mkdir -p ${INSTALL_RKE2_TAR_PREFIX}
    tar xzf &quot;${TMP_TARBALL}&quot; -C &quot;${INSTALL_RKE2_TAR_PREFIX}&quot;
    if [ &quot;${INSTALL_RKE2_TAR_PREFIX}&quot; != &quot;${DEFAULT_TAR_PREFIX}&quot; ]; then
        info &quot;updating tarball contents to reflect install path&quot;
        sed -i &quot;s|${DEFAULT_TAR_PREFIX}|${INSTALL_RKE2_TAR_PREFIX}|&quot; ${INSTALL_RKE2_TAR_PREFIX}/lib/systemd/system/rke2-*.service ${INSTALL_RKE2_TAR_PREFIX}/bin/rke2-uninstall.sh
        info &quot;moving systemd units to /etc/systemd/system&quot;
        mv -f ${INSTALL_RKE2_TAR_PREFIX}/lib/systemd/system/rke2-*.service /etc/systemd/system/
        info &quot;install complete; you may want to run:  export PATH=\$PATH:${INSTALL_RKE2_TAR_PREFIX}/bin&quot;
    fi
}

# download_airgap_checksums downloads the checksum file for the airgap image tarball
# and prepares the checksum value for later validation.
download_airgap_checksums() {
    if [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        return
    fi
    AIRGAP_CHECKSUMS_URL=${STORAGE_URL}/rke2-images.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.zst.sha256sum
    # try for zst first; if that fails use gz for older release branches
    if ! check_download &quot;${AIRGAP_CHECKSUMS_URL}&quot;; then
        AIRGAP_CHECKSUMS_URL=${STORAGE_URL}/rke2-images.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.gz.sha256sum
    fi
    info &quot;downloading airgap checksums at ${AIRGAP_CHECKSUMS_URL}&quot;
    download &quot;${TMP_AIRGAP_CHECKSUMS}&quot; &quot;${AIRGAP_CHECKSUMS_URL}&quot;
    AIRGAP_CHECKSUM_EXPECTED=$(grep &quot;rke2-images.${SUFFIX}.tar&quot; &quot;${TMP_AIRGAP_CHECKSUMS}&quot; | awk '{print $1}')
}

# download_airgap_tarball downloads the airgap image tarball.
download_airgap_tarball() {
    if [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        return
    fi
    AIRGAP_TARBALL_URL=${STORAGE_URL}/rke2-images.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.zst
    # try for zst first; if that fails use gz for older release branches
    if ! check_download &quot;${AIRGAP_TARBALL_URL}&quot;; then
        AIRGAP_TARBALL_URL=${STORAGE_URL}/rke2-images.${SUFFIX}-${INSTALL_RKE2_COMMIT}.tar.gz
    fi
    info &quot;downloading airgap tarball at ${AIRGAP_TARBALL_URL}&quot;
    download &quot;${TMP_AIRGAP_TARBALL}&quot; &quot;${AIRGAP_TARBALL_URL}&quot;
}

# verify_airgap_tarball compares the airgap image tarball checksum to the value
# calculated by CI when the file was uploaded.
verify_airgap_tarball() {
    if [ -z &quot;${AIRGAP_CHECKSUM_EXPECTED}&quot; ]; then
        return
    fi
    info &quot;verifying airgap tarball&quot;
    AIRGAP_CHECKSUM_ACTUAL=$(sha256sum &quot;${TMP_AIRGAP_TARBALL}&quot; | awk '{print $1}')
    if [ &quot;${AIRGAP_CHECKSUM_EXPECTED}&quot; != &quot;${AIRGAP_CHECKSUM_ACTUAL}&quot; ]; then
        fatal &quot;download sha256 does not match ${AIRGAP_CHECKSUM_EXPECTED}, got ${AIRGAP_CHECKSUM_ACTUAL}&quot;
    fi
}

# install_airgap_tarball moves the airgap image tarball into place.
install_airgap_tarball() {
    if [ -z &quot;${AIRGAP_CHECKSUM_EXPECTED}&quot; ]; then
        return
    fi
    mkdir -p &quot;${INSTALL_RKE2_AGENT_IMAGES_DIR}&quot;
    # releases that provide zst artifacts can read from the compressed archive; older releases
    # that produce only gzip artifacts need to have the tarball decompressed ahead of time
    if grep -sqF '.tar.zst' &quot;${TMP_AIRGAP_CHECKSUMS}&quot; || [ &quot;${AIRGAP_TARBALL_FORMAT}&quot; = &quot;zst&quot; ]; then
        info &quot;installing airgap tarball to ${INSTALL_RKE2_AGENT_IMAGES_DIR}&quot;
        mv -f &quot;${TMP_AIRGAP_TARBALL}&quot; &quot;${INSTALL_RKE2_AGENT_IMAGES_DIR}/rke2-images.${SUFFIX}.tar.zst&quot;
    else
        info &quot;decompressing airgap tarball to ${INSTALL_RKE2_AGENT_IMAGES_DIR}&quot;
        gzip -dc &quot;${TMP_AIRGAP_TARBALL}&quot; &amp;gt; &quot;${INSTALL_RKE2_AGENT_IMAGES_DIR}/rke2-images.${SUFFIX}.tar&quot;
    fi
    # Search for and install additional rke2 images
    for IMAGE in &quot;${INSTALL_RKE2_ARTIFACT_PATH}&quot;/rke2-images-*.&quot;${SUFFIX}&quot;*; do 
        if [ -f &quot;${IMAGE}&quot; ]; then
            info &quot;Installing airgap image from ${IMAGE}&quot;
            cp &quot;${IMAGE}&quot; &quot;${INSTALL_RKE2_AGENT_IMAGES_DIR}&quot;
        fi
    done
}

# install_dev_rpm orchestrates the installation of RKE2 unsigned development rpms
install_dev_rpm() {
    rpm_installer=&quot;${1:-yum}&quot;
    distro=&quot;${2:-centos7}&quot;

    [ -z &quot;${TMP_DIR}&quot; ] &amp;amp;&amp;amp; setup_tmp

    case &quot;${rpm_installer}&quot; in
        *zypper*)
            ${rpm_installer} install --allow-unsigned-rpm -y \
                &quot;${STORAGE_URL}/rke2-common-${INSTALL_RKE2_COMMIT}.${distro}.rpm&quot; \
                &quot;${STORAGE_URL}/rke2-${INSTALL_RKE2_TYPE}-${INSTALL_RKE2_COMMIT}.${distro}.rpm&quot;
            ;;
        *)
            download_dev_rpm &quot;${TMP_DIR}/rke2-common-${INSTALL_RKE2_COMMIT}.${distro}.rpm&quot;
            download_dev_rpm &quot;${TMP_DIR}/rke2-${INSTALL_RKE2_TYPE}-${INSTALL_RKE2_COMMIT}.${distro}.rpm&quot;
            ${rpm_installer} install -y  &quot;${TMP_DIR}&quot;/*.rpm
            ;;
    esac

    if [ -f &quot;${INSTALL_RKE2_ARTIFACT_PATH}/rke2-images.${SUFFIX}.tar.zst&quot; ]; then
        return
    fi

    download_airgap_checksums
    download_airgap_tarball
    verify_airgap_tarball
    install_airgap_tarball
}

# do_install_rpm builds a yum repo config from the channel and version to be installed,
# and calls yum to install the required packages.
do_install_rpm() {
    . /etc/os-release
    if [ -r /etc/redhat-release ] || [ -r /etc/centos-release ] || [ -r /etc/oracle-release ] || [ -r /etc/system-release ] || [ &quot;${ID_LIKE%%[ ]*}&quot; = &quot;suse&quot;  ]; then
        repodir=/etc/yum.repos.d
        if [ -d /etc/zypp/repos.d ]; then
            repodir=/etc/zypp/repos.d
        fi
        if [ &quot;${ID_LIKE%%[ ]*}&quot; = &quot;suse&quot; ]; then
            # create the /var/lib/rpm-state in SLE systems to fix the prein selinux macro
            if [ &quot;${TRANSACTIONAL_UPDATE=false}&quot; != &quot;true&quot; ] &amp;amp;&amp;amp; [ -x /usr/sbin/transactional-update ]; then
                transactional_update_run=&quot;transactional-update --no-selfupdate -d run&quot;
            fi
            ${transactional_update_run} mkdir -p /var/lib/rpm-state
            # configure infix and rpm_installer
            rpm_site_infix=microos
            if [ &quot;${VARIANT_ID:-}&quot; = sle-micro ] || [ &quot;${ID:-}&quot; = sle-micro ] || [ &quot;${ID:-}&quot; = sl-micro ]; then
                rpm_site_infix=slemicro
                package_installer=zypper
            fi
            rpm_installer=&quot;zypper --gpg-auto-import-keys&quot;
            if [ &quot;${TRANSACTIONAL_UPDATE=false}&quot; != &quot;true&quot; ] &amp;amp;&amp;amp; [ -x /usr/sbin/transactional-update ]; then
                rpm_installer=&quot;transactional-update --no-selfupdate -d run ${rpm_installer}&quot;
            fi
        else
            maj_ver=$(echo &quot;$VERSION_ID&quot; | sed -E -e &quot;s/^([0-9]+)\.?[0-9]*$/\1/&quot;)
            case ${maj_ver} in
                7|8|9|10)
                    :
                    ;;
                2023) # detect amazon linux 2023 distro
                    maj_ver=&quot;8&quot;
                    ;;
                *) # set default distro to centos 8, for edge cases such as fedora
                    maj_ver=&quot;8&quot;
                    ;;
            esac
            rpm_site_infix=centos/${maj_ver}
            rpm_installer=&quot;yum&quot;
        fi

    fi
    case &quot;${INSTALL_RKE2_CHANNEL}&quot; in
        v*.*)
            # We are operating with a version-based channel, so we should parse our version out
            rke2_majmin=$(echo &quot;${INSTALL_RKE2_CHANNEL}&quot; | sed -E -e &quot;s/^v([0-9]+\.[0-9]+).*/\1/&quot;)
            rke2_rpm_channel=$(echo &quot;${INSTALL_RKE2_CHANNEL}&quot; | sed -E -e &quot;s/^v[0-9]+\.[0-9]+-(.*)/\1/&quot;)
            # If our regex fails to capture a &quot;sane&quot; channel out of the specified channel, fall back to `stable`
            if [ &quot;${rke2_rpm_channel}&quot; = ${INSTALL_RKE2_CHANNEL} ]; then
                info &quot;using stable RPM repositories&quot;
                rke2_rpm_channel=&quot;stable&quot;
            fi
            ;;
        *)
            get_release_version
            rke2_majmin=$(echo &quot;${INSTALL_RKE2_VERSION}&quot; | sed -E -e &quot;s/^v([0-9]+\.[0-9]+).*/\1/&quot;)
            rke2_rpm_channel=${1}
            ;;
    esac
    info &quot;using ${rke2_majmin} series from channel ${rke2_rpm_channel}&quot;
    rpm_site=&quot;rpm.rancher.io&quot;
    if [ &quot;${rke2_rpm_channel}&quot; = &quot;testing&quot; ]; then
        rpm_site=&quot;rpm-${rke2_rpm_channel}.rancher.io&quot;
    fi

    rm -f ${repodir}/rancher-rke2*.repo
    cat &amp;lt;&amp;lt;-EOF &amp;gt;&quot;${repodir}/rancher-rke2.repo&quot;
[rancher-rke2-common-${rke2_rpm_channel}]
name=Rancher RKE2 Common (${1})
baseurl=https://${rpm_site}/rke2/${rke2_rpm_channel}/common/${rpm_site_infix}/noarch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://${rpm_site}/public.key
EOF

    if [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
    cat &amp;lt;&amp;lt;-EOF &amp;gt;&amp;gt;&quot;${repodir}/rancher-rke2.repo&quot;
[rancher-rke2-${rke2_majmin}-${rke2_rpm_channel}]
name=Rancher RKE2 ${rke2_majmin} (${1})
baseurl=https://${rpm_site}/rke2/${rke2_rpm_channel}/${rke2_majmin}/${rpm_site_infix}/$(uname -m)
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://${rpm_site}/public.key
EOF
    fi

    if rpm -q --quiet rke2-selinux; then
            # remove rke2-selinux module in el9 before upgrade to allow container-selinux to upgrade safely
            if check_available_upgrades container-selinux &amp;amp;&amp;amp; check_available_upgrades rke2-selinux &amp;amp;&amp;amp; check_breaking_version container-selinux 2 189; then
                MODULE_PRIORITY=$(semodule --list=full | grep rke2 | cut -f1 -d&quot; &quot;)
                if [ -n &quot;${MODULE_PRIORITY}&quot; ]; then
                    semodule -X $MODULE_PRIORITY -r rke2 || true
                fi
            fi
    fi

    if [ -z &quot;${INSTALL_RKE2_VERSION}&quot; ] &amp;amp;&amp;amp; [ -z &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        ${rpm_installer} install -y &quot;rke2-${INSTALL_RKE2_TYPE}&quot;
    elif [ -n &quot;${INSTALL_RKE2_COMMIT}&quot; ]; then
        rel_distro=&quot;$(echo &quot;${rpm_site_infix}&quot; | tr -d /)&quot;
        install_dev_rpm &quot;${rpm_installer}&quot; &quot;${rel_distro}&quot;
    else
        rke2_rpm_version=$(echo &quot;${INSTALL_RKE2_VERSION}&quot; | sed -E -e &quot;s/[\+-]/~/g&quot; | sed -E -e &quot;s/v(.*)/\1/&quot;)
        if [ -n &quot;${INSTALL_RKE2_RPM_RELEASE_VERSION}&quot; ]; then
            ${rpm_installer} install -y &quot;rke2-${INSTALL_RKE2_TYPE}-${rke2_rpm_version}-${INSTALL_RKE2_RPM_RELEASE_VERSION}&quot;
        else
            ${rpm_installer} install -y &quot;rke2-${INSTALL_RKE2_TYPE}-${rke2_rpm_version}&quot;
        fi
    fi
}

check_breaking_version() {
  maj=$2
  min=$3

  current_maj=$(rpm -qi $1 | awk -F': ' '/Version/ {print $2}' | sed -E -e &quot;s/^([0-9]+)\.([0-9]+).*/\1/&quot;)
  current_min=$(rpm -qi $1 | awk -F': ' '/Version/ {print $2}' | sed -E -e &quot;s/^([0-9]+)\.([0-9]+).*/\2/&quot;)

  if [ &quot;${current_maj}&quot; == &quot;${maj}&quot; ] &amp;amp;&amp;amp; [ $current_min -le $min ]; then
    return 0
  fi

  return 1
}

check_available_upgrades() {
    . /etc/os-release
    set +e
    if [ &quot;${ID_LIKE%%[ ]*}&quot; = &quot;suse&quot; ]; then
        available_upgrades=$(zypper -q -t -s 11 se -s -u --type package $1 | tail -n 1 | grep -v &quot;No matching&quot; | awk '{print $3}')
    else
        available_upgrades=$(yum -q --refresh list $1 --upgrades | tail -n 1 | awk '{print $2}')
    fi
    set -e
    if [ -n &quot;${available_upgrades}&quot; ]; then
        return 0
    fi
    return 1
}

do_install_tar() {
    setup_tmp

    if [ -n &quot;${INSTALL_RKE2_ARTIFACT_PATH}&quot; ]; then
        stage_local_checksums
        stage_local_airgap_tarball
        stage_local_tarball
    else
        get_release_version
        info &quot;using ${INSTALL_RKE2_VERSION:-commit $INSTALL_RKE2_COMMIT} as release&quot;
        download_airgap_checksums
        download_airgap_tarball
        download_checksums
        download_tarball
    fi

    verify_airgap_tarball
    install_airgap_tarball
    verify_tarball
    unpack_tarball

    if [ -z &quot;${INSTALL_RKE2_SKIP_RELOAD}&quot; ]; then
        systemctl daemon-reload
    fi
}

setup_fapolicy_rules() {
    if [ -r /etc/redhat-release ] || [ -r /etc/centos-release ] || [ -r /etc/oracle-release ] || [ -r /etc/rocky-release ] || [ -r /etc/system-release ]; then
        verify_fapolicyd || return 0
        # setting rke2 fapolicyd rules
        cat &amp;lt;&amp;lt;-EOF &amp;gt;&quot;/etc/fapolicyd/rules.d/80-rke2.rules&quot;
allow perm=any all : dir=/var/lib/rancher/
allow perm=any all : dir=/opt/cni/
allow perm=any all : dir=/run/k3s/
allow perm=any all : dir=/var/lib/kubelet/
EOF
        if [ -z &quot;${INSTALL_RKE2_SKIP_RELOAD}&quot; ]; then
            fagenrules --load || fatal &quot;failed to load rke2 fapolicyd rules&quot;
            systemctl restart fapolicyd
        fi
    fi
}

do_install() {
    setup_env
    check_method_conflict
    setup_arch
    if [ -z &quot;${INSTALL_RKE2_ARTIFACT_PATH}&quot; ]; then
        verify_downloader curl || verify_downloader wget || fatal &quot;can not find curl or wget for downloading files&quot;
    fi

    case ${INSTALL_RKE2_METHOD} in
    yum | rpm | dnf)
        do_install_rpm &quot;${INSTALL_RKE2_CHANNEL}&quot;
        ;;
    *)
        do_install_tar &quot;${INSTALL_RKE2_CHANNEL}&quot;
        ;;
    esac
    if [ -z &quot;${INSTALL_RKE2_SKIP_FAPOLICY}&quot; ]; then
        setup_fapolicy_rules
    fi
}

do_install
exit 0&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Configuring the Linux Installation Script : 설치에 적용할 수 있는 환경변수 - &lt;a href=&quot;https://docs.rke2.io/install/configuration#configuring-the-linux-installation-script&quot;&gt;Docs&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;INSTALL_RKE2_VERSION : GitHub에서 다운로드할 RKE2 버전 - stable(기본값),&lt;/li&gt;
&lt;li&gt;INSTALL_RKE2_TYPE : 생성할 systemd 서비스의 유형 - server(기본값), agent&lt;/li&gt;
&lt;li&gt;INSTALL_RKE2_CHANNEL_URL : RKE2 다운로드 URL을 가져오기 위한 채널 URL - &lt;a href=&quot;https://update.rke2.io/v1-release/channels&quot;&gt;https://update.rke2.io/v1-release/channels&lt;/a&gt; (기본값)&lt;/li&gt;
&lt;li&gt;INSTALL_RKE2_CHANNEL : RKE2 다운로드 URL을 가져오는 데 사용할 채널 - stable(기본값), latest, testing&lt;/li&gt;
&lt;li&gt;INSTALL_RKE2_METHOD : 사용할 설치 방법 - rpm(RPM 기반 시스템 경우 기본값), tar(그외 경우 기본값)&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;pre id=&quot;code_1771763732942&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemd 기반 시스템에 서비스로 편리하게 설치할 수 있는 설치 스크립트를 제공 : 서비스와 바이너리 파일이 컴퓨터에 설치
# https://docs.rke2.io/install/methods
curl -sfL https://get.rke2.io --output install.sh
chmod +x install.sh
INSTALL_RKE2_CHANNEL=v1.33 ./install.sh
...
Installed:
  rke2-common-1.33.7~rke2r3-0.el9.aarch64             rke2-selinux-0.22-1.el9.noarch             rke2-server-1.33.7~rke2r3-0.el9.aa

# rke2 버전 확인
rke2 --version
rke2 version v1.33.7+rke2r3 (7e4fd1a82edf497cab91c220144619bbad659cf4)
go version go1.24.11 X:boringcrypto

# repo 추가 확인
dnf repolist
rancher-rke2-1.33-stable                                                  Rancher RKE2 1.33 (v1.33)
rancher-rke2-common-stable                                                Rancher RKE2 Common (v1.33)

tree /etc/yum.repos.d/
cat /etc/yum.repos.d/rancher-rke2.repo
[rancher-rke2-common-stable]
name=Rancher RKE2 Common (v1.33)
baseurl=https://rpm.rancher.io/rke2/stable/common/centos/9/noarch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://rpm.rancher.io/public.key
[rancher-rke2-1.33-stable]
name=Rancher RKE2 1.33 (v1.33)
baseurl=https://rpm.rancher.io/rke2/stable/1.33/centos/9/aarch64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://rpm.rancher.io/public.key

# 디렉터리 생성 확인
tree /etc/rancher/
tree /var/lib/rancher/

# rke2 명령 확인
# https://docs.rke2.io/install/configuration#running-the-binary-directly
# rke2 server : Run the RKE2 management server, which will also launch the Kubernetes control plane components such as the API server, controller-manager, and scheduler. Only Supported on Linux.
# rke2 agent : Run the RKE2 node agent. This will cause RKE2 to run as a worker node, launching the Kubernetes node services kubelet and kube-proxy. Supported on Linux and Windows.
rke2 --h
   server           Run management server
   agent            Run node agent


# RKE2 설정 : cni 플러그인(canal) 등
# https://docs.rke2.io/install/configuration
# https://docs.rke2.io/advanced
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/rancher/rke2/config.yaml
write-kubeconfig-mode: &quot;0644&quot;

debug: true

cni: canal

bind-address: 192.168.10.11
advertise-address: 192.168.10.11
node-ip: 192.168.10.11

disable-cloud-controller: true

disable:
  - servicelb
  - rke2-coredns-autoscaler
  - rke2-ingress-nginx
  - rke2-snapshot-controller
  - rke2-snapshot-controller-crd
  - rke2-snapshot-validation-webhook
EOF
cat /etc/rancher/rke2/config.yaml

# canal cni 플러그인 helm chart values 파일 작성
# https://docs.rke2.io/networking/basic_network_options
# https://github.com/rancher/rke2-charts/blob/main-source/packages/rke2-canal/charts/values.yaml
mkdir -p /var/lib/rancher/rke2/server/manifests/
cat &amp;lt;&amp;lt; EOF &amp;gt; /var/lib/rancher/rke2/server/manifests/rke2-canal-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-canal
  namespace: kube-system
spec:
  valuesContent: |-
    flannel:
      iface: &quot;enp0s9&quot;
EOF

# coredns 의 autoscaler 미설치를 위한 helm chart values 파일 작성
# https://docs.rke2.io/add-ons/helm#customizing-packaged-components-with-helmchartconfig
# https://github.com/rancher/rke2-charts/tree/main/charts/rke2-coredns/rke2-coredns/1.45.200
cat &amp;lt;&amp;lt; EOF &amp;gt; /var/lib/rancher/rke2/server/manifests/rke2-coredns-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-coredns
  namespace: kube-system
spec:
  valuesContent: |-
    autoscaler:
      enabled: false
EOF
    

# 모니터링 : 신규 터미널창
watch -d pstree -a
journalctl -u rke2-server -f


# RKE2 시작 : 2분 정도 소요 -&amp;gt; coredns 파드까지 정상화 대략 1~2분 추가 소요
systemctl enable --now rke2-server.service
systemctl status rke2-server --no-pager


# 프로세스 확인
pstree -a | grep -v color | grep 'rke2$' -A5
pstree -a | grep -v color | grep 'containerd-shim ' -A2


# 자격증명 파일 복사
mkdir ~/.kube
ls -l /etc/rancher/rke2/rke2.yaml
cp /etc/rancher/rke2/rke2.yaml ~/.kube/config

# /etc/rancher 디렉터리 확인
tree /etc/rancher/
cat /etc/rancher/node/password
cat /etc/rancher/rke2/config.yaml
cat /etc/rancher/rke2/rke2-pss.yaml 

# 바이너리 파일 확인
tree /var/lib/rancher/rke2/bin/
├── containerd
├── containerd-shim-runc-v2
├── crictl
├── ctr
├── kubectl
├── kubelet
└── runc

# PATH 안 건드리고 표준 위치로 바이너리 노출 설정 : 심볼릭 링크 방식 
ln -s /var/lib/rancher/rke2/bin/containerd /usr/local/bin/containerd
ln -s /var/lib/rancher/rke2/bin/kubectl /usr/local/bin/kubectl
ln -s /var/lib/rancher/rke2/bin/crictl /usr/local/bin/crictl
ln -s /var/lib/rancher/rke2/bin/runc /usr/local/bin/runc
ln -s /var/lib/rancher/rke2/bin/ctr /usr/local/bin/ctr
ln -s /var/lib/rancher/rke2/agent/etc/crictl.yaml /etc/crictl.yaml

runc --version
containerd --version
kubectl version

# 편의성 설정
source &amp;lt;(kubectl completion bash)
alias k=kubectl
complete -F __start_kubectl k
echo 'source &amp;lt;(kubectl completion bash)' &amp;gt;&amp;gt; /etc/profile
echo 'alias k=kubectl' &amp;gt;&amp;gt; /etc/profile
echo 'complete -F __start_kubectl k' &amp;gt;&amp;gt; /etc/profile

k9s

# 확인
kubectl cluster-info -v=6
Kubernetes control plane is running at https://192.168.10.11:6443

# 노드, 파드 정보 확인
kubectl get node -owide
NAME        STATUS   ROLES                AGE   VERSION          INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                      KERNEL-VERSION                  CONTAINER-RUNTIME
k8s-node1   Ready    control-plane,etcd   15m   v1.34.3+rke2r3   192.168.10.11   &amp;lt;none&amp;gt;        Rocky Linux 9.6 (Blue Onyx)   5.14.0-570.52.1.el9_6.aarch64   containerd://2.1.5-k3s1

helm list -A
NAME               	NAMESPACE  	REVISION	UPDATED                                	STATUS  	CHART                             	APP VERSION
rke2-canal         	kube-system	1       	2026-02-09 14:30:45.002896818 +0000 UTC	deployed	rke2-canal-v3.31.3-build2026011900	v3.31.3
rke2-coredns       	kube-system	1       	2026-02-09 14:30:44.989504642 +0000 UTC	deployed	rke2-coredns-1.45.008             	1.13.1
rke2-metrics-server	kube-system	1       	2026-02-09 14:31:13.07011618 +0000 UTC 	deployed	rke2-metrics-server-3.13.006      	0.8.0
rke2-runtimeclasses	kube-system	1       	2026-02-09 14:31:17.059719859 +0000 UTC	deployed	rke2-runtimeclasses-0.1.000       	0.1.0

kubectl get pod -A
NAMESPACE     NAME                                         READY   STATUS      RESTARTS   AGE
kube-system   etcd-k8s-node1                               1/1     Running     0          2m31s
kube-system   helm-install-rke2-canal-8ck96                0/1     Completed   0          2m42s
kube-system   helm-install-rke2-coredns-mhw4x              0/1     Completed   0          2m42s
kube-system   helm-install-rke2-metrics-server-rhqcp       0/1     Completed   0          2m42s
kube-system   helm-install-rke2-runtimeclasses-7m69w       0/1     Completed   0          2m42s
kube-system   kube-apiserver-k8s-node1                     1/1     Running     0          2m31s
kube-system   kube-controller-manager-k8s-node1            1/1     Running     0          2m31s
kube-system   kube-proxy-k8s-node1                         1/1     Running     0          2m31s
kube-system   kube-scheduler-k8s-node1                     1/1     Running     0          2m31s
kube-system   rke2-canal-dkw2n                             2/2     Running     0          2m22s
kube-system   rke2-coredns-rke2-coredns-784bcb7f4d-tpt2d   1/1     Running     0          2m22s
kube-system   rke2-metrics-server-7b59bd8854-m5w2c         1/1     Running     0          114s&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;/var/lib/rancher/rke2&amp;nbsp;디렉터리&amp;nbsp;:&amp;nbsp;addon&amp;nbsp;helm&amp;nbsp;chart,&amp;nbsp;인증서&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771763762026&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 디렉터리 확인
tree /var/lib/rancher/rke2 -L 1
├── agent
├── bin -&amp;gt; /var/lib/rancher/rke2/data/v1.34.3-rke2r3-5b8349de68df/bin
├── data
└── server

# server 디렉터리
tree /var/lib/rancher/rke2/server/
tree /var/lib/rancher/rke2/server/ -L 1
├── agent-token -&amp;gt; /var/lib/rancher/rke2/server/token
├── cred           # kubeconfig 정보들
├── db             # etcd snapshots, etcd member snap/wal
├── etc            
├── manifests      # helm chart manifests by helm controller
├── node-token -&amp;gt; /var/lib/rancher/rke2/server/token
├── tls            # 인증서 관련 파일들
└── token

## A token that can be used to register other server or agent nodes will be created at /var/lib/rancher/rke2/server/node-token
ls -l /var/lib/rancher/rke2/server/
cat /var/lib/rancher/rke2/server/node-token
cat /var/lib/rancher/rke2/server/token
K1087d08f6b3f21e75c6835e1b959d634dfbe44970c7b1c93c61d8c510f8076822f::server:466038852cf7cdcd389872ba4ab64c04

## helm chart manifests + values 포함
cat /var/lib/rancher/rke2/server/manifests/rke2-coredns.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChart
...
  set:
    global.clusterCIDR: 10.42.0.0/16
    global.clusterCIDRv4: 10.42.0.0/16

# addon, helm controller 관련 crd 확인
kubectl get crd | grep -E 'helm|addon'
addons.k3s.cattle.io                                    2026-02-14T07:33:46Z
helmchartconfigs.helm.cattle.io                         2026-02-14T07:33:46Z
helmcharts.helm.cattle.io                               2026-02-14T07:33:46Z

kubectl get helmcharts.helm.cattle.io -n kube-system -owide
NAME                  REPO   CHART   VERSION   TARGETNAMESPACE   BOOTSTRAP   FAILED   JOB
rke2-canal                                                       true        False    helm-install-rke2-canal
rke2-coredns                                                     true        False    helm-install-rke2-coredns
rke2-metrics-server                                                          False    helm-install-rke2-metrics-server
rke2-runtimeclasses                                                          False    helm-install-rke2-runtimeclasses

kubectl get job -n kube-system
NAME                               STATUS     COMPLETIONS   DURATION   AGE
helm-install-rke2-canal            Complete   1/1           21s        118m
helm-install-rke2-coredns          Complete   1/1           20s        118m
helm-install-rke2-metrics-server   Complete   1/1           52s        118m
helm-install-rke2-runtimeclasses   Complete   1/1           51s        118m

kubectl get helmchartconfigs -n kube-system
NAME           AGE
rke2-canal     119m
rke2-coredns   119m

kubectl describe helmchartconfigs -n kube-system rke2-canal
...
Spec:
  Failure Policy:  reinstall
  Values Content:  flannel:
  iface: &quot;enp0s9&quot;

kubectl get addons.k3s.cattle.io -n kube-system
NAME                  SOURCE                                                            CHECKSUM
rke2-canal            /var/lib/rancher/rke2/server/manifests/rke2-canal.yaml            2de126b0695f5c424eba2b8e7c45c67a757edac6ecccce7a6141228e6c2fd852
rke2-canal-config     /var/lib/rancher/rke2/server/manifests/rke2-canal-config.yaml     122823431e18d9f23621508c24bff9aff307f46b4d6d9208bbbbf23311db058e
rke2-coredns          /var/lib/rancher/rke2/server/manifests/rke2-coredns.yaml          523c74e141f7722f3721ce5b3b6556241199bf3aed9b781a5cbfeebd2fe595f4
rke2-coredns-config   /var/lib/rancher/rke2/server/manifests/rke2-coredns-config.yaml   9c8e2bbb7603c69c233c9343c516d3d302fd59b758e2e7084c7ab08b5bfba0e4
rke2-metrics-server   /var/lib/rancher/rke2/server/manifests/rke2-metrics-server.yaml   4fe80fe31c6cc3d5f1cb7cd19f4d3181f1c810751ed995fa1ca5d9a560c436f8
rke2-runtimeclasses   /var/lib/rancher/rke2/server/manifests/rke2-runtimeclasses.yaml   7915375038cf0683ea2ee558d01adb9a478d1c51ec3f0a508b9082cd4f0a7145


## 인증서 관련 파일들
cat /var/lib/rancher/rke2/server/tls/server-ca.crt | openssl x509 -text -noout # k8s ca 인증서
cat /var/lib/rancher/rke2/server/tls/serving-kube-apiserver.crt | openssl x509 -text -noout # apiserver web server 인증서
        Issuer: CN=rke2-server-ca@1770533252
        Validity
            Not Before: Feb  8 06:47:32 2026 GMT
            Not After : Feb  8 06:47:32 2027 GMT
        Subject: CN=kube-apiserver
        ...
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Subject Alternative Name: 
                DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:localhost, DNS:k8s-node1, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1, IP Address:192.168.10.11, IP Address:192.168.10.11, IP Address:10.43.0.1


# data 디렉터리
tree /var/lib/rancher/rke2/data/
tree /var/lib/rancher/rke2/data/ -L 2
└── v1.34.3-rke2r3-5b8349de68df
    ├── bin        # 핵심 구성요소 관련 바이너리 파일들
    └── charts     # helm charts by helm controller

## helm chart manifests
cat /var/lib/rancher/rke2/data/v1.34.3-rke2r3-5b8349de68df/charts/rke2-coredns.yaml


# agent 디렉터리
tree /var/lib/rancher/rke2/agent/ | more
tree /var/lib/rancher/rke2/agent/ -L 3
├── client-ca.crt                             # client 관련 kubeconfig 및 인증서 파일들
...
├── containerd                                # containerd root 디렉터리 , (설정값) root = &quot;/var/lib/rancher/rke2/agent/containerd&quot;
│   ├── bin
│   ├── containerd.log
│   ├── io.containerd.content.v1.content
│   │   ├── blobs
│   │   └── ingest
    ...
│   └── tmpmounts
├── etc                                       # 주요 설정 파일
│   ├── containerd
│   │   └── config.toml
│   ├── crictl.yaml
│   └── kubelet.conf.d
│       └── 00-rke2-defaults.conf
├── images
│   ├── cloud-controller-manager-image.txt
│   ├── etcd-image.txt
│   ├── kube-apiserver-image.txt
│   ├── kube-controller-manager-image.txt
│   ├── kube-proxy-image.txt
│   ├── kube-scheduler-image.txt
│   └── runtime-image.txt
├── kubelet.kubeconfig                        # client 관련 kubeconfig 및 인증서 파일들
...
├── logs                                      # kubelet 로그
│   └── kubelet.log
├── pod-manifests                             # static pod manifests
│   ├── cloud-controller-manager.yaml
│   ├── etcd.yaml
│   ├── kube-apiserver.yaml
│   ├── kube-controller-manager.yaml
│   ├── kube-proxy.yaml
│   └── kube-scheduler.yaml
...

## crictl.yaml 파일 확인 : k3s 포함된 경로 확인!
crictl ps
cat /var/lib/rancher/rke2/agent/etc/crictl.yaml
runtime-endpoint: unix:///run/k3s/containerd/containerd.sock

ln -s /var/lib/rancher/rke2/agent/etc/crictl.yaml /etc/crictl.yaml
crictl ps
crictl images

## rke2 에서 config.toml 은 직접 수정 비권장 -&amp;gt; 직접 수정하면 rke2/k3s 재시작 시 덮어써짐!
cat /var/lib/rancher/rke2/agent/etc/containerd/config.toml
# File generated by rke2. DO NOT EDIT. Use config.toml.tmpl instead.
version = 3
root = &quot;/var/lib/rancher/rke2/agent/containerd&quot;
...
[plugins.'io.containerd.cri.v1.images'.registry]
  config_path = &quot;/var/lib/rancher/rke2/agent/etc/containerd/certs.d&quot;

## containerd registry 설정 디렉터리/파일이 현재는 없음
ls -l /var/lib/rancher/rke2/agent/etc/containerd/certs.d
ls: cannot access '/var/lib/rancher/rke2/agent/etc/containerd/certs.d': No such file or directory

## 컨테이너 이미지 명
grep -H '' /var/lib/rancher/rke2/agent/images/*
/var/lib/rancher/rke2/agent/images/etcd-image.txt:index.docker.io/rancher/hardened-etcd:v3.6.7-k3s1-build20260126
/var/lib/rancher/rke2/agent/images/kube-apiserver-image.txt:index.docker.io/rancher/hardened-kubernetes:v1.34.3-rke2r3-build20260127
/var/lib/rancher/rke2/agent/images/kube-controller-manager-image.txt:index.docker.io/rancher/hardened-kubernetes:v1.34.3-rke2r3-build20260127
/var/lib/rancher/rke2/agent/images/kube-proxy-image.txt:index.docker.io/rancher/hardened-kubernetes:v1.34.3-rke2r3-build20260127
/var/lib/rancher/rke2/agent/images/kube-scheduler-image.txt:index.docker.io/rancher/hardened-kubernetes:v1.34.3-rke2r3-build20260127
/var/lib/rancher/rke2/agent/images/runtime-image.txt:index.docker.io/rancher/rke2-runtime:v1.34.3-rke2r3

crictl images

## kubelet config : 보안 권장 설정 및 엔터프라이즈 권장 설정 반영되어 있음
cat /var/lib/rancher/rke2/agent/etc/kubelet.conf.d/00-rke2-defaults.conf
address: 192.168.10.11
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /var/lib/rancher/rke2/agent/client-ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
clusterDNS:
- 10.43.0.10
clusterDomain: cluster.local
containerRuntimeEndpoint: unix:///run/k3s/containerd/containerd.sock
cpuManagerReconcilePeriod: 10s
crashLoopBackOff: {}
evictionHard:
  imagefs.available: 5%
  nodefs.available: 5%
evictionMinimumReclaim:
  imagefs.available: 10%
  nodefs.available: 10%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: false                  # 원래 쿠버네티스는 스왑(Swap) 메모리가 켜져 있으면 실행되지 않지만, RKE2는 이를 허용하도록 설정되어 있습니다.
fileCheckFrequency: 20s
healthzBindAddress: 127.0.0.1
httpCheckFrequency: 20s
imageMaximumGCAge: 0s
imageMinimumGCAge: 2m0s
kind: KubeletConfiguration
logging:
  flushFrequency: 5s
  format: text
  options:
    json:
      infoBufferSize: &quot;0&quot;
    text:
      infoBufferSize: &quot;0&quot;
  verbosity: 0
memorySwap: {}
nodeStatusReportFrequency: 5m0s
nodeStatusUpdateFrequency: 10s     # 10초마다 &quot;나 살아있어요&quot;라고 컨트롤플레인 노드에 보고
resolvConf: /etc/resolv.conf
runtimeRequestTimeout: 2m0s
serializeImagePulls: false         # 이미지 병렬 pull 허용
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /var/lib/rancher/rke2/agent/pod-manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
tlsCertFile: /var/lib/rancher/rke2/agent/serving-kubelet.crt
tlsPrivateKeyFile: /var/lib/rancher/rke2/agent/serving-kubelet.key
volumeStatsAggPeriod: 1m0s

## kubelet 로그
tail -f /var/lib/rancher/rke2/agent/logs/kubelet.log&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Kubernetes</category>
      <category>c</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/54</guid>
      <comments>https://netoppa.tistory.com/54#entry54comment</comments>
      <pubDate>Sun, 22 Feb 2026 21:36:39 +0900</pubDate>
    </item>
    <item>
      <title>[6주차] Kubespray offline 설치</title>
      <link>https://netoppa.tistory.com/53</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 포스팅은 Cloud@net의 기시다님의 온라인 강좌 내용을 정리한 스터디 요약 자료입니다.&lt;/blockquote&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;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&amp;nbsp;Kubespray offline 설치&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&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;폐쇄망 환경 소개&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;보안이 요구되는 일반적인 기업망 구성&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;내부망 내부에서는 외부 인터넷 접속이 불가능하며, 필요 시 방화벽 정책 승인 후 Bastion Server 를 통해서 다운로드 가능하다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1347&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ntemy/dJMcadt8toI/PQ78ldN1Ka1uvWKVDO1Bek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ntemy/dJMcadt8toI/PQ78ldN1Ka1uvWKVDO1Bek/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ntemy/dJMcadt8toI/PQ78ldN1Ka1uvWKVDO1Bek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNtemy%2FdJMcadt8toI%2FPQ78ldN1Ka1uvWKVDO1Bek%2Fimg.jpg&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;1347&quot; height=&quot;236&quot; data-origin-width=&quot;1347&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&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; &lt;span data-token-index=&quot;0&quot;&gt;폐쇄망에서 서비스 동작을 위해 필요한 주요 구성요소&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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 data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;NTP (Network Time Protocol) Server&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,0,1&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,1,0,0&quot;&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,0,1,1,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; 로그 분석, 보안 인증(Certificate), 데이터 정합성 유지에 필수적입니다. 외부 표준시를 받아올 수 없으므로 내부 전용 장비(GPS 연동 등)를 통해 시간을 공급&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;DNS (Domain Name System) Server&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,1,1&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,1,1,0,0&quot;&gt;설명:&lt;/b&gt; 도메인 주소(예: registry.internal.local)를 실제 IP 주소로 변환&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,1,1,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; 하드코딩된 IP 대신 유연한 서비스 연결을 지원하며, 내부 서비스들의 위치를 식별하는 전화번호부 역할&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,2,0&quot;&gt;Network Gateway (IGW, NATGW)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,2,1&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,2,1,0,0&quot;&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,2,1,1,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; 내부망(Private)과 완충 구역(DMZ) 사이의 트래픽을 제어하며, 보안 정책에 따라 허용된 통신만 라우팅&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;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;Local (Mirror) YUM/DNF Repository&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,0,1&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,1,0,0&quot;&gt;설명:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;리눅스(CentOS, RHEL, Rocky 등) 소프트웨어 패키지(.rpm) 저장소의 로컬 복사&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,1,1,0&quot;&gt;주요 도구:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;reposync(외부 저장소 동기화), createrepo(메타데이터 생성).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,1,2,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;인터넷 연결 없이도 서버에 새로운 패키지를 설치하거나 보안 업데이트를 수행&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;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;Helm Artifact Repository (Server)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12,1,1&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,1,1,0,0&quot;&gt;설명:&lt;/b&gt; 쿠버네티스용 패키지 관리자인 Helm의 '차트(Chart, 설정 묶음)' 저장소&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,1,0&quot;&gt;주요 도구:&lt;/b&gt; ChartMuseum, zot (OCI 규격 지원).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,2,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; 복잡한 쿠버네티스 리소스 배포 설정을 버전별로 관리하여 일관된 배포를 지원&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;12,1,0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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 data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;Private PyPI (Python Package Index) Mirror&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16,0,1&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,1,0,0&quot;&gt;설명:&lt;/b&gt; Python 라이브러리(pip install) 저장소의 사설 미러&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,1,1,0&quot;&gt;주요 도구:&lt;/b&gt; Devpi.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,1,2,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; 외부 인터넷의 PyPI에 접속할 수 없는 개발자들이 필요한 라이브러리를 내부망에서 즉시 다운 가능&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;16,1,0&quot;&gt;Private Go Module Proxy&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16,1,1&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,1,1,0,0&quot;&gt;설명:&lt;/b&gt; Go 언어의 모듈(라이브러리)을 캐싱하고 제공하는 프록시 서버&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,1,1,0&quot;&gt;주요 도구:&lt;/b&gt; Athens.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,1,2,0&quot;&gt;폐쇄망 내 역할:&lt;/b&gt; Go 빌드 시 필요한 외부 모듈들을 내부에서 관리하여 빌드 속도를 높이고 외부 의존성을 제거&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;p data-path-to-node=&quot;12,1,0&quot; 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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;폐쇄망 서비스 실습&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn0I7s/dJMcafMkLhY/LStMcxUaIKij6Bji26kyEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn0I7s/dJMcafMkLhY/LStMcxUaIKij6Bji26kyEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn0I7s/dJMcafMkLhY/LStMcxUaIKij6Bji26kyEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn0I7s%2FdJMcafMkLhY%2FLStMcxUaIKij6Bji26kyEk%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;2048&quot; height=&quot;1117&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1117&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;p data-ke-size=&quot;size16&quot;&gt;일반적으로 &lt;span data-token-index=&quot;1&quot;&gt;DMZ 서버&lt;/span&gt; &amp;rarr; &lt;span data-token-index=&quot;3&quot;&gt;내부 admin서버&lt;/span&gt;(파일 가져오기, kubespary 실행)이지만, PC Spec과 시간 단축을 위해 &lt;span data-token-index=&quot;5&quot;&gt;1대 서버로 구성&lt;/span&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; Vagrantfile : admin 서버 120GB 용량 증설, k8s-node 2대(각각 Ctrl, Wk 1대씩) &lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771087330186&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Base Image  https://portal.cloud.hashicorp.com/vagrant/discover/bento/rockylinux-10.0
BOX_IMAGE = &quot;bento/rockylinux-10.0&quot; # &quot;bento/rockylinux-9&quot;
BOX_VERSION = &quot;202510.26.0&quot;
N = 2 # max number of Node

Vagrant.configure(&quot;2&quot;) do |config|

# Nodes 
  (1..N).each do |i|
    config.vm.define &quot;k8s-node#{i}&quot; do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider &quot;virtualbox&quot; do |vb|
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--groups&quot;, &quot;/Kubespary-offline-Lab&quot;]
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--nicpromisc2&quot;, &quot;allow-all&quot;]
        vb.name = &quot;k8s-node#{i}&quot;
        vb.cpus = 4
        vb.memory = 2048
        vb.linked_clone = true
      end
      subconfig.vm.host_name = &quot;k8s-node#{i}&quot;
      subconfig.vm.network &quot;private_network&quot;, ip: &quot;192.168.10.1#{i}&quot;
      subconfig.vm.network &quot;forwarded_port&quot;, guest: 22, host: &quot;6000#{i}&quot;, auto_correct: true, id: &quot;ssh&quot;
      subconfig.vm.synced_folder &quot;./&quot;, &quot;/vagrant&quot;, disabled: true
      subconfig.vm.provision &quot;shell&quot;, path: &quot;init_cfg.sh&quot; , args: [ N ]
    end
  end

# Admin Node
    config.vm.define &quot;admin&quot; do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider &quot;virtualbox&quot; do |vb|
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--groups&quot;, &quot;/Kubespary-offline-Lab&quot;]
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--nicpromisc2&quot;, &quot;allow-all&quot;]
        vb.name = &quot;admin&quot;
        vb.cpus = 4
        vb.memory = 2048
        vb.linked_clone = true
      end
      subconfig.vm.host_name = &quot;admin&quot;
      subconfig.vm.network &quot;private_network&quot;, ip: &quot;192.168.10.10&quot;
      subconfig.vm.network &quot;forwarded_port&quot;, guest: 22, host: &quot;60000&quot;, auto_correct: true, id: &quot;ssh&quot;
      subconfig.vm.synced_folder &quot;./&quot;, &quot;/vagrant&quot;, disabled: true
      subconfig.vm.disk :disk, size: &quot;120GB&quot;, primary: true # https://developer.hashicorp.com/vagrant/docs/disks/usage
      subconfig.vm.provision &quot;shell&quot;, path: &quot;admin.sh&quot; , args: [ N ]
    end

end&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; admin.sh &lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771087363917&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;


echo &quot;[TASK 1] Change Timezone and Enable NTP&quot;
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo &quot;[TASK 2] Disable firewalld and selinux&quot;
systemctl disable --now firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo &quot;[TASK 3] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
echo &quot;192.168.10.10 admin&quot; &amp;gt;&amp;gt; /etc/hosts
for (( i=1; i&amp;lt;=$1; i++  )); do echo &quot;192.168.10.1$i k8s-node$i&quot; &amp;gt;&amp;gt; /etc/hosts; done


echo &quot;[TASK 4] Delete default routing - enp0s9 NIC&quot; # setenforce 0 설정 필요
nmcli connection modify enp0s9 ipv4.never-default yes
nmcli connection up enp0s9 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 5] Config net.ipv4.ip_forward&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/sysctl.d/99-ipforward.conf
net.ipv4.ip_forward = 1
EOF
sysctl --system  &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 6] Install packages&quot;
dnf install -y python3-pip git sshpass cloud-utils-growpart &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 7] Install Helm&quot;
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | DESIRED_VERSION=v3.20.0 bash &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 8] Increase Disk Size&quot;
growpart /dev/sda 3 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 # lsblk
xfs_growfs /dev/sda3 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 # df -hT /


echo &quot;[TASK 9] Setting SSHD&quot;
echo &quot;root:qwe123&quot; | chpasswd

cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF
systemctl restart sshd &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 10] Setting SSH Key&quot;
ssh-keygen -t rsa -N &quot;&quot; -f /root/.ssh/id_rsa &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
sshpass -p 'qwe123' ssh-copy-id -o StrictHostKeyChecking=no root@192.168.10.10  &amp;gt;/dev/null 2&amp;gt;&amp;amp;1  # cat /root/.ssh/authorized_keys
ssh -o StrictHostKeyChecking=no root@admin-lb hostname &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
for (( i=1; i&amp;lt;=$1; i++  )); do sshpass -p 'qwe123' ssh-copy-id -o StrictHostKeyChecking=no root@192.168.10.1$i &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 ; done
for (( i=1; i&amp;lt;=$1; i++  )); do sshpass -p 'qwe123' ssh -o StrictHostKeyChecking=no root@k8s-node$i hostname &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 ; done


echo &quot;[TASK 11] Install K9s&quot;
CLI_ARCH=amd64
if [ &quot;$(uname -m)&quot; = &quot;aarch64&quot; ]; then CLI_ARCH=arm64; fi
wget -P /tmp https://github.com/derailed/k9s/releases/latest/download/k9s_linux_${CLI_ARCH}.tar.gz  &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
tar -xzf /tmp/k9s_linux_${CLI_ARCH}.tar.gz -C /tmp
chown root:root /tmp/k9s
mv /tmp/k9s /usr/local/bin/
chmod +x /usr/local/bin/k9s


echo &quot;[TASK 12] ETC&quot;
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc


echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; init_cfg.sh &lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771087383527&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;


echo &quot;[TASK 1] Change Timezone and Enable NTP&quot;
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo &quot;[TASK 2] Disable firewalld and selinux&quot;
systemctl disable --now firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo &quot;[TASK 3] Disable and turn off SWAP &amp;amp; Delete swap partitions&quot;
swapoff -a
sed -i '/swap/d' /etc/fstab
sfdisk --delete /dev/sda 2 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
partprobe /dev/sda &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 4] Config kernel &amp;amp; module&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/modules-load.d/k8s.conf
overlay
br_netfilter
vxlan
EOF
modprobe overlay &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
modprobe br_netfilter &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

cat &amp;lt;&amp;lt; EOF &amp;gt;/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 5] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
echo &quot;192.168.10.10 admin&quot; &amp;gt;&amp;gt; /etc/hosts
for (( i=1; i&amp;lt;=$1; i++  )); do echo &quot;192.168.10.1$i k8s-node$i&quot; &amp;gt;&amp;gt; /etc/hosts; done


echo &quot;[TASK 6] Delete default routing - enp0s9 NIC&quot; # setenforce 0 설정 필요
nmcli connection modify enp0s9 ipv4.never-default yes
nmcli connection up enp0s9 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 7] Setting SSHD&quot;
echo &quot;root:qwe123&quot; | chpasswd

cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF
systemctl restart sshd &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 8] Install packages&quot;
dnf install -y python3-pip git &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 9] ETC&quot;
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc


echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1771087396064&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir k8s-offline
cd k8s-offline

curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-kubespary-offline/Vagrantfile
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-kubespary-offline/admin.sh
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-kubespary-offline/init_cfg.sh

vagrant up
vagrant status

# admin, k8s-node1, k8s-node2 각각 접속 : 호스트 OS에 sshpass가 없을 경우 ssh로 root로 접속 후 암호 qwe123 입력
sshpass -p 'qwe123' ssh root@192.168.10.10 # ssh root@192.168.10.10
sshpass -p 'qwe123' ssh root@192.168.10.11 # ssh root@192.168.10.11
sshpass -p 'qwe123' ssh root@192.168.10.12 # ssh root@192.168.10.12&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;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;☞ [참고] NetworkManager 기본 정보&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771087431628&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# admin-lb
ip -c route
default via 10.0.2.2 dev enp0s8 proto dhcp src 10.0.2.15 metric 100
10.0.2.0/24 dev enp0s8 proto kernel scope link src 10.0.2.15 metric 100
192.168.10.0/24 dev enp0s9 proto kernel scope link src 192.168.10.10 metric 101

# enp0s8
cat /etc/NetworkManager/system-connections/enp0s8.nmconnection
[connection]
id=enp0s8
uuid=7f94e839-e070-4bfe-9330-07090381d89f
type=ethernet
interface-name=enp0s8

[ethernet]

[ipv4]
method=auto                  # DHCP 서버로부터 IP 주소, 서브넷 마스크, 게이트웨이 정보를 자동으로 할당받습니다.
...

# enp0s9
cat /etc/NetworkManager/system-connections/enp0s9.nmconnection
[connection]
id=enp0s9
uuid=74ceb4c4-b514-4afd-bc84-7bdc7767b649
type=ethernet
autoconnect-priority=-100    # 자동 연결 우선순위가 낮게 설정되어 있습니다.
autoconnect-retries=1        # 실패 시 1회만 재시도
interface-name=enp0s9
timestamp=1769701094

[ethernet]
mac-address=08:00:27:86:00:C2

[ipv4]
address1=192.168.10.10/24
method=manual
never-default=true           # 절대 Default Route 생성 안 함
...

# NetworkManager.service : 실제 네트워크 장치를 관리하고 IP를 할당하는 핵심 데몬입니다. D-Bus를 통해 시스템과 통신합니다.
systemctl status NetworkManager.service --no-pager
cat /usr/lib/systemd/system/NetworkManager.service
[Unit]
Description=Network Manager
Documentation=man:NetworkManager(8)
Wants=network.target
After=network-pre.target dbus.service   # DBus 준비 후 NetworkManager 시작
Before=network.target                   # network.target 이전에 네트워크 구성 완료
BindsTo=dbus.service

[Service]
Type=dbus
BusName=org.freedesktop.NetworkManager
ExecReload=/usr/bin/busctl call org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager Reload u 0
ExecStart=/usr/sbin/NetworkManager --no-daemon
Restart=on-failure
KillMode=process
TimeoutStartSec=600
CapabilityBoundingSet=CAP_NET_ADMIN CAP_DAC_OVERRIDE CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_MODULE CAP_AUDIT_WRITE CAP_KILL CAP_SYS_CHROOT
ProtectSystem=true
ProtectHome=read-only
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
Also=NetworkManager-dispatcher.service
Also=NetworkManager-wait-online.service

# NetworkManager-wait-online.service : 시스템 부팅 시 네트워크가 완전히 연결될 때까지 다른 서비스들의 시작을 잠시 대기시키는 역할을 합니다. (oneshot 타입)
systemctl status NetworkManager-wait-online.service --no-pager
cat /usr/lib/systemd/system/NetworkManager-wait-online.service
[Unit]
Description=Network Manager Wait Online
Documentation=man:NetworkManager-wait-online.service(8)
Requires=NetworkManager.service
After=NetworkManager.service
Before=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/nm-online -s -q
RemainAfterExit=yes
Environment=NM_ONLINE_TIMEOUT=60

[Install]
WantedBy=network-online.target

# NetworkManager-dispatcher.service : 인터페이스가 UP(활성화)되거나 DOWN(비활성화)될 때, /etc/NetworkManager/dispatcher.d/에 있는 사용자 정의 스크립트를 자동으로 실행.
## 발동 조건 : 인터페이스 up/down, IP 변경, DHCP 갱신
systemctl status NetworkManager-dispatcher.service --no-pager
cat /usr/lib/systemd/system/NetworkManager-dispatcher.service
[Unit]
Description=Network Manager Script Dispatcher Service
Documentation=man:NetworkManager-dispatcher.service(8)

[Service]
Type=dbus
BusName=org.freedesktop.nm_dispatcher
ExecStart=/usr/libexec/nm-dispatcher
NotifyAccess=main
KillMode=process

[Install]
Alias=dbus-org.freedesktop.nm-dispatcher.service

# 디렉터리 내에 파일 없는 상태
tree /etc/NetworkManager/dispatcher.d/
/etc/NetworkManager/dispatcher.d/
├── no-wait.d
├── pre-down.d
└── pre-up.d
4 directories, 0 files&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;kubespary offline 설치 소개&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;&lt;span data-token-index=&quot;0&quot;&gt;kubespray&lt;/span&gt;는 오프라인 환경에서 k8s를 &lt;span data-token-index=&quot;2&quot;&gt;배포를 위한 편의성&lt;/span&gt;을 지원 &lt;/b&gt;&lt;/h3&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;컨테이너 이미지 목록&lt;/b&gt;을 파일별로 생성&lt;/li&gt;
&lt;li&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;파일&lt;/b&gt;(목록)을 &lt;b&gt;다운로드&lt;/b&gt; 하고 &lt;b&gt;Nginx 컨테이너를&lt;/b&gt; 실행하여, &lt;b&gt;파일 다운로드 기능&lt;/b&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #5733b1; color: #ffffff;&quot;&gt;&lt;b&gt;&amp;nbsp;kubespary-offline 설치 실습&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;☞ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;git clone 후 download-all.sh 로 설치에 필요한 파일들 다운로드 수행 (3.3GB 정도) 17분 소요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - download-all.sh&lt;/span&gt; : 아래 후속 실행되는 스크립트들 확인&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771087769447&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

run() {
    echo &quot;=&amp;gt; Running: $*&quot;
    $* || {
        echo &quot;Failed in : $*&quot;
        exit 1
    }
}

source ./config.sh

#run ./install-docker.sh
#run ./install-nerdctl.sh
run ./precheck.sh
run ./prepare-pkgs.sh || exit 1
run ./prepare-py.sh
run ./get-kubespray.sh
if $ansible_in_container; then
    run ./build-ansible-container.sh
else
    run ./pypi-mirror.sh
fi
run ./download-kubespray-files.sh
run ./download-additional-containers.sh
run ./create-repo.sh
run ./copy-target-scripts.sh

echo &quot;Done.&quot;&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;config.sh &amp;rarr; target-scripts/config.sh : 설치되는 버전 정보 변수 설정 :&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;1&quot;&gt; 버전 변경 시에는 이 단계에서 수정 필요!, 버전 변수에 파일 다운로드 됨&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107769150&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

source ./target-scripts/config.sh

# container runtime for preparation node
docker=${docker:-podman}
#docker=${docker:-docker}
#docker=${docker:-/usr/local/bin/nerdctl}

# Run ansible in container?
ansible_in_container=${ansible_in_container:-false}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771107773093&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;cat ./target-scripts/config.sh

#!/bin/bash
# Kubespray version to download. Use &quot;master&quot; for latest master branch.
KUBESPRAY_VERSION=${KUBESPRAY_VERSION:-2.30.0}
#KUBESPRAY_VERSION=${KUBESPRAY_VERSION:-master}

# Versions of containerd related binaries used in `install-containerd.sh`
# These version must be same as kubespray.
# Refer `roles/kubespray_defaults/vars/main/checksums.yml` of kubespray.
RUNC_VERSION=1.3.4
CONTAINERD_VERSION=2.2.1
NERDCTL_VERSION=2.2.1
CNI_VERSION=1.8.0

# Some container versions, must be same as ../imagelists/images.txt
NGINX_VERSION=1.29.4
REGISTRY_VERSION=3.0.0

# container registry port
REGISTRY_PORT=${REGISTRY_PORT:-35000}

# Additional container registry hosts
ADDITIONAL_CONTAINER_REGISTRY_LIST=${ADDITIONAL_CONTAINER_REGISTRY_LIST:-&quot;myregistry.io&quot;}

# Architecture of binary files
# Detect OS type and get architecture accordingly
map_arch() {
    case &quot;$1&quot; in
        x86_64)
            echo &quot;amd64&quot;
            ;;
        aarch64)
            echo &quot;arm64&quot;
            ;;
        *)
            echo &quot;$1&quot;
            ;;
    esac
}

if [ -e /etc/redhat-release ]; then
    # RHEL/AlmaLinux/Rocky Linux
    ARCH=$(uname -m)
    IMAGE_ARCH=$(map_arch &quot;$ARCH&quot;)
elif command -v dpkg &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    # Ubuntu/Debian
    IMAGE_ARCH=$(dpkg --print-architecture)
else
    # Fallback: use uname -m
    ARCH=$(uname -m)
    IMAGE_ARCH=$(map_arch &quot;$ARCH&quot;)
fi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;precheck.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107806828&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

source /etc/os-release
source ./config.sh

if [ &quot;$docker&quot; != &quot;podman&quot; ]; then
    if ! command -v $docker &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
        echo &quot;No $docker installed&quot;
        exit 1
    fi
fi

if [ -e /etc/redhat-release ] &amp;amp;&amp;amp; [[ &quot;$VERSION_ID&quot; =~ ^7.* ]]; then
    if [ &quot;$(getenforce)&quot; == &quot;Enforcing&quot; ]; then
        echo &quot;You must disable SELinux for RHEL7/CentOS7&quot;
        exit 1
    fi
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prepare-pkgs.sh : dnf&amp;nbsp;install&amp;nbsp;-y&amp;nbsp;rsync&amp;nbsp;gcc&amp;nbsp;libffi-devel&amp;nbsp;createrepo&amp;nbsp;git&amp;nbsp;podman&amp;nbsp;createrepo_c&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107859137&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

echo &quot;==&amp;gt; prepare-pkgs.sh&quot;

. /etc/os-release
. ./scripts/common.sh

# Select python version
. ./target-scripts/pyver.sh

# Install required packages
if [ -e /etc/redhat-release ]; then
    echo &quot;==&amp;gt; Install required packages&quot;
    $sudo dnf check-update

    $sudo dnf install -y rsync gcc libffi-devel createrepo git podman || exit 1

    case &quot;$VERSION_ID&quot; in
        7*)
            # RHEL/CentOS 7
            echo &quot;FATAL: RHEL/CentOS 7 is not supported anymore.&quot;
            exit 1
            ;;
        8*)
            # RHEL/CentOS 8
            if ! command -v repo2module &amp;gt;/dev/null; then
                echo &quot;==&amp;gt; Install modulemd-tools&quot;
                $sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
                $sudo dnf copr enable -y frostyx/modulemd-tools-epel
                $sudo dnf install -y modulemd-tools
            fi
            ;;
        9*)
            # RHEL 9
            if ! command -v repo2module &amp;gt;/dev/null; then
                $sudo dnf install -y modulemd-tools
            fi
            ;;
        10*)
            # RHEL 9
            if ! command -v repo2module &amp;gt;/dev/null; then
                $sudo dnf install -y createrepo_c
            fi
            ;;
        *)
            echo &quot;Unknown version_id: $VERSION_ID&quot;
            exit 1
            ;;
    esac

    # Install python
    $sudo dnf install -y python${PY} python${PY}-pip python${PY}-devel || exit 1
else
    $sudo apt update
    if [ &quot;$1&quot; == &quot;--upgrade&quot; ]; then
        $sudo apt upgrade
    fi
    $sudo apt -y install lsb-release curl gpg gcc libffi-dev rsync git software-properties-common || exit 1

    case &quot;$VERSION_ID&quot; in
        20.04)
            # Prepare for podman
            echo &quot;deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /&quot; | $sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
            curl -SL https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_${VERSION_ID}/Release.key | $sudo apt-key add -

            # Prepare for latest python3
            sudo add-apt-repository ppa:deadsnakes/ppa -y || exit 1
            $sudo apt update
            ;;
    esac
    $sudo apt install -y python${PY} python${PY}-venv python${PY}-dev python3-pip python3-selinux podman || exit 1
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prepare-py.sh&amp;nbsp;&amp;rarr;&amp;nbsp;target-scripts/venv.sh&amp;nbsp;:&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;-U&amp;nbsp;pip&amp;nbsp;setuptools&amp;nbsp;&amp;amp;&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;-r&amp;nbsp;requirements.txt&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107905470&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

# Create python3 env

echo &quot;==&amp;gt; prepare-py.sh&quot;

. /etc/os-release

. ./target-scripts/venv.sh

source ./scripts/set-locale.sh

echo &quot;==&amp;gt; Update pip, etc&quot;
pip install -U pip setuptools
#if [ &quot;$(getenforce)&quot; == &quot;Enforcing&quot; ]; then
#    pip install -U selinux
#fi

echo &quot;==&amp;gt; Install python packages&quot;
pip install -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771107924655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat target-scripts/venv.sh

#!/bin/bash

source /etc/os-release

# Select python version
source &quot;$(dirname &quot;${BASH_SOURCE[0]}&quot;)/pyver.sh&quot;

python3=python${PY}
VENV_DIR=${VENV_DIR:-~/.venv/${PY}}

echo &quot;python3 = $python3&quot;
echo &quot;VENV_DIR = ${VENV_DIR}&quot;
if [ ! -e ${VENV_DIR} ]; then
    $python3 -m venv ${VENV_DIR}
fi
source ${VENV_DIR}/bin/activate&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get-kubespray.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107949346&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

CURRENT_DIR=$(cd $(dirname $0); pwd)
source config.sh

KUBESPRAY_TARBALL=kubespray-${KUBESPRAY_VERSION}.tar.gz

KUBESPRAY_DIR=./cache/kubespray-${KUBESPRAY_VERSION}

umask 022

mkdir -p ./cache
mkdir -p outputs/files/

remove_kubespray_cache_dir() {
    if [ -e ${KUBESPRAY_DIR} ]; then
        /bin/rm -rf ${KUBESPRAY_DIR}
    fi
}

# If KUBESPRAY_VERSION looks like a git commit hash, check out that commit
if [[ $KUBESPRAY_VERSION =~ ^[0-9a-f]{7,40}$ ]]; then
    remove_kubespray_cache_dir
    echo &quot;===&amp;gt; Checkout kubespray commit: $KUBESPRAY_VERSION&quot;

    git clone https://github.com/kubernetes-sigs/kubespray.git ${KUBESPRAY_DIR}
    cd ${KUBESPRAY_DIR}
    git checkout $KUBESPRAY_VERSION || {
        echo &quot;Error: commit $KUBESPRAY_VERSION not found&quot;
        exit 1
    }
    cd - &amp;gt;/dev/null

    tar czf outputs/files/${KUBESPRAY_TARBALL} -C ./cache kubespray-${KUBESPRAY_VERSION}
    echo &quot;Done (commit checkout).&quot;
    exit 0
fi

if [ $KUBESPRAY_VERSION == &quot;master&quot; ] || [[ $KUBESPRAY_VERSION =~ ^release- ]]; then
    remove_kubespray_cache_dir
    echo &quot;===&amp;gt; Checkout kubespray branch : $KUBESPRAY_VERSION&quot;
    if [ ! -e ${KUBESPRAY_DIR} ]; then
        git clone -b $KUBESPRAY_VERSION https://github.com/kubernetes-sigs/kubespray.git ${KUBESPRAY_DIR}
        tar czf outputs/files/${KUBESPRAY_TARBALL} -C ./cache kubespray-${KUBESPRAY_VERSION}
    fi
    exit 0
fi


if [ ! -e outputs/files/${KUBESPRAY_TARBALL} ]; then
    echo &quot;===&amp;gt; Download ${KUBESPRAY_TARBALL}&quot;
    curl -SL https://github.com/kubernetes-sigs/kubespray/archive/refs/tags/v${KUBESPRAY_VERSION}.tar.gz &amp;gt;outputs/files/${KUBESPRAY_TARBALL} || exit 1

    remove_kubespray_cache_dir
fi

if [ ! -e ${KUBESPRAY_DIR} ]; then
    echo &quot;===&amp;gt; Extract ${KUBESPRAY_TARBALL}&quot;
    tar xzf outputs/files/${KUBESPRAY_TARBALL}

    mv kubespray-${KUBESPRAY_VERSION} ${KUBESPRAY_DIR}

    sleep 1  # ad hoc, for vagrant shared directory

    # Apply patches
    patch_dir=${CURRENT_DIR}/target-scripts/patches/${KUBESPRAY_VERSION}
    if [ -d $patch_dir ]; then
        for patch in ${patch_dir}/*.patch; do
            echo &quot;===&amp;gt; Apply patch $patch&quot;
            (cd $KUBESPRAY_DIR &amp;amp;&amp;amp; patch -p1 &amp;lt; $patch) || exit 1
        done
    fi
fi

echo &quot;Done.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pypi-mirror.sh&amp;nbsp;:&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;-U&amp;nbsp;pip&amp;nbsp;python-pypi-mirror&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771107985054&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

source config.sh

KUBESPRAY_DIR=./cache/kubespray-${KUBESPRAY_VERSION}
if [ ! -e $KUBESPRAY_DIR ]; then
    echo &quot;No kubespray dir at $KUBESPRAY_DIR&quot;
    exit 1
fi

source /etc/os-release

source ./target-scripts/venv.sh

source ./scripts/set-locale.sh

umask 022

echo &quot;==&amp;gt; Create pypi mirror for kubespray&quot;
#set -x
pip install -U pip python-pypi-mirror

DEST=&quot;-d outputs/pypi/files&quot;
PLATFORM=&quot;--platform manylinux2014_$(uname -m)&quot;  # PEP-599
#PLATFORM=&quot;--platform manylinux_2_17_$(uname -m)&quot;  # PEP-600

REQ=requirements.tmp
#sed &quot;s/^ansible/#ansible/&quot; ${KUBESPRAY_DIR}/requirements.txt &amp;gt; $REQ  # Ansible does not provide binary packages
cp ${KUBESPRAY_DIR}/requirements.txt $REQ
echo &quot;PyYAML&quot; &amp;gt;&amp;gt; $REQ  # Ansible dependency
echo &quot;ruamel.yaml&quot; &amp;gt;&amp;gt; $REQ # Inventory builder

for pyver in 3.11 3.12; do
    echo &quot;===&amp;gt; Download binary for python $pyver&quot;
    pip download $DEST --only-binary :all: --python-version $pyver $PLATFORM -r $REQ || exit 1
done
/bin/rm $REQ

echo &quot;===&amp;gt; Download source packages&quot;
pip download $DEST --no-binary :all: -r ${KUBESPRAY_DIR}/requirements.txt

echo &quot;===&amp;gt; Download pip, setuptools, wheel, etc&quot;
pip download $DEST pip setuptools wheel || exit 1
pip download $DEST pip setuptools==40.9.0 || exit 1  # For RHEL...

echo &quot;===&amp;gt; Download additional packages&quot;
PKGS=selinux  # need for SELinux (#4)
PKGS=&quot;$PKGS flit_core&quot;  # build dependency of pyparsing (#6)
PKGS=&quot;$PKGS cython&amp;lt;3&quot;  # PyYAML requires Cython with python 3.10 (ubuntu 22.04)
pip download $DEST pip $PKGS || exit 1

pypi-mirror create $DEST -m outputs/pypi

echo &quot;pypi-mirror.sh done&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;download-kubespray-files.sh*&amp;nbsp;:&amp;nbsp;파일과&amp;nbsp;이미지&amp;nbsp;목록&amp;nbsp;작성&amp;nbsp;&amp;rarr;&amp;nbsp;download-images.sh&amp;nbsp;실행&amp;nbsp;&amp;rArr;&amp;nbsp;kubespary에&amp;nbsp;contrib/offline/generate_list.sh&amp;nbsp;사용&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108030123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

umask 022

source ./config.sh
source scripts/common.sh
source scripts/images.sh

KUBESPRAY_DIR=./cache/kubespray-${KUBESPRAY_VERSION}
if [ ! -e $KUBESPRAY_DIR ]; then
    echo &quot;No kubespray dir at $KUBESPRAY_DIR&quot;
    exit 1
fi

FILES_DIR=outputs/files

# Decide relative directory of file from URL
#
# kubernetes/vx.x.x        : kubeadm/kubectl/kubelet
# kubernetes/etcd          : etcd
# kubernetes/cni           : CNI plugins
# kubernetes/cri-tools     : crictl
# kubernetes/calico/vx.x.x : calico
# kubernetes/calico        : calicoctl
# runc/vx.x.x              : runc
# cilium-cli/vx.x.x        : cilium-cli
# gvisor/{ver}/{arch}      : gvisor (sunrc, containerd-shim)
# scopeo/vx.x.x            : scopeo
# yq/vx.x.x                : yq
#
decide_relative_dir() {
    local url=$1
    local rdir
    rdir=$url
    rdir=$(echo $rdir | sed &quot;s@.*/\(v[0-9.]*\)/.*/kube\(adm\|ctl\|let\)@kubernetes/\1@g&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/etcd-.*.tar.gz@kubernetes/etcd@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/cni-plugins.*.tgz@kubernetes/cni@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/crictl-.*.tar.gz@kubernetes/cri-tools@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\(v.*\)/calicoctl-.*@kubernetes/calico/\1@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\(v.*\)/runc.${IMAGE_ARCH}@runc/\1@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\(v.*\)/cilium-linux-.*@cilium-cli/\1@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\([^/]*\)/\([^/]*\)/runsc@gvisor/\1/\2@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\([^/]*\)/\([^/]*\)/containerd-shim-runsc-v1@gvisor/\1/\2@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\(v[^/]*\)/skopeo-linux-.*@skopeo/\1@&quot;)
    rdir=$(echo $rdir | sed &quot;s@.*/\(v[^/]*\)/yq_linux_*@yq/\1@&quot;)
    if [ &quot;$url&quot; != &quot;$rdir&quot; ]; then
        echo $rdir
        return
    fi

    rdir=$(echo $rdir | sed &quot;s@.*/calico/.*@kubernetes/calico@&quot;)
    if [ &quot;$url&quot; != &quot;$rdir&quot; ]; then
        echo $rdir
    else
        echo &quot;&quot;
    fi
}

get_url() {
    url=$1
    filename=&quot;${url##*/}&quot;

    rdir=$(decide_relative_dir $url)

    if [ -n &quot;$rdir&quot; ]; then
        if [ ! -d $FILES_DIR/$rdir ]; then
            mkdir -p $FILES_DIR/$rdir
        fi
    else
        rdir=&quot;.&quot;
    fi

    if [ ! -e $FILES_DIR/$rdir/$filename ]; then
        echo &quot;==&amp;gt; Download $url&quot;
        for i in {1..3}; do
            curl --location --show-error --fail --output $FILES_DIR/$rdir/$filename $url &amp;amp;&amp;amp; return
            echo &quot;curl failed. Attempt=$i&quot;
        done
        echo &quot;Download failed, exit : $url&quot;
        exit 1
    else
        echo &quot;==&amp;gt; Skip $url&quot;
    fi
}

# execute offline generate_list.sh
generate_list() {
    #if [ $KUBESPRAY_VERSION == &quot;2.18.0&quot; ]; then
    #    export containerd_version=${containerd_version:-1.5.8}
    #    export host_os=linux
    #    export image_arch=amd64
    #fi
    LANG=C /bin/bash ${KUBESPRAY_DIR}/contrib/offline/generate_list.sh || exit 1

    #if [ $KUBESPRAY_VERSION == &quot;2.18.0&quot; ]; then
    #    # check roles/download/default/main.yml to decide version
    #    snapshot_controller_tag=${snapshot_controller_tag:-v4.2.1}
    #    sed -i &quot;s@\(.*/snapshot-controller:\)@\1${snapshot_controller_tag}@&quot; ${KUBESPRAY_DIR}/contrib/offline/temp/images.list || exit 1
    #fi
}

. ./target-scripts/venv.sh

generate_list # 목록 작성 스크립트 실행

mkdir -p $FILES_DIR

cp ${KUBESPRAY_DIR}/contrib/offline/temp/files.list $FILES_DIR/
cp ${KUBESPRAY_DIR}/contrib/offline/temp/images.list $IMAGES_DIR/

# download files : 파일 다운로드
files=$(cat ${FILES_DIR}/files.list)
for i in $files; do
    get_url $i
done

# download images : 컨테너 (이미지) 다운로드
./download-images.sh || exit 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;download-images.sh : 기본 (컨테이너) 이미지 다운로드 실행&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108058702&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

source ./config.sh
source scripts/common.sh
source scripts/images.sh

if [ &quot;$SKIP_DOWNLOAD_IMAGES&quot; = &quot;true&quot; ]; then
    exit 0
fi

if [ ! -e &quot;${IMAGES_DIR}/images.list&quot; ]; then
    echo &quot;${IMAGES_DIR}/images.list does not exist. Run download-kubespray-files.sh first.&quot;
    exit 1
fi

# download images
images=$(cat ${IMAGES_DIR}/images.list)
for i in $images; do
    get_image $i
done&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;download-additional-containers.sh : 추가 (컨테이너) 이미지 다운로드 실행 - nginx, registry&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108077592&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

if [ &quot;$SKIP_DOWNLOAD_IMAGES&quot; = &quot;true&quot; ]; then
    exit 0
fi

echo &quot;==&amp;gt; Pull additional container images&quot;

umask 022

source ./config.sh
source scripts/common.sh
source scripts/images.sh

cat imagelists/*.txt | sed &quot;s/#.*$//g&quot; | sort -u &amp;gt; $IMAGES_DIR/additional-images.list
cat $IMAGES_DIR/additional-images.list

IMAGES=$(cat $IMAGES_DIR/additional-images.list)

for image in $IMAGES; do
    image=$(expand_image_repo $image)
    get_image $image
done&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create-repo.sh : scripts/create-repo-rhel.sh 후속 실행&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108097129&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

if [ -e /etc/redhat-release ]; then
    ./scripts/create-repo-rhel.sh || exit 1
else
    ./scripts/create-repo-ubuntu.sh || exit 1
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scripts/create-repo-rhel.sh&amp;nbsp;:&amp;nbsp;createrepo&amp;nbsp;outputs/rpms/local&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108129669&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

umask 022

. /etc/os-release

REQUIRE_MODULE=false

VERSION_MAJOR=$VERSION_ID
case &quot;${VERSION_MAJOR}&quot; in
    8*)
        REQUIRE_MODULE=true
        VERSION_MAJOR=8
        ;;
    9*)
        REQUIRE_MODULE=true
        VERSION_MAJOR=9
        ;;
    10*)
        VERSION_MAJOR=10
        ;;
    *)
        echo &quot;Unsupported version: $VERSION_MAJOR&quot;
        ;;
esac

# packages
PKGS=$(cat pkglist/rhel/*.txt pkglist/rhel/${VERSION_MAJOR}/*.txt | grep -v &quot;^#&quot; | sort | uniq)

CACHEDIR=cache/cache-rpms
mkdir -p $CACHEDIR

RT=&quot;sudo dnf download --resolve --alldeps --downloaddir $CACHEDIR&quot;

echo &quot;==&amp;gt; Downloading: &quot; $PKGS
$RT $PKGS || {
    echo &quot;Download error&quot;
    exit 1
}

# create rpms dir
RPMDIR=outputs/rpms/local
if [ -e $RPMDIR ]; then
    /bin/rm -rf $RPMDIR || exit 1
fi
mkdir -p $RPMDIR
/bin/cp $CACHEDIR/*.rpm $RPMDIR/
/bin/rm $RPMDIR/*.i686.rpm

echo &quot;==&amp;gt; createrepo&quot;
createrepo $RPMDIR || exit 1

#Wait a second to avoid error on Vagrant
sleep 1

if $REQUIRE_MODULE; then
    cd $RPMDIR
    #createrepo_c . || exit 1
    echo &quot;==&amp;gt; repo2module&quot;
    LANG=C repo2module -s stable . modules.yaml || exit 1
    echo &quot;==&amp;gt; modifyrepo&quot;
    modifyrepo_c --mdtype=modules modules.yaml repodata/ || exit 1
fi

echo &quot;create-repo done.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;copy-target-scripts.sh&amp;nbsp;:&amp;nbsp;cp&amp;nbsp;-f&amp;nbsp;-r&amp;nbsp;target-scripts/*&amp;nbsp;outputs/&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108154128&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 60G -&amp;gt; 120G 증설 확인
lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   120G  0 disk
├─sda1   8:1    0   600M  0 part /boot/efi
├─sda2   8:2    0   3.8G  0 part [SWAP]
└─sda3   8:3    0 115.6G  0 part /

df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sda3      xfs   116G  5.5G  111G   5% /


# git clone
git clone https://github.com/kubespray-offline/kubespray-offline
tree kubespray-offline/
cd kubespray-offline/

# 변수 정보 확인 : nginx 와 registry 는 각각 1.29.4 와 3.0.0 변수 선언 확인 &amp;lt;- 버전 변경 시에는 이 단계에서 수정 필요!
source ./config.sh
echo -e &quot;kubespary $KUBESPRAY_VERSION&quot;
echo -e &quot;runc $RUNC_VERSION&quot;
echo -e &quot;containerd $CONTAINERD_VERSION&quot;
echo -e &quot;nercdtl $NERDCTL_VERSION&quot;
echo -e &quot;cni $CNI_VERSION&quot;
echo -e &quot;nginx $NGINX_VERSION&quot;
echo -e &quot;registry $REGISTRY_VERSION&quot;
echo -e &quot;registry_port: $REGISTRY_PORT&quot;
echo -e &quot;Additional container registry hosts: $ADDITIONAL_CONTAINER_REGISTRY_LIST&quot;
echo -e &quot;cpu arch: $IMAGE_ARCH&quot;

# 17분 소요
cat download-all.sh
./download-all.sh
...
(230/230): container-selinux-2.240.0-1.el10.noarch.rpm                                    4.6 kB/s |  56 kB     00:12    
/bin/rm: cannot remove 'outputs/rpms/local/*.i686.rpm': No such file or directory # 32비트 RPM 파일이 애초에 없어서 지울 게 없음. 오류 아니여서 무시해도됨.
==&amp;gt; createrepo
Directory walk started
Directory walk done - 230 packages
Temporary output repo path: outputs/rpms/local/.repodata/
Pool started (with 5 workers)
Pool finished
create-repo done.
=&amp;gt; Running: ./copy-target-scripts.sh
==&amp;gt; Copy target scripts
Done.

# venv 디렉터리 확인
du -sh ~/.venv
490M	/root/.venv

tree ~/.venv | more
/root/.venv
└── 3.12
    ├── bin
    │&amp;nbsp;&amp;nbsp; ├── activate
    │&amp;nbsp;&amp;nbsp; ├── activate.csh
    │&amp;nbsp;&amp;nbsp; ├── activate.fish
    │&amp;nbsp;&amp;nbsp; ├── Activate.ps1
    │&amp;nbsp;&amp;nbsp; ├── ansible
    ...

# /root/.cache 디렉터리 확인
tree ~/.cache | more
du -sh ~/.cache
814M	/root/.cache

# 다운로드 될 파일과 이미지 생성 스크립트는 kubespary repo 에 offline 참고 확인
tree /root/kubespray-offline/cache/kubespray-2.30.0/contrib/offline/
cache/kubespray-2.30.0/contrib/offline/
├── docker-daemon.json
├── generate_list.sh
├── generate_list.yml
├── manage-offline-container-images.sh
├── manage-offline-files.sh
├── nginx.conf
├── README.md
├── registries.conf
├── temp
│&amp;nbsp;&amp;nbsp; ├── files.list*
│&amp;nbsp;&amp;nbsp; ├── files.list.template
│&amp;nbsp;&amp;nbsp; ├── images.list*
│&amp;nbsp;&amp;nbsp; └── images.list.template
└── upload2artifactory.py


# 용량 확인
du -sh /root/kubespray-offline/outputs/
3.3G    outputs/

# 디렉터리/파일 구조 확인
tree /root/kubespray-offline/outputs/ | more
tree /root/kubespray-offline/outputs/ -L 1
outputs/
├── config.sh
├── config.toml
├── containerd.service
├── extract-kubespray.sh
├── files                  # kubectl/kubelet/kubeadm, containerd 등 바이너리 파일들
├── images                 # 컨테이너 이미지를 .tar.gz 압축파일
├── install-containerd.sh
├── load-push-all-images.sh
├── nginx-default.conf
├── patches
├── playbook               # 노드들에 offline repo 설정을 위한 playbook/role
├── pypi                   # python 패키지 파일들 - index.html, *.whl, *.tar.gz
├── pyver.sh
├── rpms                   # rpm 패키지 파일들
├── setup-all.sh
├── setup-container.sh
├── setup-offline.sh
├── setup-py.sh
├── start-nginx.sh
├── start-registry.sh
└── venv.sh

7 directories, 15 files&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1] outputs 디렉터리 이동 후 &lt;span data-token-index=&quot;1&quot;&gt;setup-container.sh&lt;/span&gt; 실행 : 추가로 install-containerd.sh 실행됨&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108244081&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# setup-container.sh
#!/bin/bash

# install containerd
./install-containerd.sh

# Load images
echo &quot;==&amp;gt; Load registry, nginx images&quot;
NERDCTL=/usr/local/bin/nerdctl
cd ./images

for f in docker.io_library_registry-*.tar.gz docker.io_library_nginx-*.tar.gz; do
    sudo $NERDCTL load -i $f || exit 1
done

if [ -f kubespray-offline-container.tar.gz ]; then
    sudo $NERDCTL load -i kubespray-offline-container.tar.gz || exit 1
fi​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771108272140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# install-containerd.sh
# outputs 확인
cd /root/kubespray-offline/outputs
ls -l *.sh
-rw-r--r--. 1 root root 1371 Feb  3 19:46 config.sh
-rwxr-xr-x. 1 root root  719 Feb  3 19:46 extract-kubespray.sh
-rwxr-xr-x. 1 root root 2544 Feb  3 19:46 install-containerd.sh
-rwxr-xr-x. 1 root root 1141 Feb  3 19:46 load-push-all-images.sh
-rw-r--r--. 1 root root  607 Feb  3 19:46 pyver.sh
-rwxr-xr-x. 1 root root  394 Feb  3 19:46 setup-all.sh
-rwxr-xr-x. 1 root root  408 Feb  3 19:46 setup-container.sh
-rwxr-xr-x. 1 root root 2106 Feb  3 19:46 setup-offline.sh
-rwxr-xr-x. 1 root root 1213 Feb  3 19:46 setup-py.sh
-rwxr-xr-x. 1 root root  654 Feb  3 19:46 start-nginx.sh
-rwxr-xr-x. 1 root root  445 Feb  3 19:46 start-registry.sh
-rw-r--r--. 1 root root  322 Feb  3 19:46 venv.sh


# Install containerd from local files. Load nginx and registry images to containerd.
./setup-container.sh
==&amp;gt; Install runc
==&amp;gt; Install nerdctl
==&amp;gt; Install containerd
==&amp;gt; Start containerd
==&amp;gt; Install CNI plugins
==&amp;gt; Load registry, nginx images

# 설치된 바이너리 파일 및 버전 확인
which runc &amp;amp;&amp;amp; runc --version
which containerd &amp;amp;&amp;amp; containerd --version
which nerdctl &amp;amp;&amp;amp; nerdctl --version
tree -ug /opt/cni/bin/

# containerd systemd unit 확인
cat /etc/containerd/config.toml
cat /etc/systemd/system/containerd.service
systemctl status containerd.service --no-pager

# 다운받은 이미지를 압축해제 후 로컬에 load 확인 : CPU Arch = PLATFORM 확인!
nerdctl images
REPOSITORY    TAG              IMAGE ID        CREATED               PLATFORM       SIZE       BLOB SIZE
nginx         1.29.4           4c333d291372    About a minute ago    linux/arm64    190.7MB    184MB
nginx         1.28.0-alpine    bcb5257f77e1    About a minute ago    linux/arm64    52.73MB    51.23MB
registry      3.0.0            496d3637ba81    About a minute ago    linux/arm64    57.52MB    57.34MB
registry      2.8.1            b1524398e0af    About a minute ago    linux/arm64    25.85MB    25.68MB&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[2] &lt;span data-token-index=&quot;0&quot;&gt;start-nginx.sh&lt;/span&gt; 실행 : 웹 서버로 files, images, pypi, rpms 제공&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771108331457&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# start-nginx.sh
# (옵션) nginx conf 파일 수정 : 디렉터리 목록 표시되게
cp nginx-default.conf nginx-default.bak
cat &amp;lt;&amp;lt; EOF &amp;gt; nginx-default.conf 
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        # index  index.html index.htm;

        autoindex on;                 # 디렉터리 목록 표시
        autoindex_exact_size off;     # 파일 크기 KB/MB/GB 단위로 보기 좋게
        autoindex_localtime on;       # 서버 로컬 타임으로 표시
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # Force sendfile to off
    sendfile off;     
}
EOF

# Start nginx container.
./start-nginx.sh
# (옵션) 실행 명령 참고
echo &quot;===&amp;gt; Start nginx&quot;
$NERDCTL container run -d \
    --network host \
    --restart always \
    --name nginx \
    -v ${BASEDIR}:/usr/share/nginx/html \
    -v ${BASEDIR}/nginx-default.conf:/etc/nginx/conf.d/default.conf \
    ${NGINX_IMAGE} || exit 1

# nginx 컨테이너 확인
nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED           STATUS    PORTS    NAMES
0b71e6989724    docker.io/library/nginx:1.29.4    &quot;/docker-entrypoint.&amp;hellip;&quot;    18 seconds ago    Up                 nginx

ss -tnlp | grep nginx
LISTEN 0      511          0.0.0.0:80         0.0.0.0:*    users:((&quot;nginx&quot;,pid=19771,fd=6),(&quot;nginx&quot;,pid=19770,fd=6),(&quot;nginx&quot;,pid=19769,fd=6),(&quot;nginx&quot;,pid=19768,fd=6),(&quot;nginx&quot;,pid=19735,fd=6))
LISTEN 0      511             [::]:80            [::]:*    users:((&quot;nginx&quot;,pid=19771,fd=7),(&quot;nginx&quot;,pid=19770,fd=7),(&quot;nginx&quot;,pid=19769,fd=7),(&quot;nginx&quot;,pid=19768,fd=7),(&quot;nginx&quot;,pid=19735,fd=7))

# nginx 웹 접속
open http://192.168.10.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u5Cth/dJMcafle0fg/3bECoKJ7VhwgArBHmP7Q30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u5Cth/dJMcafle0fg/3bECoKJ7VhwgArBHmP7Q30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u5Cth/dJMcafle0fg/3bECoKJ7VhwgArBHmP7Q30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu5Cth%2FdJMcafle0fg%2F3bECoKJ7VhwgArBHmP7Q30%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;615&quot; height=&quot;466&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[3] &lt;span data-token-index=&quot;1&quot;&gt;setup-offline.sh&lt;/span&gt; 실행 : offline repo 설정, pypi mirror 전역 설정&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108373445&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# setup-offline.sh
# 스크립트 실행 전 기본 정보 확인
dnf repolist
repo id                                              repo name
appstream                                            Rocky Linux 10 - AppStream
baseos                                               Rocky Linux 10 - BaseOS
extras                                               Rocky Linux 10 - Extras
...

cat /etc/redhat-release
Rocky Linux release 10.0 (Red Quartz)


# 스크립트 실행 : Setup yum/deb repo config and PyPI mirror config to use local nginx server.
./setup-offline.sh
===&amp;gt; Disable all yumrepositories
===&amp;gt; Setup local yum repository
===&amp;gt; Setup PyPI mirror

# 기존 repo 이름이 .original로 변경되고, offline.repo 추가 확인
tree /etc/yum.repos.d/
/etc/yum.repos.d/
├── offline.repo
├── rocky-addons.repo.original
├── rocky-devel.repo.original
├── rocky-extras.repo.original
└── rocky.repo.original

# offline.repo 파일 확인
cat /etc/yum.repos.d/offline.repo
[offline-repo]
name=Offline repo
baseurl=http://localhost/rpms/local/
enabled=1
gpgcheck=0

# offline repo 확인
dnf clean all
dnf repolist
repo id                                                      repo name
offline-repo                                                 Offline repo

# pip 전역 설정 : pypi mirror 설정 확인
cat ~/.config/pip/pip.conf
[global]
index = http://localhost/pypi/
index-url = http://localhost/pypi/
trusted-host = localhost&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771108429907&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크립트 실행 전 기본 정보 확인
dnf repolist
repo id                                              repo name
appstream                                            Rocky Linux 10 - AppStream
baseos                                               Rocky Linux 10 - BaseOS
extras                                               Rocky Linux 10 - Extras
...

cat /etc/redhat-release
Rocky Linux release 10.0 (Red Quartz)


# 스크립트 실행 : Setup yum/deb repo config and PyPI mirror config to use local nginx server.
./setup-offline.sh
===&amp;gt; Disable all yumrepositories
===&amp;gt; Setup local yum repository
===&amp;gt; Setup PyPI mirror

# 기존 repo 이름이 .original로 변경되고, offline.repo 추가 확인
tree /etc/yum.repos.d/
/etc/yum.repos.d/
├── offline.repo
├── rocky-addons.repo.original
├── rocky-devel.repo.original
├── rocky-extras.repo.original
└── rocky.repo.original

# offline.repo 파일 확인
cat /etc/yum.repos.d/offline.repo
[offline-repo]
name=Offline repo
baseurl=http://localhost/rpms/local/
enabled=1
gpgcheck=0

# offline repo 확인
dnf clean all
dnf repolist
repo id                                                      repo name
offline-repo                                                 Offline repo

# pip 전역 설정 : pypi mirror 설정 확인
cat ~/.config/pip/pip.conf
[global]
index = http://localhost/pypi/
index-url = http://localhost/pypi/
trusted-host = localhost&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[4] &lt;span data-token-index=&quot;1&quot;&gt;setup-py.sh&lt;/span&gt; 실행 : offline repo 로 부터 python${PY} 설치 시도 &amp;rarr; offline repo 동작 여부 확인&lt;/p&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;gt; setup-py.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108457958&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

. /etc/os-release

IS_OFFLINE=${IS_OFFLINE:-true}

# Select python version
source &quot;$(dirname &quot;${BASH_SOURCE[0]}&quot;)/pyver.sh&quot;

# Install python and dependencies
echo &quot;===&amp;gt; Install python, venv, etc&quot;
if [ -e /etc/redhat-release ]; then
    # RHEL
    DNF_OPTS=
    if [[ $IS_OFFLINE = &quot;true&quot; ]]; then
        DNF_OPTS=&quot;--disablerepo=* --enablerepo=offline-repo&quot;
    fi
    #sudo dnf install -y $DNF_OPTS gcc libffi-devel openssl-devel || exit 1

    if [[ &quot;$VERSION_ID&quot; =~ ^7.* ]]; then
        echo &quot;FATAL: RHEL/CentOS 7 is not supported anymore.&quot;
        exit 1
    fi

    sudo dnf install -y $DNF_OPTS python${PY} || exit 1
    #sudo dnf install -y $DNF_OPTS python${PY}-devel || exit 1
else
    # Ubuntu
    sudo apt update
    case &quot;$VERSION_ID&quot; in
        20.04)
            if [ &quot;${IS_OFFLINE}&quot; = &quot;false&quot; ]; then
                # Prepare for latest python3
                sudo apt install -y software-properties-common
                sudo add-apt-repository ppa:deadsnakes/ppa -y || exit 1
                sudo apt update
            fi
            ;;
    esac
    #sudo apt install -y python${PY}-dev gcc libffi-dev libssl-dev || exit 1
    sudo apt install -y python${PY}-venv || exit 1
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;gt; pyver.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108487193&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Install python3 and venv from local repo.
## sudo dnf install -y --disablerepo=* --enablerepo=offline-repo python${PY}
./setup-py.sh
===&amp;gt; Install python, venv, etc
Last metadata expiration check: 0:06:40 ago on Wed 04 Feb 2026 12:23:11 AM KST.
Package python3-3.12.12-3.el10_1.aarch64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

# 변수 확인
source pyver.sh
echo -e &quot;python_version $python${PY}&quot;
python_version 3.12

# offline-repo 에 패키지 파일 확인
dnf info python3
tree rpms/local/ | grep -i python
├── libcap-ng-python3-0.8.4-6.el10.aarch64.rpm
├── python3-3.12.12-3.el10_1.aarch64.rpm
...&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771108502170&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Install python3 and venv from local repo.
## sudo dnf install -y --disablerepo=* --enablerepo=offline-repo python${PY}
./setup-py.sh
===&amp;gt; Install python, venv, etc
Last metadata expiration check: 0:06:40 ago on Wed 04 Feb 2026 12:23:11 AM KST.
Package python3-3.12.12-3.el10_1.aarch64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

# 변수 확인
source pyver.sh
echo -e &quot;python_version $python${PY}&quot;
python_version 3.12

# offline-repo 에 패키지 파일 확인
dnf info python3
tree rpms/local/ | grep -i python
├── libcap-ng-python3-0.8.4-6.el10.aarch64.rpm
├── python3-3.12.12-3.el10_1.aarch64.rpm
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[5] &lt;span data-token-index=&quot;1&quot;&gt;start-registry.sh&lt;/span&gt; 실행 : (컨테이너) 이미지 저장소 컨테이너로 기동&lt;/p&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;gt; start-registry.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108538670&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Start docker private registry container.
./start-registry.sh
===&amp;gt; Start registry
# (옵션) 실행 명령 참고
echo &quot;===&amp;gt; Start registry&quot;
sudo /usr/local/bin/nerdctl run -d \
    --network host \
    -e REGISTRY_HTTP_ADDR=0.0.0.0:${REGISTRY_PORT} \
    --restart always \
    --name registry \
    -v $REGISTRY_DIR:/var/lib/registry \
    $REGISTRY_IMAGE || exit 1

# 관련 변수 확인
source config.sh
echo -e &quot;registry_port: $REGISTRY_PORT&quot;
registry_port: 35000

# 확인
nerdctl ps
CONTAINER ID    IMAGE                               COMMAND                   CREATED               STATUS    PORTS    NAMES
778cd497fcaf    docker.io/library/registry:3.0.0    &quot;/entrypoint.sh /etc&amp;hellip;&quot;    About a minute ago    Up                 registry
0b71e6989724    docker.io/library/nginx:1.29.4      &quot;/docker-entrypoint.&amp;hellip;&quot;    30 minutes ago        Up                 nginx

ss -tnlp | grep registry
LISTEN 0      4096               *:35000            *:*    users:((&quot;registry&quot;,pid=20021,fd=7))                                                                                                 
LISTEN 0      4096               *:5001             *:*    users:((&quot;registry&quot;,pid=20021,fd=3))  

# 현재는 registry 서버 내부에 저장된 (컨테이너) 이미지 없는 상태 : (참고) REGISTRY_DIR=${REGISTRY_DIR:-/var/lib/registry}
tree /var/lib/registry/
/var/lib/registry/

# tcp 5001 port : debug, metrics 
curl 192.168.10.10:5001/metrics
curl 192.168.10.10:5001/debug/pprof/&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1771108551728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Start docker private registry container.
./start-registry.sh
===&amp;gt; Start registry
# (옵션) 실행 명령 참고
echo &quot;===&amp;gt; Start registry&quot;
sudo /usr/local/bin/nerdctl run -d \
    --network host \
    -e REGISTRY_HTTP_ADDR=0.0.0.0:${REGISTRY_PORT} \
    --restart always \
    --name registry \
    -v $REGISTRY_DIR:/var/lib/registry \
    $REGISTRY_IMAGE || exit 1

# 관련 변수 확인
source config.sh
echo -e &quot;registry_port: $REGISTRY_PORT&quot;
registry_port: 35000

# 확인
nerdctl ps
CONTAINER ID    IMAGE                               COMMAND                   CREATED               STATUS    PORTS    NAMES
778cd497fcaf    docker.io/library/registry:3.0.0    &quot;/entrypoint.sh /etc&amp;hellip;&quot;    About a minute ago    Up                 registry
0b71e6989724    docker.io/library/nginx:1.29.4      &quot;/docker-entrypoint.&amp;hellip;&quot;    30 minutes ago        Up                 nginx

ss -tnlp | grep registry
LISTEN 0      4096               *:35000            *:*    users:((&quot;registry&quot;,pid=20021,fd=7))                                                                                                 
LISTEN 0      4096               *:5001             *:*    users:((&quot;registry&quot;,pid=20021,fd=3))  

# 현재는 registry 서버 내부에 저장된 (컨테이너) 이미지 없는 상태 : (참고) REGISTRY_DIR=${REGISTRY_DIR:-/var/lib/registry}
tree /var/lib/registry/
/var/lib/registry/

# tcp 5001 port : debug, metrics 
curl 192.168.10.10:5001/metrics
curl 192.168.10.10:5001/debug/pprof/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[6] &lt;span data-token-index=&quot;1&quot;&gt;load-push-images.sh&lt;/span&gt; 실행 : (컨테이너) 이미지 저장소에 이미지 push&lt;/p&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;gt; load-push-images.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108593884&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크립트 실행 전 관련 변수 확인
echo -e &quot;cpu arch: $IMAGE_ARCH&quot;
cpu arch: arm64

## (옵션) 'registry.k8s.io k8s.gcr.io gcr.io ghcr.io docker.io quay.io' 이외에 추가로 필요한 저장소가 있다면 설정
echo -e &quot;Additional container registry hosts: $ADDITIONAL_CONTAINER_REGISTRY_LIST&quot;
Additional container registry hosts: myregistry.io

# nerdctl load 할 .tar.gz 파일들
ls -l images/*.tar.gz
-rw-r--r--. 1 root root  10068768 Feb  3 19:42 images/docker.io_amazon_aws-alb-ingress-controller-v1.1.9.tar.gz
-rw-r--r--. 1 root root 175407403 Feb  3 19:44 images/docker.io_amazon_aws-ebs-csi-driver-v0.5.0.tar.gz
-rw-r--r--. 1 root root  95707259 Feb  3 19:40 images/docker.io_cloudnativelabs_kube-router-v2.1.1.tar.gz
...


# Load all container images to containerd. Tag and push them to the private registry.
./load-push-all-images.sh
# FATA[0003] image might be filtered out (Hint: set `--platform=PLATFORM` or `--all-platforms`) 
# aarch64) PLATFORM=&quot;linux/arm64&quot; vs x86_64) PLATFORM=&quot;linux/amd64&quot;

# (TS) mac사용자: 아래 내용 추가 후 스크립트 다시 실행
vi load-push-all-images.sh
# 아래 추가 -----------------------
...
load_images() {
    for image in $BASEDIR/images/*.tar.gz; do
        echo &quot;===&amp;gt; Loading $image&quot;
        sudo $NERDCTL load --all-platforms -i $image || exit 1
    done
...
push_images() {
    ...
        echo &quot;===&amp;gt; Push ${newImage}&quot;
        sudo $NERDCTL push --platform=linux/arm64 ${newImage} || exit 1 # 왼쪽 추가 안해도 됨
    done
# -------------------------------

# 다시 실행 후 성공! : 2분 소요
./load-push-all-images.sh

# 로컬 이미지 load 확인
nerdctl images
REPOSITORY                                               TAG                                                             IMAGE ID        CREATED               PLATFORM       SIZE       BLOB SIZE
localhost:35000/kube-proxy                               v1.34.3                                                         fa5ed2c96dd3    46 seconds ago        linux/arm64    78.05MB    75.94MB
localhost:35000/kube-scheduler                           v1.34.3                                                         985575f183de    46 seconds ago        linux/arm64    53.34MB    51.59MB
localhost:35000/kube-controller-manager                  v1.34.3                                                         354700b61969    47 seconds ago        linux/arm64    74.38MB    72.62MB
localhost:35000/kube-apiserver                           v1.34.3                                                         dece5cf2dd3b    47 seconds ago        linux/arm64    86.56MB    84.81MB
...
localhost:35000/flannel/flannel-cni-plugin               v1.7.1-flannel1                                                 332db17b4c4a    About a minute ago    linux/arm64    11.39MB    11.37MB
localhost:35000/flannel/flannel                          v0.27.3                                                         3b36a8d4db19    About a minute ago    linux/arm64    102.6MB    101.5MB
...
registry.k8s.io/pause                                    3.10.1                                                          3f85f9d8a6bc    About a minute ago    linux/arm64    516.1kB    516.9kB
registry.k8s.io/metrics-server/metrics-server            v0.8.0                                                          87ccea7af925    About a minute ago    linux/arm64    82.58MB    80.84MB
registry.k8s.io/kube-scheduler                           v1.34.3                                                         985575f183de    About a minute ago    linux/arm64    53.34MB    51.59MB
registry.k8s.io/kube-proxy                               v1.34.3                                                         fa5ed2c96dd3    About a minute ago    linux/arm64    78.05MB    75.94MB
registry.k8s.io/kube-controller-manager                  v1.34.3                                                         354700b61969    About a minute ago    linux/arm64    74.38MB    72.62MB
registry.k8s.io/kube-apiserver                           v1.34.3                                                         dece5cf2dd3b    About a minute ago    linux/arm64    86.56MB    84.81MB
registry.k8s.io/ingress-nginx/controller                 v1.13.3                                                         68a587e5104f    About a minute ago    linux/arm64    336.3MB    334.2MB
registry.k8s.io/dns/k8s-dns-node-cache                   1.25.0                                                          7071feee8b70    About a minute ago    linux/arm64    90.54MB    88.43MB
registry.k8s.io/cpa/cluster-proportional-autoscaler      v1.8.8                                                          4146047e636f    About a minute ago    linux/arm64    39.98MB    37.86MB
registry.k8s.io/coredns/coredns                          v1.12.1                                                         e674cf21adf3    About a minute ago    linux/arm64    74.94MB    73.19MB
...
quay.io/metallb/speaker                                  v0.13.9                                                         51f18d4f5d4d    About a minute ago    linux/arm64    111.1MB    111.1MB
quay.io/metallb/controller                               v0.13.9                                                         b724b69a4c9b    About a minute ago    linux/arm64    63.12MB    63.11MB
quay.io/jetstack/cert-manager-webhook                    v1.15.3                                                         2d91656807bb    About a minute ago    linux/arm64    58.15MB    56.39MB
quay.io/jetstack/cert-manager-controller                 v1.15.3                                                         5114bfbeac23    About a minute ago    linux/arm64    67.13MB    65.37MB
quay.io/jetstack/cert-manager-cainjector                 v1.15.3                                                         a13418dc926e    About a minute ago    linux/arm64    44.65MB    42.89MB
quay.io/coreos/etcd                                      v3.5.26                                                         4b003fe9069c    About a minute ago    linux/arm64    66.06MB    63.34MB
...
rancher/local-path-provisioner                           v0.0.32                                                         4a3d51575c84    2 minutes ago         linux/arm64    61.37MB    61.35MB
mirantis/k8s-netchecker-server                           v1.2.2                                                          8e0ef348cf54    2 minutes ago         linux/amd64    125.8MB    123.7MB
mirantis/k8s-netchecker-agent                            v1.2.2                                                          e07c83f8f083    2 minutes ago         linux/amd64    5.681MB    5.856MB
flannel/flannel                                          v0.27.3                                                         3b36a8d4db19    2 minutes ago         linux/arm64    102.6MB    101.5MB
flannel/flannel-cni-plugin                               v1.7.1-flannel1                                                 332db17b4c4a    2 minutes ago         linux/arm64    11.39MB    11.37MB
...

# (참고) kube-proxy 컨테이너가 localhost:35000 과 registry.k8s.io 로 push 된 상태 확인
nerdctl images | grep -i kube-proxy
localhost:35000/kube-proxy                               v1.34.3                                                         fa5ed2c96dd3    24 minutes ago    linux/arm64    78.05MB    75.94MB
registry.k8s.io/kube-proxy                               v1.34.3                                                         fa5ed2c96dd3    24 minutes ago    linux/arm64    78.05MB    75.94MB

# localhost 있는 이미지가 각각 registry, quay, rancher, flannel 등으로 push 된것을 알 수 있다
nerdctl images | grep localhost
nerdctl images | grep localhost | wc -l
55

nerdctl images | grep -v localhost
nerdctl images | grep -v localhost | wc -l
56


# 이미지 저장소 카탈로그 확인
curl -s http://localhost:35000/v2/_catalog | jq
{
  &quot;repositories&quot;: [
    &quot;amazon/aws-alb-ingress-controller&quot;,
    &quot;amazon/aws-ebs-csi-driver&quot;,
    ...
 
# kube-apiserver 정보 확인  
curl -s http://localhost:35000/v2/kube-apiserver/tags
curl -s http://localhost:35000/v2/kube-apiserver/tags/list | jq
{
  &quot;name&quot;: &quot;kube-apiserver&quot;,
  &quot;tags&quot;: [
    &quot;v1.34.3&quot;
  ]
}

## Image Manifest
curl -s http://localhost:35000/v2/kube-apiserver/manifests/v1.34.3 | jq
{
  &quot;schemaVersion&quot;: 2,
  &quot;mediaType&quot;: &quot;application/vnd.docker.distribution.manifest.v2+json&quot;,
  &quot;config&quot;: {
    &quot;mediaType&quot;: &quot;application/vnd.docker.container.image.v1+json&quot;,
    &quot;digest&quot;: &quot;sha256:cf65ae6c8f700cc27f57b7305c6e2b71276a7eed943c559a0091e1e667169896&quot;,
    &quot;size&quot;: 2906
  },
  &quot;layers&quot;: [
    {
      &quot;mediaType&quot;: &quot;application/vnd.docker.image.rootfs.diff.tar&quot;,
      &quot;digest&quot;: &quot;sha256:378b3db0974f7a5a8767b6329ad310983bc712d0e400ff5faa294f95f869cc8c&quot;,
      &quot;size&quot;: 327680
    },
  ...(생략)

# 이미지 저장소의 저장 디렉터리 확인
tree /var/lib/registry/ -L 5
/var/lib/registry/
└── docker
    └── registry
        └── v2
            ├── blobs
            │   └── sha256
            └── repositories
                ├── amazon
                ├── calico
                ├── cilium
                ├── cloudnativelabs
                ├── coredns
                ├── coreos
                ├── cpa
                ├── dns
                ├── flannel
                ├── ingress-nginx
                ├── jetstack
                ├── k8snetworkplumbingwg
                ├── kube-apiserver
                ├── kube-controller-manager
                ├── kubeovn
                ├── kube-proxy
                ├── kubernetesui
                ├── kube-scheduler
                ├── kube-vip
                ├── library
                ├── metallb
                ├── metrics-server
                ├── mirantis
                ├── pause
                ├── provider-os
                ├── rancher
                └── sig-storage&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3rjVk/dJMcahJ9gp3/J962aG7oa7RKGyLHuI8TsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3rjVk/dJMcahJ9gp3/J962aG7oa7RKGyLHuI8TsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3rjVk/dJMcahJ9gp3/J962aG7oa7RKGyLHuI8TsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3rjVk%2FdJMcahJ9gp3%2FJ962aG7oa7RKGyLHuI8TsK%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;1059&quot; height=&quot;631&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;631&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nBvzt/dJMcacB3BWP/KJfTC4KrLF7mMDRvMe8uJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nBvzt/dJMcacB3BWP/KJfTC4KrLF7mMDRvMe8uJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nBvzt/dJMcacB3BWP/KJfTC4KrLF7mMDRvMe8uJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBvzt%2FdJMcacB3BWP%2FKJfTC4KrLF7mMDRvMe8uJk%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;439&quot; height=&quot;395&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[7] &lt;span data-token-index=&quot;1&quot;&gt;extract-kubespary.sh&lt;/span&gt; 실행 : kubespary 저장소 압축 해제&lt;/p&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;gt; extract-kubespary.sh&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108649362&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

cd $(dirname $0)
CURRENT_DIR=$(pwd)
source ./config.sh

KUBESPRAY_TARBALL=files/kubespray-${KUBESPRAY_VERSION}.tar.gz
DIR=kubespray-${KUBESPRAY_VERSION}

if [ -d $DIR ]; then
    echo &quot;${DIR} already exists.&quot;
    exit 0
fi

if [ ! -e $KUBESPRAY_TARBALL ]; then
    echo &quot;$KUBESPRAY_TARBALL does not exist.&quot;
    exit 1
fi

tar xvzf $KUBESPRAY_TARBALL || exit 1

# apply patches
sleep 1 # avoid annoying patch error in shared folders.
if [ -d $CURRENT_DIR/patches/${KUBESPRAY_VERSION} ]; then
    for patch in $CURRENT_DIR/patches/${KUBESPRAY_VERSION}/*.patch; do
        if [[ -f &quot;${patch}&quot; ]]; then
          echo &quot;===&amp;gt; Apply patch: $patch&quot;
          (cd $DIR &amp;amp;&amp;amp; patch -p1 &amp;lt; $patch)
        fi
    done
fi&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;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;kubespary-2.18.0 버전에서 patch 되는 내용으로, 2.30.0과 관계없음&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771108673781&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;From 24f1402a142bfd72c5c0a624fdeef0b30c43c490 Mon Sep 17 00:00:00 2001
From: Choi Yongbeom &amp;lt;59861163+mircyb@users.noreply.github.com&amp;gt;
Date: Wed, 5 Jan 2022 18:14:33 +0900
Subject: [PATCH] nerdctl insecure registry config (#8339)

* Update prep_download.yml

nerdctl insecure registry config

* Update prep_download.yml

* Update prep_download.yml

apply conversations advice

* Update prep_download.yml

* Update prep_download.yml

* Update prep_download.yml

* Update prep_download.yml

* Update prep_download.yml

* Update prep_download.yml

* Update main.yml

* Update main.yml

* Update prep_download.yml

* Update prep_download.yml
---
 roles/download/defaults/main.yml       | 3 +++
 roles/download/tasks/prep_download.yml | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/roles/download/defaults/main.yml b/roles/download/defaults/main.yml
index eecee3f5..a0cf9dbe 100644
--- a/roles/download/defaults/main.yml
+++ b/roles/download/defaults/main.yml
@@ -48,6 +48,9 @@ download_delegate: &quot;{% if download_localhost %}localhost{% else %}{{ groups['kub
 # Arch of Docker images and needed packages
 image_arch: &quot;{{host_architecture | default('amd64')}}&quot;
 
+# Nerdctl insecure flag set
+nerdctl_extra_flags: '{%- if containerd_insecure_registries is defined and containerd_insecure_registries|length&amp;gt;0 -%}\&quot; --insecure-registry&quot;{%- else -%}{%- endif -%}'
+
 # Versions
 kubeadm_version: &quot;{{ kube_version }}&quot;
 etcd_version: v3.5.0
diff --git a/roles/download/tasks/prep_download.yml b/roles/download/tasks/prep_download.yml
index 6fc84bc0..342f20c8 100644
--- a/roles/download/tasks/prep_download.yml
+++ b/roles/download/tasks/prep_download.yml
@@ -16,7 +16,7 @@
 - name: prep_download | Set image pull/info command for containerd
   set_fact:
     image_info_command: &quot;{{ bin_dir }}/nerdctl -n k8s.io images --format '{% raw %}{{ '{{' }} .Repository {{ '}}' }}:{{ '{{' }} .Tag {{ '}}' }}{% endraw %}' 2&amp;gt;/dev/null | grep -v ^:$ | tr '\n' ','&quot;
-    image_pull_command: &quot;{{ bin_dir }}/nerdctl -n k8s.io pull --quiet&quot;
+    image_pull_command: &quot;{{ bin_dir }}/nerdctl -n k8s.io pull --quiet{{ nerdctl_extra_flags }}&quot;
   when: container_manager == 'containerd'
 
 - name: prep_download | Set image pull/info command for crio
@@ -34,7 +34,7 @@
 - name: prep_download | Set image pull/info command for containerd on localhost
   set_fact:
     image_info_command_on_localhost: &quot;{{ bin_dir }}/nerdctl -n k8s.io images --format '{% raw %}{{ '{{' }} .Repository {{ '}}' }}:{{ '{{' }} .Tag {{ '}}' }}{% endraw %}' 2&amp;gt;/dev/null | grep -v ^:$ | tr '\n' ','&quot;
-    image_pull_command_on_localhost: &quot;{{ bin_dir }}/nerdctl -n k8s.io pull --quiet&quot;
+    image_pull_command_on_localhost: &quot;{{ bin_dir }}/nerdctl -n k8s.io pull --quiet{{ nerdctl_extra_flags }}&quot;
   when: container_manager_on_localhost == 'containerd'
 
 - name: prep_download | Set image pull/info command for crio on localhost
-- 
2.35.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771108694262&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스크립트 실행 전 관련 파일 확인 : kubespary repo 압축된 파일
ls -lh files/kubespray-*
-rw-r--r--. 1 root root 2.5M Feb  3 19:30 files/kubespray-2.30.0.tar.gz

# patches 파일 : kubespary-2.18.0 버전에서 patch 되는 내용으로, kubespary-2.30.0과 관계없음
tree patches/
└── 2.18.0
    ├── 0001-nerdctl-insecure-registry-config-8339.patch
    ├── 0002-Update-config.toml.j2-8340.patch
    └── 0003-generate-list-8537.patch


# Extract kubespray tarball and apply all patches.
./extract-kubespray.sh

# kubespary 저장소 압축해제된 파일들 확인
tree kubespray-2.30.0/ -L 1
kubespray-2.30.0/
├── ansible.cfg
├── CHANGELOG.md
├── cluster.yml
├── CNAME
├── code-of-conduct.md
├── _config.yml
├── contrib
├── CONTRIBUTING.md
├── Dockerfile
├── docs
├── extra_playbooks
├── galaxy.yml
├── index.html
├── inventory
├── library
├── LICENSE
├── logo
├── meta
├── OWNERS
├── OWNERS_ALIASES
├── pipeline.Dockerfile
├── playbooks
├── plugins
├── README.md
├── recover-control-plane.yml
├── RELEASE.md
├── remove-node.yml
├── remove_node.yml
├── requirements.txt
├── reset.yml
├── roles
├── scale.yml
├── scripts
├── SECURITY_CONTACTS
├── test-infra
├── tests
├── upgrade-cluster.yml
├── upgrade_cluster.yml
└── Vagrantfile

14 directories, 26 files&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;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;&lt;span style=&quot;background-color: #9feec3; color: #eb5757;&quot; data-token-index=&quot;0&quot;&gt;&amp;nbsp;kubespary 설치&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; kubespary 설치 &lt;span style=&quot;color: #cf5148;&quot; data-token-index=&quot;1&quot;&gt;3분 소요&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; offline.yml : etcd cpu arch 변수 수정 {{ etcd_version }}-linux-amd64 &amp;rarr; {{ etcd_version }}-linux-{{ image_arch }}&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108889771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# check python version
python --version
Python 3.12.12

# venv 실행
python3.12 -m venv ~/.venv/3.12
source ~/.venv/3.12/bin/activate
which ansible
tree ~/.venv/3.12/ -L 4
/root/.venv/3.12/
├── bin
│   ├── activate
│   ├── activate.csh
│   ├── activate.fish
│   ├── Activate.ps1
│   ├── ansible
...

# kubespary 디렉터리 이동
cd /root/kubespray-offline/outputs/kubespray-2.30.0

# Install ansible : 이미 설치 완료된 상태
pip install -U pip                # update pip
pip install -r requirements.txt   # Install ansible


# offline.yml 파일 복사 후 inventory 복사
cp ../../offline.yml .
cp -r inventory/sample inventory/mycluster
tree inventory/mycluster/

# 웹서버와 이미지 저장소 정보 수정 : http_server, registry_host
cat offline.yml
sed -i &quot;s/YOUR_HOST/192.168.10.10/g&quot; offline.yml
cat offline.yml | grep 192.168.10.10
http_server: &quot;http://192.168.10.10&quot;
registry_host: &quot;192.168.10.10:35000&quot;

# 수정 반영된 offline.yml 파일을 inventory 디렉터리 내부로 복사
\cp -f offline.yml inventory/mycluster/group_vars/all/offline.yml
cat inventory/mycluster/group_vars/all/offline.yml

# inventory 파일 작성
cat &amp;lt;&amp;lt;EOF &amp;gt; inventory/mycluster/inventory.ini
[kube_control_plane]
k8s-node1 ansible_host=192.168.10.11 ip=192.168.10.11 etcd_member_name=etcd1

[etcd:children]
kube_control_plane

[kube_node]
k8s-node2 ansible_host=192.168.10.12 ip=192.168.10.12
EOF
cat inventory/mycluster/inventory.ini

# ansible 연결 확인
ansible -i inventory/mycluster/inventory.ini all -m ping

# 각 노드에 offline repo 설정
tree ../playbook/
├── offline-repo.yml
└── roles
    └── offline-repo
        ├── defaults
        │   └── main.yml
        ├── files
        │   └── 99offline
        └── tasks
            ├── Debian.yml
            ├── main.yml
            └── RedHat.yml

mkdir offline-repo
cp -r ../playbook/ offline-repo/
tree offline-repo/
ansible-playbook -i inventory/mycluster/inventory.ini offline-repo/playbook/offline-repo.yml

# k8s-node 확인
ssh k8s-node1 tree /etc/yum.repos.d/
ssh k8s-node1 dnf repolist
repo id                          repo name
appstream                        Rocky Linux 10 - AppStream
baseos                           Rocky Linux 10 - BaseOS
extras                           Rocky Linux 10 - Extras
offline-repo                     Offline repo for kubespray

ssh k8s-node1 cat /etc/yum.repos.d/offline.repo
[offline-repo]
baseurl = http://192.168.10.10/rpms/local
enabled = 1
gpgcheck = 0
name = Offline repo for kubespray

## 추가로 설치를 위해 기존 repo 제거 : 미실행할 경우, kubespary 실행 시 fail됨
for i in rocky-addons rocky-devel rocky-extras rocky; do
  ssh k8s-node1 &quot;mv /etc/yum.repos.d/$i.repo /etc/yum.repos.d/$i.repo.original&quot;
  ssh k8s-node2 &quot;mv /etc/yum.repos.d/$i.repo /etc/yum.repos.d/$i.repo.original&quot;
done

ssh k8s-node1 tree /etc/yum.repos.d/
ssh k8s-node1 dnf repolist

ssh k8s-node2 tree /etc/yum.repos.d/
ssh k8s-node2 dnf repolist


# admin-lb 에 kubectl 없는 것 확인
which kubectl

# group vars 실습 환경에 맞게 설정
echo &quot;kubectl_localhost: true&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml # 배포를 수행하는 로컬 머신의 bin 디렉토리에도 kubectl 바이너리를 다운로드
sed -i 's|kube_owner: kube|kube_owner: root|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_network_plugin: calico|kube_network_plugin: flannel|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_proxy_mode: ipvs|kube_proxy_mode: iptables|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|enable_nodelocaldns: true|enable_nodelocaldns: false|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
grep -iE 'kube_owner|kube_network_plugin:|kube_proxy_mode|enable_nodelocaldns:' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
echo &quot;enable_dns_autoscaler: false&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml

echo &quot;flannel_interface: enp0s9&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml

sed -i 's|helm_enabled: false|helm_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
sed -i 's|metrics_server_enabled: false|metrics_server_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
grep -iE 'metrics_server_enabled:' inventory/mycluster/group_vars/k8s_cluster/addons.yml
echo &quot;metrics_server_requests_cpu: 25m&quot;     &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/addons.yml
echo &quot;metrics_server_requests_memory: 16Mi&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/addons.yml

# 지원 버전 정보 확인
cat roles/kubespray_defaults/vars/main/checksums.yml | grep -i kube -A40


# [macOS 사용자] (TS) 이슈 해결
# -----------------------------------------------
TASK [download : Download_file | Download item] **************************************************************************
fatal: [k8s-node1]: FAILED! =&amp;gt; {&quot;attempts&quot;: 4, &quot;changed&quot;: false, &quot;dest&quot;: &quot;/tmp/releases/etcd-3.5.26-linux-arm64.tar.gz&quot;, &quot;elapsed&quot;: 0, &quot;msg&quot;: &quot;Request failed&quot;, &quot;response&quot;: &quot;HTTP Error 404: Not Found&quot;, &quot;status_code&quot;: 404, &quot;url&quot;: &quot;http://192.168.10.10/files/kubernetes/etcd/etcd-v3.5.26-linux-amd64.tar.gz&quot;}
http://192.168.10.10/files/kubernetes/etcd/etcd-v3.5.26-linux-amd64.tar.gz
# vi roles/download/tasks/download_file.yml &amp;gt;&amp;gt; no_log: false # (참고) 실패 task 상세 로그 출력 설정하여 원인 파악

cat inventory/mycluster/group_vars/all/offline.yml | grep amd64
etcd_download_url: &quot;{{ files_repo }}/kubernetes/etcd/etcd-v{{ etcd_version }}-linux-amd64.tar.gz&quot;
sed -i 's/amd64/arm64/g' inventory/mycluster/group_vars/all/offline.yml
# -----------------------------------------------


# 배포 : 설치 완료까지 3분!
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version=&quot;1.34.3&quot;


# 설치 후 NetworkManger 에 dns 설정 파일 추가 확인
ssh k8s-node2 cat /etc/NetworkManager/conf.d/dns.conf
[global-dns-domain-*]
servers = 10.233.0.3,192.168.10.10
[global-dns]
searches = default.svc.cluster.local,svc.cluster.local
options = ndots:2,timeout:2,attempts:2
# 하지만 '/etc/NetworkManager/conf.d/99-dns-none.conf' 파일로 인해, 위 설정이 resolv.conf 에 반영되지 않음, 즉 노드에서는 service명으로 도메인 질의는 불가능.
ssh k8s-node2 cat /etc/resolv.conf
nameserver 192.168.10.10

# 설치 후 NetworkManger 에서 특정 NIC은 관리하지 않게 설정 추가 확인
ssh k8s-node2 cat /etc/NetworkManager/conf.d/k8s.conf
[keyfile]
unmanaged-devices+=interface-name:kube-ipvs0;interface-name:nodelocaldns


# kubectl 바이너리 파일을 ansible-playbook 실행한 서버에 다운로드 확인
file inventory/mycluster/artifacts/kubectl
ls -l inventory/mycluster/artifacts/kubectl
tree inventory/mycluster/
inventory/mycluster/
├── artifacts
│   └── kubectl
├── credentials
│   └── kubeadm_certificate_key.creds
├── group_vars
...

cp inventory/mycluster/artifacts/kubectl /usr/local/bin/
kubectl version --client=true
Client Version: v1.34.3
Kustomize Version: v5.7.1

# k8s admin 자격증명 확인 
mkdir /root/.kube
scp k8s-node1:/root/.kube/config /root/.kube/
sed -i 's/127.0.0.1/192.168.10.11/g' /root/.kube/config
k9s

# 자동완성 및 단축키 설정
source &amp;lt;(kubectl completion bash)
alias k=kubectl
complete -F __start_kubectl k
echo 'source &amp;lt;(kubectl completion bash)' &amp;gt;&amp;gt; /etc/profile
echo 'alias k=kubectl' &amp;gt;&amp;gt; /etc/profile
echo 'complete -F __start_kubectl k' &amp;gt;&amp;gt; /etc/profile

# 이미지 저장소가 192.168.10.10:35000 임을 확인
kubectl get deploy,sts,ds -n kube-system -owide
NAME                             READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS       IMAGES                                                     SELECTOR
deployment.apps/coredns          2/2     2            2           4m9s   coredns          192.168.10.10:35000/coredns/coredns:v1.12.1                k8s-app=kube-dns
deployment.apps/metrics-server   1/1     1            1           4m5s   metrics-server   192.168.10.10:35000/metrics-server/metrics-server:v0.8.0   app.kubernetes.io/name=metrics-server,version=0.8.0

NAME                                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE     CONTAINERS     IMAGES                                        SELECTOR
daemonset.apps/kube-flannel              0         0         0       0            0           &amp;lt;none&amp;gt;                   4m25s   kube-flannel   192.168.10.10:35000/flannel/flannel:v0.27.3   app=flannel
...
daemonset.apps/kube-proxy                1         1         1       1            1           kubernetes.io/os=linux   4m43s   kube-proxy     192.168.10.10:35000/kube-proxy:v1.34.3        k8s-app=kube-proxy&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;(TS) flannel 파드 최초 정상 기동을 위해 k8s-node 에 디폴트 라우팅 추가 필요 : 해결을 위해 k8s-node 에 디폴트 라우트 적용되어 있음&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108852159&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (TS) k8s-node 에 디폴트 라우팅 추가 필요
# -----------------------------------------------
# Flannel 파드가 정상 기동 시, kube-flannel 컨테이너가 호스트에 /run/flannel/subnet.env 파일 생성함
# Flannel은 기본적으로 노드의 Default Route가 설정된 인터페이스를 찾아서 그 IP를 노드 간 통신(VxLAN 등)의 엔드포인트로 사용하려 합니다. 
# 기본 라우팅이 없으면 Flannel이 어떤 인터페이스를 통해 다른 노드와 통신해야 할지 결정하지 못해 실행이 중단됨. 결국 파드가 정상 기동 실패됨.
# 파드 기동 실패가 되면, 호스트에 /run/flannel/subnet.env 파일 생성이 안됨. 결국 해당 Task 에서 실패하게 됨!
TASK [network_plugin/flannel : Flannel | Wait for flannel subnet.env file presence] **************************************
fatal: [k8s-node1]: FAILED! =&amp;gt; {&quot;changed&quot;: false, &quot;elapsed&quot;: 600, &quot;msg&quot;: &quot;Timeout when waiting for file /run/flannel/subnet.env&quot;}
#ansible-playbook -i inventory/mycluster/inventory.ini -v reset.yml -e kube_version=&quot;1.34.3&quot;
[k8s-nodes]
nmcli connection modify enp0s9 +ipv4.routes &quot;0.0.0.0/0 192.168.10.10 200&quot;
nmcli connection up enp0s9
ip route

[admin-lb] 
# NAT 설정 제거하여, 실제적으로 k8s-node 가 외부 인터넷이 안되는 상황을 만들고 실습 진행해보자
iptables -t nat -D POSTROUTING -o enp0s8 -j MASQUERADE
iptables -t nat -S
# -----------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[k8s-node] 이미지 저장소 관련 정보 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771108901028&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[k8s-node1 , k8s-node2]

# 로컬 이미지 확인
crictl images
IMAGE                                               TAG                 IMAGE ID            SIZE
192.168.10.10:35000/coredns/coredns                 v1.12.1             138784d87c9c5       73.2MB
192.168.10.10:35000/flannel/flannel-cni-plugin      v1.7.1-flannel1     e5bf9679ea8c3       11.4MB
192.168.10.10:35000/flannel/flannel                 v0.27.3             cadcae92e6360       102MB
192.168.10.10:35000/kube-apiserver                  v1.34.3             cf65ae6c8f700       84.8MB
192.168.10.10:35000/kube-controller-manager         v1.34.3             7ada8ff13e54b       72.6MB
192.168.10.10:35000/kube-proxy                      v1.34.3             4461daf6b6af8       75.9MB
192.168.10.10:35000/kube-scheduler                  v1.34.3             2f2aa21d34d2d       51.6MB
192.168.10.10:35000/library/nginx                   1.28.0-alpine       5a91d90f47ddf       51.2MB
192.168.10.10:35000/metrics-server/metrics-server   v0.8.0              bc6c1e09a843d       80.8MB
192.168.10.10:35000/pause                           3.10.1              d7b100cd9a77b       517kB

# containerd 정보 확인
tree /etc/containerd/
/etc/containerd/
├── certs.d
│   └── 192.168.10.10:35000
│       └── hosts.toml
├── config.toml
└── cri-base.json

cat /etc/containerd/config.toml
...
  [plugins.&quot;io.containerd.cri.v1.images&quot;.pinned_images]
    sandbox = &quot;192.168.10.10:35000/pause:3.10.1&quot;
  [plugins.&quot;io.containerd.cri.v1.images&quot;.registry]
    config_path = &quot;/etc/containerd/certs.d&quot;
....

cat /etc/containerd/certs.d/192.168.10.10\:35000/hosts.toml
server = &quot;https://192.168.10.10:35000&quot;
[host.&quot;http://192.168.10.10:35000&quot;]
  capabilities = [&quot;pull&quot;,&quot;resolve&quot;]
  skip_verify = true
  override_path = false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;kubespary 에 offline 설치를 위한 지원&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;다운로드 파일, 이미지 목록 작성* : &lt;/span&gt;generate_list.sh &amp;rarr; generate_list.yml&lt;/p&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;gt; generate_list.sh&lt;/p&gt;
&lt;pre id=&quot;code_1771108990509&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# generate_list.sh
#!/bin/bash
set -eo pipefail

CURRENT_DIR=$(cd $(dirname $0); pwd)
TEMP_DIR=&quot;${CURRENT_DIR}/temp&quot;
REPO_ROOT_DIR=&quot;${CURRENT_DIR%/contrib/offline}&quot;

: ${DOWNLOAD_YML:=&quot;roles/kubespray_defaults/defaults/main/download.yml&quot;}

mkdir -p ${TEMP_DIR}

# generate all download files url template
grep 'download_url:' ${REPO_ROOT_DIR}/${DOWNLOAD_YML} \
    | sed 's/^.*_url: //g;s/\&quot;//g' &amp;gt; ${TEMP_DIR}/files.list.template

# generate all images list template
sed -n '/^downloads:/,/download_defaults:/p' ${REPO_ROOT_DIR}/${DOWNLOAD_YML} \
    | sed -n &quot;s/repo: //p;s/tag: //p&quot; | tr -d ' ' \
    | sed 'N;s#\n# #g' | tr ' ' ':' | sed 's/\&quot;//g' &amp;gt; ${TEMP_DIR}/images.list.template

# add kube-* images to images list template
# Those container images are downloaded by kubeadm, then roles/kubespray_defaults/defaults/main/download.yml
# doesn't contain those images. That is reason why here needs to put those images into the
# list separately.
KUBE_IMAGES=&quot;kube-apiserver kube-controller-manager kube-scheduler kube-proxy&quot;
for i in $KUBE_IMAGES; do
    echo &quot;{{ kube_image_repo }}/$i:v{{ kube_version }}&quot; &amp;gt;&amp;gt; ${TEMP_DIR}/images.list.template
done

# run ansible to expand templates
/bin/cp ${CURRENT_DIR}/generate_list.yml ${REPO_ROOT_DIR}

(cd ${REPO_ROOT_DIR} &amp;amp;&amp;amp; ansible-playbook $* generate_list.yml &amp;amp;&amp;amp; /bin/rm generate_list.yml) || exit 1&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;gt; generate_list.yml&lt;/p&gt;
&lt;pre id=&quot;code_1771109017696&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- name: Collect container images for offline deployment
  hosts: localhost
  become: false

  roles:
    # Just load default variables from roles.
    - role: kubespray_defaults
      when: false
    - role: download
      when: false

  tasks:
    # Generate files.list and images.list files from templates.
    - name: Collect container images for offline deployment
      template:
        src: ./contrib/offline/temp/{{ item }}.list.template
        dest: ./contrib/offline/temp/{{ item }}.list
        mode: &quot;0644&quot;
      with_items:
        - files
        - images&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;gt; download.yml&lt;/p&gt;
&lt;pre id=&quot;code_1771109032362&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
cd /root/kubespray-offline/outputs/kubespray-2.30.0/contrib/offline
tree
├── docker-daemon.json
├── generate_list.sh
├── generate_list.yml
├── manage-offline-container-images.sh
├── manage-offline-files.sh
├── nginx.conf
├── README.md
├── registries.conf
└── upload2artifactory.py

# 다운받을 파일과 (컨테이너)이미지 목록 파일로 작성 스크립트(ansible generate_list.yml 실행 포함) 실행
# https://github.com/kubernetes-sigs/kubespray/blob/master/roles/kubespray_defaults/defaults/main/download.yml
cat ../../roles/kubespray_defaults/defaults/main/download.yml  # 다운로드될 파일 목록과 컨테이너 이미지 목록 참고 파일
./generate_list.sh
PLAY [Collect container images for offline deployment] ****************************************************************************
TASK [Collect container images for offline deployment] ****************************************************************************
...

# temp 디렉터리 확인
tree temp
├── files.list
├── files.list.template
├── images.list
└── images.list.template

# 파일 목록 확인
cat temp/files.list.template
{{ dl_k8s_io_url }}/release/v{{ kube_version }}/bin/linux/{{ image_arch }}/kubelet
{{ dl_k8s_io_url }}/release/v{{ kube_version }}/bin/linux/{{ image_arch }}/kubectl
{{ dl_k8s_io_url }}/release/v{{ kube_version }}/bin/linux/{{ image_arch }}/kubeadm
...
cat temp/files.list | tee 1-files.list
https://dl.k8s.io/release/v1.34.3/bin/linux/arm64/kubelet
https://dl.k8s.io/release/v1.34.3/bin/linux/arm64/kubectl
https://dl.k8s.io/release/v1.34.3/bin/linux/arm64/kubeadm
...

# (컨테이너)이미지 목록 확인
cat temp/images.list | tee 1-images.list
tail temp/images.list -n 4 | tee 1-images.list
registry.k8s.io/kube-apiserver:v1.34.3
registry.k8s.io/kube-controller-manager:v1.34.3
registry.k8s.io/kube-scheduler:v1.34.3
registry.k8s.io/kube-proxy:v1.34.3

cat temp/images.list.template
tail temp/images.list.template -n 4
{{ kube_image_repo }}/kube-apiserver:v{{ kube_version }}
{{ kube_image_repo }}/kube-controller-manager:v{{ kube_version }}
{{ kube_image_repo }}/kube-scheduler:v{{ kube_version }}
{{ kube_image_repo }}/kube-proxy:v{{ kube_version }}


# 목록 정보 수정 필요 시: 필요한 변수명 확인 후 설정하여 반영
## cat temp/files.list.template
## cat temp/images.list.template
 # cat ../../roles/kubespray_defaults/vars/main/checksums.yml | grep -i kube -A40
 # arm64

# 다시 목록 생성 : ansible-playbook 실행이 되니, 스크립트 실행 시 -e 변수 지정 가능
./generate_list.sh -e kube_version=1.33.7 -e image_arch=amd64  # Windows 사용자분들은 arm64 사용할 것!
cat temp/files.list | tee 2-files.list
cat temp/images.list | tee 2-images.list

# 변경 비교
diff 1-files.list 2-files.list
vi -d 1-files.list 2-files.list

diff 1-images.list 2-images.list
vi -d 1-images.list 2-images.list
rm -rf *.list&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;manage-offline-container-images.sh : 오프라인 배포를 위한 컨테이너 이미지 수집 스크립트&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109049948&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

OPTION=$1
CURRENT_DIR=$(cd $(dirname $0); pwd)
TEMP_DIR=&quot;${CURRENT_DIR}/temp&quot;

IMAGE_TAR_FILE=&quot;${CURRENT_DIR}/container-images.tar.gz&quot;
IMAGE_DIR=&quot;${CURRENT_DIR}/container-images&quot;
IMAGE_LIST=&quot;${IMAGE_DIR}/container-images.txt&quot;
RETRY_COUNT=5

function create_container_image_tar() {
	set -e

	if [ -z &quot;${IMAGES_FROM_FILE}&quot; ]; then
		echo &quot;Getting images from current \&quot;$(kubectl config current-context)\&quot;&quot;

		IMAGES=$(mktemp --suffix=-images)
		trap 'rm -f &quot;${IMAGES}&quot;' EXIT

		kubectl describe cronjobs,jobs,pods --all-namespaces | grep &quot; Image:&quot; | awk '{print $2}' | sort | uniq &amp;gt; &quot;${IMAGES}&quot;
		# NOTE: etcd and pause cannot be seen as pods.
		kubectl cluster-info dump | grep -E &quot;quay.io/coreos/etcd:|registry.k8s.io/pause:&quot; | sed s@\&quot;@@g &amp;gt;&amp;gt; &quot;${IMAGES}&quot;
	else
		echo &quot;Getting images from file \&quot;${IMAGES_FROM_FILE}\&quot;&quot;
		if [ ! -f &quot;${IMAGES_FROM_FILE}&quot; ]; then
			echo &quot;${IMAGES_FROM_FILE} is not a file&quot;
			exit 1
		fi
		IMAGES=$(realpath $IMAGES_FROM_FILE)
	fi

	rm -f  ${IMAGE_TAR_FILE}
	rm -rf ${IMAGE_DIR}
	mkdir  ${IMAGE_DIR}
	cd     ${IMAGE_DIR}

	sudo --preserve-env=http_proxy,https_proxy,no_proxy ${runtime} pull registry:latest
	sudo ${runtime} save -o registry-latest.tar registry:latest

	while read -r image
	do
		FILE_NAME=&quot;$(echo ${image} | sed s@&quot;/&quot;@&quot;-&quot;@g | sed s/&quot;:&quot;/&quot;-&quot;/g | sed -E 's/\@.*//g')&quot;.tar
		set +e
		for step in $(seq 1 ${RETRY_COUNT})
		do
			sudo --preserve-env=http_proxy,https_proxy,no_proxy ${runtime} pull ${image}
			if [ $? -eq 0 ]; then
				break
			fi
			echo &quot;Failed to pull ${image} at step ${step}&quot;
			if [ ${step} -eq ${RETRY_COUNT} ]; then
				exit 1
			fi
		done
		set -e
		sudo ${runtime} save -o ${FILE_NAME}  ${image}

		# NOTE: Here removes the following repo parts from each image
		# so that these parts will be replaced with Kubespray.
		# - kube_image_repo: &quot;registry.k8s.io&quot;
		# - gcr_image_repo: &quot;gcr.io&quot;
		# - ghcr_image_repo: &quot;ghcr.io&quot;
		# - docker_image_repo: &quot;docker.io&quot;
		# - quay_image_repo: &quot;quay.io&quot;
		FIRST_PART=$(echo ${image} | awk -F&quot;/&quot; '{print $1}')
		if [ &quot;${FIRST_PART}&quot; = &quot;registry.k8s.io&quot; ] ||
		   [ &quot;${FIRST_PART}&quot; = &quot;gcr.io&quot; ] ||
		   [ &quot;${FIRST_PART}&quot; = &quot;ghcr.io&quot; ] ||
		   [ &quot;${FIRST_PART}&quot; = &quot;docker.io&quot; ] ||
		   [ &quot;${FIRST_PART}&quot; = &quot;quay.io&quot; ] ||
		   [ &quot;${FIRST_PART}&quot; = &quot;${PRIVATE_REGISTRY}&quot; ]; then
			image=$(echo ${image} | sed s@&quot;${FIRST_PART}/&quot;@@ | sed -E 's/\@.*/\n/g')
		fi
		echo &quot;${FILE_NAME}  ${image}&quot; &amp;gt;&amp;gt; ${IMAGE_LIST}
	done &amp;lt; &quot;${IMAGES}&quot;

	cd ..
	sudo chown ${USER} ${IMAGE_DIR}/*
	tar -zcvf ${IMAGE_TAR_FILE}  ./container-images
	rm -rf ${IMAGE_DIR}

	echo &quot;&quot;
	echo &quot;${IMAGE_TAR_FILE} is created to contain your container images.&quot;
	echo &quot;Please keep this file and bring it to your offline environment.&quot;
}

function register_container_images() {
	create_registry=false
	REGISTRY_PORT=${REGISTRY_PORT:-&quot;5000&quot;}

	if [ -z &quot;${DESTINATION_REGISTRY}&quot; ]; then
		echo &quot;DESTINATION_REGISTRY not set, will create local registry&quot;
		create_registry=true
		DESTINATION_REGISTRY=&quot;$(hostname):${REGISTRY_PORT}&quot;
	fi
	echo &quot;Images will be pushed to ${DESTINATION_REGISTRY}&quot;

	if [ ! -f ${IMAGE_TAR_FILE} ]; then
		echo &quot;${IMAGE_TAR_FILE} should exist.&quot;
		exit 1
	fi
	if [ ! -d ${TEMP_DIR} ]; then
		mkdir ${TEMP_DIR}
	fi

	# To avoid &quot;http: server gave http response to https client&quot; error.
	if [ -d /etc/docker/ ]; then
		set -e
		# Ubuntu18.04, RHEL7/CentOS7
		cp ${CURRENT_DIR}/docker-daemon.json      ${TEMP_DIR}/docker-daemon.json
		sed -i s@&quot;HOSTNAME&quot;@&quot;$(hostname)&quot;@  ${TEMP_DIR}/docker-daemon.json
		sudo cp ${TEMP_DIR}/docker-daemon.json           /etc/docker/daemon.json
	elif [ -d /etc/containers/ ]; then
		set -e
		# RHEL8/CentOS8
		cp ${CURRENT_DIR}/registries.conf         ${TEMP_DIR}/registries.conf
		sed -i s@&quot;HOSTNAME&quot;@&quot;$(hostname)&quot;@  ${TEMP_DIR}/registries.conf
		sudo cp ${TEMP_DIR}/registries.conf   /etc/containers/registries.conf
  elif [ &quot;$(uname)&quot; == &quot;Darwin&quot; ]; then
    echo &quot;This is a Mac, no configuration changes are required&quot;
	else
		echo &quot;runtime package(docker-ce, podman, nerctl, etc.) should be installed&quot;
		exit 1
	fi

	tar -zxvf ${IMAGE_TAR_FILE}

	if ${create_registry}; then
		sudo ${runtime} load -i ${IMAGE_DIR}/registry-latest.tar
		set +e

		sudo ${runtime} container inspect registry &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
		if [ $? -ne 0 ]; then
			sudo ${runtime} run --restart=always -d -p &quot;${REGISTRY_PORT}&quot;:&quot;${REGISTRY_PORT}&quot; --name registry registry:latest
		fi
		set -e
	fi

	while read -r line; do
		file_name=$(echo ${line} | awk '{print $1}')
		raw_image=$(echo ${line} | awk '{print $2}')
		new_image=&quot;${DESTINATION_REGISTRY}/${raw_image}&quot;
		load_image=$(sudo ${runtime} load -i ${IMAGE_DIR}/${file_name} | head -n1)
		org_image=$(echo &quot;${load_image}&quot;  | awk '{print $3}')
		# special case for tags containing the digest when using docker or podman as the container runtime
		if [ &quot;${org_image}&quot; == &quot;ID:&quot; ]; then
		  org_image=$(echo &quot;${load_image}&quot;  | awk '{print $4}')
		fi
		image_id=$(sudo ${runtime} image inspect --format &quot;{{.Id}}&quot; &quot;${org_image}&quot;)
		if [ -z &quot;${file_name}&quot; ]; then
			echo &quot;Failed to get file_name for line ${line}&quot;
			exit 1
		fi
		if [ -z &quot;${raw_image}&quot; ]; then
			echo &quot;Failed to get raw_image for line ${line}&quot;
			exit 1
		fi
		if [ -z &quot;${org_image}&quot; ]; then
			echo &quot;Failed to get org_image for line ${line}&quot;
			exit 1
		fi
		if [ -z &quot;${image_id}&quot; ]; then
			echo &quot;Failed to get image_id for file ${file_name}&quot;
			exit 1
		fi
		sudo ${runtime} load -i ${IMAGE_DIR}/${file_name}
		sudo ${runtime} tag  ${image_id} ${new_image}
		sudo ${runtime} push ${new_image}
	done &amp;lt;&amp;lt;&amp;lt; &quot;$(cat ${IMAGE_LIST})&quot;

	echo &quot;Succeeded to register container images to local registry.&quot;
	echo &quot;Please specify \&quot;${DESTINATION_REGISTRY}\&quot; for the following options in your inventry:&quot;
	echo &quot;- kube_image_repo&quot;
	echo &quot;- gcr_image_repo&quot;
	echo &quot;- docker_image_repo&quot;
	echo &quot;- quay_image_repo&quot;
}

# get runtime command
if command -v nerdctl 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;nerdctl&quot;
elif command -v podman 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;podman&quot;
elif command -v docker 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;docker&quot;
else
    echo &quot;No supported container runtime found&quot;
    exit 1
fi

if [ &quot;${OPTION}&quot; == &quot;create&quot; ]; then
	create_container_image_tar
elif [ &quot;${OPTION}&quot; == &quot;register&quot; ]; then
	register_container_images
else
	echo &quot;This script has two features:&quot;
	echo &quot;(1) Get container images from an environment which is deployed online, or set IMAGES_FROM_FILE&quot;
	echo &quot;    environment variable to get images from a file (e.g. temp/images.list after running the&quot;
	echo &quot;    ./generate_list.sh script).&quot;
	echo &quot;(2) Deploy local container registry and register the container images to the registry.&quot;
	echo &quot;&quot;
	echo &quot;Step(1) should be done online site as a preparation, then we bring&quot;
	echo &quot;the gotten images to the target offline environment. if images are from&quot;
	echo &quot;a private registry, you need to set PRIVATE_REGISTRY environment variable.&quot;
	echo &quot;Then we will run step(2) for registering the images to local registry, or to an existing&quot;
	echo &quot;registry set by the DESTINATION_REGISTRY environment variable. By default, the local registry&quot;
	echo &quot;will run on port 5000. This can be changed with the REGISTRY_PORT environment variable&quot;
	echo &quot;&quot;
	echo &quot;${IMAGE_TAR_FILE} is created to contain your container images.&quot;
	echo &quot;Please keep this file and bring it to your offline environment.&quot;
	echo &quot;&quot;
	echo &quot;Step(1) can be operated with:&quot;
	echo &quot; $ ./manage-offline-container-images.sh   create&quot;
	echo &quot;&quot;
	echo &quot;Step(2) can be operated with:&quot;
	echo &quot; $ ./manage-offline-container-images.sh   register&quot;
	echo &quot;&quot;
	echo &quot;Please specify 'create' or 'register'.&quot;
	echo &quot;&quot;
	exit 1
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;manage-offline-files.sh : 지정된 모든 파일을 다운로드하고, Nginx 컨테이너를 실행하여 파일 다운로드 기능 제공&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109066117&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

CURRENT_DIR=$( dirname &quot;$(readlink -f &quot;$0&quot;)&quot; )
OFFLINE_FILES_DIR_NAME=&quot;offline-files&quot;
OFFLINE_FILES_DIR=&quot;${CURRENT_DIR}/${OFFLINE_FILES_DIR_NAME}&quot;
OFFLINE_FILES_ARCHIVE=&quot;${CURRENT_DIR}/offline-files.tar.gz&quot;
FILES_LIST=${FILES_LIST:-&quot;${CURRENT_DIR}/temp/files.list&quot;}
NGINX_PORT=80

# download files
if [ ! -f &quot;${FILES_LIST}&quot; ]; then
    echo &quot;${FILES_LIST} should exist, run ./generate_list.sh first.&quot;
    exit 1
fi

rm -rf &quot;${OFFLINE_FILES_DIR}&quot;
rm &quot;${OFFLINE_FILES_ARCHIVE}&quot;
mkdir  &quot;${OFFLINE_FILES_DIR}&quot;

while read -r url; do
  if ! wget -x -P &quot;${OFFLINE_FILES_DIR}&quot; &quot;${url}&quot;; then
    exit 1
  fi
done &amp;lt; &quot;${FILES_LIST}&quot;

tar -czvf &quot;${OFFLINE_FILES_ARCHIVE}&quot;  &quot;${OFFLINE_FILES_DIR_NAME}&quot;

[ -n &quot;$NO_HTTP_SERVER&quot; ] &amp;amp;&amp;amp; echo &quot;skip to run nginx&quot; &amp;amp;&amp;amp; exit 0

# run nginx container server
if command -v nerdctl 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;nerdctl&quot;
elif command -v podman 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;podman&quot;
elif command -v docker 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    runtime=&quot;docker&quot;
else
    echo &quot;No supported container runtime found&quot;
    exit 1
fi

sudo &quot;${runtime}&quot; container inspect nginx &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
if [ $? -ne 0 ]; then
    sudo --preserve-env=http_proxy,https_proxy,no_proxy &quot;${runtime}&quot; run \
        --restart=always -d -p ${NGINX_PORT}:80 \
        --volume &quot;${OFFLINE_FILES_DIR}&quot;:/usr/share/nginx/html/download \
        --volume &quot;${CURRENT_DIR}&quot;/nginx.conf:/etc/nginx/nginx.conf \
        --name nginx nginx:alpine
fi&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;0&quot;&gt;upload2artifactory.py : 디렉터리 아래의 각 파일을 Artifactory의 일반 저장소에 재귀적으로 업로드&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109085535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env python3
&quot;&quot;&quot;This is a helper script to manage-offline-files.sh.

After running manage-offline-files.sh, you can run upload2artifactory.py
to recursively upload each file to a generic repository in Artifactory.

This script recurses the current working directory and is intended to
be started from 'kubespray/contrib/offline/offline-files'

Environment Variables:
    USERNAME -- At least permissions'Deploy/Cache' and 'Delete/Overwrite'.
    TOKEN -- Generate this with 'Set Me Up' in your user.
    BASE_URL -- The URL including the repository name.

&quot;&quot;&quot;
import os
import urllib.request
import base64


def upload_file(file_path, destination_url, username, token):
    &quot;&quot;&quot;Helper function to upload a single file&quot;&quot;&quot;
    try:
        with open(file_path, 'rb') as f:
            file_data = f.read()

        request = urllib.request.Request(destination_url, data=file_data, method='PUT') # NOQA
        auth_header = base64.b64encode(f&quot;{username}:{token}&quot;.encode()).decode()
        request.add_header(&quot;Authorization&quot;, f&quot;Basic {auth_header}&quot;)

        with urllib.request.urlopen(request) as response:
            if response.status in [200, 201]:
                print(f&quot;Success: Uploaded {file_path}&quot;)
            else:
                print(f&quot;Failed: {response.status} {response.read().decode('utf-8')}&quot;) # NOQA
    except urllib.error.HTTPError as e:
        print(f&quot;HTTPError: {e.code} {e.reason} for {file_path}&quot;)
    except urllib.error.URLError as e:
        print(f&quot;URLError: {e.reason} for {file_path}&quot;)
    except OSError as e:
        print(f&quot;OSError: {e.strerror} for {file_path}&quot;)


def upload_files(base_url, username, token):
    &quot;&quot;&quot; Recurse current dir and upload each file using urllib.request &quot;&quot;&quot;
    for root, _, files in os.walk(os.getcwd()):
        for file in files:
            file_path = os.path.join(root, file)
            relative_path = os.path.relpath(file_path, os.getcwd())
            destination_url = f&quot;{base_url}/{relative_path}&quot;

            print(f&quot;Uploading {file_path} to {destination_url}&quot;)
            upload_file(file_path, destination_url, username, token)


if __name__ == &quot;__main__&quot;:
    a_user = os.getenv(&quot;USERNAME&quot;)
    a_token = os.getenv(&quot;TOKEN&quot;)
    a_url = os.getenv(&quot;BASE_URL&quot;)
    if not a_user or not a_token or not a_url:
        print(
            &quot;Error: Environment variables USERNAME, TOKEN, and BASE_URL must be set.&quot; # NOQA
        )
        exit()
    upload_files(a_url, a_user, a_token)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubespary-offline&amp;nbsp;에&amp;nbsp;kube_version&amp;nbsp;변경&amp;nbsp;적용하여&amp;nbsp;관련&amp;nbsp;파일&amp;nbsp;다운로드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109116763&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 디렉터리 이동 후 기존 정보 백업
cd /root/kubespray-offline
tree cache/kubespray-2.30.0/contrib/offline/temp/
├── files.list
├── files.list.template
├── images.list
└── images.list.template
mv cache/kubespray-2.30.0/contrib/offline/temp/files.list cache/kubespray-2.30.0/contrib/offline/temp/files-2.list
mv cache/kubespray-2.30.0/contrib/offline/temp/images.list cache/kubespray-2.30.0/contrib/offline/temp/images-2.list

# (옵션) 다운로드 스크립트에 실제 다운로드 실행하는 부분 제거 시
# -----------------------------------------------
cat download-kubespray-files.sh
cp download-kubespray-files.sh download-kubespray-files.bak
sed -i '/generate_list$/,$ { /generate_list/!d }' download-kubespray-files.sh
diff download-kubespray-files.sh download-kubespray-files.bak

# 다운로드 목록 작성 스크립트(ansible-playbook 후속 실행)에 kube_version=1.33.7 추가
sed -i 's|offline/generate_list.sh|offline/generate_list.sh -e kube_version=1.33.7|g' download-kubespray-files.sh
cat download-kubespray-files.sh | grep kube_version
    LANG=C /bin/bash ${KUBESPRAY_DIR}/contrib/offline/generate_list.sh -e kube_version=1.33.7 || exit 1
# -----------------------------------------------


# 스크립트 실행 후 확인
./download-kubespray-files.sh
cd cache/kubespray-2.30.0/contrib/offline/temp

# 최초 설치 버전과 비교 확인
diff files-2.list files.list
vi -d files-2.list files.list

diff images-2.list images.list
vi -d images-2.list images.list


# (옵션) kube_version 1.33.7 관련 버전을 적용하여 파일과 이미지 다운로드 실행 시
cp download-kubespray-files.bak download-kubespray-files.sh
sed -i 's|offline/generate_list.sh|offline/generate_list.sh -e kube_version=1.33.7|g' download-kubespray-files.sh
./download-kubespray-files.sh&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;&amp;nbsp;K8S 관련 폐쇄망 서비스 실습&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폐쇄망에서 서비스 동작을 위해 필요한 주요 구성요소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oCGhR/dJMcahDlcMh/kVgRPSd5khOKo632q4ZHBk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oCGhR/dJMcahDlcMh/kVgRPSd5khOKo632q4ZHBk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oCGhR/dJMcahDlcMh/kVgRPSd5khOKo632q4ZHBk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoCGhR%2FdJMcahDlcMh%2FkVgRPSd5khOKo632q4ZHBk%2Fimg.jpg&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;1818&quot; height=&quot;209&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;209&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;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 샘플 앱 배포 : nginx:alpine&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109375870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# [k8s-node] 외부 통신은 안되는 상태
ping -c 1 -w 1 -W 1 8.8.8.8
ip route
crictl images
tree /etc/containerd/certs.d/
/etc/containerd/certs.d/
└── 192.168.10.10:35000
    └── hosts.toml


# [admin] nginx 디플로이먼트 배포 시도
cat &amp;lt;&amp;lt; EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine   # docker.io/library/nginx:alpine
          ports:
            - containerPort: 80
EOF

# 확인 : docker.io/library/nginx:alpine 이미지 가져오기 실패!
kubectl describe pod
...
  Warning  Failed     8s    kubelet            Failed to pull image &quot;nginx:alpine&quot;: failed to pull and unpack image &quot;docker.io/library/nginx:alpine&quot;: failed to resolve image: failed to do request: Head &quot;https://registry-1.docker.io/v2/library/nginx/manifests/alpine&quot;: dial tcp [2600:1f18:2148:bc01:c6a5:489e:7233:242c]:443: connect: no route to host
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;gt; (컨테이너) 이미지 저장소에 이미지 push&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109417178&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# [admin] (컨테이너) 이미지 저장소에 이미지 push
podman images

# 로컬에 nginx:alpine 다운로드
podman pull nginx:alpine
docker.io/library/nginx:alpine 선택

podman images | grep nginx

# (컨테이너) 이미지 저장소에 이미지 push
podman tag nginx:alpine 192.168.10.10:35000/library/nginx:alpine
podman images | grep nginx

# 기본적으로 컨테이너 엔진들은 HTTPS를 요구합니다. 내부망에서 HTTP로 테스트하려면 Registry 주소를 '안전하지 않은 저장소'로 등록해야 합니다.
# (참고) registries.conf 는 containers-common 설정이라서, 'podman, skopeo, buildah' 등 전부 동일하게 적용됨.
cat &amp;lt;&amp;lt;EOF &amp;gt;&amp;gt; /etc/containers/registries.conf
[[registry]]
location = &quot;192.168.10.10:35000&quot;
insecure = true
EOF
grep &quot;^[^#]&quot; /etc/containers/registries.conf

# 프라이빗 레지스트리에 업로드 : 성공!
podman push 192.168.10.10:35000/library/nginx:alpine

# 업로드된 이미지와 태그 조회
curl -s 192.168.10.10:35000/v2/_catalog | jq
{
  &quot;repositories&quot;: [
     ...
    &quot;library/nginx&quot;,

curl -s 192.168.10.10:35000/v2/library/nginx/tags/list | jq
{
  &quot;name&quot;: &quot;library/nginx&quot;,
  &quot;tags&quot;: [
    &quot;1.28.0-alpine&quot;,
    &quot;1.29.4&quot;,
    &quot;alpine&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 샘플 앱 이미지 업데이트 후 배포 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109435428&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 현재 파드 상태
kubectl get pod
NAME                    READY   STATUS             RESTARTS   AGE
nginx-54fc99c8d-m6fl5   0/1     ImagePullBackOff   0          16m

# 이미지 정보
kubectl get deploy -owide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
nginx   0/1     1            0           17m   nginx        nginx:alpine   app=nginx

# 디플로이먼트에 이미지 정보 업데이트
kubectl set image deployment/nginx nginx=192.168.10.10:35000/library/nginx:alpine
kubectl get deploy -owide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                                     SELECTOR
nginx   1/1     1            1           19m   nginx        192.168.10.10:35000/library/nginx:alpine   app=nginx

# 현재 파드 상태
kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-5ff7dd7b8-t6b2n   1/1     Running   0          22s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;gt; k8s-node 에서 저장소 미러 설정 후 배포 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109451869&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 삭제 후 다시 디플로이먼트 배포
kubectl delete deployments.apps nginx

cat &amp;lt;&amp;lt; EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine   # docker.io/library/nginx:alpine
          ports:
            - containerPort: 80
EOF

# 현재 파드 상태
kubectl get pod


[k8s-node1, k8s-node2]
# docker.io 대신 내부 이미지 레지스트리 주소 설정
mkdir -p /etc/containerd/certs.d/docker.io
cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/containerd/certs.d/docker.io/hosts.toml
server = &quot;https://docker.io&quot;         # [HTTPS] 원본 registry 주소 , docker.io 를 대상으로 할 때 이 설정을 참조한다는 의미

[host.&quot;http://192.168.10.10:35000&quot;]  # [HTTP] 내부 레지스트리를 미러로 지정 , docker.io 대신 실제 이미지를 가져올 내부 레지스트리 주소
  capabilities = [&quot;pull&quot;, &quot;resolve&quot;] # &quot;pull&quot;: 이미지 다운로드 허용 , &quot;resolve&quot;: 태그 &amp;rarr; 다이제스트 해석
  skip_verify = true                 # HTTPS 인증서 검증 스킵 (HTTP라서 사실상 의미 없는지 테스트 해보자)
EOF
systemctl restart containerd

# 이미지 가져오기 실행 후 확인 : k8s-nodes는 현재 외부 통신 불능 상태인데, 아래 처럼 docker.io 미러 설정되어서 가져오는 것을 확인!
nerdctl pull docker.io/library/nginx:alpine
crictl images | grep nginx
192.168.10.10:35000/library/nginx                   alpine              aea88c29b151e       25.7MB
docker.io/library/nginx                             alpine              aea88c29b151e       25.7MB


# 현재 파드 상태
kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-54fc99c8d-8jpqt   1/1     Running   0          12m&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;gt; kubespary 에 &lt;span style=&quot;color: #387dc9;&quot; data-token-index=&quot;1&quot;&gt;containerd_registries_mirrors&lt;/span&gt; values 설정 후 적용 &lt;span style=&quot;color: #387dc9;&quot; data-token-index=&quot;3&quot;&gt;--tags containerd&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109468911&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[admin]

# containerd registry 정보 확인
cat /root/kubespray-offline/outputs/kubespray-2.30.0/inventory/mycluster/group_vars/all/offline.yml | head -n 15
...
http_server: &quot;http://192.168.10.10&quot;
registry_host: &quot;192.168.10.10:35000&quot;

# Insecure registries for containerd
containerd_registries_mirrors:
  - prefix: &quot;{{ registry_host }}&quot;
    mirrors:
      - host: &quot;http://{{ registry_host }}&quot;
        capabilities: [&quot;pull&quot;, &quot;resolve&quot;]
        skip_verify: true

# 수정
nano inventory/mycluster/group_vars/all/offline.yml
-------------------------------------------------
# Insecure registries for containerd
containerd_registries_mirrors:
  - prefix: &quot;{{ registry_host }}&quot;
    mirrors:
      - host: &quot;http://{{ registry_host }}&quot;
        capabilities: [&quot;pull&quot;, &quot;resolve&quot;]
        skip_verify: true
  - prefix: &quot;docker.io&quot;
    mirrors:
      - host: &quot;http://192.168.10.10:35000&quot;
        capabilities: [&quot;pull&quot;, &quot;resolve&quot;]
        skip_verify: false
  - prefix: &quot;registry-1.docker.io&quot;
    mirrors:
      - host: &quot;http://192.168.10.10:35000&quot;
        capabilities: [&quot;pull&quot;, &quot;resolve&quot;]
        skip_verify: false
  - prefix: &quot;quay.io&quot;
    mirrors:
      - host: &quot;http://192.168.10.10:35000&quot;
        capabilities: [&quot;pull&quot;, &quot;resolve&quot;]
        skip_verify: false
-------------------------------------------------
cat inventory/mycluster/group_vars/all/offline.yml | head -n 30

# 설정 업데이트
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version=&quot;1.34.3&quot; --tags containerd

# 확인
ssh k8s-node2 tree /etc/containerd
/etc/containerd
├── certs.d
│&amp;nbsp;&amp;nbsp; ├── 192.168.10.10:35000
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── hosts.toml
│&amp;nbsp;&amp;nbsp; ├── docker.io
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── hosts.toml
│&amp;nbsp;&amp;nbsp; ├── quay.io
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── hosts.toml
│&amp;nbsp;&amp;nbsp; └── registry-1.docker.io
│&amp;nbsp;&amp;nbsp;     └── hosts.toml
├── config.toml
└── cri-base.json

ssh k8s-node2 cat /etc/containerd/certs.d/quay.io/hosts.toml
server = &quot;https://quay.io&quot;
[host.&quot;http://192.168.10.10:35000&quot;]
  capabilities = [&quot;pull&quot;,&quot;resolve&quot;]
  skip_verify = false
  override_path = false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 이미지 사용하는 디플로이먼트 배포 테스트&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1771109500242&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# quay.io nginx 이미지 가져오기
podman pull quay.io/nginx/nginx-unprivileged
podman images | grep quay

podman tag quay.io/nginx/nginx-unprivileged 192.168.10.10:35000/nginx/nginx-unprivileged
podman images | grep nginx

# 프라이빗 레지스트리에 업로드
podman push 192.168.10.10:35000/nginx/nginx-unprivileged

# 업로드된 이미지와 태그 조회
curl -s 192.168.10.10:35000/v2/_catalog | jq
curl -s 192.168.10.10:35000/v2/nginx/nginx-unprivileged/tags/list | jq


# quay.io nginx 디플로이먼트 배포 시도
cat &amp;lt;&amp;lt; EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-quay
  labels:
    app: nginx-quay
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-quay
  template:
    metadata:
      labels:
        app: nginx-quay
    spec:
      containers:
        - name: nginx-quay
          image: quay.io/nginx/nginx-unprivileged
          ports:
            - containerPort: 80
EOF

# 확인
kubectl get pod -l app=nginx-quay -owide
kubectl get deploy nginx-quay -owide
NAME         READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                             SELECTOR
nginx-quay   1/1     1            1           66s   nginx-quay   quay.io/nginx/nginx-unprivileged   app=nginx-quay

ssh k8s-node2 crictl images | grep quay
quay.io/nginx/nginx-unprivileged                    latest              1805d678a9a4a       60.9MB

# 삭제
kubectl delete deploy nginx-quay&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/53</guid>
      <comments>https://netoppa.tistory.com/53#entry53comment</comments>
      <pubDate>Sun, 15 Feb 2026 01:57:44 +0900</pubDate>
    </item>
    <item>
      <title>[5주차] Kubespary HA &amp;amp; Upgrade</title>
      <link>https://netoppa.tistory.com/52</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 포스팅은 Cloud@net의 기시다님의 온라인 강좌 내용을 정리한 스터디 요약 자료입니다.&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;&lt;span style=&quot;background-color: #9feec3;&quot;&gt; 실습 환경 배포&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;position: absolute;&quot; 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;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;&lt;span data-token-index=&quot;0&quot;&gt;[Case2] &lt;/span&gt;&lt;span style=&quot;color: #9a6bb4;&quot; data-token-index=&quot;1&quot;&gt;External LB (L4스위치) &amp;rarr; 컨트플 플레인 노드(3대)&lt;/span&gt; + &lt;span style=&quot;color: #50946e;&quot; data-token-index=&quot;3&quot;&gt;(Worker Client-Side LoadBalancing)&lt;/span&gt;&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;1541&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FG7DL/dJMcaioHopc/xQjlgvnZtc7ylL9W0EKuv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FG7DL/dJMcaioHopc/xQjlgvnZtc7ylL9W0EKuv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FG7DL/dJMcaioHopc/xQjlgvnZtc7ylL9W0EKuv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFG7DL%2FdJMcaioHopc%2FxQjlgvnZtc7ylL9W0EKuv0%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;1541&quot; height=&quot;740&quot; data-origin-width=&quot;1541&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;스터디 내용은 Vagrant 를 활용한 노드를 구성하고 있으나 실제 프러덕션 환경으로 테스트하기 위해 오픈스택 환경에 설치 및 구성하였기 때문에 설치 방식은 다릅니다.&amp;nbsp;&amp;nbsp;&lt;/blockquote&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;[구성 정보]&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; - 하이퍼바이저 : OPENSTACK 7.1.4 (버전: 2023.2&amp;nbsp; Antelope) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- EXTERNAL LB : 가상화 L4 (PAS-KS 사용)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- admin-node : Kubespray, k8s 베포를 위한 노드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 서버노드 : 인스턴스 5식&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 운영체제 : Rocky linux 9.x&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Kubespray&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;2fa50aec-5edf-816b-9c94-e834486f5a81&quot; style=&quot;border-collapse: collapse; width: 100%; height: 138px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;height: 16px; width: 15.1163%;&quot;&gt;NAMED&lt;/td&gt;
&lt;td style=&quot;height: 16px; width: 20.0582%;&quot;&gt;escription&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Flavor&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 16px; width: 6.6279%;&quot;&gt;CPU&lt;/td&gt;
&lt;td style=&quot;height: 16px; width: 7.32557%;&quot;&gt;RAM&lt;/td&gt;
&lt;td style=&quot;height: 16px; width: 22.5581%;&quot;&gt;NIC&lt;/td&gt;
&lt;td style=&quot;height: 16px; width: 16.8605%;&quot;&gt;floating ip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-818f-8b74-d64ee263ef1c&quot; style=&quot;height: 21px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 21px; width: 15.1163%;&quot;&gt;&lt;b&gt;PAS-KS&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 21px; width: 20.0582%;&quot;&gt;가상화 L4스위치&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.pask&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 21px; width: 6.6279%;&quot;&gt;1&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 21px; width: 7.32557%;&quot;&gt;3GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 21px; width: 22.5581%;&quot;&gt;-&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 21px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.222&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.1163%; height: 21px;&quot;&gt;&lt;b&gt; &lt;b&gt;k8s-admin&lt;/b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20.0582%; height: 21px;&quot;&gt;K8S admin&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.small&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 6.6279%; height: 21px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 7.32557%; height: 21px;&quot;&gt;2GB&lt;/td&gt;
&lt;td style=&quot;width: 22.5581%; height: 21px;&quot;&gt;100.100.100.244&lt;/td&gt;
&lt;td style=&quot;width: 16.8605%; height: 21px;&quot;&gt;&lt;b&gt;192.168.7.244&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-8174-b8a2-da1fc332a7b6&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 16px; width: 15.1163%;&quot;&gt;&lt;b&gt;k8s-c1&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 16px; width: 20.0582%;&quot;&gt;K8S ControlPlane&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.large&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 16px; width: 6.6279%;&quot;&gt;4&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 16px; width: 7.32557%;&quot;&gt;8GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 16px; width: 22.5581%;&quot;&gt;100.100.100.245&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 16px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.245&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-8109-9cab-fb7d30f454ea&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 16px; width: 15.1163%;&quot;&gt;&lt;b&gt;k8s-c2&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 16px; width: 20.0582%;&quot;&gt;K8S ControlPlane&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.large&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 16px; width: 6.6279%;&quot;&gt;4&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 16px; width: 7.32557%;&quot;&gt;8GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 16px; width: 22.5581%;&quot;&gt;100.100.100.246&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 16px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.246&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-81c8-a09e-c054d58c8360&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 16px; width: 15.1163%;&quot;&gt;&lt;b&gt;k8s-c3&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 16px; width: 20.0582%;&quot;&gt;K8S ControlPlane&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.large&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 16px; width: 6.6279%;&quot;&gt;4&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 16px; width: 7.32557%;&quot;&gt;8GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 16px; width: 22.5581%;&quot;&gt;100.100.100.247&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 16px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.247&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-8184-9fc6-e7722ff043ce&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 16px; width: 15.1163%;&quot;&gt;&lt;b&gt;k8s-w1&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 16px; width: 20.0582%;&quot;&gt;K8S Worker&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.medium&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 16px; width: 6.6279%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 16px; width: 7.32557%;&quot;&gt;4GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 16px; width: 22.5581%;&quot;&gt;100.100.100.248&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 16px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.248&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2fa50aec-5edf-815f-840f-d9a6f0229e2c&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;HHee&quot; style=&quot;height: 16px; width: 15.1163%;&quot;&gt;&lt;b&gt;k8s-w2&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bbH?&quot; style=&quot;height: 16px; width: 20.0582%;&quot;&gt;K8S Worker&lt;/td&gt;
&lt;td style=&quot;width: 11.4535%; height: 16px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;m1.medium&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;y&amp;lt;=y&quot; style=&quot;height: 16px; width: 6.6279%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;KAmR&quot; style=&quot;height: 16px; width: 7.32557%;&quot;&gt;4GB&lt;/td&gt;
&lt;td id=&quot;~nZU&quot; style=&quot;height: 16px; width: 22.5581%;&quot;&gt;100.100.100.249&lt;/td&gt;
&lt;td id=&quot;RuHK&quot; style=&quot;height: 16px; width: 16.8605%;&quot;&gt;&lt;b&gt;192.168.7.249&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ynOEA/dJMcabiN0xj/ku6cZvCr7KUUk5TYMN5KY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ynOEA/dJMcabiN0xj/ku6cZvCr7KUUk5TYMN5KY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ynOEA/dJMcabiN0xj/ku6cZvCr7KUUk5TYMN5KY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FynOEA%2FdJMcabiN0xj%2Fku6cZvCr7KUUk5TYMN5KY0%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;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&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;891&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czoZb8/dJMcah4nmMu/lYNLjRbUiTVWg83w1TKW00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czoZb8/dJMcah4nmMu/lYNLjRbUiTVWg83w1TKW00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czoZb8/dJMcah4nmMu/lYNLjRbUiTVWg83w1TKW00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczoZb8%2FdJMcah4nmMu%2FlYNLjRbUiTVWg83w1TKW00%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;891&quot; height=&quot;910&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;실습 환경 배포 수행&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오픈스택 &lt;b&gt;Orchestration Stack&lt;/b&gt; 노드 베포 설정&lt;/p&gt;
&lt;pre id=&quot;code_1770356671971&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;heat_template_version: &quot;2018-08-31&quot;

description: &amp;gt;
  Rocky Linux 9.6 기반 K8S 클러스터 구성 (6개 노드)
  부팅 소스 충돌 해결 및 OS::Heat::CloudConfig 적용 버전

parameters:
  key_name:
    type: string
    default: &quot;cli-key&quot;
  flavor_admin:
    type: string
    default: &quot;m1.small&quot;
  flavor_ctrl:
    type: string
    default: &quot;m1.large&quot;
  flavor_worker:
    type: string
    default: &quot;m1.medium&quot;
  private_subnet:
    type: string
    default: &quot;cli-subnet&quot;

resources:
  # [통합 초기화 스크립트 리소스]
  common_script:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        runcmd:
          - ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
          - systemctl stop firewalld &amp;amp;&amp;amp; systemctl disable firewalld
          - setenforce 0
          - sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
          - swapoff -a
          - sed -i '/swap/s/^/#/' /etc/fstab
          - |
            cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/modules-load.d/k8s.conf
            overlay
            br_netfilter
            EOF
          - modprobe overlay
          - modprobe br_netfilter
          - |
            cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/sysctl.d/k8s.conf
            net.bridge.bridge-nf-call-iptables  = 1
            net.bridge.bridge-nf-call-ip6tables = 1
            net.ipv4.ip_forward                 = 1
            EOF
          - sysctl --system
          - dnf install -y sshpass jq git tree vim tar curl &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
          - echo 'root:qwe123' | chpasswd
          - echo 'vagrant:qwe123' | chpasswd
          - sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
          - echo &quot;PermitRootLogin yes&quot; &amp;gt;&amp;gt; /etc/ssh/sshd_config
          - systemctl restart sshd
          - |
            cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
            100.100.100.244 k8s-admin
            100.100.100.245 k8s-c1
            100.100.100.246 k8s-c2
            100.100.100.247 k8s-c3
            100.100.100.248 k8s-w1
            100.100.100.249 k8s-w2
            EOF

  # [1] 관리 노드 (20GB)
  k8s_admin:
    type: OS::Nova::Server
    properties:
      name: k8s-admin
      flavor: { get_param: flavor_admin }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.244 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 20, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  # [2] 컨트롤 플레인 1 (80GB)
  k8s_c1:
    type: OS::Nova::Server
    properties:
      name: k8s-c1
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.245 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 80, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  # [3] 컨트롤 플레인 2 (80GB)
  k8s_c2:
    type: OS::Nova::Server
    properties:
      name: k8s-c2
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.246 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 80, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  # [4] 컨트롤 플레인 3 (80GB)
  k8s_c3:
    type: OS::Nova::Server
    properties:
      name: k8s-c3
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.247 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 80, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  # [5] 워커 노드 1 (40GB)
  k8s_w1:
    type: OS::Nova::Server
    properties:
      name: k8s-w1
      flavor: { get_param: flavor_worker }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.248 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 40, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  # [6] 워커 노드 2 (40GB)
  k8s_w2:
    type: OS::Nova::Server
    properties:
      name: k8s-w2
      flavor: { get_param: flavor_worker }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.249 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 40, delete_on_termination: true }]
      user_data_format: RAW
      user_data: { get_resource: common_script }&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Kubespary 를 통한 k8s 배포 &lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #50946e;&quot; data-token-index=&quot;0&quot;&gt;(Worker Client-Side LoadBalancing)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;k8s-admin 노드 설정 작업&lt;/h4&gt;
&lt;pre id=&quot;code_1770442278154&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo -i
cat &amp;gt;/etc/hosts &amp;lt;&amp;lt;'EOF'
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
100.100.100.244 k8s-admin
100.100.100.245 k8s-c1
100.100.100.246 k8s-c2
100.100.100.247 k8s-c3
100.100.100.248 k8s-w1
100.100.100.249 k8s-w2
EOF&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;/h4&gt;
&lt;pre id=&quot;code_1770442316078&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo -i
dnf install -y git rsync gcc python3 python3-pip python3-devel
dnf install -y nmap-ncat jq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;SSH 키 생성 및 전송&lt;/p&gt;
&lt;pre id=&quot;code_1770427754855&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo -i
mkdir -p /root/.ssh &amp;amp;&amp;amp; chmod 700 /root/.ssh
[ -f /root/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -N &quot;&quot; -f /root/.ssh/id_ed25519
NODES=(k8s-c1 k8s-c2 k8s-c3 k8s-w1 k8s-w2)

for h in &quot;${NODES[@]}&quot;; do
  ssh-copy-id -i /root/.ssh/id_ed25519.pub -o StrictHostKeyChecking=no rocky@&quot;$h&quot;
done


# 로그인 검증
for h in &quot;${NODES[@]}&quot;; do
  echo -n &quot;&amp;gt;&amp;gt; $h : &quot;
  ssh -o BatchMode=yes -i /root/.ssh/id_ed25519 rocky@&quot;$h&quot; &quot;hostname; whoami&quot; &amp;amp;&amp;amp; echo &quot;OK&quot; || echo &quot;FAIL&quot;
done



# k8s-c1~c3(컨트롤플레인/etcd)에서 확인
ssh rocky@k8s-c1 &quot;python3 -V; sudo -n true &amp;amp;&amp;amp; echo SUDO_OK&quot;
ssh rocky@k8s-c2 &quot;python3 -V; sudo -n true &amp;amp;&amp;amp; echo SUDO_OK&quot;
ssh rocky@k8s-c3 &quot;python3 -V; sudo -n true &amp;amp;&amp;amp; echo SUDO_OK&quot;

# k8s-w1~w2(워커)에서 확인
ssh rocky@k8s-w1 &quot;python3 -V; sudo -n true &amp;amp;&amp;amp; echo SUDO_OK&quot;
ssh rocky@k8s-w2 &quot;python3 -V; sudo -n true &amp;amp;&amp;amp; echo SUDO_OK&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Kubespray 설치/가상환경&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Kubespray clone + venv 구성&lt;/h4&gt;
&lt;pre id=&quot;code_1770442475463&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo -i
mkdir -p ~/k8s-ha-kubespray &amp;amp;&amp;amp; cd ~/k8s-ha-kubespray
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray

# python3.11을 쓰는 경우: python3.11 -m venv venv
python3 -m venv venv
source venv/bin/activate
pip install -U pip
pip install -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인벤토리 작성&lt;/h4&gt;
&lt;pre id=&quot;code_1770442548391&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inventory 생성

cd ~/k8s-ha-kubespray/kubespray
source venv/bin/activate
cp -rfp inventory/sample inventory/mycluster


# inventory.ini

cat &amp;gt; inventory/mycluster/inventory.ini &amp;lt;&amp;lt;'EOF'
[all]
k8s-c1 ansible_host=100.100.100.245 ip=100.100.100.245 access_ip=100.100.100.245
k8s-c2 ansible_host=100.100.100.246 ip=100.100.100.246 access_ip=100.100.100.246
k8s-c3 ansible_host=100.100.100.247 ip=100.100.100.247 access_ip=100.100.100.247
k8s-w1 ansible_host=100.100.100.248 ip=100.100.100.248 access_ip=100.100.100.248
k8s-w2 ansible_host=100.100.100.249 ip=100.100.100.249 access_ip=100.100.100.249

[kube_control_plane]
k8s-c1
k8s-c2
k8s-c3

[etcd]
k8s-c1
k8s-c2
k8s-c3

[kube_node]
k8s-w1
k8s-w2

[k8s_cluster:children]
kube_control_plane
kube_node

[all:vars]
ansible_user=rocky
EOF

# 검증

ansible-inventory -i inventory/mycluster/inventory.ini --host k8s-c1 \
 | egrep -i 'ansible_host|ip|access_ip|ansible_user'

ansible-inventory -i inventory/mycluster/inventory.ini --host k8s-c1 \
 | egrep -i 'ansible_host|ip|access_ip|ansible_user'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;클러스터 변수 최적화 (Addons &amp;amp; Network)&lt;/p&gt;
&lt;pre id=&quot;code_1770443861021&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /root/kubespray/ 2&amp;gt;/dev/null || cd ~/k8s-ha-kubespray/kubespray
source venv/bin/activate

# 기본 설정 변경 (owner, plugin, proxy 모드 등)
sed -i 's|kube_owner: kube|kube_owner: root|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_network_plugin: calico|kube_network_plugin: flannel|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_proxy_mode: ipvs|kube_proxy_mode: iptables|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|enable_nodelocaldns: true|enable_nodelocaldns: false|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml

# [중요] 외부 L4 VIP(PAS-KS)를 API Server 로드밸런서로 등록
# -&amp;gt; 인증서 SAN에 VIP가 포함되고, kubectl이 L4를 통해 통신 가능
# (중복 방지: 이미 있으면 추가하지 않음)
grep -q '^apiserver_loadbalancer_domain_name:' inventory/mycluster/group_vars/all/all.yml \
  || echo 'apiserver_loadbalancer_domain_name: &quot;PAS-KS&quot;' &amp;gt;&amp;gt; inventory/mycluster/group_vars/all/all.yml

grep -q '^loadbalancer_apiserver:' inventory/mycluster/group_vars/all/all.yml \
  || echo 'loadbalancer_apiserver: { address: 192.168.7.222, port: 6443 }' &amp;gt;&amp;gt; inventory/mycluster/group_vars/all/all.yml

# Flannel 인터페이스 지정 (대부분 eth0, 노드에서 실제 인터페이스명 확인 권장)
# (중복 방지)
grep -q '^flannel_interface:' inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml \
  || echo &quot;flannel_interface: eth0&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml

# Metrics Server 활성화 및 자원 최적화
sed -i 's|metrics_server_enabled: false|metrics_server_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
grep -q '^metrics_server_requests_cpu:' inventory/mycluster/group_vars/k8s_cluster/addons.yml \
  || echo &quot;metrics_server_requests_cpu: 25m&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/addons.yml
grep -q '^metrics_server_requests_memory:' inventory/mycluster/group_vars/k8s_cluster/addons.yml \
  || echo &quot;metrics_server_requests_memory: 16Mi&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/addons.yml
  
  
  # 적용값 확인
  grep -iE 'kube_owner:|kube_network_plugin:|kube_proxy_mode:|enable_nodelocaldns:' \
  inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml

egrep -n 'apiserver_loadbalancer_domain_name|loadbalancer_apiserver' \
  inventory/mycluster/group_vars/all/all.yml

grep -n '^flannel_interface:' inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml

egrep -n 'metrics_server_enabled|metrics_server_requests_' \
  inventory/mycluster/group_vars/k8s_cluster/addons.yml&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;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;VIP(PAS-KS) 연동 설정&lt;/h4&gt;
&lt;pre id=&quot;code_1770442652474&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# L4스위치를 VIP 진입점으로 사용할 때

cat &amp;gt;&amp;gt; inventory/mycluster/group_vars/all/all.yml &amp;lt;&amp;lt;'EOF'
apiserver_loadbalancer_domain_name: &quot;PAS-KS&quot;
loadbalancer_apiserver:
  address: 192.168.7.221
  port: 6443
EOF&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;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;가상화 L4스위치(PAS-K) 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1770467127218&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# L4스위치 설정
# 리얼서버 3식 (control-plane)
# LB 방식 : Round-Robin
# port : 6443
# vip : 192.168.7.221

test-L4# show run brief slb k8s_cluster
!
! Application switch configuration (v2.2.7.3.1)
! 2026/02/06 16:08:16
!
! Slb configuration
!
slb k8s_cluster
  nat-mode lan-to-lan
  health-check 100
!   id 100, type tcp, port 6443
  vip 192.168.7.221 protocol tcp vport 6443
  lan-to-lan 0.0.0.0/0
  apply
  filter 1
    protocol tcp
    dip 192.168.7.221/32
    dport 6443
    apply
  real 245
!   id 245, rip 192.168.7.245
  real 246
!   id 246, rip 192.168.7.246
  real 247
!   id 247, rip 192.168.7.247
  apply
  exit&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 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;배포 전 최종 접속 점검&lt;/h4&gt;
&lt;pre id=&quot;code_1770443779850&quot; class=&quot;awk&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# ping 테스트

cd ~/k8s-ha-kubespray/kubespray
source venv/bin/activate
ansible -i inventory/mycluster/inventory.ini all -u rocky -b -m ping&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;p data-ke-size=&quot;size18&quot;&gt;Kubespray 배포 시작&lt;/p&gt;
&lt;pre id=&quot;code_1770443926236&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/k8s-ha-kubespray/kubespray
source venv/bin/activate

# 배포 전 Task 확인
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --list-tasks

# 실제 설치 실행 (v1.32.9 기준)
ANSIBLE_FORCE_COLOR=true \
ansible-playbook -i inventory/mycluster/inventory.ini -b -v cluster.yml \
  -e kube_version=&quot;1.32.9&quot; | tee kubespray_install.log&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;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;배포 후 검증&lt;/h3&gt;
&lt;pre id=&quot;code_1770363239485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. API 호출 확인 (PAS-KS L4 VIP 기준)
echo &quot;&amp;gt;&amp;gt; [Checking K8S API via PAS-KS L4] &amp;lt;&amp;lt;&quot;
curl -sk https://192.168.7.222:6443/version | grep Version

# 2. 자격증명 설정 (admin 노드에서 실행)
mkdir -p /root/.kube
scp k8s-c1:/root/.kube/config /root/.kube/config

# 3. API Server 주소를 L4 VIP로 고정 (고가용성 확보)
sed -i 's/127.0.0.1/192.168.7.222/g' /root/.kube/config
kubectl cluster-info

# 4. 노드 상태 확인
kubectl get nodes -owide

# 5. ETCD 상태 확인 (c1~c3 대상)
for i in {1..3}; do 
    echo &quot;&amp;gt;&amp;gt; k8s-c$i &amp;lt;&amp;lt;&quot;; 
    ssh k8s-c$i etcdctl.sh endpoint status -w table; 
    echo; 
done

# 6. 자동완성 및 별칭 설정
cat &amp;lt;&amp;lt;EOF &amp;gt;&amp;gt; /etc/profile
source &amp;lt;(kubectl completion bash)
alias k=kubectl
alias kc=kubecolor
complete -F __start_kubectl k
EOF
source /etc/profile&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;클러스터 상태&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;2394&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mdcPm/dJMcabJSW9y/C3Gi6Jy0Z1NfGCkaH4b4e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mdcPm/dJMcabJSW9y/C3Gi6Jy0Z1NfGCkaH4b4e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mdcPm/dJMcabJSW9y/C3Gi6Jy0Z1NfGCkaH4b4e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmdcPm%2FdJMcabJSW9y%2FC3Gi6Jy0Z1NfGCkaH4b4e0%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;2394&quot; height=&quot;141&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;PAS-K L4 스위치 클러스터 서비스 확인&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;1813&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vcz3y/dJMcajnxzrl/07ECwW974FmXAzi8ETzlR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vcz3y/dJMcajnxzrl/07ECwW974FmXAzi8ETzlR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vcz3y/dJMcajnxzrl/07ECwW974FmXAzi8ETzlR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvcz3y%2FdJMcajnxzrl%2F07ECwW974FmXAzi8ETzlR0%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;1813&quot; height=&quot;486&quot; data-origin-width=&quot;1813&quot; data-origin-height=&quot;486&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;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;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; &lt;span style=&quot;color: #50946e;&quot; data-token-index=&quot;0&quot;&gt;(Worker Client-Side LoadBalancing)&lt;/span&gt; Kubespary 를 통한 k8s 배포&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사전설정(ssh 키설정 동기화)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770603403377&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NODES=&quot;k8s-c1 k8s-c2 k8s-c3 k8s-w1 k8s-w2&quot;

for n in $NODES; do
  echo &quot;===== APPLY: $n =====&quot;
  ssh root@&quot;$n&quot; 'set -e

# 1) 스크립트 생성
cat &amp;gt; /usr/local/sbin/fix-root-authorizedkeys.sh &amp;lt;&amp;lt;'&quot;'&quot;'EOF'&quot;'&quot;'
#!/usr/bin/env bash
set -euo pipefail

install -d -m 700 -o root -g root /root/.ssh

cat &amp;gt; /root/.ssh/authorized_keys &amp;lt;&amp;lt;'&quot;'&quot;'EOK'&quot;'&quot;'
no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command=&quot;echo '&quot;'&quot;'Please login as the user \&quot;rocky\&quot; rather than the user \&quot;root\&quot;.'&quot;'&quot;';echo;sleep 10;exit 142&quot; ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs1SkMGLxNzWgK75TDiII1KWqNfLHWk5X792nNCuAM3zjMvSiE2T9rEpM9Gko3gyxIaInCsmqAHqyqFX5LP6txy+dZEPusKKSDZvbWFcg/8tyuYXR0gC4G8tR/PEmtEh5RoGJWQKXNMzN1NXdW8X1saaRP1Usxl9pX0/nFKyMlD6Fn9VR1dMqEUfybENTHSxeMgkCi3agvprtbgfsBUxvX87n5G967sZWDTgAgf2/+MKPfY4xEPcfZI0efE/YfDotbKP2QANtzEeXDb986G72cy4r8lIGjowmrrDyYmCU9vkKsDwP5tq4Fwi0wuCs2QXUbK0PVMBKEx+3iWDZe0bcr Generated-by-Nova
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRGOoRW7879SCsJIbqmYEKDpa6jJNBla2FgEmrrlpcR root@k8s-admin.novalocal
EOK

chmod 600 /root/.ssh/authorized_keys
chown root:root /root/.ssh/authorized_keys
restorecon -Rv /root/.ssh &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || true
EOF
chmod +x /usr/local/sbin/fix-root-authorizedkeys.sh

# 2) systemd service
cat &amp;gt; /etc/systemd/system/fix-root-authorizedkeys.service &amp;lt;&amp;lt;'&quot;'&quot;'EOF'&quot;'&quot;'
[Unit]
Description=Force /root/.ssh/authorized_keys to lab value
After=network-online.target sshd.service
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/fix-root-authorizedkeys.sh

[Install]
WantedBy=multi-user.target
EOF

# 3) systemd timer (원복 대비)
cat &amp;gt; /etc/systemd/system/fix-root-authorizedkeys.timer &amp;lt;&amp;lt;'&quot;'&quot;'EOF'&quot;'&quot;'
[Unit]
Description=Periodically enforce /root/.ssh/authorized_keys

[Timer]
OnBootSec=10
OnUnitActiveSec=30
Unit=fix-root-authorizedkeys.service

[Install]
WantedBy=timers.target
EOF

# 4) enable + run
systemctl daemon-reload
systemctl enable --now fix-root-authorizedkeys.service
systemctl enable --now fix-root-authorizedkeys.timer
/usr/local/sbin/fix-root-authorizedkeys.sh

# 5) verify
echo &quot;### verify on $(hostname)&quot;
nl -ba /root/.ssh/authorized_keys
'
done&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;워커노드 배포&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770465244462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd /root/k8s-ha-kubespray/kubespray
source venv/bin/activate
pwd
# /root/k8s-ha-kubespray/kubespray
 
# inventory 디렉터리 확인
cd /root/k8s-ha-kubespray/kubespray
source venv/bin/activate

tree inventory/mycluster/

# inventory.ini
cat inventory/mycluster/inventory.ini

[kube_control_plane]
k8s-c1 ansible_host=100.100.100.245 ip=100.100.100.245 etcd_member_name=etcd1
k8s-c2 ansible_host=100.100.100.246 ip=100.100.100.246 etcd_member_name=etcd2
k8s-c3 ansible_host=100.100.100.247 ip=100.100.100.247 etcd_member_name=etcd3

[etcd:children]
kube_control_plane

[kube_node]
k8s-w1 ansible_host=100.100.100.248 ip=100.100.100.248
k8s-w2 ansible_host=100.100.100.249 ip=100.100.100.249

# 확인
ansible-inventory -i inventory/mycluster/inventory.ini --graph

# 워커에 ssh 접속 가능한지 확인
ssh rocky@100.100.100.248 &quot;hostname; id&quot;
ssh rocky@100.100.100.249 &quot;hostname; id&quot;


# inventory.ini의 워커 라인에 ansible_user 추가
ansible -i inventory/mycluster/inventory.ini kube_node -m ping -b -o

# Ansible ping으로 최종 확인
ansible -i inventory/mycluster/inventory.ini kube_node -m ping -b -o


# k8s-cluster.yml
grep -iE 'kube_owner:|kube_network_plugin:|kube_proxy_mode:|enable_nodelocaldns:|enable_dns_autoscaler:' \
  inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
  
  
# flannel 인터페이스

grep -n &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml


# 배포 전 Task 확인
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --list-tasks

# 워커만 설치 실행
ANSIBLE_FORCE_COLOR=true \
ansible-playbook -i inventory/mycluster/inventory.ini -b -v cluster.yml \
  -e kube_version=&quot;1.32.9&quot; \
  --limit kube_node | tee kubespray_worker_install.log&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 설치 확인 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770466757169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get node -owide
kubectl describe node k8s-w1 | egrep -i 'Ready|Taints|NetworkUnavailable|Conditions'
kubectl describe node k8s-w2 | egrep -i 'Ready|Taints|NetworkUnavailable|Conditions'
kubectl get pod -A -owide | head -n 80&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;span data-token-index=&quot;0&quot;&gt;Kubespray 변수 우선순위&lt;/span&gt; 구조 및 검색&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ansible variable precedence : 숫자가 높을수록 우선순위가 높음 - &lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable&quot;&gt;Ansible_Docs&lt;/a&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Command-line ****values (for example,&amp;nbsp;u&amp;nbsp;my_user, these are not variables)&lt;/li&gt;
&lt;li&gt;Role defaults (as defined in&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_reuse_roles.html#role-directory-structure&quot;&gt;Role directory structure&lt;/a&gt;)&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id13&quot;&gt;1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inventory file or script group vars&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id14&quot;&gt;2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inventory &lt;b&gt;group_vars/all&lt;/b&gt;&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Playbook &lt;b&gt;group_vars/all&lt;/b&gt;&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inventory &lt;b&gt;group_vars/&lt;/b&gt;*&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Playbook &lt;b&gt;group_vars/&lt;/b&gt;*&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inventory file or script host vars&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id14&quot;&gt;2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Inventory &lt;b&gt;host_vars/&lt;/b&gt;*&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Playbook &lt;b&gt;host_vars/&lt;/b&gt;*&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id15&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Host facts and cached set_facts&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id16&quot;&gt;4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Play vars&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Play vars_prompt&lt;/li&gt;
&lt;li&gt;Play vars_files&lt;/li&gt;
&lt;li&gt;Role vars (as defined in&amp;nbsp;&lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_reuse_roles.html#role-directory-structure&quot;&gt;Role directory structure&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Block vars (for tasks in block only)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Task vars&lt;/b&gt; (for the task only)&lt;/li&gt;
&lt;li&gt;include_vars&lt;/li&gt;
&lt;li&gt;Registered vars and set_facts&lt;/li&gt;
&lt;li&gt;Role (and include_role) params&lt;/li&gt;
&lt;li&gt;include params&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Extra vars&lt;/b&gt; (for example,&amp;nbsp;e&amp;nbsp;&quot;user=my_user&quot;)(always win precedence)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1770444898565&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예시) 특정 변수 선언 및 사용 검색
grep -Rn &quot;allow_unsupported_distribution_setup&quot; inventory/mycluster/ playbooks/ roles/ -A1 -B1
inventory/mycluster/group_vars/all/all.yml-141-## If enabled it will allow kubespray to attempt setup even if the distribution is not supported. For unsupported distributions this can lead to unexpected failures in some cases.
inventory/mycluster/group_vars/all/all.yml:142:allow_unsupported_distribution_setup: false
--
roles/kubernetes/preinstall/tasks/0040-verify-settings.yml-22-  assert:
roles/kubernetes/preinstall/tasks/0040-verify-settings.yml:23:    that: (allow_unsupported_distribution_setup | default(false)) or ansible_distribution in supported_os_distributions
roles/kubernetes/preinstall/tasks/0040-verify-settings.yml-24-    msg: &quot;{{ ansible_distribution }} is not a known OS&quot;


# Kubespray 변수 우선순위 구조 (Override Flow)
[ 낮은 우선순위 ]
┌─────────────────────────────────────────────┐
│ roles/*/defaults/main.yml                   │  &amp;larr; Kubespray role 기본값
│   (예: bin_dir, kube_version 기본값)        │
└─────────────────────────────────────────────┘
                ⬇ override

┌─────────────────────────────────────────────┐
│ roles/*/vars/main.yml                       │  &amp;larr; role 내부 강제 변수 (웬만해선 안 건드림)
└─────────────────────────────────────────────┘
                ⬇ override

┌─────────────────────────────────────────────┐
│ inventory/mycluster/group_vars/all/*.yml    │  &amp;larr; 전체 노드 공통 설정 # 99% 여기서 조절
│ inventory/mycluster/group_vars/k8s_cluster/*.yml
│ inventory/mycluster/group_vars/etcd.yml     │
└─────────────────────────────────────────────┘
                ⬇ override

┌─────────────────────────────────────────────┐
│ inventory/mycluster/host_vars/&amp;lt;node&amp;gt;.yml    │  &amp;larr; 특정 노드에만 적용 # 특정 노드만 다르게 쓸 때
└─────────────────────────────────────────────┘
                ⬇ override

┌─────────────────────────────────────────────┐
│ playbook vars (vars:, vars_files:)          │  &amp;larr; reset.yml / cluster.yml 내부 vars # 실행하는 플레이북에 선언된 경우
└─────────────────────────────────────────────┘
                ⬇ override

┌─────────────────────────────────────────────┐
│ --extra-vars (-e)                           │  &amp;larr; CLI에서 준 값 (최강자)
│   ex) -e kube_version=v1.29.3               │
└─────────────────────────────────────────────┘
[ 높은 우선순위 ]


# 전체 노드 공통 설정(예시) : 99% 여기서 조절
inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
kube_version: v1.29.3
kube_network_plugin: cilium

# 특정 노드에만 적용(예시) : 특정 노드만 다르게 쓸 때
inventory/mycluster/host_vars/k8s-ctr1.yml
node_labels:
  node-role.kubernetes.io/control-plane: &quot;true&quot;

# 실행하는 플레이북에 선언된 경우(예) : 아래 경우 플레이북을 import 할 때만 적용되는 &amp;ldquo;로컬 변수 override&amp;rdquo;
cat playbooks/scale.yml | grep 'Install etcd' -A5
- name: Install etcd
  vars:                               # inventory/group_vars 보다 우선순위가 높음, 이 playbook import 범위 안에서만 유효
    etcd_cluster_setup: false         # etcd 신규 클러스터 bootstrap 로직 비활성화
    etcd_events_cluster_setup: false  # 이벤트 전용 etcd 클러스터 구성 안 함
  import_playbook: install_etcd.yml   # install_etcd.yml이라는 별도의 플레이북 파일을 현재 위치에 포함시켜 실행하라는 명령


# 예시) dns autoscaler 미설치 하기 위해 검색
grep -Rni &quot;autoscaler&quot; inventory/mycluster/ playbooks/ roles/ -A2 -B1
grep -Rni &quot;autoscaler&quot; inventory/mycluster/ playbooks/ roles/ --include=&quot;*.yml&quot; -A2 -B1
roles/kubespray_defaults/defaults/main/main.yml:130:# Enable dns autoscaler
roles/kubespray_defaults/defaults/main/main.yml:131:enable_dns_autoscaler: 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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;kube-ops-view 설치&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1770460933347&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# kube-ops-view
## helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/


# macOS 사용자
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 \
  --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 \
  --set env.TZ=&quot;Asia/Seoul&quot; --namespace kube-system \
  --set image.repository=&quot;abihf/kube-ops-view&quot; --set image.tag=&quot;latest&quot;

# Windows 사용자
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 \
  --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 \
  --set env.TZ=&quot;Asia/Seoul&quot; --namespace kube-system 

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : nodePor 이므로 IP는 all node 의 IP 가능!
open &quot;http://192.168.10.14:30000/#scale=1.5&quot;
open &quot;http://192.168.10.14:30000/#scale=2&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xxvLK/dJMcacWhIO5/hk6lGKtDRcPWpZkK5hK3eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xxvLK/dJMcacWhIO5/hk6lGKtDRcPWpZkK5hK3eK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xxvLK/dJMcacWhIO5/hk6lGKtDRcPWpZkK5hK3eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxxvLK%2FdJMcacWhIO5%2Fhk6lGKtDRcPWpZkK5hK3eK%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;1135&quot; height=&quot;309&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;309&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;샘플 애플리케이션 배포, 반복 호출&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 샘플 애플리케이션 배포&lt;/p&gt;
&lt;pre id=&quot;code_1770460973471&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 샘플 애플리케이션 배포
cat &amp;lt;&amp;lt; EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: &quot;kubernetes.io/hostname&quot;
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30003
  type: NodePort
EOF&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;- 반복 호출&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6ZyfW/dJMcahpMtkU/EySfKjbqH6F3gasKIKZCIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6ZyfW/dJMcahpMtkU/EySfKjbqH6F3gasKIKZCIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6ZyfW/dJMcahpMtkU/EySfKjbqH6F3gasKIKZCIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6ZyfW%2FdJMcahpMtkU%2FEySfKjbqH6F3gasKIKZCIK%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;1000&quot; height=&quot;1369&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1770461008979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 배포 확인
kubectl get deploy,svc,ep webpod -owide

[admin-lb] # IP는 node 작업에 따라 변경
while true; do curl -s http://192.168.10.14:30003 | grep Hostname; sleep 1; done


# (옵션) k8s-node 에서 service 명 호출 확인
ssh k8s-node1 cat /etc/resolv.conf
# Generated by NetworkManager
search default.svc.cluster.local svc.cluster.local
nameserver 10.233.0.3
nameserver 168.126.63.1
nameserver 8.8.8.8
options ndots:2 timeout:2 attempts:2

# 성공
ssh k8s-node1 curl -s webpod -I
HTTP/1.1 200 OK

# 성공
ssh k8s-node1 curl -s webpod.default -I
HTTP/1.1 200 OK

# 실패
ssh k8s-node1 curl -s webpod.default.svc -I
ssh k8s-node1 curl -s webpod.default.svc.cluster -I

# 성공
ssh k8s-node1 curl -s webpod.default.svc.cluster.local -I
HTTP/1.1 200 OK&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; (장애 재현) 만약 컨트롤 플레인 1번 노드 장애 발생 시 영향도 &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;1268&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rEYqp/dJMcaiIWTgX/viYK3QpE0fVcgNwkCmH1Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rEYqp/dJMcaiIWTgX/viYK3QpE0fVcgNwkCmH1Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rEYqp/dJMcaiIWTgX/viYK3QpE0fVcgNwkCmH1Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrEYqp%2FdJMcaiIWTgX%2FviYK3QpE0fVcgNwkCmH1Hk%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;1268&quot; height=&quot;140&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1770461052376&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# [admin-lb] kubeconfig 자격증명 사용 시 정보 확인
cat /root/.kube/config | grep server
    server: https://192.168.10.11:6443

    
# 모니터링 : 신규 터미널 4개
# ----------------------
## [admin-lb]
while true; do kubectl get node ; echo ; curl -sk https://192.168.10.12:6443/version | grep gitVersion ; sleep 1; echo ; done

## [k8s-node2]
watch -d kubectl get pod -n kube-system
kubectl logs -n kube-system nginx-proxy-k8s-node4 -f

## [k8s-node4] 
while true; do curl -sk https://127.0.0.1:6443/version | grep gitVersion ; date; sleep 1; echo ; done
# ----------------------


# 장애 재현 
[k8s-node1] poweroff


# [k8s-node2]
kubectl logs -n kube-system nginx-proxy-k8s-node4 -f
2026/01/28 12:47:08 [error] 20#20: *3145 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: 127.0.0.1:6443, upstream: &quot;192.168.10.11:6443&quot;, bytes from/to client:0/0, bytes from/to upstream:0/0
2026/01/28 12:47:08 [warn] 20#20: *3145 upstream server temporarily disabled while connecting to upstream, client: 127.0.0.1, server: 127.0.0.1:6443, upstream: &quot;192.168.10.11:6443&quot;, bytes from/to client:0/0, bytes from/to upstream:0/0

# [k8s-node4] 하지만 백엔드 대상 서버가 나머지 2대가 있으니 아래 요청 처리 정상!
while true; do curl -sk https://127.0.0.1:6443/version | grep gitVersion ; date; sleep 1; echo ; done
  &quot;gitVersion&quot;: &quot;v1.32.9&quot;,

# [admin-lb] 아래 자격증명 서버 정보 수정 필요
while true; do kubectl get node ; echo ; curl -sk https://192.168.10.12:6443/version | grep gitVersion ; sleep 1; echo ; done
Unable to connect to the server: dial tcp 192.168.10.11:6443: connect: no route to host  # &amp;lt;&amp;lt; 요건 실패!
  &quot;gitVersion&quot;: &quot;v1.32.9&quot;,  # &amp;lt;&amp;lt; 요건 성공!

sed -i 's/192.168.10.11/192.168.10.12/g' /root/.kube/config

while true; do kubectl get node ; echo ; curl -sk https://192.168.10.12:6443/version | grep gitVersion ; sleep 1; echo ; done
NAME        STATUS     ROLES           AGE     VERSION
k8s-node1   NotReady   control-plane   4h35m   v1.32.9
k8s-node2   Ready      control-plane   4h35m   v1.32.9
k8s-node3   Ready      control-plane   4h35m   v1.32.9
k8s-node4   Ready      &amp;lt;none&amp;gt;          4h34m   v1.32.9
  &quot;gitVersion&quot;: &quot;v1.32.9&quot;,&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #9a6bb4;&quot; data-token-index=&quot;0&quot;&gt;External LB &amp;rarr; HA 컨트플 플레인 노드(3대) : k8s apiserver 호출 설정&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770461081387&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
curl -sk https://192.168.10.10:6443/version | grep gitVersion
  &quot;gitVersion&quot;: &quot;v1.32.9&quot;,

#
sed -i 's/192.168.10.12/192.168.10.10/g' /root/.kube/config

# 인증서 SAN list 확인
kubectl get node
E0128 23:53:41.079370   70802 memcache.go:265] &quot;Unhandled Error&quot; err=&quot;couldn't get current server API group list: Get \&quot;https://192.168.10.10:6443/api?timeout=32s\&quot;: tls: failed to verify certificate: x509: certificate is valid for 10.233.0.1, 192.168.10.11, 127.0.0.1, ::1, 192.168.10.12, 192.168.10.13, 10.0.2.15, fd17:625c:f037:2:a00:27ff:fe90:eaeb, not 192.168.10.10&quot;

# 인증서에 SAN 정보 확인
ssh k8s-node1 cat /etc/kubernetes/ssl/apiserver.crt | openssl x509 -text -noout
...

ssh k8s-node1 kubectl get cm -n kube-system kubeadm-config -o yaml
    apiServer:
      certSANs:
      - kubernetes
      - kubernetes.default
      - kubernetes.default.svc
      - kubernetes.default.svc.cluster.local
      - 10.233.0.1
      - localhost
      - 127.0.0.1
      - ::1
      - k8s-node1
      - k8s-node2
      - k8s-node3
      - lb-apiserver.kubernetes.local
      - 192.168.10.11
      - 192.168.10.12
      - 192.168.10.13
      - 10.0.2.15
      - fd17:625c:f037:2:a00:27ff:fe90:eaeb

# 인증서 SAN 에 'IP, Domain' 추가
echo &quot;supplementary_addresses_in_ssl_keys: [192.168.10.10, k8s-api-srv.admin-lb.com]&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
  
# ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;kube-apiserver&quot; --list-tasks
# ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;kubeadm&quot; --list-tasks
# ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;facts&quot; --list-tasks
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;control-plane&quot; --list-tasks
...
 play #10 (kube_control_plane): Install the control plane	TAGS: []
    tasks:
      ...
      kubernetes/control-plane : Kubeadm | aggregate all SANs	TAGS: [control-plane, facts]
...


# (신규터미널) 모니터링
[k8s-node4]
while true; do curl -sk https://127.0.0.1:6443/version | grep gitVersion ; date ; sleep 1; echo ; done


# 1분 이내 완료
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;control-plane&quot; --limit kube_control_plane -e kube_version=&quot;1.32.9&quot;
Gather minimal facts ------------------------------------------------------------------------------------------------------- 2.00s
kubernetes/control-plane : Kubeadm | Check apiserver.crt SAN hosts --------------------------------------------------------- 1.57s
kubernetes/control-plane : Kubeadm | Check apiserver.crt SAN IPs ----------------------------------------------------------- 1.33s
kubernetes/control-plane : Backup old certs and keys ----------------------------------------------------------------------- 1.26s
Gather necessary facts (hardware) ------------------------------------------------------------------------------------------ 0.98s
kubernetes/control-plane : Install | Copy kubectl binary from download dir ------------------------------------------------- 0.95s
kubernetes/preinstall : Create other directories of root owner ------------------------------------------------------------- 0.92s
win_nodes/kubernetes_patch : debug ----------------------------------------------------------------------------------------- 0.84s
kubernetes/control-plane : Backup old confs -------------------------------------------------------------------------------- 0.83s
kubernetes/control-plane : Update server field in component kubeconfigs ---------------------------------------------------- 0.78s
kubernetes/control-plane : Kubeadm | Create kubeadm config ----------------------------------------------------------------- 0.76s
kubernetes/preinstall : Create kubernetes directories ---------------------------------------------------------------------- 0.67s
kubernetes/control-plane : Kubeadm | regenerate apiserver cert 2/2 --------------------------------------------------------- 0.50s
kubernetes/control-plane : Renew K8S control plane certificates monthly 2/2 ------------------------------------------------ 0.46s
kubernetes/control-plane : Create kube-scheduler config -------------------------------------------------------------------- 0.41s
Gather necessary facts (network) ------------------------------------------------------------------------------------------- 0.38s
kubernetes/control-plane : Install script to renew K8S control plane certificates ------------------------------------------ 0.37s
kubernetes/control-plane : Kubeadm | regenerate apiserver cert 1/2 --------------------------------------------------------- 0.34s
kubernetes/control-plane : Kubeadm | aggregate all SANs -------------------------------------------------------------------- 0.29s
kubernetes/control-plane : Check which kube-control nodes are already members of the cluster ------------------------------- 0.28s


# 192.168.10.10 엔드포인트 요청 성공!
kubectl get node -v=6
...
I0129 00:17:13.825729   81610 round_trippers.go:560] GET https://192.168.10.10:6443/api/v1/nodes?limit=500 200 OK in 8 milliseconds
NAME        STATUS   ROLES           AGE     VERSION
k8s-node1   Ready    control-plane   7h      v1.32.9
k8s-node2   Ready    control-plane   7h      v1.32.9
k8s-node3   Ready    control-plane   7h      v1.32.9
k8s-node4   Ready    &amp;lt;none&amp;gt;          6h59m   v1.32.9

# ip, domain 둘 다 확인
sed -i 's/192.168.10.10/k8s-api-srv.admin-lb.com/g' /root/.kube/config

# 추가 확인
ssh k8s-node1 cat /etc/kubernetes/ssl/apiserver.crt | openssl x509 -text -noout
X509v3 Subject Alternative Name:
  DNS:k8s-api-srv.admin-lb.com, DNS:k8s-node1, DNS:k8s-node2, DNS:k8s-node3, DNS:kubernetes, DNS:kubernetes.default,
  DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:lb-apiserver.kubernetes.local, 
  DNS:localhost, IP Address:10.233.0.1, IP Address:192.168.10.11, IP Address:127.0.0.1, 
  IP Address:0:0:0:0:0:0:0:1, IP Address:192.168.10.10, IP Address:192.168.10.12, IP Address:192.168.10.13, 
  IP Address:10.0.2.15, IP Address:FD17:625C:F037:2:A00:27FF:FE90:EAEB

# 해당 cm은 최초 설치 후 자동 업데이트 X, 업그레이드에 활용된다고 하니, 위 처럼 kubeadm config 변경 시 직접 cm도 같이 변경해두자.
kubectl get cm -n kube-system kubeadm-config -o yaml
...

kubectl edit cm -n kube-system kubeadm-config # or k9s -&amp;gt; cm kube-system
...&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; 노드 관리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;노드 추가 : playbook(&lt;span data-token-index=&quot;1&quot;&gt;scale.yml&lt;/span&gt;), role(&lt;span data-token-index=&quot;3&quot;&gt;playbooks/scale.yml&lt;/span&gt;) , 기존 클러스터는 건드리지 않고, 새로 추가된 노드만 단계적으로 합류&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;2131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dsy6i/dJMcachHVWi/gmej6WlqjnEZj1vSQXQfQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dsy6i/dJMcachHVWi/gmej6WlqjnEZj1vSQXQfQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dsy6i/dJMcachHVWi/gmej6WlqjnEZj1vSQXQfQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDsy6i%2FdJMcachHVWi%2Fgmej6WlqjnEZj1vSQXQfQ0%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;1240&quot; height=&quot;2131&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;2131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1770461163884&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
cat scale.yml
---
- name: Scale the cluster
  ansible.builtin.import_playbook: playbooks/scale.yml

cat playbooks/scale.yml
---
- name: Common tasks for every playbooks
  import_playbook: boilerplate.yml

- name: Gather facts
  import_playbook: internal_facts.yml

- name: Install etcd  # 기존 etcd 클러스터는 변경하지 않음, 새 노드가 etcd 멤버일 경우에만 join
  vars:
    etcd_cluster_setup: false
    etcd_events_cluster_setup: false
  import_playbook: install_etcd.yml

- name: Download images to ansible host cache via first kube_control_plane node # download_run_once 설정이 되어 있다면, 첫 번째 control-plane 노드에서만 실행 : 이미지/바이너리 캐시를 ansible host에 적재
  hosts: kube_control_plane[0]
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults, when: &quot;not skip_downloads and download_run_once and not download_localhost&quot; }
    - { role: kubernetes/preinstall, tags: preinstall, when: &quot;not skip_downloads and download_run_once and not download_localhost&quot; }
    - { role: download, tags: download, when: &quot;not skip_downloads and download_run_once and not download_localhost&quot; }

- name: Target only workers to get kubelet installed and checking in on any new nodes(engine) # 워커 노드 준비
  hosts: kube_node
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/preinstall, tags: preinstall }
    - { role: container-engine, tags: &quot;container-engine&quot;, when: deploy_container_engine }
    - { role: download, tags: download, when: &quot;not skip_downloads&quot; }
    - role: etcd  # (조건부): Calico 같은 네트워크 플러그인이 etcd를 직접 사용하는 경우, 워커 노드에서도 접속 가능하도록 설정합니다.
      tags: etcd
      vars:
        etcd_cluster_setup: false
      when:
        - etcd_deployment_type != &quot;kubeadm&quot;
        - kube_network_plugin in [&quot;calico&quot;, &quot;flannel&quot;, &quot;canal&quot;, &quot;cilium&quot;] or cilium_deploy_additionally | default(false) | bool
        - kube_network_plugin != &quot;calico&quot; or calico_datastore == &quot;etcd&quot;

- name: Target only workers to get kubelet installed and checking in on any new nodes(node) # kubelet 설치
  hosts: kube_node
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/node, tags: node } # kubelet 설치, systemd 등록, 아직 클러스터 join X

- name: Upload control plane certs and retrieve encryption key # kubeadm 인증서 공유
  ## 새 노드가 클러스터에 안전하게 조인할 수 있도록 kubeadm을 통해 인증서를 업로드하고, 조인에 필요한 certificate_key를 추출하여 변수로 저장합니다.
  hosts: kube_control_plane | first # 대상: 첫 번째 마스터 노드
  environment: &quot;{{ proxy_disable_env }}&quot;
  gather_facts: false
  tags: kubeadm
  roles:
    - { role: kubespray_defaults }
  tasks:
    - name: Upload control plane certificates
      command: &amp;gt;-
        {{ bin_dir }}/kubeadm init phase       # kubeadm init phase upload-certs --upload-certs
        --config {{ kube_config_dir }}/kubeadm-config.yaml
        upload-certs
        --upload-certs
      environment: &quot;{{ proxy_disable_env }}&quot;
      register: kubeadm_upload_cert
      changed_when: false
    - name: Set fact 'kubeadm_certificate_key' for later use
      set_fact:
        kubeadm_certificate_key: &quot;{{ kubeadm_upload_cert.stdout_lines[-1] | trim }}&quot;
      when: kubeadm_certificate_key is not defined

- name: Target only workers to get kubelet installed and checking in on any new nodes(network) # 클러스터 조인 및 네트워크 설정
  hosts: kube_node
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/kubeadm, tags: kubeadm }       # 새 워커 노드에서 kubeadm join 명령을 실행하여 클러스터에 공식적으로 등록합니다.
    - { role: kubernetes/node-label, tags: node-label } # 노드에 지정된 라벨(Label)과 테인트(Taint)를 적용합니다.
    - { role: kubernetes/node-taint, tags: node-taint } # 상동
    - { role: network_plugin, tags: network }           # CNI(Calico, Flannel 등) 설정을 적용하여 노드 간 통신이 가능하게 합니다.

- name: Apply resolv.conf changes now that cluster DNS is up # DNS 설정
  hosts: k8s_cluster
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/preinstall, when: &quot;dns_mode != 'none' and resolvconf_mode == 'host_resolvconf'&quot;, tags: resolvconf, dns_late: true }
  # resolvconf: 클러스터 내부 DNS(CoreDNS 등)가 활성화되었으므로, 각 노드의 /etc/resolv.conf를 업데이트하여 노드들이 내부 도메인을 해석할 수 있도록 수정합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;노드 추가&lt;/span&gt; k8s-node5 : 3분 소요&lt;/p&gt;
&lt;pre id=&quot;code_1770461191823&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inventory.ini 수정
cat &amp;lt;&amp;lt; EOF &amp;gt; /root/kubespray/inventory/mycluster/inventory.ini
[kube_control_plane]
k8s-node1 ansible_host=192.168.10.11 ip=192.168.10.11 etcd_member_name=etcd1
k8s-node2 ansible_host=192.168.10.12 ip=192.168.10.12 etcd_member_name=etcd2
k8s-node3 ansible_host=192.168.10.13 ip=192.168.10.13 etcd_member_name=etcd3

[etcd:children]
kube_control_plane

[kube_node]
k8s-node4 ansible_host=192.168.10.14 ip=192.168.10.14
k8s-node5 ansible_host=192.168.10.15 ip=192.168.10.15
EOF

ansible-inventory -i /root/kubespray/inventory/mycluster/inventory.ini --graph
@all:
  |--@ungrouped:
  |--@etcd:
  |  |--@kube_control_plane:
  |  |  |--k8s-node1
  |  |  |--k8s-node2
  |  |  |--k8s-node3
  |--@kube_node:
  |  |--k8s-node4
  |  |--k8s-node5

# ansible 연결 확인
ansible -i inventory/mycluster/inventory.ini k8s-node5 -m ping

# 모니터링
watch -d kubectl get node
kube-ops-view

# 워커 노드 추가 수행 : 3분 정도 소요
ansible-playbook -i inventory/mycluster/inventory.ini -v scale.yml --list-tasks
ANSIBLE_FORCE_COLOR=true ansible-playbook -i inventory/mycluster/inventory.ini -v scale.yml --limit=k8s-node5 -e kube_version=&quot;1.32.9&quot; | tee kubespray_add_worker_node.log


# 확인
kubectl get node -owide
NAME        STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION                  CONTAINER-RUNTIME
k8s-node1   Ready    control-plane   48m   v1.32.9   192.168.10.11   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5
k8s-node2   Ready    control-plane   48m   v1.32.9   192.168.10.12   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5
k8s-node3   Ready    control-plane   48m   v1.32.9   192.168.10.13   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5
k8s-node4   Ready    &amp;lt;none&amp;gt;          47m   v1.32.9   192.168.10.14   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5
k8s-node5   Ready    &amp;lt;none&amp;gt;          66s   v1.32.9   192.168.10.15   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5

kubectl get pod -n kube-system -owide |grep k8s-node5
kube-flannel-ds-arm64-2djxl         1/1     Running   1 (80s ago)   114s   192.168.10.15   k8s-node5   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-proxy-x6cmm                    1/1     Running   0             114s   192.168.10.15   k8s-node5   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
nginx-proxy-k8s-node5               1/1     Running   0             113s   192.168.10.15   k8s-node5   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;

# 변경 정보 확인
ssh k8s-node5 tree /etc/kubernetes
ssh k8s-node5 tree /var/lib/kubelet
ssh k8s-node5 pstree -a


# 샘플 파드 분배
kubectl get pod -owide
kubectl scale deployment webpod --replicas 1
kubectl get pod -owide
kubectl scale deployment webpod --replicas 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;노드 삭제 : playbook(&lt;span data-token-index=&quot;1&quot;&gt;remove-node.yml&lt;/span&gt;), role(&lt;span data-token-index=&quot;3&quot;&gt;playbooks/remove_node.yml&lt;/span&gt;) 특정 노드를 안전하게 제거(Remove/Graceful Termination)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;1890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJf0H/dJMcaajTtYO/PzDmK2MvDdjhWKpgU7AzYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJf0H/dJMcaajTtYO/PzDmK2MvDdjhWKpgU7AzYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJf0H/dJMcaajTtYO/PzDmK2MvDdjhWKpgU7AzYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJf0H%2FdJMcaajTtYO%2FPzDmK2MvDdjhWKpgU7AzYk%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;790&quot; height=&quot;1890&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;1890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1770461226482&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
cat remove-node.yml
---
- name: Remove node
  ansible.builtin.import_playbook: playbooks/remove_node.yml

#
cat playbooks/remove_node.yml
---
- name: Validate nodes for removal # &amp;ldquo;어떤 노드를 지울 건지 명확히 지정했는지&amp;rdquo; 강제 체크
  hosts: localhost
  gather_facts: false
  become: false
  tasks:
    - name: Assert that nodes are specified for removal
      assert:
        that:
          - node is defined
          - node | length &amp;gt; 0
        msg: &quot;No nodes specified for removal. The `node` variable must be set explicitly.&quot;

- name: Common tasks for every playbooks # 공통 설정 로드(모든 playbook에서 반복되는 준비 단계) : kubespray 공통 변수, handler, 기본 설정 로딩 등
  import_playbook: boilerplate.yml

- name: Confirm node removal # 실제로 노드가 삭제되기 전 사용자에게 최종 확인을 받습니다. 사용자가 yes라고 입력해야만 다음 단계로 넘어갑니다. 자동화 스크립트 등에서 이 단계를 건너뛰려면 -e skip_confirmation=true 옵션을 사용합니다.
  hosts: &quot;{{ node | default('this_is_unreachable') }}&quot;
  gather_facts: false
  tasks:
    - name: Confirm Execution
      pause:
        prompt: &quot;Are you sure you want to delete nodes state? Type 'yes' to delete nodes.&quot;
      register: pause_result
      run_once: true # 노드 여러 개여도 한 번만 묻기
      when:
        - not (skip_confirmation | default(false) | bool)

    - name: Fail if user does not confirm deletion
      fail:
        msg: &quot;Delete nodes confirmation failed&quot;
      when: pause_result.user_input | default('yes') != 'yes'

- name: Gather facts
  import_playbook: internal_facts.yml
  when: reset_nodes | default(True) | bool

- name: Reset node # 실제 노드 제거*
  hosts: &quot;{{ node | default('this_is_unreachable') }}&quot;
  gather_facts: false
  environment: &quot;{{ proxy_disable_env }}&quot;
  pre_tasks:
    - name: Gather information about installed services
      service_facts:
      when: reset_nodes | default(True) | bool
  roles:
    - { role: kubespray_defaults, when: reset_nodes | default(True) | bool } # 기본 변수 로딩
    - { role: remove_node/pre_remove, tags: pre-remove }  # 노드에서 실행 중인 파드들을 다른 노드로 옮기고(Drain), 더 이상 스케줄링되지 않게 만듭니다(Cordon). kubelet 중지.
    - role: remove-node/remove-etcd-node                  # 해당 노드가 etcd 멤버인 경우, etcd 클러스터 정족수에서 해당 노드를 안전하게 제거합니다. (데이터 무결성 유지)
      when: &quot;'etcd' in group_names&quot;
    - { role: reset, tags: reset, when: reset_nodes | default(True) | bool } # 노드에 설치된 쿠버네티스 구성 요소(kubeadm reset -f, binaries, configs, network interfaces)를 삭제하여 클린 상태로 만듭니다.

# Currently cannot remove first control plane node or first etcd node # 첫 번째 마스터 노드나 첫 번째 etcd 노드는 이 플레이북으로 제거할 수 없습니다(클러스터 파괴 위험)
- name: Post node removal          # 클러스터의 마스터 노드(Control Plane) 설정에서 제거된 노드에 대한 잔재 정보를 완전히 삭제합니다.
  hosts: &quot;{{ node | default('this_is_unreachable') }}&quot;
  gather_facts: false
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults, when: reset_nodes | default(True) | bool }
    - { role: remove-node/post-remove, tags: post-remove } # kubectl delete node &amp;lt;node명&amp;gt; 로 노드 메타데이터 삭제&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;노드 삭제&lt;/span&gt; (2분 소요) 후 다시 &lt;span data-token-index=&quot;2&quot;&gt;노드 추가&lt;/span&gt; (3분 소요) k8s-node5&lt;/p&gt;
&lt;pre id=&quot;code_1770461245389&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# webpod deployment 에 pdb 설정 : 해당 정책은 항상 최소 2개의 Pod가 Ready 상태여야 함 , drain / eviction 시 단 하나의 Pod도 축출 불가
kubectl scale deployment webpod --replicas 1
kubectl scale deployment webpod --replicas 2

cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: webpod
  namespace: default
spec:
  maxUnavailable: 0
  selector:
    matchLabels:
      app: webpod
EOF

# 확인
kubectl get pdb
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
webpod   N/A             0                 0                     6s


# 삭제 실패
ansible-playbook -i inventory/mycluster/inventory.ini -v remove-node.yml --list-tags
ansible-playbook -i inventory/mycluster/inventory.ini -v remove-node.yml -e node=k8s-node5
...
PLAY [Confirm node removal] *******************************************************************************************************
Thursday 29 January 2026  14:10:10 +0900 (0:00:00.106)       0:00:01.562 ******
[Confirm Execution]
Are you sure you want to delete nodes state? Type 'yes' to delete nodes.: yes
...
TASK [remove_node/pre_remove : Remove-node | List nodes] **************************************************************************
ok: [k8s-node5 -&amp;gt; k8s-node1(192.168.10.11)] =&amp;gt; {&quot;changed&quot;: false, &quot;cmd&quot;: [&quot;/usr/local/bin/kubectl&quot;, &quot;--kubeconfig&quot;, &quot;/etc/kubernetes/admin.conf&quot;, &quot;get&quot;, &quot;nodes&quot;, &quot;-o&quot;, &quot;go-template={{ range .items }}{{ .metadata.name }}{{ \&quot;\\n\&quot; }}{{ end }}&quot;], &quot;delta&quot;: &quot;0:00:00.159970&quot;, &quot;end&quot;: &quot;2026-01-31 15:02:13.863633&quot;, &quot;msg&quot;: &quot;&quot;, &quot;rc&quot;: 0, &quot;start&quot;: &quot;2026-01-31 15:02:13.703663&quot;, &quot;stderr&quot;: &quot;&quot;, &quot;stderr_lines&quot;: [], &quot;stdout&quot;: &quot;k8s-node1\nk8s-node2\nk8s-node3\nk8s-node4\nk8s-node5&quot;, &quot;stdout_lines&quot;: [&quot;k8s-node1&quot;, &quot;k8s-node2&quot;, &quot;k8s-node3&quot;, &quot;k8s-node4&quot;, &quot;k8s-node5&quot;]}
Saturday 31 January 2026  15:02:13 +0900 (0:00:00.552)       0:00:22.561 ******
FAILED - RETRYING: [k8s-node5 -&amp;gt; k8s-node1]: Remove-node | Drain node except daemonsets resource (3 retries left).
CTRL+C 취소

# pdb 삭제
kubectl delete pdb webpod

# 다시 삭제 시도 : 2분 20초 소요
ansible-playbook -i inventory/mycluster/inventory.ini -v remove-node.yml -e node=k8s-node5
...
PLAY [Confirm node removal] *******************************************************************************************************
Thursday 29 January 2026  14:10:10 +0900 (0:00:00.106)       0:00:01.562 ******
[Confirm Execution]
Are you sure you want to delete nodes state? Type 'yes' to delete nodes.: yes
...

# 확인
kubectl get node -owide

# 삭제 확인
ssh k8s-node5 tree /etc/kubernetes
ssh k8s-node5 tree /var/lib/kubelet
ssh k8s-node5 pstree -a


# inventory.ini 수정
cat &amp;lt;&amp;lt; EOF &amp;gt; /root/kubespray/inventory/mycluster/inventory.ini
[kube_control_plane]
k8s-node1 ansible_host=192.168.10.11 ip=192.168.10.11 etcd_member_name=etcd1
k8s-node2 ansible_host=192.168.10.12 ip=192.168.10.12 etcd_member_name=etcd2
k8s-node3 ansible_host=192.168.10.13 ip=192.168.10.13 etcd_member_name=etcd3

[etcd:children]
kube_control_plane

[kube_node]
k8s-node4 ansible_host=192.168.10.14 ip=192.168.10.14
EOF&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;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;span style=&quot;background-color: #9feec3;&quot;&gt; 모니터링 설정 &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;NFS subdir external provisioner 설치&lt;/h4&gt;
&lt;pre id=&quot;code_1770461294426&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# NFS subdir external provisioner 설치 : admin-lb 에 NFS Server(/srv/nfs/share) 설정 되어 있음
kubectl create ns nfs-provisioner
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -n nfs-provisioner \
    --set nfs.server=192.168.10.10 \
    --set nfs.path=/srv/nfs/share \
    --set storageClass.defaultClass=true

# 스토리지 클래스 확인
kubectl get sc
NAME                   PROVISIONER                                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-client (default)   cluster.local/nfs-provisioner-nfs-subdir-external-provisioner   Delete          Immediate           true                   30s    

# 파드 확인
kubectl get pod -n nfs-provisioner -owide
NAME                                                              READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
nfs-provisioner-nfs-subdir-external-provisioner-b549b9dff-b2bsn   1/1     Running   0          57s   10.244.1.4   k8s-w1   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;&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;kube-prometheus-stack 설치, 대시보드 추가&lt;/h4&gt;
&lt;pre id=&quot;code_1770461313252&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; monitor-values.yaml
prometheus:
  prometheusSpec:
    scrapeInterval: &quot;20s&quot;
    evaluationInterval: &quot;20s&quot;
    storageSpec:
      volumeClaimTemplate:
        spec:
          accessModes: [&quot;ReadWriteOnce&quot;]
          resources:
            requests:
              storage: 10Gi
    additionalScrapeConfigs:
      - job_name: 'haproxy-metrics'
        static_configs:
          - targets:
              - '192.168.10.10:8405'
    externalLabels:
      cluster: &quot;myk8s-cluster&quot;
  service:
    type: NodePort
    nodePort: 30001

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator
  service:
    type: NodePort
    nodePort: 30002

alertmanager:
  enabled: false
defaultRules:
  create: false
kubeProxy:
  enabled: false
prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false
EOT
cat monitor-values.yaml

# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 80.13.3 \
-f monitor-values.yaml --create-namespace --namespace monitoring

# 확인
helm list -n monitoring
kubectl get pod,svc,ingress,pvc -n monitoring
kubectl get prometheus,servicemonitors,alertmanagers -n monitoring
kubectl get crd | grep monitoring

# 각각 웹 접속 실행 : NodePort 접속
open http://192.168.10.14:30001 # prometheus
open http://192.168.10.14:30002 # grafana : 접속 계정 admin / prom-operator

# 프로메테우스 버전 확인
kubectl exec -it sts/prometheus-kube-prometheus-stack-prometheus -n monitoring -c prometheus -- prometheus --version
prometheus, version 3.9.1

# 그라파나 버전 확인
kubectl exec -it -n monitoring deploy/kube-prometheus-stack-grafana -- grafana --version
grafana version 12.3.1&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;그라파나 Dashboard :&lt;/span&gt; &lt;span style=&quot;color: #d27b2d;&quot; data-token-index=&quot;2&quot;&gt;15661, 12693 &amp;amp; &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/dotdc/grafana-dashboards-kubernetes&quot;&gt;https://github.com/dotdc/grafana-dashboards-kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770461342527&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 대시보드 다운로드
curl -o 12693_rev12.json https://grafana.com/api/dashboards/12693/revisions/12/download
curl -o 15661_rev2.json https://grafana.com/api/dashboards/15661/revisions/2/download
curl -o k8s-system-api-server.json https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/refs/heads/master/dashboards/k8s-system-api-server.json

# sed 명령어로 uid 일괄 변경 : 기본 데이터소스의 uid 'prometheus' 사용
sed -i -e 's/${DS_PROMETHEUS}/prometheus/g' 12693_rev12.json
sed -i -e 's/${DS__VICTORIAMETRICS-PROD-ALL}/prometheus/g' 15661_rev2.json
sed -i -e 's/${DS_PROMETHEUS}/prometheus/g' k8s-system-api-server.json

# my-dashboard 컨피그맵 생성 : Grafana 포드 내의 사이드카 컨테이너가 grafana_dashboard=&quot;1&quot; 라벨 탐지!
kubectl create configmap my-dashboard --from-file=12693_rev12.json --from-file=15661_rev2.json --from-file=k8s-system-api-server.json -n monitoring
kubectl label configmap my-dashboard grafana_dashboard=&quot;1&quot; -n monitoring

# 대시보드 경로에 추가 확인
kubectl exec -it -n monitoring deploy/kube-prometheus-stack-grafana -- ls -l /tmp/dashboards
-rw-r--r--    1 grafana  472         333790 Jan 22 06:27 12693_rev12.json
-rw-r--r--    1 grafana  472         198839 Jan 22 06:27 15661_rev2.json
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpcysT/dJMcai90BjY/kZkCKeu4IGBTsmhT2FClK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpcysT/dJMcai90BjY/kZkCKeu4IGBTsmhT2FClK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpcysT/dJMcai90BjY/kZkCKeu4IGBTsmhT2FClK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpcysT%2FdJMcai90BjY%2FkZkCKeu4IGBTsmhT2FClK1%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;309&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;309&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;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;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;span style=&quot;background-color: #9feec3;&quot;&gt; 업그레이드 &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(사전 작업) flannel cni plugin upgrade&lt;/h4&gt;
&lt;pre id=&quot;code_1770461383043&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 관련 변수 검색
grep -Rni &quot;flannel&quot; inventory/mycluster/ playbooks/ roles/ --include=&quot;*.yml&quot; -A2 -B1
...
roles/kubespray_defaults/defaults/main/download.yml:115:flannel_version: 0.27.3
roles/kubespray_defaults/defaults/main/download.yml:116:flannel_cni_version: 1.7.1-flannel1
roles/kubespray_defaults/defaults/main/download.yml:219:flannel_image_repo: &quot;{{ docker_image_repo }}/flannel/flannel&quot;
roles/kubespray_defaults/defaults/main/download.yml:220:flannel_image_tag: &quot;v{{ flannel_version }}&quot;
roles/kubespray_defaults/defaults/main/download.yml:221:flannel_init_image_repo: &quot;{{ docker_image_repo }}/flannel/flannel-cni-plugin&quot;
roles/kubespray_defaults/defaults/main/download.yml:222:flannel_init_image_tag: &quot;v{{ flannel_cni_version }}&quot;

# 현재 정보 확인
kubectl get ds -n kube-system -owide
NAME                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE    CONTAINERS     IMAGES                               SELECTOR
kube-flannel              0         0         0       0            0           &amp;lt;none&amp;gt;                   167m   kube-flannel   docker.io/flannel/flannel:v0.27.3    app=flannel

ssh k8s-node1 crictl images
IMAGE                                                 TAG                 IMAGE ID            SIZE
docker.io/flannel/flannel-cni-plugin                  v1.7.1-flannel1     e5bf9679ea8c3       5.14MB
docker.io/flannel/flannel                             v0.27.3             cadcae92e6360       33.1MB

# 노드에 미리 이미지 다운로드 해두기 : play 로 미리 다운로드 후 적용이니 굳이 아래 과정 할 필요 없음
ssh k8s-node3 crictl pull ghcr.io/flannel-io/flannel:v0.27.4
ssh k8s-node3 crictl pull ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1

# flannel 설정 수정
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
flannel_version: 0.27.4
EOF
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml


# 모니터링
watch -d &quot;ssh k8s-node3 crictl ps&quot;

# flannel tag : Network plugin flannel =&amp;gt; 아래 전부 실패
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --tags &quot;flannel&quot; --list-tasks
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --tags &quot;flannel&quot; --limit k8s-node3 -e kube_version=&quot;1.32.9&quot;

ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;network,flannel&quot; --list-tasks
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --tags &quot;network,flannel&quot; --limit k8s-node3 -e kube_version=&quot;1.32.9&quot;

ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml --tags &quot;cni,network,flannel&quot; --list-tasks
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --tags &quot;cni,network,flannel&quot; --limit k8s-node3 -e kube_version=&quot;1.32.9&quot;

## cordon -&amp;gt; apiserver 파드 재생성 -&amp;gt; uncordon 
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --list-tasks
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --limit k8s-node3 -e kube_version=&quot;1.32.9&quot;

# flannel 은 ds 이므로 특정 대상 노드로 수행 불가 -&amp;gt; 민감한 클러스터 환경이라면 cni plugin 은 kubespary 와 별로 배포 관리 후 특정 노드별 순차 적용 해야 될듯.
ansible-playbook -i inventory/mycluster/inventory.ini -v upgrade-cluster.yml --tags &quot;flannel&quot; -e kube_version=&quot;1.32.9&quot;

# 확인
kubectl get ds -n kube-system -owide
NAME                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE     CONTAINERS     IMAGES                               SELECTOR
kube-flannel              0         0         0       0            0           &amp;lt;none&amp;gt;                   3h27m   kube-flannel   docker.io/flannel/flannel:v0.27.4    app=flannel
...

ssh k8s-node1 crictl images
IMAGE                                                 TAG                 IMAGE ID            SIZE
docker.io/flannel/flannel-cni-plugin                  v1.7.1-flannel1     e5bf9679ea8c3       5.14MB
docker.io/flannel/flannel                             v0.27.3             cadcae92e6360       33.1MB
docker.io/flannel/flannel                             v0.27.4             7a52f3ae4ee60       33.2MB

kubectl get pod -n kube-system -l app=flannel -owide
NAME                          READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
kube-flannel-ds-arm64-48r2f   1/1     Running   0          98s     192.168.10.11   k8s-node1   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-flannel-ds-arm64-hchn8   1/1     Running   0          108s    192.168.10.15   k8s-node5   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-flannel-ds-arm64-jbjw9   1/1     Running   0          2m13s   192.168.10.12   k8s-node2   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-flannel-ds-arm64-qf6q9   1/1     Running   0          112s    192.168.10.13   k8s-node3   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-flannel-ds-arm64-qtv2m   1/1     Running   0          2m2s    192.168.10.14   k8s-node4   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;&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; kubespary 업그레이드 공식 문서 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/kubernetes-sigs/kubespray/blob/master/docs/operations/upgrades.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt; &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;Unsafe upgrade&lt;/b&gt; 안전하지 않은 (즉시) 업그레이드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cluster.yml 사용&lt;/li&gt;
&lt;li&gt;-e upgrade_cluster_setup=true 정상적인 업그레이드 과정에서만 수행되는 kube-apiserver와 같은 배포를 즉시 마이그레이션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Graceful upgrade&lt;/b&gt; 우아한 업그레이드 : &lt;b&gt;노드의 cordon 설정, drain 및 uncordon 를 지원&lt;/b&gt; &amp;larr; 최소 1개의 kube_control_plane이 이미 배포되어 있는 환경
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;upgrade-cluster.yml 사용&lt;/li&gt;
&lt;li&gt;serial: 20%(기본값) 기본값 사용 시 20% 비중 먼지 실행, 만약 1로 설정하면 worker Node를 1개씩 업그레이드 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ansible serial : 지정된 수 혹은 백분율의 호스트에 play 실행 후 다음 호스트 실행 - &lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_strategies.html#setting-the-batch-size-with-serial&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pausing the upgrade&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;upgrade_node_confirm: true
&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;터미널에서 &quot;yes&quot;를 입력하여 수동으로 승인하면 플레이북 실행이 다시 시작됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;upgrade_node_pause_seconds: 60
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 기능을 사용하면 각 노드를 업그레이드하기 전에 플레이북 실행이 60초 동안 일시 중지됩니다. 60초 후 플레이북 실행이 자동으로 재개됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
각 노드 &lt;b&gt;업그레이드 후&lt;/b&gt;&amp;nbsp;일시 중지하는 것은&amp;nbsp;커널 업데이트를 적용하기 위해 노드를 재부팅하거나, 아직 격리된 노드를 테스트하는 데 유용 할 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;upgrade_node_post_upgrade_confirm: true
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 옵션은 각 노드 업그레이드 후, 노드의 차단이 해제되기 전에 플레이북 실행을 일시 중지합니다. 터미널에서 &quot;yes&quot;를 입력하여 수동으로 승인하면 플레이북 실행이 다시 시작됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;upgrade_node_post_upgrade_pause_seconds: 60
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 옵션은 각 노드 업그레이드 후, 노드의 차단이 해제되기 전에 플레이북 실행을 60초 동안 일시 중지합니다. 60초 후 플레이북 실행이 자동으로 재개됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 &lt;b&gt;업그레이드&amp;nbsp;*전&lt;/b&gt;에*&amp;nbsp;일시 중지하면 해당 노드에서 실행 중인 Pod를 검사하거나 노드에서 수동 작업을 수행하는 데 유용 할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770461432473&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 수행
# limit 사용 전 모든 노드의 facts 캐시 최신화
ansible-playbook playbooks/facts.yml -b -i inventory/sample/hosts.ini

# 컨트롤 플레인
ansible-playbook upgrade-cluster.yml -b -i inventory/sample/hosts.ini -e kube_version=1.20.7 --limit &quot;kube_control_plane:etcd&quot;

# 워커 노드
ansible-playbook upgrade-cluster.yml -b -i inventory/sample/hosts.ini -e kube_version=1.20.7 --limit &quot;node4:node6:node7:node12&quot;
ansible-playbook upgrade-cluster.yml -b -i inventory/sample/hosts.ini -e kube_version=1.20.7 --limit &quot;node5*&quot;

# 워컨 노드 1번에 1대의 노드만 업그레이드
ansible-playbook upgrade-cluster.yml -b -i inventory/sample/hosts.ini -e kube_version=1.20.7 -e &quot;serial=1&quot;&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;설치 순서&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;/li&gt;
&lt;li&gt;컨테이너&lt;/li&gt;
&lt;li&gt;기타&lt;/li&gt;
&lt;li&gt;kubelet과 kube-proxy&lt;/li&gt;
&lt;li&gt;네트워크 플러그인(예: Calico)&lt;/li&gt;
&lt;li&gt;kube-apiserver, kube-scheduler 및 kube-controller-manager&lt;/li&gt;
&lt;li&gt;추가 기능(예: KubeDNS)&lt;/li&gt;
&lt;/ul&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;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;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;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/52</guid>
      <comments>https://netoppa.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 6 Feb 2026 16:34:25 +0900</pubDate>
    </item>
    <item>
      <title>[4주차] Kubespary 배포 분석</title>
      <link>https://netoppa.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 포스팅은 Cloud@net의 기시다님의 온라인 강좌 내용을 정리한 스터디 요약 자료입니다.&lt;/blockquote&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; kubespary 소개&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;kubespray는 Ansible 기반으로 쿠버네티스(Kubernetes) 클러스터를 자동으로 설치&amp;middot;업그레이드&amp;middot;관리하기 위한&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;오픈소스 배포 도구이다.&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로덕션 레벨의 클러스터를 손쉽게 구축할 수 있으며, 다양한 CNI 플러그인과 애드온을 지원한다.&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주요 기능&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;퍼블릭/폐쇄망 서버 환경에서도 쿠버네티스 배포 가능&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;951&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oSSO1/dJMcacBYDJ6/p0JO9vasxFKBumzrLS3vZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oSSO1/dJMcacBYDJ6/p0JO9vasxFKBumzrLS3vZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oSSO1/dJMcacBYDJ6/p0JO9vasxFKBumzrLS3vZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoSSO1%2FdJMcacBYDJ6%2Fp0JO9vasxFKBumzrLS3vZ1%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;951&quot; height=&quot;478&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블릭 서버 환경은 IaC(Terraform 등)으로 구성 &amp;rarr; kubespary 로 K8S 배포&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z0Y3J/dJMcajnvnob/0u6AbLEYcwQDadcK59jEq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z0Y3J/dJMcajnvnob/0u6AbLEYcwQDadcK59jEq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z0Y3J/dJMcajnvnob/0u6AbLEYcwQDadcK59jEq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz0Y3J%2FdJMcajnvnob%2F0u6AbLEYcwQDadcK59jEq1%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;452&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;452&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1925&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5sHJL/dJMb99L0nkM/acSYzQafkcaibg8X8IWC31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5sHJL/dJMb99L0nkM/acSYzQafkcaibg8X8IWC31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5sHJL/dJMb99L0nkM/acSYzQafkcaibg8X8IWC31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5sHJL%2FdJMb99L0nkM%2FacSYzQafkcaibg8X8IWC31%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;1925&quot; height=&quot;676&quot; data-origin-width=&quot;1925&quot; data-origin-height=&quot;676&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; 실습 배포 환경 분석&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;1065&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beIUjK/dJMcaaEa6aB/PuvvYr3AAWivkx20SMESsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beIUjK/dJMcaaEa6aB/PuvvYr3AAWivkx20SMESsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beIUjK/dJMcaaEa6aB/PuvvYr3AAWivkx20SMESsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeIUjK%2FdJMcaaEa6aB%2FPuvvYr3AAWivkx20SMESsK%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;1065&quot; height=&quot;504&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HCbb3/dJMcadU9l33/75lGHjolTHfYANpdljaNy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HCbb3/dJMcadU9l33/75lGHjolTHfYANpdljaNy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HCbb3/dJMcadU9l33/75lGHjolTHfYANpdljaNy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHCbb3%2FdJMcadU9l33%2F75lGHjolTHfYANpdljaNy1%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;786&quot; height=&quot;530&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;530&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;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; &lt;span style=&quot;background-color: #ffffff; color: #34343c; text-align: start;&quot;&gt;실습 환경 배포를 위한 디렉터리 생성 및 스크립트 다운로드&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1770254636328&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir k8s-ha-upgrade
cd k8s-ha-upgrade

curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-ha-upgrade/Vagrantfile
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-ha-upgrade/admin-lb.sh
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-ha-upgrade/init_cfg.sh
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/k8s-ha-upgrade/k8s-ctr.sh&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; &lt;span style=&quot;background-color: #ffffff; color: #34343c; text-align: start;&quot;&gt;배포&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1770254664214&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vagrant up
vagrant status&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 id=&quot;kubeam에-의한-k8s-배포&quot; style=&quot;background-color: #ffffff; color: #2a2a2a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;kubeam에 의한 k8s 배포&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;1-haproxy-동작-확인&quot; style=&quot;background-color: #ffffff; color: #2a2a2a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;HAProxy 동작 확인&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770254704503&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로드밸런서 진입
vagrant ssh admin-lb

# haproxy config파일 확인
cat /etc/haproxy/haproxy.cfg

# 확인
systemctl status haproxy.service --no-pager
journalctl -u haproxy.service --no-pager
ss -tnlp | grep haproxy

open http://192.168.10.10:9000/haproxy_stats&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;p id=&quot;2-3개의-컨트롤-플레인-설정&quot; style=&quot;background-color: #ffffff; color: #2a2a2a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Vagrantfile&lt;/p&gt;
&lt;pre id=&quot;code_1770255034076&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Base Image  https://portal.cloud.hashicorp.com/vagrant/discover/bento/rockylinux-10.0
BOX_IMAGE = &quot;bento/rockylinux-10.0&quot;
BOX_VERSION = &quot;202510.26.0&quot;

Vagrant.configure(&quot;2&quot;) do |config|

# ControlPlane Nodes 
    config.vm.define &quot;k8s-ctr&quot; do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider &quot;virtualbox&quot; do |vb|
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--groups&quot;, &quot;/Kubespray-Lab&quot;]
        vb.customize [&quot;modifyvm&quot;, :id, &quot;--nicpromisc2&quot;, &quot;allow-all&quot;]
        vb.name = &quot;k8s-ctr&quot;
        vb.cpus = 4
        vb.memory = 4096
        vb.linked_clone = true
      end
      subconfig.vm.host_name = &quot;k8s-ctr&quot;
      subconfig.vm.network &quot;private_network&quot;, ip: &quot;192.168.10.10&quot;
      subconfig.vm.network &quot;forwarded_port&quot;, guest: 22, host: &quot;60100&quot;, auto_correct: true, id: &quot;ssh&quot;
      subconfig.vm.synced_folder &quot;./&quot;, &quot;/vagrant&quot;, disabled: true
      subconfig.vm.provision &quot;shell&quot;, path: &quot;init_cfg.sh&quot;
    end

end&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;p data-ke-size=&quot;size18&quot;&gt;init_cfg.sh&lt;/p&gt;
&lt;pre id=&quot;code_1770255098453&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;


echo &quot;[TASK 1] Change Timezone and Enable NTP&quot;
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo &quot;[TASK 2] Disable firewalld and selinux&quot;
systemctl disable --now firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo &quot;[TASK 3] Disable and turn off SWAP &amp;amp; Delete swap partitions&quot;
swapoff -a
sed -i '/swap/d' /etc/fstab
sfdisk --delete /dev/sda 2 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
partprobe /dev/sda &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 4] Config kernel &amp;amp; module&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
modprobe br_netfilter &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

cat &amp;lt;&amp;lt; EOF &amp;gt;/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;[TASK 5] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
192.168.10.10 k8s-ctr
EOF


echo &quot;[TASK 6] Delete default routing - enp0s9 NIC&quot; # setenforce 0 설정 필요
nmcli connection modify enp0s9 ipv4.never-default yes
nmcli connection up enp0s9 &amp;gt;/dev/null 2&amp;gt;&amp;amp;1


echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&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;사전 설정 수행 &amp;amp; git clone&lt;/p&gt;
&lt;pre id=&quot;code_1770255131196&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# user 확인
whoami
pwd

# Linux Kernel Requirements : 5.8+ 이상 권장
uname -a
Linux k8s-ctr 6.12.0-55.39.1.el10_0.aarch64 #1 SMP PREEMPT_DYNAMIC Wed Oct 15 11:18:23 EDT 2025 aarch64 GNU/Linux

# Python : 3.10 ~ 3.12 : (참고) bento/rockylinux-9 경우 3.9
which python  &amp;amp;&amp;amp; python -V
which python3 &amp;amp;&amp;amp; python3 -V
3.12.9

# pip , git 설치
dnf install -y python3-pip git
which pip  &amp;amp;&amp;amp; pip -V
which pip3 &amp;amp;&amp;amp; pip3 -V
pip 23.3.2 from /usr/lib/python3.12/site-packages/pip (python 3.12)


# /etc/hosts 확인
ip -br -c -4 addr
cat /etc/hosts
ping -c 1 k8s-ctr

# SSH 접속을 위한 설정
# -----------------
echo &quot;root:qwe123&quot; | chpasswd

cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF
systemctl restart sshd

# Setting SSH Key
ssh-keygen -t rsa -N &quot;&quot; -f /root/.ssh/id_rsa
ls -l ~/.ssh

# ssh-copy-id
ssh-copy-id -o StrictHostKeyChecking=no root@192.168.10.10
root@192.168.10.10's password: qwe123

# ssh 접속 확인 : IP, hostname
cat /root/.ssh/authorized_keys
ssh root@192.168.10.10 hostname
ssh -o StrictHostKeyChecking=no root@k8s-ctr hostname
ssh root@k8s-ctr hostname
# -----------------

# Clone Kubespray Repository
git clone -b v2.29.1 https://github.com/kubernetes-sigs/kubespray.git /root/kubespray
cd /root/kubespray

# (옵션) IDE에서 VM SSH 접속(root/qwe123)해서 편집 창 열기

# 최상단 plybook 확인 -&amp;gt; 각각 import_playbook 확인
ls -l *.yml
-rw-r--r--. 1 root root  88 Jan 24 20:55 cluster.yml    # ansible.builtin.import_playbook: playbooks/cluster.yml
-rw-r--r--. 1 root root  30 Jan 24 20:55 _config.yml
-rw-r--r--. 1 root root 747 Jan 24 20:55 galaxy.yml
-rw-r--r--. 1 root root 105 Jan 24 20:55 recover-control-plane.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 remove-node.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 remove_node.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 reset.yml
-rw-r--r--. 1 root root  85 Jan 24 20:55 scale.yml      # ansible.builtin.import_playbook: playbooks/scale.yml
-rw-r--r--. 1 root root  93 Jan 24 20:55 upgrade-cluster.yml
-rw-r--r--. 1 root root  93 Jan 24 20:55 upgrade_cluster.yml

# 
tree -L 2
...
├── inventory
│   ├── local
│   └── sample
...
├── playbooks
│   ├── ansible_version.yml
│   ├── boilerplate.yml
│   ├── cluster.yml*
│   ├── facts.yml
│   ├── install_etcd.yml
│   ├── internal_facts.yml
│   ├── recover_control_plane.yml
│   ├── remove_node.yml
│   ├── reset.yml
│   ├── scale.yml
│   └── upgrade_cluster.yml
...
├── roles
│   ├── adduser
│   ├── bastion-ssh-config
│   ├── bootstrap-os
│   ├── bootstrap_os
│   ├── container-engine
│   ├── download
│   ├── dynamic_groups
│   ├── etcd
│   ├── etcdctl_etcdutl
│   ├── etcd_defaults
│   ├── helm-apps
│   ├── kubernetes
│   ├── kubernetes-apps
│   ├── kubespray-defaults
│   ├── kubespray_defaults
│   ├── network_facts
│   ├── network_plugin
│   ├── recover_control_plane
│   ├── remove-node
│   ├── remove_node
│   ├── reset
│   ├── system_packages
│   ├── upgrade
│   ├── validate_inventory
│   └── win_nodes
...

# Install Python Dependencies
cat requirements.txt
ansible==10.7.0
# Needed for community.crypto module
cryptography==46.0.3
# Needed for jinja2 json_query templating
jmespath==1.0.1
# Needed for ansible.utils.ipaddr
netaddr==1.3.0

pip3 install -r /root/kubespray/requirements.txt
Successfully installed MarkupSafe-3.0.3 ansible-10.7.0 ansible-core-2.17.14 cffi-2.0.0 cryptography-46.0.2 jinja2-3.1.6 jmespath-1.0.1 netaddr-1.3.0 pycparser-3.0 resolvelib-1.0.1

# ansible 버전 확인 : Ansible 2.17.3 이상
which ansible
ansible --version
ansible [core 2.17.14]
  config file = /root/kubespray/ansible.cfg
  ...
  python version = 3.12.9 (main, Aug 14 2025, 00:00:00) [GCC 14.2.1 20250110 (Red Hat 14.2.1-7)] (/usr/bin/python3)
  jinja version = 3.1.6
  libyaml = True

# pip list 확인
pip list
Package                   Version
------------------------- -----------
ansible                   10.7.0
ansible-core              2.17.14
...
Jinja2                    3.1.6
jmespath                  1.0.1
...
netaddr                   1.3.0
...

# 해당 폴더에서 ansible-playbook 실행 시 적용되는 ansible.cfg
cat ansible.cfg
[ssh_connection] # 통신 속도 및 안정성 최적화
pipelining=True  # SSH 세션을 여러 번 열지 않고 하나의 세션에서 여러 명령을 한꺼번에 실행
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null
## ControlMaster=auto -o ControlPersist=30m: 한 번 연결된 SSH 커넥션을 30분 동안 유지합니다. 매번 로그인할 필요가 없어 성능이 향상됩니다.
## ConnectionAttempts=100: 네트워크 불안정으로 연결 실패 시 100번까지 재시도합니다.
## UserKnownHostsFile=/dev/null: 접속 대상의 지문(fingerprint)을 저장하지 않아 관리가 편해집니다.
#control_path = ~/.ssh/ansible-%%r@%%h:%%p

[defaults]
# https://github.com/ansible/ansible/issues/56930 (to ignore group names with - and .)
force_valid_group_names = ignore  # Ansible은 원래 그룹 이름에 -나 . 사용을 제한하지만, 쿠버네티스 리소스 명칭 규칙상 이를 허용하도록 설정
host_key_checking=False # 새 서버 접속 시 &quot;Are you sure you want to continue connecting?&quot;이라는 확인 창이 뜨지 않게 합니다.
gathering = smart         # 대상 서버의 정보(Fact)를 한 번만 수집하고 /tmp에 JSON 파일로 저장합니다. (아래 설명 이어서)
fact_caching = jsonfile   # 재실행 시 서버 정보를 다시 수집하지 않아 시간이 단축됩니다. 86400(24시간) 동안 캐시를 유지합니다.
fact_caching_connection = /tmp
fact_caching_timeout = 86400
timeout = 300
stdout_callback = default
display_skipped_hosts = no
library = ./library
callbacks_enabled = profile_tasks # 각 Task가 실행되는 데 걸리는 시간을 표시해 줍니다. 어떤 단계에서 병목이 생기는지 확인할 때 매우 유용합니다.
roles_path = roles:$VIRTUAL_ENV/usr/local/share/kubespray/roles:$VIRTUAL_ENV/usr/local/share/ansible/roles:/usr/share/kubespray/roles
deprecation_warnings=False
inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo, .creds, .gpg # 백업용이나 임시 파일을 인벤토리로 인식하여 에러가 발생하는 것을 방지합니다.

[inventory]
ignore_patterns = artifacts, credentials # 배포 결과물(artifacts)이나 중요 정보(credentials) 폴더 내의 파일을 인벤토리 스캔 대상에서 제외합니다.

# (참고) Vagrantfile
cat Vagrantfile&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;p data-ke-size=&quot;size18&quot;&gt;Kubespary 를 통한 k8s 배포&lt;/p&gt;
&lt;pre id=&quot;code_1770255165285&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inventory 디렉터리 복사
cp -rfp /root/kubespray/inventory/sample /root/kubespray/inventory/mycluster
tree inventory/mycluster/
inventory/mycluster/
├── group_vars
│   ├── all
│   │   ├── all.yml
│   │   ├── aws.yml
│   │   ├── azure.yml
│   │   ├── containerd.yml
│   │   ├── coreos.yml
│   │   ├── cri-o.yml
│   │   ├── docker.yml
│   │   ├── etcd.yml
│   │   ├── gcp.yml
│   │   ├── hcloud.yml
│   │   ├── huaweicloud.yml
│   │   ├── oci.yml
│   │   ├── offline.yml
│   │   ├── openstack.yml
│   │   ├── upcloud.yml
│   │   └── vsphere.yml
│   └── k8s_cluster
│       ├── addons.yml
│       ├── k8s-cluster.yml
│       ├── k8s-net-calico.yml
│       ├── k8s-net-cilium.yml
│       ├── k8s-net-custom-cni.yml
│       ├── k8s-net-flannel.yml
│       ├── k8s-net-kube-ovn.yml
│       ├── k8s-net-kube-router.yml
│       ├── k8s-net-macvlan.yml
│       └── kube_control_plane.yml
└── inventory.ini

# inventory.ini 작성
cat &amp;lt;&amp;lt; EOF &amp;gt; /root/kubespray/inventory/mycluster/inventory.ini
k8s-ctr ansible_host=192.168.10.10 ip=192.168.10.10

[kube_control_plane]
k8s-ctr

[etcd:children]
kube_control_plane

[kube_node]
k8s-ctr
EOF
cat /root/kubespray/inventory/mycluster/inventory.ini


# https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible/vars.md
## &amp;lt;your-favorite-editor&amp;gt; inventory/mycluster/group_vars/all.yml # for every node, including etcd
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/all/all.yml
---
bin_dir: /usr/local/bin
loadbalancer_apiserver_port: 6443
loadbalancer_apiserver_healthcheck_port: 8081
no_proxy_exclude_workers: false
kube_webhook_token_auth: false
kube_webhook_token_auth_url_skip_tls_verify: false
ntp_enabled: false
ntp_manage_config: false
ntp_servers:
  - &quot;0.pool.ntp.org iburst&quot;
  - &quot;1.pool.ntp.org iburst&quot;
  - &quot;2.pool.ntp.org iburst&quot;
  - &quot;3.pool.ntp.org iburst&quot;
unsafe_show_logs: false
allow_unsupported_distribution_setup: false

## &amp;lt;your-favorite-editor&amp;gt; inventory/mycluster/group_vars/k8s_cluster.yml # for every node in the cluster (not etcd when it's separate)
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
---
kube_config_dir: /etc/kubernetes
kube_script_dir: &quot;{{ bin_dir }}/kubernetes-scripts&quot;
kube_manifest_dir: &quot;{{ kube_config_dir }}/manifests&quot;
kube_cert_dir: &quot;{{ kube_config_dir }}/ssl&quot;
kube_token_dir: &quot;{{ kube_config_dir }}/tokens&quot;
kube_api_anonymous_auth: true
local_release_dir: &quot;/tmp/releases&quot;
retry_stagger: 5
kube_owner: kube
kube_cert_group: kube-cert
kube_log_level: 2
credentials_dir: &quot;{{ inventory_dir }}/credentials&quot;
kube_network_plugin: calico
kube_network_plugin_multus: false
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
kube_network_node_prefix: 24
kube_service_addresses_ipv6: fd85:ee78:d8a6:8607::1000/116
kube_pods_subnet_ipv6: fd85:ee78:d8a6:8607::1:0000/112
kube_network_node_prefix_ipv6: 120
kube_apiserver_ip: &quot;{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(1) | ansible.utils.ipaddr('address') }}&quot;
kube_apiserver_port: 6443  # (https)
kube_proxy_mode: ipvs
kube_proxy_strict_arp: false
kube_proxy_nodeport_addresses: &amp;gt;-
  {%- if kube_proxy_nodeport_addresses_cidr is defined -%}
  [{{ kube_proxy_nodeport_addresses_cidr }}]
  {%- else -%}
  []
  {%- endif -%}
kube_encrypt_secret_data: false
cluster_name: cluster.local
ndots: 2
dns_mode: coredns
enable_nodelocaldns: true
enable_nodelocaldns_secondary: false
nodelocaldns_ip: 169.254.25.10
nodelocaldns_health_port: 9254
nodelocaldns_second_health_port: 9256
nodelocaldns_bind_metrics_host_ip: false
nodelocaldns_secondary_skew_seconds: 5
enable_coredns_k8s_external: false
coredns_k8s_external_zone: k8s_external.local
enable_coredns_k8s_endpoint_pod_names: false
resolvconf_mode: host_resolvconf
deploy_netchecker: false
skydns_server: &quot;{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(3) | ansible.utils.ipaddr('address') }}&quot;
skydns_server_secondary: &quot;{{ kube_service_subnets.split(',') | first | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(4) | ansible.utils.ipaddr('address') }}&quot;
dns_domain: &quot;{{ cluster_name }}&quot;
container_manager: containerd
kata_containers_enabled: false
kubeadm_certificate_key: &quot;{{ lookup('password', credentials_dir + '/kubeadm_certificate_key.creds length=64 chars=hexdigits') | lower }}&quot;
k8s_image_pull_policy: IfNotPresent
kubernetes_audit: false
default_kubelet_config_dir: &quot;{{ kube_config_dir }}/dynamic_kubelet_dir&quot;
volume_cross_zone_attachment: false
persistent_volumes_enabled: false
event_ttl_duration: &quot;1h0m0s&quot;
auto_renew_certificates: false
# auto_renew_certificates_systemd_calendar: &quot;Mon *-*-1,2,3,4,5,6,7 03:00:00&quot;  # First Monday of each month
kubeadm_patches_dir: &quot;{{ kube_config_dir }}/patches&quot;
kubeadm_patches: []
remove_anonymous_access: false

# 테스트할 기능 관련 수정
sed -i 's|kube_network_plugin: calico|kube_network_plugin: flannel|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|kube_proxy_mode: ipvs|kube_proxy_mode: iptables|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|enable_nodelocaldns: true|enable_nodelocaldns: false|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|auto_renew_certificates: false|auto_renew_certificates: true|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
sed -i 's|# auto_renew_certificates_systemd_calendar|auto_renew_certificates_systemd_calendar|g' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml
grep -iE 'kube_network_plugin:|kube_proxy_mode|enable_nodelocaldns:|^auto_renew_certificates' inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml


## flannel 설정 수정  inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
cat inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
echo &quot;flannel_interface: enp0s9&quot; &amp;gt;&amp;gt; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/k8s-net-flannel.yml


## &amp;lt;your-favorite-editor&amp;gt; inventory/mycluster/group_vars/kube_control_plane.yml # for the control plane
cat inventory/mycluster/group_vars/k8s_cluster/kube_control_plane.yml 
# Reservation for control plane kubernetes components
# kube_memory_reserved: 512Mi
# kube_cpu_reserved: 200m
# kube_ephemeral_storage_reserved: 2Gi
# kube_pid_reserved: &quot;1000&quot;

# Reservation for control plane host system
# system_memory_reserved: 256Mi
# system_cpu_reserved: 250m
# system_ephemeral_storage_reserved: 2Gi
# system_pid_reserved: &quot;1000&quot;

## &amp;lt;your-favorite-editor&amp;gt; addons.yml
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/k8s_cluster/addons.yml
---
helm_enabled: false
registry_enabled: false
metrics_server_enabled: false
local_path_provisioner_enabled: false
local_volume_provisioner_enabled: false
gateway_api_enabled: false
ingress_nginx_enabled: false
ingress_publish_status_address: &quot;&quot;
ingress_alb_enabled: false
cert_manager_enabled: false
metallb_enabled: false
metallb_speaker_enabled: &quot;{{ metallb_enabled }}&quot;
metallb_namespace: &quot;metallb-system&quot;
argocd_enabled: false
kube_vip_enabled: false
node_feature_discovery_enabled: false

# 테스트할 기능 관련 수정
sed -i 's|helm_enabled: false|helm_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
sed -i 's|metrics_server_enabled: false|metrics_server_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
sed -i 's|node_feature_discovery_enabled: false|node_feature_discovery_enabled: true|g' inventory/mycluster/group_vars/k8s_cluster/addons.yml
grep -iE 'helm_enabled:|metrics_server_enabled:|node_feature_discovery_enabled:' inventory/mycluster/group_vars/k8s_cluster/addons.yml


# etcd.yml : 파드가 아닌 systemd unit
grep &quot;^[^#]&quot; inventory/mycluster/group_vars/all/etcd.yml
---
etcd_data_dir: /var/lib/etcd
etcd_deployment_type: host

# containerd.yml 
cat inventory/mycluster/group_vars/all/containerd.yml
---
# Please see roles/container-engine/containerd/defaults/main.yml for more configuration options

# containerd_storage_dir: &quot;/var/lib/containerd&quot;
# containerd_state_dir: &quot;/run/containerd&quot;
# containerd_oom_score: 0

# containerd_default_runtime: &quot;runc&quot;
# containerd_snapshotter: &quot;native&quot;

# containerd_runc_runtime:
#   name: runc
#   type: &quot;io.containerd.runc.v2&quot;
#   engine: &quot;&quot;
...(생략)...

# 기본 환경 정보 출력 저장
ip addr | tee -a ip_addr-1.txt 
ss -tnlp | tee -a ss-1.txt
df -hT | tee -a df-1.txt
findmnt | tee -a findmnt-1.txt
sysctl -a | tee -a sysctl-1.txt

# 지원 버전 정보 확인
cat roles/kubespray_defaults/vars/main/checksums.yml | grep -i kube -A40

# 배포: 아래처럼 반드시 ~/kubespray 디렉토리에서 ansible-playbook 를 실행하자!
# Deploy Kubespray with Ansible Playbook - run the playbook as root
# The option `--become` is required, as for example writing SSL keys in /etc/,
# installing packages and interacting with various systemd daemons.
ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version=&quot;1.33.3&quot; --list-tasks # 배포 전, Task 목록 확인
ANSIBLE_FORCE_COLOR=true ansible-playbook -i inventory/mycluster/inventory.ini -v cluster.yml -e kube_version=&quot;1.33.3&quot; | tee kubespray_install.log

# 설치 확인 : /root/.kube/config
more kubespray_install.log
kubectl get node -v=6
cat /root/.kube/config

# k8s
kubectl get node -owide
NAME      STATUS   ROLES           AGE    VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION                  CONTAINER-RUNTIME
k8s-ctr   Ready    control-plane   113s   v1.33.3   192.168.10.10   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5

kubectl get pod -A
...

# 기본 환경 정보 출력 저장
ip addr | tee -a ip_addr-2.txt 
ss -tnlp | tee -a ss-2.txt
df -hT | tee -a df-2.txt
findmnt | tee -a findmnt-2.txt
sysctl -a | tee -a sysctl-2.txt

# 파일 출력 비교 : 빠져나오기 ':q' -&amp;gt; ':q' =&amp;gt; 변경된 부분이 어떤 동작과 역할인지 조사해보기! , ctrl + f / b
vi -d ip_addr-1.txt ip_addr-2.txt
vi -d ss-1.txt ss-2.txt
vi -d df-1.txt df-2.txt
vi -d findmnt-1.txt findmnt-2.txt
vi -d sysctl-1.txt sysctl-2.txt&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;k8s 설치 전후 서버 환경 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/solidcellaMoon/opkhs-2026-k8s-deploy/tree/main/4-kubespray/diff-about-k8s&quot;&gt;https://github.com/solidcellaMoon/opkhs-2026-k8s-deploy/tree/main/4-kubespray/diff-about-k8s&lt;/a&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;alias, 자동 완성 및 k9s 설치&lt;/h4&gt;
&lt;pre id=&quot;code_1770342479869&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Source the completion
source &amp;lt;(kubectl completion bash)
source &amp;lt;(kubeadm completion bash)

# Alias kubectl to k
alias k=kubectl
complete -o default -F __start_kubectl k

# k9s 설치 : https://github.com/derailed/k9s
CLI_ARCH=amd64
if [ &quot;$(uname -m)&quot; = &quot;aarch64&quot; ]; then CLI_ARCH=arm64; fi
wget https://github.com/derailed/k9s/releases/latest/download/k9s_linux_${CLI_ARCH}.tar.gz
tar -xzf k9s_linux_*.tar.gz
ls -al k9s
chown root:root k9s
mv k9s /usr/local/bin/
chmod +x /usr/local/bin/k9s
k9s&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;span style=&quot;background-color: #9feec3;&quot;&gt; Ansible Playbook &amp;amp; Role 분석 &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ansible Play &amp;rarr; Task 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;2345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YtYeH/dJMcaiWtGkb/kC4cDgy7o5AtfddHsJV2Ok/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YtYeH/dJMcaiWtGkb/kC4cDgy7o5AtfddHsJV2Ok/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YtYeH/dJMcaiWtGkb/kC4cDgy7o5AtfddHsJV2Ok/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYtYeH%2FdJMcaiWtGkb%2FkC4cDgy7o5AtfddHsJV2Ok%2Fimg.jpg&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;475&quot; height=&quot;2345&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;2345&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;PLAY 과정&lt;/p&gt;
&lt;pre id=&quot;code_1770342585292&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
cat kubespray_install.log | grep -E 'PLAY'
PLAY [Check Ansible version] ******************************    # Kubespray가 지원하는 Ansible 버전인지 확인
PLAY [Inventory setup and validation] *********************    # inventory 설정 정합성 검증 : kube_control_plane, etcd 그룹 존재 여부, etcd 노드 수 (odd 권장), Pod CIDR / Service CIDR 유효성, Kubernetes 버전 지원 여부
PLAY [Install bastion ssh config] *************************    # Bastion(점프 호스트) 환경 지원 : bastion 미사용 시 대부분 skip
PLAY [Bootstrap hosts for Ansible] ************************    # 모든 노드를 Ansible 실행 가능한 상태로 만듦 : Python 설치, sudo 권한 확보, 기본 패키지 설치, /usr/bin/python 보장
PLAY [Gather facts] ***************************************    # Ansible fact 수집 : 이후 TASK들이: when: ansible_os_family == &quot;Debian&quot; 같은 조건 분기에서 사용됨
PLAY [Prepare for etcd install] ***************************    # etcd 설치 전 사전 준비 : etcd user 생성, 디렉터리 생성, 방화벽 / 포트, cert 경로 준비
PLAY [Add worker nodes to the etcd play if needed] ********    # worker + etcd 겸용 노드 지원 : kube_node: + etcd: 둘 다 포함된 노드를 etcd PLAY에 추가
PLAY [Install etcd] ***************************************    # etcd 설치 : etcd binary 설치, TLS 인증서 생성, systemd 등록, 클러스터 구성
PLAY [Install Kubernetes nodes] ***************************    # 모든 노드에 공통 K8s 컴포넌트 설치, 아직 클러스터 join은 안 함
PLAY [Install the control plane] **************************    # kube_control_plane 그룹 : control-plane 노드 구성
PLAY [Invoke kubeadm and install a CNI] *******************    # kubeadm init / join 실행 , 네트워크(CNI) 설치
PLAY [Install Calico Route Reflector] *********************    # Calico BGP 미사용환경이면 skip
PLAY [Patch Kubernetes for Windows] ***********************    # Linux-only 환경이면 skip
PLAY [Install Kubernetes apps] ****************************    # 기본 애드온 설치 : CoreDNS, metrics-server 등
PLAY [Apply resolv.conf changes now that cluster DNS is up]    # CoreDNS 설치 후 노드의 DNS 설정 최종 정리 : bootstrap 단계에선 임시 resolv.conf 사용, 클러스터 DNS 안정화 후 되돌림
PLAY RECAP ************************************************&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;p data-ke-size=&quot;size18&quot;&gt;TASK&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;세부&amp;nbsp;task&amp;nbsp;559개 &lt;br /&gt;cat&amp;nbsp;kubespray_install.log&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;-E&amp;nbsp;'TASK'&amp;nbsp;|&amp;nbsp;wc&amp;nbsp;-l &lt;br /&gt;559 &lt;br /&gt;&lt;br /&gt;cat&amp;nbsp;kubespray_install.log&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;-E&amp;nbsp;'TASK' &lt;br /&gt;TASK&amp;nbsp;[Check&amp;nbsp;2.17.3&amp;nbsp;&amp;lt;=&amp;nbsp;Ansible&amp;nbsp;version&amp;nbsp;&amp;lt;&amp;nbsp;2.18.0]&amp;nbsp;******************************** &lt;br /&gt;TASK&amp;nbsp;[dynamic_groups&amp;nbsp;:&amp;nbsp;Match&amp;nbsp;needed&amp;nbsp;groups&amp;nbsp;by&amp;nbsp;their&amp;nbsp;old&amp;nbsp;names&amp;nbsp;or&amp;nbsp;definition]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[validate_inventory&amp;nbsp;:&amp;nbsp;Stop&amp;nbsp;if&amp;nbsp;removed&amp;nbsp;tags&amp;nbsp;are&amp;nbsp;used]&amp;nbsp;********************** &lt;br /&gt;TASK&amp;nbsp;[bootstrap_os&amp;nbsp;:&amp;nbsp;Fetch&amp;nbsp;/etc/os-release]&amp;nbsp;************************************ &lt;br /&gt;TASK&amp;nbsp;[system_packages&amp;nbsp;:&amp;nbsp;Gather&amp;nbsp;OS&amp;nbsp;information]&amp;nbsp;********************************* &lt;br /&gt;TASK&amp;nbsp;[bootstrap_os&amp;nbsp;:&amp;nbsp;Create&amp;nbsp;remote_tmp&amp;nbsp;for&amp;nbsp;it&amp;nbsp;is&amp;nbsp;used&amp;nbsp;by&amp;nbsp;another&amp;nbsp;module]&amp;nbsp;******* &lt;br /&gt;TASK&amp;nbsp;[network_facts&amp;nbsp;:&amp;nbsp;Gather&amp;nbsp;ansible_default_ipv4]&amp;nbsp;***************************** &lt;br /&gt;TASK&amp;nbsp;[Gather&amp;nbsp;minimal&amp;nbsp;facts]&amp;nbsp;**************************************************** &lt;br /&gt;TASK&amp;nbsp;[adduser&amp;nbsp;:&amp;nbsp;User&amp;nbsp;|&amp;nbsp;Create&amp;nbsp;User&amp;nbsp;Group]&amp;nbsp;************************************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/preinstall&amp;nbsp;:&amp;nbsp;Check&amp;nbsp;if&amp;nbsp;/etc/fstab&amp;nbsp;exists]&amp;nbsp;********************** &lt;br /&gt;TASK&amp;nbsp;[container-engine/validate-container-engine&amp;nbsp;:&amp;nbsp;Validate-container-engine&amp;nbsp;|&amp;nbsp;check&amp;nbsp;if&amp;nbsp;fedora&amp;nbsp;coreos]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[download&amp;nbsp;:&amp;nbsp;Prep_download&amp;nbsp;|&amp;nbsp;Set&amp;nbsp;a&amp;nbsp;few&amp;nbsp;facts]&amp;nbsp;****************************** &lt;br /&gt;TASK&amp;nbsp;[Gathering&amp;nbsp;Facts]&amp;nbsp;********************************************************* &lt;br /&gt;TASK&amp;nbsp;[Check&amp;nbsp;if&amp;nbsp;nodes&amp;nbsp;needs&amp;nbsp;etcd&amp;nbsp;client&amp;nbsp;certs&amp;nbsp;(depends&amp;nbsp;on&amp;nbsp;network_plugin)]&amp;nbsp;****** &lt;br /&gt;TASK&amp;nbsp;[adduser&amp;nbsp;:&amp;nbsp;User&amp;nbsp;|&amp;nbsp;Create&amp;nbsp;User&amp;nbsp;Group]&amp;nbsp;************************************** &lt;br /&gt;TASK&amp;nbsp;[etcd&amp;nbsp;:&amp;nbsp;Check&amp;nbsp;etcd&amp;nbsp;certs]&amp;nbsp;************************************************* &lt;br /&gt;TASK&amp;nbsp;[etcdctl_etcdutl&amp;nbsp;:&amp;nbsp;Download&amp;nbsp;etcd&amp;nbsp;binary]&amp;nbsp;********************************** &lt;br /&gt;TASK&amp;nbsp;[etcd&amp;nbsp;:&amp;nbsp;Install&amp;nbsp;etcd]&amp;nbsp;***************************************************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/node&amp;nbsp;:&amp;nbsp;Set&amp;nbsp;kubelet_cgroup_driver_detected&amp;nbsp;fact&amp;nbsp;for&amp;nbsp;containerd]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/control-plane&amp;nbsp;:&amp;nbsp;Pre-upgrade&amp;nbsp;|&amp;nbsp;Delete&amp;nbsp;control&amp;nbsp;plane&amp;nbsp;manifests&amp;nbsp;if&amp;nbsp;etcd&amp;nbsp;secrets&amp;nbsp;changed]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/client&amp;nbsp;:&amp;nbsp;Set&amp;nbsp;external&amp;nbsp;kube-apiserver&amp;nbsp;endpoint]&amp;nbsp;**************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes-apps/cluster_roles&amp;nbsp;:&amp;nbsp;Kubernetes&amp;nbsp;Apps&amp;nbsp;|&amp;nbsp;Wait&amp;nbsp;for&amp;nbsp;kube-apiserver]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/kubeadm&amp;nbsp;:&amp;nbsp;Set&amp;nbsp;kubeadm_discovery_address]&amp;nbsp;********************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/node-label&amp;nbsp;:&amp;nbsp;Set&amp;nbsp;role&amp;nbsp;node&amp;nbsp;label&amp;nbsp;to&amp;nbsp;empty&amp;nbsp;list]&amp;nbsp;*************** &lt;br /&gt;TASK&amp;nbsp;[network_plugin/cni&amp;nbsp;:&amp;nbsp;CNI&amp;nbsp;|&amp;nbsp;make&amp;nbsp;sure&amp;nbsp;/opt/cni/bin&amp;nbsp;exists]&amp;nbsp;**************** &lt;br /&gt;TASK&amp;nbsp;[network_plugin/flannel&amp;nbsp;:&amp;nbsp;Flannel&amp;nbsp;|&amp;nbsp;Create&amp;nbsp;Flannel&amp;nbsp;manifests]&amp;nbsp;************* &lt;br /&gt;TASK&amp;nbsp;[win_nodes/kubernetes_patch&amp;nbsp;:&amp;nbsp;Ensure&amp;nbsp;that&amp;nbsp;user&amp;nbsp;manifests&amp;nbsp;directory&amp;nbsp;exists]&amp;nbsp;*** &lt;br /&gt;TASK&amp;nbsp;[kubernetes-apps/ansible&amp;nbsp;:&amp;nbsp;Kubernetes&amp;nbsp;Apps&amp;nbsp;|&amp;nbsp;Wait&amp;nbsp;for&amp;nbsp;kube-apiserver]&amp;nbsp;***** &lt;br /&gt;TASK&amp;nbsp;[kubernetes-apps/helm&amp;nbsp;:&amp;nbsp;Helm&amp;nbsp;|&amp;nbsp;Gather&amp;nbsp;os&amp;nbsp;specific&amp;nbsp;variables]&amp;nbsp;************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes-apps/metrics_server&amp;nbsp;:&amp;nbsp;Metrics&amp;nbsp;Server&amp;nbsp;|&amp;nbsp;Delete&amp;nbsp;addon&amp;nbsp;dir]&amp;nbsp;****** &lt;br /&gt;TASK&amp;nbsp;[adduser&amp;nbsp;:&amp;nbsp;User&amp;nbsp;|&amp;nbsp;Create&amp;nbsp;User&amp;nbsp;Group]&amp;nbsp;************************************** &lt;br /&gt;TASK&amp;nbsp;[kubernetes/preinstall&amp;nbsp;:&amp;nbsp;Check&amp;nbsp;resolvconf]&amp;nbsp;********************************&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;playbooks/cluster.yml&lt;/span&gt; 설치 절차&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/root/kubespray/cluster.yml &amp;rarr; playbooks/cluster.yml&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770342667411&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 
cat /root/kubespray/cluster.yml 

--- 
- name: Install Kubernetes ansible.builtin.import_playbook: playbooks/cluster.yml&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cluster.yml은 Kubespray의 메인 플레이북으로, 클러스터 생성의 전체 과정을 정의&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;1053&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pdP2Z/dJMcahcdvnh/qA9CthyK0C7h7b9DRT3bg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pdP2Z/dJMcahcdvnh/qA9CthyK0C7h7b9DRT3bg1/img.png&quot; data-alt=&quot;https://velog.io/@bytebliss/kubespray-Ansible-Playbook-Role-분석&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pdP2Z/dJMcahcdvnh/qA9CthyK0C7h7b9DRT3bg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpdP2Z%2FdJMcahcdvnh%2FqA9CthyK0C7h7b9DRT3bg1%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;1053&quot; height=&quot;419&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://velog.io/@bytebliss/kubespray-Ansible-Playbook-Role-분석&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1433&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceVRBP/dJMcadU9PIa/V8SHH3kvDJq2gNikBdtAjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceVRBP/dJMcadU9PIa/V8SHH3kvDJq2gNikBdtAjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceVRBP/dJMcadU9PIa/V8SHH3kvDJq2gNikBdtAjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceVRBP%2FdJMcadU9PIa%2FV8SHH3kvDJq2gNikBdtAjK%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;1433&quot; height=&quot;630&quot; data-origin-width=&quot;1433&quot; data-origin-height=&quot;630&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;1925&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/taZB3/dJMcadOm7zR/PKmv2yNesFBDARANeiEjB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/taZB3/dJMcadOm7zR/PKmv2yNesFBDARANeiEjB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/taZB3/dJMcadOm7zR/PKmv2yNesFBDARANeiEjB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtaZB3%2FdJMcadOm7zR%2FPKmv2yNesFBDARANeiEjB1%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;1925&quot; height=&quot;676&quot; data-origin-width=&quot;1925&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://velog.io/@bytebliss/kubespray-cluster.yaml&quot;&gt;https://velog.io/@bytebliss/kubespray-cluster.yaml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;2130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfhfRD/dJMcadnkjXH/uHhov37KiYjNAXJnrQjpC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfhfRD/dJMcadnkjXH/uHhov37KiYjNAXJnrQjpC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfhfRD/dJMcadnkjXH/uHhov37KiYjNAXJnrQjpC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfhfRD%2FdJMcadnkjXH%2FuHhov37KiYjNAXJnrQjpC0%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;1010&quot; height=&quot;2130&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;2130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://sirzzang.github.io/kubernetes/Kubernetes-Kubespray-04-01-00/&quot;&gt;https://sirzzang.github.io/kubernetes/Kubernetes-Kubespray-04-01-00/&lt;/a&gt;&lt;/p&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;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;0&quot;&gt;playbooks/cluster.yml&lt;/span&gt;: 전체 과정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A. 초기화 및 정보 수집 (Boilerplate &amp;amp; Facts)&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B. 인프라 및 엔진 준비 (Prepare for etcd &amp;amp; container-engine)&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;kubernetes/preinstall&lt;/b&gt;: 방화벽, 커널 파라미터, Swap 비활성화 등 K8s 설치를 위한 OS 최적화를 수행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;container-engine&lt;/b&gt;: Docker나 Containerd 같은 컨테이너 런타임을 설치합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;download&lt;/b&gt;: 설치에 필요한 모든 바이너리와 이미지들을 미리 다운로드합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;C. 데이터 저장소 및 노드 구성 (Etcd &amp;amp; K8s Nodes)&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;etcd&lt;/b&gt;: 쿠버네티스의 상태 정보를 저장하는 DB 클러스터를 구축합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubernetes/node&lt;/b&gt;: 모든 노드에 Kubelet, Kube-proxy 등 기초 컴포넌트를 설치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;D. 컨트롤 플레인 및 네트워크 (Control Plane &amp;amp; CNI)&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;control-plane&lt;/b&gt;: 마스터 노드에 API 서버, 스케줄러 등을 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubeadm&lt;/b&gt;: kubeadm init 또는 join을 통해 클러스터를 하나로 묶습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;network_plugin&lt;/b&gt;: Calico, Flannel 등 CNI를 설치하여 파드 간 통신을 가능하게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;E. 부가 서비스 설치 (Apps &amp;amp; DNS)&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;Ingress Controller, Storage Provisioner 등 클러스터 운영에 필요한 앱들을 배포하고, 최종적으로 노드의 DNS(resolv.conf)가 클러스터 내부 DNS를 바라보도록 수정합니다.&lt;/li&gt;
&lt;/ul&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;/p&gt;
&lt;pre id=&quot;code_1770342898948&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat playbooks/cluster.yml
---
- name: Common tasks for every playbooks
  import_playbook: boilerplate.yml

- name: Gather facts
  import_playbook: internal_facts.yml

- name: Prepare for etcd install
  hosts: k8s_cluster:etcd        # 이 작업은 쿠버네티스 클러스터에 속한 모든 노드(k8s_cluster)와 etcd 전용 노드(etcd) 전체를 대상으로 실행.
  gather_facts: false            
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot; # 하나라도 에러가 발생하면 전체 배포 프로세스를 즉시 중단합니다.
  environment: &quot;{{ proxy_disable_env }}&quot;                     # 인터넷 통신 시 프록시 설정을 제어합니다. 주로 내부 통신 시 프록시를 우회하도록 설정할 때 사용됩니다.
  roles:
    - { role: kubespray_defaults }                           # Kubespray 전체에서 사용하는 공통 변수(경로, 버전 등)를 로드합니다.
    - { role: kubernetes/preinstall, tags: preinstall }      # OS 레벨의 사전 설정을 수행합니다. 예) 커널 파라미터(sysctl) 최적화 등
    - { role: &quot;container-engine&quot;, tags: &quot;container-engine&quot;, when: deploy_container_engine } # 컨테이너 런타임(containerd, Docker 등)을 설치합니다.
    - { role: download, tags: download, when: &quot;not skip_downloads&quot; } # 설치에 필요한 모든 바이너리와 컨테이너 이미지를 다운로드합니다.
 
- name: Install etcd
  vars:
    etcd_cluster_setup: true  # etcd를 &amp;ldquo;클러스터 모드&amp;rdquo;로 초기화. 단일 노드라도 내부 로직은 클러스터 기준으로 동작. 신규 클러스터 생성 시 반드시 true
    etcd_events_cluster_setup: &quot;{{ etcd_events_cluster_enabled }}&quot; # true 일 경우 events 전용 etcd 클러스터를 별도로 구성, 대부분 환경에서는 false
  import_playbook: install_etcd.yml  # Playbook을 import 하는 구조, 즉, 여기에는 hosts:가 없고 install_etcd.yml 안에 실제 Play들이 있음.

- name: Install Kubernetes nodes    # 이 노드를 &quot;kubelet이 돌아가는 Kubernetes 노드&quot;로 만든다.
  hosts: k8s_cluster                # 모든 k8s 노드 : control-plane , worker
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/node, tags: node }  # 예) kubelet 설치 등

- name: Install the control plane
  hosts: kube_control_plane         # control-plane 노드만
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/control-plane, tags: control-plane } # 설치 : apiserver, kcm, scheduler
    - { role: kubernetes/client, tags: client }               # kubectl 설치, admin kubeconfig 생성 준비
    - { role: kubernetes-apps/cluster_roles, tags: cluster-roles } # Kubernetes 기본 ClusterRole 생성, bootstrap / node / admin 권한 관련

- name: Invoke kubeadm and install a CNI                      
  hosts: k8s_cluster
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/kubeadm, tags: kubeadm}           # kubeadm init* / join* 실행
    - { role: kubernetes/node-label, tags: node-label }    # inventory 기반으로 node label 설정
    - { role: kubernetes/node-taint, tags: node-taint }    # control-plane taint 설정
    - { role: kubernetes-apps/common_crds }                # CNI / ingress / storage에서 사용하는 공통 CRD 먼저 설치
    - { role: network_plugin, tags: network }              # 실제 CNI 플러그인 설치 단계* : Calico / Cilium / Flannel / Weave 등

- name: Install Calico Route Reflector
  hosts: calico_rr
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: network_plugin/calico/rr, tags: ['network', 'calico_rr'] }

- name: Patch Kubernetes for Windows
  hosts: kube_control_plane[0]
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: win_nodes/kubernetes_patch, tags: [&quot;control-plane&quot;, &quot;win_nodes&quot;] }

- name: Install Kubernetes apps
  hosts: kube_control_plane        # control-plane 노드만 : 실제로는 클러스터 전체에 리소스를 배포하지만, 명령 실행 위치만 control-plane
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults } # 공통 변수 로딩, apps 관련 enable/disable 값 정리
    - { role: kubernetes-apps/external_cloud_controller, tags: external-cloud-controller } # cloud-controller-manager 외부 실행 버전, 베어메탈 / lab 환경이면 보통 비활성
    - { role: kubernetes-apps/policy_controller, tags: policy-controller }    # Kubernetes Policy 계열 컨트롤러, 예) PodSecurity Admission, OPA / Gatekeeper 연계
    - { role: kubernetes-apps/ingress_controller, tags: ingress-controller }  # Ingress Controller 설치, 예) NGINX Ingress, HAProxy Ingress, Traefik
    - { role: kubernetes-apps/external_provisioner, tags: external-provisioner } # 외부 스토리지 프로비저너, 예) CSI provisioner, NFS external provisioner, Ceph RBD / CephFS
    - { role: kubernetes-apps, tags: apps } # CoreDNS (이미 설치됐지만 재확인), Metrics Server, Local Path Provisioner, Helm add-ons

- name: Apply resolv.conf changes now that cluster DNS is up
  hosts: k8s_cluster      # 모든 노드
  gather_facts: false
  any_errors_fatal: &quot;{{ any_errors_fatal | default(true) }}&quot;
  environment: &quot;{{ proxy_disable_env }}&quot;
  roles:
    - { role: kubespray_defaults }
    - { role: kubernetes/preinstall, when: &quot;dns_mode != 'none' and resolvconf_mode == 'host_resolvconf'&quot;, tags: resolvconf, dns_late: 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;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;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;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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/51</guid>
      <comments>https://netoppa.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 6 Feb 2026 10:55:18 +0900</pubDate>
    </item>
    <item>
      <title>[3주차] Kubeadm &amp;amp; K8S Upgrade</title>
      <link>https://netoppa.tistory.com/50</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: left;&quot;&gt;이 포스팅은 Cloud@net의 기시다님의 온라인 강좌 내용을 정리한 스터디 요약 자료입니다.&lt;/span&gt;&lt;/blockquote&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Kubeadm deep dive&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h2 data-end=&quot;213&quot; data-start=&quot;172&quot; data-ke-size=&quot;size26&quot;&gt;1. kubeadm 개요 (Cluster Lifecycle 프로젝트)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;kubeadm 은 Kubernetes 클러스터를 구성하고 생명주기를 관리하기 위한 공식 도구입니다.&lt;br /&gt;주요 목적은 Kubernetes 클러스터를 구성하는 각 노드가 정해진 절차에 따라 안전하고 일관되게 bootstrap&lt;br /&gt;되도록 돕는 것입니다.&lt;/blockquote&gt;
&lt;p data-end=&quot;397&quot; data-start=&quot;369&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;470&quot; data-start=&quot;399&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;470&quot; data-start=&quot;401&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubeadm은 Kubernetes 클러스터를 구성하기 위한 노드 부트스트래퍼(node bootstrapper)이다.&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;633&quot; data-start=&quot;486&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;514&quot; data-start=&quot;486&quot;&gt;컨테이너 오케스트레이션을 직접 수행하지 않고&lt;/li&gt;
&lt;li data-end=&quot;536&quot; data-start=&quot;515&quot;&gt;애플리케이션 배포 도구도 아니며&lt;/li&gt;
&lt;li data-end=&quot;633&quot; data-start=&quot;537&quot;&gt;Kubernetes의 핵심 컴포넌트(control plane, worker node)가 &lt;b&gt;정상적으로 기동되고 서로 연결되도록 초기 설정을 자동화&lt;/b&gt;하는 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 쿠버네티스(Kubernetes) 핵심 명령어: kubeadm&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;kubeadm init&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,0,1&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;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;kubeadm join&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,1,1&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;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;kubeadm upgrade&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 버전 업데이트&lt;/li&gt;
&lt;li&gt;운영 중인 쿠버네티스 클러스터를 최신 버전으로 안전하게 업그레이드&lt;/li&gt;
&lt;li&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,3,0&quot;&gt;kubeadm reset&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 설정 초기화&lt;/li&gt;
&lt;li&gt;init 또는 join을 통해 호스트에 적용되었던 모든 변경 사항을 삭제하고, 클러스터 설치 전의 깨끗한 상태로 복원&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;Who uses kubeadm? : minikube, kind, clusterapi, kubespary 등.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-size: 1.12em; letter-spacing: 0px;&quot;&gt;Part of the composable solution&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baWsfd/dJMcacu6NSL/W65OcVo3G5yiGuiPxwa1Qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baWsfd/dJMcacu6NSL/W65OcVo3G5yiGuiPxwa1Qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baWsfd/dJMcacu6NSL/W65OcVo3G5yiGuiPxwa1Qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaWsfd%2FdJMcacu6NSL%2FW65OcVo3G5yiGuiPxwa1Qk%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;1002&quot; height=&quot;528&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot; data-token-index=&quot;0&quot;&gt;kubeadm 으로 k8s 구성 절차&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;[공통] 사전 설정&lt;/li&gt;
&lt;li&gt;[공통] CRI 설치 : containerd&lt;/li&gt;
&lt;li&gt;[공통] kubeadm, kubelet 및 kubectl 설치&lt;/li&gt;
&lt;li&gt;[Controlplane node] kubeadm 으로 k8s 클러스터 구성 &amp;rarr; Flannel CNI 설치 &amp;rarr; 편의성 설치 및 확인&lt;/li&gt;
&lt;li&gt;[Worker nodes] kubeadm 으로 k8s 클러스터 join &amp;rarr; 확인&lt;/li&gt;
&lt;li&gt;모니터링 툴 설치 : 프로메테우스-스택 설치 &amp;rarr; 인증서 익스포터 설치 &amp;rarr; 그라파나 대시보드 확인&lt;/li&gt;
&lt;li&gt;샘플 애플리케이션 배포&lt;/li&gt;
&lt;li&gt;kubeadm 인증서 갱신&lt;/li&gt;
&lt;/ol&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;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주요 설치 버전 정보&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;버전 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;v1.32.11&lt;/p&gt;
&lt;table id=&quot;2ec50aec-5edf-8105-b520-fae750397d08&quot; style=&quot;border-collapse: collapse; width: 100%; height: 198px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;버전&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;k8s버전 호환성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-81f7-8203-f1f9bedf9a68&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;Rocky Linux&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;10.0-1.6&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;RHEL 10 소스 기반 배포판으로 RHEL 정보 참고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-817d-bf8d-cdebadf7842e&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;containerd&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v2.1.5&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;CRI Version(v1), k8s 1.32~1.35 지원 - &lt;a href=&quot;https://containerd.io/releases/#kubernetes-support&quot;&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-81bd-8c27-c5e4044c0586&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;runc&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v1.3.3&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;정보 조사 필요 &lt;a href=&quot;https://github.com/opencontainers/runc&quot;&gt;https://github.com/opencontainers/runc&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-81b6-a1c4-c52a0f42c3f2&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;kubelet&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v1.32.11&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;k8s 버전 정책 문서 참고 - &lt;a href=&quot;https://v1-32.docs.kubernetes.io/releases/version-skew-policy/&quot;&gt;Docs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-819d-86ef-f7b5334b7a99&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;kubeadm&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v1.32.11&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;상동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-818c-8486-e742314f4867&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;kubectl&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v1.32.11&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;상동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-8143-be1c-c624acc7b942&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;helm&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v3.18.6&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;k8s 1.30.x ~ 1.33.x 지원 - &lt;a href=&quot;https://helm.sh/docs/v3/topics/version_skew/&quot;&gt;Docs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2ec50aec-5edf-8130-8833-ed0031154ebf&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;^VeG&quot; style=&quot;height: 22px;&quot;&gt;flannel cni&lt;/td&gt;
&lt;td id=&quot;Bg:\&quot; style=&quot;height: 22px;&quot;&gt;v0.27.3&lt;/td&gt;
&lt;td id=&quot;cwBE&quot; style=&quot;height: 22px;&quot;&gt;k8s 1.28~ 이후 - &lt;a href=&quot;https://github.com/flannel-io/flannel/releases&quot;&gt;Release&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;목표 구성도&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YsdlU/dJMcahQIK3u/4YUqaFNG4WLh4eSL8wCFY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YsdlU/dJMcahQIK3u/4YUqaFNG4WLh4eSL8wCFY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YsdlU/dJMcahQIK3u/4YUqaFNG4WLh4eSL8wCFY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYsdlU%2FdJMcahQIK3u%2F4YUqaFNG4WLh4eSL8wCFY0%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;819&quot; height=&quot;583&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;583&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;table style=&quot;border-collapse: collapse; width: 100%; height: 69px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 18px;&quot;&gt;노드 종류&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px;&quot;&gt;호스트명&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px;&quot;&gt;&amp;nbsp;IP 주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px;&quot;&gt;운영체제&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px;&quot;&gt;노드 수량&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;LoadBallacer&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;lb-pask&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;100.100.100.221&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;PNOS&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;Controle-node&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;k8s-ctr1 ~ ctr3&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;100.100.100.246 ~ 248&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;rocky-linux 9.6&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Worker-node&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;k8s-w1 ~ w2&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;100.100.100.249 ~ 250&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;rocky-linux 9.6&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 17px;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- LoadBallacer : PAS-KS(가상화 L4)&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- Master 노드 : k8s-ctr1 ~ ctr3&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;- Worker 노드 : k8s-w1 ~ w2&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt;&lt;b&gt;1. [공통] 사전 설정 작업&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인스턴스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- k8s 클러스터 구성을 위한 노드 베포(OpenStack&amp;nbsp; 인스턴스 생성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768880975870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;heat_template_version: &quot;2018-08-31&quot;

description: &amp;gt;
  Rocky Linux 9 Kubeadm HA Cluster (5-Node)
  Control Plane (3): m1.large / Worker (2): m1.medium
  UEFI 부팅 최적화 및 Fixed IP 설정

parameters:
  key_name:
    type: string
    default: &quot;cli-key&quot;
  flavor_ctrl:
    type: string
    default: &quot;m1.large&quot;
  flavor_worker:
    type: string
    default: &quot;m1.medium&quot;
  private_subnet:
    type: string
    default: &quot;cli-subnet&quot;
  public_net:
    type: string
    default: &quot;external-network&quot;

resources:
  # [공통 초기화 스크립트]
  common_script:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        runcmd:
          - ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
          - systemctl stop firewalld &amp;amp;&amp;amp; systemctl disable firewalld
          - setenforce 0
          - sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
          - dnf install -y sshpass jq git tree vim tar curl &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
          - echo 'root:qwe123' | chpasswd
          - sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
          - echo &quot;PermitRootLogin yes&quot; &amp;gt;&amp;gt; /etc/ssh/sshd_config
          - systemctl restart sshd
          - |
            cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
            100.100.100.246 k8s-ctr1
            100.100.100.247 k8s-ctr2
            100.100.100.248 k8s-ctr3
            100.100.100.249 k8s-w1
            100.100.100.250 k8s-w2
            EOF

  # [1] Control Plane Nodes (3대 - m1.large)
  k8s_ctr1:
    type: OS::Nova::Server
    properties:
      name: k8s-ctr1
      image: rocky9
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.246 }]
      user_data_format: RAW
      user_data: { get_resource: common_script }

  ctr1_fip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: { get_param: public_net }

  ctr1_fip_assoc:
    type: OS::Nova::FloatingIPAssociation
    properties:
      floating_ip: { get_resource: ctr1_fip }
      server_id: { get_resource: k8s_ctr1 }

  k8s_ctr2:
    type: OS::Nova::Server
    properties:
      name: k8s-ctr2
      image: rocky9
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.247 }]
      user_data: { get_resource: common_script }

  k8s_ctr3:
    type: OS::Nova::Server
    properties:
      name: k8s-ctr3
      image: rocky9
      flavor: { get_param: flavor_ctrl }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.248 }]
      user_data: { get_resource: common_script }

  # [2] Worker Nodes (2대 - m1.medium)
  k8s_w1:
    type: OS::Nova::Server
    properties:
      name: k8s-w1
      image: rocky9
      flavor: { get_param: flavor_worker }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.249 }]
      user_data: { get_resource: common_script }

  k8s_w2:
    type: OS::Nova::Server
    properties:
      name: k8s-w2
      image: rocky9
      flavor: { get_param: flavor_worker }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.250 }]
      user_data: { get_resource: common_script }&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;pre id=&quot;code_1768881079654&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 인스턴스 베포
openstack stack create -t k8s-cluster.yaml k8s-setup&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q7Cdx/dJMcagK3Cir/kAUVEHjZQ4IYs7SdkykniK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7Cdx/dJMcagK3Cir/kAUVEHjZQ4IYs7SdkykniK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7Cdx/dJMcagK3Cir/kAUVEHjZQ4IYs7SdkykniK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7Cdx%2FdJMcagK3Cir%2FkAUVEHjZQ4IYs7SdkykniK%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;1635&quot; height=&quot;553&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;노드별 기본 설정(공통)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 기본 정보 확인&lt;/p&gt;
&lt;pre id=&quot;code_1768881186590&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# User 정보
whoami
id                   # uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
pwd                  # /home/vagrant

# cpu, mem
lscpu
free -h

# Disk
lsblk                # sda      8:0    0   64G  0 disk
df -hT

# Network
ip -br -c -4 addr    # enp0s9           UP             192.168.10.100/24
ip -c route
ip addr

# Host Info, Kernel
hostnamectl          # Kernel: Linux 6.12.0-55.39.1.el10_0.aarch64
uname -r
rpm -aq | grep release
rocky-release-10.0-1.6.el10.noarch

# cgroup 버전 확인
stat -fc %T /sys/fs/cgroup
cgroup2fs

findmnt
mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memory_recursiveprot)

## systemd cgroup 계층 구조 확인
systemd-cgls --no-pager

# Process
pstree
lsns&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;root 권한(로그인 환경) 전환&lt;/h3&gt;
&lt;pre id=&quot;code_1768886940866&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo su -&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;/p&gt;
&lt;pre id=&quot;code_1768897496481&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo dnf update -y &amp;amp;&amp;amp; sudo dnf upgrade --refresh -y &amp;amp;&amp;amp; sudo reboot
sudo dnf clean all&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;p data-ke-size=&quot;size18&quot;&gt;Time, NTP 설정 : 인증서 만료 시간, 로그 타임스탬프 등 모든 노드에 동기화된 시간 필요&lt;/p&gt;
&lt;pre id=&quot;code_1768886990362&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# timedatectl 정보 확인
timedatectl status # RTC in local TZ: yes -&amp;gt; Warning:...
timedatectl set-local-rtc 0
timedatectl status

# 시스템 타임존(Timezone)을 한국(KST, UTC+9) 으로 설정 : 시스템 시간은 UTC 기준 유지, 표시만 KST로 변환
date
timedatectl set-timezone Asia/Seoul
date

# systemd가 시간 동기화 서비스(chronyd) 를 관리하도록 설정되어 있음 : ntpd 대신 chrony 사용 (Rocky 9/10 기본)
timedatectl status
timedatectl set-ntp true # System clock synchronized: yes -&amp;gt; NTP service: active

# chronyc 확인
# chrony가 어떤 NTP 서버들을 알고 있고, 그중 어떤 서버를 기준으로 시간을 맞추는지를 보여줍니다.
## Stratum 2: 매우 신뢰도 높은 서버
## Reach 377: 최근 8회 연속 응답 성공 (최대값)
chronyc sources -v
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* 211.108.117.211               2   6   377     9    -56us[  -31us] +/- 3253us
...

# 현재 시스템 시간이 얼마나 정확한지 종합 성적표
chronyc tracking
Reference ID    : D36C75D3 (211.108.117.211)
...&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;h3 data-ke-size=&quot;size23&quot;&gt;SELinux 설정, firewalld(방화벽) 끄기&lt;/h3&gt;
&lt;pre id=&quot;code_1768887281987&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# SELinux 설정 : Kubernetes는 Permissive 권장
getenforce
sestatus      # Current mode:                   enforcing
setenforce 0
getenforce
sestatus      # Current mode:                   permissive

# 재부팅 시에도 Permissive 적용
cat /etc/selinux/config | grep ^SELINUX
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
cat /etc/selinux/config | grep ^SELINUX


# firewalld(방화벽) 끄기
systemctl status firewalld
systemctl disable --now firewalld
systemctl status firewalld&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Kubernetes 설치 시 SELinux를 permissive 로 두는 이유는 컨테이너가 호스트 파일 시스템에 접근할 때 발생할 수 있는 권한 충돌을 방지하기 위함&lt;/blockquote&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;h3 data-ke-size=&quot;size23&quot;&gt;Swap 비활성화&lt;/h3&gt;
&lt;pre id=&quot;code_1768887531441&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Swap 비활성화
lsblk
free -h
free -h | grep Swap
swapoff -a
lsblk
free -h | grep Swap


# 재부팅 시에도 'Swap 비활성화' 적용되도록 /etc/fstab에서 swap 라인 삭제
cat /etc/fstab | grep swap
sed -i '/swap/d' /etc/fstab
cat /etc/fstab | grep swap&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;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; 커널 모듈 및 커널 파라미터 설정(네트워크 설정)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1768887781338&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 커널 모듈 확인
lsmod
lsmod | grep -iE 'overlay|br_netfilter'

# 커널 모듈 로드
modprobe overlay
modprobe br_netfilter
lsmod | grep -iE 'overlay|br_netfilter'

# 
cat &amp;lt;&amp;lt;EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
tree /etc/modules-load.d/

# 커널 파라미터 설정 : 네트워크 설정 - 브릿지 트래픽이 iptables를 거치도록 함
cat &amp;lt;&amp;lt;EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
tree /etc/sysctl.d/

# 설정 적용
sysctl --system

# 적용 확인
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.ipv4.ip_forward&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; hosts 설정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1768887922800&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# hosts 설정
cat /etc/hosts
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts

cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
100.100.100.246 k8s-ctr1
100.100.100.246 k8s-ctr2
100.100.100.247 k8s-ctr3
100.100.100.248 k8s-w1
100.100.100.249 k8s-w2
EOF
cat /etc/hosts

# 확인
ping -c 1 k8s-ctr1
ping -c 1 k8s-ctr2
ping -c 1 k8s-ctr3
ping -c 1 k8s-w1
ping -c 1 k8s-w2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;607&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzAjqo/dJMcagK3CO6/xgGHkZ9cLTgawKjLqm1sj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzAjqo/dJMcagK3CO6/xgGHkZ9cLTgawKjLqm1sj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzAjqo/dJMcagK3CO6/xgGHkZ9cLTgawKjLqm1sj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzAjqo%2FdJMcagK3CO6%2FxgGHkZ9cLTgawKjLqm1sj1%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;843&quot; height=&quot;607&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;607&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; 2. [공통] CRI 설치 : containerd(runc) v2.1.5&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;containerd는 CNCF(Cloud Native Computing Foundation)의 멤버이며 &lt;b data-index-in-node=&quot;58&quot; data-path-to-node=&quot;3&quot;&gt;'졸업(Graduated)'&lt;/b&gt; 단계에 있는 프로젝트입니다. 이는 &lt;b data-index-in-node=&quot;93&quot; data-path-to-node=&quot;3&quot;&gt;개방적이고 신뢰할 수 있는 컨테이너 런타임&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size18&quot;&gt;containerd는 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;4&quot;&gt;단순성, 견고함, 그리고 이식성&lt;/b&gt;에 중점을 둔 업계 표준 컨테이너 런타임입니다. 리눅스와 윈도우에서 데몬(daemon) 형태로 사용 가능하며, 호스트 시스템의 &lt;b data-index-in-node=&quot;100&quot; data-path-to-node=&quot;4&quot;&gt;전체 컨테이너 생명주기(lifecycle)를 관리&lt;/b&gt;할 수 있습니다: 이미지 전송 및 저장, 컨테이너 실행 및 감독, 저수준(low-level) 저장소 및 네트워크 연결 등이 여기에 포함됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;containerd는 개발자나 엔드 유저(일반 사용자)가 직접 사용하기보다는,&amp;nbsp; &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;5&quot;&gt;embedded 되도록 설계&lt;/b&gt;되었습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;1149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Osy9/dJMcaiPz7YL/FnnJ5v5Yrh4ldG85OmEYX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Osy9/dJMcaiPz7YL/FnnJ5v5Yrh4ldG85OmEYX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Osy9/dJMcaiPz7YL/FnnJ5v5Yrh4ldG85OmEYX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Osy9%2FdJMcaiPz7YL%2FFnnJ5v5Yrh4ldG85OmEYX1%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;2004&quot; height=&quot;1149&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;1149&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;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1fODF/dJMcabQuHIT/z7oLP7KqLQlbhKPNCrTWCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1fODF/dJMcabQuHIT/z7oLP7KqLQlbhKPNCrTWCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1fODF/dJMcabQuHIT/z7oLP7KqLQlbhKPNCrTWCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1fODF%2FdJMcabQuHIT%2Fz7oLP7KqLQlbhKPNCrTWCk%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;1080&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;1080&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;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ykdrf/dJMcacu6SXm/Zdtk9E1kpkzWLGWe0Vnyy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ykdrf/dJMcacu6SXm/Zdtk9E1kpkzWLGWe0Vnyy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ykdrf/dJMcacu6SXm/Zdtk9E1kpkzWLGWe0Vnyy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fykdrf%2FdJMcacu6SXm%2FZdtk9E1kpkzWLGWe0Vnyy1%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;2270&quot; height=&quot;476&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Containerd 와 K8S(1.32~1.34) 호환 버전 확인(2.1.0+, 2.0.1+, 1.7.24+, 1.6.36+&amp;hellip;) - &lt;a href=&quot;https://containerd.io/releases/#kubernetes-support&quot;&gt;Docs&lt;/a&gt; &amp;nbsp;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.4976%;&quot;&gt;Kubernetes Version&lt;/td&gt;
&lt;td style=&quot;width: 40.8212%;&quot;&gt;containerd Version&lt;/td&gt;
&lt;td style=&quot;width: 37.6812%;&quot;&gt;CRI Version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.4976%;&quot;&gt;&lt;b&gt;1.32&lt;/b&gt;*&lt;/td&gt;
&lt;td style=&quot;width: 40.8212%;&quot;&gt;&lt;b&gt;2.1.0+&lt;/b&gt;, 2.0.1+, 1.7.24+, 1.6.36+&lt;/td&gt;
&lt;td style=&quot;width: 37.6812%;&quot;&gt;v1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.4976%;&quot;&gt;&lt;b&gt;1.33&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8212%;&quot;&gt;&lt;b&gt;2.1.0+,&lt;/b&gt; 2.0.4+, 1.7.24+, 1.6.36+&lt;/td&gt;
&lt;td style=&quot;width: 37.6812%;&quot;&gt;v1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.4976%;&quot;&gt;&lt;b&gt;1.34&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8212%;&quot;&gt;&lt;b&gt;2.1.3+,&lt;/b&gt; 2.0.6+, 1.7.28+, 1.6.39+&lt;/td&gt;
&lt;td style=&quot;width: 37.6812%;&quot;&gt;v1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.4976%;&quot;&gt;1.35&lt;/td&gt;
&lt;td style=&quot;width: 40.8212%;&quot;&gt;2.2.0+, 2.1.5+, 1.7.28+&lt;/td&gt;
&lt;td style=&quot;width: 37.6812%;&quot;&gt;v1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;li&gt;Daemon Configuration : /etc/containerd/config.tomlConfiguration Version Minimum containerd version
&lt;table style=&quot;border-collapse: collapse; width: 52.657%; height: 152px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.2174%;&quot;&gt;Configuration Version&lt;/td&gt;
&lt;td style=&quot;width: 59.7826%;&quot;&gt;Minimum containerd version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.2174%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 59.7826%;&quot;&gt;v1.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.2174%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 59.7826%;&quot;&gt;v1.3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 40.2174%;&quot;&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 59.7826%;&quot;&gt;&lt;b&gt;v2.0.0&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; containerd(runc) 설치 v2.1.5 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/containerd/containerd/blob/main/docs/getting-started.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Getting-started&lt;/span&gt;&lt;/a&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFReIp/dJMcacu6S3z/Zm44lXTjza3J1AjEmpiPIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFReIp/dJMcacu6S3z/Zm44lXTjza3J1AjEmpiPIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFReIp/dJMcacu6S3z/Zm44lXTjza3J1AjEmpiPIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFReIp%2FdJMcacu6S3z%2FZm44lXTjza3J1AjEmpiPIK%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;2000&quot; height=&quot;1105&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1105&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;pre id=&quot;code_1768888675879&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# dnf == yum, 버전 정보 확인
dnf
yum
dnf --version
yum --version

# Docker 저장소 추가 : dockerd 설치 X, containerd 설치 OK
dnf repolist
tree /etc/yum.repos.d/
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf repolist
tree /etc/yum.repos.d/
cat /etc/yum.repos.d/docker-ce.repo
dnf makecache

# 설치 가능한 모든 containerd.io 버전 확인
dnf list --showduplicates containerd.io
Available Packages
containerd.io.x86_64                                                      1.7.22-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.23-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.24-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.25-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.26-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.27-3.1.el9                                                      docker-ce-stable
containerd.io.x86_64                                                      1.7.28-1.el9                                                        docker-ce-stable
containerd.io.x86_64                                                      1.7.28-2.el9                                                        docker-ce-stable
containerd.io.x86_64                                                      1.7.29-1.el9                                                        docker-ce-stable
containerd.io.x86_64                                                      2.1.5-1.el9                                                         docker-ce-stable
containerd.io.x86_64                                                      2.2.0-2.el9                                                         docker-ce-stable
containerd.io.x86_64                                                      2.2.1-1.el9                                                         docker-ce-stable
[
# containerd 설치
dnf install -y containerd.io-2.1.5-1.el9
Downloading Packages:
containerd.io-2.1.5-1.el9.aarch64.rpm

# 설치된 파일 확인
which runc &amp;amp;&amp;amp; runc --version
which containerd &amp;amp;&amp;amp; containerd --version
which containerd-shim-runc-v2 &amp;amp;&amp;amp; containerd-shim-runc-v2 -v
which ctr &amp;amp;&amp;amp; ctr --version
cat /etc/containerd/config.toml
tree /usr/lib/systemd/system | grep containerd
cat /usr/lib/systemd/system/containerd.service


# 기본 설정 생성 및 SystemdCgroup 활성화 (매우 중요)
containerd config default | tee /etc/containerd/config.toml
version = 3                    # containerd version 2.0 이상 시
root = '/var/lib/containerd'
state = '/run/containerd'
...

# https://v1-32.docs.kubernetes.io/ko/docs/setup/production-environment/container-runtimes/#cgroupfs-cgroup-driver
# cgroupfs 드라이버는 kubelet의 기본 cgroup 드라이버이다. 
# cgroupfs 드라이버가 사용될 때, kubelet과 컨테이너 런타임은 직접적으로 cgroup 파일시스템과 상호작용하여 cgroup들을 설정한다.
# cgroupfs 드라이버가 권장되지 않는 때가 있는데, systemd가 init 시스템인 경우이다. 
# 이것은 systemd가 시스템에 단 하나의 cgroup 관리자만 있을 것으로 기대하기 때문이다. 
# 또한, cgroup v2를 사용할 경우에도 cgroupfs 대신 systemd cgroup 드라이버를 사용한다.
# -----------------------------------------------------------
# https://github.com/containerd/containerd/blob/main/docs/cri/config.md
## In containerd 2.x
version = 3
[plugins.'io.containerd.cri.v1.images']
  snapshotter = &quot;overlayfs&quot;
## In containerd 1.x  
version = 2
[plugins.&quot;io.containerd.grpc.v1.cri&quot;.containerd]
  snapshotter = &quot;overlayfs&quot;
# -----------------------------------------------------------
cat /etc/containerd/config.toml | grep -i systemdcgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
cat /etc/containerd/config.toml | grep -i systemdcgroup

# systemd unit 파일 최신 상태 읽기
systemctl daemon-reload

# containerd start 와 enabled
systemctl enable --now containerd

# 
systemctl status containerd --no-pager
journalctl -u containerd.service --no-pager
pstree -alnp
systemd-cgls --no-pager

# containerd의 유닉스 도메인 소켓 확인 : kubelet에서 사용 , containerd client 3종(ctr, nerdctr, crictl)도 사용
containerd config dump | grep -n containerd.sock
ls -l /run/containerd/containerd.sock
ss -xl | grep containerd
ss -xnp | grep containerd

# 플러그인 확인
ctr --address /run/containerd/containerd.sock version
ctr plugins ls
TYPE                                      ID                       PLATFORMS         STATUS    
io.containerd.content.v1                  content                  -                 ok     # 이미지 레이어 저장
...
io.containerd.snapshotter.v1              native                   linux/arm64/v8    ok        
io.containerd.snapshotter.v1              overlayfs                linux/arm64/v8    ok     # Kubernetes 기본 snapshotter
io.containerd.snapshotter.v1              zfs                      linux/arm64/v8    skip      
...   
io.containerd.metadata.v1                 bolt                     -                 ok     # 메타데이터 DB (bolt)&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;3. [공통] kubeadm, kubelet 및 kubectl 설치 v1.32.11 - &lt;a style=&quot;color: #000000; background-color: #9feec3;&quot; href=&quot;https://v1-32.docs.kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot; data-token-index=&quot;5&quot;&gt;Docs&lt;/a&gt; &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1768889377371&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;kubeadm 설치하기&quot; data-og-description=&quot;이 페이지에서는 kubeadm 툴박스 설치 방법을 보여준다. 이 설치 프로세스를 수행한 후 kubeadm으로 클러스터를 만드는 방법에 대한 자세한 내용은 kubeadm으로 클러스터 생성하기 페이지를 참고한다.&quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://v1-32.docs.kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot; data-og-url=&quot;https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/h1dHL/dJMb8UHI4nM/1b85QKg0gbF2l7qIMewwM1/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373,https://scrap.kakaocdn.net/dn/nYmvM/dJMb9aKyZAk/jrgihUfsCVaEZX1r76OPM0/img.png?width=1292&amp;amp;height=1475&amp;amp;face=0_0_1292_1475&quot;&gt;&lt;a href=&quot;https://v1-32.docs.kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://v1-32.docs.kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/h1dHL/dJMb8UHI4nM/1b85QKg0gbF2l7qIMewwM1/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373,https://scrap.kakaocdn.net/dn/nYmvM/dJMb9aKyZAk/jrgihUfsCVaEZX1r76OPM0/img.png?width=1292&amp;amp;height=1475&amp;amp;face=0_0_1292_1475');&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;kubeadm 설치하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 페이지에서는 kubeadm 툴박스 설치 방법을 보여준다. 이 설치 프로세스를 수행한 후 kubeadm으로 클러스터를 만드는 방법에 대한 자세한 내용은 kubeadm으로 클러스터 생성하기 페이지를 참고한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MO4h9/dJMcahb6vq8/3S9VGqdGkLJiwcGKwEVHK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MO4h9/dJMcahb6vq8/3S9VGqdGkLJiwcGKwEVHK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MO4h9/dJMcahb6vq8/3S9VGqdGkLJiwcGKwEVHK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMO4h9%2FdJMcahb6vq8%2F3S9VGqdGkLJiwcGKwEVHK1%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;1693&quot; height=&quot;946&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;946&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768889443708&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# repo 추가
## exclude=... : 실수로 dnf update 시 kubelet 자동 업그레이드 방지
dnf repolist
tree /etc/yum.repos.d/
cat &amp;lt;&amp;lt;EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
dnf makecache

# 설치
## --disableexcludes=... kubernetes repo에 설정된 exclude 규칙을 이번 설치에서만 무시(1회성 옵션 처럼 사용)
## 설치 가능 버전 확인
dnf list --showduplicates kubelet # '--disableexcludes=kubernetes' 아래 처럼 있는 경우와 없는 경우 비교해보기
dnf list --showduplicates kubelet --disableexcludes=kubernetes
dnf list --showduplicates kubeadm --disableexcludes=kubernetes
dnf list --showduplicates kubectl --disableexcludes=kubernetes
...
kubectl.x86_64                                        1.32.10-150500.1.1                                        kubernetes
kubectl.aarch64                                       1.32.11-150500.1.1                                        kubernetes
...

## 버전 정보 미지정 시, 제공 가능 최신 버전 설치됨.
dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
Installing:
 kubeadm                       aarch64                1.32.11-150500.1.1                  kubernetes                 10 M
 kubectl                       aarch64                1.32.11-150500.1.1                  kubernetes                9.4 M
 kubelet                       aarch64                1.32.11-150500.1.1                  kubernetes                 13 M
Installing dependencies:
 cri-tools                     aarch64                1.32.0-150500.1.1                   kubernetes                6.2 M
 kubernetes-cni                aarch64                1.6.0-150500.1.1                    kubernetes                7.2 M

# kubelet 활성화 (실제 기동은 kubeadm init 후에 시작됨)
systemctl enable --now kubelet
ps -ef |grep kubelet

# 설치 파일들 확인
which kubeadm &amp;amp;&amp;amp; kubeadm version -o yaml

which kubectl &amp;amp;&amp;amp; kubectl version --client=true
Client Version: v1.32.11
Kustomize Version: v5.5.0

which kubelet &amp;amp;&amp;amp; kubelet --version
Kubernetes v1.32.11

# cri-tools
which crictl &amp;amp;&amp;amp; crictl version
WARN[0000] Config &quot;/etc/crictl.yaml&quot; does not exist, trying next: &quot;/usr/bin/crictl.yaml&quot; 

# /etc/crictl.yaml 파일 작성
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
EOF

crictl info | jq
{
  &quot;cniconfig&quot;: {
    &quot;Networks&quot;: [
      {
        &quot;Config&quot;: {
          &quot;CNIVersion&quot;: &quot;0.3.1&quot;,
          &quot;Name&quot;: &quot;cni-loopback&quot;,
          &quot;Plugins&quot;: [
            {
              &quot;Network&quot;: {
                &quot;ipam&quot;: {},
                &quot;type&quot;: &quot;loopback&quot;
              },
              &quot;Source&quot;: &quot;{\&quot;type\&quot;:\&quot;loopback\&quot;}&quot;
            }
          ],
          &quot;Source&quot;: &quot;{\n\&quot;cniVersion\&quot;: \&quot;0.3.1\&quot;,\n\&quot;name\&quot;: \&quot;cni-loopback\&quot;,\n\&quot;plugins\&quot;: [{\n  \&quot;type\&quot;: \&quot;loopback\&quot;\n}]\n}&quot;
        },
        &quot;IFName&quot;: &quot;lo&quot;
      }
    ],
    &quot;PluginConfDir&quot;: &quot;/etc/cni/net.d&quot;,
    &quot;PluginDirs&quot;: [
      &quot;/opt/cni/bin&quot;
    ],
    &quot;PluginMaxConfNum&quot;: 1,
    &quot;Prefix&quot;: &quot;eth&quot;
  },
  ...
    &quot;containerdEndpoint&quot;: &quot;/run/containerd/containerd.sock&quot;,
    &quot;containerdRootDir&quot;: &quot;/var/lib/containerd&quot;,
  ...
  &quot;status&quot;: {
      ...
      {
        &quot;message&quot;: &quot;Network plugin returns error: cni plugin not initialized&quot;,
        &quot;reason&quot;: &quot;NetworkPluginNotReady&quot;,
        &quot;status&quot;: false,
        &quot;type&quot;: &quot;NetworkReady&quot;
      },

# kubernetes-cni : 파드 네트워크 구성을 위한 CNI 바이너리 파일 확인
ls -al /opt/cni/bin
tree /opt/cni
/opt/cni
└── bin
    ├── bandwidth
    ├── bridge
    ├── portmap
    ...

tree /etc/cni/
/etc/cni/
└── net.d

#
systemctl is-active kubelet
systemctl status kubelet --no-pager
journalctl -u kubelet --no-pager
tree /usr/lib/systemd/system | grep kubelet -A1
├── kubelet.service
├── kubelet.service.d
│   └── 10-kubeadm.conf

cat /usr/lib/systemd/system/kubelet.service
cat /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
...

tree /etc/kubernetes
tree /var/lib/kubelet
cat /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=

# cgroup , namespace 정보 확인
systemd-cgls --no-pager
lsns

# containerd의 유닉스 도메인 소켓 확인 : kubelet에서 사용 , containerd client 3종(ctr, nerdctr, crictl)도 사용
ls -l /run/containerd/containerd.sock
ss -xl | grep containerd
ss -xnp | grep containerd&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;span style=&quot;background-color: #9feec3;&quot;&gt;4. [k8s-ctr] kubeadm 으로 k8s 클러스터 구성 &amp;amp; Flannel CNI 설치 v0.27.3 &amp;amp; 편의성 설정 등&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Customizing components with the kubeadm API - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://v1-32.docs.kubernetes.io/docs/setup/production-environment/tools/kubeadm/control-plane-flags/&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubeadm Configuration (v1beta4) - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://v1-32.docs.kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1768890012318&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;kubeadm Configuration (v1beta4)&quot; data-og-description=&quot;Overview Package v1beta4 defines the v1beta4 version of the kubeadm configuration file format. This version improves on the v1beta3 format by fixing some minor issues and adding a few new fields. A list of changes since v1beta3: v1.35: Add httpEndpoints fi&quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://v1-32.docs.kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/&quot; data-og-url=&quot;https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dI9vQV/dJMb9hCVdx3/uEKqPgaKNjWD9r18tBAktK/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373&quot;&gt;&lt;a href=&quot;https://v1-32.docs.kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://v1-32.docs.kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dI9vQV/dJMb9hCVdx3/uEKqPgaKNjWD9r18tBAktK/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373');&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;kubeadm Configuration (v1beta4)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Overview Package v1beta4 defines the v1beta4 version of the kubeadm configuration file format. This version improves on the v1beta3 format by fixing some minor issues and adding a few new fields. A list of changes since v1beta3: v1.35: Add httpEndpoints fi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768890025416&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
bootstrapTokens:
  - token: &quot;9a08jv.c0izixklcxtmnze7&quot;
    description: &quot;kubeadm bootstrap token&quot;
    ttl: &quot;24h&quot;
  - token: &quot;783bde.3f89s0fje9f38fhf&quot;
    description: &quot;another bootstrap token&quot;
    usages:
  - authentication
  - signing
    groups:
  - system:bootstrappers:kubeadm:default-node-token

nodeRegistration:
  name: &quot;ec2-10-100-0-1&quot;
  criSocket: &quot;unix:///var/run/containerd/containerd.sock&quot;
  taints:
    - key: &quot;kubeadmNode&quot;
      value: &quot;someValue&quot;
      effect: &quot;NoSchedule&quot;
  kubeletExtraArgs:
    - name: v
      value: &quot;5&quot;
  ignorePreflightErrors:
    - IsPrivilegedUser
  imagePullPolicy: &quot;IfNotPresent&quot;
  imagePullSerial: true

localAPIEndpoint:
  advertiseAddress: &quot;10.100.0.1&quot;
  bindPort: 6443
certificateKey: &quot;e6a2eb8581237ab72a4f494f30285ec12a9694d750b9785706a83bfcbbbd2204&quot;
skipPhases:
  - preflight
timeouts:
  controlPlaneComponentHealthCheck: &quot;60s&quot;
  kubenetesAPICall: &quot;40s&quot;
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
etcd:

  # one of local or external
  local:
    imageRepository: &quot;registry.k8s.io&quot;
    imageTag: &quot;3.2.24&quot;
    dataDir: &quot;/var/lib/etcd&quot;
    extraArgs:
      - name: listen-client-urls
        value: http://10.100.0.1:2379
    extraEnvs:
      - name: SOME_VAR
        value: SOME_VALUE
    serverCertSANs:
      - ec2-10-100-0-1.compute-1.amazonaws.com
    peerCertSANs:
      - 10.100.0.1
  # external:
  #   endpoints:
  #     - 10.100.0.1:2379
  #     - 10.100.0.2:2379
  #   caFile: &quot;/etcd/kubernetes/pki/etcd/etcd-ca.crt&quot;
  #   certFile: &quot;/etcd/kubernetes/pki/etcd/etcd.crt&quot;
  #   keyFile: &quot;/etcd/kubernetes/pki/etcd/etcd.key&quot;

networking:
  serviceSubnet: &quot;10.96.0.0/16&quot;
  podSubnet: &quot;10.244.0.0/24&quot;
  dnsDomain: &quot;cluster.local&quot;
kubernetesVersion: &quot;v1.21.0&quot;
controlPlaneEndpoint: &quot;10.100.0.1:6443&quot;
apiServer:
  extraArgs:
    - name: authorization-mode
      value: Node,RBAC
  extraEnvs:
    - name: SOME_VAR
      value: SOME_VALUE
  extraVolumes:
    - name: &quot;some-volume&quot;
      hostPath: &quot;/etc/some-path&quot;
      mountPath: &quot;/etc/some-pod-path&quot;
      readOnly: false
      pathType: File
  certSANs:
    - &quot;10.100.1.1&quot;
    - &quot;ec2-10-100-0-1.compute-1.amazonaws.com&quot;

controllerManager:
  extraArgs:
    - name: node-cidr-mask-size
      value: &quot;20&quot;
  extraVolumes:
    - name: &quot;some-volume&quot;
      hostPath: &quot;/etc/some-path&quot;
      mountPath: &quot;/etc/some-pod-path&quot;
      readOnly: false
      pathType: File

scheduler:
  extraArgs:
    - name: address
      value: 10.100.0.1
  extraVolumes:
    - name: &quot;some-volume&quot;
      hostPath: &quot;/etc/some-path&quot;
      mountPath: &quot;/etc/some-pod-path&quot;
      readOnly: false
      pathType: File

certificatesDir: &quot;/etc/kubernetes/pki&quot;
imageRepository: &quot;registry.k8s.io&quot;
clusterName: &quot;example-cluster&quot;
encryptionAlgorithm: ECDSA-P256
dns:
  disabled: true  # disable CoreDNS
proxy:
  disabled: true   # disable kube-proxy

---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# kubelet specific options here
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# kube-proxy specific options here&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;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] &lt;span data-token-index=&quot;1&quot;&gt;kubeadm init&lt;/span&gt; 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769295564186&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 환경 정보 출력 저장
crictl images
crictl ps
cat /etc/sysconfig/kubelet
tree /etc/kubernetes  | tee -a etc_kubernetes-1.txt
tree /var/lib/kubelet | tee -a var_lib_kubelet-1.txt
tree /run/containerd/ -L 3 | tee -a run_containerd-1.txt
pstree -alnp | tee -a pstree-1.txt
systemd-cgls --no-pager | tee -a systemd-cgls-1.txt
lsns | tee -a lsns-1.txt
ip addr | tee -a ip_addr-1.txt 
ss -tnlp | tee -a ss-1.txt
df -hT | tee -a df-1.txt
findmnt | tee -a findmnt-1.txt
sysctl -a | tee -a sysctl-1.txt

# kubeadm Configuration 파일 작성
cat &amp;lt;&amp;lt; EOF &amp;gt; kubeadm-init.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
bootstrapTokens:
- token: &quot;123456.1234567890123456&quot;
  ttl: &quot;0s&quot;
  usages:
  - signing
  - authentication
nodeRegistration:
  kubeletExtraArgs:
    - name: node-ip
      value: &quot;192.168.10.100&quot;  # 미설정 시 10.0.2.15 맵핑
  criSocket: &quot;unix:///run/containerd/containerd.sock&quot;
localAPIEndpoint:
  advertiseAddress: &quot;192.168.10.100&quot;
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: &quot;1.32.11&quot;
networking:
  podSubnet: &quot;10.244.0.0/16&quot;
  serviceSubnet: &quot;10.96.0.0/16&quot;
EOF
cat kubeadm-init.yaml

# (옵션) 컨테이너 이미지 미리 다운로드 : 특히 업그레이드 작업 시, 작업 시간 단축을 위해서 수행할 것
kubeadm config images pull

# k8s controlplane 초기화 설정 수행
kubeadm init --config=&quot;kubeadm-init.yaml&quot; --dry-run
tree /etc/kubernetes
kubeadm init --config=&quot;kubeadm-init.yaml&quot;
[init] Using Kubernetes version: v1.32.11
...
[certs] Using certificateDir folder &quot;/etc/kubernetes/pki&quot;
[certs] Generating &quot;ca&quot; certificate and key
[certs] Generating &quot;apiserver&quot; certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-ctr kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.10.100]
[certs] Generating &quot;apiserver-kubelet-client&quot; certificate and key
[certs] Generating &quot;front-proxy-ca&quot; certificate and key
[certs] Generating &quot;front-proxy-client&quot; certificate and key
[certs] Generating &quot;etcd/ca&quot; certificate and key
[certs] Generating &quot;etcd/server&quot; certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-ctr localhost] and IPs [192.168.10.100 127.0.0.1 ::1]
[certs] Generating &quot;etcd/peer&quot; certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-ctr localhost] and IPs [192.168.10.100 127.0.0.1 ::1]
[certs] Generating &quot;etcd/healthcheck-client&quot; certificate and key
[certs] Generating &quot;apiserver-etcd-client&quot; certificate and key
[certs] Generating &quot;sa&quot; key and public key
[kubeconfig] Using kubeconfig folder &quot;/etc/kubernetes&quot;
[kubeconfig] Writing &quot;admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;super-admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;kubelet.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;controller-manager.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;scheduler.conf&quot; kubeconfig file
[etcd] Creating static Pod manifest for local etcd in &quot;/etc/kubernetes/manifests&quot;
[control-plane] Using manifest folder &quot;/etc/kubernetes/manifests&quot;
[control-plane] Creating static Pod manifest for &quot;kube-apiserver&quot;
[control-plane] Creating static Pod manifest for &quot;kube-controller-manager&quot;
[control-plane] Creating static Pod manifest for &quot;kube-scheduler&quot;
[kubelet-start] Writing kubelet environment file with flags to file &quot;/var/lib/kubelet/kubeadm-flags.env&quot;
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/config.yaml&quot;
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory &quot;/etc/kubernetes/manifests&quot;
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 1.003793571s
[api-check] Waiting for a healthy API server. This can take up to 4m0s
[api-check] The API server is healthy after 3.004974627s
[upload-config] Storing the configuration used in ConfigMap &quot;kubeadm-config&quot; in the &quot;kube-system&quot; Namespace
[kubelet] Creating a ConfigMap &quot;kubelet-config&quot; in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-ctr as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-ctr as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: 123456.1234567890123456
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the &quot;cluster-info&quot; ConfigMap in the &quot;kube-public&quot; namespace
[kubelet-finalize] Updating &quot;/etc/kubernetes/kubelet.conf&quot; to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

# crictl 확인
crictl images
IMAGE                                     TAG                 IMAGE ID            SIZE
registry.k8s.io/coredns/coredns           v1.11.3             2f6c962e7b831       16.9MB
registry.k8s.io/etcd                      3.5.24-0            1211402d28f58       21.9MB
registry.k8s.io/kube-apiserver            v1.32.11            58951ea1a0b5d       26.4MB
registry.k8s.io/kube-controller-manager   v1.32.11            82766e5f2d560       24.2MB
registry.k8s.io/kube-proxy                v1.32.11            dcdb790dc2bfe       27.6MB
registry.k8s.io/kube-scheduler            v1.32.11            cfa17ff3d6634       19.2MB
registry.k8s.io/pause                     3.10                afb61768ce381       268kB

crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID              POD                               NAMESPACE
a04be00090580       dcdb790dc2bfe       26 seconds ago      Running             kube-proxy                0                   1fd91b0a982bb       kube-proxy-7w44b                  kube-system
b005f34739da5       82766e5f2d560       37 seconds ago      Running             kube-controller-manager   0                   555d146c3ec07       kube-controller-manager-k8s-ctr   kube-system
eb42b9c47fdce       cfa17ff3d6634       37 seconds ago      Running             kube-scheduler            0                   e649514d0a1b7       kube-scheduler-k8s-ctr            kube-system
bbe8495d2a205       58951ea1a0b5d       37 seconds ago      Running             kube-apiserver            0                   be25c00dd555c       kube-apiserver-k8s-ctr            kube-system
c00a944599500       1211402d28f58       37 seconds ago      Running             etcd                      0                   ce6b89dea28da       etcd-k8s-ctr                      kube-system

# kubeconfig 작성
mkdir -p /root/.kube
cp -i /etc/kubernetes/admin.conf /root/.kube/config
chown $(id -u):$(id -g) /root/.kube/config

# 확인
kubectl cluster-info

kubectl get node -owide
NAME      STATUS     ROLES           AGE     VERSION    INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION                  CONTAINER-RUNTIME
k8s-ctr   NotReady   control-plane   6m45s   v1.32.11   192.168.10.100   &amp;lt;none&amp;gt;        Rocky Linux 10.0 (Red Quartz)   6.12.0-55.39.1.el10_0.aarch64   containerd://2.1.5

kubectl get nodes -o json | jq &quot;.items[] | {name:.metadata.name} + .status.capacity&quot;
...

kubectl get pod -n kube-system -owide
NAME                              READY   STATUS    RESTARTS   AGE     IP               NODE      NOMINATED NODE   READINESS GATES
coredns-668d6bf9bc-bmdjw          0/1     Pending   0          6m55s   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;    &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
coredns-668d6bf9bc-cbtn9          0/1     Pending   0          6m55s   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;    &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
etcd-k8s-ctr                      1/1     Running   0          7m2s    192.168.10.100   k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-apiserver-k8s-ctr            1/1     Running   0          7m4s    192.168.10.100   k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-controller-manager-k8s-ctr   1/1     Running   0          7m2s    192.168.10.100   k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-proxy-zfr9d                  1/1     Running   0          6m55s   192.168.10.100   k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
kube-scheduler-k8s-ctr            1/1     Running   0          7m3s    192.168.10.100   k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;

# coredns 의 service name 확인 : kube-dns
kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   &amp;lt;none&amp;gt;        53/UDP,53/TCP,9153/TCP   3h26m

# cluster-info ConfigMap 공개 : cluster-info는 '신원 확인 전, 최소한의 신뢰 부트스트랩 데이터'
kubectl -n kube-public get configmap cluster-info
kubectl -n kube-public get configmap cluster-info -o yaml
kubectl -n kube-public get configmap cluster-info -o jsonpath='{.data.kubeconfig}' | grep certificate-authority-data | cut -d ':' -f2 | tr -d ' ' | base64 -d | openssl x509 -text -noout

curl -s -k https://192.168.10.100:6443/api/v1/namespaces/kube-public/configmaps/cluster-info | jq
curl -s -k https://192.168.10.100:6443/api/v1/namespaces/default/pods # X
kubectl -n kube-public get role
kubectl -n kube-public get rolebinding

# kubeadm init 시 생성되는 객체
- Namespace: kube-public
- ConfigMap: cluster-info
- Role + RoleBinding 
&amp;gt;&amp;gt; 대상: system:unauthenticated (인증 안 된 사용자)
&amp;gt;&amp;gt; 권한: get on configmaps/cluster-info
  아직 클러스터 인증서가 없는 노드(worker) 가 (kubeadm join 전) API Server에 처음 접속해서 최소 정보(엔드포인트 + CA)를 얻기 위해 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] k8s 관련 작업 &lt;span data-token-index=&quot;1&quot;&gt;편의성 설정&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769295611351&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc

# Source the completion
source &amp;lt;(kubectl completion bash)
source &amp;lt;(kubeadm completion bash)
echo 'source &amp;lt;(kubectl completion bash)' &amp;gt;&amp;gt; /etc/profile
echo 'source &amp;lt;(kubeadm completion bash)' &amp;gt;&amp;gt; /etc/profile
kubectl get &amp;lt;tab 2번&amp;gt;

# Alias kubectl to k
alias k=kubectl
complete -o default -F __start_kubectl k
echo 'alias k=kubectl' &amp;gt;&amp;gt; /etc/profile
echo 'complete -o default -F __start_kubectl k' &amp;gt;&amp;gt; /etc/profile
k get node

# kubecolor 설치 : https://kubecolor.github.io/setup/install/
dnf install -y 'dnf-command(config-manager)'
dnf config-manager --add-repo https://kubecolor.github.io/packages/rpm/kubecolor.repo
dnf repolist
dnf install -y kubecolor
kubecolor get node

alias kc=kubecolor
echo 'alias kc=kubecolor' &amp;gt;&amp;gt; /etc/profile
kc get node
kc describe node

# Install Kubectx &amp;amp; Kubens&quot;
dnf install -y git
git clone https://github.com/ahmetb/kubectx /opt/kubectx
ln -s /opt/kubectx/kubens /usr/local/bin/kubens
ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx


# Install Kubeps &amp;amp; Setting PS1
git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1
cat &amp;lt;&amp;lt; &quot;EOT&quot; &amp;gt;&amp;gt; /root/.bash_profile
source /root/kube-ps1/kube-ps1.sh
KUBE_PS1_SYMBOL_ENABLE=true
function get_cluster_short() {
  echo &quot;$1&quot; | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT

# 빠져나오기
exit
exit

# 다시 접속
vagrant ssh k8s-ctr
whoami
pwd
kubectl config rename-context &quot;kubernetes-admin@kubernetes&quot; &quot;HomeLab&quot;
kubens default

# helm 3 설치 : https://helm.sh/docs/intro/install
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | DESIRED_VERSION=v3.18.6 bash
helm version

# k9s 설치 : https://github.com/derailed/k9s
CLI_ARCH=amd64
if [ &quot;$(uname -m)&quot; = &quot;aarch64&quot; ]; then CLI_ARCH=arm64; fi
wget https://github.com/derailed/k9s/releases/latest/download/k9s_linux_${CLI_ARCH}.tar.gz
tar -xzf k9s_linux_*.tar.gz
ls -al k9s
chown root:root k9s
mv k9s /usr/local/bin/
chmod +x /usr/local/bin/k9s
k9s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] Flannel CNI 설치 &lt;span data-token-index=&quot;1&quot;&gt;v0.27.3&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769295636602&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 현재 k8s 클러스터에 파드 전체 CIDR 확인
kc describe pod -n kube-system kube-controller-manager-k8s-ctr
...
    Command:
      kube-controller-manager
      --allocate-node-cidrs=true
      --cluster-cidr=10.244.0.0/16
      --service-cluster-ip-range=10.96.0.0/16
      ...

# 노드별 파드 CIDR 확인 
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{&quot;\t&quot;}{.spec.podCIDR}{&quot;\n&quot;}{end}'
k8s-ctr 10.244.0.0/24


# Deploying Flannel with Helm
# https://github.com/flannel-io/flannel/blob/master/Documentation/configuration.md
helm repo add flannel https://flannel-io.github.io/flannel
helm repo update

kubectl create namespace kube-flannel
cat &amp;lt;&amp;lt; EOF &amp;gt; flannel.yaml
podCidr: &quot;10.244.0.0/16&quot;
flannel:
  cniBinDir: &quot;/opt/cni/bin&quot;
  cniConfDir: &quot;/etc/cni/net.d&quot;
  args:
  - &quot;--ip-masq&quot;
  - &quot;--kube-subnet-mgr&quot;
  - &quot;--iface=enp0s9&quot;  
  backend: &quot;vxlan&quot;
EOF

helm install flannel flannel/flannel --namespace kube-flannel --version 0.27.3 -f flannel.yaml

# 확인
helm list -A
helm get values -n kube-flannel flannel
kubectl get ds,pod,cm -n kube-flannel -owide
kc describe cm -n kube-flannel kube-flannel-cfg
kc describe ds -n kube-flannel
...
    Command:
      /opt/bin/flanneld
      --ip-masq
      --kube-subnet-mgr
      --iface=enp0s9

# flannel cni 바이너리 설치 확인
ls -l /opt/cni/bin/
-rwxr-xr-x. 1 root root 2974540 Jan 17 01:35 flannel
...

# cni 설정 정보 확인
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-flannel.conflist | jq

# cni 설치 후 아래 상태(conditions) 정상 확인
crictl info | jq
  &quot;status&quot;: {
    &quot;conditions&quot;: [
      {
        &quot;message&quot;: &quot;&quot;,
        &quot;reason&quot;: &quot;&quot;,
        &quot;status&quot;: true,
        &quot;type&quot;: &quot;RuntimeReady&quot;
      },
      {
        &quot;message&quot;: &quot;&quot;,
        &quot;reason&quot;: &quot;&quot;,
        &quot;status&quot;: true,
        &quot;type&quot;: &quot;NetworkReady&quot;
      },
      {
        &quot;message&quot;: &quot;&quot;,
        &quot;reason&quot;: &quot;&quot;,
        &quot;status&quot;: true,
        &quot;type&quot;: &quot;ContainerdHasNoDeprecationWarnings&quot;
      }
    ]
  }
}

# coredns 파드 정상 기동 확인
kubectl get pod -n kube-system -owide
NAME                              READY   STATUS    RESTARTS   AGE    IP               NODE      NOMINATED NODE   READINESS GATES
coredns-668d6bf9bc-bmdjw          1/1     Running   0          137m   10.244.0.3       k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
coredns-668d6bf9bc-cbtn9          1/1     Running   0          137m   10.244.0.2       k8s-ctr   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
...

# network 정보 확인
ip -c route | grep 10.244.
ip addr # cni0, flannel.1, vethY.. 확인
bridge link
lsns -t net

# iptables 규칙 확인
iptables -t nat -S
iptables -t filter -S
iptables-save&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] 노드 정보 확인, 기본 환경 정보 출력 비교, sysctl 변경 확인&lt;/p&gt;
&lt;pre id=&quot;code_1769295664972&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# # kubelet 활성화 확인 : 실제 기동은 kubeadm init 후에 시작됨
systemctl is-active kubelet
systemctl status kubelet --no-pager

# 노드 정보 확인 : 일반 워크로드가 Control Plane에 스케줄 X
kc describe node
Labels:             ...
                    node-role.kubernetes.io/control-plane=
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

# 기본 환경 정보 출력 저장
cat /etc/sysconfig/kubelet
tree /etc/kubernetes  | tee -a etc_kubernetes-2.txt
tree /var/lib/kubelet | tee -a var_lib_kubelet-2.txt
tree /run/containerd/ -L 3 | tee -a run_containerd-2.txt
pstree -alnp | tee -a pstree-2.txt
systemd-cgls --no-pager | tee -a systemd-cgls-2.txt
lsns | tee -a lsns-2.txt
ip addr | tee -a ip_addr-2.txt 
ss -tnlp | tee -a ss-2.txt
df -hT | tee -a df-2.txt
findmnt | tee -a findmnt-2.txt
sysctl -a | tee -a sysctl-2.txt

# 파일 출력 비교 : 빠져나오기 ':q' -&amp;gt; ':q' =&amp;gt; 변경된 부분이 어떤 동작과 역할인지 조사해보기!
vi -d etc_kubernetes-1.txt etc_kubernetes-2.txt
vi -d var_lib_kubelet-1.txt var_lib_kubelet-2.txt
vi -d run_containerd-1.txt run_containerd-2.txt
vi -d pstree-1.txt pstree-2.txt
vi -d systemd-cgls-1.txt systemd-cgls-2.txt
vi -d lsns-1.txt lsns-2.txt
vi -d ip_addr-1.txt ip_addr-2.txt
vi -d ss-1.txt ss-2.txt
vi -d df-1.txt df-2.txt
vi -d findmnt-1.txt findmnt-2.txt

# kubelet 에 --protect-kernel-defaults=false 적용되어 관련 코드에 sysctl 커널 파라미터 적용 : 아래 링크 확왼
## 위 설정 시, 커널 튜닝 가능 항목 중 하나라도 kubelet의 기본값과 다르면 오류가 발생합니다
vi -d sysctl-1.txt sysctl-2.txt
kernel.panic = 0 -&amp;gt; 10 변경
kernel.panic_on_oops = 1 기존값 그대로
vm.overcommit_memory = 0 -&amp;gt; 1 변경
vm.panic_on_oom = 0 기존값 그대로

sysctl kernel.keys.root_maxkeys  # 1000000 기존값 그대로
sysctl kernel.keys.root_maxbytes # 25000000 # root_maxkeys * 25 기존값 그대로


# kube-proxy 에서도 관련 코드에 sysctl 커널 파라미터 적용 : 아래 링크 확왼
net.nf_conntrack_max = 65536 -&amp;gt; 131072
net.netfilter.nf_conntrack_max = 65536 -&amp;gt; 131072
net.netfilter.nf_conntrack_count = 1 -&amp;gt; 282
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60 -&amp;gt; 3600
net.netfilter.nf_conntrack_tcp_timeout_established = 432000 -&amp;gt; 86400&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;[k8s-ctr] 인증서 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[certs]&amp;nbsp;Using&amp;nbsp;certificateDir&amp;nbsp;folder&amp;nbsp;&quot;/etc/kubernetes/pki&quot; &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;ca&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;apiserver&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;apiserver&amp;nbsp;serving&amp;nbsp;cert&amp;nbsp;is&amp;nbsp;signed&amp;nbsp;for&amp;nbsp;DNS&amp;nbsp;names&amp;nbsp;[k8s-ctr&amp;nbsp;kubernetes&amp;nbsp;kubernetes.default&amp;nbsp;kubernetes.default.svc&amp;nbsp;kubernetes.default.svc.cluster.local]&amp;nbsp;and&amp;nbsp;IPs&amp;nbsp;[10.96.0.1&amp;nbsp;192.168.10.100] &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;apiserver-kubelet-client&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;front-proxy-ca&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;front-proxy-client&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;etcd/ca&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;etcd/server&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;etcd/server&amp;nbsp;serving&amp;nbsp;cert&amp;nbsp;is&amp;nbsp;signed&amp;nbsp;for&amp;nbsp;DNS&amp;nbsp;names&amp;nbsp;[k8s-ctr&amp;nbsp;localhost]&amp;nbsp;and&amp;nbsp;IPs&amp;nbsp;[192.168.10.100&amp;nbsp;127.0.0.1&amp;nbsp;::1] &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;etcd/peer&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;etcd/peer&amp;nbsp;serving&amp;nbsp;cert&amp;nbsp;is&amp;nbsp;signed&amp;nbsp;for&amp;nbsp;DNS&amp;nbsp;names&amp;nbsp;[k8s-ctr&amp;nbsp;localhost]&amp;nbsp;and&amp;nbsp;IPs&amp;nbsp;[192.168.10.100&amp;nbsp;127.0.0.1&amp;nbsp;::1] &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;etcd/healthcheck-client&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;apiserver-etcd-client&quot;&amp;nbsp;certificate&amp;nbsp;and&amp;nbsp;key &lt;br /&gt;[certs]&amp;nbsp;Generating&amp;nbsp;&quot;sa&quot;&amp;nbsp;key&amp;nbsp;and&amp;nbsp;public&amp;nbsp;key &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kubeadm-config&amp;nbsp;확인 &lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;cm&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kubeadm-config &lt;br /&gt;... &lt;br /&gt;apiVersion:&amp;nbsp;kubead&lt;a href=&quot;http://m.k8s.io/v1beta4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://m.k8s.io/v1beta4&lt;/a&gt;&lt;br /&gt;caCertificateValidityPeriod:&amp;nbsp;87600h0m0s&amp;nbsp;#&amp;nbsp;CA인증서:&amp;nbsp;10년 &lt;br /&gt;certificateValidityPeriod:&amp;nbsp;8760h0m0s&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;인증서&amp;nbsp;&amp;nbsp;:&amp;nbsp;1년 &lt;br /&gt;certificatesDir:&amp;nbsp;/etc/kubernetes/pki&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;인증서&amp;nbsp;위치 &lt;br /&gt;etcd: &lt;br /&gt;&amp;nbsp;&amp;nbsp;local: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataDir:&amp;nbsp;/var/lib/etcd &lt;br /&gt;imageRepository:&amp;nbsp;registry.k8s.io &lt;br /&gt;... &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;Checks&amp;nbsp;expiration&amp;nbsp;for&amp;nbsp;the&amp;nbsp;certificates&amp;nbsp;in&amp;nbsp;the&amp;nbsp;local&amp;nbsp;PKI&amp;nbsp;managed&amp;nbsp;by&amp;nbsp;kubeadm. &lt;br /&gt;kubeadm&amp;nbsp;certs&amp;nbsp;check-expiration &lt;br /&gt;[check-expiration]&amp;nbsp;Reading&amp;nbsp;configuration&amp;nbsp;from&amp;nbsp;the&amp;nbsp;&quot;kubeadm-config&quot;&amp;nbsp;ConfigMap&amp;nbsp;in&amp;nbsp;namespace&amp;nbsp;&quot;kube-system&quot;... &lt;br /&gt;[check-expiration]&amp;nbsp;Use&amp;nbsp;'kubeadm&amp;nbsp;init&amp;nbsp;phase&amp;nbsp;upload-config&amp;nbsp;--config&amp;nbsp;your-config.yaml'&amp;nbsp;to&amp;nbsp;re-upload&amp;nbsp;it. &lt;br /&gt;&lt;br /&gt;CERTIFICATE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-etcd-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-kubelet-client&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;controller-manager.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-healthcheck-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-peer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-server&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;scheduler.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;super-admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;16,&amp;nbsp;2027&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;14,&amp;nbsp;2036&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;14,&amp;nbsp;2036&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;14,&amp;nbsp;2036&amp;nbsp;14:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;# &lt;br /&gt;tree&amp;nbsp;/etc/kubernetes/ &lt;br /&gt;tree&amp;nbsp;/etc/kubernetes/pki &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;CA&amp;nbsp;인증서 &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/pki/ca.crt&amp;nbsp;|&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-text&amp;nbsp;-noout &lt;br /&gt;... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Issuer:&amp;nbsp;CN=kubernetes &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Validity &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;Before:&amp;nbsp;Jan&amp;nbsp;16&amp;nbsp;14:28:05&amp;nbsp;2026&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;After&amp;nbsp;:&amp;nbsp;Jan&amp;nbsp;14&amp;nbsp;14:33:05&amp;nbsp;2036&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Subject:&amp;nbsp;CN=kubernetes &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;extensions: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Key&amp;nbsp;Usage:&amp;nbsp;critical &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Digital&amp;nbsp;Signature,&amp;nbsp;Key&amp;nbsp;Encipherment,&amp;nbsp;Certificate&amp;nbsp;Sign &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Basic&amp;nbsp;Constraints:&amp;nbsp;critical &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CA:TRUE &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;apiserver&amp;nbsp;인증서&amp;nbsp;:&amp;nbsp;'TLS&amp;nbsp;Web&amp;nbsp;Server'&amp;nbsp;키&amp;nbsp;용도&amp;nbsp;확인 &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/pki/apiserver.crt&amp;nbsp;|&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-text&amp;nbsp;-noout &lt;br /&gt;... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Issuer:&amp;nbsp;CN=kubernetes &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Validity &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;Before:&amp;nbsp;Jan&amp;nbsp;16&amp;nbsp;14:28:05&amp;nbsp;2026&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;After&amp;nbsp;:&amp;nbsp;Jan&amp;nbsp;16&amp;nbsp;14:33:05&amp;nbsp;2027&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Subject:&amp;nbsp;CN=kube-apiserver &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;extensions: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Key&amp;nbsp;Usage:&amp;nbsp;critical &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Digital&amp;nbsp;Signature,&amp;nbsp;Key&amp;nbsp;Encipherment &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Extended&amp;nbsp;Key&amp;nbsp;Usage:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TLS&amp;nbsp;Web&amp;nbsp;Server&amp;nbsp;Authentication &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Basic&amp;nbsp;Constraints:&amp;nbsp;critical &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CA:FALSE &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Authority&amp;nbsp;Key&amp;nbsp;Identifier:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;40:6A:34:8B:CD:FC:94:4C:19:69:E6:0F:07:E3:4E:7B:29:2F:26:C6 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Subject&amp;nbsp;Alternative&amp;nbsp;Name:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DNS:k8s-ctr,&amp;nbsp;DNS:kubernetes,&amp;nbsp;DNS:kubernetes.default,&amp;nbsp;DNS:kubernetes.default.svc,&amp;nbsp;DNS:kubernetes.default.svc.cluster.local,&amp;nbsp;IP&amp;nbsp;Address:10.96.0.1,&amp;nbsp;IP&amp;nbsp;Address:192.168.10.100 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;apiserver-kubelet-client&amp;nbsp;인증서&amp;nbsp;:&amp;nbsp;'TLS&amp;nbsp;Web&amp;nbsp;Client'&amp;nbsp;키&amp;nbsp;용도&amp;nbsp;확인 &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/pki/apiserver-kubelet-client.crt&amp;nbsp;|&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-text&amp;nbsp;-noout &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Subject:&amp;nbsp;O=kubeadm:cluster-admins,&amp;nbsp;CN=kube-apiserver-kubelet-client &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;... &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;X509v3&amp;nbsp;Extended&amp;nbsp;Key&amp;nbsp;Usage:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TLS&amp;nbsp;Web&amp;nbsp;Client&amp;nbsp;Authentication &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] kubeconfig 확인&lt;/p&gt;
&lt;pre id=&quot;code_1769295736820&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 관리자 용도
cat /etc/kubernetes/admin.conf 
cat /etc/kubernetes/super-admin.conf

# kcm
cat /etc/kubernetes/controller-manager.conf 

# scheduler
cat /etc/kubernetes/scheduler.conf 

# kubelet
cat /etc/kubernetes/kubelet.conf
...
users:
- name: system:node:k8s-ctr
  user:
    client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
    client-key: /var/lib/kubelet/pki/kubelet-client-current.pem

#
ls -l /var/lib/kubelet/pki
-rw-------. 1 root root 2822 Jan 17 09:34 kubelet-client-2026-01-17-09-34-41.pem
lrwxrwxrwx. 1 root root   59 Jan 17 09:34 kubelet-client-current.pem -&amp;gt; /var/lib/kubelet/pki/kubelet-client-2026-01-17-09-34-41.pem
-rw-r--r--. 1 root root 2262 Jan 17 09:34 kubelet.crt
-rw-------. 1 root root 1679 Jan 17 09:34 kubelet.key

# kubelet 서버 역할 : Subjec, Key Usage, SAN 확인
cat /var/lib/kubelet/pki/kubelet.crt | openssl x509 -text -noout
        Issuer: CN=k8s-ctr-ca@1768610081
        Validity
            Not Before: Jan 16 23:34:41 2026 GMT
            Not After : Jan 16 23:34:41 2027 GMT
        Subject: CN=k8s-ctr@1768610081
        ...
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier: 
                D5:FE:B1:E9:89:9F:46:A8:56:D4:4E:4B:01:7B:2B:49:44:FA:61:85
            X509v3 Subject Alternative Name: 
                DNS:k8s-ctr

# kubelet 클라이언트 역할 : Subjec, Key Usage 확인
cat /var/lib/kubelet/pki/kubelet-client-current.pem | openssl x509 -text -noout
        Issuer: CN=kubernetes
        Validity
            Not Before: Jan 17 00:28:48 2026 GMT
            Not After : Jan 17 00:33:48 2027 GMT
        Subject: O=system:nodes, CN=system:node:k8s-ctr
        ...
            X509v3 Extended Key Usage: 
                TLS Web Client Authentication&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;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] static pod 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;kubeket에&amp;nbsp;의해&amp;nbsp;기동되는&amp;nbsp;static&amp;nbsp;pod&amp;nbsp;대상&amp;nbsp;매니페스트&amp;nbsp;디렉터리&amp;nbsp;확인 &lt;br /&gt;tree&amp;nbsp;/etc/kubernetes/manifests/ &lt;br /&gt;/etc/kubernetes/manifests/ &lt;br /&gt;├──&amp;nbsp;etcd.yaml &lt;br /&gt;├──&amp;nbsp;kube-apiserver.yaml &lt;br /&gt;├──&amp;nbsp;kube-controller-manager.yaml &lt;br /&gt;└──&amp;nbsp;kube-scheduler.yaml &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kubelt&amp;nbsp;설정&amp;nbsp;확인 &lt;br /&gt;cat&amp;nbsp;/var/lib/kubelet/config.yaml &lt;br /&gt;authentication: &lt;br /&gt;&amp;nbsp;&amp;nbsp;anonymous: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;enabled:&amp;nbsp;false &lt;br /&gt;&amp;nbsp;&amp;nbsp;webhook: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cacheTTL:&amp;nbsp;0s &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;enabled:&amp;nbsp;true &lt;br /&gt;&amp;nbsp;&amp;nbsp;x509: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clientCAFile:&amp;nbsp;/etc/kubernetes/pki/ca.crt &lt;br /&gt;cgroupDriver:&amp;nbsp;systemd &lt;br /&gt;staticPodPath:&amp;nbsp;/etc/kubernetes/manifests &lt;br /&gt;... &lt;br /&gt;&lt;br /&gt;cat&amp;nbsp;/var/lib/kubelet/kubeadm-flags.env &lt;br /&gt;KUBELET_KUBEADM_ARGS=&quot;--container-runtime-endpoint=unix:///run/containerd/containerd.sock&amp;nbsp;--node-ip=192.168.10.100&amp;nbsp;--pod-infra-container-image=registry.k8s.io/pause:3.10&quot; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;static&amp;nbsp;파드&amp;nbsp;확인 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;pod&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;READY&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESTARTS&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NOMINATED&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;READINESS&amp;nbsp;GATES &lt;br /&gt;etcd-k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h9m&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;kube-apiserver-k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h9m&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;kube-controller-manager-k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h9m&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;kube-scheduler-k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h9m&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;... &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;etcd&amp;nbsp;:&amp;nbsp;etcd&amp;nbsp;client&amp;nbsp;는&amp;nbsp;https://192.168.10.100:2379&amp;nbsp;호출,&amp;nbsp;metrics&amp;nbsp;은&amp;nbsp;http://127.0.0.1:2381&amp;nbsp;확인 &lt;br /&gt;tree&amp;nbsp;/var/lib/etcd/ &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/manifests/etcd.yaml &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--advertise-client-urls=https://192.168.10.100:2379 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--listen-client-urls=https://127.0.0.1:2379,https://192.168.10.100:2379 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--listen-metrics-urls=http://127.0.0.1:2381 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;volumeMounts: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;mountPath:&amp;nbsp;/var/lib/etcd &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name:&amp;nbsp;etcd-data &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;mountPath:&amp;nbsp;/etc/kubernetes/pki/etcd &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name:&amp;nbsp;etcd-certs &lt;br /&gt;&amp;nbsp;&amp;nbsp;hostNetwork:&amp;nbsp;true &lt;br /&gt;&amp;nbsp;&amp;nbsp;priority:&amp;nbsp;2000001000 &lt;br /&gt;&amp;nbsp;&amp;nbsp;priorityClassName:&amp;nbsp;system-node-critical &lt;br /&gt;... &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kube-apiserver &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/manifests/kube-apiserver.yaml &lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;command: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;kube-apiserver &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Listen&amp;nbsp;https://&amp;lt;IP&amp;gt;:6443 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--advertise-address=192.168.10.100 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--secure-port=6443 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;etcd&amp;nbsp;client&amp;nbsp;-&amp;gt;&amp;nbsp;etcd&amp;nbsp;server(https://127.0.0.1:2379) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--etcd-servers=https://127.0.0.1:2379 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubelet-client&amp;nbsp;-&amp;gt;&amp;nbsp;kubelet &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;k8s&amp;nbsp;servicd&amp;nbsp;cid &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--service-cluster-ip-range=10.96.0.0/16 &lt;br /&gt;&lt;br /&gt;ss&amp;nbsp;-tnlp&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;apiserver &lt;br /&gt;LISTEN&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4096&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*:6443&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*:*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;users:((&quot;kube-apiserver&quot;,pid=6400,fd=3)) &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;k8s&amp;nbsp;내부에서&amp;nbsp;api&amp;nbsp;호출&amp;nbsp;시&amp;nbsp;:&amp;nbsp;https://10.96.0.1&amp;nbsp;혹은&amp;nbsp;&lt;a href=&quot;https://kubernetes.default.svc.cluster.local&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.default.svc.cluster.local&lt;/a&gt;&lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;svc,ep &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TYPE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLUSTER-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNAL-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;PORT(S)&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE &lt;br /&gt;service/kubernetes&amp;nbsp;&amp;nbsp;&amp;nbsp;ClusterIP&amp;nbsp;&amp;nbsp;&amp;nbsp;10.96.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;443/TCP&amp;nbsp;&amp;nbsp;&amp;nbsp;4h26m &lt;br /&gt;&lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ENDPOINTS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE &lt;br /&gt;endpoints/kubernetes&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100:6443&amp;nbsp;&amp;nbsp;&amp;nbsp;4h26m &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;scheduler &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/manifests/kube-scheduler.yaml &lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;command: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;kube-scheduler &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--authentication-kubeconfig=/etc/kubernetes/scheduler.conf &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--authorization-kubeconfig=/etc/kubernetes/scheduler.conf &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--bind-address=127.0.0.1 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--kubeconfig=/etc/kubernetes/scheduler.conf &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--leader-elect=true &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;tcp&amp;nbsp;10259&amp;nbsp;Listen&amp;nbsp;포트&amp;nbsp;확인 &lt;br /&gt;ss&amp;nbsp;-tnlp&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;scheduler &lt;br /&gt;LISTEN&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4096&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;127.0.0.1:10259&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0.0.0.0:*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;users:((&quot;kube-scheduler&quot;,pid=6397,fd=3))&amp;nbsp; &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;scheduler&amp;nbsp;파드가&amp;nbsp;1개&amp;nbsp;이상일&amp;nbsp;경우&amp;nbsp;리더&amp;nbsp;역할&amp;nbsp;파드&amp;nbsp;확인 &lt;br /&gt;##&amp;nbsp;Lease는&amp;nbsp;k8s의&amp;nbsp;경량&amp;nbsp;coordination&amp;nbsp;리소스&amp;nbsp;:&amp;nbsp;리더&amp;nbsp;선출&amp;nbsp;(Leader&amp;nbsp;Election),&amp;nbsp;노드/컴포넌트&amp;nbsp;상태&amp;nbsp;heartbeat,&amp;nbsp;저부하(high-scale)&amp;nbsp;상태&amp;nbsp;갱신 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;leases.coordination.k8s.io&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kube-scheduler&amp;nbsp;-o&amp;nbsp;yaml &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;leases.coordination.k8s.io&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kube-scheduler &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HOLDER&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE &lt;br /&gt;kube-scheduler&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr_7d815157-fdd5-4753-8a64-023d115d3704&amp;nbsp;&amp;nbsp;&amp;nbsp;4h31m &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;Node&amp;nbsp;Heartbeat&amp;nbsp;(Node&amp;nbsp;상태)&amp;nbsp;:&amp;nbsp;node&amp;nbsp;heartbeat&amp;nbsp;전용&amp;nbsp;네임스페이스 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;lease&amp;nbsp;-n&amp;nbsp;kube-node-lease &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;lease&amp;nbsp;-n&amp;nbsp;kube-node-lease&amp;nbsp;-o&amp;nbsp;yaml &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kube-controller-manager &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/manifests/kube-controller-manager.yaml &lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;command: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;kube-controller-manager &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kcm&amp;nbsp;bind&amp;nbsp;address &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--bind-address=127.0.0.1 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;노드별&amp;nbsp;파드&amp;nbsp;cidr&amp;nbsp;할당 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--allocate-node-cidrs=true &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--cluster-cidr=10.244.0.0/16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;k8s&amp;nbsp;svc&amp;nbsp;cidr &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--service-cluster-ip-range=10.96.0.0/16 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kcm&amp;nbsp;controller &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--controllers=*,bootstrapsigner,tokencleaner &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;lease&amp;nbsp;사용&amp;nbsp;:&amp;nbsp;리더 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--leader-elect=true &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;모든&amp;nbsp;컨트롤러가&amp;nbsp;kcm의&amp;nbsp;단일&amp;nbsp;권한(identity)&amp;nbsp;사용하지&amp;nbsp;않고,&amp;nbsp;컨트롤러별&amp;nbsp;개별&amp;nbsp;ServiceAccount&amp;nbsp;+&amp;nbsp;RBAC&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;--use-service-account-credentials=true &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;tcp&amp;nbsp;10257&amp;nbsp;Listen&amp;nbsp;포트&amp;nbsp;확인 &lt;br /&gt;ss&amp;nbsp;-tnlp&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;controller &lt;br /&gt;LISTEN&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4096&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;127.0.0.1:10257&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0.0.0.0:*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;users:((&quot;kube-controller&quot;,pid=6393,fd=3))&amp;nbsp; &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;노드별&amp;nbsp;파드&amp;nbsp;CIDR&amp;nbsp;확인&amp;nbsp; &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;nodes&amp;nbsp;-o&amp;nbsp;jsonpath='{range&amp;nbsp;.items[*]}{.metadata.name}{&quot;\t&quot;}{.spec.podCIDR}{&quot;\n&quot;}{end}' &lt;br /&gt;k8s-ctr&amp;nbsp;10.244.0.0/24 &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;kcm&amp;nbsp;파드가&amp;nbsp;1개&amp;nbsp;이상일&amp;nbsp;경우&amp;nbsp;리더&amp;nbsp;역할&amp;nbsp;파드&amp;nbsp;확인 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;lease&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kube-controller-manager&amp;nbsp;-o&amp;nbsp;yaml &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;lease&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kube-controller-manager &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;컨트롤러별&amp;nbsp;개별&amp;nbsp;ServiceAccount&amp;nbsp;+&amp;nbsp;RBAC&amp;nbsp;사용 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;sa&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;controller &lt;br /&gt;attachdetach-controller&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h44m &lt;br /&gt;certificate-controller&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h44m &lt;br /&gt;clusterrole-aggregation-controller&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h44m &lt;br /&gt;...(생략)... &lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;필수 애드온 설치 (coredns, kube-proxy) 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;coredns&amp;nbsp;확인 &lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;deploy&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;coredns &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;deploy&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;coredns&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;READY&amp;nbsp;&amp;nbsp;&amp;nbsp;UP-TO-DATE&amp;nbsp;&amp;nbsp;&amp;nbsp;AVAILABLE&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CONTAINERS&amp;nbsp;&amp;nbsp;&amp;nbsp;IMAGES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECTOR &lt;br /&gt;coredns&amp;nbsp;&amp;nbsp;&amp;nbsp;2/2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h46m&amp;nbsp;&amp;nbsp;&amp;nbsp;coredns&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registry.k8s.io/coredns/coredns:v1.11.3&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-app=kube-dns &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;label&amp;nbsp;도&amp;nbsp;아직&amp;nbsp;예전&amp;nbsp;kube-dns&amp;nbsp;사용 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;pod&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;-l&amp;nbsp;k8s-app=kube-dns&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;READY&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESTARTS&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NOMINATED&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;READINESS&amp;nbsp;GATES &lt;br /&gt;coredns-668d6bf9bc-cspcd&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h48m&amp;nbsp;&amp;nbsp;&amp;nbsp;10.244.0.2&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;coredns-668d6bf9bc-gh225&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h48m&amp;nbsp;&amp;nbsp;&amp;nbsp;10.244.0.3&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;&lt;br /&gt;## &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;svc,ep&amp;nbsp;-n&amp;nbsp;kube-system &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TYPE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLUSTER-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNAL-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;PORT(S)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE &lt;br /&gt;service/kube-dns&amp;nbsp;&amp;nbsp;&amp;nbsp;ClusterIP&amp;nbsp;&amp;nbsp;&amp;nbsp;10.96.0.10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;53/UDP,53/TCP,9153/TCP&amp;nbsp;&amp;nbsp;&amp;nbsp;4h50m &lt;br /&gt;&lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ENDPOINTS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE &lt;br /&gt;endpoints/kube-dns&amp;nbsp;&amp;nbsp;&amp;nbsp;10.244.0.2:53,10.244.0.3:53,10.244.0.2:53&amp;nbsp;+&amp;nbsp;3&amp;nbsp;more...&amp;nbsp;&amp;nbsp;&amp;nbsp;4h49m &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;프로메테우스&amp;nbsp;메트릭&amp;nbsp;엔드포인트&amp;nbsp;확인 &lt;br /&gt;curl&amp;nbsp;-s&amp;nbsp;http://10.96.0.10:9153/metrics&amp;nbsp;|&amp;nbsp;head &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;configmap&amp;nbsp;확인 &lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;cm&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;coredns &lt;br /&gt;.:53&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;errors &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;health&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lameduck&amp;nbsp;5s &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ready &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;kubernetes&amp;nbsp;cluster.local&amp;nbsp;in-addr.arpa&amp;nbsp;ip6.arpa&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pods&amp;nbsp;insecure &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fallthrough&amp;nbsp;in-addr.arpa&amp;nbsp;ip6.arpa &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ttl&amp;nbsp;30 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prometheus&amp;nbsp;:9153 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;forward&amp;nbsp;.&amp;nbsp;/etc/resolv.conf&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;max_concurrent&amp;nbsp;1000 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cache&amp;nbsp;30&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;disable&amp;nbsp;success&amp;nbsp;cluster.local &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;disable&amp;nbsp;denial&amp;nbsp;cluster.local &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;loop &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reload &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;loadbalance &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;forward&amp;nbsp;정보&amp;nbsp;확인 &lt;br /&gt;cat&amp;nbsp;/etc/resolv.conf&amp;nbsp; &lt;br /&gt;#&amp;nbsp;Generated&amp;nbsp;by&amp;nbsp;NetworkManager &lt;br /&gt;nameserver&amp;nbsp;168.126.63.1 &lt;br /&gt;nameserver&amp;nbsp;8.8.8.8 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kube-proxy&amp;nbsp;확인 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;ds&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DESIRED&amp;nbsp;&amp;nbsp;&amp;nbsp;CURRENT&amp;nbsp;&amp;nbsp;&amp;nbsp;READY&amp;nbsp;&amp;nbsp;&amp;nbsp;UP-TO-DATE&amp;nbsp;&amp;nbsp;&amp;nbsp;AVAILABLE&amp;nbsp;&amp;nbsp;&amp;nbsp;NODE&amp;nbsp;SELECTOR&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CONTAINERS&amp;nbsp;&amp;nbsp;&amp;nbsp;IMAGES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECTOR &lt;br /&gt;kube-proxy&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;kubernetes.io/os=linux&amp;nbsp;&amp;nbsp;&amp;nbsp;4h53m&amp;nbsp;&amp;nbsp;&amp;nbsp;kube-proxy&amp;nbsp;&amp;nbsp;&amp;nbsp;registry.k8s.io/kube-proxy:v1.32.11&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-app=kube-proxy &lt;br /&gt;&lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;pod&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;-l&amp;nbsp;k8s-app=kube-proxy &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;pod&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;-l&amp;nbsp;k8s-app=kube-proxy&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;READY&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESTARTS&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NOMINATED&amp;nbsp;NODE&amp;nbsp;&amp;nbsp;&amp;nbsp;READINESS&amp;nbsp;GATES &lt;br /&gt;kube-proxy-7w44b&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4h54m&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;&lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;cm&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kube-proxy &lt;br /&gt;bindAddress:&amp;nbsp;0.0.0.0 &lt;br /&gt;metricsBindAddress:&amp;nbsp;&quot;&quot; &lt;br /&gt;clusterCIDR:&amp;nbsp;10.244.0.0/16 &lt;br /&gt;conntrack: &lt;br /&gt;&amp;nbsp;&amp;nbsp;maxPerCore:&amp;nbsp;null &lt;br /&gt;&amp;nbsp;&amp;nbsp;min:&amp;nbsp;null &lt;br /&gt;mode:&amp;nbsp;&quot;&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;기본&amp;nbsp;모드&amp;nbsp;:&amp;nbsp;iptables &lt;br /&gt;nodePortAddresses:&amp;nbsp;null&amp;nbsp;&amp;nbsp;#&amp;nbsp;NodePort&amp;nbsp;서비스가&amp;nbsp;'모든&amp;nbsp;노드&amp;nbsp;인터페이스(IP)'에&amp;nbsp;바인딩됨 &lt;br /&gt;portRange:&amp;nbsp;&quot;&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kube-proxy&amp;nbsp;자체가&amp;nbsp;'포트&amp;nbsp;범위를&amp;nbsp;제한하지&amp;nbsp;않음' &lt;br /&gt;&lt;br /&gt;## &lt;br /&gt;ss&amp;nbsp;-tnlp&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;kube-proxy &lt;br /&gt;LISTEN&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4096&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;127.0.0.1:10249&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0.0.0.0:*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;users:((&quot;kube-proxy&quot;,pid=6631,fd=10))&amp;nbsp;#&amp;nbsp;헬스&amp;nbsp;체크&amp;nbsp;전용&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;LISTEN&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4096&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*:10256&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*:*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;users:((&quot;kube-proxy&quot;,pid=6631,fd=9))&amp;nbsp;&amp;nbsp;#&amp;nbsp;메트릭&amp;nbsp;노출용 &lt;br /&gt;&lt;br /&gt;curl&amp;nbsp;127.0.0.1:10249/healthz&amp;nbsp;;&amp;nbsp;echo &lt;br /&gt;ok &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;엔드포인트&amp;nbsp;정보&amp;nbsp;확인해보자 &lt;br /&gt;curl&amp;nbsp;http://127.0.0.1:10256/metrics &lt;br /&gt;curl&amp;nbsp;http://192.168.10.100:10256/metrics &lt;br /&gt;404&amp;nbsp;page&amp;nbsp;not&amp;nbsp;found &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;iptables&amp;nbsp;rule&amp;nbsp;확인 &lt;br /&gt;iptables&amp;nbsp;-t&amp;nbsp;nat&amp;nbsp;-S &lt;br /&gt;iptables&amp;nbsp;-t&amp;nbsp;filter&amp;nbsp;-S &lt;br /&gt;iptables-save &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;conntrack&amp;nbsp;전용&amp;nbsp;툴&amp;nbsp;설치 &lt;br /&gt;dnf&amp;nbsp;install&amp;nbsp;-y&amp;nbsp;conntrack-tools &lt;br /&gt;conntrack&amp;nbsp;-V &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;conntrack&amp;nbsp;툴&amp;nbsp;사용 &lt;br /&gt;conntrack&amp;nbsp;-L&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;전체&amp;nbsp;conntrack&amp;nbsp;엔트리&amp;nbsp;조회 &lt;br /&gt;conntrack&amp;nbsp;-L&amp;nbsp;-p&amp;nbsp;tcp&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;TCP&amp;nbsp;연결만&amp;nbsp;보기 &lt;br /&gt;conntrack&amp;nbsp;-L&amp;nbsp;-p&amp;nbsp;tcp&amp;nbsp;--state&amp;nbsp;ESTABLISHED&amp;nbsp;#&amp;nbsp;특정&amp;nbsp;상태&amp;nbsp;필터링 &lt;br /&gt;conntrack&amp;nbsp;-L&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;dport=443&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;특정&amp;nbsp;포트&amp;nbsp;관련&amp;nbsp;연결 &lt;br /&gt;conntrack&amp;nbsp;-E&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;실시간&amp;nbsp;이벤트&amp;nbsp;추적 &lt;br /&gt;&lt;br /&gt;##&amp;nbsp;conntrack&amp;nbsp;sysctl&amp;nbsp;주요&amp;nbsp;파라미터 &lt;br /&gt;##&amp;nbsp;nf_conntrack_max&amp;nbsp;:&amp;nbsp;최대&amp;nbsp;엔트리&amp;nbsp;수 &lt;br /&gt;##&amp;nbsp;nf_conntrack_count&amp;nbsp;:&amp;nbsp;현재&amp;nbsp;사용&amp;nbsp;중 &lt;br /&gt;##&amp;nbsp;nf_conntrack_tcp_timeout_established&amp;nbsp;:&amp;nbsp;TCP&amp;nbsp;유지&amp;nbsp;시간 &lt;br /&gt;sysctl -a | grep conntrack&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[k8s-w1/w2] 설정&lt;/h4&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;/p&gt;
&lt;pre id=&quot;code_1769296198421&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# root 권한(로그인 환경) 전환
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc
sudo su -

# Time, NTP 설정
timedatectl set-local-rtc 0

# 시스템 타임존(Timezone)을 한국(KST, UTC+9) 으로 설정 : 시스템 시간은 UTC 기준 유지, 표시만 KST로 변환
timedatectl set-timezone Asia/Seoul

# SELinux 설정 : Kubernetes는 Permissive 권장
setenforce 0

# 재부팅 시에도 Permissive 적용
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config

# firewalld(방화벽) 끄기
systemctl disable --now firewalld

# Swap 비활성화
swapoff -a

# 재부팅 시에도 'Swap 비활성화' 적용되도록 /etc/fstab에서 swap 라인 주석 처리
sed -i '/swap/d' /etc/fstab

# 커널 모듈 로드
modprobe overlay
modprobe br_netfilter
cat &amp;lt;&amp;lt;EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

# 커널 파라미터 설정 : 네트워크 설정 - 브릿지 트래픽이 iptables를 거치도록 함
cat &amp;lt;&amp;lt;EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 설정 적용
sysctl --system &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

# hosts 설정
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
192.168.10.100 k8s-ctr
192.168.10.101 k8s-w1
192.168.10.102 k8s-w2
EOF
cat /etc/hosts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;CRI 설치 : &lt;span data-token-index=&quot;1&quot;&gt;containerd&lt;/span&gt;(runc) &lt;span data-token-index=&quot;3&quot;&gt;v2.1.5&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296215046&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Docker 저장소 추가
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# containerd 설치
dnf install -y containerd.io-2.1.5-1.el10

# 기본 설정 생성 및 SystemdCgroup 활성화 (매우 중요)
containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

# systemd unit 파일 최신 상태 읽기
systemctl daemon-reload

# containerd start 와 enabled
systemctl enable --now containerd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;kubeadm, kubelet 및 kubectl&lt;/span&gt; 설치 &lt;span data-token-index=&quot;2&quot;&gt;v1.32.11&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296230440&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# repo 추가
cat &amp;lt;&amp;lt;EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.32/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF

# 설치
dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

# kubelet 활성화 (실제 기동은 kubeadm init 후에 시작됨)
systemctl enable --now kubelet

# /etc/crictl.yaml 파일 작성
cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;kubeadm 으로 &lt;span data-token-index=&quot;1&quot;&gt;k8s join*&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296248426&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 환경 정보 출력 저장
crictl images
crictl ps
cat /etc/sysconfig/kubelet
tree /etc/kubernetes  | tee -a etc_kubernetes-1.txt
tree /var/lib/kubelet | tee -a var_lib_kubelet-1.txt
tree /run/containerd/ -L 3 | tee -a run_containerd-1.txt
pstree -alnp | tee -a pstree-1.txt
systemd-cgls --no-pager | tee -a systemd-cgls-1.txt
lsns | tee -a lsns-1.txt
ip addr | tee -a ip_addr-1.txt 
ss -tnlp | tee -a ss-1.txt
df -hT | tee -a df-1.txt
findmnt | tee -a findmnt-1.txt
sysctl -a | tee -a sysctl-1.txt

# kubeadm Configuration 파일 작성
NODEIP=$(ip -4 addr show enp0s9 | grep -oP '(?&amp;lt;=inet\s)\d+(\.\d+){3}')
echo $NODEIP
cat &amp;lt;&amp;lt; EOF &amp;gt; kubeadm-join.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration
discovery:
  bootstrapToken:
    token: &quot;123456.1234567890123456&quot;
    apiServerEndpoint: &quot;192.168.10.100:6443&quot;
    unsafeSkipCAVerification: true
nodeRegistration:
  criSocket: &quot;unix:///run/containerd/containerd.sock&quot;
  kubeletExtraArgs:
    - name: node-ip
      value: &quot;$NODEIP&quot;
EOF
cat kubeadm-join.yaml

# join
kubeadm join --config=&quot;kubeadm-join.yaml&quot;
[preflight] Running pre-flight checks
[preflight] Reading configuration from the &quot;kubeadm-config&quot; ConfigMap in namespace &quot;kube-system&quot;...
[preflight] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it.
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/config.yaml&quot;
[kubelet-start] Writing kubelet environment file with flags to file &quot;/var/lib/kubelet/kubeadm-flags.env&quot;
[kubelet-start] Starting the kubelet
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 501.164948ms
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap

# crictl 확인
crictl images
crictl ps

# cluster-info cm 호출 가능 확인
curl -s -k https://192.168.10.100:6443/api/v1/namespaces/kube-public/configmaps/cluster-info | jq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-ctr] k8s-w1/w2 관련 정보 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;join&amp;nbsp;된&amp;nbsp;워커&amp;nbsp;노드&amp;nbsp;확인 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;node&amp;nbsp;-owide &lt;br /&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS&amp;nbsp;&amp;nbsp;&amp;nbsp;ROLES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VERSION&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INTERNAL-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNAL-IP&amp;nbsp;&amp;nbsp;&amp;nbsp;OS-IMAGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;KERNEL-VERSION&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CONTAINER-RUNTIME &lt;br /&gt;k8s-ctr&amp;nbsp;&amp;nbsp;&amp;nbsp;Ready&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;control-plane&amp;nbsp;&amp;nbsp;&amp;nbsp;6h58m&amp;nbsp;&amp;nbsp;&amp;nbsp;v1.32.11&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.100&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Rocky&amp;nbsp;Linux&amp;nbsp;10.0&amp;nbsp;(Red&amp;nbsp;Quartz)&amp;nbsp;&amp;nbsp;&amp;nbsp;6.12.0-55.39.1.el10_0.aarch64&amp;nbsp;&amp;nbsp;&amp;nbsp;containerd://2.1.5 &lt;br /&gt;k8s-w1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Ready&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2m29s&amp;nbsp;&amp;nbsp;&amp;nbsp;v1.32.11&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.101&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Rocky&amp;nbsp;Linux&amp;nbsp;10.0&amp;nbsp;(Red&amp;nbsp;Quartz)&amp;nbsp;&amp;nbsp;&amp;nbsp;6.12.0-55.39.1.el10_0.aarch64&amp;nbsp;&amp;nbsp;&amp;nbsp;containerd://2.1.5 &lt;br /&gt;k8s-w2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Ready&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2m29s&amp;nbsp;&amp;nbsp;&amp;nbsp;v1.32.11&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.102&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Rocky&amp;nbsp;Linux&amp;nbsp;10.0&amp;nbsp;(Red&amp;nbsp;Quartz)&amp;nbsp;&amp;nbsp;&amp;nbsp;6.12.0-55.39.1.el10_0.aarch64&amp;nbsp;&amp;nbsp;&amp;nbsp;containerd://2.1.5 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;노드별&amp;nbsp;파드&amp;nbsp;CIDR&amp;nbsp;확인&amp;nbsp; &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;nodes&amp;nbsp;-o&amp;nbsp;jsonpath='{range&amp;nbsp;.items[*]}{.metadata.name}{&quot;\t&quot;}{.spec.podCIDR}{&quot;\n&quot;}{end}' &lt;br /&gt;k8s-ctr 10.244.0.0/24 &lt;br /&gt;k8s-w1 10.244.1.0/24 &lt;br /&gt;k8s-w2 10.244.2.0/24 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;다른&amp;nbsp;노드의&amp;nbsp;파드&amp;nbsp;CIDR(Per&amp;nbsp;Node&amp;nbsp;Pod&amp;nbsp;CIDR)에&amp;nbsp;대한&amp;nbsp;라우팅이&amp;nbsp;자동으로&amp;nbsp;커널&amp;nbsp;라우팅에&amp;nbsp;추가됨을&amp;nbsp;확인&amp;nbsp;:&amp;nbsp;flannel.1&amp;nbsp;을&amp;nbsp;통해&amp;nbsp;VXLAN&amp;nbsp;통한&amp;nbsp;라우팅 &lt;br /&gt;ip&amp;nbsp;-c&amp;nbsp;route&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;flannel &lt;br /&gt;10.244.1.0/24&amp;nbsp;via&amp;nbsp;10.244.1.0&amp;nbsp;dev&amp;nbsp;flannel.1&amp;nbsp;onlink &lt;br /&gt;10.244.2.0/24&amp;nbsp;via&amp;nbsp;10.244.2.0&amp;nbsp;dev&amp;nbsp;flannel.1&amp;nbsp;onlink &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;k8s-ctr&amp;nbsp;에서&amp;nbsp;10.244.1.0&amp;nbsp;IP로&amp;nbsp;통신&amp;nbsp;가능(vxlan&amp;nbsp;overlay&amp;nbsp;사용)&amp;nbsp;확인 &lt;br /&gt;ping&amp;nbsp;-c&amp;nbsp;1&amp;nbsp;10.244.1.0 &lt;br /&gt;PING&amp;nbsp;10.244.1.0&amp;nbsp;(10.244.1.0)&amp;nbsp;56(84)&amp;nbsp;bytes&amp;nbsp;of&amp;nbsp;data. &lt;br /&gt;64&amp;nbsp;bytes&amp;nbsp;from&amp;nbsp;10.244.1.0:&amp;nbsp;icmp_seq=1&amp;nbsp;ttl=64&amp;nbsp;time=1.19&amp;nbsp;ms &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;워커&amp;nbsp;노드에&amp;nbsp;Taints&amp;nbsp;정보&amp;nbsp;확인 &lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;node&amp;nbsp;k8s-w1 &lt;br /&gt;Taints:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;k8s-w1&amp;nbsp;노드에&amp;nbsp;배치된&amp;nbsp;파드&amp;nbsp;확인 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;pod&amp;nbsp;-A&amp;nbsp;-owide&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;k8s-w2 &lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;pod&amp;nbsp;-A&amp;nbsp;-owide&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;k8s-w1 &lt;br /&gt;kube-flannel&amp;nbsp;&amp;nbsp;&amp;nbsp;kube-flannel-ds-nhfhh&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4m3s&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.101&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-w1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt; &lt;br /&gt;kube-system&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;kube-proxy-7zczb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Running&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4m3s&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;192.168.10.101&amp;nbsp;&amp;nbsp;&amp;nbsp;k8s-w1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;none&amp;gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[k8s-w1/w2 노드 정보 확인, 기본 환경 정보 출력 비교, sysctl 변경 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;#&amp;nbsp;kubelet&amp;nbsp;활성화&amp;nbsp;확인 &lt;br /&gt;systemctl&amp;nbsp;status&amp;nbsp;kubelet&amp;nbsp;--no-pager &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;기본&amp;nbsp;환경&amp;nbsp;정보&amp;nbsp;출력&amp;nbsp;저장 &lt;br /&gt;cat&amp;nbsp;/etc/sysconfig/kubelet &lt;br /&gt;tree&amp;nbsp;/etc/kubernetes&amp;nbsp;&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;etc_kubernetes-2.txt &lt;br /&gt;/etc/kubernetes &lt;br /&gt;├──&amp;nbsp;kubelet.conf &lt;br /&gt;├──&amp;nbsp;manifests &lt;br /&gt;└──&amp;nbsp;pki &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;ca.crt &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/kubelet.conf &lt;br /&gt;&lt;br /&gt;tree&amp;nbsp;/var/lib/kubelet&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;var_lib_kubelet-2.txt &lt;br /&gt;tree&amp;nbsp;/run/containerd/&amp;nbsp;-L&amp;nbsp;3&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;run_containerd-2.txt &lt;br /&gt;pstree&amp;nbsp;-alnp&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;pstree-2.txt &lt;br /&gt;systemd-cgls&amp;nbsp;--no-pager&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;systemd-cgls-2.txt &lt;br /&gt;lsns&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;lsns-2.txt &lt;br /&gt;ip&amp;nbsp;addr&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;ip_addr-2.txt&amp;nbsp; &lt;br /&gt;ss&amp;nbsp;-tnlp&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;ss-2.txt &lt;br /&gt;df&amp;nbsp;-hT&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;df-2.txt &lt;br /&gt;findmnt&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;findmnt-2.txt &lt;br /&gt;sysctl&amp;nbsp;-a&amp;nbsp;|&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;sysctl-2.txt &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;kubelet&amp;nbsp;에&amp;nbsp;--protect-kernel-defaults=false&amp;nbsp;적용되어&amp;nbsp;관련&amp;nbsp;코드에&amp;nbsp;sysctl&amp;nbsp;커널&amp;nbsp;파라미터&amp;nbsp;적용 &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;sysctl-1.txt&amp;nbsp;sysctl-2.txt &lt;br /&gt;kernel.panic&amp;nbsp;=&amp;nbsp;0&amp;nbsp;-&amp;gt;&amp;nbsp;10&amp;nbsp;변경 &lt;br /&gt;vm.overcommit_memory&amp;nbsp;=&amp;nbsp;0&amp;nbsp;-&amp;gt;&amp;nbsp;1&amp;nbsp;변경 &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;파일&amp;nbsp;출력&amp;nbsp;비교&amp;nbsp;:&amp;nbsp;빠져나오기&amp;nbsp;':q'&amp;nbsp;-&amp;gt;&amp;nbsp;':q'&amp;nbsp;=&amp;gt;&amp;nbsp;변경된&amp;nbsp;부분이&amp;nbsp;어떤&amp;nbsp;동작과&amp;nbsp;역할인지&amp;nbsp;조사해보기! &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;etc_kubernetes-1.txt&amp;nbsp;etc_kubernetes-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;var_lib_kubelet-1.txt&amp;nbsp;var_lib_kubelet-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;run_containerd-1.txt&amp;nbsp;run_containerd-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;pstree-1.txt&amp;nbsp;pstree-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;systemd-cgls-1.txt&amp;nbsp;systemd-cgls-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;lsns-1.txt&amp;nbsp;lsns-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;ip_addr-1.txt&amp;nbsp;ip_addr-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;ss-1.txt&amp;nbsp;ss-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;df-1.txt&amp;nbsp;df-2.txt &lt;br /&gt;vi&amp;nbsp;-d&amp;nbsp;findmnt-1.txt&amp;nbsp;findmnt-2.txt&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모니터링 툴 설치 : 프로메테우스-스택 설치 &amp;rarr; 인증서 익스포터 설치 &amp;rarr; 그라파나 대시보드 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;metrics-server 설치&lt;/p&gt;
&lt;pre id=&quot;code_1769296315761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system

# 확인
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;kube-prometheus-stack&lt;/span&gt; 설치&lt;/p&gt;
&lt;pre id=&quot;code_1769296328138&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; monitor-values.yaml
prometheus:
  prometheusSpec:
    scrapeInterval: &quot;20s&quot;
    evaluationInterval: &quot;20s&quot;
    externalLabels:
      cluster: &quot;myk8s-cluster&quot;
  service:
    type: NodePort
    nodePort: 30001

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator
  service:
    type: NodePort
    nodePort: 30002

alertmanager:
  enabled: true
defaultRules:
  create: true

kubeProxy:
  enabled: false
prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false
EOT
cat monitor-values.yaml

# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 80.13.3 \
-f monitor-values.yaml --create-namespace --namespace monitoring

# 확인
helm list -n monitoring
kubectl get pod,svc,ingress,pvc -n monitoring
kubectl get prometheus,servicemonitors,alertmanagers -n monitoring
kubectl get crd | grep monitoring

# 각각 웹 접속 실행 : NodePort 접속
open http://192.168.10.100:30001 # prometheus
open http://192.168.10.100:30002 # grafana : 접속 계정 admin / prom-operator

# 프로메테우스 버전 확인
kubectl exec -it sts/prometheus-kube-prometheus-stack-prometheus -n monitoring -c prometheus -- prometheus --version
prometheus, version 3.9.1

# 그라파나 버전 확인
kubectl exec -it -n monitoring deploy/kube-prometheus-stack-grafana -- grafana --version
grafana version 12.3.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;kube-controller-manager, etcd, kube-scheduler 메트릭 수집 설정&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296346300&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# kube-controller-manager bind-address 127.0.0.1 =&amp;gt; 0.0.0.0 변경
sed -i 's|--bind-address=127.0.0.1|--bind-address=0.0.0.0|g' /etc/kubernetes/manifests/kube-controller-manager.yaml
cat /etc/kubernetes/manifests/kube-controller-manager.yaml | grep bind-address
    - --bind-address=0.0.0.0

# kube-scheduler bind-address 127.0.0.1 =&amp;gt; 0.0.0.0 변경
sed -i 's|--bind-address=127.0.0.1|--bind-address=0.0.0.0|g' /etc/kubernetes/manifests/kube-scheduler.yaml
cat /etc/kubernetes/manifests/kube-scheduler.yaml | grep bind-address
    - --bind-address=0.0.0.0

# etcd metrics-url(http) 127.0.0.1 에 192.168.10.100 추가
sed -i 's|--listen-metrics-urls=http://127.0.0.1:2381|--listen-metrics-urls=http://127.0.0.1:2381,http://192.168.10.100:2381|g' /etc/kubernetes/manifests/etcd.yaml
cat /etc/kubernetes/manifests/etcd.yaml | grep listen-metrics-urls
    - --listen-metrics-urls=http://127.0.0.1:2381,http://192.168.10.100:2381&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;k8s 인증서 위치 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;Check&amp;nbsp;certificates&amp;nbsp;expiration&amp;nbsp;for&amp;nbsp;a&amp;nbsp;Kubernetes&amp;nbsp;cluster &lt;br /&gt;kubeadm&amp;nbsp;certs&amp;nbsp;check-expiration &lt;br /&gt;CERTIFICATE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-etcd-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-kubelet-client&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;controller-manager.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-healthcheck-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-peer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-server&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;scheduler.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;super-admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;# &lt;br /&gt;tree&amp;nbsp;/etc/kubernetes &lt;br /&gt;/etc/kubernetes &lt;br /&gt;|--&amp;nbsp;admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubeconfig&amp;nbsp;file &lt;br /&gt;|--&amp;nbsp;controller-manager.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubeconfig&amp;nbsp;file &lt;br /&gt;|--&amp;nbsp;kubelet.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubeconfig&amp;nbsp;file&amp;nbsp;-&amp;gt;&amp;nbsp;인증서/키&amp;nbsp;파일은&amp;nbsp;/var/lib/kubelet/pki/&amp;nbsp;에&amp;nbsp;위치 &lt;br /&gt;|--&amp;nbsp;manifests &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;etcd.yaml &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;kube-apiserver.yaml &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;kube-controller-manager.yaml &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;`--&amp;nbsp;kube-scheduler.yaml &lt;br /&gt;|--&amp;nbsp;pki&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;해당&amp;nbsp;디렉터리&amp;nbsp;아래에는&amp;nbsp;인증서와&amp;nbsp;키파일&amp;nbsp;위치&amp;nbsp;:&amp;nbsp;/etc/kubernetes/pki &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver-etcd-client.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver-etcd-client.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver-kubelet-client.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver-kubelet-client.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;apiserver.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;ca.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;ca.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;etcd&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;해당&amp;nbsp;디렉터리&amp;nbsp;아래에는&amp;nbsp;ETCD&amp;nbsp;관련&amp;nbsp;인증서와&amp;nbsp;키파일&amp;nbsp;위치&amp;nbsp;:&amp;nbsp;/etc/kubernetes/pki/etcd &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;ca.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;ca.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;healthcheck-client.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;healthcheck-client.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;peer.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;peer.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;server.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;`--&amp;nbsp;server.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;front-proxy-ca.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;front-proxy-ca.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;front-proxy-client.crt &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;front-proxy-client.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;|--&amp;nbsp;sa.key &lt;br /&gt;|&amp;nbsp;&amp;nbsp;&amp;nbsp;`--&amp;nbsp;sa.pub &lt;br /&gt;|--&amp;nbsp;scheduler.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubeconfig&amp;nbsp;file &lt;br /&gt;`--&amp;nbsp;super-admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;kubeconfig&amp;nbsp;file &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;위&amp;nbsp;kubelet.conf&amp;nbsp;에&amp;nbsp;대한&amp;nbsp;인증서/키&amp;nbsp;파일&amp;nbsp;위치&amp;nbsp;:&amp;nbsp;worker&amp;nbsp;노드&amp;nbsp;동일 &lt;br /&gt;tree&amp;nbsp;/var/lib/kubelet/pki/ &lt;br /&gt;/var/lib/kubelet/pki/ &lt;br /&gt;|--&amp;nbsp;kubelet-client-2026-01-11-05-06-09.pem &lt;br /&gt;|--&amp;nbsp;kubelet-client-current.pem&amp;nbsp;-&amp;gt;&amp;nbsp;/var/lib/kubelet/pki/kubelet-client-2026-01-11-05-06-09.pem &lt;br /&gt;|--&amp;nbsp;kubelet.crt &lt;br /&gt;`--&amp;nbsp;kubelet.key&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;x509 certificate exporter 설치&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296392433&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# w1/w2 에 node label 설정
kubectl label node k8s-w1 worker=&quot;true&quot; --overwrite
kubectl label node k8s-w2 worker=&quot;true&quot; --overwrite
kubectl get nodes -l worker=true

# values 파일 작성
cat &amp;lt;&amp;lt; EOF &amp;gt; cert-export-values.yaml
# -- hostPaths Exporter
hostPathsExporter:
  hostPathVolumeType: Directory

  daemonSets:
    cp:
      nodeSelector:
        node-role.kubernetes.io/control-plane: &quot;&quot;
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/control-plane
        operator: Exists
      watchFiles:
      - /var/lib/kubelet/pki/kubelet-client-current.pem
      - /var/lib/kubelet/pki/kubelet.crt
      - /etc/kubernetes/pki/apiserver.crt
      - /etc/kubernetes/pki/apiserver-etcd-client.crt
      - /etc/kubernetes/pki/apiserver-kubelet-client.crt
      - /etc/kubernetes/pki/ca.crt
      - /etc/kubernetes/pki/front-proxy-ca.crt
      - /etc/kubernetes/pki/front-proxy-client.crt
      - /etc/kubernetes/pki/etcd/ca.crt
      - /etc/kubernetes/pki/etcd/healthcheck-client.crt
      - /etc/kubernetes/pki/etcd/peer.crt
      - /etc/kubernetes/pki/etcd/server.crt
      watchKubeconfFiles:
      - /etc/kubernetes/admin.conf
      - /etc/kubernetes/controller-manager.conf
      - /etc/kubernetes/scheduler.conf

    nodes:
      nodeSelector:
        worker: &quot;true&quot;
      watchFiles:
      - /var/lib/kubelet/pki/kubelet-client-current.pem
      - /etc/kubernetes/pki/ca.crt

prometheusServiceMonitor:
  create: true
  scrapeInterval: 15s
  scrapeTimeout: 10s
  extraLabels:
    release: kube-prometheus-stack

prometheusRules:
  create: true
  warningDaysLeft: 28
  criticalDaysLeft: 14
  extraLabels:
    release: kube-prometheus-stack

grafana:
  createDashboard: true
secretsExporter:
  enabled: false
EOF

# helm chart 설치
helm repo add enix https://charts.enix.io
helm install x509-certificate-exporter enix/x509-certificate-exporter -n monitoring --values cert-export-values.yaml

# 설치 확인
helm list -n monitoring

## x509 대시보드 추가 : grafana sidecar 컨테이너가 configmap 확인 후 추가
kubectl get cm -n monitoring x509-certificate-exporter-dashboard
kubectl get cm -n monitoring x509-certificate-exporter-dashboard -o yaml


# 데몬셋 확인 : cp, nodes 각각
kubectl get ds -n monitoring -l app.kubernetes.io/instance=x509-certificate-exporter
NAME                              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                            AGE
x509-certificate-exporter-cp      1         1         1       1            1           node-role.kubernetes.io/control-plane=   19m
x509-certificate-exporter-nodes   1         1         1       1            1           kubernetes.io/hostname=myk8s-worker      19m

# 파드 정보 확인 : IP 확인
kubectl get pod -n monitoring -l app.kubernetes.io/instance=x509-certificate-exporter -owide
NAME                                    READY   STATUS    RESTARTS   AGE   IP            NODE                  NOMINATED NODE   READINESS GATES
x509-certificate-exporter-cp-fzwrl      1/1     Running   0          20m   10.244.0.5    myk8s-control-plane   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
x509-certificate-exporter-nodes-fckwl   1/1     Running   0          20m   10.244.1.12   myk8s-worker          &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;

# 프로메테우스 서비스모니터 수집을 위한 Service(ClusterIP) 정보 확인
kubectl get svc,ep -n monitoring x509-certificate-exporter
NAME                                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/x509-certificate-exporter   ClusterIP   10.96.6.160   &amp;lt;none&amp;gt;        9793/TCP   54s

NAME                                  ENDPOINTS                          AGE
endpoints/x509-certificate-exporter   10.244.0.5:9793,10.244.1.11:9793   54s

# 컨트롤플레인 노드에 배포된 'x509 익스포터' 파드에 메트릭 호출 확인
curl -s 10.244.0.5:9793/metrics | grep '^x509' | head -n 3
x509_cert_expired{filename=&quot;apiserver-etcd-client.crt&quot;,filepath=&quot;/etc/kubernetes/pki/apiserver-etcd-client.crt&quot;,issuer_CN=&quot;etcd-ca&quot;,serial_number=&quot;3639503804793909323&quot;,subject_CN=&quot;kube-apiserver-etcd-client&quot;} 0
x509_cert_expired{filename=&quot;apiserver.crt&quot;,filepath=&quot;/etc/kubernetes/pki/apiserver.crt&quot;,issuer_CN=&quot;kubernetes&quot;,serial_number=&quot;2465930159994320660&quot;,subject_CN=&quot;kube-apiserver&quot;} 0
x509_cert_expired{filename=&quot;ca.crt&quot;,filepath=&quot;/etc/kubernetes/pki/ca.crt&quot;,issuer_CN=&quot;kubernetes&quot;,serial_number=&quot;7083266840940024496&quot;,subject_CN=&quot;kubernetes&quot;} 0

# 워커 노드에 배포된 'x509 익스포터' 파드에 메트릭 호출 확인
curl -s 10.244.1.11:9793/metrics | grep '^x509' | head -n 3
x509_cert_expired{filename=&quot;ca.crt&quot;,filepath=&quot;/etc/kubernetes/pki/ca.crt&quot;,issuer_CN=&quot;kubernetes&quot;,serial_number=&quot;7083266840940024496&quot;,subject_CN=&quot;kubernetes&quot;} 0
x509_cert_expired{filename=&quot;kubelet-client-current.pem&quot;,filepath=&quot;/var/lib/kubelet/pki/kubelet-client-current.pem&quot;,issuer_CN=&quot;kubernetes&quot;,serial_number=&quot;137383486421320059000355181040497730044&quot;,subject_CN=&quot;system:node:myk8s-worker&quot;,subject_O=&quot;system:nodes&quot;} 0
x509_cert_not_after{filename=&quot;ca.crt&quot;,filepath=&quot;/etc/kubernetes/pki/ca.crt&quot;,issuer_CN=&quot;kubernetes&quot;,serial_number=&quot;7083266840940024496&quot;,subject_CN=&quot;kubernetes&quot;} 2.083467966e+09

# 프로메테우스 CR 정보 확인 : 서비스모니터와 룰 셀렉터 정보 확인
kubectl get prometheuses.monitoring.coreos.com -n monitoring -o yaml
...
    serviceMonitorSelector:
      matchLabels:
        release: kube-prometheus-stack
    ruleSelector:
      matchLabels:
        release: kube-prometheus-stack
...

# helm 배포 시, label 추가 해둠
kubectl edit servicemonitors -n monitoring x509-certificate-exporter
...
  labels:
    app.kubernetes.io/instance: x509-certificate-exporter
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: x509-certificate-exporter
    app.kubernetes.io/version: 3.19.1
    helm.sh/chart: x509-certificate-exporter-3.19.1
    release: kube-prometheus-stack
...

# helm 배포 시, label 추가 해둠
kubectl get prometheusrules.monitoring.coreos.com -n monitoring x509-certificate-exporter -o yaml | head -n 20
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  ...
  labels:
    app.kubernetes.io/instance: x509-certificate-exporter
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: x509-certificate-exporter
    app.kubernetes.io/version: 3.19.1
    helm.sh/chart: x509-certificate-exporter-3.19.1
    release: kube-prometheus-stack
  name: x509-certificate-exporter
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;프로메테우스 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1851&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YWcxF/dJMcafrRmtN/pLcuJ6llP78aw67dk91Ef0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YWcxF/dJMcafrRmtN/pLcuJ6llP78aw67dk91Ef0/img.png&quot; data-alt=&quot;서비스모니터에 의해 수집된 타겟 정보 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YWcxF/dJMcafrRmtN/pLcuJ6llP78aw67dk91Ef0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYWcxF%2FdJMcafrRmtN%2FpLcuJ6llP78aw67dk91Ef0%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;1851&quot; height=&quot;253&quot; data-origin-width=&quot;1851&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서비스모니터에 의해 수집된 타겟 정보 확인&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6EC6j/dJMcaaxkz6T/1MjFujZcNa5Vx94ukTGOnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6EC6j/dJMcaaxkz6T/1MjFujZcNa5Vx94ukTGOnK/img.png&quot; data-alt=&quot;Query 창에서 Explore 에서 x509 관련 메트릭 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6EC6j/dJMcaaxkz6T/1MjFujZcNa5Vx94ukTGOnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6EC6j%2FdJMcaaxkz6T%2F1MjFujZcNa5Vx94ukTGOnK%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;985&quot; height=&quot;329&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Query 창에서 Explore 에서 x509 관련 메트릭 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAkQLG/dJMcai28QHz/xyJkckSGYbr50wTVKAuxZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAkQLG/dJMcai28QHz/xyJkckSGYbr50wTVKAuxZ1/img.png&quot; data-alt=&quot;알람 설정 정보 확인 : 14일, 28일 잔여 시 각각 알람&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAkQLG/dJMcai28QHz/xyJkckSGYbr50wTVKAuxZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAkQLG%2FdJMcai28QHz%2FxyJkckSGYbr50wTVKAuxZ1%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;1852&quot; height=&quot;901&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;901&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;알람 설정 정보 확인 : 14일, 28일 잔여 시 각각 알람&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그라파나 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uRgAh/dJMcabiHRbV/SyjgngJXm2CocgWdPRDpP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uRgAh/dJMcabiHRbV/SyjgngJXm2CocgWdPRDpP0/img.png&quot; data-alt=&quot;Overview 정보(전체 요약 정보), Expiration(남은 시간 확인)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uRgAh/dJMcabiHRbV/SyjgngJXm2CocgWdPRDpP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuRgAh%2FdJMcabiHRbV%2FSyjgngJXm2CocgWdPRDpP0%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;1858&quot; height=&quot;942&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Overview 정보(전체 요약 정보), Expiration(남은 시간 확인)&lt;/figcaption&gt;
&lt;/figure&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;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH85yi/dJMb996fSkw/HrCAA6thYcKz7YKiIJhkD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH85yi/dJMb996fSkw/HrCAA6thYcKz7YKiIJhkD1/img.png&quot; data-alt=&quot;Charts : Top Issuers, Top Instances, Host Paths 에 유효 기간 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH85yi/dJMb996fSkw/HrCAA6thYcKz7YKiIJhkD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH85yi%2FdJMb996fSkw%2FHrCAA6thYcKz7YKiIJhkD1%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;1848&quot; height=&quot;765&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Charts : Top Issuers, Top Instances, Host Paths 에 유효 기간 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baJ7JS/dJMcagqLeRm/ZrVq3kM5eGwKXHtOO2B091/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baJ7JS/dJMcagqLeRm/ZrVq3kM5eGwKXHtOO2B091/img.png&quot; data-alt=&quot;Exporters : 인증서 정보 수집 동작 관련 메트릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baJ7JS/dJMcagqLeRm/ZrVq3kM5eGwKXHtOO2B091/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaJ7JS%2FdJMcagqLeRm%2FZrVq3kM5eGwKXHtOO2B091%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;1856&quot; height=&quot;798&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Exporters : 인증서 정보 수집 동작 관련 메트릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;샘플 애플리케이션 배포&lt;/h4&gt;
&lt;pre id=&quot;code_1769296543377&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 샘플 애플리케이션 배포
cat &amp;lt;&amp;lt; EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: &quot;kubernetes.io/hostname&quot;
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
EOF&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;샘플 애플리케이션 배포&lt;/p&gt;
&lt;pre id=&quot;code_1769296556282&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 배포 확인
kubectl get deploy,svc,ep webpod -owide

# webpod service clusterip 변수 지정
SVCIP=$(kubectl get svc webpod -o jsonpath='{.spec.clusterIP}')
echo $SVCIP

# 통신 확인
curl -s $SVCIP
curl -s $SVCIP | grep Hostname

# 반복 호출(신규 터미널)
while true; do curl -s $SVCIP | grep Hostname; sleep 1; done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;애플리케이션 확인 &amp;amp; &lt;span data-token-index=&quot;1&quot;&gt;반복 호출&lt;/span&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;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;kubeadm 인증서 갱신&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;현재 인증서 정보 확인&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;#&amp;nbsp;Check&amp;nbsp;certificates&amp;nbsp;expiration&amp;nbsp;for&amp;nbsp;a&amp;nbsp;Kubernetes&amp;nbsp;cluster &lt;br /&gt;kc&amp;nbsp;describe&amp;nbsp;cm&amp;nbsp;-n&amp;nbsp;kube-system&amp;nbsp;kubeadm-config&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;-i&amp;nbsp;cert &lt;br /&gt;caCertificateValidityPeriod:&amp;nbsp;87600h0m0s &lt;br /&gt;certificateValidityPeriod:&amp;nbsp;8760h0m0s &lt;br /&gt;certificatesDir:&amp;nbsp;/etc/kubernetes/pki &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;현재&amp;nbsp;인증서가&amp;nbsp;UTC&amp;nbsp;기준&amp;nbsp;1월&amp;nbsp;17일&amp;nbsp;00:34&amp;nbsp;분&amp;nbsp;생성되어서,&amp;nbsp;유효기간&amp;nbsp;365일(1년)&amp;nbsp;이후&amp;nbsp;만료일은&amp;nbsp;'27년&amp;nbsp;1월&amp;nbsp;17일&amp;nbsp;00:33분. &lt;br /&gt;kubeadm&amp;nbsp;certs&amp;nbsp;check-expiration&amp;nbsp;-v&amp;nbsp;6 &lt;br /&gt;[check-expiration]&amp;nbsp;Reading&amp;nbsp;configuration&amp;nbsp;from&amp;nbsp;the&amp;nbsp;&quot;kubeadm-config&quot;&amp;nbsp;ConfigMap&amp;nbsp;in&amp;nbsp;namespace&amp;nbsp;&quot;kube-system&quot;... &lt;br /&gt;... &lt;br /&gt;CERTIFICATE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-etcd-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;apiserver-kubelet-client&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;controller-manager.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-healthcheck-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-peer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-server&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-client&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;scheduler.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;super-admin.conf&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;17,&amp;nbsp;2027&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;364d&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;CERTIFICATE&amp;nbsp;AUTHORITY&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRES&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESIDUAL&amp;nbsp;TIME&amp;nbsp;&amp;nbsp;&amp;nbsp;EXTERNALLY&amp;nbsp;MANAGED &lt;br /&gt;ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etcd-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;front-proxy-ca&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jan&amp;nbsp;15,&amp;nbsp;2036&amp;nbsp;00:33&amp;nbsp;UTC&amp;nbsp;&amp;nbsp;&amp;nbsp;9y&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;no&amp;nbsp; &lt;br /&gt;&lt;br /&gt;tree&amp;nbsp;/etc/kubernetes/pki/ &lt;br /&gt;ls&amp;nbsp;-l&amp;nbsp;/etc/kubernetes/pki/etcd/ &lt;br /&gt;ls&amp;nbsp;-l&amp;nbsp;/etc/kubernetes/pki &lt;br /&gt;-rw-r--r--.&amp;nbsp;1&amp;nbsp;root&amp;nbsp;root&amp;nbsp;1281&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;09:34&amp;nbsp;apiserver.crt &lt;br /&gt;-rw-r--r--.&amp;nbsp;1&amp;nbsp;root&amp;nbsp;root&amp;nbsp;1123&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;09:34&amp;nbsp;apiserver-etcd-client.crt &lt;br /&gt;-rw-------.&amp;nbsp;1&amp;nbsp;root&amp;nbsp;root&amp;nbsp;1679&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;09:34&amp;nbsp;apiserver-etcd-client.key &lt;br /&gt;-rw-------.&amp;nbsp;1&amp;nbsp;root&amp;nbsp;root&amp;nbsp;1675&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;09:34&amp;nbsp;apiserver.key &lt;br /&gt;... &lt;br /&gt;&lt;br /&gt;#&amp;nbsp;apiserver&amp;nbsp;인증서(예시)&amp;nbsp;:&amp;nbsp; &lt;br /&gt;cat&amp;nbsp;/etc/kubernetes/pki/apiserver.crt&amp;nbsp;|&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-text&amp;nbsp;-noout &lt;br /&gt;Certificate: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Data: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Version:&amp;nbsp;3&amp;nbsp;(0x2) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Serial&amp;nbsp;Number:&amp;nbsp;9019049356910942135&amp;nbsp;(0x7d2a199aea6457b7) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Signature&amp;nbsp;Algorithm:&amp;nbsp;sha256WithRSAEncryption &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Issuer:&amp;nbsp;CN=kubernetes &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Validity &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;Before:&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;00:28:48&amp;nbsp;2026&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Not&amp;nbsp;After&amp;nbsp;:&amp;nbsp;Jan&amp;nbsp;17&amp;nbsp;00:33:48&amp;nbsp;2027&amp;nbsp;GMT &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Subject:&amp;nbsp;CN=kube-apiserver &lt;br /&gt;...&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;수동 인증서 갱신 소개&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kubeadm certs renew&lt;/b&gt; : CA는 유지한 채 control-plane 인증서를 재서명하며, kubeconfig 재생성됨. &amp;rarr; static pod 재기동이 반드시 필요!&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;k8s API 요청 : 수초~수십 초 중단&lt;/li&gt;
&lt;li&gt;kubectl : 일시적 실패&lt;/li&gt;
&lt;li&gt;워크로드 : 영향 없음&lt;/li&gt;
&lt;li&gt;etcd 영향
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;single-node etcd : etcd 재기동 &amp;rarr; api server 잠시 불가&lt;/li&gt;
&lt;li&gt;multi-node etcd : 순차 갱신 필요(동시 수행 시, 쿼럼 붕괴 위험)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;worker node 영향
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubelet 인증서는 자동 갱신 : kubelet client cert
&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;에는 kubelet.conf 파일이 포함되어 있지 않습니다.&lt;/li&gt;
&lt;li&gt;kubeadm은 순환 가능한 인증서를 사용하여 &lt;b&gt;kubelet의 자동 인증서 갱신을 구성&lt;/b&gt;하기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kubeadm rejoin 필요 없음&lt;/li&gt;
&lt;li&gt;워크로드 영향 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HA 컨트롤 플레인&lt;/b&gt;을 사용하는 클러스터를 실행하는 경우, 이 명령어는 &lt;b&gt;모든 컨트롤 플레인 노드에서 실행&lt;/b&gt;해야 합니다.&lt;/li&gt;
&lt;li&gt;이 명령은 에 저장된 CA 인증서와 키를 사용하여 갱신을 수행합니다 &amp;rArr; &lt;b&gt;CA(cert/key, 기본 10년 유효 기간)는 재생성하지 않음.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;/etc/kubernetes/pki/ ├── **ca.crt / ca.key** (❌ renew 대상 아님) ├── **etcd/** │ ├── **ca.crt / ca.key** (❌ renew 대상 아님)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;명령어를 실행한 후&lt;/b&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; 이 과정이 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/&quot;&gt;정적 Pod는&lt;/a&gt;&amp;nbsp;API 서버가 아닌 로컬 kubelet에 의해 관리되므로 kubectl 명령어를 사용하여 삭제 및 재시작할 수 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정적 Pod를 재시작&lt;/b&gt;하려면 매니페스트 파일을 해당 /etc/kubernetes/manifests/ 디렉터리에서 &lt;b&gt;일시적으로 제거하고 20초&lt;/b&gt; 동안 기다립니다(&amp;nbsp;&lt;a href=&quot;https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/&quot;&gt;KubeletConfiguration 구조체&lt;/a&gt;fileCheckFrequency&amp;nbsp;의 값 참조).&lt;/li&gt;
&lt;li&gt;Pod가 매니페스트 디렉터리에 없으면 &lt;b&gt;kubelet이 Pod를 종료&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;그런 다음 파일을 원래 위치로 되돌리고 일정&amp;nbsp;시간이 지나면 &lt;b&gt;kubelet이 Pod를 다시 생성&lt;/b&gt;하고 &lt;b&gt;구성 요소의 인증서 갱신이 완료&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;**touch /etc/kubernetes/manifests/*.yaml
watch -d crictl ps**
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;kubeadm certs renew 특정 인증서를 갱신하거나, all 명령을 사용하여&amp;nbsp;모든 인증서를 한 번에 갱신할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1769296664022&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 갱신 시 : 기존 cert 삭제 -&amp;gt; CA로 재서명된 새 cert 생성 
kubeadm certs renew apiserver 
kubeadm certs renew etcd-server 
kubeadm certs renew all&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;수동 인증서 갱신 실행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769296700130&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 샘플 애플리케이션 반복 호출(신규 터미널)
SVCIP=$(kubectl get svc webpod -o jsonpath='{.spec.clusterIP}')
while true; do curl -s $SVCIP | grep Hostname; sleep 1; done


# 사전 백업 : HA Controlplane 모두
cp -r /etc/kubernetes/pki /etc/kubernetes/pki.backup.$(date +%F)
ls -l /etc/kubernetes/pki.backup.$(date +%F)

mkdir /etc/kubernetes/backup-conf.$(date +%F)
cp /etc/kubernetes/*.conf /etc/kubernetes/backup-conf.$(date +%F)
ls -l /etc/kubernetes/backup-conf.$(date +%F)


# 인증서 만료 상태 확인
kubeadm certs check-expiration


# 인증서 전체 갱신 : 기존 cert 삭제 -&amp;gt; CA로 재서명된 새 cert 생성
kubeadm certs renew all
certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed
certificate for serving the Kubernetes API renewed
certificate the apiserver uses to access etcd renewed
certificate for the API server to connect to kubelet renewed
certificate embedded in the kubeconfig file for the controller manager to use renewed
certificate for liveness probes to healthcheck etcd renewed
certificate for etcd nodes to communicate with each other renewed
certificate for serving etcd renewed
certificate for the front proxy client renewed
certificate embedded in the kubeconfig file for the scheduler manager to use renewed
certificate embedded in the kubeconfig file for the super-admin renewed

Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.


# 인증서 만료 상태 확인
kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Jan 17, 2027 12:25 UTC   364d            ca                      no      
apiserver                  Jan 17, 2027 12:25 UTC   364d            ca                      no      
apiserver-etcd-client      Jan 17, 2027 12:25 UTC   364d            etcd-ca                 no      
apiserver-kubelet-client   Jan 17, 2027 12:25 UTC   364d            ca                      no      
controller-manager.conf    Jan 17, 2027 12:25 UTC   364d            ca                      no      
etcd-healthcheck-client    Jan 17, 2027 12:25 UTC   364d            etcd-ca                 no      
etcd-peer                  Jan 17, 2027 12:25 UTC   364d            etcd-ca                 no      
etcd-server                Jan 17, 2027 12:25 UTC   364d            etcd-ca                 no      
front-proxy-client         Jan 17, 2027 12:25 UTC   364d            front-proxy-ca          no      
scheduler.conf             Jan 17, 2027 12:25 UTC   364d            ca                      no      
super-admin.conf           Jan 17, 2027 12:25 UTC   364d            ca                      no      

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Jan 15, 2036 00:33 UTC   9y              no      
etcd-ca                 Jan 15, 2036 00:33 UTC   9y              no      
front-proxy-ca          Jan 15, 2036 00:33 UTC   9y              no 

# ca 인증서는 그대로, 나머지 인증서는 신규 생성
ls -lt /etc/kubernetes/pki/
-rw-r--r--. 1 root root 1119 Jan 17 21:25 front-proxy-client.crt
-rw-------. 1 root root 1675 Jan 17 21:25 front-proxy-client.key
-rw-r--r--. 1 root root 1176 Jan 17 21:25 apiserver-kubelet-client.crt
-rw-------. 1 root root 1675 Jan 17 21:25 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1123 Jan 17 21:25 apiserver-etcd-client.crt
-rw-------. 1 root root 1675 Jan 17 21:25 apiserver-etcd-client.key
-rw-r--r--. 1 root root 1281 Jan 17 21:25 apiserver.crt
-rw-------. 1 root root 1675 Jan 17 21:25 apiserver.key
-rw-------. 1 root root 1675 Jan 17 09:34 sa.key
-rw-------. 1 root root  451 Jan 17 09:34 sa.pub
drwxr-xr-x. 2 root root  162 Jan 17 09:34 etcd
-rw-r--r--. 1 root root 1123 Jan 17 09:34 front-proxy-ca.crt
-rw-------. 1 root root 1675 Jan 17 09:34 front-proxy-ca.key
-rw-r--r--. 1 root root 1107 Jan 17 09:34 ca.crt
-rw-------. 1 root root 1679 Jan 17 09:34 ca.key

ls -lt /etc/kubernetes/pki/etcd
-rw-r--r--. 1 root root 1196 Jan 17 21:25 server.crt
-rw-------. 1 root root 1675 Jan 17 21:25 server.key
-rw-r--r--. 1 root root 1196 Jan 17 21:25 peer.crt
-rw-------. 1 root root 1679 Jan 17 21:25 peer.key
-rw-r--r--. 1 root root 1123 Jan 17 21:25 healthcheck-client.crt
-rw-------. 1 root root 1679 Jan 17 21:25 healthcheck-client.key
-rw-r--r--. 1 root root 1094 Jan 17 09:34 ca.crt
-rw-------. 1 root root 1675 Jan 17 09:34 ca.key

# apiserver 인증서
cat /etc/kubernetes/pki/apiserver.crt | openssl x509 -text -noout
        Issuer: CN=kubernetes
        Validity
            Not Before: Jan 17 12:20:10 2026 GMT
            Not After : Jan 17 12:25:10 2027 GMT
        Subject: CN=kube-apiserver

# control component 의 kubeconfig 신규 생성 확인
ls -lt /etc/kubernetes/*.conf
-rw-------. 1 root root 5682 Jan 17 21:25 /etc/kubernetes/super-admin.conf
-rw-------. 1 root root 5626 Jan 17 21:25 /etc/kubernetes/scheduler.conf
-rw-------. 1 root root 5682 Jan 17 21:25 /etc/kubernetes/controller-manager.conf
-rw-------. 1 root root 5654 Jan 17 21:25 /etc/kubernetes/admin.conf
-rw-------. 1 root root 1974 Jan 17 09:34 /etc/kubernetes/kubelet.conf

# 특히 admin.conf 변경 확인
ls -l /etc/kubernetes/admin.conf
ls -l /etc/kubernetes/backup-conf.$(date +%F)/admin.conf 
vi -d /etc/kubernetes/backup-conf.$(date +%F)/admin.conf /etc/kubernetes/admin.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;control-plane &lt;span data-token-index=&quot;1&quot;&gt;static pod 재기동&lt;/span&gt; &amp;amp; admin.conf kubeconfig 재적용&lt;/p&gt;
&lt;pre id=&quot;code_1769296719123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 사전 백업 : static pod 매니페스트
cp -r /etc/kubernetes/manifests /etc/kubernetes/manifests.backup.$(date +%F)
ls -l /etc/kubernetes/manifests.backup.$(date +%F)

# static pod 모니터링(신규 터미널)
watch -d crictl ps


# static pod manifest 삭제
rm -rf /etc/kubernetes/manifests/*.yaml

# static pod manifest 삭제 확인 
crictl ps

# static pod manifest 복사 -&amp;gt; 파드 재기동  
cp /etc/kubernetes/manifests.backup.$(date +%F)/*.yaml /etc/kubernetes/manifests
tree /etc/kubernetes/manifests

# 파드 기동 확인 : CA가 바뀌지 않았기 때문에 예전 인증서도 신뢰됨 &amp;gt;&amp;gt; 다만, 예전(?) 인증서의 만료 기간을 놓칠 수 있으니, 같이 갱신 할 것!
crictl ps
kubectl get pod -n kube-system -owide -v=6

# 특히 admin.conf 변경 확인
ls -l /root/.kube/config
ls -l /etc/kubernetes/admin.conf
ls -l /etc/kubernetes/backup-conf.$(date +%F)/admin.conf

vi -d /root/.kube/config /etc/kubernetes/admin.conf
vi -d /etc/kubernetes/backup-conf.$(date +%F)/admin.conf /etc/kubernetes/admin.conf

# admin.conf kubeconfig 재적용
yes | cp  /etc/kubernetes/admin.conf ~/.kube/config ; echo
chown $(id -u):$(id -g) ~/.kube/config
kubectl config rename-context &quot;kubernetes-admin@kubernetes&quot; &quot;HomeLab&quot;
kubens default&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;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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; K8S Upgrade by kubeadm&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; K8S Version Skew 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;메이저.마이너.패치 버전&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNefmg/dJMcagK5EFO/ekIH3995kKju9cf4gDxW60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNefmg/dJMcagK5EFO/ekIH3995kKju9cf4gDxW60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNefmg/dJMcagK5EFO/ekIH3995kKju9cf4gDxW60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNefmg%2FdJMcagK5EFO%2FekIH3995kKju9cf4gDxW60%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;1024&quot; height=&quot;468&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;1년에 3개의 마이너 버전 출시 &amp;rarr; 최근 3개 버전 release branches(패치) 지원 (그이상 힘듬..) - &lt;a href=&quot;https://kubernetes.io/releases/&quot; data-token-index=&quot;3&quot;&gt;Link&lt;/a&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjWPpt/dJMcabJM7g1/iVck3yKFB8PeaHbojKHPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjWPpt/dJMcabJM7g1/iVck3yKFB8PeaHbojKHPK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjWPpt/dJMcabJM7g1/iVck3yKFB8PeaHbojKHPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjWPpt%2FdJMcabJM7g1%2FiVck3yKFB8PeaHbojKHPK1%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;491&quot; height=&quot;742&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&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;kube-apiserver 가 HA 일 경우, NEW 버전(1.32) 과 마이너 OLD 1개 아래 버전(1.31) 가능&lt;/li&gt;
&lt;li&gt;kubelet
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubelet 은 kube-apiserver 보다 NEW 버전을 사용할 수 없다&lt;/li&gt;
&lt;li&gt;kubelet 은 kube-apiserver 보다 3개의 마이너 OLD 버전 가능&lt;/li&gt;
&lt;li&gt;예시1) kube-apiserver 1.32 일 때 &amp;rarr; kubelet 1.32, 1.31, 1.30, 1.29 가능&lt;/li&gt;
&lt;li&gt;예시2) kube-apiserver (HA) 1.32, 1.31 일때 &amp;rarr; kubelet 1.31, 1.30, 1.29 가능 (1.32는 아직 api가 1.31이 있으니 안됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube-controller-manager(kcm), kube-scheduler, and cloud-controller-manager(ccm)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 3개 구성요소는 kube-apiserver 보다 NEW 버전을 사용할 수 없다&lt;/li&gt;
&lt;li&gt;위 3개 구성요소는 kube-apiserver 보다 1개의 마이너 OLD 버전 가능&lt;/li&gt;
&lt;li&gt;예시1) kube-apiserver 1.32 일 때 &amp;rarr; 위 3개 구성요소 1.32, 1.31 가능&lt;/li&gt;
&lt;li&gt;예시2) kube-apiserver (HA) 1.32, 1.31 일때 &amp;rarr; 위 3개 구성요소 1.31 만 가능 (1.32는 아직 api가 1.31이 있으니 안됨)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, kube-apiserver (HA) 환경에서는 kube-apiserver (HA) 먼저 1.31 &amp;rarr; 1.32 작업 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube-proxy
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-proxy 는 kube-apiserver 보다 NEW 버전을 사용할 수 없다&lt;/li&gt;
&lt;li&gt;kube-proxy 은 kube-apiserver 보다 3개의 마이너 OLD 버전 가능&lt;/li&gt;
&lt;li&gt;kube-proxy 은 kubelet 보다 3개의 마이너 NEW 혹은 OLD 버전 가능&lt;/li&gt;
&lt;li&gt;예시1) kube-apiserver 1.32 일 때 &amp;rarr; kube-proxy 1.32, 1.31, 1.30, 1.29 가능&lt;/li&gt;
&lt;li&gt;예시2) kube-apiserver (HA) 1.32, 1.31 일때 &amp;rarr; kube-proxy 1.31, 1.30, 1.29 가능 (1.32는 아직 api가 1.31이 있으니 안됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kubectl
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl 은 kube-apiserver 보다 1개의 마이너 NEW/OLD 버전 가능&lt;/li&gt;
&lt;li&gt;예시1) kube-apiserver 1.32 일 때 &amp;rarr; kubectl 1.33, 1.32, 1.31 가능&lt;/li&gt;
&lt;li&gt;예시2) kube-apiserver (HA) 1.32, 1.31 일때 &amp;rarr; kubectl 1.32, 1.31 가능 (1.33는 아직 api가 1.31이 있으니 안됨)&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; &lt;span data-token-index=&quot;0&quot;&gt;가상의 온프레미스 환경 K8S 에서 업그레이드&lt;/span&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;1650&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ekp1h/dJMcai28QQR/52hxDKKouiqJuCiitUGPWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ekp1h/dJMcai28QQR/52hxDKKouiqJuCiitUGPWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ekp1h/dJMcai28QQR/52hxDKKouiqJuCiitUGPWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEkp1h%2FdJMcai28QQR%2F52hxDKKouiqJuCiitUGPWk%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;1650&quot; height=&quot;490&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;490&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U0jwr/dJMcaaxkz9l/JtILno4RccNx9ohE7wchWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U0jwr/dJMcaaxkz9l/JtILno4RccNx9ohE7wchWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U0jwr/dJMcaaxkz9l/JtILno4RccNx9ohE7wchWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU0jwr%2FdJMcaaxkz9l%2FJtILno4RccNx9ohE7wchWk%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;2048&quot; height=&quot;422&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;422&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;Ubuntu 22.04 (kernel 5.15)&lt;/li&gt;
&lt;li&gt;Containerd 1.7.z&lt;/li&gt;
&lt;li&gt;K8S Version 1.28.2 - kubeadm, kubelet&lt;/li&gt;
&lt;li&gt;CNI : Cilium 1.4.z&lt;/li&gt;
&lt;li&gt;CSI : OpenEBS 3.y.z&lt;/li&gt;
&lt;li&gt;애플리케이션 및 요구사항 확인 : 현황 조사 후 작성&amp;hellip;&lt;/li&gt;
&lt;/ul&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; &lt;span data-token-index=&quot;0&quot;&gt;목표 버전 : K8S &lt;/span&gt;&lt;span style=&quot;color: #7d7a75;&quot; data-token-index=&quot;1&quot;&gt;1.28 &amp;rArr;&lt;/span&gt;&lt;span data-token-index=&quot;2&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #cf5148;&quot; data-token-index=&quot;3&quot;&gt;1.32!&lt;/span&gt; &lt;/b&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;버전 호환성 검토&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;K8S(kubelet, apiserver..) 1.32 요구 커널 버전 확인 : 예) user namespace 사용 시 커널 6.5 이상 필요 - &lt;a href=&quot;https://kubernetes.io/docs/reference/node/kernel-version-requirements/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;containerd 버전 조사 : 예) user namespace 에 대한 CRI 지원 시 containerd v2.0 및 runc v1.2 이상 필요 - &lt;a href=&quot;https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md&quot;&gt;Docs&lt;/a&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;K8S 호환 버전 확인 - &lt;a href=&quot;https://containerd.io/releases/#kubernetes-support&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;CNI(Cilium) 요구 커널 버전 확인 : 예) BPF-based host routing 기능 필요 시 커널 5.10 이상 필요 - &lt;a href=&quot;https://docs.cilium.io/en/stable/operations/system_requirements/&quot;&gt;Docs&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CNI 이 지원하는 K8S 버전 확인 - &lt;a href=&quot;https://docs.cilium.io/en/stable/network/kubernetes/compatibility/&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSI : Skip&amp;hellip;&lt;/li&gt;
&lt;li&gt;애플리케이션 요구사항 검토&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;업그레이드 방법 결정&lt;/b&gt; : in-place vs blue-green&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dev - Stg - Prd 별 각기 다른 방법 vs 모두 동일 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;결정된 방법으로 업그레이드 계획 수립&lt;/span&gt; : 예시) in-place 결정&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;2048&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2o0dZ/dJMcadOhHNq/vRyVbJKyeD3WtDTCKThQt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2o0dZ/dJMcadOhHNq/vRyVbJKyeD3WtDTCKThQt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2o0dZ/dJMcadOhHNq/vRyVbJKyeD3WtDTCKThQt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2o0dZ%2FdJMcadOhHNq%2FvRyVbJKyeD3WtDTCKThQt1%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;2048&quot; height=&quot;449&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;449&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;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; &lt;span data-token-index=&quot;0&quot;&gt;사전 준비&lt;/span&gt; &lt;/b&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;/li&gt;
&lt;li&gt;모니터링 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(1)&lt;/b&gt; ETCD 백업&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(2)&lt;/b&gt; CNI(cilium) 업그레이드&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;CP 노드 순차 업그레이드&lt;/b&gt; : 1.28 &amp;rarr; 1.29 &amp;rarr; 1.30&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 1대씩 작업 진행 1.28 &amp;rarr; 1.29
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(3)&lt;/b&gt; Ubuntu OS(kernel) 업그레이드 &amp;rarr; 재부팅&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(4)&lt;/b&gt; containerd 업그레이드 &amp;rarr; containerd 서비스 재시작&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(5)&lt;/b&gt; kubeadm 업그레이드 &amp;rarr; kubelet/kubectl 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;노드 1대씩 작업 진행 1.29 &amp;rarr; 1.30
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(5)&lt;/b&gt; kubeadm 업그레이드 &amp;rarr; kubelet/kubectl 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 완료 후 CP 노드 상태 확인&lt;/li&gt;
&lt;/ul&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;DP 노드 순차 업그레이드&lt;/b&gt; : 1.28 &amp;rarr; 1.29 &amp;rarr; 1.30&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 1대씩 작업 진행 1.28 &amp;rarr; 1.29
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(6)&lt;/b&gt; 작업 노드 drain 설정 후 파드 Evicted 확인, 서비스 중단 여부 모니터링 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(7)&lt;/b&gt; Ubuntu OS(kernel) 업그레이드 &amp;rarr; 재부팅&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(8)&lt;/b&gt; containerd 업그레이드 &amp;rarr; containerd 서비스 재시작&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(9)&lt;/b&gt; kubeadm 업그레이드 &amp;rarr; kubelet 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;노드 1대씩 작업 진행 1.29 &amp;rarr; 1.30
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(9)&lt;/b&gt; kubeadm 업그레이드 &amp;rarr; kubelet 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 완료 후 DP 노드 상태 확인 &amp;rArr; &lt;b&gt;(10)&lt;/b&gt; 작업 노드 uncordon 설정 후 다시 상태 확인&lt;/li&gt;
&lt;/ul&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;K8S 관련 전체적인 동작 1차 점검&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;/li&gt;
&lt;/ul&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;CP 노드 순차 업그레이드&lt;/b&gt; : &lt;b&gt;1.30&lt;/b&gt; &amp;rarr; 1.31 &amp;rarr; &lt;b&gt;1.32&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;노드 1대씩 작업 진행 x 버전별 반복
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubeadm 업그레이드 &amp;rarr; kubelet/kubectl 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 완료 후 CP 노드 상태 확인&lt;/li&gt;
&lt;/ul&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;DP 노드 순차 업그레이드&lt;/b&gt; : 1.30 &amp;rarr; 1.31 &amp;rarr; 1.32&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 1대씩 작업 진행 x 버전별 반복
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업 노드 drain 설정 후 파드 Evicted 확인, 서비스 중단 여부 모니터링 확인&lt;/li&gt;
&lt;li&gt;kubeadm 업그레이드 &amp;rarr; kubelet 업그레이드 설치 &amp;rarr; kubelet 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;작업 완료 후 DP 노드 상태 확인 &amp;rArr; 작업 노드 uncordon 설정 후 다시 상태 확인&lt;/li&gt;
&lt;/ul&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; &lt;span data-token-index=&quot;0&quot;&gt;K8S 관련 전체적인 동작 2차 점검&lt;/span&gt; &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;/li&gt;
&lt;/ul&gt;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/50</guid>
      <comments>https://netoppa.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 20 Jan 2026 17:26:15 +0900</pubDate>
    </item>
    <item>
      <title>[2주차] Ansible 기초</title>
      <link>https://netoppa.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 포스팅은 Cloud@net의 기시다님의 온라인 강좌 내용을 정리한 스터디 요약 자료입니다.&lt;br /&gt;실습환경은 하이퍼바이저로 오픈스택을 활용하여 실습하였기에 실제 스터디 환경과 다르나 Ansible 설치 및 운영하는 실습은 동일함.&lt;/blockquote&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 개념&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Ansible은 서버 관리를 자동화하는 Configuration Management 도구로서 여러 시스템의 설정을 코드 기반으로 작성하여 여러 환경에 동일하게 적용될 수 있게 하는 IaC(Infra as a Code) 자동화 도구입니다.&amp;nbsp;&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOY7TD/dJMcadm849X/GnEK1dsQwb1IBoU2f5xnu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOY7TD/dJMcadm849X/GnEK1dsQwb1IBoU2f5xnu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOY7TD/dJMcadm849X/GnEK1dsQwb1IBoU2f5xnu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOY7TD%2FdJMcadm849X%2FGnEK1dsQwb1IBoU2f5xnu1%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;1280&quot; height=&quot;569&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 특징&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앤서블은 리눅스, MacOS, BSC 계열 유닉스, WSL을 지원하는 원도우에 &lt;b&gt;파이썬&lt;/b&gt;과 &lt;b&gt;앤서블 코어&lt;/b&gt;만 설치하면 어디에서나 &lt;b&gt;플레이북&lt;/b&gt;(YAML 형식의 작업들은 순서대로 작성해 놓은 파일)을 작성하고 이를 &lt;b&gt;실행&lt;/b&gt;시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;앤서블은 이렇게 &lt;b&gt;앤서블 코어&lt;/b&gt;가 설치되고 플레이북을 작성하여 실행할 수 있는 &lt;b&gt;제어 노드 Control Node&lt;/b&gt;와 &lt;b&gt;플레이북&lt;/b&gt;이 실행되어 애플리케이션 설치나 클라우드 시스템의 가상 서버 생성과 같은 작업이 수행되는 &lt;b&gt;관리 노드 Managed Node&lt;/b&gt;로 구성됩니다.&lt;/li&gt;
&lt;li&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;플레이북&lt;/b&gt;과 관리 노드를 정의해놓은 &lt;b&gt;인벤토리 파일&lt;/b&gt;에 의해 SSH 프로토콜을 사용하여 다양한 환경의 관리 노드 업무 자동화를 수행할 수 있습니다.&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: #f89009;&quot;&gt;&lt;b&gt;No! 에이전트(Agentless)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 자동화 도구인 퍼펫 Puppet이나 셰프 Chef는 자동화 관리 대상 서버에 별도의 에이전트를 설치하고 이를 통해 자동화 업무를 수행합니다.&lt;/li&gt;
&lt;li&gt;이러한 데몬 형식의 에이전트에 기반한 자동화 도구는 관리를 위한 복잡한 추가 작업이나 운영체제 버전에 따라 추가 패키지나 모듈을 설치하는 등의 작업이 발생합니다.&lt;/li&gt;
&lt;li&gt;그러나 앤서블은 &lt;b&gt;에이전트 설치 없이 SSH로 접속&lt;/b&gt;하여 쉽게 대상 서버들을 관리할 수 있습니다. 이것이 앤서블의 가장 큰 특징이자 장점입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;멱등성 Idempotent&lt;/b&gt;&lt;/span&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;쉬운 사용법과 다양한 모듈 제공&lt;/b&gt;&lt;/span&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;b&gt;YAML 문법&lt;/b&gt;을 사용하여 쉽게 작성하고 읽을 수 있습니다.&lt;/li&gt;
&lt;li&gt;또한 파일 복사와 같은 일반 &lt;b&gt;시스템 관리 모듈&lt;/b&gt;부터 다양한 환경의 퍼블릭 &lt;b&gt;클라우드 관련 모듈&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 분류&lt;/b&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;커뮤니티 앤서블&lt;/b&gt;&lt;/span&gt; : 오픈소스 형태로 운영체제가 리눅스라면 어디에나 설치하여 사용할 수 있음레드햇 앤서블 &lt;br /&gt;- &lt;b&gt;오토메이션 플랫폼&lt;/b&gt; : 레드햇 서브스크립션을 통해 사용&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;커뮤니티 앤서블 아키텍처&lt;/span&gt; : 제어 노드 + 관리 노드 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앤서블 안에는 다양한 &lt;span data-token-index=&quot;1&quot;&gt;모듈&lt;/span&gt;과 &lt;span data-token-index=&quot;3&quot;&gt;플러그&lt;/span&gt;인이 함께 설치되어 있으며, 앤서블이 관리하는 &lt;span data-token-index=&quot;5&quot;&gt;노드 정보&lt;/span&gt;를 저장하고 있는 &lt;span data-token-index=&quot;7&quot;&gt;인벤토리&lt;/span&gt;와 관리 노드에서 수행될 &lt;span data-token-index=&quot;9&quot;&gt;작업 절차&lt;/span&gt;가 작성되어 있는 &lt;span data-token-index=&quot;11&quot;&gt;플레이북&lt;/span&gt;이 존재합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcLs1q/dJMcabpnobt/HkZh8vNmkz7BzpCWs6oN1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcLs1q/dJMcabpnobt/HkZh8vNmkz7BzpCWs6oN1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcLs1q/dJMcabpnobt/HkZh8vNmkz7BzpCWs6oN1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcLs1q%2FdJMcabpnobt%2FHkZh8vNmkz7BzpCWs6oN1k%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;1109&quot; height=&quot;698&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;698&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제어 노드 Control Node&lt;/b&gt;&lt;/span&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;관리 노드 Managed Node&lt;/b&gt;&lt;/span&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;관리 노드는 [그림 2-1]처럼 리눅스 or 윈도우가 설치된 노드일 수 있습니다.&lt;/li&gt;
&lt;li&gt;앤서블은 별도의 에이전트를 설치하지 않으므로 관리 노드는 &lt;b&gt;제어 노드와 SSH 통신이 가&lt;/b&gt;능해야 하며, &lt;b&gt;파이썬이 설치&lt;/b&gt;되어 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;인벤토리 Inventory&lt;/b&gt;&lt;/span&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;/li&gt;
&lt;li&gt;또한 인벤토리 목록은 관리 노드의 성격별로 다음과 같이 그룹핑할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768202582572&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ vi inventory
192.168.10.101

[WebServer]
web1.example.com
web2.example.com

[DBServer]
db1.example.com
db2.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;모듈 Modules&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앤서블은 관리 노드의 작업을 수행할 때 SSH를 통해 연결한 후 &amp;lsquo;&lt;b&gt;앤서블 모듈&lt;/b&gt; Modules&amp;rsquo;이라는 &lt;b&gt;스크립트&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;b&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;플러그인 Plugins&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;별도의 프로세스로 실행&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;핵심 기능&lt;/b&gt;(데이터 변환, 로그 출력, 인벤토리 연결 등)에 대한 &lt;b&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;플레이북 Playbook&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;을 YAML 문법을 이용해 &lt;b&gt;순서대로 작성해놓은 파일&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;엔서블은 이렇게 작성된 플레이북을 활용하여 관리 노드에 SSH로 접근해 작업을 수행합니다.&lt;/li&gt;
&lt;li&gt;따라서 플레이북은 자동화를 완성하는 가장 중요한 파일이며 사용자가 직접 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768202641100&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: webservers
  serial: 5  # 한 번에 5대의 머신을 업데이트하라는 의미
  roles:
  - common
  - webapp

- hosts: content_servers
  roles:
  - common
  - content&lt;/code&gt;&lt;/pre&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 아키텍처&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔서블은&amp;nbsp; &lt;b&gt;제어부 노드(Control-node)&lt;/b&gt;와 &lt;b&gt;관리부(&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;managed-node&lt;/span&gt;)&lt;/b&gt;로 분리되어 코드를 통해 실행되는 구조입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;제어부(Control-node)&lt;/b&gt; 는&amp;nbsp; ssh 프로토콜을 사용하여 &lt;b&gt;관리부(Managed-node)&lt;/b&gt;에 ansible 모듈을 복사합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관리부(Managed-node)&lt;/b&gt;는 전달받은 ansible모듈을 파이썬으로 실행합니다.&lt;/li&gt;
&lt;/ul&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;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 실습환경(by OPENSTACK)&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오픈스택 가상 머신 구성&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;- 하이퍼바이저 : OPENSTACK 7.1.4 (버전: 2023.2&amp;nbsp; Antelope)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;☞ 실제 Production Private 클라우드를 구성하고자 &lt;span style=&quot;color: #ee2323;&quot;&gt;OPENSTACK&lt;/span&gt;에 인스턴스를 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;2e550aec-5edf-81d4-b3e4-c336d2b1f1ff&quot; style=&quot;border-collapse: collapse; width: 100%; height: 86px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;노드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;OS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;&amp;nbsp;커널&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;vCPU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Mem&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Disk&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;NIC IP&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;관리자계정&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;일반계정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-812f-b379-c2f46a258f3d&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;]un]&quot; style=&quot;height: 16px;&quot;&gt;server&lt;/td&gt;
&lt;td id=&quot;gJrs&quot; style=&quot;height: 16px;&quot;&gt;Ubuntu 24.04&lt;/td&gt;
&lt;td id=&quot;GJi|&quot; style=&quot;height: 16px;&quot;&gt;6.8.0&lt;/td&gt;
&lt;td id=&quot;PDBE&quot; style=&quot;height: 16px;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;rxlr&quot; style=&quot;height: 16px;&quot;&gt;2 GB&lt;/td&gt;
&lt;td id=&quot;:ZKh&quot; style=&quot;height: 16px;&quot;&gt;30GB&lt;/td&gt;
&lt;td id=&quot;N;QM&quot; style=&quot;height: 16px;&quot;&gt;192.168.7.245&lt;/td&gt;
&lt;td id=&quot;HYX[&quot; style=&quot;height: 16px;&quot;&gt;root / qwe123&lt;/td&gt;
&lt;td id=&quot;vLJo&quot; style=&quot;height: 16px;&quot;&gt;ansible / qwe123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-81c4-a1ee-f0d58fb47598&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;]un]&quot; style=&quot;height: 22px;&quot;&gt;tnode1&lt;/td&gt;
&lt;td id=&quot;gJrs&quot; style=&quot;height: 22px;&quot;&gt;상동&lt;/td&gt;
&lt;td id=&quot;GJi|&quot; style=&quot;height: 22px;&quot;&gt;상동&lt;/td&gt;
&lt;td id=&quot;PDBE&quot; style=&quot;height: 22px;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;rxlr&quot; style=&quot;height: 22px;&quot;&gt;2 GB&lt;/td&gt;
&lt;td id=&quot;:ZKh&quot; style=&quot;height: 22px;&quot;&gt;30GB&lt;/td&gt;
&lt;td id=&quot;N;QM&quot; style=&quot;height: 22px;&quot;&gt;192.168.7.246&lt;/td&gt;
&lt;td id=&quot;HYX[&quot; style=&quot;height: 22px;&quot;&gt;root / qwe123&lt;/td&gt;
&lt;td id=&quot;vLJo&quot; style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ansible&lt;span&gt; &lt;/span&gt;&lt;/span&gt;/ qwe123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-8159-8d2c-df787e39d237&quot; style=&quot;height: 10px;&quot;&gt;
&lt;td id=&quot;]un]&quot; style=&quot;height: 10px;&quot;&gt;tnode2&lt;/td&gt;
&lt;td id=&quot;gJrs&quot; style=&quot;height: 10px;&quot;&gt;상동&lt;/td&gt;
&lt;td id=&quot;GJi|&quot; style=&quot;height: 10px;&quot;&gt;상동&lt;/td&gt;
&lt;td id=&quot;PDBE&quot; style=&quot;height: 10px;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;rxlr&quot; style=&quot;height: 10px;&quot;&gt;2 GB&lt;/td&gt;
&lt;td id=&quot;:ZKh&quot; style=&quot;height: 10px;&quot;&gt;30GB&lt;/td&gt;
&lt;td id=&quot;N;QM&quot; style=&quot;height: 10px;&quot;&gt;192.168.7.247&lt;/td&gt;
&lt;td id=&quot;HYX[&quot; style=&quot;height: 10px;&quot;&gt;root / qwe123&lt;/td&gt;
&lt;td id=&quot;vLJo&quot; style=&quot;height: 10px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ansible&lt;span&gt; &lt;/span&gt;&lt;/span&gt;/ qwe123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-8105-8f36-d9d21eee5ce1&quot; style=&quot;height: 16px;&quot;&gt;
&lt;td id=&quot;]un]&quot; style=&quot;height: 16px;&quot;&gt;tnode3&lt;/td&gt;
&lt;td id=&quot;gJrs&quot; style=&quot;height: 16px;&quot;&gt;Rocky Linux 9&lt;/td&gt;
&lt;td id=&quot;GJi|&quot; style=&quot;height: 16px;&quot;&gt;5.14.0&lt;/td&gt;
&lt;td id=&quot;PDBE&quot; style=&quot;height: 16px;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;rxlr&quot; style=&quot;height: 16px;&quot;&gt;2 GB&lt;/td&gt;
&lt;td id=&quot;:ZKh&quot; style=&quot;height: 16px;&quot;&gt;60GB&lt;/td&gt;
&lt;td id=&quot;N;QM&quot; style=&quot;height: 16px;&quot;&gt;192.168.7.248&lt;/td&gt;
&lt;td id=&quot;HYX[&quot; style=&quot;height: 16px;&quot;&gt;root / qwe123&lt;/td&gt;
&lt;td id=&quot;vLJo&quot; style=&quot;height: 16px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ansible&lt;span&gt; &lt;/span&gt;&lt;/span&gt;/ qwe123&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGlcv1/dJMcafFkwmH/xyZQs5RyvOKNDEYNkdHvJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGlcv1/dJMcafFkwmH/xyZQs5RyvOKNDEYNkdHvJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGlcv1/dJMcafFkwmH/xyZQs5RyvOKNDEYNkdHvJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGlcv1%2FdJMcafFkwmH%2FxyZQs5RyvOKNDEYNkdHvJ0%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;1482&quot; height=&quot;492&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;492&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;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;실습환경 네트워크 구성도&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;802&quot; data-origin-height=&quot;883&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkyx7G/dJMb99LR6CM/O8nUmUB5ffaOkcyZ3p9Ylk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkyx7G/dJMb99LR6CM/O8nUmUB5ffaOkcyZ3p9Ylk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkyx7G/dJMb99LR6CM/O8nUmUB5ffaOkcyZ3p9Ylk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkyx7G%2FdJMb99LR6CM%2FO8nUmUB5ffaOkcyZ3p9Ylk%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;883&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;883&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;실습 환경 배포 &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Private 클라우드로 가장 보편적으로 사용되고 있는 &lt;span style=&quot;color: #ee2323;&quot;&gt;OPENSTACK&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;에 실습용 인스턴스를 생성하여 Ansible 설치 실습을 진행하고자 함. Vagrant는 개발환경에서 많이 사용되나 실제 프러덕션 환경인 OPENSTACK 에는 적합하지 않으므로 인스턴스 생성 및 초기 구성은 OPENSTACK 에서 제공하는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Orchestratioin Stack&lt;/b&gt;&lt;/span&gt;을 활용하여 실습환경을 구성하였음&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Rocky linux 9 &amp;amp; &lt;/b&gt;&lt;b&gt;Ubuntu 24.02 LTS 인스턴스 배포&lt;/b&gt;&lt;/h4&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;☞ OPENSTACK &lt;span style=&quot;color: #ee2323;&quot;&gt;오케스트레이션 STACK&lt;/span&gt;을 활용하여 실습 환경 구성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;; 오케스트레이션&amp;nbsp; STACK 은 &lt;/b&gt;OPENSTACK에서 가상머신을 구성하고 설치하는 작업을 도와주는 IaC 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;오케스트레이션 Stack 스크립트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768268589756&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;heat_template_version: &quot;2018-08-31&quot;

description: &amp;gt;
  내부망(100.100.100.0/24)과 외부망 Floating IP(192.168.7.x)를 사용하는 4대 노드 구성 템플릿입니다.

parameters:
  key_name:
    type: string
    default: &quot;cli-key&quot;
  flavor:
    type: string
    default: &quot;m1.small&quot;
  private_net:
    type: string
    default: &quot;cli-network&quot;
  private_subnet:
    type: string
    default: &quot;cli-subnet&quot;
  public_net:
    type: string
    description: Floating IP를 할당받을 외부 네트워크 이름 (예: public)
    default: &quot;public&quot;

resources:
  # [1] Server Node: Ubuntu 24.04
  server_node:
    type: OS::Nova::Server
    properties:
      name: server
      image: ubuntu24
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.245 }]
      user_data_format: RAW
      user_data: &amp;amp;ubuntu_script |
        #!/bin/bash
        # [TASK 1-4: 기존 설정과 동일]
        echo 'alias vi=vim' &amp;gt;&amp;gt; /etc/profile
        echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/vagrant/.bashrc
        ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
        systemctl stop ufw &amp;amp;&amp;amp; systemctl disable ufw &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        apt update -qq &amp;amp;&amp;amp; apt install tree sshpass unzip -y -qq &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        echo 'vagrant:qwe123' | chpasswd
        echo 'root:qwe123' | chpasswd
        sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
        echo &quot;PermitRootLogin yes&quot; &amp;gt;&amp;gt; /etc/ssh/sshd_config
        systemctl restart ssh
        # [TASK 5] 내부망 IP 기반 호스트 설정
        cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
        100.100.100.245 server
        100.100.100.246 tnode1
        100.100.100.247 tnode2
        100.100.100.248 tnode3
        EOF

  # [Floating IP 할당 및 연결 - Server용]
  server_fip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: { get_param: public_net }

  server_fip_assoc:
    type: OS::Nova::FloatingIPAssociation
    properties:
      floating_ip: { get_resource: server_fip }
      server_id: { get_resource: server_node }

  # [2] tnode1
  tnode1:
    type: OS::Nova::Server
    properties:
      name: tnode1
      image: ubuntu24
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.246 }]
      user_data_format: RAW
      user_data: *ubuntu_script

  # [3] tnode2
  tnode2:
    type: OS::Nova::Server
    properties:
      name: tnode2
      image: ubuntu24
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.247 }]
      user_data_format: RAW
      user_data: *ubuntu_script

  # [4] tnode3: Rocky Linux 9
  tnode3:
    type: OS::Nova::Server
    properties:
      name: tnode3
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
      networks: [{ subnet: { get_param: private_subnet }, fixed_ip: 100.100.100.248 }]
      block_device_mapping_v2: [{ boot_index: 0, image: rocky9, volume_size: 60, delete_on_termination: true }]
      user_data_format: RAW
      user_data: |
        #!/bin/bash
        ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
        systemctl stop firewalld &amp;amp;&amp;amp; systemctl disable firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        setenforce 0
        dnf install -y yum sshpass jq git tree &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        echo 'vagrant:qwe123' | chpasswd
        echo 'root:qwe123' | chpasswd
        sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
        echo &quot;PermitRootLogin yes&quot; &amp;gt;&amp;gt; /etc/ssh/sshd_config
        systemctl restart sshd
        cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
        100.100.100.245 server
        100.100.100.246 tnode1
        100.100.100.247 tnode2
        100.100.100.248 tnode3
        EOF

outputs:
  server_public_ip:
    description: Floating IP of the Server Node
    value: { get_attr: [server_fip, floating_ip_address] }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&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;span&gt;&lt;span&gt; init_cfg 설정 (ubuntu24)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768353011761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Ubuntu Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;

# [TASK 0] ansible 유저 체크 및 생성 (홈 디렉토리 보장)
if ! id &quot;ansible&quot; &amp;amp;&amp;gt;/dev/null; then
    echo &quot;[TASK 0] Creating ansible user...&quot;
    useradd -m -s /bin/bash ansible
else
    echo &quot;[TASK 0] ansible user exists. Checking home directory...&quot;
    if [ ! -d &quot;/home/ansible&quot; ]; then
        mkdir -p /home/ansible
        cp -r /etc/skel/. /home/ansible/
        chown -R ansible:ansible /home/ansible
        echo &quot;Home directory created for existing ansible user.&quot;
    fi
fi

echo &quot;[TASK 1] Setting Profile &amp;amp; Change Timezone&quot;
echo 'alias vi=vim' &amp;gt;&amp;gt; /etc/profile
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/ansible/.bashrc
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

echo &quot;[TASK 2] Disable AppArmor &amp;amp; UFW&quot;
systemctl stop ufw &amp;amp;&amp;amp; systemctl disable ufw &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
systemctl stop apparmor &amp;amp;&amp;amp; systemctl disable apparmor &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

echo &quot;[TASK 3] Install Packages&quot;
apt update -qq &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
apt-get install tree sshpass unzip -y -qq &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

echo &quot;[TASK 4] Config account &amp;amp; ssh config&quot;
echo 'ansible:qwe123' | chpasswd
echo 'root:qwe123' | chpasswd
echo &quot;ansible ALL=(ALL) NOPASSWD:ALL&quot; &amp;gt; /etc/sudoers.d/ansible

sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
sed -i &quot;s/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g&quot; /etc/ssh/sshd_config
systemctl restart ssh

echo &quot;[TASK 5] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
100.100.100.245 server
100.100.100.246 tnode1
100.100.100.247 tnode2
100.100.100.248 tnode3
EOF

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Ubuntu Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&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;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;init_cfg2.sh 설정(rocky9)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768353212472&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Rocky Linux Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;

# [TASK 0] ansible 유저 체크 및 생성 (홈 디렉토리 보장)
if ! id &quot;ansible&quot; &amp;amp;&amp;gt;/dev/null; then
    echo &quot;[TASK 0] Creating ansible user...&quot;
    useradd -m -s /bin/bash ansible
else
    echo &quot;[TASK 0] ansible user exists. Checking home directory...&quot;
    if [ ! -d &quot;/home/ansible&quot; ]; then
        mkdir -p /home/ansible
        cp -r /etc/skel/. /home/ansible/
        chown -R ansible:ansible /home/ansible
        echo &quot;Home directory created for existing ansible user.&quot;
    fi
fi

echo &quot;[TASK 1] Setting Profile &amp;amp; Change Timezone&quot;
echo 'alias vi=vim' &amp;gt;&amp;gt; /etc/profile
echo &quot;sudo su -&quot; &amp;gt;&amp;gt; /home/ansible/.bashrc
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

echo &quot;[TASK 2] Disable Firewall &amp;amp; SELinux&quot;
systemctl stop firewalld &amp;amp;&amp;amp; systemctl disable firewalld &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

echo &quot;[TASK 3] Install Packages&quot;
dnf install -y epel-release -q &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
dnf install -y tree sshpass unzip vim-enhanced -q &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

echo &quot;[TASK 4] Config account &amp;amp; ssh config&quot;
echo 'ansible:qwe123' | chpasswd
echo 'root:qwe123' | chpasswd
echo &quot;ansible ALL=(ALL) NOPASSWD:ALL&quot; &amp;gt; /etc/sudoers.d/ansible

# SSH 설정 (Rocky는 PermitRootLogin이 아예 없는 경우가 있어 강제 추가 로직 포함)
sed -i &quot;s/^#PasswordAuthentication yes/PasswordAuthentication yes/g&quot; /etc/ssh/sshd_config
sed -i &quot;s/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g&quot; /etc/ssh/sshd_config
grep -q &quot;^PermitRootLogin yes&quot; /etc/ssh/sshd_config || echo &quot;PermitRootLogin yes&quot; &amp;gt;&amp;gt; /etc/ssh/sshd_config
systemctl restart sshd

echo &quot;[TASK 5] Setting Local DNS Using Hosts file&quot;
sed -i '/^127\.0\.0\.1/! s/^127\..*//' /etc/hosts
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
100.100.100.245 server
100.100.100.246 tnode1
100.100.100.247 tnode2
100.100.100.248 tnode3
EOF

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Rocky Linux Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&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; &lt;b&gt;☞&lt;span&gt; &lt;/span&gt;&lt;/b&gt;기본 정보 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768204385601&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 계정 정보 확인
whoami
id

# Kernel, CPU, Mem, Disk, NIC 확인
uname -r
hostnamectl
htop
free -h
lsblk
df -hT /
ip -c addr

# /etc/hosts 확인
cat /etc/hosts

# 노드간 통신 확인
for i in {1..3}; do ping -c 1 tnode$i; done&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1768357188748&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@server:~# uname -r
6.8.0-87-generic
root@server:~#
root@server:~#
root@server:~# hostnamectl
 Static hostname: server
       Icon name: computer-vm
         Chassis: vm  
      Machine ID: cf3c7fb2966a4477b3d35107055b5f15
         Boot ID: 42addd2b3c164cbdaa81d89e727ba914
  Virtualization: kvm
Operating System: Ubuntu 24.04.3 LTS
          Kernel: Linux 6.8.0-87-generic
    Architecture: x86-64
 Hardware Vendor: RDO
  Hardware Model: OpenStack Compute
Firmware Version: 1.16.3-4.el9
   Firmware Date: Tue 2014-04-01
    Firmware Age: 11y 9month 2w
root@server:~#
root@server:~#
root@server:~# htop
root@server:~#
root@server:~#
root@server:~#
root@server:~# free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       393Mi       661Mi       5.4Mi       1.1Gi       1.5Gi
Swap:             0B          0B          0B
root@server:~#
root@server:~#
root@server:~#
root@server:~#
root@server:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda     253:0    0   20G  0 disk
├─vda1  253:1    0   19G  0 part /
├─vda14 253:14   0    4M  0 part
├─vda15 253:15   0  106M  0 part /boot/efi
└─vda16 259:0    0  913M  0 part /boot
root@server:~#
root@server:~#
root@server:~#
root@server:~# df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
tmpfs          tmpfs  197M  1.1M  196M   1% /run
/dev/vda1      ext4    19G  2.2G   17G  12% /
tmpfs          tmpfs  984M     0  984M   0% /dev/shm
tmpfs          tmpfs  5.0M     0  5.0M   0% /run/lock
/dev/vda16     ext4   881M  117M  703M  15% /boot
/dev/vda15     vfat   105M  6.2M   99M   6% /boot/efi
tmpfs          tmpfs  197M   12K  197M   1% /run/user/1000
root@server:~#
root@server:~#
root@server:~#
root@server:~# ip -c addr
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc fq_codel state UP group default qlen 1000
    link/ether fa:16:3e:2e:f6:44 brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    inet 100.100.100.245/24 metric 100 brd 100.100.100.255 scope global dynamic ens3
       valid_lft 83335sec preferred_lft 83335sec
    inet6 fe80::f816:3eff:fe2e:f644/64 scope link
       valid_lft forever preferred_lft forever
root@server:~#
root@server:~#
root@server:~# cat /etc/hosts
127.0.0.1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
100.100.100.245 server
100.100.100.246 tnode1
100.100.100.247 tnode2
100.100.100.248 tnode3
root@server:~#
root@server:~#
root@server:~#
root@server:~# for i in {1..3}; do ping -c 1 tnode$i; done
PING tnode1 (100.100.100.246) 56(84) bytes of data.
64 bytes from tnode1 (100.100.100.246): icmp_seq=1 ttl=64 time=1.73 ms

--- tnode1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.730/1.730/1.730/0.000 ms
PING tnode2 (100.100.100.247) 56(84) bytes of data.
64 bytes from tnode2 (100.100.100.247): icmp_seq=1 ttl=64 time=1.59 ms

--- tnode2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.590/1.590/1.590/0.000 ms
PING tnode3 (100.100.100.248) 56(84) bytes of data.
64 bytes from tnode3 (100.100.100.248): icmp_seq=1 ttl=64 time=1.70 ms

--- tnode3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.699/1.699/1.699/0.000 ms&lt;/code&gt;&lt;/pre&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible 설치&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ansible-server 설치(우분투 기준)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768204663554&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 작업 기본 디렉터리 확인
whoami
pwd

# 파이썬 버전 확인
python3 --version

# 설치
apt install software-properties-common -y
add-apt-repository --yes --update ppa:ansible/ansible
apt install ansible -y

# 확인
ansible --version
ansible [core 2.19.5]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0] (/usr/bin/python3)
  jinja version = 3.1.2
  pyyaml version = 6.0.1 (with libyaml v0.2.5)
  
cat /etc/ansible/ansible.cfg
ansible-config list
which ansible

# 작업 디렉터리 생성
mkdir my-ansible
cd my-ansible&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;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;&lt;span data-token-index=&quot;0&quot;&gt;앤서블 접근을 위한 SSH 인증 구성&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;앤서블은 로컬 사용자에게 개인 SSH 키가 있거나 관리 호스트에서 원격 사용자임을 인증 가능한 키가 구성된 경우 자동으로 로그인됩니다.&lt;/blockquote&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;▶ ssh-keygen 명령어를 이용하여 SSH 키를 생성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768204748461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 모니터링
tree ~/.ssh
watch -d 'tree ~/.ssh'

# Create SSH Keypair
ssh-keygen -t rsa -N &quot;&quot; -f /root/.ssh/id_rsa

# 기본 cloud-init 파일 제거
# ubuntu 24.02
cat /etc/ssh/sshd_config.d/
sudo rm -rf /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
sudo systemctl restart ssh

# rocky linux 9
cat /etc/ssh/sshd_config.d/50-cloud-init.conf
sudo rm -rf /etc/ssh/sshd_config.d/50-cloud-init.conf
sudo systemctl restart sshd&lt;/code&gt;&lt;/pre&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;▶ &lt;/b&gt;&lt;b&gt;공개 키를 관리 노드에 복사&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768204767226&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 공개 키를 관리 노드에 복사
for i in {1..3}; do sshpass -p 'qwe123' ssh-copy-id -o StrictHostKeyChecking=no root@tnode$i; done

# 복사 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i cat ~/.ssh/authorized_keys; echo; done

# ssh 접속 테스트
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i hostname; echo; done

# python 정보 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i python3 -V; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci6e9d/dJMcabpoik8/Jxri3gBrjrYskyS6qLK7AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci6e9d/dJMcabpoik8/Jxri3gBrjrYskyS6qLK7AK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci6e9d/dJMcabpoik8/Jxri3gBrjrYskyS6qLK7AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci6e9d%2FdJMcabpoik8%2FJxri3gBrjrYskyS6qLK7AK%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;590&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;590&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;호스트 선정&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; ▶ &lt;span data-token-index=&quot;0&quot;&gt;인벤토리를 이용한 자동화 대상 호스트 설정&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;인벤토리&lt;/span&gt; 파일은 텍스트 파일이며, 앤서블이 자동화 대상으로 하는 &lt;span data-token-index=&quot;2&quot;&gt;관리 호스트&lt;/span&gt;를 &lt;span data-token-index=&quot;4&quot;&gt;지정&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yqx5g/dJMcahQGzeC/41EaRimkVnpEPBCAg5V5H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yqx5g/dJMcahQGzeC/41EaRimkVnpEPBCAg5V5H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yqx5g/dJMcahQGzeC/41EaRimkVnpEPBCAg5V5H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYqx5g%2FdJMcahQGzeC%2F41EaRimkVnpEPBCAg5V5H0%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;1280&quot; height=&quot;725&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;725&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;▶ &lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;IP&lt;/span&gt;를 이용한 &lt;span data-token-index=&quot;2&quot;&gt;인벤토리 파일&lt;/span&gt; 생성 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;inventory&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768366304429&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inventory 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; inventory
100.100.100.246
100.100.100.247
100.100.100.247
EOT

# inventory 검증 : -i 특정 인벤토리 지정
ansible-inventory -i ./inventory --list | jq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bdgfv/dJMcagEgx8F/RE5soRqUPKpYAa0xqKpJR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bdgfv/dJMcagEgx8F/RE5soRqUPKpYAa0xqKpJR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bdgfv/dJMcagEgx8F/RE5soRqUPKpYAa0xqKpJR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdgfv%2FdJMcagEgx8F%2FRE5soRqUPKpYAa0xqKpJR1%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;942&quot; height=&quot;396&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;396&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;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; &lt;span data-token-index=&quot;0&quot;&gt;▶ 호스트명&lt;/span&gt;을 이용한 &lt;span data-token-index=&quot;2&quot;&gt;인벤토리 파일&lt;/span&gt; 생성 &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768366520075&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# /etc/hosts 파일 확인
cat /etc/hosts

# inventory 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; inventory
tnode1
tnode2
tnode3
EOT

# inventory 검증
ansible-inventory -i ./inventory --list | jq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNcaZx/dJMcagYwsIN/fvUbTB0ktau2hPcWGM1441/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNcaZx/dJMcagYwsIN/fvUbTB0ktau2hPcWGM1441/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNcaZx/dJMcagYwsIN/fvUbTB0ktau2hPcWGM1441/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNcaZx%2FdJMcagYwsIN%2FfvUbTB0ktau2hPcWGM1441%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;843&quot; height=&quot;384&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;384&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;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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;역할에 따른 호스트 그룹 설정&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트별로&amp;nbsp;롤&amp;nbsp;Role(역할)을&amp;nbsp;주고&amp;nbsp;롤별로&amp;nbsp;특정&amp;nbsp;작업을&amp;nbsp;수행해야&amp;nbsp;하는&amp;nbsp;경우가&amp;nbsp;종종&amp;nbsp;발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768366684251&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 참고 파일
cat /etc/ansible/hosts&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;그룹별 호스트 설정 &lt;/span&gt;&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;/li&gt;
&lt;li&gt;이 경우 다음과 같이 그룹명을 대괄화 ([]) 내에 작성하고 해당 그룹에 속하는 호스트명이나 IP를 한 줄에 하나씩 나열합니다.&lt;/li&gt;
&lt;li&gt;아래 인벤토리는 두 개의 호스트 그룹인 webservers와 db-servers를 정의한 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768366745958&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[webservers]
web1.example.com
web2.example.com

[db-servers]
db01.example.com
db02.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768366784923&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[webservers]
web1.example.com
web2.example.com
192.0.2.42

[db-servers]
db01.example.com
db02.example.com

[east-datacenter]
web1.example.com
db01.example.com

[west-datacenter]
web2.example.com
db02.example.com

[production]
web1.example.com
web2.example.com
db01.example.com
db02.example.com

[development]
192.168.0.42&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶ &lt;/span&gt;&lt;span data-token-index=&quot;0&quot;&gt;중첩 그룹 정의&lt;/span&gt;&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;호스트 그룹&lt;/b&gt;을 &lt;b&gt;포함&lt;/b&gt;할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;이 경우 호스트 그룹 이름 생성 시 &lt;b&gt;:children&lt;/b&gt; 이라는 &lt;b&gt;접미사&lt;/b&gt;를 추가하면 됩니다.&lt;/li&gt;
&lt;li&gt;다음은 webservers 및 db-servers 그룹의 모든 호스트를 포함하는 datacenter 그룹을 생성하는 예입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768366893906&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[webservers]
web1.example.com
web2.example.com

[db-servers]
db01.example.com
db02.example.com

[datacenter:children]
webservers
dbservers&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶ &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;범위를 사용한 호스트 사양 간소화&lt;/span&gt; &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;인벤토리의 호스트 이름 또는 IP 주소를 설정할 때 &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;b&gt;대괄호&lt;/b&gt; 사이에 시작 구문과 종료 구문을 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768366947826&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[start:end]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같이 webservers는 대괄호 사이에 [1:2]로 범위를 지정했으며, db-servers는 [01:02]로 범위를 지정했습니다.&lt;/li&gt;
&lt;li&gt;이렇게 지정하면 webservers의 경우 &lt;a href=&quot;http://web1.example.com&quot;&gt;web1.example.com&lt;/a&gt; , &lt;a href=&quot;http://web2.example.com&quot;&gt;web2.example.com&lt;/a&gt; 에 해당하는 호스트에 앤서블 플레이북 작업들이,&lt;/li&gt;
&lt;li&gt;db-servers의 경우 &lt;a href=&quot;http://db01.example.com&quot;&gt;db01.example.com&lt;/a&gt; , &lt;a href=&quot;http://db02.example.com&quot;&gt;db02.example.com&lt;/a&gt; 에 해당하는 호스트에 앤서블 플레이북 작업들이 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768366966723&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[webservers]
web[1:2].example.com

[db-servers]
db[01:02].example.com&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 외에도 IP 주소 범위를 표현할 때나 여러 호스트의 호스트명을 지정할 때, 혹은 DNS와 같이 호스트의 어미 부분을 범위로 지정하면 IPv6의 범위 설정에도 사용할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768367043251&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# IP 범위 설정 : 192.168.4.0 ~ 192.168.4.255 사이의 IP 범위를 표현
[defaults]
192.168.4.[0:255]

# 호스트명 범위 설정 : com01.example.com ~ com20.example.com 의 범위를 표현
[compute]
com[01:20].example.com

# DNS 범위 설정 : a.dns.example.com , b.dns.example.com , c.dns.example.com 을 의미함
[dns]
[a:c].dns.example.com

# IPv6 범위 설정 : 2001:db08::a ~ 2001:db08::f 사이의 IPv6 범위를 표현
[ipv6]
2001:db8::[a:f]&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;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Ansible Configuration Settings&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶ &lt;/span&gt;&lt;span data-token-index=&quot;0&quot;&gt;실습을 위한 인벤토리 그룹 구성 &amp;amp; ansible config 적용 우선순위&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1768367125657&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inventory 그룹 구성
cat &amp;lt;&amp;lt;EOT &amp;gt; inventory
[web]
tnode1
tnode2

[db]
tnode3

[all:children]
web
db
EOT

# inventory 검증
ansible-inventory -i ./inventory --list | jq
ansible-inventory -i ./inventory --graph&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYJ80Y/dJMcaf6oWdm/VuQl1KkJB8k8EFAQTll7bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYJ80Y/dJMcaf6oWdm/VuQl1KkJB8k8EFAQTll7bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYJ80Y/dJMcaf6oWdm/VuQl1KkJB8k8EFAQTll7bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYJ80Y%2FdJMcaf6oWdm%2FVuQl1KkJB8k8EFAQTll7bk%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;962&quot; height=&quot;750&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;750&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;현재 프로젝트 디렉터리 내에 ansible.cfg 라는 앤서블 환경 설정 파일을 구성 시, -i 옵션을 사용하지 않아도 ansible.cfg 설정 파일에 정의된 인벤토리의 호스트 정보를 확인할 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768367152322&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ansible.cfg 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; ansible.cfg
[defaults]
inventory = ./inventory
EOT

# inventory 목록 확인
ansible-inventory --list | jq


# ansible config 적용 우선 순위 확인
echo $ANSIBLE_CONFIG
cat $PWD/ansible.cfg   # kubespary 실행 시, 디렉터리 위치 고정 이유
ls ~/.ansible.cfg
tree ~/.ansible
cat /etc/ansible/ansible.cfg

#
ansible-config dump
ansible-config list&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(참고) ansible config 적용 우선 순위 - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html&quot;&gt;Link&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ANSIBLE_CONFIG&amp;nbsp;(environment variable if set)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ansible.cfg&amp;nbsp;(in the current directory)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;~/.ansible.cfg&amp;nbsp;(in the home directory)&lt;/li&gt;
&lt;li&gt;/etc/ansible/ansible.cfg&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt; 플레이북 작성 &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;▶ &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;플레이북 환경 설정 (엔서블 환경 설정)&lt;/span&gt; &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;ansible.cfg&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;/li&gt;
&lt;li&gt;섹션 제목은 &lt;b&gt;대괄호&lt;/b&gt;로 묶여 있으며, 기본적인 실행을 위해 다음 예제와 같이 [&lt;b&gt;defaults&lt;/b&gt;]와 [&lt;b&gt;privilege_escalation&lt;/b&gt;] 두 개의 섹션으로 구성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768367380145&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false&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; &lt;span data-token-index=&quot;0&quot;&gt;[defaults] 섹션&lt;/span&gt; : 앤서블 작업을 위한 기본값 설정 &lt;/b&gt;&lt;/h4&gt;
&lt;table id=&quot;2e550aec-5edf-8127-8cc8-eefd7a1481a2&quot; style=&quot;border-collapse: collapse; width: 84.7673%; height: 200px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 12.7907%; height: 22px;&quot;&gt;매개 변수&lt;/td&gt;
&lt;td style=&quot;width: 72.1654%; height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-81c1-a3f9-c72eb54032e5&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;L~@G&quot; style=&quot;width: 12.7907%; height: 22px;&quot;&gt;inventory&lt;/td&gt;
&lt;td id=&quot;d]|~&quot; style=&quot;width: 72.1654%; height: 22px;&quot;&gt;인벤토리 파일의 경로를 지정함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-819b-9a20-dc747af431d5&quot; style=&quot;height: 44px;&quot;&gt;
&lt;td id=&quot;L~@G&quot; style=&quot;width: 12.7907%; height: 44px;&quot;&gt;remote_user&lt;/td&gt;
&lt;td id=&quot;d]|~&quot; style=&quot;width: 72.1654%; height: 44px;&quot;&gt;앤서블이 관리 호스트에 연결할 때 사용하는 사용자 이름을 지정함. 이때, 사용자 이름을 지정하지 않으면 현재 사용자 이름으로 지정됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-811d-90e2-f9dc3c988df0&quot; style=&quot;height: 44px;&quot;&gt;
&lt;td id=&quot;L~@G&quot; style=&quot;width: 12.7907%; height: 44px;&quot;&gt;ask_pass&lt;/td&gt;
&lt;td id=&quot;d]|~&quot; style=&quot;width: 72.1654%; height: 44px;&quot;&gt;SSH 암호를 묻는 메시지 표시 여부를 지정함. SSH 공개 키 인증을 사용하는 경우 기본값은 false임.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;span data-token-index=&quot;0&quot;&gt;[privilege_escalation] 섹션&lt;/span&gt; : 보안/감사로 인해 원격 호스트에 권한 없는 사용자 연결 후 관리 액세스 권한을 에스컬레이션하여 루트 사용자로 가여올 때&lt;/h4&gt;
&lt;table id=&quot;2e550aec-5edf-8118-a47f-de15dd068275&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;매개 변수&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-8195-87a0-c02c91510de8&quot;&gt;
&lt;td id=&quot;;DUr&quot; style=&quot;width: 14.5349%;&quot;&gt;become&lt;/td&gt;
&lt;td id=&quot;EevB&quot; style=&quot;width: 85.3488%;&quot;&gt;기본적으로 권한 에스컬레이션을 활성화할 때 사용하며, 연결 후 관리 호스트에서 자동으로 사용자를 전환할지 여부를 지정함.&lt;br /&gt;일반적으로 root로 전환되며, 플레이북에서도 지정할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-81a8-b158-e1822be6d8f7&quot;&gt;
&lt;td id=&quot;;DUr&quot; style=&quot;width: 14.5349%;&quot;&gt;become_method&lt;/td&gt;
&lt;td id=&quot;EevB&quot; style=&quot;width: 85.3488%;&quot;&gt;권한을 에스컬레이션하는 사용자 전환 방식을 의미함. 일반적으로 기본값은 sudo를 사용하며, su는 옵션으로 설정할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-8103-bc74-c52642004116&quot;&gt;
&lt;td id=&quot;;DUr&quot; style=&quot;width: 14.5349%;&quot;&gt;become_user&lt;/td&gt;
&lt;td id=&quot;EevB&quot; style=&quot;width: 85.3488%;&quot;&gt;관리 호스트에서 전환할 사용자를 지정함. 일반적으로 기본값은 root임.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e550aec-5edf-81a8-9f00-d7ae3431915d&quot;&gt;
&lt;td id=&quot;;DUr&quot; style=&quot;width: 14.5349%;&quot;&gt;become_ask_pass&lt;/td&gt;
&lt;td id=&quot;EevB&quot; style=&quot;width: 85.3488%;&quot;&gt;become_method 매개 변수에 대한 암호를 묻는 메시지 표시 여부를 지정함. 기본값은 false임.&lt;br /&gt;권한을 에스컬레이션하기 위해 사용자가 암호를 입력해야 하는 경우, 구성 파일에 become_ask_pass = true 매개 변수를 설정하면 됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앤서블은 리눅스에서 기본적으로 &lt;b&gt;SSH&lt;/b&gt; 프로토콜을 사용하여 &lt;b&gt;관리 호스&lt;/b&gt;트에 연결합니다.&lt;/li&gt;
&lt;li&gt;앤서블에서 관리 호스트에 연결하는 방법을 제어하는 가장 중요한 매개 변수는 [&lt;b&gt;defaults&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;다른 사용자&lt;/b&gt;를 지정하려면 &lt;b&gt;remote_user&lt;/b&gt; 매개 변수를 사용하여 해당 사용자 이름으로 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;실습을 위한 엔서블 환경 설정&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;ansible.cfg&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768367692473&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOT &amp;gt; ansible.cfg
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[ad-hoc] &lt;span data-token-index=&quot;1&quot;&gt;ansible ping 모듈&lt;/span&gt; - &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.ansible.com/archive/ansible/2.3/ping_module.html&quot;&gt;https://docs.ansible.com/archive/ansible/2.3/ping_module.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;span data-token-index=&quot;0&quot;&gt;ping 모듈&lt;/span&gt;을 이용하여 web 그룹의 호스트로 정상 연결(pong반환)이면 &amp;lsquo;SUCCESS&amp;rsquo; 출력 &amp;larr; icmp(ping) 아니며, &lt;span data-token-index=&quot;2&quot;&gt;python 테스트 모듈&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768368474373&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
ansible -m ping web

# Ansible이 '지금은 잘 동작하지만 미래에 예기치 않게 Python 인터프리터가 바뀔 수 있다'는 점을 알려주는 예방용 경고
# 암묵적 Python 자동 선택을 지양하고, 명시적 설정을 권장 : 기본값(ansible_python_interpreter=auto)

# inventory 그룹 구성
cat &amp;lt;&amp;lt;EOT &amp;gt; inventory
[web]
tnode1 ansible_python_interpreter=/usr/bin/python3
tnode2 ansible_python_interpreter=/usr/bin/python3

[db]
tnode3 ansible_python_interpreter=/usr/bin/python3

[all:children]
web
db
EOT
ansible-inventory -i ./inventory --list | jq

#
ansible -i ./inventory web -m ping

#
ansible -i ./inventory db -m ping&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R6GL2/dJMcagK05Ru/mqsbTzGkTsMBAbg4K1QA0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R6GL2/dJMcagK05Ru/mqsbTzGkTsMBAbg4K1QA0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R6GL2/dJMcagK05Ru/mqsbTzGkTsMBAbg4K1QA0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR6GL2%2FdJMcagK05Ru%2FmqsbTzGkTsMBAbg4K1QA0K%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;950&quot; height=&quot;710&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;710&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;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;p data-ke-size=&quot;size16&quot;&gt;2. 옵션 설정으로 암호 입력 후 실행 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768369215148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 암호 입력 후 실행
ansible -i ./inventory web -m ping --ask-pass
SSH password: &amp;lt;암호입력&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNjLl7/dJMcafk2aUD/t5NoIdUCMVhGmUuQ2LWKr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNjLl7/dJMcafk2aUD/t5NoIdUCMVhGmUuQ2LWKr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNjLl7/dJMcafk2aUD/t5NoIdUCMVhGmUuQ2LWKr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNjLl7%2FdJMcafk2aUD%2Ft5NoIdUCMVhGmUuQ2LWKr0%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;898&quot; height=&quot;208&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;208&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 다른 사용자 계정으로 실행 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- User ID : ansible&lt;/p&gt;
&lt;pre id=&quot;code_1768369445026&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 배포 서버(root@server)에서 실행
# tnode1, 2, 3 모두에게 ansible 유저 생성 및 비밀번호 'qwe123' 설정
for i in {1..3}; do
    echo &quot;&amp;gt;&amp;gt;&amp;gt; tnode$i 작업 중...&quot;
    ssh root@tnode$i &quot;useradd -m ansible &amp;amp;&amp;amp; echo 'ansible:qwe123' | chpasswd&quot;
    ssh root@tnode$i &quot;echo 'ansible ALL=(ALL) NOPASSWD:ALL' &amp;gt; /etc/sudoers.d/ansible&quot;
done


# 배포 서버에서 현재 사용자의 키를 ansible 계정으로 복사
for i in {1..3}; do
    sshpass -p 'qwe123' ssh-copy-id -o StrictHostKeyChecking=no ansible@tnode$i
done


# ansible.cfg 확인
[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False

# 접속 확인
ansible web -m ping -u ansible&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FyH2g/dJMcacBQAbW/GsZnkPL5i49sT5gIqx1MV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FyH2g/dJMcacBQAbW/GsZnkPL5i49sT5gIqx1MV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FyH2g/dJMcacBQAbW/GsZnkPL5i49sT5gIqx1MV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFyH2g%2FdJMcacBQAbW%2FGsZnkPL5i49sT5gIqx1MV1%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;722&quot; height=&quot;186&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;186&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[ad-hoc] &lt;span data-token-index=&quot;1&quot;&gt;ansible shell 모듈 - &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/2.8/modules/shell_module.html&quot;&gt;https://docs.ansible.com/ansible/2.8/modules/shell_module.html&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shell은 노드들에 명령 구문을 전달하고 해당 결과를 반환하는 모듈&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768370617303&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
ansible -m shell -a uptime db

#
ansible -m shell -a &quot;free -h&quot; web

#
ansible -m shell -a &quot;tail -n 3 /etc/passwd&quot; all&lt;/code&gt;&lt;/pre&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;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pawGh/dJMcacoiX70/cQOcP0Lv8ad4wOKykbKBSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pawGh/dJMcacoiX70/cQOcP0Lv8ad4wOKykbKBSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pawGh/dJMcacoiX70/cQOcP0Lv8ad4wOKykbKBSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpawGh%2FdJMcacoiX70%2FcQOcP0Lv8ad4wOKykbKBSk%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;923&quot; height=&quot;742&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;742&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;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;&lt;span data-token-index=&quot;0&quot;&gt;첫 번째 플레이북 작성하기&lt;/span&gt; - &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/getting_started/get_started_playbook.html&quot;&gt;https://docs.ansible.com/ansible/latest/getting_started/get_started_playbook.html&lt;/a&gt; &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;플레이북은 YAML 포맷으로 작성된 텍스트 파일이며, 일반적으로 &lt;b&gt;.yml&lt;/b&gt;이라는 확장자를 사용하여 저장됩니다.&lt;/li&gt;
&lt;li&gt;플레이북은 대상 호스트나 호스트 집합에 수행할 작업을 정의하고 이를 실행합니다. 이때 특정 작업 단위를 수행하기 위해 모듈을 적용합니다.&lt;/li&gt;
&lt;li&gt;용어 - &lt;a href=&quot;https://docs.ansible.com/projects/ansible/latest/getting_started/get_started_playbook.html#creating-a-playbook&quot;&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;span data-token-index=&quot;0&quot;&gt;&lt;b&gt;1. 플레이북 작성하기&lt;/b&gt; : &lt;/span&gt;debug 모듈을 이용하여 문자열을 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;first-playbook.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768374581208&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: all
  tasks:
    - name: Print message
      debug:
        msg: Hello CloudNet@ Ansible Study&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;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;first-playbook-with-error.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768374643347&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: all
  tasks:
    - name: Print message
      debug:
      msg: Hello CloudNet@ Ansible Study&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;2. 플레이북 문법 체크하기&lt;/span&gt; : &lt;/b&gt;자체 문법 체크 옵션 제공&lt;/p&gt;
&lt;pre id=&quot;code_1768374705513&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법 체크 : 문법 오류 확인
ansible-playbook --syntax-check first-playbook.yml
ansible-playbook --syntax-check first-playbook-with-error.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdy5f1/dJMcai9QgXf/hDisBBHrWY3pzqSxXfwFek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdy5f1/dJMcai9QgXf/hDisBBHrWY3pzqSxXfwFek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdy5f1/dJMcai9QgXf/hDisBBHrWY3pzqSxXfwFek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdy5f1%2FdJMcai9QgXf%2FhDisBBHrWY3pzqSxXfwFek%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;1023&quot; height=&quot;246&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1768449954917&quot; class=&quot;yaml&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# 문법 오류 수정
---
- hosts: all
  tasks:
    - name: Print message
      debug:
        msg: &quot;Hello Ansible World!&quot;   # 들여쓰기 2칸 이동&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUpCv4/dJMcafFkYli/DGH52P7Cvl5jKDb1wIcBz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUpCv4/dJMcafFkYli/DGH52P7Cvl5jKDb1wIcBz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUpCv4/dJMcafFkYli/DGH52P7Cvl5jKDb1wIcBz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUpCv4%2FdJMcafFkYli%2FDGH52P7Cvl5jKDb1wIcBz0%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;1022&quot; height=&quot;85&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;85&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;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;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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;3. 플레이북 실행하기&lt;/span&gt; &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;ansible-playbook&lt;/b&gt; 명령어를 이용합니다. 환경 설정 파일인 &lt;b&gt;ansible.cfg&lt;/b&gt;가 존재하는 프로젝트 디렉터리 내에서 실행할 경우에는 ansible-playbook 명령어와 함께 실행하고자 하는 &lt;b&gt;플레이북 파일명&lt;/b&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;1605&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAxJkm/dJMcajnnFjc/Rge0NEf4k1IVIjnQCzrEKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAxJkm/dJMcajnnFjc/Rge0NEf4k1IVIjnQCzrEKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAxJkm/dJMcajnnFjc/Rge0NEf4k1IVIjnQCzrEKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAxJkm%2FdJMcajnnFjc%2FRge0NEf4k1IVIjnQCzrEKK%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;1605&quot; height=&quot;507&quot; data-origin-width=&quot;1605&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;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;4. 서비스 재시작하는 플레이북 작성&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;restart-service.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768450188776&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: all
  tasks:
    - name: Restart sshd service
      ansible.builtin.service:
        name: ssh # sshd
        state: restarted&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;state&lt;/b&gt; - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#parameter-state&quot;&gt;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#parameter-state&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;started&lt;/b&gt; : Start service httpd, if not started&lt;/li&gt;
&lt;li&gt;&lt;b&gt;stooped&lt;/b&gt; : Stop service httpd, if started&lt;/li&gt;
&lt;li&gt;&lt;b&gt;restarted&lt;/b&gt; : Restart service httpd, in all cases&lt;/li&gt;
&lt;li&gt;&lt;b&gt;reloaded&lt;/b&gt; : Reload service httpd, in all cases&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;5. 플레이북 실행&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768450271123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (신규터미널) 모니터링 : 서비스 재시작 실행 여부 확인
ssh tnode1 tail -f /var/log/syslog

# 실행 전 check 옵션으로 플레이북의 실행 상태를 미리 점검 가능 : 출력 중 changed=1 확인
ansible-playbook --check restart-service.yml


# 플레이북 실제 실행
ansible-playbook restart-service.yml
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BNarL/dJMcaaquHjn/kOyVJ0offpArm1BVQ5w2D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BNarL/dJMcaaquHjn/kOyVJ0offpArm1BVQ5w2D1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BNarL/dJMcaaquHjn/kOyVJ0offpArm1BVQ5w2D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBNarL%2FdJMcaaquHjn%2FkOyVJ0offpArm1BVQ5w2D1%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;1450&quot; height=&quot;243&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;243&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;1417&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIJ6VN/dJMb996b3yl/KqvrWCJAXBhqUqETdNZMWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIJ6VN/dJMb996b3yl/KqvrWCJAXBhqUqETdNZMWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIJ6VN/dJMb996b3yl/KqvrWCJAXBhqUqETdNZMWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIJ6VN%2FdJMb996b3yl%2FKqvrWCJAXBhqUqETdNZMWK%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;557&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;557&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;p data-ke-size=&quot;size16&quot;&gt;☞ tnode3의 경우 OS 가 rocky linux 9 이어서 restart-service.yml 을 실행할 경우 ssh 서비스명이&amp;nbsp; sshd 이므로 오류가 발생한다. 그러므로 OS 별 조건 분리를 통해 Debian 계열과 Redhat 계열을 분리하여 설정을 작성해야 한다.&lt;/p&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;6. &lt;span data-token-index=&quot;0&quot;&gt;OS 별 조건 분리&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768450780871&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- hosts: all
  tasks:
    - name: Restart SSH on Debian
      ansible.builtin.service:
        name: ssh
        state: restarted
      when: ansible_facts['os_family'] == 'Debian'

    - name: Restart SSH on RedHat
      ansible.builtin.service:
        name: sshd
        state: restarted
      when: ansible_facts['os_family'] == 'RedHat'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYLZCL/dJMcadHuaYn/457aN1kLuGEWJ8Hp5O1Elk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYLZCL/dJMcadHuaYn/457aN1kLuGEWJ8Hp5O1Elk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYLZCL/dJMcadHuaYn/457aN1kLuGEWJ8Hp5O1Elk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYLZCL%2FdJMcadHuaYn%2F457aN1kLuGEWJ8Hp5O1Elk%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;1427&quot; height=&quot;467&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;467&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;pre id=&quot;code_1768450932566&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# you can see the &amp;lsquo;raw&amp;rsquo; information for any host in your inventory by running this ad-hoc ansible command at the command line:
ansible tnode1 -m ansible.builtin.setup | grep -iE 'os_family|ansible_distribution'
        &quot;ansible_distribution&quot;: &quot;Ubuntu&quot;,
        &quot;ansible_distribution_file_parsed&quot;: true,
        &quot;ansible_distribution_file_path&quot;: &quot;/etc/os-release&quot;,
        &quot;ansible_distribution_file_variety&quot;: &quot;Debian&quot;,
        &quot;ansible_distribution_major_version&quot;: &quot;24&quot;,
        &quot;ansible_distribution_release&quot;: &quot;noble&quot;,
        &quot;ansible_distribution_version&quot;: &quot;24.04&quot;,
        &quot;ansible_os_family&quot;: &quot;Debian&quot;,

ansible tnode3 -m ansible.builtin.setup | grep -iE 'os_family|ansible_distribution'
        &quot;ansible_distribution&quot;: &quot;Rocky&quot;,
        &quot;ansible_distribution_file_parsed&quot;: true,
        &quot;ansible_distribution_file_path&quot;: &quot;/etc/redhat-release&quot;,
        &quot;ansible_distribution_file_variety&quot;: &quot;RedHat&quot;,
        &quot;ansible_distribution_major_version&quot;: &quot;9&quot;,
        &quot;ansible_distribution_release&quot;: &quot;Blue Onyx&quot;,
        &quot;ansible_distribution_version&quot;: &quot;9.6&quot;,
        &quot;ansible_os_family&quot;: &quot;RedHat&quot;,


# 실행 전 check 옵션으로 플레이북의 실행 상태를 미리 점검
ansible-playbook --check restart-service.yml


# 플레이북 실제 실행
ansible-playbook restart-service.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmIrOc/dJMcabXe4IU/3n1QbamKFi5gv6uwrg3QCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmIrOc/dJMcabXe4IU/3n1QbamKFi5gv6uwrg3QCK/img.png&quot; data-alt=&quot;참고로 Ubuntu도 os_family는 Debian 으로 출력 - Link&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmIrOc/dJMcabXe4IU/3n1QbamKFi5gv6uwrg3QCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmIrOc%2FdJMcabXe4IU%2F3n1QbamKFi5gv6uwrg3QCK%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;1247&quot; height=&quot;492&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참고로 Ubuntu도 os_family는 Debian 으로 출력 - Link&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1407&quot; data-origin-height=&quot;487&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGS9DP/dJMcaf6pfeN/OKAqmk2QoYWbeMgrqrB0ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGS9DP/dJMcaf6pfeN/OKAqmk2QoYWbeMgrqrB0ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGS9DP/dJMcaf6pfeN/OKAqmk2QoYWbeMgrqrB0ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGS9DP%2FdJMcaf6pfeN%2FOKAqmk2QoYWbeMgrqrB0ak%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;1407&quot; height=&quot;487&quot; data-origin-width=&quot;1407&quot; data-origin-height=&quot;487&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;1367&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JCXxf/dJMcadHua07/QxKhCIjLUkK7cDwGlJVeX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JCXxf/dJMcadHua07/QxKhCIjLUkK7cDwGlJVeX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JCXxf/dJMcadHua07/QxKhCIjLUkK7cDwGlJVeX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJCXxf%2FdJMcadHua07%2FQxKhCIjLUkK7cDwGlJVeX0%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;1367&quot; height=&quot;482&quot; data-origin-width=&quot;1367&quot; data-origin-height=&quot;482&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;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; &lt;span data-token-index=&quot;0&quot;&gt;7. 실습 완료 후 작성한 yml 파일 삭제&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768451174704&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rm -r *.yml&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;&lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt; 변수 &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&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;/blockquote&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;span style=&quot;background-color: #9feec3;&quot;&gt; &lt;b&gt;그룹 변수&lt;/b&gt; : 인벤토리에 정의된 호스트 그룹에 적용하는 변수 &lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹 변수는 인벤토리에 정의된 &lt;span data-token-index=&quot;1&quot;&gt;호스트 그룹&lt;/span&gt;에 적용하는&lt;span data-token-index=&quot;3&quot;&gt; 변수&lt;/span&gt;를 의미합니다. 따라서 인벤토리에 선언해야 하고, 선언하고자 하는 그룹명과 함께 &lt;span style=&quot;background-color: #9feec3; color: #eb5757;&quot; data-token-index=&quot;5&quot;&gt;:vars &lt;/span&gt;라는 문자열을 추가해 변수를 선언한다는 것을 알려줍니다.&amp;nbsp;&lt;/p&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;[ansible-server]&lt;/span&gt;의 &lt;span data-token-index=&quot;2&quot;&gt;my-ansible&lt;/span&gt; 디렉터리에 있는 &lt;span data-token-index=&quot;4&quot;&gt;inventory&lt;/span&gt; 파일 하단에 &lt;span style=&quot;background-color: #9feec3; color: #eb5757;&quot; data-token-index=&quot;6&quot;&gt;[all:vars] &lt;/span&gt;섹션을 선언하고 해당 섹션 아래에 &lt;span data-token-index=&quot;8&quot;&gt;user=ansible &lt;/span&gt;이라는 변수와 값을 선언합니다. 이렇게 하면 &lt;span data-token-index=&quot;10&quot;&gt;all&lt;/span&gt;이라는 그룹에서 &lt;span data-token-index=&quot;12&quot;&gt;user&lt;/span&gt;라는 변수를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제에서 all 그룹에는 web 그룹과 db 그룹이, web 그룹에는 tnode1-ubuntu.local, tnode2-ubuntu.local이 포함되며, db그룹에는 tnode3 호스트가 포함됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;inventory&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768451647882&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[web]
tnode1 ansible_python_interpreter=/usr/bin/python3
tnode2 ansible_python_interpreter=/usr/bin/python3

[db]
tnode3 ansible_python_interpreter=/usr/bin/python3

[all:children]
web
db

[all:vars]
user=testuser&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 create-user.yml 이라는 파일을 생성합니다. 해당 파일은 &lt;b&gt;사용자&lt;/b&gt;를 &lt;b&gt;생성&lt;/b&gt;하는 &lt;b&gt;태스크&lt;/b&gt;를 포함합니다. 앤서블에서 시스템 사용자(account)를 생성하기 위해서는 &lt;b&gt;ansible.builtin.user&lt;/b&gt;라는 &lt;b&gt;모듈&lt;/b&gt;을 사용합니다. 그리고 인벤토리에서 선언한 user라는 변수를 &lt;b&gt;겹중괄호&lt;/b&gt; 사이에 넣어주면, &lt;b&gt;해당 변수&lt;/b&gt;를 플레이북에서 사용할 수 있습니다. 이때 겹 중괄호와 변수명 사이는 &lt;b&gt;항상 한 칸씩 띄워주어야&lt;/b&gt; 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768451828267&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: all
  tasks:
  - name: Create User {{ user }}
    ansible.builtin.user:
      name: &quot;{{ user }}&quot;
      state: present&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;p data-ke-size=&quot;size16&quot;&gt;앞에서 생성한 플레이북을 ansible-playbook 명령어와 함께 실행합니다. 그러면 태스트명으로 &amp;ldquo;&lt;span style=&quot;background-color: #9feec3; color: #eb5757;&quot; data-token-index=&quot;1&quot;&gt;Create User {{ user }}&lt;/span&gt;&amp;rdquo; 라고 변수를 사용한 부분에 해당 변수의 값인 &lt;span data-token-index=&quot;3&quot;&gt;ansible&lt;/span&gt; 이라는 문자열이 보이는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768451899219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) 모니터링
watch -d &quot;ssh tnode1 tail -n 3 /etc/passwd&quot;

# 실행
ansible-playbook create-user.yml
...
TASK [Create User ansible] *********
...

# 한번 더 실행 : 멱등성 확인
ansible-playbook create-user.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMbGNU/dJMcaaRzj8g/aw7JyyD7cSziVKlisyBDXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMbGNU/dJMcaaRzj8g/aw7JyyD7cSziVKlisyBDXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMbGNU/dJMcaaRzj8g/aw7JyyD7cSziVKlisyBDXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMbGNU%2FdJMcaaRzj8g%2Faw7JyyD7cSziVKlisyBDXK%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;585&quot; height=&quot;133&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;133&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;1291&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qn7F7/dJMcabCVUZL/D6iLRzujepjyXun6dKv7zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qn7F7/dJMcabCVUZL/D6iLRzujepjyXun6dKv7zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qn7F7/dJMcabCVUZL/D6iLRzujepjyXun6dKv7zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqn7F7%2FdJMcabCVUZL%2FD6iLRzujepjyXun6dKv7zk%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;1291&quot; height=&quot;382&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;382&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;580&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AVCup/dJMcajt9pTj/prXWHBMpk1CD7rT6MtWonk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AVCup/dJMcajt9pTj/prXWHBMpk1CD7rT6MtWonk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AVCup/dJMcajt9pTj/prXWHBMpk1CD7rT6MtWonk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAVCup%2FdJMcajt9pTj%2FprXWHBMpk1CD7rT6MtWonk%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;580&quot; height=&quot;147&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;147&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실제 대상 호스트에서 ansible 사용자 생성 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768452409615&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# tnode1~3에서 ansible 사용자 생성 확인
#ssh tnode1 tail -n 3 /etc/passwd
#ssh tnode1 ls -l /home
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i tail -n 3 /etc/passwd; echo; done
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i ls -l /home; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lkm2X/dJMcaaRzkfx/GtLOWhL7L9OyWpUWvwHJK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lkm2X/dJMcaaRzkfx/GtLOWhL7L9OyWpUWvwHJK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lkm2X/dJMcaaRzkfx/GtLOWhL7L9OyWpUWvwHJK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLkm2X%2FdJMcaaRzkfx%2FGtLOWhL7L9OyWpUWvwHJK0%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;1203&quot; height=&quot;781&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;수동으로 tnode1 에 &lt;span style=&quot;color: #ee2323;&quot;&gt;testuser&lt;/span&gt; 사용자 삭제 후 다시 플레이북 실행 확인&lt;/p&gt;
&lt;pre id=&quot;code_1768452465657&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# tnode1 에 ansible 사용자 삭제 후 확인
ssh tnode1 userdel -r ansible
ssh tnode1 tail -n 2 /etc/passwd

# 실행
ansible-playbook create-user.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TrAXa/dJMcaiaZRUo/1MHhGWlCYtI2s9KDY9Gv4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TrAXa/dJMcaiaZRUo/1MHhGWlCYtI2s9KDY9Gv4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TrAXa/dJMcaiaZRUo/1MHhGWlCYtI2s9KDY9Gv4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTrAXa%2FdJMcaiaZRUo%2F1MHhGWlCYtI2s9KDY9Gv4K%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;1381&quot; height=&quot;392&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;392&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt; &lt;b&gt;호스트 변수&lt;/b&gt; : 말 그대로 해당 호스트에서만 사용할 수 있음&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs8n32/dJMcahXtxjc/BW3J1lDtWsmk3PSg8pk6vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs8n32/dJMcahXtxjc/BW3J1lDtWsmk3PSg8pk6vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs8n32/dJMcahXtxjc/BW3J1lDtWsmk3PSg8pk6vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs8n32%2FdJMcahXtxjc%2FBW3J1lDtWsmk3PSg8pk6vK%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;1280&quot; height=&quot;678&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;678&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 인벤토리 파일을 열고 db 그룹의 tnode-3 호스트 옆에 변수를 선언&lt;/p&gt;
&lt;pre id=&quot;code_1768452772760&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[web]
tnode1 ansible_python_interpreter=/usr/bin/python3
tnode2 ansible_python_interpreter=/usr/bin/python3

[db]
tnode3 ansible_python_interpreter=/usr/bin/python3 user=ansible1

[all:children]
web
db

[all:vars]
user=ansible&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;p data-ke-size=&quot;size18&quot;&gt;2. 플레이북 파일 작성 : hosts 를 all &amp;rarr; db&lt;/p&gt;
&lt;pre id=&quot;code_1768452886238&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db
  tasks:
  - name: Create User {{ user }}
    ansible.builtin.user:
      name: &quot;{{ user }}&quot;
      state: present&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 플레이북 실행 확인. user 명 확인 &amp;rarr; 호스트 변수와 그룹 변수의 우선순위 확인&lt;/p&gt;
&lt;pre id=&quot;code_1768452919462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) 모니터링
watch -d &quot;ssh tnode3 tail -n 3 /etc/passwd&quot;

# 실행
ansible-playbook create-user1.yml
...
TASK [Create User ansible1] *********
...

# 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i tail -n 3 /etc/passwd; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEzxah/dJMcagqHImC/0N34jpknNh2kcwNfygeTbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEzxah/dJMcagqHImC/0N34jpknNh2kcwNfygeTbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEzxah/dJMcagqHImC/0N34jpknNh2kcwNfygeTbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEzxah%2FdJMcagqHImC%2F0N34jpknNh2kcwNfygeTbK%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;702&quot; height=&quot;120&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;120&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;1201&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eCyiO7/dJMcaihLzjF/b4Dkn0qjwj7J2G9gH98pO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eCyiO7/dJMcaihLzjF/b4Dkn0qjwj7J2G9gH98pO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eCyiO7/dJMcaihLzjF/b4Dkn0qjwj7J2G9gH98pO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeCyiO7%2FdJMcaihLzjF%2Fb4Dkn0qjwj7J2G9gH98pO1%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;1201&quot; height=&quot;261&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;261&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;1285&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPL2E6/dJMcacV8Kqa/t4kUIYyGQ4ZDV3kYwQzIrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPL2E6/dJMcacV8Kqa/t4kUIYyGQ4ZDV3kYwQzIrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPL2E6/dJMcacV8Kqa/t4kUIYyGQ4ZDV3kYwQzIrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPL2E6%2FdJMcacV8Kqa%2Ft4kUIYyGQ4ZDV3kYwQzIrk%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;1285&quot; height=&quot;316&quot; data-origin-width=&quot;1285&quot; data-origin-height=&quot;316&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 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;플레이 변수&lt;/b&gt; : 플레이북 내에서 선언되는 변수, 별도 파일 분리 &lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PMLot/dJMcahb4IKM/HsWt2fv3BaoZcOtNtB7w6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PMLot/dJMcahb4IKM/HsWt2fv3BaoZcOtNtB7w6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PMLot/dJMcahb4IKM/HsWt2fv3BaoZcOtNtB7w6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPMLot%2FdJMcahb4IKM%2FHsWt2fv3BaoZcOtNtB7w6k%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;1280&quot; height=&quot;808&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;808&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;1280&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NOngo/dJMcahiRii5/XGj4ZWMGkXxqDpV34luKA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NOngo/dJMcahiRii5/XGj4ZWMGkXxqDpV34luKA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NOngo/dJMcahiRii5/XGj4ZWMGkXxqDpV34luKA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNOngo%2FdJMcahiRii5%2FXGj4ZWMGkXxqDpV34luKA1%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;1280&quot; height=&quot;659&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;659&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 플레이북 작성 : hosts 아래에 vars: 를 추가하고 그 아래에 다시 user: ansible2 라는 변수와 값을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;create-user2.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768453195485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: all
  vars:
    user: ansible2

  tasks:
  - name: Create User {{ user }}
    ansible.builtin.user:
      name: &quot;{{ user }}&quot;
      state: present&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;p data-ke-size=&quot;size18&quot;&gt;2. 플레이북 실행 확인. 인벤토리에 선언한 그룹 변수 vs 호스트 변수 vs 플레이 변수 우선 순위 확인.&lt;/p&gt;
&lt;pre id=&quot;code_1768453527381&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) 모니터링
watch -d &quot;ssh tnode3 tail -n 3 /etc/passwd&quot;

#  인벤토리에 선언한 그룹 변수와 호스트 변수 확인
cat inventory

# 실행
ansible-playbook create-user2.yml
...
TASK [Create User ansible2] *********
...

# 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i tail -n 3 /etc/passwd; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1877&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhjBjs/dJMcafk2vQS/yGXkWkkWeCKbgbX1861DM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhjBjs/dJMcafk2vQS/yGXkWkkWeCKbgbX1861DM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhjBjs/dJMcafk2vQS/yGXkWkkWeCKbgbX1861DM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhjBjs%2FdJMcafk2vQS%2FyGXkWkkWeCKbgbX1861DM1%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;1877&quot; height=&quot;513&quot; data-origin-width=&quot;1877&quot; data-origin-height=&quot;513&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;1050&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZBnaD/dJMcaajJhmi/Zw1ITrbIV0yoOV7VES9fE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZBnaD/dJMcaajJhmi/Zw1ITrbIV0yoOV7VES9fE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZBnaD/dJMcaajJhmi/Zw1ITrbIV0yoOV7VES9fE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZBnaD%2FdJMcaajJhmi%2FZw1ITrbIV0yoOV7VES9fE1%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;1050&quot; height=&quot;339&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;☞ 플레이변수가 적용된 플레이북을 실행하여 ansible2 라는 계정이 신규로 생성됨&lt;/p&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;p data-ke-size=&quot;size18&quot;&gt;3. 플레이 변수를 별도의 파일로 분리하여 정의한 후 이를 플레이북에서 선언하는 방법으로 변수를 사용해보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible&lt;span style=&quot;color: #387dc9;&quot; data-token-index=&quot;1&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: #387dc9;&quot; data-token-index=&quot;2&quot;&gt;vars&lt;/span&gt;&lt;span data-token-index=&quot;3&quot;&gt;/users.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768453886858&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir vars
echo &quot;user: ansible3&quot; &amp;gt; vars/users.yml&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;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;create-user3.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768453995988&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: all
  vars_files:
    - vars/users.yml

  tasks:
  - name: Create User {{ user }}
    ansible.builtin.user:
      name: &quot;{{ user }}&quot;
      state: present&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;p data-ke-size=&quot;size18&quot;&gt;4. 플레이북 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768454171450&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) 모니터링
watch -d &quot;ssh tnode3 tail -n 3 /etc/passwd&quot;

# 플레이 변수 파일 확인
cat vars/users.yml

# 실행
ansible-playbook create-user3.yml
...
TASK [Create User ansible3] *********
...

# 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i tail -n 4 /etc/passwd; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5jRvA/dJMcab30l0m/KFyUIwbEqnxC30AMOMS1n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5jRvA/dJMcab30l0m/KFyUIwbEqnxC30AMOMS1n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5jRvA/dJMcab30l0m/KFyUIwbEqnxC30AMOMS1n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5jRvA%2FdJMcab30l0m%2FKFyUIwbEqnxC30AMOMS1n1%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;1871&quot; height=&quot;408&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;408&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;1169&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/62gFl/dJMcajnnGU7/B73XY1KY3Dkhev5IRcisEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/62gFl/dJMcajnnGU7/B73XY1KY3Dkhev5IRcisEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/62gFl/dJMcajnnGU7/B73XY1KY3Dkhev5IRcisEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F62gFl%2FdJMcajnnGU7%2FB73XY1KY3Dkhev5IRcisEK%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;1169&quot; height=&quot;375&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;375&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 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #9feec3;&quot; data-token-index=&quot;0&quot;&gt;추가 변수&lt;/span&gt;&lt;/b&gt; : 외부에서 ansible-playbook를 &lt;span data-token-index=&quot;2&quot;&gt;실행&lt;/span&gt; 할 때 함께 &lt;span data-token-index=&quot;4&quot;&gt;파라미터&lt;/span&gt;로 넘겨주는 &lt;span data-token-index=&quot;6&quot;&gt;변수&lt;/span&gt;를 의미. 앞에 변수 중 &lt;span data-token-index=&quot;8&quot;&gt;우선순위&lt;/span&gt;가&lt;span data-token-index=&quot;10&quot;&gt; 가장 높음&lt;/span&gt;.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 실행 시 -e(&lt;b&gt;extra_vars&lt;/b&gt; 약자) 옵션으로 추가 변수 선언&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; my-ansible/&lt;b&gt;create-user3.yml&lt;/b&gt; &amp;larr; 바로 위 플레이 변수에서 작성한 플레이북임&lt;/p&gt;
&lt;pre id=&quot;code_1768454431418&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) 모니터링
watch -d &quot;ssh tnode3 tail -n 3 /etc/passwd&quot;

# 실행
ansible-playbook -e user=ansible4 create-user3.yml
...
TASK [Create User ansible4] *********
...

# 확인
for i in {1..3}; do echo &quot;&amp;gt;&amp;gt; tnode$i &amp;lt;&amp;lt;&quot;; ssh tnode$i tail -n 5 /etc/passwd; echo; done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQEwr6/dJMcad1LXFs/Y8OIfY4WjXejD160f1TFYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQEwr6/dJMcad1LXFs/Y8OIfY4WjXejD160f1TFYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQEwr6/dJMcad1LXFs/Y8OIfY4WjXejD160f1TFYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQEwr6%2FdJMcad1LXFs%2FY8OIfY4WjXejD160f1TFYk%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;1838&quot; height=&quot;421&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;421&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;1182&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UsEgQ/dJMcajt9qL0/liUEMHRGyykm6Mu2NFQMM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UsEgQ/dJMcajt9qL0/liUEMHRGyykm6Mu2NFQMM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UsEgQ/dJMcajt9qL0/liUEMHRGyykm6Mu2NFQMM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUsEgQ%2FdJMcajt9qL0%2FliUEMHRGyykm6Mu2NFQMM1%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;1182&quot; height=&quot;431&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;431&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;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;&lt;span style=&quot;background-color: #99cefa; color: #ee2323;&quot; data-token-index=&quot;0&quot;&gt;&amp;clubs; 변수 우선 순위 &lt;/span&gt;&lt;/b&gt;: 추가변수(실행 시 파라미터) &amp;gt; 플레이 변수 &amp;gt; 호스트 변수 &amp;gt; 그룹 변수&lt;/p&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; &lt;span data-token-index=&quot;0&quot;&gt;작업 변수&lt;/span&gt; &lt;/b&gt;: 플레이북의&lt;span data-token-index=&quot;2&quot;&gt; 수행 결과&lt;/span&gt;를 &lt;span data-token-index=&quot;4&quot;&gt;저장&lt;/span&gt;. 특정 작업 수행 후 그 결과를 후속 작업에서 사용할 때 주로 사용됨&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일 복사 후 수정, Create User 태스트에 register: result 라는 문구를 추가.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;register를 선언하면 태스트를 실행한 결과를 register 다음에 나오는 result라는 변수에 저장하겠다는 의미가 됩니다.&lt;/li&gt;
&lt;li&gt;그리고 result라는 변수에 저장한 결과를 debug 모듈을 통해 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;create-user4.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768454820279&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db
  tasks:
  - name: Create User {{ user }}
    ansible.builtin.user:
      name: &quot;{{ user }}&quot;
      state: present
    register: result
  
  - ansible.builtin.debug:
      var: result&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;p data-ke-size=&quot;size18&quot;&gt;2. 추가 변수와 함께 플레이북 실행 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QoBcI/dJMcabiD777/t58d0ZPsTfVcReff5yiQn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QoBcI/dJMcabiD777/t58d0ZPsTfVcReff5yiQn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QoBcI/dJMcabiD777/t58d0ZPsTfVcReff5yiQn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQoBcI%2FdJMcabiD777%2Ft58d0ZPsTfVcReff5yiQn1%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;1732&quot; height=&quot;623&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;623&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;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;[도전과제 1]&amp;nbsp;&lt;/b&gt; 생성된 user : testuser 를 ansible.builtin.user 모듈을 통해서 제거&lt;/h4&gt;
&lt;pre id=&quot;code_1768455502231&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: db
  tasks:
    - name: &quot;testuser 계정 삭제&quot;
      ansible.builtin.user:
        name: testuser
        state: absent
        remove: yes  # 홈 디렉토리와 메일함도 함께 삭제하려면 추가 
      register: result

    - name: 삭제 결과 출력
      ansible.builtin.debug:
        var: result&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;[도전관제1 - 결과]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbP0gC/dJMcac9DmVA/CX36GuJ7Z3YIf0Yy3U7s41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbP0gC/dJMcac9DmVA/CX36GuJ7Z3YIf0Yy3U7s41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbP0gC/dJMcac9DmVA/CX36GuJ7Z3YIf0Yy3U7s41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbP0gC%2FdJMcac9DmVA%2FCX36GuJ7Z3YIf0Yy3U7s41%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;1875&quot; height=&quot;529&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;529&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;[도전과제 2] &lt;/b&gt;&lt;span&gt;&amp;nbsp;관리 대상에&lt;/span&gt; uptime 을 ansible.builtin.debug 모듈을 통해서 제거&lt;/h4&gt;
&lt;pre id=&quot;code_1768456876843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: all
  gather_facts: yes  # OS 계열을 파악하기 위해 Facts 수집
  
  tasks:
    # 1. 공통적으로 uptime 명령어를 실행하여 변수에 저장
    - name: 
      ansible.builtin.command: uptime
      register: uptime_raw

    # 2. 우분투(Debian 계열)일 경우 실행 결과 출력
    - name:
      ansible.builtin.debug:
        msg: &quot;[Ubuntu/Debian] {{ inventory_hostname }} 상태: {{ uptime_raw.stdout }}&quot;
      when: ansible_os_family == &quot;Debian&quot;

    # 3. 로키 리눅스(RedHat 계열)일 경우 실행 결과 출력
    - name:
      ansible.builtin.debug:
        msg: &quot;[Rocky/RedHat] {{ inventory_hostname }} 상태: {{ uptime_raw.stdout }}&quot;
      when: ansible_os_family == &quot;RedHat&quot;&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;[도전관제2 - 결과]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KhT28/dJMb99ZqSpA/g4bkG1KP4lRF4F1WHDgVj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KhT28/dJMb99ZqSpA/g4bkG1KP4lRF4F1WHDgVj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KhT28/dJMb99ZqSpA/g4bkG1KP4lRF4F1WHDgVj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKhT28%2FdJMb99ZqSpA%2Fg4bkG1KP4lRF4F1WHDgVj1%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;1328&quot; height=&quot;696&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;696&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;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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt; Facts &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;팩트 Facts는 앤서블이 관리 호스트에서 자동으로 검색한 변수(자동 예약 변수)입니다. 팩트에는 플레이, 조건문, 반복문 또는 관리 호스트에서 수집한 값에 의존하는 기타 명령문의 일반 변수처럼 사용 가능한 호스트별 정보가 포함되어 있습니다. 관리 호스트에서 수집된 일부 팩트에는 다음 내용들이 포함될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;li&gt;네트워크 인터페이스 이름&lt;/li&gt;
&lt;li&gt;운영체제 버전&lt;/li&gt;
&lt;li&gt;CPU 개수&lt;/li&gt;
&lt;li&gt;사용 가능한 메모리&lt;/li&gt;
&lt;li&gt;스토리지 장치의 크기 및 여유 공간&lt;/li&gt;
&lt;li&gt;등등 ...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;팩트 사용하기&lt;/span&gt; :&lt;/b&gt; 기본 활성화로, 플레이북을 실행할 때 자동으로 팩트가 수집됨. ansible_facts 변수로 사용.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;facts.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768458439092&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db

  tasks:
  - name: Print all facts
    ansible.builtin.debug:
      var: ansible_facts&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;2. 플레이북 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768458471125&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
ansible-playbook facts.yml
...
TASK [Gathering Facts] ***********************************************************************************************************************************************************************
ok: [tnode3]

TASK [Print all facts] ***********************************************************************************************************************************************************************
ok: [tnode3] =&amp;gt; {
...
  &quot;hostname&quot;: &quot;tnode3&quot;,
...
  &quot;default_ipv4&quot;: {
      &quot;address&quot;: &quot;10.10.1.13&quot;,
...
  &quot;os_family&quot;: &quot;Debian&quot;,    # 참고로 Ubuntu도 os_family는 Debian 으로 출력 - Link
...&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[출력결과]&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1768458626892&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@server:~/my-ansible# ansible-playbook facts.yml

PLAY [db] ************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************
ok: [tnode3]

TASK [Print all facts] ***********************************************************************************************************
ok: [tnode3] =&amp;gt; {
    &quot;ansible_facts&quot;: {
        &quot;all_ipv4_addresses&quot;: [
            &quot;100.100.100.248&quot;
        ],
        &quot;all_ipv6_addresses&quot;: [
            &quot;fe80::f816:3eff:fe97:a6bc&quot;
        ],
        &quot;ansible_local&quot;: {},
        &quot;apparmor&quot;: {
            &quot;status&quot;: &quot;disabled&quot;
        },
        &quot;architecture&quot;: &quot;x86_64&quot;,
        &quot;bios_date&quot;: &quot;04/01/2014&quot;,
        &quot;bios_vendor&quot;: &quot;SeaBIOS&quot;,
        &quot;bios_version&quot;: &quot;1.16.3-4.el9&quot;,
        &quot;board_asset_tag&quot;: &quot;NA&quot;,
        &quot;board_name&quot;: &quot;NA&quot;,
        &quot;board_serial&quot;: &quot;NA&quot;,
        &quot;board_vendor&quot;: &quot;NA&quot;,
        &quot;board_version&quot;: &quot;NA&quot;,
        &quot;chassis_asset_tag&quot;: &quot;NA&quot;,
        &quot;chassis_serial&quot;: &quot;NA&quot;,
        &quot;chassis_vendor&quot;: &quot;Red Hat&quot;,
        &quot;chassis_version&quot;: &quot;RHEL 7.6.0 PC (i440FX + PIIX, 1996)&quot;,
        &quot;cmdline&quot;: {
            &quot;BOOT_IMAGE&quot;: &quot;(hd0,gpt3)/vmlinuz-5.14.0-570.17.1.el9_6.x86_64&quot;,
            &quot;console&quot;: &quot;ttyS0,115200n8&quot;,
            &quot;crashkernel&quot;: &quot;1G-4G:192M,4G-64G:256M,64G-:512M&quot;,
            &quot;net.ifnames&quot;: &quot;0&quot;,
            &quot;no_timer_check&quot;: true,
            &quot;root&quot;: &quot;UUID=9e80229d-48ee-4412-ae76-c18439c52af8&quot;
        },
        &quot;date_time&quot;: {
            &quot;date&quot;: &quot;2026-01-15&quot;,
            &quot;day&quot;: &quot;15&quot;,
            &quot;epoch&quot;: &quot;1768458542&quot;,
            &quot;epoch_int&quot;: &quot;1768458542&quot;,
            &quot;hour&quot;: &quot;15&quot;,
            &quot;iso8601&quot;: &quot;2026-01-15T06:29:02Z&quot;,
            &quot;iso8601_basic&quot;: &quot;20260115T152902301907&quot;,
            &quot;iso8601_basic_short&quot;: &quot;20260115T152902&quot;,
            &quot;iso8601_micro&quot;: &quot;2026-01-15T06:29:02.301907Z&quot;,
            &quot;minute&quot;: &quot;29&quot;,
            &quot;month&quot;: &quot;01&quot;,
            &quot;second&quot;: &quot;02&quot;,
            &quot;time&quot;: &quot;15:29:02&quot;,
            &quot;tz&quot;: &quot;KST&quot;,
            &quot;tz_dst&quot;: &quot;KST&quot;,
            &quot;tz_offset&quot;: &quot;+0900&quot;,
            &quot;weekday&quot;: &quot;Thursday&quot;,
            &quot;weekday_number&quot;: &quot;4&quot;,
            &quot;weeknumber&quot;: &quot;02&quot;,
            &quot;year&quot;: &quot;2026&quot;
        },
        &quot;default_ipv4&quot;: {
            &quot;address&quot;: &quot;100.100.100.248&quot;,
            &quot;alias&quot;: &quot;eth0&quot;,
            &quot;broadcast&quot;: &quot;100.100.100.255&quot;,
            &quot;gateway&quot;: &quot;100.100.100.254&quot;,
            &quot;interface&quot;: &quot;eth0&quot;,
            &quot;macaddress&quot;: &quot;fa:16:3e:97:a6:bc&quot;,
            &quot;mtu&quot;: 1450,
            &quot;netmask&quot;: &quot;255.255.255.0&quot;,
            &quot;network&quot;: &quot;100.100.100.0&quot;,
            &quot;prefix&quot;: &quot;24&quot;,
            &quot;type&quot;: &quot;ether&quot;
        },
        &quot;default_ipv6&quot;: {},
        &quot;device_links&quot;: {
            &quot;ids&quot;: {
                &quot;vda&quot;: [
                    &quot;virtio-21f8752b-eda7-4c34-a&quot;
                ],
                &quot;vda1&quot;: [
                    &quot;virtio-21f8752b-eda7-4c34-a-part1&quot;
                ],
                &quot;vda2&quot;: [
                    &quot;virtio-21f8752b-eda7-4c34-a-part2&quot;
                ],
                &quot;vda3&quot;: [
                    &quot;virtio-21f8752b-eda7-4c34-a-part3&quot;
                ],
                &quot;vda4&quot;: [
                    &quot;virtio-21f8752b-eda7-4c34-a-part4&quot;
                ]
            },
            &quot;labels&quot;: {
                &quot;vda2&quot;: [
                    &quot;EFI&quot;
                ],
                &quot;vda3&quot;: [
                    &quot;BOOT&quot;
                ],
                &quot;vda4&quot;: [
                    &quot;rocky&quot;
                ]
            },
            &quot;masters&quot;: {},
            &quot;uuids&quot;: {
                &quot;vda2&quot;: [
                    &quot;DFA8-417E&quot;
                ],
                &quot;vda3&quot;: [
                    &quot;17cc1761-0d3c-44cb-ae30-a94796e7ac81&quot;
                ],
                &quot;vda4&quot;: [
                    &quot;9e80229d-48ee-4412-ae76-c18439c52af8&quot;
                ]
            }
        },
        &quot;devices&quot;: {
            &quot;vda&quot;: {
                &quot;holders&quot;: [],
                &quot;host&quot;: &quot;&quot;,
                &quot;links&quot;: {
                    &quot;ids&quot;: [
                        &quot;virtio-21f8752b-eda7-4c34-a&quot;
                    ],
                    &quot;labels&quot;: [],
                    &quot;masters&quot;: [],
                    &quot;uuids&quot;: []
                },
                &quot;model&quot;: null,
                &quot;partitions&quot;: {
                    &quot;vda1&quot;: {
                        &quot;holders&quot;: [],
                        &quot;links&quot;: {
                            &quot;ids&quot;: [
                                &quot;virtio-21f8752b-eda7-4c34-a-part1&quot;
                            ],
                            &quot;labels&quot;: [],
                            &quot;masters&quot;: [],
                            &quot;uuids&quot;: []
                        },
                        &quot;sectors&quot;: 4096,
                        &quot;sectorsize&quot;: 512,
                        &quot;size&quot;: &quot;2.00 MB&quot;,
                        &quot;start&quot;: &quot;2048&quot;,
                        &quot;uuid&quot;: null
                    },
                    &quot;vda2&quot;: {
                        &quot;holders&quot;: [],
                        &quot;links&quot;: {
                            &quot;ids&quot;: [
                                &quot;virtio-21f8752b-eda7-4c34-a-part2&quot;
                            ],
                            &quot;labels&quot;: [
                                &quot;EFI&quot;
                            ],
                            &quot;masters&quot;: [],
                            &quot;uuids&quot;: [
                                &quot;DFA8-417E&quot;
                            ]
                        },
                        &quot;sectors&quot;: 204800,
                        &quot;sectorsize&quot;: 512,
                        &quot;size&quot;: &quot;100.00 MB&quot;,
                        &quot;start&quot;: &quot;6144&quot;,
                        &quot;uuid&quot;: &quot;DFA8-417E&quot;
                    },
                    &quot;vda3&quot;: {
                        &quot;holders&quot;: [],
                        &quot;links&quot;: {
                            &quot;ids&quot;: [
                                &quot;virtio-21f8752b-eda7-4c34-a-part3&quot;
                            ],
                            &quot;labels&quot;: [
                                &quot;BOOT&quot;
                            ],
                            &quot;masters&quot;: [],
                            &quot;uuids&quot;: [
                                &quot;17cc1761-0d3c-44cb-ae30-a94796e7ac81&quot;
                            ]
                        },
                        &quot;sectors&quot;: 2048000,
                        &quot;sectorsize&quot;: 512,
                        &quot;size&quot;: &quot;1000.00 MB&quot;,
                        &quot;start&quot;: &quot;210944&quot;,
                        &quot;uuid&quot;: &quot;17cc1761-0d3c-44cb-ae30-a94796e7ac81&quot;
                    },
                    &quot;vda4&quot;: {
                        &quot;holders&quot;: [],
                        &quot;links&quot;: {
                            &quot;ids&quot;: [
                                &quot;virtio-21f8752b-eda7-4c34-a-part4&quot;
                            ],
                            &quot;labels&quot;: [
                                &quot;rocky&quot;
                            ],
                            &quot;masters&quot;: [],
                            &quot;uuids&quot;: [
                                &quot;9e80229d-48ee-4412-ae76-c18439c52af8&quot;
                            ]
                        },
                        &quot;sectors&quot;: 123570143,
                        &quot;sectorsize&quot;: 512,
                        &quot;size&quot;: &quot;58.92 GB&quot;,
                        &quot;start&quot;: &quot;2258944&quot;,
                        &quot;uuid&quot;: &quot;9e80229d-48ee-4412-ae76-c18439c52af8&quot;
                    }
                },
                &quot;removable&quot;: &quot;0&quot;,
                &quot;rotational&quot;: &quot;1&quot;,
                &quot;sas_address&quot;: null,
                &quot;sas_device_handle&quot;: null,
                &quot;scheduler_mode&quot;: &quot;mq-deadline&quot;,
                &quot;sectors&quot;: 125829120,
                &quot;sectorsize&quot;: &quot;512&quot;,
                &quot;size&quot;: &quot;60.00 GB&quot;,
                &quot;support_discard&quot;: &quot;0&quot;,
                &quot;vendor&quot;: &quot;0x1af4&quot;,
                &quot;virtual&quot;: 1
            }
        },
        &quot;distribution&quot;: &quot;Rocky&quot;,
        &quot;distribution_file_parsed&quot;: true,
        &quot;distribution_file_path&quot;: &quot;/etc/redhat-release&quot;,
        &quot;distribution_file_variety&quot;: &quot;RedHat&quot;,
        &quot;distribution_major_version&quot;: &quot;9&quot;,
        &quot;distribution_release&quot;: &quot;Blue Onyx&quot;,
        &quot;distribution_version&quot;: &quot;9.6&quot;,
        &quot;dns&quot;: {
            &quot;nameservers&quot;: [
                &quot;8.8.8.8&quot;
            ]
        },
        &quot;domain&quot;: &quot;novalocal&quot;,
        &quot;effective_group_id&quot;: 0,
        &quot;effective_user_id&quot;: 0,
        &quot;env&quot;: {
            &quot;BASH_FUNC_which%%&quot;: &quot;() {  ( alias;\n eval ${which_declare} ) | /usr/bin/which --tty-only --read-alias --read-functio                                                      ns --show-tilde --show-dot $@\n}&quot;,
            &quot;DBUS_SESSION_BUS_ADDRESS&quot;: &quot;unix:path=/run/user/0/bus&quot;,
            &quot;DEBUGINFOD_IMA_CERT_PATH&quot;: &quot;/etc/keys/ima:&quot;,
            &quot;DEBUGINFOD_URLS&quot;: &quot;https://debuginfod.rockylinux.org/ &quot;,
            &quot;HOME&quot;: &quot;/root&quot;,
            &quot;LANG&quot;: &quot;en_US.UTF-8&quot;,
            &quot;LESSOPEN&quot;: &quot;||/usr/bin/lesspipe.sh %s&quot;,
            &quot;LOGNAME&quot;: &quot;root&quot;,
            &quot;LS_COLORS&quot;: &quot;rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;37;41:                                                      su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=0                                                      1;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01                                                      ;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01                                                      ;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio                                                      =01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.m                                                      jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;3                                                      5:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm                                                      =01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.                                                      rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.                                                      cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.m4a=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36                                                      :*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.oga=01;36:*.opus=01;36:*.spx=01;36:*.xspf=01;36:&quot;,
            &quot;MOTD_SHOWN&quot;: &quot;pam&quot;,
            &quot;PATH&quot;: &quot;/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin&quot;,
            &quot;PWD&quot;: &quot;/root&quot;,
            &quot;SELINUX_LEVEL_REQUESTED&quot;: &quot;&quot;,
            &quot;SELINUX_ROLE_REQUESTED&quot;: &quot;&quot;,
            &quot;SELINUX_USE_CURRENT_RANGE&quot;: &quot;&quot;,
            &quot;SHELL&quot;: &quot;/bin/bash&quot;,
            &quot;SHLVL&quot;: &quot;1&quot;,
            &quot;SSH_CLIENT&quot;: &quot;100.100.100.245 59842 22&quot;,
            &quot;SSH_CONNECTION&quot;: &quot;100.100.100.245 59842 100.100.100.248 22&quot;,
            &quot;SSH_TTY&quot;: &quot;/dev/pts/0&quot;,
            &quot;TERM&quot;: &quot;xterm&quot;,
            &quot;USER&quot;: &quot;root&quot;,
            &quot;XDG_RUNTIME_DIR&quot;: &quot;/run/user/0&quot;,
            &quot;XDG_SESSION_CLASS&quot;: &quot;user&quot;,
            &quot;XDG_SESSION_ID&quot;: &quot;2224&quot;,
            &quot;XDG_SESSION_TYPE&quot;: &quot;tty&quot;,
            &quot;_&quot;: &quot;/usr/bin/python3&quot;,
            &quot;which_declare&quot;: &quot;declare -f&quot;
        },
        &quot;eth0&quot;: {
            &quot;active&quot;: true,
            &quot;device&quot;: &quot;eth0&quot;,
            &quot;features&quot;: {
                &quot;esp_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;esp_tx_csum_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;generic_receive_offload&quot;: &quot;on&quot;,
                &quot;generic_segmentation_offload&quot;: &quot;on&quot;,
                &quot;highdma&quot;: &quot;on [fixed]&quot;,
                &quot;hsr_dup_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_fwd_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_tag_ins_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_tag_rm_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hw_tc_offload&quot;: &quot;off [fixed]&quot;,
                &quot;l2_fwd_offload&quot;: &quot;off [fixed]&quot;,
                &quot;large_receive_offload&quot;: &quot;off [fixed]&quot;,
                &quot;loopback&quot;: &quot;off [fixed]&quot;,
                &quot;macsec_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;ntuple_filters&quot;: &quot;off [fixed]&quot;,
                &quot;receive_hashing&quot;: &quot;off [fixed]&quot;,
                &quot;rx_all&quot;: &quot;off [fixed]&quot;,
                &quot;rx_checksumming&quot;: &quot;on [fixed]&quot;,
                &quot;rx_fcs&quot;: &quot;off [fixed]&quot;,
                &quot;rx_gro_hw&quot;: &quot;on&quot;,
                &quot;rx_gro_list&quot;: &quot;off&quot;,
                &quot;rx_udp_gro_forwarding&quot;: &quot;off&quot;,
                &quot;rx_udp_tunnel_port_offload&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_filter&quot;: &quot;on [fixed]&quot;,
                &quot;rx_vlan_offload&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_stag_filter&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_stag_hw_parse&quot;: &quot;off [fixed]&quot;,
                &quot;scatter_gather&quot;: &quot;on&quot;,
                &quot;tcp_segmentation_offload&quot;: &quot;on&quot;,
                &quot;tls_hw_record&quot;: &quot;off [fixed]&quot;,
                &quot;tls_hw_rx_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tls_hw_tx_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_fcoe_crc&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_ip_generic&quot;: &quot;on&quot;,
                &quot;tx_checksum_ipv4&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_ipv6&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_sctp&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksumming&quot;: &quot;on&quot;,
                &quot;tx_esp_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_fcoe_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gre_csum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gre_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gso_list&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gso_partial&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gso_robust&quot;: &quot;on [fixed]&quot;,
                &quot;tx_ipxip4_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_ipxip6_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_nocache_copy&quot;: &quot;off&quot;,
                &quot;tx_scatter_gather&quot;: &quot;on&quot;,
                &quot;tx_scatter_gather_fraglist&quot;: &quot;off [fixed]&quot;,
                &quot;tx_sctp_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_tcp6_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp_ecn_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp_mangleid_segmentation&quot;: &quot;off&quot;,
                &quot;tx_tcp_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tunnel_remcsum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_udp_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_udp_tnl_csum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_udp_tnl_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_vlan_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tx_vlan_stag_hw_insert&quot;: &quot;off [fixed]&quot;,
                &quot;vlan_challenged&quot;: &quot;off [fixed]&quot;
            },
            &quot;hw_timestamp_filters&quot;: [],
            &quot;ipv4&quot;: {
                &quot;address&quot;: &quot;100.100.100.248&quot;,
                &quot;broadcast&quot;: &quot;100.100.100.255&quot;,
                &quot;netmask&quot;: &quot;255.255.255.0&quot;,
                &quot;network&quot;: &quot;100.100.100.0&quot;,
                &quot;prefix&quot;: &quot;24&quot;
            },
            &quot;ipv6&quot;: [
                {
                    &quot;address&quot;: &quot;fe80::f816:3eff:fe97:a6bc&quot;,
                    &quot;prefix&quot;: &quot;64&quot;,
                    &quot;scope&quot;: &quot;link&quot;
                }
            ],
            &quot;macaddress&quot;: &quot;fa:16:3e:97:a6:bc&quot;,
            &quot;module&quot;: &quot;virtio_net&quot;,
            &quot;mtu&quot;: 1450,
            &quot;pciid&quot;: &quot;virtio1&quot;,
            &quot;promisc&quot;: false,
            &quot;speed&quot;: -1,
            &quot;timestamping&quot;: [],
            &quot;type&quot;: &quot;ether&quot;
        },
        &quot;fibre_channel_wwn&quot;: [],
        &quot;fips&quot;: false,
        &quot;flags&quot;: [
            &quot;fpu&quot;,
            &quot;vme&quot;,
            &quot;de&quot;,
            &quot;pse&quot;,
            &quot;tsc&quot;,
            &quot;msr&quot;,
            &quot;pae&quot;,
            &quot;mce&quot;,
            &quot;cx8&quot;,
            &quot;apic&quot;,
            &quot;sep&quot;,
            &quot;mtrr&quot;,
            &quot;pge&quot;,
            &quot;mca&quot;,
            &quot;cmov&quot;,
            &quot;pat&quot;,
            &quot;pse36&quot;,
            &quot;clflush&quot;,
            &quot;mmx&quot;,
            &quot;fxsr&quot;,
            &quot;sse&quot;,
            &quot;sse2&quot;,
            &quot;syscall&quot;,
            &quot;nx&quot;,
            &quot;pdpe1gb&quot;,
            &quot;rdtscp&quot;,
            &quot;lm&quot;,
            &quot;constant_tsc&quot;,
            &quot;rep_good&quot;,
            &quot;nopl&quot;,
            &quot;xtopology&quot;,
            &quot;cpuid&quot;,
            &quot;tsc_known_freq&quot;,
            &quot;pni&quot;,
            &quot;pclmulqdq&quot;,
            &quot;vmx&quot;,
            &quot;ssse3&quot;,
            &quot;cx16&quot;,
            &quot;pcid&quot;,
            &quot;sse4_1&quot;,
            &quot;sse4_2&quot;,
            &quot;x2apic&quot;,
            &quot;popcnt&quot;,
            &quot;tsc_deadline_timer&quot;,
            &quot;aes&quot;,
            &quot;hypervisor&quot;,
            &quot;lahf_lm&quot;,
            &quot;cpuid_fault&quot;,
            &quot;pti&quot;,
            &quot;tpr_shadow&quot;,
            &quot;flexpriority&quot;,
            &quot;ept&quot;,
            &quot;vpid&quot;,
            &quot;tsc_adjust&quot;,
            &quot;arat&quot;,
            &quot;vnmi&quot;,
            &quot;umip&quot;,
            &quot;arch_capabilities&quot;
        ],
        &quot;form_factor&quot;: &quot;Other&quot;,
        &quot;fqdn&quot;: &quot;tnode3.novalocal&quot;,
        &quot;gather_subset&quot;: [
            &quot;all&quot;
        ],
        &quot;hostname&quot;: &quot;tnode3&quot;,
        &quot;hostnqn&quot;: &quot;&quot;,
        &quot;interfaces&quot;: [
            &quot;eth0&quot;,
            &quot;lo&quot;
        ],
        &quot;is_chroot&quot;: false,
        &quot;iscsi_iqn&quot;: &quot;&quot;,
        &quot;kernel&quot;: &quot;5.14.0-570.17.1.el9_6.x86_64&quot;,
        &quot;kernel_version&quot;: &quot;#1 SMP PREEMPT_DYNAMIC Fri May 23 22:47:01 UTC 2025&quot;,
        &quot;lo&quot;: {
            &quot;active&quot;: true,
            &quot;device&quot;: &quot;lo&quot;,
            &quot;features&quot;: {
                &quot;esp_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;esp_tx_csum_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;generic_receive_offload&quot;: &quot;on&quot;,
                &quot;generic_segmentation_offload&quot;: &quot;on&quot;,
                &quot;highdma&quot;: &quot;on [fixed]&quot;,
                &quot;hsr_dup_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_fwd_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_tag_ins_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hsr_tag_rm_offload&quot;: &quot;off [fixed]&quot;,
                &quot;hw_tc_offload&quot;: &quot;off [fixed]&quot;,
                &quot;l2_fwd_offload&quot;: &quot;off [fixed]&quot;,
                &quot;large_receive_offload&quot;: &quot;off [fixed]&quot;,
                &quot;loopback&quot;: &quot;on [fixed]&quot;,
                &quot;macsec_hw_offload&quot;: &quot;off [fixed]&quot;,
                &quot;ntuple_filters&quot;: &quot;off [fixed]&quot;,
                &quot;receive_hashing&quot;: &quot;off [fixed]&quot;,
                &quot;rx_all&quot;: &quot;off [fixed]&quot;,
                &quot;rx_checksumming&quot;: &quot;on [fixed]&quot;,
                &quot;rx_fcs&quot;: &quot;off [fixed]&quot;,
                &quot;rx_gro_hw&quot;: &quot;off [fixed]&quot;,
                &quot;rx_gro_list&quot;: &quot;off&quot;,
                &quot;rx_udp_gro_forwarding&quot;: &quot;off&quot;,
                &quot;rx_udp_tunnel_port_offload&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_filter&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_offload&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_stag_filter&quot;: &quot;off [fixed]&quot;,
                &quot;rx_vlan_stag_hw_parse&quot;: &quot;off [fixed]&quot;,
                &quot;scatter_gather&quot;: &quot;on&quot;,
                &quot;tcp_segmentation_offload&quot;: &quot;on&quot;,
                &quot;tls_hw_record&quot;: &quot;off [fixed]&quot;,
                &quot;tls_hw_rx_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tls_hw_tx_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_fcoe_crc&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_ip_generic&quot;: &quot;on [fixed]&quot;,
                &quot;tx_checksum_ipv4&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_ipv6&quot;: &quot;off [fixed]&quot;,
                &quot;tx_checksum_sctp&quot;: &quot;on [fixed]&quot;,
                &quot;tx_checksumming&quot;: &quot;on&quot;,
                &quot;tx_esp_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_fcoe_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gre_csum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gre_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gso_list&quot;: &quot;on&quot;,
                &quot;tx_gso_partial&quot;: &quot;off [fixed]&quot;,
                &quot;tx_gso_robust&quot;: &quot;off [fixed]&quot;,
                &quot;tx_ipxip4_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_ipxip6_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_nocache_copy&quot;: &quot;off [fixed]&quot;,
                &quot;tx_scatter_gather&quot;: &quot;on [fixed]&quot;,
                &quot;tx_scatter_gather_fraglist&quot;: &quot;on [fixed]&quot;,
                &quot;tx_sctp_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp6_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp_ecn_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp_mangleid_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tcp_segmentation&quot;: &quot;on&quot;,
                &quot;tx_tunnel_remcsum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_udp_segmentation&quot;: &quot;on&quot;,
                &quot;tx_udp_tnl_csum_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_udp_tnl_segmentation&quot;: &quot;off [fixed]&quot;,
                &quot;tx_vlan_offload&quot;: &quot;off [fixed]&quot;,
                &quot;tx_vlan_stag_hw_insert&quot;: &quot;off [fixed]&quot;,
                &quot;vlan_challenged&quot;: &quot;on [fixed]&quot;
            },
            &quot;hw_timestamp_filters&quot;: [],
            &quot;ipv4&quot;: {
                &quot;address&quot;: &quot;127.0.0.1&quot;,
                &quot;broadcast&quot;: &quot;&quot;,
                &quot;netmask&quot;: &quot;255.0.0.0&quot;,
                &quot;network&quot;: &quot;127.0.0.0&quot;,
                &quot;prefix&quot;: &quot;8&quot;
            },
            &quot;ipv6&quot;: [
                {
                    &quot;address&quot;: &quot;::1&quot;,
                    &quot;prefix&quot;: &quot;128&quot;,
                    &quot;scope&quot;: &quot;host&quot;
                }
            ],
            &quot;mtu&quot;: 65536,
            &quot;promisc&quot;: false,
            &quot;timestamping&quot;: [],
            &quot;type&quot;: &quot;loopback&quot;
        },
        &quot;loadavg&quot;: {
            &quot;15m&quot;: 0.36,
            &quot;1m&quot;: 0.68,
            &quot;5m&quot;: 0.44
        },
        &quot;locally_reachable_ips&quot;: {
            &quot;ipv4&quot;: [
                &quot;100.100.100.248&quot;,
                &quot;127.0.0.0/8&quot;,
                &quot;127.0.0.1&quot;
            ],
            &quot;ipv6&quot;: [
                &quot;::1&quot;,
                &quot;fe80::f816:3eff:fe97:a6bc&quot;
            ]
        },
        &quot;lsb&quot;: {},
        &quot;lvm&quot;: &quot;N/A&quot;,
        &quot;machine&quot;: &quot;x86_64&quot;,
        &quot;machine_id&quot;: &quot;1768bbd2179641ffab980faaef7d2b39&quot;,
        &quot;memfree_mb&quot;: 822,
        &quot;memory_mb&quot;: {
            &quot;nocache&quot;: {
                &quot;free&quot;: 1556,
                &quot;used&quot;: 215
            },
            &quot;real&quot;: {
                &quot;free&quot;: 822,
                &quot;total&quot;: 1771,
                &quot;used&quot;: 949
            },
            &quot;swap&quot;: {
                &quot;cached&quot;: 0,
                &quot;free&quot;: 0,
                &quot;total&quot;: 0,
                &quot;used&quot;: 0
            }
        },
        &quot;memtotal_mb&quot;: 1771,
        &quot;module_setup&quot;: true,
        &quot;mounts&quot;: [
            {
                &quot;block_available&quot;: 14932299,
                &quot;block_size&quot;: 4096,
                &quot;block_total&quot;: 15429883,
                &quot;block_used&quot;: 497584,
                &quot;device&quot;: &quot;/dev/vda4&quot;,
                &quot;dump&quot;: 0,
                &quot;fstype&quot;: &quot;xfs&quot;,
                &quot;inode_available&quot;: 30851998,
                &quot;inode_total&quot;: 30892528,
                &quot;inode_used&quot;: 40530,
                &quot;mount&quot;: &quot;/&quot;,
                &quot;options&quot;: &quot;rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota&quot;,
                &quot;passno&quot;: 0,
                &quot;size_available&quot;: 61162696704,
                &quot;size_total&quot;: 63200800768,
                &quot;uuid&quot;: &quot;9e80229d-48ee-4412-ae76-c18439c52af8&quot;
            },
            {
                &quot;block_available&quot;: 172723,
                &quot;block_size&quot;: 4096,
                &quot;block_total&quot;: 239616,
                &quot;block_used&quot;: 66893,
                &quot;device&quot;: &quot;/dev/vda3&quot;,
                &quot;dump&quot;: 0,
                &quot;fstype&quot;: &quot;xfs&quot;,
                &quot;inode_available&quot;: 511635,
                &quot;inode_total&quot;: 512000,
                &quot;inode_used&quot;: 365,
                &quot;mount&quot;: &quot;/boot&quot;,
                &quot;options&quot;: &quot;rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota&quot;,
                &quot;passno&quot;: 0,
                &quot;size_available&quot;: 707473408,
                &quot;size_total&quot;: 981467136,
                &quot;uuid&quot;: &quot;17cc1761-0d3c-44cb-ae30-a94796e7ac81&quot;
            },
            {
                &quot;block_available&quot;: 45826,
                &quot;block_size&quot;: 2048,
                &quot;block_total&quot;: 51091,
                &quot;block_used&quot;: 5265,
                &quot;device&quot;: &quot;/dev/vda2&quot;,
                &quot;dump&quot;: 0,
                &quot;fstype&quot;: &quot;vfat&quot;,
                &quot;inode_available&quot;: 0,
                &quot;inode_total&quot;: 0,
                &quot;inode_used&quot;: 0,
                &quot;mount&quot;: &quot;/boot/efi&quot;,
                &quot;options&quot;: &quot;rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro&quot;,
                &quot;passno&quot;: 0,
                &quot;size_available&quot;: 93851648,
                &quot;size_total&quot;: 104634368,
                &quot;uuid&quot;: &quot;DFA8-417E&quot;
            }
        ],
        &quot;nodename&quot;: &quot;tnode3.novalocal&quot;,
        &quot;os_family&quot;: &quot;RedHat&quot;,
        &quot;pkg_mgr&quot;: &quot;dnf&quot;,
        &quot;proc_cmdline&quot;: {
            &quot;BOOT_IMAGE&quot;: &quot;(hd0,gpt3)/vmlinuz-5.14.0-570.17.1.el9_6.x86_64&quot;,
            &quot;console&quot;: &quot;ttyS0,115200n8&quot;,
            &quot;crashkernel&quot;: &quot;1G-4G:192M,4G-64G:256M,64G-:512M&quot;,
            &quot;net.ifnames&quot;: &quot;0&quot;,
            &quot;no_timer_check&quot;: true,
            &quot;root&quot;: &quot;UUID=9e80229d-48ee-4412-ae76-c18439c52af8&quot;
        },
        &quot;processor&quot;: [
            &quot;0&quot;,
            &quot;GenuineIntel&quot;,
            &quot;Westmere E56xx/L56xx/X56xx (Nehalem-C)&quot;
        ],
        &quot;processor_cores&quot;: 1,
        &quot;processor_count&quot;: 1,
        &quot;processor_nproc&quot;: 1,
        &quot;processor_threads_per_core&quot;: 1,
        &quot;processor_vcpus&quot;: 1,
        &quot;product_name&quot;: &quot;OpenStack Compute&quot;,
        &quot;product_serial&quot;: &quot;1768bbd2-1796-41ff-ab98-0faaef7d2b39&quot;,
        &quot;product_uuid&quot;: &quot;1768bbd2-1796-41ff-ab98-0faaef7d2b39&quot;,
        &quot;product_version&quot;: &quot;30.0.0-1.el9s&quot;,
        &quot;python&quot;: {
            &quot;executable&quot;: &quot;/usr/bin/python3&quot;,
            &quot;has_sslcontext&quot;: true,
            &quot;type&quot;: &quot;cpython&quot;,
            &quot;version&quot;: {
                &quot;major&quot;: 3,
                &quot;micro&quot;: 21,
                &quot;minor&quot;: 9,
                &quot;releaselevel&quot;: &quot;final&quot;,
                &quot;serial&quot;: 0
            },
            &quot;version_info&quot;: [
                3,
                9,
                21,
                &quot;final&quot;,
                0
            ]
        },
        &quot;python_version&quot;: &quot;3.9.21&quot;,
        &quot;real_group_id&quot;: 0,
        &quot;real_user_id&quot;: 0,
        &quot;selinux&quot;: {
            &quot;config_mode&quot;: &quot;permissive&quot;,
            &quot;mode&quot;: &quot;permissive&quot;,
            &quot;policyvers&quot;: 33,
            &quot;status&quot;: &quot;enabled&quot;,
            &quot;type&quot;: &quot;targeted&quot;
        },
        &quot;selinux_python_present&quot;: true,
        &quot;service_mgr&quot;: &quot;systemd&quot;,
        &quot;ssh_host_key_ecdsa_public&quot;: &quot;AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDh2IWQkVCPc8tYN1RqCE3zlYQMXvcuShOHcqKAp                                                      9c+XOxcGEZNYO7ugLeMaZdeLMdR2KkMkz0DJIFy3bHhpofI=&quot;,
        &quot;ssh_host_key_ecdsa_public_keytype&quot;: &quot;ecdsa-sha2-nistp256&quot;,
        &quot;ssh_host_key_ed25519_public&quot;: &quot;AAAAC3NzaC1lZDI1NTE5AAAAIPiaBDbNx0ve4Dvn1iY+czndIZOV/NShkAJLrMvk80Rf&quot;,
        &quot;ssh_host_key_ed25519_public_keytype&quot;: &quot;ssh-ed25519&quot;,
        &quot;ssh_host_key_rsa_public&quot;: &quot;AAAAB3NzaC1yc2EAAAADAQABAAABgQCr0Q3mJgtEoQqtj+q4fQgOmcQWuvzIK3N3kXB/9HeVim+unejuSnAlKgfYs3mQpR                                                      2PMZ34bDS6+25kb5o6TLjrinCsU+LouFaADTuUu8DqGNQoIWR/GlCBo0htsrkQ2OmHeFZhsSrMK3I7Go/9ohw/k/1/5uqtifcufwDGjpK3u/b5U/rzhdzzu+ezh4x8KJDB                                                      oZRHT8+9nmDDYSzN93putEawhhmuWXq4RRljVMnPXCwI13UeUwg278IrOTB3y4NBdrgaH4KdVoZabpCE82phm1agPy9UcC1Xl/K0azIDLI1peRrsD8Y3PrfldGgKRAYNdt                                                      gszoFMEkssM45wK5tk4t2KdBJlGkrnfKfL4fiPILfivXplpYCfjI07SdyrZP2Dx+pUp39H5qB8pczLFZZHhEWhLgb6H8niXl2VZAQrMzlegDgHLe+CQJyORegkWuqxztJ4                                                      E5fZwTKrtZ8ebr15RabiQpP0GkR339lvWjml6WV9o9LE0+2uVkiG+NkY8d8=&quot;,
        &quot;ssh_host_key_rsa_public_keytype&quot;: &quot;ssh-rsa&quot;,
        &quot;swapfree_mb&quot;: 0,
        &quot;swaptotal_mb&quot;: 0,
        &quot;system&quot;: &quot;Linux&quot;,
        &quot;system_capabilities&quot;: [],
        &quot;system_capabilities_enforced&quot;: &quot;False&quot;,
        &quot;system_vendor&quot;: &quot;RDO&quot;,
        &quot;systemd&quot;: {
            &quot;features&quot;: &quot;+PAM +AUDIT +SELINUX -APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS                                                       +FIDO2 +IDN2 -IDN -IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_                                                      FRAMEWORK +XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified&quot;,
            &quot;version&quot;: 252
        },
        &quot;uptime_seconds&quot;: 186165,
        &quot;user_dir&quot;: &quot;/root&quot;,
        &quot;user_gecos&quot;: &quot;root&quot;,
        &quot;user_gid&quot;: 0,
        &quot;user_id&quot;: &quot;root&quot;,
        &quot;user_shell&quot;: &quot;/bin/bash&quot;,
        &quot;user_uid&quot;: 0,
        &quot;userspace_architecture&quot;: &quot;x86_64&quot;,
        &quot;userspace_bits&quot;: &quot;64&quot;,
        &quot;virtualization_role&quot;: &quot;guest&quot;,
        &quot;virtualization_tech_guest&quot;: [
            &quot;openstack&quot;
        ],
        &quot;virtualization_tech_host&quot;: [
            &quot;kvm&quot;
        ],
        &quot;virtualization_type&quot;: &quot;openstack&quot;
    }
}

PLAY RECAP ***********************************************************************************************************************
tnode3                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 팩트를 통해 수집된 변수 중 특정 값만 추출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;facts1.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768458673653&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db

  tasks:
  - name: Print all facts
    ansible.builtin.debug:
      msg: &amp;gt;
        The default IPv4 address of {{ ansible_facts.hostname }}
        is {{ ansible_facts.default_ipv4.address }}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqvVS8/dJMcad1LY4W/CJhvLs47nJqyuRq1PssPz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqvVS8/dJMcad1LY4W/CJhvLs47nJqyuRq1PssPz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqvVS8/dJMcad1LY4W/CJhvLs47nJqyuRq1PssPz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqvVS8%2FdJMcad1LY4W%2FCJhvLs47nJqyuRq1PssPz1%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;1534&quot; height=&quot;306&quot; data-origin-width=&quot;1534&quot; data-origin-height=&quot;306&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;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; &lt;span data-token-index=&quot;0&quot;&gt;변수로 사용할 수 있는 앤서블 팩트&lt;/span&gt;&lt;/b&gt; : 엔서블 2.* 버전에서는 ansible_facts.* 네임스페이스 표기법을 따름&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 198px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;팩트&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.* 표기법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;호스트명&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;hostname&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;도메인 기반 호스트명&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;fqdn&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;기본 IPv4 주소&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;default_ipv4.address&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;네트워크 인터페이스 이름 목록&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;interfaces&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;/dev/vda1 디스크 파티션 크기&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;device.vda.partitions.vda1.size&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;DNS 서버 목록&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;dns.nameservers&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;현재 실행 중인 커널 버전&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;kernel&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;운영체제 종류&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;ansible_facts.&lt;b&gt;distribution&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ansible_* 표기법&lt;/td&gt;
&lt;td&gt;&amp;nbsp;ansible_facts.* 표기법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_hostname&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;hostname&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_fqdn&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;fqdn&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_default_ipv4.address&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;default_ipv4.address&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_interfaces&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;interfaces&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_device.vda.partitions.vda1.size&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;device.vda.partitions.vda1.size&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_dns.nameservers&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;dns.nameservers&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_kernel&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;kernel&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible_distribution&lt;/td&gt;
&lt;td&gt;ansible_facts.&lt;b&gt;distribution&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 위 두 개의 표기법 모두 인지. 이는 앤서블 환경 설정 파일인 ansible.cfg [&lt;span data-token-index=&quot;1&quot;&gt;defaults&lt;/span&gt;] 섹션에 있는 &lt;span data-token-index=&quot;3&quot;&gt;inject_facts_as_vars&lt;/span&gt; 매개 변수 기본 설정 값이 true이기 때문이며, &lt;span data-token-index=&quot;5&quot;&gt;false&lt;/span&gt;로 설정하면 &lt;span data-token-index=&quot;7&quot;&gt;ansible_* 표기법을 비활성화&lt;/span&gt; 할 수 있습니다.&lt;/p&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;p data-ke-size=&quot;size18&quot;&gt;1. 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-ansible/&lt;span data-token-index=&quot;1&quot;&gt;facts2.yml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768458927314&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db

  tasks:
  - name: Print all facts
    ansible.builtin.debug:
      msg: &amp;gt;
        The node's host name is {{ ansible_hostname }}
        and the ip is {{ ansible_default_ipv4.address }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;2. 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768458953466&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
ansible-playbook facts2.yml
...
TASK [Print all facts] **************************************************************************
ok: [tnode3-ubuntu.local] =&amp;gt; {
    &quot;msg&quot;: &quot;The node's host name is tnode3 and the ip is 10.10.1.13&quot;
}&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;p data-ke-size=&quot;size18&quot;&gt;3. ansible.cfg 수정 후 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768459030084&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ansible.cfg 파일 편집
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false
inject_facts_as_vars = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

# 실행
ansible-playbook facts2.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/conzkk/dJMcachxzDi/52W8D5rNd8NC3fkbdNMZ6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/conzkk/dJMcachxzDi/52W8D5rNd8NC3fkbdNMZ6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/conzkk/dJMcachxzDi/52W8D5rNd8NC3fkbdNMZ6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fconzkk%2FdJMcachxzDi%2F52W8D5rNd8NC3fkbdNMZ6K%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;1237&quot; height=&quot;318&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;0&quot;&gt;ansible.builtin.&lt;/span&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;1&quot;&gt;setup &lt;/span&gt;모듈 - &lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html&quot;&gt;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html&lt;/a&gt;&lt;/p&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;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;팩트 수집 끄기&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 팩트 수집 실행 시 관리 호스트에 프로세스 확인&lt;/p&gt;
&lt;pre id=&quot;code_1768461232538&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (터미널2) tnode3에 SSH 접속 후 아래 모니터링
ssh tnode3
watch -d -n 1 pstree -a

# [ansible-server]에서 아래 플레이북 실행
ansible-playbook facts.yml
ansible-playbook facts.yml
ansible-playbook facts.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfX2TN/dJMb99ZqTuW/2y9c7AWWtMekVKubkYvk81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfX2TN/dJMb99ZqTuW/2y9c7AWWtMekVKubkYvk81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfX2TN/dJMb99ZqTuW/2y9c7AWWtMekVKubkYvk81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfX2TN%2FdJMb99ZqTuW%2F2y9c7AWWtMekVKubkYvk81%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;1860&quot; height=&quot;730&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;730&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 플레이북 작성&lt;/p&gt;
&lt;pre id=&quot;code_1768461980536&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---

- hosts: db
  gather_facts: no

  tasks:
  - name: Print all facts
    ansible.builtin.debug:
      msg: &amp;gt;
        The default IPv4 address of {{ ansible_facts.hostname }}
        is {{ ansible_facts.default_ipv4.address }}&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;3. 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768462004457&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 실행 결과 확인 : 팩트를 수집하지 않았는데, 팩트에서 수집해야만 하는 변수를 사용하려고 해서 에러가 발생
ansible-playbook facts3.yml


# 파일 복사
cp facts3.yml facts3-2.yml

# facts3-2.yml 파일 편집
---

- hosts: db
  gather_facts: no

  tasks:
  - name: Print message
    debug:
      msg: Hello Ansible World

# 실행 : TASK [Print all facts] 가 없다!
ansible-playbook facts3-2.yml
PLAY [db] ***************************************************************************

TASK [Print message] ****************************************************************
ok: [tnode3] =&amp;gt; {
    &quot;msg&quot;: &quot;Hello Ansible World&quot;
}
...&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 매뉴얼한 방법으로 플레이북에 팩트 수집을 설정&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# 파일 복사
cp facts3.yml facts4.yml

# facts4.yml 파일 편집
---

- hosts: **db
  gather_facts: no**

  tasks:
  **- name: Manually gather facts
    ansible.builtin.setup:**

  - name: Print all facts
    ansible.builtin.debug:
      msg: &amp;gt;
        The default IPv4 address of {{ ansible_facts.hostname }}
        is {{ ansible_facts.default_ipv4.address }}&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkA16A/dJMcaa46q8s/kY3AYLbFpeG0AQKkFhI7T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkA16A/dJMcaa46q8s/kY3AYLbFpeG0AQKkFhI7T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkA16A/dJMcaa46q8s/kY3AYLbFpeG0AQKkFhI7T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkA16A%2FdJMcaa46q8s%2FkY3AYLbFpeG0AQKkFhI7T1%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;1247&quot; height=&quot;303&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;303&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;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;Gathering Facts 캐싱&lt;/b&gt; - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/plugins/cache.html&quot;&gt;https://docs.ansible.com/ansible/latest/plugins/cache.html&lt;/a&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;facts&lt;/b&gt; 정보와 &lt;b&gt;inventory&lt;/b&gt; 정보는 기본적으로 default 캐시 플러그인인 &lt;b&gt;memory&lt;/b&gt; 플러그인을 사용하고 있습니다.&lt;/li&gt;
&lt;li&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;/ul&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;Gathering Facts 캐싱 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ansible.cfg&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false
**gathering = smart
fact_caching = jsonfile
fact_caching_connection = myfacts**

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false&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;DEFAULT_GATHERING(수집 정책 3가지) : implicit(기본값), explicit, &lt;b&gt;smart&lt;/b&gt; - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-gathering&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;fact_caching_connection : 연결 정의 or &lt;b&gt;캐싱 경로&lt;/b&gt; - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#cache-plugin-connection&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;사용자 지정 팩트 만들기&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;관리 호스트&lt;/b&gt;의 &lt;b&gt;로컬&lt;/b&gt;에 있는 &lt;b&gt;/etc/ansible/facts.d&lt;/b&gt; 디렉터리 내에 &amp;lsquo;*&lt;b&gt;.fact&lt;/b&gt;&amp;rsquo;로 저장되어야만 앤서블이 플레이북을 실행할 때 &lt;b&gt;자동&lt;/b&gt;으로 &lt;b&gt;팩트&lt;/b&gt;를 &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;p data-ke-size=&quot;size18&quot;&gt;1. ansible-server 에 디렉터리 생성 후 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1768462541590&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
mkdir /etc/ansible/facts.d

# my-custom.fact 파일 생성
cat &amp;lt;&amp;lt;EOT &amp;gt; /etc/ansible/facts.d/my-custom.fact
[packages]
web_package = httpd
db_package = mariadb-server

[users]
user1 = ansible
user2 = gasida
EOT

cat /etc/ansible/facts.d/my-custom.fact&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 플레이북 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1768462593262&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 파일 복사
cp facts4.yml facts5.yml

# facts5.yml 파일 편집
---

- hosts: localhost

  tasks:
  - name: Print all facts
    ansible.builtin.debug:
      var: ansible_local&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;p data-ke-size=&quot;size18&quot;&gt;3. 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768462629103&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 실행 : 커스텀으로 생성했던 팩트 내용이 출력
ansible-playbook facts5.yml
PLAY [localhost] ******************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [localhost]

TASK [Print all facts] ************************************************************************************************************************************************************
ok: [localhost] =&amp;gt; {
    &quot;ansible_local&quot;: {
        &quot;my-custom&quot;: {
            &quot;packages&quot;: {
                &quot;db_package&quot;: &quot;mariadb-server&quot;,
                &quot;web_package&quot;: &quot;httpd&quot;
            },
            &quot;users&quot;: {
                &quot;user1&quot;: &quot;ansible&quot;,
                &quot;user2&quot;: &quot;gasida&quot;
            }
        }
    }
}
...

# 
tail -n 5 /etc/passwd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qDs3R/dJMcabXe8nN/QqkT8ngcBCMxkS3G151ozK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qDs3R/dJMcabXe8nN/QqkT8ngcBCMxkS3G151ozK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qDs3R/dJMcabXe8nN/QqkT8ngcBCMxkS3G151ozK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqDs3R%2FdJMcabXe8nN%2FQqkT8ngcBCMxkS3G151ozK%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;1273&quot; height=&quot;515&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;515&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;4. 실습 완료 후 작성한 yml 파일 삭제&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768463028693&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rm -r *.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;2&quot;&gt;&lt;b&gt;[도전과제 3]&lt;/b&gt; 팩트&lt;/span&gt;를 사용하여 3개의 서버(노드)의 &amp;lsquo;&lt;span data-token-index=&quot;4&quot;&gt;커널 버전&lt;/span&gt;&amp;rsquo;과 &amp;lsquo;&lt;span data-token-index=&quot;6&quot;&gt;운영체제 종류&lt;/span&gt;&amp;rsquo;를 출력해보자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. kernel.yml&amp;nbsp; 작성&lt;/p&gt;
&lt;pre id=&quot;code_1768463083022&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
- hosts: all
  gather_facts: yes  # 커널과 OS 정보를 가져오기 위한 설정

  tasks:
    - name: &quot;각 서버의 OS 및 커널 정보 출력&quot;
      ansible.builtin.debug:
        msg: &amp;gt;
          대상 서버: {{ inventory_hostname }}
          운영체제: {{ ansible_facts.distribution }} {{ ansible_facts.distribution_version }}
          커널 버전: {{ ansible_facts.kernel }}&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;p data-ke-size=&quot;size18&quot;&gt;2. 실행&lt;/p&gt;
&lt;pre id=&quot;code_1768463180468&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; # kernel.yml 실행
 ansible-playbook kernel.yml&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;3. 출력결과&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxPzsr/dJMcahC8KZp/UBCEjvjUi2R8Nn1WcrXQo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxPzsr/dJMcahC8KZp/UBCEjvjUi2R8Nn1WcrXQo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxPzsr/dJMcahC8KZp/UBCEjvjUi2R8Nn1WcrXQo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxPzsr%2FdJMcahC8KZp%2FUBCEjvjUi2R8Nn1WcrXQo1%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;1241&quot; height=&quot;502&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;502&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;반복문&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ansible **Loops** - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html&quot;&gt;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반복문을 사용하면 동일한 모듈을 사용하는 작업을 여러 번 작성하지 않아도 됩니다. 예를 들어 서비스에 필요한 포트를 방화벽에 추가한다고 하면, 포트를 추가하는 작업을 여러 개 작성하는 대신 loop 반복문을 이용해 작업 하나로 여러 개의 포트를 추가할 수 있습니다.&lt;/blockquote&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; &lt;span data-token-index=&quot;0&quot;&gt;단순 반복문&lt;/span&gt; &lt;/b&gt;: 특정 항목에 대한 작업을 반복 - item 변수, 변수 목록 지정&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;loop&lt;/b&gt; 키워드를 작업에 추가하면 작업을 반복해야 하는 항목의 &lt;b&gt;목록&lt;/b&gt;을 &lt;b&gt;값&lt;/b&gt;으로 사용합니다. 그리고 해당하는 값을 사용하려면 &lt;b&gt;item&lt;/b&gt; &lt;b&gt;변수&lt;/b&gt;를 이용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;플레이북 파일 생성 : sshd 와 rsyslog 서비스가 시작되어 있지 않다면 시작 &amp;larr; 최종 목적 동작&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;state&lt;/b&gt; - &lt;a href=&quot;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#parameter-state&quot;&gt;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#parameter-state&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;started&lt;/b&gt; : &lt;b&gt;Start service httpd, if not started&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;stooped : Stop service httpd, if started&lt;/li&gt;
&lt;li&gt;restarted : Restart service httpd, in all cases&lt;/li&gt;
&lt;li&gt;reloaded : Reload service httpd, in all cases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&amp;nbsp; &amp;nbsp;실행&lt;/p&gt;
&lt;pre id=&quot;code_1768548955823&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 프로세스 동작 확인
ansible -m shell -a &quot;pstree |grep sshd&quot; all
ansible -m shell -a &quot;pstree |grep rsyslog&quot; all

# systemd로 실행 중인 서비스 목록을 확인
systemctl list-units --type=service

# 실행
ansible-playbook check-services.yml&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/49</guid>
      <comments>https://netoppa.tistory.com/49#entry49comment</comments>
      <pubDate>Mon, 12 Jan 2026 16:48:24 +0900</pubDate>
    </item>
    <item>
      <title>[1주차] Bootstrap Kubernetes the hard way</title>
      <link>https://netoppa.tistory.com/48</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;※ &lt;/b&gt;&lt;b&gt;Kubernetes the hard way의 목적&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Kubernetes the hard way의 핵심은 &quot;자동화 도구(Managed Services나 Installer)를 전혀 사용하지 않고, &lt;br /&gt;모든 구성 요소를 밑바닥부터 직접 설치하고 설정하는 것. kubeadm 도구조차 사용하지 않고 설치하는 것이 특징이며 각각의 구성요소들을 직접 한땀한땀 설치하므로써 kubernetes의 내부 구조를 이해하고 분석할 수 있는 능력을 키우기 위함&lt;br /&gt;&lt;br /&gt;본 자료는 Cloud@net 스터디 자료로 실제 교육 내용을 토대로 현재 프로덕션 환경에 적용하기 위한 목적이어서 실제 물리서버로 설치하는 방식으로 실습을 진행하였음&lt;/blockquote&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;▶ 사전 준비물&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;☞ 하이퍼바이저 물리 서버 1식 (VM 4식)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 하이퍼바이저 : Proxmox&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 서버 모델 : HP Gen G7 (CPU : 4 core,&amp;nbsp; MEM : 250G. HDD : 2T)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - VM : 총 4식 (운영체제 : Debian 12)&lt;/p&gt;
&lt;table id=&quot;2de50aec-5edf-816a-b94c-c4114228ca70&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.0465%;&quot;&gt;NAME&lt;/td&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;Description&lt;/td&gt;
&lt;td style=&quot;width: 5.81395%;&quot;&gt;CPU&lt;/td&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;MEM&lt;/td&gt;
&lt;td style=&quot;width: 14.7674%;&quot;&gt;NIC&lt;/td&gt;
&lt;td style=&quot;width: 34.7674%;&quot;&gt;HOSTNAME&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8199-a060-cd4f7036780d&quot;&gt;
&lt;td id=&quot;VRnf&quot; style=&quot;width: 11.0465%;&quot;&gt;jumpbox&lt;/td&gt;
&lt;td id=&quot;K?dQ&quot; style=&quot;width: 21.7442%;&quot;&gt;Administration host&lt;/td&gt;
&lt;td id=&quot;Jn}l&quot; style=&quot;width: 5.81395%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;e]E?&quot; style=&quot;width: 11.7442%;&quot;&gt;1536 MB&lt;/td&gt;
&lt;td id=&quot;ocJ=&quot; style=&quot;width: 14.7674%;&quot;&gt;&lt;b&gt;192.168.7.245&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;uSw;&quot; style=&quot;width: 34.7674%;&quot;&gt;&lt;b&gt;jumpbox&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-813e-a57c-f8de4d433c67&quot;&gt;
&lt;td id=&quot;VRnf&quot; style=&quot;width: 11.0465%;&quot;&gt;server&lt;/td&gt;
&lt;td id=&quot;K?dQ&quot; style=&quot;width: 21.7442%;&quot;&gt;Kubernetes server&lt;/td&gt;
&lt;td id=&quot;Jn}l&quot; style=&quot;width: 5.81395%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;e]E?&quot; style=&quot;width: 11.7442%;&quot;&gt;2GB&lt;/td&gt;
&lt;td id=&quot;ocJ=&quot; style=&quot;width: 14.7674%;&quot;&gt;&lt;b&gt;192.168.7.246&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;uSw;&quot; style=&quot;width: 34.7674%;&quot;&gt;server.kubernetes.local &lt;b&gt;server&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81e4-a2f1-f62d64975a5b&quot;&gt;
&lt;td id=&quot;VRnf&quot; style=&quot;width: 11.0465%;&quot;&gt;node-0&lt;/td&gt;
&lt;td id=&quot;K?dQ&quot; style=&quot;width: 21.7442%;&quot;&gt;Kubernetes worker&lt;/td&gt;
&lt;td id=&quot;Jn}l&quot; style=&quot;width: 5.81395%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;e]E?&quot; style=&quot;width: 11.7442%;&quot;&gt;2GB&lt;/td&gt;
&lt;td id=&quot;ocJ=&quot; style=&quot;width: 14.7674%;&quot;&gt;&lt;b&gt;192.168.7.247&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;uSw;&quot; style=&quot;width: 34.7674%;&quot;&gt;node-0.kubernetes.local &lt;b&gt;node-0&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81ce-8329-c871a47f8b38&quot;&gt;
&lt;td id=&quot;VRnf&quot; style=&quot;width: 11.0465%;&quot;&gt;node-1&lt;/td&gt;
&lt;td id=&quot;K?dQ&quot; style=&quot;width: 21.7442%;&quot;&gt;Kubernetes worker&lt;/td&gt;
&lt;td id=&quot;Jn}l&quot; style=&quot;width: 5.81395%;&quot;&gt;2&lt;/td&gt;
&lt;td id=&quot;e]E?&quot; style=&quot;width: 11.7442%;&quot;&gt;2GB&lt;/td&gt;
&lt;td id=&quot;ocJ=&quot; style=&quot;width: 14.7674%;&quot;&gt;&lt;b&gt;192.168.7.248&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;uSw;&quot; style=&quot;width: 34.7674%;&quot;&gt;node-1.kubernetes.local &lt;b&gt;node-1&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;☞ 스터디 수업에서는 NIC 이 외부와 내부로 분리하여 구성하고 있으나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;실제 프로덕션 환경에서 구축하는 방식으로 실습을 진행하였기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;VirtualBox와 Vegrant를 사용하지 않고 베어메탈 서버의 단일 네트워크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;환경으로 구축하였음&lt;/p&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;▶ 실습 구성도&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GNrlB/dJMcaaRwzSf/BMBQQZ5CKJiFNxhjCWkid0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GNrlB/dJMcaaRwzSf/BMBQQZ5CKJiFNxhjCWkid0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GNrlB/dJMcaaRwzSf/BMBQQZ5CKJiFNxhjCWkid0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGNrlB%2FdJMcaaRwzSf%2FBMBQQZ5CKJiFNxhjCWkid0%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;1246&quot; height=&quot;845&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;845&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;p data-ke-size=&quot;size16&quot;&gt;☞&amp;nbsp; 우분투 로컬 서버 (KIND 설치) 노드 - 1식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우분투 노드 1식은 kubernetes the hardway 의 설치 및 구성 정보를&amp;nbsp; KIND를 이용하여 서로 비교하기 위해 필요하며 본인은 테스트를 위한 베어메탈 서버 자원이 이미 확보된 상황이어서 실습할 때&amp;nbsp; WSL을 사용할 필요가 없음. 우분투가 설치된 프러덕션 환경을 이용하여 실습할 경우 스터디 내용 처럼 VirtualBox와 baygrant 그리고 WSL 관련 내용이 불필요하므로 제외시켰음&lt;/p&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;▶ 우분투 가상화 노드에 KIND 설치&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;# 도커 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767845840461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
...

# 설치 확인
docker info
docker ps
sudo systemctl status docker
cat /etc/group | grep docker&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;# KIND 및 관리 툴 설치&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767845902762&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 사용자 디렉터리 이동
cd $PWD
pwd

#
sudo systemctl stop apparmor &amp;amp;&amp;amp; sudo systemctl disable apparmor

# 
sudo apt update &amp;amp;&amp;amp; sudo apt-get install bridge-utils net-tools jq tree unzip kubectx kubecolor -y

# Install Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
kind --version

# Install kubectl
curl -LO &quot;https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl&quot;
chmod +x kubectl
sudo mv ./kubectl /usr/bin
sudo kubectl version --client=true

# Install Helm
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm version

# Source the completion
source &amp;lt;(kubectl completion bash)
echo 'source &amp;lt;(kubectl completion bash)' &amp;gt;&amp;gt; ~/.bashrc

# Alias kubectl to k
echo 'alias k=kubectl' &amp;gt;&amp;gt; ~/.bashrc
echo 'complete -o default -F __start_kubectl k' &amp;gt;&amp;gt; ~/.bashrc

# Install Kubeps &amp;amp; Setting PS1
git clone https://github.com/jonmosco/kube-ps1.git
echo -e &quot;source $PWD/kube-ps1/kube-ps1.sh&quot; &amp;gt;&amp;gt; ~/.bashrc
cat &amp;lt;&amp;lt;&quot;EOT&quot; &amp;gt;&amp;gt; ~/.bashrc
KUBE_PS1_SYMBOL_ENABLE=true
function get_cluster_short() {
  echo &quot;$1&quot; | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT

# .bashrc 적용을 위해서 logout 후 터미널 다시 접속 하자
exit&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# KIND로 K8S 배포&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767847083125&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 클러스터 배포 전 확인
docker ps

# Create a cluster with kind
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - &amp;lt;&amp;lt;EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
- role: worker
EOF

# 확인
kind get nodes --name myk8s
kubens default

# kind 는 별도 도커 네트워크 생성 후 사용 : 기본값 172.18.0.0/16
docker network ls
docker inspect kind | jq

# k8s api 주소 확인 : 어떻게 로컬에서 접속이 되는 걸까?
kubectl cluster-info

# 노드 정보 확인 : CRI 는 containerd 사용
kubectl get node -o wide

# 파드 정보 확인 : CNI 는 kindnet 사용
kubectl get pod -A -o wide

# 네임스페이스 확인 &amp;gt;&amp;gt; 도커 컨테이너에서 배운 네임스페이스와 다릅니다!
kubectl get namespaces

# 컨트롤플레인/워커 노드(컨테이너) 확인 : 도커 컨테이너 이름은 myk8s-control-plane , myk8s-worker 임을 확인
docker ps
docker images
docker exec -it myk8s-control-plane ss -tnlp

# 디버그용 내용 출력에 ~/.kube/config 권한 인증 로드
kubectl get pod -v6

# kube config 파일 확인 : &quot;server: https://127.0.0.1:YYYYY&quot; 127.0.0.1:Port로 접속 가능을 확인!
cat ~/.kube/config
ls -l ~/.kube/config&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;p data-ke-size=&quot;size16&quot;&gt;(참고) 클러스터 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1767847232760&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 클러스터 삭제
kind delete cluster --name myk8s
docker ps
cat ~/.kube/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;K8S the hard way 클러스터 설치&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 노드 공통&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1767847349309&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Proxmox VM Initial Config Start &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;

# [TASK 1] 사용자 설정 및 시간대
# Cloud-init 기본 사용자인 'debian' 기준 (vagrant 대신 debian 사용)
echo &quot;alias vi=vim&quot; &amp;gt;&amp;gt; /etc/profile
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# [TASK 2] AppArmor 비활성화 (K8s 호환성)
systemctl stop apparmor &amp;amp;&amp;amp; systemctl disable apparmor &amp;gt;/dev/null 2&amp;gt;&amp;amp;1

# [TASK 3] SWAP 비활성화 (K8s 필수 설정)
swapoff -a &amp;amp;&amp;amp; sed -i '/swap/s/^/#/' /etc/fstab

# [TASK 4] 필수 패키지 설치
apt update -qq
apt install tree git jq yq unzip vim sshpass -y -qq

# [TASK 5] Root 및 Debian 사용자 패스워드 설정
echo &quot;root:qwe123&quot; | chpasswd
echo &quot;debian:qwe123&quot; | chpasswd

# [TASK 6] SSH 설정 수정 (Root 접속 허용)
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd

# [TASK 7] 로컬 DNS 설정 (/etc/hosts) - 요청하신 IP 반영
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/hosts
192.168.7.245 jumpbox
192.168.7.246 server.kubernetes.local server
192.168.7.247 node-0.kubernetes.local node-0
192.168.7.248 node-1.kubernetes.local node-1
EOF

echo &quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Initial Config End &amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot;&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. jumpbox 가상머신&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1767847565467&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 사용자 확인
whoami
pwd

# OS version 확인
cat /etc/os-release
PRETTY_NAME=&quot;Debian GNU/Linux 12 (bookworm)&quot;
NAME=&quot;Debian GNU/Linux&quot;
VERSION_ID=&quot;12&quot;
VERSION=&quot;12 (bookworm)&quot;
VERSION_CODENAME=bookworm
ID=debian

# AppArmor 상태 확인
aa-status
systemctl is-active apparmor
inactive
     
# /etc/hosts 파일 내용 확인
# https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution
cat /etc/hosts&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Set Up The Jumpbox&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;설치에 필요한 파일 등 준비 -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/02-jumpbox.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 항목버전&lt;/p&gt;
&lt;table id=&quot;2de50aec-5edf-8195-83ba-f10192bfdabe&quot; style=&quot;border-collapse: collapse; width: 40.814%; height: 133px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;설치 항목&lt;/td&gt;
&lt;td&gt;버전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81d7-89c6-e7581bbe5aa9&quot;&gt;
&lt;td id=&quot;I\Wt&quot;&gt;k8s 관련 (kube-apiserver, kubelet 등)&lt;/td&gt;
&lt;td id=&quot;T&amp;gt;Fe&quot;&gt;1.32.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-812b-9e9c-d2995055bc61&quot;&gt;
&lt;td id=&quot;I\Wt&quot;&gt;etcd&lt;/td&gt;
&lt;td id=&quot;T&amp;gt;Fe&quot;&gt;3.6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8170-8687-de5f7dcbd72c&quot;&gt;
&lt;td id=&quot;I\Wt&quot;&gt;containerd&lt;/td&gt;
&lt;td id=&quot;T&amp;gt;Fe&quot;&gt;2.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-812f-b6bd-ce91f94a6dc4&quot;&gt;
&lt;td id=&quot;I\Wt&quot;&gt;runc&lt;/td&gt;
&lt;td id=&quot;T&amp;gt;Fe&quot;&gt;1.3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767848015942&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 툴 설치 : 이미 적용되어 있음
apt-get update &amp;amp;&amp;amp; apt install tree git jq yq unzip vim sshpass -y

# Sync GitHub Repository
pwd
git clone --depth 1 https://github.com/kelseyhightower/kubernetes-the-hard-way.git
cd kubernetes-the-hard-way
tree
pwd


(확인)
debian@jumpbox:~$ pwd
git clone --depth 1 https://github.com/kelseyhightower/kubernetes-the-hard-way.git
cd kubernetes-the-hard-way
tree
pwd
/home/debian
Cloning into 'kubernetes-the-hard-way'...
remote: Enumerating objects: 41, done.
remote: Counting objects: 100% (41/41), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 41 (delta 3), reused 14 (delta 1), pack-reused 0 (from 0)
Receiving objects: 100% (41/41), 29.27 KiB | 3.66 MiB/s, done.
Resolving deltas: 100% (3/3), done.
.
├── CONTRIBUTING.md
├── COPYRIGHT.md
├── LICENSE
├── README.md
├── ca.conf
├── configs
│&amp;nbsp;&amp;nbsp; ├── 10-bridge.conf
│&amp;nbsp;&amp;nbsp; ├── 99-loopback.conf
│&amp;nbsp;&amp;nbsp; ├── containerd-config.toml
│&amp;nbsp;&amp;nbsp; ├── encryption-config.yaml
│&amp;nbsp;&amp;nbsp; ├── kube-apiserver-to-kubelet.yaml
│&amp;nbsp;&amp;nbsp; ├── kube-proxy-config.yaml
│&amp;nbsp;&amp;nbsp; ├── kube-scheduler.yaml
│&amp;nbsp;&amp;nbsp; └── kubelet-config.yaml
├── docs
│&amp;nbsp;&amp;nbsp; ├── 01-prerequisites.md
│&amp;nbsp;&amp;nbsp; ├── 02-jumpbox.md
│&amp;nbsp;&amp;nbsp; ├── 03-compute-resources.md
│&amp;nbsp;&amp;nbsp; ├── 04-certificate-authority.md
│&amp;nbsp;&amp;nbsp; ├── 05-kubernetes-configuration-files.md
│&amp;nbsp;&amp;nbsp; ├── 06-data-encryption-keys.md
│&amp;nbsp;&amp;nbsp; ├── 07-bootstrapping-etcd.md
│&amp;nbsp;&amp;nbsp; ├── 08-bootstrapping-kubernetes-controllers.md
│&amp;nbsp;&amp;nbsp; ├── 09-bootstrapping-kubernetes-workers.md
│&amp;nbsp;&amp;nbsp; ├── 10-configuring-kubectl.md
│&amp;nbsp;&amp;nbsp; ├── 11-pod-network-routes.md
│&amp;nbsp;&amp;nbsp; ├── 12-smoke-test.md
│&amp;nbsp;&amp;nbsp; └── 13-cleanup.md
├── downloads-amd64.txt
├── downloads-arm64.txt
└── units
    ├── containerd.service
    ├── etcd.service
    ├── kube-apiserver.service
    ├── kube-controller-manager.service
    ├── kube-proxy.service
    ├── kube-scheduler.service
    └── kubelet.service

4 directories, 35 files
/home/debian/kubernetes-the-hard-way
debian@jumpbox:~/kubernetes-the-hard-way$ pwd
/home/debian/kubernetes-the-hard-way





# Download Binaries : k8s 구성을 위한 컴포넌트 다운로드

# CPU 아키텍처 확인
dpkg --print-architecture
arm64   # macOS 사용자
amd64   # Windows 사용자

debian@jumpbox:~/kubernetes-the-hard-way$ dpkg --print-architecture
amd64


# CPU 아키텍처 별 다운로드 목록 정보 다름
ls -l downloads-*
-rw-r--r-- 1 root root 839 Jan  4 10:30 downloads-amd64.txt
-rw-r--r-- 1 root root 839 Jan  4 10:30 downloads-arm64.txt

# https://kubernetes.io/releases/download/
cat downloads-$(dpkg --print-architecture).txt
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kubectl
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kube-apiserver
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kube-controller-manager
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kube-scheduler
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kube-proxy
https://dl.k8s.io/v1.32.3/bin/linux/amd64/kubelet
https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.32.0/crictl-v1.32.0-linux-amd64.tar.gz
https://github.com/opencontainers/runc/releases/download/v1.3.0-rc.1/runc.amd64
https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz
https://github.com/containerd/containerd/releases/download/v2.1.0-beta.0/containerd-2.1.0-beta.0-linux-amd64.tar.gz
https://github.com/etcd-io/etcd/releases/download/v3.6.0-rc.3/etcd-v3.6.0-rc.3-linux-amd64.tar.gz

# wget 으로 다운로드 실행 : 500MB Size 정도
wget -q --show-progress \
  --https-only \
  --timestamping \
  -P downloads \
  -i downloads-$(dpkg --print-architecture).txt


kubectl                                             100%[=================================================================================================================&amp;gt;]  54.67M  4.27MB/s    in 13s
kube-apiserver                                      100%[=================================================================================================================&amp;gt;]  88.94M  4.13MB/s    in 21s
kube-controller-manager                             100%[=================================================================================================================&amp;gt;]  82.00M  3.98MB/s    in 21s
kube-scheduler                                      100%[=================================================================================================================&amp;gt;]  62.79M  3.61MB/s    in 16s
kube-proxy                                          100%[=================================================================================================================&amp;gt;]  63.75M  4.02MB/s    in 15s
kubelet                                             100%[=================================================================================================================&amp;gt;]  73.82M  3.85MB/s    in 19s
crictl-v1.32.0-linux-amd64.tar.gz                   100%[=================================================================================================================&amp;gt;]  18.21M  4.18MB/s    in 4.4s
runc.amd64                                          100%[=================================================================================================================&amp;gt;]  11.30M  4.53MB/s    in 2.5s
cni-plugins-linux-amd64-v1.6.2.tgz                  100%[=================================================================================================================&amp;gt;]  50.35M  3.55MB/s    in 13s
containerd-2.1.0-beta.0-linux-amd64.tar.gz          100%[=================================================================================================================&amp;gt;]  37.01M  3.38MB/s    in 10s
etcd-v3.6.0-rc.3-linux-amd64.tar.gz                 100%[=================================================================================================================&amp;gt;]  22.48M  3.84MB/s    in 5.9s



# 확인
ls -oh downloads
total 566M
-rw-r--r-- 1 debian 51M Jan  7  2025 cni-plugins-linux-amd64-v1.6.2.tgz
-rw-r--r-- 1 debian 38M Mar 18  2025 containerd-2.1.0-beta.0-linux-amd64.tar.gz
-rw-r--r-- 1 debian 19M Dec  9  2024 crictl-v1.32.0-linux-amd64.tar.gz
-rw-r--r-- 1 debian 23M Mar 28  2025 etcd-v3.6.0-rc.3-linux-amd64.tar.gz
-rw-r--r-- 1 debian 89M Mar 12  2025 kube-apiserver
-rw-r--r-- 1 debian 83M Mar 12  2025 kube-controller-manager
-rw-r--r-- 1 debian 64M Mar 12  2025 kube-proxy
-rw-r--r-- 1 debian 63M Mar 12  2025 kube-scheduler
-rw-r--r-- 1 debian 55M Mar 12  2025 kubectl
-rw-r--r-- 1 debian 74M Mar 12  2025 kubelet
-rw-r--r-- 1 debian 12M Mar  4  2025 runc.amd64


# Extract the component binaries from the release archives and organize them under the downloads directory.
ARCH=$(dpkg --print-architecture)
echo $ARCH

mkdir -p downloads/{client,cni-plugins,controller,worker}
tree -d downloads
downloads
├── client
├── cni-plugins
├── controller
└── worker

5 directories

# 압축 풀기
tar -xvf downloads/crictl-v1.32.0-linux-${ARCH}.tar.gz \
  -C downloads/worker/ &amp;amp;&amp;amp; tree -ug downloads

(확인)  
crictl
[debian   debian  ]  downloads
├── [debian   debian  ]  client
├── [debian   debian  ]  cni-plugins
├── [debian   debian  ]  cni-plugins-linux-amd64-v1.6.2.tgz
├── [debian   debian  ]  containerd-2.1.0-beta.0-linux-amd64.tar.gz
├── [debian   debian  ]  controller
├── [debian   debian  ]  crictl-v1.32.0-linux-amd64.tar.gz
├── [debian   debian  ]  etcd-v3.6.0-rc.3-linux-amd64.tar.gz
├── [debian   debian  ]  kube-apiserver
├── [debian   debian  ]  kube-controller-manager
├── [debian   debian  ]  kube-proxy
├── [debian   debian  ]  kube-scheduler
├── [debian   debian  ]  kubectl
├── [debian   debian  ]  kubelet
├── [debian   debian  ]  runc.amd64
└── [debian   debian  ]  worker
    └── [debian   debian  ]  crictl

5 directories, 12 files
  
  
  

tar -xvf downloads/containerd-2.1.0-beta.0-linux-${ARCH}.tar.gz \
  --strip-components 1 \
  -C downloads/worker/ &amp;amp;&amp;amp; tree -ug downloads


bin/containerd-shim-runc-v2
bin/containerd
bin/containerd-stress
bin/ctr
[debian   debian  ]  downloads
├── [debian   debian  ]  client
├── [debian   debian  ]  cni-plugins
├── [debian   debian  ]  cni-plugins-linux-amd64-v1.6.2.tgz
├── [debian   debian  ]  containerd-2.1.0-beta.0-linux-amd64.tar.gz
├── [debian   debian  ]  controller
├── [debian   debian  ]  crictl-v1.32.0-linux-amd64.tar.gz
├── [debian   debian  ]  etcd-v3.6.0-rc.3-linux-amd64.tar.gz
├── [debian   debian  ]  kube-apiserver
├── [debian   debian  ]  kube-controller-manager
├── [debian   debian  ]  kube-proxy
├── [debian   debian  ]  kube-scheduler
├── [debian   debian  ]  kubectl
├── [debian   debian  ]  kubelet
├── [debian   debian  ]  runc.amd64
└── [debian   debian  ]  worker
    ├── [debian   debian  ]  containerd
    ├── [debian   debian  ]  containerd-shim-runc-v2
    ├── [debian   debian  ]  containerd-stress
    ├── [debian   debian  ]  crictl
    └── [debian   debian  ]  ctr

5 directories, 16 files




tar -xvf downloads/cni-plugins-linux-${ARCH}-v1.6.2.tgz \
  -C downloads/cni-plugins/ &amp;amp;&amp;amp; tree -ug downloads
  

./
./ipvlan
./tap
./loopback
./host-device
./README.md
./portmap
./ptp
./vlan
./bridge
./firewall
./LICENSE
./macvlan
./dummy
./bandwidth
./vrf
./tuning
./static
./dhcp
./host-local
./sbr
[debian   debian  ]  downloads
├── [debian   debian  ]  client
├── [debian   debian  ]  cni-plugins
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  LICENSE
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  README.md
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  bandwidth
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  bridge
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  dhcp
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  dummy
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  firewall
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  host-device
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  host-local
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  ipvlan
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  loopback
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  macvlan
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  portmap
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  ptp
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  sbr
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  static
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  tap
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  tuning
│&amp;nbsp;&amp;nbsp; ├── [debian   debian  ]  vlan
│&amp;nbsp;&amp;nbsp; └── [debian   debian  ]  vrf
├── [debian   debian  ]  cni-plugins-linux-amd64-v1.6.2.tgz
├── [debian   debian  ]  containerd-2.1.0-beta.0-linux-amd64.tar.gz
├── [debian   debian  ]  controller
├── [debian   debian  ]  crictl-v1.32.0-linux-amd64.tar.gz
├── [debian   debian  ]  etcd-v3.6.0-rc.3-linux-amd64.tar.gz
├── [debian   debian  ]  kube-apiserver
├── [debian   debian  ]  kube-controller-manager
├── [debian   debian  ]  kube-proxy
├── [debian   debian  ]  kube-scheduler
├── [debian   debian  ]  kubectl
├── [debian   debian  ]  kubelet
├── [debian   debian  ]  runc.amd64
└── [debian   debian  ]  worker
    ├── [debian   debian  ]  containerd
    ├── [debian   debian  ]  containerd-shim-runc-v2
    ├── [debian   debian  ]  containerd-stress
    ├── [debian   debian  ]  crictl
    └── [debian   debian  ]  ctr

5 directories, 36 files

  

## --strip-components 1 : etcd-v3.6.0-rc.3-linux-amd64/etcd 경로의 앞부분(디렉터리)을 제거
tar -xvf downloads/etcd-v3.6.0-rc.3-linux-${ARCH}.tar.gz \
  -C downloads/ \
  --strip-components 1 \
  etcd-v3.6.0-rc.3-linux-${ARCH}/etcdctl \
  etcd-v3.6.0-rc.3-linux-${ARCH}/etcd &amp;amp;&amp;amp; tree -ug downloads





# 확인
tree downloads/worker/
tree downloads/cni-plugins
ls -l downloads/{etcd,etcdctl}

debian@jumpbox:~/kubernetes-the-hard-way$ tree downloads/worker/
downloads/worker/
├── containerd
├── containerd-shim-runc-v2
├── containerd-stress
├── crictl
└── ctr

1 directory, 5 files
debian@jumpbox:~/kubernetes-the-hard-way$
debian@jumpbox:~/kubernetes-the-hard-way$
debian@jumpbox:~/kubernetes-the-hard-way$ tree downloads/cni-plugins
downloads/cni-plugins
├── LICENSE
├── README.md
├── bandwidth
├── bridge
├── dhcp
├── dummy
├── firewall
├── host-device
├── host-local
├── ipvlan
├── loopback
├── macvlan
├── portmap
├── ptp
├── sbr
├── static
├── tap
├── tuning
├── vlan
└── vrf

1 directory, 20 files



debian@jumpbox:~/kubernetes-the-hard-way$ ls -l downloads/{etcd,etcdctl}
-rwxr-xr-x 1 debian debian 25219224 Mar 28  2025 downloads/etcd
-rwxr-xr-x 1 debian debian 16421016 Mar 28  2025 downloads/etcdctl




# 파일 이동 
mv downloads/{etcdctl,kubectl} downloads/client/
mv downloads/{etcd,kube-apiserver,kube-controller-manager,kube-scheduler} downloads/controller/
mv downloads/{kubelet,kube-proxy} downloads/worker/
mv downloads/runc.${ARCH} downloads/worker/runc

# 확인
tree downloads/client/
tree downloads/controller/
tree downloads/worker/


tree downloads/controller/
tree downloads/worker/
downloads/client/
├── etcdctl
└── kubectl

1 directory, 2 files
downloads/controller/
├── etcd
├── kube-apiserver
├── kube-controller-manager
└── kube-scheduler

1 directory, 4 files
downloads/worker/
├── containerd
├── containerd-shim-runc-v2
├── containerd-stress
├── crictl
├── ctr
├── kube-proxy
├── kubelet
└── runc

1 directory, 8 files


# 불필요한 압축 파일 제거
ls -l downloads/*gz
rm -rf downloads/*gz

-rw-r--r-- 1 debian debian 52794236 Jan  7  2025 downloads/cni-plugins-linux-amd64-v1.6.2.tgz
-rw-r--r-- 1 debian debian 38807044 Mar 18  2025 downloads/containerd-2.1.0-beta.0-linux-amd64.tar.gz
-rw-r--r-- 1 debian debian 19100418 Dec  9  2024 downloads/crictl-v1.32.0-linux-amd64.tar.gz
-rw-r--r-- 1 debian debian 23577153 Mar 28  2025 downloads/etcd-v3.6.0-rc.3-linux-amd64.tar.gz



# Make the binaries executable.
ls -l downloads/{client,cni-plugins,controller,worker}/*
sudo chmod +x downloads/{client,cni-plugins,controller,worker}/*
ls -l downloads/{client,cni-plugins,controller,worker}/*

# 일부 파일 소유자 변경
tree -ug downloads # cat /etc/passwd | grep vagrant &amp;amp;&amp;amp; cat /etc/group | grep vagrant
sudo chown root:root downloads/client/etcdctl
sudo chown root:root downloads/controller/etcd
sudo chown root:root downloads/worker/crictl
tree -ug downloads


# kubernetes client 도구인 kubectl를 설치
ls -l downloads/client/kubectl
cp downloads/client/kubectl /usr/local/bin/

# can be verified by running the kubectl command:
kubectl version --client
Client Version: v1.32.3
Kustomize Version: v5.5.0&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;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;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;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;p data-ke-size=&quot;size16&quot;&gt;[K8S Docs] &lt;span data-token-index=&quot;1&quot;&gt;Release*&lt;/span&gt; : 1년에 3개의 &lt;span data-token-index=&quot;3&quot;&gt;마이너&lt;/span&gt; 버전 출시 &amp;rarr; 최근 3개 버전 release branches(&lt;span data-token-index=&quot;5&quot;&gt;패치)&lt;/span&gt; 지원 &lt;span data-token-index=&quot;7&quot;&gt;(그이상 힘듬..)&lt;/span&gt; - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://kubernetes.io/releases/&quot; data-token-index=&quot;9&quot;&gt;&lt;span&gt;Link&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uFym3/dJMcac9AK1j/MiA6BjR134j9PGTmdXeVN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uFym3/dJMcac9AK1j/MiA6BjR134j9PGTmdXeVN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uFym3/dJMcac9AK1j/MiA6BjR134j9PGTmdXeVN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuFym3%2FdJMcac9AK1j%2FMiA6BjR134j9PGTmdXeVN0%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;878&quot; height=&quot;651&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;651&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;763&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwD2DT/dJMcad1I5yP/obviOuQpPGYi2lcB1KhBWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwD2DT/dJMcad1I5yP/obviOuQpPGYi2lcB1KhBWK/img.png&quot; data-alt=&quot;https://kubernetes.io/releases/patch-releases/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwD2DT/dJMcad1I5yP/obviOuQpPGYi2lcB1KhBWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwD2DT%2FdJMcad1I5yP%2FobviOuQpPGYi2lcB1KhBWK%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;763&quot; height=&quot;803&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://kubernetes.io/releases/patch-releases/&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;Download - &lt;a href=&quot;https://kubernetes.io/releases/download/&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Release Cycle - &lt;a href=&quot;https://kubernetes.io/releases/release/&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Patch Releases - &lt;a href=&quot;https://kubernetes.io/releases/patch-releases/&quot;&gt;Link&lt;/a&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;Kubernetes 1.32가 유지보수 모드로 전환되었습니다.2025년 12월 28일&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;Kubernetes 1.32의 지원 종료일은 다음과 같습니다.2026년 2월 28일.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Release Managers - &lt;a href=&quot;https://kubernetes.io/releases/release-managers/&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Release Notes - &lt;a href=&quot;https://kubernetes.io/releases/notes/&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Version Skew Policy - &lt;a href=&quot;https://kubernetes.io/releases/version-skew-policy/&quot;&gt;Link&lt;/a&gt;*
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kube-apiserver&lt;/b&gt; : HA apiserver 경우 newest 와 oldest 가 1개 마이너 버전 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;newest&amp;nbsp;kube-apiserver&amp;nbsp;is at&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;other&amp;nbsp;kube-apiserver&amp;nbsp;instances are supported at&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;&amp;nbsp;and&amp;nbsp;&lt;b&gt;1.31&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubelet/kube-proxy&lt;/b&gt; : kubelet 는 apiserver 보다 높을수 없음. 추가로 apiserver 보다 3개 older 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-apiserver&amp;nbsp;is at&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;kubelet&amp;nbsp;is supported at&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;,&amp;nbsp;&lt;b&gt;1.31&lt;/b&gt;,&amp;nbsp;&lt;b&gt;1.30&lt;/b&gt;, and&amp;nbsp;&lt;b&gt;1.29&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;만약 apiserver 가 HA로 1.32, 1.31 있다면 kubelet 는 1.32는 안되고, 1.31, 1.30, 1.29 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kube-controller-manager, kube-scheduler, and cloud-controller-manager&lt;/b&gt; : skip&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kubectl&lt;/b&gt; : one minor version (older or newer) of&amp;nbsp;kube-apiserver
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kube-apiserver&amp;nbsp;is at&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;kubectl&amp;nbsp;is supported at&amp;nbsp;&lt;b&gt;1.33&lt;/b&gt;,&amp;nbsp;&lt;b&gt;1.32&lt;/b&gt;, and&amp;nbsp;&lt;b&gt;1.31&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Provisioning Compute Resources&lt;/span&gt; &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;☞&amp;nbsp; To ssh jumpbox&lt;/p&gt;
&lt;pre id=&quot;code_1767848309358&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Machine Database (서버 속성 저장 파일) : IPV4_ADDRESS FQDN HOSTNAME POD_SUBNET
## 참고) server(controlplane)는 kubelet 동작하지 않아서, 파드 네트워크 대역 설정 필요 없음
cat &amp;lt;&amp;lt;EOF &amp;gt; machines.txt
192.168.7.246 server.kubernetes.local server
192.168.7.247 node-0.kubernetes.local node-0 10.200.0.0/24
192.168.7.248 node-1.kubernetes.local node-1 10.200.1.0/24
EOF
cat machines.txt

while read IP FQDN HOST SUBNET; do
  echo &quot;${IP} ${FQDN} ${HOST} ${SUBNET}&quot;
done &amp;lt; machines.txt

192.168.7.246 server.kubernetes.local server
192.168.7.247 node-0.kubernetes.local node-0 10.200.0.0/24
192.168.7.248 node-1.kubernetes.local node-1 10.200.1.0/24



# Configuring SSH Access 설정
 
# sshd config 설정 파일 확인 : 이미 암호 기반 인증 접속 설정 되어 있음
grep &quot;^[^#]&quot; /etc/ssh/sshd_config

PasswordAuthentication yes
KbdInteractiveAuthentication no
UsePAM yes
X11Forwarding yes
PrintMotd no
AcceptEnv LANG LC_*
Subsystem       sftp    /usr/lib/openssh/sftp-server
ClientAliveInterval 120
PermitRootLogin yes



...
PasswordAuthentication yes
PermitRootLogin yes

# Generate a new SSH key
ssh-keygen -t rsa -N &quot;&quot; -f /root/.ssh/id_rsa
debian@jumpbox:~/kubernetes-the-hard-way$ sudo ls -l /root/.ssh
total 16
-rw------- 1 root root    0 Jan  5 19:55 authorized_keys
-rw------- 1 root root 2602 Jan  8 16:20 id_rsa
-rw-r--r-- 1 root root  566 Jan  8 16:20 id_rsa.pub

# Copy the SSH public key to each machine
while read IP FQDN HOST SUBNET; do
  sshpass -p 'qwe123' ssh-copy-id -o StrictHostKeyChecking=no root@${IP}
done &amp;lt; machines.txt

  sshpass -p '1' ssh-copy-id -o StrictHostKeyChecking=no root@${IP}
done &amp;lt; machines.txt
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: &quot;/root/.ssh/id_rsa.pub&quot;
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   &quot;ssh -o 'StrictHostKeyChecking=no' 'root@192.168.7.246'&quot;
and check to make sure that only the key(s) you wanted were added.

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: &quot;/root/.ssh/id_rsa.pub&quot;
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   &quot;ssh -o 'StrictHostKeyChecking=no' 'root@192.168.7.247'&quot;
and check to make sure that only the key(s) you wanted were added.

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: &quot;/root/.ssh/id_rsa.pub&quot;
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added: 1

Now try logging into the machine, with:   &quot;ssh -o 'StrictHostKeyChecking=no' 'root@192.168.7.248'&quot;
and check to make sure that only the key(s) you wanted were added.



while read IP FQDN HOST SUBNET; do
  ssh -n root@${IP} cat /root/.ssh/authorized_keys
done &amp;lt; machines.txt

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeZbf084vEL9Whhj7FWB6NGxTA7n1eHjTrxG6+gyof6ANcvfKEzTPwHOPZlkyQS+SxizkTz/QU0tNUbyEf0YthLO95VrtF/obB2Yh6TifECug6QNNlxn901hbttU8TQgffsY/Zwqnl7amdqN6/RNu5iRFmGLrE9hjmg8nuJ1uAxtAz2ifbWwgIuyRANbycFLQ7u/Hm+gfOcRPwlFRojcLq2HEPn8LiZkfRnQTTtsXfM7pmFmacHHdB/Xvx+8ZAQhBMMBFl5EPF6kdu56VHl/0cmJ4fwzsfEgKRUPMBW9Xns3BZ42W4UfTpPzoZS9klZxttHKiJNy00TBVRJW0UUVjUJUgNA4T1bstd8OlMvjqYmmKTgZQxbKkU5TLguZFYujS4bP4VqGO8lxBErUpZqPNVaYhQjIaJnLZjj9nbxjZzp2tv6ckYWDmjN94VV1Rpcq4USPI+Q9xQiJ0VW2Nye4Mip/vGqZvPmb4nFlO91LhLRJ3ZuATE4SVBffi1R5WUny0= root@jumpbox
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0YRbcxtxXdfdGp6AL/pHNgu5WvGbqF+adax8VXfg/IJC/iPWHE2srBjb0TyEA2fqOX8Y97c67cpCFLmbEk7gDHzzo+gHc5zgA6cBg3DCg6q0oPpaxlhIm9uE7XzVevPSe6NbmCNPaDPefToJ9GAsOtGzcc8gz7X5C9YgvChstHbSuDW9EZJvKJkN3MO0cPy5LQempuRjpme2LEHTR+L4YPpnT/bil6hVglbx4JZOAetdgogUb4aczAFYGPYQIS8xn9J8u1GtC09Tjq8cIOBac4NDeiRHI5wT1xCPakc6o/faEpLDIduaVDKKrneBlYu5SfEEHwJDSoaTfY/dORk24PgNV2WflzuaNGb1zF+EicPopGmQMvU2p70fa4ZtX4wTdYsGXlUSd22aogqH3Qv7WINdN6SOjyKc9wIQL7p7rbcfxzbU9UWnGHPN/ze8N0hwlMoO1wMWaPXe7lXgrRJw88FDio67Vipu0lloAOdWS1zCDjzH2UAp4IZ/Vc7FMGYM= root@jumpbox
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeZbf084vEL9Whhj7FWB6NGxTA7n1eHjTrxG6+gyof6ANcvfKEzTPwHOPZlkyQS+SxizkTz/QU0tNUbyEf0YthLO95VrtF/obB2Yh6TifECug6QNNlxn901hbttU8TQgffsY/Zwqnl7amdqN6/RNu5iRFmGLrE9hjmg8nuJ1uAxtAz2ifbWwgIuyRANbycFLQ7u/Hm+gfOcRPwlFRojcLq2HEPn8LiZkfRnQTTtsXfM7pmFmacHHdB/Xvx+8ZAQhBMMBFl5EPF6kdu56VHl/0cmJ4fwzsfEgKRUPMBW9Xns3BZ42W4UfTpPzoZS9klZxttHKiJNy00TBVRJW0UUVjUJUgNA4T1bstd8OlMvjqYmmKTgZQxbKkU5TLguZFYujS4bP4VqGO8lxBErUpZqPNVaYhQjIaJnLZjj9nbxjZzp2tv6ckYWDmjN94VV1Rpcq4USPI+Q9xQiJ0VW2Nye4Mip/vGqZvPmb4nFlO91LhLRJ3ZuATE4SVBffi1R5WUny0= root@jumpbox
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0YRbcxtxXdfdGp6AL/pHNgu5WvGbqF+adax8VXfg/IJC/iPWHE2srBjb0TyEA2fqOX8Y97c67cpCFLmbEk7gDHzzo+gHc5zgA6cBg3DCg6q0oPpaxlhIm9uE7XzVevPSe6NbmCNPaDPefToJ9GAsOtGzcc8gz7X5C9YgvChstHbSuDW9EZJvKJkN3MO0cPy5LQempuRjpme2LEHTR+L4YPpnT/bil6hVglbx4JZOAetdgogUb4aczAFYGPYQIS8xn9J8u1GtC09Tjq8cIOBac4NDeiRHI5wT1xCPakc6o/faEpLDIduaVDKKrneBlYu5SfEEHwJDSoaTfY/dORk24PgNV2WflzuaNGb1zF+EicPopGmQMvU2p70fa4ZtX4wTdYsGXlUSd22aogqH3Qv7WINdN6SOjyKc9wIQL7p7rbcfxzbU9UWnGHPN/ze8N0hwlMoO1wMWaPXe7lXgrRJw88FDio67Vipu0lloAOdWS1zCDjzH2UAp4IZ/Vc7FMGYM= root@jumpbox
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeZbf084vEL9Whhj7FWB6NGxTA7n1eHjTrxG6+gyof6ANcvfKEzTPwHOPZlkyQS+SxizkTz/QU0tNUbyEf0YthLO95VrtF/obB2Yh6TifECug6QNNlxn901hbttU8TQgffsY/Zwqnl7amdqN6/RNu5iRFmGLrE9hjmg8nuJ1uAxtAz2ifbWwgIuyRANbycFLQ7u/Hm+gfOcRPwlFRojcLq2HEPn8LiZkfRnQTTtsXfM7pmFmacHHdB/Xvx+8ZAQhBMMBFl5EPF6kdu56VHl/0cmJ4fwzsfEgKRUPMBW9Xns3BZ42W4UfTpPzoZS9klZxttHKiJNy00TBVRJW0UUVjUJUgNA4T1bstd8OlMvjqYmmKTgZQxbKkU5TLguZFYujS4bP4VqGO8lxBErUpZqPNVaYhQjIaJnLZjj9nbxjZzp2tv6ckYWDmjN94VV1Rpcq4USPI+Q9xQiJ0VW2Nye4Mip/vGqZvPmb4nFlO91LhLRJ3ZuATE4SVBffi1R5WUny0= root@jumpbox
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0YRbcxtxXdfdGp6AL/pHNgu5WvGbqF+adax8VXfg/IJC/iPWHE2srBjb0TyEA2fqOX8Y97c67cpCFLmbEk7gDHzzo+gHc5zgA6cBg3DCg6q0oPpaxlhIm9uE7XzVevPSe6NbmCNPaDPefToJ9GAsOtGzcc8gz7X5C9YgvChstHbSuDW9EZJvKJkN3MO0cPy5LQempuRjpme2LEHTR+L4YPpnT/bil6hVglbx4JZOAetdgogUb4aczAFYGPYQIS8xn9J8u1GtC09Tjq8cIOBac4NDeiRHI5wT1xCPakc6o/faEpLDIduaVDKKrneBlYu5SfEEHwJDSoaTfY/dORk24PgNV2WflzuaNGb1zF+EicPopGmQMvU2p70fa4ZtX4wTdYsGXlUSd22aogqH3Qv7WINdN6SOjyKc9wIQL7p7rbcfxzbU9UWnGHPN/ze8N0hwlMoO1wMWaPXe7lXgrRJw88FDio67Vipu0lloAOdWS1zCDjzH2UAp4IZ/Vc7FMGYM= root@jumpbox





# Once each key is added, verify SSH public key access is working
# 아래는 IP 기반으로 접속 확인

while read IP FQDN HOST SUBNET; do
  ssh -n root@${IP} hostname
done &amp;lt; machines.txt


server
node-0
node-1



# Hostnames 설정

# 확인 : init_cfg.sh 로 이미 설정되어 있음
while read IP FQDN HOST SUBNET; do
  ssh -n root@${IP} cat /etc/hosts
done &amp;lt; machines.txt

while read IP FQDN HOST SUBNET; do
  ssh -n root@${IP} hostname --fqdn
done &amp;lt; machines.txt

# 아래는 hostname 으로 ssh 접속 확인
cat /etc/hosts
while read IP FQDN HOST SUBNET; do
  sshpass -p 'qwe123' ssh -n -o StrictHostKeyChecking=no root@${HOST} hostname
done &amp;lt; machines.txt

while read IP FQDN HOST SUBNET; do
  sshpass -p 'qwe123' ssh -n root@${HOST} uname -o -m -n
done &amp;lt; machines.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767848782033&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Your system has configured 'manage_etc_hosts' as True.
# As a result, if you wish for changes to this file to persist
# then you will need to either
# a.) make changes to the master file in /etc/cloud/templates/hosts.debian.tmpl
# b.) change or remove the value of 'manage_etc_hosts' in
#     /etc/cloud/cloud.cfg or cloud-config from user-data
#
127.0.0.1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

192.168.7.245 jumpbox
192.168.7.246 server.kubernetes.local server
192.168.7.247 node-0.kubernetes.local node-0
192.168.7.248 node-1.kubernetes.local node-1&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;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;Provisioning a CA and Generating TLS Certificates&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;☞ CA 설정 및 TLS 인증서 생성 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/04-certificate-authority.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;table id=&quot;2de50aec-5edf-81c5-a67a-e9508ce9dc4c&quot; style=&quot;border-collapse: collapse; width: 110%; height: 255px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;개인키&lt;/td&gt;
&lt;td&gt;CSR &amp;nbsp;&lt;/td&gt;
&lt;td&gt;인증서&lt;/td&gt;
&lt;td&gt;참고 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-811f-a8e9-ec2fa6529260&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 17px; width: 13.9535%;&quot;&gt;Root CA&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 17px; width: 16.8076%;&quot;&gt;ca.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 17px; width: 14.6934%;&quot;&gt;X&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 17px; width: 16.7019%;&quot;&gt;ca.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 17px; width: 37.7378%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8193-8c09-e84bbf4590e4&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 17px; width: 13.9535%;&quot;&gt;admin&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 17px; width: 16.8076%;&quot;&gt;admin.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 17px; width: 14.6934%;&quot;&gt;admin.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 17px; width: 16.7019%;&quot;&gt;admin.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 17px; width: 37.7378%;&quot;&gt;CN = admin, O = system:masters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8190-94cc-fd4f205448a0&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 17px; width: 13.9535%;&quot;&gt;node-0&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 17px; width: 16.8076%;&quot;&gt;node-0.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 17px; width: 14.6934%;&quot;&gt;node-0.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 17px; width: 16.7019%;&quot;&gt;node-0.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 17px; width: 37.7378%;&quot;&gt;CN = system:node:node-0, O = system:nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8103-bb1d-e39503cab38a&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 17px; width: 13.9535%;&quot;&gt;node-1&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 17px; width: 16.8076%;&quot;&gt;node-1.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 17px; width: 14.6934%;&quot;&gt;node-1.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 17px; width: 16.7019%;&quot;&gt;node-1.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 17px; width: 37.7378%;&quot;&gt;CN = system:node:node-1, O = system:nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-816c-b231-e71bd0a5e4b2&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 17px; width: 13.9535%;&quot;&gt;kube-proxy&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 17px; width: 16.8076%;&quot;&gt;kube-proxy.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 17px; width: 14.6934%;&quot;&gt;kube-proxy.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 17px; width: 16.7019%;&quot;&gt;kube-proxy.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 17px; width: 37.7378%;&quot;&gt;CN = system:kube-proxy, O = system:node-proxier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81d6-90b3-cdd91caf4c2e&quot; style=&quot;height: 35px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 35px; width: 13.9535%;&quot;&gt;kube-scheduler&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 35px; width: 16.8076%;&quot;&gt;kube-scheduler.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 35px; width: 14.6934%;&quot;&gt;kube-scheduler&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 35px; width: 16.7019%;&quot;&gt;kube-scheduler.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 35px; width: 37.7378%;&quot;&gt;CN = system:kube-scheduler, O = system:kube-scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81a1-a496-cf6f2c7bcb0f&quot; style=&quot;height: 35px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 35px; width: 13.9535%;&quot;&gt;kube-controller-manager&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 35px; width: 16.8076%;&quot;&gt;kube-controller-manager.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 35px; width: 14.6934%;&quot;&gt;kube-controller-manager.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 35px; width: 16.7019%;&quot;&gt;kube-controller-manager.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 35px; width: 37.7378%;&quot;&gt;CN = system:kube-controller-manager, O = system:kube-controller-manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81dc-b6a0-e07d5b5692fb&quot; style=&quot;height: 35px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 35px; width: 13.9535%;&quot;&gt;kube-api-server&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 35px; width: 16.8076%;&quot;&gt;kube-api-server.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 35px; width: 14.6934%;&quot;&gt;kube-api-server.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 35px; width: 16.7019%;&quot;&gt;kube-api-server.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 35px; width: 37.7378%;&quot;&gt;CN = kubernetes, SAN: IP(127.0.0.1, &lt;b&gt;10.32.0.1&lt;/b&gt;), DNS(kubernetes,..)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8158-8781-c41dc06e65f2&quot; style=&quot;height: 35px;&quot;&gt;
&lt;td id=&quot;}qZ&amp;gt;&quot; style=&quot;height: 35px; width: 13.9535%;&quot;&gt;service-accounts&lt;/td&gt;
&lt;td id=&quot;vnLq&quot; style=&quot;height: 35px; width: 16.8076%;&quot;&gt;service-accounts.key&lt;/td&gt;
&lt;td id=&quot;h]=Z&quot; style=&quot;height: 35px; width: 14.6934%;&quot;&gt;service-accounts.csr&lt;/td&gt;
&lt;td id=&quot;YdPc&quot; style=&quot;height: 35px; width: 16.7019%;&quot;&gt;service-accounts.crt&lt;/td&gt;
&lt;td id=&quot;RVNz&quot; style=&quot;height: 35px; width: 37.7378%;&quot;&gt;CN = service-accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;2de50aec-5edf-818d-a3de-d443458b416a&quot; style=&quot;border-collapse: collapse; width: 51.3953%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 22.6744%;&quot;&gt;네트워크 대역&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81a3-94e7-de2b3302205f&quot;&gt;
&lt;td id=&quot;huHS&quot; style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;clusterCIDR&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;lb{B&quot; style=&quot;width: 22.6744%;&quot;&gt;10.200.0.0/16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8180-9a30-db109769627e&quot;&gt;
&lt;td id=&quot;huHS&quot; style=&quot;width: 28.6047%;&quot;&gt;&amp;rarr; node-0 PodCIDR&lt;/td&gt;
&lt;td id=&quot;lb{B&quot; style=&quot;width: 22.6744%;&quot;&gt;10.200.0.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8166-a723-c29836d2da9f&quot;&gt;
&lt;td id=&quot;huHS&quot; style=&quot;width: 28.6047%;&quot;&gt;&amp;rarr; node-1 PodCIDR&lt;/td&gt;
&lt;td id=&quot;lb{B&quot; style=&quot;width: 22.6744%;&quot;&gt;10.200.1.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81c0-b00b-e21ea25b6ae9&quot;&gt;
&lt;td id=&quot;huHS&quot; style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;ServiceCIDR&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;lb{B&quot; style=&quot;width: 22.6744%;&quot;&gt;&lt;b&gt;10.32.0.0/24&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81b6-86ae-c1969fec03c8&quot;&gt;
&lt;td id=&quot;huHS&quot; style=&quot;width: 28.6047%;&quot;&gt;&amp;rarr; &lt;b&gt;api clusterIP&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;lb{B&quot; style=&quot;width: 22.6744%;&quot;&gt;&lt;b&gt;10.32.0.1&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; (참고) kind k8s 에서 인증서 관련 파일 확인 &lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1767849229350&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update &amp;amp;&amp;amp; apt install tree yq jq -y'

# Check certificates expiration for a Kubernetes cluster
docker exec -i myk8s-control-plane kubeadm certs check-expiration

[check-expiration] Reading configuration from the &quot;kubeadm-config&quot; ConfigMap in namespace &quot;kube-system&quot;...
[check-expiration] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it.

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Jan 06, 2027 06:59 UTC   363d            ca                      no
apiserver                  Jan 06, 2027 06:59 UTC   363d            ca                      no
apiserver-etcd-client      Jan 06, 2027 06:59 UTC   363d            etcd-ca                 no
apiserver-kubelet-client   Jan 06, 2027 06:59 UTC   363d            ca                      no
controller-manager.conf    Jan 06, 2027 06:59 UTC   363d            ca                      no
etcd-healthcheck-client    Jan 06, 2027 06:59 UTC   363d            etcd-ca                 no
etcd-peer                  Jan 06, 2027 06:59 UTC   363d            etcd-ca                 no
etcd-server                Jan 06, 2027 06:59 UTC   363d            etcd-ca                 no
front-proxy-client         Jan 06, 2027 06:59 UTC   363d            front-proxy-ca          no
scheduler.conf             Jan 06, 2027 06:59 UTC   363d            ca                      no
super-admin.conf           Jan 06, 2027 06:59 UTC   363d            ca                      no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Jan 04, 2036 06:59 UTC   9y              no
etcd-ca                 Jan 04, 2036 06:59 UTC   9y              no
front-proxy-ca          Jan 04, 2036 06:59 UTC   9y              no


#
docker exec -it myk8s-control-plane tree /etc/kubernetes

/etc/kubernetes
|-- admin.conf
|-- controller-manager.conf
|-- kubelet.conf
|-- manifests
|   |-- etcd.yaml
|   |-- kube-apiserver.yaml
|   |-- kube-controller-manager.yaml
|   `-- kube-scheduler.yaml
|-- pki
|   |-- apiserver-etcd-client.crt
|   |-- apiserver-etcd-client.key
|   |-- apiserver-kubelet-client.crt
|   |-- apiserver-kubelet-client.key
|   |-- apiserver.crt
|   |-- apiserver.key
|   |-- ca.crt
|   |-- ca.key
|   |-- etcd
|   |   |-- ca.crt
|   |   |-- ca.key
|   |   |-- healthcheck-client.crt
|   |   |-- healthcheck-client.key
|   |   |-- peer.crt
|   |   |-- peer.key
|   |   |-- server.crt
|   |   `-- server.key
|   |-- front-proxy-ca.crt
|   |-- front-proxy-ca.key
|   |-- front-proxy-client.crt
|   |-- front-proxy-client.key
|   |-- sa.key
|   `-- sa.pub
|-- scheduler.conf
`-- super-admin.conf

4 directories, 31 files&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;☞ cat ca.conf 파일 내용 확인&lt;/div&gt;
&lt;table id=&quot;2de50aec-5edf-8101-bd2d-d78c1c872a19&quot; style=&quot;border-collapse: collapse; width: 59.6512%; height: 232px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-816d-8bb9-e2effff4a1da&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[req]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;OpenSSL 요청 기본 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8186-917c-c266d77e0a75&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[ca_*]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;CA 인증서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-811b-b18b-f2284ec42ce5&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[admin]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;관리자 (kubectl)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81b3-88ff-eef31ae1a636&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[service-accounts]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;ServiceAccount 토큰 서명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81f2-9fc4-e5213aa92ffd&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[node-*]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;워커 노드(kubelet)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-812a-b196-ff8fe6edceba&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 17px;&quot;&gt;[kube-proxy]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 17px;&quot;&gt;kube-proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81e4-8fb6-df62f0b0f78a&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[kube-controller-manager]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;컨트롤러&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-814e-a950-eff9f18de26a&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[kube-scheduler]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;스케줄러&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-814d-862f-df3af2563718&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 17px;&quot;&gt;[kube-api-server]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 17px;&quot;&gt;API Server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-81a2-9325-daf4271f10e8&quot; style=&quot;height: 22px;&quot;&gt;
&lt;td id=&quot;UzXq&quot; style=&quot;height: 22px;&quot;&gt;[default_req_extensions]&lt;/td&gt;
&lt;td id=&quot;j?Em&quot; style=&quot;height: 22px;&quot;&gt;공통 CSR 옵션&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767849373362&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[req]
distinguished_name = req_distinguished_name
prompt             = no                      # CSR 생성 시 대화형 입력 없음
x509_extensions    = ca_x509_extensions      # CA 인증서 생성 시 사용할 확장

[ca_x509_extensions]                         # CA 인증서 설정 (Root of Trust)
basicConstraints = CA:TRUE                   # CA 권한 인증서
keyUsage         = cRLSign, keyCertSign      # 다른 인증서를 서명 가능, Kubernetes 모든 인증의 신뢰 루트

[req_distinguished_name]
C   = US
ST  = Washington
L   = Seattle
CN  = CA                                     # 클러스터 CA

[admin]                                      # Admin 사용자 (kubectl)
distinguished_name = admin_distinguished_name
prompt             = no
req_extensions     = default_req_extensions

[admin_distinguished_name]
CN = admin                                   # [K8S] CN &amp;rarr; user
O  = system:masters                          # [K8S] O &amp;rarr; group , system:masters - Kubernetes 슈퍼유저 그룹, 모든 RBAC 인가 우회

# Service Accounts
#
# The Kubernetes Controller Manager leverages a key pair to generate
# and sign service account tokens as described in the
# [managing service accounts](https://kubernetes.io/docs/admin/service-accounts-admin/)
# documentation.

[service-accounts]                           # Service Account 서명자
distinguished_name = service-accounts_distinguished_name
prompt             = no
req_extensions     = default_req_extensions

[service-accounts_distinguished_name] 
CN = service-accounts                        # controller-manager가 사용하는 ServiceAccount 토큰 서명용 인증서 , apiserver에서 --service-account-key-file 로 사용

# Worker Nodes
#
# Kubernetes uses a [special-purpose authorization mode](https://kubernetes.io/docs/admin/authorization/node/)
# called Node Authorizer, that specifically authorizes API requests made
# by [Kubelets](https://kubernetes.io/docs/concepts/overview/components/#kubelet).
# In order to be authorized by the Node Authorizer, Kubelets must use a credential
# that identifies them as being in the `system:nodes` group, with a username
# of `system:node:&amp;lt;nodeName&amp;gt;`.

[node-0]                                    # Worker Node 인증서 (kubelet)
distinguished_name = node-0_distinguished_name
prompt             = no
req_extensions     = node-0_req_extensions

[node-0_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth  # clientAuth: apiserver &amp;rarr; kubelet &amp;amp; serverAuth: kubelet HTTPS 서버(10250)
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Node-0 Certificate&quot;
subjectAltName       = DNS:node-0, IP:127.0.0.1
subjectKeyIdentifier = hash

[node-0_distinguished_name]
CN = system:node:node-0                     # kubelet 사용자 , CN = system:node:&amp;lt;nodeName&amp;gt;
O  = system:nodes                           # Node Authorizer 그룹  ,O = system:nodes
C  = US
ST = Washington
L  = Seattle

[node-1]
distinguished_name = node-1_distinguished_name
prompt             = no
req_extensions     = node-1_req_extensions

[node-1_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Node-1 Certificate&quot;
subjectAltName       = DNS:node-1, IP:127.0.0.1
subjectKeyIdentifier = hash

[node-1_distinguished_name]
CN = system:node:node-1
O  = system:nodes
C  = US
ST = Washington
L  = Seattle


# Kube Proxy Section
[kube-proxy]                                # kube-proxy
distinguished_name = kube-proxy_distinguished_name
prompt             = no
req_extensions     = kube-proxy_req_extensions

[kube-proxy_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Kube Proxy Certificate&quot;
subjectAltName       = DNS:kube-proxy, IP:127.0.0.1
subjectKeyIdentifier = hash

[kube-proxy_distinguished_name]
CN = system:kube-proxy
O  = system:node-proxier                    # system:node-proxier ClusterRoleBinding 존재 , 서비스 네트워크 제어 가능
C  = US
ST = Washington
L  = Seattle


# Controller Manager
[kube-controller-manager]
distinguished_name = kube-controller-manager_distinguished_name
prompt             = no
req_extensions     = kube-controller-manager_req_extensions

[kube-controller-manager_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Kube Controller Manager Certificate&quot;
subjectAltName       = DNS:kube-controller-manager, IP:127.0.0.1
subjectKeyIdentifier = hash

[kube-controller-manager_distinguished_name] # 클러스터 상태 관리, Node, ReplicaSet, SA 토큰 등 관리
CN = system:kube-controller-manager
O  = system:kube-controller-manager
C  = US
ST = Washington
L  = Seattle


# Scheduler
[kube-scheduler]
distinguished_name = kube-scheduler_distinguished_name
prompt             = no
req_extensions     = kube-scheduler_req_extensions

[kube-scheduler_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Kube Scheduler Certificate&quot;
subjectAltName       = DNS:kube-scheduler, IP:127.0.0.1
subjectKeyIdentifier = hash

[kube-scheduler_distinguished_name]
CN = system:kube-scheduler
O  = system:kube-scheduler                  # Pod 스케줄링 전용 권한
C  = US
ST = Washington
L  = Seattle


# API Server
#
# The Kubernetes API server is automatically assigned the `kubernetes`
# internal dns name, which will be linked to the first IP address (`10.32.0.1`)
# from the address range (`10.32.0.0/24`) reserved for internal cluster
# services.

[kube-api-server]                           # API Server 인증서
distinguished_name = kube-api-server_distinguished_name
prompt             = no
req_extensions     = kube-api-server_req_extensions

[kube-api-server_req_extensions]
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth, serverAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client, server
nsComment            = &quot;Kube API Server Certificate&quot;
subjectAltName       = @kube-api-server_alt_names
subjectKeyIdentifier = hash

[kube-api-server_alt_names]                 # SAN (Subject Alternative Name) : 모든 내부/외부 접근 주소
IP.0  = 127.0.0.1
IP.1  = 10.32.0.1
DNS.0 = kubernetes
DNS.1 = kubernetes.default
DNS.2 = kubernetes.default.svc
DNS.3 = kubernetes.default.svc.cluster
DNS.4 = kubernetes.svc.cluster.local
DNS.5 = server.kubernetes.local
DNS.6 = api-server.kubernetes.local

[kube-api-server_distinguished_name]
CN = kubernetes
C  = US
ST = Washington
L  = Seattle


[default_req_extensions]                    # 공통 CSR 확장 : 대부분 클라이언트 인증서 -&amp;gt; kubelet / apiserver만 serverAuth 추가
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Admin Client Certificate&quot;
subjectKeyIdentifier = hash&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;b&gt;☞ &lt;span data-token-index=&quot;0&quot;&gt;C&lt;/span&gt;ertificate &lt;span data-token-index=&quot;2&quot;&gt;A&lt;/span&gt;uthority (인증서 설치)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767849455744&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Generate the CA configuration file, certificate, and private key

# Root CA 개인키 생성 : ca.key
openssl genrsa -out ca.key 4096
ls -l ca.key
-rw------- 1 root root 3272 Jan  8 16:34 ca.key


cat ca.key
openssl rsa -in ca.key -text -noout # 개인키 구조 확인





# Root CA 인증서 생성 : ca.crt
## -x509 : CSR을 만들지 않고 바로 인증서(X.509) 생성, 즉, Self-Signed Certificate
## -noenc : 개인키를 암호화하지 않음, 즉, CA 키(ca.key)에 패스프레이즈 없음
## -config ca.conf : 인증서 세부 정보는 설정 파일에서 읽음 , [req] 섹션 사용됨 - DN 정보 &amp;rarr; [req_distinguished_name] , CA 확장 &amp;rarr; [ca_x509_extensions]
openssl req -x509 -new -sha512 -noenc \
  -key ca.key -days 3653 \
  -config ca.conf \
  -out ca.crt
ls -l ca.crt
-rw-r--r-- 1 root root 1899 Jan  8 16:36 ca.crt

# ca.conf 관련 내용
cat ca.conf
-------------------------------------------
[req]
distinguished_name = req_distinguished_name
prompt             = no
x509_extensions    = ca_x509_extensions

[ca_x509_extensions]
basicConstraints = CA:TRUE                    # 이 인증서는 CA 역할 가능
keyUsage         = cRLSign, keyCertSign       # cRLSign: 인증서 폐기 목록(CRL) 서명 가능, keyCertSign: 다른 인증서를 서명할 수 있음

[req_distinguished_name]
C   = US
ST  = Washington
L   = Seattle
CN  = CA
-------------------------------------------

cat ca.crt
openssl x509 -in ca.crt -text -noout # 인증서 전체 내용 확인
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            56:fb:42:82:5e:2f:96:cf:f5:83:2e:78:46:98:6e:3f:08:ee:99:67
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 00:46:22 2026 GMT
            Not After : Jan  4 00:46:22 2036 GMT
        Subject: C = US, ST = Washington, L = Seattle, CN = CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:ae:ce:95:7e:db:50:a2:4a:71:8b:99:54:0d:b4:
                    ce:1e:6f:13:3c:a6:54:30:0f:5b:0a:76:56:8c:44:
                    75:98:58:a6:57:7d:d2:38:e8:05:3c:cc:a5:e9:86:
                    57:73:98:c5:17:52:7c:7e:c8:48:6c:6b:86:13:1c:
                    7a:72:5d:10:3a:15:72:8d:66:35:e3:55:06:3e:f7:
                    44:7f:1b:fc:9e:4a:2b:4a:28:dd:2c:34:63:8d:26:
                    cc:39:50:3b:44:e5:f8:fe:68:c8:c0:a5:94:ba:b1:
                    d5:e3:55:1d:d9:98:0c:03:23:3f:9d:d9:a0:79:2c:
                    e9:ce:c9:92:b8:1f:6e:83:cb:08:1e:e6:28:cf:55:
                    29:b3:f3:19:1b:fe:c2:d8:30:6e:ee:68:7e:80:c3:
                    9a:53:77:d1:ae:2a:21:ee:82:94:d7:b5:f3:8f:a3:
                    98:f8:85:c6:c9:94:72:f3:1e:61:45:84:97:e4:25:
                    69:c8:5e:11:2c:75:2a:85:a6:b4:75:50:5f:a1:6c:
                    0a:54:1e:78:ab:25:a3:2e:04:18:21:68:86:11:3d:
                    90:09:95:02:aa:fc:32:2c:c5:ed:ac:d1:14:3c:d7:
                    fc:c3:a3:9f:dd:52:07:eb:2f:a7:fc:22:5e:2c:23:
                    ad:f6:f5:1e:90:db:3f:32:eb:10:38:34:c3:f0:40:
                    5d:c9:0b:d0:01:fd:78:73:0b:80:92:75:0c:24:76:
                    c1:6d:93:42:86:4b:a0:6d:99:7b:72:46:b6:52:b1:
                    2f:47:90:a0:ed:d8:93:71:23:c4:20:c8:63:04:a1:
                    f6:b6:d8:6e:6b:20:1a:2b:56:43:02:47:5e:77:ae:
                    4e:00:d5:ec:05:f6:e8:a4:ab:aa:8b:14:8b:b9:da:
                    d4:a6:e6:c6:c2:35:5a:fd:24:51:7d:29:bb:3c:d3:
                    fd:a7:bf:a9:5b:77:5a:e1:b1:7b:51:ab:29:a4:15:
                    e7:ac:f6:2a:1e:38:68:bb:f6:6f:60:e4:26:34:cc:
                    45:08:2b:e0:71:9a:8e:67:e3:0d:d4:67:63:0b:76:
                    27:bd:ff:8d:9c:78:e5:b8:55:f0:ce:c1:35:b7:b6:
                    e7:44:60:60:25:ae:f1:0f:3d:c6:7e:25:03:a3:c8:
                    87:f8:3d:cd:4b:06:1b:d1:94:63:31:50:33:5b:3f:
                    3c:66:a8:4d:df:2a:b4:76:a4:fa:54:73:43:09:ac:
                    6c:21:0b:9c:35:e9:14:ca:25:cd:f1:72:c1:fe:0f:
                    aa:56:59:1d:ea:45:a7:ab:f5:41:a5:d1:50:3d:da:
                    f0:71:ff:8b:d2:3b:04:0a:d2:80:e9:17:d6:9a:a3:
                    1a:5f:19:9b:a0:ef:08:36:d4:88:65:2b:50:42:10:
                    14:e1:a9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:TRUE
            X509v3 Key Usage: 
                Certificate Sign, CRL Sign
            X509v3 Subject Key Identifier: 
                B3:5D:82:13:B6:1C:44:59:8C:0A:4E:DB:2B:18:98:77:0D:7A:2F:5B&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; (참고) kind k8s ca.crt 정보 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1767849497488&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -i myk8s-control-plane cat /etc/kubernetes/pki/ca.crt | openssl x509 -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 2963297175414940201 (0x291fbf14b086a229)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = kubernetes
        Validity
            Not Before: Jan  8 04:37:34 2026 GMT
            Not After : Jan  6 04:42:34 2036 GMT
        Subject: CN = kubernetes
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:d2:b2:32:b5:5a:28:f4:fc:d4:0b:4a:f8:46:e8:
                    a5:cf:5a:55:56:aa:23:6b:f7:f5:12:0b:30:2e:3a:
                    b3:63:b6:1c:52:a6:2e:fa:01:12:bb:9c:8e:5f:55:
                    f1:e0:a3:25:d5:44:b7:ae:21:57:7c:a4:50:4d:8a:
                    22:d1:c3:8d:4a:03:89:5b:c2:5f:ba:f5:12:ce:b3:
                    60:43:e0:3b:ed:1f:45:89:c8:2a:e1:17:a7:06:9b:
                    dd:16:c2:61:78:c6:20:03:02:20:1f:f0:b7:46:a7:
                    c8:87:ce:6f:c1:ce:ff:93:22:cf:7d:77:c0:15:a4:
                    1e:8a:36:94:c0:34:5f:c0:aa:eb:9a:b2:07:97:b6:
                    89:c8:41:95:95:36:3c:7f:a4:97:6a:a0:3c:08:47:
                    3b:d7:75:f8:bd:3d:4d:70:ac:a6:81:2a:a4:9f:fb:
                    33:d3:84:a6:63:c7:92:1f:7a:d6:07:d8:b9:70:fa:
                    6d:b6:4e:15:17:33:4f:64:91:64:80:66:8f:37:e9:
                    87:45:b6:d3:18:06:98:f8:e3:94:e9:32:61:c8:ea:
                    3a:d4:5b:eb:b0:5c:20:9c:86:64:8a:39:08:cc:62:
                    c0:32:ed:49:5c:4d:56:08:85:e9:97:71:e0:01:78:
                    39:d5:a4:5d:66:95:e9:3f:e7:42:cf:52:34:55:8e:
                    6b:b5
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment, Certificate Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier:
                C2:48:9F:C8:3A:EE:CC:BE:0E:A9:A8:AA:5E:1B:16:3F:78:76:7B:D0
            X509v3 Subject Alternative Name:
                DNS:kubernetes
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        b0:65:db:82:8f:e8:be:65:fd:5d:d2:e0:0f:fd:df:9d:fc:b9:
        01:66:21:01:66:12:56:fd:7b:a5:e7:c7:59:b1:ec:c2:9c:a9:
        ee:ac:ac:07:26:b9:b4:9a:0f:63:2f:07:a2:63:73:4c:cd:67:
        31:4d:da:98:bc:bf:51:0c:26:36:4e:9e:72:15:b0:5e:26:44:
        00:77:7f:2e:41:80:dc:92:32:30:92:7b:64:39:22:a3:b0:fd:
        9b:8b:8a:10:56:73:fa:1d:20:47:0e:31:54:93:23:70:4d:8b:
        3f:9f:9b:bb:2e:cc:69:eb:ea:04:c7:f1:14:db:8d:47:ba:c6:
        1e:2a:f1:b0:5a:9a:0b:f4:52:35:32:64:33:92:c4:02:cc:4c:
        fc:a9:a7:a2:a1:86:0a:da:f9:c9:17:f1:81:22:cf:0d:1e:0c:
        e4:78:ff:69:7d:ed:7b:d3:ab:7c:1c:50:07:e0:2a:57:15:aa:
        b5:e7:68:28:68:2e:59:13:fa:f0:39:1d:4c:ff:e6:69:4f:52:
        c6:b8:02:65:10:4a:20:af:e0:56:5d:01:03:b8:91:ab:76:aa:
        f7:31:b1:4a:73:36:41:54:d6:30:c8:79:54:86:f8:dc:7b:7d:
        65:52:80:78:0d:df:77:0e:a0:5e:22:37:47:46:80:f6:b0:67:
        4d:29:80:d0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;b&gt;☞&lt;span&gt; Create Client and Server Certificates : &lt;span data-token-index=&quot;1&quot;&gt;admin&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767849728490&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Create Client and Server Certificates : admin
openssl genrsa -out admin.key 4096
ls -l admin.key

# ca.conf 에 admin 섹션
cat ca.conf
-------------------------------------------
[admin]
distinguished_name = admin_distinguished_name
prompt             = no
req_extensions     = default_req_extensions

[admin_distinguished_name]
CN = admin
O  = system:masters

[default_req_extensions]                    # 공통 CSR 확장
basicConstraints     = CA:FALSE
extendedKeyUsage     = clientAuth
keyUsage             = critical, digitalSignature, keyEncipherment
nsCertType           = client
nsComment            = &quot;Admin Client Certificate&quot;
subjectKeyIdentifier = hash
-------------------------------------------

# csr 파일 생성 : admin.key 개인키를 사용해 'CN=admin, O=system:masters'인 Kubernetes 관리자용 클라이언트 인증서 요청(admin.csr) 생성
openssl req -new -key admin.key -sha256 \
  -config ca.conf -section admin \
  -out admin.csr
ls -l admin.csr
openssl req -in admin.csr -text -noout # CSR 전체 내용 확인


# ca에 csr 요청을 통한 crt 파일 생성
## -req : CSR를 입력으로 받아 인증서를 생성, self-signed 아님, CA가 서명하는 방식
## -days 3653 : 인증서 유효기간 3653일 (약 10년)
## -copy_extensions copyall : CSR에 포함된 모든 X.509 extensions를 인증서로 복사
## -CAcreateserial : CA 시리얼 번호 파일 자동 생성, 다음 인증서 발급 시 재사용, 기본 생성 파일(ca.srl)
openssl x509 -req -days 3653 -in admin.csr \
  -copy_extensions copyall \
  -sha256 -CA ca.crt \
  -CAkey ca.key \
  -CAcreateserial \
  -out admin.crt
Certificate request self-signature ok
subject=CN = admin, O = system:masters

ls -l admin.crt
openssl x509 -in admin.crt -text -noout
...
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 01:01:40 2026 GMT
            Not After : Jan  4 01:01:40 2036 GMT
        Subject: CN = admin, O = system:masters
        ...
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Extended Key Usage: 
                TLS Web Client Authentication
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Admin Client Certificate
            ...&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;(참고)&amp;nbsp;system:masters&amp;nbsp;그룹에&amp;nbsp;속한&amp;nbsp;사용자&amp;nbsp;특징&amp;nbsp;:&amp;nbsp;인증&amp;nbsp;후&amp;nbsp;&amp;rarr;&amp;nbsp;인가&amp;nbsp;우회.&amp;nbsp;즉,&amp;nbsp;인증&amp;nbsp;시&amp;nbsp;바로&amp;nbsp;슈퍼유저&amp;nbsp;권한&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RBAC 기반 권한검사(Role/ClusterRole)를 &lt;b&gt;거치지 않음.&lt;/b&gt; 웹훅 기반 인가도 &lt;b&gt;거치지 않음.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;즉, API 서버에서 모든 동작을 무조건 허용함. 실질적으로 클러스터 슈퍼유저(superuser) 권한&lt;/li&gt;
&lt;li&gt;system:masters 그룹은 API 서버에 대한 &lt;b&gt;무제한(superuser) 접근 권한을 부여&lt;/b&gt;하는 &lt;b&gt;내장(built-in) 그룹.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이 그룹에 속한 사용자는 &lt;b&gt;모든 요청에 대해 RBAC이나 Webhook 같은 인가(authorization) 검사를 받지 않고 API 접근을 허용받음.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;따라서 일반적인 RBAC 정책(ClusterRole, ClusterRoleBinding)과 상관 없이 &lt;b&gt;전체 클러스터에 대한 관리자 권한을 갖습니다&lt;/b&gt; &amp;rarr; 즉 인가 검증이 사실상 우회됨.&lt;/li&gt;
&lt;li&gt;공식 체크리스트에서도 이 그룹의 사용은 최소화하라고 권고. (부트스트랩 이후에는 사용자/컴포넌트에 system:masters 인증 정보로 접근하지 않아야 함) &amp;rarr; &lt;b&gt;AWS Root 계정&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;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;b&gt;☞&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Create Client and Server Certificates :&lt;span&gt; 나머지 전부&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767849891186&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ca.conf 수정
cat ca.conf | grep system:kube-scheduler
CN = system:kube-scheduler
O  = system:system:kube-scheduler
sed -i 's/system:system:kube-scheduler/system:kube-scheduler/' ca.conf
cat ca.conf | grep system:kube-scheduler
CN = system:kube-scheduler
O  = system:kube-scheduler


# 변수 지정
certs=(
  &quot;node-0&quot; &quot;node-1&quot;
  &quot;kube-proxy&quot; &quot;kube-scheduler&quot;
  &quot;kube-controller-manager&quot;
  &quot;kube-api-server&quot;
  &quot;service-accounts&quot;
)

# 확인
echo ${certs[*]}
node-0 node-1 kube-proxy kube-scheduler kube-controller-manager kube-api-server service-accounts

# 개인키 생성, csr 생성, 인증서 생성
for i in ${certs[*]}; do
  openssl genrsa -out &quot;${i}.key&quot; 4096

  openssl req -new -key &quot;${i}.key&quot; -sha256 \
    -config &quot;ca.conf&quot; -section ${i} \
    -out &quot;${i}.csr&quot;

  openssl x509 -req -days 3653 -in &quot;${i}.csr&quot; \
    -copy_extensions copyall \
    -sha256 -CA &quot;ca.crt&quot; \
    -CAkey &quot;ca.key&quot; \
    -CAcreateserial \
    -out &quot;${i}.crt&quot;
done
Certificate request self-signature ok
subject=CN = system:node:node-0, O = system:nodes, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = system:node:node-1, O = system:nodes, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = system:kube-proxy, O = system:node-proxier, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = system:kube-scheduler, O = system:kube-scheduler, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = system:kube-controller-manager, O = system:kube-controller-manager, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = kubernetes, C = US, ST = Washington, L = Seattle
Certificate request self-signature ok
subject=CN = service-accounts





ls -1 *.crt *.key *.csr
admin.crt
admin.csr
admin.key
ca.crt
ca.key
kube-api-server.crt
kube-api-server.csr
kube-api-server.key
kube-controller-manager.crt
kube-controller-manager.csr
kube-controller-manager.key
kube-proxy.crt
kube-proxy.csr
kube-proxy.key
kube-scheduler.crt
kube-scheduler.csr
kube-scheduler.key
node-0.crt
node-0.csr
node-0.key
node-1.crt
node-1.csr
node-1.key
service-accounts.crt
service-accounts.csr
service-accounts.key


# 인증서 정보 확인
openssl x509 -in node-0.crt -text -noout
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 02:41:09 2026 GMT
            Not After : Jan  4 02:41:09 2036 GMT
        Subject: CN = system:node:node-0, O = system:nodes, C = US, ST = Washington, L = Seattle
        ...
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Extended Key Usage: 
                TLS Web Client Authentication, TLS Web Server Authentication
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Node-0 Certificate
            X509v3 Subject Alternative Name: 
                DNS:node-0, IP Address:127.0.0.1

openssl x509 -in node-1.crt -text -noout
        Subject: CN = system:node:node-1, O = system:nodes, C = US, ST = Washington, L = Seattle
            X509v3 Subject Alternative Name: 
                DNS:node-1, IP Address:127.0.0.1

openssl x509 -in kube-proxy.crt -text -noout
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 02:41:13 2026 GMT
            Not After : Jan  4 02:41:13 2036 GMT
        Subject: CN = system:kube-proxy, O = system:node-proxier, C = US, ST = Washington, L = Seattle
        ...
        X509v3 extensions:
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Kube Proxy Certificate
            X509v3 Subject Alternative Name: 
                DNS:kube-proxy, IP Address:127.0.0.1

openssl x509 -in kube-scheduler.crt -text -noout
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 02:41:13 2026 GMT
            Not After : Jan  4 02:41:13 2036 GMT
        Subject: CN = system:kube-scheduler, O = system:kube-scheduler, C = US, ST = Washington, L = Seattle
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Kube Scheduler Certificate
            X509v3 Subject Alternative Name: 
                DNS:kube-scheduler, IP Address:127.0.0.1

openssl x509 -in kube-controller-manager.crt -text -noout
        Validity
            Not Before: Jan  3 02:41:16 2026 GMT
            Not After : Jan  4 02:41:16 2036 GMT
        Subject: CN = system:kube-controller-manager, O = system:kube-controller-manager, C = US, ST = Washington, L = Seattle
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Kube Controller Manager Certificate
            X509v3 Subject Alternative Name: 
                DNS:kube-controller-manager, IP Address:127.0.0.1

# api-server : SAN 정보에 10.32.0.1 은 kubernetes (Service) ClusterIP. 다른 인증서와 다르게 SSL Server 역할 추가 확인
openssl x509 -in kube-api-server.crt -text -noout
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 02:41:17 2026 GMT
            Not After : Jan  4 02:41:17 2036 GMT
        Subject: CN = kubernetes, C = US, ST = Washington, L = Seattle
            Netscape Cert Type: 
                SSL Client, SSL Server
            Netscape Comment: 
                Kube API Server Certificate
            X509v3 Subject Alternative Name: 
                IP Address:127.0.0.1, IP Address:10.32.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.svc.cluster.local, DNS:server.kubernetes.local, DNS:api-server.kubernetes.local

# service-accounts
openssl x509 -in service-accounts.crt -text -noout
        Issuer: C = US, ST = Washington, L = Seattle, CN = CA
        Validity
            Not Before: Jan  3 02:41:17 2026 GMT
            Not After : Jan  4 02:41:17 2036 GMT
        Subject: CN = service-accounts
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Extended Key Usage: 
                TLS Web Client Authentication
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            Netscape Cert Type: 
                SSL Client
            Netscape Comment: 
                Admin Client Certificate&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;(참고) kind k8s 에 관련 정보 : 인증서 등&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767850102247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
docker exec -it myk8s-control-plane tree /etc/kubernetes
docker exec -it myk8s-control-plane tree /var/lib/kubelet/pki


# node 인증서 정보
docker exec -it myk8s-control-plane tree /var/lib/kubelet/pki
/var/lib/kubelet/pki
|-- kubelet-client-2026-01-04-01-20-27.pem
|-- kubelet-client-current.pem -&amp;gt; /var/lib/kubelet/pki/kubelet-client-2026-01-04-01-20-27.pem
|-- kubelet.crt
`-- kubelet.key

docker exec -i myk8s-control-plane cat /var/lib/kubelet/pki/kubelet.crt | openssl x509 -text -noout
        Issuer: CN=myk8s-control-plane-ca@1767489627
        Validity
            Not Before: Jan  4 00:20:27 2026 GMT
            Not After : Jan  4 00:20:27 2027 GMT
        Subject: CN=myk8s-control-plane@1767489627
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier:
                5A:E4:1B:29:0F:79:7E:EB:5A:A2:BC:5B:B2:51:70:5F:90:D2:EC:9B
            X509v3 Subject Alternative Name:
                DNS:myk8s-control-plane

docker exec -i myk8s-worker cat /var/lib/kubelet/pki/kubelet.crt | openssl x509 -text -noout
        Subject: CN=myk8s-worker@1767489640
        ...
            X509v3 Subject Alternative Name:
                DNS:myk8s-worker


# kube-proxy 인증서 정보
kubectl get cm -n kube-system kube-proxy -o yaml
data:
  config.conf: |-
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    bindAddress: 0.0.0.0
    bindAddressHardFail: false
    clientConnection:
      acceptContentTypes: &quot;&quot;
      burst: 0
      contentType: &quot;&quot;
      kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
...
  kubeconfig.conf: |-
    apiVersion: v1
    kind: Config
    clusters:
    - cluster:
        certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        server: https://myk8s-control-plane:6443
      name: default
    contexts:
    - context:
        cluster: default
        namespace: default
        user: default
      name: default
    current-context: default
    users:
    - name: default
      user:
        tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token


# scheduler 인증서 정보
docker exec -i myk8s-control-plane cat /etc/kubernetes/scheduler.conf
docker exec -i myk8s-control-plane cat /etc/kubernetes/scheduler.conf | grep client-certificate-data | cut -d ':' -f 2 | base64 -d | openssl x509 -text -noout
docker exec -i myk8s-control-plane cat /etc/kubernetes/scheduler.conf | grep client-certificate-data | cut -d ':' -f 2 | tr -d ' ' | base64 -d | openssl x509 -text -noout
        Issuer: CN = kubernetes
        Validity
            Not Before: Jan  3 02:46:18 2026 GMT
            Not After : Jan  3 02:51:18 2027 GMT
        Subject: CN = system:kube-scheduler
        ...
            X509v3 Extended Key Usage:
                TLS Web Client Authentication

kubectl get pod -n kube-system -l component=kube-scheduler
kubectl describe pod -n kube-system -l component=kube-scheduler
kubectl rolesum -k User system:kube-scheduler
kubectl rbac-tool lookup system:kube-scheduler


# kcm 인증서 정보
docker exec -i myk8s-control-plane cat /etc/kubernetes/controller-manager.conf
docker exec -i myk8s-control-plane cat /etc/kubernetes/controller-manager.conf | grep client-certificate-data | cut -d ':' -f 2 | base64 -d | openssl x509 -text -noout
docker exec -i myk8s-control-plane cat /etc/kubernetes/controller-manager.conf | grep client-certificate-data | cut -d ':' -f 2 | tr -d ' ' | base64 -d | openssl x509 -text -noout
        Subject: CN=system:kube-controller-manager

kubectl rolesum -k User system:kube-controller-manager
kubectl rbac-tool lookup system:kube-controller-manager


# api-server 인증서 정보 : IP Address:10.96.0.1, IP Address:192.168.97.3, IP Address:127.0.0.1
docker exec -i myk8s-control-plane cat /etc/kubernetes/pki/apiserver.crt | openssl x509 -text -noout
        Issuer: CN=kubernetes
        Validity
            Not Before: Jan  3 03:31:30 2026 GMT
            Not After : Jan  3 03:36:30 2027 GMT
        Subject: CN=kube-apiserver
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier:
                3D:E2:06:BE:D3:96:A8:C6:46:48:DC:91:47:DB:54:0D:5B:12:91:01
            X509v3 Subject Alternative Name:
                DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:localhost, DNS:myk8s-control-plane, IP Address:10.96.0.1, IP Address:192.168.97.3, IP Address:127.0.0.1

kubectl get svc,ep
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    &amp;lt;none&amp;gt;        443/TCP   74m

NAME                   ENDPOINTS           AGE
endpoints/kubernetes   192.168.97.3:6443   74m

# apiserver 파드 args 정보
kubectl describe pod -n kube-system kube-apiserver-myk8s-control-plane
...
    Command:
      kube-apiserver
      --advertise-address=192.168.97.3                      # 클러스터 내 다른 컴포넌트가 접근할 API 서버 주소
      --allow-privileged=true                               # privileged: true 파드 허용, CNI, kube-proxy, CSI, Cilium 같은 시스템 파드 필수
      --authorization-mode=Node,RBAC                        # [인가] Node Authorizer(system:node:* , kubelet 전용 권한 검증) -&amp;gt; RBAC (사용자 / 그룹 기반 권한)
      --client-ca-file=/etc/kubernetes/pki/ca.crt           # [인증] X.509 클라이언트 인증 : kubectl, controller, scheduler 인증서 모두 여기로 검증
      --enable-admission-plugins=NodeRestriction            # Admission Controller : kubelet이 자기 노드/파드만 조작 가능하게 제한, Node Authorizer와 함께 작동
      --enable-bootstrap-token-auth=true                    # [인증] Bootstrap 토큰 인증 : 신규 노드 join 시 bootstrap.kubernetes.io/token 사용
      --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt                 # etcd 연동 : kube-apiserver &amp;rarr; etcd mTLS , apiserver는 etcd의 클라이언트
      --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt # 상동
      --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key  # 상동
      --etcd-servers=https://127.0.0.1:2379                         # etcd 연동 : etcd 엔드포인트
      --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt  # kubelet 통신 : API 서버 &amp;rarr; kubelet 호출 시 사용
      --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key          # kubelet 통신 : 로그, exec, metrics, stats 접근
      --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname               # kubelet 통신 : 노드 접근 주소 우선순위
      --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt   # Aggregated API / Front Proxy : API Aggregation Layer 인증
      --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key    # 상동
      --requestheader-allowed-names=front-proxy-client                      # 프록시 인증서 CN 제한
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt # 프록시가 전달한 사용자 정보 신뢰
      --requestheader-extra-headers-prefix=X-Remote-Extra-                  # metrics-server, aggregated apiserver 사용 시 필수
      --requestheader-group-headers=X-Remote-Group                          # 상동
      --requestheader-username-headers=X-Remote-User                        # 상동
      --runtime-config=                                     # 비어 있음 &amp;rarr; 기본 API 활성화
      --secure-port=6443                                    # HTTPS API 포트 , HTTP(8080)는 기본 비활성화
      --service-account-issuer=https://kubernetes.default.svc.cluster.local  # ServiceAccount 토큰 관리 : SA 토큰 issuer (OIDC 호환), BoundServiceAccountTokenVolume 필수
      --service-account-key-file=/etc/kubernetes/pki/sa.pub                  # 상동 : 토큰 검증용 공개키
      --service-account-signing-key-file=/etc/kubernetes/pki/sa.key          # 상동 : 토큰 서명용 개인키
      --service-cluster-ip-range=10.96.0.0/16
      --tls-cert-file=/etc/kubernetes/pki/apiserver.crt         # kube-apiserver 서버 인증서(tls) : 클라이언트(kubectl, kubelet 등)가 이 인증서 검증
      --tls-private-key-file=/etc/kubernetes/pki/apiserver.key  # 상동

# (참고) api -&amp;gt; kubelet 호출 시 : api client crt 정보
docker exec -it myk8s-control-plane ls -l /etc/kubernetes/pki | grep apiserver
-rw-r--r-- 1 root root 1123 Jan  4 01:20 apiserver-etcd-client.crt
-rw------- 1 root root 1679 Jan  4 01:20 apiserver-etcd-client.key
-rw-r--r-- 1 root root 1176 Jan  4 01:20 apiserver-kubelet-client.crt
-rw------- 1 root root 1679 Jan  4 01:20 apiserver-kubelet-client.key
-rw-r--r-- 1 root root 1326 Jan  4 01:20 apiserver.crt
-rw------- 1 root root 1679 Jan  4 01:20 apiserver.key

docker exec -i myk8s-control-plane cat /etc/kubernetes/pki/apiserver-kubelet-client.crt | openssl x509 -text -noout
...

# ServiceAccount 토큰 검증용 공개키/개인키 확인 : api 파드 service-account-key-file, service-account-signing-key-file 에서 사용
docker exec -it myk8s-control-plane ls -l /etc/kubernetes/pki/sa.{key,pub}
-rw------- 1 root root 1679 Jan  4 01:20 /etc/kubernetes/pki/sa.key
-rw------- 1 root root  451 Jan  4 01:20 /etc/kubernetes/pki/sa.pub&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;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞ &lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Distribute&lt;/span&gt; the Client and Server Certificates &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767850155924&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Copy the appropriate certificates and private keys to the node-0 and node-1 machines
for host in node-0 node-1; do
  ssh root@${host} mkdir /var/lib/kubelet/

  scp ca.crt root@${host}:/var/lib/kubelet/

  scp ${host}.crt \
    root@${host}:/var/lib/kubelet/kubelet.crt

  scp ${host}.key \
    root@${host}:/var/lib/kubelet/kubelet.key
done

# 확인
ssh node-0 ls -l /var/lib/kubelet
ssh node-1 ls -l /var/lib/kubelet

ssh node-1 ls -l /var/lib/kubelet
total 60
-rw-r--r-- 1 root root  1899 Jan  8 16:42 ca.crt
drwx------ 2 root root  4096 Jan  6 16:34 checkpoints
-rw------- 1 root root    62 Jan  6 16:34 cpu_manager_state
drwxr-xr-x 2 root root  4096 Jan  6 16:34 device-plugins
-rw------- 1 root root 10161 Jan  6 16:16 kubeconfig
-rw-r--r-- 1 root root   610 Jan  6 16:31 kubelet-config.yaml
-rw-r--r-- 1 root root  2147 Jan  8 16:42 kubelet.crt
-rw------- 1 root root  3272 Jan  8 16:42 kubelet.key
-rw------- 1 root root    61 Jan  6 16:34 memory_manager_state
drwxr-x--- 2 root root  4096 Jan  6 16:34 plugins
drwxr-x--- 2 root root  4096 Jan  6 16:34 plugins_registry
drwxr-x--- 2 root root  4096 Jan  6 16:34 pod-resources
drwxr-x--- 3 root root  4096 Jan  6 16:44 pods
total 60
-rw-r--r-- 1 root root  1899 Jan  8 16:42 ca.crt
drwx------ 2 root root  4096 Jan  6 16:36 checkpoints
-rw------- 1 root root    62 Jan  6 16:36 cpu_manager_state
drwxr-xr-x 2 root root  4096 Jan  6 16:36 device-plugins
-rw------- 1 root root 10161 Jan  6 16:16 kubeconfig
-rw-r--r-- 1 root root   610 Jan  6 16:31 kubelet-config.yaml
-rw-r--r-- 1 root root  2147 Jan  8 16:42 kubelet.crt
-rw------- 1 root root  3268 Jan  8 16:42 kubelet.key
-rw------- 1 root root    61 Jan  6 16:36 memory_manager_state
drwxr-x--- 2 root root  4096 Jan  6 16:36 plugins
drwxr-x--- 2 root root  4096 Jan  6 16:36 plugins_registry
drwxr-x--- 2 root root  4096 Jan  6 16:36 pod-resources
drwxr-x--- 3 root root  4096 Jan  6 16:44 pods




# Copy the appropriate certificates and private keys to the server machine
scp \
  ca.key ca.crt \
  kube-api-server.key kube-api-server.crt \
  service-accounts.key service-accounts.crt \
  root@server:~/

ca.key                                                                                                                                                                      100% 3272     2.4MB/s   00:00
ca.crt                                                                                                                                                                      100% 1899     1.6MB/s   00:00
kube-api-server.key                                                                                                                                                         100% 3268     2.9MB/s   00:00
kube-api-server.crt                                                                                                                                                         100% 2354     2.0MB/s   00:00
service-accounts.key                                                                                                                                                        100% 3272     2.9MB/s   00:00
service-accounts.crt                                                                                                                                                        100% 2004     2.1MB/s   00:00


# 확인
ssh server ls -l /root

-rw------- 1 root root 3272 Jan  6 15:31 admin.key
-rw------- 1 root root 9953 Jan  6 16:16 admin.kubeconfig
-rw-r--r-- 1 root root 1899 Jan  8 16:42 ca.crt
-rw------- 1 root root 3272 Jan  8 16:42 ca.key
-rw-r--r-- 1 root root 2354 Jan  8 16:42 kube-api-server.crt
-rw------- 1 root root 3268 Jan  8 16:42 kube-api-server.key
-rw-r--r-- 1 root root  727 Jan  6 16:20 kube-apiserver-to-kubelet.yaml
-rw-r--r-- 1 root root 2004 Jan  8 16:42 service-accounts.crt
-rw------- 1 root root 3272 Jan  8 16:42 service-accounts.key&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; ☞ &lt;span data-token-index=&quot;0&quot;&gt;Generating Kubernetes Configuration Files for Authentication&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;목표 : API Server와 통신을 위한 Client 인증 설정 파일 작성 - Docs&lt;br /&gt;Kubernetes client configuration files&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The &lt;b&gt;kubelet&lt;/b&gt; Kubernetes Configuration File&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;When generating kubeconfig files for Kubelets the client certificate matching the Kubelet's node name must be used.&lt;/li&gt;
&lt;li&gt;This will ensure &lt;b&gt;Kubelet&lt;/b&gt;s are properly &lt;b&gt;authorized&lt;/b&gt; by the Kubernetes&amp;nbsp;&lt;a href=&quot;https://v1-32.docs.kubernetes.io/docs/reference/access-authn-authz/node/&quot;&gt;Node Authorizer&lt;/a&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Node Authorization :&lt;/b&gt; Kubelet이 수행하는 API 요청에 대한 권한을 특별히 부여하는 특수 목적의 권한 부여 모드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767850274130&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# apiserver 파드 args 정보
kubectl describe pod -n kube-system kube-apiserver-myk8s-control-plane
    Command:
      kube-apiserver
      --authorization-mode=Node,RBAC  


# Generate a kubeconfig file for the node-0 and node-1 worker nodes

# config set-cluster
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://server.kubernetes.local:6443 \
  --kubeconfig=node-0.kubeconfig &amp;amp;&amp;amp; ls -l node-0.kubeconfig &amp;amp;&amp;amp; cat node-0.kubeconfig

kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://server.kubernetes.local:6443 \
  --kubeconfig=node-1.kubeconfig &amp;amp;&amp;amp; ls -l node-1.kubeconfig &amp;amp;&amp;amp; cat node-1.kubeconfig

# config set-credentials
kubectl config set-credentials system:node:node-0 \
  --client-certificate=node-0.crt \
  --client-key=node-0.key \
  --embed-certs=true \
  --kubeconfig=node-0.kubeconfig &amp;amp;&amp;amp; cat node-0.kubeconfig

kubectl config set-credentials system:node:node-1 \
  --client-certificate=node-1.crt \
  --client-key=node-1.key \
  --embed-certs=true \
  --kubeconfig=node-1.kubeconfig &amp;amp;&amp;amp; cat node-1.kubeconfig
  
# set-context : default 추가
kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:node:node-0 \
  --kubeconfig=node-0.kubeconfig &amp;amp;&amp;amp; cat node-0.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:node:node-1 \
  --kubeconfig=node-1.kubeconfig &amp;amp;&amp;amp; cat node-1.kubeconfig
...
contexts:
- context:
    cluster: kubernetes-the-hard-way
    user: system:node:node-0
  name: default

# use-context : current-context 에 default 추가
kubectl config use-context default \
  --kubeconfig=node-0.kubeconfig

kubectl config use-context default \
  --kubeconfig=node-1.kubeconfig
...
current-context: default

#
ls -l *.kubeconfig
-rw------- 1 root root 10157 Jan  3 14:55 node-0.kubeconfig
-rw------- 1 root root 10068 Jan  3 14:50 node-1.kubeconfig&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;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;실습 검증&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ls&amp;nbsp;-l&amp;nbsp;*.kubeconfig&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8mRw4/dJMcab3XQtk/XnH2orKmA0bIDi6j1ueBhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8mRw4/dJMcab3XQtk/XnH2orKmA0bIDi6j1ueBhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8mRw4/dJMcab3XQtk/XnH2orKmA0bIDi6j1ueBhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8mRw4%2FdJMcab3XQtk%2FXnH2orKmA0bIDi6j1ueBhK%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;902&quot; height=&quot;48&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;48&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;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;p data-ke-size=&quot;size18&quot;&gt;(참고) kind system:node 관련 정보&lt;/p&gt;
&lt;pre id=&quot;code_1767850305219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 
kubectl describe clusterroles system:node
kubectl get clusterrolebindings.rbac.authorization.k8s.io system:node

# 일반적인 ClusterRoleBinding 다르게 subjects 필드가 없음
# system:node 는 특수한 bootstrap RBAC
# 노드 인증서의 O = system:nodes 가 이 ClusterRoleBinding 과 매칭됨 , 참고로 CN = system:node:&amp;lt;node명&amp;gt;
kubectl get clusterrolebindings.rbac.authorization.k8s.io system:node -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: &quot;true&quot;
  creationTimestamp: &quot;2026-01-03T03:36:37Z&quot;
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:node
  resourceVersion: &quot;146&quot;
  uid: a451c617-1577-499a-9b04-59af6789e3ee
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node

#
docker exec -i myk8s-worker cat /etc/kubernetes/kubelet.conf
docker exec -i myk8s-worker cat /var/lib/kubelet/pki/kubelet-client-current.pem | openssl x509 -text -noout
        Subject: O=system:nodes, CN=system:node:myk8s-worker&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞ The &lt;span data-token-index=&quot;1&quot;&gt;kube-proxy&lt;/span&gt; Kubernetes Configuration File &lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767850353682&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Generate a kubeconfig file for the kube-proxy service
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://server.kubernetes.local:6443 \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-credentials system:kube-proxy \
  --client-certificate=kube-proxy.crt \
  --client-key=kube-proxy.key \
  --embed-certs=true \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config use-context default \
  --kubeconfig=kube-proxy.kubeconfig

# 확인
cat kube-proxy.kubeconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1647&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LmT2S/dJMcaaYirvI/oNGNB7klFHSgTBNmFLgT00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LmT2S/dJMcaaYirvI/oNGNB7klFHSgTBNmFLgT00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LmT2S/dJMcaaYirvI/oNGNB7klFHSgTBNmFLgT00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLmT2S%2FdJMcaaYirvI%2FoNGNB7klFHSgTBNmFLgT00%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;1647&quot; height=&quot;720&quot; data-origin-width=&quot;1647&quot; data-origin-height=&quot;720&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;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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞ &lt;/b&gt;The &lt;span data-token-index=&quot;1&quot;&gt;kube-controller-manager&lt;/span&gt; Kubernetes Configuration File&lt;/h3&gt;
&lt;pre id=&quot;code_1767850387532&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Generate a kubeconfig file for the kube-controller-manager service
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://server.kubernetes.local:6443 \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-credentials system:kube-controller-manager \
  --client-certificate=kube-controller-manager.crt \
  --client-key=kube-controller-manager.key \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config use-context default \
  --kubeconfig=kube-controller-manager.kubeconfig

# 확인
cat kube-controller-manager.kubeconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x6nbg/dJMcaaKKOHM/MxtV5JwXKZrzn1GvqVe4I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x6nbg/dJMcaaKKOHM/MxtV5JwXKZrzn1GvqVe4I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x6nbg/dJMcaaKKOHM/MxtV5JwXKZrzn1GvqVe4I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx6nbg%2FdJMcaaKKOHM%2FMxtV5JwXKZrzn1GvqVe4I0%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;1641&quot; height=&quot;732&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; ☞ The &lt;span data-token-index=&quot;1&quot;&gt;admin&lt;/span&gt; Kubernetes Configuration File &lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767850538342&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Generate a kubeconfig file for the admin user
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=admin.kubeconfig

kubectl config set-credentials admin \
  --client-certificate=admin.crt \
  --client-key=admin.key \
  --embed-certs=true \
  --kubeconfig=admin.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=admin \
  --kubeconfig=admin.kubeconfig

kubectl config use-context default \
  --kubeconfig=admin.kubeconfig

# 확인
cat admin.kubeconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;785&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzMDFU/dJMcadgm1jf/agUJtvkMzGdK54hSCGJSGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzMDFU/dJMcadgm1jf/agUJtvkMzGdK54hSCGJSGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzMDFU/dJMcadgm1jf/agUJtvkMzGdK54hSCGJSGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzMDFU%2FdJMcadgm1jf%2FagUJtvkMzGdK54hSCGJSGK%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;1660&quot; height=&quot;785&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;785&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞ &lt;span data-token-index=&quot;0&quot;&gt;Distribute&lt;/span&gt; the Kubernetes Configuration Files &lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767850606126&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
ls -l *.kubeconfig
-rw------- 1 root root  9949 Jan  3 15:00 admin.kubeconfig
-rw------- 1 root root 10305 Jan  3 14:59 kube-controller-manager.kubeconfig
-rw------- 1 root root 10187 Jan  3 14:58 kube-proxy.kubeconfig
-rw------- 1 root root 10231 Jan  3 14:59 kube-scheduler.kubeconfig
-rw------- 1 root root 10157 Jan  3 14:55 node-0.kubeconfig
-rw------- 1 root root 10068 Jan  3 14:50 node-1.kubeconfig

# Copy the kubelet and kube-proxy kubeconfig files to the node-0 and node-1 machines
for host in node-0 node-1; do
  ssh root@${host} &quot;mkdir -p /var/lib/{kube-proxy,kubelet}&quot;

  scp kube-proxy.kubeconfig \
    root@${host}:/var/lib/kube-proxy/kubeconfig \

  scp ${host}.kubeconfig \
    root@${host}:/var/lib/kubelet/kubeconfig
done

# 확인
ssh node-0 ls -l /var/lib/*/kubeconfig
ssh node-1 ls -l /var/lib/*/kubeconfig


# Copy the kube-controller-manager and kube-scheduler kubeconfig files to the server machine
scp admin.kubeconfig \
  kube-controller-manager.kubeconfig \
  kube-scheduler.kubeconfig \
  root@server:~/

# 확인
ssh server ls -l /root/*.kubeconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/msvY0/dJMcai22eJu/ZX7HSDq8ofNZ6OwnPkPRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/msvY0/dJMcai22eJu/ZX7HSDq8ofNZ6OwnPkPRK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/msvY0/dJMcai22eJu/ZX7HSDq8ofNZ6OwnPkPRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmsvY0%2FdJMcai22eJu%2FZX7HSDq8ofNZ6OwnPkPRK1%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;876&quot; height=&quot;107&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;107&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;1635&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLUPGw/dJMcadUXMbq/z5pu9lWaoxMfahYPeOfkkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLUPGw/dJMcadUXMbq/z5pu9lWaoxMfahYPeOfkkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLUPGw/dJMcadUXMbq/z5pu9lWaoxMfahYPeOfkkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLUPGw%2FdJMcadUXMbq%2Fz5pu9lWaoxMfahYPeOfkkK%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;1635&quot; height=&quot;331&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞&lt;span&gt; &lt;span data-token-index=&quot;0&quot;&gt;Generating the Data Encryption Config and Key&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 : ETCD 에 Secret 저장 시, 암호화 저장 설정 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/06-data-encryption-keys.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt; , &lt;a style=&quot;color: #000000;&quot; href=&quot;https://v1-32.docs.kubernetes.io/docs/tasks/administer-cluster/encrypt-data/&quot; data-token-index=&quot;3&quot;&gt;&lt;span&gt;Encrypting Confidential Data at Rest&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767850698361&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# The Encryption Key

# Generate an encryption key
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
echo $ENCRYPTION_KEY
JMnUP1PUUORZE9iadPdzYifnvPVIniSzOW6NUoMofVc=


# The Encryption Config File

# Create the encryption-config.yaml encryption config file
# (참고) 실제 etcd 값에 기록되는 헤더 : k8s:enc:aescbc:v1:key1:&amp;lt;ciphertext&amp;gt;
cat configs/encryption-config.yaml
kind: EncryptionConfiguration           # kube-apiserver가 etcd에 저장할 리소스를 어떻게 암호화할지 정의
apiVersion: apiserver.config.k8s.io/v1  # --encryption-provider-config 플래그로 참조
resources:
  - resources:
      - secrets                         # 암호화 대상 Kubernetes 리소스 : 여기서는 Secret 리소스만 암호화
    providers:                          # 지원 providers(위 부터 적용됨) : identity, aescbc, aesgcm, kms v2, secretbox
      - aescbc:                         # etcd에 저장될 Secret을 AES-CBC 방식으로 암호화
          keys:
            - name: key1                # 키 식별자 (etcd 데이터에 기록됨)
              secret: ${ENCRYPTION_KEY}
      - identity: {}                    # 암호화하지 않음 (Plaintext), 주로 하위 호환성 / 점진적 암호화 목적
      
envsubst &amp;lt; configs/encryption-config.yaml &amp;gt; encryption-config.yaml
cat encryption-config.yaml

# Copy the encryption-config.yaml encryption config file to each controller instance:
scp encryption-config.yaml root@server:~/
ssh server ls -l /root/encryption-config.yaml&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞&lt;span&gt;&lt;span&gt; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Bootstrapping the etcd Cluster&lt;/span&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 : server 노드에 etcd 서비스 기동&lt;/p&gt;
&lt;pre id=&quot;code_1767851035625&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Prerequisites

# hostname 변경 : controller -&amp;gt; server
# http 평문 통신!
# Each etcd member must have a unique name within an etcd cluster. 
# Set the etcd name to match the hostname of the current compute instance:
cat units/etcd.service | grep controller

ETCD_NAME=server
cat &amp;gt; units/etcd.service &amp;lt;&amp;lt;EOF
[Unit]
Description=etcd
Documentation=https://github.com/etcd-io/etcd

[Service]
Type=notify
ExecStart=/usr/local/bin/etcd \\
  --name ${ETCD_NAME} \\
  --initial-advertise-peer-urls http://127.0.0.1:2380 \\
  --listen-peer-urls http://127.0.0.1:2380 \\
  --listen-client-urls http://127.0.0.1:2379 \\
  --advertise-client-urls http://127.0.0.1:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster ${ETCD_NAME}=http://127.0.0.1:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
cat units/etcd.service | grep server

# Copy etcd binaries and systemd unit files to the server machine
scp \
  downloads/controller/etcd \
  downloads/client/etcdctl \
  units/etcd.service \
  root@server:~/


# 아래는 server 가상머신 접속 후 명령 실행
# The commands in this lab must be run on the server machine. Login to the server machine using the ssh command. Example:
ssh root@server
-------------------------------------------------------------------
# Bootstrapping an etcd Cluster

# Install the etcd Binaries
# Extract and install the etcd server and the etcdctl command line utility
pwd
mv etcd etcdctl /usr/local/bin/

# Configure the etcd Server
mkdir -p /etc/etcd /var/lib/etcd
chmod 700 /var/lib/etcd
cp ca.crt kube-api-server.key kube-api-server.crt /etc/etcd/

# Create the etcd.service systemd unit file:
mv etcd.service /etc/systemd/system/
tree /etc/systemd/system/

# Start the etcd Server
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd

# 확인
systemctl status etcd --no-pager
ss -tnlp | grep etcd
LISTEN 0      4096       127.0.0.1:2380      0.0.0.0:*    users:((&quot;etcd&quot;,pid=2829,fd=3))                          
LISTEN 0      4096       127.0.0.1:2379      0.0.0.0:*    users:((&quot;etcd&quot;,pid=2829,fd=6)) 

# List the etcd cluster members
etcdctl member list
702b0a34e2cfd39, started, server, http://127.0.0.1:2380, http://127.0.0.1:2379, false

etcdctl member list -w table
etcdctl endpoint status -w table

exit
-------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qucS/dJMcagqE7ME/ob7lh9fLRQ5CbxfZLwtYC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qucS/dJMcagqE7ME/ob7lh9fLRQ5CbxfZLwtYC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qucS/dJMcagqE7ME/ob7lh9fLRQ5CbxfZLwtYC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qucS%2FdJMcagqE7ME%2Fob7lh9fLRQ5CbxfZLwtYC1%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;1107&quot; height=&quot;57&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;57&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1665&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blPqBU/dJMcaiPvIEK/BVGYfqRKEjKg1HhYKKVYm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blPqBU/dJMcaiPvIEK/BVGYfqRKEjKg1HhYKKVYm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blPqBU/dJMcaiPvIEK/BVGYfqRKEjKg1HhYKKVYm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblPqBU%2FdJMcaiPvIEK%2FBVGYfqRKEjKg1HhYKKVYm1%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;1665&quot; height=&quot;780&quot; data-origin-width=&quot;1665&quot; data-origin-height=&quot;780&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; (참고) kind k8s etcd 관련 인증서 정보 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767851614710&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# https 암호 통신 설정 정보 확인
kubectl describe pod -n kube-system etcd-myk8s-control-plane
...
    Command:
      etcd
      --advertise-client-urls=https://192.168.97.3:2379
      --cert-file=/etc/kubernetes/pki/etcd/server.crt
      --client-cert-auth=true
      --data-dir=/var/lib/etcd
      --experimental-initial-corrupt-check=true
      --experimental-watch-progress-notify-interval=5s
      --initial-advertise-peer-urls=https://192.168.97.3:2380
      --initial-cluster=myk8s-control-plane=https://192.168.97.3:2380
      --key-file=/etc/kubernetes/pki/etcd/server.key
      --listen-client-urls=https://127.0.0.1:2379,https://192.168.97.3:2379
      --listen-metrics-urls=http://127.0.0.1:2381
      --listen-peer-urls=https://192.168.97.3:2380
      --name=myk8s-control-plane
      --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
      --peer-client-cert-auth=true
      --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
      --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
      --snapshot-count=10000
      --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

#
docker exec -it myk8s-control-plane tree /etc/kubernetes/pki/etcd
/etc/kubernetes/pki/etcd
|-- ca.crt
|-- ca.key
|-- healthcheck-client.crt
|-- healthcheck-client.key
|-- peer.crt
|-- peer.key
|-- server.crt
`-- server.key

# etcd 별도 CA 확인
docker exec -i myk8s-control-plane cat /etc/kubernetes/pki/etcd/ca.crt | openssl x509 -text -noout
        Issuer: CN=etcd-ca
        Validity
            Not Before: Jan  4 01:15:25 2026 GMT
            Not After : Jan  2 01:20:25 2036 GMT
        Subject: CN=etcd-ca
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment, Certificate Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier:
                8C:D1:86:C6:90:7E:1E:9F:16:2B:FE:7C:6F:3F:3F:1A:6A:5F:B9:AD
            X509v3 Subject Alternative Name:
                DNS:etcd-ca

# kubeadm 에 인증서 정보 확인
docker exec -i myk8s-control-plane kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
apiserver-etcd-client      Jan 04, 2027 01:20 UTC   364d            etcd-ca                 no
etcd-healthcheck-client    Jan 04, 2027 01:20 UTC   364d            etcd-ca                 no
etcd-peer                  Jan 04, 2027 01:20 UTC   364d            etcd-ca                 no
etcd-server                Jan 04, 2027 01:20 UTC   364d            etcd-ca                 no
...

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Jan 02, 2036 01:20 UTC   9y              no
etcd-ca                 Jan 02, 2036 01:20 UTC   9y              no
front-proxy-ca          Jan 02, 2036 01:20 UTC   9y              no

# apiserver(client) -&amp;gt; etcd(server) 호출 시 사용하는 클라이언트 인증서 정보 확인
docker exec -i myk8s-control-plane cat /etc/kubernetes/pki/apiserver-etcd-client.crt | openssl x509 -text -noout
        Issuer: CN=etcd-ca
        Validity
            Not Before: Jan  4 01:15:25 2026 GMT
            Not After : Jan  4 01:20:25 2027 GMT
        Subject: CN=kube-apiserver-etcd-client
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Bootstrapping the Kubernetes Control Plane&lt;/span&gt; &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 노드에 &amp;lsquo;api server, scheduler, kcm&amp;rsquo; 서비스 기동 - &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/08-bootstrapping-kubernetes-controllers.md&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;Docs&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;table id=&quot;2de50aec-5edf-80c3-883f-cc878aa06a79&quot; style=&quot;border-collapse: collapse; width: 39.7674%; height: 199px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;네트워크 대역 or IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-80da-9fd8-dd9ca05fe51b&quot;&gt;
&lt;td id=&quot;huHS&quot;&gt;&lt;b&gt;clusterCIDR&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;lb{B&quot;&gt;&lt;b&gt;10.200.0.0/16&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8033-9525-d2f316aa1178&quot;&gt;
&lt;td id=&quot;huHS&quot;&gt;&amp;rarr; node-0 PodCIDR&lt;/td&gt;
&lt;td id=&quot;lb{B&quot;&gt;10.200.0.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-80eb-9fd4-f31265decac1&quot;&gt;
&lt;td id=&quot;huHS&quot;&gt;&amp;rarr; node-1 PodCIDR&lt;/td&gt;
&lt;td id=&quot;lb{B&quot;&gt;10.200.1.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-8009-847c-fd32a489cffa&quot;&gt;
&lt;td id=&quot;huHS&quot;&gt;&lt;b&gt;ServiceCIDR&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;lb{B&quot;&gt;&lt;b&gt;10.32.0.0/24&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2de50aec-5edf-80d7-8b21-ed5a8f5d6fec&quot;&gt;
&lt;td id=&quot;huHS&quot;&gt;&amp;rarr; api clusterIP&lt;/td&gt;
&lt;td id=&quot;lb{B&quot;&gt;10.32.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;설정 파일 작성 후 server 에 전달 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767851749300&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Prerequisites

# kube-apiserver.service 수정 : service-cluster-ip-range 추가
# https://github.com/kelseyhightower/kubernetes-the-hard-way/issues/905
# service-cluster-ip 값은 ca.conf 에 설정한 [kube-api-server_alt_names] 항목의 Service IP 범위
cat ca.conf | grep '\[kube-api-server_alt_names' -A2
[kube-api-server_alt_names]
IP.0  = 127.0.0.1
IP.1  = 10.32.0.1

cat units/kube-apiserver.service
cat &amp;lt;&amp;lt; EOF &amp;gt; units/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --allow-privileged=true \\
  --apiserver-count=1 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.crt \\
  --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --etcd-servers=http://127.0.0.1:2379 \\
  --event-ttl=1h \\
  --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.crt \\
  --kubelet-client-certificate=/var/lib/kubernetes/kube-api-server.crt \\
  --kubelet-client-key=/var/lib/kubernetes/kube-api-server.key \\
  --runtime-config='api/all=true' \\
  --service-account-key-file=/var/lib/kubernetes/service-accounts.crt \\
  --service-account-signing-key-file=/var/lib/kubernetes/service-accounts.key \\
  --service-account-issuer=https://server.kubernetes.local:6443 \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kube-api-server.crt \\
  --tls-private-key-file=/var/lib/kubernetes/kube-api-server.key \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
cat units/kube-apiserver.service


# kube-apiserver가 kubelet(Node)에 접근할 수 있도록 허용하는 '시스템 내부용 RBAC' 설정
cat configs/kube-apiserver-to-kubelet.yaml ; echo
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: &quot;true&quot;  # Kubernetes가 업그레이드 시 자동 관리
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - &quot;&quot;                                               # Core API group (v1) : Node 관련 서브리소스는 core group에 속함
    resources:                                           # 아래 처럼, kubelet API 대부분을 포괄
      - nodes/proxy                                      ## apiserver &amp;rarr; kubelet 프록시 통신
      - nodes/stats                                      ## 노드/파드 리소스 통계 (cAdvisor)
      - nodes/log                                        ## metrics-server / top 명령
      - nodes/spec                                       ## kubectl logs
      - nodes/metrics                                    ## metrics-server / top 명령
    verbs:
      - &quot;*&quot;                                              # 대상은 &amp;ldquo;nodes 하위 리소스&amp;rdquo;로 한정 + 모든 동작 허용 (get, list, watch, create, proxy 등)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver                            # 누가 이 권한을 쓰는가? &amp;rarr; kube-apiserver 자신
  namespace: &quot;&quot;
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes                         # 사용자 kubernetes ,이 사용자는 kube-apiserver가 사용하는 클라이언트 인증서의 CN

# api-server : Subject CN 확인
openssl x509 -in kube-api-server.crt -text -noout
        Subject: CN = kubernetes,

# api -&amp;gt; kubelet 호출 시 Flow
kube-apiserver (client)
  |
  | (TLS client cert, CN=kubernetes)
  &amp;darr;
kubelet API Server 역할 (/stats, /log, /metrics)
  |
  &amp;darr;
RBAC 평가:
  User = kubernetes
  &amp;rarr; ClusterRoleBinding system:kube-apiserver 매칭
  &amp;rarr; ClusterRole system:kube-apiserver-to-kubelet 권한 부여


# kube-scheduler
cat units/kube-scheduler.service ; echo
cat configs/kube-scheduler.yaml ; echo


# kube-controller-manager : cluster-cidr 는 POD CIDR 포함하는 대역, service-cluster-ip-range 는 apiserver 설정 값 동일 설정.
cat units/kube-controller-manager.service ; echo
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
  --bind-address=0.0.0.0 \
  --cluster-cidr=10.200.0.0/16 \
  --cluster-name=kubernetes \
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.crt \
  --cluster-signing-key-file=/var/lib/kubernetes/ca.key \
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \
  --root-ca-file=/var/lib/kubernetes/ca.crt \
  --service-account-private-key-file=/var/lib/kubernetes/service-accounts.key \
  --service-cluster-ip-range=10.32.0.0/24 \
  --use-service-account-credentials=true \
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target


# Connect to the jumpbox and copy Kubernetes binaries and systemd unit files to the server machine
scp \
  downloads/controller/kube-apiserver \
  downloads/controller/kube-controller-manager \
  downloads/controller/kube-scheduler \
  downloads/client/kubectl \
  units/kube-apiserver.service \
  units/kube-controller-manager.service \
  units/kube-scheduler.service \
  configs/kube-scheduler.yaml \
  configs/kube-apiserver-to-kubelet.yaml \
  root@server:~/

# 확인
ssh server ls -l /root&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1633&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n8j4e/dJMcac9AOC6/hIy4SMKFmWUSQ8w7vzqkH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n8j4e/dJMcac9AOC6/hIy4SMKFmWUSQ8w7vzqkH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n8j4e/dJMcac9AOC6/hIy4SMKFmWUSQ8w7vzqkH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn8j4e%2FdJMcac9AOC6%2FhIy4SMKFmWUSQ8w7vzqkH1%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;1633&quot; height=&quot;637&quot; data-origin-width=&quot;1633&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;☞ &lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;Provision the Kubernetes Control Plane&lt;/span&gt; : kubectl 확인 &lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767851807012&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh root@server
---------------------------------------------------------------
# Create the Kubernetes configuration directory:
pwd
mkdir -p /etc/kubernetes/config


# Install the Kubernetes binaries:
mv kube-apiserver \
  kube-controller-manager \
  kube-scheduler kubectl \
  /usr/local/bin/
ls -l /usr/local/bin/kube-*


# Configure the Kubernetes API Server
mkdir -p /var/lib/kubernetes/
mv ca.crt ca.key \
  kube-api-server.key kube-api-server.crt \
  service-accounts.key service-accounts.crt \
  encryption-config.yaml \
  /var/lib/kubernetes/
ls -l /var/lib/kubernetes/

## Create the kube-apiserver.service systemd unit file:
mv kube-apiserver.service \
  /etc/systemd/system/kube-apiserver.service
tree /etc/systemd/system


# Configure the Kubernetes Controller Manager

## Move the kube-controller-manager kubeconfig into place:
mv kube-controller-manager.kubeconfig /var/lib/kubernetes/

## Create the kube-controller-manager.service systemd unit file:
mv kube-controller-manager.service /etc/systemd/system/


# Configure the Kubernetes Scheduler

## Move the kube-scheduler kubeconfig into place:
mv kube-scheduler.kubeconfig /var/lib/kubernetes/

## Create the kube-scheduler.yaml configuration file:
mv kube-scheduler.yaml /etc/kubernetes/config/

## Create the kube-scheduler.service systemd unit file:
mv kube-scheduler.service /etc/systemd/system/


# Start the Controller Services : Allow up to 10 seconds for the Kubernetes API Server to fully initialize.
systemctl daemon-reload
systemctl enable kube-apiserver kube-controller-manager kube-scheduler
systemctl start  kube-apiserver kube-controller-manager kube-scheduler

# 확인
ss -tlp | grep kube
LISTEN 0      4096               *:6443              *:*    users:((&quot;kube-apiserver&quot;,pid=3071,fd=3))                
LISTEN 0      4096               *:10257             *:*    users:((&quot;kube-controller&quot;,pid=3072,fd=3))               
LISTEN 0      4096               *:10259             *:*    users:((&quot;kube-scheduler&quot;,pid=3073,fd=3))  

systemctl is-active kube-apiserver
systemctl status kube-apiserver --no-pager
journalctl -u kube-apiserver --no-pager

systemctl status kube-scheduler --no-pager
systemctl status kube-controller-manager --no-pager

# Verify this using the kubectl command line tool:
kubectl cluster-info dump --kubeconfig admin.kubeconfig
kubectl cluster-info --kubeconfig admin.kubeconfig
Kubernetes control plane is running at https://127.0.0.1:6443

kubectl get node --kubeconfig admin.kubeconfig
kubectl get pod -A --kubeconfig admin.kubeconfig

kubectl get service,ep --kubeconfig admin.kubeconfig
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.32.0.1    &amp;lt;none&amp;gt;        443/TCP   110m

NAME                   ENDPOINTS        AGE
endpoints/kubernetes   10.0.2.15:6443   110m

# clusterroles 확인
kubectl get clusterroles --kubeconfig admin.kubeconfig
NAME                                                                   CREATED AT
admin                                                                  2026-01-03T08:42:54Z
cluster-admin                                                          2026-01-03T08:42:54Z
edit                                                                   2026-01-03T08:42:54Z
system:aggregate-to-admin                                              2026-01-03T08:42:54Z
system:aggregate-to-edit                                               2026-01-03T08:42:54Z
system:aggregate-to-view                                               2026-01-03T08:42:54Z
system:auth-delegator                                                  2026-01-03T08:42:54Z
system:basic-user                                                      2026-01-03T08:42:54Z
system:certificates.k8s.io:certificatesigningrequests:nodeclient       2026-01-03T08:42:54Z
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient   2026-01-03T08:42:54Z
system:certificates.k8s.io:kube-apiserver-client-approver              2026-01-03T08:42:54Z
system:certificates.k8s.io:kube-apiserver-client-kubelet-approver      2026-01-03T08:42:54Z
system:certificates.k8s.io:kubelet-serving-approver                    2026-01-03T08:42:54Z
system:certificates.k8s.io:legacy-unknown-approver                     2026-01-03T08:42:54Z
system:controller:attachdetach-controller                              2026-01-03T08:42:54Z
system:controller:certificate-controller                               2026-01-03T08:42:54Z
system:controller:clusterrole-aggregation-controller                   2026-01-03T08:42:54Z
system:controller:cronjob-controller                                   2026-01-03T08:42:54Z
system:controller:daemon-set-controller                                2026-01-03T08:42:54Z
system:controller:deployment-controller                                2026-01-03T08:42:54Z
system:controller:disruption-controller                                2026-01-03T08:42:54Z
system:controller:endpoint-controller                                  2026-01-03T08:42:54Z
system:controller:endpointslice-controller                             2026-01-03T08:42:54Z
system:controller:endpointslicemirroring-controller                    2026-01-03T08:42:54Z
system:controller:ephemeral-volume-controller                          2026-01-03T08:42:54Z
system:controller:expand-controller                                    2026-01-03T08:42:54Z
system:controller:generic-garbage-collector                            2026-01-03T08:42:54Z
system:controller:horizontal-pod-autoscaler                            2026-01-03T08:42:54Z
system:controller:job-controller                                       2026-01-03T08:42:54Z
system:controller:legacy-service-account-token-cleaner                 2026-01-03T08:42:54Z
system:controller:namespace-controller                                 2026-01-03T08:42:54Z
system:controller:node-controller                                      2026-01-03T08:42:54Z
system:controller:persistent-volume-binder                             2026-01-03T08:42:54Z
system:controller:pod-garbage-collector                                2026-01-03T08:42:54Z
system:controller:pv-protection-controller                             2026-01-03T08:42:54Z
system:controller:pvc-protection-controller                            2026-01-03T08:42:54Z
system:controller:replicaset-controller                                2026-01-03T08:42:54Z
system:controller:replication-controller                               2026-01-03T08:42:54Z
system:controller:resourcequota-controller                             2026-01-03T08:42:54Z
system:controller:root-ca-cert-publisher                               2026-01-03T08:42:54Z
system:controller:route-controller                                     2026-01-03T08:42:54Z
system:controller:service-account-controller                           2026-01-03T08:42:54Z
system:controller:service-controller                                   2026-01-03T08:42:54Z
system:controller:statefulset-controller                               2026-01-03T08:42:54Z
system:controller:ttl-after-finished-controller                        2026-01-03T08:42:54Z
system:controller:ttl-controller                                       2026-01-03T08:42:54Z
system:controller:validatingadmissionpolicy-status-controller          2026-01-03T08:42:54Z
system:discovery                                                       2026-01-03T08:42:54Z
system:heapster                                                        2026-01-03T08:42:54Z
system:kube-aggregator                                                 2026-01-03T08:42:54Z
system:kube-controller-manager                                         2026-01-03T08:42:54Z
system:kube-dns                                                        2026-01-03T08:42:54Z
system:kube-scheduler                                                  2026-01-03T08:42:54Z
system:kubelet-api-admin                                               2026-01-03T08:42:54Z
system:monitoring                                                      2026-01-03T08:42:54Z
system:node                                                            2026-01-03T08:42:54Z
system:node-bootstrapper                                               2026-01-03T08:42:54Z
system:node-problem-detector                                           2026-01-03T08:42:54Z
system:node-proxier                                                    2026-01-03T08:42:54Z
system:persistent-volume-provisioner                                   2026-01-03T08:42:54Z
system:public-info-viewer                                              2026-01-03T08:42:54Z
system:service-account-issuer-discovery                                2026-01-03T08:42:54Z
system:volume-scheduler                                                2026-01-03T08:42:54Z
view                                                                   2026-01-03T08:42:54Z

kubectl describe clusterroles system:kube-scheduler --kubeconfig admin.kubeconfig


# kube-scheduler subject 확인
kubectl get clusterrolebindings --kubeconfig admin.kubeconfig
kubectl describe clusterrolebindings system:kube-scheduler --kubeconfig admin.kubeconfig
Role:
  Kind:  ClusterRole
  Name:  system:kube-scheduler
Subjects:
  Kind  Name                   Namespace
  ----  ----                   ---------
  User  system:kube-scheduler 

---------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1645&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rRxbq/dJMcaaRwGoU/XzNseRM50s4M56s2d1jOx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rRxbq/dJMcaaRwGoU/XzNseRM50s4M56s2d1jOx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rRxbq/dJMcaaRwGoU/XzNseRM50s4M56s2d1jOx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrRxbq%2FdJMcaaRwGoU%2FXzNseRM50s4M56s2d1jOx0%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;1645&quot; height=&quot;698&quot; data-origin-width=&quot;1645&quot; data-origin-height=&quot;698&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;702&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXCTaL/dJMcafeeImH/8wgH3KVsDAChOyeHkY3GXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXCTaL/dJMcafeeImH/8wgH3KVsDAChOyeHkY3GXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXCTaL/dJMcafeeImH/8wgH3KVsDAChOyeHkY3GXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXCTaL%2FdJMcafeeImH%2F8wgH3KVsDAChOyeHkY3GXK%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;702&quot; height=&quot;122&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;122&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;752&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnv0Ci/dJMcabCS5tD/MZuuPf3IWgodlkypBDjjkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnv0Ci/dJMcabCS5tD/MZuuPf3IWgodlkypBDjjkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnv0Ci/dJMcabCS5tD/MZuuPf3IWgodlkypBDjjkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcnv0Ci%2FdJMcabCS5tD%2FMZuuPf3IWgodlkypBDjjkk%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;752&quot; height=&quot;130&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;130&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DYfnt/dJMcabv76f6/vGJcM0zB7s8wbkWiDma9Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DYfnt/dJMcabv76f6/vGJcM0zB7s8wbkWiDma9Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DYfnt/dJMcabv76f6/vGJcM0zB7s8wbkWiDma9Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDYfnt%2FdJMcabv76f6%2FvGJcM0zB7s8wbkWiDma9Ak%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;1408&quot; height=&quot;847&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;847&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;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;span data-token-index=&quot;0&quot;&gt;▶ RBAC for Kubelet Authorization&lt;/span&gt; &lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 섹션에서는 Kubernetes API 서버가 각 작업자 노드에서 Kubelet API에 액세스할 수 있도록 RBAC 권한을 구성합니다. Kubelet API에 대한 액세스 권한은 메트릭, 로그를 검색하고 포드에서 명령을 실행하는 데 필요합니다. 이 튜토리얼에서는 Kubelet --authorization-mode 플래그를 Webhook으로 설정합니다. Webhook 모드에서는 SubjectAccessReview API를 사용하여 권한을 결정합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767859644903&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh root@server # 이미 server 에 ssh 접속 상태
---------------------------------------------------------------
# api -&amp;gt; kubelet 접속을 위한 RBAC 설정
# Create the system:kube-apiserver-to-kubelet ClusterRole with permissions to access the Kubelet API and perform most common tasks associated with managing pods:
cat kube-apiserver-to-kubelet.yaml
kubectl apply -f kube-apiserver-to-kubelet.yaml --kubeconfig admin.kubeconfig
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created

# 확인
kubectl get clusterroles system:kube-apiserver-to-kubelet --kubeconfig admin.kubeconfig
kubectl get clusterrolebindings system:kube-apiserver --kubeconfig admin.kubeconfig

---------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce2Vmm/dJMcaiotyii/UtEW3KltXWRg2st177RQXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce2Vmm/dJMcaiotyii/UtEW3KltXWRg2st177RQXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce2Vmm/dJMcaiotyii/UtEW3KltXWRg2st177RQXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce2Vmm%2FdJMcaiotyii%2FUtEW3KltXWRg2st177RQXK%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;887&quot; height=&quot;542&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;542&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;905&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r8KfT/dJMcagjTMy6/qDo65JOnrm01olobfOXTEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r8KfT/dJMcagjTMy6/qDo65JOnrm01olobfOXTEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r8KfT/dJMcagjTMy6/qDo65JOnrm01olobfOXTEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr8KfT%2FdJMcagjTMy6%2FqDo65JOnrm01olobfOXTEk%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;905&quot; height=&quot;95&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;95&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; (참고) kind k8s 에서 kubelet config 설정 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767851943587&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh root@server # 이미 server 에 ssh 접속 상태
---------------------------------------------------------------
# api -&amp;gt; kubelet 접속을 위한 RBAC 설정
# Create the system:kube-apiserver-to-kubelet ClusterRole with permissions to access the Kubelet API and perform most common tasks associated with managing pods:
cat kube-apiserver-to-kubelet.yaml
kubectl apply -f kube-apiserver-to-kubelet.yaml --kubeconfig admin.kubeconfig
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created

# 확인
kubectl get clusterroles system:kube-apiserver-to-kubelet --kubeconfig admin.kubeconfig
kubectl get clusterrolebindings system:kube-apiserver --kubeconfig admin.kubeconfig

---------------------------------------------------------------&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;jumpbox&lt;/span&gt; 서버에서 &lt;span data-token-index=&quot;2&quot;&gt;k8s controlplane&lt;/span&gt; 정상 동작 확인 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767851990355&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -s -k --cacert ca.crt https://server.kubernetes.local:6443/version | jq
{
  &quot;major&quot;: &quot;1&quot;,
  &quot;minor&quot;: &quot;32&quot;,
  &quot;gitVersion&quot;: &quot;v1.32.3&quot;,
  &quot;gitCommit&quot;: &quot;32cc146f75aad04beaaa245a7157eb35063a9f99&quot;,
  &quot;gitTreeState&quot;: &quot;clean&quot;,
  &quot;buildDate&quot;: &quot;2025-03-11T19:52:21Z&quot;,
  &quot;goVersion&quot;: &quot;go1.23.6&quot;,
  &quot;compiler&quot;: &quot;gc&quot;,
  &quot;platform&quot;: &quot;linux/arm64&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/05Rn8/dJMcaiaXbQw/kkKlAnnUkOWhy7wQwCB950/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/05Rn8/dJMcaiaXbQw/kkKlAnnUkOWhy7wQwCB950/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/05Rn8/dJMcaiaXbQw/kkKlAnnUkOWhy7wQwCB950/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F05Rn8%2FdJMcaiaXbQw%2FkkKlAnnUkOWhy7wQwCB950%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;787&quot; height=&quot;187&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;187&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;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;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;</description>
      <category>Kubernetes</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/48</guid>
      <comments>https://netoppa.tistory.com/48#entry48comment</comments>
      <pubDate>Thu, 8 Jan 2026 14:26:16 +0900</pubDate>
    </item>
    <item>
      <title>[proxmox] pask vm 생성</title>
      <link>https://netoppa.tistory.com/47</link>
      <description>&lt;p&gt;#pask&lt;/p&gt;
&lt;p&gt;프록스목스에 임포트하기&lt;/p&gt;
&lt;h3&gt;성공한 qm 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;root@hwlim:~# qm config 102
bios: seabios
boot: order=sata0
cores: 4
cpu: x86-64-v2-AES
efidisk0: storage-r5:vm-102-disk-0,efitype=4m,pre-enrolled-keys=1,size=4M
ide0: storage-r5:vm-102-disk-1,size=30G
ide2: none,media=cdrom
memory: 4096
meta: creation-qemu=8.1.5,ctime=1764130458
name: pask
net0: virtio=BC:24:11:86:44:4F,bridge=vmbr0,firewall=1
numa: 0
ostype: l26
sata0: storage-r5:vm-102-disk-1,size=30G
scsi0: storage-r5:vm-102-disk-1,size=30G
scsihw: virtio-scsi-single
serial0: socket
smbios1: uuid=a83ea597-441d-4396-8fcf-f43fd9f85768
sockets: 1
unused0: local:102/vm-102-disk-0.qcow2
vmgenid: 4aa3aea3-eaff-484d-a657-48c35df497b4&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;VM 생성 (Legacy BIOS + SATA 설정)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qm create 1500 \
  --name pask \
  --memory 4096 \
  --cores 2 \
  --net0 virtio,bridge=vmbr0 \
  --bios seabios \
  --ostype l26 \
  --serial0 socket&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;qcow2 변환&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qemu-img convert -p -f vmdk PLOS-PASK-KS-v2.2.7.3.1-disk1.vmdk -O qcow2 PLOS-PASK-KS-v2.2.7.3.1.qcow2&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;qcow2 파일을 SCSI 디스크로 임포트 (가장 안정적)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qm importdisk 1500 /var/lib/vz/template/qcow/PLOS-PASK-KS-v2.2.6.4.0.qcow2 storage-r5 --format qcow2&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;임포트된 디스크를 SATA0에 연결&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qm set 1500 --sata0 storage-r5:vm-1500-disk-0&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;부팅 순서 설정 (SATA 우선)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qm set 1500 --boot order=sata0&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;추가 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qm set 1500 --cpu x86-64-v2-AES
qm set 1500 --numa 0
qm set 1500 --scsihw virtio-scsi-single
qm set 1500 --sockets 1
qm set 1500 --firewall 1&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Hypervisor</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/47</guid>
      <comments>https://netoppa.tistory.com/47#entry47comment</comments>
      <pubDate>Wed, 26 Nov 2025 16:37:17 +0900</pubDate>
    </item>
    <item>
      <title>Jupyterhub 인프라 구축 및 설치</title>
      <link>https://netoppa.tistory.com/46</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Jupyterhub&amp;nbsp;인프라&amp;nbsp;구축&amp;nbsp;및&amp;nbsp;설치&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;설치환경&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;인프라 : Kind 쿠버네티스&lt;br /&gt;설치툴 : 헬름&lt;br /&gt;호스트 : 우분투 22.04 LTS&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764034423012&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# KIND : k8s(1.23.7) 배포

git clone https://github.com/AcornPublishing/istio-in-action
cd istio-in-action/book-source-code-master
pwd # 각자 자신의 pwd 경로
code .

# 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - &amp;lt;&amp;lt;EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP
    hostPort: 30000
  - containerPort: 30001 # Prometheus
    hostPort: 30001
  - containerPort: 30002 # Grafana
    hostPort: 30002
  - containerPort: 30003 # Kiali
    hostPort: 30003
  - containerPort: 30004 # Tracing
    hostPort: 30004
  - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
    hostPort: 30005
  - containerPort: 30006 # TCP Route
    hostPort: 30006
  - containerPort: 30007 # kube-ops-view
    hostPort: 30007
  extraMounts: # 해당 부분 생략 가능
  - hostPath: /hoem/ubuntu/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정
    containerPath: /istiobook
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.0.0/22
EOF

# 설치 확인
docker ps

# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update &amp;amp;&amp;amp; apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'

# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ=&quot;Asia/Seoul&quot; --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

## kube-ops-view 접속 URL 확인
open &quot;http://localhost:30007/#scale=1.5&quot;
open &quot;http://localhost:30007/#scale=1.3&quot;

# (옵션) metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf7QF2/dJMcai9xyAC/oyB0LkuXvzU5cxCCAQZHL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf7QF2/dJMcai9xyAC/oyB0LkuXvzU5cxCCAQZHL0/img.png&quot; data-alt=&quot;KIND 쿠버네티스 클러스터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf7QF2/dJMcai9xyAC/oyB0LkuXvzU5cxCCAQZHL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf7QF2%2FdJMcai9xyAC%2FoyB0LkuXvzU5cxCCAQZHL0%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;575&quot; height=&quot;57&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;KIND 쿠버네티스 클러스터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Helm 저장소 추가&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1764034578325&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;helm repo add jupyterhub https://hub.jupyter.org/helm-chart/
helm repo update&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;values.yaml 생성&lt;/b&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;pre id=&quot;code_1764034629687&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir -p /home/ubuntu/jupyter/config
cat &amp;gt; /home/ubuntu/jupyter/config/values.yaml &amp;lt;&amp;lt;'EOF'
proxy:
  service:
    type: NodePort
    nodePorts:
      http: 30006     # &amp;larr; 반드시 이 값
  https:
    enabled: false  # HTTP만 사용 (Kind 테스트용)

hub:
  extraEnv:
    JUPYTERHUB_ADMIN_USERS: &quot;admin&quot;   # &amp;larr; 여기서 관리자 지정 (콤마로 여러 명 가능)

  config:
    JupyterHub:
      authenticator_class: dummy
    DummyAuthenticator:
      password: &quot;jupyter123!&quot;         # &amp;larr; 모든 유저가 이 비밀번호로 로그인

singleuser:
  storage:
    dynamic:
      storageClass: null
    capacity: 10Gi
  memory:
    guarantee: 1G
    limit: 2G
  cpu:
    guarantee: 0.5
    limit: 1.0

# (선택) 프로필 예시
singleuser:
  profileList:
    - display_name: &quot;Minimal Notebook&quot;
      default: true
      kubespawner_override:
        image: jupyter/minimal-notebook:latest
    - display_name: &quot;Data Science Full&quot;
      kubespawner_override:
        image: jupyter/datascience-notebook:latest
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Jupyter chart 설치&lt;/b&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1764034742643&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# namespace 생성
kubectl create ns jupyterhub

# helm 설치
helm upgrade --install jupyterhub jupyterhub/jupyterhub \
  --namespace jupyterhub \
  --version 3.3.5 \
  --values /home/ubuntu/jupyter/config/values.yaml \
  --timeout 10m \
  --wait --cleanup-on-fail&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M8wfz/dJMcachervQ/CP7svLJMvRK1fc3eA2yw7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M8wfz/dJMcachervQ/CP7svLJMvRK1fc3eA2yw7K/img.png&quot; data-alt=&quot;쿠버네티스 배포 상태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M8wfz/dJMcachervQ/CP7svLJMvRK1fc3eA2yw7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM8wfz%2FdJMcachervQ%2FCP7svLJMvRK1fc3eA2yw7K%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;977&quot; height=&quot;442&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿠버네티스 배포 상태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Jupyter 확인 및 접속&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1764034805130&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 서비스 확인
kubectl get svc -n jupyterhub proxy-public
# &amp;rarr; 80:30006/TCP 확인

# 브라우저 접속
http://192.168.9.248:30006&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brarJ9/dJMcabWVCz5/1FL5woG1XnJ7skSz41mjok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brarJ9/dJMcabWVCz5/1FL5woG1XnJ7skSz41mjok/img.png&quot; data-alt=&quot;http://192.168.9.248:30006 (admdin / jupyter123!)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brarJ9/dJMcabWVCz5/1FL5woG1XnJ7skSz41mjok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrarJ9%2FdJMcabWVCz5%2F1FL5woG1XnJ7skSz41mjok%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;1517&quot; height=&quot;797&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;http://192.168.9.248:30006 (admdin / jupyter123!)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;초기화 및 삭제&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1764034855862&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;helm delete jupyterhub -n jupyterhub
kubectl delete ns jupyterhub
kind delete cluster --name myk8s&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>jupyter</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/46</guid>
      <comments>https://netoppa.tistory.com/46#entry46comment</comments>
      <pubDate>Tue, 25 Nov 2025 10:42:01 +0900</pubDate>
    </item>
    <item>
      <title>[openstack] 가상화 웹방화벽과 ADC 설치</title>
      <link>https://netoppa.tistory.com/45</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;가상화 웹방화벽&amp;amp;ADC 마이그레이션 설치(OVA to QCOW2)&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사전준비&lt;br /&gt;-가상화 웹방화벽(WEBFRONT-KV) OVA 파일&lt;br /&gt;- 가상화 ADC(PAS-KS) OVA파일&amp;nbsp;&lt;/blockquote&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;p data-ke-size=&quot;size16&quot;&gt;오픈스택 환경에 가상화 웹방화벽(WAF)과 L4스위치(ADC) 인스턴스를 설치하여 클라우드 환경에서 웹서비스 보안과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부하분산 서비스(SLB)를 실습을 진행하고자 함.&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;VMDK 파일을 QCOW2로 변환 (Conversion) &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1763638141266&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 웹방화벽 ova 파일 압축해제
tar vxf PLOS-WFK-KS-V4.0.8.63.6-rc1-disk1.ova

# qcow2로 변환 (입력 파일명은 사용자의 ls 결과에 따름)
qemu-img convert -f vmdk -O qcow2 PLOS-WFK-KS-V4.0.8.63.6-rc1-disk1.vmdk PLOS-WFK-v4.0.8.qcow2



# ADC(PAS-KS) ova 파일 압축해제
tar vxf PLOS-PASK-KS-v2.2.5.7.5-disk1.ova

# qcow2로 변환 (입력 파일명은 사용자의 ls 결과에 따름)
qemu-img convert -f vmdk -O qcow2 PLOS-PASK-KS-v2.2.5.7.5-disk1.vmdk PLOS-PASK-v2.2.5.qcow2&lt;/code&gt;&lt;/pre&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; OpenStack 이미지를 Glance에 등록&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1763638241162&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 웹방화벽 이미지 생성
openstack image create &quot;PLOS-WFK-v4.0.8&quot; \
  --file PLOS-WFK-v4.0.8.qcow2 \
  --disk-format qcow2 \
  --container-format bare \
  --public \
  --property hw_disk_bus=virtio \
  --property hw_vif_model=virtio
  
  
  
  # ADC 이미지 생성
openstack image create &quot;PLOS-PASK-2.2.6&quot; \
  --file PLOS-PASK-KS-v2.2.6.4.0.qcow2 \
  --disk-format qcow2 \
  --container-format bare \
  --shared \
  --property hw_disk_bus=virtio \
  --property hw_vif_model=virtio&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ADC Flavor(사양) 생성 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763642429263&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ADC OVF 파일 분석 결과에 따른 정확한 리소스
vCPU: 1 core
RAM: 3072 MB (3GB)
Disk: 30 GB

# Flavor 생성 명령어
openstack flavor create --ram 3072 --disk 30 --vcpus 1 plos.pask.custom&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; OVF 파일(PLOS-WFK-KS-V4.0.8.63.6-rc1.ovf) 내용을 분석 &lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;cat PLOS-WFK-KS-V4.0.8.63.6-rc1.ovf&lt;br /&gt;&amp;lt;--- 내용 생략 --&amp;gt;&lt;br /&gt;&lt;br /&gt;vCPU: 2 core&lt;br /&gt;RAM: 2000 MB (약 2GB)&lt;br /&gt;Disk: 40 GB (OVF의 ovf:capacity=&quot;40&quot; 부분 참고)&lt;/blockquote&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;인스턴스 생성 (Launch Instance)&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;1996&quot; data-origin-height=&quot;1650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BxcjL/dJMcafyeElz/g4VpEEq9gZaCPprvDOEPK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BxcjL/dJMcafyeElz/g4VpEEq9gZaCPprvDOEPK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BxcjL/dJMcafyeElz/g4VpEEq9gZaCPprvDOEPK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxcjL%2FdJMcafyeElz%2Fg4VpEEq9gZaCPprvDOEPK0%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;1996&quot; height=&quot;1650&quot; data-origin-width=&quot;1996&quot; data-origin-height=&quot;1650&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2075&quot; data-origin-height=&quot;1293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csBPpM/dJMcaiIrPs6/ASAzGQGhm12G4biBbd61QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csBPpM/dJMcaiIrPs6/ASAzGQGhm12G4biBbd61QK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csBPpM/dJMcaiIrPs6/ASAzGQGhm12G4biBbd61QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsBPpM%2FdJMcaiIrPs6%2FASAzGQGhm12G4biBbd61QK%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;2075&quot; height=&quot;1293&quot; data-origin-width=&quot;2075&quot; data-origin-height=&quot;1293&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;※ 로그 확인&lt;/p&gt;
&lt;pre id=&quot;code_1763638410954&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;openstack console log show PLOS-WFK-Instance&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;웹방화벽(WEBFRON-KS) 접속&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;2544&quot; data-origin-height=&quot;1856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boqO5O/dJMcaaReijm/9EP2fxWxeEObEViUc6Y05k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boqO5O/dJMcaaReijm/9EP2fxWxeEObEViUc6Y05k/img.png&quot; data-alt=&quot;https://192.168.7.238:8443/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boqO5O/dJMcaaReijm/9EP2fxWxeEObEViUc6Y05k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboqO5O%2FdJMcaaReijm%2F9EP2fxWxeEObEViUc6Y05k%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;2544&quot; height=&quot;1856&quot; data-origin-width=&quot;2544&quot; data-origin-height=&quot;1856&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://192.168.7.238:8443/&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;웹방화벽 초기 설정&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;1130&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kZqMZ/dJMcai2KB7r/pbo4LelkISXPj8se7XmKQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kZqMZ/dJMcai2KB7r/pbo4LelkISXPj8se7XmKQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kZqMZ/dJMcai2KB7r/pbo4LelkISXPj8se7XmKQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkZqMZ%2FdJMcai2KB7r%2Fpbo4LelkISXPj8se7XmKQk%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;1130&quot; height=&quot;482&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; 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;1337&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Toqb6/dJMcaacCGRI/8R9yTRddPbP6WS6jsBksok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Toqb6/dJMcaacCGRI/8R9yTRddPbP6WS6jsBksok/img.png&quot; data-alt=&quot;ssh 접속&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Toqb6/dJMcaacCGRI/8R9yTRddPbP6WS6jsBksok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FToqb6%2FdJMcaacCGRI%2F8R9yTRddPbP6WS6jsBksok%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;1337&quot; height=&quot;738&quot; data-origin-width=&quot;1337&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ssh 접속&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;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;ADC(PAS-KS) 접속&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>openstack</category>
      <author>넷오빠</author>
      <guid isPermaLink="true">https://netoppa.tistory.com/45</guid>
      <comments>https://netoppa.tistory.com/45#entry45comment</comments>
      <pubDate>Thu, 20 Nov 2025 21:54:48 +0900</pubDate>
    </item>
  </channel>
</rss>