Foreverly

メモ帳

Terraform and CORS-Enabled AWS API Gateway and AWS WAF

この記事はAWS WAF と API Gateway endpoint で CORS を有効化について。

以下にsampleを載せてあります。

REST API リソースが API 独自のドメイン以外のドメインからリクエストを受け取る場合、 リソースの選択されたメソッドで Cross-Origin Resource Sharing (CORS) を有効にする必要があるので、

CORS 対応のために、今回は以下のレスポンスヘッダーを付与させます。

API GatewayREST APIの設定

WAFとAPI Gatewayをつなげるので、 endpoint_configurationの設定が必要になる。

    ## API Gateway - Rest API
    resource "aws_api_gateway_rest_api" "rest_api" {
      name                     = "${var.env}-${var.name}-api-gateway"
      description              = "${var.env}-${var.name}-api-gateway"
      minimum_compression_size = 0
    
      endpoint_configuration {
        types = ["REGIONAL"]
      }
    }

API GatewayのResourcesの設定

TerraformのProviderを参考に作成するだけで大丈夫。

    ## API Gateway - Resources
    resource "aws_api_gateway_resource" "resource" {
      depends_on  = ["aws_api_gateway_rest_api.rest_api"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      parent_id   = "${aws_api_gateway_rest_api.rest_api.root_resource_id}"
      path_part   = "${var.name}"
    }

API GatewayのMethodの設定

response_modelsresponse_parameters をつける以外は

TerraformのProviderを参考に作成するだけで大丈夫。

CORS対応のために Access-Control-Allow-Originをレスポンスヘッダに付与させます。

    ## API Gateway - Method Setting
    # post
    resource "aws_api_gateway_method" "method_post" {
      depends_on       = ["aws_api_gateway_resource.resource"]
      rest_api_id      = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id      = "${aws_api_gateway_resource.resource.id}"
      http_method      = "POST"
      authorization    = "NONE"
      api_key_required = false
    
      request_parameters = {
        "method.request.querystring.key" = true
      }
    }
    
    resource "aws_api_gateway_method_response" "method_response_post_200" {
      depends_on  = ["aws_api_gateway_method.method_post"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_post.http_method}"
      status_code = "200"
    
      response_models = {
        "application/json" = "Empty"
      }
    
      response_parameters {
        "method.response.header.Access-Control-Allow-Origin" = true
      }
    }
    
    resource "aws_api_gateway_method_response" "method_response_post_400" {
      depends_on  = ["aws_api_gateway_method.method_post"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_post.http_method}"
      status_code = "400"
    
      response_models = {
        "application/json" = "Empty"
      }
    
      response_parameters {
        "method.response.header.Access-Control-Allow-Origin" = true
      }
    }

API GatewayのIntegrationの設定

  • ハマりポイント
    • selection_pattern が必要
    • aws_api_gateway_integration_responseaws_api_gateway_method_responseに依存する。
    ## API Gateway - Integration Setting
    resource "aws_api_gateway_integration" "integration_post" {
      depends_on              = ["aws_api_gateway_method.method_post"]
      rest_api_id             = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id             = "${aws_api_gateway_resource.resource.id}"
      http_method             = "${aws_api_gateway_method.method_post.http_method}"
      integration_http_method = "POST"
      type                    = "HTTP"
      uri                     = "${var.url}"
    }
    
    resource "aws_api_gateway_integration_response" "integration_response_post_200" {
      depends_on  = ["aws_api_gateway_method_response.method_response_post_200"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_post.http_method}"
      status_code = "${aws_api_gateway_method_response.method_response_post_200.status_code}"
      selection_pattern = "200"
    
      response_templates = {
        "application/json" = ""
      }
    
      response_parameters = {
        "method.response.header.Access-Control-Allow-Origin" = "'*'"
      }
    }
    
    resource "aws_api_gateway_integration_response" "integration_response_post_400" {
      depends_on  = ["aws_api_gateway_method_response.method_response_post_400"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_post.http_method}"
      status_code = "${aws_api_gateway_method_response.method_response_post_400.status_code}"
      selection_pattern = "4\\d{2}"
    
      response_templates = {
        "application/json" = ""
      }
    
      response_parameters = {
        "method.response.header.Access-Control-Allow-Origin" = "'*'"
      }
    }

API Gateway では、Mock 統合タイプで OPTIONS メソッドを設定して CORS を有効にし、 前述のレスポンスヘッダーをメソッドのレスポンスヘッダーとして返す。 200 レスポンスで Access-Control-Allow-Origin:'*request-originating server addresses*' ヘッダーを返すようにすること。 特定の request-originating server addresses の静的な値を * に置き換えることもできるけど 広範なサポートを有効にすることはよく考えること。任意のサーバーを指定可能なのでできるならその方がよいので。

今回では * をつけています。 今回はMock統合タイプでbackendがGoogle Formを例にしている。

Access-Control-Allow-Originを設定すれば CORS は有効化できます。

  • ハマりポイント
    • OPTIONS メソッドを使用するのを忘れない
    • selection_pattern が必要
    • aws_api_gateway_integration_responseaws_api_gateway_method_responseに依存する。
    # options
    resource "aws_api_gateway_method" "method_options" {
      depends_on       = ["aws_api_gateway_resource.resource"]
      rest_api_id      = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id      = "${aws_api_gateway_resource.resource.id}"
      http_method      = "OPTIONS"
      authorization    = "NONE"
      api_key_required = false
    
      request_parameters = {
        "method.request.querystring.key" = true
      }
    }
    
    resource "aws_api_gateway_method_response" "method_response_options_200" {
      depends_on  = ["aws_api_gateway_method.method_options"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_options.http_method}"
      status_code = "200"
    
      response_models = {
        "application/json" = "Empty"
      }
    
      response_parameters {
        "method.response.header.Access-Control-Allow-Origin" = true
      }
    }
    
    resource "aws_api_gateway_method_response" "method_response_options_400" {
      depends_on  = ["aws_api_gateway_method.method_options"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_options.http_method}"
      status_code = "400"
    
      response_models = {
        "application/json" = "Empty"
      }
    
      response_parameters {
        "method.response.header.Access-Control-Allow-Origin" = true
      }
    }
    
    resource "aws_api_gateway_integration" "integration_options" {
      depends_on              = ["aws_api_gateway_method.method_options"]
      rest_api_id             = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id             = "${aws_api_gateway_resource.resource.id}"
      http_method             = "${aws_api_gateway_method.method_options.http_method}"
      integration_http_method = "OPTIONS"
      type                    = "MOCK"
    }
    
    resource "aws_api_gateway_integration_response" "integration_response_options_200" {
      depends_on  = ["aws_api_gateway_method_response.method_response_options_200"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_options.http_method}"
      status_code = "${aws_api_gateway_method_response.method_response_options_200.status_code}"
      selection_pattern = "200"
    
      response_templates = {
        "application/json" = ""
      }
    
      response_parameters = {
        "method.response.header.Access-Control-Allow-Origin" = "'*'"
      }
    }
    
    resource "aws_api_gateway_integration_response" "integration_response_options_400" {
      depends_on  = ["aws_api_gateway_method_response.method_response_options_400"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      resource_id = "${aws_api_gateway_resource.resource.id}"
      http_method = "${aws_api_gateway_method.method_options.http_method}"
      status_code = "${aws_api_gateway_method_response.method_response_options_400.status_code}"
      selection_pattern = "4\\d{2}"
    
      response_templates = {
        "application/json" = ""
      }
    
      response_parameters = {
        "method.response.header.Access-Control-Allow-Origin" = "'*'"
      }
    }
  • ハマりポイント
    • aws_api_gateway_deploymenaws_api_gateway_stage は両方必要ない (stage-nameがconflict する)。こちら参照)
    ## API Gateway - Deployment
    resource "aws_api_gateway_deployment" "deployment_200" {
      depends_on = [
        "aws_api_gateway_integration.integration_post",
        "aws_api_gateway_integration_response.integration_response_post_200",
      ]
    
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      stage_name  = "${var.env}"
    }
    
    resource "aws_api_gateway_method_settings" "method_settings_200" {
      depends_on  = ["aws_api_gateway_deployment.deployment_200"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      stage_name  = "${var.env}"
      method_path = "*/*"
    
      settings {
        metrics_enabled = true
        logging_level   = "INFO"
      }
    }
    
    resource "aws_api_gateway_deployment" "deployment_400" {
      depends_on = [
        "aws_api_gateway_integration.integration_post",
        "aws_api_gateway_integration_response.integration_response_post_400",
      ]
    
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      stage_name  = "${var.env}"
    }
    
    resource "aws_api_gateway_method_settings" "method_settings_400" {
      depends_on  = ["aws_api_gateway_deployment.deployment_400"]
      rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
      stage_name  = "${var.env}"
      method_path = "*/*"
    
      settings {
        metrics_enabled = true
        logging_level   = "INFO"
      }
    }

WAFのRuleはIP制限。

同一IPアドレスから5分間に2000(最低値)を超えたリクエストをトリガー。

WAFとAPI Gatewayを繋げたいので、

WAF ResourcesではなくWAF Regional Resoucesを使う必要がある。

  • ハマりポイント
    • metric_name は英数字のみなので -などは使わない。こちら参照)
    ## WAF RULE
    resource "aws_wafregional_rate_based_rule" "wafrule" {
      name        = "${var.env}-bov-form-protect-dos-rule"
      metric_name = "${var.env}bovrule"
    
      rate_key   = "IP"
      rate_limit = 2000
    }

WAF RULEに該当したらBLOCKさせる設定をする。

aws_wafregional_web_acl_association) ではまだ ALBしか公式サポートしていないようだけど、

API Gatewayでも繋ぐことができた。

