An Example Implement Ansible Deployment on Github Action

- name: write secrets into json run: | echo "${{ toJSON(secrets) }}" > _github_secrets.json - name: write github repo vars into json run: | echo "${{ toJSON(vars) }}" > _github_vars.json - name: write ssh private key run: | echo "${{ secrets.STAG_SSH_PRIVATE_KEY }}" > ${{ github.workspace }}/.ssh_private_key.pem chmod 0400 ${{ github.workspace }}/.ssh_private_key.pem - name: write ssl certificate run: | echo "${{ secrets.showmecodes_TLS_CERTIFICATES }}" > ${{ github.workspace }}/showmecodes.ai.pem echo "${{ secrets.showmecodes_TLS_KEY }}" > ${{ github.workspace }}/showmecodes.ai.key - name: deploy showmecodes to stag uses: dawidd6/action-ansible-playbook@v2 with: playbook: playbook-showmecodes.yml key: ${{ secrets.STAG_SSH_PRIVATE_KEY }} options: | --inventory env_vars/${{env.APP_ENV}}/hosts.yaml --extra-vars "app_backend_zip_path=${{ needs.init_build_version.outputs.backendArtifactName }} app_frontend_zip_path=${{ needs.init_build_version.outputs.fontendStagArtifactName }} app_version=${{ needs.init_build_version.outputs.VERSION }} ansible_ssh_private_key_file=${{ github.workspace }}/.ssh_private_key.pem showmecodes_tls_certificate_file=${{ github.workspace }}/showmecodes.ai.pem showmecodes_tls_private_key_file=${{ github.workspace }}/showmecodes.ai.key" --extra-vars=@_github_vars.json --extra-vars=@_github_secrets.json

2024-04-22 · 1 min · 114 words · 翟志军 Jack Zhai

Web前端构建之依赖版本管理最佳实践

本文需要读者懂一点点前端的构建知识: package.json文件的作用之一是管理外部依赖; .npmrc是npm命令默认配置,放在工程根目录。 Web前端构建一直都是一个不难,但是非常烦人的问题,在DevOps、CI/CD领域。 烦人的是偶尔发生这样的事情: 开发在本地构建通过,但是流水构建失败。这时前端开发人员会经常报怨Pipeline不稳定; 流水线构建通过,但是在生产环境上启动不了,或者出现运行错误; 不使用Docker可以启动,但是打包成Docker镜像后启动就失败。 这类问题,不是今天解决了,明天就不会发生。而是你根本不知道它什么时候又发生。 据我观察,绝大多数时候都是依赖版本管理没有做好导致的。 Web前端的依赖版本管理包括以下几个维度: node的版本 外部依赖的版本 我们需要在开发环境,构建环境,运行环境保证它们的版本是一致的。这样,在本地开发环境测试通过,那么,在其它环境就理论上也应该能通过。 接下来是具体的最佳实践。 保证Node版本一致 要保证Node版本一致,就要保证所有的环境使用同一个版本的node。而且是要具体到某一个精确的版本,如v20.11.1,而不是20这样一个粗略版本。 以下是我们以v20.11.1为例。 设置开发环境 设置开发环境的node的版本,需要在package.json中加入: { "engines": { "node": "v20.11.1", "npm": "10.2.4" }, } 这时,如果存在开发环境与配置的版本不匹配的情况,执行npm install,会出现以下警告,但是命令还是会继续执行: npm WARN EBADENGINE Unsupported engine { npm WARN EBADENGINE package: '[email protected]', npm WARN EBADENGINE required: { node: 'v20.10.1', npm: '10.2.4' }, npm WARN EBADENGINE current: { node: 'v20.11.1', npm: '10.2.4' } npm WARN EBADENGINE } 希望强制要求版本一致,就在根目录的.npmrc文件加入: engine-strict=true 发生版本不一致的情况,报错日志如下,且命令会停止执行: npm ERR! code EBADENGINE npm ERR! engine Unsupported engine npm ERR! engine Not compatible with your version of node/npm: [email protected] npm ERR! notsup Not compatible with your version of node/npm: [email protected] npm ERR! notsup Required: {"node":"v20.10.1","npm":"10.2.4"} npm ERR! notsup Actual: {"npm":"10.2.4","node":"v16.0.1"} 设置构建环境 我们以Github Actions为例。在设置node环境时,应设置为: ...

2024-03-12 · 1 min · 168 words · 翟志军 Jack Zhai

优秀的DevOps工程师应该具有什么特质?

