TektonでCI/CDパイプラインを手の内化しよう

この記事は NTTコミュニケーションズ [Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nttcom) の4日目です。
昨日は @diesekiefer さんの[オーディオファイル コーデックまとめ](https://qiita.com/diesekiefer/items/e4b35643d6e5292b067b) でした。
まだCDが主流だった頃，買ってきたCDをPCに取り込んでた時を思い出して少し懐かしくなりました。
※ちなみに本記事で紹介するCDはCompact Diskのことではありません(・∀・)

# はじめに

## 今回紹介したいこと

早いもので昨年アドベントカレンダーで書かせていただいた「[DevOpsってこんな仕事！考え方とスキルセットのまとめ](https://qiita.com/TAR_O_RIN/items/840340d12b5c135f04e8)」から1年が経ちました。今年も1年を通して多くの時間をDevOps, CI/CD, Infrastructure as Codeに費やしたので，簡単な振り返りと，次のステップに関連した「Tekton」というKubernetes Native CI/CDフレームワークをご紹介しようと思います。

## 1年間 DevOpsへの取り組みを振り返って
#### DevOpsやSREといった取り組みはビジネスレイヤに訴求しやすい
 - 「現行サービスのリリースサイクルどのくらいですか？」の一言で定量的に必要性を進言できる。
 - 成果もリリースサイクルという形でサービスの競争力となり定量的に見える。
 - Cloud NativeやDevOps，SREが何のためにあるのか分かりやすく実践して伝えることができる。
 

#### CI/CDプロセスの品質はサービスの品質に直結する
 - 良質なCI/CDプロセスは提供しているサービス，会社そのものの競争力の源泉になる。
 - それ即ち差別化のポイントになりうる。
 - その評価指標はサービスのリリースサイクルに帰結するべき。


#### パブリッククラウドが強すぎて仕事の内容が変わりつつある
 - これまで一生懸命取り組んできた泥臭い仕事が殆ど不要になる。
 - サーバのOS管理，脆弱性対応，VMのプロビジョニング すべてオンプレミスでやるより速くて確実。
 - パブリッククラウドの使い方を知っていればインフラが作れてしまい，本質的な部分を知らなくても動く。
 - 極めつけはサーバレス。最高にCool。（ISPの立場からするとIaaSが出た時点でネットワークレスだった）


## 振り返りを基に次のステップを踏む

これまでCI/CDパイプラインを作り，その改良を続けてきました。たくさんのSaaSやOSSを触ってきました。Jenkinsが好きだった時代も一瞬ありました。その一方で下記のような学びと問題に直面しました。

### CIプロセス
- SaaS強い。CircleCI，Travis CI，GitHub Actions これらは確実にCIのあるべき姿を教えてくれた。
    - 最近 [GitHub Actions Meetup Tokyo β](https://gaugt.connpass.com/event/147220/) にも参加させて頂き勉強させていただいていますm(_ _)m
- Google Cloud Buildのシンプルで，Containerを組み合わせてワークフローを作るアプローチは素晴らしかった。
- CIサービスのビジネスモデルに起因するキャッシュやアーティファクト(生成物)の扱いに対する制限。
- 現実のCIサイクルはもっと複雑で，コンポーネントA,B,CをそれぞれX,Y,Zのバージョンで同時に動かすとどうか，のような確認も必要。

### CDプロセス
- 純粋にKubernetesだけを見てデプロイメントしても上手くいかないフェーズまで来た。
    - GCPのNEG(NetworkEndpointGroup)に代表されるようなパブリッククラウドとKubernetesの密な結合と最適化。
    - 断無くアプリケーションをデプロイするには挙動を理解した上で細かいロジックを組み入れる必要があった。
    - 恐らくそれはパブリッククラウド毎に異なり，デリバリープロセスのチューニングの範疇。
    - 世の中を見ると同じ問題に当たっている。[ZOZOテクノロジーさんの例。](https://techblog.zozo.com/entry/cloudnext19tokyo-imagesearch)
- デリバリープロセスの中に生じるアプリケーションや社内コンテキスト起因による独自ロジックの要求。
    - プロセスAとプロセスBの間には人間の判断を挟もう。
    - コンポーネントA,B,Cはこの順番でバージョンを上げて欲しい。

最初はCI/CDをやってくれるSaaSやOSSはキラキラして見えた。よく分からないけど，動かしたら自動でやってくれる。しかし，ある程度の期間取り組んでいるとロジックはもう学んだ，何をすれば良いのかももう分かる。結局必要なのはワークフローエンジンだけなのでは？これまでの学びからもっとプリミティブなワークフローエンジンを使ってCI/CDプロセスを作ることに挑戦しようという結論に至りました。


# なぜTekton
ここまで少し長かったですが，このような背景でTektonという選択肢が生まれました。カンファレンスでTektonは「Cloud Native CI/CD framework」と言われていますが，私は「Container based workflow engine」という捉え方をしています。CI/CDはそのワークフローの1つです。何がどのように出来るのか分かりやすく示すと

- 実行ワークロードはKubernetesを利用する。
- Tekton自体はKubernetes CRDsとControllerで実装されている。
- 処理(TektonではStepと呼びます)の最小単位をContainerで定義することが出来る。(Run on K8s)
    - 世界に公開されているContainerを利用する事ができる
    - Containerなので自分で処理を作ることも出来る
    - **実装上も[Kubernetes Container Spec](https://github.com/tektoncd/pipeline/blob/master/pkg/apis/pipeline/v1alpha1/task_types.go#L67)と言い換えて良い**
- 複数のContainerをシーケンシャルに実行することで一連の処理(TektonではTaskと呼びます)を実現する
    - Kubernetes Podの中にあるContainersに実行順序を定義できる
    - リソース定義としてはTaskがKubernetes Container Specを複数持てる (下記のサンプルを参照)
    - 同一Podのため**Container間でDiskなどのリソースはシェアできる**
- 一連の処理の順序関係を指定したり，パラレルに実行する(TektonではPipelineと呼びます)
    - **Kubernetes Podの実行順序や同時実行を定義できる**
    - 処理のFanIn/Outを実現できる

実際のKubernetesリソース定義があった方が分かりやすいと思うので[IBMさんのチュートリアル](https://github.com/IBM/tekton-tutorial/blob/master/tekton/tasks/deploy-using-kubectl.yaml)からTaskの例を下記に参照させて頂きました。StepSに複数処理(Kubernetes Container Spec)を定義しているのがわかります。これらは単一のPodとしてデプロイされ，中のContainerは下記の順序を守って実行されます。

```yaml:task.yaml

apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: deploy-using-kubectl
spec:
  inputs:
    resources:
      - name: git-source
        type: git
    params:
      - name: pathToYamlFile
        description: The path to the yaml file to deploy within the git source
      - name: imageUrl
        description: Url of image repository
      - name: imageTag
        description: Tag of the images to be used.
        default: "latest"
  steps:
    - name: update-yaml
      image: alpine
      command: ["sed"]
      args:
        - "-i"
        - "-e"
        - "s;__IMAGE__;${inputs.params.imageUrl}:${inputs.params.imageTag};g"
        - "/workspace/git-source/${inputs.params.pathToYamlFile}"
    - name: run-kubectl
      image: lachlanevenson/k8s-kubectl
      command: ["kubectl"]
      args:
        - "apply"
        - "-f"
        - "/workspace/git-source/${inputs.params.pathToYamlFile}"
```

ココまで読んで「何でも出来るやん」と思った人はナカーマ(・∀・) はい、[入り口](https://github.com/tektoncd/pipeline/blob/master/docs/install.md)。
※ 今回の記事はv0.8.0-releaseを利用して動作を確認しています。

### GitHub Actionsとの用語比較
参考までにTRIGGERMESHさんが出してくれているGitHub Actionsとの用語比較を載せておきます。既に利用経験のある方は用語のマッピングがしやすいかと思います。
![github actions terminology mapping.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216192/7e58eb53-08ec-c280-1ebc-9b5278699e03.png)


## DeepDive: Tektonが自動で提供してくれる共有Volume
Task内部のStep間では共有ディレクトリを持つことが出来る。また，ContainerのデフォルトworkingDirも/workspaceに設定される。emptyDir，downwardAPIを用いた下記のディレクトリ，ファイルが全てのStep間で自動共有される。

上の2つはTektonが利用しているため，TektonのStep間でArtifacts(生成物)を共有する場合は下の2つのディレクトリを利用することをおすすめします。

- /builder/tools
- /builder/downward
- /workspace
- /builder/home


TaskによってインサートされるPodのVolumes

```
  volumes:
  - emptyDir: {}
    name: tools
  - downwardAPI:
      defaultMode: 420
      items:
      - fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['tekton.dev/ready']
        path: ready
    name: downward
  - emptyDir: {}
    name: workspace
  - emptyDir: {}
    name: home
```

TaskによってインサートされるPodのMountVolumes

```
    volumeMounts:
    - mountPath: /builder/tools
      name: tools
    - mountPath: /builder/downward
      name: downward
    - mountPath: /workspace
      name: workspace
    - mountPath: /builder/home
      name: home
    workingDir: /workspace

```

## DeepDive: TektonのContainer実行順序制御

Task(≒Pod)でStep(≒Container)は複数個定義することが出来る。通常Pod内のContainer(Sidecar含む)は同時に起動して処理が始まってしまうが，TektonではTaskからPodを生成する時に独自EntrypointをKubernetes Pod Specに差し込むことで実行順序を制御している。

全てのStepは起動時に全て同時に起動(ContainerのPull等は並列に実施する)するが, 上記のEntrypointによって本来のCommandの実行が待たされる仕組み。

下記にある通りwait_fileとpost_fileというオプションを使って前のStepが終了したら，Pod内で共有されているVolumeを指定場所として「ファイルを作成」することでStepの状況を共有している。/builder/downward/readyというの最初のStep用のTaskがReadyになったことを示す値。尚，失敗の時はファイル名の末尾に「.err」を付けることで次のStep以降をスキップさせる仕様。Tektonの[Entrypointに対するREADME](https://github.com/tektoncd/pipeline/tree/2cef29e98b5db8f2667f6851ef85b1a794cce453/cmd/entrypoint)として存在するよく分かる解説。

<img width="600" alt="TektonInsertEntrypoint.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216192/acc996da-99fb-2c8d-0ff9-8049bb09b941.png">


# 最後に

Tektonはまだv1.0(2019年12月4日現在)になっておらず，APIもアルファバージョンで日々コードが動いています。Knativeを始めとしたKubernetesのベストプラクティスが集まってくる場所なので学ぶことも多いためこれからが楽しみです。読者の方から一人でもKubernetesやTekton，CI/CD，DevOpsに興味を持ち，学んだことや気付きがあったならば幸いです。

今回の記事にご興味ある学生の方は[今年の採用ページ](https://www.ntt.com/about-us/recruit/event/techWorkshop.html)を見て頂けると嬉しいです(｀･ω･´)ゞ

明日は @negi111111 さんの記事です！お楽しみに！

---

参考になった講演一覧

- KubeCon2019 Euro: Modern CI/CD with Tekton and Prow Automated via Jenkins X - James Rawlings, Cloudbees
    - https://kccnceu19.sched.com/event/MPZ4
- Next Generation CI/CD with GKE and Tekton (Cloud Next '19)
    - https://www.youtube.com/watch?time_continue=1&v=TQJ_pdTxZr0
- CDF: Tekton Project Showcase - Christie Wilson, Jason Hall 
    - https://www.youtube.com/watch?v=V0LpYdnTpsg