Post

[Docker] 동일한 환경 구성을 위한 컨테이너와, 이를 위한 컨테이너 구축/관리 도구 도커의 기본 명령어

특정 버전의 실행환경

🐀 똑같은 개발환경을 갖는 것은 상당한 가치가 있다

로컬환경, 개발환경에만 애플리케이션이 동작하는데 필요한 버전의 프로그램이 설치되어 있는 채로,
해당 애플리케이션이 호스트되어야 하는 서버의 원격시스템에 배포할 경우,
해당 원격시스템에는 동일한 버전의 프로그램이 없어 동작하지 않을 수 있다.

특정 버전의 실행환경을 고정해서 애플리케이션과 함께 배포할 수 있다면
애플리케이션은 항상 구동에 필요한 정확한 버전에서 실행될 수 있을 것이다.

즉, 항상 같은 환경을 사용한다는 🏆 재현성을 구현할 수 있다.
컨테이너📦라는 애플리케이션 코드코드가 필요로 하는 모든 것을 포함하는 환경을 만들어,
어디서나 동작하는 애플리케이션을 구현할 수 있다.

도커란

🐁 컨테이너 기술이다, 컨테이너를 구축하고 관리프로세스를 단순화하는 도구일 뿐이다

컨테이너📦애플리케이션 코드가 포함된 패키지이며 해당 코드를 실행하는데 필요한 모든 종속성과 도구🍡가 포함된 표준화된 소프트웨어 유닛을 말한다.

🎯 항상 동일한 환경에서 동일한 애플리케이션을 구동할 수 있다.

도커 컨테이너는 호스트 os의 내장 컨테이너가 작동하도록 그 위에 도커 엔진🚂을 실행시킨다.
도커엔진🚂을 기반으로 각각의 도커 컨테이너를 가동시킬 수 있다.

도커는 구성파일(Dockerfile)을 통해 컨테이너를 구성하고, 이를 공유하여 재구성할 수 있다.
이미지💽라는 패키지로 캡슐화해서 공유하기 쉬운 형태로 만든다.

예제: 🐋 Dockerfile

⚔️ 예제: Dockerfile (컨테이너를 구성)

🐋 Dockerfile을 기반으로 이미지를 빌드하고, 이미지를 기반으로 컨테이너를 실행한다.

🍍 빌드(build)란
실행파일을 만드는 과정을 말한다.
즉, 작업한 파일들(개발자코드, 라이브러리, 이미지 etc)을 출시하기 적합한 형태로 포장하는 일을 말한다. 압축, 변환

🐋 Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# NodeJS를 기본 이미지로 사용, FROM (DockerHub's Image)
FROM node:14

# 작업 디렉토리 설정
WORKDIR /app

# package.json을 작업 디렉토리에 복사
COPY package.json .

# 애플리케이션에 필요한 모든 종속성 설치
RUN npm install

# 나머지 코드 복사
COPY . .

# 빌드타임 인수
ARG DEFAULT_PORT=3000

# 환경 변수 사용함을 도커에게 알림, 환경변수명 디폴트값, 🐋 Dockerfile 내부에서만 사용가능
ENV PORT $DEFAULT_PORT

# 수신대기 포트 노출
EXPOSE $PORT

# 이미지 기반으로 컨테이너를 실행할 때, 실행 명령
CMD [ "node", "app.mjs" ]
📘 빌드타임 인수와 환경변수
도커는 빌드타임 인수환경변수를 지원한다.
빌드타임 인수와 환경변수를 사용하면 하드코딩을 줄여주기 때문에 보다 유연한 이미지/컨테이너를 만들 수 있다.
빌드타임 인수를 통해 이미지를 빌드할 때마다 다른 값을 플러그인 할 수 있다.
또한, 빌드타임 인수는 실행 중인 애플리케이션의 전체 애플리케이션 코드에서 사용할 수 있다.
$PORT에서 $는 빌드타임 인수/환경변수 명임을 도커에게 알려준다.
이미지를 빌드할 때나, 컨테이너를 실행할 때 동적으로 지정할 수 있다.

빌드타임 인수 지정
ex. docker build --build-arg DEFAULT_PORT=3000 .

환경변수 지정
ex. docker run --env PORT=3000 ..
(키값 쌍으로 여러개 --env에 추가할 수 있다.)

.env 파일에 키값을 정의하고 해당 파일을 컨테이너 실행 시 옵션으로 넘길 수 있다.
ex. docker --env-file ./.env ..

레이어 기반 아키텍처

🪐 컨테이너는 이미지 위에 추가된 얇은 레이어일 뿐이다

Alt text

