Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jenkinsstudy2018 juc2018 takamii228

takamii228
September 23, 2018

Jenkinsstudy2018 juc2018 takamii228

#JenkinsStudy #juc2018 @takamii228

takamii228

September 23, 2018
Tweet

More Decks by takamii228

Other Decks in Technology

Transcript

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

    View Slide

  2. 自己紹介
    ● @takamii228
    ○ SIerのAgile開発を頑張る部署に所属
    ○ 認定スクラムマスター
    ○ アジャイル開発における開発基盤整備やプロジェクト支援に従事
    ● なんでもやる雑食エンジニア
    ○ CI/CD環境構築、アーキ設計、 AWS設計、Scrum運営、チームビルディング
    ○ 仕事でよく使う言語は Java ( Spring )、PHP ( Laravel )
    ○ 開発基盤(GitLab, Jenkins, Mattermost, JIRA, Conflu等) on k8sを運用中 

    View Slide

  3. とあるプロジェクトの、何の数字でしょうか?
    491
    1592
    1991

    View Slide

  4. とあるプロジェクトの、何の数字でしょうか?
    ● 491
    ○ 商用環境へデプロイするJenkinsジョブの実行回数( 約1年間 )
    ● 1592
    ○ 検証環境へデプロイするJenkinsジョブの実行回数( 約2年間 )
    ● 1991
    ○ クローズされたPull Requestの数( 約2年間 )

    View Slide

  5. 今日主に話すこと
    ● 頻繁にデプロイ可能な仕組みをAWSとJenkinsでどう作ったのか
    ● Jenkinsの運用上の工夫や発生した問題をどのように対処したか
    ● なぜ頻繁に商用デプロイ可能な仕組みを構築できたのか
    ● 【番外編】Kubernetes上でJenkinsを使う話

    View Slide

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

    View Slide

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

    View Slide

  8. 1. とあるWebサービス開発プロジェクトの概要

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. 頻繁な変更に耐えうるアーキテクチャ設計
    API定義や設定を頻繁に変更可能なアーキテクチャを設計した
    1. Git と Jenkinsによる CI/CD環境を構築する
    2. Swaggerを使ってインクリメンタルなAPI開発を実現する
    3. AWSとAnsibleでImmutable Infrastatucreを実現する
    これらを踏まえたJenkinsのビルドパイプラインを構築した。

    View Slide

  13. 【参考】OpenAPI Specification(Swagger)
    REST APIを記述するための規格
    ● OpenAPI Initiativeが企画
    ● YAMLファイルでAPI定義を記述する
    主要なツール
    ● Swagger Editor(エディタ)
    ● Swagger UI(ビュワー)
    ● Swagger Codegen(コード生成ツール)

    View Slide

  14. https://swagger.io/tools/open-source/

    View Slide

  15. 使用したソフトウェアやライブラリ
    バックエンド フロントエンド
    言語 Java PHP
    フレームワーク Spring Boot -
    ライブラリ Spring Cloud
    Swagger Codegen
    Flyway など
    SwaggerCodegen
    テストフレームワーク JUnit
    Spock
    PHPUnit

    View Slide

  16. 2.開発フローとJenkinsのビルドパイプライン

    View Slide

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

    View Slide

  18. JenkinsfileとMultibranch Pipelineでジョブを定義
    ● JenkinsのジョブはMultibranch Pipelineで定義
    ● ブランチ毎に同じCIが走るようにGitレポジトリにJenkinsfileを含めた
    ● ジョブ定義がJenkinsfileに集約されるため管理性が高まる
    ● JenkinsfileはScripted Pipelineで記述 ( 当時はDeclarativeがなかった )

    View Slide

  19. Jenkinsで3種類のJenkinsパイプラインを定義
    1. Git Push時に実行するブランチビルドパイプライン
    2. リリース資材を作成するリリースビルドパイプライン
    3. リリース資材をAWS環境にデプロイするデプロイパイプライン

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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 [email protected]'
    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}"
    }
     }

    View Slide

  24. 3. デプロイパイプライン
    ● AWSのEC2 Base AMIとAnsibleでImmutable Infrastractureを構築
    ● AutoScaling Group切り替えでBlue Greenデプロイを実現

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  32. スローテスト問題に対する解決策(2/2)
    パイプラインのうち同時実行可能な部分を並列化して待ち時間を半減させた

    View Slide

  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

    View Slide

  34. 2.ビルドのゴミが溜まってDiskフルが発生問題
    問題
    ● Jenkinsのディスク容量が不足してジョブ実行に失敗した
    ● ビルドログ、Dockerビルドのゴミが残留していた
    ○ Docker Buildを使っている場合はイメージのゴミが残り続ける
    対処
    ● 【暫定】ビルド結果を定期的に削除する設定を追加した
    ● 【恒久】Jenkinsに対して1TBのEBSをアタッチ(お金で解決)
    ○ Docker Build部分はAWS CodeBuildに移行した

    View Slide

  35. 3. Jenkinsおじさんボトルネック問題
    問題
    ● Jenkinsのジョブがブラックボックス化し有識者しかメンテできない
    ● Jenkinsジョブが失敗したときの原因分析が有識者しかできない
    ● 有識者がボトルネックになり不在時の対応が遅れる
    対処
    ● Jenkins有識者をチーム内に育成する
    ● Jenkinsfileのジョブ定義のドキュメント作成し
    Confluenceで共有
    ● 運用作業はJenkins有識者とペアで作業する

    View Slide

  36. 4. 振り返りとまとめ

    View Slide

  37. なぜここまで頻繁に商用デプロイできたのか?
    ● JenkinsのCI/CDパイプラインを本格開発前に準備できていた
    ○ アーキテクチャが本格開発前に固まっていた
    ○ CI/CDパイプラインはコーディングの本格開始前までに準備しておくべき
    ● AWSを使うことで検証環境と商用環境をほぼ差分なく構築できた
    ○ AWSとAnsible使うことでImmutable Infrastractureが構築できた
    ○ 差分はあってもインスタンスタイプや環境変数程度だった
    ● 検証環境で実績を積んだJenkinsジョブを商用環境にそのまま利用できた
    ○ 検証環境で成功が保証されたものを商用環境でもそのまま利用できた

    View Slide

  38. まとめ
    頻繁にサービスをデプロイ可能にするために、
    ● アーキテクチャとCI/CD環境を開発本格化前に準備・構築しておく
    ● 検証環境で自動デプロイの実績を積み、そのまま商用環境に持っていく
    ● Jenkinsの運用がブラックボックスにならないように透明化しておく

    View Slide

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

    View Slide

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

    View Slide

  41. Kubernetes Dashboardによる運用

    View Slide

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

    View Slide

  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

    View Slide

  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を採用

    View Slide

  45. 【参考】AWS CodeBuild
    AWSが提供するマネージドのコンテナビルドサービス
    ● 指定したランタイムのコンテナイメージによるビルドを実行する
    ● AWS ECRに登録した自作のコンテナイメージも使える
    ● 最大20並列で実行できる
    ● かかる費用はビルド時間単位の従量課金制
    ● Jenkinsからはプラグイン経由でビルド実行できる
    https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-awsjenkins

    View Slide

  46. Jenkins のジョブを AWS CodeBuildで動かす
    GitLabへのPush契機でAWS CodeBuildを実行し、成果物をS3に配置する
    https://takamii.hatenablog.com/entry/2018/09/07/234509

    View Slide

  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}}]",
    )
    }
    }

    View Slide

  48. おわり

    View Slide

  49. 参考資料
    ● @takamii228
    ○ つばくろぐ : https://takamii.hatenablog.com/
    ○ SpeakerDeck : https://speakerdeck.com/takamii228
    ● @int128
    ○ GeekFactory : https://int128.hatenablog.com/
    ○ SpeakerDeck : https://speakerdeck.com/int128

    View Slide

  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)

    View Slide