공부하고 기록하는, 경제학과 출신 개발자의 노트

프로그래밍/이것저것_개발일지

Paketo buildpack의 Stack Customization 테스트 기록

inspirit941 2023. 4. 12. 13:24
반응형

시도했던 이유

  • Knative Function은 paketo buildpack을 기본 빌드팩으로 채택해서 쓰고 있다.
  • 빌드팩은 runtime version과 같은 정보를 동적으로 입력받은 뒤
    실행 시점에서 binary를 다운로드받는다.
  • 그런데, 신규 함수를 생성할 때마다 CPython이나 Node같은 runtime을 매번 다운로드받는 방식은
    빌드 속도가 느려지는 원인이 된다.
  • 빌드팩이 실행될 환경인 builder 이미지에서 특정 runtime은 미리 다운로드 받아두고,
    buildpack에서 빌드를 시도할 때 builder 이미지에 해당 runtime이 있다면 재사용하도록 만들 수 있지 않을까?

 

cf. 원래 Buildpack은 애초에 'runtime을 동적으로 관리할 수 있도록 한다'는 철학에서 출발한 프로덕트이기 때문에,
매번 같은 runtime을 다운받는 데서 발생하는 빌드 시간은 사용자가 감내해야 할 요소다.

그럼에도 혹시나 싶어서 해봤던 거고, 결과적으로는 실패했다. 어떻게 했는지, 왜 안됐는지를 기록하는 글.

실행환경은 Mac 기준이다.


CPython binary를 builder stack에 포함해보기 위한 작업

https://paketo.io/docs/howto/create-custom-stack/

 

How to Create a Custom Stack - Paketo Buildpacks

This documentation explains how to create a custom stack based off of the existing Paketo stacks. Check out the stacks concept page for more information about what a stack is and the Paketo offerings. It’s recommended to use an existent Paketo stack offe

paketo.io

https://buildpacks.io/docs/operator-guide/create-a-stack/

 

Create a stack · Cloud Native Buildpacks

Creating a custom stack allows you to configure the base images for the build-time environment for your builder and the run-time for your application.

buildpacks.io

위 문서들을 참고해서 진행했다.


23.03.17일 기준으로
builder / buildpackless-base-builder 이미지에서 사용하는 default stack 이미지는 bionic-base-stack (ubuntu 18.04).
ubuntu 18.04에 build에 쓰일 기본 binary (openssl 등)를 설치된 두 개의 이미지가 있다.

https://github.com/paketo-buildpacks/bionic-base-stack/tree/main/stack

  • base-image: build.Dockerfile
  • run-image: run.Dockerfile

 

build.Dockerfile에 cpython binary를 추가한다.

FROM ubuntu:bionic

ARG sources
ARG packages
ARG package_args='--allow-downgrades --allow-remove-essential --allow-change-held-packages --no-install-recommends'

RUN echo "$sources" > /etc/apt/sources.list
RUN echo "Package: $packages\nPin: release c=multiverse\nPin-Priority: -1\n\nPackage: $packages\nPin: release c=restricted\nPin-Priority: -1\n" > /etc/apt/preferences

