> 이 글은 [원문](https://medium.com/@vbehar/jenkins-x-pipelines-internals-part-1-from-github-webhook-event-to-tekton-pipeline-6927c6eb879c)을 번역한 글 입니다.  "새로운" [Jenkins X 파이프라인](https://jenkins-x.io/docs/concepts/jenkins-x-pipelines/)들은 (Jenkinx X 2.0 부터 적용된) 오픈소스 도구인 [Prow](https://github.com/kubernetes/test-infra/tree/master/prow)와 [Tekton](https://tekton.dev/) 으로 구성되어있다. 간단히 말하면 Prow는 Kubernetes에 내장되어 Github 이벤트에 반응하기, Job 테스트 트리거, Pull 요청에 자동으로 머지하기, Chat-Ops 식 사용자 인터페이스 등 모든 Github과 관련된 자동화 처리를 담당하고 있다. Prow는 오픈 소스 커뮤니티들 중에서 큰 곳 중 하나인 Kubdrnetes 커뮤니티에서 활발하게 테스트를 진행 하였다. 그리고 Tekton은 [Knative](https://knative.dev/) 프로젝트에서 분리된 갓 시작된 프로젝트로 Kubernetes에 파이프라인을 실행하기 위한 빌딩 블록을 제공하기 위해 설계되었다. 점점 더 많은 프로젝트들이 Tekton을 기반으로 파이프라인 시스템을 구축하기 시작하고 있으며, [Jenkins X](https://jenkins-x.io/)와 [OpenShift](https://www.okd.io/)는 이 중 가장 잘 알려진 오픈 소스들이다. [여기](https://github.com/tektoncd/friends)서 Tekton 기반으로 진행중인 프로젝트 목록을 확인할 수 있다. 본 포스팅에서는 Github webhooks 이벤트가 트리거 되었을때 어떻게 동작하는지 이해하기 위해 Prow, Tekton 그리고 Jenkins X 내부를 자세히 살펴볼 것이다. 이 내용들은 분명 쉬운 내용들은 아니지만 너무 걱정할 필요 없다. 파이프라인을 실행시키기 위해서 자세히 알 필요는 없으나 단지 궁금증을 해결 하기 위해 살펴보도록 하겠다. --- ### Initial configuration `jx import`를 실행시키면 모든 것이 시작된다. 해당 명령어를 실행할때 어떤일이 일어 나는지에 대해서는 추후 다루도록 하겠다. 지금은 해당 명령을 실행하면 Jenkins X에서 정의된 [SourceRepository](https://jenkins-x.io/docs/reference/api/#jenkins.io/v1.SourceRepository) [Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) (CRD) 을 이용해 Kubernetes 리소스들을 생성한다는 것을 알아야한다. [여기](https://github.com/jenkins-x/environment-tekton-weasel-dev/blob/v0.0.314/repositories/templates/jenkins-x.yaml)서 예시를 볼 수 있다.이 설정으로 Prow를 설정하여 `SourceRepository`에 정의된 Github repository에서 Github Webhook 이벤트를 수신 할 때 동작하는 방법을 알 수 있다. 또한 해당 repository에 새로운 WebHook을 등록하고 repository에 무슨 일이 일어날 때마다 Prow에 알리는데 사용될 것이다. ### Prow and Hook Github repository에 수정된 것을 pull 요청을 할때, Github은 repositry에 설정된 Webhook URL을 통해 Prow에 HTTP request를 보낸다. 사실 Prow는 [여러개의 컴포넌트](https://github.com/jenkins-x/test-infra/blob/master/prow/cmd/README.md)로 구성되어 있으며 Webhook 이벤트를 받는 컴포넌트를 ...`hook`이라고 부른다. Jenkins X 쿠버네티스 클러스터에 `jx` 네임스페이스 안에 deployment나 pod 리스트를 살펴보면 `hook`이라는 deployment가 띄워져 있다. 이 디플로이먼트는 Jenkins X와 [Prow 에서 for한 Jenkinx X](https://github.com/jenkins-x/test-infra/)에서 관리하는 [Prow Chart](https://github.com/jenkins-x-charts/prow)에서 생성되었다. repository 이름이 이상하게 (`test-infra`) 되어있는지 궁금할 수도 있다. Prow 프로젝트가 Kuberenetes Test Infrastructure 팀에서 개발이 진행되어 이러한 이름을 가지게 되었다. 따라서 `hook` pod은 [`hook` 명령어](https://github.com/jenkins-x/test-infra/tree/master/prow/cmd/hook)를 통해 시작되었고 Github webhook 이벤트가 발생시키는 `/hook` 엔드포인트를 대기하고 있다. Hook이 해당 이벤트를 받으면 하나 이상의 plugin들에 전파된다. Plugin들은 Prow의 내부 또는 외부가 될 수 있다. 외부 플러그인은 단지 Hook이 HTTP 요청을 보낼 HTTP 서버일 뿐이다. 내부 플러그인들은 Prow 소스 코드에 들어있으며 hook 내부에서 동작한다. 플러그인들은 Prow 설정파일인 `plugins` Kuberenetes ConfigMap에정의되어 있으며 `jx import` 명령어를 실행할 때 Jenkins X에서 자동으로 생성한다. 더 자세한건 이후 포스팅에서 다루겠다. `kubectl get configmap plugins -n jx` 명령어를 실행하면 아래와 같은 설정을 볼 수 있다. ``` yaml plugins: githubOrg/repoName: - approve - trigger [...] triggers: - repos: - githubOrg/repoName trusted_org: githubOrg ``` 해당 plugin 들 중 현재 신경써야하는 플러그인은 하나 이상의 Job을 트리거 시켜주는 [trigger](https://github.com/jenkins-x/test-infra/tree/master/prow/plugins/trigger)이다. trigger는 Jenkins X 가 자동으로 생성한 `config` ConfigMap에서 "presubmit" job을 찾는 것으로 시작한다. 아래는 해당 샘플 설정파일이다. ``` yaml presubmits: githubOrg/repoName: - agent: tekton always_run: true context: pr-build name: pr-build rerun_command: /test this trigger: (?m)^/test( all| this),?(\s+|$) ``` Prow는 2가지 종류의 job을 가지고 있다: - `presubmit`: 해당 job은 master 브랜치에 머지하기 전에 pull request 컨텍스트에 실행된다. - `postsubmit`: 해당 job은 master 브랜치에 머지할때 실행된다. postsubmit은 이 후 포스팅에서 좀 더 다루도록 하겠다. 그 후 `trigger` 플러그인은 Prow에서 정의된 `ProwJob` CRD 쿠버네티스 리소스를 생성한다. 각 presubmit은 설정파일에 정의된다. ```yaml apiVersion: prow.k8s.io/v1 kind: ProwJob metadata: name: 00bd20bf-394e-11ea-ad56-7e495e75e966 labels: prow.k8s.io/job: pr-build prow.k8s.io/refs.org: githubOrg prow.k8s.io/refs.pull: "1" prow.k8s.io/refs.repo: repoName prow.k8s.io/type: presubmit spec: agent: tekton context: pr-build job: pr-build refs: org: githubOrg pulls: - number: 1 [...] repo: repoName repo_link: https://github.com/githubOrg/repoName [...] type: presubmit ``` 위에서 볼 수 있듯이 해당 설정은 파이프라인을 실행하기 위한 모든 정보를 가지고 있다 (보기 쉽게 불필요한 정보는 생략하였다). 중요한 부분은 `tekton`이라고 설정되어있는 `agent`이다. 이 값은 Jenkins X에서 설정한 부분으로 Prow의 기본 agent는 `kubernetes`이지만 Jenkins X에서 Tekton을 이용하여 커스텀 로직을 활용하기 위해 변경하였다. ### Pipeline controllers 만약 kubernetes의 "controller"에 생소하다면, 단지 특정 이벤트를 지켜보고 반응하기 위한 것이라고 생각해도 좋다. 쉬운 예로 ReplicaSet 리소스에 정의되어 있는 replica 개수를 조절하기 위해 동작하는 ReplicaSet Controller 등이 있다. 이는 Kubernetes의 고전적인 패턴 방식으로 쿠버네티스 리소스들을 생성할 때마다 리소스가 변경된것들에 반응하기 위한 Controller들이 어딘가에 띄워져 있다고 생각하면 된다. Jenkins X 내부의 경우, Hook는 ProwJob을 생성하고 Prow는 이것에 반응하기 위한 Pipeline controller를 가지고 있다. 해당 컨트롤러는 위와 동일한 Prow Chart에서 생성된 `pipelin` deployment에서 동작하고 있다. 사실은 해당 컨트롤러는 Jenkinx X 팀에서 Prow에 contribute 한 것이다. Pipeline controller는 `pipeline` 명령어로 시작되어 모든 ProwJob 리소스들이 변경되는 것을 지켜보고 있지만 agent가 `tekton`인 ProwJob만 신경쓴다. Prowjob은 `kubernetes`, `jenkins` 나 `knative-build`같은 다른 agent를 사용할 경우 다른 controller로 관리한다. Prow Pipeline Controller는 두가지 모드를 가지고 있다: - Direct: Tekton 파이프라인 스펙에 ProwJob이 있는 경우 Tekton 리소스 자체에 생성된다. - Delegate: Tekton 리소스를 생성하는 다른 컴포넌트로 job 요청을 전달한다. 해당 모드는 ProwJob이 Tekton 파이프라인 스펙에 없는 경우 동작한다. Jenkins X는 Tekton의 복잡성을 추상화 하기 위해 두번째 모드로 동작한다. 따라서 Jenkinx X 문법에서 Tekton 문법으로 변환하기 위한 또다른 단계가 필요하다. ### Jenkins X PipelineRunner Controller Jenkinx X 팀에 의해 관리되는 Prow Chart에서 배포되어 실행 중인 `pipelinerunner` 컴포넌트는 Jenkins X 자체 컴포넌트다. `jx controller pipelinerunner` 명령어로 실행되어 HTTP 요청을 기다린다. PipelineRunner Controller는 Prow Pipeline Controller에서 오는 `PipelineRunRequest`를 파싱하고, 복제할 Git repository 검색, 체크아웃을 위해 branch/commit 하고 ( `pullRequest` 또는 `release` ) 하나씩 증가하는 ID를 가진 빌드 번호를 검색한다. 해당 빌드 번호는 repository에 `SourceRepository`리소스에 저장되어 있다. 위에 필요한 정보를 모두 얻고 나면 Git에서 pipeline을 검색하는데 필요한 모든 것들이 준비되었고 Tekton 문법으로 번역하여 실행 시키도록 할것이라 생각이 들 수도 있다. 그러나 아직 몇가지가 더 필요하다. Jenkinx X pipeline은 여러 단계의 상속을 사용할 수 있기 때문에 관련된 모든 Git repository를 복제하고 "유효한" pipeline을 구축하는 것은 해당 컴포넌트가 모두 처리하기에는 힘들다. 대신에 모든 복잡한 작업은 (또다시) ... 파이프라인에게 전달된다. Jenkins X는 우리가 실행할 pipeline을 빌드하기 이전에 pipeline을 실행시켜 해당 빌드를 수행한다. 이것을 "meta pipeline" 이라고 하며 다음 포스팅에서 자세히 다루도록 하겠다. 다시 Pipeline Runner로 돌아가서, meta pipeline을 실행하기 위해 필요한 리소스들은 아래와 같다: - [Tekton Task](https://github.com/tektoncd/pipeline/blob/v0.8.0/docs/tasks.md) : 몇가지 단계가 필요하며 다음 포스팅에서 다루도록 하겠다. - [Tekton Pipeline](https://github.com/tektoncd/pipeline/blob/v0.8.0/docs/pipelines.md) : 우리가 수행할 Task를 감싸기 위해 필요하다. - [Tekton PipelineRun](https://github.com/tektoncd/pipeline/blob/v0.8.0/docs/pipelineruns.md) : pipeline을 실행 시켜 준다. - [Jenkins X PipelineActivity](https://jenkins-x.io/docs/reference/api/#jenkins.io/v1.PipelineActivity) : Jenkins X에서 `jx get activities`와 같은 명령어를 실행할 때 Pipeline과 상호작용을 하는 entry point 이다. 해당 리소스는 Tekton 리소스에서 "owner"로 설정되며 해당 리소스가 지워지면 아래 자식 리소스들도 지워지게 된다. - [Jenkins X PipelineStructure](https://jenkins-x.io/docs/reference/api/#jenkins.io/v1.PipelineStructure) : `pipeline` & `pipelineren` 과 `task` & `taskrun` 같은 Tekton 리소스들을 하나로 묶어준다. ### Tekton Pipelines Controller 이제 예상했겠지만 Tekton 또한 컨트롤러를 가지고 있다. 여기선 2가지를 중점적으로 살펴보겠다. - [`pipelinerun` controller](https://github.com/abayer/build-pipeline/tree/0.8.0-jx-support-backwards-incompats/pkg/reconciler/pipelinerun) : PipelineRun 이벤트를 지켜보고 있다. - [`taskrun` controller](https://github.com/abayer/build-pipeline/tree/0.8.0-jx-support-backwards-incompats/pkg/reconciler/taskrun) : TaskRun 이벤트를 지켜보고 있다. 위 두가지 컨트롤러 모두 Jenkins X 팀에서 관리하는 Tekton Chart의 일부분인 `tekton-pipelines-controller` deployment에서 돌고 있다. 따라서 Jenkins X PipelineRunner Controller는 PipelineRun 리소스를 포함한 Tekton 리소스들을 생성한다. 이로 인해 Tekton Pipelinerun은 새로운 PipelineRun 리소스들을 관리하고 해당 리소스를 참조하는 파이프라인에서 각 Task별로 TaskRun 리소스를 생성한다. 그리고 Tekton Taskrun Controller는 새로운 TaskRun 리소스에 반응하고 각각 Pod을 생성해 준다. 이 pod은 meta pipeline의 모든 단계를 실행하는데 필요한 모든 컨테이너를 가지고 있다. 다음 포스팅에서 더 자세히 다루도록 하겠다. ### 최종 단계  요약하자면 단계는 아래와 같다. - **GitHub** : Hook에 WebHook Event를 JSON 형태로 HTTP Request를 이용하여 보낸다. - **Hook** : `trigger` 플러그인에 이벤트를 전달하며 해당 플로거인은 하나 이상의 ProwJob 리소스를 생성한다. - **Prow's Pipeline Controller** : ProwJob의 생성을 지켜보고 있으며 Jenkins X PipelineRunner에 HTTP Request를 보낸다. - **Jenkins X's PipelineRunner Controller** : PipelineRun 생성을 지켜보고 있으며 pipeline을 참고하여 각 작업마다 TaskRun을 생성한다. - **Tekton's Taskrun Controller** : TaskRun 생성을 지켜보고 있으며 각 작업별로 Pod을 생성한다. [다음 포스팅](https://uzihoon.com/post/66e2cf70-7164-11ea-a8b4-299838a825b5)에서는 "**Meta Pipeline**"에 대해 자세히 알아보도록 하겠다. 해당 단계는 정확한 파이프라인을 찾아 실행하고 Tekton에서 실행할 수 있도록 문법 변환을 이루어 주는 단계이다.