이미지는 다수의 레이어로 구성되어 있다.
해당 레이어 개념은 빌드 속도를 최적화하기 위해 존재한다.
🐋 Dockerfile의 모든 명령은 레이어를 나타내고, 도커는 레이어를 캐시할 수 있다.
이미지를 빌드할 때마다 모든 명령 결과를 캐시하고, 이미지를 재빌드할 때 명령을 다시 실행할 필요가 없으면 캐시된 결과를 사용한다.

이미지를 기반으로 컨테이너를 실행하면 다수의 레이어로 구성된 이미지 위에 새로운 레이어가 추가되고 활성화된다.
컨테이너는 이미지에 저장된 코드와 환경을 사용하여 설정 및 구성된 애플리케이션을 실행한다.
(이 때 추가되는 레이어는 READ-WRITE 권한을 가진다.)

레이어 기반 아키텍처는 이미지가 변경될 때 의미가 있다.
이미지가 변경되고 재빌드할 때, 변경된 레이어 포함 후속 레이어가 재빌드된다.
⚠️변경된 레이어 이전 레이어는 캐시된 결과를 사용

때문에 Dockerfile 최적화를 위해서는 변경될 가능성이 제일 많은 COPY . . 명령 전에 종속성 설치 명령을 작성한다.
COPY package.json .
RUN npm npm install
COPY . .
이렇게 하면, (종속성이 변경되지 않는 한) 소스코드가 변경되고 재빌드할 때
종속성 설치는 캐시된 결과를 사용하며, 변경된 소스코드를 복사하는 명령과 그 이후의 명령만 재빌드되어 빌드 속도를 높일 수 있다.

즉, 소스코드를 변경할 때마다 종속성을 설치할 필요가 없다. 캐시된 결과를 이용

이미지와 컨테이너

🐙 이미지 = 컨테이너의 블루프린트

컨테이너는 항상 이미지를 기반으로 한다.
애플리케이션을 포함한 모든 설정명령, 의존성 및 도구가 포함된 공유가능한 패키지이미지💽라 한다.

이미지를 기반으로 컨테이너를 만든다.
한 번만 정의해놓으면 다른 시스템과 다른 서버에서 여러번 실행할 수 있다.
즉, 하나의 이미지로 수많은 컨테이너를 만들 수 있다.

컨테이너📦이미지의 구체적인 실행 인스턴스라 말할 수 있다.
이미지와 컨테이너의 관계는 프로그램프로그램을 실행시킨 프로세스 관계와 유사하다.
⚠️ 프로그램은 정적인 로직을 말하고 프로세스는 실행중인 로직을 말한다.
⚠️ 프로그램 : 프로세스 = 1 : N

컨테이너의 단발성과 영속성

🐁 컨테이너는 프로세스와 같은 맥락의 개념이기 때문에 다운로드 개념이 없다

컨테이너를 생성했다고 해서 다 조회되는 것은 아니다.
컨테이너는 단발성영속성으로 나눌 수 있고, 단발성은 🐋 docker ps 조회 명령어에는 뜨지 않는다.

도커 컨테이너가 단발성이라는 것은 해당 컨테이너가 실행된 후에는 그 내용이 유지되지 않고 사라진다는 것을 의미한다.
즉, 컨테이너는 실행되는 동안에만 존재하며, 종료되면 컨테이너 내부의 데이터나 상태는 모두 사라진다.

🎯 매일 특정시간에 DB에서 데이터를 추출하여 처리하고 결과를 전송하는 배치 작업을 수행하는 컨테이너

🎯 애플리케이션의 특정 부분을 테스트하기 위해 사용하는 테스트 환경 구축 컨테이너

반면, 도커 컨테이너가 영속성이 있다는 것은 컨테이너가 종료되어도 그 내용이 사라지지 않고 유지되는 것을 말한다.
때문에 🐋 docker ps와 같은 조회명령 결과에 해당 고유 CONTAINER_ID, NAMES, STATUS 등의 정보가 뜨게 된다.

🎯 데이터베이스를 실행하는 컨테이너

🎯 사용자가 업로드한 파일을 저장하고 제공하는 서버를 실행하는 컨테이너

⚠️ 컨테이너가 종료되더라도 데이터는 보존되어야 한다.

도커 컨테이너의 외부 통신

🪐 도커 컨테이너는 독립적인 공간에서 실행되기 때문에 접근이 안되는게 정상이다

컨테이너와 호스트 os 사이에는 default 연결이 없기 때문에, 통신하려는 컨테이너의 포트를 열어야 한다 노출.
즉, 외부 포트와 컨테이너 내부 포트를 연결해주어야 통신할 수 있다.

이 같이 포트를 연결해주는 과정포트포워딩이라 하며,
네트워크 바깥쪽으로 나가 소통할 수 있는 수단이다.

🐋 Dockerfile

1
2
3
4
5
6
7
# ...(생략)

