社外の開発メンバーをAWSアカウントに入れるときのIAM設定を考えている

サービスを開発する際に、社外の業者さんに開発をお願いしたり、社外パートナーに開発に参加してもらう、ということがよくあります。 開発に使うAWS環境として、うちの会社で作成したAWSアカウントに入ってきてもらうこともあるのですが、このときにAWSアカウントの管理者として社外の開発メンバーにどのような権限を持たせるのが良いか、それをどう実現するのが良いか、いつも悩みます。

このエントリでは現時点での考えと実装方法をまとめておこうと思います。

課題

私が関わる案件で社外の開発メンバーに協力を仰ぐ場合、大抵はPoCから始まるような新規サービスの構築案件となるためAWSのどのサービスを使うか最初からすべて決まっていることは稀です。
最初は ALB + EC2 + RDS で作り始めたシステムにDynamoDBが導入され、AWS IoT coreが導入され、Kinesis Stream が導入され、、、と拡張されていったり、API Gateway + Lambda でサーバーレス構成に切り替えたり、と様々な状況が発生します。また、AWSから新サービスが発表されたら検証のために触ってみたりもします。

そういう事情があるので、IAMポリシー設計の超大原則である「基本は全拒否、必要なサービスへのアクセスのみ許可」というホワイトリスト方式では柔軟性に欠け効率が悪くなるケースが多いです。運用フェーズに入るとホワイトリスト方式でまわることも多いのですが、開発中はある程度自由にAWSを触ってもらえる権限を付与しておきたいところです。

かといって、闇雲にAdministrator権限のように本当になんでもできる権限を付与するのもリスクが大きすぎます。最低限のセキュリティは維持しつつ、自由に開発してもらうにはどのくらいの権限を残し、どのような権限を制限するのが良いでしょうか。

基本方針

  • 社内外に関わらず、AWS利用者が個人レベルで識別できるようにIAMユーザを作成する
  • 基本的に、ほぼすべてのAWSのサービスを自由に使える権限を開放する
  • ただし、IAM関連やBilling関連、CloudTrailやAWS ConfigなどなどAWSアカウントの管理に関わる部分は触れないようにする
  • IAMだけで制御するのが難しい点は、AWS Config Rulesなどを活用して問題をすぐ検知できるようにする

今のところ、この辺を落とし所としています。

具体的な設定内容

入り口

  • 社外の開発メンバーには個人ごとにIAMユーザを作成する
  • IAMユーザはMFA設定を必須とし、パスワードポリシーもある程度の複雑さを担保できるレベルに縛る
  • IAMユーザには自ユーザのアクセスキー/シークレットキーを発行できる権限を与え、必要になったら自分でキーの発行を実施してもらう
  • IAMユーザに直接IAMポリシーを付与せず、グループに所属させた上でグループにIAMポリシーを付与して権限を管理する

いきなりベストプラクティスから外れていますが、社外メンバー用に IAMユーザを作成する運用です。

セキュリティ的には外部ID (ExternalId) *1を必要とするIAMロールを用意して、社外開発メンバーには自社のAWSアカウントのIAMユーザもしくはIAMロールからAssumeRole API でロールの切り替えをしてもらえばベストなわけですが、外部IDを必要とするIAMロールへはAWSマネジメントコンソール上から切り替えることができません*2。社外の開発メンバーが皆AWSのIAM周りに詳しく、しかもAWSマネジメントコンソールを使わず開発を進められるような状況はそうそう無いと思います。普通はマネジメントコンソールも使いたいですよね。

そうなると、Switch Role用のIAMロールを用意するか、IAMユーザを用意するか、という選択になります。自社のAWSアカウントからSwitch Role して入ってくるほうが本人たちは楽だと思うのですが、今度は元のアカウントのIAMユーザがセキュリティ上どれだけ保護されているのかがわからないため、うちの会社のAWSアカウントでIAMユーザを作成してそれを使ってもらっています。こうすることで、MFAが設定されているか、パスワードやアクセスキーのローテーションは行われているか、最後にアクセスしてきたのはいつか、といった情報を監査できるようになります。なお、社内のメンバーについては、元のAWSアカウントのIAMユーザの状況が把握可能な場合はむしろSwitch Roleを推奨しています。また社外のメンバーでもうちのAWSアカウントを複数使う場合は二つ目以降は Switch Role を使ってもらっています。基本的に開発環境用AWSアカウントにIAMユーザーを作成し、QA環境や本番環境のAWSアカウントには Switch Role で入る、という感じになります。

