ためすう

Dockerで、ホストOSからゲストOSへファイルを渡す

2020-12-10

やったこと

Dockerで、ホストOSからゲストOSへファイルを渡します。

確認環境

$ docker --version
Docker version 19.03.13, build 4484c46d9d

調査

ホストOS

$ docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS                    NAMES
257d4f38cf99        kalilinux/kali-rolling   "/bin/bash"         17 minutes ago      Up 17 minutes                                stoic_archimedes

ホストOS

$ docker cp flag.zip stoic_archimedes:/tmp/flag.zip

ゲストOS (コンテナ)

# ls /tmp/
flag.zip

ファイル転送することができました。

help はこちらになります。

$ docker cp help
"docker cp" requires exactly 2 arguments.
See 'docker cp --help'.

Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
	docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

起動中のDockerコンテナに接続する

2020-12-09

やったこと

起動中のDockerコンテナに、別タブから接続します。

確認環境

$ docker --version
Docker version 19.03.13, build 4484c46d9d

調査

使うイメージ

$ docker image ls | grep ruby | grep 2.5
ruby                                 2.5                                                                875fc97a76ff        2 weeks ago         843MB

コンテナ起動

タブ1

$ docker container run \
> -d \
> --name abc_container \
> -h host1 \
> -it \
> ruby:2.5 \
> /bin/bash
c5fd47fd970256a98c9fb9ed1c95f68e5129a3571e0a874b04a5ebbcb6d03b04

$ docker container ls -a | grep abc
c5fd47fd9702        ruby:2.5              "/bin/bash"              9 seconds ago       Up 8 seconds                                   abc_container

docker container exec で接続

タブ2

$ docker container exec -it abc_container bash
root@host1:/#

docker container --help より

exec Run a command in a running container

docker container attach で接続

タブ3

$ docker container attach abc_container
root@host1:/#

docker container --help より

attach Attach local standard input, output, and error streams to a running container

おまけ

status が Exitのコンテナを削除する

$ docker container ls -a | grep container | grep ruby
28b447b3bbdc        ruby:2.5              "/bin/bash"              4 minutes ago       Exited (0) About a minute ago                              abc_container
eb715bbaf632        ruby:2.5              "/bin/bash"              10 minutes ago      Exited (130) 10 minutes ago                                tmp01_container
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
28b447b3bbdc414b288d2766bf87f2f41349d694b817ec6a5f650428b92426ac
eb715bbaf6322a08b6f591090bc9d70498b4832a6b0301a4297bff3425575627

Total reclaimed space: 10B

最後に

docker container execdocker container attach は何が違うのでしょうか。

docker container attach から exit すると別タブも exit されることだけ確認できました。

exec はコマンドを渡せるので、bashで入ったり、zshで入ったり出来るようになるの良いのかもしれませんね。

Dockerコンテナの起動と削除

2020-12-08

やったこと

Dockerコンテナの起動と削除を実行してみます。

確認環境

$ docker --version
Docker version 19.03.13, build 4484c46d9d

調査

使うイメージ

$ docker image ls | grep ruby | grep 2.5
ruby                                 2.5                                                                875fc97a76ff        2 weeks ago         843MB

Dockerコンテナの起動と削除

タブ1

$ docker container run \
> --name tmp01_container \
> -h host1 \
> -it \
> ruby:2.5 \
> /bin/bash

root@host1:/#

docker container run --help より

Usage: docker container run [OPTIONS] IMAGE [COMMAND] [ARG…]

-i, –interactive Keep STDIN open even if not attached -t, –tty Allocate a pseudo-TTY –name string Assign a name to the container -h, –hostname string Container host name

タブ2

$ docker container ls | grep tmp
ed9b4c865708        ruby:2.5              "/bin/bash"              43 seconds ago      Up 41 seconds                                  tmp01_container

タブ1: exitします

root@host1:/# exit
exit

タブ2: コンテナの確認

$ docker container ls -a | grep tmp
ed9b4c865708        ruby:2.5              "/bin/bash"              2 minutes ago       Exited (0) 35 seconds ago                              tmp01_container

タブ2: コンテナの削除

$ docker container rm tmp01_container
tmp01_container

docker container --help より

