20201027のMySQLに関する記事は9件です。

Youtuberヒカキンさんのじゃんけんを統計したWebアプリを開発してみた!

なぜ作ったのか?

国民的アニメのじゃんけんを集めたデータで機械学習でそのじゃんけんに勝てるプログラムを作っていたのが始まりです。
で、プログラムを作って満足はしたんですが、今度は違う題材で機械学習プログラム作ってみようと思いました。
それでじゃんけんって言ったら他に誰が人気かって言うと真っ先に思い浮かんだのは子供達に大人気のYoutuberである「ヒカキンさん」と考えました。
なので、HikakinTVでヒカキンさんがじゃんけんをし始めた2014年11月からの動画を視聴し続けて結果を記録していきました。

現状、機械学習するまでは至ってはいませんが、他にヒカキンさんのじゃんけん記録している人って探した限りいませんでした。
それならば!自分がヒカキンさんのじゃんけんの第一人者になっても良いんじゃないかって思ったので、突発的にフレームワークの勉強しながら統計したWebサイトを作成してみました

またヒカキンさんを選んだ理由として他にはこんな理由があります。

  • テレビとは違ってYoutubeに投稿されているので、投稿者が消去しなければ半永久的に全ての動画が存在するので記録しやすい

  • ヒカキンさんのじゃんけんはゲストと一緒にしたり変装や特殊な環境下が多かったりするので、じゃんけんでも様々なバリエーションがあって面白そう

  • ヒカキンさんは子供にも人気あるので、もしかしたら子供達も大勢みてくれるサイトになるかも!?という甘い願望

環境・ツール

開発PC:MacBook Pro macOS High Sierra 10.13.6
フレームワーク:Laravel
サーバサイドプログラム言語:PHP
フロントエンドプログラム言語:Javascript,scss
データベース:MySQL
ライブラリ:Webpack
運用サーバ:さくらVPS(Webサーバ,DBサーバの2つ) CentOS7.8

サイトマップやワイヤーフレームなどはなく作成しながらデザインを決め、テーブル設計もボロが出てしまっていると思います。
制作時間はわかりませんが、期間は大体3ヶ月近くかかりました。

成果物

URL:https://hikakinjunken.tk/
PC向けとして公開しています。
スマホでもみれますが、やや文字が小さくタップしにくいかと思います。

サイトについて紹介している動画
Youtube:https://www.youtube.com/watch?v=aOOWyjU3kLc
ニコニコ:https://www.nicovideo.jp/watch/sm37629624

GitHub:後日公開予定...

トップページ

トップページにアクセスすると、
左側は累計のじゃんけん数とそのグー・パー・チョキの内訳、
右側は最近投稿した動画でじゃんけんした動画5選表示されます。
- 「結果↓」をクリックするとじゃんけんの結果が表示されます。
- サムネイルまたはタイトルをクリックすると動画視聴ページに移行します。
image.png

キーワード検索

動画のタイトルと参戦したゲストに一致するワードを検索します。

ゲスト検索

じゃんけんに一緒に行ったゲストをプルダウンメニューから検索します。
image.png

じゃんけん一覧

年月ごとにHikakinTV内でアップロードされた動画を検索します。
image.png

検索ページ

キーワード検索とゲスト検索で検索すると、以下のように条件にあった動画が検索されてじゃんけん結果も確認できます。
- 例として、ゲスト検索で「はじめしゃちょー」を選んだときの結果です。
- 動画の投稿順やじゃんけんで出した手を決めて詳細に検索することも可能です。

image.png

動画視聴ページ

動画のサムネイルかタイトルをクリックすると、動画視聴ページに移行できます。
このページでYoutubeに行かなくてもHikakinTVに投稿された動画を視聴できます。(怒られるかもしれない)
- 「タグ一覧」では、最大5つ動画に関連したタグを登録することができ、「キーワード検索」で登録したタグのワードで検索するとその動画が出てきます。(ニコニコ動画のタグ機能に似ている)
image.png

じゃんけん結果管理ページ

これは、私(管理者)しかアクセスできないページですが、ヒカキンさんの投稿動画とじゃんけん結果を登録・管理するページを作成しています。
このページのフォームに動画情報とじゃんけん結果、参戦したゲストを記載して登録することでMySQL側のデータベースに情報がInsertされます。
image.png

 反省点

  • 最初にサイトマップや要件を洗い出して作った方が良かった

Laravelで作ろうとは考えていましたが、環境を整えていたりコーディングしている間にも必要なライブラリが欲しかったり、こういう機能も入れた方が良いみたいな考えが出てきて寄り道することが多かった

  • テーブル設計も考えて作るべきであった

ER図は作成したが第3正規化までのことをしていなかったので、もっとデータベースのテーブルはよく考えて作るべきだった

  • 現状は単にじゃんけん結果を見るだけのサイトになってしまっている

じゃんけんの結果見るだけの需要でしかなく、サイトに満足して?使ってもらえるようにはしていないです。
機械学習でじゃんけんの結果を予測するページを作ったり、動画視聴ページでコメントできる機能を盛り込んで見ようかなとは考えています。

まとめ

フレームワークを使ってオリジナルのアプリ開発するのは初めてに近い感じでしたので、色々と不備はあるかもしれないがある程度形にできたサイトになったのは良かったです。
自分がサボらなければ、ヒカキンさんが動画投稿したらじゃんけん結果を確認して、サイトに記録する...みたいなことを続けていきたいとは思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django+MySQL+nginx の開発環境を Docker Compose で構築する

動作環境とこれまでの経緯

というわけで、Web アプリケーションを開発するために、Django+MySQL+nginx というマッスルドッキングな開発環境を Docker で構築していきたいと思います。

ディレクトリ構成

当初のディレクトリ構成は次のとおり。

app
├── docker-compose.yml
├── mysql
│   ├── Dockerfile
│   └── init.d
│       └── init.sql
├── nginx
│   ├── conf
│   │   └── app_nginx.conf
│   └── uwsgi_params
└── python
    ├── .dockerignore
    ├── Dockerfile
    └── requirements.txt

この状態から
- docker-compose.yml
- Dockerfile(python)
- .dockerignore
- requirements.txt
- Dockerfile(mysql)
- init.sql
- app_nginx.conf
- uwsgi_params
の8つのファイルを編集することになります。

ちなみに、以下では Django で「app」というアプリケーションを作る例を説明しています。各自で作りたいアプリケーションの名前に置き換えてね。

docker-compose.yml は最終的にこうなった

最初に結果を書きましょう。
ググったり本を読んだりしながら試行錯誤し、無慈悲なエラーメッセージと戦いながら、最終的にこんな docker-compose.yml にたどり着きました。長い道中を振り返れば、思わず目頭が熱くなる。

docker-compose.yml
version: '3.7'

services:
  python:
    build:
      context: ./python
      dockerfile: Dockerfile
    command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log
    restart: unless-stopped
    container_name: Django
    networks:
      - django_net
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8001"
    depends_on:
      - db

  db:
    build:
      context: ./mysql
      dockerfile: Dockerfile
    restart: unless-stopped
    container_name: MySQL
    networks:
      - django_net
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: "*******"
      TZ: "Asia/Tokyo"
    volumes:
      - app.db.volume:/var/lib/mysql
      - ./mysql/init.d:/docker-entrypont-initdb.d

  nginx:
    image: nginx:1.17
    restart: unless-stopped
    container_name: nginx
    networks:
      - django_net
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
    depends_on:
      - python

networks:
  django_net:
    driver: bridge

volumes:
  app.db.volume:
    name: app.db.volume

構成を適当に略記すると、こんなイメージかしら。
図1.png

Django の設定

なにはともあれ、Django の設定から。
「python」ディレクトリ直下の Dockerfile を、次のように編集します。

./python/Dockerfile
FROM python:3.8.5
WORKDIR /code
ADD requirements.txt /code/
RUN pip install --upgrade pip && pip install -r requirements.txt
ADD . /code/

まず、code という名前の作業ディレクトリを作成し、そこに移ります。そのディレクトリに requirements.txt を追加した後、pip で必要なパッケージをインストールして、最後に code をコンテナに追加します。

で、pip でインストールするパッケージは、次のように requirements.txt に列挙しておきます。今回必要になるのは、Django 本体と、nginx とソケット通信するための uWSGI と、MySQL に接続するための mysqlclient の3つ。

./python/requirements.txt
Django==2.2.12
uwsgi==2.0.18
mysqlclient==1.4.6

ついでに、.dockerignore も編集しておきましょう。余計なファイルがコンテナに入ると困るからね。

./python/.dockerignore
**/__pycache__
**/.DS_Store
**/.Thumbs.db
**/.git
**/.gitignore

ここまでの設定を踏まえて docker-compose.yml で Django を設定すると、次のとおりになります。

