Lambdaでselenium実行 / 空のHTMLがreturn|

Lambda(AWS) ✕ seleniumを利用して、下記の関数コードでスクレイピングを実行した。(Lambdaの設定詳細は割愛)

from selenium import webdriver
 import os
 os.environ['HOME'] = '/opt/'
 def lambda_handler(event, context):
     options = webdriver.ChromeOptions()
     options.add_argument("--headless")
     options.add_argument("--disable-gpu")
     options.add_argument("--hide-scrollbars")
     options.add_argument("--single-process")
     options.add_argument("--ignore-certificate-errors")
     options.add_argument("--window-size=880x996")
     options.add_argument("--no-sandbox")
     options.add_argument("--homedir=/tmp")
     options.binary_location = "/opt/headless/python/bin/headless-chromium"
     #headless-chromiumへのPATHは、ご自身の環境に応じて設定して下さい。

    browser = webdriver.Chrome("/opt/headless/python/bin/chromedriver", options=options)
    #choromedriverへのPATHは、ご自身の環境に応じて設定して下さい。

    browser.get("https://www.google.com/")
    title = browser.title
   
    print(title)
    print(browser.page_source)    
   
    browser.close()
    return 'ok'

すると、printしたかったtitleが空白。
page_sourceが下記のように空っぽのHTMLで返ってきた。

<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>

解決方法

optionに下記を追加して下さい。

options.add_argument('--disable-dev-shm-usage')

どうやら、Chromeがメモリ不足でクラッシュしてしまっているようです。(Lambdaの設定でメモリは十分に高くしていても関係ないようです。)

メモリ保持場所を/dev/shmの代わりに/tmpディレクトリを使うことができる設定です。

参考;

https://qiita.com/yoshi10321/items/8b7e6ed2c2c15c3344c6

追加したコード

from selenium import webdriver
 import os
 os.environ['HOME'] = '/opt/'
 def lambda_handler(event, context):
     options = webdriver.ChromeOptions()
     options.add_argument("--headless")
     options.add_argument("--disable-gpu")
     options.add_argument("--hide-scrollbars")
     options.add_argument("--single-process")
     options.add_argument("--ignore-certificate-errors")
     options.add_argument("--window-size=880x996")
     options.add_argument("--no-sandbox")
     options.add_argument("--homedir=/tmp")
     options.add_argument('--disable-dev-shm-usage')
     options.binary_location = "/opt/headless/python/bin/headless-chromium"
     #headless-chromiumへのPATHは、ご自身の環境に応じて設定して下さい。

    browser = webdriver.Chrome("/opt/headless/python/bin/chromedriver", options=options)
    #choromedriverへのPATHは、ご自身の環境に応じて設定して下さい。

    browser.get("https://www.google.com/")
    title = browser.title
   
    print(title)
    print(browser.page_source)    
   
    browser.close()
    return 'ok'

【最小労力】AWS Lambdaの関数で、S3へファイルアップロードする方法

Lambda → S3 へファイルアップロードをする方法をコンパクトにお伝えします。 連携できるを確認したい、通信方法を理解したい、といった場合には最小労力での動かし方を把握しましょう。

S3、Lambdaそれぞれのサービスを設定していきましょう。

①:S3で、ファイルアップロード先のbucketを作成

S3のbucket作成しましょう。
バケット名、AWSリージョンを設定して → bucket作成

S3のbucket設定

②-1:Lambdaの関数を作成

関数の作成 >> 1から作成 を選択します。

実行ロールの部分が重要です。この後、S3にアップロードする際にS3へのアクセス権限が必要です。

赤枠で囲んだ部分が、ロールです。
後ほど、このロールにS3へのファイルアップロード可能なアクセス権限を付与していきます。

②-2:Lambdaの関数を作成:ファイルアップロードを実行するコードソース

関数のコードソースに書きを反映します。
下記部分は環境に応じて変更して下さい。
bucket = '{S3で作成したアップロード先のbucket名}'

