如何在Fargate上使用Kaniko代理的无服务器Jenkins

469 阅读4分钟

Fargate上的无服务器Jenkins与Kaniko Agent

简介

Jenkins是一种流行的CI/CD工具,被开发人员用来构建、部署和测试他们的代码。企业可以在公有云或私有云中管理自己的Jenkins服务器,甚至在自己的物理基础设施上也可以。

最近我的团队一直在AWS中运行一个大型的Jenkins实例和多个代理,有数百条管道。然而,这样做的费用很快导致我们提出了一个问题:在团队放下一天的工具后,我们真的需要所有这些基础设施在夜间运行吗?答案当然是否定的。

在这篇文章中,我将解释我们如何在AWS Fargate上切换到一个按需使用的无服务器Jenkins环境,同时仍然保留使用Kaniko构建Docker镜像的能力。

AWS Fargate上的无服务器Jenkins

我们的出发点是这个帖子。在这篇文章中,Jenkins主任务被构建为Fargate任务,在专用ECS集群中没有实例,并将共享EFS存储附加到这些主任务上。下图取自该帖子,描述了整体解决方案的架构。

AWS Fargate Jenkins的架构图

在我们的案例中,我们决定基础设施资源将由AWS Terraform代码部署。Terraform还使用Yaml文件中的Jenkins配置为代码插件来配置Jenkins插件和Jenkins节点的配置。它也会从该文件中构建初始管道。

Jenkins控制器UI中的预配置作业

一个新的问题

这看起来不错,但现在我们面临一个新问题。我们有一堆作为Docker镜像构建的应用程序。为了构建这些应用程序,我们在托管实例的Jenkins代理容器中运行Docker命令。

然而,ECS实例不允许我们在Fargate容器中使用Docker守护程序。这是有道理的,因为在一个有特权的容器中运行的恶意(或脆弱)活动有可能对共享云主机中的其他容器产生影响。然而,我们需要一个工具来构建Docker镜像,即使我们无法访问Docker命令。

卡尼科

有几个工具可以实现这个目标。我们选择了Kaniko--一个在容器或Kubernetes集群内构建容器镜像的开源工具--因为我们发现它比buildah等工具更容易嵌入到Jenkins代理中。Kaniko是由谷歌开发的,但不是谷歌官方支持的产品。

Kaniko可以从Docker文件中构建一个镜像,并将其推送到注册表。引用文档中的话。

kaniko执行器镜像负责从Docker文件中建立一个镜像,并将其推送到注册表。在执行器镜像中,我们提取基本镜像的文件系统(Dockerfile中的FROM镜像)。然后,我们执行Dockerfile中的命令,在每条命令之后,在用户空间中快照文件系统。在每条命令之后,我们在基础镜像上添加一层变化的文件(如果有的话),并更新镜像元数据。

-来自Kaniko

然后,我们的想法是自己建立一个Jenkins Kaniko镜像,把它作为Jenkins代理放入集群中,让它在代理内运行Kaniko命令,以建立和上传镜像到应用注册表。

将Kaniko嵌入到无服务器的Jenkins中

嵌入Kaniko到无服务器的Jenkins架构中的代码可以在这里找到。Kaniko建议在其官方执行器镜像中运行kaniko命令。在我们的案例中,我们选择管理我们自己的镜像,该镜像是基于官方的。

具体来说,我们把官方镜像中的Kaniko二进制文件和配置文件复制到一个Alpine Jenkins代理镜像中。

FROM gcr.io/kaniko-project/executor:debug AS kaniko
FROM jenkins/inbound-agent:latest-alpine
USER root
RUN apk --update add \
bash \
curl \
git \
jq \
unzip \
npm
#
# Add kaniko to this image by re-using binaries and steps from official image
#
COPY --from=kaniko /kaniko/ /kaniko/
ENV SSL_CERT_DIR /kaniko/ssl/certs
ENV PATH $PATH:/usr/local/bin:/kaniko
ENV DOCKER_CONFIG /kaniko/.docker/
ENV DOCKER_CREDENTIAL_GCR_CONFIG /kaniko/.config/gcloud/docker_credential_gcr_config.json
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && ./aws/install
COPY files/Dockerfile.example /home/Dockerfile
COPY files/scripts/execute.sh /home/execute.sh
COPY files/config.json /root/.docker/config.json
RUN chmod 755 /home/execute.sh /home/Dockerfile /root/.docker/config.json
USER root

