No space left on device
Github Action 워크플로 스크립트에서 runner: ubuntu-latest 를 사용하여 컨테이너 이미지를 빌드하다가 만난 오류이다.
github action의 기본 ubuntu runner의 디스크 크기가 그리 넉넉하진 않다(14GB):
더 큰 runnner가 있다:
Processor (CPU) | Memory (RAM) | Storage (SSD) | Operating system (OS) |
4 | 16 GB | 150 GB | Ubuntu |
8 | 32 GB | 300 GB | Ubuntu, Windows |
16 | 64 GB | 600 GB | Ubuntu, Windows |
32 | 128 GB | 1200 GB | Ubuntu, Windows |
64 | 256 GB | 2040 GB | Ubuntu, Windows |
(스토리지 엄청 넉넉하네; 역시 기본 runner만 빡빡한건가)
하지만 github action의 과금 체계는 vCPU 수에 비례하기 때문에 비용이 늘어난다:
검색
github action runner no space left on device 로 검색해보면 역시나 똑같은 고통을 받았던 사람들이 있다:
여기서 몇 가지 힌트를 얻었다.
•
runner의 디스크 사용량을 들여다 볼 필요가 있다는 것(df -h)
•
채택된 답변처럼 사용하지 않는 불필요한 것을 삭제하면 되겠다는 것
그리고 이것과 관련한 좋은 사례를 찾았다:
구현
전사에 제공하는 build/push 용 composite 워크플로에 적용하자. 먼저 df -h 로 디스크 사용량을 확인한다:
name: Docker Image Build and Push
description: ...
inputs:
...
debug:
description: "Debug mode"
required: false
default: ""
...
runs:
using: "composite"
steps:
- name: Check disk
if: ${{ inputs.debug == 'true' }}
shell: bash
run: df -h
...
YAML
복사
모든 run 마다 불필요한 로그를 남기지 않기 위해 debug 입력을 추가하고 활성화 되었을 때만 실행하도록 했다. 이렇게 출력한 현재 ubunu-latest(22.04) runner의 디스크는 다음과 같다:
> Run df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 84G 60G 25G 72% /
tmpfs 3.4G 172K 3.4G 1% /dev/shm
tmpfs 1.4G 1.1M 1.4G 1% /run
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sdb15 105M 6.1M 99M 6% /boot/efi
/dev/sda1 14G 4.1G 9.0G 31% /mnt
tmpfs 693M 12K 693M 1% /run/user/1001
Bash
복사
여유 공간 25G. 문서의 14GB 보단 많다. 그리고 전체 디스크 크기는 80G 였다. 무엇에 이렇게 많이 쓰고 있을지 확인해본다:
steps:
...
- name: List packages
if: ${{ inputs.debug == 'true' }}
shell: bash
run: |
echo "Listing 100 largest packages"
dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 100
- name: List largest directories under /usr
if: ${{ inputs.debug == 'true' }}
shell: bash
run: |
echo "Listing 100 largest directories under /usr"
sudo du -ah /usr | sort -h | tail -n 100
...
YAML
복사
위에서 링크한, flink에서 사용하는 free_disk_space.sh을 참고하여 크기가 큰 패키지와 디렉토리를 확인한다. 디렉토리를 /usr/ 아래를 검사하는 것은 다른 시스템 디렉토리는 괜히 건드리지 않기 위함 같다. 결과의 일부를 공유하면:
> Run echo "Listing 100 largest packages"
Listing 100 largest packages
...
199815 temurin-8-jdk
208517 gcc-13
240557 firefox
251340 llvm-13-dev
270995 llvm-14-dev
293154 llvm-15-dev
317409 temurin-11-jdk
322796 temurin-17-jdk
326369 google-chrome-stable
337404 dotnet-sdk-6.0
351993 temurin-21-jdk
390274 dotnet-sdk-8.0
403092 dotnet-sdk-7.0
555711 microsoft-edge-stable
783977 azure-cli
785565 google-cloud-cli
> Run echo "Listing 100 largest directories under /usr"
Listing 100 largest directories under /usr
...
1.9G /usr/share/swift
1.9G /usr/share/swift/usr
2.0G /usr/local/.ghcup/ghc/9.6.3/lib
2.0G /usr/local/.ghcup/ghc/9.6.3/lib/ghc-9.6.3
2.0G /usr/local/.ghcup/ghc/9.8.1/lib
2.0G /usr/local/.ghcup/ghc/9.8.1/lib/ghc-9.8.1
2.0G /usr/local/lib/android/sdk/ndk/24.0.8215888/toolchains
2.0G /usr/local/lib/android/sdk/ndk/24.0.8215888/toolchains/llvm
2.0G /usr/local/lib/android/sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt
2.0G /usr/local/lib/android/sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64
2.0G /usr/local/lib/android/sdk/ndk/26.1.10909125/toolchains
2.0G /usr/local/lib/android/sdk/ndk/26.1.10909125/toolchains/llvm
2.0G /usr/local/lib/android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt
2.0G /usr/local/lib/android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64
2.1G /usr/local/lib/android/sdk/ndk/26.1.10909125
2.2G /usr/local/lib/android/sdk/ndk/24.0.8215888
2.6G /usr/local/.ghcup/ghc/9.6.3
2.7G /usr/local/.ghcup/ghc/9.8.1
2.9G /usr/lib
2.9G /usr/local/lib/android/sdk/build-tools
4.8G /usr/share
5.3G /usr/local/.ghcup
5.3G /usr/local/.ghcup/ghc
5.8G /usr/local/lib/android/sdk/ndk
12G /usr/local/lib/android
12G /usr/local/lib/android/sdk
14G /usr/local/lib
23G /usr/local
33G /usr
Bash
복사
결과와 참고 스크립트를 대조하면 어떤 패키지가 그리고 어떤 디렉토리가 도커 이미지 빌드 시 필요치 않지만 크기가 큰지 파악할 수 있다. 따라서 현재 ubuntu-latest runner에선 다음과 같이 삭제하고 사용하기로 했다:
inputs:
...
large-image:
description: "Large image"
required: false
default: ""
...
steps:
...
# ref. https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh#L28-L53
- name: Free unused disk space
if: ${{ inputs.large-image == 'true' }}
shell: bash
run: |
# delete packages
sudo apt-get remove -y '^dotnet-.*'
sudo apt-get remove -y '^llvm-.*'
sudo apt-get remove -y '^temurin-.*'
sudo apt-get remove -y '^mysql-server-core-.*'
sudo apt-get remove -y '^postgresql-.*'
sudo apt-get remove -y azure-cli google-chrome-stable google-cloud-cli firefox powershell microsoft-edge-stable mono-devel
sudo apt-get autoremove -y
sudo apt-get clean
# delete directories
sudo rm -rf /usr/share/dotnet/
sudo rm -rf /usr/share/swift
sudo rm -rf /usr/share/miniconda
sudo rm -rf /usr/local/graalvm/
sudo rm -rf /usr/local/.ghcup/
sudo rm -rf /usr/local/share/powershell
sudo rm -rf /usr/local/share/chromium
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/local/lib/node_modules
YAML
복사
결과적으로 이 작업은 large-image 라는 입력 플래그로 실행 여부를 제어했다. 시간이 꽤 걸리기 때문. 검색 결과에서 타고타고 들어가다가 위 스크립트를 만났지만, github action ubuntu runner에 어떤 패키지를 설치하는지도 확인할 수 있었다:
Operating system | Package manager | Third-party repos and packages |
Ubuntu | ||
ansible-coreyamllint |
결과
다시 한번 df -h 했을 때, 기존 대비 약 40%, 31G의 디스크 공간을 확보하게 됐다:
> Run df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 84G 28G 56G 34% /
tmpfs 3.4G 172K 3.4G 1% /dev/shm
tmpfs 1.4G 1.2M 1.4G 1% /run
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sdb15 105M 6.1M 99M 6% /boot/efi
/dev/sda1 14G 4.1G 9.0G 31% /mnt
tmpfs 693M 12K 693M 1% /run/user/1001
Bash
복사
56G 정도의 여유 공간이면 도커 이미지 빌드하는데에 문제 없겠지…?
습득 교훈
•
지금은 large-image 라는 플래그 인자로 디스크 공간 확보(불필요한 패키지 및 디렉토리 삭제)를 한다. self-hosted runner로 더 최적화 할 수 있을지 고민해볼 수 있다(처음에 비교했을 땐 github hosted runner가 사용 + 관리 비용에서 더 이점이 있다고 판단했다).