Automating the Workflow: My Journey from Jenkins Freestyle Jobs to Declarative Pipelines
Automating the Workflow: My Journey from Jenkins Freestyle Jobs to Declarative Pipelines
自动化工作流:我从 Jenkins 自由风格任务到声明式流水线的旅程
The Infrastructure: Setting Up Jenkins on AWS
基础设施:在 AWS 上搭建 Jenkins
The foundation of this project began by provisioning an Ubuntu EC2 instance on AWS. Setting up the environment meant defining strict networking rules (opening Port 22 for SSH and Port 8080 for the Jenkins UI) and structuring the Jenkins environment with clear access controls. In Jenkins, maintaining a secure and organized environment generally falls into two roles: 该项目的基础始于在 AWS 上配置一台 Ubuntu EC2 实例。搭建环境意味着需要定义严格的网络规则(开放用于 SSH 的 22 端口和用于 Jenkins UI 的 8080 端口),并构建具有清晰访问控制的 Jenkins 环境。在 Jenkins 中,维护一个安全且有序的环境通常分为两个角色:
- Administrators: Responsible for managing the Jenkins cluster, installing necessary plugins, and handling data backups.
- 管理员: 负责管理 Jenkins 集群、安装必要的插件以及处理数据备份。
- Users: Focused purely on creating jobs to run their respective workflows.
- 用户: 专注于创建任务以运行各自的工作流。
The Magic of Docker-out-of-Docker (DooD)
Docker-out-of-Docker (DooD) 的魔力
One of the most critical architectural choices was deciding how to let Jenkins build Docker images without installing a heavy, nested Docker engine inside the Jenkins container itself. The solution was a Docker-out-of-Docker configuration. By running the following command, I spun up the Jenkins container while binding it directly to the host machine’s Docker socket: 最关键的架构选择之一是决定如何让 Jenkins 构建 Docker 镜像,而无需在 Jenkins 容器内部安装沉重的嵌套 Docker 引擎。解决方案是采用 Docker-out-of-Docker 配置。通过运行以下命令,我在启动 Jenkins 容器的同时,将其直接绑定到宿主机的 Docker 套接字:
docker run -p 8080:8080 -p 50000:50000 -d \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/usr/bin/docker jenkins/jenkins:lts
This single command did a lot of heavy lifting. It mapped port 8080 for the UI and 50000 for Jenkins agent communication. More importantly, mapping /var/run/docker.sock gave the Jenkins container the ability to pass docker build and docker push commands directly to the EC2 host’s Docker engine. (Just remember to ensure your jenkins user has the right permissions to access that socket!).
这一条命令完成了大量繁重的工作。它映射了用于 UI 的 8080 端口和用于 Jenkins 代理通信的 50000 端口。更重要的是,映射 /var/run/docker.sock 使 Jenkins 容器能够直接将 docker build 和 docker push 命令传递给 EC2 宿主机的 Docker 引擎。(只需记住确保你的 jenkins 用户拥有访问该套接字的正确权限!)。
Hitting the Wall: The Limitations of Freestyle Jobs
碰壁:自由风格任务的局限性
Initially, I set up the application lifecycle running npm install, npm test, and npm pack using a standard Jenkins Freestyle job. Freestyle jobs are great for quick, isolated tasks. However, their limitations become glaringly obvious when you try to build a project with multiple automation steps. Orchestrating a complex workflow by chaining multiple Freestyle jobs together is inefficient and quickly leads to overloaded jobs that are impossible to maintain or debug.
最初,我使用标准的 Jenkins 自由风格任务(Freestyle job)来设置应用程序生命周期,运行 npm install、npm test 和 npm pack。自由风格任务非常适合快速、孤立的任务。然而,当你尝试构建一个包含多个自动化步骤的项目时,它们的局限性就变得非常明显。通过串联多个自由风格任务来编排复杂的工作流效率低下,并且很快会导致任务负载过重,变得难以维护或调试。
The Solution: Embracing Declarative Pipelines
解决方案:拥抱声明式流水线
To solve this, I migrated the entire workflow to a Pipeline job. Pipelines are written in Groovy (a language similar to Java) and are designed specifically to orchestrate long-running, complex activities that can span multiple build agents. The repository for this project contains all the necessary configuration files, including the Dockerfile, Jenkinsfile, package.json, and the main application code in server.js. By placing a Jenkinsfile directly in the repository, the pipeline became part of the application’s source code (Infrastructure as Code). 为了解决这个问题,我将整个工作流迁移到了流水线(Pipeline)任务中。流水线使用 Groovy(一种类似于 Java 的语言)编写,专门用于编排可以跨越多个构建代理的长期、复杂的活动。该项目的仓库包含了所有必要的配置文件,包括 Dockerfile、Jenkinsfile、package.json 以及 server.js 中的主要应用程序代码。通过将 Jenkinsfile 直接放入仓库,流水线成为了应用程序源代码的一部分(即“基础设施即代码”)。
The declarative syntax allowed me to clearly define distinct stages: 声明式语法使我能够清晰地定义不同的阶段:
- Checkout: Pulling the latest code from GitHub.
- 检出: 从 GitHub 拉取最新代码。
- Install & Test: Running standard Node.js NPM commands.
- 安装与测试: 运行标准的 Node.js NPM 命令。
- Package & Build: Compiling the .tgz artifact and building the Docker image.
- 打包与构建: 编译 .tgz 制品并构建 Docker 镜像。
- Push: Authenticating and pushing the final image to a private DockerHub repository.
- 推送: 验证身份并将最终镜像推送到私有 DockerHub 仓库。
Pro-Tip: When pushing to a private registry in a pipeline, you need to use Jenkins’ “Secret text or file” plugin to securely pass your credentials into the docker login command via a withCredentials block, preventing sensitive data from leaking into your build logs!
专业提示: 在流水线中推送到私有注册表时,你需要使用 Jenkins 的“Secret text or file”插件,通过 withCredentials 代码块将凭据安全地传递给 docker login 命令,从而防止敏感数据泄露到构建日志中!
What’s Next?
未来展望
Moving forward, I plan to implement Multibranch Pipelines, which will allow Jenkins to automatically scan my repository, detect any branch containing a Jenkinsfile, and dynamically build a pipeline for it using when condition statements. I am also exploring Jenkins Parameters to inject external configurations, making it incredibly easy to select specific application versions for deployment dynamically.
展望未来,我计划实现多分支流水线(Multibranch Pipelines),这将允许 Jenkins 自动扫描我的仓库,检测任何包含 Jenkinsfile 的分支,并使用 when 条件语句为该分支动态构建流水线。我还在探索 Jenkins 参数,以便注入外部配置,从而极其轻松地动态选择特定的应用程序版本进行部署。
The transition from clicking through the Jenkins UI to writing robust, version-controlled pipelines has completely transformed how I approach CI/CD. 从通过 Jenkins UI 点击操作转变为编写健壮的、版本控制的流水线,彻底改变了我处理 CI/CD 的方式。