Kekeの日記

エンジニア、読書なんでも

Easy CI/CDらしいServerlessなJenkins XでPRでJobをキックする

f:id:bobchan1915:20190507204340p:plain
Jenkins X

本記事

本記事では過去に書いたSpinnakerによる宣言的継続的デリバリー(DCD)の経験から、「Easy CI/CD」とうたうJenkin Xを使用して使い勝手などを検証しようと思います。

そのSpinnakerの記事が以下のリンクです。

www.1915keke.com

Jenkins自体は日本だけでいくつかの企業に採用実績があるものの、費用対効果の観点からJenkins XをKubernetes環境で導入しているというケースはあまり聞きません。

なので調査をしてみようと思います。注意点としては無料アカウントだと簡単にRequested Quotaを超える可能性があるので、短期的に課金することをおすすめします。

余談

Countinous Deployment Foundationがローンチされて、より継続的デリバリーが当たり前な世の中になっていくと思います。

cd.foundation

現時点で、どの会社もアジャイルの方針を取っていて、ベロシティの観点だとより重要度が増してくるのは言うまでもありません。

そのような世界情勢の意識とともに、本記事を書いていきたいです。

Jenkins Xについて

概要

Jenkins Xとは

KubernetesのためのモダンなCI/CD手法を提供するJenkinsの派生プロジェクト

です。また自動的にKnativeもインストールされます。

公式ドキュメント

jenkins-x.io

機能

Jenkins Xは以下の機能を提供します。

1. 自動CI/CD

Jenkinsパイプラインを熟知する必要なく、Jenkins XではCI/CDをすることができます。

2. プルリクプレビュー環境

プルリクでプレビュー環境を自動的に提供するので、masterにマージする前にフィードバックをもらうことができる。

3. GitOpsによる環境プロモーション

チーム毎に環境を定義することができて、Jenkins Xはバージョン間などの環境の自動化をする。

4. Issueやプルリクへのフィードバック

コミット、Issue, プルリクに自動でコメントしたりして、プレビューをできるかどうかをフィードバックをします。

今までの課題とJenkins XのSolution

従来のJenkinsでは以下のような問題がありました。

  • 単一障害点でmergeなどWebhookを見逃すケースがあった
  • ディスクを使いきって、人間が管理しないといけなかった
  • プラグインのバージョン管理の問題
  • GithubのRateは制限されている
  • 多くのメモリをパイプライン実行中以外でも消耗する

それを解決するために

  • Buildが必要なときにだけ起動する
  • エフェラメラルなパイプラインを作ってビルドが終わると消える
  • CIでJenkinsのプラグインが最新かをチェックする
  • 単一障害点をつくらず、高いスケール性と可用性
  • GithubAPIスキャンを避ける
  • リカバリ戦略をBuild設定がGitで管理することによって実現する

機能を実現したのがKnativeな、Serverless JenkinsXです。

アーキテクチャ

Jenkins XはDevOpsを実現できるようなアーキテクチャになっています。 あくまでも略図なので注意してください。

f:id:bobchan1915:20190510015146p:plain
Jenkins Xのアーキテクチャ

本来はこれまでの理解で十分でしたが、Jenkins Xの環境構築でつまづいた時に詳細のアーキテクチャ図が役に立ったので少し解説します。

f:id:bobchan1915:20190510030742p:plain

1. Prow

CI/CDを担っているアプリケーションです。

github.com

構成する以下の要素もあります。

  • Tide: Github PRについてマネージする

github.com

2. Tekton

パイプラインを標準化しているものです。

tekton.dev

3. Registory

以下の三つの構成要素です。

  • Nexus: Repository Manager
  • Docker Registory: Docker Imageを格納
  • ChartMuseum: Helm Chartを格納

github.com

github.com

macOSへjxのインストール

以下のコマンドでインストールできます。

$ brew tap jenkins-x/jx 
$ brew install jx 

試しに以下のコマンドを実行して確認をしてみてください。

jx version

実際に使ってみる

これより、実際にKubernetesクラスタを構築してみようと思います。

まず困ったら

コマンドなど困ったら以下のコマンドを使うとヘルプをみることができます。

$ jx help

Installing:
  install                   Install Jenkins X in the current Kubernetes cluster
  uninstall                 Uninstall the Jenkins X platform
  upgrade                   Upgrades a resource
  create cluster            Create a new Kubernetes cluster
  update cluster            Updates an existing Kubernetes cluster
  create jenkins token      Adds a new username and API token for a Jenkins server
  init                      Init Jenkins X

