도커/개념 및 실습

06장-2, Dockerfile

Jaden Park 2021. 6. 4. 09:42

Dockerfile 개요

  • 도커는 Dockerfile을 사용하여 이미지를 제작할 수 있다
  • Dockerfile은 코드 형태의 텍스트 문서이며, 여러 가지 지시어를 사용하여 이미지를 제작할 수 있다.
  • 일반적으로 이미지를 생성할 때 commit 이나 import 보다는 Dockerfile을 사용한다.
    • 컨테이너가 실행하는 CMD 또는 ENTRYPOINT 는 commit 이나 import 로는 지정하기 어렵기 때문
    • 코드 형태로 되어 있어 버전 관리에 용이함
    • 이미지의 기능을 파악하기도 쉬움

Dockerfile 지시어

지시어 설명
FROM 베이스 이미지 지정
RUN 이미지를 생성하면서 실행할 명령 지정
ENTRYPOINT 컨테이너의 애플리케이션 지정
EXPOSE 컨테이너의 포트 지정
ADD 이미지 생성시 파일 추가
VOLUME 컨테이너의 볼륨 지정
WORKDIR 컨테이너 작업 디렉토리 지정
MAINTAINER 이미지 작성자 명시
CMD 컨테이너의 애플리케이션 지정
LABEL 이미지의 라벨 지정
ENV 컨테이너의 환경 변수 지정
COPY 이미지 생성시 파일 복사
USER 컨테이너의 사용자 지정
  • RUN, CMD, ENTRYPOINT 지시어들은 exec 과 shell 형식으로 사용할 수 있음
  • exec 형식은 명령을 쉘을 통해 실행하지 않으며, shell 형식은 /bin/sh 과 같은 쉘을 통해 지정된명령을 실행한다.
형식 표현방법
exec ["yum", "-y", "install", "httpd"]
shell yum -y install httpd

RUN, CMD, ENTRYPOINT

RUN
  • 보통 패키지 설치 등에 사용
FROM ubuntu:latest
RUN apt-get update && apt-get install -y \
    curl \
    nginx
CMD
  • CMD 명령의 주용도는 컨테이너를 실행할 때 사용하는 default를 설정
  • 명령이나 인자값이 변경될 수 있고 컨테이너에서 명령 설정하지 않을 시 CMD에 기재된 명령을 기본값으로 실행
FROM centos:7
CMD echo "This is a CentOS 7"
  • docker run 실행 시 아무런 커맨드를 주지 않으면 CMD 명령 실행
$ docker run -it --rm <image-name>
This is a CentOS 7
$
  • echo " " 라고 실행할 커맨드를 주게 되면 CMD 는 무시되고 커맨드가 실행
$ docker run -it --rm <image-name> echo "This is a Ubuntu 18.04"
This is a Ubuntu 18.04
$
ENTRYPOINT
  • docker run 실행 시 실행되는 명령
  • CMD에서 설정한 default 파라미터가 ENTRYPOINT 에서 사용
  • 인자로 명령이 수정 불가능하여 사용자에 의해 변경되지 않고 고정적으로 실행될 명령을 사용할 때 많이 씀
    • 같은 명령에 다른 옵션은 수정가능
FROM ubuntu
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
  • docker run 명령 실행 시 파라미터 주면 CMD 는 사용되지 않지만 ENTRYPOINT 는 사용됌.
    • 이것이 가장 큰 차이점
$ docker run -it --rm <image-name>
Hello world
$ docker run -it --rm <image-name> bye
Hello bye
$
  • shell form 으로 실행 시 변수 등을 그대로 가능.
FROM centos
ENTRYPOINT ["echo","$HOME"]

$ docker run -it --rm <image-name>
$HOME
$
  • exec form 은 변수가 변환됌.
FROM centos
ENTRYPOINT echo $HOME

$ docker run -it --rm <image-name>
/root
$
  • CMD는 명령과 인자값이 변경될 수 있고, 컨테이너에서 명령 설정하지 않을 시 CMD에 기재된 명령을 기본값으로 실행.
  • ENTRYPOINT는 명령 수정이 불가능하여 사용자에 의해 변경되지 않고 고정적으로 실행될 명령은 ENTRYPOINT를 사용하는것이 좋음.

 

 

centos 기반에서 apache 서비스를 실행하는 이미지를 제작하는 예시 Dockerfile

[root@docker ~]# cat > /root/Dockerfile
FROM centos:latest
RUN yum -y install httpd
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"] ENV WEBPORT 80
EXPOSE ${WEBPORT}
EXPOSE 443
VOLUME /var/www/html
COPY index.html /var/www/html/index.html

dockerfile 이미지 제작

[root@docker ~]# docker build --help

