728x90

간혹 새로운 장비에서 Dockerfile 을 이용하여 이미지를 빌드해야 하는 경우가 생깁니다. 늘 사용하던 장비와 빌드하던 이미지라면 특별히 문제가 생기지 않지만 새로운 오픈소스 코드나 다른 사람이 만들어둔 Dockerfile을 생각 없이 새 장비에서 빌드하다보면 에러 메세지로 당황하기 마련입니다. 

오늘도 어김 없이 새 장비에서 Dockerfile을 이용한 이미지 빌드 실패건을 겪었는데요 미래의 저를 위해 간단하게 내용을 정리해 봅니다. 한줄 요약을 하자면 Docker의 버전이 낮아서 발생한 문제였습니다. 


새로운 오픈소스를 Docker 이미지로 빌드후 구동하기 위해 소스코드를 클론하고 make docker 명령으로 이미지 빌드를 시도했습니다. go로 개발된 프로메테우스 익스포터류의 코드였는데요 잠시 빌드가 진행되는 듯 하더니 FROM으로 지정된 이미지의 포맷이 잘못되었다는 에러 메세지와 함께 빌드가 실패했습니다. 

$ make docker
docker build -t "akamai/akamai-gtm-metrics-exporter-linux-amd64:master" \
        -f ./Dockerfile \
        --build-arg ARCH="amd64" \
        --build-arg OS="linux" \
        ./
Sending build context to Docker daemon 15.25 MB
Step 1/8 : FROM docker.io/golang:1.16.6 AS builder
Error parsing reference: "docker.io/golang:1.16.6 AS builder" is not a valid repository/tag: invalid reference format
make: *** [common-docker-amd64] Error 1

 

도커 이미지가 없어서 그런가 생각해봤지만 docker image 명령을 쳐보면 이미지가 정상적으로 로컬 환경에 저장되어 있는 것이 확인되었습니다. 이미지의 빌드 넘버가 없어서 그런가하여 도커 허브에 들어가 golang 태그를 찾아봐도 분명 존재하는 태그였습니다.

$ docker images
REPOSITORY                                   TAG                 IMAGE ID            CREATED             SIZE
docker.io/golang                             1.16.6              028d102f774a        3 days ago          862 MB
docker.io/victoriametrics/victoria-metrics   latest              27ae96c3f0f7        2 weeks ago         23.5 MB
docker.io/prom/prometheus                    latest              9dfc442be98c        3 weeks ago         189 MB
docker.io/victoriametrics/vmagent            latest              4d8a8d15c6ec        4 weeks ago         20.4 MB
docker.io/prom/node-exporter                 latest              c19ae228f069        4 months ago        26 MB

 

문제는 도커의 버전이었습니다. 빌드하려던 Dockerfile의 내용을 꼼꼼히 살펴보지 않아 Dockerfile이 멀티 스테이지 빌드가 필요하다는 것을 미처 확인하지 못했던 것이죠. 멀티 스테이지 빌드는 경량 도커 이미지를 만들기 위한 방법으로 제공되는 빌드 방식이고 도커 17버전 이상에서 지원되는 것으로 확인됩니다. 도커를 새로운 버전으로 업데이트 하고 다시 빌드를 시도해 보았습니다.

 

 

Use multi-stage builds

 

docs.docker.com

 

// 설치된 버전 확인
$ docker --version
Docker version 1.13.1, build 7d71120/1.13.1

// 설치된 docker 삭제
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
                  
// Community Edition 설치를 위한 repo 정보 추가
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

// 최신 버전의 docker 설치
$ sudo yum install docker-ce docker-ce-cli containerd.io

// docker daemon 구동
$ sudo systemctl start docker

// 설치된 버전 확인
$ docker --version
Docker version 20.10.7, build f0df350

// 이제 빌드가 잘 되네요!
$ make docker
docker build -t "akamai/akamai-gtm-metrics-exporter-linux-amd64:master" \
        -f ./Dockerfile \
        --build-arg ARCH="amd64" \
        --build-arg OS="linux" \
        ./
Sending build context to Docker daemon  15.25MB
Step 1/8 : FROM docker.io/golang:1.16.6 AS builder
1.16.6: Pulling from library/golang
Digest: sha256:4544ae57fc735d7e415603d194d9fb09589b8ad7acd4d66e928eabfb1ed85ff1
Status: Downloaded newer image for golang:1.16.6
 ---> 028d102f774a
Step 2/8 : WORKDIR /go/src/github.com/akamai/akamai-gtm-metrics-exporter
 ---> Running in dab4aaede8da
Removing intermediate container dab4aaede8da
...
..
...
Successfully built a005e3513fae
Successfully tagged akamai/akamai-gtm-metrics-exporter-linux-amd64:master

 

참 쉽죠!?

 

