카테고리 보관물: Tools & Tips

Update failed: upgrade-temp-backup 디렉토리를 생성할 수 없습니다.

WordPress에서 몇 번 플러그인 업데이트 실패에 대한 알람이 왔었는데, 오늘에야 살펴보니 자동은 물론 수동 업데이트도 계속해서 실패하고 있었다.

새로 릴리즈된 WordPress 6.3에서 플러그인과 테마를 롤백할 수 있는 기능이 추가되었는데, 이 기능을 위해 wp-content/upgrade-temp-backup/plugins와 wp-content/upgrade-temp-backup/themes 디렉토리를 사용하는 모양이다. (관련 기사 링크)

문제의 원인은 간단했는데, wp-content 디렉토리의 쓰기 권한이 daemon에게 없기 때문에 디렉토리를 만들지 못하고 실패하는 것이었다. 따라서 간단히 디렉토리를 수동으로 만들고 권한을 할당해 주는 것으로 해결되었다.

콘솔에 접근해서 다음과 같이 명령어들을 입력한다.

# WordPress경로로 이동해서
#e.g) cd ~/apps/wordpress/htdocs

# 디렉토리 경로를 만들어주고
mkdir -p ./wp-content/upgrade-temp-backup/plugins
mkdir -p ./wp-content/upgrade-temp-backup/themes

# 소유권 그룹을 할당한 다음
sudo chown bitnami:daemon -R ./wp-content/upgrade-temp-backup

# daemon group에 쓰기 권한을 준다.
chmod 775 -R ./wp-content/upgrade-temp-backup

관리자 화면으로 돌아와서 설치 재시도 하니 잘 동작되었다.

Multi-container app의 Bad Gateway(502) 문제 디버깅

Intel Training and Learning Suite(TLS)는 처음으로 경험해보는 multi-container application이다. 중간에 native개발을 위한 환경 설정을 끼워 넣어 보려고 이것 저것 시도하는 중에 web interface를 담당하는 tls_proxy라는 docker가 Bad Gateway를 띄우면서 문제가 생겼다. 디버깅 하는 과정에서 사용해 본 docker-compose명령어 들의 쓰임새가 유용할 것 같아서 기록으로 남겨 둔다.

Port number와 docker찾기: docker-compose ps

