2장. 컨테이너 생성과 실행

챕터 소개

쿠버네티스, 어플리케이션, 프로그램

  • 쿠버네티스 > 어플리케이션 > 프로그램

    • 쿠버네티스: 분산 어플리케이션을 생성, 배포, 관리하기 위한 플랫폼

    • 어플리케이션: 개별 머신에서 실행되는 하나 이상의 프로그램

    • 프로그램: 입력을 받아들이고 데이터를 조작한 다음 결과를 반환하는 객체

  • 분산 시스템 구축에 앞서 고려할 점

    • 프로그램을 포함하는 애플리케이션 컨테이너 이미지의 빌드

    • 분산 시스템 구성 방법

어플리케이션 프로그램

  • element:

    • 프로그래밍 언어 런타임

    • 라이브러리

      • 해당 라이브러리에 대한 의존성을 가짐

      • 일반적으로 특정 머신에 설치한 os의 공유 컴포넌트 형태로 제공

    • 소스코드

라이브러리 관련 의존성 문제 상황:

  • 로컬 환경에서 개발된 애플리케이션이 운영 시스템 os로 이관되었을 때 동작하지 않는 라이브러리를 포함하고 있을 경우

  • 운영 시스템에 배포하는 패키지 내에서 의존 관계에 있는 파일이 빠졌을 때

단일 머신에서 여러 프로그램을 실행하기 위한 전통적 방법

  • 모든 프로그램이 동일한 버전의 공유 라이브러리를 공유하는 것

    • → 불필요한 결합 + 복잡성의 증대

    • 비잔틴 장애 케이스가 필연적으로 포함

    • 컨테이너 이미지의 불변성을 통해 해결 가능

컨테이너 이미지

오버레이 파일 시스템

  • 이전 계층으로부터 파일을 추가/제거/수정하는 것

컨테이너 계층화

with chatgpt

Docker의 컨테이너 계층화(Container Layering) Docker 이미지가 어떻게 구성되고 관리되는지를 이해하는  중요한 개념입니다. Docker 이미지는 여러 개의 계층(layer)으로 구성되어 있으며, 이러한 계층화는 이미지의 생성, 수정, 재사용을 효율적으로 처리하는  도움을 줍니다.

컨테이너 계층화의 기본 개념
이미지와 계층:

이미지는 여러 개의 읽기 전용 계층으로 구성됩니다.  계층은 특정 작업(: 파일 복사, 명령어 실행 ) 수행한 결과를 포함합니다.
계층은 Dockerfile의  명령어(RUN, COPY, ADD ) 의해 생성됩니다. 예를 들어, RUN apt-get update 명령어는 새로운 계층을 생성합니다.
파일 시스템의 일관성:

Docker는  계층이 읽기 전용이기 때문에, 새로운 계층이 생성될 때마다 파일 시스템의 일관성이 유지됩니다.
새로운 파일이나 변경 사항은 새로운 계층에 추가되며, 기존 계층은 변경되지 않습니다.
계층의 재사용:

Docker는 계층을 효율적으로 재사용하여 저장 공간과 네트워크 대역폭을 절약합니다. 동일한 계층이 여러 이미지에서 사용될  있습니다.
예를 들어, node:14 이미지를 사용하는 여러 Dockerfile이 있을 경우, 공통된 계층(: node:14 기본 이미지 계층) 재사용됩니다.
계층화의 동작 방식
 명령어는 새로운 계층을 생성:

Dockerfile의  명령어는 새로운 계층을 생성합니다. 예를 들어:

dockerfile
코드 복사
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install -y nginx
COPY . /app
 Dockerfile에서 FROM, RUN, COPY 명령어 각각은 새로운 계층을 생성합니다.

계층의 병합:

Docker는 여러 계층을 병합하여 하나의 최종 이미지를 만듭니다. 최종 이미지에는  계층의 변경 사항이 반영됩니다.
컨테이너가 실행될 , 읽기 전용 이미지 계층 위에 읽기-쓰기 계층이 추가됩니다.  읽기-쓰기 계층에서 컨테이너의 변경 사항이 저장됩니다.
캐싱과 효율성:

