Foreverly

メモ帳

Python入門者の集い #6 に参加してきました

f:id:oza__shu:20180126100847p:plain

突然ですが、煽られたのでこちらの勉強会にLTしてきました。

python-nyumon.connpass.com

発表したもの

最近PyQやっているので、PyQの紹介とPythonでライブラリ使ってLinuxと仲良くなりたい話をしました。 ファイルディスクリプタのところは自分でもスライド書いてて理解を整理できたので発表するのも勉強になってよい。 黒曜石を使ったのですが良かったけど、下むいて発表してしまったので、 事前の発表練習も必要だなと反省。

speakerdeck.com

その他感想

LTでPillowというライブラリを使ったモザイクアートがおもしろかった。 嫁は二次元にいると言っていて愛を感じた。 作ったものを公開するとたのしそうだし、周りからの反応もあると嬉しいだろうなあ。

懇親会で何人かに声をかけて貰えて、参考になったと言ってもらえたのは嬉しかったです。 次に発表するときは内容が濃いものにしたいので、Pythonで何か作ったものを発表したい。

発表する敷居が低いのでお勧めの勉強会です。

Apache Usedslotの見方

Apache Usedslotの見方をまとめてみた

稼働状況の表示(ステータス情報表示)

稼働中のプロセス数,待機中のプロセス数および各プロセスのステータス(R,W,Lなど)をWebブラウザに表示 この情報を基に,StartServers,MinSpareServers,MaxSpareServers,MaxClientsディレクティブなどをチューニングできる

server-statusハンドラの指定

ステータス情報の表示機能を利用するには,次に示すようにserver-statusハンドラを指定 Webサーバのステータス情報はアクセス制御して,エンドユーザには非公開にするのが一般的

<Location /server-status>
      SetHandler server-status
</Location>

URLの指定

ステータス情報を表示するには,Webブラウザやcurlで次の形式でURLを指定

http://IPアドレス/server-status?auto

autoはプレーンテキスト形式で表示できる

# curl http://127.0.0.1/server-status/?auto
Total Accesses: 4366246
Total kBytes: 77563940
CPULoad: .34465
Uptime: 150727
ReqPerSec: 28.9679
BytesPerSec: 526949
BytesPerReq: 18190.8
BusyWorkers: 13
IdleWorkers: 7
Scoreboard: K_KKKW._.K..K._.K___KK.W.K_.K..............................................(略)..........

※ステータス情報の表示機能で取得できる情報(auto指定がある場合)

  • Total accesses:合計アクセス回数
  • Total kBytes:合計通信量
  • CPU load:CPU使用率
  • Uptime:サーバプロセスの稼働時間(秒)
  • ReqperSec:1秒当たりのリクエスト数
  • BytesPerSec:1秒当たりの通信量
  • BytesPerReq:1リクエスト当たりの通信量
  • BusyWorkers:リクエスト処理中のサーバプロセス(スレッド)数
  • idle workers:リクエスト待ち状態のサーバプロセス(スレッド)数
  • Scoreboard:個々のスレッドの動作状況

※稼働中のサーバプロセスの状態の記号

  • _ : リクエスト待ち状態
  • S : 起動処理中
  • R : クライアントからのリクエストを受信中
  • W : リクエストの処理実行およびクライアントへレスポンス送信中
  • K : 持続型接続状態でリクエスト受信待ち
  • D : ルックアップ中
  • C : 接続を終了中
  • L : ログ出力処理中
  • G : graceful restartにおける処理終了待ち
  • I : スレッド停止中
  • . : 起動していない状態

見方のPoint

  • 「R」 or「W」 が多ければ待機プロセスを増やしたほうがいいので、MaxClients を増やす。
  • 「.」が多ければ、ServerLimit を減らす。
  • 「_」が多ければ、待機中プロセスが多いので、MinSpareServers と MaxSpareServers を減らす。

Apacheスコアボードの監視とチューニング

server-status

監視では

.と_を抜かした個数を分子にして、.を抜かした個数を分母にすれば以下のことがわかる。

  • Apache の同時接続数の割合を監視することができる
  • MaxClients に対しての 使用中のスロット数 の割合を監視対象の数値とすることができる

スケーラビリティの技術の基礎

サーバやアプリをクラウドに置くと安いし便利ってだけではなく、 クラウドで実現可能なことは全世界に展開するような WEBスケーラブルなシステムが作れることが可能になったこと。 技術的には大変だけど... 全世界に展開するようなWEBシステムに携わるかはわからないけど、 大規模分散システムの原理原則や考え方を知っておくのは何かの役に立つかもしれない。

複製管理における基礎技術について

べき等性

重複実行してもよい処理のこと

分散システムにおける一般的な問題

クライアントがあるリクエストに対し完了通知がこないときに、 リクエストを再送すると、すでに処理済みである場合がある

対応策1

重複実行してもよい(べき等・Idempotent)処理か確認し、 そうであれば何度でもリクエストに対し処理を起動する(固定ファイル新規作成処理など)

対応策2

ミドルウェアまたはアプリケーションがリクエストにIDを振って、 処理完了を記録することで処理済みの処理を2回起動しないようにする(課金処理など) 処理がまだなら課金処理をして、処理済みだったら終了通知を発行させる処理を入れれば、 同じリクエストを投げても、2重課金がおきない。