Usage:  docker build [OPTIONS] PATH | URL | -
  • docker build 명령으로 Dockerfile을 지정하여 이미지를 제작할 수 있음.
  • 필수 옵션은 아니지만 -t 옵션으로 이미지 이름을 지정하는 것이 좋음. 그렇지 않으면 이미지를 사용할 때 ID 값으로 사용해야 함.
  • PATH에는 Dockerfile 이라는 이름의 파일을 가지고 있는 디렉토리 경로를 지정해야 하며, 만약 파일이름이 Dockerfile이 아닌 경우 다른 옵션을 추가로 사용해야 함.
  • Dockerfile을 작성할 때 작업용 디렉토리를 생성하고 접근하여 사용하는 것을 권장
    • 이미지를 제작하면서 필요한 파일을 COPY하거나 ADD할 때 같은 위치에 있으면 상대경로로 적을 수 있어 편리하기 때문
//작업용 디렉토리 생성
[root@docker ~]# mkdir centos_web && cd centos_web

//Dockerfile 작성
[root@docker centos_web]# cat > Dockerfile
FROM centos:latest
RUN yum -y install httpd
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"] ENV WEBPORT 80
EXPOSE ${WEBPORT}
EXPOSE 443
VOLUME /var/www/html
COPY index.html /var/www/html/index.html


[root@docker centos_web]# echo "CentOS apache" > index.html

[root@docker centos_web]# docker build -t JadenPark/docker:centosweb .
Successfully built f919b51ad04d
Successfully tagged JadenPark/docker:centosweb

[root@docker centos_web]# docker images JadenPark/docker:centosweb 
REPOSITORY     TAG         IMAGE ID       CREATED          SIZE
JadenPark/docker   centosweb   f919b51ad04d   28 seconds ago   252MB

//지정했던 설정 확인
[root@docker centos_web]# docker inspect JadenPark/docker:centosweb
            "ExposedPorts": {
                "443/tcp": {}
            },

...

            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/usr/sbin/httpd",
                "-D",
                "FOREGROUND"
            ],

...

            "Volumes": {
                "/var/www/html": {}
            },

...

        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:2653d992f4ef2bfd27f94db643815aa567240c37732cae1405ad1c1309ee9859",
                "sha256:28571101685b4f9ef9a9eaa99ee3b99a2588d5bb9b9a9e1ebad451386530ee63",
                "sha256:33845f856dab3eeef1a69a5f3f5b673a1c7d2dc8c4fd865fd39425c69b7cd432"
            ]

commitDockerfile 을 이용해서 hello.py 파일을 이용해 "hello-world" 출력 이미지 제작 및 컨테이너 실행

commit
[root@docker ~]# cat > test.py
print("hello-world")
[root@docker ~]# docker run -itd --name os1 centos:7
[root@docker ~]# docker exec os1 yum -y install python
[root@docker ~]# docker exec os1 mkdir /app
[root@docker ~]# docker cp test.py os1:/app

//둘 다 같은 결과임.
// 1
[root@docker ~]# docker commit --change 'CMD ["/app/test.py"]' --change 'ENTRYPOINT ["/usr/bin/python"]' --change 'VOLUME /app' --change 'WORKDIR /app' osp1 python_test1:v1
[root@docker ~]# docker run --name pythontest1_1 python_test1:v1
hello-world

//2
[root@docker ~]# docker commit --change "VOLUME /app" --change "WORKDIR /app" --change 'CMD ["python","test.py"]' osp1 python_test1:v2
[root@docker ~]# docker run --name pythontest1_2 python_test1:v2
hello-world
Dockerfile
[root@docker ~]# mkdir pytest && cd pytest
[root@docker pytest]# cat > test.py
print("hello-world")

//둘 다 같은 결과임.
//1
[root@docker pytest]# cat > Dockerfile
FROM centos:7
RUN  yum -y install python
RUN  mkdir /app
COPY test.py /app/test.py
CMD ["/app/test.py"]
ENTRYPOINT ["/usr/bin/python"]
VOLUME /app
WORKDIR /app

//2
[root@docker pytest]# cat > Dockerfile
FROM centos:7
RUN yum -y install python
CMD ["mkdir","app"]
COPY test.py /app/test.py
WORKDIR /app
VOLUME /app
CMD ["python","/app/test.py"]


[root@docker pytest]# docker build -t pthon_test2:v1 .
[root@docker pytest]# docker run --name pythontest2
hello-world

Dockerfile 로 go 언어로 만든 hello world 이미지 제작 및 컨테이너 실행

[root@docker ~]# yum -y install epel-release.noarch 
[root@docker ~]# yum -y install golang-bin
[root@docker ~]# cat > /root/hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello This is golang")
}

[root@docker ~]# go build hello.go

[root@docker ~]# cat > Dockerfile
FROM scratch
COPY hello .
CMD ["/hello"]

[root@docker ~]# docker build -t golang_test:v1 .

[root@docker ~]# docker images golang_test:v1
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
golang_test   v1        0000e96bc235   10 minutes ago   2.03MB

[root@docker ~]# docker run --name golangtest2 golang_test:v1