# 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 이미지의 파일 시스템을 여러 읽기 전용 계층으로 나누어 효율적으로 관리하는 방법입니다. 계층의 재사용과 캐싱 기능 덕분에 이미지 빌드와 배포가 더 빠르고 효율적입니다. 읽기 전용 계층과 읽기-쓰기 계층을 통해 컨테이너의 데이터 일관성과 무결성을 유지합니다. ``` ## 도커 파일 ### 관련 예시 ```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"] ``` ## 도커 파일 작성시 유의해야 할 점 ### 이미지 크기 - 자주 변경되는 것이 가장 최신 층에 놓여 있어야 한다. - 이전 층에 놓여있게 되면 최신 층과 고치려는 층 까지 영향을 받기 때문 ### 이미지 보안 - 컨테이너 계층의 경우 한 계층에서 파일을 삭제해도 이전 계층에 파일이 남아 있기 때문에 비밀번호를 포함된 채로 컨테이너를 빌드하면 보안상 문제가 생김 - 비밀로 유지해야 하는 정보와 이미지가 혼재해서는 안된다.(위와 동일한 의미라고 생각) - 애플리케이션 실행에 필요한 것 이외의 모든 파일은 지우는 것이 좋다. - 이미지에 추가된 모든 라이브러리는 애플리케이션에 나타날 수 있는 라이브러리 자체의 잠재적인 취약점을 드러내기 때문 ### 다단계 이미지 빌드 - 불필요한 개발 도구를 실제 배포용 이미지와 분리함으로써 다음과 같은 효과를 얻는다 - 용량이 줄어들어 컨테이너 배포 속도가 빨라진다. - 필요없는 파일이 최종 이미지에서 제거된다 - 관련 코드 예시 ```docker 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"] ``` - 빌드와 실행 환경을 분리함으로써 이미지의 용량을 줄임