rm Remove one or more containers

コンテナがなくなっていることが確認できます。

$ docker container ls -a | grep tmp

Dockerコンテナの終了時に即座に削除する

タブ1: コンテナの起動

$ docker container run \
--rm \
--name tmp01_container \
-h host1 \
-it \
ruby:2.5 \
/bin/bash

–rm Automatically remove the container when it exits

タブ2: コンテナが起動していることの確認

$ docker container ls -a | grep tmp
20b0403d390d        ruby:2.5              "/bin/bash"              15 seconds ago      Up 13 seconds                                  tmp01_container

タブ1: exit

root@host1:/# exit
exit

タブ2: コンテナが削除されていることの確認

$ docker container ls -a | grep tmp

最後に

チートシートみたいなものを作成したいですね。

GitHubのユーザー名を変えたら、GitHubと接続する時にパスワードが求められるようになったのでパスワード入力をスキップする

2020-12-06

やったこと

ふとGitHubのユーザー名を変えたくなったので、変更しました。

その時、GitHubからpull、GitHubにpushというGitHubと接続する時にパスワード入力が必要になってしまったので、パスワード入力をスキップさせます。

状況はこんな感じです。

  • GitHubのユーザー名を変えた
  • git の remote urlを git@github.com に設定し直した
  • pull、pullする時にパスワードの入力をスキップさせるようにした

調査

  • ユーザー名を before_user から after_user へ変更
  • 対象のリポジトリは my-repo

git の remote urlを git@github.com に設定し直す

変更前

$ git remote -v
origin	https://github.com/before_user/my-repo.git (fetch)
origin	https://github.com/before_user/my-repo.git (push)

変更後

$ git remote set-url origin git@github.com:after_user/my-repo.git

$ git remote -v
origin	git@github.com:after_user/my-repo.git (fetch)
origin	git@github.com:after_user/my-repo.git (push)

公開鍵をGitHubに登録する

GitHub のここら辺 (2020/12/05時点はここ)

Settings -> SSH and GPG keys の SSH key の方に公開鍵を設定します。

私は鍵ペアをすでに持っていたので、ここは実施しません。

ちなみに鍵の生成はこちらを参考に。

ssh-add に登録する

こちらを参考にします。

最後に

GitHubのユーザー名を変える前、httpsで接続する時パスワード入力を求められないのは何でなのか分かりませんでしたが、これはまたの機会に。

keep_if を使ってみる (Ruby)

2020-12-05

やったこと

keep_if を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

調査

$ irb
irb(main):001:0> a = %w[a b c d]
irb(main):002:0> a.keep_if {|v| v == 'a' || v == 'd'}
=> ["a", "d"]

block のなかで false 判定の要素を削除します。

ちなみに Array#select! Array#filter! でも同じことが出来ます。

irb(main):004:0> a = %w[a b c d]
irb(main):005:0> a.select! {|v| v == 'a' || v == 'd'}
=> ["a", "d"]
irb(main):006:0> a
=> ["a", "d"]
...
irb(main):009:0> a = %w[a b c d]
irb(main):010:0> a.filter! {|v| v == 'a' || v == 'd'}
=> ["a", "d"]
irb(main):011:0> a
=> ["a", "d"]

少し深ぼってみます

  # Equivalent to Set#keep_if, but returns nil if no changes were
  # made. Returns an enumerator if no block is given.
  def select!(&block)
    block or return enum_for(__method__) { size }
    n = size
    keep_if(&block)
    self if size != n
  end

  # Equivalent to Set#select!
  alias filter! select!

削除対象があるかどうかで、違いがあるようですね。

irb(main):012:0> a = %w[a b c d]
irb(main):013:0> a.filter! {|v| true }
=> nil
irb(main):014:0> a = %w[a b c d]
irb(main):015:0> a.keep_if {|v| true }
=> ["a", "b", "c", "d"]

コメントにある通り、削除対象がない場合に nil を返すのか self を返すのかで挙動が違うことが分かりました。

参考

SHOW PROFILE を使ってみる (MySQL)

2020-11-23

やったこと

SHOW PROFILE を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

プロファイリング対象のクエリを発行

