Docker 빌드(1)

뾰족머리삼돌이·2024년 11월 21일

서버

목록 보기
3/10

Docker Build

Docker의 Build는 Client-Server 아키텍처를 띄고있다.

  • Client( Buildx ) : 빌드를 실행 및 관리에 대한 UI
  • Server( BuildKet ) : 빌드 실행 처리

만약, 빌드를 실행하면 Buildx 클라이언트에서 백엔드의 BuildKet으로 빌드 요청을 전달한다.
BuildKit은 요청에 포함된 빌드명령을 해석하고 빌드단계를 실행시킨다.

빌드의 결과물은 클라이언트에게 반환되거나 DockerHub같은 레지스트리에 저장된다.

Buildx와 BuildKit은 모두 Docker Desktop, Docker Engine과 함께 기본적으로 설치된다.
즉, docker build 명령을 실행한다면 Buildx로 빌드를 실행할 때, Docker와 함께 제공되는 BuildKit을 사용하는 것이다.

Docker Desktop, Docker Engine과 함께 설치되기 때문에, 윈도우에서는 그냥 Docker Desktop 쓰면 된다

Buildx

Buildx는 Docker에서 빌드실행에 사용되는 CLI 툴을 의미한다.

docker build 명령어는 Buildx를 감싸는 형태를 띈다.

Buildx는 단순히 빌드를 실행하는 것 외에도, BuildKit 백엔드를 만들고 관리할 수 있다.
또한, 레지스트리에서 이미지를 관리하고 여러 빌드를 동시에 실행할 수도 있다.

관련된 정보는 GitHub Readme 에서 확인할 수 있다.


Docker Desktop이 설치된 환경에서 --help를 쳐보면 Buildx가 설치되어 있음을 확인할 수 있다.

BuildKit

BuildKit은 실제 빌드의 실행작업을 처리하는 데몬 프로세스다.
docker build 명령을 통해 Buildx에서 빌드요청을 전달받으면서 동작이 진행된다.

이 빌드요청에 포함되는 정보는 아래와 같다.

  • DockerFile
  • Build arguments
  • Export 옵션
  • Caching 옵션

BuildKit이 빌드작업을 수행하는 동안 Buildx에서 진행사항을 모니터링하고 터미널에 출력한다.
즉, 빌드작업에서 추가적인 자원이 필요한 경우 Buildx를 이용해 사용자에게 자원을 요청한다.

BuildKit의 핵심은 LLB(Low-Level Build) 형식이다.

DockerFile을 통해 Build 명령이 BuildKit으로 도착하면, 이는 LLB형식으로 변환되어 실제 빌드과정에 사용된다.
즉, LLB는 효율적인 이미지 빌드와 캐시 최적화, 분산 빌드등을 지원하기 위해 변환되는 바이너리 중간 포맷을 의미한다.

LLB에 대한 추가적인 정보는 readme를 참고하자

Dockerfile

Dockerfile은 이미지 빌드를 위해 처리해야할 작업을 모아놓은 지침서를 의미한다.
소스코드를 실행하기 위한 가이드라인을 적어놓은 텍스트 문서의 형태를 띈다.

대표적으로 사용되는 지침들을 살펴보면 아래와 같다.

InstructionDescription
FROM <image>기준이 될 이미지를 정의
RUN <command>기준이 된 이미지 위에서 명령어를 실행하고 결과를 커밋
WORKDIR <directory>RUN, CMD, ENTRYPOINT, COPY 등의 지침들을 실행할 작업 디렉토리 정의
COPY <src> <dest>src의 파일이나 디렉토리를 복사하여 컨테이너의 dest위치에 추가
CMD <command>이미지 빌드를 통해 컨테이너를 생성 및 실행할 때, 동작한 기본 프로그램 정의
DockerFile마다 하나만 있어야하며, 여러개인 경우에는 마지막 명령만 실행됨

