XieJava's blog

记录最好的自己


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于

django快速实现个人博客(附源码)

发表于 2024-08-10 | 更新于: 2025-07-27 | 分类于 技术 | | 阅读次数:
字数统计: 3.7k | 阅读时长 ≈ 19

Django作为一款成熟的Python Web开发框架提供了丰富的内置功能,如ORM(对象关系映射)、Admin管理界面、URL分发、模板系统、表单处理等,使得开发者能够快速搭建Web应用,大幅提高了开发效率。以前写过一篇博文《Django+Vue快速实现博客网站》介绍了通过Djang+Vue快速实现博客网站,django+vue作为个人博客来说稍显复杂,部署起来也比较麻烦,Vue的单页面架构也不利于SEO,更简单的解决方案其实还是用django的模板系统快速构建web应用,对于个人博客来说部署和运维更加简单也利于SEO。下面介绍如何快速的通过django模板系统快速实现个人博客。

一、工程目录组织结构

在这里插入图片描述

二、模型及管理实现

模型及管理端的实现沿用《Django+Vue快速实现博客网站》文章中的实现,用Django搭建很快很简单。
模型很简单,根据博客要显示的内容包括有‘文章分类’、‘文章标签’、‘博客文章’、‘站点信息’、‘社交信息’、‘聚焦’,模型定义分别如下: 这里要说明的是因为博客文章内容准备用markdown编写,所以引入了mdeditor from mdeditor.fields import MDTextField 内容字段content=MDTextField(verbose_name='内容')
模型代码示例如下:

1、模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from django.db import models
from common.basemodel import BaseModel
from mdeditor.fields import MDTextField
# Create your models here.
'''文章分类'''
class BlogCategory(BaseModel):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=50,verbose_name='分类名称',default='')
href = models.CharField(max_length=100,verbose_name='分类路径',default='')

def __str__(self):
return self.title

class Meta:
verbose_name = '文章分类'
verbose_name_plural = '文章分类'


'''文章标签'''


class Tag(BaseModel):
id=models.AutoField(primary_key=True)
tag=models.CharField(max_length=20, verbose_name='标签')

def __str__(self):
return self.tag

class Meta:
verbose_name='标签'
verbose_name_plural='标签'


'''博客文章'''
class BlogPost(BaseModel):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=200, verbose_name='文章标题', unique = True)
category = models.ForeignKey(BlogCategory, blank=True,null=True, verbose_name='文章分类', on_delete=models.DO_NOTHING)
isTop = models.BooleanField(default=False, verbose_name='是否置顶')
isHot = models.BooleanField(default=False, verbose_name='是否热门')
isShow = models.BooleanField(default=False, verbose_name='是否显示')
summary = models.TextField(max_length=500, verbose_name='内容摘要', default='')
content = MDTextField(verbose_name='内容')
viewsCount = models.IntegerField(default=0, verbose_name="查看数")
commentsCount = models.IntegerField(default=0, verbose_name="评论数")
tags = models.ManyToManyField(to=Tag, related_name="tag_post", blank=True, default=None, verbose_name="标签")
blogSource = models.CharField(max_length=200, blank=True, null=True, default='',verbose_name='文章来源')
pubTime = models.DateTimeField(blank=True, null=True, verbose_name='发布日期')

@property
def tag_list(self):
return ','.join([i.tag for i in self.tags.all()])

def __str__(self):
return self.title

class Meta:
verbose_name = '博客文章'
verbose_name_plural = '博客文章'


'''站点信息'''


class Site(BaseModel):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, verbose_name='站点名称', unique = True)
avatar = models.CharField(max_length=200, verbose_name='站点图标')
slogan = models.CharField(max_length=200, verbose_name='站点标语')
domain = models.CharField(max_length=200, verbose_name='站点域名')
notice = models.CharField(max_length=200, verbose_name='站点备注')
desc = models.CharField(max_length=200, verbose_name='站点描述')

def __str__(self):
return self.name

class Meta:
verbose_name = '站点信息'
verbose_name_plural = '站点信息'


'''社交信息'''


class Social(BaseModel):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=20, verbose_name='标题')
icon = models.CharField(max_length=200, verbose_name='图标')
color = models.CharField(max_length=20, verbose_name='颜色')
href = models.CharField(max_length=100, verbose_name='路径')

def __str__(self):
return self.title

class Meta:
verbose_name = '社交信息'
verbose_name_plural = '社交信息'


'''聚焦'''


class Focus(BaseModel):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=20, verbose_name='标题')
img = models.CharField(max_length=100, verbose_name='路径')

def __str__(self):
return self.title

class Meta:
verbose_name = '聚焦'
verbose_name_plural = '聚焦'


'''友链'''


class Friend(BaseModel):
id = models.AutoField(primary_key=True)
siteName = models.CharField(max_length=20, verbose_name='友链站点名称')
path = models.CharField(max_length=100, verbose_name='地址路径')
desc = models.CharField(max_length=200, verbose_name='描述')

def __str__(self):
return self.siteName

class Meta:
verbose_name = '友链'
verbose_name_plural = '友链'

2、admin管理

实际上只要把模型注册到admin就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from django.contrib import admin
from blog.models import *
# Register your models here.
@admin.register(BlogCategory)
class BlogCategoryAdmin(admin.ModelAdmin):
admin.site.site_title="ishareblog后台"
admin.site.site_header="ishareblog后台"
admin.site.index_title="ishareblog管理"

list_display = ['id', 'title', 'href']

@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin):
list_display = ['title','category','isTop','isHot']
search_fields = ('title',)

@admin.register(Site)
class SiteAdmin(admin.ModelAdmin):
list_display = ['name','slogan','domain','desc']

@admin.register(Social)
class SocialAdmin(admin.ModelAdmin):
list_display = ['title','href']

@admin.register(Focus)
class FoucusAdmin(admin.ModelAdmin):
list_display = ['title','img']

@admin.register(Friend)
class FoucusAdmin(admin.ModelAdmin):
list_display = ['siteName','path','desc']

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['id','tag']

三、博客展现实现

博客前端展现用django的模板技术实现。在网上找了一个基于Bootstrap v4.3.1的小清新风格HTML博客模板,https://gitee.com/yinqi/Light-Year-Blog 这个博客模只有三个页面,首页,详细页和About页面,样式和js都不多,比较简单。将html模板放入到templates的blog目录,为了便于维护将一些公共部分抽到了base.html,index.html和post.html 通过{ % extends 'blog/base.html' % }进行应用

1、视图实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from django.http import HttpResponse, Http404
from django.template import loader
from django.core.paginator import Paginator
from blog.models import BlogPost, Tag, BlogCategory
from django.shortcuts import render
from django.db.models import Count
from django.db.models.functions import TruncYear

import markdown2


# 首页/列表页视图实现.
def index(request):
category_id = request.GET.get('category')
tag_id = int(request.GET.get('tag',0))
year = request.GET.get('year')
search = request.GET.get('search')
if category_id:
blogpost_list = BlogPost.objects.filter(category=category_id, isShow=True).order_by('-isTop', '-pubTime')
elif tag_id:
blogpost_list = BlogPost.objects.filter(tags__id=tag_id, isShow=True).order_by('-isTop', '-pubTime')
elif year:
blogpost_list = BlogPost.objects.filter(pubTime__year=year, isShow=True).order_by('-isTop', '-pubTime')
elif search:
blogpost_list = BlogPost.objects.filter(content__icontains=search, isShow=True).order_by('-isTop', '-pubTime')
else:
# 筛选出需要显示的博客文章
blogpost_list = BlogPost.objects.filter(isShow=True).order_by('-isTop', '-pubTime', '-update_time')
# 每页显示的数量
per_page = 10

# 创建分页器实例
paginator = Paginator(blogpost_list, per_page)

# 获取当前页码,如果没有提供,则默认为第一页
page_number = request.GET.get('page') or 1

# 获取当前页的数据
page_obj = paginator.get_page(page_number)

# 计算显示的页码范围
current_page = int(page_number)
pages_to_show = 11 # 当前页前后各5页加上当前页共11页
start_page = max(current_page - 5, 1)
end_page = min(start_page + pages_to_show - 1, paginator.num_pages)

template = loader.get_template("blog/index.html")
context = {
"page_obj": page_obj,
'start_page': start_page,
'end_page': end_page,
'hot_posts': get_hot_posts(),
'tags': get_all_tags(),
'post_grouped_by_year':get_post_groped_by_year(),
'categories': get_categories(),
'category_id': category_id,
'tag_id': tag_id,
'year': year,
'search': search,
}
return HttpResponse(template.render(context, request))


# 详情页视图实现.
def post_detail(request, id):
try:
post_obj = BlogPost.objects.get(id=id)
html_content = markdown2.markdown(post_obj.content,
extras=["code-color", "fenced-code-blocks", "cuddled-lists", "tables",
"with-toc", "highlightjs-lang"])
html_content = html_content.replace('< table >', '< table class="table table-bordered" >')
html_content = html_content.replace('< img src=', '< img style="max-width:100%;height:auto;" src= ')
context = {"post_obj": post_obj, "html_content": html_content, "hot_posts": get_hot_posts(),"tags": get_all_tags(),"post_grouped_by_year":get_post_groped_by_year(),'categories': get_categories()}
except BlogPost.DoesNotExist:
raise Http404("Post does not exist")
return render(request, "blog/post.html", context)


