Image?

namkun·2022년 4월 27일
0

Docker & Kubernetes

목록 보기
3/16

이미지

이미지는 모든 설정 명령과 모든 코드가 포함되어있는 패키지라고 할 수 있다.

즉, 컨테이너 실행을 위한 모든 파일과 설정값과 같은 것들이 이미지에 모두 들어가있다고 보면 된다.

그렇기에 우리가 특정 이미지가 있다면, 우리는 어느 컨테이너에서나 해당 이미지 안에 들어가있는 정보를 통해서 동일한 기능을 실행할 수 있다.

사용법

이러한 이미지은 사용법이 두 가지 방법이 있다.

  1. 미리 만들어진 이미지를 가져오는 방법(docker hub..)
  2. 직접 이미지를 만드는 방법

만들어진 이미지를 가져오는 방법

우선 첫번째 방법부터 알아보자.

간단하게 사용하는 방법은 다음과 같다.

$ docker run node

위의 명령어와 같이 run 명령어 뒤에 node 라고 붙이면 다음과 같은 내용이 보일 것이다.

Unable to find image 'node:latest' locally

node에 대해서 이미지를 로컬에 세팅하거나, 다운받아놓은 적이 없기에 그렇다.

그 다음에는 다음과 같이 이미지를 docker hub에서 자동으로 다운받는 것을 볼 수 있다.

latest: Pulling from library/node
6aefca2dc61d: Downloading [===================>       ]  21.06MB/54.94MB
967757d56527: Download complete
c357e2c68cb3: Download complete
c766e27afb21: Downloading [=============>             ]  15.12MB/54.58MB
32a180f5cf85: Downloading [=====>                     ]  20.47MB/196.7MB
3507b5066a40: Waiting
fa4934a906af: Pulling fs layer
fd7c6a234db2: Waiting
e9fdaad45501: Waiting

이렇게 이미지를 다운로드를 다 한 다음에는 아까 입력해뒀던 run 명령어를 통해 다운로드받은 이미지를 통해 컨테이너를 실행한다.

자 그럼 이제 컨테이너가 제대로 생성되었는지 확인해보자.

$ docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                      PORTS     NAMES
b2fc8690f9eb   node           "docker-entrypoint.s…"   6 seconds ago   Exited (0) 4 seconds ago              strange_rosalind

node이미지를 통해 id가 b2fc8690f9eb인 컨테이너가 실행되었음을 알 수 있다.

맨 끝의 NAMES 를 통해 이름을 자동으로 알아서 생성하였음도 알 수 있다. (이는 옵션을 통해서 설정할 수 있다.)

그러나 이러한 컨테이너가 생성되었다고 한들 우리에게는 큰 의미가 없다.

왜냐면 우리가 직접 node 명령어를 내릴 수 없으니까..(직접 컨테이너에 접속하는게 아니고서야..)

그렇기에 다음에는 이렇게 실행해보자.

$ docker run -it node

여기서 보이는 -it 옵션은 컨테이너 내부에서 호스팅되는 머신으로 인터렉티브 세션을 노출하도록 한다.

그러면 이제 다음과 같이 노드 명령어를 실행할 수 있는 인터렉티브 노드 터미널을 만날 수 있다.

$ docker run -it node
Welcome to Node.js v18.0.0.
Type ".help" for more information.
>

자 이제 이건 우리의 로컬에서 실행하는 것이 아니라는 것을 확인해보자.

위에 보면 노드 18버전을 사용하고 있음을 알 수 있다.

이제 ctr + c를 두 번 눌러서 컨테이너에서 나온 뒤, 로컬에서 node -v 명령어를 쳐보자.

$ node -v

Command 'node' not found, but can be installed with:

sudo apt install nodejs

아예 설치되어있지도 않다.

우리가 그 전에 본 노드는 오직 컨테이너에 설치된 것이다.

직접 이미지를 만드는 방법

두 번째로 직접 이미지를 만드는 방법을 알아보자.

직접 이미지를 만들려면 소스코드가 있는 경로로 우선 이동해야한다.

그리고 그 곳에서 Dockerfile을 만든다.

말 그대로 파일 이름을 저렇게 지정해야하며, 해당 이름을 통해 도커가 식별한다.

Dockerfile에는 이미지를 빌드할 때 실행하고 싶은 것에 대한 것들이 들어가 있어야 한다.

자 이제 Dockerfile에서 사용하는 몇가지 명령어에 대해 알아보자.

  1. FROM
FROM node