docker-compose.yml(抜粋)
python:
  build:
    context: ./python        # docker-compose.yml からの Dockerfile の相対パス
    dockerfile: Dockerfile   # 「Dockerfile」 に設定を書いてますよ、という明示的な指定
  command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log
  restart: unless-stopped    # コンテナが異常停止した場合は再起動する
  container_name: Django     # コンテナ名を定義
  networks:
    - django_net             # ネットワーク名「django_net」を指定
  volumes:
    - ./src:/code            # ./src に /code をマウントする
    - ./static:/static       # ./static に /static をマウントする
  expose:
    - "8001"     # uwsgi が nginx とソケット通信するために、8001 番のポートを開けておく
  depends_on:
    - db

uWSGI の設定がややこしいですが、app.wsgi の部分が [Django のプロジェクト名].wsgi であることを守れば、あとはおまじないです。

MySQL の設定

MySQL の設定は、正直よく分かりません。だいたい、私はクエリが満足に書けないレベルで RDB は嫌いなのです。

マニュアルなどをいろいろと調べた結果、どうやらこれがベストプラクティスらしい。しらんけど。

./mysql/Dockerfile
FROM mysql:5.7
COPY init.d/* /docker-entrypoint-initdb.d/
./mysql/init.d/init.sql
CREATE DATABASE IF NOT EXISTS app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER IF NOT EXISTS 'app_user'@'%' IDENTIFIED BY '[パスワード]';
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%';
FLUSH PRIVILEGES;

ちなみに、上記の init.sql で作成した app.db(データベースの名前)は、後述の settings.py の設定に必要になるので、忘れないように。

この設定に基づいて、docker-compose.yml で MySQL を設定すると、次のとおりになります。

docker-compose.yml(抜粋)
db:
  build:
    context: ./mysql         # docker-compose.yml からの Dockerfile の相対パス
    dockerfile: Dockerfile   # 「Dockerfile」 に設定を書いてますよ、という明示的な指定
  restart: unless-stopped    # コンテナが異常停止した場合は再起動する
  container_name: MySQL      # コンテナ名を定義
  networks:
    - django_net             # ネットワーク名「django_net」を指定
  ports:
    - "3306:3306"            # 通信するポート番号を指定
  environment:
    MYSQL_ROOT_PASSWORD: "*******"  # ルートパスワードを環境変数で設定
    TZ: "Asia/Tokyo"                # タイムゾーンを環境変数で設定
  volumes:
    - app.db.volume:/var/lib/mysql   # データベースをボリューム「app.db.volume」に保存
    - ./mysql/init.d:/docker-entrypont-initdb.d

「app.db.volume」という名前のボリュームにデータベースを保存することにしたので、「volumes」を設定します。

docker-compose.yml(抜粋)
volumes:
  app.db.volume:
    name: app.db.volume

nginx の設定

uWSGI の設定もよく分かりません。正直そこまで uWSGI のことをガッツリ調べる気にならんかった。すまん。

次のとおり、適当にパラメータを設定しておけばいいらしい。しらんけど。

./nginx/uwsgi_params
uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

で、nginx の設定なのですが、まず uWSGI でソケット通信するためのポート 8001 番を開けてやる必要があります。

また、nginx の待ち受けポートを設定します。私の設定は 80 番になっているのですが、これはコンテナを動かす仮想マシンの設定で、ゲスト(仮想マシン)の 80 番を流れるデータを、ホストの 8081 番に流すように設定しているからです。

これで、ホストのブラウザで「127.0.0.1:8081」にアクセスすれば、ゲストで動く Web アプリが見られるというわけ。

./nginx/conf/app_nginx.conf
upstream django {
  ip_hash;
  server python:8001;  # uWSGI で Django と nginx とが通信するためのポート
}

server {
  listen      80;      # 待ち受けポート
  server_name 127.0.0.1;
  charset     utf-8;

  location /static {
    alias /static;
  }

  client_max_body_size 75M;

  location / {
    uwsgi_pass  django;
    include     /etc/nginx/uwsgi_params;
  }
}

server_tokens off;

この設定に基づいて、docker-compose.yml で nginx を設定すると、次のとおりになります。Django の設定と同様に、./static/static にマウントしているので、Django で処理しない静的コンテンツは nginx でレスポンスを返せます。

docker-compose.yml(抜粋)
nginx:
  image: nginx:1.17         # nginx のイメージを適当に取ってくる
  restart: unless-stopped   # コンテナが異常停止した場合は再起動する
  container_name: nginx     # コンテナ名を定義
  networks:
    - django_net            # ネットワーク名「django_net」を指定
  ports:
    - "80:80"               # 通信するポート番号を指定
  volumes:
    - ./nginx/conf:/etc/nginx/conf.d
    - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
    - ./static:/static      # ./static に /static をマウントする
  depends_on:
    - python

あと、ここまで「django_net」を使うと言っておきながら全然設定していなかったので、忘れずに「networks」の項目も設定します。

docker-compose.yml(抜粋)
networks:
  django_net:
    driver: bridge

Django プロジェクトの設定

さて、ここまでで Docker の設定は全部終わりました。あとは Django 本体の設定を残すだけです。

まずはプロジェクトを立ち上げましょう。

$ docker-compose run python django-admin.py startproject app .

このコマンドを叩いた後で srcstatic の中を覗くと、いろいろと Django 用のファイルができているはず。

次に、./src/app/settings.py を編集します。

./src/app/settings.py
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',    # コメントアウト
        'ENGINE': 'django.db.backends.mysql',        # 追加
        # 'NAME': os.path.join(BASE_DIR, 'app_db'),  # コメントアウト
        'NAME': 'app_db',    # init.sql で CREATE したデータベース名と同じことを確認
        'USER': 'app_user',  # 追加
        'PASSWORD': '*****', # 追加(環境変数で指定したパスワードと同じもの)
        'PORT': '3306',      # 追加
        'HOST': 'db',        # 追加
    }
}

STATIC_URL = '/static/'   # 追加
STATIC_ROOT = '/static/'  # 追加

...中略

LANGUAGE_CODE = 'ja'      # 編集
TIME_ZONE = 'Asia/Tokyo'  # 編集

あとは Django でおなじみのコマンドを docker-compose で叩くだけです。

$ docker-compose run python ./manage.py migrate          # DBマイグレーション
$ docker-compose run python ./manage.py createsuperuser  # 管理者の設定
$ docker-compose run python ./manage.py collectstatic    # 静的ファイルをコピー

コンテナの起動と停止

さて、いよいよコンテナを動かします。
出でよ Django!

$ docker-compose up -d --rm

そして、ホストのブラウザから 127.0.0.1:8081 にアクセスすると…
スクリーンショット 2020-10-27 21.26.10.png
やったぜ!!
ハロー!Docker!
ハロー!Django!

コンテナを停止する時は、stop します。--rm オプションを付けて起動したので、停止させるとコンテナは削除されます。

$ docker-compose stop

無慈悲なエラーメッセージたち

さて、こんな感じで記事にまとめると、まるで私がサクサクと設定したように誤解されそうですので、どれほど七転八倒しながら紆余曲折を経たかを書き残しておこうと思います。

まずこのエラーメッセージ。

ERROR: The Compose file './docker-compose.yml' is invalid because:
services.pithon.volumes contains an invalid type, it should be an array

なんやねんこれ、何が悪いねん、と憤りながらググりまくったのですが、直接的な解決法は見つかりません。

ふと「it should be array」(そこは配列のはずなのに、そうなってないでしょ)がポイントであることに気づき、YAML の構文を調べたところ……「YAML では、行頭に『-』をつけることで配列を表現します。『-』のあとには半角スペースを入れてください」という記載が。

よく見ると、ここ、

volumes:
  - ./src:/code

が、

volumes:
  -./src:/code  # 「-」 の後に半角スペースがない

こうなってた。これに気づくのに2時間かかった

このクソ計算機が!!お前は親戚のめんどくさいオッサンか!!
たった半角スペースが1個なかっただけでこの仕打ち。もうやってらんない。そもそも「ヤムル」とかいう名前が気に食わない。韓国料理かよ(それはナムル)。

次に、このエラーメッセージ。

Unsupported config option for services.nginx: 'deponds_on'

よく見たら分かるのですが、deponds_on ではなく、depends_on が正しい。アホみたいなスペルミスだが、これに気づくのに2時間かかった

このクソ計算機が!!私の貴重な人生の時間を返せ!!
ディープラーニングで顔認識とか正直どうでもよくて、この手のヒューマンエラーをそっと修正してくれる気の利いた計算機が欲しい。

はい、さらにこのエラーメッセージ。

ERROR: Service 'nginx' depends on service 'python' which is undefined.

なんでこんなエラーが出ていたかというと、pythonpithon とミスタイプしていたから(最初のエラーメッセージ参照)。これに気づくのに1時間かかった

このクソ計算機が!!てめえの頭はハッピーセットかよ!!
2コマ漫画みたいな展開に我ながら愕然としますが、Dvorak 配列では、y と i が上下で隣り合わせであることが原因と思われます。

次回をお楽しみに

さて、私がどれほど計算機を憎んでいるか、なぜ大学院で機械学習を専攻しようと考えたか、そしてなぜ研究者もエンジニアも辞めたか、お分かりいただけたでしょうか(本稿の趣旨とは全然違う話)。

オモチャとして遊ぶにはちょうどいいが、これを仕事にする気にはなれない
要するに、どう見ても無才です。本当にありがとうございました。

最後は大変ハートウォーミングなオチになってしまいましたが、次回は AWS に本番環境を作っていきたいと思います。乞うご期待。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sqlacodegenを使って既存のMySQLサーバからSQLAlchemyのテーブル定義を生成する

まっさらなAmazonLinux2でやるときの手順です

実行環境

OS: AmazonLinux2 (Linux version 4.14.193-149.317.amzn2.x86_64)
python: Python 2.7.18

やること

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
pip install sqlacodegen pymysql
sqlacodegen mysql+pymysql://user:password@sqlhost/dbname

オプション:
--outfile
--tables
これで標準出力に全テーブルのSQLAlchemy定義が出力されます

公式サイトにはoursqlというのを使うと書いてありますが、この環境では下記エラーが出るためpymysqlを使います
https://pypi.org/project/sqlacodegen/

Traceback (most recent call last):
  File "/home/ec2-user/.local/bin/sqlacodegen", line 8, in <module>
    sys.exit(main())
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlacodegen/main.py", line 44, in main
    engine = create_engine(args.url)
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/__init__.py", line 500, in create_engine
    return strategy.create(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/strategies.py", line 61, in create
    entrypoint = u._get_entrypoint()
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/url.py", line 172, in _get_entrypoint
    cls = registry.load(name)
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 254, in load
    loader = self.auto_fn(name)
  File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/dialects/__init__.py", line 32, in _auto_fn
    dialect, driver = name.split(".")
ValueError: too many values to unpack
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】初めてのCRUD機能

Laravelでの単純なCRUD機能作成時の備忘録。

最終的なブラウザ表示

  • 一覧表示ページ と 詳細表示ページ
  • 新規投稿ページ と 編集ページ

最終的なコード

■ ルーティング

実装機能 HTTPメソッド メソッド
一覧表示 get index
新規作成 get → post create → store
詳細表示 get show
編集 get → post edit → update
削除 delete destroy
web.php
Route::get('/', function () {
    return redirect('/articles');
});
Route::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list');                 // 一覧表示ページ
Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new');              // 新規投稿ページ
Route::post('/article', [App\Http\Controllers\ArticleController::class, 'store'])->name('article.store');                // 新規保存
Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show');              // 詳細表示ページ
Route::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete');      // 削除
Route::get('/article/edit/{id}', [App\Http\Controllers\ArticleController::class, 'edit'])->name('article.edit');         // 編集ページ表示
Route::post('/article/update/{id}', [App\Http\Controllers\ArticleController::class, 'update'])->name('article.update');  // 更新

■ コントローラー

Articleコントローラー
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller{
  // 一覧表示
  public function index(Request $request){
    if ($request->filled('keyword')) {
      $keyword = $request->input('keyword');
      $message = '検索キーワード: '.$keyword;
      $articles = Article::where('content', 'like', '%'.$keyword.'%')->get();
    }else{
      $message = "検索キーワードを入力してください。";
      $articles = Article::all();
    }
    return view('index', ['message' => $message, 'articles' => $articles]);
  }
  // 新規投稿ページ表示
  public function create(Request $request){
    $message = '投稿フォーム: ';
    return view('new', ['message'=>$message]);
  }
  // 新規保存
  public function store(Request $request){
    $article = new Article();
    $article->content = $request->content;
    $article->user_name = $request->user_name;
    $article->save();
    return redirect()->route('article.show', ['id' => $article->id]);
  }
  // 詳細表示
  public function show(Request $request, $id, Article $article){
    $message = '記事の内容: ';
    $article = Article::find($id);
    return view('show', ['message' => $message, 'article' => $article]);
  }
  // 編集ページ表示
  public function edit(Request $request, $id, Article $article){
    $message = '記事の編集: '.$id;
    $article = Article::find($id);
    return view('edit', ['message'=>$message, 'article'=>$article]);
  }
  // 更新
  public function update(Request $request, $id, Article $article){
    $article = Article::find($id);
    $article->content = $request->content;
    $article->user_name = $request->user_name;
    $article->save();
    return redirect()->route('article.show', ['id' => $article->id]);
  }
  // 削除
  public function destroy(Request $request, $id, Article $article){
    $article = Article::find($id);
    $article -> delete();
    return redirect("/articles");
  }
}

■ ビュー

  • Bootstrapを使用。

- 共通部分

layout.blade.php
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset='utf-8'>
    <title>Laravel_sample</title>
    <style>body {padding: 10px;}</style>
    @include('style-sheet')      <!-- Bootstrapの読み込み -->
  </head>

  <body>
    @include('nav')      <!-- ナビゲーションバー -->
    <div class='container'>
      @yield('content')
    </div>
  </body>
</html>
  • ナビゲーションバー
nav.blade.php
<nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'>
  <a class='navbar-brand' href='{{ route("article.list") }}'>My Articles?</a>
</nav>
style-sheet.blade.php
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<style>body {padding-top: 80px;}</style>  <!-- 設置位置の調節  -->

- 一覧表示ページ(root)

一覧表示ページ(index.blade.php)
@extends('layout')
@section('content')
  <h1>一覧表示ページ</h1>
  <p>{{ $message }}</p>
  @include('search')     <!-- 検索フォーム -->
  <table class="table table-striped table-hover">
    @foreach ($articles as $article)
      <tr>
        <td>
          <a href='{{ route("article.show", ["id" => $article->id]) }}' >
            {{ $article -> content }}
          </a>
        </td>
        <td>{{ $article -> user_name }}</td>
        <td>{{ $article -> created_at }}</td>
      </tr>
    @endforeach
  </tabel>
  <div>
    <a href = '{{ route("article.new") }}' class="btn btn-primary">投稿</a>
  </div>
@endsection

- 新規投稿ページ

新規投稿ページ(new.blade.php)
@extends('layout')
@section('content')
  <h1>新規投稿ページ</h1>
  <p>{{ $message }}</p>

  {{ Form::open(['route'=>'article.store']) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者名: ') }}
      {{ Form::text('user_name', null, ['placeholder'=>'投稿者名を入力']) }}
    </div>

    <div class='form-group'>
      {{ Form::submit('投稿', ['class' => 'btn btn-primary']) }}  <!-- 投稿ボタン -->
      <a href='{{ route("article.list") }}'>もどる</a>  <!-- indexページへのリンク -->
    </div>
  {{ Form::close() }}
@endsection

- 詳細表示ページ

詳細表示ページ(show.blade.php)
@extends('layout')
@section('content')
  <h1>詳細表示ページ</h1>
  <p>{{ $message }}</p>
  <p>{{ $article -> content }}</p>
  <p>{{ $article -> user_name }}</p>
  <p>{{ $article -> created_at }}</p>
  <p>
    <a href='{{ route("article.edit", ["id"=> $article->id]) }}' class="btn btn-secondary">編集</a>
    <a href = '{{ route("article.list") }}' class="btn btn-outline-secondary">戻る</a>
  </p>
  <div>
    {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article -> id]]) }}
      {{ form::submit("削除ボタン") }}
    {{ Form::close() }}
  </div>
@endsection

- 編集ページ

編集ページ(edit.blade.php)
@extends('layout')
@section('content')
  <h1>編集ページ</h1>
  <p>{{ $message }}</p>

  {{ Form::model($article, ['route'=> ['article.update', $article->id] ]) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者名: ') }}
      {{ Form::text('user_name', null) }}
    </div>

    <div class='form-group'>
      {{ Form::submit('保存', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.show", ["id"=> $article->id]) }}'>もどる</a>
    </div>
  {{ Form::close() }}
@endsection

- 検索フォーム

  • 部分テンプレートで作成し、indexページに設置。
検索フォーム(search.blade.php)
{{ Form::open(['method' => 'get']) }}
  {{ csrf_field() }}

  <div class='form-group'>
    {{ Form::label('keyword', 'キーワード:') }}
    {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }}
  </div>

  <div class='form-group'>
    {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }}
    <a href='{{ route("article.list") }}'>クリア</a>
  </div>
{{ Form::close() }}

各論

前提

  • MySQLに、Articlesテーブル(content、user_nameカラム)作成済みの状態とする。
  • 動作確認のために、rootページにコントローラーからデータを送れるか?、表示できるか? 程度は確認しとく。
参)コントローラー作成コマンド
% php artisan make:controller articleController
動作確認(ルーティング→コントローラー→indexビュー)
// コントローラー
public function index(){
  $message = '動作確認用';
  return view('index', ['message' => $message]);    // indexビューに、message を渡してみる
}

// ルーティング
Route::get('/', function () {
  return redirect('/articles');
});

// ビュー(index.brade.php で、messageを取得してみる)
<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>
    <title>Laravel_sample</title>
    <style>body {padding: 10px;}</style>
  </head>
  <body>
    <p>{{ $message }}</p>
  </body>
</html>

CRUD機能 の実装

■ 一覧表示(index)

  • コントローラー(モデルから全データを取得し、ビューに送る) → ビュー(一覧表示) の流れ。
/app/Http/Controllers/ArticleController.php
public function index(){
  $message = 'Hello world';
  $articles = Article::all();        // モデルから全データ取得
  return view('index', ['message' => $message, 'articles' => $articles]);  // ビューに渡す
}
/resources/views/index.blade.php
@extends('layout')      // 部分テンプレートを呼び出す
@section('content')
  <h1>一覧表示ページ</h1>
  <p>{{ $message }}</p>               // コントローラーから $message 取得
  @foreach ($articles as $article)    // コントローラーから $article をループで取得
    <p>{{ $article->content }}</p>
  @endforeach
@endsection

■ 詳細表示(show)

  • コントローラー(モデルから1つの指定データ取得し、ビューに送る) → ビュー(詳細表示) の流れ。
/app/Http/Controllers/ArticleController.php
public function show(Request $request, $id, Article $article){            // id、記事情報が必要
  $message = '詳細表示: '.$id;
  $article = Article::find($id);       // モデルから指定データ取得
  return view('show', ['message' => $message, 'article' => $article]);   // ビューに渡す
}
  • 詳細表示ページを作成。
/resources/views/show.blade.php
<h1>詳細表示ページ</h1>
<p>{{ $message }}</p>
<p>{{ $article->content }}</p>
<p>
  <a href='{{ route("article.list") }}'>戻る</a>      // 一覧表示ページへのリンク (例:ルーティング名「article.list」と名付けてる)
</p>
  • 一覧表示ページに、詳細表示ページへのリンクを貼る。
/resources/views/index.blade.php
@foreach ($articles as $article)
  <p>
    <a href='{{ route("article.show", ["id" =>  $article->id]) }}'>
      {{ $article->content }}
      {{ $article->user_name }}
      {{ $article->created_at }}
    </a>
  </p>
@endforeach

■ 新規投稿(create、store)

  • 投稿ページへのルーティングを設定。
    • 記述順序に注意!! newよりshowを先に記述するとエラー! /article/{id}を探しに行っちゃうから。
/routes/web.php
Route::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list');       // 一覧表示ページ
Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new');    // 新規投稿ページ
Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show');    // 詳細表示ページ
  • コントローラーに、投稿ページ表示のためのメソッド(create)を追加。
/app/Http/Controllers/ArticleController.php
public function create(Request $request){   // createでは、取得したい情報はない
  // 確認用:フォームを使わずに、固定テキストや日時を格納する場合 (※ storeメソッドは不要)
  $article = new Article();       // インスタンス作成
  $article->content = '投稿内容';
  $article->user_name = '投稿者';
  $article->save();
  return redirect('/articles');   // 保存したら、indexページにリダイレクト

  // フォームから、投稿する場合
  $message = '投稿フォーム: ';
  return view('new', ['message'=>$message]);   // newページへ送る
}
  • 続いて、保存のためのメソッド(store)も追加。
app/Http/Controllers/ArticleController.php
public function store(Request $request){
  $article = new Article();                   // インスタンス作成
  $article->content = $request->content;      // 投稿内容
  $article->user_name = $request->user_name;  // 投稿者名
  $article->save();                           // 保存
  // レコード保存後に、showページへデータを渡してリダイレクト
  return redirect('article.show', ['id' => $article->id]);
}
  • indexページに投稿ページへのリンクを設置。
    • 上で、新規投稿ページへのルーティング名は、「article.new」と名付けたので、それを使用。
/resources/views/index.blade.php
<div>
  <a href='{{ route("article.new") }}'>新規投稿</a>   // 新規投稿ページへのリンク
</div> 
  • 最後に、投稿フォームを作成する。
    • 投稿フォームを部分テンプレートとして作成。
    • Laravelでのフォーム設置には、laravelcollective/html ライブラリ のインストールが必要。
      注意)インストールを忘れると、Class 'Form' not found エラー!!
ターミナルでフォーム使用時に必要なライブラリをインストール
% composer require laravelcollective/html
// メモリ制限エラー( Allowed memory size of 1610612736 bytes exhausted)の場合は、メモリを上げてインストールしてみる
% php -d memory_limit=-1 /usr/local/bin/composer require laravelcollective/html

% composer info      // インストール済みか確認
resources/views/new.blade.php
@extends('layout')
@section('content')
  <h1>投稿フォーム</h1>
  <p>{{ $message }}</p>

  {{ Form::open(['route' => 'article.store']) }}  <!-- 投稿フォーム  -->
    <div class='form-group'>                      <!-- 投稿内容の記述エリア -->
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }}
    </div>

    <div class='form-group'>                      <!-- 投稿者名の記述エリア -->
      {{ Form::label('user_name', '投稿者:') }}
      {{ Form::text('user_name', null, ['placeholder'=>'投稿者を入力']) }}
    </div>

    <div class="form-group">                      <!-- ボタン設置 -->
      {{ Form::submit('投稿ボタン', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.list") }}'>一覧に戻る</a>
    </div>
  {{ Form::close() }}
@endsection

■ 編集 (edit、 update)

  • 編集ページ表示機能を実装(editメソッド)。
app/Http/Controllers/ArticleController.php
public function edit(Request $request, $id, Article $article){  // 編集には、id情報 と 記事データが必要
  $message = '記事の編集: '.$id;    // 表示用
  $article = Article::find($id);  // 編集するレコードをid情報から取得
  return view('edit', ['message'=>$message, 'article'=>$article]);  // 編集ページに渡す
}
  • 編集フォームを作成する。
resources/views/edit.blade.php
@extends('layout')
@section('content')
  <h1>編集フォーム</h1>
  <p>{{ $message }}</p>
  <!-- Form::model : 編集内容を表示してくれる -->
  {{ Form::model($article, ['route' => ['article.update', $article->id]]) }}
    <div class='form-group'>
      {{ Form::label('content', '内容:') }}
      {{ Form::text('content', null) }}
    </div>

    <div class='form-group'>
      {{ Form::label('user_name', '投稿者:') }}
      {{ Form::text('user_name', null) }}
    </div>

    <div class="form-group">
      {{ Form::submit('保存ボタン', ['class' => 'btn btn-primary']) }}
      <a href='{{ route("article.show", ["id" =>  $article->id]) }}'>もどる</a>
    </div>
  {{ Form::close() }}
@endsection
  • showページに、編集ボタンを設置。
resources/views/show.blade.php
<a href='{{ route("article.edit", ["id" => $article->id]) }}' class='btn btn-outline-primary'>編集ボタン</a>
  • 編集内容を保存する更新機能を実装(updateメソッド)。
app/Http/Controllers/ArticleController.php
public function update(Request $request, $id, Article $article){
  $article = Article::find($id);
  $article->content = $request->content;
  $article->user_name = $request->user_name;
  $article->save();
  return redirect()->route('article.show', ['id' => $article->id]);
}

■ 削除(delete)

  • Laravelでは、ブラウザとの通信にHTTPメソッドを指定するが、ブラウザが deleteメソッドをサポートしてないので、POSTで隠しメソッドを送って、コントローラーメソッドのdestroyを実行してもらうイメージ。

  • まず、削除用のルーティングを設定。

/routes/web.php
Route::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete');
  • コントローラーにアクション(destroy)を定義。
/app/Http/Controllers/ArticleController.php
public function destroy(Request $request, $id, Article $article){   // 削除するレコードのid、記事データ が必要
  $article = Article::find($id);   // id情報から、レコードを取得
  $article->delete();              // 削除
  return redirect('/articles');    // 削除後に、一覧表示ページへリダイレクト
}
  • 詳細表示ページに、削除ボタンを設置。
/resources/views/show.blade.php
<div>
  {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article->id]]) }}
    {{ Form::submit('削除ボタン') }}
  {{ Form::close() }}
</div>

■ 検索

  • 削除と同じく、Laravelでフォームを使うには、「laravelcollective/html」ライブラリが必要。
参)laravelcollective/htmlのインストール
% composer require laravelcollective/html
% composer info      // インストール済みかの確認
  • Laravelでのフォームは、{{ Form:: ... }}に記述する(Formファサードという)。
    (※ ファサード(facade)とは、建物の入り口という意味。)
  • 1. まず、検索フォームの部分テンプレートを作成する。
    • GETメソッドによって、text型で、キー:「keyword」に値を格納してコントローラーにデータを送る。
resources/views/search.blade.php
{{ Form::open(['method' => 'get']) }}
  {{ csrf_field() }}           <!-- CSRFトークン -->
  <div class='form-group'>
    {{ Form::label('keyword', 'キーワード:') }}
    {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }}
  </div>
  <div class='form-group'>
    {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }}  <!-- 検索ボタン -->
    <a href='{{ route("article.list") }}'>クリア</a>                     <!-- indexページへのリンク -->
  </div>
{{ Form::close() }}    <!-- フォーム終了タグ -->
  • 一覧表示ページに、部分テンプレートを読み込む。
resources/views/index.blade.php
<p>{{ $message }}</p>
@section('content')
  @include('search')   <!-- ココで、検索フォームを読み込み -->
@endsection
  • 2. 次に、検索機能の実装。
    • コントローラーで、フォームから送られたデータを取得し、該当する投稿をビューに渡して表示させる。
app/Http/Controllers/ArticleController.php
public function index(Request $request){      // GETメソッドで受け取ったデータは、引数に指定した変数$requestで取得できる
  if ($request->filled('keyword')) {          // データの有無で条件分岐
    $keyword = $request->input('keyword');    // $request(フォームで送られたデータ) の中から、「keyword」を取得して、変数に定義
    $message = '検索結果: '.$keyword;          // ビュー表示のため、変数に格納
    $articles = モデル名::where('検索するカラム名', 'like', '%'.$keyword.'%')->get();
  }else{     // 未入力の場合
    $message = "検索キーワードを入力してください。";
    $articles = モデル名::all();              // 未入力なら、全データ表示
  }
  return view('検索結果を送りたいビュー名', ['message' => $message, 'articles' => $articles]);  // ビューに渡す
}

✔️ モデル名::where()

  • 指定条件で、配列を絞り込む。
検索条件の指定(where)
use Illuminate\Support\Arr;
$array = [100, '200', 300, '400', 500];
$filtered = Arr::where($array, function($value, $key) {
  return is_string($value);    // string型のみ取得。 [1=>'200', 3=>'400']
});

✔️ filled( )

  • 値が空でないか?の判定。
  • blank メソッドの逆の動作。
空でないか?の判定(filled関数)
hoge1 = 'value';
$request->filled(hoge1);   // true : キーが存在、値が空でない
$request->filled(hoge2);   // false : キーが存在しない or 空

参) artisan tinker (対話型コンソール)

  • artisan tinker:対話型コンソール
  • 手っ取り早く、コンソールからDBにデータを登録したい時などに便利。
対話型コンソールの使い方
% php artisan tinker     // 対話型コンソールの起動
>>> Article::all()       // 全データ取得

>>> $article = Article::find(1)   // 指定idのレコードを取得
>>> $article->content             // カラムを出力
>>> $article                      // レコード出力
>>> $article->delete()            // レコード削除

// レコード追加
>>> $article = new Article()      // インスタンス作成
>>> $article->content = 'テスト'    // 変数に格納
>>> $article->save()              // データ保存

>>> exit       // CTRL + C でもok

(おまけ) 関連づけたテーブルからのデータ取得

例)articlesテーブル、categoriesテーブルのデータを表示したい
// モデル にリレーションを定義 (例: Articleモデルは、Categoryモデルに対して、1対多の場合)
public function category() {
  return $this->belongsTo('App\Models\Category');
}

// ビュー( ※変数はコントローラーで定義 )
{{ $article -> name }}               // articlesテーブルのnameカラムを表示
{{ $article -> category -> name }}   // articlesテーブルに関連付けたcategoriesテーブルのnameカラムを表示

(おまけ) 関連づけたテーブルへのデータ保存

例)articlesコントローラーから、categoriesテーブルにデータ保存したい
// コントローラー
use App\Models\Category;   // Categoryモデルの使用を宣言
         :
public function create(){
  $categories = Category::all()->pluck('name', 'id');   // Categoryモデルから、nameとidカラムだけ取得 
  return view('new', ['article' => $article, 'categories'=> $categories] );
}
public function store(Request $request){
  $article = new Article();
  $article -> content = request('content');
  $article -> user_name = request('user_name');
  $article -> category_id = request('category_id');
  $article -> save();
  return redirect() -> route('article.detail', ['article' => $article->id] );
}

// ビュー
{{ Form::open(['route' => 'shop.store']) }}
  {{ Form::text('content', null) }}               // articlesテーブルへのデータ保存
  {{ Form::select('category_id', $categories) }}  // categoriesテーブルのデータ選択
  {{ Form::submit('登録') }}
{{ Form::close() }}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ISUCON10 Online event

こんにちは, This is Santosh.I participated in ISUCON2020 Online event, It was held on September 12,2020.
Rules and regulations are mentioned on the official sites. please go through once(~5 minutes)
http://isucon.net/archives/54704557.html
Around 500~ teams participated on that day, It was fun as my first isucon! let's move to the technical side,
There was an application given「isummo」、It was about finding chair(isu in japanese) by different conditions. The goal was to improve the search time and processing speed of isummo application.
issumo1.png
issumo2.png

lightly just go through the following;
http://sinatrarb.com/
・new relic
ruby version check-install,start mysql(may be it will helpful next time too)
installruby.png
dbSetting.png
If any login issue with mysql just try this or follow stackoverflow↓
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_passw

About coding language, you can choose any language Rust, Python, Go, Php or whatever you are comfortable with,I did with ruby as other team member were very familiar with it.Now the question is how to optimize our isummo app?
① Check all SQL queries -> Add index, as no index key was there
eg. SQL -> select * from isuumo.chair where kind = 'some value'
check kind is indexed or not ,if not -> KEY kind (kind)
adding index is easy as soon as you find the query, and it's fasten our app to some level.(rank was going up & team was enjoying)

② Index based on order BY ASC OR DESC
eg; select * from issumo.desk where some_condition order by popularity DESC;
Please not here, order by have column by DESC, and the default index we add earlier will be ASC .To speedup execution of query make it, DESC INDEX, -> KEY popularity (popularity DESC)
https://dev.mysql.com/doc/refman/8.0/en/descending-indexes.html
After this our app performance increased a bit.

③ Next, find n+1,source code and try to fix that,
this was a bit tough ,I tried but there was no such improvement in speed.
Other things can be done on infrastructure side,as other member was doing that part I don't have much idea about it. Next isucon will try to cover up infra as well.
Good luck everyone for next event!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bash+WSL+MySQLを使用したTXT形式ファイルのDB IMPORT

1.実行前

==USER IMPORTからEND==迄にUSERテーブルにINSERTしたいデータが格納されている

DATA_SAMPLE1.TXT
...
==USER IMPORT
ckhaorus      ckhaorus      pawpOMFPAOKWDDK41po2kldaodkPAOWK  
matchan      matchan      HcrUC/zoaeQjF4XLVQbYk1TkHjhHkEkD+z8DFtc9SrY=  
END==

==SCHEDULE IMPORT
2019/10/27 01:42:32
2019/10/27 02:15:16
...

2.処理内容

text_import_env.sh
#!/bin/bash

### ディレクトリ名
INPUT_DIR="/home/ckhaorus/csv-import/INPUT_DIR"
WORK_DIR="/home/ckhaorus/csv-import/WORK_DIR"

### MYSQL
MYSQL_HOST="127.0.0.1"
MYSQL_USER="root"
MYSQL_PWD="root"
MYSQL_DB="sample"

###返却値
END_STATUS=0 # 正常終了
INVALID_DIRECTORY_STATUS=10 # INPUT_DIR不正

function log(){
  echo -e "$(date '+%Y-%m-%dT%H:%M:%S') $1"
}

function infStr(){
  log "INFO SH10001[]: $1: START[]"
}

function infEnd(){
  log "INFO SH10002[]: $1: END[] STATUS CODE: $2"
  exit "$2"
}

function inputFileIsNotFoundErr(){
  log "ERROR SH20001[]: $1: INPUT FILE IS NOT FOUND."
}
text_import.sh
#!/bin/bash

### ファイル名
CONFIGURATION_FILE="text_import_env.sh"
BASEFILE_NAME="text_import.sh"
### ファイルパターン
INPUT_FILE_PATTERN=".*DATA_[A-Z]+[1-9].TXT.*"
DATA_START_PATTERN="==USER IMPORT"
DATA_END_PATTERN="END=="
LINE_NO_PATTERN="s/:.*//g"