def get_hot_posts():
# 获取点赞数最高的前5篇文章
hot_posts = BlogPost.objects.filter(isShow=True).order_by('-viewsCount', '-pubTime')[:5]
return hot_posts


def get_all_tags():
# 获取所有的标签
tags = Tag.objects.all() # 获取所有的标签
return tags

def get_post_groped_by_year():
# 将发布日期截断为年份,并计算每年的文章数量。
post_grouped_by_year = (
BlogPost.objects
.annotate(year=TruncYear('pubTime'))
.values('year') # 返回的字典包含'year'键
.annotate(publication_count=Count('id')) # 计算每年的文章数量
.order_by('-year') # 按年排序
)
return post_grouped_by_year

def get_categories():
# 获取所有分类
categories = BlogCategory.objects.all()
return categories

2、模板实现

静态文件如css、js等放到static的blog目录,html模板文件放到templates的blog目录
在setting.py文件中配置 STATIC_URL = 'static/',在html模板文件中通过{ % load static % } 将静态文件的地址引用进来
将公共部分抽取出来形成base.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
{ % load static % }
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>XieJava的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="xiejava" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/bootstrap.min.css' % }" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/materialdesignicons.min.css' % }" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/style.min.css' % }" />
</head>
<body>
<header class="lyear-header text-center" style="background-image:url(images/left-bg.jpg);">
<div class="lyear-header-container">
<div class="lyear-mask"></div>
<h1 class="lyear-blogger pt-lg-4 mb-0"><a href="{ % url 'index' % }">XieJava的博客</a></h1>
<nav class="navbar navbar-expand-lg">
<a class="navbar-toggler" data-toggle="collapse" data-target="#navigation" aria-controls="navigation" aria-expanded="false" aria-label="Toggle navigation">
<div class="lyear-hamburger">
<div class="hamburger-inner"></div>
</div>
</a>

<div id="navigation" class="collapse navbar-collapse flex-column">
<div class="profile-section pt-3 pt-lg-0">
<img class="profile-image mb-3 rounded-circle mx-auto" src="https://img9.doubanio.com/icon/ul70489051-4.jpg" width="120" height="120" alt="xiejava" >
<div class="lyear-sentence mb-3">
记录最好的自己<br>
写是为了更好的思考,坚持写作,力争更好的思考。
</div>
<hr>
</div>

<ul class="navbar-nav flex-column text-center">
<li class="nav-item active">
<a class="nav-link" href="{ % url 'index' % }">首页</a>
</li>
{ % for category in categories % }
<li class="nav-item">
<a class="nav-link" href="{ % url 'index' % }?category={ { category.id } }">{ { category.title } }</a>
</li>
{ % endfor % }
<li class="nav-item">
<a class="nav-link" href="{ % url 'index' % }">关于我</a>
</li>
</ul>

<div class="my-2 my-md-3">
<form class="lyear-search-form form-inline justify-content-center pt-3">
<input type="text" id="search" name="search" class="form-control mr-md-1" placeholder="搜索关键词" />
</form>
</div>
</div>
</nav>
</div>
</header>
<div class="lyear-wrapper">
<section class="mt-5 pb-5">
<div class="container">

<div class="row">
<!-- 文章列表 -->
<div class="col-xl-8">
<!-- 内容 -->
{ % block content % }
<!-- 默认内容 -->
{ % endblock % }
</div>
<!-- 内容 end -->

<!-- 侧边栏 -->
<div class="col-xl-4">
<div class="lyear-sidebar">
<!-- 热门文章 -->
<aside class="widget widget-hot-posts">
<div class="widget-title">热门文章</div>
<ul>
{ % for post in hot_posts % }
<li>
<a href="{ % url 'post_detail' id=post.id % }">{ { post.title } }</a> <span>{ { post.pubTime } }</span>
</li>
{ % endfor % }
</ul>
</aside>

<!-- 归档 -->
<aside class="widget">
<div class="widget-title">归档</div>
<ul>
{ % for post in post_grouped_by_year % }
<li><a href="{ % url 'index' % }?year={ { post.year|date:'Y' } }" >{ % if year == post.year|date:'Y' % }<b>{ { post.year|date:'Y' } } 年 </b>{ % else % }{ { post.year|date:'Y' } } 年{ % endif % }</a> ({ { post.publication_count } })</li>
{ % endfor % }
</ul>
</aside>

<!-- 标签 -->
<aside class="widget widget-tag-cloud">
<div class="widget-title">标签 </div>
<div class="tag-cloud">
{ % for tag in tags % }
<a href="{ % url 'index' % }?tag={ { tag.id } }" { % if tag_id == tag.id % }class="badge badge-primary"{ % else % }class="badge badge-light"{ % endif % }>{ { tag.tag } }</a>
{ % endfor % }
</div>
</aside>
</div>
</div>
<!-- 侧边栏 end -->
</div>

</div>
<!-- end container -->
</section>
</div>
<script type="text/javascript" src="{ % static 'blog/js/jquery.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/jquery.nicescroll.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/bootstrap.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/main.min.js' % }"></script>
</body>
</html>

博客首页/列表页
通过{ % extends 'blog/base.html' % } 将公共部门引入进来后index.html的内容就简洁了很多
index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

{ % extends 'blog/base.html' % }

<!-- 内容 -->
{ % block content % }
{ % if page_obj.object_list.count > 0 % }
{ % for blogpost in page_obj.object_list % }
<article class="lyear-arc">
<div class="arc-header">
<h2 class="arc-title"><a href="article/{ { blogpost.id } }">{ { blogpost.title } }</a></h2>
<ul class="arc-meta">
<li><i class="mdi mdi-calendar"></i> { { blogpost.pubTime } }</li>
<li><i class="mdi mdi-tag-text-outline">
</i> { % for tag in blogpost.tags.all % }<a href="{ % url 'index' % }?tag={ { tag.id } }">{ { tag.tag } }</a>&nbsp{ % endfor % }</li>
<!--<li><i class="mdi mdi-comment-multiple-outline"></i> <a href="#">3 评论</a></li>-->
<li><i class="mdi mdi-heart-outline"></i> <a href="#">{ { blogpost.viewsCount } } 喜欢</a></li>
</ul>
</div>

<div class="arc-synopsis">
<p>{ { blogpost.summary } }</p>
</div>
</article>
{ % endfor % }


<!-- 分页 -->
<div class="row">
<div class="col-lg-12">
<ul class="pagination">
{ % if page_obj.has_previous % }
<li class="page-item"><a class="page-link" href="?page={ { page_obj.previous_page_number } }"><i class="mdi mdi-chevron-left"></i></a></li>
{ % endif % }

{ % for page_no in page_obj.paginator.page_range % }
{ % if page_no >= start_page and page_no <= end_page % }
{ % if page_no == page_obj.number % }
<li class="page-item active"><a class="page-link" href="#">{ { page_no } }</a></li>
{ % else % }
<li class="page-item"><a class="page-link" href="?page={ { page_no } }{ % if tag_id % }&tag={ { tag_id } }{ % endif % }{ % if year % }&year={ { year } }{ % endif % }{ % if search % }&search={ { search } }{ % endif % }">{ { page_no } }</a></li>
{ % endif % }
{ % endif % }
{ % endfor % }

{ % if page_obj.has_next % }
<li class="page-item"><a class="page-link" href="?page={ { page_obj.next_page_number } }{ % if tag_id % }&tag={ { tag_id } }{ % endif % }{ % if year % }&year={ { year } }{ % endif % }{ % if search % }&search={ { search } }{ % endif % }"><i class="mdi mdi-chevron-right"></i></a></li>
{ % endif % }

<p>总页数: { { page_obj.paginator.num_pages } }</p>
</ul>
</div>
</div>
{ % else % }
<p> 没有找到文章 </p>
{ % endif % }
<!-- 分页 end -->
{ % endblock % }
<!-- 内容 end -->

博客详情页post.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{ % extends 'blog/base.html' % }
<!-- 文章阅读 -->

{ % block content % }

<article class="lyear-arc">
<div class="arc-header">
<h2 class="arc-title"><a href="#">{ { post_obj.title } }</a></h2>
<ul class="arc-meta">
<li><i class="mdi mdi-calendar"></i> { { post_obj.pubTime } }</li>
<li> { % for tag in post_obj.tags.all % }<a href="{ % url 'index' % }?tag={ { tag.id } }">{ { tag.tag } }</a>{ % endfor % }</li>
<!--<li><i class="mdi mdi-comment-multiple-outline"></i> <a href="#">3 评论</a></li>-->
<li><i class="mdi mdi-heart-outline"></i> <a href="#">{ { post_obj.viewsCount } } 喜欢</a></li>
</ul>
</div>

<div class="arc-preview">
<img src="images/blog/post-1.png" alt="" class="img-fluid rounded" />
</div>

<div class="lyear-arc-detail">
{ { html_content|safe } }
</div>

</article>

{ % endblock % }
<!-- 内容 end -->

四、部署及效果

在部署之前执行python manage.py collectstatic 将admin等其他模块用到的静态文件统一输出到static的目录。
通过 python manage.py runserver 启动应用就可以看到效果。
实际效果见 http://iblog.ishareread.com/
博客首页
在这里插入图片描述