# 애플리케이션이 수신대기하는 포트를 외부에 노출
# 컨테이너 외부에서 해당 포트를 도달할 수 있게 명시
EXPOSE 3000

# ...(생략)

✨ 네트워크 기반 애플리케이션에서, 해당 명령을 사용하여 외부로의 통신이 가능하게끔
내부에서 포트를 열어 수신대기하는 포트를 외부에 노출할 수 있지만, documentation 목적으로만 추가되었을 뿐이다.

즉, 이미지를 기반으로 컨테이너를 실행할 때 docker run -p 8000:3000 ..와 같이 도커에게 로컬 호스트 머신에서 Access하려는 포트도커 컨테이너에서 노출하여 연결하려는 포트를 알려줘야 한다.

docker run -d --rm -p 8000:80 --name nginx_1 nginx

🐋 -d (detach) : 백그라운드 모드로 실행
🐋 -p (publish) : 바깥쪽 포트 : 안쪽 포트 연결 (포트포워딩)
🐋 --name : 컨테이너 이름 : 태그 지정 *
🐋 --rm : 종료된 컨테이너 자동 제거

📕 IMAGE_NAME:TAG
이미지 이름을 이미지 그룹이라 하면, 태그는 해당 그룹의 특정화된 버전 및 구성을 정의할 수 있다.
이미지를 빌드할 때, 고유한 태그를 지정할 수 있는데 딱히 지정하지 않으면, default로 latest가 지정된다.
docker build -t nginx:1.27.1

🎯 이미지id 대신 name:tag 조합을 사용해서 도커명령을 할 수 있다.

* 태그 이름 변경
docker tag PRIOR_NAME:PRIOR_TAG NEW_NAME:NEW_TAG
※ DockerHub에는 다양한 태그를 지닌 특정 이미지들이 있다.
📗 nginx란
WAS(Servlet Container)를 도와주는 경량 웹서버를 말한다.
서버의 제일 앞단에 쓰며, DispatcherServlet 역할을 한다.
nginx를 쓰면 🎯어떤 도메인으로 접근했는지 감지할 수 있고, 🎯포워딩이 빠르다.

도커 기본 명령어

🐙 도커 기본 명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 💽도커 이미지 다운
docker pull IMAGE_NAMES

# 💽도커 이미지 조회
docker images

# 💽 이미지 세부정보 조회
docker image inspect IMAGE_NAME

# 💽도커 이미지 삭제
# (단, 컨테이너에 속한 이미지는 우선 컨테이너를 제거해야 한다)
docker rmi IMAGE_NAMES

# 💽 모든 도커 이미지 삭제
docker rmi -f $(docker images -qa)

# 💽 안쓰는 모든 이미지 제거
docker image prune

# 📦 이미지기반 컨테이너 실행
docker run IMAGE_NAME

# 📦 도커 컨테이너 (실행중 + 실행했었던) 조회
docker ps -a

# 📦 도커 컨테이너 삭제
docker rm CONTAINER_ID

# 📦 도커 컨테이너 중지
docker stop CONTAINER_ID

# 📦 도커 컨테이너 로그
docker logs CONTAINER_ID

# 📦 컨테이너 세부정보 조회
docker container inspect CONTAINER_ID | less

# 📦 모든 도커 컨테이너 삭제
docker rm -f $(docker ps -qa)

# 도커 프로젝트 삭제
sudo rm -rf /docker/projects
sudo rm -rf /docker_projects

DockerHub에 이미지 push/pull

🍝 DockerHub는 도커 공식 이미지 레지스트리를 말한다

🐋 docker push IMAGE_NAME
DockerHub Repository와 동일한 이름을 이미지 이름으로 설정하면 docker push를 할 수 있다.
이후에 변경된 이미지를 올릴 때는 태그만 변경해서 올린다.
즉, 동일한 저장소 내에 각기 다른 태그른 붙인 이미지를 포함할 수 있다.

docker logindocker push
docker push를 할 때는 DockerHub에 있는 것을 제외한 필요한 추가 코드만을 내부적으로 푸시한다.

🐋 docker pull IMAGE_NAME
DockerHub에서 이미지를 다운로드하는 과정으로, 항상 그 이름의 최신 이미지를 가져온다.

📕 docker run과 docker pull
docker run ... 해당 명령어를 실행했을 때, 로컬 호스트 머신에서 해당 이름의 이미지를 찾지 못한다면
컨테이너 히스토리에 접근해서 자동으로 이미지를 풀링하지만
로컬 호스트 머신에서 찾는 이미지가 있다면, 최신 버전인지 아닌지에 상관없이 찾은 이미지를 사용한다.
때문에 docker run을 실행하기 전에 docker pull을 실행해야 한다.
This post is licensed under CC BY 4.0 by the author.