Docker는 빌드 과정에서 생성된 계층을 캐싱하여, 동일한 계층이 반복적으로 생성되지 않도록 합니다. 이로 인해 빌드 속도가 빨라지고, 네트워크와 저장 공간이 절약됩니다.
예를 들어, RUN npm install 명령어가 실행되면,  결과가 새로운 계층에 저장됩니다. 이후 같은 명령어가 변경되지 않는다면, Docker는 기존 계층을 재사용합니다.
예시
아래는 Dockerfile의 계층화를 설명하는 간단한 예시입니다:

dockerfile
코드 복사
FROM ubuntu:20.04          # 계층 1: 기본 이미지
RUN apt-get update         # 계층 2: 패키지 리스트 업데이트
RUN apt-get install -y nginx # 계층 3: Nginx 설치
COPY . /app                # 계층 4: 애플리케이션 코드 복사
계층 1: ubuntu:20.04 이미지를 기반으로 하는 계층입니다.
계층 2: apt-get update 명령어에 의해 생성된 계층입니다.
계층 3: apt-get install -y nginx 명령어에 의해 생성된 계층입니다.
계층 4: COPY . /app 명령어에 의해 생성된 계층입니다.
요약
계층화는 Docker 이미지의 파일 시스템을 여러 읽기 전용 계층으로 나누어 효율적으로 관리하는 방법입니다.
계층의 재사용과 캐싱 기능 덕분에 이미지 빌드와 배포가  빠르고 효율적입니다.
읽기 전용 계층과 읽기-쓰기 계층을 통해 컨테이너의 데이터 일관성과 무결성을 유지합니다.

도커 파일

관련 예시

# node 16 image에서 시작
from node:16

# work dir 지정
workdir /usr/src/app

# 관련 명령어 지정
copy pakage*.json ./
run npm install
run npm install express

# 모든 앱 파일을 img에 복사
copy . .

# 컨테이너 시작시 실행할 명령어
cdm ["npm", "start"]

도커 파일 작성시 유의해야 할 점

이미지 크기

  • 자주 변경되는 것이 가장 최신 층에 놓여 있어야 한다.

    • 이전 층에 놓여있게 되면 최신 층과 고치려는 층 까지 영향을 받기 때문

이미지 보안

  • 컨테이너 계층의 경우 한 계층에서 파일을 삭제해도 이전 계층에 파일이 남아 있기 때문에 비밀번호를 포함된 채로 컨테이너를 빌드하면 보안상 문제가 생김

  • 비밀로 유지해야 하는 정보와 이미지가 혼재해서는 안된다.(위와 동일한 의미라고 생각)

  • 애플리케이션 실행에 필요한 것 이외의 모든 파일은 지우는 것이 좋다.

    • 이미지에 추가된 모든 라이브러리는 애플리케이션에 나타날 수 있는 라이브러리 자체의 잠재적인 취약점을 드러내기 때문

다단계 이미지 빌드

  • 불필요한 개발 도구를 실제 배포용 이미지와 분리함으로써 다음과 같은 효과를 얻는다

    • 용량이 줄어들어 컨테이너 배포 속도가 빨라진다.

    • 필요없는 파일이 최종 이미지에서 제거된다

  • 관련 코드 예시

    from golang:1.17-alpine as build
    
    ## 관련 명령어
    run apk update && apk upgrade && apk add --no-cache git nodejs bash npm
    
    run go get -u ...
    run go get ..
    
    ## workdir 설정
    workdir /
    
    ## 이미지로 복사할 폴더 선택
    copy . .
    
    ## env 설정
    env verbose=0
    
    ## 빌드
    run build build.sh
    
    ## 위에서 만든 중간 산출물을 활용하여 빌드
    from alpine
    user nobody:nobody
    ## 이전 stage(build)의 /go/bin/kuard를 현재 이미지의 /kuard로 이동
    copy --from=build /go/bin/kuard /kuard
    cmd ["/kuard"]
    
    • 빌드와 실행 환경을 분리함으로써 이미지의 용량을 줄임