0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KubernetesクラスタをVMware Workstationでオンプレ想定で構築してみる (RHEL9) - (5) 自作したGoアプリのリモートデバッグ編 -

Last updated at Posted at 2025-05-15

はじめに

Kubernetesクラスタに自分で開発したGoアプリケーションをディプロイし、それに対してVisual Studio Codeからリモートデバッグする際の備忘録 (小ネタ)。つまりコンテナ内で実行されるGoアプリケーションをDelveにより起動しリモートからデバッグ作業を行う。

例えばKubernetesクラスタ環境内に既に動作している他のコンポーネントとの連携動作を含めてデバッグしたい場合やDeoployment定義により構成された環境(ボリュームのマウント構成など)でデバッグを実施したい場合などを想定している。

利用するソフトウェア

  • VMware Workstation 17 Pro (Windows 11 / X86_64)
  • RHEL 9.5 (VM)
  • Kubernetes (v1.32)
  • Harbor (2.13.0)
  • Rook-Ceph (v1.17.1)

(参考) テストアプリケーションの作成環境

  • go (1.23.6)
  • Delve Debugger (1.24.1)
  • Podman (5.2.2)
  • Visual Studio Code (1.100.0)
  • Visual Studio Code拡張: Go (0.46.1)
  • Visual Studio Code拡張: Remote Development (0.26.0)
  • Visual Studio Code拡張: Remote Explorer (0.5.0)
  • Visual Studio Code拡張: Dev Containers (0.413.0)
  • Visual Studio Code拡張: YAML (1.18.0)
  • コンテナ: ubi9/go-toolset (1.23)
  • コンテナ: ubi9 (latest)

Kubernetesクラスタ構成

以下の前回の記事で構築済みの環境を利用する。

test_apps1.png

network1.png

リモートデバッグ接続

VMWare WorkstationのホストOS (Windows11)上のVisual Studio Code (Remote Development/Remote - SSH拡張)から作業する。そこより各ワーカーノード上のGoアプリケーションPod上のDelveへ(Kubernetes ServiceのVIP経由で)アクセスしリモートデバッグする。

利用するテストアプリケーション

テストアプリ名 エンドポイント API 説明
k8s-test-app 前回作成したテストアプリケーション。単純なAPIをエキスポートする
hxxps://k8s-test-app.test.k8s.local GET /ping ping要求に対してpong応答(json)を返すAPI

k8s-test-appの構成定義

項目 タイプ Volumeマウント先
TLS: サーバ証明書 Secret /app/certs/tls.crt
TLS: サーバ秘密鍵 Secret /app/certs/tls.key
API-KEY Secret /app/secret/testapp-secret.yaml
アクセスログ PersistentVolumeClaim
(Ceph File System)
/mnt/cephfs/k8s-test-app/log/history.log

開発VMのディレクトリ構成 - k8s-test-appアプリケーション

前回の記事で作成したk8s-test-appアプリを対象にしてみる。mng.test.k8s.local(RHEL9.5)で作業する。

開発ディレクトリツリー構成
/home/hoge/k8s-test-app/
├── Containerfile                 # 通常ビルド用
├── Containerfile.dlv             # デバッグビルド用
│
├── main.go                       # Goソースコード
├── test-app                      # Exeファイル
├── go.mod
├── go.sum
│
├── openssl.cnf # TLS証明書設定 (OpenSSL)
│
├── k8s-test-app-deploy.yaml      # kubernetes: Deployment定義
├── k8s-test-app-deploy-dlv.yaml  # kubernetes: Deployment定義 (デバッグ用)
├── k8s-test-app-svc.yaml         # kubernetes: Service定義
├── k8s-test-app-svc-dlv.yaml     # kubernetes: Service定義 (デバッグ用)
├── k8s-test-app-api-key.yaml     # kubernetes: Secret定義
├── k8s-test-app-tls-secret.yaml  # kubernetes: Secret定義
│
└── podman # Podmanランタイム用のローカルボリューム
    │
    ├── certs/                  # TLS証明書(読み取り専用でマウント)
    │   ├── tls.crt             # サーバ証明書(PEM形式)
    │   └── tls.key             # サーバ秘密鍵(PEM形式)
    │
    ├── secret/                 # 認証用APIキー(読み取り専用でマウント
    │   └── testapp-secret.yaml # APIキー定義ファイル
    │
    └── cephfs/
        └── k8s-test-app/
            └── log/ # アクセスログ保存先(書き込み可能でマウント)

準備: ローカルでデバッグ - Podman runtime

作成したGoアプリケーションのコンテナイメージをPodmanによりビルドおよびそれをローカルのコンテナランタイムで起動し、リモート接続でデバッガーを利用して作業してみる。