이렇게 적으면, 우리는 도커파일에 '우선적으로 node 이미지를 가져와줘' 라고 선언한 것이다.

애플리케이션을 실행할 때, 필요한 이미지가 있다면 외부에서 이렇게 갖고 오면 된다.

  1. COPY
COPY . .

COPY 다음에 오는 파라미터는 컨테이너 외부 경로(source), 그 다음은 컨테이너 내부 경로(target)이다.

위처럼 . 으로 쓰면 도커에게 기본적으로 Dockerfile이 포함된 동일한 폴더라는 것을 알려주는 것이지만, 이에 대해 Dockerfile은 자동으로 제외된다.

이미지를 기반으로 생성된 모든 컨테이너에는 자체 내부 파일 시스템이 있는데,

뒤에 찍은 .은 해당 내부 파일시스템의 root 경로를 의미한다.

보통은 따로 지정한 경로에 저장하는 것이 좋기에 우리도 아래 처럼 바꾼다.

COPY . /app

해당 컨테이너 내부 파일시스템에는 /app과 같은 경로는 알아서 생성된다.

  1. RUN
RUN npm install

우리가 작성한 node 애플리케이션의 모든 종속성을 설치하기 위해서는 npm install이라는 명령어가 필요하다.

그렇기에 우리도 내부 컨테이너에서 실행이 필요한데, 그때 사용하는 명령어가 RUN이다.

해당 명령어 뒤에 우리가 실행해야 하는 명령어를 추가로 입력해주면 된다.

  1. WORKDIR

그러나 위와 같은 RUN은 따로 어딘가 경로를 지정해주지 않으면 root 경로에서 작동하기에, 우리처럼 따로 경로를 지정해서 파일을 복사해뒀다면 다음과 같이 우리가 어디서 해당 명령어를 사용할지도 정해줘야 한다.

그럴때 사용하는 명령어가 바로 WORKDIR이다. 사용법은 다음과 같이 사용하면 된다.

WORKDIR /app

이 명령어를 쓰면 도커는 이후의 모든 명령어가 여기서 실행된다고 인식한다.

그렇기에 이 명령어를 COPY 앞에 써준다면, COPY에서 ./과 같이 현재 작업 디렉토리를 의미하는 명령어를 써주면, 알아서 /app으로 복사할 것이다.

WORKDIR /app

COPY . ./ <- /app 하단으로 복사.

이에 대해서는 /app으로 써도 되고 ./ 으로 써도 된다.

이래도 되고 저래도 되니 본인 편한대로 합시다.

개인적으로는 명시해주는게 좋아서 /app을 사용한다.

  1. CMD

마지막으로 모든 설정 작업이 끝났으면 내부에서 애플리케이션을 실행하면 된다.

이때도 위에서 배웠던 RUN을 쓸까?

사실 써도 동작은 하겠지만 이건 틀린 사용법이다.

RUN은 이미지가 빌드될 때 실행되기 때문에 이 명령어를 통해서 컨테이너 내부에서 애플리케이션을 실행하는 명령어를 실행하기에는 옳지 않다.

우리가 원하는 것은 이미지를 실행하는 것이 아니라, 이미지를 기반으로 컨테이너를 실행하는 것이라고 할 수 있다.

그렇기에 우리는 이미지를 빌드할 때 애플리케이션을 실행 하는게 아니라, 이미지를 먼저 빌드하고, 그 후에 빌드 된 이미지를 통해서 컨테이너를 실행하고, 그 뒤에 명령어를 실행 해야하는 것이다.

이렇게 해야 동일한 이미지를 통해서 여러 개의 컨테이너를 실행하고, 그 안에서 여러개의 동일한 애플리케이션을 실행할 수 있다.

(잘 이해가 안가면 뒤에 실습을 하며 이해를 해보자.)

이러한 상황을 위해서는 CMD 명령어를 사용하면 된다.

RUN과의 차이점은 이미지가 생성될 때 실행되지 않고, 빌드된 이미지를 기반으로 컨테이너를 실행할 때 실행된다는 점이다.

CMDRUN과 사용하는 방법이 다르다.

CMD ["command", "parameter1", "parameter2"]

아래처럼 배열로 명령어를 전달하며, 순서대로 명령어 - 파라미터1 - 파라미터2 - ... 이다

  1. EXPOSE

컨테이너는 호스트 머신과 환경이 따로 격리되어있다.

그렇기에 컨테이너 내부의 애플리케이션이 지정된 포트로 통신하도록 지정되어있어도, 그 포트로는 우리의 호스트머신에서 통신할 수 없다.

