Skip to content
Published on

Docker & Kubernetes 入門完全ガイド — コンテナからオーケストレーションまで

Authors

1. コンテナとは何か

仮想マシンとコンテナの違い

従来の仮想マシン(VM)は、ハイパーバイザー上でゲストOS全体を実行します。各VMにはカーネル、ライブラリ、バイナリがすべて含まれるため、数GBのディスクと数十秒の起動時間が必要です。

コンテナはホストOSのカーネルを共有し、プロセスレベルで分離を実現します。イメージサイズは数十MB程度で、起動時間はミリ秒単位です。

項目仮想マシンコンテナ
分離レベルOS全体プロセス
イメージサイズ数GB数十~数百MB
起動時間数十秒ミリ秒
リソースオーバーヘッド高い低い
ポータビリティハイパーバイザー依存カーネルが一致すればどこでも

Linuxネームスペースとcgroups

コンテナの分離は、2つのLinuxカーネル機能で実現されています。

ネームスペース(Namespace) は、プロセスが見えるシステムリソースの範囲を制限します。

  • PIDネームスペース: コンテナ内ではPID 1から始まる独立したプロセスツリーが見えます
  • NETネームスペース: 独立したネットワークインターフェース、IPアドレス、ルーティングテーブルを持ちます
  • MNTネームスペース: 独立したファイルシステムマウントポイントを持ちます
  • UTSネームスペース: 独立したホスト名を持ちます
  • IPCネームスペース: 独立したIPCリソースを持ちます
  • USERネームスペース: 独立したUID/GIDマッピングを持ちます

cgroups(Control Groups) は、CPU、メモリ、ディスクI/Oなどのハードウェアリソース使用量を制限します。

# cgroupでメモリ制限を確認する
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.limit_in_bytes

この2つの機能により、コンテナは独立したマシンのように動作しながらもカーネルを共有し、軽量な状態を維持できます。


2. Dockerの基礎

3つのコア概念

イメージ(Image) は読み取り専用のテンプレートです。アプリケーションコード、ランタイム、システムライブラリ、設定ファイルがレイヤー構造で格納されています。

コンテナ(Container) はイメージの実行インスタンスです。イメージの上に書き込み可能なレイヤーが追加されます。

レジストリ(Registry) はイメージを保存・配布するサービスです。Docker Hubが代表的で、AWS ECRやGitHub Container Registryなどのプライベートレジストリもあります。

必須コマンド

# イメージのpull
docker pull nginx:1.25

# コンテナの実行
docker run -d --name my-nginx -p 8080:80 nginx:1.25

# 実行中のコンテナを確認
docker ps

# コンテナのログを確認
docker logs my-nginx

# コンテナ内部にアクセス
docker exec -it my-nginx /bin/bash

# コンテナの停止と削除
docker stop my-nginx
docker rm my-nginx

# イメージのビルド
docker build -t my-app:1.0 .

# イメージをレジストリにプッシュ
docker tag my-app:1.0 registry.example.com/my-app:1.0
docker push registry.example.com/my-app:1.0

イメージレイヤー構造の理解

Dockerイメージは複数の読み取り専用レイヤーで構成されています。Dockerfileの各命令が1つのレイヤーを作成します。

# イメージレイヤーの確認
docker history my-app:1.0

レイヤーはキャッシュされます。変更されていないレイヤーは再ビルドされないため、Dockerfileで変更頻度の低い命令を上部に配置することがビルド速度最適化の鍵です。


3. Dockerfileベストプラクティス

基本的なDockerfile構造

# ベースイメージの指定
FROM node:20-alpine

# 作業ディレクトリの設定
WORKDIR /app

# 依存関係ファイルを先にコピー(キャッシュ最適化)
COPY package.json package-lock.json ./
RUN npm ci --only=production

# ソースコードをコピー
COPY . .

# ポートの公開
EXPOSE 3000

# 実行コマンド
CMD ["node", "server.js"]

マルチステージビルド

ビルドツールとランタイム環境を分離し、最終イメージのサイズを大幅に削減できます。

# ステージ1: ビルド
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# ステージ2: プロダクション
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

EXPOSE 3000
USER node
CMD ["node", "dist/server.js"]

ビルド段階でのみ必要なdevDependencies、ソースコード、ビルドツールが最終イメージに含まれません。

レイヤーキャッシュの最適化

# 悪い例: ソースが変更されるたびにnpm installが再実行される
COPY . .
RUN npm ci

# 良い例: package.jsonが変更されていなければキャッシュを活用
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

.dockerignoreファイル

