GCP青山さんのKubernetes完全ガイド読みましたインプレスさんより出版されているKubernetes完全ガイドを読みました。今まで行き当たりばったりでKubernetesを学んできた自分にとって、ものすごく学びが多かったですし、体系だった知識に整理できた一冊でした。図がたくさんあって理解もスムーズだったように思えます。Kubernetesにこれから触る人や私みたいにその場しのぎで学んできた方は絶対に読むべき一冊だと思います。ECSとGKEを使い始めて半年くらいですbook.impress.co.jp今回Kubernetes入門を見て改めて整理出来た項目を中心に挙げていきたいと思います。AffinityTaintsとTolerationsまとめkubectlContextの切り替えkubectlで操作する際に、操作対象のクラスターや認証情報が必要になります。実態はデフォルトだと、$HOME/.kube/configに記載されるらしいです。EKSとGKEを使っている場合に、複数の異なる環境にアクセスする場合にはContextの登録・切り替えが必要になります。今まで意識してこなかったと同時に、なぜ意識できなかったのか後悔の多いところです。applyのマニフェストのマージ方法というところになります。削除項目の差分適用は、新しくapplyされるマニフェストと、以前applyされたマニフェストとの比較で行われますが、追加・更新の項目の差分適用は以前applyされたマニフェストではなく、そこからKubernetesが変更を加えたものとの比較になります。yamlした結果との比較です。applyした結果、podのテンプレートと変更があれば、ローリングアップデートが起こりますね。以前適応したマニフェストにrollingUpdateについて書いてない状態から、rollingUpdateでmaxSurge25%、minUnAvailable25%ととしても、デフォルト値としてKubernetes側がセットした値とかわらないため、rollingUpdateは起こりません。削除項目の差分適用が以前applyされたマニフェストとの比較ということで、単純にcreateされたマニフェストから、applyで更新すると削除できない項目が出てきてしまいます。--save-configを使用すること。結合度の高いリソースである場合、例えばDeploymentとService、HPA、PDBは同じマニフェストファイルに書いても良いかもしれない。ConfigMapやSecretなどの疎結合のリソースに関してはマニフェストファイルを分けるべき。1つのファイルに書く場合には---で区切る。上から順に適用されていくため、書く順番は考慮されるべきである。再帰的にマニフェストを適応させることが出来るらしいので、ディレクトリ構造は極力わかりやすい形に持っていくことも可能。環境変数KUBE_EDITORで使用するエディタを指定できる。8080:3306でkubectlを実行したインスタンス(もしくはローカルマシン)のポート8080にアクセスすると対象Podの3306番のpodに接続される。デバッグは-v=6や-v=8を使う。WorkloadsリソースPodのデザインパターンサイドカー:メインコンテナに機能を追加する。個人的にはkinesisにアクセスログを流すflunetdコンテナとかがこれに当たると思いました。アンバサダー:外部システムとのやり取りの代理を行う。HAProxyコンテナのイメージです。AWSでいうならRDSの死活監視を行ってトラフィックを流すようなもの。アダプタ:外部からのアクセスのインターフェース。Exporterのようなイメージでしょうか。ローリングアップデート戦略にもとづいて、現在のReplicaSetのPod数を減らし新しいReplicaSetのPod数を増やしていく。ロールバックは、戻すリビジョンを指定しそれに対応するReplicaSetのPod数を増やし、現在のReplicaSetのPod数を減らしていく。sampleでローリングアップデートを止めることも可能。OndeleteはDaemosetのtemplateが変更されてもPodの更新は行われない。更新する場合には手動でPodをdeleteしオートヒーリングでPodを新たに作成する際に新しい定義となる。Updateですが、それより前のバージョンでのデフォルト値はOndeleteなので注意が必要。Setですが他のワークロードとの違いが顕著であるように見受けられます。以下に列挙していきます。作成されるPod名のSuffixは数字のインデックスが付与されたのものになる。scaleすると、今あるpodのIndexに+1された名前のPodが作成される。デフォルトでは、Ready状態になってから次のPodを作成し始める。増えるときは1つずつ。podManagementPolicyをPararellにするとdeploymentと同じように複数同時に起動する。UpdateはpodManagementPolicyにかかわらず1つずつ行われる。Networkに作り出される仮想IP。spec.ports.portがClusterIPで待ち受けるIPアドレス。spec.ports.targetPortが転送先コンテナのPort番号。手動でIPを固定する場合には、spec.ClusterIPを指定する。typeはClusterIP。特定のKubernetesノードで受信したトラフィックをコンテナに転送する。ExternalIPと似ている。これは、全ノードからトラフィックを受け付けるイメージ。Production環境でクラスタ外からトラフィックを受けるときに使用される。Kubernetesクラスタ外に疎通性のある仮想IPを払い出す。上の外部疎通ServiceはノードがSPOFになるが、これは外部のロードバランサを利用するためにノードの障害に強い。GCPとAWSでは、spec.loadBalancerSourceRangesに接続を許可する送信元IPアドレス範囲を指定できる。DNSラウンドロビンを使ったエンドポイントの提供。type:ClusterIPかつspec.ClusterIPがNoneのもの。クラスタ内からService名で名前解決するとCNAMEが返ってくる。外部サービスと疎結合にするために使用する。自分で指定したメンバに負荷分散することが出来る。EndPointとServiceを同じ名前で作ることで紐付けられる。typeは自由だがメリットを考えるとClusterIP一択。Selectorには何も指定しないIngressIngressリソースは一旦作成すると、その後あんまりにも触る機会が無く忘れてしまいがちです。L7のロードバランシングを行うリソース。L4のServiceと区別するため別のリソースとして扱われている。大別してクラスタ外のロードバランサを利用するものとクラスタ内のIngress用Podをデプロイするものがある。Ingressは事前に作成されたServiceをバックエンドとして転送を行う仕組みになっています。NodePortのリソースをまず用意します。その後に、IngressリソースのserviceNameの項目で結びつけます。L7なので当然パスベースルーティングもマニフェストで指定することが出来ます。https通信を行う場合には別途でSecretを作成する必要性がある。[namespace].svc.cluster.localという文言があるため。StorageリソースSecret1つのSecretの中に複数のKey-Value値。大体は、マニフェストから作成することが考えられる。base64でエンコードした値をマニフェストに埋め込みますが、その状態で管理してもセキュリティなど無いに等しいため、kubesec(https://github.com/shyiko/kubesec)と暗号鍵のマネージドサービスを使って暗号化した状態でリポジトリに保存しておくのが良きかと思う。MWYyZDFlMmU2N2Rm利用方法も複数ある。環境変数として渡す手段とVolumeとしてマウントする手段があります。Volumeとしてマウントした場合には動的なSecretの変更を行うことができます。それぞれ特定のKeyを渡す手段(spec.containers[].env[].valueFrom.secretKeyRef)とすべてのKeyを渡す手段(spec.containers[].envFrom.secretRef)がある。マニフェストに一覧性を持たせたい場合には前者、マニフェストを冗長にしたくない場合には後者を用いると良さげ。マニフェストから作成するケースが多そう。DBなどの資格情報はこれで管理する。/path/to/tls.crtで作成。keyが秘密鍵でcertが証明書ファイル。Registoryサーバと認証情報を引数で指定して作成する。podテンプレートのspec.containers[].imagePullSecretsで名前を指定する。自動で作成されるもの。利用者が意識することはほとんどないConfigMapConfigMapも殆どの場合でマニフェストから作成されるケースが多そう。Secretと違いBase64にエンコードしない点に注意。数値はダブルクォートで囲む。spec.containers[].envFrom.configMapRefとフィールドの名前が変わったくらい。ホスト上の領域を一時的に確保して利用できる。ホスト上の領域をコンテナにマッピングする。VMから読み取り専用でマウントされる。大体は予めデータを置いている想定かと思われる。Provisioningを使用しない場合にはラベルを付与することアクセスモードが複数あるが、大体はReadWriteOnceかReadOnlyManyが許可されていて、ReadWriteManyは許可されていない。Volumeの挙動を決められる。Volumeを要求するリソース。マニフェストに指定した条件にあうPVの割当をおこなう。podテンプレートの定義のspec.volumes[].persistentVolumeClaim.ClaimNameにPVCの名前を指定する。Provisioning。StorageClassを作成してPVCのマニフェストで指定する必要あり。Asia/Tokyopodの情報を参照。env[].valueFrom.fieldRef.fieldPathで指定する。コンテナの情報を参照。env[].valueFrom.resourceFieldRef.containerNameでコンテナの名前、env[].valueFrom.resourceFieldRef.resouceで取得するフィールドを指定する。SubPathマウントで特定のディレクトリをルートとしてマウントすることができる。mountPathがコンテナ側・subPathがボリューム側のディレクトリパスの指定となる。リソース管理とオートスケーリングrequestsとlimitrequests/limitともに各コンテナに定義する。この値が適正かどうか判断する際に役に立つのが、Nodeごとにリソース状況。hogehogeで見ることが可能。Requestsを大きくしすぎず、RequestsとLimitsの間に顕著な差を作らないことで適正なスケールアウトができるようになる。requests使用するリソースの下限を指定するもの。この値をもとにNodeにPodがスケジューリングされる。Limits使用するリソースの上限を指定するもの。この値を超えないようにPodの使用するリソース量がコントロールされる。メモリの場合はLimitsを超えるとOOMでコンテナプロセスが殺される。HPA(水平スケーリング)30秒に1回の頻度でオートスケーリングすべきかのチェックが走る。必要なレプリカ数の計算は以下で計算される。マニフェストの中でDeploymentを指定する。0.9その他LimitRangeでリソースの最小・最大値・デフォルト値を設定可能。新規でPodを作成するときに適用される。Effort」「Burstable」「Guaranteed」の順で停止される。RequestsとLimitsが指定されていてなおかつ値が同じ場合には「Guaranteed」となり削除されにくくなる。逆にどちらも指定していない場合には、停止される対象になりやすい。ResouceQuotaによってNameSpaceごとのリソース量・数に制限をかけることができる。ECSでもELBを使用したヘルスチェックがありますが、それよりもだいぶ柔軟です。ヘルスチェック方式は3種類あり、コマンドベースのチェック(exec)とHTTPベースのチェック(httpGet)、TCPベースのチェック(tcpSocket)がある。HTTPベースのチェックではホスト名やHeaderを設定できたりもする。ECSにおける「healthCheckGracePeriodSeconds(ヘルスチェックまでの猶予期間)」にあたるのがinitialDelaySecondsとなります。Probe失敗時にはPodを再起動する。メモリリークなどでPodの再起動なしには状態が回復しずらい場合に使用。ProbeReadの活用ではなくReadyの活用。トラフィックが流れる準備が出来ているかの確認。失敗時にはトラフィックを流さなくなる。コンテナライフサイクルrestartPolicyはPodに定義するものでPodが停止した場合にどのような挙動を取るか指定したものです。基本的には、終了コードにかかわらず起動するAlwaysなのではと思うのですが、Jobなどは終了コード0以外で再起動するOnFailureになるのではと思います。Neverは再起動を行わないものです。Containers初期化処理を行うためのコンテナ。InitContainersの処理が終わらないと、spec.containersで指定したコンテナが起動しない。GCSからファイルをGETしてくる必要性があるときなどに使用している。ECSにも欲しい...。postStart/preStopコンテナの起動・終了時に行われる処理。postStartは、コンテナのENTRYPOINTとほぼ同じタイミングで実行されるため、どうしてもコンテナ内部で処理をしなくてはいけない場合以外はinitContainersに処理をしてもらったほうが良さそう。Qiita)で知っていましたが、改めて別の図も交えてだとよりわかりやすいなと思う。spec.terminationGracePeriodSecondsをpreStop処理にかかる時間を考慮した時間にしないと、デフォルトの30秒がたった時点でSIGKILLコマンド、preStop処理の途中だった場合はSIGTERMコマンドが送られ、その2秒後にSIGKILLが送られる。Shatdownを行う場合に、コマンドだと非同期での実行となる場合があるのでsleepコマンドを入れておくことが望ましい。親リソース(ReplicaSet)が削除された場合には子リソースのPodを削除するという処理がはいる。この親子関係は、マニフェストのmetadata.ownerReferenceから読み取ることが可能。AffinityPodが特定のノード上でしか実行できないような条件付ができる。AntiAffinityこれは、matchExpressionで指定できるオペレータでNotがついているものを使えば実現できるもの。Affinityと同じだが、topologyKeyというのを指定する必要がある。topologyKeyはスケジューリング対象の範囲を示す。これはホストやゾーンなどを指定でき、matchExpressionのじょうけんを満たすPodのホストもしくはゾーンに配置するという意味になる。TaintsとTolerationsPodが条件を提示してNode側が許可する形のスケジューリング。affinityと違う点は他にもあり、考慮されるのがスケジューリングのときだけではないという点がある。まとめ本当にめっちゃめちゃ良い本だったので買いましょう。kubernetes2018年8月20日からGKEのマスターノードのVersionが1.8.xは1.9.7に、1.9.xは1.10.6に上がりました。ノードの自動アップグレードをonにしているクラスターは何もしなくても、ワーカーノードのVersionも上がってることかと思います。バージョン上がると急に期待している動作と違う挙動が見られることもあります。v1.9.7ではgcePersistentDiskのSubPathマウントがうまくいかず、Podが起動しないという事象が起こり得ます。本番運用しているシステムでワーカーノードの自動アップグレードはリスクがあります。そのため、マスターノードがバージョンアップしたタイミングで、新しいバージョンのノードプールを作成し、Podを新バージョンのノードプールで起動するのを確認していく方法を取るというプロジェクトが多いのではと思います。バージョンアップに際し、変更点を認識することが必要です。いうことで、ver.1.9.xになることでどのような変化があるのか調べてみました。deploymentなどのワークロードがGAになったこともあり、デフォルト値の変更などが気になるところです。以下を参考にいたしました。気になったことがあれば順次拾っていきます。v1へと変更になります。v1への開発は行われるが、その前のバージョンには行われないようなのでこの機会にマニフェストを変更しておきたいところです。v1beta1と表示されることがあります。これは、登録したマニフェストがapiServer内部で互換性のあるバージョンに関しては変換されるからです。--v=6と打つと、/apis/extensions/v1beta1/namespaces/...というリソースにアクセスしていることが分かる。deployments.v1.appsと打つと、表示されるマニフェストのapiVersionはapps/v1となり、オプションで--v=6として再度叩くと、apis/apps/v1/namespaces...というリソースにアクセスしていることが分かる。convertを使用して、グループバージョン間でマニフェストを変換できるとのことです。以下のコマンドで変換できます。--output-version='apps/v1'デフォルト値として、今までセットされていたものが外れているケースもある。.spec.selectorだ。今までは、デフォルト値として.template.metadata.labelsに記載された値をセットしていた。apps/v1からは明確な記載となる。webhooksがbetaになった。Controllerの拡張機能。Controllerは認証・認可の後、Objectが永続化する前にクライアントからの要求を受け入れるか判定する仕組み。2種類あり、mutatingはクライアントの要求書き換え、validatingはクライアントの要求を受け入れるかどうかを判断する。mutatingはマニフェストに、決まったアノテーションを付けたり、別コンテナ(例えばEnvoy)を付与したりできる。validatingは、マニフェストの内容やクライアントによって受容するか決められる。Controlを成立させる機能。GKEにおいても使えそうな感じがするのですが(Istioがこの仕組み使って自動でEnvoyを配置しているため)、方法がわかりませんでした。(CRD)というのがbetaになった。ユーザ独自で定義したリソースのこと。knativeもこの仕組みを使っているはず。aggregationで使うことができる。以下に出てくるcronTabはカスタムリソースのため組み込みのRoleでは制御されていない。ClusterRoleなので、namespace関係なし。Roleであれば、namespaceで分割する。pdbでデフォルト値をセットしなくなったらしいNetworkIPv6IPv6対応(alpha)したとのこと。GKEの場合にはクラスター作成時に指定をすることが必要なので、面倒かもしれない。CoreDNSCoreDNSがリリース。PersistentVolumeClaimのサイズは0より大きくなくてはいけない。GCEマルチクラスタでは、ノードを持たないゾーンでPersistentVolumeオブジェクトが動的にプロビジョニングされなくなります。まとめ1.9でBetaになった機能は拡張するためのものが多い。エンタープライズでも運用できるように改善されてきたイメージです。使いどころを間違えると管理がかなり大変になりそうので気をつけなきゃですね。記事書いてて思いましたが、まだまだ勉強が全然足らないですね...。golangGoの並行処理について書かれたブログmedium.comこのブログがイラスト付きで本当に可愛くGoの平行処理の基礎を教えてくれて良いんです。このブログを参考に整理してみるこのブログはある一つの例を取って、並行処理に記述しています。それは鉱物の発見・掘り出し・加工です。元のブログでは、それぞれの動作をするGopher君が描かれています(めちゃめちゃ愛らしいので是非見てください)。シングルスレッド処理とマルチスレッド処理の違いシングルスレッドGopher族Gary君が1人で鉱物の加工までをこなします。なんてマルチな才能なんだ。ブログではシングルスレッドのソースコードは載ってなかったので愚直に書きました。smeltedOre)}マルチスレッドGary君は鉱物の発見、Janeは掘り出し、Peterは加工を行うようにしました。ブログのほうではそれぞれをgoroutineにして処理を行っていました。そのまま写経するのも面白くないのでselectとcontextを使って書いてみました。<-ctx.Done()}channelchannelはgoroutineが相互にやり取りするときに使用されます。上の例だと、finderとminerがやり取りするときとminerがsmelterとやり取りするときに使用しています。goroutineはchannelに対して送信と受信ができます。<-を使用して表現します。受信このchannelという機構のおかげでJaneはGary君がmineをすべて見つけるのを待たずして、見つけた分から処理をしていくことができます。channelはいろんな状況でgoroutineをブロックします。これによって同期的な処理を行うことも可能です。バッファ付きチャネルとそうでないチャネルの2種類があります。チャネルに送信するときバッファがない場合は、ブロックされます。5)宣言のときにcapを指定しないとbufferなしのchannelになるはずです。contextgoの1.6まではcontextパッケージが無く自前で実装していたそうな。contextパッケージは、キャンセルを伝搬できるのも強みとのこと。SOTAまとめselectとchannelの組み合わせで楽に並行処理を実装できる。場合によっては、selectじゃなくてfor~rangeのほうが良いかもしれない。タイムアウト系はcontextパッケージを利用する。bashgsutilがどうしても使えないけどGCS使いたい例えば、KubernetesのPostStartとかPrestopでGCSからダウンロード/アップロードするとか。Dockerfile内でGCS使わなくてはいけなくなった。それだけのためにRUNして後でお片づけするのも時間ちょっと増えるし嫌みたいな時とか。curlでの処理アクセストークンを取得jqが使えれば、もっとちゃんとしたのが書けるのですが今回は無い想定で書いてます。良い方法があれば是非教えていただきたく。"https://www.googleapis.com/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${OBJECT_NAME}"k8sのdeploymentのpostStartに記述してみるアップロードするパスは後で見やすくなるように適宜スラッシュ入れて区切るべしってやつですね。アップロードが多くなるようなら日にちで一旦スラッシュ入れたりとかになるんでしょうか。preStopで複数行の記述をする方法に関しては以下を参考にしました。}]まとめコンテナの起動や削除は開発環境(ローカル)はcompose、検証・本番環境はECS/GKEだから使用頻度が高いのは調査系のみだった。dockerネットワークのメトリクス大事ですよね障害が発生したときなどに、何が起因なのか切り分ける場合などにネットワークのメトリクスはとても大事になると思います。TCPのコネクションなどはデータベースとの接続を見るときとかに役に立ちますよね。exporterを使って、コンテナが乗っているノードのメトリクスを取ることが多いと思います。ある日、ECSでコンテナを動かしていたときに、ネットワーク関係のメトリクスが上手く取れていないことに気がつきました。明らかに0でない値が入るであろうメトリクスなのに0で取得されるというものです。何が原因だったのか私の予想・考察的要素もあります。ECSでポートマッピングを使いたかったため、ネットワークモードをBridgeにして使用していました。デフォルトもBridgeだったはずです。Bridgeモードはdockerデーモンを起動した際に作成される仮想ブリッジdocker0を使用します。docker0に割り当てられるIPアドレス空間は、ホストの既存の空いているIPアドレスから特定の範囲を取ってきたものです。Ethernet)インターフェースが割り当てられ、コンテナは外部と通信するときにに、このvethを経由することになる。画像は以下から引用させていただきました。namespaceが別々になっていることがわかります。namespace内の情報だったと思われます。実際に外部とのコネクションを持ってるのはホストなので、想定するメトリクスが得られないのではと結論づけました。どのように対応したかホストのネットワークスタックを監視するようにコンテナのネットワークモードをhostに変更しました。namespace内で通信を行います。この場合だと期待されたメトリクスを取得することができました。まとめ監視の際のノードのメトリクス取得はdockerのネットワークモードを考慮する必要性がある。exporterのネットワーク関係のメトリクスが取れないTweet広告を非表示にするもっと読むコメントを書く2018-08-01pod(Kubernetes)のlifecycle.prestopの挙動kubernetesコンテナ削除時すぐにコンテナをSTOPされると困ることありません?例えば....?ApacheやNginxなどのWebサーバはSIGTERMが送られても、処理中のコネクションがCloseされないまま終了してしまう。理想としては、残っている接続済みのコネクションを終了してからコンテナがSTOPして欲しいですよね。し終わるまでに数秒かかるかもしれません。その後、親プロセス自身が終了します。これでは、まずいのでPrestopフックを入れます。PrestopフックはPodのシャットダウンプロセスの最初に実行されるものです。Deploymentの.spec.template.spec.containers.lifecycle.preStopに処理を記載していきます。httpGet等でhttpリクエストを引っ掛けることもできますが、基本的にはexecで記述することが多いように思えます。execは記述したコマンドの同一のコンテナで実行します。graceful-stopをDeploymentの.spec.template.spec.containers.lifecycle.preStop.exec.commandに書いていくような形となるかと思います。フックの実行が先に実行されてしまいアクセスがまだ来る状態でコネクション作成をやめてしまうことになります。graceful-stopの前に数秒置く必要があります。指定したコマンドが非同期の場合には、勝手にpreStopフックが完了されたとみなし、SIGTERMがPodのコンテナに送られてしまいます。そのため、非同期のコマンドのあとにはそのコマンドでかかる時間sleepしておくなどするべきです。apacheのリクエストのタイムアウト時間を30秒としているならば、以下のような設定となるかと思います。terminationGracePeriodSecondsはpodのシャットダウンプロセスが始まってからSIGKILLが送られるまでの時間です。preStopの設定を入れている場合は、preStop処理が終了後にSIGTERMが送られ、シャットダウンプロセスが始まってからterminationGracePeriodSecondsで指定した秒数が経過してもプロセスが完了していない場合にはSIGLKILLが送られます。デフォルトは30秒となっているはずです。40kubernetesのソースコード上では以下のように、preStopが実装されてます。preStopの定義がDeploymentにない場合は、すぐにStopContainerが送られてしまうのがわかると思います。}Prestopフックですが、標準出力などに吐き出してもStackdriverなどでは拾えないので注意です。<pod>のイベントのところに表示されます。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。