Docker run & start (with volume)

EBAB!·2023년 7월 13일
0

Docker

목록 보기
8/15

생성된 이미지로 컨테이너를 실행할 수 있습니다.

$ docker run [image_ID or image_name]

여기에 이제 여러가지 옵션을 줄 수 있습니다. 그 전에 start 명령어를 살펴본 뒤 옵션에 대해 설명해보겠습니다.


docker start

옵션을 주기전에 start 명령어를 보겠습니다.

짚고 넘어가야 하는 점은 run은 컨테이너 생성, start는 중지된 컨테이너를 재시작하는 역할입니다. 다만 둘이 같지 않은 점이 중요하기에 짚고 넘어가는 내용입니다.

docker run/start 둘 다 컨테이너를 실행하고 명령어 옵션이 거의 비슷하지만, run은 foreground에서 실행시키고 start는 background에서 실행됩니다.
좀 더 쉽게 말하면 run으로 실행 시, 실행되는 동안 터미널을 사용할 수 없고 start는 백그라운드 실행이므로 터미널을 사용할 수 있습니다.

포어그라운드에서 실행되는 상태를 attached 모드, 백그라운에서 실행되는 경우를 detached 모드입니다.

  • attached 모드가 가지는 의미는 컨테이너의 출력 결과를 수신하는 것을 의미합니다.
  • detached 모드는 말 그대로 백그라운드에서 실행시키는 것을 목표로 하고, 컨테이너의 출력 결과를 볼 수 없습니다.
    • 하지만 docker logs [container_name]을 통해 출력 결과를 가져올 수 있습니다. -f 옵션을 통해 지속적으로 수신 대기 상태로 볼 수도 있습니다.

따라서 run/start의 디폴트 상태를 알고, 상황에 맞게 옵션을 주어 포어그라운드로 할 지 백그라운드로 할 지 설정해줄수 있어야 합니다.

  • 백그라운드 : -d , 포어그라운드 : -a

컨테이너 포트 매핑

우선, 앞서 Dockerfile에서 작성한 EXPOSE 80은 문서화의 목적이지 실제 포트를 열지 않는다고 했습니다. 그래서 옵션을 통해 포트를 열어 줍니다.

# docker run [image_ID or image_name]

$ docker run -p 3000:80 [image_ID]

-p [App_port:container_port] 설정을 통해 프로세스와 내부 컨테이너의 포트를 노출합니다.
컨테이너는 외부와 격리된 공간이므로 외부에서의 포트번호는 내부에서 무의미합니다. 그러므로 외부에서 컨테이너로 접근하는 포트번호(예시에선 3000), 컨테이너 내부에서 받아들이는 포트번호(예시에선 80)이 됩니다.
즉, 컨테이너 내부의 웹 서버 포트번호가 80이라면, 컨테이너로 접근하기 위해 3000 포트로 접근하면 자동으로 컨테이너에서 80으로 받아들이게 됩니다.

  • 추가적으로 -p 3000:80/tcp와 같이 프로토콜 설정도 가능합니다.
  • 문서화하는 이유는 좀 전에 나왔듯이, 컨테이너의 포트번호는 외부에서 결정되지만 내부 포트는 컨테이너 생성 시 결정할 수 있습니다. 따라서 문서화된 포트번호가 있어야 내부 포트번호가 올바르게 매핑되어 통신에 문제가 없게됩니다.

컨테이너 interactive 모드

예를 들어 컨테이너에서 실행된 파이썬 앱을 통해 계산을 물을 때, 파이썬처럼 컨테이와 interactive하게 소통할 수 있는 방법입니다.
두 가지 옵션을 추가해줍니다.

  • -i : interactive 모드로 시작하게 해줍니다.
  • -t : pesudo TTY가 할당됩니다(터미널을 생성).

이 두 가지를 따로 쓰지않고 -it로 옵션을 줘서 실행할 수 있습니다.

$ docker run -it [image_ID]

만약 docker start로 실행한다면 start는 기본적으로 detached 모드기 때문에 -a 옵션을 주고 -i옵션을 주면 됩니다. docker start --help에서 볼 수 있듯이 -t 옵션이 따로 없으므로 두 가지만 주면 시작될 수 있습니다.

$ docker start -a -i [image_ID]

중지된 컨테이너 자동 삭제

docker stop [container_id]를 통해 컨테이너를 중지시키면, 컨테이너가 삭제되지 않고 중지 상태에서 머무릅니다. 중지와 삭제를 반복하지 않고 중지되는 순간 바로 삭제되도록 명령어를 설정해줄 수 있습니다.

# --rm : 중지시 삭제

$ docker run -d -p 3000:80 --rm [image_ID]:[tag]

볼륨 설정

컨테이너로 실행된 앱과 호스트는 격리되어 있습니다. 이 때 저장소에 대한 문제가 생깁니다.

기본적으로 컨테이너 내부에 저장되는 데이터는 컨테이너가 중지될 때는 보존되지만 삭제될 때 사라집니다. 만약 --rm 옵션을 사용한다면 데이터가 쉽게 사라지게 될 수 있습니다.

이런 상황을 막기위해 볼륨을 사용합니다.
볼륨은 호스트의 폴더입니다. 호스트에 장착된 하드 드라이브의 저장소를 컨테이너로 매핑하는 것을 의미합니다.

만약 우리가 볼륨을 추가해주는 경우 컨테이너가 제거되어도 해당 볼륨이 유지되므로 볼륨의 데이터 또한 유지됩니다.

