Search
Duplicate

Docker 사용방법

Created
9/3/2021, 4:03:00 AM
Tags
Empty

Docker란 무엇인가?

OS에 부담을 주지 않는 매우 효율이 좋은 가상화 도구로 VM보다 훨씬 구조가 간단해서 기본 OS 99% 의 성능을 낼 수 있는 매우 유용한 가상화 시스템이다.
결론적으로 얘기하자면, 도커는 시대의 흐름이고 앞으로 모든 것이 도커를 기반으로 돌아갈 것이다.

Docker에서 쓰이는 용어

이미지: 미리 임의의 환경을 구워놓은 이미지 파일컨테이너 : 이미지를 실행하는 독립된 light VM 같은 것

Ubuntu 18.04 에 docker 설치하기

간편하게 정리된 도커 공식 설치 스크립트를 사용하기

curl https://get.docker.com | sh && sudo systemctl --now enable docker

수동으로 설치하기

sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo docker run hello-world

docker 명령어 사용시 sudo를 매번 붙이지 않게 하기

OS에서 아래의 유저 권한을 한 번 주어야한다. 방법은 아래와 같이 리눅스의 groupadd명령어로 도커를 추가해버리면 된다.
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker

Docker 사용 메뉴얼

# docker upgradesudo apt-get upgrade docker-engine
# docker 이미지들
다음의 github에는 왠만한 머신러닝 라이브러리를 전부 도커파일로 설치하게 만들어놨음. https://github.com/Kaixhin/dockerfiles
# docker 기본 명령어
docker info : docker 가 설치된 환경이나 남은 용량 등을 확인 할 수 있다.
docker -v : 버전 확인docker inspect 컨테이너ID : 해당 컨테이너의 정보 조사(label 등)
label : 도커에 일종의 태그를 커스텀하게 달 수 있는 기능. 이것을 이용해 ps 할 때 filtering을 하거나 할 수 있음.
docker -H 주소 ps : 해당 ip의 container를 확인
docker logs --tail 100 ContainerID : 해당 컨테이너의 로그 출력
# docker 이미지 다운 받기
docker pull kaixhin/cuda-theano(https://hub.docker.com/r/kaixhin/cuda-theano/ 에서 받아오는 것임)
docker pull b.gcr.io/tensorflow/tensorflow:latest-devel-gpu 을 쳐서 gpu를 지원하는 텐서플로우를 받는다
(https://www.tensorflow.org/versions/master/get_started/os_setup.html)
docker를 켰을 때 보여줄 root로 사용할 임의의 dir을 만든다.(context라고 하는듯)
그 dir안에 Dockerfile 을 만들고 입력한다.
이 도커파일을 대충 원래 설치방식대로 막하면 높은 확률로 에러가 뜸. 꼭 남이 도커파일 만들어 놓은데서 긁어올 것
같은 dir안에 .dockerignore 파일을 생성하고, 하위 이미지에서 설치하지 않을 대상을 지정한다.(정규식으로)
(https://docs.docker.com/engine/reference/builder/#dockerignore-file)
docker 이미지 만들기
docker file이 들어있는 폴더에 들어간 다음 다음을 입력한다. (sudo 계정으로 해야만 되는듯)
docker build --rm -t imcomking/bi_tensorflow -f Dockerfile_tensorflow .
docker build --rm -t imcomking/ttsk -f Dockerfile_ttsk .
(From 으로 가져올 때, pull 일일이 안해도 알아서 풀링해오네)
(.은 필수임. 이게 현재 폴더를 context로 하겠다는 뜻임)
(-f 다음에는 도커파일의 위치를 설정)
(-t 는 생성한 이미지를 저장시킬 repository 이름임)
(--rm 은 빌드하는 과정에 생긴 임시 컨테이너를 자동으로 삭제해줌)

exec를 이용해서 docker container에 프로세스 실행하기

attach로 접속시, ctrl C를 눌르면 해당 컨테이너가 종료되어버리는 경우가 많다. 따라서 아래와 같이 /bin/bash를 새로 실행시키는게 매우 안전 한 방법이다.
docker exec -it -u root docker-airflow_worker_1 /bin/bash

screen이용해서 exec로 실행한 프로세스에 다시 attach하기

원래 exec로 실행된 process에 대해서는 다시 re-attach하는 것이 불가능하다. 그러나 screen을 먼저 생성한 다음에, docker exec를 실행할 경우 이 프로세스에 다시 접근하는 것이 가능하다.
단 이렇게 할 경우, exec process를 빠져나올 때  screen 명령어를 이용해서 빠져나와야한다. 즉 ctrl pq가 아니라 ctrl ad을 써야한다.
screen -S exec$(screen -ls | grep exec | wc -l) docker exec -it containerName bash
만약 detached 상태로 실행하려면 주의해야하는데, docker exec에서 -d 옵션을 주면 안되고 screen에서 -dmS 옵션을 주어야한다.
screen -dmS juyter docker exec -it dhkwak_pytorch jupyter notebook

exec로 여러개 명령어 실행하기

docker exec -it containerName bash -c "cd /var/www && /bin/bash"

exec command를 실행할 working dir 바꾸기

docker exec -w /my/dir container_name command
음.. 여러 방법을 해보아도 /bin/bash를 실행하는 순간 무조건 home dir로 가버려서, exec를 이용해서 default dir을 바꾸려는 방법들은 소용이 없다.

exec로 실행한 docker상의 process 종료하기

docker attach 컨테이너ID
top 명령어로 pid확인
kill pid
결국 exec로 실행한 process는 docker top 명령어로 pid를 확인하여 종료할 수 있다.
그러면 해당 pid를 host에서 바로 sudo kill -9 pid하여 종료하는 게 가능하다.
# Run 명령어 예시
docker run -it --net=host --device /dev/nvidiactl --device /dev/nvidia-uvm --device /dev/nvidia0 imcomking/bi_deeplearning
docker run [OPTIONS] 이미지이름 [실행할COMMAND와 Args]
Run 옵션들
p N : N번 포트를 외부에 노출한다.(컨테이너에서 맵핑되는 내부 포트는 알아서 랜덤으로 정해짐)
p A:B : 내부 A포트를 외부 B에 노출시킨다.
it 를 해야 컨테이너가 종료안되고 정상 실행된다
e 옵션은 환경변수를 설정하는 것이다. ex) -e USER=user -e PASS=pass
v <host_dir>:<container_dir>) 이 옵션을 사용하면, container에다가 해당 host 경로를 추가시켜줄 수 있음. 즉 컨테이너에게 data 폴더를 넣어주는 역할이라고 볼 수 있음. 즉 저장공간을 컨테이너내부와 외부에 공유하는 기능이다.
-net=host : 이 옵션을 추가해야만 컨테이너에서 인터넷이 되는걸로 무언가 도커에서 패치가 된듯하다.
-device /dev/nvidiactl --device /dev/nvidia-uvm --device /dev/nvidia0 이것은 nvidia gpu를 쓰기위한 옵션이다. 이중 nvidia-uvm 모듈은 , nvidia-smi를 한 번 실행시켜주면 생긴다. 혹은 그래도 nvidia-uvm을 찾을 수 없다는 에러가 뜨면 다음을 시도해본다.
-device /dev/snd: sound play를 하기 위해서는 반드시 이 옵션을 추가해주어야한다.
sudo apt-get install nvidia-modprobesudo modprobe nvidia-uvmsudo mknod -m 666 /dev/nvidia-uvm c 250 0
-rm : 도커 컨테이너에서 컨트롤 pq 나오게 되면 자동으로 종료 하는 옵션(따라서 restart한 다음 attach해야함)
-name : 컨테이너 이름 설정. 이 명령어가 좋은 점은 같은 이름의 컨테이너를 2개 만들 수 없다는 점이다. 이를 이용해서 한 사용자가 하나의 컨테이너만 소유하게 할 수 있다. 2개를 생성하려하면 다음과 같은 에러가 뜬다. docker: Error response from daemon: Conflict. The name "/bilab" is already in use by container ~~~
-label : docker ps에서 했을 때, 특정 컨테이너만 볼 수 있게 label로 필터링하기 위해서, 컨테이너에 태깅Ex) docker ps에서 label을 이용해 필터링 하기docker ps -a --filter="label=username=$USER"
이런식으로 filter옵션을 주어 레이블링 해둔 컨테이너만 확인이 가능
-gpus all : 모든 gpu를 mount
-gpus device=0,2 : 첫번째랑 세번째 gpu를 mounthttps://docs.docker.com/engine/reference/commandline/run/#access-an-nvidia-gpu
## 이미 실행된 컨테이너에서 run command 옵션 알아내기
sudo docker ps --no-trunc
# ttsk image의 container 생성하기
docker run -it --device /dev/nvidiactl --device /dev/nvidia-uvm --device /dev/nvidia0 -p 22 -p 6006 -p 8888 imcomking/ttsk
# Container volume mount된 host 경로 알아내기sudo docker inspect -f '{{ .Mounts }}' containerID
# Container start command 알아내기sudo docker inspect -f '{{ .Config.Cmd }}' containerID
# Container NetworkMode 알아내기sudo docker inspect -f '{{ .HostConfig.NetworkMode }}' containerID
# 다른 Process를 돌리고 있는 container에 bash접속하기sudo docker exec -it containerIDbash
docker push 로 docker hub에 올리기
요약하면 먼저 https://hub.docker.com/ 에 회원 가입을 한 다음, repository 새로 만든 뒤 리눅스 커멘드에서 다음을 입력
docker login(아이디, 비번, 이메일 입력)docker push 아이디/이미지이름
이때 반드시 docker hub의 repository에 작성한 이름, 내 로컬 이미지의 이름만은 반드시 같아야한다.
또한 docker image의 이름 역시 hubid/repo이름 이렇게 정해져야한다.
이미지에 태그를 달기
docker tag 345d03e79a10 imcomking/allinone:devel
이미지 이름 변경하기
docker image tag 원래이름 새로운이름
Container 이름 변경하기
docker rename my_container my_new_container
-------- 기타 docker 명령어 ---------
docker search tensorflow : git에 등록된 tensorflow docker검색
docker images : 이미지 리스트 확인
docker rmi 이미지이름: 이미지 삭제하기 (-f를 붙이면 강제 삭제)
docker ps -a : 실행중인 컨테이너(도커 instance = 프로세스) 확인
docker rm 컨테이너이름: 컨테이너 삭제
docker rm `docker ps -aq` : 모든 컨테이너 삭제 (-f를 붙이면 강제 삭제, 작은따옴표 아님. 물결표시아래에 있는 ` 이거임)
docker restart 컨테이너이름: 재시작
docker attach 컨테이너이름: 재접속
attch한 컨테이너에서 detach 하는 방법: Ctrl +PQ
docker start 컨테이너이름: 프로세스 시작
docker stop 컨테이너이름: 프로세스 정지
exit : 도커 나가기, 단 백그라운드에 뭐가 돌고있으면 안됨.
screen 만들 때 반드시 환경변수가 잘 설정되도록 해야함. 아래 처럼생성
screen -S ipython -s -/bin/bash
http://superuser.com/questions/114264/gnu-screen-wont-inherit-my-path-on-10-5-8
뭔가 이상하게 죽은 screen 제거시
screen -wipe
docker -H tcp://localhost:1847 ps -a : 메인서버에서 노드들의 swarm까지 켜져있는 모든 거 확인
docker -H tcp://localhost:1847 rm `docker -H tcp://localhost:1847 ps -aql` : 모든 서버노드들의 컨테이너를 위에서부터 하나씩 삭제
aql하면, latest 컨테이너부터 하나씩 삭제함
ctrl + z : background로 넣어 놓기
ctrl + d : python 종료
jobs : background 보기
fg : foreground로보기
# 현재 컨테이너를 새로 image로 구워서 저장하기(이 명령어는 사용중인 컨테이너를 이미지로 굽는 용도)
기존 이미지와 이름이 겹칠 수 있으므로, 이미지를 새로 생성 할 때, 반드시 :tag 를 사용해서 구분을 해주어야 할듯.
docker commit -c "EXPOSE 12345"  컨테이너이름 imcomking/bi_tensorflow:dhkwak
docker commit 컨테이너ID new_image_name
docker -H tcp://localhost:1847 push imcomking/bi_tensorflow:dhkwak
이렇게 해서 변경된 컨테이너가 반영된 이미지를 다시 굽고, run을 다시 실행시키면 됨.
run 할 때, 포트옵션을 추가해주면, 추가된 상태로 다시 열림

Advanced Topic

Docker의 process는 기본적으로 root로 실행이 되기 때문에 user id를 수동으로 세팅하지 않는 이상 어떤 user가 실행했는지 알아내기가 어렵다.

process id로부터 container 이름 알아내기

1.
nvidia-smi 추적하고자 하는 PID를 찾는다.
2.
pstree -sg PID | grep containerd-shim 해당 PID를 호출한 container의 ID와 bash ID를 찾는다.
3.
docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Name}}' 어떤 container에서 실행되었는지 추적한다.

