Closed1

wkhtmltopdf-binary を使っている Rails コンテナのイメージサイズ削減

daisaru11daisaru11

wkhtmltopdf-binary が結構イメージサイズを食っているということがあった。

bin/ 以下に各種アーキテクチャ・OS別のバイナリがあるので、これがそこそこ大きい
https://github.com/zakird/wkhtmltopdf_binary_gem/tree/e1baf3e9dc97e64d79f8333bd1a5848de5dee407/bin
実行時には、これらのうち実際のアーキテクチャ・OSにマッチするバイナリ以外しかいらないはずなので、いらないものはインストールしないようにしたい。

やや豪快だが、インストール後に不要なバイナリは削除することにした。
下記のようなスクリプトを足す。

ただし、アップデートで、バイナリの命名規則が変わると動かなくなることに注意する。

remove_unnecessary_binaries.rb
require 'rbconfig'

# バイナリの判定ロジックは、こちらを参考にしている: https://github.com/zakird/wkhtmltopdf_binary_gem/blob/e1baf3e9dc97e64d79f8333bd1a5848de5dee407/bin/wkhtmltopdf 

def architecture
  case RbConfig::CONFIG['host_cpu']
  when *%w[arm64 aarch64 arch64]
    'arm64'
  when 'x86_64'
    'amd64'
  else
    'i386'
  end
end

def detect_binary_suffix
  suffix = case RbConfig::CONFIG['host_os']
           when /linux/
             os = `. /etc/os-release 2> /dev/null && echo ${ID}_${VERSION_ID}`.strip
       
             os = 'ubuntu_16.04' if os.start_with?('ubuntu_16.') ||
                                    os.start_with?('ubuntu_17.') ||
                                    os.start_with?('linuxmint_18.')
       
             os = 'ubuntu_18.04' if os.start_with?('ubuntu_18.') ||
                                    os.start_with?('ubuntu_19.') ||
                                    os.start_with?('elementary') ||
                                    os.start_with?('linuxmint_19.') ||
                                    os.start_with?('pop') ||
                                    os.start_with?('zorin')
       
             os = 'ubuntu_20.04' if os.start_with?('ubuntu_20.') ||
                                    os.start_with?('linuxmint_20.')
       
       	    os = 'ubuntu_21.10' if os.start_with?('ubuntu_21.') ||
                                    os.start_with?('linuxmint_21.')
       
             os = 'ubuntu_22.04' if os.start_with?('ubuntu_22.') ||
                                    os.start_with?('ubuntu_24.') ||
                                    os.start_with?('tuxedo_22.') ||
                                    os.start_with?('linuxmint_22')
       
             os = 'centos_6' if (os.start_with?('amzn_') && os != 'amzn_2' && os != 'amzn_2023') ||
                                (os.empty? && File.read('/etc/centos-release').start_with?('CentOS release 6'))
       
             os = 'centos_7' if (os.start_with?('amzn_2') && !os.start_with?('amzn_20')) ||
                                os.start_with?('rhel_7.')
       
             os = 'centos_8' if os.start_with?('rocky_8') ||
                                os.start_with?('rhel_8.') ||
                                os.start_with?('ol_8.') ||
                                os.start_with?('almalinux_8') ||
                                os.start_with?('alinux_') ||
                                os == 'amzn_2023'
       
             os_based_on_debian_9 = os.start_with?('debian_9') ||
                                    os.start_with?('deepin')
             os = 'debian_9' if os_based_on_debian_9
       
             os = 'debian_10' if !os_based_on_debian_9 && os.start_with?('debian_10')
       
             os = 'debian_11' if !os_based_on_debian_9 && os.start_with?('debian_11')
       
             os = 'debian_12' if !os_based_on_debian_9 && os.start_with?('debian_12')
       
             os = 'archlinux' if os.start_with?('arch_') ||
                                 os.start_with?('manjaro_')
       
             "#{os}_#{architecture}"
           when /darwin/
             'macos_cocoa'
           else
             'unknown'
           end

  suffix
end

binary_dir = File.dirname(Gem.bin_path("wkhtmltopdf-binary", "wkhtmltopdf"))
binary_suffix = detect_binary_suffix

exclude_file = File.join(binary_dir, "wkhtmltopdf_#{binary_suffix}.gz")

Dir.glob("#{binary_dir}/*.gz").each do |file|
  if File.file?(file) && file != exclude_file
    puts "Removing #{file}"
    File.delete(file)
  end
end

Dockerfile 内で bundle install した後にこのスクリプトを実行する。

...
COPY remove_unnecessary_binaries.rb /app/remove_unnecessary_binaries.rb

RUN bundle config set --local without 'test development' \
  && bundle install -j4 \
  && ruby /app/remove_unnecessary_binaries.rb \
  && rm -rf /usr/local/bundle/cache/*.gem \
  && find /usr/local/bundle/gems/ -name "*.c" -delete \
  && find /usr/local/bundle/gems/ -name "*.o" -delete
このスクラップは2025/02/10にクローズされました