# 設定ファイルの読み込み
source "${CONFIGURATION_FILE}"

# 処理の開始
infStr "${BASEFILE_NAME}"

# INPUT_DIR内にファイルが配置されているか確認
if [ ! "$(ls "${INPUT_DIR}")" ]; then
  # ファイルが配置されていない場合、ERRORログ「SH20001」: INPUT_DIR内にファイルが存在しないを表示し、シェルを終了する。
  inputFileIsNotFoundErr "${BASEFILE_NAME}"
  infEnd "${BASEFILE_NAME}" "${INVALID_DIRECTORY_STATUS}"
fi

# WORK_DIR内の全ディレクトリ・ファイルに対して削除を行い、WORK_DIRを初期化する。
rm -rf "${WORK_DIR:?}/"*

# INPUT_DIRからFILE_PATTERNにマッチする全ファイルに対して、WORK_DIRに移動する。
find "${INPUT_DIR}" -type f -regextype posix-egrep -regex "${INPUT_FILE_PATTERN}" | xargs -I% mv % "${WORK_DIR}"

# WORK_DIR内のFILE_PATTERNにマッチする全ファイルに対してループ
for filepath in $(find "${WORK_DIR}" -type f -regextype posix-egrep -regex "${INPUT_FILE_PATTERN}"); do
  # ファイル名取得
  base_filename=$(basename "${filepath}")
  # 出力ファイル名取得
  data_filename=$(echo "${base_filename}" | cut -d "." -f 1)"_USER.TXT"
  # データの開始~終了行取得
  start_line=$(grep -no "${DATA_START_PATTERN}" "$filepath" | sed -e "${LINE_NO_PATTERN}")
  end_line=$(grep -no "${DATA_END_PATTERN}" "$filepath" | sed -e "${LINE_NO_PATTERN}")
  # 行番号指定でデータを抜き出し、<元ファイル名_USER.TXT>に出力する。
  sed -n "${start_line},${end_line}p" "$filepath" > "${WORK_DIR}/${data_filename}"
