[욕망의 인프라 : 도커 & 쿠버네티스] 도커와 이미지, 레이어

이열음·2022년 12월 9일
0

욕망의 인프라

목록 보기
2/2

본 스터디(이하 욕망의 인프라)는 알렉스(https://github.com/yxxnghwan)와 함께합니다.

도커

저번엔 컨테이너와 쿠버네티스가 뭔지 알아봤다. 그래서 쿠버네티스가 관리하는 도커는 결국 뭐냐고 묻는다면.. 컨테이너 개념을 기반으로 한 가상화 플랫폼이다. 뭔지 모르겠다고? 그냥 하드웨어의 OS 위에서 컨테이너 돌리는 컨테이너 런타임 기술이라고 생각하면 편하다. 뒤에 나올 이미지를 실행시키는 독립적으로 격리된 공간을 컨테이너라고 하고, 이걸 관리하고 실행시키는게 컨테이너 런타임인데 도커가 그중에 하나이다.

이미지?

특정 프로세스(여기서는 컨테이너)를 실행시킬때 필요한 프로그램, 소스코드, 라이브러리, 명령어 싹다 묶은 불변의 파일을 의미한다. 난 스냅샷 개념으로 이해했다. 이렇게 이미지로 뜬 시점의 애플리케이션과 가상환경을 고대로 유지하게 되고 변하지 않기 때문에 개발자들이 동일한 조건에서 개발할 수 있는게 보장된다. 또한 한번 이미지로 떠두면 여러개의 컨테이너를 생성할 수 있다. 복제해서 쓸일이 있다면 좋으려나...🤔

이미지에는 내가 직접 만드는 커스텀 이미지도 있고 docker hub에 올라와있는 이미지들도 있다. 찾아보면 MySQL이나 Ubuntu 뭐 요런거 있더라. 필요한거 받아서 컨테이너 구성 하면 된다.

어떻게 실행되나?

이미지 이름을 test 라고 가정해보면 다음과 같이 동작한다.

  1. 일단 Docker client 에서 docker run test 명령어를 받아 docker server에 전달한다.

  2. Docker Server에서 Image cache 영역에 test 라는 이미지가 있는지 함 확인하고 없으면 hub에서 찾아서 받아온다. 없으면 이런 명령어가 뜨면서 풀을 겁나 땡겨온다.

Unable to find image 'test:latest' locally
  1. 가져온 image를 Image cache 영역에 저장하고 컨테이너를 생성하여 프로그램을 실행한다.

이미지는 어떻게 만드나?

위에서 말했듯 Dockerfile 이라는걸 작성하면 된다.
적당한 명령어들을 입력해주면 되는데 자세한 명령어들이나 옵션은 여기서 보면 된다.
나는 FROM, WORKDIR, RUN, ENTRYPOINT, COPY, ENV, EXPOSE 정도 써봤다.
난 WAS에 올릴 코드를 이미지로 만들었는데, 그때 쓴 스크립트 기반으로 간단히 설명을 하려한다.
전체 스크립트는 다음과 같다.

FROM adoptopenjdk/openjdk11 as build

COPY  . .
RUN chmod +x ./gradlew
RUN ./gradlew clean
RUN ./gradlew bootjar

FROM adoptopenjdk/openjdk11
COPY --from=build build/libs/*.jar app.jar
ENV MYSQL_ROOT_URL jdbc:mysql://db/coffeeshop
ENV MYSQL_USER root
ENV MYSQL_PASSWORD root
EXPOSE 8080/tcp
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar"]

FROM

베이스 이미지를 지정하는 명령어로 Docker hub에 올라와있는 공개이미지를 끌어온다.
베이스 이미지로 기반을 만들어놓고, 그 위에 우리가 이미지로 만들 애들을 쌓는 개념이다.
나는 자바 기반의 어플리케이션을 개발했으니 JDK11을 받아주었다.

COPY

내 로컬 컴퓨터에 있는 디렉토리나 파일을 Docker image의 파일 시스템 내로 복사한다.
절대경로, 상대경로 모두 가능하며 상대경로에서는 WORKDIR로 정해놓은 위치에서 복사를 하게된다.
ADD도 비슷한 명령어긴 한데, 걔는 압축파일같이 일반파일이 아니라 좀 특이한 파일 옮길때 쓰는거고
앵간하면 COPY가 권장된다는 것 같다.

RUN

위에 스크립트에서 유추하다싶이 터미널에서 실행하는 명령어를 입력하면 실행시켜준다.
뭔가를 설치하거나, 실행시킬때 사용한다. 권한 열때도 사용해도 되는지는 모르겠다. 아무튼 명령어를 실행시켜준다.

ENV

docker image의 파일시스템 내에 환경변수를 생성해준다.
빌드때 필요한 변수들을 넣는 선택지가 여러갠데(Dockerfile, docker-compose.yml의 env.. 등등)
여기다 넣고 git에 올릴때는 Docker hub에 이미지만 올리고
Dockerfile은 ignore 시켜서 노출을 줄일 수 있을 것 같아 나는 여기다 넣어봤다.

EXPOSE

들어오는 요청을 받을 포트를 지정해준다. 이거 해준다고 로컬 컴퓨터에서 바로 접근할 수 있는건 아니라서 docker run 할때 -p로 옵션을 주긴해야한다. 그냥 포트를 연다 정도로 이해하면 될 것 같다.
TCP가 디폴트고 UDP는 설정하기 나름이다.

ENTRYPOINT

이미지를 컨테이너로 띄울 때 항상 실행되는 명령어를 적는다. 딱 한번 실행되고, 얼핏보면 CMD랑 비슷한데 차이점이 있다면 이미지를 다 뜨고 이걸 실행할때 사용하는 명령어를 주로 쓴다는 점 정도 있다. 이 명령어로 실행된 프로세스가 죽을때 컨테이너도 같이 죽일수가 있기 때문이다. 그래서 대체로 Dockerfile 마지막줄에 쓴다.

CMD

CMD는 ENTRYPOINT와 비슷하게 딱 한번 실행된다. ENTRYPOINT에 쓰이는 기본 파라미터를 지정할때 써주면 좋다. 뭐 이를테면

ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod"]
CMD ["app.jar"]

이런식으로 지정해주면 docker run 할때 기본은 app.jar,
뒤에 파라미터를 붙여서 run 하면 그 파라미터로 실행되어서 필요에 따라 인자를 바꿔줄 수 있다.

레이어

도커 이미지는 한 컨테이너를 실행하기 위한 모든 정보를 가지고 있어서 용량이 은근 크다. 파일 하나 추가했다고 매번 죄다 다시 다운을 받는다...? 말이 안돼 너무 비효율적이다. 개발자들은 이런걸 두고 볼 위인들이 아니지. 도커에는 레이어라는 개념이 있다. 코드를 레이어로 변환하여 차곡차곡 쌓아놓은 다음, 이걸 이미지를 떠놓고 컨테이너 돌리는 과정에서 무언가 변하면 또 새로운 레이어로 쌓아둔 뒤, 나중에 새로운 레이어만 슉슉 빌드해서 수정사항을 효율적으로 반영한다.

무슨 원리인가?

도커 이미지를 뜨면 명령어 한줄이 레이어 하나로 바뀐다. 이것들을 image layer라고 하는데, Docker conatiner 는 Union file system 이란걸 기반으로 동작해서 이 여러개의 image layer를 하나의 파일시스템처럼 사용할 수 있게 해준다.

이렇게 여러개의 레이어로 구성되어있는 이미지를 실행시키면 도커는 자기가 관리하는 파일시스템 영역에 이미지를 복사한 뒤, 최상단에 Container layer라는 레이어를 하나 추가하여 컨테이너를 생성한다. 사용자가 컨테이너 안에서 하는 모든 동작은 container layer 내에 복사된다.
이렇게 하면 image layer는 불변으로 유지하면서 동시에 컨테이너 내에서의 수정사항도 기록해둘 수 있다.

+) 나아아아중에 혹시라도 container layer 내에 있는 내용을 image layer에 적용하고 싶으면 docker commit을 사용하면 된다.

REF

https://docs.docker.com/get-started/
https://www.44bits.io/ko/post/how-docker-image-work
https://jenakim47.tistory.com/40
https://www.daleseo.com/dockerfile/

0개의 댓글