Skip to main content

4 posts tagged with "devops"

View All Tags

· 6 min read

error: externally-managed-environment

Homebrew를 사용하여 설치한 Python 3.12를 새로 사용하게 되었다. Homebrew는 $(brew --prefix)/lib/pythonX.Y/site-packages 경로에 패키지를 설치하기 때문에, 기존에 사용하던 여러 패키지(module)들을 다시 설치해야 했다. 아무 생각없이 pip install sshtunnel로 패키지 설치를 시도하였지만, 아래와 같은 에러를 보게 되었다.

> pip3 install sshtunnel
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
xyz, where xyz is the package you are trying to
install.

If you wish to install a non-brew-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip.

If you wish to install a non-brew packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

웬 처음보는 에러가 나오는데, --break-system-packages라는, 딱 봐도 별로 쓰고싶지 않은 flag를 써서 문제를 우회할 수 있다는 내용이 있었다.

이를 적용하기에는 무언가 찜찜했기 때문에, 결국 hint에 언급되는 PEP 668에 대해 찾아보게 되었다.

PEP 668과 Homebrew Python@3.12

Python-specific 패키지 관리 도구들(pip 등)은 기본적으로 전역 환경에 패키지들을 설치해왔으나, 이는 distro-installed 패키지 관리 도구(brew 등)와 충돌을 유발하면서 여러 문제를 유발하였다. 이에 Python-specific 패키지 관리 도구는 전역 환경에 패키지를 설치/제거하는 것을 지양하고 가상 환경에만 패키지를 관리할 것을 권장하는 것이 PEP 688이 제안하는 내용이다.

내가 여태까지 안일하게 사용하던, 프로젝트별 가상환경 없이 pip로 $(brew --prefix)/lib/pythonX.Y/site-packages경로에 패키지를 설치하는 방식에 문제가 있었던 것은 사실이다. 패키지 이름, 버전 충돌 등 여러 예상치 못한 문제가 발생할 수 있기 때문에, 프로덕션 환경에 적합한 방식은 절대 아니다. 하지만 이 PEP는 2021년에 제안된 것으로, 갑자기 이제 와서 문제를 발생시키는 것이 의아했다. Python 3.11까지는 저런 에러가 발생한 적이 없었는데, 하필 3.12부터 이를 적용하게 된 걸까?

검색을 거듭하여, Homebrew Python 문서에서 답을 찾을 수 있었다.

Starting with Python@3.12, Homebrew follows PEP 668.

정말로 Homebrew는 3.12부터 PEP 668을 따르게 된 것이었다. 공식 문서에서 제안하는 Python 패키지를 설치하는 방식은 아래 2가지였다.

  • python 3 -m venv path/to/venv 로 가상환경을 생성하고 path/to/venv/bin/python, path/to/venv/bin/pip를 사용하는 방식
  • pipx install xyz와 같이 pipx를 사용하는 방식

venv야말로 정석적인 해결방식이겠지만, pipx는 또 뭘까?

pipx

pipx는 pip와 같이 Python 패키지를 설치/관리하는 도구이다. 하지만 pip가 환경에 구애받지 않고 라이브러리/애플리케이션을 설치하는 범용 패키지 관리자인 반면, pipx는 애플리케이션 단위의 설치와 실행에 초점을 맞추기 때문에 대상 애플리케이션과 그 연관 패키지들만을 위한 고립된 환경을 생성한다는 차이점이 있다. 즉 pipx는 이미 완성된 Python 애플리케이션을 설치하고 실행하는 도구이므로, 프로젝트별 개발 환경을 세팅하는 단계에서 venv의 대체재가 될 수 있는 도구는 아닌 것으로 보인다.

결론

