原田です。今回は弊社が提供するプロジェクト管理のクラウドサービス「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フレームワークなどを使用したアプリケーション開発を行うスタッフを募集しています。詳細はこちら
弊社での勤務に関心をお持ちの方は、知り合いの弊社社員・関係者を通じてご連絡ください。
![]() |
お客様のご協力により、サービスを改善できたことはとてもうれしいです。 |
![]() |
MacでWindowsアプリを動作させるアイデアの一つとしてAppStreamを使ってみました。 |
![]() |
OSC 2022オンラインFallで「はじめてのプロジェクト管理ツール〜Redmine超入門〜」を発表。 |
![]() |
Redmineを利用して構築されている国民年金基金連合会の「他年金調査 事業所回答システム」を調べてみました。 |
![]() |
RedmineのSettingクラスの実装があまり見ない感じで面白かったので紹介します。 |
![]() |
社員研修に伴うサポート体制変更・休業のお知らせ(5/20〜23) 社員研修に伴い、5月20日〜23日はサポート体制の変更および休業とさせていただきます。 |
![]() |
オープンソースカンファレンス2025 Nagoyaに弊社代表の前田が登壇(ブース出展あり) オープンソースカンファレンス(OSC)2025 Nagoyaに弊社代表の前田が登壇。『Redmineの意外と知らない便利な機能(Redmine 6.0 対応版)』をテーマに発表します。 |
![]() |
エンタープライズプラン向け「優先サポート」を開始 My Redmineでは、エンタープライズプランをご契約のお客様向けにサポート対応を優先的に行う「優先サポート(プライオリティサポート)」を開始いたしました。 |
![]() |
プロジェクト管理ツール「RedMica」バージョン 3.1.0をリリース Redmine互換のオープンソースソフトウェア ファーエンドテクノロジー株式会社は、2024年11月19日(日本時間)、Redmine互換のプロジェクト管理ソフトウェア「RedMica 3.1.0」をリリースしました。 |
![]() |
Redmineの最新情報をメールでお知らせする「Redmine News」配信中 新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け |