Dockerfile이란 도커 컨테이너를 빌드하기 위한 스크립트로 Docker 이미지를 생성하는 데 사용된다.
Docker를 처음 배우게 되면 Docker의 다양한 기술과 기능, 문법들 때문에 헷갈리기도 하고, 어떻게 구동되고 돌아가는지 어려울 때가 많다.
Docker를 이미 배우고 시작한 분들이라면 어렵지 않은 내용이지만 처음에는 많이 생소하고 본인조차도 엄두가 나질 않았다.
어떻게 하면 Docker를 이해할 수 있을까... 고민하다가 Docker 컨테이너를 띄우기 위해 생성되는 이미지를 만들어 보는 것부터 시작하는 것이 맞지 않을까 하는 생각에 글을 작성하게 됐다.
한 번에 많은 내용을 익히려고 하면 엄두가 나지 않기 때문에 하나씩 배워서 천천히 익혀나가는 것이 멀리 봤을 때는 오히려 더 오래 남을 것이다.
Dockerfile이란 무엇인가?
Dockerfile은 Docker의 명령어를 이용해서 Docker 이미지를 생성하기 위해 정의하고 구성하는 파일이며,
컨테이너 내부 환경을 정의하고 필요한 소프트웨어를 설치하는 데 사용된다.
Dockerfile은 문서의 확장자 없이 텍스트 편집기를 통해 새로운 파일을 생성하고 파일 이름을 'Dockerfile' 이라고 생성하면 Dockerfile의 작성 준비는 끝이다.
파일을 생성했다면, Dockerfile의 명령어 FROM, WORKDIR, COPY, RUN, EXPOSE 등을 사용해서 만들고자 하는 이미지의 내용을 추가해 주고, Command에서 Docker 명령어를 통해 이미지 빌드를 하면 된다.
Dockerfile의 중요성
Dockerfile은 Docker 이미지를 빌드하는 데 있어 핵심적인 역할을 한다. Dockerfile을 간단하게 생각하면 이미지를 만들기 위한 명세서라고 생각하면 된다.
1. 이미지가 문제가 있거나 변경이 필요한 경우에 언제든지 원하는 데로 변경을 해서 새로운 이미지를 만들 수 있다.
예전에 서버 시스템 개발을 하면서 Dockerfile을 이용해 이미지를 만들 때 다양한 버전을 만들어 테스트가 가능하여 업무 효율성을 높여줬던 기억이 있다.
2. 언제든 변경이 가능하기 때문에 Dockerfile을 통해서 이미지의 버전관리가 가능하며, 코드 저장소에 포함하여 협업의 용이성도 좋다.
실제 팀원들과 협업을 하면서 각자가 만든 이미지를 코드 저장소에 관리를 하며 서로 만든 이미지를 공유하며 손쉽게 작업을 하기도 했다.
3. 버전 관리를 통해 이전 이미지에서 변경된 부분만 다시 빌드되기 때문에 빠르고 효율적인 관리가 가능하다.
이미지 작업을 하다보면 만들고자 하는 이미지의 용량이 커서 빌드하는 데 오랜 시간이 걸리는 경우가 종종 있다. 그런데 이때마다 이미지를 새로 만든다면 그만큼 작업 시간이 늘어나는데, Dockerfile을 이용해 이미지 버전관리를 한다면 이미지가 무겁더라도 최초 한 번만 오래 걸리고 그 뒤로는 시간이 매우 많이 단축되는 효과가 있다.
Dockerfile 명령어
Dockerfile을 작성하기 위해서 명령어를 알아야 한다. 이미지를 만들기 위해서 다양한 명령어가 있지만 한번에 너무 많은 이미지를 익히려면 부담이 되기 때문에 대중적으로 많이 사용하는 명령어 위주로 정리를 해봤다.
FROM
이미지를 생성할 때 베이스가 되는 이미지 설정 명령어다.
검증이 된 이미지 위주로 설정하여 기본 베이스가 되도록 하고, 베이스 이미지에 작성자가 원하는 데로 만들어 간다고 생각하면 된다.
예시: FROM mysql:latest
WORKDIR
이미지 내에서 작업 디렉토리를 설정하는 명령어로 이미지가 빌드되면서 작업이 필요한 디렉터리 설정이 가능하다.
예시: WORKDIR /app/work/
COPY
해당 명령어는 Dockerfile의 이미지 빌드를 실행한 작업 환경의 파일이나 디렉토리를 이미지 내의 원하는 디렉터리로 복사하는 명령어다.
복사할 때에는 좌측이 작업 환경 (로컬), 우측이 이미지 디렉터리라고 보면 된다.
예시: COPY ./source.txt /app/source/
RUN
이미지 빌드시 쉘 명령어를 실행할 수 있도록 도와주는 명령어다. 이미지 빌드를 하면서 초기에 실행이 꼭 필요한 명령어가 있을 경우에 사용하기 적합하다.
예시: RUN apt-get update && apt-get install -y curl
EXPOSE
해당 명령어는 이미지 빌드 후 리스닝할 때 해당 이미지의 포트를 지정하는 명령어다.
예시: EXPOSE 8080
CMD
이미지 빌드 후 컨테이너를 실행할 때 실행할 명령어나 애플리케이션을 지정하는 역할을 하는 명령어다.
해당 명령어는 사용자가 컨테이너를 실행할 때 명령어를 인자값으로 전달해 유연하게 명령어를 변경하여 사용할 수 있다.
예시: CMD ["echo", "Hello!"]
컨테이너 실행 : docker run echo hi!
-> cmd 명령어는 echo hi!로 덮어 씌게 된다.
CMD 명령어는 RUN 명령어와 헷갈릴 수 있는데 실행되는 시점만 잘 숙지한다면 어렵지 않을 것이다.
RUN : 이미지가 빌드될 때 실행
CMD : 이미지가 빌드된 후 컨테이너가 실행될 때 실행
ENTRYPOINT
해당 명령어는 컨테이너를 실행할 때 실행 파일이나 스크립트를 정의할 때 사용되는 명령어다. CMD와 유사하지만 CMD와는 다르게 docker run 실행 시 명령어를 덮어쓰지 않는다는 차이점을 가지고 있으며, CMD와 조합하여 사용할 수 있다.
예시 : ENTRYPOINT python main.py
# CMD와 조합 예시
ENTRYPOINT python main.py
CMD param1
#########################################
# 컨테이너 실행 1
docker run
# 실행 결과
python main.py param1
#########################################
# 컨테이너 실행 2
docker run param2
# 실행 결과 2
python main.py param2
위와 같이 두 명령어간의 차이점이 있으며, 조합해서 사용이 가능하다.
Dockerfile 작성 예시
이번에는 위에서 정리한 명령어를 몇 개 사용해서 간단한 예시를 작성해 보려고 한다.
# 베이스 이미지 node
FROM node:14
# 작업 디렉토리 이동
WORKDIR /usr/src/app
# 현재 로컬 디렉토리의 소스코드 디렉토리를 컨테이너 내의 /usr/src/app 디렉토리로 복사
COPY ./workspace/sourcecode .
# 애플리케이션 의존성 설치
RUN npm install
# 포트 설정
EXPOSE 3000
# 컨테이너 실행시 명령어
CMD ["npm", "start"]
위의 Dockerfile은 node 이미지를 베이스 가져와 간단하게 소스파일을 컨테이너 내부로 복사하고 npm 설치 후 실행될 수 있도록 작성한 내용이다.
그럼 위에서부터 하나씩 정리해 보면,
1. FROM
가장 먼저 FROM 명령어를 통해서 node의 기본 이미지를 베이스로 설정한다.
2. WORKDIR
이미지 내부의 작성 대상 경로를 /usr/src/app으로 이동한다.
3. COPY
로컬에서 작업한 소스코드 파일을 이미지 내부의 작업 대상 경로로 복사한다.
4. RUN
npm을 사용하여 애플리케이션의 의존설을 설치한다.
5. EXPOSE
컨테이너가 실행되면 사용할 포트를 3000으로 설정한다.
6. CMD
컨테이너가 시작될 때 실행될 명령을 지정한다.
위에서 정리했던 명령어 몇개로 간단하게 Dockerfile 작성하는 방법을 정리해 보았다. 이론으로만 정리할 때보다 직접 작성해 보는 것이 이해하는데 훨씬 도움이 된다.
Dockerfile 작성 팁
Dockerfile 작성을 하고 이미지 빌드를 하다보면 필요한 부분을 추가하면서 명령어별로 크게 생각하지 않고 작성할 수 있다. 그러나 각 명령어의 특성과 Dockerfile의 기능을 잘 이해한다면 간단한 차이로 큰 효과를 볼 수 있다.
1. 단계별 캐시 (수정 빈도가 적은 명령어를 먼저 작성)
명령어를 나열할 때 자주 변경되지 않는 단계를 먼저 추가하고 변경 가능성이 높은 명령어를 나중에 작성을 한다.
이렇게 작성을 하면 나중에 docker 빌드를 진행할 때 이전에 실행했던 명령어가 변경이 없으면 캐시를 활용하여 빠른 빌드를 진행할 수 있다.
2. 파일 복사시 불필요한 파일 제외
COPY 명령어를 사용할 때 무턱대고 디렉터리 전체 또는 '*'를 사용하게 되면 불필요한 파일이나 디렉터리가 포함될 수 있다. 그렇게 되면 빌드되는 이미지의 크기가 늘어날 수 있으므로 COPY를 사용할 때에는 반드시 확인을 하는 것이 중요하다.
3. 다중 스테이지 빌드
하나의 Dockerfile에서 여러 스테이지로 나눠서 불필요한 의존성이나 파일을 최소화하고 최종 이미지를 최적화할 수 있다.
이 부분을 잘 몰랐을 때에는 FROM 명령어를 여러번 쓰면서 생각했던 이미지가 나오지 않아 이슈가 있던 적이 있었다.
# 빌드 스테이지
FROM builder as build
COPY source.txt /app
RUN execute
# 최종 이미지
FROM base
COPY --from=build /app /app
4. 최신 베이스 이미지 업데이트
사용되는 베이스 이미지는 가능한 한 최신 버전으로 사용하여 보안 업데이트 및 최적화하는 것이 좋다.
그렇다고 실제 운영중인 이미지를 검토하지 않고 업데이트를 하게 되면 운영에 큰 타격이 있을 수 있기 때문에 버전 업데이트는 항상 신중히 해야 한다.
5. 불필요한 패키지 제거
이미지 빌드 과정에서 패키지를 설치한 후, 불필요한 패키지는 제거를 하여 이미지를 최적화시키는 것이 좋다.
Docker는 가상으로 운영을 하는 것이지만 결국 실제 서버나 장비의 자원을 가져와 쓰는 것이기 때문에 항상 이미지 최적화하는 것을 신경 써줘야 한다.
본인은 이미지 작업을 하면서 이 부분을 크게 생각하지 않고 무작위로 이미지를 만들다가 장비의 자원이 바닥났던 경우가 자주 있었다. 그래도 자원을 최대한 쓰면서 개발을 해본 경험이 나쁘지는 않았던 것 같다.
마무리
처음 Docker를 배우고 개발을 할 때에는 정말 생소하면서 너무 헷갈리고 어려웠던 기억이 있다. 명칭은 어디서 다 들어본 것 같은데 이해는 되지 않고... 하지만 Docker도 소프트웨어 기술의 한 부분이고 다시 하나씩 천천히 정리해 가면서 사용하다 보니 금방 익숙해졌다.
현재 진행하고 있는 과제에서는 Docker를 크게 사용하고 있진 않지만 생각보다 많은 시스템 환경이 Docker로 돌아가고 있기 때문에 조금씩이나마 이렇게 하나씩 정리를 해가며 복습을 하려고 습관을 들이고 있다.
다음 글을 쓸 때에는 실제 서비스하려고 만든 소스코드를 Dockerfile로 이미지를 만들어 서버에 실행해보는 내용을 정리해 봐야겠다.
'Docker > docker' 카테고리의 다른 글
Docker를 활용한 Nginx 서버 구성하기 (1) | 2024.03.23 |
---|---|
서비스의 효율을 높여주는 도커 이미지에 대한 정리! (1) | 2024.01.21 |
도커 Compose로 애플리케이션 관리하기 (1) | 2023.10.17 |