728x90
728x90

아마 대부분의 경우 리눅스 환경에 접근시 루트 권한이 아닌 계정을 쓰고 있을 겁니다. 이 환경에서 docker 명령을 쓰다보면 pull 등 기본적인 명령 수행시에도 permission denied 를 만나게 되어 sudo 를 남발해야 하는 경우가 종종 발생합니다. 조금더 쉽게 docker를 사용하고 작업을 수행하기 위해 간단한 사용자, 그룹 변경으로 문제를 회피하는 방법을 정리해 봅니다. 


제가 만났던 permission denied 는 docker pull #이미지# 를 수행하는 과정에 발생했습니다. 발생한 위치는 나름 정확하게 나와서 /var/run/docker.sock 파일에 대한 접근을 하는 과정에서 권한 문제가 발생한 것으로 확인됩니다.

$ docker pull victoriametrics/victoria-metrics
Using default tag: latest
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.26/images/create?fromImage=victoriametrics%2Fvictoria-metrics&tag=latest: dial unix /var/run/docker.sock: connect: permission denied

 

이 권한 문제는 /var/run/docker.sock 파일의 접근 권한이 root 사용자에게 한정되어 있기 때문에 발생합니다. 따라서 접근 권한을 낮추거나 실사용자가 포함된 그룹을 만들어 파일의 소유권을 변경하는 것이 가장 깔끔한 방법입니다. 

# root 사용자/그룹에게 접근 권한이 있습니다
$ ls -al /var/run/docker.sock
srw-rw---- 1 root root 0 Jul  8 16:35 /var/run/docker.sock

# docker 그룹을 새로 만들어 사용자($USER)를 docker 그룹에 추가후 파일의 권한을 변경합니다.
$ sudo /usr/sbin/groupadd -f docker
$ sudo /usr/sbin/usermod -aG docker $USER
$ sudo chown root:docker /var/run/docker.sock

# 변경후 권한은 이렇게 바뀌었습니다
$ ls -al /var/run/docker.sock
srw-rw---- 1 root docker 0 Jul  8 16:35 /var/run/docker.sock

 

이제 별 문제 없이 pull 할 수 있게 되었습니다. (만세)

$ docker pull victoriametrics/victoria-metrics
Using default tag: latest
Trying to pull repository docker.io/victoriametrics/victoria-metrics ...
latest: Pulling from docker.io/victoriametrics/victoria-metrics
5843afab3874: Pull complete
576d2fdb56fe: Pull complete
a4c69497911f: Pull complete
Digest: sha256:dedd22e46e72518f7944355dd6b236231948c6a24d60f7ca201a5c007013da64
Status: Downloaded newer image for docker.io/victoriametrics/victoria-metrics:latest

 

참고자료

 

Docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