mysql> show variables like "%profiling%"
    -> ;
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| have_profiling         | YES   |
| profiling              | OFF   |
| profiling_history_size | 15    |
+------------------------+-------+
3 rows in set (0.00 sec)
mysql> SELECT COUNT(*), created_at FROM bulk_test_org
    -> GROUP BY created_at
    -> ORDER BY COUNT(*) DESC
    -> LIMIT 10;
+----------+---------------------+
| COUNT(*) | created_at          |
+----------+---------------------+
|  8388608 | 2020-11-03 07:36:31 |
|  4194304 | 2020-11-03 07:34:32 |
|  2097152 | 2020-11-03 07:34:19 |
|  1048576 | 2020-11-03 07:34:12 |
|   524288 | 2020-11-03 07:34:07 |
|   262144 | 2020-11-03 07:34:03 |
|   131072 | 2020-11-03 07:34:01 |
|    65536 | 2020-11-03 07:34:00 |
|    32768 | 2020-11-03 07:33:59 |
|    16384 | 2020-11-03 07:33:58 |
+----------+---------------------+
10 rows in set (20.51 sec)

結果確認

mysql> SHOW PROFILES;
+----------+-------------+-------------------------------------------------------------------------------------------------------+
| Query_ID | Duration    | Query                                                                                                 |
+----------+-------------+-------------------------------------------------------------------------------------------------------+
|        1 |  0.00901450 | select count(*), created_at  from bulk_test group by created_at order by count(*) desc limit 3        |
|        2 |  0.00022100 | select * from bulk_test limit 3                                                                       |
|        3 |  0.00224100 | select * from bulk_test_org limit 3                                                                   |
|        4 |  0.00024325 | select * from bulk_test_org limit 3                                                                   |
|        5 |  0.00000075 | SELECT COUNT(*), created_at FROM bulk_test
GROUP BY created_at
ORDER BY COUNT(*) DESC
LIMIT 10     |
|        6 | 20.49806075 | SELECT COUNT(*), created_at FROM bulk_test_org
GROUP BY created_at
ORDER BY COUNT(*) DESC
LIMIT 10 |
+----------+-------------+-------------------------------------------------------------------------------------------------------+
6 rows in set, 1 warning (0.00 sec)

mysql> SHOW PROFILE;
+----------------------+-----------+
| Status               | Duration  |
+----------------------+-----------+
| starting             |  0.000060 |
| checking permissions |  0.000008 |
| Opening tables       |  0.000018 |
| init                 |  0.000020 |
| System lock          |  0.000009 |
| optimizing           |  0.000006 |
| statistics           |  0.000014 |
| preparing            |  0.000011 |
| Creating tmp table   |  0.000030 |
| Sorting result       |  0.000005 |
| executing            |  0.000003 |
| Sending data         | 20.480386 |
| Creating sort index  |  0.009607 |
| end                  |  0.000435 |
| removing tmp table   |  0.000028 |
| end                  |  0.000234 |
| query end            |  0.000015 |
| closing tables       |  0.001094 |
| freeing items        |  0.002723 |
| logging slow query   |  0.000010 |
| cleaning up          |  0.003346 |
+----------------------+-----------+
21 rows in set, 1 warning (0.01 sec)

参考

Amazon RDS から CSV を出力する方法 (MySQL)

2020-11-15

やったこと

Amazon RDS から CSV ファイルを出力します。

確認環境

※ RDS ではありませんが、コマンド実行元にCSVファイルを出力することで、 RDS でも同様に適用できます。

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

$ python --version
Python 2.6.6

調査

dump 対象となるテーブル

  • データベース名: test
  • テーブル名: test_table
mysql> select * from test_table;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  1 | 2    | 3    | 4    |
|  2 | 2    | 3    | 4    |
+----+------+------+------+
2 rows in set (0.00 sec)

mysqldump から CSV を生成するツールの準備

mysqldump の出力を CSV に変換するツールを使います。

git clone https://github.com/jamesmishra/mysqldump-to-csv.git

clone したファイルが下記のように配置します。

$ pwd
/home/vagrant/rds-mysql

$ chmod 755 mysqldump_to_csv.py

mysqldump から CSV を取得する

