JUINTINATION

도커(Docker) 가볍게 입문해보기 - 2 본문

StudyNote

도커(Docker) 가볍게 입문해보기 - 2

DEOKJAE KWON 2024. 1. 26. 22:13
반응형

지난 도커(Docker) 가볍게 입문해보기 - 1에서 이어지는 내용이며 해당 글은 이 링크로 들어가면 확인할 수 있다. 지난 글에서 언급했듯이 이 스터디는 학교 선배가 작성한 강의 자료를 참고하였으며 ETRI에서 대여한 관련 책과 추가로 대여한 다른 책을 참고했다.

 

GitHub - J-Hoplin/Docker-n-K8S-Lecture-Note: Docker,Kubernetes 강의자료

Docker,Kubernetes 강의자료. Contribute to J-Hoplin/Docker-n-K8S-Lecture-Note development by creating an account on GitHub.

github.com

Docker Image

결론적으로 도커 이미지라고 하면 리눅스 배포판의 User Land만 설치된 파일을 의미하는 베이스 이미지에 필요한 프로그램, 라이브러리, 소스코드가 설치된 하나의 파일 이라고 할 수 있다.

User Land에 대해 간단히 알아보자. 지난 3학년 1학기에 운영체제에서 배웠듯이 프로세스는 커널모드와 유저모드 두가지 모드에서 실행된다. 유저모드는 사용자가 접근할 수 있는 영역으로 프로그램 자원 내부에 침입하지 못하도록 하는 모드이다.
반대로 커널 모드는 모든 자원에 대한 접근이 가능하며 운영체제의 모든 작업을 할 수 있는 영역이다(커널에 의한 제어).
이와 비슷한 정의대로, User Land는 부팅에 필요한 실행파일(ex : /sbin/init)과 라이브러리, 패키징 시스템(apt,yarn,microdnf...etc)을 포함시킨다.

Base Image에는 순수 리눅스 배포판 뿐만 아니라, Nginx, MySQL과 같은 미들웨어나 애플리케이션이 설치된 베이스 이미지도 존재한다.

베이스 이미지는 Dockerfile을 정의하여 만들 수 있는데 매번 이미지를 만들 때 이미지가 중복으로 생성되어 용량에 부담이 갈 수 도 있다 생각할 수 있지만 도커는 베이스 이미지에서 바뀐 부분에 대해서만 이미지로 생성하며 실행할 때는 베이스 이미지와 바뀐 부분을 합쳐서 실행한다. 추가적으로 Docker Image는 16진수로 된 ID로 구분되며, 각각의 이미지는 독립적이다.

https://github.com/juintination/Docker-n-K8S-Lecture-Note/raw/main/docker-1-What-is-docker%3F/imgs/8.jpeg

Docker Container

도커 컨테이너란 이미지를 실행한 상태이다. 하나의 이미지로 여러개의 컨테이너를 만들 수 있으며 운영체제에 비유하자면 이미지는 실행파일, 컨테이너는 프로세스이다. 결론적으로 Docker는 애플리케이션 단위의 실행환경을 만들기에 최적화 되어있다. 그렇기에 클라우드 환경에서는 하나의 이미지로 여러개의 서버를 한번에 올리는것도 가능하며 서버라는 호스트 머신에 직접적인 소프트웨어, 엔진을 설치하지 않아도 되고, 이에 따른 버전 의존성 문제도 해결된다.

간단한 Docker 실습

우선 너무 오래 전에 설치해두고 방치해서 정확히 기억은 나지 않지만 나는 아마 Homebrew를 통해 Docker Desktop을 설치했을 것이다. 그래서 우선 $ brew install --cask docker 커맨드를 실행하여 Docker를 설치하자.

이후에 회원가입을 진행했던 것 같은데 거기까진 정확히 기억이 나질 않는다. 나중에 재설치할 일이 있으면 내용 추가하도록 하겠다.

아무튼 설치가 완료되면 터미널에서 $ docker -v 커맨드를 실행하여 도커 버전을 확인하면서 설치가 제대로 완료되었는지 확인한다.

