├── .github └── ISSUE_TEMPLATE │ ├── 01_feature_request.md │ ├── 09_bug_report.md │ └── config.yml ├── .gitignore ├── README.md ├── ci ├── .DS_Store ├── lab03-jenkins │ ├── README.md │ ├── images │ │ ├── .gitkeep │ │ ├── freestyle-project-config.png │ │ ├── freestyle-test-print-ip.png │ │ ├── jenkins-home.png │ │ ├── jenkins-master-logs.png │ │ ├── pipeline-project-config.png │ │ ├── systemctl-status-docker.png │ │ └── tools.jpg │ ├── sysctl.conf │ └── templates │ │ ├── baseimage │ │ └── Jenkinsfile │ │ └── general │ │ └── Jenkinsfile ├── lab05-tekton │ ├── README.md │ ├── demo │ │ ├── pipeline │ │ │ └── build-pipeline.yaml │ │ ├── run │ │ │ └── run.yaml │ │ ├── serviceaccount.yaml │ │ ├── tasks │ │ │ ├── deploy-to-k8s.yaml │ │ │ └── source-to-image.yaml │ │ └── trigger │ │ │ ├── event-listener.yaml │ │ │ ├── trigger-binding.yaml │ │ │ └── trigger-template.yaml │ └── media │ │ ├── 16553854211360.jpg │ │ ├── 16553856425843.jpg │ │ ├── 16553864459332.png │ │ ├── 16553875121264.jpg │ │ ├── 2022-06-26 at 17.39.31.png │ │ ├── CI:CD流程.png │ │ └── tekton-concept.jpg └── lab06-teamcity │ ├── README.md │ └── images │ ├── dsl │ ├── kotlin-dsl.png │ └── versioned-settings.png │ ├── installation │ ├── post-install-setup-step1.png │ ├── post-install-setup-step2.png │ ├── post-install-setup-step3.png │ ├── post-install-setup-step4.png │ ├── post-install-setup-step5.png │ ├── signup-teamcity-cloud-step1.png │ ├── signup-teamcity-cloud-step2.png │ ├── signup-teamcity-cloud-step3.png │ ├── signup-teamcity-cloud-step4.png │ ├── signup-teamcity-cloud-step5.png │ └── signup-teamcity-cloud-step6.png │ ├── integration │ └── teamcity-plugin.png │ ├── lab1 │ ├── lab1-step1.png │ ├── lab1-step2.png │ ├── lab1-step3.png │ ├── lab1-step4.png │ ├── lab1-step5.png │ ├── lab1-step6.png │ ├── lab1-step7.png │ ├── lab1-step8.png │ └── lab1-step9.png │ ├── lab2 │ ├── lab2-step1.png │ ├── lab2-step2.png │ ├── lab2-step3.png │ ├── lab2-step4.png │ ├── lab2-step5.png │ ├── lab2-step6.png │ ├── lab2-step7.png │ └── lab2-step8.png │ ├── lab3 │ ├── lab3-step1.png │ ├── lab3-step2.png │ ├── lab3-step3.png │ ├── lab3-step4.png │ ├── lab3-step5.png │ ├── lab3-step6.png │ ├── lab3-step7.png │ ├── lab3-step8.png │ └── lab3-step9.png │ ├── teamcity-icon.png │ ├── teamcity-logo.png │ └── workflow │ └── teamcity-workflow.png ├── config ├── lab01-ansible │ ├── ansible.cfg │ ├── app-stack.yml │ ├── hosts.ini │ ├── img │ │ ├── 192-1928992_order-66.jpg │ │ ├── 1_BORbGnI7OfdUdsCk7URXKg.png │ │ ├── 1_N2yWueFgHi6M_lQGGsnxVQ.png │ │ ├── ansible-wide.png │ │ └── t8redb90cc631.jpg │ ├── init-users.yml │ ├── inventory.v1 │ ├── inventory.v2 │ ├── readme.md │ └── vars │ │ └── default.yml └── lab09-terraform │ ├── README.md │ └── images │ ├── neworg.jpg │ ├── plan.jpg │ ├── tf.JPG │ ├── var.JPG │ └── vars.jpg ├── database └── lab06-liquibase │ ├── README.md │ └── images │ ├── flyway-1.jpg │ ├── flyway-2.jpg │ ├── flyway-3.jpg │ ├── flyway-4.jpg │ └── liquibase.jpg ├── deploy └── lab04-argocd │ ├── 0.necessary_before_start.md │ ├── 1.install-k8s-cluster.md │ ├── README.MD │ ├── crds │ ├── app-guestbook.yaml │ ├── apps-matrix-generator.yaml │ ├── appset-cluster-generator-base.yaml │ ├── appset-cluster-generator-label.yaml │ ├── appset-cluster-generator-value.yaml │ ├── appset-git-generator-base.yaml │ ├── appset-git-generator-file.yaml │ ├── appset-git-gernator-exclude.yaml │ ├── appset-list-generator.yaml │ ├── argocd.yaml │ ├── config-monday.yaml │ └── eip-pool.yaml │ └── images │ ├── argocd-ui-create-app.png │ ├── argocd-ui-login.png │ ├── guest-book-app-1.png │ └── guest-book-app-2.png ├── orchestration └── lab10-k3s │ └── README.md └── platform ├── lab02-jihu-gitlab ├── README.md └── img │ ├── jihu-gitlab.png │ ├── pipeline-build-log.png │ ├── pipeline-build-result.png │ ├── runner-succ.png │ └── runner-token.png └── lab09-atlassian ├── README.md ├── dev-process.png ├── devops-products.png ├── js-management.png ├── scrum.png └── users.png /.github/ISSUE_TEMPLATE/01_feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 报名DevOps工具鉴宝分享 💠🪕💎 3 | title: '[Tools_name] ' 4 | labels: new lab, review needed 5 | about: 对工具的独到见解和技巧都值得分享给大家 💡🔥🔥🔥 6 | assignees: 7 | - martinliu 8 | - majinghe 9 | --- 10 | <!-- 11 | 说明 12 | 13 | >请分享者想要分享的工具和内容,主办方确认了提议之后,就用通过pr把分享内容提交上来。所有分享内容都放在同子目录中,用 Readme.md 描述所有操作步骤,操作过程中所必要的代码和配置文件也需要放在这个目录中。 14 | --> 15 | 16 | ## 概述 17 | <!-- 18 | 请在分享内容概述中,尽量说明的详尽一些。 19 | --> 20 | >DevOps 工具 21 | * 名称: 22 | * 版本: 23 | 24 | 25 | >就职公司名称: 26 | <!-- 27 | 您目前工作的公司。或者求职状态。 28 | --> 29 | 30 | >分享排期: 31 | <!-- 32 | 请说明你希望在那个月份分享。 33 | --> 34 | 35 | 36 | >难度级别: 37 | <!-- 38 | 入门 | 中等 | 高级 39 | --> 40 | 41 | >分享目标: 42 | <!-- 43 | 希望别人从中学到什么程度。 44 | --> 45 | 46 | 47 | 48 | >分享内容概述: 49 | <!-- 50 | 希望别人从中学习到哪些知识,范围,功能点有哪些? 51 | --> 52 | 53 | 54 | >是否原创: 55 | <!-- 56 | 是 | 否 57 | --> 58 | 59 | >参考资料链接: 60 | * URL1: 61 | * URL2: 62 | * URL3: 63 | 64 | 65 | 66 | ## 运行环境 67 | 68 | <!-- 69 | 尽量演示这个工具最常用的一种配置状态和运行环境。 70 | --> 71 | 72 | >实操者运行环境: 73 | 74 | * 操作系统: 75 | * 工具版本: 76 | * 编程语言【如果有/需要】: 77 | 78 | 79 | >运行环境描述: 80 | <!-- 81 | 工具有可能需要操作某些外部虚拟机或者云环境、云服务等。 82 | --> 83 | * 虚拟机/云主机数量: 84 | * 操作系统: 85 | * 编程语言【如果有/需要】: 86 | * 云服务: 87 | 88 | 89 | ## 关于直播 90 | 91 | > 期望分享的时长: 92 | 93 | > 是否需要社区提供云资源支持,需要的话请概述需求: 94 | 95 | > 是否需求社区提供直播环境或者设备: 96 | 97 | 说明:如果对于主办方还有其它诉求,请加社区小助手微信【DevOps-SQ】联系我们。 98 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/09_bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐜 报告 Bug 3 | about: 如果你发现了某个示例练习的 Bug ,或者值得优化的地方 🔧 4 | --- 5 | 6 | ### Version Information 7 | | Software | Version(s) | 8 | | ------------------------| ---------- | 9 | | NuGet Package | | 10 | | .NET Core? | | 11 | | .NET Full Framework? | | 12 | | Driver Windows OS? | | 13 | | Driver Linux OS? | | 14 | | Visual Studio? | | 15 | | RethinkDB Server | | 16 | | Server Windows OS? | | 17 | | Server Linux OS? | | 18 | 19 | ### What is the expected behavior? 20 | 21 | ### What is the actual behavior? 22 | 23 | ### Please provide a unit test that demonstrates the bug. 24 | 25 | ### Other notes on how to reproduce the issue? 26 | 27 | ### Please provide JSON protocol traces and log files. 28 | 29 | Enable driver logging and JSON protocol traces here: 30 | * https://github.com/bchavez/RethinkDb.Driver/wiki/Protocol-Debugging 31 | 32 | ### Any possible solutions? 33 | 34 | ### Can you identify the location in the driver source code where the problem exists? 35 | 36 | ### If the bug is confirmed, would you be willing to submit a PR? 37 | 38 | Yes / No _(Help can be provided if you need assistance submitting a PR)_ 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🔖 关注中国DevOps社区官网 4 | url: https://DevOpsChina.org 5 | about: 访问社区官网了解“DevOps工具鉴宝”活动详情 🌐 6 | - name: 🔖 关注社区微信公众号 7 | url: https://mp.weixin.qq.com/s/__h3SpSNMdZ0LzsDEWV83A 8 | about: 与社区保持密切联系 🌐 9 | # name: Bug Report 10 | # description: File a bug report 11 | # title: "[Bug]: " 12 | # labels: ["bug", "triage"] 13 | # assignees: 14 | # - octocat 15 | # body: 16 | # - type: markdown 17 | # attributes: 18 | # value: | 19 | # Thanks for taking the time to fill out this bug report! 20 | # - type: input 21 | # id: contact 22 | # attributes: 23 | # label: Contact Details 24 | # description: How can we get in touch with you if we need more info? 25 | # placeholder: ex. email@example.com 26 | # validations: 27 | # required: false 28 | # - type: textarea 29 | # id: what-happened 30 | # attributes: 31 | # label: What happened? 32 | # description: Also tell us, what did you expect to happen? 33 | # placeholder: Tell us what you see! 34 | # value: "A bug happened!" 35 | # validations: 36 | # required: true 37 | # - type: dropdown 38 | # id: version 39 | # attributes: 40 | # label: Version 41 | # description: What version of our software are you running? 42 | # options: 43 | # - 1.0.2 (Default) 44 | # - 1.0.3 (Edge) 45 | # validations: 46 | # required: true 47 | # - type: dropdown 48 | # id: browsers 49 | # attributes: 50 | # label: What browsers are you seeing the problem on? 51 | # multiple: true 52 | # options: 53 | # - Firefox 54 | # - Chrome 55 | # - Safari 56 | # - Microsoft Edge 57 | # - type: textarea 58 | # id: logs 59 | # attributes: 60 | # label: Relevant log output 61 | # description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 62 | # render: shell 63 | # - type: checkboxes 64 | # id: terms 65 | # attributes: 66 | # label: Code of Conduct 67 | # description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com) 68 | # options: 69 | # - label: I agree to follow this project's Code of Conduct 70 | # required: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DevOps Tools live show | 工具鉴宝活动 2 | 3 | >Must know DevOps Tools for Developer [codename = *MKT4D*] 4 | 5 | Welcome to the DevOps China code live show! This lab is a collection of hands-on, self-paced exercises that will help you gain practical experience with various DevOps concepts and tools. The exercises are designed to be simple, straightforward, and easy to follow, with step-by-step instructions. 6 | 7 | Season one playlist 8 | * [Bilibili](https://space.bilibili.com/370989874/channel/collectiondetail?sid=338624) 9 | * [YouTube](https://www.youtube.com/watch?v=XBglZDywiXk&list=PLbiCRAJeBtFucYb2W_autUr8G5OGAUFAU&ab_channel=DevOpsChina) 10 | 11 | Season two is now open for summit a new idea at [MKT4D issues](https://github.com/DevopsChina/mkt4d/issues) 12 | 13 | Each code live show is a separate directory in this repository, and each directory contains a README file with detailed instructions, along with any necessary configuration files or sample code. Whether you're new to DevOps or an experienced practitioner, this lab will help you take your skills to the next level and give you the confidence to tackle real-world challenges. 14 | 15 | ### 第二季鉴宝活动的鉴宝人火热招募中,请提交你的分享思路到:[MKT4D issues](https://github.com/DevopsChina/mkt4d/issues) 16 | 17 | ## 活动简介 18 | 19 | 大家想要获取DevOps的理念和技术,收获深刻的理解;社区建议的途径是示例项目实操。这里是直播节目的代码仓库。我们用 DevOps 工具鉴宝的直播方式,请各路实战经验丰富的工程师给社区分享一些列有趣有料的Codelab,并在直播过程中进行生动的讲解和互动问答。 20 | 21 | "DevOps鉴宝活动“的目标是:公开面向社区的所有人,征集DevOps工具链中各种工具的实战操作技能。 22 | 23 | 相关的 DevOps 工具应该包含,但不仅限于以下分类。 24 | 25 | - 软件仓库 | code – 管理软件版本的工具–目前使用最广泛的是Git。 26 | - 构建工具 | build – 有些软件在打包或使用前需要编译,传统的构建工具包括Make、Ant、Maven和MSBuild。 27 | - 持续集成工具 | ci – 在配置好以后,每次将代码提交到存储库中时,它都会对软件进行构建、部署和测试。这通常可以提高软件质量和上市时间。这个市场上最流行的工具是 Jenkins、Travis、TeamCity和Bamboo。 28 | - 代码分析/审查工具 | verify – 这些工具可以查找代码中的错误,检查代码格式和质量,以及测试覆盖率。这些工具因编程语言而异。SonarQube是这个领域的一个流行工具,还有其他各种 “轻量的 “工具。 29 | - 配置管理 | config – 配置管理工具和数据库通常存储所有关于你的硬件和软件项目的信息,以及提供一个脚本和/或模板系统,用于自动化常见任务。在这个领域似乎有很多玩家。传统的玩家是Chef、Puppet和Salt Stack。 30 | - 部署工具 | deploy – 这些工具有助于软件的部署。许多CI工具也是CD(持续部署)工具,它们协助软件的部署。传统上在Ruby语言中,Capistrano工具被广泛使用;在Java语言中,Maven被很多人使用。所有的编排工具也都支持某种形式的部署。 31 | - 编排工具 | release – 这些工具配置、调度和管理计算机系统和软件。它们通常将 “自动化 “和 “工作流 “作为其服务的一部分。Kubernetes是一个非常流行的编排工具,它专注于容器。Terraform是一个非常流行的编排工具,它的关注点更广,包括云编排。另外,每个云提供商都有自己的一套工具(CloudFormation、GCP Deployment Manager, 和ARM)。 32 | - 监控工具 | monitor - 这些工具允许监控硬件和软件。通常,它们包括监控代理程序,用于监视进程和日志文件,以确保系统的健康。Nagios是一种流行的监控工具。 33 | - 测试工具 | test - 测试工具用于管理测试,以及测试自动化,包括性能和负载测试等。 34 | 35 | ![devops tool chain](https://elasticstack-1300734579.cos.ap-nanjing.myqcloud.com/2021-04-02-1-wTp-r9QJvF-DXGZDZUHmbA.jpeg) 36 | 37 | ## 学习清单 38 | 39 | 以下是所有 DevOps 鉴宝活动直播内容的回放清单,请大家根据需求学习打卡。社区也欢迎你在视频中发表弹幕和留言,分享你对 DevOps 工具的见解。 40 | 41 | ### code 42 | 43 | 44 | ### build 45 | 46 | 47 | ### test 48 | 49 | 50 | ### verify 51 | 52 | 53 | ### 持续集成 - ci 54 | 55 | - 第一季:第 3 集 恐龙级CI/CD工具 Jenkins [【Codelab】](ci/lab03-jenkins) - [【B 站视频】](https://www.bilibili.com/video/BV1MA4y1Z7Fk) 56 | - 第一季:第 5 集 云原生 CI 中的强者 Tekton [【Codelab】](ci/lab05-tekton) - [【B 站视频】](https://www.bilibili.com/video/BV1e94y117tY) 57 | - 第一季:第 6 集 云开发者可快速上手的 TeamCity [【Codelab】](ci/lab06-teamcity) - [【B 站视频】](https://www.bilibili.com/video/BV1pY4y17754) 58 | 59 | ### 配置管理 60 | 61 | - 第一季: 第 1 集 配置管理神器 Ansible - 新手入门指南 [【Codelab】](config/lab01-ansible) - [【B 站视频】](https://www.bilibili.com/video/BV1Uv4y1K7u9) 62 | - 第一季: 第 7 集 数据库变更版本管理利器 Liquibase [【Codelab】](database/lab06-liquibase) - [【B 站视频】](https://www.bilibili.com/video/BV1iT411E7Fk) 63 | - 第一季:第 9 集 - 基础架构自动化编排工具 Terraform [【Codelab】](config/lab09-terraform) - [【B 站视频】](https://www.bilibili.com/video/BV1HU4y1e7wZ) 64 | 65 | 66 | ### 部署 - deploy 67 | 68 | - 第一季:第 4 集 GitOps 的不二之选 ArgoCD [【Codelab】](deploy/lab04-argocd) -[【B 站视频】](https://www.bilibili.com/video/BV11a411L7f5) 69 | 70 | ### 编排 - Orchestration 71 | 72 | - 第一季:第 10 集 品鉴极简风格的容器平台 K3S [【Codelab】](orchestration/lab10-k3s) -[【B 站视频】](https://www.bilibili.com/video/BV11e411g7qx) 73 | 74 | ### 监控 - monitor 75 | 76 | ### 平台 - platform 77 | 78 | - 第一季:第 2 集 - 全能级 DevOps 平台 GitLab [【Codelab】](platform/lab02-jihu-gitlab) - [【B 站视频】](https://www.bilibili.com/video/BV1hR4y1c75b) 79 | - 第一季:第 9 集 - 品鉴 Atlassian 的 DevOps 全家桶的妙用 [【Codelab】](platform/lab09-atlassian) - [【B 站视频】](https://www.bilibili.com/video/BV1vG4y1a7ph) 80 | 81 | 82 | 83 | 84 | ## 贡献说明 85 | 86 | ### 目录结构 87 | 88 | 通过创建 PR 的方式,在以上分类目录中,按编号创建一个新的文件夹,用于容纳一个独立lab的所有相关代码。必须包含清晰的 readme 文件,在 readme 文件中清晰概要的描述相关技术知识要点,不能有意无意的包含任何产品市场宣传的内容。 89 | 90 | ```sh 91 | ├── README.md 92 | ├── build 93 | ├── ci 94 | ├── code 95 | ├── config 96 | │   └── lab01-ansible 97 | │   └── readme.md 98 | ├── deploy 99 | ├── monitor 100 | ├── release 101 | ├── test 102 | └── verify 103 | └── platform 104 | ``` 105 | 106 | ### 参与贡献过程 107 | 108 | 本代码库面向所有社区朋友开放。为了让大家能够顺利的参与贡献,请参考以下参与流程。 109 | 110 | 1. 在代码库的 issue 列表中,用模板创建一个新 issue,简单描述 codelab 的内容,必须包含工具、技术、开发语言和难度级别等,让其他人更清晰的了解知识的范围。 111 | 2. Issue 的沟通和确认阶段,社区评审同学和提议者在 issue 的评论区完成讨论确认过程。 112 | 3. 进入 codelab 代码的 PR 提交阶段,请创建符合规则的分支名称 【 分类 - 编号 - 工具名称】,例如: code-lab01-git 113 | 4. 进入 PR 搞了确认,社区评审同学会进行评审和测试,并提出修改建议。 114 | 5. 提交 PR 的合并请求,本代码库的维护同学审批 MR ,代码正式纳入主干。 115 | 6. 进入 Codelab 的学习和宣传阶段,Codelab的提交者和其他鉴宝人,用线上直播的方式演示整个 codelab 的操作过程,并将录播视频在 B 站分享给大家。 116 | 7. 关闭 issue 。 117 | 8. Codelab 的 readme 文件内容和 B 站视频 会分享给社区的所有人。 118 | 119 | ## 鸣谢 120 | 121 | ### 赞助商福利资源 122 | 123 | * Red Hat :欢迎免费注册[红帽开发者账号](https://developers.redhat.com/),可以免费获得开发者个人订阅的详情[「点这里」](https://www.redhat.com/wapps/tnc/viewterms/72ce03fd-1564-41f3-9707-a09747625585?extIdCarryOver=true&intcmp=701f2000001OMHaAAO&sc_cid=701f2000001OH7YAAW);订阅中包含了 16 个物理/虚拟机上 RHEL 操作系统的开发者使用订阅,并且可以访问几乎红帽所有产品的安装包和源代码。官方微信公众号「红帽支持与服务」 124 | * 极狐 GitLab :欢迎[免费注册使用极狐GitLab SaaS](https://gitlab.cn/)。凡是通过 DevOps 社区鉴宝活动注册的用户,可以发邮件至 marketing@gitlab.cn,申请超长(超 1 个月以上)时间的旗舰版试用(发邮件是为了方便获取需要延长旗舰版试用的 Group 等信息)。 125 | * Elastic:欢迎[免费注册 Elastic Cloud 超长(30 天)试用账号](https://info.elastic.co/elasticsearch-service-trial-community-showcase.html?ultron=community-martin&hulk=30d),只需要邮箱,无需输入信用卡号,直接体验当前最新版本的 Elastic Stack 技术栈。官方微信公众号「Elastic搜索」 126 | * JetBrains TeamCity Cloud 平台焕然一新,CI/CD 本色依旧,[免费试用](https://www.jetbrains.com/zh-cn/teamcity/cloud/)DevOps社区成员亮明身份还可通过链接获取[更长试用时间](https://www.jetbrains.com/zh-cn/teamcity/get-in-touch/) 官方微信号「JetBrainsChina」 127 | * 阿里云云起实验室:由阿里云提供的面向个人和企业开发者的零门槛云上实践平台,让开发者能够在“做中学”。实验室提供详细的实验手册指导和免费的实验云资源,一键生成预置实验环境,让开发者能够快速体验云计算、大数据、人工智能等云服务实验场景和解决方案,帮助开发者快速提升使用云服务的能力。[免费体验](https://developer.aliyun.com/adc/?utm_content=g_1000342863) 128 | * Rancher:Rancher 是一个开源的企业级 Kubernetes 管理平台,实现了 K8s 集群在混合云+本地数据中心的集中部署与管理。关注「Rancher 微信公众号」,第一时间了解 Rancher 中文论坛精选内容,公众号后台回复「工具鉴宝」还能免费解锁 K8s 安全防护终极指南哦~ 129 | 130 | 131 | ### 赞助商参与 132 | 133 | #### 第一季 134 | 135 | 第一期: 136 | 137 | * Red Hat & 极狐GitLab 赞助了直播抽奖礼品 138 | * JetBrains 赞助鉴宝人礼品 139 | 140 | 第二期: 141 | 142 | * Elastc & 极狐GitLab 赞助了直播抽奖礼品 143 | * JetBrains 赞助鉴宝人礼品 144 | 145 | 第三期: 146 | 147 | * 阿里云云起实验室 & Elastc 赞助了直播抽奖礼品 148 | * JetBrains 赞助鉴宝人礼品 149 | 150 | 第四期: 151 | 152 | * 阿里云云起实验室 & Elastc 赞助了直播抽奖礼品 153 | * JetBrains 赞助鉴宝人礼品 154 | 155 | 第五期: 156 | 157 | * 阿里云云起实验室 & Elastc 赞助了直播抽奖礼品 158 | * JetBrains 赞助鉴宝人礼品 159 | 160 | 第六期: 161 | 162 | * 阿里云云起实验室 & Elastc 赞助了直播抽奖礼品 163 | 164 | 第七期: 165 | 166 | * 阿里云云起实验室 & Elastc 赞助了直播抽奖礼品 167 | 168 | 第八期: 169 | 170 | - 阿里云云起实验室、中国DevOps社区 171 | 172 | 第九期: 173 | 174 | - 阿里云云起实验室 &Rancher 175 | 176 | 第十期: 177 | 178 | - 阿里云云起实验室 &Rancher 179 | 180 | ## 行为准则 181 | 182 | 本项目是面向所有社区参与者的共创学习项目,请任何参与者遵守[《中国DevOps社区行为规范》](https://www.devopschina.org/codeofconduct/) 183 | -------------------------------------------------------------------------------- /ci/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/.DS_Store -------------------------------------------------------------------------------- /ci/lab03-jenkins/README.md: -------------------------------------------------------------------------------- 1 | # Jenkins 快速入门和使用技巧 2 | 3 | <image src="https://www.jenkins.io/images/logos/jenkins/Jenkins.svg"> 4 | 5 | 6 | ## 1. Jenkins 介绍 7 | 8 | ```markdown 9 | # 构建伟大,无所不能。 10 | 11 | Jenkins 是开源 CI&CD 软件领导者, 提供超过1000个插件来支持构建、部署、自动化,满足任何项目的需要。 12 | 13 | > 出自 [Jenkins 官网](https://www.jenkins.io/zh/) 14 | 15 | ``` 16 | 17 | ### 1.1 历史考古 18 | 19 | 1. 大约在 2001 年,ThoughtWorks 开源了 CruiseControl(后面出了商业版本叫 Cruise,再后来改名为 GoCD)。 20 | 1. 2004 年夏天由 Sun 公司开发的 Hudson ,在 2005 年 2 月开源并发布第一个版本。 21 | 1. 大约在 2007 年,Hudson 被称为 Cruise Control 和其他开源构建服务器的更好替代品。 22 | 1. 2008 年 5 月的 JavaOne 大会上,Hudson 获得了开发解决方案类的 Duke's Choice 奖项。 23 | 1. 大约在 2010 年,甲骨文声称拥有 Hudson 的商标的权利,所以在 2011 年 1 月通过社区投票改名为 Jenkins (事件起因是因为 2009 年 6月 Oracle 收购 Sun)。 24 | 1. 2011 年 2 月,Jenkins 的第一个版本 1.396 版可供公众使用。 25 | 1. 2015 年 5 月,Jenkins 发布了基于 Java 7 的 1.612 版本。 26 | 1. 2016 年 4 月,Jenkins 发布了 2.0 版本。 27 | 1. 2017 年 4 月,Jenkins 发布了基于 Java 8 的 2.54 版本。 28 | 1. 2019 年 2 月,Jenkins 发布了基于 Java 8 和 Java 11的 2.164 版本。 29 | 1. 2022 年 5 月,截至目前最新版本 Jenkins 2.347 Weekly 和 Jenkins 2.332.3 LTS 30 | 31 | 资料链接 32 | 33 | * https://martinfowler.com/articles/continuousIntegration.html 34 | * http://cruisecontrol.sourceforge.net/download.html 35 | * https://en.wikipedia.org/wiki/Jenkins_(software) 36 | * https://www.jenkins.io/blog/2012/02/02/happy-birthday-jenkins/ 37 | * https://www.jenkins.io/2.0/ 38 | * https://www.jenkins.io/blog/2017/04/10/jenkins-has-upgraded-to-java-8/ 39 | * https://www.jenkins.io/download/ 40 | * https://www.jenkins.io/blog/2011/06/16/jenkins-long-term-support-release/ 41 | * https://www.jenkins.io/blog/2012/03/13/why-does-jenkins-have-blue-balls/ 42 | * https://www.jenkins.io/blog/2017/01/17/Jenkins-is-upgrading-to-Java-8/ 43 | 44 | ### 1.3 相关工具对比 45 | 46 | * https://en.wikipedia.org/wiki/Comparison_of_continuous_integration_software 47 | * https://github.blog/2017-11-07-github-welcomes-all-ci-tools/ 48 | 49 | 50 | ### 1.4 推荐学习文档 51 | 52 | * https://www.jenkins.io/doc/book/pipeline/ 53 | * https://www.bilibili.com/video/BV1fp4y1r7Dd 54 | * https://github.com/cloudbees/groovy-cps/blob/master/doc/cps-model.md 55 | * https://github.com/cloudbees/groovy-cps/blob/master/doc/cps-basics.md 56 | 57 | ## 2. 环境安装 58 | 59 | 本教程推荐使用 `docker` 在 Rocky Linux 8上安装 Jenkins 。 60 | 61 | > 备注:任何安装有 `docker` 工具的环境都可以。 62 | 63 | 测试环境服务器信息: 64 | 65 | * 本地 Rocky Linux 8 虚拟机,配置 4C16G 40G(sys) 100G(data) 66 | * IP:192.168.2.220 67 | 68 | ### 2.1 搭建原理 69 | 70 | 借助 docker 启动 Jenkins 简化服务部署,通过 Swarm 实现 Agent 节点的自动注册。其中 **Swarm** 是一个 **Jenkins** 插件,它允许节点加入附近的 **Jenkins**,从而形成一个特别的集群。通过这个插件我们可以自动向 **Jenkins** 添加 **Agent** 节点,而不用先在 **Jenkins** 上手动创建节点和注册,这个是一个不错的想法。 71 | 72 | ### 2.2 初始环境 73 | 74 | 在执行下面的步骤之前需要先完成节点的初始化,比如:关闭防火墙,性能调优配置(sysctl/ulimit)等 75 | 76 | 1. 安装 docker/docker-compose 工具 77 | 78 | 运行下面的命令: 79 | ```bash 80 | yum install -y yum-utils 81 | yum-config-manager \ 82 | --add-repo \ 83 | https://download.docker.com/linux/centos/docker-ce.repo 84 | 85 | # 注意命令 docker-compose 已经作为 docker 插件,使用时需要将 docker-compose 改成 docker compose 86 | yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin 87 | 88 | mkdir -p /etc/docker 89 | # 配置 docker ,让数据放在数据盘目录,比如:/data 90 | cat << EOF > /etc/docker/daemon.json 91 | { 92 | "bip":"192.168.101.1/24", 93 | "data-root":"/data/var/lib/docker/", 94 | "log-driver": "json-file", 95 | "log-opts": { 96 | "max-size": "10m", 97 | "max-file": "3" 98 | 99 | }, 100 | "exec-opts": ["native.cgroupdriver=systemd"], 101 | "default-ulimits": { 102 | "nofile": { 103 | "Name": "nofile", 104 | "Hard": 64000, 105 | "Soft": 64000 106 | } 107 | }, 108 | "storage-driver": "overlay2", 109 | "storage-opts": [ 110 | "overlay2.override_kernel_check=true" 111 | ], 112 | "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"] 113 | } 114 | EOF 115 | # 注意: 116 | # 1. 在EOF后面输入一个回车键,并且里面的网段可以自己指定,切记不好和主机所在网段冲突。 117 | # 2. 需要将本文档同级目录下文件 sysctl.conf 的内容放到本次演示环境的 /etc/sysctl.conf 中,然后执行 sysctl -p 让其配置生效。 118 | 119 | systemctl stop firewalld 120 | systemctl disable firewalld 121 | 122 | systemctl restart docker 123 | systemctl status docker 124 | systemctl enable docker 125 | ``` 126 | 上面的命令执行完成,正常情况会出现如下图: 127 | 128 | ![](./images/systemctl-status-docker.png) 129 | 130 | > 注:其他发行版本的搭建方式请参见:https://docs.docker.com/engine/install/ 131 | 132 | ### 2.3 启动服务 133 | 134 | 1. 创建目录、导入启动文件、启动服务、观察日志 135 | 136 | ```bash 137 | alias docker-compose='docker compose' 138 | # 创建系统目录和数据目录 139 | mkdir -p /data/opsbox-dev/{system,data}/jenkins/ 140 | # 设置数据目录权限,因为会将容器数据映射到主机 141 | chown -R 1000:1000 /data/opsbox-dev/data/jenkins 142 | 143 | cd /data/opsbox-dev/system/jenkins 144 | cat << EOF > docker-compose.yml 145 | version: '3' 146 | services: 147 | jenkins-master: 148 | container_name: jenkins 149 | image: registry.jihulab.com/opsbox-dev/oes-jenkins-plus:jenkins-v0.1.1-2.319.1-SNAPSHOT 150 | restart: unless-stopped 151 | ports: 152 | - 30080:8080 153 | - 50000:50000 154 | volumes: 155 | - /data/opsbox-dev/data/jenkins:/var/jenkins_home 156 | environment: 157 | JAVA_OPTS: >- 158 | -server 159 | -Xmx2g -Xms1g 160 | -XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport 161 | -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC 162 | -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy 163 | -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 164 | -Djenkins.install.runSetupWizard=false 165 | -Dhudson.model.LoadStatistics.clock=2000 166 | -Dhudson.model.ParametersAction.keepUndefinedParameters=true 167 | -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai 168 | -Duser.timezone=Asia/Shanghai 169 | -Dcom.sun.jndi.ldap.connect.pool.timeout=300000 170 | -Dhudson.security.csrf.DefaultCrumbIssuer.EXCLUDE_SESSION_ID=true 171 | 172 | jenkins-swarm: 173 | image: registry.jihulab.com/opsbox-dev/oes-jenkins-plus:jenkins-swarm 174 | restart: unless-stopped 175 | privileged: true 176 | volumes: 177 | - /tmp:/tmp 178 | - /lib/modules:/lib/modules # 好奇怪的配置在 rockylinux8,如果没有在执行 iptable 时会报异常 179 | depends_on: 180 | - jenkins-master 181 | links: 182 | - jenkins-master 183 | environment: 184 | JENKINS_URL: http://jenkins-master:8080 185 | JENKINS_USR: admin 186 | JENKINS_PSW: jenkins 187 | LABELS: docker 188 | 189 | EOF 190 | # 注意这里要给一个回车键 191 | 192 | # 拉取镜像 193 | docker-compose pull 194 | # 启动服务 195 | docker-compose up -d 196 | # 检查服务 197 | docker-compose ps 198 | # 查看日志 199 | docker-compose logs -f 200 | ``` 201 | 202 | > 注:里面使用到的镜像打包源码地址:https://github.com/opsbox-dev/oes-jenkins-plus 203 | 204 | 启动日志效果如下图: 205 | 206 | ![](./images/jenkins-master-logs.png) 207 | 208 | 2. 打开浏览器访问 http://192.168.2.220:30080 209 | 210 | > 账号:*admin* 密码:*jenkins* 211 | 212 | ### 2.4 测试环境 213 | 214 | 创建一个任务测试环境,主要是确保 docker 服务可用。 215 | 216 | ![](./images/jenkins-home.png) 217 | 218 | ## 3. 基础概念 219 | 220 | ### 3.1 流程和工具 221 | 222 | 大师 **Martin Fowler** 对持续集成是这样定义的:持续集成是一种软件开发实践,即团队开发成员经常**集成他们的工作**,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括**编译,发布,自动化测试**)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。 223 | 224 | 软件开发和交付过程,开发人员会在不同阶段持续使用如下面图中的工具在实现构建、打包、归档、部署和测试,执行完一次就算一次交付。 225 | 226 | ![](images/tools.jpg) 227 | 228 | > 注:上图整理了我目前在 CI/CD 工作中用的最多的工具,大家可以根据自己的情况整理出自己的技术栈和相关工具。 229 | 230 | ### 3.2 自由风格工程 和 Pipeline 工程的对比 231 | 232 | #### 1. 自由风格工程特点 233 | 234 | 配置页面分为 6 个区域,包括:工程信息、源码管理、构建触发器、构建环境、构建步骤和构建后动作。配置界面如下图: 235 | 236 | ![](./images/freestyle-project-config.png) 237 | 238 | #### 2. Pipeline 工程的特点 239 | 240 | 配置界面分为 4 个区域,包括:工程信息、构建触发器和流水线配置。配置界面如下图: 241 | 242 | ![](./images/pipeline-project-config.png) 243 | 244 | #### 3. 两种风格对比演示 245 | 246 | 自由风格配置界面 247 | 248 | ![](./images/freestyle-test-print-ip.png) 249 | 250 | 采用流水线风格描述 251 | 252 | ```groovy 253 | pipeline { 254 | agent any 255 | 256 | stages { 257 | stage('Hello') { 258 | environment { 259 | // 目前 credentials 支持 Secret Text/Secret File/Username and password/SSH with Private Key等 4 种类型。 260 | SSHKEY = credentials('vm-220') 261 | } 262 | steps { 263 | echo 'Hello Jenkins' 264 | 265 | // 打印当前 Jenkins Agent 的 IP,在 Jenkins Swarm Agent 容器里面。 266 | sh "ip addr" 267 | 268 | // 打印 192.168.2.220 节点 IP 信息 269 | sh "ssh -o StrictHostKeyChecking=no -i ${SSHKEY} ${SSHKEY_USR}@192.168.2.220 'ip addr'" 270 | 271 | } 272 | } 273 | } 274 | } 275 | 276 | ``` 277 | 278 | > 注:执行需要创建登录 192.168.2.220 的密钥,并将私钥录入 Jenkins 的 Credentials 中。 279 | 280 | 相关文档链接: 281 | * https://www.jenkins.io/doc/pipeline/steps/credentials-binding/ 282 | * https://www.jenkins.io/doc/book/pipeline/syntax/ 283 | 284 | ### 3.3 Jenkins 2.0 中核心的 Jenkinsfile 两种语法背后的历史和原理 285 | 286 | #### 1. Jenkins 2.0 历史 287 | 288 | 历史回顾: 289 | 290 | 1. 2014 年 5 月,groovy-cps 库诞生了 0.1 版本。 291 | 2. 2016 年 4 月,Jenkins 发布了 2.0 版本,对应的 groovy-cps-1.7。 292 | 4. 2016 年 8 月,Jenkins 的 pipeline-model-definition 插件发布 0.1 版本。 293 | 5. 2017 年 2 月,Jenkins 的 pipeline-model-definition 插件发布 1.0 GA 版本。 294 | 6. 2017 年 4 月,Blue Ocean 1.0 295 | 7. 2017 年 4 月,Jenkins 发布了基于 Java 8 的 2.54 版本,对应的 pipeline-model-definition-1.1.1。 296 | 297 | #### 2. Scripted Pipeline 特点和原理 298 | 299 | **特点** 300 | 301 | Scripted Pipeline 支持更多的 Groovy 语言语法,不像 Declarative Pipeline 受那么多的结构化限制。由于可以编写灵活的逻辑,可以认为是高级版的 pipeline。 302 | 303 | 如果打算实现的逻辑比较灵活,比如有判断、分支,或者需要用 Groovy 语言编写复杂的运行步骤,都应该选择使用 Scripted Pipeline。 304 | 305 | **原理** 306 | 307 | 所有的开始都是起源于 KK 的一个叫 groovy-cps 项目。 308 | 309 | CPS(Continuation-Passing-Style, 续体传递风格)是一种编程风格:所有的控制块都通过 continuation 来显式传递。在 CPS 风格中,函数不能有返回语句,它的调用者要想获得它的结果,需要显式传递一个回调函数来获取结果并继续执行。而为了保证整个程序执行下去,这个回调函数还会一直嵌套下去。这里的回调函数就是一个 continuation 。 310 | 311 | 使用 CPS 来实现 Jenkins Pipeline 的原因是期望在任何时候都可以中断代码的执行保存状态,并在适当时候恢复执行。这可以应对 Jenkins Agent 宕机的场景。如果一个函数执行过后就返回了,那么就会丢失一部分状态,CPS 代码由于在中间不返回结果,因此可以解决这个问题。 312 | 313 | 然而,编写 Pipeline 代码的 Groovy 语言,其本身并不是 CPS 风格的,这就需要一个解释器将代码编译成 CPS 风格,在 Jenkins 里面通过 workflow-cps-plugin 包装 groovy-cps 这个库来完成。 314 | 315 | 在 workflow-cps-plugin 插件中,将 Job 配置的 Jenkinsfile 解析转为为 `CpsScript` 对象,并借助 Groovy 强大的 DSL (领域特定语言) 能力,实现对特点关键的解析。 316 | 317 | 318 | 举个栗子: 319 | 320 | ```groovy 321 | node { 322 | def mvnHome = tool 'M3' 323 | 324 | stage('Checkout') { 325 | checkout scm 326 | } 327 | 328 | stage('Build') { 329 | sh "${mvnHome}/bin/mvn -B package" 330 | } 331 | } 332 | 333 | ``` 334 | > 代码来源:https://github.com/cloudogu/jenkinsfiles/blob/1-scripted/Jenkinsfile 335 | 336 | 337 | 参考链接: 338 | 339 | * https://docs.groovy-lang.org/3.0.7/html/gapi/index.html?groovy/lang/Script.html 340 | * https://github.com/cloudbees/groovy-cps/blob/master/doc/cps-basics.md 341 | * https://github.com/cloudbees/groovy-cps 342 | * https://github.com/jenkinsci/workflow-cps-plugin 343 | * https://github.com/jenkinsci/pipeline-stage-step-plugin 344 | * https://github.com/cloudogu/jenkinsfiles 345 | * https://www.jenkins.io/blog/2016/09/06/jenkins-world-speaker-blog-pipeline-model-definition/ 346 | * https://www.jenkins.io/blog/2016/12/19/declarative-pipeline-beta/ 347 | * https://www.jenkins.io/blog/2017/02/03/declarative-pipeline-ga/ 348 | * https://www.jenkins.io/blog/2017/02/15/declarative-notifications/ 349 | * https://www.jenkins.io/blog/2017/04/05/say-hello-blueocean-1-0/ 350 | * https://www.jenkins.io/blog/2017/04/10/jenkins-has-upgraded-to-java-8/ 351 | * https://www.jenkins.io/blog/2014/07/08/workflow-plugin-tutorial-writing-a-step-impl/ 352 | 353 | 354 | #### 3. Declarative Pipeline 特点和原理 355 | 356 | **特点** 357 | 358 | Declarative Pipeline 设计意图是使用户将所需要的 Pipeline 以各种维度的参数声明出来,而不是编程的方式描述出来。相对 Scripted Pipeline 语法前者更简单, 但是 Declarative Pipeline 缺少灵活性,所以 Scripted Pipeline 中使用的部分语法在 Declarative Pipeline 中都不能直接使用,但是可以通过在 Declarative Pipeline 中使用 `script` Step 来支持。 359 | 360 | Declarative Pipeline 的特点是结构化的声明语句,各模块的从属关系比较固定,类似填写 Jenkins 配置 Job 页面的表单。固定格式的声明语句,还有利于从 BlueOcean 中查看工作流。 361 | 362 | **原理** 363 | 364 | Declarative Pipeline 是在 Scripted Pipeline 基础上开发,通过实现 `pipeline` 块(block)的语法,以此实现配置风格的流水线描述方式。所以在开发 Declarative Pipeline 时可以在 `pipeline` 块之外可以写 Groovy Scripts。 365 | 366 | 语法结构 367 | 368 | ```groovy 369 | 370 | pipeline { 371 | // 运行的节点 372 | agent {} 373 | 374 | // 全局预设环境变量,包括 Credentinal 变量 375 | envrionment {} 376 | 377 | // 触发器 378 | triggers {} 379 | 380 | // 外部库 381 | libraries {} 382 | 383 | // 特效配置 384 | options { 385 | //打开控制台日志的时间戳 386 | timestamps() 387 | // 指定失败后的重试次数 388 | retry(3) 389 | // 指定启动前等待的秒数 390 | quietPeriod(30) 391 | // 指定任务的超时时间,超时将放弃该任务 392 | timeout(time: 1, unit: 'HOURS') 393 | } 394 | 395 | // 构建参数 396 | parameters {} 397 | 398 | // 载入工具 399 | tools {} 400 | 401 | // 构建任务编排 402 | stages { 403 | stage { 404 | agent {} 405 | environment {} 406 | tools {} 407 | input {} 408 | when {} 409 | steps { 410 | sh "" 411 | echo "" 412 | script { 413 | //... 414 | } 415 | withEnv { 416 | 417 | } 418 | } 419 | // 并行任务编排 420 | parallel {} 421 | } 422 | // other stages 423 | } 424 | 425 | // 构建后处理 426 | post { 427 | 428 | } 429 | } 430 | 431 | ``` 432 | 433 | 参考链接 434 | 435 | * https://github.com/jenkinsci/pipeline-model-definition-plugin 436 | * https://github.com/jenkinsci/pipeline-model-definition-plugin/blob/master/EXTENDING.md 437 | 438 | #### 4. Jenkinsfile 几种扩展方式 439 | 440 | 1. 直接在 Jenkinsfile 中开发功能函数 441 | 442 | ```groovy 443 | pipeline { 444 | 445 | agent any 446 | 447 | options { 448 | disableConcurrentBuilds() 449 | skipDefaultCheckout true 450 | } 451 | 452 | stages{ 453 | 454 | stage("Checkout Code") { 455 | steps { 456 | script { 457 | deleteDir() 458 | cleanWs() 459 | def branch = purgeBranchString(git.branch) 460 | git branch: "${branch}", credentialsId: "${git.auth}", url: "${git.url}" 461 | } 462 | } 463 | } 464 | } 465 | } 466 | 467 | def purgeBranchString(branch) { 468 | 469 | def gitBranch = branch 470 | 471 | if (gitBranch?.startsWith("refs/heads/")) { 472 | gitBranch = gitBranch.replace("refs/heads/", "") 473 | } 474 | 475 | if (gitBranch?.startsWith("refs/tags/")) { 476 | gitBranch = gitBranch.replace("refs/tags/", "") 477 | } 478 | 479 | return gitBranch 480 | } 481 | 482 | 483 | ``` 484 | 485 | 2. 通过外部共享库扩展 486 | 487 | * https://github.com/SAP/jenkins-library/tree/0.1 488 | 489 | 3. 通过开发插件,这里面又有两种,一种是兼容自由风格的,一种是直接继承 `Step` 类来实现的。 490 | 491 | * https://github.com/jenkinsci/pipeline-utility-steps-plugin 492 | * https://github.com/opsbox-dev/oes-pipeline-plugin 493 | 494 | ## 4. 实战练习 495 | 496 | >两三句话的简介? 497 | 498 | * https://github.com/seanly/cloudogu-jenkinsfiles 499 | -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/.gitkeep -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/freestyle-project-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/freestyle-project-config.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/freestyle-test-print-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/freestyle-test-print-ip.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/jenkins-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/jenkins-home.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/jenkins-master-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/jenkins-master-logs.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/pipeline-project-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/pipeline-project-config.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/systemctl-status-docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/systemctl-status-docker.png -------------------------------------------------------------------------------- /ci/lab03-jenkins/images/tools.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab03-jenkins/images/tools.jpg -------------------------------------------------------------------------------- /ci/lab03-jenkins/sysctl.conf: -------------------------------------------------------------------------------- 1 | # 禁用sysrq 2 | kernel.sysrq = 0 3 | # 为core文件名添加pid扩展 4 | kernel.core_uses_pid = 1 5 | # 设置消息队列 6 | ## 消息队列中最大的字节数 7 | kernel.msgmnb = 65536 8 | ## 一个进程发送到另一个进程的消息最大长度 9 | kernel.msgmax = 65536 10 | ## 参数定义了共享内存段的最大尺寸(以字节为单位),64G 11 | kernel.shmmax = 68719476736 12 | ## 表示统一一次可以使用的共享内存总量(以页为单位)。默认是2097152 13 | kernel.shmall = 4294967296 14 | # 表示将mmap的基址,stack,vdso页面,栈(heap)的随机化 15 | kernel.randomize_va_space = 2 16 | # 检测到soft lockup时自动panic 17 | kernel.softlockup_panic = 1 18 | # softlockup时回溯所有cpu信息 19 | kernel.softlockup_all_cpu_backtrace = 1 20 | 21 | # socket监听(listen)的backlog上限 22 | net.core.somaxconn = 32768 23 | #接收套接字缓冲区大小的默认值(以字节为单位) 24 | net.core.rmem_default = 262144 25 | #发送套接字缓冲区大小的默认值(以字节为单位)。 26 | net.core.wmem_default = 262144 27 | #接收套接字缓冲区大小的最大值(以字节为单位) 28 | net.core.rmem_max = 16777216 29 | #发送套接字缓冲区大小的最大值(以字节为单位)。 30 | net.core.wmem_max = 16777216 31 | # #当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。这个参数表示该队列的最大值。 32 | net.core.netdev_max_backlog = 20000 33 | 34 | # 开启路由转发 35 | net.ipv4.ip_forward = 1 36 | # 禁用所有ip源路由 37 | net.ipv4.conf.default.accept_source_route = 0 38 | # 决定检查一次相邻层记录的有效性的周期,秒 39 | net.ipv4.neigh.default.gc_stale_time=120 40 | # 关闭rp_filter 41 | net.ipv4.conf.all.rp_filter=0 42 | net.ipv4.conf.default.rp_filter=0 43 | net.ipv4.conf.lo.arp_announce=2 44 | net.ipv4.conf.lo.arp_announce=2 45 | # arp数据包yuanip选择策略 46 | net.ipv4.conf.default.arp_announce = 2 47 | net.ipv4.conf.all.arp_announce=2 48 | # 开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击 49 | net.ipv4.tcp_syncookies = 1 50 | # 内核最多重试几次发送SYN包 51 | net.ipv4.tcp_synack_retries = 2 52 | # 启用时间戳 53 | net.ipv4.tcp_timestamps = 1 54 | # 关闭空闲连接tcp慢启动 55 | net.ipv4.tcp_slow_start_after_idle=0 56 | # socker使用内存大小 最小字节数,默认字节数,最大字节数 57 | net.ipv4.tcp_rmem = 4096 12582912 16777216 58 | net.ipv4.tcp_wmem = 4096 12582912 16777216 59 | # 确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。在这个层次上可以将报文丢弃,从而减少对内存的使用 60 | net.ipv4.tcp_mem = 786432 2097152 3145728 61 | # TCP处于FIN-WAIT-2连接状态的时间 62 | net.ipv4.tcp_fin_timeout = 30 63 | #表示那些尚未收到客户端确认信息的连接(SYN消息)队列的长度,默认为1024,加大队列长度为16384,可以容纳更多等待连接的网络连接数。 64 | net.ipv4.tcp_max_syn_backlog = 16384 65 | # 表示系统同时保持TIME_WAIT套接字的最大数量。如果超过此数,TIME_WAIT套接字会被立刻清除并且打印警告信息。之所以要设定这个限制,纯粹为了抵御那些简单的DoS攻击,不过,过多的TIME_WAIT套接字也会消耗服务器资源,甚至死机。 66 | net.ipv4.tcp_max_tw_buckets = 180000 67 | # 允许重用TIME_WAIT状态的套接字用于新的TCP连接 68 | net.ipv4.tcp_tw_reuse = 1 69 | #关闭TCP连接中TIME_WAIT套接字的快速回收 70 | net.ipv4.tcp_tw_recycle = 0 71 | #允许系统打开的端口范围 72 | net.ipv4.ip_local_port_range = 1024 65535 73 | # 不处理ipv6数据包 74 | #net.bridge.bridge-nf-call-ip6tables = 0 75 | # 不处理数据包 76 | #net.bridge.bridge-nf-call-arptables = 0 77 | # 默认过滤数据包 78 | #net.bridge.bridge-nf-call-iptables = 1 79 | 80 | # iptables对于已建立的连接,1200秒若没有活动,那么则清除掉 81 | net.netfilter.nf_conntrack_tcp_timeout_established = 1200 82 | # iptables最大连接数 83 | net.netfilter.nf_conntrack_max = 1048576 84 | 85 | # 当可用内存小于10%时使用交换空间 86 | vm.swappiness = 10 87 | 88 | # 内存脏数据占比20%时,阻塞io,刷入磁盘 89 | vm.dirty_ratio = 20 90 | # 内存脏数据占比10%时,后台进程会稍后清理脏数据 91 | vm.dirty_background_ratio = 10 92 | # 表示内核允许分配所有的物理内存,不检测当前的内存状态如何 93 | vm.overcommit_memory = 1 94 | 95 | # 一个进程可以拥有的VMA(虚拟内存区域)的数量 96 | vm.max_map_count = 655360 97 | # 该文件表示内核回收用于directory和inode cache内存的倾向 98 | vm.vfs_cache_pressure=300 99 | # Linux VM最低保留多少空闲内存(Kbytes) 100 | vm.min_free_kbytes=1048576 101 | 102 | # 文件系统最大打开句柄数 103 | fs.file-max = 2097152 104 | # 表示每一个real user ID可创建的inotify instatnces的数量上限 105 | fs.inotify.max_user_instances = 8192 106 | # 表示同一用户同时可以添加的watch数目(watch一般是针对目录,决定了同时同一用户可以监控的目录数量 107 | fs.inotify.max_user_watches = 524288 108 | # inotify队列最大长度 109 | fs.inotify.max_queued_events = 16384 110 | 111 | -------------------------------------------------------------------------------- /ci/lab03-jenkins/templates/baseimage/Jenkinsfile: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | # 功能描述 4 | 5 | 构建基础镜像 6 | 7 | # 参数配置 8 | 9 | ```yaml 10 | 11 | coderepo: 12 | auth: xxx 13 | url: xxx 14 | 15 | dockerpush: 16 | auth: xxx 17 | registry: xxx 18 | namespace: xxx 19 | image: xxx 20 | 21 | build_method: docker-compose 22 | ``` 23 | 24 | # 插件依赖 25 | 26 | * https://www.jenkins.io/doc/pipeline/steps/workflow-basic-steps/ 27 | * https://plugins.jenkins.io/docker-workflow/ 28 | * https://github.com/opsbox-dev/oes-template-plugin 29 | 30 | 31 | # 使用方法 32 | 33 | */ 34 | 35 | def branch = purgeBranchString(coderepo.branch) 36 | 37 | pipeline { 38 | 39 | agent any 40 | 41 | options { 42 | disableConcurrentBuilds() 43 | skipDefaultCheckout true 44 | } 45 | 46 | stages { 47 | 48 | stage("Checkout Code") { 49 | steps { 50 | script { 51 | deleteDir() 52 | def gitParams = [ 53 | $class : 'GitSCM', 54 | branches : [[name: "${branch}"]], 55 | doGenerateSubmoduleConfigurations: false, 56 | extensions : [[$class: 'CleanBeforeCheckout']], 57 | submoduleCfg : [], 58 | userRemoteConfigs : [[url: "${coderepo.url}"]] 59 | ] 60 | if (coderepo.auth != null) { 61 | gitParams.userRemoteConfigs = [[credentialsId: "${coderepo.auth}", 62 | url : "${coderepo.url}"]] 63 | } 64 | checkout(gitParams) 65 | } 66 | } 67 | } // end: Checkout Code 68 | 69 | stage("Build Image by Docker Compose") { 70 | when { 71 | expression { build_method == "docker-compose" } 72 | } 73 | steps { 74 | script { 75 | // 检查关键文件是否存在 76 | if (!fileExists('docker-compose.yml')) { 77 | error( "--//ERR: 缺少docker-compose.yml文件") 78 | } 79 | 80 | def _docker_compose_j2 = """ 81 | |{% set version = "${branch}" %} 82 | |version: '3' 83 | |services: 84 | |{%- for name, _ in services.items() %} 85 | | {{ name }}: 86 | | {% if version == "main" %} 87 | | image: "${dockerpush.registry}/${dockerpush.namespace}/${dockerpush.image}:{{name}}" 88 | | {% else %} 89 | | image: "${dockerpush.registry}/${dockerpush.namespace}/${dockerpush.image}:{{name}}-{{ version }}" 90 | | {% endif %} 91 | |{%- endfor %} 92 | """.stripMargin().stripIndent() 93 | writeFile(file: 'docker-compose.j2', text: _docker_compose_j2) 94 | 95 | def _dockerfile = ''' 96 | FROM rockylinux:8 97 | RUN yum install -y python3 && pip3 install jinja2-cli[yaml] 98 | '''.stripIndent() 99 | writeFile(file: 'Dockerfile', text: _dockerfile) 100 | 101 | def jinja2Image = docker.build("rockylinux8:jinja2") 102 | jinja2Image.inside { 103 | sh """ 104 | set -eux 105 | jinja2 ./docker-compose.j2 ./docker-compose.yml > docker-compose.override.yml 106 | """ 107 | } 108 | 109 | docker.withRegistry("https://${dockerpush.registry}", "${dockerpush.auth}") { 110 | sh """ 111 | set -eux 112 | docker-compose -f docker-compose.yml -f docker-compose.override.yml build 113 | docker-compose -f docker-compose.yml -f docker-compose.override.yml push 114 | """ 115 | } 116 | } 117 | } 118 | } // end: Build Image by Docker Compose 119 | 120 | stage("Build Image by Docker") { 121 | when { 122 | expression { build_method == "docker" } 123 | } 124 | steps { 125 | script { 126 | if (!fileExists('Dockerfile')) { 127 | error( "--//ERR: 缺少 Dockerfile 文件") 128 | } 129 | docker.withRegistry("https://${dockerpush.registry}", "${dockerpush.auth}") { 130 | def _tag = branch 131 | if (branch == "master") { 132 | _tag = "latest" 133 | } 134 | def _image = docker.build("${dockerpush.registry}/${dockerpush.namespace}/${dockerpush.image}:${_tag}") 135 | _image.push() 136 | } 137 | } 138 | } 139 | } // end: Build Image by Docker 140 | 141 | } 142 | } 143 | 144 | // looks for string [ci skip] in commit message 145 | boolean getCiSkip() { 146 | sh(returnStdout: true, script: 'git show --pretty=%s%b -s', 147 | label : 'check skip CI?' 148 | ).toLowerCase().contains('[ci skip]') 149 | } 150 | 151 | String getGitCommit() { 152 | sh( 153 | returnStdout: true, script: 'git rev-parse HEAD', 154 | label : 'getting GIT commit' 155 | ).trim() 156 | } 157 | 158 | def purgeBranchString(branch) { 159 | def gitBranch = branch 160 | if (gitBranch?.startsWith("refs/heads/")) { 161 | gitBranch = gitBranch.replace("refs/heads/", "") 162 | if (gitBranch != "main") { 163 | error("--//INFO: 不支持除 main 之外的分支") 164 | } 165 | } 166 | if (gitBranch?.startsWith("refs/tags/")) { 167 | gitBranch = gitBranch.replace("refs/tags/", "") 168 | } 169 | return gitBranch 170 | } 171 | -------------------------------------------------------------------------------- /ci/lab03-jenkins/templates/general/Jenkinsfile: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | # 功能描述 4 | 5 | 构建基础镜像 6 | 7 | # 参数配置 8 | 9 | ```yaml 10 | 11 | coderepo: 12 | auth: xxx 13 | url: xxx 14 | branch: mkt4d 15 | 16 | registry: 17 | url: xxx 18 | auth: xxx 19 | 20 | pipeline: 21 | build: 22 | script: | 23 | # 配置环境变量 24 | # 使用 dapper 编译代码 25 | dapper -m bind 26 | 27 | archive: 28 | script: | 29 | # 打包镜像 30 | docker build -t $DOCKER_REG/xxx/xxx:version . -f Dockerfile 31 | docker push $DOCKER_REG/xxx/xxx:version 32 | 33 | deploy: 34 | image: xxx/xxx 35 | kubeconfig: xxx # credential id. 36 | script: | 37 | kubectl set image <app> <app>=$DOCKER_REG/xxx/xxx:version -n <group> 38 | kubectl rollout status --timeout=300 <app> -n <group> 39 | 40 | ``` 41 | 42 | # 插件依赖 43 | 44 | * https://www.jenkins.io/doc/pipeline/steps/workflow-basic-steps/ 45 | * https://plugins.jenkins.io/docker-workflow/ 46 | * https://www.jenkins.io/doc/book/pipeline/docker/ 47 | * https://docs.cloudbees.com/docs/admin-resources/latest/plugins/docker-workflow 48 | * https://github.com/opsbox-dev/oes-template-plugin 49 | 50 | 51 | # 使用方法 52 | 53 | 演示样例代码:https://jihulab.com/oes-workspace/spring-demo 54 | */ 55 | 56 | def branch = purgeBranchString(coderepo.branch) 57 | 58 | pipeline { 59 | 60 | agent any 61 | 62 | options { 63 | disableConcurrentBuilds() 64 | skipDefaultCheckout true 65 | } 66 | 67 | stages { 68 | 69 | stage("Checkout Code") { 70 | steps { 71 | script { 72 | deleteDir() 73 | def gitParams = [ 74 | $class : 'GitSCM', 75 | branches : [[name: "${branch}"]], 76 | doGenerateSubmoduleConfigurations: false, 77 | extensions : [[$class: 'CleanBeforeCheckout']], 78 | submoduleCfg : [], 79 | userRemoteConfigs : [[url: "${coderepo.url}"]] 80 | ] 81 | if (coderepo.auth != null) { 82 | gitParams.userRemoteConfigs = [[credentialsId: "${coderepo.auth}", 83 | url : "${coderepo.url}"]] 84 | } 85 | checkout(gitParams) 86 | } 87 | } 88 | } // end: Checkout Code 89 | 90 | stage("build") { 91 | steps { 92 | script { 93 | docker.withRegistry("https://${registry.url}", "${registry.auth}") { 94 | sh """ 95 | ${pipeline.build.script} 96 | """.stripIndent() 97 | } 98 | } 99 | } 100 | } 101 | 102 | stage("archive") { 103 | steps { 104 | script { 105 | docker.withRegistry("https://${registry.url}", "${registry.auth}") { 106 | sh """ 107 | export DOCKER_REG=${registry.url} 108 | ${pipeline.build.script} 109 | """.stripIndent() 110 | } 111 | } 112 | } 113 | } 114 | 115 | stage("deploy") { 116 | environment { 117 | KUBECONFIG = credentials("${pipeline.deploy.kubeconfig}") 118 | } 119 | steps { 120 | script { 121 | docker.withRegistry("https://${registry.url}", "${registry.auth}") { 122 | docker.image("${pipeline.deploy.image}").inside("-e KUBECONFIG=/root/.kube/config -v ${KUBECONFIG}:/root/.kube/config") { 123 | sh """ 124 | ${pipeline.deploy.script} 125 | """.stripIndent() 126 | } 127 | } 128 | } 129 | } 130 | } // end deploy stage. 131 | } 132 | } 133 | 134 | def purgeBranchString(branch) { 135 | def gitBranch = branch 136 | if (gitBranch?.startsWith("refs/heads/")) { 137 | gitBranch = gitBranch.replace("refs/heads/", "") 138 | if (gitBranch != "main") { 139 | error("--//INFO: 不支持除 main 之外的分支") 140 | } 141 | } 142 | if (gitBranch?.startsWith("refs/tags/")) { 143 | gitBranch = gitBranch.replace("refs/tags/", "") 144 | } 145 | return gitBranch 146 | } 147 | -------------------------------------------------------------------------------- /ci/lab05-tekton/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 云原生 CI/CD Tekton 3 | 4 | ![](media/16553854211360.jpg) 5 | 6 | tektōn 在古希腊语中有工匠、手艺人的意思,比如木匠、石匠、建筑工人。 7 | 8 | ## Tekton 介绍 9 | 10 | Tekton 是 Google 开源的 Kubernetes 原生 CI/CD 系统,功能强大扩展性强。前身是 Knative 里的 build-pipeline 项目,后期孵化成独立的项目。并成为 CDF 下的四大初始项目之一,其他三个是 Jenkins, Jenkins X, Spinnaker。 11 | 12 | ### 优势 13 | 14 | * 可定制 15 | * 可重用 16 | * 可扩展 17 | * 标准化 18 | * 可伸缩 19 | 20 | ### 概念 21 | 22 | * `Step`:CI/CD 工作流中的一个操作,比如编译 Java 程序、运行单元测试等等。 23 | * `Task`:有序 Step 的集合。Tekton 在 Kubernetes 的 Pod 中运行 `Task`,每个 `Step` 则对应 Pod 中的容器。如何 Pod 中的容器可以共享环境一样,`Task` 中的 `Step` 也可以彼此间共享数据。比如在 Pod 中挂在一个卷,各个容器都可以访问卷中的内容。 24 | * `Pipeline`:一些列有序 `Task` 的集合。Tekton 将 `Task` 组合成有序无环图(DAG),并按顺序执行。体现在 Kubernetes 中,Tekton 会按顺序依次创建 Pod 来执行 `Task`,并最终完成整个流水线的执行。 25 | * `PipelineRun`:Pipeline 承载流水线的定义,实际每次运行时都需要创建一个 `PipelineRun` 资源,指定要执行的流水线及其所需的入参。 26 | * `TaskRun`:是 `Task` 的执行。 27 | 28 | 29 | ![](media/16553864459332.png) 30 | 31 | ### CRD 32 | 33 | 为什么说 Tekton 是 Kubernetes 原生的,因为其基于 Kubernetes 的 CRD 定义了 Pipeline 流水线。 34 | 35 | * [`Tasks`](https://github.com/tektoncd/pipeline/blob/main/docs/tasks.md) 36 | * [`Pipeline`](https://github.com/tektoncd/pipeline/blob/main/docs/pipelines.md) 37 | * [`TaskRun`](https://github.com/tektoncd/pipeline/blob/main/docs/taskruns.md) 38 | * [`PipelineRun`](https://github.com/tektoncd/pipeline/blob/main/docs/pipelineruns.md) 39 | 40 | ### Tekton CRD VS Native Resource 41 | 42 | ![](media/16553856425843.jpg) 43 | 44 | ## 工作原理 45 | 46 | 从 `PipelineRun` 到 `TaskRun` 再到 Pod 和容器。 47 | 48 | ![tekton-concept](media/tekton-concept.jpg) 49 | 50 | 51 | 详细分析见[Tekton 的工作原理](https://atbug.com/how-tekton-works/) 52 | 53 | ## Tekton 生态 54 | 55 | ### 组件 56 | 57 | Tekton 包含了多个组件: 58 | 59 | * [Tekton Pipelines](https://github.com/tektoncd/pipeline/blob/main/docs/README.md) 60 | * [Tekton Triggers](https://github.com/tektoncd/triggers/blob/main/README.md) 61 | * [Tekton CLI](https://github.com/tektoncd/cli/blob/main/README.md) 62 | * [Tekton Dashboard](https://github.com/tektoncd/dashboard/blob/main/README.md) 63 | * [Tekton Catalog](https://github.com/tektoncd/catalog/blob/v1beta1/README.md) 64 | * [Tekton Hub](https://github.com/tektoncd/hub/blob/main/README.md) 65 | * [Tekton Operator](https://github.com/tektoncd/operator/blob/main/README.md) 66 | * [Tekton Results](https://github.com/tektoncd/results) 67 | 68 | ## 演示 69 | 70 | 既然 Tekton 是 Kubernetes 原生的框架,在正式开始之前需要创建一个 Kubernetes 集群。 71 | 72 | 在这个集群上我们会安装 Tekton,为了简化架构,在 CD 阶段会将应用也部署这个集群上(实际场景下,CI/CD 的集群基本不会与应用共享集群。当然,共享也没有问题)。 73 | 74 | 这个演示中我们会实现一个简单的 CI/CD 的流水线:完成一个 [Java 项目](https://github.com/addozhang/tekton-demo)从代码到部署的整个流程。 75 | 76 | 这个 Java 项目是个 web 服务,有一个 `/hi` 端点,返回 `hello world`。演示的重点是流水线的实现,所以选用了最简单的项目。 77 | 78 | ### 环境介绍 79 | 80 | * k3s v1.21.13+k3s1 81 | * 2c8g 虚拟机 Ubuntu 20.04 82 | * 本地 macOS 83 | 84 | ### 安装集群 85 | 86 | 我们使用 k3s 作为 Kubernetes 集群,通过下面的命令可以初始化单节点的集群。 87 | 88 | 这里我使用的是 2c8g 的 vm 作为节点,既是控制节点也是计算节点。 89 | 90 | ```shell 91 | export INSTALL_K3S_VERSION=v1.21.13+k3s1 92 | curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config 93 | ``` 94 | 95 | ### 安装 Tekton Pipeline 96 | 97 | 最新版本是 v0.36,从 v0.33.x 开始要求 Kubernetes 的版本至少是 1.21。 98 | 99 | ```shell 100 | kubectl apply --filename \ 101 | https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml 102 | ``` 103 | 104 | 检查相关的 CRD。 105 | 106 | ```shell 107 | kubectl api-resources --api-group=tekton.dev 108 | NAME SHORTNAMES APIVERSION NAMESPACED KIND 109 | clustertasks tekton.dev/v1beta1 false ClusterTask 110 | conditions tekton.dev/v1alpha1 true Condition 111 | pipelineresources tekton.dev/v1alpha1 true PipelineResource 112 | pipelineruns pr,prs tekton.dev/v1beta1 true PipelineRun 113 | pipelines tekton.dev/v1beta1 true Pipeline 114 | runs tekton.dev/v1alpha1 true Run 115 | taskruns tr,trs tekton.dev/v1beta1 true TaskRun 116 | tasks tekton.dev/v1beta1 true Task 117 | ``` 118 | 119 | 检查组件运行 120 | 121 | ```shell 122 | kubectl get po -n tekton-pipelines 123 | NAME READY STATUS RESTARTS AGE 124 | tekton-pipelines-controller-5cfb9b8cfc-q4crs 1/1 Running 0 24s 125 | tekton-pipelines-webhook-6c9d4d5798-7xg8n 1/1 Running 0 24s 126 | ``` 127 | 128 | ### 安装 Tekton CLI 129 | 130 | ```shell 131 | brew install tektoncd-cli 132 | ``` 133 | 134 | ### 安装 Tekton Dashboard 135 | 136 | 通过 Dashboard 我们可以实时查看 `PipelineRun` 和 `TaskRun` 的状态,以及运行的日志;还可以查看定义的各种 CR。 137 | 138 | ```shell 139 | kubectl apply --filename \ 140 | https://storage.googleapis.com/tekton-releases/dashboard/latest/tekton-dashboard-release.yaml 141 | ``` 142 | 143 | 创建 NodePort service 以便从集群外进行访问。 144 | 145 | ```shell 146 | kubectl expose deploy tekton-dashboard --name tekton-dashboard-node --port 9097 --target-port 9097 --type NodePort -n tekton-pipelines 147 | 148 | kubectl get svc tekton-dashboard-node -o jsonpath="{.spec.ports[0].nodePort}" -n tekton-pipelines 149 | ``` 150 | 151 | ### Hello, Tekton 152 | 153 | 创建 Task 154 | 155 | ```shell 156 | kubectl apply -f - <<EOF 157 | apiVersion: tekton.dev/v1beta1 158 | kind: Task 159 | metadata: 160 | name: hello-tekton 161 | spec: 162 | steps: 163 | - name: echo 164 | image: alpine 165 | script: | 166 | #!/bin/sh 167 | echo "Hello, Tekton" 168 | EOF 169 | ``` 170 | 171 | 运行 172 | 173 | ```shell 174 | kubectl apply -f - <<EOF 175 | apiVersion: tekton.dev/v1beta1 176 | kind: TaskRun 177 | metadata: 178 | name: hello-tekton-task-run 179 | spec: 180 | taskRef: 181 | name: hello-tekton 182 | EOF 183 | ``` 184 | 185 | ### Demo 186 | 187 | 我们使用Spring Initializer生成的项目为例, 演示如何使用 Tekton 实现 CICD. 188 | 189 | 开始之前简单整理下这个项目的 CICD 流程: 190 | 191 | 1. 拉取代码 192 | 2. maven 打包 193 | 3. 构建镜像并推送 194 | 4. 部署 195 | 196 | *注意:所有的操作都是在 `tekton-pipelines` namespace 下操作* 197 | 198 | #### 0x01 RBAC 199 | 200 | 用于 PipelineRun 运行的 service account。 201 | 202 | ```shell 203 | kubectl apply -f - <<EOF 204 | apiVersion: v1 205 | kind: ServiceAccount 206 | metadata: 207 | name: tekton-build 208 | namespace: tekton-pipelines 209 | 210 | --- 211 | apiVersion: rbac.authorization.k8s.io/v1 212 | kind: ClusterRoleBinding 213 | metadata: 214 | name: pipeline-admin-binding 215 | roleRef: 216 | apiGroup: rbac.authorization.k8s.io 217 | kind: ClusterRole 218 | name: admin # use cluster role admin 219 | subjects: 220 | - kind: ServiceAccount 221 | name: tekton-build 222 | namespace: tekton-pipelines 223 | EOF 224 | ``` 225 | 226 | #### 0x02 克隆代码 227 | 228 | ```shell 229 | kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.4/git-clone.yaml 230 | ``` 231 | 232 | 或者 233 | 234 | ```shell 235 | tkn hub install task git-clone 236 | ``` 237 | 238 | #### 0x03 maven 打包 239 | 240 | ```yaml 241 | spec: 242 | steps: 243 | - name: maven 244 | image: maven:3.5-jdk-8-alpine 245 | imagePullPolicy: IfNotPresent 246 | workingDir: $(workspaces.source.path) 247 | command: 248 | - mvn 249 | args: 250 | - clean 251 | - install 252 | - -DskipTests 253 | volumeMounts: 254 | - name: m2 255 | mountPath: /root/.m2 256 | volumes: 257 | - name: m2 258 | hostPath: 259 | path: /data/.m2 260 | ``` 261 | 262 | #### 0x04 构建并推送镜像 263 | 264 | 推送镜像到 Docker Hub,需要相关的登录凭证。 265 | 266 | kaniko 需要将 docker config 的文件存在于 /kanika/.docker 目录下。这里的思路是将 docker 的 config.json,以 secret 的方式持久化,在通过先添加 docker-registry类型的 secret,然后通过 workspace 的方式输入到 kaniko 运行环境中。 267 | 268 | ```shell 269 | kubectl create secret docker-registry dockerhub --docker-server=https://index.docker.io/v1/ --docker-username=$DOCKER_USERNAME --docker-password=$DOCKER_PASSWORD --dry-run=client -o json | jq -r '.data.".dockerconfigjson"' | base64 -d > /tmp/config.json && kubectl create secret generic docker-config --from-file=/tmp/config.json && rm -f /tmp/config.json 270 | ``` 271 | 272 | 构建镜像需要指定资源,比如 Dockerfile 的路径、镜像 URL、tag 等,通过 `params` 输入。 273 | 274 | ```yaml 275 | spec: 276 | params: 277 | - name: pathToDockerFile 278 | description: The path to the dockerfile to build (relative to the context) 279 | default: Dockerfile 280 | - name: imageUrl 281 | description: Url of image repository 282 | - name: imageTag 283 | description: Tag to apply to the built image 284 | default: latest 285 | - name: IMAGE 286 | description: Name (reference) of the image to build. 287 | steps: 288 | - name: build-and-push 289 | image: gcr.io/kaniko-project/executor:v1.6.0-debug 290 | imagePullPolicy: IfNotPresent 291 | command: 292 | - /kaniko/executor 293 | args: 294 | - --dockerfile=$(params.pathToDockerFile) 295 | - --destination=$(params.imageUrl):$(params.imageTag) 296 | - --context=$(workspaces.source.path) 297 | - --digest-file=$(results.IMAGE_DIGEST.path) 298 | ``` 299 | 300 | #### 0x05 部署 301 | 302 | 在部署阶段,也就是 CD 中的 delivery。我们将使用项目中的 yaml 文件,对应用进行部署。 303 | 304 | 前面提到,应用会部署到当前的集群中。部署成功后,我们可以通过访问 `http://[node-ip]:30080/hi` 来进行验证。 305 | 306 | ```yaml 307 | spec: 308 | params: 309 | - name: pathToYamlFile 310 | description: The path to the yaml file to deploy within the git source 311 | default: deployment.yaml 312 | workspaces: 313 | - name: source 314 | steps: 315 | - name: run-kubectl 316 | image: lachlanevenson/k8s-kubectl:v1.21.11 317 | imagePullPolicy: IfNotPresent 318 | command: ["kubectl"] 319 | args: 320 | - "apply" 321 | - "-f" 322 | - "$(workspaces.source.path)/$(params.pathToYamlFile)" 323 | ``` 324 | 325 | #### 0x06 组装流水线 326 | 327 | 在前面我们已经完成了流水线的各个 `step` 和 `task`,接下来就是将所有的 task 组装成真正的流水线 `Pipeline`。 328 | 329 | 在 `Pipeline` 中,我们设定流水线各个 `step` 所需的入参,并按照顺序将 `task` “摆放”。默认情况下这些 `task` 会同时执行,我们通过 `runAfter` 字段对执行顺序进行编排。 330 | 331 | ```yaml 332 | apiVersion: tekton.dev/v1beta1 333 | kind: Pipeline 334 | metadata: 335 | name: build-pipeline 336 | spec: 337 | params: 338 | - name: git-url 339 | - name: git-revision 340 | - name: pathToContext 341 | description: The path to the build context, used by Kaniko - within the workspace 342 | default: . 343 | - name: imageUrl 344 | description: Url of image repository 345 | - name: imageTag 346 | description: Tag to apply to the built image 347 | workspaces: 348 | - name: git-source 349 | - name: docker-config 350 | tasks: 351 | - name: fetch-from-git 352 | taskRef: 353 | name: git-clone 354 | params: 355 | - name: url 356 | value: "$(params.git-url)" 357 | - name: revision 358 | value: "$(params.git-revision)" 359 | workspaces: 360 | - name: output 361 | workspace: git-source 362 | - name: source-to-image 363 | taskRef: 364 | name: source-to-image 365 | params: 366 | - name: imageUrl 367 | value: "$(params.imageUrl)" 368 | - name: IMAGE 369 | value: "$(params.imageUrl)" 370 | - name: imageTag 371 | value: "$(params.imageTag)" 372 | workspaces: 373 | - name: source 374 | workspace: git-source 375 | - name: dockerconfig 376 | workspace: docker-config 377 | runAfter: 378 | - fetch-from-git 379 | - name: deploy-to-k8s 380 | taskRef: 381 | name: deploy-to-k8s 382 | params: 383 | - name: pathToYamlFile 384 | value: deployment.yaml 385 | workspaces: 386 | - name: source 387 | workspace: git-source 388 | runAfter: 389 | - source-to-image 390 | ``` 391 | 392 | #### 0x07 执行流水线 393 | 394 | 前面我们已经完成了流水线的定义,在执行的时候,需要通过定义 `PipelineRun` 来为其指定入参。比如这里的 `git-revision`、`git-url`、`imageUrl`、`imageTag`。 395 | 396 | 拉取代码和编译两个 `task` 的运行是在不同的 Pod 中完成的,因此需要出持久化的存储来进行数据(代码仓库)的共享。 397 | 398 | ```yaml 399 | apiVersion: tekton.dev/v1beta1 400 | kind: PipelineRun 401 | metadata: 402 | generateName: generic-pr- 403 | name: generic-pipeline-run 404 | spec: 405 | pipelineRef: 406 | name: build-pipeline 407 | params: 408 | - name: git-revision 409 | value: main 410 | - name: git-url 411 | value: https://github.com/addozhang/tekton-demo.git 412 | - name: imageUrl 413 | value: addozhang/tekton-test 414 | - name: imageTag 415 | value: latest 416 | workspaces: 417 | - name: git-source 418 | volumeClaimTemplate: 419 | spec: 420 | accessModes: 421 | - ReadWriteOnce 422 | resources: 423 | requests: 424 | storage: 1Gi 425 | - name: docker-config 426 | secret: 427 | secretName: docker-config 428 | serviceAccountName: tekton-build 429 | ``` 430 | 431 | ### 测试 432 | 433 | 执行下面的命令创建 `PipelineRun` 资源启动流水线。 434 | 435 | ```yaml 436 | kubectl apply -f run/run.yaml 437 | ``` 438 | 439 | 在执行的过程中,你会看到下面几个 Pod 被创建: 440 | 441 | * generic-pipeline-run-deploy-to-k8s-xxx 442 | * generic-pipeline-run-fetch-from-git-xxx 443 | * generic-pipeline-run-source-to-image-xxx 444 | 445 | 同时还有我们应用的 Pod `tekton-test-xxx`。 446 | 447 | 尝试发送请求到 `http://[node-ip]:30080/hi`,查看返回结果。 448 | 449 | ## 总结 450 | 451 | Tekton 是个很有意思的项目,其生态也在一步步的壮大,与此同时业界也不断涌现出各种周边的工具。由于时间原因,无法一一介绍。有兴趣的同学,可以看下我之前写过的文档。后续有时间,也希望能在这里继续给大家分享。 452 | 453 | * [CICD 的供应链安全工具 Tekton Chains](https://atbug.com/tekton-chains-secure-supply-chain/) 454 | * [Jenkins 如何与 Kubernetes 集群的 Tekton Pipeline 交互?](https://atbug.com/jenkins-interact-with-tekton-pipelines-via-plugin/) 455 | * [云原生CICD: Tekton Trigger 实战](https://atbug.com/tekton-trigger-practice/) 456 | 457 | CI/CD 平台是一件有挑战且充满乐趣的事情,在这个过程中我们会将现实世界中的工作流程以软件的方式实现出来。 458 | 459 | ![CI:CD流程](media/CI:CD%E6%B5%81%E7%A8%8B.png) 460 | 461 | 各家企业有自己独特的组织架构、管理制度,以及研发流程,即使是发展的不同阶段对平台也会有不同的需求。 462 | 463 | 平台的实现可以简单,也可以很复杂。 464 | 465 | ## FAQ 466 | 467 | 以下问题是直播时社区爱好者的提问,问题中有些工具/产品我过往没有使用或者了解,回答也是基于网络和产品官网的内容。如有问题和不足,欢迎指出。欢迎根据我们的回答继续深入讨论。 468 | 469 | ### 无情的工作机器啊:Tekton目前适合什么样的系统使用? 470 | 471 | Tekton 是个云原生的 CI/CD 框架,运行于 Kubernetes 环境。Tekton 是一个用于构建 CI/CD平台的框架,与其说什么样的系统适合 Tekton,不如说我们对 CI/CD 的平台有什么要求?Tekton 给我们带来的是扩展性、重用性、标准化、伸缩性等方面的优势。如果当面面临的是来自这些方面的问题,我认为 Tekton 是个不错的选择。 472 | 473 | 474 | ### bili_88058603179 : 与Jenkins动态slaver优势在哪? 475 | 476 | Tekton 的创建我觉得在两个方面流水线定义和基础设施资源的使用。在资源使用方面,Tekton 与 Jenkins 动态 slaver 大同小异,都是借助 Kubernetes 的弹性、自动化以及容器来实现动态和隔离。 477 | 478 | - Tekton 通过对流水线的定义,将可重用和标准化的功能粒度变得更小。比如一个流水线拆分成多个可重用 Task 来执行,在同一时间只有一个 Task(非并行)的 Pod 在运行,资源利用方面更精细。 479 | - Jenkins 动态 slaver 的优势在于不改变原有实现(Jenkins)的基础上(成本更低),让原有 CI/CD 平台的资源利用更加高效。 480 | 481 | 二者各有优势。 482 | 483 | ### barbaz : tekton和drone有什么区别? 484 | 485 | 我之前没有了解过 Drone,简单看了下。Drone 的实现与 Tekton 的原理都差不多,都是通过一个 runner/controller 来创建 Pod 来执行 Pipeline。 486 | 487 | 从概念上来看,Drone 的最小组件称为 `Step`,对应的是 Pod 中的 Container。再上一层是 `Pipeline`,对应 Pod。每个 `Pipeline` 都是运行在同一个 Pod 中。组件的重用是通过镜像来实现的。 488 | 489 | Tekton 的最小组件也是 `Step`,再上一层是 `Task`(对应 Kubernetes 中的 Pod)。每个 `Pipeline` 可以由一个或多个 `Task` 组件,也就是说运行时会由一个或者多个 Pod 来完成流水线的执行。重用可以通过 `Step` 使用的镜像,或者 `Task` 来实现功能的重用。 490 | 491 | 还有就是 Tekton 是开源的框架,而 Drone 的 runner 需要 [Drone Enterprice 许可](https://docs.drone.io/enterprise/#what-is-the-difference-between-open-source-and-enterprise)。 492 | 493 | ![Drone Enterprise 许可](media/2022-06-26%20at%2017.39.31.png) 494 | 495 | ### 没钱买鱼:有没有缓存加速编译的策略? 496 | 497 | 不知道我对“缓存加速编译”的理解是否正确,如不准确请指正。 498 | 499 | 就拿 Java 项目的编译来说,需要用到的缓存加速应该是各种依赖包。在演示中,我们使用了持久化存储来保存初次编译时下载的依赖包。流水线执行的过程中,这个持久化存储都会通过卷的方式挂在到 Pod 中,不会重新下载依赖包。其他语言也是类似。 500 | -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/pipeline/build-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: build-pipeline 5 | spec: 6 | params: 7 | - name: git-url 8 | - name: git-revision 9 | - name: pathToContext 10 | description: The path to the build context, used by Kaniko - within the workspace 11 | default: . 12 | - name: imageUrl 13 | description: Url of image repository 14 | - name: imageTag 15 | description: Tag to apply to the built image 16 | workspaces: 17 | - name: git-source 18 | - name: docker-config 19 | tasks: 20 | - name: fetch-from-git 21 | taskRef: 22 | name: git-clone 23 | params: 24 | - name: url 25 | value: "$(params.git-url)" 26 | - name: revision 27 | value: "$(params.git-revision)" 28 | workspaces: 29 | - name: output 30 | workspace: git-source 31 | - name: source-to-image 32 | taskRef: 33 | name: source-to-image 34 | params: 35 | - name: imageUrl 36 | value: "$(params.imageUrl)" 37 | - name: IMAGE 38 | value: "$(params.imageUrl)" 39 | - name: imageTag 40 | value: "$(params.imageTag)" 41 | - name: CHAINS-GIT_COMMIT 42 | value: "$(tasks.fetch-from-git.results.commit)" 43 | - name: CHAINS-GIT_URL 44 | value: "$(tasks.fetch-from-git.results.url)" 45 | 46 | workspaces: 47 | - name: source 48 | workspace: git-source 49 | - name: dockerconfig 50 | workspace: docker-config 51 | runAfter: 52 | - fetch-from-git 53 | - name: deploy-to-k8s 54 | taskRef: 55 | name: deploy-to-k8s 56 | params: 57 | - name: pathToYamlFile 58 | value: deployment.yaml 59 | workspaces: 60 | - name: source 61 | workspace: git-source 62 | runAfter: 63 | - source-to-image -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/run/run.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | # generateName: generic-pr- 5 | name: generic-pipeline-run 6 | spec: 7 | pipelineRef: 8 | name: build-pipeline 9 | params: 10 | - name: git-revision 11 | value: main 12 | - name: git-url 13 | value: https://github.com/addozhang/tekton-demo.git 14 | - name: imageUrl 15 | value: addozhang/tekton-test 16 | - name: imageTag 17 | value: latest 18 | workspaces: 19 | - name: git-source 20 | volumeClaimTemplate: 21 | spec: 22 | accessModes: 23 | - ReadWriteOnce 24 | resources: 25 | requests: 26 | storage: 1Gi 27 | - name: docker-config 28 | secret: 29 | secretName: docker-config 30 | serviceAccountName: tekton-build 31 | -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tekton-build 5 | namespace: tekton-pipelines 6 | 7 | --- 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRoleBinding 10 | metadata: 11 | name: pipeline-admin-binding 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: admin # use cluster role admin 16 | subjects: 17 | - kind: ServiceAccount 18 | name: tekton-build 19 | namespace: tekton-pipelines -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/tasks/deploy-to-k8s.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: deploy-to-k8s 5 | spec: 6 | params: 7 | - name: pathToYamlFile 8 | description: The path to the yaml file to deploy within the git source 9 | default: deployment.yaml 10 | workspaces: 11 | - name: source 12 | steps: 13 | - name: run-kubectl 14 | image: lachlanevenson/k8s-kubectl:v1.21.11 15 | imagePullPolicy: IfNotPresent 16 | command: ["kubectl"] 17 | args: 18 | - "apply" 19 | - "-f" 20 | - "$(workspaces.source.path)/$(params.pathToYamlFile)" -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/tasks/source-to-image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: source-to-image 5 | spec: 6 | params: 7 | - name: pathToDockerFile 8 | description: The path to the dockerfile to build (relative to the context) 9 | default: Dockerfile 10 | - name: imageUrl 11 | description: Url of image repository 12 | - name: imageTag 13 | description: Tag to apply to the built image 14 | default: latest 15 | - name: IMAGE 16 | description: Name (reference) of the image to build. 17 | - name: CHAINS-GIT_COMMIT 18 | description: Commit value 19 | - name: CHAINS-GIT_URL 20 | description: Git repo URL 21 | workspaces: 22 | - name: source 23 | - name: dockerconfig 24 | mountPath: /kaniko/.docker 25 | results: 26 | - name: IMAGE_DIGEST 27 | description: Digest of the image just built. 28 | - name: IMAGE_URL 29 | description: URL of the image just built. 30 | steps: 31 | - name: maven 32 | image: maven:3.5-jdk-8-alpine 33 | imagePullPolicy: IfNotPresent 34 | workingDir: $(workspaces.source.path) 35 | command: 36 | - mvn 37 | args: 38 | - clean 39 | - install 40 | - -DskipTests 41 | volumeMounts: 42 | - name: m2 43 | mountPath: /root/.m2 44 | - name: build-and-push 45 | image: gcr.io/kaniko-project/executor:v1.6.0-debug 46 | imagePullPolicy: IfNotPresent 47 | command: 48 | - /kaniko/executor 49 | args: 50 | - --dockerfile=$(params.pathToDockerFile) 51 | - --destination=$(params.imageUrl):$(params.imageTag) 52 | - --context=$(workspaces.source.path) 53 | - --digest-file=$(results.IMAGE_DIGEST.path) 54 | - name: write-url 55 | image: bash 56 | script: | 57 | set -e 58 | echo $(params.IMAGE) | tee $(results.IMAGE_URL.path) 59 | volumes: 60 | - name: m2 61 | hostPath: 62 | path: /data/.m2 63 | -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/trigger/event-listener.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: EventListener 3 | metadata: 4 | name: trigger-test-eventlistener 5 | namespace: tekton-pipelines 6 | spec: 7 | serviceAccountName: tekton-test 8 | triggers: 9 | - bindings: 10 | - name: trigger-test-triggerbinding 11 | template: 12 | name: trigger-test-triggertemplate -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/trigger/trigger-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: TriggerBinding 3 | metadata: 4 | name: trigger-test-triggerbinding 5 | namespace: tekton-pipelines 6 | spec: 7 | params: 8 | - name: gitrevision 9 | value: $(body.after) 10 | - name: namespace 11 | value: tekton-pipelines 12 | - name: gitrepositoryurl 13 | value: $(body.project.git_http_url) 14 | - name: projectname 15 | value: $(body.project.name) -------------------------------------------------------------------------------- /ci/lab05-tekton/demo/trigger/trigger-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: TriggerTemplate 3 | metadata: 4 | name: trigger-test-triggertemplate 5 | namespace: tekton-pipelines 6 | spec: 7 | params: 8 | - name: gitrevision 9 | description: The git revision 10 | default: master 11 | - name: gitrepositoryurl 12 | description: The git repository url 13 | - name: namespace 14 | description: The namespace to create the resources 15 | default: tekton-pielines 16 | - name: projectname 17 | description: The project name 18 | - name: imagetag 19 | description: The image tag 20 | default: latest 21 | resourcetemplates: 22 | - apiVersion: tekton.dev/v1alpha1 23 | kind: PipelineRun 24 | metadata: 25 | name: tekton-test-pipeline-run-$(uid) 26 | namespace: $(params.namespace) 27 | spec: 28 | serviceAccountName: tekton-test 29 | params: 30 | - name: imageUrl 31 | value: addozhang/$(params.projectname) 32 | - name: imageTag 33 | value: $(params.imagetag) 34 | pipelineRef: 35 | name: build-pipeline 36 | resources: 37 | - name: git-source 38 | resourceSpec: 39 | type: git 40 | params: 41 | - name: revision 42 | value: $(params.gitrevision) 43 | - name: url 44 | value: $(params.gitrepositoryurl) -------------------------------------------------------------------------------- /ci/lab05-tekton/media/16553854211360.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/16553854211360.jpg -------------------------------------------------------------------------------- /ci/lab05-tekton/media/16553856425843.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/16553856425843.jpg -------------------------------------------------------------------------------- /ci/lab05-tekton/media/16553864459332.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/16553864459332.png -------------------------------------------------------------------------------- /ci/lab05-tekton/media/16553875121264.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/16553875121264.jpg -------------------------------------------------------------------------------- /ci/lab05-tekton/media/2022-06-26 at 17.39.31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/2022-06-26 at 17.39.31.png -------------------------------------------------------------------------------- /ci/lab05-tekton/media/CI:CD流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/CI:CD流程.png -------------------------------------------------------------------------------- /ci/lab05-tekton/media/tekton-concept.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab05-tekton/media/tekton-concept.jpg -------------------------------------------------------------------------------- /ci/lab06-teamcity/README.md: -------------------------------------------------------------------------------- 1 | # 面向 DevOps 新手的 TeamCity 快速入门 2 | 3 | ![](images/teamcity-logo.png) 4 | 5 | ## 本教程学习材料 6 | 7 | * [视频](https://www.bilibili.com/video/BV1pY4y17754) 8 | * [PPT](https://docs.qq.com/pdf/DTmlIRFBJWllsaGJx) 9 | * [练习用代码仓库](https://github.com/shengyou/china-devops-teamcity-lab) 10 | 11 | ## TeamCity 简介 12 | 13 | * 由 JetBrains 推出的团队工具,协助团队做构建管理、持续集成及持续部署的工作。 14 | * 第一版发布于 2006 年,积累 JetBrains 内部超过 15 年构建及部署经验的产品。 15 | * TeamCity 可说是 JetBrains [Dogfooding](https://www.bilibili.com/video/BV1Dg411f7iX) 的代表,在早期没有 CI/CD 解决方案时,由团队自建而生的产品。 16 | 17 | ## 为什么选择 TeamCity? 18 | 19 | * 高颜值 Web UI,新手小白也能立即上手 20 | * 与 JetBrains IDE 深度集成,开发者不需离开 IDE 即可完成工作 21 | * 可用高语意、IDE 友好的 Kotlin DSL 描述设置,大规模设置更轻松 22 | 23 | ## 版本 24 | 25 | * TeamCity (on-premises) 26 | - 可至官网下载打包好的 Jar 文件,搭配 JVM 运行。 27 | - 可至 Docker Hub 下载官方发布的 Image,以 Docker 运行。 28 | - 提供不限用户、不限编译时间、100 个 Build Configuration、3 个 Agent 的免费额度。 29 | - 开源项目可申请免费授权。 30 | * TeamCity Cloud 31 | - TeamCity 团队提供的云服务。 32 | - 免安装、不限用户、并发运行、完全托管。 33 | - 提供 14 天免费试用 (中国 DevOps 社区可申请延长试用)。 34 | 35 | ## 安装 36 | 37 | * 以 Jar 文件安装 38 | - 安装 JDK 8 (推荐以 SDKMAN 安装)。 39 | ```shell 40 | # 安装 SDKMAN 41 | $ curl -s "https://get.sdkman.io" | bash 42 | 43 | # 安装 OpenJDK 8 44 | $ sdk install java 8.0.332-tem 45 | ``` 46 | - 至 [TeamCity 官网下载页](https://www.jetbrains.com/teamcity/download/#section=on-premises) 下载 Jar 文件,请选 Linux (.tar.gz) 文件下载。 47 | - 解压缩下载的文件,将解开的目录并放在 `/opt` 底下。 48 | - 开启终端,将目录切换到 TeamCity 文件夹,运行 `bin` 文件夹里的 `runAll` 脚本。 49 | ```shell 50 | $ cd <TeamCity 目录> 51 | $ bash ./bin/runAll.sh start 52 | ``` 53 | - 以浏览器开启 `http://localhost:8111/` 完成首次启用设置 54 | - 设置 Data 文件夹路径。 55 | ![](images/installation/post-install-setup-step1.png) 56 | - 设置数据库。 57 | ![](images/installation/post-install-setup-step2.png) 58 | - 同意使用条款。 59 | ![](images/installation/post-install-setup-step3.png) 60 | - 设置 admin 帐号密码。 61 | ![](images/installation/post-install-setup-step4.png) 62 | - 完成首次启用设置! 63 | ![](images/installation/post-install-setup-step5.png) 64 | * 以 Docker 运行 65 | - 安装 Docker (以 Ubuntu 为例)。 66 | ```shell 67 | # 设定 Repository 68 | $ sudo apt-get update 69 | $ sudo apt-get install \ 70 | apt-transport-https \ 71 | ca-certificates \ 72 | curl \ 73 | gnupg \ 74 | lsb-release 75 | 76 | # 设定 Docker 官方 GPG key 77 | $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 78 | 79 | # 设置 Stable Repository (以 AMD64 为例) 80 | $ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ 81 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 82 | 83 | # 安装 Docker 84 | $ sudo apt-get update 85 | $ sudo apt-get install docker-ce docker-ce-cli containerd.io 86 | ``` 87 | - 运行 TeamCity 容器。 88 | ```shell 89 | $ docker run -it --name teamcity-server-instance \ 90 | -v <path to data directory>:/data/teamcity_server/datadir \ 91 | -v <path to logs directory>:/opt/teamcity/logs \ 92 | -p <port on host>:8111 \ 93 | jetbrains/teamcity-server 94 | ``` 95 | - 以浏览器开启 `http://localhost:8111/` 完成首次启用设置 96 | - 设置 Data 文件夹路径。 97 | ![](images/installation/post-install-setup-step1.png) 98 | - 设置数据库。 99 | ![](images/installation/post-install-setup-step2.png) 100 | - 同意使用条款。 101 | ![](images/installation/post-install-setup-step3.png) 102 | - 设置 admin 帐号密码。 103 | ![](images/installation/post-install-setup-step4.png) 104 | - 完成首次启用设置! 105 | ![](images/installation/post-install-setup-step5.png) 106 | * 开通 TeamCity Cloud 107 | - 在 [TeamCity Cloud 登录页](https://www.jetbrains.com/teamcity/signup/)输入 Email 申请开通。 108 | - 收取邮件并点击邮件里的 URL。 109 | ![](images/installation/signup-teamcity-cloud-step1.png) 110 | - 在注册页面依表单输入个人信息。 111 | ![](images/installation/signup-teamcity-cloud-step2.png) 112 | - 设置 Instance URL。 113 | ![](images/installation/signup-teamcity-cloud-step3.png) 114 | - 设置 admin 帐号密码。 115 | ![](images/installation/signup-teamcity-cloud-step4.png) 116 | - 同意使用条款。 117 | ![](images/installation/signup-teamcity-cloud-step5.png) 118 | - 完成开通 TeamCity Cloud! 119 | ![](images/installation/signup-teamcity-cloud-step6.png) 120 | 121 | ## TeamCity 运作流程 122 | 123 | TeamCity 是一个 Server 搭配 Agent 的架构,在正式使用前,预先了解 Server 及 Agent 的架构与分工,有助于了解整体工作流程。TeamCity 的运作流程可用下图来说明: 124 | 125 | ![](images/workflow/teamcity-workflow.png) 126 | 127 | * 在 TeamCity 里创建项目后,TeamCity Server 会持续侦测 (Polling or Webhook) 代码仓库的变更。 128 | * 当 TeamCity 发现有变更时,会将变更信息储存在数据库里。 129 | * 当 Trigger 发现数据里的变更时,就会依照 Build Configuration 初始化构建。 130 | * 被 Trigger 的构建任务就会被放进 Queue 里。 131 | * 构建任务会被指派给一个闲置且符合构建条件的 Agent。 132 | * Agent 依照 Build Configuration 逐一运行,运行时会将生成的报告同步送回 Server。 133 | * 构建完成后,Agent 会将 Artifact 送回 Server 储存。 134 | 135 | ## 重要名词解释 136 | 137 | * **TeamCity Server**:TeamCity 的管理中心,负责监控所有连线到的 Agent,依照 Agent 的环境分配 Queue 里的构建任务并显示报告。 138 | * **Database**:储存包括代码仓库的变更、构建历史、Agent 及 Queue 信息、帐号及权限等信息。 139 | * **Build Agent**:实际运行构建的程序,依项目需求的不同,可以将 Agent 安装在不同平台、不同操作系统并搭配不同运行环境来运行。 140 | * **Build Configuration**:设置 VCS 来源、Trigger、Build Step 的组合。 141 | * **Build Step**:在构建过程中实际执行的任务,每一个步骤会以特定的 Runner 运行特定工具(比方说 Ant、Gradle、MSBuild、NUnit、静态分析)。 142 | * **Build Trigger**:Trigger 用于事件发生时,启动对应的构建任务。TeamCity 支持数种 Trigger,比方说 VCS Trigger 或 Timer Trigger 等。 143 | * **Build Queue**:储放已被 Trigger、等待被运行的任务清单,TeamCity 会将这些任务分配给符合运行条件且闲置的 Agent 运行。 144 | * **Build Artifact**:构建完成后产生的文件,比方说 Jar/War、报表、日志等,可依需求下载使用。 145 | 146 | ## 设计项目的发布流水线 147 | 148 | 不论项目再怎么复杂,为项目建立发布流水线的第一步,就是先用 **人工** 把所有构建任务运行一次,并将所有要运行的命令手写下来。本教程以一个用 Kotlin 编程语言撰写的 ShoppingCart 项目为例,流水线的设计如下: 149 | 150 | * 以 Gradle 运行构建并生成 Artifact 151 | * 以 kotest 运行测试并生成覆率报告 152 | * 以 ktlint 检查 Coding Style 153 | * 以 Qodana 和 detekt 运行静态分析 154 | * 以 Dokka 生成 API 文档 155 | * 部署 API 文档 156 | 157 | ## Lab 1:完成第一个构建 158 | 159 | 有了上面的蓝图后,接下来用三个 Lab 逐步以 TeamCity 将发布流水线搭建起来。第一步要先在 TeamCity 里完成第一个构建,确认项目可顺利编译。 160 | 161 | * 以 Admin 帐号登入 TeamCity,点选上方菜单最右边的 Administration 的链接进入设置,选择左侧栏菜单里的 Projects,点选右边的 Create project 按钮。 162 | ![](images/lab1/lab1-step1.png) 163 | * TeamCity 支持从 GitHub、Bitbucket Cloud、GitLab 引入代码仓库,或是提供 Repository URL 也行。练习时可直接使用笔者的[代码仓库](https://github.com/shengyou/shopping-cart),输入后按 Process。 164 | ![](images/lab1/lab1-step2.png) 165 | * 设置 Project Name (自动以 Repo 名称代入)、Build Configuration Name (默认为 Build)、Default Branch (默认为 main)。没有需求的话全以默认值代入即可。 166 | ![](images/lab1/lab1-step3.png) 167 | * 有了 Build Configuration 后,接着要设置 Build Step,也就是构建过程中要运行的任务步骤。TeamCity 会自动扫描 Repository 使用的开发工具,扫描后列出可运行的 Build Step。以演示项目为例,TeamCity 发现有两个动作可以做,一个是 Gradle、一个是 Command Line。我们只需要 Gradle 即可,勾选 Gradle 选项,点 Use selected 进到下一步。 168 | ![](images/lab1/lab1-step4.png) 169 | * 在构建完成后,要将生成的 Artifact 储存下来。回到 Build 的 General Settings 设置,在 Artifact paths 设置要储存的文件夹路径为 `+:build/libs => libs.zip`。 170 | ![](images/lab1/lab1-step5.png) 171 | * 完成后就可以点选划面右上方的 Run 按钮执行第一次的建置工作。 172 | ![](images/lab1/lab1-step6.png) 173 | * TeamCity 会自动跳转至构建运行的页面,我们可以即时看到 TeamCity 正在运行的任务,TeamCity 会以时间线显示构建过程,并可实时查看终端输出。若构建过程没有发生错误,则那这个建置就会被视为成功,您可以看到 TeamCity 会以绿色字及打勾的图标来表示构建结果。 174 | ![](images/lab1/lab1-step7.png) 175 | * 切换到 Artifact Tab,可以看到构建生成的 Artifact,可直接点击下载。 176 | ![](images/lab1/lab1-step8.png) 177 | * 由于 Gradle 在运行构建时,也会一并运行测试,可以在 Tests Tab 里看到该次构建运行测试的结果。 178 | ![](images/lab1/lab1-step9.png) 179 | 180 | ## Lab 2:依计划增加更多 Build Step 181 | 182 | 第二个 Lab 要依照流水线计划增加更多 Build Step,包括以 ktlint 检查 Coding Style,以 Qodana 和 detekt 运行静态分析,并为测试生成覆盖率报告。 183 | 184 | * 转至项目设置,进入 Build Step 设置,新增一个 Build Step。 185 | ![](images/lab2/lab2-step1.png) 186 | * 由于演示代码使用 Gradle 集成 ktlint,Runner Type 可使用 Gradle Runner。Step name 可命名为 `Check Code Style`,Gradle tasks 里填入要运行的命令 `lintKotlin`,其余留空按 Save 储存即可。 187 | ![](images/lab2/lab2-step2.png) 188 | * 再新增一个 Build Step,同样使用 Gradle Runner,Step name 命名为 `Static Analysis`,Gradle tasks 里填入要运行的命令 `detekt`,其余留空按 Save 储存即可。 189 | ![](images/lab2/lab2-step3.png) 190 | * 再新增一个 Build Step,改用 Qodana Runner,Step name 命名为 `Qodana Scan`,Tools 里选择 Code Inspection,Linter 选 Qodana for JVM,其余留空按 Save 储存即可。 191 | ![](images/lab2/lab2-step4.png) 192 | * 回到 Build Step 页面,编辑 Build 的设置,下方有一个 Code Coverage 的区块,把 Choose coverage runner 从 <No coverage> 改成 IntelliJ IDEA,而 Classes to instrument 则输入 `io.kraftsman.*`,表示只要是在 io.kraftsman 这个 Package 底下的所有 Class 都要生成覆盖率报告,完成后按 Save 储存。 193 | ![](images/lab2/lab2-step5.png) 194 | * 若有需要可以调整 Step 的顺序,完成后点击右上角 Run 按钮运行构建,并转至 Build 页面看构建结果。页面中间多了一个 Code Coverage 的 Tab,点击 Tab 就可以看到 TeamCity 把覆盖率报告显示在 Tab 内,不需要将生成的报告上传到其他服务器上,非常方便! 195 | ![](images/lab2/lab2-step6.png) 196 | ![](images/lab2/lab2-step7.png) 197 | ![](images/lab2/lab2-step8.png) 198 | 199 | ## Lab 3:生成并部署 API 文档 200 | 201 | 第三个 Lab 要依照流水线计算生成项目的 API 文档,并将文档部署至指定服务器。演示的代码仓库使用的是 Dokka 文件引擎,由于 Dokka 也有提供 Gradle 插件,因此在 TeamCity 里一样使用 Gradle Runner 即可生成文件。 202 | 203 | * 转至项目 Build Step 设置,新增一个 Step,Runner Type 选 Gradle、Step name 输入 `Generate Document`、Gradle tasks 输入 `dokkaHtml`,其他保留默认后按 Save 储存。 204 | ![](images/lab3/lab3-step1.png) 205 | * 虽然 Build Step 会生成文件,但别忘了要把文件变成 Artifact 的一部份。回到项目的 Build Configuration 设置页,选择左侧边栏的 General Settings,在 UI 下方 Artifact paths 里指定要保存的路径 `+:build/dokka/html => docs.zip`。 206 | ![](images/lab3/lab3-step2.png) 207 | 208 | 接下来,与前面构建的步骤不同,部署文件的任务不需要跟构建绑在一起,可以单独创建一个 Build Configuration,等需要部署时再「人工部署」。 209 | 210 | * 进入项目设置,在 General Settings 里,创建一个新的 Build Configuration。 211 | ![](images/lab3/lab3-step3.png) 212 | * 这次部署的来源不是原本的 VCS,所以改成选择 Manually 方式,并把这个 Build Configuration 取名为 Deploy document。 213 | ![](images/lab3/lab3-step4.png) 214 | * 下一步会转至 Deploy document 设置,这里想将前面 Build Configuration 生成的 Artifact 当成来源,因此要把上一个 Build Configuration 设为相依 (Dependencies)。点选左边侧边栏的 Dependencies,选择画面上的 Add new artifact dependency。 215 | ![](images/lab3/lab3-step5.png) 216 | * 在弹出式窗口里,设定 Depend on 为 Shopping Cart / Build,Get artifacts from 为 Latest successful build (上一次成功的构建),Artifacts rules 要包含要从 Artifact 拿出的文件路径 `+:docs.zip`,完成后按 Save 储存。 217 | ![](images/lab3/lab3-step6.png) 218 | * 切换到 Build Steps,并点击 Add build step。第一步先把 `docs.zip` 上传到目标主机上,Runner type 选 SSH Upload、Step name 取名为 Upload,Target、Port、Username 请依照目标服务设置,Paths to sources 则是设定要上传的文件,完成后按 Save 储存。 219 | ![](images/lab3/lab3-step7.png) 220 | * 上一步只把 API 文件的压缩档上传,还没办法让用户浏览。所以要搭配第二步动作,直接 SSH 进服务器,把压缩档解开放到 Nginx Site 底下。回到 Build Steps 设定再新增第二个 Build Step,Runner type 选 SSH Exec,Step name 取名为 Publish,Target、Port、Username 请依照目标服务器设置,Commands 就直接把在服务器上运行的 Shell Script 直接黏贴上去,设定好后按 Save 储存。(此步骤请依照自己的环境做设置,以下命令仅供参考) 221 | 222 | ```shell 223 | mv upload/docs.zip upload/docs-%build.number%.zip 224 | unzip upload/docs-%build.number%.zip -d upload/docs-%build.number% 225 | mv upload/docs-%build.number% docs/ 226 | ``` 227 | 228 | ![](images/lab3/lab3-step8.png) 229 | 230 | * 完成后回到首页,每次在 Build 那步的 API Docs Tab 确认没问题,想要部署 API 文件到服务器上时,就可以点选 Deploy document 旁的 Run,这样 TeamCity 就会把文件部署到服务器。 231 | ![](images/lab3/lab3-step9.png) 232 | 233 | ## 举一反三 234 | 235 | 由于演示代码是以 Kotlin 编写,所以在设计 Build Step 时,使用的构建工具都是以 Gradle 为核心。而 TeamCity 做为一个通用的持续集成服务器,并没有限制使用的构建工具,其支持多种 Build Runner,开发者可依据项目需求运行所需的构建工具,也可搭配 Docker 搭建运行环境。 236 | 237 | 以下依不同开发生态系举几个例子: 238 | 239 | * JVM 项目:使用 `Gradle`、`Maven` 或 `Ant` Runner 240 | * JavaScript 项目:使用 `Node.js` Runner 241 | * Python 项目:使用 `Python` Runner 242 | * PHP 项目:以 `Command Line` Runner 搭配 `composer` Docker 243 | 244 | 用于部署的 Runner: 245 | 246 | * 打包 Image:使用 `Docker` Runner 247 | * 直接上传:使用 `SSH Upload` 或 `FTP Upload` Runner 248 | * 直接运行命令:使用 `SSH Exec` Runner 249 | 250 | ## 集成 JetBrains IDE 251 | 252 | 开发者若使用 JetBrains IDE,可安装 [TeamCity 插件](https://plugins.jetbrains.com/plugin/1820-teamcity),在 IDE 内会多一个 TeamCity 窗口,登入帐号密码后,即可在 IDE 里查看 TeamCity 的 Build Log,不需离开 IDE、不需中断心流,更高效的完成工作。 253 | 254 | ![](images/integration/teamcity-plugin.png) 255 | 256 | ## 以 Kotlin DSL 描述设置 257 | 258 | TeamCity 也可用设置文件来描述 CI/CD 行为,大规模设置更轻松。但有别于其他解决方案,TeamCity 使用的语法是 Kotlin DSL 而不是 YAML,原因有二: 259 | 260 | * DSL 可依场景设计领域专用语言,让不是开发者的团队成员也能迅速理解并上手,降低维护难度。 261 | * Kotlin DSL 完全是 Kotlin 语法,不需要额外的语法检查器即可用 Kotlin 编译器码证语法,也可以直接在 IDE 做语法提示,编写代码时更安全。 262 | 263 | 新手不需要手动撰写 TeamCity 的 Kotlin DSL,可直接从 Web UI 导出: 264 | 265 | * 转至项目设置 General Settings,点击右上方 Actions 下拉菜单,选择 Download settings in Kotlin format...。 266 | ![](images/dsl/kotlin-dsl.png) 267 | * TeamCity 会将项目设置导出成 Zip 文件。 268 | * 将下载下来的 Zip 解压缩,把文件夹重新命名为 `.teamcity` 后,放在项目根文件夹底下。 269 | * 转至项目设置 Versioned Settings,点取 UI 上的 Synchronization enabled 并按下 Apply 按钮,未来 TeamCity 就会以项目内的设置文件运行设置,Web UI 变为只读。 270 | ![](images/dsl/versioned-settings.png) 271 | 272 | ## 延伸主题讨论 273 | 274 | 本教程以 DevOps 新手快速入门 TeamCity 为目标,以熟悉 TeamCity 基本操作为主。不过 TeamCity 还有更多特性可深入研究,以下列出进阶主题供同学参考: 275 | 276 | * **定制化 Agent**:若项目有用到特殊的环境或工具,可自行定制 Agent 后挂载至 TeamCity Server 上使用。或是定制项目所需的 Docker Image,在设置 Build Step 时使用指定的 Image 运行。 277 | * **调用不同 Trigger**:演示仅用到 VCS Trigger,TeamCity 还支持 Schedule Trigger、Branch Remote Run Trigger 等,可弹性组合以符合更种场景。 278 | * **设定 Build Feature**:TeamCity 支持在构建完成后触发更多动作,比方说合并 PR、自动提交等,可将更多流程自动化。 279 | * **与团队工具集成**:TeamCity 可与其他团队工具集成,比方说与市场上常见的 Issue Tracker 集成,或是与 JetBrains IDE 集成,让开发流程更顺畅。 280 | 281 | ## 官方学习材料 282 | 283 | TeamCity 团队提供丰富的文档供开发者自学,统整学习材料清单如下: 284 | 285 | * **[TeamCity 视频教程](https://www.youtube.com/playlist?list=PLQ176FUIyIUZVX0oSnlZh3mfqE3ZWDEZh)**:由 TeamCity 布道师 Marco 亲自录制的视频教程,是最高效的学习材料!若想学习 TeamCity,可先由这份教程入门。 286 | * **[官方教程](https://www.jetbrains.com/teamcity/tutorials/)**:若您比较喜欢主题式的学习,尤其是想了解如何将 TeamCity 应用于项目的开发生态系,那可以从这份教程开始,直接挑您想看的编程语言即可。 287 | * **[官方文档](https://www.jetbrains.com/help/teamcity/teamcity-documentation.html)**:TeamCity 团队提供完整的官方文档,遇到问题时可用关键字搜索对应的主题。 288 | * **[CI/CD 指南](https://www.jetbrains.com/teamcity/ci-cd-guide/)**:若您对 DevOps 的观念及名称有兴趣想深入,TeamCity 团队整理的这份 CI/CD 很值得做为学习时的补充读物。 289 | * **[TeamCity Technology Day](https://www.youtube.com/playlist?list=PLQ176FUIyIUal1FCy2F8KUgVU1ol0O9sU)** :TeamCity 团队在 2020 年尾举办了一场线上技术日,在这个 YouTube 播放清单里,有很多不同面向的 TeamCity 主题分享,可以挑自己有兴趣的主题来听。 290 | * **[TeamCity Cloud Launch Event](https://www.youtube.com/playlist?list=PLQ176FUIyIUYKymnmoMnzwjyA7jEc39nJ)**:喜欢使用 TeamCity Cloud 的小伙伴,请参考这个 Launch Event 的视频,可以更清楚 TeamCity Cloud 提供的功能与进阶功能。 291 | 292 | ## Q&A 293 | 294 | 1. Artifact Path 的语法要怎么写? 295 | 其语法为 `[+:]source [=> target]`。`+:` 指要包含后面的路径,`source` 指要生成压缩文件的路径,`=>` 指要生成压缩文件,`target` 指压缩文件的文件名。更多说明及用例可以参考[官方文档](https://www.jetbrains.com/help/teamcity/cloud/2022.06/configuring-general-settings.html#Artifact+Paths)。 296 | 297 | 2. 视频里演示时,IDE 的进度条效果是怎么做出来的? 298 | 这是安装了一个名为 [Unicorn Progress Bar](https://plugins.jetbrains.com/plugin/18271-unicorn-progress-bar) 的插件,在 IDE 安装后就能有一样的效果。 299 | -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/dsl/kotlin-dsl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/dsl/kotlin-dsl.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/dsl/versioned-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/dsl/versioned-settings.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/post-install-setup-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/post-install-setup-step1.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/post-install-setup-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/post-install-setup-step2.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/post-install-setup-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/post-install-setup-step3.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/post-install-setup-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/post-install-setup-step4.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/post-install-setup-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/post-install-setup-step5.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step1.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step2.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step3.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step4.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step5.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/installation/signup-teamcity-cloud-step6.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/integration/teamcity-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/integration/teamcity-plugin.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step1.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step2.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step3.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step4.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step5.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step6.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step7.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step8.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab1/lab1-step9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab1/lab1-step9.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step1.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step2.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step3.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step4.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step5.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step6.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step7.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab2/lab2-step8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab2/lab2-step8.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step1.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step2.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step3.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step4.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step5.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step6.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step7.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step8.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/lab3/lab3-step9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/lab3/lab3-step9.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/teamcity-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/teamcity-icon.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/teamcity-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/teamcity-logo.png -------------------------------------------------------------------------------- /ci/lab06-teamcity/images/workflow/teamcity-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/ci/lab06-teamcity/images/workflow/teamcity-workflow.png -------------------------------------------------------------------------------- /config/lab01-ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | # 应该避免本文件在当前目录中存在,这样不安全 2 | [defaults] 3 | host_key_checking = false 4 | inventory = ./hosts.ini 5 | command_warnings=False 6 | deprecation_warnings=False 7 | roles_path = ./roles 8 | nocows = 1 9 | retry_files_enabled = False 10 | 11 | [ssh_connection] 12 | control_path = %(directory)s/%%h-%%p-%%r 13 | pipelining = True -------------------------------------------------------------------------------- /config/lab01-ansible/app-stack.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 在所有应用服务器上做配置和部署 3 | - hosts: app 4 | become: true 5 | tasks: 6 | - name: Config NTP Server # 配置 ntp 服务器 7 | yum: 8 | name: chrony 9 | state: present 10 | - name: Start NTP service # 启动 chronyd 服务 11 | service: 12 | name: chronyd 13 | state: started 14 | enabled: yes 15 | - name: Install Python3-pip&git 16 | yum: 17 | name: python3-pip,git 18 | state: present 19 | - name: Install django package 20 | pip: 21 | name: django<4 22 | state: present 23 | - name: Deploy from github 24 | git: 25 | repo: https://github.com/martinliu/hellodjango.git 26 | dest: /opt/hello 27 | update: yes 28 | - name: enable app 8000 port 29 | firewalld: 30 | port: 8000/tcp 31 | permanent: yes 32 | state: enabled 33 | - name: Reload Firewalld service 34 | service: 35 | name: firewalld 36 | state: reloaded 37 | enabled: yes 38 | - name: Start my hello app 39 | command: sh /opt/hello/run-hello.sh 40 | 41 | # 在所有所有服务器上做配置和部署 42 | - hosts: db 43 | become: true 44 | tasks: 45 | - name: Install Mariadb Server 46 | yum: 47 | name: mariadb-server,python3-PyMySQL 48 | state: present 49 | - name: Start DB service 50 | service: 51 | name: mariadb 52 | state: started 53 | enabled: yes 54 | - name: Install firewalld 55 | yum: 56 | name: firewalld 57 | state: present 58 | - name: Start Firewalld service 59 | service: 60 | name: firewalld 61 | state: started 62 | enabled: yes 63 | - name: Open the db port 64 | firewalld: 65 | port: 3306/tcp 66 | permanent: yes 67 | state: enabled -------------------------------------------------------------------------------- /config/lab01-ansible/hosts.ini: -------------------------------------------------------------------------------- 1 | # 应用服务器组 2 | [app] 3 | 192.168.31.165 4 | 192.168.31.124 5 | 6 | # 数据库服务器组 7 | [db] 8 | 192.168.31.58 9 | 10 | # 名为 localvm 的嵌套组 11 | [localvm:children] 12 | app 13 | db 14 | 15 | # 给嵌套组定义变量,应用于所有服务器 16 | [localvm:vars] 17 | ansible_user=sysops 18 | ansible_ssh_private_key_file=~/.ssh/id_rsa 19 | ansible_ssh_common_args='-o StrictHostKeyChecking=no' -------------------------------------------------------------------------------- /config/lab01-ansible/img/192-1928992_order-66.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab01-ansible/img/192-1928992_order-66.jpg -------------------------------------------------------------------------------- /config/lab01-ansible/img/1_BORbGnI7OfdUdsCk7URXKg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab01-ansible/img/1_BORbGnI7OfdUdsCk7URXKg.png -------------------------------------------------------------------------------- /config/lab01-ansible/img/1_N2yWueFgHi6M_lQGGsnxVQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab01-ansible/img/1_N2yWueFgHi6M_lQGGsnxVQ.png -------------------------------------------------------------------------------- /config/lab01-ansible/img/ansible-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab01-ansible/img/ansible-wide.png -------------------------------------------------------------------------------- /config/lab01-ansible/img/t8redb90cc631.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab01-ansible/img/t8redb90cc631.jpg -------------------------------------------------------------------------------- /config/lab01-ansible/init-users.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localvm 3 | become: true 4 | # 引用变量文件 5 | vars_files: 6 | - vars/default.yml 7 | tasks: 8 | # Sudo 用户组配置 9 | - name: Make sure we have a 'wheel' group 10 | group: 11 | name: wheel 12 | state: present 13 | # 允许 'wheel' 组里的用户执行sudo可以不输入用户密码 14 | - name: Make sudo command without password 15 | lineinfile: 16 | path: /etc/sudoers 17 | state: present 18 | regexp: '^%wheel' 19 | line: '%wheel ALL=(ALL) NOPASSWD: ALL' 20 | validate: '/usr/sbin/visudo -cf %s' 21 | 22 | # 创建远程命令执行的用户,并配置ssh密钥 23 | - name: Create a new regular user with sudo privileges 24 | user: 25 | name: "{{ create_user }}" 26 | state: present 27 | groups: wheel 28 | append: true 29 | create_home: true 30 | shell: /bin/bash 31 | # 将本地 ssh 公钥注入远程授权访问秘钥文件 32 | - name: Set authorized key for remote user 33 | authorized_key: 34 | user: "{{ create_user }}" 35 | state: present 36 | key: "{{ copy_local_key }}" -------------------------------------------------------------------------------- /config/lab01-ansible/inventory.v1: -------------------------------------------------------------------------------- 1 | # 应用服务器组 2 | [app] 3 | 192.168.31.165 4 | 192.168.31.124 5 | 6 | # 数据库服务器组 7 | [db] 8 | 192.168.31.58 9 | 10 | # 名为 localvm 的嵌套组 11 | [localvm:children] 12 | app 13 | db 14 | 15 | # 给嵌套组定义变量,应用于所有服务器 16 | [localvm:vars] 17 | ansible_user=root 18 | ansible_password='devops1234' -------------------------------------------------------------------------------- /config/lab01-ansible/inventory.v2: -------------------------------------------------------------------------------- 1 | # 应用服务器组 2 | [app] 3 | 192.168.31.165 4 | 192.168.31.124 5 | 6 | # 数据库服务器组 7 | [db] 8 | 192.168.31.58 9 | 10 | # 名为 localvm 的嵌套组 11 | [localvm:children] 12 | app 13 | db 14 | 15 | # 给嵌套组定义变量,应用于所有服务器 16 | [localvm:vars] 17 | ansible_user=sysops 18 | ansible_ssh_private_key_file=~/.ssh/id_rsa 19 | ansible_ssh_common_args='-o StrictHostKeyChecking=no' -------------------------------------------------------------------------------- /config/lab01-ansible/readme.md: -------------------------------------------------------------------------------- 1 | # Ansible 新手入门指南 2 | 3 | ![ansible-wide](https://user-images.githubusercontent.com/582423/166117795-a7c0d761-f9f5-40e0-9d85-7bfb9af404b0.png) 4 | 5 | [【查看 B 站视频】](https://www.bilibili.com/video/BV1Uv4y1K7u9) 6 | 7 | ## Ansible 的发音? 8 | 9 | ![Starwar](https://user-images.githubusercontent.com/582423/166117837-19f53d31-98e4-471b-9a31-3c264fcb79b4.jpg) 10 | 11 | 12 | ## Ansible 究竟是何物? 13 | 14 | [维基百科](https://en.wikipedia.org/wiki/Ansible_(software)):术语 "ansible "是乌苏拉-K-勒古恩在她1966年的小说《罗卡农的世界》中创造的,它指的是虚构的即时通信系统。是一类虚构的设备或技术,能够进行近乎瞬时或比光速更快的通信。 15 | 16 | ## 创始人小传 17 | 18 | 关于 Ansible 的小故事:“摸鱼创业成功的经典案例!”,看下 Founder, CEO & Chairman - Ansible 的 LinkedIn 你就懂了。 19 | 20 | * https://www.linkedin.com/in/saidziouani/ 21 | * https://www.linkedin.com/in/timothy-gerla/ 22 | * https://www.linkedin.com/in/michaeldehaan/ 23 | 24 | 25 | ![then push me back in](https://user-images.githubusercontent.com/582423/166117869-ae72ad01-5977-4557-a147-496a09389409.jpg) 26 | 27 | 28 | ## 推荐学习文档 29 | 30 | Ansible 简介文档: 31 | 32 | * https://docs.ansible.com/ansible/latest/index.html 33 | * https://en.wikipedia.org/wiki/Ansible_(software) 34 | * https://www.ansible.com/hubfs//AnsibleFest%20ATL%20Slide%20Decks/Getting%20Started%20with%20Ansible%20-%20Jake.pdf 35 | * https://aap2.demoredhat.com/decks/ansible_best_practices.pdf 36 | * https://www.ansible.com/hubfs/Webinar%20PDF%20slides/%5BWIP%5D%20MBU%20_%20ANA%20_%20Webinar%20-%20Ansible%20Network%20Meta%20Collection.pdf 37 | 38 | Ansible 和其他几种同类工具的对比: 39 | 40 | ![工具的对比](https://user-images.githubusercontent.com/582423/166117891-d0d288f6-ada3-467b-ab06-c22992b5bff3.png) 41 | 42 | Ansible 最简化架构图: 43 | 44 | ![架构图](https://user-images.githubusercontent.com/582423/166117928-55190fb0-0105-4cfb-95ed-027ed54a7539.png) 45 | 46 | 47 | ## 安装 Ansible 48 | 49 | 本教程推荐使用 `pip` 在 Fedora 35 上安装 Ansible。 50 | 51 | ### 环境说明 52 | 53 | 推荐使用 4 个虚拟机的学习环境,本教程模拟的是上面的架构图中的效果。 54 | 55 | 控制器 - Contorler 【Master】: 56 | 57 | * os : Fedora 35 58 | * ip :192.168.31.30 59 | 60 | 被管理服务器 - Hosts 【node】 61 | 62 | * 本地 Fedora 35 虚拟机 63 | * app1 : 192.168.31.165 64 | * app2 :192.168.31.124 65 | * db :192.168.31.58 66 | 67 | 以上是建议的最佳学习环境配置。而最低配置可以是:本地笔记本电脑(Win/macOS) + 一个虚拟机(Linux)所组成的开发环境。 68 | 69 | 在本地安装开发环境:以操作系统版本 macOS 12.3 (21E230) 的安装配置过程为例,其中第四个步骤需要按实际情况修改路径。 70 | 71 | 步骤: 72 | 73 | 1. 先安装 Python3 (步骤省略) 74 | 2. 确认 Python3 的版本:`python3 --version` 75 | 3. 运行 ` pip3 install ansible` 76 | 4. 在 shell 的环境配置文件加入 Python 的可执行文件路径。例如:使用 zsh 的系统在 ~.zshrc 文件中加入这一行 `export PATH="$PATH:/Users/martinliu/Library/Python/3.8/bin"`,不同的用户名和 Python 版本对应这里的路径不同。 77 | 5. 让配置文件生效,运行 `source ~.zshrc` 78 | 6. 验证 Ansible 安装的版本,运行 `ansible --version` 79 | 80 | 如果你使用的是 Windows 操作系统,请使用 Windows 10/11 的版本自带的 Windows Subsystem for Linux 功能,在 Linux 的子系统里完成以上的操作。 81 | 82 | ### 在 Fedora 35 上安装 【生产环境】 83 | 84 | 本教程推荐用` pip` 安装 Ansible 的方式。同时下面也提供了用 `dnf` 的安装方法。 85 | 86 | #### pip 安装 87 | 88 | 下面,我们在 Master (controler)上安装部署 Ansble 工具集。 89 | 90 | 为了安装到 Ansible 的最新版本,还需要先升级 python 的版本到 >= 3.8;目前 Fedora 35 自带的Python 版本为 3.10.1,已经满足了 Ansible 对 Python 的最低版本需求。 91 | 92 | 步骤如下: 93 | 94 | 1. 安装必要的软件包,运行命令 `dnf install python3-pip sshpass git -y` 95 | 2. 升级 `pip3` 用 `python3 -m pip install --upgrade pip` 96 | 3. 切换到非 root 用户 (如果有的话 ) 97 | 4. 先设置国内的 pypi 安装源 `pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple` 98 | 5. 用 `pip3` 安装 Ansible , 运行 `pip3 install ansible` 99 | 6. 验证 Ansible 安装的版本,运行 `ansible --version` 100 | 101 | #### dnf 安装 102 | 103 | 在控制器 (master)上安装部署 Ansble 工具集。 104 | 105 | 步骤如下: 106 | 107 | 1. 运行命令 `dnf install ansible -y` 108 | 2. 验证 Ansible 安装的版本,运行 `ansible --version` 109 | 3. 安装必要的软件包 `dnf install sshpass git -y` 110 | 111 | dnf 安装到的版本会稍微老一些,没有 pip 安装的版本新。 112 | 113 | 而在生产中,往往需要安装特定的某个 Ansible 版本,而非当前的最新版本。 114 | 115 | ## 配置 Ansble 运行环境 116 | 117 | 本教程演示和讲解的是将 4 个空白无任何配置的服务器配置为:单一控制器,三个被管理节点的运行环境。 118 | 119 | ### 初始化控制器 120 | 121 | #### 创建 SSH 无密码访问秘钥对 122 | 123 | Ansible 的控制器(Master)通过 SSH 访问和管理被管理的节点,并完成系统、服务配置工作。 124 | 125 | 首先,在控制器上生成 ssh 访问的秘钥对。ssh 登陆到控制器,最好切换到非root用户,执行 `ssh-keygen` 命令,如果不需要秘钥文件的密码,则需要输入一系列回车即可;新创建 ssh 的密钥对,用于无密码访问其它三个被服务器节点。 126 | 127 | 创建的测试用秘钥对,查看所在位置: 128 | 129 | ```sh 130 | [martin@ctl ~]$ ssh-keygen 131 | [martin@ctl ~]$ ls ~/.ssh 132 | id_rsa id_rsa.pub 133 | ``` 134 | 135 | 尝试使用密码的访问其它被管理服务器,确认你拥有所有正确的密码。(如果你已经有一个统一的访问秘钥,请忽略此步骤) 136 | 137 | #### 配置 ansible.cfg 基础配置文件 138 | 139 | Ansible 控制器节点的执行引擎的行为特性的配置文件是 `ansible.cfg` 文件,对此文件路径的搜索顺序如下: 140 | 141 | 1. ANSIBLE_CONFIG (环境变量中) 142 | 2. ansible.cfg (当前目录中) 。 143 | 3. ~/.ansible.cfg (当前用户的 home 目录下) 144 | 4. /etc/ansible/ansible.cfg (操作系统的路径)- 本教程中使用的方式 145 | 146 | 在当前用户的` Home `目录中创建一个内容如下的 `.ansible.cfg` 配置文件 (注意是以句点开头的隐藏文件) 147 | 148 | ```yml 149 | [defaults] 150 | host_key_checking = false 151 | inventory = ./hosts.ini 152 | command_warnings = False 153 | deprecation_warnings = False 154 | roles_path = ./roles 155 | nocows = 1 156 | retry_files_enabled = False 157 | 158 | [ssh_connection] 159 | control_path = %(directory)s/%%h-%%p-%%r 160 | pipelining = True 161 | ``` 162 | 163 | #### 编写主机清单文件 - Inventory 164 | 165 | 主机清单文件就是所谓的 “Inventory”,它是描述所有被管理节点的数据文件。 166 | 167 | 创建内容如下的 `inventory.v1` 配置文件 168 | 169 | ```yml 170 | # 组织方式:功能、地域、环境 171 | # 应用服务器组 172 | [app] 173 | 192.168.31.165 174 | 192.168.31.124 175 | 176 | # 数据库服务器组 177 | [db] 178 | 192.168.31.58 179 | 180 | # 名为 localvm 的嵌套组 181 | [localvm:children] 182 | app 183 | db 184 | 185 | # 给嵌套组定义变量,应用于所有服务器 186 | [localvm:vars] 187 | ansible_user=root 188 | ansible_password='devops1234' 189 | 190 | ``` 191 | 192 | 其他可用的定义方法详见:https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html 193 | 194 | 195 | 执行首个 Ansible 命令 `ansible -i inventory.v1 all -m ping` ,如果能正常使用 inventory 文件中的用户名和密码登录所有被管理节点的话,改命令的结果应该如下: 196 | 197 | ```sh 198 | [martin@ctl live]$ ansible -i inventory.v1 all -m ping 199 | 192.168.31.58 | SUCCESS => { 200 | "ansible_facts": { 201 | "discovered_interpreter_python": "/usr/bin/python3" 202 | }, 203 | "changed": false, 204 | "ping": "pong" 205 | } 206 | 192.168.31.165 | SUCCESS => { 207 | "ansible_facts": { 208 | "discovered_interpreter_python": "/usr/bin/python3" 209 | }, 210 | "changed": false, 211 | "ping": "pong" 212 | } 213 | 192.168.31.124 | SUCCESS => { 214 | "ansible_facts": { 215 | "discovered_interpreter_python": "/usr/bin/python3" 216 | }, 217 | "changed": false, 218 | "ping": "pong" 219 | } 220 | ``` 221 | 222 | #### 创建 Ansible 专用的用户 223 | 224 | 在拥有所有节点 root 访问的基础上,在所有被管理节点上进一步配置一个 Ansible 专用的账号。 225 | 226 | inventory.v1 配置文件中携带密码是很危险的,下面使用 Ansible 来解决这个问题,并配置好所有的被管理节点。 227 | 228 | 编写 Playbook 实现下面的工作: 229 | 230 | * 创建一个 Ansible 专用的名为 `sysops` 的用户账号 (不建议使用 root 用户,而是非 root 用户) 231 | * 将其配置为 sudo 提权限并不需要密码的用户。 232 | * 将控制器当前用户新创建的秘钥对的公钥配置部署到所有被管理节点的 sysops 的授权用户中。 233 | 234 | 235 | 创建第一个 Playbook `init-users.yml` 。 236 | 237 | * 使用到的Ansible模块有 group, lineinfile, user 和 authorized_key 238 | * 引用了变量文件 239 | 240 | 文件的内容如下: 241 | 242 | ```yml 243 | --- 244 | - hosts: localvm 245 | become: true 246 | # 引用变量文件 247 | vars_files: 248 | - vars/default.yml 249 | tasks: 250 | # Sudo 用户组配置 251 | - name: Make sure we have a 'wheel' group 252 | group: 253 | name: wheel 254 | state: present 255 | # 允许 'wheel' 组里的用户执行sudo可以不输入用户密码 256 | - name: Set sudo without password 257 | lineinfile: 258 | path: /etc/sudoers 259 | state: present 260 | regexp: '^%wheel' 261 | line: '%wheel ALL=(ALL) NOPASSWD: ALL' 262 | validate: '/usr/sbin/visudo -cf %s' 263 | 264 | # 创建远程命令执行的用户,并配置ssh密钥 265 | - name: Create a new regular user with sudo privileges 266 | user: 267 | name: "{{ create_user }}" 268 | state: present 269 | groups: wheel 270 | append: true 271 | create_home: true 272 | shell: /bin/bash 273 | 274 | - name: Set authorized key for remote user 275 | authorized_key: 276 | user: "{{ create_user }}" 277 | state: present 278 | key: "{{ copy_local_key }}" 279 | ``` 280 | 281 | 这个文件引用了一个变量文件 `vars/default.yml` ,下面创建这个目录和文件,文件的内容如下: 282 | 283 | ```yml 284 | create_user: sysops 285 | copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}" 286 | ``` 287 | 288 | 下一步执行这个 Playbook 完成被管理节点的初始化配置 :`ansible-playbook -i inventory.v1 init-users.yml` ,结果如下: 289 | 290 | ```sh 291 | [martin@ctl live]$ ansible-playbook -i inventory.v1 init-users.yml 292 | 293 | PLAY [localvm] ********************************************************************************* 294 | 295 | TASK [Gathering Facts] ************************************************************************* 296 | ok: [192.168.31.124] 297 | ok: [192.168.31.165] 298 | ok: [192.168.31.58] 299 | 300 | TASK [Make sure we have a 'wheel' group] ******************************************************* 301 | ok: [192.168.31.165] 302 | ok: [192.168.31.58] 303 | ok: [192.168.31.124] 304 | 305 | TASK [允许 'wheel' 组里的用户执行sudo可以不输入用户密码] *************************************** 306 | changed: [192.168.31.58] 307 | changed: [192.168.31.124] 308 | changed: [192.168.31.165] 309 | 310 | TASK [Create a new regular user with sudo privileges] ****************************************** 311 | changed: [192.168.31.165] 312 | changed: [192.168.31.124] 313 | changed: [192.168.31.58] 314 | 315 | TASK [Set authorized key for remote user] ****************************************************** 316 | changed: [192.168.31.124] 317 | changed: [192.168.31.58] 318 | changed: [192.168.31.165] 319 | 320 | PLAY RECAP ************************************************************************************* 321 | 192.168.31.124 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 322 | 192.168.31.165 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 323 | 192.168.31.58 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 324 | ``` 325 | 326 | `ansible-playbook` 命令是解析和执行 Playbook 脚本文件的命令。我们可以看到 Playbook 是一个 yml 格式的状态声明式定义文件。它描述了被管理节点的操作系统中的各种配置细节。 327 | 328 | 在控制器上验证无密码SSH密钥认证登陆,选取任何一个被管理节点的 ip 地址,执行 `ssh sysops@192.168.31.124`;登录后查看 .ssh 目录中的内容。 329 | 330 | 在所有host上我们配置好了一个Ansible专用的无sudo密码的普通用户。优化 inventory.v1 文件,删除其中的用户名和密码,创建内容如下的 inventory.v2 文件: 331 | 332 | ```yml 333 | # 组织方式:功能、地域、环境 334 | # 应用服务器组 335 | [app] 336 | 192.168.31.165 337 | 192.168.31.124 338 | 339 | # 数据库服务器组 340 | [db] 341 | 192.168.31.58 342 | 343 | # 名为 localvm 的嵌套组 344 | [localvm:children] 345 | app 346 | db 347 | 348 | # 给嵌套组定义变量,应用于所有服务器 349 | [localvm:vars] 350 | ansible_user=sysops 351 | ansible_ssh_private_key_file=~/.ssh/id_rsa 352 | ansible_ssh_common_args='-o StrictHostKeyChecking=no' 353 | ``` 354 | 355 | 最后用首个执行的 Ansible 命令 `ansible -i inventory.v2 all -m ping` 验证所有主机是否可以用SSH正常访问,确认得到全绿的输出结果。 356 | 357 | ```sh 358 | [martin@ctl live]$ ansible -i inventory.v1 all -m ping 359 | 192.168.31.58 | SUCCESS => { 360 | "ansible_facts": { 361 | "discovered_interpreter_python": "/usr/bin/python3" 362 | }, 363 | "changed": false, 364 | "ping": "pong" 365 | } 366 | 192.168.31.124 | SUCCESS => { 367 | "ansible_facts": { 368 | "discovered_interpreter_python": "/usr/bin/python3" 369 | }, 370 | "changed": false, 371 | "ping": "pong" 372 | } 373 | 192.168.31.165 | SUCCESS => { 374 | "ansible_facts": { 375 | "discovered_interpreter_python": "/usr/bin/python3" 376 | }, 377 | "changed": false, 378 | "ping": "pong" 379 | } 380 | ``` 381 | 382 | 到此为止,我们完成了 Ansible 运行环境的配置,包括: 383 | 384 | * 准备 ssh 登录的秘钥 385 | * 配置控制器节点的基础配置文件 386 | * 初始化三个被管理几点的远程登录用户账号 387 | * 创建了优化的 Inventory 主机清单文件 388 | * 使用 ping 模块再次确认所有被管理主机的 ssh 登录 389 | 390 | ## 用 Ansible 命令执行运维工作 | Ad-hoc 391 | 392 | 用下面的命令体会 Ansible 的特性和内置模块的功能。在执行下面的命令之前,复制 inventory.v2 文件为 hosts.ini 文件【ansible.cnf 参数中已做配置】。这样在执行命令的时候就不需要用参数制定 inventory 文件的位置,因此可以更加简洁。 393 | 394 | ### 默认并发执行特性 395 | 396 | 运行多次下面相同的命令,了解 Ansible 的多线程并发特性。 397 | 398 | * 使用 -a 参数远程执行 Linux 原生命令 399 | 400 | ```sh 401 | ansible localvm -a "hostname" 402 | ansible localvm -a "hostname" 403 | ansible localvm -a "hostname" 404 | 405 | ansible localvm -a "hostname" -f 1 406 | ansible localvm -a "hostname" -f 1 407 | ansible localvm -a "hostname" -f 1 408 | ``` 409 | 410 | 从以上结果中观察 node 节点的执行顺序。 411 | 412 | ### 环境状况检查 413 | 414 | 使用 -a 参数的远操作系统里的原生命令。 415 | 416 | ```sh 417 | ansible localvm -a "df -h" 418 | 419 | ansible localvm -a "free -m" 420 | 421 | ansible localvm -a "date" 422 | ``` 423 | 424 | ### 使用 Ansible 模块做变更 425 | 426 | 使用 Ansible 的核心模块变更系统。 427 | 428 | * yum 和 servce 模块混用 429 | * 搭配 -a 的命令行执行 430 | * 在需要提权的地方,使用 -b 参数 431 | 432 | ```sh 433 | ansible localvm -a "date" 434 | 435 | ansible localvm -b -m yum -a "name=chrony state=present" 436 | 437 | ansible localvm -b -m service -a "name=chronyd state=started enabled=yes" 438 | 439 | ansible localvm -b -a "chronyc tracking" 440 | 441 | ansible localvm -a "date" 442 | ``` 443 | 444 | ## 部署目标应用系统 445 | 446 | 本教程在 GitHub 上准备了一个简单的 hellodjango 应用程序。 447 | 448 | 应用系统配置部署所需的流程如下: 449 | 450 | * 在 App1 和 App2 上配置好 Django 运行环境 451 | * 在 DB 上安装 Mariadb 452 | * 在所有服务器上启动防火墙服务,并配置好需要开放的端口 453 | * 在两个 App 服务器上安装并启动 Django 应用 454 | 455 | ### 配置 Django 运行环境 456 | 457 | 开始编写第一个应用系统在一套服务器上的综合配置 Playbook。 458 | 459 | 配置应用服务器:安装 python3 和 django 460 | 461 | * yum 和 pip 模块的混用 462 | * 辅助 -a 的命令行执行 463 | 464 | ```sh 465 | ansible app -b -m yum -a "name=python3-pip state=present" 466 | 467 | ansible app -b -m pip -a "name=django<4 state=present" 468 | 469 | ansible app -a "python3 -m django --version" 470 | 471 | ``` 472 | 473 | 本教程中使用了先用逐条 Ansible 配置指令调试的方式,然后在将其翻译到 Playbook 中的方式,在熟悉了这个开发过程和 Ansible 的常用模块后,我们则可以直接开发 Playbook 了。 474 | 475 | 根据以上的所有 Ansible 指令,创建名为 app-stack.yml 的文件,内容如下: 476 | 477 | ```yml 478 | --- 479 | - hosts: app 480 | become: true 481 | tasks: 482 | - name: Config NTP Server # 配置 ntp 服务器 483 | yum: 484 | name: chrony 485 | state: present 486 | - name: Start NTP service # 启动 chronyd 服务 487 | service: 488 | name: chronyd 489 | state: started 490 | enabled: yes 491 | - name: Install Python3-pip&git 492 | yum: 493 | name: python3-pip,git 494 | state: present 495 | - name: Install django package 496 | pip: 497 | name: django<4 498 | state: present 499 | ``` 500 | 501 | 下面执行 ansible-play app-stack.yml 可以得到相同的结果状态。 502 | 503 | ### 配置数据库服务器 504 | 505 | 接着我们执行安装 Mariadb 服务器的操作。 506 | 507 | * yum、service 和 防火墙模块混用 508 | 509 | 逐条执行下面的调试命令: 510 | 511 | ```sh 512 | ansible db -b -m yum -a "name=mariadb-server state=present" 513 | 514 | ansible db -b -m service -a "name=mariadb state=started enabled=yes" 515 | 516 | ansible db -b -m yum -a "name=firewalld state=present" 517 | 518 | ansible db -b -m service -a "name=firewalld state=started enabled=yes" 519 | 520 | ansible db -b -m firewalld -a "port=3306/tcp zone=public state=enabled permanent=yes" 521 | 522 | ansible db -b -m yum -a "name=python3-PyMySQL state=present" 523 | 524 | ``` 525 | 526 | firewall-cmd --list-all 527 | 528 | 529 | 在以上命令的结果都符合预期后,在 app-stack.yml 中增加如下内容 530 | 531 | ```yml 532 | 533 | - hosts: db 534 | become: true 535 | tasks: 536 | - name: Install Mariadb Server 537 | yum: 538 | name: mariadb-server,python3-PyMySQL 539 | state: present 540 | - name: Start DB service 541 | service: 542 | name: mariadb 543 | state: started 544 | enabled: yes 545 | - name: Install firewalld 546 | yum: 547 | name: firewalld 548 | state: present 549 | - name: Start Firewalld service 550 | service: 551 | name: firewalld 552 | state: started 553 | enabled: yes 554 | - name: Open the db port 555 | firewalld: 556 | port: 3306/tcp 557 | permanent: yes 558 | state: enabled 559 | ``` 560 | 561 | 在控制器上执行更新后的 Playbook,运行命令 `ansible-playbook app-stack.yml` ,确保执行结果输出正常。 562 | 563 | ### 部署 Django 应用系统 564 | 565 | 从 GitHub 中部署目标应用代码: 566 | 567 | * 运用 git ,yum , serviice 和 firewalld 模块 568 | * 用命令行方式启动应用 569 | 570 | ```sh 571 | ansible localvm -b -m yum -a "name=git state=present" 572 | 573 | ansible app -b -m git -a "repo=https://github.com/martinliu/hellodjango.git \ 574 | dest=/opt/hello update=yes" 575 | 576 | ansible app -b -m firewalld -a "port=8000/tcp state=enabled permanent=yes" 577 | 578 | ansible app -b -m service -a "name=firewalld state=reloaded enabled=yes" 579 | 580 | ansible app -b -a "sh /opt/hello/run-hello.sh" 581 | ``` 582 | 583 | 在浏览器中输入应用的访问网址: http://192.168.31.165:8000/hello/ ,应该在两个 app 服务器上都可以看到相同的结果。 584 | 585 | 586 | 在 app-satck.yml 中的 App 部署部分加入下面的内容 587 | 588 | ```yml 589 | - name: Deploy from github 590 | git: 591 | repo: https://github.com/martinliu/hellodjango.git 592 | dest: /opt/hello 593 | update: yes 594 | - name: enable app 8000 port 595 | firewalld: 596 | port: 8000/tcp 597 | permanent: yes 598 | state: enabled 599 | - name: Reload Firewalld service 600 | service: 601 | name: firewalld 602 | state: reloaded 603 | enabled: yes 604 | - name: Start my hello app 605 | command: sh /opt/hello/run-hello.sh 606 | ``` 607 | 608 | 执行最后的应用部署 `ansible-playbook app-stack.yml` 确认执行后的结果正常,用浏览器可以正常访问 Django 应用页面。 609 | 610 | ## 总结 611 | 612 | 希望大家通过本教程学会: 613 | 614 | * Ansible 运行环境的基础配置 615 | * Ansible 的 ad-hoc 方式用法 616 | * Ansible 核心中的几个常用模块 617 | * 逐步编写部署目标应用的 Ansible Playbook 618 | 619 | ## 呼唤下一位鉴宝人 620 | 621 | 建议分享的内容如下(不限于此): 622 | 623 | * Role 的开发 624 | * CI/CD 工具中执行 Playbook 625 | * 容器,k8s 相关主题 626 | * AWX Ansible 相关主题 627 | * 生产项目中的经验分享 628 | -------------------------------------------------------------------------------- /config/lab01-ansible/vars/default.yml: -------------------------------------------------------------------------------- 1 | create_user: sysops 2 | copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}" -------------------------------------------------------------------------------- /config/lab09-terraform/README.md: -------------------------------------------------------------------------------- 1 | ![](./images/tf.JPG) 2 | 3 | ## 1. Terraform 简介 4 | 5 | ---------------------------------------------------------------------- 6 | 7 | [Terraform](https://www.terraform.io/)是一个开源工具,用于安全高效地预览、配置和管理(CRUD)多云基础架构和各类其他[资源](https://registry.terraform.io/browse/providers)。 8 | 9 | Terraform是一个IT基础架构自动化编排工具,可以用代码来管理和维护IT资源。它编写了描述云资源拓扑的配置文件,例如虚拟机、负载均衡器。Terraform的命令行接口(Command Line Interface,CLI)提供一种简单机制,用于将配置文件部署到AWS、阿里云或其他任意支持的云上,并对其进行版本控制。 10 | 11 | 同时,Terraform是一个高度可扩展的工具,通过Terraform提供的SDK,编写Go代码,调用任意的API,实现[自研的provider](https://www.hashicorp.com/blog/writing-custom-terraform-providers)。也就是说只要提供了API,就可以成为一个provider。 12 | 13 | ## 2. 基本概念 14 | 15 | ------------------------------------------------------------------------------------------- 16 | 17 | ### 2.1 Provider 18 | 19 | `provider`其实就是某个资源的API集合,比如[Alicloud Provider](https://registry.terraform.io/providers/aliyun/alicloud/1.173.0) 20 | 21 | 初始化 provider,每种provider的初始化方式不一样,阿里云支持AK/SK, Credentials, etc. 22 | 23 | ```shell 24 | # init with AK/SK 25 | terraform { 26 | required_providers { 27 | alicloud = { 28 | source = "aliyun/alicloud" 29 | version = "1.156.0" 30 | } 31 | } 32 | } 33 | 34 | provider "alicloud" { 35 | access_key = "xxxxxxxxxxxxxx" 36 | secret_key = "xxxxxxxxxxxxxx" 37 | region = "cn-shanghai" 38 | } 39 | ``` 40 | 41 | ```shell 42 | # init with profile, the path is $HOME/.aliyun/config.json 43 | terraform { 44 | required_providers { 45 | alicloud = { 46 | source = "aliyun/alicloud" 47 | version = "1.156.0" 48 | } 49 | } 50 | } 51 | 52 | provider "alicloud" { 53 | region = "cn-shanghai" 54 | profile = "customprofile" 55 | } 56 | ``` 57 | 58 | 由于terraform本身的版本在不断升级,以及provider的版本升级,不同的版本组合下,terraform预览资源可能会出现不一样的视图。解决方法,[lock文件](https://www.terraform.io/language/files/dependency-lock),`.terraform.lock.hcl`。 建议在git仓库下,将这个文件一并上传以此保证版本的一致性。 59 | 60 | ### 2.2 Resource 61 | 62 | `resource`就是具体某一个API的调用,创建(Create)一个具体的资源,比如[VPC](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/vpc),[NAT Gateway](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/nat_gateway),[ECS](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance)等。 63 | 64 | ```shell 65 | resource "alicloud_vpc" "vpc" { 66 | vpc_name = var.name 67 | cidr_block = "10.0.0.0/8" 68 | } 69 | ``` 70 | 71 | ### 2.3 Data Sources 72 | 73 | `data`就是读取(Read/Retrieve)某个已经存在的可用资源,比如[虚拟机镜像](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/images) 74 | 75 | ```shell 76 | data "alicloud_images" "images_ds" { 77 | owners = "system" 78 | name_regex = "^centos_6" 79 | } 80 | ``` 81 | 82 | ### 2.4 Variables 83 | 84 | `variable` 就是定义参数(arguments),terraform支持各种[类型](https://www.terraform.io/language/expressions/types)的参数。 85 | 86 | ```shell 87 | variable "vpc_name" { 88 | type = string 89 | description = "variable for vpc name" 90 | default = "test_vpc" 91 | } 92 | ``` 93 | 94 | terraform支持几种传递参数的方式,第一大类,自动传参。第二大类,手动传参。 95 | 96 | 自动传参: `terraform.tfvars`, `*.auto.tfvars` 97 | 98 | 手动传参: `-var="instance_type=t2.large"`, `export TF_VAR_image_id=ami-abc123` , 最后在都没有找到的情况下,在交互界面上要求给出变量值 99 | 100 | ![](./images/var.JPG) 101 | 102 | ### 2.5 locals 103 | 104 | 有变量就会有常量,`locals`定义会重复使用到的常量。 105 | 106 | ```shell 107 | locals { 108 | service_name = "forum" 109 | owner = "Community Team" 110 | } 111 | ``` 112 | 113 | ### 2.6 Output 114 | 115 | 有了输入(var),就会有输出。`output`支持输出具体的资源信息(attributes)。 116 | 117 | ```shell 118 | output "vpc_id" { 119 | value = alicloud_vpc.vpc.id 120 | } 121 | ``` 122 | 123 | ### 2.7 State file 124 | 125 | Terraform和Ansible一样,它们的操作都支持幂等性。这种幂等性其实是通过现实的状态与state file中的预期状态进行比对得到的。 126 | 127 | Terraform支持两大类的state file,第一大类,本地state file。第二大类,远程state file。 128 | 129 | 本地state file: `terraform.tfstate`, `terraform.tfstate.backup`, 就你一个人用 130 | 131 | 远程state file: `state.tf` , 支持[AWS S3](https://www.terraform.io/language/settings/backends/s3),[Alicloud OSS](https://www.terraform.io/language/settings/backends/oss), 适合团队开发,上传到git仓库,并且支持锁机制,可以确保互斥,同一时刻只能有一个人在CRUD云资源。 132 | 133 | ```shell 134 | terraform { 135 | backend "oss" { 136 | bucket = "remote-state-dns" 137 | prefix = "mystate/state" 138 | key = "terraform.tfstate" 139 | region = "cn-beijing" 140 | } 141 | } 142 | ``` 143 | 144 | ## 3. Terraform命令行(实战) 145 | 146 | ------------------------------------------------------ 147 | 148 | ### 3.1 安装Terraform 149 | 150 | 安装terraform非常简单,支持手动安装下载zip包,解压zip包,添加到环境变量中即可。也支持在线安装,`apt-get install terraform`, 按照这篇[install guide](https://learn.hashicorp.com/tutorials/terraform/install-cli)即可。 151 | 152 | ### 3.2 Terraform 命令 153 | 154 | #### 3.2.1 terraform init 155 | 156 | 进入到包含`tf`文件的目录下,初始化terraform,执行`terraform init`, 它会下载对应的provider,验证你的credential,初始化remote state file。 157 | 158 | `-upgrade`参数决定是否升级模块代码以及provider版本 159 | 160 | #### 3.2.2 terraform fmt 161 | 162 | `terraform fmt`命令用来格式化Terraform代码文件的格式和规范,增加可读性。 163 | 164 | #### 3.2.3 terraform validate 165 | 166 | `terraform validate`命令用来检查目录下Terraform代码,只检查语法文件,不会真正访问远程资源。逻辑上对即可,并不会真正判断现实情况。 167 | 168 | #### 3.2.4 terraform plan 169 | 170 | `terraform plan`命令用来预览资源的变化情况,它会真正检查state file以及现实情况。比如新增(Create)一个资源,更新(Update)一个资源,删除(Destroy)一个资源。 171 | 172 | `-out`参数将变更计划保存到指定路径下的文件中,随后可以使用terraform apply执行该计划 173 | 174 | #### 3.2.5 terraform apply/destroy 175 | 176 | `terraform apply`和`terraform destroy`命令是terraform真正干活的两个命令。apply命令用来生成执行计划(可选)并执行之,使得基础设施资源状态符合代码的描述。destroy命令用来销毁并回收所有Terraform管理的基础设施资源。 177 | 178 | `-target`参数指定特定的某一个资源,当terraform已经管理了非常多的资源下,很有用 179 | 180 | `-auto-approve`参数指定自动同意执行,慎用!建议在jenkinsfile中加入`input`来手动同意。 181 | 182 | #### 3.2.6 terraform show 183 | 184 | `terraform show`命令用来展示(Read)当前所有归terraform控制的资源的状态信息。 185 | 186 | #### 3.2.7 terraform state 187 | 188 | `terraform state`命令用来进行复杂的状态管理操作。直接修改state file是不推荐的,如果确实需要改动state file,使用`terraform state`命令来修改。 189 | 190 | `terraform state list`: 列出所有资源的状态信息,同`terraform show` 191 | 192 | `terraform state show`: 列出具体某一个资源的状态信息 193 | 194 | `terraform state mv`: 重命名一个资源 195 | 196 | `terraform state rm`: 从状态文件中删除一个或多个对象,删除对象并非删除实际基础设施对象,而只是状态不再由Terraform管理,从状态文件中删除而已。 197 | 198 | #### 3.2.8 terraform import 199 | 200 | `terraform import`命令用来将已经存在的手动创建的资源对象导入Terraform,由terraform来控制。 201 | 202 | ## 4. Terraform Cloud 203 | 204 | ------------------------------------------------------------ 205 | 206 | [Terraform Cloud](https://www.terraform.io/cloud-docs)是一个帮助团队一起使用 Terraform 的应用程序。它在一个可靠的云上环境中运行Terraform,包括提供轻松访问tfstate、团队审批基础设施的更改、自动关联VCS(GitHub, GitLab, etc.)、云上资源预算评估、执行哨兵等功能。 207 | 208 | Terraform官方提供了非常详尽的使用[guide](https://learn.hashicorp.com/tutorials/terraform/cloud-sign-up?in=terraform/cloud-get-started),请自行参考guide进行实验。 209 | 210 | 基本的使用步骤如下: 211 | 212 | 1. 登录Terraform Cloud 213 | 214 | 2. 新建一个 Org 215 | 216 | <img src="./images/neworg.jpg" alt="neworg" style="zoom:50%;" /> 217 | 218 | 3. 新建一个workspace,并且关联到GitHub repo 219 | 220 | 4. 设置AK/SK两个环境变量 221 | 222 | <img src="./images/vars.jpg" alt="vars" style="zoom:33%;" /> 223 | 224 | 5. 执行Plan/Apply 225 | 226 | <img src="./images/plan.jpg" alt="plan" style="zoom:60%;" /> 227 | 228 | ## 5. Terraform Module 229 | 230 | ------------------------------------------------------------ 231 | 232 | 为了能够复用某些resource,terraform支持将一个目录下的所有tf文件封装成一个module。每个tf文件和平时的用法一致。然后通过关键字mudole来调用这个目录下的所有resource。 233 | 234 | ```shell 235 | module "tsj_demo" { 236 | source = "git::https://jihulab.com/dhutsj/terraform-practice.git//alicloud_vpc_nat_sg?ref=main" 237 | 238 | vpc_name = var.vpc_name 239 | } 240 | 241 | ``` 242 | 243 | 244 | 245 | ## 6. 鸣谢及推荐 246 | 247 | ------------------------------------------------ 248 | 249 | 非常感谢 DevOps 中国社区举办此次鉴宝活动,我才有这次机会和大家见面,很高兴认识这么多热爱开源的工程师。 250 | 251 | 如果在学习/使用Terraform时有任何疑问或想法,欢迎和我交流,微信(wechat):dhutsj。 252 | 253 | Terraform 版本管理工具 tfswitch: https://tfswitch.warrensbox.com/ 254 | 255 | Terraform 多环境管理工具 terragrunt: https://terragrunt.gruntwork.io/ 256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /config/lab09-terraform/images/neworg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab09-terraform/images/neworg.jpg -------------------------------------------------------------------------------- /config/lab09-terraform/images/plan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab09-terraform/images/plan.jpg -------------------------------------------------------------------------------- /config/lab09-terraform/images/tf.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab09-terraform/images/tf.JPG -------------------------------------------------------------------------------- /config/lab09-terraform/images/var.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab09-terraform/images/var.JPG -------------------------------------------------------------------------------- /config/lab09-terraform/images/vars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/config/lab09-terraform/images/vars.jpg -------------------------------------------------------------------------------- /database/lab06-liquibase/images/flyway-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/database/lab06-liquibase/images/flyway-1.jpg -------------------------------------------------------------------------------- /database/lab06-liquibase/images/flyway-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/database/lab06-liquibase/images/flyway-2.jpg -------------------------------------------------------------------------------- /database/lab06-liquibase/images/flyway-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/database/lab06-liquibase/images/flyway-3.jpg -------------------------------------------------------------------------------- /database/lab06-liquibase/images/flyway-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/database/lab06-liquibase/images/flyway-4.jpg -------------------------------------------------------------------------------- /database/lab06-liquibase/images/liquibase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/database/lab06-liquibase/images/liquibase.jpg -------------------------------------------------------------------------------- /deploy/lab04-argocd/0.necessary_before_start.md: -------------------------------------------------------------------------------- 1 | Gitops 工具链中, ArgoCD 非常受欢迎。虽然其功能强大,操作简单,但想要真正掌握它,还需要把基础打牢,融会贯通才能理解得更加深刻。 2 | 3 | # Kubernetes (k8s) 4 | 5 | 学习中所有实验,都运行在 k8s 集群中,所以要求诸位对 kubernetes 基本概念有所了解,如 Deployment, Service, Ingress 等等。然而这仅仅是听懂课程的前提,最理想的状态,是有独立部署 k8s 集群及基本的排错能力。 6 | 7 | 部署 k8s 集群有多种方式,kubeadmin,kubeasz 等等,大家各显神通,用自己最熟悉的方案即可。本次分享中我们使用 kubesphere 全家桶中的 [kubekey](https://github.com/kubesphere/kubekey) 及 [openelb](https://github.com/kubesphere/openelb)。 8 | 9 | # Git 版本管理 10 | 11 | 无论是 Github 还是 Gitlab,抑或是极狐 Gitlab,它们的底层都基于 Git 这个版本管理工具。而我们要探讨的 Gitops,更是把 Git 版本管理的理念作为单一可信任源。 12 | 13 | Git 命令不能仅限于 pull 和 push,还应对其原理近一步了解。如 什么是 gitflow,优缺点都有哪些等等;用成熟的 Git 最佳实践来规范团队多人协作,是实践 Gitops 的首要工作。 14 | 15 | # Gitops 16 | 17 | Gitops 日趋火热,但这是一种仍然发展中的技术实践,成熟度仍待完善。如果仅学习 ArgoCD 这个工具,其意义本身并不大。笔者建议更应从 Gitops 发展历史入手,进而弄明白何为“推模式”,何为“拉模式”,各有什么优缺点,最后再来看 ArgoCD 解决了哪些问题,又引入了哪些问题。 18 | 19 | 诸位要对这些问题有所思考,带着问题学习 ArgoCD,最终才能知其然,知其所以然。 20 | 21 | # Helm & Kubestomize 22 | 23 | 就像 Linux 一切皆文件,在 k8s 中一切皆 “配置清单”。部署应用时,组织配置清单的方案有两种,就是 helm 和 kustomize。helm 通过 golang 模板渲染的方式,把 charts 渲染成为配置清单,而 kustomize 则更接近原生的 k8s 配置清单。个中差异,还需要在课前有所了解。 24 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/1.install-k8s-cluster.md: -------------------------------------------------------------------------------- 1 | # 1. 安装工具 2 | 3 | 开始动手之前,我们来介绍两款工具,kubekey 和 k9s。 4 | 5 | kubeykey 是 KubeSphere 团队基于 GoLang 语言开发的 kubernetes 集群部署工具,使用 KubeKey,您可以轻松、高效、灵活地单独安装和管理 Kubernetes,当然如果你部署 Kubesphere 也是分厂方便的。 6 | 7 | k9s 是一款命令行下的 k8s 集群管理工具,可以非常有效地提高你对 k8s 的管理效率。本次分享我们对 k8s 资源的观察和操作,就是通过 k9s 来实现的。 8 | 9 | > [kubekey release](https://github.com/kubesphere/kubekey) 10 | 11 | > [k9s release](https://github.com/derailed/k9s) 12 | 13 | ```shell 14 | # kubekey 15 | curl -sfL https://get-kk.kubesphere.io | sh - 16 | 17 | # k9s 18 | curl -sS https://webinstall.dev/k9s | bash 19 | ``` 20 | 21 | # 2. 部署 k8s 环境 22 | 23 | ## 2.1. 生成 kubekey 配置 24 | 25 | ```shell 26 | # 创建集群配置,集群名称为 monday 27 | ./kk create config --name monday 28 | ``` 29 | 30 | ## 2.2. 修改集群配置 31 | 32 | ```yaml 33 | # 修改配置如下 34 | apiVersion: kubekey.kubesphere.io/v1alpha1 35 | kind: Cluster 36 | metadata: 37 | name: monday 38 | spec: 39 | hosts: 40 | - { 41 | name: tm-opsinit-01, 42 | address: 10.10.14.99, 43 | internalAddress: 10.10.14.99, 44 | } 45 | roleGroups: 46 | etcd: 47 | - tm-opsinit-01 48 | master: 49 | - tm-opsinit-01 50 | worker: 51 | - tm-opsinit-01 52 | controlPlaneEndpoint: 53 | domain: monday-api.automan.fun 54 | address: "" 55 | port: 6443 56 | kubernetes: 57 | version: v1.21.5 58 | imageRepo: kubesphere 59 | clusterName: cluster.local 60 | network: 61 | plugin: calico 62 | kubePodsCIDR: 10.233.64.0/18 63 | kubeServiceCIDR: 10.233.0.0/18 64 | registry: 65 | registryMirrors: [] 66 | insecureRegistries: [] 67 | addons: [] 68 | ``` 69 | 70 | ## 2.3. 检查 docker 配置 71 | 72 | docker daemon 默认创建的 docker0 网桥,使用 172.17.0.0 网段地址,如果你的服务器使用的 172.17.0.0 的网段,可以通过修改 docker daemon 的配置来修改 docker0 网桥的地址段,避免 地址冲突。 73 | 74 | ```json 75 | # /etc/docker/daemon.json 76 | 77 | { 78 | "log-opts": { 79 | "max-size": "5m", 80 | "max-file":"3" 81 | }, 82 | "exec-opts": ["native.cgroupdriver=systemd"], 83 | "bip":"192.168.0.1/24" 84 | } 85 | ``` 86 | 87 | 修改完配置后,如果服务器上已安装 docker 服务,执行如下命令重载配置。如无运行 docker 服务,请忽略。 88 | 89 | ```shell 90 | systemctl daemon-reload && systemctl restart docker 91 | ``` 92 | 93 | ## 2.4. 创建集群 94 | 95 | 创建集群前,有三个点需要检查: 96 | 97 | 1. 禁用 selinux 98 | 2. 禁用防火墙 99 | 3. 安装相关系统级依赖 100 | 101 | ```shell 102 | # 临时禁用 selinux 103 | setenforce 0 104 | sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config 105 | 106 | # 禁用防火墙 107 | systemctl disable firewalld && systemctl stop firewalld && systemctl status firewalld 108 | 109 | # 安装系统依赖 110 | yum install socat conntrack ebtables ipset 111 | 112 | # 创建集群 113 | export KK_ZONE=cn 114 | ./kk create cluster -f config-monday.yaml 115 | ``` 116 | 117 | ## 2.5. 安装 kubectl 客户端 118 | 119 | > k8s 集群部署好后,我们来安装相关 kubectl 管理工具。 120 | 121 | ```shell 122 | 123 | # 添加 k8s yum 源 124 | cat <<EOF > /etc/yum.repos.d/kubernetes.repo 125 | [kubernetes] 126 | name=Kubernetes 127 | baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 128 | enabled=1 129 | gpgcheck=0 130 | repo_gpgcheck=0 131 | gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg 132 | EOF 133 | 134 | # 安装 kubectl 135 | yum install -y kubectl 136 | 137 | # k8s 命令行参数很多,可以通过 bash-completion 来自动补全命令 138 | # 命令行代码补全 139 | yum install bash-completion -y 140 | echo "source <(kubectl completion bash)" >> ~/.bashrc 141 | source ~/.bashrc 142 | ``` 143 | 144 | # 3. 部署 OpenELB 145 | 146 | 集群中的应用,如果要在集群外部访问,可以采用 NodePort 方式暴露服务,也可以采用 LoadBalancer 的方式。为了更好的模拟生产环境,咱们使用 LoadBalancer 的方式暴露服务。OpenLB 是由 Kubesphere 团队开发的支持裸金属服务器提供 LoadBalancer 类型服务的工具。具体功能细节就不额外讲解,大家可以自行参考官方文档。国人主导开发的工具,中文支持非常好。 147 | 148 | > Github: `https://github.com/kubesphere/openelb` 149 | 150 | ```shell 151 | kubectl apply -f https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml 152 | ``` 153 | 154 | ## 3.1 OpenELB layer2 原理讲解 155 | 156 | 我们实验中使用 OpenLB 的 layer2 模式。这种模式需要配置一些集群主机网段的空地址,也就是说这些 IP 不能绑定物理网卡。OpenLB 提供 Eip 这个 CRD 来配置这些空 IP。为什么需要这样的 IP 呢?我们来简单讲解下 Layer2 模式的原理。 157 | 158 | 当外部有流量请求某个空 IP 时,路由器会发出 ARP 广播报文,也就是到集群服务器网段内询问,数据要发给谁。显然不会有任何一个主机响应,此时,OpenLB 的 159 | port-manager 就会相应这个 ARP 报文。通过这种 ARP 报文欺骗的黑客手段,实现流量的劫持。剩下的步骤,就由 PortLB 来转发流量到相应的 Service 中。 160 | 161 | PortLB 的 Layer2 模式,就是这样工作的。 162 | 163 | ```yaml 164 | apiVersion: network.kubesphere.io/v1alpha2 165 | kind: Eip 166 | metadata: 167 | name: eip-pool 168 | spec: 169 | address: 10.10.14.91-10.10.14.92 170 | protocol: layer2 171 | interface: ens192 172 | disable: false 173 | ``` 174 | 175 | # 4. 部署 nginx-ingress 176 | 177 | OpenLB 可以方便地暴露 4 层协议,比如 TCP 协议等等,但在 7 层协议中的一些操作,就无能为力啦,比如卸载 https 证书,路由分发等等。 178 | 179 | > https://kubernetes.github.io/ingress-nginx/deploy/ 180 | 181 | ```shell 182 | # 1. 应用配置清单 183 | 184 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/cloud/deploy.yaml 185 | 186 | # 2. service.metadata.annotations 中指定 OpenELB 配置 187 | 188 | lb.kubesphere.io/v1alpha1: openelb 189 | protocol.openelb.kubesphere.io/v1alpha1: layer2 190 | ``` 191 | 192 | > PortELB 通过这两条 annotation 信息来识别要提供服务的 Service 193 | 194 | # 5. 部署 OpenEBS 195 | 196 | Kubernates 集群中,对于有状态应用,需要申请 PV 支持。openEBS 支持把节点主机的文件系统映射为存储服务,这对我们的 All In One 实验来说,是个非常好的支持。 197 | 198 | > https://openebs.io/docs/user-guides/installation 199 | 200 | ```shell 201 | # 推荐使用 operator 部署应用 202 | kubectl apply -f https://openebs.github.io/charts/openebs-operator.yaml 203 | ``` 204 | 205 | ## 5.1 验证 hostpath 206 | 207 | ```shell 208 | # /var/openebs/local 209 | kubectl apply -f https://openebs.github.io/charts/examples/local-hostpath/local-hostpath-pvc.yaml 210 | kubectl apply -f https://openebs.github.io/charts/examples/local-hostpath/local-hostpath-pod.yaml 211 | ``` 212 | 213 | # 6. 安装 Metric Server 214 | 215 | ```shell 216 | # Installation 217 | 218 | kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 219 | ``` 220 | 221 | > bx509: cannot validate certificate for because it doesn't contain any IP SANs" 222 | 223 | 上述异常因证书验证未通过导致,可以配置合法域名 SSL 证书,也可通过如下方式禁用 Metric Server 证书验证. 224 | 225 | ```yaml 226 | # Metric Server deployment 添加镜像启动参数 227 | --- 228 | spec: 229 | containers: 230 | - args: 231 | # 添加如下参数 232 | - --kubelet-insecure-tls 233 | ``` 234 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/README.MD: -------------------------------------------------------------------------------- 1 | ![ArgoCD](https://github.com/argoproj/argoproj/raw/master/docs/assets/argo.png) 2 | 3 | # 1. ArgoCD 简介 4 | 5 | 基于 kubernetes 的声明式 Gitops 持续部署工具。 6 | 7 | 持续部署工具有很多,如 Jenkins 等等,我们为什么选择 ArgoCD 呢? 8 | 9 | 1. 应用定义,配置和环境变量管理等等,都是声明式的,基于云原生的。 10 | 2. 所有声明清单都存储在代码仓库中,受版本管理 11 | 3. 应用发布和生命周期管理都是自动化的,可审计的。 12 | 13 | 最重要的,ArgoCD 操作简单,非常易用。 14 | 15 | # 2. 工作原理 16 | 17 | ![](https://argo-cd.readthedocs.io/en/stable/assets/argocd_architecture.png) 18 | 19 | ArgoCD 被设计并实现为 Kubernetes 控制器,它会持续监控 ArgoCD 应用状态。ArgoCD 中的应用会对应一个 Git 仓库,ArgoCD 控制器确保应用状态始终同步。此处的 Git 仓库,并不存放项目源码,它保存的是项目在 Kubernetes 中的运行状态,也就是配置清单。Git 仓库内容的组织形式,支持 Helm, Kustomize 等; 20 | 21 | 当用户向 Git 仓库提交合并请求,合并被受理后,Git 仓库中应用状态的配置清单发生变化,此时 Git 仓库可以通过 WebHook 触发 ArgoCD 的应用同步。如果未配置 WebHook,ArgoCD 会轮询检测 Git 仓库的变更,检测周期默认为 3 分钟。当然,用户也可以通过 UI 或 CLI 的方式手动触发应用同步。 22 | 23 | ArgoCD 的 Hook 机制,会在应用状态同步前,同步中,同步后及同步失败后,触发响应的钩子方法,用来完成一些额外操作,可以实现更加复杂的应用控制。ArgoRollouts 的蓝绿发布,就非常好的利用了 hooks 机制。 24 | 25 | ArgoCD 不仅可以将应用发布到它所在的 Kubernates 集群,它也可以托管其他集群,实现多集群的应用部署。该功能由 ApplicationSet 实现。 26 | 27 | # 3. 安装 ArgoCD 28 | 29 | > [k8s 集群部署参考文档](install-k8s-cluster.md) 30 | 31 | 推荐用 operator 的方式部署 ArgoCD。推荐理由如下: 32 | 33 | - 使用默认配置快速安装并启动 ArgoCD 34 | - 无缝升级 ArgoCD 所有组件 35 | - 支持定期计划备份和恢复 ArgoCD 集群 36 | - 聚合并发布 ArgoCD 及 Operator 本身的指标,供 Prometheus 和 Grafana 使用 37 | - 根据需要自动缩放对应组件 38 | 39 | ## 3.1 安装 ArgoCD Operator 40 | 41 | > ref: https://operatorhub.io/operator/argocd-operator 42 | 43 | ```shell 44 | 45 | curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.21.2/install.sh | bash -s v0.21.2 46 | 47 | kubectl create -f https://operatorhub.io/install/argocd-operator.yaml 48 | 49 | kubectl get csv -n operators 50 | ``` 51 | 52 | > !!! 注: 如果不配置环境变量,则 ArgoCD 无法创建 Namespace 53 | 54 | > ref: https://github.com/argoproj/argo-cd/issues/5886 55 | 56 | 默认使用 Operator 部署的 ArgoCD,没有集群资源的控制权限;找到 `Subscription` 类型的资源,给 Operator 添加环境变量配置; 57 | 58 | ```yaml 59 | apiVersion: operators.coreos.com/v1alpha1 60 | kind: Subscription 61 | metadata: 62 | name: argocd 63 | namespace: argocd 64 | spec: 65 | channel: alpha 66 | name: argocd-operator 67 | source: operatorhubio-catalog 68 | sourceNamespace: olm 69 | config: 70 | env: 71 | - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES 72 | value: <argocd-namespaces(s)> 73 | # 此处案例中 value 配置为 argocd 74 | ``` 75 | 76 | ## 3.2 创建 ArgoCD 实例 77 | 78 | > https://argocd-operator.readthedocs.io/en/latest/usage/ingress/ 79 | 80 | ```yaml 81 | apiVersion: argoproj.io/v1alpha1 82 | kind: ArgoCD 83 | metadata: 84 | name: monday-argocd 85 | namespace: argocd 86 | spec: 87 | server: 88 | insecure: true 89 | ingress: 90 | enabled: true 91 | ``` 92 | 93 | ## 3.3 修改配置导出服务 94 | 95 | ArgoCD Operator 默认 Ingress 域名为资源名,此处即为 `monday-argocd`。Operator 官方文档中说可以 `overwrite` 域名,但翻遍了 CRD 的描述信息,也没找到。翻源码才发现 Operator 并没有控制 `host` 字段。直接 `kubectl edit` 即可 @\_@ 96 | 97 | > !!! 注:官方文档言辞模糊,竟然可以直接修改 98 | 99 | > ingress 域名: argo.monday.fun 100 | 101 | 本地主机配置 hosts 如下: 102 | 103 | ```shell 104 | # /etc/hosts 105 | 106 | 10.10.14.91 argo.monday.fun 107 | ``` 108 | 109 | > > 看到下图请为自己鼓掌 !!! 110 | 111 | ![](./images/argocd-ui-login.png) 112 | 113 | # 3.4. 获取登录密码 114 | 115 | > 默认 admin 密码存放在 `<ArgoCD 实例名>-cluster` secret 里 116 | 117 | ```shell 118 | 119 | kubectl -n argocd get secret monday-argocd-cluster -o jsonpath='{.data.admin\.password}' | base64 -d 120 | ``` 121 | 122 | # 4. 部署样例仓库 123 | 124 | > ref: https://github.com/argoproj/argocd-example-apps.git 125 | 126 | 可以通过 UI 界面或者直接 `kubectl apply` 下方配置清单,创建 ArgoCD 应用清单。 127 | 128 | ```yaml 129 | apiVersion: argoproj.io/v1alpha1 130 | kind: Application 131 | metadata: 132 | name: guestbook 133 | spec: 134 | destination: 135 | name: "" 136 | namespace: guestbook 137 | server: "https://kubernetes.default.svc" 138 | source: 139 | path: kustomize-guestbook 140 | repoURL: "https://github.com/argoproj/argocd-example-apps.git" 141 | targetRevision: HEAD 142 | project: default 143 | syncPolicy: 144 | automated: 145 | prune: true 146 | selfHeal: true 147 | syncOptions: 148 | - CreateNamespace=true 149 | ``` 150 | 151 | > 常见问题: Namespace "guestbook" for Service "guestbook-helm-guestbook" is not managed 152 | 153 | > !!! 注: 这个问题就是 3.1 步骤中没有添加环境导致的 154 | 155 | ```shell 156 | # 通过给 Namespace 打标签的方式,给 ArgoCD 授权 157 | # 默认情况下 $NAMESPACE=argocd 158 | kubectl label ns guestbook argocd.argoproj.io/managed-by=$NAMESPACE 159 | ``` 160 | 161 | 上述配置清单也可以通过 UI 界面生成,具体操作见下图: 162 | 163 | ![](./images/argocd-ui-create-app.png) 164 | ![](./images/guest-book-app-1.png) 165 | ![](./images/guest-book-app-2.png) 166 | 167 | # 5. app-of-apps 模式 168 | 169 | 在 argocd 中,用户所管理的对象,叫做应用。应用的状态,由它对应的资源清单描述。我们可以创建一个仓库,集中管理这些应用配置清单,而这个仓库,也可以当做应用来看待。 170 | 171 | 这种模式,就是 app of apps 模式。 172 | 173 | > https://github.com/argoproj/argocd-example-apps/tree/master/apps 174 | 175 | 官方示例中,使用 Helm Chart 来实现 app of apps 模式,templates 目录下每个文件都是一个 ArgoCD Application 配置清单。 176 | 177 | ```text 178 | ├── Chart.yaml 179 | ├── templates 180 | │   ├── guestbook.yaml 181 | │   ├── helm-dependency.yaml 182 | │   ├── helm-guestbook.yaml 183 | │   └── kustomize-guestbook.yaml 184 | └── values.yaml 185 | ``` 186 | 187 | 当我们删除 app of apps 应用时,对应的其他应用,也会被级联删除。 188 | 189 | ## 5.1 应用场景 190 | 191 | 1. 集群应用迁移 192 | 193 | 微服务开发模式中,一个项目往往对应多个子项目。我们可以为创建 app of apps 应用,管理项目的微服务。当项目需要迁移时,只需要在新集群或者新 Namespace 中应用即可。 194 | 195 | 2. 集群初始化 196 | 197 | 生产环境中,k8s 集群部署好后,还会安装一些必要的支撑服务应用。此时可以把集群的初始化工作,使用 app of apps 应用描述。这样,新集群的初始化工作,也仅仅是创建个 argocd 应用而已。 198 | 199 | ## 5.2 俄罗斯套娃模式 200 | 201 | 在容器云原生的世界里,俄罗斯套娃模式很常见。例如,docker in docker,k8s in k8s。我们刚讲过的 app of apps 也是类似套娃模式。我们是否可以再套一层呢? 202 | 203 | 答案是肯定的。app of apps 应用,本质上仍然是 argocd Application,我们依旧可以将它放在更上层的 app of apps 应用中。 204 | 205 | 这里的想象空间就有点过于复杂了,这里仅仅给大家提供一种思考的方向,目前还没有听说哪个团队这么实践过。 206 | 207 | # 6. ArgoCD 添加多集群 208 | 209 | ArgoCD 在 Web UI 界面上无法添加 Kubernetes 集群,目前仅支持通过 argocd 命令行客户端工具; 210 | 211 | ## 6.1 安装 argocd 命令行客户端工具 212 | 213 | ```shell 214 | # ref: https://argo-cd.readthedocs.io/en/stable/cli_installation/ 215 | 216 | curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 217 | chmod +x /usr/local/bin/argocd 218 | ``` 219 | 220 | ## 6.2 添加集群方法一 221 | 222 | argocd 命令行客户端,提供 cluster add 方法,可以从 kubectl 的连接上下文或者认证文件中添加集群信息。 223 | 当我们在 kubectl 的上下文中,添加 tuesday 集群的上下文后,就可以使用 argocd cluster add 命令了; 224 | 225 | ```shell 226 | # 拷贝 Tuesday 集群的认证秘钥到 227 | scp 10.10.15.153:~/.kube/config ~/.kube/tuesday.config 228 | 229 | # 通过环境变量配置 kubectl 230 | export KUBECONFIG=$HOME/.kube/config:$HOME/.kube/tuesday.config 231 | 232 | # 查看 kubectl 上下文 233 | kubectl config get-contexts 234 | 235 | CURRENT NAME CLUSTER AUTHINFO NAMESPACE 236 | * kubernetes-admin@cluster.monday cluster.monday kubernetes-admin 237 | kubernetes-admin@cluster.tuesday cluster.tuesday kubernetes-admin 238 | 239 | # 导出 argocd-server 443 服务 240 | kubectl port-forward svc/monday-argocd-server -n argocd 9999:443 241 | 242 | # 登录 argocd,输入 admin 账号密码 243 | argocd login 127.0.0.1:9999 244 | 245 | # 通过 kubeconfig 添加集群 246 | argocd cluster add kubernetes-admin@cluster.tuesday 247 | 248 | # optional: 也可以不配置 kubectl 而直接通过 Kubeconfig 文件添加集群 249 | argocd cluster add --kubeconfig tuesday.config --name tuesday kubernetes-admin@cluster.tuesday 250 | ``` 251 | 252 | ## 6.3 添加集群方法二 253 | 254 | argocd-cli 客户端支持从 kubeconfig 文件直接添加集群信息; 255 | 256 | ```shell 257 | $ argocd cluster add --kubeconfig tuesday.config 258 | 259 | ERRO[0000] Choose a context name from: 260 | CURRENT NAME CLUSTER SERVER 261 | * kubernetes-admin@cluster.tuesday cluster.tuesday https://tuesday-api.automan.fun:6443 262 | 263 | $ argocd cluster add --kubeconfig tuesday.config kubernetes-admin@cluster.tuesday 264 | ``` 265 | 266 | ## 6.4 查看集群列表 267 | 268 | ```shell 269 | # 查看集群列表 270 | argocd cluster list 271 | 272 | SERVER NAME VERSION STATUS MESSAGE 273 | https://tuesday-api.automan.fun:6443 tuesday Unknown Cluster has no application and not being monitored. 274 | https://kubernetes.default.svc in-cluster Unknown Cluster has no application and not being monitored. 275 | ``` 276 | 277 | > 由于 ArgoCD 并未在集群中部署应用,所以对集群的健康检查是关闭的。 278 | 279 | # 7. ApplicationSet 280 | 281 | 说到 ArgoCD 多集群部署应用,首先想到的方案可能在 UI 界面上配置多个应用,选择不同的集群发布即可;其次还可能想到,针对不同集群,写不同的 Application 配置清单,直接 `kubectl apply` 即可;我们不能说上述的两种方法不能用,但绝对不符合程序员对优雅的审美。程序员的优雅到底是有多优雅呢? ArgoCD 官方用一个子项目给出了答案。这个项目,就是我们今天要讲的 ArgoCD ApplicationSet。 282 | 283 | 当然跨集群部署应用,只是 ApplicationSet 的功能之一。我们将全面地把它讲透。 284 | 285 | 顾名思义,ApplicationSet 就是 Application 的集合,它控制器和相应的 CRD 构成。 286 | 287 | ## 7.1 ArgoCD 添加 ApplicationSet 控制器 288 | 289 | 只需在 ArgoCD CR 资源中添加 applicationSet 字段即可。 290 | 291 | ```shell 292 | apiVersion: argoproj.io/v1alpha1 293 | kind: ArgoCD 294 | metadata: 295 | name: monday-argocd 296 | namespace: argocd 297 | spec: 298 | version: v2.3.4 299 | server: 300 | insecure: true 301 | ingress: 302 | enabled: true 303 | applicationSet: {} # 增加 304 | ``` 305 | 306 | ## 7.2 ApplicationSet 特性 307 | 308 | 针对于跨集群部署应用和 monorepo,ApplicationSet 这个 CRD 可以实现自动化和更大的灵活性。 309 | 310 | 官方给出四个特性: 311 | 312 | 1. 使用单个 ApplicationSet 配置清单控制多个 kubernetes 集群的能力 313 | 2. 使用单个 ApplicationSet 配置清单从一个或多个 Git 存储仓库部署多个应用程序 314 | 3. 在多租户集群中,提高单个集群租户使用 ArgoCD 部署应用程序的能力。无需让特权集群管理员参与启用目标集群或命名空间。假如我们有 A B 两个集群,通过 ApplicationSet,A 集群中的租户,不需要拥有 B 集群的权限,就可以在 B 集群中发布应用。这样就可以把持续发布的权限提前,放到持续集成阶段管理。 315 | 4. 增强对 monorepo 的支持 316 | 317 | > monorepo: 单个 Git 仓库中定义多个 ArgoCD 应用程序资源 318 | 319 | 一个项目常常由多个微服务构成,这些微服务的部署配置清单,可以放到各自独立的仓库中,也可以化零为整,在同一个仓库中管理。独立仓库管理,对单个微服务开发团队来说,可以更方便发版和回滚,但对于整个项目来说,高灵活度会带来不稳定性。ArgoCD 则推荐使用 MonoRepo 的方式,统一管理微服务的配置。原因也很简单,统一管理能更好的控制各微服务之间的依赖关系,从而提高整个项目的稳定性。 320 | 321 | ## 7.3 工作原理 322 | 323 | 当创建,更新或删除 ApplicationSet CRD 资源时,ApplicationSet 控制器会通过创建,更新或删除一个或多个对应的 ArgoCD Application 资源来响应。 324 | 325 | 实际上,ApplicationSet 的唯一职责就是操作 ArgoCD Application,类似 kubernetes 中 Replicaset 和 Replicas 一样。 326 | 327 | ![](https://argocd-applicationset.readthedocs.io/en/stable/assets/Argo-CD-Integration/ApplicationSet-Argo-Relationship-v2.png) 328 | 329 | - ApplicationSet 并不会操作 kubernetes 资源 330 | - 除 ArgoCD 部署的集群外,ApplicationSet 不会连接其他集群 331 | - 除 ArgoCD 部署的命名空间外,ApplicationSet 不与其他命名空间交互。 332 | 333 | ## 7.4 生成器介绍 334 | 335 | ApplicationSet 生成器负责生成参数,然后将这些参数根据模板渲染成 ArgoCD Application 资源。 336 | 337 | ApplicationSet 控制器当前支持多个生成器: 338 | 339 | 1. 列表生成器:根据列表中的元素生成参数,列表参数由用户自定义。 340 | 2. 集群生成器:基于 ArgoCD 中定义的集群自动生成集群参数,可以看做列表生成器的一个特例。 341 | 3. Git 生成器:根据 ApplicationSet CRD 中定义的 Git 仓库中包含的文件或文件夹生成参数。 342 | - 包含 JSON 值的各个目录将会被解析成模板参数 343 | - Git 仓库中的各目录路径也可以作为参数 344 | 4. 矩阵生成器: 矩阵生成器结合了两个子生成器的生成参数,迭代每个生成器生成的参数的每个组合。通过组合两个升起参数,生成所有可能的组合。 345 | 346 | 这是四个常用的生成器,更多生成器可以参考官方文档。接下来将针对这四种生成器分别进行试验。 347 | 348 | ## 7.5 列表生成器介绍 349 | 350 | 列表生成器基于一些键值对来生成参数,不过要求键对应的值必须为字符串类型。 351 | 352 | ```yaml 353 | apiVersion: argoproj.io/v1alpha1 354 | kind: ApplicationSet 355 | metadata: 356 | name: guestbook 357 | namespace: argocd 358 | spec: 359 | generators: 360 | - list: 361 | elements: 362 | - cluster: monday 363 | url: https://kubernetes.default.svc 364 | - cluster: tuesday 365 | url: https://tuesday-api.automan.fun:6443 366 | template: 367 | metadata: 368 | name: "{{cluster}}-guestbook" 369 | namespace: guestbook 370 | spec: 371 | project: default 372 | source: 373 | repoURL: https://github.com/argoproj/argocd-example-apps.git 374 | targetRevision: HEAD 375 | path: kustomize-guestbook 376 | destination: 377 | server: "{{url}}" 378 | namespace: guestbook 379 | syncPolicy: 380 | automated: {} 381 | syncOptions: 382 | - CreateNamespace=true 383 | ``` 384 | 385 | ## 7.6 集群生成器介绍 386 | 387 | 在 ArgoCD 中,托管集群存储在 ArgoCD 被部署的 Secret 中,ApplicationSet 控制器根据这些信息生成参数来识别和定位多集群。 388 | 389 | 对于每个注册到 ArgoCD 中的集群,都会提供一下参数: 390 | 391 | ```shell 392 | 1. name: 集群名 393 | 2. server: 集群服务器 394 | 3. metadata.labels.<key> 395 | 4. metadata.annotations.<key> 396 | ``` 397 | 398 | ## 7.6.1 基础用法 399 | 400 | ```yaml 401 | apiVersion: argoproj.io/v1alpha1 402 | kind: ApplicationSet 403 | metadata: 404 | name: guestbook-cluster 405 | namespace: argocd 406 | spec: 407 | generators: 408 | - clusters: {} # Automatically use all clusters defined within Argo CD 409 | template: 410 | metadata: 411 | name: "{{name}}-guestbook" 412 | namespace: guestbook 413 | spec: 414 | project: default 415 | source: 416 | repoURL: https://github.com/argoproj/argocd-example-apps.git 417 | targetRevision: HEAD 418 | path: kustomize-guestbook 419 | destination: 420 | server: "{{server}}" 421 | namespace: guestbook 422 | syncPolicy: 423 | automated: {} 424 | syncOptions: 425 | - CreateNamespace=true 426 | ``` 427 | 428 | ## 7.6.2 标签选择器 429 | 430 | 标签选择器可以将目标集群的范围缩小到匹配特定标签的集群。 431 | 432 | ```yaml 433 | apiVersion: argoproj.io/v1alpha1 434 | kind: ApplicationSet 435 | metadata: 436 | name: guestbook-cluster 437 | namespace: argocd 438 | spec: 439 | generators: 440 | - clusters: 441 | selector: 442 | matchLabels: 443 | argocd.argoproj.io/secret-type: cluster 444 | template: 445 | metadata: 446 | name: "{{name}}-guestbook" 447 | namespace: guestbook 448 | spec: 449 | project: default 450 | source: 451 | repoURL: https://github.com/argoproj/argocd-example-apps.git 452 | targetRevision: HEAD 453 | path: kustomize-guestbook 454 | destination: 455 | server: "{{server}}" 456 | namespace: guestbook 457 | syncPolicy: 458 | automated: {} 459 | syncOptions: 460 | - CreateNamespace=true 461 | ``` 462 | 463 | ## 7.6.3 values 字段 464 | 465 | 可以通过`values`集群生成器的字段传递额外的、任意的字符串键值对。通过该`values`字段添加的值添加为`values.(field)` 466 | 467 | ```yaml 468 | apiVersion: argoproj.io/v1alpha1 469 | kind: ApplicationSet 470 | metadata: 471 | name: guestbook-cluster 472 | namespace: argocd 473 | spec: 474 | generators: 475 | - clusters: 476 | selector: 477 | matchLabels: 478 | name: tuesday 479 | values: 480 | version: v1 481 | template: 482 | metadata: 483 | name: "{{name}}-guestbook" 484 | namespace: guestbook 485 | spec: 486 | project: default 487 | source: 488 | repoURL: https://github.com/argoproj/argocd-example-apps.git 489 | targetRevision: HEAD 490 | path: kustomize-guestbook 491 | destination: 492 | server: "{{server}}" 493 | namespace: guestbook 494 | syncPolicy: 495 | automated: {} 496 | syncOptions: 497 | - CreateNamespace=true 498 | info: 499 | - name: version 500 | value: "{{values.version}}" 501 | ``` 502 | 503 | Git 生成器有两个子生成器:Git 目录生成器和 Git 文件生成器。 504 | 505 | # 7.7 Git 目录生成器 506 | 507 | 它使用指定 Git 仓库的目录结构生成参数。 508 | 509 | ## 7.7.1 基本使用 510 | 511 | ```yaml 512 | apiVersion: argoproj.io/v1alpha1 513 | kind: ApplicationSet 514 | metadata: 515 | name: cluster-addons 516 | namespace: argocd 517 | spec: 518 | generators: 519 | - git: 520 | repoURL: https://github.com/argoproj-labs/applicationset.git 521 | revision: HEAD 522 | directories: 523 | - path: examples/git-generator-directory/cluster-addons/* 524 | template: 525 | metadata: 526 | name: "{{path.basename}}" 527 | spec: 528 | project: default 529 | source: 530 | repoURL: https://github.com/argoproj-labs/applicationset.git 531 | targetRevision: HEAD 532 | path: "{{path}}" 533 | destination: 534 | server: https://kubernetes.default.svc 535 | namespace: "{{path.basename}}" 536 | ``` 537 | 538 | ```text 539 | 1. {{path}}: 与 path 通配符匹配的 Git 仓库目录路径 540 | 2. {{path.basename}}: path 路径的目录名。 541 | ``` 542 | 543 | ## 7.7.2 排除目录 544 | 545 | 有时并非所有的目录都需要生成参数,可以通过如下两种方式排除目录: 546 | 547 | 1. Git 目录生成器会自动排除 `.` 开头的文件夹; 548 | 2. 通过 exclude 选项排除单个目录; 549 | 550 | 需要注意,exclude 的优先级最高,即一个目录一旦被排除,其子目录也会被排除,而且即使明确写明包含目录也无效。exclude 不受 path 的先后顺序影响。 551 | 552 | ```yaml 553 | apiVersion: argoproj.io/v1alpha1 554 | kind: ApplicationSet 555 | metadata: 556 | name: cluster-addons 557 | namespace: argocd 558 | spec: 559 | generators: 560 | - git: 561 | repoURL: https://github.com/argoproj-labs/applicationset.git 562 | revision: HEAD 563 | directories: 564 | - path: examples/git-generator-directory/excludes/cluster-addons/* 565 | - path: examples/git-generator-directory/excludes/cluster-addons/exclude-helm-guestbook 566 | exclude: true 567 | template: 568 | metadata: 569 | name: "{{path.basename}}" 570 | spec: 571 | project: default 572 | source: 573 | repoURL: https://github.com/argoproj-labs/applicationset.git 574 | targetRevision: HEAD 575 | path: "{{path}}" 576 | destination: 577 | server: https://kubernetes.default.svc 578 | namespace: "{{path.basename}}" 579 | ``` 580 | 581 | ## 7.7.3 Git 文件生成器 582 | 583 | 在 git 仓库中找到 JSON/YAML 文件并使用其内容生成参数。 584 | 585 | ```yaml 586 | apiVersion: argoproj.io/v1alpha1 587 | kind: ApplicationSet 588 | metadata: 589 | name: guestbook 590 | namespace: argocd 591 | spec: 592 | generators: 593 | - git: 594 | repoURL: https://github.com/argoproj-labs/applicationset.git 595 | revision: HEAD 596 | files: 597 | - path: "examples/git-generator-files-discovery/cluster-config/**/config.json" 598 | template: 599 | metadata: 600 | name: "{{cluster.name}}-guestbook" 601 | spec: 602 | project: default 603 | source: 604 | repoURL: https://github.com/argoproj-labs/applicationset.git 605 | targetRevision: HEAD 606 | path: "examples/git-generator-files-discovery/apps/guestbook" 607 | destination: 608 | server: "{{cluster.address}}" 609 | namespace: guestbook 610 | ``` 611 | 612 | 上述 cluster-config 目录下找到的任何 config.json 文件,都讲会根据其内容生成参数。JSON 字段被扁平化为键值对。 613 | 614 | 除了来自配置文件的扁平化键值对之外,还提供了 Git 目录生成器的参数`{{path}}`, `{{path.basename}}` 615 | 616 | # 7.8 矩阵生成器 617 | 618 | ```yaml 619 | apiVersion: argoproj.io/v1alpha1 620 | kind: ApplicationSet 621 | metadata: 622 | name: list-git 623 | namespace: argocd 624 | spec: 625 | generators: 626 | - matrix: 627 | generators: 628 | - clusters: {} 629 | - list: 630 | elements: 631 | - color: red 632 | size: middle 633 | - color: green 634 | size: large 635 | template: 636 | metadata: 637 | name: "{{name}}-{{color}}-guestbook" 638 | namespace: guestbook 639 | spec: 640 | project: default 641 | source: 642 | repoURL: https://github.com/argoproj/argocd-example-apps.git 643 | targetRevision: HEAD 644 | path: kustomize-guestbook 645 | destination: 646 | server: "{{server}}" 647 | namespace: "{{color}}-{{size}}" 648 | syncPolicy: 649 | syncOptions: 650 | - CreateNamespace=true 651 | info: 652 | - name: "color" 653 | value: "{{color}}" 654 | - name: "size" 655 | value: "{{size}}" 656 | ``` 657 | 658 | # 8. 鸣谢及推荐 659 | 660 | 非常感谢 Devops 中国社区的 @martinliu 和 极狐布道师 @小马哥,正是两位前辈不遗余力地推送此次鉴宝活动,这次分享才能如约与大家见面,于我个人而言,也才有幸认识诸位鉴宝讲师以及更多热爱开源的“技术人”。 661 | 662 | 如果在学习 ArgoCD 时有任何疑问或想法,欢迎加我的微信 - hsdtsyl ("黑色的碳酸饮料" 首字母) - 交流。 663 | 664 | > 推荐: ArgoCD 相关视频教程 665 | 666 | https://space.bilibili.com/322351901 667 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/app-guestbook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: guestbook 5 | spec: 6 | destination: 7 | name: "" 8 | namespace: guestbook 9 | server: "https://kubernetes.default.svc" 10 | source: 11 | path: kustomize-guestbook 12 | repoURL: "https://github.com/argoproj/argocd-example-apps.git" 13 | targetRevision: HEAD 14 | project: default 15 | syncPolicy: 16 | automated: 17 | prune: true 18 | selfHeal: true 19 | syncOptions: 20 | - CreateNamespace=true 21 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/apps-matrix-generator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: list-git 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - matrix: 9 | generators: 10 | - clusters: {} 11 | - list: 12 | elements: 13 | - color: red 14 | size: middle 15 | - color: green 16 | size: large 17 | template: 18 | metadata: 19 | name: "{{name}}-{{color}}-guestbook" 20 | namespace: guestbook 21 | spec: 22 | project: default 23 | source: 24 | repoURL: https://github.com/argoproj/argocd-example-apps.git 25 | targetRevision: HEAD 26 | path: kustomize-guestbook 27 | destination: 28 | server: "{{server}}" 29 | namespace: "{{color}}-{{size}}" 30 | syncPolicy: 31 | syncOptions: 32 | - CreateNamespace=true 33 | info: 34 | - name: "color" 35 | value: "{{color}}" 36 | - name: "size" 37 | value: "{{size}}" 38 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-cluster-generator-base.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: guestbook-cluster 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - clusters: {} # Automatically use all clusters defined within Argo CD 9 | template: 10 | metadata: 11 | name: "{{name}}-guestbook" 12 | namespace: guestbook 13 | spec: 14 | project: default 15 | source: 16 | repoURL: https://github.com/argoproj/argocd-example-apps.git 17 | targetRevision: HEAD 18 | path: kustomize-guestbook 19 | destination: 20 | server: "{{server}}" 21 | namespace: guestbook 22 | syncPolicy: 23 | automated: {} 24 | syncOptions: 25 | - CreateNamespace=true 26 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-cluster-generator-label.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: guestbook-cluster 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - clusters: 9 | selector: 10 | matchLabels: 11 | argocd.argoproj.io/secret-type: cluster 12 | template: 13 | metadata: 14 | name: "{{name}}-guestbook" 15 | namespace: guestbook 16 | spec: 17 | project: default 18 | source: 19 | repoURL: https://github.com/argoproj/argocd-example-apps.git 20 | targetRevision: HEAD 21 | path: kustomize-guestbook 22 | destination: 23 | server: "{{server}}" 24 | namespace: guestbook 25 | syncPolicy: 26 | automated: {} 27 | syncOptions: 28 | - CreateNamespace=true 29 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-cluster-generator-value.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: guestbook-cluster 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - clusters: 9 | selector: 10 | matchLabels: 11 | name: tuesday 12 | values: 13 | version: v1 14 | template: 15 | metadata: 16 | name: "{{name}}-guestbook" 17 | namespace: guestbook 18 | spec: 19 | project: default 20 | source: 21 | repoURL: https://github.com/argoproj/argocd-example-apps.git 22 | targetRevision: HEAD 23 | path: kustomize-guestbook 24 | destination: 25 | server: "{{server}}" 26 | namespace: guestbook 27 | syncPolicy: 28 | automated: {} 29 | syncOptions: 30 | - CreateNamespace=true 31 | info: 32 | - name: version 33 | value: "{{values.version}}" 34 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-git-generator-base.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: cluster-addons 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - git: 9 | repoURL: https://github.com/argoproj-labs/applicationset.git 10 | revision: HEAD 11 | directories: 12 | - path: examples/git-generator-directory/cluster-addons/* 13 | template: 14 | metadata: 15 | name: "{{path.basename}}" 16 | spec: 17 | project: default 18 | source: 19 | repoURL: https://github.com/argoproj-labs/applicationset.git 20 | targetRevision: HEAD 21 | path: "{{path}}" 22 | destination: 23 | server: https://kubernetes.default.svc 24 | namespace: "{{path.basename}}" 25 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-git-generator-file.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: guestbook 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - git: 9 | repoURL: https://github.com/argoproj-labs/applicationset.git 10 | revision: HEAD 11 | files: 12 | - path: "examples/git-generator-files-discovery/cluster-config/**/config.json" 13 | template: 14 | metadata: 15 | name: "{{cluster.name}}-guestbook" 16 | spec: 17 | project: default 18 | source: 19 | repoURL: https://github.com/argoproj-labs/applicationset.git 20 | targetRevision: HEAD 21 | path: "examples/git-generator-files-discovery/apps/guestbook" 22 | destination: 23 | server: "{{cluster.address}}" 24 | namespace: guestbook 25 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-git-gernator-exclude.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: cluster-addons 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - git: 9 | repoURL: https://github.com/argoproj-labs/applicationset.git 10 | revision: HEAD 11 | directories: 12 | - path: examples/git-generator-directory/excludes/cluster-addons/* 13 | - path: examples/git-generator-directory/excludes/cluster-addons/exclude-helm-guestbook 14 | exclude: true 15 | template: 16 | metadata: 17 | name: "{{path.basename}}" 18 | spec: 19 | project: default 20 | source: 21 | repoURL: https://github.com/argoproj-labs/applicationset.git 22 | targetRevision: HEAD 23 | path: "{{path}}" 24 | destination: 25 | server: https://kubernetes.default.svc 26 | namespace: "{{path.basename}}" 27 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/appset-list-generator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ApplicationSet 3 | metadata: 4 | name: guestbook 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - list: 9 | elements: 10 | - cluster: monday 11 | url: https://kubernetes.default.svc 12 | - cluster: tuesday 13 | url: https://tuesday-api.automan.fun:6443 14 | template: 15 | metadata: 16 | name: "{{cluster}}-guestbook" 17 | namespace: guestbook 18 | spec: 19 | project: default 20 | source: 21 | repoURL: https://github.com/argoproj/argocd-example-apps.git 22 | targetRevision: HEAD 23 | path: kustomize-guestbook 24 | destination: 25 | server: "{{url}}" 26 | namespace: guestbook 27 | syncPolicy: 28 | automated: {} 29 | syncOptions: 30 | - CreateNamespace=true 31 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/argocd.yaml: -------------------------------------------------------------------------------- 1 | # ref: https://operatorhub.io/operator/argocd-operator 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: ArgoCD 4 | metadata: 5 | name: monday-argocd 6 | spec: {} 7 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/config-monday.yaml: -------------------------------------------------------------------------------- 1 | # kk create config -f config-monday.yaml 2 | apiVersion: kubekey.kubesphere.io/v1alpha1 3 | kind: Cluster 4 | metadata: 5 | name: monday 6 | spec: 7 | hosts: 8 | - { 9 | name: tm-opsinit-01, 10 | address: 10.10.14.99, 11 | internalAddress: 10.10.14.99, 12 | } 13 | roleGroups: 14 | etcd: 15 | - tm-opsinit-01 16 | master: 17 | - tm-opsinit-01 18 | worker: 19 | - tm-opsinit-01 20 | controlPlaneEndpoint: 21 | domain: monday-api.automan.fun 22 | address: "" 23 | port: 6443 24 | kubernetes: 25 | version: v1.19.8 26 | imageRepo: kubesphere 27 | clusterName: cluster.local 28 | network: 29 | plugin: calico 30 | kubePodsCIDR: 10.233.64.0/18 31 | kubeServiceCIDR: 10.233.0.0/18 32 | registry: 33 | registryMirrors: [] 34 | insecureRegistries: [] 35 | addons: [] 36 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/crds/eip-pool.yaml: -------------------------------------------------------------------------------- 1 | # ref: https://openelb.github.io/docs/getting-started/configuration/configure-ip-address-pools-using-eip/ 2 | # 集群中所有节点的网卡名必须一致 3 | apiVersion: network.kubesphere.io/v1alpha2 4 | kind: Eip 5 | metadata: 6 | name: eip-pool 7 | spec: 8 | address: 10.10.14.91-10.10.14.92 9 | protocol: layer2 10 | interface: ens192 11 | disable: false 12 | -------------------------------------------------------------------------------- /deploy/lab04-argocd/images/argocd-ui-create-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/deploy/lab04-argocd/images/argocd-ui-create-app.png -------------------------------------------------------------------------------- /deploy/lab04-argocd/images/argocd-ui-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/deploy/lab04-argocd/images/argocd-ui-login.png -------------------------------------------------------------------------------- /deploy/lab04-argocd/images/guest-book-app-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/deploy/lab04-argocd/images/guest-book-app-1.png -------------------------------------------------------------------------------- /deploy/lab04-argocd/images/guest-book-app-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/deploy/lab04-argocd/images/guest-book-app-2.png -------------------------------------------------------------------------------- /orchestration/lab10-k3s/README.md: -------------------------------------------------------------------------------- 1 | <div align=center><img src="https://rancher.com/assets/img/brand-guidelines/project-logos/k3s/horizontal/logo-horizontal-k3s.svg" width="500px"></div> 2 | 3 | --- 4 | 5 | # K3s 介绍 6 | 7 | K3s 是一个经过 CNCF 认证的轻量级的 Kubernetes 发行版,可用于生产、易于安装、内存减半,2020 年捐献给 CNCF。 8 | 9 | **非常适合:** 10 | 11 | - Edge 12 | - IoT 13 | - CI 14 | - Development 15 | - ARM 16 | - Embedding k8s 17 | 18 | **特点:** 19 | 20 | - CNCF 认证的 Kubernetes 发行版 21 | - 不到 100MB 二进制包,500MB 内存消耗 22 | - 单一进程包含 Kubernetes master, Kubelet 和 containerd 23 | - 支持 SQLite/Mysql/PostgreSQL/MariaDB 和 etcd 24 | - 同时为 x86_64, Arm64, 和 Armv7 平台发布 25 | 26 | ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h5hmpty0wbj21bm0u0wh3.jpg) 27 | 28 | ## 为什么叫 K3s? 29 | 30 | 我们希望安装的 Kubernetes 在内存占用方面只是一半的大小。Kubernetes 是一个 10 个字母的单词,简写为 K8s。所以,有 Kubernetes 一半大的东西就是一个 5 个字母的单词,简写为 K3s。K3s 没有全称,也没有官方的发音。 31 | 32 | ## 为什么比 Kubernetes 节省资源? 33 | 34 | - 通过在单个进程内运行许多组件来减少内存占用。这消除了每个组件会重复的大量开销。 35 | - 通过删除第三方存储驱动程序和云提供商,二进制文件变得更小。 36 | 37 | ## 发布周期 38 | 39 | K3s 与上游 Kubernetes 版本保持同步。目标是在几天内与上游版本和次要版本在同一天发布补丁版本。 40 | 41 | **v1.20.4+k3s1**: `v1.20.4` 为 K8s 版本,`k3s1` 为补丁版本,如果我们在 `v1.20.4+k3s1` 中发现了一个严重程度较高的错误并需要立即发布修复,我们将发布 `v1.20.4+k3s2`。 42 | 43 | ## 架构 44 | 45 | ![](https://camo.githubusercontent.com/fb753e178fce8d3b13a7d8c66a39ad7e37f693a80ae589fd877a732098179c8b/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f30303865476d5a456c7931676f3079773531766c676a33316976307530337a372e6a7067) 46 | 47 | ## 参考文档 48 | 49 | - 中文文档:https://docs.rancher.cn/k3s 50 | - 英文文档:https://rancher.com/docs/k3s/latest/en/ 51 | - Github 地址:https://github.com/k3s-io/k3s 52 | 53 | # K3s 安装 54 | 55 | ## 安装要求 56 | 57 | ### OS 58 | 59 | K3s 有望在大多数现代 Linux 系统上运行: 60 | 61 | - SLES: 15 SP3, 15 SP2, 15 SP1 62 | - SLE Micro: 5.1 63 | - OpenSUSE Leap: 15.3 64 | - Ubuntu: 18.04, 20.04 65 | - CentOS: 7.8, 7.9 66 | - Oracle Linux: 8.3、7.9 67 | - RHEL: 7.8, 7.9, 8.2, 8.3, 8.4, 8.5 68 | - Rocky Linux: 8.4 69 | 70 | ### CPU 和内存 71 | 72 | - CPU: 最低 1 73 | - 内存: 最低 512MB(建议至少为 1GB) 74 | 75 | ### 磁盘 76 | 77 | K3s 的性能取决于数据库的性能。为了确保最佳速度,我们建议尽可能使用 SSD。在使用 SD 卡或 eMMC 的 ARM 设备上,磁盘性能会有所不同。 78 | 79 | ## 单节点安装 80 | 81 | ![](https://docs.rancher.cn/assets/images/k3s-architecture-single-server-42bb3c4899985b4f6d8fd0e2130e3c0e.png) 82 | 83 | **官方安装脚本:** 84 | 85 | ``` 86 | # 安装 k3s server 节点: 87 | curl -sfL https://get.k3s.io | sh - 88 | 89 | # 安装 k3s agent 节点: 90 | curl -sfL https://get.k3s.io | \ 91 | K3S_URL=https://myserver:6443 \ 92 | K3S_TOKEN=mynodetoken \ 93 | sh - 94 | ``` 95 | 96 | #### 安装环境 97 | 98 | | 角色 | 主机名 | IP | 99 | | ---------- | ------------- | ------------ | 100 | | k3s server | k3s-single-m | 10.24.12.139 | 101 | | k3s agent | k3s-single-w1 | 10.24.12.140 | 102 | | k3s agent | k3s-single-w2 | 10.24.12.142 | 103 | 104 | #### 部署结构图 105 | 106 | ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h5j0cps6tfj218o0r63zv.jpg) 107 | 108 | #### 启动 K3s Server 109 | 110 | 国内推荐使用: 111 | 112 | ``` 113 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | \ 114 | INSTALL_K3S_MIRROR=cn \ 115 | K3S_TOKEN=devops \ 116 | sh -s - \ 117 | --system-default-registry "registry.cn-hangzhou.aliyuncs.com" 118 | ``` 119 | 120 | #### 添加 K3s Agent 节点 121 | 122 | 分别在两个 agent 主机上执行以下安装 k3s agent 命令: 123 | 124 | 国内推荐使用: 125 | 126 | ``` 127 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 128 | K3S_URL=https://10.24.12.139:6443 \ 129 | K3S_TOKEN=devops \ 130 | sh -s - 131 | ``` 132 | 133 | ## 高可用安装 134 | 135 | ### 使用外部数据库实现高可用安装 136 | 137 | ![](https://docs.rancher.cn/assets/images/k3s-architecture-ha-server-46bf4c38e210246bda5920127bbecd53.png) 138 | 139 | K3s 支持以下外部数据存储选项: 140 | 141 | - PostgreSQL (经过认证的版本:10.7 和 11.5) 142 | - MySQL (经过认证的版本:5.7) 143 | - MariaDB (经过认证的版本:10.3.20) 144 | - etcd (经过认证的版本:3.3.15) 145 | 146 | 单节点 k3s server 集群可以满足各种用例,但是对于需要 Kubernetes control-plane 稳定运行的重要环境,您可以在 HA 配置中运行 K3s。一个 K3s HA 集群由以下几个部分组成: 147 | 148 | - 两个或多个 server 节点,将为 Kubernetes API 提供服务并运行其他 control-plane 服务。 149 | - 零个或多个 agent 节点,用于运行您的应用和服务。 150 | - 外部数据存储 (与单个 k3s server 设置中使用的嵌入式 SQLite 数据存储相反) 151 | - 固定的注册地址,位于 server 节点的前面,以允许 agent 节点向集群注册 152 | 153 | **安装环境:** 154 | 155 | | 角色 | 主机名 | IP | 156 | | ------------ | -------- | ------------ | 157 | | k3s server 1 | k3s-ha-1 | 10.24.12.141 | 158 | | k3s server 2 | k3s-ha-2 | 10.24.12.143 | 159 | | k3s agent | k3s-ha-2 | 10.24.12.144 | 160 | | DB | k3s-ha-4 | 10.24.12.145 | 161 | 162 | 安装 mysql 数据库: 163 | 164 | > 在 DB 165 | 166 | ``` 167 | docker run --name some-mysql --restart=unless-stopped -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d mysql:5.7 168 | ``` 169 | 170 | 添加第一个 server 节点: 171 | 172 | ``` 173 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 174 | sh -s - \ 175 | server \ 176 | --token=devops \ 177 | --datastore-endpoint="mysql://root:password@tcp(10.24.12.145:3306)/k3s_db" \ 178 | --system-default-registry "registry.cn-hangzhou.aliyuncs.com" 179 | ``` 180 | 181 | 加入其他的 server 节点: 182 | 183 | ``` 184 | # 然后可以使用 token添加其他 server 节点: 185 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 186 | sh -s - \ 187 | server \ 188 | --token=devops \ 189 | --datastore-endpoint="mysql://root:password@tcp(10.24.12.145:3306)/k3s_db" \ 190 | --system-default-registry "registry.cn-hangzhou.aliyuncs.com" 191 | ``` 192 | 193 | 加入 agent 节点: 194 | 195 | ``` 196 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 197 | K3S_URL=https://10.24.12.141:6443 \ 198 | K3S_TOKEN=devops \ 199 | sh -s - 200 | ``` 201 | 202 | 更多集群数据存储选项,请参考 [K3s 文档](https://docs.rancher.cn/docs/k3s/installation/datastore/_index/) 203 | 204 | **生产案例:** https://mp.weixin.qq.com/s/0Wk2MzfWqMqt8DfUK_2ICA 205 | 206 | ### 嵌入式 DB 的高可用 207 | 208 | 要在这种模式下运行 K3s,你必须有奇数的 server 节点。我们建议从三个节点开始。 209 | 210 | 要开始运行,首先启动一个 server 节点,使用 cluster-init 标志来启用集群,并使用一个标记作为共享的密钥来加入其他服务器到集群中。 211 | 212 | ``` 213 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 214 | K3S_TOKEN=SECRET sh -s - \ 215 | server \ 216 | --cluster-init \ 217 | --system-default-registry "registry.cn-hangzhou.aliyuncs.com" 218 | ``` 219 | 220 | 启动第一台 server 后,使用共享密钥将第二台和第三台 server 加入集群。 221 | 222 | ``` 223 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 224 | K3S_TOKEN=SECRET sh -s - server \ 225 | --server https://<ip or hostname of server1>:6443 \ 226 | --system-default-registry "registry.cn-hangzhou.aliyuncs.com" 227 | ``` 228 | 229 | 查询 ETCD 集群状态: 230 | 231 | ``` 232 | ETCDCTL_ENDPOINTS='https://< server ip >:2379,https://< server ip >:2379,https://< server ip >:2379' \ 233 | ETCDCTL_CACERT='/var/lib/rancher/k3s/server/tls/etcd/server-ca.crt' \ 234 | ETCDCTL_CERT='/var/lib/rancher/k3s/server/tls/etcd/server-client.crt' \ 235 | ETCDCTL_KEY='/var/lib/rancher/k3s/server/tls/etcd/server-client.key' \ 236 | ETCDCTL_API=3 etcdctl endpoint status --write-out=table 237 | ``` 238 | 239 | > etcd 证书默认目录:/var/lib/rancher/k3s/server/tls/etcd etcd 数据默认目录:/var/lib/rancher/k3s/server/db/etcd 240 | 241 | # 访问集群 242 | 243 | 存储在/etc/rancher/k3s/k3s.yaml 的 kubeconfig 文件用于对 Kubernetes 集群的访问。如果你已经安装了上游的 Kubernetes 命令行工具,如 kubectl 或 helm,你需要用正确的 kubeconfig 路径配置它们。这可以通过导出 KUBECONFIG 环境变量或调用--kubeconfig 命令行标志来完成。详情请参考下面的例子。 244 | 245 | ``` 246 | export KUBECONFIG=/etc/rancher/k3s/k3s.yaml 247 | kubectl get pods --all-namespaces 248 | helm ls --all-namespaces 249 | ``` 250 | 251 | # 网络选项 252 | 253 | 默认情况下,K3s 将以 flannel 作为 CNI 运行,使用 VXLAN 作为默认后端。CNI 和默认后端都可以通过参数修改。 254 | 255 | ## Flannel 选项 256 | 257 | Flannel 的默认后端是 VXLAN。要启用加密,请使用下面的 IPSec(Internet Protocol Security)或 WireGuard 选项。 258 | 259 | | CLI Flag 和 Value | 描述 | 260 | | :---------------------------- | :---------------------------------------------------------------------- | 261 | | `--flannel-backend=vxlan` | (默认) 使用 VXLAN 后端。 | 262 | | `--flannel-backend=ipsec` | 使用 IPSEC 后端,对网络流量进行加密。 | 263 | | `--flannel-backend=host-gw` | 使用 host-gw 后端。 | 264 | | `--flannel-backend=wireguard` | 使用 WireGuard 后端,对网络流量进行加密。可能需要额外的内核模块和配置。 | 265 | 266 | ``` 267 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | \ 268 | INSTALL_K3S_EXEC="--flannel-backend=host-gw" \ 269 | INSTALL_K3S_MIRROR=cn sh - 270 | ``` 271 | 272 | ## 自定义 CNI 273 | 274 | 使用 `--flannel-backend=none` 运行 K3s,然后在安装你选择的 CNI。 275 | 276 | #### Calico 277 | 278 | 参考:https://docs.projectcalico.org/getting-started/kubernetes/k3s/quickstart 279 | 280 | #### Cilium 281 | 282 | 参考:https://docs.cilium.io/en/v1.9/gettingstarted/k3s/ 283 | 284 | # 仪表盘 285 | 286 | ## 仪表盘 287 | 288 | - Kubernetes Dashboard 289 | - Rancher UI 290 | - kube-explorer 291 | 292 | ## 1. Kubernetes Dashboard 293 | 294 | 参考 [K3s 官网](http://docs.rancher.cn/docs/k3s/installation/kube-dashboard/_index/) 295 | 296 | ## 2. Rancher UI 297 | 298 | 可以将 K3s 导入到 Rancher UI 中去管理,参考 [Rancher 官网](http://docs.rancher.cn/docs/rancher2/cluster-provisioning/imported-clusters/_index/#%E5%AF%BC%E5%85%A5-k3s-%E9%9B%86%E7%BE%A4) 299 | 300 | 导入 K3s 集群时,Rancher 会将其识别为 K3s,除了其他导入的集群支持的功能之外,Rancher UI 还提供以下功能: 301 | 302 | - 能够升级 K3s 版本。 303 | - 能够配置在升级集群时,同时可以升级的最大节点数。 304 | - 在主机详情页,能够查看(不能编辑)启动 K3s 集群时每个节点的 K3s 配置参数和环境变量。 305 | 306 | ## 3. kube-explorer 307 | 308 | 项目地址:https://github.com/cnrancher/kube-explorer 309 | 310 | kube-explorer 是 Kubernetes 的便携式资源管理器,没有任何依赖。 并提供了一个几乎完全无状态的 Kubernetes 资源管理器。 311 | 312 | #### 使用 313 | 314 | 从[发布页面](https://github.com/cnrancher/kube-explorer/releases)下载二进制文件 315 | 316 | 运行: 317 | 318 | ``` 319 | ./kube-explorer --kubeconfig=xxxx --http-listen-port=9898 --https-listen-port=0 320 | ``` 321 | 322 | 然后,打开浏览器访问 http://x.x.x.x:9898 323 | 324 | # K3s 升级 325 | 326 | ## 使用安装脚本升级 K3s 327 | 328 | 要从旧版本升级 K3s,你可以使用相同的标志重新运行安装脚本,例如: 329 | 330 | ``` 331 | curl -sfL https://get.k3s.io | sh - 332 | 333 | # 国内环境可使用: 334 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh - 335 | ``` 336 | 337 | 如果你想升级到一个特定 channel 的较新版本(如最新),你可以指定 channel: 338 | 339 | ``` 340 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=c \ 341 | INSTALL_K3S_CHANNEL=latest sh - 342 | ``` 343 | 344 | 如果你想升级到特定的版本,你可以运行以下命令: 345 | 346 | ``` 347 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=c | \ 348 | INSTALL_K3S_VERSION=vX.Y.Z-rc1 sh - 349 | ``` 350 | 351 | ## 使用二进制文件手动升级 K3s 352 | 353 | 1. 从[发布](https://github.com/rancher/k3s/releases)下载所需版本的 K3s 二进制文件 354 | 2. 将下载的二进制文件复制到 `/usr/local/bin/k3s`(或您所需的位置) 355 | 3. 重启 k3s 356 | 357 | ## 自动升级 358 | 359 | 使用 Rancher 的 system-upgrad-controller 来管理 K3s 集群升级。 360 | 361 | #### 安装 system-upgrade-controller 362 | 363 | ``` 364 | kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/system-upgrade-controller.yaml 365 | ``` 366 | 367 | #### 配置计划 368 | 369 | 建议您最少创建两个计划:升级 server(master)节点的计划和升级 agent(worker)节点的计划。根据需要,您可以创建其他计划来控制跨节点的滚动升级。以下两个示例计划将把您的集群升级到 K3s v1.17.4+k3s1。创建计划后,控制器将接收这些计划并开始升级您的集群。 370 | 371 | ``` 372 | # Server plan 373 | apiVersion: upgrade.cattle.io/v1 374 | kind: Plan 375 | metadata: 376 | name: server-plan 377 | namespace: system-upgrade 378 | spec: 379 | concurrency: 1 380 | cordon: true 381 | nodeSelector: 382 | matchExpressions: 383 | - key: node-role.kubernetes.io/master 384 | operator: In 385 | values: 386 | - "true" 387 | serviceAccountName: system-upgrade 388 | upgrade: 389 | image: rancher/k3s-upgrade 390 | version: v1.17.4+k3s1 391 | --- 392 | # Agent plan 393 | apiVersion: upgrade.cattle.io/v1 394 | kind: Plan 395 | metadata: 396 | name: agent-plan 397 | namespace: system-upgrade 398 | spec: 399 | concurrency: 1 400 | cordon: true 401 | nodeSelector: 402 | matchExpressions: 403 | - key: node-role.kubernetes.io/master 404 | operator: DoesNotExist 405 | prepare: 406 | args: 407 | - prepare 408 | - server-plan 409 | image: rancher/k3s-upgrade 410 | serviceAccountName: system-upgrade 411 | upgrade: 412 | image: rancher/k3s-upgrade 413 | version: v1.17.4+k3s1 414 | ``` 415 | 416 | # 备份和恢复 417 | 418 | K3s 的备份和恢复方式取决于使用的数据存储类型: 419 | 420 | - 使用嵌入式 SQLite 数据存储进行备份和恢复 421 | - 使用外部数据存储进行备份和恢复 422 | - 使用嵌入式 etcd 数据存储进行备份和恢复 423 | 424 | ## 使用嵌入式 SQLite 数据存储进行备份和恢复 425 | 426 | #### 方式 1:备份/恢复数据目录 427 | 428 | - 备份 429 | 430 | ``` 431 | # cp -rf /var/lib/rancher/k3s/server/db /opt/db 432 | ``` 433 | 434 | - 恢复 435 | 436 | ``` 437 | # systemctl stop k3s 438 | # rm -rf /var/lib/rancher/k3s/server/db 439 | # cp -rf /opt/db /var/lib/rancher/k3s/server/db 440 | # systemctl start k3s 441 | ``` 442 | 443 | #### 方式 2:通过 SQLite cli 444 | 445 | - 备份 446 | 447 | ``` 448 | # sqlite3 /var/lib/rancher/k3s/server/db/state.db 449 | SQLite version 3.22.0 2018-01-22 18:45:57 450 | Enter ".help" for usage hints. 451 | sqlite> .backup "/opt/kine.db" 452 | sqlite> .exit 453 | ``` 454 | 455 | - 恢复 456 | 457 | ``` 458 | # systemctl stop k3s 459 | 460 | # sqlite3 /var/lib/rancher/k3s/server/db/state.db 461 | SQLite version 3.22.0 2018-01-22 18:45:57 462 | Enter ".help" for usage hints. 463 | sqlite> .restore '/opt/kine.db' 464 | sqlite> .exit 465 | 466 | # systemctl start k3s 467 | ``` 468 | 469 | ## 使用外部数据存储进行备份和恢复 470 | 471 | 当使用外部数据存储时,备份和恢复操作是在 K3s 之外处理的。数据库管理员需要对外部数据库进行备份,或者从快照或转储中进行恢复。我们建议将数据库配置为执行定期快照。 472 | 473 | - 备份 474 | 475 | ``` 476 | # mysqldump -uroot -p --all-databases --master-data > k3s-dbdump.db 477 | ``` 478 | 479 | - 恢复 480 | 481 | 停止 K3s 服务 482 | 483 | ``` 484 | # systemctl stop k3s 485 | ``` 486 | 487 | 恢复 mysql 数据 488 | 489 | ``` 490 | mysql -uroot -p < k3s-dbdump.db 491 | ``` 492 | 493 | 启动 K3s 服务 494 | 495 | ``` 496 | # systemctl start k3s 497 | ``` 498 | 499 | ## 使用嵌入式 etcd 数据存储进行备份和恢复 500 | 501 | - 创建快照 502 | 503 | K3s 默认启用快照。快照目录默认为 `/var/lib/rancher/k3s/server/db/snapshots`。要配置快照间隔或保留的快照数量,请参考: 504 | 505 | | 参数 | 描述 | 506 | | :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------- | 507 | | `--etcd-disable-snapshots` | 禁用自动 etcd 快照 | 508 | | `--etcd-snapshot-schedule-cron` | 以 Cron 表达式的形式配置触发定时快照的时间点,例如:每 5 小时触发一次`* */5 * * *`,默认值为每 12 小时触发一次:`0 */12 * * *` | 509 | | `--etcd-snapshot-retention` | 保留的快照数量,默认值为 5。 | 510 | | `--etcd-snapshot-dir` | 保存数据库快照的目录路径。(默认位置:`${data-dir}/db/snapshots`) | 511 | | `--cluster-reset` | 忘记所有的对等体,成为新集群的唯一成员,也可以通过环境变量`[$K3S_CLUSTER_RESET]`进行设置。 | 512 | | `--cluster-reset-restore-path` | 要恢复的快照文件的路径 | 513 | 514 | - 从快照恢复集群 515 | 516 | 当 K3s 从备份中恢复时,旧的数据目录将被移动到`/var/lib/rancher/k3s/server/db/etcd-old/`。然后 K3s 会尝试通过创建一个新的数据目录来恢复快照,然后从一个带有一个 etcd 成员的新 K3s 集群启动 etcd。 517 | 518 | 要从备份中恢复集群,运行 K3s 时,请使用`--cluster-reset`选项运行 K3s,同时给出`--cluster-reset-restore-path`,如下: 519 | 520 | ```shell 521 | ./k3s server \ 522 | --cluster-reset \ 523 | --cluster-reset-restore-path=<PATH-TO-SNAPSHOT> 524 | ``` 525 | 526 | **结果:** 日志中出现一条信息,**Etcd 正在运行,现在需要在没有 `--cluster-reset` 标志的情况下重新启动。备份和删除每个对等 etcd 服务器上的 ${datadir}/server/db 并重新加入节点** 527 | 528 | ### S3 兼容 API 支持 529 | 530 | K3s 支持向具有 S3 兼容 API 的系统写入 etcd 快照和从系统中恢复 etcd 快照。S3 支持按需和计划快照。 531 | 532 | 下面的参数已经被添加到 `server` 子命令中。这些标志也存在于 `etcd-snapshot` 子命令中,但是 `--etcd-s3` 部分被删除以避免冗余。 533 | 534 | ``` 535 | k3s etcd-snapshot \ 536 | --s3 \ 537 | # --s3-endpoint minio.kingsd.top:9000 \ 538 | --s3-bucket=<S3-BUCKET-NAME> \ 539 | --s3-access-key=<S3-ACCESS-KEY> \ 540 | --s3-secret-key=<S3-SECRET-KEY> 541 | ``` 542 | 543 | 要从 S3 中执行按需的 etcd 快照还原,首先确保 K3s 没有运行。然后运行以下命令: 544 | 545 | ``` 546 | k3s server \ 547 | --cluster-init \ 548 | --cluster-reset \ 549 | --etcd-s3 \ 550 | # --etcd-s3-endpoint minio.kingsd.top:9000 \ 551 | --cluster-reset-restore-path=<SNAPSHOT-NAME> \ 552 | --etcd-s3-bucket=<S3-BUCKET-NAME> \ 553 | --etcd-s3-access-key=<S3-ACCESS-KEY> \ 554 | --etcd-s3-secret-key=<S3-SECRET-KEY> 555 | ``` 556 | 557 | # 使用 docker 作为容器运行时 558 | 559 | ``` 560 | curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn \ 561 | sh -s - \ 562 | --docker 563 | ``` 564 | 565 | # 自动部署清单 566 | 567 | 在 `/var/lib/rancher/k3s/server/manifests` 中找到的任何文件都会以类似 `kubectl apply` 的方式自动部署到 Kubernetes,在启动和在磁盘上更改文件时都是如此。从该目录中删除文件不会从集群中删除相应的资源。 568 | 569 | # Service Load Balancer 570 | 571 | K3s 提供了一个名为[Klipper Load Balancer](https://github.com/rancher/klipper-lb)的负载均衡器,它可以使用可用的主机端口。 允许创建 LoadBalancer 类型的 Service,但不包括 LB 的实现。某些 LB 服务需要云提供商,例如 Amazon EC2 或 Microsoft Azure。相比之下,K3s service LB 使得可以在没有云提供商的情况下使用 LB 服务。 572 | 573 | ## 示例 574 | 575 | ``` 576 | # service_lb_demo.yaml 577 | apiVersion: apps/v1 578 | kind: Deployment 579 | metadata: 580 | name: nginx-deployment 581 | labels: 582 | app: nginx 583 | spec: 584 | replicas: 3 585 | selector: 586 | matchLabels: 587 | app: nginx 588 | template: 589 | metadata: 590 | labels: 591 | app: nginx 592 | spec: 593 | containers: 594 | - name: nginx 595 | image: kingsd/nginx:install-tools 596 | ports: 597 | - containerPort: 80 598 | 599 | --- 600 | apiVersion: v1 601 | kind: Service 602 | metadata: 603 | name: nginx 604 | spec: 605 | type: LoadBalancer 606 | selector: 607 | app: nginx 608 | ports: 609 | - port: 8000 610 | targetPort: 80 611 | ``` 612 | 613 | ## Service LB 如何工作 614 | 615 | K3s 创建了一个控制器,该控制器为 service load balancer 创建了一个 Pod,这个 Pod 是[Service](https://kubernetes.io/docs/concepts/services-networking/service/)类型的 Kubernetes 对象。 616 | 617 | 对于每个 service load balancer,都会创建一个[DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/)。 DaemonSet 在每个节点上创建一个前缀为`svc`的 Pod。 618 | 619 | Service LB 控制器会监听其他 Kubernetes Services。当它找到一个 Service 后,它会在所有节点上使用 DaemonSet 为该服务创建一个代理 Pod。这个 Pod 成为其他 Service 的代理,例如,来自节点上 8000 端口的请求可以被路由到端口 8888 上的工作负载。 620 | 621 | 如果创建多个 Services,则为每个 Service 创建一个单独的 DaemonSet。 622 | 623 | 只要使用不同的端口,就可以在同一节点上运行多个 Services。 624 | 625 | 如果您尝试创建一个在 80 端口上监听的 Service LB,Service LB 将尝试在集群中找到 80 端口的空闲主机。如果该端口没有可用的主机,LB 将保持 Pending 状态。 626 | 627 | ### 从节点中排除 Service LB 628 | 629 | 要排除节点使用 Service LB,请将以下标签添加到不应排除的节点上: 630 | 631 | ``` 632 | svccontroller.k3s.cattle.io/enablelb 633 | ``` 634 | 635 | 如果使用标签,则 service load balancer 仅在标记的节点上运行。 636 | 637 | ## 禁用 Service LB 638 | 639 | 要禁用嵌入式 LB,请使用`--disable servicelb`选项运行 k3s server。 640 | 641 | 如果您希望运行其他 LB,例如 MetalLB,这是必需的。 642 | 643 | # 周边项目 644 | 645 | - [Rancher Desktop](https://github.com/rancher-sandbox/rancher-desktop):Rancher Desktop 是一款在桌面上提供容器和 Kubernetes 管理的应用。它可以在 Windows、macOS 和 Linux 上运行 646 | - [K3d](https://github.com/k3d-io/k3d):K3d 创建容器化的 k3s 集群。这意味着,你可以使用 docker 在单台机器上启动多节点 k3s 集群。 647 | - [K3sup](https://github.com/alexellis/k3sup):K3sup 是一个轻量级应用程序,可以在任何本地或远程主机上安装和使用 k3s。你只需要 ssh 访问权限和 k3sup 二进制文件即可立即获得 kubectl 访问权限。 648 | - [AutoK3s](https://github.com/cnrancher/autok3s):AutoK3s 是用于简化 K3s 集群管理的轻量级工具,你可以使用 AutoK3s 在任何地方运行 K3s 服务。支持:阿里云、腾讯云、AWS、Google、K3d、Harvester、Native 649 | 650 | # 关注 K3s 651 | 652 | ![](https://camo.githubusercontent.com/2b75358b397e2e6b0517fe4389b28af657ccfde24f9a7ac51571f516bab3b50a/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f30303865476d5a456c7931676f30327275346334796a333072363065347769352e6a7067) 653 | -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/README.md: -------------------------------------------------------------------------------- 1 | 2 | # DevOps工具鉴宝之极狐GitLab 3 | 4 | 全过程验证项目在 GitLab 中从初始化设置、计划、需求和代码管理、持续构建和扫描等全流程。 5 | 6 | 7 | ## 极狐GitLab 和 GitLab CE/EE 8 | 9 | <img src=img/jihu-gitlab.png width=60% highth=60%> 10 | 11 | ## 鉴宝剧本 12 | 13 | 模拟一个创业团队从零开始使用**极狐GitLab**的场景 14 | 15 | ### 前序准备 16 | 0. 注册极狐SaaS 17 | `https://jihulab.com/users/sign_up` 18 | 19 | 1. 登陆极狐SaaS 20 | `https://jihulab.com` 21 | 22 | 2. 设置语言环境(可选) 23 | **用户设置(User Setting) > 偏好设置(Preferences) > 本地化(Localization) > 语言(Language)** 24 | 25 | 3. 申请旗舰版试用 26 | **创建群组(Group) > 群组设置(Group Setting) > 计费(Billing) > 开始试用(Start Free Trail)** 27 | > 此处创建的群组为根群组,根群组下面可以创建子群组或项目 28 | 29 | ## 计划阶段 30 | ### 群组和项目(Group and Project) 31 | 1. 创建名为`电商BU`的群组(Group) 32 | 2. 在`电商BU`群组下面创建两个子群组(Sub Group) 33 | - 支付团队 34 | - 订单团队 35 | 3. 在`订单团队`子群组下面分别创建项目 36 | - hello-order-service 37 | 4. 在`支付团队`子群组下面分别创建项目 38 | - hello-pay-service 39 | > 项目的创建可以考虑使用项目模板(Project Template),模板有内置的,同时也支持自定义 40 | 41 | ### 标签(Label) 42 | > 本示例中创建的标签均在Group层级,Project层级也可以创建标签 43 | 1. 任务类型标签,用于标记任务的类型,如Feature,Bug,TechDebt等 44 | 2. 优先级标签,用于标记任务的优先级 45 | 3. 价值流标签,用于标记任务的状态 46 | 47 | ### 里程碑与迭代(Milestone and Iteration) 48 | 1. 创建两个`群组里程碑`,以每个月为一个里程碑,设置里程碑开始和结束的时间,类似部门级的里程碑规划 49 | 2. 基于时间创建迭代,创建4个迭代,模拟2周一个迭代,两个迭代一个里程碑的敏捷开发周期。 50 | 51 | ## 需求阶段 52 | ### 史诗与子史诗(Epics and Sub-Epics) 53 | 1. 回到`电商BU`群组,创建名为`B2B电商系统`的史诗,代表电商部门一个比较大的商业想法 54 | 2. 创建两个名为`订单功能`和`支付功能`的子史诗,代表商业想法的需求初步拆分 55 | 56 | ### 议题(Issues) 57 | 1. 在`hello-order-service`项目下创建名为`订单查询功能`的议题,代表一个用户故事 58 | 2. 关联议题到史诗、里程碑、迭代 59 | 3. 设置议题指派人、截止日期、预计投入时间、权重和标签等 60 | 61 | ### 路线图(Roadmap) 62 | 1. 为创建的史诗、子史诗和议题分别设定开始和结束时间,会自动生成产品的路线图 63 | 64 | ### 史诗看板(Epic Boards) 65 | 1. 在史诗看板中创建列表,并选择对应的标记 66 | 2. 将史诗和子史诗拖拽到对应的看板泳道中 67 | 68 | ### 议题看板(Issue Boards) 69 | 1. 在议题看板中创建列表,并选择对应的标记 70 | 2. 将议题拖拽到对应的看板泳道中 71 | 72 | ### 价值流分析 73 | 1. 回到`电商BU`群组 -> 分析(Analytics) -> 价值流(Value Stream Analytics) ->创建新的价值流(Create New Value Stream) 74 | 2. 不使用模板创建(Create from no template) -> 填入阶段名称-->选择开始和结束事件 75 | 76 | ## 开发阶段 77 | ### 推送规则(Push Rules) 78 | 推送规则可以在实例级、群组级和项目级进行配置,并批量向下继承,低层级的配置会覆盖高层级。以下以项目级为例: 79 | `hello-order-service`项目 -> 设置(Settings) -> 仓库(Repository) -> 推送规则(Push Rules) 80 | 81 | - 设置一个提交信息的规则 `^\[(([a-z,A-Z]+))\]\s#(\d*)` 82 | - 设置一个文件推送的规则 `(jar|exe)$` 83 | 84 | ### 受保护分支(Protected Branches) 85 | 受保护分支用于在分支层面更加细粒度的权限管理,仅允许指定的角色、组或用户对收到保护的分支进行推送(Push)和合并(Merge) 86 | `hello-order-service`项目 -> 设置(Settings) -> 仓库(Repository) -> 受保护分支(Protected branches) 87 | 88 | ### 审批规则(Merge Request Approval Rules) 89 | 审批规则定义在合并请求(Merge Request)在合并之前需要经过的审批配置。审批规则可以在实例级、项目级和合并请求级配置。以下以项目级为例: 90 | `hello-order-service`项目 -> 设置(Settings) -> 通用(General) -> 合并请求审批(Merge request approvals) 91 | 92 | - 禁止MR提交人审批 93 | - 指定MR的审批人 94 | 95 | --- 96 | # 极狐GitLab Runner 介绍及使用 97 | 98 | 极狐GitLab Runner 是极狐GitLab 的重要组件,是实现 CI/CD 的“瑞士军刀”,具有安装简单、使用灵活的特点,而且支持多种 CPU 架构、多种 Linux 发行版以及多种 OS。 99 | 100 | * 支持的 CPU 架构有:x86, AMD64, ARM64, ARM, s390x, ppc64le 101 | * 支持的 Linux 发行版有:CentOS, Debian, Ubuntu, RHEL, Fedora, Mint 102 | * 支持如下的 OS:Linux, Windows, macOS, FreeBSD 103 | 104 | 极狐GitLab Runner 的安装使用方式也很灵活,可以通过安装包安装,也可以用 docker 来运行,甚至都支持 Kubernetes。 105 | 106 | 107 | ## 极狐GitLab Runner 的多种安装和运行方式 108 | 109 | 110 | - [用 k3s 来运行极狐GitLab Runner](https://about.gitlab.cn/blog/2022/02/03/k3s-runner/) 111 | - [以 docker 的方式来运行极狐GitLab Runner](https://about.gitlab.cn/blog/2021/12/09/runner-docker/) 112 | - [以 Kubernetes 的方式来运行极狐GitLab Runner](https://about.gitlab.cn/blog/2021/12/09/runner-k8s/) 113 | - [用 Omnibus 来安装和运行极狐GitLab Runner](https://about.gitlab.cn/blog/2021/12/07/runner-ubuntu/) 114 | 115 | 116 | 117 | ## 用 docker 的方式来安装和运行极狐GitLab Runner 118 | 119 | ### 前提条件 120 | 121 | * docker 环境 122 | 123 | docker 的安装可以根据不同 OS 在[docker 官网上](https://docs.docker.com/engine/install/ubuntu/)找到对应的安装方式,本次分享以在 Ubuntu 20.04 上安装 docker 为例来讲述。执行如下命令即可完成安装: 124 | 125 | ``` 126 | $ apt-get update 127 | $ apt-get install \ 128 | ca-certificates \ 129 | curl \ 130 | gnupg \ 131 | lsb-release 132 | $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 133 | $ echo \ 134 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ 135 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 136 | 137 | $ apt-get update && apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin 138 | ``` 139 | 140 | * 极狐GitLab SaaS 账号 141 | 142 | * Runner Token 143 | 144 | 可以通过 Project --> Settings --> CI/CD --> Runners(中文对应项目 --> 设置 --> CI/CD --> Runners)找到: 145 | 146 | ![runner-token](img/runner-token.png) 147 | 148 | 149 | ### Runner 安装 150 | 151 | 执行如下命令启动一个 Runner 容器: 152 | 153 | ``` 154 | $ docker run -d --name jh-gitlab-runner-docker --restart always -v $PWD:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest 155 | ``` 156 | 然后进入启动的容器内并进行 Runner 的注册: 157 | 158 | ``` 159 | $ docker exec -it jh-gitlab-runner-docker sh 160 | gitlab-runner register 161 | Runtime platform arch=amd64 os=linux pid=38 revision=c6bb62f6 version=14.10.0 162 | Running in system-mode. 163 | 164 | Enter the GitLab instance URL (for example, https://gitlab.com/): 165 | https://jihulab.com 166 | Enter the registration token: 167 | GR1348941MUKFzfxSkyXjs7k1_oxr 168 | Enter a description for the runner: 169 | [07530386ce76]: devops community and jh 170 | Enter tags for the runner (comma-separated): 171 | devops,jh,community 172 | Enter optional maintenance note for the runner: 173 | xiaomage 174 | Registering runner... succeeded runner=GR1348941MUKFzfxS 175 | Enter an executor: docker, parallels, docker+machine, docker-ssh+machine, custom, shell, ssh, virtualbox, kubernetes, docker-ssh: 176 | docker 177 | Enter the default Docker image (for example, ruby:2.7): 178 | docker:20.10.7-dind 179 | Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 180 | ``` 181 | 182 | 上面是通过 `register` 这个命令来根据注册一步步完成注册,也可以直接将需要的参数全部传入来完成注册,如下所示: 183 | 184 | ``` 185 | $ gitlab-runner register -n \ 186 | --url https://jihulab.com/ \ 187 | --registration-token GR1348941MUKFzfxSkyXjs7k1_oxr \ 188 | --executor docker \ 189 | --description "devops community and jihu gitlab" \ 190 | --tag-list "devops,jihu,gitlab" \ 191 | --docker-image "docker:20.10.7-dind" \ 192 | --docker-volumes /var/run/docker.sock:/var/run/docker.sock 193 | ``` 194 | 195 | 注册成功,可以在极狐GitLab Runner 界面看到: 196 | 197 | ![runner-succ](img/runner-succ.png) 198 | 199 | 200 | ## Runner 的使用 201 | 202 | 下面讲述使用自建 Runner 来构建 CI/CD 的过程。 203 | 204 | ### 前提条件 205 | 206 | * 一个用于构建 CI/CD 的项目。 207 | 208 | Demo 项目也托管在极狐GitLab SaaS 上,地址为:https://jihulab.com/jh-xiaomage-devops/go-demo。 209 | 210 | 211 | ### 运行 CI/CD 212 | 213 | 在代码根目录中添加一个 `.gitlab-ci.yml` 文件,内容如下: 214 | 215 | 216 | ``` 217 | services: 218 | - docker:20.10.7-dind 219 | 220 | stages: 221 | - build 222 | 223 | build: 224 | tags: 225 | - devops 226 | stage: build 227 | script: 228 | - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY 229 | - docker build -t $CI_REGISTRY_IMAGE:v1.0.0 . 230 | - docker push $CI_REGISTRY_IMAGE:v1.0.0 231 | ``` 232 | 233 | 可以在 CI/CD Pipeline 中看到构建结果: 234 | 235 | ![pipeline-build-result](img/pipeline-build-result.png) 236 | 237 | 以及构建日志,而且在构建日志中可以看到使用的是 self-host 的runner 而非极狐GitLab SaaS 默认的 runner 完成的整个构建: 238 | 239 | ![pipeline-build-log](img/pipeline-build-log.png) 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/img/jihu-gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab02-jihu-gitlab/img/jihu-gitlab.png -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/img/pipeline-build-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab02-jihu-gitlab/img/pipeline-build-log.png -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/img/pipeline-build-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab02-jihu-gitlab/img/pipeline-build-result.png -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/img/runner-succ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab02-jihu-gitlab/img/runner-succ.png -------------------------------------------------------------------------------- /platform/lab02-jihu-gitlab/img/runner-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab02-jihu-gitlab/img/runner-token.png -------------------------------------------------------------------------------- /platform/lab09-atlassian/README.md: -------------------------------------------------------------------------------- 1 | # DevOps 工具鉴宝之 Atlassian Jira 2 | 3 | 从 DevOps 角度,体验 Atlassian 全家桶核心用户场景和产品功能,了解大型企业的需求管理和研发工具链落地实施。 4 | 5 | ## 产品介绍 (5 min) 6 | 7 | Atlassian Corporation Plc 成立于 2002 年,旨在帮助软件团队更好地合作。该公司的产品可帮助团队组织,讨论和完成他们的工作,从而为组织带来卓越的成果。该公司的主要产品包括针对软件团队的 Jira Software 和针对其他业务团队的 Jira Work Management,用于内容创建和共享的 Confluence,用于捕获并快速添加结构的 Trello。团队的形成工作,团队服务和支持应用程序的 Jira Service Management,事件管理的 Opsgenie,企业敏捷计划的 Jira Align,用于代码共享和管理的 Bitbucket,以及用于企业级安全和集中管理的 Atlassian Access。**公司的产品在一起构成了一个用于组织,讨论和完成共享工作的集成系统,从而深深地扎根于人们如何协作以及组织如何运作**。21 年底,Atlassian Marketplace 累计销售额超过 **20 亿 美元**,拥有 **5,300+** 应用程序。 8 | 9 | - user case 10 | 11 | ![](users.png) 12 | 13 | ## 产品选择和配置管理 (10 min - yy) 14 | 15 | ![img](devops-products.png) 16 | 17 | - plan 如何规划 18 | - process 如何实施 19 | - result 效果展示(后续演示) 20 | 21 | ## 全流程交付场景演示 (40 min) 22 | 23 | ### 需求管理 - requirement management (yy) 24 | 25 | ![img](scrum.png) 26 | 27 | - 产品路线规划 product roadmap (set fix version) 28 | - 产品文档管理 PRD in confluence => user story => backlog 29 | - 敏捷研发模式选择 scrum board /kanban 30 | - 站会跟进 active sprint (Kanban - WIP,开发流程后续演示) 31 | - 拉取分支时,自动更新 Jira Story 卡片到 In Progress 状态 32 | - PR Merged 时,自动更新 Jira Story 卡片到 Resolved 状态 33 | - 冲刺结束 close sprint (后续演示) 34 | - 冲刺回顾 retrospective (后续演示) 35 | 36 | ### 研发集成 development integration (toby) 37 | 38 | ![img](dev-process.png) 39 | 40 | - 选择需求 select user story as example 41 | - 创建分支 create feature branch 42 | - 本地开发 IDE integration with Jira and Bitbucket 43 | - VSCode 前端 44 | - Intellij IDEA 后端 45 | - SourceTree 官方客户端 46 | - 提交规范 commit convention 47 | - 代码审核 code push & code review in bitbucket (jira in progress) 48 | - 流水线自动化 Bitbucket pipeline (provided by atlassian) 49 | - build 50 | - test 51 | - scan 52 | - publish 53 | - deploy 54 | - 审核注释和任务跟进 fix review comment and push again, resolve review tasks 55 | - 合并到主分支 merge to main branch (jira revolved) 56 | - 部署到 Staging 环境 CD triggered in main branch, go to staging environment 57 | - 回归测试 start regression testing in staging (mention testing plugin) 58 | - 生产环境变更审核 pause prod deployment & gating 59 | - 变更管理和自动化流程 release management => change ticket 60 | - 生产环境部署 validate ticket and go production, create tag in Bitbucket 61 | - 自动完成需求卡片 close jira 62 | 63 | ![img](js-management.png) 64 | 65 | ### 部署管理 deployment management (toby) 66 | 67 | - 在 CD 中,到 Production 部署时,会自动创建线上变更申请单; 68 | - CD 关联的代码仓库对应的服务变更的 Approvers 收到审批提醒 (邮件,站内信) 69 | - Service Approvers 审批通过 70 | - JSM Change workflow 变更过程自动化 71 | - CD 生产环境部署继续 72 | - CD 生成环境部署完成(成功),Jira Story 卡片自动更新到 Done 状态 73 | 74 | ### 敏捷迭代 sprint closing (toby) 75 | 76 | - 查看需求所关联研发流程数据 Review Jira detail 77 | - 自动状态流转 Status change to done 78 | - 开发分支和提交记录 Development panel 79 | - 自动化历史记录 Automation history 80 | - 发布上线记录 Review deployment status and deployment environment 81 | - 关闭一个冲刺迭代 Closing sprint 82 | - 创建回顾会议记录 Create retrospective 83 | - 查看敏捷迭代报告 Review Jira reports 84 | -------------------------------------------------------------------------------- /platform/lab09-atlassian/dev-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab09-atlassian/dev-process.png -------------------------------------------------------------------------------- /platform/lab09-atlassian/devops-products.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab09-atlassian/devops-products.png -------------------------------------------------------------------------------- /platform/lab09-atlassian/js-management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab09-atlassian/js-management.png -------------------------------------------------------------------------------- /platform/lab09-atlassian/scrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab09-atlassian/scrum.png -------------------------------------------------------------------------------- /platform/lab09-atlassian/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevopsChina/lab/c10de9119a233abe255482e5e021228890919e94/platform/lab09-atlassian/users.png --------------------------------------------------------------------------------