Adding Projects to Jenkins X:
  import                    Imports a local project or Git repository into Jenkins
  create archetype          Create a new app from a Maven Archetype and import the generated code into Git and Jenkins for CI/CD
  create spring             Create a new Spring Boot application and import the generated code into Git and Jenkins for CI/CD
  create lile               Create a new Lile based application and import the generated code into Git and Jenkins for CI/CD
...

わかりやすく書かれているので、ぜひコマンドを忘れたりするとまず一回やってみるのがいいかもしれません。

クラスタを作成する

まず、Jenkins Xがインストールされたクラスタを作成します。

jx create cluster gke --skip-login

オプションは--helpで確認をすることができます。

      --default-admin-username='admin': the default admin username to access Jenkins, Kubernetes Dashboard, ChartMuseum and Nexus
      --default-environment-prefix='': Default environment repo prefix, your Git repos will be of the form 'environment-$prefix-$envName'
  -d, --disk-size='': Size in GB for node VM boot disks. Defaults to 100GB
     ...
      --no-gitops-vault=false: When using GitOps to create the source code for the development environment this flag disables the creation of a vault
      --no-tiller=true: Whether to disable the use of tiller with helm. If disabled we use 'helm template' to generate the YAML from helm charts then we use 'kubectl apply' to install it to avoid using tiller completely.
      --on-premise=false: If installing on an on premise cluster then lets default the 'external-ip' to be the Kubernetes master IP address

いろいろ質問が聞かれるので、必要に応じて答えてください。ここではすでにgcloudが認証済みであることを前提にしているので、ログインをしていなければ以下のようにしましょう。

gcloud auth login

最初の質問はどのプロジェクトに作るか?です。

? Google Cloud Project:  [Use arrows to move, space to select, type to filter, ? for more help]
  ...
  ...
  ...
> keke-test
  keke-hoge
  ...
  ...

次はどのようなクラスタにするかでMulti zoneクラスタなど設定できます

? What type of cluster would you like to create  [Use arrows to move, space to select, type to filter, ? for more help]
  Regional
> Zonal

次に選択すると、それに応じでRegionなりZoneなりを選択することになります。

? Google Cloud Zone:  [Use arrows to move, space to select, type to filter, ? for more help]
  asia-east1-a
  asia-east1-b
> asia-east1-c
  asia-east2-a
  asia-east2-b
  asia-east2-c
  asia-northeast1-a
  asia-northeast1-b
  asia-northeast1-c
  asia-northeast2-a

次はMachineタイプです。

? Google Cloud Machine Type:  [Use arrows to move, space to select, type to filter, ? for more help]
  g1-small
  n1-standard-1
> n1-standard-2
  n1-standard-4
  n1-standard-8
  n1-standard-16
  n1-standard-32
  n1-standard-64
  n1-standard-96
  n1-highmem-2

次はNodeの数と最大Node数です。? 今回は3にします。

? Minimum number of Nodes (per zone) [? for help] (3)
? Maximum number of Nodes [? for help] (5)

次にプリエンティティブノードを使用するかを尋ねられます。実験や検証などはYesと答えましょう。コストが70%削減できます。

? Would you like use preemptible VMs? [? for help] (y/N)

そしてCloud StorageおよびCloud Registoryに対して権限が必要かを尋ねられます。

? Would you like to access Google Cloud Storage / Google Container Registry? [? for help] (y/N) N

そしてKanikoが必要かどうかを聞かれます。

? Would you like to enable Kaniko for building container images [? for help] (y/N)

これ以降は、次節と全く同じのため、次節にまとめて書きます。

既存のクラスタにインストールする

jxコマンドを使ってクラスタを作成しない場合は、既存のクラスタにJenkins Xをインストールすることになるかと思います。

クラスタは以下の要求を満たさないといけません。

  • RBACが有効になっていること
  • クラスタのDefault storage classが有効になっていること
  • awseksを使っていないならdocker registry(DockerHubやGoogle container registry)を有効かする必要があります
  • 4vCPUがmasterNodeとは用意されていること

今回はすでにアプリケーションを動かしているクラスタがあるので、それを元に使ってみようと思います。

環境をテストする

最初にクラスタに導入可能かどうかをチェックし、必要ならばネームスペースなどが自動で作成されます。

$ jx compliance run

