인프라

Docker(3) - 도커를 실행해보자

조앤박 2023. 8. 5. 01:33

지난 글에서 도커의 내부 구조와 그 이유에 대해 설명했다.

 

도커 설치는 많은 글에서 나와 있으므로 건너뛰고, 바로 실행해보도록 하자.

도커도 git과 마찬가지로 데스크톱 어플리케이션을 이용해 gui를 쓸 수도 있고, shell로 명령을 줄 수도 있지만

cli를 사용하는 것이 도커를 가장 잘 이용할 수 있으니, 웬만하면 cli로 쓸 수 있도록 하자.

 

백문이 불여일타, 아파치 서버를 띄워보면서 사용법을 익히자.

Docker official image는 도커허브에서 지원하는 공식 이미지이다.

아파치 서버는 httpd라는 이름의 이미지로 도커 허브에서 제공되고 있다.

들어가보면, 제목 옆에 회색 박스로 docker pull httpd라고 써져 있다.

그걸 그대로 cli에 붙여넣으면 된다.

error during connect: this error may indicate that the docker daemon is not running: 
Post "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/images/create?fromImage=httpd&tag=latest": 
open //./pipe/docker_engine: The system cannot find the file specified.

혹시 cli에서 이 문구가 뜬다면, Docker desktop을 실행하지 않아서 나는 오류니 docker desktop을 실행해서

꼭 우측 하단에 이런 고래 모양이 나오는지 확인하고 실행하도록 하자.

$ docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
648e0aadf75a: Already exists
c76ba39af630: Already exists
b9819ffb14ec: Already exists
37baa60548e6: Already exists
6dbce5de7542: Already exists
Digest: sha256:d7262c0f29a26349d6af45199b2770d499c74d45cee5c47995a1ebb336093088
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest

What's Next?
  View summary of image vulnerabilities and recommendations → docker scout quickview httpd

이런 문구가 나오면 정상적으로 이미지를 pull한 것이다.

Docker desktop에 들어가면, 이미지가 정상적으로 풀 되었다는 것을 알 수 있다.

cli로 docker images 명령어를 쳐도 똑같이 나온다.

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
httpd        latest    96a2d0570deb   6 days ago   168MB

다시 Docker desktop에 와서 Actions 탭 밑의 ▶버튼을 누르면, 다음과 같은 창을 확인할 수 있다.

Docker run

run을 누르면 docker desktop의 containers 탭에서 인스턴스가 잘 구동되고 있음을 알 수 있다.

중지와 삭제도 할 수 있다. 

cli에서도 똑같이 할 수 있다.

Docker run (cli)

docs.docker.com > reference > command and reference에 가면 cli에서 실행할 수 있는 명령어에 대한 설명을 볼 수 있다.

기본적으로, 우리가 위에서 했던 실행은 명령어를 입력하면 된다.

$ docker run httpd
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Thu Aug 03 19:35:47.361498 2023] [mpm_event:notice] [pid 1:tid 139700299224960] AH00489: Apache/2.4.57 (Unix) configured -- resuming normal operations
[Thu Aug 03 19:35:47.361621 2023] [core:notice] [pid 1:tid 139700299224960] AH00094: Command line: 'httpd -D FOREGROUND'

 

이제 컨테이너가 정상적으로 실행되는지를 확인해보자.

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS                      PORTS     NAMES
96837dea1d96   httpd     "httpd-foreground"   44 seconds ago   Exited (0) 36 seconds ago             stoic_lehmann

docker ps 명령어를 치면 현재 실행되고 있는 컨테이너, docker ps - a 옵션을 주면 중지된 컨테이너까지 다 볼 수 있다.

 

주의할 점은 아까 App store와 Docker를 비교할 때 도커도 한 프로그램에서 여러 프로세스를 띄울 수 있는 것처럼, 한 이미지에서 여러 컨테이너를 띄울 수 있다고 했다. 잘 켜졌는지 확인하려고 계속 docker run httpd를 치면 인스턴스가 계속 생성된다.

