도커 파일

mohadang·2022년 6월 5일
0

도커

목록 보기
21/26
post-thumbnail

이미지 생성 방법

수동

  1. Ubuntu:14.04, Centros:7 같은 이미지로 컨테이너 생성
  2. 컨테이너에 어플리케이션을 위한 환경을 설치, 소스소드 복제하고 잘 동작하는 것을 확인
  3. 컨테이너를 이미지로 커밋
  • 형상 관리, 자동화 안됨

도커 파일

  • Dockerfile이 자동화 스크립트이다. Dockerfile을 사용하면 직접 컨테이너를 생성하고 이미지로 커밋해야 하는 번거로움을 덜 수 있다. 깃과 같은 개발 도구를 통해 어플리케이션의 빌드 및 배포를 자동화할 수 있다.
  • 이미지를 생성하기 위해 컨테이너에 설치해야 하는 패키지, 추가해야 하는 소스코드, 실행해야 하는 명령어와 shell 스크립트 등을 하나의 파일에 기록해 두면 도커는 이 파일을 읽어 컨테이너에서 작업을 수행한 뒤 이미지로 만든다.
  • FROM, RUN과 같은 명령어를 도커 파일에 기록하여 작업들을 실행
  • Dockerfile은 도커 허브등을 통해 배포할 때 이미지 자체를 배포하는 대신 이미지를 생성하는 Dockerfile을 배포할 수도 있다.(이 방법이 더 좋은 방법인듯)
    • 배포되는 이미지를 신뢰할 수 없거나 직접 이미지를 생성해서 사용하고 싶다면 도커 허브에 올려져 있는 Dockerfile로 빌드
    • 실제로 도커 허브에 올려져 있는 대부분의 이미지는 Dockerfile을 함께 제공한다.
  • 이미지 빌드, 배포, 형상 관리 측면에서 도커 파일을 사용하는것이 유리
  • 이미지 빌드 과정에서는 별도의 입력이 불가능
    • 이미지를 빌드할 때 별도의 입력을 받아야 하는 RUN이 있다면 build 명령어는 이를 오류로 간주하고 빌드 종료
  • 명령어를 소문자로 표기해도 상관 없지만 일반적으로 대문자 표기
  • 도커 엔진은 Dockerfile을 읽어 들일 때 기본적으로 현재 디렉터리에 있는 Dockerfile이라는 이름을 가진 파일 선택

도커 파일 작성

EX) 도커 파일

root@red-virtual-machine:~# mkdir dockerfile && cd dockerfile
root@red-virtual-machine:~/dockerfile# echo test >> test.html
# 생성할 이미지의 베이스가 될 이미지, 반드시 한번은 입력
FROM ubuntu:14.04
# 이미지 생성한 개발자의 정보, 일반적으로 이름과 이메일 입력
MAINTAINER mohadang
# 이미지 메타 데이터 추가, 메타데이터는 docker inspect 명령어로 이미지의 정보를 구해서 확인 가능
LABEL "purpose"="practice"
# 이미지를 만들기 위해 컨테이너 내부에서 명령어를 실행
# 이미지 빌드 과정에서는 별도의 입력이 불가능하기 때문에 
RUN apt-get update
RUN apt-get install apache2 -y # 별도의 입력이 불가능 해서 -y
# 파일을 이미지에 추가. 추가하는 파일은 Dockerfile이 위치한 디렉터리인 컨텍스트에서 가져온다
# 추가할 파일은 JSON 배열 형태로 여러개 지정 가능, 마지막은 컨테이너에 추가될 위치 지정
# ["추가할 파일 이름", ... "컨테이너에 추가될 위치"]
ADD test.html /var/www/html
WORKDIR /var/ww/html
# 쉘을 이용한 명령어 수행
RUN ["/bin/bash", "-c", "echo hello >> test2.html"] # 쉘을 이용한 명령어 수행
# Dockerfile의 빌드로 생성된 이미지에서 노출할 포트를 설정
# 반드시 이 포트가 호스트의 포트와 바인딩되는 것은 아니며, 단지 컨테이너의 80번 포트를 사용할 것임을 나타냄
EXPOSE 80
# 컨테이너가 시작될 때마다 실행할 명령어를 설정, Dockerfile에서 한 번만 사용 가능
CMD apachectl -DFOREGROUND