アクセスキーの運用に関しては、AWSに慣れている人は何の問題もなく自分でアクセスキーを発行して管理・運用できますし、逆にAWSに慣れてない人に無闇にアクセスキーを渡すとソースコード内にハードコードしてしまったりIAMロールとの使い分けが曖昧になってしまったりすることがあるため、自分でIAMユーザ・アクセスキーとIAMロールの使い分けなどを学んだ後に必要性を感じたらアクセスキーを発行してもらう、という運用にしています。

IAMユーザに直接IAMポリシーを付与しないでグループ単位で権限を管理しているのは、権限を変更するときにIAMユーザによって抜け漏れが発生してしまうのを防ぐのと、人の入れ替わりがあるときに権限付与の手間を省く意味があります。大体、1社につき開発者用グループと閲覧専用グループを用意する感じですが、閲覧専用グループはあまり使われる機会がないな、という感触です。AWSマネジメントコンソールに入って来るような人は大抵開発者なので開発者用グループを使いますし、開発フェーズでは「CloudWatchだけ見たい」みたいなニーズに遭遇したことがありません。

これ以降の項ではこの「開発者用グループ」にどのような権限を与えているかを示します。

IAMポリシー全体

gist.github.com

1つのIAMポリシーにまとめずとも、後半のいくつかのStatement は別のIAMポリシーに切り出して、ケースバイケースで一部許可したりなど調整できるようにしてもよいと思います。今のところ、だいたいこんな感じにしている、というサンプルです。

基本は全許可

IAMはデフォルトで全ての操作が禁止されている*3ため、4行目からの最初のStatementでは、一部のAWSサービスについての操作(Action) 以外を一括で明示的に許可しています。 一部制限をかけている操作は、AWS Organizations や AWS Single Sign-On など、まあ普通は社外の開発メンバーが気にする必要はないよね、というサービスを列挙しています。

        {
            "Effect": "Allow",
            "NotAction": [
                "organizations:*",
                "sso:*",
                "sso-directory:*"
            ],
            "Resource": "*"
        },

案件によっては「このAWSサービスは絶対に使わないだろう」というものがあるかもしれません。その場合は、触る必要の無いAWSサービスをこのStatement の NotAction に追加してしまうと楽ちんです。たとえば、うちの会社ではAWS Ground Station は当面使うことはなさそうですから、ここでNotResourceに追加してしまってもいいかもしれません。

ここから先のStatementでは、禁止したい行為を一つ一つ制限していくことになります。

勝手に「入り口」のルールを変えてはならない

続くStatement では先述した「入り口」のルールが勝手に壊されないようにIAM関連の特定の操作を禁止しています。

        {
            "Effect": "Deny",
            "Action": [
                "iam:AddUserToGroup",
                "iam:AttachGroupPolicy",
                "iam:AttachUserPolicy",
                "iam:CreateGroup",
                "iam:DeleteGroup",
                "iam:DeleteGroupPolicy",
                "iam:DetachGroupPolicy",
                "iam:PutGroupPolicy",
                "iam:CreateUser",
                "iam:DeleteUser",
                "iam:DeleteUserPolicy",
                "iam:DetachUserPolicy",
                "iam:PutUserPolicy",
                "iam:RemoveUserFromGroup"
            ],
            "Resource": "*"
        },
  • 勝手に新しいIAMユーザやIAMグループを作ったり、既存のIAMユーザやIAMグループを消してはならない
  • 勝手にIAMユーザをIAMグループに加えたり、IAMユーザをIAMグループから抜いてはならない
  • 勝手にIAMユーザやIAMグループにIAMポリシーを付与したり削除したりしてはならない

ここに穴があると、自分でより強い権限のIAMポリシーを作成して自分のIAMユーザやIAMグループに付与する、ということができてしまいます。
上記のポリシー設定だと、自分でIAMポリシーを作ることは可能ですがIAMユーザやIAMグループに付与(Attach)することができないため、結果として「人」に対する権限付与の自由を制限していることになります(なってると思う…)。こんなめんどくさいことしなくてもIAMポリシーを作ること自体を禁止すればいいのでは?と思えるのですが、そうすると今度はEC2やLambdaなどのAWSリソースに割り当てるIAMロールに付与するIAMポリシーが作れなくなってしまい、開発に支障が出てしまいます。そのため、あくまで「人」に対して意図しない強い権限を付与できないところを防衛ラインとしています(ここのStatementだけでは穴があるので、次のStatement+αでその穴を潰しています)。

