<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>ci | 伪架构师</title>
    <link>/tags/ci/</link>
      <atom:link href="/tags/ci/index.xml" rel="self" type="application/rss+xml" />
    <description>ci</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Fri, 03 Nov 2017 04:49:29 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>ci</title>
      <link>/tags/ci/</link>
    </image>
    
    <item>
      <title>CI/CD 工具链的分分合合</title>
      <link>/post/cicd-toolchain-or-toolset/</link>
      <pubDate>Fri, 03 Nov 2017 04:49:29 +0800</pubDate>
      <guid>/post/cicd-toolchain-or-toolset/</guid>
      <description>

&lt;h2 id=&#34;作案动机&#34;&gt;作案动机&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;一种对 ci/cd 工具的轻量化和解耦的尝试？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jenkins 的传统集群方式，是使用不同环境的服务器构成不同能力的 Jenkins 节点，由主节点根据任务
环节的需要，调度不同能力的子节点来完成构建或部署任务。&lt;/p&gt;

&lt;p&gt;进入容器云时代，情况发生了变化，我们可以使用不同能力的 Jenkins 镜像，使用 Kubernetes 插件来
完成这种任务的拆分和调度，为此，我构建了一个包含所有我们平时用到的工具的 Jenkins 镜像，简化了
节点的扩展和选择过程。&lt;/p&gt;

&lt;p&gt;然而随着学习和应用的深入，我意识到这种做法有几个问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DevOps 中隐含着发挥个人能力的愿景，工具链的所谓大而全，只不过是在画一个比较大的圈，使用这样的一套 Jenkins，还是要被其中所包含的仅有的工具中进行选择，对身陷其中的技术人员绝不能说是友好，也绝不是鼓励各展所长的态度。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;现有的功能测试、接口测试、压力测试等工具，越来越专业化，往往会有各自的工作集群调度甚至是托管方案，例如 selenium grid、JMeter 集群等。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;同样的测试工作，可能有多种工具都可以完成，例如一个 Restful 的接口测试，不管是 JMeter 还是Postman，或者 SoapUI 以及五花八门的自有工具，都可以完成这样的工作。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;各种 DevOps 以及微服务管控和治理平台会有各自的工具链构成以及扩展方案。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;让各种工具自成镜像，无疑对镜像尺寸和更新速度都会有更好的支持。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这样就让我产生一个新的思路：能不能让 Jenkins 回归到一个原始状态，只负责问题的定义、任务的分发和结果的归集呢？于是就有了这样的一点尝试。&lt;/p&gt;

&lt;p&gt;这一尝试的思路是，Jenkins 镜像/容器只使用插件和一些 Shell 脚本，同外部的调度能力（例如
Docker 的容器或者 Kubernetes 的任务等）进行交互，利用网络和共享存储，来实现任务的分发和协调
以及最后的结果汇聚。&lt;/p&gt;

&lt;p&gt;下面以 JMeter 为例，进行一个简单的压力测试，测试环境为了节省起见，使用的是 Docker 加本地目录
共享的方式，这种方式也可以很方便的扩展为 Kubernetes 的 Job + PV/PVC 方式。&lt;/p&gt;

&lt;h2 id=&#34;镜像&#34;&gt;镜像&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Jenkins: dustise/jenkins&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;JMeter: hauptmedia/jmeter&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;注意此处随意的选择了一个 2.x 版本的 JMeter。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;jenkins-插件&#34;&gt;Jenkins 插件&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker_build_step&lt;/code&gt;：用于执行 Docker 指令，类似功能的 Docker Common 插件，其参数不支持环境变量，因此淘汰。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Performance Plugin：一个用于搜集和展示多种测试结果报告的插件。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;存储&#34;&gt;存储&lt;/h2&gt;

&lt;p&gt;这里我们需要为 Jenkins 和各种工具（这里是 JMeter）提供一个可以共享访问的文件交换区域：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;单机 Docker 下，可以是共享的主机目录。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Kubernetes 环境可以使用共享的分布式存储卷。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;例如我们创建名为&lt;code&gt;/var/cicd/exchange/jmeter&lt;/code&gt;的目录，进行文件交换。&lt;/p&gt;

&lt;h2 id=&#34;输入&#34;&gt;输入&lt;/h2&gt;

&lt;p&gt;使用 JMeter GUI 录制 jmx 文件，为任务生成汇总日志（保存到/root/summary.log）。
将 jmx 上传到上面所说的目录之中，这样就保证了 Docker 和 Jenkins 都能通过 &lt;code&gt;-v&lt;/code&gt; 来进行加载。&lt;/p&gt;