예전에 실습하면서 만들었던 컨테이너 파일들이 조금 보이지만 처음 설치하게 된다면 당연히 아무런 파일도 보이지 않을 것이다.

$ docker ps 커맨드를 실행하면 현재 실행중인 컨테이너 목록이 보이게 된다. 아직은 아무 컨테이너도 실행중이지 않기 때문에 아무것도 출력되지 않을건데 $ docker ps -a 커맨드를 실행하면 실행중이지 않은 컨테이너까지 모두 보이게 된다.


docker pull 커맨드로 실행하여 원하는 이미지를 docker hub에서 가져올 수 있다. 그럼 일단 간단하게 ubuntu 14.04버전을 컨테이너로 실행시켜보자. $ docker pull ubuntu:14.04 커맨드를 실행한 후에 ubuntu 14.04 버전을 가져온 이후에 $ docker images | grep ubuntu 커맨드를 실행하여 이미지를 잘 가져왔는지 확인할 수 있다.

이후에 $ docker run -it --name my-ubuntu-env ubuntu:14.04 /bin/bash 커맨드를 실행하여 우분투 이미지를 컨테이너로 실행해보자.

$ docker run 커맨드에는 여러 옵션이 있는데 옵션은 아래에 적어두겠다.

이 상태에서 $ exit 또는 $ docker stop my-ubuntu-env 커맨드를 실행하여 컨테이너를 멈출 수 있고 $ docker rm my-ubuntu-env 또는 $ docker rmi ubuntu:14.04 커맨드를 실행하여 멈춘 이후에 해당 컨테이너를 지울 수 있다.

이외에도 docker start (컨테이너 ID 혹은 컨테이너 이름) docker restart (컨테이너 ID 혹은 컨테이너 이름) 커맨드를 실행하여 동작을 멈춘 컨테이너를 다시 시작할 수 있다.

docker run 커맨드로 도커 컨테이너 실행하기

기본적인 docker run 커맨드의 형태는 다음과 같다.

$ docker run <옵션> <이미지 이름> <실행할 파일 혹은 실행 명령어(이하 실행 명령어, 주어지지 않으면 이미지에 정의된 기본 명령어 혹은 파일이 실행된다.)>

우선 실행 모드 옵션에는 -i -t -it 옵션이 대표적이며 -it -itd -d 이 세가지 옵션을 주로 사용한다. 각각이 나타내는것은 아래와 같으며, 크게 Attach Mode, Detach Mode로 나뉜다.

  • Attach Mode : 컨테이너 콘술 출력값을 확인하고 싶은 경우에 사용한다.
    • -i : interactive를 의미한다. 상호 작용이 가능하다는 의미이다
    • -t : Pseudo TTY를 의미한다.
      • TTY란 teletypewriter의 약자로서 리눅스 디바이스 드라이버 중 콘솔이나 터미널을 의미한다.
    • -it : 컨테이너 실행시 실행 명령어에 대한 상호작용 가능한 쉘(혹은 tty)로 실행이 된다.
      • 예를 들어 실행명령어가 bash면 bash shell로 mysql이면 mysql shell로 연결된다
      • 주로 -i와 -t는 따로쓰지 않고 함께 사용한다.
  • Detach Mode : 콘솔창을 확인할 필요가 없으며 백그라운드에서 작동만 하면 되는경우에 사용한다.
    • -d : detach모드이다. detach모드는 백그라운드에서 실행하는 모드를 의미한다.
    • -itd : detach 모드에서 상호작용 가능한 쉘이 수행된다. 즉 상호작용이 가능한 쉘이 실행되지만 백그라운드에서 실행되어 보이지 않는다.
      • 주로 컨테이너 실험(도커 네트워크, 볼륨, Dockerfile 작동 디버깅)을 하거나 테스트를 할때 유용하게 쓰인다.

