<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>초집중</title>
    <link>https://choiblog.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 29 Jun 2026 12:19:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>초집중</managingEditor>
    <image>
      <title>초집중</title>
      <url>https://tistory1.daumcdn.net/tistory/5396134/attach/b8098d5b359840a6aeea6e3686ad0249</url>
      <link>https://choiblog.tistory.com</link>
    </image>
    <item>
      <title>Pods란</title>
      <link>https://choiblog.tistory.com/234</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 작업자 노드에 직접 컨테이너를 배포하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너는 쿠버네티스 객체라고 불리는 Pod라는 형태로 포장되어 제공됩니다.&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;Pod는 애플리케이션의 단일 인스턴스로 쿠버네티스에서 만들 수 있는 가장 작은 물체입니다.&lt;/p&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 data-ke-size=&quot;size16&quot;&gt;이런 상황에서 스핀업할 때 같은 애플리케이션의 새로운 인스턴스로 완전히 새로운 pod를 만들어냅니다.&lt;/p&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 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 data-ke-size=&quot;size16&quot;&gt;따라서 정리해보면 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod는 애플리케이션에서 실행되는 컨테이너와 1:1 관계를 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;확장하려면 새로운 Pod를 만들고, 축소하려면 기존 Pod를 삭제해야 합니다.&lt;/li&gt;
&lt;li&gt;애플리케이션 규모를 키우기 위해 기존 Pod에 추가적인 컨테이너를 만들지 않습니다.&lt;/li&gt;
&lt;li&gt;Pod에 어떤 컨테이너로 구성되어 있는지만 정의하면 됩니다.&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;Multi-Container Pods&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 포드와 컨테이너는 1:1이지만, 하나의 Pod에 여러 개의 컨테이너가 있을 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 종류의 컨테이너가 여러 개 있는 것이 아닙니다.&lt;/li&gt;
&lt;li&gt;가끔씩 Helper Container를 통해 같은 포드에서 컨테이너가 2개 될 수 있지만, Helper 원본 컨테이너가 죽으면 Helper 컨테이너도 죽고, 생성되면 같이 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 컨테이너는 localhost라고 불리며 같은 네트워크를 공유해 서로 통신이 가능합니다.&lt;/li&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;h2 data-ke-size=&quot;size26&quot;&gt;kubectl - Pod 배포하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl run nignx&amp;nbsp;명령을 통해 포드를 생성하여 Docker container를 배포합니다.&lt;/li&gt;
&lt;li&gt;kubectl run nginx --image nginx&amp;nbsp;이미지 파라미터를 사용해 이미지 이름을 명시합니다.&lt;/li&gt;
&lt;li&gt;kubectl get pods&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Minikube install&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6s79t/btsGAK3KZHt/NUS6NsHaUfC20fxY3oMUvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6s79t/btsGAK3KZHt/NUS6NsHaUfC20fxY3oMUvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6s79t/btsGAK3KZHt/NUS6NsHaUfC20fxY3oMUvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6s79t%2FbtsGAK3KZHt%2FNUS6NsHaUfC20fxY3oMUvK%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;919&quot; height=&quot;217&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;217&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;928&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zPoZK/btsGAPqpmk4/PYw1h6w7upg2vDQjTSuXoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zPoZK/btsGAPqpmk4/PYw1h6w7upg2vDQjTSuXoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zPoZK/btsGAPqpmk4/PYw1h6w7upg2vDQjTSuXoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzPoZK%2FbtsGAPqpmk4%2FPYw1h6w7upg2vDQjTSuXoK%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;928&quot; height=&quot;330&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;330&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;756&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceDoqQ/btsGAzuBWl0/MJs2AXFMLNyh7F9qdslut1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceDoqQ/btsGAzuBWl0/MJs2AXFMLNyh7F9qdslut1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceDoqQ/btsGAzuBWl0/MJs2AXFMLNyh7F9qdslut1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceDoqQ%2FbtsGAzuBWl0%2FMJs2AXFMLNyh7F9qdslut1%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;756&quot; height=&quot;265&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;265&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;kubectl run nginx --image=nginx
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 버전의 Kubernetes에서 kubectl run 명령어는 기본적으로 &lt;b&gt;파드(Pod)를 실행&lt;/b&gt;하는 데 사용됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;명령형 배포 사용시&amp;nbsp;kubectl create deployment nginx --image=nginx
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령어는 디플로이먼트(Deployment) 리소스를 생성하는 데 특화되어있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;정리하자면 kubectl run은 주로 단일 파드를 빠르게 실행하는 데 사용되며, kubectl create deployment는 디플로이먼트를 생성하여 하나 이상의 파드를 관리하는 데 사용됩니다.&lt;/p&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/234</guid>
      <comments>https://choiblog.tistory.com/234#entry234comment</comments>
      <pubDate>Fri, 12 Apr 2024 21:36:14 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes overview</title>
      <link>https://choiblog.tistory.com/233</link>
      <description>&lt;h1&gt;개요&lt;/h1&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;h1&gt;Container&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/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;558&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/opbQP/btsGAzH5VIX/YhpP0yh4tiagZnDfzc7Kvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/opbQP/btsGAzH5VIX/YhpP0yh4tiagZnDfzc7Kvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/opbQP/btsGAzH5VIX/YhpP0yh4tiagZnDfzc7Kvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FopbQP%2FbtsGAzH5VIX%2FYhpP0yh4tiagZnDfzc7Kvk%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;558&quot; height=&quot;352&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;352&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 두 가지로 구성(OS 커널과 소프트웨어 세트)&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제 구조 안에서 커널을 공유합니다. 만약 다른 커널(윈도우)을 공유할 필요가 생기게 된다면 윈도우 서버의 Docker를 사용해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 OS의 핵심 기능을 실행하지 못하며, 다른 운영체제와 커널을 같은 하드웨어단에서 가상화하거나실행하지 않는 것은 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;컨테이너 오케스트레이션&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 수가 증가해서 애플리케이션 확장이 필요하거나 감소할 때 규모를 줄이는 방법으로, 플랫폼은 컨테이너간 연결을 조정하여 로드에 따라 자동으로 스케일업, 다운을 해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker Swarm&lt;/li&gt;
&lt;li&gt;MESOS&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 오케스트레이션 툴들이 있으며, 현재 모든 공용 클라우드 서비스 공급자에서 지원됩니다.&lt;/p&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;h1&gt;Kubernetes 기본 개념&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Node&lt;/h2&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;과거에는 minions라고 불렸습니다.&lt;/li&gt;
&lt;li&gt;따라서 실행 중인 노드가 Fail 상태로 넘어가면 서버가 다운되므로, 노드가 하나 이상이어야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Clusster&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터는 노드의 묶음으로 한 노드가 실패해도 다른 노드를 통해 애플리케이션에 접근할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다중 노드는 로드 밸런서로 활용하기도 용이합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Master&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 관리, 클러스터 데이터 저장, 노드 모니터링, 노드 fail시 해당 fail 노드의 작업을 다른 작업자 노드에게 전달시키기 위해 필요합니다.&lt;/li&gt;
&lt;li&gt;마스터는 쿠버네티스가 설치된 노드 중 하나로, 워커 노드에서 컨테이너의 실제 오케스트레이션 관리를 책임집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Components&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠버네티스를 설치할 때 다양한 구성 요소를 설치하게 됩니다.(API Server, kubelet, kube, 런타임 컨트롤러, 스케쥴러..)&lt;/li&gt;
&lt;li&gt;API Server는 쿠버네티스의 프론트엔드와 같이 사용자, CLI 모두 API Server와 통신해 클러스터와 상호작용합니다.&lt;/li&gt;
&lt;li&gt;etcd(key store)는 Key-Value 저장소로 쿠버네티스가 클러스터 관리에 사용되는 모든 데이터를 저장하는데 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터에 노드가 여럿이고 마스터도 여럿일 경우 분산된 방식으로 클러스터 내부의 모든 노드에 관한 정보를 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scheduler는 다중 노드에 걸쳐 작업이나 컨테이너를 배포하는 역할을 합니다. &amp;rarr; 새로 생성된 노드를 찾아 컨테이너에 할당합니다.&lt;/li&gt;
&lt;li&gt;Controller &amp;rarr; Node, Container, Endpoint가 다운될 때 이를 인지하고 대응합니다. 이를 통해 새로운 컨테이너를 실행할지 결정하는 역할을 담당합니다.&lt;/li&gt;
&lt;li&gt;container runtime은 컨테이너를 실행하는데 사용되는 기본 소프트웨어입니다. &amp;rarr; Docker&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;에이전트는 예상대로 컨테이너가 노드에서 실행되는지 확인하게 됩니다.&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;Master vs. Worker Nodes&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Worker 노드는 컨테이너 런타임과 Kubelet agent를 가지고 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;agent로 수집된 노드의 health state를 마스터에게 제공하며, 마스터가 요청한 작업을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&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;수집된 모든 정보는 마스터의 key-value저장소(etcd)에 존재합니다.&lt;/li&gt;
&lt;li&gt;Controller, Scheduler 또한 존재합니다.&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;Kubectl&lt;/h2&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;kubectl run 명령은 클러스터에 응용 프로그램을 배포하는데 사용됩니다.&lt;/li&gt;
&lt;li&gt;kubectl cluster-info 명령은 클러스터 정보를 보는데 사용 됩니다.&lt;/li&gt;
&lt;li&gt;kubectl get 노드 명령은 모든 노드를 열거하는데 사용합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/233</guid>
      <comments>https://choiblog.tistory.com/233#entry233comment</comments>
      <pubDate>Fri, 12 Apr 2024 21:33:44 +0900</pubDate>
    </item>
    <item>
      <title>Docker 배포 최적화</title>
      <link>https://choiblog.tistory.com/232</link>
      <description>&lt;h1&gt;개요&lt;/h1&gt;
&lt;div&gt;배포환경을 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;npm start⁠ 를 통한 개발 서버로 시작하기에는 프로덕션 환경에서 최적화 되지않아&amp;nbsp; 너무 느리게 동작합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;따라서 프로덕션을 위해 자체 빌드 스크립트를 통해 가져와 코드 컴파일 및 최적화를 수행하고 최적화된 파일만 전달하여 실제로 더 효율적으로 사용할 수 있게 해야합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;빌드전용 컨테이너 만들기&lt;/h1&gt;
&lt;div&gt;React로 개발한 프로젝트에서 기본적으로 개발과 프로덕션 환경은 서로 다르게 실행되어야 합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;다시말해, 두 가지의 다른 환경을 설정해야하며 어떠한 환경을 구축해야하는지를 이해해야합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;특히 React를 사용하는 프로젝트를 배포하고자 빌드했을 때의 결과물은 nodejs가 필요없는 파일들을 생성하게 되며, 이는 사용자 환경에서 실행되는 것이지 서버의&lt;span&gt; Node.js 환경에서 실행되는 것이 아닙니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;⁠Dockerfile.dev&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;현재 컨테이너를 올릴 때 다음과 같은 Dockerfile을 사용하고 있습니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 3000

CMD [ &quot;npm&quot;, &quot;start&quot; ]&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile.prod&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;해당 환경에서는 빌드 및 배포에 최적화된 Javascript 파일을 만들기 위한 웹서버를 만드는 코드를 작성해야합니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;npm build : 서버를 만드는 게 아닌 코드를 최적화하는 과정입니다.&lt;/li&gt;
&lt;li&gt;npm start : 서버를 만들지만 최적화되지 않았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;FROM node:14-alpine

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

CMD [ &quot;npm&quot;, &quot;run&quot;, &quot;build&quot; ]&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;Multi-Stage Builds&lt;/h1&gt;
&lt;div&gt;파일 내부에 &amp;lsquo;스테이지'라고 하는 여러 빌드 단계 또는 설정 단계를 정의하는 하나의 Dockerfile을 가질 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;스테이지는 서로의 결과를 복사할 수 있으므로, 최적화된 파일을 생성하는 스테이지와 해당 생성된 파일을 제공하는 스테이지로 나눠지게 되며 위에서 아래로 단계별로 모든 스테이지를 거쳐 전체 Dockerfile을 빌드할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;또는 빌드 단계를 건너뛰어 빌드하기 위해서 개별 스테이지를 선택할 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;background-color: #000000;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# as를 추가하여 스테이지명을 설정 가능
FROM node:14-alpine as build

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

RUN npm run build

# 명령 후에 다른 베이스 이미지로 전환
# 빌드 과정에만 node 환경이 필요하기 때문입니다.
# FROM을 통해 새로운 스테이지를 만들며 동일한 이미지를 가져온다 하더라도 새로운 스테이지로 생성됩니다.
FROM nginx:stable-alpine

# 빌드 스테이지를 참조하여 가져옵니다
# 빌드 과정 중 /build 폴더에 저장하기 때문에 따로 경로를 지정  
COPY --from=build /app/build /usr/share/nginx/html

EXPOSE 80

CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;# 브라우저는 기본적으로 응답을 준 서버의 도메인으로 그대로 전송합니다.
# 만약 다른 도메인으로 요청을 보내야한다면 따로 도메인 변경이 필요합니다.
const domain = &quot;http://localhost/..&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dockerfile RUN &amp;amp; CMD의 차이&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;RUN&lt;/div&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;RUN 지시어는 이미지를 빌드할 때 실행되며&lt;/b&gt;, 이미지에 소프트웨어를 설치하거나 설정을 변경하는 데 사용됩니다. RUN으로 실행된 명령의 결과는 이미지의 새로운 레이어로 저장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: 소프트웨어 패키지 설치, 빌드 과정에서 필요한 파일 복사 또는 설정 파일 수정 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;CMD&lt;/div&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;CMD 지시어는 이미지로부터 컨테이너를 시작할 때 실행되는 기본 명령을 정의&lt;/b&gt;합니다. CMD는 이미지가 실행될 때 한 번 실행되며, docker run 명령어를 사용해 오버라이드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: 컨테이너가 시작할 때 실행될 서버, 스크립트 실행, 기본 실행 명령어 제공 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RUN은 &lt;b&gt;이미지 빌드 과정&lt;/b&gt;에서 실행되어 결과를 이미지에 반영합니다. 이는 빌드 시점에만 영향을 미치며, 이미지 내부에 변경사항을 적용합니다.&lt;/li&gt;
&lt;li&gt;CMD는 &lt;b&gt;컨테이너 실행 시점&lt;/b&gt;에 기본 명령을 정의하며, 이미지를 기반으로 하는 모든 컨테이너에 대한 기본 동작을 설정합니다. 이는 실행 시점에만 영향을 미치며, 사용자가 docker run 명령어로 쉽게 대체할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/232</guid>
      <comments>https://choiblog.tistory.com/232#entry232comment</comments>
      <pubDate>Wed, 10 Apr 2024 14:39:35 +0900</pubDate>
    </item>
    <item>
      <title>Docker 배포</title>
      <link>https://choiblog.tistory.com/231</link>
      <description>&lt;h1 style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;⁠도커를 수동으로 설치하고 배포하는 방식&lt;/h1&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;호스트머신(EC2)에 처음부터 모든 것을 설치하게 되면 높은 자유도를 갖지만 그만큼의 단점을 얻게 됩니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;보안부터 네트워크, 운영까지 리모트 머신의 구성에 대한 모든 책임을 가지게 됩니다.&amp;nbsp;&lt;br /&gt;충분한 컴퓨팅 파워, 트래픽 모니터링, 시스템 버전 업데이트까지 항상 최신 상태로 유지해야하며 관리 책임의 의무가 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;여러가지 기본 환경을 익혀야할 필요가 있으며, 배포 워크플로를 구성하는 명령어를 만들어야합니다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;완전히 다른 기술이 필요합니다.&lt;br /&gt;애플리케이션 자체, 다중 컨테이너 애플리케이션, 규모가 큰 애플리케이션 등등 해야할 작업을 실제로 파악하고 행동해야한다는 점이 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;이러한 부분을 해결하기 위해 우리는 관리형 서비스를 이용하게 됩니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #000000; color: #f9241c;&quot;&gt;⁠&lt;/span&gt;⁠수동배포에서 관리형 서비스로&lt;/h1&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&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;/ul&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #000000; color: #f9241c;&quot;&gt;⁠&lt;/span&gt;⁠ECS&lt;/h1&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;ECS는 4가지 범주로 분류 됩니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&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;/ul&gt;
&lt;/div&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;컨테이너&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;단순히 ECS가&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker run&amp;nbsp;을 실행하는 방법을 지정하기만 하면 됩니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&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;Health check&lt;/li&gt;
&lt;li&gt;환경변수 혹은 entrypoint&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;pre class=&quot;crmsh&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;$ docker run --name node-demo -p 80:80 # 위 명령을 컨테이너로 설정합니다&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #f98700;&quot;&gt;⁠&lt;/span&gt;⁠&lt;span style=&quot;background-color: #000000; color: #f98700;&quot;&gt;⁠&lt;/span&gt;⁠태스크&lt;/h2&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;컨테이너에 대한 태스크를 정의할 수 있습니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;애플리케이션의 블루프린트로 aws에 컨테이너를 시작하는 방법을 알릴 수 있으며, 정리하자면&amp;nbsp;단순한 ⁠docker run⁠ 명령이 아닌 실행하는 서버를 구성하는 방법입니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;그러므로 태스크에는 둘 이상의 컨테이너가 포함될 수 있으며, 하나의 태스크에 여러 개의 컨테이너를 등록할 수 있습니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;즉 하나 이상의 컨테이너를 실행하는 하나의 원격 머신(EC2...)을 태스크라고 정의할 수 있습니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;Fargate가 디폴트로 직접 EC2 인스턴스를 실행하는게 아닌 컨테이너와 실행 설정을 저장하여 컨테이너에 대한 추가적인 요청이 있을 때마다 컨테이너를 시작하고, 처리하고 중지합니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&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;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #f98700;&quot;&gt;⁠&lt;/span&gt;⁠서비스&lt;/h2&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;구성된 애플리케이션과 애플리케이션을 포함하는 컨테이너를 실행하는 방법을 컨트롤 합니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;로드밸런서를 추가하거나 리다이렉션, 컨테이너 실행 등등을 처리하며, 모든 태스크가 서비스에 의해 실행됩니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;태스크 당 하나의 서비스가 권장사항입니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #000000; color: #f98700;&quot;&gt;⁠&lt;/span&gt;⁠클러스터&lt;/h2&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;서비스가 실행되는 전체 네트워크라고 할 수 있습니다.&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #404040; text-align: start;&quot;&gt;여러 개의 태스크와 서비스를 통해 컨테이너가 만들어지게 되면 해당 컨테이너들은 모두 논리적으로 함께 속하게 되고 서로 통신할 수 있게 됩니다.&lt;/div&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/231</guid>
      <comments>https://choiblog.tistory.com/231#entry231comment</comments>
      <pubDate>Wed, 3 Apr 2024 21:46:04 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch 튜토리얼</title>
      <link>https://choiblog.tistory.com/230</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #404040; text-align: start;&quot;&gt;PyTorch 튜토리얼&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #404040; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html&quot;&gt;https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tutorials.pytorch.kr/beginner/basics/intro.html&quot;&gt;https://tutorials.pytorch.kr/beginner/basics/intro.html&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PI4eU/btsGeRCAPBa/gYwoag3PQ2ltz3hjA7BSGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PI4eU/btsGeRCAPBa/gYwoag3PQ2ltz3hjA7BSGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PI4eU/btsGeRCAPBa/gYwoag3PQ2ltz3hjA7BSGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPI4eU%2FbtsGeRCAPBa%2FgYwoag3PQ2ltz3hjA7BSGk%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;378&quot; height=&quot;133&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;텐서는 배열 및 행렬과 매우 유사한 특수한 데이터 구조입니다. PyTorch에서는 텐서를 사용하여 모델의 입력과 출력은 물론 모델의 매개변수도 인코딩합니다.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;텐서는 GPU나 기타 특수 하드웨어에서 실행되어 컴퓨팅을 가속화할 수 있다는 점을 제외하면 NumPy의 ndarray와 유사합니다.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;div&gt;Python 기반의 패키지로 다음과 같은 두 가지를 대상으로 합니다:&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NumPy를 대체하고 GPU의 연산력을 사용&lt;/li&gt;
&lt;li&gt;최대한의 유연성과 속도를 제공하는 딥러닝 연구 플랫폼&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;h1&gt;Pytorch 기본 익히기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이토치(PyTorch)에는&amp;nbsp;&lt;a href=&quot;https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Fpytorch.org%2Fdocs%2Fstable%2Fdata.html&quot;&gt;데이터 작업을 위한 기본 요소&lt;/a&gt; 두가지 요소가 존재합니다torch.utils.data.DataLoader 와 torch.utils.data.Dataset로 Dataset 은 샘플과 정답(label)을 저장하고, DataLoader 는 Dataset 을 순회 가능한 객체(iterable)로 감쌉니다.&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;pre class=&quot;xl&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import torch
from torch import nn
from torch.utils.data import DataLoaderfrom torchvision import datasets
from torchvision.transforms import ToTensor&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;886&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xyddt/btsGf9QdSUX/jLk4W6f113KHCL6YhAcnM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xyddt/btsGf9QdSUX/jLk4W6f113KHCL6YhAcnM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xyddt/btsGf9QdSUX/jLk4W6f113KHCL6YhAcnM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXyddt%2FbtsGf9QdSUX%2FjLk4W6f113KHCL6YhAcnM1%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;886&quot; height=&quot;67&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;67&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 class=&quot;routeros&quot;&gt;&lt;code&gt;# 공개 데이터셋에서 학습 데이터를 내려받습니다.
training_data = datasets.FashionMNIST(
    root=&quot;data&quot;,
    train=True,
    download=True,
    transform=ToTensor(),
)