RUN echo "debconf debconf/frontend select noninteractive" | debconf-set-selections && \
  export DEBIAN_FRONTEND=noninteractive && \
  apt-get -y $package_args update && \
  apt-get -y $package_args upgrade && \
  apt-get -y $package_args install locales && \
  locale-gen en_US.UTF-8 && \
  update-locale LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 && \
  apt-get -y $package_args install $packages && \
  rm -rf /var/lib/apt/lists/* /tmp/* /etc/apt/preferences

# customized cpython을 이미지에 설치, 경로는 buildpack이 binary 저장하는 경로인 /layers/paketo-buildpacks_cpython/cpython 로 지정
WORKDIR /workspace
ARG PYTHON_VERSION=3.10.8
RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz && \
  tar -xzf Python-${PYTHON_VERSION}.tgz && \
  cd Python-${PYTHON_VERSION} && \
  ./configure --prefix=/layers/paketo-buildpacks_cpython/cpython && \
  make && \
  make install && \
  cd .. && \
  rm -rf Python-${PYTHON_VERSION}.tgz Python-${PYTHON_VERSION}


RUN for path in /workspace /workspace/source-ws /workspace/source; do git config --system --add safe.directory "${path}"; done

RUN curl -sSfL -o /usr/local/bin/yj https://github.com/sclevine/yj/releases/latest/download/yj-linux-amd64 \
  && chmod +x /usr/local/bin/yj

stack.toml를 사용해서 OCI image를 빌드, container registry에 push한다.

paketo 공식문서를 보면 jam과 skopeo를 사용해서 OCI 이미지를 생성하라고 안내하고 있다.

 

  • skopeo: mac 기준 brew install skopeo 로 설치 가능
  • jam: https://github.com/paketo-buildpacks/jam 를 받아서 써야 한다.
    • repo를 로컬에 clone하고
    • go build main.go 실행해서 소스코드를 빌드한 다음
    • 빌드 결과인 binary를 /usr/local/bin 으로 옮겨서 터미널로 실행 가능하도록 한다.

 

이미지를 커스텀한 root 경로로 와서

 

jam create-stack --config stack.toml --build-output test-cpython.oci.tar --run-output test-cpython-run.oci.tar

 

명령어를 실행한다. (공식문서 명령어대로 하면 동작을 안 한다. --build-output과 --run-output의 확장자를 .tar로 명시해야 한다.)

정상적으로 실행되었다면, .tar 파일 두 개가 생성된다.

skopeo login -u <docker.io id> <your-container-registry>

로 registry에 로그인한다. private registry라면 docker.io 대신 private registry domain을 입력하면 된다.

 

skopeo copy oci-archive:test-cpython.oci.tar docker://<your-registry-path>/base-image
skopeo copy oci-archive:test-cpython-run.oci.tar docker://<your-registry-path>/run-image

명령어를 수행하면, local path에 있는 tar 파일을 container registry에 push할 수 있다.

 

새로 만들어진 build-image, run-image로 paketo builder를 생성한다

https://github.com/paketo-buildpacks/buildpackless-base-builder/blob/main/builder.toml

 

위의 repo를 로컬에 clone한 뒤, builder.toml 파일을 아래와 같이 수정한다.

description = "Ubuntu bionic base image with no buildpacks included. To use, specify buildpacks at build time."

[lifecycle]
  version = "0.16.0" # 23.03.17 기준 main브랜치의 lifecycle 버전이 0.16.0으로 설정되어 있었음.

[stack]
  build-image = "<your-registry-path>/base-image"
  id = "io.buildpacks.stacks.bionic"
  run-image = "<your-registry-path>/base-image"
  run-image-mirrors = ["<your-registry-path>/base-image"]

수정한 builder.toml와 pack cli를 사용해서 새 builder image를 생성한다.

 

pack builder create <docker-registry-image>/buildpackless-base-builder-customize --config ./builder.toml --publish

--publish 옵션을 주면 빌드 후 registry push까지 진행된다.

 

커스텀한 builder image를 사용해서 knative function을 빌드한다.

knative function의 빌드에 쓰이는 func.yaml 파일을 대략 아래와 같이 변경한다.

specVersion: 0.25.0
name: ## name
namespace: ## namespace
runtime: python
image: <your-knative-function-image>
imageDigest: ""
build: local
git: {}
builderImages: ## 0.25.0 이후 버전부터는 이 필드의 이름이 약간 달라졌다.
  pack: <docker-registry-image>/buildpackless-base-builder-customize # buildpackless-builder에 cpython binary 추가한 커스텀 builder
buildpacks:
- gcr.io/paketo-buildpacks/python:2.5.0 # python buildpack
volumes: []
buildEnvs:
- name: BP_DISABLE_SBOM
  value: "True"
envs: []
annotations: {}
options: {}
labels: []
healthEndpoints:
  liveness: /health/liveness
  readiness: /health/readiness
created: 2022-10-13T10:05:09.494481+09:00
invocation:
  format: cloudevent

이걸로 knative function 빌드를 시도하면, 아래와 같은 에러가 발생한다.

설치된 binary를 사용하지 않음 + metadata.toml 정보와 일치하지 않는 binary이므로 삭제 시도한다.

 

포기한 이유

paketo buildpack의 경우, 빌드에 사용된 binary의 무결성 확인 용도로 metadata.toml 파일이 image 내부에 생성된다. 대략 아래와 같은 구조.

buildpack-default-process-type = "web"

[[bom]]
  name = "helper"
  [bom.metadata]
    layer = "helper"
    names = ["ca-certificates-helper"]
    version = "3.4.0"
  [bom.buildpack]
    id = "paketo-buildpacks/ca-certificates"
    version = "3.4.0"

[[bom]]
  name = "CPython"
  [bom.metadata]
    cpe = "cpe:2.3:a:python:python:3.10.8:*:*:*:*:*:*:*"
    licenses = ["0BSD", "CNRI-Python-GPL-Compatible", "PSF-2.0"]
    purl = "pkg:generic/python@3.10.8?checksum=f400c3fb394b8bef1292f6dc1292c5fadc3533039a5bc0c3e885f3e16738029a&download_url=https://www.python.org/ftp/python/3.10.8/Python-3.10.8.tgz"
    uri = "https://artifacts.paketo.io/python/python_3.10.8_linux_x64_bionic_b38c22eb.tgz"
    version = "3.10.8"
    [bom.metadata.checksum]
      algorithm = "SHA-256"
      hash = "b38c22eb18d000f88267ab8c845ad88362d05834e0c076be076e290344f3d0e6"
    [bom.metadata.source]
      uri = "https://www.python.org/ftp/python/3.10.8/Python-3.10.8.tgz"
      [bom.metadata.source.checksum]
        algorithm = "SHA-256"
        hash = "f400c3fb394b8bef1292f6dc1292c5fadc3533039a5bc0c3e885f3e16738029a"
  [bom.buildpack]
    id = "paketo-buildpacks/cpython"
    version = "1.7.2"

[[buildpacks]]
  id = "paketo-buildpacks/ca-certificates"
  version = "3.4.0"
  api = "0.7"
  homepage = "https://github.com/paketo-buildpacks/ca-certificates"

[[buildpacks]]
  id = "paketo-buildpacks/cpython"
  version = "1.7.2"
  api = "0.7"

[[buildpacks]]
  id = "paketo-buildpacks/pip"
  version = "0.16.1"
  api = "0.7"

[[buildpacks]]
  id = "paketo-buildpacks/pip-install"
  version = "0.5.7"
  api = "0.7"

[[buildpacks]]
  id = "paketo-buildpacks/python-start"
  version = "0.14.0"
  api = "0.8"
  homepage = "https://github.com/paketo-buildpacks/python-start"

[[buildpacks]]
  id = "paketo-buildpacks/procfile"
  version = "5.4.0"
  api = "0.7"
  homepage = "https://github.com/paketo-buildpacks/procfile"

[[processes]]
  type = "web"
  command = "python -m parliament ."
  args = []
  direct = false
  buildpack-id = "paketo-buildpacks/procfile"

빌드 과정에서 다운로드받은 python buildpack binary와 CPython 정보가 담겨 있다. pack에서 binary를 동적으로 관리할 수 있도록 만들어주는 역할을 하는 것으로 보인다.

여기 있는 metadata.toml 파일까지 커스텀하게 건드린다면 가능할지도 모르겠지만...

  • pack과 buildpack에서 동적으로 binary를 관리하는 핵심 기능을 이 정도까지 수정해가며 무력화시킬 필요가 있을까?
  • 위 metadata.toml는 builder나 buildpack 이미지가 아니라, pack build로 수행된 후 knative function로 배포할 이미지에 저장된다.
    • pack build 프로세스를 하나하나 따라가면서 수정할 부분을 파악해야 하는데, 이 정도까지 할 거라면 그냥 빌드팩을 쓰지 말고 다른 방식으로 이미지 빌드하는 법을 찾는 게 낫다.

 

기타 참고자료

https://www.altoros.com/blog/cloud-native-buildpacks-creating-custom-components/

 

Cloud Native Buildpacks: Creating a Stack for Custom Components

Why create custom buildpack components?Cloud Native Buildpacks (CNB) provide a strong value proposition in terms of both industry trends and process advantages. In the previous post, we wrote how

www.altoros.com

https://www.altoros.com/blog/cloud-native-buildpacks-how-to-create-a-custom-builder/

 

Cloud Native Buildpacks: How to Design a Custom Builder

The builderAfter creating a stack and a buildpack, we are ready to add the third and final Cloud Native Buildpacks component, the builder, to our example repository. The builder specifies which st

www.altoros.com

https://eggboy.medium.com/cloud-native-buildpack-inside-out-e282233206d2

 

Cloud Native Buildpack Inside Out

This is a follow-up to the previous article, Creating Spring Boot container in a minute with Cloud Native Buildpacks for Azure Container…

eggboy.medium.com

 

반응형