データプレーン@Istio¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. データプレーンとは¶
サイドカー型の場合¶
サイドカー型のデータプレーンは、istio-iptables、 istio-init
コンテナ、istio-proxy
コンテナ、といったコンポーネントから構成される。
アンビエンドメッシュの場合¶
記入中...
02. データプレーンの要素¶
istio-init
コンテナ¶
▼ istio-init
コンテナとは¶
コンテナの起動時に、istio-iptables
コマンドを実行することにより、istio-iptablesをPodに適用する。
istio-iptables¶
▼ istio-iptablesとは¶
istio-iptablesは、istio-proxy
コンテナを持つPod内のネットワークの経路を制御する。
サービスディスカバリーとしてPodのIPアドレスを持つのはistio-proxy
コンテナであり、istio-iptablesではないことに注意する。
# istio-initコンテナの起動時に実行する。
$ istio-iptables \
-p 15001 \
-z 15006 \
-u 1337 \
-m REDIRECT \
-i * \
-x \
-b * \
-d 15090,15020
▼ ルール¶
(1)
-
ps
コマンドを使用して、istio-proxy
コンテナのenvoy
プロセスのID (PID) を取得する。
# PIDが出力結果の2行目である。そのため、awkコマンドを使用して、2行目のみを取得している。
$ ps aux | grep envoy | awk '{print $2}'
1234567
2345678
3456789
(2)
-
nsenter
コマンドを使用して、コンテナの稼働するユーザー空間を介し、コンテナにiptables
コマンドを送信する。Istioによって管理されているChainのルールを取得できる。
$ nsenter -t <istio-proxyコンテナのPID> -n iptables -L -n -t nat --line-number
Chain PREROUTING (policy ACCEPT)
...
Chain INPUT (policy ACCEPT)
...
Chain OUTPUT (policy ACCEPT)
...
Chain POSTROUTING (policy ACCEPT)
...
# istio-proxyコンテナへのインバウンド通信時に、NAPT処理を実行する。
Chain ISTIO_INBOUND (1 references)
num target prot opt source destination
1 RETURN tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:15008
2 RETURN tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:15090 # メトリクス収集ツールからのリクエストを待ち受ける。
3 RETURN tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:15021 # kubeletからのReadinessProbeチェックを待ち受ける。
4 RETURN tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:15020 # データプレーンのデバッグエンドポイントに対するリクエストを待ち受ける。
5 ISTIO_IN_REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_IN_REDIRECT (3 references)
num target prot opt source destination
1 REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 redir ports 15006 #
# istio-proxyコンテナからのアウトバウンド通信時に、NAPT処理を実行する。
Chain ISTIO_OUTPUT (1 references)
num target prot opt source destination
1 RETURN all -- 127.0.0.6 0.0.0.0/0
2 ISTIO_IN_REDIRECT all -- 0.0.0.0/0 !127.0.0.1 owner UID match 1337
3 RETURN all -- 0.0.0.0/0 0.0.0.0/0 ! owner UID match 1337
4 RETURN all -- 0.0.0.0/0 0.0.0.0/0 owner UID match 1337
5 ISTIO_IN_REDIRECT all -- 0.0.0.0/0 !127.0.0.1 owner GID match 1337
6 RETURN all -- 0.0.0.0/0 0.0.0.0/0 ! owner GID match 1337
7 RETURN all -- 0.0.0.0/0 0.0.0.0/0 owner GID match 1337
8 RETURN all -- 0.0.0.0/0 127.0.0.1
9 ISTIO_REDIRECT all -- 0.0.0.0/0 0.0.0.0/0
Chain ISTIO_REDIRECT (1 references)
num target prot opt source destination
1 REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 redir ports 15001
▼ ローカルホストは127.0.0.1
ではない¶
istio-proxy
コンテナがインバウンドをマイクロサービスにプロキシする時、127.0.0.6
にリクエストを送信する。
127.0.0.1
にするとiptables上で処理がループしてしまう。
Istiov1.9
までは127.0.0.1
で、v1.10
から127.0.0.6
になった。
▼ Pod外からのインバウンド通信の場合¶
Pod外からアプリコンテナへのインバウンド通信は、istio-iptablesにより、istio-proxy
コンテナの15006
番ポートにリダイレクトされる。
istio-proxy
コンテナはこれを受信し、ローカルホスト (http://127.0.0.6:<アプリコンテナのポート番号>
) のアプリコンテナにルーティングする。
▼ Pod外へのアウトバウンド通信の場合¶
アプリコンテナからPod外へのアウトバウンド通信は、istio-iptablesにより、istio-proxy
コンテナの15001
番ポートにリダイレクトされる。
サービスディスカバリーによってPod等の宛先情報が、istio-proxy
コンテナ内のEnvoyに登録されており、istio-proxy
コンテナはアウトバウンド通信をPodに向けてルーティングする。
▼ ローカスホスト通信の場合¶
アプリコンテナからローカルホスト (http://127.0.0.6:<ポート番号>
) へのアウトバウンド通信は、istio-iptablesにより、istio-proxy
コンテナの15001
番ポートにリダイレクトされる。
istio-proxy
コンテナ¶
▼ istio-proxy
コンテナとは¶
リバースプロキシの能力を持つサイドカーコンテナである。
Dockerfileとしては、Envoyのバイナリファイルをインストールした後にpilot-agentを実行している。
そのため、pilot-agent、Envoy、が稼働している。
...
# Install Envoy.
ARG TARGETARCH
COPY ${TARGETARCH:-amd64}/${SIDECAR} /usr/local/bin/${SIDECAR}
...
# The pilot-agent will bootstrap Envoy.
ENTRYPOINT ["/usr/local/bin/pilot-agent"]
istio-proxy
コンテナは、アプリコンテナのあるPodのみでなく、Istio IngressGatewayのPod内にも存在している。
Istioのサービスメッシュ外のネットワークからのインバウンド通信では、Istio IngressGateway内のistio-proxy
コンテナにて、Pod等の宛先情報に基づいて、ルーティングを実行している。
一方で、アプリコンテナを持つPod間通信では、Pod内のistio-proxy
コンテナに登録されたものに基づいて、Pod間で直接的に通信している。
仕様上、NginxやApacheを必須とする言語 (例:PHP) では、Pod内にリバースプロキシが2
個ある構成になってしまうことに注意する。
▼ InitContainerとして¶
Kubernetesのv1.28
では、InitContainerでサイドカーを作成できるようになった。
Istioでもこれをサポートしている。
istio-proxy
コンテナのインジェクションの仕組みはそのままで、PodのマニフェストのPatch処理の内容をInitContainerのインジェクションに変更している。
これにより、Podの作成時にInitContainerをインジェクションできるようになる。
今まで、.spec.containers[*].lifecycle.preStop
キーや.spec.containers[*].lifecycle.postStart
キーに自前のコマンドを定義してistio-proxy
コンテナの起動/終了タイミングを制御する必要があったが、InitContainerではそれが不要になる。
apiVersion: v1
kind: Pod
metadata:
name: foo-pod
spec:
containers:
- name: app
image: app:1.0.0
ports:
- containerPort: 8080
volumeMounts:
- name: app-volume
mountPath: /go/src
initContainers:
- name: istio-proxy
image: istio/proxyv2:latest
restartPolicy: Always
▼ startProbe¶
サイドカーは、10
分以上起動が完了しないと、Podが終了する。
istio-cniによるistio-validation
コンテナ¶
▼ istio-cniとは¶
各Node上で、istio-cni-node
という名前のDaemonSetとして稼働する。
istio-init
コンテナはistio-iptablesをPodに適用する権限を持っている。
しかし、Linuxのiptablesを操作するためにはroot権限が必要になるため、脆弱性が指摘されている (同様にして、ユーザーがiptables
コマンドを実行する時もsudo
権限が必要である) 。
istio-init
コンテナの代替案として、istio-cniが提供されている。
もしistio-cniを使用する場合は、istio-init
コンテナが不要になる代わりに、istio-validation
コンテナが必要になる。
▼ istio-validation
コンテナ¶
istio-cniを採用している場合にのみそう挿入されるコンテナ。
istio-cniのDaemonSetがistio-iptablesを適用し終了することを待機するために、これが完了したかどうかを検証する。
02-02. istio-proxy
コンテナ¶
pilot-agent (新istio-agent)¶
▼ pilot-agentとは¶
元々は、istio-agentといわれていた。
実体は、GitHubのpilot-agent
ディレクトリ配下のmain.go
ファイルで実行されるGoのバイナリファイルである。
ADS-APIとの間で双方向ストリーミングRPCを確立し、EnvoyからのADS-APIへのリクエストと反対にADS-APIからのリクエストを仲介する。
▼ ADSクライアントの実装¶
package adsc
import (
...
discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
...
)
...
func (a *ADSC) Run() error {
var err error
// 双方向ストリーミングRPCの接続を確立する。
a.client = discovery.NewAggregatedDiscoveryServiceClient(a.conn)
// Envoyのgo-control-planeパッケージから提供されている。
// https://github.com/envoyproxy/go-control-plane/blob/v0.11.0/envoy/service/discovery/v3/ads.pb.go#L213-L220
// また。.protoファイルで双方向ストリーミングRPCとして定義されている。
// https://github.com/envoyproxy/envoy/blob/v1.25.0/api/envoy/service/discovery/v3/ads.proto#L32-L33
a.stream, err = a.client.StreamAggregatedResources(context.Background())
if err != nil {
return err
}
a.sendNodeMeta = true
a.InitialLoad = 0
for _, r := range a.cfg.InitialDiscoveryRequests {
if r.TypeUrl == v3.ClusterType {
a.watchTime = time.Now()
}
// istio-proxyコンテナの起動時に、Istiodコントロールプレーンにリクエストを送信する。
_ = a.Send(r)
}
a.RecvWg.Add(1)
// ADS-APIからリクエストを受信し、Envoyの各処理コンポーネント別に整理する。
go a.handleRecv()
return nil
}
handleRecv
メソッド内で、Envoyの各処理コンポーネントを整理し、最後にXDSUpdates
チャネルに値を送信している。
func (a *ADSC) handleRecv() {
for{
...
a.VersionInfo[msg.TypeUrl] = msg.VersionInfo
switch msg.TypeUrl {
// 受信した宛先Podのリスナーを処理する。
case v3.ListenerType:
listeners := make([]*listener.Listener, 0, len(msg.Resources))
for _, rsc := range msg.Resources {
...
}
a.handleLDS(listeners)
// 受信した宛先Podのクラスターを処理する。
case v3.ClusterType:
clusters := make([]*cluster.Cluster, 0, len(msg.Resources))
for _, rsc := range msg.Resources {
...
}
a.handleCDS(clusters)
// 受信した宛先Podのエンドポイントを処理する。
case v3.EndpointType:
eds := make([]*endpoint.ClusterLoadAssignment, 0, len(msg.Resources))
for _, rsc := range msg.Resources {
...
}
a.handleEDS(eds)
// 受信した宛先Podのルートを処理する。
case v3.RouteType:
routes := make([]*route.RouteConfiguration, 0, len(msg.Resources))
for _, rsc := range msg.Resources {
...
}
a.handleRDS(routes)
default:
if isMCP {
a.handleMCP(gvk, msg.Resources)
}
}
...
select {
// XDSUpdatesチャネルに値を送信する。
// 最終的に、Envoyに設定する。
case a.XDSUpdates <- msg:
default:
}
}
}
▼ ADSクライアントとしてのistioctl
コマンドの実装¶
Run
メソッドによるXDS-APIとの通信は、istioctl
コマンドでも使用されている。
func GetXdsResponse(dr *discovery.DiscoveryRequest, ns string, serviceAccount string, opts clioptions.CentralControlPlaneOptions, grpcOpts []grpc.DialOption,) (*discovery.DiscoveryResponse, error) {
...
err = adscConn.Run()
if err != nil {
return nil, fmt.Errorf("ADSC: failed running %v", err)
}
err = adscConn.Send(dr)
if err != nil {
return nil, err
}
response, err := adscConn.WaitVersion(opts.Timeout, dr.TypeUrl, "")
return response, err
}
Envoy¶
▼ Envoyとは¶
istio-proxy
コンテナにて、リバースプロキシとして動作する。Envoyは、pilot-agentを介して、ADS-APIにリモートプロシージャーコールを実行する。また反対に、XDS-APIからのリモートプロシージャーコールをpilot-agentを介して受信する。
Graceful Drainモード¶
istio-proxyは、自分自身を安全に停止する。
(1)
-
現在のEnvoyプロセスがホットリロードを実行し、新しいEnvoyプロセスが起動する。
(2)
-
terminationDrainDuration
値 (デフォルト5
秒) によるGraceful Drainモード待機時間が開始する。 (3)
-
現在のEnvoyプロセスは、Graceful Drainモードを開始する。
(4)
-
現在のEnvoyプロセスへの接続を新しいEnvoyに段階的に移行する。
この時、現在のEnvoyプロセスはすぐに通信を閉じず、
drainDuration
値 (デフォルト5
秒) による待機時間だけ、リクエストを受信しながら移行していく。 (5)
-
現在のEnvoyは、プロセスのGraceful Drainモードを終了する。
(6)
-
Graceful Drainモードの終了後、
terminationDrainDuration
によるGraceful Drainモード待機時間が完了する。 (7)
-
現行EnvoyプロセスにSIGKILLを送信する。
02-03. 待ち受けるポート番号¶
15000
番¶
istio-proxy
コンテナの15000
番ポートでは、Envoyのダッシュボードに対するリクエストを待ち受ける。
# istio-proxyコンテナ内でローカルホストにリクエストを送信する。
istio-proxy@<Pod名>: $ curl http://127.0.0.1:15000/config_dump
15001
番¶
istio-proxy
コンテナの15001
番ポートでは、アプリコンテナからのアウトバウンド通信を待ち受ける。
アプリコンテナからのアウトバウンド通信は、一度、istio-proxy
コンテナの15001
番ポートにリダイレクトされる。
15004
番¶
istio-proxy
コンテナの15004
番ポートでは、コントロールプレーンのコンテナの8080
番ポートと一緒に使用される。
用途がわからず記入中...
15006
番¶
istio-proxy
コンテナの15006
番ポートでは、アプリコンテナへのインバウンド通信を待ち受ける。
アプリコンテナへのインバウンド通信は、一度、istio-proxy
コンテナの15006
番ポートにリダイレクトされる。
15020
番¶
istio-proxy
コンテナの15020
番ポートでは、データプレーンのデバッグエンドポイントに対するリクエストを待ち受ける。
15021
番¶
istio-proxy
コンテナの15021
番ポートでは、kubeletからのReadinessProbeチェックを待ち受ける。
istio-proxy
コンテナ内のEnvoyが、/healthz/ready
エンドポイントでReadinessProbeチェックを待ち受けており、もしEnvoyが停止してれば503
ステータスのレスポンスを返却する。
15053
番¶
記入中...
15090
番¶
istio-proxy
コンテナの15090
番ポートでは、istio-proxy
コンテナのメトリクス収集ツール (例:Prometheus) からのリクエストを待ち受ける。
istio-proxy
コンテナ内のEnvoyが、/stats/prometheus
エンドポイントでリクエストを待ち受けており、データポイントを含むレスポンスを返信する。
ただ、discovery
コンテナにも/stats/prometheus
エンドポイントがあり、メトリクス収集ツールはこれを指定することが多い。
$ kubectl exec \
-it foo-pod \
-n foo-namespace \
-c istio-proxy \
-- bash -c "curl http://127.0.0.1:15090/stats/prometheus"
istio_build{component="proxy",tag="<リビジョン番号>"} 1
...
istio_request_bytes_count{...}
istio_request_messages_total{...}
...