LoginSignup
1
3

More than 3 years have passed since last update.

AWS Cloud9 に Ruby 2.5.0 と thinreports gem を インストールして Lambda で PDFを生成する方法

Last updated at Posted at 2019-10-26

はじめに

AWS Cloud9 環境にて Rubyで作成した Lambda関数を Cloud9でzip圧縮→Lambdaにアップロードして動かすまでやってみました。

ポイントは下記の通り

  • LambdaのRubyランタイムと合わせる為に、Cloud9 に rvm で Ruby 2.5.0 をインストール
  • S3バケットにLambda関数からPDFファイルを書き込む為の権限設定

最終的に、[kintone] → [API Gateway] → [Lambda] → [PDF生成] → [/tmp保存] → [kintone添付ファイルに保存] までの予定ですが、まず [/tmp保存] まで実装します。

実装イメージ

今回はオレンジ色の範囲を実装します。

kintoneAwsLambdaThinreports.png

実行環境

  • AWS Cloud9
  • macOS 10.13.6
  • Ruby 2.5.0

処理の流れ

  1. Cloud9 に Ruby 2.5.0 をインストール
  2. kintoneアプリ作成→PDF生成処理の[Lambda]関数を実行する[API Gateway]を叩くJSを仕込みます
  3. PDF生成用のPDFレイアウトファイルの作成
  4. PDF生成用のLambda関数の作成
  5. S3バケットの権限設定
  6. S3バケットにPDFファイルを生成できることを確認
  7. /tmp にPDFファイルを保存

一旦前回の処理と同様にS3にPDFファイルを生成することが出来ることを確認してから、/tmpに保存してみたいと思います。

0.(事前準備)Cloud9 に Ruby 2.5.0 をインストール

(2019-10-25現在)Lambdaで実行される Rubyランタイム のバージョンは 2.5.0 です。
作成するライブラリやディレクトリ構成も合わせる必要があるので、Cloud9 に rvm を利用して Ruby 2.5.0 をインストールします。
また、今回必要なライブラリも bundler で プロジェクトディレクトリの下の vendoer/bundle にインストールします。(最終的にZipでまとめて Lambda にアップロードする為です)

rvm で Ruby 2.5.0 をインストール

ターミナルから rvm で Rubyをインストールしていきます。
rvm list で確認すると、現在は Ruby 2.6.0 がデフォルトでインストールされています。
rvm install ruby 2.5.0 で Ruby 2.5.0 をインストールします。

$ rvm list
=* ruby-2.6.3 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

$ rvm install 2.5.0
$ rvm install 2.5.0
Warning, new version of rvm available '1.29.9', you are using older version '1.29.8'.
You can disable this warning with:    echo rvm_autoupdate_flag=0 >> ~/.rvmrc
You can enable  auto-update  with:    echo rvm_autoupdate_flag=2 >> ~/.rvmrc
Searching for binary rubies, this might take some time.
No binary rubies available for: amazon/2018.03/x86_64/ruby-2.5.0.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for amazon.
Requirements installation successful.
Installing Ruby from source to: /home/ec2-user/.rvm/rubies/ruby-2.5.0, this may take a while depending on your cpu(s)...
ruby-2.5.0 - #downloading ruby-2.5.0, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 13.3M  100 13.3M    0     0  28.0M      0 --:--:-- --:--:-- --:--:-- 28.0M
ruby-2.5.0 - #extracting ruby-2.5.0 to /home/ec2-user/.rvm/src/ruby-2.5.0.....
ruby-2.5.0 - #applying patch /home/ec2-user/.rvm/patches/ruby/2.5.0/prelude_gcc_diagnostic.patch.
ruby-2.5.0 - #applying patch /home/ec2-user/.rvm/patches/ruby/2.5.0/libressl_2_7.patch.
ruby-2.5.0 - #configuring...................................................................
ruby-2.5.0 - #post-configuration..
ruby-2.5.0 - #compiling...................................................................................
ruby-2.5.0 - #installing.............................
ruby-2.5.0 - #making binaries executable..
ruby-2.5.0 - #downloading rubygems-3.0.6
ruby-2.5.0 - #extracting rubygems-3.0.6......
ruby-2.5.0 - #removing old rubygems........
ruby-2.5.0 - #installing rubygems-3.0.6...............................................
ruby-2.5.0 - #gemset created /home/ec2-user/.rvm/gems/ruby-2.5.0@global
ruby-2.5.0 - #importing gemset /home/ec2-user/.rvm/gemsets/global.gems................................................................
ruby-2.5.0 - #generating global wrappers.......
ruby-2.5.0 - #gemset created /home/ec2-user/.rvm/gems/ruby-2.5.0
ruby-2.5.0 - #importing gemsetfile /home/ec2-user/.rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.5.0 - #generating default wrappers.......
ruby-2.5.0 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.5.0 - #complete 
Please be aware that you just installed a ruby that requires 2 patches just to be compiled on an up to date linux system.
This may have known and unaccounted for security vulnerabilities.
Please consider upgrading to ruby-2.6.3 which will have all of the latest security patches.
Ruby was built without documentation, to build it run: rvm docs generate-ri
admin-user:~/environment $ rvm list
=> ruby-2.5.0 [ x86_64 ]
 * ruby-2.6.3 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

