Skip to main content

3 posts tagged with "weekend"

View All Tags

· 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/

· 8 min read

네트워크 애플리케이션 구조

클라이언트/서버 구조

  • 웹 애플리케이션과 같이, 항상 켜져있는 서버 호스트가 다른 많은 클라이언트 호스트의 요청을 처리하는 구조
  • 클라이언트는 직접 통신하지 않음
  • 서버가 고정 IP주소라는 잘 알려진 주소를 가짐
  • 하나의 서버 호스트가 클라이언트의 모든 요청을 처리하기 어려울 경우, 데이터 센터 등으로 가상 서버를 생성

P2P 구조

  • 항상 켜져있는 기반 서버에 최소 의존 또는 전혀 의존하지 않음
  • 간헐적으로 연결된 호스트 쌍이 서로 직접 통신
  • 클라이언트-서버 구조와 P2P 요소를 결합한 하이브리드 구조도 존재
  • 자가 확장성을 가지며, 비용 효율적

프로세스 간 통신

  • 서로 다른 2개의 종단 시스템에서 각각의 프로세스는 메시지 교환을 통해 통신

프로세스 - 네트워크 인터페이스

  • 프로세스는 소켓(socket, 호스트의 애플리케이션 레이어와 트랜스포트 레이어 간 인터페이스)을 통해 네트워크로 메시지를 주고받음
  • 애플리케이션 개발자는 트랜스포트 계층에는 트랜스포트 프로토콜의 선택과 약간의 매개변수만 통제가 가능

TCP와 UDP

TCP

  • 연결 지향형 서비스: 클라이언트와 서버는 전송 제어 정보를 교환하는 핸드셰이크 단계를 거친 후에 연결이 생성되어 양방향 메시지 전달이 가능해지고, 메시지 전송이 끝나면 연결을 끊는다.
  • 신뢰적 데이터 전송 서비스
  • SSL: 암호화를 제공하지 않는 TCP에 보안성을 갖추기 위해 애플리케이션 레이어에 구현된 암호화 프로토콜
  • 혼잡 제어 방식: 인터넷의 전체 성능 향상을 위해 각 연결의 대역폭 조정 및 제한

UDP

  • 최소 서비스 모델: 비연결형, 비신뢰적 데이터 전송 서비스
  • 혼잡 제어 방식 미포함

HTTP

  • HTTP는 클라이언트 프로그램(웹 브라우저)과 서버 프로그램(웹 서버)으로 구현된다.
  • 웹 페이지를 구성하는 각 객체의 URL은 1)해당 객체가 존재하는 서버의 호스트 네임과 2)객체의 경로 이름을 가진다.
  • HTTP는 TCP를 기반으로 하며, 브라우저와 서버의 프로세스는 소켓 인터페이스를 통해 TCP 통신을 진행한다.
  • HTTP는 상태가 없다(stateless).

HTTP 메시지 포맷

HTTP req 메시지

  • request 라인: HTTP req 메시지의 첫 번째 줄이다.
    • 메서드 필드(GET, POST, HEAD, PUT, DELETE, etc.)
      • GET: URL에 해당하는 객체를 요청한다.
      • POST: 사용자가 입력한 form 정보를 전달을 포함한 객체를 요청한다.
      • HEAD: GET과 유사하나, 객체는 보내지 않고 HTTP 메시지 res만 요청한다.
      • PUT: 웹 서버에 업로드할 객체가 필요한 애플리케이션이 사용한다.
      • DELETE: 웹 서버에 있는 객체의 삭제를 요청한다.
    • URL 필드
    • HTTP 버전 필드
  • header 라인: HTTP req 메시지의 나머지 줄이다.
    • Host: 객체가 존재하는 호스트 네임이며, 웹 프록시 캐시가 요구하는 정보이다.
    • Connection: 지속 연결을 사용할지 여부
    • User-agent: 브라우저 타입
    • Accept-language: 선호 언어
  • entity body: GET에서는 empty 상태이며, POST에서는 사용자가 입력한 form 정보를 포함한다.

HTTP res 메시지

  • 초기 상태 라인
    • HTTP 버전 필드
    • 상태 코드 및 메시지
      • 200 OK
      • 301 Moved Permanently
      • 400 Bad Request
      • 404 Not Found
      • 505 HTTP Version Not Supported
  • header 라인:
    • Connection: 클라이언트에게 메시지를 보낸 후 연결을 지속할지 여부
    • Date: 서버가 HTTP res를 생성하고 보낸 시간
    • Server: 웹 서버 타입(req의 User-agent와 유사)
    • Last-Modified: 객체 생성 또는 최근 수정 시간이며, 캐싱 기능에 중요하게 사용된다.
    • Content-Length: 객체 크기
    • Conent-Type: 객체 타입
  • entity body