done

# user_import.shを起動
bash user_import.sh

# WORK_DIR内の全ディレクトリ・ファイルに対して削除を行い、WORK_DIRを初期化する。
rm -rf "${WORK_DIR:?}/"*

# 処理の終了
infEnd "${BASEFILE_NAME}" "${END_STATUS}"
user_import.sh
#!/bin/bash

### ファイル名
CONFIGURATION_FILE="text_import_env.sh"
BASEFILE_NAME="user_import.sh"

### ファイルパターン
USER_FILE_PATTERN=".*DATA_[A-Z]+[1-9]_USER.TXT.*"

### テーブル名
TEMP_TABLENAME="temp_user"

# 環境設定ファイルの読み込み
source "${CONFIGURATION_FILE}"

# WORK_DIR内のFILE_PATTERNにマッチする全ファイルに対してループ
for filepath in $(find "${WORK_DIR}" -type f -regextype posix-egrep -regex "${USER_FILE_PATTERN}"); do
  while read line; do
    if [[ ${line:0:1} =~ [a-z] ]]; then
      # ファイル名取得
      base_filename=$(basename "$filepath")
      # 出力ファイル名取得
      csv_filename=$(echo "${base_filename}" | cut -d "." -f 1)".CSV"
      # 各カラムの取得
      login_id=$(echo "${line}" | awk '{print $1}')
      name=$(echo "${line}" | awk '{print $2}')
      password=$(echo "${line}" | awk '{print $3}')
      # TODO:LOAD DATA LOCAL INFILE文が使用不可
      # echo "\"${login_id}\",\"${name}\",\"${password}\"" >> "${WORK_DIR}/${csv_filename}"
      # temp_userテーブルに対してINSERTを行う。
      mysql -h "${MYSQL_HOST}" -u"${MYSQL_USER}" -p"${MYSQL_PWD}" --database="${MYSQL_DB}" -e "INSERT INTO ${TEMP_TABLENAME} (login_id, name, password) VALUES ('${login_id}', '${name}', '${password}');"
    fi    
  done < "${filepath}"