bundle install で aws-sdk-s3, thinreports をインストール

Ruby 2.5.0 がインストールされ利用できる様になりました。
次は以下の2つのRubyライブラリをインストールします。

プロジェクトのディレクトリに移動して、
bundle init
出来たGemfileに追記

gem 'thinreports'
gem 'aws-sdk-s3', '~> 1'

bundle install を実行します

$ bundle install
Fetching gem metadata from https://rubygems.org/...............
Resolving dependencies...
Fetching aws-eventstream 1.0.3
Installing aws-eventstream 1.0.3
Fetching aws-partitions 1.228.0
Installing aws-partitions 1.228.0
Fetching aws-sigv4 1.1.0
Installing aws-sigv4 1.1.0
Fetching jmespath 1.4.0
Installing jmespath 1.4.0
Fetching aws-sdk-core 3.72.0
Installing aws-sdk-core 3.72.0
Fetching aws-sdk-kms 1.25.0
Installing aws-sdk-kms 1.25.0
Fetching aws-sdk-s3 1.51.0
Installing aws-sdk-s3 1.51.0
Using bundler 1.17.3
Fetching pdf-core 0.7.0
Installing pdf-core 0.7.0
Fetching ttfunk 1.5.1
Installing ttfunk 1.5.1
Fetching prawn 2.2.2
Installing prawn 2.2.2
Fetching thinreports 0.10.3
Installing thinreports 0.10.3
Bundle complete! 2 Gemfile dependencies, 12 gems now installed.
Bundled gems are installed into `./vendor/bundle`

ディレクトリ構成確認

プロジェクトディレクトリは下記の構成になっています。

|--hello-world.rb
|--hello_world.tlf
|--kintoneToPdf
| |--index.js
|--template.yaml
|--vendor
| |--bundle
| | |--ruby
| | | |--2.5.0
| | | | |--bin
| | | | |--build_info
| | | | |--cache
| | | | |--doc
| | | | |--extensions
| | | | |--gems

| | | | | |--aws-sdk-s3-1.51.0
| | | | | | |--lib
| | | | | | | |--aws-sdk-s3
| | | | | | | | |--bucket_acl.rb
| | | | | | | | |--bucket_cors.rb
| | | | | | | | |--bucket_lifecycle_configuration.rb
| | | | | | | | |--bucket_lifecycle.rb
| | | | | | | | |--bucket_logging.rb
| | | | | | | | |--bucket_notification.rb
| | | | | | | | |--bucket_policy.rb
| | | | | | | | |--bucket.rb
| | | | | | | | |--bucket_region_cache.rb

| | | | | |--thinreports-0.10.3
| | | | | | |--CHANGELOG.md
| | | | | | |--fonts
| | | | | | |--Gemfile
| | | | | | |--lib
| | | | | | | |--thinreports
| | | | | | | | |--config.rb
| | | | | | | | |--core

vendor/bundle/ruby/2.5.0/gems 以下に aws-sdk-s3, thinreports の Ruby gems(ライブラリ) がインストールされました。

1. kintoneアプリ作成

次の拙稿の記事を参考にして下さい。
https://qiita.com/sy250f/items/0f2aaf17fd670bf82c84#kintone%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E6%88%90