I am new to docker. I just tried to use docker in my local machine(Ubuntu 16.04) with Jenkins. I configured a new job with below pipeline script. node { stage('Build') { docker.image('...

stackoverflow.com

 

728x90
728x90

CentOS7 환경인지 먼저 점검합니다.

$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

 

운영체제의 패키지들을 최신 버전으로 업데이트 합니다. (선택사항)

$ sudo yum update

 

docker와 docker-registry 패키지를 설치합니다

$ sudo yum -y install docker docker-registry

 

설치된 docker를 재부팅시에도 자동 실행하도록 합니다 (선택사항)

$ sudo systemctl enable docker.service
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

 

docker 서비스를 실행하고 상태를 확인해 봅니다

$ sudo systemctl start docker.service
$ systemctl status docker.service
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2021-07-08 15:55:35 JST; 24s ago
     Docs: http://docs.docker.com
     ...

 

docker-registry에 등록된 이미지를 가져와서 실행해 봅니다

$ sudo docker pull centos
$ docker run -it centos /bin/bash

 

728x90
728x90

파이썬 장고가 제공하는 단위 테스트는 코드 배포전에 에러를 검출해 볼 수 있는 방법으로 무척 유용합니다. 젠킨스 빌드시에도 활용하여 자칫 로컬 환경과 원격 빌드/운영 환경의 차이에도 대응할 수 있어 장애를 막는 지름길이 되기도 합니다.

파이썬 장고의 단위 테스트를 구성해서 잘 사용하던 중, 갑자기 (원인은 알 수 없습니다 ㅜㅜ) TransactionManagementError와 함께 테스트가 지속적으로 실패하기 시작하여 열심히 구글링하고 삽질한 내용을 간략히 정리해 봅니다. 


장고 단위 테스트를 사용하기 위해서는 아래와 같은 코드를 만들게 됩니다. 참고로 코드는 장고 공식 문서에서 가져왔습니다. 이때, 실제 테스트를 수행하는 클래스는 매개변수로 TestCase 클래스의 인스턴스를 받게 됩니다. 아래의 코드에서는 TestCase 클래스를 가져왔네요.

import unittest
from django.test import Client

class SimpleTest(unittest.TestCase):
    def setUp(self):
        # Every test needs a client.
        self.client = Client()

    def test_details(self):
        # Issue a GET request.
        response = self.client.get('/customer/details/')

        # Check that the response is 200 OK.
        self.assertEqual(response.status_code, 200)

        # Check that the rendered context contains 5 customers.
        self.assertEqual(len(response.context['customers']), 5)

 

코드를 보면 실제 테스트 함수들이 클래스 내에 기술이 되어 있습니다. 제 경우 문제는 테스트 함수들이 데이터베이스에 대한 질의를 수행하는 코드를 호출하면서 발생했습니다. 일반적인 TestCase의 서브 클래스로는 데이터베이스 관련 코드를 수행하고 테스트 결과를 돌려주는데 문제가 있었습니다. 테스트가 수행되면서 발생한 에러는 아래와 같습니다.

raise TransactionManagementError(
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

 

이 에러 메세지가 참 얄미운게 내용이 좀 뭉뚱그려져 있습니다. 굉장히 명확한 에러이기 때문에 TestCase 클래스를 트랜잭션을 지원하는 것으로 변경하라고 하면 되었을 것을 저렇게 표현하고 있어서 애를 먹게 만들더군요. 이 문제에 대한 첫번째 접근은 장고 공식문서에서 가이드하는 TransactionTestCase 클래스의 사용입니다. 

https://docs.djangoproject.com/en/3.2/topics/testing/tools/#django.test.TransactionTestCase

 

제 경우는 장고 REST Framework 를 사용하고 있던 관계로 이쪽에서 필요한 클래스의 변경 작업을 진행했습니다. 기존에 에러가 발생하던 테스트 코드에서는 REST Framework가 제공하는 test 패키지의 APITestCase 클래스를 쓰고 있었습니다. 이 클래스를 APITransactionTestCase로 변경하니 에러가 말끔히 사라지고 테스트도 정상적으로 수행되기 시작했습니다.

# 기존
from rest_framework.test import APITestCase

# 변경
from rest_framework.test import APITransactionTestCase

 


TransactionManagementError로 검색을 하다보면 여러가지 문제 상황과 이에 대한 대처법들이 소개되고 있습니다. 가령 1) 테스트 테이블에 데이터를 피딩하는 과정에 문제였고 이를 수정하여 해결 했다는 분도 계셨고, 2) 테스트가 수행하는 코드 내에 try~except 구문이 사용되는 경우 transaction.atomic() 을 이용해서 코드를 변경해야 된다는 글도 있었습니다.

다양한 트러블슈팅 사례와 방법이 있는 것을 보면 에러 메세지가 조금 더 친절하게 출력되면서 원인을 빠르게 확인할 수 있었으면 어땠을까 하는 생각이 듭니다. 그래도 트러블슈팅을 하면서 또 파이썬에 대해, 장고에 대해 조금 더 알 수 있었기에 의미 없는 시간은 아니었던 것 같습니다!

파이썬과 관련하여 최근에 겪었던 다른 이슈들과 해결 방법은 아래의 글을 참고해 보시기 바랍니다!

 

M1 Apple silicon 맥북에서 파이썬 cryptography 패키지 설치가 안되는 문제 / python cryptography package installa

M1 silicon 맥북에서는 안되는게 참 많습니다. 새로운 CPU 아키텍쳐라서 여기저기서 패키지들이 오동작하거나 설치가 안되는 문제들이 많이 발생합니다. 오늘의 주인공인 파이썬 cryptography 패키지

ondemand.tistory.com

 

 

애플실리콘 M1 맥북에서 파이썬 magic 패키지가 로딩되지 않는 현상

애플실리콘 기반의 맥북이 등장하면서 세상이 참 행복해졌습니다. 베터리도 오래가고 이륙하지도 않고... 이렇게 쾌적한 맥 운영체제는 간만에 느껴보는 즐거움이었습니다. 하지만 의외의 장소

ondemand.tistory.com

 

 

PIP를 통한 mysqlclient 라이브러리 설치 에러 (mysql_config not found)

새로운 개발 장비가 생기면 이전에 사용하던 장비의 환경과 동일하게 준비하는 것이 은근 번거롭습니다. 한동안 손대지 않고 있던 파이썬 Python 장고 Django 프레임웍으로 개발된 코드를 새 장비

ondemand.tistory.com

 

728x90
728x90

M1 silicon 맥북에서는 안되는게 참 많습니다. 새로운 CPU 아키텍쳐라서 여기저기서 패키지들이 오동작하거나 설치가 안되는 문제들이 많이 발생합니다. 오늘의 주인공인 파이썬 cryptography 패키지도 마찬가지입니다. 그나마 이 녀석은 에러가 나름 명확한 친구임에도 문제를 해결하기 위해 적지 않은 시간을 쏟아야 했습니다.

우선 에러 메세지 보고 가시겠습니다. :-) 이 에러 메세지는 pip install -r requirements.txt 를 수행하는 동안 발생헀고 requirements.txt 에는 cryptography가 기술되어 있었습니다. (드라마 24시에서 Following takes place... 하는 느낌이네요)

Building wheels for collected packages: cryptography
  Building wheel for cryptography (PEP 517) ... error
  ERROR: Command errored out with exit status 1:
...
...
 ...
    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 -Werror=implicit-function-declaration -I/Users/nopd/project/venv/include -I/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/include/python3.8 -c build/temp.macosx-10.14.6-arm64-3.8/_openssl.c -o build/temp.macosx-10.14.6-arm64-3.8/build/temp.macosx-10.14.6-arm64-3.8/_openssl.o -Wconversion -Wno-error=sign-conversion
  build/temp.macosx-10.14.6-arm64-3.8/_openssl.c:575:10: fatal error: 'openssl/opensslv.h' file not found
  #include <openssl/opensslv.h>
           ^~~~~~~~~~~~~~~~~~~~
  1 error generated.
  
      =============================DEBUG ASSISTANCE=============================
      If you are seeing a compilation error please try the following steps to
      successfully install cryptography:
      1) Upgrade to the latest pip and try again. This will fix errors for most
         users. See: https://pip.pypa.io/en/stable/installing/#upgrading-pip
      2) Read https://cryptography.io/en/latest/installation.html for specific
         instructions for your platform.
      3) Check our frequently asked questions for more information:
         https://cryptography.io/en/latest/faq.html
      =============================DEBUG ASSISTANCE=============================
  
  error: command 'clang' failed with exit status 1
  ----------------------------------------
  ERROR: Failed building wheel for cryptography