リモートからデバッグするためにDelveを同梱したデバッグ用のビルドイメージを用意する。

Contanerfile.dlv
#
# Stage 1: Build
#
FROM registry.access.redhat.com/ubi9/go-toolset:1.23 AS builder

USER root
WORKDIR /app

# ビルドに必要な依存モジュールを取得
COPY go.mod go.sum ./
RUN go mod download

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

# Delveをビルド環境へインストール
RUN go install github.com/go-delve/delve/cmd/dlv@latest
# RUN find / -print |grep dlv
# RUN echo "==== /usr/bin/dlv ====" && ls -l /usr/bin/dlv

# デバッグ用にアプリケーションをビルド
RUN go build -gcflags="all=-N -l" -o app .

#
# Stage 2: Runtime
#
FROM registry.access.redhat.com/ubi9

# トラブルシューティング用ツールを入れておく
RUN dnf install -y \
    shadow-utils \
    ca-certificates \
    procps-ng \
    iproute \
    iputils \
    vim \
    bind-utils \
    tcpdump \
    nc \
    jq \
    && dnf clean all

WORKDIR /app

# ビルドしたアプリケーション実行ファイルをコピー
COPY --from=builder /app/app .

# Delve実行ファイルをコピー
COPY --from=builder /usr/bin/dlv /usr/local/bin/dlv

EXPOSE 8443 # API用ポート (/ping)
EXPOSE 2345 # Delveポート

# Delveを起動
CMD ["dlv", "exec", "/app/app", "--headless", "--listen=:2345", "--api-version=2", "--accept-multiclient"]
デバッグ用イメージをビルド
[mng k8s-test-app]$ podman build -t k8s-test-app:debug -f Containerfile.dlv .

[mng k8s-test-app]$ podman images
REPOSITORY                                         TAG         IMAGE ID      CREATED            SIZE
...
localhost/k8s-test-app                             debug       71dad901fadf  3 hours ago        320 MB
...

※ イメージ名に"debug"タグをつけて区別するようにする。

デバッグ用イメージでコンテナを起動
[mng k8s-test-app]$ podman run -it -p 8443:8443 -p 2345:2345 -v /home/hoge/k8s-test-app/podman/certs:/app/certs:ro -v /home/hoge/k8s-test-app/podman/secret:/app/secret:ro -v /home/hoge/k8s-test-app/podman/cephfs/k8s-test-app/log:/mnt/cephfs/k8s-test-app/log k8s-test-app:debug
API server listening at: [::]:2345
2025-05-05T10:43:56Z warn layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)

VSCodeからの接続待ち...
接続前の状態を確認
[mng k8s-test-app]$ podman ps
CONTAINER ID  IMAGE                         COMMAND               CREATED        STATUS        PORTS                                                               NAMES
8292797caf0c  localhost/k8s-test-app:debug  dlv exec /app/app...  2 minutes ago  Up 2 minutes  0.0.0.0:2345->2345/tcp, 0.0.0.0:8443->8443/tcp, 2345/tcp, 8443/tcp  lucid_swartz

# リモートからコンテナへシェルで接続
[mng k8s-test-app]$ podman exec -it lucid_swartz /bin/bash
[root@8292797caf0c app]#

[root@8292797caf0c app]# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.1  2.9 6104168 52428 pts/0   Ssl  10:43   0:00 dlv exec /app/app --headless --listen=:2345 --api-version=2 --accept-multiclient
...
root          14  0.0  0.0  10536   128 pts/0    t+   10:43   0:00 /app/app
...

# まだAPI用のポート(8443)がオープンでない = アプリケーションが開始していない
[root@8292797caf0c app]# ss -lnt
State   Recv-Q   Send-Q    Local Address:Port   Peer Address:Port    Process
LISTEN  0        128       *:2345               *:*
VSCodeのlaunch.jsonを設定
{
    "version": "0.2.0",
    "configurations": [    
        {
            "name": "Remote Attach to dlv on Podman",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "host": "127.0.0.1",  # コンテナ側のIP
            "port": 2345,         # コンテナ側のDelveポート
            "cwd": "/home/hoge/k8s-test-app", # ソースコードのディレクトリ (ホスト側)
            "remotePath": "/app", # コンテナ側の実行ファイルのディレクトリ
            "apiVersion": 2,
            "trace": "verbose", # DelveとVSCode間の通信ログを表示
            "showLog": true     # デバッグアダプタのログ出力を有効化        
        }
    ]
}

デバッガーを開始しアプリケーションの起動部分(main関数)でブレークポイント設定してみる。

delve_vscode1.png

