原田です。今回は弊社が提供するプロジェクト管理のクラウドサービス「My Redmine」の ログダウンロード機能 の改善について書きます。
My Redmineのログダウンロード機能では、過去5ヶ月間のログを年月ごとにダウンロードできます。
そのログダウンロード機能で、ログファイルが解凍できない事象がありました。
$ gzip -d xxxxx-YYYYMM-production.log.gz gzip: invalid compressed data--crc error gzip: xxxxx-YYYYMM-production.log.gz: uncompress failed
今回発生した問題を調査したところ以下のことが分かりました。
メモリ不足が原因の一つと考えて、次のように改善することにしました。
Zlib::GzipWriter
で実施しているが、これをOpen3.capture3
を使用してコマンド(gzip
)で圧縮するtgz(tar.gz)
形式に圧縮する現状は以下のように処理しており、全てのログデータを収集後ファイルに出力していました。今回はGB単位のログデータが存在していましたのでファイルに出力する前にOut of memoryエラーになっていた可能性も考えられます。
# ログデータ収集 logs = src_objs.each_with_object([]) do |key, arr| gz_raw_data = AWS::S3::Bucket.object(key).get.body.read gz_raw_data.each_line do |message| # # 各種処理 # arr << message end end # ログファイル出力 obj = AWS::S3::Bucket.object(dest_key) obj.upload_stream do |write_stream| Zlib::GzipWriter.wrap(write_stream) do |gzw| gzw << logs.join("\n") gzw.finish end end
これを1行読み込むたびにファイルに出力(追記)するよう見直しました。
Dir.mktmpdir(nil, '/tmp') do |dir| src_objs.each do |key| gz_raw_data = AWS::S3::Bucket.object(key).get.body.read gz_raw_data.each_line do |message| # # 各種処理 # # ログファイル出力(追記) File.open(tmp_log_file_path, 'a') do |f| f.puts(message) end end end # # ログファイルを圧縮 # # 圧縮ファイルをアップロード obj = AWS::S3::Bucket.object(dest_key) obj.upload_file(tmp_gzip_file_path) end
現状はzlibライブラリを使用してログファイルの圧縮を実施していますが、コマンド(gzip
)を実行することで同様の圧縮は可能です。コマンドは多くの方々により日々改善されています。自前で圧縮処理を作成するより速度や効率の面から見てもコマンドを使用するべきと思いました。
任意のコマンドが実行できる Open3.capture3 メソッドを使用することで、標準出力・標準エラー・終了ステータスの取得が可能です。
# gzip圧縮 o, e, s = Open3.capture3("gzip %s" % tmp_log_file_path)
一ヶ月分のログデータがGB単位になることを私は想定していませんでした。利用頻度が多くなると出力されるログデータも増加すると思います。
そこでログデータを一定のファイルサイズで分割し、分割したファイルをまとめて(アーカイブし)圧縮するtgz(tar.gz)形式に変更します。現状の圧縮形式(gzip)で問題なく運用できていますので、この「一定のファイルサイズ」を大きな値(例えば、500MB)にしておく必要があります。
Dir.mktmpdir(nil, '/tmp') do |dir| tmp_log_file_path = nil tmp_file_paths = [] src_objs.each do |key| gz_raw_data = AWS::S3::Bucket.object(key).get.body.read gz_raw_data.each_line do |message| # # 各種処理 # unless tmp_log_file_path tmp_log_file_path = "#{TMP_LOG_FILE_PATH}.%03d" % (tmp_file_paths.size + 1) tmp_file_paths << tmp_log_file_path end # ログファイル出力(追記) File.open(tmp_log_file_path, 'a') do |f| f.puts(message) if FileTest.size(f) >= SPLIT_FILE_SIZE tmp_log_file_path = nil end end end end if tmp_file_paths.size == 1 # gzip圧縮(現状と同じ) else # tgz圧縮 o, e, s = Open3.capture3( "tar -czf %s %s" % [tmp_compression_file_path, tmp_file_paths.join(' ')] ) end # # 圧縮ファイルをアップロード # end
AWS EC2のようにサーバーを管理することが無く、アプリケーションの実装に集中できますので私にはFargate・Lambda・Step Functionsが性に合っています。今回のことでサーバーレスコンピューティングをもっと理解しなければと痛感しました。
【スタッフ募集中】
弊社ではAWSを活用したソリューションの企画・設計・構築・運用や、Ruby on Rails・JavaScriptフレームワークなどを使用したアプリケーション開発を行うスタッフを募集しています。詳細はこちら
弊社での勤務に関心をお持ちの方は、知り合いの弊社社員・関係者を通じてご連絡ください。
お客様のご指摘から「My Redmine」を改善できました お客様のご協力により、サービスを改善できたことはとてもうれしいです。 |
|
仮想マシン+Windowsの代わりにAmazon AppStream2.0を使ってみるのはどうでしょう? MacでWindowsアプリを動作させるアイデアの一つとしてAppStreamを使ってみました。 |
|
オープンソースカンファレンス 2022 でセミナー発表しました OSC 2022オンラインFallで「はじめてのプロジェクト管理ツール〜Redmine超入門〜」を発表。 |
|
Redmineで構築されている国民年金基金連合会の「他年金調査 事業所回答システム」を調べてみた Redmineを利用して構築されている国民年金基金連合会の「他年金調査 事業所回答システム」を調べてみました。 |
|
RedmineのSettingクラスのコードを読んでみる RedmineのSettingクラスの実装があまり見ない感じで面白かったので紹介します。 |
2024年5月11日 オライリー本の全冊公開日のお知らせ(もくもく勉強会も同時開催) ファーエンドテクノロジーが所蔵するオライリー本(全冊)公開日のご案内です。公開日には「もくもく勉強会」も同時開催します。 |
|
入門Redmine 第6版 出版記念企画セミナー「Redmineのアクセス制御」【2024/5/30開催】 入門Redmine 第6版(2024年3月23日発売)の書籍から「Redmineのアクセス制御」について解説します。 |
|
My Redmine 初回ご契約で「入門Redmine 第6版」プレゼントのお知らせ Redmineのクラウドサービス「My Redmine」を初めてご契約いただいたお客様にRedmine解説書「入門Redmine 第6版」を進呈いたします。 |
|
2024年度ブランドパートナーに島根県在住のモデル ユイさんを継続起用 ユイさん(モデルスタジオミューズ所属)をファーエンドテクノロジーの2024年度ブランドパートナーとして継続して起用します。 |
|
Redmineの最新情報をメールでお知らせする「Redmine News」配信中 新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け |