k8s

쿠버네티스 완벽 가이드 1장 : Docker

Joon0464 2023. 1. 19. 14:59

Docker란?


  • 컨톄이너를 실행하기 위한 실행 환경(컨테이너 런타임) 및 툴킷이다.
  • 쿠버네티스의 경우 도커 이외의 컨테이너 런타임도 지원

Docker Container란?


  • 도커 이미지를 기반으로 실행되는 프로세스

장점 1. 이식성이 높다.

  • 도커 이미지 -> 환경의 영향을 받지 않고 다양한 환경에서 컨테이너를 기동 가능
  • BORA(Build Once Run Anywhere) 콘셉트

장점 2. 가상머신에 비해 가볍다.

  • '가상머신에 비해 가볍다.' , '시작과 중지가 빠르다.' 등의 장점이 있다.
  • 가상머신 -> 하이퍼 바이저를 이용하여 게스트 OS를 동작시킴
  • 도커 컨테이너 -> 호스트 머신의 커널을 이용하면서 네임스페이스 분리와 cgroups를 이요한 제어를 통해 독립적은 OS와 같은 환경을 만듦
  • 따라서 게스트 OS 기동을 기다릴 필요 없이 프로세스를 빠르게 시작 및 중지 가능

Docker Container 설계


도커 컨테이너 설계 주의사항

  1. 하나의 컨테이너당 하나의 프로세스 -> 가장 중요
    - 이를 무시할 경우 관리에 어려움이 발생할 수 있음
  2. 변경 불가능한 인프라(Immutable Infrastructure) 이미지로 생성
    - 이미지 안에 실행 애플리케이션, 바이너리 및 관련 리소스를 가능한 포함시켜야 함g
    - 외부 다운로드, 패키지 설치 권장 x
  3. 최대한 경량 도커 이미지로 생성
    - 이미지 외부 레포지토리 다운로드를 해야하는 경우가 발생할 수 있기 때문
    - yum, apt 패키지 설치 후 저장소 패키지 목록 등의 캐시 파일을 삭제하는 방법, 경량 베이스 이미지 사용 등의 방법이 있다.
  4. 실행 계정은 root 이외의 사용자로 설정
    - 보안 취약점 발생할 수 있음

Dokcerfile 작성법


다음은 HTTP 요청이 오면 'Hello Kubernetes' 라고 응답하는 Go 언어 애플리케이션(main.go)을 실행하는 컨테이너 이미지 생성 예시 Dockerfile이다.

# Alpine 3.7 버전 golang 1.10.1 이미지를 사용
FROM golang:1.10.1-alpine3.7

# 8080 포트 오픈
EXPOSE 8080

# 빌드할 머신에 있는 main.go 파일을 컨테이너에 복사
COPY ./main.go ./

# 컨테이너 내부에서 명령어 실행
RUN go build -o ./go-app ./main.go

# 실행 계정을 nobody로 변경
USER nobody

# 컨테이너가 기동할 때 실행할 명령어 정의
ENTRYPOINT ["./go-app"]
  • FROM : 컨테이너 이미지에서 기반이 되는 이미지를 지정(Base Image)
  • RUN, COPY 명령을 사용하여 패키지 설치 및 파일 복사 등을 처리
  • ENTRYPOINT , CMD 는 컨테이너가 기동할 때 실행하는 명령어를 지정
    • e.g ENTRYPOINT /bin/sleep , CMD 3600 으로 설정하면 "/bin/slepp 3600" 명령어가 컨테이너 기동 시 수행된다.
  • ENTRYPOINT 는 기본적으로 변경할 필요 없는 부분을 정의하고, CMD는 인수를 지정하여 나중에 변경하여 실행할 수 있다.

Dockerfile에서 사용 가능한 명령 리스트

명령 요약
FROM Base Image 지정
MAINTAINER 컨테이너 이미지 관리자 정보 기입 (현재는 비추천, 아래의 LABEL을 주로 사용)
LABEL 컨테이너 이미지 메타데이터를 key:value 형식으로 기입
(e.g. LABEL maintainer="Song Hyunjoon<thdguswns31@gmail.com>")
USER 명령어 실행 계정 지정
WORKDIR 명령어를 실행할 작업 디렉터리 설정, 디렉터리가 없을 경우 생성
EXPOSE 컨테이너 실행 시 Listen할 포트 설정
COPY 로컬에 있는 파일을 컨테이너로 복사
ADD 로컬에 있는 tar.gz 파일을 압축 해제하여 컨테이너로 복사
RUN 컨테이너 안에서 명령어를 실행
ENTRYPOINT 컨테이너 기동 시에 실행할 명령어
CMD 컨테이너 기동 시에 실행할 명령 인수

 

대표적인 Base Image 리스트