import json import urllib.parse import boto3 import datetime def lambda_handler(event, context): try: # Get the object from the event and show its content type s3 = boto3.resource(‘s3’) bucket = ‘{S3で作成したアップロード先のbucket名}’ key = ‘test_{}.txt’.format(datetime.datetime.now().strftime(‘%Y-%m-%d-%H-%M-%S’)) file_contents = ‘S3へファイルアップロード’ obj = s3.Object(bucket,key) obj.put( Body=file_contents ) except Exception as e: print(e) raise e

②-3:Lambdaのテストイベント設定

イベント JSON部分に下記で指定した、形式を反映して下さい。

JSON イベント形式

{ “Records”: [ { “eventVersion”: “2.0”, “eventSource”: “aws:s3”, “awsRegion”: “us-east-1”, “eventTime”: “1970-01-01T00:00:00.000Z”, “eventName”: “ObjectCreated:Put”, “userIdentity”: { “principalId”: “EXAMPLE” }, “requestParameters”: { “sourceIPAddress”: “127.0.0.1” }, “responseElements”: { “x-amz-request-id”: “EXAMPLE123456789”, “x-amz-id-2”: “EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH” }, “s3”: { “s3SchemaVersion”: “1.0”, “configurationId”: “testConfigRule”, “bucket”: { “name”: “example-bucket”, “ownerIdentity”: { “principalId”: “EXAMPLE” }, “arn”: “arn:aws:s3:::example-bucket” }, “object”: { “key”: “test%2Fkey”, “size”: 1024, “eTag”: “0123456789abcdef0123456789abcdef”, “sequencer”: “0A1B2C3D4E5F678901” } } } ] }

③:Lambdaの実行ロールにS3へのアクセス権限を付与

Lambdaの設定 >> 実行ロール へ。

先程作成した「実行ロール」が記載されています。ここからリンク遷移をして下さい。

IAM Mangerへ飛びます。

Lambdaの実行ロールとなっているロール(リンク遷移でこればそのままでOK)に対して、
アクセス許可を追加 >> ポリシーをアタッチ
から
AmazonS3FullAccess をアタッチして下さい。

④:テストで確認

Deploy → テスト を実行。
S3の指定したbucketへファイルがアップロードされています。

以上です。

今回紹介した、S3へのファイルアップロード方法だけでは、あまり使いみちがありませんが、トリガーなども組み合わせると、サーバーレスで、S3にファイル格納をすることが可能です。

EC2へPCからファイルアップロード|Linux/chmod/scp/ec2-user

タイトルの通り、EC2のディレクトリ内へローカルPCからファイルをアップロードする方法を解説します。

幾つかのSTEPで解説していきます。

①:AWS上の環境構築

ここは概ね省略します。

VPC、サブネット、セキュリティグループを設定。
サブネット上に、EC2インスタンスを起動させておく。

EC2には、sshで接続できる状態へ

②:ローカルPCからEC2へファイルアップロード(ディレクトリまるごと)

早速ですが、EC2へファイルアップロードするコマンドです。

scp -r -i ~{秘密鍵のディレクトリ}/{pemファイル名称.pem} ~{アップするローカルPCディレクトリ} ec2-user@{EC2のIPアドレス}:{EC2のアップディレクトリ}

上記のコマンドで、EC2にssh接続して、そのままディレクトリ丸ごとアップロードすることが出来ます。

※ -r のオプションは、ディレクトリ毎アップロード

ですが、アップロードするディレクトリによっては権限エラー(Permission denied)で、アップ出来ない場合があると思います。

原因は、アップロードしようとしたEC2内のディレクトリが、rootユーザーのみという場合です。

では、EC2内に入って、権限変更していきましょう。

③:EC2内のディレクトリで編集権限を変更

ディレクトリ編集 コマンド

sudo chmod {コマンドコード} {権限編集するディレクトリ} -R

※ -Rはオプションで、ディレクトリまるごとアップする場合に必要です。ファイルのみの場合は不要

アップロードする権限を付与する場合は、コマンドコード 777が良いと思います。

sudo chmod 777 {権限編集するディレクトリ} -R

で、権限を編集します。

②のコマンドを実行すると、アップロードが開始されます。

