深淵の闇から逃れる為の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のデュアルマスタ機能を試すことが出来ました。

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

最高。

続きを読む

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

続きを読む

Amazon LinuxにアタッチされているEBSのボリュームサイズを拡張する。

はじめに

つい先日、Amazon EBSのアップデートで 「エラスティックボリューム」の発表 がありました。
これはEC2にアタッチされたEBSに対し、「ボリュームサイズの拡張」「パフォーマンスの調整」「ボリュームタイプの変更」がオンラインで行うことができます。
ということで今回、「ボリュームサイズの拡張」を行ってみたいと思います。

事前確認

今回作業するOSは Amazon Linux 2016.09 です。
まずは状況確認しておきます。

デバイスの確認
$ lsblk 
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk 
└─xvda1 202:1    0   8G  0 part /
xvdb    202:16   0  10G  0 disk /data

現在、 xvdb という名前でボリュームサイズが10GBのデバイスがEC2インスタンスにアタッチされています。

ボリュームの確認
$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         236M   60K  236M    1% /dev
tmpfs            246M     0  246M    0% /dev/shm
/dev/xvda1       7.8G  984M  6.7G   13% /
/dev/xvdb        9.8G   23M  9.2G    1% /data

また、 ファイルシステムとしても10GBのボリュームが /data 領域にマウントされているのを確認できます。

今回はこの xvdb のボリュームサイズを拡張します。

EBSサイズの拡張

[EC2] > [ボリューム] から対象となるボリュームをチェックし、[アクション]から[Modify Volume]をクリックします。
expand-ebs01.png

今回は 20GB へ拡張するので「Size」の1020に変更し[Modify]をクリックします。
expand-ebs02.png

「ボリュームの変更をしても本当にいいですか?」と聞かれるので、[Yes]をクリックします。
expand-ebs03.png

すると「in-use optimizing」というステータスになります。変更したサイズへ拡張中です。
expand-ebs04.png

しばらくして「in-use completed」になったらEBS拡張処理の完了です。
expand-ebs05.png

ファイルシステムサイズの拡張

それでは先ほど拡張したボリュームサイズがちゃんと20GBに増えているか確認してみます。

デバイスの確認
$ lsblk 
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk 
└─xvda1 202:1    0   8G  0 part /
xvdb    202:16   0  20G  0 disk /data

無事、20GBのボリュームとして認識されているのが確認できました。
ですが、今のままだとEBSデバイスのサイズが増えただけです。

ボリュームの確認
$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         236M   60K  236M    1% /dev
tmpfs            246M     0  246M    0% /dev/shm
/dev/xvda1       7.8G  985M  6.7G   13% /
/dev/xvdb        9.8G   23M  9.2G    1% /data

なので、まだこのままではOSから利用することはできません。
ということでOSから利用できるようにresize2fsでファイルシステムをリサイズします。

ファイルシステムの拡張
$ sudo resize2fs /dev/xvdb 
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/xvdb is mounted on /data; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 2
The filesystem on /dev/xvdb is now 5242880 (4k) blocks long.

これでファイルシステムのリサイズ完了です。

ボリュームの確認
$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         236M   60K  236M    1% /dev
tmpfs            246M     0  246M    0% /dev/shm
/dev/xvda1       7.8G  985M  6.7G   13% /
/dev/xvdb         20G   28M   19G    1% /data

ということで無事にOSから利用できるようになりました。

おわりに

これで「とりあえず多めに確保」をする必要も無くなり、「今必要なサイズ」を割り当てれば良くなりました。
また、拡張作業もオンタイムで行えるので、費用面だけでなく運用面のコスト削減にもなります。
これで「いろんな人の負担」がかなり軽くなりますね。

続きを読む

AWS IoT Device Shadowを試すためのpython mock

はじめに

AWS IoTを利用する1つの理由として、Thing shadowによるThing/Deviceコントロールが出来ることがあるかと思います。
一方で概要は理解できたものの、”Thing”がないから動作確認ができないとお思いの方も多いかと、、
こちらについては実はDeviceSDKを使えばLinux上で確認が可能です。
ここでは、EC2を仮想デバイスとして、Thing shadowの動作確認をしてみようと思います。

準備

AWS IoT

Thingの作成

registryを選択し、thingsを選択。Thingペインが表示されたら、画面上部のcreateを押す。
スクリーンショット 2017-02-07 23.08.11.png

Thingの作成ですが、任意の名前を設定し、create thingを押す。
ここではthinnameを、shadow-testとしてます。

Thing作成後の画面で、Certificateを選択。表示された画面のcreate certificateを押す。
スクリーンショット 2017-02-07 23.11.03.png

作成された証明書3つをダウンロードしておいてください。(cert.pem/public.key/private.key)
また画面下部に表示された Activeを押し、証明書を有効化します。

Endpointの確認

AWS IoTコンソール、画面左下にあるSettingsを押し、表示された画面にエンドポイント確認、textなどにコピーしておいてください。
endpointはregionごとに一つとなります。
{random}.iot.{region}.amazon.com
の形で表示されます。

EC2

EC2の起動

EC2でamazon linuxを起動してください。t2.microで十分です。
EC2の起動方法がわからない方、こちらをご参考に。
HDDサイズもデフォルトで十分です。SSHでログインするためのGlobal IPの設定をお忘れなく。

EC2へIoT DeviceSDKのinstall

mockはpythonSDKで作っているのでPythonSDKをインストールします。
EC2のコンソールから以下のコマンドを発行

pip install AWSIoTPythonSDK

dummyプログラム(mock)の設置

必要なものとして

  • プログラム本体
  • setup.json
  • 証明書 3つ (rootCA/private/cert)

を配置します
本投稿では、/tmpの下で作業することを想定して記述します。

mkdir /tmp/dummy-shadow
cd /tmp/dummy-shadow
mkdir certs
cd certs

証明書の設定

rootCAの取得 (certsの下に配置します)
ベリサインのこちらから取得

curl https://www.symantec.com/content/en/us/enterprise/veisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -o /tmp/root.pem

またこのcertsディレクトリに先程AWS IoTで作成したprivate/certの証明書を配置します。
scp/viでのコピペでも構いません。ファイル名は
cert => cert.pem
private => private.pem
としてください。以下の結果になるようにしてください。

ls -1 /tmp/certs
cert.pem
private.pem
root.pem

dummy(mock)の設置

/tmp/dummy-shadowで以下のファイルを配置します。
YOUR_AWSIOT_DOMAINは各自の環境に併せて、設定して下さい。
ドメインの確認は、AWS IoTのコンソール Settings => Custom endpointを参照して下さい。

setup.json
{
   "AWSIoT": {
        "ENDPOINT":"YOUR_AWSIOT_DOMAIN",
        "CERT_PATH":"./certs/",
        "KEYS":["cert.pem", "public.pem", "root.pem"]
   },
   "DEVICE":{
        "NAME":"shadow-test",
   }
}