ACID

複数のデータストアに対するトランザクション(ひとかたまりの論理的な操作)の性質と 書き込みなどのトランザクション実施時において、ACID実現のために必要なこと(高コスト)

  • Atomicity(原子性):含まれる操作がすべて実行されるか、すべて実行されないかのいずれかの結果になる
    • 原子性のため、全ノードで全操作の実施可否を確認し、全ノードが実施可能である時の実施
  • Consistency(一貫性):アプリケーションが定める不変条件をみたす
    • 一貫性のための判断を実施可否確認時に行う
  • Isolation(孤立性):複数のトランザクションは互いに干渉しない(外部から、途中過程や部分的に実施された状態にアクセスできることはない)
    • 孤立性のため、実施時には影響がおよぶ領域への他の読み書きをブロックする
  • Durability(耐久性):一度実施することが確定すると、その変更は永続的に反映される。

2PCプロトコル

Two-Phase Commitment(2PC)Protocol

ACIDを達成するための有名なプロトコル

参加者はcommitまたはabortに投票

クォーラム(定足数)

読み書き操作に関する一貫性のみを考えると、 必ずしも全ノードを同一の状態に揃える必要はない

  • 最新値を読み出すことができる 
  • 最新値がシステム内に複数存在することはない

過半数と多数決 書き込みと読み込みの数を変えてコストを調整できる。

2QW(書き込み数) > n かつ QW(書き込み数) + QR(読み込み数) > n(全ノード数)

2PCとクォーラムはノードの増減は想定していないけど、 実際は増減する。障害やオートスケールなど

過半数の合意を取ることでノード増減してもクォーラムなどでも対応できる。

一貫性

データストアに格納された値が最新値(書き込みの発生順序が守られている)に関して データストア(を実現する複数ノード)で合意を取ることが前提(しかし高コスト) これがそうでない場合はどうだろうか。

複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うと タイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまう。

一貫性モデル

データを管理するデータストアと、アプリケーションプログラムの間の約束事

  • アプリケーションプログラムが守るべきルール
  • データストアが保証する性質

順序一貫性(Sequential Consistency)

どのような捜査の結果も、すべての読み書き操作があたかも直線的な順序で行われたとしたときの結果と同一となる 複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うとタイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまうことは許さない!!!! 内部実装としては、複数間で「次に扱う書き込み」について常に合意しながら進めることになる

因果一貫性(Causal Consistency)

因果的に関連している可能性のある書き込みは、必ず同じ順序で観測される 順序一貫性で許されなかった複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うとタイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまうことは許される

許されないことは?

aを読んでbを書き込むという因果関係の例

P1:Write x:=a P2:Read x -> a その後、Write x:=b P3:Read x -> a その後、Read x->b P4:Read x -> b その後、Read x->a

P4の観測結果はaを読んでbを書いたという把握できる順序関係に反している!!!

ベクトルタイムスタンプ

イベントの発生順序を判別するために使う論理クロックの一手法 物理クロックは誤差があるかもしれず、物理時間での前後関係をすべて気にする必要もない 時間ではなくて、前後関係がわかるタイムスタンプのこと

概要

各ノードは、「自分や他のノードでいくつかのイベントが起きたか」に関する情報をもつ 自ノードでイベントが発生したらインクリメント 他ノードへのメッセージ送信時には、自身が持つ情報を添えて送り、受信側はその情報を使って自身が持つ情報を更新

A[5,2,8]<Y[5,5,10] # Yの方が後 C[7,5,8]?Y[5,5,10] # どっちが先か後か断言できない

因果一貫性を実現するには書き込みマルチキャストだけをカウントする

オーバヘッドが少ないので、負荷を気にせずできる。

弱一貫性(Weak Consistency)

アプリケーション側から一貫性を保たせるには、API側で最新値を取らせる(Lockなど)、順序一貫性よりかは軽い。

  • 順序一貫性を保証する「同期変数」への操作を提供して、アプリケーションプログラム側において、一貫性が必要な場合に制御可能にする
    • データストアへLockをとるイメージ
  • すべての先行の書き込みがすべての場所で完了するまで、同期変数への操作は許容されない
    • 読み込み前に同期操作を行えば、最新値を読み取れる
    • Lockをとってから読めば、最新値をとれる
  • すべての先行の同期変数へのオペレーションが完了するまで、データへの操作は許容されない
    • 書き込み後に同期操作を行えば、進行中・部分的な書き込み、書き込み結果の伝搬を完了できる

イベンチュアル一貫性/結果一貫性(Eventual COnsistency)

更新は全てのコピーに伝搬される(更新がなければすべての複製は同一に収束していく) 書き込み同士の衝突はなくて、読み込みが必ずしも最新でなくても構わないデータストアに適している(DNS,Webキャッシュ) クラウドでは、スケーラビリティを重視するために採用されている(書き込み同士の衝突もあるし、読み込みは最新がよいが、それでも採用したほうが良いと考える)

クラウドサービスにおける設計

AmazonSQS