볼륨에는 두 가지 종류가 있습니다.

  • Anonymous volume(익명 볼륨)
    • 볼륨을 설정했지만 이름을 붙이지 않은 경우 임의로 부여되는 이름을 가진 볼륨.
    • 컨테이너 삭제 시 함께 사라진다.
  • named volume(명명된 볼륨)
    • 볼륨 설정 시 볼륨의 이름을 명명해준 볼륨
    • 컨테이너가 삭제되어도 사라지지 않는다.
# -v [volume_name]:[dest_in_container]

$ docker run -d -p 3000:80 --rm -v [volume_name]:[dest_in_container] [image_ID]:[tag]

이런 방식으로 볼륨을 새롭게 생성, 저장하고 다른 컨테이너에서도 사용될 수 있도록 볼륨을 설정할 수 있습니다.

  • 이름을 명명해주냐에 따라 익명,명명된 볼륨의 차이가 생깁니다.
  • 참고로 컨테이너 자동 삭제를 해주지 않았다면 익명볼륨이 자동삭제 되지 않으므로 쌓일 수 있습니다.
  • Dockerfile에 작성하냐 명령어로 실행하냐의 차이는 고정된 설정(Dockerfile)을 사용할 것인지 유연하게 사용(명령어)할 것인지 판단해서 사용합니다.

바인드 마운트 (코드 공유)

호스트에서의 코드 변화를 컨테이너에서 바로 공유하기 위한 설정입니다. 개발 도중에 도커를 사용한다면 이런 설정이 매우 중요합니다.

개념은 볼륨과 비슷하지만 중요한 차이점은, 볼륨의 경우 할당된 저장공간이 어디인지 알 수 없지만 바인드 마운트의 경우 설정을 통해 확실히 알 수 있다는 점입니다.

이렇게 바인드 마운트를 해주면 컨테이너는 스냅샷에서 소스 코드를 복사하는 것이 아닌 바인드 마운트에서 복사합니다.

그리고 볼륨과는 달리 컨테이너 내부만의 스케일이 아니므로 Dockerfile에서는 설정할 수 없고 docker 명령어로만 설정이 가능합니다.

명령어 옵션은 볼륨과 같지만 작성이 다릅니다.

# -v "[마운트 위치 절대경로]:[dest_in_container]"

$ docker run -d -p 3000:80 --rm -v [volume_name]:[dest_in_container] -v "[마운트 위치 절대경로]:[dest_in_container]" [image_ID]:[tag]

-v 명령어의 :의 앞 부분이 경로가 오면 바인딩마운트, 이름이 오면 명명된 볼륨, 없다면 익명볼륨이 됩니다

  • 절대경로는 파일과 폴더 둘 다 가능합니다. 단 컨테이너에 매핑될 경로도 파일이면 파일, 폴더면 폴더로 맞게 설정해야 합니다.
  • -v이후 전체를 큰 따옴표로 묶어서 공배이나 특수문자가 깨지지 않도록 해줍니다.

바인딩 마운트로 공유 시 덮어쓰지 않는지 확인하기

만약 우리의 폴더를 컨테이너 내부의 다른 폴더로 바인딩 마운트를 한다면 해당 폴더의 내용 전체를 덮어쓰게 됩니다.

은근 치명적인게, 코드 공유를 위해 사용한다고 하면 당연히 같은 디렉토리의 여러 파일들이 컨테이너 생성 전에 환경을 위해 복사된 상태일 것입니다.

때문에 코드 공유를 위해 바로 폴더끼리 바인딩 마운트를 한다면 덮어씌여지면서 컨테이너 생성 시 만들어둔 파일들이 사라질 위험이 있습니다.

해결방안

여러 가지가 있겠지만 만약 사라질 파일들이 파악이 되는 상황이라면 그 파일들만 다른 볼륨에 저장하여 익명볼륨으로 설정해둘 수 있습니다.

볼륨데이터 보존

마운트가 되었다면 컨테이너에서 수정한 사항이 호스트 머신에 영향을 끼칠 수 있다는 뜻입니다. 이런 양방향 관계가 아니라 호스트의 수정사항만 컨테이너에 반영하고 싶을 수 있습니다. 그럴 때 볼륨뒤에 :ro를 붙여줍니다.(ro, read-only)
디폴트는 rw입니다.

# -v "[마운트 위치 절대경로]:[dest_in_container]:ro"

$ docker run -d -p 3000:80 --rm -v [volume_name]:[dest_in_container] -v "[마운트 위치 절대경로]:[dest_in_container]:ro" [image_ID]:[tag]

환경변수 설정

Dockerfile에서도 설정이 가능하고 명령어 옵션으로도 가능합니다.
이 경우에도 고정적이냐 유동적이냐에 따라 Dockerfile에 쓸 지 명령어에 쓸 지 선택하면 되지만, 보안과 관련된 환경변수를 Dockerfile에 저장했다가 실수로 커밋이라도 하면 큰일이 나므로 보안 관련 환경 변수는 되도록 명령어 창에서 실행하는 것이 좋습니다.

# -e PORT=8000 # 환경변수로 PORT 설정
# --env-file ./.env # 환경변수 파일을 한번에 설정

$ docker run -d -p 3000:80 --rm -v [volume_name]:[dest_in_container] -v "[마운트 위치 절대경로]:[dest_in_container]:ro -e PORT=80 [image_ID]:[tag]
profile
공부!

0개의 댓글