若您期望实现从源代码自动构建、推送镜像到最终部署应用的一体化自动化操作。您可以使用Jenkins实现镜像的CI/CD,只要您在GitLab中提交源代码,容器镜像会自动使用源代码构建镜像,容器服务会自动拉取镜像部署应用,并自动发送事件通知到钉钉群。
前提条件
已安装Git、GitLab和Jenkins。
说明GitLab需要安装JDK8,部分插件不能在JDK11上运行。
已创建ACK集群,且与ACR实例位于同一地域。具体操作,请参见创建Kubernetes托管版集群。
已创建钉钉机器人,并记录下Webhook地址和加签密钥。具体操作,请参见步骤一:创建钉钉机器人。
使用ACR的交付链功能,需要您升级至ACR高级版实例。具体操作,请参见计费说明。
使用Jenkins实现镜像的CI
只要您在GitLab中提交源代码,容器镜像会自动使用源代码构建镜像,然后您可以对镜像进行漏洞扫描,镜像扫描完成后,将自动发送事件通知到钉钉群。
在GitLab中创建项目。
登录GitLab。
在顶部导航栏中,选择 。
在Projects页面单击右上角的New Project,单击Create blank project。
在Create blank project页面设置Project name、Project URL和Project slug,设置Visibility Level为Private,然后单击Create project。
使用以下内容,在本地创建Dockerfile、pom.xml、DemoApplication.java和HelloController.java文件。
Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/public-toolbox/maven:3.8.3-openjdk-8-aliyun AS build COPY src /home/app/src COPY pom.xml /home/app RUN ["/usr/local/bin/mvn-entrypoint.sh","mvn","-f","/home/app/pom.xml","clean","package","-Dmaven.test.skip=true"] FROM registry.cn-hangzhou.aliyuncs.com/public-toolbox/openjdk:8-jdk-alpine COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo-0.0.1-SNAPSHOT.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/usr/local/lib/demo-0.0.1-SNAPSHOT.jar"]
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
DemoApplication.java
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.TimeZone; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); SpringApplication.run(DemoApplication.class, args); } }
HelloController.java
package com.example.demo; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; @RestController @Slf4j public class HelloController { @RequestMapping({"/hello", "/"}) public String hello(HttpServletRequest request) { return "Hello World at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); } }
执行以下命令,上传构建文件至GitLab。
cd java-web #进入构建文件所在目录。 git remote set-url origin http://8.218.20*.***/shoppingmall/java-web.git git push origin master * [new branch] master -> master
在Jenkins配置构建镜像的流水线。
在Jenkins配置GitLab的SSH Key。
登录Jenkins。
在Jenkins左侧导航栏单击系统管理。
在安全区域单击Manage Credentials。
在Stores scoped to Jenkins区域单击存储列下的Jenkins,然后单击全局凭据。
在左侧导航栏单击添加凭据。
设置类型为SSH Username with private key,然后输入描述、Username,选中Enter directly,然后单击确定。
在全局凭据页面会自动生成凭据ID,保存该凭据ID。
创建流水线。
在Jenkins左侧导航栏单击新建任务。
输入任务名称,选中流水线,单击确定。
单击构建触发器页签,选中Build when a change is pushed to GitLab,然后选中Push Events。
在Build when a change is pushed to GitLab右侧记录Webhook的地址。
单击高级,单击Secret token右下角的Generate。
Jenkins会生成一个Secret token,保存该Secret token。
单击流水线页签,根据实际情况修改以下模板,然后将内容复制到文本框中,然后单击保存。
def git_auth_id = "6d5a2c06-f0a7-43c8-9b79-37b8c266****" #凭据ID def git_branch_name = "master" #分支名 def git_url = "git@172.16.1*.***:shoppingmall/java-web.git" #GitLab仓库地址 def acr_url = "s*****-devsecops-registry.cn-hongkong.cr.aliyuncs.com" #镜像仓库地址 def acr_username = "acr_test_*****@test.aliyunid.com" #容器镜像用户名 def acr_password = "HelloWorld2021" #容器镜像密码 def acr_namespace = "ns" #命名空间 def acr_repo_name = "test" #镜像仓库名称 def tag_version = "0.0.1" #镜像版本 node { stage('checkout git repo') { checkout([$class: 'GitSCM', branches: [[name: "*/${git_branch_name}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth_id}", url: "${git_url}"]]]) } stage('build image') { sh "sudo docker build -t java-web:${tag_version} ." sh "sudo docker tag java-web:${tag_version} ${acr_url}/${acr_namespace}/${acr_repo_name}:${tag_version}" } stage('push image') { sh "sudo docker login --username=${acr_username} --password=${acr_password} ${acr_url}" sh "sudo docker push ${acr_url}/${acr_namespace}/${acr_repo_name}:${tag_version}" } }
添加Webhook地址到GitLab中。
登录GitLab。
在Projects页面单击上文创建的Project名称。
在左侧导航栏选择
,输入Webhook地址和Secret token,取消选中Enable SSL verification,然后单击Add webhooks。
创建事件通知。
登录容器镜像服务控制台。
在顶部菜单栏,选择所需地域。
在左侧导航栏,选择实例列表。
在实例列表页面单击目标企业版实例。
在实例详情页面左侧导航栏选择 。
在事件规则页签下单击创建规则。
在事件范围配置向导中设置规则名称,事件类型为镜像扫描完成,选中扫描完成,设置生效范围为命名空间,选择命名空间为ns,然后单击下一步。
在事件通知配置向导中设置通知方式为钉钉,输入钉钉的Webhook地址和加签密钥,然后单击保存。
触发镜像构建。
执行以下命令,修改HelloController.java文件,提交文件到GitLab中,从而触发镜像构建。
vim java/com/example/demo/HelloController.java #根据实际情况在本地修改HelloController.java文件 git add . && git commit -m 'commit' && git push origin #提交文件到GitLab
稍等一段时间,在企业版实例详情页面左侧导航栏选择
,在右侧页面单击目标仓库test,在仓库管理页面左侧导航栏单击镜像版本,可以看到镜像版本页面生成一个镜像。设置漏洞扫描。
在镜像版本页面单击目标镜像右侧操作下的安全扫描。
在安全扫描页面,单击立即扫描。
待镜像扫描完成后,您可以在钉钉群里收到通知。
使用Jenkins实现镜像的CD
只要您在GitLab中提交源代码,容器镜像会自动用源代码构建镜像,镜像构建成功后,自动触发执行交付链,待交付链运行完成后,将自动发送HTTP请求至Jenkins,然后触发ACK的Deployment重新拉取镜像部署应用。
创建应用。
登录容器服务管理控制台。
在控制台左侧导航栏,单击集群。
在集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情。
在集群管理页左侧导航栏,选择 。
在无状态页面单击右上角的使用YAML创建资源。
在创建页面顶部选择命名空间,设置示例模板为自定义,然后复制以下内容到模板中,然后单击创建。
说明没有设置免密拉取镜像,您需要在容器镜像服务中开启公网访问,并设置镜像为公开镜像。具体操作,请参见配置公网的访问控制。
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: demo name: demo spec: replicas: 3 minReadySeconds: 5 progressDeadlineSeconds: 60 revisionHistoryLimit: 5 selector: matchLabels: app: demo strategy: rollingUpdate: maxUnavailable: 1 type: RollingUpdate template: metadata: annotations: prometheus.io/port: "9797" prometheus.io/scrape: "true" creationTimestamp: null labels: app: demo spec: containers: - image: s*****-devsecops-registry.cn-hongkong.cr.aliyuncs.com/ns/test:0.0.1 imagePullPolicy: Always name: demo ports: - containerPort: 8080 name: http protocol: TCP readinessProbe: initialDelaySeconds: 5 tcpSocket: port: 8080 timeoutSeconds: 5 resources: limits: cpu: "2" memory: 512Mi requests: cpu: 100m memory: 64Mi status: {} --- apiVersion: v1 kind: Service metadata: name: demo-svc spec: selector: app: demo ports: - protocol: TCP port: 80 targetPort: 8080 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: demo labels: app: demo spec: rules: - host: app.demo.example.com http: paths: - backend: serviceName: demo-svc servicePort: 80 ---
在无状态页面单击目标应用demo,单击访问方式页签。
在访问方式页签下获取外部端点地址。
在本地hosts绑定以下内容。
<外部端点地址> app.demo.example.com
在浏览器中输入app.demo.example.com。
看到以上页面,说明应用部署成功。
创建触发器。
在无状态页面单击目标应用demo的名称。
在应用详情页面单击触发器页签,单击创建触发器。
在创建触发器对话框设置触发器行为为重新部署,然后单击确定。
在触发器页签下获取触发器链接。
创建流水线。
登录Jenkins。
在左侧导航栏单击新建任务。
输入任务名称,单击流水线。
单击构建触发器页签,选中Generic Webhook Trigger。
在Generic Webhook Trigger下可以获取HTTP请求触发地址为
JENKINS_URL/generic-webhook-trigger/invoke
,本文设置Generic Webhook token
为helloworld2021
,因此Webhook地址为JENKINS_URL/generic-webhook-trigger/invoke?token=helloworld2021
。单击流水线页签,根据实际情况修改以下模板中的Generic Webhook token和触发器链接,然后将内容复制到文本框中,然后单击保存。
pipeline { agent any triggers { GenericTrigger( genericVariables: [ [key: 'InstanceId', value: '$.data.InstanceId'], [key: 'RepoNamespaceName', value: '$.data.RepoNamespaceName'], [key: 'RepoName', value: '$.data.RepoName'], [key: 'Tag', value: '$.data.Tag'] ], causeString: 'Triggered on $ref', token: 'helloworld2021', #Generic Webhook token,请根据实际情况替换。 tokenCredentialId: '', printContributedVariables: true, printPostContent: true, silentResponse: false, regexpFilterText: '$ref' ) } stages { stage('Some step') { steps { sh "echo 'will print post content'" sh "echo $InstanceId" sh "echo $RepoNamespaceName" sh "echo $RepoName" sh "echo $Tag" sh "echo 'redeploy to ACK or you can deoloy to other platforms use before message'" sh "curl 'https://cs.console.aliyun.com/hook/trigger?token=g****' #触发器链接,请根据实际情况替换。 sh "echo 'done'" } } } }
创建交付链。具体操作。请参见创建交付链。
创建事件规则。
在企业版实例管理页面左侧导航栏中选择 。
在事件规则页签下单击创建规则。
在事件范围配置向导中设置规则名称,事件类型为交付链处理完成,选中成功,设置生效范围为命名空间,选择命名空间为ns,然后单击下一步。
在事件通知配置向导中设置通知方式为HTTP,输入步骤3获取的Webhook地址,然后单击保存。
执行以下命令,修改HelloController.java文件,提交代码到GitLab中,从而触发镜像构建。
vim java/com/example/demo/HelloController.java #根据实际情况在本地修改HelloController.java文件。 git add . && git commit -m 'add update' && git push origin #提交文件到GitLab中。
镜像构建成功后,触发执行交付链,交付链执行完毕后,触发ACK的Deployment重新拉取镜像部署应用。
执行以下命令,验证应用是否重新部署。
curl app.demo.example.com
可以看到输出内容发生了变化, 说明应用重新部署成功。