1장 도커 복습과 Hello, Kubernetes
1.1 도커복습
알아야 할 도커지식
- 컨테이너 설계
- 파일 작성법
- 이미지 빌드
- 레지스트리로 이미지 푸시
1.1.1 도커 컨테이너란?
- 컨테이너는 도커 이미지를 기반으로 실행되는 프로세스
- 환경의 영향을 받지 않아 이식성이 높다
1.1.2 도커 컨테이너 설계
- 1 컨테이너당 1 프로세스
- 변경 불가능한 인프라(Immutable Infrastructure) 이미지로 생성한다.
- 경량의 도커 이미지로 생성한다.
- 실행 계정은 root 이외의 사용자로 한다.
1 컨테이너당 1 프로세스
- 관례임으로 안지키면 도커의 에코시스템과 맞지않아 관리가 힘들어짐.
변경 불가능한 인프라(Immutable Infrastructure) 이미지로 생성
- 컨테이너 기동 후에 외부 실행 바이너리, 또는 외부 패키지를 설치하면 컨테이너 실행 결과가 달라질 수 있음.
- 관련 리소스를 가능한 이미지에 포함시켜 최대한 일관된 결과를 보장
경량의 도커 이미지로 생성한다.
- 도커 이미지가 무거우면 (다운로드를 통해 이미지를 가져오기 때문에) 배포가 느려짐.
- 빌드 단계에서만 사용되는 패키지를 제거, 경량 배포판(알파인, scratch) 이미지를 사용, 도커 빌드 레이어 수를 줄이는 등등의 방법 있음.
실행 계정은 root 이외의 사용자로 한다.
- 보안사고 예방
1.1.3 도커 파일 작성법
FROM
에서는 기반이 되는 도커 이미지를 지정COPY
명령으로 빌드할 머신에 있는 파일을 컨테이너에 복사RUN
명령은 빌드 시에 컨테이너에서 명령어를 실행ENTRYPOINT
명령과CMD
명령은 컨테이너가 기동할 때 실행하는 명령어ENTRYPOINT
에 기본적으로 바꿀 필요가 없는 부분을 정의CMD
에 기본값 인수 등을 정의
1.1.4 도커 이미지 빌드
- docker image build 명령어를 사용
-t
옵션으로 이미지의 이름과 태그를 지정- 태그는 버전을 지정하는 것이 일반적
멀티 스테이지 빌드
- 여러 컨테이너 이미지를 사용해 빌드하고 결과물만 실행용 컨테이너 이미지에 복사
- 이미지가 경량화되고 보안측면도 더 나음.
- 빌드킷(https://github.com/moby/buildkit) 등으로 빌드 의존 관계를 자동 파악해 병렬 처리하는 것도 가능
1.1.5 이미지 레이어 통합과 이미지 축소화
- 다이브(https://github.com/wagoodman/dive)로 레이어의 용량 소비를 조사 가능함.
- docker image build 시
--squash
옵션을 사용하면 한 개의 레이어로 통합 (아직 실험적 experimental 기능)
1.1.6 도커 레지스트리로 이미지 업로드
- 레지스트리는 도커 이미지를 보관하는 저장소 서버
- 되도록 비공식 이미지 보단 공식 이미지를 사용할 것을 권장
1.1.7 컨테이너 기동
- 쿠버네티스를 사용하면 직접 도커 명령어를 사용하지 않음
2장 왜 쿠버네티스가 필요할까?
2.1 쿠버네티스란?
- 애플리케이션의 배포, 확장 등을 관리하는 것을 자동화하기 위한 플랫폼
- 도커(단독으로)는 여러 도커 호스트를 동시에 동작, 통합 관리하는 기능을 제공하지 않음
2.2 쿠버네티스의 역사
- 구글이 내부적으로 사용하던 컨테이너 클러스터 관리 도구 Borg에서 파생된 오픈 소스
- 리눅스 재단 산하의 클라우드 네이티브 컴퓨팅 파운데이션(CNCF)으로 이관
- 사실상 표준이 된 배경에는 대형 클라우드 사업자(구글, 애저, 아마존등)가 쿠버네티스 관리형 서비스를 제공한다는 점 때문
2.3 쿠버네티스를 사용하면 무엇을 할 수 있을까?
- 여러 쿠버네티스 노드 관리
- 컨테이너 스케줄링
- 롤링 업데이트
- 스케일링/오토 스케일링
- 컨테이너 모니터링
- 자동화된 복구
- 서비스 디스커버리
- 로드 밸런싱
- 데이터 관리
- 워크로드 관리
- 로그 관리
- 선언적 코드를 사용한 관리
- 그 외 에코시스템과의 연계 및 확장
2.3.1 선언적 코드를 사용한 관리(IaC)
YAML, JSON 형식으로 작성한 선언적 코드(매니페스트)로 리소스를 관리
2.3.2 스케일링/오토 스케일링
같은 이미지 기반의 컨테이너 수를 자동으로 늘리거나 줄여 부하를 분산함
2.3.3 스케줄링
어피니티, 안티어피니티(https://kubernetes.io/ko/docs/concepts/scheduling-eviction/assign-pod-node/#%EC%96%B4%ED%94%BC%EB%8B%88%ED%8B%B0-affinity-%EC%99%80-%EC%95%88%ED%8B%B0-%EC%96%B4%ED%94%BC%EB%8B%88%ED%8B%B0-anti-affinity)을 사용해 워크로드 특징이나 노드 성능을 기준으로 컨테이너를 어느 노드에 배치할 것인지 제어 가능.
2.3.4 리소스 관리
- (따로 스케줄링 설정이 없으면) 노드의 CPU나 메모리의 여유 리소스 상태에 따라 자동 스케줄링
- 리소스 상태에 따라 자동적으로 클러스터에 노드 추가나 삭제도 가능
2.3.5 자동화된 복구
- 컨테이너 프로세스 정지를 감지하면 자동 재배포
- 노드가 제거되도 컨테이너를 다른 노드로 자동 복구함
2.3.6 로드 밸런싱과 서비스 디스커버리
- 로드 밸런서 기능(서비스, 인그레스)을 제공
- 컨테이너의 확장, 업데이트, 또는 장애가 발생했을 때 필요한 엔드 포인트 서비스 변경 작업(컨테이너 등록, 삭제, 분리등)을 자동 수행
- 서비스 디스커버리 기능으로 복수의 서비스간 연계가 쉬움. (마이크로 서비스 구축 용이)
2.3.7 데이터 관리
- 내부 백엔드 데이터 스토어로 etcd 사용
- 안전하고 이중화된 상태로 중앙 집중 관리
2.4 정리
쿠버네티스를 사용하면 컨테이너의 이동성과 경량화를 활용한 빠른 개발과 전체 시스템의 배포 자동화를 구현할 수 있다.
3장 쿠버네티스 환경 선택
생략합니다. 나중에 여유 있으면 하겠음
4장 API 리소스와 kubectl
4.1 이 책을 읽기 위한 준비
- CLI 도구인 kubectl을 설치
- 쿠버네티스 클러스터 생성
4.2 쿠버네티스 기초
- 쿠버네티스는 k8s
마스터
와 k8s노드
로 구성 - k8s
마스터
는 API 엔드포인트 제공, 컨테이너 스케줄링, 스케일링 등을 담당 - k8s
노드
는 컨테이너를 기동시키는 노드 - kubectl이 매니페스트 파일(YAML,JSON)을 바탕으로 쿠버네티스 마스터에 리소스를 등록
4.3 쿠버네티스와 리소스
4.3.1 워크로드 API 카테고리
컨테이너를 기동하기 위해 사용되는 리소스. 내부적으로 사용하는 것외 사용자가 직접 관리할 수 있는 것은 아래 8가지.
- 파드(Pod)
- 레플리케이션 컨트롤러(ReplicationController)
- 레플리카셋(ReplicaSet)
- 디플로이먼트(Deployment)
- 데몬셋(DaemonSet)
- 스테이트풀셋(StatefulSet)
- 잡(Job)
- 크론잡(CronJob)
4.3.2 서비스 API 카테고리
서비스 디스커버리와 클러스터 외부 접속이 가능한 엔드포인트 제공하는 리소스.
- 서비스
- ClusterIP
- ExternalIP(ClusterIP의 한 종류)
- NodePort
- LoadBalancer
- Headless(None)
- ExternalName
- None-Selector
- 인그레스
4.3.3 컨피그 & 스토리지 API 카테고리
설정과 기밀 데이터를 컨테이너에 담거나 영구 볼륨을 제공하는 리소스.
- 시크릿(Secret)
- 컨피그맵(ConfigMap)
- 영구 볼륨 클레임(PersistentVolumeClaim)
4.3.4 클러스터 API 카테고리
클러스터 동작(보안 관련 설정이나 정책, 클러스터 관리성을 향상시키는 기능)을 정의하는 리소스.
- 노드(Node)
- 네임스페이스(Namespace)
- 영구 볼륨(PersistentVolume)
- 리소스 쿼터(ResourceQuota)
- 서비스 어카운트(ServiceAccount)
- 롤(Role)
- 클러스터 롤(ClusterRole)
- 롤바인딩(RoleBinding)
- 클러스터롤바인딩(ClusterRoleBinding)
- 네트워크 정책(NetworkPolicy)
4.3.5 메타데이터 API 카테고리
내부의 다른 리소스 동작을 제어하기 위한 리소스.
- LimitRange
- HorizontalPodAutoscaler(HPA)
- PodDisruptionBudget(PDB)
- 커스텀 리소스 데피니션(CustomResourceDefinition)
4.4 네임스페이스로 가상적인 클러스터 분리
- 네임스페이스는 가상적인 쿠버네티스 클러스터 분리 기능
- 하나의 클러스터를 여러 팀에서 사용하거나 서비스/스테이징/개발 환경으로 구분하는 경우 사용
- 클러스터를 (여러 팀에서) 공용으로 사용하지 않고 시스템이 단순하면 기본 네임스페이스만 사용하는 것도 괜찮음.
저자 추천
아래 3가지 이유로 저자는 서비스/스테이징/개발 환경으로 나누는 용도 보단 마이크로서비스를 개발하는 팀 단위로 나눌 것을 추천.
- 클러스터 장애시 모든 환경에서 장애가 나는 것을 격리, 차단할 수 있음.
- 매니페스트 파일 재사용성이 떨어짐. (같은 리소스임에도 prod 매니페스트랑 dev 매니페스트로 나뉨)
- 위와 같은 이유로 같은 리소스의 서비스 주소가 달라짐. (ex, SERVICE.prod.svc.cluster.local과 SERVICE.dev.svc.cluster.local)
4.5 커맨드 라인 인터페이스(CLI) 도구 kubectl
CLI 도구인 kubectl
을 사용해 마스터의 API 요청을 보내 클러스터를 조작
4.5.1 인증 정보와 컨텍스트(config)
- 쿠버네티스 마스터와 통신할 때 필요한 접속 대상의 서버, 인증 정보.
clusters
,users
,contexts
3가지 정보로 구성clusters
는 접속 대상 클러스터 정보users
는 사용자 인증 정보contexts
는 사용할 cluster와 user, 네임스페이스 조합.- 조합된 컨텍스트를 전환해, 여러 환경을 여러 권한으로 조작 가능
4.5.2 kubectx/kubens를 사용한 전환
kubectx/kubens(https://github.com/ahmetb/kubectx) 사용하면 컨텍스트, 네임스페이스 전환이 kubectl 명령어를 사용하는 것 보다 편리함.
4.5.3 매니페스트와 리소스 생성/삭제/갱신
- 리소스를 생성할 때는 kubectl
create
명령어 - 생성한 파드는 kubectl
get pods
명령어로 확인 가능 - 삭제할 때는 kubectl
delete
명령어 --wait
옵션은 리소스 처리가 끝나고 명령어가 종료됨. (기본은 명령어가 비동기로 실행, 기다리지 않는다)- 즉시 강제 삭제는
--force
옵션 - 리소스 업데이트는 kubectl
apply
명령어 - 기존 리소스가 없을 때 kubectl
apply
는 kubectlcreate
와 동일 - kubectl
create
따로 사용하는 것 보단 kubectlapply
로 통일하는 것이 편리 - (특정 리소스 경우) 최초 생성 후에 kubectl apply로 변경할 수 없는 필드도 존재함.
4.5.4 리소스 생성에도 kubectl apply를 사용해야 하는 이유
- kubectl apply에 create 기능이 포함되어 있어서 애초 구분이 불필요.
- k8s는 필드삭제를 이전 매니페스트와 적용될 다음 매니페스트를 비교해 정하는데 create는 이전 매니페스트 정보를 저장하지 않아서 필드 삭제가 누락될 수 있음.
4.5.5 Server-side apply
- 클라이언트(kubectl)에서 변경사항을 계산해 요청을 보내기 때문에 (Client-side apply) 여러 사용자가 동시에 같은 필드를 변경하는 경우 경합 현상이 발생.
- kubectl 이름을 기록하고 서버 측에서 변경 사항을 계산, 충돌 감지하는 기능 (Server-side apply) 도입
- 충돌 감지만 제공할 뿐, 해결은 사용자가 직접 해야함.
--server-side
옵션을 사용- 동일한 kubectl일지라도 강제로
--field-manager
옵션을 통해 kubectl 이름을 바꿔 충돌감지가 가능함.
개인생각 노트: 충돌 해결을 직접해야 하기 때문에 여러 사용자가 동일한 리소스를 관리해야 한다면 매니페스트에 대한 버전 관리가 필요해보임 (argocd?)
4.5.6 파드 재기동
rollout restart
로 디플로이먼트 등의 리소스와 연결되어 있는 모든 파드를 재기동.- 단독 파드에서는 사용할 수 없음.
개인생각 노트: 롤 아웃이 레플리카셋과 같은 여러 팟 그룹을 swap하면서 이뤄지기 때문에 단독 팟보다 더 높은 추상화 리소스가 요구되는 것 아닌가하는?
4.5.7 generateName으로 임의의 이름을 가진 리소스 생성
- kubectl create로 리소스 생성시,
generateName
값에 난수를 붙여 팟 이름을 자동생성 - kubectl apply에서는 사용할 수 없음.
개인생각 노트: apply에서 사용할 수 없는 이윤 아마 본래 기존 리소스를 수정하는 기능이니깐 매번 새로 난수 이름 생성하는 내용을 허용하게 되면 본래 기능이 무의미해지기 때문인듯?
4.5.8 리소스 상태 체크와 대기(wait)
- kubectl
wait
명령어는 리소스가 지정한 상태가 될 때까지 대기함. --for
옵션 뒤에 상태 지정--timeout
옵션으로 최대 대기 시간 지정 (기본은 30초)- 리소스 대신 매니페스트 파일이 대상이 될 때, (매니페스트의) 모든 리소스가 같은 상태가 될 때 까지 대기함.
4.5.9 매니페스트 파일 설계
하나의 매니페스트 파일에 여러 리소스를 정의
- 파드를 기동하는 워크로드 리소스와 외부에 공개하는 서비스 리소스를 묶어 하나의 매니페스트 파일로 정의하는 것이 일반적
- 실행 순서를 정확하게 지켜야 하거나 리소스 간의 결합도를 높일 때 하나로 정의
- 공통으로 사용되는 리소스(컨피그맵, 시크릿)는 별도 매니페스트로 분리
- 위에서부터 정의된 리소스 순서대로 적용됨.
- 중간에 문법 에러등으로 중단 시, 이후 리소스는 적용되지 않음.
여러 매니페스트 파일을 동시에 적용
- kubectl
apply
실행 시, 디렉토리 지정 가능 - 파일명 순으로 적용 (순서를 지켜야하면 파일명 앞에 실행순서 넘버링)
-R
옵션은 하위 디렉토리의 매니페스트 파일까지 재귀적으로 적용됨.- 문법 에러등으로 중단 시, 중단된 파일의 이후 리소스는 마찬가지 적용되지 않으나 다른 매니페스트는 적용함.
매니페스트 파일 설계 방침
- 많이 쓰이는 3가지 패턴
- 규모가 크지 않을 경우 디렉토리 하나로 관리하는 패턴
- 서브시스템, 부서별, 내부정책에 따른 그루핑등으로 나눠 관리하는 패턴
- 마이크로서비스 별로 나눠 관리하는 패턴 (서비스간 적용순서를 제어하기 어려움)
- 개발 팀의 구성(조직별로 변경 가능한 범위등)을 고려하는 것도 중요
- 개발 조직도와 매니페스트 관리구조가 서로 유사하게 구성됨.