不要なファイルがビルドコンテキストに含まれないようにします。

node_modules
.git
.env
*.md
dist
.DS_Store
coverage

セキュリティベストプラクティス

# 1. rootでないユーザーで実行
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 2. 特定バージョンのベースイメージを使用(latestタグは使用禁止)
FROM node:20.11.1-alpine3.19

# 3. ADDではなくCOPYを使用
COPY ./config /app/config

# 4. 機密情報をイメージに含めない
# ビルド時のARGを使用する場合は最終イメージに残らないよう注意

4. Docker Compose

マルチコンテナオーケストレーション

Docker Composeは、複数のコンテナを1つのYAMLファイルで定義・管理します。

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    volumes:
      - ./src:/app/src
    networks:
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - backend

volumes:
  postgres_data:

networks:
  backend:
    driver: bridge

Composeの主要コマンド

# 全サービスの起動
docker compose up -d

# ログの確認
docker compose logs -f app

# 特定サービスを再ビルドして起動
docker compose up -d --build app

# サービスの状態確認
docker compose ps

# 全サービスの停止とリソースのクリーンアップ
docker compose down -v

ネットワークとボリューム

ネットワーク: 同じComposeファイル内のサービスは、サービス名で互いに通信できます。上記の例では、appサービスは db:5432 でデータベースにアクセスします。

ボリューム: コンテナが削除されてもデータが保持されます。postgres_data という名前付きボリュームがデータベースファイルを保管します。

開発環境 vs プロダクション環境

# docker-compose.override.yml(開発環境で自動適用)
services:
  app:
    build:
      target: development
    volumes:
      - ./src:/app/src
    environment:
      - NODE_ENV=development
    command: npm run dev
# プロダクションデプロイ時はoverrideを除外
docker compose -f docker-compose.yml up -d

5. Kubernetesアーキテクチャ

なぜKubernetesなのか

Docker Composeは単一ホストで複数コンテナを管理するのに適していますが、実際のプロダクション環境では以下が必要です。

  • 複数サーバーにまたがるコンテナデプロイ
  • オートスケーリング
  • サービスディスカバリとロードバランシング
  • ローリングアップデートとロールバック
  • 自己修復(self-healing)

Kubernetes(K8s)はこれらすべてを提供するコンテナオーケストレーションプラットフォームです。

Control Planeコンポーネント

API Server(kube-apiserver): クラスタのすべてのリクエストを処理する中央ゲートウェイです。kubectlコマンド、内部コンポーネント、外部クライアントのすべてがAPI Serverを経由します。

etcd: クラスタの全状態を保存する分散キーバリューストアです。どのPodがどこで実行されているか、どのServiceが存在するかなどの情報が格納されています。

Scheduler(kube-scheduler): 新しく作成されたPodをどのノードに配置するかを決定します。リソース要件、ノードの状態、アフィニティルールなどを考慮します。

Controller Manager(kube-controller-manager): クラスタの現在の状態を希望する状態(desired state)に合わせる役割を担います。ReplicaSet Controller、Node Controller、Job Controllerなどが含まれます。

Worker Nodeコンポーネント

kubelet: 各ノードで実行され、API Serverの指示に従ってコンテナを実行し、状態を報告します。

kube-proxy: 各ノードでネットワークルールを管理し、Serviceのロードバランシングを処理します。

Container Runtime: 実際にコンテナを実行するソフトウェアです。containerdが最も広く使用されています。

Control Plane
  +------------------+
  | API Server       |<--- kubectl、クライアント
  | etcd             |
  | Scheduler        |
  | Controller Mgr   |
  +------------------+
        |
  Worker Node 1          Worker Node 2
  +-----------------+   +-----------------+
  | kubelet         |   | kubelet         |
  | kube-proxy      |   | kube-proxy      |
  | containerd      |   | containerd      |
  | [Pod] [Pod]     |   | [Pod] [Pod]     |
  +-----------------+   +-----------------+

6. Kubernetesコアオブジェクト

Pod

PodはK8sでデプロイ可能な最小単位です。1つ以上のコンテナを含み、同じネットワークとストレージを共有します。

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  containers:
    - name: app
      image: my-app:1.0
      ports:
        - containerPort: 3000
      resources:
        requests:
          memory: "128Mi"
          cpu: "250m"
        limits:
          memory: "256Mi"
          cpu: "500m"
      livenessProbe:
        httpGet:
          path: /healthz
          port: 3000
        initialDelaySeconds: 10
        periodSeconds: 5
      readinessProbe:
        httpGet:
          path: /ready
          port: 3000
        initialDelaySeconds: 5
        periodSeconds: 3