&lt;p&gt;这里我们启动 Jenkins 的时候，使用 &lt;code&gt;-v /var/cicd/exchange/jmeter:/exchange/jmeter&lt;/code&gt;
参数让 Jenkins 加载这一目录。&lt;/p&gt;

&lt;h2 id=&#34;jenkins-任务&#34;&gt;Jenkins 任务&lt;/h2&gt;

&lt;h3 id=&#34;准备工作&#34;&gt;准备工作&lt;/h3&gt;

&lt;p&gt;首先的环节是，为后面要开工的 JMeter 准备工作环境。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# 创建当前 Build 目录
mkdir -p /exchange/jmeter/$BUID_TAG
# 复制 jmx 到当前 Build 目录
cp /exchange/jmeter/jd.jmx /exchange/jmeter/$BUID_TAG
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;设置容器&#34;&gt;设置容器&lt;/h3&gt;

&lt;p&gt;准备好文件之后，我们需要添加下一个环节就是设置一个容器：&lt;/p&gt;

&lt;p&gt;这里添加的环节是&lt;code&gt;docker_build_step&lt;/code&gt;的&lt;code&gt;Execute Docker Command&lt;/code&gt;环节。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Image name: hauptmedia/jmeter&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Command：bin/jmeter -n -t /root/jd2.jmx -l /root/result.log -j /root/process.log&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Bind Mounts：&lt;code&gt;/var/cicd/kube/$BUILD_TAG /root&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;注意绑定卷这里就使用了 $BUILD_TAG 变量&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这样就完成了 Jenkins 和 JMeter 的文件共享：&lt;/p&gt;

&lt;p&gt;Jenkins 的 &lt;code&gt;/exchange/jmeter/$BUILD_TAG&lt;/code&gt;，对应的是新创建容器的&lt;code&gt;/root&lt;/code&gt;目录。&lt;/p&gt;

&lt;h3 id=&#34;启动容器&#34;&gt;启动容器&lt;/h3&gt;

&lt;p&gt;使用&lt;code&gt;Execute Docker Command&lt;/code&gt;的&lt;code&gt;start container(s)&lt;/code&gt;环节，&lt;code&gt;Container ID(s)&lt;/code&gt;填写变量
&lt;code&gt;$DOCKER_CONTAINER_IDS&lt;/code&gt;，代表启动我们刚才创建的容器。&lt;/p&gt;

&lt;h3 id=&#34;等待任务&#34;&gt;等待任务&lt;/h3&gt;

&lt;p&gt;如果我们需要使用 Jenkins 进行结果的汇聚，那么这里就需要进行阻塞——等到 JMeter 执行完毕后，
才能进行下面的搜集结果、清理现场等操作。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;RESULT=&amp;quot;/exchange/jmeter/$BUILD_TAG/summary.log&amp;quot;
while [ ! -f $RESULT ]
do
  sleep 30
done


while [[ `lsof | grep summary.log` ]]
do
    sleep 10
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面的脚本利用 lsof 来检测 summary.log 的占用情况，一旦该文件关闭，说明压力测试已经结束。&lt;/p&gt;

&lt;h3 id=&#34;清理容器&#34;&gt;清理容器&lt;/h3&gt;

&lt;p&gt;使用 &lt;code&gt;Execute Docker Command&lt;/code&gt; 的 &lt;code&gt;remove container(s)&lt;/code&gt; 环节，&lt;code&gt;Container ID(s)&lt;/code&gt; 填写变量
&lt;code&gt;$DOCKER_CONTAINER_IDS&lt;/code&gt;，代表清理我们刚才创建的容器。&lt;/p&gt;

&lt;h3 id=&#34;结果展示&#34;&gt;结果展示&lt;/h3&gt;

&lt;p&gt;Performance Plugin 能够识别 JMeter、SoapUI 以及 Parrot 生成的报告文件，这里我们只设置一
个选项，就是 &lt;code&gt;Source data files (autodetects format):&lt;/code&gt;，这里填写
&lt;code&gt;/exchange/jmeter/$BUILD_TAG/summary.log&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;插件会把这一文件拷贝到 Workspace，进行解析和显示。&lt;/p&gt;

&lt;h2 id=&#34;kubernetes-job&#34;&gt;Kubernetes Job&lt;/h2&gt;