以下にdummyプログラム

dummy-shadow.py
import os
import sys
import json
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient


CONFIG_FILE = './setup.json'

#アプリ起動時の状態を固定で定義(shadowと同期する変数)
SIGNAL    = "green"
WAIT_TIME = 10

#コールバック関数からshadowを読み出すのでglobalで定義
shadow         = None
shadow_hundler = None

##########################################################
# setup.jsonから設定を読み出す
##########################################################
def readConfig():
    print 'start readConfig func'

    try:
        # read config file
        f = open(CONFIG_FILE, "r")
        jsonData = json.load(f)
        f.close()

        list = {}
        list["ENDPOINT"]    = jsonData["AWSIoT"]["ENDPOINT"]
        list["CERT_PATH"]   = jsonData["AWSIoT"]["CERT_PATH"]
        list["DEVICE_NAME"] = jsonData["DEVICE"]["NAME"]

        return list

    except Exception as e:
        print 'Config load error'
        print e.message
        sys.exit()

##########################################################
# Shadow update命令の発行
##########################################################
def updateThing(report):
    try: 
        report_json = '{"state":{"reported":'+ report + '}}'
        print "send currnet status to cloud-shadow"
        print report_json
        shadow_hundler.shadowUpdate(report_json, None, 5)

        return

    except Exception as e:
        print e.message
        sys.exit()

##########################################################
# shadowRegisterDeltaCallbackからコールバック型で呼び出される
#
##########################################################
def getDelta(payload, responseStatus, token):
    try:
        print '======get Delta======'
        dict_delta = json.loads(payload)
        print dict_delta
        state = dict_delta["state"]

        if state.has_key("wait_time"):
            delta = int(state["wait_time"])
            global WAIT_TIME
            WAIT_TIME = delta
            print "wait time change :%s" % WAIT_TIME
        if state.has_key('signal'):
            global SIGNAL
            SIGNAL = state['signal']
            print "SIGNAL change :%s" % SIGNAL

        report = json.dumps(dict_delta["state"])
        updateThing(report)

        return

    except Exception as e:
        print "Error on Delta function"
        print e.message
        raise

################################################
# Shadow接続の手続き関数
################################################
def initShadow(Config):
    ##--need device cert / private / rootCA--
    # rootCA: get from symantec
    ROOT_KEY    = Config['CERT_PATH'] + 'root.pem'
    CERT_KEY    = Config['CERT_PATH'] + 'cert.pem'
    PRIVATE_KEY = Config['CERT_PATH'] + 'private.pem'

    try:
        # init shadow connect procedure
        global shadow
        shadow = AWSIoTMQTTShadowClient(Config["DEVICE_NAME"])
        shadow.configureEndpoint(Config["ENDPOINT"], 8883)    # Setting URL-ENDPOINT & Port
        shadow.configureCredentials(ROOT_KEY, PRIVATE_KEY, CERT_KEY ) # Cert file setting
        shadow.configureConnectDisconnectTimeout(10)# CONNACK wait time (sec)
        shadow.configureMQTTOperationTimeout(5)     # QoS1 publish (sec)
        print 'start connct shadow'
        shadow.connect()
        print 'shadow connect'

        return

    except Exception as e:
        print 'Error on Init Shadow'
        raise

####
if __name__ == '__main__':
    Config = readConfig()

    try:
        initShadow(Config)
        print 'satrt subscribe shadow'
        shadow_hundler = shadow.createShadowHandlerWithName(Config['DEVICE_NAME'], True)
        shadow_hundler.shadowRegisterDeltaCallback(getDelta)
        default_state = '{"wait_time":' + str(WAIT_TIME) + ', "signal":"green"}'
        updateThing(default_state)

        while True:
            time.sleep(WAIT_TIME)
            print 'Currnet WAIT_TIME=%s' % str(WAIT_TIME)
            pass

    except KeyboardInterrupt:
         print 'Keyboard Interrupt'
         sys.exit()

    except Exception as e:
        print e.message
        sys.exit()

実行方法

python PROGRAM.py &

でバックグラウンド起動します。

shadow update

コンソールもしくは、CLIでshadow updateをします。
Resistor => Thing => [Thing名] => Shadowを選択します。

以下のようにdesiredセクションを追加し、wait_time:30 , signal:”green”で登録してみます。
するとdeltaとして、 wait_time:30 つまり差分のみがdeltaとしてthingにレポートされます。

スクリーンショット 2017-02-19 22.20.44.png

プログラム側のコンソールを見ると、deltaを受けソレが表示さているはずです。
で、コードの方をみてもらうとわかりますが、変数のwait_timeを変更し、[state]を送りかえしています。
deltaのstateには、結果整合としてあるべき状態が書いてあるので、正しく状態遷移が出来たらこれをreportするだけでいちいちJSONを作成する必要はありません。
上記の通り、正しく状態遷移出来ない状態で report するとズレますので要注意。

このレポートが送られるとdeltaがなくなり、desired/reportedが同じ値になっているはずです。

大まかなシーケンス

大まかなシーケンスを以下に示します。

スクリーンショット 2017-02-19 22.38.16.png

プログラム起動時に report statusとして ハードコーディングされている
SIGNAL = “green”
WAIT_TIME = 10
を通知します。何度か、shadowの状態を変えた場合はプログラム起動直後に上記初期パラメータを通知し、即deltaを受ける事になります。

免責

本投稿は、個人の意見で、所属する企業や団体は関係ありません。
また掲載しているsampleプログラムの動作に関しても保障いたしませんので、参考程度にしてください。

続きを読む

Tensorflow 1.0でGPU利用 with AWS

そろそろ春の足音がしだして、相変わらずの何もできてないっぷりに愕然とする今日このごろです。

さすがに何もしていないのはまずいので、以前触った TensorFlow を再び触り始めています。
相変わらずのGoogleクオリティで、APIが色々と変わっているような気がしますが、いろいろ忘れているので調度良いかなと。

何か一つ作ってみようと、作ってみている最中です(いずれ記事に・・・)が、DeepLearningとかコモディティ化してきていると言われているとはいえ、理解しながら作ろうとすると、もれなく数式とコンニチワするので、文字通りに頭の痛い日々です。でも楽しい。

Amazon LinuxでGPU利用する

今作ってみているものは、学習をCPUだけでやると時間がかかってしょうがない+学習用データが大きすぎて洒落にならないため、GPUで実施したくなります。
GPUは一応部屋のPCについているとはいえ、CUDAのインストールトラウマもあって、流石に普段使いのPCにインストールするのが面倒です。

ちなみに現時点で、CPUだけで学習すると1ステップ辺り、1データで2分とかかかります。20000ステップとか考えたくもないですね。電気代的な意味で。