ReplicaSet

指定された数のPodレプリカが常に実行されることを保証します。通常は直接使用せず、Deploymentを通じて管理します。

Deployment

宣言的にPodとReplicaSetを管理します。ローリングアップデート、ロールバック、スケーリングをサポートします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: my-app:1.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "500m"
# デプロイの状態確認
kubectl rollout status deployment/my-app

# イメージの更新(ローリングアップデートのトリガー)
kubectl set image deployment/my-app app=my-app:2.0

# ロールバック
kubectl rollout undo deployment/my-app

# スケーリング
kubectl scale deployment/my-app --replicas=5

Service

Podに安定したネットワークエンドポイントを提供します。Podは一時的ですが、ServiceのIPとDNSは固定です。

ClusterIP(デフォルト): クラスタ内部からのみアクセス可能です。

apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 3000

NodePort: 各ノードの特定ポートを通じて外部からアクセスします。

apiVersion: v1
kind: Service
metadata:
  name: my-app-nodeport
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30080

LoadBalancer: クラウドプロバイダーのロードバランサーを自動的にプロビジョニングします。

apiVersion: v1
kind: Service
metadata:
  name: my-app-lb
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 3000

Ingress

HTTP/HTTPSトラフィックをクラスタ内部のServiceにルーティングします。ホスト名とパスに基づくルーティングルールを定義できます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80
  tls:
    - hosts:
        - app.example.com
      secretName: tls-secret

7. Kubernetes設定管理

ConfigMap

環境設定をコンテナイメージから分離して管理します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DATABASE_HOST: "db-service"
  DATABASE_PORT: "5432"
  LOG_LEVEL: "info"
  app.properties: |
    server.port=3000
    cache.ttl=300
# PodでConfigMapを使用
spec:
  containers:
    - name: app
      image: my-app:1.0
      envFrom:
        - configMapRef:
            name: app-config
      volumeMounts:
        - name: config-volume
          mountPath: /app/config
  volumes:
    - name: config-volume
      configMap:
        name: app-config
        items:
          - key: app.properties
            path: app.properties

Secret

パスワード、APIキーなどの機密データを管理します。base64エンコードされて保存されます。

# Secretの作成
kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=s3cret
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=
  password: czNjcmV0
# PodでSecretを使用
spec:
  containers:
    - name: app
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password

PersistentVolume(PV)とPersistentVolumeClaim(PVC)

Podのライフサイクルとは独立してデータを保持します。

# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard
# DeploymentでPVCを使用
spec:
  containers:
    - name: postgres
      image: postgres:16-alpine
      volumeMounts:
        - name: postgres-storage
          mountPath: /var/lib/postgresql/data
  volumes:
    - name: postgres-storage
      persistentVolumeClaim:
        claimName: postgres-pvc

8. Helm - Kubernetesパッケージマネージャー

Helmとは

HelmはK8sマニフェストをパッケージ(チャート)にまとめて管理するツールです。複数のYAMLファイルを1つの単位としてインストール、アップグレード、ロールバックできます。

チャート構造

my-app-chart/
  Chart.yaml          # チャートメタデータ
  values.yaml         # デフォルト設定値
  templates/          # K8sマニフェストテンプレート
    deployment.yaml
    service.yaml
    ingress.yaml
    configmap.yaml
    _helpers.tpl      # テンプレートヘルパー関数
  charts/             # 依存関係チャート

values.yaml

# values.yaml
replicaCount: 3

image:
  repository: my-app
  tag: "1.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  hostname: app.example.com

resources:
  requests:
    cpu: 250m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilization: 70

Helmの主要コマンド

# チャートのインストール
helm install my-release ./my-app-chart

# カスタムvaluesでインストール
helm install my-release ./my-app-chart -f production-values.yaml

# リリースのアップグレード
helm upgrade my-release ./my-app-chart --set image.tag=2.0

# リリース一覧の確認
helm list

# リリースの状態確認
helm status my-release

# リリース履歴
helm history my-release

# ロールバック
helm rollback my-release 1

# リリースの削除
helm uninstall my-release

環境別valuesファイル管理

# 環境別ファイル構造
values.yaml              # 共通デフォルト値
values-dev.yaml          # 開発環境
values-staging.yaml      # ステージング環境
values-prod.yaml         # プロダクション環境
# ステージングデプロイ
helm upgrade --install my-app ./my-app-chart \
  -f values.yaml \
  -f values-staging.yaml \
  --namespace staging

