プラクティス集¶
はじめに¶
本サイトにつきまして、【IT技術の知見】はじめに - 俺の技術ノート をご認識のほど宜しくお願いいたします。
01. セットアップ¶
少ないコントロールプレーン¶
クラウドプロバイダーで Istio を稼働させる場合、各 AZ や各リージョンにコントロールプレーンを 1 個だけセットアップし、できるだけ多くのマイクロサービスのサービスメッシュとなるようにする。
冗長化¶
コントロールプレーンの可用性を高めるために、コントロールプレーンを異なる AZ に冗長化させる。
サービスメッシュに登録しない Pod の選定 (サイドカーモードの場合)¶
▼ 監視系の Pod¶
サイドカーモードに登録する Pod が増えると、その分 istio-proxy が増える。
そのため、Pod 当たりのハードウェアリソースの消費量が増えてしまう。
テレメトリーを収集する必要のない Pod (例:監視を責務に持つ Pod) は、サイドカーモードへ登録しないようにする。
▼ Job 配下の Pod¶
ネイティブサイドカーを使用しない場合、Job 配下の Pod 内でジョブ処理が終了しても istio-proxy が終了せず、Pod のフェーズが Completed にならない問題がある。
Job 配下の Pod は、基本的にはサービスメッシュへ登録しない。
ただし、HTTP リクエストを定期的に送信するジョブは、トラフィック管理のために登録する。
ネイティブサイドカーを使用する場合、ジョブ処理の完了後に istio-proxy が自動で終了する。
ネイティブサイドカーを使用せずにサービスメッシュへ登録する場合は、Pod 内のコンテナで、ジョブ処理の成否にかかわらず istio-proxy の『localhost:15020/quitquitquit』へ POST リクエストを送信する。
apiVersion: batch/v1
kind: Job
metadata:
name: foo-job
spec:
template:
metadata:
name: foo-job
spec:
containers:
- name: foo
command:
- /bin/bash
- -c
args:
- |
trap 'curl -fs -X POST http://localhost:15020/quitquitquit || echo "Failed to shutdown istio-proxy"' EXIT
until curl -fsI http://localhost:15021/healthz/ready; do
echo "Waiting for Sidecar to be healthy";
sleep 3;
done
echo "Sidecar available. Running job command..."
<CronJobのコマンド>
exit $?
GitHub Actions 上で実行する場合¶
▼ .github/workflows/test-istio.yml ファイル¶
GitHub Actions の runner 上で Cluster を作成し、Istio をデプロイする。
リポジトリに setup-asdf.sh、.tool-versions、Helmfile の設定を配置し、Repository variables に AWS_ACCOUNT_ID と AWS_DEFAULT_REGION、Actions secrets に AWS の資格情報を登録する。
name: Test Istio
on:
push:
branches: [master, develop]
pull_request:
workflow_dispatch:
permissions:
contents: read
env:
# ブランチとイベントに応じてHelmfileの実行環境を切り替える
ENV: ${{ (github.head_ref || github.ref_name) == 'master' && 'prd' || (github.head_ref || github.ref_name) == 'develop' && 'stg' || 'tes' }}
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }}
# 検証するKubernetesとIstioのバージョン
K8S_CURRENT_VERSION: "1.24.0"
K8S_NEXT_VERSION: "1.26.0"
ISTIO_CURRENT_VERSION: "1.15.3"
ISTIO_NEXT_VERSION: "1.17.5"
CLUSTER_NAME: ci-${{ github.run_id }}-${{ github.run_attempt }}
jobs:
test-istio:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up tools
run: |
source setup-asdf.sh
# 後続ステップでもasdfで導入したコマンドを使用する
echo "$HOME/.asdf/bin" >> "$GITHUB_PATH"
echo "$HOME/.asdf/shims" >> "$GITHUB_PATH"
curl -fsSL https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d version
curl -fsSLO "https://dl.k8s.io/release/v${K8S_NEXT_VERSION}/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/kubectl
kubectl version --client
- name: Set up k3d registry configuration
env:
AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID }}
run: |
AWS_ECR="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
cat <<EOF > k3d-config.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
registries:
config: |
mirrors:
${AWS_ECR}:
endpoint:
- "https://${AWS_ECR}"
configs:
${AWS_ECR}:
auth:
username: AWS
password: $(aws ecr get-login-password --region "$AWS_DEFAULT_REGION")
EOF
- name: Create cluster
run: |
# GitHub-hosted runnerのDockerを使用し、同じジョブ内でClusterを検証する
k3d cluster create --config k3d-config.yaml "$CLUSTER_NAME" \
--image "rancher/k3s:v${K8S_NEXT_VERSION}-k3s1" --agents 2 || \
k3d cluster create --config k3d-config.yaml "$CLUSTER_NAME" \
--image "rancher/k3s:v${K8S_NEXT_VERSION}-rc1-k3s1" --agents 2
kubectl label node "k3d-${CLUSTER_NAME}-agent-0" node.kubernetes.io/nodetype=ingress --overwrite
kubectl label node "k3d-${CLUSTER_NAME}-agent-1" node.kubernetes.io/nodetype=system --overwrite
k3d cluster list
kubectl get node --show-labels
- name: Deploy and verify Istio
run: |
kubectl apply -f "https://raw.githubusercontent.com/istio/istio/${ISTIO_NEXT_VERSION}/manifests/charts/base/crds/crd-all.gen.yaml"
kubectl create ns istio-ingress
kubectl label ns istio-ingress istio.io/rev=default
kubectl create ns istio-system
kubectl get ns -L istio.io/rev
helmfile -e "$ENV" -f "helmfile_istiod_${ISTIO_NEXT_VERSION//./-}.yaml" apply --skip-crds --skip-diff-on-install
helmfile -e "$ENV" -f helmfile_istio-base.yaml apply --skip-diff-on-install
helmfile -e "$ENV" -f helmfile_istio-ingressgateway.yaml apply --skip-diff-on-install
istioctl version
istioctl proxy-status
kubectl get all -n istio-ingress
kubectl get all -n istio-system
- name: Delete cluster
run: k3d cluster delete "$CLUSTER_NAME"
▼ setup-asdf.sh ファイル¶
#!/bin/bash
# asdfをインストールする
git clone --depth 1 --branch v0.14.1 https://github.com/asdf-vm/asdf.git ~/.asdf
export ASDF_DIR=~/.asdf
source "$ASDF_DIR/asdf.sh"
# コマンドをインストールする
asdf plugin add helm https://github.com/Antiarchitect/asdf-helm.git
asdf plugin add helmfile https://github.com/feniix/asdf-helmfile.git
asdf plugin add istioctl https://github.com/virtualstaticvoid/asdf-istioctl.git
asdf install
asdf list
# 正しいバージョンをインストールできていることを確認する
helm version
helmfile version
istioctl version
02. トラフィック管理¶
Istio Ingress Gateway について¶
▼ Istiod コントロールプレーンとは異なる Namespace におく¶
セキュリティ上の理由から、Istio Ingress Gateway と Istiod コントロールプレーンは異なる Namespace におくほうがよい。
▼ NodePort Service を選ぶ¶
Istio Ingress Gateway では、内部的に作成される Service のタイプ (NodePort Service、ClusterIP Service、LoadBalancer Service) を選べる。
NodePort Service を選ぶ場合、Node の送信元に開発者がロードバランサーを作成し、NodePort Service にインバウンド通信をルーティングできるようにする。
パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L7 ロードバランサー (単一の L7 ロードバランサーを作成し、異なるポートを開放する複数の L4 ロードバランサーの振り分ける)
AWS ALB
⬇⬆️︎
# L4 ロードバランサー
NodePort Service (Istio Ingress Gateway)
⬇⬆️︎
Istio Ingress Gateway (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
一方で、LoadBalancer Service を選ぶ場合、クラウドプロバイダーのロードバランサーが自動的に作成される。
そのため、このロードバランサーから LoadBalancer Service へルーティングできるようにする。
LoadBalancer Service では、クラウドプロバイダーのリソースと Kubernetes リソースの責務の境界が曖昧になってしまうため、NodePort Service を選ぶようにする。
補足として、デフォルトでは Istio Ingress Gateway の内部では LoadBalancer Service を作成されてしまう。
NodePort Service を選ぶためには、Istio Ingress Gateway ではなく、IstioOperator や istio チャート上で Service のタイプを設定し、Istio Ingress Gateway を作成する必要がある。
▼ ClusterIP Service を選ぶ (AWS Load Balancer Controller を使用する場合のみ)¶
AWS Load Balancer Controller を使用する場合、Istio Ingress Gateway で ClusterIP Service を使用できる。
Ingress にて、alb.ingress.kubernetes.io/target-type キー値を ip とすると、AWS Load Balancer Controller は Pod にリクエストを直接的に L7 ロードバランシングする。
パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L7 ロードバランサー (単一の L7 ロードバランサーを作成し、異なるポートを開放する複数の L4 ロードバランサーの振り分ける)
AWS Load Balancer ControllerによるAWS ALB
⬇⬆️︎
# ClusterIP Service を経由せず、ALB から Pod へ直接ルーティング
Istio Ingress Gateway の Pod (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
▼ 共有入口と占有入口を選ぶ¶
Istio Ingress Gateway を複数の Namespace で共有する共有入口パターンと、特定の Namespace が占有する占有入口パターンがある。
入口の責任範囲を分割する必要がない場合は共有し、流入経路や管理の責任範囲を分けたい場合は Namespace ごとに配置する。
占有入口パターンでは、問題の原因を切り分けやすくなる一方で、Istio Ingress Gateway の管理が煩雑になる。
デフォルトルートにサブセットを指定する¶
複数のサブセットを定義する場合でも、デフォルトルートでは特定のサブセットを指定する。
これにより、個別のルーティング条件に一致しない通信も、特定のバージョンの Pod にルーティングできる。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: foo-virtual-service
spec:
hosts:
# 特定のマイクロサービスへのリクエストのみを扱うため、ホスト名もそれのみを許可する
# ただし、gateways オプションがある VirtualService ではワイルドカードする
- foo
http:
- route:
- destination:
host: foo
subset: v1
Istio リソースのリクエスト可能な範囲を限定する¶
Istio リソースを同じ Namespace 内だけに公開する場合、.spec.exportTo キーでは『. (ドット) 』を設定する。
これにより、Istiod が設定を同じ Namespace のプロキシだけに配布する。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: foo-virtual-service
spec:
exportTo:
- "."
# 特定のマイクロサービスへのリクエストのみを扱うため、ホスト名もそれのみを許可する
# ただし、gateways オプションがある VirtualService ではワイルドカードする
hosts:
- foo
http:
- route:
- destination:
host: foo
DestinationRule を最初に更新する¶
新しいサブセットを追加する場合、DestinationRule を最初に更新する。
これにより、ダウンタイムなしでサブセットを追加できる。
DestinationRule を更新する前に新しいサブセットを持つ VirtualService を更新してしまうと、VirtualService は新しいサブセットを持つ DestinationRule を見つけられず、503 レスポンスを返信してしまう。
DestinationRule を最初に更新し、正常に完了することを待機した後に、VirtualService を更新する。
02-02. 通信ルーティングのパターン¶
LoadBalancer Service の場合¶
LoadBalancer Service を使用する場合、以下のようなネットワーク経路がある。
パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L4 ロードバランサー
LoadBalancer Service (Istio Ingress Gateway) によるAWS NLB
⬇⬆️︎
Istio Ingress Gateway (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
NodePort Service の場合¶
▼ L7 ロードバランサーがない場合¶
Node の NIC の宛先情報は、Node 外から宛先 IP アドレスとして指定できるため、インバウンド通信に Ingress を必要としない。
パブリックネットワーク
⬇⬆️︎
# L4 ロードバランサー
NodePort Service (Istio Ingress Gateway)
⬇⬆️︎
Istio Ingress Gateway (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
▼ L7 ロードバランサーがある場合¶
パブリックプロバイダーのロードバランサー (例:AWS ALB) を別に配置する。
単一の L7 ロードバランサーを作成し、複数の L4 ロードバランサーに振り分けるとよい。
パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L7 ロードバランサー (単一の L7 ロードバランサーを作成し、異なるポートを開放する複数の L4 ロードバランサーの振り分ける)
AWS ALB
⬇⬆️︎
# L4 ロードバランサー
NodePort Service (Istio Ingress Gateway)
⬇⬆️︎
Istio Ingress Gateway (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
ClusterIP Service の場合¶
AWS Load Balancer Controller を使用する場合、Istio Ingress Gateway で ClusterIP Service を使用できる。
Ingress にて、alb.ingress.kubernetes.io/target-type キー値を ip とすると、AWS Load Balancer Controller は Pod にリクエストを直接的に L7 ロードバランシングする。
パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L7 ロードバランサー (単一の L7 ロードバランサーを作成し、異なるポートを開放する複数の L4 ロードバランサーの振り分ける)
AWS Load Balancer ControllerによるAWS ALB
⬇⬆️︎
# ClusterIP Service を経由せず、ALB から Pod へ直接ルーティング
Istio Ingress Gateway の Pod (Gateway と VirtualService の設定を使用)
⬇⬆️︎
# Service を経由せず、Istio Ingress Gateway から宛先 Pod へ直接ルーティング
Pod (istio-proxy)
03. アップグレード¶
設計規約¶
▼ サポート期間¶
Istio では、マイナーバージョンごとのアップグレードを推奨しており、またマイナーバージョンのサポートが半年ごとに終了する。
実質的に半年ごとにアップグレード工数が発生する。
▼ マイナーバージョン単位でアップグレード¶
Istio の開発プロジェクトでは、マイナーバージョンを 1 個ずつ新しくするアップグレードしか検証していない。
そのため、マイナーバージョンを 2 個以上跨いだアップグレードを推奨していない。
▼ Istiod コントロールプレーンでダウンタイムを発生させない¶
Istiod コントロールプレーンでダウンタイムが発生すると、istio-proxy 内の pilot-agent が最新の宛先情報を取得できなくなる。
そのため、古いバージョンのマイクロサービスの宛先情報を使用してしまう。
Istiod コントロールプレーンをカナリアアップグレードを採用する。
▼ Istio Ingress Gateway でダウンタイムを発生させない¶
Istio Ingress Gateway でダウンタイムが発生すると、アプリへのインバウンド通信が遮断されてしまう。
インプレース方式¶
▼ インプレース方式とは¶
既存の Istiod コントロールプレーンと Istio Ingress Gateway の両方をインプレース方式でアップグレードする。
▼ 手順¶
-
CRD を更新する。
必要なCRDのマニフェストは、リポジトリで確認する必要がある。
$ git clone https://github.com/istio/istio.git $ kubectl apply -f manifests/charts/base/crds -
Istiod コントロールプレーンと Istio Ingress Gateway の両方をインプレース方式でアップグレードする。
$ istioctl install -
データプレーンの istio-proxy を再インジェクションする。
$ kubectl rollout restart deployment app-deployment -n app
カナリア方式¶
▼ カナリア方式とは¶
▼ istioctl コマンドの場合¶
▼ helm コマンドの場合¶
-
Helm では CRD を管理しないようにし、
kubectlコマンドでこれを作成する。$ kubectl diff -f https://raw.githubusercontent.com/istio/istio/1.15.3/manifests/charts/base/crds/crd-all.gen.yaml -
istiod チャートを使用して、古いバージョンの MutatingWebhookConfiguration のみを削除する。
この時、既存のリリースは古いリリースとして扱う。
$ helm upgrade <古いバージョンのリリース名> <チャートリポジトリ名>/istiod -n istio-system --version <古いバージョン> --set revisionTags=null -
istiod チャートを使用して、新しいバージョンの MutatingWebhookConfiguration を作成しつつ、Istiod コントロールプレーンに関する Kubernetes リソースを変更する。
この時、リリースを新しく命名する。
$ helm upgrade <新しいバージョンのリリース名> <チャートリポジトリ名>/istiod -n istio-system --version <新しいバージョン> -
特定の Namespace をアップグレードする。
-
動作確認し、問題なければ、残りの Namespace もアップグレードする。
-
istiod チャートを使用して、古いリリースで作成した Istiod コントロールプレーンに関する Kubernetes リソースを削除する。
$ helm upgrade <古いバージョンのリリース名> <チャートリポジトリ名>/istiod -n istio-system --version <古いバージョン> --set revisionTags=null -
istio-base を使用して、Istio に関する CRD を変更する。
この時、リリースを新しく命名する。
$ helm upgrade <新しいバージョンのリリース名> <チャートリポジトリ名>/base -n istio-system --version <新しいバージョン> -
gateway チャートを使用して、Istio Ingress Gateway に関する Kubernetes リソースを変更する。
この時、リリースを新しく命名する。
$ helm upgrade <新しいバージョンのリリース名> <チャートリポジトリ名>/gateway -n istio-ingress --version <新しいバージョン>
04. アーキテクチャ特性¶
性能¶
リクエストが istio-proxy を経由する分だけレイテンシーが増加し、また istio-proxy ごとに CPU やメモリを消費するため、性能が劣化する。