博客详情页
在这里插入图片描述

五、源代码

所有源代码及说明见 https://gitee.com/xiejava/ishareblog


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

django集成pytest进行自动化单元测试实战

发表于 2024-08-03 | 更新于: 2025-07-27 | 分类于 技术 | | 阅读次数:
字数统计: 1.3k | 阅读时长 ≈ 5

在Django项目中集成Pytest进行单元测试可以提高测试的灵活性和效率,相比于Django自带的测试框架,Pytest提供了更为丰富和强大的测试功能。本文通过一个实际项目ishareblog介绍django集成pytest进行自动化单元测试实战。

一、引入pytest相关的包

1
2
3
pip install pytest
pip install pytest-django
pip install pytest-html

其中pytest-django插件,它提供了Django和Pytest之间的桥梁,pytest-html 是一个 pytest 的插件,用于生成详细的 HTML 测试报告。这个插件能够将 pytest 运行的结果转化为一个直观、易于阅读的 HTML 格式报告,这对于分享测试结果、审查测试覆盖率以及归档测试历史非常有帮助。

二、配置pytest

1、将django的配置区分测试环境、开发环境和生产环境

因为测试环境、开发环境和生产环境的环境配置参数不一样,一个好的实践是将开发、测试和生产环境通过配置区分开,django的配置主要集中在项目的settings.py文件,这里通过settings.py的配置文件将开发、测试、生产区分开,不同的环境调用不通的配置文件。

在这里插入图片描述

因为大部分的配置参数都是一样的,在这里我将公共的配置参数都抽到了base.py,环境配置中有差异的部分分别放到各自的配置文件中,如开发环境用的是mysql,测试环境用sqlite3,就可以将不同的配置给区分开。
测试环境是settings_test.py,这里除了数据库的配置不一样,其他都沿用基础的公共配置。settings_test.py配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from .base import *

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'test_db.sqlite3'),
}
}

2、配置pytest

在Django项目根目录下,创建或编辑pytest.ini文件,来配置Pytest。
在这里插入图片描述

pytest.ini代码如下:

1
2
3
4
[pytest]
DJANGO_SETTINGS_MODULE = ishareblog.settings_test

python_files = tests.py test_*.py *_tests.py

DJANGO_SETTINGS_MODULE = ishareblog.settings_test 指定了pytest用到的环境配置
python_files = tests.py test_*.py *_tests.py 指定了pytest将测试以test开头的py文件中的测试用例。

三、编写测试用例

接下来,可以在tests.py或test_*.py文件中编写你的测试用例。由于pytest-django插件的存在,你可以像平常一样使用Django的测试机制,同时也能享受Pytest带来的便利。以下以我的ishareblog博客代码通过业务测试和接口测试来编写测试用例。

在这里插入图片描述

1、业务测试

我的isharebog业务相对简单,主要是测试验证业务模型模块的增删改查是否符合预期。
业务测试tests.py示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pytest
from django.test import TestCase
from blog.models import BlogCategory

@pytest.mark.django_db
class TestBlogCategory(TestCase):

def setUp(self):
self.blogcategory = BlogCategory.objects.create(id=1,title="Test Category", href='/category/1')

def test_BogCategoryModel(self):
blog_category = BlogCategory.objects.get(id=self.blogcategory.id)
self.assertEqual(blog_category.title, "Test Category")
self.assertEqual(blog_category.href, '/category/1')


@pytest.mark.django_db
def test_blog_category_create():
blogcategory = BlogCategory.objects.create(id=1,title="Test Category", href='/category/1')
category_count = BlogCategory.objects.count()
assert category_count > 0, "Blog category was not created category_count=0."
assert blogcategory.id > 0, "Blog category was not created."
assert blogcategory.title == "Test Category", "Blog category title is wrong."
assert blogcategory.href == "/category/1", "Blog category href is wrong."


@pytest.mark.django_db
def test_blog_category_query():
category_count = len(BlogCategory.objects.all())
assert category_count >= 0, "Blog category query error."


if __name__ == '__main__':
pytest.main(["-s", "-v", "-p", "no:warnings", "--tb=short", "--html=report.html", "blog/tests.py"])

业务测试举了通过测试类和测试方法写的测试用例,分别对博客目录进行添加和查询编写了测试用例。

2、接口测试

接口是暴露给前端程序调用的,接口测试主要是测试接口正不正常,接口值是不是符合预期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import pytest

host = "http://localhost:8000"


class TestApi:
def test_getcategory_list(self):
url = f'{host}/api/category/'
response = requests.get(url)
assert response.status_code == 200, f'Expected status code 200 but got {response.status_code}'
assert response.json() != None, f'Expected to get json response but got {response.text}'
print(response.json())

def test_getpost_list(self):
url = f'{host}/api/post/list'
response = requests.get(url)
assert response.status_code == 200, f'Expected status code 200 but got {response.status_code}'
assert response.json() != None, f'Expected to get json response but got {response.text}'


if __name__ == '__main__':
pytest.main(["-s", "-v", "-p", "no:warnings", "--tb=short", "--html=report.html", "api/tests.py"])

接口测试部分,对获取目录的API接口和文章列表的API接口编写了测试用例。

四、进行测试

最后可以分别在blog目录和api目录下运行test.py 分别进行业务和接口的单元测试。
注意在进行测试之前需要执行 python manage.py makemigrations --settings=ishareblog.settings_test 初始化环境。
在进行api接口测试之前需要将django的应用服务启动 python manage.py runserver 8000 --settings=ishareblog.settings_test 启动的时候也带上测试环境的配置。
可以通过pytest --html=report.html 自动执行所有的单元测试,并生成可读的html的测试报告。
在这里插入图片描述

pytest生成的report.html测试报告
在这里插入图片描述

以上通过一个ishareblog的实际项目介绍django集成pytest进行自动化单元测试实战。
ishareblog的所有代码包括pytest的配置见 https://gitee.com/xiejava/ishareblog


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

解决django与sqlite3不兼容报SQLite 3.9.0 or later is required (found 3.8.2)错的问题

发表于 2024-07-23 | 更新于: 2025-07-27 | 分类于 技术 , 开发 | | 阅读次数:
字数统计: 331 | 阅读时长 ≈ 1

今天在尝试用pytest进行django的单元测试,pytest用的数据库是sqlite3,在window环境下测试得好好的,但是放到linux环境下就报错,具体是报django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.8.2).的错。

在这里插入图片描述

从报错的提示看是sqlite3的版本太低,期望是3.9.0但是当前版本是3.8.2
在网上找了很多资料,也都是说django与sqlite3的版本不兼容,需要升级sqlite3的版本。
于是将sqlite3升级到目前的稳定版本3.46.0,然而发现并没有什么用,还是报sqlite3.NotSupportedError: deterministic=True requires SQLite 3.8.3 or higher 。

在这里插入图片描述

通过sqlite3 –version 查看版本号已经是3.46.0了,但还是提示要求SQLite版本在3.8.3以上。重装django4和python3.8的环境都没有用。
最后在网上找到解决办法。使用第三方包pysqlite3运行SQLite3
具体方法如下:
1、安装pysqlite3和pysqlite3-binary

1
2
pip install pysqlite3
pip install pysqlite3-binary

2、找到django的sqlite3驱动包,/usr/local/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py 找到 from sqlite3 import dbapi2 as Database 注释它,添加代码

1
2
#from sqlite3 import dbapi2 as Database  #注释它
from pysqlite3 import dbapi2 as Database #新加这段代码

历经周折终于解决。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

什么是软件定义安全SDSec

发表于 2024-07-09 | 更新于: 2025-07-27 | 分类于 技术 , 网络安全 | | 阅读次数:
字数统计: 2.4k | 阅读时长 ≈ 8

一、软件定义安全SDSec产生的背景

软件定义安全(Software Defined Security,SDSec)的产生背景主要源于传统网络安全防护方法在面对复杂网络环境时的不适应性,以及软件定义网络(SDN)技术的发展和应用。

SDN技术影响与数据与控制分离的理念:SDN的出现将网络的控制平面与数据平面分离,实现了网络资源的集中管理和自动化配置。SDN控制器可以全局视角管理网络,这为安全策略的统一规划和执行提供了可能。SDSec的核心理念是数据与控制分离,安全管理与控制集中化,这与SDN的内在性质相契合,适合用于网络安全的场景。

复杂网络环境与传统安全设备的挑战:在物理与虚拟网络共存的数据中心等复杂环境中,传统的网络安全设备部署过程繁复,不能灵活地区分处理流经的流量,安全防护范围僵化,且安全设备可能成为单一故障点,传统的安全网关设备如防火墙、入侵防护系统等,存在互操作性差、难以集成等问题,SDN的理念可以拓展到这些设备上,作为下一代安全产品开发的指导思想,从而产生了SDSec的概念

云计算和虚拟化技术的发展对安全的需求:云计算系统的开放API、虚拟化技术的资源池能力、SDN的支持,以及中小型企业对安全防护系统有效性、便捷性和灵活性的需求,促使软件定义安全理念在云计算安全防护中得到体现。

SDSec的产生是为了解决传统网络安全方法在现代网络环境中的局限性,同时利用SDN技术的优势,实现更高效、灵活和自动化的网络安全防护。

