Javaプロセスを一定時間毎にチェックし、ハングしていればPodを再起動する仕組みの備忘録です。
Kubernetes LivenessProbeに関する詳細はこちらをご参照ください。

Java実装

監視対象クラス

テスト用に、インスタンスが生成されてから10秒後に isAlive() == falseになるように実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class SomeResource {
  final long createdTime;

  public SomeResource() {
    this.createdTime = System.currentTimeMillis();
  }
  public boolean isAlive() {
    return System.currentTimeMillis() - createdTime < 10000;
  }
}

監視用エンドポイント

SomeResource#isAlive() == trueの時はレスポンスコード 200, falseの時は 500を返すように実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;

public class HeartBeat {
  private final SomeResource target;
  private HttpServer httpServer;
  private final int port;

  public HeartBeat(SomeResource target, int port) {
    this.target = target;
    this.port = port;
  }

  void start() throws IOException {
    httpServer =
        HttpServer.create(new InetSocketAddress(port), 0);
    httpServer.createContext("/heartbeat", httpExchange -> {
      int responseCode;
      if (target.isAlive()) {
        responseCode = 200;
      } else {
        responseCode = 500;
      }
      byte[] responseBody = String.valueOf(target.isAlive()).getBytes(StandardCharsets.UTF_8);
      httpExchange.getResponseHeaders().add("Content-Type", "text/plain; charset=UTF-8");
      httpExchange.sendResponseHeaders(responseCode, responseBody.length);
      httpExchange.getResponseBody().write(responseBody);
      httpExchange.close();
    });
    httpServer.start();
  }

  void stop() {
    httpServer.stop(0);
  }
}

Mainクラス

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.concurrent.CountDownLatch;

public class Main {

  public static void main(String[] args) {
    final CountDownLatch latch = new CountDownLatch(1);
    SomeResource resource = new SomeResource();
    HeartBeat heartbeat = new HeartBeat(resource, 1234);// ポート1234番を使用

    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        heartbeat.stop();
        latch.countDown();
      }
    });

    try {
      heartbeat.start();
      latch.await();
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(-1);
    }
  }
}

curlでエンドポイントをテストする

SomeClassインスタンス生成直後

1
2
3
4
5
6
7
$ curl --dump-header - http://localhost:1234/heartbeat
HTTP/1.1 200 OK
Date: Tue, 02 Jul 2019 07:24:20 GMT
Content-type: text/plain; charset=UTF-8
Content-length: 4

true

10秒後

1
2
3
4
5
6
7
$ curl --dump-header - http://localhost:1234/heartbeat
HTTP/1.1 500 Internal Server Error
Date: Tue, 02 Jul 2019 07:24:30 GMT
Content-type: text/plain; charset=UTF-8
Content-length: 5

false

Kubernetesにデプロイ

コンテナの作成、レジストリへのpush手順は省略します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-resource-watch-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-resource-watch-test
  template:
    metadata:
      labels:
        app: java-resource-watch-test
    spec:
      containers:
      - name: java-resource-watch-test
        image: some_registry/test:latest
        livenessProbe:
          httpGet:
            path: /heartbeat
            port: 8080
            httpHeaders:
            - name: Custom-Header
              value: HealthCheck
          initialDelaySeconds: 1
          periodSeconds: 1

kubectl apply -f ./deployment.yaml

SomeResourceの寿命が切れると同時にPodが終了し、再起動される事が確認できるかと思います。