返回
自从知道了CI/CD开始,就一直充满好奇和疑问,它的“自动、智能”化让人不禁浮想联翩,但又雀雀欲试。同时,不想再使用石器时代的方式——手动通过FTP上传代码到服务器,一直驱动着笔者去揭开这层神秘的面纱。之后做过一系列的尝试——Jenkins、Travis、GitHub Actions。期间为了更好的使用那3种方式,还简单了解了Linix命令、shell命令和docker。
当完成了一些CI/CD的部署后,却发现只是简单地学会了使用这3种工具而已。没有任何值得分享的东西。为了加深印象,强化学习成果,还是要记录下这段“心酸的血泪史”。
如果你也是CICD的初学者,想要找到一个CICD的参考例子,那就继续向下看吧~
CI全称持续集成(Continuous Integration),指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码"集成"到主干。
好处:
目的:
核心措施:
CD全称持续交付(Continuous Delivery),指的是频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。
CD全称持续部署(Continuous Deployment),指的是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。
目标:
前提:
两个CD的区别:
在这篇文章里面的CD我们指的是持续部署。通常开发过程中,至少有dev/master两个分支,dev触发生产环境CD,部署成功后,QA就可以开始一系列的测试。QA测试通过后,master触发生产环境的CD,即可供所有真实用户使用。
参考链接:持续集成是什么?
经过对于Jenkins、Travis、GitHub Actions Actions的使用,发现他们大概的主要步骤是:
Jenkins、Travis、GitHub Actions在使用起来,简易程度依次提升。
接下来,主要给大家介绍Tarvis 和 GitHub Actions如何实现CI/CD
Travis CI 提供的是持续集成服务(Continuous Integration,简称 CI)。它绑定 Github 上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。
在install阶段:
install:
- command1
- command2
如果command1失败了,整个构建就会停下来,不再往下进行。
如果不需要安装,即跳过安装阶段,就直接设为true。
install: true
在script阶段:
script:
- command1
- command2
如果command1失败,command2会继续执行。但是,整个构建阶段的状态是失败。
如果command2只有在command1成功后才能执行,就要写成下面这样。
script: command1 && command2
//更改gem源
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
sudo gem install travis
//macOS
brew install travis
env:
- DB=postgres
- SH=bash
- PACKAGE_VERSION="1.0.*"
前提,在客户端登录travis
如果正在使用 travis-ci.com, 而不是travis-ci.org,需要添加 --pro,--pro可以添加在travis的所有命令后面
//
travis login --pro
travis encrypt SOMEVAR="secretvalue"
//--add,可以
travis encrypt SOMEVAR="secretvalue" --add
// 下面的代码会自动添加变量到.tarvis.yml
env:
global:
secure: WVnueaUvOoou0/K+ZfSIgJCrL4jku5vWIJKJ/Bipba1442FHKlBx90/9+sjt3IzhiGjHABpX6xJRku4FVbPJIrpzcYbmCcXaSDU0VJq8X2Ea23Ie2SQB6KgQdjauaqN1JOTmvjBy2yeJ/BqgK1fr9xSfvKhAcsJq3TvrhGBPhMSoL6mA3KB/EQRlVXojQ4e2TGMf+7Hmy/9ORARQSId+8wU5QrHSkdUUcGi+yPF6Q6Q4OVVJM44eIYHm5YxlyXChk1mbVqjh/NOOmBU4h0nK2AiW/2O8XTKlPUldK/RHRTGhJVyLfLFz2sy9f9WoJhROhwKXvHovahXE91WnolpAN7xUoAkGqE+EJ2wiL560lLpGYZvqk40+Xdc6yxv5ojvBD3uPc59jAhC/i/h8+u4fcYCWMQ4ZUYpdP82fl5d7Vwiu+s/dP+p71YiwQCArZ+1mOra0zrkrGvHr1remQp2FSZLCk34fAazZwBezjl0ivlz7eT05xeH2DzvB/cLDm2p8zsvOSSJToi7rnSVWygyjgYIn8XzCWGA+Jk9ro2wQu+IPhfW8C+Ys6nymUAGIWdfvVJq7AYd15WAk2DYlGuCi5nV7uIFwGUKqHy51FMn1FegxxmY/hcWCmwyKlgiO3rE0b8OmcJfjU8iawkMEwEXb3o2qH/oX7vNScnaBsHvNA1c=
travis encrypt-file bacon.txt --add
language: node_js
node_js:
- 12.10.0
下面以https://github.com/Heyff12/vuepress-theme-yaya仓库为例,配置.travis.yml
language: node_js
node_js:
- 12.10.0
install:
- npm install
script:
- npm run build
deploy:
- provider: pages
github_token: "$GITHUB_TOKEN"
keep_history: true
local-dir: "./docs/.vuepress/dist/"
target-branch: gh-pages
skip_cleanup: true
verbose: true
on:
branch: master
备注:GitHub Pages的部署服务器是GitHub本身,github_token就是实现Travis与部署服务器关联的核心
在上面项目的基础之上,继续配置.travis.yml
language: node_js
node_js:
- 12.10.0
install:
- npm install
script:
- npm run build
deploy:
- provider: pages
github_token: "$GITHUB_TOKEN"
keep_history: true
local-dir: "./docs/.vuepress/dist/"
target-branch: gh-pages
skip_cleanup: true
verbose: true
on:
branch: master
- provider: npm
email: 645068564@qq.com
skip_cleanup: true
api_key:
secure: WXswpjk-----------------= #防止信息泄露,这里的字符串是随机数据
on:
tags: true
branch: master
repo: Heyff12/vuepress-theme-yaya
tag: latest
npm addUser
,输入npm账号相关信息cat ~/.npmrc
,查看该文件的详情,找到authToken对应的值travis encrypt YOUR_AUTH_TOKEN --add deploy.api_key
,对这个值进行加密,YOUR_AUTH_TOKEN是上个步骤中找到的authToken的值备注:npm package的部署终端在npm平台,api_key就是实现Travis与部署服务器关联的核心
//查看所有tag
git tag
//添加tag
git tag v1.0.0
//伴随着代码提交,同时提交tag
git push origin --follow-tags v1.0.0
//删除本地tag
git tag -d v1.0.0
//删除远端tag
// git push origin --delete v1.0.0
自定义服务器部署的核心——实现对该服务器的免密登录。这是因为在Travis执行过程中,不能手动输入用户名和密码。
登录服务器后,执行
cd ~/.ssh
//生成秘钥对,-f指定秘钥文件名称
ssh-keygen -t rsa -f id_rsa_remote_login -C "RemoteLoginCIDeployKey"
//将公钥 放入 authorized_keys
cat id_rsa_remote_login.pub >> authorized_keys
//拷贝 私钥,到自己的电脑
scp root@000.00.00.00:/root/.ssh/id_rsa_remote_login ./id_rsa_remote_login
//测试免密登录
ssh -i id_rsa_travis root@000.00.00.00
切换到当前项目,实现当前项目与服务器之间的免密登录
//切换到 当前项目根目录,
//登录 travis
travis login --pro
//加密私钥
travis encrypt-file ~/.ssh/id_rsa_remote_login --add
// 会自动生成id_rsa_remote_login.enc ,同时.travis.yml 自动生成before_install openssl 代码,在travis.com平台的该项目下,会自动生产两个加密变量
注意事项:要把 out 后的文件名中的转义字符 '' 删掉,否则Travis CI运行过程中会报错
language: node_js
node_js:
- 12.10.0
git:
submodules: false
branches:
only:
- master
addons:
ssh_known_hosts:
- 000.00.00.00
before_install:
- openssl aes-256-cbc -K $encrypted_f8f2226d74ef_key -iv $encrypted_f8f2226d74ef_iv
-in id_rsa_remote_login.enc -out ~/.ssh/id_rsa_remote_login -d
- chmod 600 ~/.ssh/id_rsa_remote_login
- eval "$(ssh-agent -s)"
- echo -e "Host 000.00.00.00\n\tUser root\n\tHostName 000.00.00.00\n\tPort 22\n\tStrictHostKeyChecking
no\n\tIdentityFile ~/.ssh/id_rsa_remote_login\n\tIdentitiesOnly yes\n\tGSSAPIAuthentication
no\n" >> ~/.ssh/config
- ssh-add ~/.ssh/id_rsa_remote_login
install:
- npm install
script:
- npm run build
after_success:
- travis_wait scp -i ~/.ssh/id_rsa_remote_login -o GSSAPIAuthentication=no -r dist/ root@000.00.00.00:/project/nodeweb/vuepressYaya/src
- ssh -i ~/.ssh/id_rsa_remote_login root@000.00.00.00 "cd /project/nodeweb/vuepressYaya && rm -rf
public && mv src public && exit"
参考链接:
大家知道,持续集成由很多操作组成,比如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions。
github actions必须放置在项目根目录.github/workflows文件夹下。在 .github/workflows中,可以添加多个工作流程,后缀必须是 .yml 或 .yaml 文件
on: [push, pull_request]
on:
push:
branches:
- master
jobs:
my_first_job:
name: My first job
my_second_job:
name: My second job
jobs:
job1:
job2:
needs: job1
job3:
needs: [job1, job2]
在GitHub平台,进入一个项目界面
在这个CI/CD实现过程中,需要使用docker。大致思路:
mkdir node-test
cd node-test
npm init -y
npm install koa
mkdir src
cd src
在src目录下新建app.js文件
const Koa = require('koa');
const app = new Koa();
const PORT = 3002;
const HOST = '0.0.0.0';
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(PORT);
console.log(`Running on http://${HOST}:${PORT}`);
在package.json文件中添加
"scripts": {
"start": "node app.js"
},
启动该服务
npm run start
在根目录新建Dockerfile文件
FROM node:12
RUN mkdir -p /home/Service
WORKDIR /home/Service
COPY . /home/Service
RUN npm install
EXPOSE 3002
CMD ["npm","start"]
备注:
docker build -t node-test .
docker run --name my-node-test -d --rm -it -p 3002:3002 node-test
//清空之前docker痕迹,卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine
//执行以下命令安装依赖包
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
//添加 yum 软件源
sudo yum-config-manager \
--add-repo \
https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
sudo sed -i 's/download.docker.com/mirrors.ustc.edu.cn\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
//如果需要测试版本的 Docker CE 请使用以下命令:
sudo yum-config-manager --enable docker-ce-test
//更新 yum 软件源缓存,并安装 docker-ce
sudo yum makecache fast
sudo yum install docker-ce
//启动 Docker CE
sudo systemctl enable docker
sudo systemctl start docker
#测试 Docker 是否安装正确
docker run hello-world
在项目根目录新建.github文件夹,在.github文件夹中新建workflows文件夹,在workflows文件夹下新建ali.yml
# 指定该action的名称
name: Ali deploy CI
# 指定该action触发的事件情形,当master分子在push、pull_request时触发
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# 定义全局变量
env:
IMAGE_NAME: heyff12/node-test # 镜像名称
VERSION: latest # 镜像版本
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} #docker用户名
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} #docker密码
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.10.0]
steps:
- uses: actions/checkout@v2 #使用这个共有action拉取最新代码
- name: Use Node.js ${{ matrix.node-version }} #设置node版本
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci #执行npm install
- run: npm run build --if-present
- run: npm test
env:
CI: true
- name: store file #构建缓存数据,以便下一个step使用
uses: actions/upload-artifact@v2
with:
name: store
path: ./
docker-push-image: #登录docker,构建并push镜像
needs: build #这个job需要build job完成后才能执行
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v2 #下载上个step存储的代码
with:
name: store
- name: Build the Docker image
run: |
echo `pwd`
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker build -t $IMAGE_NAME:$VERSION -f ./Dockerfile .
docker push $IMAGE_NAME:$VERSION
docker-run: #登录服务器,拉取最新镜像,并启动容器
needs: docker-push-image
runs-on: ubuntu-latest
env:
CONTAINER_NAME: heyff12-node-test
DOCKER_REG: heyff12/node-test
steps:
- name: deploy on remote server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.ACCESS_TOKEN }}
port: ${{ secrets.REMOTE_PORT }}
command_timeout: 30m
envs: CONTAINER_NAME, DOCKER_REG
script: |
docker pull $DOCKER_REG
docker image prune -f
if [ $(docker ps -a | grep -c $CONTAINER_NAME) -gt 0 ]; then docker stop $CONTAINER_NAME;docker rm $CONTAINER_NAME;fi
docker run --name $CONTAINER_NAME -d --rm -p 3002:3002 $DOCKER_REG
备注:
参考链接:
新建.github/workflows/ali.yml
name: Ali deploy CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test
env:
CI: true
- name: ssh Deploy
uses: easingthemes/ssh-deploy@v2.1.2
env:
SSH_PRIVATE_KEY: ${{ secrets.ACCESS_TOKEN }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
REMOTE_PORT: ${{ secrets.REMOTE_PORT }}
ARGS: "-avz --delete"
SOURCE: "dist/"
TARGET: "/project/nodeweb/vuepressYaya/public"
000.00.00.00 需要替换成自己的服务器IP地址