&lt;p&gt;上述过程可以很方便的改造成为 Kubernetes Job：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker 相关内容，改为使用 kubectl 进行的 job 文件的生成和操作。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;阻塞过程可以查询任务节点的状态&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;文件存储可以使用 PVC 来共享&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Jenkins：乘着 Kubernetes 的翅膀</title>
      <link>/post/jenkins-worknode-by-kubernetes/</link>
      <pubDate>Thu, 09 Mar 2017 07:38:10 +0800</pubDate>
      <guid>/post/jenkins-worknode-by-kubernetes/</guid>
      <description>

&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;

&lt;p&gt;Kubernetes + Docker 是一对有意思的组合，为微服务架构的落地，扫清了最后一公里的障碍，在符合企业 IT 治理需求的前提之下，为传统企业应用的平滑过渡提供了有效条件和方法。&lt;/p&gt;

&lt;p&gt;作为软件生产环节中重要组成部分的持续构建和发布过程，自然也要随势而动，这方面的老将 Jenkins 不但提供了用于构建、推送 Docker 镜像的插件，更提供了利用 Kubernetes 运行构建集群的能力。本文将利用一个简单的 Hello world 项目，来展示 Jenkins 的这一特性。&lt;/p&gt;

&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;

&lt;h3 id=&#34;kubernetes-集群安装&#34;&gt;Kubernetes 集群安装&lt;/h3&gt;

&lt;p&gt;要在 Kubernetes 集群完成下面所有的工作，因此首先进行集群的安装和配置，这方面可以参考 &lt;a href=&#34;http://kubernetes.io/docs/hellonode/&#34; target=&#34;_blank&#34;&gt;官方入门文档&lt;/a&gt;，如果英语不灵但动手排错能力强的话，也可以参考&lt;a href=&#34;http://blog.fleeto.us/content/centos-7-kubernetes-11x-docker-19x-zhuang-zhi-nan&#34; target=&#34;_blank&#34;&gt;已经过时的拙作&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;集群安装后，应该具有以下能力：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;集群具有受 DNS 支持的服务寻址能力；&lt;/li&gt;
&lt;li&gt;能够利用 Kubectl 或者其他方法发布容器应用的能力；&lt;/li&gt;
&lt;li&gt;私有镜像库，或者到 Docker Hub 的网络连接；&lt;/li&gt;
&lt;li&gt;Pause 镜像也自然是 Kubernetes 中运行应用的必须条件。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;jenkins-镜像&#34;&gt;Jenkins 镜像&lt;/h3&gt;

&lt;p&gt;本文例子采用自制的一个集成镜像为基础工具，该镜像集成了众多常用的 CI/CD 工具，另外同时还包含了 Jenkins 的 Master 和 Slave 两种模式，镜像托管在 &lt;a href=&#34;https://hub.docker.com/r/dustise/jenkins/&#34; target=&#34;_blank&#34;&gt;Docker Hub&lt;/a&gt;，源码可在 &lt;a href=&#34;https://github.com/fleeto/docker-jenkins&#34; target=&#34;_blank&#34;&gt;Github&lt;/a&gt; 浏览和下载。&lt;/p&gt;

&lt;p&gt;如果具有直接连接 Docker Hub 的网络连接，则可无需理会；否则就需要下载镜像，并上传至私库。为行文方便，这里假设采用私库方式，镜像地址为 &lt;code&gt;10.211.55.5:5000/jenkins:2.7.4.5&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&#34;共享存储&#34;&gt;共享存储&lt;/h3&gt;

&lt;p&gt;因容器在集群中的运行状况未知，一般来说是需要为容器化应用提供共享存储服务的，本文中采用最简单的 NFS 方式，当然也可以使用官方支持的其他方式，例如 GlusterFS、Flocker 以及 Ceph 等等，具体支持能力可以参看&lt;a href=&#34;http://kubernetes.io/docs/user-guide/volumes/&#34; target=&#34;_blank&#34;&gt;官方说明&lt;/a&gt;，当然，如果只是学习测试，使用缺省的 Empty 格式亦可，或者偷懒使用 HostPath 方式结合限定 Node 运行的方式也能够完成任务。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;本文中的卷挂载方式很是粗糙，目前推荐的共享存储方式是&lt;a href=&#34;http://kubernetes.io/docs/user-guide/persistent-volumes/&#34; target=&#34;_blank&#34;&gt;持久卷（PV &amp;amp; PVC）&lt;/a&gt;方式，以提供更好的管控能力，中文可参考：&lt;a href=&#34;http://blog.fleeto.us/translation/persistent-volumes&#34; target=&#34;_blank&#34;&gt;Kubernetes 中的 Persistent Volumes&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;jenkins-master&#34;&gt;Jenkins Master&lt;/h2&gt;