Amazon Simple Queue Service (SQS) マイクロサービス、分散システム、およびサーバーレスアプリケーション用の完全マネージド型メッセージキュー。

  • メッセージの送受信のためのキュー機能を提供する。
    • キューは複製されている。キューが飛んでデータが吹っ飛ばないようにしている。
    • 全てのキューが同じデータを持つ状態に「収束」していく(イベンチュアル一貫性)
    • キューを読み出した時、その前におくられたメッセージが含まれるとは限らない(FIFOとは言っていない。)

キューもworkerも落ちる可能性がある。

  • 疎結合アーキテクチャに寄与する
    • 提供者と利用者の待ち合わせなし
    • 空いたインスタンスが随時タスクを請負可能
  • 少なくとも一回実行のための仕様(2回以上しないという仕様は含まれていない)
    • 受信されたメッセージは、キューから取り除かれるけど、一定時間後に再びキューに含まれる
    • メッセージを受信してタスクを請け負ったプロセスが落ちる可能性を想定
    • タスクが完了したら、明示的にメッセージ削除をする
    • メッセージを重複して受信、処理がされる可能性がある。(処理中なのにメッセージが復活するケースなど)

AmazonSimpleDB/DynamoDB

イベンチュアル一貫性のあるデータストアで、Read/Writeするとなにがおきるか説明されてあるドキュメント。

ConsistencyのAWSのドキュメント

Amazon DynamoDB

  • 整合性があり、10 ミリ秒未満のレイテンシーを必要
  • すべての規模のアプリケーションに対応した、高速かつフレキシブルなNoSQLデータベースサービス
  • 完全マネージド型のクラウドデータベース
  • ドキュメントとキー値のストアモデルの両方をサポート
  • 支払いはスループットに発生する。1秒間に何回書き込みできるようにするか。

Amazon DynamoDB

料金概要

仮想的な測定指標に対してお金を払う。わかりづらい。

1 つの書き込みキャパシティーユニット (WCU) は、1 秒あたり最大 1 回の書き込みを提供 1 つの読み込みキャパシティーユニット (RCU) は、1 秒あたり最大 2 回の読み込みを提供

1 日に 500 万回の書き込みと500万回の結果的に整合性のある読み込みを実行する必要があるとします

結果的に整合性のある読み込み 常に最新というわけではない。誤差は出てくる。 1秒間に500万アクセスきても大丈夫だし、値段もわかる。 オートスケールさせると値段が見えにくくなってくる。

Amazon DynamoDB では、ユーザー定義プライマリキーを使用したGET/PUTオペレーションがサポート

  • BatchGetItems
    • BatchGetItems演算は、複数の項目の属性を複数のテーブルから、それぞれのプライマリキーを使用して返す。16件の応答のサイズ制限は1MBで、最大100個の項目を返す。強い整合性と結果整合性の両方をサポート
    • 16件の応答のサイズ制限は1MBで、最大100個の項目というクラウドとしては微小量の割合だけど、重い処理をしたときに他のユーザへ影響がでないようにしている。
  • BatchWriteItem
    • 1 回のトランザクションとしてではなく、1回のリクエストにより複数のテーブル間で複数の項目を挿入、置換、削除。PutまたはDeleteで最大 25 項目のバッチをサポートし、合計リクエストサイズは最大16MB。
  • UpdateItem
    • 既存の項目の属性を編集。条件付き演算子を使用して、項目の属性値が所定の条件に一致する場合にのみ更新を実行することも可能
    • カウンターのようなもの。GETして値をインクリメントしてPUTする。他の処理でインクリメントされていた場合、上書きするおそれがある。
    • この問題にはマルチスレッドではLockをとって対処する。
    • クラウドではLockは重いのでやらない、計算式を送り込む。計算式を送り込んで、サーバ側でロックをしてインクリメントすると負荷が低い。

Amazon DynamoDB では、スカラー値でアトミックなインクリメント演算とデクリメント演算を行うことができる。 アトミックとは今の値を読み込んで、+1して書き込むではなく、一度の処理でやる。 その他の書き込みなどは防がない。 タイムアウトなどしたら、書き込みはべき等ではない。 UpdateItemは正確な値を取りたいときには辞めたほうがいい。

Amazon DynamoDBはデータストアだけど、並行システム、マルチスレッドシステムの知識がないと使えない。 ユーザ側に並行システムを持つことに、これらの知識が求められている。

条件付きの書き込み

Aliceとボブがいる。今の価格が10とする。 Aliceが価格を8に更新する。Bobが後で価格を12に変更。するとAliceの価格が上書きされる。 値引きと値上げがタイミングによって起きている。 これを防ぐためにはユーザのプログラミングでUPDATEを投げる時には状況が変わっていることを前提にする。 もし価格が10なら8にする。もし価格が10なら12にする。というAPIを呼ぶ。 こういう可能性が知っていないと、気づけ無いし対応できない。 マルチスレッドプログラミングとおなじ。並行プログラミングもしっていないとだめ。 Lockすれば解決できるけど、ブロックしたくない、誰かのために他のひとを待たせたくないからこういう仕様。 悲観的、楽観的な発想。

DynamoDBにはScanもある。 1回のScanリクエストで、最大1MBのデータを取得 一度の処理で全データをみるのはやめてほしい。

ブロックさせない、他のシステムが遅くさせることはさせない。 プログラミングの不備があると、不整合によってユーザの手元のデータが消える。ユーザに試されている。