Web browser로 서비스에 접근하면(https://localhost) Bad Gateway(502) error가 발생하는데 처음으로 web-browser의 접속 요청을 받아서 처리하는 docker container는 docker-compose ps 명령어로 docker image별로 listening하는 port number들을 찾을 수 있다.

TLS의 경우는 tls_proxy가 http요청 처리를 위한 80번과 https를 위한 443번 port를 listening하고 있다.

$ docker-compose ps
    Name                  Command               State     Ports                     
------------------------------------------------------------------
tls_apiui      ./webservices/start.sh           Exit 0                                                                                                                                                     
tls_core       ./start.sh                       Up                                                                                                                                                         
tls_mongo      docker-entrypoint.sh --tls ...   Up       127.0.0.1:27017->27017/tcp                                                                                                                        
tls_openvino   /bin/bash                        Exit 0                                                                                                                                                     
tls_proxy      /docker-entrypoint.sh /bin ...   Up       0.0.0.0:443->443/tcp,:::443->443/tcp, 80/tcp                                                                                                      
tls_rabbitmq   docker-entrypoint.sh /init ...   Up       0.0.0.0:15672->15672/tcp,:::15672->15672/tcp, 15691/tcp, 15692/tcp, 0.0.0.0:1883->1883/tcp,:::1883->1883/tcp, 25672/tcp, 4369/tcp, 5671/tcp, 5672/tcp, 0.0.0.0:8883->8883/tcp,:::8883->8883/tcp
tls_redis      docker-entrypoint.sh sh st ...   Up       127.0.0.1:6379->6379/tcp

Docker의 log 보기: docker logs tls_proxy

tls_proxy가 요청을 처리하지 못한 이유는 docker logs <서버 이름>으로 확인할 수 있다.

$ docker logs tls_proxy
2023/05/31 01:31:06 [error] 12#12: *5 connect() failed (113: Host is unreachable) while connecting to upstream, client: 172.31.0.1, server: , request: "GET / HTTP/1.1", upstream: "http://172.31.0.8:3000/", host: "localhost"
...

사설 네트워크인 172.31.0.x로 docker service들을 구성했는데, 그중 인터페이스를 담당하는 172.31.0.1 docker가 172.31.0.8에 요청을 연결해 주어야 하는데 이 부분에서 오류가 나고 있는 모양이다.

Docker network 확인: docker network ls

현재 docker용으로 구성되어 있는 network을 확인해 보면 다음과 같다.

$ docker network ls
NETWORK ID     NAME                DRIVER    SCOPE
d15b28e7a1d5   bridge              bridge    local
acc99b5947d1   cvat_cvat           bridge    local
4485972f8274   host                host      local
6f21c18ba66f   none                null      local
20d132ccfc2a   tls_default         bridge    local

각 네트워크의 구성을 검사(inspect)해 볼 수 있는데 docker network inspect cvat_cvat명령어로 해당 네트워크를 검사해 보면 다음과 같이 위에서 오류가 발생했던 ip인 172.21.0.8번(tls_openvino)이 등록되지 않은 것을 볼 수 있다.

$ docker network inspect cvat_cvat|grep "Name\|IPv4Address"
        "Name": "cvat_cvat",
                "Name": "cvat_redis",
                "IPv4Address": "172.31.0.4/16",
                "Name": "cvat_opa",
                "IPv4Address": "172.31.0.3/16",
                "Name": "cvat_db",
                "IPv4Address": "172.31.0.5/16",
                "Name": "traefik",
                "IPv4Address": "172.31.0.2/16",
                "Name": "cvat",
                "IPv4Address": "172.31.0.6/16",
                "Name": "cvat_ui",
                "IPv4Address": "172.31.0.7/16",
                "Name": "tls_proxy",
                "IPv4Address": "172.31.0.9/16",

Docker의 log 보기: docker logs tls_openvino

문제가 되고 있는 docker container를 특정했으니 docker logs tls_openvino명령어로 다시 한번 해당 container의 로그를 확인해 본다.

error: A hook (`userconfig`) failed to load!
error: Failed to lift app: Error: Attempted to `require('/home/tls/webservices/apiserver/config/env/production.js')`, but an error occurred:
--
Error: ENOENT: no such file or directory, open '../../thirdparty/security/ca/ca_certificate.pem'
    at Object.openSync (fs.js:498:3)
    at Object.readFileSync (fs.js:394:35)
    at Object.<anonymous> (/home/tls/webservices/apiserver/config/env/production.js:70:15)

../../third party/security/ca/ca_certificate.pem파일을 읽지 못해서 오류가 발생했다고 나오는데, 실제로 container에서는 해당 파일의 위치를 상대 경로로 접근하지 않는다. Call stack에 나와 있는 production.js line 70근처의 내용을 보면 먼저 절대경로인 /run/secrets/*에 접근을 시도할 때 예외가 발생해서 상대 경로로 접근을 시도하고(개발용 코드로 추정) 이 마저도 실패한 것이 로그에 나온 것이다.

apiserver/config/env/production.js

try {
  tls_ca = fs.readFileSync("/run/secrets/ca_tls")
  tls_server_cert = fs.readFileSync("/run/secrets/tlsserver_cert")
  tls_server_key = fs.readFileSync("/run/secrets/tlsserver_key")
} catch (err) {
  tls_ca = fs.readFileSync("../../thirdparty/security/ca/ca_certificate.pem");
  tls_server_cert = fs.readFileSync("../../thirdparty/security/TLS_server_cert.crt");
  tls_server_key = fs.readFileSync("../../thirdparty/security/TLS_server_key.pem");
}

정리하자면, 문제는 server secret을 생성하는 과정에서 발생한 것이 docker image상의 key file path read에서 오류를 발생시키고 tls_openvino container가 제대로 뜨지 못한 문제이다.

해결(?)

문제의 원인은 생각보다 싱거웠는데, docker-compose 명령어를 실행시키는 native용 script에서 다음 줄을 연결하기 위한 \를 빼먹는 사소한(!) Bash 문법 오류가 발생 했었고, 이 오류가 무시된 채로 docker-compose build 명령어가 계속 수행되어 service가 up되는 상황까지 된 것이었다.

-       && pip install -U pip wheel setuptools
+       && pip install -U pip wheel setuptools \
        && pip install \

위의 수정과 함께 오류가 생기면 build를 멈추도록 set -e를 bash script에 추가 하는 것으로 이 문제는 일단락 되었다.

Mac OSX에서 numpy 설치할 때 빌드 실패 문제

Intel Mac에서 pip로 numpy(ver1.22)를 설치하려고 했더니 설치에 실패하면서 아주 아주 긴 오류가 나오는데 Clang compiler option의 architecture flag가 좀 이상하다.

% python3 -m pip install numpy
...
 clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -iwithsysroot/System/Library/Frameworks/System.framework/PrivateHeaders -iwithsysroot/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/Headers -arch arm64 -arch x86_64 ...
  clang: error: the clang compiler does not support '-march=native'
...

“-arch arm64 -arch x86_64” 라는 건 Apple silicon과 Intel architecture를 모두 지원해 보겠다는 뜻인가? 혹시나 해서 환경 변수로 ARCHFLAGS=”-arch x86_64″를 주고 재 실행해 봤더니 이전에 있던 -march=native 플래그가 지원되지 않는다는 에러가 없어지면 잘 설치가 되었다.

% ARCHFLAGS="-arch x86_64" python3 -m pip install numpy

좀 더 일반적으로 적용될 수 있는 다른 방법으로 “–only-binary” 옵션을 주어서 wheel package file build를 안 하도록 하는 방법도 고려해 볼 수 있겠다.

% pip install numpy --only-binary numpy
Collecting numpy
  Downloading numpy-1.22.4-cp38-cp38-macosx_10_15_x86_64.whl (17.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 17.6/17.6 MB 30.0 MB/s eta 0:00:00
Installing collected packages: numpy
Successfully installed numpy-1.22.4

Visual Studio Code test explorer에서 explorer로 변경되는 문제

최근 업데이트(1.65.2) 이후 부터 였던 것 같은데 VSCode의 test explorer에서 테스트를 실행하면 자꾸만 explorer로 자동 변경되는 문제가 생겼다.

이 문제는 새롭게 생긴 설정인 testing.openTesting을 neverOpen으로 변경해 주면 해결된다.

{ // settings.json
   ...
    "testing.openTesting": "neverOpen"
}

또는

Setings -> Features -> Testing -> Testing: Open Testing

왜 이런 짓을??

정보의 바다에서 이와 관련해 의견을 나눈 기록을 찾았는데 Switching to test explorer when users clicks ‘run tests’에 따르면 커스터머 인터뷰 중에 IntelliJ에 구현된 이와 관련된 유사한 기능이 요청된 모양이다. 다행히도 이 기능이 VSCode에서는 오히려 불편하기만 할꺼라는 의견을 가진 사람 덕분에 option으로 빠지게 되었는데 default 값은 이 기능이 동작하는 openOnTestStart로 되어 버렸다. 변경 사항에 관련된 ticket은 여기에서 찾을 수 있다.

Windows DLL loading 실패 디버깅

Windows programming 중에 DLL loading을 실패해서 오류가 발생하면 문제가 생겼다는 에러코드(0xc0000135)만 출력되고 어떤 것이 실패했는지 또 어느 곳을 찾아 봤는지에 대한 정보는 자세히 알려주지 않는다.

Windows SDK에 포함된 gflags.exe(Global Flags)를 이용하면 DLL loading에 대한 세부정보를 볼 수 있다. gflags.exe가 실행하는데 admin권한을 필요로 하기 때문에 관리자 권한으로 터미널을 띄우고 Windows SDK안에 포함되어 있는 gflags.exe를 다음과 같이 실행해서 세부 내역을 보고 싶은 실행파일의 이름과 sls(Show Loader Snaps) option을 명시 해준다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe -i <프로그램이름.exe> +sls
Current Registry Settings for <프로그램이름.exe> executable are: 00000002
    sls - Show Loader Snaps

다시 Visual studio로 돌아와서 프로그램을 실행하면 Output window에 DLL loading에 실패한 라이브러리의 이름과 시도해본 path들에 대한 세부정보가 표시된다.

8e9c:55d4 @ 621281687 - LdrpFindKnownDll - ENTER: DLL name: <실패하는 라이브러리>.dll
8e9c:55d4 @ 621281687 - LdrpFindKnownDll - RETURN: Status: 0xc0000135
8e9c:55d4 @ 621281687 - LdrpFindKnownDll - ENTER: DLL name: vccorlib140d_app.DLL
8e9c:55d4 @ 621281687 - LdrpFindKnownDll - RETURN: Status: 0xc0000135
8e9c:55d4 @ 621281687 - LdrpFindKnownDll - ENTER: DLL name: MSVCP140D_APP.dll
8e9c:55d4 @ 621281687 - LdrpFindKnownDll - RETURN: Status: 0xc0000135

이전 상태로 되돌리려면 옵션을 `-sls`로 변경해서 다시 한 번 gflgas.exe를 실행해 주면 된다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe -i ieproxytest.exe -sls
Current Registry Settings for ieproxytest.exe executable are: 00000000