深淵の闇から逃れる為のMariaDB Galera Cluster 5.5 入門(2 node編)

昔つかってたHatena Blog より転載

MariaDB Galera Cluster 5.5を試す

oujiにMySQLの深淵の闇(マスタ昇格とかマスタ昇格とか)から逃れたいと相談したところ MariaDB Galera Clusterが良いと教えてもらったのでやっとこさ試した記録を残します。

RDS最高なのですが、選定できないと思われるケースもあるのでこっちが利用できたらいいな。

MariaDB Galera Clusterって何

デュアルマスタ構成が可能なデータベースクラスタらしいです。最高。

今回はMySQL 5.5からMariaDBへの移行を考え MariaDB Galera Clusterは5.5で試しています。

MariaDB 5.5はMySQL 5.5と互換性があるので、アプリケーションやデータベースを変更せずmysqldumpで移行できると思います。

詳しい資料は

こことか

日本語で詳しいのはさくらインターネット研究所さんの記事だと思います。

資料を熟読してないのですが今回はインストールとMaster破損時の挙動確認をしてみたいとおもいます。

Galera Cluster 5.5 ServerとClientのインストール

今回はEC2(t2.micro)*2をVagrantで準備し、それぞれの内部IPを以下にして検証しています。OSはAmazon Linuxです。

役割 IPアドレス
Master 10.0.0.245
Secondary 10.0.0.67

参考資料

  • リポジトリの準備

Amazon Linux用のリポジトリは提供されていませんが、動RHEL向けのリポジトリを使いました

$ sudo cat /etc/yum.repos.d/mariadb.repo << EOF
# MariaDB 5.5 RedHat repository list - created 2014-09-20 02:09 UTC
# http://mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/5.5/rhel6-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
EOF
  • Galera Clusterのインストール
$ sudo yum install --enablerepo=mariadb MariaDB-Galera-server galera MariaDB-Client

Galera Clusterの事前準備

  • Master Nodeの設定
$ sudo vi /etc/my.cnf.d/server.cnf
[galera]
# Mandatory settings
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address=gcomm://
wsrep_node_address=10.0.0.245
wsrep_slave_threads=1
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
query_cache_size=0
  • Secondary Nodeの設定
$ sudo vi /etc/my.cnf.d/server.cnf
[galera]
# Mandatory settings
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.245
wsrep_node_address=10.0.0.67
wsrep_slave_threads=1
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
query_cache_size=0

mysqldの起動

Master Node

  • Master Nodeのmysqld起動
$ sudo service mysql start
  • サンプルデータベースの作成
$ mysql -u root -e "create database orenomaria"
$ mysql -u root -e "show databases"
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| orenomaria         |
| performance_schema |
| test               |
+--------------------+
$ mysql -u root -e "create table orenomaria.test( col varchar(10))"
$ mysql -u root -e "insert into orenomaria.test values('galera')"
$ mysql -u root -e "select * from orenomaria.test"
+--------+
| col    |
+--------+
| galera |
+--------+

Secondary Node

  • Secondary Nodeのmysqld起動

Master Nodeが起動済みでないと同期できず起動しない

$ sudo service mysql start
Starting MySQL.....SST in progress, setting sleep higher. SUCCESS!``
  • 同期確認
$ sudo mysql -u root -e "show databases"
$ mysql -u root -e "select * from orenomaria.test"
+--------+
| col    |
+--------+
| galera |
+--------+

Secondary Node 起動後の確認

Master Node

  • wsrep系パラメータの確認
$ mysql -u root -e "show status like  'wsrep_%'"
+------------------------------+--------------------------------------+
| Variable_name                | Value                                |
+------------------------------+--------------------------------------+
| wsrep_local_state_uuid       | 3f056461-4076-11e4-a2b8-6216b1337d22 |
| wsrep_protocol_version       | 5                                    |
| wsrep_last_committed         | 0                                    |
| wsrep_replicated             | 0                                    |
| wsrep_replicated_bytes       | 0                                    |
| wsrep_repl_keys              | 0                                    |
| wsrep_repl_keys_bytes        | 0                                    |
| wsrep_repl_data_bytes        | 0                                    |
| wsrep_repl_other_bytes       | 0                                    |
| wsrep_received               | 23                                   |
| wsrep_received_bytes         | 1821                                 |
| wsrep_local_commits          | 0                                    |
| wsrep_local_cert_failures    | 0                                    |
| wsrep_local_replays          | 0                                    |
| wsrep_local_send_queue       | 0                                    |
| wsrep_local_send_queue_avg   | 0.000000                             |
| wsrep_local_recv_queue       | 0                                    |
| wsrep_local_recv_queue_avg   | 0.043478                             |
| wsrep_local_cached_downto    | 18446744073709551615                 |
| wsrep_flow_control_paused_ns | 0                                    |
| wsrep_flow_control_paused    | 0.000000                             |
| wsrep_flow_control_sent      | 0                                    |
| wsrep_flow_control_recv      | 0                                    |
| wsrep_cert_deps_distance     | 0.000000                             |
| wsrep_apply_oooe             | 0.000000                             |
| wsrep_apply_oool             | 0.000000                             |
| wsrep_apply_window           | 0.000000                             |
| wsrep_commit_oooe            | 0.000000                             |
| wsrep_commit_oool            | 0.000000                             |
| wsrep_commit_window          | 0.000000                             |
| wsrep_local_state            | 4                                    |
| wsrep_local_state_comment    | Synced                               |
| wsrep_cert_index_size        | 0                                    |
| wsrep_causal_reads           | 0                                    |
| wsrep_cert_interval          | 0.000000                             |
| wsrep_incoming_addresses     | 10.0.0.245:3306,10.0.0.67:3306       |
| wsrep_cluster_conf_id        | 10                                   |
| wsrep_cluster_size           | 2                                    |
| wsrep_cluster_state_uuid     | 3f056461-4076-11e4-a2b8-6216b1337d22 |
| wsrep_cluster_status         | Primary                              |
| wsrep_connected              | ON                                   |
| wsrep_local_bf_aborts        | 0                                    |
| wsrep_local_index            | 0                                    |
| wsrep_provider_name          | Galera                               |
| wsrep_provider_vendor        | Codership Oy <info@codership.com>    |
| wsrep_provider_version       | 25.3.5(rXXXX)                        |
| wsrep_ready                  | ON                                   |
| wsrep_thread_count           | 2                                    |
+------------------------------+--------------------------------------+
  • Master Node に Anther Node が接続される時のログ

Cluster UUID [3f056461-4076-11e4-a2b8-6216b1337d22]に新しいメンバーがJoinしてwsrep_sst_rsyncが走っている

$ cat /dev/null > `hostname`.err
140920 12:47:18 [Note] WSREP: declaring d193920e-4078-11e4-9422-86a0381f90eb stable
140920 12:47:18 [Note] WSREP: Node 3f04f808-4076-11e4-a830-1bcc73dcf244 state prim
140920 12:47:18 [Note] WSREP: view(view_id(PRIM,3f04f808-4076-11e4-a830-1bcc73dcf244,10) memb {
        3f04f808-4076-11e4-a830-1bcc73dcf244,0
        d193920e-4078-11e4-9422-86a0381f90eb,0
} joined {
} left {
} partitioned {
})
140920 12:47:18 [Note] WSREP: New COMPONENT: primary = yes, bootstrap = no, my_idx = 0, memb_num = 2
140920 12:47:18 [Note] WSREP: STATE_EXCHANGE: sent state UUID: d1e0b305-4078-11e4-a8ed-16954c252dca
140920 12:47:18 [Note] WSREP: STATE EXCHANGE: sent state msg: d1e0b305-4078-11e4-a8ed-16954c252dca
140920 12:47:18 [Note] WSREP: STATE EXCHANGE: got state msg: d1e0b305-4078-11e4-a8ed-16954c252dca from 0 ()
140920 12:47:19 [Note] WSREP: STATE EXCHANGE: got state msg: d1e0b305-4078-11e4-a8ed-16954c252dca from 1 ()
140920 12:47:19 [Note] WSREP: Quorum results:
        version    = 3,
        component  = PRIMARY,
        conf_id    = 9,
        members    = 1/2 (joined/total),
        act_id     = 0,
        last_appl. = 0,
        protocols  = 0/5/3 (gcs/repl/appl),
        group UUID = 3f056461-4076-11e4-a2b8-6216b1337d22
140920 12:47:19 [Note] WSREP: Flow-control interval: [23, 23]
140920 12:47:19 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:0, view# 10: Primary, number of nodes: 2, my index: 0, protocol version 3
140920 12:47:19 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 12:47:19 [Note] WSREP: REPL Protocols: 5 (3, 1)
140920 12:47:19 [Note] WSREP: Service thread queue flushed.
140920 12:47:19 [Note] WSREP: Assign initial position for certification: 0, protocol version: 3
140920 12:47:19 [Note] WSREP: Service thread queue flushed.
140920 12:47:21 [Note] WSREP: Member 1.0 () requested state transfer from '*any*'. Selected 0.0 ()(SYNCED) as donor.
140920 12:47:21 [Note] WSREP: Shifting SYNCED -> DONOR/DESYNCED (TO: 0)
140920 12:47:21 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 12:47:21 [Note] WSREP: Running: 'wsrep_sst_rsync --role 'donor' --address '10.0.0.67:4444/rsync_sst' --auth '(null)' --socket '/var/lib/mysql/mysql.sock' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --gtid '3f056461-4076-11e4-a2b8-6216b1337d22:0''
140920 12:47:21 [Note] WSREP: sst_donor_thread signaled with 0
140920 12:47:21 [Note] WSREP: Flushing tables for SST...
140920 12:47:21 [Note] WSREP: Provider paused at 3f056461-4076-11e4-a2b8-6216b1337d22:0 (25)
140920 12:47:21 [Note] WSREP: Tables flushed.
140920 12:47:22 [Note] WSREP: resuming provider at 25
140920 12:47:22 [Note] WSREP: Provider resumed.
140920 12:47:22 [Note] WSREP: 0.0 (): State transfer to 1.0 () complete.
140920 12:47:22 [Note] WSREP: Shifting DONOR/DESYNCED -> JOINED (TO: 0)
140920 12:47:22 [Note] WSREP: Member 0.0 () synced with group.
140920 12:47:22 [Note] WSREP: Shifting JOINED -> SYNCED (TO: 0)
140920 12:47:22 [Note] WSREP: Synchronized with group, ready for connections
140920 12:47:22 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 12:47:25 [Note] WSREP: 1.0 (): State transfer from 0.0 () complete.
140920 12:47:25 [Note] WSREP: Member 1.0 () synced with group.

Secondary Node

  • Secondary Node起動後のwsrep系パラメータ