컨테이너를 실행할 때 --name 옵션을 사용하여 이름을 지정해 줄 수 있다. 만약 이름을 지정하지 않는다면 아래 예시와 같이 Docker Engine에 의해 랜덤 이름으로 생성된다.

이 때 해당 컨테이너를 종료하거나 삭제하고 싶을 때 해당 컨테이너의 id을 모두 적을 필요 없이 id의 앞부분만 입력하여 커맨드를 실행할 수 있다. 나는 이 규칙이 Longest prefix match 기법을 사용하는 것으로 알고 있다. 즉, 실행중인 컨테이너가 여러개라면 겹치지 않을 만큼의 가장 긴 부분만 적으면 된다. 나는 아직 id가 길게 겹친적은 없어서 주로 앞의 4자리만 입력한다.


-p 옵션을 지정하면 컨테이너의 특정 포트를 호스트 머신(도커를 실행하는 PC 혹은 서버)의 포트와 연결시켜줄 수 있다. 단, 당연하게도 바인딩할 호스트 머신의 포트는 점유되지 않은 포트여야 한다. -p 옵션은 여러개 지정할 수 있으며 사용법은 아래와 같다

-p (호스트 머신 포트):(컨테이너 포트)

이제 다음 커맨드를 각각 실행하여 실습에 사용해볼 도커 이미지 세개를 pull 해보자.

// https://hub.docker.com/_/mysql
$ docker pull mysql 

// https://hub.docker.com/_/ubuntu
$ docker pull ubuntu

// https://hub.docker.com/_/httpd
$ docker pull httpd

이후에 $ docker run -d --name my-apache -p 9000:80 -p 9100:443 httpd 커맨드를 실행하여 다음의 Week2의 실습 조건을 만족해보자.

  • 컨테이너 이름은 my-apache로 지정한다.
  • 웹서버이므로 콘솔을 볼 필요가 없이 실행이 목적이다. 그렇기에 detach 모드로 실행한다.
  • Apache는 기본적으로 80번 포트를 http로, 443번 포트를 https로 사용한다.
    • 두 포트 각각 호스트 머신의 9000,9100으로 바인딩 해본다.
  • 실행 명령어는 기본 명령어는 지정하지 않는다.(이미지의 기본 실행명령어 사용)

이후에 localhost:9000에 접속하면 다음과 같은 It works! 화면을 볼 수 있을 것이다.


-e 옵션을 통해 컨테이너 환경변수를 지정해 줄 수 있다. 예를 들어 MySQL 이미지의 경우에는 필수 환경변수, 선택적 환경변수를 지정할 수 있다. 지정하는 방식은 -e (환경변수 이름)=(값) 과 같이 Key-Value 형식으로 지정한다. 문자열 값은 작은 따옴표('')로 묶는다.

이제 MySQL 컨테이너 실습이다. 실습 조건은 다음과 같다.

  • 컨테이너 이름은 my-rdb로 지정한다.
  • detach 모드로 실행한다.
  • MySQL은 3306번이 기본 포트이다. 로컬 포트의 9200번 포트와 연결해본다.
  • MYSQL_ROOT_PASSWORD 환경변수는 'pw1234'로 값을 지정하고 MYSQL_DATABASE 환경변수는 'example'로 값을 지정한다.(원래는 password1234! 였는데 그냥 짧게 줄여봤다.)
  • 실행 명령어는 이미지 기본 실행 명령어를 사용한다
  • MySQL이 제대로 작동하는지 확인하기 위해 mysql-connect-test.py를 실행해본다.(나는 해당 코드에서 host="localhost"로, password="pw1234"로 변경하였다.)
    • 실행을 위해 $ pip3 install pymysql cryptography 커맨드를 실행하여 필요한 패키지들을 미리 설치한다.

Solution은 다음과 같다. 먼저 mysql-connect-test.py가 있는 디렉터리로 이동한 후에 아래와 같이 조건에 맞춰 컨테이너를 실행하고 해당 파이썬 파일을 실행한다.(컨테이너를 실행한 이후에 디렉터리를 이동해도 상관없다.)

$ cd Study/DockerStudy/

