步骤 11: 对代码进行分支

5.0 version
Maintained

对代码进行分支

在项目中有很多方式来组织代码修改的流程。但是直接工作在 Git 的 master 分支上,并且不经测试直接部署到生产环境中,这很可能不是最好的方案。

测试并不仅仅关于单元测试或功能测试,它也应该包括可以用生产数据来检查应用程序的行为。如果你或你周围的 利益相关者 可以像部署完毕后普通用户那样试用应用程序,这会是一个巨大的优势,它可以让你充满信心地进行部署。当非技术人员也能去确认新功能时,这一点就显得尤为强大。

出于简化和避免重复的考虑,在下面的步骤中,我们仍然会在 Git 的 master 分支上进行所有工作,但是我们先来看看如何使用一个更好的方案。

采用一个 Git 工作流

一个可行的工作流是对每个新功能或问题修复都新建一个分支。这个方案简单有效。

描述你的基础设施

把软件环境的描述存储在文件中,和代码放在一起,这非常有用,可能你都还没有意识到这点。Docker 和 SymfonyCloud 用配置文件来描述项目会用到的软件环境。当一个新功能需要一个额外服务时,代码的改动和软件环境的改动会是同一个补丁的一部分。

创建分支

这个工作流从创建一个 Git 分支开始:

1
$ git branch -D sessions-in-redis || true
1
$ git checkout -b sessions-in-redis

这个命令从 master 分支创建了名为 sessions-in-redis 的分支。代码和软件环境的配置从此 分为 两个分支。

把会话数据(session)存放在 Redis 里

你可能从分支名已经猜到了,我们想要把会话数据的存储从文件系统切换为 Redis。

实现这个切换的步骤很典型:

  1. 创建一个 Git 分支;
  2. 如有需要,更新 Symfony 配置;
  3. 如有需要,增加或修改一些代码;
  4. 更新 PHP 配置(增加 PHP 的 Redis 扩展);
  5. 更新 Docker 和 SymfonyCloud 的软件环境配置(增加 Redis 服务);
  6. 在本地测试;
  7. 在远程测试;
  8. 把这个分支合并到 master;
  9. 部署到生产环境;
  10. 删除这个分支;

第 2 步到第 5 步可以在一个补丁里;

patch_file
 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
--- a/.symfony.cloud.yaml
+++ b/.symfony.cloud.yaml
@@ -15,8 +15,13 @@ runtime:
 build:
     flavor: none

+variables:
+    php-ext:
+        redis: 5.3.1
+
 relationships:
     database: "db:postgresql"
+    redis: "rediscache:redis"

 web:
     locations:
--- a/.symfony/services.yaml
+++ b/.symfony/services.yaml
@@ -2,3 +2,6 @@ db:
     type: postgresql:11
     disk: 1024
     size: S
+
+rediscache:
+    type: redis:5.0
--- a/config/packages/framework.yaml
+++ b/config/packages/framework.yaml
@@ -7,7 +7,7 @@ framework:
     # Enables session support. Note that the session will ONLY be started if you read or write from it.
     # Remove or comment this section to explicitly disable session support.
     session:
-        handler_id: null
+        handler_id: '%env(REDIS_URL)%'
         cookie_secure: auto
         cookie_samesite: lax

--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -8,3 +8,7 @@ services:
             POSTGRES_PASSWORD: main
             POSTGRES_DB: main
         ports: [5432]
+
+    redis:
+        image: redis:5-alpine
+        ports: [6379]

难道不是很漂亮吗?

“重启” Docker 来启动 Redis 服务:

1
2
$ docker-compose stop
$ docker-compose up -d

请你打开本地网站来测试。因为我们没有做页面展示上的改动,而且还没有开始用会话功能,所以一切应该像之前一样正常运行。

部署一个分支

在部署到生成环境之前,我们必须在和生成环境一样的软件配置中测试这个分支。我们也需要确保在 Symfony 的 prod 环境中一切正常运行(本地网站则使用了 Symfony 的 dev 环境)。

首先,确保把你的改动提交到新分支:

1
2
$ git add .
$ git commit -m'Configure redis sessions'

现在,让我们来根据 Git 分支 创建一个 SymfonyCloud 环境

1
$ symfony env:delete sessions-in-redis --no-interaction
1
$ symfony env:create

这个命令创建了一个新的环境,如下所示:

  • 这个分支从当前的 Git 分支(sessions-in-redis)集成了代码和软件环境配置;
  • 这个分支的数据来自主分支环境(也就是生产环境)所有服务数据的快照,包括文件(比如用户上传的文件)和数据库内容;
  • 一个专用的集群会被创建,用来部署代码,数据还有软件环境。

这里的部署和部署到生产环境的步骤是一样的,所以数据库结构迁移也会被执行。这是验证数据库结构迁移是否可以在生产数据上工作的好方法。

这些非 master 环境和 master 环境非常相似,除了一些小的差异:比如,默认情况下非 master 环境不发送邮件。

当部署完成后,用浏览器打开这个新分支的页面:

1
$ symfony open:remote

请注意所有的 SymfonyCloud 命令在当前 Git 分支上都可以运行。上面的操作会打开 sessions-in-redis 分支上部署的 URL,它的形式类似 https://sessions-in-redis-xxx.eu.s5y.io/

在新环境中测试下网站,你应该会看到在主环境中添加的所有数据。

如果你在 master 环境下新建更多的会议,它们不会出现在 sessions-in-redis 环境下,反过来也一样。这些环境是互相独立和隔离的。

如果 master 分支上的代码继续不断更新,你总是可以对 Git 分支进行 rebase,然后部署更新的版本,从而解决代码和软件环境配置的冲突。

你甚至可以把 master 上的数据同步回 sessions-in-redis 环境:

1
$ symfony env:sync

在部署前调试生产部署

默认情况下,所有 SymfonyCloud 上的环境设置和 master/prod 环境是一样的(也就是 Symfony 的 prod 环境)。这样你才能在真实条件下测试应用程序。你会觉得像是在生产服务器上直接开发和测试,但此时你却没有直接操作生产服务器的风险。这让我想起来了过去用 FTP 部署时的美好时光。

出问题的时候,你可以切换到 Symfony 的 dev 环境:

1
$ symfony env:debug

当排查完错误后,返回到生产环境的设置:

1
$ symfony env:debug --off

警告

绝对不要master 分支上使用 dev 环境或 Symfony 分析器;它会让你的应用变得很慢,而且会导致很多严重的安全问题。

在部署前测试生产部署

用生产数据预览即将发布的网站版本,这带来了很多可能性,无论是对于用户界面/前端的回归测试,还是性能测试。Blackfire 是用来做性能测试的完美工具。

参考关于“性能”的那个步骤,从而更多了解如何使用 Blackfire 在部署前测试你的代码。

合并到生产环境

当你对分支上的改动满意后,把代码和软件环境配置合并到 Git 的 master 分支:

1
2
$ git checkout master
$ git merge sessions-in-redis

然后部署:

1
$ symfony deploy

部署的时候,只有代码和软件环境配置的更新会被推送到 SymfonyCloud;数据不会受到任何影响。

清理

最后,移除 Git 分支和 SymfonyCloud 环境:

1
2
$ git branch -d sessions-in-redis
$ symfony env:delete --env=sessions-in-redis --no-interaction

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.