$ docker stop 96837dea1d96
96837dea1d96

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS                      PORTS     NAMES
96837dea1d96   httpd     "httpd-foreground"   9 minutes ago   Exited (0) 24 seconds ago             stoic_lehmann

$ docker rm 96837dea1d96
96837dea1d96

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS                      PORTS     NAMES

docker stop CID를 입력하면 컨테이너 실행을 중지할 수 있고, docker ps에는 나오지 않지만 -a 옵션을 주면 나오게 된다.

그럴 때는 docker rm(remove) CID를 줘서 삭제하면 -a옵션을 줘도 말끔하게 삭제되는 것을 볼 수 있다.

Docker desktop과는 다르게 cid는 컨테이너가 실행중일 때는 삭제할 수 없고, 중지 후 삭제를 해야한다.

귀찮으면 --force 옵션을 주면 중지를 건너뛰고 삭제할 수 있다.

$ docker rm --force 96837dea1d96
96837dea1d96

이미지를 삭제하고 싶을 때는 rmi(remove image)를 해주면 된다.

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
httpd        latest    96a2d0570deb   6 days ago   168MB

$ docker rmi httpd
Untagged: httpd:latest
Untagged: httpd@sha256:d7262c0f29a26349d6af45199b2770d499c74d45cee5c47995a1ebb336093088
Deleted: sha256:96a2d0570deb75d5b01df09165d903f7647da37952e1fd9692d791b8e3a6f0cd

$ docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

다시 docker images 명령어를 입력하면 잘 삭제되었음을 볼 수 있다.

잘 삭제되었는지 gui를 이용해 보고 싶으면, docker desktop을 다시 열어 확인해보자.

 

지금까지 우리가 쳤던 명령어를 다시 정리해보자. 위에서 치지 않았던 명령어도 한 번씩 다시 쳐보도록 하자.

모든 cli 명령어는 docker desktop에서도 gui로 확인할 수 있다.

레지스트리에서 이미지를 가져오기
• docker pull [OPTIONS] NAME[:TAG|@DIGEST]
도커 이미지 확인
• docker images [OPTIONS] [REPOSITORY[:TAG]]
도커 이미지에서 컨테이너를 생성하고 실행
• docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
컨테이너 확인 (전부 확인 : -a)
• docker ps 
컨테이너 중지
• docker stop [OPTIONS] CONTAINER [CONTAINER...]
컨테이너 로그 확인 (계속 확인 : -f)
• docker logs [OPTIONS] CONTAINER
컨테이너 삭제 (중지 안하고 삭제 : --force)
• docker rm [OPTIONS] CONTAINER [CONTAINER...]
이미지 삭제
• docker rmi [OPTIONS] IMAGE [IMAGE...]

더 많은 명령어는 아까 말했듯 docs.docker.com에서 확인할 수 있다.

 

그런데 아까 gui에서 컨테이너를 run할 때 보면 optional settings라는 메뉴가 있는 것을 볼 수 있다.

이것들은 대체 무엇일까? 일단 제일 위부터 확인해보자.

Container name

컨테이너에 이름을 부여할 수 있다. 나중에 컨테이너를 사용할 때 이름 또는 CID로 제어해야 하므로, 알아볼 수 있는 이름을 붙여주도록 하자. 

나는 httpd의 이미지를 가지고 gui에서는 ws2로, cli에서는 docker run --name ws3 httpd라는 명령어를 주었다.

둘 다 잘 실행됨을 알 수 있다.

아까는 docker stop 7806cef04db6을 했던 것을 이제는 docker stop ws2로만 옵션을 줘도 중지가 가능하다.

그리고 image 탭에 보면 httpd:latest와 httpd가 다름을 알 수 있다. 이건 나중에 확인하도록 하자.

Port, Volumes, Environment variable

사실 도커를 이해하기 위해서는 네트워크 지식이 어느정도는 필요하다.

 

먼저, 도커 없이 웹 서버를 사용하는 방법에 대해 알아보자.

웹 서버를 사용하기 위해서는 두 대의 컴퓨터가 필요하다.

한 대의 컴퓨터에는 웹 브라우저(client)가 깔려 있어야 하며, 다른 컴퓨터에는 웹 서버(server)가 깔려 있어야 하고, 웹 파일을 특정 디렉토리(file server)에 위치시켜야 한다.

