이미지
이미지는 실행 가능한 소프트웨어의 템플릿이다.
파일 시스템과 애플리케이션 실행에 필요한 의존성, 설정, 코드, 라이브러리 등이 포함된 읽기 전용 스냅샷
- 읽기 전용(Read-only): 이미지는 변하지 않습니다.
- 레이어(Layered File System): 이미지 파일은 여러 레이어로 구성되며, 새로운 이미지를 생성할 때 기존 레이어 위에 추가됩니다. 이 덕분에 효율적으로 스토리지를 사용합니다.
- 생성: 이미지는 Dockerfile이라는 설정 파일을 기반으로 생성됩니다.
컨테이너
컨테이너는 이미지를 기반으로 실행 중인 애플리케이션의 인스턴스이다.
쉽게 말해, 이미지를 실행시키면 컨테이너가 생성된다.
- 가볍고 독립적: 호스트 시스템의 커널을 공유하므로 가볍고, 실행 환경이 독립적입니다.
- 읽기-쓰기 가능(Read-Write): 컨테이너는 이미지를 읽기 전용으로 사용하지만, 실행 중 데이터는 읽기-쓰기 가능한 계층에 저장됩니다.
- 라이프사이클: 컨테이너는 생성, 실행, 일시 정지, 삭제 등의 상태를 가질 수 있습니다.
즉 이미지에서 컨테이너를 생성하고, 이미지는 컨테이너를 만들기 위한 기본 템플릿이다.
컨테이너는 이미지에 의존한다.
여러 컨테이너를 생성할 수 있다.
실제로 컨테이너를 실행할 수 있도록 이미지를 생성하고, 가져오는 방법은 2가지이다
1. 기존에 존재하는 이미지 사용(누가 구축 했거나, docker.hub 사용)
docker.hub에 있는 공식 node 이미지를 사용해보자.
프로젝트를 vscode로 연다.
터미널에
docker run node
위 명령어를 입력하면 자동으로 node가 설치되고 컨테이너가 생성되게 된다.
비슷하게
docker run -it node
를 작성하면 컨테이너 안에서 해당 노드를 사용할 수 있다.
실제로 내 노트북에넌 node 버전이 20.18.1이지만 컨테이너 내부 노드는 23.6.1이다.
이렇게 컨테이너를 만들어 다른 버전의 노드를 유지할 수 있다.
그 후에
docker ps -a
를 작성하면 컨테이너 목록 전체를 볼 수 있고 방금 생성한 컨테이너 2개를 확인할 수 있다.
2. 이미지 커스텀(공식 이미지를 가져온 후 코드를 추가하여 새로운 이미지를 생성)
프로젝트에 Dockerfile 파일을 생성한다.(파일명 변경 금지)
그 후 아래와 같이 코드 작성(설명은 주석에)
FROM node
# 이 작업을 통해 앞으로 모든 설정은 /app에서 진행된다고 정할 수 있다.
WORKDIR /app
# 첫 번째 경로는 컨테이너 외부, 이미지의 외부 경로로 이미지로 복사되어야 할 파일들이 있는 곳(.은 Dockerfile이 포함된 동일한 폴더임을 알림)
# 두 번째 경로는 그 파일을 저장해야 하는 이미지 내부의 경로(루트에 저장하는 것 보다 서브 폴더를 만들어서 저장하는 것이 좋다.)
# 여기서 원래 /app가 맞지만 위에서 WORKDIR을 설정해줬기 때문에 ./로 작성(/app으로 작성해도 가능, 직관적이라 추천천)
COPY . ./
# npm install
# Docker 컨테이너 및 이미지의 작업 디렉토리에서 실행
# Defalt로 컨테이너 파일 시스템의 루트 폴더로 되어 있다.
# 현재 /app 에 파일을 저장했기 때문에 /app에서 실행해야 한다.
# 그래서 위에 WORKDIR을 /app으로 설정
RUN npm install
# 포트 설정
EXPOSE 80
# RUN과 다른 점은 이미지가 생성될 때 실행되지 않고 이미지를 기반으로 컨테이너가 시작될 때 실행된다.
# 도커에게 이미지를 기반으로 컨테이너가 생성될 때마다 그 컨테이너 내부에 있는 node 명령을 사용하여 server.js 파일을 실행하도록 지시
CMD ["node", "server"]
이제 실행시켜보자.
실행시키기 전에 위 설정대로 이미지를 생성하자.
# 여기서 . 은 Dockerfile을 찾기 위한 것으로 현재 터미널 위치를 의미한다.
docker build .
생성하면 이미지 ID 값을 터미널에서 확인할 수 있고 그 값을 활용하여 실행하자.
docker run a228ed423852b1d3a63491fece9a695374760900538557cb08171f53338c953a
하지면 실행을 해도 localhost:30으로 접근이 되지 않을 것이다.
그 이유는 포트 설정을 문서화만 해줬을 뿐 연결해주지 않았기 때문이다.
도커 내부에서만 80 포트를 사용한다고 설정하고 외부에 알리지 않았다.
EXPOSE는 선택사항 이지만 하는 것을 추천한다!
이를 해결하기 위해 아래와 같이 실행한다.
docker run -p 3000:80 a228ed423852b1d3a63491fece9a695374760900538557cb08171f53338c953a
# 여기서 ID 값을 꼭 풀로 써 줄 필요는 없다.
# 중복되는 컨테이너가 없을 경우 중간까지만 작성해도 된다.
3000포트를 통해 80포트로 연결된다는 뜻? 같다..!
이러면 localhost:3000으로 접근 가능하다.
참고로 컨테이너를 중지하기 위해서는 따로 명령어를 입력 해야한다.(안하면 중지되지 않는다.)
docker stop 컨테이너 이름
컨테이너 이름은 docker ps(또는 docker ps -a)를 통해 알 수 있다.
여기서 이제 코드를 살짝 변경하고 다시 도커를 실행하면 어떻게 될까?
실제로 html 코드에 !를 추가한 후 새로 docker를 실행시켜보자
docker run -p 3000:80 a228ed423852b1d3a63491fece9a695374760900538557cb08171f53338c953a
실행 시켜도 홈페이지에서는 변화가 없다.
왜 이럴까?
그 이유는 COPY를 실행한 시점에서 코드 스냅샷을 만들어 저장하기 때문이다.
즉, 빌드할 때마다 코드는 저장된다.
재빌드 후 재실행 하면 변경된 코드가 반영되어 있을 것이다.
즉 이미지는 읽기 전용이며 닫혀있다!!
레이어 기반
이미지를 빌드하거나 다시 빌드할 때 변경된 부분의 명령과 그 이후의 모든 명령이 재평가된다는 의미
즉, 변경 사항이 있으면 재빌드 해야하는 부분 이후로만 빌드한다.
(캐싱을 통해 이전에 한 빌드 정보를 저장하고 있음)
하지면 현재 설절대로면 코드가 변경될 때마다 npm install이 실행된다.
이를 최적화 하기 위해 설정을 아래와 같이 변경해보자.
// Dockerfile
FROM node
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . ./
EXPOSE 80
CMD ["node", "server.js"]
이러면 코드가 변경되어도 COPY . ./ 이후로만 재 실행된다.(package.json 파일이 변경되지 않았기 때문)
이제 이미지와 컨테이너를 관리해보자.
docker --help
를 입력하면 가능한 명령어들이 전부 나온다.
'인프라 > Docker & Kubernetes' 카테고리의 다른 글
1. 시작하기 (1) | 2025.01.25 |
---|