이번 장에서 하게 될 것
- 이미지를 어떻게 만드는지 알기
- Dockerfile 구문 사용해보기
- 애플리케이션을 컨테이너화 할 때 사용하게 되는 핵심 패턴 중 일부
1. 도커 허브에서 이미지 사용하기
지난 글에서
"docker container run" 커맨드를 통해
로컬에 캐시되어 저장되지 않더라도
어딘가에서 다운로드 받아온다는 것을 확인했다.
이 동작이 가능한 이유는
도커 내부적으로 소프트웨어 배포 기능이 내장되어 있기 때문이다.
컨테이너 실행을 통해 이 동작을 하도록 도커에게 맡길 수도 있지만,
도커 CLI를 사용해 이미지를 다운로드 할 수 있다.
예제
- 커맨드
docker image pull diamol/ch03-web-ping
- 실행 결과
다운로드 받은 이미지의 이름은 "diamol/ch03-web-ping"이다.
도커는 이미지를 다운로드 받기 위해 기본 설정 값인 도커 허브를 찾는다.
이미지를 저장하여 다운로드 받을 수 있게 만들어 둔 이미지 서버들을 레지스트리라고 부른다.
도커 허브는 이 중 하나로 무료로 사용할 수 있는 공용 레지스트리이다.
출력 결과를 보면
"diamol/ch03-web-ping" 이미지를 Pulling 하는데,
Pull complete가 여러 라인으로 출력된 것을 확인할 수 있다.
도커 이미지는 논리적으로는 하나이지만,
여러 개의 레이어로 나뉘어 있어 각각 따로 다운로드 받는다는 것을 알 수 있고,
이 레이어들이 모두 다운로드 되어야 해당 이미지를 사용할 수 있다.
위 이미지는 Node.js로 만들어진 애플리케이션으로
특정 서버로 요청을 보내고 그 응답이 얼마나 오래 걸렸는지를 로깅한다.
예제
- 커맨드
docker container run -d --name web-ping diamol/ch03-web-ping
- 실행 결과
- --name [지정할 별칭]
- 해당 컨테이너를 특정할 수 있는 별칭을 지정하기 위한 플래그
해당 앱은 로깅만 실행하기 때문에 별도의 포트로 게시할 필요는 없다.
여기서 "web-ping"이라는 별칭을 지정하여
도커가 무작위로 지정한 해시 ID 값이 아닌 "web-ping"이라는 별칭으로
해당 이미지를 지정할 수 있다.
지정한 별칭으로 해당 애플리케이션이 로깅 중인 내용을 살펴보자.
예제
- 커맨드
docker container logs web-ping
- 실행 결과
백그라운드에서 실행 중인 "web-ping"이
특정 서버로 요청을 보내 그 응답에 대한 속도와 지연시간 등을 로깅하고 있는 것을 확인할 수 있다.
이 앱은 요청을 보낼 URL, 요청 간 간격, HTTP 요청의 유형을
환경 변수를 지정함으로써 다르게 구성할 수가 있다.
환경 변수를 지정해
다른 URL로 요청을 보낸 결과를 로깅해보자.
예제
- 커맨드
# 실행 중인 도커 컨테이너 삭제
docker rm -f web-ping
docker container run --env TARGET=google.com diamol/ch03-web-ping
- 실행 결과
- --env, -e [key=value]
- 컨테이너에서 사용될 환경 변수를 지정하기 위해 사용됨
여기에서 중요하게 봐야 할 점은
도커의 이미지들은 애플리케이션을 구성할 때 기본 값을 가지도록 패키징할 수 있지만,
컨테이너를 실행할 때, 다른 환경을 설정 할 수 있어야 한다는 것이다
"web-ping"이라는 애플리케이션은
환경 변수의 키 값으로 "TARGET"을 가지며,
"TARGET"에 대한 환경 변수 값으로 요청을 보낸다.
호스트 컴퓨터도 자체적인 환경 변수를 가지지만,
그것과 별개로 각각의 컨테이너는 실행 될 때, 도커에서 제공하는 환경 변수만을 가질 수 있다.
위 그림에서 볼 수 있듯이
컨테이너에서 실행되는 애플리케이션 자체는 동일하지만,
도커 CLI로 입력한 환경 변수를 도커로부터 제공받아
서로 다른 작업을 실행한다는 것을 알 수 있다.
2. 첫 Dockerfile 작성하기
Dockerfile은 애플리케이션을 패키징하기 위해 작성해야 할 스크립트이다.
여기에 작성한 스크립트를 기준으로 도커 이미지가 생성된다.
"web-ping" 이미지에 대한 Dockerfile을 예시로 살펴보자.
FROM diamol/node
ENV TARGET="blog.sixeyed.com"
ENV METHOD="HEAD"
ENV INTERVAL="3000"
WORKDIR /web-ping
COPY app.js .
CMD ["node", "/web-ping/app.js"]
- FROM [시작 이미지 이름]
- 기본 이미지를 지정하기 위함
- 모든 이미지는 다른 이미지에서부터 시작해야 한다.
- "web-ping" 이미지는 "diamol/node" 이미지를 시작 이미지로 사용한다.
- ENV [key="value"]
- 환경 변수를 지정하기 위함
- WORKDIR [작업 폴더 경로]
- 컨테이너 이미지 전용 파일시스템 폴더를 만들 경로, 작업 폴더를 지정하기 위함
- 위 예시에서, Linux는 "/web-ping"의 위치에, Windows는 "C:\web-ping"의 위치에 작업 폴더를 생성한다.
- COPY [원래 경로] [작업 폴더 기준 복사할 경로]
- 컨테이너 이미지 안의 파일시스템에 파일 및 폴더를 복사하기 위함
- 위 예시에서는 작업 폴더에 "app.js"를 복사한다.
- CMD ["커맨드 1", "커맨드 2", ... ]
- 이미지로부터 컨테이너를 실행할 때, 컨테이너 내부에서 실행할 커맨드를 지정하기 위함
- 위 예시에서는 "/web-ping/"위치에 있는 "app.js"를 Node.js를 통해 실행시킨다.
여기에서 중요한 점은
Node.js에 대한 이해없이도 도커 CLI 명령어만 알고 있다면,
언제든지 컨테이너 이미지로부터 컨테이너를 실행해
내부에 있는 애플리케이션을 실행시킬 수 있다는 것이다.
Dockerfile 구문은 위의 다섯가지 말고도 많지만,
우선은 자주 사용하는 것들이기 때문에 먼저 알아두자.
3. 컨테이너 이미지 빌드하기
예제
- 실행 전 폴더 구조
- 커맨드
docker image build --tag web-ping .
- 실행 결과
- --tag [이미지 이름] [패키징할 파일들이 위치해 있는 폴더 경로]
- "--tag"와 이미지 이름을 지정함으로써 빌드되는 이미지의 이름을 지정할 수 있다.
- 그 다음 파라미터로 어떤 파일들을 패키징해야 할 지 경로를 지정해 줘야 한다.
- 예제의 마침표는 현재 폴더를 사용하라는 의미이다.
커맨드를 실행하면 아래와 같이 이미지를 확인할 수 있다.
4장. 도커 이미지와 이미지 레이어 이해하기
이미지가 어떻게 동작하고,
이미지와 이미지 레이어는 어떤 관계로 이어져 있는지 알아보자.
도커 이미지는
컨테이너의 파일시스템이 되는 모든 파일들을 가지고 있으며,
이미지 자체에 대해 설명하는 메타데이터를 포함하고 있다.
메타데이터에는 이미지가 빌드 되었을 당시의 히스토리도 포함되어 있으며,
이 메타데이터를 사용하여 이미지의 각 레이어와 레이어를 만든 명령어를 볼 수 있다.
예제
- 커맨드
docker image history web-ping
- 실행 결과
출력 결과를 보면,
"CREATED BY"는 Dockerfile에 작성된 명령어들이다.
Dockerfile에 작성한 명령어 한 줄당 하나의 이미지 레이어를 만든다는 것을 알 수 있다.
이렇게 이미지 레이어를 구성한 이유는 도커를 효율적으로 사용하기 위해서이다.
이에 대해서 더 자세히 살펴보자.
도커 이미지는 이미지 레이어들을 모아 놓은 것이다.
이러한 이미지 레이어들은 도커 엔진의 캐시 안에 물리적으로 저장되는 파일들이다.
이렇게 저장된 캐시들은 다른 이미지들과 컨테이너 사이에서 공유 될 수 있다.
예를 들어, 같은 Node.js를 실행하는 컨테이너들이 있다면,
Node.js 런타임을 포함한 이미지 레이어들 중 일부를 서로 공유하게 된다는 것이다.
이를 확인하기 위해 우선 이미지 목록으로부터
각 이미지들의 크기를 확인해보자.
예제
- 커맨드
docker image ls
- 실행 결과
"web-ping" 이미지를 빌드하기 위해 도커 허브에서 다운로드한 "diamol/ch03-web-ping" 이미지와
그 이미지로부터 새로 빌드한 "web-ping" 이미지를 확인할 수 있다.
두 이미지는 모두 75.5MB의 용량을 가지고 있고 합하면 151MB이다.
하지만 이건 이미지의 논리적인 크기이기 때문에, 실제로 디스크 공간을 차지하는 용량이 아니다.
도커에서는
실제 시스템에서 도커가 디스크 공간을 얼마나 사용하고 있는지
확인할 수 있는 기능을 제공한다.
예제
- 커맨드
docker system df
- 실행 결과
결과를 보면
"Images"의 크기는 75.49MB로,
이미지 목록에서 살펴봤던 이미지들의 합과 일치하지 않는다.
여기에서 알 수 있는 점은
이미지들 사이에서 공유될 수 있는 이미지 레이어는 서로 공유하기 때문에,
실제 디스크 공간을 차지하는 크기도 절약할 수 있다는 것이다.
이것이 Dockerfile 명령어 한 줄당 하나의 이미지 레이어로 분리하여
도커를 효율적으로 사용할 수 있다는 의미이다.
하지만 단점으로는
이미지 레이어들이 공유되고 있기 때문에 수정이 될 수 없다는 것이다.
공유되고 있는 이미지를 수정하게 된다면,
그 수정사항으로부터 영향을 받지 말아야 할 이미지도 변경되는 문제가 발생하기 때문이다.
도커는 이미지를 읽기 전용(Read Only)으로 만듬으로써 이를 해결했다.
한 번 이미지를 빌드하고 나면 다른 이미지들과 이미지 레이어를 공유 할 수 있지만 수정될 수는 없기 때문에,
빌드 후에도 최대한 많은 이미지 레이어를 공유하도록 최적화하여
도커 이미지를 더 작게 만들어 빌드를 더 빠르게 할 수 있다.
5장. 이미지 레이어 캐시를 사용해 Dockerfile 최적화하기
앞서 빌드했던 "web-ping" 이미지가 있는 상태에서
코드는 변경하지 말고 "app.js"의 맨 마지막 줄에 빈 줄을 추가한 뒤 다시 빌드해보자.
예제
- 커맨드
docker image build -t web-ping:v2 .
- 실행 결과
위 이미지를 보면
"[3/3] COPY app.js ." Dockerfile 명령어에 대해서는
CACHED가 붙어있지 않은 것을 볼 수 있다.
앞서 살펴 본 것처럼,
Dockerfile의 모든 명령어의 결과로 이미지 레이어들이 만들어진다.
하지만 빌드하는 과정에서
기존 이미지 대비 파일과 폴더들의 변경사항이 없고,
명령어의 내용이 동일하다면,
도커는 캐시 데이터에 저장되어 있던 이전 이미지 레이어를 사용한다.
도커는 이미지 레이어를 생성할 때,
입력 값을 나타내는 해시를 생성해
기존 캐시 데이터와 일치되는 해시가 있는지 비교 계산한다.
만약, 기존 캐시 데이터와 일치되는 것이 없다면,
새로운 이미지 레이어를 생성하여 저장하고,
그 이후의 명령어들에 대해서는 캐시 여부와 관계없이
새로운 이미지 레이어를 생성한다.
"app.js" 파일에 대해 변경사항이 발생하였고,
그로 인해 "COPY app.js ." 이후의 명령어에 대해서도
캐시 데이터를 이용하지 않고 새로운 이미지 레이어를 생성하기 때문에,
Dockerfile 명령어 중 변경사항이 없는 "CMD" 명령어도 새롭게 이미지 레이어를 생성한다.
Dockerfile에서 필요한 최적화는
바로 이처럼 변경사항이 발생했을 때,
이미지 레이어가 불필요하게 새로 생성되지 않도록
명령어들의 순서와 사용을 최적화 해야 된다는 것을 의미한다.
따라서 Dockerfile의 모든 명령어들은
자주 변경되는 명령어들이 아래에 위치하도록 최적화 해야 한다.
아래는 기존의 Dockerfile을 최적화 한 결과이다.
FROM diamol/node
CMD ["node", "/web-ping/app.js"]
ENV TARGET="blog.sixeyed.com" \
METHOD="HEAD" \
INTERVAL="3000"
WORKDIR /web-ping
COPY app.js .
- "CMD" 명령어는 "FROM" 명령어 이후 라인에어느 위치에 있더라도 동일하게 동작한다.
- "ENV" 명령어는 각 환경 변수를 따로따로 선언해서 지정하지 않고, \ (역슬래시)를 이용해 한번에 선언할 수 있다.
이는 이미지 레이어를 굳이 3개를 생성하지 않고 하나로 묶어 생성할 수 있게 만든다.
이렇게 최적화 된 Dockerfile은
빌드 시에 "app.js" 파일에 변경사항이 있더라도
그 이전의 명령어, 이미지 레이어들에 대해서는
기존의 캐시 데이터를 사용하기 때문에 새로 생성하지 않는다.
정리
이번 장에서는
- 도커 이미지 빌드
- Dockerfile 작성
- 도커 이미지와 이미지 레이어와의 관계
- Dockerfile 최적화
에 대해서 알아보았다.
효율적인 도커 이미지 관리를 위해 왜 Dockerfile의 최적화를 해야 하는지 알 수 있었고,
도커의 이미지를 직접 빌드해봄으로써 Dockerfile의 작성법과 일부 명령어에 대한 이해를 할 수 있었다.
'도서 > Learn Docker in a month of lunches' 카테고리의 다른 글
2장. Docker의 기본적인 사용법 (1) | 2023.09.18 |
---|---|
1장. 시작하기 전에 (1) | 2023.09.16 |