그래야 client가 server에 어떤 파일을 달라고 요청을 보냈을때, server가 file server에 가서 정확한 파일을 가지고 올 수 있기 때문이다.

예를 들어 서버가 /usr/local/apache2/htdocs/라는 디렉토리에 index.html을 위치시켰다고 가정해보자.

그리고 웹서버에게 ‘웹 서버야, 누가 index.html을 달라고 할 때는 이 디렉토리에서 찾아줘!’라고 각인시켰다고 생각해보자.

그런데, 클라이언트와 서버는 어디에서 만나서 이 파일을 달라고 하고 꺼내주는 작업을 해야 할까?

바로 포트가 이 만남을 주선하는 창구 역할을 한다.

우리 컴퓨터에는 65535개의 포트가 컴퓨터에 설치되어 있는 여러 소프트웨어를 네트워크적으로 구분해준다. 그리고 기본적으로 80번 포트에서 접속을 대기하고 있다가 요청이 오면 마중을 나간다.

 

이번에는 도커를 이용해서 웹서버를 사용하는 방법을 알아보자.

도커를 이용하면, 웹 서버가 컨테이너에 설치된다.(docker run httpd)

그리고 이 컨테이너가 설치된 OS를 ‘docker host’라고 부른다.

하나의 docker host에는 여러개의 컨테이너가 있으며

이 도커 호스트와 여러개의 컨테이너는 서로 독립된 실행구조를 따르기 때문에,

우리가 전에 했던것처럼 80포트에 연결하면 우리의 웹서버가 마중을 나가지 못한다.

그래서 도커 호스트와 컨테이너의 포트를 서로 연결해주어야 한다.

이럴 때 작성하는 명령어는 다음처럼 작성한다.

docker run -p 80:80 httpd

docker hub에 따르면 p옵션은 '컨테이너의 포트를 호스트에게 알린다(연결시킨다)' 라는 뜻이다.

 

따라서 이 명령어를 자연어로 번역하면 다음과 같다.

'도커에서 httpd라는 이름의 이미지를 컨테이너로 띄우는데, 호스트의 80번 포트와 컨테이너의 80번 포트로 이어줘!'

이 작업을 포트포워딩(port forwarding)이라고 한다.

그림으로 확인해보자.

다시 돌아가서 포트포워딩을 실행해보고, localhost:80으로 들어가보자.

It works!가 뜨면 성공적으로 연결된 것이다.

스프링에서의 whitelabel error page가 뜨는것과 같다. ws라는 컨테이너의 index.html이 렌더링된 것이다.

 

명령어 실행

docker desktop 안에 들어가서 cli를 띄우고 pwd(print working directory)를 실행해보자.

나는 실험적으로 호스트의 8888번 포트와 컨테이너의 80포트를 연결했다.

이 pwd는 위의 그림에서 host의 pwd일까, container의 pwd일까, 아니면 둘 다의 pwd일까?

당연히 host와 컨테이너는 독립적으로 동작한다고 하였으므로, 이 cli에서 실행하는 명령어들은 호스트에서 pwd를 하는것이 아니라, 그 컨테이너 안으로 들어가서 pwd를 한 것이다.

ls -al또한 마찬가지다.

그럼, 매번 내가 컨테이너에 들어가서 이걸 실행할 수도 없고, 호스트 cli에서 실행할 수 있는 방법은 없을까?

 

있다. 바로 exec(execute) 명령어다.

docker exec [name] /bin/sh

이 명령어를 치게 되면 본쉘(Bourne shell)이 실행되게 된다.

쉘이라는 프로그램의 본질은 사용자가 입력한 명령어를 쉘 프로그램이 받아서 그것을 운영체제에게 전달해주는 일종의 창구다.

 

호스트 cli에서 docker exec ws3 /bin/sh를 입력하면, 그냥 넘어가게 된다.

리눅스가 사람이었다면 아마 ‘쉘 켰습니다. 근데 어쩌라고요?’라는 말을 했을 것이다.

그래서 다음과 같은 명령어를 준다.