アプリケーション起動確認
[root@8292797caf0c app]# ss -lnt
State   Recv-Q   Send-Q    Local Address:Port   Peer Address:Port    Process
LISTEN  0        128       *:8443               *:* # API用のポート(8443)がオープン
LISTEN  0        128       *:2345               *:*
API呼び出し
[mng k8s-test-app]$ curl -k -H 'Authorization: TestApp my-super-secret-key' https://localhost:8443/ping | jq .
{
  "message": "pong"
}
コンテナ実行画面
[mng k8s-test-app]$ podman run -it   -p 8443:8443   -p 2345:2345   -v /home/hoge/k8s-test-app/podman/certs:/app/certs:ro   -v /home/hoge/k8s-test-app/podman/secret:/app/secret:ro   -v /home/hoge/k8s-test-app/podman/cephfs/k8s-test-app/log:/mnt/cephfs/k8s-test-app/log   k8s-test-app:debug
API server listening at: [::]:2345
2025-05-05T10:43:56Z warn layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
2025/05/05 10:55:18 INFO: API key successfully loaded
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.handlePing (4 handlers)
2025/05/05 10:59:47 INFO: Starting HTTPS server on :8443
2025/05/05 11:00:09 INFO: /ping request
[GIN] 2025/05/05 - 11:01:08 | 200 | 58.801611862s |       127.0.0.1 | GET      "/ping" ★

API処理部分でブレークポイント設定してみる。

delve_vscode2.png

コンテナ停止とクリーンアップ
[mng k8s-test-app]$ podman stop lucid_swartz
[mng k8s-test-app]$ podman rm lucid_swartz

Kubernetesクラスタ内のPodをリモートデバッグ

プライベートレジストリ (Harbor) へPush

コンテナイメージにdebugタグ付け
[mng k8s-test-app]$ podman tag k8s-test-app:debug harbor.test.k8s.local/k8s-test-app/k8s-test-app:debug
ログイン
[mng k8s-test-app]$ podman login harbor.test.k8s.local
Username: admin
Password:
Login Succeeded!
Push
[mng k8s-test-app]$ podman push k8s-test-app:debug harbor.test.k8s.local/k8s-test-app/k8s-test-app:debug

Kubernetesクラスタへデバッグ用コンテナイメージで再ディプロイ

k8s-test-app-deploy-dlv.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-test-app
  labels:
    app: k8s-test-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: k8s-test-app
  template:
    metadata:
      labels:
        app: k8s-test-app
    spec:
      imagePullSecrets:
        - name: harbor-creds
      initContainers:
        - name: k8s-test-app-init
          image: busybox
          command:
            - sh
            - -c
            - |
              echo "[INIT] Creating log directory...";
              if mkdir -p /mnt/cephfs/k8s-test-app/log; then
                chmod -R 777 /mnt/cephfs/k8s-test-app;
              else
                echo "[ERROR] Failed to create /mnt/cephfs/k8s-test-app/log" >&2;
                exit 1;
              fi
          volumeMounts:
            - name: history-vol
              mountPath: /mnt/cephfs
      containers:
        - name: k8s-test-app
          image: harbor.test.k8s.local/k8s-test-app/k8s-test-app:debug # ★debug用のイメージを指定
          imagePullPolicy: Always
          ports:
            - containerPort: 8443
            - containerPort: 2345 # ★Delveポートを追加
          volumeMounts:
            - name: certs
              mountPath: /app/certs
              readOnly: true
            - name: api-key
              mountPath: /app/secret
              readOnly: true
            - name: history-vol
              mountPath: /mnt/cephfs
      volumes:
        - name: certs
          secret:
            secretName: k8s-test-app-tls
        - name: api-key
          secret:
            secretName: k8s-test-app-api-key
        - name: history-vol
          persistentVolumeClaim:
            claimName: cephfs-pvc
適用
[mng k8s-test-app]$ kubectl apply -f k8s-test-app-deploy-dlv.yaml
確認
[mng k8s-test-app]$ k8s-test-app]$ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
NAME                             READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
k8s-test-app-789d7fc76f-2t47l    1/1     Running   0          97s   172.20.194.71    k8s-worker1   <none>           <none>
k8s-test-app-789d7fc76f-j9d8z    1/1     Running   0          97s   172.23.229.146   k8s-worker0   <none>           <none>
k8s-test-app-789d7fc76f-jlhlm    1/1     Running   0          98s   172.30.126.31    k8s-worker2   <none>           <none>
...

Serviceをデバッグ用に更新

k8s-test-app-deploy-dlv.yaml
apiVersion: v1
kind: Service
metadata:
  name: k8s-test-app
  labels:
    app: k8s-test-app
spec:
  type: LoadBalancer
  selector:
    app: k8s-test-app
  ports:
    - name: test-app
      protocol: TCP
      port: 443
      targetPort: 8443

    - name: dlv # ★Delveポート定義を追加
      protocol: TCP
      port: 2345
      targetPort: 2345
