Rails 콘솔

Tier: Free, Premium, Ultimate Offering: Self-Managed

GitLab의 핵심은 Ruby on Rails 프레임워크를 사용하여 구축된 웹 애플리케이션입니다. Rails 콘솔은 명령줄에서 GitLab 인스턴스와 상호작용하고 Rails에 내장된 놀라운 도구에 액세스할 수 있는 방법을 제공합니다.

caution
Rails 콘솔은 GitLab과 직접 상호작용합니다. 많은 경우, 프로덕션 데이터를 영구적으로 수정, 파괴 또는 손상시키는 것을 방지할 수 있는 안전장치가 없습니다. 어떠한 결과도 없이 Rails 콘솔을 탐험하려면 테스트 환경에서 진행하는 것이 권장됩니다.

Rails 콘솔은 문제 해결을 위해 GitLab 시스템 관리자가 문제를 해결하거나 GitLab 애플리케이션에 직접 액세스하여 가져와야 하는 데이터를 검색할 때 사용됩니다. Ruby의 기본 지식이 필요합니다(빠른 소개로 이 30분 튜토리얼을 시도해보세요). Rails 경험은 유용하지만 필수는 아닙니다.

Rails 콘솔 세션 시작

Rails 콘솔 세션을 시작하는 프로세스는 GitLab 설치 유형에 따라 다릅니다.

Linux 패키지 (Omnibus)
sudo gitlab-rails console
도커
docker exec -it <container-id> gitlab-rails console
이 자체 컴파일(소스)
sudo -u git -H bundle exec rails console -e production
Helm 차트 (쿠버네티스)

콘솔은 toolbox pod에 있습니다. 세부 정보는 쿠버네티스 치트 시트를 참고하세요.

콘솔을 종료하려면 quit을 입력하세요.

자동 완성 비활성화

Ruby 자동 완성은 터미널을 느리게 만들 수 있습니다. 자동 완성을:

  • 비활성화하려면 Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE] = false를 실행하세요.
  • 다시 활성화하려면 Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE] = true를 실행하세요.

Active Record 로깅 활성화

Rails 콘솔 세션에서 Active Record 디버그 로깅의 출력을 활성화하려면 다음을 실행하세요:

ActiveRecord::Base.logger = Logger.new($stdout)

이는 콘솔에서 실행하는 Ruby 코드에 의해 트리거된 데이터베이스 쿼리에 대한 정보를 보여줍니다. 로깅을 다시 끄려면 다음을 실행하세요:

ActiveRecord::Base.logger = nil

속성

사용 가능한 속성을 보려면 pretty print(pp)를 사용하여 형식화하세요.

예를 들어, 사용자 이름과 이메일 주소가 속성에 포함되어 있는지 확인하세요:

u = User.find_by_username('someuser')
pp u.attributes

부분 결과:

{"id"=>1234,
 "email"=>"someuser@example.com",
 "sign_in_count"=>99,
 "name"=>"S User",
 "username"=>"someuser",
 "first_name"=>nil,
 "last_name"=>nil,
 "bot_type"=>nil}

그런 다음 이러한 속성을 활용할 수 있습니다. 예를 들어 SMTP를 테스트해보세요:

e = u.email
n = u.name
Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
#
Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now

(이하 생략)

작업 시간 메트릭

작업 하나 이상의 시간을 메트릭하려면 다음 형식을 사용하십시오. <operation> 플레이스홀더를 사용자가 선택한 Ruby 또는 Rails 명령어로 바꿉니다.

# 단일 작업
Benchmark.measure { <operation> }

# 여러 작업의 분해
Benchmark.bm do |x|
  x.report(:label1) { <operation_1> }
  x.report(:label2) { <operation_2> }
end

자세한 내용은 성능 테스트에 관한 개발자 문서를 참조하십시오.

Active Record 객체

데이터베이스에 저장된 객체 조회

Rails는 Active Record를 사용하여 애플리케이션 객체를 PostgreSQL 데이터베이스로 읽고 쓰며 매핑하는 객체-관계 매핑 시스템을 내부적으로 사용합니다. 이러한 매핑은 Active Record 모델에 의해 처리되며, 이는 Rails 앱에서 정의된 Ruby 클래스입니다. GitLab의 경우 모델 클래스는 /opt/gitlab/embedded/service/gitlab-rails/app/models에서 찾을 수 있습니다.

이제 Active Record의 디버그 로깅을 활성화하여 데이터베이스 쿼리를 확인해봅시다:

ActiveRecord::Base.logger = Logger.new($stdout)

이제 데이터베이스에서 사용자를 검색해 봅시다:

user = User.find(1)

이제 다음을 시도해 보십시오:

  • user.username
  • user.created_at
  • user.admin

이제 데이터베이스 스키마에서 테이블 및 열 이름 디렉터리을 찾을 수 있습니다. 해당 디렉터리은 /opt/gitlab/embedded/service/gitlab-rails/db/schema.rb에서 이용할 수 있습니다.