docker exec -it ws /bin/sh

-it 옵션은 컨테이너를 종료하지 않은 상태로 터미널의 입력을 컨테이너 내부로 전달하기 위해 사용된다. 

 

그러면 이제 화면이 달라지고, 여기서 치는 명령어로 계속 소통을 할 수 있게 된다.

exit를 치면, 다시 호스트로 돌아갈 수 있다.

참고로, /bin/bash를 치면 bash shell이 실행된다.

 

이걸 왜 했냐? 바로 컨테이너 내부의 index.html을 변경하기 위해서이다.

hub.docker.com/_httpd에서는 httpd의 index.html이 어디에 있는지 상세하게 설명해준다.

/usr/local/apache2/htdocs에 있다고 한다.

docker exec -it ws3 /bin/bash 명령어를 치고 cd로 /usr/local/apache2/htdocs/에 가보자.

컨테이너는 기본적으로 적은 용량을 구비하는것이 미덕이기때문에 vim이나 nano등은 구비하지 않았다.

나노 에디터 설치부터 index.html을 변경하는 방법은 다음 주소에서 확인할 수 있다.

https://youtu.be/P0ZFyB4iQd0?t=262

에디터로 이 index.html을 수정하고

다시 host의 포트에서 접속을 해보면, index.html이 바뀌어 있는 것을 알 수 있다.

이렇게 열심히 index.html을 수정했다.

거의 애플 공식 홈페이지 저리가라 할 정도로 멋있게 헬로 머현 페이지를 꾸며놨는데,

그런데 갑자기 내가 키우던 고양이가 멋대로 내 컴퓨터에 올라와서는 컨테이너를 지웠다.

나는 부랴부랴 기도하며 다시 컨테이너를 켰지만, 나를 반기는건

전혀 work가 되어있지 않은데 '작동하니 안심하라고!'라는 페이지였다.

이 불상사는 어떻게 해야 좋을까? 고양이를 두들겨 패서는 해답이 나오지 않는다.

또, 내가 대현이를 환영하기 위해 만든 300개의 똑같은 Hello Meohyun 페이지를 만드려면

모든 컨테이너에 나노 에디터를 깔고 똑같이 수정해야할까?

생각만 해도 괘씸하고 눈물이 나고.... 아무튼 그렇다.

 

우리가 컨테이너를 사용하는 이유는 필요할 때 언제든지 생성하고, 필요가 없을때는 지울 수 있는게 가장 큰데,

우리가 호스트의 파일 시스템에서 수정한 내용이 컨테이너의 파일 시스템과 연결될 수 있다면?

그러면 컨테이너가 날라가도 소스코드를 그대로 들고 있을 수 있다.

터미널을 열어서 다음과 같은 명령어를 쳐보자.

docker run -p 8888:80 -v ~/Desktop/htdocs:/usr/local/apache2/htdocs httpd

아까 p 옵션에 대해 배웠으므로, 이제는 v 옵션에 대해 배워보자.

volume의 약자인 v는, 호스트의 파일 시스템에 있는 파일과 컨테이너의 파일 시스템에 있는 파일을 연결시켜준다.

따라서 저 위의 명령어를 자연어로 해석하면

'httpd의 이미지를 run할건데, 내 ~/Desktop/htdocs에 있는 index.html을 컨테이너의 /usr/local/apache2/htdocs에 마운트 시켜줘' 라는 말과 같다.

 

~/Desktop/htdocs는 각 호스트 OS에 있는 파일 경로마다 다르니, 꼭 자신의 파일이 있는 곳으로 해서 실습해보자.

 

주의점

만약 내가 내 OS의 index.html에

<html>
 <body>
  <h1> Hello, Meohyun !</h1>
 </body>
</html>

을 작성했는데, docker run ~ 명령어를 쳐놓고 It works!가 뜬다면,

마운트가 잘 되지 않은 것이다.

이를 대비해서 디렉토리 루트 설정을 되도록이면 소문자 + '-'를 최소화하는 방향으로 사용하자.

 

마지막으로 , Container의 라이프사이클을 보며 다시 한번 복습하고 글을 마친다.