Python3.12부터(Homebrew 기준) PEP 668을 도입함에 따라 --break-system-packages 플래그를 사용해야만 전역 환경에 패키지를 설치할 수 있도록 변경되었다. 그간 가상환경 구분 따위 없이 default 경로에(즉, 전역 환경에) 마구잡이로 패키지를 설치하여 사용해왔으나, 이를 계기로 프로젝트별 venv를 사용함으로써 보다 안정적인 Python 실행 환경을 구축하고자 한다.

References

PEP 688 관련

Homebrew 문서

pipx

· 5 min read

Amazon CodeGuru

Amazon CodeGuru는 코드를 넣으면 코드를 분석하고 결과를 출력하는 서비스이다. 특히 AWS API를 사용하는 코드를 분석할 때 뛰어난 성능을 보여준다고 한다. CodeGuru 서비스는 CodeGuru Reviewer와 Profiler로 나뉘는데, Reviewer는 안정성(보안 취약점 식별, Best practice 제안), Profiler는 퍼포먼스에 초점을 둔 서비스로 볼 수 있다. 내가 관심있는 기능은 Reviewer이기 때문에, 이번 포스트에서는 Reviewer에 한정하여 작성한다.

CodeGuru로는 Java와 Python 코드를 분석할 수 있으며, AWS CodeCommit, Bitbucket, GitHub, GitHub Enterprise Server의 리포지토리는 Reviewer와 연동하여 사용이 가능하다. 문제는 우리 팀은 리모트 리포지토리로 GitLab을 사용하고 있어서 리포지토리와 직접 연동이 어렵다는 점이다. 하지만 해결책은 있기 마련이어서, GitLab에서 제공하는 Repository mirroring 기능을 활용하면 GitLab 리포지토리와 AWS CodeCommit 리포지토리를 연동할 수 있고, AWS 블로그에서 CodeGuru Reviewer CLI를 사용하여 CI/CD Pipeline을 설정하는 방법도 찾을 수 있었다.

GitLab Repository Mirroring(AWS Codecommit)

AWS CodeCommit 리포지토리는 AWS 루트 계정에 종속된 자원이므로, 아래와 같이 IAM을 통한 권한 설정이 선행되어야 한다.

  1. IAM User에 AWSCodeCommitPowerUser 정책을 붙인다.
  2. 해당 User의 Security credentials에 가서 HTTPS Git credentials for AWS CodeCommit에서 Git credential을 생성한다.

이후 CodeCommit 리포지토리를 생성하고 그 URL을 복사한다. 주의할 점은 CodeGuru는 2022년 9월 현재 Seoul 리전에서 서비스되지 않기 때문에 CodeGuru가 목적일 경우 다른 리전에 생성하여야 한다.

CodeCommit 리포지토리의 URL과 CodeCommitPowerUser 정책이 붙은 IAM User의 Git credential 2가지 정보가 있다면 비로소 GitLab에서 Reporitory mirroring 기능을 설정할 수 있다.

GitLab 리포지토리의 Setting > Repository > Mirroring repositories에서 URL을 입력하는데, 앞서 언급한 2개의 정보를 조합하여야 한다. CodeCommmit 리포지토리 URL은 https://git-codecommit.REGION_NAME.amazonaws.com/v1/repos/REPOSITORY_NAME 형태인데, HTTP로 Git을 사용하기 위해 프로토콜 뒤에 Git Credential의 username을 추가하고 @를 추가해야 한다. 즉, 아래와 같은 형태로 URL을 입력한다 https://GIT_CREDENTIAL_USERNAME@git-codecommit.REGION_NAME.amazonaws.com/v1/repos/REPOSITORY_NAME 이후 아래 필드에 Password를 입력하고 저장하면 GitLab 리포지토리와 CodeCommit 리포지토리 간 Mirroring 관계가 성립된다.

CodeGuru Reviewer CLI

고민했지만 AWS 블로그의 자세한 설명 이상으로 좋은 설명을 쓸 자신은 없어서 링크로 대신하기로 했다.