2. Thinreportsレイアウトファイルの作成

ThinreportsThinreports Editorを使って作成したレイアウトファイルをテンプレートとして、PDFを生成します。

次の拙稿の記事を参考にして下さい。
https://qiita.com/sy250f/items/0f2aaf17fd670bf82c84#thinreports%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90

作成したレイアウトファイルは、後ほど Zip圧縮するためにCloud9のプロジェクトディレクトリにアップロードしておきます。

3. AWS Lambad for Ruby と Thinreports によるPDF出力

前段の記事では、ローカルの mac環境で作成したプログラムをZipにしてLambdaにアップロードしていました。
今回は Cloud9環境 で Lambda を作成してデプロイしてみます。

Cloud9 から Lambdaアプリの作成

新しく Lambdaアプリを作成していきます。
λ+(create new lambda function)ボタンから作成していきます。下記スクリーンショットを参考にしてください。

1.λ+ボタン押下
スクリーンショット 2019-10-24 9.37.02.png

2.ファイル名、アプリ名入力(ディレクトリは アプリ名/ファイル名/ になります)
スクリーンショット 2019-10-24 9.33.49.png

3.blueprintの選択に Ruby が無いのでとりあえず Node.js を選びます。
スクリーンショット 2019-10-24 9.34.23.png

4.API Gateway の設定をします。
スクリーンショット 2019-10-24 9.34.55.png

5.ロールは自動作成にします。
後ほどS3の権限も合わせて調整します。
スクリーンショット 2019-10-24 9.35.07.png

Ruby にて Lambda関数 の作成

Ruby にて Lambda関数を作成します。

コードは拙稿を参考ください。
https://qiita.com/sy250f/items/0f2aaf17fd670bf82c84#lambda%E3%83%95%E3%82%A1%E3%83%B3%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E6%88%90

Lambdaアプリの直下のディレクトリに右クリックして [hello_world.rb] を作成します。
コードは上記をそのままコピーします。
スクリーンショット 2019-10-24 10.03.46.png

zipファイル作成

Lambdaの実行に必要なファイルをzip圧縮します。
必要なファイルとフォルダは下記の通り。

  • hello-world.rb(プログラム本体)
  • hello_world.tlf(Thinreports Editor で作成したレイアウトファイル)
  • vendor(gemファイルのディレクトリ)

zip -r アーカイブファイル名 圧縮するファイルやディレクトリをスペース区切りで指定。

詳しくは man zip にて確認してください。

$ ls -la
total 42076
drwxr-xr-x 6 ec2-user ec2-user     4096 Oct 26 01:21 .
drwxr-xr-x 8 ec2-user ec2-user     4096 Oct 24 00:35 ..
-rw-r--r-- 1 ec2-user ec2-user      349 Oct 24 02:26 .application.json
drwxrwxr-x 2 ec2-user ec2-user     4096 Oct 24 02:55 .bundle
-rw-r--r-- 1 ec2-user ec2-user      189 Oct 25 15:57 Gemfile
-rw-rw-r-- 1 ec2-user ec2-user      784 Oct 26 00:19 Gemfile.lock
drwxrwxr-x 8 ec2-user ec2-user     4096 Oct 24 01:15 .git
-rw-r--r-- 1 ec2-user ec2-user       34 Oct 24 02:53 .gitignore
-rw-r--r-- 1 ec2-user ec2-user     1498 Oct 25 09:23 hello-world.rb
-rw-r--r-- 1 ec2-user ec2-user    56945 Oct 24 13:07 hello_world.tlf
drwxr-xr-x 2 ec2-user ec2-user     4096 Oct 24 22:24 kintoneToPdf
-rw-r--r-- 1 ec2-user ec2-user      841 Oct 24 06:15 template.yaml
drwxrwxr-x 3 ec2-user ec2-user     4096 Oct 26 00:19 vendor

$ zip -r hello-world.zip hello-world.rb hello_world.tlf vendor

