Search
🌍

bc6__2. title: 도커파일의 CMD 는 선택 인자(Optional argument)같이 작동하지만 엄연히 다르다. 컨테이너에서 실행할 스크립트의 옵션 선택권을 부여해야 한다면 -it 을 사용하라. 모범 사례(‣)가 있다.

생성
🚀 prev note
🚀 next note
14 more properties
도커파일의 CMD 명령어 라인은 docker run 시에 덮어쓸 수 있다는 특징을 가졌기 때문에(from1) 자칫 optional argument 처럼 사용하기 쉽다. 하지만 이것은 좋은 생각이 아니다. 다음과 같은 상황을 살펴보자.
아래는 main.py 스크립트의 일부이다. 도커 컨테이너는 실행 시 이 파일을 실행하도록 dockerfile 에 ENTRYPOINT ["python3", "main.py"] 와 같이 스립팅되어 있다.
# main.py import argparse parser = argparse.ArgumentParser() parser.add_argument('--data-dir', type=str, default='/tmp/mnist_data') parser.add_argument('--epochs', type=int) parser.add_argument('--lr', type=float, default=0.001) parser.add_argument('--use-cuda', action='store_true', default=False) args = parser.parse_args() print(args.data_dir) print(args.epochs) print(args.lr) print(args.use_cuda)
Python
복사
위와 같이 argparse 등 명령줄 파싱 라이브러리를 이용해 명령행 인자를 입력받는 형태의 프로그램은 수많은 오픈소스에서 쉽게 찾아볼 수 있다. 위 코드에서 볼 수 있다시피 우리가 사용하려는 스크립트에는 한 개의 필수 인자(--epochs)와 세 개의 선택 인자가 존재한다. 우리의 직관에 따르면 dockerfile 에는 다음과 같은 형태로 CMD 명령어를 사용하게 된다. 아래 CMD 명령어에는 --data-dir 의 기본값인 /tmp/mnist_data 대신 /home/janghoo/dev/data/coco2017/train 를 사용했고, --lr 의 기본값인 0.001 대신 0.005 를 사용했다는 사실에 주목해 보자.
# Dockerfile ENTRYPOINT ["python3", "main.py"] CMD ["--data-dir", "/home/janghoo/dev/data/coco2017/train", "--epochs", "100", "--lr", "0.005", "--use-cuda"]
Docker
복사
# script docker run my_container:v0
Bash
복사
빌드를 마치고 컨테이너를 실행하면 다음과 같은 출력을 얻을 수 있다.
/home/janghoo/dev/data/coco2017/train 100 0.005 True
Plain Text
복사
여기까지는 아무 문제가 없어 보인다. 다음으로 CMD 의 특징을 이용해 --epochs 인자로 통과시키는 값만을 10000으로 변경해서 실행해 보자.
# script docker run my_container:v0 --epochs 10000
Bash
복사
그 결과는 아래와 같다.
/tmp/mnist_data 10000 0.001 False
Docker
복사
CMD 는 어떤 것이 인자 이름이고 어떤 것이 인자 값인지를 구분해낼 수 있는 능력이 없다. 그래서 docker run 을 실행할 때 명령줄을 통해 새로운 값이 들어오는 순간 CMD 의 값을 전부 다 덮어써버리기 때문이다.

조금 더 나은 방법: ENV

