[다단계 구축] Go 언어로 개발된 제품의 이미지 크기 감소

본문의 목표


이 글의 목표는 Docker의 Multi-stage build을 사용하여 Go 언어로 개발된 제품의 이미지 크기를 줄이는 것이다.

본문의 구조


이 보도는 모두 4장으로 구성되어 있다.다음은 각 장의 내용이다.
1장: Multi-stage build
제2장: Go 언어를 사용한 간단한 제품 소개
3장: Go 언어의 Multi-stage build 정보
4장: 요약

1장 Multi-stage build


Multi-stage build 소개
멀티에는'많이','다중','복수'라는 뜻이 있다.
따라서 Multi-stage build은 "다수의 스테이지를 사용한 빌드"입니다.
그렇다면 여러 무대를 사용하는 것은 어떻게 된 일일까.
일반적으로 Docker 이미지에는 이미지 빌드와 관련된 라이브러리도 포함됩니다.
그러나 공식적인 환경에서 응용 프로그램 실행에 필요한 것만 구축하고 싶다.
거기서 여러 무대를 쓰면 이 고민은 없어진다.
여기서 두 개의 무대를 준비한다.
첫 번째 단계에서는 응용 프로그램의 Docker 이미지를 만듭니다.
두 번째 무대에서는 첫 번째 무대에서 만들어진 인상에서만 필요한 것을 복제한다.
이처럼 두 개의 무대를 준비했고, 최종 Docker 이미지에는 필요한 것만 담겼다.
그 결과 이미지 사이즈가 작아지고 정식 환경의 활용 성능이 향상됐다.

제2장 Go 언어를 사용한 간단한 제품 소개


이번에는 REST API를 구축하는 응용 프로그램을 Go 언어를 사용하는 간단한 제품으로 사용합니다.
프로젝트 전체 코드는 https://github.com/NaokiYazawa/multi-stage-build를 참조하십시오.
우선 클론 창고로 갈게요.
$ git clone [email protected]:NaokiYazawa/multi-stage-build.git
$ cd multi-stage-build
.env를 작성하여 PostgreSQL의 환경 변수를 정의합니다.
$ touch .env
.env
POSTGRES_USER=postgres
POSTGRES_PASSWORD=secret
POSTGRES_DB=mydb
POSTGRES_HOST=postgres
DB_PORT=5432
그러면 Docker Compose를 사용하여 응용 프로그램을 시작합니다.
$ docker-compose up
다음과 같이 서버를 부팅하면 됩니다.
api         |
api         |    ____    __
api         |   / __/___/ /  ___
api         |  / _// __/ _ \/ _ \
api         | /___/\__/_//_/\___/ v3.3.10-dev
api         | High performance, minimalist Go web framework
api         | https://echo.labstack.com
api         | ____________________________________O/_______
api         |                                     O\
api         | ⇨ http server started on [::]:8080
우리 빨리 API를 두드리자.
Makefile에 curl 명령이 기재되어 있습니다.
$ make create_user
# curl -X POST "http://localhost:8080/users" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"name\":\"Jack\"}"
# {"id":1,"name":"Jack"}
$ make get_users
# curl -X GET "http://localhost:8080/users" -H  "accept: application/json"
# [{"id":1,"name":"Jack"}]
$ make get_user
# curl -X GET "http://localhost:8080/users/1" -H  "accept: application/json"
# {"id":1,"name":"Jack"}
$ make update_user
# curl -X PUT "http://localhost:8080/users/1" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"name\":\"updated-Jack\"}"
# {"id":1,"name":"updated-Jack"}
$ make delete_user
# curl -X DELETE "http://localhost:8080/users/1" -H  "accept: application/json" -H  "Content-Type: application/json
에 상기 명령을 내려 응답이 정상적으로 돌아왔음을 확인했다.
응용 프로그램이 정상적으로 실행될 때 다음 장에서는 Multi-stage build에 대해 설명합니다.

제3장 Go 언어의 Multi-stage build 정보


이 장에서는 방금 응용 프로그램을 시작할 때 사용한 Multi-stage build에 대해 설명합니다.

