우리가 개발한 환경을 이미지화해서 컨테이너를 만들고, 다시 이미지를 수정할 경우 계속 언급해왔듯 변경 사항이 바로 반영이 되지않으므로 이미지를 재구축하고 다시 실행해야 했다. 개발하는 동안에 코드를 수정할 때마다 이 과정을 반복하면 매우 불편하고 시간 소요도 클 것이다. 이것을 바인드 마운트가 도와준다.
호스트 컴퓨터의 절대경로로 현재 app이 지정하는 파일 경로를 붙여서 다음과같이 run 을 실행한다. ( 한 줄로 구성된 명령어입니다. )
3. Docker 확장 프로그램을 설치하고 다운로드 받았던 프로젝트 안에 Dockerfile 을 만들어준다.
/Dockerfile
# 위에서 도커 허브 node 이미지를 기반으로 로컬로 다운로드 및 캐싱 되었기 때문에 이미지를 가져올 수 있다.
FROM node
# 만약 컨테이너 안의 이미지의 경로가 /app 이런식으로 되어있다면 작업할 div 경로를 설정할 수도 있다.
# 설정해주면 COPY 의 두번째 경로를 ./ 이것으로 했을 때 자동으로 /app 경로가 된다.
WORKDIR /app
# 어떤 파일이 이미지에 들어가야 하는지
# 첫 번째 .은 이 프로젝트의 모든 폴더 및 파일들 (Dockerfile을 제외한)
# 두 번째 .은 파일을 저장할 컨테이너 내부 경로 (ex /app)
COPY . /app
# 이미지를 받으면 npm install을 자동으로 해줌
RUN npm install
# 도커에게 우리가 서버를 실행할 포트를 말해준다.
EXPOSE 80
# 이미지가 생성될 때 실행되지 않고 컨테이너가 실행될 때 수행하는 명령어
CMD ["node", "server.js"]
4. 터미널에서 아래 명령어를 입력해보자
$docker build .
위에 생성된 image ID 를 복사해서 docker run을 해보면,
이렇게 컨테이너가 실행된 것을 확인할 수 있다.
이제 이미지로 만들었고 컨테이너 실행도 했겠다 80번 포트에 열린 사이트를 확인해 보자.
??? 화면이 뜨질 않는다 이 이유는 아래에 나온다.
5. docker ps 명령어를 입력하면 현재 실행중인 프로세스를 확인할 수 있다.
컨테이너 실행도 잘 되었다. 하지만 우리는 문서상으로만 포트 80번을 expose 한다고 명시하였고, 실제로 컨테이너안의 포트를 로컬 포트에 할당시켜주질 않았다. 이는 run 명령어를 실행할 때 쓰면 된다. 일단 컨테이너를 중지시키고 포트를 할당해준 뒤 다시 시작해보자.
$ docker stop '위에서 확인한 컨테이너의 이름'
포트를 할당해주고 다시 실행한다. (위에서 만들었던 컨테이너의 id 를 씁니다.)
$ docker run -p 3000:80 sha256:5ed67ec470b78df4b721d82a9ae3924059e432e58d710782d2f73a6efd6dbb75
그 다음 웹을 켜보면
로컬 3000번 포트에 컨테이너의 80번 포트가 할당되어 잘 나타나는 것을 볼 수 있다.
이미지는 읽기 전용이기 때문에 이 방법으로는 만약 변경사항이 생겨 적용하고 싶으면 이미지를 다시 빌드해야 한다.
또한 Dockerfile 이미지 레이어에는 캐시를 사용하는데, 빌드했었던 이미지를 다시 빌드할 때 변경사항이 없으면 매우 빠르게 빌드된다.
이것을 이용해서 다음과 같이 npm package에 대한 변경사항이 없을 때 npm install 을 빠르게 넘어갈 수도 있다
/Dockerfile
# 위에서 도커 허브 node 이미지를 기반으로 로컬로 다운로드 및 캐싱 되었기 때문에 이미지를 가져올 수 있다.
FROM node
# 만약 컨테이너 안의 이미지의 경로가 /app 이런식으로 되어있다면 작업할 div 경로를 설정할 수도 있다.
# 설정해주면 COPY 의 두번째 경로를 ./ 이것으로 했을 때 자동으로 /app 경로가 된다.
WORKDIR /app
# package.json 파일을 복사한다. 만약 다시 빌드할 때 변경사항이 없을 경우 npm install까지 그냥 넘어간다.
COPY package.json /app
# 이미지를 받으면 npm install을 자동으로 해줌
RUN npm install
# 어떤 파일이 이미지에 들어가야 하는지
# 첫 번째 .은 이 프로젝트의 모든 폴더 및 파일들 (Dockerfile을 제외한)
# 두 번째 .은 파일을 저장할 컨테이너 내부 경로 (ex /app)
COPY . /app
# 도케에게 우리가 서버를 실행할 포트를 말해준다.
EXPOSE 80
# 이미지가 생성될 때 실행되지 않고 컨테이너가 실행될 때 수행하는 명령어
CMD ["node", "server.js"]
Docker란 컨테이너를 생성하고 관리하기 위한 도구이다. 여기서 컨테이너란 표준화된 소프트웨어 유닛을 말한다.
기본적으로 해당 코드를 실행하는데 필요한 종속성과 도구가 포함된 패키지라고 보면 된다. 컨테이너라는 곳에 우리가 개발을 한 환경 자체를 넣어버리고 그 환경을 다른 곳에서 불러오면 문제없이 동일하게 사용할 수 있다.
2. 컨테이너가 왜 필요한가?
소프트웨어에서 왜 독립적인 표준화된 애플리케이션 패키지를 원하는지에 대한 이유를 생각해보면 컨테이너의 중요성을 좀 더 정확하게 깨닫게 된다. 도커의 주요 사용 사례 중 하나는 우리가 종종 다른 개발 제품 생산 환경을 가지게 될 때 환경 별로 관리하기 위한 사례인데,
예를 들어 Node js 버전이 14.3인 환경에서
다음과 같은 코드를 짰을 때 에러가 뜨는 것을 확인할 수 있고 이에 따라 우리는 14.13 보다 높은 버전이 필요한 것을 알게 된다.
이와 같이 버전 문제로 인해 문법에 에러가 뜨는데 우리가 사용할 수 있는 환경이 로컬밖에 없다면 이 코드는 아예 사용할 수 없을 것이다.
그렇다고 코드 자체를 버리기엔 애매한 상황이 있을 때 우리가 로컬 환경 자체를 바꾼다고 해서(Node js 버전 업그레이드 등)는 상당한 소요가 필요할 것이다. (이 코드 뿐만 아니라 다른 곳에서 에러가 뜰 수도 있기 때문에)
이 때 애플리케이션이 자체적으로 필요한 Node 버전을 제공하는 컨테이너를 사용한다면 테스트를 해보기에 훨씬 편리할 수 있다.
물론 프로젝트가 몇 개 안되고 모든 프로젝트가 하나의 버전을 똑같이 사용한다면 별로 필요가 없겠지만, 큰 회사에서 복잡한 프로젝트들이 여러개 있을 경우 각각의 프로젝트에 적합한 버전이 있을 수도 있을 것이고, 그 복잡한 프로젝트를 똑같은 버전으로 만드는 것 자체가 큰 일이 될 수도 있다고 생각되었다. 그럴 때 컨테이너를 여러개 만들고 버전별로 관리하면 상당히 유용할 것이다.
3. 가상 머신과 도커 컨테이너의 차이점?
물론 위의 상황이 있을 때 가상머신으로도 버전 별로 프로젝트를 관리하는 것은 가능하다. 하지만 가상 머신을 사용할 때 가장 큰 문제점은 오버헤드이다. 컴퓨터 안에 컴퓨터를 집어넣는 행동을 여러번 반복하다보면 메모리, cpu, 내 하드 드라이브의 공간을 낭비하게 될 것이고 컴퓨터는 계속 무거워지고 나중엔 오버헤드가 발생할 것이다. 실제로 학생 때 가상 머신을 사용해봤는데 가상 환경을 한 개만 만들었어도 컴퓨터 성능이 별로 좋지 않으면 내 컴퓨터 자체가 느려지는 것을 볼 수 있었다. 이에 비해 컨테이너는 자체에 OS를 가지고 있고, 가상 머신보단 훨씬 가벼운 느낌이 있다.