Docker로 개발 환경 구축하기 (2)

Docker로 개발 환경 구축하기 (2)

그 전 글은 여기에서 볼 수 있다.

이제 우리는 Docker 이미지를 다룰 수 있다. 이미지를 다루는 방법을 잘 모르는 상태라면 이전 글을 읽고 오는 편이 좋지만, 알고 있다면 더 준비할 내용 없이 바로 이 글을 읽어도 좋을 것 같다. 컨테이너를 다루는 방법은 이미지를 다루는 방법에 비해서 짧고 간단하다.

Docker 컨테이너

짧게 컨테이너 명령어를 위한 기본 지식을 살펴본 다음 명령어들을 확인해보자.

  • 컨테이너를 사용해서 실행되는 앱을 객체화 한 것처럼 실행할 수 있다.
  • 컨테이너는 다음과 같은 상태로 나뉜다.
    1. 실행 중
    2. 정지
    3. 파기

위 주기는 용어로서 사용 된 건 아니고 그냥 말 그대로 이해하면 된다. 실행 중은 도커 컨테이너 안에서 프로세스가 동작하고 있는 상태이고, 정지는 컨테이너는 남아있지만 프로세스가 종료된 상태, 파기는 컨테이너가 Terminate 되는 것을 의미한다.

docker container ls

기본적으로는 현재 실행 중인 컨테이너의 목록을 보여준다. -a옵션을 붙이는 것을 통해서 현재 정지 상태에 있는 컨테이너까지 모두 볼 수 있다.

docker container run

docker container run으로 이미지에서 컨테이너를 만들고 CMD 명령어를 실행시킬 수 있다. 명령어는 다음과 같이 쓴다.

1
2
docker container run [옵션] 이미지명:태그 [명령] [명령 파라미터] or
docker container run [옵션] 이미지ID [명령] [명령 파라미터]

옵션과 명령, 명령 파라미터 부분은 모두 생략이 가능하다. 명령, 명령 파라미터는 Dockerfile에서 정의했던 CMD 인스트럭션 부분을 오버라이드 할 수 있다.

1
2
3
4
5
6
$ docker container run changhoi/hello-world-docker

> hello-world-docker@1.0.0 start /
> node index

Hello world Docker!

주로 사용하는 옵션들은 아래와 같다.

  • -p: 포트포워딩을 정의한다. 컨테이너의 포트와 호스트의 포트를 연결하는 옵션이다. 이에 대한 설명은 아래 잠깐 더 나와있다.
  • -d: 백그라운드에서 실행하도록 하는 옵션이다.
  • --name 컨테이너명: 실행되는 컨테이너에 원하는 이름을 붙일 수 있다. 이후 도커 명령을 사용할 일이 있을 때 컨테이너를 특정하기 쉬워진다.
  • -i: 컨테이너의 표준 입력을 호스트와 연결한다.
  • -t: 터미널 기능을 활성화 한다.
  • --rm: 컨테이너가 종료될 때 컨테이너가 삭제되도록 한다.
  • -v: 호스트와 컨테이너간 디렉토리, 파일을 공유하기 위해 사용하는 옵션이다. (갓도우는 무슨 설정을 해줘야 사용 가능하다.)

포트포워딩을 설명하자면, 일단 도커 컨테이너는 마치 하나의 게스트 OS처럼 자체 포트를 가지고 있다. 따라서 도커 안에 올라간 앱이 만약 3000번 포트를 listen하는 서버라고 치면 localhost:3000에서는 접근할 수 없다는 뜻이다. 그래서 도커 컨테이너의 3000번 포트를 로컬 호스트의 3000번 포트와 연결하겠다고 옵션을 만들어줘야 위 주소로 접근이 가능한 것이다.

-p옵션은 다음과 같이 사용할 수 있다. -p 로컬의포트:도커포트

1
2
3
4
5
6
$ docker container run -p 8000:3000 changhoi/hands-on

> hello-world-docker@1.0.0 start /
> node index

Server is on 3000

위에 작동 중인 Docker는 3000번 포트를 사용하지만, 로컬 호스트의 8000번 포트와 붙어있는 것이기 때문에 localhost:8000에서 확인할 수 있다.

1
2
curl localhost:8000
<h1>Hello world docker!!!</h1>%

여러 글을 확인 해본 결과 -i 옵션과 -t 옵션은 보통 같이 쓰이는 경우가 많다. 다음 파이썬 프로그램을 도커에 올렸다고 치자. 그 아래는 Dockerfile을 정의한 모습이다.

1
2
3
4
5
6
7
8
# cal.py
def add_ten(num):
return num + 10

val = int(input("enter number: "))
output = add_ten(val);

print(output);
1
2
3
4
5
FROM python:latest

COPY . .

CMD ["python3", "cal.py"]

