CI/CD

CI/CD

  • CI/CD:持续集成、持续交付

  • 程序传统上线流程

递交
反馈
提交
开发部
测试部
运维部
  • 程序语言分类

    • 解释执行:python / shell / php
    • 编译执行:c / c++ / go / java
  • CI/CD流程

提交
克隆
下载
下载
下载
下载
程序员
git服务器
jenkins
应用服务器
应用服务器
应用服务器
应用服务器

git应用

  • 安装与配置
[root@dev ~]# yum install -y git
[root@dev ~]# git config --global user.name zhangzhg
[root@dev ~]# git config --global user.email zhangzg@tedu.cn
[root@dev ~]# git config --global core.editor vim
[root@dev ~]# git config --list
user.name=zhangzhg
user.email=zhangzg@tedu.cn
core.editor=vim
  • 电子书推荐:https://git-scm.com/book/zh/v2
  • git重要的工作区域
    • 工作区:项目目录
    • 暂存区:工作区和版本库之间的缓冲地带,也叫stage或index。位于.git/index
    • 版本库:快照存储区域,工作区下的.git目录
add
commit
工作区
暂存区
版本库
  • 文件状态

    • 未跟踪:工作区中的文件,尚未执行add操作
    • 已跟踪:
      • 已暂存:通过git add将文件放到暂存区
      • 未修改:git commit后,没有再做修改。工作区文件内容与版本库中的一致
      • 已修改:git commit后,修改文件,但是没有执行git add操作
  • 操作

# 创建版本库,方法一
[root@dev ~]# mkdir projects; cd projects
[root@dev projects]# git init mytest
初始化空的 Git 版本库于 /root/projects/mytest/.git/
[root@dev projects]# ls
mytest
[root@dev projects]# ls -A mytest/
.git

# 创建版本库,方法二
[root@dev projects]# mkdir myweb; cd myweb
[root@dev myweb]# echo 'Hello World' > readme.md
[root@dev myweb]# git init
初始化空的 Git 版本库于 /root/projects/myweb/.git/
[root@dev myweb]# ls -A
.git  readme.md

# 查看文件状态
[root@dev myweb]# git status
# 位于分支 master
#
# 初始提交
#
# 未跟踪的文件:
#   (使用 "git add <file>..." 以包含要提交的内容)
#
#	readme.md
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

# 暂存目录下所有文件
[root@dev myweb]# git add .
[root@dev myweb]# git status
# 位于分支 master
#
# 初始提交
#
# 要提交的变更:
#   (使用 "git rm --cached <file>..." 撤出暂存区)
#
#	新文件:    readme.md
#

# 提交文件
[root@dev myweb]# git commit -m "project init"

# 修改文件
[root@dev myweb]# echo '2nd line' >> readme.md 
[root@dev myweb]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#	修改:      readme.md
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"# 创建.gitignore来阻止某些文件加入到跟踪
[root@dev myweb]# echo '8-1 xxxx' > plan.txt
[root@dev myweb]# echo '8-2 aaaa' >> plan.txt 
[root@dev myweb]# vim .gitignore
plan.txt
*.swp
.gitignore
[root@dev myweb]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#	修改:      readme.md
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"# 丢弃工作区改动。慎用!!!它会将工作区所有更改清理掉。
[root@dev myweb]# git checkout -- readme.md
[root@dev myweb]# git status
# 位于分支 master
无文件要提交,干净的工作区
[root@dev myweb]# cat readme.md 
Hello World

# 技巧。如果发送的git目录太大,可以把工作区内容删除。接收方再恢复
# 发送方
[root@localhost ~]# cp -r nsd2020/ /tmp/
[root@localhost tmp]# du -sh nsd2020/
47M	nsd2020/
[root@localhost tmp]# cd nsd2020/
[root@localhost nsd2020]# ls
ebooks  nsd1912  nsd2003  README.en.md  README.md  review
[root@localhost nsd2020]# rm -rf *
[root@localhost nsd2020]# du -sh .
14M	.
# 接收方
[root@localhost nsd2020]# git checkout -- *
[root@localhost nsd2020]# ls
ebooks  nsd1912  nsd2003  README.en.md  README.md  review
[root@localhost nsd2020]# du -sh .
46M	.