# 공개 데이터셋에서 테스트 데이터를 내려받습니다.
test_data = datasets.FashionMNIST(
    root=&quot;data&quot;,
    train=False,
    download=True,
    transform=ToTensor(),
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dataloader를 통해 순회 가능한 방식으로 변환하고 각 데이터를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;batch_size = 64

# 데이터로더를 생성합니다.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print(f&quot;Shape of X [N, C, H, W]: {X.shape}&quot;)
    print(f&quot;Shape of y: {y.shape} {y.dtype}&quot;)
    break
&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;nn.Module을 상속받는 클래스를 생성하여 정의합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;batch_size = 64

# 데이터로더를 생성합니다.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print(f&quot;Shape of X [N, C, H, W]: {X.shape}&quot;)
    print(f&quot;Shape of y: {y.shape} {y.dtype}&quot;)
    break
&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;⁠__init__&amp;nbsp;함수에서 신경망의 계층(layer)들을 정의하고&amp;nbsp;forward&amp;nbsp;함수에서 신경망에 데이터를 어떻게 전달할지 지정합니다. 가능한 경우 GPU 또는 MPS로 신경망을 이동시켜 연산을 가속(accelerate)합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;# 학습에 사용할 CPU나 GPU, MPS 장치를 얻습니다.
device = (
    &quot;cuda&quot;
    if torch.cuda.is_available()
    else &quot;mps&quot;
    if torch.backends.mps.is_available()
    else &quot;cpu&quot;
)
print(f&quot;Using {device} device&quot;)

# 모델을 정의합니다.
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=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;a href=&quot;https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Fpytorch.org%2Fdocs%2Fstable%2Fnn.html%23loss-functions&quot;&gt;손실 함수(loss function)&lt;/a&gt;&amp;nbsp;와&amp;nbsp;&lt;a href=&quot;https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Fpytorch.org%2Fdocs%2Fstable%2Foptim.html&quot;&gt;옵티마이저(optimizer)&lt;/a&gt;&amp;nbsp;가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
&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;각 학습 단계(training loop)에서 모델은 (배치(batch)로 제공되는) 학습 데이터셋에 대한 예측을 수행하고, 예측 오류를&amp;nbsp;&lt;b&gt;역전파&lt;/b&gt;하여 모델의 매개변수를 조정합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # 예측 오류 계산
        pred = model(X)
        loss = loss_fn(pred, y)

        # 역전파
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f&quot;loss: {loss:&amp;gt;7f}  [{current:&amp;gt;5d}/{size:&amp;gt;5d}]&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;학습이 정상적으로 진행되었는지 검승하기 위해 테스트 데이터셋으로 모델의 성능을 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval() #모델 평가 모드로 변경
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    correct /= size
    print(f&quot;Test Error: \\n Accuracy: {(100*correct):&amp;gt;0.1f}%, Avg loss: {test_loss:&amp;gt;8f} \\n&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;Epochs를 통한 모델을 반복 학습시키기&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;epochs = 5
for t in range(epochs):
    print(f&quot;Epoch {t+1}\\n-------------------------------&quot;)
    train(train_dataloader, model, loss_fn, optimizer) # 훈련
    test(test_dataloader, model, loss_fn) # 테스트
print(&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;모델을 저장하기&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;torch.save(model.state_dict(), &quot;model.pth&quot;)
print(&quot;Saved PyTorch Model State to model.pth&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;모델을 불러오는 과정에는 모델 구조를 다시 만들고 상태 사전을 모델에 불러오는 과정이 포함됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;model = NeuralNetwork().to(device)
model.load_state_dict(torch.load(&quot;model.pth&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;모델을 사용하여 예측합니다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;# 해당되는 10개의 class로 분류됩니다.
classes = [
    &quot;T-shirt/top&quot;,
    &quot;Trouser&quot;,
    &quot;Pullover&quot;,
    &quot;Dress&quot;,
    &quot;Coat&quot;,
    &quot;Sandal&quot;,
    &quot;Shirt&quot;,
    &quot;Sneaker&quot;,
    &quot;Bag&quot;,
    &quot;Ankle boot&quot;,
]

model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    x = x.to(device)
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: &quot;{predicted}&quot;, Actual: &quot;{actual}&quot;')

tensor([[-2.2504, -2.4639, -0.8681, -1.9371, -1.1067,  2.3279, -1.0071,  2.6530, 1.7159,  3.0928]])
# 테스트 이미지가  10개의 클래스 중 Ankle boot와 가장 가까운 것으로 예측되었고 실제 Actual과 유사한 것을 확인할 수 있습니다.
Predicted: &quot;Ankle boot&quot;, Actual: &quot;Ankle boot&quot;
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/230</guid>
      <comments>https://choiblog.tistory.com/230#entry230comment</comments>
      <pubDate>Mon, 1 Apr 2024 20:28:47 +0900</pubDate>
    </item>
    <item>
      <title>Docker Compose</title>
      <link>https://choiblog.tistory.com/229</link>
      <description>&lt;div&gt;다수의 docker build와 docker run 명령을 대체할 수 있는 단 하나의 구성으로 가지며 모든 컨테이너를 즉시 시작할 수 있습니다. 필요하다면 모든 이미지를 빌드하는 오케스트레이션 명령 도구 중 하나입니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하나의 명령을 사용하여 모든 컨테이너를 중지하고, 시작할 수 있습니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h1&gt;&lt;span style=&quot;background-color: #000000; color: #f9241c;&quot;&gt;⁠&lt;/span&gt;⁠Docker Compose가 안되는 것&lt;/h1&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile을 대체할 수 없습니다.&lt;/li&gt;
&lt;li&gt;이미지나 컨테이너를 대체할 수 없습니다&lt;/li&gt;
&lt;li&gt;다수의 호스트에서 컨테이너를 관리할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;Docker Compose Files 작성하기&lt;/h1&gt;
&lt;div&gt;컴포즈 파일에서 중요한 요스는&amp;nbsp;Service(컨테이너)⁠입니다.&lt;/div&gt;
&lt;div&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;...&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;기본적으로 터미널을 통해 도커 명령으로 할 수 있는 모든 것을 정의할 수 있습니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;컴포즈 버전을 확인할 수 있습니다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/compose/compose-file/&quot;&gt;https://docs.docker.com/compose/compose-file/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;services 아래 만들어지는 service명들은 모두 소스코드에서 하드코딩 될 수 있으며, 도커가 알아서 변환해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# docker-compose.yaml

#  version속성은 이전 버전과의 호환성을 위해 Compose 사양에 의해 정의됩니다.
version: &quot;3.8&quot;

# service의 하위로 컨테이너를 정의해야합니다.
services:
  mongodb:
    # 사용하려는 이미지 / 로컬 혹은 도커 이미지에서 조회됩니다.
    image: &quot;mongo&quot;
    volumes:
      # -v 플래그와 함께 사용한 것과 정확히 동일합니다.
      # 볼륨이름:/컨테이너 내부 경로 : read only
      - data:/data/db:ro
    environment:
      # 총 3가지 방식으로 지정이 가능합니다.
      Name: value
      env_file:
        - ./.env
      # - PASSWORD=SECRET
    networks:
      # 컴포즈가 모든 서비스를 즉시 네트워크에 추가합니다.

  backend:
    # 완성 이미지를 사용하는 대신, 이미지를 빌드하는데 필요한 모든 정보를 제공할 수 있습니다.
    # image: &quot;goals-node&quot;
    # Dockerfile 위치를 지정하면 찾아서 동작할 수 있습니다.
    # build: ./backend
    # Dockerfile이 여러 개라면 특정 파일을 지정할 수 있습니다.
    build:
      context: ./backend # context의 위치를 주의, 해당 위치만 참조합니다.
      dockerfile: Dockerfile
      args: # 빌드 인수 참조 가능
        - name=value
    ports:
      - &quot;80:80&quot; # 호스트 포트:내부포트 매핑
    volumes:
      - logs:/app/logs
      # 바인드 마운트
      - ./backend:/app
      # 익명 볼륨
      - /app/node_modules

    # 여러 컨테이너가 동시에 생길 때, 의존성을 해결하기 위함
    depends_on:
      - mongodb
      - ...

  frontend:
    # 인터렉티브 모드를 추가
    build: ./frontend
    ports:
      - &quot;3000:3000&quot;
    volumes:
      # 바인드마운트
      - ./frontend/src:/app/src
    # 인터렉티브 모드
    stdin_open: true
    tty: true # -it 플래그 / 터미널에 연결
    depends_on:
      - backend

# 네임드 볼륨을 사용하기 위해 생성해야합니다.
# 다른 서비스에서 동일한 볼륨 이름을 사용하면 볼륨은 공유됩니다.
volumes:
  data:
  logs:&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&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;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ docker-compose up
$ docker-compose down&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker-compose 명령 변형&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;$ docker-compose --build # build key가 존재한다면 이미지 빌드를 강제합니다. image key가 아님&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;service명은 컨테이너 이름 자체가 아닌, 컨테이너 이름의 일부분입니다.&lt;/li&gt;
&lt;li&gt;전체 이름은 현재 디렉토리와 여러 정보들끼리 복합적으로 만들어지며 컨테이너 이름을 지정하려고 하는 경우엔 container_name을 입력해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;services:
  mongodb:
    # 사용하려는 이미지 / 로컬 혹은 도커 이미지에서 조회됩니다.
    image: &quot;mongo&quot;
    volumes:
      # -v 플래그와 함께 사용한 것과 정확히 동일합니다.
      # 볼륨이름:/컨테이너 내부 경로 : read only
      - data:/data/db:ro
    environment:
      # 총 3가지 방식으로 지정이 가능합니다.
      Name: value
      env_file:
        - ./.env
      # - PASSWORD=SECRET
    networks:
      # 컴포즈가 모든 서비스를 즉시 네트워크에 추가합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/229</guid>
      <comments>https://choiblog.tistory.com/229#entry229comment</comments>
      <pubDate>Thu, 21 Mar 2024 22:21:29 +0900</pubDate>
    </item>
    <item>
      <title>Amazon Bedrock과 Langchain Workshop</title>
      <link>https://choiblog.tistory.com/228</link>
      <description>&lt;h1&gt;Generative AI on AWS&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Bedrock의 기능적 특징&lt;/h2&gt;
&lt;div&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;LangChain과 통합&lt;/li&gt;
&lt;li&gt;고객 데이터는 사용지않고 보안을 유지 가능&lt;/li&gt;
&lt;li&gt;Private Link 지원 &amp;rarr; TLS, 암호화, 보안까지 모두 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롬프트 엔지니어링 기법&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;AI가 수행해야하는 작업을 설명하는 자연어 텍스트를 프롬프트라고 합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;프롬프트에 따라 성능이 달라지며, 경험적 과학입니다.&lt;/div&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;div&gt;Prompt Engineering을 사용하여 개선 가능&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Human Assistant&lt;/li&gt;
&lt;li&gt;명확하고 구체적인 작업 지시가 필요&lt;/li&gt;
&lt;li&gt;XML 태그를 자주 사용할 것&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;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAG&lt;/h2&gt;
&lt;div&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;div&gt;모델 성능을 높히기 위한 한 가지 방법으로 도메인에 최적화된 LLM 모델을 만들 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;prompt 엔지니어링&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;RAG &amp;rarr; 파운데이션 모델에 모댈 자료 입력&lt;/div&gt;
&lt;div&gt;사전훈련된 파운데이션 모델 파인튜닝&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;데이터 추출, 청크 단위로 분할 &amp;rarr; 벡터 임베딩 생성 &amp;rarr; 데이터 적재 &amp;rarr; 데이터 활용&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;hands-on&lt;/h1&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;boto3&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;bedrock_model_id = &quot;anthropic.claude-v2:1&quot; #파운데이션 모델 설정

prompt = &quot;Human:뉴햄프셔에서 가장 큰 도시가 어디인가요? Assistant:&quot; #모델에 보낼 프롬프트 설정

body = json.dumps({
    &quot;prompt&quot;: prompt, #claude
    &quot;max_tokens_to_sample&quot;: 1024, 
    &quot;temperature&quot;: 0, 
    &quot;top_p&quot;: 0.5, 
    &quot;stop_sequences&quot;: [], # 추론 결과와 관련되어 있습니다.
}) #요청 payload 설정&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Langchain&lt;/h2&gt;
&lt;div&gt;이전 실습에서 Boto3를 통해 Amazon Bedrock을 호출했던 것과 기능은 비슷하지만, 이번 실습에서는 두 가지 접근 방식을 비교할 수 있도록 LangChain을 사용합니다. 특히 텍스트 입력 및 텍스트 출력에 집중하고 싶을 때 LangChain을 사용하면, Boto3 클라이언트 사용의 많은 세부 사항을 추상화할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Boto3 클라이언트가 제공하는 모든 제어 기능이 필요한 경우, 언제든지 선택하실 수 있습니다. Boto3를 사용할 경우 더 많은 코드가 필요할 수 있지만, JSON 요청 및 응답 객체에 대한 전체 액세스 권한을 제공받게 됩니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import os
from langchain.llms.bedrock import Bedrock

llm = Bedrock( #Bedrock llm 클라이언트 생성
    credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), #AWS 자격 증명에 사용할 프로필 이름 설정 (기본값이 아닌 경우)
    region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), #리전 설정 (기본값이 아닌 경우)
    endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #엔드포인트 URL 설정 (필요한 경우)
    model_id=&quot;anthropic.claude-v2:1&quot; #파운데이션 모델 설정
)

prompt = &quot;Human:대한민국에서 가장 큰 도시가 어디인가요? 한 문장으로 말해주세요. Assistant:&quot; 

response_text = llm.predict(prompt) #프롬프트에 응답 반환

print(response_text)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추론 매개변수&lt;/h2&gt;
&lt;div&gt;모듈마다 달라질 수 있으니 주의할 것&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import sys
import os
from langchain.llms.bedrock import Bedrock

# 추론 매개변수를 가져옴
def get_inference_parameters(model): #모델의 공급자에 따라 기본 매개변수 집합 반환
    bedrock_model_provider = model.split('.')[0] #모델 ID의 첫 번째 부분에서 모델 공급자 가져오기
    
    if (bedrock_model_provider == 'anthropic'): #Anthropic 모델
        return { #anthropic
            &quot;max_tokens_to_sample&quot;: 512,
            &quot;temperature&quot;: 0, 
            &quot;top_k&quot;: 250, 
            &quot;top_p&quot;: 1, 
            &quot;stop_sequences&quot;: [&quot;\n\nHuman:&quot;] 
           }
    
    elif (bedrock_model_provider == 'ai21'): #AI21 모델
        return { #AI21
            &quot;maxTokens&quot;: 512, 
            &quot;temperature&quot;: 0, 
            &quot;topP&quot;: 0.5, 
            &quot;stopSequences&quot;: [], 
            &quot;countPenalty&quot;: {&quot;scale&quot;: 0 }, 
            &quot;presencePenalty&quot;: {&quot;scale&quot;: 0 }, 
            &quot;frequencyPenalty&quot;: {&quot;scale&quot;: 0 } 
           }
    
    elif (bedrock_model_provider == 'cohere'): #COHERE 모델
        return {
            &quot;max_tokens&quot;: 512,
            &quot;temperature&quot;: 0,
            &quot;p&quot;: 0.01,
            &quot;k&quot;: 0,
            &quot;stop_sequences&quot;: [],
            &quot;return_likelihoods&quot;: &quot;NONE&quot;
        }
    
    else: #Amazon
        #LangChain Bedrock 구현의 경우, 이러한 매개변수는 LangChain이 생성하는 textGenerationConfig 구성항목에 추가됩니다.
        return { 
            &quot;maxTokenCount&quot;: 512, 
            &quot;stopSequences&quot;: [], 
            &quot;temperature&quot;: 0, 
            &quot;topP&quot;: 0.9 
        }


def get_text_response(model, input_content): #text-to-text 클라이언트 함수
    
    model_kwargs = get_inference_parameters(model) #선택한 모델에 따른 기본 매개변수 할당
    
    llm = Bedrock( #Bedrock llm 클라이언트 생성
        credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), #AWS 자격 증명에 사용할 프로필 이름 설정 (기본값이 아닌 경우)
        region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), #리전 설정 (기본값이 아닌 경우)
        endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #엔드포인트 URL 설정 (필요한 경우)
        model_id=model,
        model_kwargs = model_kwargs
    )
    
    return llm.predict(input_content) #프롬프트에 대한 응답 반환

response = get_text_response(sys.argv[1], sys.argv[2])

print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;응답 가변성 제어&lt;/h2&gt;
&lt;div&gt;temperature에 따른 response 차이&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import sys
import os
from langchain.llms.bedrock import Bedrock

def get_text_response(input_content, temperature): #text-to-text client 함수
    
    model_kwargs = {
        &quot;max_tokens_to_sample&quot;: 1024, 
        &quot;temperature&quot;: temperature, 
        &quot;top_p&quot;: 0.5, 
        &quot;stop_sequences&quot;: [], 
    }
    
    llm = Bedrock( #Bedrock llm 클라이언트 생성
    credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), #AWS 자격 증명에 사용할 프로필 이름 설정 (기본값이 아닌 경우)
    region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), #리전 설정 (기본값이 아닌 경우)
    endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #엔드포인트 URL 설정 (필요한 경우)
    model_id=&quot;anthropic.claude-v2:1&quot;, #파운데이션 모델 설정
    model_kwargs = model_kwargs
    )
    
    return llm.predict(input_content) #프롬프트에 대한 응답 반환

for i in range(3):
    response = get_text_response(sys.argv[1], float(sys.argv[2]))
    print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스트리밍 데이터&lt;/h2&gt;
&lt;div&gt;유저들에게 빠른 응답을 위해 스트리밍 방식으로 사용됨.&lt;/div&gt;
&lt;div&gt;한 번에 모든 텍스트를 전달하는게 아닌 잘라서 리턴&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import os
import json
import boto3

session = boto3.Session(
    profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;)
) #AWS 자격 증명에 사용할 프로필 이름 설정

bedrock = session.client(
    service_name='bedrock-runtime', #Bedrock 클라이언트 생성
    region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;),
    endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;)
)

def chunk_handler(chunk):
    print(chunk, end='')

def get_streaming_response(prompt, streaming_callback):

    bedrock_model_id = &quot;anthropic.claude-v2&quot; #파운데이션 모델 설정

    body = json.dumps({
        &quot;prompt&quot;: prompt, #ANTHROPIC
        &quot;max_tokens_to_sample&quot;: 4000,
        &quot;temperature&quot;: 0, 
        &quot;top_k&quot;: 250, 
        &quot;top_p&quot;: 1, 
        &quot;stop_sequences&quot;: [&quot;\n\nHuman:&quot;] 
    })

    response = bedrock.invoke_model_with_response_stream(modelId=bedrock_model_id, body=body) #스트리밍 메서드 호출
    stream = response.get('body')
    if stream:
        for event in stream: #스트림에서 반환된 각 이벤트 처리
            chunk = event.get('chunk')
            if chunk:
                chunk_json = json.loads(chunk.get('bytes').decode())
                streaming_callback(chunk_json[&quot;completion&quot;]) #콜백 메서드에 최신 청크의 텍스트 전달

prompt = &quot;\n\nHuman:가장 친한 친구가 된 강아지 두 마리와 새끼 고양이 두 마리에 대한 이야기를 들려주세요.\n\nAssistant:&quot;
                