DockerFile 작성을 위해 생성되는 파일명은 DockerFile 이어야하며, 확장자는 필요하지 않다.
특정 상황에서는 별도의 Dockerfile이 필요할 수 있는데, 이 경우에는 <something>.Dockerfile 형식의 파일을 작성하면 된다. 이후, 빌드 명령에서 --file 플래그를 이용해서 실행될 DockerFile을 지정해줘야 한다.

Dockerfile 예시

우분투 환경에서 Spring Boot 애플리케이션을 실행한다고 가정해보자

# 우분투 22.04 버전 이미지 사용
FROM ubuntu:22.04 

우선 실행시킬 OS 환경에 우분투이기 때문에 DockerFile을 작성할 때, 기준이 될 이미지를 우분투로 설정해줘야 한다.


# jdk 설치
RUN apt-get update && apt-get install -y openjdk-17-jdk

이후에는 우분투 환경에서 jar 파일을 동작시켜야 하므로, 우분투 이미지로 생성된 컨테이너에서 동작할 명령어를 작성해줘야 한다.


# jar 파일을 컨테이너로 이동
COPY vanilla-0.0.1-SNAPSHOT.jar /

이미지를 통해 생성된 컨테이너에서 Spring Boot 애플리케이션을 동작시켜야하므로, java 빌드를 통해 생성한 jar파일을 컨테이너의 루트 디렉토리(/)로 옮긴다.


# 실행된 컨테이너에서 jar파일 동작
CMD java -jar vanilla-0.0.1-SNAPSHOT.jar

최종적으로 컨테이너가 실행되고나서 동작할 명령을 작성해준다.


# 우분투 22.04 버전 이미지 사용
FROM ubuntu:22.04 

# jdk 설치
RUN apt-get update && apt-get install -y openjdk-17-jdk

# jar 파일을 컨테이너로 이동
COPY vanilla-0.0.1-SNAPSHOT.jar /

# 실행된 컨테이너에서 jar파일 동작
CMD java -jar vanilla-0.0.1-SNAPSHOT.jar

모아놓으면 이런 형태가 될 것이다.

빌드명령은 위와 같은 형식으로 docker build를 통해 실행할 수 있다.

이때 -t 플래그는 생성될 컨테이너의 태그를 작성하는 것이다.
즉, 생성되는 이미지는 test 라는 이름이 될 것이며 이미지의 버전(태그)는 lastest가 될 것이다.
또한, 뒤에있는 .은 현재 위치의 DockerFile을 이용해 빌드하라는 의미가 된다.

해당 명령을 실행한 폴더에는 위와 같은 파일이 존재한다.


실제로 생성되는 이미지를 확인해보면, 이름에 test, 태그에 lastest가 입력된 것을 확인할 수 있다.


이후에 docker run 명령을 통해 이미지를 실행시키면 컨테이너가 생성되고, 애플리케이션이 동작하는 것을 확인할 수 있다.


생성된 컨테이너를 확인해보면 confident_matsumoto 라는 이상한 이름으로 생성된 것을 확인할 수 있는데, 이는 docker에서 자동으로 생성하는 이름이라서 그렇다.


docker run 명령에서 --name태그를 통해 원하는대로 지정하는게 가능하다.

다만, 이렇게 실행시킨 Spring Boot 애플리케이션은 Docker 컨테이너의 네트워크 설정이 없으므로 웹에서 접속이 불가능하다.

접속이 가능하게 하고싶다면 docker run --name my-spring -p 8080:8080 test:lastest 명령을 통해 이미지를 실행하면 된다.


컨테이너 터미널을 들어가보면 COPY 했던 파일이 루트 디렉토리에 존재하는 것을 확인할 수 있다.

Build Context

앞선 DockerFile 예시에서 빌드명령에 docker build -t test:lastest . 를 사용했다.
여기서 Build Context.에 해당한다. 즉, 빌드작업에 사용될 수 있는 파일 집합을 의미한다.

