hiroportation

ITの話だったり、音楽の話、便利なガジェットの話題などを発信しています

機械学習アルゴリズムの復習(ロジスティック回帰)

ロジスティック回帰による線形クラス分類は、特徴量からクラス分けを行うために使う。


データの用意

method note
np.random.multivariate_normal([平均], [今日分散], 生成数) ランダムな多次元正規乱数の生成
train_test_split(x軸, y軸, 分割の割合) ホールドアウト検証用に各xy配列を訓練用と、検証用に分割


訓練、検証データ作成

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np
 
np.random.seed(seed=0)
X_0 = np.random.multivariate_normal( [5,5],  [[5,0],[0,5]],  20 ) # [5,5]:平均、[[5,0],[0,5]]:共分散、20:生成する個数
y_0 = np.zeros(len(X_0)) # 0のリストを生成(赤)
 
X_1 = np.random.multivariate_normal( [9,10],  [[6,0],[0,6]],  20 ) # [9,10]:平均、[[6,0],[0,6]]:共分散、20:生成する個数
y_1 = np.ones(len(X_1)) # 1のリストを生成(青)
 
X = np.vstack((X_0, X_1)) # vstack:縦方向に配列を結合
y = np.append(y_0, y_1)
 

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

X_train

array([[ 2.60572435,  7.35782574],
       [11.0987978 ,  8.40531949],
       [ 7.40193187,  9.04236372],
~snip~

y_train

array([0., 1., 1., 1., 0., 1., 1., 0., 1., 1., 0., 0., 0., 1., 1., 1., 0.,
       1., 1., 1., 0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1.,
       0., 0., 1., 1., 1., 1., 1., 0., 1., 0., 1., 1., 0., 1., 0., 0., 1.,
~snip~


正規化

method note
StandardScaler() データの標準化をやるクラス(データの平均や、標準偏差など)
sc.fit_transform() 変換式の計算と変換式を使ったデータ変換を行う

データセットを標準化

# 特徴データを標準化(平均 0、標準偏差 1)
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train) # fit と transform をまとめて行う
X_test_std = sc.transform(X_test) # fitの結果を使ってデータ変換

X_train_std

array([[-1.43501522, -0.15974167],
       [ 1.53555855,  0.17214758],
       [ 0.24252688,  0.37398948],
~snip~

X_test_std

array([[ 1.46270099e+00,  1.31652928e+00],
       [ 1.14335461e+00, -1.47439949e+00],
       [-1.50884298e+00, -2.68581862e-01],
~snip~


プロット

plt.scatter(X_train_std[y_train==0, 0], X_train_std[y_train==0, 1], c='red', marker='x', label='train 0')
plt.scatter(X_train_std[y_train==1, 0], X_train_std[y_train==1, 1], c='blue', marker='x', label='train 1')
plt.scatter(X_test_std[y_test==0, 0], X_test_std[y_test==0, 1], c='red', marker='o', s=60, label='test 0')
plt.scatter(X_test_std[y_test==1, 0], X_test_std[y_test==1, 1], c='blue', marker='o', s=60, label='test 1')
 
plt.legend(loc='upper left')

f:id:thelarklife1021:20210831144912p:plain:h300



学習

from sklearn.linear_model import LogisticRegression # ロジスティック回帰のクラスインポート
 
# 訓練
lr = LogisticRegression()
lr.fit(X_train_std, y_train)

# テストデータ 80個を分類
print (lr.predict(X_test_std))

# 精度を確認
print (lr.score(X_test_std, y_test))

lr.predict(X_test_std)

テストデータを0,1で分類

[1. 0. 0. 0. 1. 1. 0. 0. 0. 1. 1. 1. 1. 0. 1. 0. 0. 1. 0. 0. 1. 0. 0. 1.
 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 1. 1. 0. 1. 1. 1. 1. 0. 0. 1. 1. 0. 1. 0.]

精度

完全に分類できていない

0.8958333333333334



結果の可視化

# 切片を出力
print (lr.intercept_) 

# 重みを出力
print (lr.coef_) 


w_0 = lr.intercept_[0]
w_1 = lr.coef_[0,0]
w_2 = lr.coef_[0,1]
 
# 重みと切片を使って境界を作る
print(list(map(lambda x: (-w_1 * x - w_0)/w_2, [-2,2])))

# 境界線 プロット
plt.plot([-2,2], list(map(lambda x: (-w_1 * x - w_0)/w_2, [-2,2])))

# データを重ねる
plt.scatter(X_train_std[y_train==0, 0], X_train_std[y_train==0, 1], c='red', marker='x', label='train 0')
plt.scatter(X_train_std[y_train==1, 0], X_train_std[y_train==1, 1], c='blue', marker='x', label='train 1')
plt.scatter(X_test_std[y_test==0, 0], X_test_std[y_test==0, 1], c='red', marker='o', s=60, label='test 0')
plt.scatter(X_test_std[y_test==1, 0], X_test_std[y_test==1, 1], c='blue', marker='o', s=60, label='test 1')
plt.show()

f:id:thelarklife1021:20210831160333p:plain:h300

上記の赤と青はランダム関数で設定した平均と分散により位置が変化する。
現状、線形では完全に分類できていないため、精度も 0.8958333333333334 となってしまっている。
線形で綺麗に分類できるデータを作成したい場合は、 np.random.multivariate_normal の引数を調整してやる。
以下は各平均を離してあげることにより綺麗に分類している。

np.random.seed(seed=0)
X_0 = np.random.multivariate_normal( [1,1],  [[5,0],[0,5]],  20 ) # [5,5]:平均、[[5,0],[0,5]]:共分散、20:生成する個数
y_0 = np.zeros(len(X_0)) # 0のリストを生成
 
X_1 = np.random.multivariate_normal( [9,10],  [[6,0],[0,6]],  20 ) # [9,10]:平均、[[6,0],[0,6]]:共分散、20:生成する個数
y_1 = np.ones(len(X_1)) # 1のリストを生成

f:id:thelarklife1021:20210831160842p:plain:h300

精度も1.0になる
print (lr.score(X_test_std, y_test))

1.0

以上

ドミノピザのウルトラジャンボサイズはすごかった

先日ドミノピザでLサイズのピザとウルトラジャンボサイズのピザを注文したら、ウルトラジャンボサイズの大きさがすごかった。

f:id:thelarklife1021:20210509113814j:plain:h500

左がLサイズで右がウルトラジャンボサイズ

噂通りやばかったので、覚悟して注文した方が良さそう。

シドニーマラソン 2021 は バーチャル・ランになるらしい

www.sydneymarathon.jp

詳細は不明ですが、今年のシドニーラソンは現地人以外はバーチャル・ランになるらしいです。
5月中に詳細な告知があるとのこと。

コロナで当面は現地でのマラソンは不可能そうだからとても悲しい。

ちなみにバーチャル・ラン、私は可能であれば参加したいと思います。

ホットケーキ作りが上手くなってきた

f:id:thelarklife1021:20210425045802p:plain

f:id:thelarklife1021:20210425045708p:plain

どうでしょう?

コツ

  • とにかく温度管理が重要
  • 弱火がベスト、中火以上で短時間でやろうとすると、中が半生状態になる
  • 一枚焼いたら水でフライパンを毎回冷やしてください
  • 焼くときは蓋をして生地全体の温度を均等にあげる感じのイメージ
  • 生地は高い位置からフライパンに落とすことにより生地の形をまん丸にする
  • ふわっと仕上げたい場合はマヨネーズを入れると良いですよ(マヨネーズ含まれるお酢に効果があるのだとか)

またここの海老フライ食べたい

今年のGWはコロナで県境横断自粛中ですが、 去年は御殿場アウトレットに行っていました。

アウトレットは普通に買い物を楽しめたのですが、帰りにかつ榮(かつえい)によってジャンボエビフライととんかつをテイクアウトしました。

ここのジャンボエビフライがとても大きい! 付属のキャベツの千切り、ソース、タルタルも惜しみなく付いてきます。

f:id:thelarklife1021:20210501215800p:plain:h400

tsuboguchi.co.jp

エビフライ、とんかつ好きの方は御殿場アウトレット帰りに ここによることをお勧めします!

Terraform によるマルチクラウド(AWS / GCP / Azure)環境構築と動作検証

Terraform にてAWSGCP、Azure を使ったマルチクラウドを構築してみました。

今回使うgithubレポジトリは以下になります。

https://github.com/vn-cdr/multicloud-docker

1. 何を検証したいか

Terraform にて自由な形式のマルチクラウド環境を短時間で用意し、様々なケースを想定したアプリケーション開発役立てたい。
以下クラウドVPNで繋げてDirectory Serviceを設定するところまでを対象にしたいと思います。



2. Terraform 実行環境をコンテナで用意

Docker コンテナ上であれば、プラットフォームを気にすることなくTerraformが使えます。

AWS/Azure/GCP APIクライアントを一つのコンテナにて管理するようにする。複数にしても管理が大変なだけ。
※terraformコマンドをローカルホスト上で実行できるようにしたいが、今後別途考える

f:id:thelarklife1021:20210429050024p:plain:h500
terraform を コンテナ上で実行する



3. 接続設定

Terraform実行のためにAPIクライアントを準備します。 各クラウドAPI用ユーザの作成方法がやや異なります。



3.1. AWS APIクライアントの準備

AWSでは一般ユーザと同じIAM画面で作成できます。

認証に必要なパラメータ

AWS_ACCESS_KEY_ID="<aws_access_key_id>"
AWS_SECRET_ACCESS_KEY="<aws_secret_access_key>"

取得手順:


① IAMにてユーザを作成する

  • ユーザタブへ移動
  • ユーザ作成を押下
    f:id:thelarklife1021:20210429051555p:plain:h200


② ユーザ設定

  • ユーザ名設定
  • アクセス種類から「プログラムによるアクセス」をチェック
    f:id:thelarklife1021:20210429051804p:plain:h200


③ ユーザの権限設定

  • アクセス許可の設定から「既存ポリシーを直接アタッチ」を選択
  • ポリシー一覧からポリシーを選択(AdministratiorAccessを選べば全てのサービスのオーナー権限を設定できます)
    f:id:thelarklife1021:20210429051932p:plain:h200


④ タグ設定(オプション)

  • 必要な場合はここでタグを作成することにより、ユーザ管理に役立てられます
  • 最後に右下の設定確認をして完了
    f:id:thelarklife1021:20210429052020p:plain:h200



3.2. GCP APIクライアントの準備

GCPでは サービスアカウント にてCLIアクセスが可能になります。 サービスアカウントでは扱うアカウント情報が多いためか一般的にjsonファイルを指定してCLIアクセスするようです。

  • 認証に必要なパラメータ(GCPjsonファイルになります)
<account名>.json

※今回account名はgcp-service-accountで設定します。

取得手順: サービスアカウント作成


① サービスアカウントの作成

  • 「IAMと管理」からサービスタブを選択
  • 「サービスアカウントの作成」を押下
  • サービスアカウント名の設定や権限の設定を入力し進めていく
  • 完了を押下
    f:id:thelarklife1021:20210429052419p:plain:h200


② サービスアカウントのアカウント情報を取得

  • サービスアカウント一覧からの作成したアカウントの行の操作 列を選択して「鍵を作成」を選択する
  • そうするとjsonファイルがダウンロードされる
    f:id:thelarklife1021:20210429052506p:plain:h200


③ サービスアカウント権限の有効化

  • 使用するサービスアカウントのAPIを有効化します



3.3. Azure APIクライアントの準備

Azureでは サービスプリンシパル というユーザにてCLIアクセスを可能にします。

  • 認証に必要なパラメータ
export ARM_SUBSCRIPTION_ID="<arm_subscription_id>"
export ARM_CLIENT_ID="<arm_client_id>"
export ARM_CLIENT_SECRET="<arm_client_secret>"
export ARM_TENANT_ID="<arm_tenant_id>"


① アプリの登録


② 新規ユーザ作成

  • 新規作成を押下
    f:id:thelarklife1021:20210429052646p:plain:h200


③ 必要事項入力

  • 情報を入力すれば完了(名前入力して、"サポートされているアカウントの種類"はそのまま)
    f:id:thelarklife1021:20210429052750p:plain:h200

※手順は以下より https://docs.microsoft.com/ja-jp/azure/active-directory/develop/howto-create-service-principal-portal



4. 各クラウドVPN接続

今回Terraform で構築するマルチクラウド論理構成は以下のようになります。

f:id:thelarklife1021:20210430035956p:plain
マルチクラウド論理構成図



4.1. AWS/Azureクレデンシャルの設定

cd multicloud-docker

mkdir ./.secret

cat << EOL > ./.secret/admin-rc.txt
# AWS Credential
AWS_ACCESS_KEY_ID="<aws_access_key_id>"
AWS_SECRET_ACCESS_KEY="<aws_secret_access_key>"

# Azure Credential
export ARM_SUBSCRIPTION_ID="<arm_subscription_id>"
export ARM_CLIENT_ID="<arm_client_id>"
export ARM_CLIENT_SECRET="<arm_client_secret>"
export ARM_TENANT_ID="<arm_tenant_id>"
EOL

# GCPサービスアカウントファイルは以下に配置
vi ./.secret/gcp-service-account.json


4.2. terraform変数の設定

# サンプルからコピー
cp  terraform.tfvars.sample terraform.tfvars

# e.g. を参考に変数を埋める (詳しくは 5.3. terraform.tfvars を参照ください)
vi terraform.tfvars


4.3. コンテナ起動

# コンテナビルド
docker build . -t multicloud-docker:latest

# コンテナ起動
docker run -it --name multicloud-docker --env-file .secret/admin-rc.txt multicloud-docker:latest

# コンテナに入れたことを確認
root@9f2f313b91bc:~# ls
aws  azure  google  main.tf  providers.tf  terraform.tfvars  variables.tf  vpn


4.4. Terraform 実行

# Terraform 初期設定
terraform init

# tfファイルのフォーマットチェック
terraform fmt -check

# Terraform 実行計画
terraform plan 

# Terraform 実行
time TF_LOG=debug TF_LOG_PATH="./terraform_log_`date "+%Y%m%d-%H%M%S"`" terraform apply


GCPAPIが無効になってエラーになる場合は有効化しておいてください。
terrafrom上でも有効化、無効化は可能ですが、エラーなどで意図せず有効化さたままになってしまうことを防ぐため、今回terraformには含めませんでした。


以下のようなエラーになる場合は terraform applyを再実行してくさい.

Error: Error creating customer gateway: MissingParameter: The request must contain the parameter ipAddress
    status code: 400, request id: XXXXXXXXX

  on vpn/aws.tf line 49, in resource "aws_customer_gateway" "azure":
  49: resource "aws_customer_gateway" "azure" {


4.5. terraform apply 実行結果

# エラーなく以下の通りメッセージが出ればデプロイ完了です

Apply complete! Resources: 65 added, 0 changed, 0 destroyed.

real 59m21.090s
user 0m12.054s
sys  0m22.781s
root@99ae86e95c98:~#


5. VMインスタンスの追加と動作確認

最後に動作確認の手順を記載します。 AWSGCP、Azureで同じ準備が必要になります。


5.1. 各クラウドVMインスタンスを追加

AWSGCP、Azure、にて以下条件でインスタンスを立ち上げる

  • VPCは今回terraformで作成したものを使うこと
  • サブネットは作成したVPC内のprivate subnetを使うこと
  • セキュリティグループはICMPとSSHのインバウンド、アウトバウンドを許可にすること
  • 他OSやスペックは好みのものを選んでください

ファイアウォールまたはACLの穴あけが必要ですので、各クラウドVPCに紐づいているACLに穴を開けてください

③外部IP(GIP)を設定している場合はsshできることを確認してください


5.2. ネットワーク疎通

下記の通りprivate-ipにtracepathすると インターネットを経由せずにAWS-GCP間で疎通が通ることを確認できる。 これはVPNトンネルにて通信がフォワーディングされているためである。

vn-cdr@instance-1:~$ tracepath 10.0.21.84
 1?: [LOCALHOST]                      pmtu 1390
 1:  no reply
 2:  10.0.21.84                                           30.783ms reached
     Resume: pmtu 1390 hops 2 back 2 
vn-cdr@instance-1:~$

※インターネットを通ってしまう場合や疎通できない場合はVPC周りのルートまたはファイアウォールを確認すること


6. terraform 処理概要

処理を担うtfファイルは、なるべくサービスごとに分けて作成

.
├── aws
│   ├── dns
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── vpc
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── azure
│   └── vnet
│       ├── main.tf
│       ├── output.tf
│       └── variables.tf
├── google
│   └── vpc
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── main.tf
├── providers.tf
├── terraform.tfstate
├── terraform.tfstate.backup
├── terraform.tfvars
├── terraform.tfvars.sample
├── variables.tf
└── vpn
    ├── aws.tf
    ├── azure.tf
    ├── google.tf
    └── variables.tf


6.1. 処理フロー

変数群は./providors.tf ./terraform.tfvars を順次読み込み
 ↓
AWSのawsのvpc、dns作成(./main.tf)
 ↓
GCPのVPC作成(./google/vpc/main.tf)
 ↓
AzureのVNET作成(./azure/vnet/main.tf)
 ↓
VPNに必要なVGW作成をAWS GCP Azure順で作成(./vpn/{aws.tf, google.tf, azure.tf})
 ↓
設定に問題がなければここで各クラウドの VGW でステータスが BGP Established になる


6.2. Dockerfile

Dockerfileでは各クラウドでterraformを実行するために必要なパッケージをインストールするように設定している。

FROM python:3.8

ARG pip_installer="https://bootstrap.pypa.io/get-pip.py"
ARG awscli_version="1.16.168"

# install aws-cli
RUN pip install awscli==${awscli_version}

# install sam
RUN pip install --user --upgrade aws-sam-cli
ENV PATH $PATH:/root/.local/bin

# install command & azure-cli.
RUN apt update && apt install -y less vim wget unzip curl npm && npm install azure-cli

# install terraform.
RUN wget https://releases.hashicorp.com/terraform/0.14.4/terraform_0.14.4_linux_amd64.zip && \
    unzip ./terraform_0.14.4_linux_amd64.zip -d /usr/local/bin/

# copy resources.
COPY ./main.tf /root/
COPY ./providers.tf /root/
COPY ./variables.tf /root/
COPY ./aws /root/aws
COPY ./google /root/google
COPY ./azure /root/azure
COPY ./vpn /root/vpn
COPY ./.secret/gcp-service-account.json /root/.config/gcloud/gcp-service-account.json
COPY ./terraform.tfvars /root/

# initialize command.
ENTRYPOINT ["/bin/bash"]

WORKDIR /root


6.3. terraform.tfvars

以下の通りTerraform実行に必要な引数を定義している。構築リージョンや各PWなどをここで設定します。

aws_dns_suffix = ""                     # e.g. "compute.internal"
aws_directory_service_password = ""     # e.g. "Password123!"
google_region = ""                      # e.g. "us-central1"
google_project_id = ""                  # e.g. "sample-project"
google_service_account_json = ""        # e.g. "gcp-service-account.json"
azure_resource_group_name = ""          # e.g. "resource_group"
azure_location = ""                     # e.g. "westeurope"
項目 説明
aws_dns_suffix AWS Managed Microsoft AD に使うDNSサフィックスを設定してください
aws_directory_service_password AWS Managed Microsoft AD に使うパスワードを設定してください
google_region リソースを作成するGCPのリージョンを設定してください
google_project_id 使用する Project ID を設定してください
google_service_account_json 使用するサービスアカウントのjsonファイルを指定してください
azure_resource_group_name Azureで作成するリソースグループを設定してください


6.4. メイン処理(一部抜粋)

AWS全体に必要な変数はここに書きます。VPCDNSVPNはモジュール化します。

./main.tf

module "aws_vpc" {
  source = "./aws/vpc"

  vpc_name     = "aws-test"
  cidr_block   = "10.0.0.0/16"
  subnet_count = 1
}

module "dns" {
  source = "./aws/dns"

  vpc_id                 = module.aws_vpc.vpc_id
  directory_name         = "test.internal"
  directory_password     = var.aws_directory_service_password
  dns_subnet_cidr_prefix = "10.0.0.0/20"
  private_route_table_id = module.aws_vpc.private_route_table_id
}

module "vpn" {
  source = "./vpn"

  aws_vpc_id          = module.aws_vpc.vpc_id
  aws_route_table_ids = [module.aws_vpc.private_route_table_id, module.aws_vpc.public_route_table_id]

  dns_network_acl_id = module.dns.dns_network_acl_id
}

./providers.tf

terraform {
  required_providers {
    aws = "~> 2.39.0"
  }
}

provider "aws" {
  region = var.aws_region
}

./variables.tf

variable "aws_region" {
  type        = string
  description = "aws region to use"
}

variable "aws_dns_suffix" {
  type        = string
  description = "DNS suffix Setting"
}

variable "aws_directory_service_password" {
  type        = string
  description = "password to use for the aws directory service (enabling DNS)"
}

VPC設定

VPC周りはここに書きます。VPC、subnet、セキュリティグループ、ルーティングテーブルなどを作成します。

./aws/vpc/main.tf

resource "aws_vpc" "main" {
  cidr_block = var.cidr_block

  tags = {
    Name = var.vpc_name
  }
}

resource "aws_subnet" "private" {
  count = var.subnet_count

  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.cidr_block, 4, count.index * 2 + 1)

  tags = {
    Name = "private-subnet-${count.index}"
  }
}

resource "aws_subnet" "public" {
  count = var.subnet_count

  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.cidr_block, 4, count.index * 2 + 2)

  tags = {
    Name = "public-subnet-${count.index}"
  }
}

resource "aws_network_acl" "public" {
  vpc_id     = aws_vpc.main.id
  subnet_ids = aws_subnet.public[*].id

  ingress {
    protocol   = "tcp"
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 80
    to_port    = 80
  }

  ingress {
    protocol   = "tcp"
    rule_no    = 200
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 443
    to_port    = 443
  }

  ingress {
    protocol   = "tcp"
    rule_no    = 400
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 1024
    to_port    = 65535
  }

  ingress {
    protocol   = "udp"
    rule_no    = 500
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 1024
    to_port    = 65535
  }

  ingress {
    protocol   = -1
    rule_no    = 1000
    action     = "allow"
    cidr_block = aws_vpc.main.cidr_block
    from_port  = 0
    to_port    = 0
  }

  egress {
    protocol   = -1
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  tags = {
    Name = "public-acl"
  }
}

resource "aws_network_acl" "private" {
  vpc_id     = aws_vpc.main.id
  subnet_ids = aws_subnet.private[*].id

  ingress {
    protocol   = "tcp"
    rule_no    = 400
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 1024
    to_port    = 65535
  }

  ingress {
    protocol   = "udp"
    rule_no    = 500
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 1024
    to_port    = 65535
  }

  ingress {
    protocol   = -1
    rule_no    = 1000
    action     = "allow"
    cidr_block = aws_vpc.main.cidr_block
    from_port  = 0
    to_port    = 0
  }

  egress {
    protocol   = -1
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  tags = {
    Name = "private-acl"
  }
}

# Gateways

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.vpc_name}-internet-gateway"
  }
}

resource "aws_eip" "nat" {
  vpc = true

  tags = {
    Name = "nat-elastic-ip"
  }

  depends_on = [aws_internet_gateway.gw]
}

resource "aws_nat_gateway" "gw" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public[0].id

  tags = {
    Name = "${var.vpc_name}-nat-gateway"
  }

  depends_on = [aws_internet_gateway.gw]
}

# Route Tables

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "public-route-table"
  }
}

resource "aws_route" "public_igw" {
  route_table_id            = aws_route_table.public.id
  destination_cidr_block    = "0.0.0.0/0"
  gateway_id = aws_internet_gateway.gw.id
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "private-route-table"
  }
}

resource "aws_route" "private_nat" {
  route_table_id            = aws_route_table.private.id
  destination_cidr_block    = "0.0.0.0/0"
  nat_gateway_id = aws_nat_gateway.gw.id
}

resource "aws_route_table_association" "public" {
  count = var.subnet_count

  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private" {
  count = var.subnet_count

  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private.id
}

以下は ./main.tf のローカル変数から取得します。 ./aws/vpc/variables.tf

variable "vpc_name" {
  type        = string
  description = "the name for the vpc"
}

variable "cidr_block" {
  type        = string
  description = "the cidr block to use for the vpc"
}

variable "subnet_count" {
  type        = number
  description = "how many private and private subnets to create"
}

以下は ./main.tf のモジュール呼び出しに返します。 ./aws/vpc/outputs.tf

output "vpc_id" {
  value       = aws_vpc.main.id
  description = "the id of the vpc"
}

output "vpc_cidr_block" {
  value       = aws_vpc.main.cidr_block
  description = "the cidr block of the vpc"
}

output "private_route_table_id" {
  value = aws_route_table.private.id
  description = "the private route table of the vpc"
}

output "public_route_table_id" {
  value = aws_route_table.public.id
  description = "the public route table of the vpc"
}

以下にはRoute53とSimpleADを設定します。 ./aws/dns/main.tf

data "aws_vpc" "main" {
  id = var.vpc_id
}

data "aws_availability_zones" "available" {
  state = "available"
}

locals {
  dns_subnet_availability_zones = slice(data.aws_availability_zones.available.names, 0, 2)
}

resource "aws_subnet" "dns" {
  count = length(local.dns_subnet_availability_zones)

  vpc_id            = var.vpc_id
  cidr_block        = cidrsubnet(var.dns_subnet_cidr_prefix, 4, count.index)
  availability_zone = local.dns_subnet_availability_zones[count.index]

  tags = {
    Name = "directory-service-${local.dns_subnet_availability_zones[count.index]}"
  }
}

resource "aws_route_table_association" "dns" {
  count = length(aws_subnet.dns)

  route_table_id = var.private_route_table_id
  subnet_id      = aws_subnet.dns[count.index].id
}

resource "aws_network_acl" "dns" {
  vpc_id     = var.vpc_id
  subnet_ids = aws_subnet.dns[*].id

  tags = {
    Name = "directory-service-acl"
  }
}

resource "aws_network_acl_rule" "ingress" {
  network_acl_id = aws_network_acl.dns.id

  egress      = false
  protocol    = -1
  rule_number = 100
  rule_action = "allow"
  cidr_block  = data.aws_vpc.main.cidr_block
  from_port   = 0
  to_port     = 0
}

resource "aws_network_acl_rule" "egress" {
  network_acl_id = aws_network_acl.dns.id

  egress      = true
  protocol    = -1
  rule_number = 100
  rule_action = "allow"
  cidr_block = "0.0.0.0/0"
  from_port   = 0
  to_port     = 0
}

resource "aws_directory_service_directory" "dns" {
  name        = var.directory_name
  description = "internal directory for dns forwarding over vpns"

  type = "SimpleAD"
  size = "Small"

  password = var.directory_password

  vpc_settings {
    vpc_id     = var.vpc_id
    subnet_ids = aws_subnet.dns[*].id
  }
}

./aws/dns/variables.tf

variable "vpc_id" {
  type        = string
  description = "the id of the aws vpc"
}

variable "private_route_table_id" {
  type        = string
  description = "private route table id of the aws vpc"
}

variable "directory_name" {
  type        = string
  description = "the name for the directory"
}

variable "directory_password" {
  type        = string
  description = "the admin password for the directory"
}

variable "dns_subnet_cidr_prefix" {
  type        = string
  description = "the cidr prefix for aws dns server subnets"
}

./aws/dns/output.tf

output "dns_ip_addresses" {
  value = split(",", join(",", aws_directory_service_directory.dns.dns_ip_addresses))
}

output "dns_network_acl_id" {
  value = aws_network_acl.dns.id
}



7. 今後について

続きの投稿は以下を予定していますが、投稿日は未定です。気長にお願いします。

  • SSOでのMFAの検証
  • Terraformによる CI/CD
  • マルチクラウドによるサービス冗長化検証

知っておきたかったLinuxサーバ設計、構築、運用知識まとめ

サーバ業務周りの管理、運用について役に立ちそうなナレッジをまとめました。
長期的に書いているため用語に統一性がなかったり、不足分など随時修正したいと思います。

1. サーバ設計

サーバスペックはどうするべき?

最近はクラウドにて必要スペックを自由に決めることができるため、最低限必要なスペックは確保したい。 そのため以下は検証、確認しておきたい。

  • スペック(CPU/RAM/Network)の最低要件は何か?
  • 停止可能なサーバか?(スケールアウト、アップできるか?)

使用するOSは?

利用したいミドルウェアに依存します。また、Rethatのように有料か無料かも判断材料に入ってきます。 例えば、UbuntuCentOSどちらも使える場合は、以下で判断すると良いかもです。

  • stable(安定ver.)なバージョンのOSを使う
  • 他のサーバOSに合わせる
  • 知見が多いOSで決める
  • 最小のOS(minimal)を選ぶようにしましょう

CentOS開発終了について

CentOS開発終了が決定されており、最新のCentOS8は 2021年12月31日でサポートが終了します(ただしCentOS7は2024年6月30日までサポート)。 CentOSCentOS Streamとなり、常に最新パッケージのみしか提供されないくなるため、MW以上の対応を常時やらないといけなくなります。

blog.centos.org

CentOSを導入される方はそのことを考慮した上で検討する必要があります。

ただ、Linuxディストリビューションはたくさんあるため、この機に色々冒険するのもありかと思います。 また最近はクラウド利用が進んでいるため、提供されているOSを使えば、大きな負担にはならないはずです。

MWは何を使うべきか

OS同様MWにも注意が必要です

  • MWは必要な分だけインストールしましょう
  • stableなバージョンが存在しているため、最新かつstableなバージョンを使うようにしましょう

Debianであれば dpkg -l | grep <パッケージ名>RHELであれば yum list installed | grep <パッケージ名> で検索して無理のない範囲で最小構成を目指しましょう。

Webサーバ構築にはどちらを使うべき?Apache?Nginx?

Nginxにする必要が特にない場合はプリインストールされているApacheにしましょう。 初期インストールされているものは推奨されているMWです。

サーバセキュリティで最低限押さえておきたいことは?

商用でサーバを使う場合最低限以下は確認したい。

  • listenしているポートは最低限か?
  • 個人ユーザには最低限の権限のみ与えているか?(認可の話)
  • インターネットに出る際にはプロキシを使っているか?
  • アンチウイルスは使っているか?

listenするポートは最小限にしましょう

ss -auntコマンドや、lsof -iコマンドで必要なポートだけlistenするようにしてください。 不明なポートはiptablesなどで閉じましょう。 つまりインストールされているMWの使用ポートは全て把握する必要があります。

ファイアウォール設定で送受信IPアドレス、ポートの通信制御はしておきましょう

初心者の場合は以下でざっくり設定をします 個人的な環境で設定を行う場合は

より細かいフィルタ制御をしたい方は iptables(CentOS/Ubuntu共通) を使用します

外部に出る際にはプロキシサーバを経由するようにする

  • セキュリティリスクとして内部でデータが不正に取得されても外部に出なければ良いので、プロキシサーバを経由させるようにしてデータ流出を防ぎましょう
  • 外部通信をするため毎回プロキシサーバの宛先を環境変数に設定をするようにしておくと良いとおもいます

随時パッチを当てるようにする

サポート切れや脆弱性のあるパッケージは随時アップデートを行いましょう 使用しているOSでパッケージが古いかどうかは apt-cache などで確認できます

linuxでのアンチウイルスソフトの検討

OSSとして ClamAV が比較的有名で検討材料になるかと思います。

個人アカウントで変更系コマンドは実行させないようにする

個人アカウントは必要最低限の権限のみ付与しましょう。 ホスト全体に影響がでるような変更は sudo をつけるようにします。

ログについて考えること

ホストが生成されるログはエビデンスにもなるため重要です。

  • 必ず所有者をrootにします
  • 特に理由がない場合は推奨されているrsyslogを使うようにする
  • 必要なログだけ残し、容量圧迫につながるログは削除したり、ローテーションを計画しましょう

ストレージ容量には気をつける

Linux ではストレージ容量がいっぱいになると大半のコマンドが打てなくなり、最悪サーバ上のアプリは停止します。

  • crontabなどで常時ログローテションするか、定期的に古いログ等は削除する設計はしておきましょう
  • sshとdfコマンドを組み合わせて常にホストの閾値を以下であることを監視する
  • Zabbixなどの監視ミドルウェアを使える場合はそれを使う
  • パブリッククラウドの場合にはそれぞれ専用監視システムがあるためそれを使うようにする

データベースはどう決めたら良いか

データベースだけでも膨大に種類があるため、慎重に決める必要がある。 以下の要件で決める。

  • 複雑なテーブル要件と各テーブルのリレーションが必要
    -> リレーショナルDB(OracleDB、MySQLPostgreSQLなど)

  • key、valueのように単純なテーブル構成(各テーブル同士のリレーショナルもいらない)
    -> NoSQL (Cassandra/MongoDB)

※Redisは特殊な用途で使われてるため、対象外。更なる検討が必要。

MySQLPostgreSQLか?

よく出る話題です。 以下要件で選択しましょう。

  • 大規模なら多機能な PostgreSQL
  • 小規模ならシンプルな MySQL

PostgreSQLは大規模対応のため、追記型アーキテクチャを採用しており、UPDATEを実行すると DELETE -> INSERTする形になっています。
MySQLような単純変換ではなく、実質2コマンドを実行するため小規模だとパフォーマンスが出ませんが、大規模での大量UPDATEとなるとINSERTの処理に探索が不要なためPostgreSQLの方が有利になります。

個人的にまずはMySQLを使用して、パフォーマンスが出なかったり、機能不足を感じた場合はPostgreSQLに移行するのもありかと思っています。

2.サーバ構築

環境変数の初期値

各ユーザのホームディレクトリには以下ファイルが生成されています。 これはユーザログイン時に毎回実行されるもので必要に応じて環境変数を追加してデフォルト環境変数として扱えるようにします。

~/.bashrc
~/.bash_profile

sudoの際に別の環境変数を設定したい

visudo のsetenvで設定します。 例えばsudo実行時は各ユーザ毎の実行ファイルを使うときになどが当たります。

%hoge        ALL=(ALL)       SETENV: /usr/bin/fuga

サーバ設定値の管理はどうするか?

監視系のように更新頻度の多いサーバ設定ファイルは、複数人数で何度も編集することによりファイルの中身が煩雑になったり、 万が一ファイル損失した際、リカバリが難しくなってしまいます。 できればgithubなどで管理し、crontabで定期的に git add -> git commit -> git push して管理しましょう。

Rootで直接ログインできないようにしましょう

特に理由がなければ、以下でrootでの直接ログインを不可にしておきましょう。

PermitRootLogin no

rootで作業したい場合は、ログイン後にsudoを利用してroot権限での作業をしましょう。そうしないと誰がrootで作業したのかログからはわからくなってしまいます。

visudoの落とし穴には気をつけよう

visudo内での設定順序によっては、sudoがうまく働かない場合があります。(例えば設定したのにNOPASSでsudo実行できないなど) これは visudo内のファイル設定が上から処理されるからです。 例えば以下のように設定してしまうと①のユーザ設定が②で上書きされてしまいます。

Aユーザの設定 --- ①


Aユーザを含むグループの設定 --- ②

そのため、①のような小さい単位の設定を行う場合はvisudo内の下部に記載してください。 また、そのようなトラブルを避けるため、include ディレクトリを設けて小さい単位の設定はその中で設定するルールにしておくと良いと思います。

ログ取得に条件式を設けて、明らかに不要なログは削除しましょう

rsyslogのRainerScriptによって設定ファイルにスクリプトを定義することができます。

www.rsyslog.com

※注意 単純にif文を使えばRainerScriptを使用しているとみなされるだが、一部のrsyslogのレガシー設定と併用が不可能なため気をつける必要がある。 またRainerScriptはrsyslogの機能の中でも比較的新しい機能であるためなるべく最新のrsyslogをインストールしてください。

rsyslogでログの受信制限を設定する

rsyslogの機能でratelimitが存在するためそれが一番簡単です。 またこれも RainerScriptで設定が可能なためratelimit以外の複雑なログ処理を必要とする場合はRainerScriptを使いましょう。

ログのローテーションは設定しましょう

ログは増え続けるものなので必ず商用サーバにはログローテションは必要です。 以下のように、一定周期でアーカイブサーバなどにローテーションしておきましょう。

# 以下をcrontabに設定する
find /var/log/ -mtime +365 -exec rm {} +;

systemctlのサービスを登録する方法

使用する実行ファイルは必要に応じてsystemctlで管理しましょう

vi /etc/systemd/system/<service名>.service

[Unit]
Description=<Description>   #Descriptionを書くことにより、systemctlコマンド実行時にサービス概要を確認できる
After=network.target syslog.target # 

[Service]
Type=simple #
ExecStartPre=/usr/local/sbin/<prestart時の実行コマンド>
ExecReload=/usr/local/sbin/<reload時の実行コマンド>
ExecStart=/usr/local/sbin/<start時の実行コマンド>
ExecStop=/bin/kill -s TERM $MAINPID

[Install] 
WantedBy=multi-user.target 

ログイン時の注意喚起のためにmotdを使う

  • よくある話としては本番機と検証機を間違えないようにするために設定する
  • 初心者向けにサーバの説明する時など

vi /etc/motd

##################
This is a Production  !!!!
##################

間違ってshutdownやrebootしないようにする

誤ったshutdownやrebootを防ぐにはいくつかの予防策があります。

shutdownやrebootの実行はroot権限が必要なため、例えばsudoでは実行できないようにすることで、個人アカウントで誤って実行することを防げます。

またmolly-guard を使う方法もあります。

sudo apt install molly-guard

これはsshでログインした時のみ有効ですが以下のようにshutdownしてもすぐにshutdownが実行されず、ホスト名を入力するように促されます。 正しいホスト名を入力すればそのままshutdownが実行されます。

# shutdown -h now
W: molly-guard: SSH session detected!
Please type in hostname of the machine to shutdown:

危険コマンド対策

有名な例をあげると以下ですが、他にもたくさんあるので、誤って打ちそうなコマンドを探すと良いでしょう。

危険コマンド 説明
sudo rm -rf / 全てを無にする
sudo mkfs.ext4 /dev/sda /dev/sdaとはOS実行に必要な情報が入る記憶領域なためこれを初期化すると・・・
mv /etc/* /dev/null ありとあらゆる設定ファイルを無にする
dd if=/dev/random of=/dev/sda /dev/sdaをランダムで上書きしてしまうので・・・

これらのコマンドは誤って打たないように同じ形のコマンド実行をaliasで実行不可な状態にしたり、スクリプトで監視したりする方法があります。

shellscriptを作るまでの流れ

構築を行っていく際に同じ工程が何度も発生するのであれば、 コマンドを結合したり、for分使ったりしてどんどんスクリプト化していきましょう。 その方がコマンドの打ち間違えも防げます。

繰り返し実行するコマンド群が発生
↓
コマンドを改修(コマンドの結合やスクリプト化)
↓
shellscriptを本格的に作成する

sshにも多要素認証を

セキュリティの重要性が高まる中でsshも多要素が求められてきたようです。 商用でのサーバ管理では多要素認証はsshのみならず全て必須になっていくでしょうから、sshの多要素認証対応はしておきましょう。

sshのデフォルトポートは変えておきましょう

インターネット直接繋がっているホストはDDoS攻撃の的にされるので、必ずsshftpなどのログイン、転送系のポートは変更しておきましょう。 セキュリティを強固にしておけば不正侵入は防げますが、DDoSによるリクエストをL4で受け取ってしまうとサーバダウンに繋がってしまいます。

3.サーバ運用

サーバ運用の際に使うコマンドについてまとめました

最低限知っておいた方が良いsshコマンドの使い方

インフラ業務ではsshとても多様するため正直すべてのオプションを把握しておいた方が良いですが、

コマンドオプション 説明
ssh -i <秘密鍵のパス> 鍵認証でアクセスする際に使います
ssh -p <ポート番号> sshポートは22ですが、それ以外でlistenされているホストにログインする際には指定する必要があります
ssh -L <転送元ポート番号>:<転送先ホスト名またはIP>:<転送先ポート番号> ポートフォワードに使います
ssh -A 一般的には秘密鍵の転送を経由する踏み台サーバにすることにより、ローカル環境にある秘密鍵で踏み台先のホストにアクセスできるようにします
ssh -o ServerAliveInterval=<keepaliceパケットの送信間隔> 意外によく使うオプションで、特にパブリッククラウドでのインスタンスでは長期間のセッションを防ぐため一定時間でsshセッションを切断する仕組みが儲けられています
このオプションを使うことにより一定間隔でkeepaliveパケットを投げることができるため、予期しない切断を防ぐことができます

ssh コマンドについて

sshコマンドの後ろにコマンドを書くことによってssh先でコマンドを実行できます

ssh  <ユーザ名>@<ホスト名>  <ssh先で実行したいコマンド>

スクリプト化した際など、パスワードなしでこれを実行したい場合は sshpassコマンドを併用します。

ログインマクロについて

Windowsの場合

Windowsですと、ターミナルツールとしてteratermが一般ですが、teratermをインストールする際に一緒にインストールできる LogmeTT の使用をお勧めしたいです。 とても使いやすいマクロツールでこれを使えば私が知る限りののログインプロセスは自動化できます。

ttssh2.osdn.jp

Macの場合

Macの場合はlinuxと同様に ssh_config を作成するようにします。 また、ターミナルツールには iTerm2 を使用します。

多段ポートフォワード

インフラ構築の場合、大抵は踏み台サーバを経由して対象サーバの構築や閲覧を行うかと思います。 踏み台先のサーバに例えば80番ポートでブラウザアクセスしたい場合はポートフォワードを使用します。 また踏み台を2回経由しないといけない場合などでは多段ポートフォワードを実行することによってブラウザアクセス等が可能になります。

ローカルPC
↓
踏み台サーバ1
↓
踏み台サーバ2
↓
対象サーバ(8080ポートでアクセスしたい!)

上記のような場合は以下手順でsshコマンドを実行します

# ローカルサーバで実行
ssh -L 2222:<localhost>:22  <ユーザ名>@<踏み台サーバ1>

# 踏み台サーバ1で実行
ssh -L 2223:<localhost>:22  <ユーザ名>@<踏み台サーバ2>

# 踏み台サーバ2で実行
ssh -L 8081:<localhost>:8080  <ユーザ名>@<対象サーバ>

ブラウザ localhost:8081 にアクセス

鍵転送について

踏み台先から鍵認証対象サーバにアクセスしたい場合は秘密鍵を踏み台に置かずに鍵転送を使用しよう。 秘密鍵は絶対に複製せず、ローカルにのみ置いて管理してください。

# ssh エージェントを有効化
$ eval `ssh-agent`

# 転送したい秘密鍵を sshエージェントに登録する
$ ssh-add ~/.ssh/id_rsa

# 鍵転送を実行してログイン
$ ssh -A <ログインユーザ>@<秘密鍵を使用したいホスト>

# 秘密鍵を使いたいホストにてsshエージェントが有効であることを確認(Agent名が)
$ ssh-add -l
Agent pid 37343

公開鍵の配布

Ansibleで一般的になったような気がしますが、公開鍵の転送には ssh-copy-id を使用します。

linuxには複数のテキストエディターが存在する

比較的有名なテキストエディタは以下でvimだけ使えれば良いと思います。

vimについて知っておくべきこと

vimにも種類がありまして、vim.tinyが最小構成のもので機能が限られます。
最初は使いやすいですが、すぐに編集スピードに限界を感じるかと思います。

bashではなくzshでプリインストールされているvimは比較高機能なものであり、これに慣れておくことをお勧めします。

実践Vim 思考のスピードで編集しよう! (アスキー書籍) | Drew Neil, 新丈 径 | 工学 | Kindleストア | Amazon

nanoは使いたくない vim が使いたい!

linux でvisudoなどを利用する際にテキストエディタがnanoになっている時があるのですが、vimに変えたい場合は以下で変更できます。

sudo update-alternatives --set editor /usr/bin/vim.basic

一時的にvimにしたい場合は以下のようにコマンド前に環境変数を入れて変更する

EDITOR=vim visudo

画面をクリーンしたい時

teratermのプロンプトが見えていない時や、termの表示が荒れてしまったときに、 よく ctrl+cctrl+z したり、 enter 連打したりと散見されますが、 想定外のコマンドが実行されてしまったり、ジョブを誤って停止してしまったりとトラブルに繋がってしまう可能性があるため、 ctrl+l で対処するようにしましょう。

ctrl+lでプロンプトに何もないこと確認した上で Enterを連打してログの見やすさを意識すると良いかと思います。

ショートカットキーは使いましょう

ctrl+a,e をはじめ、bashzsh上では様々なショートカットが使えるため、覚えるようにしましょう。

パッケージ更新ができない

古いLinux OSの場合は参照先のパッケージサーバがサポートを終了している場合があります。 その場合は /etc/apt/source.list にてパッケージサーバを変更する必要があります。 大抵はアーカイブとして見つかりますが、見つからない場合は早期にOSのバージョンアップが必要になります

cp、mvコマンドなどでのpath指定

pathは一歩間違えると事故につながりますので、短く書くように工夫しましょう。

cp /var/log/source_log /var/log/copy_log ❌

cp /var/log/{source_log, copy_log} ⭕️

秘密鍵の強度

技術の発展とともに秘密鍵の安全性も毎年変わってきます。 秘密鍵は一定の安全性を確保する必要があるため、使用時期に応じてNISTが定めている暗号とハッシュを使う必要があります。

https://www.ipa.go.jp/files/000013413.pdf

ssh-keyscan

sshでログインすると ~/.ssh/known_hosts というファイルがssh実行元に作成されますが、これをログインする前に作成しておきたい場合はssh-keyscanコマンドを使用します。 初回のログインでこの ~/.ssh/known_hosts を作るか選択する必要があるのですが、これがスクリプト作成や自動化などでの妨げになります。 ssh-keyscanコマンド を使った後にsshログインすれば聞かれなくなります。

vimで中身全部削除

コマンドモードで 1GdG と入力。間違えたらuでUNDOしましょう。

vimを使う時はInsertモードのまま移動しないように工夫しよう

ついInsertモードで移動しがちだが、vimは極力カーソル移動を減らすように設計されているのでコマンドモードを駆使して移動しよう。 行移動なら 1G 3G G など

vimで置き換えする際にはCheckオプションをつけましょう

ファイル内で置き換えを行う際にはコマンドモードにて :%s/<置換前>/<置換後>/gc としましょう。 cをつけることによって置換を一つずつ確認することができます。

sudo vimはやめよう

sudo vimでファイルを編集してしまうと所有者がrootでないファイルも所有者rootにされてしまいます。 大きなトラブルの元につながりますので、特に理由がない場合は代わりにsudoeditを使うと良いでしょう。

ログ確認をコマンドで

journalctl

重たいファイルはscpではなくrsyncで転送しよう

例えば転送に何時間もかかるようなファイルの移動はscpではなくrsyncを使って転送するようにしましょう。

shutdown をhistoryから消すとトラブル回避に繋がる

shutdownコマンドやrebootコマンドや影響範囲の広い変更コマンドは一度打った後にhistoryから削除しましょう

vi ~/.bash_history

$? $! とは

$?は終了ステータスです。直前のコマンドやスクリプトが正常に終了したかどうかを確認することができます。
特にスクリプト作成で使うことが多く、終了ステータスが 0以外(異常終了)の場合は例外処理に流したり、エラーメッセージに分岐させたりします。

<スクリプト or コマンド実行>

# 標準出力が0なら直前の正常終了、異常終了
echo $?

$! は直近で実行したプロセスIDを返します。
これもスクリプトで使われることが多く直前に実行したコマンドのプロセスIDを使って別処理に使ったりします。

エラーを隠す場合は

スクリプトなどでエラーが発生しても標準出力させたくない場合は以下のようにエラーを /dev/null に葬ってしまいます。

<コマンド実行> 2>/dev/null

tmux または screen でセッション共有をしましょう

商用環境で作業を行う際には複数のメンバが同じ画面を見ることになると思います。 直接サブディスプレイで画面を共有しても良いですが、tmuxやscreenを使ってセッションを共有すると良いと思います。

また、sshでリモートログインしている中でコマンド実行中に作業者PCの異常で通信不可になってもセッションは残るため、商用環境での作業では必ずtmuxまたはscreenのいずれかを利用しましょう。

スクリプトでも応用が効き、複数セッションを同時に立ち上げて各セッションでスクリプトを実行することも可能です。

ttylog でttyを共有する

同じホストの他ユーザの実行画面を閲覧できます。商用環境で作業をする際に便利です。 tmuxとは違いセッションを共有するのではなくttyに標準出力された内容を他ユーザにも標準出力される仕組みのようです。 そのためtmuxやscreenと違いセッションを奪うようなことはできません。

4. サーバ自体について

データセンタ作業では、日頃では中々触らないサーバ作業が存在します。 またデータセンター作業に必要な準備については以下に記載しています。

どのようなサーバが業務で使われているか?

メーカ別の主なサーバ製品は以下になります。 メーカによって仕様が全く違うため、使用する際にはメーカー、製品毎の検証は必要です。

サーバ名 感想
HPE ProLiant 数あるサーバの中で一番使いやすいサーバでした。IPMIの管理ユーティリティがしっかりとしていて、故障率も低かった印象です。
Fujitsu PRIMERGY 故障率がまあまあ高かったですが、ユーティリティ周りがしっかりとしていたため、使いやすい印象です。
DELL Power Edge ネットワーク周りに難ありでした。想定と違う動き(linkup/down)をする印象でHPEと違い、扱いづらかった印象です。
Supermicro 確か割安のサーバでしたが、故障率が高く、IPMIユーティリティにもいくつか難ありの印象です。

OpenRack

サーバは一般的にとてもでかく重いですが、以下のような小さなサーバも存在します。 これはクラウド需要を見越して、限られたスペースに必要なだけのサーバをできる限り効率的に収納するためのプロジェクトのようです

www.ctc-g.co.jp

Open Compute Project Japan | オープンコンピュートプロジェクトジャパンは、OCP (Open Compute Project) の日本コミュニティです。

IPMI とは

一般コンシューマー向けPCではほとんど見かけませんが、業務系サーバでは IPMI *1と呼ばれる特殊なインターフェースモジュールが存在します。 業務系サーバではこれがとても大事で主に遠隔からのサーバ管理に使われます。

主にやれることは以下になります

  • 電源オンオフ
  • サーバ詳細情報の閲覧、変更
  • ファームウェアのバージョンアップ
  • OSへのイメージマウント〜OSインストール
  • OS上の操作
  • REST APIでのサーバ操作

最近ではこのIPMIのREST APIを使用して、BIOSの設定、ファームウェアのVer調整などを構築自動化する動きもあるようです。

IPMIの必要性

IPMIは業務系サーバでは必須のインターフェースモジュールです。 例えばデータセンタ現地での作業を行った後に、サーバのOS再インストールや再起動、OS、フォームウェアのアップグレードなどが必要になった場合、 遠隔でこれらを行おうとすると、少なくともOSが起動してIPがアサインされるまではネットワーク疎通性がなくなってしまい管理ができなくなってしまいます。

これは場合によっては再度IPアサインをデータセンタ現地で行わないといけないため、業務上では大きな遅延に繋がってしまいます。 そのためIPMIでのIP設定はある意味、業務上での命綱となります。

そういった理由もあり、IPMIは物理的に独立したモジュールをサーバ本体に繋ぐような形で実現しており、OSの影響を受けないように設計されています。

BIOS (UEFI/Legacy)について

f:id:thelarklife1021:20210427063323p:plain

BIOSには大きく分けてUEFIとLegacyの2種類が存在しており、日頃見かける上記のようなBIOS画面はUEFIの画面になります。 UEFIは選択式のBIOSで、初心者でも簡単に操作が可能です。

対してLegacy BIOSCLIBIOSで、専用コマンドを入力して操作を行います。 UEFIでは対応していない機能が存在していた場合、Legacy BIOSに切り替えて設定を行います。

意外と言葉は出てくるので、概要だけ知っておくと良いと思います。

サーバでもSFPの時代

SFPは光インターフェースモジュールのことで、高速な通信を必要とする場合に使用されます。

ひと昔まではCiscoやJuniperなどのネットワーク機器に使われるのが一般的でしたが、最近ではサーバにも使われるようになっているようです。

また、一般コンシューマ向けのPCでも10GbE以上を搭載したい場合はこれを使う場合があります。 サーバだけでなくストレージでも使うので、データセンタ業務で必ず目にすると思います。

f:id:thelarklife1021:20210427064406p:plain

Amazon | QNAP(キューナップ) 10GbE SFP デュアルポート ネットワークカード QNAP専用 LAN-10G2SF-MLX | QNAP(キューナップ) | ネットワークカード 通販

  • SFP

f:id:thelarklife1021:20210427064327p:plain

Amazon | 10GBase-T SFP+モジュール, Cisco互換, Ubiquiti, Netgear, D-Link, Supermicro, TP-Link, Broadcom, Linksys, Avago, QNAP, RJ45コネクタ 10Gb/s 30m【5年保証】 | ADOP | パソコン・周辺機器 通販

*1:Intelligent Platform Management Interface