Key-Value Storeにおける思想

  • RDB
    • 依存関係の整理(正規化)に基づく設計,保存データ量の最小化
      • ex)「ID→氏名、会社名」「会社名→会社住所」
    • 実行時にSQLクエリに応じた複雑な処理が発生
      • 上の2テーブルを結合してIDから会社住所をひっぱってくる
    • 実行時にコストをとるような考え
  • KVS
    • あらかじめクエリパターンを考慮して、実行時には高速なget/putだけで済むような設計
      • ex)頻発するなら、「ID→会社名、会社住所」DBも、「会社住所→ID,氏名」DBもつくればいい
    • keyから引っ張ってくる、冗長的、書き込む時は書き直さないと正確に値を引っ張ってこれない、

内部版のDynamo

Amazonが使っていたDynamo

要求・仮定

  • 1MB未満くらいのデータの読み書き(keyへのget/put)ができればいい
    • 複数のデータにまたがる操作はない
    • Amazonの通販サイトの多くの部分がこれで動く
  • 可用性のために一貫性をゆるめる(ACIDのC)
    • 一つのkeyに対する読み書きだけ考えていて、一連操作性(I)のための保証は考えていない
  • 分布の99.9%以上に対し、読み書き数百msec未満
  • 一貫性や耐久性と性能などトレードオフは設定可能にしてある
  • 常に書き込み可能に
    • 2フェーズコミットや、イベンチュアル一貫性も使っていない
    • 古典的には、読み込みを軽くし、一貫性のための調整コスト(ブロックや失敗)は書き込みが追う
      • ex)書き込み全・読み込み1のQuorum
  • 一貫性のための競合解決方法は、アプリ開発者が決める柔軟性を残す
    • 「物理時間で最新のものを残す」などだけではなく
  • 少しずつノードを追加可能・対称性を重視(どのノードも同じ)・非中央集権・ノードは異種混合になっている

読み書きの実装

  • "Sloppy"(だらしない)Quorum
    • 読み書きリクエストを受け取ったノードは、N個の複製に送信し、読み書きそれぞれに定められた一定数(R,W)以上の確認をもって成功とする ※必ずR+W >N(全ノード数), 2W>Nにするとは言っていない
  • 書き込みの周知が想定ノードに届かなかった場合、別の代替ノードに送信 (可能であれば元のノードに書き戻す)
  • 耐久性への保証を犠牲にして性能を上げる設定
    • 基本的にはメモリ上のバッファを使って、そこへの書き込み完了を持って確認を返信する
    • バックグラウンドで随時二次記憶に書き込む(その前にクラッシュするとその複製では書き込みが失われる)
    • N個の書き込み周知先のうち、1つだけ二次記憶にすぐに書かせることで耐久性を少し向上

書き込み競合

  • 常に書き込み可能
    • 書き込みを受け付けた後で複製間の同期を行う
    • 並行する書き込みや一時的なネットワーク分断により、複数の「最新」値が存在しうる
  • ベクトルタイムスタンプを使ってバージョンを管理する
    • 把握できる因果順序がある場合、自動的に競合解決可能
  • そうでない場合は、次の書き込み手に意味を考慮した競合解決が委ねられる
  • アプリ依存の競合解決の例
    • ショッピングカート内の商品リストへ追加
    • セッションではなく二次記憶に保存している
  • 以上は論文から読み取れる内容
  • 同じ仕組みで「ショッピングカート内の商品リストから削除」を実現するとどうなるか
    • 削除はどうしているかは不明
    • 操作ログだけをもっているのかもしれない
  • 実際の発生率(複数の最新値が存在するときは)
    • 最新値が1つ:99.94%
    • 2つ:0.0005%
    • 3つ:0.00047%
    • 4つ:0.00009%
    • 最新値2つ以上は殆どbotせいらしい

CAP定理

  • 複数を用いるアプリケーションは、下記のうち3つのうち2つのみを実現することができる
  • 定理よりかは原理原則
    • Consistency(一貫性):複数全体での結果一致を守ってのデータ更新やその耐久性(Durability)、一貫性の程度もある
    • Availability(可用性):(十分に早く)結果を返答する
    • Partition Tolerance(ネットワーク分断への耐性):システムの一部が分断・故障しても機能する

CAP定理とクラウド

  • First Tier:リクエスト処理を行うWEBページ
    • すぐに応答を返さないといけない。一貫性を妥協して古い情報をみせるとか。APが重要でCを妥協する。
    • ユーザ体験を大事にする気持ち。
  • Second Tier:スケーラブルなKey-Valueデータ
  • Inner Tiers/Backend:データベースやインデックスバッチ分析処理など

  • フロントエンド側において、CよりAPを優先する例

    • バックエンド側と繋がらなくなっても、ユーザにはキャッシュやエラー時用の情報などを見せる
    • 予約状況DBとは別に、残室情報DBをもって、「残り◯室」といった宣伝表示に使うとしても、この残室情報DBは正確な最新値とは限らない
      • ユーザには最新ではない可能性と表示する
      • フロントエンドでキャッシュをもっててもいいかもしれない
    • 人気の限定商品の開始時に、在庫数がある程度減るまでは、システム全体で正確な在庫数を共有することはせず、各複製サーバがどんどん売ってしまう
      • ほんとはACIDやらないといけないけど、ある程度まではだいだいあるってことでガンガン売る
      • Amazonのあれとか