&lt;h3 id=&#34;yaml&#34;&gt;YAML&lt;/h3&gt;

&lt;p&gt;首先要为我们的 Jenkins 镜像编写一个 Yaml ，用于提交到集群中运行：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: ReplicationController
apiVersion: v1
metadata:
  name: jenkins
  labels:
    name: jenkins
spec:
  replicas: 1
  selector:
    name: jenkins
  template:
    metadata:
      labels:
        name: jenkins
    spec:
      containers:
      - name: jenkins
        image: 10.211.55.5:5000/jenkins:2.7.4.5
        ports:
        - containerPort: 8080
          protocol: TCP
        - containerPort: 8081
          protocol: TCP
        volumeMounts:
          - name: jenkins
            mountPath: /data/jenkins
      volumes:
      - name: jenkins
        nfs:
          server: 10.211.55.5
          path: /var/data/nfs/ci/jenkins
          readOnly: false
---
kind: Service
apiVersion: v1
metadata:
  name: jenkins
  labels:
    name: jenkins
spec:
  type: NodePort
  ports:
  - protocol: TCP
    nodePort: 32502
    targetPort: 8080
    port: 8080
    name: web
  - protocol: TCP
    targetPort: 8081
    port: 8081
    name: service
  selector:
    name: jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面的 YML 文件有几点需要注意&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;挂载：Jenkins 的所有插件、配置和工作文件都处于环境变量 &lt;code&gt;JENKINS_HOME&lt;/code&gt; 所示的路径中，因此我们这里利用 NFS 把位于 &lt;code&gt;10.211.55.5&lt;/code&gt; 上的  &lt;code&gt;/var/data/nfs/ci/jenkins&lt;/code&gt; 目录映射到容器的  &lt;code&gt;/data/jenkins&lt;/code&gt; 之中，让 Jenkins 获得持久化存储。&lt;/li&gt;
&lt;li&gt;端口：Jenkins 缺省运行需要 8080 端口对外提供 Web 界面。这里我们另外声明了一个 8081 端口，为集群内新建的 Slave 提供通信能力，可以看到，下面的 Service 定义中，仅仅使用 NodePort 方式暴露了一个 Web 端口&lt;/li&gt;
&lt;li&gt;时区：镜像提供了 TIMEZONE 环境变量，这一变量将会在运行时影响容器操作系统以及 Jenkins 的 JVM 的时区设置。这一变量缺省使用 &lt;code&gt;Asia/Shanghai&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JENKINS_MODE&lt;/code&gt;：用于指示 Jenkins 的运行模式，可选值为 &lt;code&gt;MASTER&lt;/code&gt; 或者 &lt;code&gt;SLAVE&lt;/code&gt;，缺省运行在 &lt;code&gt;MASTER&lt;/code&gt; 模式下。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;运行和配置&#34;&gt;运行和配置&lt;/h2&gt;

&lt;p&gt;接下来就可以使用 &lt;code&gt;kubectl create -f jenkins.yaml&lt;/code&gt; 来运行镜像了。执行之后可以刷 &lt;code&gt;kubectl get pods&lt;/code&gt; 来查看启动情况。&lt;/p&gt;

&lt;p&gt;这一 RC 的启动需要加载 NFS 卷，因此如果启动时间过长，可以使用 &lt;code&gt;kubectl get events&lt;/code&gt; 命令查看是否加载出了问题。&lt;/p&gt;

&lt;p&gt;Pod 变为 Running 状态之后，就可以尝试采用上面 YAML 中定义的 Node Port 来访问 Jenkins 界面了，经过一段时间的 &lt;code&gt;&amp;quot;Jenkins正在启动，请稍后.....&amp;quot;&lt;/code&gt;，系统要求输入 &lt;code&gt;/data/jenkins/secrets/initialAdminPassword&lt;/code&gt; 文件中保存的初始密码，假设前面 get pods 命令得到的 Pod 名称是 &lt;code&gt;jenkins-7nmka&lt;/code&gt;，这里就可以使用 &lt;code&gt;kubectl exec jenkins-7nmka cat /data/jenkins/secrets/initialAdminPassword&lt;/code&gt; 来查看，获取结果后即可复制黏贴到录入框中继续安装。&lt;/p&gt;

&lt;p&gt;接下来的插件安装，为节省时间直接 Select None。接下来设置管理员用户名密码，保存后，安装结束。&lt;/p&gt;