그렇기에 호스트머신의 로컬 시스템에서 특정 포트를 노출시킬 수 있도록 연결해줘야 하는데, Dockerfile에서 그것에 대해 명시하는 명령어는 EXPOSE이다.

사용방법은 다음과 같다.

EXPOSE 지정할 포트 번호

이렇게 해서 포트를 지정해주면 우리는 컨테이너 내의 프로세스에서 해당 포트를 노출할 것임을 명시하는 것이다.

명시만 해서 뭐하냐고? 명시를 하는게 '모범적인 사용방법'이다.

이미지 빌드

자 이제 이미지를 빌드해보자.

우선 생성한 Dockerfile이 있는 경로로 이동한 뒤, 해당 경로에서 다음과 같이 명령어를 입력해준다.

docker build .

마지막 .은 빌드하고 싶은 Dockerfile이 같은 경로에 있음을 알려준다.

다른 경로에 있다면 다른 경로를 입력해주면 된다.

이러저러한 명령어들이 지나간 뒤, 다음 명령어를 입력해서 이미지가 제대로 만들어졌는지 확인해보자.

$ docker images

그럼 다음과 같이 이미지가 생성된 것을 확인해볼 수 있다.

REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    c411c7a9af12   3 minutes ago   1GB

빌드 된 이미지를 통한 컨테이너 실행

그 다음 해당 이미지를 기반으로 컨테이너를 실행해보자.

docker run c411c7a9af12

컨테이너가 실행되면, 우리가 아까 명시한 80포트로 접속해보자.

(실제 내부 애플리케이션에서도 80포트를 물고 실행된다.)

잘 되는가? 분명 접속이 잘 안될 것이다.

왜일까?

따로 터미널을 열고 다음과 같이 명령어를 입력해보자

$ docker ps

위의 명령어는 실행중인 컨테이너들만 보여준다.

CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS     NAMES
0df8884dbce3   c411c7a9af12   "docker-entrypoint.s…"   About a minute ago   Up About a minute   80/tcp    reverent_meitner

우리가 방금 실행시킨 것도 잘 돌아가고 있다.

80포트도 잘 물고 올라간 것 같다.

해당 컨테이너를 우선 종료해보자.

$ docker stop reverent_meitner

자동으로 할당된 이름을 파라미터로 넣어주면 잘 종료될 것이다.

이제 아래 명령어로 한번 더 확인해보자.

$ docker ps -a

CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                        PORTS     NAMES
0df8884dbce3   c411c7a9af12   "docker-entrypoint.s…"   7 minutes ago   Exited (137) 29 seconds ago             reverent_meitner

STATUS가 Exited인걸 보면 잘 종료된 것 같다.

자, 돌아가서 왜 80포트로 접속할 수 없었을까?

그 이유는 EXPOSE라고 적어만 두면 실제로 컨테이너에서 로컬 호스트로 오픈되거나 하지 않기 때문이다.

그렇기에 위에서 EXPOSE에 대해서 계속 명시한다고 설명했다.

이렇게 명시한 포트는 docker run -p 명령을 통해서만 호스트로 열 수 있다.

명시는 필수가 아니지만, EXPOSE를 통해 명시하는 것이 모범적인 사용방법이다.

실제로 포트를 열고 싶다면 이미지 이름 앞에 -p 외부노출포트 : 내부노출포트 로 옵션을 주면 된다.

정확하게는 내부 컨테이너에 액세스하기위한 로컬 시스템 포트 : 내부 도커 컨테이너 포트 라고 알아두면 된다.

만약에 호스트에서 3000 포트로 내부 컨테이너의 80포트로 접속하고 싶으면 아래처럼 옵션을 주면 된다.

$ docker run -p 3000:80 이미지이름

자 그럼 위의 명령어로 아까의 이미지를 그대로 실행하고, 접속해보면?

원하는 애플리케이션에 접속할 수 있음을 알 수 있다.

docker ps 명령어로 확인해보자.

$ docker ps

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                  NAMES
82f269c5eefb   c411c7a9af12   "docker-entrypoint.s…"   36 seconds ago   Up 35 seconds   0.0.0.0:3000->80/tcp   beautiful_wing

PORTS에 0.0.0.0:3000->80/tcp 에서 확인할 수 있듯, 로컬 호스트의 3000포트에서 내부 컨테이너의 80포트로 연결되었음을 알 수 있다.

profile
개발하는 중국학과 사람

0개의 댓글