공부/docker

도커 교과서 ch04 (도커 이미지 2)

KidAnt 2023. 12. 18. 10:16

실습환경 초기화 명령
docker container rm -f $(docker container ls -aq)

이미지 차지 디스크 용량 회수 명령
docker image rm -f $(docker image ls -f reference='diamol/*' -q)

도커의 장점 -> 모든 환경에서 사용가능하며 Dockerfile 스크립트를 작성하여 이미지를 만든 후 어플리케이션 패키징을 위한 Dockerfile 스크립트에서 이 이미지를 사용해 소스코드를 컴파일함으로써 어플리케이션을 패키징 할 수 있다. 

FROM diamol/base AS build-stage
RUN echo 'Building...' > /build.txt
#'Building..' 이라는 단어를 build.txt에 입력

FROM diamol/base AS test-stage
COPY --from=build-stage /build.txt /build.txt
RUN echo 'Testing...' >> /build.txt
#build-stage에 있던 build.txt를 가져옴 그 직후 'Testing...'을 입력

FROM diamol/base
COPY --from=test-stage /build.txt /build.txt
CMD cat /build.txt
#test-stage에 잇던 build.txt를 가져와서 출력

#멀티 스테이지 빌드를 적용한 Dockerfile 스크립트
#FORM 뒤에 AS를 붙여 이름 적용이 가능하다
#빌드가 3개나 되지만 결국 최종산출물은 마지막 단계의 내용물을 담은은 도커 이미지다.
#COPY --from=: 호스트 컴퓨터의 파일시스템이 아니라 앞선 빌드 단계의 파일시스템에 있는 파일임을 선언
(ex: build-stage 파일생성 -> 해당 파일을  test-stage로 복사 -> 다시 test-stage에서 생성한 파일을 마지막 단계로 복사
#RUM: 빌드중 컨테이너 안에서 명령을 실행한 다음 그 결과를 이미지 레이어에 저장한다, 명령에 제한은 없지만 from 인스트럭션에서 저장한 이미지에서 실행할 수 있는 것이여야 한다.
#각 빌드 단계는 서로 격리되어잇다. -> 중간에 한 단계만 실패해도 전체 빌드가 실패한다.

DESKTOP-KU8L7UV:~/080258/ch04/exercises/multi-stage$ docker image build -t multi-stage .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  3.072kB
Step 1/8 : FROM diamol/base AS build-stage
latest: Pulling from diamol/base
31603596830f: Already exists
792f5419a843: Already exists
Digest: sha256:787fe221a14f46b55e224ea0436aca77d345c3ded400aaf6cd40125e247f35c7
Status: Downloaded newer image for diamol/base:latest
 ---> 9fc3f74c8b53
Step 2/8 : RUN echo 'Building...' > /build.txt
 ---> Running in f8a74e09d58b
Removing intermediate container f8a74e09d58b
 ---> f003b9350d06
#build-stage 단계 -> build.txt파일이 생성

Step 3/8 : FROM diamol/base AS test-stage
 ---> 9fc3f74c8b53
Step 4/8 : COPY --from=build-stage /build.txt /build.txt
 ---> 28e220caf261
Step 5/8 : RUN echo 'Testing...' >> /build.txt
 ---> Running in 77a335e6ab03
Removing intermediate container 77a335e6ab03
 ---> bd92c27363bd
#test-stage 단계 -> 앞서 생성한 build.txt파일을 이미지에 추가

Step 6/8 : FROM diamol/base
 ---> 9fc3f74c8b53
Step 7/8 : COPY --from=test-stage /build.txt /build.txt
 ---> 5a09f3bd080d
Step 8/8 : CMD cat /build.txt
 ---> Running in 29c8fcdf756b
Removing intermediate container 29c8fcdf756b
 ---> 409616d3a93b
Successfully built 409616d3a93b
Successfully tagged multi-stage:latest
#최종 결과물이 될 이미지를 생성 -> test-stage 단계의 build.txt를 복사해온다.

-----------------------------------------------------------------------------------------------------------

도커 이미지를 통한 중앙집중 관리 가능 -> 팀 공통의 설정에서 벗어날 가능성 자체가 원천적으로 차단됨

메이븐, OpenJDK -> 표준 자바 도구
메이븐-> 빌드절차와 의존모듈의 입수방법을 정의하는 도구 (빌드 절차가 정의된 xml문서 사용하며 mvn명령을 실행해 사용)
OpenJDK -> 자유로이 재배포가 가능한 자바 런타임이자 개발자 키트

메이븐을 사용해 자바 애플리케이션을 빌드하는 Dockerfile 스크립트

FROM diamol/maven AS builder
#메이븐과 OpenJDK가 포함된 이미지

WORKDIR /usr/src/iotd
COPY pom.xml .
#이미지에 /user/src/iotd라는 디렉토리 생성 후 해당 디렉토리에 pom.xml파일을 복사해서 넣음

RUN mvn -B dependency:go-offline
#RUN명령어를 이용해서 모든 의존모듈 받기 -> 시간소모량이 커 메이브 별도의 단계로 분리해 레이어 캐시를 활용할 수 잇도록 한다.
(만일 새로운 의존 모듈이 추가됬거나 xml파일이 변경되었을 경우 이단계가 다시 실행된다) 

COPY . .
#인스트럭션을 통해 나머지 소스코드가 복사된다. -> 도커 빌드가 실행중인 디렉토리에 포함된 모든 파일과 서브 디렉토리를 현재 이미지 내 작업 디렉토리로 복사

RUN mvn package
#mvn package 실행 -> 어플리케이션을 빌드하고 패키징하라는 뜻

# app
FROM diamol/openjdk
#자바 11 런타임 포함된 이미지

WORKDIR /app
COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar .
#app이라는 디렉토리 생성후 앞서 만든 builder의 jar파일을 넣는다. -> jar파일은 모든 의존 모듈과 컴파일된 어플리케이션을 포함하는 단일 파일

EXPOSE 80
#EXPOSE로 80 port 외부에 공개

ENTRYPOINT ["java", "-jar", "/app/iotd-service-0.1.0.jar"]
#ENTRYPOINT == CMD -> 이미지로 컨테이너가 실행되면 정의한 명령을 실행

도커 빌드
DESKTOP-KU8L7UV:~/080258/ch04/exercises/image-of-the-day$ docker image build -t image-of-the-day .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  24.58kB
Step 1/11 : FROM diamol/maven AS builder
latest: Pulling from diamol/maven
0ecb575e629c: Pull complete
7467d1831b69: Pull complete
feab2c490a3c: Pull complete
f15a0f46f8c3: Pull complete
b4735181ba0c: Pull complete
fdfee37d01c3: Pull complete
84289cf02da5: Pull complete
0579b2a6f92c: Pull complete
6a2b01f2ccf7: Pull complete
85e47f7d664b: Pull complete
Digest: sha256:bc24b7b3beaae18550590fd986b09d7833c4daedb2632c76daddab21351b934f
Status: Downloaded newer image for diamol/maven:latest
 ---> 918c80d96888
Step 2/11 : WORKDIR /usr/src/iotd

....(중략)

[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ iotd-service ---
[INFO] Building jar: /usr/src/iotd/target/iotd-service-0.1.0.jar
#builder 단계의 마지막은 메이븐을 사용해 자바 어플리케이션을 JAR파일로 패키징하는 부분이다.
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:repackage (repackage) @ iotd-service ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.936 s
[INFO] Finished at: 2023-12-15T04:09:15Z
[INFO] ------------------------------------------------------------------------
Removing intermediate container fc480a9ceed2
 ---> d1f26430f5a0
Step 7/11 : FROM diamol/openjdk
latest: Pulling from diamol/openjdk
6f28985ad184: Pull complete
b3555c388585: Pull complete
42965fccf401: Pull complete
484bb3bd28a7: Pull complete
53aadb25d987: Pull complete
Digest: sha256:bc11278602c48a60f71ea01031c54a73878d19db4803f7dd8705aa77bab89808
Status: Downloaded newer image for diamol/openjdk:latest
 ---> ace3932f8772
Step 8/11 : WORKDIR /app
 ---> Running in ee17c4146970
Removing intermediate container ee17c4146970
 ---> e817a85db802
Step 9/11 : COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar . 
#앞서 builder 단계에서 패키징된 JAR파일을 빌드 마지막 단계에서 복사
 ---> 55fb8594685f

#해당 어플리케이션은 REST API(천문사진 받아오는거)

docker network create net
#도커 컨테이너 네트워크 생성(default is bridge)

docker container run --name iotd -d -p 800:80 --network net image-of-the-day
#도커 컨테이너 구축
#--natwork: 컨테이너 구축시 해당 네트워크로 연결하는 옵션
#이미지에 빌드 도구 포함X (명령어 사용 불가능) -> 빌드단계의 마지막에서 콘텐츠만 포함되어 있엇기 때문 -> 이전단계에서 포함시키고 싶은 것이 있다면 최종단계에서 명시적으로 해당 콘텐츠를 복사해야함

Node.js 어플리케이션은 자바스크립트로 구현 -> 별도의 컴파일 X -> 컨테이너화된 Node.js 어플리케이션을 실행하려면 Node.js 런타임과 소스 코드가 어플리케이션 이미지에 포함되어야 한다.

하지만 멀티 스테이지 빌드는 필요함 -> 의존모듈 최적화 용도 -> Node.js는 npm 패키지 관리자를 사용해 의존모듈을 관리

npm을 사용해 Node.js 어플리케이션을 빌드하는 Dockerfile 스크립트
FROM diamol/node AS builder

WORKDIR /src
COPY src/package.json .
RUN npm install
#의존모듈이 정의돤 package.json 복사 후 npm install 명령어 실행해서 의존모듈을 내려받는다.

# app
FROM diamol/node

EXPOSE 80
CMD ["node", "server.js"]
#포트와 시작명령어 선언

WORKDIR /app
COPY --from=builder /src/node_modules/ /app/node_modules/
COPY src/ .
#작업 디렉토리 생성 및 host로 부터 어플리케이션 아티팩트를 모두 복사해 넣는 다.

DESKTOP-KU8L7UV:~/080258/ch04/exercises/access-log$ docker image build -t access-log .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

....(중략)

added 142 packages from 189 contributors and audited 142 packages in 5.459s
found 0 vulnerabilities 
#builder 단계에서 어플리케이션의 의존 모듈을 내려받는다. 

Removing intermediate container 0b7b61bcf30d
 ---> ab129550c84b
Step 5/10 : FROM diamol/node
 ---> 9dfa73010b19
Step 6/10 : EXPOSE 80
 ---> Running in 8deeb2c4ce6b
Removing intermediate container 8deeb2c4ce6b
 ---> a03ca0703423
Step 7/10 : CMD ["node", "server.js"]
 ---> Running in 517ac6a8692b
Removing intermediate container 517ac6a8692b
 ---> 8053eda4da53
Step 8/10 : WORKDIR /app
 ---> Running in 7721e5242c20
Removing intermediate container 7721e5242c20
 ---> 385218a4f804
Step 9/10 : COPY --from=builder /src/node_modules/ /app/node_modules/
#그전 단계에서 받은 의존모듈을 application 단계에서 복사
 ---> f4f22dfff649
Step 10/10 : COPY src/ .
#host src디렉토리에서 자바스크립트 파일을 복사해 온다.
 ---> ad4cdfa6c54f
Successfully built ad4cdfa6c54f
Successfully tagged access-log:latest

-----------------------------------------------------------------------------------------------------------

Go: 네이티브 바이너리로 컴파일되는 크로스 플랫폼 언어(어떤 플랫폼이든 동작)

Go 어플리케이션의 멀티 스테이지 빌드를 위한 Dockerfile 스트립트 예

DESKTOP-KU8L7UV:~/080258/ch04/exercises/image-gallery$ cat Dockerfile
FROM diamol/golang AS builder
#Go언어의 도구가 설치된 기반이미지

COPY main.go .
RUN go build -o /server
#의존모듈 내려받는 단계 생략하고 바로 빌드로 들어감

# app
FROM diamol/base #최소한의 운영체제 레이어만 받음
ENV IMAGE_API_URL="http://iotd/image" \
    ACCESS_API_URL="http://accesslog/access-log"

CMD ["/web/server"]
#ENV로 설정값을 환경변수 형태로 설정하고 컴파일된 바이너리 실행해 어플리케이션 실행

WORKDIR /web
COPY index.html .
COPY --from=builder /server .
#builder 단계에서 빌드한 웹 서버 바이너리와 웹 서버가 제공할 HTML 파일을 복사하는 과정으로 마무리한다.

RUN chmod +x server
#추가적으로 바이너리 파일이 chmod 명령을 통해 명시적으로 실행권한을 부여받음

해당 Go 이미지 빌드

DESKTOP-KU8L7UV:~/080258/ch04/exercises/image-gallery$ docker image build -t image-gallery .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  6.144kB
Step 1/10 : FROM diamol/golang AS builder
latest: Pulling from diamol/golang
d6ff36c9ec48: Pull complete
c958d65b3090: Pull complete
edaf0a6b092f: Pull complete
80931cf68816: Pull complete
813643441356: Pull complete
799f41bb59c9: Pull complete
16b5038bccc8: Pull complete
Digest: sha256:ffc019466b60046d67b71628afafc9f80cc4d6a6bce824dc89d51300ecec0902
Status: Downloaded newer image for diamol/golang:latest
 ---> 119cb20c3f56
Step 2/10 : COPY main.go .
 ---> 51ef92adab09
Step 3/10 : RUN go build -o /server
 ---> Running in 5a508cd2eb20
#builder 단계에서 컴파일이 된다. (Go 컴파일러는 실패하지 않는이상 로그X)

Removing intermediate container 5a508cd2eb20
 ---> fbded467d568
Step 4/10 : FROM diamol/base
 ---> 9fc3f74c8b53
Step 5/10 : ENV IMAGE_API_URL="http://iotd/image"     ACCESS_API_URL="http://accesslog/access-log"
 ---> Running in 29d2ee7de507
Removing intermediate container 29d2ee7de507
 ---> 4e0561d93748
Step 6/10 : CMD ["/web/server"]
 ---> Running in 4c9bf9bc4dc7
Removing intermediate container 4c9bf9bc4dc7
 ---> 8864e85193a4
Step 7/10 : WORKDIR /web
 ---> Running in e19a7499b4d8
Removing intermediate container e19a7499b4d8
 ---> e688d535da1c
Step 8/10 : COPY index.html .
 ---> 7be09b13bd4f
#host에 있는 HTML 파일을 최종 이미지로 복사해 온다.

Step 9/10 : COPY --from=builder /server .
 ---> 4a7453b1b6d5
#builder 단계에서 빌드한 웹 서버 바이너리를 복사해온다.

Step 10/10 : RUN chmod +x server
 ---> Running in 60272d1cdf9a
Removing intermediate container 60272d1cdf9a
 ---> 43c43c6abe80
Successfully built 43c43c6abe80
Successfully tagged image-gallery:latest

#기존 이미지와 용량비교
DESKTOP-KU8L7UV:~/080258/ch04/exercises/image-gallery$ docker image ls -f reference=diamol/golang -f reference=image-gallery
REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
image-gallery   latest    43c43c6abe80   32 minutes ago   27.1MB
diamol/golang   latest    119cb20c3f56   3 years ago      803MB
#docker image ls 옵션
-f reference=[이미지]: 해당 태그를 포함하는 이미지만 출력하도록 필터링하는 옵션
#빌드 도구를 포함한 Go 이미지가 800MB넘어가지만 실제 AP(어플리케이션)이미지는 25MB에 불가하다. -> 논리적 크기라 이미지간에 많은 수 레이어 공유도 가능 -> 리소스 효율성 극대화, 취약성 최소화

docker container run -d -p 802:80 --network net image-gallery
#이미지 컨테이너화

#localhost:802 port로 NASA가 제공하는 천문사진 확인가능

------------------------------------------------------------------------------------------------------------------

Dockerfile 스크립트의 동작원리와 컨테이너 안에서 어플리케이션을 빌드하는 것이 왜 유용한가

1. 표준화 -> 운영체제와 로컬환경에 상관없이 모든 빌드과정이 컨테이너 내부에서 이루어 진다 -> 컨테이너는 모든 도구를 정확한 버전으로 갖추고 있다. -> 실무 적용시 신규 개발적응기간, 빌드서버 관리부담, 개발자간 도구 버전차이로 인한 실패 확률 최소화

2. 성능향상 -> 멀티 스테이지 빌드의 각 단계는 각각의 독자적 캐시가 존재하며 도커는 빌드 중 각 인스트럭션에 해당하는 레이어 캐시를 찾는다 -> 못찾으면 인스트럭션이 모두 실행되지만 그 범위는 해당 단계 안으로 국한되고 이어지는 다음단계는 다시 캐시 재사용이 가능하다. -> Dockerfile 스크립트를 세심하게 최적화하면 재사용을 통해 시간절약이 가능하다.

3. 멀티스크립트 Dockerfile 스크립트를 통해 빌드과정을 세밀하게 조정해서 이미지를 가능한 한 작게 유지가 가능하다.

--------------------------------------------------------------------------------------------------------------
연습문제

DESKTOP-KU8L7UV:~/080258/ch04/lab$ ls -al
total 36
drwxr-xr-x 2 root root 4096 Dec 11 13:34 .
drwxr-xr-x 4 root root 4096 Dec 11 13:34 ..
-rw-r--r-- 1 root root  178 Dec 11 13:34 Dockerfile
-rw-r--r-- 1 root root  225 Dec 11 13:34 Dockerfile.optimized
-rw-r--r-- 1 root root  262 Dec 11 13:34 Jenkinsfile
-rw-r--r-- 1 root root  314 Dec 11 13:34 Jenkinsfile.optimized
-rw-r--r-- 1 root root  951 Dec 11 13:34 README.md
-rw-r--r-- 1 root root  398 Dec 11 13:34 index.html
-rw-r--r-- 1 root root  350 Dec 11 13:34 main.go

#Dockerfile 이미지 최적화(목표: 800-> 15MB)

DESKTOP-KU8L7UV:~/080258/ch04/lab$ cat Dockerfile
FROM diamol/golang

WORKDIR web
COPY index.html .
COPY main.go .

RUN go build -o /web/server
RUN chmod +x /web/server

CMD ["/web/server"]
ENV USER=sixeyed

원본 빌드

EXPOSE 80 DESKTOP-KU8L7UV:~/080258/ch04/lab$ docker image build -t golang_test .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  8.704kB
Step 1/9 : FROM diamol/golang
 ---> 119cb20c3f56
Step 2/9 : WORKDIR web
 ---> Running in 80138e660a56
Removing intermediate container 80138e660a56
 ---> 583de296df10
Step 3/9 : COPY index.html .
 ---> dec4ffd3716e
Step 4/9 : COPY main.go .
 ---> c3fa87a1e975
Step 5/9 : RUN go build -o /web/server
 ---> Running in 1dc3cc1d0d75
Removing intermediate container 1dc3cc1d0d75
 ---> a2112d6023ba
Step 6/9 : RUN chmod +x /web/server
 ---> Running in 4f19ed8ef897
Removing intermediate container 4f19ed8ef897
 ---> 1b3878ef4be7
Step 7/9 : CMD ["/web/server"]
 ---> Running in 334251c4481f
Removing intermediate container 334251c4481f
 ---> 107d92b6ea82
Step 8/9 : ENV USER=sixeyed
 ---> Running in 778abcd9feb2
Removing intermediate container 778abcd9feb2
 ---> e295d634be92
Step 9/9 : EXPOSE 80
 ---> Running in 62bc63990460
Removing intermediate container 62bc63990460
 ---> bf5999ca5535
Successfully built bf5999ca5535

#용량확인
DESKTOP-KU8L7UV:~/080258/ch04/lab$ docker image ls
REPOSITORY         TAG       IMAGE ID       CREATED          SIZE
golang_test        latest    bf5999ca5535   24 seconds ago   832MB
Successfully tagged golang_test:latest

#최적화
DESKTOP-KU8L7UV:~/080258/ch04/lab$ cat Dockerfile
FROM diamol/golang As builder
#원본 이미지를 불러오고 이름을 붙임

COPY main.go .
RUN go build -o /server
RUN chmod +x /server
#빌드 및 권한분배

# app
FROM diamol/base
#기반 이미지 생성

EXPOSE 80
#포트 생성
CMD ["/web/server"]
ENV USER="sixeyed"
#바이너리 실행 및 컴파일러된 환경변수 설정

WORKDIR web
COPY --from=builder /server .
#builder의 /server 부분만 가져옴
COPY index.html .

DESKTOP-KU8L7UV:~/080258/ch04/lab$ sudo docker image ls
REPOSITORY         TAG       IMAGE ID       CREATED          SIZE
golang_test3       latest    2903770c6101   12 seconds ago   17MB
golang_test        latest    bf5999ca5535   20 minutes ago   832MB

#test3가 최적화 이미지 test가 원본이미지