get_streaming_response(prompt, chunk_handler)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;임베딩&lt;/h2&gt;
&lt;div&gt;텍스트 데이터를 벡터로 만들 때 사용합니다.&lt;/div&gt;
&lt;div&gt;벡터로 만드는 이유는 문장 간 유사도를 검색하기 위함이며, 연산 자체를 빠르게 진행할 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from langchain.embeddings import BedrockEmbeddings
from numpy import dot
from numpy.linalg import norm # 정규화를 위해 임포트 하였습니다.

#Bedrock Embeddings LangChain 클라이언트 생성
belc = BedrockEmbeddings()

class EmbedItem:
    def __init__(self, text):
        self.text = text
        self.embedding = belc.embed_query(text)

class ComparisonResult:
    def __init__(self, text, similarity):
        self.text = text
        self.similarity = similarity

def calculate_similarity(a, b): # 코사인 유사도를 확인하세요: https://en.wikipedia.org/wiki/Cosine_similarity
    return dot(a, b) / (norm(a) * norm(b))

#비교할 임베딩 목록을 작성합니다.
#다양한 언어로 구성된 문장에 유사도를 비교
items = []

with open(&quot;items.txt&quot;, &quot;r&quot;) as f:
    text_items = f.read().splitlines()

for text in text_items:
    items.append(EmbedItem(text))


for e1 in items:
    print(f&quot;Closest matches for '{e1.text}'&quot;)
    print (&quot;----------------&quot;)
    cosine_comparisons = []
    
    for e2 in items:
        similarity_score = calculate_similarity(e1.embedding, e2.embedding)
        
        cosine_comparisons.append(ComparisonResult(e2.text, similarity_score)) # 비교 내용을 목록에 저장합니다.
        
    cosine_comparisons.sort(key=lambda x: x.similarity, reverse=True) # 가장 가까운 일치 항목을 먼저 나열합니다.
    
    for c in cosine_comparisons:
        print(&quot;%.6f&quot; % c.similarity, &quot;\t&quot;, c.text)
    
    print()&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Closest matches for 'Can you please tell me how to get to the bakery?'
----------------
1.000000         Can you please tell me how to get to the bakery?
0.712236         I need directions to the bread shop
0.541959         Pouvez-vous s'il vous pla&amp;icirc;t me dire comment me rendre &amp;agrave; la boulangerie?
0.492384         빵집으로 가는 길을 알려주세요.
0.484672         Can you please tell me how to get to the stadium?
0.455479         パン屋への行き方を教えてください
0.406388         パン屋への道順を知りたい
0.369163         Kannst du mir bitte sagen, wie ich zur B&amp;auml;ckerei komme?
0.238000         경기장 가는 방법을 알려주시겠어요?
0.109972         고양이, 개, 쥐
0.078357         猫、犬、ネズミ
0.022138         Cats, dogs, and mice
0.015661         Lions, tigers, and bears
0.005211         Chats, chiens et souris
-0.007595        Felines, canines, and rodents&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Streamlit&lt;/h2&gt;
&lt;div&gt;streamlit docs 참고&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검색 증강 생성 (RAG)&lt;/h2&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;Question과 유사도를 통해 가져온 컨텍스트를 파운데이션 모델에 넣어 response를 얻습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import os
from langchain.embeddings import BedrockEmbeddings
from langchain.indexes import VectorstoreIndexCreator
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader
from langchain.llms.bedrock import Bedrock
#임베딩, 인덱스, 벡터스토어, 텍스트 청크를 위한 스플리터, pdf에서 추출을 위한 pdf loader, bedrock 클래스

def get_llm():
    
    model_kwargs = { #Anthropic Cloude v2.1
        &quot;max_tokens_to_sample&quot;: 1024, 
        &quot;temperature&quot;: 0, 
        &quot;top_p&quot;: 0.5, 
    }
    
    llm = Bedrock(
        credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), # AWS 자격 증명에 사용할 프로필 이름 설정(기본값이 아닌 경우)
        region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), # 리전 이름 설정(기본값이 아닌 경우)
        endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #endpoint URL 설정(필요한 경우)
        model_id=&quot;anthropic.claude-v2:1&quot;, #파운데이션 모델 설정
        model_kwargs=model_kwargs) #Claude에 대한 속성
    
    return llm

def get_index(): #애플리케이션에서 사용할 인메모리 벡터 저장소를 생성하고 반환합니다
    
    embeddings = BedrockEmbeddings(
        credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), #기본값이 아닌 경우 AWS 자격 증명에 사용할 프로필 이름을 설정합니다
        region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), #리전 이름 설정(기본값이 아닌 경우)
        endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #endpoint URL 설정(필요한 경우)
    ) #Titan Embeddings 클라이언트 생성
    
    pdf_path = &quot;2022-Shareholder-Letter-ko.pdf&quot; #로컬 PDF 파일 (2022년 주주 서한 - 한글)

    loader = PyPDFLoader(file_path=pdf_path) #pdf 파일을 로드합니다
    
    #텍스트 청크화.
    # overlap은 이전 청크와 겹쳐 만든다.
    text_splitter = RecursiveCharacterTextSplitter( #텍스트 분할기를 만듭니다
        chunk_size=1000, #구분 기호를 사용하여 1000자 청크로 분할
        chunk_overlap=100 #이전 청크와 겹칠 수 있는 문자 수
    )
    
    # 벡터 스토어(FAISS는 인메모리 벡터 DB)
    index_creator = VectorstoreIndexCreator( #벡터 스토어 팩토리 생성
        vectorstore_cls=FAISS, #데모 목적으로 인메모리 벡터 스토어 사용
        embedding=embeddings, #Titan Embeddings 사용
        text_splitter=text_splitter, #재귀적 텍스트 분할기 사용
    )
    
    # pdf에서 벡터 스토어 인덱스들이 생성되어 반환됩니다.
    index_from_loader = index_creator.from_loaders([loader]) #로드된 PDF에서 벡터 스토어 인덱스 생성
    
    return index_from_loader #클라이언트 앱에서 캐시할 인덱스 반환

def get_rag_response(index, question): #rag client 함수
    
    llm = get_llm()
    
    response_text = index.query(question=question, llm=llm) #인메모리 인덱스에 대해 검색하고 결과를 프롬프트에 채워서 llm으로 전송
    
    return response_text&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAG를 사용한 챗봇&lt;/h2&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;/li&gt;
&lt;li&gt;타이탄 임베딩을 사용하여 질문을 벡터로 변환한 다음 벡터 데이터베이스에서 가장 가까운 벡터와 매치합니다.&lt;/li&gt;
&lt;li&gt;결합된 기록, 지식 및 새 메시지가 모델로 전송됩니다.&lt;/li&gt;
&lt;li&gt;모델의 응답이 사용자에게 표시됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&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;/h2&gt;
&lt;div&gt;큰 문서를 작은 청크 단위로 나누고 중간 요약을 만듭니다. 이렇게 만들어진 중간 요약들을 다시 결합하여 요약된 결과를 리턴합니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import os
from langchain.prompts import PromptTemplate
from langchain.llms.bedrock import Bedrock

# 문서 요약
from langchain.chains.summarize import load_summarize_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader

def get_llm():
    
    model_kwargs =  { #Anthropic 모델
        &quot;max_tokens_to_sample&quot;: 8000,
        &quot;temperature&quot;: 0, 
        &quot;top_k&quot;: 250, 
        &quot;top_p&quot;: 0.5, 
        &quot;stop_sequences&quot;: [&quot;\n\nHuman:&quot;] 
    }
    
    llm = Bedrock(
        credentials_profile_name=os.environ.get(&quot;BWB_PROFILE_NAME&quot;), #AWS 자격 증명에 사용할 프로필 이름을 설정합니다(기본값이 아닌 경우)
        region_name=os.environ.get(&quot;BWB_REGION_NAME&quot;), #리전 이름을 설정합니다(기본값이 아닌 경우)
        endpoint_url=os.environ.get(&quot;BWB_ENDPOINT_URL&quot;), #엔드포인트 URL 설정(필요한 경우)
        model_id=&quot;anthropic.claude-v2:1&quot;, #파운데이션 모델 설정하기
        model_kwargs=model_kwargs) #Claude의 속성을 구성합니다.
    
    return llm


pdf_path = &quot;uploaded_file.pdf&quot;

def get_example_file_bytes(): #사용자가 기존 생성된 예제를 다운로드할 수 있도록 파일 바이트를 제공합니다.
    with open(&quot;2022-Shareholder-Letter-ko.pdf&quot;, &quot;rb&quot;) as file:
        file_bytes = file.read()
    
    return file_bytes


def save_file(file_bytes): #업로드한 파일을 디스크에 저장하여 나중에 요약합니다.   
    with open(pdf_path, &quot;wb&quot;) as f: 
        f.write(file_bytes)
    
    return f&quot;Saved {pdf_path}&quot;


def get_docs():
    
    loader = PyPDFLoader(file_path=pdf_path)
    documents = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(
        separators=[&quot;\n\n&quot;, &quot;\n&quot;, &quot;.&quot;, &quot; &quot;], chunk_size=4000, chunk_overlap=100 
    )
    docs = text_splitter.split_documents(documents=documents)
    
    return docs

def get_summary(return_intermediate_steps=False):
    
    map_prompt_template = &quot;{text}\n\n위의 내용을 Korean으로 bullet point 3개로 요약합니다:&quot;
    map_prompt = PromptTemplate(template=map_prompt_template, input_variables=[&quot;text&quot;])
    
    combine_prompt_template = &quot;{text}\n\n위의 내용을 Korean으로 간결하게 bullet point 5개로 요약합니다:&quot;
    combine_prompt = PromptTemplate(template=combine_prompt_template, input_variables=[&quot;text&quot;])
    
    llm = get_llm()
    docs = get_docs()
    
    chain = load_summarize_chain(llm, chain_type=&quot;map_reduce&quot;, map_prompt=map_prompt, combine_prompt=combine_prompt, return_intermediate_steps=return_intermediate_steps,verbose=True)
    
    if return_intermediate_steps:
        return chain({&quot;input_documents&quot;: docs}, return_only_outputs=True) #반환 구조를 chain.run(docs)와 일관성 있게 만들기
    else:
        return chain.run(docs)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;import streamlit as st
import summarization_lib_kr as glib

st.set_page_config(layout=&quot;wide&quot;, page_title=&quot;문서 요약&quot;)
st.title(&quot;문서 요약&quot;)

uploaded_file = st.file_uploader(&quot;Select a PDF&quot;, type=['pdf'])

upload_button = st.button(&quot;Upload&quot;, type=&quot;primary&quot;)

if upload_button:
    with st.spinner(&quot;Uploading...&quot;):

        upload_response = glib.save_file(file_bytes=uploaded_file.getvalue())

        st.success(upload_response)
        
        st.session_state.has_document = True

if 'has_document' in st.session_state: #문서가 업로드되었는지 확인하기
    
    return_intermediate_steps = st.checkbox(&quot;중간 단계 요약 보기&quot;, value=True)
    summarize_button = st.button(&quot;요약하기&quot;, type=&quot;primary&quot;)
    
    
    if summarize_button:
        st.subheader(&quot;통합 요약&quot;)

        with st.spinner(&quot;Running...&quot;):
            response_content = glib.get_summary(return_intermediate_steps=return_intermediate_steps)


        if return_intermediate_steps:

            st.write(response_content[&quot;output_text&quot;])

            st.subheader(&quot;중간 단계 요약&quot;)

            for step in response_content[&quot;intermediate_steps&quot;]:
                st.write(step)
                st.markdown(&quot;---&quot;)

        else:
            st.write(response_content)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;응답 스트리밍&lt;/h2&gt;
&lt;div&gt;모든 내용을 한 번에 반환하는 게 아닌 중간 중간 계속해서 응답을 반환합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;임베딩 검색&lt;/h2&gt;
&lt;div&gt;실제 시나리오에서는 OpenSearch Serverless를 사용해서 배포합니다.&lt;/div&gt;
&lt;div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;문서가 텍스트 청크로 분할됩니다. 이 청크는 Titan Embedding으로 전달되어 벡터로 변환됩니다. 그런 다음 벡터는 벡터 데이터베이스에 저장됩니다.&lt;/li&gt;
&lt;li&gt;사용자가 질문을 제출합니다.&lt;/li&gt;
&lt;li&gt;질문은 Amazon Titan Embeddin을 사용하여 벡터로 변환된 다음 벡터 데이터베이스에서 가장 가까운 벡터와 매칭됩니다.&lt;/li&gt;
&lt;li&gt;일치하는 벡터에서 결합된 콘텐츠가 사용자에게 반환됩니다.&lt;/li&gt;
&lt;/ol&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 컨텐츠를 임베딩시켜 두었다가 컨텐츠에 대한 요청이 들어왔을 때 BedRock을 통해 개인화된 추천을 해주는 방식입니다.&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;Text to JSON 데이터 추출하기&lt;/h2&gt;
&lt;div&gt;고객과 대화하면서 감정 분석을 위해 사용합니다.&lt;/div&gt;
&lt;div&gt;또한 콜센터에서 상담을 하며 요약할 때 고객의 정보와 CS 내용 등을 분석하고 정리할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Text to CSV 데이터 추출하기&lt;/h2&gt;
&lt;div&gt;데이터 전처리 혹은 비정혀 데이터를 처리하는데 사용합니다.&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;프롬프트 엔지니어링&lt;/h1&gt;
&lt;div&gt;&lt;b&gt;프롬프트 엔지니어링&lt;/b&gt;&amp;nbsp;이란 사용자의 요청에 대한 파운데이션 모델의 응답 품질과 성능을 최적화하는 작업입니다.
&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;b&gt;few-shot learning&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;줄 바꿈 및 콘텐츠 구분 기호 사용&lt;/li&gt;
&lt;li&gt;모델이 학습했던 방식과 일치하는 정해진 형식 따르기&lt;/li&gt;
&lt;li&gt;모델이 텍스트 생성을 중단해야 하는 시점을 알 수 있도록&amp;nbsp;&lt;b&gt;stop sequences&lt;/b&gt;&amp;nbsp;사용&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롬프트 엔지니어링 사용 사례&lt;/h2&gt;
&lt;/div&gt;
&lt;div&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;콘텐츠에 대한 간략한 설명&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;
&lt;li&gt;이메일 회신&lt;/li&gt;
&lt;li&gt;권장 조치&lt;/li&gt;
&lt;li&gt;키워드 추출&lt;/li&gt;
&lt;li&gt;몇 가지 예제를 제공(Few-shot prompting)&lt;/li&gt;
&lt;li&gt;Human: / Assistant:&lt;/li&gt;
&lt;li&gt;서문 제거하기&lt;/li&gt;
&lt;li&gt;서문 건너뛰기&lt;/li&gt;
&lt;li&gt;XML 태그에 출력&lt;/li&gt;
&lt;/ul&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;/div&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/228</guid>
      <comments>https://choiblog.tistory.com/228#entry228comment</comments>
      <pubDate>Thu, 21 Mar 2024 20:21:51 +0900</pubDate>
    </item>
    <item>
      <title>Docker network</title>
      <link>https://choiblog.tistory.com/227</link>
      <description>&lt;div&gt;컨테이너 내부에서 네트워크를 사용하여 다수의 컨테이너를 연결하고 실행 중인 애플리케이션을 로컬 호스트 머신에 연결해야합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;⁠Containers &amp;amp; Network Requests&lt;/h1&gt;