$ mysqldump -h127.0.0.1 -uroot -p test test_table -t --single-transaction | mysqldump_to_csv.py
Enter password:
1,2,3,4
2,2,3,4

標準出力で CSV データを得ることができました。

参考

Rails の cache ストレージで Active Record のオブジェクトを入れると事故りやすいと思う件

2020-11-14

やったこと

今回、Rails のキャッシュ機構で、Active Record のオブジェクトを保存すると “色々、大変なことがあるぞ” ということについて書いていきたいと思います。

ちなみにここでいうキャッシュ機構は、cache_store のことを指します。

それでは始めます。

確認環境

$ bundle exec rails --version
Rails 5.2.4.3

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.43, for osx10.13 (x86_64) using  EditLine wrapper

検証

準備

テーブル作成

CREATE TABLE `pencils` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `maker_name` varchar(50) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC

データ登録

INSERT INTO pencils VALUES (NULL, 'maker1', NOW(), NOW());

app/models/pencil.rb

class Pencil < ApplicationRecord
end

config/environments/development.rb (抜粋)

config.cache_store = :memory_store

cache に保存する

$ bundle exec rails c
Running via Spring preloader in process 65049
Loading development environment (Rails 5.2.4.3)
irb(main):001:0> p = Pencil.last
   (1.3ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Pencil Load (0.9ms)  SELECT  `pencils`.* FROM `pencils` ORDER BY `pencils`.`id` DESC LIMIT 1
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):002:0> Rails.cache.write('pencil1', p)
=> true
irb(main):003:0> Rails.cache.read('pencil1')
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):004:0> Rails.cache.read('pencil2')
=> nil

クラス名を変更する (そろそろ来るぞ!)

app/models/pencil.rb を下記のように変更します。

app/models/pencil2.rb

class Pencil2 < ApplicationRecord
end

保存してある cache を読み込む

irb(main):004:0> reload!
Reloading...
=> true
irb(main):005:0> Rails.cache.read('pencil1')
Traceback (most recent call last):
        1: from (irb):5
NameError (uninitialized constant Pencil)

はい。Pencil クラスがなくなってしまったので、ロードで失敗してしまいました。

これは、”色々、大変なことがあるぞ” の1つです。

対応方針

cacheストレージ使うときは、”安心なデータ” を入れる必要があります。

データ構造じゃない、value を入れるのが安心です。

例えば以下が “安心なデータ” と考えます。

  • 数値 例: 123
  • 文字列 例: あいう

配列、json、ハッシュもデータ構造が変わるときに対応を忘れないようにすれば、使って良いと思います。

Active Record のオブジェクトは、cacheストレージに保存しない方が安心です。

なぜかといえば、例えば、Railsのバージョンが変わったとき、ApplicationRecord の内部の構造も変わり

読み込めなくなる可能性があるからです。

はい。実際にありました。

ApplicationRecord は rails の本体に入っているため、この事態に気が付きにくいです。

また、システムが大きくなればなるほど、対応範囲の調査が大変になります(経験談)

まとめ

cacheストレージ使うときは、構造が変更される可能性が低いデータを入れましょう!!!

“安心なデータ” は、構造が変更される可能性が低いデータになります。

  • 数値 例: 123
  • 文字列 例: あいう

もちろん、配列、json、ハッシュなどを使ったほうがいいケースもあり、その場合は構造を変更するときに注意してください。

構造が変更される可能性があって、検知しづらいのが Active Record のオブジェクト だと思います。

Active Record のオブジェクト をcacheストレージに入れた方が良い場合があるかもしれませんが、

いまのところ、私は、Active Record のオブジェクト をcacheストレージに使わないほうが良いと思っています。

LOAD DATA INFILE を使う (MySQL)

2020-11-08

やったこと

データ登録するときに、外部ファイルを読み込む LOAD DATA INFILE を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

対象テーブル

移行前テーブル

mysql> select count(id) from bulk_test
    -> ;
+-----------+
| count(id) |
+-----------+
|  16777216 |
+-----------+
1 row in set (6.41 sec)

テーブル名は、bulk_test_1 bulk_test_2 とインクリメントします。