쿠키

  • HTTP는 stateless하므로, 서버가 사용자를 추적하기 위해 쿠키(cookie)를 사용한다.
  • 쿠키는 아래와 같은 4개 요소로 작동한다.
    • HTTP res 메시지 쿠키 헤더 라인: 서버는 사용자 식별을 위한 ID를 생성하고 Set-cookie: 헤더에 이를 포함한다.
    • 브라우저 쿠키 파일: 브라우저는 이를 자신이 관리하는 쿠키 파일에 저장한다.
    • HTTP req 메시지 쿠키 헤더 라인: 브라우저는 쿠키 파일에 현재 사이트에서 발급받은 쿠키가 있을 경우 이를 Cookie: 헤더로 포함하여 req 메시지를 전달한다.
    • 서버(사이트) 백엔드 데이터베이스: 쿠키로 식별한 각 사용자의 활동 정보가 저장된다.

웹 캐싱

  • 웹 캐시는 프록시 서버라고도 불리며, origin을 대신하여 HTTP req를 처리해줄 수 있는 네트워크 개체이다.
  • 이를 위해 자체 저장 디스크를 갖추고 최근 호출된 객체의 사본을 저장한다.
  • 브라우저 설정을 통해 모든 HTTP 요청을 웹 캐시를 거치도록 구성할 수 있다.

· 5 min read

구성요소로 본 인터넷

  • 인터넷은 여러 개의 컴퓨팅 장치(종단 시스템)를 연결하는 컴퓨터 네트워크
  • 종단 시스템은 통신 링크(물리 매체)와 패킷 스위치의 네트워크로 연결
  • packet = segment(데이터의 조각) + header
  • 가장 널리 사용되는 패킷 스위치는 라우터와 링크-레이어 스위치로, 최종 목적지 방향으로 패킷을 전달하는 역할을 수행하며, 링크-레이어 스위치는 주로 액세스 네트워크에서, 라우터는 네트워크 코어에서 사용

서비스 측면에서 본 인터넷

  • 인터넷에 접속된 종단 시스템(출발지)은 인터넷 인프라 구조에게 다른 종단 시스템(목적지)에서 수행되는 프로그램에 데이터를 어떻게 전달하도록 요구하는지 명시하는 API(Application Programing Interface)를 제공
  • API는 송신 프로그램이 따라야 하는 규칙의 집합이며, 인터넷은 이 규칙에 따라 데이터를 목적지로 전달

액세스 네트워크, 네트워크 코어

  • 액세스 네트워크: 종단 시스템 간 경로 중 출발지와 첫 번째 라우터를 연결하는 네트워크
  • 네트워크 코어: 패킷 스위치와 링크의 메시(mesh)
  • 링크, 스위치의 네트워크를 통한 데이터의 전송에는 회선 교환과 패킷 교환 방식이 존재

인터넷 프로토콜 스택

  • 네트워크 프로토콜은 레이어 구조를 가지며, 각 프로토콜은 한 레이어에 속하며, 상위 레이어는 하위 레이어의 서비스를 이용할 수 있다.
  • 인터넷 프로토콜 스택은 Application, Transport, Network, Link, Physical 5개 레이어로 구성된다.

Application Layer

  • 인터넷에서 사용하는 애플리케이션 계층 프로토콜은 대표적으로 HTTP, SMTP, FTP등이 있다.
  • 애플리케이션 레이어에서의 정보 패킷을 메시지(message)라 부른다.

Transport Layer

  • 애플리케이션 레이어로부터 전달받은 메시지를 전송하는 서비스를 제공한다.
  • 인터넷에서는 TCP, UDP 2가지의 트랜스포트 프로토콜이 사용된다.
  • 트랜스포트 레이어에서의 정보 패킷을 세그먼트(segment)라 부른다.

Network Layer

  • 출발지 호스트의 트랜스포트 레이어로부터 전달받은 세그먼트를 목적지 호스트의 트랜스포트 레이어로 전송하는 서비스를 제공한다.
  • 네트워크 레이어에서의 정보 패킷을 데이터그램(datagram)이라 부른다.
  • IP는 데이터그램의 필드 및 네트워크 요소(종단 시스템 및 라우터)가 각 필드에 어떻게 동작하는지 정의하는, 유일한 프로토콜이다.
  • 라우팅 프로토콜 역시 네트워크 레이어에 속하며, 인터넷은 다양한 라우팅 프로토콜을 가지고 있다.
  • 한 노드(호스트 또는 패킷 스위치)에서 다른 노드로 패킷을 전달하는 서비스를 제공한다.
  • 이더넷, 와이파이, DOCSIS(Data Over Cable Service Interface Specification) 등이 있다.
  • 링크 레이어에서의 정보 패킷을 프레임(frame)이라 부른다.

Physical Layer

  • 한 노드에서 다른 노드로 프레임 내부의 각 비트를 전달하는 기능을 담당한다.

캡슐화(Encapsulation)

  • 패킷은 각 레이어를 거치면서, 상위 계층에서 받은 정보에 헤더를 추가하면서 데이터를 캡슐화한다.

네트워크 공격

  • 호스트에서 악성코드 실행
  • 네트워크 인프라 공격(DoS)
  • 패킷 탐지(Sniffing)
  • 패킷 위장(Spoofing)