前言
以下很多内容就学习自某b站大佬,给大佬打波广告
https://space.bilibili.com/31273057/video?tid=0&page=4&keyword=&order=pubdate
DevOps
1.概念
1.DevOps
DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。
它是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。
它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运维工作必须紧密
合作。
上面引用自百度百科,简单来说就是开发和运维一体化,实现自动化运维。
2.CI/CD
持续集成(Continuous Integration,CI)
持续交付(Continuous Delivery,CD)
持续部署(Continuous Deploy,CD)
以下是跟实现 CI/CD 相关的软件:
2.1 Jenkins (自动化)
通过 Jenkins 创建流水线拉取 Git 仓库代码,并执行相应的 Shell 脚本,自动完成打包、测试,构建,发布,部署等操作。类似功能的软件还有 Github 的 Travis 、Actions 等。
某公司现在 Jenkins 流水线的 Shell 脚本直接写死在了 Jenkins 的 Web 页面当中,这种 freestyle project 方式任何修改都没法享受 git 版本溯源。
以下是正确方式:
创建 pipeline 流水线并配置以下信息:
这样整个项目的 Shell 脚本就可以写在项目的 Jenkinsfile 当中,下面列举一个简单的 Jenkinsfile 结构:
1.前端Jenkinsfile
pipeline {
agent any
stages {
stage('echo node version') {
steps {
sh 'node -v'
}
}
stage('NpmInstall') {
steps {
sh 'npm i'
}
}
stage('NpmBuild') {
steps {
sh 'npm run build'
}
}
stage('docker build') {
steps {
sh 'docker build -t www.harbor.com/mxc-frontend:${BUILD_NUMBER} .' //最后有个点,代表使用项目根目录下的Dockerfile进行构建
}
}
stage('docker push') {
steps {
sh 'docker push -t www.harbor.com/mxc-frontend:${BUILD_NUMBER}'
}
}
stage('k8s deploy') {
steps {
sh 'kubectl -n namespace set image deployment/xxx xxx=www.harbor.com/mxc-frontend:${BUILD_NUMBER}'
}
}
}
post {
always {
echo 'Finish!!'
}
}
}
2.后端Jenkinsfile
pipeline {
agent any
stages {
stage('echo java version') {
steps {
sh 'java -version'
}
}
stage('maven build') {
steps {
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('docker build') {
steps {
sh 'docker build -t www.harbor.com/mx-backendc:${BUILD_NUMBER} .' //最后有个点,代表使用项目根目录下的Dockerfile进行构建
}
}
stage('docker push') {
steps {
sh 'docker push -t www.harbor.com/mx-backendc:${BUILD_NUMBER}'
}
}
stage('k8s deploy') {
steps {
sh 'kubectl -n namespace set image deployment/xxx xxx=www.harbor.com/mx-backendc:${BUILD_NUMBER}'
}
}
}
post {
always {
echo 'Finish!!'
}
}
}
2.2 Docker (容器化)
容器的概念跟虚拟机很像,虚拟机相对而言更接近一个完整的操作系统,但是也占用更多的资源。而 Docker 更轻量级,通过 Docker 这样的容器运行时环境(Container Runtime)可以为每个容器模拟出一个独立的环境,每个环境有自己的操作系统,然后我们会把开发的软件放入容器中,这样运行一个容器就相当于运行一个操作系统 + 一个软件,这种操作系统和软件一一结合的容器模式有以下好处:
- 确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题;—— 一致的运行环境
- 可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间
- 避免公用的服务器,资源会容易受到其他用户的影响。——隔离性
- 善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展
- 可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便
- 使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署
下面是 Docker 在运维过程中的一些操作:
- Dockerfile 描述了一个项目打包完之后(前端一般是 dist 包,后端一般是 war 包或者 jar 包)如何构建成一个 Docker 镜像(image),下面列举一个简单的 Dockerfile 结构:
1.前端Dockerfile
//Dockerfile
FROM nginx:1.14.0-alpine
MAINTAINER liangchaogui <[email protected]>
ADD nginx-default.conf /etc/nginx/conf.d/default.conf
ADD dist /usr/share/nginx/html/mxc-frontend
镜像是一种一层套一层的套娃结构,每个镜像的构建一般都是基于上一层的镜像来构建。
- FROM 代表了当前构建的基础镜像(除了 FROM 其它都是非必须的)
可以看到采用的基础镜像是 Nginx 镜像,Nginx 是现在主流部署架构的前置服务器(首当其冲被攻击) ,具有域名转发、负载均衡、反向代理等功能,作为前置服务器责无旁贷。 但 Nginx 更是一种静态Web服务器,具有高度的可配置性:缓存,https、http2、路径转发等, 作为 Docker 镜像的部署再合适不过,以下是一个的Nginx 配置文件:
//nginx-default.conf
server {
listen 80;
server_name _;
location ^~/mxc-frontend {
root /usr/share/nginx/html;
try_files $uri $uri/ /mxc-frontend/index.html;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
MAINTAINER 写作者名称。
第一个ADD 代表把 Jenkins 服务器下项目的 Nginx 配置文件复制到 Nginx 容器中,第二个 ADD 代表把项目通过 Jenkins 打包形成的 dist 包复制到 Nginx 容器的 html 目录下。
2.后端Dockerfile
后端目前有两种打包模式:一种是 Spring 项目打 war 包部署在另外的 Tomcat 中,另一种是更主流的SpringBoot 项目打 Jar 包直接通过 java -jar 运行(SpringBoot 内嵌 Tomcat)。
- war包方式(在构建容器时打包)
FROM maven:3 AS bd
MAINTAINER liangchaogui <[email protected]>
WORKDIR /code
ADD ./ /code
RUN mvn package -Dmaven.test.skip=true
FROM tomcat:7-jre7
MAINTAINER liangchaogui <[email protected]>
COPY --from=bd /code/target/*.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]
基于 maven 镜像打包,把 war 包复制到 Tomcat 镜像的 webapps 目录下
- war包方式(在Jenkins服务器上进行打包再构建容器)
FROM tomcat:7-jre7
MAINTAINER liangchaogui <[email protected]>
ADD /code/target/*.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]
省略了 maven 的打包步骤
- jar包方式
FROM java:8-jre
MAINTAINER liangchaogui <[email protected]>
ADD ./target/mxc-backend.jar /app/
CMD ["java", "-jar", "/app/mxc-backend.jar"]
EXPOSE 7000
- CMD 代表镜像通过 Docker 指令运行成容器时要执行的shell指令
- EXPOSE 代表当前服务暴露的端口
PS:实际中如果采用 Tomcat 镜像,必然会有配置文件需要替换,都可以通过 ADD 命令把项目的配置文件添加到 Tomcat 容器中
写好镜像构建文件后,通过
docker build
docker push
就可以把我们的应用构建成一个镜像,然后推送到镜像仓库,如外网 Docker Hub 或者内网服务 Harbor
2.3 Docker-Compose(多容器运行)
有了 Docker 镜像(image)之后,我们自然就要考虑如何把镜像运行起来成为一个容器,当然我们可以直接的通过在 Docker 宿主机上执行
docker run -d -p 80:80 nginx
的方式直接运行起来一个容器,但是如果多个容器都需要同时按顺序启动,这种手动输入 Shell 指令的方式显得太不智能,而 Docker-Compose 正是为了解决这种的问题。
Docker-Compose 其实就是一个把容器运行的命令以 yml 文件的形式进行配置,最后通过
docker-compose up -d
指令就可以把多个镜像一次启动起来
以下是 docker-compose.yml 配置文件:
version: '3'
services:
web:
image: nginx
ports:
- "80:80"
volumes:
- /html:/usr/share/nginx/html
- /conf/nginx.conf:/etc/nginx/nginx.conf
php:
image: devilbox/php-fpm:5.2-work-0.89
volumes:
- /html:/var/www/html
mysql:
image: mysql:5.6
environment:
- MYSQL_ROOT_PASSWORD=12345
这个脚本中启动了一个 Mysql 服务、一个 Php 服务和一个 Nginx 服务。
2.4. Kubernetes(容器编排)
Kubernetes 也叫 k8s,是一种容器编排管理工具(容器不止 Docker,只是生活中我们更常用的是 Dcoker),功能比 Docker-Compose 强大得多,Docker-Compose 只能管理当前宿主机的容器,而 k8s 是一种分布式的容器管理解决方案,并且提供了非常漂亮的可视化界面用于编排容器(Dashboard)。
接下来简单介绍 K8s 的结构和基础概念:
k8s结构介绍:
k8s 是分布式的,由 master 节点和 node 节点组成:
master 主节点,由4个重要进程构成:
- api-server :api-sever 是 master 对外交流的接口,master 会通过它对 node 节点发送指令,同时api-server也是面向用户的,用户可以通过它对 master下发指令,一共有三种方式:
- kubectl 命令行指令
- HTTP形式的Restful API接口
- WebUI界面(上面提到的Dashboard)
- ETCD:元数据库,整个 k8s 集群的数据都会存在这里
- controller-manager:整个集群的CPU
- 负责调度当前 pod 运行在哪个 node 节点上
node奴隶节点,由3个重要进程构成:
- kubelet : 负责 node 节点跟 master 节点的通讯
- kube-proxy:在集群层面创建虚拟网卡
- 容器运行时环境进程:最常见的就是Docker
k8s基础概念介绍:
- Pod:k8s的最小调度单元,一般情况下一个pod包含一个容器
假设现在有个高并发场景,我们创建了10个pod,并通过Nginx的负载均衡把请求打到10个pod上,然后其中2个pod服务由于不可抗因素关闭了,此时8台机器已经不足以应对当前的请求并发,以前我们一般会设置脚本实时监听在服务器关闭的情况下发短信或者邮件的方式通知运维人员登陆服务器重新启动2个pod服务,但是这样就太不智能了, 此时我们需要一个进程守护的东西,来让服务始终维持在10个pod。这个东西就是deployment。
- deployment:维持pod数量
上面说到 Nginx 实现负载均衡,但其实在 k8s 已经有相同的角色存在,它就是 service
- service:把多个 pod 抽象成一个服务对外暴露统一端口,对内实现负载均衡
以上就是 k8s 最常见的三个概念,当然 k8s 的概念远不止这几个,常见的还有:
- ingress:端口映射,通过配置它就可以通过外网 ip 访问 service
- replicaset:多实例的副本集,代表一个 deployment 有 pod 实例数量
以下是k8s.yml 配置文件:
---
apiVersion: v1
kind: Service
metadata:
name: mxc-ui
namespace: mxc
spec:
type: NodePort
selector:
app: ui
ports:
- name: svc-port
port: 80
targetPort: 80
nodePort: 30184
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: ui
namespace: mxc
spec:
replicas: 1
template:
metadata:
labels:
app: ui
spec:
volumes:
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
- name: mxc-pvc
persistentVolumeClaim:
claimName: mxc-pvc
imagePullSecrets:
- name: harborsecret
containers:
- name: ui
image: www.harbor.com/mxc/ui:1.0.4-324
imagePullPolicy: IfNotPresent
volumeMounts:
- name: localtime
mountPath: /etc/localtime
- name: mxc-pvc
mountPath: /usr/share/nginx/html/video
subPath: nginx/video
- name: mxc-pvc
mountPath: /mxc
subPath: mxc
配置文件中核心概念就是 delopyment 和 service,通过 delopyment 维持pod数量,通过service暴露服务。
2.5 补充
Devops 的相关软件非常多,除了 Docker 和 K8s,没有提到的还有如 Gitlab,JIRA,Sonar,Confluence 等等,重头搭建内网 DevOps 架构显然太遥远了,而且这些在某公司已经使用类似的云服务代替,而云服务搭配云服务也不错,所以下节介绍如何使用国产 CICD 云平台DaoCloud。
2.实践
1.DaoCloud
DaoCloud 是一个企业级容器云平台,通过它我们就不用自己在内网了安装 Docker和 k8s,降低运维的压力
1.1.新建Springboot工程
编写一个接口和测试用例,完成后提交到 git 仓库
1.2.新建DaoCloud项目
- 选择项目->创建新项目,并指向我们刚才创建的 git 项目仓库
- 前往流程定义,相当于可视化的配置 Jenkins 的每一个 stage
- 修改测试任务,修改 test 脚本为mvn test,由于测试脚本由云端容器执行,所以修改基础镜像为 maven
- 修改构建任务,使用本地 Dockerfile,然后保存
3.项目新增Dockerfile
FROM maven:3 AS bd
MAINTAINER liangchaogui <[email protected]>
WORKDIR /code
ADD ./ /code
RUN mvn package -Dmaven.test.skip=true
FROM java:8
MAINTAINER liangchaogui <[email protected]>
COPY --from=bd /code/target/*.jar /app.jar
CMD ["java", "-jar","/app.jar"]
先采用 maven 镜像进行打包,再使用 java 镜像部署
由于涉及到了双镜像,所以在 COPY 指令中加入了 --from 指令表示从一个镜像拷贝文件到另一个镜像
4.构建镜像(CI)
点击右上角的手动触发
可以看到 Daocloud 先将仓库代码 clone 到云端机器,接着拉取我们配置好的 maven3 镜像,然后开始下载maven 依赖,最后执行 mvn test。
测试完成后,会进入下一个构建阶段(测试不通过则直接中断),开始基于我们的 Dockerfile 每一行的指令进行镜像的构建
完成
构建完成之后点击镜像
可以看到镜像已经推送到了 Daocloud 的服务器上,之后我们只要在自己的服务器执行
docker pull + docker run 指令就可以把我们的镜像运行成容器了,但是这样就太不自动化了,下面介绍如何实现自动化部署。
5.DaoCloud连接自有服务器
选择集群管理->导入主机
在自有服务器上通过 curl 执行该 http 请求
以虚拟机为例子,执行该请求
请求成功
DaoCloud和自有主机连接成功
完成之后相当于我们自己的 docker 服务器就跟 daocloud 的主机连接上了,这样就在 DaoCloud 面板上有一个可视化的界面来看当前自有主机 Docker 环境的一些情况
以上操作都只是构建完成了镜像,并打通了 Daocloud 和自有服务器,也就是完成了 CI 持续集成部分,接下来就是完成 CD 持续部署部分。
6.部署应用(CD)
选择应用->创建应用->部署最新版本
选择应用的名称、应用的镜像、镜像版本号以及要部署的运行环境(这里就是部署到我们刚才连接好的虚拟机上)
接下来进入一个配置界面,相当于可视化的配置 docker run 指令,这里简单介绍下 docker run 指令:
docker run -d -p 80:80 nginx -v /home/data/:/home/data/
- -d指定后台运行(不要阻塞 Shell)
- -p指令端口,冒号左边是服务器的端口,冒号右边是容器的端口
- -v指定文件映射,例如运行了一个Mysql容器,我们当然希望数据是持久化到我们服务器上,而不是仅仅存在容器里
我们设置容器的端口为8080(Springboot 默认启动端口),主机端口随机就好
填写完成后点击立即部署
部署成功,点击随机生成的端口就可以打开部署的页面了
上面我们都是手动部署应用的,接下来就是把这个部署动作也加入到自动化的 stage 中
7.自动化部署
选择发布阶段
发布自有主机
选择我们刚才创建的应用
完成
接下来只要有代码推送到仓库(push),整个流水线就会重新跑一遍,从测试、构建到部署,完成自动化
每次部署完成也会发邮件到邮箱提醒
3. 总结
1.目前存在的问题
1.部署方式千奇百怪
有基于 nodejs 直接连接服务器上传的,有通过Jenkins但是配置文件写死在 web 页面的等,还有手动部署的
2.部署成本极大(沟通成本)
开发人员部署一个应用要跟运维人员说很多话
2.未来的方向
Web 项目采用 Jenkins + Jenkinsfile + Dockerfile的方式在 DaoCloud 平台进行部署,当然也可以通过服务器自身安装 docker + k8s 的形式部署,杜绝运维人员手动部署的情况。
Cocos 项目由于 Creator 没有 linux 版本,所以无法在 Jenkins 服务器上进行部署(除非搞台 Window 服务器,显然成本太高),而且 Creator 版本众多,只能在开发人员的机器上部署。