MAINTAINER는 도커 1.13.0 이후 사용되지 않음 대신 LABEL maintainer "mohadang ehdl@gmail.com"
같은 방식으로 사용.

Dockerfile의 일부 명령어는 이처럼 배열의 형태로 사용할 수 있다.

  • RUN ["/bin/bash", "-c", "echo hello >> test2.html"]
  • RUN ["실행 가능한 파일", "명령줄 인자 1". "명령줄 인자 2", ...]

JSON 배열의 입력 형식을 따른다.
단 JSON 배열 형태로 Dockerfile의 명령어를 사용하면 shell을 실행하지 않는다.
["echo", "$MY_ENV"] 는 $MY_ENV 환경변수를 사용하지 않는다. 이 형태로 사용하려면
["sh", "-c", "echo $MY_ENV"]와 같이 사용
앞에는 항상 shell이 있어야 할 듯

Dockerfile에 CMD를 명시함으로써 이미지에 apachectl -DFOREGROUND라는 커맨드를 내장하면 컨테이너를 생성할때 별도의 커맨드를 입력하지 않아도 이미지에 내장된 apachectl -DFOREGROUND라는 커맨드가 적용되어 컨테이너가 시작될 때 자동으로 아파치 웹 서버가 실행
아파치 웹 서버는 하나의 터미널을 차지하는 포그라운드 모드로 실행되기 때문에 -d 옵션을 사용해 detached 모드로 컨테이너를 생성해야 한다.
CMD는 run 명령어의 이미지 이름 뒤에 입력하는 커맨드와 같은 역할을 하지만 docker run 명령어에서 커맨드 명령줄 인자를 입력하면 Dockerfile에서 사용한 CMD의 명령어는 run의 커맨드로 덮어 쓰인다.

Dockerfile 빌드

EX) Dockerfile 빌드

 $ docker build -t mybuild:0.0 ./
 - t : 생성될 이미지의 이름
 build 명령어의 끝에는 Dockerfile이 저장된 경로 입력
 $ docker run -d -P --name myserver mybuild:0.0
- P : 이미지에 설정된 EXPOSE의 모든 포트를 호스트에 연결하도록 설정
Dockerfile을 작성하는 개발자는 EXPOSE를 이용해 이미지가 실제로 사용될 때  어떤 포트가 사용돼야 하는지 명시 가능
이미지를 사용하는 입장에서는 컨테이너의 어플리케이션이 컨테이너 내분에서 어떤 포트를 사용하는지 알 수 있음


$ docker port myserver
80/tcp -> 0.0.0.0:49153
80/tcp -> :::49153
EXPOSE로 노풀된 포트를 호스트에서 사용 가능한 포트에 차례로 연결하므로 이 컨테이너가 호스트의 
어떤 포트와 연결됐는지 확인할 필요가 있다.
호스트의 IP와 이 포트로 컨테이너의 웹 서버에 접근 가능

$ docker images --filter "label=purpose=practice"
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mybuild      0.0       7464cbc69e6c   8 minutes ago   222MB
이미지의 라벨을 purpose=practice로 설정했으므로 docker images 명령어의 필터에 이 라벨을 적용할 수 있음
--filter 옵션을 통해 해당 라벨을 가지는 이미지를 출력

Dockerfile 빌드 과정

빌드 컨텍스트

이미지 빌드를 시작하면 도커는 가장 먼저 빌드 컨텍스트를 읽어 들인다.
빌드 컨텍스트는 이미지를 생성하는 데 필요한 각종 파일, 소스코드, 메타데이터 등을 담고 있는 디렉터리를 의미

