概要

KubernetesのAPIを呼び出すJavaのラッパーライブラリが実は公式で用意されており、Javaアプリケーションから比較的簡単にKubernetesの操作を行うことができる。

今回はJobを起動するREST APIをSpring Bootで作成してみる。

環境

  • Java 8
  • Spring Boot 1.5.10.RELEASE
  • Kubernetes 1.8

今回のアプリの仕様

  1. 公式ドキュメントにあるpiを算出するJobサンプルを実行する
  2. Jobの定義はYAMLファイルから読み込む
    • client-javaはJSON(GSON)を想定しているので、YAML->JSON変換してバインドする
  3. Job名は一意になるように日付を付加する
  4. 認証はサービスアカウントのkubernetes configを使う

実装

プロジェクトの作成

curl -s https://start.spring.io/starter.tgz \
    -d baseDir=k8s-job-sample \
    -d type=gradle-project \
    -d javaVersion=1.8 \
    -d dependencies=web,lombok | tar -xzvf -

build.gradleに依存を追加

compile('io.kubernetes:client-java:1.0.0-beta2')

Job manifestを設置

src/main/resources/job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(100)"]
      restartPolicy: Never
  backoffLimit: 4

Bean定義

DemoApplication.java
package com.example.demo;

import com.google.gson.Gson;
import io.kubernetes.client.ApiClient;
import io.kubernetes.client.apis.BatchV1Api;
import io.kubernetes.client.util.Config;
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public Gson gson() {
        return new Gson();
    }

    @Bean
    public Yaml yaml() {
        return new Yaml(new SafeConstructor());
    }

    @Bean
    public ApiClient apiClient() throws IOException {
        return Config.fromConfig("/path/to/k8s-config.yaml");
    }

    @Bean
    public BatchV1Api batchV1Api(ApiClient apiClient) {
        return new BatchV1Api(apiClient);
    }

}

※簡易化のため、Applicationクラスに記述

Controller

JobController.java
package com.example.demo;

import com.google.gson.Gson;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.apis.BatchV1Api;
import io.kubernetes.client.models.V1Job;
import io.kubernetes.client.models.V1ObjectMeta;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.yaml.snakeyaml.Yaml;

@RestController
@RequestMapping("jobs")
@RequiredArgsConstructor
public class JobController {
    private final BatchV1Api batchApi;
    private final ResourceLoader resourceLoader;
    private final Gson gson;
    private final Yaml yaml;
    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    @PostMapping
    public V1Job executeJob(@RequestParam(required = false, defaultValue = "default") String nameSpace)
            throws IOException, ApiException {

        V1Job job = loadJobFromYaml("job.yaml");

        V1ObjectMeta metaData = job.getMetadata();
        metaData.setName(String.format("%s-%s", metaData.getName(), dateTimeFormatter.format(LocalDateTime.now())));

        return batchApi.createNamespacedJob(nameSpace, job, null);
    }

    private V1Job loadJobFromYaml(String fileName) throws IOException {
        Map jobManifest = (Map) yaml.load(
                resourceLoader.getResource(ResourceLoader.CLASSPATH_URL_PREFIX + fileName).getInputStream());

        return gson.fromJson(gson.toJson(jobManifest), V1Job.class);
    }
}

動作確認

$ curl -X POST http://localhost:8080/jobs?nameSpace=develop
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.