최근 키즈노트 FE개발파트는 도커(Docker) 도입을 검토하고 있어요. 사실 도커의 ㄷ
도 모르는 상태인데요, 🥺 도입 후 도커랑 어색한 사이이면 안 되겠죠?
이번 포스트에서는 도커에 대해 간단히 알아보고, 도커를 이용해서 리액트 어플리케이션을 실행해 보도록 할게요.
가상 머신은 소프트웨어를 통해 생성된 가상의 컴퓨터 환경이에요.
가상 머신은 실제 컴퓨터 시스템과 유사한 환경을 제공하기 때문에, 가상 환경에서 별도의 OS와 어플리케이션을 실행할 수 있어요.
가상 머신은 위 그림처럼 하이퍼바이저(Hypervisor)라는 소프트웨어에 의해 관리돼요. 하이퍼바이저는 물리적 하드웨어의 자원을 가상화하여 여러 가상 머신이 자원을 공유하면서도 독립적으로 실행될 수 있도록 합니다.
(물리적 하드웨어의 자원을 가상화한다는 것은 CPU, 메모리 등의 물리적인 자원을 소프트웨어를 통해 논리적인 자원으로 변환하는 것을 의미해요.)
실제로 가상 머신을 실행한 모습은 아래와 같습니다.
도커는 가상 머신과 마찬가지로 하나의 물리 서버 위에서 여러 독립적인 환경을 실행하고, 가상 머신의 단점이었던 성능 문제를 보완하고, 환경 의존성과 설정 문제로 인해 흔하게 발생했던 배포 오류를 해결하기 위해 등장했어요.
도커는 하이퍼바이저, 게스트 OS를 설치하지 않는 대신 도커 엔진 위에서 독립된 환경인 컨테이너(Container)를 만들고 관리해요.
Dockerfile
Dockerfile은 어플리케이션을 이미지로 빌드 하기 위한 설정 파일이에요. 이 파일에는 어플리케이션 실행에 필요한 OS, 패키지, 설정과 명령어를 작성해요.
Dockerfile을 기반으로 이미지를 만들고, 이 이미지를 실행하여 컨테이너를 생성할 수 있어요.
아래는 Dockerfile의 예시입니다.
빌드를 실행하면 명령어들이 순차적으로 실행되면서 각각의 이미지 레이어가 생성돼요. 이렇게 만들어진 레이어들은 스택 구조로 쌓이게 됩니다.
빌더는 이전 빌드에서 만들어진 레이어를 재사용하려고 시도하는데요, 만약 레이어가 변경되지 않았다면 캐싱 된 레이어를 가져오지만 레이어가 변경되었다면 해당 레이어부터 이후의 모든 레이어들을 다시 빌드 하게 됩니다.
Image
이미지는 Dockerfile을 바탕으로 만들어진 실행 환경의 스냅샷이에요. 이미지는 컨테이너가 실행될 수 있는 기본 환경을 제공해요. 필요한 라이브러리, 설정 등이 포함되어 있어 하나의 이미지로 여러 개의 컨테이너를 만들 수 있어요.
이미지는 Docker Hub와 같은 공개 레지스트리에 공유할 수 있어요.
Container
컨테이너는 실제로 실행되는 어플리케이션 인스턴스에요. 컨테이너는 어플리케이션 실행에 필요한 모든 종속성을 포함해서 독립적인 환경을 제공해요.
컨테이너는 다른 어플리케이션이나 프로세스를 충돌 없이 한 시스템에서 동시에 실행할 수 있어요.
다운로드 페이지에서 도커를 설치해 줄게요. 설치가 완료된 후 도커 앱을 실행하면 아래와 같은 화면을 볼 수 있어요.
Create React App을 이용하여 간단한 리액트 어플리케이션을 생성해 주었어요. 어플리케이션을 실행하면 localhost:3000
에서 아래와 같은 화면이 나타나요.
루트 디렉토리에 아래와 같이 Dockerfile
을 생성해줄게요.
FROM
: 디폴트 이미지를 설정할 때 사용하며, Dockerfile
은 FROM
명령어로 시작해야 해요. Docker Hub에서 이미지를 가져와서 사용하는 것이 가장 쉽고 일반적이에요.COPY
: COPY <src> <dest>
문법을 사용하며, 호스트 컴퓨터의 <src>
파일 또는 디렉토리를 → 컨테이너의 <dest>
로 복사해요.. .
로 지정해 주었어요. 추후 단계에서 컨테이너를 실행시켜보면, 컨테이너의 루트 디렉토리에 모든 파일들이 옮겨진 것을 확인할 수 있어요.RUN
: 이미지를 빌드 하는 과정에서 실행되기 때문에, 일반적으로 어플리케이션 실행에 필요한 라이브러리를 이 단계에서 설치해요.npm install
로 패키지들을 설치해 줄게요.CMD
: 컨테이너를 최초로 실행할 때 사용되는 명령어입니다. Dockerfile
에서는 하나의 CMD
명령어를 가질 수 있으며, 둘 이상의 CMD
명령어가 있는 경우 마지막 명령어만 실행돼요.npm start
를 사용했어요.EXPOSE
: 컨테이너가 실행될 때 사용할 포트 번호를 명시해요. EXPOSE
명령어는 포트 번호를 명시하는 단순한 문서 역할을 하기 때문에, 실제 컨테이너 실행에는 영향을 주지 않아요.아래 명령어를 이용해서 이미지를 빌드해 줄게요.
docker build -t simple-react-docker-practice .
-t
옵션을 이용해서 이미지에 simple-react-docker-practice
라는 태그를 붙어주었어요.
터미널에서 명령어들이 실행되는 것을 확인할 수 있어요. FROM
명령어에 적었던 Node.js는 캐시를 사용했다는 것을 알 수 있네요!
아래 명령어를 이용해서 컨테이너를 실행해 줄게요.
docker run -dp 127.0.0.1:3000:3000 simple-react-docker-practice
-d
: 컨테이너를 백그라운드에서 실행시킬 때 사용하는 옵션이에요. 컨테이너를 백그라운드에서 실행시킨다면 컨테이너의 실행에 필요한 터미널을 계속 열어둘 필요가 없기 때문에 보통 백그라운드에서 실행합니다.-p
: 호스트와 컨테이너 간의 포트 연결을 위해 사용하는 옵션이에요. 호스트 주소:컨테이너 포트 번호
형식을 사용하며, 저는 호스트의 localhost:3000
과 컨테이너의 3000
포트를 연결해 주었어요. 이러한 매핑 옵션이 없다면 호스트에서 컨테이너의 어플리케이션에 접근을 할 수 없습니다.터미널에서 리액트 어플리케이션을 종료한 후, http://localhost:3000 으로 접속하면 컨테이너에서 어플리케이션이 실행 중인 것을 확인할 수 있습니다.
이번엔 App.tsx
파일에서 코드를 살짝 수정해 볼게요.
"4. 이미지 빌드하기", "5. 컨테이너 실행하기" 단계와 동일하게 컨테이너를 실행시켰더니, 아래와 같이 에러가 발생했어요.
이전 컨테이너가 호스트 3000번 포트를 사용하고 있기 때문에 또다시 3000번 포트로 실행이 불가능한 상태에요. 이를 해결하기 위해서는 이전 컨테이너를 제거해 주어야 합니다.
아래 명령어를 이용해서 현재 실행 중인 컨테이너 목록을 조회해 줄게요.
docker ps
3000번 포트를 사용 중인 컨테이너 아이디를 확인한 후, 컨테이너를 중지시켜 줍니다.
docker stop <container-id>
컨테이너가 중지되었다면 아래 명령어를 이용해서 컨테이너를 제거할 수 있어요.
docker rm <container-id>
다시 컨테이너를 실행한 후 localhost:3000
으로 접근하면 정상적으로 실행 중인 것을 확인할 수 있어요. 수정한 대로 잘 나오고 있네요!👍🏻
"1. 도커 설치하기" 단계에서 설치했던 도커 앱에서도 컨테이너의 상태를 확인할 수 있어요.
지금까지는 도커에 대해 잘 몰랐기 때문에 굉장히 어색한 사이였는데요, 이제는 1px 정도 친해진 기분이 들어요.
배포 단계에서 환경 설정 문제로 배포에 실패하는 케이스를 간혹 보았는데요, 도커를 도입해서 잘 사용하면 배포 실패 확률을 많이 줄일 수 있지 않을까 하는 기대가 듭니다. 앞으로도 도커와 친해지고 싶어요!
실습에 사용된 코드는 여기서 확인할 수 있습니다.
안녕하세요~ 멋진 포스팅을 보기 위해 미래에서 왔습니다. 🚀