Nvidia-smi 에서 user이름과 실행 command 알아내기

ps -up `nvidia-smi -q -x | grep pid | sed -e 's/<pid>//g' -e 's/<\/pid>//g' -e 's/^[[:space:]]*//'`
Bash

설치하면 편한 방법

pip install nvidia-htop

process id로부터 실행 위치 알아내기

sudo pwdx PID
Bash

Container의 TimeZone을 한국 시간으로 설정하기

docker run 할때 아래의 옵션을 넣어준다.
e TZ=Asia/Seoul

Docker Container 가 저장되는 위치/방법/크기수정

일단, 모든 도커 이미지와 컨테이너는 다음 경로 아래에 위치한다.
/var/lib/docker/~~~~
그런데 이중에서 컨테이너는 기본값으로 총 100GB만 사용할 수 있고, 각 컨테이너는 10GB만 사용이 가능하다. 그리고 docker에서는 이 컨테이너들의 용량을 효율적으로 다루기 위해 Device Mapper plugin 을 이용한 thin provisioning을 사용한다. 이 기능은 총 100GB의 용량으로 10GB짜리를 1000개 이상 만들 수 있는 방법이다.
또한 thin target은 snapshot의 역할도 하며, data랑 metadata 두군데로 나누어 큰 거 / 작은거(공통) 이런식으로 관리하는 것 같다.
하여튼 이 기본 용량을 100GB보다 키우려면 다음 문서의 #We need a bigger pool 부분을 참조.