# 撤出暂存区
[root@dev myweb]# echo '3rd line.' >> readme.md 
[root@dev myweb]# git add .
[root@dev myweb]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#	修改:      readme.md
#
####### 此时,发现readme.md没有修改完,需要继续修改#####
[root@dev myweb]# git reset HEAD readme.md
重置后撤出暂存区的变更:
M	readme.md
[root@dev myweb]# git status
# 位于分支 master
# 尚未暂存以备提交的变更:
#   (使用 "git add <file>..." 更新要提交的内容)
#   (使用 "git checkout -- <file>..." 丢弃工作区的改动)
#
#	修改:      readme.md
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a"[root@dev myweb]# cat readme.md   # 新增加的行仍然在
Hello World!
2nd line
3rd line.
[root@dev myweb]# echo '4th line.' >> readme.md 
[root@dev myweb]# git add .
[root@dev myweb]# git commit -m "add lines to readme.md"


# 在git操作中,没有单独的复制,因为复制就是在工作区增加了一个文件
# 移动/改名
[root@dev myweb]# git mv readme.md readme
[root@dev myweb]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#	重命名:    readme.md -> readme
#
[root@dev myweb]# git commit -m "modify readme.md => readme"

# 删除
[root@dev myweb]# git rm readme
rm 'readme'
[root@dev myweb]# ls
plan.txt
[root@dev myweb]# git status
# 位于分支 master
# 要提交的变更:
#   (使用 "git reset HEAD <file>..." 撤出暂存区)
#
#	删除:      readme
#
[root@dev myweb]# git commit -m "remove readme"

# 查看版本库的文件
[root@dev myweb]# git ls-files
# 查看所有的commit
[root@dev myweb]# git log
commit 0925d232fbd10ef34a01411eb6e6749e52c49341
Author: zhangzhg <zhangzg@tedu.cn>
Date:   Fri Jul 31 11:43:17 2020 +0800

    remove readme

commit bc2c22d43400579dba6dd222c8ced5dc56dd8e30
Author: zhangzhg <zhangzg@tedu.cn>
Date:   Fri Jul 31 11:42:22 2020 +0800

    modify readme.md => readme
... ...
# 返回到某一个时间点的快照
[root@dev myweb]# git checkout bc2c22d43400579dba6dd222c8ced5dc56dd8e30
root@dev myweb]# ls
plan.txt  readme

# 回到最新的master状态
[root@dev myweb]# git checkout master
之前的 HEAD 位置是 bc2c22d... modify readme.md => readme
切换到分支 'master'
[root@dev myweb]# ls
plan.txt

# 查看当前所在版本
[root@dev myweb]# git rev-parse HEAD
  • tag标记,可以将某一次提交打标记,以示本提交重要。通常用于将某次提交标记为一个版本
# 查看所有的tag
[root@dev myweb]# git tag
# 为现在的版本打标记为1.0
[root@dev myweb]# git tag 1.0
[root@dev myweb]# git tag
1.0

分支

  • 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。
# 查看分支
[root@dev myweb]# git branch 
* master
# 创建名为b1的分支
[root@dev myweb]# git branch b1
[root@dev myweb]# git branch 
  b1
* master  # 当前在哪个分支,前面有*标识
# 切换分支
[root@dev myweb]# git checkout b1
切换到分支 'b1'
[root@dev myweb]# git branch 
* b1
  master
# 在分支上提交代码
[root@dev myweb]# cp /etc/hosts .
[root@dev myweb]# git add .
[root@dev myweb]# git commit -m "add hosts"
[root@dev myweb]# ls
hosts  plan.txt

# 合并b1分支到master
[root@dev myweb]# git checkout master
切换到分支 'master'
[root@dev myweb]# ls
plan.txt
[root@dev myweb]# git merge b1 -m "merge b1 to master"
[root@dev myweb]# ls
hosts  plan.txt

gitlab

  • 安装
[root@gitserver ~]# cd docker_gitlab/
[root@gitserver docker_gitlab]# ls
docker_pkgs  images
[root@gitserver docker_gitlab]# yum install -y docker_pkgs/*rpm
[root@gitserver docker_gitlab]# systemctl start docker
[root@gitserver docker_gitlab]# systemctl enable docker
[root@gitserver docker_gitlab]# docker load -i images/gitlab_zh.tar 
[root@gitserver ~]# vim /etc/ssh/sshd_config 
Port 2022
[root@gitserver ~]# systemctl restart sshd
# 如果有客户端连接,需要客户端重新连接
[root@gitserver ~]# docker run -d -h gitlab --name gitlab -p 443:443 -p 80:80 -p 22:22 --restart always -v /srv/gitlab/config:/etc/gitlab -v /srv/gitlab/logs:/var/log/gitlab -v /srv/gitlab/data:/var/opt/gitlab gitlab_zh:latest 
[root@gitserver ~]# docker ps  # 直到状态成为healthy才可用
  • 访问gitlab:http://192.168.1.102/ 。第一次访问时,需要为root用户配置复杂密码。

  • 在首页上点击扳手图标,左边栏下面找到外观,可以修改登陆页面信息。

  • gitlab中重要的概念

    • 用户:为每个使用gitlab的人创建帐号
    • 组:一般对应一个开发团队,包括成员
    • 项目:对应开发项目。可以为个人或组创建项目。
  • 客户端推送代码到gitlab

