배경
logstash를 docker container로 띄우고 postgres db를 postgres db로 전달하려고 했다. db가 업데이트 되면 새로운 row들도 지속적으로 전달해야하는데 이미 업로드한 데이터는 전달하면 안되니 config에 아래와 같은 설정을 해두었다.SELECT 문에 sql_last_value 설정
WHERE id > :spl_last_value
record_last_run 옵션과 last_run_metadapa_path 옵션 설정
record_last_run => true
last_run_metadata_path => "/usr/share/logstash/last_run_record/.logstash_jdbc_last_run
위 두 설정에 따라, .logstash_jdbc_last_run 파일에 가장 최근에 업로드한 row의 id 번호가 기록되고 다음번 logstash가 실행될때 이 번호보다 같거나 작은 id를 가진 row는 더이상 업데이트하지 않는다.
crontab설정
schedule => "* * * * *"
새로 업데이트되는 db 내용도 지속적으로 logstash를 통해 전달하기 위해 매분마다 logstash가 실행되도록 schedule을 설정했다.
예시 conf 내용은 아래와 같다
input {
jdbc {
jdbc_connection_string => "jdbc:postgresql://db:5432/db_name"
jdbc_user => "user"
jdbc_password => "password"
jdbc_driver_library => "/usr/share/logstash/postgresql-42.2.14.jar"
jdbc_driver_class => "org.postgresql.Driver"
jdbc_paging_enabled => true
jdbc_page_size => 10000
tracking_column_type => "numeric"
tracking_column => id
use_column_value => true
statement =>
"SELECT ... FROM table
WHERE id > :sql_last_value
ORDER BY id asc"
schedule => "0 * * * *"
clean_run => false
record_last_run => true
last_run_metadata_path => "/usr/share/logstash/last_run_record/.logstash_jdbc_last_run"
}
}
docker-compose 설정
logstash
image: logstash:7.13.0
container_name: logstash
hostname: logstash
volumes:
- ./config:/usr/share/logstash/config
- ./last_run_record:/usr/share/logstash/last_run_record
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "1M"
max-file: "30"
depends_on:
- postgres
volume 내용을 보면, docker-compose 파일이 있는 폴더내 config 폴더에 conf파일을 저장해놓고 last_run_record 폴더에는 .logstash_jdbc_last_run 파일을 --- 0 이라는 내용으로 저장해놓았다.
예상하는 정상작동 시나리오
1. logstash가 첫번째 실행됐을 때
업데이트할 row가 10개가 있고 데이터들의 id가 1부터 10까지라고 하면 10개 row를 logstash가 두번째 postgres db로 전달하고 .logstash_jdbc_last_run파일은 --- 0에서 --- 10으로 저장된다.
2. schedule 설정에 따라 1분뒤 두번째 실행됐을 때
첫번때 postgres db에 id 11를 갖는 row한 줄이 1분사이에 추가되었다고 하면 logstash가 이 row를 전달하면서 .logstash_jdbc_last_run파일은 --- 11로 저장되야한다.
오류내용
11번째 row가 업데이트가 안되서 docker logs로 logstash의 로그를 봤더니 아래와 같은 에러 메시지가 출력되었다.
{ 2064 rufus-scheduler intercepted an error:
2064 job:
2064 Rufus::Scheduler::CronJob "* * * * *" {}
2064 error:
2064 2064
2064 Errno::EACCES
2064 Permission denied - /usr/share/logstash/last_run_record/.logstash_jdbc_last_run
2064 org/jruby/RubyIO.java:1237:in `sysopen'
2064 org/jruby/RubyIO.java:3800:in `write'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-integration-jdbc-5.0.7/lib/logstash/plugin_mixins/jdbc/value_tracking.rb:122:in `write'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-integration-jdbc-5.0.7/lib/logstash/plugin_mixins/jdbc/value_tracking.rb:46:in `write'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-integration-jdbc-5.0.7/lib/logstash/inputs/jdbc.rb:325:in `execute_query'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-integration-jdbc-5.0.7/lib/logstash/inputs/jdbc.rb:279:in `block in run'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:234:in `do_call'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:258:in `do_trigger'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:300:in `block in start_work_thread'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:299:in `block in start_work_thread'
2064 org/jruby/RubyKernel.java:1442:in `loop'
2064 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/rufus-scheduler-3.0.9/lib/rufus/scheduler/jobs.rb:289:in `block in start_work_thread'
2064 tz:
2064 ENV['TZ']:
2064 Time.now: 2021-10-10 06:23:00 UTC
2064 scheduler:
2064 object_id: 2050
2064 opts:
2064 {:max_work_threads=>1}
2064 frequency: 0.3
2064 scheduler_lock:
2064 trigger_lock:
2064 uptime: 128.51644 (2m8s516)
2064 down?: false
2064 threads: 2
2064 thread:
2064 thread_key: rufus_scheduler_2050
2064 work_threads: 1
2064 active: 1
2064 vacant: 0
2064 max_work_threads: 1
2064 mutexes: {}
2064 jobs: 1
2064 at_jobs: 0
2064 in_jobs: 0
2064 every_jobs: 0
2064 interval_jobs: 0
2064 cron_jobs: 1
2064 running_jobs: 1
2064 work_queue: 0
} 2064 .
원인
결론은 파일 쓰기 권한 문제이다.
docker volume을 bind mount했는데 호스트에 저장된 파일의 읽고 쓰는 권한과 container 내에서의 권한이 일치 하지 않아서이다.
docker-composse 파일이 저장된 위치가 /opt/ 하위 폴더 였고 해당폴더의 사용자가 root였다.
반면 logstash container의 계정을 확인해보면 아래와 같이 root가 아니다.
$ docker exec -it logstash-cxr bash
bash-4.2$ whoami
logstash
bash-4.2$ echo $UID
1000
호스트에서로 같은 UID가 1000인 상태로 .logstash_jdbc_last_run 파일을 쓰고 컨테이너로 업데이트 하려고 하게 되고 당연히 권한이 없으므로 에러가 발생한다.
해결책
bind mount한 .last_run_record 폴더의 소유자를 UID 1000으로 맞춰주면 해결된다.
/opt 하위에 폴더, 파일을 만들면 읽기 권한이 other에도 열려있으니 대게 bind mount에 문제없으나, 해당 파일을 docker container를 통해 실행하는 서비스가 업데이트해야하는 상황이 있으면 위와 같은 에러가 발생한다.
0 댓글