Microsoft MVP성태의 닷넷 이야기
오류 유형: 934. Azure - rm: cannot remove '...': Directory not empty [링크 복사], [링크+제목 복사],
조회: 5314
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

Azure - rm: cannot remove '...': Directory not empty

Azure Web App 환경의 경우, 예를 들어 Python 앱이라면,

파이썬 웹 앱을 Azure App Service에 배포하기
; https://www.sysnet.pe.kr/2/0/13830

리눅스 환경에서 호스팅하게 되는데요, 여기서 임의의 데이터를 지속하고 싶다면 반드시 /home 디렉터리 하위에 위치시켜야 합니다.

예를 들어, 파이썬 앱에서 사용하는 패키지를 다음과 같이 설치하면,

$ pip install test-app

Scale-out이 된 경우, 저 SSH로 들어간 환경에서만 test-app 패키지를 사용할 수 있는 데다, 만약 해당 Web App 인스턴스가 재시작하면 저 test-app을 설치한 디렉터리는 다시 초기화되므로 사라지게 됩니다.

따라서, 이런 경우 반드시 /home 디렉터리 하위에 설치해야 합니다.

$ pip install test-app -t /home/test_pacakge

/home 디렉터리 외에도, Azure는 이를 우회할 수 있는 자동화 스크립트 방법을 제공하는데요,

Customize build automation
; https://learn.microsoft.com/en-us/azure/app-service/configure-language-python#customize-build-automation

정리하면 SCM_DO_BUILD_DURING_DEPLOYMENT 환경변수를 1로 설정하고, 원하는 스크립트를 PRE_BUILD_COMMAND / POST_BUILD_COMMAND 환경변수에 설정하거나, 보유하고 있는 requirements.txt에 해당 패키지를 추가하는 식으로 처리할 수 있습니다.




여기서는 /home 디렉터리 하위에 직접 사용자가 설치한 경우를 예로 들어 보겠습니다.

$ pip install test-app -t /home/test_pacakge

이런 상태에서 Python Web App이 실행 중일 때, /home/test_pacakge 디렉터리 하위에 있는 "실행 파일"을 함께 구동했다고 가정하면, 그 실행 파일은 Web App이 Scale-out하면 그 인스턴스 수만큼 실행이 될 것입니다.

재미있는 건, 그 상태에서 해당 패키지가 업그레이드돼 다시 설치하게 되면,

# pip install test-app -U -t /home/test_pacakge
Collecting test-app
...[생략]...

