- 投稿日:2020-11-30T22:52:37+09:00
MySQLのEXPLAINをかいつまんで見る
概要
MySQLのEXPLAINを読むと良いぞ。
というか読まなきゃダメな時がいつかくる。各項目の説明についてはこの記事がよくまとまってる。(参考にさせていただきました)
MySQL EXPLAINのそれぞれの項目についての覚書なんでEXPLAINが大事なの?
EXPLAINは「実行計画」を表示してくれる。どういう順序で、どういう方法でデータを取ってくるか表示してくれるので、クエリのパフォーマンスをチェックすることができる。
スロークエリが発生していたら必ずEXPLAINする癖をつけよう。発生していなくても、自信がなかったらEXPLAINしよう。RDBMSによって出し方や出力が異なるので注意。
今回はMySQLの話。そもそも遅くなる要員(よく見かけるやつら)
EXPLAINを見る前に、SQLがなぜ遅くなるのか把握してきたい。
インデックスを使っていない・設定していない
言わずもがな。フルテーブルスキャンって感じ。
O(n)の計算量。インデックスを使えばO(logn)。相関サブクエリ
二重ループが走ることになる。外側のクエリでヒットしたデータ一件ずつに対してサブクエリが実行される。
O(nm)の計算量のイメージ。filesort
ソートにクイックソートを使う。O(nlogn)。
インデックスを使ったソートの方が圧倒的に早い(というかインデックスつきのカラムは実質ソートされた状態でデータが保存されている)。temporary
一時テーブルを使ってソートする。filesortはオンメモリで行うが、データ量が大きくなった場合は一時テーブルを作って、そこからselectする形をとる。
I/Oが重くなるだけでなくテーブルを一回作って書き込むわけだからそりゃ重い。インデックスが有効活用できない
実質O(n)の計算量。フルインデックススキャンとかその辺。結局O(n)になる
例えば10000件データが入っているテーブルを検索して9999件ヒットする絞り込み条件を選択した場合、インデックスが設定されているカラムで条件を指定していたとしてもほとんど意味がない。見るべきところ
上で述べたことが、EXPLAINのどこに現れるのか
select_type
DEPENDENT SUBQUERY
に注意。相関サブクエリを意味する。
外側のクエリで十分絞り込めていれば遅くはならない。type
index
、ALL
に注意。
フルインデックススキャン、フルテーブルスキャンになり、インデックスが有効活用できていない。key
null
に注意。
インデックスが使われていないことを意味する。
ここがnull
の場合は possible_keys を見ると良い。利用可能なインデックスを探す。
ただしインデックスが設定してあっても利用されないことがあるので注意。rows
数字が大きいと注意
テーブルから取得する予想件数が多いことになるので、単純に n が大きくなる。I/Oも辛くなる。
特にDEPENDENT SUBQUERY
やfilesort
、temporary
との合わせ技が発揮されると一瞬で死ぬ。filteredの数字が小さいとまだ救われる(filteredは絞り込まれる割合を百分率で表す)
extra
Using filesort
、Using temporary
に注意。まとめ
EXPLAINの結果をしっかり見て、ボトルネックを探そう。
- 投稿日:2020-11-30T18:09:53+09:00
閉包表で子孫から祖先を参照
概要
組織図のようなツリー構造のデータがあったときに、子孫から祖先を参照するのに閉包表という方法を知ったので、それの備忘録
今回のケース
所属1 (1) ┠ グループ1 (2) ┃ ┠ チーム1 (5) ┃ ┠ チーム2 (6) ┃ ┝ グループ2 (3) ┃ ┠ チーム3 (7) ┃ ┝ グループ3 (4)みたいな組織図があった時に子孫から祖先を参照して、データを取得したい
テーブルとデータ作成
テーブル
CREATE TABLE IF NOT EXISTS departments ( id INTEGER NOT NULL UNIQUE PRIMARY KEY, name TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS department_paths ( ancestor INTEGER NOT NULL REFERENCES entries (id), descendant INTEGER NOT NULL REFERENCES entries (id), depth INTEGER NOT NULL CHECK (depth >= 0), PRIMARY KEY (descendant ASC, ancestor ASC) );データ
INSERT INTO departments (id, name) VALUES (1, 'Department 1'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (1, 1, 0); INSERT INTO departments (id, name) VALUES (2, 'Group 1'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (2, 1, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (2, 2, 0); INSERT INTO departments (id, name) VALUES (3, 'Group 2'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (3, 1, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (3, 3, 0); INSERT INTO departments (id, name) VALUES (4, 'Group 3'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (4, 1, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (4, 4, 0); INSERT INTO departments (id, name) VALUES (5, 'Team 1'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (5, 1, 2); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (5, 2, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (5, 5, 0); INSERT INTO departments (id, name) VALUES (6, 'Team 2'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (6, 1, 2); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (6, 2, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (6, 6, 0); INSERT INTO departments (id, name) VALUES (7, 'Team 3'); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (7, 1, 2); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (7, 3, 1); INSERT INTO department_paths (descendant, ancestor, depth) VALUES (7, 7, 0);データの取得
各departmentsから見た階層の深さと経路
SELECT id, name, MAX(depth) AS depth, GROUP_CONCAT(ancestor) AS path FROM ( /* 各departmentsから見た親子関係をリストアップ */ SELECT id, name, descendant, ancestor, depth FROM departments JOIN department_paths ON descendant = id ORDER BY depth DESC ) dept_deptpath GROUP BY descendant ORDER BY path;結果
id name depth path 1 Department 1 0 1 2 Group 1 1 1,2 5 Team 1 2 1,2,5 6 Team 2 2 1,2,6 3 Group 2 1 1,3 7 Team 3 3 1,3,7 4 Group 3 1 1,4 経路は取れたけど、1行に祖先、親、子供にデータを取りたい
親子関係を行にまとめて取得
SELECT dept.id, dept.name as child_department, parent.name as parent_department, grand_parent.name as grand_parent_department, MAX(depth) as depth FROM departments dept LEFT OUTER JOIN departments AS parent ON parent.id = ( SELECT ancestor FROM department_paths WHERE descendant = dept.id AND depth = 1) LEFT OUTER JOIN departments AS grand_parent ON grand_parent.id = ( SELECT ancestor FROM department_paths WHERE descendant = dept.id AND depth = 2) JOIN department_paths deptpath ON deptpath.descendant = dept.id GROUP BY dept.id, parent.id, grand_parent.id結果
id child parent grand_parent depth 1 Department 1 NULL NULL 0 2 Group 1 Department 1 NULL 1 3 Group 2 Department 1 NULL 1 4 Group 3 Department 1 NULL 1 5 Team 1 Group 1 Department 1 2 6 Team 2 Group 1 Department 1 2 7 Team 3 Group 2 Department 1 2 仕様で三階層まで取れればいいので、今回はこれにしたけれど、深さに左右されない書き方があれば知りたい
おまけ: 子孫から見た祖先だけ取りたい
SELECT descendant, dept.name as department FROM department_paths JOIN departments dept ON dept.id = ancestor /* 祖先は子孫としては1レコードしかないことを利用 */ WHERE ancestor IN ( SELECT id FROM departments WHERE 1 = ( SELECT COUNT(*) FROM department_paths WHERE descendant = id ) )
- 投稿日:2020-11-30T14:23:50+09:00
not existsを扱う
以下のテーブルがある。
select * from testscores; +------------+---------+-------+ | student_id | subject | score | +------------+---------+-------+ | 100 | 国語 | 80 | | 100 | 理科 | 80 | | 100 | 算数 | 100 | | 200 | 国語 | 95 | | 200 | 算数 | 80 | | 300 | 国語 | 90 | | 300 | 社会 | 55 | | 300 | 算数 | 40 | | 400 | 算数 | 80 | +------------+---------+-------+全ての教科で50点以上取っている生徒を取得する。
select distinct student_id from testscores ts1 where not exists ( select * from testscores ts2 where ts1.student_id = ts2.student_id and ts2.score < 50 ) +------------+ | student_id | +------------+ | 100 | | 200 | | 400 | +------------+次は算数が80点以上、国語が50点以上の生徒を取得する。
select distinct student_id from testscores ts1 where subject in('算数','国語') and not exists ( select * from testscores ts2 where ts1.student_id = ts2.student_id and 1 = case when subject = '算数' and score < 80 then 1 when subject = '国語' and score < 50 then 1 else 0 end ) +------------+ | student_id | +------------+ | 100 | | 200 | | 400 | +------------+上記のSQLでは、国語のデータが存在しないstudent_id400の生徒が取得されてしまっている。
それを除外したい場合、行数を数えるhaving句を追加すればいい。select student_id from testscores ts1 where subject in('算数','国語') and not exists ( select * from testscores ts2 where ts1.student_id = ts2.student_id and 1 = case when subject = '算数' and score < 80 then 1 when subject = '国語' and score < 50 then 1 else 0 end ) group by student_id having count(*) = 2 +------------+ | student_id | +------------+ | 100 | | 200 | +------------+こちらを参考にさせていただきました。
達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ
- 投稿日:2020-11-30T13:30:15+09:00
テーブルに存在しないデータを探す
以下のテーブルがある
select * from meetings; +-----------+--------+ | meeting | person | +-----------+--------+ | 第1回 | 伊藤 | | 第1回 | 坂東 | | 第1回 | 水島 | | 第2回 | 伊藤 | | 第2回 | 宮田 | | 第3回 | 坂東 | | 第3回 | 宮田 | | 第3回 | 水島 | +-----------+--------+meetingに出席しなかったpersonを取得する。
方法として、全員が解禁したと仮定した場合の集合を作り、
そこから現実に出席した人々を引き算する。全員が解禁した場合の集合はクロス結合で作れる。
select distinct m1.meeting ,m2.person from meetings m1 cross join meetings m2 +-----------+--------+ | meeting | person | +-----------+--------+ | 第1回 | 伊藤 | | 第2回 | 伊藤 | | 第3回 | 伊藤 | | 第1回 | 坂東 | | 第2回 | 坂東 | | 第3回 | 坂東 | | 第1回 | 水島 | | 第2回 | 水島 | | 第3回 | 水島 | | 第1回 | 宮田 | | 第2回 | 宮田 | | 第3回 | 宮田 | +-----------+--------+上記から実際の出席者の集合であるmeetingsテーブルに存在しない組み合わせに限定する。
select distinct m1.meeting ,m2.person from meetings m1 cross join meetings m2 where not exists ( select * from meetings m3 where m1.meeting = m3.meeting and m2.person = m3.person ) order by m1.meeting +-----------+--------+ | meeting | person | +-----------+--------+ | 第1回 | 宮田 | | 第2回 | 坂東 | | 第2回 | 水島 | | 第3回 | 伊藤 | +-----------+--------+こちらを参考にさせていただきました。
達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ
- 投稿日:2020-11-30T12:33:35+09:00
MySQL DBとテーブルとカラムの作成方法
目的
- ターミナルからMySQLにログインしてDB(データベース)とテーブル、テーブル内のカラムを作成する方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提情報
- 筆者はMacに直接MySQLを導入しターミナルからMySQLにログインして本記事の内容を検証した。
- MySQLへのログインはrootユーザを使用する。(MySQLのrootユーザのパスワードが分からなくなってしまった方はこちら→[Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法](https://qiita.com/miriwo/items/1880e9d2ebcfd3c0e60d)
今回作成するDBとテーブル、カラムの詳細を下記に記載する。
- DB名: create_test
- テーブル名: users
カラム
カラム名 データ型 id int name varchar(255) 概要
- 準備
- DBの作成
- テーブルとカラムの作成
- 確認
詳細
準備
下記コマンドを実行してMySQLにログインする。
$ mysql -u root -pDBの作成
MySQLにログイン後下記SQLを実行してDBを作成する。(
create database DB名
とするとDBを作成する事ができる。)create database create_test;下記SQLを実行して只今作成したDBを選択する。
use create_test;テーブルとカラムの作成
下記SQLを実行してテーブルとカラムを同時に作成する。
create table users( id int, name varchar(255) );確認
下記SQLを実行して出力されたテーブル名一覧にusersテーブルがあることを確認する。
show tables;下記SQLを実行して出力されたusersテーブルのカラムデータが前提情報に記載したものと一致しているか確認する。
show columns from users;
- 投稿日:2020-11-30T10:52:14+09:00
ローカルでMySQLコンテナに大量INSERTしていたら"unexpected EOF"になった時の話
はじめに
データ削除バッチのベンチマークを取るため、ローカルのMySQLコンテナにデータを800万レコードほどINSERTしていたところ、
packets.go:36: unexpected EOF
というエラーに遭遇しました。その時に調べたことや対処したことなどをまとめます。環境:
- Go 1.15.2
- go-sql-driver/mysql v1.4.1
- MySQL 5.7.30
対処と原因
MaxLifetimeを設定する
packets.go:36: unexpected EOF
でぐぐってみると、結構ヒットしたので比較的よくある現象のようです。それらによると、SetConnMaxLifetime
を設定すれば解消するとのことでした。
SetConnMaxLifetime
に関してはこちらの記事で説明されています。その名の通り、接続の寿命を設定するもののようです。
Re: Configuring sql.DB for Better PerformanceMySQL では wait_timeout という設定で接続がサーバーから切られる恐れがあります。また、OSやルーターが長時間利用されていないTCP接続を切断することもあります。どのケースでも、 go-sql-driver/mysql はクエリを送信した後、レスポンスを受信しようとして初めてTCPが切断されたことを知ります。切断を検知するのに何十秒もかかるかもしれませんし、送信したクエリが実行されたかどうかを知ることもできないので安全なリトライもできません。
こういった危険をなるべく避けるためには、長時間使われていなかった接続を再利用せずに切断し、新しい接続を使うべきです。 SetConnMaxLifetime() は接続の寿命を設定するAPIですが、寿命を10秒に設定しておけば、10秒使われていなかった接続を再利用することもありません。引数に与える秒数に関しては、
SetConnMaxLifetime() は最大接続数 × 1秒 程度に設定する。多くの環境で1秒に1回接続する程度の負荷は問題にならない。
とあったため、とりあえず1秒を設定しました。
つまり、こんな感じの実装です。
db.SetConnMaxLifetime(time.Second)これにより、エラーは発生しなくなりました!!
なぜこの設定で問題解消されるのか
MaxLifetime
の値はデフォルトでは無制限です。
一見すると、コネクションはできるだけ使いまわした方が再接続のオーバーヘッドも少なく、良いように思われるのですが、接続時間が長すぎることのデメリットも多くあるようです。いくつかの記事でそのことが説明されていますが、例えば、こちらの記事ではこのように説明されていました。コネクション回数が減る代わりにアイドルのコネクションが必要なくなってもキープし続ける負荷があったり、DB起因でコネクションが中断されたり、フェイルオーバしたりした際に、外部要因で既に切れたコネクションを持ち続けて切断の検知に時間がかかったり、新しいコネクションに切り替えて欲しい際に切り替えがなかなか起こらないというリスクを含んでしまいます
つまり、今回は「大量INSERT中になにかしらのDBコンテナ側の起因でコネクションが中断された」と推測されます。
DB側でコネクションが中断されても、GoクライアントはFIN通知を受け取るわけではないのでそのコネクションは生きていると判断します。Goクライアントからクエリパケットを送る際に初めてパケット喪失をして気づき、unexpected EOF
のエラーを受け取る格好になります。
このようにならない為に、Goクライアント側でMaxLifeTime
の設定をし、Goクライアント側で接続を1秒毎に切っておく対処を追加することで解消されるのかと。なぜMySQLでエラーが発生したのか
ここまででは、
unexpected EOF
が発生する理由と解決方法はわかったのですが、そもそもなぜMySQLでエラーが発生してしまったのかという根本原因が分かっていません。MySQLコンテナ内のログを確認してみると、
ibuf cursor restoration fails
というエラーログがでていることが分かりました。これ自体の意味はよく分かっていないのですが、 MySQL5.7.31でfixされたようです。
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-31.html最終的な解決手段
そこで、当時最新であったMySQL5.7.32にアップデート(ローカルにある5.7のimagreをいったんrmして、再起動)して、
SetConnMaxLifetime
の実装を削除して、無事動作することを確認しました。
つまり、バージョン上げるだけで解決しました。これから
本番環境やステージング環境ではAWSのAuroraを使っており、Auroraでも同様の問題が発生してしまうのかの確認が必要です。
それはこれからの話なので、進展ありましたら、記載します。
- 投稿日:2020-11-30T08:48:55+09:00
Cloud9 × Rails × Docker × MySQL で環境構築
はじめに
cloud9を使ったDockerの環境構築の記事が少なく戸惑ったこと、自身のアウトプットのために執筆します。
別の記事でも記載していますが、現在転職活動中の身ですのでプロではありません。
間違いなどありましたらご指摘いただければ、自身でも調べ速やかに修正いたします。参考記事:
丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)ファイル内容は上記サイトから頂きましたが、port8080に対応するため一部編集を加えています。
目標
タイトルの構成で環境構築をし、「Yay! You’re on Rails!」を表示させる。
環境
Amazon linux
Ruby 2.6.3
Rails 5.2.4.4
MySQL 5.5.62
Docker 19.03.6
Docker-compose 1.27.4やってきましょう
インスタンスのボリュームを増やす
cloud9を立ち上げてできるインスタンスはサイズが10GBとなっているので、容量が不足する可能性があります。
その都度不要ファイルを削除できればいいのですが、手っ取り早くコンソールから容量を増やしました。
(極少額の課金が発生するかもしれません、料金はご自身で調べてみてください)まずはcloud9を立ち上げます。
その後EC2の画面に行くと、cloud9と同時に立ち上がったインスタンスがあります。EC2のサイドメニューから下記の順番で選択します。
1.ボリューム
2.先ほど立ち上がったインスタンスのチェックボックス
3.アクション
4.ボリュームの変更インスタンスの状態がoptimizing(黄色のマーク)なので、10分程度放置し終わるのを待ちます。
状態が緑のマークになると、サイズも16GBとなり反映が確認できます。
cloud9のターミナルに移動します。
以下のコマンドで内部からもサイズ変更が反映されているかを確認します。$ df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 483M 60K 483M 1% /dev tmpfs 493M 0 493M 0% /dev/shm /dev/xvda1 16G 8.6G 7.0G 55% /もしリロードしても反映されていないようなら次のコマンドを入力します。
$ sudo growpart /dev/xvda 1 $ sudo resize2fs /dev/xvda1Docker-Composeのインストール
インストールの前に このページ で最新のバージョンを確認しておいてください。
下の
1.27.4
の箇所を確認した最新のバージョンに書き換えてインストールします。
私は"/home/ec2-user/environment"ディレクトリで実行しています。$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ docker-compose
コマンドを実行できるように権限を与えます。$ sudo chmod +x /usr/local/bin/docker-composeバージョンの確認をして、表示されればインストールは完了です。
$ docker-compose --version docker-compose version 1.27.4, build 40524192ファイルの準備
プロジェクトディレクトリを作成して、その中に移動します。
$ mkdir myApp $ cd myAppそうしたら必要なファイルを
touch
コマンドで作成していきます。
ここでファイルは4つ用意します。$ touch Dockerfile $ touch Gemfile $ touch Gemfile.lock $ touch docker-compose.yml通常は
vi
コマンドなどでファイルを書いていきますが、cloud9はせっかくエディタが付いているのでそちらで作業しましょう。なお
Gemfile.lock
は記載せずに空のままで大丈夫です。各ファイルの項目は Docker × Ruby on Rails × MySQLの環境構築 この記事を参考にしてください。
DockerfileFROM ruby:2.6.3 #適したバージョンを指定 RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs RUN mkdir /app_name ENV APP_ROOT /app_name WORKDIR $APP_ROOT ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock RUN bundle install ADD . $APP_ROOTFROM ruby:2.6.3
cloud9にはあらかじめrubyがインストールされています。
以前同様の環境構築をした際、インストール済みのバージョンとここで指定したバージョンが異なりエラーになりました。(原因は他にあるのかもしれませんが、2つのバージョンが競合したのか?)その為ここのrubyのバージョンは
$ ruby -v
で確認したインストール済みのバージョンと同様にしてあります。Gemfilesource 'https://rubygems.org' gem 'rails', '5.2.4.4' #適したバージョンを指定rails5系で現在最新のバージョンを指定。Ruby on Rails最新情報
docker-compose.ymlversion: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: パスワード MYSQL_DATABASE: データベース名 ports: - "3306:3306" web: build: . command: rails s -p 8080 -b '0.0.0.0' volumes: - .:/app_name ports: - "8080:8080" links: - dbcommand: rails s -p 8080 -b '0.0.0.0'
ports:
- "8080:8080"cloud9ではport8080でサーバーに接続します。
その為、ホスト:コンテナ の両方を8080番で指定しています。パスワードとデータベース名は任意の値を記載します。
これで設定を記載した3つのファイルと、空のGemfile.lockができました。
Railsプロジェクト作成
カレントディレクトリがプロジェクトディレクトリ(今回は
/home/ec2-user/environment/myApp
)であることを確認します。そうしたら
rails new
を実行してアプリケーションの型を作ります。$ docker-compose run web rails new . --force --database=mysql --skip-bundle問題がなければプロジェクトディレクトリ配下にRailsのひな形が出来上がります。
$ ls app bin config config.ru db docker-compose.yml Dockerfile Gemfile Gemfile.lock lib log package.json public Rakefile README.md storage test tmp vendorデータベース周りの設定のため
/myApp/config/database.yml
を修正します。database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: パスワード host: dbパスワードの箇所には先ほど"docker-compose.yml"で設定したのと同様の値を記載します。
ここで保存しようとすると、恐らくcloud9の上部に警告文が表示されます。EACCES: permission denied
これは
database.yml
に対する書き込み権限が無いので保存できない旨の内容なので、権限を変更指定保存できるようにします。/myApp/configに入り、ファイルの権限を確認します。
$ cd config $ ls -l -rw-r--r-- 1 root root 1657 Nov 29 19:06 database.yml書き込み権限を変更し、反映されれば
database.yml
を保存できるようになります。$ sudo chmod 666 database.yml $ ls -l -rw-rw-rw- 1 root root 1657 Nov 29 19:06 database.ymlDockerコンテナのビルド
プロジェクトディレクトリに戻り、コンテナを立ち上げます。
$ docker-compose buildPermissionError: [Errno 13] Permission denied: '/home/ec2-user/environment/myApp/config/master.key
このエラーが出るのは先ほど同様
myApp/config/master.key
に対するパーミッションエラーなので、権限を変更します。ディレクトリを移動して権限を確認。
$ cd config $ls -l -rw------- 1 root root 32 Nov 29 19:06 master.key権限を変更して反映を確認します。
$ sudo chmod 666 master.key $ ls -l -rw-rw-rw- 1 root root 32 Nov 29 19:06 master.key再トライ
$ docker-compose build $ docker-compose upこれで私はうまくいきました。
データベースの作成
コンテナが起動したらショートカットキー
Alt + T
でcloud9にターミナルをもう一つ立ち上げます。開いたターミナルからプロジェクトファイルに移動して、データベースを立ち上げます。
$ cd myApp $ docker-compose run web rails db:create成功していれば Preview → Preview Running Application で「Yay! You’re on Rails!」が表示されます。
この時**********.vfs.cloud9.ap-northeast-1.amazonaws.com で接続が拒否されました。
と表示されたら、右上の矢印ボタンを押して別タブで開けば表示されます。
サーバーを停止させる場合は
Ctrl + C
ではなく、新しく開いた方のターミナルで下記を入力します。$ docker-compose downその他のエラー
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
database.ymlのhostの変更を忘れていると出ます。
Can't connect to MySQL server on 'db' (115)
コンテナが立ち上がり切っていない時に表示されます。
数秒待ってからリロードすれば表示内容が変わります。Unknown database 'app_name_development'
データベースを作成していないとこの表示がされます。
新しいターミナルを開いてデータベースを作成すれば解消されます。
- 投稿日:2020-11-30T08:48:55+09:00
Cloud9 × Raisl × Docker × MySQL で環境構築
はじめに
cloud9を使ったDockerの環境構築の記事が少なく戸惑ったこと、自身のアウトプットのために執筆します。
別の記事でも記載していますが、現在転職活動中の身ですのでプロではありません。
間違いなどありましたらご指摘いただければ、自身でも調べ速やかに修正いたします。参考記事:
丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)ファイル内容は上記サイトから頂きましたが、port8080に対応するため一部編集を加えています。
目標
タイトルの構成で環境構築をし、「Yay! You’re on Rails!」を表示させる。
環境
Amazon linux
Ruby 2.6.3
Rails 5.2.4.4
MySQL 5.5.62
Docker 19.03.6
Docker-compose 1.27.4やってきましょう
インスタンスのボリュームを増やす
cloud9を立ち上げてできるインスタンスはサイズが10GBとなっているので、容量が不足する可能性があります。
その都度不要ファイルを削除できればいいのですが、手っ取り早くコンソールから容量を増やしました。
(極少額の課金が発生するかもしれません、料金はご自身で調べてみてください)まずはcloud9を立ち上げます。
その後EC2の画面に行くと、cloud9と同時に立ち上がったインスタンスがあります。EC2のサイドメニューから下記の順番で選択します。
1.ボリューム
2.先ほど立ち上がったインスタンスのチェックボックス
3.アクション
4.ボリュームの変更インスタンスの状態がoptimizing(黄色のマーク)なので、10分程度放置し終わるのを待ちます。
状態が緑のマークになると、サイズも16GBとなり反映が確認できます。
cloud9のターミナルに移動します。
以下のコマンドで内部からもサイズ変更が反映されているかを確認します。$ df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 483M 60K 483M 1% /dev tmpfs 493M 0 493M 0% /dev/shm /dev/xvda1 16G 8.6G 7.0G 55% /もしリロードしても反映されていないようなら次のコマンドを試してみてください。
$ sudo growpart /dev/xvda 1 $ sudo resize2fs /dev/xvda1Docker-Composeのインストール
インストールの前に このページ で最新のバージョンを確認しておいてください。
下の
1.27.4
の箇所を確認した最新のバージョンに書き換えてインストールします。
私は"/home/ec2-user/environment"ディレクトリで実行しています。$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ docker-compose
コマンドを実行できるように権限を与えます。$ sudo chmod +x /usr/local/bin/docker-composeバージョンの確認をして、表示されればインストールは完了です。
$ docker-compose --version docker-compose version 1.27.4, build 40524192ファイルの準備
プロジェクトディレクトリを作成して、その中に移動します。
$ mkdir myApp $ cd myAppそうしたら必要なファイルを
touch
コマンドで作成していきます。
ここでファイルは4つ用意します。$ touch Dockerfile $ touch Gemfile $ touch Gemfile.lock $ touch docker-compose.yml通常は
vi
コマンドなどでファイルを書いていきますが、cloud9はせっかくエディタが付いているのでそちらで作業しましょう。なお
Gemfile.lock
は記載せずに空のままで大丈夫です。各ファイルの項目は Docker × Ruby on Rails × MySQLの環境構築 この記事を参考にしてください。
DockerfileFROM ruby:2.6.3 RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs RUN mkdir /app_name ENV APP_ROOT /app_name WORKDIR $APP_ROOT ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock RUN bundle install ADD . $APP_ROOTFROM ruby:2.6.3
cloud9にはあらかじめrubyがインストールされています。
以前同様の環境構築をした際、インストール済みのバージョンとここで指定したバージョンが異なりエラーになりました。(原因は他にあるのかもしれませんが、2つのバージョンが競合したのか?)その為ここのrubyのバージョンは
$ ruby -v
で確認したインストール済みのバージョンと同様にしてあります。Gemfilesource 'https://rubygems.org' gem 'rails', '5.2.4.4'rails5系で現在最新のバージョンを指定。Ruby on Rails最新情報
docker-compose.ymlversion: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: パスワード MYSQL_DATABASE: データベース名 ports: - "3306:3306" web: build: . command: rails s -p 8080 -b '0.0.0.0' volumes: - .:/app_name ports: - "8080:8080" links: - dbcommand: rails s -p 8080 -b '0.0.0.0'
ports:
- "8080:8080"cloud9ではport8080でサーバーに接続します。
その為、ホスト:コンテナ の両方を8080番で指定しています。パスワードとデータベース名は任意の値を記載します。
これで設定を記載した3つのファイルと、空のGemfile.lockができました。
Railsプロジェクト作成
カレントディレクトリがプロジェクトディレクトリ(今回は
/home/ec2-user/environment/myApp
)であることを確認します。そうしたら
rails new
を実行してアプリケーションの型を作ります。$ docker-compose run web rails new . --force --database=mysql --skip-bundle問題がなければプロジェクトディレクトリ配下にRailsのひな形が出来上がります。
$ ls app bin config config.ru db docker-compose.yml Dockerfile Gemfile Gemfile.lock lib log package.json public Rakefile README.md storage test tmp vendorデータベース周りの設定のため
/myApp/config/database.yml
を修正します。database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: パスワード host: dbパスワードの箇所には先ほど"docker-compose.yml"で設定したのと同様の値を記載します。
ここで保存しようとすると、恐らくcloud9の上部に警告文が表示されます。EACCES: permission denied
これは
database.yml
に対する書き込み権限が無いので保存できない旨の内容なので、権限を変更指定保存できるようにします。/myApp/configに入り、ファイルの権限を確認します。
$ cd config $ ls -l -rw-r--r-- 1 root root 1657 Nov 29 19:06 database.yml書き込み権限を変更し、反映されれば
database.yml
を保存できるようになります。$ sudo chmod 666 database.yml $ ls -l -rw-rw-rw- 1 root root 1657 Nov 29 19:06 database.ymlDockerコンテナのビルド
プロジェクトディレクトリに戻り、コンテナを立ち上げます。
$ docker-compose buildPermissionError: [Errno 13] Permission denied: '/home/ec2-user/environment/myApp/config/master.key
このエラーが出るのは先ほど同様
myApp/config/master.key
に対するパーミッションエラーなので、権限を変更します。ディレクトリを移動して権限を確認。
$ cd config $ls -l -rw------- 1 root root 32 Nov 29 19:06 master.key権限を変更して反映を確認します。
$ sudo chmod 666 master.key $ ls -l -rw-rw-rw- 1 root root 32 Nov 29 19:06 master.key再トライ
$ docker-compose build $ docker-compose upこれで私はうまくいきました。
データベースの作成
コンテナが起動したらショートカットキー
Alt + T
でcloud9にターミナルをもう一つ立ち上げます。開いたターミナルからプロジェクトファイルに移動して、データベースを立ち上げます。
$ cd myApp $ docker-compose run web rails db:create成功していれば Preview → Preview Running Application で「Yay! You’re on Rails!」が表示されます。
この時**********.vfs.cloud9.ap-northeast-1.amazonaws.com で接続が拒否されました。
と表示されたら、右上の矢印ボタンを押して別タブで開けば表示されます。
サーバーを停止させる場合は
Ctrl + C
ではなく、新しく開いた方のターミナルで下記を入力します。$ docker-compose downその他のエラー
Can't connect to MySQL server on 'db' (115)
コンテナが立ち上がり切っていない時に表示されます。
数秒待ってからリロードすれば表示内容が変わります。Unknown database 'app_name_development'
データベースを作成していないとこの表示がされます。
新しいターミナルを開いてデータベースを作成すれば解消されます。
- 投稿日:2020-11-30T06:25:09+09:00
【AWS】Redmineの環境構築
AWSでRedmineの環境構築をしたときのメモ
失敗
最初はAWS上にDBを作成し、新規DBとRedmineを接続しようとしていました。
試行1
③のブログの記載どおりにPowershellのコマンドから接続しようとしましたが、
"command not found"が表示されたので、いったん諦めました。
後で調べてみるとRubyの必要なファイルをインストールしていなかったことが原因だったようです。
→Windows10にRubyGemsをインストールする方法※AWSのコンソールに表示されているエンドポイントを正しく指定していても接続できない場合
→セキュリティグループでポート番号”3306”(DBで使用しているポート)を追加すると接続できるようになります。▼その時参考にしたサイトたち
(MySQLのインストール方法)
①Windows10にインストーラーでMySQLをインストールする方法
②MySQLの開発環境を用意しよう(windows)
③EC2-RDSを使ってRedmineをインストールする。ついでにサブディレクトリで。(Redmineの設定方法)
④Redmineのインストール(MySQLの設定方法)
⑤MySQL データベースを作成して接続する成功
結局見つけた方法は、Redmine用のAMIから新規インスタンスを作成するというものでした。
こちらのブログの記載どおりにするとRedmineに接続できました…!Todo
- PowerShellのコマンドを勉強
- MySQLのサーバー接続について調べる
- 投稿日:2020-11-30T00:22:33+09:00
Slackで投稿内容やファイルを閲覧するページを自作したお話 ①導入編
背景
Slackは非常に便利なコミュケーションツールなのですが、無料枠で使っていると閲覧上限1万件&ストレージ制限5GBが大きな制約となります。
自分の所属する団体では150人とかで使うので、1万件の制限があると3ヶ月分ぐらいしか閲覧できません。
(課金しろという話ですが、スタンダードプランにしても10万/月以上かかるので、、)解決策??
ワークスペースを複数で運用したり、ログを取得するGASスプリクトをコピペして運用したりしていました。
参考) コピペのみ。Slackメッセージログを自動で保存する方法
問題点
ワークスペースを複数で運用すると、メッセージの共有ができないなど、コミュケーションツールとしての良さが失われてしまいます。また、GASスプリクトはログを残しておくという点では良いですが、後で閲覧するには不適です。
解決策: ログの取得や閲覧ページを自作する!
既存の方法で解決しないなら自分で作ってしまえ!という発想です。
とりあえず、
- ログの取得・保存
- 見やすい閲覧ページ
を実装しようと計画しました。
仕様
使用言語
- React6.14.6
- PHP7.4.4
- MySQL5.7
フロントにはReact、バックエンドはPHPで実装しました。
Slackからデータを取得
SlackAPIでAppを作成してデータを取得します。APIを用いて以下のデータを取得しています。
- ユーザー
- チャンネル
- チャット
- ファイル
これらのデータはPHPで取得した後、MySQLでDBに格納します。
詳しくは別記事で説明します。
- SlackAPI https://api.slack.com/
取得したデータを閲覧
フロントは勉強も兼ねてReactで実装しています。
また、DBに格納されたデータをフロントで取得するエンドポイントはPHPで作成しています。
詳しくは別記事で説明します。
まとめ
PHP+SlackAPIでデータ取得、MySQLでデータ格納して、PHPでエンドポイント作成、閲覧ページはReact+axiosという感じです。
編集後記
このシステムを作るのに、SlackAPIのドキュメントを何周もしました。
わからないところは、Google翻訳に助けられながらもなんとか理解できました。続きの記事
時間あるときに書いていくので、気長にお待ち下さい、、