20200802のMySQLに関する記事は4件です。

【AWS】EC2 MySQL パスワードエラーを解決(Rails アプリデプロイ時)

はじめに

(下準備編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
こちらの記事を参考にさせていただき、Rails のオリジナルアプリをAWSを利用しデプロイしようと試みました。

すると、こちらの記事
(デプロイ編②)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
Ruby

デプロイ編②の「MySQLの設定」でかなり躓いたのでエラー内容と解決した方法を記述する。

エラー内容から解決までの簡単な流れ

ターミナルでEC2インスタンスにログインし、MySQLを起動した状態で

  1. rake db:create RAILS_ENV=productionを実行 → 「パスワードがないから、アクセスできないよ」とエラーがでる
  2. コマンドを実行しパスワードを確認する
  3. 確認したパスワードを database.yml に記述する
  4. 再度rake db:create RAILS_ENV=productionを実行 → 「パスワードが有効期限切れだよ」とエラーがでる
  5. パスワードを変更する
  6. 変更したパスワードを database.yml に記述する
  7. rake db:create RAILS_ENV=production 実行できた!!

以下でもう少し詳細に説明する

解決のためにやったことを説明

1. rake db:create RAILS_ENV=productionを実行

$ rake db:create RAILS_ENV=production
# エラー文
Access denied for user 'root'@'localhost' (using password: NO)

「パスワードがないからアクセスできないよ」と拒否される。

確かに database.yml の production環境記述 password の部分は空欄になっていた

database.yml
production:
  <<: *default
  database: thankshabit_production
  username: root
  password:           # ←ここが空欄

2. コマンドを実行しパスワードを確認する

パスワードがわからないので、こちらのコマンドを実行しパスワードを確認する

$ sudo cat /var/log/mysqld.log | grep 'temporary password'
[Note] A temporary password is generated for root@localhost: XXXXXX

このコマンドを実行し表示された最後の部分「XXXXX」がパスワード。
この部分を database.yml に記述する

3. 確認したパスワードを database.yml に記述する

アプリのディレクトリにいる状態で

$ vi config/database.yml

を実行し、確認したパスワードを database.yml に記載する

database.yml
production:
  <<: *default
  database: thankshabit_production
  username: root
  password: XXXXX # ←ここに記述 

4. 再度rake db:create RAILS_ENV=productionを実行

再度実行したらまた別のエラーがでる

$ rake db:create RAILS_ENV=production
# エラー文
Mysql2::Error: Your password has expired. To log in you must change it using a client that supports expired passwords.

「パスワードが有効期限切れだから、変更する必要がある」と言われている。

5. パスワードを変更する

コマンドを実行し、パスワードを変更する

$ sudo mysql_secure_installation
NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!
In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.
Enter current password for root (enter for none): 

Enter を押す

OK, successfully used password, moving on...
Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.
Set root password? [Y/n] y

パスワードを設定するかどうか聞かれているため、「y」+「Enter」

New password: 
Re-enter new password: 
Password updated successfully!
Reloading privilege tables..
 ... Success!

新しいパスワードを確認含めて2回入力する
ここで注意なのですが、このパスワードの文字制約がかなり厳しい
筆者はこちらなどを利用して制約をクリアするパスワードを設定した。

Remove anonymous users? [Y/n] y
 ... Success!

Disallow root login remotely? [Y/n] y
 ... Success!

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reload privilege tables now? [Y/n] y
 ... Success!
Cleaning up...

全てに「y」+「Enter」で返していく

これでパスワードの新規設定が完了!!

6. 変更後のパスワードを database.yml に記述する

アプリのディレクトリにいる状態で

$ vi config/database.yml

を実行し、先ほど新規に設定したパスワードを記述し直す

database.yml
production:
  <<: *default
  database: thankshabit_production
  username: root
  password: XXXXX # ←ここに新パスワードを記述し直す 

7. rake db:create RAILS_ENV=production 実行

$ rake db:create RAILS_ENV=production 

無事実行できた!!

最後に

無事にこのつまずきを乗り越えてAWSでデプロイできました。色々な記事などを参考にさせていただきRailsアプリを無事にデプロイできました。
DBについてもネットワークやインフラ面についても理解はまだ浅いですが非常に勉強になりました。

参考にさせていただいた記事、サイトには本当に感謝しかないです。
ありがとうございました。

今回エラー解決に参考にさせていただいた記事↓↓ ありがとうございました!!

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

PHPでポートフォリオ 開発(イベント管理アプリ)

記事の概要

私が開発したWebアプリ(ポートフォリオ)の解説をします。
開発にいたる背景から、作成手順、機能、工夫したところ、課題などをまとめました。

開発したWebアプリはEventRunnerにて公開中。

以下のテストユーザーでログインできます。↓↓
アドレス :test0001@gmail.com
パスワード:test0001

開発に必要な設計書や要件定義書は以下のリンクからご覧ください。
GitHub
設計書URL

開発の背景

私は、学生の頃から兵庫県に拠点を置く、ボランティア団体に所属しています。
この団体では、2ヶ月に1回のペースで、行事(地域奉仕活動やワークショップなど)が行われます。

それらの行事への参加者を募るために、毎回SNSで情報を共有し、出欠をとり、参加者をSNSグループに招待し、運営していました。

これが、かなり手間で、特に取りまとめする人「リーダー」の仕事量がかなり多く大変でした。
(私は、この「リーダー」の経験があり、とても大変だったと記憶しております)

行事の情報収集や参加表明などができるプラットフォームがあり、
「個人で、情報収集や登録などができれば良いなぁ〜〜〜」と思い、
イベントの参加や登録ができるWebアプリを作ってみることにしました。

また、Web系開発企業のバックエンドエンジニアへの転職を考えていたので、
このアプリを転職活動のポートフォリオとすることに決めました。

目的

  • PHPを用いたWebアプリケーション開発経験を積む
  • フルスクラッチ開発によってWebアプリの基本的な構成、動作を知る
  • Webの仕組みの理解を深めて、自分の言葉でWebについて説明できるようになる
  • 企画〜開発〜運用までを経験することで、アプリケーション開発の流れを掴む
  • 成果品はポートフォリオとして、転職活動で使う
  • 開発アプリによって所属団体の行事運営の円滑化を計る

スペック (as of 2020/08/01)

言語
PHP 7.2.31

DBMS
MySQL 7.4.6

CSSフレームワーク
bootstrap4

開発環境
MacOS Catalina 10.15.6
Apache 2.4.41

バージョン管理
Git 2.27.0

本番環境
Conoha

主な機能

  • 開催イベントの登録
    開催される基本情報を登録・公開することができる(開催場所・時間・概要など)。

  • 参加表明
    ログイン済みの参加者が、「出席」ボタンを押すと、参加者として登録される。
    また、参加の取り消しをすることもできる。

  • トップページでは、本サイトの使い方を簡単に記載しておく

  • マイページで参加予定のイベント詳細情報が閲覧できる

  • 絞り込み検索ができる
    新着・定員数で絞り込み検索ができる

テーブル設計

以下の通り、テーブルを設計した。
ゼロからテーブル設計するのは初めてだったため、解説動画や解説サイトで情報収集し、行った。

会員テーブル(users)

No カラム名 カラム名(日本語) 必須 AI 主キー
1 user_id 会員ID integer Yes Yes Yes
2 name 名前 string Yes
3 email メールアドレス string Yes
4 password パスワード integer Yes
5 img 写真 string Yes
6 introduction 紹介文 text Yes
7 status 会員ステータス integer Yes
8 create_datetime 登録日時 datetime Yes Yes
9 update_datetime 更新日 datetime Yes Yes

開催場所テーブル(location)

No カラム名 カラム名(日本語) 必須 AI 主キー
1 location_id 開催箇所id integer Yes Yes Yes
2 location 開催箇所名 string Yes
3 address 住所 string Yes
4 create_datetime 登録日時 datetime Yes Yes
5 update_datetime 更新日 datetime Yes Yes

イベントテーブル(events)

No カラム名 カラム名(日本語) 必須 AI 主キー
1 event_id イベントid integer Yes Yes Yes
2 user_id 会員ID(主催者) integer Yes
3 location_id 開催箇所id integer Yes
4 event_name 題名 string Yes
5 introduction 説明文 string Yes
6 date 開催日時 date Yes
7 time 開催時間 time Yes
8 create_datetime 作成日時 datetime Yes Yes
9 update_datetime 更新日時 datetime Yes Yes

参加者テーブル(paticipants)

No カラム名 カラム名(日本語) 必須 AI 主キー
1 paticipant_id 参加ステータスid integer Yes Yes Yes
2 user_id 会員ID integer Yes
3 event_id イベントID integer Yes
4 create_datetime 作成日時 datetime Yes Yes
5 update_datetime 更新日時 datetime Yes Yes

開発手順

1. 要件定義

まずは、開発するWebアプリの全体像を把握するため、以下のドキュメントを作成。

  • アプリ概要
  • 機能一覧
  • テーブル設計書
  • スケジュール

実際に、作成したものを以下のリンクからご覧ください。

設計書URL

アプリ概要には、開発にいたる背景や目的などをまとめた。

機能一覧には、Webアプリにつける機能をまとめた。

実際に、クライアントから依頼があったという想定で、進めるためスケジュールを作成。
納期まで、「2週間で完成させてクライアントに返す」、と想定した。

2. 環境選定

学習言語がPHPだったため、PHPでの開発を決める。
また、所有するラップトップにLAMP環境を構築したので、その環境を試したかった。
データベースは、スクールでMySQLの利用経験がある、かつ、情報量が多いことを考慮し、選んだ。

(開発途中で、Conohaサーバー内にCloud9を置いたので、開発後期ではCloud9を利用して開発した)

データベース設計

必要機能から、どんな情報を保存するか、それらをどう関連付けて管理するかを考えながらデータベース設計を行った。

コーディング

  • MVCモデルで開発をする。 繰り返しのプログラムをなるべく避け、ユーザー関数を利用するなどし、第三者にも理解しやすいソースコードを心がけた。
  • まずは、フロント部分を開発し全体のイメージを掴めるように開発した。
    ある程度、全体像が掴んでから、サーバーサイドの開発をした。

  • Gitを利用し、バージョン管理
    チーム開発を想定しているので、開発内容をissueで管理した。
    ひとつの課題を終えるごとに、都度、pull request/mergeなどを利用した。

(これは、gitコマンドの練習にもなって、本当によかったと思っています)

CSSフレームワーク実装

PHPを書くことに集中するため、
CSSフレームワーク「Bootstrap4」を使用し、フロントは最小限の労力で実装した。

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

本番環境へ(Conoha)

初めてのレンタルサーバーを契約し、作ったWebアプリを公開した。

SFTPとして、FileZillaを使用した。

公開鍵を設定し、セキュリティを高めた。

バージョン管理

GitHubにソースコードをシェアした。

工夫したところや感想

開発直後は、MVCの考え方を理解できずなかなか進まなかった。。。
どうなることかと心配したが、納期に間に合うように開発することができた。

工夫したことを以下に記録する。

実際の現場に近い状況で開発
実際の開発現場に近い状況で取り組みたかったので、開発に取り掛かる前に、
スクールのメンター先生に、現場の開発の流れを事前に情報収集し実行した。

メンター先生からのアドバイスとしては、以下があった。

  • フロント画面を先に作成し、客先承認をもらう。承認をもらったフロント部分には、PHPをなるべく書かない。
  • グループ開発なら、GitHubでバージョン管理(pull request/mergeなど)する。
  • グループ内の情報共有はSlack。

Gitでバージョン管理
グループ開発を想定し、gitHubでバージョン管理した。
開発中に遭遇した課題に対して、都度issueを発行し、その課題を解決することを目指した。

積極的にGitコマンドを利用することで、苦手だった、黒い画面でのやりとりへの苦手意識が少なくなった。

サーバー契約し、実際に公開する
当初は、一般公開までするつもりはなかった。
「自分のPCの中だけで、運用できて、ポートフォリオ になればいいなぁ」
くらいに考えていた。

開発していくに連れて、作ったWebアプリに愛着が湧いてきて、
「本番環境に置いて、みんなに見てもらいたい」
と思うようにり、

結果的には、サーバーを契約していた!!!

本番環境に置いたことで、web全体の仕組みの理解を深めることにつながったように思う。

開発後期では、Conohaサーバー内にCloud9を置いた。
それにより、直接サーバーのディレクトリを編集することができたので、作業が捗った。

実際に抱えている問題を解決する
やるなら、自分の身の回りにある課題を解決できるようなシステムを作りたいと思った。

10年近く所属している、団体で感じていた悩みから、このテーマを思いついた。

まだまだ、実運転するには、問題があるので、今後も継続して小さな問題点を解消していきたい。
実際に、使えるアプリになるまで作り込んでいきたい。

その他

スクールで学んだことを盛り込んだWebアプリができた。
ものづくりの楽しさに触れられたのが、一番大きな収穫だったと思う。

実際に開発したことで、Webの仕組みについて、理解が深まった。
レスポンスやリクエスト、サーバーや、IPアドレスなど、
言葉としてしか理解できていなかったことが、実体として理解することができた。

学習してスキルを身につければ身につけるほど、より洗練されたモノが作れるようになることが、
本当に、楽しい。

一方で、自分の苦手とすることや、今後の課題が明確になった。
以下で、課題について記述している。

課題点と今後の展望

  • フィードバックもらった箇所を改修する
    所属している団体の友人に、実際に使用感をフィードバックしてもらった。
    (フィードバック内容は、要件定義書の「今後の課題」に記載。)
    継続して、開発していく。

  • ひと昔前のWebサイトのよう。。。
    Bootstrapの力を借りて、なんとなく良い感じにできているが、、、
    PHPを書くことに集中しているため、フロントがダサい。。
    JavaScriptを学び、動きのあるWebアプリに改修していきたい。

  • 本当は、もっと実装したい機能がたくさんある。例えば、登録したイベントを編集できる機能とか、会員同士の友達登録(フォロー)など、、、、
    それらを実装していく

参考文献

HTMLクイックリファレンス
サル先生のGit入門
データベース設計入門#3 テーブル設計の手順【実戦形式で解説】


最後まで、読んでいただきありがとうございます。

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

MySQLインストール :CentOS7

備忘録。細かいところ忘れがち・・・

リポジトリ

公式からリポジトリのURLをチェック(yumインストールするため)
https://dev.mysql.com/downloads/repo/yum/
(豆:ubuntuは末尾が/apt/)

管理者権限
--> 上記で拾ってきたURLを http://dev.mysql.com/get/の後に入力
# yum localinstall http://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
-> 確認
# ls /etc/yum.repo.d | grep mysql
mysql-community.repo
mysql-community-source.repo
-> 2つ見つかればOK

yumでインストール

管理者権限
-> インストール実行
# yum install -y mysql-community-server

-> インストール確認
# yum list installed | grep mysql
-> パッケージの中身を覗いておきましょう!

起動設定

管理者権限
-> 起動
# systemctl start mysqld
-> 確認
# systemctl status mysqld
-> 自動起動設定
# systemctl enable mysqld

rootでログイン

管理者権限
-> 自動設定された仮パスワードを調べる
# cat /var/log/mysqld.log | grep root
-> ログイン
# mysql -u root -p -> 仮パスワードを入力 -> ログイン完了
-> rootパスワードの変更
mysql> SET PASSWORD = '{任意のパス}'; -> 8文字以上・英文字・数字・記号入り

(豆:パスは自動的にハッシュ化されます。)

パスワードポリシーの確認
mysql> show variables like 'validate_password%';
文字コードの確認
mysql> show variables like "chara%";

今回は初期設定でutf8mb4となっていました。
WS000030.JPG

ユーザー

-> 作成済みユーザー一覧
mysql> SELECT user, host FROM mysql.user;
-> ユーザー作成
mysql> CREATE user '{ユーザー名}'@'localhost' IDENTIFIED BY '{パスワード}';
-> ローカル接続のみユーザーの作成

お疲れさまでした!
LGTMお願いします!
モチベーション上がります(^^)

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

Google Analytics APIを使用してデータベースを作った話

はじめに

Google Analyticsとは,googleが提供するWedアクセス解析ツールです。
Wedサイトへの訪問者のアクセス地域,時間,使用デバイス,ブラウザなど様々な情報を取得できます。
GUIも充実しているのでそれだけでも十分といえば十分ですが,無料でAPIも提供されているので試しにAPI接続してデータベースを作ってみました。

API接続しMySQLへ保存

1.アナリティクス API を有効にする

Google API コンソールからAPIを有効にしておきます.

2.Google クライアント ライブラリをインストールする 

sudo pip install --upgrade google-api-python-client

3.実際に接続してみる

main関数に日付と取得したい情報を入力として,そのDataFrameを返す。

'''fetch_analytics_data.py'''

from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
import pandas as pd

SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
KEY_FILE_LOCATION = '<REPLACE_WITH_JSON_FILE>'
VIEW_ID = '<ID_NUMBER>'


def initialize_analyticsreporting():
    """Initializes an Analytics Reporting API V4 service object.

    Returns:
      An authorized Analytics Reporting API V4 service object.
    """
    credentials = ServiceAccountCredentials.from_json_keyfile_name(
        KEY_FILE_LOCATION, SCOPES)

    # Build the service object.
    analytics = build('analyticsreporting', 'v4', credentials=credentials)

    return analytics


def get_report(analytics, start_day, end_day, dimension_list):
    """Queries the Analytics Reporting API V4.

    Args:
      analytics: An authorized Analytics Reporting API V4 service object.
    Returns:
      The Analytics Reporting API V4 response.
    """
    return analytics.reports().batchGet(
        body={
            'reportRequests': [
                {
                    'viewId': VIEW_ID,
                    'dateRanges': [{'startDate': start_day, 'endDate': end_day}],
                    # 'metrics': [{'expression': 'ga:sessions'}],
                    'dimensions': dimension_list
                    #                         [{'name': 'ga:country'},
                    #                          {'name': 'ga:browser'},
                    #                          {'name': 'ga:city'},
                    #                          {'name': 'ga:sex'}
                    #                         ]
                }]
        }
    ).execute()


def fetch_columns_name(response):
    col_name = [i.lstrip('ga:') for i in list(list(list(response.values())[0][0].values())[0].values())[0]]
    #     col_name.append('visitor')
    return col_name


def split_dimensions(response, col_name):
    df = pd.json_normalize(response.get('reports'), sep=' ')["data rows"]
    df = pd.json_normalize(df[0])

    # split dimensions
    for i in range(len(df['dimensions'][0])):
        new_feature = [df['dimensions'][j][i] for j in range(df.shape[0])]
        feature_name = col_name[i]
        df[feature_name] = new_feature
    df.drop(["dimensions"], axis=1, inplace=True)

    # fetch visitor (not use now)
    # df["visitor"] = [int(list(df['metrics'][i][0].values())[0][0]) for i in range(df.shape[0])]
    df.drop(['metrics'], axis=1, inplace=True)

    return df

def main(start_day, end_day, dim_list):
    analytics = initialize_analyticsreporting()
    response = get_report(analytics, start_day, end_day, dim_list)
    col_name = fetch_columns_name(response)
    df = split_dimensions(response, col_name)
    return df

if __name__ == "main":
    main()

4.Databaseに書き出す

無料版では一度に引ける情報量に制限があるので,4回に分けて取得しています。

import pandas as pd
import fetch_analytics_data as fa
from database_connect import database_engine
import sys
from sqlalchemy import create_engine

# -----------------------------------------------------------------
dim_list1 = [
    # Geo Network
    {'name': 'ga:country'},
    {'name': 'ga:city'},
    {'name': "ga:continentId"},

    ## Platform of Device
    {'name': "ga:browser"},

    ## Users
    {'name': 'ga:userType'},
    {'name': 'ga:sessionCount'},
    {'name': 'ga:daysSinceLastSession'},
    # {'name': 'ga:userDefinedValue'},
    # {'name': 'ga:userBucket'},

    ## session
    {'name': 'ga:sessionDurationBucket'},

    ## Time
    {'name': 'ga:dateHourMinute'},
]

# ----------------------------------------------------------------
dim_list2 = [
    ## Geo Network
    {'name': 'ga:city'},

    ## Platform of Device
    # {'name':"ga:operatingSystem"},
    # {'name':'ga:mobileDeviceBranding'},
    # {'name':'ga:mobileDeviceModel'},
    # {'name':"ga:mobileInputSelector"},
    # {'name':"ga:mobileDeviceInfo"},
    # {'name':'ga:mobileDeviceMarketingName'},
    {'name': 'ga:deviceCategory'},

    ## Page Tracking
    {'name': 'ga:pagePath'},
    {'name': 'ga:exitPagePath'},
    {'name': 'ga:pageDepth'},
    # {'name':'ga:pageTitle'},

    ## Time
    {'name': 'ga:dateHourMinute'},
]

# ---------------------------------------------------------------
dim_list3 = [
    # Geo Network
    {'name': 'ga:city'},

    ## Traffic Sources
    {'name': "ga:referralPath"},
    # {'name': "ga:campaign"},  # all not set
    {'name': "ga:source"},
    {'name': "ga:medium"},
    {'name': "ga:sourceMedium"},
    {'name': "ga:hasSocialSourceReferral"},

    ## Time
    {'name': 'ga:dateHourMinute'},
]

# -----------------------------------------------------------------
dim_list4 = [
    ## Geo Network
    {'name': 'ga:city'},

    ## Platform of Device
    {'name': "ga:operatingSystem"},
    {'name': 'ga:mobileDeviceBranding'},
    {'name': 'ga:mobileDeviceModel'},
    {'name': "ga:mobileInputSelector"},
    {'name': "ga:mobileDeviceInfo"},

    ## Time
    {'name': 'ga:dateHourMinute'},
]

# -----------------------------------------------------------

def database_engine():
      db_url = '<Database_URL>'
      engine = create_engine(db_url)
      return engine

def main():
    start_day = sys.argv[1]
    end_day = sys.argv[1]
    df1 = fa.main(start_day, end_day, dim_list1)
    df2 = fa.main(start_day, end_day, dim_list2)
    df3 = fa.main(start_day, end_day, dim_list3)
    df4 = fa.main(start_day, end_day, dim_list4)
    # merge
    df = pd.merge(df1, df2, on=['city','dateHourMinute'], how='outer').merge(df3, on=['city','dateHourMinute'], how='outer').merge(df4, on=['city','dateHourMinute'], how='outer')
    en = database_engine()
    df.to_sql('analytics_table', con=en, if_exists='append', index=False)

if __name__ == '__main__':
    main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む