[root@dev myweb]# git remote add origin http://192.168.1.102/devops/myweb.git
[root@dev myweb]# git push -u origin --all  # 推送所有分支
Username for 'http://192.168.1.102': zzg
Password for 'http://zzg@192.168.1.102': 
[root@dev myweb]# git push -u origin --tags  # 推送tag
Username for 'http://192.168.1.102': zzg
Password for 'http://zzg@192.168.1.102': 

# 配置ssh免密上传
[root@dev myweb]# ssh-keygen 
[root@dev myweb]# cat ~/.ssh/id_rsa.pub 
# 将查看到的公钥,粘贴到gitlab用户设置的ssh公钥中
# 重新绑定远程仓库
[root@dev myweb]# git remote -v
origin	http://192.168.1.102/devops/myweb.git (fetch)
origin	http://192.168.1.102/devops/myweb.git (push)
[root@dev myweb]# git remote remove origin
[root@dev myweb]# git remote -v
[root@dev myweb]# git remote add origin git@192.168.1.102:devops/myweb.git
# 上传测试
[root@dev myweb]# echo '<h1>Hello World</h1>' > index.html
[root@dev myweb]# git add .
[root@dev myweb]# git commit -m "add index.html"
[root@dev myweb]# git tag 2.0
[root@dev myweb]# git push -u origin --all
[root@dev myweb]# git push -u origin --tags

用到的软件包:https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/

https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/jenkins-2.235.1-1.1.noarch.rpm

jenkins

提交
克隆
下载
下载
下载
下载
程序员
git
jenkins
应用服务器
应用服务器
应用服务器
应用服务器
  • Jenkins是由java编写的一款开源软件

  • 作为一款非常流行的CI(持续集成)工具,用于构建和测试各种项目

  • Jenkins 的主要功能是监视重复工作的执行,例如软件工程的构建或在cron下设置的jobs

  • 持续集成(CI)是当下最为流行的应用程序开发实践方式

  • 程序员在代码仓库中集成了修复bug、新特性开发或是功能革新

  • CI工具通过自动构建和自动测试来验证结果。这可以检测到当前程序代码的问题,迅速提供反馈

  • 安装

[root@jenkins ~]# rpm -qa | grep java
java-1.8.0-openjdk-1.8.0.161-2.b14.el7.x86_64
[root@jenkins ~]# rpm -ihv jenkins-2.235.1-1.1.noarch.rpm 
[root@jenkins ~]# systemctl start jenkins
[root@jenkins ~]# systemctl enable jenkins
# 访问 http://x.x.x.x:8080
  • 在/var/lib/jenkins/secrets/initialAdminPassword取出密码 -> 选择插件来安装 -> 点击中间上面的“无”,不安装任何插件,点击右下角的“安装” -> 点击右下角的“使用admin继续” -> 点击“保存并完成” -> 点击"开始使用jenkins"
  • 进入jenkins首页后,改admin密码

右上角的"admin" -> configure -> password

  • 配置通过国内站点安装插件
[root@localhost ~]# ls /var/lib/jenkins/updates/default.json 
[root@localhost ~]# sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' /var/lib/jenkins/updates/default.json && sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' /var/lib/jenkins/updates/default.json
[root@localhost ~]# systemctl restart jenkins

如果/var/lib/jenkins/updates/default.json不存在,可以 Manage Jenkins -> Manage Plugins -> Advanced选项卡 -> Update site : https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json -> 点击submit提交。

插件
  • 安装插件

Manage Jenkins -> Manage Plugins -> Available选项卡,按ctrl+f进行搜索并勾选Git Parameter / Localization: Chinese(Simplified) / DingTalk / GitLab -> 点击Install without restart -> 勾选Restart Jenkins when installation is complete and no jobs are running

jenkins应用

  • 在jenkins服务器上安装git
[root@jenkins ~]# yum install -y git
  • 新建任务

首页 -> 新建Item -> myweb / Freestyle project -> 确定 -> 源码管理 => git / Repository URL: http://192.168.1.102/devops/myweb.git -> 保存

