컨테이너를 사용하면 애플리케이션의 기술 스택과 무관하게 동일한 방법으로 애플리케이션을 실행하고 관리할 수 있다. 이번 포스팅에서는 이러한 컨테이너의 모체가 되는 이미지(Image)를 만들어보고자 한다.
이전글)
2024.02.25 - [Infra/Docker] - Docker - 컨테이너(Container)
< 목차 >
- 공유된 이미지 내려받기
- Dockerfile
- 이미지 빌드하기
- 환경 변수 설정하기
1. 공유된 이미지 내려받기
도커에서 이미지(Image)는 컨테이너를 생성하고 실행하기 위한 기본 단위로 애플리케이션 실행에 필요한 파일 시스템과 설정을 포함하는, 경량화된, 독립적인, 실행 가능한 소프트웨어 패키지라고 할 수 있다. 이미지는 애플리케이션 및 애플리케이션 실행에 필요한 모든 것(코드, 런타임, 라이브러리, 환경 변수, 설정 파일 등)을 포함하고 있다. 그리고 이를 통해 우리는 어떠한 환경에서든지 일관된 방식으로 애플리케이션을 배포하고 실행할 수 있게 된다.
우선 다음의 명령어를 통해 컨테이너를 실행해보도록 하자.
$ docker container run --detach --publish 8080:80 nginx:1.23.1-alpine
위의 명령어는 nginx:1.23.1-alpine 이미지를 사용해서 컨테이너를 실행하라는 명령어이다. 로컬에 nginx:1.23.1-alpine 이미지가 없다면 명령어가 실행되면서 "Unable to find image ~ "라는 문구와 함께 이미지를 다운받아 실행하는 것을 확인할 수 있을 것이다. 이처럼 필요한 이미지 중 로컬 컴퓨터에 없는 이미지가 있으면 이미지를 내려받는 과정이 진행된다.
이미지를 내려받는 과정을 도커에 전적으로 맡길 수도 있지만, 도커 CLI를 통해 명시적으로 원하는 이미지를 내려받는 것도 가능하다. 다시 아래 명령어를 입력해 컨테이너 이미지를 내려받아보자.
## 기존에 내려받은 이미지 삭제
$ docker image rm --force nginx:1.23.1-alpine
## 이미지 내려받기
$ docker image pull nginx:1.23.1-alpine
pull 명령어를 통해 내려받고자 하는 이미지 이름은 nginx:1.23.1-alpine이고, 이 이미지는 도커가 이미지를 찾기 위해 접근하는 저장소인 도커 허브에 저장되어 있다. 이렇게 이미지를 제공하는 저장소를 "레지스트리(Registry)"라고 하는데, 도커 허브는 무료로 제공되는 공개 레지스트리이다.
도커 허브 링크) https://hub.docker.com/
도커 이미지는 논리적으로 하나의 대상이지만, 이미지를 내려받는 과정을 보면 위와 같이 여러 건의 파일을 동시에 내려받는다는 점에서 단일 파일을 내려받는 과정은 아니라는 것을 알 수 있다. 이미지는 기본적으로 계층 구조로 되어있으며 이들 각각의 파일을 "이미지 레이어"라고 부른다. 다시 말해, 도커 이미지는 물리적으로는 여러 개의 작은 파일로 구성되어 있다. 그리고 도커가 이들 파일을 조립해 컨테이너의 내부 파일 시스템을 만든다. 모든 레이어를 내려받고 나면 비로소 전체 이미지를 사용할 수 있게 되는 것이다.
2. Dockerfile
Dockerfile은 애플리케이션을 패키징하기 위한 간단한 스크립트이다. Dockerfile은 일련의 인스트럭션으로 구성되어 있는데, 인스트럭션을 실행한 결과로 도커 이미지가 만들어진다. 실제 아주 간단한 자바스크립트 파일을 Dockerfile을 이용해 패키징 해보면서 Dockerfile 스크립트 문법을 간단하게 살펴보도록 하자.
// app.js
const options = {
name: process.env.NAME,
interval: process.env.INTERVAL,
};
process.on('SIGINT', () => {
process.exit();
});
let i = 1;
setInterval(() => {
if (!options.interval) {
console.log('No interval!');
process.exit();
}
console.log(`${options.name} is running the code: ${i++};`);
}, options.interval);
# Dockerfile
FROM node:18.15-alpine
ENV NAME="bitkunst"
ENV INTERVAL="3000"
WORKDIR /App
COPY app.js .
CMD ["node", "/App/app.js"]
Dockerfile 스크립트에 등장한 인스트럭션은 FROM, ENV, WORKDIR, COPY, CMD이다. 참고로 이들 다섯 가지 인스트럭션만으로도 어지간한 애플리케이션을 도커로 패키징하는데 별 문제가 없다. 각각의 인스트럭션에 대해 하나씩 살펴보면 다음과 같다.
👉 FROM
- 모든 이미지는 다른 이미지로부터 출발한다.
- 위의 이미지는 node:18.15-alpine 이미지를 시작점으로 지정한 것이다.
- node:18.15-alpine 이미지에는 app.js 파일을 실행시키는데 필요한 런타임인 Node.js가 설치되어 있다.
👉 ENV
- 환경 변수 값을 지정하기 위한 인스트럭션이다.
- 값을 지정하기 위해 key="value" 형식을 따른다.
- 위의 이미지에서는 ENV 인스트럭션을 두 번 사용해 총 두 개의 환경 변수를 설정했다.
👉 WORKDIR
- 컨테이너 이미지 파일 시스템에 디렉토리를 만들고, 해당 디렉토리를 작업 디렉토리로 지정하는 인스트럭션이다.
- 리눅스와 윈도우 컨테이너 모두 구분자로 슬래시(/)를 사용한다.
👉 COPY
- 로컬 파일 시스템의 파일 혹은 디렉토리를 컨테이너 이미지로 복사하는 인스트럭션이다.
- COPY [원본경로] [복사경로] 형식으로 지정한다.
- 위의 스크립트에서는 로컬 파일 시스템에 있는 app.js 파일을 이미지의 작업 디렉토리로 복사한 것이다.
👉 CMD
- 도커가 이미지로부터 컨테이너를 실행했을 때, 실행할 명령을 지정하는 인스트럭션이다.
- 위의 스크립트에서는 Node.js 런타임이 애플리케이션을 실행하도록 app.js를 지정했다.
3. 이미지 빌드하기
이미지를 빌드하기 위해서는 앞서 작성한 Dockerfile 스크립트와 함께 몇가지 고려해야할 사항들이 있다. 바로 이미지의 이름, 패키징에 필요한 파일의 경로를 추가로 지정해 주어야 한다. 다음과 같이 docker image build 명령어를 사용해 Dockerfile 스크립트로 이미지를 빌드할 수 있다.
$ docker image build --tag sample-app .
--tag 옵션과 함께 전달한 값(sample-app)은 이미지의 이름이 되고, 이어지는 인자는 Dockerfile 및 이미지에 포함시킬 파일이 위치한 경로가 된다. 도커에서는 이 디렉토리를 "컨텍스트"라고 한다. 마지막의 .은 현재 작업 디렉토리를 의미하며 도커가 빌드 컨텍스트 정보를 필요로 하기 때문에 명령 마지막에 현재 작업 디렉토리를 나타내는 .을 빠트리면 안된다. 다시 해당 명령어를 해석해보면 "새로 빌드되는 이미지의 이름은 sample-app이고 이미지에 들어갈 파일이 위치한 디렉토리는 현재 디렉토리이다"가 된다.
- --tag , -t
- 태그는 새로 빌드되는 이미지의 이름이다.
성공적으로 이미지가 빌드되었다면, 빌드된 이미지는 로컬 이미지 캐시에 저장된다. 다음의 도커 명령으로 이미지 목록을 확인해보자.
$ docker image ls 's*'
's'로 시작하는 태그명을 가진 이미지 목록을 확인할 수 있을 것이다. 그리고 이렇게 빌드된 이미지는 앞서 도커 허브와 같은 공개 레지스트리에서 내려받은 이미지와 똑같이 사용할 수 있다.
4. 환경 변수 설정하기
환경 변수(environment variable)는 운영체제에서 제공하는 키-값 쌍이다. 윈도우나 리눅스나 같은 방식으로 동작하며, 아주 적은 양의 데이터를 저장하는데 유용하다. 도커 컨테이너도 별도의 환경 변수를 가질 수 있는데 이 환경 변수는 호스트 운영체제의 것을 가져오는게 아니라 컨테이너의 호스트명이나 IP 주소처럼 도커가 부여해주는 것이다. 앞서 빌드한 sample-app 이미지에도 이러한 환경 변수의 기본값을 세팅해 놓았다. Dockerfile 스크립트의 ENV 인스트럭션으로 설정해 놓은 값이 그것이다. 컨테이너를 실행하면 도커가 이들 기본값을 컨테이너에 적용하고 이 값을 애플리케이션에서 사용한다.
$ docker container run --name sample sample-app
sample-app 이미지를 사용해서 sample이라는 이름의 컨테이너를 실행시켰다. Dockerfile에서 설정한 환경 변수 NAME, INTERVAL의 기본값에 의해 "bitkunst is running ~"이라는 텍스트가 3초 간격으로 콘솔에 찍히는 것을 확인할 수 있다.
- --name
- 컨테이너를 조작하려면 처음 실행할 때 임의로 생성된 컨테이너 ID를 입력해 대상 컨테이너를 지정해줘야 한다.
- --name 플래그를 사용하면 컨테이너에 원하는 이름을 붙이고 이 이름으로 컨테이너를 지칭할 수 있다.
하지만 컨테이너를 생성할 때 기본값과 다른 값을 환경 변수로 설정할 수도 있다. 당연하게도 환경 변수의 값을 변경하면 애플리케이션의 동작 내용도 바뀌게 된다. 도커 이미지는 설정값의 기본값을 포함해 패키징되지만, 컨테이너를 실행할 때 이 설정값을 바꿀 수 있어야 한다. 그리고 환경 변수를 이용하면 이를 간단하게 구현할 수 있다.
$ docker container run --env NAME=Huchu --env INTERVAL=1000 --name sample sample-app
sample 애플리케이션의 코드는 NAME과 INTERVAL이라는 이름의 환경 변수 값을 사용한다. 이미지에는 이 환경 변수의 값이 각각 "bitkunst" 와 "3000"으로 설정되어 있으나 docker container run 명령에서 --env 플래그를 사용해 다른 값을 지정할 수 있다. 결과적으로 위의 명령어로 실행된 sample 컨테이너 애플리케이션은 "Huchu is running ~"이라는 텍스트를 1초 간격으로 출력하게 된다.
호스트 컴퓨터에도 고유의 환경 변수가 있다. 그러나 호스트 컴퓨터의 환경 변수는 컨테이너와는 별개다. 컨테이너는 도커가 부여한 환경 변수만을 갖는다. 결국 패키징 과정을 Dockerfile 스크립트에 작성하고 이미지에 포함시킬 리소스를 모은 다음, 사용자로 하여금 애플리케이션의 동작을 어떤 방식으로 설정하게 할 것인지 결정하게 되는데, 이 때 우리는 환경 변수를 이용해 다양한 설정값이 반영될 수 있도록 할 수 있다.
References
'Infra > Docker' 카테고리의 다른 글
Docker - 컨테이너(Container) (0) | 2024.02.25 |
---|