CREATE TABLE `bulk_test_1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `text_data` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `uniq_col` int(11) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_col` (`uniq_col`)
) ENGINE=InnoDB AUTO_INCREMENT=17366761 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
;

insert select で投入

mysql> INSERT INTO bulk_test_2 SELECT * FROM bulk_test;
Query OK, 16777216 rows affected (1 min 33.62 sec)
Records: 16777216  Duplicates: 0  Warnings: 0
mysql> SET FOREIGN_KEY_CHECKS = 0;
Query OK, 0 rows affected (0.05 sec)

mysql> SET UNIQUE_CHECKS = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET AUTOCOMMIT = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO bulk_test_1 SELECT * FROM bulk_test;
Query OK, 16777216 rows affected (1 min 49.48 sec)
Records: 16777216  Duplicates: 0  Warnings: 0

LOAD DATA LOCAL INFILE

投入する CSV 例

1,dummy text34,1,2020-11-03 07:31:48,2020-11-03 07:31:48
2,dummy text58,2,2020-11-03 07:33:16,2020-11-03 07:33:16
[vagrant@localhost ~]$ time mysql -u root -p test -e "LOAD DATA LOCAL INFILE '/tmp/bulk_test3.csv' INTO TABLE bulk_test_4 FIELDS TERMINATED BY ','"
Enter password:

real	1m40.355s
user	0m0.067s
sys	0m2.468s

LOAD DATA INFILE

投入する CSV 例

1,dummy text34,1,2020-11-03 07:31:48,2020-11-03 07:31:48
2,dummy text58,2,2020-11-03 07:33:16,2020-11-03 07:33:16
mysql> LOAD DATA INFILE '/tmp/bulk_test3.csv'
    ->   INTO TABLE bulk_test_3
    ->   FIELDS TERMINATED BY ','
    -> ;
Query OK, 16777216 rows affected (1 min 34.17 sec)
Records: 16777216  Deleted: 0  Skipped: 0  Warnings: 0

mysqldump で dump を流し込む

[vagrant@localhost ~]$ time mysqldump -uroot -p test bulk_test > /tmp/20201108.sql
Enter password:

real	0m47.587s
user	0m13.386s
sys	0m2.482s

投入前に別名に rename しておきます。

mysql> rename table bulk_test to bulk_test_org
    -> ;
Query OK, 0 rows affected (0.02 sec)
[vagrant@localhost ~]$ time mysql -u root -p test < /tmp/20201108.sql
Enter password:

real	2m39.268s
user	0m14.920s
sys	0m2.572s

まとめ

自分の PC で検証した限りだと、INSERT INTO ... SELECTLOAD DATA INFILE は、そんなに速度差はないみたいでした。

CSV 出力の時間が追加されるので、合計では INSERT INTO ... SELECT の方が短時間で出来るかもしれません。

参考

MySQL で CSV 出力する (MySQL)

2020-11-08

やったこと

MySQL のデータを CSV 出力する方法を2通りやってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

mysqldump で CSV を出力する

mysqldump -uroot -p --tab=/tmp --fields-terminated-by=, test bulk_test
[vagrant@localhost ~]$ ls -lh /tmp/
合計 985M
-rw-rw-r--  1 vagrant vagrant 1.6K 11月  7 11:15 2020 bulk_test.sql
-rw-rw-rw-  1 mysql   mysql   981M 11月  7 11:15 2020 bulk_test.txt

mysqldump --help より

-T, –tab=name Create tab-separated textfile for each table to given path. (Create .sql and .txt files.) NOTE: This only works if mysqldump is run on the same machine as the mysqld server.

–fields-escaped-by=name Fields in the output file are escaped by the given character.

mysql で CSV を出力する

mysql> SELECT * FROM bulk_test
    ->     INTO OUTFILE '/tmp/bulk_test2.csv'
    ->   FIELDS TERMINATED BY ','
    -> ENCLOSED BY '"'
    ->  ESCAPED BY '"'
    ->    LINES TERMINATED BY '\r\n';
Query OK, 16777216 rows affected (19.63 sec)
[vagrant@localhost ~]$ ls -lh /tmp/ | grep test2
-rw-rw-rw-  1 mysql   mysql   1.1G 11月  7 11:19 2020 bulk_test2.csv