DBeaverを利用して、プライベートサブネットのRDSへ接続する方法

まず、DBeaverとは何か。といいますと。
開発者、データベース管理者、アナリスト、およびデータベースを操作する必要があるすべての人々のための無料のマルチプラットフォームデータベースツール となっております。

DBeaver上からデータベースに対して、アクセスして、データの参照やデータの操作をすることが出来るツールと考えて頂けたらいいと思います。 ターミナル上で、データを確認するのは操作性・可視性の観点でも難しいので、そこを解決してくれます。

さて、本題は、このDBeaverを活用して、AWSのプライベートサブネットに配置してあるRDSへアクセスすることです。

前提としては、RDSのセキュリティグループのインバウンドの条件をEC2からのmysqlアクセスのみしか受け付けていない。ということを前提にしていきます。

この場合、外部からRDSのエンドポイントにアクセスしようとしても、拒絶されてしまいます。

これを解決する方法があります。
sshポートフォワード といわれる手法です。

この辺が詳細に書いている部分だと思います。
https://qiita.com/lighttiger2505/items/ea33291639a8656d50b4

簡単にまとめてしまうと、
「一度、EC2にsshでアクセスして、そこからRDSにmysqlでアクセスする」という手法です。アクセスできるトンネルを作るといった表現をされたりもします。

これを、 DBeaver では簡単に出来てしまいます。

DBeaverで、 sshポートフォワード を実装する

DBeaverの「編集 接続 」で、sshタブを開きましょう。

「SSH Tunnnelを使用する」を選択する。

各項目を設定しますが
ssh接続は、EC2に対して、ssh接続できるためのものです。
なので、
・HOST/IP :EC2のパブリックIPアドレス
・User Name:EC2にログインするためのusername
・Private Key:設定した内容によりますが、pemファイルが置いてあるファイル場所を指定しております。

つづいて、 一般タブを開きましょう。

こちらは、RDSへ接続するための情報です。

・Server Host:RDSのエンドポイント
・Database:作成したDatabase名
・ユーザー名:Databaseへのアクセスが認められているユーザー名
・パスワード:パスワード

これで、Privateサブネット配下にある、RDSに対して、DBeaverを利用してアクセスすることが出来ます。

Laravelからs3に画像をUP~画像からUP編~

Laravelからs3へ画像ファイルをアップロードする情報はqiitaなどで沢山あるので、そちらを参考に頂けると良いと思います。

今回は、画像からs3へアップロードする方法を紹介します。
画像から??という方もいるかと思います。
一般的にs3のアップロードで使用する方法は、HTMLのFormからファイルをPostして、そのファイルデータ(画像が多いと思いますが)をs3へアップする方法だと思います。

この場合、 <画像>ではなくて<画像ファイル>に変換しております。

つまり、今回ご紹介するのは、
画像を一度画像ファイルへ変換して、s3にアップロードする方法です。

まずは、画像ファイルをそのままアップロードする、最小コードです。

$filename = Storage::disk('s3')->putFile('s3のファイルディレクトリ', $request->file('image'), 'public'); 

※下記実行済みが前提
・$request->file(‘image’)で、画像ファイルがpostされてくること
・.envの処理、composerでライブラリインストールなどは実行済み

上記と同じように画像をアップロードしようとします。

$filename = Storage::disk('s3')->putFile('s3のファイルディレクトリ', $image, 'public'); 

$imageは画像です。
こうすると下記のerrorが発生します。

 Command (HashName) is not available for driver (Gd). 

errorの内容は、
ファイルの形式が扱えない形式であるということです。

画像を、画像ファイル形式へ変換してあげます。

$filename = Storage::disk('s3')->putFile( 's3のファイルディレクトリ' , new File($save_path), 'public');  

$save_path は、storage上のpathです。
storageにある画像を指定しております。

AWSで作成したRDSの設定を.envの設定で接続/DB_HOST,DB_DATABASE…

AWSで作成したRDSを、Laravelの.envに正しく設定する方法を解説致します。

Laravelでローカルで使っている人でも、.envでデータベースの設定はしていると思います。
どんな設定項目があるでしょうか。