Failed to build cryptography
ERROR: Could not build wheels for cryptography which use PEP 517 and cannot be installed directly

 

화려한 에러 메세지이지만 결론은 설치가 잘 안되었다 입니다. DEBUG ASSISTANCE의 내용은 평이합니다. PIP가 구버전이라 그럴 수 있으니 업데이터 해보고 cryptography 설치 가이드 문서를 읽어 본 다음, faq에 단서가 있을지도 모르니 찾아보라는 내용입니다. TL;DR 하자면 아래의 한줄이 모든 것을 이야기 해주고 있었습니다.

build/temp.macosx-10.14.6-arm64-3.8/_openssl.c:575:10: fatal error: 'openssl/opensslv.h' file not found

 

Trouble Shoot #1 - Install separate openssl with brew

구글 검색을 통해 살펴봤던 문서를 종합해보면 M1 silicon 기종에 선탑재되어 나오는 openssl이 cryptography 빌드에 필요한 버전과 상이하다고 합니다. 실제로 M1 silicon 탑재 기종에서 openssl version 명령으로 버전을 확인해보면 LibreSSL 이 설치되어 있는 것을 알 수 있습니다. 아래와 같이 brew 를 이용해 openssl 을 별도로 설치해 보도록 하겠습니다. 공식 가이드에서 안내하는 1.1 버전을 지정해서 설치 진행했습니다. 