$ ls -la
total 42076
drwxr-xr-x 6 ec2-user ec2-user     4096 Oct 26 01:21 .
drwxr-xr-x 8 ec2-user ec2-user     4096 Oct 24 00:35 ..
-rw-r--r-- 1 ec2-user ec2-user      349 Oct 24 02:26 .application.json
drwxrwxr-x 2 ec2-user ec2-user     4096 Oct 24 02:55 .bundle
-rw-r--r-- 1 ec2-user ec2-user      189 Oct 25 15:57 Gemfile
-rw-rw-r-- 1 ec2-user ec2-user      784 Oct 26 00:19 Gemfile.lock
drwxrwxr-x 8 ec2-user ec2-user     4096 Oct 24 01:15 .git
-rw-r--r-- 1 ec2-user ec2-user       34 Oct 24 02:53 .gitignore
-rw-r--r-- 1 ec2-user ec2-user     1498 Oct 25 09:23 hello-world.rb
-rw-r--r-- 1 ec2-user ec2-user    56945 Oct 24 13:07 hello_world.tlf
-rw-rw-r-- 1 ec2-user ec2-user 42976393 Oct 26 01:21 hello-world.zip
drwxr-xr-x 2 ec2-user ec2-user     4096 Oct 24 22:24 kintoneToPdf
-rw-r--r-- 1 ec2-user ec2-user      841 Oct 24 06:15 template.yaml
drwxrwxr-x 3 ec2-user ec2-user     4096 Oct 26 00:19 vendor

出来たzipファイルはzipファイル上で右クリック→ダウンロードを選択して、ローカルへダウンロードします。

4. Lambda 及び S3 バケットへの権限設定

S3に書き込み権限を付与した[IAM]を作成して、LambdaにIAMを設定します。
また、S3バケットに作成した[IAM]にてオブジェクト(今回はPDF)を書き込める様に設定します。

下記の図の様に権限設定を行います。

s3_read_write_setting.png

IAM ポリシー

Lambdaの実行ポリシーとS3バケットの書き込み権限を付けます。

Lambdaの実行権限を付与

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