这是我最近面试时遇到的一个非常好的问题: 在你的心目中,优秀的DevOps工程师应该是什么样的? 很长一段时间里,我没有想到这个问题。所以,当HR问起时,我边思考边回答。 我已经不记得原话,本文就当作从性格角度思考“优秀的DevOps工程师应该具有什么特质”。 首先,优秀的DevOps工程师,TA应该是严谨的。 一个严谨的特质的人才会主动考虑软件工程化过程中的各种可能。 其次,TA应该对手工操作产生“生理性上的不适”,即追求自动化极致。 一个再怎么严谨的人,如果是手工操作,面对复杂的线上环境的运维,TA的能力也是有限的。TA必须自动化所有能自动化的东西,并争取自动化所有的内容,TA才能有可能“驯服野兽”。 然后,TA应该是一个节约的人,即节俭。 因为浪费导致企业不必要损失,是否要避免这个损失,很多时间里是一个DevOps工程师的选择问题。 最后,TA应该是一个热爱这个领域的人。我常常忘记自己对这个领域的热爱。以致于我忘记回答。 只有热爱,才能产生自驱力,驱动TA去成长,去不断思考。即使你再严谨,你也有考虑不到的地方,只有热爱,TA才会探索到TA不知道他不知道的。 以上只是我个人的看法,欢迎一起讨论。

2024-03-12 · 1 min · 13 words · 翟志军 Jack Zhai

DevOps架构师是如何看待Github Actions的共享制品解决方案的?

前言 Github Actions是Github提供的一个CICD Pipeline服务。除了Pipeline,它还提供Secret和简单的配置管理。 本文并不是它的一个完整介绍和知识的罗列。而是我在实际使用Github Actions后,对Github Actions的“共享问题”的解决方案的总结。 不要小看这个问题,它是所有的Pipeline平台(包括Gitlab CI)都会遇到的问题。只要对这一问题深入理解,所有的平台一通百通。 提示1:下文可能会是Workflows和Pipeline两个术语共用。因为它们本质上就是同一个东西,只是不同平台不同的叫法。 提示2:下文可能会共用DevOps平台和Pipeline平台,虽然它们可能是完全不同的平台,但是在本文中,它们都是能提供Pipeline的平台。 共享问题 只要是Pipeline平台,都会遇到共享问题。那么,什么是共享问题? 共享问题就是Pipeline中不同的位置之间共享资源,以实现不重复执行、生成准确结果的目标。 定义听起来有些枯燥。我们列举一个有共享的场景,就比较好理解了: 比如对于一个单仓库,它同时包含多个前端工程,这些前端工程同时依赖于一个common模板。其它前端工程只有等它构建完成,并取得构建,才能开始自己的构建。如果common模板的构建结果不能被其它工程构建共享使用,就会存在构建结果不一致、重复构建的问题; 比如一个Pipeline中,版本号是有特定格式的,需要在第一步骤计算出来后,其它步骤取得这个版本号,进行打包工作。如果没有实现版本号在多个步骤之间共享,很可能会导致版本号不一致问题。 我们稍微对共享问题进行抽象和理解,根据共享的范围,共享问题,可以分为: Workflows之间进行共享; Workflow内的Jobs之间进行共享; Job内的Step之间进行共享。 根据共享的内容,可以分为: 共享源码; 共享制品; 共享变量。 Github Actionsr制品的定义 在Github Actions中的制品(Artifact)的概念和我们平时所说的“制品”有一定的区别。在Github Actions中,制品指的是Job生成的文件或者文件夹。 我们平时所说的,更广意的“制品”,在Github Actions叫Packages。 Workflows之间的共享制品 一般只有在大型项目才会存在Workflows之间的共享。而我个人是不建议将依赖Pipeline实现大型项目的构建的,而是依赖构建工具本身的能力。 由于笔者时间有限,不再亲身做实验,本节内容,请读者自行测试。 如果Workflow是由workflow_run事件触发的情况下,它们就可以直接使用actions/upload-artifact和actions/download-artifact两个actions来实现制品的共享。相关文档:https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#downloading-artifacts-during-a-workflow-run 有趣的是Github Actions提供了一种reusable的Workflow概念。说到底是一种模板化Workflow的方式。但是这种方式不适合用来实现共享制品。因为它并不是共享制品。相关文档:https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-outputs-from-a-reusable-workflow Jobs之间共享制品 在同一个Workflow中,多个Job进行制品共享。如下代码: jobs: // 生成制品的job build: runs-on: ubuntu-latest steps: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and export uses: docker/build-push-action@v5 with: context: . tags: myimage:latest outputs: type=docker,dest=/tmp/myimage.tar - name: Upload artifact uses: actions/upload-artifact@v3 with: name: myimage path: /tmp/myimage.tar // 使用制品的job use: runs-on: ubuntu-latest needs: build steps: - name: Download artifact uses: actions/download-artifact@v3 with: name: myimage path: /tmp - name: Load image run: | docker load --input /tmp/myimage.tar docker image ls -a 以上案例来自:https://docs.docker.com/build/ci/github-actions/share-image-jobs/ ...