DB_CONNECTION=
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD= 

こちらを正しく設定していくことで、LaravelからRDSとの接続が可能です。
(通常のアーキテクトでは、Webサーバー(EC2)から、RDSへ接続をしていると思いますが)

DB_CONNECTION
 → RDSで選択した、RDSのアプリケーション
   mysqlを選択したので、mysql

DB_HOST
 → RDSで作成したエンドポイントを設定

DB_PORT
 → RDSで設定したPORT番号を設定します。
   DBサーバーの場合は、主に3306が設定されます。

DB_DATABASE
 → RDSで作成したDatabaseの名称を設定します。

DB_USERNAME
DB_PASSWORD
 → データベースに接続可能なユーザー、パスワードを設定します。
   RDS作成時にマスターユーザー名とパスワードを設定することになるので、そちらを設定してください。

以上です。

DockerでLaravel構築した後に、bootstrapを導入。エラー祭りを解決

Dockerを使って、Laravelを起動しています。
昔、xamppでLaravelでbootstrapした際は、すんなりいったと思うのですが、
今回は色々なエラーに苦しんだので、記録して残しておきます。

Dockerの環境は、docker-composeを利用して、
php, nginx, mysqlを別々のコンテナで起動しています。

ざっくりとした流れはこちらです。

・php へbashでアタッチする
・node.jsをインストール
・laravel/ui導入
・bootstrap導入
・npm を install & run

php へbashでアタッチする

まずは、Dockerコンテナのphpの中に入ります。
php のコンテナ名はphp としております。

docker-compose exec php bash

cd コマンドを使って、Laravelアプリケーションのルートディレクトリへいきます。
artisan , composer などが存在するディレクトリです。

node.jsをインストール

その階層で、下記コマンドを別々に打ち込んでいきます。

apt-get install -y gnupg
curl -sL https://deb.nodesource.com/setup_10.x | bash -
apt-get install -y nodejs

laravel/ui導入

 composer require laravel/ui 

bootstrap導入

 php artisan ui bootstrap 

npm を install & run

 npm install && npm run dev 

そこで、下記のようなエラーがでてきました。

npm WARN deprecated popper.js@1.16.1: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
 npm ERR! code ENOENT
 npm ERR! syscall rename
 npm ERR! path /work/app/node_modules/.vue-loader.DELETE/node_modules/source-map
 npm ERR! dest /work/app/node_modules/vue-loader/node_modules/source-map
 npm ERR! errno -2
 npm ERR! enoent ENOENT: no such file or directory, rename '/work/app/node_modules/.vue-loader.DELETE/node_modules/source-map' -> '/work/app/node_modules/vue-loader/node_modules/source-map'
 npm ERR! enoent This is related to npm not being able to find a file.
 npm ERR! enoent
 npm ERR! A complete log of this run can be found in:
 npm ERR!     /root/.npm/_logs/2020-10-06T03_43_21_673Z-debug.log

 こちらを見てみると、似たようなエラーが….

https://stackoverflow.com/questions/49651221/npm-enoent-no-such-file-or-directory-rename

 rm package-lock.json 

一度、package-lock.json を削除した後に
再度、

npm install && npm run dev 

これで、いけました!

【windows編】AWSのEC2へssh接続するコマンドを紹介

AWSにssh接続するコマンドを紹介します。
簡潔に下記がそのコマンドです。

ssh -i 公開鍵のファイル ec2-user@IPアドレス

公開鍵のファイル

公開鍵のファイル:ディレクトリの場所も指定する必要があります。
例えば、 \Users\Desktop\ssh\aws-and-infra-ssh-key.pem
※ \Users\Desktop\ssh まではディレクトリで、それ以降がファイル名です。

IPアドレス

EC2のインスタンスで登録されているIPアドレスです。
(※Elastic IP を関連付けている場合は、Elastic IP)
確認場所は、

EC2 > インスタンス > 使用するインスタンスを選択

そして、下部に存在するタブの中から「説明」を指定すると
「 IPv4 パブリック IP」という項目で、確認できます。