본 글은 도커 교과서를 읽고 정리한 내용입니다.
이번 장에선 도커 이미지를 만드는 방법, Dockerfile 문법을 배울 예정
도커 허브에 공유된 이미지 사용하기
docker image pull diamol/ch03-web-ping
도커는 위 이미지(diamol/ch03-web-ping)가 로컬에 존재하지 않으면 도커 허브에서 이미지를 갖고옴.
(도커 허브 : 도커가 가장 먼저 이미지를 찾기 위해 접근하는 저장소)
이미지를 제공하는 저장소를 레지스트리(registry)라고 하는데, 도커 허브는 무료 공개 레지스트리
도커 이미지는 논리적으론 하나의 대상(애플리케이션 스택 전체가 하나의 파일로 압축된 압축 파일)!
이미지를 내려받는 과정을 보면 여러개 파일을 동시에 내려받음
이 여러개의 파일을 이미지 레이어라 부름.
도커 이미지는 물리적으론 여러개의 작은 파일로 구성되어 있다!
그리고 도커가 이 파일들을 조립해 컨테이너 내부 파일 시스템을 만듬.
모든 레이어를 받고나면 전체 이미지를 사용할 수 있게 됨!
아래 명령어를 실행하면
docker container run -d --name web-ping diamol/ch03-web-ping
diamol/ch03-web-ping 이미지로 컨테이너를 실행하는데 -d(=detach) 플래그때문에 백그라운드에서 동작함
--name: 컨테이너에 이름을 붙여주는 역할
docker container logs web-ping
위 명령어를 통해 web-ping 컨테이너의 로그를 확인할 수 있음.
환경 변수란 운영체제에서 제공하는 키-값 쌍으로 아주 적은 양의 데이터를 저장하는데 유용함.
도커 컨테이너도 별도의 환경변수를 가질 수 있다.
그러나 이 환경변수는 호스트 운영체제의 것을 가져오는 게 아니라 컨테이너의 호스트명이나 IP 주소처럼 도커가 부여해줌
우리가 실행한 web-ping 이미지에도 환경 변수의 기본값이 포함돼있다.
컨테이너를 실행하면 도커가 이 기본값을 컨테이너에 적용하고, 이 값을 애플리케이션에 사용함.
컨테이너를 생성할 때 기본값과 다른 값을 환경 변수로 설정할 수 있다! -> 당연히 환경 변수 값이 변하면 애플리케이션의 동작 내용도 바뀜!
docker rm -f web-ping
docker container run --env TARGET=google.com diamol/ch03-web-ping
위 명령어를 통해 실행중인 컨테이너를 삭제하고 환경 변수 TARGET의 값을 google.com으로 바꿔보자!
-d 플래그를 주지 않았기에 애플리케이션의 출력 내용이 콘솔에 표시됨.
pinging하는 곳을 보면 google.com으로 바껴있음.
여기서 알 수 있는 것은 도커 이미지는 설정값의 기본값을 포함해 패키징되지만, 컨테이너를 실행할 때 이 설정 값을 바꿀 수 있다!
호스트 컴퓨터에도 고유한 환경 변수들이 있지만, 이 환경 변수들은 컨테이너 내에서 사용되는 환경 변수와 다르다!
컨테이너는 도커가 부여한 환경 변수만을 갖는다~!
Dockerfile 작성하기
Dockerfile은 애플리케이션을 패키징하기 위한 간단한 스크립트
Dockefile은 일련의 인스트럭션으로 구성되어 있는데, 인스트럭션을 실행한 결과로 도커 이미지가 만들어진다.
Dockefile은 어떠한 애플리케이션이라도 패키징할 수 있다.
앞의 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: 모든 이미지는 다른 이미로부터 출바함. 현재 이 이미지는 diamol/node를 시작점으로 지정했다. diamol/node 이미지엔 web-ping 애플리케이션을 실행하는데 필요한 런타임 Node.js가 설치돼있다.
- ENV:환경 변수 값을 지정하기 위한 인스트럭션. 값을 지정하기 위해 [key]="[value]" 형식을 따름.
- WORKDIR: 컨테이너 이미지 파일 시스템에 디렉토리를 만들고, 해당 디렉토리를 작업 디렉토리로 지정하는 인스트럭션.
- COPY: 로컬 파일 시스템의 파일 혹은 디렉토리를 컨테이너 이미지로 복사하는 인스트럭션. [원본경로] [복사경로] 형식으로 지정한다. 위 스크립트에선 로컬 파일 시스템에 있는 app.js 파일을 이미지의 작업 디렉토리로 복사한 것!
- CMD: 도커가 이미지로부터 컨테이너를 실행했을 때 실행할 명령을 지정하는 인스트럭션. 여기선 node.js 런타임이 애플리케이션을 시작하도록 app.js 지정함.
Node.js나 자바스크립트에 대해 전혀 몰라도 애플리케이션을 패키징하고 도커를 통해 실행하는데 아무 문제가 없음.
컨테이너 이미지 빌드하기
이미지를 빌드하려면 Dockerfile 스크립트 외에도 필요한 것이 더 있음.
이미지 이름 , 패키징에 필요한 파일의 경로를 추가로 지정해 주어야 함.
아래 명령어를 통해 Dockerfile 스크립트로 이미지 빌드해보자
docker image build --tag web-ping .
--tag의 인자값(web-ping)은 이미지의 이름이며, 그 뒤의 인자는 Dockerfile 및 이미지에 포함시킬 파일이 위치한 경로.
도커에선 이 디렉토리를 컨텍스트라 함!
마지막의 . 은 '현재 작업 디렉토리'라는 뜻!
build 명령을 입력하면 Dockerfile 스크립트에 포함된 인스트럭션이 차례로 실행되며 그 결과가 출력됨.
cf) 만약 build 명령에서 어떤 오류가 발생했다면,
- 도커 엔진이 정상적으로 동작 중인지 확인
- 도커 데스크톱의 상태 확인
- 현재 작업 디렉토리가 정확한지 확인(현재 작업 디렉토리 내 Dockerfile과 app.js가 있는지 확인)
- build 명령을 정확하게 입력했는지 확인
- 도커가 빌드 컨텍스트 정보를 필요로 하기에 명령 마지막에 현재 작업 디렉토리를 나타내는 .을 빠트리면 안됨.
아래 명령어를 통해 'w'로 시작하는 태그명을 가진 이미지 목록을 확인해보자
docker image ls 'w*'
이렇게 빌드된 이미지는 도커 허브에서 내려받은 이미지와 똑같이 사용할 수 있다!
애플리케이션을 이미지로 패키징해 도커로 실행하는 방법은 다음과 같음.
패키징 과정을 Dockerfile 스크립트에 작성하고 이미지에 포함시킬 리소스를 모은 다음, 사용자로 하여금 애플리케이션의 동작을 어떤 방식으로 설정하게 할 것인지 결정!
도커 이미지와 이미지 레이어 이해하기
도커 이미지엔 우리가 패키징에 포함시킨 모든 파일이 들어있다. 이 파일은 나중에 컨테이너의 파일 시스템을 형성함.
이 외에도 이미지엔 자신에 대한 여러 메타데이터 정보도 들어있으며 이 정보 중엔 이미지가 어떻게 빌드됐는지에 대한 간단한 이력도 있음.
이 정보를 통해 이미지를 구성하는 각 레이어가 무엇인지, 이 레이어가 어떤 명령으로 빌드됐는지 알 수 있다.
docker image history web-ping
위 명령어로 알 수 있다.
CREATED BY는 해당 레이어를 구성한 Dockerfile 인스트럭션이다.
Dockerfile 인스트럭션과 이미지 레이어는 1:1 관계를 가진다.
도커 이미지는 이미지 레이어가 모인 논리적 대상으로, 레이어는 도커 엔진의 캐시에 물리적으로 저장된 파일!
이미지 레이어는 여러 이미지 컨테이너에서 공유됨.
web-ping과 other-node-app 모두 diamol/node 이미지를 기반으로 한다.
기반 이미지의 모든 레이어를 포함한다!
Dockerfile의 FROM 인스트럭션이 의미하는것이 이것!
이미지 레이어를 여러 이미지가 공유한다면, 공유되는 레이어는 수정할 수 없어야함!
만약 이미지 레이어를 수정할 수 있다면 그 수정이 레이어를 공유하는 다른 이미지에도 영향을 미치게 될 것이니까!
도커는 이미지 레이어를 읽기 전용으로 만들어 두어 이런 문제를 방지함.
이미지를 빌드하면서 레이어가 만들어지면 레이어는 다른 이미지에 재사용될 수 있다. 그러나 레이어를 수정할 수는 없다.
이미지 레이어 캐시를 이용한 Dockerfile 스크립트 최적화
특정 파일을 수정한 뒤 이미지를 다시 빌드하면 새로운 이미지 레이어가 생긴다.
도커의 이미지 레이어가 특정한 순서대로 배치된다 가정할 때, 순서 중간에 있는 레이어가 변경되면 변경된 레이어보다 위에 오는 레이어를 재사용할 수 없다.
중요한 점은 Dockerfile 스크립트의 인스트럭션은 각각 하나의 이미지 레이어와 1:1로 연결된다. 그러나 인스트럭션의 결과가 이전 빌드와 같다면, 이전에 캐시된 레이어를 재사용한다. -> 인스트럭션을 다시 실행하는 낭비 줄임
특정 레이어가 변경 돼 인스트럭션이 재실행되면, 그 뒤에 오는 인스트럭션은 수정된 것이 없더라도 모두 다시 실행된다.
그러므로 Dockerfile 스크립트의 인스트럭션은 잘 수정하지 않는 인스트럭션이 앞으로 오록 자주 수정되는 인스트럭션이 뒤에 오도록 배치해야 함! => Dockerfile 스크립트 최적화