&lt;div&gt;컨테이너가 통신하는 대상은 여러 종류의 대상이 있을 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너 내부에서 외부 인터넷으로 요청을 전송하는 경우&lt;/h2&gt;
&lt;div&gt;특별한 설정없이 바로 작동할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너와 로컬 호스트 머신 간 통신하는 경우&lt;/h2&gt;
&lt;div&gt;&lt;a&gt;mongodb://host.docker.internal:27017/swfavorites⁠&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 도메인을 통해 도커 컨테이너 내부에서 알 수 있는 방식으로 호스트 머신의 IP주소로 변환됩니다.&lt;/li&gt;
&lt;li&gt;도메인을 작성하면 도커가 알아서 변환해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;host.docker.internal⁠&lt;/b&gt;을 URL, 즉 도메인으로 사용해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너간 통신하는 경우&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;두 컨테이너는 일반적으로 통신할 수 없습니다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/_/mongo&quot;&gt;https://hub.docker.com/_/mongo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;아래 명령을 사용하면 도커&amp;nbsp;IPAddress를 찾을 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;docker container inspect &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;이렇게 찾아낸 IP address를 등록할 수 있지만&amp;nbsp;&lt;b&gt;mongodb://&amp;lt;your_ip_address&amp;gt;:27017/swfavorites&amp;nbsp;mongodb&lt;/b&gt; 컨테이너를 업데이트하거나 ip 주소가 변경될 때마다 재빌드해야한다는 단점이 존재합니다. 따라서 이상적인 방식은 아닙니다.&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Container Networks&lt;/h3&gt;
&lt;div&gt;다중 컨테이너가 있을 때 컨테이너간의 통신을 허용하는 것을 의미합니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker run --network &amp;lt;network-name&amp;gt;&lt;/b&gt;를 통해 모든 컨테이너를 하나의 동일한 네트워크에 밀어넣을 수 있습니다.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;모든 컨테이너간 통신을 허용하며 IP 조회 및 해결 작업을 자동으로 수행합니다. 고유한 임무와 작업을 가진 다수의 격리된 컨테이너를 가지게 만들 수 있습니다.&lt;/div&gt;
&lt;div&gt;볼륨과 달리 네트워크는 도커에서 만들지않으므로 직접 만들어야합니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network create &amp;lt;network_name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;⁠&lt;/div&gt;
&lt;div&gt;이렇게 동일한 네트워크로 연결하고 나면 도메인 url에 컨테이너 이름으로 지정할 수 있게 됩니다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;mongodb://&amp;lt;your_container_name&amp;gt;:27017/swfavorites&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;자동 생성된 이름이 있다면 해당 이름을 도메인으로 지정할 수 있습니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;앞서&amp;nbsp;&lt;b&gt;mongodb://host.docker.internal:27017/swfavorites&lt;/b&gt;⁠로 작성했던 것처럼 다른 도커 컨테이너 또한 같은 네트워크로 묶여있다면 컨테이너 이름이 도커에 의해 해당 컨테이너의 IP 주소로 변경됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이때 또 주목해야할 점은 mongoDB를 컨테이너로 실행시킬 때&amp;nbsp;-p&amp;nbsp;옵션을 통해 포트를 노출시키지 않았다는 점입니다. 로컬 호스트 머신이나 컨테이너 네트워크의 외부에서 접근할 수 없도록 폐쇄적이기 때문이고, 무언가를 연결시킬 경우에만 필요하기 때문입니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;유일하게 연결되는 부분이 같은 네트워크의 공개된 포트를 가지고 있는 컨테이너이고, 해당 컨테이너와 내부적으로 연결은 되어 공개된 컨테이너를 통해 내부 컨테이너와 통신할 수 있게 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;Docker Network IP Resolving&lt;/h1&gt;
&lt;div&gt;실제로 컨테이너 이름을 도메인 혹은 주소로 사용하게 되면, 도커가 IP 주소를 자동으로 해결해줍니다.&lt;/div&gt;
&lt;div&gt;소스 코드를 내부적으로 교체하는 것이 아닌 컨테이너의 이름을 보고 컨테이너 실행을 관리하며 플러그인된 컨테이너의 IP 주소를 연결하는 방식입니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;단순히 도커가 애플리케이션이 실행되는 환경을 가지고 인식하여 조절하는 방식입니다.&lt;/div&gt;
&lt;div&gt;따라서 컨테이너에 요청이 전달될 때만 IP 변환이 발생하며 요청이 다른 곳에서 생성됐다면 아무런 작업도 수행하지 않습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Docker Networks는 실제로 네트워크 동작에 영향을 미치는 다양한 종류의 '&lt;b&gt;드라이버&lt;/b&gt;'를 지원합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;디폴트 드라이버는 '&lt;b&gt;bridge&lt;/b&gt;' 드라이버입니다. 이 드라이버는 모듈에 나타난 동작을 제공합니다 (즉, 컨테이너가 동일한 네트워크에 있는 경우, 이름으로 서로를 찾을 수 있음).&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;드라이버는 네트워크 생성 시 '--driver' 옵션을 추가하여 간단히 설정할 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network create --driver bridge my-net&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;물론 'bridge' 드라이버를 사용하고자 하는 경우, 'bridge'가 디폴트이므로, 전체 옵션을 생략하면 됩니다.&lt;/div&gt;
&lt;div&gt;Docker는 아래의 대체 드라이버도 지원하지만 대부분의 경우 'bridge' 드라이버를 사용합니다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;host&lt;/b&gt;: 스탠드얼론 컨테이너의 경우, 컨테이너와 호스트 시스템 간의 격리가 제거됩니다 (즉, localhost를 네트워크로 공유함).&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;overlay&lt;/b&gt;: 여러 Docker 데몬 (즉, 서로 다른 머신에서 실행되는 Docker)이 서로 연결될 수 있습니다. 여러 컨테이너를 연결하는&amp;nbsp;&lt;b&gt;구식의 / 거의 사용되지 않는 방법인 'Swarm' 모드에서만 작동합니다.&lt;/b&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;macvlan&lt;/b&gt;: 컨테이너에 커스텀 MAC 주소를 설정할 수 있습니다. 그러면 이 주소를 해당 컨테이너와 통신하는데 사용할 수 있습니다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;none&lt;/b&gt;: 모든 네트워킹이 비활성화됩니다.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;b&gt;써드파티 플러그인&lt;/b&gt;: 모든 종류의 동작과 기능을 추가할 수 있는 타사 플러그인을 설치할 수 있습니다.&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fedede; color: #404040;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fedede; color: #404040;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;background-color: #fedede; color: #404040;&quot;&gt;bridge&lt;/span&gt;&lt;span style=&quot;background-color: #fedede; color: #404040;&quot;&gt;' 드라이버는 대부분의 시나리오에 가장 적합합니다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/227</guid>
      <comments>https://choiblog.tistory.com/227#entry227comment</comments>
      <pubDate>Wed, 20 Mar 2024 22:05:22 +0900</pubDate>
    </item>
    <item>
      <title>Docker ARG / ENV Variables(인수와 환경변수)</title>
      <link>https://choiblog.tistory.com/226</link>
      <description>&lt;h1&gt;ARG&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile에서 특정 Dockerfile 명령으로 다른 값을 추출하는데 사용할 수 있는 유연한 데이터 비트, 인수를 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h1&gt;ENV&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile 내부의&amp;nbsp;ENV옵션으로 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;이러한 방식을 통해 하드 코딩할 필요 없이 유연한 이미지와 컨테이너를 만들 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot; data-code-language=&quot;yaml&quot;&gt;&lt;code&gt;ENV PORT 80

# docker에게 $ 뒤에 있는 변수가 환경 변수임을 알려야합니다.
# 그래야만 PORT 환경 변수를 찾고 거기에 저장된 값을 호출합니다.
EXPORT $PORT&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker run ⁠--env PORT=8000
# or
docker run ... --env-file ./.env&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;&lt;b&gt;환경 변수(ENV) 및 보안&lt;/b&gt;에 대한 한 가지 중요한 참고사항&lt;/h1&gt;
&lt;div&gt;환경 변수에 저장하는 데이터의 종류에 따라, 보안 데이터를&amp;nbsp;Dockerfile에 직접 포함하고 싶지 않을 수도 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;그 대신 런타임에만 사용되는 별도의 환경 변수 파일로 이동시키죠. (즉,&amp;nbsp;Docker run으로 컨테이너를 실행할 때).&lt;/div&gt;
&lt;div&gt;그렇지 않으면, 값이 '이미지에 포함'되며, 모든 이가 'docker history &amp;lt;이미지&amp;gt;'를 통해, 이 값을 읽을 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;일부 값의 경우, 이것이 중요하지 않을 수도 있지만, 자격 증명, 개인 키 등의 경우에는 확실히 피해야만 합니다!&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;별도의 파일을 사용하는 경우, 'docker run'을 실행할 때 그 파일을 가리키므로, 그 값은 이미지의 일부분이 아닙니다. 하지만 소스 컨트롤을 사용하는 경우, 별도의 파일을 소스 컨트롤 저장소의 일부분으로 커밋하지 않도록 조심하세요&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;Build-time Arguments&lt;/h1&gt;
&lt;div&gt;도커 파일에 다양한 값을 플러그인하거나 도커 파일에 하드 코딩하지 않고도 이미지를 빌드할 때 다른 값을 플러그인할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# Dockerfile에서 사용할 수 있는 변수입니다.
ARG DEFAULT_PORT = 80
...
ENV PORT $DEFAULT_PORT&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;해당 ARG도 레이어를 생성하므로 만약 값이 수정된다면 모든 후속 레이어가 리빌드 되고, 재실행 됩니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;모든 후속 명령이 재실행되므로 아래에 두는 것이 적합합니다.&lt;/div&gt;
&lt;h1&gt;Module 요약⁠&lt;/h1&gt;
&lt;div&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;볼륨은 도커 컨테이너에 의해 마운트되며 도커가 관리합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네임드 볼륨: 컨테이너가 제거되도 남아있으며 데이터를 영구적으로 저장하는 경우 사용&lt;/li&gt;
&lt;li&gt;익명 볼륨 : 컨테이너에 연결되며 컨테이너가 제거되면 사라집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;바인드 마운트 : 호스트 머신의 로컬 폴더를 매핑할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 머신의 경로를 통해 유저가 직접 관리할 수 있느냐에 따라 네임드 볼륨과의 차이가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/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;외부에서 특정 데이터를 전달할 수 있고, 모든 것을 하드 코딩할 필요가 없습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/226</guid>
      <comments>https://choiblog.tistory.com/226#entry226comment</comments>
      <pubDate>Mon, 18 Mar 2024 21:28:45 +0900</pubDate>
    </item>
    <item>
      <title>이미지와 컨테이너</title>
      <link>https://choiblog.tistory.com/225</link>
      <description>&lt;div&gt;
