{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2019-07-03-scf-devops-jenkins","result":{"data":{"currentBlog":{"id":"e5cbc7f7-4743-507a-be01-abb06982a340","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586871710979-%E5%85%AC%E5%85%B1%E7%94%A8.png","authors":["李帅"],"categories":["guides-and-tutorials"],"date":"2019-07-03T00:00:00.000Z","title":"云函数 SCF 场景下的 DevOps 实现 —— Jenkins 篇","description":"本文意图描述在 SCF 场景下，如何基于 Jenkins 搭建自己的 CI/CD 流程","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["DevOps","Serverless"],"keywords":"Serverless,serverless cloud function,无服务器架构","outdated":null},"wordCount":{"words":107,"sentences":38,"paragraphs":38},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-07-03-scf-devops-jenkins.md","fields":{"slug":"/blog/2019-07-03-scf-devops-jenkins/","keywords":["java","python","serverless","spa","Pipeline","scf","stage","git","echo","Deploy","serverlesscloud"]},"html":"<p>当前Serverless热度越来越高，越来越多的开发者们开始抛弃传统开发模式基于Serverless来搭建自己的产品服务。在享受腾讯云Serverless产品SCF的免运维、低成本优势的同时，也要适应SCF与传统开发模式的差异。其中DevOps便是很重要的一环，因为SCF特有的runtime，使得开发者无法复用现有DevOps平台完成CI/CD流程。</p>\n<p>为此，腾讯云中间件团队基于Jenkins、CODING企业版以及蓝盾三种DevOps方案进行了整合打通，本文意图描述在SCF场景下，如何基于Jenkins搭建自己的CI/CD流程。后续，将会介绍基于其他两种方案的DevOps实现。</p>\n<p><strong>前言</strong></p>\n<p>本文核心目的在于描述SCF与现有DevOps平台结合的实践，以Jenkins为例。因此不会介绍过多背景知识。因此假设你已经具备以下技能。</p>\n<ul>\n<li>Jenkins或类似平台使用背景。</li>\n<li>了解SCF产品</li>\n<li>scf cli，SCF的命令行工具：<a href=\"https://github.com/tencentyun/scfcli\">https://github.com/tencentyun/scfcli</a></li>\n</ul>\n<p><strong>源码介绍</strong></p>\n<p>git地址：</p>\n<p><a href=\"https://github.com/NevenMoore/scf%5C_devops%5C_demo\">https://github.com/NevenMoore/scf\\_devops\\_demo</a></p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lya3gH7dv8mu3LSJoDUiabVuyXsaEL2mYsXAa1A4SUicTpP0ptae21RiaeA.jpg\"></p>\n<ol>\n<li><strong>Jenkinsfile, jenkins的pipeline描述文件。</strong></li>\n</ol>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"12543466475262943000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`pipeline {agent any\n\nstages {stage('Checkout') {steps {echo &quot;Checkout&quot;git 'https://github.com/NevenMoore/scf_devops_demo.git'}}stage('Build') {steps {echo 'Building'sh &quot;pip install -r requirements.txt&quot;}}stage('Test') {steps {echo 'Testing'script {ret = sh(script: &quot;scf native invoke -t ./template.yaml --no-event&quot;, returnStatus: true)if (ret != 0) {echo '[Test] Failed'currentBuild.result = 'FAILURE'return}}\n\n}}stage('Deploy - Staging') {steps {echo 'Deploy - Staging'}}stage('Sanity check') {steps {input &quot;Does the staging environment look ok?&quot;}}stage('Deploy - Production') {steps {echo 'Deploy - Production'script {ret = sh(script: &quot;scf package -t ./template.yaml&quot;, returnStatus: true)if (ret != 0) {echo '[Deploy] Failed'currentBuild.result = 'FAILURE'return}ret = sh(script: &quot;scf deploy -t ./deploy.yaml -f&quot;, returnStatus: true)if (ret != 0) {echo '[Deploy] Failed'currentBuild.result = 'FAILURE'return}}}}}\n\npost {success {echo 'I succeeeded!'}unstable {echo 'I am unstable :/'}failure {echo 'I failed :('}changed {echo 'Things were different before...'}}}`, `12543466475262943000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">pipeline <span class=\"token punctuation\">{</span>agent any\n\nstages <span class=\"token punctuation\">{</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Checkout'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>echo <span class=\"token string\">\"Checkout\"</span>git <span class=\"token string\">'https://github.com/NevenMoore/scf_devops_demo.git'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Build'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'Building'</span>sh <span class=\"token string\">\"pip install -r requirements.txt\"</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Test'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'Testing'</span>script <span class=\"token punctuation\">{</span>ret <span class=\"token operator\">=</span> <span class=\"token function\">sh</span><span class=\"token punctuation\">(</span>script<span class=\"token punctuation\">:</span> <span class=\"token string\">\"scf native invoke -t ./template.yaml --no-event\"</span><span class=\"token punctuation\">,</span> returnStatus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ret <span class=\"token operator\">!=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'[Test] Failed'</span>currentBuild<span class=\"token punctuation\">.</span>result <span class=\"token operator\">=</span> <span class=\"token string\">'FAILURE'</span><span class=\"token keyword\">return</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Deploy - Staging'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'Deploy - Staging'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Sanity check'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>input <span class=\"token string\">\"Does the staging environment look ok?\"</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token function\">stage</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Deploy - Production'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>steps <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'Deploy - Production'</span>script <span class=\"token punctuation\">{</span>ret <span class=\"token operator\">=</span> <span class=\"token function\">sh</span><span class=\"token punctuation\">(</span>script<span class=\"token punctuation\">:</span> <span class=\"token string\">\"scf package -t ./template.yaml\"</span><span class=\"token punctuation\">,</span> returnStatus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ret <span class=\"token operator\">!=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'[Deploy] Failed'</span>currentBuild<span class=\"token punctuation\">.</span>result <span class=\"token operator\">=</span> <span class=\"token string\">'FAILURE'</span><span class=\"token keyword\">return</span><span class=\"token punctuation\">}</span>ret <span class=\"token operator\">=</span> <span class=\"token function\">sh</span><span class=\"token punctuation\">(</span>script<span class=\"token punctuation\">:</span> <span class=\"token string\">\"scf deploy -t ./deploy.yaml -f\"</span><span class=\"token punctuation\">,</span> returnStatus<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ret <span class=\"token operator\">!=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'[Deploy] Failed'</span>currentBuild<span class=\"token punctuation\">.</span>result <span class=\"token operator\">=</span> <span class=\"token string\">'FAILURE'</span><span class=\"token keyword\">return</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n\npost <span class=\"token punctuation\">{</span>success <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'I succeeeded!'</span><span class=\"token punctuation\">}</span>unstable <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'I am unstable :/'</span><span class=\"token punctuation\">}</span>failure <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'I failed :('</span><span class=\"token punctuation\">}</span>changed <span class=\"token punctuation\">{</span>echo <span class=\"token string\">'Things were different before...'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">1. Checkout:检出，从github拉取代码。\n2. Build: 用pip安装项目依赖。\n3. Test：测试，SCF有自己的runtime，SCF命令行可模拟线上环境。\n4. Deploy - Staging： 灰度发布，当前云上灰度能力未完全开放，暂且跳过。\n5. Sanity check：发布审批。\n6. Deploy - Production：发布上线，这里利用了scf cli的发布能力。</code></pre></div>\n<p>2. index.py。就是scf cli init出来的helloworld模版，为了演示build阶段，特意import flask。</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"2803865277499651000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`# -*- coding: utf8 -*-import flask\ndef main_handler(event, context):print(str(event))return &quot;hello world&quot;`, `2803865277499651000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"># <span class=\"token operator\">-</span><span class=\"token operator\">*</span><span class=\"token operator\">-</span> coding<span class=\"token punctuation\">:</span> utf8 <span class=\"token operator\">-</span><span class=\"token operator\">*</span><span class=\"token operator\">-</span><span class=\"token keyword\">import</span> flask\ndef <span class=\"token function\">main_handler</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span><span class=\"token function\">print</span><span class=\"token punctuation\">(</span><span class=\"token function\">str</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token keyword\">return</span> <span class=\"token string\">\"hello world\"</span></code></pre></div>\n<ol start=\"3\">\n<li>template.yaml, scf的元信息文件，例如runtime，memsize等信息，可参见scf cli文档。</li>\n</ol>\n<p><strong>Jenkins pipeline配置</strong></p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lymbUOgSibqR9xicK4vABvBVsWAfDhkkSZB7TU75ulKrMjd47XGjlJ09oQ.jpg\"></p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lyKNxGiatTSaw6ld7qjfoUSstzliaQqKWh04JybsEY88X9vWCicptubR2Ng.jpg\"></p>\n<p>比较简单，将上面的jenkinsfile内容粘贴下配置即可完成。</p>\n<p><strong>触发&#x26;构建</strong></p>\n<p><strong>>>>></strong></p>\n<p><strong>触发</strong></p>\n<p>简单起见在jenkins上手动出发流程（当然你可以选择webhooks）自动触发。</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lyAjBoSxUpcicMXpn9hviaWOJbLVLpOicAticVa6nTT2O54ialksPhVfepibrA.jpg\"></p>\n<p><strong>></strong>>>></p>\n<p><strong>阶段视图</strong></p>\n<p>可以看见前面的checkout->build->test>Deploy - Staging阶段已自动化完成，因为上面配置了人工确认，手动确认后pipeline会将scf发布到腾讯云现网环境。</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lyicj2fDriatsAq3RraqdVEE4CaoQMkYWtgdT1oibFX7hH4DnKkajeFrkZA.jpg\"></p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lysvHM47k70A31YZKsOcOgvLf8wr8lVfNTsTtPydTiazHGb5zkG8aMaGg.jpg\"></p>\n<p><strong>>>>></strong></p>\n<p><strong>完整输出</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"9475717714692067000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`Running in Durability level: MAX_SURVIVABILITY[Pipeline] Start of Pipeline[Pipeline] nodeRunning on Jenkins in /var/lib/jenkins/workspace/scf_devops_demo[Pipeline] {[Pipeline] stage[Pipeline] { (Checkout)[Pipeline] echoCheckout[Pipeline] gitNo credentials specified> git rev-parse --is-inside-work-tree # timeout=10Fetching changes from the remote Git repository> git config remote.origin.url https://github.com/NevenMoore/scf_devops_demo.git # timeout=10Fetching upstream changes fromhttps://github.com/NevenMoore/scf_devops_demo.git> git --version # timeout=10> git fetch --tags --progress https://github.com/NevenMoore/scf_devops_demo.git +refs/heads/*:refs/remotes/origin/*> git rev-parse refs/remotes/origin/master^{commit} # timeout=10> git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10Checking out Revision a500383602e314911b62a74045295f0008b0288f (refs/remotes/origin/master)> git config core.sparsecheckout # timeout=10> git checkout -f a500383602e314911b62a74045295f0008b0288f> git branch -a -v --no-abbrev # timeout=10> git branch -D master # timeout=10> git checkout -b master a500383602e314911b62a74045295f0008b0288fCommit message: &quot;flask&quot;First time build. Skipping changelog.[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Build)[Pipeline] echoBuilding[Pipeline] sh+ pip install -r requirements.txtRequirement already satisfied: flask in /usr/local/lib/python3.6/site-packages (from -r requirements.txt (line 1)) (1.0.2)Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (6.7)Requirement already satisfied: itsdangerous>=0.24 in/usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (1.1.0)Requirement already satisfied: Werkzeug>=0.14 in/usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (0.14.1)Requirement already satisfied: Jinja2>=2.10 in /usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (2.10)Requirement already satisfied: MarkupSafe>=0.23 in/usr/local/lib64/python3.6/site-packages (from Jinja2>=2.10->flask->-r requirements.txt (line 1)) (1.1.1)[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Test)[Pipeline] echoTesting[Pipeline] script[Pipeline] {[Pipeline] sh+ scf native invoke -t ./template.yaml --no-eventSTART RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9{}\nEND RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9REPORT RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9 Duration: 0 ms BilledDuration: 100 ms Memory Size: 128 MB Max Memory Used: 32 MB\n\n&quot;hello world&quot;[Pipeline] }[Pipeline] // script[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Deploy - Staging)[Pipeline] echoDeploy - Staging[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Sanity check)[Pipeline] inputDoes the staging environment look ok?Proceed or AbortApproved by 帅哥[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Deploy - Production)[Pipeline] echoDeploy - Production[Pipeline] script[Pipeline] {[Pipeline] sh+ scf package -t ./template.yamlGenerate deploy file 'deploy.yaml' success[Pipeline] sh+ scf deploy -t ./deploy.yaml -fdeploy default begindefault scf_devops_demo already exists, update it nowDeploy function 'scf_devops_demo' successdeploy default end[Pipeline] }[Pipeline] // script[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Declarative: Post Actions)[Pipeline] echoThings were different before...[Pipeline] echoI succeeeded![Pipeline] }[Pipeline] // stage[Pipeline] }[Pipeline] // node[Pipeline] End of PipelineFinished: SUCCESS`, `9475717714692067000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">Running <span class=\"token keyword\">in</span> Durability level<span class=\"token punctuation\">:</span> <span class=\"token constant\">MAX_SURVIVABILITY</span><span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> Start <span class=\"token keyword\">of</span> Pipeline<span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> nodeRunning on Jenkins <span class=\"token keyword\">in</span> <span class=\"token operator\">/</span><span class=\"token keyword\">var</span><span class=\"token operator\">/</span>lib<span class=\"token operator\">/</span>jenkins<span class=\"token operator\">/</span>workspace<span class=\"token operator\">/</span>scf_devops_demo<span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> stage<span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">{</span> <span class=\"token punctuation\">(</span>Checkout<span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> echoCheckout<span class=\"token punctuation\">[</span>Pipeline<span class=\"token punctuation\">]</span> gitNo credentials specified<span class=\"token operator\">></span> git rev<span class=\"token operator\">-</span>parse <span class=\"token operator\">--</span>is<span class=\"token operator\">-</span>inside<span class=\"token operator\">-</span>work<span class=\"token operator\">-</span>tree # timeout<span class=\"token operator\">=</span><span class=\"token number\">10</span>Fetching changes <span class=\"token keyword\">from</span> the remote Git repository<span class=\"token operator\">></span> git config remote<span class=\"token punctuation\">.</span>origin<span class=\"token punctuation\">.</span>url https<span class=\"token punctuation\">:</span><span class=\"token operator\">/</span><span class=\"token operator\">/</span>github<span class=\"token punctuation\">.</span>com<span class=\"token operator\">/</span>NevenMoore<span class=\"token operator\">/</span>scf_devops_demo<span class=\"token punctuation\">.</span>git # timeout<span class=\"token operator\">=</span><span class=\"token number\">10</span>Fetching upstream changes fromhttps<span class=\"token punctuation\">:</span><span class=\"token operator\">/</span><span class=\"token operator\">/</span>github<span class=\"token punctuation\">.</span>com<span class=\"token operator\">/</span>NevenMoore<span class=\"token operator\">/</span>scf_devops_demo<span class=\"token punctuation\">.</span>git<span class=\"token operator\">></span> git <span class=\"token operator\">--</span>version # timeout<span class=\"token operator\">=</span><span class=\"token number\">10</span><span class=\"token operator\">></span> git fetch <span class=\"token operator\">--</span>tags <span class=\"token operator\">--</span>progress https<span class=\"token punctuation\">:</span><span class=\"token operator\">/</span><span class=\"token operator\">/</span>github<span class=\"token punctuation\">.</span>com<span class=\"token operator\">/</span>NevenMoore<span class=\"token operator\">/</span>scf_devops_demo<span class=\"token punctuation\">.</span>git <span class=\"token operator\">+</span>refs<span class=\"token operator\">/</span>heads<span class=\"token comment\">/*:refs/remotes/origin/*> git rev-parse refs/remotes/origin/master^{commit} # timeout=10> git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10Checking out Revision a500383602e314911b62a74045295f0008b0288f (refs/remotes/origin/master)> git config core.sparsecheckout # timeout=10> git checkout -f a500383602e314911b62a74045295f0008b0288f> git branch -a -v --no-abbrev # timeout=10> git branch -D master # timeout=10> git checkout -b master a500383602e314911b62a74045295f0008b0288fCommit message: \"flask\"First time build. Skipping changelog.[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Build)[Pipeline] echoBuilding[Pipeline] sh+ pip install -r requirements.txtRequirement already satisfied: flask in /usr/local/lib/python3.6/site-packages (from -r requirements.txt (line 1)) (1.0.2)Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (6.7)Requirement already satisfied: itsdangerous>=0.24 in/usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (1.1.0)Requirement already satisfied: Werkzeug>=0.14 in/usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (0.14.1)Requirement already satisfied: Jinja2>=2.10 in /usr/local/lib/python3.6/site-packages (from flask->-r requirements.txt (line 1)) (2.10)Requirement already satisfied: MarkupSafe>=0.23 in/usr/local/lib64/python3.6/site-packages (from Jinja2>=2.10->flask->-r requirements.txt (line 1)) (1.1.1)[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Test)[Pipeline] echoTesting[Pipeline] script[Pipeline] {[Pipeline] sh+ scf native invoke -t ./template.yaml --no-eventSTART RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9{}\nEND RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9REPORT RequestId: 59d1d0b0-c206-4a6d-a025-ebd364952bc9 Duration: 0 ms BilledDuration: 100 ms Memory Size: 128 MB Max Memory Used: 32 MB\n\n\"hello world\"[Pipeline] }[Pipeline] // script[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Deploy - Staging)[Pipeline] echoDeploy - Staging[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Sanity check)[Pipeline] inputDoes the staging environment look ok?Proceed or AbortApproved by 帅哥[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Deploy - Production)[Pipeline] echoDeploy - Production[Pipeline] script[Pipeline] {[Pipeline] sh+ scf package -t ./template.yamlGenerate deploy file 'deploy.yaml' success[Pipeline] sh+ scf deploy -t ./deploy.yaml -fdeploy default begindefault scf_devops_demo already exists, update it nowDeploy function 'scf_devops_demo' successdeploy default end[Pipeline] }[Pipeline] // script[Pipeline] }[Pipeline] // stage[Pipeline] stage[Pipeline] { (Declarative: Post Actions)[Pipeline] echoThings were different before...[Pipeline] echoI succeeeded![Pipeline] }[Pipeline] // stage[Pipeline] }[Pipeline] // node[Pipeline] End of PipelineFinished: SUCCESS</span></code></pre></div>\n<p><strong>>>>></strong></p>\n<p><strong>控制台检验</strong></p>\n<p>scf_devops_demo函数已经正确上传。</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lysYicZQYT0FHt9lzrNgah2MgSHaEIqlym4rjfNoPHHuibr0sPpibFSia5Pw.jpg\"></p>\n<p><strong>总结</strong></p>\n<p>从阶段视图可以看出SCF环境下的DevOps并没有什么不同。唯一的区别在于SCF有自己的runtime，需要官方提供的scf cli来模拟线上运行环境。deploy阶段直接用scf cli相比写代码云API也简单了许多。</p>\n<p>对于一些有特殊需求的SCF用户(私网CI/CD)，本文有一定借鉴作用。</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s637BIpOjtIJMe3ciaW6QJ4lyQkY6JNRXyA6coIQAelicCsCJALKibco8ul7Tl1N0kiatqpNmOBjHs6ibkg.jpg\"></p>\n<p>最后，pip install scf。下载一个scf cli，上车。</p>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a> </li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":""},"previousBlog":{"id":"d6260dcf-6000-54c1-82c8-1e96f13d0f2b","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/b96CibCt70iaaJcib7FH02wTKvoHALAMw4f553WgrmrmItKmjkMfO2fI0YJcpdO2WibwHXYAOGZyMaQruxD3skk7icQ.jpg","authors":["ecky"],"categories":["user-stories"],"date":"2019-07-04T00:00:00.000Z","title":"基于「树莓派+腾讯云」的在线甲醛监测系统","description":"本文详述腾讯云多产品组合设计和体验报告 之 “树莓派+腾讯云=在线甲醛监测系统”。涉及腾讯云产品：API网关、无服务器云函数、云数据库、腾讯云图。","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["树莓派","Serverless"],"keywords":"Serverless,serverless framework,腾讯云serverless","outdated":null},"wordCount":{"words":378,"sentences":118,"paragraphs":118},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-07-04-online-formaldehyde-monitoring-system.md","fields":{"slug":"/blog/2019-07-04-online-formaldehyde-monitoring-system/","keywords":["go","java","python","serverless","无服务器","无服务器云函数","云函数","javascript","serverlesscloud"]}},"nextBlog":{"id":"84311318-4be1-590f-a752-c8eac2dab459","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/YHl6UWa9s62sLCrvZhrtRH5BpLicw7aUOFcf8AVCXXFd1r1Gs9AgoXkqPY0icjL9koxzxcJ8RFiagsQnQdc29IKvg.jpg","authors":["李艳博"],"categories":["user-stories"],"date":"2019-07-02T00:00:00.000Z","title":"腾讯云函数访问 VPC 网络架构优化","description":"文章整理自腾讯云专家工程师周维跃及腾讯云高级工程师李艳博在 Kubecon 2019 上的分享,本篇文章分享云函数访问 VPC 网络方面的优化。","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["网络架构","Serverless"],"keywords":"Serverless,serverless framework,腾讯云serverless","outdated":null},"wordCount":{"words":188,"sentences":35,"paragraphs":35},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-07-02-function-access-vpc-network-architecture-optimization.md","fields":{"slug":"/blog/2019-07-02-function-access-vpc-network-architecture-optimization/","keywords":["go","serverless","函数计算","腾讯云函数计算","无服务器","云函数","函数","网卡","公网","访问","客户","弹性","创建"]}},"recommendBlogs":{"edges":[{"node":{"id":"665f9ce2-4451-59fd-bf98-1861789d3b3b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/Serverless_logo.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-03-19T00:00:00.000Z","title":"如何为无服务器开放源代码项目做贡献","description":"想要为无服务器开放源代码项目做贡献？您可以遵循下面的指南。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":96,"sentences":36,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-19-how-contribute-to-serverless-open-source.md","fields":{"slug":"/blog/2018-03-19-how-contribute-to-serverless-open-source/","keywords":["serverless","无服务器","serverless","github","插件","服务器","贡献","示例","blog","contribute"]}}},{"node":{"id":"a3e92579-65c3-5159-937c-32d18c5df7d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/why-not/why-not-header.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","operations-and-observability","engineering-culture"],"date":"2018-03-21T00:00:00.000Z","title":"不适合选择无服务器的情境及原因","description":"无服务器既有优点也有缺点。那么，哪些情境下不适合选择无服务器？原因又是什么呢？","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":163,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-21-when-why-not-use-serverless.md","fields":{"slug":"/blog/2018-03-21-when-why-not-use-serverless/","keywords":["faas","react","serverless","spa","无服务器","无服务器函数","无服务器架构","无服务器开发","服务器","twitter","serverless","blockquote","lang","script","en"]}}},{"node":{"id":"6a16520b-7886-582e-9182-64e50712d486","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/vendor+choice/serverless-data-portability.jpg","authors":["NickGottlieb"],"categories":["engineering-culture","guides-and-tutorials"],"date":"2018-06-20T00:00:00.000Z","title":"浅谈无服务器、数据锁定和供应商选择","description":"供应商选择是如今 IT 领导者需要考虑的最重要事项，而这一点可利用数据可移植性来实现。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":205,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-06-20-data-lockin-vendor-choice-portability.md","fields":{"slug":"/blog/2018-06-20-data-lockin-vendor-choice-portability/","keywords":["go","serverless","无服务器","无服务器架构","供应商","serverless","开发人员","数据","锁定","选择","服务"]}}},{"node":{"id":"94741abb-10ba-5db1-9756-cd1d573473fa","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/webstorm-ide/streamline-webstorm-serverless2.jpg","authors":["EslamHefnawy"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-08-15T00:00:00.000Z","title":"如何使用 WebStorm 简化无服务器工作流程","description":"在本文中，我将和您分享如何使用 WebStorm 进行无服务器特定的 IDE 设置以及如何利用它来极大地加快无服务器工作流程。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":234,"sentences":54,"paragraphs":54},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-08-15-streamline-serverless-workflow-webstorm.md","fields":{"slug":"/blog/2018-08-15-streamline-serverless-workflow-webstorm/","keywords":["nodejs","serverless","无服务器","无服务器开发","serverless","WebStorm","webstorm","服务器","blog","assets"]}}},{"node":{"id":"713a0563-4bf9-5721-bacb-3b4ef609fe4a","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/camp-fire/camp-fire-housing-thumb.jpg","authors":["EricWyne"],"categories":["guides-and-tutorials","user-stories"],"date":"2018-12-05T00:00:00.000Z","title":"Serverless Twitter 机器人帮助为坎普山火受灾者安置住房","description":"加利福尼亚州的坎普山火致使数千人流离失所，为此，我构建了一个简单的 Serverless Twitter 机器人来帮助将受灾者安置在临时住房！","authorslink":["https://serverless.com/author/ericwyne/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":157,"sentences":26,"paragraphs":26},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-05-serverless-twitter-camp-fire.md","fields":{"slug":"/blog/2018-12-05-serverless-twitter-camp-fire/","keywords":["serverless","无服务器","云函数","Serverless","org","住房","Twitter","函数","受灾","机器人","山火"]}}},{"node":{"id":"17c972d9-0583-51f6-9d5d-c2ba5f21b6a3","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191227/1577409288454-v2-577c2b21d600e3ea07f156f3e9d2d6b8_1200x500.jpg","authors":["Alfred Huang"],"categories":["guides-and-tutorials"],"date":"2019-08-21T00:00:00.000Z","title":"Serverless 的运行原理与组件架构","description":"本文重点探讨下开发者使用 Serverless 时经常遇到的一些问题，以及如何解决","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["运行原理","serverless"],"keywords":"Serverless 运行原理,Serverless 组件架构","outdated":null},"wordCount":{"words":236,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-21-serverless-operation-architecture.md","fields":{"slug":"/blog/2019-08-21-serverless-operation-architecture/","keywords":["koa","serverless","云函数","Serverless","用户","函数","请求","实例","形态","业务","serverlesscloud"]}}},{"node":{"id":"ae4fd2f8-515c-5aec-b584-38427ef33f7e","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578989800047-part-00492-780.jpg","authors":["Anycodes"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-09-16T00:00:00.000Z","title":"突破传统 OJ 瓶颈，「判题姬」接入云函数","description":"通过 Serverless 实现在线编程","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["在线编程","云函数"],"keywords":"Serverless 在线编程,Serverless OJ","outdated":null},"wordCount":{"words":169,"sentences":30,"paragraphs":30},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-16-online-Judge.md","fields":{"slug":"/blog/2019-09-16-online-Judge/","keywords":["python","serverless","云函数","代码","函数","serverless"]}}},{"node":{"id":"545ab3d2-e14e-5cc2-8548-0e863eac942b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/2019-10-deployment-best-practices/safeguard-header.png","authors":["FernandoMedinaCorey"],"categories":["guides-and-tutorials"],"date":"2019-10-14T00:00:00.000Z","title":"无服务器部署最佳实践","description":"了解部署无服务器应用时的一些最佳实践。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":221,"sentences":46,"paragraphs":46},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-14-serverless-deployment-best-practices.md","fields":{"slug":"/blog/2019-10-14-serverless-deployment-best-practices/","keywords":["serverless","无服务器","serverless","部署","服务器","开发人员","应用","安全措施","使用","函数"]}}}],"totalCount":31}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"e5cbc7f7-4743-507a-be01-abb06982a340","previousBlogId":"d6260dcf-6000-54c1-82c8-1e96f13d0f2b","nextBlogId":"84311318-4be1-590f-a752-c8eac2dab459","categories":["guides-and-tutorials"]}}}