勝手にIAMロールに強い権限のIAMポリシーを割り当ててはならない

続く2つのStatementでは、先のStatementで埋められなかった穴を多少埋めています。
IAMロールの作成は自由にできるよう許可しているため、例えばEC2に割り当てるIAMロールに (AWSが標準で用意しているAWS管理ポリシー) AdministratorAccess のような強力なIAMポリシーを付与することができてしまいます。すると当該EC2からはすべてのAWS操作が可能になってしまうので、これを禁止します。

        {
            "Effect": "Deny",
            "Action": [
                "iam:AttachRolePolicy",
                "iam:CreatePolicy*"
            ],
            "Resource": "arn:aws:iam::*:policy/Administrator*"
        },
        {
            "Effect": "Deny",
            "Action": [
                "iam:AttachRolePolicy",
                "iam:CreatePolicy*"
            ],
            "Resource": "*",
            "Condition": {
                "ArnEquals": {
                    "iam:PolicyARN": [
                        "arn:aws:iam::*:policy/Administrator*"
                    ]
                }
            }
        }
  • Administrator* のような強力なAWS管理ポリシーや自作IAMポリシーをIAMロールに付与してはならない (2個めのStatement)
  • 勝手に Administrator* のような名前のIAMポリシーを作ってはならない (1個めのStatement)

実はこれでもまだ穴があります。

例えば AdministratorAccessと同様の内容のIAMポリシーを自作し、EC2に割り当てるIAMロールにそのIAMポリシーを付与すると、結局当該EC2インスタンスからはすべてのAWSの操作が可能になってしまいます。これをIAMだけで防ぐことも Permissions Boundary を活用することで可能だとは思うのですが、IAMロールを作成するときに制約*4ができてしまったりして運用が複雑化するため、IAMで頑張ることは諦めました。 そのかわり、AWS Config Rules で検知することですぐに潰せるようにしています。AWS Config マネージドルールのiam-policy-no-statements-with-admin-access を参考に、IAM関連の特定操作が可能だったり、強い権限のIAMポリシーが作成されたら検知できるようにします。

勝手に自分以外のIAMユーザのMFA設定を変えてはならない