done

3.出力

正常系.sh
2020-10-27T07:56:46 INFO SH10001[]: text_import.sh: START[]
2020-10-27T07:56:47 INFO SH10002[]: text_import.sh: END[] STATUS CODE: 0
異常系.sh
2020-10-27T07:57:26 INFO SH10001[]: text_import.sh: START[]
2020-10-27T07:57:26 ERROR SH20001[]: text_import.sh: INPUT FILE IS NOT FOUND.
2020-10-27T07:57:26 INFO SH10002[]: text_import.sh: END[] STATUS CODE: 10
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

冴えない彼氏のKH-coderの使い方(Mac編)

はじめに

 KH-coderをMacで使おうと思いたち、インストールしようとしたのである。その際、参考となるサイトが少ないことに辟易し、ここにその方法を記そうと思い立った次第である。
(決してcpanmをclean upしようとして、sudo rm -rfして 3ヶ月間のデータを失ったからでない←聞かれてない)

後、タイトルに関しては察してくれ。

実行環境

MacBook Pro(15-inch,2018)
プロセッサ 2.9 Ghz Intel Core i9
メモリ 32GB 2400 MHz DDR4
OS:macOS Mojave(ver.10.14.6)

前準備

X-code,Homebrewをインストールできていない場合

X-codeのインストール

