# Docker ## Docker란? ![image](https://hackmd.io/_uploads/S1USx6EG0.png) Docker는 소프트웨어 개발 및 배포를 간소화하고 효율화하기 위한 플랫폼이다. 개발을 하다보면 다양한 환경에서 애플리케이션을 실행해야 하는 경우가 많다. 예를 들어, 개발자는 로컬 환경에서 애플리케이션을 개발하고, 테스트 환경에서 애플리케이션을 테스트하며, 프로덕션 환경에서 애플리케이션을 배포한다. 그런데, 실행 환경 간 불일치 문제로 인해 개발, 테스트, 배포 등의 과정에서 문제가 발생하는 경우가 많다. 즉, 개발 시에는 잘 작동하는 애플리케이션이 프로덕션 환경에서는 제대로 동작하지 않는 경우가 발생한다. ![image](https://hackmd.io/_uploads/H1aYU6NzC.png) 이러한 문제가 발생하는 이유는 각 환경마다 다른 라이브러리, 도구, 설정 등이 사용되기 때문이다. 따라서, 개발 환경과 프로덕션 환경을 일치시키는 것은 번거롭고 까다로운 작업이다. ![image](https://hackmd.io/_uploads/Hy1zc6VGA.png) 이러한 문제를 해결하기 위해 Docker는 컨테이너 기반의 가상화 기술을 사용하여 애플리케이션을 패키징하고 실행할 수 있는 환경을 제공한다. 컨테이너는 애플리케이션과 그 애플리케이션을 실행하는 데 필요한 모든 라이브러리, 도구 및 설정을 하나의 패키지로 묶은 가볍고 독립적인 실행 환경이다. Docker는 이러한 컨테이너를 관리하고 실행할 수 있는 도구와 서비스를 제공한다. 다만 이런 문제는 과거에도 이미 존재했던 문제이며, 이를 해결하기 위해 가상화 기술이 사용되었다. 그러나 가상화 기술은 가상 머신을 생성하고 운영체제를 설치하는 등의 과정이 필요했기 때문에 무겁고 느렸다. 반면 Docker는 컨테이너를 사용하여 가상화의 부담을 최소화하고 성능을 최적화하였다. 따라서 Docker는 가상화 기술의 단점을 보완하고, 더욱 경량하고 이식성이 높은 환경을 제공한다. ![image](https://hackmd.io/_uploads/By604uyMR.png) 즉, 도커는 빠른 실행이 가능한 가상 환경을 제공하며, 이를 통해 개발, 테스트, 배포 등의 과정을 효율적으로 관리할 수 있게 해준다. ## Docker의 주요 특징 1. **경량성** : 컨테이너 기반의 Docker는 가상화의 부담을 최소화하고 성능을 최적화한다. 2. **이식성** : Docker는 어떠한 환경에서도 동일하게 작동하며, 코드의 이식성을 증가시킨다. 3. **모듈성** : Docker는 애플리케이션을 작은 조각으로 나누어 관리하므로, 각 부분을 독립적으로 개발하고 업데이트 할 수 있다. ## Docker 설치하기 Docker를 시작하기 위해서는 먼저 시스템에 Docker를 설치해야 한다. ### Windows에서 Docker 설치하기 Windows 10에서는 Docker Desktop을 사용하여 Docker를 설치할 수 있다. 1. Docker Desktop을 다운로드하기 위해 Docker 허브 [Docker Hub](https://hub.docker.com/editions/community/docker-ce-desktop-windows/) 페이지에 접속한다. 2. 'Get Docker' 버튼을 클릭하여 Docker Desktop 설치 파일을 다운로드한다. 3. 다운로드 받은 실행 파일을 실행하면 설치가 시작된다. > [공식 설치 문서](https://docs.docker.com/desktop/install/windows-install/) ### MacOS에서 Docker 설치하기 MacOS에서는 Docker Desktop for Mac을 사용하여 Docker를 설치할 수 있다. 1. Docker Desktop for Mac을 다운로드하기 위해 Docker 허브 [Docker Hub](https://hub.docker.com/editions/community/docker-ce-desktop-mac/) 페이지에 접속한다. 2. 'Get Docker' 버튼을 클릭하여 Docker Desktop 설치 파일을 다운로드한다. 3. 다운로드 받은 '.dmg' 파일을 열고 'Docker.app'을 Applications 폴더로 드래그한다. > [공식 설치 문서](https://docs.docker.com/desktop/install/mac-install/) ### Linux에서 Docker 설치하기 Linux에서는 패키지 매니저를 이용하여 Docker를 설치할 수 있다. Ubuntu를 기준으로 설명하면 다음과 같다. 1. 터미널을 열고 `sudo apt-get update` 명령어를 입력하여 패키지 리스트를 업데이트한다. 2. `sudo apt-get install docker-ce` 명령어를 입력하여 Docker를 설치한다. 3. 설치가 완료되면 `docker --version` 명령어를 입력하여 정상적으로 설치되었는지 확인한다. > [공식 설치 문서](https://docs.docker.com/desktop/install/linux-install/) ## Docker의 이미지와 컨테이너 이해하기 Docker를 이해하는 데 있어 가장 중요한 개념 중 두 가지는 바로 '이미지'와 '컨테이너'다. 두 개념을 이해하면 Docker의 전반적인 동작 방식을 더 잘 이해할 수 있다. ### Docker 이미지 Docker 이미지는 애플리케이션을 실행하는 데 필요한 모든 것을 포함하고 있는 불변(immutable)의 파일 시스템이다. 이는 실행 가능한 소프트웨어 코드, 사용되는 라이브러리, 도구, 환경 변수 및 설정 파일 등을 포함한다. 이미지는 컨테이너를 생성하기 위한 '템플릿'으로 생각할 수 있다. 한 이미지에서 여러 개의 컨테이너를 생성할 수 있으며, 각각의 컨테이너는 독립적으로 작동한다. Docker 이미지는 Docker Hub와 같은 Docker 레지스트리에 저장되고 공유될 수 있다. `docker pull` 명령을 사용하면 이러한 레지스트리에서 이미지를 다운로드할 수 있다. ### Docker 컨테이너 Docker 컨테이너는 Docker 이미지를 기반으로 생성되며, 이미지의 실행 가능한 인스턴스다. 컨테이너는 이미지의 실행 버전이며, 독립적인 파일 시스템과 네트워크 인터페이스, 프로세스 공간을 가지고 있다. `docker run` 명령을 사용하면 이미지에서 컨테이너를 실행할 수 있다. 실행 중인 컨테이너는 `docker ps` 명령을 통해 확인할 수 있다. 결론적으로, Docker 이미지는 애플리케이션과 그 실행 환경을 묶은 것이며, Docker 컨테이너는 이 이미지를 실행한 상태라고 생각하면 된다. 이렇게 함으로써 개발, 테스팅, 배포 과정에서 일관된 환경을 보장할 수 있다. > 아주 따라서 극단적으로 단순화하자면, 이미지는 일종의 '프로그램'이며, 컨테이너는 이 이미지를 실행한 '프로세스'라고 생각할 수 있다. ## Docker 기본 명령어 Docker를 설치한 후에는 Docker 명령어를 사용하여 다양한 작업을 수행할 수 있다. 여기서는 Docker의 기본 명령어에 대해 알아본다. ### Docker 버전 확인 ```bash docker --version ``` 이 명령어를 입력하면 Docker의 버전 정보가 표시된다. 만약 Docker가 설치되어 있지 않다면, 먼저 Docker를 설치해야 한다. ### Docker 이미지 목록 확인 ```bash docker images ``` 이 명령어를 사용하면 현재 로컬에 저장된 Docker 이미지의 목록을 확인할 수 있다. ### Docker 이미지 다운로드 Docker 이미지를 다운로드하려면 `docker pull` 명령어를 사용한다. 예를 들어, Ubuntu 이미지를 다운로드하려면 아래와 같이 입력한다. ```bash docker pull ubuntu ``` 이 이미지는 Docker Hub에서 제공되며, Docker의 공식 이미지 저장소인 Docker Hub에는 다양한 운영체제 및 소프트웨어 이미지가 저장되어 있다. > Docker Hub는 Docker의 공식 이미지 저장소로, 다양한 운영체제 및 소프트웨어 이미지를 제공한다. DockerHub에서는 사용자가 직접 이미지를 업로드하고 공유할 수도 있다. (앱스토어와 비슷한 개념) ### Docker 이미지 삭제 Docker 이미지를 삭제하려면 `docker rmi` 명령어를 사용한다. 예를 들어, 이미지 ID가 `[ImageName]`인 이미지를 삭제하려면 아래와 같이 입력한다. ```bash docker rmi [ImageName] ``` ### Docker 컨테이너 실행 Docker 컨테이너를 실행하려면 `docker run` 명령어를 사용한다. 예를 들어, Ubuntu 컨테이너를 실행하려면 아래와 같이 입력한다. ```bash docker run -it ubuntu ``` 여기서 `-it` 옵션은 인터랙티브 모드로 터미널을 사용할 수 있도록 해준다. 위 명령어를 실행하면 Ubuntu 컨테이너 내부로 들어가게 된다. 컨테이너 내부에서 `exit` 명령어를 입력하면 컨테이너를 종료하고 나오게 된다. > run 실행 시 이미지가 없으면 자동으로 pull을 시도한다. ### 포트 포워딩을 사용한 Docker 컨테이너 실행 Docker 컨테이너를 실행할 때 포트 포워딩을 사용하려면 `-p` 옵션을 사용한다. 예를 들어, Nginx 컨테이너를 실행하고 호스트의 포트 80을 컨테이너의 포트 80에 매핑하려면 아래와 같이 입력한다. ```bash docker run -p 80:80 nginx ``` 위 명령어를 실행하면 Nginx 웹 서버가 실행되고, 웹 브라우저에서 `http://localhost`로 접속하면 Nginx의 기본 페이지를 확인할 수 있다. ### 백그라운드에서 Docker 컨테이너 실행 컨테이너를 백그라운드에서 실행하려면 `-d` 옵션을 사용한다. 예를 들어, Nginx 컨테이너를 백그라운드에서 실행하려면 아래와 같이 입력한다. ```bash docker run -d -p 80:80 nginx ``` ### 실행중인 Docker 컨테이너에 접속 만약 백그라운드에서 실행 중인 Docker 컨테이너에 접속하려면 `docker exec` 명령어를 사용한다. 예를 들어, 컨테이너 ID가 `[ContainerName]`인 컨테이너에 접속하려면 아래와 같이 입력한다. ```bash docker exec -it [ContainerName] /bin/bash ``` > `-i` : interactive 모드. 표준 입력을 통해 컨테이너에 입력할 수 있도록 함 > `-t` : tty를 할당. 쉘을 사용할 수 있도록 함 ### Docker 컨테이너 목록 확인 실행 중인 Docker 컨테이너의 목록을 확인하려면 `docker ps` 명령어를 사용한다. ```bash docker ps ``` ### Docker 컨테이너 중지 실행 중인 Docker 컨테이너를 중지하려면 `docker stop` 명령어를 사용한다. 예를 들어, 컨테이너 ID가 `[ContainerName]`인 컨테이너를 중지하려면 아래와 같이 입력한다. ```bash docker stop [ContainerName] ``` ### Docker 모든 컨테이너 확인 Docker 컨테이너는 중지되었다고 바로 삭제되지 않는다. 왜냐하면 중지된 컨테이너는 언제든 다시 시작할 수 있기 때문이다. 그런데 중지된 컨테이너는 `docker ps` 명령어로 확인할 수 없다. `docker ps` 명령어는 '실행 중인' Docker 컨테이너의 목록만을 확인하기 때문이다. 따라서 중지된 컨테이너는 확인되지 않는다. 중지된 컨테이너를 포함하여 모든 Docker 컨테이너의 목록을 확인하려면 `-a` 옵션을 사용한다. ```bash docker ps -a ``` ### Docker 컨테이너 삭제 컨테이너를 중지해도 Docker 컨테이너는 삭제되지 않는다. '중지' 했을 뿐 언제든 다시 시작할 수 있다. 컨테이너를 삭제하려면 `docker rm` 명령어를 사용한다. 예를 들어, 컨테이너 ID가 `[ContainerName]`인 컨테이너를 삭제하려면 아래와 같이 입력한다. 중지된 Docker 컨테이너를 삭제하려면 `docker rm` 명령어를 사용한다. 예를 들어, 컨테이너 ID가 `[ContainerName]`인 컨테이너를 삭제하려면 아래와 같이 입력한다. ```bash docker rm [ContainerName] ``` #### 실행중인 Docker 컨테이너에 접속 시 주의사항 실행중인 컨테이너에 접속중인 상태에서 빠져나올 때, `exit` 명령어를 사용하면 컨테이너가 종료된다. 컨테이너의 쉘에서 작업 중에 일시적으로 쉘을 빠져나가고 싶다면 `Ctrl + P`, `Ctrl + Q` 키 조합을 이용한다. 이 키 조합을 사용하면 현재 실행 중인 컨테이너를 종료하지 않고 쉘에서 빠져나올 수 있다. > Docker의 쉘에서 `exit` 혹은 `Ctrl + D`를 사용하면, 현재 실행 중인 프로세스(여기서는 쉘)가 종료되는데, 이 때 Docker는 해당 프로세스가 종료되었음을 감지하고 컨테이너를 종료시킨다. 이를 통해 컨테이너 내의 주요 프로세스가 실패하면 자동으로 컨테이너를 정리하도록 설계되어 있다. 이러한 동작은 "PID 1 문제"라고 불리우는데, UNIX와 Linux 시스템에서 PID 1은 시스템 종료 시까지 계속 실행되며, 모든 자식 프로세스의 종료를 처리한다. 컨테이너에서 실행하는 프로세스는 PID 1로 실행되며, 이 프로세스가 종료되면 컨테이너도 함께 종료된다. > > 반면에, `Ctrl + P`, `Ctrl + Q` 조합을 사용하면 쉘에서 빠져나오게 되지만, Docker 컨테이너는 여전히 실행 중이다. 이는 Docker의 'detach' 모드로, 컨테이너를 백그라운드에서 계속 실행하면서도 쉘에 접근하거나 컨테이너에서 벗어날 수 있게 해준다. 이렇게 하면 여러 컨테이너를 동시에 관리하거나, 백그라운드 작업을 수행하면서도 다른 작업을 계속할 수 있다. 이런 동작은 'detaching'이라고 부른다. > > 따라서, 컨테이너 내부에서 작업을 한 뒤 `Ctrl + P`, `Ctrl + Q`를 사용하여 detaching하면, 작업은 계속 실행되지만 사용자는 다른 명령어를 입력하거나 다른 컨테이너로 전환할 수 있다. ## Docker 파일시스템 설정 Docker 컨테이너는 격리된 파일시스템을 사용한다. 이는 각 컨테이너가 독립적으로 운영될 수 있도록 하며, 컨테이너 간의 간섭을 최소화한다. Docker의 파일시스템 설정은 크게 두 가지로 나뉜다. 볼륨(Volumes)과 바인드 마운트(Bind Mounts)가 있다. ## Docker 볼륨(Volumes) Docker 볼륨은 컨테이너가 사용하는 데이터를 호스트와 공유하거나, 컨테이너 간에 데이터를 공유하는 데 사용된다. 볼륨을 사용하면 컨테이너가 종료되어도 데이터가 유지되며, 여러 컨테이너 간에 데이터를 공유할 수 있다. ### Docker 볼륨 생성 Docker 볼륨을 생성하려면 `docker volume create` 명령어를 사용한다. 예를 들어, `my-volume`이라는 이름의 볼륨을 생성하려면 아래와 같이 입력한다. ```bash docker volume create my-volume ``` ### Docker 볼륨 목록 확인 Docker 볼륨의 목록을 확인하려면 `docker volume ls` 명령어를 사용한다. ```bash docker volume ls ``` ### Docker 볼륨 삭제 Docker 볼륨을 삭제하려면 `docker volume rm` 명령어를 사용한다. 예를 들어, `my-volume`이라는 이름의 볼륨을 삭제하려면 아래와 같이 입력한다. ```bash docker volume rm my-volume ``` ### Docker 볼륨을 사용하여 컨테이너 실행 Docker 볼륨을 사용하여 컨테이너를 실행하려면 `-v` 옵션을 사용한다. 예를 들어, `my-volume`이라는 이름의 볼륨을 사용하여 Ubuntu 컨테이너를 실행하려면 아래와 같이 입력한다. ```bash docker run -v my-volume:/app -it ubuntu ``` 위 명령어를 실행하면 `my-volume`이라는 이름의 볼륨을 `/app` 디렉토리에 마운트한 Ubuntu 컨테이너가 실행된다. 컨테이너에 접속하여 `/app` 디렉토리에 파일을 생성하면, 해당 파일은 호스트의 `my-volume` 볼륨에 저장된다. 따라서, 컨테이너가 종료되어도 데이터는 유지된다. ### Docker 볼륨을 사용하여 컨테이너 간 데이터 공유 Docker 볼륨을 사용하면 컨테이너 간에 데이터를 공유할 수 있다. 예를 들어, `my-volume`이라는 이름의 볼륨을 사용하여 Ubuntu 컨테이너를 실행하고, 해당 볼륨을 사용하여 Nginx 컨테이너를 실행하려면 아래와 같이 입력한다. ```bash docker run -v my-volume:/app -it ubuntu docker run -v my-volume:/usr/share/nginx/html -p 80:80 nginx ``` 위 명령어를 실행하면 `my-volume`이라는 이름의 볼륨을 `/app` 디렉토리에 마운트한 Ubuntu 컨테이너와 `/usr/share/nginx/html` 디렉토리에 마운트한 Nginx 컨테이너가 실행된다. ### 바인드 마운트(Bind Mounts) 바인드 마운트는 호스트 파일 시스템의 특정 디렉토리를 컨테이너 내부의 특정 경로에 연결하는 방식이다. 바인드 마운트는 호스트 시스템의 디렉토리를 그대로 사용하기 때문에 컨테이너가 종료되더라도 데이터가 그대로 유지된다. #### 바인드 마운트 사용 예제 ```shell docker run -d -v /host/data:/container/data --name my_container ubuntu ``` 위 명령어는 호스트의 `/host/data` 디렉토리를 컨테이너의 `/container/data` 경로에 마운트하여 `ubuntu` 이미지를 기반으로 `my_container`를 실행한다. ## Docker 명령어 정리 Docker를 사용할 때 자주 사용되는 명령어를 정리하면 다음과 같다. ```shell docker ├── login : Docker에 로그인 ├── version : Docker의 버전 확인 ├── images : 사용 가능한 Docker 이미지 리스트 확인 ├── image │ ├── ls : 사용 가능한 Docker 이미지 리스트 확인 │ ├── pull [ImageName] : Docker Hub 또는 다른 저장소로부터 이미지 가져오기 │ ├── push [ImageName] : Docker Hub 또는 다른 저장소로 이미지 업로드 │ ├── build -t [RepositoryName] [Directory] : Dockerfile을 사용하여 이미지 생성 │ └── rm [ImageName] : 이미지 삭제 ├── rmi [ImageName] : 이미지 삭제 ├── ps : 실행 중인 컨테이너 리스트 확인 │ ├── -a : 모든 컨테이너 리스트 확인 │ └── -q : 모든 컨테이너 ID 확인 ├── container │ ├── ls : 실행 중인 컨테이너 리스트 확인 │ ├── restart [ContainerID] : 컨테이너 재시작 │ ├── rm [ContainerID] : 컨테이너 삭제 (정지 상태 필요) │ └── prune : 정지된 모든 컨테이너 삭제 ├── exec -it [ContainerID] │ ├── [Command] : 실행 중인 컨테이너에서 명령 실행 │ ├── /bin/bash : 컨테이너에 접근하여 Bash 쉘 실행 │ └── sh : 컨테이너에 접근하여 Bourne 쉘 실행 ├── run [ImageName] │ ├── --name [Name] : 이름 지정하여 컨테이너 생성 및 실행 │ ├── -p [HostPort]:[ContainerPort] : 포트 연결하여 컨테이너 실행 │ ├── -d : 백그라운드에서 컨테이너 실행 │ └── -v [HostPath]:[ContainerPath] : 호스트와 컨테이너 경로 연결(마운트) ├── volume │ ├── create [VolumeName] : 볼륨 생성 │ ├── ls : 볼륨 리스트 확인 │ ├── rm [VolumeName] : 볼륨 삭제 │ └── prune : 모든 볼륨 삭제 ├── stop [ContainerID] : 실행중인 컨테이너 종료 ├── start [ContainerID] : 종료된 컨테이너 실행 ├── tag [ImgName]:[Tag] [ImgName]:[NewTag] : 이미지 태그 변경 ├── logs [ContainerName] : 컨테이너 로그 확인 └── inspect [ContainerName] : 컨테이너 상세 정보 확인 ```