+------------------------------+--------------------------------------+
| Variable_name                | Value                                |
+------------------------------+--------------------------------------+
| wsrep_local_state_uuid       | 3f056461-4076-11e4-a2b8-6216b1337d22 |
| wsrep_protocol_version       | 5                                    |
| wsrep_last_committed         | 0                                    |
| wsrep_replicated             | 0                                    |
| wsrep_replicated_bytes       | 0                                    |
| wsrep_repl_keys              | 0                                    |
| wsrep_repl_keys_bytes        | 0                                    |
| wsrep_repl_data_bytes        | 0                                    |
| wsrep_repl_other_bytes       | 0                                    |
| wsrep_received               | 3                                    |
| wsrep_received_bytes         | 203                                  |
| wsrep_local_commits          | 0                                    |
| wsrep_local_cert_failures    | 0                                    |
| wsrep_local_replays          | 0                                    |
| wsrep_local_send_queue       | 0                                    |
| wsrep_local_send_queue_avg   | 0.000000                             |
| wsrep_local_recv_queue       | 0                                    |
| wsrep_local_recv_queue_avg   | 0.000000                             |
| wsrep_local_cached_downto    | 18446744073709551615                 |
| wsrep_flow_control_paused_ns | 0                                    |
| wsrep_flow_control_paused    | 0.000000                             |
| wsrep_flow_control_sent      | 0                                    |
| wsrep_flow_control_recv      | 0                                    |
| wsrep_cert_deps_distance     | 0.000000                             |
| wsrep_apply_oooe             | 0.000000                             |
| wsrep_apply_oool             | 0.000000                             |
| wsrep_apply_window           | 0.000000                             |
| wsrep_commit_oooe            | 0.000000                             |
| wsrep_commit_oool            | 0.000000                             |
| wsrep_commit_window          | 0.000000                             |
| wsrep_local_state            | 4                                    |
| wsrep_local_state_comment    | Synced                               |
| wsrep_cert_index_size        | 0                                    |
| wsrep_causal_reads           | 0                                    |
| wsrep_cert_interval          | 0.000000                             |
| wsrep_incoming_addresses     | 10.0.0.245:3306,10.0.0.67:3306       |
| wsrep_cluster_conf_id        | 10                                   |
| wsrep_cluster_size           | 2                                    |
| wsrep_cluster_state_uuid     | 3f056461-4076-11e4-a2b8-6216b1337d22 |
| wsrep_cluster_status         | Primary                              |
| wsrep_connected              | ON                                   |
| wsrep_local_bf_aborts        | 0                                    |
| wsrep_local_index            | 1                                    |
| wsrep_provider_name          | Galera                               |
| wsrep_provider_vendor        | Codership Oy <info@codership.com>    |
| wsrep_provider_version       | 25.3.5(rXXXX)                        |
| wsrep_ready                  | ON                                   |
| wsrep_thread_count           | 2                                    |
+------------------------------+--------------------------------------+
  • Secondary Node起動後のログ

wsrepによりMaster Nodeからデータを同期した後(今回の同期方式はデフォルトのwsrep_sst_rsync)

mysqldが起動している

$ cat /var/lib/mysql/`hostname`.err
140920 12:47:15 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
140920 12:47:15 mysqld_safe WSREP: Running position recovery with --log_error='/var/lib/mysql/wsrep_recovery.pkbACS' --pid-file='/var/lib/mysql/ip-10-0-0-67-recover.pid'
140920 12:47:18 mysqld_safe WSREP: Recovered position 00000000-0000-0000-0000-000000000000:-1
140920 12:47:18 [Note] WSREP: wsrep_start_position var submitted: '00000000-0000-0000-0000-000000000000:-1'
140920 12:47:18 [Note] WSREP: Read nil XID from storage engines, skipping position init
140920 12:47:18 [Note] WSREP: wsrep_load(): loading provider library '/usr/lib64/galera/libgalera_smm.so'
140920 12:47:18 [Note] WSREP: wsrep_load(): Galera 25.3.5(rXXXX) by Codership Oy <info@codership.com> loaded successfully.
140920 12:47:18 [Note] WSREP: CRC-32C: using hardware acceleration.
140920 12:47:18 [Warning] WSREP: Could not open saved state file for reading: /var/lib/mysql//grastate.dat
140920 12:47:18 [Note] WSREP: Found saved state: 00000000-0000-0000-0000-000000000000:-1
140920 12:47:18 [Note] WSREP: Passing config to GCS: base_host = 10.0.0.67; base_port = 4567; cert.log_conflicts = no; debug = no; evs.inactive_check_period = PT0.5S; evs.inactive_timeout = PT15S; evs.join_retrans_period = PT1S; evs.max_install_timeouts = 1; evs.send_window = 4; evs.stats_report_period = PT1M; evs.suspect_timeout = PT5S; evs.user_send_window = 2; evs.view_forget_timeout = PT24H; gcache.dir = /var/lib/mysql/; gcache.keep_pages_size = 0; gcache.mem_size = 0; gcache.name = /var/lib/mysql//galera.cache; gcache.page_size = 128M; gcache.size = 128M; gcs.fc_debug = 0; gcs.fc_factor = 1.0; gcs.fc_limit = 16; gcs.fc_master_slave = no; gcs.max_packet_size = 64500; gcs.max_throttle = 0.25; gcs.recv_q_hard_limit = 9223372036854775807; gcs.recv_q_soft_limit = 0.25; gcs.sync_donor = no; gmcast.segment = 0; gmcast.version = 0; pc.announce_timeout = PT3S; pc.checksum = false; pc.ignore_quorum = false; pc.ignore_sb = false; pc.npvo = false; pc.version = 0; pc.wait_prim = true; pc.wait_prim_timeout = P30S; pc.weight = 1; protonet.b
140920 12:47:18 [Note] WSREP: Service thread queue flushed.
140920 12:47:18 [Note] WSREP: Assign initial position for certification: -1, protocol version: -1
140920 12:47:18 [Note] WSREP: wsrep_sst_grab()
140920 12:47:18 [Note] WSREP: Start replication
140920 12:47:18 [Note] WSREP: Setting initial position to 00000000-0000-0000-0000-000000000000:-1
140920 12:47:18 [Note] WSREP: protonet asio version 0
140920 12:47:18 [Note] WSREP: Using CRC-32C (optimized) for message checksums.
140920 12:47:18 [Note] WSREP: backend: asio
140920 12:47:18 [Note] WSREP: GMCast version 0
140920 12:47:18 [Note] WSREP: (d193920e-4078-11e4-9422-86a0381f90eb, 'tcp://0.0.0.0:4567') listening at tcp://0.0.0.0:4567
140920 12:47:18 [Note] WSREP: (d193920e-4078-11e4-9422-86a0381f90eb, 'tcp://0.0.0.0:4567') multicast: , ttl: 1
140920 12:47:18 [Note] WSREP: EVS version 0
140920 12:47:18 [Note] WSREP: PC version 0
140920 12:47:18 [Note] WSREP: gcomm: connecting to group 'my_wsrep_cluster', peer '10.0.0.245:'
140920 12:47:18 [Note] WSREP: declaring 3f04f808-4076-11e4-a830-1bcc73dcf244 stable
140920 12:47:18 [Note] WSREP: Node 3f04f808-4076-11e4-a830-1bcc73dcf244 state prim
140920 12:47:18 [Note] WSREP: view(view_id(PRIM,3f04f808-4076-11e4-a830-1bcc73dcf244,10) memb {
        3f04f808-4076-11e4-a830-1bcc73dcf244,0
        d193920e-4078-11e4-9422-86a0381f90eb,0
} joined {
} left {
} partitioned {
})
140920 12:47:19 [Note] WSREP: gcomm: connected
140920 12:47:19 [Note] WSREP: Changing maximum packet size to 64500, resulting msg size: 32636
140920 12:47:19 [Note] WSREP: Shifting CLOSED -> OPEN (TO: 0)
140920 12:47:19 [Note] WSREP: Opened channel 'my_wsrep_cluster'
140920 12:47:19 [Note] WSREP: Waiting for SST to complete.
140920 12:47:19 [Note] WSREP: New COMPONENT: primary = yes, bootstrap = no, my_idx = 1, memb_num = 2
140920 12:47:19 [Note] WSREP: STATE EXCHANGE: Waiting for state UUID.
140920 12:47:19 [Note] WSREP: STATE EXCHANGE: sent state msg: d1e0b305-4078-11e4-a8ed-16954c252dca
140920 12:47:19 [Note] WSREP: STATE EXCHANGE: got state msg: d1e0b305-4078-11e4-a8ed-16954c252dca from 0 ()
140920 12:47:19 [Note] WSREP: STATE EXCHANGE: got state msg: d1e0b305-4078-11e4-a8ed-16954c252dca from 1 ()
140920 12:47:19 [Note] WSREP: Quorum results:
        version    = 3,
        component  = PRIMARY,
        conf_id    = 9,
        members    = 1/2 (joined/total),
        act_id     = 0,
        last_appl. = -1,
        protocols  = 0/5/3 (gcs/repl/appl),
        group UUID = 3f056461-4076-11e4-a2b8-6216b1337d22
140920 12:47:19 [Note] WSREP: Flow-control interval: [23, 23]
140920 12:47:19 [Note] WSREP: Shifting OPEN -> PRIMARY (TO: 0)
140920 12:47:19 [Note] WSREP: State transfer required:
        Group state: 3f056461-4076-11e4-a2b8-6216b1337d22:0
        Local state: 00000000-0000-0000-0000-000000000000:-1
140920 12:47:19 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:0, view# 10: Primary, number of nodes: 2, my index: 1, protocol version 3
140920 12:47:19 [Warning] WSREP: Gap in state sequence. Need state transfer.
140920 12:47:21 [Note] WSREP: Running: 'wsrep_sst_rsync --role 'joiner' --address '10.0.0.67' --auth '' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --parent '21327''
140920 12:47:21 [Note] WSREP: Prepared SST request: rsync|10.0.0.67:4444/rsync_sst
140920 12:47:21 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 12:47:21 [Note] WSREP: REPL Protocols: 5 (3, 1)
140920 12:47:21 [Note] WSREP: Service thread queue flushed.
140920 12:47:21 [Note] WSREP: Assign initial position for certification: 0, protocol version: 3
140920 12:47:21 [Note] WSREP: Service thread queue flushed.
140920 12:47:21 [Warning] WSREP: Failed to prepare for incremental state transfer: Local state UUID (00000000-0000-0000-0000-000000000000) does not match group state UUID (3f056461-4076-11e4-a2b8-6216b1337d22): 1 (Operation not permitted)
         at galera/src/replicator_str.cpp:prepare_for_IST():447. IST will be unavailable.