ERROR: Exception:
Traceback (most recent call last):
  File "/tmp/8dd0d13008cfbf9/antenv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py", line 105, in _run_wrapper
    status = _inner_run()
             ^^^^^^^^^^^^
  File "/tmp/8dd0d13008cfbf9/antenv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py", line 96, in _inner_run
    return self.run(options, args)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/8dd0d13008cfbf9/antenv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py", line 67, in wrapper
    return func(self, options, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/8dd0d13008cfbf9/antenv/lib/python3.12/site-packages/pip/_internal/commands/install.py", line 519, in run
    self._handle_target_dir(
  File "/tmp/8dd0d13008cfbf9/antenv/lib/python3.12/site-packages/pip/_internal/commands/install.py", line 574, in _handle_target_dir
    shutil.rmtree(target_item_dir)
  File "/opt/python/3.12.2/lib/python3.12/shutil.py", line 785, in rmtree
    _rmtree_safe_fd(fd, path, onexc)
  File "/opt/python/3.12.2/lib/python3.12/shutil.py", line 686, in _rmtree_safe_fd
    _rmtree_safe_fd(dirfd, fullname, onexc)
  File "/opt/python/3.12.2/lib/python3.12/shutil.py", line 686, in _rmtree_safe_fd
    _rmtree_safe_fd(dirfd, fullname, onexc)
  File "/opt/python/3.12.2/lib/python3.12/shutil.py", line 697, in _rmtree_safe_fd
    onexc(os.rmdir, fullname, err)
  File "/opt/python/3.12.2/lib/python3.12/shutil.py", line 695, in _rmtree_safe_fd
    os.rmdir(entry.name, dir_fd=topfd)
OSError: [Errno 39] Directory not empty: 'amd64'

이런 식의 오류가 발생합니다. 실제로 해당 경로를 직접 삭제하려고 하면 문제가 되는 디렉터리가 밝혀지는데요,

# rm -rf /home/test_pacakge
rm: cannot remove '/home/test_pacakge/bin/linux/amd64': Directory not empty

저 amd64에는 실행 파일이 있고, 그 실행 파일이 사용 중이라 삭제가 안 되는 것입니다. 따라서, 저 디렉터리가 삭제될 수 있어야 하는데요, 만약 Web App 재시작으로 가능하다면 그렇게 하면 됩니다.

혹은, SSH 환경에서 직접 저 실행 파일을 kill 하면 되는데요,

# ps aux | grep test-app
root          65  0.1  0.2 714036  4920 ?        SNl  01:34   0:13 /home/test_package/bin/linux/amd64/test-app
root         139  0.0  0.0   6240   620 pts/1    SN+  04:31   0:00 grep test-app

# kill 65

# ps aux | grep test-app
root         141  0.0  0.0   6240   652 pts/1    SN+  04:31   0:00 grep test-app

Scale-out 환경이라면 저렇게 했어도 여전히 디렉터리 삭제가 안 될 것입니다. 왜냐하면, 저렇게 실행 중인 프로세스를 모든 인스턴스에서 종료해야 하기 때문입니다.




한 가지 재미있는 점이 있는데요, Azure Web App 환경의 SSH 접속으로 설치한 파일들의 소유자를 보면,

# pip install test-app -t /home/test_pacakge

# ls /home/test_pacakge/ -l
total 0
drwxrwxrwx 2 nobody nogroup 0 Dec  2 01:19 bin
drwxrwxrwx 2 nobody nogroup 0 Dec  2 01:19 test_app
drwxrwxrwx 2 nobody nogroup 0 Dec  2 01:19 test_app-1.0.0.0.dist-info

저렇게 nobody, nogroup이 있습니다. 검색해 보면,

Debian change owner of nobody:nogroup
; https://unix.stackexchange.com/questions/563942/debian-change-owner-of-nobodynogroup

이게 컨테이너 내에서 볼 수 없는 상위 네임스페이스에서 설정된 파일 권한이어서 그렇다고 합니다.

# ls -n 
total 3064
-rwxrwxrwx 1 65534 65534 3137536 Nov 29 01:46 test-app

이런 경우 저렇게 uid/gid도 65534로 나온다고. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 12/3/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 91  92  93  94  95  96  97  98  99  100  [101]  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11407정성태12/18/201724220.NET Framework: 712. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV [1]파일 다운로드1
11406정성태12/17/201746615.NET Framework: 711. C# - OpenCvSharp의 Mat 데이터 조작 방법 [5]파일 다운로드1
11405정성태12/17/201742612.NET Framework: 710. C# - OpenCvSharp을 이용한 Webcam 영상 처리 + Direct2D [1]파일 다운로드1
11404정성태12/16/201729905.NET Framework: 709. C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 + Direct2D [7]파일 다운로드1
11403정성태12/16/201732516.NET Framework: 708. C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 [3]파일 다운로드1
11402정성태12/15/201737171.NET Framework: 707. OpenCV 응용 프로그램을 C#으로 구현 - OpenCvSharp [2]파일 다운로드1
11401정성태12/15/201726106.NET Framework: 706. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 [2]파일 다운로드1
11400정성태12/14/201728971.NET Framework: 705. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 [9]파일 다운로드1
11399정성태12/13/201717598.NET Framework: 704. Win32 API의 UnionRect를 닷넷 BCL의 Rectangle.Union으로 바꿀 때 주의 사항
11398정성태12/13/201717801오류 유형: 442. ASP.NET Core Web Application (on .NET Framework) 프로젝트에서 외부 라이브러리 동적 로드 시 런타임 버전 문제파일 다운로드1
11397정성태12/12/201720395.NET Framework: 703. 양자 컴퓨팅을 위한 마이크로소프트의 Q# 언어
11396정성태12/8/201742766개발 환경 구성: 343. Visual Studio - 리눅스 용 프로젝트의 인텔리센스를 위한 헤더 파일 처리 방법 [3]
11395정성태12/8/201718621오류 유형: 441. 이벤트 로그 - Time Provider NtpClient: No valid response has been received from domain controller
11394정성태12/8/201718249개발 환경 구성: 342. 비주얼 스튜디오에서 실행하던 ASP.NET Core (.NET Framework) 응용 프로그램을 명령행에서 실행하는 방법
11393정성태12/7/201722806Windows: 145. 윈도우 10 빌드 17046부터 WSL에서 백그라운드 작업 지원 [5]
11392정성태12/7/201718063개발 환경 구성: 341. openSUSE에 닷넷 코어 설치
11391정성태12/7/201720913개발 환경 구성: 340. WSL을 이용해 윈도우 PC 1대에서 openSUSE 응용 프로그램을 Visual Studio로 개발하는 방법 [1]
11390정성태12/7/201729567개발 환경 구성: 339. WSL을 이용해 윈도우 PC 1대에서 Linux 응용 프로그램을 Visual Studio로 개발하는 방법 [6]
11389정성태12/7/201718236오류 유형: 440. .NET Core 오류 - 0x80131620 Unable to load DLL 'libuv'
11388정성태12/6/201721874개발 환경 구성: 338. WSL 또는 Ubuntu에 닷넷 코어 설치 [3]
11387정성태12/6/201722234오류 유형: 439. 이벤트 로그 - Data Sharing Service 서비스의 %%3239247874 오류 메시지
11386정성태12/5/201717804오류 유형: 438. Hyper-V - '...' failed to add device 'Virtual CD/DVD Disk'
11385정성태12/5/201730916VC++: 121. DXGI를 이용한 윈도우 화면 캡처 소스 코드(Visual C++) [16]파일 다운로드1
11384정성태12/5/201720202오류 유형: 437. Visual C++ - Cannot open include file: 'SDKDDKVer.h'
11383정성태12/4/201723304디버깅 기술: 110. 비동기 코드 실행 중 예외로 인한 ASP.NET 프로세스 비정상 종료 현상 [1]
11382정성태12/4/201721884오류 유형: 436. System.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired 예외 발생 시 "[Pre-Login] initialization=48; handshake=1944;" 값의 의미
... 91  92  93  94  95  96  97  98  99  100  [101]  102  103  104  105  ...