Dockerfile이 위치한 디렉터리가 빌드 컨텍스트가 된다.
Dockerfile에서 이미지에 파일을 추가하는 방법은 ADD, COPY 등이 있다.
컨텍스트는 build 명령어의 맨 마지막에 지정된 위치에 있는 파일을 전부 포함

  • ./로 지정함으로써 test.html 파일을 빌드 컨텍스트에 추가
  • ADD 명령어를 통해 빌드 컨텍스트에서 test.html 파일을 이미지에 추가

Git과 같은 외부 URL에서 Dockerfile을 읽어 들인다면 해당 저장소에 있는 파일과 서브 모듈을 포함한다. 따라서 Dockerfile이 위치한 곳에는 이미지 빌드에 필요한 파일만 있는 것이 바람직하며 루트 디렉터리와 같은 곳에서 이미지를 빌드하지 않도록 주의해야 한다.

컨텍스트는 단순 파일뿐 아니라 하위 디렉터리도 전부 포함하게 되므로 빌드에 불필요한 파일이 포함된다면 빌드 속도가 느려질뿐더러 호스트의 메모리를 지나치게 점유할 수 있다.

이를 방지하기 위해 깃에서 사용하는 .gitignore와 유사한 기능을 사용할 수 있는 .dockerignore라는 파일을 작성하면 빌드 시 이 파일에 명시된 이름의 파일을 컨텍스트에서 제외.
.dockerignore 파일은 컨텍스트의 최상위 경로, 즉 build 명령어에서 맨 마지막에 오는 경로인 Dockerfile이 위치한 경로와 같은 곳에 위치해야함

EX) .dockerignore