Active Record 모델을 사용하여 데이터베이스에서 데이터를 조회하는 다양한 방법에 대해 자세히 알아보려면 Active Record Query Interface 문서를 참조하십시오.

Active Record 모델을 사용하여 데이터베이스 쿼리

m = Model.where('attribute like ?', 'ex%')

# 예를 들어 프로젝트를 쿼리하는 방법
projects = Project.where('path like ?', 'Oumua%')

Active Record 객체 수정

이전 섹션에서 Active Record를 사용하여 데이터베이스 레코드를 검색하는 방법에 대해 알아보았습니다. 이제 데이터베이스에 변경 내용을 작성하는 방법을 알아봅시다.

먼저 root 사용자를 검색해 봅시다:

user = User.find_by(username: 'root')

다음으로 사용자의 암호를 업데이트해 보겠습니다:

user.password = 'password'
user.save

이제 다음을 시도해 보십시오:

  • User.find_by(username: 'root')
  • User.where.not(admin: true)
  • User.where('created_at < ?', 7.days.ago)

마지막 두 명령은 여러 User 객체가 포함된 것으로 보이는 ActiveRecord::Relation 객체를 반환하는 것을 알아채셨나요?

이제 관리자가 아닌 사용자의 컬렉션을 가져와서 무엇을 할 수 있는지 살펴보겠습니다:

users = User.where.not(admin: true)

이제 다음을 시도해 보십시오:

  • users.count
  • users.order(created_at: :desc)
  • users.where(username: 'support-bot')

다음 섹션에서 보듯이 User.where(username: 'support-bot') 명령어에 .where 문을 체인으로 연결하여 더 복잡한 쿼리를 생성할 수 있음을 알 수 있습니다.

또한 반환된 컬렉션에는 단일 객체만 포함되어 있는데 직접적으로 상호작용할 수 없는 것도 눈여겨 보시기 바랍니다.

더 자세한 내용은 Active Record Query Interface 문서를 참조하십시오.

Active Record 객체와 상호 작용하기

하루가 끝날 때, Active Record 객체는 일반적인 루비 객체일 뿐입니다. 따라서 임의의 작업을 수행하는 메서드를 정의할 수 있습니다.

예를 들어, GitLab 개발자들은 이중 인증을 지원하는 몇 가지 메서드를 추가했습니다.

def disable_two_factor!
  transaction do
    update(
      otp_required_for_login:      false,
      encrypted_otp_secret:        nil,
      encrypted_otp_secret_iv:     nil,
      encrypted_otp_secret_salt:   nil,
      otp_grace_period_started_at: nil,
      otp_backup_codes:            nil
    )
    self.webauthn_registrations.destroy_all # rubocop: disable DestroyAll
  end
end

def two_factor_enabled?
  two_factor_otp_enabled? || two_factor_webauthn_enabled?
end

(참조: /opt/gitlab/embedded/service/gitlab-rails/app/models/user.rb)

그런 다음 이러한 메서드를 사용하여 사용자 객체에서 사용할 수 있습니다.

user = User.find_by(username: 'root')
user.two_factor_enabled?
user.disable_two_factor!

일부 메서드는 GitLab에서 사용하는 젬 또는 루비 소프트웨어 패키지에 의해 정의됩니다. 예를 들어, GitLab이 사용자 상태를 관리하기 위해 사용하는 StateMachines 젬:

state_machine :state, initial: :active do
  event :block do
  
  ...
  
  event :activate do
  
  ...

end

해 보세요:

user = User.find_by(username: 'root')
user.state
user.block
user.state
user.activate
user.state

이전에 언급했듯이 유효성 검사 오류는 전체 객체가 데이터베이스에 저장되는 것을 방지합니다. 이는 예상치 못한 상호 작용을 일으킬 수 있습니다:

user.password = 'password'
user.password_confirmation = 'hunter2'
user.block

우리는 false를 반환합니다! 이에 대한 원인을 알아보기 위해 앞서 한 것처럼 느낌표를 추가해봅시다:

user.block!

결과로 다음과 같이 반환됩니다:

최근의 호출에 대한 추적:
        1: from (irb):87
