카테고리 보관물: Linux

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에 추가 하는 것으로 이 문제는 일단락 되었다.

Ubuntu 22.04 Linux kernel 6.1.1로 올리기

Ubuntu 22.04에서 Linux kernel 6.1.1 빌드해서 설정한 내용 정리이다. 굳이 실험정신을 억누르지 못하는게 아니라면 간단히 deb package를 다운로드 받아서 설치해도 된다.

Prerequisites

컴파일에 필요한 tool들을 설치한다.

sudo apt-get install libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf llvm

Linux kernel code를 다운로드 받고 합축을 해제해 둔다.

wget -c https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.1.tar.xz && tar xvf ./linux-6.1.1.tar.xz

Kernel config 설정 및 build 실행

Linux kernel config를 하나하나 설정하려면 시간이 오래 걸리니 현재 잘 동작하는 있는 kernel 설정 파일을 가져와서 설정하자.

cd ./linux-6.1.1
cp /boot/config-$(uname -r) ./.config

fakeroot debian/rules clean
make olddefconfig

추가로, 다음과 같이 복사된 .config를 열어서 KEY 설정을 주석으로 없애주는게 좋다. 그렇지 않으면 “No rule to make target ‘debian/canonical-certs.pem’, needed by ‘certs/x509_certificate_list’. Stop.” 오류가 발생하면서 빌드가 멈출 것이다.

#                                                                                                   
# Certificates for signature checking                                                               
#                                                                                                   
...                                                              
CONFIG_SYSTEM_TRUSTED_KEYS="" #COMMENT OUT "debian/canonical-certs.pem"                                              
...                                                            
CONFIG_SYSTEM_REVOCATION_KEYS="" #COMMENET OUT "debian/canonical-revoked-certs.pem"   

모든 설정이 완료되면 빌드를 실행한다.

fakeroot debian/rules binary -j$(nproc)

Build된 패키지 설치 및 확인

빌드가 완료되면 다음과 같이 4개의 deb file들이 source code의 상위 디렉토리에 생성되었는지를 확인하고 apt 명령어로 설치 후 system을 reboot한다.

ls ../*.deb
  ../linux-headers-6.1.1_6.1.1-2_amd64.deb
  ../linux-image-6.1.1-dbg_6.1.1-2_amd64.deb
  ../linux-image-6.1.1_6.1.1-2_amd64.deb
  ../linux-libc-dev_6.1.1-2_amd64.deb

sudo apt install ../*.deb
sudo reboot

BIOS로 진입해서 secure boot을 disable하고 GRUB에서 새로 설치한 kernel을 선택해서 부팅한 후 다음과 같이 확인할 수 있다.

uname -a
Linux <machine-name> 6.1.1 #2 SMP PREEMPT_DYNAMIC Thu Dec 29 13:15:23 KST 2022 x86_64 x86_64 x86_64 GNU/Linux