test2.html
*.html
*/*.html
test.htm?

Dockerfile을 이용한 컨테이너 생성과 커밋

build 명령어는 Dockerfile에 기록된 대로 컨테이너를 실행한 뒤 완성된 이미지를 만들어 낸다. 그렇지만 이미지로 만드는 과정이 하나의 컨테이너에서 일어나는 것은 아니다.

$ docker build -t mybuild:0.0 ./
Sending build context to Docker daemon  3.072kB
Step 1/10 : FROM ubuntu:14.04
...
Digest: sha256:96a36dd3bcd8f149fa9874c4e28c35c01f715b3e...
Status: Downloaded newer image for ubuntu:14.04
 ---> 13b66b487594
Step 2/10 : ARG DEBIAN_FRONTEND=noninteractive
 ---> Running in 825c73bbbc86
Removing intermediate container 825c73bbbc86
 ---> 41b99bab9169
Step 3/10 : MAINTAINER mohadang
 ---> Running in 780aadf8a55a
Removing intermediate container 780aadf8a55a
 ---> ca4e56afdaa7
Step 4/10 : LABEL "purpose"="practice"
 ---> Running in c508dc6d2a3c
Removing intermediate container c508dc6d2a3c
 ---> a4afc18507c2
Step 5/10 : RUN apt-get update && apt-get -y install s ...
 ---> Running in 0b8df3ebd39e

Step은 Dockerfiledp 기록된 명령어에 해당. ADD, RUN 등의 명령어가 실행될 때마다 새로운 컨테이너가 하나씩 생성되며 이를 이미지로 커밋즉 Dockerfile에서 명령어 한 줄이 실행될 때마다 이전 Step에서 생성된 이미이에 의해 새로운 컨테이너가 생성되며 Dockerfile에 적힌 명령어를 수행하고 다시 새로운 이미지 레이어로 저장.
Removing intermediate container ... 는 중간에 이미지 레이어를 생성하기 위해 임시로 생성된 컨테이너를 삭제. 삭제되기 전 출력되는 ID는 커밋된 이미지 레이어를 의미

캐시를 이용한 이미지 빌드

한 번 이미지 빌드를 마치고 난 뒤 다시 같은 빌드를 진행하면 이전의 이미지 빌드에서 사용했던 캐시를 사용
이전에 빌드했던 Dockerfile에 같은 내용이 있다면 build 명령어는 이를 새로 빌드하지 않고 같은 명령어 줄까지 이전에 사용한 이미지 레이어를 활용해 이미지를 생성.

이미지 빌드 중 오류가 발생했을 때는 build 명령어가 중지되며, 이미지 레이어 생성을 위해 마지막으로 생성된 임시 컨테이너가 삭제되지 않은 채로 남게 된다. 또한 이미지의 빌드가 완전하지 않기 때문에 -t 옵션의 값으로 지정된 이미지의 이름이 아닌 <none>:<none>으로 이미지가 생성된다.
캐시 기능이 필요하지 않을때도 있는데 git clone 등의 명령어를 사용해 빌드할 떄가 여기에 해당된다. Dockerfile에 RUN git clone ... 을 사용해 이미지를 빌드했다면 RUN에 대한 이미지 레이어를 계속 캐시로 사용하기 때문에 실제 깃 저장소에서 리비전 관리가 일어나도 매번 빌드를 할 때마다 고정된 소스코드를 사용하게 될 것이다. 캐시를 사용하지 않으려면 build 명령어에 --no-cache 옵션을 추가한다.

캐시로 사용할 이미지를 직접 지정할 수도 있다

$ docker build --cache-from nginx -t my_extend_nginx:0.0

nginx:latest 이미지를 빌드하는 Dockerfile에 일부 내용을 추가해 사용한다면 로컬의 nginx:latest 이미지를 캐시로 사용할 수 있다.

멀티 스테이지를 이용한 Dockerfile 빌드

일반적으로 어플리케이션을 빌드핼 때는 많은 의존성 패키지와 라이브러리를 필요로 한다.
Go로 작성된 소스코드를 빌드하여 실행하는 도커 이미지를 사용한다고 하였을때 단순히 Hello World를 출력하는 프로그램을 실행하는 이미지임에도 불구하고 이미지의 크기는 800MB에 달하는 것을 확인할 수 있다.
실제 실행 파일의 크기는 매우 작지만 소스코드 빌드에 사용된 각종 패키지 및 라이브러리가 불필요학 ㅔ이미지의 크기를 차지하고 있는 것이다.

package main
import "fmt"
func main() {
    fmt.PrintlnI("hello world")
}
FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go
CMD ["./mainApp"]
$ docker build . -t go_helloworld
$ docker images
REPOSITORY    SIZE 
go_helloworld 796 MB

이런 문제를 해결하기 위해 멀티 스테이지 빌드 방법을 사용할 수 있다(도커 17.05 버전 이상)

FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go

FROM alpine:latest
WORKDIR /root
# 첫번째 FROM에서 사용된 이미지의 최종 상태에 존재하는 /root/mainApp 파일을 두 번째 이미지인 alpine:latest에 복사
# --from=0은 첫 번째 FROM에서 빌드된 이미지의 최종 상태를 의미
COPY --from=0 /root/mainApp . 
CMD ["./mainApp"]

# alpine이나 busy box와 같은 이미지는 우분투나 CentOS에 비해 이미지 크기가 매우 작지만 
# 기본적인 프로그램 실행에 필요한 필수적인 런타임 요소가 포함되어 있는 리눅스 배포판 이미지
# 어플리케이션 이미지를 간단히 생성할 수 있다는 장점이 있음

멀티 스테이지 빌드는 반드시 필요한 실행 파일만 최종 이미지 결과물에 포함시킴으로써 이미지 크기를 줄일 때 유용하게 사용할 수 있다.

FROM golang
ADD main.go /root
WORKDIR /root
RUN go build -o /root/mainApp /root/main.go

FROM golang
ADD main2.go /root
WORKDIR /root
RUN go build -o /root/mainApp2 /root/main.go

FROM alpine:latest
COPY --from=0 /root/mainApp . 
COPY --from=1 /root/mainApp2 . 

멀티 스테이지 빌드를 사용하는 Dockerfile은 2개 이상의 이미지를 사용할 수 있음
각 이미지는 먼저 FROM에서 명시된 순서대로 0, 1 .. 의 순으로 차례대로 구분되어 사용
이를 활용하면 여러 개의 이미지를 사용해 멀티 스테이지 빌드를 활용할 수 있음

FROM golang as builder
...
COPY --from=builder /root/mainApp .
별칭 사용 가능
profile
mohadang

0개의 댓글