二、软件定义安全SDSec的定义

软件定义安全(Software Defined Security,SDSec)是一种从软件定义网络(SDN)引申而来的概念,其核心原理是将网络安全设备与其接入模式、部署方式、实现功能进行解耦。这种解耦使得底层的网络安全设备可以抽象为安全资源池中的资源,而顶层则通过软件编程的方式进行智能化、自动化的业务编排和管理,以完成相应的安全功能,实现灵活的安全防护 。

SDSec的工作机制可以分解为三个主要部分:软件定义流量、软件定义资源和软件定义威胁模型。这三个部分相互关联,形成一个动态、闭环的工作模型。具体来说:
● 软件定义流量通过软件编程实现网络流量的细粒度定义及转发控制管理,将目标网络流量转发到安全设备上,实现安全设备的逻辑部署和使用。
● 软件定义资源通过管理中心对安全资源进行统一注册、池化管理、弹性分配,并在虚拟计算环境下支持虚拟安全设备模板的分发和设备的创建。
● 软件定义威胁模型涉及自动化采集、分析和挖掘网络流量、网络行为、安全事件等信息,实现对未知威胁或高级安全威胁的实时分析和建模,并用建模结果指导流量定义,实现动态、闭环的安全防护。

SDSec的架构设计使得安全策略执行点(数据平面)与安全控制器(控制平面)相互分离,从而使安全设备能够快速、自动地适应业务的动态变化。这种分离的架构有助于厂商集中精力研发异常检测技术,提高安全产品的核心能力,并便于在一个计算能力更强的、开放的、综合的平台上完成异常检测

三、软件定义安全SDSec的支撑技术

软件定义安全本质上是一种技术理念,主要技术思路为参考 SDN 的数 据与控制层面分离 ,通过构建上层安全管理平台去运营、运维下层集约化部署的安 全资源。软件定义安全SDSec的主要支撑技术包括软件定义网络 (SDN)、虚拟化技术、API和标准化接口、安全编排与自动化响应 (SOAR)

软件定义网络 (SDN):SDN是SDSec的基础之一,它将网络的控制平面与数据平面分离,允许对网络流量进行集中控制和策略实施,为安全策略的灵活部署和动态调整提供了可能。
虚拟化技术:虚拟化技术支持快速准备虚拟化的安全设备,包括服务器虚拟化、网络功能虚拟化(NFV)和软件定义存储(SDS),这些技术允许安全功能如防火墙、入侵检测系统(IDS)、入侵防御系统(IPS)等作为虚拟实例运行,并灵活调度计算、存储、网络资源,可以更容易地在不同位置之间移动和扩展,为安全功能服务。
API和标准化接口:SDSec依赖于API和标准化接口来集成不同的安全组件和服务,允许不同供应商的产品之间进行互操作,增强整体安全生态系统的灵活性和可扩展性。
安全编排与自动化响应 (SOAR):自动化工具和工作流编排用于实现安全策略的快速部署、更新和响应。这包括安全策略的自动化生成、配置和变更管理,以及在检测到威胁时的自动响应。SOAR平台结合了安全事件管理、自动化响应和编排功能,用于协调多个安全工具和流程,提高安全团队的效率和响应速度。

这些技术共同作用,使SDSec能够提供一个更加强大、灵活和响应迅速的安全框架,以应对不断变化的威胁环境。

四、软件定义安全SDSec的优缺点

软件定义安全(SDSec)与传统安全产品相比,各有其独特的优点和缺点:

软件定义安全(SDSec) 传统安全产品
灵活性与可扩展性 使用SDN和NFV技术,安全策略可以动态配置和调整,适应网络流量变化和新的安全威胁。可以通过软件更新和配置更轻松地适应新的安全需求和技术进展。 通常使用硬件设备,配置和更新安全策略较为繁琐,不够灵活。硬件设备的功能和性能受到限制,升级和扩展可能需要更换设备或增加新的硬件。
集中化管理和自动化 可以通过集中的安全策略控制器管理整个网络的安全策略,实现自动化响应和安全事件管理。 管理分散在各个设备上,安全策略的更新和管理通常需要更多的人工干预。
资源利用率和成本效益 利用NFV将安全功能虚拟化在通用服务器上运行,提高了资源利用率,并降低了成本。 需要购买和维护多个专用硬件设备,成本和资源利用率不如虚拟化解决方案高效。
技术成熟度和部署复杂性 相较传统安全产品,SDSec 的技术可能需要更多时间来成熟,并且在部署和管理上可能存在一定的复杂性。 技术相对成熟,部署相对简单,但可能缺乏SDSec中的一些先进功能和灵活性。
安全性考量 虽然提供了灵活和智能的安全防护,但在实施时需要确保其本身不会成为攻击目标或单点故障。如果控制平台受到攻击,可能会对整个网络的安全造成影响。 可能因为使用独立的硬件设备,一些传统攻击方式可能更难以实施。

SDSec通过软件定义的方式提供了更灵活、智能和高效的安全解决方案,尤其适合于需要频繁更新和动态调整安全策略的现代网络环境。然而,与传统安全产品相比,SDSec在技术成熟度、部署复杂性和对网络基础设施的依赖性等方面可能存在一些挑战和限制。

需要注意的是,软件定义安全理念从逻辑上和架构上是涵盖了传统安全与云计算安全的,前者是从改变安全技术架构去提高安全管理效率,提升安全运营的 价值,而后者则是区别于安全落地的不同场景。因此软件定义安全同样适用于传统 IT 环境,如果做到了构建统一的上层管理平台,使其具备开放接口,下层各类安全 组件资源可受上层管理应用进行编排、计量等手段统一关联起来,那么这套安全防 护体系也可认为是由软件定义的。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

常见网络攻击方式及防御方法

发表于 2024-07-04 | 更新于: 2025-07-27 | 分类于 技术 , 网络安全 | | 阅读次数:
字数统计: 2k | 阅读时长 ≈ 6

网络安全威胁的不断演变和增长,网络攻击的种类和数量也在不断增加,攻防对抗实战演练在即,让我们一起了解一下常见网络攻击方式及防御方法。

1. DDOS攻击(分布式拒绝服务攻击)

借助于C/S(客户端/服务器)技术,将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDOS攻击,从而成倍地提高拒绝服务攻击的威力。
防护方法:
所有的主机平台都有低于DDOS的设置,基本的有:

  • 关闭不必要的服务
  • 限制同时打开的syn半连接数目
  • 缩短syn半连接的time out时间
  • 及时更新补丁

网络设置:
防火墙

  • 禁止对主机的非半开服务的访问,限制SYN的连接数和IP访问
  • 路由器
  • 设置SYN数据包流量速率

2. UDP洪水攻击

利用含有udp数据包的ip数据包对目标主机上的随机端口进行饱和攻击,由于udp流量不需要像tcp那样三次握手,可以低成本的运行。这种特质也使得udp非常脆弱,更容易被滥用,一些udp洪水攻击能够利用dns方法攻击的形式实施。
防护方法:
根据业务UDP最大包长设置UDP最大包大小以过滤异常流量。
建立UDP连接规则,要求所有去往该端口的UDP包,必须首先与TCP端口建立TCP连接。

3. Smurf攻击

攻击者向网络广播地址发送ICMP包,并将回复地址设置成受害网络的广播地址,通过使用ICMP应答请求数据包来淹没受害主机的方式进行,最终导致该网络的所有主机都对次ICMP应答请求作出答复,导致网络阻塞。更加复杂的Smurf攻击攻击将源地址改为第三方受害者,最终导致第三方崩溃。
防护方法:
1.配置路由器禁止IP广播包进网
2.配置网络上所有计算机的操作系统,禁止对目标地址为广播地址的ICMP包响应
3.对于从本网络向外部网络发送的数据包,本网络应该将其源地址为其他网络的这部分数据包过滤掉

4. CC攻击

CC攻击利用代理服务器向网站发送大量需要较长计算时间的URL请求,如数据库查询等,导致服务器进行大量计算而很快达到自身的处理能力而形成DOS,而攻击者一旦发送请求给代理后就主动断开连接,因为代理并不因为客户端这边连接的断开就不去连接目标服务器,因此攻击机的资源消耗相对很小,而从目标服务器看来,来自代理的请求都是合法的。
防护方法:
CC有效性在于攻击方不接受服务器回应的数据,发送完请求后就主动断开连接,因此要确认连接是否是CC,服务器端不立即执行URL请求命令,而是简单的返回一个页面转向的回应,回应中包含新的URL请求地址,如果是正常访问,客户端会主动再次连接到转向页面,对用户来说是透明的;而对于CC攻击者,由于不接收回应数据,因此就不会重新连接,服务器也就不需要进行查询操作。

5. ARP攻击(中间人攻击)

通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞,攻击者只要持续不断的发出伪造的ARP相应包就能更改目标主机ARP缓存中的IP-MAC条目,造成网络终端或中间人攻击。
防护方法:

  • 网关和终端双向绑定IP和MAC地址
  • 局域网中的每台电脑中进行静态ARP绑定
  • 打开安全防护软件的ARP防火墙功能
  • 彻底追踪查杀ARP病毒

6. 邮件钓鱼