CAP定理の位置づけ

  • バックエンド側ではCが必要
  • Cのための技術は確立されている(RDBとか)のに、Cを実現すると、APが犠牲になるのか
    • 今のRDBははやいので、十分APを実現している論
    • いやいや、想定しているスケーラビリティが違うし、コストが違うと反論
  • CAP定理とは高いスケーラビリティを実現するために一貫性を弱める事を受け入れることが低コストな解であることが多いということ

BASEトランザクション

ACIDすてたもの ACIDに対するBASE

  • Basic Availability;基本的に常に利用可能,部分的にでもリクエストが返ってる(すべての機能ではないかもしれないが)
  • Soft-State Replication:耐久性のあるメモリ上のデータストア(キャッシュ)を活用、2次記憶に書き込むのが正義ではない
  • Eventual Consistency:イベンチュアル一貫性(書き込み操作完了時には、その最新値がシステム内で合意されたとは限らない)

「D.Pritchett(EBay),Base An Acid Alternative,2008」

※少しの不整合やACID違反を「正常動作」と定義することを真剣に検討すべき [Vogles(AmazonCTO)] 何らかの不整合がでることも0ではない

今までの話はクラウドでしかできないスケーラビリティがある。 サーバやアプリをクラウドに置くと安いってだけの話ではない。 技術的には大変だけど全世界に展開するようなWEBスケーラブルなシステムが作れることが可能になった。

REST(Representational State Transfer)

REST(Representational State Transfer)

Webシステムアーキテクチャの原則を定義したもの

原則

  • Client-server
  • Stateless
  • Cacheable
  • Uniform
  • interface
  • Layered system
  • Code-on-demand(optional)

こういう考え方にしたがって作ろうよっていうもの Webシステムのデザインといってよい

以下のものが求められている

  • 可用性
    • いつでもちゃんと返事が来る,応答待ちにならない
  • スケーラビリティー
    • 大量のリクエストを捌ける
  • 耐故障性
    • サーバ側でマシンが落ちていても構わない

WEB系企業が推奨しているけど、原則6個を全て使えとは言っていない。

原則6個が言っていること

  • Client-server:ユーザインタフェースとデータストレージの関心事を分離
  • Stateless:リクエスト内にそれを理解するための情報を全て含み、サーバ側に状態を持たせない
    • スケーラビリティ、監視の容易性、故障からの復帰の容易性
    • ステートフルだとやりとりしていたサーバが落ちたら情報が消える。
  • Cacheable:キャッシュ可能なものを明確に区別
    • 効率、スケーラビリティ
  • Uniform interface:単一インターフェースに統一
    • 単純さ、インターフェースと機能の分離
    • オブジェクト指向プログラミングのメソッドが沢山でてくる。
    • インターフェースはGET、PUT、POST、DELETEの4種類にする。
    • デザインをするのはGET、PUT、POST、DELETEのでしよう。
  • Layered system: 階層構造による不要な詳細の隠蔽
    • 複雑さの軽減、依存性の低減
    • 自分の直下のレイヤーだけを知っていればいい
  • Code-on-demand(optional):必要な機能の、クライアント側へのオンデマンドでの読み込み(オプション)
    • 実行時の拡張性

シェルスクリプト②(declare,配列,連想配列)

declareでの変数宣言

オプションをつけることで、変数に属性を付与できる

オプション 意味
-r 変数を読み取り専用
-i 変数を整数とする
-a 変数を配列とする
-A 変数を連想配列とする

指定しない場合は、型は文字列になる

読み取り専用ならreadonlyコマンドをつかうとよい

配列

配列は要素を並べて格納し、インデックスという番号で参照できるデータ構造。 インデックスは0からはじまる。

  • 配列の作成

配列変数の右辺に()を書いて、要素を記述する。これを複合代入と呼びます。

# fruit=(apple orange banana)

空の配列

# list=()

declareでの配列宣言

# declare -a arr1
  • 要素の参照

配列の要素の参照は${配列名[インデックス]}

# echo ${arr1[0]}

インデックスを指定しない場合は、インデックス0が参照される。

