Blogs

    in  Scala, Book

    挫折者にも優しい「実践Scala入門」を読んだ

    Scalaは昔少し勉強して挫折した経験があります。月並みにいえば、その時こういう本があったらなぁという感想です。

    そもそも会社でのポジションはSREなので、Scalaを実務で書くことはほとんどありません。Pythonは書くシチュエーションがありますが、関数型を多用することもありませんでした。

    本書の読了が見えてきた頃からこの本を片手にScalaでCodility の問題を解く会社内の集まりに参加させてもらっています。Atcoder にも参加するようになりScalaプログラミングを楽しんでいます。

    構成

    Scalaの基本的な使い方からScalaプロジェクトを管理する上で必須のsbtやユニットテストなども含まれており、業務でScalaを使い始める人もは網羅的な内容となっています。 もちろんこの本を読んだだけでScalaを使いこなせるわけではなく、最終章の締めくくりにあるようにどんどん書いていくことが必要ですが、この本の内容を知っていたらどれだけとっかかりがスムーズだろうとか思います。

    新人が入ってくるこの時期、Scalaをメインの言語として扱っている会社はこの本をとりあえず渡しておくのもありかと思います。

    感想

    そもそものきっかけはScalaでバックエンドを書いている会社に入ったのでScalaかけるようになりたいなと購入しました。 普段はPythonを利用していて、昔はJavaを書いていた頃がありましたが、基本的にはインフラ関連の業務が多く、Terraformと戯れることが多い毎日です。

    この本の前提である1つ以上のプログラミング言語に慣れていることという点はクリアしていますが、第2章のScalaの基礎や第4章のコレクションについてはなかなか苦労しました。ここはひたすら写経ですね。

    苦労したとはいえ、著者の方々がScalaに対して敷居を下げようと努力している所は随所に感じました。 サンプルプログラムや図を用いたり言い換えを行ったりして置いてきぼりにならないような配慮が随所に見受けられました。

    例外処理や並列処理にも言及してあるのも良買ったです。特に例外処理はTryとかEitherとかこれまでの経験とは一味違う機構をScalaは備えているので助かりましたし、そもそも業務では必須の知識なので。本書の構成としても第3章という前の方に置いてある点から重要視しているのかなと思いました。

    並列処理についてもサンプルを写経することで理解が進みました。ただ、競技プログラミングをやっているだけではなかなか登場することがないので、もうちょっと何らかの他の本を読むなりして補強していかないといけないと感じている部分です。

    非機能系としてはログに関しては言及がなかったように思います。是非ログについても取り上げて欲しいですね。

    そして9章、10章では何をとっかかりに調べれば良いかわからない応用的な部分や、より良いコーディングの指針などが書いてあって、これでScalaはとりあえず書けるという気にさせてくれました。 一旦かける気になったら、あとは書くだけ。おかげで前述の通り、CodilityやAtcoderでScalaプログラミングを楽しんでいます。

    まとめ

    初学者に優しい本書だからこそ、誤植が一部あった点が残念でした。 技術評論社さんへ連絡したらすぐにサポートページを更新してくれましたが、そもそもサポートページにたどり着くまでのステップはないに越したことはありません。

    ただ、その点を差し引いても非常に網羅的で、Scalaへのいろんな障害を乗り越える上で非常に有用な本でした。


    in  Terraform

    Terraformのリファクタリング: リモートのアカウントID参照

    Terraformのリファクタリング楽しいですね。ついついいろんなハードコードを変数化するのに熱中してしまいます。

    AWSをTerraformで構築・運用するときAWSのアカウントIDが必要になることがあります。 特にVPC Peering、Private Link, SNS, S3のBucket Policyなど、クロスアカウントで利用できるリソースなどは別アカウントのIDが欲しくなる事があります。 一つならまだしも2つ以上アカウントIDと連携したりすると混乱するので、ここにハードコードしたIDではなくわかりやすい変数名、しかもリモートステートで別アカウントのIDが参照できれば良いなと思いました。

    自アカウントであれば、aws_caller_identityを利用すれば簡単に取得することができます。

    1
    
    data "aws_caller_identity" "self" { }
    

    どこかに上記のような記述をすると、下記のような形でアカウントIDを参照できます。

    1
    
    "${data.aws_caller_identity.self.account_id}"
    

    これにリモートステートの仕組みをプラスすることで、別アカウントのIDを変数化できました。 参照される側のアカウントをA, 参照する側のアカウントをBとしています。

    参照される側のアカウントの設定

    まずは参照されるアカウントAの設定です。 別アカウントBに変数を公開するにはoutputとして登録する必要があります。 今回は変数名をaccount_idとして、AWSのアカウントIDを公開しています。

    1
    2
    3
    4
    5
    
    data "aws_caller_identity" "self" {}
    
    output "account_id" {
      value = "${data.aws_caller_identity.self.account_id}"
    }
    

    また、S3のバケットポリシーを編集して、TerraformのStateをアカウントBが閲覧できるように設定をする必要があります。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "ExampleStatement1",
                "Effect": "Allow",
                "Principal": {
                    "AWS": [
                        "arn:aws:iam::[アカウントBのID]:root",
                    ]
                },
                "Action": [
                    "s3:GetBucketLocation",
                    "s3:ListBucket",
                    "s3:GetObject"
                ],
                "Resource": [
                    "arn:aws:s3:::[アカウントAのStateが入っているBucket]",
                    "arn:aws:s3:::[アカウントAのStateが入っているBucket]"/*"
                ]
            }
        ]
    }
    

    参照される側のアカウントの設定

    参照する側のアカウントBでは参照するアカウントAのStateを参照する設定を追加します。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    data "terraform_remote_state" "account_a" {
      backend = "s3"
    
      config {
        bucket = "[アカウントAのStateが入っているBucket]"
        key    = "state"
        region = "ap-northeast-1"
      }
    }
    

    例えばアカウントAとBのVPC Peeringの設定とかで利用できます。

    1
    2
    3
    4
    5
    6
    
    
    resource "aws_vpc_peering_connection" "acount_a_b" {
      peer_owner_id = "${data.terraform_remote_state.account_a.account_id}"
      peer_vpc_id   = "[アカウントAのVPC ID]"
      vpc_id        = "[アカウントBのVPC ID]"
    }
    

    アカウントAのVPC IDもリモートステートで取得する手もありますが、今回は割愛しました。

    まとめ

    Terraformを書いていていろんなアカウントと連携する設定を書くときに、アカウントID部分が数字の羅列だと混乱するので変数化してみました。 クロスアカウントの機能は充実していますし機能拡張もされ続けています。 何かと使い所はあるのではないでしょうか。


    in  Packer

    Packerを初めて使ってハマったこと

    最近はDockerの環境を運用することが多いので、すっかりEC2に対してAnsibleで環境を構築したりメンテナンスしたりすることが減りました。 それでもAWS上のECSをEC2ベースで運用していて、EC2に監視用のエージェントなどをインストールしているケースなどもあると思います。 例えば自分の場合だとPrometheusの監視モジュール、node exporterを各EC2にインストールしています。

    今後もECS OptimizedなEC2はどんどん更新されていくので、これを機にPackerを導入してベースイメージの作成をすることにしました。 ちなみにPackerを導入しようと思ったのは先日発表されたDockerの脆弱性対応のためEC2をアップデートしないといけなかったことがきっかけです。

    というわけでPackerは初めて触ったわけですが、ほとんどはまることもなくAnsibleをプロビジョナーとする構成を作ることができました。 それでも何点かつまずいた点があったので、残しておきます。

    環境は下記の通りです。

    • Mac OS Mojave 10.14.3
    • Packer 1.3.4
    • Ansible 2.6.5

    Packerのインストール

    Macを前提とします。brewで簡単にインストールできます。

    $ brew install packer
    

    最終的にbuild.jsonは下記のようになりました。 ECS用のEC2の最新版を取ってきてansibleでプロビジョンしています。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    {
        "provisioners": [
    	{
    	    "type": "ansible",
    	    "playbook_file": "./playbook.yml"
    	}
        ],
        "builders": [{
    	"type": "amazon-ebs",
    	"region": "ap-northeast-1",
    	"source_ami_filter": {
    	    "filters": {
    		"virtualization-type": "hvm",
    		"name": "amzn-ami-*-amazon-ecs-optimized",
    		"root-device-type": "ebs"
    	    },
    	    "owners": ["591542846629"],
    	    "most_recent": true
    	},
    	"instance_type": "t3.nano",
    	"ssh_username": "ec2-user",
    	"ami_name": "amzn-ami-amazon-ecs-optimized-20190225",
    	"subnet_id": "subnet-xxxxxxxxxx",
    	"associate_public_ip_address": true
        }]
    }
    

    つまずきを何点か

    default subnetが存在しない場合

    今回、Ansibleでプロビジョンするにあたり、インターネット上のモジュールをダウンロードする必要がありました。 インターネットにアクセスできない状態だとAnsibleが途中で落ちます。

    PackerはPublic IPを取得できるdefault subnet上でAMIを起動しようとします。 default subnetが存在しない環境だとSubnetを指定してあげる必要があります。 そこで上記のようにsubnet_id でsubnetを指定しています。

    PublicIPが自動割り当てされないSubnetの場合

    Subnetの項目のうち、Auto-assign public IPv4 addressがNoになっている場合、Public IPが自動で割り当てられません。 “associate_public_ip_address”: true としてPublic IPが割り当てられるようにしましょう。

    Ansibleを含めたディレクトリ構成

    ansibleのPlaybookをどこに設置すれば良いか、一瞬悩みました、 なんのことはない、build.jsonと同じ階層に置けば良いだけでした。 ディレクトリ構成はこんな感じになりました。

    ├── build.json
    ├── playbook.yml
    └── roles
        └── exporter
            ├── handlers
            │   └── main.yml
            ├── tasks
            │   └── main.yml
            ├── templates
            │   ├── node_exporter.default
            │   └── node_exporter.init.d.amzn.j2
            └── vars
                └── main.yml
    

    まとめ

    Packerはやれることも単純なこともあり、そこまで複雑でハマることもないツールです。 何点かつまずきポイントを載せましたが、エラーメッセージを見ればわかるっていうものでした。 単純ですが、OSのバージョンアップに追従したベースイメージを作成する際に非常に重宝しています。


    in  MySQL

    MySQLのibdataから個別のテーブルデータをリストアする

    バックアップは取っていてもリストアできないと宝の持ち腐れですね。

    ibdataのコールドバックアップは取っていて、サクッと一部のテーブルのデータのみリストアする方法です。 稼働中のMySQLを止める必要がないので、一部のテーブルだけ復旧したい場合や、とりあえず昔のテーブルの状況を見たい場合などに利用可能です。 データベース全体のリストアではないので、リストアの時間を短縮したいときに使えるかと思います。

    やり方としては公式のドキュメントに書いてある通りなのですが、もうちょっと細かくやり方を見ていきます。 innodb_file_per_tableがONになっていて、テーブル毎にibdataが作成されていることが前提になります。

    大まかな手順は下記のようになります。

    1. 復旧したいデータベース・テーブルがない場合はあらかじめ作成しておく
    2. 該当テーブルへの変更をLOCKする
    3. テーブルスペースを削除(ibdファイルを削除)
    4. 復旧したいibdファイルをコピーする
    5. テーブルスペースをインポート
    6. Lockを解除

    下記のバージョンで検証しました。

    • MySQL: 5.6.42
    • CentOS: 7.5

    準備

    テストのためのデータベースとテーブル・テストデータを作成します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    mysql> CREATE DATABASE `test`  CHARACTER SET utf8mb4;
    mysql> use test;
    mysql> CREATE TABLE `test_table` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(255),
      `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    mysql> insert into test_table (name , updated_at, created_at) value ('hoge', now(), now());
    mysql> insert into test_table (name , updated_at, created_at) value ('fuga', now(), now());
    
    mysql> select * from test_table;
    +----+------+---------------------+---------------------+
    | id | name | updated_at          | created_at          |
    +----+------+---------------------+---------------------+
    |  1 | hoge | 2018-12-01 11:11:37 | 2018-12-01 11:11:37 |
    |  2 | fuga | 2018-12-01 11:11:57 | 2018-12-01 11:11:57 |
    +----+------+---------------------+---------------------+
    

    MySQLを止めて、/var/lib/mysql/test/test_table.ibd をバックアップします。

    1
    2
    
    
    $ sudo systemctl stop mysqld.service
    

    リストア

    1. リストア先のデータベース・テーブルの準備

    復旧したいデータベース・テーブルがない場合はあらかじめ作成します。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    mysql> CREATE DATABASE `test`  CHARACTER SET utf8mb4;
    mysql> use test;
    mysql> CREATE TABLE `test_table` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(255),
      `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    もちろんこの段階ではデータは存在しません。

    1
    2
    
    mysql> select * from test_table;
    Empty set (0.00 sec)
    

    2. 該当テーブルへの変更をLOCK

    該当テーブルへの変更をLOCKします。

    1
    2
    
    mysql> LOCK TABLES test_table WRITE;
    Query OK, 0 rows affected (0.00 sec)
    

    3. テーブルスペースの削除

    テーブルスペースを削除(ibdファイルを削除)します。

    1
    2
    
    mysql> ALTER TABLE test_table DISCARD TABLESPACE;
    Query OK, 0 rows affected (0.00 sec)
    

    4. 復旧するファイルの移動

    復旧したいibdファイルをコピーします。

    権限も正しくmysqlユーザーで読み書きできるように設定しましょう。

    1
    2
    3
    4
    5
    6
    
    $ sudo ls -la /var/lib/mysql/test/
    drwx------. 2 mysql mysql  4096 Dec  1 11:44 .
    drwxr-xr-x. 6 mysql mysql  4096 Dec  1 11:42 ..
    -rw-rw----. 1 mysql mysql    67 Dec  1 11:42 db.opt
    -rw-rw----. 1 mysql mysql  8670 Dec  1 11:42 test_table.frm
    -rw-r-----. 1 mysql mysql 98304 Dec  1 11:45 test_table.ibd
    

    5. テーブルスペースをインポート

    テーブルスペースをインポートします。

    1
    2
    
    mysql> ALTER TABLE test_table IMPORT TABLESPACE;
    Query OK, 0 rows affected, 1 warning (0.02 sec)
    

    6. Lockを解除

    ロックを解除します。 また、データが存在することが確認できます。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    mysql> UNLOCK TABLES;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from test_table;
    +----+------+---------------------+---------------------+
    | id | name | updated_at          | created_at          |
    +----+------+---------------------+---------------------+
    |  1 | hoge | 2018-12-01 11:11:37 | 2018-12-01 11:11:37 |
    |  2 | fuga | 2018-12-01 11:11:57 | 2018-12-01 11:11:57 |
    +----+------+---------------------+---------------------+
    

    まとめ

    AWSのRDSなど、マネージドのデータベース環境を利用することが最近は多くなりました。 RDSならリストアもマネージコンソールでぽちぽちやれば簡単に切り戻せて便利ですね。

    とはいえ、まだMySQLを様々な理由で自分達で運用している環境もあるかと思います。 コールドバックアップから手軽にテーブル単位でリストアできるのは便利ですね。


    in  Docker, Book

    「Docker/Kubernetes実践コンテナ開発入門」を読んだ

    OreillyのDocker本に続いて読んだのが、現時点では比較的最近出版された本書でした。

    出来るだけ新しいDocker本で最近の情報で知識をアップデートしたかったのと、Kubernetesの説明が充実してそうというのが本書を手に取った動機です。

    Dockerに正式採用されたKubernetesと、Swarmの違いなんかも体験できればなと思いました。

    内容

    Dockerの基本的な使い方から入るところは、先に読んだOreilly本と同じでしたが、目論見通りOreilly本の時点から色々変化があった事を知ることができました。 大きな変化はKubernetesの正式採用などがありました。 小さな変化ははDockerコマンドについてのコラムにあった docker container というコマンド形式で、 以前は省略していた “container"の指定が現在は推奨されつつある等です。 dockerコマンドではpruneオプションなんかも知ることができました。

    構成としてはDockerの基本を学び、オーケストレーションを体験し、実際に運用する上での知識をつけていく流れなのですが、 サンプルで作成したToDoアプリを題材に、Swarmを経て Kubernetesへ載せていく流れでオーケストレーションについての知識が深められ非常に分かりやすかったです。 また、本番で使っているからこその著者のDockerについての考え方や知見が随所に散りばめられており、 後半の章、特に9章での軽量なDockerイメージの作り方はDockerの内部構造を意識する上でも非常にためになりました。

    特に面白かった所

    docker-composeで作成したToDoアプリが、最終的にKubernetesに載る所が、1つのクライマックスかと思います。 本書ではGCPでKubernetesを利用していますが、私はEKSでサンプルを試していきました。 ロードバランサの取り回しなど、細かい点は考慮が必要ですが、どちらのプラットフォームでも動作させれる点が、Docker, Kubernetesの強みであると実感できました。

    また、コラムが非常に充実していて、日々運用する上で何に困って、どういうツールで解決していったかを知ることができ、運用の雰囲気を知る上で有用でした。

    まとめ

    Dockerの進化を感じつつも、実践という名を冠しているだけあって、著者が本番でDockerを運用し、格闘して得た知識が詰まっています。 私自身はKubernetesに移行するかは検討段階で、まだまだやる事が山積みですが、本書を片手に現在のDockerの運用を改善しつつ、来るべきオーケストレーションに備えたいと思います。