컨텐츠로 건너뛰기

Azure Static Web Apps 빌드 속도 높이기 node_module Cache 사용

1. 소개

Azure Static Web Apps는 정적 웹 애플리케이션을 빌드, 배포 및 호스팅하기 위한 서비스로, GitHub 리포지토리와의 통합을 제공하여 CI/CD 파이프라인을 간단히 설정할 수 있습니다. 이 서비스를 사용하면 개발자는 백엔드 기능이 필요하지 않은 간단한 웹 앱을 쉽게 만들고 관리할 수 있습니다. CDN 기반의 전 세계적으로 분산된 호스팅 인프라를 통해 빠르고 안정적인 사용자 경험을 제공합니다.

2. 왜 온몸 비틀기 중인가요?

기본적으로 Azure/static-web-apps-deploy@v1를 사용하면 컨테이너로 둘둘 말아서 빌드를 시작합니다. 따라서, 기본적으로 Github Actions로 똑같이 제공하는 다른 플랫폼 빌드시간과 비교하면 심각하게 빌드시간이 오래걸립니다.

GitHub 작업 청구는 가장 가까운 분 단위로 반올림되므로 이러한 배포 중 다수에는 청구 가능 시간이 무료 범위를 초과할 가능성이 부단히 높습니다. 유료계정을 사용하고 계시다면 역시 고민 할만한 문제가 됩니다.

저는 PNPM을 좋아하지만, 기본적인 버전관리는 로컬에서 PNPM을 사용하고, 빌드시에만 NPM을 사용하는 방식으로 빌드시간을 줄여보고자 합니다. NPM을 사용하다 PNPM으로 넘어가려면, 생각할 거리가 많아지지만, PNPM을 사용하다 NPM을 사용하면 고민할게 많이 없기도 합니다.

그리고 GitHub 작업에 대해 Docker를 사용하는 것이 좀 비효율적입니다. Docker 이미지를 가져오거나 빌드할 필요 없이 최소한 TypeScript 기반 작업이 있어야 합니다. 액션에서 매번 이미지가 재구성된다는 것도 이상하구요.
Azure/static-web-apps-deploy@v1를 다운로드 하면 약 크기가 2기가 가까이 되는 것도 문제인데, 이거는 나중에 최적화 해보는 것으로 하겠습니다.

사실 Azure/static-web-apps-deploy@v1에 대해서 직접적을 최적화를 진행했는데, Azure Static Web Apps에 대한 빌드 구성 를 참고해서 skip_api_build: true, skip_app_build: true 옵션 등 온몸비틀기를 이미 시도했는데 놀랍게도 staticwebapp.config.json를 바이패싱 해버려서 이렇게나마 온몸비틀기를 해봅니다.

3. 온몸 비틀기 전 파악부터 하기

우선 Azure/static-web-apps-deploy@v1에 대해서 파악부터 하겠습니다.

1. 친절한 Azure

빌드를 시작하면 아래처럼 친절하게, 그리고 비용과 관계 없다는 듯 우아하게 빌드를 진행해줍니다.
왜냐고요? Dockerfile 기반이거든요… 관련 의존성을 빌드 요청이 있을때 마다 도커로 말아줍니다…

