Jenkinsstudy2018 juc2018 takamii228

553784f5490e80cde79ef80ee70b5ed2?s=47 takamii228
September 23, 2018

Jenkinsstudy2018 juc2018 takamii228

#JenkinsStudy #juc2018 @takamii228

553784f5490e80cde79ef80ee70b5ed2?s=128

takamii228

September 23, 2018
Tweet

Transcript

  1. AWSとJenkinsを活用して 1年間で約500回商用デプロイした話と Kubernetes活用 Jenkinsユーザ・カンファレンス東京 2018 #Jenkinsstudy #juc2018 @takamii228

  2. 自己紹介 • @takamii228 ◦ SIerのAgile開発を頑張る部署に所属 ◦ 認定スクラムマスター ◦ アジャイル開発における開発基盤整備やプロジェクト支援に従事 •

    なんでもやる雑食エンジニア ◦ CI/CD環境構築、アーキ設計、 AWS設計、Scrum運営、チームビルディング ◦ 仕事でよく使う言語は Java ( Spring )、PHP ( Laravel ) ◦ 開発基盤(GitLab, Jenkins, Mattermost, JIRA, Conflu等) on k8sを運用中 
  3. とあるプロジェクトの、何の数字でしょうか? 491 1592 1991

  4. とあるプロジェクトの、何の数字でしょうか? • 491 ◦ 商用環境へデプロイするJenkinsジョブの実行回数( 約1年間 ) • 1592 ◦

    検証環境へデプロイするJenkinsジョブの実行回数( 約2年間 ) • 1991 ◦ クローズされたPull Requestの数( 約2年間 )
  5. 今日主に話すこと • 頻繁にデプロイ可能な仕組みをAWSとJenkinsでどう作ったのか • Jenkinsの運用上の工夫や発生した問題をどのように対処したか • なぜ頻繁に商用デプロイ可能な仕組みを構築できたのか • 【番外編】Kubernetes上でJenkinsを使う話

  6. 実践する時は自分の置かれた状況で考えよう ”外から得られた学びを、そのまま自分たちの現場や仕事で適 用しようとしてもたいていうまくいかない。自分たちの「状況」に照 らし合わせてみることが必要だ。  (中略) 私たちは、他者の実践の背景にどんな状況、制約があったのか を理解し、自分たちの状況、制約の下ではどのように実践すべ きかを捉え直さないといけない。”

  7. 目次 1. とあるWebサービス開発プロジェクトの概要 2. 開発フローとJenkinsのビルドパイプライン 3. Jenkins運用中に起きたトラブルとその対応 4. 振り返りとまとめ 5.

    【番外編】JenkinsをKubernetes上で動かす話
  8. 1. とあるWebサービス開発プロジェクトの概要

  9. とあるWebサービス開発案件 • B2C向け新規Web ECサービス開発 • 既存システムと新規開発システムを含む複数のサービスをAPIで連携 • フロントエンドや各種サービスを複数の会社がそれぞれ新規に開発

  10. 開発体制と会社毎の開発範囲 弊社から画面開発ベンダへ フロントエンドの実行環境と APIを提供

  11. プロジェクトにおける課題 • サービス開始までのスケジュールがタイトである • 商用運用開始後も頻繁に変更を反映させたいという顧客要望がある • 各社が並行新規開発のため、頻繁なAPI定義変更が予想される • サービス間の連携が多く、特に結合試験時期に炎上が予想される

  12. 頻繁な変更に耐えうるアーキテクチャ設計 API定義や設定を頻繁に変更可能なアーキテクチャを設計した 1. Git と Jenkinsによる CI/CD環境を構築する 2. Swaggerを使ってインクリメンタルなAPI開発を実現する 3.

    AWSとAnsibleでImmutable Infrastatucreを実現する これらを踏まえたJenkinsのビルドパイプラインを構築した。
  13. 【参考】OpenAPI Specification(Swagger) REST APIを記述するための規格 • OpenAPI Initiativeが企画 • YAMLファイルでAPI定義を記述する 主要なツール

    • Swagger Editor(エディタ) • Swagger UI(ビュワー) • Swagger Codegen(コード生成ツール)
  14. https://swagger.io/tools/open-source/

  15. 使用したソフトウェアやライブラリ バックエンド フロントエンド 言語 Java PHP フレームワーク Spring Boot -

    ライブラリ Spring Cloud Swagger Codegen Flyway など SwaggerCodegen テストフレームワーク JUnit Spock PHPUnit
  16. 2.開発フローとJenkinsのビルドパイプライン

  17. GitBucketとJenkinsを使った開発フロー • ブランチ戦略はGitLabフローを採用 • Pull RequestとMaster Merge時にCIを実行してMasterの品質を担保 • ビルド環境はGitBucketとJenkins Master

    + Agent 3台構成
  18. JenkinsfileとMultibranch Pipelineでジョブを定義 • JenkinsのジョブはMultibranch Pipelineで定義 • ブランチ毎に同じCIが走るようにGitレポジトリにJenkinsfileを含めた • ジョブ定義がJenkinsfileに集約されるため管理性が高まる •

    JenkinsfileはScripted Pipelineで記述 ( 当時はDeclarativeがなかった )
  19. Jenkinsで3種類のJenkinsパイプラインを定義 1. Git Push時に実行するブランチビルドパイプライン 2. リリース資材を作成するリリースビルドパイプライン 3. リリース資材をAWS環境にデプロイするデプロイパイプライン

  20. 1. ブランチビルドパイプライン • Pull RequestとCIを組み合わせて、CIに成功したものだけmasterにマージ • Swaggerでのソース・ドキュメント自動生成をパイプラインに含めた • Swagger yamlと自動生成ソース・ドキュメントの二重管理を防いだ

  21. 【参考】Swaggerを使ったインクリメンタルなAPI開発 https://speakerdeck.com/int128/customize-swagger-templates

  22. 2.リリースビルドパイプライン releaseブランチのプッシュで成果物を作成する(後述)

  23. Multibranch Pipelineとtag push GitBucket / GitLabとMultiBranch Pipelineではtag push契機でジョブが実行されないため、 release/x.y.z branch

    の pushの中でtag pushを実行するようにした GitBucket : https://int128.hatenablog.com/entry/2016/10/06/224444 GitLab : https://takamii.hatenablog.com/entry/2018/07/09/001337 def releaseVersion = env.BRANCH_NAME.startsWith('release/') ? env.BRANCH_NAME.substring('release/'.length()) : null if(releaseVersion){ def userRemoteConfig = scm.userRemoteConfigs.head() withCredentials([usernameColonPassword(credentialsId: userRemoteConfig.credentialsId, variable: 'GIT_CREDENTIAL')]) { def url = userRemoteConfig.url.replace('://', "://${env.GIT_CREDENTIAL}@") sh 'git config user.email gituser@example.com' sh 'git config user.name gituser' sh "git tag -f $releaseVersion" sh "git push $url $releaseVersion -f" sh "git push $url --delete ${env.BRANCH_NAME}" }  }
  24. 3. デプロイパイプライン • AWSのEC2 Base AMIとAnsibleでImmutable Infrastractureを構築 • AutoScaling Group切り替えでBlue

    Greenデプロイを実現
  25. 【参考】オンラインデプロイパイプライン EC2をterminateしないオンラインリリース(高速デプロイ)の仕組みも作成した

  26. Jenkinsによる自動化でデプロイ頻度を増やせた • 複雑なビルド・リリース処理をJenkinsジョブで自動化した • AWSとAnsibleを使うことでインフラ構築も自動化できた • Jenkinsジョブ実行で誰でもいつでもデプロイできるようにできた • デプロイのハードルが下がり、デプロイ頻度を増やすことができた

  27. 【参考】フロントエンド開発にも越境しました 画面開発会社にもJenkinsでのデプロイジョブを提供し、開発のアジリティが向上 https://speakerdeck.com/takamii228/xpjug2018-lt-takamii228

  28. 3. Jenkins運用中に起きたトラブルとその対応

  29. Jenkins運用中に発生した事件簿 1. スローテスト問題によるJenkinsのビルド待ち行列 2. ビルドのゴミが溜まってDisk Fullが発生 3. Jenkinsおじさんボトルネック問題

  30. スローテスト問題によるJenkinsの待ち行列 • 開発が進むと1回のCI実行に20分近くかかるようになった • PR発行からMasterマージ可能になるまでに1時間以上かかるケースもあった • 開発者の開発リズムを崩す原因になっていた

  31. スローテスト問題に対する解決策(1/2) SpringのテストのMockのDIの仕方を改善し、テストケースの実行速度を向上 Field InjectionからConstructor InjectionにしてApplication Contextの再ロード時間を削減 // Field Injection方式 class

    UserInfoServiceSpec extends ServiceSpec { @Autowired UserInfoService userInfoService; @TestConfiguration static class MockConfig { final detachedMockFactory = new DetachedMockFactory() @Bean UserInfoService pointInfoService() { detachedMockFactory.Mock(UserInfoService) } ... //Constructor Injection方式 class UserInfoServiceSpec extends ServiceSpec implements ConstructorInjectionHelper { UserInfoService userInfoService = Mock() def setup() { UserInfoService = newInstanceWithMocks(UserInfoService) } ... https://int128.hatenablog.com/entry/2017/02/16/224842
  32. スローテスト問題に対する解決策(2/2) パイプラインのうち同時実行可能な部分を並列化して待ち時間を半減させた

  33. 【参考】Jenkinsfileによる並列ジョブ実行 parallelディレクティブで並列実行ジョブを定義できる parallel( apiA: { node { checkout scm stage('test')

    { withEnv(rdsEnv()) { sh './gradlew apiA:check' } } } }, apiB: { node { checkout scm stage('test') { withEnv(rdsEnv()) { sh './gradlew apiB:check' } } } }, batch: { node { checkout scm stage('test') { withEnv(rdsEnv()) { sh './gradlew batch:check' } } } }, } ) https://int128.hatenablog.com/entry/2017/07/12/131402
  34. 2.ビルドのゴミが溜まってDiskフルが発生問題 問題 • Jenkinsのディスク容量が不足してジョブ実行に失敗した • ビルドログ、Dockerビルドのゴミが残留していた ◦ Docker Buildを使っている場合はイメージのゴミが残り続ける 対処

    • 【暫定】ビルド結果を定期的に削除する設定を追加した • 【恒久】Jenkinsに対して1TBのEBSをアタッチ(お金で解決) ◦ Docker Build部分はAWS CodeBuildに移行した
  35. 3. Jenkinsおじさんボトルネック問題 問題 • Jenkinsのジョブがブラックボックス化し有識者しかメンテできない • Jenkinsジョブが失敗したときの原因分析が有識者しかできない • 有識者がボトルネックになり不在時の対応が遅れる 対処

    • Jenkins有識者をチーム内に育成する • Jenkinsfileのジョブ定義のドキュメント作成し Confluenceで共有 • 運用作業はJenkins有識者とペアで作業する
  36. 4. 振り返りとまとめ

  37. なぜここまで頻繁に商用デプロイできたのか? • JenkinsのCI/CDパイプラインを本格開発前に準備できていた ◦ アーキテクチャが本格開発前に固まっていた ◦ CI/CDパイプラインはコーディングの本格開始前までに準備しておくべき • AWSを使うことで検証環境と商用環境をほぼ差分なく構築できた ◦

    AWSとAnsible使うことでImmutable Infrastractureが構築できた ◦ 差分はあってもインスタンスタイプや環境変数程度だった • 検証環境で実績を積んだJenkinsジョブを商用環境にそのまま利用できた ◦ 検証環境で成功が保証されたものを商用環境でもそのまま利用できた
  38. まとめ 頻繁にサービスをデプロイ可能にするために、 • アーキテクチャとCI/CD環境を開発本格化前に準備・構築しておく • 検証環境で自動デプロイの実績を積み、そのまま商用環境に持っていく • Jenkinsの運用がブラックボックスにならないように透明化しておく

  39. 【番外編】JenkinsをKubernetes上で動かす話 ※他のセッションとの発表と重なる部分やK8sの詳細な説明は省略します

  40. 開発環境をKubernetesで運用しています • 以前docker-composeで運用していた開発基盤をKubernetes化 • K8s DashboardでPodのリソース・ログ確認などの運用が容易になった

  41. Kubernetes Dashboardによる運用

  42. • TerraformとkopsでAWS上に k8s Clusterを構築 • Helmfileに各Podの定義を記述し Helm Chartを使ってPodをデプロイ Terraform、kops、Helmによるk8s環境構築

  43. JenkinsのHelm Chart 公式のHelm Chartが用意されている https://github.com/helm/charts/tree/master/stable/jenkins helmfileにPodやJenkinsのdefault設定を上書く設定を記述する - name: jenkins namespace:

    devops chart: stable/jenkins values: - Master: HostName: jenkins.{{ requiredEnv "kubernetes_ingress_domain" }} resources: {requests: {cpu: 50m, memory: 256Mi}, limits: {cpu: 2000m, memory: 4096Mi}} ServiceType: ClusterIP InstallPlugins: - workflow-aggregator:2.5 - credentials-binding:1.16 - git:3.9.1 - gitlab-plugin:1.5.8 - mattermost:2.5.0 - keycloak:2.2.0 Persistence: Size: 50Gi
  44. JenkinsをKubernetes上で動かす2つの方法 1. Jenkins Master、Agent共にKubernetes上のPodで動かす ◦ AgentのPodはビルド実行単位で起動させる ◦ Agent Podの設定はKubernetes Plugin経由で設定する

    2. Jenkins MasterのみKubernetes上で動かしてAgentはk8s外で実行する ◦ Agentを従来のAgentサーバで実行する ◦ AWS CodeBuild、GCP CloudBuildなどのクラウドビルドサービスを使う 設定が容易でスケールメリットもあるAWS CodeBuildを採用
  45. 【参考】AWS CodeBuild AWSが提供するマネージドのコンテナビルドサービス • 指定したランタイムのコンテナイメージによるビルドを実行する • AWS ECRに登録した自作のコンテナイメージも使える • 最大20並列で実行できる

    • かかる費用はビルド時間単位の従量課金制 • Jenkinsからはプラグイン経由でビルド実行できる https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-awsjenkins
  46. Jenkins のジョブを AWS CodeBuildで動かす GitLabへのPush契機でAWS CodeBuildを実行し、成果物をS3に配置する https://takamii.hatenablog.com/entry/2018/09/07/234509

  47. AWS CodeBuild のかゆいところ • GitLab / GitBucketからは直接連携できないため間に Jenkinsが必要 ◦ 逆にJenkinsを間に挟むことてGit

    Push単位での実行が可能になる • ブランチ制御できないためMultibranch Pipelineと相性が悪い ◦ ブランチの情報を環境変数で渡して if文で制御する //buildspec.yml …. package: commands: - | if [ -z "${BRANCH_NAME%%release/*}" ]; then ./package.sh fi artifacts: commands: - | if [ -z "${BRANCH_NAME%%release/*}" ]; then aws s3 cp artifacts "s3://bucket/${BRANCH_NAME#release/}/artifacts" fi https://github.com/takami228/jenkins-awscodebuild-starter //Jenkinsfile …. stages { stage('codebuild') { steps { awsCodeBuild( credentialsType: 'keys', projectName: 'projectName', region: 'aws-region', sourceControlType: 'project', sourceVersion: env.BRANCH_NAME, envVariables: "[{BRANCH_NAME,${env.BRANCH_NAME}}]", ) } }
  48. おわり

  49. 参考資料 • @takamii228 ◦ つばくろぐ : https://takamii.hatenablog.com/ ◦ SpeakerDeck :

    https://speakerdeck.com/takamii228 • @int128 ◦ GeekFactory : https://int128.hatenablog.com/ ◦ SpeakerDeck : https://speakerdeck.com/int128
  50. ライセンス • The Jenkins logo is licensed under the Creative

    Commons Attribution-ShareAlike 3.0 Unported License. (https://jenkins.io/) • AWS(Amazon Web Services)は、米国その他の諸国における、 Amazon.com, Inc.またはその関連会社の商標です • Dockerは、Docker、Inc.の米国およびその他の国における商標または登録商標です • Mattermostは、Mattermost, Inc. の登録商標です • SonarQubeは、SonarSource S.A.及びその子会社、関連会社の商標または登録商標です • Ansibleは、米国およびその他の国において登録された Red Hat, Inc.の商標です • JIRA、Confluenceは、豪州およびその他の国における Atlassian および/またはその関連会社の登録商標または商標です • The GitLab logo and wordmark artwork are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.(https://gitlab.com/gitlab-com/gitlab-artwork/blob/master/README.md)