- Rails 콘솔 세션 시작
- Active Record 로깅 활성화
- 속성
- 작업 시간 메트릭
- Active Record 객체
- Active Record 모델을 사용하여 데이터베이스 쿼리
- 문제 해결
Rails 콘솔
GitLab의 핵심은 Ruby on Rails 프레임워크를 사용하여 구축된 웹 애플리케이션입니다. Rails 콘솔은 명령줄에서 GitLab 인스턴스와 상호작용하고 Rails에 내장된 놀라운 도구에 액세스할 수 있는 방법을 제공합니다.
Rails 콘솔은 문제 해결을 위해 GitLab 시스템 관리자가 문제를 해결하거나 GitLab 애플리케이션에 직접 액세스하여 가져와야 하는 데이터를 검색할 때 사용됩니다. Ruby의 기본 지식이 필요합니다(빠른 소개로 이 30분 튜토리얼을 시도해보세요). Rails 경험은 유용하지만 필수는 아닙니다.
Rails 콘솔 세션 시작
Rails 콘솔 세션을 시작하는 프로세스는 GitLab 설치 유형에 따라 다릅니다.
sudo gitlab-rails console
docker exec -it <container-id> gitlab-rails console
sudo -u git -H bundle exec rails console -e production
콘솔은 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
에서 객체 열기
때로는 객체의 컨텍스트에서 메서드를 따라가는 게 더 쉬울 때가 있습니다. 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