这个Docker文件可以在 [modules/jenkins_platform/kaniko/Dockerfile](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/blob/main/modules/jenkins_platform/kaniko/Dockerfile).

然后,我们将该镜像添加到我们的Jenkins代理注册表中,该注册表由我们的Terraform代码定义的ECS任务定义所提及。

它是如何工作的?

部署这个Terraform模块需要Terraform 0.14+、Docker 19+和AWS账户中的VPC子网。要自己使用它,部署步骤如下:

  1. 在AWS中为Terraform状态文件创建引导资源。转到[example/bootstrap](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/tree/main/example/bootstrap),用你喜欢的名字替换my-state-bucketmy-lock-table ,并运行以下命令:
terraform init
terraform apply \
-var="state_bucket_name=my-state-bucket" \
-var="state_lock_table_name=my-lock-table"

2.修改部署脚本中的变量[example/vars.sh.example](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/blob/main/example/vars.sh.example)用上一步创建的值修改部署脚本中的变量。TF_VAR_jenkins_admin_password 是为Jenkinsecsuser 设置的密码。它将被添加到由Jenkins主程序加载的SSM参数中:

#!/usr/bin/env bash 
export TF_STATE_BUCKET="my-state-bucket"
export TF_STATE_OBJECT_KEY="serverless-jenkins.tfstate"
export TF_LOCK_DB="my-lock-table"
export AWS_REGION="" 
PRIVATE_SUBNETS=''
PUBLIC_SUBNETS=''
VPC_ID="" 
export TF_VAR_route53_create_alias="false"
export TF_VAR_route53_zone_id=""
export TF_VAR_route53_domain_name=""
export TF_VAR_jenkins_admin_password=""
export TF_VAR_vpc_id=${VPC_ID}
export TF_VAR_efs_subnet_ids=${PRIVATE_SUBNETS}
export TF_VAR_jenkins_controller_subnet_ids=${PRIVATE_SUBNETS}
export TF_VAR_alb_subnet_ids=${PUBLIC_SUBNETS}

3.运行[example/deploy_example.sh](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/blob/main/example/deploy_example.sh).这将构建这个项目的所有Jenkins资源。在这次构建之后,你可以从Jenkins的负载平衡器中找到路径。你可以用Jenkins的管理密码(存储在SSM参数库中,名称为jenkins-pwd )和用户名ecsuser

4.当你登录到Jenkins时,Kaniko管道将出现在那里。当作业运行时,它将从简单的Alpine镜像中构建一个示例应用程序镜像。Docker文件被定义在[modules/jenkins_platform/kaniko/files/Dockerfile.example](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/blob/main/modules/jenkins_platform/kaniko/files/Dockerfile.example).然后,结果将由Kaniko构建执行命令推送到应用程序仓库(定义在[/modules/jenkins_platform/kaniko/files/scripts/execute.sh.tpl](https://github.com/William-Yi-Weng/serverless-fargate-jenkins-kaniko/blob/main/modules/jenkins_platform/kaniko/files/scripts/execute.sh.tpl)):

#!/bin/sh -e
/kaniko/executor     --dockerfile=/home/Dockerfile \
--verbosity debug \
--insecure \
--skip-tls-verify \
--force \
--destination=${repository_url}/kaniko-artifact:release-x.x.x

局限性

这个解决方案有一些值得注意的限制:

  • Kaniko不支持构建Windows容器
  • 官方不支持在Kaniko官方镜像以外的任何Docker镜像中运行Kaniko
  • 当使用 --snapshotMode=time参数时,Kaniko可能会因为mtime的问题而错过文件快照的变化。
  • 它不能从docker-compose.yaml 文件中读取内容
  • 在创建新的容器时,它不能运行集成测试,因为它被设计用来构建镜像而不运行容器。

结论

在这篇文章中,我描述了如何将AWS ECS Fargate架构的无服务器Jenkins扩展到使用Kaniko来构建Docker镜像*,而无需*Docker守护程序。它并非对所有的用例都是完美的;但为无服务器环境下的Docker容器应用构建提供了一种选择。欲了解更多信息,我鼓励你查看Github repo