2024-01-16 · 2 min · 290 words · 翟志军 Jack Zhai

使用Google OSV工具扫描依赖安全漏洞

安全漏洞是软件工程化能力的试金石 2021年年底,Log4j的漏洞陆续被公开。因为该框架被大量的开源软件依赖,所以,漏洞影响面非常大。 面对这个漏洞,我们遇到的第一个问题是:如何知道我们哪些工程使用了Log4j? 在我看来,这个漏洞是企业软件工程化的一颗非常好的试金石。因为: 如何第一时间了解到这个漏洞,反应这家企业的安全能力; 如何第一时间能找到所有使用了Log4j的位置,体现了这家企业第三方软件依赖管理能力; 替换Log4j的速度,体现企业的持续集成、持续部署的能力。 Google的开源软件安全漏洞扫描工具 今天介绍的OSC-Scanner,能加强我们第1项和第2项能力。 OSV-Scanner是Google在2022年12月13日推出的一款免费的安全扫描工具。它具有以下特点: 支持多生态系统,包括:Go、PyPI、RubyGens、Linux、Maven等16个生态系统; 同时支持直接依赖的扫描和间接依赖的扫描; 采用标准的漏洞记录格式; 从当前最大的开源软件漏洞数据库(https://osv.dev/)获取信息。这也是DenpencyTrack和Flutter安全工具的漏洞数据库。 OSV-Scanner是一款命令行工具,我们可以将它集成到我们的构建工具或者CICD Pipeline中。目前它已经被集成到Scorecard中。Scorecard是一款为开发源软件的安全健康度打分的开源软件。我们可以在Github Actions中使用它:https://github.com/ossf/scorecard/tree/main?tab=readme-ov-file#scorecard-github-action OSV-Scanner的安装 Windows: scoop install osv-scanner Mac Homebrew: brew install osv-scanner 也可以直接下载二进制包:https://github.com/google/osv-scanner/releases 具体安装文档:https://google.github.io/osv-scanner/installation/ OSV-Scanner的使用 Keras是一个使用Python编写的开源人工神经网络库。我们以它为例。命令行里运行以下命令: ./osv-scanner_1.3.6_linux_amd64 --format json keras/ 输出内容说明:keras存在一个“潜在内存泄漏”的漏洞。 当拿到json结果后,我们的DevOps平台就可以进行一些告警监控的操作。 后记 osv-scanner目前需要连osv.dev,才能使用。但是,已经开放实验功能,允许用户离线使用osv-scanner。这是自建DevOps平台的福音!

2023-12-25 · 1 min · 39 words · 翟志军 Jack Zhai

基于 Jenkins 的 DevOps 平台应该如何设计凭证管理

背景 了解到行业内有些团队是基于 Jenkins 开发 DevOps 平台。而基于 Jenkins 实现的 DevOps 平台,就不得不考虑凭证的管理问题。 本文就此问题进行讨论,尝试找出相对合理的管理凭证的方案。 一开始我们想到的方案可能是这样的:用户在 DevOps 平台增加凭证后,DevOps 再将凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,使用的是存储在 Jenkins 上的凭证,而不是 DevOps 平台上的。 但是,仔细想想,这样做会存在以下问题: Jenkins 与 DevOps 平台之间的凭证数据会存在不一致问题。 存在一定的安全隐患。通过 Jenkins 脚本命令行很容易就把所有密码的明文拿到。哪天 Jenkins 被注入了,所有的凭证一下子就被扒走。 无法实现 Jenkins 高可用,因为凭证存在 Jenkins master 机器上。 那么,有没有更好的办法呢? 期望实现的目标 先定我们觉得更合理的目标,然后讨论如何实现。以下是笔者觉得合理的目标: 用户还是在 DevOps 管理自己的凭证。但是 DevOps 不需要将自己凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,从 DevOps 上取。 实现方式 Jenkins 有一个 Credentials Binding Plugin 插件,在 Jenkins pipeline 中的用法如下: withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) { sh ''' curl -u "$USERPASS" https://private.server/ > output ''' } withCredentials 方法做的事情就是从 Jenkins 的凭证列表中取出 id 为 mylogin 的凭证,并将值赋到变量名为 USERPASS 的变量中。接下来,你就可以在闭包中使用该变量了。 ...

2019-05-07 · 1 min · 160 words · 翟志军 Jack Zhai

ChatOps实战