要素の数を取得するにはインデックス"@"を指定 ${#配列名[@}}

# echo ${#arr1[@]}
  • インデックスを利用した代入

bashの配列は連続している必要がないので、空のある配列は複合代入がつくれる。 以下だとインデックス2と4が空文字列の配列ができる。

# arr2=(test0 test1 [3]=test3 [5]=test5 test6 )
  • 配列の中身を変更
# test3=(hoge1 hogehoge)
# test3[1]=hoge2
# 
# echo ${test3[0]}
# echo ${test3[1]}
  • 要素の削除
# unset test3[1]
  • すべての要素の参照

配列のすべての要素を参照する場合は、インデックスに*や@を指定

# test4=(hoge0 hoge1 hoge2 hoge3 hoge4)
# echo ${test4[*]}
# echo ${test4[@]}

""でくくった違いも"$@"と"$"と同じで、 "${配列[@]}"は要素が個別の文字列として展開される "${配列[]}"は配列全体をIFS変数の最初の1文字(たいていはスペース)で連結した1つの文字列として展開される 基本的には"${配列[@]}"を使うのがよい。

配列のコピーに使える

# test4_2=("${test4[@]}")
  • 要素の追加

"${配列[@]}"の他の使い方は、元の配列の先頭や末尾に要素を追加した、新しい配列の作成です

# test4_3=(hoge5 hoge6 "${test4[@]}") # 配列の先頭に要素を追加
# test4_4=("${test4[@]}" hoge7 hoge8) # 配列の末尾に要素を追加
  • 値の存在するインデックスの取得

${!配列名[*]}や&{!配列名[@]}みたいに、配列名の前に!をつけると、配列の中で値が存在するインデックス一覧を取得できる

# echo ${!arr2[@]}

連想配列

連想配列はkeyとvalueのデータ構造 hashとかmapとかdictとか言われるやつ

declare -Aで連想配列宣言をする。 これを忘れると配列になってしまうので、注意。

# declare -A user=([id]=1 [name]=testuser)
  • 要素の参照
# echo ${user[id]}
# echo ${user[name]}
# echo ${#user[@]}
  • 要素の代入

連想配列ではキーを指定して、キーに対応する値があれば上書き。なければキーと値を追加します。

# user[name]=testuser2 # 上書き
# user[country]=Japan  # 新規にキーと値を追加
  • 要素の削除
    • unsetコマンド
# unset user[name]
  • すべての値の参照

  • キー一覧の取得

新しいシェルプログラミングの教科書はわかりやすくておすすめです。

新しいシェルプログラミングの教科書

新しいシェルプログラミングの教科書

Apache2.2の設定

前回のCentOS6.7の設定続き!

久しぶりにやったので、CentOS6.9でやりました... ipもよしなにお願いします。 2017年にApache2.2で申し訳ない

Apache2.2の設定

CentOS6系の設定を想定なのでApache2.2です。(古いね)

  • 以下内容

VirtualHostの設定をしてドメインにアクセスできること 名前ベースの仮想ホストで複数のホスト名をつける SSL通信ができること

Apacheインストール

インストール

# yum install httpd
# httpd -version
Server version: Apache/2.2.15 (Unix)

自動起動の設定

# chkconfig --list |grep httpd
httpd           0:off   1:off   2:off   3:off   4:off   5:off   6:off
#  chkconfig httpd on
# chkconfig --list |grep httpd
httpd           0:off   1:off   2:on    3:on    4:on    5:on    6:off

Apache設定ファイル

全体の設定と範囲を指定した設定の2つあります。 この範囲を「セクションコンテナ」といいます。 セクションコンテナの中で設定した項目はセクションコンテナ内だけの設定になるのです。

セクションコンテナの種類

    • ファイルを示す
    • URLを示す

Apacheの設定ファイルは、行毎に「ディレクティブ」と呼ばれる設定内容を記述します。 Apache2.2と2.4では記述の仕方が違うので設定ミスに注意が必要。 2.4使うならNginxを使ったほうがいいかも。。

ディレクティブの大文字小文字の区別はされません。 優先順位はの順番で解釈され、 先に解釈された設定は、後で解釈された設定で上書きされてしまうので、 アクセス制限などには気をつける点ですね。

編集が終わったら設定ファイルの書式が間違っていないかを確認するようにしましょう。

# service httpd configtest
Syntax OK

名前ベースの仮想ホスト

リクエストヘッダ内で指定されているHost:ヘッダーの情報からアクセス先のホストを識別できる

VirtualHostの設定

設定をスッキリさせたいので、conf.d配下に vhosts.confというVirtualHost専用の設定ファイルを作りました。 VirtualHostで指定したIPアドレスに一致した場合のみ設定が適用されます。

# cat conf.d/vhosts.conf
# 仮想ホストのIPアドレスを指定(Apache2.4では不要)
NameVirtualHost *:80

<VirtualHost *:80>
ServerAdmin sysadmin@test1.jp
DocumentRoot /var/www/vhosts/www.apachetest1.jp
ServerName www.apachetest1.jp
</VirtualHost>

<VirtualHost *:80>
ServerAdmin sysadmin@test1.jp
DocumentRoot /var/www/vhosts/www.apachetest2.jp
ServerName www.apachetest2.jp
</VirtualHost>

DocumentRootも作っておくます。 index.htmlも置いておきました。

# mkdir /var/www/vhosts/www.apachetest1.jp/
# cat /var/www/vhosts/www.apachetest1.jp/index.html
apachetest1 is OK
# mkdir /var/www/vhosts/www.apachetest2.jp
# cat /var/www/vhosts/www.apachetest2.jp/index.html
apachetest2 is OK

動作確認してみましょう。 Hostヘッダーを指定してcurlコマンドを実行すると見事index.htmlの情報をとってこれました;-)

# curl -H 'Host: www.apachetest1.jp' 192.168.33.111
apachetest1
# curl -H 'Host: www.apachetest2.jp' 192.168.33.111
apachetest2

Apache再起動時に以下のメッセージが出たので、hostsに記載。localhostで名前解決させればよさそう。

[root@www ~]# service httpd restart
Stopping httpd:                                            [  OK  ]
Starting httpd: httpd: apr_sockaddr_info_get() failed for www.apachetest1.jp
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
                                                           [  OK  ]