이제 -it 옵션인 경우, -i옵션, -t옵션을 각각 쓴 경우, 안 쓴 경우를 비교 해보자. 순서는 안 쓴 경우, -i, -t, -it 순서이다.

1
2
3
4
5
6
7
8
$ docker container run changhoi/py-cal

enter number: Traceback (most recent call last):
File "cal.py", line 4, in <module>
val = int(input("enter number: "))
EOFError: EOF when reading a line

# EOFError 에러가 난 모습
1
2
3
4
5
6
$ docker container run -i changhoi/py-cal

enter number: 10
20

# 정상 작동
1
2
3
4
5
$ docker container run -t changhoi/py-cal

enter number: 10

# 컨테이너쪽 터미널에 입력이 전달되지 않는다.
1
2
3
4
5
6
$ docker container run -it changhoi/py-cal

enter number: 10
20

# 정상 작동

실험을 통해서, -t 옵션을 써야 터미널을 활용 할 수 있는 상태가 되는 것 같고, 터미널에서 사용자 I/O가 필요한 경우에 -i 옵션을 줘야 한다는 것을 알았다. 터미널에서 사용자 입력을 하고 싶으면 -it를 사용하면 된다.


위 실험을 해보는 동안 정지 상태의 컨테이너가 많이 생겼다. 이런 일회성 컨테이너는 일일히 삭제를 해줘야 하고, 만약 --name 옵션을 준 명령어를 사용한 다음, 컨테이너를 삭제하지 않고 또 다시 실행하는 경우에는 도커는 에러를 발생시킨다. (이미 존재하는 컨테이너라고) 이런 경우 --rm 명령어를 추가해서 간단하게 바로 삭제가 가능하다. 아래 예시에서도 --rm을 붙이면 docker container ls -a에서 나타나지 않는 것을 알 수 있다.

1
2
3
4
5
6
7
8
9
10
11
$ docker container run --name unuse-rm node
$ docker container ls -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b2ed686f960e node "docker-entrypoint.s…" 9 seconds ago Exited (0) 8 seconds ago unuse-rm

$ docker container run --name use-rm --rm node
$ docker container ls -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b2ed686f960e node "docker-entrypoint.s…" 2 minutes ago Exited (0) 2 minutes ago unuse-rm

-v 옵션은 로컬의 파일시스템을 컨테이너와 공유할 수 있게 해주는 옵션이다. -v 로컬디렉토리:컨테이너디렉토리 형태로 작성한다. 아래 명령어로 현재 위치의 파일들을 컨테이너와 공유할 수 있다.

1
$ docker container run -v `{PWD}`:/app

docker container stop

실행 중인 상태의 도커를 정지 상태로 만든다. docker container stop 컨테이너명 or 컨테이너ID를 사용해서 정지할 수 있다.

docker container restart

실행 중인 상태의 도커를 재시작한다. docker container restart 컨테이너명 or 컨테이너ID를 사용해서 재시작할 수 있다.

docker container rm

정지 상태의 컨테이너를 파기할 수 있다. docker container rm 컨테이너명 or 컨테이너ID를 사용해 파기할 수 있다. 일반적으로는 컨테이너가 정지 상태여야 파기가 가능하지만, -f옵션을 주면 실행 중인 컨테이너를 정지하고 파기할 수 있다.

docker container exec

실행 중인 컨테이너에 명령을 실행할 수 있다. docker container exec [옵션] [컨테이너명 or 컨테이너ID] [실행할명령] 형태로 사용한다.

기타 알아보면 좋을 것들

  • docker container cp: 실행 중인 컨테이너에 파일을 복사해 넣을 때 사용한다.
  • docker container log: 표준 입출력으로 나타나는 로그들을 볼 수 있다. 파일에 저장되는 것은 볼 수 없다.
  • docker container prune: 정지 상태인 도커 컨테이너를 모두 삭제해준다.

마치며

컨테이너 명령어는 이미지보다 간단하게 정리했다. 이제 컨테이너들을 사용해서 개발 환경을 구축하다가 삽질 한 내용을 올려볼까 했는데 사실 그 삽질기는 여기 REF에서 더 자세하게 확인할 수 있다. 결과적으로 팀원들과 컨테이너 개발 환경을 구축했는데 개발 환경을 구축하는 것은 Docker compose를 사용해 여러 컨테이너를 활용한 로컬 개발 환경을 구축한 것이었다. 해당 결과는 이 링크에서 볼 수 있다. 해당 글은 이 시리즈처럼 자세한 이론은 설명하지 않았다. 결과적으로 어떻게 만들어내는 건지 궁금하다면 바로 링크를 확인하면 될 것 같다. 컴포즈에 대한 글은 다음 글에서 다뤄볼 생각인데, 꼼꼼한 설명보다는 필요한 내용과 어디서 어떻게 찾아볼 수 있는지에 대한 내용이 될 것 같다.

Reference

댓글

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×