INFO[0001] created object                                name=heptio-sonobuoy namespace= resource=namespaces
INFO[0001] created object                                name=sonobuoy-serviceaccount namespace=heptio-sonobuoy resource=serviceaccounts
INFO[0001] created object                                name=sonobuoy-serviceaccount-heptio-sonobuoy namespace= resource=clusterrolebindings
INFO[0001] created object                                name=sonobuoy-serviceaccount namespace= resource=clusterroles
INFO[0001] created object                                name=sonobuoy-config-cm namespace=heptio-sonobuoy resource=configmaps
INFO[0001] created object                                name=sonobuoy-plugins-cm namespace=heptio-sonobuoy resource=configmaps
INFO[0001] created object                                name=sonobuoy namespace=heptio-sonobuoy resource=pods
INFO[0001] created object                                name=sonobuoy-master namespace=heptio-sonobuoy resource=services

最大で60分以上テストをするみたいです。

もしかしらたら以下のようなエラーがGKE環境だと出るかと思います。

error: failed to start the compliance tests: failed to create object: failed to create API resource sonobuoy-serviceaccount: clusterroles.rbac.authorization.k8s.io "sonobuoy-serviceaccount" is forbidden: attempt to grant extra privileges:...

これはJenkins Xが依存しているSonobuoyがAdmin権限を要求しているのにも関わらず、GKE環境だとデフォルトでは付与されないためです。

このようなときはClusterRoleBindingをもってして、ClustorRoleを作ってあげれば解決することができます。

kubectl create clusterrolebinding <your-user-cluster-admin-binding> --clusterrole=cluster-admin --user=<your.google.cloud.email@example.org>

途中のテストの状態は以下のコマンドで確認することができます。

$ jx compliance status
Compliance tests are still running, it can take up to 60 minutes.

1時間たってもう一回確認すると以下のようになっていました。

$ jx compliance status
Compliance tests completed. Use `jx compliance results` to display the results.

そして、終了していると以下のコマンドで結果を調べることができます。

$ jx compliance results

STATUS TEST
FAILED [sig-storage] ConfigMap binary data should be reflected in volume [NodeConformance] [Conformance]
PASSED [k8s.io] Container Lifecycle Hook when create a pod with lifecycle hook should execute poststart exec hook properly [NodeConformance] [Conformance]
PASSED [k8s.io] Container Lifecycle Hook when create a pod with lifecycle hook should execute prestop exec hook properly [NodeConformance] [Conformance]
PASSED [k8s.io] Container Lifecycle Hook when create a pod with lifecycle hook should execute prestop http hook properly [NodeConformance] [Conformance]
PASSED [k8s.io] Docker Containers should be able to override the image's default arguments (docker cmd) [NodeConformance] [Conformance]
...

最初の一つだけがFailしていました。

しかし、どれが問題なのかわからないので、ひとまずこのまま進めようと思います。

$ kubectl get configmap --all-namespaces

heptio-sonobuoy   sonobuoy-config-cm                   1         1h
heptio-sonobuoy   sonobuoy-plugins-cm                  2         1h
kube-system       apib.v1                              1         5d
kube-system       extension-apiserver-authentication   6         14d
kube-system       fluentd-gcp-config-v1.2.3            4         14d
kube-system       heapster-config                      1         14d
kube-system       ingress-uid                          2         14d
kube-system       kube-dns                             0         14d
kube-system       kube-dns-autoscaler                  1         14d
kube-system       kubernetes-dashboard-settings        0         14d
kube-system       metrics-server-config                1         14d
kube-system       payment.v1                           1         13d
kube-system       payment.v2                           1         10d
linkerd           grafana-config                       3         2d
linkerd           prometheus-config                    1         2d

消したければリソースを全削除することができます。

$ jx compliance delete

Jenkins Xのコンポーネントをインストールする

以下のコマンドでJenkins XをKubernetesクラスタにインストールします。

$ jx install gke

パスワードは以下のオプションで指定をすることができます。

$ jx install gke --default-admin-password=This_is_password

このコマンドで実行されることは以下の通りです。

  1. Jenkins Xネームスペースを作成
  2. Tillerをインストール
  3. docker-registryなど必要なコンポーネントがインストール

まずプロバイダを聞かれるのでgkeを選択します。

あまり馴染みがない人も多いかと思いますが、KanikoとはDockerコンテナないでDocker Imageをビルドできるものです。

github.com

Ingress Controllerがない場合は以下の作成するかを聞かれます。

? No existing ingress controller found in the kube-system namespace, shall we install one? [? for help] (Y/n)

またDomainを聞かれるので、Yesと答えておけば大丈夫です。

Domain 35.221.150.11.nip.io