利用伪装的电邮,欺骗收件人将账号、口令等信息回复给指定的接收者;或引导收件人连接到特制的网页,这些网页通常会伪装成和真实网站一样,如银行或理财的网页,令登录者信以为真,输入信用卡或银行卡号码、账户名称及密码等而被盗取。
防护方法:

  • 不点击不知来源的邮件和附件
  • 在邮箱服务端安装安全软件,开启SPF策略
  • 安装个人电脑杀毒软件

拓展:

  • 鱼叉攻击
    攻击者将木马程序作为电子邮件的附件,并起上一个极具诱惑力的名称,发送给目标电脑,诱使受害者打开附件,从而感染木马。
  • 水坑攻击
    攻击者分析攻击目标的上网活动规律,寻找攻击目标经常访问的网站的弱点,先将此网站“攻破”并植入攻击代码,一旦攻击目标访问该网站就会“中招”。

7. WIFI钓鱼

WIFI钓鱼是一种新型的无线网络攻击方式,将中间人攻击和钓鱼攻击相结合,选定目标路由器后,将连接到路由器上的客户端全部强制退出重新登录。当用户再次登录时,会连接到假冒的路由器上,弹出登录窗口,并要求用户输入无线网络的用户名和密码来更新路由器硬件,从而盗取用户的账号密码等信息。
防护方法:
1.不连接公共场所的wifi,重要文件用手机热点发送
2.路由器定期更换密码
3.路由器定期升级

8. 挖矿木马

由于区块链技术热炒以及数字货币的推广运营,如比特币、以太币等层出不穷的数字货币各种热炒,在这些的利益驱使下便出现各种模式的挖矿木马程序,挖矿木马主要就是通过利用各种手段,将挖矿程序植入到用户的计算机中,在用户不知情的情况下,偷偷利用用户的计算机进行执行挖矿功能,从而获取收益。
用户中木马的常见方式:
1.用户往往在不注意的时候,下载并运行了来历不明的破解软件或不安全软件
2.用户点击运行了钓鱼邮件中的附件的文件
3.用户没有做好系统及时更新,通过系统漏洞传播
4.用户浏览了植入挖矿脚本的网页,浏览器就解析脚本进行挖矿
防护方法:
1.在计算机中安装病毒查杀软件(火绒、360杀毒),并及时更新病毒查杀软件的病毒库,还需做好定时全盘查杀病毒。
2.及时做好计算机系统补丁的更新。
3.服务器、主机、数据库等使用高强度的密码口令,切勿使用弱口令,防止被暴力破解。
4.网络上不要去随意下载、运行来历不明的程序或者破解程序,不随意点击来历不明的链接。

9. 0day攻击

0day漏洞是指攻击者最新发现的,厂家还未发布补丁,在网络上还未公布的系统或应用程序新漏洞。
防护方法:
1.安装实时监控和主动防御设备
2.实施网络边界防范
3.加固终端系统
4.加强网络基础设施的安全


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

什么是基于风险的漏洞管理RBVM及其优势

发表于 2024-06-13 | 更新于: 2025-07-27 | 分类于 技术 , 网络安全 | | 阅读次数:
字数统计: 2.1k | 阅读时长 ≈ 7

一、什么是漏洞管理

安全漏洞是网络或网络资产的结构、功能或实现中的任何缺陷或弱点,黑客可以利用这些缺陷或弱点发起网络攻击,获得对系统或数据的未经授权的访问,或以其他方式损害组织。常见漏洞的示例包括可能允许某些类型的恶意软件进入网络的防火墙配置错误,或可能允许黑客接管设备的操作系统远程桌面协议中未修补的错误。

漏洞管理是指通过对网络系统中的漏洞进行识别、评估、修复、验证等一系列操作,以保障系统的安全性。这一过程是IT风险管理的一个子领域,主要关注组织IT基础设施和软件中的安全漏洞。

关于漏洞的分类及漏洞管理参见《安全运营之漏洞管理》

二、什么是基于风险的漏洞管理RBVM

传统的漏洞管理方法通常依赖于扫描工具来发现漏洞,然后按照某种顺序(如时间顺序或严重程度)进行修复。随着技术的快速发展和数字化转型的推进,网络系统和应用程序的复杂性日益增加,导致安全漏洞的数量也呈现爆炸式增长。面对不断增长的漏洞列表和警报,安全团队很容易陷入警报疲劳,难以判断哪些漏洞是真正需要优先处理的。

而RBVM(Risk-based Vulnerability Management)是基于风险的漏洞管理,RBVM方法不是简单地修补所有漏洞,而是关注那些实际可利用的、对组织构成风险的漏洞。这种方法通过分析内部资产、攻击者活动以及威胁情报馈送(特别是漏洞情报),来确定哪些漏洞是需要优先处理的。是一种更加智能化和策略性的方法来识别、评估、优先级排序和修复组织中的安全漏洞。

RBVM的核心在于更好地了解和评估风险,通过综合考虑漏洞的严重性、可利用性、资产价值等因素,确定每个漏洞的风险等级,并据此制定修复策略。这种方法能够帮助安全团队集中资源处理那些真正高风险的漏洞,提高漏洞管理的效率和效果。

三、RBVM的基本流程

RBVM(基于风险的漏洞管理)与传统漏洞管理的识别、评估、修复、验证相比是多了资产识别、风险评估和优先级排序。RBVM的基本流程如下:

RBVM的基本流程

1. 资产识别

  • 列出组织内的所有关键资产,包括硬件、软件、数据和网络等。
  • 对每个资产进行分类,如生产环境、测试环境、非关键业务系统等。
  • 确定每个资产的重要性级别,如高、中、低。

2. 漏洞发现

  • 使用自动化漏洞扫描工具对关键资产进行定期扫描。
  • 结合手动渗透测试,发现可能存在的未知漏洞。
  • 订阅外部漏洞情报服务,获取最新的漏洞信息。

3. 风险评估

  • 对每个发现的漏洞进行风险评估,包括严重性、可利用性、资产重要性等因素。
  • 使用脆弱性优先级技术(Vulnerability Prioritize Technology–VPT)、CVSS(Common Vulnerability Scoring System)或其他风险评估框架进行评分。
  • 结合威胁情报,评估漏洞被利用的可能性和潜在影响。

4. 优先级排序

  • 根据风险评估结果,将漏洞按照风险级别进行排序。
  • 优先处理高风险漏洞,确保关键资产的安全。
  • 对于中低风险漏洞,制定缓解措施或监控计划。

5. 修复和缓解

  • 对于高风险漏洞,立即制定修复计划并实施。
  • 对于无法立即修复的漏洞,制定缓解措施,如限制访问权限、部署防火墙规则等。
  • 与供应商和社区保持沟通,获取漏洞修复的最新信息。

6. 监控和重新评估

  • 定期对已修复的漏洞进行验证,确保漏洞已被成功修复。
  • 监控新的漏洞和威胁情报,及时更新风险评估和优先级排序。
  • 定期对RBVM流程进行审查和更新,以适应组织环境的变化。

RBVM策略将帮助我们优先处理那些对组织构成最大威胁的漏洞,从而在有限的资源下实现最大的安全效益。

四、RBVM的特点和优势

RBVM是一种更为精细化和策略性的方法,旨在识别、评估、优先级排序和修复那些对组织构成最大风险的漏洞。与传统的漏洞管理方法相比,RBVM具有以下区别:

  1. 风险评估:传统的漏洞管理通常侧重于发现和修复所有检测到的漏洞,而不考虑这些漏洞对组织的实际风险。而RBVM则根据漏洞的严重性、可利用性、资产的重要性以及威胁的紧迫性等因素,对漏洞进行风险评估,以确定修复的优先级。
  2. 资源优化:由于RBVM侧重于修复高风险的漏洞,因此它可以帮助组织更有效地分配资源。组织可以将有限的资源集中在那些对业务影响最大的漏洞上,而不是盲目地修复所有检测到的漏洞。
  3. 业务上下文:RBVM将漏洞管理置于业务上下文中,考虑漏洞对组织业务运营和战略目标的影响。这种方法使组织能够更清楚地了解哪些漏洞是需要优先处理的,以确保业务连续性和合规性。
  4. 持续监控和评估:RBVM强调对漏洞的持续监控和评估。它使用自动化工具和流程来跟踪漏洞的状态,包括已知漏洞的发布、修复和再利用等。此外,RBVM还利用威胁情报来识别新的攻击向量和漏洞,以便及时调整漏洞管理策略。
  5. 威胁情报整合:RBVM将威胁情报与漏洞管理相结合,以便更准确地评估漏洞的风险。威胁情报提供了关于攻击者行为、目标、工具和技术的信息,这些信息有助于组织识别那些可能被攻击者利用的漏洞。
  6. 优先级排序:RBVM使用风险评估结果来确定漏洞修复的优先级。这有助于组织在有限的资源下,优先处理那些对业务影响最大的漏洞。与传统的漏洞管理方法相比,这种方法更加灵活和有效。

RBVM的优点

  1. 资源优化:通过优先处理高风险的漏洞,RBVM可以帮助组织更有效地分配资源,确保在有限的资源下取得最大的安全效益。
  2. 业务连续性:RBVM关注业务上下文与业务的关联性强,确保在修复漏洞的过程中不会中断关键业务操作或影响用户体验。
  3. 提高安全性:通过持续监控和评估漏洞风险,RBVM可以帮助组织及时发现并修复潜在的安全隐患,提高整体安全性。

