EC2のout of memoryエラー
Vue.jsとLaravelで作ったプロジェクトをAWSのEC2を使って公開していたのですが、
ある時アクセスしてみると504 Gateway-timeoutエラーで表示できなくなっていました。
AWSにログインしてインスタンスを確認してみると該当のインスタンスは
システム/インスタンス共にチェック失敗となっていました、、
システムログを確認してみます。そこでこんなエラーを見つけました。
Out of memory: Kill process 11606 (sshd) score 3 or sacrifice child
どうやらメモリ不足によりsshdがkillされてしまっているようです。
公式Docを見るとメモリ不足の発生のリスクを軽減する為の方法として下記が記載されていました。
アプリケーションを本番環境に移行する前に、
テストを実行してアプリケーションのメモリ要件を把握します。
アプリケーションは、十分なリソースがあるホストでのみ実行。
この2点について何も考えずに安いからという理由でt2.micro(メモリ1G)を選んだ結果
このようなことが起きてしまいました。反省です。
インスタンスを手動で再起動してSSH接続しfree -hコマンドでメモリを確認します。
free -h total used free shared buff/cache available Mem: 989M 802M 95M 824K 91M 70M
オプションの-hは human の頭文字で、人間が見やすいように、ちょうどよい単位で表示してくれます。
表示結果の見方はこうです。
total→メモリの合計
used→メモリの使用量
free→メモリの空き
shared→共有メモリに割り当てられたメモリ
buff/cache→バッファキャッシュに割り当てられたメモリ
available→スワップせずに使用できる量
空きが95Mとのことなのでなんらかのタイミングでメモリ使用率が100%に達してしまい
メモリ不足のエラーが発生した、ということでしょうか。
このメモリ不足を解決する為の解決策として2点が上げられていました。
+ インスタンスタイプをメモリ容量がもっと高いものにアップグレードする。
- swapファイルを作成する
インスタンスをアップグレードする方法が手っ取り早そうではあるのですがコストが高くなる可能性があるので
今回はswapファイルを作成して様子を見てみることにします。
swapファイルがわからない人の為の説明
スワップファイルとは、メモリの一部に代替されるハードディスク上の記憶領域のこと。
仮想メモリ(仮想記憶)とは、メインメモリが不足した際に、ハードディスクをメモリの一部に代替して利用するOSの機能のことを言う。
ハードディスク上に「スワップファイル」と呼ばれる専用の領域を用意し、メインメモリ容量が不足したら、ハードディスクの一部をメモリに見立てて使用する。
具体的には、メインメモリデータの中から、使われていないメモリ領域のデータを一時的にハードディスクに退避させ、必要が生じた際にメモリに書き戻す。
swapファイルの作成方法についてはこちらの公式Docの手順通りで作成できるのでそちらをご覧ください。
スワップファイルを使用して Amazon EC2 インスタンスのスワップ領域としてメモリを割り当てる
とりあえずはこれで様子を見ていこうと思います。
GeoDB Cities APIを使ってReactで都市の入力候補機能を実装する
GeoDB cities APIとは?
都市データベースです。都市、地域、国のデータを公開します。
今回はこちらのAPIを使って都市の入力候補機能を実装します。
作ったもの
こんな感じのシンプルな検索フォームです。
文字を入力するとそれに続く都市が出てきます。
早速やっていきましょう!
① API keyの取得
1. rapid API hubでアカウント作成
API Hub - Free Public & Open Rest APIs | RapidAPI
2. search for APIsで『GeoDB Cities』と検索
3. 無料プランをsubscribeします。
4. Endpoints→javaScript→fetchを選んでAPI keyを確認します。
copy codesをクリックして自分がわかりやすい場所にコピペしておきます。
② Reactアプリの準備
Create-react-appを使ってプロジェクトを作成します。
npx create-react-app react-weather-app cd react-weather-app
今回は検索フォームにreact-select-async-paginateを使用します。
執筆時(2022年7月)時点では、create-react-appで作ったReactのバージョンが18で、
react-select-async-paginateが18に完全対応していないので--forceオプションをつけてインストールしています。
npm i react-select-async-paginate --force
問題なく起動できるか確認します。
npm run start
③ コーディング
api.jsファイルを作成して先ほど確認したAPI keyとAPI URLをexportするようにします。
export const geoApioptions = { method: 'GET', headers: { 'X-RapidAPI-Key': '自分のAPIキー', 'X-RapidAPI-Host': 'wft-geo-db.p.rapidapi.com', }, }; export const GEO_API_URL = 'https://wft-geo-db.p.rapidapi.com/v1/geo';
メインのコードです。search.jsファイルを作成して下記を追加
import { useState } from 'react'; import { AsyncPaginate } from 'react-select-async-paginate'; import { GEO_API_URL, geoApioptions } from './api'; const Search = ({ onSearchChange }) => { const { search, setSearch } = useState(null); const handleOnChange = (searchData) => { setSearch(searchData); onSearchChange(searchData); }; const loadOptions = (inputValue) => { return fetch( `${GEO_API_URL}/cities?minPopulation=1000000&namePrefix=${inputValue}`, geoApioptions ) .then((response) => response.json()) .then((response) => { return { options: response.data.map((city) => { return { value: `${city.latitude} ${city.longitude}`, label: `${city.name}, ${city.countryCode}`, }; }), }; }) .catch((err) => console.error(err)); }; return ( <AsyncPaginate placeholder='Search for city' debounceTimeout={600} value={search} onChange={handleOnChange} loadOptions={loadOptions} /> ); }; export default Search;
`${GEO_API_URL}/cities?minPopulation=1000000&namePrefix=${inputValue}`, geoApioptions
citiesというのは都市の情報を持ってくるためのもの、
minPopulation=1000000
これは人口100万人以上の都市のみを表示させる為のオプションです。
namePrefix=${inputValue}で入力された文字を渡しています。
App.jsを下記に変更
import './App.css'; import Search from './search'; function App() { const handleOnSearchChange = () => { console.log(); }; return ( <div className='container'> <Search onSearchChange={handleOnSearchChange} /> </div> ); } export default App;
これでできました。
toFixed()メソッドとは
toFixedは小数点以下を指定した桁数にした「近似値」を求めるメソッドです。
toFixedメソッドは数値を文字列に変換します。
使い方
数値.toFixed([小数点の後の桁数])
例
let num = 12.345; console.log(num.toFixed()); 結果: 12.3 console.log(num.toFixed(1)); 結果: 12.3 console.log(num.toFixed(2)); >> 12.35
よくあるエラー
TypeError: 〇〇.toFixed is not a function
このエラーは○○が数値じゃない時に発生します。
数値に変換してみてください。
PHPでのエラー表示、デバックの仕方まとめ
PHPで勉強中、画面が真っ白!なんらかのエラーがあるのはわかるけど
エラーの内容がわからない。。なんて経験を思い出したのでまとめてみました。
ブラウザにエラーを表示する
.phpファイルに下記を追加すると手っ取り早くブラウザにエラーを表示できます。
ただしユーザーにもエラー内容が見えてしまうので使用するのは開発時のみにしてください。
<?php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); ?>
ini_set()
この関数はphp.iniというPHPの設定に関するファイルを上書きします。
通常このファイルの場所は下記をPHPファイルに追加することでブラウザから確認できます。
phpinfo();
phpinfo()関数は現在使用しているPHPの情報をブラウザに表示します。
Configuration File (php.ini) PathとLoaded Configuration Fileに書かれているパスがphp.iniの場所になります。
display_errors
ブラウザにエラーを表示するかどうかの設定です。1で表示、0で非表示にできます。
display_startup_errors
PHPの起動時に発生したエラーを表示する設定です。1で表示、0で非表示にできます。
error_reporting
発生したエラーをどのレベルまで表示するかどうかの設定です。E_ALLにすることで全てのエラーを表示する設定にできます。
error_reporting(0);→すべてのエラーを表示しない
変数の中身をブラウザに表示する
期待された値が入ってこない。処理がどこまで走っているかわからない。そういう時はvar_dump()関数が便利です。
下記の関数を見たい変数の後に追記することでブラウザからその変数に何が入っているかを確認することができます。
変数の中身を知ることはPHPの理解を深めるのにとても役立つのでいっぱい使ってください。
var_dump($表示させたい変数名)
ログファイルにエラーを出力する
下記を追加することでエラーをログファイル(この場合php.log)に出力することができます。
ini_set('log_errors', 'On'); ini_set('error_log', 'php.log');
log_errors
エラーログを出力するかどうかの設定です。Onで出力、Offで出力しない設定にできます。
error_log
エラーログを出力する場所の設定ができます。今回の場合プロジェクト上でphp.logというファイルを作成して
php.logという場所に出力する設定にしていますがパスを指定することで変更することができます。
エラーログの出力先の指定の他にもいろんな設定ができるので気になる方は↓をチェックしてみてください。
おまけ:エラー・デバックを表示させる関数
私がPHPを勉強していた時にやっていたエラーの表示、デバックの方法です!
作業しているディレクトリでphp.logファイルを作ることを忘れずに。
処理の流れが簡単に追えたり、SQLエラーを見るのに便利だったり非常に開発がはかどるので紹介します。
ini_set('log_errors', 'On'); ini_set('error_log', 'php.log'); //開発中のみtrue、それ以外はfalseにする $debug_flg = true; //デバッグログ用の関数 function debug($str) { global $debug_flg; if ($debug_flg === true) { error_log('デバッグ:' . $str); } else { return false; } }
使い方はこんな感じでなんらかの処理の後にdebug()に変数を入れてなにが入っているか確認したり。
debug('SessionID:' . session_id()); debug('SessionIDの配列の中身:' . print_r($_SESSION, true));
※もし配列の中身を表示したいときはprint_r($配列の変数名,true)にしないと見れません。
SQLもこんな感じで書けばエラーがあった時に出力してくれてわかりやすいです。
function userInfoAll($user_id) { debug('userInfo関数を呼び出します'); try { $dbh = dbConnect(); //これはDBに接続する為の自作関数です $sql = 'SELECT id, user_role, nickname, email, password, family_name, given_name, age, job, phone, zip, address, comment, pic, create_date, login_time, update_date FROM users WHERE id = :user_id AND delete_flg = 0'; $data = array( ':user_id' => $user_id ); $stmt = queryPost($dbh, $sql, $data); if ($stmt) { $stmt = $stmt->fetch(PDO::FETCH_ASSOC); return $stmt; } else { debug('クエリ失敗'); debug('SQLエラー' . print_r($stmt->errorInfo(), true)); return false; } } catch (Exception $e) { debug('userInfoAll関数でエラー発生:' . $e->getMessage()); } }
Herokuでsessionを使う
勉強の為にPHPで作ったWebサービスをHerokuにデプロイした後
このようなエラーが発生。
session_start(): open(C:/MAMP/bin/php/var/sess_5c0c3cbde4aisjkq6i8arsuaq1bjtabf, O_RDWR) failed: No such file or directory (2) in /app/function.php on line 30 Warning: session_start(): Failed to read session data: files (path: C:/MAMP/bin/php/var) in /app/function.php on line 30 Warning: session_regenerate_id(): Cannot regenerate session id - session is not active in /app/function.php on line 31
問題となっている部分のコードはこちら。
session_save_path("C:/MAMP/bin/php/var"); ini_set('session.gc_maxlifetime', 60 * 60 * 24 * 30); ini_set('session_cookie_lifetime', 60 * 60 * 24 * 30); session_start(); session_regenerate_id();
当たり前だと言われそうですがセッションの情報を自分のPC上のファイルに保存していた為、
セッションを保存するファイルが見つからないから
セッションスタートできないしセッションIDを生成できないよ!と言われています。
セッションの保存方法はMemcacheやRedisに保存したり、
MySQLなどのリレーショナルデータベースに保存したりいろいろありますが
今回はHerokuのサイトにやり方が載っていたのでHerokuのMemcachierアドオンを入れて
HerokuからMemcachedを動かせるようにします。
MemCachier | Heroku Dev Center
アドオンをインストールします。アドオンは無料で使えますが
このアドオンを入れる前にHerokuにクレジットカードの登録が必要です。
heroku addons:create memcachier:dev
実行したら、
heroku config
でインストールできたか確認します。
...
MEMCACHIER_SERVERS => mcX.ec2.memcachier.com
MEMCACHIER_USERNAME => bobslob
MEMCACHIER_PASSWORD => l0nGr4ndoMstr1Ngo5strang3CHaR4cteRS
...
↑の例のように3つの変数が表示されていればOKです。
ここから先はcomposerが入っていることが前提になります。
デプロイしたディレクトリを開きターミナル上でcomposer.json
{ "require": { "php": ">=5.3.2", "ext-memcached": "*" } }
を追記し
composer uodate
を実行します。
このようなエラーが発生しました。
Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires PHP extension ext-memcached * but it is missing from your system. Install or enable PHP's memcached extension.
「あなたのPCにmemcachedの拡張機能がないからインストールするか、
使えるようにしてね」と言われています。
サイトを見てみるとphp-memcachedというものをインストールする必要がありそうなので
yumを使ってインストールしていきます。
yum install -y php74-php-pecl-memcached
php74の部分はPHP7.4以外をお使いの方はお使いのPHPに書き換えてください。
例えばPHP8.0をお使いであればphp80となります。
オプションの-yはインストールしますか?[YN]の部分をスキップするためのものです。
memcachedのdの付け忘れに注意。memcacheという別のPHPライブラリをインストールしてしまいます!
(こういうことはあるあるですがややこしい名前をつけるのはやめてほしいですよね。
実際stack overflowなどを見ているといろんな人が間違えているようでした。)
次にphp.iniに以下をわかりやすい場所に追記します。
私の場合、php.iniは/etc/php.iniというパスでありました。
php.iniを編集する時は必ずcpでバックアップをとりましょう。
extension=memcached.so
php -m
でエラーなくmemchachedが入っていればOkです。
php-memcachedをインストールできたので
composer update
で今度は問題なく実行できました。
.user.iniファイルを作成します。
touch .user.ini
作成した.user.iniに下記を追記します。
私の場合PHP7系を使用してるのでmemcached.sess_binary=1 # for ext-memcached 2 / PHP 5はコメントアウトしました。
session.save_handler=memcached session.save_path="${MEMCACHIER_SERVERS}" memcached.sess_binary=1 # for ext-memcached 2 / PHP 5 memcached.sess_binary_protocol=1 # for ext-memcached 3 / PHP 7 memcached.sess_sasl_username="${MEMCACHIER_USERNAME}" memcached.sess_sasl_password="${MEMCACHIER_PASSWORD}"
再度これでデプロイしてみるとセッションのエラーが消えてました!
Windows版!VirtualBoxの仮想マシンの容量を増やす方法
ある日VirtualBoxを使ってCentOs7にheroku CLIをインストールしようとしたらこんな表示が、、
Error: No space left on device
ターミナルからdf -hを使ってスペースの使用率を見てみるとなんと、とある場所のuse%が100%になっていました。
こうなると何もインストールできないのでまずは仮想マシンの容量を増やさなければなりません。
簡単にできるだろうと思っていたのですがなんやかんやエラーが出て大変だったのでここに残して置きます😉
環境:
VM Box6.1
ホストOS:Windows10, ゲストOS:CentOs7
~仮想マシンの容量を増やす方法~
①VM BOXを起動。ホーム上部の「ツール」の右側の三本線 → メディア → プロパティ をクリック
②対象のゲストOSを選択、画面下部にある拡張後の容量を入力し、適用をクリック
以上。これで簡単にできるはずですが、私の場合このようなエラーが出ました。
エラー内容:
Resizing to new size 85899345920 is not yet supported for medium 'C:\Users\miii0\VirtualBox VMs\CentOs\CentOs.vdi'.
仮想ハードディスクを固定サイズで作成したにもかかわらずサイズの変更をしようとしていることが原因でエラーになっている様です。このエラーは元の仮想マシンのクローンを作ってあげると解決します。
クローンの作成にはVMManageのclonehdというものを使います。
コマンドプロンプトを開きます。
Windowsの場合、VMManageはC:\Program Files\Oracle\VirtualBox\VBoxManage.exeというパスであると思うのでVirtualBoxフォルダーまで移動します。
cd C:\Program Files\Oracle\VirtualBox\
先ほど②で使った画面に仮想ディスクのパスが書いてあるのでそれをコピーしましょう。
VBoxManage.exe clonehd "仮想ディスクのパス" "新しい仮想ディスクのパス" --format VDI
私の場合、こんな感じです。
VBoxManage.exe clonehd "C:\Users\miii0\VirtualBox VMs\CentOs\CentOs.vdi" "C:\Users\miii0\VirtualBox VMs\CentOs\CentOsCloned.vdi" --format VDI
もしお使いの仮想ディスクの形式がvmdk形式の場合、
リサイズに対応しておらずvdi形式でないと容量を増やすことができないので一度vdi形式にする必要があります。
vmdkとvdiの違いはこちらに詳しく載せている方がいるので気になる方はどうぞ!
https://techtoge.com/difference-virtual-disk-formats/
このような表示になっていればOKです
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone medium created in format 'VDI'. UUID: *********************
こうすることで、新しい元のハードディスクのデータを含んだ、「可変サイズ」の新しいハードディスクが作成されます。
新しく作ったディスクは上記~仮想マシンの容量の増やし方~①→②の手順で容量を増やせます。
容量が増やせたら、ストレージを新しく作ったハードディスクに乗り換えます。
①今お使いのハードディスクを右クリック→設定
②ストレージ→使用中の仮想ディスクを選択
③ハードディスク(D)の右にあるアイコンをクリック→仮想ディスクの選択から新しくクローンしたディスクをクリックして選択
次は実際の中身(ゲストOS)の容量を拡張します。
ゲストOSのパーティション拡張、これについてはこちらの記事
仮想マシンのディスク拡張方法(CentOS7) | クラウドのエスエスアイ・ラボ
を参考にやっていったら問題なくできました。
以上仮想マシンの容量を増やす方法でした😊