そして次にGitOpsのためのGithubAPI認証が始まります。

? GitHub user name: KeisukeYamashita
To be able to create a repository on GitHub we need an API Token

指定された以下のリンクにアクセスをしてAPI Tokenを発行して、入力します。

https://github.com/settings/tokens/new?scopes=repo,read:user,read:org,user:email,write:repo_hook,delete_repo

f:id:bobchan1915:20190510005907p:plain
Github API Tokenの発行

そしてGithubをGit Serverとして使用するかを聞かれるので、Bitbucketなどを使用しない限りはYesと答えます。

? Do you wish to use GitHub as the pipelines Git server: (Y/n)

そしてどのようなJenkinのタイプをインストールするかを聞かれるので、今回はJenkins Xを選択します。

? Select Jenkins installation type:  [Use arrows to move, space to select, type to filter]
> Serverless Jenkins X Pipelines with Tekton
  Static Jenkins Server and Jenkinsfiles

そして、今回はCIをするのかCI/CDをするのかを尋ねられます。今回はCI/CDの調査なので前者を選択します。

? Pick default workload build pack:   [Use arrows to move, space to select, type to filter]
> Kubernetes Workloads: Automated CI+CD with GitOps Promotion
  Library Workloads: CI+Release but no CD

次にKeisukeYamashitaというGit Accountを使ってWebhookを送信するかどうかを聞かれます。管理コストの関係で、そのようにするのが望ましいでしょう。

? Do you wish to use KeisukeYamashita as the Git account to be used to send webhook events Yes

WARNING: waiting for install to be ready, if this is the first time then it will take a while to download imagesと出るので、しばらく待ちます。

ここで20分程度かかります。正しくインストールができると以下のように出力されます。

WARNING: waiting for install to be ready, if this is the first time then it will take a while to download images
Jenkins X deployments ready in namespace jx
Configuring the TeamSettings for ImportMode YAML


    ********************************************************

         NOTE: Your admin password is: hogehoge

    ********************************************************


Creating default staging and production environments
? Select the organization where you want to create the environment repository:  [Use arrows to move, space to select, type to filter]
> KeisukeYamashita
  ...
  ...
  ...

勝手にStagingとProduction環境が追加されます。GitOpsなので今回の環境に対してRepositoryが作成されることになります。作成されています。

f:id:bobchan1915:20190510033338p:plain
作成されたRepository

また、出力されたパスワードを使用してProwのDeck WebUIにログインすることができます。

f:id:bobchan1915:20190510033627p:plain
DeckのBasic認証

今のところ、何もありません。

f:id:bobchan1915:20190510033717p:plain
DeckのWebUI

またdefault namespaceにおいて、Jenkins Consoleをインストールして、表示することができます。

$ jx console

このときにJenkinsがインストールされていなかったら、以下のコマンドでインストールをしましょう。

$ jx add app jenkins

次のセクションからCI/CDをしていきましょう。

アプリケーションのCI/CD

今回はQuickStartを使って、アプリケーションの実験をしてみましょう。

$ jx create quickstart -l go

以下のように行います。このコマンド自体は以下のことを行います。

  • QuickStartディレクトリを作成
  • Gitリポジトリを作成
  • Githubリポジトリを作成
  • ソースコードをPush
  • 各種ファイルを作成
    • Dockerfile
    • Jenkinsfile
    • Kubernetes用のHelm chart
  • GithubのWebhookを登録
  • 最初のパイプラインをトリガー

その一連の流れが標準出力で流されてきます。

Using Git provider GitHub at https://github.com
? Do you wish to use KeisukeYamashita as the Git user name? Yes


About to create repository  on server https://github.com with user KeisukeYamashita
? Which organisation do you want to use? KeisukeYamashita
? Enter the new repository name:  test-jenkins


Creating repository KeisukeYamashita/test-jenkins
Generated quickstart at /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins
### NO charts folder /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins/charts/golang-http
Created project at /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins

The directory /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins is not yet using git
? Would you like to initialise git now? Yes
? Commit message:  Initial import

Git repository created
performing pack detection in folder /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins
--> Draft detected Go (65.746753%)
selected pack: /Users/keisuke.yamashita/.jx/draft/packs/github.com/jenkins-x-buildpacks/jenkins-x-kubernetes/packs/go

replacing placeholders in directory /Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins
app name: test-jenkins, git server: github.com, org: keisukeyamashita, Docker registry org: keisukeyamashita
skipping directory "/Users/keisuke.yamashita/src/github.com/KeisukeYamashita/test-jenkins/.git"
Pushed Git repository to https://github.com/KeisukeYamashita/test-jenkins

