tag:crieit.net,2005:https://crieit.net/tags/MySQL/feed 「MySQL」の記事 - Crieit Crieitでタグ「MySQL」に投稿された最近の記事 2022-07-30T15:57:47+09:00 https://crieit.net/tags/MySQL/feed tag:crieit.net,2005:PublicArticle/18267 2022-07-30T15:43:22+09:00 2022-07-30T15:57:47+09:00 https://crieit.net/posts/PC-Modbus-2 サーバやPC上のプログラムでModbus機器からデータを取得 (2) <p>サーバやPC上のプログラムでModbus機器からデータを取得、その第二回です。<br /> 第一回では、Android上のModbusシミュレータからデータを取得するための、Perlスクリプトのサンプルを紹介しました。Perlでは問題があるという方は、他の言語で記述しても原理は変わらないはずなので、他の言語で実装してみてください。</p> <p><a href="https://crieit.net/posts/PC-Modbus-1">サーバやPC上のプログラムでModbus機器からデータを取得 (1)</a></p> <p>今回は、取得したデータをデータベースに保存します。</p> <h2 id="データベース"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9">データベース</a></h2> <p>以前の記事で使用した、MySQLとテーブル"from_plc"<br /> を使います。</p> <p><a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a></p> <pre><code>C:\Users\hoge\Downloads>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 60 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. 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> use hoge Database changed mysql> desc from_plc; +-------------+-----------+------+-----+-------------------+-------------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-----------+------+-----+-------------------+-------------------+ | body | json | YES | | NULL | | | time_insert | timestamp | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | +-------------+-----------+------+-----+-------------------+-------------------+ 2 rows in set (0.02 sec) mysql> </code></pre> <h2 id="Perlスクリプト"><a href="#Perl%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">Perlスクリプト</a></h2> <p>前回は、Perlスクリプトを僕のセルフォンTermux上で動作させましたが、今回はPCのWindows上で開発し動作させます。何故?</p> <blockquote> <p>僕のセルフォンのTurmuxのcpanになんらかの問題があるらしく、DBIのビルドができないんです。いろいろトライしたけど僕には解決は無理と判断し、Turmax上のPerlスクリプトを放棄しました。<br /> DBIは必須ではないですが、データベースをアクセスするPerlなら、DBIを使うのが一般的ですよね。<br /> DBIのない環境で、これ以上Perlを書く気にはなれませんよね。まあ、僕のせいなんですけどね。</p> </blockquote> <p>「サーバやPC上のプログラムでModbus機器からデータを取得 (1)」で作成したスクリプトに手を加えました。これで、Modbus機器からModbus/TCPにより取得したデータをデータベースに保存します。</p> <pre><code class="perl">#!/usr/bin/perl use IO::Socket; use DBI; use Time::HiRes "gettimeofday"; use utf8; use Encode; use JSON; { my $Server = 'localhost'; my $Port = 502; my $TransactionID = 0x0200; my $ProtocolID = 0x0000; my $Length = 0x0006; my $UnitID = 0x01; my $FunctionCode = 0x03; my $StartAddress = 0x0000; my $CountRegister = 0x0001; my $req = pack("n3C2n2", $TransactionID, $ProtocolID, $Length, $UnitID, $FunctionCode, $StartAddress, $CountRegister); my $old = -1; while (1) { my $socket = new IO::Socket::INET( PeerAddr=>$Server, PeerPort=>$Port, Proto=>'tcp'); die "IO::Socket : $!" unless $socket; my $size = $socket->send($req); shutdown($socket, 1); my $response = ""; $socket->recv($response, 1024); my @data = unpack("n3C3n", $response); my $Register = $data[6]; if ($old != $Register) { print "sent data:\n"; BinaryDump($req); print "received response:\n"; BinaryDump($response); my $now = getCurrentTimeStr(); my $output_data = { 'ts' => $now, 'value' => $Register }; my $json_text = decode('utf-8', encode_json( $output_data )); print $json_text . "\n"; my @values = ($json_text); $user = 'hoge'; $passwd = 'hoge001'; $db = DBI->connect('DBI:mysql:hoge:localhost', $user, $passwd); $sth = $db->prepare("INSERT INTO from_plc (body) values (?)"); $sth->execute(@values); $sth->finish; $db->disconnect; } $old = $Register; $socket->close(); sleep 1; } } # 利用させていただきました # https://netlog.jpn.org/r271-635/2018/11/perl-bynary-dumper.html sub BinaryDump { my ($buf) = @_; my $len; my $i; $len = length($buf); printf ("length = %d\n", $len); for ($i = 0; $i < $len; $i++) { printf("%02X ", ord(substr($buf, $i, 1))); # 16文字目で画面上の改行 if (($i % 16) == 15) { print "\n"; } } if (($i % 16) != 15) { print "\n"; } } # 利用させていただきました # https://akrad.hatenablog.com/entry/2018/10/20/234528 sub getCurrentTimeStr { my ($epochSec, $microSec) = gettimeofday(); my ($sec, $min, $hour, $day, $mon, $year) = localtime($epochSec); $year += 1900; $mon++; return "$year" . '-' . sprintf("%02d", $mon) . '-' . sprintf("%02d", $day) . 'T' . sprintf("%02d", $hour) . ':' . sprintf("%02d", $min) . ':' . sprintf("%02d", $sec) . '.' . "$microSec"; } </code></pre> <p>Perlの自由さと気楽さが良いですね。</p> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>Windows上ということで、再び MOD-RSsim に登場いただきます。MOD-RSsimを起動しておきます。<br /> <a href="https://crieit.now.sh/upload_images/ce54d89db6542b8d42e049dac808c83862e4cf06c02a0.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ce54d89db6542b8d42e049dac808c83862e4cf06c02a0.png?mw=700" alt="image" /></a><br /> Perlスクリプトを実行します。</p> <pre><code>C:\Users\hoge\Downloads>perl sample.pl sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 00 {"ts":"2022-07-30T15:05:59.916895","value":0} </code></pre> <p>Holding Register 40001 の0を取得しデータベースに保存されます。</p> <p>MOD-RSsim で 40001 に任意の数値を入力します。<br /> <a href="https://crieit.now.sh/upload_images/365c981a39ba1de3af4b05aa11b1bcf162e4cfae8fe0a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/365c981a39ba1de3af4b05aa11b1bcf162e4cfae8fe0a.png?mw=700" alt="image" /></a></p> <p>40001 のデータが変わる都度、データベースに値が保存されます。</p> <pre><code>C:\Users\hoge\Downloads>perl sample.pl sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 00 {"ts":"2022-07-30T15:05:59.916895","value":0} sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 01 {"ts":"2022-07-30T15:06:04.10446","value":1} sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 02 {"ts":"2022-07-30T15:06:07.41537","value":2} sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 03 {"value":3,"ts":"2022-07-30T15:06:10.72655"} sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 04 {"value":4,"ts":"2022-07-30T15:06:12.150828"} sent data: length = 12 02 00 00 00 00 06 01 03 00 00 00 01 received response: length = 11 02 00 00 00 00 05 01 03 02 00 05 {"ts":"2022-07-30T15:06:14.213053","value":5} Terminating on signal SIGINT(2) C:\Users\hoge\Downloads> </code></pre> <p>データベースのテーブル"from_plc"を確認してみます。</p> <pre><code>C:\Users\hoge\Downloads>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 75 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. 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> use hoge Database changed mysql> select * from from_plc; +----------------------------------------------------+---------------------+ | body | time_insert | +----------------------------------------------------+---------------------+ | {"ts": "2022-07-30T15:05:59.916895", "value": 0} | 2022-07-30 15:05:59 | | {"ts": "2022-07-30T15:06:04.10446", "value": 1} | 2022-07-30 15:06:04 | | {"ts": "2022-07-30T15:06:07.41537", "value": 2} | 2022-07-30 15:06:07 | | {"ts": "2022-07-30T15:06:10.72655", "value": 3} | 2022-07-30 15:06:10 | | {"ts": "2022-07-30T15:06:12.150828", "value": 4} | 2022-07-30 15:06:12 | | {"ts": "2022-07-30T15:06:14.213053", "value": 5} | 2022-07-30 15:06:14 | +----------------------------------------------------+---------------------+ 6 rows in set (0.00 sec) mysql> </code></pre> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>このように、Modbus機器からのデータ取得は、専用の機器や商用のソフトウェアがなくても可能です。今回はModbus/TCPでしたが、RTUでも原理は同じです。<br /> データ取得だけではなく、Modbus機器にデータを書き込むことも難しくはありません。Modbusの仕様を理解すれば、データの取得も書き込みも簡単です。<br /> Modbusに限らず、各メーカのPLCの仕様もほとんどがオープンになっているので、それぞれの仕様を理解しさえすれば、Modbusと同じように自分で書いたプログラムでの読み書きが可能です。<br /> 様々な種類のPLCやリモートIOなどをアクセスする場合には、ゲートウェイなどを利用したほうが良いかもしれません。一方で、限られた種類のPLCやリモートIOをアクセスする場合は、自分で書いたプログラムを使用するという選択もあると思います。</p> <p>「作らない」のではなく「作れない」んです。なぜ「作れない」のか?「作らない」からです。</p> <p><a href="https://crieit.net/posts/PC-Modbus-1">サーバやPC上のプログラムでModbus機器からデータを取得 (1)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18265 2022-07-29T07:33:22+09:00 2022-07-29T07:33:22+09:00 https://crieit.net/posts/15538b281af3d9bb53ada98193886b4e 旅先でサーバ上のデータベースをメンテナンスする <p>以前に投稿した記事「旅先でクラウド上のWindowsサーバにRDP接続」は、Androidセルフォンのアプリケーションで、SSHのポートフォワードを介して<br /> WindowsサーバにRDP接続するというものでした。</p> <p><a href="https://crieit.net/posts/Windows-RDP">旅先でクラウド上のWindowsサーバにRDP接続</a></p> <p>この記事で紹介した、僕を助けてくれるアプリケーションのうちの1つがこれなんです。</p> <p><a href="https://crieit.now.sh/upload_images/0f1620b003abae44c6e2dc0b9a1cb92a62e30b62d0a6f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0f1620b003abae44c6e2dc0b9a1cb92a62e30b62d0a6f.png?mw=700" alt="image" /></a></p> <p>これのポートフォワード設定を追加してあげると、セルフォンのアプリケーションから、サーバのデータベースのデータを参照したり更新したりすることができます。<br /> 皆さんもご存知のとおり、あたりまえのことなんですけどね。<br /> <a href="https://crieit.now.sh/upload_images/029983100bf745182d0640da3a29124262e304560a1a0.jpeg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/029983100bf745182d0640da3a29124262e304560a1a0.jpeg?mw=700" alt="image" /></a></p> <h2 id="ポートフォワード"><a href="#%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AF%E3%83%BC%E3%83%89">ポートフォワード</a></h2> <p>ConnectBotのSSHに下図にようなポートフォワードを追加してあげます。この例は、SSHサーバのローカルで稼働するMySQLに接続する設定です。<br /> <a href="https://crieit.now.sh/upload_images/47822d9b8e2a2f058338bdd5f7b2763562e30bf7b571b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/47822d9b8e2a2f058338bdd5f7b2763562e30bf7b571b.png?mw=700" alt="image" /></a><br /> RDPは以前に設定したものです。</p> <h2 id="データベースのデータを操作するアプリケーション"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">データベースのデータを操作するアプリケーション</a></h2> <p>データベースのデータを操作するのに便利なアプリケーションがあります。僕の好みです。皆さんの評価など受け付けません。<br /> <a href="https://crieit.now.sh/upload_images/f6942cff736d92fe0736b22d959d417062e30d0b1153c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f6942cff736d92fe0736b22d959d417062e30d0b1153c.png?mw=700" alt="image" /></a><br /> MySQLへの接続はこんなふうにしてあげます。<br /> <a href="https://crieit.now.sh/upload_images/8822652800e925e419f3ed4441f1dff062e30d7d6ad50.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8822652800e925e419f3ed4441f1dff062e30d7d6ad50.png?mw=700" alt="image" /></a><br /> デフォルトでSQLiteへの接続が見えますが、ここにSQLiteがいれば、おそらくつながるのでしょう。<br /> <a href="https://crieit.now.sh/upload_images/b02515b7e752989101e54ece9e6f6e0462e30dc24b9c7.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b02515b7e752989101e54ece9e6f6e0462e30dc24b9c7.png?mw=700" alt="image" /></a><br /> MySQLをクリックすると、こんなふうにオブジェクトが見えるようになります。<br /> <a href="https://crieit.now.sh/upload_images/740d71e54addfcf9377a6b27426a139a62e30e1268e41.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/740d71e54addfcf9377a6b27426a139a62e30e1268e41.png?mw=700" alt="image" /></a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18264 2022-07-28T02:01:52+09:00 2022-08-08T04:48:35+09:00 https://crieit.net/posts/MySQL-Linux MySQLでDBサーバ構築してレプリケーションさせる <p>参考<br /> ① https://videohub.oracle.com/media/MySQL%208.0%20入門セミナー%20~インストール編%20for%20Linux%20&%20アーキテクチャ編~%20%282022年6月16日%29/1_ph7s6hxj<br /> ②MySQL公式ドキュメント:https://dev.mysql.com/doc/refman/8.0/ja/linux-installation.html</p> <h1 id="1. MySQLを開始"><a href="#1.+MySQL%E3%82%92%E9%96%8B%E5%A7%8B">1. MySQLを開始</a></h1> <h2 id="1.1. リポジトリをダウンロード"><a href="#1.1.+%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89">1.1. リポジトリをダウンロード</a></h2> <p>この記事では、Oracle Linuxを使用している。</p> <pre><code>[opc@mysql-master1 ~]$ cat /etc/oracle-release Oracle Linux Server release 8.6 </code></pre> <p>yumリポジトリを利用する。<br /> <a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/">https://dev.mysql.com/</a><br /> から、MySQLサーバのOSバージョンと合致したリポジトリをネットに接続可能な端末にダウンロードし、MySQLサーバに配置する。</p> <p>画面左部の「<code>Downloads</code>」タブをクリックし切り替わった画面の<code>MySQL Yum Repository</code>をクリック。<br /> すると、複数種類のリポジトリが表示されるので、利用するMySQLサーバのOSバージョンと合致したリポジトリを選択する必要がある。</p> <pre><code>[opc@mysql-master1 ~]$ cat /etc/oracle-release Oracle Linux Server release 8.6 </code></pre> <p>ダウンロードしたら、利用予定のMySQLサーバに転送する。</p> <pre><code>[root@mysql-master1 tmp]# ll total 16 -rw-r--r--. 1 kiban kiban 14432 Jul 23 15:12 mysql80-community-release-el8-4.noarch.rpm drwx------. 3 root root 17 Jul 23 15:05 systemd-private-e967423d4c9e455a88d0cd2ee0f307be-chronyd.service-2MooXe drwx------. 3 root root 17 Jul 23 15:05 systemd-private-e967423d4c9e455a88d0cd2ee0f307be-unified-monitoring-agent.service-RX6Cai drwxr-xr-x. 2 root root 6 Jul 21 17:08 unified-monitoring-agent </code></pre> <p>以上</p> <h2 id="1.2. MySQLのインストール"><a href="#1.2.+MySQL%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1.2. MySQLのインストール</a></h2> <p>先述の1.1節で配置したインストーラーを用いてインストール開始する。</p> <p>まずはインストーラを解凍し、解凍が無事に完了したらちゃんと解凍されていることを確認も行う。</p> <pre><code>[root@mysql-master1 tmp]# rpm -Uvh mysql80-community-release-el8-4.noarch.rpm warning: mysql80-community-release-el8-4.noarch.rpm: Header V4 RSA/SHA256 Signature, key ID 3a79bd29: NOKEY Verifying... ################################# [100%] Preparing... ################################# [100%] Updating / installing... 1:mysql80-community-release-el8-4 ################################# [100%] [root@mysql-master1 tmp]# yum repolist enabled |grep 'mysql' mysql-connectors-community MySQL Connectors Community mysql-tools-community MySQL Tools Community mysql80-community MySQL 8.0 Community Server </code></pre> <p>RHEL8などLinux8ベースのOSの場合のみ同梱されているMySQLの無効化を行う。</p> <pre><code>[root@mysql-master1 tmp]# yum module disable mysql MySQL 8.0 Community Server 70 MB/s | 2.3 MB 00:00 MySQL Connectors Community 5.8 MB/s | 74 kB 00:00 MySQL Tools Community 26 MB/s | 459 kB 00:00 Dependencies resolved. ===================================================================================================================== Package Architecture Version Repository Size ===================================================================================================================== Disabling modules: mysql Transaction Summary ===================================================================================================================== Is this ok [y/N]: y Complete! </code></pre> <p>MySQLのインストールを実施。</p> <pre><code>[root@mysql-master1 tmp]# yum install mysql-community-server Last metadata expiration check: 0:02:03 ago on Sat 23 Jul 2022 03:22:37 PM GMT. Dependencies resolved. ===================================================================================================================== Package Architecture Version Repository Size ===================================================================================================================== Installing: mysql-community-server x86_64 8.0.29-1.el8 mysql80-community 53 M Installing dependencies: mysql-community-client x86_64 8.0.29-1.el8 mysql80-community 14 M mysql-community-client-plugins x86_64 8.0.29-1.el8 mysql80-community 2.4 M mysql-community-common x86_64 8.0.29-1.el8 mysql80-community 636 k mysql-community-icu-data-files x86_64 8.0.29-1.el8 mysql80-community 2.1 M mysql-community-libs x86_64 8.0.29-1.el8 mysql80-community 1.5 M Transaction Summary ===================================================================================================================== Install 6 Packages Total download size: 74 M Installed size: 347 M Is this ok [y/N]: y Downloading Packages: (1/6): mysql-community-common-8.0.29-1.el8.x86_64.rpm 25 MB/s | 636 kB 00:00 (2/6): mysql-community-client-plugins-8.0.29-1.el8.x86_64.rpm 62 MB/s | 2.4 MB 00:00 (3/6): mysql-community-icu-data-files-8.0.29-1.el8.x86_64.rpm 76 MB/s | 2.1 MB 00:00 (4/6): mysql-community-libs-8.0.29-1.el8.x86_64.rpm 64 MB/s | 1.5 MB 00:00 (5/6): mysql-community-client-8.0.29-1.el8.x86_64.rpm 95 MB/s | 14 MB 00:00 (6/6): mysql-community-server-8.0.29-1.el8.x86_64.rpm 98 MB/s | 53 MB 00:00 --------------------------------------------------------------------------------------------------------------------- Total 123 MB/s | 74 MB 00:00 MySQL 8.0 Community Server 3.0 MB/s | 3.1 kB 00:00 Importing GPG key 0x3A79BD29: Userid : "MySQL Release Engineering <[email protected]>" Fingerprint: 859B E8D7 C586 F538 430B 19C2 467B 942D 3A79 BD29 From : /etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022 Is this ok [y/N]: y Key imported successfully MySQL 8.0 Community Server 1.9 MB/s | 1.9 kB 00:00 Importing GPG key 0x5072E1F5: Userid : "MySQL Release Engineering <[email protected]>" Fingerprint: A4A9 4068 76FC BD3C 4567 70C8 8C71 8D3B 5072 E1F5 From : /etc/pki/rpm-gpg/RPM-GPG-KEY-mysql Is this ok [y/N]: y Key imported successfully Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : mysql-community-common-8.0.29-1.el8.x86_64 1/6 Installing : mysql-community-client-plugins-8.0.29-1.el8.x86_64 2/6 Installing : mysql-community-libs-8.0.29-1.el8.x86_64 3/6 Running scriptlet: mysql-community-libs-8.0.29-1.el8.x86_64 3/6 Installing : mysql-community-client-8.0.29-1.el8.x86_64 4/6 Installing : mysql-community-icu-data-files-8.0.29-1.el8.x86_64 5/6 Running scriptlet: mysql-community-server-8.0.29-1.el8.x86_64 6/6 Installing : mysql-community-server-8.0.29-1.el8.x86_64 6/6 Running scriptlet: mysql-community-server-8.0.29-1.el8.x86_64 6/6 Verifying : mysql-community-client-8.0.29-1.el8.x86_64 1/6 Verifying : mysql-community-client-plugins-8.0.29-1.el8.x86_64 2/6 Verifying : mysql-community-common-8.0.29-1.el8.x86_64 3/6 Verifying : mysql-community-icu-data-files-8.0.29-1.el8.x86_64 4/6 Verifying : mysql-community-libs-8.0.29-1.el8.x86_64 5/6 Verifying : mysql-community-server-8.0.29-1.el8.x86_64 6/6 Installed: mysql-community-client-8.0.29-1.el8.x86_64 mysql-community-client-plugins-8.0.29-1.el8.x86_64 mysql-community-common-8.0.29-1.el8.x86_64 mysql-community-icu-data-files-8.0.29-1.el8.x86_64 mysql-community-libs-8.0.29-1.el8.x86_64 mysql-community-server-8.0.29-1.el8.x86_64 Complete! </code></pre> <p>最後まで進んでインストール完了したら、MySQLを起動させる。<br /> まずはステータスの確認から。</p> <pre><code>[root@mysql-master1 tmp]# systemctl status mysqld ● mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled) Active: inactive (dead) Docs: man:mysqld(8) http://dev.mysql.com/doc/refman/en/using-systemd.html </code></pre> <p>インストールされており、サービスとしてenableとなっていることがわかる。<br /> それでは起動させる。</p> <pre><code>[root@mysql-master1 tmp]# systemctl start mysqld [root@mysql-master1 tmp]# systemctl status mysqld ● mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2022-07-23 15:45:02 GMT; 1min 29s ago Docs: man:mysqld(8) http://dev.mysql.com/doc/refman/en/using-systemd.html Process: 11752 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS) Main PID: 11834 (mysqld) Status: "Server is operational" Tasks: 37 (limit: 22765) Memory: 495.4M CGroup: /system.slice/mysqld.service mq11834 /usr/sbin/mysqld Jul 23 15:44:56 mysql-master1 systemd[1]: Starting MySQL Server... Jul 23 15:45:02 mysql-master1 systemd[1]: Started MySQL Server. </code></pre> <p>スタートさせた後は必ずステータスを確認して、起動していることを確認すること。</p> <h2 id="1.3 ログインしてパスワードを変更"><a href="#1.3+%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%97%E3%81%A6%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%82%92%E5%A4%89%E6%9B%B4">1.3 ログインしてパスワードを変更</a></h2> <p>rootユーザの初期パスワードはログに記載されているのでそこで確認し、自分の覚えやすいパスワードに変更していく。その後、他のユーザを作成してパスワード設定、権限設定を順次行う。</p> <p>まずはrootユーザのパスワードを変更する。<code>/var/log/mysql.log</code>に記載されていることを確認。</p> <pre><code>[root@mysql-master1 log]# cat /var/log/mysqld.log ~ 2022-07-23T15:44:59.597480Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: xCkVmf#Yd2ci ~ </code></pre> <p>上記をみると、<code>xCkVmf#Yd2ci</code>が初期パスワードとして設定されていることがわかる。<br /> ではログインして変更する。</p> <pre><code>[root@mysql-master1 log]# mysql -u root -p Enter password:  ←ここにxCkVmf#Yd2ciを入力。表示はされないよ ~ mysql> alter user root@localhost identified by 'MyNewPass1!'; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye </code></pre> <p>これでrootユーザのパスワードがMyNewPass4!に変更された。<br /> 再ログインしてみて、パスワード変更されたかを確認しておこう。<br /> パスワード変更コマンドはいくつかあるので紹介する。</p> <pre><code>> set password for ユーザ名@ホスト名='任意password`; 現在ログインしてるユーザのパスワードを変更するには > set password='任意passwword`; </code></pre> <p>ちなみに、パスワードポリシーはパスワード検証コンポーネントによって定義されている。これはデフォルトで有効になってる。内容は</p> <pre><code>mysql> SHOW VARIABLES LIKE 'validate_password%'; +--------------------------------------+--------+ | Variable_name | Value | +--------------------------------------+--------+ | validate_password.check_user_name | ON | | validate_password.dictionary_file | | | validate_password.length | 8 | | validate_password.mixed_case_count | 1 | | validate_password.number_count | 1 | | validate_password.policy | MEDIUM | | validate_password.special_char_count | 1 | +--------------------------------------+--------+ 7 rows in set (0.01 sec) </code></pre> <p>パスワード検証コンポーネントを無効化させるには</p> <pre><code>mysql> uninstall component 'file://component_validate_password'; Query OK, 0 rows affected (0.00 sec) </code></pre> <p>逆に有効化させるには<code>install component 'file://component_validate_password';</code>を入力する。</p> <h1 id="2. MySQLアーキテクチャ"><a href="#2.+MySQL%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3">2. MySQLアーキテクチャ</a></h1> <p><a href="https://crieit.now.sh/upload_images/cd28e3ad239066b2c265d44ba4be8d2062e1794497278.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cd28e3ad239066b2c265d44ba4be8d2062e1794497278.png?mw=700" alt="innodb-architecture.png" /></a></p> <h2 id="2.1 sampleデータ"><a href="#2.1+sample%E3%83%87%E3%83%BC%E3%82%BF">2.1 sampleデータ</a></h2> <p>サンプルデータをインストールする。<br /> <a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/index-other.html">https://dev.mysql.com/doc/index-other.html</a><br /> 今回はworld databaseのzipファイルをローカルにダウンロードし、DBサーバに転送。zipファイルを解凍してからmysqlへインポートした。</p> <pre><code>$ unzip world-db.zip $ cd world-db/ $ mysql -uroot -proomysqld < world.sql </code></pre> <p>これでインポート完了したので中身の確認をしてみる。</p> <pre><code>mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | world | +--------------------+ 5 rows in set (0.00 sec) mysql> use world Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +-----------------+ | Tables_in_world | +-----------------+ | city | | country | | countrylanguage | +-----------------+ 3 rows in set (0.01 sec) </code></pre> <p>そのほかにも自分でテーブルを作成してみる。</p> <pre><code>mysql> create table `sample1` ( -> `code` char(3) not null default '', `population` int(11) not null default '0', primary key (`code`)) -> engine=InnoDB; Query OK, 0 rows affected, 1 warning (0.02 sec) </code></pre> <p>インポートしたデータはデフォルトでは/var/lib/mysqlに配置される。</p> <pre><code>[root@mysql-master1 mysql]# ll total 189584 -rw-r-----. 1 mysql mysql 56 Jul 23 15:44 auto.cnf -rw-r-----. 1 mysql mysql 736313 Jul 27 15:29 binlog.000001 -rw-r-----. 1 mysql mysql 16 Jul 23 15:45 binlog.index -rw-------. 1 mysql mysql 1680 Jul 23 15:44 ca-key.pem -rw-r--r--. 1 mysql mysql 1112 Jul 23 15:44 ca.pem -rw-r--r--. 1 mysql mysql 1112 Jul 23 15:44 client-cert.pem -rw-------. 1 mysql mysql 1676 Jul 23 15:44 client-key.pem -rw-r-----. 1 mysql mysql 196608 Jul 27 15:31 '#ib_16384_0.dblwr' -rw-r-----. 1 mysql mysql 8585216 Jul 23 15:44 '#ib_16384_1.dblwr' -rw-r-----. 1 mysql mysql 5942 Jul 23 15:45 ib_buffer_pool -rw-r-----. 1 mysql mysql 12582912 Jul 27 15:29 ibdata1 ←システムtableスペース -rw-r-----. 1 mysql mysql 50331648 Jul 27 15:31 ib_logfile0 ←REDOログデータ -rw-r-----. 1 mysql mysql 50331648 Jul 23 15:44 ib_logfile1 ←REDOログデータ -rw-r-----. 1 mysql mysql 12582912 Jul 23 15:45 ibtmp1 drwxr-x---. 2 mysql mysql 187 Jul 23 15:45 '#innodb_temp' drwxr-x---. 2 mysql mysql 143 Jul 23 15:44 mysql  ←この配下に一般ログやslowログがある -rw-r-----. 1 mysql mysql 25165824 Jul 27 15:29 mysql.ibd srwxrwxrwx. 1 mysql mysql 0 Jul 23 15:45 mysql.sock -rw-------. 1 mysql mysql 6 Jul 23 15:45 mysql.sock.lock drwxr-x---. 2 mysql mysql 8192 Jul 23 15:44 performance_schema -rw-------. 1 mysql mysql 1680 Jul 23 15:44 private_key.pem -rw-r--r--. 1 mysql mysql 452 Jul 23 15:44 public_key.pem -rw-r--r--. 1 mysql mysql 1112 Jul 23 15:44 server-cert.pem -rw-------. 1 mysql mysql 1676 Jul 23 15:44 server-key.pem drwxr-x---. 2 mysql mysql 28 Jul 23 15:45 sys -rw-r-----. 1 mysql mysql 16777216 Jul 27 15:29 undo_001 -rw-r-----. 1 mysql mysql 16777216 Jul 27 15:31 undo_002 drwxr-x---. 2 mysql mysql 87 Jul 27 15:29 world  ←インポートしたデータ </code></pre> <h2 id="2.2 システム変数設定"><a href="#2.2+%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%A4%89%E6%95%B0%E8%A8%AD%E5%AE%9A">2.2 システム変数設定</a></h2> <p>MySQLサーバの設定はシステム変数で設定する。設定方法は、①my.cnfで設定した後に再起動を行う、②一時的な設定を行う(一部の変数しか設定変更できない)。</p> <h3 id="2.2.1 my.cnfで設定"><a href="#2.2.1+my.cnf%E3%81%A7%E8%A8%AD%E5%AE%9A">2.2.1 my.cnfで設定</a></h3> <p>設定ファイルは/etc/my.cnfが読み込まれることになっている。デフォルト設定を変更して任意のディレクトリに配置したmy.cnfを読み込ませたかったら以下を実施。複数ファイルを読み込んだら後勝ちになる。</p> <pre><code>$ mysqld --verbose --help $ mysqld --defaults-file=/usr/local/mysql/date/my.cnf & </code></pre> <p>設定ファイルを変更したら基本的にはmysqlの再起動を行う。</p> <h3 id="2.2.2 一時的な設定"><a href="#2.2.2+%E4%B8%80%E6%99%82%E7%9A%84%E3%81%AA%E8%A8%AD%E5%AE%9A">2.2.2 一時的な設定</a></h3> <p>参考:https://dev.mysql.com/doc/refman/8.0/ja/server-system-variables.html</p> <p>MySQLを再起動かけられないときなどは、my.cnfの編集なしに設定値を変更できる変数がある。ただし、一時的に設定変更しても、再起動やmy.cnfのリロードを行うと一時的な設定が元に戻る点に注意。</p> <p>設定方法は以下で行える。設定はセッション単位(LOCAL)、サーバ全体(GLOBAL)での変更が可能。</p> <pre><code>> set [global|sesison] <変数> =<値>; </code></pre> <p>設定した値の確認方法は以下。</p> <pre><code>show [global|session] variables like '%<システム変数名>%'; </code></pre> <p>実際に設定されてるシステム変数を一括で確認するには</p> <pre><code>$ mysqld --verbose --help </code></pre> <p>で確認できる。</p> <p>一時的な設定変更を永続化させることができる。その場合は設定する際にpersistを付け加えればよい。</p> <pre><code>> set persist max_connections = 500; </code></pre> <p>システム変数を永続化オプションをつけて設定した場合はmy.cnfとの設定に差異がでる。そういった場合に設定変更履歴を確認するにはperformance_schemaを参照すること。</p> <pre><code>mysql> select * from performance_schema.variables_info where variable_source='persisted'; Empty set (0.00 sec) </code></pre> <h3 id="2.2.3 システム変数例"><a href="#2.2.3+%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%A4%89%E6%95%B0%E4%BE%8B">2.2.3 システム変数例</a></h3> <p>nnodb_buffer_pool_sizeInno:DBバッファプールサイズ<br /> innodb_log_buffer_sizeInno:DBログバッファのサイズ(メモリ上)<br /> innodb_log_file_size:ログファイルのサイズ(ディスク上)<br /> innodb_log_files_in_groups:ログファイルの数(デフォルト2)<br /> binlog_expire_logs_seconds:バイナリログを削除するまでの期間を秒単位で指定。デフォルトは30日。</p> <h1 id="3. レプリケーション設定"><a href="#3.+%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E8%A8%AD%E5%AE%9A">3. レプリケーション設定</a></h1> <p>レプリケーションの設定は4手順ある。<br /> ①レプリケーション用のパラメータを設定、<br /> ②マスター側にレプリケーションユーザ作成、<br /> ③マスター側のバックアップを取得し、スレーブ側にリストアする。そして、<br /> ④スレーブ側でレプリケーションのためにマスター情報やポジション情報を設定しレプリケーションをスタートさせる。</p> <h2 id="3.1 レプリケーション用のパラメータ設定"><a href="#3.1+%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E7%94%A8%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E8%A8%AD%E5%AE%9A">3.1 レプリケーション用のパラメータ設定</a></h2> <p>マスター側で設定するパラメータ</p> <pre><code>server-id log_bin datedir gtid-mode enforce-gtid-consistency = on log-slave-updates </code></pre> <h2 id="3.2 ソースサーバにレプリケーションユーザ作成"><a href="#3.2+%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AB%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%A6%E3%83%BC%E3%82%B6%E4%BD%9C%E6%88%90">3.2 ソースサーバにレプリケーションユーザ作成</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/8.0/ja/replication-howto-repuser.html">https://dev.mysql.com/doc/refman/8.0/ja/replication-howto-repuser.html</a><br /> ソースサーバにレプリケーション用のユーザを作成して、そのユーザにレプリケーションスレーブ権限を付与させる。</p> <pre><code>mysql> create user repl@'10.1.%' identified by 'password'; Query OK, 0 rows affected (0.01 sec) mysql> grant replication slave on *.* to repl@'10.1.%'; Query OK, 0 rows affected (0.00 sec) </code></pre> <p>replユーザがどこから接続可能にしたいのかによって、<code>repl@'10.1.%'</code>の書き方は変わってくる。localhostにするとほかのサーバがreplユーザでアクセスできないので注意。ユーザを作成したらパスワードの設定も忘れずに。<br /> 設定が完了したら、想定通りの挙動になっているかの確認を行う。<br /> まずはマスター側でユーザの確認。</p> <pre><code>mysql> SELECT user, host, plugin FROM mysql.user; +------------------+-----------+-----------------------+ | user | host | plugin | +------------------+-----------+-----------------------+ | repl | 10.1.% | caching_sha2_password | | mysql.infoschema | localhost | caching_sha2_password | | mysql.session | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | | root | localhost | caching_sha2_password | +------------------+-----------+-----------------------+ 5 rows in set (0.00 sec) </code></pre> <p>replユーザのhostが<code>10.1.%</code>になっていることを確認。つまり、localhostに設定されていないので、①マスター側で接続を試みてもログインできないが②スレーブサーバからはログインできるということになる。確認しよう。<br /> ①</p> <pre><code>[kiban@mysql-master1 ~]$ mysql -urepl -p Enter password: ERROR 1045 (28000): Access denied for user 'repl'@'localhost' (using password: YES) </code></pre> <p>②</p> <pre><code>mysql -h10.1.2.148 -urepl -p Enter password: mysql> </code></pre> <p>①②を通じて、想定通りの設定が行われたことが確認できた。</p> <h2 id="3.3 dump出力"><a href="#3.3+dump%E5%87%BA%E5%8A%9B">3.3 dump出力</a></h2> <p>元から入ってるデータベース(システムデータベース)はレプリケーションされないので、それ以外(今回でいえばworld)だけエクスポートする。</p> <pre><code>mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | world |   ←これが出力対象 +--------------------+ </code></pre> <p>以下でエクスポートする。</p> <pre><code>mysqldump -uroot -p<パスワード入力> --master-data=2 --socket=/var/lib/mysql/mysql.sock --hex-blob --flush-logs --default-character-set=utf8mb4 --databases world --single-transaction --triggers --routines --events > mysql_bkup_dump_20220807.sql </code></pre> <p>出力されたダンプをレプリカサーバに転送して、インポートさせる。</p> <pre><code>mysql -hlocalhost -uroot -roomysqld < mysql_bkup_dump_20220807.sql </code></pre> <p>インポート完了したらm、mysqlサーバにログインしてworldデータベースが存在することを確認する。</p> <pre><code>mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | world | +--------------------+ 5 rows in set (0.00 sec) </code></pre> <p>あることがわかった。</p> <p>【重要】<br /> レプリカサーバ側で。datedir配下にあるauto.cnfを削除する。</p> <h2 id="3.4 レプリケーション情報のセット"><a href="#3.4+%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E6%83%85%E5%A0%B1%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88">3.4 レプリケーション情報のセット</a></h2> <p>レプリカサーバ側でレプリケーションを行うための情報として、ソースサーバの情報をセットする。</p> <p>まずはマスター側の情報を取得する。</p> <pre><code>mysql> show master status; +-------------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------------+----------+--------------+------------------+-------------------+ | master-mysql-bin.000006 | 157 | | | | +-------------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) </code></pre> <p>続いて、上記の情報をもとにスレーブ側で情報をセット。</p> <pre><code>mysql> change master to master_host='<マスターサーバのIPアドレス>', -> master_user='repl', -> master_password='<replユーザのpassword>', -> master_log_file='master-mysql-bin.000006', -> master_log_pos=157; Query OK, 0 rows affected, 8 warnings (0.02 sec) </code></pre> <p>セットが完了したら、想定通りの設定がされているかをスレーブ側で確認。</p> <pre><code>mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Master_Host: <マスターサーバのIPアドレス> Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-mysql-bin.000006 Read_Master_Log_Pos: 157 Relay_Log_File: mysql-slave1-relay-bin.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: master-mysql-bin.000006 Slave_IO_Running: No Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 157 Relay_Log_Space: 157 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 0 Master_UUID: Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0 Network_Namespace: 1 row in set, 1 warning (0.00 sec) </code></pre> <p>設定が想定通りになっていることが確認できたので、いよいよレプリケーションを始める。</p> <pre><code>mysql> start slave; Query OK, 0 rows affected, 1 warning (0.01 sec) </code></pre> <p>レプリケーションを開始すると、slaveのステータスを再度確認する。</p> <pre><code>mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Connecting to source Master_Host: 10.1.2.148 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-mysql-bin.000006 Read_Master_Log_Pos: 157 Relay_Log_File: mysql-slave1-relay-bin.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: master-mysql-bin.000006 Slave_IO_Running: Connecting Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 157 Relay_Log_Space: 157 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 2003 Last_IO_Error: error connecting to master '[email protected]:3306' - retry-time: 60 retries: 1 message: Can't connect to MySQL server on '10.1.2.148:3306' (110) Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 0 Master_UUID: Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: 220806 21:33:27 Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0 Network_Namespace: 1 row in set, 1 warning (0.00 sec) </code></pre> <p>本来なら、マスターサーバのバイナリログを取り込んでリレーログにする<code>Slave_IO_Running</code>と、取り込まれたリレーログからイベントを取り出してデータベースに反映させる<code>Slave_SQL_Running</code>のステータスがともに<code>YES</code>になるはずである。しかし、<code>Slave_IO_Running</code>が<code>Connecting</code>になっている。</p> <p>そこで、スレーブサーバからマスターサーバの3306番ポートへ接続できるか確認するために<code>nc -vz マスターのIP 3306</code>を実行したところ、接続できなかった。</p> <pre><code>$ nc -vz 10.1.2.58 3306 Ncat: Version 7.70 ( https://nmap.org/ncat ) Ncat: No route to host. </code></pre> <p>ネットワーク設定に問題がないことは確認済みなので、サーバのfierwall設定を行う。</p> <pre><code># firewall-cmd --zone=public --add-port=3306/tcp --permanet # firewall-cmd --reload # nc -vz 10.1.2.58 3306 Ncat: Version 7.70 ( https://nmap.org/ncat ) Ncat: Connected to 10.1.2.58:3306. Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds. </code></pre> <p>3306番ポートへ接続できることを確認。</p> <p>スレーブのステータスを確認すると、</p> <pre><code>Slave_IO_Running: Yes </code></pre> <p>となってた。<br /> レプリケーションされてることが確認できたので、マスター側で変更したデータがスレーブ側に連携されるかテストする。<br /> まずはマスター側でスキーマ作成。</p> <pre><code>mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | world | +--------------------+ 5 rows in set (0.01 sec) mysql> create database test1; Query OK, 1 row affected (0.00 sec) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test1 | | world | +--------------------+ 6 rows in set (0.00 sec) </code></pre> <p>続いて、スレーブ側でtest1スキーマが作成されていることを確認。</p> <pre><code>mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test1 | | world | +--------------------+ 6 rows in set (0.00 sec) </code></pre> <p>ちゃんと作成されていた。<br /> これでレプリケーションが正常にされていることがわかった。</p> <h1 id="4.MySQLチューニング"><a href="#4.MySQL%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0">4.MySQLチューニング</a></h1> <h2 id="4.1"><a href="#4.1">4.1</a></h2> <p><code>innodb_log_file_size</code>ログファイルのサイズ(ディスク上)。<code>innodb_buffer_pool_size</code>の25~100%ぐらい。</p> kawai_mizugorou tag:crieit.net,2005:PublicArticle/18256 2022-07-23T23:14:58+09:00 2022-07-23T23:47:21+09:00 https://crieit.net/posts/PLC-JSON-6 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、PLCから取得したデータをデータベースに保存しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a></p> <p>タイトルに書かれたテーマは前回で完了しています。完了していますが、これでは何か物足りないと感じ、今回はデータベースに次々と書かれるPLCからのデータをブラウザに表示してみます。</p> <p><a href="https://crieit.now.sh/upload_images/e6061bc3683ceff53f53e09554a0288e62dbf2a4188d4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6061bc3683ceff53f53e09554a0288e62dbf2a4188d4.png?mw=700" alt="image" /></a></p> <h2 id="WEBアプリケーション"><a href="#WEB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">WEBアプリケーション</a></h2> <p>データベースのデータをWEBブラウザに表示するために、WEBアプリケーションをサーバ上に作成します。このサンプルでは、WEBアプリケーションはPHPスクリプトで実装します。</p> <p>いよいよ図が窮屈になってきました。これ以上に窮屈な図は、もはや理解容易性の面で逆効果です。正確さと理解容易性は、ある時点以後、反比例します。</p> <p><a href="https://crieit.now.sh/upload_images/a96212b429a0ca11f28e750801258aea62dbf49dd5ca8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a96212b429a0ca11f28e750801258aea62dbf49dd5ca8.png?mw=700" alt="image" /></a></p> <p>PHPである必要はありません。では、このサンプルを何故にPHPスクリプトで実装するのか?それは、そこ(私の開発環境)にPHPが稼働していたから。</p> <p>PHPの準備についてはここでは解説しません。PHPの準備についての解説は、他の記事にお任せします。PHPスクリプトでのMySQLアクセスについても、他の記事にお任せします。</p> <h2 id="HTMLとPHPスクリプト、そしてJavaScript"><a href="#HTML%E3%81%A8PHP%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6JavaScript">HTMLとPHPスクリプト、そしてJavaScript</a></h2> <p>結果的には、サーバ・サイドはPHPスクリプトのみではなく、以下の3つのファイルで構成してみました。</p> <ul> <li>HTML</li> <li>PHPスクリプト</li> <li>JavaScript</li> </ul> <p>3つめの JavaScript は jQuery です。jQuery は一般に配布されているとても便利なライブラリです。jQuery を利用することにより、とても高機能なWEBアプリケーションを短期間で実装することができます。jQuery の解説も他の記事にお任せします。</p> <p>以下がサンプルです。</p> <p>index.htm</p> <pre><code class="html"><html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="Cache-Control" content="no-cache"> <title>Sample</title> <script type="text/javascript" src="js/jquery-3.6.0.min.js"></script> <script type="text/javascript"> function count_update() { $.ajax({ url:"index.php", method:"POST", success:function(data) { $('#count').html(data); } }); } $(function() { setInterval ( function() { count_update(); }, 1000 ); }); </script> </head> <body> count: <div id="count" style="display: inline-block;">count</div> </body> </html> </code></pre> <p>index.php</p> <pre><code class="php"><?php try { $dbh = new PDO('mysql:dbname=hoge;host=localhost;charset=utf8;' , 'hoge' , 'hoge001'); } catch (PDOException $e) { echo "Can't connect to database: " . $e->getMessage() . "\n"; exit(); } $sql = "SELECT JSON_EXTRACT(body, '$.value') AS count FROM from_plc ORDER BY time_insert DESC"; $res= $dbh->query($sql); foreach($res as $value) { echo $value['count']; break; } ?> </code></pre> <p>jQuery は、HTML中で参照されている、jquery-3.6.0.min.js です。<br /> HTML中には、jQueryを呼び出すオリジナルのJavaScriptスクリプトを書いています。<br /> いずれもコードの解説はいたしません。</p> <h2 id="WEBブラウザでアクセスしてみる"><a href="#WEB%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">WEBブラウザでアクセスしてみる</a></h2> <p>WEBブラウザで上記の index.htm を参照すると以下ようになります。<br /> <a href="https://crieit.now.sh/upload_images/104c32d7402ec5f8908cc9021b18f35162dbfdbf6b841.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/104c32d7402ec5f8908cc9021b18f35162dbfdbf6b841.png?mw=700" alt="image" /></a><br /> 数字は、データベース上のテーブルが更新される都度変化してます。(正確には、JSONの要素"value"の値が変化してから1秒以内に、ブラウザの表示も変化します)</p> <h2 id="最後に"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB">最後に</a></h2> <p>ここまでサンプルとして実装したシステムを応用すると、工場の機器により製造される製品の生産数を、ほぼリアルタイムに画面表示する、といったシステムを構築することができます。<br /> 勘違いしていただきたくないのは、この記事で示したサンプルはあくまでもサンプルであって、基盤となるミドルウェアやプロトコル、プログラム言語は、この記事のサンプルで使用したものである必要はありません。もし、この記事で取り上げたような、PLCからデータを取得し、これを処理するようなシステムを設計・構築する場合は、この記事の内容にとらわれず、それぞれの環境、条件、実現したい機能などに応じて、最適なハードウェア、ミドルウェア、プロトコル、プログラム言語、そしてアーキテクチャを想像し選択してください。</p> <blockquote> <p>実はこのアーキテクチャを想像する、というのが設計者にとって最も楽しく重要な工程なのです。実装のテクニックなどは、アーキテクチャの上に成立する技術であって、アーキテクチャがイマイチなシステムは、おそらく実装もへんてこりんになってしまったり、どこかアンバランスなものになってしまうものです。</p> </blockquote> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18255 2022-07-22T18:43:07+09:00 2022-07-30T16:03:47+09:00 https://crieit.net/posts/PLC-JSON-5 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、データを保存するデータベースとテーブルを準備しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a></p> <p>今回は、MQTTブローカーからデータを受信するサブスクライバーを実装します。少々駆け足になりますが、サブスクライバーには、データを受信しつつデータベースに書き込むまでを実装します。<br /> <a href="https://crieit.now.sh/upload_images/9a527b4c96ab1ffcf8096821036def3462d966e06c6c1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9a527b4c96ab1ffcf8096821036def3462d966e06c6c1.png?mw=700" alt="image" /></a></p> <h2 id="プログラミング"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">プログラミング</a></h2> <p>Pythonを使用して実装します。Pythonでなければならない合理的な理由はありませんが、私の環境では、Pythonではだめだという理由もありません。</p> <p>PythonでMQTTをハンドリングには、paho-mqttというライブラリを使うと、新規コーディング量を減らすことができます。paho-mqttの導入については、このサイトをおすすめします。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/emqx_japan/items/b63c918fe137a6db4b37">Python で MQTT (Paho)</a></p> <p>同様に、PythonからMySQLをアクセスするには、mysql-connector-python-rfというライブラリを使うと、新規コーディング量を減らすことができます。mysql-connector-python-rfの導入については、このサイトをおすすめします。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/hoto17296/items/0cfe7cdd3c47b69cc892">Python 3 から MySQL を触る</a></p> <p>最低限の機能を実装してみました。</p> <pre><code class="python">import paho.mqtt.client as mqtt import json import mysql.connector #----------------------------- # mqtt host = '127.0.0.1' port = 1883 topic = 'sample' def on_connect(client, userdata, flags, respons_code): print('status {0}'.format(respons_code)) client.subscribe(topic) def on_message(client, userdata, msg): json_text = json.dumps(json.loads(msg.payload)) print(json_text) try: #----------------------------- # database conn = mysql.connector.connect( host='127.0.0.1', port=3306, user='hoge', password='hoge001', database='hoge' ) cur = conn.cursor() sql = "insert into from_plc (body) values (%s);" cur.execute(sql, (json_text,)) conn.commit() cur.close() conn.close() except Exception as e: print(e) print("Debug: Error at Database access.") return if __name__ == '__main__': client = mqtt.Client(protocol=mqtt.MQTTv311) client.on_connect = on_connect client.on_message = on_message client.connect(host, port=port, keepalive=60) client.loop_forever() </code></pre> <p>サンプル・プログラムは、MQTTブローカー(=mosquitto)に届く、Topic = sample のメッセージを待ち受けます。メッセージが届くと、メッセージ中のデータをJSON文字列に変換し、これをMySQLのテーブル"from_plc"に追加します。この動作をひたすら繰り返します。</p> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>Pythonスクリプトを起動しておきます。</p> <pre><code>c:\Users\hoge>py sample.py status 0 </code></pre> <p>これまでと同様に、PLCには、本物の代わりにModbus/TCPシミュレーターを使用します。<br /> Modbus/TCPシミュレーター"mod_RSsim.exe"を起動します。<br /> <a href="https://crieit.now.sh/upload_images/7c92f619579f92befb1d50f450ce74b062d96b0700adb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7c92f619579f92befb1d50f450ce74b062d96b0700adb.png?mw=700" alt="image" /></a><br /> ゲートウェイのシミュレーターを起動するために、EasyBuilderのメニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/a6e571139cf659163b06d6721ff09fbf62d96b6c39201.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a6e571139cf659163b06d6721ff09fbf62d96b6c39201.png?mw=700" alt="image" /></a><br /> シミュレーターの起動を確認します。<br /> <a href="https://crieit.now.sh/upload_images/12e87729c176c0ab6bf620f55f4ff1a262d96ba8913fc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/12e87729c176c0ab6bf620f55f4ff1a262d96ba8913fc.png?mw=700" alt="image" /></a><br /> Modbus/TCPシミュレータの、アドレス40001(注意: +0です)をクリックし、Valueに任意の数値を入力し値を変更します。[OK]ボタンで閉じます。<br /> <a href="https://crieit.now.sh/upload_images/f609d225a2af3983fd89312d7fb7770862d96bd14929c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f609d225a2af3983fd89312d7fb7770862d96bd14929c.png?mw=700" alt="image" /></a><br /> Modbus/TCPシミュレータの、アドレス40001の値を変更する都度、Pythonスクリプトがこの情報をJSONで受け取り、コンソールに表示します。<br /> Pythonスクリプトを止めるには、キーボードから<code>Ctrl+C</code>を押してください。</p> <pre><code>c:\Users\hoge>py sample.py status 0 {"value": 0, "ts": "2022-07-21T15:27:43.676079"} {"value": 1, "ts": "2022-07-21T15:27:49.704662"} {"value": 2, "ts": "2022-07-21T15:27:57.641088"} {"value": 3, "ts": "2022-07-21T15:28:07.644020"} {"value": 4, "ts": "2022-07-21T15:28:11.627588"} {"value": 5, "ts": "2022-07-21T15:28:14.659965"} Traceback (most recent call last): File "c:\Users\hoge\sample.py", line 44, in <module> client.loop_forever() File "C:\Users\hoge\AppData\Local\Programs\Python\Python310\lib\site-packages\paho\mqtt\client.py", line 1756, in loop_forever rc = self._loop(timeout) File "C:\Users\hoge\AppData\Local\Programs\Python\Python310\lib\site-packages\paho\mqtt\client.py", line 1150, in _loop socklist = select.select(rlist, wlist, [], timeout) KeyboardInterrupt ^C c:\Users\hoge>py sample.py </code></pre> <p>MySQLのテーブル"from_plc"を確認すると、ゲートウェイからのJSONデータが保存されているのはわかります。</p> <pre><code>c:\Users\hoge>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 24 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. 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> use hoge Database changed mysql> select * from from_plc; +--------------------------------------------------+---------------------+ | body | time_insert | +--------------------------------------------------+---------------------+ | {"ts": "2022-07-21T15:27:43.676079", "value": 0} | 2022-07-21 23:27:43 | | {"ts": "2022-07-21T15:27:49.704662", "value": 1} | 2022-07-21 23:27:49 | | {"ts": "2022-07-21T15:27:57.641088", "value": 2} | 2022-07-21 23:27:57 | | {"ts": "2022-07-21T15:28:07.644020", "value": 3} | 2022-07-21 23:28:07 | | {"ts": "2022-07-21T15:28:11.627588", "value": 4} | 2022-07-21 23:28:11 | | {"ts": "2022-07-21T15:28:14.659965", "value": 5} | 2022-07-21 23:28:14 | +--------------------------------------------------+---------------------+ 6 rows in set (0.00 sec) mysql> </code></pre> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>PLCのデータをデータベースにJSONで書きました。もう次回は不要かもしれませんが、せっかくデータベースに書いたので、次回はこれをブラウザに表示させます。くどいなあ。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18254 2022-07-21T09:17:30+09:00 2022-07-23T23:37:55+09:00 https://crieit.net/posts/PLC-JSON-4 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 初回から前回にかけては、ゲートウェイを使って、PLCから取得したデータをMQTTでブローカーに送信するまでを実装しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a></p> <p>今回はデータベースを準備します。<br /> <a href="https://crieit.now.sh/upload_images/348f9673721149a9206023ad2b3ca8e062d7eb7ab1449.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/348f9673721149a9206023ad2b3ca8e062d7eb7ab1449.png?mw=700" alt="image" /></a></p> <h2 id="データベース"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9">データベース</a></h2> <p>このサンプルではMySQLを使用します。MySQLでなければいけない理由はまったくありません。MySQLを選択した理由は、そこ(私の開発環境、作業場ですね)で動いていたからです。<br /> テーマを「JSONで保存」とした以上は、JSON型をサポートしているDBMSである必要はあるのですが、MySQL以外にもJSON型をサポートしているDBMSはあります。</p> <p>ユーザとデータベースは事前に作成されていたものを使います。これらの作成については解説はいたしません。</p> <p>MySQLにおける、データベースとユーザの作成については、こちらを参照ください。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.dbonline.jp/mysql/database/">データベースの作成</a><br /> <a target="_blank" rel="nofollow noopener" href="https://www.dbonline.jp/mysql/user/">ユーザーの作成</a></p> <h2 id="テーブルを作成する"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">テーブルを作成する</a></h2> <p>任意のユーザとデータベースでテーブルを作成します。</p> <pre><code>hoge@localhost C:\Users\hoge>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 9 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. 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> use hoge Database changed mysql> </code></pre> <p>データを保存するためのテーブルをひとつだけ作成します。</p> <pre><code>mysql> create table from_plc ( -> body json, -> time_insert timestamp default current_timestamp -> ); Query OK, 0 rows affected (0.17 sec) mysql> show full columns from from_plc; +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ | body | json | NULL | YES | | NULL | | select,insert,update,references | | | time_insert | timestamp | NULL | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | select,insert,update,references | | +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ 2 rows in set (0.07 sec) mysql> </code></pre> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>続きは次回とさせていただきます。<br /> MySQLのセットアップを行われた方がいるとすれば、その方にとっては、この記事の中で最も時間を要した回であったはずです。そういった方に配慮し、今回は短めとし続きは次回とさせていただきます。<br /> 私の記事は先を急ぎませんので。<br /> 次回は、いよいよデータをデータベースに書き込みます。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18252 2022-07-20T08:40:18+09:00 2022-07-23T23:36:59+09:00 https://crieit.net/posts/PLC-JSON-3 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、ゲートウェイを用いてPLCからデータを取得し、ゲートウェイのメモリに保存しました。<br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a></p> <p>今回はこのデータをサーバに送信します。<br /> <a href="https://crieit.now.sh/upload_images/99f8027812fe841fafbb82cb316db3e062d514f019ff6.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/99f8027812fe841fafbb82cb316db3e062d514f019ff6.png?mw=700" alt="image" /></a></p> <p>サンプルで使用するゲートウェイは、引き続きWEINTEK製ゲートウェイの開発環境であるEasyBuilderのシミュレーターです。WEINTEK製ゲートウェイはサーバへのデータ送信に2種類のインタフェースを持っており、そのひとつがMQTTです。サンプルではMQTTを使用してサーバにデータを送信します。</p> <p>IT技術より生産現場の技術に強い方は、"MQTT"とか登場した時点で「もういやだな」となるかもしれません。そういった方はロガーの類が欲しくなりますよね。わかります。この記事ではロガーの類はまったく登場しません。この先を読み進めるかどうか、ご自由にご判断ください。</p> <p>サーバ側にはMQTTのブローカーが必要となります。サンプルでは、MQTTブローカーとしてMosquittoを使用します。<br /> <a href="https://crieit.now.sh/upload_images/ad4d70ef98a1d741deedb1e19d7eef3762d52f8173167.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ad4d70ef98a1d741deedb1e19d7eef3762d52f8173167.png?mw=700" alt="image" /></a></p> <p>Mosquittoはテストの場面で必要となります。サンプルの実装に加えて、テストをしてみたいという方は、自己責任でMosquitto、あるいは他のMQTTブローカーを準備してください。Mosquittoの準備については以下を参照してください。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/koichi_baseball/items/8fa9e0bdbe6d0aebe57d">【MQTT】MQTTの導入 mosquittoのインストール/動作確認まで</a></p> <h2 id="ゲートウェイのMQTT送信設定"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AEMQTT%E9%80%81%E4%BF%A1%E8%A8%AD%E5%AE%9A">ゲートウェイのMQTT送信設定</a></h2> <p>データをサーバに送信するために、前回まで作成したプロジェクトに設定を加えます。</p> <p>EasyBuilderのメニュー[IIoT/エネルギー]-[MQTT]をクリックすます。<br /> <a href="https://crieit.now.sh/upload_images/27403bdd1beb17ed91a9e1faad0c1ca462d5180119baf.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/27403bdd1beb17ed91a9e1faad0c1ca462d5180119baf.png?mw=700" alt="image" /></a></p> <p>[有効にする]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/419a1bed20d2da61b4a260fcfaa1a92f62d518b58b47d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/419a1bed20d2da61b4a260fcfaa1a92f62d518b58b47d.png?mw=700" alt="image" /></a></p> <p>[一般的な属性]はデフォルトのままです。サンプルでは、ゲートウェイのシミュレーターと同じWindowsでMosquittoを動作させるので、IPアドレスは127.0.0.1のままです。[OK]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/3643faef7ae563403b18ecd92652d34562d5191b1eb6a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3643faef7ae563403b18ecd92652d34562d5191b1eb6a.png?mw=700" alt="image" /></a></p> <p>[トピック発行者]タブで[新規作成]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/0602c7b6837e71a182c385cd85680f2862d51a01c7f24.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0602c7b6837e71a182c385cd85680f2862d51a01c7f24.png?mw=700" alt="image" /></a><br /> <a href="https://crieit.now.sh/upload_images/999d37a64349d3064b39bf08d0b2694f62d51a4c34273.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/999d37a64349d3064b39bf08d0b2694f62d51a4c34273.png?mw=700" alt="image" /></a></p> <p>[一般的な属性]タブで以下のように設定します。<br /> 中ほどの設定、[アドレス]のLBと0は、LB0がONになったらMQTTでサーバにデータを送信する、という意図です。<br /> <a href="https://crieit.now.sh/upload_images/01b4e17a1817470a004336900fa7ffbb62d51b4209fc2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/01b4e17a1817470a004336900fa7ffbb62d51b4209fc2.png?mw=700" alt="image" /></a></p> <p>[アドレス]タブに移り、[新規作成]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/59910b8f3292a98b49714a41816f8c3762d51c263f6ec.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59910b8f3292a98b49714a41816f8c3762d51c263f6ec.png?mw=700" alt="image" /></a></p> <p><a href="https://crieit.now.sh/upload_images/58b56e5ee811a948f574326534769a5762d51c2fa8f19.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/58b56e5ee811a948f574326534769a5762d51c2fa8f19.png?mw=700" alt="image" /></a></p> <p>ダイアログに以下のように設定し、[OK]ボタンをクリックします。<br /> [アドレス]の設定LWと0は、LW0にあるデータを送信する、という意図です。LW0には、PLCから取得したデータが置かれています。<br /> <a href="https://crieit.now.sh/upload_images/de2bc7ffeecf75b76dec7522f7a27fd162d51cb082328.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/de2bc7ffeecf75b76dec7522f7a27fd162d51cb082328.png?mw=700" alt="image" /></a></p> <p>[MQTTトピック発行者オブジェクト属性]ウィンドウの[OK]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/bc638bc4ee1e717109c9a3a6157e5d5f62d51d48b7489.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bc638bc4ee1e717109c9a3a6157e5d5f62d51d48b7489.png?mw=700" alt="image" /></a></p> <p>[MQTT]ウィンドウの[終了]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/9534a197b16903a10274457bdd2e71e662d51dbac95f8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9534a197b16903a10274457bdd2e71e662d51dbac95f8.png?mw=700" alt="image" /></a></p> <p>以上で、ゲートウェイのMQTT設定は完了です。</p> <h2 id="ゲートウェイのプログラミング"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">ゲートウェイのプログラミング</a></h2> <p>前回作成したゲートウェイのプログラムに、若干のステップを追加します。</p> <p>EasyBuilderのメニュー[プロジェクト]-[マクロ]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d5216eb34e4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d5216eb34e4.png?mw=700" alt="image" /></a><br /> <a href="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d52185a7fbe.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d52185a7fbe.png?mw=700" alt="image" /></a></p> <p>[ID : 001]をダブル・クリックします。<br /> <a href="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d52233bc50d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d52233bc50d.png?mw=700" alt="image" /></a></p> <p>以下のように、ステップを追加します。<br /> <a href="https://crieit.now.sh/upload_images/48e72c305c89ae10a73ee74cb389156462d5295243ba1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/48e72c305c89ae10a73ee74cb389156462d5295243ba1.png?mw=700" alt="image" /></a><br /> 6〜10行目では、今回取得したデータと前回取得したデータを比較して、同じデータであったら何もしない、と書いています。業務上、連続する同じデータは不要という場合に、サーバに無駄なデータを送信しません。<br /> 13〜14行目は、MQTT送信をキックするために、ゲートウェイのLB0をオンにします。LB0はMQTT送信後に自動でオフになります(そのようにMQTTを設定しました)。</p> <p>コピーしたい方はこちらからどうぞ。</p> <pre><code>macro_command main()         unsigned short        x40001 = 0         GetData(x40001, "MODBUS TCP/IP (32-Bit)", 4x, 1, 1)                  unsigned short        LW0 = 0         GetData(LW0, "Local HMI", LW, 0, 1)         if x40001 == LW0 then                 return         end if         SetData(x40001, "Local HMI", LW, 0, 1)         bool        LB0 = 1                 SetData(LB0, "Local HMI", LB, 0, 1) end macro_command </code></pre> <p>エディタの[閉じる]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d56c6e2048c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d56c6e2048c.png?mw=700" alt="image" /></a></p> <p>[コンパイルが成功]に[ID : 000]があることを確認します。<br /> <a href="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d56ca4cea61.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d56ca4cea61.png?mw=700" alt="image" /></a></p> <blockquote> <h3 id="連続する同じデータは不要"><a href="#%E9%80%A3%E7%B6%9A%E3%81%99%E3%82%8B%E5%90%8C%E3%81%98%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AF%E4%B8%8D%E8%A6%81">連続する同じデータは不要</a></h3> <p>連続する同じデータは不要という仕様は、よくある仕様です。例えば、工場の生産マシンで生産される製品の数をPLCで数えている場合、そのカウンタがこれに該当します。変化のないカウント値は不要なはずです。この不要なデータをネットワークやサーバに垂れ流すと、ネットワークやサーバにとって無駄な負荷となります。データベースのサイズも無駄に膨れ上がります。「IoTのデータは無駄なデータばかりだけど、そういうものだ」という誤った悟りを持った設計者を多く見かけますが、実は、設計次第でエレガントなシステムになります。想像が足りないだけなのです。<br /> 時系列データベースが提供する重複排除機能などは、あったほうが便利、という程度です。排除する前に発生させなければ良いのです。ただし、ロガーなどを使って無秩序に記録したデータをデータベースにロードする、というような場合は、不要なデータの排除に苦労します。<br /> 連続する同じデータをサーバ側で捨てるテクニックもあります。これについては機会があれば、別途解説をさせていただきます。</p> </blockquote> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>テストには、MQTTブローカーと、一時的にテストで使用するMQTTの汎用サブスクライバーが必要となります。<br /> これらは、Mosquittoが提供してくれます。Mosquittoの準備については以下を参照してください。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/koichi_baseball/items/8fa9e0bdbe6d0aebe57d">【MQTT】MQTTの導入 mosquittoのインストール/動作確認まで</a></p> <p>私の開発・テスト環境では、MosquittoブローカーはWindowsの設定で自動起動にしてあり、常に実行中です。<br /> <a href="https://crieit.now.sh/upload_images/d3c132e88775428a7334c810a8606fc362d5203d78fd4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d3c132e88775428a7334c810a8606fc362d5203d78fd4.png?mw=700" alt="image" /></a></p> <blockquote> <p>注意: 他のサーバ上のMosquittoへ送信する場合には注意が必要です。Mosquittoは、標準の設定では外部からのメッセージを受け付けません。この場合は、外部からのメッセージを受け付けるよう、Mosquittoの設定を変更する必要があります。</p> </blockquote> <p>受信側となるMQTTサブスクライバーを起動しておきます。<br /> Mosquittoにはコマンド起動可能な簡単なサブスクライバーが付属しています。Windowsでは、このコマンドはMosquittoのインストール・フォルダにあります。<br /> 以下のように起動しておきます。</p> <pre><code>c:\Program Files\mosquitto>mosquitto_sub -h localhost -t sample </code></pre> <p>オプション -h localhost は、localhost上のブローカーから受信することを示しています。-t sample はトピック名を示しており、ゲートウェイのMQTT設定で設定したトピック名と同じである必要があります。mosquitto_sub はトピックsampleのメッセージをひたすら待ち続けます。</p> <p>前回のテストと同様に、Modbus/TCPシミュレーター"mod_RSsim.exe"を起動します。</p> <blockquote> <p>注意: "mod_RSsim.exe"は連続起動に時間制限がある点に注意してください。制限を超えた場合は再起動させます。</p> </blockquote> <p><a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d52790afb85.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d52790afb85.png?mw=700" alt="image" /></a></p> <p>EasyBuilderのメニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/439fd0e9432caba5e9ed0546875106a462d528085da76.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/439fd0e9432caba5e9ed0546875106a462d528085da76.png?mw=700" alt="image" /></a></p> <p>シミュレーターが起動したことを確認します。<br /> <a href="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d5285c8e0d4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d5285c8e0d4.png?mw=700" alt="image" /></a></p> <p>前回のテストと同様に、Mdobus/TCPシミュレーターのアドレス40001のデータを任意の数値に書き換えます。<br /> <a href="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d5287c3564a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d5287c3564a.png?mw=700" alt="image" /></a></p> <p>Mosquittoのサブスクライバーが、受信したデータをコンソールに出力します。<br /> <a href="https://crieit.now.sh/upload_images/e6004c9810a653ca72e7b22384c6d9b862d525e0e1ffc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6004c9810a653ca72e7b22384c6d9b862d525e0e1ffc.png?mw=700" alt="image" /></a></p> <p>ゲートウェイのプログラムで、データに変化があった場合にのみ送信、としているため同じデータが次々と送られてくることはありません。ゲートウェイを単なるプロトコル変換器だと考えているなら、それは誤りです。</p> <p>ゲートウェイのMQTT設定にて、タイムスタンプを付与するよう設定したため、tsという名前でゲートウェイの時刻が送られてきます。</p> <blockquote> <p>このゲートウェイの時刻はサーバの時刻と必ずしも一致しない、という点を設計上留意してください。時刻の取扱いについては別途、解説をさせていただきます。</p> </blockquote> <p>以上でテストは終了です。</p> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>続きは次回とさせていただきます。<br /> 次回は、データベースを準備します。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18251 2022-07-19T16:02:44+09:00 2022-07-23T23:36:00+09:00 https://crieit.net/posts/PLC-JSON-2 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 今回はその2回目、ゲートウェイによるPLCからのデータ取得です。<br /> <a href="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4d91f05808.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4d91f05808.png?mw=700" alt="image" /></a><br /> サンプルの環境については第一回で解説しています。必ず第一回を参照し、前提となる環境を理解した上で以下の解説を参照してください。<br /> <a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a></p> <h2 id="想定: PLC上のデータ"><a href="#%E6%83%B3%E5%AE%9A%3A+PLC%E4%B8%8A%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF">想定: PLC上のデータ</a></h2> <p>サンプルでは、取得するPLC上のデータを以下のように想定しています。<br /> <a href="https://crieit.now.sh/upload_images/520f28de070aceae292d971ae449b7a762d4e11edce3b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/520f28de070aceae292d971ae449b7a762d4e11edce3b.png?mw=700" alt="image" /></a></p> <blockquote> <h3 id="Modbus/TCP"><a href="#Modbus%2FTCP">Modbus/TCP</a></h3> <p>Modbus/TCPについては、このドキュメントが理解しやすいドキュメントです。M・SYSTEM技研さまに感謝です。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.m-system.co.jp/mssjapanese/kaisetsu/nmmodbus.pdf">M・SYSTEM技研さま Modbusプロトコル概説書</a></p> <p>間違っていただきたくないのは、"Modbus/TCP"とは、PLCが自身のメモリを後悔するためのインタフェースのひとつにすぎない、ということです。"Modbus/TCP"が貴重なプロトコルである理由を述べます。PLCが自身のメモリを公開するためのプロトコルは他にもたくさんあります。そして、それらの多くは各PLCメーカー固有のものです。Modbus/TCPの特徴は、複数のPLCメーカー間で横断で採用されているプロトコルという点です。複数のメーカーのPLCは、標準ではModbus/TCPが使用できずオプションとして提供され、標準では当該メーカー固有のプロトコルがサポートされます。</p> <h3 id="Holding Registers"><a href="#Holding+Registers">Holding Registers</a></h3> <p>"Holding Registers"とは、Modbus/TCPが定める複数のメモリ領域のうちのひとつです。主に、数値や文字を記憶するために用いられます。アドレスは40001〜です。サンプルでは、40001上のデータをターゲットにします。<br /> 今回、PLCの代わりに活躍してくれる"mod_RSsim.exe"では、このような画面で眺めることが出来ます。<br /> <a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4ea62383a2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4ea62383a2.png?mw=700" alt="image" /></a></p> </blockquote> <h2 id="ゲートウェイの設定とプログラミング"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%A8%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">ゲートウェイの設定とプログラミング</a></h2> <p>前回は、ゲートウェイを設定し、PLCに見立てたModbus/TCPシミュレーターに接続しました。<br /> 今回は、ゲートウェイを使って、PLCに見立てたModbus/TCPシミュレーターからデータを取得します。サンプルでは前回と同様に、ゲートウェイの代わりにWEINTEK製ゲートウェイのソフトウェア開発ツールであるEasyBuilderのシミュレーターを使用します。</p> <p>前回作成したEasyBuilderのプロジェクトを開き、メニュー[プロジェクト]-[マクロ]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d4f019b0314.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d4f019b0314.png?mw=700" alt="image" /></a><br /> <a href="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d4f0418d4a6.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d4f0418d4a6.png?mw=700" alt="image" /></a></p> <p>[マクロ]ウィンドウから[新規作成]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/3dc7453b874086ec512519c0c059939862d4f063d178d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3dc7453b874086ec512519c0c059939862d4f063d178d.png?mw=700" alt="image" /></a></p> <p>[マクロエディター]に以下のようにコードを記述します。<br /> [周期的に実行する]オプションをオンにし、時間間隔を10にします。<br /> <a href="https://crieit.now.sh/upload_images/418d778add1fbee6a591cd7db72ef99462d4f0998d976.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/418d778add1fbee6a591cd7db72ef99462d4f0998d976.png?mw=700" alt="image" /></a><br /> コピーしたい方はこちらからどうぞ。</p> <pre><code>macro_command main()         unsigned short        x40001 = 0         GetData(x40001, "MODBUS TCP/IP (32-Bit)", 4x, 1, 1)         SetData(x40001, "Local HMI", LW, 0, 1) end macro_command </code></pre> <p>[マクロエディター]の下にある[閉じる]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d4f113d33c3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d4f113d33c3.png?mw=700" alt="image" /></a></p> <p>[マクロ]ウィンドウの[コンパイルが成功]に新たなマクロが追加されたら、ひとまずプログラミングは終了です。<br /> <a href="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d4f137a479e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d4f137a479e.png?mw=700" alt="image" /></a></p> <blockquote> <h3 id="マクロ=プログラムの意図"><a href="#%E3%83%9E%E3%82%AF%E3%83%AD%3D%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E6%84%8F%E5%9B%B3">マクロ=プログラムの意図</a></h3> <p>Modbus/TCPが定める"Holding Register"(アドレス: 40001〜)は、EasyBuilderでは"4x"で解釈されます。<br /> EasyBuilderでは、対象装置(今回はModbus/TCPシミュレーター)のメモリ・マップを参照することができます。EasyBuilderの[アドレス]ペイン(ウィンドウ)にて、装置名、ステーション番号、アドレスタイプを以下のように設定すると、ひとつめのアドレスが赤になっているはずです。これは、作成したマクロがこのアドレスを参照している、という証です。<br /> <a href="https://crieit.now.sh/upload_images/26365e0800f525da7b98106b7e7bd71062d4f28eba07f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/26365e0800f525da7b98106b7e7bd71062d4f28eba07f.png?mw=700" alt="image" /></a><br /> 今回作成したコードでは、行4で、対象装置の"4x"のひとつめのアドレスから1ワードだけデータを取得しています。対象装置は”MODBUS TCP/IP (32-Bit)”です。これは、前回、システムパラメータで設定した対象装置の「装置名」です。<br /> 行4で取得したデータを、行5で自らのメモリ"LW"のひとつめのアドレスにコピーしています。EasyBuilder(=WEINTEK製ゲートウェイ)も複数のメモリ領域をもっており、LWはそのうちのひとつです。LWは16bitワードであり、Modbus/TCPのHolding Registerと同じワード・サイズです。<br /> <a href="https://crieit.now.sh/upload_images/5e787b399f3f70441d4ec0226b16d5de62d4f52debe24.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5e787b399f3f70441d4ec0226b16d5de62d4f52debe24.png?mw=700" alt="image" /></a><br /> [周期的に実行する]オプションのオンはこのプログラムを周期的に実行することを意味します。そしてその周期を今回は1000m秒(=1秒)としています。<br /> このプログラムで、PLCからデータを取得する、という今回の目的は達成されるはずです。</p> <h3 id="周期的に実行する"><a href="#%E5%91%A8%E6%9C%9F%E7%9A%84%E3%81%AB%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">周期的に実行する</a></h3> <p>とても残念なことに、PLCからデータを取得するゲートウェイは、PLC側のデータ変更等のイベントをもって振る舞いを起こすということが出来ません。これは、完全にPLC側の都合によるものです。このため、ゲートウェイがPLC上のデータを参照するとき、ゲートウェイは空振りすることを承知でPLCのメモリを周期的に参照します。いまどきのシステムとしては実に残念です。"Factory Automation"の技術者は、ITの世界ではとっくに古典となった"Message Passing"や"Data Driven"といった考え方すら学んでいないのです。こういった問題は当面は放置され続けるでしょう。</p> </blockquote> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>前回と同様に、PLCには、本物の代わりにModbus/TCPシミュレーターを使用します。</p> <p>Modbus/TCPシミュレーター"mod_RSsim.exe"を起動しておきます。<br /> <a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4f9862eaeb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4f9862eaeb.png?mw=700" alt="image" /></a></p> <p>EasyBuilderのメニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4f99fb1415.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4f99fb1415.png?mw=700" alt="image" /></a></p> <p>シミュレーターが起動したら、メニュー[Object]を選択し、右側にある[+]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/f596a7ef9d3f9ee63dadb64cd89f981d62d4fa3b2cb36.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f596a7ef9d3f9ee63dadb64cd89f981d62d4fa3b2cb36.png?mw=700" alt="image" /></a></p> <p>ダイアログにて以下のように入力し[OK]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/8fc3bd495e156941cea03c12cf981d6062d4faaf89f7f.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8fc3bd495e156941cea03c12cf981d6062d4faaf89f7f.png?mw=700" alt="image" /></a></p> <p>Modbus/TCPシミュレータの、アドレス40001(注意: +0です)をクリックし、Valueに任意の数値を入力、[OK]ボタンで閉じます。<br /> <a href="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d4fc4dbfc1b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d4fc4dbfc1b.png?mw=700" alt="image" /></a><br /> アドレス40001の表示が変わります。<br /> <a href="https://crieit.now.sh/upload_images/4b7477028132e4aa88253b72874d6e0d62d50fdb774c8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4b7477028132e4aa88253b72874d6e0d62d50fdb774c8.png?mw=700" alt="image" /></a></p> <p>EasyBuilderのシミュレータ上で、LW-0も変更されることを確認します。<br /> <a href="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d4fb4d7cf76.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d4fb4d7cf76.png?mw=700" alt="image" /></a></p> <p>これでテストは終わりです。数行のプログラムのテストとしては十分でしょう。</p> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>続きは次回とさせていただきます。<br /> 今回は、初心者には少しだけ難解であったかもしれません。慌てずじっくり、しっかり理解して進んでください。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18249 2022-07-18T11:42:52+09:00 2022-07-23T23:35:09+09:00 https://crieit.net/posts/PLC-JSON-1 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> <a href="https://crieit.now.sh/upload_images/4bef24e301bab8c385bc5659d66fab1262d4b8451782b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4bef24e301bab8c385bc5659d66fab1262d4b8451782b.png?mw=700" alt="image" /></a><br /> 初回は、ゲートウェイでPLCのデータを取得します。<br /> <a href="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4b8537ad2c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4b8537ad2c.png?mw=700" alt="image" /></a><br /> サンプルは、Windowsがあれば環境の準備と動作が可能です。よろしければ、自己責任のもとで環境を準備し、サンプルを動作させてみてください。</p> <h2 id="PLC"><a href="#PLC">PLC</a></h2> <p>この解説でのサンプルでは、PLCの代わりにModbus/TCPシミュレーターを使用します。これにより、PLCの本物がなくても、実装の学習とテストが可能となります。<br /> 今回使用したModbus/TCPシミュレーターは、貴重なmod_RSsim.exeです。もちろん、他のシミュレータでも構いません。</p> <ul> <li>Modbus/TCPシミュレーター: MOD-RSsim Version 8.20</li> </ul> <p><a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4afc50041e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4afc50041e.png?mw=700" alt="image" /></a></p> <h2 id="ゲートウェイ"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4">ゲートウェイ</a></h2> <p>この解説のサンプルでは、WEINTEK製ゲートウェイを想定します。WEINTEK製ゲートウェイのソフトウェア開発ツールは、Windows上でソフトウェアを書くことができることに加え、ゲートウェイの動きをシミュレートすることが出来ます。これにより、ゲートウェイの本物がなくても、実装の学習とテストが可能となります。</p> <ul> <li>WEINTEK製ゲートウェイのソフトウェア開発環境: EasyBuilder Pro</li> </ul> <p>EasyBuilderはWEINTEKのサイトからダウンロードすることが出来ます。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.weintek.com/globalw/Download/Download.aspx">https://www.weintek.com/globalw/Download/Download.aspx</a></p> <h2 id="ゲートウェイの設定とプログラミング"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%A8%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">ゲートウェイの設定とプログラミング</a></h2> <p>ゲートウェイは各社、各モデルで実装のスタイルが大きく異なります。しかし、この解説では、多くのゲートウェイでの機能実装が可能となるよう勘案し、よりシンプルな手順になるよう考慮しています。<br /> 前述のとおり、この解説では、ゲートウェイの本物を使用しません。代わりにEasyBuilderという、WEINTEK製ゲートウェイの開発環境を使用します。</p> <p>EasyBuilder Pro を起動します。<br /> <a href="https://crieit.now.sh/upload_images/70b6249e55a43645fa24782555dba08a62d4bfe2afa50.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/70b6249e55a43645fa24782555dba08a62d4bfe2afa50.png?mw=700" alt="image" /></a></p> <p>プロジェクトを新たに作成します。<br /> これから行う設定が、どのゲートウェイの設定であるかをゲートウェイのモデルの一覧から選択します。このサンプルでは、最もシンプルなモデル"cMT-G01"を選択します。<br /> <a href="https://crieit.now.sh/upload_images/73133d6f86ad19a49ce3d64ec95ce1a662d4c0da0fe55.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/73133d6f86ad19a49ce3d64ec95ce1a662d4c0da0fe55.png?mw=700" alt="image" /></a><br /> "cMT-G01"とはこのようなハードウェアです。<br /> <a target="_blank" rel="nofollow noopener" href="https://dl.weintek.com/public/cMT/eng/Datasheet/cMT-G01_Datasheet_ENG.pdf">cMT-G01</a></p> <p>このゲートウェイが対象にするPLCを設定します。メニュー[ホーム]-[システムパラメータ]を選択します。<br /> <a href="https://crieit.now.sh/upload_images/e1fd4a5385cd74a43bada117619cb52762d4c1ed7671d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e1fd4a5385cd74a43bada117619cb52762d4c1ed7671d.png?mw=700" alt="image" /></a></p> <p>[装置]タブを選択します。<br /> <a href="https://crieit.now.sh/upload_images/173c66d906d17f0d2905b9713ccd038c62d4c305cc7ca.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/173c66d906d17f0d2905b9713ccd038c62d4c305cc7ca.png?mw=700" alt="image" /></a></p> <p>[装置/サーバを新規追加]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/116c9134b0a53cfc0182069b0ee3f37a62d4e5e3e346e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/116c9134b0a53cfc0182069b0ee3f37a62d4e5e3e346e.png?mw=700" alt="image" /></a></p> <p>以下のように設定します。<br /> <a href="https://crieit.now.sh/upload_images/b4813a2fca2935a56983ddc1dfcefddc62d4c2a3aec24.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b4813a2fca2935a56983ddc1dfcefddc62d4c2a3aec24.png?mw=700" alt="image" /></a><br /> PLCに相当するModbus/TCPシミュレータは、EasyBuilderと同じWindowsマシン上で稼働させる予定です。このため装置(=PLC=Modbus/TCPシミュレータ)のIPアドレスを127.0.0.1にしています。<br /> 装置のポートはModbus/TCPの標準である502を選択しています。</p> <blockquote> <p>上記設定は、この解説におけるサンプルのための設定です。現実は、対象とするPLCやリモートIOなどに応じた設定が必要となります。<br /> 例えば、対象の装置が三菱電機のMELSECであれば、該当のモデルを選択します。下は、Mitsubishi QJ71E71の例です。<br /> <a href="https://crieit.now.sh/upload_images/cf6a77fc1f36d91ac1b06786216393f462d634aac0aa8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cf6a77fc1f36d91ac1b06786216393f462d634aac0aa8.png?mw=700" alt="Mitsubishi QJ71E71 の例" /></a></p> </blockquote> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>ここまでで、PLC(=Modbus/TCPシミュレータ)に接続する設定が終わりました。接続のテストをします。</p> <p>mod_RSsim.exeを起動しておきます。<br /> <a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4c596c61b2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4c596c61b2.png?mw=700" alt="image" /></a></p> <p>次はEasyBuilderです。<br /> もちろん、これまでの設定をゲートウェイ(cTM-G01)にロードしてゲートウェイを動かすことも可能ですが、今回はシミュレーターを使います。<br /> メニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4c5d9df4b8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4c5d9df4b8.png?mw=700" alt="image" /></a></p> <p>シミュレーターが起動します。"MODBUS TCP/IP (32-Bit)"に接続済みであるように見えますが、これはあてにはなりません。<br /> <a href="https://crieit.now.sh/upload_images/2b4ac9177034a63ae4627b253d2cade562d4c6d9f2b98.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2b4ac9177034a63ae4627b253d2cade562d4c6d9f2b98.png?mw=700" alt="image" /></a></p> <p>Modbus/TCPシミュレーターのウィンドウの左上にある"Connected"の表示を確認します。ゲートウェイからの接続が行われていれば、[1/10]となります。<br /> <a href="https://crieit.now.sh/upload_images/320155af5baceef2c37476485fd2741962d4e7455062b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/320155af5baceef2c37476485fd2741962d4e7455062b.png?mw=700" alt="image" /></a></p> <h2 id="次回へ"><a href="#%E6%AC%A1%E5%9B%9E%E3%81%B8">次回へ</a></h2> <p>続きは次回とさせていただきます。<br /> 初心者の方々のために、ゆっくりとマッタリと進めさせていただきます。<br /> 次回は、ゲートウェイによるPLCからのデータ取得です。</p> <p><a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18234 2022-07-12T23:45:36+09:00 2022-07-12T23:45:36+09:00 https://crieit.net/posts/mysql80-with-recursive-in-standalone-eloquent-20220712 MySQL8.0 の再帰クエリを単独の Eloquent で使ってみる <p>ちょうどやりたいことができそうだったので、 MySQL8.0 で新しく追加された再帰クエリを使ってみました。なお、単独インストールした Eloquent での使用を想定しているので、リレーションの機能をそのまま使うのは難しそうなケースでした。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>内容としてはありがちな部署マスタのようなもので、親子関係まで見たいと考えたためでした。</p> <div class="table-responsive"><table> <thead> <tr> <th align="right">id</th> <th align="right">parent</th> <th align="left">name</th> </tr> </thead> <tbody> <tr> <td align="right">1</td> <td align="right">0</td> <td align="left">営業部</td> </tr> <tr> <td align="right">2</td> <td align="right">1</td> <td align="left">営業1課</td> </tr> <tr> <td align="right">3</td> <td align="right">1</td> <td align="left">営業2課</td> </tr> <tr> <td align="right">4</td> <td align="right">0</td> <td align="left">システム部</td> </tr> <tr> <td align="right">5</td> <td align="right">4</td> <td align="left">開発課</td> </tr> <tr> <td align="right">6</td> <td align="right">4</td> <td align="left">運用保守課</td> </tr> <tr> <td align="right">7</td> <td align="right">5</td> <td align="left">フロントエンドグループ</td> </tr> <tr> <td align="right">8</td> <td align="right">5</td> <td align="left">バックエンドグループ</td> </tr> </tbody> </table></div> <p>イメージとしてはこんなイメージのテーブルがあったとして、例えば「システム部」と指定した場合、「開発課」「運用保守課」「フロントエンドグループ」「バックエンドグループ」のIDを子部署として参照したい、というようなケースです。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php">$hogeObjectsArray = $this->dbConnect->connection('hogera') ->select(" WITH recursive child( depth, id, parent, name ) AS ( SELECT 0, id, parent, name FROM hoge WHERE id = {$foo} UNION ALL SELECT child.depth + 1, hoge.id, hoge.parent, hoge.name FROM hoge, child WHERE hoge.parent = child.id ) SELECT depth, id, parent, name FROM child ORDER BY depth "); </code></pre> <p>最終的には上述の形で取得できました。</p> <p><code>->table($this->table)</code> 等でテーブルを指定せず、いきなり <code>->select()</code> しているのがミソです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="with recursive"><a href="#with+recursive">with recursive</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://codezine.jp/article/detail/2679">MySQL 8.0の再帰With句のサンプル集 (1\/3):CodeZine(コードジン)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/rstliz/articles/65278841ac26f7">MySQL 再帰クエリで階層構造のデータを一度に取得</a></li> </ul> <h3 id="単独の Eloquent 内で with recursive を使用したい"><a href="#%E5%8D%98%E7%8B%AC%E3%81%AE+Eloquent+%E5%86%85%E3%81%A7+with+recursive+%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84">単独の Eloquent 内で with recursive を使用したい</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/okumurakengo/items/0670ce39d758ed65e8e7">100行ぐらいのSQLをLaravelのEloquent\/Query Builderで頑張る - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://leben.mobi/blog/laravel_raw_sql/php/">Laravel5で生のSQLを実行する方法</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Yinaura/items/562079d937021f1fc30a">MySQL — サブクエリに AS を付けないとエラーを起こす | Every derived table must have its own alias - Qiita</a></li> </ul> <h4 id="メソッド確認"><a href="#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E7%A2%BA%E8%AA%8D">メソッド確認</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Capsule/Manager.html#method_connection">Illuminate\Database\Capsule\Manager | Laravel API</a></li> <li>query(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_query">Illuminate\Database\Connection | Laravel API</a></li> <li>Builder: <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>selectRaw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_selectRaw">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>select(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_select">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>raw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_raw">Illuminate\Database\Connection | Laravel API</a></li> </ul> <h4 id="unionAll"><a href="#unionAll">unionAll</a></h4> <ul> <li>unionAll(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_unionAll">Illuminate\Database\Query\Builder | Laravel API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://mysql.sql55.com/sql/mysql-union.php">MySQL の UNION \/ UNION ALL - MySQL の基礎 - MySQL 入門</a></li> </ul> <h4 id="(未使用) 外部パッケージ"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E5%A4%96%E9%83%A8%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8">(未使用) 外部パッケージ</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/soda-framework/eloquent-closure">GitHub - soda-framework\/eloquent-closure</a></li> </ul> <h4 id="(未使用) もはや生PDOに回帰するか"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E3%82%82%E3%81%AF%E3%82%84%E7%94%9FPDO%E3%81%AB%E5%9B%9E%E5%B8%B0%E3%81%99%E3%82%8B%E3%81%8B">(未使用) もはや生PDOに回帰するか</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/jjongs08/items/f53bc04d582e63e7ad10">Laravelでクエリ結果を配列で取得する方法 - Qiita</a></li> </ul> <h4 id="whereIn()"><a href="#whereIn%28%29">whereIn()</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/miriwo/items/553dccbae72a25b5467b">laravel クエリビルダ whereInを使って賢く取得情報を絞ろう - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18194 2022-05-19T23:55:46+09:00 2022-05-19T23:55:46+09:00 https://crieit.net/posts/php-phoenix-migrate-db-depend-other-db-20220521 (PHP) Phoenix でデータ関係が依存しているDBをマイグレーションする <p><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">lulco/phoenix</a> でデータ関係が依存しているDBをマイグレーションする方法をメモ。</p> <p>ドキュメントが全然ないので地道にやっていきます……。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>早速コードを。</p> <h3 id="phoenix.php"><a href="#phoenix.php">phoenix.php</a></h3> <pre><code class="php">return [ 'migration_dirs' => [ 'first' => __DIR__ . '/migrations/first', 'second' => __DIR__ . '/migrations/second', ], 'environments' => [ 'local' => [ 'adapter' => 'mysql', 'host' => $_ENV['MYSQL_HOST'], 'port' => (int)$_ENV['MYSQL_PORT'], // optional 'username' => $_ENV['MYSQL_USER'], 'password' => $_ENV['MYSQL_PASSWORD'], 'db_name' => $_ENV['MYSQL_DBNAME'], 'charset' => 'utf8mb4', ], 'production' => [ 'adapter' => 'mysql', 'host' => $_ENV['MYSQL_HOST'], 'port' => (int)$_ENV['MYSQL_PORT'], // optional 'username' => $_ENV['MYSQL_USER'], 'password' => $_ENV['MYSQL_PASSWORD'], 'db_name' => $_ENV['MYSQL_DBNAME'], 'charset' => 'utf8mb4', ], ], 'default_environment' => 'local', 'log_table_name' => 'phoenix_log', ]; </code></pre> <p>肝は <code>migration_dirs</code> で <code>first</code> と <code>second</code> でそれぞれ対応するディレクトリを指定しているところ。</p> <h3 id="/migrations/first/hoge.php"><a href="#%2Fmigrations%2Ffirst%2Fhoge.php">/migrations/first/hoge.php</a></h3> <pre><code class="php"><?php namespace migrations; use Phoenix\Database\Element\Index; use Phoenix\Migration\AbstractMigration; class HogeMigration extends AbstractMigration { protected function up(): void { $this->table('hoge') ->addColumn('create_date', 'datetime') ->addColumn('name', 'string') ->create(); // insert $hogeData = [ [ 'name' => 'foo' ], [ 'name' => 'bar' ], [ 'name' => 'buz' ], // 略 ]; $rows = []; foreach ($hogeData as $key => $val) { $rows[] = [ 'create_date' => date('Y-m-d H:i:s'), 'name' => $val['name'], ]; } $this->insert('hoge', $rows); } protected function down(): void { $this->table('hoge') ->drop(); } } </code></pre> <p>まずは最初に <code>hoge</code> というDBを作成し、そこにデータを流し込みます。</p> <h3 id="/migrations/second/fuga.php"><a href="#%2Fmigrations%2Fsecond%2Ffuga.php">/migrations/second/fuga.php</a></h3> <pre><code class="php"><?php namespace migrations; use Phoenix\Database\Element\Index; use Phoenix\Migration\AbstractMigration; class FugaMigration extends AbstractMigration { protected function up(): void { $this->table('fuga') ->addColumn('create_date', 'datetime') ->addColumn('name', 'string') ->addColumn('hoge_id', 'integer') ->create(); // select hoge data $hogeRows = $this->select('SELECT id, name FROM hoge'); // insert $fugaData = [ [ 'name' => 'un' ], [ 'name' => 'deux' ], [ 'name' => 'trois' ], // 略 ]; $rows = []; foreach ($fugaData as $key => $val) { $id = 0; // hoge の name と fuga の name が一致する要素を array_filter() で抽出し、 array_values() で番号を詰める $hogeArray = array_values( array_filter( $hogeRows, function($hogeRow) use ($val) { $needle = mb_strlen(mb_strtolower($val[0])) > 0 ? mb_strtolower($val[0]) : 'NOTHING'; return mb_strpos(mb_strtolower($hogeRow['name']), $needle) !== false; } ) ); // $hogeArray の要素が1つ (一意に定まる) 場合はその値を、そうでない場合はデフォルト値をセット $id = count($hogeArray) === 1 ? (int)$hogeArray[0]['id'] : 0; $rows[] = [ 'create_date' => date('Y-m-d H:i:s'), 'name' => $val[0], 'hoge_id' => $id, // 上述でセットした id を使用 ]; } $this->insert('fuga', $rows); } protected function down(): void { $this->table('fuga') ->drop(); } } </code></pre> <p>次に <code>fuga</code> を作成し、そこに徐に <code>$this->select()</code> で SQL文 を発行、先程流し込んだデータを抽出します。</p> <p>その抽出したデータと <code>fuga</code> に流し込みたいデータを突き合わせて初期データを生成し、それを <code>fuga</code> に流し込む……という算段。</p> <p>これで意図したデータをマイグレーションすることができました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">GitHub - lulco\/phoenix: Framework agnostic database migrations for PHP.</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix/blob/master/src/Database/Adapter/PdoAdapter.php">phoenix\/PdoAdapter.php at master · lulco\/phoenix · GitHub</a></li> </ul></li> </ul> <p>ドキュメントがないのでコードを読んで普通に <code>select</code> とか使えそう、と思って試したりしていました。</p> arm-band tag:crieit.net,2005:PublicArticle/18186 2022-05-10T22:39:48+09:00 2022-05-10T22:39:48+09:00 https://crieit.net/posts/docker-php-7-alpine-use-composer-and-xdebug-20220510 Docker の php:7-alpine イメージで Composer と Xdebug を使えるようにする <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/slimphp/Slim-Skeleton">slimphp/Slim-Skeleton</a> を利用したいと考えました。</p> <p>手元の端末で開発しようとしたところ、 XAMPP に Xdebug を入れ忘れていたことに気付きました。しかも、 XAMPP の PHP のバージョンで Xdebug のインストールが止まってしまいました。</p> <p>そこで、スケルトンプロジェクトに付随している Docker Compose での開発を試みましたが、この Docker Compose 内で pull してくるイメージは Composer も Xdebug もないので入れることにしました。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3> <pre><code>root/ ├ _docker/ // Dockerに関する設定置き場 │ ├ dockerfiles/ │ │ ├ settings/ │ │ │ └ php.ini // PHPの設定 (Xdebug 有効化) │ │ │ │ │ └ Dockerfile │ │ │ ├ mysql/ // MariaDB 用ディレクトリ │ │ └ .gitkeep │ │ │ └ phpmyadmin/ │ ├ conf/ │ │ └ phpmyadmin-misc.ini // phpMyAdmin の設定 (メモリ上限など) │ │ │ └ sessions/ │ └ .gitkeep │ └ docker-compose.yml </code></pre> <p>Docker に関する部分のみ列挙。</p> <h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3> <pre><code class="yml">version: '3.7' volumes: logs: driver: local services: slim: build: context: ./_docker/dockerfiles/ dockerfile: Dockerfile working_dir: /var/www command: php -S 0.0.0.0:8080 -t public environment: docker: "true" ports: - 8080:8080 volumes: - .:/var/www - ./logs:/var/www/logs db: image: mariadb restart: always ports: - 3306:3306 volumes: - ./_docker/mysql/mysql:/var/lib/mysql - ./_docker/mysql/initdb.d:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=pwd - MYSQL_DATABASE=test - MYSQL_USER=user - MYSQL_PASSWORD=pwd phpmyadmin: image: phpmyadmin/phpmyadmin volumes: - ./_docker/phpmyadmin/sessions:/sessions - ./_docker/phpmyadmin/conf/phpmyadmin-misc.ini:/usr/local/etc/php/conf.d/phpmyadmin-misc.ini environment: - PMA_ARBITRARY=1 - PMA_HOST=db - PMA_USER=user - PMA_PASSWORD=pwd ports: - 8081:80 </code></pre> <p>変更点は以下。</p> <ul> <li>PHP 用環境: <code>Dockerfile</code> を使ったイメージにカスタマイズ</li> <li>DB: MariaDB のイメージをそのまま利用 <ul> <li><code>environment</code> のパラメータはそのまま使用。アプリケーションや phpMyAdmin 側と動機は取れていないですがひとまずはこれで……</li> </ul></li> <li>phpMyAdmin: これも phpMyAdmin 公式イメージを流用</li> </ul> <h3 id="_docker/dockerfiles/Dockerfile"><a href="#_docker%2Fdockerfiles%2FDockerfile">_docker/dockerfiles/Dockerfile</a></h3> <pre><code class="Dockerfile">FROM php:7-alpine RUN apk --update add curl RUN set -ex \ && apk --no-cache add \ autoconf build-base RUN pecl install xdebug RUN docker-php-ext-enable xdebug RUN docker-php-ext-install pdo_mysql COPY settings/php.ini /usr/local/etc/php/conf.d RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer </code></pre> <p><code>php:7-alpine</code> をベースに Xdebug と Composer を追加。</p> <h3 id="_docker/dockerfiles/settings/php.ini"><a href="#_docker%2Fdockerfiles%2Fsettings%2Fphp.ini">_docker/dockerfiles/settings/php.ini</a></h3> <pre><code class="ini">xdebug.mode=coverage </code></pre> <p>Xdebug でカバレッジを有効化するための設定を追加するため。なお、 <code>Dockerfile</code> で <code>COPY</code> する際のホストマシンでのパスは <strong><code>Dockerfile</code> の存在するディレクトリから下でないと参照できない</strong> という地味な制約があるので <code>dockerfiles</code> ディレクトリをわざわざ掘りました。</p> <h3 id="_docker/phpmyadmin/conf/phpmyadmin-misc.ini"><a href="#_docker%2Fphpmyadmin%2Fconf%2Fphpmyadmin-misc.ini">_docker/phpmyadmin/conf/phpmyadmin-misc.ini</a></h3> <pre><code class="ini">allow_url_fopen = Off max_execution_time = 600 memory_limit = 64M post_max_size = 64M upload_max_filesize = 64M </code></pre> <p>メモリ上限等のカスタマイズ。</p> <hr /> <p>これで今回はカバレッジ出力まで動作することを確認しました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Xdebug"><a href="#Xdebug">Xdebug</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/bf5480f4d7c751ab28aa">PHPUnitでHTMLコードカバレッジを出すまで(Docker使用) - Qiita</a></li> </ul> <blockquote> <p>Warning: XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set</p> </blockquote> <p>が出たので step3 について実施。</p> <p>なお、今回のケースでは</p> <pre><code>extension=xdebug.so </code></pre> <p>は不要だった。</p> <h2 id="Dockerfile"><a href="#Dockerfile">Dockerfile</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://crudzoo.com/blog/php-docker">Dockerで作るNginx + PHP7 + Xdebug環境 | Crudzoo</a></li> </ul> <p>ほぼこれでOK.</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ucan-lab/items/fbf021bf69896538e515">php-alpineコンテナにxdebugをインストールする時にハマったメモ - Qiita</a></li> </ul> <p>こちらも参照。 <code>php -v</code> で <code>with Xdebug</code> と付いていればOK。</p> <h4 id="Dockerfile の COPY"><a href="#Dockerfile+%E3%81%AE+COPY">Dockerfile の COPY</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://scrapbox.io/taka521-tech-notes/%E3%80%90Docker%E3%80%91COPY%E3%81%A7%E6%8C%87%E5%AE%9A%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AF%E3%80%81Dockerfile%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%8B%E3%82%89%E3%81%AE%E7%9B%B8%E5%AF%BE%E3%83%91%E3%82%B9%E3%81%A7%E3%80%81%E8%A6%AA%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E8%A6%8B%E3%82%8C%E3%81%AA%E3%81%84">【Docker】COPYで指定されたファイルは、Dockerfileが存在するディレクトリからの相対パスで、親ディレクトリを見れない - タカの技術ノート</a></li> </ul> <h3 id="Mariadb"><a href="#Mariadb">Mariadb</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/04/07/post-8227/">dockerを使用してmariadbを構築する | mebee</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tarch710/items/1236a23f7ffde4c512f2">開発環境をDockerにしたら、PDOでcould not find driverが出た - Qiita</a></li> </ul> <blockquote> <p>could not find driver</p> </blockquote> <p>普通に考えたら確かにドライバがないので追加して解決。</p> <h3 id="シェル"><a href="#%E3%82%B7%E3%82%A7%E3%83%AB">シェル</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yutachaos/items/56dd7ea09d7e2b0d9173">dockerでalpine linux ベースのcontainerに入って、shellを使いたいとき。 - Qiita</a></li> </ul> <p>bash ではなく ash 。 <code>bin/ash</code> 指定。</p> <h3 id="sr -c"><a href="#sr+-c">sr -c</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://genzouw.com/entry/2020/01/28/120014/1910/">docker-compose.ymlのcommandプロパティに複数コマンドを設定する方法 | ゲンゾウ用ポストイット</a></li> </ul> <p>最終的には使わずに済みましたが念のためメモ。</p> arm-band tag:crieit.net,2005:PublicArticle/18122 2022-02-23T00:13:45+09:00 2022-02-23T00:13:45+09:00 https://crieit.net/posts/output-timezone-datetime-of-php-and-mysql-20220223 PHP と MySQL のタイムゾーンや時刻の確認メモ <p>PHP や MySQL でタイムゾーンや時刻を確認したくなったのでメモ。</p> <h2 id="PHP"><a href="#PHP">PHP</a></h2> <pre><code class="bash"># php -r 'echo date_default_timezone_get() . PHP_EOL; echo date(&quot;y-m-d H:i:s&quot;) . PHP_EOL;' Asia/Tokyo 22-02-13 19:16:50 </code></pre> <p>コマンドライン実行でサクッと確認。</p> <h2 id="MySQL"><a href="#MySQL">MySQL</a></h2> <pre><code class="bash">mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | JST | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.00 sec) mysql> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2022-02-13 19:46:19 | +---------------------+ 1 row in set (0.00 sec) </code></pre> <p>こちらはSQL文で確認。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.softel.co.jp/blogs/tech/archives/2077">【php】ちょっとしたphpをコマンドラインからさくっと実行する at softelメモ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.date.php">PHP: date - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.date-default-timezone-get.php">PHP: date_default_timezone_get - Manual</a></li> </ul> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://mysql.javarou.com/dat/000848.html">現在の日付・時刻を取得する - MySQL 逆引きリファレンス</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tailak/items/63dce2dd7dfe049b038e">MySQLでタイムゾーンを設定する - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/time-zone-support.html">MySQL :: MySQL 5.6 リファレンスマニュアル :: 10.6 MySQL Server でのタイムゾーンのサポート</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18121 2022-02-23T00:12:52+09:00 2022-02-23T00:12:52+09:00 https://crieit.net/posts/error-at-install-mysql-community-server-import-rpm-pgp-key-20220223 Docker で MySQL の community版をインストールしようとした際に Failing package is: ... エラー <p>Docker の MySQL コンテナを構築する Dockerfile を走らせようとしたところ、</p> <blockquote> <p>The GPG keys listed for the "MySQL 8.0 Community Server" repository are already installed but they are not correct for this package.</p> </blockquote> <p>や</p> <blockquote> <p>Public key for mysql-community-client-8.0.28-1.el8.x86_64.rpm is not installed. Failing package is: mysql-community-client-8.0.28-1.el8.x86_64</p> </blockquote> <p>というエラーが発生してしまったので対処。</p> <h3 id="改修前の Dockerfile"><a href="#%E6%94%B9%E4%BF%AE%E5%89%8D%E3%81%AE+Dockerfile">改修前の Dockerfile</a></h3> <pre><code class="dockerfile">RUN dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el8-2.noarch.rpm </code></pre> <p>コミュニティ版の MySQL8 をインストールしようとしています。</p> <h3 id="改修後の Dockerfile"><a href="#%E6%94%B9%E4%BF%AE%E5%BE%8C%E3%81%AE+Dockerfile">改修後の Dockerfile</a></h3> <pre><code class="dockerfile">RUN rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 && dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el8-2.noarch.rpm </code></pre> <p>エラー文で検索したところ、</p> <pre><code class="bash">rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 </code></pre> <p>を追加すると良い、という記事を見かけたので追加したところ、エラーが発生しなくなったのでこれで対処。</p> <h3 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://support.cpanel.net/hc/en-us/articles/4419382481815-MySQL-GPG-keys-expired-preventing-installation-upgrade-of-MySQL-packages-from-the-official-repository-">MySQL GPG keys expired, preventing installation\/upgrade of MySQL packages from the official repository. – cPanel</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://support.cpanel.net/hc/en-us/articles/4419382481815-Installer-fails-due-to-The-GPG-keys-listed-for-the-MySQL-repository-are-already-installed-but-they-are-not-correct-for-this-package-">MySQL GPG keys expired, preventing installation\/upgrade of MySQL packages from the official repository. – cPanel</a></li> </ul> <p>URLは異なりますがどちらも同じ内容の模様。</p> arm-band tag:crieit.net,2005:PublicArticle/17949 2022-01-23T20:41:17+09:00 2022-02-06T21:57:21+09:00 https://crieit.net/posts/MySQL-PlanetScale-Next-js-Prisma サーバーレスMySQLのPlanetScaleをNext.js+Prismaで使ってみた <p>PlanetScaleというのはMySQL互換のサーバーレスデータベース。つまりどこからでもMySQL接続してデータベースとして利用できるサービス。</p> <p>接続方法は普通によくあるようなパスワードを使ったデータベースURLで接続可能。そのためだいたいどんなフレームワークでも利用できる。</p> <p><a target="_blank" rel="nofollow noopener" href="https://planetscale.com/">https://planetscale.com/</a></p> <h2 id="無料枠がでかい"><a href="#%E7%84%A1%E6%96%99%E6%9E%A0%E3%81%8C%E3%81%A7%E3%81%8B%E3%81%84">無料枠がでかい</a></h2> <p>すごく気に入った理由の一つとして、無料枠がかなり大きいことがあげられる。2022/1時点で容量10GB、書き込み回数は月100万回、読み込み回数も月1000万回と小さいアプリケーションであれば気にするレベルでないほど十分にある。ちょっとしたアプリをたくさん作っているという人にとってはとても嬉しい無料枠。</p> <p>というのも同じく無料で使えるHerokuのJawsDBも容量5MB、最大接続数10と、むちゃくちゃ少なく、集客下手だから…と思いつつもちょっと心配になってしまう制限のため、なんかこう、微妙だなぁ、とずっと感じていた。</p> <p>しかもPlanetScaleは東京リージョンまであるのでびっくり。</p> <h2 id="コンソールもある"><a href="#%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%82%82%E3%81%82%E3%82%8B">コンソールもある</a></h2> <p>サービス内にコンソールもあるためちょっとした調査とか、個人サービスのデータ調整とかはこれで簡単にできそう。</p> <p><a href="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a61ed30770571d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6e2af66c610d88bc766649f72032893a61ed30770571d.png?mw=700" alt="image.png" /></a></p> <p>あとは現在のスキーマも見れる。</p> <h2 id="実際に試してみた"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AB%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F">実際に試してみた</a></h2> <p>ということで実際に使ってみた。Next.jsからPrismaで利用。かんたんな書き込み、取得処理だけで試してみた。</p> <h3 id="接続方法"><a href="#%E6%8E%A5%E7%B6%9A%E6%96%B9%E6%B3%95">接続方法</a></h3> <p>PlanetScaleのConnect設定のところでパスワードを設定できる。それにより普通に接続が可能。ここはいろんなライブラリでの接続方法も表示してくれる。Prismaの設定もそのままコピペでできるように教えてくれる。</p> <h3 id="開発"><a href="#%E9%96%8B%E7%99%BA">開発</a></h3> <p>Prismaを使う場合、開発は普通にローカルのデータベースを使ってやると良さそう。というのもPrismaはShadow databaseというものを用いている。開発時はなんかそれで色々いい感じにしているらしい。ということでそのためにデータベースのCreate, Drop権限が必要。その関係でマイグレーションの作成を行うタイミングでPlanetScaleとの接続ではエラーになってしまう。そのため開発時はローカルだけで完結させておくとスムーズ。</p> <p>本番に反映させたいときだけPlanetScaleへの接続に変え、 <code>npx prisma db push</code> を実行することでマイグレーションを反映できる。</p> <p>PlanetScaleはドキュメントも結構しっかり書かれているようで、このあたりの解説もちゃんと書かれている。<br /> <a target="_blank" rel="nofollow noopener" href="https://docs.planetscale.com/tutorials/prisma-quickstart">https://docs.planetscale.com/tutorials/prisma-quickstart</a></p> <h3 id="速さ"><a href="#%E9%80%9F%E3%81%95">速さ</a></h3> <p>クラウドということで速さがちょっと気になったていたが、一つ作ったデータ取得用のAPIでだいたいTTFBが300msくらいだったので十分そうだった。</p> <p>気をつける点として、Vercelの場合は無料枠だとリージョンがUSAのEASTと決まっているのでPlanetScale側もそれに合わせておく必要がある。一旦東京で作ってみていた時は1.5sかかっていた。遅くて使えないな~と思った場合はこのあたりの設定が間違っている可能性があるかもしれないので気をつけよう。</p> <h2 id="追記"><a href="#%E8%BF%BD%E8%A8%98">追記</a></h2> <p>実際に別途使ってみるとエラーが色々出たので対処法</p> <p><a href="https://crieit.net/posts/Prisma-PlanetScale">PrismaでPlanetScaleを使う時のエラーあれこれ</a></p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>実際に作ってみたテストサイトはこちら。<br /> <a target="_blank" rel="nofollow noopener" href="https://simple-planetscale-test.vercel.app/">https://simple-planetscale-test.vercel.app/</a></p> <p>試したソースコードはこちら<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/dala00/simple-planetscale-test">https://github.com/dala00/simple-planetscale-test</a></p> <p>まだ実運用したことがないのでどうなるかはわからないが、今のところとても気に入っているのでFirestoreで作っているちょっと大きめのサービスを移行してみようか悩み中…。今後もちょっとしたちいちゃいネタサービスを作るときにも使ってみたい。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17898 2021-12-29T00:16:11+09:00 2021-12-29T00:17:49+09:00 https://crieit.net/posts/test-centos8-lamp-for-almalinux-in-docker-20211229 (Docker) Almalinux を見据えて CentOS8 での環境構築を試験してみる <p>手前味噌で恐縮ですが、自作の LAMP環境 を検証する Docker Compose について。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/docker_compose_ambergrease">GitHub - arm-band/docker_compose_ambergrease</a></li> </ul> <p>イメージを CentOS7 ベースで構築しているので、いずれは AlmaLinux なり Rocky Linux なりに移行しなければならないことは分かっていました。</p> <p>そこで、その以降を見据えて残り数日の命の CentOS8 で試験してみることにしました。なぜ AlmaLinux や Rocky Linux ではなくわざわざ CentOS8 かというと、いきなりそれらのOSに飛びついて何らかの不具合が発生したときに原因の切り分けで苦労するならば、まだ同じシリーズで差分が少ない (と想定される) CentOS8 で試験して、問題なければ次に進もう、という段階的な試験を想定したためです。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <pre><code class="bash">docker run --name php7_cent8 -it centos:centos8 /bin/bash </code></pre> <p>まずはイメージを取得し、 bash に入ります。</p> <h3 id="Apache + PHP"><a href="#Apache+%2B+PHP">Apache + PHP</a></h3> <p>最初は Apache + PHP 。こちらについては、以前 <a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/02/23/centos8-lamp-install-note/">CentOS8 をインストールして遊んだとき</a>にremiリポジトリの入り方が CentOS7 とは異なることが分かっていたので、多少手を加える必要があるだろう、と踏んでいました。</p> <pre><code class="bash"># \cp -pf /usr/share/zoneinfo/Japan /etc/localtime # </code></pre> <p>これは問題なし。</p> <pre><code class="bash"># dnf -y update && yum -y install epel-release sudo less iproute httpd-devel zip unzip openssl mod_ssl ## 略 Installed: apr-1.6.3-12.el8.x86_64 apr-devel-1.6.3-12.el8.x86_64 apr-util-1.6.1-6.el8.x86_64 apr-util-bdb-1.6.1-6.el8.x86_64 apr-util-devel-1.6.1-6.el8.x86_64 apr-util-openssl-1.6.1-6.el8.x86_64 brotli-1.0.6-3.el8.x86_64 centos-logos-httpd-85.8-2.el8.noarch cyrus-sasl-2.1.27-5.el8.x86_64 cyrus-sasl-devel-2.1.27-5.el8.x86_64 epel-release-8-11.el8.noarch expat-devel-2.2.5-4.el8.x86_64 groff-base-1.22.3-18.el8.x86_64 httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 httpd-devel-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b541f3b1.noarch httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 libdb-devel-5.3.28-42.el8_4.x86_64 libpath_utils-0.2.1-39.el8.x86_64 libpkgconf-1.4.2-1.el8.x86_64 libtalloc-2.3.2-1.el8.x86_64 mailcap-2.1.48-3.el8.noarch mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86_64 mod_ssl-1:2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 ncurses-6.1-9.20180224.el8.x86_64 openldap-devel-2.4.46-18.el8.x86_64 perl-Carp-1.42-396.el8.noarch perl-Data-Dumper-2.167-399.el8.x86_64 perl-Digest-1.17-395.el8.noarch perl-Digest-MD5-2.55-396.el8.x86_64 perl-Encode-4:2.97-3.el8.x86_64 perl-Errno-1.28-420.el8.x86_64 perl-Exporter-5.72-396.el8.noarch perl-File-Path-2.15-2.el8.noarch perl-File-Temp-0.230.600-1.el8.noarch perl-Getopt-Long-1:2.50-4.el8.noarch perl-HTTP-Tiny-0.074-1.el8.noarch perl-IO-1.38-420.el8.x86_64 perl-IO-Socket-IP-0.39-5.el8.noarch perl-IO-Socket-SSL-2.066-4.module_el8.3.0+410+ff426aa3.noarch perl-MIME-Base64-3.15-396.el8.x86_64 perl-Mozilla-CA-20160104-7.module_el8.3.0+416+dee7bcef.noarch perl-Net-SSLeay-1.88-1.module_el8.3.0+410+ff426aa3.x86_64 perl-PathTools-3.74-1.el8.x86_64 perl-Pod-Escapes-1:1.07-395.el8.noarch perl-Pod-Perldoc-3.28-396.el8.noarch perl-Pod-Simple-1:3.35-395.el8.noarch perl-Pod-Usage-4:1.69-395.el8.noarch perl-Scalar-List-Utils-3:1.49-2.el8.x86_64 perl-Socket-4:2.027-3.el8.x86_64 perl-Storable-1:3.11-3.el8.x86_64 perl-Term-ANSIColor-4.06-396.el8.noarch perl-Term-Cap-1.17-395.el8.noarch perl-Text-ParseWords-3.30-395.el8.noarch perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch perl-Time-Local-1:1.280-1.el8.noarch perl-URI-1.73-3.el8.noarch perl-Unicode-Normalize-1.25-396.el8.x86_64 perl-constant-1.33-396.el8.noarch perl-interpreter-4:5.26.3-420.el8.x86_64 perl-libnet-3.11-3.el8.noarch perl-libs-4:5.26.3-420.el8.x86_64 perl-macros-4:5.26.3-420.el8.x86_64 perl-parent-1:0.237-1.el8.noarch perl-podlators-4.11-1.el8.noarch perl-threads-1:2.21-2.el8.x86_64 perl-threads-shared-1.58-2.el8.x86_64 pkgconf-1.4.2-1.el8.x86_64 pkgconf-m4-1.4.2-1.el8.noarch pkgconf-pkg-config-1.4.2-1.el8.x86_64 sscg-2.3.3-14.el8.x86_64 sudo-1.8.29-7.el8_4.1.x86_64 unzip-6.0-45.el8_4.x86_64 zip-3.0-23.el8.x86_64 Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># dnf -y upgrade ## 略 Upgraded: epel-release-8-13.el8.noarch Complete! </code></pre> <p>特に大きな差分もなさそうです。OK。</p> <h3 id="remiリポジトリ"><a href="#remi%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA">remiリポジトリ</a></h3> <pre><code class="bash"># rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-8.rpm Retrieving http://rpms.famillecollet.com/enterprise/remi-release-8.rpm warning: /var/tmp/rpm-tmp.hlvLxe: Header V4 RSA/SHA256 Signature, key ID 5f11735a: NOKEY Verifying... ################################# [100%] Preparing... ################################# [100%] Updating / installing... 1:remi-release-8.5-2.el8.remi ################################# [100%] # rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi2021 # </code></pre> <p>OK。 CentOS8 用のリポジトリに切り替えました。鍵についてはすぐ <code>RPM-GPG-KEY-remi2022</code> にしなければならないでしょうけど。</p> <h4 id="dnf"><a href="#dnf">dnf</a></h4> <pre><code class="bash"># dnf config-manager --enable remi && dnf config-manager --enable remi-php74 No such command: config-manager. Please use /usr/bin/dnf --help It could be a DNF plugin command, try: "dnf install 'dnf-command(config-manager)'" </code></pre> <p><code>remi-php74</code> に指定で躓くかと思いきや、そもそも <code>config-manager</code> がない、と?</p> <p>元々は <code>yum-config-manager</code> だったのですが、 CentOS8 であれば <code>dnf</code> ベースの方が良いだろう、ということで置き換えてはみましたが、ダメですか……。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.trippyboy.com/2021/terraform/almalinux%E3%81%ABterraform%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92yum%E3%81%A7%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B%E3%81%AE%E5%B7%BB/">Almalinuxにterraformコマンドをyumで導入するの巻 - TrippyBoyの愉快な日々</a></li> </ul> <p>こちらより <code>dnf install -y dnf-plugins-core</code> してみます。</p> <pre><code class="bash"># dnf install dnf-plugins-core ## 略 Installed: dbus-glib-0.110-2.el8.x86_64 dnf-plugins-core-4.0.21-3.el8.noarch python3-dateutil-1:2.6.1-6.el8.noarch python3-dbus-1.2.4-15.el8.x86_64 python3-dnf-plugins-core-4.0.21-3.el8.noarch python3-six-1.11.0-8.el8.noarch Complete! </code></pre> <p>OK。</p> <h4 id="remi + PHP"><a href="#remi+%2B+PHP">remi + PHP</a></h4> <pre><code class="bash"># dnf config-manager --enable remi && dnf config-manager --enable remi-php74 Error: No matching repo to modify: remi-php74. </code></pre> <p>……やはり <code>remi-php74</code> で引っかかりましたか。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/03/12/post-7365/">CentOs8 php7.4をインストールする | mebee</a></li> </ul> <p>……想定していたものとは少し違うやり方ですが、試してみましょう。</p> <pre><code class="bash"># dnf module reset php ## 略 Remi's RPM repository for Enterprise Linux 8 - x86_64 598 kB/s | 3.9 MB 00:06 Last metadata expiration check: 0:00:03 ago on DDD dd mmm yyyy hh:ii:ss AM UTC. Dependencies resolved. Nothing to do. Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># dnf module install -y php:remi-7.4 ## 略 Installed: libedit-3.1-23.20170329cvs.el8.x86_64 libxslt-1.1.32-6.el8.x86_64 nginx-filesystem-1:1.14.1-9.module_el8.0.0+184+e34fea82.noarch oniguruma5php-6.9.7.1-1.el8.remi.x86_64 php-cli-7.4.27-1.el8.remi.x86_64 php-common-7.4.27-1.el8.remi.x86_64 php-fpm-7.4.27-1.el8.remi.x86_64 php-json-7.4.27-1.el8.remi.x86_64 php-mbstring-7.4.27-1.el8.remi.x86_64 php-xml-7.4.27-1.el8.remi.x86_64 Complete! # php --version PHP 7.4.27 (cli) (built: Dec 14 2021 17:17:06) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies </code></pre> <p>OK。入りましたね。これに従い該当箇所を書き換えます。</p> <pre><code class="dockerfile"># enable repository remi & remi-php74 #RUN dnf config-manager --enable remi && dnf config-manager --enable remi-php74 # disable default module RUN dnf module reset -y php RUN dnf module install -y php:remi-7.4 ## 略 # disable repository remi & remi-php74 #RUN dnf config-manager --disable remi && dnf config-manager --disable remi-php74 </code></pre> <p><code>config-manager</code> によるリポジトリ使用のオン・オフの切り替えを削除しました。</p> <pre><code class="bash"># dnf -y install php php-devel php-pdo php-mysqlnd php-mbstring php-gd php-pear php-pecl-apc-devel zlib-devel php-xml php-mcrypt php-pecl-xdebug ## 略 Installed: autoconf-2.69-29.el8.noarch automake-1.16.1-7.el8.noarch cmake-filesystem-3.20.2-4.el8.x86_64 cpp-8.5.0-4.el8_5.x86_64 dejavu-fonts-common-2.35-7.el8.noarch dejavu-sans-fonts-2.35-7.el8.noarch emacs-filesystem-1:26.1-7.el8.noarch fontconfig-2.13.1-4.el8.x86_64 fontpackages-filesystem-1.44-22.el8.noarch freetype-2.9.1-4.el8_3.1.x86_64 fribidi-1.0.4-8.el8.x86_64 gcc-8.5.0-4.el8_5.x86_64 gcc-c++-8.5.0-4.el8_5.x86_64 gd3php-2.3.3-4.el8.remi.x86_64 glibc-devel-2.28-164.el8.x86_64 glibc-headers-2.28-164.el8.x86_64 graphite2-1.3.10-10.el8.x86_64 harfbuzz-1.7.5-3.el8.x86_64 isl-0.16.1-6.el8.x86_64 jbigkit-libs-2.1-14.el8.x86_64 kernel-headers-4.18.0-348.2.1.el8_5.x86_64 keyutils-libs-devel-1.5.10-9.el8.x86_64 krb5-devel-1.18.2-14.el8.x86_64 libX11-1.6.8-5.el8.x86_64 libX11-common-1.6.8-5.el8.noarch libXau-1.0.9-3.el8.x86_64 libXpm-3.5.12-8.el8.x86_64 libcom_err-devel-1.45.6-2.el8.x86_64 libimagequant-2.12.5-1.el8.x86_64 libjpeg-turbo-1.5.3-12.el8.x86_64 libkadm5-1.18.2-14.el8.x86_64 libmcrypt-2.5.8-26.el8.x86_64 libmpc-1.1.0-9.1.el8.x86_64 libpng-2:1.6.34-5.el8.x86_64 libraqm-0.7.0-4.el8.x86_64 libselinux-devel-2.9-5.el8.x86_64 libsepol-devel-2.9-3.el8.x86_64 libsodium-1.0.18-2.el8.x86_64 libstdc++-devel-8.5.0-4.el8_5.x86_64 libtiff-4.0.9-20.el8.x86_64 libtool-2.4.6-25.el8.x86_64 libverto-devel-0.3.0-5.el8.x86_64 libwebp-1.0.0-5.el8.x86_64 libxcb-1.13.1-1.el8.x86_64 libxcrypt-devel-4.1.1-6.el8.x86_64 libxml2-devel-2.9.7-9.el8_4.2.x86_64 m4-1.4.18-7.el8.x86_64 make-1:4.2.1-10.el8.x86_64 openssl-devel-1:1.1.1k-5.el8_5.x86_64 pcre2-devel-10.32-2.el8.x86_64 pcre2-utf16-10.32-2.el8.x86_64 pcre2-utf32-10.32-2.el8.x86_64 perl-Thread-Queue-3.13-1.el8.noarch php-7.4.27-1.el8.remi.x86_64 php-devel-7.4.27-1.el8.remi.x86_64 php-fedora-autoloader-1.0.1-2.el8.remi.noarch php-gd-7.4.27-1.el8.remi.x86_64 php-mysqlnd-7.4.27-1.el8.remi.x86_64 php-opcache-7.4.27-1.el8.remi.x86_64 php-pdo-7.4.27-1.el8.remi.x86_64 php-pear-1:1.10.13-1.el8.remi.noarch php-pecl-apcu-5.1.21-1.el8.remi.7.4.x86_64 php-pecl-apcu-devel-5.1.21-1.el8.remi.7.4.x86_64 php-pecl-mcrypt-1.0.4-1.el8.remi.7.4.x86_64 php-pecl-xdebug-2.9.8-1.el8.remi.7.4.x86_64 php-process-7.4.27-1.el8.remi.x86_64 php-sodium-7.4.27-1.el8.remi.x86_64 xz-devel-5.2.4-3.el8.x86_64 zlib-devel-1.2.11-17.el8.x86_64 Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># php -r &quot;copy('https://getcomposer.org/installer', 'composer-setup.php');&quot; && php composer-setup.php && php -r &quot;unlink('composer-setup.php');&quot; && mv composer.phar /usr/local/bin/composer All settings correct for using Composer Downloading... Composer (version 2.2.1) successfully installed to: /etc/yum.repos.d/composer.phar Use it: php composer.phar # composer --version Composer version 2.2.1 2021-12-22 22:21:31 </code></pre> <p>Composer も入りました。</p> <pre><code class="bash"># mkdir /var/log/php # chown apache /var/log/php # chmod 755 /var/log/php # mkdir -p /etc/ssl/private </code></pre> <p>まあこの辺りは普通に。</p> <h3 id="SSL"><a href="#SSL">SSL</a></h3> <pre><code class="bash"># openssl req -new -newkey rsa:2048 -nodes -out /etc/ssl/private/server.csr -keyout /etc/ssl/private/server.key -subj &quot;/C=/ST=/L=/O=/OU=/CN=*.lvh.me&quot; ## 略 # openssl x509 -days 365 -req -signkey /etc/ssl/private/server.key -in /etc/ssl/private/server.csr -out /etc/ssl/private/server.crt Signature ok subject=CN = *.lvh.me Getting Private key </code></pre> <p>OK。</p> <h3 id="Apache の設定ファイル"><a href="#Apache+%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB">Apache の設定ファイル</a></h3> <h4 id="ssl.conf"><a href="#ssl.conf">ssl.conf</a></h4> <pre><code class="bash"># cat /etc/httpd/conf.d/ssl.conf ## 略 </code></pre> <p>表示させたSSL用の設定を現行のものと差し替えます。ただし、現行の設定をなるべく引き継ぐように。これは他の設定ファイルも同様です。</p> <pre><code class="conf">- SSLRandomSeed startup file:/dev/urandom 256 - SSLRandomSeed connect builtin - SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA </code></pre> <p>現行にあったこれらの行は削除。</p> <pre><code class="conf">+ SSLHonorCipherOrder on </code></pre> <p>これは新しく追加されていました。</p> <pre><code class="conf">SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 </code></pre> <p>プロトコルについては念のため現行の設定を引き継ぎ。流石にもう SSLv2 なんて存在しないでしょうけど……。</p> <pre><code class="conf">- <Files ~ "\.(cgi|shtml|phtml|php3?)$"> + <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars - </Files> + </FilesMatch> </code></pre> <p>微妙にディレクティブの表現が変わっていますが、影響はなさそうなので変更を受け入れます。</p> <h4 id="php.conf"><a href="#php.conf">php.conf</a></h4> <pre><code class="bash"># cat /etc/httpd/conf.d/php.conf ## 略 # Redirect to local php-fpm if mod_php (5 or 7) is not available <IfModule !mod_php5.c> <IfModule !mod_php7.c> # Enable http authorization headers SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1 <FilesMatch \.(php|phar)$> SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" </FilesMatch> </IfModule> </IfModule> </code></pre> <p>こちらは最後の部分に追記がありますね。これはそのまま受け入れ。</p> <h4 id="php.ini"><a href="#php.ini">php.ini</a></h4> <pre><code class="bash"># cat /etc/php.ini ## 略 </code></pre> <p>こちらも設定を引き継ぎ、といっても現行の設定と変更点はなく(自前の設定を引き継いだ) 差分としてはコメントの文字列くらい。</p> <h3 id="AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message"><a href="#AH00558%3A+httpd%3A+Could+not+reliably+determine+the+server%27s+fully+qualified+domain+name%2C+using+192.0.2.1.+Set+the+%27ServerName%27+directive+globally+to+suppress+this+message">AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</a></h3> <p>Apache を起動しようとしたところ、</p> <blockquote> <p>AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</p> </blockquote> <p>のエラーメッセージがログに記録されて起動してこない現象に遭遇。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/a6deebbad207d627b598">[Docker]AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using ***.***.*.*. Set the 'ServerName' directive globally to suppress this message - Qiita</a></li> </ul> <p>そういえば元々の Dockerfile には記述していましたが、 <code>httpd.conf</code> への追記をしていませんでした。</p> <pre><code class="bash">echo ServerName www.example.com:80 >> /etc/httpd/conf/httpd.conf </code></pre> <p>これでOK。</p> <pre><code class="bash"># httpd -v Server version: Apache/2.4.37 (centos) Server built: Nov 12 2021 04:57:27 </code></pre> <p>バージョンも拾えました。</p> <h3 id="(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed"><a href="#%282%29No+such+file+or+directory%3A+AH02454%3A+FCGI%3A+attempt+to+connect+to+Unix+domain+socket+%2Frun%2Fphp-fpm%2Fwww.sock+%28%2A%29+failed">(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</a></h3> <p>これで Apache も起動してきたのでブラウザで表示確認、ということで <code>phpinfo()</code> でも表示させようかとファイルを用意して <code>curl</code> してみると……</p> <blockquote> <p>Service Unavailable</p> <p>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.</p> </blockquote> <p>503エラーが出てしまいました。</p> <blockquote> <p>(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</p> </blockquote> <p>Apache のエラーログには上述のログが記録されていました。</p> <p>今までとは異なる方法で PHP をインストールしたら、どうやら php-fpm (CGIモード) で動作しようとしてしまっているようです。</p> <p>今回の環境ではCGI版かモジュール版かはあまり考慮しておらず、Webサーバも Apache のためモジュール版でも良いと判断。</p> <p>動作を切り替えます。</p> <pre><code class="bash"># less /etc/httpd/conf/httpd.conf ## 略 Include conf.modules.d/*.conf </code></pre> <p>念のためモジュール読み込みの記述があることを確認。</p> <pre><code class="bash"># ls /etc/httpd/conf.modules.d/ 00-base.conf 00-lua.conf 00-optional.conf 00-ssl.conf 01-cgi.conf 10-proxy_h2.conf README 00-dav.conf 00-mpm.conf 00-proxy.conf 00-systemd.conf 10-h2.conf 15-php.conf </code></pre> <p>関係しそうなのは <code>00-mpm.conf</code>, <code>01-cgi.conf</code>, <code>15-php.conf</code> 辺りでしょうか。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/15-php.conf # Cannot load both php5 and php7 modules <IfModule !mod_php5.c> <IfModule prefork.c> LoadModule php7_module modules/libphp7.so </IfModule> </IfModule> </code></pre> <p>ここはそのまま。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/01-cgi.conf # This configuration file loads a CGI module appropriate to the MPM # which has been configured in 00-mpm.conf. mod_cgid should be used # with a threaded MPM; mod_cgi with the prefork MPM. <IfModule mpm_worker_module> LoadModule cgid_module modules/mod_cgid.so </IfModule> <IfModule mpm_event_module> LoadModule cgid_module modules/mod_cgid.so </IfModule> <IfModule mpm_prefork_module> LoadModule cgi_module modules/mod_cgi.so </IfModule> </code></pre> <p>ここもそのまま。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/00-mpm.conf # Select the MPM module which should be used by uncommenting exactly # one of the following LoadModule lines. See the httpd.conf(5) man # page for more information on changing the MPM. # prefork MPM: Implements a non-threaded, pre-forking web server # See: http://httpd.apache.org/docs/2.4/mod/prefork.html # # NOTE: If enabling prefork, the httpd_graceful_shutdown SELinux # boolean should be enabled, to allow graceful stop/shutdown. # #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so # worker MPM: Multi-Processing Module implementing a hybrid # multi-threaded multi-process web server # See: http://httpd.apache.org/docs/2.4/mod/worker.html # #LoadModule mpm_worker_module modules/mod_mpm_worker.so # event MPM: A variant of the worker MPM with the goal of consuming # threads only for connections with active processing # See: http://httpd.apache.org/docs/2.4/mod/event.html # LoadModule mpm_event_module modules/mod_mpm_event.so </code></pre> <p>変更するのはここ。</p> <pre><code>- #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so + LoadModule mpm_prefork_module modules/mod_mpm_prefork.so - LoadModule mpm_event_module modules/mod_mpm_event.so # LoadModule mpm_event_module modules/mod_mpm_event.so </code></pre> <p>上述のうち2ヶ所を反転させます。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.out48.com/archives/5636/">CentOSにApacheとPHP入れてPHPファイルを表示しようとしたら503エラー Output48</a></li> </ul> <p>これでOK。起動も確認できました。</p> <p>テンプレートとして上述を反転させた <code>00-mpm.conf</code> を用意し、エントリポイントでファイルコピーして設定を上書きすることで対処しました。</p> <p><a href="https://crieit.now.sh/upload_images/abe7eb81a2b7f6580feb784cb81cdf1361cb21ea49eef.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/abe7eb81a2b7f6580feb784cb81cdf1361cb21ea49eef.jpg?mw=700" alt="最終的に AlmaLinux の Docker Compose で起動、確認した phpinfo() の画面" /></a></p> <p>最終的に AlmaLinux の Docker Compose を作成して起動し、ブラウザで該当の PHPファイル にアクセスし、 <code>phpinfo()</code> の画面が表示されることを確認できました。OK。</p> <h3 id="SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty"><a href="#SSLCertificateFile%3A+file+%27%2Fetc%2Fpki%2Ftls%2Fcerts%2Flocalhost.crt%27+does+not+exist+or+is+empty">SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</a></h3> <p>上述のエラーの他に</p> <blockquote> <p>SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</p> </blockquote> <p>localhost のSSL証明書がない、というエラーにも遭遇しました。</p> <p>なければ作れば良いだけ、上述で</p> <pre><code class="bash">echo ServerName www.example.com:80 >> /etc/httpd/conf/httpd.conf </code></pre> <p>としているので、このダミードメインを使います。</p> <pre><code class="bash"># openssl req -new -newkey rsa:2048 -nodes -out /etc/pki/tls/certs/localhost.csr -keyout /etc/pki/tls/private/localhost.key -subj &quot;/C=/ST=/L=/O=/OU=/CN=www.example.com&quot; ## 略 # openssl x509 -days 365 -req -signkey /etc/pki/tls/private/localhost.key -in /etc/pki/tls/certs/localhost.csr -out /etc/pki/tls/certs/localhost.crt Signature ok subject=CN = www.example.com Getting Private key </code></pre> <p>これでOK。 <code>ssl.conf</code> には記述はあるのですが……。</p> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <p>次に MySQL 側を。こちらはほぼ手を加える必要はないという想定です。</p> <pre><code class="bash"># dnf localinstall https://dev.mysql.com/get/mysql80-community-release-el8-2.noarch.rpm ## 略 Package mysql80-community-release-el8-2.noarch is already installed. Dependencies resolved. Nothing to do. Complete! </code></pre> <p>CentOS8 用ということでリポジトリの入れ方を少し変更。</p> <pre><code class="bash"># ls /etc/yum.repo.d/ CentOS-Linux-AppStream.repo CentOS-Linux-Media.repo epel-testing.repo CentOS-Linux-BaseOS.repo CentOS-Linux-Plus.repo mysql-community.repo CentOS-Linux-ContinuousRelease.repo CentOS-Linux-PowerTools.repo mysql-community-source.repo CentOS-Linux-Debuginfo.repo CentOS-Linux-Sources.repo remi-modular.repo CentOS-Linux-Devel.repo epel-modular.repo remi.repo CentOS-Linux-Extras.repo epel-playground.repo remi-safe.repo CentOS-Linux-FastTrack.repo epel.repo CentOS-Linux-HighAvailability.repo epel-testing-modular.repo </code></pre> <p><code>mysql-community.repo</code>, <code>mysql-community-source.repo</code> の2つが追加されたことを確認。</p> <pre><code class="bash"># dnf module disable mysql ## 略 Complete! </code></pre> <p>デフォルトの MySQL がいると邪魔になるので無効化して</p> <pre><code class="bash"># dnf -y install mysql-community-devel mysql-community-server ## 略 Installed: mysql-community-client-8.0.27-1.el8.x86_64 mysql-community-client-plugins-8.0.27-1.el8.x86_64 mysql-community-common-8.0.27-1.el8.x86_64 mysql-community-devel-8.0.27-1.el8.x86_64 mysql-community-libs-8.0.27-1.el8.x86_64 mysql-community-server-8.0.27-1.el8.x86_64 net-tools-2.0-0.52.20160912git.el8.x86_64 Complete! </code></pre> <p>インストール完了。</p> <pre><code class="bash"># mysqld --version /usr/sbin/mysqld Ver 8.0.27 for Linux on x86_64 (MySQL Community Server - GPL) </code></pre> <p>デフォルトでは <code>8.0.26</code> だったバージョンが地味に <code>8.0.27</code> に上がりました。</p> <pre><code class="bash"># grep 'temporary password' /var/log/mysqld.log yyyy-mm-ddThh:ii:ss.zzzzzzZ 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: XXXXXXXXXXXX </code></pre> <p>初期パスワードの出現位置が半角スペース区切りで13番目であることを確認。これはエントリポイントで <code>awk</code> を使って抽出、初期パスワードを環境変数の値で書き換える処理で使用するため個人的には重要ポイント。ここは変更しなくて良さそうです。</p> <pre><code class="bash"># cat /etc/my.cnf [mysqld] ## 略 datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid </code></pre> <p>デフォルトで4つパラメータが指定されています。うち3つはテンプレートの設定ファイルに記述がされているので不要、 <code>pid-file</code> だけ現時点では指定がないため、これだけ <code>base.cnf</code> に移植します。</p> <p><code>/etc/my.cnf</code> には上述4つを削除した代わりに <code>!includedir /etc/my.cnf.d</code> を末尾に追加したものを現行の <code>my.cnf</code> と差し替え。</p> <p>これで MySQL 側は良さそうです。</p> <p>なお、 phpMyAdmin は公式イメージをそのまま使っているので変更なし。</p> <p>以上で一通り動作検証できたので、これをベースに AlmaLinux 用に書き換えれば良さそうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Docker"><a href="#Docker">Docker</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/almalinux?tab=tags">almalinux Tags | Docker Hub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/centos?tab=tags">centos Tags | Docker Hub</a></li> </ul> <h3 id="AlmaLinux"><a href="#AlmaLinux">AlmaLinux</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.apar.jp/linux/15554/">AlmaLinux 8.4 LAMPサーバインストールメモ【Apache2.4+MySQL8.0+PHP7.4】 | あぱーブログ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yamada-hakase/items/17b41d33e30232b69fe2">AlmaLinuxを使ってみた(後編) - Qiita</a></li> </ul> <h3 id="dnf"><a href="#dnf">dnf</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.trippyboy.com/2021/terraform/almalinux%E3%81%ABterraform%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92yum%E3%81%A7%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B%E3%81%AE%E5%B7%BB/">Almalinuxにterraformコマンドをyumで導入するの巻 - TrippyBoyの愉快な日々</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/40937056/dnf-missing-config-manager-command">linux - DNF missing config-manager command - Stack Overflow</a></li> </ul> <h3 id="remi"><a href="#remi">remi</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://rpms.remirepo.net/">Remi's RPM repository</a></li> </ul> <h3 id="AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message"><a href="#AH00558%3A+httpd%3A+Could+not+reliably+determine+the+server%27s+fully+qualified+domain+name%2C+using+192.0.2.1.+Set+the+%27ServerName%27+directive+globally+to+suppress+this+message">AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/a6deebbad207d627b598">[Docker]AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using ***.***.*.*. Set the 'ServerName' directive globally to suppress this message - Qiita</a></li> </ul> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/03/12/post-7365/">CentOs8 php7.4をインストールする | mebee</a></li> </ul> <h3 id="(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed"><a href="#%282%29No+such+file+or+directory%3A+AH02454%3A+FCGI%3A+attempt+to+connect+to+Unix+domain+socket+%2Frun%2Fphp-fpm%2Fwww.sock+%28%2A%29+failed">(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/58365479/no-such-file-or-directory-ah02454-fcgi-attempt-to-connect-to-unix-domain-sock">apache - No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /var/run/php/php5.6-fpm.sock (*) failed - Stack Overflow</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.out48.com/archives/5636/">CentOSにApacheとPHP入れてPHPファイルを表示しようとしたら503エラー Output48</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.stackoverflow.com/questions/46727/amazon-linux2%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BCah02454%E3%82%84ah01079%E3%81%AA%E3%81%A9%E3%81%8C%E5%87%BA%E3%81%A6httpd%E3%81%8C%E3%81%86%E3%81%BE%E3%81%8F%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93">php - amazon-linux2でエラーAH02454やAH01079などが出てhttpdがうまく起動しません - スタック・オーバーフロー</a></li> </ul> <h3 id="php-fpm"><a href="#php-fpm">php-fpm</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kotarella1110/items/634f6fafeb33ae0f51dc">nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する - Qiita</a></li> </ul> <h3 id="SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty"><a href="#SSLCertificateFile%3A+file+%27%2Fetc%2Fpki%2Ftls%2Fcerts%2Flocalhost.crt%27+does+not+exist+or+is+empty">SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://daredemose.com/linux/ssl/">SSL/TLS | 初心者向けプログラミング講座</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://hacknote.jp/archives/51779/">いつの間にか作られてる /etc/pki/tls/certs/localhost.crt と /etc/pki/tls/private/localhost.key の謎 | ハックノート</a></li> </ul> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yasushi-jp/items/1579c301075d693a2a36">MySQL 8.0 を CentOS 8.1 にインストールする手順 - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17849 2021-12-13T23:56:29+09:00 2021-12-13T23:56:29+09:00 https://crieit.net/posts/awk-only-match-last-line-for-mysql-init-password-20211213 awk で条件に合致する最後の行のみ処理する <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>次のようなシチュエーションを考えます。</p> <ul> <li>Docker 環境で MySQL の初期パスワードを変更するシェルスクリプトをエントリポイントで実行</li> <li>MySQL のログ (特に <code>mysql-error.log</code>) をボリュームマウントしてファイルの永続化を実施している</li> <li>このプロジェクトでディレクトリを使い回して1回目の MySQL のログを削除せずに2回目のビルドを実行する</li> </ul> <p>このとき、 <code>mysql-error.log</code> 内に初期パスワードが2回記録されることになります。</p> <p>すると、条件に合致する最初の行(1回目の初期パスワード)を処理しようとした際に(2回目のパスワードが設定されているため)パスワードが合致せず、処理が失敗してしまう遭遇したので対処しました。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <pre><code>- DB_INIT_PASSWORD=$(sudo grep 'temporary password' /var/log/mysql/mysql-error.log | sudo awk '{print $13}') + DB_INIT_PASSWORD=$(sudo grep 'temporary password' /var/log/mysql/mysql-error.log | sudo awk 'END{print $13}') </code></pre> <p>エントリポイントのシェルスクリプトで、 <code>awk</code> に <code>END</code> を付け加えました。処置としてはこれだけ。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p>試しに、中身のシェルスクリプトを該当環境で手動で実行してみます。</p> <h3 id="変更前"><a href="#%E5%A4%89%E6%9B%B4%E5%89%8D">変更前</a></h3> <pre><code class="bash"># sudo grep 'temporary password' /var/log/mysql/mysql-error.log | sudo awk '{print $13}' XXXXXXXXXXXX YYYYYYYYYYYY </code></pre> <p>変更前では複数マッチしていることが確認できます。このうち、最初のパスワード <code>XXXXXXXXXXXX</code> で認証をくぐって理しようとすると冒頭のようにパスワードが合致せず失敗します。</p> <h3 id="変更後"><a href="#%E5%A4%89%E6%9B%B4%E5%BE%8C">変更後</a></h3> <pre><code class="bash"># sudo grep 'temporary password' /var/log/mysql/mysql-error.log | sudo awk 'END{print $13}' YYYYYYYYYYYY </code></pre> <p>そこで変更後。今度は1回目のパスワード <code>XXXXXXXXXXXX</code> がヒットせず、2回目のパスワード <code>YYYYYYYYYYYY</code> のみがヒットするようになりました。</p> <p>これで Docker Compose 全体のビルドもコケなくなりました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://orebibou.com/ja/home/201607/20160725_001/">awkで最後の行(最終行)のみ処理を行わせる | 俺的備忘録 -なんかいろいろ-</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17840 2021-12-08T23:55:01+09:00 2021-12-08T23:55:01+09:00 https://crieit.net/posts/mysql-bulk-import-from-sql-statement-textfile-20211208 テキストファイル(SQL文) から MySQL にインポートする (Docker) <p>大量のデータ(数十万レコード程度)を phpMyAdmin 経由で MySQL にインポートしようとしたらメモリ不足でコケたので、 CLI から直接インポートする方法を模索しました。</p> <p>今回試したのは次の2つの方法</p> <ol> <li><code>use DB_NAME;</code> を SQL文 のテキストファイルの先頭に記述した上で、 <code>mysql -u root -p < FILE_PATH</code></li> <li>MySQL にログインして <code>use DB_NAME;</code> した後に <code>source FILE_PATH</code></li> </ol> <h2 id="方法1 (mysql -u root -p &lt; FILE_PATH)"><a href="#%E6%96%B9%E6%B3%951+%28mysql+-u+root+-p+%26lt%3B+FILE_PATH%29">方法1 (mysql -u root -p < FILE_PATH)</a></h2> <p><code>use DB_NAME;</code> を SQL文 のテキストファイルの先頭に記述した上で、 <code>mysql -u root -p < FILE_PATH</code> する方法です。</p> <p>まず、 SQL文 のテキストファイル (phpMyAdmin 等でエクスポートした、テーブル定義 <code>CREATA TABLE</code> やデータの <code>INSERT</code> を含むもの) に少し手を加えます。</p> <pre><code>-- MySQL dump XX.XX -- -- Host: localhost Database: DATABASE_NAME -- ------------------------------------------------------ -- Server version XX.XX.XX USE DATABASE_NAME; /* 追記 */ /* 以下略 */ </code></pre> <p>先頭に <code>USE DATABASE_NAME;</code> を記述して、どのデータベースにインポートするのか明記します。</p> <p>この加工を施した上で、シェルに入り込み</p> <pre><code class="bash">> docker-compose exec db /bin/bash </code></pre> <p>※今回は MySQLサーバ が Docker Compose 使用の Dockerコンテナ なのでこれで入ります</p> <pre><code class="bash"># ls -al /home/MYSQL_DUMP.sql -rw-r--r-- 1 root root XXXXX WWW dd hh:ii /home/MYSQL_DUMP.sql # mysql -u root -p &lt; /home/MYSQL_DUMP.sql Enter password: # exit </code></pre> <p><code>mysql -u root -p < /home/MYSQL_DUMP.sql</code> でインポート。これで無事に入ったことを確認。</p> <h2 id="方法2 (use DB_NAME; &amp;&amp; source FILE_PATH)"><a href="#%E6%96%B9%E6%B3%952+%28use+DB_NAME%3B+%26amp%3B%26amp%3B+source+FILE_PATH%29">方法2 (use DB_NAME; && source FILE_PATH)</a></h2> <p>方法1だとお手軽ではあるのですが、実行する際に内容を確認せずに実行してしまうので若干怖い部分があります。</p> <p>しかも今回は対象のファイルがかなり大きいのでエディタで開くのも一苦労なシロモノ。</p> <p>そういった事情から、なるべくファイルの中身を編集することは避けたいと考えました。そこで2つ目の方法。</p> <pre><code class="bash">> docker-compose exec db /bin/bash </code></pre> <p>同じく入り込みます。</p> <pre><code class="bash"># mysql -u root -p Enter password: mysql> </code></pre> <p>今回は素直にログインします。</p> <pre><code class="bash">mysql> use DATABASE_NAME; Database changed </code></pre> <p>違うのはまずここ。先程はファイル内で <code>USE</code> していましたが、今回はコマンドを直接叩きます。</p> <pre><code class="bash">mysql> source /home/MYSQL_DUMP.sql # 略 Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.01 sec) mysql> exit Bye </code></pre> <p>今回は <code>source /home/MYSQL_DUMP.sql</code> の形でインポートを実行します。クエリごとにログが出力される点も先程と異なりますね。</p> <p>どちらの方法でも大量のデータを正常にインポートできることを確認しました。</p> <h2 id="余談: エクスポート"><a href="#%E4%BD%99%E8%AB%87%3A+%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88">余談: エクスポート</a></h2> <p>インポートするのが大変ならば、エクスポートするのも大変。同様に phpMyAdmin の画面からエクスポートさせようとすると固まってしまうので、エクスポートも CLI から行うことにします。</p> <pre><code class="bash"># mysqldump -u root -p -h localhost DATABASE_NAME &gt; /home/MYSQL_DUMP.sql </code></pre> <p><code>mysqldump</code>コマンド でエクスポート。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="方法1, 方法2"><a href="#%E6%96%B9%E6%B3%951%2C+%E6%96%B9%E6%B3%952">方法1, 方法2</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/mysql-batch-commands.html">MySQL :: MySQL 5.6 リファレンスマニュアル :: 4.5.1.5 テキストファイルから SQL ステートメントを実行する</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://style.potepan.com/articles/25532.html">【MySQL】SQLファイルを実行する方法【3種類】 | ポテパンスタイル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/IysKG213/items/994e9f4ad12ff8aee322">MySQLでファイルからSQLを実行する - Qiita</a></li> </ul> <h3 id="方法2"><a href="#%E6%96%B9%E6%B3%952">方法2</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ydzum1123/items/219d8e60843a678be719">【MySQL】 SQLをファイルから実行する方法 - Qiita</a></li> </ul> <h3 id="mysqldump によるエクスポート"><a href="#mysqldump+%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88">mysqldump によるエクスポート</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/PlanetMeron/items/3a41e14607a65bc9b60c">mysqldumpまとめ - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17771 2021-11-20T17:13:30+09:00 2021-11-20T17:13:30+09:00 https://crieit.net/posts/mysql-on-docker-error-lower-case-table-names-20211120 Docker から MySQL 用のコンテナで初期化・起動しようとしたら The server option 'lower_case_table_names' is configured ... エラーで失敗する <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p>Docker から自作の MySQL 用のコンテナで初期化・起動しようとしたら MySQL が起動しておらず、手動で初期化コマンドを実行したら以下のエラーで失敗する現象に遭遇しました。</p> <blockquote> <p>[ERROR] The server option 'lower_case_table_names' is configured to use case sensitive table names but the data directory is on a case-insensitive file system which is an unsupported combination. Please consider either</p> <p>using a case sensitive file system for your data directory or switching to a case-insensitive table name mode.</p> </blockquote> <h2 id="環境"><a href="#%E7%92%B0%E5%A2%83">環境</a></h2> <p>環境としては以下の自作の LAMP環境 作成用の Docker Compose を使用しました。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/docker_compose_ambergrease/tree/afa8c12e3dff1ec435643bcd8c9a3215d1409a25">GitHub - arm-band/docker_compose_ambergrease</a></li> </ul> <p>MySQL5.7系 で、 entrypoint で</p> <ul> <li>設定ファイルをテンプレートからコピーして配置</li> <li><code>/usr/sbin/mysqld --user=mysql --initialize &</code> で初期化コマンド実行</li> <li><code>/usr/sbin/mysqld --user=mysql &</code> で MySQL 起動</li> <li>MySQL のログから初期パスワードを見つけ出し、環境変数で設定された値に管理者パスワードを変更</li> </ul> <p>をしています。</p> <p>このうち、2つ目の <code>/usr/sbin/mysqld --user=mysql --initialize &</code> で上述のエラーが発生していました。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <p>エラー文は「 MySQL の設定では大文字小文字を区別する設定になっているが、 <code>data</code>ディレクトリ のファイルシステムは大文字小文字を区別しない環境になっていて不一致を起こしているので、どちらかに統一してね」ということのようです。</p> <p>今回は Docker for Windows 利用で、 <code>data</code>ディレクトリ はホスト側の Windows のフォルダにボリュームをマウントしています。また、データはコンテナを落としても保持していてほしいので Windows とのボリュームマウントを解除する方向性はなし。</p> <p>そのため、ファイルシステムは Windows 依存になるので、大文字小文字を区別するファイルシステムに寄せるのは厳しい。したがって、 MySQL 側の設定を調整することにします。</p> <pre><code class="conf">[mysqld] ## 略 # not only lowercase characters in tablename # lower_case_table_names = 0 lower_case_table_names = 1 </code></pre> <p>該当の設定は <code>lower_case_table_names</code> 。この値<code>0</code>だと大文字小文字を区別する、<code>1</code>だと小文字に変換、<code>2</code>だと大文字小文字を区別してディスクに保存するが MySQL からのルックアップ時に小文字に変換、という挙動になるようです。</p> <p>今回はシンプルに全て小文字変換で<code>1</code>とすることにしました。</p> <p>これで問題が解決されたことを確認。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://teratail.com/questions/131905">MySQL - MySQLが起動しない(mac)|teratail</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/identifier-case-sensitivity.html">MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.2.2 識別子の大文字と小文字の区別</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17204 2021-05-19T23:31:42+09:00 2021-05-19T23:31:42+09:00 https://crieit.net/posts/mysql-cant-boot-through-socket-20210519 Docker で MySQL に入ろうとしたら「Can't connect to local MySQL server through socket ...」で怒られたので起動する <p>Docker で CentOS + MySQL 環境を構築する過程で、 <code>mysql -u root -p</code> で MSQL に入ろうとしたら以下のエラーで怒られました。</p> <blockquote> <p>ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)</p> </blockquote> <p>検索すると MySQL が起動していないとこのエラーになるとのこと。</p> <p>CentOS そのものならば <code>systemctl</code> で一発なのですが、 Docker 上では <code>systemctl</code> をすぐに使うことはできません。</p> <p>そのため、 <code>/usr/sbin/mysqld --user=mysql &</code> で起動させました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/takaikeee12/items/20e9ddaf14b81737893c">ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)の対処法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://tech-blog.s-yoshiki.com/entry/220">CentOS に MySQL8.0をインストールする | 404 motivation not found</a></li> </ul> arm-band