https://aws.amazon.com/blogs/devops/automating-detection-of-security-vulnerabilities-and-bugs-in-ci-cd-pipelines-using-amazon-codeguru-reviewer-cli/

Jenkins 인스턴스에 CodeGuru Reviewer CLI(AWS CLI 아님)를 설치하고 권한을 설정하는 것이 주요 내용으로, CLI를 통해 지정한 위치에 output 파일이 생성되어 분석 결과를 확인하거나 결과값에 따라 파이프라인 분기를 설정하는 방식이다. 다만 CLI를 사용할경우 CodeGuru Console에서는 관련 내용을 확인할 수 없다는 단점이 있다.

출처

https://docs.aws.amazon.com/codeguru/latest/reviewer-ug/welcome.html https://docs.aws.amazon.com/codeguru/latest/profiler-ug/what-is-codeguru-profiler.html https://docs.gitlab.com/ee/user/project/repository/mirror/ https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-gc.html?icmpid=docs_acc_console_connect https://aws.amazon.com/blogs/devops/automating-detection-of-security-vulnerabilities-and-bugs-in-ci-cd-pipelines-using-amazon-codeguru-reviewer-cli/

· 13 min read

#74 errored

오랜만에 블로그에 포스트를 작성하고 블로그를 둘러보니, 이전에 작성한 불필요한 포스트가 보였다. 이를 로컬 디렉토리에서 삭제한 후, 블로그에 반영하기 위해 리포지토리 main 브랜치에 push하였다. 리포지토리는 Travis CI를 통해 빌드/배포 자동화가 설정되어 있으므로, 여느 때와 같이 빌드/배포 작업이 진행될거라 기대했지만, 빌드가 실패했다는 메일을 받게 되었다. 지난 11월에 블로그 프레임워크를 Hexo로 바꾸면서, Hexo 튜토리얼 문서를 보고 그대로 따라한 뒤(https://bastionsofwill.github.io/2021/11/17/hexo-travis-ghpage) 처음 겪는 빌드 실패여서 당혹스러웠으나, 몇 시간 전에 성공한 것이 실패한 이유는 당연히 안 해본 짓(포스트 삭제)을 했기 때문일 것으로 추정하고 해결을 미뤘다. 하지만 빌드 실패 결과를 살펴본 결과, 문제는 다른 곳에 있음을 알 수 있었다.

Job Log

Worker information
(생략)
Build system information
(생략)
$ git clone --depth=50 --branch=main https://github.com/bastionsofwill/bastionsofwill.github.io.git bastionsofwill/bastionsofwill.github.io

Setting environment variables from repository settings
$ export GH_TOKEN=[secure]

$ nvm install 16

Setting up build cache
$ export CASHER_DIR=${TRAVIS_HOME}/.casher
$ Installing caching utilities
attempting to download cache archive
fetching main/cache--linux-xenial-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--node-16.tgz
found cache
$ node --version
v16.15.1
$ npm --version
8.11.0
$ nvm --version
0.39.1
$ yarn --version
1.22.18

$ yarn --frozen-lockfile
hexo generate
(생략)
The command "hexo generate" exited with 0.

store build cache
changes detected (content changed, file is created, or file is deleted):\n/home/travis/.npm/_logs/2022-06-09T17_42_37_173Z-debug-0.log\n
changes detected, packing new archive
uploading main/cache--linux-xenial-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--node-16.tgz
cache uploaded

$ rvm $(travis_internal_ruby) --fuzzy do ruby -S gem install dpl
Installing deploy dependencies
ERROR: Error installing dpl-pages:
The last version of multipart-post (>= 1.2, < 3) to support your Ruby & RubyGems was 2.2.0. Try installing it with `gem install multipart-post -v 2.2.0` and then running the current command again
multipart-post requires Ruby version >= 2.6.0. The current ruby version is 2.4.5.335.
Successfully installed public_suffix-3.0.3
Successfully installed addressable-2.8.0
/home/travis/.rvm/rubies/ruby-2.4.5/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- dpl/provider/pages (LoadError)
from /home/travis/.rvm/rubies/ruby-2.4.5/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/provider.rb:93:in `rescue in block in new'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/provider.rb:68:in `block in new'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/cli.rb:41:in `fold'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/provider.rb:67:in `new'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/cli.rb:31:in `run'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/lib/dpl/cli.rb:7:in `run'
from /home/travis/.rvm/gems/ruby-2.4.5/gems/dpl-1.10.16/bin/dpl:5:in `<top (required)>'
from /home/travis/.rvm/gems/ruby-2.4.5/bin/dpl:23:in `load'
from /home/travis/.rvm/gems/ruby-2.4.5/bin/dpl:23:in `<main>'
failed to deploy

에러가 난 부분을 보면, deploy dependencies를 설치하다가 dpl-pages에서 에러가 발생했고, multipart-post가 Ruby 2.6.0을 요구하는데 현재 버전이 2.4.5.335인 것이 문제로 보인다.

errored?

먼저 Travis CI가 동작하는 과정에 대한 간략한 이해가 필요하다. Travis CI가 수행할 작업을 정의하는 구조는 phase -> job -> stage -> build로, 하나의 job은 아래와 같은 phase로 구성된다.

  1. before_install
  2. install: dependency의 설치
  3. before_script
  4. script: build script 실행
  5. before_cache(OPTIONAL)
  6. after_success/failure
  7. before_deploy(OPTIONAL)
  8. deploy(OPTIONAL)
  9. after_deploy(OPTIONAL)
  10. after_script

stage는 이러한 job들이 병렬로(in parellel) 실행되는 그룹을 말하며, build는 이러한 stage들이 순차적으로 실행되는 그룹을 말한다.

하나 이상의 job이 실패할 경우 그 job이 속한 빌드는 비정상(break)이 되며, 비정상 빌드는 아래 세 가지 키워드 중 하나로 표시된다.

  • errored: before_install, install, before_script, before_deploy phase가 정상적으로 종료되지 않을 경우(non-zero exit code) 발생
  • failed: script phase가 정상적으로 종료되지 않을 경우 발생
  • canceled: 사용자에 의한 취소 after_success/failure, after_deploy, after_script의 exit code는 빌드 결과에 영향을 미치지 않지만, 타임아웃이 발생했을 경우 빌드 결과는 failed로 표시된다.

즉, 위의 job log를 살펴본 결과, hexo generate라는 1줄짜리 스크립트가 실행되는 script phase까지는 무사히 실행되었으나, before_deploy phase에서 deploy dependency를 설치하다가 오류가 나서 build가 errored로 표시된 것임을 확인할 수 있었다.

dpl-pages와 multipart-post

에러가 난 지점에 대한 감은 잡을 수 있었다. 하지만 여전히 문제에 대한 지식은 많이 모자란 상황이다.

Installing deploy dependencies
ERROR: Error installing dpl-pages:
The last version of multipart-post (>= 1.2, < 3) to support your Ruby & RubyGems was 2.2.0. Try installing it with `gem install multipart-post -v 2.2.0` and then running the current command again
multipart-post requires Ruby version >= 2.6.0. The current ruby version is 2.4.5.335.

에러 로그를 다시 살펴보면, dpl-pages를 설치하다가 에러가 발생하였고, multipart-post가 요구하는 ruby 버전이 2.6.0 이상인 것이 문제임을 알 수 있었다.

첫번째로 든 의문은, 'dpl-pages, multipart-post는 또 뭔데?'였다. '배포 작업을 수행하기 위해 필요한 무언가' 정도로만 추정이 되는 상황이었으므로, Travis CI가 배포하는 방식에 대한 지식이 필요했다.

이전에 생각없이 hexo tutorial을 복붙하면서, 나는 인지하지 못한 채 Travis CI의 GitHub Pages Deployment 기능을 사용하고 있었다.

deploy:
provider: pages
skip-cleanup: true
github-token: $GH_TOKEN
keep-history: true
on:
branch: main
local-dir: public

위의 yml 파일이 관련 설정으로, provider: pages를 비롯한 설정을 통해 hexo generate의 결과물인 public 디렉토리 하위 파일들을 타겟 브랜치(지정하지 않았으므로 기본값인 gh-pages 브랜치)에 push하는 기능이다.

해당 기능에 대한 Travis CI 문서 확인 결과, dpl이라는 루비 코드를 사용하는 것을 알 수 있었다. 하지만 여기서 결국 dpl-pages, multipart-post에 대한 내용은 결국 찾지 못했는데, ruby에 대한 지식이 없는 상황에서 dpl의 문서에는 관련 내용이 없기 때문이다. 하지만 dpl은 rubygem으로 publish/관리되고 있다는 내용이 있었으므로, dpl, dpl-pages, multipart-post라는 gem에 대한 기본적인 정보는 찾을 수 있었다.

갈림길

Installing deploy dependencies
ERROR: Error installing dpl-pages:
The last version of multipart-post (>= 1.2, < 3) to support your Ruby & RubyGems was 2.2.0. Try installing it with `gem install multipart-post -v 2.2.0` and then running the current command again
multipart-post requires Ruby version >= 2.6.0. The current ruby version is 2.4.5.335.

위의 메시지를 자세히 보면, 배경이 어떻든 결국 multipart-post라는 gem이 ruby 2.6.0 이상을 요구하는 반면, 환경에 설치된 ruby는 2.4.5.335 버전인 것이 문제임을 알 수 있다.

따라서 이를 해결하기 위해서는 아래 두 가지 해결책을 떠올릴 수 있다.

  1. ruby 버전을 2.6.0 이상으로 올린다.
  2. multipart-post의 버전을 2.2.0으로 내린다.(에러 메시지에서 권장한 방법)

두 방법 모두 Travis CI가 동작하는 환경에 대한 조작이 필요하므로, .travis.yml 파일을 건드려야 한다. 쉽게 끝날 수도 있겠지만 아닐 가능성이 더 커서 꺼려지는데, 어느 쪽이 더 쉽고 문제가 적을지를 생각했다.

1안: ruby 버전 업그레이드

2안 같은 버전 다운그레이드는 결국 문제를 나중에 터지도록 미루는 것이라는 생각이 들었다. 또 몇 시간 전까지도 잘 동작하다가 에러가 발생한 상황이어서, ruby 2.6.0은 비교적 최근에 릴리즈된 버전일 것이라 예상했고, 새로운 버전을 쓰면 좋을 것이라는 생각이 들었다. 하지만 확인 결과, ruby 2.6.0의 릴리즈 일자는 2018년 말이고 2.6.10도 이미 지원 종료(EOL)되었다. 이를 알게 되니 '아직도 몇년 전 버전의 루비를 사용하는 것을 보니 역시 고수들도 dependency는 함부로 건드리기 어렵구나' 라는 생각에 갑자기 버전 업그레이드가 부담스러워졌다. 애초에 에러 메시지에서도 multiport-post의 버전을 낮출 것을 권장하였으니, 2안에 대해 알아보는 것이 좋을 것 같다.

2안: multipart-post 버전 다운그레이드

몇 시간 전만 해도 되던 걸 안 되게 만든 주범으로 추정되는 multipart-post는 직접적으로 확인할 수는 없었지만 에러 메시지를 통해 추정하면, 몇 시간 새 2.2.0 이후 버전이 적용되면서 Travis CI의 가상환경에서 사용되던 ruby 버전과 충돌을 일으킨 것으로 추정된다. 확인을 위해 rubygems.org에서 multipart-post를 찾아본 결과, 2022년 6월 9일에 릴리즈된 2.2.2 버전은 ruby 2.6.0 이상을 요구하였다. 하지만, 그 다음날에 릴리즈된 2.2.3 버전은 ruby 2.3.0 이상으로 요구 조건이 완화되었다. 즉, multipart-post 2.2.3 버전을 사용하면 현재 버전인 2.4.335도 문제없이 사용이 가능한 것이다.

고마워요, ioquatix!

따라서, multipart-post를 관리하는 고수님들의 빠른 조치(https://github.com/socketry/multipart-post/pull/95) 덕에, 나는 아무 것도 하지 않아도 저절로 문제가 해결되는 기쁨을 누렸다. 실제로 아무 조치 없이 빌드를 재실행한 결과, 빌드에 성공하였다. 내가 Travis CI가 사용하는 ruby의 버전을 지정하는 방법을 배워야 하는 사태가 발생하기 전에 Travis CI팀이 default ruby 버전을 계속 업그레이드 해주길 바라면서, 또 이 포스트의 속편을 바로 작성해야 하는 일이 없기를 바라면서 성공한 빌드 로그로 포스트를 마친다.

Installing deploy dependencies
Logged in as @bastionsofwill (bastionsofwill)

Preparing deploy

Deploying application
cd /tmp/d20220612-6975-s1zerh/work
commit fc19656296bd70de2ed24bdb2bf1c36bea7e3b67
Author: Deployment Bot (from Travis CI) <deploy@travis-ci.org>
Date: Sun Jun 12 12:07:27 2022 +0000
Deploy bastionsofwill/bastionsofwill.github.io to github.com/bastionsofwill/bastionsofwill.github.io.git:gh-pages
2021/07/14/web-domain-architecting/index.html | 251 ------------------------
2021/07/22/email-tracking/index.html | 13 +-
2021/08/01/tslop-04/index.html | 8 +-
2021/08/01/tslop-05/index.html | 8 +-
2021/08/06/what-is-ssl-certificate/index.html | 8 +-
2021/10/05/video-through-web-0/index.html | 8 +-
2021/11/17/hexo-travis-ghpage/index.html | 8 +-
2021/11/19/what-is-dependabot/index.html | 8 +-
2021/12/13/log4j2-vulnerability/index.html | 8 +-
2022/01/12/encoding-and-video-codecs/index.html | 8 +-
...
48 files changed, 129 insertions(+), 925 deletions(-)
cd -
Done. Your build exited with 0.

출처

https://hexo.io/ko/docs/github-pages https://docs.travis-ci.com/user/for-beginners https://docs.travis-ci.com/user/deployment https://docs.travis-ci.com/user/deployment/pages/ https://github.com/travis-ci/dpl https://rubygems.org/gems/dpl-pages https://rubygems.org/gems/multipart-post https://www.ruby-lang.org/ko/downloads/

· 2 min read

내 main 브랜치에 무슨 짓이야

Github 리포지토리에 Dependabot이라는 봇이 main 브랜치를 내가 생성한 적이 없는 브랜치와 머지하는 PR을 작성하였다. PR을 확인해보니 package-lock.json, package.json, yarn.lock 3개의 파일이 변경된 것을 확인할 수 있었는데, PR의 제목처럼 hexo-renderer-marked라는 패키지의 버전을 업데이트하면서 생긴 변경임을 확인할 수 있었다.

Dependabot이란?

Dependabot은 Bump1라는 프로젝트로부터 출발한, 프로젝트의 의존성을 최신으로 유지하는 툴이다. 핵심 기능을 담당하는 Dependabot Core는 Ruby Gem들의 집합으로 Ruby, Python, JavaScript 등 넓은 범위의 언어를 지원하며, 리포지토리에 존재하는 코드의 의존성을 확인하여 업데이트 후 빌드하여 PR을 생성하는 기능을 수행한다. 2 .github/dependabot.yml 파일을 통해 업데이트 스케줄, 최대 open된 PR의 개수 등을 추가로 설정할 수 있다.

출처