Docker file에 대한 설명


그럼 clone 응용 프로그램 Docker file을 살펴보겠습니다.
해설이 평어로 들어갔기 때문에 한 줄 한 줄 쫓아보세요.
Dockerfile
####################### Build stage #######################
# golang:<version>-alpine は、Alpine Linux プロジェクトをベースにしている。
# イメージサイズを最小にするため、git、gcc、bash などは、Alpine-based のイメージには含まれていない。
FROM golang:1.16-alpine3.13 AS builder
# 作業ディレクトリの定義をする。今回は、app ディレクトリとした。
WORKDIR /app
# go.mod と go.sum を app ディレクトリにコピー
COPY go.mod go.sum ./
# 指定されたモジュールをダウンロードする。
RUN go mod download
# ルートディレクトリの中身を app フォルダにコピーする
COPY . .
# 実行ファイルの作成
# -o はアウトプットの名前を指定。
# ビルドするファイル名を指定(今回は main.go)。
RUN go build -o main /app/main.go


####################### Run stage #######################
# Goで作成したバイナリは Alpine Linux 上で動く。
# alpineLinux とは軽量でセキュアな Linux であり、とにかく軽量。
FROM alpine:3.13
# 作業ディレクトリの定義
WORKDIR /app
# Build stage からビルドされた main だけを Run stage にコピーする。(重要)
COPY --from=builder /app/main .
# ローカルの .env と .wait-for.sh をコンテナ側の app フォルダにコピーする
COPY .env .
COPY wait-for.sh .
# wait-for.sh の権限を変更
# x ・・・ 実行権限
RUN chmod +x wait-for.sh
# EXPOSE 命令は、実際にポートを公開するわけではない。
# これは、イメージを構築する人とコンテナを実行する人の間で、どのポートを公開するかについての一種の文書として機能する。
# 今回、docker-compose.yml において、api コンテナは 8080 ポートを解放するため「8080」とする。
EXPOSE 8080
# バイナリファイルの実行
CMD [ "/app/main" ]
주목할 점은 FROM이 두 번 등장했다는 점이다.
첫 번째 FROM에서
FROM golang:1.16-alpine3.13 AS builder
이렇게,golang:1.16-alpine3.13을 기본 Image로 합니다.이거 골랭이:1.16-alpine 3.13 Go 로켈이 설치되어 있습니다.
그리고 두 번째 FROM에서
FROM alpine:3.13
위에서 말한 바와 같이alpine:3.13은 기본 Image이다.이 알피니트:3.13에는 Go 로켈이 설치되어 있지 않습니다.그러나 바이너리 파일을 실행하는 환경은 완벽하다.
그러므로
RUN go build -o main /app/main.go
상기 코드를 통해 생성된 바이너리 파일을 복사하고 실행할 수 있다.
# Build stage からビルドされた main だけを Run stage にコピーする。
COPY --from=builder /app/main .
--from=builder의builder가 제1FROM이다
FROM golang:1.16-alpine3.13 AS builder
의builder라는 이름과 일치해야 합니다.
마지막으로 상술한 절차는 그림에서 보듯이 다음과 같다.

Multi-stage build을 실행하여 이미지 크기를 줄일 수 있습니다.
다음 명령을 실행하여 그림 크기를 확인하십시오.
$ docker iamges
REPOSITORY              TAG       IMAGE ID       CREATED        SIZE
multi-stage-build_api   latest    b1fa72be0f91   3 hours ago    20.6MB
postgres                12.8      108ccc7c5fa3   6 months ago   371MB
multi-stage-build_api의 이미지 크기는 20.6MB입니다.

Multi-stage build을 사용하지 않는 경우


