포스트

Docker (7) - 컨테이너 생명주기와 실행 옵션 정리

Docker (7) - 컨테이너 생명주기와 실행 옵션 정리

1. 개요

Docker(2)에서 docker run으로 컨테이너를 간단히 띄워봤지만, 실제로는 컨테이너가 어떤 상태를 거쳐 살아있고 사라지는지, run의 옵션들이 각각 무엇을 하는지, 컨테이너 내부와 파일을 어떻게 주고받는지를 알아야 제대로 다룰 수 있다.

이번 글에서는 이미지와 컨테이너의 관계부터 시작해서, 컨테이너의 생명주기, docker run 옵션, 파일 공유, exec/commit까지 컨테이너를 다루는 데 필요한 명령어들을 한 번에 정리한다.


2. 컨테이너란 무엇인가

이미지는 읽기 전용의 불변 값이고, Docker 엔진은 이 이미지 위에 읽고 쓰기 가능한 레이어를 하나 더 얹어서 컨테이너를 만든다. 즉 컨테이너는 이미지의 스냅샷이라고 볼 수 있다.

컴퓨터에서 동작하는 애플리케이션이 프로세스로 실행되듯, 컨테이너는 격리된 공간에서 실행되는 프로세스에 가깝다. 이런 프로세스 격리 방식은 리눅스 파운데이션의 지원을 받는 OCI(Open Container Initiative) 표준을 따르고 있어서, Docker가 아닌 다른 런타임에서도 호환되어 동작할 수 있다.


3. 컨테이너를 수동으로 제어하기

docker run 한 번으로 컨테이너를 바로 만들고 실행할 수도 있지만, 아래처럼 생성과 실행을 단계별로 나눠서 다룰 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 이미지 다운로드
docker pull ubuntu:14.04

# 컨테이너 생성 (아직 실행 X)
docker create -it --name container-test1 ubuntu:14.04

# 실행 중인 컨테이너 확인
docker ps
# 중지된 것까지 전부 확인
docker ps -a

# 컨테이너 실행
docker start container-test1

# 컨테이너 내부로 접속
docker attach container-test1

# 컨테이너 삭제
docker rm container-test1

4. 컨테이너 생명주기: 실행 → 중지 → 파기

  • 실행: docker run이나 docker start로 지정한 이미지를 기반으로 컨테이너가 생성되면, 그 이미지를 만들 때 Dockerfile에 정의해둔 CMD/ENTRYPOINT 명령이 실행된다. 데몬처럼 계속 떠있는 컨테이너도 있고, 한 번 작업만 하고 종료되는 컨테이너도 있다.
  • 중지: 사용자가 명시적으로 멈추거나, 애플리케이션이 정상/비정상 종료되면 컨테이너는 중지 상태가 된다. 이때 직전 상태가 그대로 보존되기 때문에 다시 시작할 수 있다.
  • 파기: 중지된 컨테이너는 명시적으로 지우지 않는 한 디스크에 그대로 남아있다. 파기한 컨테이너는 다시 되돌릴 수 없으므로, 더 이상 필요 없는 컨테이너는 정리해주는 것이 좋다.

5. docker run 옵션 정리

docker rundocker image pull + docker container create + docker container start를 한 번에 처리해주는 명령이라서, 로컬에 이미지가 없으면 자동으로 다운로드까지 수행한다.

1
docker run [옵션] 이미지이름 [인자]
옵션의미
--name 이름컨테이너 이름 지정 (생략 시 임의 부여)
-p [호스트포트]:[컨테이너포트]포트포워딩. 외부에서 접근해야 하는 서비스는 필수
-P, --publish-all컨테이너가 쓰는 모든 포트를 임의 포트로 매핑
-v(--volume) 호스트경로:컨테이너경로호스트 경로를 컨테이너에 바인딩 (볼륨 마운트)
--net=네트워크이름지정한 네트워크에 연결
-i(--interactive)대화식 모드 연결
-t(--tty)터미널 할당
-d(--detach)백그라운드 실행. 데몬으로 동작시키려면 필수이며 -it와 같이 쓰는 경우가 많음
-e(--env)환경 변수 지정
--rm컨테이너 종료 시 자동 파기
--restart=[no\|on-failure\|on-failure:횟수\|always]종료 시 재시작 정책
-h 이름컨테이너 호스트 이름 지정 (미지정 시 컨테이너 ID)
--link=컨테이너ID지정한 컨테이너와 이름으로 통신 가능하도록 연결

-d는 컨테이너를 백그라운드로 돌리는 옵션이고, -i/-t는 내부 터미널에 접속하기 위한 옵션이라 역할이 다르다. -d 없이 실행하면 포그라운드로 떠서 컨테이너 사용이 끝날 때까지 다음 명령을 칠 수 없고, -i/-t 없이는 컨테이너 내부 파일 시스템에 직접 접근할 수 없다.


6. 실행 중인 컨테이너 조회 (docker ps)

항목의미
CONTAINER ID컨테이너 실행 시 부여되는 ID (원래 64자, 12자만 표시)
IMAGE사용한 이미지
COMMAND컨테이너 실행 시 수행하는 명령
CREATED생성 후 경과 시간
STATUS현재 상태 (실행 중이면 UP)
PORTS포트포워딩 결과 (호스트포트 -> 컨테이너포트, 두 포트가 같으면 뒤 번호 생략)
NAMES컨테이너 이름
옵션의미
-a중지된 것까지 전체 조회
-qID만 표시
-aq모든 컨테이너의 ID만 표시
-l가장 최근에 만든 컨테이너
-n 숫자최근 N개
-s컨테이너 크기 표시
--no-truncID를 자르지 않고 전체 표시
--filter조건 검색, 예: docker ps --filter status=running
--format출력 포맷 지정, 예: docker ps --format " "

