惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
有赞技术团队
有赞技术团队
博客园 - 司徒正美
量子位
N
News and Events Feed by Topic
T
Threatpost
Last Week in AI
Last Week in AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
人人都是产品经理
人人都是产品经理
T
Tenable Blog
IT之家
IT之家
雷峰网
雷峰网
腾讯CDC
博客园 - 聂微东
V
Visual Studio Blog
S
SegmentFault 最新的问题
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
N
Netflix TechBlog - Medium
J
Java Code Geeks
宝玉的分享
宝玉的分享
F
Full Disclosure
WordPress大学
WordPress大学
A
Arctic Wolf
小众软件
小众软件
AWS News Blog
AWS News Blog
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
AI
AI
Hugging Face - Blog
Hugging Face - Blog
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
N
News | PayPal Newsroom
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 开发 Tips(5)
如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
微信公众号 · 2018-11-25 · via 陈少文的网站

大公司的程序员,容易产生的错觉之一就是,误将平台能力当作自己的能力。在大团队,我们不应仅关注自己的一亩三分地,更需要了解平台的各个环节。一方面,有助于更好地利用平台相关特性,另一方面,也为了自我技术更好地成长。本文,介绍了如何使用 Jekins、Docker、GitLab 搭建 Django 自动化开发部署流程。相关工具都是开源、可以拿来即用的。

1. 开发流程

在生产环境,Web 应用采用的是 K8S 多实例部署,状态服务 MySQL、RabbitMQ 采用的是集群部署。同时,还搭建了监控和日志采集、检索等周边。

相比较于生产环境,这里的开发流程,我希望能尽量模拟生产环境,但也不需要太完善。毕竟,个人的时间和精力有限,当有需求时,逐步完善是一个不错的选择。

这里使用 GitLab 作为开发仓库,使用 Jenkins 作为自动化引擎,部署使用的是 Docker 镜像。

下面是一个简单的部署流程:

当满足触发条件时,Jenkins 就会自动从 GitLab 拉取代码,制作 Docker 镜像,最终在服务器上运行 Django 实例。这样基本就可以,模拟整个部署流程。

2. GitLab 配置

选用 GitLab 是因为,其允许创建私有仓库。

  • 创建仓库

首先得创建一个 GitLab 仓库,例如: ProjectA

  • 添加远程访问 SSH-KEY

在本地执行命令,生成远程访问需要的 SSH-KSY

1
ssh-keygen -o -t rsa -b 4096 -C "mail@chenshaowen.com"

https://gitlab.com/profile 页面,找到 【SSH Keys】,添加上面生产的 Key 值。

  • 生成个人仓库访问的 Token

https://gitlab.com/profile 页面,找到 【Access Tokens】,完善信息,点击生成 PersonToken。

3. Jenkins 配置

Jenkins 可以对外直接提供 API ,也支持插件扩展。对于熟悉 Java 的团队,Jenkins 具有很强的吸引力。使用 Jenkins ,可以满足 CI、CD 各种各样的需求。

这里的 Jenkines 主要用于部署服务。通过接收 GitLab 发送的提交信息,拉取最新的代码,执行脚本,完成部署。

  • 安装 GitLab 相关插件

这里需要使用到的 Jenkins 插件主要有:

  1. Gitlab Authentication plugin
  2. Gitlab Hook Plugin
  3. Gitlab Plugin

在 【Jenkins】-> 【插件管理】 里面,搜索并安装插件,重启 Jenkins 生效。

  • 添加 Gitlab 访问凭据

在 【Jenkins】-> 【凭据】-> 【系统】-> 【全局凭据 (unrestricted)】 中,【添加凭据】 ,类型选择 【Gitlab API token】,API token 即为在第二章节中生成的 PersonToken。

  • 新建流水线,配置仓库

创建【构建一个自由风格的软件项目】,如上图填入项目 ProjectA 的仓库地址。点击新增 SSH-Key 访问凭证。

  • 配置 Jenkins 触发规则

如上图,在 【Build Triggers】中,勾选 【Build when a change is pushed to GitLab. GitLab webhook URL:】,获取 GitLab Webhook 地址。点击【Advanced】,生成 Token。

  • GitLab 配置 Webhook

上一步中,获取到了两个值,GitLab Webhook 和 Token。

如上图,在 Gitlab 项目仓库 【Settings】-> 【Integrations】 中填入相关信息。如果你的 Webhook 不是 https 链接,还需要去掉 【Enable SSL verification】 勾选。

  • Jenkins 构建配置

构建配置实际上就是 Jenkins 拉取仓库代码之后,执行的脚本命令。这里直接执行项目下的 start.sh 脚本,即可。

4. Docker 镜像制作

使用 Docker 进行部署,有利于打包环境依赖,对服务进行水平扩展。在生产环境中,通常会采用多实例+集群的方式进行部署,以保障服务高可用。

这里主要使用 docker-compose 编译镜像,编排 Django 运行时需要的容器。

上图是整个仓库的目录结构,分为四个部分。

4.1 Django 项目代码

Django 使用的是默认目录结构,有两点需要注意:

  1. 通过环境变量,区分不同环境

在启动时,需要传入一个环境变量,提供给 Django 区分环境。在 settings.py 文件中:

1
2
3
4
if os.getenv('Env') == 'Production':
    DEBUG = False
else:
    DEBUG = True
  1. Django DEBUG=False 模式下,无法转发静态文件,需要配置 WhiteNoise
1
2
3
4
5
6
7
8
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfile')
#
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
WHITENOISE_STATIC_PREFIX = '/static/'
MIDDLEWARE.append('whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'

4.2 数据存放

通过卷的形式,将数据目录挂载到 Docker,可以保存运行状态,避免因容器重启而丢失数据。

4.3 镜像配置

Python 镜像,需要安装基本的依赖包。

Dockerfile 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
FROM python:3.7-alpine

RUN apk update \
    && apk add --no-cache --virtual bash \
    && apk add gcc \
    && apk add musl-dev \
    && apk add linux-headers \
    && apk add jpeg-dev \
    && apk add zlib-dev \
    && apk add mariadb-dev \
    && apk add libffi-dev

COPY requirements.txt /requirements.txt
RUN pip install --upgrade pip \
    && pip install -r requirements.txt \
    && rm /usr/bin/mysql*

RUN mkdir /code
WORKDIR /code

requirements.txt 文件

1
2
3
4
5
6
7
8
9
django==2.1.2
gunicorn==19.9.0
mysqlclient==1.3.13
pymysql==0.9.2
whitenoise==4.1.2
celery==4.2.1
django-celery-results==1.0.4
django-celery-beat==1.3.0
redis==2.10.6

MySQL 镜像,用于提供 DB 访问服务。

Dockerfile 文件:

1
2
FROM mysql:5.7
COPY my.cnf /etc/mysql/conf.d/my.cnf

my.cnf 文件:

1
2
3
4
[mysqld]
character-set-server=utf8
[client]
default-character-set=utf8
  • 容器编排

这里复用了 Python 镜像,提供 django 和 celery 容器环境。Django 通过 gunicorn 启动。

 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
version: '2'
services:
  python:
    build: ./docker/python
    container_name: django
    ports:
      - 7900:7900
    volumes:
      - ./code:/code
    command: >
      bash -c "pip install -r requirements.txt
      && python manage.py migrate
      && python manage.py collectstatic --no-input
      && gunicorn news.wsgi -b 0.0.0.0:7900"
    environment:
      - Env=Production
    depends_on:
      - mysql
      - redis
      - rabbitmq
      - celery
      - mongo
    networks:
      - django-networks

  mysql:
    build: ./docker/mysql
    container_name: mysql
    ports:
      - 3306:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=news
    networks:
      - django-networks

  redis:
    image: redis:latest
    container_name: redis
    expose:
      - "6379"
    networks:
      - django-networks

  rabbitmq:
    image: rabbitmq:3-management
    container_name: rabbitmq
    environment:
        - RABBITMQ_DEFAULT_USER=guest
        - RABBITMQ_DEFAULT_PASS=guest
    ports:
        - "5673:5673"
    networks:
      - django-networks

  celery:
    build: ./docker/python
    container_name: celery
    environment:
      - Env=Production
    depends_on:
        - rabbitmq
        - mysql
    volumes:
        - ./code:/code
    command: >
      bash -c "pip install -r requirements.txt
      && celery -A news.celery worker -l INFO
      && celery -A news.celery beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler"
    networks:
        - django-networks

  mongo:
    image: mongo:latest
    container_name: mongo
    ports:
      - "27018:27017"
    volumes:
      - ./data/mongo:/data/db
    networks:
      - django-networks

networks:
  django-networks:
    driver: "bridge"

start.sh 脚本,用于编译镜像,重启容器。

1
2
3
4
#!/bin/bash
docker-compose build
docker-compose stop
docker-compose up -d

5. 运行测试

向 Gitlab 仓库提交代码之后,Jenkins 流水线自动触发执行。

至此,通过 7900 端口就可以访问服务了。如果,需要绑定域名,新增一条 Nginx Server 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
     listen 80;
     server_name yourdomain.com;

     location / {
         proxy_pass http://127.0.0.1:7900;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     }
}

通过触发 Celery 后台任务,可以确定 Celery、RabbitMQ、MySQL 都能正常提供服务。

6. 与生成环境的对比

  • 高可用

高并发的关键是无状态,利用集群为状态提供高性能、高可用的服务。这里的 MySQL、RabbitMQ、Redis 都是单实例,生产环境需要采用集群部署。

另一方面,采用单机单实例部署是十分不可靠的,最好能使用多机多实例部署。

  • 运行日志

对于线上服务,日志是审计和排查错误的重要信息。使用 ELK + Filebeat 采集不同链路阶段、不同级别的日志,并将其按时间顺序串起来,十分有必要。

  • 运行隔离

生产环境的一台主机可能会运行很多实例,需要对每个实例使用的资源,CPU、内存、IO 等进行隔离,避免相互影响。

  • 服务注册

这里我们通过 Nginx 新增一条 Server 配置,来新增一个服务。这里可以,通过 Etcd + Confd 自动化这一流程,可以参考之前的一篇文章。 如果采用 K8S,借助于 Ingress 能实现类似效果。

  • 监控

生产环境,当然也少不了对各种服务状态的监控和告警。可以使用 Prometheus + Grafana 等开源监控工具,快速搭建监控系统。