- 投稿日:2020-12-01T23:36:13+09:00
【MySQL 8.0.22】 レプリケーション on Docker
Dockerを使ってMySQLのレプリケーション環境を構築していきたいと思います。
Dockerfileやdocker-composeを使った構築方法に関しては、結構たくさん記事があるので、
趣向を変えてコマンドだけで構築していきたいなと思います。
- 環境
- macOS Catalina ver: 10.15.17
- Docker Engine ver: 19.03.13
完成図
ネットワークの作成
「192.168.220.0/24」の範囲で「mysql_net」という名前のネットワークを構築します。
コマンドは以下です。docker network create -d bridge --subnet=192.168.220.0/24 mysql_net作成されたことを確認したい場合は以下のコマンドを使ってみてください。
docker network ls -f "NAME=mysql_net"bridgeモードについては以下の方の記事がわかりやすいです。
ボリュームの作成
replicationを行うのに必要なダンプを効率よく共有できるように共有するvolumeを準備します。
コマンドは以下です。docker volume create --name shared-dump
作成されたことを確認したい場合は以下のコマンドを使ってみてください。
docker volume ls -f "NAME=shared-dump"コンテナの作成
今回は、現在最新バージョンである8.0.22を利用します。
以下のコマンドでイメージをpullしてください。docker pull mysql:8.0.22以下のコマンドでイメージがpullできていることを確認して下さい。
docker images mysql:8.0.22MasterとSlaveのコンテナを一つずつ作成します。
# Master docker run -d \ -v shared-dump:/dump \ -e MYSQL_ROOT_PASSWORD=root \ --network mysql_net --ip 192.168.220.50 \ --name master \ mysql:8.0.22 # Slave docker run -d \ -v shared-dump:/dump \ -e MYSQL_ROOT_PASSWORD=root \ --network mysql_net --ip 192.168.220.100 \ --name slave \ mysql:8.0.22MasterとSlaveによるコマンドの違いはほとんどありません。
一応、どちらがMasterでどちらがSlaveであるかをわかりやすくするためにコンテナの名前を変えています。異なる点としてはこのぐらいです。ちょっとした検証でコンテナを立ち上げる際は、
--rm
オプションをつけて立ち上げるのが個人的に好きです(諸事情があって今回は付与できませんでしたが)。--rm
オプションは停止と同時にコンテナを削除してくれるのでゴミが残らないところがポイントです。設定ファイルを置き換える
設定ファイルの置き換えが
run
サブコマンドでできればいいんですけどね。Dockerfileやdocker-composeだとこの辺りの処理がとても楽ですよね。
この辺りがコマンドだと辛いですねー。というわけで、レプリケーションに必要なserver-idの設定値をそれぞれの環境に入れていきたいと思います。
# master docker exec master bash -c "echo 'server-id = 101' >> /etc/mysql/my.cnf" # slave docker exec slave bash -c "echo 'server-id = 102' >> /etc/mysql/my.cnf"設定を更新するためにmysqlを再起動する必要があります。
ただ、dockerを再起動するとコンテナは停止しちゃうのでコンテナの再起動が必要です。
そのため今回は--rm
オプションが使えませんでした。以下のコマンドを実行して再起動します。
docker restart $(docker ps -aq -f "NAME=master" -f "NAME=slave")確認には以下を実行してみてください。
for role in master slave; do docker exec ${role} bash -c "mysql -uroot -proot -e 'select @@server_id;'" doneレプリケーション
レプリケーション以外の環境は概ねできました。
ここからはあまりdocker関係ないですが、レプリケーションの検証まで行ってみます。
検証用にデータベースを作成する
for role in master slave; do docker exec ${role} bash -c "mysql -uroot -proot -e 'create database repl;'" doneレプリケーションを開始するための準備
ここから先の作業はcommandを渡して作業すると面倒なのでコンテナに入ることにします。
# 以下のコマンドでmasterコンテナに入ります。 docker exec -it master bash # rootユーザでmysqlに入ります。 mysql -uroot -proot適当なテーブルを作成し、データを投入します。
-- 適当なデータを投入します。 create table repl.users (id int unsigned not null auto_increment, name varchar(255) not null, primary key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; insert into repl.users (name) values ("sample taro"), ("sample hanako"); -- slaveが接続するために必要なレプリケーションユーザを作成します create user 'repl'@'192.168.220.100' identified with mysql_native_password by 'pQ8gDdzQHEtX@b8'; grant replication slave, replication client on *.* to 'repl'@'192.168.220.100'; -- 現在のマスターの状態を確認します。FileとPosition列の値をメモしてください show master status; quitダンプを取得します。
mysqldump -uroot -proot repl> /dump/repl_dump.sqlレプリケーションを開始する
次はslaveコンテナでの操作となりますので、まずはコンテナに入りましょう。
docker exec -it slave bashレプリケーションを開始するまでの流れとしては以下です。
- dumpを取り込む
- レプリケーションの設定を行う
- レプリケーションを開始する
まずは、ダンプを取り込みます。
mysql -uroot -proot repl < /dump/repl_dump.sqlレプリケーションの設定を行う前にmasterと差分があることを確認しましょう(mysqlへログインしている前提です)。
masterでの作業でメモした「File」と「Position」の値を利用します。-- replicationの設定を行います。 change master to master_host='192.168.220.50', master_user='repl', master_password='pQ8gDdzQHEtX@b8', master_log_file='binlog.000003', master_log_pos=1518; -- レプリケーションを開始します。 start replica;
start slave
コマンドは消されるようで今後はstart replica
を使うようです。同期を確認する
現在の
repl.users
テーブルの状態は以下です。mysql> select * from repl.users; +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | +----+---------------+ 2 rows in set (0.00 sec)適当なデータを投入し、master,slave両方でテーブルの状態を確認してみます。
mysql> insert into repl.users (name) values ("takeshi"), ("tanaka"), ("sasaki"); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 -- master mysql> select @@server_id; select * from repl.users; +-------------+ | @@server_id | +-------------+ | 101 | +-------------+ 1 row in set (0.00 sec) +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | | 3 | takeshi | | 4 | tanaka | | 5 | sasaki | +----+---------------+ 5 rows in set (0.00 sec) -- slave mysql> select @@server_id; select * from repl.users; +-------------+ | @@server_id | +-------------+ | 102 | +-------------+ 1 row in set (0.00 sec) +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | | 3 | takeshi | | 4 | tanaka | | 5 | sasaki | +----+---------------+ 5 rows in set (0.00 sec)大丈夫そうですね。
最後に
本来はもう少しひねりたかったのですが、普通の検証になってしまいました(すみません)。
インフラ業務では、本番により近い環境で作業を行うかどうかで品質が大きく変わってしまいます。
なので、dockerをもっとうまく使いこなせればなーと日々感じますねー。
- 投稿日:2020-12-01T20:13:33+09:00
Mariadb MaxScale使って、MySQLで持ってる個人情報のマスキングやってみた
なんでこんなことやったの?
- 担当プロジェクトにてセキュリティ面強化における開発要望となったため調査したら、権限周りと連動させての実装方法として意外と良さそうだったから
- アプリケーションで対応する方法もあるとは思うけど、運用で度々変更を求められることが想定されたので、設定値変更くらいで変えられる仕組みがよかった
とりあえず試してみた
使ったサーバが
Amazon Linux 1
だったのもあり、公式サイトのインストール手順にそって対応するとエラーが出てEC2にはインストールできず・・・$ cat /etc/system-release Amazon Linux AMI release 2018.03 $ curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash [error] Detected RHEL or compatible but version () is not supported. [error] The MariaDB Repository supports these Linux OSs, on x86-64 only: * RHEL/CentOS 6, 7, & 8 * Ubuntu 16.04 LTS (xenial), 18.04 LTS (bionic), & 20.04 LTS (focal) * Debian 8 (jessie), 9 (stretch), & 10 (buster) * SLES 12 & 15 [error] See https://mariadb.com/kb/en/mariadb/mariadb-package-repository-setup-and-usage/#platform-supportDockerイメージなら用意されてるみたいだったので、それでトライ
とりあえずEC2にDockerインストールして、イメージ取ってきて、作ったconfigをコピーして
docker run
してSQL実行してみたらでけた$ sudo yum update -y $ sudo yum install -y docker $ sudo service docker start Starting cgconfig service: [ OK ] Starting Docker: [ OK ] $ sudo usermod -a -G docker ec2-user $ docker pull mariadb/maxscale:latest $ docker run -d -p 4008:4008 --name mxs -v /etc/maxscale.cnf:/etc/maxscale.cnf -v /etc/maxscale.cnf.d/masking_rules.json:/etc/maxscale.cnf.d/masking_rules.json mariadb/maxscale:latest $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES XXXXXXXXXXXX mariadb/maxscale:latest "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 0.0.0.0:4008->4008/tcp mxsmaxscale.cnfファイルは以下のような感じ
maxscale.cnf# MaxScale documentation on GitHub: # https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Documentation-Contents.md # Global parameters # # Complete list of configuration options: # https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Getting-Started/Configuration-Guide.md [maxscale] threads=auto # Server definitions # # Set the address of the server to the network # address of a MySQL server. # [server1] type=server address=XX.XX.XX.XXX port=3306 protocol=MySQLBackend # Monitor for the servers # # This will keep MaxScale aware of the state of the servers. # MySQL Monitor documentation: # https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Monitors/MySQL-Monitor.md [MySQL-Monitor] type=monitor module=mysqlmon servers=server1 user=XXXXXXXXX password=XXXXXXXXX monitor_interval=10000 failcount=3 [Masking] type=filter module=masking rules=/etc/maxscale.cnf.d/masking_rules.json ←こいつにマスキング周りのルールを書く形でした warn_type_mismatch=always large_payload=ignore # Service definitions # # Service Definition for a read-only service and # a read/write splitting service. # # ReadConnRoute documentation: # https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Routers/ReadConnRoute.md [Read-Only-Service] type=service router=readconnroute servers=server1 user=XXXXXXXXX password=XXXXXXXXX router_options=slave filters=Masking # This service enables the use of the MaxAdmin interface # MaxScale administration guide: # https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Reference/MaxAdmin.md [MaxAdmin-Service] type=service router=cli # Listener definitions for the services # # These listeners represent the ports the # services will listen on. # [Read-Only-Listener] type=listener service=Read-Only-Service protocol=MySQLClient port=4008 address=0.0.0.0 [MaxAdmin-Listener] type=listener service=MaxAdmin-Service protocol=maxscaled socket=defaultマスキングルール(
/etc/maxscale.cnf.d/masking_rules.json
) はとりあえずこう/etc/maxscale.cnf.d/masking_rules.json{ "rules": [ { "replace": { "database": "sample", "table": "user", "column": "user_name" }, "with": { "fill": "X" } }, { "replace": { "database": "sample", "table": "user", "column": "mail_address" }, "with": { "fill": "@" } } ] }実際の結果
直接接続
$ mysql -u XXXXXXXX -p -h XX.XX.XX.XXX Enter password: mysql> use sample 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> select id,user_name,mail_address from user limit 3; +----+---------------------+-----------------+ | id | user_name | mail_address | +----+---------------------+-----------------+ | 1 | 山田太郎1 | yamada@test.com | | 2 | スズキ 一太郎 | suzuki@test.com | | 3 | 佐藤 二郎 | sato@test.com | +----+---------------------+-----------------+ 3 rows in set (0.00 sec)MaxScale経由で接続
$ mysql -u XXXXXXXX -p -h 172.17.0.2 -P 4008 Enter password: mysql> use sample 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> select id,user_name,mail_address from user limit 3; +----+---------------------+-----------------+ | id | user_name | mail_address | +----+---------------------+-----------------+ | 1 | XXXXXXXXXXXXXXX | @@@@@@@@@@@@@@@ | | 2 | XXXXXXXXXXXXXXXX | @@@@@@@@@@@@@@@ | | 3 | XXXXXXXXXXXXXXXXXXX | @@@@@@@@@@@@@@@ | +----+---------------------+-----------------+ 3 rows in set (0.00 sec)使ってみた感想
フルマスキングができたので、次は複数のマスキングルール試したらやれそう
電話番号のマスキングやら住所も正規表現でやればマスキング自体はできそうですね
「対象文字列の一部はそのままがいい」とかワガママ案件でなければw参考サイト
Mariadb MaxScale周り
- MariaDB MaxScale を使ってデータベース内の個人情報をリアルタイムにマスクする - LiBz Tech Blog
- Setting up MariaDB MaxScale - MariaDB Knowledge Base
- MariaDB Memory Allocation - MariaDB Knowledge Base
- MariaDB MaxScale masking フィルタによるデータ保護 | スマートスタイル TECH BLOG|データベース&クラウドの最新技術情報を配信
- MariaDB MaxScale を CentOS 7 にインストール | スマートスタイル TECH BLOG|データベース&クラウドの最新技術情報を配信
- mariadb/maxscale - Docker Hub
その他のマスキング対応ツール
- Dynamic Data Masking Solution | DataSunrise Data & DB Security
- DataSunrise Security を使用して Amazon Redshift の PII データを保護および監査する | Amazon Web Services ブログ
- ユーザーローカル 個人情報匿名化フィルター mysqlデータマスキング - mysql - Program QA
マスキングの正規表現周り
- 投稿日:2020-12-01T15:35:05+09:00
きっと正しくないレンタルサーバーの作り方 Vol.2 - ざっくりアーキテクト
はじめに
前回の記事からだいぶ時間が経ちましたが、第2回めです(・∀・)。
今回は全体的なアーキテクトについて記述していこうと思います(・∀・)。※注意!
この一連の記事で紹介するコードは動作の概念を説明するものでありセキュリティーなどは意識していません(・∀・)。実際に運用するシステムなどに使用しないでください(・∀・)。
(そのまま使うひともいないと思いますが)また、私も記事を書きながら開発をしていくので「後になってみたら最初の方の記事間違えてたー」なんて事は起きそうです(・∀・)。
ご了承ください(・∀・)。
目次
全体の構成
基本的に単一のサーバーで動作しますが、後から複数台構成に移行しやすいような構成を目指します(・∀・)。
アカウント管理
rentaserve.com では
- 管理者アカウント(以降「アカウント」と表記)
- ユーザーアカウント(以降「ユーザー」と表記)
の二種類のアカウントがあります(・∀・)。どちらもレンタルサーバーを使用するユーザーが使用するアカウントですが、ちょっと意味合いが異なります(・∀・)。
例えば
というサイトを作ったとしたら、mao を アカウント と呼びます(・∀・)。
そして
taro@mao.rentaserve.com
jiro@mao.rentaserve.com
saburo@mao.rentaserve.comのようなメール / FTP アカウントを作成した場合、taro や jiro を ユーザー と呼びます(・∀・)。
rentaserve.com では
- アカウントを LDAP(ActiveDirectory)で管理
- ActiveDirectory には Samba 4 AD を使用
- ユーザーを mariadb で管理
することにします(・∀・)。
LDAPを採用することにより、サーバーを複数台にした時にアカウントを一元管理しやすいためです(・∀・)。
各サーバーにドメインコントローラーを設置して連携させることで、他のサーバーにアカウントを追加しても他のサーバーに反映されるように出来るためです(・∀・)。また、アカウントのパスワードの秘匿性が高いという利点もあります(・∀・)。
(ActiveDirectory はたとえ管理者でもパスワードのハッシュ値さえ見れない仕様のため)rentaserve.com システムの本体からは LDAP 関数を使用してアカウントの操作をします(・∀・)。
ウェブサーバー
単純に Apache2.4 を使用します(・∀・)。
php-fpm を使用して PHP スクリプトを実行できるようにします(・∀・)。特に Apache 固有の機能などは使用しないため、別に nginx 等でも構いません(・∀・)。
サブドメイン毎に設定ファイルを作成し、php-fpm の Unix ソケット経由で PHP を実行します(・∀・)。
セキュリティー確保のため、ユーザー毎に Unix ソケットを作成して PHP を実行します(・∀・)。
PHP
前述した通り、7.3 を使用します(・∀・)。
php-fpm 用の設定を各ユーザーごとに作成して、そこにユーザー固有の設定を書くことによって xdebug などを設定可能にします(・∀・)。
また、
- exec など使い方によってはセキュリティー事故の危険性が高い関数を禁止する
- LDAP系の関数を使用禁止にする
- chroot を使用してホームディレクトリ以下までしかアクセスできないようにする
- php-fpm の実行ユーザーをアカウントユーザーと同一にする(www-data などで実行しない)
のような細工をしておきます(・∀・)。FTP
ProFTPd を使用します(・∀・)。
ユーザーは mariadb で管理し、ドメイン単位でアクセス先のディレクトリを制限します(・∀・)。
ドメイン単位でのアクセス制限とは、例えば mao というアカウントを作成したとして、そのアカウントが独自ドメイン「example.com」を取得した場合、
- taro@mao.rentaserve.com
- /home/mao/public_html/mao.rentaserve.com 以下にアクセス可能
- jiro@example.com
- /home/mao/public_html/example.com 以下にアクセス可能
というような意味です(・∀・)。
別に一緒くたにしたり混在したりした管理も可能ですが、基本的に
「そのドメインのひとがそのドメインのウェブサイトを管理するだろうなー」
という方針のため、このような仕様にします(・∀・)。また、FTPS(FTP over SSL)でのアクセスのみ可能とします(・∀・)。
生の FTP はセキュリティー的にアレなので使用させない方向で考えています(・∀・)。独自ドメインを設定したユーザーの場合、FTPクライアントで証明書関連の警告が出るかも
メール
Postfix / Dovecot を使用します(・∀・)。
これもユーザー管理に mariadb を使用します(・∀・)。
メールは IMAP のみサポートし、POP3 はサポートしないことにします(・∀・)。
(特に理由はない)こちらも IMAPS / SMTPS といった TLS 通信のみ許可する方向で考えています(・∀・)。
ファイヤーウォール
ufw などを使用します(・∀・)。
あんまり考えてはいません(・∀・)wサーバー管理
今回は考えません(・∀・)。
興味ある人は Zabbix などを入れてみるとかどうでしょう(・∀・)。
(投げやり)外部公開DNS
今回は PowerDNS を使用します(・∀・)。
(bind は使いにくい)PowerDNS 採用の理由は
- レコードを mariadb で管理できる
- Debian/GNU Linux 10 標準のパッケージなので apt で簡単に使用できる
ためです(・∀・)。ドメイン
とりあえず お名前.com を使用しています(・∀・)。
他のレジストラでも良いと思いますが、よく分かっていません(・∀・)w独自ドメインについては rentaserve.com を権威DNSサーバーとして動作させ、ネームサーバーに rentaserve.com(ns1.rentaserve.com とか ns2.rentaserve.com とか作って)アクセスさせることで実現させるつもりです(・∀・)。
独自ドメインの設定時にネームサーバーが *.rentaserve.com ではない場合に弾くような仕組みです(・∀・)。
証明書
みんな大好き Let's Encrypt を使用します(・∀・)。
*.rentaserve.com のワイルドカードドメインを取得するためちょっと細工が必要そうです(・∀・)。
最後に
つらつらと長くなりましたが、次回から実際にサーバーを構築していこうと思います(・∀・)。
- 投稿日:2020-12-01T12:07:35+09:00
Rangeパーティションを削除する方法
パーティションしたテーブルを戻したい事はほとんどないような気がしますが、
たまたまやる機会があったので備忘録的な物として残しておきます。まずは、普通に作ったパーティションを全て削除しようと思いました。
ALTER TABLE table_name DROP PARTITION p201905, p201908,
p201911, p202002, p202005, p202008, p202011, p202102, p202105, p202108, p202111;
Cannot remove all partitions, use DROP TABLE instead
というエラーが出て駄目でした。解決方法
ALTER TABLE table_name REMOVE PARTITIONING;
件数に寄りますがかなり時間が掛かります。
create unique index unique_index_activities_id on activities (id) ; ALTER TABLE table_name DROP PRIMARY KEY; ALTER TABLE table_name ADD PRIMARY KEY (id) ; DROP INDEX uniq_index_activities_id_and_created_at ON table_nameあとは、プライマリーキーを削除するために、一旦ユニークキーを作成して、プライマリーキーを削除して
IDで再度作り直して、Rangeパーティションのユニークインデックスキーを削除しました。
- 投稿日:2020-12-01T10:50:20+09:00
MySQL 結合を使用したらエラーが出た
目的
- テーブル結合を使用したら「Not unique table/alias」というエラーが出たので解決方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提情報
- 架空のマッチングアプリのユーザ情報を格納しているテーブルを用いて説明を行う。
- ユーザ情報としてマッチングしたいユーザID、ユーザ名、マッチングしたい異性の最低年齢、マッチングしたい異性の最高年齢、本人の年齢を格納しているusersテーブルに年齢のレンジを格納しているage_rangesテーブルを結合したい。
DB、テーブル、カラム、各データの挿入は下記のSQL、またはコマンドを上から実行して行った。
MySQLのログイン
$ mysql -u root -pDB.テーブル、カラム、各データの挿入は下記のSQLを実行した。
create database join_test; use join_test; create table users(id int, name varchar(255), matching_age_min int, matching_age_max int, age int); create table age_ranges( id int, age_range varchar(255)); insert into users(id, name, matching_age_min, matching_age_max, age) values (1, 'miriwo', 2, 5, 2); insert into age_ranges(id, age_range) value(1, '下限なし');SQL
show tables;
を実行したときに下記のように表示される。+---------------------+ | Tables_in_join_test | +---------------------+ | age_ranges | | users | +---------------------+ 2 rows in set (0.00 sec)SQL
select * from users;
を実行したときに下記のように表示される。+------+--------+------------------+------------------+------+ | id | name | matching_age_min | matching_age_max | age | +------+--------+------------------+------------------+------+ | 1 | miriwo | 2 | 5 | 2 | +------+--------+------------------+------------------+------+ 1 row in set (0.00 sec)SQL
select * from age_ranges;
を実行したときに下記のように表示される。+------+--------------+ | id | age_range | +------+--------------+ | 1 | 下限なし | | 2 | 20代 | | 3 | 30代 | | 4 | 40代 | | 5 | 50代 | | 6 | 上限なし | +------+--------------+ 6 rows in set (0.00 sec)テーブルの情報
usersテーブル
カラム名 データ型 id int name varchar matching_age_min int matching_age_max int age int age_rangesテーブル
カラム名 データ型 id int age_range varchar テーブル結合を使って表示したいもの
usersテーブルとage_rangesテーブルを結合して下記のように表示したい。
id name matching_age_min matching_age_max age 1 miriwo 20代 50代 20代 実行したSQLと結果
テーブル結合の基本に乗っ取り下記のSQLを実行した。
select users.id, users.name, age_ranges.age_range, age_ranges.age_range, age_ranges.age_range from users join age_ranges on users.matching_age_min = age_ranges.id join age_ranges on users.matching_age_max = age_ranges.id join age_ranges on users.age = age_ranges.id where users.id = 1;下記のエラーが出力された。
ERROR 1066 (42000): Not unique table/alias: 'age_ranges'解決策
下記のようなSQLを実行すれば問題は解決する、SQL記載後に簡単に説明をする。
select users.id, users.name, age_ranges_min.age_range, age_ranges_max.age_range, age_ranges_user.age_range from users join age_ranges as age_ranges_min on users.matching_age_min = age_ranges_min.id join age_ranges as age_ranges_max on users.matching_age_max = age_ranges_max.id join age_ranges as age_ranges_user on users.age = age_ranges_user.id where users.id = 1;ポイントは「join句で結合を指定するときにasを使って別名を指定する」である。
下記に上記のSQLのjoin句の一部を記載する。
join age_ranges as age_ranges_min on users.matching_age_min = age_ranges_min.id上記ではjoin句で結合先テーブルを指定するときにasを用いて別名を与えている。直後のon句では結合条件のテーブル名を別名を用いて指定している。
またselect句でも「join句で指定した別名.カラム名」と指定する事により、select句で出力するカラムの重複を防いでいる。
- 投稿日:2020-12-01T08:33:23+09:00
Docker(docker-compose) + MySQL でのパスワード入力時のエラー / ERROR 1045 (28000): Access denied for user
RailsをAPIモードで Docker + MySQL を使用してWebアプリ開発をしています。
環境構築時に、
docker-compose up
して dbにログインしようとすると
ERROR 1045 (28000): Access denied for user
と
ログインができないエラーに遭遇したので、私が解決できた方法を記載しておきます!解決法
私の場合は、docker-compose.ymlで
docker-compose.ymldb-data:/var/lib/mysql:cached
このようにしてdbデータをローカルに保存してマウントしていたのですが、
ここに試しに作っていた、いくつかのvolumeが残っていたため、
そちらの環境変数が優先され、現在のパスワードが反映されていないという問題でした。MySQL公式に
既にデータベースを格納しているデータディレクトリとともにコンテナを起動した場合は、下記の変数はどれも影響を及ぼさないことに留意してください。
とあるようです。
詳しくは こちら の記事をご覧ください。つまり、
マウント先のローカルのdbを一旦削除して、再度作り直せばOKということです。手順
docker-compose down docker volume ls docker volume rm (lsで表示されたvolume名をスペース区切りで並べる) docker-compose up
で、作り直して再度ログインすると、問題なくログインできました!!
上記の手順で、
docker volume rm
時にもしかすると、削除できない場合があるかもしれません。
その時は、docker system prune docker volume rm ○○
とするとうまくいくかも知れません!!
- 投稿日:2020-12-01T02:43:47+09:00
Docker-ComposeでDockerのベース環境を簡単構築![Sample:MySQL]
目的
docker-composeによって、簡単にDocker環境構築をしてみよう、と言う記事です。
自分が環境を先日作った際に、あまりyml文内の意味やオプション、コマンドって結局どういう意味やねん、、、とプチパニックになったのでまとめ直したものになります。そのため知識、かつ環境の構築がこの記事のみで完結できるような構成を目指しました。
あまりMarkDown記法になれていないこともあるため、少し拙い文章になるかと思いますが、読んでいただけると幸いです。
サクッと作るぞ!チートシート
この記事をみてサクッと環境作りたいわ、って人のために
まず実行するためだけの手順を完全に説明を省いて紹介していきます。
今回はサンプルのためにMySQL環境を作成しますが、
柔軟に変更できるようにコメントにて、可変できる部分は特記しています。構成
構成は以下の通りに作成しました。
├── docker │ └── db │ ├── data │ ├── mysql-config.cnf │ └── init │ ├── 001-create-tables.sql │ └── init-database.sh └── docker-compose.ymlソースコード
1. docker-compose.yml
アプリケーションを構成するサービスを
docker-compose.yml
に書きます。今回はMySQLにあたりますね。
これを使用するコードディレクトリの一番親のディレクトリに生成します。
# docker-compose.yml version: '3' services: # (例1)ここに使用するサービスを書きます mysql: # image > コンテナ実行時の元になるイメージです。使うサービスのリポジトリ名だったりを書きます。 image: mysql:latest # volume > 仮想環境上でのファイルをパスを指定してアクセスできるようにします。パスはこのymlファイルがあるディレクトリ基準です volumes: - ./mysql/data:/var/lib/mysql - ./mysql/mysql-config.cnf:/etc/mysql/conf.d/my.cnf - ./mysql/init:/docker-entrypoint-initdb.d # environment > 環境変数を指定します。Compose実行時に指定されるものにあたります。 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password TZ: 'Asia/Tokyo' # (例2)goを使う場合であれば以下のようになります。細かい詳細は解説にて説明、参照します。 go: build: context: ./go/ target: dev volumes: - ./go/src:/go/src:cached working_dir: /go/src ports: - 8080:8080 tty: true env_file: - ./go/.envMySQL以外の環境を構築する場合は3. へ飛んでください。
2. MySQL環境用のソースコード
MySQLの場合に初期DBを用意する必要があるので、設定ファイル(
mysql-config.cnf
)と初期テーブル(001-create-tables.sql
)のコードを書き、それを実行するコード(init-database.sh
)を書きます。### mysql-config.cnf ### [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [client] default-character-set=utf8mb4--- 001-create-tables.sql --- ---- drop ---- DROP TABLE IF EXISTS `first_table`; ---- create ---- create table IF not exists `first_table` ( `id` INT(20) AUTO_INCREMENT, `name` VARCHAR(20) NOT NULL, `created_at` Datetime DEFAULT NULL, `updated_at` Datetime DEFAULT NULL, PRIMARY KEY (`id`) ) DEFAULT CHARSET=utf8 COLLATE=utf8_bin;### init-database.sh ### #!/usr/bin/env bash #wait for the MySQL Server to come up #sleep 90s #run the setup script to create the DB and the schema in the DB mysql -u docker -pdocker test_database < "/docker-entrypoint-initdb.d/001-create-tables.sql"3. Docker起動
imageを指定していない場合(例2)、ビルドを行い、イメージを構築します。この時に参照されるのがよく巷で聞く
Dockerfile
です。今回はMySQL環境を爆速で仕上げるため省略しますが、解説にて説明します。今回は使用するイメージ(image)を
# docker-compose.yml version: '3' services: # (例1)ここに使用するサービスを書きます mysql: # image > コンテナ実行時の元になるイメージです。使うサービスのリポジトリ名だったりを書きます。 image: mysql:latestここで指定しているので、コンテナを作成し起動するところからで大丈夫です。
$ docker-compose up Creating network "backend_default" with the default driver Creating backend_mysql_1 ... done Creating backend_go_1 ... done ...上記コマンドで起動できたら、同じ階層にいる別ターミナルで以下のコマンドによって起動を確認してみてください。
$ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------ backend_go_1 bash Up 0.0.0.0:8080->8080/tcp backend_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcpここで起動し、サービス名が表示されていれば構築完了です!早いですね。
最後に以下コマンドによってコンテナとそれに関わるネットワークを停止します。
$ docker-compose down Stopping backend_mysql_1 ... done Stopping backend_go_1 ... done Removing backend_mysql_1 ... done Removing backend_go_1 ... done Removing network backend_default下記コマンドを叩いたターミナルが
$ docker-compose up Creating network "backend_default" with the default driver Creating backend_mysql_1 ... done Creating backend_go_1 ... done ... mysql_1 | 2020-11-30T14:42:11.612252Z 0 [System] [MY-013172] [Server] Received SHUTDOWN from user <via user signal>. Shutting down mysqld (Version: 8.0.22). go_1 | root@f1d2304a041d:/go/src# exit backend_go_1 exited with code 0 mysql_1 | 2020-11-30T14:42:12.324000Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.22) MySQL Community Server - GPL. backend_mysql_1 exited with code 0上記のようにexitできていれば、無事に停止しています。
これでDocker-composeによって仮想環境が構築できました。
4. MySQLでsample-tableを生成
仮想環境に入り、init-database.shを叩きます。
コンテナ内で起動中にコマンドを叩く場合は以下を参考にしてください。
# コンテナ起動 $ docker-compose up ... > 別ターミナルでdocker環境に入り、コマンド実行 # 権限付与 > ここでyml中に書いたvolumesで、パスを指定できる、と言うわけです $ docker-compose exec mysql bash -c "chmod 0775 docker-entrypoint-initdb.d/init-database.sh" # init-database.shを叩く $ docker-compose exec mysql bash -c "./docker-entrypoint-initdb.d/init-database.sh"解説
最初にアジェンダ書いてた時は、ここにがっつり解説書いていこうと思ったのですが、思ったより上で説明してしまった感が否めないです。書くことなくなってきた。。。まずいぞ。。。
そもそもdocker-composeって??
複数のコンテナを、簡易に管理し、実行するDockerアプリケーションのためのツールの1つです。
コマンドを1つ実行するだけで、docker-compose.ymlに定義した設定に基づいて環境を構築してくれるので、本当にいろんな使い方ができます!!、、(らしいですと言うのが本心。お恥ずかしい。)ymlで(よく)使用するオプション
image:
コンテナを実行する時に元となるイメージを指定します。
自分でDockerfile
を書いて実行する場合は、次のbuildオプションを使用します。
imageで使用する場合はビルドを行わず、$ docker-compose up
からで大丈夫です。build:
コンテナを実行する時に参照する
Dockerfile
を指定します。外部imageではなく、自分でDockerfile
を制作して使う場合はよく以下の構成で使用します。自分でいちから
Dockerfile
を書く、よりはリポジトリをクローンしてきて修正する、という扱い方が多いと思います。├── docker ├── Dockerfile └── docker-compose.yml# docker-compose.yml version: '3' services: hoge: # 同階層のため相対パスで指定 build: .ここで自分で
Dockerfile
を書いた場合はイメージを構築、すなわちビルドする必要があるので、初期起動前に以下コマンドを叩きます。$ docker-compose build db uses an image, skipping Building go ......
使用するcontextの指定や、dockerfile生成に関してはリファレンスも参照することをお勧めします。
https://docs.docker.jp/compose/compose-file.html#contextenvironment:
主に仮想環境上で用いる環境変数を指定します。
配列、もしくはDictionaryで定義できるので、$ docker-compose exec
で実行する際にアクセスできるようにAPI鍵や今回のようにMySQLのホストの値を記載します。env_file:
上記のenvironmentでは隠したい情報(自分はAPI鍵などはenv_fileを使用して
.gitignore
で隠す、という形をとることが多いです)の場合は、これで環境変数を記載したファイルを指定します。# docker-compose.yml version: '3' services: go: env_file: - ./go/.envなお
.env
ファイルは以下のように、変数 = 値
の形を取ります。# /go/.env DB_USER=user DB_PASSWORD=password DB_HOST=mysql:3306 DB_DATABASE=database DB_SOCKET=tcpvolumes:
自分のローカルファイルを環境上のファイルに割り当てるように指定することができます。
なお、実行手順にもコメントで書いた通り、ymlファイルがあるディレクトリを基準として相対パスは使用できます。
# docker-compose.yml version: '3' services: mysql: volumes: # ローカルファイル:仮想環境上のパス となります - ./mysql/data:/var/lib/mysql - ./mysql/mysql-config.cnf:/etc/mysql/conf.d/my.cnf - ./mysql/init:/docker-entrypoint-initdb.dports:
ホスト側とコンテナ側、両者のポートを指定することができます。
(なお、コンテナのみの指定も可能)# docker-compose.yml version: '3' services: go: ports: # ローカル側:仮想環境側 という感じです - 8080:8080 env_file: - ./go/.envdocker-composeで(よく)使うコマンド
説明するコマンドを以下に書いておきます。このほうがササッと使いやすいですよね。
$ docker-compose build $ docker-compose up $ docker-compose down $ docker-compose ps $ docker-compose start $ docker-compose stop$ docker-compose build
image:
を指定せずにbuild:
を使用する際に使います。これによりサービスをビルドします。
上記にも書いた通り、image:
を指定しない場合は、以下の$ docker-compose up
の前にビルドする必要があります。頻度の高いオプション --no-cache 構築時にイメージのキャッシュを使わない$ docker-compose up
仮想環境の立ち上げを行います。
実際はコンテナを構築、作成し、起動、アタッチまでを全てこのコマンドで行ってくれます。なお、
-d
をつけることによって、バックグラウンドで実行されるため、コンテナは起動し続ける状態を確保できます。CIなどを回す際にはこのオプションを使うと便利です。頻度の高いオプション -d バックグラウンドでコンテナを実行$ docker-compose down
起動している仮想環境を停止し、
$ docker-compose up
によって立ち上げたコンテナとネットワークを削除します。頻度の高いオプション --rmi all 全イメージを削除$ docker-compose ps
起動しているコンテナの一覧を表示できます。
$ docker-compose exec
起動している環境に対して、任意のコマンドを実行することができます。
なので、基本的に環境上でプロンプトを動かしたりする際はこれを使用します。# exec後の部分でどのコンテナを叩くか指定します $ docker-compose exec mysql bash -c "./docker-entrypoint-initdb.d/init-database.sh" # 以下コマンドのように扱えば、直接シェルを叩くこともできますが、volumesにてデフォルトの階層を指定する必要があります $ docker-compose exec hogehoge /bin/bash頻度の高いオプション -u 指定されたユーザによりコマンドを実行$ docker-compose start
既存のコンテナをサービスとして起動します。
$ docker-compose up
すれば起動するので、$ docker-compose down
ではなく(停止後コンテナを削除するため)、$ docker-compose stop
などで停止した場合に再び起動する時に使用します。$ docker-compose stop
稼働中のコンテナを停止しますが、
$ docker-compose down
とは違い、削除しません。 上記の$ docker-compose start
コマンドで、再起動できます。まとめ
docker-composeによる仮想環境構築でした。
運用だと、docker-compose.yml
に複数コンテナを組み合わせて一挙に立てて使用することになることがおおいですね。初日ということでしたが、自分が実装していた時にメモしていたことなどを噛み砕いて書いていくうちにどんどんボリュームが増しちゃいました。(笑)
ただ、語彙的にもとっつかみやすい文章になってるんじゃないでしょうか、、と思っています、、、(そうでなければ僕の語彙力の問題ですね、反省します)
皆様のますますのご活躍の少しでもお手伝いになれば、ということを書き締めたいと思います。
また質問等あればコメントにてご指摘などよろしくお願いします。
参考資料
- docker-compose オプション / リファレンス
https://docs.docker.jp/compose/compose-file.html
- docker-compose コマンド / リファレンス