중지와 삭제

1
2
3
4
docker stop {컨테이너ID 또는 이름}
docker stop $(docker ps -aq)

docker rm {컨테이너ID 또는 이름}

실습: nginx 컨테이너 생성

1
docker run -d --name nginx -p 8080:80 nginx:1.18

아파치는 80번 포트로 서비스하므로, 아래처럼 호스트 9001번 포트와 매핑해서 접근할 수 있다.

1
docker run --name apacheweb -d -p 9001:80 httpd

컨테이너는 기본적으로 호스트와 격리되어 있기 때문에, 외부에서 접근이 필요한 서비스는 반드시 -p 호스트포트:컨테이너포트로 포트포워딩을 열어줘야 한다.


7. 자주 쓰는 베이스 이미지

분류이미지
운영체제ubuntu, centos, debian, fedora, busybox, alpine
웹 서버httpd(apache), nginx
데이터베이스mysql, postgres, mariadb, redis, mongodb
언어 런타임openjdk, python, php, ruby
기타registry(도커 레지스트리), nextcloud(private cloud)

우분투 컨테이너 접속 예시

우분투처럼 대화식으로 다뤄야 하는 이미지는 -it 없이 -d만 주면 바로 종료돼버린다.

1
2
3
4
docker run -d --name ubuntu_server ubuntu   # 바로 종료됨

docker run -dit --name ubuntu_server ubuntu # 정상 동작
docker attach ubuntu_server

컨테이너 리소스/로그 확인

1
2
docker stats 컨테이너이름   # 리소스 사용량
docker logs -f 컨테이너이름 # 로그 확인 (시작 안 되거나 바로 종료될 때 원인 파악용)

8. 호스트와 컨테이너 간 파일 공유 (docker cp)

1
2
docker cp 호스트경로 컨테이너이름:컨테이너경로   # 호스트 -> 컨테이너
docker cp 컨테이너이름:컨테이너경로 호스트경로   # 컨테이너 -> 호스트

앞에 쓴 쪽이 원본, 뒤에 쓴 쪽이 목적지다.

아파치 메인 페이지 교체 예시 (index.html 경로: /usr/local/apache2/htdocs/index.html)

1
2
3
4
docker run --name webserver -d -p 9000:80 httpd
docker cp webserver:/usr/local/apache2/htdocs/index.html index.html
# index.html 수정 후
docker cp index.html webserver:/usr/local/apache2/htdocs/index.html

nginx 메인 페이지 교체 예시 (index.html 경로: /usr/share/nginx/html/index.html)

1
2
docker run -d --name nginx_server -p 9001:80 nginx
docker cp index.html nginx_server:/usr/share/nginx/html/index.html

9. 컨테이너에 명령 실행하기 (docker exec)

1
docker exec -it 컨테이너이름 명령

docker attach 대신 docker exec -it 컨테이너이름 /bin/bash처럼 셸 명령을 바로 실행할 수도 있다. attach는 컨테이너의 메인 프로세스에 직접 붙는 것이고, exec는 이미 실행 중인 컨테이너 안에서 별도의 명령을 하나 더 실행하는 것이라는 차이가 있다.

파이썬 스크립트 실행 예시

1
2
3
4
5
6
7
8
# 컨테이너 생성
docker run -dit --name python_test python

# 파일을 컨테이너로 복사
docker cp lotto.py python_test:/

# 컨테이너 안에서 명령 실행
docker exec -it python_test python /lotto.py

Node.js 서버 실행 예시

1
2
3
4
5
6
7
8
# 컨테이너 생성 (포트포워딩 포함)
docker run -dit -p 8000:8000 --name=nodejs node

# 파일 복사
docker cp index.js nodejs:/

# 실행
docker exec -it nodejs node /index.js

10. 컨테이너를 이미지로 만들기 (docker commit)

컨테이너 내부에서 파일을 바꾸거나 프로그램을 설치한 뒤, 그 상태를 그대로 새 이미지로 굳히고 싶을 때 쓴다.

1
docker commit 컨테이너이름 이미지이름[:태그]

11. 정리

컨테이너는 이미지 위에 쓰기 가능한 레이어를 얹은 실행 스냅샷이며, run(=pull+create+start) → 중지 → 파기의 흐름으로 생명주기가 관리된다. -d, -it, -p, -v, --rm 같은 옵션들이 각각 백그라운드 실행, 터미널 접속, 포트포워딩, 볼륨, 자동 삭제라는 서로 다른 역할을 한다는 걸 구분해두면 docker run 명령을 조합해서 쓰기가 훨씬 수월해진다.

컨테이너 안팎으로 파일을 옮길 때는 docker cp, 이미 떠있는 컨테이너에 명령을 추가로 넣고 싶을 때는 docker exec, 지금 상태를 그대로 이미지로 남기고 싶을 때는 docker commit을 쓴다는 식으로 상황별 명령을 나눠서 기억해두면 실무에서 헷갈릴 일이 줄어든다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.