이미 존재하는 container의 entrypoint 수정하기

/var/lib/docker/~~~~/config.json 을 직접 수정한다.
# Docker Container 용량이 부족할 때 해결방법 : 이경우 다음과 같은 에러가 뜬다.
Error running DeviceCreate (createSnapDevice) dm_task_run failed
Plain Text
다음 글에서 제안하는 thin_check 기능을 사용하면 해결이 가능하다는 것 같음.
혹은 다음 방식으로, 컨테이너를 전부 삭제하기도 하나보다. ( 작업 중인거 전부 삭제되니 주의할 것)
# kill -9 $(lsof -t -c docker) # rm -rf /var/lib/docker/* # reboot
Plain Text
# Docker Container 를 저장하는 경로 바꾸기 :
다음 방법처럼 도커 데몬을 멈추고, 도커파일을 복사 후 링크를 연결하면 된다는 듯하다.
혹은 다음 방법으로 , 도커 데몬을 실행할 때 -g 옵션을 주면 경로를 바꿀 수 있다고 한다.
# Docker Container 를 저장하는 방식 바꾸기 :
도커 데몬을 실행할 때, -s overlayfs 옵션을 주면 덮어씌운다고 한다.
# Docker swarm 과 demon 그리고 nvidia-docker:먼저 docker demon은 docker를 실행시키면 항상 백그라운드로 돌고있는 프로세스이다. swarm이나 다른 것과 무관하게 그냥 도커시스템 자체를 demon이 실행되어야하고, 이것이 모든 컨테이너 등의 명령어를 관리한다.
swarm은 docker container들을 원격으로 연결해서 클라우드처럼 활용할 수 있게 해주는 기능이다. 원격으로 docker run을 실행시키고, 명령어를 원격으로 전달할 수 있게 해준다. 이 swarm은 그냥 생성된 container들을 원격에서도 접속할 수 있게 해주는 기능이다. 따라서 container가 원격으로 작업할 수 있게 세팅만 되어있다면, 바로 원격사용이 가능하다.(그런데 docker에서는 새로운 기능을 추가할 때 container단위로 실행시키는 방식(백그라운드 프로세스처럼)의 철학을 가진 프레임워크이다. 따라서 swarm을 실행시킨다는 것은 swarm 컨테이너를 만들고 그것을 실행시키는 개념이 된다.)nvidia-docker는 host에 설치된 cuda버전을 여러개 깔고, 실행시키는 이미지에 따라서 저절로 cuda버전이 host에서 바뀌도록 해주는 툴이다. 실제 구현은 그냥 docker api를 wrapping하는 식으로 구현되어 있다. 그래서 nvidia-docker run을 하는 것은 실제로는 그냥 docker run인데 다만 몇가지 옵션이 들어가 있는 것이다.
그래서 swarm과 nvidia-docker는 같이 사용할 수가 있다. nvidia-docker에 구현된 원격 nvidia-docker run함수를 이용해서 원격에 container를 생성하면, 자동으로 swarm에 의해서 원격사용이 가능해진다.(swarm세팅이 되어있고 swarm 컨테이너가 작동중이라면)
그래서 궁극적으로 이 시스템들을 이용하면 아주 훌륭한 딥러닝 GPU 클라우드(클러스터) 시스템 구축이 가능하다.# Named Volume: 도커에서 데이터를 저장하기 위해 사용하는 특수한 Directory도커는 영구적으로 사용되는 데이터들을 매번 컨테이너에 중복 할당하는 일을 막기 위해, 또는 동일한 데이터를 컨테이너들끼리 공유하기 위해 volume이라는 시스템을 사용한다. 즉 컨테이너가 volume에 해당하는 경로에 데이터를 저장하면, 이 데이터는 다른 컨테이너도 사용이 가능하다.
즉, 중복된 데이터를 사용할 경로를 volume이라는 형태로 따로 관리하고, 이를 각 컨테이너들이 mount해서 사용한다고 이해할 수 있다. Volume을 쓰면 백업이 보다 용이하고, 볼륨의 사이즈는 컨테이너의 사이즈와 무관하게 따로 관리되므로 컨테이너가 작은 용량만을 사용해도 문제가 없게 된다.
docker volume ls : 현재 만들어둔 volume 확인docker volume inspect [볼륨이름] : 해당 볼륨에 대한 내용 확인docker volume create [볼륨이름] : 볼륨 생성docker volume rm [볼륨이름] : 볼륨 삭제
또한 docker run 명령어에서 -v 또는 --mount 옵션을 통해 컨테이너에 미리 할당해둔 volume을 사용하게 할 수 있다.(--mount 옵션이 보다 사용이 어려운 고급 옵션이다.)
docker run -v [볼륨이름]:[컨테이너에 마운트할 경로]이런식으로 컨테이너에 미리 만들어둔 볼륨을 연결할 수 있다. 그런데 만약 현재 존재하지 않는 볼륨 이름을 지정하면, docker가 알아서 해당 이름의 volume을 만들고, [컨테이너에 마운트할 경로] 에 있는 데이터를 volume에 복사하게 된다.
# Bind mounts지금까지 알아본 volume의 기능은 특수한 데이터 공유용 디렉토리를 생성하는 것이었다. 그렇다면 이번에는 Host에 있는 디렉토리나 파일을 container내부에 직접 연결시키는 Bind mounts 기능을 알아보자. 사용https://docs.docker.com/engine/admin/volumes/bind-mounts/
docker run -v [Host의 파일경로]:[컨테이너에 마운트할 경로]ex) docker run -v "$(pwd)"/dataset:/app/dataset
이때 주의할 점은, Bind mounts와 volume의 문법이 같기때문에, pwd등을 입력한 것이 volume이름처럼 혼동되어 버그를 일으킬 수 있다는 점이다.
# Windows Subsystems Linux에서 Docker 사용하기1. Docker for Windows 설치https://docs.docker.com/docker-for-windows/이 녀석을 설치한 windows 10에서, WSL ubuntu bash를 사용하면 WSL에서도 docker를 쓸 수 있다. ( WSL에는 ubuntu docker 설치가 불가능하다. 이는 WSL에서 사용하는 방식이 ubuntu kernel을 windows kernel에 맵핑하는 방식인데, docker는 너무 low-level 기능을 사용해서 매핑이 불가능하기 때문이다.)
2. .bashrc 에 alias docker=docker.exe 등록* Limitation이렇게 설치한 경우, -t 옵션 (tty) 사용이 불가능한데, winpty를 설치해서 이걸 사용하면 된다고한다. 그러나 winpty설치가 매우 번거롭고, 모든 기능을 지원하지 않아서 더 나은 해결방법이 필요해보임. 혹은 WSL의 ubuntu terminal대신, powershell이나 도커 터미널을 쓰면 해결될지도 모름.https://github.com/docker/for-win/issues/1588# WSL + Docker for Windows + Docker CLIhttps://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly# WSL + Native Dockerhttps://medium.com/rkttu/wsl%EC%97%90%EC%84%9C-native-docker-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0-ff75b1627a87
# Mounting for Windows Docker그런데 Windows에서 WSL을 이용한 ubuntu에서 mount를 하기위해서는 다음과 같은 설정이 필요하다.
이를 안해주면 에러가 뜨거나 작동을 하지 않는다.
그런데 이 shared drives를 설정하려했으나, firewall 에러가 뜨는 경우 다음링크를 참조.
## Windows Docker 방화벽문제 해결하기
1) Microsoft 네트워크용 파일 및 프린터 공유 기능 지우고 재설치하기
TOP