# プロダクションデプロイ
helm upgrade --install my-app ./my-app-chart \
  -f values.yaml \
  -f values-prod.yaml \
  --namespace production

9. 実践デプロイ例 - Webアプリ + DB + Redis

全体構成

Webアプリケーション、PostgreSQLデータベース、RedisキャッシュをKubernetesにデプロイする完全な例です。

Namespaceの作成

kubectl create namespace my-app

PostgreSQLのデプロイ

# postgres-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: mydb
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
      volumes:
        - name: postgres-data
          persistentVolumeClaim:
            claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: my-app
spec:
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432

Redisのデプロイ

# redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          ports:
            - containerPort: 6379
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "250m"
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: my-app
spec:
  selector:
    app: redis
  ports:
    - port: 6379
      targetPort: 6379

Webアプリケーションのデプロイ

# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: my-web-app:1.0
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              value: "postgres://$(DB_USER):$(DB_PASS)@postgres:5432/mydb"
            - name: REDIS_URL
              value: "redis://redis:6379"
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: username
            - name: DB_PASS
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: web-app
  namespace: my-app
spec:
  type: ClusterIP
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  namespace: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app
                port:
                  number: 80

デプロイ手順

# 1. Secretの作成
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=secure-password-here \
  -n my-app

# 2. PVCの作成
kubectl apply -f postgres-pvc.yaml

# 3. データベースのデプロイ
kubectl apply -f postgres-deployment.yaml

# 4. Redisのデプロイ
kubectl apply -f redis-deployment.yaml

# 5. Webアプリのデプロイ
kubectl apply -f app-deployment.yaml

# 6. 状態の確認
kubectl get all -n my-app

10. トラブルシューティング

CrashLoopBackOff

Podが繰り返し起動とクラッシュを繰り返している状態です。

# 原因の確認
kubectl describe pod POD_NAME -n my-app
kubectl logs POD_NAME -n my-app --previous

# よくある原因:
# - アプリケーション起動時のエラー
# - 不正な環境変数
# - 依存サービスへの接続失敗
# - livenessProbeの失敗

解決方法: ログを確認してエラーを特定します。livenessProbeのinitialDelaySecondsを増やすか、環境変数が正しいか確認します。

ImagePullBackOff

コンテナイメージを取得できない状態です。

# 原因の確認
kubectl describe pod POD_NAME -n my-app

# よくある原因:
# - イメージ名やタグのタイプミス
# - プライベートレジストリ認証の未設定
# - イメージが存在しない

# プライベートレジストリ認証の設定
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password=pass \
  -n my-app

OOMKilled

コンテナがメモリ制限を超過して強制終了された状態です。

# 原因の確認
kubectl describe pod POD_NAME -n my-app

# リアルタイムのリソース使用量を確認
kubectl top pod -n my-app

# 解決: メモリlimitsの値を増やす
resources:
  limits:
    memory: "512Mi"  # 既存の256Miから増加

一般的なデバッグコマンド

# Pod一覧と状態の確認
kubectl get pods -n my-app -o wide

# Podの詳細情報(イベント含む)
kubectl describe pod POD_NAME -n my-app

# リアルタイムログの確認
kubectl logs -f POD_NAME -n my-app

# Pod内部へのアクセス
kubectl exec -it POD_NAME -n my-app -- /bin/sh

# Serviceエンドポイントの確認
kubectl get endpoints -n my-app

# クラスタ内部でのDNS確認
kubectl run debug --rm -it --image=busybox -- nslookup web-app.my-app.svc.cluster.local

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

まとめ

この記事で取り上げた内容を整理します。

  1. コンテナの基礎: ネームスペースとcgroupsによる軽量な分離の実現
  2. Docker: イメージビルド、マルチステージビルド、セキュリティプラクティス
  3. Docker Compose: 開発環境でのマルチコンテナ管理
  4. K8sアーキテクチャ: Control PlaneとWorker Nodeの役割
  5. K8sオブジェクト: Pod、Deployment、Service、Ingressの活用
  6. 設定管理: ConfigMap、Secret、PV/PVC
  7. Helm: チャートを利用したリリース管理
  8. 実践デプロイ: Webアプリ + DB + Redisの3層アーキテクチャ
  9. トラブルシューティング: CrashLoopBackOff、ImagePullBackOff、OOMKilledへの対応

DockerとKubernetesは現代のインフラの基盤です。この記事の例をローカル環境(minikubeまたはkind)で実際に試してみてください。Podをデプロイし、Serviceを構成し、トラブルシューティングを体験することが最も早い学習方法です。