RBVM 在传统漏洞管理的基础上,进一步引入了资产攻击面、 漏洞情报、威胁情报等多维度数据 ,以脆弱性优先级技术(Vulnerability Prioritize Technology – VPT)对漏洞进行打分 与评级。如此一来,安全团队需要面对的“高危”、“严重”漏洞的范围与数量大大缩小,漏洞不再只是漏洞,而是与业务连续性、 资产关键性等联系更加紧密的脆弱点。安全团队可以更加主动地协调运维、业务部门率先修复高优先级的脆弱点。

RBVM 的主要价值在于,避免那些最需要优先被关注的可能带来巨大风险的漏洞淹没在海量漏洞告警中,转而让安全团队能够优先考虑优先级更高的关键漏洞。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

django.db.utils.operationalerror:\xF0\x9F\x9....报错解决办法

发表于 2024-05-30 | 更新于: 2025-07-27 | 分类于 技术 , 开发 | | 阅读次数:
字数统计: 280 | 阅读时长 ≈ 1

一、问题现象

今天在迁移hexo的.md博客文件到django的博客程序的时候报错了,具体报错信息如下。

1
2
3
4
5
6
7
8
9
File "D:\project\pythonproject\work\ishareblog\venv\lib\site-packages\django\db\backends\mysql\base.py", line 75, in execute
return self.cursor.execute(query, args)
File "D:\project\pythonproject\work\ishareblog\venv\lib\site-packages\MySQLdb\cursors.py", line 206, in execute
res = self._query(query)
File "D:\project\pythonproject\work\ishareblog\venv\lib\site-packages\MySQLdb\cursors.py", line 319, in _query
db.query(q)
File "D:\project\pythonproject\work\ishareblog\venv\lib\site-packages\MySQLdb\connections.py", line 254, in query
_mysql.connection.query(self, query)
django.db.utils.OperationalError: (1366, "Incorrect string value: '\\xF0\\x9F\\x98\\x82\\xE5\\x93...' for column 'content' at row 1")

报错现象

后来发现是因为.md文件中的内容含有mysql不能识别4个字节的utf8编码的字符,抛出了异常。类似于这种4个字节☺、��

报错原因

二、解决办法

1、修改mysql的数据库的字符集和的字符集

修改数据库的字符集为utf8mb4

在这里插入图片描述

修改字段的字符集为utf8mb4

在这里插入图片描述

2、在djanog的mysql连接串属性中加入字符集为utf8mb4的设置

在这里插入图片描述

修改完成后,插入正常。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

什么是安全左移,如何实现安全左移

发表于 2024-05-24 | 更新于: 2025-07-27 | 分类于 技术 , 网络安全 , 管理 , 项目管理 | | 阅读次数:
字数统计: 3.3k | 阅读时长 ≈ 11

一、传统软件开发面临的安全挑战

传统软件开发面临的安全挑战主要包括以下几个方面:

  1. 安全意识和文化的缺乏:在传统软件开发过程中,往往缺乏对安全性的足够重视和深入理解。开发团队可能更注重功能的实现和交付时间,而忽视了安全性在软件开发过程中的重要性。这种缺乏安全意识和文化的环境使得软件容易受到各种安全威胁的攻击。
  2. 代码漏洞和缺陷:在软件开发过程中,由于人为错误、不安全的编程实践或缺乏足够的安全测试,代码中可能存在各种漏洞和缺陷。这些漏洞和缺陷可能被攻击者利用,从而实现对系统的未授权访问、数据泄露或系统破坏等攻击。
  3. 依赖的第三方组件和库的安全问题:传统软件开发通常依赖于各种第三方组件和库来加速开发过程。然而,这些第三方组件和库可能存在安全漏洞或已知的安全问题,如果未经过充分的安全验证和测试,就可能会被引入到软件中,从而给软件带来安全隐患。
  4. 安全的配置和管理:在软件开发和部署过程中,如果配置不当或管理不善,可能会导致安全问题。例如,错误的权限设置、不安全的网络连接、未加密的敏感数据等,都可能成为攻击者的目标。
  5. 应对新型攻击和威胁的能力不足:随着网络攻击技术的不断发展和新型威胁的出现,传统软件开发可能无法及时应对这些新型攻击和威胁。例如,零日漏洞、勒索软件、分布式拒绝服务攻击(DDoS)等新型攻击手段可能给软件带来严重的安全风险。

为了应对这些安全挑战,传统软件开发需要采取一系列措施来加强安全性。这包括提高开发团队的安全意识和技能、加强代码审查和测试、对第三方组件和库进行充分的安全验证和测试、实施安全的配置和管理等。此外,还可以引入安全左移等新的安全开发方法,将安全性作为软件开发的核心考量因素,从源头上降低安全风险。

二、什么是安全左移

在传统的软件开发流程中,安全测试和评估通常在开发周期的后期进行,比如在测试阶段或部署前。然而,这种方法往往会导致在产品即将发布时才发现安全问题,从而增加了修复成本和风险。

安全左移(Shift-Left Security)是一种软件开发实践,其核心思想是将安全措施提前到软件开发生命周期(SDLC)的更早阶段。安全左移的目标是在软件开发的早期阶段,甚至是在编码之前,就开始考虑和实施安全措施。这样,潜在的安全问题可以在它们变得更加根深蒂固和难以修复之前被发现和解决。

三、安全左移与安全开发生命周期(SDL)

安全左移(Shift-Left Security)与安全开发生命周期(SDL, Security Development Lifecycle)紧密相关,是SDL中的一个重要概念。

安全左移是一种在软件开发过程的早期阶段就引入安全考虑的实践,旨在帮助开发人员在代码被集成、测试、记录甚至发布之前,就能发现潜在的安全风险。这种方法的目标是提高安全任务的效率,并确保这些必要的任务不会遗留到开发周期结束。

SDL

SDL由微软提出并应用一个帮助开发人员构建更安全的软件和解决安全合规要求的同时降低开发成本的软件开发过程,侧重于软件开发的安全保证过程,旨在开发出安全的软件应用。其核心理念是将安全考虑集成在软件开发的每一个阶段,包括需求分析、设计、编码、测试和维护。SDL的目标是通过在各个阶段都增加相应的安全活动,来减少软件中漏洞的数量并将安全缺陷降低到最小程度。

在安全左移与SDL的关系中,可以认为安全左移是SDL的一种实践方式。也就是说,通过实施安全左移,可以将SDL的理念和方法更好地应用到实际的软件开发过程中。安全左移强调在软件开发早期阶段就引入安全考虑,这与SDL将安全考虑集成在软件开发每个阶段的目标是一致的。

四、安全左移对开发的挑战

安全左移对开发带来的挑战主要体现在以下几个方面:

  1. 缺少计划:在实践安全左移过程中,缺少合理的规划和计划是最大的挑战之一。安全左移需要建立起安全意识与责任感,并将安全融入到开发团队的工作流程中。然而,由于缺乏明确的计划,许多企业仅仅执意进行左移,却忽略了安全规范和流程的制定,从而导致安全措施的不完善和应对漏洞的能力不足。
  2. 安全责任转嫁:安全左移的核心思想是将安全的责任从专门的安全团队转嫁给开发团队,让开发人员在软件开发过程中就能够考虑和实施必要的安全措施。然而,这种转嫁过程并不容易。开发团队通常关注的是项目的进度和功能的实现,对于安全知识和安全技能的掌握相对较弱。因此,企业需要通过定期的培训和教育,提高开发团队的安全意识和能力,使他们能够主动参与到安全左移的实践中。
  3. 技术和工具的选择:安全左移需要借助各种技术和工具来辅助实施,如自动化测试工具、安全扫描工具等。然而,如何选择和使用这些工具也是一个挑战。不同的工具有不同的适用场景和优缺点,需要根据实际情况进行选择和调整。同时,如何将这些工具与现有的开发流程相结合,也是需要考虑的问题。
  4. 平衡安全与进度:在安全左移的过程中,需要平衡安全与进度的关系。一方面,需要确保软件的安全性,避免潜在的安全漏洞和风险;另一方面,也需要保证项目的进度和交付时间。如何在保证安全的前提下,尽可能地提高开发效率,是安全左移需要解决的一个问题。
  5. 跨团队协作:安全左移需要跨团队协作,包括开发团队、安全团队和运维团队等。如何确保这些团队之间的有效沟通和协作,避免信息孤岛和沟通障碍,也是安全左移需要面临的挑战之一。

为了应对这些挑战,企业可以采取以下措施:

  1. 制定全面的计划和策略:明确安全左移的目标和步骤,制定详细的计划和策略,确保安全左移的顺利实施。
  2. 加强培训和教育:提高开发团队的安全意识和能力,使他们能够更好地参与到安全左移的实践中。
  3. 选择合适的技术和工具:根据实际需求选择合适的技术和工具,确保它们能够有效地辅助安全左移的实施。
  4. 平衡安全与进度:在制定开发计划时充分考虑安全因素,确保在保证安全的前提下尽可能地提高开发效率。
  5. 加强跨团队协作:建立良好的沟通机制和协作机制,确保各个团队之间的有效沟通和协作。

