DevOps介绍
软件开发最开始是由两个团队组成:
- 开发计划由开发团队从头开始设计和整体系统的构建。需要系统不停的迭代更新。
- 运维团队将开发团队的Code进行测试后部署上线。希望系统稳定安全运行。
这看似两个目标不同的团队需要协同完成一个软件的开发。
在开发团队指定好计划并完成coding后,需要提供到运维团队。
运维团队向开发团队反馈需要修复的BUG以及一些需要返工的任务。
这时开发团队需要经常等待运维团队的反馈。这无疑延长了事件并推迟了整个软件开发的周期。
会有一种方式,在开发团队等待的时候,让开发团队转移到下一个项目中。等待运维团队为之前的代码提供反馈。
可是这样就意味着一个完整的项目需要一个更长的周期才可以开发出最终代码。
基于现在的互联网现状,更推崇敏捷式开发,这样就导致项目的迭代速度更快,但是由于开发团队与运维团队的沟通问题,会导致新版本上线的时间成本很高。这又违背的敏捷式开发的最初的目的。
那么如果让开发团队和运维团队整合到成一个团队,协同应对一套软件呢?这就被称为DevOps。
DevOps,字面意思是Development &Operations的缩写,也就是开发&运维。
虽然字面意思只涉及到了开发团队和运维团队,其实QA测试团队也是参与其中的。
网上可以查看到DevOps的符号类似于一个无穷大的符号
这表明DevOps是一个不断提高效率并且持续不断工作的过程
DevOps的方式可以让公司能够更快地应对更新和市场发展变化,开发可以快速交付,部署也更加稳定。
核心就在于简化Dev和Ops团队之间的流程,使整体软件开发过程更快速。
整体的软件开发流程包括:
PLAN
:开发团队根据客户的目标制定开发计划CODE
:根据PLAN开始编码过程,需要将不同版本的代码存储在一个库中。BUILD
:编码完成后,需要将代码构建并且运行。TEST
:成功构建项目后,需要测试代码是否存在BUG或错误。DEPLOY
:代码经过手动测试和自动化测试后,认定代码已经准备好部署并且交给运维团队。OPERATE
:运维团队将代码部署到生产环境中。MONITOR
:项目部署上线后,需要持续的监控产品。INTEGRATE
:然后将监控阶段收到的反馈发送回PLAN阶段,整体反复的流程就是DevOps的核心,即持续集成、持续部署。
为了保证整体流程可以高效的完成,各个阶段都有比较常见的工具,如下图:
最终可以给DevOps下一个定义:DevOps 强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。
自动化的工具协作和沟通来完成软件的生命周期管理
直入主题
基础环境信息:
- centos:7.9.2009 (Core)
- docker:24.0.2
- docker-compose: v2.18.0
GitLab
在code阶段,我们需要将不同版本的代码存储到一个仓库中,常见的版本控制工具就是SVN或者Git,这里我们采用Git作为版本控制工具,GitLab作为远程仓库。
单独准备服务器,采用Docker安装
-
查看GitLab镜像
docker search gitlab
-
拉取GitLab镜像
docker pull gitlab/gitlab-ce
-
准备docker-compose.yml文件
version: '3.1' services: gitlab: image: 'gitlab/gitlab-ce:latest' container_name: gitlab restart: always environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://192.168.1.190:8929' gitlab_rails['gitlab_shell_ssh_port'] = 2224 ports: - '8929:8929' - '2224:2224' volumes: - './config:/etc/gitlab' - './logs:/var/log/gitlab' - './data:/var/opt/gitlab'
-
启动容器
docker-compose up -d
-
访问GitLab首页
-
查看root用户初始密码
docker exec -it gitlab cat /etc/gitlab/initial_root_password
-
登录root用户
Jenkins
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具
Jenkins应用广泛,大多数互联网公司都采用Jenkins配合GitLab、Docker、K8s作为实现DevOps的核心工具。
Jenkins最强大的就在于插件,Jenkins官方提供了大量的插件库,来自动化CI/CD过程中的各种琐碎功能。
Jenkins最主要的工作就是将GitLab上可以构建的工程代码拉取并且进行构建,再根据流程可以选择发布到测试环境或是生产环境。
一般是GitLab上的代码经过大量的测试后,确定发行版本,再发布到生产环境。
CI/CD可以理解为:
- CI过程即是通过Jenkins将代码拉取、构建、制作镜像交给测试人员测试。
- 持续集成:让软件代码可以持续的集成到主干上,并自动构建和测试。
- CD过程即是通过Jenkins将打好标签的发行版本代码拉取、构建、制作镜像交给运维人员部署。
- 持续交付:让经过持续集成的代码可以进行手动部署。
- 持续部署:让可以持续交付的代码随时随地的自动化部署。
安装Jenkins
-
docker-compose.yml
注意挂载数据卷,且data目录赋予足够的权限version: "3.1" services: jenkins: image: jenkins/jenkins:2.401.1-lts container_name: jenkins ports: - 8080:8080 - 50000:50000 volumes: - ./data/:/var/jenkins_home/
-
启动Jenkins容器后,由于Jenkins需要下载大量内容,可以设置国内镜像站提高下载速度。
<!-- 修改数据卷中的hudson.model.UpdateCenter.xml文件 --> <?xml version='1.1' encoding='UTF-8'?> <sites> <site> <id>default</id> <url>https://updates.jenkins.io/update-center.json</url> </site> </sites> <!-- 将下载地址替换为http://mirror.esuni.jp/jenkins/updates/update-center.json --> <?xml version='1.1' encoding='UTF-8'?> <sites> <site> <id>default</id> <url>http://mirror.esuni.jp/jenkins/updates/update-center.json</url> </site> </sites> <!-- 清华大学的插件源也可以https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json -->
-
访问Jenkins
输入密码,在日志中得到体现。
-
下载插件
下载失败的话也可以去官网上去下载插件,手动安装:友情链接 -
下载完毕设置信息进入首页
设置实例
正式进入首页
-
下载需要的插件
查看插件安装情况
配置(Maven&JDK)
代码拉取到Jenkins本地后,需要在Jenkins中对代码进行构建,这里需要Maven的环境,而Maven需要Java的环境,接下来需要在Jenkins中安装JDK和Maven,并且配置到Jenkins服务。
构建Java项目的工具一般有两种选择,一个是Maven,一个是Gradle。
这里我们选择Maven作为项目的编译工具。
注意修改Maven的仓库地址,以免后期打包依赖下载不了。
-
将包并放入映射卷
-
在jenkins中配置JDK和Maven
Publish Over SSH
程序包构建好之后,就可以根据情况发布环境,配置之前下载好的插件Publish Over SSH。
SSH配置
- 配置连接相关环境
- 按照提示填写相关信息
项目准备
- 搭建个简单的项目
- Gitlab创建相关项目并将本地项目推送
CI:持续集成
拉取代码并打包
- Jenkins创建任务
- 简单入门选择自由风格软件项目
- 配置源码管理为git并填写仓库地址
- 配置构建环境
- 选择Build Steps配置maven
- 选择配置好的maven并配置打包命令
- 尝试打包,观看相关日志
- 进入Jenkins工作目录查看Jar包
发布到目标服务器
-
项目准备dockerfile等并推送gitlab
- dockerfile
FROM java:openjdk-8u111 COPY *.jar /usr/local/test.jar WORKDIR /usr/local/ ENTRYPOINT ["/bin/sh","-c","java -Dfile.encoding=utf8 -Djava.security.egd=file:/dev/./urandom -jar test.jar"]
- docker-compose.yml
version: "3" services: my_test: build: context: ./ dockerfile: dockerfile image: test:v1 container_name: my_test ports: - "9090:9090"
- 目录结构
- dockerfile
-
追加Jenkins构建后操作脚本命令
cd /usr/local/test/docekr mv ../target/*.jar ./ docker-compose down docker-compose up -d --build
-
Jenkins尝试构建并发布
哈哈哈失败好几次,反正就是来回调试就完事了
查看#16
控制台日志
-
查看容器
-
接口验证
CD:持续交付
程序代码在经过多次集成操作到达最终可以交付,持续交付整体流程和持续集成类似,不过需要选取指定的发行版本。
- 确定Jenkins中有Git Parameter插件
- 设置项目参数化构建
- 修改Build配置并保存
- 修改项目并推送
- Gitlab添加
tag
版本
- 尝试重新构建
- 查看日志并测试程序
阶段总结
搭建的地方有两个坑,集中在配置maven和jdk两个地方,在这里列举一下,免得走弯路。
- jdk方面:因为探索阶段都是在论坛上东搞搞西搞搞看看借鉴下来的,大家都说要安装jdk,但是我后面Jenkins里面配置全局工具的时候,发现jenkins内置了Java11,所以我就直接使用了内置的。
- maven方面:Jenkins搭建的时候使用了数据卷挂载,包括解压maven到数据卷中,从上图可以看到,Jenkins容器内使用的是jenkins用户,而挂载进去的maven目录的用户和用户组为root,配置的时候并不报错,代码构建的时候报出了
Cannot run program "var/jenkins_home/maven/bin/mvn" (in directory "/var/jenkins_home/workspace/mytest"): error=2, No such file or directory
的错误。解决方案如下:设置之后配置正常。- 方案1: 在宿主机将挂载的目录用户和用户组设置为 1000(容器内jenkins所属与1000:1000)
- 方案2:容器以root用户启动(不推荐)
截至此段落位置,DevOps 简单入门版整体结构如下:
SonarQube 集成
Sonar Qube是一个开源的代码分析平台,支持Java、Python、PHP、JavaScript、CSS等25种以上的语言,可以检测出重复代码、代码漏洞、代码规范和安全性漏洞的问题。
Sonar Qube可以与多种软件整合进行代码扫描,比如Maven,Gradle,Git,Jenkins等,并且会将代码检测结果推送回Sonar Qube并且在系统提供的UI界面上显示出来
最新长期支持版本9.9.1
本文使用8.9.3-community
,最新版下载太慢了。
ps:
Sonar Qube在7.9版本中已经放弃了对MySQL的支持,并且建议在商业环境中采用PostgreSQL,那么安装Sonar Qube时需要依赖PostgreSQL。
SonarQube 安装
-
拉取镜像
docker pull postgres docker pull sonarqube:8.9.3-community
-
编写docker-compoe.yml
version: "3.1" services: db: image: postgres container_name: db ports: - 5432:5432 networks: - sonarnet environment: POSTGRES_USER: sonar POSTGRES_PASSWORD: sonar sonarqube: image: sonarqube:8.9.3-community container_name: sonarqube depends_on: - db ports: - "9000:9000" networks: - sonarnet environment: SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar SONAR_JDBC_USERNAME: sonar SONAR_JDBC_PASSWORD: sonar networks: sonarnet: driver: bridge
-
启动容器
docker-compose up -d
-
日志报错记录
2023.06.13 03:19:42 INFO es[][o.e.n.Node] initialized 2023.06.13 03:19:42 INFO es[][o.e.n.Node] starting ... 2023.06.13 03:19:42 INFO es[][o.e.t.TransportService] publish_address {127.0.0.1:40954}, bound_addresses {127.0.0.1:40954} 2023.06.13 03:19:42 INFO es[][o.e.b.BootstrapChecks] explicitly enforcing bootstrap checks ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch. bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ERROR: Elasticsearch did not exit normally - check the logs at /opt/sonarqube/logs/sonarqube.log 2023.06.13 03:19:42 INFO es[][o.e.n.Node] stopping ... 2023.06.13 03:19:42 INFO es[][o.e.n.Node] stopped 2023.06.13 03:19:42 INFO es[][o.e.n.Node] closing ... 2023.06.13 03:19:42 INFO es[][o.e.n.Node] closed 2023.06.13 03:19:42 WARN app[][o.s.a.p.AbstractManagedProcess] Process exited with exit value [es]: 78 2023.06.13 03:19:42 INFO app[][o.s.a.SchedulerImpl] Process[es] is stopped 2023.06.13 03:19:42 INFO app[][o.s.a.SchedulerImpl] SonarQube is stopped
-
按照日志要求修改虚拟内存
执行命令刷新
sysctl -p
-
重启服务观察日志
-
访问Sonar Qube首页,初始账户密码都为admin
-
第一次登陆后要求修改密码,然后进入首页
-
安装中文插件
-
成功后重启刷新喜提中文
SonarQube 代码分析
可以选择 maven 集成或者使用 sonar-scanner
进行代码质量分析。
集成 Mavne 方式
- 修改Maven的settings.xml文件配置SonarQube配置
<profile> <id>sonar</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <sonar.login>admin</sonar.login> <sonar.password>你的密码</sonar.password> <sonar.host.url>http://192.168.1.190:9000</sonar.host.url> </properties> </profile>
- 在代码目录下执行相关命令:
mvn sonar:sonar
等待执行完毕
- SonarQube主页查看结果
sonar-scanner 方式
-
下载 Sonar-scanner,为匹配SonarQube服务版本,本文选择下载4.6.x Linux版本版本。
-
上传到服务器并解压缩
unzip sonar-scanner-cli-4.6.2.2472-linux.zip mv sonar-scanner-4.6.2.2472-linux sonar-scanner
-
配置sonarQube服务端地址,修改conf目录下的
sonar-scanner.properties
-
获取SonarQube登录者令牌(Token)
-
测试使用sonar-scanner检测代码
在项目目录下执行指令~/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=test -Dsonar.login=a29d38cf824b24d9e495e4a714879deb10ced30e -Dsonar.projectKey=linux-scanner -Dsonar.java.binaries=target/
Dsonar.sources
:扫描资源目录
Dsonar.projectname
:指定项目的名称
Dsonar.projectKey
:项目键,唯一标识符
Dsonar.login
:设置账户token
Dsonar.java.binaries
:指定编译生成的 Java 二进制文件的位置 -
查看结果
Jenkins 集成
Jenkins继承SonarQube实现代码扫描需要先下载整合插件:SonarQube Scanner
-
将解压后的sonar-scanner目录移入Jenkins存储卷
注意将目录的用户和用户组设置为 1000
-
插件商店进行安装
-
开启SonarQube权限验证
-
配置Jenkins的SonarQube信息
-
配置Jenkins全局工具之SonarQube Scanner
-
修改任务,配置Sonar-scanner
-
重新构建测试并观察控制台日志
-
查看SonarQube控制台
-
接口访问验证
阶段总结
截止本段落位置,各架构关系图为
Harbor 集成
懂得都懂,不做介绍。
Harbor 搭建
下载相关资源:🖱️友情链接
-
拖拽到Linux并解压:
tar -zxvf harbor-offline-installer-v2.3.4.tgz -C /usr/local/
-
修改Harbor配置文件:
-
首先复制一份harbor.yml配置
cp harbor.yml.tmpl harbor.yml
-
编辑harbor.yml配置文件
-
-
启动
./install.sh
-
运行命令后可以发现 Harbor 也是以docker容器的形式启动的
-
访问Harbor并登录
-
创建测试项目
-
修改
daemon.json
,定义私库地址,重启Docker让配置生效
-
尝试推送镜像并查看仓库
Jenkins集成
构建镜像和发布镜像到harbor都需要使用到docker命令。设置Jenkins容器内部直接采用宿主机的Docker,节省点资源。当然,Jenkins里也可以安装docker。
Jenkins docker
- 设置宿主机docker.sock权限:
chown root:root /var/run/docker.sock chmod o+rw /var/run/docker.sock
- Jenkins 添加数据卷
version: "3.1" services: jenkins: image: jenkins/jenkins:2.401.1-lts container_name: jenkins ports: - 8080:8080 - 50000:50000 volumes: - ./data/:/var/jenkins_home/ - /usr/bin/docker:/usr/bin/docker - /var/run/docker.sock:/var/run/docker.sock - /etc/docker/daemon.json:/etc/docker/daemon.json
- 重启Jenkins并验证docker
shell脚本 - 镜像 - 推送
-
修改Jenkins任务
- 修改配置:原构建后操作删除(不需要传输了,走Harbor仓库)
- 新增构建后操作,执行shell(制作镜像并推送)
- 修改配置:原构建后操作删除(不需要传输了,走Harbor仓库)
-
测试任务
- Gitlab新的Tag
- Jenkins任务构建并推送
- 报错了:要以非交互方式运行该命令,防止密码最历史记录或日志文件中
- 报错了:要以非交互方式运行该命令,防止密码最历史记录或日志文件中
- shell命令再次修改(骂骂咧咧)
- 在Jenkins容器用户目录下生成保存密码的文件
- 修改shell命令
mv target/*.jar docker/ docker build -t 192.168.1.190:80/mytest/test:$tag docker/ docker login --username admin --password-stdin < ~/my_password.txt 192.168.1.190:80 docker push 192.168.1.190:80/mytest/test:$tag
- 在Jenkins容器用户目录下生成保存密码的文件
- Gitlab新的Tag
-
再次尝试构建任务(成功)
部署到目标服务器
部署项目需要通过Publish Over SSH插件,让目标服务器执行命令。
为了方便一次性实现拉取镜像和启动的命令,推荐采用脚本文件的方式。
添加脚本文件到目标服务器,再通过Publish Over SSH插件让目标服务器执行脚本即可。
- 目标服务器脚本文件准备
harbor_url=$1 harbor_project_name=$2 project_name=$3 tag=$4 port=$5 imageName=$harbor_url/$harbor_project_name/$project_name:$tag containerId=`docker ps -a | grep ${project_name} | awk '{print $1}'` if [ "$containerId" != "" ] ; then docker stop $containerId docker rm $containerId echo "Delete Container Success" fi imageId=`docker images | grep ${project_name} | awk '{print $3}'` if [ "$imageId" != "" ] ; then docker rmi -f $imageId echo "Delete Image Success" fi docker login --username admin --password-stdin < ~/my_password.txt $harbor_url docker pull $imageName docker run -d -p $port:$port --name $project_name $imageName echo "Start Container Success" echo $project_name
- 添加执行权限,并移入环境变量(为了可以任意文件夹下执行)
- 配置Jenkins任务构建后操作
- 测试流程
- 修改代码并Gitlab Tag
- 构建并测试接口
- Jenkins构建成功
- docker容器运行成功
- 程序接口测试成功
- Jenkins构建成功
- 修改代码并Gitlab Tag