$ docker run -d --name my-rdb -p 9200:3306 -e MYSQL_ROOT_PASSWORD='pw1234' -e MYSQL_DATABASE='example' mysql

$ python3 mysql-connect-test.py

나는 맥북으로 파이썬 개발을 하고 있지 않기 때문에 아래에 뭐 설치하라고 뜨는데 가볍게 무시해준다.


-v 옵션을 통해 컨테이너의 특정 디렉토리에 대해 볼륨(물리적 디스크 공간)을 지정할 수 있다. 도커는 기본적으로 휘발성을 가지고 있다. 그렇기에 컨테이너를 삭제하면, 안에있던 내용들도 같이 없어진다. 만약 볼륨을 지정한다면, 파일들을 물리적으로 보관할 수 있다. -v 옵션은 아래와 같이 사용할 수 있다.

-v (호스트 디렉토리 혹은 도커 볼륨 이름):(컨테이너 디렉토리 이름)

도커는 두 가지 형태로 볼륨을 가질 수 있다. 하나는 호스트 머신 볼륨이고 다른 하나는 도커 볼륨이다. 여기서는 호스트 머신 볼륨을 간단히 사용만 해본다. 호스트 머신 볼륨을 사용하게 된다면 우측에 지정한 컨테이너의 디렉토리만, 좌측에 지정한 호스트 머신의 디렉토리 영역으로 대체되는 것이며 흔히 마운트라고한다.

https://github.com/juintination/Docker-n-K8S-Lecture-Note/raw/main/docker-2-Docker-Engine-Commands/imgs/5.png

"물리적으로 동일한 영역이다" 라는 말이 무슨 뜻인지 친구와 함께 한참을 고민했었다. 그래서 생각해낸 것이 조치원 집과 대전 집이다.

나는 홍익대학교 세종캠퍼스에 재학중인데 ETRI 2024 동계 연수연구원이 되어 대전에 잠깐 살 원룸을 구해 지내고 있는데 이걸 예시로 들어 대전 집을 조치원 집의 컨테이너라고 생각해보자. 대전 집은 조치원 집에 있던 모든 물건들이 있다. 대전 집에 어떤 가구를 두면 동시에 조치원 집에 똑같은 가구가 생긴다. ETRI 근무 기간이 끝나면 더이상 대전 집은 나에게 없는 것과 다름 없기 때문에 모든 가구를 뺀다.(집을 부술 수는 없으니 여기까지만 생각하자;) 대전 집의 모든 가구를 뺐지만 조치원 집에는 모든 가구가 남아있다... 뭐 이런식으로 이해했다. 실제 예시는 여기에서 "예를 들어 필자의 경우"를 참고하자.


docker attach 명령어는 이미 실행중인 컨테이너에 접속할 때 사용한다.사용 방법은 아래와 같다.

docker attach (컨테이너 이름 혹은 ID)

주의해야할 것은 docker attach는 docker run명령어로 컨테이너 실행시 지정된 컨테이너 실행 명령어 혹은 프로그램에 접속 한다는 점이다. 컨테이너는 기본 실행 명령어가 종료되는 경우 종료되는 성질을 가지고 있다. 그렇기 때문에 docker attach 명령어를 종료하면 컨테이너도 함께 종료된다.

이미지는 기본적으로 Dockerfile이라는것을 기반으로 생성되는데 컨테이너는 실행시(docker run) 사용자가 지정한 명령어나 프로그램이 없다면 Dockerfile의 CMD 혹은 ENTRYPOINT에 기재된 기본 명령어로 컨테이너를 실행하게 된다.
컨테이너는 사용자가 지정한 명령어나 프로그램 혹은 이미지 기본 명령어가 끝나면 자동으로 꺼진다는것을 알아두자.