이미지명 이미지 사이즈
scratch 최소
alpine 작다
distroless 작다
ubuntu 크다
centos 크다
Universal Base Image 크다
  • scratch는 쉘을 포함하여 이미지 내에 아무것도 설치되어 있지 않다.
  • alpine 리눅스는 musl libc와 busybox 기반으로 한 경량 리눅스이다.
  • distroless 는 python, node.js 등 특정 애플리케이션 런타임만 포함된 이미지이다.
  • Universal Base Image는 RedHat의 기술지원을 필요로할 때 사용

Docker Image Build


위의 예시에 작성한 Dockerfile을 로컬 PC에서 빌드해보았다.

docker image build -t sample-image:0.1 .

  • -t 옵션으로 이미지 이름 및 태그를 지정할 수 있다. (이미지이름:태그)
  • 명령어 마지막에 오는 "." 은 반드시 존재해야하며 현재 경로의 Dockerfile을 자동으로 빌드하게된다.

이미지 빌드 후 생성된 이미지를 확인해보면 다음과 같다.

docker image ls

 

멀티 스테이지 빌드


방금 생성한 도커 이미지는 golang:1.14.1-alpine3.11 을 Base Image로 사용하였다. golang:1.14.1-alpine3.11 또한 alpine(5.6MB)를 Base Image로 하여 만들어진 본래 아주 작은 이미지이지만 golang 이미지에는 GO 컴파일 도구 등 약 346MB가 추가로 포함되어 있다. 즉 위에서 생성한 sample-image:0.1 에도 동일하게 해당 컴파일 도구가 포함되어 이미지 사이즈가 매우 커진다.

이를 해결하기 위한 방법이 멀티 스테이지 빌드이다.

  • 여러 컨테이너 이미지를 사용하여 처리 후 결과물만 실행용 컨테이너로 전달
  • sample-image:0.1 에서 Golang 이미지는 애플리케이션 컴파일에 필요한 도구로만 사용되며 결과물을 실행할 때는 필요하지 않다.
# Stage 1 컨테이너(애플리케이션 빌드)
FROM golang:1.10.1-alpine3.7 as builder
COPY ./main.go ./
RUN go build -o /go-app ./main.go

# Stage 2 컨테이너(빌드된 바이너리를 포함한 실행용 컨테이너 생성)
FROM alpine:3.7
EXPOSE 8080
# Stage 1에서 빌드된 결과물을 복사
COPY --from=builder /go-app .
USER nobody
ENTRYPOINT ["./go-app"]

 

햬당 과정을 그림으로 표현하면 다음과 같다

실제로 그럼 도커 이미지 빌드 테스트를 통해 결과를 비교해보면 다음과 같은 결과를 얻는다.

$ docker image build -t image-smaple:0.2 -f Dockerfile-MultiStage .
$ docker image ls

위와 같이 빌드된 이미지의 용량을 대폭 줄일 수 있게 된다.

 

도커 이미지 업로드


도커 이미지는 도커 레지스트리라는 원격 저장소에 업로드 가능하다. 대표적으로 Docker Hub, Google Container Registry(GCR), Amazon Elastic Container Registry(ECR) 등이 있다.

도커 이미지를 실제로 Docker Hub 에 업로드 해보면 다음과 같이 진행된다.

# 도커 허브 계정 생성 후 로그인
$ docker login
Username , Password 입력

# 이미지 태그명 변경
$ docker image tag sample-image:0.1 thdguswns31/sample-image:0.1
thdguswns31 -> 도커 허브 유저이름(DOCKERHUB_USER)이다.

# 도커 허브에 이미지 업로드
$ docker image push thdguswns31/sample-image:0.1

실제 터미널에서 업로드하는 과정이다.
도커 이미지가 업로드된 것을 볼 수 있다.

컨테이너 기동


도커 컨테이너를 기동시키려면 아래와 같이 진행하면 된다. 다만 쿠버네티스를 사용하는 경우 아래와 같이 직접 컨테이너를 기동시킬 일은 없을 것이다.

# 도커 컨테이너 기동(localhost:12345 로 접속하면 8080/TCP 포트로 포트포워딩 되어 컨테이너 애플리케이션에 접근)
$ docker run -d -p 12345:8080 sample-image:0.1

# 배포된 컨테이너 조회
$ docker ps -a

# 배포한 애플리케이션 동작 확인
$ curl http://localhost:12345

# 도커 컨테이너 제거
$ docker rm -f {Container ID}

docker ps -a 명령어를 통해 컨테이너 ID를 조회하여 해당 ID를 rm 명령어로 제거하는 과정

 

여기까지 쿠버네티스를 공부하기 전 기본적으로 알아야하는 도커 컨테이너에대해 정리해보았다.

이어서 다음 포스트부터는 쿠버네티스에대해 본격적으로 정리할 예정이다.