Easy CI/CDらしいServerlessなJenkins XでPRでJobをキックする
本記事
本記事では過去に書いたSpinnakerによる宣言的継続的デリバリー(DCD)の経験から、「Easy CI/CD」とうたうJenkin Xを使用して使い勝手などを検証しようと思います。
そのSpinnakerの記事が以下のリンクです。
Jenkins自体は日本だけでいくつかの企業に採用実績があるものの、費用対効果の観点からJenkins XをKubernetes環境で導入しているというケースはあまり聞きません。
なので調査をしてみようと思います。注意点としては無料アカウントだと簡単にRequested Quotaを超える可能性があるので、短期的に課金することをおすすめします。
余談
Countinous Deployment Foundationがローンチされて、より継続的デリバリーが当たり前な世の中になっていくと思います。
現時点で、どの会社もアジャイルの方針を取っていて、ベロシティの観点だとより重要度が増してくるのは言うまでもありません。
そのような世界情勢の意識とともに、本記事を書いていきたいです。
Jenkins Xについて
概要
Jenkins Xとは
KubernetesのためのモダンなCI/CD手法を提供するJenkinsの派生プロジェクト
です。また自動的にKnativeもインストールされます。
公式ドキュメント
機能
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を実現できるようなアーキテクチャになっています。 あくまでも略図なので注意してください。
本来はこれまでの理解で十分でしたが、Jenkins Xの環境構築でつまづいた時に詳細のアーキテクチャ図が役に立ったので少し解説します。
1. Prow
CI/CDを担っているアプリケーションです。
構成する以下の要素もあります。
- Tide: Github PRについてマネージする
2. Tekton
パイプラインを標準化しているものです。
3. Registory
以下の三つの構成要素です。
- Nexus: Repository Manager
- Docker Registory: Docker Imageを格納
- ChartMuseum: Helm Chartを格納
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が有効になっていること
aws
かeks
を使っていないなら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
このコマンドで実行されることは以下の通りです。
- Jenkins Xネームスペースを作成
- Tillerをインストール
- docker-registryなど必要なコンポーネントがインストール
まずプロバイダを聞かれるのでgke
を選択します。
あまり馴染みがない人も多いかと思いますが、KanikoとはDockerコンテナないでDocker Imageをビルドできるものです。
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
そして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が作成されることになります。作成されています。
また、出力されたパスワードを使用してProwの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されます。
また、パイプラインのアクティビティは以下のように取得できます。
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 ...
プロジェクトを作成すると以下のフローで進みます。
プルリクを出してみる
プルリクを出すと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を出してみました。
するとStatusは以下のようになります。
Unexpected response from pipeline runner service: 400 Bad Request
これはどういうことかというと、以下のIssueに上げられています。
つまるところ、まず自分でMergeしてね!ということです。
Mergeしてみます。するとJobがトリガーされました。
Production環境へPromote
Production環境へPromoteをするにはjx 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と統合することができます。対象ドキュメントは以下のリンク先です。
例えば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を考えていこうと思います。
最後までありがとうございました!