예를 들어서 mysql 같은 경우에 mysqld라는 데몬 프로그램이 기본 실행 명령어이다.( 링크에서 확인할 수 있다) 그렇기 때문에, docker attach 명령어로 mysql 컨테이너를 접속하면 아무것도 안뜨는 화면이 나온다.(그렇다고 해서 mysql 컨테이너를 실행할때 /bin/bash를 지정하면 안되는데 그 이유는 mysql이미지는 순수히 mysql을 실행하기 위해 설계가 되었고, 임의로 실행 명령을 바꾼다면 컨테이너가 설계 목적대로 작동하지 않을 가능성이 높기 때문이다. 이미지는 결국 애플리케이션 단위 가상화가 주 목적이고, 이미지의 설계 목적을 무조건 고려해야한다.) 반대로 ubuntu의 경우에는 기본 실행 명령어가 bash 이므로 접속이 가능하다.

detach 모드로 컨테이너를 실행하고 docker attach로 생성한 컨테이너에 접속해 보자.

$ docker ps 커맨드를 실행하여 실행중인 컨테이너 목록을 확인해보면 컨테이너가 꺼진 것을 확인할 수 있다. 이유는 docker attach 명령어는 ubuntu 컨테이너의 실행 프로그램인 /bin/bash에 접속하는것이고 exit 명령어에 의해 /bin/bash는 종료되었기 때문이다. 이 컨테이너를 다시 시작하기 위해서는 docker start명령어를 사용하여 재시작해주면 된다.


docker exec명령어는 컨테이너 내부(컨테이너의 쉘)에서 사용할 수 있는 명령어들을 실행할 수 있는 명령어이다. 사용법은 아래와 같다.

docker exec (옵션) (컨테이너 명) (명령어 args)

옵션은 위에서 본 docker run의 Attach Mode인자들을 사용할 수 있다. 예를 들어 위에서 생성한 ubuntu 컨테이너는 /bin/bash실행 프로그램으로 실행한다. 그렇기에 기본적인 Shell문법을 실행할 수 있다.

$ docker ps 커맨드를 실행하여 실행중인 컨테이너 목록을 확인해보면 컨테이너가 꺼지지 않은 것을 확인할 수 있다.

mysql 컨테이너를 실행한다. 이번에는 mysql 쉘에 접속해 볼 것이다(mysql -u root -p). MySQL 쉘은 상호작용이 가능한 쉘이기 때문에 옵션인 -it를 사용하여 쉘에 접속한다. Enter password에 환경변수로 지정한 비밀번호를 입력한다. MySQL 쉘에 접속한것을 볼 수 있다.

마찬가지로 docker attach와 비교할 수 있는 차이점은 exit를 통해 나와도 컨테이너는 꺼지지 않는다는 것이다. docker attach는 컨테이너 실행 명령어에, docker exec는 주어진 명령어에 대한 것이기 때문에 docker exec에 의해 컨테이너가 꺼지지는 않는 것이다.

결국 모든 도커 이미지는 리눅스 기반이며 그에 따라 /bin/sh 혹은 /bin/bash와 같은 쉘도 존재한다. docker exec 명령어는 이 쉘에서 주어진 명령어를 수행하는것과 동일하다. 이러한 성질이 있기때문에 docker exec을 사용해서 컨테이너의 상호작용 가능한 쉘에 접근이 가능하다.

위의 결과 처럼 mysql 컨테이너 쉘 안에서 mysql -u root -p 를 사용하면 이전과 동일하게 mysql 쉘에 접속할 수 있는것을 볼 수 있다.


결론

드디어 미루고 미루던 도커 관련 글이다. 어제까지 스퍼트 프로젝트 로그인 및 회원가입 기능을 구현하면서 정처기 공부도 미루고 도커 공부도 같이 미뤘다. 사실 Node.js는 어찌저찌 블로그를 보면서 책 빌려가면서 하겠지만 도커는 이번에 처음으로 제대로 공부를 시작한 것이기도 해서 하고 싶다는 마음이 잘 생기지 않은 것도 사실이지만.. 어쨌든 이번 글을 정리하면서(거의 강의록 복붙 수준이긴 하지만..) 도커와 조금이나마 가까워진 기분이 든다.

728x90
Comments