# 在jenkins服务器上查看代码目录
[root@localhost ~]# ls /var/lib/jenkins/workspace
ls: 无法访问/var/lib/jenkins/workspace: 没有那个文件或目录

点击项目页面左边栏的Build Now(立即构建) -> Build History 下面的#1 -> 左边栏的控制台输出

# 在jenkins服务器上查看代码目录
[root@localhost ~]# ls /var/lib/jenkins/workspace
myweb
配置机器人发送消息
  • 项目构建过程中,可以将整个构建过程通过机器人发送消息。

首页 -> Manage Jenkins -> Configure System -> 系统配置 -> 新增机器人 -> 输入名字、webhook地址以及关键字 -> 点击测试 -> 成功后保存

web登陆:https://im.dingtalk.com/

  • 修改myweb项目,用钉钉机器人发送构建过程

首页 -> 点击项目 - > 左边栏 配置 -> 勾选机器人 -> 保存

构建项目时,构建过程将会通过机器人发送消息

推送代码时自动构建项目
  • 修改jenkins项目配置 -> 构建触发器 -> 勾选Build when a change is pushed to GitLab. GitLab webhook URL: http://192.168.1.103:8080/project/myweb -> 点击 高级 -> 点击generate生成Secret token并复制它 -> 保存
  • 修改gitlab配置 -> 点击项目,如myweb -> 左边栏 设置 / 集成 -> 链接url http://192.168.1.103:8080/project/myweb / 安全令牌填写jenkins中生成的Secret token -> 在触发器选项中勾选标签推送事件 -> 点击增加web钩子。在页面中间部分找到创建的web钩子,点击test -> Tag push events测试,返回Hook executed successfully: HTTP 200表示成功。
  • 测试
# 在jenkins服务器上删除构建目录
[root@localhost ~]# rm -rf /var/lib/jenkins/workspace/*

# 程序员推送代码
[root@node2 myprojects]# cd myweb/
[root@node2 myweb]# echo 'new line.' >> index.html
[root@node2 myweb]# git add .
[root@node2 myweb]# git commit -m "modify index.html"
[root@node2 myweb]# git tag 2.1
[root@node2 myweb]# git push
[root@node2 myweb]# git push --tags

程序员推送代码到gitlab服务器后,jenkins项目将会自动构建,并通过机器人发送构建消息。

[root@localhost ~]# ls /var/lib/jenkins/workspace/
myweb

使用参数git parameter构建某一版本的代码

  • 配置jenkins通过tag标签构建

首页 -> 新建Item -> 名字myweb2 / Freestyle project -> 勾选This project is parameterized参数化构建 => 添加参数 => Git Parameter (Git参数) => Name: webver / Parameter Type: Branch or Tag / Default Value: origin/master -> 源码管理 => Git => Repositories => Repository url: http://192.168.1.102/devops/myweb.git / Branches to build: $webver -> 保存

  • 构建测试

点击 Build with Parameters -> 选择版本 -> 开始构建

检出代码到子目录
# 删除jenkins服务器上的代码目录
[root@localhost ~]# rm -rf /var/lib/jenkins/workspace/myweb2/

在jenkins的web页面上点击配置 -> 在源码管理下面找到Addtional Behaviours -> 新增 -> Checkout to a sub-directory(检出到子目录): myweb-$webver -> 保存

  • 构建测试:构建两次,每次使用不同版本

点击 Build with Parameters -> 选择版本 -> 开始构建

[root@localhost ~]# ls /var/lib/jenkins/workspace/myweb2/
myweb-1.0  myweb-2.0

修改项目,实现代码打包

  • jenkins服务器通过http协议共享打包后的软件
  • jenkins服务器发布当前软件版本和前一版本
  • 计算压缩包的md5值
# 在jenkins服务器上安装httpd
[root@localhost ~]# yum install -y httpd
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# systemctl enable httpd
# /var/www/html/deploy/live_ver:保存当前版本号
# /var/www/html/deploy/last_ver:保存前一版本的版本号
# /var/www/html/deploy/pkgs:保存软件的压缩包和它的md5值
[root@localhost ~]# mkdir -p /var/www/html/deploy/pkgs
[root@localhost ~]# chown -R jenkins:jenkins /var/www/html/deploy/
  • myweb2项目 -> 配置 -> 构建 -> 增加构建步骤 -> Execute shell