WAFとAPI Gatewayの繋ぎこみの設定は web_acl_idresource_arn を書くことで可能。

resource_arn の書き方は AWSドキュメンテーションなど参照)

もしくはAssociateWebACLのドキュメンテーション)

公式サポートされてほしい)

  • ハマりポイント
    • wafregional_ipset はALBで使うproviderで今回はIP指定もないので使わない。こちら参照)
    • aws_wafregional_ipset の変わりに aws_wafregional_rate_based_rule を使用する
    ## WAF_ACL rate_based block over 2000 access
    
    resource "aws_wafregional_web_acl" "wafacl" {
      name        = "${var.env}-bov-form-protect-dos-acl"
      metric_name = "${var.env}BovFormAccess"
    
      default_action {
        type = "ALLOW"
      }
    
      rule {
        action {
          type = "BLOCK"
        }
    
        priority = 1
        rule_id  = "${aws_wafregional_rate_based_rule.wafrule.id}"
        type     = "RATE_BASED"
      }
    }
    
    resource "aws_wafregional_web_acl_association" "wafassosiation" {
      depends_on   = ["aws_wafregional_web_acl.wafacl"]
      resource_arn = "arn:aws:apigateway:ap-northeast-1::/restapis/${aws_api_gateway_rest_api.rest_api.id}/stages/${var.env}"
      web_acl_id   = "${aws_wafregional_web_acl.wafacl.id}"
    }

AWS構築[Cloudfront+ELB+WEB+DB+S3]

AWSなれるために2018年ごろに手でポチポチ構築したときのメモ なので今は設定画面とか変わっているものがありそう [Cloudfront+ELB+WEB+DB+S3] 構成。 ほとんどの場合、Terraformで構築すると思うけど、 その前のAWS入門として一応残しておきます。

仕様と構成

サブネット構成

名前タグ VPC AZ IPv4 CIDR ブロック
subnet-hogehoge-training-ssh hog ap-northeast-b 172.31.hoge.0/24
subnet-hogehoge-training-web01 training ap-northeast-b 172.31.hoge.0/24
subnet-hogehoge-training-web02 training ap-northeast-c 172.31.hoge.0/24
subnet-hogehoge-training-db01 training ap-northeast-b 172.31.hoge.0/24
subnet-hogehoge-training-db02 training ap-northeast-c 172.31.hoge.0/24

IAMロール

  • IAMロール名
  • 権限
    • EBSスナップショット
    • S3ファイルアップロード権限

S3

サーバ構成

踏み台

  • OS
    • CentOS7(AWS Marketplaceから)
  • インスタンス対応
    • t2.micro
  • サブネット
  • 自動割当パブリックIP
    • 有効化
  • プライベートIPアドレス
  • ディスク
    • 初期値を利用
    • ボリュームタイプは汎用SSD(GP2)
  • タグ
  • セキュリティグループ
    • セキュリティグループ名:hoge-training-ssh
    • 設定:自分のNWからSSHのみ許可
  • キーペア
    • 新規作成
  • EIP
    • 必要
  • ホスト名

web

  • OS
    • CentOS7(AWS Marketplaceから)
  • インスタンス対応
    • t2.micro
  • サブネット
    • subnet-hoge-training-web01
    • subnet-hoge-training-web02
  • 自動割当パブリックIP
    • 有効化
  • プライベートIPアドレス
  • ディスク
    • 初期値を利用
    • ボリュームタイプは汎用SSD(GP2)
  • タグ
    • Name:hoge-training-web01
    • Name:hoge-training-web02
  • セキュリティグループ
    • セキュリティグループ名:hoge-training-web
    • 設定:踏み台のからSSHのみ許可,ELBからのHTTP(80)のみ許可
  • キーペア
    • 新規作成
  • EIP
    • 不要
  • ホスト名
    • hoge-training-web01
    • hoge-training-web02
  • ミドルウェア
    • Apache(yumにてインストールされる最新版)

ELB(ALB)

  • 名前
  • セキュリティグループ
    • セキュリティグループ名:hoge-training-elb
  • ルーティングの設定
    • ターゲットグループ名:hoge-training-elb
  • ターゲットの登録
    • webサーバ2台を登録

RDS(MySQL)

  • サブネットグループの作成
  • 開発/テストを洗濯
  • DBエンジンのバージョン
    • 最新版を選択
  • DBインスタンスの選択
    • db.t2.micro
  • マルチAZ配置
    • はい
  • DBインスタンス識別子
  • マスターユーザの名前
    • root
  • パスワード

  • VPC
    • training
  • サブネットグループ
    • subnet-hoge-training-db01
    • subnet-hoge-training-db02
  • パブリックアクセス可能
    • いいえ
  • VPCセキュリティグループ
    • タグ名: hoge-training-db
    • インバウンドルールの編集: webサーバのsubnetからのみ3306のアクセス許可
  • データベース名
  • バックアップウィンドウ
    • 開始時間20:00UTF
  • マイナーバージョン自動アップグレード
    • いいえ
  • メンテナンスウィンドウ
    • 開始日時:日曜日の21:00UTF

CloudFront

S3参照

Route53

hoge-trainigをサブドメインとして<ドメイン>に登録

流れ

  1. hoge-training.<ドメイン> にアクセスするとサイト表示閲覧可能にする。
    • ELB から 2 台に振り分けられている状態
    • index.html を設置
  2. RDS 作成
    • web サーバから mysql コマンドで接続可能
  3. CloudFront の設定
  4. プログラムの作成

手順

IAMロールの作成

ロールは永続的な権限(アクセスキー、シークレットアクセスキー)を保持するユーザーとは異なり、 一時的にAWSリソースへアクセス権限を付与する場合に使用します。

  • AWSリソースへの権限付与
  • クロスアカウントアクセス
    • 複数のAWSアカウント間のリソースを1つのIAMユーザアカウントで操作したい
  • IDフェデレーション
    • 社内のADやLDAPサーバに登録されているアカウントを使用して、AWSリソースにアクセスしたい場合
  • WEB IDフェデレーション
    • FBやGoogle等のアカウントを使用してAWSリソースにアクセスしたい場合

AWSコンソールのIAM→ロール→ロールの作成

EC2インスタンスだけがそのロールを取得することができるポリシーを選択 次のステップ:アクセス権限 をクリック ポリシーの作成 をクリック 後述の IAMポリシーの付与 に飛びます。

IAMユーザとグループ

今回は使わないがロールではなくユーザとグループを作成してポリシーを付与するのは以下手順

AWSコンソールのIAM→ユーザ→ユーザを追加 IAMユーザにアクセス権限を設定する場合、ユーザーに直接ポリシー付与する方法と、ポリシー付与したグループを作成して、ユーザーをグループに所属させる方法がある。 基本的にはIAMグループに対して、アクセス権限を設定するようにする。 グループの名前も管理者であることが想像できるものにするのがよい。

  • ユーザ名
  • AWSアクセスの種類を選択
    • アクセスの種類:プログラムによるアクセス、AWSコンソールへのアクセス
    • カスタムパスワード:*********
    • パスワードのリセットが必要
  • ユーザをグループに追加で グループの作成 をクリック
  • グループ名 hoge-training 入力して グループの作成 をクリック
  • ポリシーの付与はのちほど行うので 次のステップ:確認 をクリック
  • ユーザーの作成 をクリック

これでIAMユーザーが作成されました。 作成結果の画面にアクセスキーIDとシークレットアクセスキーが表示されていますので、 忘れずに控えておきましょう。 .csvのダウンロード をクリックすると、アクセスキー情報が記載されたCSVファイルをダウンロードできます、 後からアクセスキーの作成は可能だが、同じアクセスキーは二度とダウンロードできないので注意。 アクセスキーを不特定多数が閲覧可能な場所には置かないこと。Githubとか... アクセスキーを入手したら 閉じる をクリックしてIAMのユーザー画面に戻る。

IAMポリシーの付与

作成したIAMユーザにアクセス権限を設定 アクセス権限はIAMポリシーで管理されます。 ポリシーには管理ポリシーとインラインポリシーの2種類があります。

  • 管理ポリシー
    • AWS管理ポリシー
      • AWS側で作成。管理されるポリシー
      • 管理者権限、全サービスの閲覧のみの権限、EC2フルアクセス権限などが用意されている
      • 細かい設定をする必要がない場合は、このポリシーを付与するとラク
    • カスタマー管理ポリシー
      • ユーザが作成して、自由に設定できるポリシー
      • 特定のIPをからのみ操作を受け付けるといった細かい設定はこのポリシーを使う
  • インラインポリシー
    • 特定のIAMユーザ、IAMグループ、IAMロールに直接付与されるポリシー
    • 基本的には管理ポリシーを使うので、インラインポリシーが付与されることはあまりない。
    • 管理ポリシーはそのポリシーを適用している全ユーザ、グループ、ロールに影響があるので、特定のユーザのみに付与したい場合などはインラインポリシーを使う

ポリシーはJSON形式で記述します。

権限を以下のみにしたいので参考記事を参照しつつ設定してください。

  • EBSスナップショット
  • S3ファイルアップロード権限

参考記事

Review policy をクリックし、ポリシー名と説明を記入し、 Create policy をクリック 次にアクセス権限ポリシーをロールにアタッチさせます。 対象のポリシーを選択し、 次のステップ:確認 をクリック ロール名とロールの説明を入力し、 ロールの作成 をクリック

EC2で対象のインスタンスを選択し、 アクション→インスタンスの設定→IAMロールの割り当て/置換 をクリックし,アタッチしたいポリシーを選択。

サブネットの作成

