【実践Kubernetes】簡単なアプリケーションのデプロイからスケーリングまで

に公開

はじめに

Kubernetesの概念を理解したら、次は実際にアプリケーションをデプロイして動作を確認してみましょう。本記事では、Kubernetes公式のゲストブックチュートリアルを参考にしつつ、最小限の機能で構成されたシンプルなアプリケーションをデプロイし、スケーリングを確認する実践的な手順を紹介します。

本記事で学べること:

  • DeploymentとServiceの基本的な使い方
  • 外部IPアドレスでのアクセス方法
  • 負荷テストによるスケーリングの確認
  • 実践的なトラブルシューティング

前提条件

以下の環境が準備されていることを前提とします:

  • Kubernetesクラスター(Rancher Desktop、minikube、kindなど)
  • kubectlコマンドがインストールされ、クラスターに接続可能
  • 基本的なKubernetesの概念(Pod、Deployment、Service)の理解
  • ローカルマシンにk6がインストールされていること(負荷テスト用)

k6のインストール

Macの場合、Homebrewでインストールできます:

# Homebrewでk6をインストール
brew install k6

# バージョン確認
k6 version

環境確認

まず、クラスターが正常に動作しているか確認しましょう:

# クラスターの状態確認
kubectl cluster-info

# ノードの確認
kubectl get nodes

# 現在のリソース確認
kubectl get all

Rancher Desktop特有の注意点

Rancher Desktopを使用する場合、以下の点に注意してください:

  • NodePort範囲: デフォルトで30000-32767のポート範囲が使用可能
  • ノードIPアドレス: Rancher Desktopでは通常127.0.0.1(localhost)でアクセス可能
  • ポートフォワーディング: Rancher Desktopは自動的にNodePortをローカルホストにマッピング

1. シンプルなWebアプリケーションのデプロイ

1.1 アプリケーションの構成

本記事では、以下のシンプルな構成でアプリケーションをデプロイします:

  1. フロントエンド: Nginxを使用したシンプルなWebサーバー
  2. 負荷テスト: k6を使用したHTTP負荷テスト

1.2 フロントエンドDeploymentの作成

まず、Nginxを使用したフロントエンドのDeploymentを作成します:

# Deploymentの作成
kubectl create deployment frontend --image=nginx:latest --replicas=2

# Deploymentの確認
kubectl get deployment frontend

# Podの確認
kubectl get pods -l app=frontend

作成されたDeploymentの詳細を確認:

kubectl describe deployment frontend

Deploymentの特徴:

  • replicas: 2により、2つのPodが作成されます
  • Podが障害で停止した場合、自動的に新しいPodが作成されます
  • ローリングアップデートが可能です

この図は、DeploymentがReplicaSetを管理し、ReplicaSetがPodを管理する階層構造を示しています。

1.3 外部アクセス用Serviceの作成(NodePort)

次に、フロントエンドに外部(ローカルのMac)からアクセスするためのServiceを作成します。Rancher Desktopで外部からアクセスするには、NodePortタイプのServiceを使用します:

# NodePortタイプのServiceを作成
kubectl expose deployment frontend --port=80 --target-port=80 --type=NodePort --name=frontend-service

# Serviceの確認
kubectl get service frontend-service

# Serviceの詳細確認
kubectl describe service frontend-service

出力例:

NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
frontend-service   NodePort   10.96.123.45    <none>        80:31234/TCP   10s

31234がNodePortとして割り当てられたポート番号です。Rancher Desktopでは、localhost:31234でアクセス可能になります。

Serviceの役割:

  • Podの集合に安定したネットワークエンドポイントを提供
  • ロードバランシング機能
  • サービスディスカバリー(DNS名でのアクセス)
  • NodePortタイプにより外部からのアクセスが可能

Serviceは、ラベルセレクターに一致するPodを自動的に検出し、Endpointsとして登録します。NodePortによりクラスター外部からアクセスでき、Serviceがロードバランシングを行います。

2. 外部アクセスの確認とトラブルシューティング

2.1 NodePortの確認

作成したServiceのNodePortを確認します:

# Serviceの詳細確認
kubectl get service frontend-service

# NodePortを取得(後で使用)
export NODE_PORT=$(kubectl get service frontend-service -o jsonpath='{.spec.ports[0].nodePort}')
echo "NodePort: $NODE_PORT"

出力例:

NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
frontend-service   NodePort   10.96.123.45    <none>        80:31234/TCP   10s

NodePort: 31234

2.2 Rancher Desktopでの外部アクセス確認

Rancher Desktopでは、localhost経由でNodePortにアクセスできます:

# ブラウザでアクセス
open http://localhost:${NODE_PORT}

# または、curlでアクセス確認
curl http://localhost:${NODE_PORT}

正常に動作している場合、Nginxのデフォルトページが表示されます:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

2.3 疎通確認のトラブルシューティング

