실행 환경
- VirtualBox 6.1.50
- CentOS
🐳Dockerfile 기본 명령어
FROM
FROM <image>:<tag>
FROM ubuntu:16.04
- 베이스 이미지를 지정한다.
- 반드시 지정해야 하며 어떤 이미지도 베이스 이미지가 될 수 있다.
- tag 는 될 수 있으면 latest(기본값)보다 구체적인 버전(16.04)를 지정하는 것이 좋다
- 이미 만들어진 다양한 베이스 이미지는 도커 허브에서 더 볼 수 있다.
MAINTAINER
MAINTAINER <name>
MAINTAINER anna@anna.com
- Dockerfile을 관리하는 사람의 이름 또는 이메일을 정보를 적는다.
- 빌드에 영향은 주지 않는다.
COPY
COPY <src>... <dest>
COPY . /usr/src/app
- 파일이나 디렉토리를 이미지로 복사한다.
- 일반적으로 소스를 복사하는데 사용한다.
- 타겟 디렉토리가 없다면 자동으로 생성한다.
ADD
ADD <src>... <dest>
ADD . /usr/src/app
- COPY 명령어와 매우 유사하다
- src 에 파일 대산이 url을 입력할 수 있다.
- src 에 압축 파일을 입력하는 경우 자동으로 압축을 해제하며 복사된다.
RUN
RUN <command>
RUN ["executable", "param1", "param2"]
RUN bundle install
- 명령어를 그대로 실행한다.
- 내부적으로 /bin/sh -c 뒤에 명령어를 실행하는 방식이다.
CMD
CMD ["executable","param1","param2"]
CMD command param1 param2
CMD bundle exec ruby app.rb
- 도커 컨테이너가 실행되었을 때 실행되는 명령어를 정의한다.
- 빌드 할 때는 실행되지 않는다.
- 여러 개의 CMD가 존재하는 경우 가장 마지막 CMD만 실행된다.
- 한꺼번에 여러 개의 프로그램을 실행하고 싶은 경우 run.sh 파일을 작성하여 데몬으로 실행하거나 supervisord나 forego와 같은 여러 개의 프로그램을 실행하는 프로그램을 사용한다.
WORKDIR
WORKDIR /path/to/workdir
- RUN, CMD, ADD, COPY 등이 이루어질 기본 디렉토리를 설정한다.
- 각 명령어의 현재 디렉토리는 한줄 한줄마다 초기화 되기 때문에 RUN cd /path하더라도 다음 명령어에서는 다시 위치가 초기화 되기 때문이다.
- 그래서 같은 디렉토리에서 계속 작업하기 위해서는 WORKDIR을 사용한다.
EXPOSE
EXPOSE <port> [<port>...]
EXPOSE 4567
- 도커 컨테이너가 실행되었을 때 요청을 기다리고 있는 (Listen) 포트를 지정한다.
- 여러 개의 포트도 지정 할 수 있다.
VOLUME
VOLUME ["/data"]
- 컨테이너 외부에 파일 시스템을 마운트 할 때 사용한다.
- 반드시 지정하지 않아도 마운트 할 수 있지만 기본적으로 지정하는 것이 좋다.
ENV
ENV <key> <value>
ENV <key>=<value> ...
ENV DB_URL mysql
- 도커파일 또는 컨테이너에서 사용할 환경변수를 지정한다.
- 컨테이너를 실행할때 -e 옵션을 사용하면 기존 값을 오버라이딩 하게 된다.
ARG
ARG <key>=<value>
ARG myAddress = anna.com
- ENV 명령어와 비슷하지만 ARG는 도커파일 내에서만 사용하는 환경변수를 지정한다.
자세한 내용은 공식문서를 참고하자.
🐳Dockerfile 빌드 과정
# 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)
FROM ubuntu
MAINTAINER anna@anna.com
RUN apt-get -y update
# 2. ruby 설치
RUN apt-get -y install ruby
RUN gem install bundler
# 3. 소스 복사
COPY . /usr/src/app
# Gem 패키지 설치
WORKDIR /usr/src/app
RUN bundle install
RUN bundle add rackup
# 5. Sinatra 서버 실행 (Listen 포트 정의)
EXPOSE 4567
CMD bundle exec ruby app.rb -o 0.0.0.0
지난 게시물 → [Docker] Docker 이미지 만들고 컨테이너 생성하기
지난번에 간단한 웹 어플리케이션을 이미지로 만들기 위해 Dockerfile을 작성하고 컨테이너를 생성해보았다. 위 코드는 그때 작성한 Dockerfile 이다.
이 Dockerfile을 참고해서 도커파일의 빌드 과정을 알아보자.
Sending build context to Docker daemon 5.12 kB <-- (1)
Step 1/10 : FROM ubuntu <-- (2)
---> f49eec89601e <-- (3)
Step 2/10 : MAINTAINER anna@anna.com <-- (4)
---> Running in f4de0c750abb <-- (5)
---> 4a400609ff73 <-- (6)
Removing intermediate container f4de0c750abb <-- (7)
Step 3/10 : RUN apt-get -y update <-- (8)
...
...
Successfully built 20369cef9829 <-- (9)
위는 도커파일을 빌드하면 출력되는 로그들 이다.
한줄한줄 뜯어서 봐보자
(1) Sending build context to Docker daemon 5.12 kB
- 빌드 명령어를 실행한 디렉토리 파일들을 빌드 컨텍스트(build context)라고 하고 이 파일들을 도커 서버로 전송한다.
- 도커는 서버-클라이언트 구조이므로 도커 서버가 작업을 하려면 미리 파일을 전송해야 한다.
(2) Step 1/10 : FROM ubuntu
- 작성해두었던 Dockerfile을 한 줄 한 줄 수행한다.
- 베이스 이미지로 지정한 ubuntu 이미지를 다운받는다.
(3) ---> f49eec89601e
- 명령어 수행결과를 “이미지로 저장한다.”
- ubuntu 이미지의 id가 출력된다.
(4) Step 2/10 : MAINTAINER anna@anna.com
- Dockerfile의 두 번째 명령어인 MAINTANIER 를 수행한다.
(5) ---> Running in f4de0c750abb
- 명령어를 수행하기 위해 바로 이전에 생성한
f49eec89601e
이미지를 기반으로f49eec89601e
”컨테이너를 임시로 생성하여 실행”한다.
(6) ---> 4a400609ff73
- 명령어 수행 결과를 “이미지로 저장한다.”
(7) Removing intermediate container f4de0c750abb
- 명령어 수행하기 위해 임시로 생성해 두었던 “컨테이너를 제거한다.”
(8) Step 3/10 : RUN apt-get -y update
- Dockerfile의 세 번째 명령어를 수행한다.
- 이전 단계와 마찬가지로
- 전에 만들어진 이미지를 기반으로 임시 컨테이너를 생성하고
- 명령어를 실행하고
- 그 결과를 이미지로 만들어 저장하고
- 임시 컨테이너를 제거한다.
- 위 과정을 마지막 줄까지 반복한다.
(9) Successfully built 20369cef9829
- 최종 성공한 이미지의 ID를 출력한다.
결론
도커 빌드는 임시 컨테이너 생성
> 명령어 수행
> 이미지로 저장
> 임시 컨테이너 삭제
> 새로 만든 이미지 기반 임시 컨테이너 생성
> 명령어 수행
> 이미지로 저장
> 임시 컨테이너 삭제
> … 의 과정을 계속해서 반복한다.
명령어를 실행할 때마다 이미지 레이어를 저장하고 다시 빌드할 때 Dockerfile이 변경되지 않았다면 기존에 저장된 이미지를 그대로 캐시처럼 사용한다.
이러한 레이어 개념을 잘 이해하고 있어야 최적화된 이미지를 생성할 수 있다.
- 실제 log를 봤을때 이전에 한번 빌드 과정이 있었기 때문에 2, 3, 4번 과정은 Cache 되어 있던 레이어를 사용 했음을 확인 할 수 있었다.
🐳Dockerfile 리팩토링
# 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)
FROM ubuntu
MAINTAINER anna@anna.com
RUN apt-get -y update
# 2. ruby 설치
RUN apt-get -y install ruby
RUN gem install bundler
# 3. 소스 복사
COPY . /usr/src/app
# Gem 패키지 설치
WORKDIR /usr/src/app
RUN bundle install
RUN bundle add rackup
# 5. Sinatra 서버 실행 (Listen 포트 정의)
EXPOSE 4567
CMD bundle exec ruby app.rb -o 0.0.0.0
사실 앞서 만들었던 이미지에는 몇가지 최적화 문제가 있다.
이를 Dockerfile 수정을 통해 최적화를 해보자.
Base Image
위에서 만든 Ruby 어플리케이션 이미지는
ubuntu
를 베이스로 만들었지만 사실은 더 간단한 ruby 베이스 이미지가 존재한다.기존의 ruby를 설치했던 명령어는 ruby 이미지를 사용하는 것으로 간단하게 생략할 수 있다.
진작 알려줬으면 .. 그고생은 …
# before FROM ubuntu MAINTAINER anna@anna.com RUN apt-get -y update RUN apt-get -y install ruby RUN gem install bundler # after FROM ruby:3.0 MAINTAINER anna@anna.com
ruby 외에도 node.js, python, java, go 등 다양한 베이스 이미지가 존재하며 세부적인 설정이 필요하지 않다면 그대로 사용하는 게 간편하다.
Build Cache
- 도커파일을 다시 빌드하게 되면 굉장히 빠르게 빌드 되는데 이는 위에서 말했다시피 이미지를 빌드하는 과정에서 각 단계를 이미지 레이어로 저장하고 다음 빌드에서 캐시로 사용한다.
- 도커는 빌드 할 때 도커 파일의 명령어가 수정되었거나 추가하는 파일이 변경 되었을 경우에만 캐시가 깨지고 그 이후 작업들은 새로 이미지를 만들게 된다.
- ruby gem을 설치하는 과정은 꽤 많은 시간을 소요하는데 최대한 캐시를 이용하여 빌드시간을 줄일 수 있다.
- 기존 소스에서 소스파일이 수정되면서 깨지는 부분은 다음과 같다.
COPY . /usr/src/app # <- 소스파일이 변경되면 캐시가 깨짐
WORKDIR /usr/src/app
RUN bundle install # 패키지를 추가하지 않았는데 또 인스톨하게 됨 ㅠㅠ
- 복사하는 파일이 이전과 다르면 캐시를 사용하지 않고 그 이후의 명령어는 다시 실행된다.
- ruby gem 패키지를 관리하는 파일은 Gemfile 이고 Gemfile은 잘 수정되지 않으므로 다음과 같이 순서를 변경하여 ruby gem을 캐시하여 사용하도록 바꿀 수 있다.
COPY Gemfile* /usr/src/app/ # Gemfile을 먼저 복사함
WORKDIR /usr/src/app
RUN bundle install # 패키지 인스톨
COPY . /usr/src/app # <- 소스가 바꼈을 때 캐시가 깨지는 시점 ^0^
- gem 설치하는 부분을 소스파일 이전으로 옮겼다.
- 이제 소스코드가 수정되더라도 매번 gem을 설치하지 않아 더욱 빠르게 빌드 가능하다.
- 다른 언어 패키지를 쓰더라도 똑같이 적용하여 빌드시간을 줄이자.
명령어 최적화
- 이미지를 빌드할때 불필요한 로그는 무시하는게 좋고 패키지 설치시 문제 파일도 생성할 필요가 없다.
# before
RUN apt-get -y update
# after
RUN apt-get -y -qq update
-qq
옵션으로 로그를 출력하지 않게 한다.- 각종 리눅스 명령어는 보통
quite
옵션이 있으니 적절하게 적용하자.
# before
RUN bundle install
# after
RUN bundle install --no-rdoc --no-ri
--no-doc
과--no-ri
옵션으로 필요 없는 문서를 생성하지 않아 이미지 용량도 줄이고 빌드 속도도 더 빠르게 할 수 있다.
이쁘게
- 명령어는 비슷한 것끼리 묶어 주는 게 보기도 좋고 레이어 수도 줄일 수 있다.
- 도커 이미지는 스토리지 엔진에 따라 레이어의 개수가 127개로 제한되어 있는 경우도 있어 너무 많은 명령어는 좋지 않다.
# before
RUN apt-get -y -qq update
RUN apt-get -y -qq install ruby
# after
RUN apt-get -y -qq update && \
apt-get -y -qq install ruby
최종
# 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)
FROM ruby:3.0
MAINTAINER anna@anna.com
COPY Gemfile* /usr/src/app/
WORKDIR /usr/src/app
RUN bundle install --no-rdoc --no-ri
COPY . /usr/src/app
EXPOSE 4567
CMD bundle exec ruby app.rb -o 0.0.0.0
다음에는 도커허브에 대해 간단히 알아보고 배포에 대해서 간단히 알아보자.
📎 참고 링크
https://subicura.com/2017/02/10/docker-guide-for-beginners-create-image-and-deploy.html
'DevOps > Docker' 카테고리의 다른 글
[Docker] Docker 이미지 만들고 컨테이너 생성하기 (0) | 2024.01.31 |
---|---|
[Docker] Docker 컨테이너 실행하기 (2) | 2024.01.26 |
[Docker] 도커의 기본 명령어 (0) | 2024.01.25 |
[Docker] 도커 엔진 아키텍처 (0) | 2024.01.25 |
[Docker] 도커 이미지(Image)와 컨테이너(Container) (1) | 2024.01.25 |