StateMachines::InvalidTransition (Cannot transition state via :block from :active (Reason(s): Password confirmation doesn't match Password))

우리는 완전히 다른 속성에서의 유효성 검사 오류가 사용자를 업데이트하려고 시도할 때 우리를 괴롭히게 하는 것을 볼 수 있습니다.

실제적으로 GitLab 관리 설정에서 이러한 상황이 종종 발생합니다 – GitLab 업데이트로 인해 때때로 유효성 검사가 추가되거나 변경되어 이전에 저장된 설정이 이제 유효성 검사를 통과하지 못하는 경우가 있습니다. UI를 통해 한 번에 일부 설정만 업데이트할 수 있기 때문에 이 경우에는 좋은 상태로 돌아가는 유일한 방법은 Rails 콘솔을 통한 직접 조작입니다.

자주 사용되는 Active Record 모델 및 객체 조회 방법

기본 전자 메일 주소 또는 사용자 이름으로 사용자 검색:

User.find_by(email: 'admin@example.com')
User.find_by(username: 'root')

기본 또는 보조 전자 메일 주소로 사용자 검색:

User.find_by_any_email('user@example.com')

find_by_any_email 메서드는 기본 제공되는 Rails 메서드가 아닌 GitLab 개발자에 의해 추가된 사용자 정의 메서드입니다.

관리자 사용자 컬렉션 가져오기:

User.admins

admins는 내부에서 where(admin: true)를 수행하는 scope 편의 메서드입니다.

경로로 프로젝트 가져오기:

Project.find_by_full_path('group/subgroup/project')

find_by_full_path는 Rails에서 제공되는 기본 메서드가 아닌 GitLab 개발자에 의해 추가된 사용자 정의 메서드입니다.

프로젝트의 이슈 또는 Merge Request 가져오기:

project = Project.find_by_full_path('group/subgroup/project')
project.issues.find_by(iid: 42)
project.merge_requests.find_by(iid: 42)

iid는 “내부 ID”를 의미하며, 각 GitLab 프로젝트에 대해 이슈와 Merge Request ID를 유지하는 방법입니다.

경로로 그룹 가져오기:

Group.find_by_full_path('group/subgroup')

그룹의 관련 그룹 가져오기:

group = Group.find_by_full_path('group/subgroup')

# 상위 그룹 가져오기
group.parent

# 하위 그룹 가져오기
group.children

그룹의 프로젝트 가져오기:

group = Group.find_by_full_path('group/subgroup')

# 즉시 하위 프로젝트 가져오기
group.projects

# 하위 그룹의 프로젝트 가져오기 (하위 그룹 포함)
group.all_projects

CI 파이프라인 또는 빌드 가져오기:

Ci::Pipeline.find(4151)
Ci::Build.find(66124)

파이프라인 및 작업 ID 번호는 GitLab 인스턴스 전반에 걸쳐 증가하므로 이슈 또는 Merge Request과는 달리 내부 ID 속성을 사용할 필요가 없습니다.

현재 애플리케이션 설정 객체 가져오기:

ApplicationSetting.current

irb에서 객체 열기

caution
데이터를 변경하는 명령은 올바르게 실행되지 않거나 올바른 조건에서 실행되지 않으면 손해를 입힐 수 있습니다. 항상 먼저 테스트 환경에서 명령을 실행하고 복원할 백업 인스턴스를 준비하세요.

때로는 객체의 컨텍스트에서 메서드를 따라가는 게 더 쉬울 때가 있습니다. Object의 네임스페이스를 훅으로 가져와서 모든 오브젝트의 컨텍스트에서 irb를 열 수 있도록 할 수 있습니다:

Object.define_method(:irb) { binding.irb }

project = Project.last
# => #<Project id:2537 root/discard>>
project.irb
# 새로운 컨텍스트에 유의하세요
irb(#<Project>)> web_url
# => "https://gitlab-example/root/discard"

문제 해결

Rails Runner syntax error

gitlab-rails 명령은 기본적으로 git:git 사용자 및 그룹을 사용하여 Rails Runner를 실행합니다.

무언가가 잘못되었을 때 문제 해결하기 위해 몇 가지 테스트 환경을 준비해두는 것이 중요합니다.

때때로 runner는 스크립트를 찾을 수 없다는 오류 대신 문법 오류를 보여줍니다. 이 경우 원인은 때로는 스크립트가 root 계정의 home 디렉터리에 작성되었기 때문일 수 있습니다.

예시:

[root ~]# echo 'puts "hello world"' > ./helloworld.rb
[root ~]# sudo gitlab-rails runner ./helloworld.rb
Please specify a valid ruby command or the path of a script to run.
Run 'rails runner -h' for help.

/opt/gitlab/..../runner_command.rb:45: syntax error, unexpected '.'
./helloworld.rb
^
[root ~]# sudo gitlab-rails runner /root/helloworld.rb
Please specify a valid ruby command or the path of a script to run.
Run 'rails runner -h' for help.

/opt/gitlab/..../runner_command.rb:45: unknown regexp options - hllwrld

이 경우 /tmp 디렉터리로 파일을 이동하거나 응용 프로그램 설정을 업데이트하여 문제를 해결할 수 있습니다.

sudo mkdir /scripts
sudo mv /script_path/helloworld.rb /scripts
sudo chown -R git:git /scripts
sudo chmod 700 /scripts
sudo gitlab-rails runner /scripts/helloworld.rb

필터링된 콘솔 출력

콘솔에서의 일부 출력은 기본적으로 변수, 로그 또는 비밀 값을 유출하지 않도록 필터링될 수 있습니다. 이러한 출력물은 [FILTERED]로 표시됩니다. 예를 들어:

> Plan.default.actual_limits
=> ci_instance_level_variables: "[FILTERED]",

필터링을 우회하려면 객체에서 직접 값을 읽으세요. 예를 들어:

> Plan.default.limits.ci_instance_level_variables
=> 25