外部からアクセスできない場合、以下の手順で確認します:

ステップ1: Podの状態確認

# Podが正常に起動しているか確認
kubectl get pods -l app=frontend

# すべてのPodがRunning状態であることを確認
# 期待される出力:
# NAME                        READY   STATUS    RESTARTS   AGE
# frontend-xxxxxxxxxx-xxxxx   1/1     Running   0          2m
# frontend-xxxxxxxxxx-xxxxx   1/1     Running   0          2m

Podが起動していない場合:

# Podの詳細確認
kubectl describe pod <pod-name>

# Podのログ確認
kubectl logs <pod-name>

ステップ2: Serviceの確認

# Serviceが正しく作成されているか確認
kubectl get service frontend-service

# Serviceの詳細確認
kubectl describe service frontend-service

# Endpointsの確認(重要!)
kubectl get endpoints frontend-service

正常な場合のEndpoints:

NAME               ENDPOINTS                           AGE
frontend-service   10.244.1.10:80,10.244.1.11:80      5m

Endpointsが空の場合、PodとServiceのラベルセレクターが一致していません:

# Podのラベル確認
kubectl get pods -l app=frontend --show-labels

# Serviceのラベルセレクター確認
kubectl get service frontend-service -o jsonpath='{.spec.selector}'

ステップ3: クラスター内からのアクセステスト

Podレベルでアクセスできるか確認:

# テスト用のPodを作成してクラスター内からアクセス
kubectl run test-pod --image=curlimages/curl:latest --rm -it --restart=Never -- sh

# Pod内で実行(Service名でアクセス)
curl http://frontend-service:80

# 正常な場合、Nginxのデフォルトページが表示される

ステップ4: NodePortへの直接アクセステスト

# localhostからNodePortへアクセス
curl -v http://localhost:${NODE_PORT}

# 詳細な接続情報を確認
curl -v http://localhost:${NODE_PORT} 2>&1 | grep -E "Connected|HTTP"

ステップ5: Rancher Desktopの設定確認

Rancher Desktopの設定で、Kubernetesが有効になっているか確認:

  1. Rancher Desktopを開く
  2. Preferences → Kubernetes
  3. "Enable Kubernetes"が有効になっているか確認
  4. 必要に応じてRancher Desktopを再起動

ステップ6: ポートの競合確認

NodePortが他のプロセスで使用されていないか確認:

# Macでポートの使用状況を確認
lsof -i :${NODE_PORT}

# 何も表示されない場合、ポートは使用可能

2.4 よくあるトラブルと解決方法

問題 原因 解決方法
curl: (7) Failed to connect NodePortが開いていない Serviceが正しく作成されているか確認
curl: (52) Empty reply from server Podが起動していない kubectl get podsでPodの状態確認
Endpointsが空 ラベルセレクターの不一致 PodとServiceのラベル確認
Connection refused ポートが間違っている NodePort番号を再確認
ブラウザでアクセスできない Rancher Desktopの設定問題 Rancher Desktopを再起動

3. スケーリングと負荷テスト

3.1 手動スケーリング

Deploymentのレプリカ数を変更してスケーリングを確認します:

スケーリングにより、新しいPodが作成され、トラフィックが分散されます。

# レプリカ数を3に増やす
kubectl scale deployment frontend --replicas=3

# Podの確認
kubectl get pods -l app=frontend

# レプリカ数を1に減らす
kubectl scale deployment frontend --replicas=1

# Podの確認
kubectl get pods -l app=frontend

3.2 k6を使用した負荷テスト

k6は、高性能な負荷テストツールです。ローカルのMacからk6を実行してHTTPリクエストを送信し、スケーリングの動作を確認します。

k6スクリプトの作成

プロジェクトディレクトリにloadtest.jsを作成します:

// loadtest.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '10s', target: 100 },  // 10秒で100 VUsまで一気に増加
    { duration: '30s', target: 100 },  // 30秒間100 VUsを維持(高負荷)
    { duration: '10s', target: 0 },    // 10秒で0 VUsまで減少
  ],
  thresholds: {
    http_req_duration: ['p(95)<1000'], // 95%のリクエストが1秒以内
    http_req_failed: ['rate<0.1'],     // エラー率が10%未満
  },
};

export default function () {
  const SERVICE_URL = __ENV.SERVICE_URL || 'http://localhost:30000';
  const res = http.get(SERVICE_URL);
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response has content': (r) => r.body.length > 0,
  });
  // sleep時間を短くして、より多くのリクエストを送信
  sleep(0.01);
}

ローカルのMacからk6を実行

Rancher Desktopの場合、localhost経由でNodePortにアクセスします:

# NodePort番号を取得
export NODE_PORT=$(kubectl get service frontend-service -o jsonpath='{.spec.ports[0].nodePort}')
echo "NodePort: $NODE_PORT"