サブネットは大きなネットワークを複数の小さなネットワークに分割して管理する歳の、管理単位となるネットワーク ELBを作成する予定があるサブネットは余裕を持って、「/24」(256IPアドレス以上)のネットワーク範囲で作成することが推奨 なぜなら、ELB作成時でELBを作成するサブネットに20IPアドレス以上の空きが必要になるため、「/28」(16IPアドレス)で作成したサブネットにはELBを作成することができないため。

AWSコンソールで VPC→サブネット→サブネットの作成 から以下の条件でサブネットを作成する

名前タグ VPC AZ IPv4 CIDR ブロック
subnet-hoge-training-ssh training ap-northeast-b 172.31.hoge.0/24
subnet-hoge-training-web01 training ap-northeast-b 172.31.hoge.0/24
subnet-hoge-training-web02 training ap-northeast-c 172.31.hoge.0/24
subnet-hoge-training-db01 training ap-northeast-b 172.31.hoge.0/24
subnet-hoge-training-db02 training ap-northeast-c 172.31.hoge.0/24

踏み台とWEBは- 自動割当パブリックIPを有効化にしたいので、 サブネットのアクション タブの自動割り当てIP設定の変更から有効化します。

EC2作成

AWS操作用の公開鍵・秘密鍵の作成

EC2では公開鍵暗号方式でろログインをします。

EC2→キーペア→キーペアの作成でキーペアを作成します。 作成したキーペアは作成したタイミングでしかダウンロードできないので注意。

  1. AWSコンソールからEC2を選択
  2. EC2ダッシュボードからキーペアを選択
  3. キーペアの作成をクリック
  4. キーペア名に作成するキーペアの名前を入力
  5. 作成をクリック
  6. pem形式のファイルをダウンロードします。

権限は600にします。

$ chmod 600 hoge.pem

セキュリティグループを作成

セキュリティグループはホワイトリスト方式なので「何を許可するのか」のみ指定可能 また、セキュリティグループはVPCごとの設定になります。

AWSコンソールのEC2→セキュリティグループ→セキュリティグループの作成からインバウンド(内部への通信)で以下の条件で作成

  • 踏み台
    • セキュリティグループ名:hoge-training-ssh
    • 設定:オフィスIPからSSHのみ許可
    • SSH,TCP,22, 自分のネットワーク
  • WEB
    • セキュリティグループ名:hoge-training-web
    • 設定:踏み台のからSSHのみ許可,ELBからのHTTP(80)のみ許可
    • HTTP,TCP,80,172.31.0.0/16
    • SSH,TCP,22,172.31.hoge.10/32
  • ELB
    • セキュリティグループ名:hoge-training-elb
    • HTTP,TCP,80,0.0.0.0/0,::/0
  • DB
    • タグ名: hoge-training-db
    • インバウンドルールの編集: webサーバのsubnetからのみ3306のアクセス許可
    • カスタム TCP ルール,TCP,3306,172.31.hoge.0/24
    • カスタム TCP ルール,TCP,3306,172.31.hoge.0/24

EC2作成・起動

AWSコンソールのEC2→インスタンス→インスタンスの作成から作成していく

  1. AMIの選択

AWS Marketplace からCentOS7を選択

  1. インスタンスタイプの選択

t2.micro

  1. 詳細設定

ネットワークで対象のVPCを選択 サブネットはそれぞれのものを選択 自動割り当てパブリック IPは有効化 ネットワークインターフェイスのプライマリIPで以下のを指定 - ssh - 172.31.hoge.10 - web01 - 172.31.hoge.10 - web02 - 172.31.hoge.10

  1. ストレージの追加

  2. 初期値を利用

  3. ボリュームタイプは汎用SSD(GP2)

  4. タグの追加

Name:hoge-training-ssh Name:hoge-training-web01 Name:hoge-training-web02

  1. セキュリティグループの設定

既存のセキュリティグループを選択

EIP

AWSコンソールのEC2→Elastic IP→新しいアドレスの割り当てでElasticIPを作成 次にアクション→アドレスの関連付けで踏み台のインスタンスとプライベートIPを選択を選択し関連付けをおす。

ELBの作成

AWSコンソールのEC2→ロードバランサー→ロードバランサーの作成をクリック。 ALBのHTTP(S) で作成。

名前:hoge-training-elb リソースはHTTPのみ アベイラビリティーゾーン:VPCと登録するEC2インスタンスを含むAZを選択 タグ:Name,hoge-training-elb

次の手順:セキュリティ設定の構成を選択 以下のエラーが出るが気にせず、進む。

ロードバランサーのセキュリティを向上させましょう。ロードバランサーは、いずれのセキュアリスナーも使用していません。
ロードバランサーへのトラフィックを保護する必要がある場合は、フロントエンド接続に HTTPS プロトコルをお使いください。 最初のステップに戻り、基本的な設定 セクションでセキュアなリスナーを追加 / 設定することができます。または現在の設定のまま続行することもできます。

ステップ 3: セキュリティグループの設定では既存のセキュリティグループを選択するで既に作成したセキュリティグループを選択

ステップ 4: ルーティングの設定ターゲットグループでは名前だけ入力

名前:ELBTARGET

ステップ 5: ターゲットの登録 で追加したいEC2インスタンスを選択して、登録済みに追加をクリック 登録済みに追加されたら次の手順:確認を選択

ステップ 6: 確認で問題なければ作成

ELBの動作確認

webサーバにcentosをインストール

# yum -y install httpd
# systemctl list-unit-files -t service |grep httpd
httpd.service                                 disabled
# systemctl enable httpd.service
Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service to /usr/lib/systemd/system/httpd.service.
# systemctl list-unit-files -t service |grep httpd
httpd.service                                 enabled
# systemctl start httpd.service

ELBのDNS名をブラウザに入力してindex.htmlの中身が表示されることを確認 ターゲットグループのターゲットから両インスタンスhealthyになることを確認

Route53の設定

ホストゾーンドメインは既にあるものなどを使い、 hoge-trainigをサブドメインとして ドメイン に登録する。 ホストゾーンはそのドメインのリソースレコードセットの集合です。

Route53→Hosted zonesからドメインを選択 Create Record Setを選択  Name:hoge-training Type:Aレコード(IPv4) Value:Alias:Yes Alias Target:ELBのDNS名 Routing Policy:Simple

のちにAliasをCloudFrontに変更します。

RDS 作成

VPC上にRDSインスタンスを起動する場合、DBサブネットグループを作成する必要があるので まずはサブネットグループを作成します。

RDS→サブネットグループ→DBサブネットグループの作成を選択 サブネットグループの詳細 名前:hoge-training 説明:middle engineer training ozawa subnet VPC:trainingのVPCを選択 サブネットの追加でアベイラビリティーゾーンサブネット

RDS→インスタンス→DBインスタンスの起動→MySQLを選択

DB詳細の指定

  • 開発/テストを選択
  • DBエンジンのバージョン
    • 最新のバージョン
  • DBインスタンスの選択
    • db.t2.micro
  • マルチAZ配置
    • 別のゾーンにレプリカを作成します
  • DBインスタンス識別子
  • マスターユーザの名前
    • root
  • パスワード

[詳細設定]の設定 ネットワーク&セキュリティ

データベースの設定

  • データベースの名前
  • データベースのポート
    • 3306
  • DBパラメータグループ
    • デフォルト
  • オプショングループ
    • デフォルト
  • IAM DB 認証
    • 無効化

暗号化

  • 無効化(無料利用枠ではデフォルトで無効)

バックアップ

  • バックアップの保存期間
    • 1日
  • バックアップウィンドウ
    • 開始時間20:00UTF期間0.5時間
  • タグをスナップショットへコピー  - チェック

モニタリング

  • 拡張モニタリング
    • 有効
    • モニタリングロール:デフォルト
    • 60秒

ログのエクスポート

  • 監査ログ
    • 有効
  • エラーログ
    • 無効
  • 全般ログ
    • 有効
  • スロークエリログ
    • 有効

メンテナンス

  • マイナーバージョン自動アップグレード
    • 無効化
  • メンテナンスウィンドウ
    • 開始日時:日曜日の21:00UTF期間0.5時間

DBインスタンスの作成 で作成完了

webサーバにmysqlクライアントをインスタンス RDSインスタンスからエンドポイントを確認

# yum install mysql -y
# mysql -u root -h エンドポイント名 -p

S3

hoge-training.<ドメイン>バケットを作成します。