&lt;h1&gt;이미지와 컨테이너란&lt;/h1&gt;
&lt;div&gt;&lt;b&gt;컨테이너&lt;/b&gt;&lt;/div&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;div&gt;결국 컨테이너에는 소프트웨어 실행 유닛이 존재하고 우리는 그것을 단순히 실행시키는 것&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;이미지&lt;/b&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dissolver로써 템플릿으로써 컨테이너의 블루 프린트로 활용&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;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;즉 이미지는 모든 설정 명령과 모든 코드가 포함된 공유 가능한 패키지이며 컨테이너는 그러한 이미지의 구체적인 실행 인스턴스이다. 이미지를 기반으로 컨테이너를 실행하는게 핵심&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;이미지를 생성하고 가져오는 방법&lt;/h1&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;/ol&gt;
&lt;div&gt;Docker Hub 혹은 커뮤니티에서 만든 이미지를 가져와서 사용할 수 있습니다.&lt;/div&gt;
&lt;div&gt;공식 도커 이미지를 찾을 수 있습니다.&lt;/div&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSidg/btsEn1lbmbB/EdJWJO5Ge7Zwz3KqHp33pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSidg/btsEn1lbmbB/EdJWJO5Ge7Zwz3KqHp33pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSidg/btsEn1lbmbB/EdJWJO5Ge7Zwz3KqHp33pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSidg%2FbtsEn1lbmbB%2FEdJWJO5Ge7Zwz3KqHp33pk%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;693&quot; height=&quot;214&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;# 로컬에서 찾을 수 없다면 도커 허브에서 해당 이미지를 찾은 뒤 컨테이너를 만듭니다.
docker run node&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKMbMh/btsEmFXHMxF/J5YVVEC1A8B90w7wGJEId0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKMbMh/btsEmFXHMxF/J5YVVEC1A8B90w7wGJEId0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKMbMh/btsEmFXHMxF/J5YVVEC1A8B90w7wGJEId0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKMbMh%2FbtsEmFXHMxF%2FJ5YVVEC1A8B90w7wGJEId0%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;564&quot; height=&quot;247&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;기본적으로 컨테이너는 주변 환경과 격리되어 있기 때문에 내부에 실행 중인 인터렉티브 쉘이 있다고해서 사용자가 사용할 수 없습니다. 컨테이너에 의해 자동으로 노출되지 않습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS                     PORTS     NAMES
0ec115cf471f   node      &quot;docker-entrypoint.s&amp;hellip;&quot;   2 minutes ago   Exited (0) 2 minutes ago             determined_dirac&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하지만 도커에게 컨테이너 내부에서 대화형 세션을 사용하겠다는 것을 전달하면 사용가능합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;docker run -it node&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;해당 명령어를 입력하면 컨테이너에서 실행 중인 노드와 연결이 가능합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;다시 정리하면&amp;nbsp;&lt;b&gt;이미지는 컨테이너에 필요한 모든 논리와 모든 코드, 이것들을 실행하는 설정 값 등 실행과 관련된 여러가지를 포함하고 있고&lt;/b&gt;&amp;nbsp;run 명령을 사용하여 이미지를 기반으로 이미지의 인스턴스를 만듭니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;그렇게 만들어진 인스턴스는 컨테이너에 실려 사용됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Dockerfile을 사용한 자체 이미지&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;앞서 적용해봤던 공식 베이스 이미지를 기반으로 구축한 다음 그 위에 코드를 추가하여 커스텀한 이미지를 만들어서 사용합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Dockerfile로 자체 이미지를 생성하려고할 때 실행하고자하는 명령이 포함됩니다.&lt;/div&gt;
&lt;div&gt;따라서&amp;nbsp;Dockerfile에는 자체 이미지 구축에 대한 설정 명령이 포함되어 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이론적으로 도커 이미지를 처음부터 빌드할 수 있지만 코드에 필요한 기타 도구와 같은 운영 레이어가 필요합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Dockerfile을 기반으로 이미지를 생성하는 순간 이미지에 있는 설정들이 다운로드 되어 로컬에 저장됩니다.&lt;/div&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 도커가 node라는 이름의 이미지가 존재하며 그것을 찾을 수 있음
FROM node

# 작업 디렉토리를 생성하고 설정함
WORKDIR /app

# 첫 번째 경로는 이미지의 외부 경로로 이미지로 복사되어야하는 파일들이 있는 곳
# 두 번째 경로는 이미지 내부의 경로로 자체 내부 파일 시스템에 저장되는 곳(도커 컨테이너 내부에 존재)
COPY . /app

# 도커 컨테이너 및 이미지의 작업 디렉토리에서 실행됨(생성한 디렉토리로 이동해야함)
RUN npm install

# 해당 명령어는 이미지가 빌드되기만해도 실행됨
# 하지만 이미지는 컨테이너의 템플릿으로 활용되어함
# RUN node server.js

# 도커는 내부 환경과 자체적인 내부 네트워크로 격리되어 있기 때문에 포트를 노출시키지 않으면 내부에서만 수신 대기를 함
# 하지만 여기서 사용하는 EXPOSE는 단순히 문서/주석의 느낌이고 실제로는 -p를 통해 로컬 포트와 매칭시켜야 합니다.
EXPOSE 80