# vim /etc/hosts
# cat /etc/hosts
127.0.0.1   www.apachetest1.jp localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         www.apachetest1.jp localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@www ~]# service httpd restart
Stopping httpd:                                            [  OK  ]
Starting httpd:                                            [  OK  ]

QUIZ:ブラウザでindex.htmlの中身を確認したい場合はどうすればいいでしょうか?

SSL通信

let's encryptはローカルドメインには使えんので、今回はおれおれ証明書で! 本番環境ではlet's encryptもしくはverisignやcybertrustやrapid-sslなど商用のCAを使ってください!

商用CAもCSRをCAに送って、入金して、証明書がもらえたら、よしなに設定の流れです。

SSL化する理由はなんでしょうか

  • データの改竄防止
  • データ盗聴防止
  • CA(認証局)による認証によって、本物であることを証明する

何が必要でしょうか

  • openssl
    • SSLを利用するのに必要なソフトウェア
  • mod_ssl
    • OpenSSLを使ってApacheSSLに対応させるmodule

てなわけでインストール

# yum install -y mod_ssl openssl
# httpd -M | grep ssl
Syntax OK
 ssl_module (shared)

OpenSSLコマンドで以下のファイルを用意します。 複数ドメインを持つ場合は命名規則を立てたほうがよいですね。 CommonName.作成日.ファイルタイプなど

鍵の置き場所はmod_sslをインストールしたらデフォルトはconf.d/ssl.confになると思います。 また、443ポートで受けます。

# grep SSLCert conf.d/ssl.conf
# Point SSLCertificateFile at a PEM encoded certificate.  If
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
# grep Listen conf.d/ssl.conf
Listen 443

今回はvhosts.confに設置場所を以下に指定してそこに置いてみましょう。

mkdir /etc/httpd/conf/ssl
# vim vhosts.conf
<VirtualHost *:443>
  ServerName www.apachetest1.jp
  DocumentRoot /var/www/test1
  SSLEngine on
  SSLCertificateFile /etc/httpd/conf/ssl/server.crt
  SSLCertificateKeyFile /etc/httpd/conf/ssl/server.key
</VirtualHost>

デフォルトのssl.confでSSLEngine onコメントアウトしておきましょう~~

# grep SSLEngine /etc/httpd/conf.d/ssl.conf
# SSLEngine on

上のコメントアウトをしなかったら、以下のエラーメッセージが出てApacheが起動しなくなりました..