S3への書き込み権限を付与

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::S3バケット名/*"
        }
    ]
}

S3バケットの権限設定

{
    "Id": "Policy9999999999",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt9999999999",
            "Action": [
                "s3:PutObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ],
            "Principal": {
                "AWS": [
                    "arn:aws:iam::Lambdaに設定したIAMロール"
                ]
            }
        }
    ]
}

5. S3バケットに生成したPDFの出力を確認する

kintoneアプリに設置したボタンをクリックしてLabmdaを実行し、S3にPDFが生成されていることを確認します。

S3バケットにzipファイルをアップロード

S3ファイルのURLをコピーします。
スクリーンショット 2019-10-26 11.25.26.png

LambdaにS3のzipファイルをアップロード

関数コードをS3からアップロードします。先ほどコピーしたURLを貼り付けます。
スクリーンショット 2019-10-26 11.25.00.png

kintoneアプリからLambdaを実行

kintoneアプリから実行します。
スクリーンショット 2019-10-26 11.32.37.png

PDFファイルの生成を確認

バケットを確認します。
スクリーンショット 2019-10-26 11.34.06.png

PDFファイルを開いて中身を確認します。
スクリーンショット 2019-10-26 11.33.42.png

6. /tmp ディレクトリにPDF出力

コードを編集して、Lambda の /tmp ディレクトリにPDFを出力してみます。

hello_world.rb
# coding: utf-8
require 'thinreports'
require 'json'
require 'aws-sdk-s3'

def lambda_handler(event:, context:)
    begin
        print 'JSON.parse event.body => '
        pp JSON.parse(event["body"])
        event_body = JSON.parse(event["body"])
        record = event_body["record"]
        print 'record => '
        pp record
        puts '$id => ', record["$id"]["value"]
        puts 'text => ', record["text"]["value"]
    rescue => e
        puts "#{e.class}: #{e.message}"
        puts e.backtrace
    end

    begin
        region = 'S3バケットのリージョン'
        bucket_name = 'バケット名'
        key = 'put-hello-ruby.pdf'
        # S3 Backet Object init
        s3 = Aws::S3::Resource.new(region: region)
        obj = s3.bucket(bucket_name).object(key)
        report = Thinreports::Report.new layout: 'hello_world'
        # 1st page
        report.start_new_page
        report.page.item(:world).value(record["$id"]["value"])
        report.page.item(:thinreports).value(record["text"]["value"])
        obj.put(body: report.generate)

        # /tmpファイルに書き込み
        filename = '/tmp/put-hello-ruby-' + Time.new.localtime("+09:00").strftime("%Y%m%d%H%M%S") + '.pdf'
        puts filename
        File.open(filename, 'w+') { |f| f<< report.generate }
        puts `ls -la /tmp`
    rescue => e
        puts "#{e.class}: #{e.message}"
        puts e.backtrace
    end

    { statusCode: 200, body: JSON.generate('Hello Lambda! Thinreports for Ruby') }
end

ログの表示

JSON.parse event.body => {"type"=>"app.record.detail.show",
"appId"=>219,
"recordId"=>3,
"record"=>
{"レコード番号"=>{"type"=>"RECORD_NUMBER", "value"=>"3"},
"更新者"=>
{"type"=>"MODIFIER", "value"=>{"code"=>"aaaaa", "name"=>"アプリ管理者"}},
"作成者"=>{"type"=>"CREATOR", "value"=>{"code"=>"aaaaa", "name"=>"アプリ管理者"}},
"更新日時"=>{"type"=>"UPDATED_TIME", "value"=>"2019-10-26T05:01:00Z"},
"作成日時"=>{"type"=>"CREATED_TIME", "value"=>"2019-10-26T05:01:00Z"},
"text"=>{"type"=>"SINGLE_LINE_TEXT", "value"=>"/tmp に保存"},
"$revision"=>{"type"=>"__REVISION__", "value"=>"1"},
"$id"=>{"type"=>"__ID__", "value"=>"3"}}}
record => {"レコード番号"=>{"type"=>"RECORD_NUMBER", "value"=>"3"},
"更新者"=>{"type"=>"MODIFIER", "value"=>{"code"=>"aaaaa", "name"=>"アプリ管理者"}},
"作成者"=>{"type"=>"CREATOR", "value"=>{"code"=>"aaaaa", "name"=>"アプリ管理者"}},
"更新日時"=>{"type"=>"UPDATED_TIME", "value"=>"2019-10-26T05:01:00Z"},
"作成日時"=>{"type"=>"CREATED_TIME", "value"=>"2019-10-26T05:01:00Z"},
"text"=>{"type"=>"SINGLE_LINE_TEXT", "value"=>"/tmp に保存"},
"$revision"=>{"type"=>"__REVISION__", "value"=>"1"},
"$id"=>{"type"=>"__ID__", "value"=>"3"}}
$id =>
3
text =>
/tmp に保存
/tmp/put-hello-ruby-20191026143607.pdf
total 56
drwx------ 2 sbx_user1051 495 4096 Oct 26 05:36 .
dr-xr-xr-x 21 root root 4096 Sep 24 18:18 ..
-rw-rw-r-- 1 sbx_user1051 495 49084 Oct 26 05:36 put-hello-ruby-20191026143607.pdf

/tmp フォルダにPDFが出力されました。


あとがき

昨日(2019-10-25 Fri)、JAWS-UG浜松 というAWSのユーザー会に参加しました。
今回は参加された方がそれぞれ発表する会の様で、サーバーレスで顔認識するWebアプリの話(Vue,Lambda,Python,DynamoDB,Amplify,AppSync,KMS,OpenCVなど利用)。サーバーレスでブログを作った話(Vue,Python,Lambda,Cognitoなど)。AWSとラズパイで工場にMESを導入した話。などなどとても刺激になる話が聞けてモチベーションだけは高まりました。特にAWSとラズパイの話は興味深かったです。ラズパイを工場のラインに設置して工作機械からの信号をラズパイで受けて3G回線で[AWS IoT]へ送り、CloudWatch,Athena,Kinesis,QuickSightなどで分析してリアルタイムで工場のラインの出来高や稼働率をダッシュボードで表示。工場の異常はビデオで撮影。またデータはDynamoDBに保存。ラズパイを使ったハード回りやプログラミングも全て一人で行ったというのがすごかった。
CloudWathもBIとして使えるなど、AWSのサービスは知らないことが多いなと思いました。
今後も定期的にJAWS-UGに参加したいです。懇親会も楽しかった。

今後やりたいこと

下記のサービスを触ってみたいと思います。


参考リンク

AWS関連

Ruby関連

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3