五、从DevOps到DevSecOps

随着对软件安全性的要求不断提高,传统的DevOps模式开始面临挑战。什么是DevOps见《研发管理之认识DevOps》。为了确保软件在开发过程中的安全性,需要在DevOps的基础上引入安全性的考虑,这就是DevSecOps(Development,Security,Operations的组合词)的出现。

从DevOps到DevSecOps

DevSecOps是一种集开发、安全和运维于一体的新型软件开发和运营模式。它强调在快速迭代和持续交付的背景下,将安全性融入到整个软件开发过程中,实现开发、安全和运维的协同和一体化。在DevSecOps模式下,安全性不再是软件开发过程的一个附加环节,而是贯穿于整个开发流程中,从需求分析、设计、编码、测试到部署和维护的每个阶段都需要考虑安全性。

与DevOps相比,DevSecOps具有以下优势:

  1. 提高安全性:通过在开发过程中引入安全性的考虑,DevSecOps可以更早地发现和修复潜在的安全漏洞和缺陷,从而提高软件的安全性。
  2. 加速开发过程:DevSecOps通过自动化和标准化的安全流程,可以减少手动测试和修复安全漏洞的时间,从而加速开发过程。
  3. 提高团队协作效率:DevSecOps强调开发、安全和运维团队之间的紧密协作,可以提高团队协作效率,促进知识的共享和交流。

DevSecOps

DevSecOps和DevOps在理念和实践上存在一些关键的区别,主要体现在以下几个方面:

  1. 安全性集成:DevSecOps是“开发、安全和运营”的缩写,它强调在快速迭代和持续交付的背景下,将安全性融入到整个软件开发过程中。这意味着安全性是DevSecOps的一个核心组成部分,从软件开发的早期阶段就开始考虑并贯穿整个开发流程。而DevOps则更侧重于促进开发(Dev)和运维(Ops)团队之间的沟通与协作,虽然也关注安全性,但通常不是其首要关注点。
  2. 安全性防护:DevSecOps强调将安全性作为整个IT生命周期的共同责任,通过应用和基础架构的安全防护来确保软件的安全性。这包括在开发阶段进行安全编码实践、安全测试和漏洞扫描等,以及在运维阶段进行安全监控和事件响应等。而DevOps则更关注于提高开发和运维的协同效率,以及通过自动化和持续集成等技术手段来加速软件交付。
  3. 安全性实践:DevSecOps在安全性实践方面更加深入和全面,它要求在软件开发的全过程中都遵循安全最佳实践,包括安全需求分析、安全设计、安全编码、安全测试、安全部署和安全运维等。而DevOps虽然也关注安全性,但通常不会深入到这些具体的实践层面。
    DevSecOps和DevOps都是为了提高软件开发和运维的效率和质量而出现的理念和实践,但它们在安全性方面的关注度和实践方式有所不同。DevSecOps更加强调安全性的重要性,并将其作为整个软件开发和运维流程的核心组成部分,而DevOps则更侧重于促进开发和运维团队之间的沟通与协作,以及通过自动化和持续集成等技术手段来加速软件交付。

六、SDL与DevSecOps

SDL(Security Development Lifecycle)和DevSecOps(Development Security Operations)都是旨在加强软件开发过程中的安全性的方法论。SDL提供了一种系统化的方法来集成安全实践,而DevSecOps则提供了一种文化和实践框架,以支持SDL的安全实践在DevOps环境中的实施,DevSecOps可以看作是SDL在现代敏捷和DevOps环境中的扩展和适应,它强调了自动化和持续集成的重要性。

  • SDL更侧重于安全措施的过程化和文档化,而DevSecOps更侧重于安全文化的推广和自动化工具的应用。
  • 在SDL中,安全责任可能更侧重于安全团队,而在DevSecOps中,安全是开发、安全、运维团队共同的责任。
  • SDL可能更适合大型、长期、需求变化不频繁的项目,而DevSecOps更适合快速迭代、需求不断变化的环境。

博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

DevOps实践:通过云效实现hexo自动构建部署发布

发表于 2024-05-20 | 更新于: 2025-07-27 | 分类于 技术 , 开发 , 管理 , 项目管理 | | 阅读次数:
字数统计: 1.7k | 阅读时长 ≈ 5

DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。这是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例,是一个软件开发方法论。
DevOps

DevOps的目标是通过自动化“软件交付”和“架构变更”的流程,使得构建、测试、发布软件能够更加地快捷、频繁和可靠。这种方法的出现是因为软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发和运维工作必须紧密合作。

《研发管理之认识DevOps》介绍了DevOps的概念和价值,本文我们来做一个小小的实践,通过阿里的云效DevOps平台来实现我的xiejava.ishareread.com的hexo博客网站的自动化部署实现持续集成和持续部署(CI/CD)。

一、没有用DevOps之前

我的xiejava.ishareread.com个人博客是通过hexo搭建的。因为是个人博客用hexo搭建比较简单而且还可以通过github的pages服务不用购买自己的服务器都可让大家访问。hexo博客的搭建教程见《通过Git Pages+Hexo搭建自己的博客》毕竟github在国内访问不是很稳定,所以我是购买了一台阿里的云主机ECS来部署我的hexo应用。

在没有用DevOps之前,我是通过自己本地用VS Code来编辑hexo的markdown博文,让后在本地通过执行hexo g命令生成构建hexo网站,然后登录到自己的阿里云主机上将文件传到服务器目录,再在服务器上构建并部署hexo应用。hexo很简单,这样构建发布也很容易,这几年也是这样做的。唯一不方便的就是有时候写的博文要修改,改一次就要重新上传发布到服务器上。毕竟咱也是搞IT的,感觉这样还是很不优雅。能不能提交markdown博文的.md文件候就自动给我发布了,省得每次还要登服务器,上传文件,编译,重启服务搞一系列的操作。

二、用了DevOps之后

为了体现程序员的优雅,我的个人博客xiejava.ishareread.com,小小的hexo用上了DevOps的牛刀。
hexo的DevOps

用了DevOps后,我只需要在本地用VS Code来编辑hexo的markdown博文,提交到代码仓库。后面的构建,打包,部署我都不用管了,直接通过DevOps平台自动做了。

三、如何实践DevOps实现hexo网站的自动发布

DevOps平台工具有很多,最常见的就是大名顶顶的Jenkins,本来想搭一套Jenkins的我的云服务器本来就只有1核1G的内存,更本就扛不住。后来在阿里的网站上看到阿里推出了自己的DevOps平台云效,赶紧来试试。
云效DevOps平台功能很多,包括有项目协作、代码管理、流水线、测试管理、制品仓库、知识库。
在这里插入图片描述

对于我这个小小的hexo个人博客的应用,其他都用不上,我就用了代码管理和流水线。

1、代码管理

将我的hexo博客代码托管到云效的代码管理Codeup代码库。
代码管理

可以支持从github、gitee、coding等代码仓库导入代码库。
云效从其他代码库导入代码

2、构建流水线

新建一条流水线用于拉代码、构建、部署。
构建流水线

1)设置流水线源

这里流水线源就是设置代码源流水线将从代码管理的Codeup代码库获取代码。其实也可以不用托管到云效的Codeup代码库,流水线代码源支持github、gitee、coding等代码仓库。
设置流水线源

2)添加并设置构建环节

云效添加构建环节

因为hexo是通过Node.js构建的,所以这里添加构建任务Node.js构建。
Nodejs构建写入自己的构建命令

在node.js构建节点,可以选择Node的版版本,将自己hexo应用的构建命令写入到构建命令。
我这里只用到了三条命令,安装hexo,从代码库中拉取hexo的主题,通过hexo g生成hexo的博客应用。

1
2
3
npm install -g hexo-cli --unsafe-perm
git clone https://gitee.com/xiejava/hexo-theme-next.git themes/hexo-theme-next
hexo g

这一步的任务输出就会生成hexo的博客应用的制品,给你打好包。下一步的动作就是将打好的制品包上传至自己的服务器进行部署。

3)添加并设置部署环节

云效支持很多种部署,因为我是要部署到自己的云主机,所以选择“主机部署”
添加主机部署环节

要部署到自己的云主机,就要让云效知道你的主机在哪里,可以点击新建主机组,将自己的主机添加进来。
新建主机组

将需要部署的主机添加到主机组
将主机添加到主机组

然后添加在主机上执行的部署脚本
添加在主机上执行的部署脚本

我在主机上hexo 服务进程用supervisor进行了托管,在这里部署脚本只有三条命令。

1
2
3
supervisorctl stop hexo
tar zxvf /home/admin/app/package.tgz -C /home/myhexo/myblog
supervisorctl start hexo

停止hexo的服务,将制品包解压到hexo的目录,然后再启动hexo服务就可以了。
云效还可以定义任务插件,比如在部署成功后发个邮件通知等。
云效邮件通知

到这里流水线就构建好了。

3、测试流水线

设置触发条件,开启Webhook触发,实现提交代码到到云效的Codeup代码库就可以触发流水线,开启自动拉取代码,自动构建、自动打包上传至主机、自动部署的流水线作业。也可以手动运行触发流水线。
设置触发条件

流水线执行后,可以在运行历史中看到每次流水线执行的情况。
查看每次流水线执行情况