이러한 부수현상을 회피하기 위해 환경 변수를 사용하기도 한다(참고1,2). 도커파일에서 ENV 명령어를 이용하면 컨테이너에서 사용할 환경 변수를 등록할 수 있다. 이때 ENV 명령어도 CMD 명령어와 같이 docker run 을 실행할 때 새로운 값으로 덮어쓸 수 있기 때문에 선택적 인자(Optional argument)처럼 사용할 수 있게 된다.
# Dockerfile # 띄어쓰기에 주의한다. ENV PYAPP_DATA_DIR="/home/janghoo/dev/data/coco2017/train" \ PYAPP_EPOCHS=100 \ PYAPP_LR=0.005 \ PYAPP_USE_CUDA=1 ENTRYPOINT ["python3", "main.py"]
Docker
복사
위와 같이 도커파일에서 환경 변수를 등록하고, 별다른 CMD 명령을 작성하지 않는다. 그리고 파이썬 스크립트에서는 argparse 를 사용하는 대신 환경 변수를 바탕으로 값을 읽어올 수 있도록 코드를 작성한다.
# main.py import argparse env_data_dir = os.getenv("PYAPP_DATA_DIR", "/home/janghoo/dev/data/coco2017/train") env_epochs = os.getenv("PYAPP_EPOCHS", 100) env_lr = os.getenv("PYAPP_LR", 0.001) env_use_cuda = bool(int(os.getenv("PYAPP_USE_CUDA", 0))) parser = argparse.ArgumentParser() parser.add_argument('--data-dir', type=str, default=env_data_dir) parser.add_argument('--epochs', type=int, default=env_epochs) parser.add_argument('--lr', type=float, default=env_lr) parser.add_argument('--use-cuda', action='store_true', default=env_use_cuda) args = parser.parse_args() print(args.data_dir) print(args.epochs) print(args.lr) print(args.use_cuda)
Python
복사
# script docker run my_container:v0
Bash
복사
실행 결과는 다음과 같다.
/home/janghoo/dev/data/coco2017/train 100 0.005 True
Plain Text
복사
인자를 변경하고 싶은 경우 다음과 같이 실행한다.
docker run -e PYAPP_EPOCHS=10000 my_container:v0
Bash
복사
실행 결과는 다음과 같다.
/home/janghoo/dev/data/coco2017/train 10000 0.005 True
Bash
복사
하지만 이렇게 환경변수를 이용하는 경우 다음과 같은 문제들이 나타날 수 있다.
1.
환경변수가 많아지는 경우 도커파일과 파이썬 파일에 중복이 생길뿐 아니라 길고 장황해진다.
2.
파이썬 스크립트를 도커와 떨어뜨려 독립적으로 실행하기 불편하다.
3.
도커파일에서 등록하는 환경 변수들끼리는 물론 시스템 환경 변수와도 서로 충돌할 수 있다.
4.
모든 인자에 default 값이 지정됨에 따라 선택 인자와 필수 인자의 경계가 모호해진다.
5.
action=’store_true’ 와 같은 부울타입 인자 핸들링이 까다롭다.
환경변수를 파일로부터 가져오는 방법(--env-file 옵션)을 이용해도 모든 문제가 해결되지는 않는다.

가장 나은 방법: -i, --it

마지막 방법은 docker run 의 -i-it 옵션을 사용하는 것이다(참고2). 나는 이것이 모범 사례라고 생각한다. 이들 옵션에 대한 구체적인 내용은 잠시 제껴 두고, 이를 이용하면 터미널에서 컨테이너의 터미널로 직접 접근할 수 있게 된다. 즉, 우리는 컨테이너의 터미널에서 직접 main.py 를 실행할 수 있게 된다. 도커파일의 ENV, ENTRYPOINT, CMD 모두 다 필요가 없어지고, main.py 의 환경변수와 관련된 코드들도 모두 제거할 수 있다.
다음과 같이 컨테이너를 실행하고 main.py 를 실행한다.
# script docker run -i -it my_container:v0 # 컨테이너의 터미널 python3 main.py --epochs 10000
Bash
복사
parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료들.
1.
None
from : 과거의 어떤 생각이 이 생각을 만들었는가?
supplementary : 어떤 새로운 생각이 이 문서에 작성된 생각을 뒷받침하는가?
1.
None
opposite : 어떤 새로운 생각이 이 문서에 작성된 생각과 대조되는가?
1.
None
to : 이 문서에 작성된 생각이 어떤 생각으로 발전되고 이어지는가?
참고 : 레퍼런스