# 이미지를 기반으로 컨테이너가 실행될 때마다 컨테이너 node 명령어로 server.js파일을 실행하도록 명령
CMD [&quot;node&quot;, &quot;server.js&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;docker run -p &amp;lt;Local Port&amp;gt;:&amp;lt;Docker External Port&amp;gt; &amp;lt;image ID&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;-p&amp;nbsp;를 함으로써 도커에게 어떤 로컬 포트가 있는지 알려줄 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;rArr; 사용자 로컬 머신의 어떤 포트가 내부의 도커 특정 포트에 엑세스 할 수 있는지 알려줍니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;쉽게 도커의 노출된 포트와 내부 로컬 머신의 포트를 매핑시켜주는 역할&lt;/div&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 도커 이미지를 실행할 때 모든 이미지ID를 붙여넣을 필요 없이 부분만 붙여넣어도 실행됨

$ docker run -p 3000:80 sha256:af5c9e01fc071fb2a23cbe5c523fe738d14c24ca850d6583e5548bf9131fc91e
$ docker run -p 3000:80 sha256:af5c9e01fc07&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker container stop&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED              STATUS              PORTS                  NAMES    
58972211dc2a   af5c9e01fc07   &quot;docker-entrypoint.s&amp;hellip;&quot;   About a minute ago   Up About a minute   0.0.0.0:3000-&amp;gt;80/tcp   bold_chaum

$ docker stop bold_chaum
bold_chaum&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이미지 설정&lt;/h2&gt;
&lt;div&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;/ol&gt;
&lt;div&gt;소스 코드를 이미지에 복사하는데, 기본적으로 복사한 시점에서 소스 코드의 스냅샷을 생성합니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;따라서 업데이트된 소스 코드를 새 이미지로 복사하기 위해선 이미지를 다시 빌드 해야하며 이미지의 모든 것은 읽기 전용으로 사용됩니다.&amp;nbsp;&amp;nbsp;COPY . /app&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이렇게 이미지를 다시 빌드하게 되면 이미지는 완전히 새로운 이미지로 만들어지기 때문에 새로운 이미지 이름을 갖게 됩니다.&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;851&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6liJj/btsEkQZIgaX/FE9Y9k6tdEAqpemz4ZJkVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6liJj/btsEkQZIgaX/FE9Y9k6tdEAqpemz4ZJkVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6liJj/btsEkQZIgaX/FE9Y9k6tdEAqpemz4ZJkVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6liJj%2FbtsEkQZIgaX%2FFE9Y9k6tdEAqpemz4ZJkVK%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;851&quot; height=&quot;152&quot; data-origin-width=&quot;851&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;새로운 이미지는 정상적으로 반영되었습니다.&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MR075/btsEncgeeSB/62WI0zyR4cNmoMBNKPOp5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MR075/btsEncgeeSB/62WI0zyR4cNmoMBNKPOp5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MR075/btsEncgeeSB/62WI0zyR4cNmoMBNKPOp5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMR075%2FbtsEncgeeSB%2F62WI0zyR4cNmoMBNKPOp5K%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;682&quot; height=&quot;163&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&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;/ol&gt;
&lt;div&gt;이미지는 레이어 기반&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이미지를 빌드하거나 이미지를 다시 빌드할 때 변경된 부분의 명령과 그 이후의 모든 명령이 재평가됩니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;$ docker build .
[+] Building 33.4s (10/10) FINISHED                         docker:default
 =&amp;gt; [internal] load build definition from Dockerfile                  0.0s
 =&amp;gt; =&amp;gt; transferring dockerfile: 1.28kB                                0.0s
 =&amp;gt; [internal] load .dockerignore                                     0.0s
 =&amp;gt; =&amp;gt; transferring context: 2B                                       0.0s
 =&amp;gt; [internal] load metadata for docker.io/library/node:latest        2.5s
 =&amp;gt; [auth] library/node:pull token for registry-1.docker.io           0.0s
 =&amp;gt; [1/4] FROM docker.io/library/node@sha256:3af9f785cb8fc1a9c60a77  26.9s
 =&amp;gt; =&amp;gt; resolve docker.io/library/node@sha256:3af9f785cb8fc1a9c60a77c  0.0s
 =&amp;gt; =&amp;gt; sha256:3af9f785cb8fc1a9c60a77c7b31b1ba7f5c74a 1.21kB / 1.21kB  0.0s 
 =&amp;gt; =&amp;gt; sha256:8d6c41e6504f6b9d7a8c2a82803a4d158660ae 7.34kB / 7.34kB  0.0s 
 =&amp;gt; =&amp;gt; sha256:6a299ae9cfd996c1149a699d36cdaa76fa33 49.58MB / 49.58MB  3.5s 
 =&amp;gt; =&amp;gt; sha256:2cc34c90ae08e9a7447d01513af15a1f37193b 2.00kB / 2.00kB  0.0s 
 =&amp;gt; =&amp;gt; sha256:e08e8703b2fb5e50153f792f3192087d2697 24.05MB / 24.05MB  2.6s 
 =&amp;gt; =&amp;gt; sha256:68e92d11b04ec0fe48e60d59964704aca234 64.14MB / 64.14MB  3.8s 
 =&amp;gt; =&amp;gt; sha256:5b9fe7fef9befda786bc8e1dd1ae42ffd8 211.11MB / 211.11MB  9.3s 
 =&amp;gt; =&amp;gt; sha256:278d467c182fb935287ed6f4be3d44f8ac2714 3.37kB / 3.37kB  3.8s 
 =&amp;gt; =&amp;gt; extracting sha256:6a299ae9cfd996c1149a699d36cdaa76fa332c8e9d6  5.2s
 =&amp;gt; =&amp;gt; sha256:aab6430b55a2372670b8797fbe6eae0e3652 49.30MB / 49.30MB  6.7s
 =&amp;gt; =&amp;gt; sha256:b6f92f4f624059d4cf4ea96bc1f2512f74d63e 2.23MB / 2.23MB  4.2s
 =&amp;gt; =&amp;gt; sha256:2e24338062e4bb2910914b5ba45c6bad6985b6f31a 451B / 451B  4.5s 
 =&amp;gt; =&amp;gt; extracting sha256:e08e8703b2fb5e50153f792f3192087d26970d26280  0.8s 
 =&amp;gt; =&amp;gt; extracting sha256:68e92d11b04ec0fe48e60d59964704aca234084f87a  3.5s 
 =&amp;gt; =&amp;gt; extracting sha256:5b9fe7fef9befda786bc8e1dd1ae42ffd8b9c37a4cc  9.1s 
 =&amp;gt; =&amp;gt; extracting sha256:278d467c182fb935287ed6f4be3d44f8ac2714264bc  0.0s 
 =&amp;gt; =&amp;gt; extracting sha256:aab6430b55a2372670b8797fbe6eae0e36526282a1b  3.3s 
 =&amp;gt; =&amp;gt; extracting sha256:b6f92f4f624059d4cf4ea96bc1f2512f74d63ebabcc  0.1s 
 =&amp;gt; =&amp;gt; extracting sha256:2e24338062e4bb2910914b5ba45c6bad6985b6f31a6  0.0s 
 =&amp;gt; [internal] load build context                                     0.0s 
 =&amp;gt; =&amp;gt; transferring context: 2.40kB                                   0.0s 
 =&amp;gt; [2/4] WORKDIR /app                                                1.3s 
 =&amp;gt; [3/4] COPY . /app                                                 0.0s 
 =&amp;gt; [4/4] RUN npm install                                             2.2s 
 =&amp;gt; exporting to image                                                0.2s 
 =&amp;gt; =&amp;gt; exporting layers                                               0.2s 
 =&amp;gt; =&amp;gt; writing image sha256:7e90540ed7ec87a1c8ca0dd57f09d6026872cc57  0.0s 

What's Next?
  View a summary of image vulnerabilities and recommendations &amp;rarr; docker scout quickview&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CACHED&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt; =&amp;gt; CACHED [2/4] WORKDIR /app                                         0.0s 
 =&amp;gt; CACHED [3/4] COPY . /app                                          0.0s 
 =&amp;gt; CACHED [4/4] RUN npm install&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;동일한 작업 디렉토리에서 변경사항이 없으니 명령을 다시 실행될 필요가 없다는 것을 인지한 것입니다.&amp;nbsp;&lt;span&gt; 각 Dockerfile 명령이 이미지의 별도 레이어를 생성하며, 빌드 과정에서 Docker는 이러한 레이어를 캐시하여 재빌드 시 변경되지 않은 레이어에 대해 다시 실행할 필요가 없게 합니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;대신 이미지를 빌드할 때마다 도커는 모든 명령 결과를 캐시하고 이미지를 다시 빌드 할 때 명령을 다시 실행할 필요가 없다면 캐시된 결과를 사용합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이것을 레이어 기반 아키텍쳐라고 하며 Dockerfile의 모든 명령은 각각의 레이어를 나타냅니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;따라서 정리하면 이미지는 도커의 다양한 명령을 기반으로 여러 레이어를 읽기 전용으로 구성하며, 레이어를 생성하고 생덩된 레이어를 캐싱합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이미지를 기반으로 컨테이너를 실행하면 Dockerfile에 지정한 명령을 실행한 결과로 코드를 실행 중인 환경 위에 새로운 추가 레이어를 구성합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;그리고 도커 컨테이너를 실행할 때, 이 이미지 위에 새로운 가장 상위 레이어가 추가됩니다. 이 새로운 레이어는 &quot;쓰기 가능 레이어&quot;(writeable layer)라고 부르며, 컨테이너 내에서 발생하는 모든 파일 시스템의 변경사항(파일 추가, 수정, 삭제 등)을 저장합니다. 이렇게 구성함으로써, 실행 중인 컨테이너가 필요로 하는 데이터 변경을 가능하게 하면서도, 기반 이미지의 읽기 전용 레이어들은 변경되지 않고 그대로 유지됩니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;또한 각 컨테이너는 자체의 쓰기 가능 레이어를 가지지만 기반 이미지 레이어는 공유됩니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하지만 모든 레이어가 변경된 부분에 대한 세세한 판단을 하지 않기 때문에&amp;nbsp;하나의 레이어가 변경된다면 후속 모든 레이어도 변경되게 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;최적화(참고)&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;&lt;br /&gt;Node.js에서는 추가적인 패키지가 없다면 굳이&amp;nbsp; npm install을 실행할 필요가 없지만, 레이어의 특성 상 소스 코드가 수정되게 된다면, 캐시되어있어 필요없는 패키지 설치과정이 반복됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;따라서 최적화를 진행한다면 아래와 같이 먼저 package.json을 통해 패키지 정보를 가져오고 이상이 없다면 소스 코드의 변경사항을 입력하는 방식으로 최적화를 진행할 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 도커가 node라는 이름의 이미지가 존재하며 그것을 찾을 수 있음
FROM node

# 작업 디렉토리를 생성하고 설정함
WORKDIR /app

# package.json을 먼저 불러와 변경된 패키지가 있는지 확인
# 굳이 소스 코드만 변경되었음에도 패키지를 설치할 필요가 없음
COPY package.json /app

# 도커 컨테이너 및 이미지의 작업 디렉토리에서 실행됨(생성한 디렉토리로 이동해야함)
RUN npm install

# 첫 번째 경로는 이미지의 외부 경로로 이미지로 복사되어야하는 파일들이 있는 곳
# 두 번째 경로는 이미지 내부의 경로로 자체 내부 파일 시스템에 저장되는 곳(도커 컨테이너 내부에 존재)
COPY . /app

# 해당 명령어는 이미지가 빌드되기만해도 실행됨
# 하지만 이미지는 컨테이너의 템플릿으로 활용되어함
# RUN node server.js


# 도커는 내부 환경과 자체적인 내부 네트워크로 격리되어 있기 때문에 포트를 노출시키지 않으면 내부에서만 수신 대기를 함
# 하지만 여기서 사용하는 EXPOSE는 단순히 문서/주석의 느낌이고 실제로는 -p를 통해 로컬 포트와 매칭시켜야 합니다.
EXPOSE 80

# 이미지를 기반으로 컨테이너가 실행될 때마다 컨테이너 node 명령어로 server.js파일을 실행하도록 명령
CMD [&quot;node&quot;, &quot;server.js&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;첫 번째 개념 정리&lt;/h2&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;이러한 이미지를 만들기 위해 Dockerfile을 생성하며, 세부적인 명령을 재공하고 무엇을 넣고, 어떤 이미지를 사용하며, 어떤 코드와 종속성, 최종적으로 외부 컨테이너에서 단계까지 정리하여 작성합니다.&lt;/li&gt;
&lt;li&gt;컨테이너의 블루프린트이자 템플릿입니다.&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;이미지에 기반한 여러 개의 컨테이너 실행 가능합니다.&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;span&gt;따라서 이미지의 레이어를 공유하고 각 컨테이너에 독립적인 실행 환경을 제공하는 방식이라고 생각하여 정리하였습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;&lt;span&gt;컨테이너 중지 &amp;amp; 재시작&lt;/span&gt;&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&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;span&gt; docker run &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;기존 중지되어있는 컨테이너를 다시 시작&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker start &amp;lt;container name&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;Attached &amp;amp; Detached 컨테이너&lt;/h1&gt;
&lt;div&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;app.post(&quot;/store-goal&quot;, (req, res) =&amp;gt; {
  const enteredGoal = req.body.goal;
  console.log(enteredGoal);
  userGoal = enteredGoal;
  res.redirect(&quot;/&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Attached&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker run으로 시작하면 attached가 기본으로 시작됩니다.&lt;/li&gt;
&lt;li&gt;컨테이너와 연결하여 출력 결과를 수신&lt;/li&gt;
&lt;li&gt;docker attach &amp;lt;container name or id&amp;gt;&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker run -p 3000:80 sha256:da6989592eeb5a0585305f2c87758599aa3e77f5aea897bbf601ea39a5addd0d
Docker Gosu&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&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;Detached&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker start⁠ or&amp;nbsp;docker run -d⁠ 명령으로 시작하면 detached가 기본으로 시작됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;로그 메시지를 보기 위해 사용되며&amp;nbsp;docker logs &amp;lt;container id or name&amp;gt;⁠ 을 통해 볼 수 있습니다.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ docker logs interesting_villani
Docker Gosu

# attach로 계속해서 로그 메시지를 확인할 수 있습니다.
$ docker logs -f interesting_villani&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;중요한건 attached, detached든 컨테이너는 게속해서 실행 중이라는 사실이며, 컨테이너 내부의 정보가 필요한 경우 사용하게 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;인터렉티브 모드&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너는 포그라운드와 백그라운드 환경을 구분합니다.&lt;/li&gt;
&lt;li&gt;컨테이너나 컨테이너로 실행되는 애플리케이션에는 어떠한 명령어도 입력할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;$ docker run 9c38aec534e5
Please enter the min number: Traceback (most recent call last):
  File &quot;/app/rng.py&quot;, line 3, in &amp;lt;module&amp;gt;
    min_number = int(input('Please enter the min number: '))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EOFError: EOF when reading a line&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너에 접속하여 무언가를 입력하기 위해선 추가적인 옵션이 필요합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-i&lt;span&gt; 또는 &lt;/span&gt;--interactive&lt;span&gt;는 Docker 컨테이너의 표준 입력(STDIN)을 열어두어, 사용자의 입력을 컨테이너에 전달할 수 있게 합니다. 하지만 이 옵션만 사용할 경우, 컨테이너와의 상호작용은 텍스트 기반의 입력에 국한되며, 커맨드 라인의 입력/출력을 완전히 제어할 수는 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;-t&lt;span&gt; 또는 &lt;/span&gt;--tty&lt;span&gt;는 가상 터미널을 할당합니다. 이 옵션은 컨테이너와의 상호작용을 더욱 풍부하게 만들어주며, 사용자가 컨테이너 내부에서 실행되는 애플리케이션과 마치 실제 터미널을 사용하는 것처럼 상호작용할 수 있게 해줍니다. &lt;/span&gt;-t&lt;span&gt; 옵션은 컨테이너 내부의 명령어 실행 시 터미널의 표준 출력과 에러를 적절하게 포맷팅하는 데 유용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;$ docker run --help
  -i, --interactive                    Keep STDIN open even if not attached
  -t, --tty                            Allocate a pseudo-TTY
# it를 결합하면 무언가를 입력할 수 있으며 , 컨테이너가 무언가를 입력받을 수 있습니다.

$ docker run -it 9c38aec534e5
Please enter the min number: 1
Please enter the max number: 2
2

# 컨테이너를 다시 시작합니다.
# -t 옵션은 기존 run에서 있었던 환경을 유지하며 실행됩니다.
$ docker start -a -i tender_pasteur
Please enter the min number: 1
Please enter the max number: 5
4&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;이를통해 도커가 단순히 웹 서버나 애플리케이션 같은 장기적으로 실행되는 프로세스에만 적용되는게 아닌 간단히 유틸리티 애플리케이션을 도커화하는데 사용할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;위 내용처럼 단순히 입력이 필요하고 무언가를 출력하는 애플리케이션을 만들 수 있습니다.&lt;/div&gt;
&lt;div&gt;정리하자면 필요에 따라 컨테이너와 통신할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;이미지 &amp;amp; 컨테이너 삭제하기&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;docker rm &amp;lt;container name&amp;gt;&amp;nbsp;&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;기본적으로 이미지 내부의 모든 레이어를 삭제합니다.&lt;/li&gt;
&lt;li&gt;이미지가 컨테이너에 의해 사용되지 않는 경우만 삭제할 수 있습니다.&lt;/li&gt;
&lt;li&gt;docker rmi &amp;lt;image id&amp;gt;⁠&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;모든 이미지 제거하기
&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;docker image prune⁠&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;중지된 컨테이너 자동으로 제거하기&lt;/h1&gt;
&lt;div&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;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$ docker run --help
--rm                             Automatically remove the container

$ docker run -p &amp;lt;local&amp;gt;:&amp;lt;container&amp;gt; -d --rm &amp;lt;image id&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;이미지 검사&lt;/h1&gt;
&lt;div&gt;이미지가 환경과 컨테이너를 포함하기 때문에 용량이 매우 크게 잡혀있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하지만 실행 중인 컨테이너는 매우 크지 않은데, 앞선 레이어에서 알 수 있듯이 이미지 레이어 위에 추가된 작은 레이어기 때문입니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;따라서 이미지는 복사되지 않고 컨테이너는 이미지를 기반으로 빌드된다고 할 수 있습니다.&lt;/div&gt;
&lt;div&gt;동일한 이미지로 실행되는 여러 컨테이너는 이미지 내부의 코드를 공유합니다.&lt;/div&gt;
&lt;div&gt;오직 컨테이너 레이어에서만 쓰기 작업을 할 수 있습니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
&amp;lt;none&amp;gt;       &amp;lt;none&amp;gt;    9c38aec534e5   48 minutes ago      1.02GB
&amp;lt;none&amp;gt;       &amp;lt;none&amp;gt;    da6989592eeb   About an hour ago   1.11GB&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 정보 확인하기&amp;nbsp;&amp;nbsp;docker image inspect &amp;lt;image id&amp;gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;⁠날짜, 버전, 환경 변수 등등 여러 이미지 환경 정보를 알 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;$ docker image inspect da6989592eeb
[
    {
        &quot;Id&quot;: &quot;sha256:da6989592eeb5a0585305f2c87758599aa3e77f5aea897bbf601ea39a5addd0d&quot;,
        &quot;RepoTags&quot;: [],
        &quot;RepoDigests&quot;: [],
        &quot;Parent&quot;: &quot;&quot;,
        &quot;Comment&quot;: &quot;buildkit.dockerfile.v0&quot;,
        &quot;Created&quot;: &quot;2024-02-03T05:53:05.664255152Z&quot;,
        &quot;Container&quot;: &quot;&quot;,
        &quot;ContainerConfig&quot;: {
            &quot;Hostname&quot;: &quot;&quot;,
            &quot;Domainname&quot;: &quot;&quot;,
            &quot;User&quot;: &quot;&quot;,
            &quot;AttachStdin&quot;: false,
            &quot;AttachStdout&quot;: false,
            &quot;AttachStderr&quot;: false,
            &quot;Tty&quot;: false,
            &quot;OpenStdin&quot;: false,
            &quot;StdinOnce&quot;: false,
            &quot;Env&quot;: null,
            &quot;Cmd&quot;: null,
            &quot;Image&quot;: &quot;&quot;,
            &quot;Volumes&quot;: null,
            &quot;WorkingDir&quot;: &quot;&quot;,
            &quot;Entrypoint&quot;: null,
            &quot;OnBuild&quot;: null,
            &quot;Labels&quot;: null
        },
        &quot;DockerVersion&quot;: &quot;&quot;,
        &quot;Author&quot;: &quot;&quot;,
        &quot;Config&quot;: {
            &quot;Hostname&quot;: &quot;&quot;,
            &quot;Domainname&quot;: &quot;&quot;,
            &quot;User&quot;: &quot;&quot;,
            &quot;AttachStdin&quot;: false,
            &quot;AttachStdout&quot;: false,
            &quot;AttachStderr&quot;: false,
            &quot;ExposedPorts&quot;: {
                &quot;80/tcp&quot;: {}
            },
            &quot;Tty&quot;: false,
            &quot;OpenStdin&quot;: false,
            &quot;StdinOnce&quot;: false,
            &quot;Env&quot;: [
                &quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;,
                &quot;NODE_VERSION=21.6.1&quot;,
                &quot;YARN_VERSION=1.22.19&quot;
            ],
            &quot;Cmd&quot;: [
                &quot;node&quot;,
                &quot;server.js&quot;
            ],
            &quot;ArgsEscaped&quot;: true,
            &quot;Image&quot;: &quot;&quot;,
            &quot;Volumes&quot;: null,
            &quot;WorkingDir&quot;: &quot;/app&quot;,
            &quot;Entrypoint&quot;: [
                &quot;docker-entrypoint.sh&quot;
            ],
            &quot;OnBuild&quot;: null,
            &quot;Labels&quot;: null
        },
        &quot;Architecture&quot;: &quot;amd64&quot;,
        &quot;Os&quot;: &quot;linux&quot;,
        &quot;Size&quot;: 1109012962,
        &quot;VirtualSize&quot;: 1109012962,
        &quot;GraphDriver&quot;: {
            &quot;Data&quot;: {
                &quot;LowerDir&quot;: &quot;/var/lib/docker/overlay2/a9ac9rjzl1zxdqgccrumb7s1f/diff:/var/lib/docker/overlay2/6qsby0k3l2cebtbqprnohpsfj/diff:/var/lib/docker/overlay2/jx19xnlau71usuwczexarz2yd/diff:/var/lib/docker/overlay2/a7d9673ea1a995f423d9a8d8ded0192a3fd6bb71ec2f5008cd9f9749dd264cfe/diff:/var/lib/docker/overlay2/107e2a7aba3dcc149ac136bb38cbebfbf5b489fb7213027fb2cc9610977ffb30/diff:/var/lib/docker/overlay2/a89bd169688ca99c639d7046eb6e0b81bcc93ca478c1854e11bf25804909cf32/diff:/var/lib/docker/overlay2/13203f4e6364f033c96441c5306720cbf3ee3ae7cd339d4d01b6e1b1de9f5797/diff:/var/lib/docker/overlay2/20e942d2e19e4e7951e32bd797d77e78a4bbbb67ea295d2d4e1b60f1240b20c4/diff:/var/lib/docker/overlay2/88301b54245f728935b4d692d2c4655a358c08024bf8d99f319dde2394e9ef0a/diff:/var/lib/docker/overlay2/8e2e822830c6a0a1c2669bf4d5955b335e0c090fa501dc4204b505cdce5c0976/diff:/var/lib/docker/overlay2/31647be9a8c6d231e08a387b3039818f99348cafbeac5665ad4ac331416e58ad/diff&quot;,
                &quot;MergedDir&quot;: &quot;/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/merged&quot;,
                &quot;UpperDir&quot;: &quot;/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/diff&quot;,
                &quot;WorkDir&quot;: &quot;/var/lib/docker/overlay2/36hc1orifmsrgf35j3hnfx3hb/work&quot;
            },
            &quot;Name&quot;: &quot;overlay2&quot;
        },
        &quot;RootFS&quot;: {
            &quot;Type&quot;: &quot;layers&quot;,
            &quot;Layers&quot;: [
                &quot;sha256:1dae5147cd293b16e7b8c93f778dbf7ceff5c81c2b2704d3e5a98d331cdbe0ab&quot;,
                &quot;sha256:bcd354c940e1b2a5c23b23b76d7d6d1df242de7e222aae8ceb32cc6ff1a19673&quot;,
                &quot;sha256:9f843c569746b6fc06f17d77a368d723b7a702eb9dd9df8e2c86371cf05f7737&quot;,
                &quot;sha256:d43f876e6f21d85f0e43c25ff4aaf25c9a2a47de6e91b5fb54dac2b74682c820&quot;,
                &quot;sha256:e27cf095612475d2087f75327a9f8bd2103f1e82ef2531a9d650d33abcff2930&quot;,
                &quot;sha256:9c297f4ae6351c6799194a950c70c53e269f60e22790575e83babe759ca8d81a&quot;,
                &quot;sha256:56553c49b3a39be1b328e492a9431053ffacb135451e071c35c9f7eedead8cd6&quot;,
                &quot;sha256:621685762c1642715d68257b5d839adf306f15153323c3ff4a708073a432c1d6&quot;,
                &quot;sha256:a718020434adbfe225b785989227719e6b5c915e59256365d89c0ab27002b094&quot;,
                &quot;sha256:695a4d105c49c683c1dcd271d6ed95a0f674dc08c922e3fd3fd1d7b12de1fc65&quot;,
                &quot;sha256:13a097e914c6a9ccd0a89b68313425ed1de92d7267255ea695b0473c36eddabd&quot;,
                &quot;sha256:a8ce279add73a7f63e79540d00311790c0b588cda79b8b567908aea02041575f&quot;
            ]
        },
        &quot;Metadata&quot;: {
            &quot;LastTagTime&quot;: &quot;0001-01-01T00:00:00Z&quot;
        }
    }
]&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;컨테이너 검사 및 파일입출력&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 중인 컨테이너로 혹은 실행 중인 컨테이너 밖으로 파일 또는 폴더를 복사할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker cp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# /c/docker-course/dummy/sample.txt
today: 2024-02-03
hello!&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# docker cp sourceDir &amp;lt;container name&amp;gt;:&amp;lt;dir&amp;gt;
$ docker cp dummy/. musing_elion:/test
Successfully copied 2.56kB to musing_elion:/test

# 반대로 container에서 로컬로 파일 가져오기
$  docker cp musing_elion:/test dummy
Successfully copied 2.56kB to C:\docker-course\dummy&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;컨테이너를 다시 시작하고 이미지를 다시 작성하지 않고도 컨테이너에 무언가를 추가할 수 있습니다.&lt;/div&gt;
&lt;div&gt;하지만 변경 사항을 추적하기 어렵기 때문에 중요 파일을 수정하지 않도록 해야합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;컨테이너와 이미지에 이름 &amp;amp; 태그 지정하기&lt;/h1&gt;
&lt;div&gt;컨테이너와 이미지에 고유한 이름과 태그를 추가할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Container 고유한 이름 추가하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker run --name &amp;lt;name&amp;gt; &amp;lt;image id&amp;gt;
--name string                    Assign a name to the container

CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS                  NAMES
8b0f84d141a7   da6989592eeb   &quot;docker-entrypoint.s&amp;hellip;&quot;   6 seconds ago   Up 5 seconds   0.0.0.0:3000-&amp;gt;80/tcp   docker-app&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;image 고유한 이름 추가하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지에도 이름을 추가할 수 있으며 이를 태그라고 부릅니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;이미지 태그는 두 부분으로 구성됩니다.&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name(REPOSITORY) : tag⁠ /&amp;nbsp;FROM node:14
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 특정화된 이미지 그룹을 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;tag
&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;태그를 통해 특정 버전, 특정 구성을 사용할 수 있게 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Docker hub에는 다양한 태그를 지닌 특정한 이미지들이 있고, 해당 이미지를 사용하여 자체 컨테이너 환경에서 더 정확한 구성을 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&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;br /&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;태그는 단어 혹은 1,2,3과 같이 숫자도 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
&amp;lt;none&amp;gt;       &amp;lt;none&amp;gt;    9c38aec534e5   2 hours ago   1.02GB
docker-app   latest    da6989592eeb   2 hours ago   1.11GB&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;이미지 뒤에 적절한 태그를 추가함으로써 식별할 수 있는 특정화된 다양한 버전의 이미지를 생성할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;이미지 공유하기&lt;/h1&gt;
&lt;div&gt;이미지가 있는 모든 사람은 이미지를 기반으로 컨테이너를 만들 수 있습니다.&lt;/div&gt;
&lt;div&gt;이는 실제로 컨테이너를 누구와도 공유하지 않고 이미지만을 공유한다는 것을 의미합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;공유 방식에는 두 가지가 있습니다&lt;/div&gt;
&lt;div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Dockerfile 공유
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile과 애플리케이션에 관한 소스 코드를 제공하면 자체 이미지를 빌드하고 컨테이너를 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Dockerfile은 빌드되어야 컨테이너로 사용할 수 있게 되며 해당 Dockerfile에서 필요한 코드와 환경 또한 공유될 필요가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;빌드된 Image 공유
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 것이 이미지로 구성되어 있기 때문에 빌드 과정 없이 즉시 컨테이너로 실행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;Docker hub에 이미지 push하기&lt;/h1&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker Hub
&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://hub.docker.com/&quot;&gt;https://hub.docker.com/&lt;/a&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Private Registry
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;많은 공급자가 있으며, 도커 이미지를 지원하는 공급자를 사용할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;제공업체에 따라 이미지 공개범위가 다릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;공통적으로는 이미지를 푸시하여 다른 사용자들과 공유를 할 수 있다는 장점이 있습니다.&lt;/div&gt;
&lt;div&gt;docker push&amp;nbsp;,&amp;nbsp;docker pull⁠ 명령이 있으며 해당 명령에 공급자의 URL이 포함되어야 합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yaTs1/btsEj5bxIMP/Jy9CITKygmAdntKNUJCqHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yaTs1/btsEj5bxIMP/Jy9CITKygmAdntKNUJCqHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yaTs1/btsEj5bxIMP/Jy9CITKygmAdntKNUJCqHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyaTs1%2FbtsEj5bxIMP%2FJy9CITKygmAdntKNUJCqHk%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;919&quot; height=&quot;668&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Docker hub를 만들었다고 하더라도, 이미지의 로컬 설정이 해당 저장소를 인지하지 못합니다.&lt;/div&gt;
An image does not exist locally with the tag: chois6j/node-app
&lt;div&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ docker push chois6j/node-app
Using default tag: latest
The push refers to repository [docker.io/chois6j/node-app]
An image does not exist locally with the tag: chois6j/node-app&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;하지만 Docker Hub ID를 prefix로 사용하여 다시 빌드하게 된다면 로컬에서 Docker hub 저장소를 인지하고 Push가 가능하게 됩니다.&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;crmsh&quot; data-code-language=&quot;shell&quot;&gt;&lt;code&gt;# 이미지 빌드를 통한 설정
$ docker build -t chois6j/node-app .

# 기존 이미지 재설정 &amp;lt;tag&amp;gt;는 생략 가능
$ docker tag &amp;lt;target image name&amp;gt;:&amp;lt;tag&amp;gt; &amp;lt;new image name&amp;gt;:&amp;lt;tag&amp;gt;

# tag로 만들면 이전 이미지의 복제본으로 생성되게 됩니다.
$ docker tag docker-app chois6j/node-app:1.5
REPOSITORY         TAG       IMAGE ID       CREATED       SIZE
&amp;lt;none&amp;gt;             &amp;lt;none&amp;gt;    9c38aec534e5   2 hours ago   1.02GB
chois6j/node-app   1.5       da6989592eeb   3 hours ago   1.11GB
docker-app         latest    da6989592eeb   3 hours ago   1.11GB&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;하지만 push의 경우 해당 계정을 소유한 경우에만 가능하기 때문에 push 하려는 Docker hub repository account&lt;br /&gt;로그인이 필요합니다.&amp;nbsp;docker login⁠ /&amp;nbsp;docker logout&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;$ docker push chois6j/node-app:1.5
The push refers to repository [docker.io/chois6j/node-app]
a8ce279add73: Layer already exists
13a097e914c6: Layer already exists
695a4d105c49: Layer already exists
a718020434ad: Layer already exists
621685762c16: Layer already exists
56553c49b3a3: Layer already exists
9c297f4ae635: Layer already exists
e27cf0956124: Layer already exists
d43f876e6f21: Layer already exists
9f843c569746: Layer already exists
bcd354c940e1: Layer already exists
1dae5147cd29: Layer already exists
errors:
denied: requested access to the resource is denied
unauthorized: authentication require&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ docker login
Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.
You can log in with your password or a Personal Access Token (PAT). Using a limited-scope PAT grants better security and is required for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/

Username: chois6j
Password: 
Login Succeeded&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정상적으로 푸시가 된 것을 확인할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 업로드는 전체 이미지를 업로드하지 않고 의존하는 이미지에 대한 연결을 생성하여 필요한 부분만 업로드하게 됩니다.&lt;/li&gt;
&lt;li&gt;동일한 리포지토리에 다른 태그를 부여하여 업로드하더라도 여러 태그를 포함하여 저장할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;$ docker push chois6j/node-app:1.5
The push refers to repository [docker.io/chois6j/node-app]
a8ce279add73: Layer already exists
13a097e914c6: Layer already exists
695a4d105c49: Layer already exists
a718020434ad: Layer already exists
621685762c16: Layer already exists
56553c49b3a3: Layer already exists
9c297f4ae635: Layer already exists
e27cf0956124: Layer already exists
d43f876e6f21: Layer already exists
9f843c569746: Layer already exists
bcd354c940e1: Layer already exists
1dae5147cd29: Layer already exists
1.5: digest: sha256:80e4841fa6d1f0dc94a111745303b6819d7d637cf0a12e2223675bb655e9c0de size: 2836&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h1&gt;공유 이미지 가져오기(pull) &amp;amp; 사용하기&lt;/h1&gt;
docker pull&amp;nbsp; 명령으로 이미지를 가져올 수 있습니다.&lt;/div&gt;
&lt;div&gt;공개되어 있다면 로그인 할 필요없이 모든 사람이 가져갈 수 있습니다.&lt;br /&gt;
&lt;div&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ docker pull chois6j/node-app:1.5
1.5: Pulling from chois6j/node-app
6a299ae9cfd9: Already exists
e08e8703b2fb: Already exists
68e92d11b04e: Already exists
5b9fe7fef9be: Already exists
278d467c182f: Already exists
aab6430b55a2: Already exists
b6f92f4f6240: Already exists
2e24338062e4: Already exists
3b72273561c4: Already exists
d6f4e22209a7: Already exists
56e0e64c0f3c: Already exists
6b685fa59969: Already exists
Digest: sha256:80e4841fa6d1f0dc94a111745303b6819d7d637cf0a12e2223675bb655e9c0de
Status: Downloaded newer image for chois6j/node-app:1.5
docker.io/chois6j/node-app:1.5

What's Next?
  View a summary of image vulnerabilities and recommendations &amp;rarr; docker scout quickview chois6j/node-app:1.5

$ docker run -p 3000:80 --rm -d chois6j/node-app:1.5
ecba6b57821ecbd3af73f4aecb3c28ecdfccf247f828beced687a3c2afbb1644&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;Docker에서 이미지를 실행하기 위해 &lt;/span&gt;docker run&lt;span&gt; 명령을 사용할 때, 해당 이미지가 로컬 컴퓨터에 존재하지 않는 경우, Docker는 자동으로 Docker Hub 또는 지정된 다른 레지스트리에서 이미지를 찾아서 다운로드(즉, &lt;/span&gt;docker pull&lt;span&gt;을 내부적으로 실행)한 후 컨테이너를 실행합니다. 이 과정은 사용자가 명시적으로 &lt;/span&gt;docker pull&lt;span&gt; 명령을 사용하지 않아도 자동으로 수행됩니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;따라서&amp;nbsp;docker pull&amp;nbsp;chois6j/node-app:1.5⁠⁠⁠ 명령을 사용하지 않고 바로&amp;nbsp;docker run chois6j/node-app:1.5⁠ 명령을 사용한다면&amp;nbsp;docker run⁠ 은 로컬 컴퓨터에서 이미지를 찾지 못하고 Docker hub에 접근하여 이미지를 가져오게 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;하지만 로컬에 해당 이미지가 존재한다면 그것이 최신 버전이든 상관없이 해당 이미지를 실행시키게 됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;정리하면 현재 로컬에 이미지가 존재하지만, 이미지가 업데이트 되어 Docker hub에 다시 푸시되었다면&amp;nbsp;docker run&amp;nbsp;명령 만으로는 최신 업데이트 이미지를 제공받지 못합니다. 그렇기 때문에&amp;nbsp;docker pull⁠ 과정이 수행되어야합니다.&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL/개념정리</category>
      <author>초집중</author>
      <guid isPermaLink="true">https://choiblog.tistory.com/225</guid>
      <comments>https://choiblog.tistory.com/225#entry225comment</comments>
      <pubDate>Sat, 3 Feb 2024 19:42:43 +0900</pubDate>
    </item>
  </channel>
</rss>