AzureでもGCPでもいいけれども、使い慣れてるAWSのGPUインスタンスを使ってみます(使ったことがなかった)。
(というか、個人とかの場合クラウドを使わないと現実的な時間で終わんないような)

まともに利用するとかなりの金額になりそうだったので、SpotFleetで用意しました。Spot Fleetになってから初めて利用しますが、ちゃんとドキュメントを読まないと、capacityの意味が?ってなりますね。でも6割り引きくらいで利用できるのはあまりに魅力的・・・。

さて、CUDAはつい最近知識をアップデートしましたが、かなりインストールが楽になっていてびっくりです。
そのなかでも楽をするために、Ubuntu16.04のAMIを利用することにしました。CUDA/cuDNNのライブラリをnvidiaのページからダウンロードします。cuDNNはアカウントが必要なので、作る必要があります。

CUDAライブラリは、runfileにしておくと楽です、が、今回はdebにしてしまったので、次のようになります。

$ sudo dpkg -i <ダウンロードしたパッケージ名>
$ sudo apt-get update
$ sudo apt-get install cuda

cudnnライブラリは、cudaをインストールした先に展開します。debでインストールした場合は /usr/local/lib/cuda に入るので、その下になるように入れましょう。

$ tar xf cudnn-*.tgz
$ sudo cp cuda/include/* /usr/local/cuda/include
$ sudo cp cuda/lib64/* /usr/local/cuda/lib64
Tensorflowが1.0になりましたが、GPU版の要求CUDAが8.0(最新)になり、同時に要求されるnvidia-driverのバージョンが375系になるという、非常に悲しい出来事がありました。過去を振り返らないnvidiaとGoogleらしい決断とでもいうのでしょうか。
そして悲劇として、Tokyoリージョンで利用できるg2インスタンスだと、K520というGPUを積んでいるのですが、これがで、367.*系のドライバでしか動かないのです・・・。
そのため、tensorflowのバージョンを下げるか、USリージョンでp2インスタンスを利用する必要があります。
今回は涙をのんでUSリージョンにP2を立てました。Tokyoにも来てくれることを願います。

そして、.bash_profileLD_LIBRARY_PATH を設定します。これを忘れるとライブラリの読み込みでエラーになって( ゚д゚)ハッ!てなります。

if [[ -z $LD_LIBRARY_PATH ]]; then
    export LD_LIBRARY_PATH=/usr/local/cuda/lib64
else
    export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
fi

ちゃんと動作しているかどうかは、非常に簡単なTensorflowプログラムを動かしてみるのが一番手っ取り早いです。

import tensorflow as tf
sess = tf.session()
hello = sess.run(tf.constant('Hello, world!'))
print(hello)

うまく動作すると、次のようなログが出ます。

I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use FMA instructions, but these are available on your machine and could speed up CPU computations.
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:910] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties:
name: Tesla K80
major: 3 minor: 7 memoryClockRate (GHz) 0.8235
pciBusID 0000:00:1e.0
Total memory: 11.17GiB
Free memory: 11.11GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla K80, pci bus id: 0000:00:1e.0

メモリ11GiBて・・・。さすがサーバー用のGPUは驚異的です。

Tensorflowのプロファイルを取りたい

GPUを利用していても、実際に早くなったのかどうか、は計測してみないとわからないものです。まぁ大体は速くなるんですけど、想定より遅いとかもありますし。

この場合、gperfとかそういったもので取得することも出来るようですが、軟弱な我々としては、やはりChromeとかFirefoxで慣れたタイムライン表示とかがいいです。可視化最高。TensorflowはGoogle主導で開発しているためかどうかはわからないけれど、これを行うための機能がすでにあります。

既存のTensorFlowプログラムに、以下のようなコードを追記します。

from tensorflow.python.client import timeline

with tf.Session() as sess:
    run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
    run_metadata = tf.RunMetadata()

    sess.run(
        training_op,
        feed_dict=feed,
        run_metadata=run_metadata,
        options=run_options)

    # write train
    tl = timeline.Timeline(run_metadata.step_stats)
    ctf = tl.generate_chrome_trace_format()
    with open('timeline.json', 'w') as f:
        f.write(ctf)

こうやると、timeline.jsonというのがローカルに出来ます。これを、Chromeのアドレスバーで chrome://tracing といれて出るページで読み込ませてやると、見慣れた?タイムライン表示が行えます。内容的にはTensorflowのoperation単位になってるようです。

ただし、事前に LD_LIBRARY_PATH/usr/local/cuda-8.0/extras/CUPTI/lib64 を追加しておく必要があります。この中にあるlibcuptiが読み込めないとエラーになるためです。

GPUの活用は正義(多分)

最初はg2インスタンスでやろうと思っていたのですが、まさかの使えない問題でp2インスタンスを利用することになるとは思いませんでした・・・。USリージョンだとSSHがまさしく「あー海越えてるなー」って速度になるんでストレスフルですね!

とりあえず、学習とかモデルとか、その辺りを勉強しながら進めていく、というのはなんだか久しぶりで、かなり新鮮です。データセットを集める手法の確立とかも大事ですが、そこはまぁ頑張ればいいので・・・。

新しいものをすぐ使いたくなるような人(主に自分)の助けになれば幸いです。

続きを読む

[JAWS-UG CLI] EC2:#2 インスタンスの作成 (Public + UserData)

前提条件

EC2への権限

EC2に対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.19 Python/2.7.10 Darwin/15.6.0 botocore/1.4.76

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

AWSアカウントの属性

AWSアカウントがEC2-Classicに対応していないこと。

コマンド
AWS_SUPPORT_PLATFORMS=$( 
         aws ec2 describe-account-attributes 
           --query 'AccountAttributes[?AttributeName == `supported-platforms`].AttributeValues[].AttributeValue' 
           --output text 
) && echo ${AWS_SUPPORT_PLATFORMS}

結果:

  VPC

注釈: ‘VPC’の他に’EC2’が表示される場合、別のアカウントを作成もしくは
利用し てください。

0. 準備

0.1. リージョンの決定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.2. 変数の確認:

プロファイルが想定のものになっていることを確認します。

変数の確認
aws configure list

結果(例):

        Name                    Value             Type    Location
        ----                    -----             ----    --------
     profile       ec2full-prjZ-mbp13        env    AWS_DEFAULT_PROFILE
  access_key     ****************XXXX shared-credentials-file
  secret_key     ****************XXXX shared-credentials-file
      region        ap-northeast-1        env    AWS_DEFAULT_REGION

0.3. VPCの指定

既存のVPCに割り当てられているCIDRブロックを確認します。

コマンド
aws ec2 describe-vpcs 
        --query 'Vpcs[].CidrBlock'

結果(例):

  [
    "172.31.0.0/16",
    "10.192.0.0/16"
  ]

ここでは、10.192.0.0/16を範囲とするVPCを選択します。

変数の設定
VPC_CIDR='10.192.0.0/16'

VPC IDを取得します。

コマンド
VPC_ID=$( 
        aws ec2 describe-vpcs 
          --filters Name=cidr,Values=${VPC_CIDR} 
          --query 'Vpcs[].VpcId' 
          --output text 
) 
        && echo ${VPC_ID}

結果(例):

  vpc-xxxxxxxx

0.4. サブネットの指定

変数の設定
VPC_SUBNET_CIDR='10.192.0.0/24'
コマンド
VPC_SUBNET_ID=$( 
        aws ec2 describe-subnets 
          --filters Name=cidrBlock,Values=${VPC_SUBNET_CIDR} 
          --query 'Subnets[].SubnetId' 
          --output text 
) 
        && echo ${VPC_SUBNET_ID}

結果(例):

  subnet-xxxxxxxx

0.5. UserDataの指定

変数の設定
FILE_EC2_USERDATA='userdata.bash'

1. 事前作業

1.1. プライベートアドレスの決定

選択したサブネットのアドレス範囲内で、起動するインスタンスのプライベー
トIPアドレスを指定します。

変数の設定
EC2_PRIVATE_ADDR='10.192.0.8'

1.2. セキュリティグループの決定

セキュリティグループの一覧を確認します。

コマンド
aws ec2 describe-security-groups 
        --query "SecurityGroups[?VpcId == ` ${VPC_ID}`].GroupName"

結果(例):

  [
    "default",
    "ec2-ssh-global-inbound"
  ]

利用するセキュリティグループ名を指定します。

変数の設定
VPC_SG_NAME='ec2-ssh-global-inbound'
コマンド
VPC_SG_ID=$( 
        aws ec2 describe-security-groups 
          --filter Name=group-name,Values=${VPC_SG_NAME} 
          --query "SecurityGroups[?VpcId == ` ${VPC_ID}`].GroupId" 
          --output text 
) 
        && echo ${VPC_SG_ID}

結果(例):

  sg-xxxxxxxx

セキュリティグループを配列に入れておきます。

変数の設定
ARRAY_VPC_SG_ID="${VPC_SG_ID} ${ARRAY_VPC_SG_ID}" 
        && echo ${ARRAY_VPC_SG_ID}

1.3. キーペアの指定

まず、キーペアの一覧を確認します。

コマンド
aws ec2 describe-key-pairs 
        --query 'KeyPairs[].KeyName'

結果(例):

  [
      "<キーペアー名>"
  ]

利用するキーペア名を指定します。

変数の設定
EC2_KEY_PAIR_NAME='<キーペアー名>'

利用するキーペアの秘密鍵ファイルを指定します。

変数の設定
FILE_SSH_KEY="${HOME}/.ssh/<キーペア秘密鍵のファイル名>" 
        && echo ${FILE_SSH_KEY}

秘密鍵が存在することを確認しましょう。

コマンド
ls ${FILE_SSH_KEY}

1.4. イメージIDの決定

AMIを選択します。

変数の設定
AMZLINUX_VERSION='2016.03.3'
EC2_IMAGE_NAME="amzn-ami-hvm-${AMZLINUX_VERSION}.x86_64-gp2"
コマンド
EC2_IMAGE_ID=$( 
        aws ec2 describe-images 
          --filters Name=name,Values="${EC2_IMAGE_NAME}" 
          --query 'Images[].ImageId' --output text 
) 
        && echo ${EC2_IMAGE_ID}

結果(例):

  ami-XXXXXXXX

1.5. インスタンスタイプの決定

変数の設定
EC2_INSTANCE_TYPE='t2.micro'

1.6. 稼動インスタンスを確認

同一リージョンでインスタンスが起動していないことを確認します。

コマンド
EC2_INSTANCE_STATUS='running'
コマンド
aws ec2 describe-instances 
        --filters Name=instance-state-name,Values=${EC2_INSTANCE_STATUS}

結果:

  {
      "Reservations": []
  }

2. インスタンス起動

2.1. インスタンス起動

変数の確認
cat << ETX

        EC2_IMAGE_ID:              ${EC2_IMAGE_ID}
        EC2_INSTANCE_TYPE:         ${EC2_INSTANCE_TYPE}
        EC2_KEY_PAIR_NAME:         ${EC2_KEY_PAIR_NAME}
        EC2_PRIVATE_ADDR:          ${EC2_PRIVATE_ADDR}
        ARRAY_VPC_SG_ID:           ${ARRAY_VPC_SG_ID}
        VPC_SUBNET_ID              ${VPC_SUBNET_ID}
        FILE_EC2_USERDATA:         ${FILE_EC2_USERDATA}

ETX
コマンド
aws ec2 run-instances 
        --image-id ${EC2_IMAGE_ID} 
        --instance-type ${EC2_INSTANCE_TYPE} 
        --security-group-ids ${ARRAY_VPC_SG_ID} 
        --key-name ${EC2_KEY_PAIR_NAME} 
        --subnet-id ${VPC_SUBNET_ID} 
        --private-ip-address ${EC2_PRIVATE_ADDR} 
        --associate-public-ip-address  
        --user-data file://${FILE_EC2_USERDATA}

結果(例):

  {
    "OwnerId": "XXXXXXXXXXXX",
    "ReservationId": "r-xxxxxxxxxxxxxxxxx",
    "Groups": [],
    "Instances": [
      {
          "Monitoring": {
              "State": "disabled"
          },
          "PublicDnsName": "",
          "RootDeviceType": "ebs",
          "State": {
              "Code": 0,
              "Name": "pending"
          },
          "EbsOptimized": false,
          "LaunchTime": "2016-12-05T01:23:45.000Z",
          "PrivateIpAddress": "10.192.0.8",
          "ProductCodes": [],
          "VpcId": "vpc-xxxxxxxx",
          "StateTransitionReason": "",
          "InstanceId": "i-xxxxxxxxxxxxxxxxx",
          "ImageId": "ami-xxxxxxxx",
          "PrivateDnsName": "ip-xxx-xxx-xxx-xxx.ap-northeast-1ap-northeast-1.compute.internal",
          "KeyName": "<キーペアー名>",
          "SecurityGroups": [
              {
                  "GroupName": "ec2-ssh-global-inbound",
                  "GroupId": "sg-xxxxxxxx"
              }
          ],
          "ClientToken": "",
          "SubnetId": "subnet-xxxxxxxx",
          "InstanceType": "t2.micro",
          "NetworkInterfaces": [
              {
                  "Status": "in-use",
                  "MacAddress": "xx:xx:xx:xx:xx:xx",
                  "SourceDestCheck": true,
                  "VpcId": "vpc-xxxxxxxx",
                  "Description": "",
                  "NetworkInterfaceId": "eni-xxxxxxxx",
                  "PrivateIpAddresses": [
                      {
                          "Primary": true,
                          "PrivateIpAddress": "10.192.0.8"
                      }
                  ],
                  "Attachment": {
                      "Status": "attaching",
                      "DeviceIndex": 0,
                      "DeleteOnTermination": true,
                      "AttachmentId": "eni-attach-xxxxxxxx",
                      "AttachTime": "2016-12-05T01:23:45.000Z"
                  },
                  "Groups": [
                      {
                          "GroupName": "<キーペアー名>",
                          "GroupId": "sg-xxxxxxxx"
                      }
                  ],
                  "SubnetId": "subnet-xxxxxxxx",
                  "OwnerId": "XXXXXXXXXXXX",
                  "PrivateIpAddress": "10.192.0.8"
              }
          ],
          "SourceDestCheck": true,
          "Placement": {
              "Tenancy": "default",
              "GroupName": "",
              "AvailabilityZone": "ap-northeast-1a"
          },
          "Hypervisor": "xen",
          "BlockDeviceMappings": [],
          "Architecture": "x86_64",
          "StateReason": {
              "Message": "pending",
              "Code": "pending"
          },
          "RootDeviceName": "/dev/xvda",
          "VirtualizationType": "hvm",
          "AmiLaunchIndex": 0
      }
    ]
  }

2.2. インスタンスIDの取得

起動中のインスタンスからインスタンスIDを取得します。

コマンド
EC2_INSTANCE_ID=$( 
        aws ec2 describe-instances 
          --filters Name=private-ip-address,Values=${EC2_PRIVATE_ADDR} 
          --query 'Reservations[].Instances[].InstanceId' 
          --output text 
) 
        && echo ${EC2_INSTANCE_ID}

結果(例):

  i-xxxxxxxxxxxxxxxxx

3. 事後作業

3.1 インスタンスのステータス確認

コマンド
EC2_INSTANCE_STATE=$( 
        aws ec2 describe-instances 
        --instance-ids ${EC2_INSTANCE_ID} 
        --query 'Reservations[].Instances[].State.Name' 
        --output text 
) 
        && echo ${EC2_INSTANCE_STATE}

結果(例):

  running

3.2. パブリックIPアドレスの取得

コマンド
EC2_PUBLIC_IP=$( 
        aws ec2 describe-instances 
          --instance-id ${EC2_INSTANCE_ID} 
          --query "Reservations[].Instances[].PublicIpAddress" 
          --output text 
) 
        && echo ${EC2_PUBLIC_IP}

結果(例):

  xxx.xxx.xxx.xxx

4. インスタンスへのログイン

4.1. SSHログイン

変数の確認
cat << ETX

        FILE_SSH_KEY:  ${FILE_SSH_KEY}
        EC2_PUBLIC_IP: ${EC2_PUBLIC_IP}

ETX
コマンド
ssh -i ${FILE_SSH_KEY} ec2-user@${EC2_PUBLIC_IP}

結果(例):

  The authenticity of host '54.xxx.xxx.xxx (54.xxx.xxx.xxx)' can't be established.
  RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
  Are you sure you want to continue connecting (yes/no)?

入力:

  yes

結果(例):

  Warning: Permanently added '54.xxx.xxx.xxx' (RSA) to the list of known hosts.

         __|  __|_  )
         _|  (     /   Amazon Linux AMI
        ___|___|___|

  https://aws.amazon.com/amazon-linux-ami/2015.03-release-notes/
  24 package(s) needed for security, out of 53 available
  Run "sudo yum update" to apply all updates.

4.2. パッケージ更新

コマンド
sudo yum update -y

4.3. EC2メタ情報の確認

コマンド(EC2インスタンス)
echo -e "n"   && curl http://169.254.169.254/latest/meta-data/public-ipv4   && echo -e "n"

結果(例):

  xxx.xxx.xxx.xxx

4.4. EC2インスタンスからログアウト

コマンド(EC2インスタンス)
exit

完了

続きを読む

AWS ElasticBeanstalk PHP5.6からPHP7.0へ変更

  • 現バージョン:64bit Amazon Linux 2016.09 v2.3.1 running PHP 5.6
  • eb cli:3.9.0

Elastic Beanstalkが用意してるPHPプラットフォームは作成した時期によって使えるPHPバージョンが変わる。
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/platform-history-php.html

PHP5.6で作成した環境はv2.2.0からv2.3.0のようなアップデートは管理画面から簡単にできるけどPHP7.0への変更はできない。
PHP7を使うには新しい環境を作成し直すしかない。
普通に管理画面から設定の保存→保存した設定から環境の起動では上手く行かなかったので成功した手順を書いておく。

ローカルでebコマンドが使える前提。

1.eb config saveで設定の保存。
.elasticbeanstalk/saved_configs/[configuration-name].cfg.yml に設定ファイルが保存される。
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb3-config.html

2.設定ファイルの書き換え。

SolutionStack: 64bit Amazon Linux 2016.09 v2.3.1 running PHP 5.6

SolutionStack: 64bit Amazon Linux 2016.09 v2.3.1 running PHP 7.0

3.設定ファイルのアップロード
eb config put [configuration-name]

4.管理画面の「保存された設定」に追加されてるのでこの設定を元に「環境の起動」

5.Sample Applicationで起動されるので自分のプロジェクトをデプロイ。
設定見直したりRoute53を変更したり。この辺りは各自でやってみるしかない。

今後PHP7.1以降が出ても同じように変更できるはず。

続きを読む

AmazonLinuxに古いバージョンのnginxをインストールする

2017/2/15現在、AmazonLinux(2016.09)で sudo yum install nginxとやると バージョン1.10.1がインストールされます。
1.8が入れたかったのでその際の作業メモです。

nginxのリポジトリを追加

公式ドキュメントに書いてあるけど、一応説明。
http://nginx.org/packages/
上記から自分の環境にあったものを追加する。
AmazonLinuxはCentOS6互換なので、baseurlは http://nginx.org/packages/centos/6/$basearch/  が正解。

/etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1

失敗

これで sudo yum install nginx-1.8.1とやれば終了〜:raised_hands:とはならない。
amzn-mainリポジトリにnginxが存在しており、amzn-mainリポジトリには以下のようにpriorityが設定してある。

/etc/yum.repos.d/amzn-main.repo
[amzn-main]
name=amzn-main-Base
mirrorlist=http://repo.$awsregion.$awsdomain/$releasever/main/mirror.list
mirrorlist_expire=300
metadata_expire=300
priority=10
failovermethod=priority
fastestmirror_enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-amazon-ga
enabled=1
retries=5
timeout=10
report_instanceid=yes

[amzn-main-debuginfo]
name=amzn-main-debuginfo
mirrorlist=http://repo.$awsregion.$awsdomain/$releasever/main/debuginfo/mirror.list
mirrorlist_expire=300
metadata_expire=300
priority=10
failovermethod=priority
fastestmirror_enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-amazon-ga
enabled=0
retries=5
timeout=10
report_instanceid=yes

nginxリポジトリにはpriorityを設定していないので、amzn-mainリポジトリ > nginxリポジトリ となり、
amzn-mainリポジトリでnginxが見つかるのでnginxリポジトリを見に行かない→1.10.1がインストールされる。
なので、以下のようにamzn-mainを一時的に無効化しておく。

インストールできるバージョンの確認

yum --showduplicates --disablerepo=amzn-main list nginx

結果
nginx.x86_64                            1.8.0-1.el6.ngx                             nginx
nginx.x86_64                            1.8.1-1.el6.ngx                             nginx
nginx.x86_64                            1.10.0-1.el6.ngx                            nginx
nginx.x86_64                            1.10.1-1.el6.ngx                            nginx
nginx.x86_64                            1.10.2-1.el6.ngx                            nginx
nginx.x86_64                            1.10.3-1.el6.ngx                            nginx

インストール

sudo yum install -y --disablerepo=amzn-main nginx-1.8.1

バージョン確認

nginx -v

結果
nginx version: nginx/1.8.1

参考情報

CentOSにちょっと古いバージョンのNginxをインストールする
Amazon Linux で Nginx & PHP の最新版をインストールした際の記録

続きを読む

NAT 経由でインターネットアクセスする構成でのACL設定について

自社サイトブログからの転載

VPC で環境を構築しsubnetをpublic/privateと分離した構成とする場合、privateなsubnetからのインターネット側へのアクセスは基本的にNAT経由となる。 そのような構成のネットワークを組んだ際に外部のNTPサーバと同期させる設定でハマったので設定方法について考え方を整理してみた。

環境

OS及びNTPサーバ/クライアントは以下のとおり。

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.2 (Maipo)

$ chronyd --version
chronyd (chrony) version 2.1.1 

ネットワーク構成

以下のような構成を想定。

https://gyazo.com/e0e1d6fb247de73861603b404e52d8ee

Private Subnet 内に EC2 インスタンスを配置。
このインスタンスに NTPクライアント/サーバ(Chrony)を立ち上げてインターネットで公開されているNTPサーバと同期を取る設定を行う。

chrony の設定

ネットワークACLで設定し易くするため NTPクライアントとして使用するポートを固定する(以下の設定を行わない場合は1024以上のエフェメラルポートが使用される)

エディタで /etc/chrony.conf を開き以下の行を追加する。

# client port (udp)
acquisitionport 1123

設定の変更後は chronyd の再起動が必要だが、ACLの設定の後にも再起動が必要なので後述。

参考: chrony – Manual for version 2.3

ネットワークACL

NTPに必要なネットワークACLの設定は以下のとおり。

Public Subnet 側

インバウンドルール

プロトコル ポート 送信元 許可拒否 備考
UDP 123 10.0.0.0/16 許可 VPC内からのNTPを許可
UDP 1024-65535 0.0.0.0/0 許可 NATに対するNTPのレスポンスを許可

アウトバウンドルール

プロトコル ポート 送信先 許可拒否 備考
UDP 123 0.0.0.0/0 許可 NATからVPC外のNTPサーバへのリクエストを許可
UDP 1123 10.0.0.0/16 許可 private subnet のNTPクライアントへのレスポンスを許可

Private Subnet 側

インバウンドルール

プロトコル ポート 送信元 許可拒否 備考
UDP 1123 0.0.0.0/0 許可 インターネット側から(NAT経由)のNTPのレスポンスを許可する

アウトバウンドルール

プロトコル ポート 送信先 許可拒否 備考
UDP 123 0.0.0.0/0 許可 インターネットのNTPサーバへのリクエストを許可

動作状況の確認

ACLの設定を変更した場合 chronyd を再起動する。(もしかしたら不要かもしれないが再起動しないとすぐに同期が行われなかった)

$ sudo systemctl restart chronyd

再起動したら以下のコマンドでNTPサーバとの同期の状況を確認する。

$ chronyc sources
210 Number of sources = 4
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^- y.ns.gin.ntt.net              2   6     7     1  -2034us[-2839us] +/-  135ms
^- hachi.paina.jp                2   6     7     1  -4583us[-5388us] +/-   23ms
^* routerida1.soprano-asm.ne     3   6    17     0  -5855ns[-1517us] +/-   13ms
^- jp.f2tec.de                   3   6     7     1  +4904us[+4099us] +/-   64ms

先頭から2文字目が * になっているサーバが同期中のサーバを表している。このようになっていればNTPサーバとの同期が行われている。

設定の解説

設定内容を解説するにあたって、この構成で NTP が使用するポートを整理すると以下の図のようになる。

https://gyazo.com/5b470e494bdaf65a3f73cfa1802c1ede

Public Subnet 側のネットワークACL

Internet <–> VPC(Public) では NTPサーバ側(インターネット側)のポートが 123、NAT Gateway 側(VPC側)のポートが エフェメラルポート(1024〜65535の範囲のいずれかの番号)となる。
よって、Public Subnet 側のインバウンド/アウトバウンドのルールは以下のようになる。

インバウンド(a)

プロトコル ポート 送信元 許可拒否 備考
UDP 1024-65535 0.0.0.0/0 許可 NATに対するNTPのレスポンスを許可

アウトバウンド(b)

プロトコル ポート 送信先 許可拒否 備考
UDP 123 0.0.0.0/0 許可 NATからVPC外のNTPサーバへのリクエストを許可

次に VPC(Public) <–> VPC(Private) の設定について見てみる。
この場合、NAT Gateway 側(Public)のポートは123、Private側のポートは1123(前述の/etc/chrony.confで設定したポート番号)となる。
よって、 Public Subnet 側のインバウンド/アウトバウンドに必要なルールは以下のようになる。

インバウンド(c)

プロトコル ポート 送信元 許可拒否 備考
UDP 123 10.0.0.0/16 許可 VPC内からのNTPを許可

アウトバウンド(d)

プロトコル ポート 送信先 許可拒否 備考
UDP 1123 10.0.0.0/16 許可 private subnet のNTPクライアントへのレスポンスを許可

Public Subnet で必要な設定は上記のとおりとなる。

Private Subnet 側のネットワークACL

次に Private Subnet 側の設定を見ていくが、ここの設定については先に「VPC(Public) <–> VPC(Private) 」の設定と送信先/送信元を逆の視点で見た場合の設定を行えば良い。

よって以下のようになる。

インバウンド(e) ← (d)に対する逆

プロトコル ポート 送信元 許可拒否 備考
UDP 1123 0.0.0.0/0 許可 インターネット側から(NAT経由)のNTPのレスポンスを許可する

アウトバウンド(f) ← (c)に対する逆

プロトコル ポート 送信先 許可拒否 備考
UDP 123 0.0.0.0/0 許可 インターネットのNTPサーバへのリクエストを許可

以上で設定完了。

まとめ

最後にポイントをまとめると…

  • VPC内及びNATがインターネットとの通信で使用する UDP のポート123を許可する。
  • NAT がインターネット側との通信で使用するエフェメラルポートを許可する。
  • VPC内でNTPクライアントとの通信で使用するポート(/etc/chrony.confのacquisitionportに割り当てたポート)を許可する(※)

※acquisitionportを割り当てていない場合はエフェメラルポートとなるはずなのでそれを許可する設定とする必要がある。

自分がハマった点は、2番めの「NATがインターネット側との通信で使用するエフェメラルポート」の存在を見落としていて、ntpdで同期が行われないという現象でした。
また、RHEL7系ではこれまでの ntpd に代わって chrony がデフォルトになっている点も知らず、ntpdがうまく動かない(自動起動に設定していてもchronyが起動すると停止してしまう)という点でした。

RHEL(CentOS)6系から7系でいろいろ変わってるので、ほかの違いも見直しておく必要ありますね。。。

参考

続きを読む

[AWS] EC2+RDS開発環境設定 Nginx+Unicorn+Ruby2.3+Rails4.7+MySQL5.5

インフラ初心者がAWSに以下の環境を構築するまでの流れを実施したので、備忘録として残しておこうと思います。

(0)AWSの設定
(1)Apache2.2+PHP7+MySQL5.6+WordPress4.7+phpMyAdmin4.6
(2)Nginx+Unicorn+Ruby2.3+Rails4.7+MySQL5.5 ← 本エントリの内容

実施事項
◎Ruby、Railsの設定
◎MySQLの設定
◎Unicornの設定
◎Nginxの設定
◎”Hello World”の表示

【ご一読いただくにあたって】
・EC2、RDS等々の設定は済ませておいて下さい。
 まだの方はこちらから → AWSの設定
・間違いがありましたらコメントにて教えて下さい。(まだまだ勉強中です。)
・専門用語の深い解説はしておりません。
・通常デプロイはlocalやgithubにコードがあってそれをデプロイすると思いますが、当エントリではとりあえず本番環境(production)を作る目的で進んでいきます。

EC2の設定

アップデート、ユーザ追加等の下準備

sshでEC2へアクセスした状態


       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2016.09-release-notes/
3 package(s) needed for security, out of 6 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-1-86 ~]$ sudo yum update

~アップデート~

総ダウンロード容量: 51 M
Is this ok [y/d/N]: y
(途中、確認されますが[y]を入力)

完了しました!
[ec2-user@ip-10-0-1-86 ~]$ sudo su - #スーパーユーザへ切替
[root@ip-10-0-1-86 ~]# useradd myname #ユーザ追加
[root@ip-10-0-1-86 ~]# passwd myname #ユーザパスワード設定
Changing password for user myname.
New password: (パスワード入力)
Retype new password: (再入力)
passwd: all authentication tokens updated successfully. #成功
[root@ip-10-0-1-86 ~]# sudo visudo #追加したユーザに権限付与

=ファイル編集画面=

## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
myname  ALL=(ALL)       ALL  (←rootの下に追加)

===============

[root@ip-10-0-1-86 ~]# exit #スーパーユーザ ログアウト
logout
[ec2-user@ip-10-0-1-86 ~]$ su - myname #追加したユーザに切替
パスワード:
[myname@ip-10-0-1-86 ~]$

Ruby,Railsのインストール

git,rbenvのインストール

[myname@ip-10-0-1-86 ~]$ sudo yum -y install git
[myname@ip-10-0-1-86 ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv #rbenvインストール
[myname@ip-10-0-1-86 ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build #ruby-buildインストール
[myname@ip-10-0-1-86 ~]$ sudo vi .bash_profile #.bash_profileの編集

=ファイルの編集画面=

export PATH
export PATH="$HOME/.rbenv/bin:$PATH" ← 追加
eval "$(rbenv init -)" ← 追加

=================

[myname@ip-10-0-1-86 ~]$ source ~/.bash_profile #環境変数の反映
[myname@ip-10-0-1-86 ~]$ rbenv -v #バージョン確認
rbenv 1.1.0-2-g4f8925a

rbenvとは?
rbenv + ruby-build はどうやって動いているのか
Linux | source コマンドは何をしているのか

Ruby2.3.3のインストール

[myname@ip-10-0-1-86 ~]$ sudo yum install -y gcc
[myname@ip-10-0-1-86 ~]$ sudo yum install -y openssl-devel readline-devel zlib-devel
[myname@ip-10-0-1-86 ~]$ sudo rbenv install 2.3.3

Rails4.2.7のインストール

railsを展開するディレクトリを生成 (/var/www/railsに展開)

[myname@ip-10-0-1-86 ~]$ cd /var
[myname@ip-10-0-1-86 var]$ sudo mkdir www #wwwディレクトリの生成
[myname@ip-10-0-1-86 var]$ cd www
[myname@ip-10-0-1-86 www]$ sudo mkdir rails #railsディレクトリの生成
[myname@ip-10-0-1-86 www]$ sudo chown -R takahashi:takahashi rails #railsディレクトリのオーナー変更
[myname@ip-10-0-1-86 www]$ cd rails
[myname@ip-10-0-1-86 rails]$ rbenv local 2.3.3 #railsディレクトリのruby変更
[myname@ip-10-0-1-86 rails]$ ruby -v #バージョン確認
ruby 2.3.3p222 (2016-11-21 revision 57164) [x86_64-linux] 
[myname@ip-10-0-1-86 rails]$ gem update --system #gemをアップデート
[myname@ip-10-0-1-86 rails]$ rbenv exec gem install bundler #bundlerインストール 
[myname@ip-10-0-1-86 rails]$ rbenv rehash
[myname@ip-10-0-1-86 rails]$ rbenv exec bundle init #Gemfileが生成される
Writing new Gemfile to /var/www/rails/Gemfile
[myname@ip-10-0-1-86 rails]$ vi Gemfile

=ファイルの編集画面=

#gem "rails"
     ⬇︎
gem "rails", "4.2.7" ← バージョンを追加

=================

[myname@ip-10-0-1-86 rails]$ rbenv exec bundle install --path vendor/bundle
[myname@ip-10-0-1-86 rails]$ rbenv exec bundle exec rails new qiita --skip-bundle -d mysql 
[myname@ip-10-0-1-86 rails]$ rm -f Gemfile; rm -f Gemfile.lock; rm -rf .bundle; rm -rf vendor #Rails をローカルインストールするために使用した Bundler 環境を削除

rbenv rehashは何をやっているのか?

MySQLの設定

[myname@ip-10-0-1-86 qiita]$ cd qiita
[myname@ip-10-0-1-86 qiita]$ sudo yum install mysql-devel #MySQlインストール
[myname@ip-10-0-1-86 qiita]$ rbenv exec bundle install --path vendor/bundle
#bundle install する際に --path vendor/bundle をつけることで、gemのインストール先がRuby環境でなく、 ローカルのvendor/bundle ディレクトリ以下になる
[myname@ip-10-0-1-86 qiita]$ sudo vi .gitignore


=ファイルの編集画面=

/vendor/bundle ← 追加

=================


[myname@ip-10-0-1-86 qiita]$ vi config/database.yml


=ファイルの編集画面=

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: ********(←RDSインスタスのパスワード)
  host: db-id-for-qiita-nginx.xxxxxxxxx.ap-northeast-1.rds.amazonaws.com (←エンドポント)

~省略~

production:
  <<: *default
  database: db_qiita (←DB名)
  username: db_qiita_user (←ユーザ名)
  password: <%= ENV['QIITA_DATABASE_PASSWORD'] %>

=================


[myname@ip-10-0-1-86 qiita]$ vi ~/.bash_profile


=ファイルの編集画面=

export RAILS_ENV="production"
export QIITA_DATABASE_PASSWORD="*********"(←RDSインスタスのパスワード)

=================


[myname@ip-10-0-1-86 qiita]$ source ~/.bash_profile
[myname@ip-10-0-1-86 qiita]$ sudo yum install nodejs --enablerepo=epel #node.jsのインストール
[myname@ip-10-0-1-86 qiita]$ bundle exec rake db:migrate RAILS_ENV=production #dbの生成

SecretKeyの登録・設定

[myname@ip-10-0-1-86 qiita]$ bundle exec rake secret
12345678910abcdefghijklmnopqrstuvwxyz
[myname@ip-10-0-1-86 qiita]$ vi ~/.bash_profile

=ファイルの編集画面=

export SECRET_KEY_BASE="12345678910abcdefghijklmnopqrstuvwxyz"

=================

[myname@ip-10-0-1-86 qiita]$ source ~/.bash_profile

unicornのインストール・設定

[myname@ip-10-0-1-162 qiita]$ vi Gemfile

=ファイルの編集画面=

gem "unicorn"

=================

[myname@ip-10-0-1-86 qiita]$ rbenv exec bundle install --path vendor/bundle
[myname@ip-10-0-1-86 qiita]$ cd config
[myname@ip-10-0-1-86 config]$ touch unicorn.rb
[myname@ip-10-0-1-86 config]$ vi unicorn.rb

unicorn.rbの編集

=ファイルの編集画面=

# -*- coding: utf-8 -*-
worker_processes 4

listen File.expand_path('tmp/unicorn.sock')
pid File.expand_path('tmp/unicorn.pid')

stderr_path File.expand_path('log/unicorn.log')
stdout_path File.expand_path('log/unicorn.log')

preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

=================

Nginxのインストール・設定

[myname@ip-10-0-1-86 config]$ sudo yum install nginx
[myname@ip-10-0-1-86 config]$ cd /etc/nginx/conf.d/
[myname@ip-10-0-1-86 conf.d]$ sudo touch local.conf
[myname@ip-10-0-1-86 conf.d]$ vi local.conf
[myname@ip-10-0-1-86 conf.d]$ sudo vi local.conf

local.confの編集

=ファイルの編集画面=

upstream unicorn {
  server unix:/var/www/rails/qiita/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name 54.238.XXX.XXX; (←EC2のパブリックIP)

  access_log /var/log/nginx/qiita_access.log;
  error_log /var/log/nginx/qiita_error.log;

  root /var/www/rails/qiita ;

  client_max_body_size 100m;
  error_page 404 /404.html;
  error_page 500 502 503 504 /500.html;
  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }
}

================
[myname@ip-10-0-1-86 conf.d]$ cd
[myname@ip-10-0-1-86 ~]$ cd /var/www/rails/qiita
[myname@ip-10-0-1-86 qiita]$ bundle exec unicorn_rails -c /var/www/rails/qiita/config/unicorn.rb -E production -D
[myname@ip-10-0-1-86 qiita]$ sudo service nginx start
Starting nginx:                                            [  OK  ]

rails1.png

nginxは起動しているも、”The page you were looking for doesn’t exist”のエラー表示
これはサーバー内にrailsに表示すべきファイルがないことが原因のよう
そのファイルを作成すれば解決される
Unicornでrailsを動かす、vagrant環境

Hello, Worldの表示

[myname@ip-10-0-1-86 qiita]$ sudo service nginx stop #nginxを一旦停止
Stopping nginx:                                            [  OK  ]
[myname@ip-10-0-1-86 qiita]$ kill -QUIT `cat /var/www/rails/qiita/tmp/unicorn.pid` #unicornを一旦停止
[myname@ip-10-0-1-86 qiita]$ bundle exec rails g controller test
[myname@ip-10-0-1-86 qiita]$ cd app/controllers
[myname@ip-10-0-1-86 controllers]$ vi test_controller.rb


=ファイルの編集画面=

def index
end

=================


[myname@ip-10-0-1-86 controllers]$ cd ..
[myname@ip-10-0-1-86 app]$ cd views/test
[myname@ip-10-0-1-86 test]$ touch index.html.erb
[myname@ip-10-0-1-86 test]$ vi index.html.erb


=ファイルの編集画面=

<div>
Hello, World!!
</div>

=================


[myname@ip-10-0-1-86 test]$ cd ../../..
[myname@ip-10-0-1-86 qiita]$ cd config
[myname@ip-10-0-1-86 config]$ vi routes.rb


=ファイルの編集画面=

root 'start#index'

=================


[myname@ip-10-0-1-86 config]$ cd ..
[myname@ip-10-0-1-86 qiita]$ bundle exec unicorn_rails -c /var/www/rails/qiita/config/unicorn.rb -E production -D
[myname@ip-10-0-1-86 qiita]$ sudo service nginx start
Starting nginx:                                            [  OK  ]

rails4.png
unicorn,nginxの停止

[myname@ip-10-0-1-86 qiita]$ sudo service nginx stop
Stopping nginx:                                            [  OK  ]
[myname@ip-10-0-1-86 qiita]$ kill -QUIT `cat /var/www/rails/qiita/tmp/unicorn.pid`

以上です。
Ruby2.4系だと
warning: constant ::Fixnum is deprecated
warning: constant ::Bignum is deprecated
といった警告ログが出ます。
消せるようですが…
Rails 5.0.1 と Ruby 2.4.0 での警告ログを Forked Rails で消した

大変参考にさせていただきました。
Rails 5 + Unicorn + Nginx 環境構築メモ

続きを読む