pkgs_dir=/var/www/html/deploy/pkgs
# 将下载目录拷贝到web服务器目录
cp -r myweb-$webver $pkgs_dir
cd $pkgs_dir
rm -rf myweb-$webver/.git  # 删除不必要的版本库文件
# 打包
tar czf myweb-$webver.tar.gz myweb-$webver
rm -rf myweb-$webver  # 删除软件目录
# 计算压缩包的md5值
md5sum myweb-$webver.tar.gz | awk '{print $1}' > myweb-$webver.tar.gz.md5
cd ..
# 将live_ver的版本号写到last_ver中
[ -f live_ver ] && cat live_ver > last_ver
echo -n $webver > live_ver  # 将最新版本号写入live_ver
  • 构建测试

点击 Build with Parameters -> 选择版本 -> 开始构建

部署软件到应用服务器

在web服务器上部署代码
  • 下载相应版本的软件包
  • 校验下载的软件包是否损坏
  • 解压软件包
  • 部署软件包到web服务器的文档目录
# 在应用服务器上执行以下操作
[root@localhost ~]# yum install -y httpd
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# systemctl enable httpd
[root@localhost ~]# mkdir /var/www/{download,deploy}
# /var/www/download: 用于保存下载的tar包
# /var/www/deploy: 用于保存解压后的目录和live_ver文件
# /var/www/html/nsd2005: web服务器软链接
  • 编写应用服务器自动上线代码
# vim deploy.py
import os
import requests
import wget
import hashlib
import tarfile


def has_new_ver(ver_url, ver_fname):
    "判断是否有新版本,有返回True,否则为False"
    # 本地不存在版本文件,则有新版本
    if not os.path.isfile(ver_fname):
        return True

    # 如果本地版本和网上版本不一样,则有新版本
    with open(ver_fname) as fobj:
        local_ver = fobj.read()

    r = requests.get(ver_url)
    if local_ver != r.text:
        return True
    else:
        return False


def file_ok(md5url, app_fname):
    "判断文件是否完好,完好返回True,否则为False"
    # 计算本地文件md5值
    m = hashlib.md5()
    with open(app_fname, 'rb') as fobj:
        while 1:
            data = fobj.read(4096)
            if not data:
                break
            m.update(data)
    r = requests.get(md5url)
    if m.hexdigest() == r.text.strip():  # 去除文件结尾的\n
        return True
    else:
        return False

def deploy(app_fname, deploy_dir, dest):
    "部署软件"
    # 解压
    tar = tarfile.open(app_fname)
    tar.extractall(path=deploy_dir)
    tar.close()

    # 拼接出解压文件的绝对路径
    app_dir = os.path.basename(app_fname)
    app_dir = app_dir.replace('.tar.gz', '')
    app_dir = os.path.join(deploy_dir, app_dir)

    # 如果软链接已存在,先删除它
    if os.path.exists(dest):
        os.remove(dest)

    # 创建软链接
    os.symlink(app_dir, dest)

if __name__ == '__main__':
    # 判断是否有新版本
    ver_url = 'http://192.168.1.103/deploy/live_ver'
    ver_fname = '/var/www/deploy/live_ver'
    if not has_new_ver(ver_url, ver_fname):
        print('未发现新版本')
        exit(1)

    # 下载新版本软件
    download_dir = '/var/www/download'
    r = requests.get(ver_url)
    app_url = 'http://192.168.1.103/deploy/pkgs/myweb-%s.tar.gz' % r.text
    wget.download(app_url, download_dir)

    # 判断文件是否完好,如果损坏则删除它
    md5url = app_url + '.md5'
    app_fname = app_url.split('/')[-1]
    app_fname = os.path.join(download_dir, app_fname)
    if not file_ok(md5url, app_fname):
        os.remove(app_fname)
        exit(2)

    # 部署软件
    deploy_dir = '/var/www/deploy'
    dest = '/var/www/html/nsd2005'
    deploy(app_fname, deploy_dir, dest)

    # 更新live_ver文件
    if os.path.exists(ver_fname):
        os.remove(ver_fname)
    wget.download(ver_url, ver_fname)
  • 测试
# 程序员修改代码并推送
[root@dev myweb]# vim index.html 
<h1>Hello World</h1>
new line.
hello tedu
<p>
<img src="http://n.sinaimg.cn/ent/4_img/upload/d411fbc6/58/w1024h1434/20200725/1b24-iwtqvym3422393.jpg">
</p>
[root@dev myweb]# git add .
[root@dev myweb]# git commit -m "modify index.html"
[root@dev myweb]# git tag 4.0
[root@dev myweb]# git push
[root@dev myweb]# git push --tags

# 构建jenkins项目
# 运行自动部署工具(脚本)
# 查看结果http://127.0.0.1/nsd2005/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值