(쿠버네티스) 쿠버네티스란 - 쿠버네티스 인 액션
쿠버네티스 등장 이유
거대한 모놀리식 레거시 애플리케이션은 점차 마이크로서비스라고 하는 더 작고 독립적으로 실행 가능한 구성 요소로 쪼개졌다. 마이크로서비스는 서로 분리돼 있기 때문에 개별적으로 개발, 배치, 업데이트, 확장을 할 수 있다. 따라서 오늘날 급변하는 비즈니스 요구 사항을 충족시킬 수 있을 만큼 자주, 신속하게 구성 요소를 변경할 수 있다.
하지만 배포 가능한 구성 요소의 수가 증가하고 데이터 센터 규모가 커지면서 전체 시스템을 원활하게 구성, 관리, 유지하는 것이 점점 어려워졌다. 효율적으로 리소스를 사용해 하드웨이 비용을 낮추려면 각 구성 요소를 어디에 배치해야 하는지 파악하는 것이 훨씬 더 어렵다. 수동으로 이 모든 작업을 수행하기는 어렵다.
서버의 구성 요소를 자동으로 스케줄링하고, 자동으로 구성하고, 감독하고, 오류를 처리하는 자동화가 필요해진 것이다. 이것이 쿠버네티스가 등장한 이유이다.
1.1 쿠버네티스 시스템이 필요한 이유
1.1.1 모놀리식 애플리케이션에서 마이크로서비스로의 전환
- 모놀리식 애플리케이션은 모든 것이 서로 강하게 결합해 구성되어 조금만 애플리케이션을 변경해도 전체 애플리케이션을 다시 배포해야 한다.
- 시간이 지남에 따라 애플리케이션이 점점 더 복잡해져 결국 시스템 전체의 품질이 저하된다.
- 스케일 아웃은 애플리케이션의 코드를 대폭 변경해야 하는 상황이 발생했을 때 수평적으로 확장하기 어렵거나 불가능할 수 있다.
마이크로서비스로 애플리케이션 분리
- 마이크로서비스는 독립적인 프로세스로 실행되며 잘 정의된 API를 통해 통신한다.
- 마이크로서비스는 RESTful API를 제공하기 위해 HTTP 같은 동기 프로토콜을 이용해 통신하거나 AMQP(Advanced Message Queuing Protocol) 같은 비동기 프로토콜을 이용해 통신한다.
- 마이크로 서비스는 상대적으로 정적인 외부 API가 있는 독립형 프로세스이기 때문에 마이크로서비스를 개별적으로 개발하고 배포할 수 있다.
마이크로서비스의 확장
- 마이크로서비스 별로 확장할 수 있다. 리소스가 부족한 서비스가 있다면 해당 서비스만 확장한다.
- 특정 구성 요소는 복제돼 여러 서버에 배포된 다수의 프로세스가 실행
- 특정 시스템에서 운영 중인 모놀리식 애플리케이션의 일부가 확장 가능하지 않아 수평 확장이 불가능하다면 수직으로 확장할 수 있다.
마이크로서비스 배포
- 마이크로서비스는 팀으로 함께 작업을 수행하므로 서로를 찾고 통신할 수 있어야 한다.
- 이들을 배포할 때 누군가 또는 무언가는 단일 시스템으로 작동할 수 있도록 모든 마이크로 서비스를 올바르게 구성할 수 있어야 한다.
- 마이크로서비스는 여러 프로세스와 시스템에 분산돼 있기 때문에 실행 호출을 디버그하고 추적하기 어려운 문제가 있다.
- 다행히 이런 문제는 집킨 같은 분산형 추적 시스템으로 다룰 수 있게 됐다.
환경 요구사항의 다양성
- 마이크로서비스는 독립적인 방식으로 개발
- 구성 요소의 독립성과 각 구성 요소를 개발하는 팀이 따로 있기 때문에 팀의 상황에 따라 다른 라이브러리를 사용하고 바꾸는 것은 다른 팀을 방해하지 않아야 한다.
- 동일한 서버에 배포해야 하는 구성 요소 수가 많을 수록 모든 요구 사항을 충족시키면서 모든 종속성을 관리하는 것은 어렵다.
1.1.2 애플리케이션에 일관된 환경 제공
1.1.3 지속적 전달로 이동 : 데브옵스와 노옵스
- 애플리케이션을 개발한느 사람이 배포에도 참여하고 전체 라이프사이클을 관리할 수 있게 팀을 구성하는 것이 효율적이다.
- 개발자, 품질 보증, 운영 팀이 전체 프로세스를 다 함께 작업해야 한다.
- 이런 작업 방식을 데브옵스(DevOps) 라고 부른다.
데브옵스의 장점
- 개발자가 운영에서 좀 더 많은 시간을 애플리케이션을 실행하는 데 쏟게 되면 사용자가 무엇을 필요로 하는지, 어떤 문제가 있는지 더 잘 이해하게 되고, 운영 팀이 애플리케이션을 유지하는 동안 직면하는 문제를 더 잘 알 수 있다.
- 애플리케이션을 좀 더 신속하게 제공할 수 있으므로 사용자의 피드백을 받아 추가 개발 여부를 검토할 수도 있다.
- 애플리케이션 버전을 더 자주 릴리즈하려면 배포 프로세스를 간소화해야 한다. 가장 좋은 방법은 개발자가 작업자를 기다리지 않고 직접 애플리케이션을 배포하는 것이다.
개발자와 시스템 관리자가 최고로 잘 할 수 있는 것을 하게 하는 것
- 하드웨어 인프라를 전혀 알 필요 없이, 운영 팀을 거치지 않고 개발자가 직접 애플리케이션을 배포하는 방식이 이상적이다. 이를 노옵스(NoOps) 라고 부른다.
- 애플리케이션을 배포하고 실행하는 단일 플랫폼으로써 실제 하드웨어를 추상화하고 노출함으로써, 개발자는 시스템 관리자의 도움 없이도 애플리케이션을 구성하고 배포할 수 있다.
1.2 컨테이너 기술 소개
쿠버네티스는 리눅스 컨테이너 기술을 사용해 실행 중인 애플리케이션을 격리하므로 쿠버네티스 자체를 파고 들기 전에 컨테이너의 기본 사항에 익숙해져야 한다. 그리고 쿠버네티스가 자체적으로 수행하는 작업과 도커 또는 rkt(락잇) 같은 컨테이너 기술로 변화되는 내용을 이해해야 한다.
1.2.1 컨테이너 이해
리눅스 컨테이너 기술을 통한 컴포넌트 격리
- 개발자는 가상머신을 사용해 각 마이크로서비스의 환경을 격리하는 대신 리눅스 컨테이너 기술을 사용한다.
- 이 기능을 사용하면 같은 호스트 시스템에서 여러 서비스를 실행할 수 있으며 동시에 각 서비스에게 각기 다른 환경을 만들어 줄 뿐만 아니라 가상머신에 비해 오버헤드가 훨씬 적다.
- 프로세스가 별도의 운영체제에서 실행되는 가상머신과 달리 컨테이너에서 실행하는 프로세스는 다른 모든 프로세스와 마찬가지로 호스트의 운영체제 내부에서 실행된다. 그러나 컨테이너의 프로세스는 다른 프로세스와 여전히 분리돼 있다. 프로세스 입장에서는 시스템 및 운영체제에서 실행되는 유일한 프로세스인 것처럼 보인다.
가상머신과 컨테이너 비교
- 컨테이너는 가상머신에 비해 훨씬 가볍기 때문에 동일한 하드웨에에서 더 많은 소프트웨어 구성 요소를 실행할 수 있다.
- 가상머신은 각 하드웨어가 자체 시스템 프로세스 집합을 실행해야 하기 때문에 자체 프로세스에서 소비되는 것 이외에도 컴퓨팅 리소스가 필요하다.
- 컨테이너는 호스트 OS에서 실행하는 단일 격리된 프로세스 이상으로 리소스를 소비하지 않고 애플리케이션에서 사용하는 리소스만 사용하므로 더이상 프로세스의 오버헤드가 발생하지 않는다.
- 한 호스트에서 세 개의 가상머신을 실행하면 동일한 베어 메탈 하드웨어를 실행하고 완전히 분리된 3개의 운영체제가 실행된다. 이런 가상머신은 호스트의 OS와 하이퍼바이저를 기반으로 한다.
- 컨테이너는 모두 호스트 OS에서 동작하는 정확히 동일한 커널에서 시스템을 호출한다.
- 단일 커널은 호스트의 CP에서 명령어를 수행하는 유일한 커널이다.
- 동일한 시스템에서 더 많은 프로세스를 격리해 실행하려면 오버헤드가 낮은 컨테이너를 선택
컨테이너 격리를 가능하게 하는 메커니즘 소개
- 리눅스 네임스페이스(Linux Namespaces)
- 각 프로세스가 파일, 프로세스, 네트워크 인터페이스, 호스트 이름 등 시스템에 독립 뷰를 제공하는 방법
- 리눅스 컨트롤 그룹
- 프로세스가 소비할 수 있는 리소스의 양(CPU, 메모리, 네트워크 대역폭 등)을 제한하여 제공
1.2.2 도커 컨테이너 플랫폼 소개
- 도커는 컨테이너를 여러 컴퓨터에 쉽게 이식 가능하게 하는 최초의 컨테이너 시스템이다.
- 컨테이너 시스템은 애플리케이션뿐만 아니라 라이브러리와 여러 종속성, 심지어 전체 운영체제 파일 시스템에 이르기까지 패키징 프로세스를 단순화 시켰고, 도커가 운영 중인 다른 머신에 애플리케이션이 프로비저닝 가능하도록 단순하고 간편한 포터블 패키지로 변화시켰다.
도커의 개념 이해
- 도커는 애플리케이션의 패키징, 배포, 실행 플랫폼이다.
- 애플리케이션을 전체 환경과 함꼐 패키징 할 수 있다.
- 이미지
- 애플리케이션과 환경을 패캐지로 묶음
- 도커 이미지
- 레지스트리
- 도커 이미지를 저장하고 이미지를 쉽게 공유할 수 있는 스토리지
- 도커 허브
- 컨테이너
- 도커 기반 컨테이너 이미지에서 생성된 일반적인 형태의 리눅스 컨테이너
- 컨테이너는 도커를 실행하는 호스트에서 실행되는 프로세스지만 호스트와 호스트에서 실행되는 모든 프로세스 간에 완전히 분리돼 있다.
- 할당된 리소스(CPU, RAM 등)만 액세스하고 사용할 수 있다.
이미지 레이어의 이해
- 도커 이미지는 레이어로 구성
- 도커 이미지는 다른 이미지 위에 올라갈 수 있기 때문에 동일한 레이어를 포함할 수 있다.
- 첫 번째 이미지의 일부로 전송된 레이어는 다른 이미지를 전송할 때 다시 전송할 필요가 없기 때문에 네트워크에서 이미지 배포 속도를 증가시킬 수 있다.
- 각 레이어는 한번 저장되기 때문에 동일한 기본 레이어를 기반으로 하는 이미지로 만든 두 컨테이너는 동일한 파일을 읽을 수 있지만 그 중 하나가 해당 파일을 덮어쓰는 경우 다른 하나는 변경 내용을 볼 수 없다.
- 컨테이너 이미지 레이어가 읽기 전용이기 때문에 컨테이너가 실행되면 이미지의 레이어 위에 새로운 쓰기 가능한 레이어가 만들어진다.
- 컨테이너의 프로세스가 기본 레이어 중 하나에 있는 파일에 쓸 떼 프로세스가 복사본에 쓰게 된다.
컨테이너 이미지의 제한적인 이식성 이해
- 컨테이너 이미지는 도커를 실행하는 모든 리눅스 시스템에서 실행할 수 있지만 모든 컨테이너가 호스트의 리눅스 커널을 사용하기 때문에 주의할 것이 있다.
- 컨테이너화된 애플리케이션이 특정 커널 버전을 요구한다면 작동하지 않을 수 있다.
- 반대로 가상 머신은 자체 커널을 실행하기 때문에 가상머신에는 이미지에 대한 이런 제약이 없다.
- 또 특정 하드웨어 아키텍처를 위해 만들어진 컨테이너화된 애플리케이션은 같은 아키텍처를 사용하는 시스템에서만 실행될 수 있다.
- x86 아키텍처용으로 구축된 애플리케이션을 ARM 기반의 시스템에서 실행할 수 없는 경우
1.2.3 rkt 소개 : 도커의 대안
도커 자체가 프로세스를 격리하는 것이 아닌 리눅스 커널 수준에서 리눅스 네임스페이스나 리눅스 그룹 컨트롤 같은 커널 기능을 사용해 수행한다.
도커의 성공 이후 컨테이너 형식 및 런타임에 대한 개방된 업계 표준을 만들기 위해 OCI(Open Container Initiative)가 탄생했다. 도커는 리눅스 컨테이너 엔진인 rkt와 마찬가지로 OCI의 일부이다. rkt는 컨테이너를 실행하는 플랫폼이다. 보안, 결합성, 공개 표준준수에 중점을 둔다.
1.3 쿠버네티스 소개
시스템에 배포 가능한 애플리케이션 구성 요소의 수가 증가함에 따라 이를 모두 관리하는 것이 어렵다는 사실을 알았다. 구글은 소프트웨어 구성 요소와 인프라를 전세계에 확장할 수 있는 훨씬 더 좋은 방법이 필요하다는 것을 깨달은 최초의 회사일 것이다.이로 인해 수천 개의 소프트웨어 구성 요소를 개발하고 배포 할 수 있는 솔루션을 관리 가능하게 하고 비용을 효율화하여 개발해야 했다.
1.3.1 쿠버네티스의 기원
구글은 보그(Borg) 이후 오메가(Omega)로 바뀐 시스템을 통해 애플리케이션 개발자와 시스템 관리자가 수천 개의 애플리케이션과 서비스를 관리하는 데 도움을 주었다. 보그와 오메가를 10년 동안 비밀로 유지해오다가, 2014년 구글은 구글 시스템을 통해 얻은 경험을 바탕으로 한 오픈소스 시스템인 쿠버네티스를 출시했다.
1.3.2 넓은 시각으로 쿠버네티스 바라보기
- 컨테이너 시스템 상에서 컨테이너 애플리케이션을 쉽게 배포하고 관리할 수 있게 해주는 소프트웨어 시스템이다.
- 리눅스 컨테이너 기능을 사용해 이기종 애플리케이션의 내부 세부 정보를 알지 못하게 하고 이런 애플리케이션을 각 호스트에 수동으로 배포하지 않고도 이기종 애플리케이션을 실행할 수 있다.
- 클라우드 제공 업체는 호스팅된 애플리케이션을 완벽하게 격리하면서 하드웨어를 최대한 활용하기 위해 노력해야 하기 때문에 클라우드 공급자에게는 가장 중요한 점이다.
- 쿠버네티스를 사용하면 마치 모든 노드가 하나의 거대한 컴퓨터 인 것처럼, 수천개의 컴퓨터 노드에서 소프트웨어 애플리케이션을 실행할 수 있다.
- 기본 인프라를 추상화해 개발 및 운영 팀의 개발, 배포, 관리를 단순화 한다.
- 쿠버네티스를 통해 애플리케이션을 배포하는 경우 클러스터에 노드가 두개가 있던 수천 개가 있던 항상 동일하다. 클러스터의 크기는 전혀 차이가 없다. 추가 클러스터 노드는 배포된 애플리케이션의 사용 가능한 리소스의 수를 나타낼 뿐이다.
쿠버네티스 기능과 핵심
- 시스템은 마스터 노드와 여러 개의 워커 노드로 구성된다.
- 개발자가 애플리케이션 매니페스트를 마스터에게 제출하면 쿠버네티스는 이를 워커 노드 클러스터에 배포한다.
- 개발자가 특정 애플리케이션을 함께 실행해야 한다고 지정하면 쿠버네티스는 동일한 워커 노드에 배포한다.
핵심 애플리케이션 기능에 집중할 수 있도록 개발자 돕기
- 쿠버네티스는 클러스터 운영체제로 생각할 수 있다.
- 서비스 검색, 확장, 로드 밸런싱, 자가 치유, 리더 선출 같은 서비스를 포함한다.
- 따라서 애플리케이션의 실제 기능을 구현하는데 주력할 수 있다.
- 인프라와 인프라를 통합하는 방법을 파악하는 데 시간을 낭비할 필요 없다.
운영 팀이 효과적으로 리소스를 활용할 수 있도록 돕기
- 애플리케이션 실행을 유지하고 서로 통신할 수 있도록 컴포넌트에게 정보를 제공
- 애플리케이션이 어떤 노드에서 실행되어도 상관없기 때문에 애플리케이션 재배치 가능
- 애플리케이션을 혼합하고 매칭시킴으로써 수동 스케줄링보다 리소스를 훨씬 더 많이 활용할 수 있다.
1.3.3. 쿠버네티스 클러스터 아키텍처
- 마스터 노드는 전체 쿠버네티스 시스템을 관리하고 통제하는 쿠버네티스 컨트롤 플레인을 관장
- 워커 노드는 실제 배포하고자 하는 애플리케이션 실행을 담당
컨트롤 플레인
컨트롤 플레인에서는 클러스터를 관리하고 클러스터의 기능을 실행한다. 단일 마스터 노드에서 실행하거나 여러 노드로 분할되고 복제되어 고가용성을 보장할 수 있는 여러 구성 요소로 구성된다.
- 사용자와 컨트롤 플레인과 통신하는 쿠버네티스 API 서버다.
- 애플리케이션을 예약하는 스케줄러(애플리케이션의 배포 가능한 각 구성 요소에 워커 노드 할당)
- 구성 요소 복제, 워커 노드 추적, 노드 장애 처리 등 클러스터 수준 기능을 실행하는 컨트롤러 매니저
- etcd는 클러스터 구성을 지속적으로 저장하는 안정적인 분산 데이터 스토리지 컨트롤 플레인의 구성 요소는 클러스터의 상태를 유지하고 제어하지만 애플리케이션을 실행하지는 않는다. 애플리케이션은 워커 노드에서 실행된다.
노드
워커 노드는 컨테이너 화 된 애플리케이션을 실행하는 시스템으로 애플리케이션에 서비스 실행, 모니터링, 제공하는 작업은 다음 구성 요소로 수행된다.
- 컨테이너를 실행하는 도커, rkt 또는 컨테이너 런타임
- API 서버와 통신하고 노드에서 컨테이너를 관리하는 Kubelet
- 애플리케이션 구성 요소 간에 네트워크 트래픽을 분산하는 쿠버네티스 서비스 프록시(kube-proxy)
1.3.4 쿠버네티스에서 애플리케이션 실행
디스크립션이 컨테이너의 실행을 가져오는 방법
API 서버가 애플리케이션의 디스크립션을 처리할 때 스케줄러는 각 그룹에서 필요로 하는 연산 리소스와 각 노드의 할당되지 않은 리소스를 기반으로 사용 가능한 워커 노드로 컨테이너의 지정그룹을 예약한다. 그런 다음 해당 노드의 Kubelet은 필요한 컨테이너 이미지를 가져와서 컨테이너를 실행하도록 컨테이너 런타임(예 : 도커)에게 지시한다.
- 앱 디스크립터는 4개의 컨테이너를 3개의 세트로 묶어 나열(이 세트를 포드라고 지칭)
- 첫 번째 두 포드에는 각각 하나의 컨테이너만 들어 있고 마지막 포드에는 두 개의 포드가 위치
- 마지막 포드는 같은 위치에 있어야 하고 서로 격리되어서 안됨.
- 쿠버네티스에 디스크립터를 요청하면, 사용 가능한 워커 노드에 지정된 수의 포드 복제본을 예약
- 노드의 Kubelet이 이미지 레지스트리에서 컨테이너 이미지를 가져와 컨테이너를 실행하도록 도커에 알림
실행 중인 컨테이너 유지
- 애플리케이션의 배포 상태가 사용자가 제공한 디스크립션과 일치하는지 지속 확인
- 컨테이너 인스턴스 중 하나가 제대로 작동하지 않는 경우 인스턴스를 자동으로 다시 시작
- 전체 워커 노드가 죽거나 액세스 할 수 없다면 노드에서 실행 중인 모든 컨테이너의 노드를 새로 선택하고 컨테이너를 실행
복제본 수 확장
- 애플리케이션 실행 동안 복사본의 수를 늘리거나 줄일지 결정 가능
- 최적의 복제본 수를 결정하는 일을 쿠버네티스에 위임
- CPU 부하, 메모리 사용량, 초당 쿼리, 애플리케이션에서 노출하는 실시간 메트릭에 따라 숫자를 자동으로 조절 가능
이동 중인 대상에 접근하기
- k8s 는 컨테이너를 클러스터 안에서 이동 가능하다.
- when
- 실행 중인 노드가 정지했거나,
- 다른 컨테이너를 위한 공간을 만들기 위해 제거하는 경우,
- when
- 특정 서비스를 제공하는 컨테이너를 쉽게 찾을 수 있게 해준다.
- k8s 에게 동일한 서비스를 제공하는 컨테이너를 알려줌
- k8s 는 하나의 고정 ip 주소로 모든 컨테이너를 노출
- 해당 주소를 클러스터에서 실행중인 모든 app 에 노출
- 해당 변수로 제공 & dns 로 서비스 ip 조회 가능
- kube-proxy : 서비스를 제공하는 모든 컨테이너에서 서비스 연결이 로드밸런싱 되도록 함
- 컨테이너는 옮겨져 재시작하면, 새로운 IP를 받지만 , 서비스는 여전히 동일한 IP 주소를 유지한다.
- 컨테이너가 이동하더라도, 항상 연결할 수 있다.
1.3.5 쿠버네티스의 장점
애플리케이션 배포의 단순화
- k8s는 모든 워커 노드를 하나의 배포 플랫폼으로 제공
- app 개발자가 자체적으로 app 배포 가능. 클러스터 구성서버 알 필요가 없다.
- 모든 노드 -> app 이 해당 노드의 사용을 기다리는 컴퓨팅 리소스
- app 을 특정 종류의 하드웨어 에서 실행해야 하는 경우
- 특정 노드를 선택하는 대신, k8s 에 특정 하드웨어 요구 가능
하드웨어 활용도 높이기
- k8s 에 app 실행 지시 -> app 의 리소스 요구 사항에 대한 description 과 각 노드에서 사용가능한 리소스에 따라 app 실행에 가장 적합한 노드 선택 가능
- app 은 클러스터간 자유로운 이동 가능
- 클러스터에서 실행되는 다른 app 구성 요소를 혼합해 클러스터 노드에 배치 가능
- k8s 에 app 구성요소, 배포할 서버 노드의 최적의 조합을 찾아줌
상태 확인 및 자가 치유
- 서버 장애시, 클러스터간에 app 이동 가능한 시스템 필요
- 클러스터 크기가 증가하면, 컴퓨터 구성 요소의 고장을 더 자주 처리하게 된다.
- 노드 장애 발생시, 자동으로 app 을 다른 노드로 스케줄링
- 수동으로 migration 할 필요 없다.
- app 재배치 x -> 즉시 노드 자체를 수정 -> 사용 가능한 하드웨어 리소스 풀에 반환 집중
오토스케일링
- 급격한 부하 급증에 대응하기 위해 개별 app 의 부하 지속적인 모니터링이 필요 없다
- K8s 는 각 app 에서 사용하는 리소스를 모니터링 -> app 에 실행중인 인스턴스 수를 지속적 지시 가능하다.
애플리케이션 개발 단순화 X 추가로 찾아보자.
- 복잡한 인프라 관리 부담 감소
- 쿠버네티스는 컨테이너 오케스트레이션을 자동으로 처리하므로, 개발자가 직접 서버 설정, 네트워크 구성, 부하 분산 등을 고민할 필요가 없어, 애플리케이션 코드 개발에만 집중 가능
- 개발과 프로덕션 환경의 일관성
- 컨테이너로 실행되기에, 환경의 차이 없이 운영 가능
- 서비스 간 통신이 간편
- 쿠버네티스는 Service 라는 오브젝트를 통해 애플리케이션 간 통신을 자동화
- 네트워크 구성을 수동으로 설정할 필요 없이, DNS 이름으로 서비스 호출 가능
- 코드 배포 및 업데이트 단순화