# まず、アクセス可能か確認
curl http://localhost:${NODE_PORT}

# k6を実行(ローカルのMacから)
k6 run --env SERVICE_URL=http://localhost:${NODE_PORT} loadtest.js

k6実行中の出力例

          /\      |‾‾| /‾‾/   /‾‾/   
     /\  /  \     |  |/  /   /  /    
    /  \/    \    |     (   /   ‾‾\  
   /          \   |  |\  \ |  (‾)  | 
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: loadtest.js
     output: -

  scenarios: (100.00%) 1 scenario, 100 max VUs, 4m30s max duration (incl. graceful stop):
           * default: Up to 100 looping VUs for 4m0s over 4 stages (gracefulRampDown: 30s, gracefulStop: 30s)

running (4m00.5s), 000/100 VUs, 24000 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs  4m0s

     ✓ status is 200
     ✓ response has content

     checks.........................: 100.00% ✓ 48000      ✗ 0     
     data_received..................: 12 MB   50 kB/s
     data_sent......................: 2.1 MB  8.8 kB/s
     http_req_blocked...............: avg=1.2ms    min=1µs    med=3µs     max=100ms   p(90)=5µs    p(95)=7µs   
     http_req_connecting............: avg=1.1ms    min=0s     med=0s      max=99ms    p(90)=0s     p(95)=0s    
     http_req_duration..............: avg=150ms    min=10ms   med=140ms   max=500ms   p(90)=250ms  p(95)=300ms 
       { expected_response:true }...: avg=150ms    min=10ms   med=140ms   max=500ms   p(90)=250ms  p(95)=300ms 
     http_req_failed................: 0.00%   ✓ 0          ✗ 24000 
     http_req_receiving.............: avg=50µs     min=10µs   med=40µs    max=5ms     p(90)=80µs   p(95)=100µs 
     http_req_sending...............: avg=20µs     min=5µs    med=15µs    max=2ms     p(90)=30µs   p(95)=40µs  
     http_req_tls_handshaking.......: avg=0s       min=0s     med=0s      max=0s      p(90)=0s     p(95)=0s    
     http_req_waiting...............: avg=149.9ms  min=10ms   med=139.9ms max=499.9ms p(90)=249.9ms p(95)=299.9ms
     http_reqs......................: 24000   100/s
     iteration_duration.............: avg=250ms    min=110ms  med=240ms   max=600ms   p(90)=350ms  p(95)=400ms 
     iterations.....................: 24000   100/s
     vus............................: 1       min=1        max=100 
     vus_max........................: 100     min=100      max=100 

k6実行時のトラブルシューティング

問題: WARN[0000] Request Failed error="Get \"http://localhost:xxxxx\": dial tcp [::1]:xxxxx: connect: connection refused"
# 1. Serviceが正しく作成されているか確認
kubectl get service frontend-service

# 2. Podが起動しているか確認
kubectl get pods -l app=frontend

# 3. Endpointsが設定されているか確認
kubectl get endpoints frontend-service

# 4. localhostでアクセスできるか確認
curl http://localhost:${NODE_PORT}
問題: すべてのリクエストがタイムアウトする
# 1. Podのログを確認
kubectl logs -l app=frontend --tail=50

# 2. Serviceの詳細を確認
kubectl describe service frontend-service

# 3. ファイアウォールやネットワーク設定を確認
# (Rancher Desktopの場合、通常は問題なし)

3.3 自動スケーリング(HPA)の設定

Horizontal Pod Autoscaler(HPA)を設定して、負荷に応じて自動的にスケーリングされるようにします。

重要: HPAを動作させるには、Podにリソース制限を設定する必要があります。リソース制限がない場合、HPAはCPU使用率を計測できません。

ステップ1: Podにリソース制限を設定

重要: CPU limitsを低く設定することで、スケーリングが発生しやすくなります。また、HPAが動作するにはrequestsが必須です。

# Deploymentにリソース制限を設定(CPU limitsを低く設定)
kubectl set resources deployment frontend \
  --limits=cpu=50m,memory=128Mi \
  --requests=cpu=10m,memory=64Mi

# 設定を確認
kubectl describe deployment frontend | grep -A 5 "Limits\|Requests"

リソース制限の説明:

  • limits: Podが使用できる最大リソース(CPU: 50m = 0.05コア、メモリ: 128Mi)
  • requests: Podに保証される最小リソース(CPU: 10m = 0.01コア、メモリ: 64Mi)
    • 重要: HPAはrequestsを基準にCPU使用率を計算します。requestsがないとHPAは動作しません。

なぜCPU limitsを低く設定するのか:

  • CPU limitsが低いと、同じ負荷でもCPU使用率が高くなりやすい
  • HPAの閾値(70%)に到達しやすくなり、スケーリングが発生しやすくなる
  • デモンストレーション目的で、実際の本番環境では適切な値を設定してください

ステップ1.5: Podを再作成してリソース制限を適用

重要: リソース制限を設定した後、既存のPodは自動的に再作成されません。手動で再作成する必要があります。

# Podを再作成してリソース制限を適用
kubectl rollout restart deployment frontend

# Podが再作成されるまで待機
kubectl rollout status deployment frontend

# 新しいPodにリソース制限が適用されているか確認
kubectl get pods -l app=frontend
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# 確認ポイント:
# - Limits: cpu=50m, memory=128Mi
# - Requests: cpu=10m, memory=64Mi
# 両方が設定されていることを確認してください

ステップ2: metrics-serverの確認とインストール

# metrics-serverがインストールされているか確認
kubectl get deployment metrics-server -n kube-system

# metrics-serverがインストールされていない場合、インストール
# Rancher Desktopの場合:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# metrics-serverのPodが起動するまで待機
kubectl wait --for=condition=ready pod -l k8s-app=metrics-server -n kube-system --timeout=60s

# メトリクスが取得できるか確認
kubectl top pods

ステップ3: HPAの作成

基本設定:

# HPAの作成(CPU使用率が70%を超えたらスケール)
kubectl autoscale deployment frontend --cpu-percent=70 --min=1 --max=5

# HPAの確認
kubectl get hpa

# HPAの詳細確認
kubectl describe hpa frontend

HPAの設定説明:

  • --cpu-percent=70: CPU使用率が70%を超えたらスケールアップ
  • --min=1: 最小Pod数は1個
  • --max=5: 最大Pod数は5個

注意: デフォルトでは、HPAはスケールアウト(増やす)は積極的に行いますが、スケールイン(減らす)は慎重に行います。負荷が下がっても、すぐにはPod数が減らない場合があります。

ステップ3.5: スケールインを促進する設定(オプション)

負荷テスト完了後にスケールインを確認したい場合、HPAのbehaviorフィールドでスケールダウンポリシーを設定できます。

# 既存のHPAを削除
kubectl delete hpa frontend

# スケールインを促進するHPAを作成(YAMLファイルを使用)
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: frontend
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 30  # スケールダウンの安定化期間を30秒に短縮
      policies:
      - type: Percent
        value: 50  # 一度に50%まで減らせる
        periodSeconds: 15  # 15秒ごとに評価
      - type: Pods
        value: 2  # 一度に最大2個まで減らせる
        periodSeconds: 15
      selectPolicy: Min  # より控えめな方(Min)を選択
EOF

# HPAの確認
kubectl get hpa frontend

# HPAの詳細確認
kubectl describe hpa frontend

スケールダウンポリシーの説明:

  • stabilizationWindowSeconds: 30: スケールダウンの安定化期間を30秒に短縮(デフォルトは5分)
  • policies: スケールダウンのポリシー
    • type: Percent, value: 50: 一度に50%まで減らせる
    • type: Pods, value: 2: 一度に最大2個まで減らせる
    • periodSeconds: 15: 15秒ごとに評価
  • selectPolicy: Min: より控えめな方(Min)を選択

デフォルトの動作との違い:

  • デフォルト: スケールダウンの安定化期間が5分のため、負荷が下がってもすぐには減らない
  • 設定後: 安定化期間が30秒に短縮され、より早くスケールインする

HPAの動作確認:

重要: HPAが設定されていないと、自動スケーリングは発生しません。必ずHPAが作成されているか確認してください。

# HPAが作成されているか確認
kubectl get hpa

# HPAが存在しない場合、作成する
kubectl autoscale deployment frontend --cpu-percent=70 --min=1 --max=5

# HPAの詳細を確認
kubectl describe hpa frontend

別ターミナルでの監視方法:

Rancher Desktopでは、別のターミナルを開いて以下のコマンドで監視します:

# ターミナル1: HPAとPodの状態を監視(1秒ごとに更新)
watch -n 1 'kubectl get hpa frontend && echo "" && kubectl get pods -l app=frontend && echo "" && kubectl top pods -l app=frontend'

# または、個別に確認する場合:
# HPAの状態を確認
kubectl get hpa frontend

# Podの数を確認
kubectl get pods -l app=frontend

# CPU使用率を確認(重要!)
kubectl top pods -l app=frontend

負荷テストの実行:

# ターミナル2: 負荷テストを実行
# NodePort番号を取得
export NODE_PORT=$(kubectl get service frontend-service -o jsonpath='{.spec.ports[0].nodePort}')

# k6で負荷テストを実行(短時間で高負荷をかける)
k6 run --env SERVICE_URL=http://localhost:${NODE_PORT} loadtest.js

監視のポイント:

  • ターミナル1で確認すべきこと:
    1. kubectl get hpa frontendTARGETS 列でCPU使用率を確認(例: 85%/70%
    2. kubectl get pods -l app=frontend でPod数が増加しているか確認
    3. kubectl top pods -l app=frontend で各PodのCPU使用率を確認(70%を超えているか)

期待される動作:

  1. 負荷テスト開始後10秒: 100 VUsまで増加し、CPU使用率が上昇
  2. CPU使用率が70%を超える: HPAが検知し、Pod数を増やす
  3. 30秒間: 高負荷が維持され、Pod数が最大5個まで増加
  4. 負荷テスト終了後: CPU使用率が下がり、Pod数が徐々に減少

注意: デフォルトのHPA設定では、スケールイン(Pod数を減らす)は慎重に行われます。負荷テスト完了後、すぐにはPod数が減らない場合があります。スケールインを確認したい場合は、上記の「ステップ3.5: スケールインを促進する設定」を適用してください。

確認ポイント(別ターミナルで実行):

# 1. HPAが存在するか確認(重要!)
kubectl get hpa frontend

# 出力例:
# NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# frontend   Deployment/frontend   85%/70%   1         5         3          2m
#
# TARGETS列: 現在のCPU使用率 / 目標CPU使用率(70%)
# REPLICAS列: 現在のPod数

# 2. HPAの詳細を確認(Eventsセクションでスケーリングイベントを確認)
kubectl describe hpa frontend

# 3. Podの数を確認
kubectl get pods -l app=frontend

# 4. Deploymentの状態を確認(REPLICAS列でスケーリングを確認)
kubectl get deployment frontend

# 5. CPU使用率を確認(重要!負荷がかかっているか確認)
kubectl top pods -l app=frontend

# 出力例(負荷がかかっている場合):
# NAME                        CPU(cores)   MEMORY(bytes)
# frontend-xxxxxxxxxx-xxxxx   45m         30Mi
# frontend-xxxxxxxxxx-yyyyy   48m         28Mi
# frontend-xxxxxxxxxx-zzzzz   42m         32Mi
#
# CPU(cores)列: 各PodのCPU使用率(50m = 0.05コア = limitsの100%)
# limitsが50mの場合、50m = 100%のCPU使用率

正常な場合の出力例:

# HPAの状態
NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
frontend   Deployment/frontend   85%/70%   1         5         3          2m

# Deploymentの状態
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
frontend   3/3     3            3           5m

# Podの状態
NAME                        READY   STATUS    RESTARTS   AGE
frontend-xxxxxxxxxx-xxxxx    1/1     Running   0          5m
frontend-xxxxxxxxxx-yyyyy    1/1     Running   0          1m
frontend-xxxxxxxxxx-zzzzz    1/1     Running   0          30s

# CPU使用率
NAME                        CPU(cores)   MEMORY(bytes)
frontend-xxxxxxxxxx-xxxxx   150m         50Mi
frontend-xxxxxxxxxx-yyyyy   180m         48Mi
frontend-xxxxxxxxxx-zzzzz   160m         52Mi

負荷が増加すると、Podの数が自動的に増加し、最大5個までスケールすることを確認できます。

HPAは、メトリクスサーバーから取得したCPU使用率に基づいて、自動的にPodの数を調整します。

3.5 リソース使用状況の確認

負荷テスト中のリソース使用状況を確認:

# Podのリソース使用状況
kubectl top pods

# ノードのリソース使用状況
kubectl top nodes

# 詳細なメトリクス確認
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/default/pods | jq

4. 実践的なトラブルシューティング(まとめ)

4.1 接続のフローチャート

外部からアクセスできない場合、以下の順序で確認します:

1. Podが起動しているか?
   ↓ No → kubectl describe pod <pod-name> でエラー確認
   ↓ Yes
   
2. Serviceが作成されているか?
   ↓ No → kubectl expose deployment frontend --type=NodePort ...
   ↓ Yes
   
3. Endpointsが設定されているか?
   ↓ No → ラベルセレクターを確認
   ↓ Yes
   
4. クラスター内からアクセスできるか?
   ↓ No → Serviceの設定を確認
   ↓ Yes
   
5. localhostからNodePortにアクセスできるか?
   ↓ No → Rancher Desktopの設定を確認
   ↓ Yes
   
6. k6でアクセスできるか?
   ↓ No → k6スクリプトのURLを確認
   ↓ Yes
   
✓ 成功!

4.2 Podが起動しない場合

# Podの状態確認
kubectl get pods -l app=frontend

# Podが起動していない場合の詳細確認
kubectl describe pod <pod-name>

# よくあるエラー:
# - ImagePullBackOff: イメージが見つからない
# - CrashLoopBackOff: コンテナが起動後すぐにクラッシュ
# - Pending: リソース不足またはスケジューリング問題

# Podのログ確認
kubectl logs <pod-name>

# 前回クラッシュしたコンテナのログ
kubectl logs <pod-name> --previous

4.3 Serviceに接続できない場合

# Serviceの確認
kubectl get service frontend-service

# Serviceの詳細確認(Eventsを確認)
kubectl describe service frontend-service

# Endpointsの確認(重要!)
kubectl get endpoints frontend-service

# Endpointsが空の場合
kubectl get pods -l app=frontend --show-labels
kubectl get service frontend-service -o jsonpath='{.spec.selector}'

# DNS解決の確認(クラスター内のPodから)
kubectl run dns-test --image=busybox:latest --rm -it --restart=Never -- nslookup frontend-service

4.4 スケーリングが動作しない場合

ステップ1: HPAの確認

重要: HPAが設定されていないと、自動スケーリングは発生しません。

# HPAが作成されているか確認
kubectl get hpa

# HPAが存在しない場合、作成する
kubectl autoscale deployment frontend --cpu-percent=70 --min=1 --max=5

# HPAの詳細を確認
kubectl describe hpa frontend

よくあるエラー: missing request for cpu

HPAの詳細を確認した際に、以下のようなエラーが表示される場合があります:

Warning  FailedGetObjectMetric  HPA was unable to compute the replica count: 
failed to get cpu utilization: missing request for cpu in container nginx of Pod frontend-xxx-xxx

原因: PodのコンテナにCPUのrequestsが設定されていません。

対処法:

# 1. Podにリソース制限が設定されているか確認
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# 2. Requestsが設定されていない場合、Deploymentに設定する
kubectl set resources deployment frontend \
  --limits=cpu=50m,memory=128Mi \
  --requests=cpu=10m,memory=64Mi

# 3. Podを再作成してリソース制限を適用(重要!)
kubectl rollout restart deployment frontend

# 4. Podが再作成されるまで待機
kubectl rollout status deployment frontend

# 5. 新しいPodにリソース制限が適用されているか確認
kubectl describe pod <new-pod-name> | grep -A 5 "Limits\|Requests"

# 6. HPAの状態を再確認(エラーが解消されているか確認)
kubectl describe hpa frontend

ステップ2: リソース制限の確認

重要: Podにリソース制限が設定されていない場合、HPAは動作しません。特に**requestsが設定されていないと、HPAはCPU使用率を計算できません**。

# Deploymentのリソース制限を確認
kubectl describe deployment frontend | grep -A 5 "Limits\|Requests"

# 実際のPodにリソース制限が適用されているか確認(重要!)
kubectl get pods -l app=frontend
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# 確認ポイント:
# - Limits: cpu=50m, memory=128Mi が設定されているか
# - Requests: cpu=10m, memory=64Mi が設定されているか(重要!)
# Requestsが設定されていない場合、HPAは動作しません

リソース制限が設定されていない場合:

# リソース制限を設定(CPU limitsを低く設定)
kubectl set resources deployment frontend \
  --limits=cpu=50m,memory=128Mi \
  --requests=cpu=10m,memory=64Mi

# Podを再作成してリソース制限を適用(重要!)
kubectl rollout restart deployment frontend

# Podが再作成されるまで待機
kubectl rollout status deployment frontend

# 新しいPodにリソース制限が適用されているか再確認
kubectl describe pod <new-pod-name> | grep -A 5 "Limits\|Requests"

ステップ2: metrics-serverの確認

# metrics-serverの状態確認
kubectl get deployment metrics-server -n kube-system

# metrics-serverが存在しない場合、インストールが必要
# Rancher Desktopの場合:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# metrics-serverのPodが起動するまで待機
kubectl wait --for=condition=ready pod -l k8s-app=metrics-server -n kube-system --timeout=60s

# メトリクスが取得できているか確認
kubectl top pods

# メトリクスが取得できない場合、metrics-serverのログを確認
kubectl logs -n kube-system -l k8s-app=metrics-server

ステップ3: HPAの状態確認

# HPAの状態確認
kubectl describe hpa frontend

# HPAのイベントを確認(Eventsセクション)
# よくある問題:
# - "unable to get metrics": metrics-serverが動作していない
# - "missing request for cpu": リソース制限が設定されていない

ステップ4: 負荷がかかっているか確認(別ターミナルで実行)

重要: 負荷テスト実行中に、別のターミナルでCPU使用率を確認します。

# CPU使用率を確認(負荷テスト実行中)
kubectl top pods -l app=frontend

# 期待される出力(負荷がかかっている場合、limits=50mの場合):
# NAME                        CPU(cores)   MEMORY(bytes)
# frontend-xxxxxxxxxx-xxxxx   45m         30Mi
# frontend-xxxxxxxxxx-yyyyy   48m         28Mi
#
# CPU(cores)列の値:
# - limitsが50mの場合、50m = 100%のCPU使用率
# - 70%を超えるとHPAがスケールアップ(50m × 0.7 = 35m以上)
# - 実際の値が35m以上なら、HPAがスケールアップするはず

# CPU使用率が低い場合(例: 10m以下):
# - 負荷テストが正しく実行されていない可能性
# - k6スクリプトの設定を確認
# - リクエストがServiceに到達しているか確認
# - HPAが作成されているか確認(kubectl get hpa)

CPU使用率の計算方法:

  • limits=cpu=50m の場合:
    • 50m = 100%のCPU使用率
    • 35m = 70%のCPU使用率(HPAの閾値)
    • kubectl top pods で35m以上なら、HPAがスケールアップするはず

ステップ5: 負荷テストの確認

# k6の実行状況を確認
# 別のターミナルで実行中に以下を確認:

# 1. リクエスト数が増加しているか確認
kubectl logs -l app=frontend --tail=20 -f

# 2. Serviceへのアクセスログを確認
# (Nginxの場合、アクセスログが出力される)

# 3. k6の出力でリクエストレートを確認
# http_reqs が 100/s 以上になっているか確認

4.5 k6負荷テストが失敗する場合

# 1. 基本的な接続確認
curl -v http://localhost:${NODE_PORT}

# 2. NodePort番号の再確認
kubectl get service frontend-service -o jsonpath='{.spec.ports[0].nodePort}'

# 3. k6スクリプトのURLを確認
# loadtest.js内のデフォルトURLが正しいか確認

# 4. k6のバージョン確認
k6 version

# 5. 詳細なデバッグモードでk6を実行
k6 run --http-debug --env SERVICE_URL=http://localhost:${NODE_PORT} loadtest.js

4.6 よくある質問(FAQ)

Q1: Rancher Desktopでkubectl get nodesが失敗する

# Kubernetesが有効になっているか確認
# Rancher Desktop → Preferences → Kubernetes → Enable Kubernetes

# kubectlのコンテキストを確認
kubectl config get-contexts

# Rancher Desktopのコンテキストに切り替え
kubectl config use-context rancher-desktop

Q2: NodePortが割り当てられない

# Serviceのイベントを確認
kubectl describe service frontend-service

# ポート範囲を指定してServiceを作成
kubectl delete service frontend-service
kubectl expose deployment frontend --port=80 --target-port=80 --type=NodePort --name=frontend-service

Q3: 負荷テスト中にPodが増えない

# 1. HPAが作成されているか確認(重要!)
kubectl get hpa
# HPAが存在しない場合、作成する:
kubectl autoscale deployment frontend --cpu-percent=70 --min=1 --max=5

# 2. HPAの状態を確認(エラーメッセージを確認)
kubectl describe hpa frontend
# Eventsセクションでスケーリングイベントを確認
# エラー例: "missing request for cpu" が表示される場合、ステップ3へ

# 3. リソース制限が設定されているか確認(重要!)
# Deploymentの設定を確認
kubectl describe deployment frontend | grep -A 5 "Limits\|Requests"

# 実際のPodにリソース制限が適用されているか確認(重要!)
kubectl get pods -l app=frontend
kubectl describe pod <pod-name> | grep -A 5 "Limits\|Requests"

# Requestsが設定されていない場合、HPAは動作しません
# 確認ポイント:
#   - Limits: cpu=50m, memory=128Mi が設定されているか
#   - Requests: cpu=10m, memory=64Mi が設定されているか(重要!)

# 4. リソース制限が設定されていない、または適用されていない場合:
kubectl set resources deployment frontend \
  --limits=cpu=50m,memory=128Mi \
  --requests=cpu=10m,memory=64Mi

# Podを再作成してリソース制限を適用(重要!)
kubectl rollout restart deployment frontend

# Podが再作成されるまで待機
kubectl rollout status deployment frontend

# 新しいPodにリソース制限が適用されているか再確認
kubectl describe pod <new-pod-name> | grep -A 5 "Limits\|Requests"

# 5. HPAの状態を再確認(エラーが解消されているか確認)
kubectl describe hpa frontend

# 6. CPU使用率を確認(別ターミナルで負荷テスト実行中に確認)
kubectl top pods -l app=frontend

# 7. CPU使用率の計算
# limits=50mの場合:
#   - 50m = 100%のCPU使用率
#   - 35m = 70%のCPU使用率(HPAの閾値)
#   - kubectl top podsで35m以上なら、HPAがスケールアップするはず

# 8. CPU使用率が低い場合(35m未満):
#   - CPU limitsを下げる(例: 50m → 30m)
kubectl set resources deployment frontend \
  --limits=cpu=30m,memory=128Mi \
  --requests=cpu=10m,memory=64Mi
kubectl rollout restart deployment frontend

# 9. 再度負荷テストを実行
export NODE_PORT=$(kubectl get service frontend-service -o jsonpath='{.spec.ports[0].nodePort}')
k6 run --env SERVICE_URL=http://localhost:${NODE_PORT} loadtest.js

Q4: 負荷テスト完了後、Pod数が減らない(スケールインしない)

原因: HPAのデフォルト設定では、スケールイン(Pod数を減らす)は慎重に行われます。スケールダウンの安定化期間が5分のため、負荷が下がってもすぐには減りません。

対処法: HPAのbehaviorフィールドでスケールダウンポリシーを設定します。

# 1. 現在のHPAの状態を確認
kubectl get hpa frontend
kubectl describe hpa frontend

# 2. CPU使用率を確認(負荷テスト完了後)
kubectl top pods -l app=frontend
# CPU使用率が0%または低い場合、スケールインするはずですが、デフォルトでは時間がかかります

# 3. スケールインを促進する設定を適用
# 既存のHPAを削除
kubectl delete hpa frontend

# スケールインを促進するHPAを作成
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: frontend
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 30  # スケールダウンの安定化期間を30秒に短縮
      policies:
      - type: Percent
        value: 50  # 一度に50%まで減らせる
        periodSeconds: 15  # 15秒ごとに評価
      - type: Pods
        value: 2  # 一度に最大2個まで減らせる
        periodSeconds: 15
      selectPolicy: Min  # より控えめな方(Min)を選択
EOF

# 4. HPAの状態を確認
kubectl get hpa frontend
kubectl describe hpa frontend

# 5. 負荷テストを再実行して、スケールインを確認
# 負荷テスト完了後、30秒〜1分程度でPod数が減ることを確認できます

設定の説明:

  • stabilizationWindowSeconds: 30: スケールダウンの安定化期間を30秒に短縮(デフォルトは5分)
  • これにより、負荷が下がってから30秒後にはスケールインが開始されます

注意: 本番環境では、スケールインが早すぎると、一時的な負荷増加に対応できなくなる可能性があります。デフォルトの5分の安定化期間は、このような問題を防ぐための設定です。

5. クリーンアップ

作成したリソースを削除:

# DeploymentとServiceの削除
kubectl delete deployment frontend
kubectl delete service frontend-service

# HPAの削除
kubectl delete hpa frontend

# すべてのリソースの確認
kubectl get all

まとめ

本記事では、Rancher Desktopを使用したKubernetesの実践的な手順を紹介しました。特に、外部からのアクセス設定とトラブルシューティングに重点を置いています。

学んだこと:

  1. DeploymentとServiceの基本: アプリケーションのデプロイとネットワークアクセスの設定
  2. 外部アクセス: NodePortを使用したRancher Desktopからの外部アクセス方法
  3. 疎通確認: 各レイヤーでの接続確認とトラブルシューティング手順
  4. スケーリング: 手動スケーリングと自動スケーリング(HPA)の設定
  5. 負荷テスト: k6を使用したローカルMacからの負荷テスト実行
  6. トラブルシューティング: よくある問題の体系的な確認方法

重要なポイント:

  • Rancher Desktopではlocalhost経由でNodePortにアクセス可能
  • 外部アクセスには必ずNodePortタイプのServiceを使用
  • 疎通確認は段階的に実施(Pod → Service → Endpoints → 外部アクセス)
  • k6はローカルのMacから実行(クラスター内部ではない)

次のステップ:

補足: その他のServiceタイプ

本記事ではNodePortを中心に説明しましたが、環境に応じて他のServiceタイプも利用できます。

ClusterIP(デフォルト)

クラスター内部からのみアクセス可能なService。マイクロサービス間の通信に使用します。

# ClusterIPタイプのServiceを作成
kubectl expose deployment frontend --port=80 --target-port=80 --type=ClusterIP --name=frontend-internal

# クラスター内からのみアクセス可能
kubectl run test-pod --image=curlimages/curl:latest --rm -it --restart=Never -- curl http://frontend-internal:80

使用ケース:

  • バックエンドAPIサーバー
  • データベース
  • 内部専用サービス

LoadBalancer(クラウド環境)

クラウドプロバイダー(GKE、EKS、AKSなど)が提供するロードバランサーを使用します。

# LoadBalancerタイプでServiceを作成
kubectl expose deployment frontend --port=80 --target-port=80 --type=LoadBalancer --name=frontend-lb

# EXTERNAL-IPが割り当てられるまで待機
kubectl get service frontend-lb -w

# EXTERNAL-IPが表示されたら、そのIPでアクセス可能
# 例: http://35.xxx.xxx.xxx

使用ケース:

  • 本番環境での外部公開
  • インターネットからの直接アクセスが必要なサービス
  • GKE、EKS、AKSなどのクラウド環境

注意: Rancher Desktopでは、LoadBalancerタイプは<pending>状態のままで外部IPは割り当てられません。

ExternalName

外部サービスへのエイリアスを提供します。

# ExternalNameタイプのServiceを作成
kubectl create service externalname my-database --external-name=database.example.com

# クラスター内から external-name で外部サービスにアクセス可能

使用ケース:

  • 外部データベースへの接続
  • 外部APIサーバーへのアクセス
  • DNSエイリアスの作成

参考資料

Discussion