로컬 디렉토리 경로, Git Repository 원격 URL 등이 Build Context로 활용될 수 있다.
COPYADD같은 빌드 지침들은 해당 컨텍스트 하위의 모든 파일과 디렉토리를 참조할 수 있다.

로컬 Context

로컬 Context의 경우, 보통 . 를 이용해 현재 디렉토리 위치에서 빌드를 실행하는 경우가 많다.
원한다면 -f- 을 이용하여 입력받은 내용을 Dockerfile로 빌드에 사용할 수도 있다.

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

# build an image using the current directory as context
# and a Dockerfile passed through stdin
docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF

위 명령어대로 실행하면 EOF ... EOF 사이의 ...이 DockerFile로 활용된다.

원격 Context

docker build https://github.com/user/myrepo.git

원격에서 가장 먼저 떠오르는 Git Repository의 경우, Builder가 자동으로 Repository의 기본 branch의 가장 최신 커밋을 clone해서 사용한다. 만약, 특정 branch, 태그 및 하위 디렉토리를 복제하고 싶다면 build 명령에 #ref:dir 형태로 추가적인 정보를 입력하면 된다.

docker build https://github.com/user/myrepo.git#container:docker

예를들어, 위 형태로 빌드를 실행하면 container branch의 docker 서브디렉토리를 사용한다.
각 명령에 따라 사용되는 branch와 디렉토리를 나타내면 아래와 같다.

접미사사용되는 Commit branchBuild Context가 사용하는 디렉토리
myrepo.gitrefs/heads/<default branch>/
myrepo.git#mytagrefs/tags/mytag/
myrepo.git#mybranchrefs/heads/mybranch/
myrepo.git#pull/42/headrefs/pull/42/head/
myrepo.git#:myfolderrefs/heads/<default branch>/myfolder
myrepo.git#master:myfolderrefs/heads/master/myfolder
myrepo.git#mytag:myfolderrefs/tags/mytag/myfolder
myrepo.git#mybranch:myfolderrefs/heads/mybranch/myfolder

기본적으로 BuildKit은 .git 디렉토리를 유지하지 않는다.
만약, 빌드상황에서 Git 정보가 필요하다면 BUILDKIT_CONTEXT_KEEP_GIT_DIR 옵션을 1로 줌으로써 Git 정보를 사용할 수 있다.

private repository 라면, 자격증명정보를 제공해야 빌드가 가능하다.
토큰 기반의 인증방식을 사용한다면 아래와 같이 --secret 태그를 사용할 수 있다.

GIT_AUTH_TOKEN=<token> docker buildx build \
  --secret id=GIT_AUTH_TOKEN \
  https://github.com/user/private.git

토큰 기반이 아니라면, Buildx가 자동으로 $SSH_AUTH_SOCK의 SSH 자격증명을 감지하고 사용한다. 상세 참고는 여기

docker buildx build --ssh default git@github.com:user/private.git

Build Context에서 특정 디렉토리나 파일을 제외하고 싶다면 .dockerignore 파일을 만들면 된다.
원치않는 파일이나 디렉토리가 빌더로 전송되는 것을 막아, 빌드속도 향상에 도움이 된다.

.
├── index.ts
├── src/
├── docker
│   ├── build.Dockerfile
│   ├── build.Dockerfile.dockerignore
│   ├── lint.Dockerfile
│   ├── lint.Dockerfile.dockerignore
│   ├── test.Dockerfile
│   └── test.Dockerfile.dockerignore
├── package.json
└── package-lock.json

위와 같이 여러 Dockerfile을 사용한다면 각 Dockerfile에 해당하는 .dockerignore을 생성해 관리할 수 있다.
직접적인 파일 혹은 디렉토리 명 외에도 패턴을 이용한 매칭도 가능하다. 상세 참고

참고

0개의 댓글