xcode-select --install

Homebrewをインストール

Homebrewの公式サイト(https://brew.sh/index_ja.html) にインストールコマンドが掲載されているので、それに載っているスクリプトをターミナルを開き実行。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

インストール後の確認

brew doctor #インストール完了後の確認
brew --version

HomebrewでGitをインストール

brew install git

準備

XQuartzをインストール

このサイト(https://www.xquartz.org/index.html )のdmgをインストール

Stanford-Posttaggerをインストール

Stanford Log-linear Part-Of-Speech Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)の公式サイトからインストールする。

openjdkのインストール

brew install openjdk13

mecabのインストール

brew install mecab
brew install mecab-ipandic

mysqlのインストール

brew install mysql

インストール終了後

brew services start mysql
==> Successfully started `mysql` (label: homebrew.mxcl.mysql)
#成功すると上記のように表示される

起動後、次のコマンドを実行しパスワードを設定する。
コマンドを実行すると、最初にVALIDATE PASSWORD PLUGINという強固なパスワード設定を助けるプラグインを使用するかどうかを質問される。
(今回私はローカルでの使用しか想定していため、何も入力しなかった。)

次にrootユーザーのパスワードを設定する

mysql_secure_installation

(今回はパスワードを設定せず、次に進んだ)

mysql -u root

これで

(base) user:~ user$ mysql -u root

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 8.0.22 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

となれば成功。

mysqlの起動,終了

起動

brew services start mysql

もしくは

mysql.server start

####終了
```bash
brew services stop mysql

もしくは

mysql.server stop

Rのインストール

brew cask  install R

R単体と同時にRstudio(https://rstudio.com/products/rstudio/ )も一緒にインストールした

以下KH-coderを動かすために必要なパッケージ

install.packages("ade4", dependencies=TRUE)
install.packages("amap", dependencies=TRUE)
install.packages("Cairo", dependencies=TRUE)
install.packages("cluster", dependencies=TRUE)
install.packages("codetools", dependencies=TRUE)
install.packages("colorspace", dependencies=TRUE)
install.packages("dichromat", dependencies=TRUE)
install.packages("foreign", dependencies=TRUE)
install.packages("ggdendro", dependencies=TRUE)
install.packages("ggplot2", dependencies=TRUE)
install.packages("ggnetwork", dependencies=TRUE)
install.packages("ggsci", dependencies=TRUE)
install.packages("gtable", dependencies=TRUE)
install.packages("igraph", dependencies=TRUE)
install.packages("KernSmooth", dependencies=TRUE)
install.packages("lattice", dependencies=TRUE)
install.packages("maptools", dependencies=TRUE)
install.packages("MASS", dependencies=TRUE)
install.packages("Matrix", dependencies=TRUE)
install.packages("mgcv", dependencies=TRUE)
install.packages("munsell", dependencies=TRUE)
install.packages("nlme", dependencies=TRUE)
install.packages("nnet", dependencies=TRUE)
install.packages("permute", dependencies=TRUE)
install.packages("pheatmap", dependencies=TRUE)
install.packages("plyr", dependencies=TRUE)
install.packages("proto", dependencies=TRUE)
install.packages("RcolorBrewer", dependencies=TRUE)
install.packages("Rcpp", dependencies=TRUE)
install.packages("reshape2", dependencies=TRUE)
install.packages("rgl", dependencies=TRUE)
install.packages("rpart", dependencies=TRUE)
install.packages("scales", dependencies=TRUE)
install.packages("scatterplot3d", dependencies=TRUE)
install.packages("slam", dependencies=TRUE)
install.packages("som", dependencies=TRUE)
install.packages("sp", dependencies=TRUE)
install.packages("spatial", dependencies=TRUE)
install.packages("stringr", dependencies=TRUE)
install.packages("survival", dependencies=TRUE)
install.packages("vegan", dependencies=TRUE)
install.packages("wordcloud", dependencies=TRUE)

perlのインストール

anyenvのインストール

brew install anyenv
echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
exec $SHELL -l
anyenv install --init

anyenvのPluginのインストール

mkdir -p ~/.anyenv/plugins #anyenvのプラグインをインストールする「plugins」ディレクトリの作成

git clone https://github.com/znz/anyenv-update.git ~/.anyenv/plugins/anyenv-update
#anyenv updateコマンドでanyenvで入れた**env系の全てをアップデートしてくれるプラグイン

git clone https://github.com/znz/anyenv-git.git ~/.anyenv/plugins/anyenv-git
#anyenv gitコマンドでanyenvで入れた**env系の全てのgitコマンドを実行するプラグイン

plenv

anyone install plenv
exec  $SHELL -l

perlのインストール

plenv install (インストールしたいperl-version)
exec  $SHELL -l
plenv global (インストールしたperl-version)

cpanmをインストール

plenv install-cpanm
exec  $SHELL -l

cpanモジュールをアンインストール

cpanm Unicode::Japanese
exec  $SHELL -l

後はKH-coderに必要なもの(https://github.com/ko-ichi-h/khcoder/issues/91 )をただインストールするだけ

cpanm Jcode*
cpanm Tk*
cpanm DBI
cpanm DBD::CSV (allow testing, say “y”)
cpanm File::BOM*
cpanm Lingua::JA::Regular::Unicode (needs sudo)*
cpanm Net::Telnet
cpanm Excel::Writer::XLSX
cpanm DBD::mysql
cpanm Spreadsheet::ParseExcel::FmtJapan
cpanm Spreadsheet::ParseXLSX
cpanm Statistics::ChisqIndep*
cpanm Statistics::Lite*
cpanm Unicode::Escape*
cpanm Algorithm::NaiveBayes*
cpanm Lingua::Sentence*
cpanm Proc::Background

このperlパッケージをインストールするところが準備段階で一番ハマる(私はPCのデータを全て吹き飛ばしたw)。

以下ハマったこと(cpanm)

cpanm DBD::mysql

とした時に以下のエラーメッセージが出た

Can't link/include C library 'ssl', 'crypto', aborting.

このサイト( https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql )を参考にして、以下のように対応した

cpanm DBD::mysql --configure-args="--libs='-L/usr/local/opt/openssl/lib -lssl -lcrypto -L/usr/local/lib -lmysqlclient'"

KH-coderのインストール

git clone https://github.com/ko-ichi-h/khcoder.git

KH-coder起動

mysql起動

ターミナルからmysqlを起動する

mysql.server start mysql
mysql -u root

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 8.0.22 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SET GLOBAL local_infile=on; #これがないとmysqlに接続できないエラーが起きる
Query OK, 0 rows affected (0.00 sec)
SET GLOBAL local_infile=on; 

KH-coder 起動

configにあるconfig.iniを以下のように書き換える

sql_username    root
sql_password    #今回は設定しなかった

XQuartzを起動し、KH-coderのkh_coder.plのある場所に移動する。

cd (kh_coder.plのある場所)
perl kh_coder.pl

プロジェクトの設定を開き以下の画面が出てくる
スクリーンショット 2020-10-26 23.31.49.png

日本語がMeCabしか使えない上(少なくとも茶筌が見つからなかった)、辞書コードはutf-8であるため
utf-8
と設定する

Stanford POS Taggerの設定を開き
.jarファイル、.taggerファイルを設定する

後はKH-coderの公式のチュートリアルデータ(https://khcoder.net/tutorial.html )でどのエラー画面が起きるかテストする。

日本語入力について

XQuartzは日本語を入力できないため、適当なテキストエディタに日本語を書き、ctr+vで貼り付ける。

最後に

KH-coderを使うときはWindowsでやろう。絶対それが楽。

参考にしたもの

KH-coder公式

KH-coder GitHub (https://github.com/ko-ichi-h/khcoder)

MacでKH-coderインストールした方々の備忘録

池田大輔氏のMacでのKH Coderのインストール(https://researchmap.jp/blogs/blog_entries/view/75043/1665e5f4b34a161b470604b59bf39e12?frame_id=616670)
@y-urakawa 氏の【Mac】KH Coder導入手順(https://qiita.com/y-urakawa/items/0423bf6775de8e6305f3)

mysql関連

progarteのMySQLの開発環境を用意しよう(macOS)(https://prog-8.com/docs/mysql-env)

mita2db氏のMySQL 8.0 の LOAD DATA で The used command is not allowed with this MySQL version エラー(https://mita2db.hateblo.jp/entry/2020/01/13/163218)

perl関連

@rinpa 氏のanyenv + macOS環境構築(https://qiita.com/rinpa/items/81766cd6a7b23dea9f3c)

@laikuaut 氏のplenvの基本メモ書き(https://qiita.com/laikuaut/items/82c9b402920e30c2c4eb)

みつと氏のmacOS(mojave)にDBD::mysqlをインストールしたいっ!(https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql)

KH-coder module関連

Stanford Pos Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

冴えない彼氏のKH-coderの使い方(Mac導入編)

はじめに

 KH-coderをMacで使おうと思いたち、インストールしようとしたのである。その際、参考となるサイトが少ないことに辟易し、ここにその方法を記そうと思い立った次第である。
(決してcpanmをclean upしようとして、sudo rm -rfして 3ヶ月間のデータを失ったからでない←聞かれてない)
今回は導入編であるので、具体的な解析については他のサイトを読んでほしい。

後、タイトルに関しては察してくれ。

実行環境

MacBook Pro(15-inch,2018)
プロセッサ 2.9 Ghz Intel Core i9
メモリ 32GB 2400 MHz DDR4
OS:macOS Mojave(ver.10.14.6)

前準備

X-code,Homebrewをインストールできていない場合

X-codeのインストール

xcode-select --install

Homebrewをインストール

Homebrewの公式サイト(https://brew.sh/index_ja.html) にインストールコマンドが掲載されているので、それに載っているスクリプトをターミナルを開き実行。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

インストール後の確認

brew doctor #インストール完了後の確認
brew --version

HomebrewでGitをインストール

brew install git

準備

XQuartzをインストール

このサイト(https://www.xquartz.org/index.html )のdmgをインストール

Stanford-Posttaggerをインストール

Stanford Log-linear Part-Of-Speech Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)の公式サイトからインストールする。

openjdkのインストール

brew install openjdk13

mecabのインストール

brew install mecab
brew install mecab-ipandic

mysqlのインストール

brew install mysql

インストール終了後

brew services start mysql
==> Successfully started `mysql` (label: homebrew.mxcl.mysql)
#成功すると上記のように表示される

起動後、次のコマンドを実行しパスワードを設定する。
コマンドを実行すると、最初にVALIDATE PASSWORD PLUGINという強固なパスワード設定を助けるプラグインを使用するかどうかを質問される。
(今回私はローカルでの使用しか想定していため、何も入力しなかった。)

次にrootユーザーのパスワードを設定する

mysql_secure_installation

(今回はパスワードを設定せず、次に進んだ)

mysql -u root

これで

(base) user:~ user$ mysql -u root

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 8.0.22 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

となれば成功。

mysqlの起動,終了

起動

brew services start mysql

もしくは

mysql.server start

####終了
```bash
brew services stop mysql

もしくは

mysql.server stop

Rのインストール

brew cask  install R

R単体と同時にRstudio(https://rstudio.com/products/rstudio/ )も一緒にインストールした

以下KH-coderを動かすために必要なパッケージ

install.packages("ade4", dependencies=TRUE)
install.packages("amap", dependencies=TRUE)
install.packages("Cairo", dependencies=TRUE)
install.packages("cluster", dependencies=TRUE)
install.packages("codetools", dependencies=TRUE)
install.packages("colorspace", dependencies=TRUE)
install.packages("dichromat", dependencies=TRUE)
install.packages("foreign", dependencies=TRUE)
install.packages("ggdendro", dependencies=TRUE)
install.packages("ggplot2", dependencies=TRUE)
install.packages("ggnetwork", dependencies=TRUE)
install.packages("ggsci", dependencies=TRUE)
install.packages("gtable", dependencies=TRUE)
install.packages("igraph", dependencies=TRUE)
install.packages("KernSmooth", dependencies=TRUE)
install.packages("lattice", dependencies=TRUE)
install.packages("maptools", dependencies=TRUE)
install.packages("MASS", dependencies=TRUE)
install.packages("Matrix", dependencies=TRUE)
install.packages("mgcv", dependencies=TRUE)
install.packages("munsell", dependencies=TRUE)
install.packages("nlme", dependencies=TRUE)
install.packages("nnet", dependencies=TRUE)
install.packages("permute", dependencies=TRUE)
install.packages("pheatmap", dependencies=TRUE)
install.packages("plyr", dependencies=TRUE)
install.packages("proto", dependencies=TRUE)
install.packages("RcolorBrewer", dependencies=TRUE)
install.packages("Rcpp", dependencies=TRUE)
install.packages("reshape2", dependencies=TRUE)
install.packages("rgl", dependencies=TRUE)
install.packages("rpart", dependencies=TRUE)
install.packages("scales", dependencies=TRUE)
install.packages("scatterplot3d", dependencies=TRUE)
install.packages("slam", dependencies=TRUE)
install.packages("som", dependencies=TRUE)
install.packages("sp", dependencies=TRUE)
install.packages("spatial", dependencies=TRUE)
install.packages("stringr", dependencies=TRUE)
install.packages("survival", dependencies=TRUE)
install.packages("vegan", dependencies=TRUE)
install.packages("wordcloud", dependencies=TRUE)

perlのインストール

anyenvのインストール

brew install anyenv
echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
exec $SHELL -l
anyenv install --init

anyenvのPluginのインストール

mkdir -p ~/.anyenv/plugins #anyenvのプラグインをインストールする「plugins」ディレクトリの作成

git clone https://github.com/znz/anyenv-update.git ~/.anyenv/plugins/anyenv-update
#anyenv updateコマンドでanyenvで入れた**env系の全てをアップデートしてくれるプラグイン

git clone https://github.com/znz/anyenv-git.git ~/.anyenv/plugins/anyenv-git
#anyenv gitコマンドでanyenvで入れた**env系の全てのgitコマンドを実行するプラグイン

plenv

anyone install plenv
exec  $SHELL -l

perlのインストール

plenv install (インストールしたいperl-version)
exec  $SHELL -l
plenv global (インストールしたperl-version)

cpanmをインストール

plenv install-cpanm
exec  $SHELL -l

cpanモジュールをアンインストール

cpanm Unicode::Japanese
exec  $SHELL -l

後はKH-coderに必要なもの(https://github.com/ko-ichi-h/khcoder/issues/91 )をただインストールするだけ

cpanm Jcode*
cpanm Tk*
cpanm DBI
cpanm DBD::CSV (allow testing, say “y”)
cpanm File::BOM*
cpanm Lingua::JA::Regular::Unicode (needs sudo)*
cpanm Net::Telnet
cpanm Excel::Writer::XLSX
cpanm DBD::mysql
cpanm Spreadsheet::ParseExcel::FmtJapan
cpanm Spreadsheet::ParseXLSX
cpanm Statistics::ChisqIndep*
cpanm Statistics::Lite*
cpanm Unicode::Escape*
cpanm Algorithm::NaiveBayes*
cpanm Lingua::Sentence*
cpanm Proc::Background

このperlパッケージをインストールするところが準備段階で一番ハマる(私はPCのデータを全て吹き飛ばしたw)。

以下ハマったこと(cpanm)

cpanm DBD::mysql

とした時に以下のエラーメッセージが出た

Can't link/include C library 'ssl', 'crypto', aborting.

このサイト( https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql )を参考にして、以下のように対応した

cpanm DBD::mysql --configure-args="--libs='-L/usr/local/opt/openssl/lib -lssl -lcrypto -L/usr/local/lib -lmysqlclient'"

KH-coderのインストール

git clone https://github.com/ko-ichi-h/khcoder.git

KH-coder起動

mysql起動

ターミナルからmysqlを起動する

mysql.server start mysql
mysql -u root

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 8.0.22 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SET GLOBAL local_infile=on; #これがないとmysqlに接続できないエラーが起きる
Query OK, 0 rows affected (0.00 sec)
SET GLOBAL local_infile=on; 

KH-coder 起動

configにあるconfig.iniを以下のように書き換える

sql_username    root
sql_password    #今回は設定しなかった

XQuartzを起動し、KH-coderのkh_coder.plのある場所に移動する。

cd (kh_coder.plのある場所)
perl kh_coder.pl

プロジェクトの設定を開き以下の画面が出てくる
スクリーンショット 2020-10-26 23.31.49.png

日本語がMeCabしか使えない上(少なくとも茶筌が見つからなかった)、辞書コードはutf-8であるため
utf-8
と設定する

Stanford POS Taggerの設定を開き
.jarファイル、.taggerファイルを設定する

後はKH-coderの公式のチュートリアルデータ(https://khcoder.net/tutorial.html )でどのエラー画面が起きるかテストする。

日本語入力について

XQuartzは日本語を入力できないため、適当なテキストエディタに日本語を書き、ctr+vで貼り付ける。

最後に

KH-coderを使うときはWindowsでやろう。絶対それが楽。

参考にしたもの

KH-coder公式

KH-coder GitHub (https://github.com/ko-ichi-h/khcoder)

MacでKH-coderインストールした方々の備忘録

池田大輔氏のMacでのKH Coderのインストール(https://researchmap.jp/blogs/blog_entries/view/75043/1665e5f4b34a161b470604b59bf39e12?frame_id=616670)
@y-urakawa 氏の【Mac】KH Coder導入手順(https://qiita.com/y-urakawa/items/0423bf6775de8e6305f3)

mysql関連

progarteのMySQLの開発環境を用意しよう(macOS)(https://prog-8.com/docs/mysql-env)

mita2db氏のMySQL 8.0 の LOAD DATA で The used command is not allowed with this MySQL version エラー(https://mita2db.hateblo.jp/entry/2020/01/13/163218)

perl関連

@rinpa 氏のanyenv + macOS環境構築(https://qiita.com/rinpa/items/81766cd6a7b23dea9f3c)

@laikuaut 氏のplenvの基本メモ書き(https://qiita.com/laikuaut/items/82c9b402920e30c2c4eb)

みつと氏のmacOS(mojave)にDBD::mysqlをインストールしたいっ!(https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql)

KH-coder module関連

Stanford Pos Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS入門】RDSによるMySQL環境の構築

【AWS入門】EC2インスタンス上に.NET Core + nginx環境を構築の続きです。

今回はRDSを使用したMySQLインスタンスをAWS上に構築し、EC2インスタンス上に配置されたWebアプリから接続するところまでをご紹介しようと思います。

環境

MySQL 8.0.20

RDSインスタンスの作成

まずはRDSインスタンスを作成していきましょう。
以下手順でRDSのコンソールを開き、データベースの作成メニューを開いてください。

  1. AWSマネジメントコンソールにログイン
  2. 「サービス」の「すべてのサービス」にRDSを入力
  3. RDSへのリンクが表示されるので押下
  4. RDSコンソールが表示されるので、「データベースの作成」を押下

以降各セクションごとに説明します。


データベースの作成方法

簡単作成でも問題ありませんが、今回は練習の意味で標準作成を選択します。
スクリーンショット 2020-10-21 3.32.21.png

エンジンのオプション

MySQLを選択します。
バージョンは8.0.20にしました。
スクリーンショット 2020-10-26 19.21.14.png

テンプレート

今回は勉強用なので、無料利用枠を使用します。
スクリーンショット 2020-10-21 3.31.23.png

設定

MySQLに接続するマスターユーザの情報を入力します。
今回はインスタンス名をasp-net-app-db、ユーザ名はデフォルトのadminに設定しました。
パスワードは適宜設定してください。

また、ユーザ名とパスワードは後ほど接続文字列を設定する際に利用するので、どこかに書き留めておいてください。
スクリーンショット 2020-10-26 19.27.01.png

DB インスタンスサイズ

無料枠なのでdb.t2.microのみ有効です。
そのまま進みましょう。
スクリーンショット 2020-10-21 3.31.43.png

ストレージ

ストレージタイプ、ストレージ割り当てはそれぞれデフォルト値でOKです。
ストレージの自動スケーリングについては、万が一なにか起こった場合にスケールされるのを防ぐため、一応チェックを外しておきました。
スクリーンショット 2020-10-26 19.35.25.png

可用性と耐久性

このセクションは無料枠の場合特に設定できる項目がないので次に進みます。

接続

RDSのインスタンスを配置するVPCを選択してください。
要件として、VPC内に別々のアベイラビリティゾーンに属するサブネットが最低2つ必要です。
スクリーンショット 2020-10-21 3.33.08.png

次に、追加の接続設定を編集していきます。

設定名 設定値
サブネットグループ デフォルト値
パブリックアクセス可能 なし
VPCセキュリティグループ 事前に作成したセキュリティグループ
アベイラビリティゾーン 指定なし
データベースポート 3306

※VPCセキュリティグループについてはポート3306が開いている必要があります。

スクリーンショット 2020-10-26 19.50.19.png

データベース認証

パスワード認証を設定します。
スクリーンショット 2020-10-21 3.35.05.png

追加設定

最初のデータベース名にasp_net_sampleを入力し、それ以外は全て無効に設定します。

スクリーンショット 2020-10-21 3.36.07.png
スクリーンショット 2020-10-21 3.36.28.png


設定後、データベースの作成を押下するとインスタンスの作成が開始されます。

接続文字列をServiceの環境変数に記述

最後に前回作成したアプリケーションからRDSインスタンスを参照できるように、Serviceの環境変数を編集します。

EC2インスタンスにSSH後、以下コマンドを実行します。

$ sudo vim /etc/systemd/system/kestrel-asp-net-app.service

#Environment=ASPNETCORE_ENVIRONMENT=Production以下に追加
Environment=DB_CONNECTION_STRING=server=サーバのエンドポイント;uid=admin;pwd=パスワード;database=asp_net_sample
Environment=ELASTIC_SEARCH_SERVER=http://localhost:9200

設定後、以下コマンドを実行します。

$ sudo service nginx restart

確認

ローカルマシンにてhttp://サーバIP/をブラウザで開いてください。
RDSインスタンスが作成されたばかりでテーブルが存在していないので、環境の初期化を押下します。
以下のような画面が表示されれば正常に動作しています。
スクリーンショット 2020-10-27 0.05.05.png

次回

Elasticsearchと連携 -> 作成中...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む