Jekyll2022-02-01T05:40:18+00:00https://kimkoungho.github.io//feed.xmlNess Blogdevelop studynesskoungho632@gmail.comJava NIO2022-01-14T00:00:00+00:002022-01-16T00:00:00+00:00https://kimkoungho.github.io//java/java-nio<h1 id="java-nionon-blocking-io">Java NIO(Non-blocking I/O)</h1>
<p>Java NIO 는 JDK 1.4 에 적용된 패키지로 기존의 Java I/O 느린 단점을 보완하기 위해 추가되었다.<br /></p>
<h2 id="기존-java-io">기존 Java IO</h2>
<p>기본적으로 Java 에서는 C/C++ 처럼 메모리를 직접 관리하거나 OS 수준의 시스템 콜(커널 라이브러리)을 직접 사용 할 수 없다.</p>
<ul>
<li>JNI 등을 이용하는 방법은 논외로 둔다.</li>
</ul>
<p>기존의 Java IO 는 커널 버퍼를 직접 접근할 수 없었다.<br />
그래서 소켓이나 파일 IO 가 발생하면 JVM 은 커널의 버퍼 영역에서 JVM 내부 메모리로 해당 데이터를 불러온 후에 접근이 가능했기 때문에 <strong>커널 버퍼의 데이터를 JVM 내부 버퍼로 복사하는 오버헤드</strong> 가 존재했음.<br /></p>
<p><img src="/assets/images/posts/20220114/java-nio_bytebuffer-2.jpeg" alt="java-nio_bytebuffer-2.jpeg" /><br />
출처 : <a href="http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/">http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/</a></p>
<p>위 그림은 기존의 Java IO 가 디스크에서 파일을 읽는 과정</p>
<ul>
<li>JVM 프로세스는 file 을 읽기 위해 커널에 명령을 전달</li>
<li>커널은 시스템 콜을 사용하여 DMA(Direct Memory Access) 를 호출</li>
<li>DMA 는 디스크 컨트롤러를 이용해 디스크에서 파일을 읽어 커널 버퍼로 복사</li>
<li>JVM 은 커널 버퍼의 내용을 내부 버퍼에 복사</li>
</ul>
<blockquote>
<p>DMA(Direct Memory Access) <br />
DMA 는 CPU 와 독립적으로 메모리에 접근 할 수 있게 하는 시스템의 기능 <br />
PIO(Programmed I/O) 는 CPU 가 주변장치와 데이터를 주고 받는 방식으로 CPU 리소스를 사용하는 오버헤드가 존재<br />
이를 극복하기 위해서 DMA 가 개발되었다. <br />
CPU 가 DMA 컨트롤러를 호출 > DMA 컨트롤러가 주변장치 데이터를 읽은 후 메모리 전송 > 전송완료 후 CPU 에게 완료 신호 송신</p>
</blockquote>
<p>이런 과정은 다음과 같은 오버헤드가 있음</p>
<ul>
<li>JVM 내부 버퍼로 복사하는 과정에서 CPU 리소스 사용하기 때문에 오버 헤드
<ul>
<li>커널 버퍼 > JVM 버퍼로 데이터를 복사하는 일은 CPU 를 사용하게 됨 (PIO)</li>
<li>DMA 를 이용해 CPU 자원을 사용하지 않으면 CPU 자원을 다른 곳에서 사용할 수 있음</li>
</ul>
</li>
<li>JVM 내부 버퍼는 사용 후 GC 대상</li>
<li>복사를 진행하는 동안 I/O 를 요청한 thread 는 blocking 됨
<ul>
<li>OS 는 디스크에서 읽는 효율을 높이기 위해 최대한 많은 양의 데이터를 커널 버퍼에 저장함</li>
<li>따라서 커널 버퍼에서 JVM 내부 버퍼로 데이터를 복사하는 동안 해당 thread 는 다른 일을 할 수 없음</li>
</ul>
</li>
</ul>
<p>따라서 이런 오버헤드를 줄이기 위해서 커널 버퍼를 JVM 에서 직접 사용하기 위해서 NIO 가 등장</p>
<h2 id="java-nio">Java NIO</h2>
<p>NIO 핵심 용어</p>
<ul>
<li>Channel</li>
<li>Buffer</li>
<li>Selector</li>
</ul>
<h2 id="channel">Channel</h2>
<ul>
<li>압출력 중 1개만 가능한 단방향 채널과 입출력 모두 가능한 양방향 채널을 지원하는 클래스
<ul>
<li>기존의 Stream 기반 IO 에서는 읽기 or 쓰기만 가능했음 (단방향)</li>
</ul>
</li>
<li>blocking 과 non-blocking 모두 지원
<ul>
<li>non-blocking IO 를 위해서 데이터를 요청한 스레드와 데이터가 있는 버퍼 사이에서 동작 가능</li>
</ul>
</li>
</ul>
<h3 id="channel-클레스">Channel 클레스</h3>
<ul>
<li>FileChannel : 파일 입출력</li>
<li>DatagramChannel : UDP 네트워크 입출력</li>
<li>SocketChannel : TCP 네트워크 입출력</li>
<li>ServerSocketChannel : TCP 연결 수신을 대기 하고 각 연결에 대해 SocketChannel 을 생성</li>
</ul>
<p><img src="/assets/images/posts/20220114/nio-channel.png" alt="nio-channel.png" /><br />
출처 : <a href="https://velog.io/@tkadks123/Java-NIO%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-12">https://velog.io/@tkadks123/Java-NIO%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-12</a></p>
<p>NIO 는 Channel 을 통해 데이터 입출력을 처리함 <br /></p>
<ul>
<li>입출력 요청 스레드는 채널에 데이터 입력 요청</li>
<li>채널은 해당 데이터를 버퍼에 추가</li>
<li>스레드는 버퍼에서 데이터를 확인</li>
</ul>
<h3 id="java-nio-scattergather">Java NIO Scatter/Gather</h3>
<ul>
<li>Scatter/Gather 는 채널에서 데이터 read/write 에서 사용하는 개념</li>
</ul>
<p>Scattering Read</p>
<ul>
<li>하나의 채널은 여러 버퍼로 데이터를 읽음</li>
<li>따라서 채널은 데이터를 여러 버퍼로 분산함</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ByteBuffer</span> <span class="n">head</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="mi">128</span><span class="o">);</span>
<span class="nc">ByteBuffer</span> <span class="n">body</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="mi">1024</span><span class="o">);</span>
<span class="nc">ByteBuffer</span><span class="o">[]</span> <span class="n">byteBuffers</span> <span class="o">=</span> <span class="o">{</span><span class="n">head</span><span class="o">,</span> <span class="n">body</span><span class="o">};</span>
<span class="n">channel</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">byteBuffers</span><span class="o">);</span>
</code></pre></div></div>
<ul>
<li>위 예시는 고정된 크기의 헤더와 본문이 있을 경우 분산 읽기를 수행하는 예제</li>
<li>channel.read() 에서는 버퍼가 발생하는 순서대로 데이터를 채움</li>
<li>head 를 먼저 채우고 body 영역을 채운다</li>
<li>즉, 고정된 크기에 버퍼를 이용하여 분산 읽기가 가능함</li>
</ul>
<p>Gathering Write</p>
<ul>
<li>여러 버퍼의 데이터를 단일 채널에 쓰기</li>
<li>따라서 채널은 여러 버퍼의 데이터를 하나의 채널로 수집 가능</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ByteBuffer</span> <span class="n">head</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="mi">128</span><span class="o">);</span>
<span class="nc">ByteBuffer</span> <span class="n">body</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="mi">1024</span><span class="o">);</span>
<span class="nc">ByteBuffer</span><span class="o">[]</span> <span class="n">byteBuffers</span> <span class="o">=</span> <span class="o">{</span><span class="n">head</span><span class="o">,</span> <span class="n">body</span><span class="o">};</span>
<span class="n">channel</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">byteBuffers</span><span class="o">);</span>
</code></pre></div></div>
<ul>
<li>channel.write() 를 통해 버퍼의 내용을 순서대로 쓰기를 수행</li>
<li>실제로 버퍼는 사용하는 만큼만 기록됨
<ul>
<li>head 버퍼는 128 바이트 할당했지만 실제로 58 바이트만 사용중이면 58 바이트만 기록됨</li>
</ul>
</li>
<li>따라서 동적 데이터에 대한 처리도 가능함</li>
</ul>
<blockquote>
<p>Scatter/Gather<br />
각 버퍼를 개별적으로 매핑하여 전송하지 않고 여러 버퍼 영역에 한번에 전송할 수 있는 DMA 기술 <br />
물리적으로 인접하지 않은 여러 버퍼가 있을때 논리적으로 하나의 버퍼로 처리할 수 있도록 도와주는 기법<br />
시스템 콜 호출 빈도를 줄이기 위해서 사용한다</p>
</blockquote>
<h2 id="buffer">Buffer</h2>
<ul>
<li>버퍼는 채널과 상호작용할 때 사용되며 채널에서 데이터 읽거나 쓸 때 사용함</li>
</ul>
<h3 id="buffer-클래스">Buffer 클래스</h3>
<ul>
<li>ByteBuffer</li>
<li>MappedByteBuffer</li>
<li>CharBuffer</li>
<li>ShortBuffer</li>
<li>IntBuffer</li>
<li>LongBuffer</li>
<li>FloatBuffer</li>
<li>DoubleBuffer</li>
</ul>
<h3 id="direct-buffer-vs-non-direct-buffer">Direct Buffer vs Non Direct Buffer</h3>
<p>Direct Buffer</p>
<ul>
<li>커널 버퍼를 사용하는 방식으로 위에서 말한 오버헤드를 방지할 수 있음</li>
<li>다만 Byte 형식으로만 사용할 수 있음</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocateDirect</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
</code></pre></div></div>
<ul>
<li>버퍼의 할당 및 해제 비용이 높음</li>
<li>위 방식을 사용한 객체의 래퍼런스는 JVM Heap 에 생성되어 GC 되지만 커널 버퍼 영역은 JVM 이 아닌 커널에서 제어를 수행함</li>
<li>따라서 IO 영향을 많이 받는 life time 긴 버퍼에 대해서만 할당하는 것이 좋음</li>
</ul>
<p>Non Direct Buffer</p>
<ul>
<li>기존 Java IO 처럼 JVM 내부 메모리에 버퍼를 복사하는 방식을 사용함</li>
<li>ByteBuffer.allocateDirect 를 제외한 모든 버퍼가 Non Direct Buffer</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// heap used</span>
<span class="nc">ByteBuffer</span> <span class="n">buffer</span> <span class="o">=</span> <span class="nc">ByteBuffer</span><span class="o">.</span><span class="na">allocate</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="일반적인-버퍼-사용">일반적인 버퍼 사용</h3>
<ol>
<li>버퍼에 데이터 쓰기</li>
<li>쓰기모드에서 읽기 모드로 전환 (buffer.flip())</li>
<li>버퍼에 데이터 읽기</li>
<li>읽은 데이터를 버퍼에서 지운다. (buffer.clear() / buffer.compact())
<ol>
<li>buffer.clear() : 버퍼 전체를 비움</li>
<li>buffer.compact() : 읽은 데이터만 지움</li>
</ol>
</li>
</ol>
<h3 id="버퍼의-속성">버퍼의 속성</h3>
<ul>
<li>Capacity : 버퍼의 고정된 크기로 버퍼가 가득차지 않도록 버퍼를 비워야 함</li>
<li>Position
<ul>
<li>처음 0에서 시작하며 읽기와 쓰기에서 다른 의미를 갖음</li>
<li>쓰기 수행시 다음에 쓰여질 위치를 의미</li>
<li>읽기 수행시 읽을 위치를 의미</li>
<li>쓰기 모드에서 읽기 모드로 전환되면 위치는 0으로 재설정됨</li>
</ul>
</li>
<li>Limit
<ul>
<li>쓰기 모드에서는 Buffer 에 쓸 수 있는 데이터의 한계</li>
<li>읽기 모드에서는 읽을 수 있는 데이터의 제한을 의미</li>
<li>읽기 모드로 전환될 때 쓰기 모드의 쓰기 위치로 제한이 설정됨</li>
</ul>
</li>
</ul>
<h2 id="selector">Selector</h2>
<ul>
<li>하나의 스레드로 여러 채널을 관리하고 처리할 수 있는 클래스 (Reactor 패턴)</li>
</ul>
<h3 id="등장-배경">등장 배경</h3>
<ul>
<li>Selector 이전에는 각 채널에 응답을 기다리기 위해서 스레드와 채널을 1:1 매핑</li>
<li>따라서 각 스레드는 blocking 된 상태로 채널의 응답을 기다린다</li>
<li>하지만 채널이 매우 많아지면 채널 응답을 대기하는 스레드도 늘어나기 때문에 리소스 낭비가 발생한다</li>
</ul>
<p>이런 단점을 보완하기 위해서 1개의 스레드로 여러 채널을 관리할 수 있는 Selector 가 도입됨</p>
<h3 id="멀티-플렉싱">멀티 플렉싱</h3>
<ul>
<li>하나의 채널을 통해 둘 이상의 데이터(시그널)를 전송하는데 사용되는 기술</li>
<li>물리적 장치의 효율성을 높이기 위해 최소한의 물리적인 요소만 사용해서 최대한의 데이터를 전달하기 위해서 사용하는 기술</li>
</ul>
<p>IBM 비동기 bokcing I/O
<img src="/assets/images/posts/20220114/multiplexing.png" alt="multiplexing.png" /><br /></p>
<ul>
<li>어플리케이션에서 커널에 IO 요청을 보내면 커널은 IO 미완료 상태를 전송</li>
<li>어플리케이션은 커널의 응답을 계속 select 한다</li>
<li>select 의 결과가 유의미한 값이 나오면 어플리케이션은 커널로 부터 IO 처리를 시작한다</li>
<li>IO 전체가 blocking 되는 것이 아니라 커널의 응답이 blocking 된 것이다.<br /></li>
</ul>
<h3 id="java-nio-selector">Java NIO Selector</h3>
<p><img src="/assets/images/posts/20220114/java_nio_selector.png" alt="java_nio_selector.png" /><br />
출처 : <a href="http://tutorials.jenkov.com/java-nio/selectors.html">http://tutorials.jenkov.com/java-nio/selectors.html</a></p>
<ul>
<li>Java 의 셀렉터는 커널 라이브러리인 select() 를 사용하기 위해서 지원하는 클래스
<ul>
<li>실제로 OS 환경에 따라 지원하는 select() 가 다름 (select, poll, epoll, etc..)</li>
</ul>
</li>
<li>채널을 셀렉터에 등록하고 select() 호출하면 채널 들 중에서 준비가 완료된 채널을 가져올 수 있음
<ul>
<li>반환할 채널이 없다면 block 됨</li>
</ul>
</li>
<li>셀렉터에 의해서 채널이 반환되면 스레드는 해당 채널을 이용해 이벤트 처리 가능</li>
</ul>
<h3 id="selector-장점">Selector 장점</h3>
<ul>
<li>단일 스레드로 여러 채널을 처리하기 때문에 스레드 수가 적어도 된다</li>
<li>스레드 간 전환은 OS 비용이 많이 들고 메모리를 차지하기 때문에 스레드가 적을 수록 좋음</li>
</ul>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/nio/package-summary.html">https://docs.oracle.com/javase/8/docs/api/java/nio/package-summary.html</a></li>
<li><a href="http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/">http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/</a></li>
<li><a href="https://brunch.co.kr/@myner/47">https://brunch.co.kr/@myner/47</a></li>
<li><a href="https://ko.wikipedia.org/wiki/%EC%A7%81%EC%A0%91_%EB%A9%94%EB%AA%A8%EB%A6%AC_%EC%A0%91%EA%B7%BC">https://ko.wikipedia.org/wiki/%EC%A7%81%EC%A0%91<em>%EB%A9%94%EB%AA%A8%EB%A6%AC</em>%EC%A0%91%EA%B7%BC</a></li>
<li><a href="https://velog.io/@tkadks123/Java-NIO%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-12">https://velog.io/@tkadks123/Java-NIO%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-12</a></li>
<li><a href="http://tutorials.jenkov.com/java-nio/scatter-gather.html">http://tutorials.jenkov.com/java-nio/scatter-gather.html</a></li>
<li><a href="http://tutorials.jenkov.com/java-nio/buffers.html">http://tutorials.jenkov.com/java-nio/buffers.html</a></li>
<li><a href="https://hbase.tistory.com/39">https://hbase.tistory.com/39</a></li>
<li><a href="https://incredible-larva.tistory.com/entry/IO-Multiplexing-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0-1%EB%B6%80">https://incredible-larva.tistory.com/entry/IO-Multiplexing-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0-1%EB%B6%80</a></li>
</ul>nesskoungho632@gmail.com쿠버네티스(kubernates) 아키텍쳐2020-12-01T00:00:00+00:002022-02-01T00:00:00+00:00https://kimkoungho.github.io//devops/kubernates_architecture<h1 id="kubernates-architecture">kubernates architecture</h1>
<h2 id="쿠버네티스-클러스터-아키텍처">쿠버네티스 클러스터 아키텍처</h2>
<ul>
<li>쿠버네티스의 아키텍쳐는 서비스 검색을 위해 유연하고 느슨하게 결합된 메커니즘을 제공</li>
<li>대부분의 분산 컴퓨팅 플랫폼과 마탄가지로 쿠버네티스 클러스터는 하나 이상의 마스터와 여러 컴퓨팅 노드로 구성됨</li>
</ul>
<p><img src="/assets/images/posts/20201201/kubernetes_architecture.png" alt="kubernetes_architecture" /><br />
출처: <a href="https://tkssharma.com/kubernetes_architecture/">https://tkssharma.com/kubernetes_architecture/</a>
<br /></p>
<h3 id="마스터-노드">마스터 노드</h3>
<ul>
<li>마스터 노드는 전체 쿠버네티스 시스템을 제어하고 관리하는 역할을 수행</li>
<li>쿠버네티스 클러스터를 제어하는 컨트롤 플레인(Control plane)이 실행됨</li>
<li>마스터 노드는 API 제공하고 개발자 or 관리자는 해당 API(kubelet, UI) 를 이용해 클러스터를 관리</li>
<li>마스터 노드 내부에 API 서버는 외부 API 요청과 컨트롤 플레인 구성 요소들과 통신
<ul>
<li>쿠버네티스 관리를 위한 리소스 항목과 인증, 권한 부여, 엑세스 제어, API 등록 및 검색을 위한 메커니즘을 제공</li>
</ul>
</li>
<li>스케줄러는 어플리케이션의 배포를 담당
<ul>
<li>스케줄링 정책에 따라서 워커 노드에 pod 를 스케줄링</li>
<li>=> 어플리케이션의 배포 가능한 구성 요소를 워커 노드에 할당</li>
</ul>
</li>
<li>컨트롤로 매니저는 구성 요소의 복제본, 워커 노드 추적, 노드 장애 처리 등과 같은 클러스터 단의 기능을 수행</li>
<li>etcd 는 클러스터 구성에 대한 정보를 저장하는 분산 데이터 저장소
<ul>
<li>SSOT(Single Source of Truth) 역할을 수행</li>
<li>CoreOS 의 오픈소스 분산 키-값 데이터 베이스</li>
</ul>
</li>
<li>마스터는 etcd 를 쿼리하여 노드, pod 의 상태의 다양한 매개변수를 검색함</li>
</ul>
<p><img src="/assets/images/posts/20201201/kubernetes_label_selector.png" alt="kubernetes_label_selector" /><br />
출처: <a href="https://tkssharma.com/kubernetes_architecture/">https://tkssharma.com/kubernetes_architecture/</a>
<br /></p>
<h3 id="워커-노드">워커 노드</h3>
<ul>
<li>워커 노드는 실제 배포되는 컨테이너 어플리케이션을 실행함</li>
<li>각 워커 노드는 마스터와 통신을 위한 쿠버네티스 에이전트 (kubelet) 존재
<ul>
<li>kubelet 은 터케니어늬 life cycle 관리하고 볼륨(CSI) 및 네트워크(CNI) 관리</li>
</ul>
</li>
<li>컨테이너 실행을 위한 컨테이너 런타임(docker)</li>
<li>어플리케이션 구성 요소 간의 네트워크 트래픽을 로드밸런싱 하는 쿠버네티스 서비스 프록시(kube-proxy)</li>
<li>워커 노드는 로깅, 모니터링, 서비스 검색 및 추가 기능을 위한 추가 구성요소도 실행될 수 있음</li>
</ul>
<h3 id="pod">pod</h3>
<ul>
<li>위 그림에서 1개의 원이 1개의 pod</li>
<li>pod 는 하나 이상의 컨테이너 모음으로 쿠버네티스의 핵심 관리 단위로 동일한 컨테스트와 리소스를 공유하는 컨테이너의 논리적 경계 역할을 수행함</li>
<li>pod 그룹화 메커니즘은 여러 종속 프로세스를 함께 실행 할 수 있도록 한다</li>
<li>pod 는 service 를 통해 내부 or 외부에서 접근 될 수 있음</li>
<li>pod 는 label, selector 라는 key-value 를 통해 service 에 연결됨</li>
<li>service 는 지정된 selector 와 일치하는 label 을 가진 pod 를 자동으로 검색함</li>
</ul>
<h3 id="label-and-selector">Label and Selector</h3>
<p>Label : 쿠버네티스 오브젝트를 식별하기 위한 메타 데이터로 key-value 쌍으로 구성됨<br />
Selector : 쿠버네티스의 그룹화에 사용되는 핵심<br /></p>
<ul>
<li>Label Selector 를 이용해서 쿠버네티스 객체들을 그룹화하여 관리 할 수 있음</li>
</ul>
<p>label, selector, namespace 는 쿠버네티스 리소스들을 유연하고 동적으로 관리하는데 도움을 준다<br /></p>
<ul>
<li>Label Selector 는 namespace 내에서 유일해야 함</li>
</ul>
<p>쿠버네티스 자체는 분산 아키텍처를 기반으로 하므로 MSA 구축하고 관리하는데 큰 도움을 준다<br /></p>
<h2 id="오케스트레이션-layer">오케스트레이션 layer</h2>
<p>컨테이너 오케스트레이션은 컨테이너의 배포, 관리, 확장(scale out), 네트워킹 등을 자동화 하는 도구로 <a href="https://kimkoungho.github.io/devops/kubernates/">이전 글</a>에서 설명했다<br /></p>
<p>오케스트레이션 layer 에서 수행하는 것들</p>
<ul>
<li>컨테이너의 스케줄링 수행</li>
<li>컨테이너가 중지되면 restart</li>
<li>컨테이너 네트워크 활성화</li>
<li>필요에 따라 컨테이너 및 관련 리소스를 확장 or 축소</li>
<li>서비스 discovery</li>
</ul>
<h2 id="컨테이너-네트워킹">컨테이너 네트워킹</h2>
<ul>
<li>컨테이너 간의 네트워킹은 컨테이너 오케스트레이션에서 까다로운 문제 중 하나</li>
<li>여기서는 docker 가 컨테이너 네트워킹을 처리하는 방법과 kubernetes 가 네트워킹을 처리하는 방법을 비교해본다</li>
</ul>
<h3 id="docker-네트워킹">docker 네트워킹</h3>
<ul>
<li>docker 컨테이너는 host-private 네트워킹을 사용</li>
<li>이를 위해서 docker 는 virtual bridge 로 불리는 <code class="language-plaintext highlighter-rouge">docker0</code> 인터페이스를 호스트에 프로비저닝 함</li>
<li>virtual bridge 에 연결하기 위해 docker 는 <code class="language-plaintext highlighter-rouge">veth</code> (가상 이더넷 장치) 를 할당하고 <code class="language-plaintext highlighter-rouge">eth0</code> NAT 를 통해 IP 를 매핑</li>
<li>NAT 는 패킷의 IP 헤더에 있는 네트워크 주소 정보를 수정하여 하나의 IP 를 내부 IP 로 매핑하는 방법</li>
<li>이런 docker 네트워킹은 동일한 머신 or 동일한 virtual bridge 를 갖는 컨테이너만 통신 할 수 있는 문제가 있음</li>
<li>많은 호스트와 시스템이 관련된 서비스에서는 위 제약사항은 문제가 됨</li>
<li>그리고 NAT 에 대한 의존도를 무시할 수 없기 때문에 성능 저하로 이어질 수 있음</li>
</ul>
<h3 id="kubernetes-네트워킹">kubernetes 네트워킹</h3>
<ul>
<li>쿠버네티스를 사용한 네트워킹은 docker 네트워킹을 사용하는 것 보다 <strong>성능과 확장성을 높이기 위한 것</strong></li>
<li>모든 컨테이너는 NAT 없이 다른 컨테이너와 통신 할 수 있음</li>
<li>컨테이너는 다른 컨테이너가 참조하는 데 사용하는 것과 동일한 IP 로 자신을 참조</li>
</ul>
<h2 id="래퍼런스">래퍼런스</h2>
<ul>
<li><a href="http://www.yes24.com/Product/Goods/89607047">쿠버네티스_인_액션_도서</a></li>
<li><a href="https://tkssharma.com/kubernetes_architecture/">https://tkssharma.com/kubernetes_architecture/</a></li>
</ul>nesskoungho632@gmail.com쿠버네티스(kubernates) 개념2020-07-04T00:00:00+00:002022-01-28T00:00:00+00:00https://kimkoungho.github.io//devops/kubernates<h1 id="kubernates">kubernates</h1>
<h2 id="등장-배경">등장 배경</h2>
<h3 id="모놀리틱-에서-마이크로-서비스로-전환">모놀리틱 에서 마이크로 서비스로 전환</h3>
<p>모놀리틱 어플리케이션</p>
<ul>
<li>기존의 모놀리틱 어플리케이션은 모든 것이 강하게 결합되어 있었음</li>
<li>해당 어플리케이션의 한 부분만 수정되어도 전체를 배포해야 했음</li>
<li>시간이 지남에 따라 구성 요소 간의 경계가 불분명해지고 의존성이 커지면서 시스템의 복잡성이 증가</li>
<li>어플리케이션을 수행하기 위한 충분한 성능의 서버가 필요하다</li>
<li>scale out 시에는 고려해야 할 부분이 많으며 복잡하다
<ul>
<li>관계형 DB 는 scale out 이 힘듬</li>
</ul>
</li>
</ul>
<blockquote>
<p>모놑리틱 어플리케이션은 여러 기능을 담당 하고 있기 때문에 특정 API 에 대한 요청이 증가하면 어플리케이션을 scale out 해야한다<br />
그런데 해당 어플리케이션이 DB, Redis 등을 사용하고 있다면 scale out 에 따른 connection 부족 문제가 생길 수 있다<br />
요청이 증가한 API 는 해당 DB, Redis 를 사용하지 않더라도 말이다 <br />
(이 부분은 제 개인적인 경험입니다)</p>
</blockquote>
<p>마이크로 서비스 어플리케이션(MSA)</p>
<ul>
<li>모놀리틱 서비스는 이런 저런 이유로 마이크로 서비스로 분할 되어야 한다</li>
<li>각 마이크로 서비스는 독립적인 프로세스로 실행되며 다른 마이크로 서비스와 통신한다</li>
<li>마이크로 서비스는 독립적으로 실행되기 때문에 독립적으로 배포가 가능하다</li>
</ul>
<p>마이크로 서비스의 단점</p>
<ul>
<li>어려운 배포
<ul>
<li>각 서비스간의 상호 의존성이 있기 때문에 배포의 조합 수가 많아 고민이 필요하다</li>
<li>전체 서비스가 하나의 시스템 처럼 동작 할 수 있도록 고려되어야 함</li>
</ul>
</li>
<li>디버깅의 어려움
<ul>
<li>일반적으로 하나의 어플리케이션으로 구성되는 모놀리틱에 비해 네트워크 통신을 이용하기 때문에 디버깅이 어렵다</li>
<li>다행히 Zipkin 과 같은 분산 추적 시스템으로 해결 가능 함</li>
</ul>
</li>
<li>환경의 다양성
<ul>
<li>마이크로 서비스는 독립적으로 동작하기 때문에 독립적인 방식으로 개발된다</li>
<li>동일한 서버에서 여러 마이크로 서비스가 동작한다면 서로 다른 버전의 공유 라이브러리가 의존성 충돌이 발생 할 수 있다</li>
</ul>
</li>
</ul>
<h3 id="어플리케이션에-일관된-환경-제공">어플리케이션에 일관된 환경 제공</h3>
<p>각 어플리케이션은 개발 환경과 실행되는 환경이 다르며 실제 운영중인 서버들은 시간이 지남에 따라 환경이 변할 수 있다<br /></p>
<ul>
<li><a href="https://kimkoungho.github.io/devops/docker_basic/#%EB%8F%84%EC%BB%A4%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%20%EC%9D%B4%EC%9C%A0">이전 글</a> 에서 도커를 사용하는 이유 참고
프로덕션 환경에서만 나타나는 문제를 줄이기 위해 개발과 프로덕션 환경이 정확히 동일하게 실행된다면 미리 문제를 발견할 수 있다<br /></li>
</ul>
<p>devops</p>
<ul>
<li>과거 개발 팀은 어플리케이션을 만들고 이를 배포하면 배포된 어플리케이션은 운영팀에서 관리되었다</li>
<li>하지만 개발 팀이 어플리케이션을 배포하고 관리하는 것이 더 낫다는 것을 깨달았다</li>
<li>이렇게 개발 팀이 배포와 관리를 함께 하는 것을 devops 라고 함</li>
<li>개발자가 어플리케이션 실행 환경을 관리하면 사용자가 무엇이 필요하고 어떤 문제가 있는지 더 잘 이해할 수 있다</li>
<li>하지만 개발자가 배포를 하기 위해서는 인프라에 대한 이해가 필요한데 대부분의 개발자는 인프라에 대해 자세히 알지 못하고 알고 싶어 하지도 않음
<ul>
<li>대게 시스템 관리자가 알아서 관리해주길 원함</li>
</ul>
</li>
</ul>
<p>쿠버네티스를 사용하면 하드웨어를 추상화 하고 어플리케이션 배포, 실행을 위한 플랫폼으로 제공 할 수 있다<br />
개발자는 쿠버네티스를 이용해 시스템 관리자의 도움 없이도 어플리케이션을 구성, 배포 가능하다<br />
시스템 관리자는 실행되는 어플리케이션에 관계 없이 인프라 운영에만 집중 할 수 있다</p>
<h2 id="컨테이너-오케스트레이션-이란">컨테이너 오케스트레이션 이란?</h2>
<p>쿠버네티스는 컨테이너 오케스트레이션 도구 중 하나인데, 먼저 컨테이너 오케스트레이션에 대해서 알아보자</p>
<p>컨테이너 오케스트레이션은 컨테이너의 배포, 관리, 확장(scale out), 네트워킹 등을 자동화 하는 도구이다<br />
도커의 등장으로 서버 관리를 컨테이너를 이용하여 관리가 쉬어졌다.<br />
하지만 Containerization 만으로는 한계가 존재<br /></p>
<h3 id="배포-문제">배포 문제</h3>
<p>도커 컨테이너 위에 돌아가는 어플리케이션을 실행하기 위해서 각 서버에 ssh 로 접속하여 docker stop, run 등을 실행 해주어야 했음</p>
<blockquote>
<p>on-promise 환경에서는 어플리케이션 환경 구성을 위해 서버에 직접 설치했음 <br />
docker 의 등장으로 환경 구성은 docker container 를 이용하여 관리가 가능하지만 실제 어플리케이션 관리를 위해서 docker 명령어가 필요하다</p>
</blockquote>
<p>배포된 어플리케이션을 롤백하려면 이전 버전의 도커 이미지를 수동으로 실행해야함 -> 빠른 롤백의 어려움</p>
<h3 id="서비스-검색service-discovery-문제">서비스 검색(Service Discovery) 문제</h3>
<p>일반적으로 운영환경에서는 로드 밸런서를 이용하여 서비스를 구축하는데, 스케일 아웃이 필요하면 로드 밸런서의 설정 업데이트가 필요</p>
<ul>
<li>새로 등록된 서버 IP 를 추가 해주어야 함</li>
</ul>
<h3 id="서비스-장애부하-모니터링의-문제">서비스 장애,부하 모니터링의 문제</h3>
<p>예상치 못한 부하에 대한 대응을 일일히 사람이 해주어야 함
서비스 장애도 사람이 수동으로 모니터링하여 대응해야 함</p>
<p>이러한 문제점들을 해결하기 위해서 <strong>컨테이너 환경을 효율적으로 관리</strong> 하기 위해서 등장한 기술이 <strong>컨테이너 오케스트레이션</strong> 이다</p>
<h2 id="쿠버네티스의-등장">쿠버네티스의 등장</h2>
<p>쿠버네티스는 다른 오케스트레이션 도구들에 비해서 비교적 늦게 등장했는데, <br />
기존에 도커 진영에서 개발된 <a href="https://docs.docker.com/engine/swarm/">도커 스웜</a> 이 쉽고 간단한 사용법으로 세력을 넓히고 있었고
AWS 에서는 ECS, Hashicorp 에서는 <a href="https://www.nomadproject.io/">Nomad</a>,
Mesos 에서는 <a href="https://mesosphere.github.io/marathon/">Marathon</a> 을 발표했었으나 <br />
구글의 컨테이너 관리 노하우가 녹아있는 쿠버네티스가 등장하고 나서는 쿠버네티스가 압도적인 점유율을 자랑하게 되었다<br />
클라우드의 대명사인 AWS, Google Cloud, Azure 등 클라우드 환경에서도 쿠버네티스 관리형 서비스를 내놓은 것만 봐도 그 위상을 알 수 있다<br />
<br />
쿠버네티스 vs 도커 스웜</p>
<ul>
<li>쿠버네티스는 스웜 보다 더 많은 기능을 갖춘 컨테이너 <strong>오케스트레이션 시스템</strong> 으로 컨테이너 런타임을 다룰 수 있음</li>
</ul>
<h2 id="쿠버네티스-란-">쿠버네티스 란 ?</h2>
<p><strong>쿠버네티스는 컨테이너를 쉽고 빠르게 배포/확장(scale out) 하고 관리를 자동화 해주는 오픈소스 플랫폼이다</strong></p>
<ul>
<li>쿠버네티스라는 명칭은 키잡이(helmsman) / 파일럿(pilot)을 뜻하는 그리스어에서 유래 되었다고 함</li>
<li>주로 컨테이너 오케스트레이션 도구라고 불리며 구글 주도로 개발되었음</li>
<li>컨테이너들을 다루기 위한 API 및 CLI 환경을 지원함</li>
<li>단순히 컨테이너 관리 플랫폼을 넘어서 MSA, 클라우드 플랫폼을 지향하고 손쉽게 관리할 수 있는 그릇역할을 수행</li>
</ul>
<h2 id="쿠버네티스의-특징">쿠버네티스의 특징</h2>
<ul>
<li>서비스 디스커버리와 로드 밸런싱 : DNS 이름을 사용하거나 자체 IP 주소를 이용하여 컨테이너를 노출 가능함</li>
<li>스토리지 오케스트레이션 : 로컬 저장소, 클라우드 저장소 등을 연결하여 사용 가능함</li>
<li>자동화된 롤아웃과 롤백 : 쿠버네티스로 배포된 컨테이너는 원하는 상태 (desired state) 를 서술할 수 있으며 현재 상태를 원하는 상태가 되도록 한다<br />
ex) 배포시 마다 매번 새로운 컨테이너를 띄우도록 자동화하면 기존 컨테이너를 제거하고 새로운 컨테이너에 모든 리소스를 적용할 수 있다</li>
<li>자동화된 빈 패킹(bin packing) : 컨테이너화 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공함<br />
각 컨테이너가 필요로 하는 CPU 와 메모리를 쿠버네티스에게 지시하면 쿠버네티스는 컨테이너를 노드에 맞추어서 리소스를 잘 사용할 수 있도록 해줌</li>
<li>자동화된 복구(self-healing) : 쿠버네티스는 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며, 응답하지 않는 컨테이너를 죽이는 등 원하는 상태가 되도록 노력한다<br />
서비스가 준비가 되기 전까지 클라이언트에게 알려주지 않음</li>
<li>시크릿과 구성 관리 : Oauth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리할 수 있음<br />
컨테이너 이미지를 재구성하지 않고 스크릿 및 애플리케이션 구성을 배포 및 업데이트 가능함</li>
</ul>
<h2 id="쿠버네티스의-기본-개념">쿠버네티스의 기본 개념</h2>
<h3 id="desired-state">Desired State</h3>
<p><img src="/assets/images/posts/20200704/desired-state.png" alt="Desired_State" /></p>
<ul>
<li>desired state (원하는 상태) 는 쿠버네티스의 가장 중요한 개념중 하나로 관리자가 바라는 환경을 의마함</li>
<li>관리자는 원하는 서버의 대수, 포트 등의 상태를 정의할 수 있음</li>
<li>쿠버네티스는 현재 상태 (current state) 를 모니터링 하면서 관리자가 정의한 원하는 상태(desired state) 로 유지하려고 내부적으로 이러저런 처리를 수행함</li>
<li>이런 개념때문에 실제 배포시에도 어떤 동작을 명령하는 방식이 아닌 상태를 선언하는 방식을 사용한다</li>
<li>ex) nginx 를 실행하라는 명령(imperative) 대신에 nginx 컨테이너 1개를 유지해달라는 상태를 선언(declarative) 한다</li>
<li>이런 차이는 명령에서도 알 수 있음</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="c"># 명령 </span>
<span class="nv">$ </span>kubectl create <span class="c"># 원하는 상태 생성 </span>
</code></pre></div></div>
<p><strong>쿠버네티스의 핵심은 상태이며 쿠버네티스를 사용하려면 어떤 상태들이 있고 어떻게 상태를 선언해야 하는지 알아야 함</strong></p>
<h3 id="쿠버네티스-object">쿠버네티스 Object</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/kubernetes-objects/">공식_쿠버네티스 Object</a><br />
쿠버네티스는 상태를 나타내기 위해서 오브젝트를 이용하며 구체적으로는 아래와 같음</p>
<ul>
<li>어떤 컨테이너화된 애플리케이션이 동작 중인지 (+어느 노드인지)</li>
<li>그 애플리케이션이 사용가능한 리소스</li>
<li>그 애플리케이션의 재구동 정책, 업그레이드, 문제 발생시 정책 등 <br />
쿠버네티스에는 여러가지 상태를 나타내기 위한 여러 오브젝트를 제공하고 있으며 새로운 오브젝트 추가가 쉬워 확장성이 좋음</li>
</ul>
<p>쿠버네티스 오브젝트는 상태 외에도 명세(spec) 가 있는데 이것은 오브젝트에 대한 정보를 yaml 로 정의 하고 원하는 상태를 기술하는데 사용함</p>
<ul>
<li>오브젝트의 명세는 생성, 조회, 삭제로 관리할 수 있어서 REST API 로 쉽게 노출이 가능함</li>
</ul>
<blockquote>
<p>쿠버네티스 오브젝트는 다양한 종류가 있는데, 여기서는 자주 사용되는 몇 가지만 소개함</p>
</blockquote>
<h3 id="pod">Pod</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/workloads/pods">공식_쿠버네티스 Pod</a></p>
<p><img src="/assets/images/posts/20200704/node_pod.png" alt="node_pod" /></p>
<ul>
<li>파드는 쿠버네티스 애플리케이션의 배포가능한 가장 작은 단위로 한 개 이상의 컨테이너와 스토리지, 네트워크 속성이 있음</li>
<li>파드는 노드에 배치되는 컨테이너 그룹의 논리적인 개념이라고 보면됨</li>
<li>Pod 에 속한 컨테이너들은 Pod 의 네트워크와 스토리지를 공유하고 localhost 로 서로 접근이 가능함</li>
<li>Pod 의 네트워크 인터페이스에는 Node 의 IP 와 연결되기 위한 IP 가 존재함</li>
<li>실제 어플리케이션은 Pod 내부의 container 로 구동되기 때문에 외부에서 접근 할 수 있는 방법을 정의해야 한다</li>
</ul>
<h3 id="replicaset">ReplicaSet</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/workloads/controllers/replicaset/">공식_쿠버네티스 ReplicaSet</a><br />
Pod 를 여러 개 복제하여 생성하고 관리하는 오브젝트<br />
레플리카 세트를 이용하면 지정된 수의 레플리카 파드가 유지되도록 보장해줌<br />
일반적으로 레플리카 세트를 직접 사용하기 보다는 아래 나오는 디플로이먼트(Deployment) 를 사용함</p>
<blockquote>
<p>공식 도큐에서도 디플로이먼트 사용을 권장하고 있다</p>
</blockquote>
<h3 id="deployment">Deployment</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/workloads/controllers/deployment/">공식_쿠버네티스_Deployment</a> <br />
디플로이먼트는 레플리카 셋의 상위 개념으로 추가적인 유용한 기능들을 제공하는 오브젝트<br />
ㄴ 파드 개수를 유지 해줄 뿐만 아니라 배포 작업을 좀 더 세분화 하여 관리가 가능함<br />
ㄴ ex) 롤링 업데이트, 블루/그린 배포 등 <br />
디플로이먼트에서 의도하는 상태를 기술하면 디플로이먼트 컨트롤러는 현재 상태를 의도한 상태가 되도록 조정한다<br />
ㄴ 일반적으로 애플리케이션 배포에 기본이 되는 단위라고 볼 수 있음</p>
<p>디플로이먼트를 사용해서 애플리케이션을 배포하게 되면 새로운 레플리카 세트가 생성되며 기존에 있던 레플리카 세트는 교체된다</p>
<ul>
<li>pod 의 갯수만 수정한 경우(scale out)에는 기존 레플리카 세트를 그대로 유지한다</li>
</ul>
<p>디플로이먼트는 레플리카와 가장 큰 차이점으로 <strong>리비전</strong> 을 갖는데, 이는 배포에 대한 버전이라고 생각하면 된다 <br />
디플로이먼트가 업데이트 되면 리비전도 업데이트 된다 <br />
ㄴ ex) REVISION=1 -> REVISION=2<br />
롤백을 수행한 경우에 쿠버네티스는 이 리비전을 이용하여 롤백을 수행한다</p>
<h3 id="service">Service</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/services-networking/service/">공식_쿠버네티스_Service</a><br />
네트워크와 관련된 오브젝트로써 파드를 외부 네트워크와 연결해주고 여러 개의 Pod 를 바라보는 내부 로드 밸런서를 생성할 때 사용한다</p>
<p>말이 좀 어려운데, 왜 필요한지 먼저 살펴보자</p>
<ul>
<li>쿠버네티스의 파드는 비영구적인 리소스로 디플로이먼트를 이용하여 배포를 수행하면 동적으로 파드가 생성되거나 제거 될 수 있음</li>
<li>각 파드는 고유한 IP 주소를 갖지만, 디플로이먼트 업데이트를 수행하면 기존의 파드는 교체 된다</li>
<li>이것은 파드의 집합을 IP 만으로 찾을 수 없음을 의미한다 > 서비스 디스커버리 불가능</li>
</ul>
<p>서비스 오브젝트를 이용하면 클러스터 내부/외부에서 클러스터 상에 존재하는 파드 집합을 찾을 수 있도록 서비스 디스커버리 역할을 수행함 <br />
ㄴ 서비스 Object 는 일반적으로 파드 집합을 식별하기 위한 <strong>셀렉터</strong> 가 필요함<br />
ㄴ 헤드리스 서비스(headless service) 라는 셀렉터를 지정하지 않는 서비스도 존재함</p>
<p>서비스 오브젝트의 Type</p>
<ul>
<li>
<p>ClusterIP <br />
서비스 생성시 type 을 ClusterIP 로 지정하여 클러스터의 고유한 IP 를 지정 가능<br />
ㄴ type 의 기본값<br />
ClusterIP 는 클러스터 내부에 IP 를 노출시키며 외부로 부터는 접근 할 수 없다 <br />
클러스터 내부 노드나 파드에서 ClusterIP 를 이용해서 서비스에 연결되어 파드에 접근이 가능하다</p>
</li>
<li>
<p>NodePort<br />
고정 포트로 각 노드의 IP 에 서비스를 노출 시킬 수 있으며 외부에서 접근이 가능하다
ㄴ 외부에서 <노드 IP="">:<NodePort> 로 접근 가능
NodePort 타입은 자동으로 ClusterIP 가 생성됨
ㄴ 실제로 노드 1 에만 파드가 떠있고 노드 2 에는 파드가 없어도 노드2 IP 를 이용해서 접근해도 노드 1의 파드가 응답이 가능</NodePort></노드></p>
</li>
<li>
<p>LoadBalancer<br />
클라우드 공급자가 제공하는 로드 밸런서를 사용하여 서비스를 외부에 노출 시킴<br />
외부 로드 밸런서가 라우팅되는 NodePort 와 ClusterIP 가 자동으로 생성됨<br />
해당 로드 밸런서의 IP 를 이용해서 외부에서 접근이 가능함</p>
</li>
<li>
<p>ExternalName<br />
서비스를 ExternalName 값이랑 매칭하는 역할을 수행<br />
클러스터 내부에서 외부로 접근할 때 주로 사용됨<br />
이 서비스로 접근하면 설정한 CNAME 값으로 연결되어 클러스터 외부로 접근이 가능함<br />
내부에서 외부로 접근할 때 사용하는 값이기 때문에 <strong>셀렉터가 필요 없음</strong></p>
</li>
</ul>
<blockquote>
<p>Ingress 를 이용하여 서비스를 노출시킬 수 있는데, 서비스 유형은 아니지만 클러스터의 진입점 역할을 수행함</p>
</blockquote>
<h3 id="ingress">Ingress</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/services-networking/ingress/">공식_쿠버네티스_Ingress</a><br />
인그레스는 클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트로 일반적으로 HTTP 를 관리</p>
<p>인그레스의 간단한 예시<br />
<img src="/assets/images/posts/20200704/kubernetes_ingress.png" alt="kubernetes_ingress" /></p>
<ul>
<li>인그레스는 외부에서 서비스로 접속 가능한 URL, 로드밸런스 트래픽, SSL/TLS 종료, 이름 기반의 가상 호스팅을 제공하도록 구성 가능함</li>
<li>인그레스가 위의 기능들을 정의 해둔 자원이라면 인그레스 컨트롤러는 정의한 대로 동작하게 해주는 관리자</li>
<li>클라우드 상에서는 일반적으로 로드 밸런서 서비스들과 인그레스를 연동해 주지만, 직접 인그레스를 사용하기 위해서는 인그레스 컨트롤러를 인그레스와 직접 연동해주어야 함</li>
</ul>
<h3 id="volume">Volume</h3>
<p><a href="https://kubernetes.io/ko/docs/concepts/storage/volumes/">공식_쿠버네티스_Volume</a><br />
Volume 은 저장소와 관련된 오브젝트다</p>
<p>볼륨이 필요한 이유</p>
<ul>
<li>컨테이너의 특성 상 어떤 문제가 발생하여 컨테이너가 삭제되면 데이터도 같이 삭제된다</li>
<li>ex) 컨테이너 런타임에 생성된 로그 파일</li>
<li>컨테이너가 종료되더라도 유지해야 하는 파일을 저장하기 위해서 볼륨을 이용해야 함</li>
</ul>
<p>볼륨은 호스트 디렉토리를 그대로 사용할 수 있고 EBS 같은 스토리지를 동적으로 생성하여 사용 가능함</p>
<ul>
<li>임시 볼륨인 Pod 의 볼륨과 영구 저장용 볼륨인 퍼시스턴트 볼륨 등이 있음</li>
</ul>
<h2 id="래퍼런스">래퍼런스</h2>
<ul>
<li><a href="https://kubernetes.io/ko/docs/concepts/overview/what-is-kubernetes/">공식_쿠버네티스란 무엇인가?</a></li>
<li><a href="https://wikibook.co.kr/docker-kubernetes/">위키북스 도커/쿠버네티스를 활용한 컨테이너 개발입문</a></li>
<li><a href="https://subicura.com/2019/05/19/kubernetes-basic-1.html">subicura - 쿠버네티스 기본</a></li>
<li><a href="https://www.redhat.com/ko/topics/containers/what-is-container-orchestration">Redhat container orchestration</a></li>
<li><a href="https://velog.io/@modolee/kubernetes-newbie-guide-01">modolee 블로그 - kubernetes-newbie-guide-01</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/kubernetes-objects/">공식_쿠버네티스 Object</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/workloads/pods">공식_쿠버네티스 Pod</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/workloads/controllers/replicaset/">공식_쿠버네티스 ReplicaSet</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/workloads/controllers/deployment/">공식_쿠버네티스_Deployment</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/services-networking/service/">공식_쿠버네티스_Service_doc</a></li>
<li><a href="https://arisu1000.tistory.com/27838">https://arisu1000.tistory.com/27838</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/services-networking/ingress/">공식_쿠버네티스_Ingress</a></li>
<li><a href="https://kubernetes.io/ko/docs/concepts/storage/volumes/">공식_쿠버네티스_Volume</a></li>
</ul>nesskoungho632@gmail.com도커 실습 해보기2020-02-22T00:00:00+00:002020-05-31T00:00:00+00:00https://kimkoungho.github.io//devops/docker_example<h1 id="도커-설치">도커 설치</h1>
<p>도커 설치는 리눅스 기반에서 설치하는 것과 mac 이나 window 에서 설치하는 것에는 차이가 있다.</p>
<ul>
<li>실제로 리눅스 환경과 mac/window 도커 환경은 차이가 있다고 함
도커는 기본적으로 리눅스 컨테이너 기술이기 때문에 리눅스에서 설치 하는것을 설명하려고 함</li>
</ul>
<h2 id="도커-설치-1">도커 설치</h2>
<p>centos 7 에서 설치를 진행함</p>
<ul>
<li><a href="https://docs.docker.com/engine/install/centos/">centos 도커 설치</a></li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> yum-utils <span class="se">\</span>
device-mapper-persistent-data <span class="se">\</span>
lvm2
<span class="nv">$ </span><span class="nb">sudo </span>yum-config-manager <span class="se">\</span>
<span class="nt">--add-repo</span> <span class="se">\</span>
https://download.docker.com/linux/centos/docker-ce.repo
<span class="nv">$ </span><span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> docker-ce docker-ce-cli containerd.io
<span class="nv">$ </span><span class="nb">sudo </span>systemctl start docker
</code></pre></div></div>
<ul>
<li>이렇게 도커를 설치하고 나면 도커 데몬이 뜨게 된다</li>
</ul>
<p>리눅스 환경에서는 도커 데몬을 위한 유저를 추가해주어야 함</p>
<ul>
<li>도커 데몬은 root 권한이 필요한데 도커 유저인 docker 는 root 권한이 없기 때문에 추가가 필요</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 확인</span>
<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span> /var/run/docker.sock
srw-rw---- 1 root root 0 8월 3 14:49 /var/run/docker.sock
<span class="c"># 도커 유저 추가</span>
<span class="nv">$ </span><span class="nb">sudo </span>groupadd docker
<span class="nv">$ </span><span class="nb">sudo </span>systemctl restart docker
<span class="c"># docker 로 변경됨</span>
<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span> /var/run/docker.sock
srw-rw---- 1 root docker 0 8월 3 14:50 /var/run/docker.sock
<span class="c"># 도커 group 에 유저 추가</span>
<span class="nv">$ </span><span class="nb">sudo </span>usermod <span class="nt">-aG</span> docker me
<span class="c"># - me 는 현재 접속중인 사용자라고 생각하면 됨 </span>
<span class="c"># 그룹 확인</span>
<span class="nv">$ </span><span class="nb">cat</span> /etc/group | <span class="nb">grep</span> <span class="s2">"docker:"</span>
</code></pre></div></div>
<h2 id="맥-환경-설치">맥 환경 설치</h2>
<p>필자는 mac 환경이기 때문에 mac 환경 설치를 추가적으로 작성했다<br />
로컬에서 도커를 테스트 해보기 위해서는 설치가 필요하기 때문에 … <br />
mac 환경 설치는 아래 가이드에 따라서 설치 파일을 다운로드 하면된다</p>
<ul>
<li><a href="https://docs.docker.com/docker-for-mac/install/">맥 환경에서 도커 설치</a></li>
</ul>
<p>설치를 진행하고 나면 도커에 대한 환경 설정을 볼수 있음</p>
<ul>
<li>
<p>우측 상단에 도커 toolbar 가 표시됨
<img src="/assets/images/posts/20200222/docker_toolbar.png" alt="No Image" /></p>
</li>
<li>
<p>preferences 메뉴에서 도커 관련 설정을 할 수 있음
<img src="/assets/images/posts/20200222/dockerfile_image_container.png" alt="No Image" /></p>
</li>
</ul>
<h2 id="도커-설치-확인하기">도커 설치 확인하기</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker version
Client: Docker Engine - Community
Version: 19.03.5
API version: 1.40
Go version: go1.12.12
Git commit: 633a0ea
Built: Wed Nov 13 07:22:34 2019
OS/Arch: darwin/amd64
Experimental: <span class="nb">false
</span>Server: Docker Engine - Community
Engine:
Version: 19.03.5
API version: 1.40 <span class="o">(</span>minimum version 1.12<span class="o">)</span>
Go version: go1.12.12
Git commit: 633a0ea
Built: Wed Nov 13 07:29:19 2019
OS/Arch: linux/amd64
Experimental: <span class="nb">false
</span>containerd:
Version: v1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
</code></pre></div></div>
<h1 id="dockerfile">Dockerfile</h1>
<p>Dockerfile 은 도커 이미지를 만들때 사용하는 코드로 <a href="https://hub.docker.com/">도커 허브</a> 에 있는 도커 이미지들은 모두 Dockerfile 을 빌드한 결과 이미지이다</p>
<p><img src="/assets/images/posts/20200222/dockerfile_image_container.png" alt="No Image" /></p>
<ul>
<li>출처: <a href="https://geekflare.com/dockerfile-tutorial/https://geekflare.com/dockerfile-tutorial/">geekflare의 docker_tutorial</a>
위 이미지에서 알 수 있듯이 <strong>Dockerfile 은 도커 이미지 생성에 사용하는 템플릿 파일</strong></li>
<li><a href="https://kimkoungho.github.io/devops/docker_basic/">이전 포스팅</a>에도 설명했지만 도커는 Dockerfile 을 작성하여 코드로 관리하는 인프라를 구현하고 있음</li>
</ul>
<h2 id="dockerfile-작성하기">Dockerfile 작성하기</h2>
<p>Dockerfile 에 사용되는 명령어들을 살펴보자</p>
<h3 id="from">FROM</h3>
<p>현재 만들 이미지의 베이스 이미지를 지정하는 것으로 기본적으로 도커 허브에 지정된 이미지를 받아올 수 있다</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> centos:7</span>
</code></pre></div></div>
<ul>
<li>centos:7 만 설치된 도커 이미지를 base image 로 해서 이미지를 생성한다</li>
<li>:7 는 해당 이미지의 태그이며 일반적으로 버전정보를 나타낼때 사용된다</li>
</ul>
<h3 id="run">RUN</h3>
<p>Dockerfile 을 이용해서 이미지를 빌드할 때, 도커 컨테이너 내부에 실행할 명령어를 지정할 수 있다<br />
RUN 에는 2가지 형태가 존재하는데</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># format</span>
<span class="k">RUN </span><span class="s2">"command"</span>
<span class="c"># example</span>
<span class="k">RUN </span>yum <span class="nb">install</span> <span class="nt">-y</span> python
</code></pre></div></div>
<ul>
<li>/bin/bash -c command 형식으로 shell 명령을 실행하는 것</li>
</ul>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">RUN </span><span class="o">[</span><span class="s2">"executable"</span>, <span class="s2">"param1"</span>, <span class="s2">"param2"</span><span class="o">]</span>
<span class="c"># example</span>
<span class="k">RUN </span><span class="o">[</span><span class="s2">"/bin/bash"</span>, <span class="s2">"-c"</span>, <span class="s2">"echo hello"</span><span class="o">]</span>
</code></pre></div></div>
<ul>
<li>executable 은 명령어를 실행할 양식 (bash, zsh, etc)</li>
<li>executable 을 이용해서 별도의 python, ruby 스크립트를 실행 가능하다</li>
<li>param1, param2 는 해당 명령어를 실행하면서 전달할 파라미터</li>
</ul>
<p>RUN 명령어의 결과물은 이미지 내부에 저장된다</p>
<ul>
<li>도커에서는 이미지를 빌드 하는 시점과 컨테이너를 구동 하는 시점에 따라서 행위를 구분할 수 있으며 CMD, RUN 명령의 가장 큰 차이라고 보면 된다</li>
</ul>
<h3 id="cmd">CMD</h3>
<p>이미지를 도커 컨테이너로 실행하기 전에 먼저 실행할 명령어를 지정할 수 있다<br />
RUN 명령어는 이미지를 빌드하는 시점에 실행되는 반면에 CMD 는 컨테이너를 실행할 때 명령어를 수행한다<br />
주의 해야할 점은 Dockerfile 에서 CMD 는 1개만 적용되며 여러 개를 지정한 경우 마지막 CMD 만이 적용된다</p>
<p>CMD 에는 3가지 명령어 형식이 있음</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 가장 기본 형식 </span>
<span class="k">CMD</span><span class="s"> ["java ", "param1", "param2"]</span>
<span class="c"># 쉘 명령어 실행 </span>
<span class="k">CMD</span><span class="s"> command param1 param2</span>
<span class="c"># ENTRYPOINT 의 매개변수 전달 </span>
<span class="k">CMD</span><span class="s"> ["", ""]</span>
</code></pre></div></div>
<ul>
<li>컨테이너에 기본 값을 지정하기 위해서 일반적으로 사용되며, 컨테이너 구동시 실행할 어플리케이션 시작 명령을 포함할 수 있다</li>
</ul>
<h3 id="entrypoint">ENTRYPOINT</h3>
<p>CMD 와 같이 도커 컨테이너를 실행하기 전에 실행할 명령어 <br />
CMD 와 마찬가지로 1번만 수행할 수 있음</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 바람직한 형태 </span>
<span class="k">ENTRYPOINT</span><span class="s"> ["executable", "param1", "param2"]</span>
<span class="c"># 쉘 명령어 형태 </span>
<span class="k">ENTRYPOINT</span><span class="s"> command param1 param2</span>
</code></pre></div></div>
<p>CMD 와의 차이점은 ENTRYPOINT 는 CMD 를 인자로 받아 사용할 수 있는 스크립트</p>
<h3 id="label">LABEL</h3>
<p>Dockerfile 로 생성하려는 이미지에 메타 정보를 추가할 때 사용<br />
key 와 value 의 쌍으로 표현함</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">LABEL</span><span class="s"> <key>=<value> <key>=<value></span>
<span class="k">LABEL</span><span class="s"> "com.example.vendor"="ACME Incorporated"</span>
</code></pre></div></div>
<ul>
<li>하나의 이미지에 여러개의 라벨을 지정 가능함</li>
<li>부모 이미지의 라벨은 자식 이미지가 상속</li>
</ul>
<p>라벨 출력방법</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker image inspect <span class="nt">--format</span><span class="o">=</span><span class="s1">''</span> myimage
</code></pre></div></div>
<h3 id="env">ENV</h3>
<p>Dockerfile 에서 사용할 환경 변수를 지정<br />
2 가지 형식으로 모두 지정가능</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ENV</span><span class="s"> <key> <value></span>
<span class="k">ENV</span><span class="s"> <key>=<value></span>
<span class="k">ENV</span><span class="s"> JAVA_HOME /usr/lib/java</span>
</code></pre></div></div>
<ul>
<li>해당 환경 변수는 자동으로 등록되는 특징이 있음</li>
<li>도커 컨테이너 구동후 echo $JAVA_HOME 을 입력하면 /usr/lib/java 가 출력됨</li>
</ul>
<h3 id="copy">COPY</h3>
<p>파일을 도커 컨테이너 내부로 복사하는 명령이다</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">COPY</span><span class="s"> [--chown=<user>:<group>] <src>... <dest></span>
<span class="c"># 공백이 포함된 경우 </span>
<span class="k">COPY</span><span class="s"> [--chown=<user>:<group>] ["<src>",... "<dest>"]</span>
</code></pre></div></div>
<ul>
<li>파일, 디렉토리를 복사하는 명령어</li>
</ul>
<h3 id="add">ADD</h3>
<p>COPY 와 같이 파일을 복사하는데 사용하는 명령으로 URL 및 압축파일도 지정이 가능하다</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ADD</span><span class="s"> [--chown=<user>:<group>] <src>.. <dest></span>
<span class="c"># 공백이 필요한 경우 </span>
<span class="k">ADD</span><span class="s"> [--chown=<user>:<group>] ["<src>".. "<dest>"]</span>
<span class="k">ADD</span><span class="s"> test.txt /absoluteDir/</span>
</code></pre></div></div>
<ul>
<li>이미지를 빌드 환경에 있는 파일을 이미지로 복사하는 것</li>
</ul>
<h3 id="expose">EXPOSE</h3>
<p>도커가 컨테이너의 런타임에 지정된 네트워크 포트에서 수신 대기를 한다는 것을 나타냄</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">EXPOSE</span><span class="s"> <port> [<port>/<protocol>...]</span>
<span class="k">EXPOSE</span><span class="s"> 80/tcp</span>
</code></pre></div></div>
<ul>
<li>프로토콜의 기본 값은 tcp</li>
<li>EXPOSE 명령은 실제로 해당 포트를 띄우는 것이 아니라 이미지를 만드는 사람과 컨테이너를 띄우는 사람간의 문서로써의 역할</li>
</ul>
<p>실제로 포트를 바인딩하는 예</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">-p</span> 80:80/tcp
</code></pre></div></div>
<ul>
<li>호스트 os 의 80 포트를 컨테이너 80 포트로 바인딩</li>
</ul>
<h3 id="volume">VOLUME</h3>
<p>지정된 이름으로 마운트 포인트를 생성하고 호스트 OS 에서 마운트된 볼륨을 보유하는 것으로 표시</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">VOLUME</span><span class="s"> ["/data"]</span>
<span class="k">VOLUME</span><span class="s"> ["/var/log/"]</span>
</code></pre></div></div>
<h3 id="workdir">WORKDIR</h3>
<p>작업 디렉토리를 설정하는 것으로 WORKDIR 을 지정하면 RUN, CMD, ENTRYPOINT, COPY, ADD 등의 명령이 영향을 받는다</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">WORKDIR</span><span class="s"> /path/to/workdir</span>
<span class="k">WORKDIR</span><span class="s"> /a</span>
<span class="k">WORKDIR</span><span class="s"> b</span>
<span class="k">WORKDIR</span><span class="s"> c</span>
<span class="k">RUN </span><span class="nb">pwd</span>
<span class="c"># /a/b/c 가 출려됨 </span>
</code></pre></div></div>
<h3 id="stopsignal">STOPSIGNAL</h3>
<p>컨테이너로 보내질 시스템 호출 신호를 설정<br />
해당 신호는 커널의 syscall 테이블에 위치</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">STOPSIGNAL</span><span class="s"> signal</span>
</code></pre></div></div>
<h3 id="user">USER</h3>
<p>도커를 실행하는 사용자를 지정하는 명령어</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">USER</span><span class="s"> <user>[:<group>]</span>
<span class="k">USER</span><span class="s"> <UID>[:<GID>]</span>
</code></pre></div></div>
<ul>
<li>사용자 지정후 RUN, CMD, ENTRYPOINT 등의 명령은 해당 사용자의 권한으로 실행됨</li>
<li>컨테이너 구동시에도 해당 사용자가 기본 사용자가 된다</li>
</ul>
<h3 id="onbuild">ONBUILD</h3>
<p>빌드된 이미지를 기반으로 하는 다른 이미지가 Dockerfile 로 생성될 때 실행할 명령어를 추가</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ONBUILD <INSTRUCTION>
</code></pre></div></div>
<ul>
<li>상속 구조를 갖는 이미지들이 있을 경우 base 가 되는 이미지에서 미리 수행할 명령을 등록하는 것</li>
<li>하나의 트리거로써 동작함</li>
<li>ONBUILD 명령은 해당 이미지가 FROM 절에 있을때 실행되는 명령
<ul>
<li>이미지 자체를 빌드할 때는 수행되지 않음</li>
</ul>
</li>
</ul>
<h2 id="dockerfile-작성-예제">Dockerfile 작성 예제</h2>
<p>centos 기반으로 ssh 접속이 가능한 도커 이미지를 생성한다</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> centos:7</span>
<span class="c">### user</span>
<span class="k">ARG</span><span class="s"> user=deploy</span>
<span class="k">ARG</span><span class="s"> group=deploy</span>
<span class="k">ARG</span><span class="s"> uid=1000</span>
<span class="k">ARG</span><span class="s"> gid=1000</span>
<span class="k">ARG</span><span class="s"> USER_HOME=/home/deploy</span>
<span class="c"># user home</span>
<span class="k">RUN </span><span class="nb">mkdir</span> <span class="nt">-p</span> <span class="k">${</span><span class="nv">USER_HOME</span><span class="k">}</span> <span class="se">\
</span> <span class="o">&&</span> <span class="nb">chown</span> <span class="k">${</span><span class="nv">uid</span><span class="k">}</span>:<span class="k">${</span><span class="nv">gid</span><span class="k">}</span> <span class="k">${</span><span class="nv">USER_HOME</span><span class="k">}</span> <span class="se">\
</span> <span class="o">&&</span> groupadd <span class="nt">-g</span> <span class="k">${</span><span class="nv">gid</span><span class="k">}</span> <span class="k">${</span><span class="nv">group</span><span class="k">}</span> <span class="se">\
</span> <span class="o">&&</span> useradd <span class="nt">-d</span> <span class="s2">"</span><span class="k">${</span><span class="nv">USER_HOME</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-u</span> <span class="k">${</span><span class="nv">uid</span><span class="k">}</span> <span class="nt">-g</span> <span class="k">${</span><span class="nv">gid</span><span class="k">}</span> <span class="nt">-m</span> <span class="nt">-s</span> /bin/bash <span class="k">${</span><span class="nv">user</span><span class="k">}</span>
<span class="c"># sudo user</span>
<span class="k">RUN </span>yum <span class="nb">install</span> <span class="nt">-y</span> <span class="nb">sudo</span> <span class="se">\
</span> <span class="o">&&</span> <span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">user</span><span class="k">}</span><span class="s2"> ALL=(ALL) NOPASSWD:ALL"</span> <span class="o">>></span> /etc/sudoers
<span class="c">### ssh</span>
<span class="k">RUN </span>yum <span class="nb">install</span> <span class="nt">-y</span> epel-release <span class="se">\
</span> <span class="o">&&</span> yum <span class="nb">install</span> <span class="nt">-y</span> openssh-server openssh-clients net-tools <span class="nb">hostname</span> <span class="se">\
</span> <span class="o">&&</span> yum clean all
<span class="c"># 비밀번호 인증을 비활성화</span>
<span class="k">RUN </span><span class="nb">sed</span> <span class="nt">-i</span> <span class="nt">-e</span> <span class="s1">'s~^PasswordAuthentication yes~PasswordAuthentication no~g'</span> /etc/ssh/sshd_config
<span class="c">## create ssh key </span>
<span class="k">RUN </span>ssh-keygen <span class="nt">-q</span> <span class="nt">-b</span> 1024 <span class="nt">-N</span> <span class="s1">''</span> <span class="nt">-t</span> rsa <span class="nt">-f</span> /etc/ssh/ssh_host_rsa_key <span class="se">\
</span> <span class="o">&&</span> ssh-keygen <span class="nt">-q</span> <span class="nt">-b</span> 1024 <span class="nt">-N</span> <span class="s1">''</span> <span class="nt">-t</span> dsa <span class="nt">-f</span> /etc/ssh/ssh_host_dsa_key <span class="se">\
</span> <span class="o">&&</span> ssh-keygen <span class="nt">-q</span> <span class="nt">-b</span> 521 <span class="nt">-N</span> <span class="s1">''</span> <span class="nt">-t</span> ecdsa <span class="nt">-f</span> /etc/ssh/ssh_host_ecdsa_key <span class="se">\
</span> <span class="o">&&</span> ssh-keygen <span class="nt">-q</span> <span class="nt">-b</span> 521 <span class="nt">-N</span> <span class="s1">''</span> <span class="nt">-t</span> ed25519 <span class="nt">-f</span> /etc/ssh/ssh_host_ed25519_key
<span class="c"># sudo 명령어 경로 추가 - secure_path 주석처리 </span>
<span class="k">RUN </span><span class="nb">echo</span> <span class="s1">'Defaults env_keep += "PATH PROMPT_COMMAND"'</span> <span class="o">>></span> /etc/sudoers
<span class="c"># ssh user</span>
<span class="k">RUN </span><span class="nb">mkdir</span> <span class="nt">-p</span> <span class="k">${</span><span class="nv">USER_HOME</span><span class="k">}</span>/.ssh <span class="se">\
</span> <span class="c">## 생성된 키를 인증할 수 있는 키 목록에 추가</span>
&& cat /etc/ssh/ssh_host_rsa_key >> ${USER_HOME}/.ssh/id_rsa \
&& cat /etc/ssh/ssh_host_rsa_key.pub >> ${USER_HOME}/.ssh/authorized_keys \
&& chown -R ${uid}:${gid} ${USER_HOME} \
&& chmod 600 ${USER_HOME}/.ssh/id_rsa \
&& chmod 644 ${USER_HOME}/.ssh/authorized_keys
<span class="c">### utility lib</span>
<span class="k">RUN </span>yum <span class="nt">-y</span> <span class="nb">install </span>bash-completion curl <span class="nb">hostname </span>vim-enhanced wget
<span class="k">EXPOSE</span><span class="s"> 22</span>
<span class="k">USER</span><span class="s"> ${user}</span>
<span class="k">CMD</span><span class="s"> ["sudo", "/usr/sbin/sshd", "-D"]</span>
</code></pre></div></div>
<ul>
<li><a href="https://github.com/kimkoungho/dockerfile_project/blob/master/centos7_ssh/Dockerfile">github</a></li>
</ul>
<h2 id="dockerfile-실행">Dockerfile 실행</h2>
<ul>
<li>이미지 빌드하기</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 형식: docker image build -t 이미지명[:태그명] Dockerfile 경로</span>
<span class="nv">$ </span>docker build <span class="nt">-t</span> centos7_ssh:latest <span class="nb">.</span>
<span class="c"># -t 이미지_이름:버전 </span>
<span class="c"># 태그명을 지정하지 않으면 기본적으로 latest 가 됨 </span>
<span class="c"># . 은 현재 디렉토리를 의미</span>
<span class="nv">$ </span>docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7_ssh latest 47e9c5c43729 2 months ago 553MB
</code></pre></div></div>
<ul>
<li>컨테이너 뛰워보기</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker run <span class="nt">--name</span> node1 <span class="se">\</span>
<span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">-d</span> centos7_ssh
<span class="nv">$ </span>docker run <span class="nt">--name</span> node2 <span class="se">\</span>
<span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">-d</span> centos7_ssh
<span class="c"># --rm 은 컨테이너 종료시 자동으로 삭제 하는 옵션</span>
<span class="c"># -d 는 백그라운드로 컨테이너를 구동 </span>
<span class="nv">$ </span>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f73276ed5a2d centos7_ssh <span class="s2">"sudo /usr/sbin/sshd…"</span> 2 seconds ago Up 1 second 22/tcp node2
d41861cf9683 centos7_ssh <span class="s2">"sudo /usr/sbin/sshd…"</span> 6 seconds ago Up 6 seconds 22/tcp node1
</code></pre></div></div>
<ul>
<li>실제 접속을 해보자</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># node1 에 bash 로 접속 </span>
<span class="nv">$ </span>docker <span class="nb">exec</span> <span class="nt">-it</span> node1 /bin/bash
bash-4.2<span class="err">$</span>
<span class="c"># 배시 를 새창으로 띄워서 </span>
<span class="c"># node2 에 bash 로 접속 </span>
<span class="nv">$ </span>docker <span class="nb">exec</span> <span class="nt">-it</span> node2 /bin/bash
bash-4.2<span class="err">$</span>
</code></pre></div></div>
<ul>
<li>ssh 로 접속해보기
node1 에서 node2 로 ssh 접속 <br />
먼저 node2 의 IP 를 확인</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash-4.2<span class="nv">$ </span>ifconfig
eth0: <span class="nv">flags</span><span class="o">=</span>4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.3 netmask 255.255.0.0 broadcast 172.17.255.255
...
</code></pre></div></div>
<p>node1 에서 node2 로 접속</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash-4.2<span class="nv">$ </span>ssh 172.17.0.3
The authenticity of host <span class="s1">'172.17.0.3 (172.17.0.3)'</span> can<span class="s1">'t be established.
ECDSA key fingerprint is SHA256:CEoPcuVROOzbABUO5CCsEf0KI+58qxIagaizYTblLHU.
ECDSA key fingerprint is MD5:12:0b:86:9a:fb:db:de:6f:81:46:1b:47:1b:bc:79:b6.
Are you sure you want to continue connecting (yes/no)?
</span></code></pre></div></div>
<ul>
<li>known_hosts 에 추가할 것인지 묻는데 yes 때리면 됨</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-bash-4</span>.2<span class="nv">$ </span>ifconfig
eth0: <span class="nv">flags</span><span class="o">=</span>4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.3 netmask 255.255.0.0 broadcast 172.17.255.255
<span class="nt">-bash-4</span>.2<span class="nv">$ </span><span class="nb">exit
</span>bash-4.2<span class="err">$</span>
</code></pre></div></div>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://wikibook.co.kr/docker-kubernetes/">위키북스 도커/쿠버네티스를 활용한 컨테이너 개발입문</a> 2장</li>
<li><a href="https://docs.docker.com/engine/reference/builder/">도커 dockerfile 래퍼런스</a></li>
</ul>nesskoungho632@gmail.comRPC2020-01-27T00:00:00+00:002020-02-01T00:00:00+00:00https://kimkoungho.github.io//network/rpc<h1 id="rpc-remote-procedure-call">RPC (Remote Procedure Call)</h1>
<p>RPC는 <strong>서버-클라이언트 구조 애플리케이션에서 원격에 있는 프로시저를 실행할 수 있게 하는 통신기술</strong>로 개발자는 애플리케이션이 로컬인지 원격인지에 관계없이 프로 시저를 호출함으로써 하나의 시스템 처럼 동작할 수 있는 기술이다. RPC 는 LPC(Local Procedure Call) 의 확장 개념으로 기술적으로는 프로토콜이 아니며 단지 서버-클라이언트와 같은 분산 시스템을 구성하는 일반적인 메커니즘으로 봐야한다. RPC 프로토콜은 TCP와 같은 표준 프로토콜이 아니며 RPC 가 사용되는 기능에 따라 모두 다르게 동작한다.<br />
즉, RPC 프로토콜은 표준이 없기 때문에 데이터 통신을 위한 추상화된 프르토콜로써 실제 데이터 전송을 위해서 TCP or UDP 를 사용하여 통신한다. 여러 대의 컴퓨터에 프로시져를 분산하고, 결과를 취합하는 방식으로 <strong>분산 시스템</strong>에서 사용가능하다. <br />
RPC 는 동기 서버, 비동기 서버, 스트리밍 서버에 모두 사용된다. 특정 프로토콜을 이용한 기술적인 개념이 아닌 <strong>원격 프로시저를 호출할 수 있다</strong>는 키워드에 집중하 면 될 듯 하다.</p>
<h2 id="rpc-동작방식">RPC 동작방식</h2>
<p>RPC 는 서버/클라이언트 모두 3개의 layer 로 나눌수 있다.</p>
<ul>
<li>Caller/Callee: 클라이언트/서버 비지니스 로직을 작성하는 Layer</li>
<li>(Client/Server) Stub: parameter 를 marshelling 하여 message 로 변환하거나 message 를 unmarshelling 하여 parameter 로 변환하는 작업을 수행하는 Layer</li>
<li>RPC Protocol: RPC Runtime 이라고도 불리는 RPC Protocol 은 RPC 메커니즘의 기초가 되는 네트워크 통신을 처리하는 라이브러리로 RPC 호출 과정에서 클라이언트/서버 런타임 시스템 코드를 바인딩하고 적절한 프로토콜을 통해 통신을 수행하는 Layer
<ul>
<li>통신에 대한 오류처리도 담당</li>
</ul>
</li>
</ul>
<p><img src="/assets/images/posts/20200127/rpc_mechanism.png" alt="No Image" /> <br />
출처: <a href="https://book.systemsapproach.org/e2e/rpc.html">네트워크 ebook A System Approach RPC</a></p>
<ol>
<li>Caller 는 클라이언트 스텁에 매개변수를 갖고 호출</li>
<li>클라이언트 스텁은 변수를 marshalling 하여 요청 메시지로 변환하고 RPC 프로토콜을 호출</li>
<li>RPC 프로토콜은 요청 메시지를 서버 시스템으로 전송</li>
<li>서버의 RPC 프로토콜은 서버 스텁에 요청 메시지를 전달</li>
<li>서버 스텁은 요청 메시지를 unmarshalling 하여 변수로 변환하고 Callee 에 전달</li>
<li>Callee 는 요청에 대한 처리를 수행하고 응답 값을 서버 스텁으로 리턴</li>
<li>서버 스텁은 응답 값을 marshalling 하여 응답 메시지를 RPC 프로토콜을 통해 클라이언트로 반환</li>
<li>클라이언트 스텁은 응답 메시지를 unmarshalling 하여 응답 값을 Caller 로 반환</li>
</ol>
<h2 id="marshalling--unmarshalling---서버-클라이언트-환경의-차이">marshalling / unmarshalling - 서버-클라이언트 환경의 차이</h2>
<p>RPC 는 서버-클라이언트 환경에서 실행되기 때문에 서버와 클라이언트 환경에 종속적이다. 즉, 네트워크 상에 전송될 데이터의 형식과 서버, 클라이언트에서 처리할 데이터의 형식을 미리 정의해야 한다. 서버와 클라이언트는 서로 다른 시스템일 수 있는데 이런 경우 데이터 포맷도 달라질 수 있는 문제가 있기 때문이다.<br />
대표적인 예로 Unix 시스템의 RISC 프로세서 계열 에서 사용하 는 Big Endian 과 인텔 계열 프로세서에서 주로 사용하되는 Little Endian 이다. Little Endian 은 메모리 시작 주소가 하위 바이트부터 기록되는데, Big Endian 은 시 작 주소가 상위 바이트부터 기록된다.</p>
<ul>
<li>marshalling : marshalling 은 데이터를 네트워크 상에 전송할 형식으로 변환하는 작업을 말하며 java serialize 가 대표적인 예라고 할 수 있다</li>
<li>unmarshalling : unmarshalling 은 네트워크에서 받은 데이터를 다시 어플리케이션에서 사용할 데이터 형식으로 변환하는 작업으로 java deserialize 가 대표적인 예다</li>
</ul>
<h2 id="binding---rpc-에서는-클라이언트와-서버는-어떻게-연결할까">Binding - RPC 에서는 클라이언트와 서버는 어떻게 연결할까?</h2>
<p>클라이언트가 서버에 프로시저를 호출하기 위해서는 <strong>요청을 보낼 서버의 주소와 서버에서 동작 중인 프로세스</strong>를 식별해야 한다. 일반적으로 서버 내부에는 여러 프로 세스가 동작하는데, RPC 서버 프로시저를 식별하기 위해서 PORT 번호를 이용할 수 있다. 클라이언트에서 서버의 주소를 찾는 방법은 서버의 주소를 어떻게 지정하는지에 따라 <strong>static binding, dynamic binding</strong> 으로 나눌 수 있다.</p>
<ul>
<li>static binding
서버 주소를 hard coding 하는 방법<br />
단순하고 효율적이지만 유연하지 않음<br />
서버의 주소 변경시 마다 클라이언트 스텁을 매번 재컴파일 해야하는 문제</li>
<li>dynamic binding
주소 변경에 매우 유연함<br />
네트워크 네임 서버 필요<br />
여러 서버로 구성된 경우 추가적인 load balancing 필요</li>
</ul>
<p><img src="/assets/images/posts/20200127/RPC DYNAMIC_BINDING.jpg" alt="No Image" /> <br />
출처: <a href="https://www.slideshare.net/sandpoonia/5-26928882">slideshare Distributed Operating Systems</a></p>
<h2 id="rpc-는-왜-사용할까-">RPC 는 왜 사용할까 ?</h2>
<p>우리는 보편적으로 REST 를 이용해서 서버와 클라이언트 통신을 수행하고 있다.</p>
<ul>
<li>사실 필자도 REST 만을 이용해서 개발을 하다가 이직을 하면서 RPC 로 구성된 어플리케이션을 개발하면서 RPC 에 대해서 찾아보게 되었다
해당 내용은 RPC 구현체 중 하나인 Aphache Thrift 를 비교한 글에서 찾을 수 있었는데</li>
</ul>
<p><img src="/assets/images/posts/20200127/comapre_thrift.png" alt="No Image" /></p>
<ul>
<li>y: 서비스를 1백만번 호출하는 시간 (초)
출처: <a href="https://www.adaltas.com/en/2017/10/28/apache-thrift-vs-rest/">adaltas 의 apache thrift vs rest</a></li>
</ul>
<p>위 그림에서는 REST 를 이용한 HTTP 통신은 일반적인 웹 환경에서는 적합하지만 성능이슈가 있다는 것을 지적하고 있다. <br />
예를들어 사내 서비스를 개발할 때, 통신 성능이 중요하다면 RPC 를 선택하는 것도 좋은 방법이라고 주장한다.</p>
<ul>
<li>이렇게 RPC 가 REST 비해 성능이 좋은 이유는 apache thrift 가 내부적으로 빠른 binary serailize 를 구현하고 있기 때문이라고 함</li>
</ul>
<h2 id="결론">결론</h2>
<p>실제로 구글도 내부 서비스에서 <a href="https://developers.google.com/protocol-buffers">프로토콜 버퍼</a> 를 사용하고 있는데, 프로토콜 버퍼도 RPC 기술 중 하나이다. 사실 위에서 설명한 apache thrift 는 업데이트가 거의 중단된 서비스 인데 비해 프로토콜 버퍼는 지속적으로 업데이트 되고 있다.<br />
이번 기회에 서버-클라이언트 간 통신을 위해서 REST vs RPC 를 잘 비교해서 사용해보는 것이 좋겠다는 생각을 하게 됨</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://www.cs.princeton.edu/~wlloyd/classes/599s15/slides/2_RPC_and_MapReduce.pdf">Wyatt Lloyd 의 분산 시스템 RPC 와 MapReduce</a></li>
<li><a href="https://book.systemsapproach.org/e2e/rpc.html">네트워크 ebook A System Approach RPC</a></li>
<li><a href="https://www.slideshare.net/sandpoonia/5-26928882">slideshare Distributed Operating Systems</a></li>
<li><a href="https://www.joinc.co.kr/w/Site/Network_Programing/Documents/RPC">joinc RPC</a></li>
<li><a href="https://www.geeksforgeeks.org/remote-procedure-call-rpc-in-operating-system/">geeksforgeeks remote-procedure-call</a></li>
<li><a href="https://nesoy.github.io/articles/2019-07/RPC">nesoy RPC</a></li>
<li><a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html">Dave Marshall RPC</a></li>
<li><a href="https://www.slideshare.net/sandpoonia/5-26928882">Poonia 의 Distributed Operating Systems</a></li>
<li><a href="https://ko.wikipedia.org/wiki/%EC%97%94%EB%94%94%EC%96%B8">Wiki 엔디언</a></li>
<li><a href="https://www.programmableweb.com/news/rest-losing-its-flair-rest-api-alternatives/analysis/2013/12/19">ProgrammalbeWeb 기사 rest-losing-its-flair-rest-api-alternatives</a></li>
<li><a href="https://www.adaltas.com/en/2017/10/28/apache-thrift-vs-rest/">adaltas 의 apache thrift vs rest</a></li>
<li><a href="https://developers.google.com/protocol-buffers">프로토콜 버퍼</a></li>
</ul>nesskoungho632@gmail.comDocker 란?2019-10-29T00:00:00+00:002022-01-30T00:00:00+00:00https://kimkoungho.github.io//devops/docker_basic<h1 id="도커란">도커란?</h1>
<p><a href="https://ko.wikipedia.org/wiki/%EB%8F%84%EC%BB%A4_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4)">위키 도커</a> 정의에 따르면, 도커는 <strong>컨테이너 가상화 기술을 사용하는 플랫폼</strong> 으로 리눅스의 응용 프로그램들을 컨테이너라는 독립된 공간에 배치시키는 작업을 자동화하는 오픈소스 프로젝트이다.</p>
<h1 id="도커의-역사">도커의 역사</h1>
<p>dotCloud 라는 PaaS 기업의 창업자인 Solomon Hykes 가 사내 프로젝트였던 도커를 2013년 3월 산타클라라에서 열린 Pycon Conference 에서 <a href="https://www.youtube.com/watch?v=wW9CAH9nSLs&feature=youtu.be">The future of Linux Containers</a> 세션을 발표하면서 알려졌음<br />
발표 이후 도커가 엄청난 인기를 끌면서 회사 이름을 dotCloud 에서 Docker Inc 로 바꾸고 2014년 6월 docker 1.0 을 발표함</p>
<p>현재 Solomon Hykes 는 Docker 를 퇴사 했는데, 창립자이면서 도커를 퇴사하는 Solomon Hykes 심정을 도커 블로그에 남겼다.</p>
<ul>
<li><a href="https://www.docker.com/blog/au-revoir/">Solomon Hykes 가 퇴사하면서 남긴 포스팅</a></li>
</ul>
<h1 id="컨테이너란">컨테이너란?</h1>
<p><a href="https://www.docker.com/resources/what-container">도커 레퍼런스 what-container</a> 에 따르면, 컨테이너는 애플리케이션의 코드와 구동 환경을 추상화한 논리적인 패키지 라고 설명하고 있는데, 쉽게 말하면 <strong>실행가능한 격리된 공간</strong> 이다.</p>
<h2 id="vm-vs-도커-container">VM vs 도커 Container</h2>
<p>도커와 VM은 모두 가상화 기술을 이용한 소프트웨어라는 공통점이 있지만 <strong>가상화를 위해 어떤 기술을 사용하는지, 어떤 시스템 레벨을 가상화하는지</strong>에 대한 차이점이 존재한다.<br />
VM 에서 사용하는 하이퍼바이저 기반 가상화는 추가적으로 게스트 OS를 설치해야 하는 오버헤드가 존재했다. LXC(Linux Containers) 에서는 이런 문제를 해결하기 위해서 <strong>프로세스를 격리</strong> 하는 기법을 이용하여 VM과 유사한 역할을 하는 격리된 공간을 컨테이너로 정의한다.</p>
<ul>
<li>LXC(Linux Containers) 는 도커 이전에 컨테이너 기반 가상화를 지원하는 리눅스의 프로젝트</li>
<li>도커도 LXC 기반으로 개발되었다가 도커 0.9 버전에서 <a href="https://github.com/docker/libcontainer">libcontainer</a> 라는 자체적인 컨테이너 기술을 사용하면서 LXC 없이도 동작할 수 있게 되었다. 도커 1.11 버전에서는 libcontainer 가 <a href="https://github.com/opencontainers/runc">runc</a> 프로젝트에 합쳐졌다.</li>
<li>runc 는 도컨 엔진의 런타임 모듈로 호스트 OS 커널과 통신한다.</li>
</ul>
<p><img src="/assets/images/posts/20191029/docker_vs_vm.png" alt="No Image" /> <br />
출처: <a href="https://www.docker.com/resources/what-container">도커 레퍼런스 what-container</a></p>
<ul>
<li>VM 의 하이퍼바이저는 하드웨어를 에뮬레이트 하는 방법을 사용하기 때문에 추가적인 게스트 OS 가 필요</li>
<li>VM 의 호스트 OS 에 관련없이 독립적인 게스트 OS 를 구축할 수 있음</li>
<li>도커 엔진은 호스트 OS 위에서 동작하며 추가적인 게스트 OS 없이 프로세스를 격리하여 컨테이너의 독립성을 보장</li>
<li>도커의 컨테이너는 호스트 OS 커널(리눅스 커널)을 공유함으로써 기존의 VM 방식에 비해서 빠르고 가볍게 동작 가능</li>
<li>하지만 도커는 호스트 OS 커널을 공유하기 때문에 별도의 OS 환경을 구축할 수 없음</li>
</ul>
<blockquote>
<p>가상화에 대해서 추가적인 내용은 이전 포스트인 <a href="https://kimkoungho.github.io/virtualization/">가상화 포스팅</a>을 참고하자.</p>
</blockquote>
<h3 id="컨테이너-격리하는-메커니즘">컨테이너 격리하는 메커니즘</h3>
<p>동일한 OS 에서 프로세스를 어떻게 격리할까 ?<br />
2가지 메커니즘으로 가능함<br /></p>
<ul>
<li>리눅스 네임스페이스로 각 프로세스가 시스템(파일, 프로세스, 네트워크 인터페이스, 호스트 이름 등)에 대한 독립된 뷰만 볼 수 있도록 함</li>
<li>리눅스 컨트롤 그룹(cgroups) 으로 프로세스가 사용할 수 있는 리소스(CPU, 메모리, 네트워크 대역폭 등)의 양을 제한함</li>
</ul>
<h3 id="리눅스-네임스페이스로-프로세스-격리">리눅스 네임스페이스로 프로세스 격리</h3>
<ul>
<li>리눅스 시스템은 초기 구동 시 하나의 네임스페이스가 존재</li>
<li>파일 시스템, 프로세스 ID, 사용자 ID, 네트워크 인터페이스 등 모든 시스템 리소스는 하나의 네임스페이스에 포함됨</li>
<li>프로세스는 네임스페이스에서 실행되며 프로세스는 해당 네임스페이스에 리소스만 접근이 가능</li>
<li>프로세스는 여러 네임스페이스에 포함될 수 있음</li>
</ul>
<p>네임스페이스 종류<br /></p>
<ul>
<li>마운트(mnt)</li>
<li>프로세스 ID(pid)</li>
<li>프로세스 간 통신(ipc)</li>
<li>호스트와 도메인 이름(uts)</li>
<li>사용자 ID(user)</li>
</ul>
<p>각 네임스페이스는 특정 리소스 그룹을 격리하는 데 사용된다.<br />
ex) UTS 는 실행 중인 프로세스가 사용할 호스트 이름과 도메인을 결정하는데, <br />
2개의 UTS 네임스페이스를 한 쌍의 프로세스에 각각 지정하면 서로 다른 로컬 호스트 이름을 보게 할 수 있음 <br />
두 프로세스를 마치 2개의 다른 시스템에서 실행 중인 것처럼 보이게 할 수 있다.<br />
<br /></p>
<h3 id="프로세스-가용-리소스-제한">프로세스 가용 리소스 제한</h3>
<p>리눅스 커널의 cgroups 기능을 이용해 프로세스의 리소스 사용을 제한한다.<br />
프로세스는 설정된 양 이상의 CPU, 메모리, 네트워크 대역폭 등을 사용할 수 없다.<br />
이런 방식으로 프로세스는 다른 프로세스용으로 예약된 리소스를 사용할 수 없으며,<br />
각 프로세스가 별도의 시스템에서 실행된 것 처럼 보이게 한다.<br /></p>
<h1 id="이미지란">이미지란?</h1>
<p>이미지는 <strong>컨테이너를 구성하는 파일과 실행하는 애플리케이션 설정을 포함된 것</strong>으로 컨테이너를 생성하는 하나의 <strong>템플릿</strong>이다. 이미지는 하나의 템플릿으로 같은 이미지를 이용해서 1개 or 여러개의 컨테이너를 생성하고 실행할 수 있다.</p>
<p>이미지의 가장 큰 특징은 <strong>Immutable(불변)</strong> 이다. 이미지는 Immutable 이므로 실행 중인 컨테이너를 이용하여 이미지를 생성하는 경우 매번 새로운 이미지가 생성되고 컨테이너 생성후 추가되거나 변경된 사항은 컨테이너 자체에서 저장된다. 컨테이너가 삭제되어도 이미지에는 영향을 주지 않는다.</p>
<p>도커 이미지는 <a href="https://hub.docker.com/">docker hub</a>에 등록이 가능하며 ubuntu 이미지, nginx, mysql 등 자주 사용되는 이미지들은 docker hub 에서 다운받아서 사용가능하다. 또한 자체적인 <a href="https://docs.docker.com/registry/">docker registry</a>를 구축하여 사용할 수도 있다.</p>
<h2 id="이미지-활용-예제">이미지 활용 예제</h2>
<p>1개의 컨테이너에는 mysql 을 설치하고 다른 컨테이너에는 mysql 와 wordpress 를 설치해야 한다고 가정해보자</p>
<p><img src="/assets/images/posts/20191029/docker-image.png" alt="No Image" /> <br />
출처: <a href="https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html">subicura 도커란 무엇인가</a><br />
위 그림에서 처럼 docker hub 에서 mysql image를 pull 받은 후, mysql 을 base image 로 하는 wordpress 이미지를 생성할 수 있다. 2개의 이미지를 이용하여 원하는 컨테이너를 생성하고 실행하는 것이 가능하다.</p>
<h2 id="레이어-저장-방식">레이어 저장 방식</h2>
<p>도커 이미지는 컨테이너를 실행하기 위한 모든 정보를 가지고 있기 때문에 기존 이미지에 파일 1개를 추가했다고 이미지를 다시 다운받는 것은 비효율적이다. 도커는 이런 문제를 해결하기 위해서 <strong>레이어</strong> 라는 개념을 사용한다. 이미지를 각 레이어로 나누어서 이미지 전체를 다운받지 않아도 특정 레이어만 다운받을 수 있다.<br />
이것이 가능한 이유는 도커가 <a href="https://en.wikipedia.org/wiki/UnionFS">유니온 파일 시스템</a>을 이용하여 여러 개의 레이어를 하나의 파일 시스템으로 사용할 수 있도록 지원하기 때문이다.</p>
<p><img src="/assets/images/posts/20191029/image-layer.png" alt="No Image" /> <br />
출처: <a href="https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html">subicura 도커란 무엇인가</a></p>
<p>예를들어 ubuntu 이미지가 A, B, C 레이어의 집합이라면, ubuntu 를 base image 로 하여 만든 nginx 이미지는 A, B, C, nginx 레이어를 갖게 된다. webapp 이미지를 nginx 를 기반으로 만들면 A, B, C, nginx, web app source 레이어로 생성된다. webapp 을 수정한 경우 매번 A, B, C, nginx 레이어를 다시 설치하지 않고 web app source 레이어만 다시 다운받아 사용하는 것이 가능하다.<br />
컨테이너를 생성하면 기존 이미지 레이어 위에 Read-Write 레이어가 생성된다. Read-Write 레이어는 컨테이너를 실행중에 생성되거나 변경된 내용이 저장되는 영역이다.</p>
<p>배포시에 수정된 내용만을 다시 받으면 되기 때문에 효과적이고 빠르게 배포가 가능해진다.</p>
<h1 id="레지스트리">레지스트리</h1>
<ul>
<li>도커 이미지를 저장하고 다른 사람과 공유할 수 있는 저장소</li>
<li>이미지를 빌드한 결과는 로컬에 보관하거나 레지스트리로 업로드(push) 할 수 있다</li>
<li>다른 컴퓨터에서는 업로드된 이미지를 다운로드(pull) 할 수 있다</li>
<li>공개 레지스트리는 누구나 이미지를 가져올 수 있으며 다양한 이미지들이 레지스트리에 이미 존재한다</li>
</ul>
<h1 id="도커를-사용하는-이유">도커를 사용하는 이유</h1>
<ul>
<li>코드를 통한 실행 환경 구축 및 애플리케이션 구성</li>
<li>변하지 않는 실행환경으로 멱등성(Indempotency) 확보</li>
<li>실행 환경과 애플리케이션 일체화로 이식성 향상</li>
<li>시스템을 구성하는 애플리케이션 및 미들웨어 관리 용이성</li>
</ul>
<h2 id="코드를-통한-실행-환경-구축-및-애플리케이션-구성---코드로-관리하는-인프라">코드를 통한 실행 환경 구축 및 애플리케이션 구성 - 코드로 관리하는 인프라</h2>
<p>여러 서버에 어플리케이션을 배포하기 위해서는 애플리케이션에 의존하는 수 많은 라이브러리를 설치해야 한다. 이러한 라이브러리들을 수동으로 설치할 수 없기 때문에 일반적으로 쉘 스크립트를 작성하여 환경을 설정해 왔다. 하지만 쉘 스크립트로 클러스터 환경에 존재하는 수많은 서버에 동일한 환경을 구축하는것은 한계가 있다.</p>
<p>이러한 한계점을 극복하기 위해서 나온 개념이 코드로 관리하는 인프라 개념이다. Chef, Puppet, Ansible 등은 프로비저닝 툴로써 인프라를 코드로 선언하고 모든 서버에 배포함으로써 환경을 동일하게 만들수 있다.<br />
그 중 Ansible 은 많이 사랑받고 있는 환경 자동화 도구인데, playbook 에 yaml 형식으로 환경을 코드로 선언하여 사용한다.</p>
<p>도커도 Dockfile 에 환경을 코드로 작성함으로써 인프라를 코드로 관리한다.</p>
<h2 id="변하지-않는-실행환경으로-멱등성indempotency-확보---불변-인프라">변하지 않는 실행환경으로 멱등성(Indempotency) 확보 - 불변 인프라</h2>
<p>하지만 코드로 관리하는 인프라에도 문제점이 존재하는데 인프라는 시간이 지나면서 변한다는 것이다. 도커는 이런 문제를 해결하기 위해서 불변 인프라 환경을 제공한다. 아래 예제를 보자</p>
<p>애플리케이션의 트래픽이 높아져 이미 A 서버를 사용하고 있는데 새로 B 서버를 투입한다고 가정해보자.
B 서버에 A 서버와 같은 환경을 구축했다고 하더라도 원하는 대로 동작하지 않는 경우가 흔하게 등장한다. A 서버와 B 서버를 구축한 시점이 다르기 때문에 두 서버에 설치된 OS, 컴파일러, 의존 패키지들까지 완벽하게 같기는 어렵다. 서로 모양이 다른 서버가 존재하는 것을 Snowflake Server(눈송이 서버) 라고 한다. 눈송이가 모두 모양이 다르듯 서버들도 서로 다른 모습이라는 것이다.</p>
<blockquote>
<p>예를들어 개발환경인 mac os 업데이트로 이후 기존에 잘 동작하던 것들이 제대로 동작하지 않는 경험은 수 없이 해봤다. OS 뿐만 아니라 npm install 로 stable 버전을 설치해서 많이 사용하곤 하는데, stable 버전은 안정화된 버전이지 변경이 없는 버전은 아니라는 것이다.</p>
</blockquote>
<p>이러한 문제의 근본적인 원인은 인프라의 가변성(mutable infrastructure)을 허용하고 있기 때문이다.</p>
<p>Ansible vs Docker<br />
<img src="/assets/images/posts/20191029/ansible_vs_docker.png" alt="No Image" /> <br />
출처: <a href="https://www.44bits.io/ko/post/why-should-i-use-docker-container#%EC%9A%B4%EC%98%81%ED%95%98%EB%A9%B4%EC%84%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EB%88%88%EC%86%A1%EC%9D%B4-%EC%84%9C%EB%B2%84%EB%93%A4snowflake-servers">44bits why-should-i-use-docker-container</a></p>
<ul>
<li>Ansible(앤서블)은 provisioning 도구로써 인프라 환경을 코드로 관리하는 유명한 오픈소스</li>
<li>앤서블과 같은 provisioning 도구들은 실행 시점에 서버의 상태가 결정되지만 도커는 이미지를 이용해 구성 시점에 서버의 상태가 결정됨</li>
<li>따라서 도커는 항상 같은 실행 결과를 보장하는 불변 인프라(immutable infrastructure) 를 구축 가능</li>
<li>도커의 이미지는 도커의 운영 시점에 대한 snapshot 을 파일로 변환 한 것으로써 불변 인프라의 기반</li>
</ul>
<h2 id="실행-환경과-애플리케이션-일체화로-이식성-향상">실행 환경과 애플리케이션 일체화로 이식성 향상</h2>
<p>기존에 서버에 애플리케이션을 배포하는 방식은 이미 구축된 서버에 배포되는 방식으로 장비가 추가되면 인프라를 복제하는 작업과 애플리케이션을 배포하는 작업이 분리되어 있었다. <br />
하지만 도커는 컨테이너라는 개념을 이용하여 인프라와 애플리케이션을 함께 묶는 것이 가능하다. 따라서 도커를 이용하면 장비 추가되더라도 도커 이미지만 빌드하여 얼마든지 배포가 가능하다.</p>
<h2 id="시스템을-구성하는-애플리케이션-및-미들웨어-관리-용이성">시스템을 구성하는 애플리케이션 및 미들웨어 관리 용이성</h2>
<p>일정 규모를 넘는 시스템들은 주로 여러 개의 애플리케이션과 미들웨어를 조합하는 형태로 구성된다. 각 애플리케이션과 미들웨어를 조합하지 않으면 시스템을 구성할 수 없는 문제가 발생한다.<br />
도커는 여러 컨테이너를 한 덩어리 처럼 동작시키기 위해서 도커 컨테이너 오케스트레이션 시스템들이 존재한다. 도커는 여러 컨테이를 사용하는 애플리케이션을 쉽게 관리할 수 있도록 Docker Compose 라는 도구 제공한다. 도커 컴포즈를 이용하면 컨테이너를 정의하고 컨테이너 간의 의존 관계를 정의해서 시작 순서 등을 조작할 수 있다.<br />
도커는 여러 서버에 걸쳐 있는 여러 컨테이너를 관리할 수 있는 기능을 가진 컨테이너 오케스트레이션 도구인
도커 스웜, 쿠버네티스 라는 도구들도 존재한다. 컨테이너 오케스트레이션 도구를 활용하면 컨테이너 증가/감소와 노드의 리소스를 효율적으로 사용하기 위해서 컨테이너 배치 및 로드 밸런싱 기능 등이 포함되어 있다.</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://wikibook.co.kr/docker-kubernetes/">위키북스 도커/쿠버네티스를 활용한 컨테이너 개발입문</a></li>
<li><a href="https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html">subicura 도커란 무엇인가</a></li>
<li><a href="https://ko.wikipedia.org/wiki/%EB%8F%84%EC%BB%A4_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4)">위키 도커</a></li>
<li><a href="https://www.youtube.com/watch?v=wW9CAH9nSLs&feature=youtu.be">Pycon Conference 에서 The future of Linux Containers 세션</a></li>
<li><a href="docker.com/blog/au-revoir/">도커의 창업자가 도커를 퇴사하면서 남긴 포스팅</a></li>
<li><a href="https://www.docker.com/resources/what-container">도커 레퍼런스 what-container</a></li>
<li><a href="https://cloud.google.com/containers/?hl=ko">구글 클라우드 containers</a></li>
<li><a href="https://www.redhat.com/ko/topics/containers/whats-a-linux-container">redhat whats-a-linux-container</a></li>
<li><a href="https://github.com/docker/libcontainer">libcontainer</a></li>
<li><a href="https://github.com/opencontainers/runc">runc</a></li>
<li><a href="https://en.wikipedia.org/wiki/UnionFS">위키 유니온 파일 시스템</a></li>
<li><a href="https://rampart81.github.io/post/docker_image/">docker image</a></li>
<li><a href="https://www.44bits.io/ko/post/why-should-i-use-docker-container#%EC%9A%B4%EC%98%81%ED%95%98%EB%A9%B4%EC%84%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EB%88%88%EC%86%A1%EC%9D%B4-%EC%84%9C%EB%B2%84%EB%93%A4snowflake-servers">raccoony why-should-i-use-docker-container</a></li>
</ul>nesskoungho632@gmail.com가상화(Virtualization)2019-10-28T00:00:00+00:002019-10-30T00:00:00+00:00https://kimkoungho.github.io//devops/virtualization<h1 id="가상화virtualization-란">가상화(Virtualization) 란?</h1>
<p><a href="https://en.wikipedia.org/wiki/Virtualization">위키의 정의</a>에서 가상화는 하드웨어 리소스를 추상화하여 가상버전을 만드는 행위라고 정의하고 있다.</p>
<p>좀 말이 어려운데, 소프트웨어를 사용해 물리적인 하드웨어를 추상화하여 가상머신(VM)이라고 하는 여러 <strong>가상 컴퓨터</strong>를 만드는 것이다.<br />
가상화는 물리적 인프라에서 독립된 환경 만들 수 있도록 해준다.</p>
<p>가상화는 클라우드 컴퓨팅의 근간이 된 기술인데, 클라우드 컴퓨팅에서 가상화를 통해 물리적 하드웨어를 추상화한 리소스를 사용자에게 제공하며 사용자는 필요한 컴퓨팅 리소스만 구매하여 사용가능 하다.</p>
<h1 id="가상화-동작-방식">가상화 동작 방식</h1>
<p>가상화에서는 크게 3가지 요소로 나눌수 있는데 가상머신(VM), 하이퍼바이저(hypervisor), 하드웨어로 나누어진다.</p>
<p><img src="/assets/images/posts/20191028/virtualization.png" alt="No Image" /> <br />
출처: <a href="https://www.redhat.com/ko/topics/virtualization">redhat virtualization</a></p>
<h2 id="가상머신vm">가상머신(VM)</h2>
<ul>
<li>가상머신은 내부에 OS를 갖는 가상 컴퓨터 시스템(하나의 독립된 운영환경)</li>
<li>하나의 물리적인 서버에 여러 VM 을 생성하여 여러 OS or 애플리케이션을 실행 가능</li>
<li>여러 VM 은 하이퍼바이저에게 물리 리소스를 요청하고 하이퍼바이저는 해당 리소스를 할당하여 하드웨어 리소스를 공유함</li>
<li>각 VM 은 완벽히 독립되어 하이퍼바이저에 의해서 실행됨</li>
<li>가상머신은 하나의 데이터 파일로 한 컴퓨터에서 다른 컴퓨터로 이동하더라도 동일하게 동작</li>
</ul>
<h2 id="하드웨어">하드웨어</h2>
<ul>
<li>하드웨어는 일반적으로 서버, 데스크탑과 같은 컴퓨터 자원을 말함</li>
<li>하드웨어의 리소스는 하이퍼바이저에 의해서 특정 VM 할당되거나 해제된다</li>
</ul>
<h2 id="하이퍼바이저hypervisor">하이퍼바이저(Hypervisor)</h2>
<ul>
<li>VMMM(Virtual Machine Monitor or Virtual Machine Manager) 이라고도 함</li>
<li>하이퍼바이저는 가상머신을 생성하고 실행하기 위한 소프트웨어로 하드웨어와 VM 중간 계층에 위치하여 중간 관리자 역할을 수행</li>
<li>하이퍼바이저는 일반적으로 서버와 같은 하드웨어에 직접 설치되어 실행됨</li>
<li>하이퍼 바이저는 2가지 유형으로 분류된다.</li>
</ul>
<h3 id="네이티브-하이퍼바이저native-or-bare-metal-hypervisor">네이티브 하이퍼바이저(native or bare-metal hypervisor)</h3>
<p><img src="/assets/images/posts/20191028/native_hypervisor.png" alt="No Image" /> <br />
출처: <a href="https://en.wikipedia.org/wiki/Hypervisor">위키 Hypervisor</a></p>
<ul>
<li>하이퍼바이저가 하드웨어 바로 위에서 실행되는 방식으로 CPU, 메모리, 스토리지 등을 직접제어함</li>
<li>하이퍼바이저가 호스트 OS를 대신함</li>
<li>장점: 하드웨어를 직접 제어하기 때문에 매우 효율적이고 별도의 호스트 OS가 없으므로 보안이 향상됨</li>
<li>단점: 하드웨어에 직접 설치하기 때문에 설치가 어렵고 상황에 따라 별도의 관리 시스템이 필요할 수도 있음</li>
<li>대표적으로 Xen, 마이크로소프트 Hyper-V, KVM(Kernel-based Virtual Machine) 등이 있음</li>
</ul>
<h3 id="호스트된-하이퍼바이저hosted-hypervisor">호스트된 하이퍼바이저(hosted hypervisor)</h3>
<p><img src="/assets/images/posts/20191028/hosted_hypervisor.png" alt="No Image" /> <br />
출처: <a href="https://en.wikipedia.org/wiki/Hypervisor">위키 Hypervisor</a></p>
<ul>
<li>하이퍼바이저가 호스트 OS 위에 애플리케이션으로 설치되는 방식</li>
<li>게스트 OS(VM)는 하드웨어 자원을 하이퍼바이저를 통해서 호스트 OS에서 할당받음</li>
<li>일반적으로 서버기반 환경이 아닌 개인 PC 나 노트북 환경에서 수행되는 방식</li>
<li>MAC OS 위에 VM Ware 를 설치하여 윈도우나 리눅스를 구동시키는 경우</li>
<li>장점: 호스트 OS에서 게스트 OS에 빠르고 쉽게 접근이 가능</li>
<li>단점
<ul>
<li>호스트 OS를 통해 하드웨어 자원을 액세스하므로 네이티브 방식에 비해서 성능이 나쁨</li>
<li>해커에 의해 호스트 OS가 손상된 경우 게스트 OS를 조작할 수 있어 보안 위험이 있음</li>
</ul>
</li>
<li>대표적으로 VMware server, VMware Workstation, Virtual box 등이 있음</li>
</ul>
<h1 id="가상화-방법">가상화 방법</h1>
<p>가상화 방법은 전 가상화(Full Virtualization)와 반 가상화(Para-Virtualization) 방법이 있다.</p>
<h2 id="전-가상화full-virtualization">전 가상화(Full Virtualization)</h2>
<ul>
<li>전 가상화는 하드웨어를 모두 가상화하는 방법</li>
<li>게스트 OS(VM)는 하드웨어 자원을 사용하기 위해서 반드시 하이퍼바이저가 중재해야 됨</li>
</ul>
<p><img src="/assets/images/posts/20191028/full_virtualization.png" alt="No Image" /> <br />
출처: <a href="https://library.gabia.com/contents/infrahosting/7426">가비아 서버 가상화 기술의 진화</a></p>
<ul>
<li>게스트 OS는 ‘DOM 0’ 이라는 관리 머신을 거쳐서 하이퍼바이저와 통신</li>
<li>‘DOM 0’ 는 여러 게스트 OS의 요청을 모두 받아서 처리함으로 CPU, RAM, I/O 처리가 잦으면 성능이 저하됨</li>
</ul>
<h2 id="반-가상화para-virtualization">반 가상화(Para-Virtualization)</h2>
<ul>
<li>반 가상화는 하드웨어를 완전히 가상화하지 않음</li>
<li>게스트 OS(VM)의 커널을 일부 수정하여 ‘DOM 0’ 없이 하이퍼바이저를 통해서 하드웨어와 직접 통신</li>
<li>게스트 OS를 수정해야 하므로 윈도우 같은 경우 별도의 Tool 이 필요할 수 있음</li>
</ul>
<p><img src="/assets/images/posts/20191028/para_virtulalization.png" alt="No Image" /> <br />
출처: <a href="https://library.gabia.com/contents/infrahosting/7426">가비아 서버 가상화 기술의 진화</a></p>
<ul>
<li>수정된 게스트 OS는 Hyper Call 이라는 명령어로 하이퍼바이저를 호출</li>
<li>하이퍼바이저는 하드웨어 자원을 수정된 게스트 OS에 할당</li>
</ul>
<h1 id="가상화-사용-사례">가상화 사용 사례</h1>
<h2 id="서버-가상화">서버 가상화</h2>
<ul>
<li>단일 물리적인 서버를 여러 개의 가상 서버로 분리하여 사용하는 기술로 일반적으로 가상화라고 하면 서버 가상화를 말함</li>
<li>서버 가상화의 가장 큰 목적은 서버 리소스 사용률을 극대화 하기 위해서 사용함</li>
<li>물리적인 서버를 확장하는 것보다 VM 을 이용하여 확장하는 것이 훨씬 효율적임
<ul>
<li>물리 서버의 환경을 설정하는 것 보다 VM 실행환경만 구축하고 이미지를 이용한 확장이 훨씬 쉬움</li>
</ul>
</li>
</ul>
<p>예를들어 메일, 웹, 사내용 레거시 앱 3개의 서비스가 각각 물리 서버에 배포되어 있다고 가정해보자.<br />
<img src="/assets/images/posts/20191028/server-virtualization_1.png" alt="No Image" /> <br />
출처: <a href="https://www.redhat.com/ko/topics/virtualization/what-is-virtualization">redhat what-is-virtualization</a><br />
위 그림에서처럼 각 서비스는 서버의 30% 용량만을 사용하고 있다면 각 서버는 70% 용량이 낭비되고 있는 것이다. 이런 문제를 해결하기 위해 서버 가상화를 이용하여 1번 서버에 메일을 위한 가상환경과 레거시 앱의 가상환경을 구축하여 낭비를 줄일 수 있다.
<img src="/assets/images/posts/20191028/server-virtualization_2.png" alt="No Image" /> <br />
출처: <a href="https://www.redhat.com/ko/topics/virtualization/what-is-virtualization">redhat what-is-virtualization</a></p>
<p>사실 1개의 서버에 여러 작업을 수행하게 할 수 있지만, 그런경우 메일 서비스에 문제가 생겨 서버에 영향을 주는 경우 모든 서비스를 사용할 수 없는 문제가 발생할 수 있다. 하지만 VM을 이용하여 가상 메일 서버와 가상 레거시 앱 서버를 구축한다면 서로가 완벽히 격리되어 안전성을 보장된다.</p>
<h2 id="데스크탑-가상화">데스크탑 가상화</h2>
<ul>
<li>데스크탑 가상화는 데스크탑 환경과 실제 클라이언트 장치를 분리하는 기술</li>
<li>데스크탑 환경이 데이터 센터에 저장되고 사용자는 로그인을 통해 데스크탑 환경을 이용하는 것</li>
<li>일반적으로 회사에서 많이 사용하는 방법으로 직원들은 원격 데스크탑에 접속하여 독립된 PC를 사용하는 것 처럼 사용함</li>
<li>즉, 사용자의 클라이언트에 관련없이 원격 서버에 성능으로 가상 PC를 이용할 수 있음</li>
<li>대표적으로 VDI(Virtual Desktop Infrastructure), DaaS 가 있음</li>
<li>VDI: 사용자는 VDI 접속 프로그램에 로그인하여 원격 서버의 데스크탑 환경을 이용하는 방법
<ul>
<li>중앙 집중식 데스크탑 관리가 가능함으로써 보안이 향상되어 많은 기업에서 사용하고 있는 솔루션</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>서버 가상화와 데스크탑 가상화의 차이점</strong><br />
서버 가상화는 서버의 사용률을 극대화 하기 위해서 사용하며 매일 같은 기능을 수행하기 때문에 예측하기 쉽다. 하지만 데스크탑 가상화는 원격환경에서 가상 PC환경을 제공함으로써 더 유연한 환경을 제공한다. 사용자의 행위를 예측할 수 없기 때문에 가상 데스크탑 호스팅을 제공하는 서버는 사용자의 비정상적인 행위에 대응할 수 있어야 한다.</p>
</blockquote>
<h2 id="네트워크-가상화">네트워크 가상화</h2>
<ul>
<li>네트워크 가상화는 물리적인 네트워크를 논리적인 단위의 네트워크로 나누어 사용하는 기술</li>
<li>네트워크 가상화는 네크워크에 있는 두 도메인을 물리적으로 연결하지 않고 터널을 만들어 두 도메인을 연결</li>
<li>네트워크 관리자는 물리적인 요소(스위치, 라우터, 링크 등)들을 건드리지 않고 요소들을 수정하고 제어할 수 있음</li>
<li>대표적으로 SDN(Software Define Network), NFV(Network Functional Virtualization) 가 있음</li>
<li>NFV: 네트워크 가상화로 만들어진 터널에 서비스를 배치하는 것으로 방화벽, IDS, IPS 와 같은 4~7 계층의 로드밸런싱을 가상화 하는 것</li>
<li>SDN: 제어 플레인을 데이터 플레인과 분리하는 방법으로 네트워크를 프로그래밍 가능하도록 함
<ul>
<li>제어 플레인: 네트워크에 무엇이 어디에 이동할지 알려주는 역할</li>
<li>데이터 플레인: 특정 목적지로 패킷을 보내는 역할</li>
</ul>
</li>
</ul>
<h2 id="스토리지-가상화">스토리지 가상화</h2>
<ul>
<li>네트워크 상에 있는 모든 스토리지를 단일 스토리지 처럼 액세스하고 관리할 수 있도록 함</li>
<li>모든 스토리지 블록을 단일 공유 풀로 통합하여 네트워크의 모든 VM에 할당할 수 있음</li>
<li>스토리지 가상화를 통해서 네트워크 안에 스토리지를 최대한 활용 가능</li>
</ul>
<h2 id="데이터-가상화">데이터 가상화</h2>
<ul>
<li>데이터 가상화는 데이터 소스, 형식, 위치에 관계없이 모든 애플리케이션들이 데이터에 접근가능 하도록 함</li>
</ul>
<h2 id="애플리케이션-가상화">애플리케이션 가상화</h2>
<ul>
<li>애플리케이션을 사용자의 OS에 직접 설치하지 않고 가상환경의 애플리케이션을 실행</li>
<li>가상환경에는 오직 애플리케이션만을 설치하므로 위의 데스크탑 가상화와는 다름</li>
<li>애플리케이션 가상화의 3가지 유형
<ul>
<li>로컬 애플리케이션 가상화</li>
<li>애플리케이션 스트리밍</li>
<li>서버기반 애플리케이션 가상화</li>
</ul>
</li>
</ul>
<h2 id="cpu-가상화">CPU 가상화</h2>
<ul>
<li>CPU 가상화는 하이퍼바이저, virtual system, OS를 가능하게 하는 기본 기술</li>
<li>단일 VM 을 여러 VM 에서 사용할 수 있도록 여러 가상 CPU 로 나눌수 있음</li>
<li>최근 프로세서들은 CPU 가상화를 지원하는 명령어 세트들이 포함되어 있음</li>
</ul>
<h2 id="리눅스-가상화">리눅스 가상화</h2>
<ul>
<li>리눅스에는 Intel 및 AMD의 가상화 프로세서 확장을 지원하는 KVM(Kernel Virtualization Machine)이 자체적으로 포함되어 있음</li>
<li>리눅스는 호스트 OS 내에서 x86기반 VM을 생성가능</li>
</ul>
<h1 id="컨테이너형-가상화">컨테이너형 가상화</h1>
<ul>
<li>최근 가상화 기술은 하이퍼바이저 기반 가상화 방식에서 컨테이너 기반 가상화로 변하고 있음 (도커)</li>
<li>기존의 하이퍼바이저 기반의 가상화 방식에서는 VM 에 추가적으로 게스트 OS를 설치하기 때문에 성능 이슈가 존재했음</li>
<li>이를 개선하기 위해 LXC(Linux Container) 에서 컨테이너라는 개념을 처음 도입</li>
<li>LXC 에서는 게스트 OS를 설치하지 않고 <strong>프로세스를 격리</strong> 하여 VM과 유사한 독립적인 공간을 컨테이너라고 표현함</li>
<li>LXC는 호스트 OS(리눅스 커널) 위에서 네임 스페이스와 cgroup 을 이용하여 구현됨
<ul>
<li>네임 스페이스: 리눅스 시스템 자원을 묶어 프로세스를 할당하는 방식</li>
<li>cgroup: 프로세스 그룹의 시스템 자원을 사용량을 관리하여 특정 애플리케이션이 자원을 과다 사용하는 것을 제한</li>
</ul>
</li>
</ul>
<p><img src="/assets/images/posts/20191028/vm_vs_container.png" alt="No Image" /> <br />
출처: <a href="https://library.gabia.com/contents/infrahosting/7426">가비아 서버 가상화 기술의 진화</a></p>
<ul>
<li>컨테이너 엔진에는 실행에 필요한 환경이 모두 갖춰져 있으므로 어떤 환경에서도 실행이 가능</li>
<li>컨테이너에는 게스트 OS는 존재하지 않아서 VM 보다 가볍고 OS를 부팅하는 과정이 없어서 빠르게 시작이 가능</li>
</ul>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Virtualization">위키 Virtualization</a></li>
<li><a href="https://www.ibm.com/cloud/learn/virtualization-a-complete-guide">IBM Virtualization</a></li>
<li><a href="https://www.vmware.com/kr/solutions/virtualization.html">VM Ware Virtualization</a></li>
<li><a href="https://www.redhat.com/ko/topics/virtualization">redhat virtualization</a></li>
<li><a href="https://www.redhat.com/ko/topics/virtualization/what-is-virtualization">redhat what-is-virtualization</a></li>
<li><a href="https://opensource.com/resources/virtualization">opensource.com virtualization</a></li>
<li><a href="https://en.wikipedia.org/wiki/Hypervisor">위키 Hypervisor</a></li>
<li><a href="https://library.gabia.com/contents/infrahosting/7426">가비아 서버 가상화 기술의 진화</a></li>
<li><a href="https://www.ibm.com/cloud/learn/hypervisors">ibm hypervisors</a></li>
<li><a href="https://www.ibm.com/cloud/learn/virtualization-a-complete-guide">ibm virtualization-a-complete-guide</a></li>
<li><a href="https://en.wikipedia.org/wiki/Desktop_virtualization">위키 Desktop_virtualization</a></li>
<li><a href="https://searchvirtualdesktop.techtarget.com/feature/Differences-between-desktop-and-server-virtualization">TechTarget Differences-between-desktop-and-server-virtualization</a></li>
<li><a href="https://brownbears.tistory.com/60">불곰님 블로그 네트워크 가상화</a></li>
</ul>nesskoungho632@gmail.comHTTPS2019-08-10T00:00:00+00:002019-09-14T00:00:00+00:00https://kimkoungho.github.io//network/https<h2 id="https-란">HTTPS 란?</h2>
<p><a href="https://en.wikipedia.org/wiki/HTTPS">위키</a> 의 정의에 따르면,</p>
<ul>
<li>HTTPS (Hypertext Transfer Protocol Secure) 는 HTTP(Hypertext Transfer Protocol) 에 보안을 강화한 것</li>
<li>HTTP 프로토콜에 TLS(Transport Layer Security), SSL(Secure Sockets Layer) 을 이용하여 암호화를 적용한 버전 (HTTP 의 암호화 버전)</li>
<li>HTTPS 는 HTTP over TLS(SSL) 이라고 불림</li>
</ul>
<h3 id="http-vs-https">HTTP vs HTTPS</h3>
<p><img src="/assets/images/posts/20190810/https_vs_http.png" alt="No Image" /></p>
<ul>
<li>HTTP는 전송 데이터를 암호화 하지 않으므로 중간자 공격에 취약
<ul>
<li>공격자는 사이트의 중요한 정보를 도청 가능</li>
<li>공격자가 웹 페이지를 수정하여 맬웨어 or 광고를 주입가능</li>
</ul>
</li>
<li>HTTPS 는 TLS 를 이용하여 전송 데이터를 암호화 함으로써 공격을 방어 가능
<ul>
<li>HTTPS 는 URL, 쿼리 스트링, 헤더 및 쿠키 등을 암호화하여 전송</li>
<li>HTTPS 는 호스트와 포트는 TCP/IP 계층의 일부 이므로 암호화 할 수 없음</li>
</ul>
</li>
</ul>
<blockquote>
<table>
<thead>
<tr>
<th>PROTOCOL</th>
<th>URL</th>
<th>PORT</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTTP</td>
<td>http://</td>
<td>80</td>
</tr>
</tbody>
<tbody>
<tr>
<td>HTTPS</td>
<td>https://</td>
<td>443</td>
</tr>
</tbody>
</table>
</blockquote>
<h2 id="tlsssl-란-">TLS(SSL) 란 ?</h2>
<ul>
<li>TLS(Transport Layer Security)는 두 컴퓨터 간에 통신에서 응용 프로그램 간의 인증, 개인정보 및 무결성을 제공하는 암호화 프로토콜</li>
<li>TLS 는 웹 브라우징, 이메일, VoIP 등에 가장 널리 사용되는 암호화 프로토콜</li>
<li>SSL 은 Netscape 사에서 처음 개발했지만, 그 이후 IETF 에서 SSL 3.1 버전을 인수하여 공개 했고 TLS 1.0 으로 릴리즈 했음</li>
<li>SSL 과 TLS 를 혼용해서 사용하고 있지만, 정식 명칭은 TLS</li>
</ul>
<h3 id="tls-장점목적">TLS 장점(목적)</h3>
<ul>
<li>통신 내용을 공격자에게 노출되는 것을 막을 수 있음 (도청 방지)</li>
<li>통신 내용의 악의적인 변경을 방지 (위조 방지)</li>
<li>클라이언트가 접속하려는 서버가 신뢰 할 수 있는 서버인지를 판단 가능
<ul>
<li>요청한 서버가 위조 사이트 아님을 보증 (피싱 방지)</li>
<li>서버가 신뢰할 수 없는 인증서를 사용한다면 브라우저는 사용자에게 경고 메시지를 출력</li>
</ul>
</li>
</ul>
<h2 id="디지털-인증서">디지털 인증서</h2>
<ul>
<li>클라이언트와 서버 같은 엔티티의 ID 를 <strong>제3자</strong> 가 보증해주는 전자화된 문서</li>
<li>클라이언트는 웹 서버가 제공한 인증서를 <strong>제3자</strong> 에게 요청하면 <strong>제3자</strong> 는 해당 웹 서버를 보증</li>
<li>제3자는 CA(Certificate Authority) 로써 신뢰할 수 있는 공인된 기업</li>
</ul>
<h3 id="ca-certificate-authority">CA (Certificate Authority)</h3>
<ul>
<li>클라이언트가 접속하려는 서버가 의도한 서버가 맞는지 보장해주는 민간기업</li>
<li>CA 는 신뢰성이 엄격하게 공인된 기업들만이 참여 가능</li>
<li>TLS(SSL) 이용하려는 서비스는 CA 를 통해서 인증서를 구입해야함</li>
<li>CA의 개인키는 절대로 유출되어서는 안됨 (이것이 노출되어 파산한 회사도 있음)</li>
</ul>
<h3 id="tlsssl-인증서-내용">TLS(SSL) 인증서 내용</h3>
<p>크롬에서 Github 인증서</p>
<p><img src="/assets/images/posts/20190810/github_digital_certificates_1.png" alt="No Image" /> <br />
<img src="/assets/images/posts/20190810/github_digital_certificates_2.png" alt="No Image" /></p>
<ul>
<li>서비스 정보
<ul>
<li>이 영역은 해당 서비스(github)에 대한 정보</li>
<li>CA 에서 인증서를 구입하는 서비스는 서비스 도메인, 공개키와 같은 정보를 인증서를 구입할때 제출함</li>
</ul>
</li>
<li>발급자 정보
<ul>
<li>인증서를 발급한 CA 정보, 만기일 등의 정보</li>
<li>클라이언트가 접속한 서버가 클라이언트가 의도한 서버가 맞는지 확인하기 위해서 사용</li>
</ul>
</li>
<li>공개키 정보
<ul>
<li>공개키의 알고리즘, 공개키의 내용 등의 정보</li>
<li>클라이언트가 서버와 통신할 때 사용할 공개키와 그 공개키의 암호화 방법들의 정보</li>
</ul>
</li>
<li>전자서명
<ul>
<li>CA 가 해당 서비스(github) 을 식별하기 위해서 사용하는 정보</li>
</ul>
</li>
</ul>
<h2 id="tlsssl-암호화-방법">TLS(SSL) 암호화 방법</h2>
<p>TLS 의 암호화는 <strong>대칭키</strong> 방식과 <strong>공개키</strong> 방식을 혼합해서 사용함</p>
<h3 id="대칭키">대칭키</h3>
<ul>
<li>동일한 키로 암호화와 복호화를 하는 방법</li>
<li>암호를 주고 받는 사람들 사이에 대칭키를 전달하는 것이 어려움</li>
<li>대칭키 유출되면 공격자도 암호화 내용을 복호화 할 수 있기 때문</li>
</ul>
<h3 id="공개키">공개키</h3>
<ul>
<li>대칭키 방식의 단점을 보완하고자 나온 방법</li>
<li>공개키는 대칭키와 달리 공개키, 개인키 2개의 키를 이용한 방법</li>
<li>개인키는 자신만이 가지고 있고, 공개키는 타인에게 제공</li>
<li>공개키를 이용해서 암호화, 개인키를 이용해서 복호화하는 방법</li>
<li>즉, 개인키가 없으면 복호화 할 수 없음</li>
<li>공개키 암호화는 개인키 암호화보다 많은 컴퓨터 자원을 사용하는 단점이 있음</li>
</ul>
<h2 id="tlsssl-기술-스택">TLS(SSL) 기술 스택</h2>
<p><img src="/assets/images/posts/20190810/tls_stack.jpg" alt="No Image" /></p>
<ul>
<li>TLS 는 위 그림에서 처럼 TCP 계층과 HTTP 계층 사이에서 동작</li>
<li>TLS 의 가장 기본적인 부분은 레코드 프로토콜</li>
</ul>
<h3 id="레코드-프로토콜의-하위-프로토콜">레코드 프로토콜의 하위 프로토콜</h3>
<ul>
<li>Handshake 프로토콜: 이 프로토콜은 보안 연결을 위한 매개변수를 설정하는데 사용함</li>
<li>Application 프로토콜: handshake 프로세스 이후 시작되며 클라이언트와 서버간의 데이터가 안전하게 전달됨</li>
<li>Alert 프로토콜: 오류, 안정성 문제 등이 있을때 한쪽에서 다른쪽에 noti 하기 위해서 사용</li>
<li>Change chiper spec 프로토콜: 암호화 매개변수를 수정하기 위해서 사용함</li>
<li>Heartbeat 프로토콜: 커넥션이 여전히 활성상태인지 확인하기 위해서 사용됨</li>
</ul>
<p>alert, change chiper spec, heartbeat 프로토콜은 특정상황에 필요할 때 사용되어짐<br />
여기서 중요한 것은 handshake와 application 프로토콜</p>
<h3 id="tls-handshake-프로토콜">TLS Handshake 프로토콜</h3>
<p><strong>기본 TLS Handshake</strong></p>
<ul>
<li>이 유형은 서버만 인증되고 클라이언트는 인증되지 않음</li>
</ul>
<p><img src="/assets/images/posts/20190810/tls_basic_handshake.jpg" alt="No Image" /></p>
<ul>
<li>TLS Handshake 는 우선 TCP 3-way Handshake 이후에 발생함
<ul>
<li>TLS 는 TCP 위에서 동작하기 때문에 ..</li>
</ul>
</li>
<li>클라이언트: client hello
<ul>
<li>클라이언트가 지원하는 TLS 버전, 지원하는 암호화 방식, <strong>클라이언트가 생성한 랜덤 데이터</strong>, 세션 ID 등을 전송</li>
<li>세션 아이디 : 이미 TLS handshake 를 했다면, 비용과 시간을 절약하기 위해 기존 세션을 사용함 (약식 TLS handshake)</li>
</ul>
</li>
<li>서버: server hello (certificate)
<ul>
<li>서버가 선택한 세션 ID, 선택된 암호화 방식, <strong>서버가 생성한 랜덤한 데이터</strong>, 인증서 등을 전송</li>
<li>서버는 인증서를 전송할 때 개인키로 암호화하여 전송 - 해당 서버가 공인된 CA 에게 보증받는 인증서임을 보장</li>
<li>위에서 봤지만, 인증서에는 서비스 정보, 공개키, CA 정보 등을 포함한다</li>
</ul>
</li>
<li>서버: server hello done
<ul>
<li>서버는 클라이언트에게 server hello 가 종료되었음을 알려줌</li>
</ul>
</li>
<li>클라이언트: 인증서 확인
<ul>
<li>클라이언트(브라우저)는 먼저 인증서를 확인하여 자신의 CA 리스트에서 CA 를 확인</li>
<li>브라우저는 인증서를 공개키로 복호화 하여 인증서 내용을 확인</li>
<li>인증서를 공개키로 복호화 가능하다는 것은 해당 CA 가 발급한 개인키로 인증서가 암호화 된 것을 의미 - 신뢰할 수 있는 서버임을 보증</li>
</ul>
</li>
<li>클라이언트: client key exchange
<ul>
<li>클라이언트는 premaster secret 을 생성 (생성방법은 cipher suite에 의존적임)</li>
<li>클라이언트는 premaster secret 을 서버의 공개키를 이용하여 암호화하여 서버에 전송</li>
<li>서버는 암호화된 premaster secret 을 개인키로 복호화</li>
<li>서버는 premaster secret 과 이전에 클라이언트로 부터 전송된 랜덤 데이터를 이용하여 master secret 을 생성</li>
<li>클라이언트도 premaster secret 과 이전에 서버로 부터 전송된 랜덤 데이터를 이용하여 master secret 을 생성</li>
</ul>
</li>
<li>클라이언트: change chiper spec
<ul>
<li>클라이언트는 암호사양 변경되었다는 메시지를 전송</li>
<li>이제부터 전송하는 클라이언트의 모든 데이터는 대칭키 암호화 방식을 이용</li>
</ul>
</li>
<li>클라이언트: finished
<ul>
<li>클라이언트는 handshake 종료 메시지를 전송</li>
<li>해당 데이터는 대칭키로 암호화된 데이터</li>
</ul>
</li>
<li>서버: change chiper spec
<ul>
<li>서버도 암호사양 변경 수신했다는 정보를 전송</li>
<li>이제부터 서버도 모든 전송 데이터를 대칭키 암호화 방식으로 전송</li>
</ul>
</li>
<li>서버: finished
<ul>
<li>서버도 handshake 종료 메시지를 전송</li>
<li>해당 데이터는 대칭키로 암호화된 데이터</li>
</ul>
</li>
</ul>
<p><strong>클라이언트 인증 TLS Handshake</strong></p>
<ul>
<li>이 방법은 클라이언트 인증서를 서버가 사용해야 하는 경우에 사용하는 방법 (일반적 x)</li>
<li>server hello 단계에서 서버가 인증서를 전송할 때, 클라이언트도 인증서를 보내라는 요청을 함께 전송</li>
<li>그러면 클라이언트는 server hello done 이전에 클라이언트의 인증서 정보를 클라이언트의 개인키를 이용하여 암호화하여 전송</li>
<li>서버는 해당 인증서 내용이 유효한지 확인하고 유효하면 server hello done 을 전달함</li>
<li>나머지 단계는 기본 TLS Handshake 와 같음</li>
</ul>
<p><strong>약식 TLS Handshake</strong></p>
<ul>
<li>client hello 전송시에 클라이언트는 session ID 를 함께 전송하는데, 서버가 해당 session ID 를 미리 알고 있는 경우에 해당</li>
<li>서버가 session ID 를 알고 있으므로 인증서 및 키 교환 단계를 건너 뛰게 됨</li>
</ul>
<h2 id="세션-실제-데이터-전송">세션: 실제 데이터 전송</h2>
<ul>
<li>클라이언트: 데이터 요청
<ul>
<li>클라이언트는 요청 데이터를 대칭키 방식으로 암호화하여 전송</li>
</ul>
</li>
<li>서버: 데이터 응답
<ul>
<li>요청 데이터를 대칭키 방식으로 복호화한 뒤 적절한 응답 데이터를 대칭키로 암호화하여 전송</li>
</ul>
</li>
</ul>
<blockquote>
<p><a href="https://opentutorials.org/course/228/4894">생활코딩 블로그</a> 에서는 대칭키를 session key 라고 설명되어 있으나 … <br />
다른 블로그들 자료들에서는 master secret 을 이용 하는것으로 보임</p>
</blockquote>
<p><a href="https://www.comparitech.com/blog/information-security/tls-encryption/">comparitech</a>, <a href="https://www.acunetix.com/blog/articles/establishing-tls-ssl-connection-part-5/">acunetix</a></p>
<h3 id="master-secret">Master secret</h3>
<ul>
<li>전송되는 모든 레코드들을 보호하기 위한 목적으로 사용됨</li>
<li>클라이언트와 서버 모두 master secret 을 가지고 있으며 각 3개의 key 가 있음</li>
<li>MAC(Message Authentication code): 암호화된 메시지들이 상대방이 암호화한것이 맞는지 무결성을 검증할 때 사용하는 인증</li>
</ul>
<h3 id="master-secret-구성">Master secret 구성</h3>
<ul>
<li>클라이언트 쓰기 MAC key: 서버에서 클라이언트가 보낸 데이터의 무결성을 확인하는데 사용되는 key</li>
<li>클라이언트 쓰기 암호화 key: 서버는 클라이언트에서 전송된 데이터를 암호화 하는데 사용되는 key</li>
<li>클라이언트 쓰기 IV key: 서버에서 AEAD 암호화에 사용되는 key (다른 암호화 알고리즘에서는 사용되지 않음)</li>
<li>서버 쓰기 MAC key: 클라이언트가 서버에서 보낸 데이터의 무결성을 확인하는데 사용되는 key</li>
<li>서버 쓰기 암호화 key: 클라이언트는 서버에서 전송된 데이터를 암호화 하는데 사용되는 key</li>
<li>서버 쓰기 IV key: 클라이언트에서 AEAD 암호화에 사용되는 key (다른 암호화 알고리즘에서는 사용되지 않음)</li>
</ul>
<h2 id="tls-와-osi-7-계층">TLS 와 OSI 7 계층</h2>
<ul>
<li>OSI 7 계층은 통신 시스템 및 프로토콜을 보는 방법을 표준화 한것</li>
<li>이것은 하나의 모델일 뿐, 모든 프로토콜이 해당 모델을 지키고 있는 것은 아님 …</li>
<li>위에서 보는것 처럼 TLS 는 OSI 모델을 따르고 있지 않음</li>
<li>물론 TLS 는 TCP 위에서 동작하므로 Transport 계층 처럼 보일 수 있으나 ..</li>
<li>HTTP 프로토콜 위에 동작함으로 순수 Transport 계층으로 볼수 없음 …</li>
</ul>
<blockquote>
<p>TLS 와 OSI 관련 질문들 …</p>
<ul>
<li><a href="https://security.stackexchange.com/questions/93333/what-layer-is-tls">https://security.stackexchange.com/questions/93333/what-layer-is-tls</a></li>
<li><a href="https://security.stackexchange.com/questions/195229/where-exactly-in-the-osi-model-does-tls-ssl-belong">https://security.stackexchange.com/questions/195229/where-exactly-in-the-osi-model-does-tls-ssl-belong</a></li>
</ul>
</blockquote>
<p>TODO</p>
<ul>
<li>TLS 사용 하기</li>
<li>TLS 보안 문제 …</li>
</ul>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/HTTPS">https://en.wikipedia.org/wiki/HTTPS</a></li>
<li><a href="https://seopressor.com/blog/http-vs-https/">https://seopressor.com/blog/http-vs-https/</a></li>
<li><a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">https://en.wikipedia.org/wiki/Transport_Layer_Security</a></li>
<li><a href="https://searchsecurity.techtarget.com/definition/Transport-Layer-Security-TLS">https://searchsecurity.techtarget.com/definition/Transport-Layer-Security-TLS</a></li>
<li><a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/">https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/</a></li>
<li><a href="https://opentutorials.org/course/228/4894">https://opentutorials.org/course/228/4894</a></li>
<li><a href="https://www.comparitech.com/blog/information-security/tls-encryption/">https://www.comparitech.com/blog/information-security/tls-encryption/</a></li>
<li><a href="https://www.acunetix.com/blog/articles/establishing-tls-ssl-connection-part-5/">https://www.acunetix.com/blog/articles/establishing-tls-ssl-connection-part-5/</a></li>
<li><a href="https://www.ibm.com/support/knowledgecenter/ko/SSEPGG_11.1.0/com.ibm.db2.luw.admin.sec.doc/doc/c0053515.html">https://www.ibm.com/support/knowledgecenter/ko/SSEPGG_11.1.0/com.ibm.db2.luw.admin.sec.doc/doc/c0053515.html</a></li>
<li><a href="https://rsec.kr/?p=455">https://rsec.kr/?p=455</a></li>
<li><a href="https://security.stackexchange.com/questions/93333/what-layer-is-tls">https://security.stackexchange.com/questions/93333/what-layer-is-tls</a></li>
<li><a href="https://security.stackexchange.com/questions/195229/where-exactly-in-the-osi-model-does-tls-ssl-belong">https://security.stackexchange.com/questions/195229/where-exactly-in-the-osi-model-does-tls-ssl-belong</a></li>
</ul>nesskoungho632@gmail.com동일 출처 원칙(Same-origin Policy)2019-07-21T00:00:00+00:002019-07-27T00:00:00+00:00https://kimkoungho.github.io//security/same-origin-policy<h2 id="동일-출처-원칙same-origin-policy-이란">동일 출처 원칙(Same-origin Policy) 이란?</h2>
<p><a href="https://en.wikipedia.org/wiki/Same-origin_policy">위키 Same-origin_policy</a> 의 정의에 따르면,</p>
<ul>
<li><strong>동일 출처(same-origin)</strong> 에서만 스크립트를 이용하여 데이터(DOM)를 접근을 허용하는 정책
<ul>
<li>출처(Origin)은 URI 스키마, 호스트, Port 로 구성된 정보를 말함</li>
</ul>
</li>
<li>동일 출처 원칙은 웹 어플리케이션에서 중요한 보안 개념으로써 대부분의 브라우저들이 해당 정책을 따르고 있음
<ul>
<li>동일 출처 원칙은 브라우저에서 동작하는 제약이기 때문에, 브라우저 별로 다를 수 있음</li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy">MDN Same-origin_policy</a> 를 보면 IE 브라우저에 대한 예외사항이 기술되어 있음</li>
</ul>
</li>
<li>동일 출처 원칙 사용 이유
<ul>
<li>XSS 와 같은 스크립트 삽입 공격을 통해서 출처가 다른 웹 어플리케이션에 접근을 방지</li>
<li>현대의 웹 어플리케이션은 인가된(authenticated) 사용자의 세션을 유지하기 위해서 HTTP Cookie 에 의존하기 때문</li>
<li>최근 웹 어플리케이션 들은 인증된 사용자 세션정보를 HTTP 쿠키에 담아서 광범위하게 사용하곤 하는데, 출처가 다른 페이지에서 스크립트를 이용해 해당 쿠키정보를 추출할 수 있기 때문</li>
<li>데이터의 기밀성 또는 일관성을 유지하기 위해서 클라이언트 측에서 관계 없는 사이트에서 제공된 컨탠츠를 분리해야함</li>
</ul>
</li>
</ul>
<h3 id="출처-origin-결정-원칙">출처 (origin) 결정 원칙</h3>
<p>URI 를 결정하는 알고리즘은 <a href="https://tools.ietf.org/html/rfc6454">RFC 6454</a> 에 명시되어 있음<br />
출처 (origin) 은 URI 스키마, Host, Port 번호 등으로 구성된 정보를 의미</p>
<p>ex) http://www.example.com 과 출처 비교</p>
<blockquote>
<table>
<thead>
<tr>
<th>비교 대상 URL</th>
<th>결과</th>
<th>이유</th>
</tr>
</thead>
<tbody>
<tr>
<td>http://www.example.com/main</td>
<td>성공</td>
<td>같은 호스트, 포트, 프로토콜</td>
</tr>
</tbody>
<tbody>
<tr>
<td>http://www.example.com:81</td>
<td>실패</td>
<td>다른 포트</td>
</tr>
</tbody>
<tbody>
<tr>
<td>https://www.example.com</td>
<td>실패</td>
<td>다른 프로토콜</td>
</tr>
</tbody>
<tbody>
<tr>
<td>http://mail.example.com</td>
<td>실패</td>
<td>다른 호스트 (정확한 일치 필요)</td>
</tr>
</tbody>
<tbody>
<tr>
<td>http://www.example.com:80</td>
<td>의존</td>
<td>명시적 포트 (브라우저 구현에 따라 다름)</td>
</tr>
</tbody>
</table>
</blockquote>
<ul>
<li>IE 는 출처를 결정할 때 포트를 사용하지 않고 Security Zone 을 사용</li>
</ul>
<h3 id="동일-출처-원칙의-적용-범위">동일 출처 원칙의 적용 범위</h3>
<ul>
<li><strong>1.</strong> 동일 출처 원칙은 오직 스크립트 에만 적용됨
<ul>
<li>Microsoft Silverlight, Adobe Flash, or Adobe Acrobat 와 같은 다른 웹 스크립트에도 적용되어 있음</li>
<li>DOM 제어와는 관련 없는 XMLHttpRequest 에도 적용되어 있음</li>
</ul>
</li>
</ul>
<p>ex) iframe(or frame) 에서 출처가 다른 부모 frame 에 접근</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- parent url: www.example.com --></span>
<span class="c"><!-- current url: www.example-sub.com --></span>
<span class="nt"><script></span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="c1">// error</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">parent</span><span class="p">.</span><span class="nx">resizeIFrame</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">scrollHeight</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>ex) Ajax 를 이용하여 출처가 다른 Rest API 호출</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// current url: www.example.com</span>
<span class="c1">// error </span>
<span class="nx">$</span><span class="p">.</span><span class="nx">getJson</span><span class="p">(</span><span class="dl">"</span><span class="s2">www.openapi.com/image?appKey=~</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(){</span>
<span class="p">}).</span><span class="nx">fail</span><span class="p">(</span><span class="nx">fucntion</span><span class="p">(){</span>
<span class="p">});</span>
</code></pre></div></div>
<ul>
<li><strong>2.</strong> 동일 출처 원칙은 Html 문서 안에 포함된 image, css, 동적으로 로드된 스크립트 에는 적용되지 않음
<ul>
<li>CSRF(Cross-site_request_forgery) 공격은 이러한 점을 이용한 공격</li>
</ul>
</li>
</ul>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- current url: www.example.com --></span>
<span class="c"><!-- bootstrap css --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"</span><span class="nt">></span>
<span class="c"><!-- bootstrap js --></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"</span><span class="nt">></script></span>
<span class="c"><!-- pixabay image --></span>
<span class="nt"><img</span> <span class="na">src=</span><span class="s">"https://pixabay.com/ko/photos/%EC%97%B0%EB%91%90-%EB%85%B9%EC%83%89-%EC%9E%8E-%EC%9E%90%EC%97%B0-%EB%82%98%EB%AD%87%EC%9E%8E-4291098/"</span><span class="nt">/></span>
</code></pre></div></div>
<h2 id="동일-출처-원칙-회피-방법">동일 출처 원칙 회피 방법</h2>
<h3 id="1-documentdomain-속성">1. document.domain 속성</h3>
<ul>
<li>이 방법은 부분적으로 도메인이 다른경우에 적용할 수 있는 방법</li>
<li>orders.example.com 과 catelog.example.com 처럼 부분적으로 도메인이 다른 경우, document.domain = “example.com” 으로 설정하여 두 도메인의 출처를 같도록 할 수 있음</li>
</ul>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// current url : orders.example.com</span>
<span class="c1">// call url : catelog.example.com</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">domain</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">example.com</span><span class="dl">"</span>
</code></pre></div></div>
<ul>
<li>이 방법을 사용하는 경우 암시적으로 port 번호는 null 로 설정</li>
<li>브라우저에서 해당 속성을 지정하기 위해서 2개 페이지 모두 해당 속성을 지정해야함</li>
</ul>
<h3 id="2-corscross-orgin-resource-sharing">2. CORS(Cross-Orgin Resource Sharing)</h3>
<ul>
<li>동일 출처 원칙을 회피하기 위한 방법으로 출처 자원 공유(CORS) 라는 방법이 표준으로 자리 잡음
<ul>
<li>W3C 에서 권장하는 메커니즘 : <a href="https://www.w3.org/TR/cors/#cross-origin-request-with-preflight0">W3C CORS</a></li>
</ul>
</li>
<li>이 방법은 HTTP 에 새로운 Origin 요청 헤더와 새로운 Access-Control-Allow-Origin 응답 헤더를 추가하는 방법</li>
<li>서버에서는 파일 요청을 허용한 명시적인 출처 리스트에 헤더를 사용하여 해당 사이트가 요청을 하도록 허용하는 방법</li>
<li>파이어폭스, 사파리, IE 10 에서는 이런 방법으로 XMLHttpRequest 로 원본 요청을 허용함</li>
</ul>
<p><strong>CORS 종류</strong></p>
<ul>
<li>Simple Request</li>
<li>Preflight Request</li>
<li>Credential Request</li>
<li>Request without Credential</li>
</ul>
<p><strong>Simple Request</strong></p>
<ul>
<li>Simple Request는 아래의 조건을 만족해야 함 (클라이언트 쪽 제약)
<ul>
<li>HTTP METHOD: GET, HEAD, POST 만</li>
<li>수동으로 설정 가능한 HTTP 헤더: Accept, Accept-Language, Content-Language, Content-Type</li>
<li>가능한 Content-Type 종류: application/x-www-form-urlencoded, mulitpart/form-data, text/plain</li>
</ul>
</li>
<li>동작 방식<br />
http://foo.example 에서 http://bar.other 에 요청하는 예시</li>
</ul>
<p><img src="/assets/images/posts/20190721/cors_simple_request.png" alt="No Image" /></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">invocation</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">http://bar.other/resources/post-here/</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">body</span> <span class="o">=</span> <span class="dl">'</span><span class="s1"><?xml version="1.0"?><person><name>Arun</name></person></span><span class="dl">'</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">callOtherDomain</span><span class="p">(){</span>
<span class="k">if</span><span class="p">(</span><span class="nx">invocation</span><span class="p">)</span>
<span class="p">{</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="dl">'</span><span class="s1">X-PINGOTHER</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pingpong</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">application/xml</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="nx">handler</span><span class="p">;</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">body</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><strong>요청 헤더</strong></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /resources/public-data/ HTTP/1.1
Host: bar.other
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<p><strong>응답 헤더</strong></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.1 200 OK
Access-Control-Allow-Origin: <span class="k">*</span>
<span class="c"># *: 모든 호스트에 대하여 허용을 의미 </span>
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>서버에서 응답 헤더에 Access-Control-Allow-Origin 속성을 추가하여 클라이언트가 컨탠츠를 사용하는 것을 허용</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Access-Control-Allow-Origin: http://foo.example
</code></pre></div></div>
<ul>
<li>특정 호스트에 대해서만 허용하는 방법</li>
</ul>
<p><strong>Preflight Request</strong></p>
<ul>
<li>Simple Request 가 아닌 경우 Preflight Request 에 해당한다</li>
<li>Preflight Request 는 클라이언트에서 서버에 요청을 보내기전에 OPTIONS 메소드 방식으로 요청하는 것을 말함</li>
<li>동작 방식</li>
</ul>
<p><img src="/assets/images/posts/20190721/cors_preflight_request.png" alt="No Image" /></p>
<ul>
<li>먼저 서버에 Preflight Request 를 보냄</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
Origin: http://foo.example
Access-Control-Request-Method: POST <span class="c"># 실제 보낼 요청을 전달 </span>
Access-Control-Request-Headers: X-PINGOTHER <span class="c"># 실제 요청의 커스텀 헤더 </span>
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>서버에서 Preflight Request 에 대한 응답</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example <span class="c"># 가능한 origin</span>
Access-Control-Allow-Methods: POST, GET, OPTIONS <span class="c"># 가능한 메소드</span>
Access-Control-Allow-Headers: X-PINGOTHER <span class="c"># 가능한 헤더 </span>
Access-Control-Max-Age: 1728000 <span class="c"># Preflight Request 의 캐시시간 </span>
Vary: Accept-Encoding, Origin
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>서버에 실제 요청을 전송</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST /resources/post-here/ HTTP/1.1
Host: bar.other
X-PINGOTHER: pingpong <span class="c"># 커스텀 헤더 </span>
Referer: http://foo.example/examples/preflightInvocation.html
Origin: http://foo.example
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>서버에서 실제 요청에 대해 응답</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<p><strong>Credential Request</strong></p>
<ul>
<li>Http Cookie, Http Authentication 등의 인증된 정보를 인식할 수 있게 하는 요청</li>
</ul>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">invocation</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">http://bar.other/resources/credentialed-content/</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">callOtherDomain</span><span class="p">(){</span>
<span class="k">if</span><span class="p">(</span><span class="nx">invocation</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">withCredentials</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="nx">handler</span><span class="p">;</span>
<span class="nx">invocation</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>withCredentials 설정을 true 함으로써 Credential Request 요청</li>
<li>Credential Request 는 조건에 따라서 Simple Reqeust / Preflight Request 이다.</li>
<li>쿠키나 인증정보를 서버에서 응답하는 경우 해당 정보들에 접근하려는 상황이 Credential Request 임
<ul>
<li>실제 Ajax 등을 이용한 서버의 응답 쿠키는 자동으로 갱신되지 않음</li>
</ul>
</li>
<li>요청 헤더</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: <span class="nv">pageAccess</span><span class="o">=</span>2
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>응답 헤더</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: <span class="nb">true</span> <span class="c"># 쿠키 접근 가능함</span>
<span class="c"># 응답 쿠키 </span>
Set-Cookie: <span class="nv">pageAccess</span><span class="o">=</span>3<span class="p">;</span> <span class="nv">expires</span><span class="o">=</span>Wed, 31-Dec-2008 01:34:53 GMT
<span class="c"># ... 생략 ...</span>
</code></pre></div></div>
<ul>
<li>서버에서 응답 헤더에 Access-Control-Allow-Credentials: true 를 반환함으로써 클라이언트에서 쿠키 접근이 가능해짐</li>
<li>Access-Control-Allow-Origin: * 과 같은 응답은 불가능함</li>
</ul>
<p><strong>Request without Credential</strong></p>
<ul>
<li>CORS 요청은 기본적으로 Non-Credential 요청</li>
<li>withCredentials 설정을 한 경우에만 Credential 요청</li>
</ul>
<h3 id="3-cross-document-messaging">3. Cross-document messaging</h3>
<ul>
<li>이 방법은 브라우저에서 제공하는 메시징 API 를 이용하는 방법으로 각 스크립트의 출처와 관계없이 텍스트 메시지를 전송 가능</li>
<li>메시징 API 는 비동기로 호출</li>
<li>이 방법은 출처에 대한 확인이 충분하지 않을 경우 보안 이슈가 발생할 수 있다.</li>
</ul>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/** targetWindow.postMessage(message, targetOrigin, [transfer])
* targetWindow: 메시지를 전송할 윈도우 객체 (iframe, opener, parent, etc)
* message: 전송할 테스트 메시지
* targetOrigin: targetWindow 의 origin 을 명시적으로 작성
* targetWindow 의 실제 URL 과 다를 경우 메시지는 전달되지 않음
* 특정 도메인이 아닌 경우 * 로 지정 (이 방법은 보안에 취약함)
* transfer: 메시지와 함께 전송되는 일련의 개체
**/</span>
<span class="kd">function</span> <span class="nx">receiveMessage</span><span class="p">(</span><span class="nx">event</span><span class="p">){</span>
<span class="c1">// 예상하는 origin 일때만 기능 수행</span>
<span class="k">if</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">origin</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">www.example.com</span><span class="dl">"</span><span class="p">){</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span> <span class="c1">// 전송된 문자열 메시지</span>
<span class="c1">//event.source : 이벤트를 보낸 윈도우 객체 </span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// message 이벤트 등록 </span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">message</span><span class="dl">"</span><span class="p">,</span> <span class="nx">receiveMessage</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre></div></div>
<h3 id="4-jsonp-json-with-padding">4. JSONP (JSON with Padding)</h3>
<ul>
<li>이 방법은 script 태그의 src 속성을 이용하여 데이터를 주고 받는 방법</li>
<li>위에서 언급했듯이 Same-origin Policy 는 서로 다른 origin 에 대해서 script 를 Html 내부에 임베디드 하는 것은 허용한다는 점을 이용함</li>
<li>데이터를 응답 해주는 origin 에서 데이터를 요청한 orgin 에서 전달한 callback 함수를 호출하여 데이터를 주고 받는 방식</li>
<li>
<p>JSONP 는 script 태그를 DOM 임베드함으로써 동작하기 때문에 GET 방식으로만 구현됨</p>
</li>
<li>동작 원리</li>
</ul>
<blockquote>
<p>데이터를 요청하는 Origin : www.example.com <br />
데이터를 응답하는 Origin : www.sample.com</p>
</blockquote>
<p><strong>1.</strong> www.example.com 에서는 데이터 요청을 위해 script 태그를 작성하고 해당 요청에 대한 응답 함수를 정의</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- www.example.com 페이지의 head --></span>
<span class="nt"><head></span>
<span class="c"><!-- 응답시 동작할 call back 함수 작성 --></span>
<span class="nt"><script></span>
<span class="kd">function</span> <span class="nx">callbackLog</span><span class="p">(</span><span class="nx">otherData</span><span class="p">){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">otherData</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt"></script></span>
<span class="c"><!-- 요청을 위한 script 태그 작성 --></span>
<span class="nt"><script </span><span class="na">type=</span><span class="s">"application/javascript"</span> <span class="na">src=</span><span class="s">"www.sample.com?callback=callbackLog"</span><span class="nt">></script></span>
<span class="nt"></head></span>
</code></pre></div></div>
<p><strong>2.</strong> www.sample.com 에서는 응답 정보를 작성</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@RequestMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/test"</span><span class="o">,</span> <span class="n">method</span> <span class="o">=</span> <span class="nc">RequestMethod</span><span class="o">.</span><span class="na">GET</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">jsonpTest</span><span class="o">(</span><span class="nd">@RequestParam</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"callback"</span><span class="o">)</span> <span class="nc">String</span> <span class="n">callback</span><span class="o">){</span>
<span class="c1">// DB 조회등 서버 데이터 세팅</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">></span> <span class="n">serverMap</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">>();</span>
<span class="n">serverMap</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"serverCode"</span><span class="o">,</span> <span class="s">"jsonpGood"</span><span class="o">);</span>
<span class="c1">// 화면에서 전달 받은 호출할 콜백 함수에 data 를 넣어서 반환 </span>
<span class="k">return</span> <span class="n">callback</span> <span class="o">+</span> <span class="s">"("</span> <span class="o">+</span> <span class="n">serverMap</span> <span class="o">+</span> <span class="s">")"</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>JSONP vs Ajax</strong></p>
<p><a href="http://dev.epiloum.net/1311">이미지 출처: http://dev.epiloum.net/1311</a><br />
<img src="/assets/images/posts/20190721/comparison_between_ajax_and_jsonp.png" alt="No Image" /></p>
<ul>
<li>JSONP는 기본적인 동작 방식에는 Ajax와 큰 차이가 없지만 사용법의 차이가 존재</li>
<li>Ajax는 응답을 responseText 속성으로 가져와서 XMLHttpRequest.onreadystatechange 에 있는 콜백 함수를 실행</li>
<li>JSONP는 콜백함수 호출 코드를 서버에서 완성하여 반환</li>
</ul>
<h3 id="5-websockets">5. WebSockets</h3>
<ul>
<li>현대의 브라우저는 웹 소켓 API 를 제공함</li>
<li>요청 헤더에 Origin 속성에서 클라이언트의 호스트 정보를 알 수 있는 것을 이용하는 방식으로 서버 측에서 Origin 헤더를 검사하여 컨텐츠 접근을 허용하는 방식</li>
</ul>
<h3 id="todo">TODO</h3>
<ul>
<li>Ajax 요청은 쿠키 업데이트 불가</li>
</ul>
<p><a href="https://stackoverflow.com/questions/3340797/can-an-ajax-response-set-a-cookie">https://stackoverflow.com/questions/3340797/can-an-ajax-response-set-a-cookie</a></p>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Same-origin_policy">https://en.wikipedia.org/wiki/Same-origin_policy</a></li>
<li><a href="https://iamawebdeveloper.tistory.com/38">https://iamawebdeveloper.tistory.com/38</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy">https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy</a></li>
<li><a href="https://www.internetmap.kr/entry/Sameorigin-policy">https://www.internetmap.kr/entry/Sameorigin-policy</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS">https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS</a></li>
<li><a href="https://zamezzz.tistory.com/137">https://zamezzz.tistory.com/137</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage</a></li>
<li><a href="https://en.wikipedia.org/wiki/JSONP">https://en.wikipedia.org/wiki/JSONP</a></li>
<li><a href="http://dev.epiloum.net/1311">http://dev.epiloum.net/1311</a></li>
</ul>nesskoungho632@gmail.comUpper Bound 와 Lower Bound2019-07-14T00:00:00+00:002019-07-20T00:00:00+00:00https://kimkoungho.github.io//algorithms/lowebound-upperbound<p>여기서 설명하는 <strong>Upper Bound</strong> 와 <strong>Lower Bound</strong> 는 이분 탐색(Binary Search)에 Upper Bound 와 Lower Bound 알고리즘을 정리한 것.<br />
(c++ std 라이브러리)</p>
<p><a href="https://ko.wikipedia.org/wiki/%EC%83%81%ED%95%9C%EA%B3%BC_%ED%95%98%ED%95%9C">위키</a> 에서는 집합에서의 Upper Bound 와 Lower Bound 를 설명하고 있음</p>
<h2 id="upper-bound">Upper Bound</h2>
<ul>
<li>std::upper_bound() 함수는 정렬된 배열 [first, end) range 에서 찾으려는 값(key) 보다 큰 처음 요소의 위치를 반환한다</li>
<li>[first, end) : first 를 포함하고 end 는 포함하지 않는 구간을 의미</li>
<li>end 를 포함하지 않는 이유 : 모든 요소가 찾으려는 값(key) 보다 작은 경우 end 를 반환</li>
<li><a href="http://www.cplusplus.com/reference/algorithm/upper_bound/">c++ std::upper_bound</a></li>
<li>이진 탐색을 이용하여 upper bound 를 찾는 예제</li>
</ul>
<blockquote>
<p>입력 데이터<br />
array: 10 10 10 20 20 20 30 30<br />
key: 20</p>
</blockquote>
<p><strong>1.</strong> first = 0, end = 8</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> v - first v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] = key -> 오른쪽 구간으로 이동
</code></pre></div></div>
<p><strong>2.</strong> first = 5, end = 8</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> first - v v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] > key -> 왼쪽 구간으로 이동
</code></pre></div></div>
<p><strong>3.</strong> first = 5, end = 6</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> first - v v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] = key -> 오른쪽 구간으로 이동
</code></pre></div></div>
<p><strong>4.</strong> first = end -> 종료</p>
<h3 id="구현-코드">구현 코드</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">upperBound</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">arr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">first</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">end</span> <span class="o">=</span> <span class="n">arr</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="k">while</span><span class="o">(</span><span class="n">first</span> <span class="o"><</span> <span class="n">end</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="o">(</span><span class="n">first</span> <span class="o">+</span> <span class="n">end</span><span class="o">)</span> <span class="o">>>></span> <span class="mi">1</span><span class="o">;</span>
<span class="c1">// if) from + 1 = to, mid = from </span>
<span class="k">if</span><span class="o">(</span><span class="n">key</span> <span class="o">>=</span> <span class="n">arr</span><span class="o">[</span><span class="n">mid</span><span class="o">])</span> <span class="o">{</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span><span class="k">else</span> <span class="o">{</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">mid</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">first</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="lower-bound">Lower Bound</h2>
<ul>
<li>std::lower_bound() 함수는 정렬된 배열 [first, end) range 에서 찾으려는 값(key) 보다 작지 않은 처음 요소의 위치를 반환한다</li>
<li>[first, end) : first 를 포함하고 end 는 포함하지 않는 구간을 의미</li>
<li>end 를 포함하지 않는 이유 : 모든 요소가 찾으려는 값(key) 보다 작은 경우 end 를 반환</li>
<li><a href="http://www.cplusplus.com/reference/algorithm/lower_bound/">c++ std::lower_bound</a></li>
</ul>
<blockquote>
<p>입력 데이터<br />
array: 10 10 10 20 20 20 30 30<br />
key: 20</p>
</blockquote>
<p><strong>1.</strong> first = 0, end = 8</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> v - first v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] = key -> 왼쪽 구간으로 이동
</code></pre></div></div>
<p><strong>2.</strong> first = 0, end = 4</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>v - first v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] < key -> 오른쪽 구간으로 이동
</code></pre></div></div>
<p><strong>3.</strong> first = 3, end = 4</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>first- v v - end
10 10 10 20 20 20 30 30 x
^ - mid
array[mid] < key -> 오른쪽 구간으로 이동
</code></pre></div></div>
<p><strong>4.</strong> first = end -> 종료</p>
<h3 id="구현-코드-1">구현 코드</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">lowerBound</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">arr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">first</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">end</span> <span class="o">=</span> <span class="n">arr</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="k">while</span><span class="o">(</span><span class="n">first</span> <span class="o"><</span> <span class="n">end</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="o">(</span><span class="n">first</span> <span class="o">+</span> <span class="n">end</span><span class="o">)</span> <span class="o">>>></span> <span class="mi">1</span><span class="o">;</span>
<span class="c1">// </span>
<span class="k">if</span><span class="o">(</span><span class="n">key</span> <span class="o"><=</span> <span class="n">arr</span><span class="o">[</span><span class="n">mid</span><span class="o">])</span> <span class="o">{</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">mid</span><span class="o">;</span>
<span class="o">}</span><span class="k">else</span> <span class="o">{</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">end</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="binary-search-upper-bound-lower-bound">Binary Search, Upper Bound, Lower Bound</h3>
<p>예제를 통해 Binary Search, Upper Bound, Lower Bound 를 비교</p>
<blockquote>
<p>입력 데이터<br />
array: 2 3 4 5 5 5 6 7 9</p>
</blockquote>
<p><strong>중복 된 데이터를 찾는 경우</strong> : key = 5</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> binary search
v v - upper bound
2 3 4 5 5 5 6 7 9
^ - lower bound
</code></pre></div></div>
<ul>
<li>binary search: 배열의 총 길이에 따라 결과 값이 달라짐</li>
<li>upper bound / lower bound : 항상 같은 결과</li>
</ul>
<p><strong>찾으려는 값이 배열의 모든 요소보다 작은 경우</strong> : key = 1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>v - upper bound
2 3 4 5 5 5 6 7 9
^ - lower bound
</code></pre></div></div>
<ul>
<li>binary search: 값이 존재하지 않으므로 -1 반환 (not found)</li>
<li>lower bound / upper bound : 0 반환, [first, end) 구간에서 first 를 반환함</li>
</ul>
<p><strong>찾으려는 값이 배열의 모든 요소보다 큰 경우</strong> : key = 10</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> v - upper bound
2 3 4 5 5 5 6 7 9 x
^ - lower bound
</code></pre></div></div>
<ul>
<li>binary search: 값이 존재하지 않으므로 -1 반환 (not found)</li>
<li>lower bound / upper bound : 10 반환, [first, end) 구간에서 end 를 반환함</li>
</ul>
<p><strong>찾으려는 값이 배열에 없는 경우</strong> : key = 8</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> v - upper bound
2 3 4 5 5 5 6 7 9
^ - lower bound
</code></pre></div></div>
<ul>
<li>binary search: 값이 존재하지 않으므로 -1 반환 (not found)</li>
<li>lower bound / upper bound : 9 반환</li>
</ul>
<blockquote>
<p>binary search 는 배열에서 찾으려는 값(key)가 해당 배열에 있다는 가정이 있음<br />
따라서 값을 찾지 못했을 경우, 음수 값을 반환
실제로 <a href="https://en.cppreference.com/w/cpp/algorithm/binary_search">c++ std::binary_search</a> 의 구현에서 내부적으로 std::lower_bound() 호출하여 구현하고 있음</p>
</blockquote>
<p><a href="https://github.com/kimkoungho/Algorithm/blob/master/src/search/BinarySearch.java">git 소스</a></p>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EC%83%81%ED%95%9C%EA%B3%BC_%ED%95%98%ED%95%9C">https://ko.wikipedia.org/wiki/%EC%83%81%ED%95%9C%EA%B3%BC_%ED%95%98%ED%95%9C</a></li>
<li><a href="http://www.cplusplus.com/reference/algorithm/upper_bound/">http://www.cplusplus.com/reference/algorithm/upper_bound/</a></li>
<li><a href="http://www.cplusplus.com/reference/algorithm/lower_bound/">http://www.cplusplus.com/reference/algorithm/lower_bound/</a></li>
<li><a href="https://stackoverflow.com/questions/41958581/difference-between-upper-bound-and-lower-bound-in-stl">https://stackoverflow.com/questions/41958581/difference-between-upper-bound-and-lower-bound-in-stl</a></li>
<li><a href="https://stackoverflow.com/questions/28389065/difference-between-basic-binary-search-for-upper-bound-and-lower-bound">https://stackoverflow.com/questions/28389065/difference-between-basic-binary-search-for-upper-bound-and-lower-bound</a></li>
<li><a href="https://en.cppreference.com/w/cpp/algorithm/binary_search">https://en.cppreference.com/w/cpp/algorithm/binary_search</a></li>
</ul>nesskoungho632@gmail.com