cryptography 설치 가이드의 안내  (https://cryptography.io/en/latest/installation/)

% openssl version
LibreSSL 2.8.3
% brew install openssl@1.1 rust
Updating Homebrew...

 

Trouble Shoot #2 - Uninstall cffi then install cryptography again

별도의 openssl 설치와 혹시 몰라서 rust(cryptography는 rust를 사용합니다)를 설치하는 작업이 끝나면 이제 파이썬 패키지중에서 C언어를 호출할 때 사용되는 cffi 도 삭제후에 다시 설치해 주도록 하겠습니다. 아래의 명령을 순서대로 터미널에서 입력하면 cryptography 가 설치됩니다. 제 경우 cryptography를 3.3.1 버전으로 지정해서 사용할 일이 있어서 버전을 지정했습니다만 그렇지 않은 경우에는 버전 정보를 빼고 설치하면 되겠습니다. 

% pip uninstall cffi
% LDFLAGS=-L$(brew --prefix libffi)/lib CFLAGS=-I$(brew --prefix libffi)/include pip install cffi --no-binary :all:
% LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" CFLAGS="-I$(brew --prefix openssl@1.1)/include" pip install cryptography==3.3.1

 

문제가 해결되었습니다. 비슷한 이슈를 겪는 분들은 정리된 내용대로 패키지 삭제, 재설치를 진행해 보시기 바랍니다. 영어로 된 자료도 흥미롭게 읽을 수 있다면 보다 깊은 내용과 관련 링크가 공유된 stackoverflow 의 아래 스레드를 읽어 보시면 도움이 되겠습니다!

 

Installing Cryptography on an Apple Silicon M1 Mac

Help! I'm trying to install cryptography on my m1. I know I can run terminal in rosetta mode, but I'm wondering if there is a way not to do that. Output: ERROR: Command errored out with exit st...

stackoverflow.com

 

Hope this helpful to someone who experiencing similar issue.

728x90
728x90

JSON을 다루는 것은 개발자에게는 숙명입니다. 그래도 SOAP 보다 편리하고 쉽다는게 어디냐며 위로해 보지만 할 때마다 새롭고 매번 처음 보는 것 같이 헤메는 것이 또한 JSON 다루기의 특징이기도 합니다.

터미널을 위시한 커맨드라인에서 jq를 이용하면 이런 번잡스러운 일을 간단하게 줄일 수 있습니다. 이미 많은 분들이 쓰고 있고 저 역시 쓰고 있지만 JSON을 다루는 만큼 매번 새롭기에... 하나씩 활용 방법을 찾아서 정리해보고자 합니다.


JSON은 단순히 어떤 요청에 대한 결과를 하나의 데이터 셋으로 내려주기도 하지만, 데이터 셋안에 여러개의 반복되는 데이터가 포함되어 있는 경우도 많습니다. 반복되는 JSON에서 원하는 속성의 값만을 뽑아내는 방법을 살펴보겠습니다. 

오늘의 데이터는 주택금융공사의
전세자금대출 고객 금리정보입니다.

 

시작부터 엄청 구미가 당기는 데이터이지 않습니까? 국가 공공데이터포털의 오픈API중에서 가장 먼저 눈에 띈 녀석으로 가져와 봤습니다. 여전히 SOAP만 제공하는 API도 많지만 많이 사용되는 데이터 셋은 JSON을 제공하는 경우가 많아 서비스를 개발하거나 연습할 때 무척 유용합니다. 

 

jq의 시작, jq '.' 사용하기

API의 자세한 스펙도 살펴보면 좋겠지만 우리의 목적은 흥미로운 JSON을 jq로 다뤄보는 연습을 하는 것이니 규격에 대한 설명은 생략하도록 하겠습니다. 요지는 JSON으로 동일한 속성을 가진 여러벌의 JSON 데이터가 나온다는 점입니다. 2021년 7월의 전세자금대출 정보를 쿼리해보니 은행별로 최저, 최대 금리가 나오고 대출 횟수가 같이 나옵니다. 

curl로 기본적인 GET 요청을 던졌고 돌아오는 응답을 파이프로 연결하여 jq '.' 로 넘겨보았습니다. 이렇게 하는 것 만으로 아래와 같이 두가지 극단적인 결과를 볼 수 있습니다. jq를 써보신 분들은 다 아시고, jq를 처음 쓴다면 일단 외워두는 것이 jq '.' 입니다. 

아.. 머리가 아프다...
단지 jq '.' 를 파이프로 연결했을 뿐인데...

 

// 머리아픈 JSON보기
curl -v "http://apis.data.go.kr/B551408/rent-loan-rate-multi-dimensional-info/dimensional-list?serviceKey=##공공데이터포털에서_키를받아_넣으세요!##&loanYm=202105&cbGrd=1&debt=11&numOfRows=5&pageNo=1&dataType=json"

// 속시원한 JSON보기
curl -v "http://apis.data.go.kr/B551408/rent-loan-rate-multi-dimensional-info/dimensional-list?serviceKey=##공공데이터포털에서_키를받아_넣으세요!##&loanYm=202105&cbGrd=1&debt=11&numOfRows=5&pageNo=1&dataType=json" | jq '.'

 

특정 아이템만 뽑아내보자

jq '.'를 사용해서 사람이 읽기 좋은 포맷을 쉽게 만들어 보았습니다. 하지만 데이터가 많다면 이 데이터를 한 번 더 필터링 해서 원하는 정보만 발라내서 보고 싶어지기 마련입니다. 

JSON의 구조를 잘 보니 최상위 속성으로 "header"와 "body"가 눈에 띕니다. "header"는 API 호출에 대한 처리 결과를 담고 있으니 우리에겐 중요하지 않습니다. 우리는 두번째 속성인 "body"의 내용에 관심이 있습니다. "body"의 하위 JSON만 뽑아내려면 어떻게 해야 할까요?

jq '.body'

// jq '.body' 로 파이프를 거세요!
curl -v "http://apis.data.go.kr/B551408/rent-loan-rate-multi-dimensional-info/dimensional-list?serviceKey=##공공데이터포털에서_키를받아_넣으세요!##&loanYm=202105&cbGrd=1&debt=11&numOfRows=5&pageNo=1&dataType=json" | jq '.body'

앞서 사용했던 jq 명령을 조금 더 진화시켜서 jq '.body'를 했더니 특정한 속성 하위의 JSON만 출력할 수 있었습니다. 참 쉽죠? 이쯤되면 조금 더 욕심이 나실겁니다. 잘 보니 "items"라는 배열 하위에 찐 정보들이 가득합니다. 과감하게 jq '.body.items'를 하면 원하는 값이 나오겠죠?

jq '.body.items'

 

반복되는 JSON에서 특정 속성만 뽑아내기

자 그런데 여전히 뭔가 번잡해 보입니다. 금융 서비스나 핀테크 서비스를 만든다면 사용자들에게 특정 은행의 전세자금대출 상품 소개를 하면서 최저금리를 안내해서 클릭을 유도하고 싶을 수 있습니다. 그렇다면 최소 금리를 나타내는 항목인 "minLoanRat"만 뽑아서 보면 좋을 것 같다는 생각이 듭니다. jq '.body.items.minLoanRat'을 하면 될 것 같죠?

jq '.body.items.minLoanRa' 은 에러입니다!!

하지만 결과는 제대로 나오지 않고 만나고 싶지 않았던 에러 메세지를 맞딱드렸습니다. 뭐가 문제일까요? 그것은 바로 앞선 jq '.body.items'의 결과가 배열이기 때문입니다. 배열은 인덱스라는 순서가 존재합니다. 이를 나타내기 위해서는 []를 써야 합니다. jq '.body.items[].minLoanRat'으로 명령을 바꿔서 시도해보겠습니다!

jq '.body.items[].minLoanRat'

결과가 잘 나왔습니다! 하지만 뭔가 아쉽습니다. 도대체 어느 은행에서 이 금액으로 대출을 해준건지 도통 알수가 없는 상태이기 때문이죠. 은행의 이름도 분명 원래의 JSON 데이터에 있었는데... 이걸 jq 로 함께 뽑아서 < "은행": #최소대출금리# >의 형태로 볼 수 있다면 얼마나 좋을까요?

 

반복되는 JSON을 조작하여 새로운 JSON 만들어내기

jq는 여러분을 위해 이미 그렇게 할 수 있는 방법을 준비해 두었습니다. jq는 curl과 같은 다른 명령으로부터 JSON 데이터를 파이프(|)로 전달 받을 수 있는 것은 물론이고, 자신이 스스로 데이터를 몇 번씩 가공하여 파이프로 연결해서 가공할 수 있습니다. 우리가 원하는 결과를 만들기 위해서는 아래와 같은 jq 연산을 해볼 수 있습니다.

jq '.body.items[] | { bankNm, avgLoanRat }'

jq '.body.items[] { bankNm, avgLoanRat }'

드디어 완성이 된 것 같습니다. 하지만 약간 더 손을 보면 다른 어플리케이션에서 데이터를 다루기 더 쉬워질 수 있습니다. 위 그림에서의 JSON은 JSON의 규격 위반으로 다른 프로그램에서 JSON으로 파싱할때 에러가 발생합니다. 각 항목이 콤마로 연결되어야 하고 배열이기 때문에 []로 묶일 필요가 있습니다. 

jq '[ .body.items[] | { bankNm, avgLoanRat } ]'

전체를 []로 묶어주니 자동으로 각 항목과 항목 사이를 콤마로 연결해 주어 완성된 JSON의 형태를 만들어 주었습니다. 이렇게 만들어진 JSON이 정말 문제 없는지 jsonlint.com 에서 검증을 해보았습니다. 네, 역시 문제 없네요!

 


이번 포스팅에서는 jq 를 이용하여 간단하게 데이터를 조작하는 방법을 살펴보았습니다. 다음 포스팅에서는 조건문을 활용하여 jq 를 보다 어렵게(?) 사용하는 방법을 살펴보도록 하겠습니다.

728x90
728x90

vscode 환경의 장점은 수많은 플러그인을 통해 개발 생산성을 극도로 향상시킬 수 있다는 점입니다. 특히 각 언어 환경에서 제공되는 플러그인의 통합이 잘되는 편이라 단일 IDE 환경에서 다양한 플러그인의 혜택을 누릴 수 있습니다.

Python으로 코드를 만들면서 구문의 오류나 스타일 문제를 잡아내기 위해 널리 사용되는 플러그인이 pylint 입니다. vscode가 아닌 파이썬 환경에서 사용할 수 있는 linter 중 하나로 넓은 사용자층을 보유하고 있습니다.

그런데 이 플러그인이 vscode에서 궁합이 잘 맞지 않을 때가 있습니다. 기존에 개발 장비에서 없던 문제가 새 개발 장비에서 하나, 둘 계속 나오니 참 환장할 노릇입니다 ^^;; 오늘은 그 중에서 vscode 환경에서 pylint가 내뿜는 no-member 에러를 없애는 방법을 살펴보겠습니다. 


 

pylint의 no-member 오류란?

pylint는 linter 도구로 코드를 분석하여 구문의 오류를 찾아내는 역할을 합니다. 구문의 오류 범주에는 사용자가 직접 만든 클래스나 함수도 당연히 포함됩니다. 제가 겪었던 문제는 Django 환경에서 클래스의 멤버 객체를 찾지 못하는 문제였습니다. 

vscode는 pylint의 도움을 받아 아래와 같이 특정한 객체에 대하여 정의되지 않은 멤버를 호출하는 코드에 빨간 물결줄을 그려주면서 존재하지 않는 멤버라는 것을 알려줍니다. 문제는 존재하지 않는 멤버가 아니라는 것입니다 ^^;;

 

빌드는 잘되지만 에러라고 pylint가 자꾸 심기를 건드립니다.

 

이 문제가 더 당혹스러운 이유는 빌드에는 문제가 없기 때문입니다. 실제로 구문에 문제가 없지만 pylint가 제대로 사용자 정의 객체를 탐색하지 못하면서 발생하는 이슈라 보면 정확합니다. 빌드에 문제가 없지만 vscode IDE 상에서는 문제가 있는 구문인 것처럼 소스코드 파일을 열때마다 시뻘건 줄과 경고 문구를 쏟아내는데 스트레스가 꽤 큽니다. 

 

해결방법 : vscode 공통 환경 설정에서 pylint 옵션 지정하기

이 문제를 해결하기 위해서는 vscode의 공통 환경 설정 settings.json 파일에서 pylint에 대한 옵션을 추가로 지정해 주어야 합니다. 기본적으로는 settings.json 파일에 pylint와 관련한 항목은 설정되어 있지 않습니다.

우리가 추가해야 할 내용은 아래와 같습니다. 이미지에는 오타가 있기 때문에 (--generated-members가 아니라 --generate-members입니다) 아래의 구문을 복사해서 settings.json 파일에 추가하는 것을 권장드립니다. 

  1. Mac에서는 Shift+Command+P를, Windows에서는 Ctrl+Shift+P를 눌러 Command Palette를 엽니다
  2. Command Palette에서 "Preferences: Open Settings (JSON)"을 입력하거나 찾아서 선택합니다
  3. Setting.json 파일에 아래의 항목을 추가합니다.
  4. vscode를 재기동하여 프로젝트를 열어 확인합니다.
"python.linting.pylintArgs": ["--generate-members", "from_json,query"],

 

 


 

vscode를 사용하지 않는 환경에서는 pip를 이용해 pylint-django 플러그인을 설치하면 에러를 해결할 수 있다는 글도 검색하면서 봤던 것 같습니다. vscode가 아닌 환경에서 문제를 겪고 있다면 pylint-django 공식 가이드 문서에 따라 패키지 설치후 django 환경 변수를 지정해 보시기 바랍니다!

 

pylint-django

A Pylint plugin to help Pylint understand the Django web framework

pypi.org

 

그외에 vscode 환경에서 python 개발을 하면서 겪을 수 있는 다른 문제들에 대해서는 아래의 글들을 참고하시면 도움이 되실지도 모르겠습니다. 변수가 많은 vscode 환경에서의 개발에서 꼭 승리하시길 기원하겠습니다!

 

Visual Studio Code 에서 Python 패키지의 Unresolved Import Error 를 처리하는 방법

코드를 만들 필요가 있을때 왠만하면 Node.js 를 이용하는 편입니다. 아무래도 익숙하기도 하거니와 자유로운 자바스크립트의 DNA 가 살아 있기 때문에 "대략 이렇게 돌아갈까?" 하는 것들이 동작

ondemand.tistory.com

 

728x90
728x90

애플실리콘 기반의 맥북이 등장하면서 세상이 참 행복해졌습니다. 베터리도 오래가고 이륙하지도 않고... 이렇게 쾌적한 맥 운영체제는 간만에 느껴보는 즐거움이었습니다. 하지만 의외의 장소에서 복병을 만나게 될줄은 몰랐고 비슷한 짜증을 경험할 수 있는 누군가(아마도 미래의 나...)를 위해 메모를 남겨 둡니다. 뜬금 없이 왜 애플실리콘 이야기인가...는 글을 읽으시면서 알 수 있게 됩니다. 


맥 환경에서 운영체제 패키지 관리를 위해 Homebrew를 보통 사용하게 됩니다. python의 유명한 라이브러리 중 하나인 magic을 사용하기 위해서는 운영체제 환경에 libmagic 패키지를 설치해 둘 필요가 있습니다. 

  1. virtualenv 로 가상 환경 구성 및 activate
  2. pip install 로 requirements.txt의 항목 설치

를 하고 디버깅을 시작하는데 import magic 에서 에러가 발생했습니다. 가상 환경에서 magic 패키지가 잘 설치되어 있고 brew 로 libmagic 설치를 확인해봐도 아무런 문제가 없는데 python이 계속 libmagic이 설치되었는지 확인하라고 짜증을 내더군요. 네, 저도 짜증이 나기 시작했습니다. 

% python3
Python 3.8.2 (default, Apr  8 2021, 23:19:18) 
[Clang 12.0.5 (clang-1205.0.22.9)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import magic
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "......./venv/lib/python3.8/site-packages/magic.py", line 176, in <module>
    raise ImportError('failed to find libmagic.  Check your installation')
ImportError: failed to find libmagic.  Check your installation

 

구글과 함께 문제의 원인과 해결방법을 찾아보면 대부분 패키지가 잘 설치되었는지 확인해봐라, brew 로 libmagic 설치하면 된다 류의 답변이 대부분입니다. 하지만 그 어떤 방법도 동작하지 않았습니다. 왜냐하면 python 패키지든 OS 패키지든 필요한 건 문제없이 다 설치되어 있었기 때문입니다.

두가지 방향으로 다시 조사를 시작했습니다. 범인은 brew와 pip 둘 중 하나일 것이라고 보고 brew에서 설치하는 libmagic과 pip를 통해 설치하는 magic(패키지명은 이것도 libmagic입니다)에 무슨 문제가 있는가... 하는 접근이었습니다. 먼저 발견한 흥미로운 단서는 brew에서 나왔습니다.

https://docs.brew.sh/FAQ

애플실리콘이 등장하면서 homebrew가 사용하는 기본 prefix 경로가 바뀌었다는 이야기가 FAQ에 등재되어 있었습니다. 기본 경로가 달라졌다는 것은 brew를 통해서 설치되는 패키지들도 경로가 달라졌다는 말이 됩니다. 그래 뭐 바뀌었겠지... 하다가 문득, python libmagic 패키지가 절대경로로 라이브러리를 참조하나? 싶은 생각이 들었고 python libmagic의 github 저장소를 들러보았습니다.

 

ahupp/python-magic

A python wrapper for libmagic. Contribute to ahupp/python-magic development by creating an account on GitHub.

github.com

유레카!

python libmagic 패키지가 운영체제의 libmagic 라이브러리를 참조하는 코드는 하드 코딩된 몇 가지 절대 경로를 탐색하도록 만들어져 있었습니다. 그 경로중 하나는 바로 brew가 인텔과 애플실리콘 환경에서 변화가 생긴 바로 그 경로였습니다. 오우... 코드에 기여할 수 있는 것인지 혹은 이미 이슈가 올라왔는지...는 조금 후에 생각하기로 하고 libmagic의 설치 경로를 확인해보고 symlink를 만들어 추정이 맞는지 시험해 보기로 했습니다.

(venv) % brew info libmagic
libmagic: stable 5.40 (bottled)
Implementation of the file(1) command
https://www.darwinsys.com/file/
/opt/homebrew/Cellar/libmagic/5.40 (339 files, 8.4MB) *
  Poured from bottle on 2021-05-18 at 15:25:48
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/libmagic.rb
License: Cannot Represent
==> Analytics
install: 13,403 (30 days), 39,666 (90 days), 127,530 (365 days)
install-on-request: 3,672 (30 days), 11,365 (90 days), 34,209 (365 days)
build-error: 0 (30 days)

 

우려했던, 그리고 Homebrew FAQ에서 알려줬던 것처럼 운영체제 패키지는 /opt/homebrew/Cellar/libmagic/{버전} 경로에 설치가 되어 있었습니다. 해당 경로에 진입하여 libmagic.dylib 의 정확한 경로를 확인하고 /usr/local/lib 로 이동해서 symlink 를 만들었습니다. 

% pwd        
/usr/local/lib
% ln -s /opt/homebrew/Cellar/libmagic/5.40/lib/libmagic.dylib libmagic.dylib
%

 

symlink 까지 만들어주고 다시 코드를 실행하니 import magic 구문에서 문제가 더 이상 발생하지 않았습니다. 보통 python 에서 패키지 문제가 생기면 1) 가상 환경에 설치가 되었는지 보고, 2) 버전을 타는 문제가 있나 확인하는 정도로 문제가 해결되었는데 이번 케이스는 좀 그렇지 않아 시간을 은근히 쓸 수 밖에 없었네요. 금방 한것처럼 보이지만 구글링을 얼마나 했던지... 


 

728x90

+ Recent posts