S3→バケットを作成する で作成します。

  • バケット名を入力
  • リージョンを選択
  • 次へをクリック
  • フォルダの作成image フォルダを作成
  • アクセス権限 → バケットポリシーを以下にしてパブリックにする
{
    "Version": "2012-10-17",
    "Id": "PublicRead",
    "Statement": [
        {
            "Sid": "ReadAccess",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::hoge-training.<ドメイン/*"
        }
    ]
}

プロパティの設定およびアクセス許可の設定はデフォルトのまま進めます。

CloudFrontの設定

CloudFrontはAWSCDNサービスです。 CDNとはコンテンツを配信するために最適化されたネットワークのことです。 分散して配置されたサーバからコンテンツを配布することで効率的にコンテンツを配信する仕組み。

CloudFrontはAWSが世界中に配置したエッジサーバを利用して効率的にコンテンツを配信します。 エッジサーバはCloudFrontのコンテンツの配布ポイントでユーザーのアクセスを最も近くにあるエッジサーバに誘導します。 「xxx.cloudfront.net」というドメインにアクセスすると、最も近いエッジサーバのIPアドレスが返されます。 CloudFrontではコンテンツの配信元のサーバをオリジンサーバと呼びます。 CloudFrontを利用していない場合は、全てのアクセスがオリジンサーバにいくが、 利用するとオリジンサーバに到達する前に、エッジサーバがコンテンツを返す。 そのため、オリジンサーバの負荷を下げることができ負荷分散としても大きな意味があります。 なのでEC2やS3等コンテンツを配信するサーバの前には、なるべくCloudFrontを配置するべき。

エッジロケーションはエッジサーバが存在する地域で、日本では東京と大阪にある。

CloudFront→Create Distributionをクリック

  • コンテンツの配信方法の選択

HTTP[S]で表示するWEBコンテンツの配信なのでWEBを選択します。

  • Distributionの設定

OriginSettingでオリジンサーバの設定をします。 今回CloudFrontはS3とALBの前に設置するので、 Origin Domain Name にS3とALBを選択して入力することになります。

  • Origin Domain Name
  • Behaviors
    • /image/* 300 秒キャッシュ
  • Origin Domain Name
    • hoge-training-elb-1353485913.ap-northeast-1.elb.amazonaws.com
  • Behaviors

    • /js/* 600 秒キャッシュ
    • /css/* 600 秒キャッシュ
    • Default (*)
  • Distributionの設定(Origin Settings)

オリジンサーバの設定をする

設定項目 説明
Origin Domain Name S3バケットのエンドポイント オリジンサーバのドメインを入力
Origin Path 値なし CloudFrontへのリクエストをオリジンサーバの特定のパスにルーティングしたい場合に設定。
Origin ID 任意の名前 このDistributionを区別するための名前を設定
Restrict Bucket Access No オリジンがS3の場合のみ有効。S3バケットのコンテンるへのアクセスをCloudFrontからのみに制限する場合はYes。直接エンドポイントにアクセスできるようにするならNo
Origin Custom Headers 値なし リクエストをオリジンサーバに転送する際のヘッダーを指定
  • Default Cache Behavior Setting

キャッシュの動作の設定をする

設定項目 説明
Path Pattern Default(*) キャッシュを有効にするパスのパターンを指定。
Viewer Priticol Policy HTTP and HTTPS コンテンツにアクセスする際にどのプロトコルを使用するか選択
Allowed HTTP Methods GET,HEAD エンドユーザーに許可するHTTPメソッドを選択
Field-level Encryption Config 値なし 特定のデータフィールドにフィードレベル暗号化を適用する場合に指定
Cached HTTP Methods GET,HEAD CloudFrontでのキャッシュが有効になるHTTPメソッドを選択
Cached Based on Selected Request Headers None CloudFrontがオリジンサーバに転送するリクエストヘッダーの指定とヘッダー値に基づいてオブジェクトをキャッシュするかの設定を行う。
Object Caching Use Origin Cache Headers CloudFrontがキャッシュを保持する時間の設定。オリジンサーバで追加したCache-COntorolの時間に応じてキャッシュを保持する場合は、今回の値を設定する。
Minimum TTL 0 キャッシュの最小保持期間
Maximum TTL 31536000 キャッシュの最大保持期間
Default TTL 86400 キャッシュのデフォルトの保持期間
Foward Cookied None CloudFrontからオリジンサーバに転送するCookieを指定。オリジンがS3のときは無効。
Query String Fowarding and Caching None クエリ文字列に基づいてキャッシュを行う
Smooth Streaming No Microsoftスムーズストリーミング形式のメディアファイルを配信する場合はYES
Restrict Viewer Access No 署名付きURLを利用するかの選択
Compress Objects Automatically No ファイルを自動的に圧縮する
Lambda Function Assopciations 値なし トリガーを追加するLambda関数のARNを指定
  • Distribution Setting

Distributionの詳細を設定

設定項目 説明
Price Class Use ALL Edge Locations 価格クラスを選択。
AWS WAF Web ACL None AWS WAFを使用する場合にウェブACLを選択する・
Alternate Domain Names 値なし 独自ドメインを利用する場合に設定
SSL Certificate Default CloudFrint Cerificate DefaultではCloudFrontにデフォルトで用意されている証明書を利用する。
Supported HTTP Versions HTTP/2,HTTP/1.1,HTTP/1.0 CloudFrontとの通信に使用するHTTPバージョンを指定
Default Root Object index.html デフォルトのルートオブジェクトを設定
Logging OFF ログを取得するか設定
Bucket for Logs 値なし ログを配置するS3のバケットを選択
Log Prefix 値なし ログファイル名の戦闘につける文字列を指定
Cookie Logging off ログにCookieも記録するか選択
Enable IPv6 有効 IPv6を有効にするか選択
Comment 値なし 入力は任意。
Distribution State Enabled Distributionの使用準備が整ったあと、自動的にこのDistributionを有効にするか選択

Deployedになったら、Domain Nameに表示されている「****.cloudfront.net」にアクセスすればS3のコンテンツが表示されます。

  • Route53との連系

Route53にCLoudFrontのエンドポイントを設定することで、独自ドメインでCloudFrontを利用することができます。 作成したDistributionを選択し、Distribution Settingをクリック。 GeneralタブのEditで編集画面に行き、Alternate Domain Namesに設定したい独自ドメインを入力する。

次にRoute5→Hotsted Zonesから使用する独自ドメインのホストゾーンを選択し、Aliasレコードを追加する。

確認項目

セキュリティ

  • SSH 事務所からのみ接続できることを確認
  • HTTP 事務所からのみ閲覧できることを確認
  • MySQL web サーバからのみ接続できることを確認

RDS

  • 手動フェイルオーバを行なう
    • AZ が切り替わっていることを確認
    • サービスが閲覧できることを確認

S3

  • web サーバから aws コマンドを利用してS3 のバケットにテストファイルをアップロードする
  • S3 のバケットから aws コマンドを利用してテストファイルをダウンロードする
  • 踏み台からは S3 操作が不可であることを確認する

EBS

  • web サーバから aws コマンドを利用して、EBS スナップショットを取得する
  • 踏み台からは EBS スナップショット取得が出来ないことを確認

サイト表示

  • yourname-training.<ドメイン> 画面表示が正常であることを確認
    • web01, web02 に分散されていることを確認
    • web01 を ELB から切り離しても正常に閲覧できることを確認
  • image 配下へのアクセス
    • S3 へアクセスが行っていることを確認
  • js, css 配下へのアクセス ELB を経由していることを確認
    • キャッシュ時間に応じてキャッシュされていることを確認

上記以外
- ELB を経由していることを確認
- キャッシュされていないことを確認

podのCrashLoopBackOff原因およびNodeのNot Readyフラッピング問題調査

これは何?

とあるPodがCrashLoopBackOffのステータスになりあがってくれず、 それにあわせてNodeのstatusがNot Readyでフラッピングしている。 調査しつつ周辺技術(kubelet, CRI,CNIとか)についても調べて自信ニキになりたい。

事象

fluentdのpodがCrashLoopBackOffのステータスになり起動しない、 それに伴いNodeのstatusもNot Readyにフラッピングしている。

  • 各nodeの状態
# kubectl get pods |grep fluentd
node1                             0/1     CrashLoopBackOff   2029       28d
node2                            0/1     CrashLoopBackOff   1075       28d
node3                            0/1     CrashLoopBackOff   1052       28d
node4                            0/1     CrashLoopBackOff   1036       28d
node5                            0/1     CrashLoopBackOff   2994       28d

どんなとき

fluentdにログが流れてなかった時期

原因

CNIのbug fluentd PodのPodSandboxのコンテナがないためkubeletでPodの起動に失敗している。

根本対応

bugfixされたlibcniをpullする。

暫定対応

PodSandboxのコンテナ起動もしくはfluentd Podの再作成(DeamonSetなので消せば再起動するため)もしくはkubelet再起動

調査した流れとログ

pod起動していないのでlogsは不可。 kubeletかdescribeでまずは調べる。 台数あるのでまずは fluentd を中心に調べていく。 その次にNode NotreadyについてPLEG周りを調べていく。

  • node server

kubeletが fluentd- podへの動機に失敗している模様

# journalctl -u kubelet
-- Logs begin at 月 2018-11-19 12:14:46 JST, end at 月 2019-02-04 16:29:22 JST. --
 1月 24 01:49:36 node kubelet[10796]: E0124 01:49:36.768314   10796 pod_workers.go:190] Error syncing pod a094d5a4-125b-11e9-8374-fa163e76fc98 ("fluentd(a094d5a4-125b-11e9-8374-fa163e76fc98)"), skipping: rpc err
 1月 24 01:49:42 node kubelet[10796]: I0124 01:49:42.507809   10796 setters.go:72] Using node IP: "10.64.152.1"
 1月 24 01:49:51 node kubelet[10796]: E0124 01:49:51.768283   10796 pod_workers.go:190] Error syncing pod a094d5a4-125b-11e9-8374-fa163e76fc98 ("fluentd(a094d5a4-125b-11e9-8374-fa163e76fc98)"), skipping: rpc err
 1月 24 01:49:52 node kubelet[10796]: I0124 01:49:52.523387   10796 setters.go:72] Using node IP: "10.64.152.1"
  • Master側

CrashLoopBackOffはどうやら以下の内容

コンテナ内のプロセスの終了を検知してコンテナの再起動を繰り返している。

fluentdDaemonset だった。なので自動起動を何度も試みていると考えられる。

# kubectl get pods fluentd -o wide
NAME    READY   STATUS             RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
fluentd   0/1     CrashLoopBackOff   1075       28d   172.16.69.8    node   <none>           <none>
  • master側

OCIruntimeがコンテナ起動作成失敗している模様 Eventsに status: rpc error とあるので、kubeletからCRIへのgrpcプロトコルの命令が処理されずエラーになっていると考えられる。

# kubectl describe pods fluentd
(略)
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       ContainerCannotRun
      Message:      OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:402: container init caused \"\"": unknown
      Exit Code:    128
      Started:      Fri, 11 Jan 2019 16:26:40 +0900
      Finished:     Fri, 11 Jan 2019 16:26:40 +0900
(中略)
Events:
  Type     Reason      Age                    From                                     Message
  ----     ------      ----                   ----                                     -------
  Warning  FailedSync  60m (x85 over 19d)     kubelet, node  error determining status: rpc error: code = Unknown desc = operation timeout: context deadline exceeded
  Warning  FailedSync  8s (x123314 over 23d)  kubelet, node  error determining status: rpc error: code = DeadlineExceeded desc = context deadline exceeded

PodSandboxStatus of sandbox for pod "fluentd" error: rpc error: code = Unknown desc = Error: No such container: とあるので fluentd podのための PodSandbox がないメッセージが出力されている。

  • node
# less /var/log/messages-20190113
(略)
Jan  7 09:07:16 node004 kubelet: E0107 18:07:16.254903   10796 kuberuntime_manager.go:857] PodSandboxStatus of sandbox "5983c305299bada0906df670eba18f8be1e5192d75b825f1a01075dd5595c5f3" for pod "fluentd(a094d5a4-125b-11e9-8374-fa163e76fc98)" error: rpc error: code = Unknown desc = Error: No such container: 5983c305299bada0906df670eba18f8be1e5192d75b825f1a01075dd5595c5f3
Jan  7 09:07:16 node004 kubelet: E0107 18:07:16.254959   10796 generic.go:247] PLEG: Ignoring events for pod fluentd/default: rpc error: code = Unknown desc = Error: No such container: 5983c305299bada0906df670eba18f8be1e5192d75b825f1a01075dd5595c5f3
  • node

fluentd コンテナは起動しきってない。 gcr.io/google_containers/pause-amd64k8s_POD_fluentd_default_a094d5a4-125b-11e9-8374-fa163e76fc98_1 をみにいっている。 google_containers/pause-amd64 pause-amdd64はimage周りで使うコンテナの模様 image_gc_manager_test.goでもpauseコンテナについて書いてある。 pauseコンテナの説明はここがわかりやすい

# docker ps -a                                                                                                                                                                                                 [85/1657]
CONTAINER ID        IMAGE                                                        COMMAND                  CREATED             STATUS                PORTS               NAMES
000016b6000        asia.gcr.io/hoge/fluentd                            "/bin/sh -c '/run.sh…"   3 weeks ago         Created                                   k8s_fluentd_fluentd_default_a094d5a4-125b-11e9-8374-fa163e76fc98_1076
0004301c000       asia.gcr.io/hoge/fluentd                           "/bin/sh -c '/run.sh…"   3 weeks ago         Removal In Progress                       k8s_fluentd_fluentd_default_a094d5a4-125b-11e9-8374-fa163e76fc98_1075
0002e7bc000        gcr.io/google_containers/pause-amd64:3.1                         "/pause"                 3 weeks ago         Up 3 weeks                                k8s_POD_fluentd_default_a094d5a4-125b-11e9-8374-fa163e76fc98_1

以上をまとめると fluentd コンテナは作成され起動しようとしているけど、OCIruntimeがコンテナ起動作成失敗していると考えられる。 fluentd_default podのための PodSandbox がないメッセージが出力されているので、 PodSandbox に調べてみる。 PodSandbox は以下。

流れとして kubelet→CRI(docker)→podsandbox→pod となる。

Before starting a pod, kubelet calls RuntimeService.RunPodSandbox to create the environment. This includes setting up networking for a pod (e.g., allocating an IP). Once the PodSandbox is active, individual containers can be created/started/stopped/removed independently. To delete the pod, kubelet will stop and remove containers before stopping and removing the PodSandbox.

Kubelet is responsible for managing the lifecycles of the containers through the RPCs, exercising the container lifecycles hooks and liveness/readiness checks, while adhering to the restart policy of the pod.

つまりkubeletはポッドを開始する前に、RuntimeServiceを呼ぶ。そしてRunPodSandboxを呼び出して環境を作成する。(ポッド用のネットワーク設定(IP割当て)などもする。) PodSandboxがアクティブになると、個々のコンテナを個別に作成/開始/停止/削除することができる。 ポッドを削除するには、kubeletはPodSandboxを停止して削除する前にコンテナを停止して削除する。 Kubeletは、ポッドの再起動ポリシーを守りながら、RPCを通じてコン​​テナのライフサイクルを管理して、 コンテナーのライフサイクルフックを実行し、liveness/readiness のチェックを行う。

とあるのでkubeletがCRIにgRPCでコンテナ起動させようとしても PodSandboxのコンテナがないため、コンテナの起動に失敗している。 そしてDeamonSetが起動させようとして、Woeker NodeのStatusもフラッピングしていると考えられる。

Not ReadyのNodeでは PLEG でFalseしている。 PLEGはここ参照。 PLEGはkubeletの内部モジュールで、 Pod Lifecycle Event Generatorという名前の通り、kubeletがPodの管理およびコンテナの状態を管理するためのもの。

# kubectl describe nodes node
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Tue, 05 Feb 2019 14:36:43 +0900   Fri, 21 Dec 2018 20:19:20 +0900   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Tue, 05 Feb 2019 14:36:43 +0900   Fri, 21 Dec 2018 20:19:20 +0900   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Tue, 05 Feb 2019 14:36:43 +0900   Fri, 21 Dec 2018 20:19:20 +0900   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            False   Tue, 05 Feb 2019 14:36:43 +0900   Tue, 05 Feb 2019 14:36:33 +0900   KubeletNotReady              ≈is not healthy: pleg was last seen active 3m11.707539232s ago; threshold is 3m0s
(中略)
Events:
  Type    Reason        Age                     From                    Message
  ----    ------        ----                    ----                                     -------
  Normal  NodeReady     19m (x8929 over 45d)    kubelet, node004  Node node004 status is now: NodeReady
  Normal  NodeNotReady  4m15s (x8932 over 24d)  kubelet, node004  Node node004 status is now: NodeNotReady

PLEGのhealth checkは3分毎。docker ps でコンテナの状態変化を検出し、docker psinspect でこれらのコンテナの詳細を取得する。 これらが終了した後にタイムスタンプを更新するのだけれども、タイムスタンプが更新されていない場合(3分)、ヘルスチェックは失敗する。 https://github.com/kubernetes/kubernetes/blob/a1539d8e52c7ca5f6a590eebc66a5b33acc9c036/pkg/kubelet/pleg/generic.go#L134-L146

Master側はNodeのkubeletのPLEGからpodが落ちていると受け取って、Not Readyとしている。 そしてPLEGがpodが落ちていると判断しているのはPodSandboxがおらずコンテナが起動していないから。 そしてCNIのbugでpodsandboxの起動時失敗でtimeoutのままlockを要求して、podsandboxが再起動されず起動していない模様。 https://github.com/containernetworking/cni/issues/567

# kubectl describe pods fluentd
(略)
Events:
  Type     Reason      Age                    From                                     Message
  ----     ------      ----                   ----                                     -------
  Warning  FailedSync  60m (x85 over 19d)     kubelet, node004  error determining status: rpc error: code = Unknown desc = operation timeout: context deadline exceeded
  Warning  FailedSync  8s (x123314 over 23d)  kubelet, node004  error determining status: rpc error: code = DeadlineExceeded desc = context deadline exceeded

コンテナがどうやって起動しているのか、コンテナランタイムの実装などをみて、 理解が必要だと思ったので、理解を深めてからまた、kubernatesのpod起動周りについて調べ直したい。

kubeadmでHA構成のetcdクラスタ構築

最新手順はこちら

Kubeadmは、コントロールプレーンノード上のkubeletによって管理される静的ポッドで単一メンバーのetcdクラスタをデフォルトで実行する。 etcdクラスターに含まれるメンバーは1つだけで、メンバーが使用不可になっても持続できないため、これはHA設定ではない。 このタスクでは、kubeadmを使用してkubernetesクラスタを設定するときに外部etcdとして使用できる3つのメンバからなる高可用性etcdクラスタを作成する手順をみていく。

始める前に

  • ポート2379および2380を介して互いに通信できる3つのホスト。(今回はこれらをデフォルトポートと想定。kubeadmの設定ファイルで設定可能)
  • 各ホストにはdocker、kubelet、およびkubeadmがインストールされている必要がある。
  • ホスト間でファイルをコピーするためのssh, scpなどが可能であること。

クラスタを設定

一般的な方法は、1つのノードですべての証明書を生成し、必要なファイルだけを他のノードに配布すること。

※kubeadmには、以下に説明する証明書を生成するために必要なすべての暗号化機構が含まれているので他の暗号化ツールは必要ない

  1. etcのサービスマネージャになるようにkubeletを設定

etcdが最初に作成されたので、kubeadm提供のkubeletユニットファイルよりも高い優先順位を持つ新しいユニットファイルを作成することによって サービスの優先順位を上書きする必要がある。

cat << EOF > /etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
[Service]
ExecStart=
ExecStart=/usr/bin/kubelet --address=127.0.0.1 --pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true
Restart=always
EOF

systemctl daemon-reload
systemctl restart kubelet
  1. kubeadm用の設定ファイルを作成

以下のスクリプトを使用して、etcdメンバーが実行されるホストごとに1つのkubeadm構成ファイルを生成する。

# Update HOST0, HOST1, and HOST2 with the IPs or resolvable names of your hosts
export HOST0=10.64.20.1
export HOST1=10.64.20.2
export HOST2=10.64.20.3

# Create temp directories to store files that will end up on other hosts.
mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/

ETCDHOSTS=(${HOST0} ${HOST1} ${HOST2})
NAMES=("infra0" "infra1" "infra2")

for i in "${!ETCDHOSTS[@]}"; do
HOST=${ETCDHOSTS[$i]}
NAME=${NAMES[$i]}
cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml
apiVersion: "kubeadm.k8s.io/v1beta1"
kind: ClusterConfiguration
etcd:
    local:
        serverCertSANs:
        - "${HOST}"
        peerCertSANs:
        - "${HOST}"
        extraArgs:
            initial-cluster: ${NAMES[0]}=https://${ETCDHOSTS[0]}:2380,${NAMES[1]}=https://${ETCDHOSTS[1]}:2380,${NAMES[2]}=https://${ETCDHOSTS[2]}:2380
            initial-cluster-state: new
            name: ${NAME}
            listen-peer-urls: https://${HOST}:2380
            listen-client-urls: https://${HOST}:2379
            advertise-client-urls: https://${HOST}:2379
            initial-advertise-peer-urls: https://${HOST}:2380
EOF
done
  1. 認証局を生成する

すでにCAを持っているなら、 crtkey ファイルを /etc/kubernetes/pki/etcd/ca.crt/etc/kubernetes/pki/etcd/ca.key にコピーするだけがアクションです これらのファイルをコピーしたら、次の手順「各メンバーの証明書を作成する」に進む。ない場合は作成する。

  1. 各メンバーの証明書を作成する
kubeadm init phase certs etcd-server --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST2}/
# cleanup non-reusable certificates
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST1}/
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete

kubeadm init phase certs etcd-server --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
# No need to move the certs because they are for HOST0

# clean up certs that should not be copied off this host
find /tmp/${HOST2} -name ca.key -type f -delete
find /tmp/${HOST1} -name ca.key -type f -delete
  1. 証明書とkubeadm設定をコピーする

証明書が生成されたので、今度はそれらをそれぞれのホストに移動する必要がある。

 USER=hogeuser
 HOST=${HOST1}
 scp -r /tmp/${HOST}/* ${USER}@${HOST}:
 ssh ${USER}@${HOST}
 USER@HOST $ sudo -Es
 root@HOST $ chown -R root:root pki
 root@HOST $ mv pki /etc/kubernetes/
  1. 必要なファイルがすべて存在することを確認。

$HOST0 に必要なファイルの完全なリストは以下。

/tmp/${HOST0}
└── kubeadmcfg.yaml
---
/etc/kubernetes/pki
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
└── etcd
    ├── ca.crt
    ├── ca.key
    ├── healthcheck-client.crt
    ├── healthcheck-client.key
    ├── peer.crt
    ├── peer.key
    ├── server.crt
    └── server.key

$HOST1

$HOME
└── kubeadmcfg.yaml
---
/etc/kubernetes/pki
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
└── etcd
    ├── ca.crt
    ├── healthcheck-client.crt
    ├── healthcheck-client.key
    ├── peer.crt
    ├── peer.key
    ├── server.crt
    └── server.key

$HOST2

$HOME
└── kubeadmcfg.yaml
---
/etc/kubernetes/pki
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
└── etcd
    ├── ca.crt
    ├── healthcheck-client.crt
    ├── healthcheck-client.key
    ├── peer.crt
    ├── peer.key
    ├── server.crt
    └── server.key
  1. 静的ポッドマニフェストを作成

証明書と設定が整ったので、次にマニフェストを作成。 各ホストで、kubeadmコマンドを実行してetcdの静的マニフェストを生成する。

root@HOST0 $ kubeadm init phase etcd local --config=/tmp/${HOST0}/kubeadmcfg.yaml
root@HOST1 $ kubeadm init phase etcd local --config=/home/ubuntu/kubeadmcfg.yaml
root@HOST2 $ kubeadm init phase etcd local --config=/home/ubuntu/kubeadmcfg.yaml
  1. オプション:クラスターの正常性を確認
docker run --rm -it \
--net host \
-v /etc/kubernetes:/etc/kubernetes quay.io/coreos/etcd:${ETCD_TAG} etcdctl \
--cert-file /etc/kubernetes/pki/etcd/peer.crt \
--key-file /etc/kubernetes/pki/etcd/peer.key \
--ca-file /etc/kubernetes/pki/etcd/ca.crt \
--endpoints https://${HOST0}:2379 cluster-health
...
cluster is healthy

${ETCD_TAG} :あなたのetcd画像のバージョンタグを設定。例えばv3.2.24。 ${HOST0} :テストしているホストのIPアドレスを設定。

Creating multi master cluster with kubeadm(ver1.13以下)

document

https://kubernetes.io/docs/setup/independent/install-kubeadm/ https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ kubeadmのトラブルシューティング

始める前に

  1. 以下のいずれかを実行している1台以上のマシンを用意
  2. Ubuntu 16.04+
  3. CentOS 7
  4. マシンごとに2 GB以上のRAM(それ以下にすると、アプリケーション用のスペースが狭まる)
  5. 2 CPU以上
  6. クラスタ内のすべてのマシン間の完全なネットワーク接続(パブリックまたはプライベートネットワークは問題なし)
  7. ノードごとに固有のホスト名、MACアドレス、およびproduct_uuid。
  8. マシンでは特定のポートを開けとく
  9. スワップ無効(kubeletを正しく動作させるためにスワップを無効にする。)

今回の構成

ホスト名 役割
haproxy01 ロードバランサー(HAProxy)
master-node01 MasterNode
master-node02 MasterNode
master-node03 MasterNode
worker-node01 WorkerNode
worker-node02 WorkerNode
worker-node03 WorkerNode

クライアントに必要なもの

cfsslとkubectlをインストールする

HAProxyロードバランサーのインストール

3つのKubernetesマスターノードを配置するので、 トラフィックを分散させるためにそれらの前にHAPRoxyロードバランサーを配置する必要がある。

  1. LBにするサーバにSSHで接続します。
  2. OS update
  3. $ sudo yum update -y
  4. HAProxyをインストール
  5. $ sudo yum install haproxy
  6. 3つのKubernetesマスターノード間でトラフィックを負荷分散するようにHAProxyを設定。
$ sudo vim /etc/haproxy/haproxy.cfg
global
...
default
...
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend kubernetes
    bind 10.64.21.35:6443
    option tcplog
    mode tcp
    default_backend kubernetes-master-nodes
#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
backend static
    balance     roundrobin
    server      static 127.0.0.1:4331 check

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend kubernetes-master-nodes
    mode tcp
    balance     roundrobin
    option tcp-check
    server  master-node01 10.64.20.01:6443 check
    server  master-node02 10.64.20.02:6443 check
    server  master-node03 10.64.20.03 :6443 check
  1. HAProxyを再起動。

$ sudo systemctl restart haproxy

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

# Set SELinux in permissive mode (effectively disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable kubelet && systemctl start kubelet

以下は各kubeノードで実行

sudo visudo で一般ユーザにroot権限を付与 sudo yum -y install vim

Dockerのインストール

最新の手順はこちら

$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum -y install docker-ce-18.06.1.ce-3.el7
$ sudo systemctl start docker & sudo systemctl enable docker

kubeadmのインストール

最新の手順はこちら

まず事前準備として、全ノードで Docker、CLI 等の関連パッケージのインストールと、 クラスタ内で利用するオーバーレイネットワーク用にカーネルパラメータを変更しておきます。

# 必要な依存パッケージのインストール
$ sudo yum -y update
# リポジトリの登録と更新
$ sudo su -
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

# SELinux disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

# Kubernetes 関連パッケージのインストール
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable --now kubelet
# オーバーレイネットワーク用にカーネルパラメータを変更 
# RHEL / CentOS 7の一部のユーザーは、iptablesがバイパスされているために
# トラフィックが誤ってルーティングされるという問題を報告しています。
# あなたのnet.bridge.bridge-nf-call-iptables設定で1に設定されていることを確認する必要がある
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

kubeadmを使ったHAクラスタの作成

最新の手順はこちら

TLS証明書を生成する

これらの手順は、cfsslツールをインストールした場所に応じて、 LinuxデスクトップまたはHAProxyマシン上にあれば実行できる。

認証局を作成する

  1. 認証局設定ファイルを作成します。
$ vim ca-config.json
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
  1. 認証局署名要求設定ファイルを作成します。
$ vim ca-csr.json
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
  {
    "C": "IE",
    "L": "Cork",
    "O": "Kubernetes",
    "OU": "CA",
    "ST": "Cork Co."
  }
 ]
}
  1. 認証局証明書と秘密鍵を生成します。
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
  1. ca-key.pemとca.pemが生成されたことを確認します。
$ ls -la

Etcdクラスター用の証明書を作成する

  1. 証明書署名要求設定ファイルを作成します。
$ vim kubernetes-csr.json
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
  {
    "C": "IE",
    "L": "Cork",
    "O": "Kubernetes",
    "OU": "Kubernetes",
    "ST": "Cork Co."
  }
 ]
}
  1. 証明書と秘密鍵を生成します。
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-hostname=10.64.20.1,10.64.20.2,10.64.20.3,10.64.21.35,127.0.0.1,kubernetes.default \
-profile=kubernetes kubernetes-csr.json | \
cfssljson -bare kubernetes
  1. kubernetes-key.pemファイルとkubernetes.pemファイルが生成されたことを確認

ls -la

  1. 証明書を各ノードにコピー
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.1~
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.2:~
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.3:~
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.4:~
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.5:~
$ scp ca.pem kubernetes.pem kubernetes-key.pem hogeuser@10.64.20.6:~

最初のコントロールプレーンノードの手順

  1. 最初のコントロールプレーンノードで、次の設定ファイルを作成 kubeadm-config.yaml

  2. kubernetesVersion 使用するKubernetesバージョンに設定する必要があります。この例では stable を使用。

  3. controlPlaneEndpoint ロードバランサのアドレスまたはDNSとポートを一致させる必要があり。
  4. kubeadm、kubelet、kubectl およびKubernetesのバージョンを一致させることがお勧め。
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: stable
apiServer:
  certSANs:
  - "10.64.21.35"
controlPlaneEndpoint: "10.64.21.35:6443"
  1. ノードがクリーンな状態にあることを確認
sudo kubeadm init --config=kubeadm-config.yaml

次のようなものが見えるはず

To start using your cluster, you need to run the following as a regular user:

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

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

kubeadm join 10.64.21.35:6443 --token c6c3id.bba2b5hih8ka9jx3 --discovery-token-ca-cert-hash sha256:b078841bd826050e1461341835ffc5a5b0cf2e32365a33794eb77512ca0c016a
  1. この出力をテキストファイルにコピーし。後で他のコントロールプレーンノードをクラスタに参加させるために必要になる。
  2. Weave CNIプラグインを適用
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

kubectl を kubelet に利用可能にするために以下のコマンドを実行しておくこと

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

実行しない場合は次のようになる。

# kubectl get pod --all-namespaces
    Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
  1. 次のように入力して、コンポーネントのPodを見始める。
kubectl get pod -n kube-system -w
  1. 最初のノードの初期化が完了した後にのみ、新しいコントロールプレーンノードを結合させる。

次の例でCONTROL_PLANE_IPSは、他のコントロールプレーンノードのIPアドレスに置き換えます。

USER=hogeuser
CONTROL_PLANE_IPS="10.64.20.190 10.64.20.186"
for host in ${CONTROL_PLANE_IPS}; do
    scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
    scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
    scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
    scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
    scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
    scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
    scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
    scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
    scp /etc/kubernetes/admin.conf "${USER}"@$host:
done

注意:上記のリストの証明書だけをコピーしてください。kubeadmは、参加しているコントロールプレーンインスタンスに必要なSANを使用して、残りの証明書を生成します。すべての証明書を誤ってコピーした場合、必要なSANが不足しているために追加のノードを作成できない可能性があります。

残りのコントロールプレーンノードの手順

ノード一つずつ、クラスタに組み込んでから次のノードに進むこと

  1. 前の手順で作成したファイルを scp を使用した場所に移動させる
USER=hogeuser
sudo mkdir -p /etc/kubernetes/pki/etcd
sudo mv /home/${USER}/ca.crt /etc/kubernetes/pki/
sudo mv /home/${USER}/ca.key /etc/kubernetes/pki/
sudo mv /home/${USER}/sa.pub /etc/kubernetes/pki/
sudo mv /home/${USER}/sa.key /etc/kubernetes/pki/
sudo mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
sudo mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
sudo mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
sudo mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
sudo mv /home/${USER}/admin.conf /etc/kubernetes/admin.conf
  1. 最初のノードで以前に渡されたjoinコマンドを使用して、このノードで起動( kubeadm init )します。これは次のようになります。
sudo kubeadm join 10.64.21.35:6443 --token c6c3id.bba2b5hih8ka9jx3 --discovery-token-ca-cert-hash sha256:b078841bd826050e1461341835ffc5a5b0cf2e32365a33794eb77512ca0c016a --experimental-control-plane

--experimental-control-plane フラグの追加に注意してください。このフラグは、このコントロールプレーンノードをクラスタに参加させることを自動化します。

コントロールプレーンノードをクラスタ追加失敗時は kubeadm reset して kubeadm init する。

  1. 次のように入力して、コンポーネントのポッドを見始めます。
kubectl get pod -n kube-system -w
  1. 残りのコントロールプレーンノードに対してこれらの手順を繰り返します。

workerノードのクラスタ組み込み

さきほどの kubeadm join のコマンドを --experimental-control-planeをつけないで 実行。 1台ずつpodが組み込まれて完了したことを確認してから実行していくこと。

unshareコマンドでLinuxのNamespaceに入門

Namespaceはコンテナで使われている技術の一つで、コンテナを理解深めるために知っておくとよいので調べてみた。 どんな使われ方をしているかというと、 例えばPID namespaceはPIDの number spaceを隔離する。 これは同じホスト上で実行されている2つのプロセスが同じPIDを持つことができるということになる。

namespaceがなくて隔離されてないとコンテナAがコンテナB、C、Dなどに filesystemのunmountやホスト名の変更、NWインターフェースの削除など できたりしてしまうので、必ず他のコンテナのプロセスがみれないようにする。

Namespace

Kernel/OSのリソースで、物理リソースは制限しない(cgroupsでする)が、 以下の項目についてNamespaceを分離する。 namespaceを分離した環境では、許可されたリソースしか見えなくなるので コンテナ内の要素だけ見えるように制限できる。

  • Mount Namespace(ファイルシステムのマウントポイントを分離: Namespace 内の mount / umount が他の Namespace に影響を与えないようにする)
  • UTS Namespace(ホスト名,ドメイン名の分離)
  • PID Namespace(PID 空間の分離、新しい PID Namespace では PID 1 から始まる)
  • IPC Namespace(セマフォ、MQ、共有メモリなどのプロセス間通信の分離)
  • User Namespace(UID、GID Namespaceの分離)
  • Network Namespace(インターフェース、ルーティングテーブル、ソケットなど)
  • cgroup Namespace(cgroupのルートディレクトリを分離)

cgroups

cpuやmemory、ディスクなどの物理リソース制限は cgroupsと呼ばれるカーネル機能で計測されアクセス制限される。 cgroupsではタスクをグループ化したり、そのグループ内のタスクに対して 以下のような物理リソースを制限できる。使用量やアクセスを制限する。

  • CPU
  • メモリ
  • ブロックデバイス(mmap可能なストレージとほぼ同義)
  • ネットワーク
  • /dev以下のデバイスファイル

unshareコマンドでNamespaceについて確認

unshare コマンドはparentから unshared されている namespaceを使用してプログラムを実行できるらしい。 unshare が新しいnamespace でどんなプログラムでも走らせることができるということ。実際にUTS Namespaceを例に unshareコマンドの動きを確認してみる。

    $ unshare -h
    
    Usage:
     unshare [options] <program> [<argument>...]
    
    Run a program with some namespaces unshared from the parent.
    
    オプション:
     -m, --mount               unshare mounts namespace
     -u, --uts                 unshare UTS namespace (hostname etc)
     -i, --ipc                 unshare System V IPC namespace
     -n, --net                 unshare network namespace
     -p, --pid                 unshare pid namespace
     -U, --user                unshare user namespace
     -f, --fork                fork before launching <program>
         --mount-proc[=<dir>]  mount proc filesystem first (implies --mount)
     -r, --map-root-user       map current user to root (implies --user)
         --propagation <slave|shared|private|unchanged>
                               modify mount propagation in mount namespace
     -s, --setgroups allow|deny  control the setgroups syscall in user namespaces
    
     -h, --help     display this help and exit
     -V, --version  output version information and exit
    
    For more details see unshare(1).

UTS Namespaceは名前空間ごとにホスト名やドメイン名を独自に持つことができる。 以下で UTS Namespace を unshare コマンドで操作してみる。

unshare -u /bin/shUTS名前空間を指定してunshare実行している これでホスト名、ドメイン名が分離されたので、 もとのホスト名は test-ozawa-dev01.amb-infra-dev.incvb.io だが、 unshare で切り替え後、 my-new-hostname に変更している。

もちろんこのホスト名は新たな namespace だけで有効なので、 unshareで起動したシェルを終了すると、ホスト名はもとに戻る。

    $ sudo su       # root userになる
    # uname -n   # 現在のhostnameを確認
    test01
    # unshare -u /bin/sh  # 新しいUTS namespaceでshellを作成
    sh-4.2# hostname my-new-hostname                 # hostnameを設定
    sh-4.2# uname -n                                 # 新しいhostnameを確認
    my-new-hostname
    sh-4.2# exit                        # 新しいUTS namespaceから出る
    exit
    # uname -n   # 元のhostnameが変更されていないことを確認
    test01

2018年読んだ本

2018年の読書メーター
読んだ本の数:15
読んだページ数:4818
ナイス数:70

アシュリーの戦争 -米軍特殊部隊を最前線で支えた、知られざる「女性部隊」の記録アシュリーの戦争 -米軍特殊部隊を最前線で支えた、知られざる「女性部隊」の記録感想
15時間ほどで読めた。CST(文化支援部隊)という女性メンバーだけの特殊部隊を追ったノンフィクション。CSTの役割はアフガニスタンでの戦闘でアフガニスタン人の女性や子供たちから情報を聞き出すことが彼女たちの任務で地上戦の最前線でレンジャーの任務を支えていました。主人公のアシュリーが英雄視されているようで、プロパガンダ的作品と感じとってしまいました。2016年にアメリカの女性兵士も地上戦闘に参加することが法的に認められたのも、このCSTがあってのことのようで米軍にとっても大きな影響を与えた部隊だと思います。
読了日:12月30日 著者:ゲイル・スマク・レモン
シベリアの掟シベリアの掟感想
15時間ほどで読めた。かなりの力作だけど面白かった。筆者の幼少期の自伝的小説。これはntddkさんが面白いとツイートしていたので読んだ。この本については多くは語るのは不要だと思いますので、この本についての感想は本の中で語られているシベリアの掟の引用で締めくくる。『私たちシベリアの民は、子供の頃から「言葉にフィルターをかける」、すなわちたとえ故意でなくとも決して過ちを犯さないよう、口から出る言葉に気をつけることを教えられてきた。シベリアの掟によれば、「一度口から飛び去った言葉は二度と戻ってこない」からだ。』
読了日:12月30日 著者:ニコライ・リリン
五色の虹 満州建国大学卒業生たちの戦後五色の虹 満州建国大学卒業生たちの戦後感想
8時間ほどで読めた。日本が満州国を建国した時の理念である五族協和満州国を引っ張っていくリーダーを育成するための学校である満州建国大学についての話。日・漢・朝・満・蒙から集められた超エリートの卒業生たちを取材することで少ない資料の中、どんな学校だったのか、どんなことを考えて授業を受けたのか、戦後どうなったのか話をきいて浮き彫りになっていく建国大学。日本の敗戦とともにエリートだった学生たちが不遇の待遇を受けていたりと、国がその優秀な人材を生かさず中には忙殺する亡国もあったりと、貴重な歴史的資料のような一冊。
読了日:12月30日 著者:三浦 英之
悪童日記 (ハヤカワepi文庫)悪童日記 (ハヤカワepi文庫)感想
5時間ほどで読めた。薄い小説だけどrebuild.fmで紹介されていたように凄まじい本。打ちのめされるような本。双子の男の子の日記の体でストーリーが進んでいきます。男の子たちの行動原理みたいなものが読み取れてるので、その行動が加速するにつれてページを捲るスピードも上がっていきました。全部で第3部らしいのですが、素晴らしい作品のようですのでほかも読んでみたいです。子供だから視点は狭いようで、大人とは別の視点を持ち戦争下の描写が書かれています。筆者の幼少期の体験を双子の男の子を通して追体験するような感覚です。
読了日:12月30日 著者:アゴタ クリストフ
UNIXという考え方―その設計思想と哲学UNIXという考え方―その設計思想と哲学感想
20時間ぐらいで読めたと思う。途中何年も積んだりしたので、正確な時間がわかないけど、薄いけど一気に読めるような文章ではないので読むのが大変だった。名著ということでとりあえず読んでみたのが良くないのか、あんまり理解できなかった。これだけみんな名著と思って読まれているのにUNIXという考え方で実装していないのは何故だと思うぐらい複雑だったり謎の処理つけたりしているので気をつけたいですね。Linuxコマンドは強力でそれを使うスクリプトも強力ですね。こんな内容の現代の本が読みたいのでオススメあったら教えて下さい。
読了日:11月30日 著者:Mike Gancarz
スラスラ読める JavaScript ふりがなプログラミング (ふりがなプログラミングシリーズ)スラスラ読める JavaScript ふりがなプログラミング (ふりがなプログラミングシリーズ)感想
6時間ほどで読めた。タイトルの通りスラスラ読めました。ES6でJavaScriptを説明していますので、これからJavaScriptを勉強したい人や、プログラミングを覚えたい人が読む最初の一冊には良いと思いました。ふりがなが全てに振ってあるので、これがどんな意味なのかを、おまじないなどでごまかしていないので納得して読み進めることができると思います。監修も及川卓也さんなので内容も安心して読むことができました。次のステップはどう進めていけばいいのかは書いてないので、どうステップを進めるかが難しいと思いました。
読了日:08月20日 著者:リブロワークス
Linuxサーバーセキュリティ徹底入門 オープンソースによるサーバー防衛の基本Linuxサーバーセキュリティ徹底入門 オープンソースによるサーバー防衛の基本感想
8時間ほどで読めました。CentOS6で内容は少し古いですが、現在動いているシステムを運用している人は読んでおくとよいと思いました。LinuxのOS全般とApacheやメールなどの設定でセキュアな設定について細かく書かれており、わかりやすく書かれています。どのような運用が望ましいのかなども自分で調べるには難しいですが、このような本が増えてほしいと思いました。MySQLなどのDBやPHPなどについては記載ありませんが、それらはどのような本を読めばいいかなどの動線があればよいなと思います。おすすめの一冊です。
読了日:08月16日 著者:中島 能和
インフラエンジニアの教科書2 スキルアップに効く技術と知識インフラエンジニアの教科書2 スキルアップに効く技術と知識感想
10時間ほどで読めた。インフラエンジニア教科書とのことなのでインフラエンジニアとして読んでみた。1は立ち読みしたことありますが、個人的には2の方が読んでいて面白かったです。オペレーションシステムの基礎知識をわりと実用的なレベルで紹介されていて、インフラエンジニアになった人のまさに教科書的な存在だと思います。個人的にはなるほどUnixなどで、手で動かしてから読んでから読んだ方が理解が深まると思います。OSの動作の理屈をいきなり本を読んで理解できる人は頭がいいと思うので、普通の人は手を動かすといいと思います。
読了日:07月31日 著者:佐野 裕
絵で見てわかるOS/ストレージ/ネットワーク~データベースはこう使っている (DB Magazine Selection)絵で見てわかるOS/ストレージ/ネットワーク~データベースはこう使っている (DB Magazine Selection)感想
読むのに30時間ぐらいかかったと思う。新人の時(6年前)から何度も途中で諦めたりした本。OSやストレージ、DBMS、ネットーワークとシステム周り全般のボトルネックがどこか、リソースをどう使っているのかなどをコマンド実行レイヤ、絵を通して詳細に解説してくれている。個人的には詳解システムパフォーマンスの前に読んでおくと良いのではないかと思った。内容はオンプレミスのシステム前提で少し古いので、今のクラウド時代での内容に即した内容ではどうなるか気になるし、どんな本がそれにあたるのか気になる。このような本読みたい。
読了日:07月31日 著者:小田 圭二
オブジェクト指向でなぜつくるのか 第2版オブジェクト指向でなぜつくるのか 第2版感想
15時間ほどで読めた。Pythonでクラスをどういう時に書くべきなのか、オブジェクト指向でプログラミングするとはどういうことなのかよくわかっていないのでオブジェクト指向とは何なのかじっくり調べたいと思って読みました。この本は1冊まるまる書いているので説明が豊富でわかりやすかったです。Pythonの入門本とかだと犬とか人間とかを例にしたりして解説するのが余計に混乱させられていたのでオブジェクト指向の歴史と意義やクラス(カプセル化)・継承・ポリモーフィズムがどう意味なのかわかりやすく説明してありよかったです。
読了日:07月31日 著者:平澤 章
アカマイ―知られざるインターネットの巨人 (角川EPUB選書)アカマイ―知られざるインターネットの巨人 (角川EPUB選書)感想
6時間ほどで読めた。インターネットのトラフィックがどう流れているのか、CDNのアカマイがどういう成り立ちでできたのか。どのようにサービスを成り立たせているのか、どのような仕組みなのかなどをわかりやすく説明されているので読んでいて楽しかったです。CDNを利用すればオリジンサーバへの負荷分散にもなり、ユーザも高速にアクセスができ本当に良いものです。ネットワークの基本のようなものと併せて本書を読むことでインターネットの大枠を掴むことができるのではないでしょうか。誰にでも読むことができるので、オススメの一冊です。
読了日:06月21日 著者:小川 晃通
MySQL徹底入門 第3版 ~5.5新機能対応~MySQL徹底入門 第3版 ~5.5新機能対応~感想
10時間ほどで読めた。4年ほど積んでいましたが、そろそろMySQLに入門したい気持ちになり読みました。内容としては設定から運用までわかりやすくまとめっていて良いと思います。内容も5.5なのでそこまで古くはないので今読んでも参考になりました。昨今MySQL8がリリースされましたが、ほとんどの運用されているMySQLのバージョンはおいくつでしょうか?そうですね。おわかりのように、この本の内容は古くはないのです。次は実践ハイパフォーマンスMySQLをよんでMySQLの内部にもう少し踏み込んでいきたいと思います。
読了日:05月30日 著者:遠藤 俊裕,坂井 恵,館山 聖司,鶴長 鎮一,とみた まさひろ,班石 悦夫,松信 嘉範
忘れられた巨人忘れられた巨人感想
40時間ぐらいかけて読んだ。カズオ・イシグロは初めて読みます。牧歌的なファンタジー小説という印象で、THE・ビッグオーのような世界観です。記憶を奪う霧のせいで過去が曖昧な老夫婦がある日息子の存在を思い出して、どこに住んでいるかも顔も思い出せない息子に会いに行こうと行くあてのない旅を初めます。舞台はイングランドで時代はアーサー王伝説なので、この物語には騎士王伝説の知識があるとより楽しめるのかもしれません。読んでいて、ついさっきの記憶も危うい人達の会話を延々と読むことになるので、かなりの混乱が起こりました。
読了日:05月02日 著者:カズオ イシグロ
データ分析基盤構築入門[Fluentd、Elasticsearch、Kibanaによるログ収集と可視化]データ分析基盤構築入門[Fluentd、Elasticsearch、Kibanaによるログ収集と可視化]感想
8時間ほどで読めた。EFKで可視化したい人向けの本。 td-agentとElasticStackはバージョンが上がってしまったので、この本の内容は古いが、十分参考になる内容です。Qiitaぐらいしか日本語での情報がないEFKの可視化について上手くまとまっています。また、運用Tipsについても詳しく書かれていますので、運用や監視の勘所やチューニングの勘所についても参考になります。これからEFKで可視化をしたいと思っている人にはお勧めの一冊です。データ分析基盤の本で代表的な一冊になると思います。おすすめです。
読了日:04月22日 著者:鈴木 健太,吉田 健太郎,大谷 純,道井 俊介
Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版感想
6時間ほどで読めた。AWSでWEB/DB構成をインスタンス1台ずつたてて、Wordpressを構築するLessonとなっています。新改訂版でコンソールが日本語対応となっているので、わかりやすくなっています。一部コンソール表示は変わっていましたが。他にもネットワークの説明、VPNやサブネットなどAWS特有の説明もされているのでAWSはじめに読む本には良いと思います。TCP/IP周りの説明もされていますので、サーバやネットワークの勉強がしたい人にもおすすめです。内容は薄いのでサクサクすすめてよいと思います。
読了日:01月11日 著者:玉川憲,片山暁雄,今井雄太,大澤文孝

読書メーター