140920 12:47:21 [Note] WSREP: Member 1.0 () requested state transfer from '*any*'. Selected 0.0 ()(SYNCED) as donor.
140920 12:47:21 [Note] WSREP: Shifting PRIMARY -> JOINER (TO: 0)
140920 12:47:21 [Note] WSREP: Requesting state transfer: success, donor: 0
140920 12:47:22 [Note] WSREP: 0.0 (): State transfer to 1.0 () complete.
140920 12:47:22 [Note] WSREP: Member 0.0 () synced with group.
WSREP_SST: [INFO] Joiner cleanup. (20140920 12:47:23.479)
WSREP_SST: [INFO] Joiner cleanup done. (20140920 12:47:23.987)
140920 12:47:23 [Note] WSREP: SST complete, seqno: 0
140920 12:47:23 InnoDB: The InnoDB memory heap is disabled
140920 12:47:23 InnoDB: Mutexes and rw_locks use GCC atomic builtins
140920 12:47:23 InnoDB: Compressed tables use zlib 1.2.3
140920 12:47:23 InnoDB: Using Linux native AIO
140920 12:47:23 InnoDB: Initializing buffer pool, size = 128.0M
140920 12:47:24 InnoDB: Completed initialization of buffer pool
140920 12:47:24 InnoDB: highest supported file format is Barracuda.
InnoDB: Log scan progressed past the checkpoint lsn 1598129
140920 12:47:24  InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Restoring possible half-written data pages from the doublewrite
InnoDB: buffer...
InnoDB: Doing recovery: scanned up to log sequence number 1598283
140920 12:47:24  InnoDB: Waiting for the background threads to start
140920 12:47:25 Percona XtraDB (http://www.percona.com) 5.5.38-MariaDB-35.2 started; log sequence number 1598283
140920 12:47:25 [Note] Plugin 'FEEDBACK' is disabled.
140920 12:47:25 [Note] Server socket created on IP: '0.0.0.0'.
140920 12:47:25 [Note] Event Scheduler: Loaded 0 events
140920 12:47:25 [Note] WSREP: Signalling provider to continue.
140920 12:47:25 [Note] WSREP: SST received: 3f056461-4076-11e4-a2b8-6216b1337d22:0
140920 12:47:25 [Note] /usr/sbin/mysqld: ready for connections.
Version: '5.5.39-MariaDB-wsrep'  socket: '/var/lib/mysql/mysql.sock'  port: 3306  MariaDB Server, wsrep_25.10.r4014
140920 12:47:25 [Note] WSREP: 1.0 (): State transfer from 0.0 () complete.
140920 12:47:25 [Note] WSREP: Shifting JOINER -> JOINED (TO: 0)
140920 12:47:25 [Note] WSREP: Member 1.0 () synced with group.
140920 12:47:25 [Note] WSREP: Shifting JOINED -> SYNCED (TO: 0)
140920 12:47:25 [Note] WSREP: Synchronized with group, ready for connections
140920 12:47:25 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.

Multi Masterの確認

Secondary Nodeに同期されたサンプルデータベースのテーブルにデータを挿入します

Secondary Node

$ mysql -u root -e "insert into orenomaria.test values('galera')"
$ mysql -u root -e "select * from orenomaria.test"
+--------+
| col    |
+--------+
| galera |
| galera |
+--------+

Master Node

確認

$ mysql -u root -e "select * from orenomaria.test"
+--------+
| col    |
+--------+
| galera |
| galera |←最高
+--------+

非常に簡単にMulti Masterを構成することが出来ました。

Master Nodeを壊す

Master Node

Master Nodeのmysqldatadirを削除してプロセスをkillします

$ ps -ef|grep mysql |grep -v grep
root     23107     1  0 14:57 pts/1    00:00:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --pid-file=/var/lib/mysql/ip-10-0-0-245.pid
mysql    23338 23107  0 14:57 pts/1    00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --wsrep-provider=/usr/lib64/galera/libgalera_smm.so --log-error=/var/lib/mysql/ip-10-0-0-245.err --pid-file=/var/lib/mysql/ip-10-0-0-245.pid --wsrep_start_position=3f056461-4076-11e4-a2b8-6216b1337d22:14
$ sudo rm -rf /var/lib/mysql
$ ll mysql
ls: mysql にアクセスできません: そのようなファイルやディレクトリはありません
$ sudo kill -9 23107 23338
$ ps -ef|grep mysql
root     23552 22176  0 15:06 pts/0    00:00:00 grep mysql

死にました

Secondary Node

  • Master破損時のログ
$ cat /var/lib/mysql/`hostname`.err
140920 15:06:50 [Note] WSREP: (b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, 'tcp://0.0.0.0:4567') turning message relay requesting on, nonlive peers: tcp://10.0.0.245:4567
140920 15:06:51 [Note] WSREP: (b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, 'tcp://0.0.0.0:4567') reconnecting to 11c5fdd2-408b-11e4-8712-53af37cbe2b9 (tcp://10.0.0.245:4567), attempt 0
140920 15:06:54 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, OPERATIONAL, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:55 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:55 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:56 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:56 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:57 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:57 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:58 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:58 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:59 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:06:59 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:00 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:00 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:01 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:01 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:02 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:02 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:03 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:03 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:04 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) suspecting node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:04 [Note] WSREP: evs::proto(b9f4ed9f-408b-11e4-b85d-53a5bbcd3048, GATHER, view_id(REG,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4)) detected inactive node: 11c5fdd2-408b-11e4-8712-53af37cbe2b9
140920 15:07:05 [Note] WSREP: view(view_id(NON_PRIM,11c5fdd2-408b-11e4-8712-53af37cbe2b9,4) memb {
        b9f4ed9f-408b-11e4-b85d-53a5bbcd3048,0
} joined {
} left {
} partitioned {
        11c5fdd2-408b-11e4-8712-53af37cbe2b9,0
})
140920 15:07:05 [Note] WSREP: New COMPONENT: primary = no, bootstrap = no, my_idx = 0, memb_num = 1
140920 15:07:05 [Note] WSREP: Flow-control interval: [16, 16]
140920 15:07:05 [Note] WSREP: Received NON-PRIMARY.
140920 15:07:05 [Note] WSREP: Shifting SYNCED -> OPEN (TO: 19)
140920 15:07:05 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:19, view# -1: non-Primary, number of nodes: 1, my index: 0, protocol version 3
140920 15:07:05 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 15:07:05 [Note] WSREP: view(view_id(NON_PRIM,b9f4ed9f-408b-11e4-b85d-53a5bbcd3048,5) memb {
        b9f4ed9f-408b-11e4-b85d-53a5bbcd3048,0
} joined {
} left {
} partitioned {
        11c5fdd2-408b-11e4-8712-53af37cbe2b9,0
})
140920 15:07:05 [Note] WSREP: New COMPONENT: primary = no, bootstrap = no, my_idx = 0, memb_num = 1
140920 15:07:05 [Note] WSREP: Flow-control interval: [16, 16]
140920 15:07:05 [Note] WSREP: Received NON-PRIMARY.
140920 15:07:05 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:19, view# -1: non-Primary, number of nodes: 1, my index: 0, protocol version 3
140920 15:07:05 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
  • データ確認

Master Nodeが破損したためかクエリを受け付けなくなっています

$ mysql -u root -e "select count(*) from orenomaria.test"
ERROR 1047 (08S01) at line 1: WSREP has not yet prepared node for application use

復旧

Secondary Node

  • Secondary NodeのMaster化

Secondary Nodeのwsrep_cluster_addressをgcomm://に変更します

mysql -u root -e "set global wsrep_cluster_address='gcomm://'"

検索できるようになりました。データの挿入もOKっぽいです

$ mysql -u root -e "select count(*) from orenomaria.test"
+----------+
| count(*) |
+----------+
|       15 |
+----------+
$ mysql -u root -e "insert into orenomaria.test values('galera')"
$ mysql -u root -e "select count(*) from orenomaria.test"
+----------+
| count(*) |
+----------+
|       16 |
+----------+
  • ログの確認

Masterが停止しwsrep_cluster_addressを変更するまでのログを確認してみます。
WSREP: Quorum: No node with complete state: などとかでてますね。
AIX触っていた頃のVG Quorumを思い出します。

やつも確かQuorum50%以下になったらVGがinactiveになるので3本以上のDiskでVG構成すると良いよとかあったような(うるおぼえ)

Quorum型ってことは3node以上でGalera Clusterを作っておけば1台死んでもトランザクションは止まらないのかしら?

140920 15:10:00 [Note] WSREP: Stop replication
140920 15:10:00 [Note] WSREP: Closing send monitor...
140920 15:10:00 [Note] WSREP: Closed send monitor.
140920 15:10:00 [Note] WSREP: gcomm: terminating thread
140920 15:10:00 [Note] WSREP: gcomm: joining thread
140920 15:10:00 [Note] WSREP: gcomm: closing backend
140920 15:10:00 [Note] WSREP: view((empty))
140920 15:10:00 [Note] WSREP: Received self-leave message.
140920 15:10:00 [Note] WSREP: gcomm: closed
140920 15:10:00 [Note] WSREP: Flow-control interval: [0, 0]
140920 15:10:00 [Note] WSREP: Received SELF-LEAVE. Closing connection.
140920 15:10:00 [Note] WSREP: Shifting OPEN -> CLOSED (TO: 19)
140920 15:10:00 [Note] WSREP: RECV thread exiting 0: Success
140920 15:10:00 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:19, view# -1: non-Primary, number of nodes: 0, my index: -1, protocol version 3
140920 15:10:00 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 15:10:00 [Note] WSREP: applier thread exiting (code:0)
140920 15:10:00 [Note] WSREP: recv_thread() joined.
140920 15:10:00 [Note] WSREP: Closing replication queue.
140920 15:10:00 [Note] WSREP: Closing slave action queue.
140920 15:10:02 [Note] WSREP: rollbacker thread exiting
140920 15:10:02 [Note] WSREP: Start replication
140920 15:10:02 [Note] WSREP: Setting initial position to 3f056461-4076-11e4-a2b8-6216b1337d22:19
140920 15:10:02 [Note] WSREP: protonet asio version 0
140920 15:10:02 [Note] WSREP: Using CRC-32C (optimized) for message checksums.
140920 15:10:02 [Note] WSREP: backend: asio
140920 15:10:02 [Note] WSREP: GMCast version 0
140920 15:10:02 [Note] WSREP: (c2657b9f-408c-11e4-9081-e700f8703e0a, 'tcp://0.0.0.0:4567') listening at tcp://0.0.0.0:4567
140920 15:10:02 [Note] WSREP: (c2657b9f-408c-11e4-9081-e700f8703e0a, 'tcp://0.0.0.0:4567') multicast: , ttl: 1
140920 15:10:02 [Note] WSREP: EVS version 0
140920 15:10:02 [Note] WSREP: PC version 0
140920 15:10:02 [Note] WSREP: gcomm: connecting to group 'my_wsrep_cluster', peer ''
140920 15:10:02 [Note] WSREP: Node c2657b9f-408c-11e4-9081-e700f8703e0a state prim
140920 15:10:02 [Note] WSREP: view(view_id(PRIM,c2657b9f-408c-11e4-9081-e700f8703e0a,1) memb {
        c2657b9f-408c-11e4-9081-e700f8703e0a,0
} joined {
} left {
} partitioned {
})
140920 15:10:02 [Note] WSREP: gcomm: connected
140920 15:10:02 [Note] WSREP: Changing maximum packet size to 64500, resulting msg size: 32636
140920 15:10:02 [Note] WSREP: Shifting CLOSED -> OPEN (TO: 19)
140920 15:10:02 [Note] WSREP: Opened channel 'my_wsrep_cluster'
140920 15:10:02 [Note] WSREP: New COMPONENT: primary = yes, bootstrap = no, my_idx = 0, memb_num = 1
140920 15:10:02 [Note] WSREP: STATE_EXCHANGE: sent state UUID: c265d73a-408c-11e4-a52c-2a7ddfe1c27a
140920 15:10:02 [Note] WSREP: STATE EXCHANGE: sent state msg: c265d73a-408c-11e4-a52c-2a7ddfe1c27a
140920 15:10:02 [Note] WSREP: STATE EXCHANGE: got state msg: c265d73a-408c-11e4-a52c-2a7ddfe1c27a from 0 ()
140920 15:10:02 [Warning] WSREP: Quorum: No node with complete state:


        Version      : 3
        Flags        : 0x1
        Protocols    : 0 / 5 / 3
        State        : NON-PRIMARY
        Prim state   : SYNCED
        Prim UUID    : ba41f234-408b-11e4-b1fa-d37de606186c
        Prim  seqno  : 4
        First seqno  : -1
        Last  seqno  : 19
        Prim JOINED  : 2
        State UUID   : c265d73a-408c-11e4-a52c-2a7ddfe1c27a
        Group UUID   : 3f056461-4076-11e4-a2b8-6216b1337d22
        Name         : ''
        Incoming addr: '10.0.0.67:3306'

140920 15:10:02 [Note] WSREP: Partial re-merge of primary ba41f234-408b-11e4-b1fa-d37de606186c found: 1 of 2.
140920 15:10:02 [Note] WSREP: Quorum results:
        version    = 3,
        component  = PRIMARY,
        conf_id    = 4,
        members    = 1/1 (joined/total),
        act_id     = 19,
        last_appl. = 0,
        protocols  = 0/5/3 (gcs/repl/appl),
        group UUID = 3f056461-4076-11e4-a2b8-6216b1337d22
140920 15:10:02 [Note] WSREP: Flow-control interval: [16, 16]
140920 15:10:02 [Note] WSREP: Restored state OPEN -> SYNCED (19)
140920 15:10:02 [Note] WSREP: New cluster view: global state: 3f056461-4076-11e4-a2b8-6216b1337d22:19, view# 5: Primary, number of nodes: 1, my index: 0, protocol version 3
140920 15:10:02 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 15:10:02 [Note] WSREP: REPL Protocols: 5 (3, 1)
140920 15:10:02 [Note] WSREP: Service thread queue flushed.
140920 15:10:02 [Note] WSREP: Assign initial position for certification: 19, protocol version: 3
140920 15:10:02 [Note] WSREP: Service thread queue flushed.
140920 15:10:02 [Note] WSREP: Synchronized with group, ready for connections
140920 15:10:02 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.

破損したMaster NodeをSecondaryとして起動

  • Master NodeのSecondary化

wsrep_cluster_addressを新Master(10.0.0.67)に変更します

$ vi /etc/my.cnf.d/server.conf
# Mandatory settings
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.67
wsrep_node_address=10.0.0.245
wsrep_slave_threads=1
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
query_cache_size=0
  • 起動と確認

SSTにより新Master(10.0.0.67)からデータが転送されてSuccessが返ります。

データも同期され、新しいデータの挿入もOKです

$ sudo service mysql start
Starting MySQL.......SST in progress, setting sleep higher. SUCCESS!
mysql -u root -e "select count(*) from orenomaria.test"
+----------+
| count(*) |
+----------+
|       16 |
+----------+
$ mysql -u root -e "insert into orenomaria.test values('galera')"
$ mysql -u root -e "select count(*) from orenomaria.test"
+----------+
| count(*) |
+----------+
|       17 |
+----------+

新Master Nodeでの確認

  • データの確認

新Secondaryで挿入したデータは反映されてます。最高です

$ mysql -u root -e "select count(*) from orenomaria.test"
+----------+
| count(*) |
+----------+
|       17 |
+----------+
  • ログの確認

新しいメンバー通知が走りwsrep_sst_rsyncが行われています。

完了後にmemberとしてJOINしています。

140920 15:31:37 [Note] WSREP: REPL Protocols: 5 (3, 1)
140920 15:31:37 [Note] WSREP: Service thread queue flushed.
140920 15:31:37 [Note] WSREP: Assign initial position for certification: 20, protocol version: 3
140920 15:31:37 [Note] WSREP: Service thread queue flushed.
140920 15:31:37 [Warning] WSREP: Releasing seqno 20 before 21 was assigned.
140920 15:31:39 [Note] WSREP: Member 1.0 () requested state transfer from '*any*'. Selected 0.0 ()(SYNCED) as donor.
140920 15:31:39 [Note] WSREP: Shifting SYNCED -> DONOR/DESYNCED (TO: 20)
140920 15:31:39 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 15:31:39 [Note] WSREP: Running: 'wsrep_sst_rsync --role 'donor' --address '10.0.0.245:4444/rsync_sst' --auth '(null)' --socket '/var/lib/mysql/mysql.sock' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --gtid '3f056461-4076-11e4-a2b8-6216b1337d22:20''
140920 15:31:39 [Note] WSREP: sst_donor_thread signaled with 0
140920 15:31:39 [Note] WSREP: Flushing tables for SST...
140920 15:31:39 [Note] WSREP: Provider paused at 3f056461-4076-11e4-a2b8-6216b1337d22:20 (16)
140920 15:31:39 [Note] WSREP: Tables flushed.
140920 15:31:41 [Note] WSREP: resuming provider at 16
140920 15:31:41 [Note] WSREP: Provider resumed.
140920 15:31:41 [Note] WSREP: 0.0 (): State transfer to 1.0 () complete.
140920 15:31:41 [Note] WSREP: Shifting DONOR/DESYNCED -> JOINED (TO: 20)
140920 15:31:41 [Note] WSREP: Member 0.0 () synced with group.
140920 15:31:41 [Note] WSREP: Shifting JOINED -> SYNCED (TO: 20)
140920 15:31:41 [Note] WSREP: Synchronized with group, ready for connections
140920 15:31:41 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140920 15:31:43 [Note] WSREP: 1.0 (): State transfer from 0.0 () complete.
140920 15:31:43 [Note] WSREP: Member 1.0 () synced with group.

以上でMariaDB Galera Clusterのデュアルマスタ機能を試すことが出来ました。

引き続き検証を続けたいと思います。

最高。

続きを読む

Terraform で AWS環境を実運用する上で困ったことと、その対処

忘備録的なもの。
2017年2月時点、Terraformは0.8.6.

操作用AWSアカウントの認証情報の扱い

困ったこと

ネットの参考情報だと、awsの認証情報(credentials)を直接書くサンプルが非常に多かった。
しかし、tfファイルを書き換える運用だと、いつか間違えてcommit & pushしてインシデントになりそう。

Terraformを最初に動かすためのユーザーの認証情報は、その性質上大きな権限を持っていることが多いと思うので、慎重になりたい。

解決策

AWS-CLIのNamed Profile使おう。

$ aws configure --profile my-profile-name
AWS Access Key ID [None]: xxxxxxxxxx
AWS Secret Access Key [None]: xxxxxxxxxx
Default region name [None]: ap-northeast-1
Default output format [None]: 

事前にこうして設定しておけば、認証情報は$home/.aws/credentialsに名前付きで入る。

スクリプトからはこう参照する。これならばcommitしてしまっても問題ない。

example1.tf
provider "aws" {
  profile = "my-profile-name"
}

あとは、上記の設定手順をREADME.md なんかに書いて、KEYIDとSECRET自体はいつも通り正しく管理してあげればいい。

.tfstateファイルの扱い

困ったこと

.tfstateファイルを紛失してしまうと、作成したインスタンスを、Terraformから管理できなくなってしまうので、最重要ファイルだ。
かといって、gitにcommitするルールだと、commit忘れが怖いし、その際pullし忘れるとつらい。
一方、手動であちこち引き回しても同じことで、別の開発者が古いstateに基づいて、重複したインスタンスを立ててしまうかもしれない…

解決策

Backendの、remote state機能使おう。

ちゃんと公式ドキュメントある。 https://www.terraform.io/docs/state/remote/s3.html

こんな感じ。profileも指定できるので、そちらにregionを書いておけば省略できる。

$ terraform remote config 
    -backend=s3 
    -backend-config="bucket=my-tfstate-store-name-at-s3" 
    -backend-config="key=hogehoge/terraform.tfstate" 
    -backend-config="profile=my-profile-name"

これを実行した時点で、存在していたterraform.tfstateは、./.terraform/terraform.tfstate に移動されるようだ。

あとは自動でtfstateファイルをアップロード、ダウンロードしてくれる。
指定するバケットはacl=privateにしておくこと!!
あと、上記リンクでは、S3のversioningとかもつけておくことを勧めている。(私はやらなかった)

S3以外にも、いろいろ手段は用意されているようだ。

環境ごとのvariableの扱い

困ったこと

ステージングと本番、みたいに分けようと思って、環境ごとにvariableで設定値を与えられるようにしたけど、
-var オプションで引数に全部指定するの辛い

解決策

共通の設定ならば、terraform.tfvars という名前のファイルに書いておけば、指定しないでも勝手に読み込んでくれるとのこと。
https://www.terraform.io/intro/getting-started/variables.html#from-a-file

環境ごとに違う変数は、-var-fileオプションを使ってスイッチするのがよさそうだ。

$ terraform plan -var-file staging.tfvars
$ terraform plan -var-file production.tfvars

的な。

varは後ろに指定したものが有効(上書きされる)とのことなので、上手に使えば強いはず。

Packerで作ったAMIでEC2インスタンスを生成したい

困ったこと

auto-scalingを考えたときに、元となるAMIをちゃんと運用したい。
(注意、私はAWSのAutoScaling をよくわかっていないかも。)

そこで、Packerでイミュータブルなイメージ作ったらすごーいはず。
イミュータブルということは、イメージにDB接続情報がないといけない気がする。
よって、Terraformで先にRDS立てないといけないけど、そのTerraform側でAMIを使いたいからこういう話になっているわけで…
循環参照してしまう。

そもそも、AutoScaling配下のインスタンスを入れ替える際に全インスタンスを落とさないようにする、というのがなかなか厳しいようだ。
AutoScalingとの相性は、改善まち、という感じか。

参考: http://qiita.com/minamijoyo/items/e32eaeebc906b7e77ef8

準解決策1 null_resource

最終的にはうまくいかなかったが、最初に試した方法

null_resourceというものがある。
https://www.terraform.io/docs/provisioners/null_resource.html

何もしない代わりに、自身の状態(=変更が起きるトリガー)を定義するtriggers属性と、provisionerを設定できるリソースだ。
これを使って、

example2.tf
variable "ami_name_prefix" {}

resource "null_resource" "packer" {
  triggers {
    db_host = "${aws_db_instance.hogehoge.address}"
  }

  provisioner "local-exec" {
    command = <<EOS
packer build 
  -var 'DB_HOST=${aws_db_instance.hogehoge.address}' 
  -var 'AMI_NAME_PREFIX=${ami_name_prefix}' 
  packer.json"
EOS
  }
}

data "aws_ami" "packer_ami" {
  most_recent = true
  name_regex = "^${var.ami_name_prefix}.*"
  owners = ["self"]
  depends_on = ["null_resource.packer"]
}

resource "aws_instance" "hoge" {
  ami_id = "${data.aws_ami.packer_ami.id}"
  ...
}

とこんな感じ。

しかし、Terraform 0.8.6だと、triggersの中にinterpolationを混ぜると、問答無用でcomputed扱いになってしまうようで、
この記述では毎回AMIが作成されてしまって、差分のみ実行というTerraformの観点からは、使えなかった。

準解決策2 イミュータブルを諦める

オートスケーリング自体はこのパターンでは不可能か。AMI化を再度行う必要があると思う。

ほぼ完成品のAMIを組み立てておいて、aws_instanceのprovisionerで最後の仕上げをする。
Dockerなんかは、環境変数で外部情報を読み込ませる、なんてことをするらしいので、この手法に踏み切った。

// .envが欠けた状態でAMIをつくる
$ packer packer.json
example3.tf
data "aws_ami" "packer_ami" {
  most_recent = true
  name_regex = "^packer-ami-.*$"
  owners = ["self"]
  depends_on = ["null_resource.packer"]
}

data "template_file" "envfile" {
  # template = "${file(./env.tpl)}" などとした方が望ましいが例示のため。
  template = <<EOS
export DB_HOST="${db_host}"
export DB_PORT="${db_port}"
....
EOS

  vars {
    db_host = "${aws_db_instance.main.address}"
    db_port = "${aws_db_instance.main.port}"
  }
}

resource "aws_instance" "ec2" {
  ami_id = "${data.aws_ami.packer_ami.id}"
  ...

  # envファイルをuploadする。envファイルはdirenvとかdotenvとかで読み込まれるようにしておく。
  provisioner "file" {
    content = "${data.template_file.envfile.rendered}"
    destination = "/home/ec2-user/.env"
  }

  # 上で入れた環境変数を使ってサービスを立ち上げる。
  provisioner "remote-exec" {
    inline = ["sudo service unicorn start"]
  }

  # provisionerは、実行した環境からssh(のgolang実装。sshコマンドではない)で接続することになる。
  # 当然security_groupとvpc_internet_gatewayが適切に設定されている必要がある。
  connection {
    type = "ssh"
    ...
  }
}

tfファイルを構造化したい

困ったこと

コピペや反復は悪だし、再利用性も下がる。

COUNT

COUNT = nで配列のように同じタイプのリソースを複数作れる。
それぞれ微妙に違う値を書きたいなら、

COUNT = 2
ATTR = "${element(list("a", "b", "c", "d"), count.index)}"

などとできる。
listは変数化するとなおよい。

module

複数のリソースにまたがっての反復パターンなら、module化してしまうとよい。
module自体の書き方はここでは説明しないが、

./modules/my_module/variables.tf
./modules/my_module/main.tf
./modules/my_module/output.tf

を作り、

example4.tf
module "use_of_my_module" {
  source = "./my_module"
  var1 = ...
  var2 = ...
}

と書くことで使用。

$ terraform get

で、モジュールをterraformに読み込ませる準備をさせる。(./.terraform/modules/ にsym-linkが作成されるようだ)

様々に公開されているmoduleもあるようなので、むしろ自分でresourceを書かないほうが良いのかもしれない。

その他

また何かあれば書く。

続きを読む

AWS X-RayでSolrの検索処理をトレースする

AWS X-Ray(プレビュー版)を使ってSolrの検索処理をトレースしてみる。
Solrへリクエストを投げるところからトレースを開始する。

システム構成

サーバ

使用するインスタンスは2台。
Amazon Linuxを使う。

  • Webサーバ(express/node.js – t2.micro)
  • 検索エンジン(Solr/Jetty – t2.medium)

サービスマップ

こんな感じになる。
localhostと書いてあるけどSolrへのリクエスト。

servicemap.png

サーバの構築

共通

セキュリティグループ

ローカルマシン→Webサーバの80番ポートは開けておく。
Webサーバ→検索エンジンの80番ポートも開けておく。
ローカルマシン→Solrにアクセスするならそちらの8983番ポートも開けておく。

IAMロール/インスタンスプロファイル

後述のX-RayデーモンがAWS X-Rayサービスにデータを送るので書き込み権限を設定してあげる。
インスタンスに関連付けるIAMロールを適当に作り、AWSマネージドポリシーからAWSXrayWriteOnlyAccessポリシーをアタッチする。
ポリシーの内容はこんな感じ。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords"

            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

X-Rayデーモン

X-Rayデーモンのインストール。
もう起動してる。気が早い。

$ curl -sL https://s3.amazonaws.com/aws-xray-assets.us-east-1/xray-daemon/aws-xray-daemon-1.x.rpm -o /home/ec2-user/xray.rpm
$ sudo yum install -y xray.rpm
$ sudo initctl status xray
xray start/running, process 2607

インストール内容はこんな感じ。
xrayデーモンはGolang製の模様。
systemdの設定もあるようだがデフォルトだとUpstartで起動している。

$ rpm -ql xray
/etc/amazon/xray/cfg.yaml
/etc/init/xray.conf
/etc/systemd/system/xray.service
/usr/bin/xray

設定はデフォルトのままでいじらず。

Webサーバ

検索エンジンのIPアドレスをxray-solrとしてを/etc/hostsに登録した。

node.js

epelからインストールすると0.10系になるので最新版になる方法でインストールする。
レポジトリを登録。

$ curl -sL https://rpm.nodesource.com/setup_7.x | sudo bash -

## Installing the NodeSource Node.js 7.x repo...


## Inspecting system...

+ rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release || rpm -q --whatprovides sl-release
+ uname -m

## Confirming "el7-x86_64" is supported...

+ curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub_7.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'

## Downloading release setup RPM...

+ mktemp
+ curl -sL -o '/tmp/tmp.OHBteTsWWV' 'https://rpm.nodesource.com/pub_7.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'

## Installing release setup RPM...

+ rpm -i --nosignature --force '/tmp/tmp.OHBteTsWWV'

## Cleaning up...

+ rm -f '/tmp/tmp.OHBteTsWWV'

## Checking for existing installations...

+ rpm -qa 'node|npm' | grep -v nodesource

## Run `yum install -y nodejs` (as root) to install Node.js 7.x and npm.
## You may also need development tools to build native addons:
##   `yum install -y gcc-c++ make`

v7.6.0をインストール。

$ sudo yum install -y nodejs
$ node -v
v7.6.0

yarn

npmpackage.jsonを管理する気があるのかないのか分からないのでyarnをインストールする。

$ sudo curl -sL https://dl.yarnpkg.com/rpm/yarn.repo -o /etc/yum.repos.d/yarn.repo
$ sudo yum install -y yarn

express

/home/ec2-user/web以下にプロジェクトを作った。

$ yarn init -y
$ yarn add express aws-sdk aws-xray-sdk

ここまででpackage.jsonはこんな感じ。

{
  "name": "web",
  "version": "1.0.0",
  "main": "index.js",
  "repository": {},
  "license": "MIT",
  "dependencies": {
    "aws-sdk": "^2.12.0",
    "aws-xray-sdk": "^1.0.4-beta",
    "express": "^4.14.1"
  }
}

リクエストが来たら検索エンジンで検索するWebアプリを書く。

/home/ec2-user/web/index.js
const express = require('express');
const app = express();

const AWSXRay = require('aws-xray-sdk');
AWSXRay.config([AWSXRay.plugins.EC2]);

const http = require('http');
AWSXRay.captureHTTPs(http);


app.use(AWSXRay.express.openSegment('xray-web'));

app.get('/', (req, res) => {
  const options = {
    hostname: 'xray-solr',
    port: 8983,
    path: '/solr/xray/select?indent=on&q=*:*&wt=json'
  };
  http.get(options, (response) => {
    let data = '';
    response.on('data', (chunk) => data += chunk);
    response.on('end', () => {
      res.send(data);
    });
  });
});

app.use(AWSXRay.express.closeSegment());


app.listen(80);

EC2プラグインを指定するとAWS X-Rayに渡される情報にインスタンス情報が付加される。
今のところインスタンスIDとAZのみ。

AWSXRay.config([AWSXRay.plugins.EC2]);

サンプリングルールの設定も出来る模様。

AWSXRay.setSamplingRules('sampling-rules.json');

公式ドキュメントに記載されている形式だとエラーで動かないので下記のような形式にすると動いた。
ただDefaultの設定しか反映されないので、どこか間違っていそう。
毎秒fixed_target数のリクエストをトレース、その後はrateの割合でトレース。

sampling-rules.json
{
  "rules": {
    "1": {
      "service_name": "*",
      "http_method": "*",
      "url_path": "*",
      "fixed_target": 10,
      "rate": 0.1
    },
    "Default": {
      "fixed_target": 10,
      "rate": 0.1
    }
  }
}

express用のミドルウェアが用意されているので使わせて頂く。
これでWebサーバに来たリクエストをトレース出来る。

app.use(AWSXRay.express.openSegment('xray-web'));
  :
app.use(AWSXRay.express.closeSegment());

httphttpsを使った通信をトレースすることが出来る。
内部的にhttp.requestを置き換えている。
これで検索エンジンとの通信をトレース出来る。

AWSXRay.captureHTTPs(http);

http.get('http://.../')のようにURLを直接渡すとhttp://localhost/へのリクエストが無限ループになって死ぬ。
接続先が未知の場合はlocalhostに設定されるのはnode.jsの仕様らしい。誰が喜ぶんだこれ。
今のところoptionsはハッシュで渡されることが期待されている。

const options = {
  hostname: 'xray-solr',
  port: 8983,
  path: '/solr/xray/select?indent=on&q=*:*&wt=json'
};
http.get(options, (response) => {...});

検索エンジン

自分のIPアドレスをxray-solrとしてを/etc/hostsに登録した。

node.js

SolrへのプロキシとしてWebサーバと同様に node.js + express をインストールした。
コードはWebサーバとほぼ同じだが、検索エンジンのクライアントは色々ある前提でDynamic Naming Modeにしてみた。
defaultNameを与えないとエラーになった。

AWSXRay.middleware.enableDynamicNaming();
app.use(AWSXRay.express.openSegment('defaultName'));

java

1.8以上が必要らしい。

$ sudo yum install java-1.8.0-openjdk
$ sudo alternatives --config java

2 プログラムがあり 'java' を提供します。

  選択       コマンド
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:2

Solr

/home/ec2-user/solr/以下にSolrをインストールした。

$ curl -sL 'http://ftp.jaist.ac.jp/pub/apache/lucene/solr/6.4.1/solr-6.4.1.tgz' -o /home/ec2-user/solr.tgz
$ tar zxf solr.tgz
$ mv solr-6.4.1 solr

デフォルトの8983番ポートで起動。

$ cd solr
$ ./bin/solr start

コアを新規作成する。
solrコマンドのヘルプは./bin/solr create_core -helpなどと打てば見られる。

$ ./bin/solr create_core -c xray -d sample_techproducts_configs

Servlet Filter

GradleやMavenでのビルド方法がよく分からないので、とりあえず必要そうなJARを持ってきてぶち込んだ。

$ curl -sL 'http://central.maven.org/maven2/com/amazonaws/aws-xray-recorder-sdk-core/1.0.4-beta/aws-xray-recorder-sdk-core-1.0.4-beta.jar' -o aws-xray-recorder-sdk-core-1.0.4-beta.jar
$ curl -sL 'http://central.maven.org/maven2/com/amazonaws/aws-java-sdk-core/1.11.91/aws-java-sdk-core-1.11.91.jar' -o aws-java-sdk-core-1.11.91.jar
$ curl -sL 'http://central.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.8.6/jackson-databind-2.8.6.jar' -o jackson-databind-2.8.6.jar
$ curl -sL 'http://central.maven.org/maven2/com/fasterxml/jackson/dataformat/jackson-dataformat-cbor/2.8.6/jackson-dataformat-cbor-2.8.6.jar' -o jackson-dataformat-cbor-2.8.6.jar
$ curl -sL 'http://central.maven.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar' -o commons-logging-1.2.jar
$ curl -sL 'http://central.maven.org/maven2/joda-time/joda-time/2.9.7/joda-time-2.9.7.jar' -o joda-time-2.9.7.jar
$ curl -sL 'http://central.maven.org/maven2/software/amazon/ion/ion-java/1.0.2/ion-java-1.0.2.jar' -o ion-java-1.0.2.jar
$ curl -sL 'http://central.maven.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.8.6/jackson-annotations-2.8.6.jar' -o jackson-annotations-2.8.6.jar
$ curl -sL 'http://central.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.8.6/jackson-core-2.8.6.jar' -o jackson-core-2.8.6.jar
$ mv *.jar /home/ec2-user/solr/server/solr-webapp/webapp/WEB-INF/lib/

/home/ec2-user/solr/server/solr-webapp/webapp/WEB-INF/web.xmlAWSXRayServletFilterの設定を追加。
SolrRequestFilterの手前に入れた。

web.xml
<filter>
  <filter-name>AWSXRayServletFilter</filter-name>
  <filter-class>com.amazonaws.xray.javax.servlet.AWSXRayServletFilter</filter-class>
  <init-param>
    <param-name>fixedName</param-name>
    <param-value>xray-solr</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>AWSXRayServletFilter</filter-name>
  <url-pattern>/solr/*</url-pattern>
</filter-mapping>

リクエストとトレースの確認

node.jsとSolrを起動してxray-webにリクエストを投げてみる。
トレース結果は下記のようになった。

trace.png

時間がずれるとこううまく表示されないのが気持ち悪い。
なにが悪いのか・・・

trace2.png

続きを読む

[AWS] EC2 で OpenVPN サーバを起動する

OpenVPN サーバ作る必要があったので、作業メモ

OpenVPN サーバのローンチ

MarketPlace に AMI があるので、それから作るのが簡単です。
Launch on EC2: OpenVPN Access Server
上のリンクからローンチすればセキュリティグループも作ってくれるけど、自分でセキュリティグループ作りたい場合は、インバウンドで最低限以下を許可するように設定。

Connection Method Protocol Port Range Source (IP or Group)
SSH tcp 22 Anywhere or IP Range
HTTPS tcp 443 Anywhere
tcp 943 Anywhere
udp 1194 Anywhere

起動したら EIP 割り当てておけば、後々便利。

しばらくすると EC2 起動してくるので、ssh 接続してイニシャルセッティングする。

$ ssh -i ~/.ssh/openvpn.pem openvpnas@xxx.xxx.xxx.xxx
Welcome to OpenVPN Access Server Appliance 2.1.4b

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

user-data not available: HTTP Error 404: Not Found: util/ec2:12,util/ec2:7,python2.7/urllib2:154,python2.7/urllib2:437,python2.7/urllib2:550,python2.7/urllib2:475,python2.7/urllib2:409,python2.7/urllib2:558 (urllib2.HTTPError)

          OpenVPN Access Server
          Initial Configuration Tool
------------------------------------------------------
OpenVPN Access Server End User License Agreement (OpenVPN-AS EULA)
 ()

Please enter 'yes' to indicate your agreement [no]: yes

Once you provide a few initial configuration settings,
OpenVPN Access Server can be configured by accessing
its Admin Web UI using your Web browser.

Will this be the primary Access Server node?
(enter 'no' to configure as a backup or standby node)
> Press ENTER for default [yes]: yes

Please specify the network interface and IP address to be
used by the Admin Web UI:
(1) all interfaces: 0.0.0.0
(2) eth0: 192.168.0.1
Please enter the option number from the list above (1-2).
> Press Enter for default [2]: 2

Please specify the port number for the Admin Web UI.
> Press ENTER for default [943]: 943

Please specify the TCP port number for the OpenVPN Daemon
> Press ENTER for default [443]: 443

Should client traffic be routed by default through the VPN?
> Press ENTER for default [no]: no

Should client DNS traffic be routed by default through the VPN?
> Press ENTER for default [no]: yes

Use local authentication via internal DB?
> Press ENTER for default [yes]: yes

Private subnets detected: ['19.168.0.0/16']

Should private subnets be accessible to clients by default?
> Press ENTER for EC2 default [yes]: yes

To initially login to the Admin Web UI, you must use a
username and password that successfully authenticates you
with the host UNIX system (you can later modify the settings
so that RADIUS or LDAP is used for authentication instead).

You can login to the Admin Web UI as "openvpn" or specify
a different user account to use for this purpose.

Do you wish to login to the Admin UI as "openvpn"?
> Press ENTER for default [yes]: yes 
(admin ユーザ名変えたい時は、ここで no 答えて、次の質問で変えたい名前を入力)

Type the password for the 'openvpn' account:
Confirm the password for the 'openvpn' account:

> Please specify your OpenVPN-AS license key (or leave blank to specify later): 


Initializing OpenVPN...
Adding new user login...
useradd -s /sbin/nologin "openvpn"
Writing as configuration file...
Perform sa init...
Wiping any previous userdb...
Creating default profile...
Modifying default profile...
Adding new user to userdb...
Modifying new user as superuser in userdb...
Getting hostname...
Hostname: xxx.xxx.xxx.xxx
Preparing web certificates...
Getting web user account...
Adding web group account...
Adding web group...
Adjusting license directory ownership...
Initializing confdb...
Generating init scripts...
Generating PAM config...
Generating init scripts auto command...
Starting openvpnas...

NOTE: Your system clock must be correct for OpenVPN Access Server
to perform correctly.  Please ensure that your time and date
are correct on this system.

Initial Configuration Complete!

You can now continue configuring OpenVPN Access Server by
directing your Web browser to this URL:

https://xxx.xxx.xxx.xxx:943/admin
Login as "openvpn" with the same password used to authenticate
to this UNIX host.

During normal operation, OpenVPN AS can be accessed via these URLs:
Admin  UI: https://xxx.xxx.xxx.xxx:943/admin
Client UI: https://xxx.xxx.xxx.xxx:943/

See the Release Notes for this release at:
   http://www.openvpn.net/access-server/rn/openvpn_as_2_1_4b.html

openvpnas@openvpnas2:~$ sudo apt-get update && sudo apt-get upgrade

これで https://{public IP Address}:943/admin にアクセスすることで、ローンチした OpenVPN サーバの管理パネルに接続できます。
(自己署名証明書による HTTPS 接続なので、セキュリティ例外追加しないと接続できないので注意)

OpenVPN サーバで接続用のユーザ作成

https://{public IP Address}:943/admin から管理パネルにログインして [User Management] の [User Permitions] でユーザを追加。
[Allow Auto-login] にチェックしておくと、ユーザの設定が簡略化されるのでオススメ。

他の設定とか諸々はドキュメント参照。

OpenVPN クライアントのインストールと設定

Tunnelblick あたりをインストールしておけばいいです。
上の設定で VPN 接続用のユーザを作成してあるのであれば https://{public IP Address}:943/ にログイン。
Screen Shot 2017-02-22 at 12.54.03.png

すると、以下のような画面が表示されるので [Yourself (autologin profile)] ってリンクから ovpn ファイルをダウンロード。
Screen Shot 2017-02-22 at 12.54.13.png

ダウンロードした ovpn ファイルをダブルクリックすると Tunnelblick が起動して、よしなに設定してくれます。

iPhone とかでも VPN 接続したい場合は iPhoneやiPad (iOS) でOpenVPNを使ってみよう! を参考にしてみましょう。

MarketPlace にある AMI 使うと簡単にできて便利すなぁ。

参考 URL

続きを読む

AWS re:Invent 2016 Security Follow Up に行ってきた

こんにちは、ひろかずです。
AWS re:Invent 2016 Security Follow Up に行ってきたので、一筆書きます。

  • 例によって、リアルタイム執筆ですので、誤字、脱字、表記ゆれはご容赦ください。

スタートが19:00のせいか、半分くらいの人出からのスタートです。
最終的には8割位埋まっていました。

[AWSセッション]

「セキュリティ新サービス紹介① AWS Organizations」

桐山 隼人
アマゾン ウェブ サービス ジャパン株式会社
技術本部 レディネスソリューション部
セキュリティソリューションアーキテクト

AWS Organizations は…

  • 現在プレビュー申し込みできます。

サービス概要

複数のAWSアカウントの一元管理

  • アプリケーションや環境、チームのような単位でAWSアカウントをグループ管理
  • グループポリシーを適用

AWSアカウント管理の自動化

  • コンソール、SDK、CLIでの管理操作
  • 管理操作をCloudTrailでロギング

請求の簡素化

  • 複数アカウントの一括請求(Consolidated Billing)
  • Consolidated Billing ファミリーの自動移行

主要なコンセプト(用語)

組織

  • 一元管理可能な複数のAWSアカウントのセット

AWSアカウント

  • 管理される最小単位
  • IAMを用いてAWSリソースへアクセス

マスターアカウント

  • 組織内の他アカウントの作成招待削除ができる特別なアカウント
  • 組織内コントロールポリシーの適用
  • 組織における支払いアカウント

組織単位(OU)

  • 後述

管理ルート

  • 後述

利用の流れ

1. 組織の作成

  • マスターアカウントは支払いアカウントになるので、慎重に決めましょう

2. AWSアカウントの自動作成

  • マスターアカウントからの自動作成

入力要素

  • メアド(必須)
  • アカウント名(必須)
  • IAMロール名(必須)
  • BillingへのIAMユーザアクセス(任意設定)

モードは二つ

  • FullControlモード
  • Billingモード

FullControlモード

  • Billingモードを含む
  • Billingからの昇格には、全てのAWSのアカウントからの同意が必要

Billingモード
– 課金状況を見れる

3. 既存AWSのアカウントの組織への招待

  • 招待されたアカウントは、了解/拒否を選べる。(デフォルト拒否)
  • 参加した瞬間にポリシーが適用される

4. AWSアカウントのグループ化

AWSアカウントは組織単位(OU)に所属できる

  • アプリケーションとか環境とかチームとか
  • 階層になるので、最上位が 管理ルート になる。

5. 組織Controlポリシー

  • 適用対象は、組織全体、OU、AWSアカウント
  • ポリシーは、階層構造に基いて継承される。
  • ローカル管理者からは上書きできない
  • IAMと記述方式は一緒
  • IAMユーザ、IAMロールの権限は、 組織ControlポリシーとIAMポリシーの最大公約数

どんなことができるの?

例:サービスControlポリシー

  • 呼び出しを許可するAPIを定義(ホワイト、ブラックリスト)
  • 業界で認可されたサービスのみを許可する(本番環境)
  • 開発環境はちょっと緩くすることもアリ

「セキュリティ新サービス紹介② AWS Shield」

荒木 靖宏
アマゾン ウェブ サービス ジャパン株式会社
技術本部 レディネスソリューション部 部長

AWS Shieldとは?

  • AWSが管理(マネージ)するDDoSプロテクションサービス

攻撃の種別

  • ボリューム攻撃(UDP反射型:全体の6割を占める:SSDP反射型が一番多い、その他NTPやDNS、SNMPも)
  • ステート管理攻撃(SYNフラッドとか:2割くらい)
  • アプリケーションレイヤー(F5攻撃とか、slowles:15%くらい)

DDoSを緩和する取り組み

難しさはどこに?

  • 複雑な前準備(オペレータやISPとの調整、スクレイピングサービスへのトラフィック誘導)
  • 事前の帯域確保(工事時間がかかる)
  • アプリの見直し(仕様書起こすとか大変)

難しいのはマニュアルでの対応
AWSでスクレイピングしても、ユーザーからはレイテンシ向上にしか見えない。お金は?
AWSの解

  • ありきたり攻撃の自動保護(サービス差別化につながらない部分を持つ)
  • 可用性の確保

DDoS防御はAWSにあらかじめ組み込まれている

  • 各リージョン
  • 常時オン(外部Routingなし)
  • SYB/ACK、UDPフラッド反射型
  • 追加費用無し

お客様の声

  • 大きなDDoS対策
  • 見える化
  • アプリレイヤー守ってよ

AWSの解

  • 無料サービス(Standard)
  • 有料サービス(Advanced)

Standard

BLACK Watch

  • 決定論的フィルタリング
  • 不正な形式のTCPパケットはドロップする
  • インライン検査のスコアリングに基づくトラフィック優先順位付け(優先順位の低い攻撃トラフィックを優先的に破棄)

キャパ追加

  • Routingポリシーで内部的に分散

検知と緩和を継続(ヒューリスティック異常検出:通常のベースラインから異常を知る)

  • リクエスト数/s
  • 送信元IP
  • URL
  • UserAgent

Advanced

AWS内で完結する
常時オン
レイテンシは最小限
手頃な価格
対応しているのは以下サービス(EC2は入っていない。TCPのみ。UDPは使えない。)

  • ALB
  • CLB
  • CF
  • Route53

L7は、AWS WAFの利用

  • Standard:セルフサービス(自分でカスタムシグネチャを書く)
  • Standard/Advanced:DDoSエキスパートチームからのアドバイス
  • Advanced:積極的DRT(DDoS Response Team)が関与する

DRT(DDoS Response チーム)はなにやるの?

  • 常時モニタがDRTを呼び出す
  • トリアージ
  • DRTチームがルールを書いて提供

レポート

  • ニアリアルタイムで、パケット情報見れる(サンプル取得:全部じゃない)

DRTへのコンタクト

  • 第一報はサポート経由でしてね(エンタープライズサポート)

費用

  • Standard:アドバイスに基づくスケール費用は 有料 :月額課金なし
  • Advance:アドバイスに基づくスケール費用は無料:$3000/月:一年継続コミット

[パートナーセッション]

「スポンサー側から見た AWS re:Invent の醍醐味と感じたセキュリティの流れ」

南原 正樹様
トレンドマイクロ株式会社 パートナービジネス推進本部
アライアンスパートナーグループ 担当課長代理

Trend MicroとAWS re:invent

ダイヤモンドスポンサー
並び順は一番最後(アルファベット順)
出展したのは、DeepSecurityとDSaaS
スポンサーアクティビティ(セッションは各1時間:youtubeに上ってるよ)

  • AWSに特化したセッション
  • CISOに聞いてみよう(コンプライアンス、レギュレーションの話)
  • ブースは、DevSecOpsを安全な空の旅をモチーフに表現

スポンサーで感じたこと(日本 vs 北米)

  • 日本は、捕まると長いので敬遠される。アンケート欠かされる。製品売ろう。
  • 北米は、コンタクト獲得と製品が分業。スキャンしたらノベルティたくさんくれる。コンプライアンス準拠。ビジネス視点。

来場者

  • 北米はカジュアルに自分のアイデアを語ってくる。Tシャツ大好き。

re:inventで見た気になるセキュリティの流れ

Security Automation

セキュリティオペレーションの自動化を目指すことでセキュリティ運用コストの低減と対応の迅速化を目指す。
セキュリティはインフラ構築と分離しない。併せて考える。

  • AWS WAFとLambdaとの連携(ブラックIPとか)
  • CloudTrailのIPを検査とか
  • GitHubに公開されてるよ!

Security for ServerLess

サーバレスを主体として組み上げた環境でのセキュリティのポイント

  • IAMを中心とした権限管理
  • 許可されたトラフィックの精査(サニタイズからログの精査まで)
  • 例えば、CF/WAF前、API Gateway中、Lambda後ろ、最後はDB

最後に

AWSはエコシステムとマーケットチャンス
セキュリティもニューノーマルからスーパーパワー
積極的に新しい取り組みに挑戦しよう(日本の方がセキュリティに深く真面目に考えている)

「攻撃傾向と企業が取るべきセキュリティ施策 in Las Vegas」

佐藤 裕貴様
三井物産セキュアディレクション株式会社
Alert Logic事業部 マネージャー

大橋 和正様
三井物産セキュアディレクション株式会社
プロフェッショナルサービス事業部
アカウントマネージャー

昨今の攻撃傾向

海外(Verizonレポート)

  • WebAPに対する攻撃が増えてきている。(前年度4倍)

日本(IPAレポート)

  • インターネットバンキング、標的型、ランサム、Webサービス不正ログイン、サイト改ざん

事前対策

セキュアコーディング
脆弱性診断
アクセス分離(SG、NACL、ELB)、2要素認証(IAMロール、MFA)

  • 監視ポイントが限定できて運用負荷を軽減

ログの収集

  • 平時のログ傾向を把握する
  • ログは不変であること(削除/改ざんの防止)
  • 以下の状態であること(アクセス性,検索性,継続モニタ)

セキュリティ運用の最適化

  • L1:SG,IAM
  • L2:IDS/WAF,脆弱性スキャン
  • L3:CloudTrail,SIEM

[エンドユーザー様セッション]

「ユーザーからみたre:Invent のこれまでと今後」

宮崎 幸恵 様
株式会社リクルートテクノロジーズ
ITソリューション統括部 インフラソリューション2部 RAFTEL2G

  • 2012年からre:invent皆勤賞
  • 一年おきにセキュリティ系サービスのビッグウェーブが来る傾向
  • Organizationsキター
  • 現地は熱い!体感!感じろ!

バッテリー切れでライブでかけませんでした!すみません!

「AWS re:Invent の衝撃 ~Large Scale Violence & Security Culture Shock~」

大橋 衛様
KDDI株式会社
技術統括本部 プラットフォーム開発本部
アジャイル開発センター フレームワークG 課長補佐

  • 現地は熱い!体感!感じろ!飯はマズイ!
  • 予防、検知、回復の話
  • 日本は、予防に重点を置いている制約型.検知が最小限で、回復は手動と尻すぼみ
  • 海外は、予防はsoso、検知にかなり力を入れて、回復は自動化とパワーのかけ方が日本と逆

バッテリー切れでライブでかけませんでした!すみません!

雑感

モバイルバッテリーがそろそろ必要なようです。

続きを読む

IAMロールを持たないEC2に対して、一括でIAMロールを付与する

はじめに

IAMロール割り当て方針

  • Nameタグをもとに、IAMロールの名前を決めた

    • Name「hoge01」 => IAMロール「ec2_hoge」
    • profile も同じ
  • IAMロールに割り当てるポリシーは空
    • これは別途やっていくので、ここでは触れない

環境

  • AWS CLI

    • aws-cli/1.11.46 Python/2.7.10 Darwin/15.6.0 botocore/1.5.9
  • jq
    • 1.5

実行

アクセスキーは、環境変数又はawsコマンドのプロファイルに適宜設定しておく

$ ./attach_iam_role_to_all_ec2.sh

実行後にもう一度実行して何も出なければ、全て完了している

やっていること

  • インスタンスプロファイルが未設定のインスタンスを抽出
  • IAMロールが無ければ作成
  • インスタンスプロファイルが無ければ作成
  • インスタンスプロファイルにIAMロールが設定されていなければ登録
  • インスタンスプロファイルが出来上がるのを待つ (wait 直後にassociate-iam-instance-profileを行うと失敗する事があるので追加でsleep)
  • EC2にインスタンスプロファイルをアタッチする

https://gist.github.com/tkimura/8c4f709df633d09052bcc89aabf01c17

attach_iam_role_to_all_ec2.sh
#!/bin/bash

AWS_DEFAULT_REGION=ap-northeast-1
export AWS_DEFAULT_REGION

list_roles=`mktemp`
list_profiles=`mktemp`

aws iam list-roles| jq -r '.Roles[]' > $list_roles
aws iam list-instance-profiles | jq -r '.InstanceProfiles[]' > $list_profiles

cat <<_JSON > ec2_role_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
_JSON


aws ec2 describe-instances |\
jq -r '.Reservations[] | .Instances[] | select(.IamInstanceProfile==null) | [.InstanceId, (.Tags[] | select(.Key=="Name")).Value] | @tsv' |\
while read instanceid name;do
  sleep=0
  role_name=ec2_${name%%[0-9]*}

  exist_role=`cat $list_roles | jq -r "select(.RoleName==\"$role_name\")"`
  if [ -z "$exist_role" ]; then
    # create role
    role_arn=`aws iam create-role --role-name $role_name --assume-role-policy-document file://ec2_role_policy.json | jq -r '.Role | .Arn'`
    sleep=10
  else
    role_arn=`echo $exist_role | jq -r '.Arn'`
  fi

  exist_profile=`cat $list_profiles | jq -r "select(.InstanceProfileName==\"$role_name\")"`
  if [ -z "$exist_profile" ]; then
    # create profile
    profile_arn=`aws iam create-instance-profile --instance-profile-name $role_name --instance-profile-name $role_name | jq -r '.Role | .Arn'`
    sleep=10
  else
    profile_arn=`echo $exist_profile | jq -r '.Arn'`
  fi

  exist_role_in_profile=`aws iam list-instance-profiles-for-role --role-name $role_name | jq -r '.InstanceProfiles[]'`
  if [ -z "$exist_role_in_profile" ]; then
    # add role to profile
    aws iam add-role-to-instance-profile --role-name $role_name --instance-profile-name $role_name
    sleep=10
  fi

  # waiting until create profile
  aws iam wait instance-profile-exists --instance-profile-name $role_name
  sleep $sleep

  # attach role to EC2
  echo "$name : $instanceid , $role_name , $role_arn , $profile_arn"
  if ! aws ec2 associate-iam-instance-profile --instance-id $instanceid --iam-instance-profile Name=$role_name; then
    exit 1
  fi

done

rm -f $list_roles $list_profiles

AWS CLIでの操作メモ

インスタンスプロファイルが未設定のインスタンスのID一覧を得る

aws --region ap-northeast-1 ec2 describe-instances |\
jq -r '.Reservations[] | .Instances[] | select(.IamInstanceProfile==null) | .InstanceId'

インスタンスプロファイルが未設定のインスタンスのIDとNameタグ一覧を得る (JSON)

aws --region ap-northeast-1 ec2 describe-instances | jq -r '.Reservations[] | .Instances[] | select(.IamInstanceProfile==null) | {"InstanceId": .InstanceId, "Name": (.Tags[] | select(.Key=="Name")).Value}'

インスタンスプロファイルが未設定のインスタンスのIDとNameタグ一覧を得る (TSV)

aws --region ap-northeast-1 ec2 describe-instances | jq -r '.Reservations[] | .Instances[] | select(.IamInstanceProfile==null) | [.InstanceId, (.Tags[] | select(.Key=="Name")).Value] | @tsv'

IAMロール一覧を得る

aws iam list-roles

インスタンスプロファイル一覧を得る

aws iam list-instance-profiles

名前でロールの有無を確認

aws iam list-roles| jq -r '.Roles[] | select(.RoleName=="ロール名")'

IAMロールを作る

aws iam create-role --role-name $role_name --assume-role-policy-document file://ec2_role_policy.json

インスタンスプロファイルを作る

aws iam create-instance-profile --instance-profile-name $role_name --instance-profile-name $role_name

インスタンスプロファイルにIAMロールを登録する

aws iam add-role-to-instance-profile --role-name $role_name --instance-profile-name $role_name

EC2にインスタンスプロファイルをアタッチする

aws ec2 associate-iam-instance-profile --instance-id $instanceid --iam-instance-profile Name=$role_name

続きを読む

CloudWatch エージェントは SSM エージェントと呼ばれる別のエージェントに移行されましたとさ

ログは外に出しますよね

EC2 の Windows インスタンスでイベントログやアプリケーションログを CloudWatch Logs に転送して運用しています。
ログを見るためにいちいちインスタンスに入る必要もないし、必要なログがインスタンス外にあることで AutoScaling でインスタンスが勝手に破棄されても大丈夫という設計です。

これまで

ログ転送設定を C:Program FilesAmazonEc2ConfigServiceSettingAWS.EC2.Windows.CloudWatch.json に書いて EC2Config サービスを再起動することでインスタンスごとにログ転送の仕方を設定していました。
これを CodeDeploy の Hook スクリプトで行い、アプリケーションの配置と同時にログが指定した(アプリケーションごとの)ロググループにロストすることなく集めることができていました。
ちなみに Windows Server 2012 R2 です。

EC2Config ログ転送やめるってよ

あるとき、こんな記事を見つけました。
Windows ServerのCloudWatch LogsをSSMで行う | Developers.IO

従来のEC2 Configの設定を行っても、CloudWatch Logsへログの転送が行われないので注意してください。

え?まじで?
試しに新しい AMI で試してみましたがやっぱりログは転送されません。
今後は SSM で設定しなくちゃいけないのかと調べましたが、どうもやりたいことができません。
やりたいことって何よ?を書くと長くなりそうなんで書きませんが、とにかく、インスタンスに置いたローカルファイルからログ転送設定をしたいんです。

どうも SSMAgent です

EC2Config バージョン履歴的なドキュメント を確認したらなんだかローカル設定ファイルを使えそうな雰囲気が・・・

ローカル設定ファイル (AWS.EC2.Windows.CloudWatch.json) を使用して、インスタンスで CloudWatch の統合を有効にしている場合、SSM エージェントと連携して動作するようファイルを設定する必要があります。詳細については、「Windows Server 2016 インスタンスで CloudWatch 統合用のローカル設定ファイルを使用する」を参照してください。

お?・・・え? Windows Server 2016 だけなん?

結局、サポートに問い合わせて回答をいただきました。

CloudWatch Logsの設定方法

これまでと同様にローカルのファイルをお使い頂けますが、CloudWatchもしくはCloudwath Logsの設定変更が必要な場合には、下記の手順を実施頂くようお願いいたします。

  1. Amazonssmagent サービスを停止します。
    コマンドプロンプトで net stop amazonssmagent を実行することで停止できます。
  2. C:Program FilesAmazonSSMPluginsawsCloudWatchAWS.EC2.Windows.CloudWatch.json を削除いたします。
  3. C:Program FilesAmazonEc2ConfigServiceSettingAWS.EC2.Windows.CloudWatch.json の内容を編集いたします。
  4. Amazomssmagent サービスを開始します。
    コマンドプロンプトで net start amazonssmagent を実行することで開始できます。

新しい設定が反映されますと、AWS.EC2.Windows.CloudWatch.json の内容が C:Program FilesAmazonSSMPluginsawsCloudWatchAWS.EC2.Windows.CloudWatch.json に出力されます。
テキストファイルの改行コードが変わり、Headerが追加されますが、影響はございませんので、無視頂くようお願いいたします。

・・・ふむ。
これ通りに試したところ、無事にログ転送がされました。

極端なことを言ってしまえば僕のケースでは、再起動するサービスを EC2Config から SSMAgent に変えるだけで良かったと言う話です。(担当部署が変わったからそっちに聞いて的な)
でもあくまで設定ファイルの大本は今まで通り EC2Config の下にあるファイルで、それを読み込んで SSM の下のワークスペース的なところに保持するって感じなんですね。

まとめ

  • 新しい Windows AMI でも CloudWatch Logs のログ転送設定をローカルファイルで行える
  • ただし扱うサービスは SSMAgent
  • ファイルの置き場に注意
  • AWS のドキュメントはやっぱり追いつくのが遅い
  • AWS のサポートは丁寧で助かる

続きを読む

DeepLearning ハンズオン @ MaruLabo × JAWS-UG #2

この資料で書いていること

ディープラーニング・ハンズオン @MaruLabo × JAWS-UG AI #2 で利用する資料です。

ハンズオン環境

  • Python2 系 GPU / CPU 版 TensorFlow
  • AWS の設定
    • 本ハンズオン用に作成した AMI を利用
    • GPU インスタンス(使えない方は CPU 版を利用)
    • ポートは TensorBoard に 6006、とデモアプリに 80 を開放

1. AWS マネジメントコンソールでの操作

1.1 マネジメントコンソールにログイン

https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Instances

  • 今回のハンズオンは無料枠の範囲ではないインスタンスタイプを利用します
  • クーポンを利用する際は該当のアカウントであることを確認しましょう

1.2 AMI の指定

以下のリンクをクリックし、ハンズオンに利用する AMI を選択します。

▼ バージニアリージョンなら

https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-821fcb94

▼ オレゴンリージョンなら

https://console.aws.amazon.com/ec2/v2/home?region=us-west-2#LaunchInstanceWizard:ami=ami-f9ba3b99

  • NVIDIA ドライバ 367.48、CUDA 8.0、cuDNN 5.1 がインストールされています
  • 本ハンズオン後に非公開にします(※事情: ライセンス)

1.3 GPU インスタンスの指定

  • g2.2xlarge を選択します
  • GPU 起動を事前に申請できなかった方c4.4xlarge
  • 次の手順: インスタンスの詳細を設定 に進みましょう

gpu.png

1.4 パブリック IP などの確認

  • 自動割り当てパブリック IP有効 であることを確認
  • EC2 に慣れている方は スポット価格の設定 もぜひどうぞ
  • 次の手順: ストレージの追加 に進みましょう

type.png

1.5 公開ポートの設定

  • ストレージタグ の設定はそのまま次の手順へ
  • ルールの追加 から、80 番6006 番ポートを追加
  • 確認と作成 を押します

sg.png

1.6 起動インスタンスの確認

  • cuDNN v5.1 for CUDA 8.0 on Ubuntu 16.04
  • g2.2xlarge(または c4.4xlarge)
  • 22 / 80 / 6006 番ポートの解放
  • 作成 を押します

confirm.png

1.7 SSH 鍵のダウンロード

  • 新しいキーペアの作成 を選択します
  • 適当な名前をつけて キーペアのダウンロード を押しましょう
  • インスタンスの作成 を押します

create.png

2. TensorFlow 動作確認

2.1 サーバのパブリック IP の確認

起動したインスタンスを選択し、パプリック IP アドレスを確認します。
https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Instances

public-ip.png

2.2 サーバへ SSH 接続

  • ダウンロードした pem を使って SSH ログイン
  • 不慣れな方は こちら をご覧ください..
  • ユーザ名は ec2-user ではなく ubuntu です。ご注意を。
$ chmod 400 keypair.pem
$ ssh -i keypair.pem ubuntu@<ec2.public.ip.address>

2.3 GPU の確認

  • (GPU でない方は飛ばしてください)
  • Deep Learning AMI はなんと NVIDIA ドライバもインストール済!
  • 以下のコマンドで GPU の状況を確認してみましょう
$ nvidia-smi

Mon Feb 20 07:20:25 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 367.48                 Driver Version: 367.48                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GRID K520           Off  | 0000:00:03.0     Off |                  N/A |
| N/A   37C    P8    17W / 125W |      0MiB /  4036MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

2.4 TensorFlow のインストール

GPU 版 TensorFlow のインストール1

sudo apt install -y python-pip
pip install "tensorflow_gpu==0.12"

CPU 版 TensorFlow のインストール

sudo apt install -y python-pip
pip install "tensorflow==0.12"

以下共通

# TensorFlow バージョン確認 (応答まで 1 分程度かかります)
python -c 'import tensorflow as tf; print(tf.__version__)'

# 確認用 Python スクリプトの取得
git clone --depth 1 https://github.com/tensorflow/tensorflow.git
cd tensorflow/tensorflow/examples/tutorials/mnist/

# MNIST (softmax 版) の動作確認
python mnist_softmax.py

# MNIST (DNN 版) の動作確認
python fully_connected_feed.py

3. TensorFlow 実習

3.1 デモアプリ概要

以下のアプリは、MaruLabo 管理者の 古川さん が作成したものです。
TensorFlow チュートリアル「Deep MNIST for Experts」(原文翻訳)のMNIST モデルの学習状況をブラウザで閲覧できるようにしたものです。

3.2 デモアプリのダウンロード

cd ~
wget https://github.com/ornew/mnist-visual/releases/download/v3.0.1/mnist-visualize-example_3.0.1.tar.gz
tar zxvf mnist-visualize-example_3.0.1.tar.gz
cd ~/mnist-visualize-example_3.0.1

3.3 サンプルサーバの起動

# 依存パッケージのインストール
pip install scipy tornado futures Pillow

# (GPU 版のみ) CUDA ライブラリのリンク
sudo ldconfig /usr/local/cuda/lib64

# 起動
sudo python server.py -p 80

3.4 Web ブラウザから訓練を実施

パプリック IP アドレスをブラウザの URL に入力してアプリに接続します。

ブラウザでアクセスしたら「訓練を開始する」というボタンを押してください。少し待っているとテスト結果が表示され始めます。

3.5 テスト結果の見方

  • 緑 → テストで正解したもの
  • 赤 → テストで間違えたもの

スクリーンショット 2017-01-16 13.59.17.2.png

参考: 100 ステップ目

スクリーンショット 2017-01-16 13.59.17.png

参考: 1,000 ステップ目

スクリーンショット 2017-01-16 13.59.27.png

参考: 20,000 ステップ目

スクリーンショット 2017-01-16 15.34.28.png

3.6 手書きテスト

実際に訓練で作ったモデルを試すことが出来ます。ステップを選択したら、枠線の中に数字を書いて評価してみてください。下部に結果と分布が表示されます2

スクリーンショット 2017-01-18 23.11.38.png

同じ数字のまま、ステップだけを切り替えてみると、学習状況によって判定が変わっていくのが確認できます。「なんとなく精度が上がっている」のが見てとれると思います。

3.7 TensorBoard の起動・アクセス

tensorboard --logdir ~/mnist-visualize-example_3.0.1/var --noinspect &

この状態でインスタンスの IP アドレスの 6006 番ポートにアクセスすると TensorBoard にアクセスできます。

3.8 クロスエントロピーの推移

精度が上がっていることをきちんと数値で示すと以下のようなグラフになります。これは、クロスエントロピーの値を学習ステップごとにプロットしたものです。

スクリーンショット 2017-01-16 19.00.18.png

最初の 100 ステップで大幅に下がったのち、徐々に 0 に収束していくのが確認できます。

3.9 誤差の評価

このモデルでは、算出した確率分布と本当の答えが「どのくらい間違ってるのか」(これは、見方を変えれば「どのくらい正しいのか」を示す値でもあります)をクロスエントロピーという手法で定式化して計算しています。

cross_entropy = tf.reduce_mean(
  tf.nn.softmax_cross_entropy_with_logits(
    inference, # 予測された値
    label))    # 正解の値

この値は、両者の値の誤差を示すもので、両者の値が近づくほどに小さくなります。つまり、この値が0に収束するということは、正解の値に近づいている=精度が上がっているということを示します。

3.10 誤差をどうやって小さくしている?

tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

このプログラムは、「cross_entropy を最小化するようにグラフを ADAM オプティマイザで最適化する」という意味です。

ディープラーニングでは、この誤差が小さくなるように確率的勾配降下法などを利用してモデルを訓練をしていくのが一般的です。このサンプルでは ADAM オプティマイザを利用しています。

TensorFlow では、この一行ですべてやってくれます。学習率を調整したければ1e-4 を変更するだけです。ADAM オプティマイザ以外の最適化アルゴリズムも組み込みで用意されています。理論を知らなくても、TensorFlow に組み込みのオプティマイザを使うことができますし、独自のものを自分で定義する能力も TensorFlow には用意されています。

 


  1. ここで 0.12 を指定せず pip install tensorflow_gpu とすれば、v1.0 がインストールされます。 

  2. この分布はソフトマックスで正規化していません。これは、訓練によって中身がどう変わっているのかを直感的に確認することが、このサンプルの目的であるためです。意図的なものです。 

続きを読む

Amazon Elastic BeanstalkでサクッとElixir製サーバーをAWSにデプロイする

はじめに

Amazon Elastic Beanstalk では、 Dockerを使ってよしなにアプリケーションをAWSにデプロイできる。
Docker ImageがS3に上がり、サーバーはEC2にデプロイされる。
これを使ってElixir製のping叩いたらpong返してくるだけのアプリをデプロイしてみる。

mix で新規プロジェクトを作成

今回はelixir:1.4.1を用いる。

$ mix new testex

ライブラリの追加

今回は cowboy(HTTPサーバ), poison (JSON ライブラリ), distillery(release 用ライブラリ)を使う。

mix.exs に以下のように追加して

defmodule Testex.Mixfile do
  use Mix.Project

  def project do
    [app: :testex,
     version: "0.1.0",
     elixir: "~> 1.4",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  def application do
    # Specify extra applications you'll use from Erlang/Elixir
    [applications: [:logger, :cowboy, :poison],
      mod: {Testex,[]}]
  end

  defp deps do
   [{:cowboy, github: "ninenines/cowboy"},
    {:poison, "~> 3.0.0"},
    {:distillery, "~> 1.1.0", runtime: false}]
  end
end
$ mix deps.get
$ mix release

$ _build/dev/rel/testex/bin/testex console

これで elixir が走るようになるはず。

cowboy で http サーバを走らせる

ポートやルーティングの設定

lib/testex.ex
defmodule Testex do
  def start(_type, _args) do
  import Supervisor.Spec, warn: false
  children = []
  dispatch = :cowboy_router.compile([
      {:_, [
           {"/ping", Testex.Handlers.Ping, []},
      ]}
    ])
    {:ok, _} = :cowboy.start_clear(:http,
                                   100,
                                   [{:port, 4000}],
                                   %{env: %{dispatch: dispatch}})
  opts = [strategy: :one_for_one, name: Testex.Supervisor]
  Supervisor.start_link(children, opts)
  end
end

ホントに pong を返すだけ

lib/testex/handlers/testex.ex

defmodule Testex.Handlers.Ping do
    def init(req, opts) do
    body = %{"type" => "pong"} |> Poison.encode!()
    req2 = :cowboy_req.reply 200, %{"content-type" => "application/json"}, body, req
    {:ok, req2, opts}
    end
end

AWS用のDocker 設定ファイルを作成

Dockerrun.aws.json
{
  "AWSEBDockerrunVersion": 1,
  "volumes": [
    {
      "name": "testex",
      "host": {
        "sourcePath": "/app"
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "testex",
      "essential": true,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 4000
        }
      ]
    }
  ]
}

WORKDIR として /app を明記するのがポイント

Dockerfileの作成

Dockerfile

# elixir の Docker Image を引っ張ってくる。今回は Phoenix を使わないので node 等は不要
FROM trenpixster/elixir:1.4.1

# WORKDIR の作成
RUN mkdir /app
WORKDIR /app

# mix deps.get
ADD mix.* ./
RUN MIX_ENV=prod mix local.rebar
RUN MIX_ENV=prod mix local.hex --force
RUN MIX_ENV=prod mix deps.get

# Install app
ADD . .
RUN MIX_ENV=prod mix release

# Exposes this port from the docker container to the host machine
EXPOSE 4000

# distillery の run command を実行する。今回はフォアグラウンドで
CMD MIX_ENV=prod _build/prod/rel/testex/bin/testex foreground

eb コマンドの実行

あらかじめ eb をインストールする。 pip で入る。
http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html

これで、

$ eb init # awsアカウント情報を入力
$ eb create # サーバ名等を入力。Dockerfileがあるとeb側でよしなに判断してくれる。
$ eb deploy testex

すると本番にデプロイされる。

おわり

これはお試しですが、実際に運用するなら、本番・開発環境を切り分けたり、CORS対策としてAPI Gatewayなどを別途で使う必要あり。

サンプルコード

https://github.com/GigantechHQ/ex-server-sample

続きを読む