마지막으로Multi-stage build을 사용하지 않으면 이미지의 크기를 확인하십시오.
다음 명령을 실행하여 용기를 삭제하십시오.
$ docker-compose down
또한, 다음 명령의 멀티스테이지-buildapi의 IMAGE ID를 확인하고 이미지를 삭제합니다.
$ docker images
$ docker rmi -f <IMAGE ID>
그리고 다음처럼 Docker file을 다시 쓰십시오.
Dockerfile
# Build stage
# golang:<version>-alpine は、Alpine Linux プロジェクトをベースにしている。
# イメージサイズを最小にするため、git、gcc、bash などは、Alpine-based のイメージには含まれていない。
FROM golang:1.16-alpine3.13 AS builder
# 作業ディレクトリの定義をする。今回は、app ディレクトリとした。
WORKDIR /app
# go.mod と go.sum を app ディレクトリにコピー
COPY go.mod go.sum ./
# 指定されたモジュールをダウンロードする。
RUN go mod download
# src ディレクトリの中身を app フォルダにコピーする
COPY . .
# 実行ファイルの作成
# -o はアウトプットの名前を指定。
# ビルドするファイル名を指定(今回は main.go)。
RUN go build -o main /app/main.go
# wait-for.sh の権限を変更
# x ・・・ 実行権限
RUN chmod +x wait-for.sh
# EXPOSE 命令は、実際にポートを公開するわけではない。
# これは、イメージを構築する人とコンテナを実行する人の間で、どのポートを公開するかについての一種の文書として機能する。
# 今回、docker-compose.yml において、api コンテナは 8080 ポートを解放するため「8080」とする。
EXPOSE 8080
# バイナリファイルの実行
CMD [ "/app/main" ]
위의 Docker file에는 Build stage만 있습니다.
그럼 다음 명령을 실행하고 프로그램을 시작합시다.
$ docker-compose up 
프로그램이 정상적으로 시작될 때 그림 크기를 확인합니다.
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
multi-stage-build_api   latest    9c21dc557a66   9 seconds ago   446MB
postgres                12.8      108ccc7c5fa3   6 months ago    371MB
위에서 설명한 대로 멀티스테이지-buildapi의 이미지 사이즈가 446MB로 팽창했습니다.
Go 로켈에 설치된 Golang: 1.16-alpine 3입니다.13을 직접 사용했기 때문이다.
Multi-stage build을 사용할 때 이미지 크기가 20.6MB이기 때문에 Multi-stage build을 사용하지 않으면 이미지 크기가 약 22배로 변합니다.
Multi-stage build의 유효성을 이해하셨습니까?

제3장 총결산


끝까지 읽어주셔서 감사합니다.
Go 언어에서 Docker를 사용할 때는 Multi-stage build에 반드시 도전하십시오.
틀린 글자가 있으면 메시지를 남겨 주세요.

보충하다


docker-compose.다음은 yml에 대한 설명입니다.
이번에는 데이터베이스 PostgreSQL을 정의하고 API 기능인 Go 두 컨테이너를 실행합니다.
services.api.build에서 Docker file이 있는 디렉토리의 경로를 지정합니다.
docker-compose.yml
version: "3.8"

services:
  postgres:
    # コンテナ名を指定
    container_name: postgres
    image: postgres:12.8
    # OSの起動時にコンテナを起動させる
    restart: always
    env_file:
      - .env
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    ports:
      - 5432:5432
    volumes:
      - db:/var/lib/postgresql/data
  api:
    # コンテナ名を指定
    container_name: api
    build:
      # 「.」は本docker-compose.ymlがあるディレクトリ(現在のディレクトリ)を指す
      # 今回は、Dockerfile をルートディレクトリに配置する
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      POSTGRES_HOST: "${POSTGRES_HOST}"
    # depends_on は起動順を制御するだけである。
    # したがって、postgres コンテナが起動してから api コンテナが起動するという保証はされない
    depends_on:
      - postgres
    # entrypoint を設定すると、Dockerfile の ENTRYPOINT で設定されたデフォルトのエントリポイントが上書きされ、イメージのデフォルトコマンドがクリアされる。
    # つまり、Dockerfile に CMD 命令があれば、それは無視される。
    # よって、docker-compose.yml においても実行するコマンドを明示的に指定する必要がある。
    entrypoint: ["/app/wait-for.sh", "postgres:5432", "--"]
    command: ["/app/main"]

volumes:
  db:

좋은 웹페이지 즐겨찾기