Contents
持续集成与自动化部署
- 什么是持续集成?
Contiunous Integration (CI)
持续集成式一种软件开发实践,即团队开发成员经常集成她们呢的工作,通常每个成员至少集成一次,也就是意味着每天可能会发生多次的集成。每次集成都通过自动化的构建(包括编译、发布、自动化测试)来验证,从而尽快地发现集成错误,许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。
- 没有持续集成 -> 导致?
1.浪费大量的时间:项目做模块集成的时候,发现很多借口都不通。
2.构建过程不透明:需要人手动去便宜打包最新的代码。
3.脚本乱飞:发布代码,上线,基本靠手动。
- 持续集成的最佳实践
1.维护一个单一的代码库
2.使构建自动化
3.执行测试是构建的一部分
4.集成日志及历史记录
5.使用统一的依赖包管理库
6.每天集成一次
过去生产中存在的痛点
过去不智能不一体化的部署盘点:
- 1.纯手工SCP
- 2.纯手工登入git pull ,svn updata
- 3.纯手工xftp往上拉
- 4.开发给打包一个压缩包,rz上去,解压完成工程
缺点:
- 1.全程运维参与,占用大量时间
- 2.上线速度慢,一台一台上线
- 3.人为操作失误多
- 4.文件系统管理混乱
- 5.回滚慢,不及时,容易造成大量的损失
正常的应用上线环境的规划:
(1)开发环境-开发者本地有自己的环境,然后运维需要设置的开发环境大家共用服务。例如:开发数据库mysql,其他redis、memcached.
(2)测试环境:功能测试环境和性能测试环境。
(3)预生产环境:生产环境集群中的某一个节点担任。
(4)生产环境:直接对用户提供服务环境。
预生产环境产生的原因:
- 数据库不一致:测试环境和生产环境数据库肯定是不一样的。
-
使用生产环境的联调接口。例如,支付接口。
如何设计一套生产自动化部署系统
- 1.规划方案
-
2.实现部署
-
3.总结和扩展
-
4.实际生产环境中的运用
生产中的部署例子
-
1个集群中有10个节点
-
实现一键部署这个10个节点
-
一键回滚到任意版本
-
一键回滚到上个版本
1.部署:
- 代码放在哪?:svn、git
-
获取什么版本的代码?
- svn+git直接拉取某个分支
-
svn:指定版本号
-
git:指定tag
2.差异解决:
- 各个节点直接差异:配置文件未必一样:crontab.xml 预生产节点
-
代码仓库和实际的差异,配置文件是否放在仓库中。
-
config.snmple 配置文件只在部署身上有,单独的项目。
3.如何更新:
- MV走再移动来一个新的。(野蛮)
-
java tomcat等需要重启
4.测试
5.串行和并行
- 考虑是不是分组部署
6.如何执行:
- 1.shell ./执行
-
2.web界面 –例子:腾讯蓝鲸
生产中存在的安全问题:
- 开发不应该知道当前的版本号,运维应该来控制知道版本号
-
帐号的用户密码,应该运维来管理
-
master帐号密码应该主管来管理
-
如果涉及到支付的KEY问题,知道的人越少越好,权限的保密。
-
Web用户应该为普通用户,所有的WEB服务都不应该监听80,除了负载均衡
流程图
实际部署:
创建普通用户双机互信:
groupadd www
useradd -g www www
ssh-keygen -t rsa -P '' ##一路回车
ssh-copy-id -i ~/.ssh/id_rsa.pub root@IP
ssh IP 'ifconfig' ##测试是否连通
回滚流程图
整体代码:
#!/bin/bash
# author=ddy4633
# Node List
PRE_LIST="192.168.175.192"
GROUP_LIST="192.168.175.193"
ROLLBACK_LIST="192.168.175.192 192.168.175.193"
# Date Env
TIME=(date "+%Y-%m-%d-%H-%M-%S") # tar 中出现 ":"会报错 DATE=(date "+%Y-%m-%d-%T")
# SHell Env
SHELL_DIR='/home/www/'
SHELL_NAME='deploy.sh'
SHELL_LOG="{SHELL_DIR}/{SHELL_NAME}.log"
# Code Env
PRO_NAME="web-demo"
CODE_DIR="/deploy/code/web-demo"
CONFIG_DIR="/deploy/config/web-demo"
TMP_DIR='/deploy/tmp'
TAR_dir='/deploy/tar'
LOCK_FILE='/tmp/deploy.lock'
usage(){
echo "Usage: 0 { deploy | rollback [ list | version] }"
}
write_log(){
LOGINFO=1 echo "{DATE} : {SHELL_NAME} : {LOGINFO}" >> {SHELL_LOG} } url_test(){ URL=1
curl -s --head "URL" | grep "200" if [ ? -ne 0 ];then
shell_unlock;
write_log "err : URL Rturn=false && exit" fi write_log "Text: URL --Return=Success"
echo "URL curl is ok" } shell_lock(){ touch {LOCK_FILE}
}
shell_unlock(){
rm -rf {LOCK_FILE} } code_get(){ write_log "code_get"; cd CODE_DIR && echo "git pull"
cp -r {CODE_DIR} {TMP_DIR}/
API_VER="V1.1"
}
code_java_build(){
echo 'code_java_build'
}
code_config(){
write_log "code_config"
/bin/cp -r {CONFIG_DIR}/base/* {TMP_DIR}/"{PRO_NAME}" PKG_NAME="{PRO_NAME}"_"API_VER"_"{TIME}"
cd {TMP_DIR} && mv {PRO_NAME} {PKG_NAME} } code_tar(){ write_log "code_tar" cd {TMP_DIR} && tar -czf {PKG_NAME}.tar.gz {PKG_NAME} && write_log "tar czf {PKG_NAME}.tar.gz" } code_scp(){ for node in PRE_LIST;do
scp {TMP_DIR}/{PKG_NAME}.tar.gz node:/opt/webroot/ # 保证能有www使用权限 write_log "code_scp node complate!"
done
for node in GROUP_LIST;do scp {TMP_DIR}/{PKG_NAME}.tar.gz node:/opt/webroot/ # 保证能有www使用权限
write_log "code_scp node complate!" done } pre_deploy(){ write_log "cluster_node_remove" for node in PRE_LIST;do
ssh node "cd /opt/webroot && tar zxf {PKG_NAME}.tar.gz"
# 自动化部署的精髓软连接
ssh node "rm -rf /webroot/web-demo && ln -s /opt/webroot/{PKG_NAME} /webroot/web-demo"
write_log "node-pre complate!"
done
scp {CONFIG_DIR}/other/192.168.175.192.crontab.xml PRE_LIST:/opt/webroot/{PKG_NAME}/contab.xml if [ ? -ne 0 ];then
shell_unlock;
write_log "Err: scp err !!!"
fi
url_test "http://{PRE_LIST}/index.html" write_log "OK: pre_deploy addd to cluster seccess!" } group_deploy(){ write_log "cluster_node_remove" for node in GROUP_LIST;do
ssh node "cd /opt/webroot && tar zxf {PKG_NAME}.tar.gz"
# 自动化部署的精髓软连接
ssh node "rm -rf /webroot/web-demo && ln -s /opt/webroot/{PKG_NAME} /webroot/web-demo"
write_log "node complate!"
done
url_test "http://{GROUP_LIST}/index.html" write_log "OK: pre_deploy addd to cluster seccess!" } config_scp(){ echo 'config_scp' } cluster_node_in(){ echo 'cluster_node_in' } rollback_fun(){ for node in ROLLBACK_LIST;do
# if [ -d /opt/webroot/1];then ssh node "rm -f /webroot/web-demo && ln -s /opt/webroot/1 /webroot/web-demo" # fi done } rollback(){ if [ -z 1 ];then
shell_unlock;
echo "Err:Please input robblback version!" && exit;
fi
case 1 in list) ls -l /deploy/tmp/*.tar.gz ;; *) rollback_fun 1
esac
}
main(){
if [ -f LOCK_FILE ];then echo "Deploy is running" && exit; fi DEPLOY_METHOD=1
ROLLBACK_VER=2 case DEPLOY_METHOD in
deploy)
shell_lock;
code_get;
code_java_build;
code_config;
code_tar;
code_scp;
pre_deploy;
group_deploy;
config_scp;
url_test;
cluster_node_in;
shell_unlock;
;;
rollback)
shell_lock;
rollback ROLLBACK_VER; shell_unlock; ;; *) usage; esac } main 1 $2
Git的介绍
GIT的生态
- Git 分布式版本管理系统
-
Gitlab git 私库解决方案
-
Gitlab git 公有库解决方案
-
1.GIT基础
-
2.分支管理
-
3.GIT高级
-
4.远程管理
Git的原理
- 4个区域
- 4个状态
版本控制器的发展
常用命令
基本命令名称 | 相关作用 |
---|---|
git add | 加入暂存(索引区) |
git status | 查看状态 |
git status -s | 状态概览 |
git diff | 尚未暂存的文件 |
git diff –staged | 暂存区文件 |
git commit | 提交更新 |
git reset | 回滚 |
git rm | 从版本库中移除 |
git rm –cached Readme | 从暂存区中移除 |
git mv | 相当于MV git rm git add 三个 命令 |
简单的Git编译安装
官方那个地址:https://github.com/git/git/
环境安装
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils-MakeMaker
下载编译
wget https://github.com/git/git/archive/v2.7.4.zip
unzip v2.7.4.zip
cd git-2.7.4
make prefix=/usr/local/git all
make prefix=/usr/local/git install
rm -rf /usr/bin/git
ln -s /usr/local/git/bin/git /usr/bin/git
git --version
Git初始化
git init
git config --global user.name "git账号"
git config --global user.email 邮箱地址
git config --list
Git基础操作
Git高级知识
- git chechout 用于切换分支
Checkout一个文件和带文件路径Git reset非常像,除了它更改的是工作目录而不是缓存区。不像提交层面的checkout命令,它不会移动HEAD引用,也就是你不会切换到别的分支上去。
如果你缓存并且提交了checkout的文件,它具备将某个文件回撤到之前的版本的效果。注意它撤销了这个文件后面所有的更改,而Git revert命令只是撤销某一个特定提交的更改。
– git check –file.txt 撤销对文件的修改
- git reset 回滚
- –soft 缓存区和工作目录都不会被改变
-
–mixed 默认选项,缓存区和你指定的提交同步但是工作目录不受到影响
-
–hard 缓存区和工作目录都同步到你指定的提交
文件层操作
- 当检测到文件路径时,Git reset将存储区同步到你指定的目录中去。
-
git reset HEAD~2 文件名
-
运行git reset
Git远程管理
原理图
github上clone自己的项目
git clone 自己项目地址
gitlab私有库部署和回滚
- 什么是Gitlab
GitLab是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。
GitLab拥有与Github类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。
- 一个基于Git的源码托管解决方案
-
基于Ruby on rails 开发
-
集成了Nginx postgreSQL redis sidekiq等组件
Gitlab的组件
- Nginx:静态Web服务器
-
gitlab-shell:用于处理Git命令和修改authorized keys列表
-
gitlab-workhorse:轻量级的反向代理服务器
-
logrotate:日志文件管理工具
-
postgresql:数据库
-
redis:缓存数据库
-
sidekiq:用于在后台执行队列任务(异步执行)
-
unicorn:GitLab Rails应用是托管在这个服务器上面的
Gitlab相关目录
- /var/opt/gitlab/git-data/repositories/ -> 默认存储目录
-
/var/opt/gitlab -> gitlab-ctl reconfigure命令编译后的应用数据和配置文件,不需要人为的去修改配置
-
/etc/gitlab -> 配置文件目录
-
/var/log/gitlab -> 此目录下存放了Gitlab各个组件产生的日志
-
/var/opt/gitlab/backups/ -> 备份文件生成的目录
变更主配置文件
- gitlab-ctl reconfgure 重置配置文件
-
gitlab-ctl show-config 验证配置文件
-
gitlab-ctl restart 重启服务
1.配置的基础环境部署
yum install curl policycoreutils openssh-server openssh-clients postfix
systemctl start postfix
2.配置国内的YUM源
vim /etc/yum.repos.d/gitlab-ce.repo
[gitlab-ce]
name=gitlab-ce
baseurl=https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/
repo_gpgcheck=0
gpgcheck=0
enabled=1
gpgkey=https://packages.gitlab.com/gpg.key
yum makecahe
yum -y install gitlab-ce
3.配置启动Gitlab-ce
vim /etc/gitlab/gitlab.rb
external_url 'http://IP:port'
gitlab-ctl reconfigure
如果使用的是虚拟机把内存分配为2G左右最好,否则占用wap分区。
4.访问gitlab
curl http://IP:port
第一次安装需要修改默认的root密码
5.本地不需要注册所以关闭注册功能
6.上传密钥实现SSH克隆:
ssh-keygen -t dsa -f /root/.ssh/id_dsa -P "" #非交互式生成密钥
需要的机器上 ssh-keygen 生成密钥
cat .ssh/id_rsa.pub 公钥
复制到Web上保存下来
当你使用SSH克隆时候如果提示需要输入密码则表示你需要配置密钥了,否则永远无法克隆下来!
7.创建用户组
8.创建用户
用户名 | 职责 |
---|---|
cto | 不解释BOSS,管理者 |
dev01 | 开发玩家一,linux |
dev02 | 开发玩家二,windos |
9.创建项目
克隆或者上传文件
cd /deploy/code && rm -rf web-demo
git clone git@192.168.191.161:root/my_python.git #克隆
使用windos连接Gitlab
- 下载安装包
-
除了以下的选项其他的统一默认下一步
- 在你磁盘上创建一个新的文件夹
-
进入你创建的目录,单击右键git bash here
- 创建密钥,存放在:C:\Users\Administrator.ssh
- 使用新建的开发者用户登入gitlab,授权project并重复之前的条件KEY
Gitlab备份与恢复
- 配置文件中加入
gitlab_rails['backup_path'] = "/var/opt/gitlab/backups" #备份的路径
gitlab_rails['backup_archive_permissions'] = 0644 #权限
- 如果自定义备份目录需要富裕git权限
- mkdir /data/backup/gitlab
-
chown -R git.git /data/backup/gitlab
-
定时任务Crontab中计入
0 2 * * * /usr/bin/gitlab-rake gitlab:backup:create
- 策略建议:本地保留3天、在异地备份永久保留
重载配置
gitlab-ctl reconfigure
gitlab-ctl restart
gitlab-rake gitlab:backup:restore BACKUP=备份的时间戳 #执行数据的恢复
gitlab-ctl restart #等待30秒后恢复数据
数据的恢复
- 停止数据的写入服务
-
gitlab-ctl stop unicorn
-
gitlab-ctl stop sidekiq
配置Gitlab邮件通知
-
需要在配置文件中开启所有的选项
-
合并代码工作任务自动邮箱通知
gitlab_rails['time_zone'] = 'Asia/Shanghai'
gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = '发件人@xxx.com'
gitlab_rails['gitlab_email_display_name'] = 'gitlab'
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.126.com"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "ddy"
gitlab_rails['smtp_password'] = "your_password"
gitlab_rails['smtp_domain'] = "126.com"
gitlab_rails['smtp_authentication'] = "login"
项目部署
某项目的规划
需求 | 开发人员 | 完成时间 |
---|---|---|
首页 | dev01 | xx.xx |
新闻 | dev01 | xx.xx |
支付 | dev02 | xx.xx |
关于 | dev02 | xx.xx |
cto定制开发计划
- cto项目经理定制项目任务
- 构建项目计划表
- 发布新的任务
- 分配所有的任务
什么是jenkins
- 什么是JenKINS?
持续集成、自动测试、持续部署的超级引擎,支持自定义工具集、多种交付通道。
- jenkins存在的坑
jenkins卡启动,机器连接网络出现问题,不断的连接官方网站,关闭网络,离线安装模式。
jenkins的安装及其部署
1.环境的初始化
- 1.1 iptables、firewalld、selinux、NetworkManage 四件套干掉
-
1.2 Jenkins运行在JAVA环境下所以jdk
-
1.3 yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel
-
1.4 上清华的开源镜像网
- https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat/ # jenkins源可以YUM安装也可以RPM
- 1.5 清华的centos-7-epel安装方法
- https://mirrors.tuna.tsinghua.edu.cn/help/centos/
- 1.6 yum安装Jenkins启动,8080开放就说明开启成功
-
1.7 登入ip:8080
-
1.8 cat /var/lib/jenkins/secrets/initialAdminPassword # 查看jenkins的初始密码
Jenkins的目录
- /var/lib/jenkins 主目录
- /etc/init.d/jenkins 启动文件
- /var/cache/jenkins 程序文件
- /var/log/jenkins 日志文件
jenkins的升级
systemctl stop jenkins
cd /usr/lib/jenkins
mv 老版本 老版本.back # 备份一下
如图所示:
2.网页的安装
Jenkins插件plugins
手动下载插件:https://plugins.jenkins.io/
rz plugins.tar.gz
tar xf mv plugins.tar.gz && plugins /var/lib/jenkins/plugins && chown -R jenkins:jenkins /var/lib/jenkins/plugins
freetype job
#!/bin/sh
export PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
#Print env variable
echo "[INOF] --> Print env variable"
echo "Current deployment envrionment is Deployment_env" >> test.properties echo "The build is Version" >> test.properties
echo "[INOF] --> Done ...."
#Check test properties
echo "[INOF]Check test properties"
if [ -s test.properties ]
then
cat test.properties
echo "[INFO] --> Done ...."
else
echo "test.properties is empty!"
fi
echo "[INOF] Build finished !!!"
pipeline基础架构
- 所有的代码包裹在pipeline{}层内
-
stages{}层用来包含该pipeline所有stage子层
-
stage{}层用来包含具体的我们需要的编写任务的steps{}层
-
steps{}层用来添加具体需要调用的模块语句
pipeline Job编写规范
agnet区域
agent定义:pipeline在哪里运行可以使用any,none或具体的jenkins node主机名等
例如:agent{node{label 'node1'}}
environment区域
(1)变量名称=变量值 #定义环境变量
(2)可以定义全局环境变量,应用所有stages任务
(3)可以定义stage环境变量,应用单独的stage任务
script区域
-
groovy脚本语言
-
可以编写脚本逻辑运算
-
steps区域
(1)echo:打印输出
(2)sh:调用Linux系统的shell命令
(3)git url:调用git模块进行git相关的操作
构建配置过程
#!groovy
pipeline {
agent {node {label 'master'}}
environment {
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"
}
parameters {
choice(
choices: 'dev\nprod',
description: 'choose deployment environment',
name: 'Deployment_env'
)
string(name: 'Version', defaultValue: '1.0.0', description: 'Build Version !!!')
}
stages {
stage("Checkout Test Repo") {
steps{
sh 'git config --global http.sslVerify false'
dir ("{env.WORKSPACE}") { git branch: 'master', credentialsId:"2e3b5f3a-12e1-45ad-9257-6b216048b1e0",url: 'http://192.168.42.145:3000/root/ddy.git' } } } stage("Print env variable") { steps { dir ("{env.WORKSPACE}") {
sh """
echo "[Begin] -------------------------"
echo "[Title] ---> Print env variable "
echo "[Event] --> Current deployment environment is Deployment_env" >> test.properties echo "[Event] --> The Build is Version" >> test.properties
echo "[INFO] ---> Done ......."
echo "[END] --------------------------"
"""
}
}
}
stage("Check Test Properties") {
steps {
dir ("${env.WORKSPACE}") {
sh """
echo "[Begin] -------------------------"
echo "[Title] ---> Check Test Properties"
if [ -s test.properties ]
then
cat test.properties
echo "[INFO] --> Done ......"
else
echo "test.properties is empy"
fi
echo "[INFO] Build finished ..."
"""
}
}
}
}
}
jenkins应用
#!/bin/sh
echo "--------------欢迎使用自动化构建系统----------------"
echo "The Current user is 【 `whoami` 】"
echo "The Current Build Environment is 【 deployment_env 】" echo "The Current Version is 【 version 】"
echo "The Current Password is 【 password 】" if bool
then
echo "Request is approved -> ok"
else
echo "Request is rejected -> err"
fi
echo "--------------end-----------------"
jenkins集成演示环节