次のStatement では、自分のIAMユーザ以外のIAMユーザのMFA設定を勝手に変えられないように制限をかけています。
他人のIAMユーザのMFA設定を自由に変えられてしまうと、設定されているMFAデバイスとの紐づけを解除して自分が持っているMFAデバイスと紐付けることで他人のIAMユーザをロックすることができてしまいます。共同開発しているような状況で他人のIAMユーザをロックしたくなる動機があるのかどうかは謎ですが。

        {
            "Effect": "Deny",
            "Action": [
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice",
                "iam:DeactivateMFADevice",
                "iam:DeleteVirtualMFADevice"
            ],
            "NotResource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        }

ミソは自分のIAMユーザに対してはMFA設定を行える権限を維持するために NotResource で「自分のIAMユーザ については操作禁止対象から除外」している点です。二重否定になりややこしいのですが、IAMポリシーでは明示的な拒否(Deny)は明示的な許可(Allow)よりも強い*5ため、"Resource": "*" に対してDeny したあと "Resource": (自分のIAMユーザとMFAデバイス) にAllowの設定を書いても効かず(DenyとAllowの順序を逆にしてもダメ)、こうするしかないのかなぁと思っています。より良い書き方がありそうな気はしているのですが...。 なお、自分のIAMユーザに対してのMFA設定権限がないと、「IAMユーザにはMFA設定を必須とする」という方針と矛盾してしまいます。

勝手に自分以外のIAMユーザのアクセスキーやパスワードを作ったり消したりしてはならない

次のStatement では、自分のIAMユーザ以外のIAMユーザの認証情報をいじれないように制限をかけています。

        {
            "Effect": "Deny",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:UpdateAccessKey",
                "iam:ChangePassword",
                "iam:DeleteLoginProfile",
                "iam:UpdateLoginProfile",
                "iam:DeleteSSHPublicKey",
                "iam:UpdateSSHPublicKey",
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential",
                "iam:DeleteSigningCertificate",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "NotResource": [
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },

こちらもMFA関連の設定と同じく、ミソは自分のIAMユーザに対してはアクセスキーの発行やパスワードの変更を許可しなければならないため、NotResource で自身のIAMユーザは制限の対象外としている点です。

この辺までは割と汎用的に使える設定かと思っていますが、これ以降のStatementは社外の開発メンバーにお願いしている業務内容によっては一部許可しても良いのでは?というものも含まれています。 なので、これ以降のStatement は別々のIAMポリシーに切り出して、例えば会社ごとに足したり引いたり調整できるようにしてもよいと考えています。

勝手にAWSアカウントの設定を変更してはならない

次のStatement は、AWSのアカウント情報(「マイアカウント」画面)の参照や支払い方法の変更、リージョンの有効化・無効化などを制限するためのものです。

        {
            "Effect": "Deny",
            "Action": [
                "aws-portal:*Account",
                "aws-portal:ModifyBilling",
                "aws-portal:*PaymentMethods",
                "account:EnableRegion",
                "account:DisableRegion"
            ],
            "Resource": [
                "*"
            ]
        },

アカウント情報は、場合によっては参照されても困らないこともあるかと思いますが、連絡先などを伏せたい場合が多いので制限をかけています。危険なのは aws-portal: ModifyAccount で、これはAWSアカウントの設定情報を変更ができてしまうので確実に禁止しておくことにしています。 同様に、支払い方法を勝手に変更されるのも困るので、aws-portal:ModifyBilling Action も禁止しています。
リージョンの有効化・無効化は現時点(2019年9月) では 中東 (バーレーン) と アジアパシフィック (香港) しか切り替えることができません。今後、現在は無効化できないリージョンが無効・有効を切り替えられるようになったりした場合などに混乱を防ぐために禁止しています。(意図しないリージョンにAWSリソースを作ってしまったり、コッソリ誰も使っていないようなリージョンにAWSリソースを作成されるのを防ぐため、現在は無効化できないリージョンも将来 無効・有効が選べるようになるといいな、と期待しています)

勝手にAWS CloudTrail を無効化したり証跡情報を削除してはならない

次のStatementはAWS CloudTrail に関するものです。

        {
            "Effect": "Deny",
            "Action": [
                "cloudtrail:StopLogging",
                "cloudtrail:DeleteTrail"
            ],
            "Resource": "*"
        },

AWS CloudTrailは基本的に私が関わる全AWSアカウントの全リージョンで有効化しています。いつ・誰が・何をしたか の証跡情報を確実に残しておくため、AWS CloudTrail を勝手に停止されたり、証跡情報を削除されたりしないように制限をかけています。AWS CloudTrail の状態監視にはAWS Config Rules のマネージドルールもいくつか用意されている*6ので、合わせて活用しています。
AWS CloudTrail の証跡情報はS3やCloudWatch Logsに保存できるのですが、それらのバケットやログストリームについても削除されないよう、同じAWSアカウント内にある場合はIAMポリシーで保護しておくのが良いでしょう。

勝手にAWS Config の記録を停止したり構成管理情報を削除してはならない

次のStatementはAWS Config に関するものです。

        {
            "Effect": "Deny",
            "Action": [
                "config:Delete*",
                "config:StopConfigurationRecorder"
            ],
            "Resource": "*"
        }

AWS Configは、全てのAWSリソースタイプに対応しているわけではありませんが、IAMやEC2、S3などなどメジャーなサービスの構成変更履歴を自動で記録してくれるサービスで、AWS CloudTrail と同様に基本的に全リージョンで有効化しています。これも勝手に構成変更の記録を停止されたり削除されたりしては困るので、制限をかけています。

その他

今回記載したIAMポリシーの例には出ていない Amazon GuardDuty や Amazon Macie など、セキュリティ関連のAWSサービスではアクセス権限を管理したいものがありそうですが、この辺はまだ案件によって導入できてたりできてなかったりするので割愛しました。

まとめ

AWSアカウントの管理者として、社外の開発メンバーに対してどのようなアクセス制限が必要か、またそれをどのように実現するかについて、現時点での考えをまとめました。

考慮が漏れている点やもっとキレイに実現できる方法などなどたくさんありそうなので、いいやり方をご存知でしたらフィードバックをいただけたら幸いです。

参考

AWSアカウント内の全リージョンのLambda関数リストを出力する

少し前になりますが、AWS Lambdaのランタイムのうち、Node.js 6.10のサポート終了がアナウンスされました。

4月30日にNode.js 6.10のサポートが終了になります - Forums

こういったアナウンスが出たときなどに、自分が関わっているLambda関数の全リストを確認したくなるのですが、複数のAWSアカウントを運用していると、全AWSアカウントの全リージョンをManagement Consoleでポチポチ確認するのは現実的ではありません。
せめて1つのAWSアカウントの全リージョンのLambda関数のリストくらいはパパっと取得したいものです。

というわけで、簡単なシェルスクリプトを書いてみました。

実行するには以下の条件を満たしている必要があります。

  • Bash が実行できる
  • AWS CLI がインストール済み
  • ec2:DescribeRegions や lambda:ListFunctions が実行可能な権限を持ったIAMユーザ or IAMロールのクレデンシャルが設定済み

手元のPCや開発機で実行する前提で書いたので、第1引数にProfile名を渡せばAWS CLIを実行するときのprofileを変更できるようにしました。引数なしで実行すると、[default] profileを使用します。

AWSの全リージョンのリストを取得する

ちょくちょく新リージョンが開設されたりするので、スクリプト内にべた書きでリストアップするのもいかがなものか、と思ってWebを検索していると、こんな記事を見つけました。

dev.classmethod.jp

なるほど!EC2 APIの DescribeRegions ActionでEC2がサポートされているリージョンの一覧を取得する、という方法です。
確かにAWSの公式情報を見てもEC2をサポートしていないリージョンは無いですし、AWSの全リージョンのリストを取得するのはこれでバッチリです。

aws.amazon.com

ちなみに、本日 2019/04/18 時点だと大阪リージョンだけAWS Lambdaをサポートしていないようです。へぇー。

Lambda関数のリストを取得する

こちらはLambdaのAPIにずばり ListFunctions というActionがあり、これを利用します。

list-functions — AWS CLI 1.16.142 Command Reference

AWS CLI の--query オプションで出力内容を FunctionName、Runtime、LastModifiedそしてDescription に絞り、Linuxcolumn コマンドで見た目の整形をしています。columnコマンドで整形せずAWS CLI のTEXT出力形式のまま出力すればタブ区切りになるので、出力結果をExcelなどに展開する場合にはそのほうが都合がよいかもしれません。sort もこのスクリプトの例ではFunctionaNameでソートをかけていますが、LastModified などでソートしてもよいでしょう。
これを、先の手順で取得したAWSの全リージョンに対して for 文内で順次実行しているだけです。

実行結果イメージ

このような感じで、リージョン毎に FunctionName、Runtime、Last Modified、Description をリスト表示します。

$ bash lambda-function-list.bash
[eu-north-1]
---------------------
[ap-south-1]
---------------------
[eu-west-3]
---------------------
[eu-west-2]
FunctionName-A    nodejs8.10  2018-07-24T09:41:13.364+0000
FunctionName-B    nodejs6.10  2018-07-31T10:46:06.598+0000  Description-B
FunctionName-C    nodejs6.10  2018-10-24T03:02:12.718+0000  Description-C
---------------------
[eu-west-1]
FunctionName-D-in_eu-west-1    nodejs6.10  2019-02-07T11:25:14.242+0000 Description D
FunctionName-E-in_eu-west-1    nodejs6.10  2019-02-07T11:21:40.267+0000  
FunctionName-F-in_eu-west-1    nodejs6.10  2019-02-07T11:20:07.790+0000 Description F
---------------------
[ap-northeast-2]
---------------------
[ap-northeast-1]
(以下略)

まとめ

実際に自分が関係しているいくつかのAWSアカウントで実行したところ、結構わさわさNode.js 6.10のLambda関数があることがわかりました😇

「Knativeの歩き方 KubernetesからServerlessを訪ねて」を写経しつつ読んだ

先週、San Franciscoで開催されたGoogle Cloud Nextに参加した。 f:id:kmiya_bbm:20190416233348j:plain

参加できることが決まった3月上旬から、せっかく行くなら予習しとこう!と前から気になっていたKubernetesを調べたり触ってみたりして勉強していた(過去の記事を参照)のだけれど、その中でKubernetes 上にサーバーレス環境を構築するKnative なるOSSがあると知り、興味本位で情報を集めていた。

イムリーにServerless Meetup Tokyo #11 でKnative についての発表があったようで、こちらの資料は概要を掴むのにとても良かった。
入門 Knative 〜KubernetesとServerlessとの出会い〜 / getting started with knative - Speaker Deck

Kubernetes や Knative を調べてからGoogle Cloud Nextに参加したのは結果的には大正解で、今回のNextで発表された Anthos (実際には昨年発表された Cloud Services Platformのリブランドのようだが) はKubernetes を前提としているし、Cloud Run も KubernetesやKnativeをベースとしたサービスだったため、予習しないで行っていたら完全に置いてけぼりだったと思われる。 Cloud Run がとてもおもしろそうだったので、日本に帰ったらKnative と共にもう少し詳しく調べてみよう、と思っていたところにこちらの記事を発見した。

toshi0607.com

出発前の予習でお世話になった資料を作られた方が技術書典 #6 で Knative の本を出されると聞き、早速BOOTHで購入、写経してみた次第。 地方在住の子育て世帯ということもあり都内で週末に行われるイベントにはほとんど参加できないが、こうして手軽に電子版を購入できるのはありがたい。

toshi0607.booth.pm

感想

目次や主な内容については上にリンクを貼った著者様のブログ記事にて言及されているので割愛するが、Knative v0.5 で大きく仕様が変わった Eventing まわりの内容に追従できているのがまずすごい。Eventingについては Nextでもセッションで詳しく取り上げられていたのでこのあたりの動画と合わせて復習すると良さそう。

www.youtube.com

この本はとにかくハンズオンのための説明が丁寧で、写経していて詰まるところはほぼなかった。ほんの一部の手順で思う通りに実行できなかった箇所があった(最後の Kubernetes イベントを拾って表示するところ)ので、勉強のため引き続き調査して解決したい。
また、一部コマンド実行例に誤植があったので記録しておく。p.25 と p.37 の環境変数 KNATIVE_INGRESS を設定する箇所。

$ export KNATIVE_INGRESS=$(kubectl get svc istio-ingressgateway --namespace istio-system --output 'jsonpath={.status.loadBalancer.ingress[0].ip')}

↓

$ export KNATIVE_INGRESS=$(kubectl get svc istio-ingressgateway --namespace istio-system --output 'jsonpath={.status.loadBalancer.ingress[0].ip}')

2019/04/18 追記

著者様が光の速さで正誤表ページを用意しつつ、修正版v1.0.1もリリースしてくださいました!対応速っ!!ありがとうございます!


なんというか、著者様の愛をとても感じる本で、全体を通して構成や文章も商業書籍のようによく練られていて、するする読めてしまった。
するする読めるし写経も詰まることがなかったおかげで、ちゃんと理解できていなかったり知識が定着していない箇所が多そうなので、折に触れて読み返そうと思う。

Knative についてはそれ自体も興味深いが、Cloud RunをはじめTriggerMeshが公開したKubernetes 上にAWS Lambda互換環境を構築するOSS 「Knative Lambda Runtime (KLR)」 など、Knative をベースにして展開されるサービスがとてもおもしろくなりそうなので、この周辺の技術はウォッチしていきたい。

参考資料

Google Cloud Next 19でも Knative関係のセッションがいくつかあったのでリンクを貼っておく。
質疑応答コーナーが面白いセッションもあったが、動画ではカットされていてちょっと残念。

www.youtube.com

www.youtube.com

[コンテナ再入門]Ubuntu 18.04 で写経しつつ「Docker/Kubernetes 実践コンテナ開発入門」を完走した

なんやかんやハマりながらも、なんとかUbuntu18.04で写経しつつ「Docker/Kubernetes 実践コンテナ開発入門」を最後まで読み進めました。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

本についての感想

Docker ~ Docker Compose の内容までは過去にも調べて使ってみたことがあったのですぐに理解できたし、Docker Swarmも初めて触ったけれど書籍の解説がわかりやすく概要をつかむことができた。しかし、5章以降 Kubernetes の内容に入ってからは初めての概念がたくさん出てきて読み進めるのに時間がかかってしまった。それでも、Kubernetes がDocker Swarmよりもさらに運用に有用な機能が多く実装されていることや、Helmなどの便利な周辺ツールがたくさんあること、Google Kubernetes Engine (GKE) がKubernetesを使うにあたってどのくらい便利なのか、などについて、順を追ってわかりやすく書かれていてとても勉強になった。ログの取り回しやCI/CDをどうするのが良いか、など運用上の疑問点についても現時点でのベストプラクティスがまとめられており、導入を進める立場の視点で助かる内容が多かった。 なかなかウェブ上の断片的な記事だけではここまで順を追ってコンテナ~コンテナオーケストレーションの流れや主要なツールの使い方を学ぶことは難しいので、個人的に非常にいいタイミングで良い本に出会えたと思う。

また、書籍内ではコラム内で軽く紹介されていただけだったが、Knative がとても面白そうなので別途調べていこうと思う。

Kubernetes についての所感

オートスケーリングやデプロイの仕組みが非常に洗練されていて、なるほど流行るのも納得、という感じだったが、よく言われるように学習コストは非常に高そうだなという感触を持った。今いるチームはアプリケーションのコードを書く開発者は多くいるが、サーバやネットワークを構築・運用したりシステム監視設計や運用設計をしたりする、いわゆるインフラ寄りのメンバーはほとんどいないし、運用しているアプリケーション自体もまだまだ規模が小さいので、kubernetes を導入したりそもそも開発環境からコンテナ化するメリットに対して導入コストがまだ大きすぎるかなという気がする*1。 なので、チームで導入するタイミングはまだしばらく待ちつつ、個人的には興味深い分野なのでコンテナやオーケストレーションツールの動向を追っていきたいな、という感じ。

*1:うちでは割とサーバーレス構成を採用しているが、これは運用のコスト削減のためなら開発のコスト増は飲めるようなメンバー構成なため

[コンテナ再入門]Ubuntu 18.04 に Kubernetes環境を構築する

Ubuntu18.04で写経しつつ「Docker/Kubernetes 実践コンテナ開発入門」を読み進めている。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

書籍ではDocker for Windows/Mac をサポート対象としており、それ以外のLinuxなどの環境で写経するには自分で環境構築を行う必要がある。
5章からはKubernetesの話題に入っていくのだが、Ubuntu18.04 上にKubernetes環境を構築するのに結構時間がかかったのでメモを残す。

環境

  • ホスト Ubuntu16.04 Desktop 64bit
  • VM: Ubuntu18.04 server 64bit (Vagrant + VirtualBox)
    • Docker 18.09.3

kubeadm のインストール

ウェブでLinux環境へのKubernetes環境構築について検索するといろんな方法がヒットするが、結論としては公式ドキュメントで書かれている通り kubeadm を使って構築した。

kubernetes.io

「Before you begin」の項目のうち、自分のようにローカルPC上の仮想マシンで構築を進める場合、ハマりそうなのは以下の3点かと思う。

  • 2 GB or more of RAM per machine (any less will leave little room for your apps)
  • 2 CPUs or more
  • Swap disabled. You MUST disable swap in order for the kubelet to work properly.

SWAPをOffにするのは、以下のコマンドを実行する。このコマンドではOS再起動時に設定がもとに戻ってしまうが、とりあえず環境構築するには充分である。

sudo swapoff -a

docker はすでにインストール済みとする(関連記事 )。
あとは、ドキュメントに書かれているとおり以下のコマンドを実行して kubelet、kubeadm、 kubectl をインストールできる。

sudo apt-get update
sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
sudo cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

kubeadm で single master cluster を構築する

続いて公式ドキュメントのこのページの作業を進める。

kubernetes.io

Pod間通信用のアドオンが何種類かあって選べるのだが、ウェブで検索していてよく見かけた Flannel を使うことにした(よくわかってない)。
Flannelを使う場合のインストール手順に書かれているとおり、--pod-network-cidr=10.244.0.0/16 とオプションをつけてkubeadm init を実行する。

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

成功すると、その後実行するべきコマンドが表示されるのでその通り実行する(公式ドキュメント内にも記述がある)。

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Flannel をインストールする。

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml

とりあえずここまで作業すれば、書籍の続きの内容であるダッシュボードのインストールに進める。

[コンテナ再入門]ホストにMySQLがインストールされていると、MySQLのコンテナが起動しないことがある

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門 の4章でMySQLのDockerコンテナを立ち上げる手順があるのだが、そこで詰まったのでメモ。

状況

  • ホスト Ubuntu18.04 server 64bit
  • Docker 18.09.3

Docker in Dockerの構成で、mysql:5.7 イメージを元にしたMySQLのDockerコンテナを起動させようとしたところ、起動しては以下のエラーを吐いてコンテナが停止する、という挙動を繰り返した。

$ docker container exec -it manager docker service lslogs todo_mysql_master
todo_mysql_master.1.(略)    | ERROR: mysqld failed while attempting to check config
todo_mysql_master.1.(略)    | command was: "mysqld --verbose --help"
todo_mysql_master.1.(略)    | 
todo_mysql_master.1.(略)    | mysqld: Can't read dir of '/etc/mysql/conf.d/' (Errcode: 13 - Permission denied)
todo_mysql_master.1.(略)    | mysqld: [ERROR] Fatal error in defaults handling. Program aborted!

「Docker in Docker」「MySQL」「起動しない」といったワードで検索していたところ、こちらの記事を発見。

qiita.com

まんまこのシチュエーションであった。

先に結論

コンテナを実行するホスト(母艦) にMySQLがインストールされていると、priviledged モードを有効にしたコンテナ上でのMySQL起動がAppArmorという強制アクセス制御システムの働きにより阻害されることがある。

何が起きていたか

書籍の手順では、Docker Swarmのような複数台のホストにまたがるコンテナオーケストレーションの挙動を1台のホスト(=読者のノートPCなど)上で再現して体験させるため、Docker in Dockerで「Dockerをホストするためのコンテナ群」の上に「MySQLやNginxなど本来複数台のホストにまたがって展開させたいコンテナ群」を構築することになっていた(3章 3.5.節以降)。
このDocker in Dockerで「Dockerをホストするためのコンテナ群」を構築する際、「Dockerをホストするためのコンテナ群」に本来のホスト(=読者のノートPCなど)とほぼ同じ権限をもたせるためにpriviledged モードを有効にしていたようだ (ようだ、というか、「Dockerをホストするためのコンテナ群」を起動するためのdocker-composeのYAMLファイルにはちゃんとprivileged: trueと書いてあったのだが、あまり意味がわかっておらず読み飛ばしていた。。。)。

docs.docker.com

こちらのドキュメントの「Runtime privilege and Linux capabilities」の項を読むと、priviledged モードというのはコンテナホストのデバイス等にアクセスを許可するためのフラグらしい。これがないと Docker in DockerのようにDockerコンテナ内部からDockerを起動させるような芸当ができない。
基本的にはホストとコンテナ間は疎結合である方が望ましいので、priviledged モードは開発環境以外では使わないものらしい。

AppArmorとは

www.usupi.org

AppArmor とは、プログラム単位でMAC(Mandatory Access Control - 強制アクセス制御) を行うためのセキュリティ機構です。
MACとは、従来のファイルのパーミッションの設定等とは関係なく、 強制的にアクセス制限を設けることができる機構です。

役割としてはSELinuxなどと同じようなもので、AppArmorの場合はプログラム単位で制限がかけられるらしい。 今回は、MySQLがAppArmorの管理対象だったところに Docker in Docker上でさらにMySQLを実行しようとしたため、AppArmorが「許可していないリソース上で MySQL が動作している」と認識して実行をブロックした、ということだったようだ。

対処

前述のQiita記事 Docker で MySQL コンテナーがなぜか起動できない問題 - Qiita に記載されていた2つの解消方法のうち、AppArmor の監視から MySQL を除外する方法を採った。

$ sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/
$ sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld

教訓

書籍やハンズオンで環境構築を行う場合は、なるべくまっさらなVMなどを用意してやりましょう。

…そうはいっても毎回そう上手く環境を用意できないかもしれないので、後で同じような状況で詰まる人(将来の自分を含む)の役にたてばと簡単にメモをまとめました。

参考にさせていただいたURL

[コンテナ再入門]Ubuntu 18.04 に Docker Compose をインストールする

kmiya-bbm.hatenablog.com

これの続き。

Docker Composeのインストールも公式ドキュメントに沿って進めればよい。

docs.docker.com

  • Githubのレポジトリで最新のバージョンを確認する。

  • 以下のコマンドで最新版のdocker-composeをダウンロードする

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  • ダウンロードしたdocker-compose に実行権を付与する
$ sudo chmod +x /usr/local/bin/docker-compose
  • 自分の環境では /usr/local/bin にPATHが通ってなかったので、公式ドキュメントの通りシンボリックリンクを張った
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
  • インストールできたか確認する
$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01

よさそう!