也可以收到云效自动发过来的每次部署情况的邮件。
云效邮件通知

4、其他

云效针对流水线还提供了统计报表,可以看到流水线运行的概况的统计数据。
流水线运行的概况的统计数据

四、总结

本文通过一个hexo个人博客进行了DevOps的实践,当然因为项目太小不能实践到DevOps的全部,但也可以窥豹一斑。通过DevOps拉通开发和运维,通过应用DevOps平台能实现自动化“软件交付”的流程,使得构建、测试、发布软件能够更加地快捷、频繁和可靠,提交研发交付效率。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

研发管理之认识DevOps

发表于 2024-05-14 | 更新于: 2025-07-27 | 分类于 管理 , 项目管理 | | 阅读次数:
字数统计: 2.8k | 阅读时长 ≈ 9

一、什么是DevOps

DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。这是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例,是一个软件开发方法论。

DevOps

DevOps的目标是通过自动化“软件交付”和“架构变更”的流程,使得构建、测试、发布软件能够更加地快捷、频繁和可靠。这种方法的出现是因为软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发和运维工作必须紧密合作。

DevOps它强调开发(Development)和运维(Operations)之间的协作与交流,以提高软件交付的速度和质量。DevOps的核心目标是缩短系统开发生命周期,提供持续的交付和快速的反馈。

DevOps

二、DevOps的背景和起源

DevOps的背景和起源可以追溯到软件开发行业面临的一些挑战和变革。

在传统的软件开发模式中,设计、开发、测试、部署等各个环节往往是顺序进行的又叫瀑布开发模型,每个阶段都有明确的输出和交接点。然而,这种模式在面对快速变化的市场需求时显得捉襟见肘,无法满足用户对软件的快速迭代和持续交付的需求。因此,敏捷开发模式开始受到广泛关注。

敏捷开发强调跨部门、跨角色的协作与沟通,注重快速响应变化,持续交付价值。在这种模式下,开发团队被划分为多个小团队,每个小团队负责一部分功能或模块的开发,通过短周期的迭代开发方式,逐步实现软件的整体功能。这种模式有效地提高了软件开发的并行度和效率,但也给运维带来了新的挑战。

持续开发带来的运维问题主要包括:随着软件功能的不断增加和版本迭代速度的加快,运维工作面临巨大的压力。开发团队和运维团队之间往往存在沟通障碍,导致开发完成的功能在部署和运维阶段出现问题。此外,传统的软件开发模式通常将开发、IT运营和质量保障设为各自分离的部门,这种分工方式使得跨部门的协作变得困难,进一步加剧了运维问题。

开发模型演进

为了解决这些问题,DevOps应运而生。DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

DevOps的起源可以追溯到2009年左右,当时一些欧洲的IT企业开始尝试将开发和运维工作结合起来,以提高软件交付的效率和质量。随着这种模式的不断发展和完善,DevOps逐渐成为了全球软件开发行业的一种主流趋势。

DevOps将开发、测试、运维拉到同一战线,使产品持续开发、持续集成、持续测试、持续部署、持续监控,非常频繁地发布新版本。它的目标是打破开发和运维之间的壁垒,实现二者的紧密结合,从而提高软件交付的速度和质量。

开发模型之间的关系

如今,越来越多的企业开始采用DevOps模式来改进软件开发和运维流程,以应对快速变化的市场需求。

三、DevOps的特点和价值

DevOps(Development和Operations的组合),旨在通过自动化、协作和共享责任来提高软件开发和运维的效率、质量和安全性。以下是DevOps的主要特点和价值:

1、特点:

● 文化和团队协作:DevOps强调开发(Dev)和运维(Ops)团队之间的紧密合作,打破传统的壁垒,实现跨部门、跨角色的沟通和协作。
● 自动化:通过引入自动化工具和流程,DevOps可以显著减少手动操作,提高开发、测试、部署和运维的效率。
● 持续集成和持续部署(CI/CD):CI/CD是DevOps的核心实践之一,通过自动化构建、测试和部署流程,实现快速迭代和交付。
● 监控和反馈:DevOps强调对系统性能、安全性和可靠性的实时监控,以便及时发现问题并进行修复。同时,通过收集和分析数据,为决策提供支持。
● 共享责任:在DevOps文化中,开发和运维团队共同承担系统质量、稳定性和可靠性的责任,共同推动系统的持续改进和优化。

2、价值:

● 提高交付速度和质量:通过自动化和协作,DevOps可以显著缩短开发周期,提高交付速度。同时,通过引入持续集成和持续部署等实践,可以确保代码质量和系统稳定性。
● 降低运维成本:通过自动化运维流程和工具,DevOps可以降低手动操作的错误率,减少故障恢复时间,从而降低运维成本。
● 增强安全性:DevOps强调对系统性能的实时监控和数据分析,有助于及时发现潜在的安全风险并进行修复。此外,通过引入自动化安全测试和审计工具,可以确保系统的安全性。
● 提高客户满意度:通过快速迭代和交付高质量的产品,DevOps可以满足客户的不断变化的需求,提高客户满意度。
● 促进创新和竞争力:DevOps鼓励团队之间的协作和创新,有助于推动组织的持续创新和竞争力提升。

DevOps将开发、测试、运维拉到同一战线,使产品持续开发、持续集成、持续测试、持续部署、持续监控,非常频繁地发布交付新版本。对比前面所说的瀑布式开发和敏捷开发,我们可以明显看出,DevOps贯穿了软件全生命周期。下面这张图,更明显地说明了DevOps所处的位置,还有它的价值:

devops价值

DevOps通过打破传统壁垒、引入自动化和协作实践、实时监控和数据分析等手段,为组织带来了诸多价值和优势。随着技术的不断发展和市场需求的变化,DevOps将在未来继续发挥重要作用。

四、DevOps如何帮助提高软件交付速度和质量

DevOps通过一系列的实践和工具,帮助提高软件交付速度和质量,具体体现在以下几个方面:

  1. 持续集成(CI)和持续交付(CD):
    ○ DevOps强调将代码集成到主干开发分支中,并经过自动化测试,以确保每次提交的代码都是可靠的。持续集成使得开发人员能够频繁地将代码变更集成到共享的主干代码库中,并通过自动化测试和构建流程进行验证。
    ○ 持续交付则通过自动化构建、测试和部署软件,实现快速、可靠的交付流程。当代码通过所有测试后,可以自动部署到生产环境,从而大大缩短了发布周期。
  2. 自动化测试:
    ○ 自动化测试是DevOps中不可或缺的一部分。通过自动化测试,可以快速、高效地发现和解决代码问题,同时减少手动测试的工作量。常见的自动化测试包括单元测试、集成测试和端到端测试等。这些测试可以在代码集成阶段就进行,从而确保代码的质量。
  3. 自动化部署和配置管理:
    ○ DevOps强调自动化部署和配置管理,通过使用工具和脚本自动化应用程序的部署和配置过程。自动化部署可以减少人为错误,确保发布过程的一致性和可重复性。配置管理工具则可以帮助团队管理应用程序的配置,提供版本控制和自动化更新,使得应用程序的部署和配置变得更加灵活和可管理。
  4. 实时监控和快速响应:
    ○ 通过实时监控,DevOps可以及时发现和解决系统问题,减少系统故障和停机时间。同时,快速响应也可以保证在系统出现问题时能够快速解决问题,进一步减少停机时间。这种实时监控和快速响应的机制有助于确保软件的稳定性和可用性。
  5. 文化与团队协作:
    ○ DevOps强调跨团队协作和沟通,打破了传统开发和运维之间的壁垒。它鼓励团队成员共同制定标准、流程和最佳实践,共享知识和经验,从而提高团队整体的工作效率。这种文化和团队协作的氛围有助于加快软件开发和交付的速度。
  6. 反馈迭代循环:
    ○ DevOps通过建立反馈循环,将客户反馈和业务需求纳入产品迭代计划中。这使得团队能够更快地响应客户需求和变化,不断改进产品以满足市场的期望。这种反馈循环有助于确保软件交付的质量符合客户和业务的需求。

DevOps通过持续集成、自动化测试、自动化部署和配置管理、实时监控和快速响应、文化与团队协作以及反馈循环等手段,帮助提高软件交付速度和质量。这些实践和工具使得软件开发和交付过程更加高效、可靠和灵活。以下是DevOps生态圈中的一些工具,企业可以通过集成这些工具搭建自己的DevOps平台。

devops生态圈

在DevOps的流程下,运维人员会在项目开发期间就介入到开发过程中,了解开发人员使用的系统架构和技术路线,从而制定适当的运维方案。而开发人员也会在运维的初期参与到系统部署中,并提供系统部署的优化建议。除了平台工具外,想要将DevOps真正落地,首先第一点,是思维转变,DevOps并不仅仅是组织架构变革,更是企业文化和思想观念的变革。根据DevOps思想重新梳理全流程的规范和标准。


博客地址:http://xiejava.ishareread.com/


“fullbug”微信公众号

关注:微信公众号,一起学习成长!

<1234…21>
XieJava

XieJava

205 日志
12 分类
26 标签
RSS
GitHub
友情链接
  • 爱分享读书
  • CSDN
  • 豆瓣
© 2025 XieJava | Site words total count: 422k

主题 — NexT.Muse
0%