# 개요
기본적인 docker에 대한 동작 방식은 알고 있는 상태에서 명령어만 정리하겠다.
1. docker hub에서 이미지를 받을때는 pull 명령을 사용한다. 예를 들어 wildfly의 최신버전(latest)를 받을 경우 다음과 같이 한다.
docker pull jboss/wildfly:latest
docker pull <이미지명>
docker 내에 docker-hub로부터 이미지를 다운로드 하라.
2. 내가 받은 모든 이미지들(예전에 ISO파일과 비슷하다고 보면 될 듯)의 목록을 볼 경우 다음과 같이 한다
docker images
docker 내에 지금까지 설치된 모든 이미지를 조회하라.
3. 이미지 세부 정보를 확인할 때는 inspect 명령어를 사용한다.
docker inspect jboss/wildfly:latest
docker inspect <이미지명>
해당 이미지의 상세 정보를 조회하라.(os, cpu 등등이 표시된다.)
세부 정보는 json 문자열 형태로 보여지며 주요 정보로는 이미지 ID, 생성일, Docker 버전, 이미지 생성자, CPU 등을 제공한다
정보가 json 문자열 형태로 길게 나오기때문에 만약 특정 정보를 보고자 할때는 다음과 같이 format 옵션을 주어 보고자 하는 항목을 지정하면 그 항목에 대해서만 볼 수 있다. 예를 들어
docker inspect --format="{{ .Os}}" jboss/wildfly:latest
docker inspect --format="{{ .Config.Hostname}}" jboss/wildfly:latest
4. 이미지를 삭제할때는 다음과 같이 한다
docker rmi jboss/wildfly:latest
docker rmi <이미지명>
docker 내에서 해당 이미지를 삭제하라.
5. docker 이미지를 받았으면 이제 이 이미지를 가지고 하나의 컨테이너를 생성해서 실행시키도록 한다. 이해하기 쉽게끔 설명하자면 프로그래밍에서 얘기하는 클래스와 객체로 설명하면 이해하기 쉬울듯 하다.
A라는 클래스를 이용해서 B, C라는 객체를 생성할 때..
클래스 -> 이미지
객체 -> 컨테이너
A B = new A(); // 이 시점부터 실질적으로, 독립적인 공간이 생성
A C = new A();
A라는 클래스를 이용해서 만든 객체 b와 c가 있지만 이 b와 c는 서로를 간섭하지는 않는다. 이것을 docker에 비춰보면 클래스는 이미지가 되고 객체는 컨테이너가 된다. 필요성에 따라 이미지를 이용해서 0개 이상의 컨테이너를 만들수 있지만 이렇게 만들어진 컨테이너들은 서로 독립적인 공간이 되어 서로 간섭하지를 않게 된다
컨테이너의 라이프 사이클 : create(생성) -> start(시작) -> stop(중지) -> rm(삭제)
- docker create <신규 컨테이너명>
- docker start <컨테이너명>
- docker create & start <컨테이너명> = docker run <신규 컨테이너명>
- docker stop <컨테이너명>
- docker end & start <컨테이너명> = docker restart <컨테이너명>
- docker end <컨테이너명>
- docker rm <컨테이너명>
6. docker에서 컨테이너를 생성하면서 동시에 시작할때는 run을 사용한다. wildfly를 예로 들자면 다음과 같이 한다
docker run -it --name "wildfly" -p 8080:8080 jboss/wildfly:latest
docker run -it --name <신규 컨테이너명> -p <외부HOST포트>:<컨테이너 내 포트> <이미지명>
해당 이미지로 컨테이너를 생성하고, 시작하되, 포어그라운드(-it)로 실행하라. (현재 cmd 창에서는 컨테이너내 실행 결과들을 받아 볼 수 있음)
run 명령어를 사용하면서 같이 사용된 옵션에 대해 설명을 하자면
-i 옵션은 컨테이너 표준 입력을 연다는 뜻이고, -t는 tty(단말 디바이스)를 사용한다는 의미이다. 이 둘을 묶어서 -it로 사용한 것이다.
-i는 interactive하게 컨테이너와 소통하겠다. -t는 tty를 사용한다는 뜻, 즉 터미널과 비슷한 환경을 조성해주는 것(-it 명령어가 없으면, 해당 컨테이너를 생성하고 실행하는 것으로만 그칠 것이면, 굳이 필요없다. 생성 후 cmd와 비슷한 명령어를 통해 컨테이너 내의 작업을 하길 원한다면 반드시 붙여야된다.)
--name 옵션을 주면 해당 컨테이너에 대해 특별한 이름을 부여하는 옵션이다. 컨테이너를 start 명령어로 시작하거나 stop 명령어를 이용해서 종료할때 대상 컨테이너를 지정하기 위해 컨테이너 ID를 입력해야 하는데 이 ID라는게 의미가 없는 숫자/문자가 조합된 문자열이라 타이핑하기 어렵다. 이러한 상황을 좀더 쉽게 사용하게 하기 위해 --name 옵션을 주어 의미있는 문자열을 지정함으로써 차후에 start, stop 명령어를 사용할때 그 문자열을 사용하여 해당 명령어를 쉽게 실행할 수 있다.
-p는 호스트와 컨테이너 간의 포트를 연결하는 옵션이다. wildfly는 8080 포트를 이용해서 통신하게 되는데 이것은 어디까지나 컨테이너의 8080 포트를 열은 것이기 때문에 우리가 테스트하기위해 브라우저에서 http://localhost:8080을 입력해도 동작이 이루어지지 않는다. 이로 인해 호스트의 8080 포트를 컨테이너의 8080 포트와 연결해야 제대로 된 동작을 할 수 있다. 한가지 더 첨언을 하자면 컨테이너가 8080 포트를 열었다고 해서 호스트도 반드시 8080 포트를 열으라는 법은 없다. 예를 들어 wildfly 컨테이너를 2개 이상 운영하는데 두 컨테이너가 모두 8080 포트를 열어도 문제가 되는 것은 없다. 그러나 이 두 포트를 모두 호스트의 8080 포트에 연결할 수는 없다. 즉 둘 중 하나의 컨테이너는 -p 8081:8080 이런 식으로 호스트의 8081 포트를 컨테이너의 8080 포트로 열어서 동작하게끔 해야한다
7. 6번의 과정으로 실행을 하게 되면 docker의 foreground로 실행하는 것이기 때문에 이런 식으로의 실행은 추천하지 않는다. 즉 다른 컨테이너를 시작할려면 다시 쉘 창을 열어서 docker 관련 명령을 실행해야 하기 때문이다. 대신 foreground에서 실행시키는 것이기 때문에 컨테이너를 중지할려면 ctrl-c를 눌러 중지할 수 있으며 관련 콘솔 로그 내용도 바로 확인 할 수 있는 장점은 있다. 그러나 docker로 실행되는 것이 WAS나 DB 서버 같은 서버용 프로그램인것을 감안하면 background로 실행되는 것이 좋다
8. 7번에서 설명한대로 background로 컨테이너가 실행되게끔 할려면 -d 옵션을 붙이면 된다
docker run -d --name "wildfly" -p 8080:8080 jboss/wildfly:latest
docker run -d --name <신규 컨테이너명> -p <호스트 포트번호>:<컨테이너 포트번호> <이미지명>
해당 이미지로 컨테이너를 생성하고, 시작하되, 백그라운드(-d)로 실행하라. (현재 cmd 창에서는 아무것도 표시되지 않음)
// -d가 사용되는 경우, -it는 필요없다.
// -it 또는 -d가 주로 사용된다고 생각하자
6번에서 설명했을때는 -it옵션을 붙였으나 여기서는 background로 실행하는 것이기 때문에 컨테이너 표준입력을 사용할 수가 없다. 그걸 제외한 나머지 옵션은 동일하다
그러나 이렇게 실행하면 6번에서 컨테이너가 보여주는 로그들을 볼 수 없는 문제가 있다. 이를 볼려면 logs 명령어를 사용하면 된다
docker logs -f wildfly
docker logs -f <컨테이너명>
해당 컨테이너의 로그를 현재 cmd 창에 보여라.(-f는 실시간)
-f 옵션은 linux 에서 tail 명령 사용했을때 -f 사용한 것을 생각하면 된다. 즉 로그를 계속 연속성으로 보여주는 것으로 이해하면 된다. 뒤에 wildfly가 붙은 것은 우리가 docker run을 이용해서 컨테이너를 실행할때 --name 옵션을 이용해서 주었던 그 wildfly이다. 이렇게 컨테이너를 run 명령어를 이용해서 실행할때 --name 옵션으로 별칭 준것을 docker 관련 명령 실행시 특정 컨테이너를 지칭하는데 사용함으로써 편리함을 준다.
9. 컨테이너 안에는 os가 있다는 것이다. 즉 os 안에 관련 프로그램이 설치되어 있는 개념인 것이다. 이 부분은 나중에 build를 공부하면서 확연히 알게 되었다. 즉 기본 베이스 이미지를 centos 같은 리눅스 이미지로 삼은뒤 여기에 apache를 yum 명령어로 설치하면서 이미지 만드는 법을 알게 되었다 물론 베이스 이미지를 centos 같은 os 이미지가 아니라 wildfly 이미지를 사용할 수 있다. 그렇다해도 wildfly에 centos가 이미 있기 때문에 결국 wildfly 이미지에 있는 centos 를 이용하는 것임에는 변함이 없다. 이러한 개념이기 때문에 해당 컨테이너는 독자적인 OS와 그에 따른 환경변수를 가지고 실행할 수가 있는것이며 서로간의 컨테이너에 영향을 주지 않고 독립적으로 움직이게 되는 것이다.
docker 이미지는 os가 포함이 되어 있는 0개 이상의 프로그램이 설치되어 있는 이미지이다
10. 9번에서 설명했다시피 이미지는 자체적으로 OS가 포함되어 있다고 했다. 그렇기 때문에 우리는 컨테이너가 실행중일때 이 컨테이너를 구동시키는 기반이 되는 OS에 접속할 수가 있다. 즉 우리가 wildfly 이미지를 이용하여 컨테이너를 실행중일때 이 컨테이너의 OS에 연결할 수 있다는 것이다. 이것은 나름 작업(?)적인 차원에서 중요한 의미가 있다. 예를 들어 우리가 wildfly에서 구동중인 web application을 만든다고 가정해보자. 그러면 파일 업로드 기능을 넣어야 하는데 파일 업로드를 하게 되면 OS에서 관리되는 디렉토리 중 하나에 사용자가 업로드한 파일이 있어야 하며 그러한 디렉토리를 만들어놔야 한다. 그러면 이 디렉토리를 만들려면 wildfly 컨테이너의 os 쉘에 접근해서 디렉토리를 만드는 명령(mkdir)을 실행해야 한다. 이러한 OS 쪽의 작업을 하거나 또는 wildfly의 설정파일(예를 들면 standalone.xml)을 수정할 수 있다. 그리고 이렇게 변경이 된 내용이 있는 컨테이너를 docker commit 명령을 이용해서 새로운 이미지로 만들어 이를 다른 사람에게 배포할 수 있다.
서론이 길었는데 이 기능을 수행하는것에 대해 설명하도록 하겠다
먼저 경우를 분리해서 생각해야 할 부분이 있는데 우리가 컨테이너를 실행시킬때 /bin/bash 같은 쉘을 실행시킨 상황과 그렇지 않은 상황으로 분리해야한다. /bin/bash를 실행시켜서 현재 컨테이너가 bash shell이 실행중인 상황이면 docker의 attach 명령을 이용해서 접속하면 된다
docker attach wildfly
docker attach <컨테이너명>
해당 컨테이너에 접속하고, 해당 프로세스 화면을 보여줘라.
이러면 bash shell 에 접속하여 shell 명령어를 실행시킬수 있다. 여기서는 컨테이너 이름을 지칭하기 위해 wildfly를 썼지만 실제로 wildfly는 이렇게 명령해도 접속이 되질 않는다. 왜냐면 wildfly는 bash shell을 실행시키지 않고 컨테이너를 띄우기 때문이다. 때문에 attach 명령으로 접속 시도를 해도 동작이 되질 않는다. 또한 백그라운드로 실행시킨 컨테이너의 경우도 마찬가지이다(왜 그런지는 곰곰히 생각해보면 이해하기 쉽다. 우리가 background로 컨테이너를 실행시킨다는건 docker 명령어를 실제로 내리는 shell 창에서 억세스하는 것이 아니라 말그대로 background로 돌리는 것인데 거기서 shell 명령을 실행시키는 /bin/bash가 실행될리가 없잖은가?) 이렇게 /bin/bash 를 실행시키지 않고 시작한 컨테이너나 또는 background에서 실행중인 컨테이너를 접속하기 위해서는 exec 명령을 사용해서 /bin/bash를 실행시켜 접속하면 된다
docker exec -it -u root wildfly /bin/bash
docker exec -it -u root <컨테이너명> <터미널 명령어>
해당 컨테이너의 root 사용자권한으로 해당 터미널 명령어를 실행시켜라.(/bin/bash 명령어란 리눅스계열에서, 터미널 쉘을 여는 명령어?)
이렇게 exec 명령을 통해 /bin/bash를 실행시켜서 bash shell 명령을 실행시킬수가 있게 된다. -u는 os의 어떤 계정을 통해서 명령을 실행시킬것인지를 지정하는 것이다. 그러면 어떤 계정을 사용해야 하는가? 그것은 해당 컨테이너의 docker 이미지를 build 하는데 사용된 Dockerfile의 소스를 보면 알 수 있다.
일반적으로 docker 이미지들은 github 에서 이미지 관련 파일들이 존재하여 이를 github에서 빌드하여 docker hub에 배포되는 형태를 취하고 있다. 그래서 이 Dockerfile의 소스를 볼려면 github을 봐야한다. wildfly를 예로 들면 docker hub의 wildfly에 대한 페이지(https://hub.docker.com/r/jboss/wildfly/)를 가보면 Source 항목에 github과 링크되어 있는 링크를 볼 수 있다. 이를 클릭하면 github에 있는 해당 이미지를 빌드하는데 사용된 Dockerfile을 볼 수 있게 된다. 이 Dockerfile 을 클릭해서 상세 내용을 보면 USER 란 항목이 있어 Dockerfile을 이용해 빌드하면서 실행하게 되는 명령어를 수행하는 OS 계정을 언급하게 된다. 바로 이 USER 항목에 언급된 계정을 -u 옵션 뒤에 사용해주면 된다. 모든 docker 이미지가 root 계정을 사용하는 것은 아니기 때문에 이것을 확인하고 진행하기 바란다.
attach와 exec에 대한 추가 설명
위의 내용을 읽어보면 bash shell 기능을 위주로 설명했기때문에 현재 bash shell이 실행중인 컨테이너를 접속하여 할때는 attach, bash shell 이 실행중이지 않은 컨테이너를 접속하려 할때는 exec를 사용하는 것으로 이해가 될 것 같아서 부가설명을 하고자 한다.
attach는 현재 실행중인 컨테이너에 접속 한다고 보는 것이 맞다. docker 이미지가 컨테이너로 생성되면서 run이 될때 이미지 가 내부적으로 특정 프로그램을 실행시킬수도 있고 또는 run 명령을 내리는 시점에서 특정 프로그램을 실행 시키게도 할 수 있다. 이때 bash shell 이 실행되도록 /bin/bash 를 실행시켜서 attach 시 바로 bash shell prompt가 보이게끔 할 수도 있다. 그러나 그렇지 않은 이미지도 있다. 예를 들어 위에서 언급했던 wildfly 이미지의 경우 이 이미지는 컨테이너로 올라가는 시점에 wildfly의 standalone.sh 스크립트가 실행시켜 wildfly가 구동되도록 하고 있기 때문에 콘솔 화면에서 wildfly의 로그가 올라오는 것을 볼 수 있다. 그래서 docker의 attach 명령을 이용해서 wildfly의 컨테이너를 접속하면 wildfly 구동되고 있는 상황에서 나오는 콘솔로그를 볼 수 있다. 또한 이 상태에서 ctrl-c를 클릭하면 wildfly 가 실행되는 것이 종료되는 것을 볼 수 있다.
이에 반해 exec는 현재 실행중인 컨테이너에 특정 shell script를 실행하는 것이라고 보는게 맞다. 예를 들어 wildfly 이미지의 컨테이너가 wildfly란 이름으로 컨테이너가 실행되어 있는 상태에서 docker exec -it wildfly /bin/bash 로 명령을 내리면 해당 컨테이너에 bash shell을 실행시키는 것이다. 그래서 bash shell prompt가 화면에 나오게 되는 것이다. 만약 /bin/bash가 아는 사용자가 만든 별도 스크립트를 실행시키면 그 스크립트가 실행시키는 것이다(exec 명령어를 사용할때 -it 옵션을 주었기 때문에 해당 명령의 실행 결과가 현재 보는 화면에 바로 보이게 된다)
11. docker 에서 등록되어 있는 컨테이너 목록을 확인 하는 명령은 다음과 같이 한다
docker ps -a
현재 docker에 등록된 모든 컨테이너 목록을 조회하라.
-a 옵션을 빼고 docker ps 로 실행하게 되면 현재 동작중인 컨테이너만 보여주기 때문에 중지되어 있는 컨테이너는 보여주지 않게 된다. 그래서 -a 옵션을 항상 붙여서 중지되어 있는 것도 같이 확인하는 버릇(?)을 들이는 것이 좋다
12. docker에서 실행중인 각 컨테이너가 사용하는 CPU 및 메모리 점유율 등 컨테이너의 상태를 확인하기 위해서는 다음의 명령을 사용한다
docker stats
현재 docker에 등록된 컨테이너의 CPU, 메모리 점유율, 상태를 조회하라.
13. 중지되어 있는 컨테이너를 구동하는 명령은 다음과 같이 한다
docker start wildfly
docker start <컨테이너명>
중지되어 있던 해당 컨테이너를 구동하라.
14. 실행중인 컨테이너를 중지시키는 명령은 다음과 같이 한다.
docker stop wildfly
docker stop <컨테이너명>
현재 실행중인 해당 컨테이너를 중지하라.
15. 컨테이너를 재시작할때의 명령은 다음과 같이 한다
docker restart wildfly
docker restart <컨테이너명>
현재 실행중인 해당 컨테이너를 재시작하라.
16. 컨테이너를 삭제할때의 명령은 다음과 같이 한다
docker rm -f wildfly
docker rm -f <컨테이너명>
해당 컨테이너를 완전히 삭제하라.
원래 컨테이너를 삭제할때는 현재 상태가 중지되어 있는 컨테이너만 삭제가 가능하기 때문에 실행중인 컨테이너는 삭제를 할 수가 없다. 그러나 -f 옵션을 붙이면 실행중인 컨테이너도 삭제하게 된다
docker rm $(docker ps -a -q)
현재 docker에 등록되어 있는 컨테이너 중 실행된 컨테이너를 제외하고 모두 삭제하라.
docker rm -f $(docker ps -a -q)
현재 docker에 등록되어 있는 모든 컨테이너를 삭제하라.
17. 현재 구동중인 컨테이너에서 실행 중인 프로세스를 모두 일시정지 시키고자 할 때는 다음과 같이 한다
docker pause wildfly
docker pause <컨테이너명>
해당 컨테이너에서 실행 중인 모든 프로세스를 일시중지하라.
일시정지된 컨테이너를 docker ps 명령어를 이용해서 컨테이너 목록으로 확인해보면 Status에 Paused로 나온다(참고로 stop 명령을 이용해서 컨테이너를 중지시킨 경우 docker ps 명령을 이용해 목록을 확인해보면 Status에 Exited로 나온다)
이렇게 일시정지된 컨테이너를 다시 재시작 시킬때는 다음과 같이 한다
docker unpause wildfly
docker unpause <컨테이너명>
일시중지였던 해당 컨테이너를 다시 정상화하라.
18. 현재 구동중인 컨테이너에서 실행중인 프로세스를 확인할때는 다음과 같이 한다
docker top wildfly
docker top <컨테이너명>
해당 컨테이너의 실행중인 프로세스를 조회하라.
linux에서 top 명령을 실행한것과 같은 유형의 결과물을 보여준다
19. 구동중인 컨테이너에서 실행중인 프로세스가 이용중인 포트를 확인하고자 할때는 다음과 같이 한다
docker port wildfly
docker port <컨테이너명>
해당 컨테이너에서 사용중인 포트를 조회하라
(ex)
8080/tcp ->0.0.0.0:8080
왼쪽 : 컨테이너의 포트 -> 8080 포트(왼쪽의 8080/tcp)를
오른쪽 : 호스트 컴퓨터(정확하게는 현재 내가 사용중인 OS)의 포트 ->8080 포트(0:.0.0.0:8080)와 연결되고 있음
20. 컨테이너와 호스트 컴퓨터 간의 파일 복사는 다음과 같이 한다
docker cp wildfly:/opt/jboss/wildfly/standalone/configuration/standalone.xml D:/dockerimages
docker cp <컨테이너명>:<복사파일경로> <붙여넣을 폴더 로컬 경로>
컨테이너의 파일을 로컬 경로로 복사하라.
docker cp D:/dockerimages/standalone.xml wildfly:/opt/jboss/wildfly/standalone/configuration
docker cp <복사 로컬파일 경로> <컨테이너명>:<붙여넣을 컨테이너 경로>
로컬 파일을 컨테이너 경로로 복사하라.
# 출처
https://zgundam.tistory.com/132
'[CDS] DevOps > Docker' 카테고리의 다른 글
[Docker ] Docker 실습 (0) | 2022.05.04 |
---|---|
Docker 사용법 (0) | 2020.11.24 |
[Docker] Docker 이론 (0) | 2020.11.23 |
최근댓글