Creating GitHub webhook for KeisukeYamashita/test-jenkins for url http://hook.jx.34.85.89.64.nip.io/hook

Watch pipeline activity via:    jx get activity -f test-jenkins -w
Browse the pipeline log via:    jx get build logs KeisukeYamashita/test-jenkins/master
Open the Jenkins console via    jx console
You can list the pipelines via: jx get pipelines
When the pipeline is complete:  jx get applications

For more help on available commands see: https://jenkins-x.io/developing/browsing/

そして、最初のデプロイメントは以下のようになってPendingされます。

f:id:bobchan1915:20190512012214p:plain
プロジェクトを作成してPendingJobの状態

また、パイプラインのアクティビティは以下のように取得できます。

jx get activity -f test-jenkins -w

STEP                                                 STARTED AGO DURATION STATUS
KeisukeYamashita/test-jenkinsxxx/master #1                 1m19s          Running
  from build pack                                          1m19s          Running
    Credential Initializer 5xjfz                           1m19s       0s Succeeded
    Git Source Keisukeyamashita Test Jenkinsxx X2fzp       1m15s       0s Succeeded https://github.com/KeisukeYamashita/test-jenkinsxxx
    ...
    Build Post Build                                                      Pending
    Promote Changelog                                                     Pending
    Promote Helm Release                                                  Pending
    Promote Jx Promote                                                    Pending
   ...

プロジェクトを作成すると以下のフローで進みます。

f:id:bobchan1915:20190510073748p:plain
Projectを作成したときの挙動

プルリクを出してみる

プルリクを出すとWebhookを通してJenkins Xに通知され、Jenkins Pipelineを持ってしてDocker ImageとHelm Chartが作成、ChartMuseumへPushされます。

するとStaging環境に対してJenkins Xがプルリクを出しアプリケーションのPromoteの準備が整います。

自動でMergeし、Promotionを行います。

ここからは実際にやってみましょう。

git push origin test-branch

でPushをしてPull Requestを出してみます。

以下のPull Requestを出してみました。

f:id:bobchan1915:20190511153646p:plain
出したPull Request

するとStatusは以下のようになります。

Unexpected response from pipeline runner service: 400 Bad Request

これはどういうことかというと、以下のIssueに上げられています。

github.com

つまるところ、まず自分でMergeしてね!ということです。

Mergeしてみます。するとJobがトリガーされました。

f:id:bobchan1915:20190511154122p:plain
TriggerされたJob

Production環境へPromote

Production環境へPromoteをするにはjx promoteコマンドを使用します。

フローとしては、以下のような流れになります。

f:id:bobchan1915:20190510083312p:plain
Production環境へのPromoteフロー

Applicationの導入

自分のApplicationをJenkinsXをつかってCI/CDしていきたいときは、以下のコマンドをgitリポジトリで実行します。

$ jx import --org KeisukeYamashita

すると、先程のQuickStartと同様にこれらのファイルがなければ、以下のことを行います。

  • QuickStartディレクトリを作成
  • Gitリポジトリを作成
  • Githubリポジトリを作成
  • ソースコードをPush
  • 各種ファイルを作成
    • Dockerfile
    • Jenkinsfile
    • Kubernetes用のHelm chart
  • GithubのWebhookを登録
  • 最初のパイプラインをトリガー

Addonの追加

拡張機能が欲しい時にAddonを使ってJenkins Xと統合することができます。対象ドキュメントは以下のリンク先です。

jenkins-x.io

例えばIstioがほしいときは

jx create addon istio

とすれば導入をすることができます。

クリーンアップ

以下のコマンドでクリーンアップを行うことができます。

$ jx uninstall

Jenkins X has been successfully uninstalled from team namespace jx

まとめ

今回はJenkins Xの環境構築をしてみました。その中で、CI/CDを導入するまでの負担が見えてきたと思い、Serverlessであるのも納得がいくでしょう。

Jenkins XはDevOpsを回していくという観点では非常に導入しやすいですが、少し大きな規模になってしまうでしょう。

Spinnakerはデプロイメントパイプラインを定義し、戦略的なデプロイメントを行っていくことに主眼が当てられており、CIの要素は少ないでしょう。ユースケースによってSpinnakerやJenkins Xは技術選定が必要だなと感じました。

次はProgressive Deploymentを考えていこうと思います。

最後までありがとうございました!

参考文献

medium.com

opensource.com