Spring BootのHelloWorld APIをTektonのPipelineでデプロイ

Tektonは、Kubernetes上で動作する、強力で柔軟なオープンソースのCI/CDフレームワークです。Kubernetesのカスタムリソース定義 (CRD) を利用して、パイプラインを定義し、実行します。Tektonは、クラウドネイティブなアプリケーション開発とデプロイメントを促進するために設計されており、コンテナイメージのビルド、テスト、デプロイメントなどのタスクを自動化できます。

Spring BootのHelloWorld APIをTektonのPipelineでデプロイする方法を説明します。この例では、以下のステップを実行します。

### 1. Spring Boot HelloWorld APIの作成:

[Spring Initializr](https://start.spring.io/) を使用して、シンプルなHelloWorld APIを作成します。

- Dependencies: Spring Web を選択
- Group: com.example
- Artifact: hello-world

src/main/java/com/example/helloworld/HelloWorldApplication.java を編集して、RestControllerを追加します。
```java
package com.example.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HelloWorldApplication {

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

    @GetMapping("/")
    public String hello() {
        return "Hello, World!";
    }
}
```
### 2. Dockerfileの作成:

- プロジェクトのルートディレクトリに Dockerfile を作成します。
```dockerfile
FROM openjdk:17-jdk-slim
COPY target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
```
### 3. Kubernetesマニフェストの作成:

- k8s/deployment.yaml を作成します。
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-world
        image: <YOUR_DOCKER_REGISTRY>/hello-world:latest  # イメージ名は後で置き換えます
        ports:
        - containerPort: 8080
```
- k8s/service.yaml を作成します。
```yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  selector:
    app: hello-world
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer
```

### 4. Gitリポジトリにプッシュ:

- 作成したコード、Dockerfile、KubernetesマニフェストをGitリポジトリにプッシュします。

### 5. Tekton Taskの定義:

- GitリポジトリからコードをチェックアウトするTask (git-clone.yaml):
```yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: git-clone
spec:
  params:
    - name: url
      type: string
      description: The git repository url to clone from
    - name: revision
      type: string
      description: The git revision to checkout (branch, tag, sha, ref…)
      default: "main"
    - name: subdirectory
      type: string
      description: The subdirectory inside the git repository
      default: ""
    - name: deleteExisting
      type: string
      description: Clean out the contents of the volume if it exists
      default: "true"
  workspaces:
    - name: output
      description: The workspace consisting of the cloned repo.
  steps:
    - name: clone
      image: alpine/git:v1.17
      workingDir: $(workspaces.output.path)
      securityContext:
        runAsUser: 0
      script: |
        #!/usr/bin/env sh
        set -eux
        REPO_URI="${inputs.params.url}"
        CHECKOUT_REV="${inputs.params.revision}"
        SUB_PATH="${inputs.params.subdirectory}"
        if [ "${inputs.params.deleteExisting}" = "true" ] ; then
          rm -rf /tekton/home/*
        fi
        git init .
        git remote add origin "${REPO_URI}"
        git fetch --depth 1 origin "${CHECKOUT_REV}"
        git checkout FETCH_HEAD
        if [ ! -z "${SUB_PATH}" -a "${SUB_PATH}" != "." ] ; then
          mkdir -p "${SUB_PATH}"
          rsync -a ./ "${SUB_PATH}"
        fi
        ls -lR
```
- DockerイメージをビルドするTask (build-push.yaml):
```yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-push
spec:
  params:
    - name: IMAGE
      type: string
      description: The name of the image to build
    - name: DOCKERFILE
      type: string
      description: Path to the Dockerfile
      default: Dockerfile
    - name: CONTEXT
      type: string
      description: Path to the build context
      default: .
  workspaces:
    - name: source
      description: Directory containing the source code
  steps:
    - name: build-and-push
      image: docker:20.10.7-dind
      securityContext:
        privileged: true
      workingDir: $(workspaces.source.path)
      script: |
        #!/usr/bin/env sh
        set -eux
        dockerd-entrypoint.sh &
        sleep 10
        docker build -t $(inputs.params.IMAGE) -f $(inputs.params.DOCKERFILE) $(inputs.params.CONTEXT)
        docker push $(inputs.params.IMAGE)
```
- KubernetesにデプロイするTask (kubectl-apply.yaml):
```yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: kubectl-apply
spec:
  params:
    - name: manifest_dir
      type: string
      description: The directory containing the Kubernetes manifests
  workspaces:
    - name: source
      description: Directory containing the source code
  steps:
    - name: apply
      image: bitnami/kubectl:latest
      workingDir: $(workspaces.source.path)
      script: |
        #!/usr/bin/env sh
        set -eux
        kubectl apply -f $(inputs.params.manifest_dir)
```
### 6. Tekton Pipelineの定義 (pipeline.yaml):
```yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: hello-world-pipeline
spec:
  params:
    - name: git-url
      type: string
      description: The git repository url to clone from
    - name: image-name
      type: string
      description: The name of the image to build
  workspaces:
    - name: shared-workspace
  tasks:
    - name: fetch-source
      taskRef:
        name: git-clone
      workspaces:
        - name: output
          workspace: shared-workspace
      params:
        - name: url
          value: $(params.git-url)
    - name: build-and-push
      taskRef:
        name: build-push
      workspaces:
        - name: source
          workspace: shared-workspace
      params:
        - name: IMAGE
          value: $(params.image-name)
      runAfter:
        - fetch-source
    - name: deploy
      taskRef:
        name: kubectl-apply
      workspaces:
        - name: source
          workspace: shared-workspace
      params:
        - name: manifest_dir
          value: k8s
      runAfter:
        - build-and-push
```
### 7. Tekton PipelineRunの定義 (pipelinerun.yaml):
```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: hello-world-pipelinerun
spec:
  pipelineRef:
    name: hello-world-pipeline
  params:
    - name: git-url
      value: <YOUR_GIT_REPOSITORY_URL>  # GitリポジトリのURLに置き換えます
    - name: image-name
      value: <YOUR_DOCKER_REGISTRY>/hello-world:latest  # Dockerレジストリとイメージ名に置き換えます
  workspaces:
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
```
### 手順:
### 1.Taskの適用:
```console
kubectl apply -f git-clone.yaml
kubectl apply -f build-push.yaml
kubectl apply -f kubectl-apply.yaml
```
### 2.Pipelineの適用:
```console
kubectl apply -f pipeline.yaml
```
### 3.PipelineRunの適用:

- <YOUR_GIT_REPOSITORY_URL> と <YOUR_DOCKER_REGISTRY>/hello-world:latest を適切な値に置き換えてください。
```console
kubectl apply -f pipelinerun.yaml
```
### 4.PipelineRunの監視:
```console
kubectl get pipelinerun hello-world-pipelinerun -w
```
### 補足:

- Dockerレジストリ: Dockerイメージをプッシュするためには、Dockerレジストリが必要です。Docker Hub、Google Container Registry、Amazon ECRなどを使用できます。
- Kubernetesクラスタ: Kubernetesクラスタが必要です。Minikube、Kind、GKE、EKS、AKSなどを使用できます。
- kubectl: Kubernetesクラスタにアクセスするために、kubectlコマンドラインツールが必要です。
- Tekton Dashboard: Tekton Dashboardをインストールすると、パイプラインの実行状況をGUIで確認できます。
- Workspace: Task間でデータを共有するために、Workspaceを使用しています。この例では、shared-workspaceという名前のWorkspaceを定義し、各Taskで共有しています。
- Service Account: Kubernetesクラスタにアクセスするために、適切な権限を持つService Accountが必要です。必要に応じて、Service Accountを作成し、PipelineRunに紐付けてください。
- Docker in Docker: Dockerイメージをビルドするために、build-push Taskでは、Docker in Docker (dind) を使用しています。securityContext: privileged: true を設定する必要があります。
この例は、基本的なSpring Boot HelloWorld APIをTektonでデプロイする方法を示しています。必要に応じて、TaskやPipelineをカスタマイズし、より複雑なCI/CDパイプラインを構築できます。