[error] Illegal attempt to re-initialise SSL for server (theoretically shouldn't happen!)

秘密鍵の作成

# openssl genrsa -out server.key 2048

genrsa: RSA形式の秘密鍵を作成 -out 秘密鍵: 秘密鍵の名前指定 2048: 2048バイト長の鍵を作成

CSR作成

今回はwww.apachetest1.jpだけHTTPS

# openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.........................................+++
...+++
e is 65537 (0x10001)
[root@localhost ssl]# openssl req -new -sha256 -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) [Default City]:Shinjuku-ku
Organization Name (eg, company) [Default Company Ltd]:Default Company Ltd
Organizational Unit Name (eg, section) []:section
Common Name (eg, your name or your server's hostname) []:www.apachetest1.jp
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CommonNameとbit長など間違いないか確認

# openssl req -in server.csr -text
Certificate Request:
   Data:
       Version: 0 (0x0)
       Subject: C=JP, ST=Tokyo, L=Shinjuku-ku, O=Default Company Ltd, OU=Technology, CN=www.apachetest1.jp
       Subject Public Key Info:
           Public Key Algorithm: rsaEncryption
               Public-Key: (2048 bit)
               Modulus:
                   00:ca:d9:de:81:97:43:ff:21:d9:4b:7c:26:50:e1:
  • CSR作成の前に以下の項目を確認しておくといいです。
DN フィールド 説明
Country Name (2 letter code) 二文字の国名 JP
State or Province Name (full name) 都道府県 Tokyo, Tokyo-to
Locality Name (eg, city) 市区町村 Shinjuku-ku
Organization Name (eg, company) 会社名・組織名 Default Company Ltd
Organizational Unit Name(eg, section) 部署名 Technology
Common Name (eg, YOUR name) ドメイン名と同。間違えるとサイトみれなくなるので注意!!! 「http://」や「https://」は入力不要
www有りの場合 www.example.com
wwwなしの場合 example.com
サブドメインの場合 japan.example.com
サブドメイン無制限の場合 *.example.com
Email Address メールアドレス test@example.jp(値なしでも可)
A challenge password 値なし
An optional company name 値なし

証明書を作成し自己署名する

# openssl x509 -in server.csr -days 365 -req -signkey server.key -out server.crt
Signature ok
subject=/C=JP/ST=Tokyo/L=Shinjuku-ku/O=Default Company Ltd/OU=Technology/CN=www.apachetest1.jp
Getting Private key

-req: 入力ファイルがCSRファイルであることを指定 -days: 証明書の有効期限 x509: X.509形式の証明書を作成 -signkey 秘密鍵ファイル: 自己証明書作成時に使用するオプション。秘密鍵ファイルを指定

iptablesの設定を行う

iptablesがインストールされていることを確認

# which iptables
/sbin/iptables

iptablesのポリシー

以下の基本ポリシーを”DROP”(拒否)、”ACCEPT”(許可)のどちらかに設定します。

INPUT:サーバーに入ってくる通信のポリシー。基本ポリシーを”DROP”にして、あとで個別のポートに対して許可する設定にする。 OUTPUT:サーバーから出て行く通信のポリシー。基本ポリシーは”ACCEPT“にする。 FORWARD:受信したデータを他のサーバーへ転送する際に適用される設定。ここでは、特に転送するサーバーは無いので”DROP”。

現状設定されていないです。  

# iptables -nvL --line
Chain INPUT (policy ACCEPT 6332 packets, 472K bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 3243 packets, 254K bytes)
num   pkts bytes target     prot opt in     out     source               destination

以下の設定ファイルを作成していきます。 記載内容はさくらインターネットさんの記事を参照で

# vim /etc/sysconfig/iptables

設定を反映させます。

# service iptables restart
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
iptables: Applying firewall rules:                         [  OK  ]

22,53,80,443ポートが開放されていることが確認できました。

# iptables -nvL --line
Chain INPUT (policy DROP 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
2        0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:0x3F/0x00
3        0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:!0x17/0x02 state NEW
4        0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:0x3F/0x3F
5        0     0 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0           icmp type 8 limit: up to 1/min burst 10 mode srcip htable-expire 120000
6       11   576 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
7        0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0           udp spt:53
8        0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22 flags:0x17/0x02 limit: up to 1/min burst 10 mode srcip htable-expire 120000
9        0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:80
10       0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp dpt:443

Chain FORWARD (policy DROP 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6 packets, 448 bytes)
num   pkts bytes target     prot opt in     out     source               destination

コマンドなら以下ですね

iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT

graceful再起動で反映

/etc/init.d/httpd graceful

https://www.apachetest1.jpにアクセスできることを確認

シェルスクリプト①

シェルスクリプトまとめ

一行目のおまじない

1行目にシェバンと呼ぶ、bashで動作しますよというおまじないがある。

#!/bin/bash

変数

変数宣言では空白文字列は入れてはだめ

変数=値

declareで宣言していない場合は値は文字列とされる。

値にスペースやタブを含みたい場合はシングル[ダブル]クォートでくくる

test='test test'

空白文字も格納できる

empty1=

or

empty2=''

変数を参照する場合は変数名の前に$をつける

$test

宣言していない変数を参照してもエラーにならず、空白文字として実行されてしまうので注意

環境変数

環境変数は親プロセスから子プロセスに引き継がれるもので、親プロセスのシェルで設定されていた環境変数の値は子プロセスにコピーされコマンドから参照可能。

printenvコマンドで環境変数の一覧を表示可能

環境変数の定義

exportコマンドで環境変数の定義が可能

CONFIG_NAME=test #変数CONFIG_NAMEを宣言
export CONFIG_NAME #exportで環境変数にする

or

export CONFIG_NAME=test #一行で変数定義と環境変数にするのもできる

特殊なシェル変数

ホームディレクトリの表示

  • $HOME

カレントディレクトリの表示

ログインユーザーのログインシェル

  • $SHELL

現在動作しているbashコマンドのフルパス

現在動作しているbashのバージョン情報

現在実行しているシェルスクリプトの行番号を格納している。 いま何行目なのか出力できるのでデバックなどに使える。

  • $LINENO

ロケールを指定

  • $LANG

localeコマンドで現在設定されているロケールが確認できる

PATHは、シェルがコマンドを探すディレクトリを指定するための変数 ディレクトリは:で区切る コマンド検索パスと呼ばれ、左から順に探していく

  • $PATH

コマンドを配置するためのディレクトリを作成した場合、コマンドを探すパスを追加したい場合、このPATHを使う。 以下は現在のパスに加えてホームディレクトリ配下のbinディレクトリからもコマンドを探すように指定

PATH=$HOME/bin:$PATH

IFSはシェルの区切り文字を指定するための変数 bashは文字列を単語に分割する必要があるときは、ここの文字列を区切り文字として使う。 通常はスペース、タブ、改行が設定されている。

$ echo "$IFS" |od -a
0000000  sp  ht  nl  nl
0000004

odコマンドはファイルの内容を8進数で表示しますが、-aをつけると、制御文字などが文字の名前で出力される sp=スペース、ht=タブ、nlは改行

位置パラメータ

$1,$2のように1から始まる数値を名前に持つ変数で、 引数を参照することができる。

位置パラメータをすべて参照する場合は、$*や$@で可能

ダブルクォートでくくると動作が変わるので注意 "$@"=位置パラメータそれぞれが文字列として展開される "$*"=連結した一つの文字パラメータとして展開されてしまう。

なので$@を使ったほうが意図しない動作を起こさない。

特殊パラメータ

変数名 内容
$# 位置パラメータの個数
$? 直前に実行したコマンドの終了ステータス値
$$ 現在のプロセスのプロセスID
$! 最後に実行したバックグラウンドコマンドのプロセスID

シェルスクリプトに与えられた引数の個数

  • $#

コマンド終了ステータス値 0は成功,0以外は失敗

  • $?

現在のプロセスIDを表示

  • $$

ユニークなファイル名を作る時にも使える

tempfile=/tmp/tempfile$$

バックグラウンドのプロセスIDを表示

  • $!

バックグラウンドで実行したプロセスをkillするときに使える