Try to validate location at: '/github/workspace'.
App Directory Location: '/' was found.
Try to validate location at: '/github/workspace/swa-db-connections'.
Looking for event info
Starting to build app with Oryx
Azure Static Web Apps utilizes Oryx to build both static applications and Azure Functions. You can find more details on Oryx here: https://github.com/microsoft/Oryx
Oryx will build app with the following custom override command: npm run push:azure
---Oryx build logs---
Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
You can report issues at https://github.com/Microsoft/Oryx/issues
Oryx Version: 0.2.20231128.3, Commit: 0b76566110f0db32097b869761e056fd9b01848d, ReleaseTagName: 20231128.3
Build Operation ID: build-8282
OS Type : bullseye
Image Type : jamstack
Detecting platforms...
Detected following platforms:
nodejs: 20.11.0
Version '20.11.0' of platform 'nodejs' is not installed. Generating script to install it...
Detected the following frameworks: Astro,Typescript
Source directory : /github/workspace
Destination directory: /bin/staticsites/build-please-8282-swa-oryx/app
Installing common platform dependencies...
Get:1 http://deb.debian.org/debian bullseye InRelease [116 kB]
Get:2 http://deb.debian.org/debian-security bullseye-security InRelease [48.4 kB]
Get:3 http://deb.debian.org/debian bullseye-updates InRelease [44.1 kB]
Get:4 http://deb.debian.org/debian bullseye/main amd64 Packages [8068 kB]
Get:5 http://deb.debian.org/debian-security bullseye-security/main amd64 Packages [268 kB]
Get:6 http://deb.debian.org/debian bullseye-updates/main amd64 Packages [18.8 kB]
Fetched 8563 kB in 2s (5069 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
Calculating upgrade...
The following packages will be upgraded:
base-files curl libc-bin libc-dev-bin libc6 libc6-dev libcurl3-gnutls
libcurl4 libglib2.0-0 libgnutls30 libmariadb-dev libmariadb-dev-compat
libmariadb3 libnghttp2-14 libperl5.32 libpq-dev libpq5 linux-libc-dev
mariadb-common perl perl-base perl-modules-5.32 tar tzdata
24 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 22.6 MB of archives.
After this operation, 89.1 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bullseye/main amd64 base-files amd64 11.1+deb11u9 [70.2 kB]
Get:2 http://deb.debian.org/debian bullseye/main amd64 libperl5.32 amd64 5.32.1-4+deb11u3 [4121 kB]
Get:3 http://deb.debian.org/debian bullseye/main amd64 perl amd64 5.32.1-4+deb11u3 [293 kB]

2. 이게 전부냐고요?

그럴리가요 친절하게 그리고 우아하게 npm만 사용하시라고 npm install도 진행해줍니다.

Removing existing manifest file
Creating directory for command manifest file if it does not exist
Creating a manifest file...
Node Build Command Manifest file created.
Using Node version:
v20.11.0
Using Npm version:
10.2.4
Running 'npm install'...

사실 여기서부터 정신이 어질어질 해졌는데, 저기 npm install을 막을 방법이 없습니다.
도커 파일의 바이너리에 포함되어 있습니다. (/bin/staticsites)

3. Github actions에서 npm install을 하면?

우리의 우아한 동작을 보이는 Azure는 github actions에서

- run: npm install

을 넣어버리면 아~! 너 PHP도 필요하구나?

Detecting platforms...
Detected following platforms:
nodejs: 20.11.0
php: 8.0.30
Version '20.11.0' of platform 'nodejs' is not installed. Generating script to install it...
Version '8.0.30' of platform 'php' is not installed. Generating script to install it...
Detected the following frameworks: Astro,Typescript

이렇게 친절하게 PHP도 설치해 줍니다. 그래서 시간이 전혀 단축되지 않습니다.

4. 빠르게 회피해봅시다.

정리

  1. skip_api_build: true, skip_app_build: true 옵션 사용하면 staticwebapp.config.json를 바이패싱 해버린다.
  2. Dockerfile로 빌드를 시도한다.
  3. 바이너리로 npm install을 막을 방법이 없다. (있는데 귀찮다)
  4. 현재 디렉토리를 전부 Dockerfile 로 COPY를 시도한다. (COPY . .)
  5. 무식하게 덩치큰 node_module을 git에 우겨넣을 수는 없다.

결론

  1. 앗 그러면 github actions에서 node를 설치하고
  2. npm install을 하지 않고, npm의 node_module만 캐시로 사용하면 되지 않을까?
  3. 어차피 Dockerfile에서 npm install을 해주는데 ?

간단히 추가합니다.

- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: 'package-lock.json'

노드 버전은 api의 경우 staticwebapp.config.json 혹은 app의 경우 package.json에 적절히 우겨넣어 줍니다.

원래의 Azure Static Web Apps CI/CD.yml
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: 'package-lock.json'
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_궁시렁궁시렁 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: 'upload'
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: '/' # App source code path
api_location: '' # Api source code path - optional
output_location: 'dist' # Built app content directory - optional
###### End of Repository/Build Configurations ######
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_궁시렁궁시렁 }}
action: 'close'

5. 빌드 시간이 얼마나 단축되었을까요?

1. 기본 값: 4분 27초

기본 값 적용시: 소요시간 4분 27초

  • 저는 Opengraph image를 동적으로 생성하고 있기도 하고
  • HTML과 JS, CSS등의 minify도 진행하고 있어서 시간이 오래걸린 것도 있습니다.

2. 최적화 적용 값: 2분 57초

Github Actions Cache 적용시: 소요시간 2분 57초 당연히 캐시 생성되고나서 2회차 결과입니다.

3. 생성된 캐시 크기: 450MB

저 정말 PNPM으로도 온몸 비틀기 해봤다니까요!!

6. 여담

저는 app 뿐만 아니라 api도 있는데 우리의 친절한 Azure는 api 경로에 대해서도 역시 우아한 방법으로 별개로 취급하는거 아세요? 그래서 api에도 무언가가 있으면 단축 시간이 극적이지는 않습니다… 어지럽네요…

관련 이슈도 있는데 2년 이상 방치된걸 봐서는, 직접 도커 이미지를 말아야겠습니다…
차라리 Github Actions에서 CLI로 하는게 더 빠르고 비용 효율적일 것 같기도 하구요…

수고 많으셨습니다. 감사합니다.


Askfront.com (에스크프론트)

기존의 댓글 대신, 초보자도 자유롭게 질문할 수 있는 포럼을 만들었습니다.
에스크프론트에서는 가이드뿐만 아니라 모든 종류의 질문을 하실 수 있습니다.
검색해도 오래된 정보나 도움이 되지 않는 정보만 나오는 것 같고, 주화입마에 빠진 것 같은 기분이 들 때가 있습니다.
그럴 때, 부담 없이 질문해 주세요 :) 같이 의논하며 생각해봅시다.
가능하다면, 제가 답변 드리겠습니다. 고맙습니다.