適用
[mng k8s-test-app]$ kubectl apply -f k8s-test-app-svc-dlv.yaml
確認
[mng k8s-test-app]$ kubectl get svc -o wide
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                        AGE    SELECTOR
k8s-test-app     LoadBalancer   10.100.133.21   10.0.0.64     443:32278/TCP,2345:31387/TCP   2d9h   app=k8s-test-app
...

※ ポート2345が追加されている。

APIアクセス実行 (デバッグ開始前)
[mng k8s-test-app]$ curl --cacert ../ceph-test/tls/private_ca.crt -H 'Authorization: TestApp a9b2F0c8d1E7f6A4b3C5d2E8f7A5b4C6' https://k8
s-test-app.test.k8s.local/ping | jq .

* Failed to connect to k8s-test-app.test.k8s.local port 443: Connection refused

※ デバッガーでアプリケーションを開始していないので接続エラー。

Visual Studio Codeからリモートデバッグを実行

VSCodeのlaunch.jsonを設定
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [    
        {
            "name": "Remote Attach to dlv on k8s - k8s-test-app",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "host": "k8s-test-app.test.k8s.local", # Goアプリケーションへの接続ホスト名 (VIP)
            "port": 2345,         # コンテナ側のDelveポート
            "cwd": "/home/hoge/k8s-test-app", # ソースコードのディレクトリ (ホスト側)
            "remotePath": "/app", # コンテナ側の実行ファイルのディレクトリ
            "apiVersion": 2,
            "trace": "verbose", # DelveとVSCode間の通信ログを表示
            "showLog": true     # デバッグアダプタのログ出力を有効化        
        }
    ]
}

VSCodeからデバッグを開始しアプリケーションのソースコードにブレークポイント設定してみる。

リモートからコンテナへシェルで接続し状態を確認
[mng k8s-test-app]$ kubectl exec -it k8s-test-app-789d7fc76f-2t47l -- bash
Defaulted container "k8s-test-app" out of: k8s-test-app, k8s-test-app-init (init)

[root@k8s-test-app-789d7fc76f-2t47l app]# ps ax
    PID TTY      STAT   TIME COMMAND
      1 ?        Ssl    0:00 dlv exec /app/app --headless --listen=:2345 --api-version=2 --accept-multiclient
      7 ?        Ssl    0:00 /usr/local/bin/dlv ** telemetry **
      8 ?        Sl     0:00 /app/app
     51 pts/0    Ss     0:00 bash
     62 pts/0    R+     0:00 ps ax

[root@k8s-test-app-789d7fc76f-2t47l app]# ss -lnt
State   Recv-Q   Send-Q    Local Address:Port   Peer Address:Port    Process
LISTEN  0        128       *:8443               *:* # API用のポート(8443)がオープン
LISTEN  0        128       *:2345               *:*
API呼び出し

# デバッガ接続したPodに分散された場合には成功
[mng k8s-test-app]$ curl --cacert ../tls/private_ca.crt -H 'Authorization: TestApp a9b2F0c8d1E7f6A4b3C5d2E8f7A5b4C6' https://k8
s-test-app.test.k8s.local/ping | jq .
{
  "message": "pong"
}

# デバッガ未接続のPodに分散された場合には接続エラー
[mng k8s-test-app]$ curl --cacert ../tls/private_ca.crt -H 'Authorization: TestApp a9b2F0c8d1E7f6A4b3C5d2E8f7A5b4C6' https://k8s-test-app.test.k8s.local/ping | jq .

curl: (7) Failed to connect to k8s-test-app.test.k8s.local port 443: Connection refused
Pod側のログを確認

[mng k8s-test-app]$ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
k8s-test-app-789d7fc76f-2t47l    1/1     Running   0          61m
...

[mng k8s-test-app]$ kubectl logs k8s-test-app-789d7fc76f-2t47l
Defaulted container "k8s-test-app" out of: k8s-test-app, k8s-test-app-init (init)

API server listening at: [::]:2345 ★デバッガーでアプリケーション開始
2025-05-05T13:14:44Z warn layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
2025/05/05 13:20:10 INFO: API key successfully loaded
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.handlePing (4 handlers)
2025/05/05 13:20:19 INFO: Starting HTTPS server on :8443
2025/05/05 13:23:36 INFO: /ping request
[GIN] 2025/05/05 - 13:23:43 | 200 |  7.461114369s |      10.0.0.157 | GET      "/ping" ★OK
2025/05/05 13:24:26 INFO: /ping request
[GIN] 2025/05/05 - 13:24:29 | 200 |  3.397720163s |      10.0.0.157 | GET      "/ping"

API処理部分でブレークポイント設定している。

delve_vscode3.png

関連記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?