&lt;h2 id=&#34;安装和配置-kubernetes-插件&#34;&gt;安装和配置 Kubernetes 插件&lt;/h2&gt;

&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;

&lt;p&gt;接下来就是进入 &lt;code&gt;pluginManager/available&lt;/code&gt; 页面安装插件。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;如果网络需要代理，可以进入网址 &lt;code&gt;/pluginManager/advanced&lt;/code&gt;，设置代理服务器。
如果可选插件页面为空，可以在 &lt;code&gt;/pluginManager/advanced&lt;/code&gt; 页面里面点击 &amp;ldquo;立即获取&amp;rdquo; 按钮进行刷新。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;过滤框中输入 &amp;ldquo;kubernetes&amp;rdquo; 会看到列表中的 &amp;ldquo;Kubernetes plugin&amp;rdquo;，选择安装。&lt;/p&gt;

&lt;h3 id=&#34;配置-jenkins&#34;&gt;配置 Jenkins&lt;/h3&gt;

&lt;p&gt;安装成功后，进入配置页面（&lt;code&gt;/configure&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;首先为了测试方便，我们把 “执行者数量” 设置为 0，也就是说只使用 Jenkins Slave 进行构建。&lt;/p&gt;

&lt;h3 id=&#34;配置-kubernetes&#34;&gt;配置 Kubernetes&lt;/h3&gt;

&lt;p&gt;可以看到，页面下方有一个按钮 “新增一个云”，点击后出现 Kubernetes 配置项目。&lt;/p&gt;

&lt;p&gt;这里我们用无认证的 http 方式进行连接，Kubernetes URL 中填入 API Server 的 http 地址，例如：&lt;code&gt;http://10.211.55.5:8080&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;Jenkins URL 中，这里要注意我们不应该使用浏览器地址栏中的 Node 地址，而是应该使用集群内部的服务地址，根据上文中 Service 的定义，这里使用 &lt;code&gt;http://jenkins:8080&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;最后是 ”Add Pod Template“ 按钮，来定义 Slave 的 Pod 模板：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;这里的 &amp;ldquo;Docker Image&amp;rdquo; 项目跟上面使用的是同一个地址，也就是 &lt;code&gt;10.211.55.5:5000/jenkins:2.7.4.5&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;根据前文说道的环境变量，这里我们新增一个环境变量：JENKINS_MODE=SLAVE，&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Jenkins slave root directory&amp;rdquo; 这一项填写 &lt;code&gt;/data/jenkins&lt;/code&gt;，&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Command to run slave agent   &amp;rdquo; 这里填写镜像中的启动命令 &lt;code&gt;/usr/local/bin/run.sh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;填写完成后，保存。&lt;/p&gt;

&lt;h3 id=&#34;配置端口&#34;&gt;配置端口&lt;/h3&gt;

&lt;p&gt;最后需要到安全配置页面（&lt;code&gt;/configureSecurity&lt;/code&gt;），&lt;code&gt;TCP port for JNLP agents&lt;/code&gt; 一项填写固定端口 8081。&lt;/p&gt;

&lt;h2 id=&#34;hello-world&#34;&gt;Hello world&lt;/h2&gt;

&lt;h3 id=&#34;创建&#34;&gt;创建&lt;/h3&gt;

&lt;p&gt;新建一个 Free Style 项目。构建步骤中新增 &amp;ldquo;Execute shell script&amp;rdquo; 环节，并保存，内容为&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;echo &amp;quot;Hello World&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;构建&#34;&gt;构建&lt;/h3&gt;

&lt;p&gt;点击该任务的 “立即构建” 按钮，即可触发构建动作。&lt;/p&gt;

&lt;p&gt;因为前面我们设置 Master 不执行构建工作，所以在构建启动之后，会在构建执行状态中看到有节点被动态新建，来执行我们的构建过程。这一过程中如果使用 &lt;code&gt;kubectl get events&lt;/code&gt; 或者 &lt;code&gt;kubectl get pods&lt;/code&gt; 命令，能看到 Jenkins Slave Pod 的创建、执行和销毁过程。&lt;/p&gt;

&lt;h2 id=&#34;尾声&#34;&gt;尾声&lt;/h2&gt;

&lt;p&gt;上面只是一个最为基础的构建过程，甚至都无法称为是一个完整过程，Kubernetes 插件还提供了很多其他选项，结合自定义的 Slave 镜像，能够完成更多更复杂的任务，用于配合实际的生产过程。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