ChatOps概念在国内已经有一些文章谈过,但是都处于理论范畴。而本文则是一篇ChatOps实践的文章。 有必要说明我对ChatOps的理解,ChatOps表面上就是在一个聊天窗口中,发送一个命令给运维机器人bot,然后bot根据我们预定义的操作进行执行,并返回执行结果。至于更深层次的作用,就是将重复性的手工的运维工作自动化了,开发人员、运维人员可以按需执行一些运维操作。 另外,我做到了自动化搭建这一套东西(感谢Github上那么多开源项目,让我少写很多Ansible脚本)。为什么要自动化搭建呢?因为我懒,我不想每次通过一条条shell手工搭建。 本文主题 在RocketChat的聊天窗口中命令Hubot执行一次Jenkins构建任务。 工具介绍 有必要简单说明一下我们此次实现ChatOps的这几个工具。 RocketChat 可以把RocketChat想像成一个具有更多功能的IRC或者微信。它依赖于MongoDB,所以,我们还将自动化安装MongoDB。 如果你了解过Slack的话,它可以作为Slack的开源替代表。 Hubot Hubot是Github出品的一个运维机器人。本质上就是一个接收命令消息,执行预定义操作的一个程序。而接收命令消息的这个组件在Hubot中被称为Adapter。比如我们希望Hubot接收来自RocketChat聊天窗口里的消息,我们就必须为Hubot安装一个RocketChat的Adapter。市面上,已经有很多Adapter了,我们很少需要自己实现自定义Adapter。 那么,Hubot接收到命令消息后,怎么知道执行哪些操作呢?这部分是需要我们实现了。本质上就是通过正则表达式匹配命令消息,然后操作。实际上通过写Coffescript脚本实现。比如: robot.respond /open the (.*) doors/i, (res) -> doorType = res.match[1] if doorType is "pod bay" res.reply "I'm afraid I can't let you do that." else res.reply "Opening #{doorType} doors" Jenkins 就这个就不用多介绍了。值得一提是Github已经有不少自动化搭建Jenkins的Ansible脚本了(完全不需要人工干预),本文使用的是geerlingguy的。 Ansible 能让开发人员快速上手的自动化运维工具。我们使用Ansible实现自动化。想简单了解Anbible,可以看看简单易懂Ansible系列 —— 解决了什么。 准备环境 需要准备几台机器: IP OS 安装 192.168.61.11 CentOS7 Jenkins,Openresty(for Jenkins) 192.168.61.14 CentOS7 Openresty(for RocketChat) 192.168.61.15 CentOS7 RocketChat Server, MongoDB,Hubot 因为我是在本地做实验的,所以需要在本机虚拟化3台机器。我使用Vagrant + VirtualBox的方式来实现。具体Vagrant如何使用,不在本文讨论范围。你也可以手工在VirtualBox或Vmware上创建相应的虚拟机。Vagrant只不过是自动化了这个过程。Vagrant会基于一个称为Vagrantfile的文件来创建机器。 ...

2017-10-08 · 1 min · 205 words · 翟志军 Jack Zhai

简单易懂Ansible系列 —— 实现ssh key主机之间复制

我们在搭建Hadoop完全分布式环境时,Hadoop的name node节点(理解为master节点)需要无密码登录到所有的data node节点。 当然,我们使用手工的方式很容易就实现了: 在name node节点上生成ssh key:ssh-keygen 将public key copy到所有的data node节点上:ssh-copy-id slave1 同时,你还必须设置~/.ssh/config,以防止登录时不停的问yes or no: ```yml Host * StrictHostKeyChecking no ``` 完了,还要设置这个文件的权限为400。 以上步骤当然可以手工一步步执行。但是,总有那么一些人:希望所有的操作都可以版本化,所有的操作都应该自动化。我属于这些人。 再说了,我发现在搭建Jenkins环境时,也遇到了同样的问题:需要将Jenkins master的public key加入到Jenkins agent机器中。 可以预见到将来我还会遇到类似的问题。于是,我找到一个方法来自动化以上操作。 在name node机器上执行task如下 创建用户的时候生成ssh_key: - name: create hadoop user user: name: "{{hadoop_user}}" group: "{{hadoop_group}}" createhome: yes generate_ssh_key: yes ssh_key_bits: 2048 ssh_key_file: .ssh/id_rsa tags: - hadoop 将id_rsa.pub拉取到ansible执行机器上 - name: fetch public key fetch: src: "/home/{{hadoop_user}}/.ssh/id_rsa.pub" dest: /tmp/ flat: yes tags: - hadoop 设置StrictHostKeyChecking no 因为我们只想修改这个用户的ssh行为,所以我们的ssh的配置只是针对当前这个用户的: ...

2017-08-19 · 1 min · 157 words · 翟志军 Jack Zhai