如何使用Docker和CoreOS在DigitalOcean上自托管ReviewNinja

来自菜鸟教程
跳转至:导航、​搜索

介绍

代码审查已成为现代软件开发过程中不可分割的一部分。 随着分布式版本控制系统的出现,尤其是 GitHub 的诞生,pull request-review-merge 模型在软件开发社区中得到了普及。 然而,GitHub 的内置拉取请求审查系统还有很多不足之处。 因此,存在许多与 GitHub 集成的第三方代码审查工具来改进流程。 ReviewNinja 就是这样一种工具。

ReviewNinja 在原版 GitHub 拉取请求审查体验之上添加了一些功能。 它使我们能够通过给予“忍者星”来明确签署拉取请求,因此不再需要像 :shipit:LGTM 或其他流行约定之类的评论。 如果至少 2 名团队成员没有签署拉取请求,或者有人在拉取请求上添加了 !fix 之类的评论,您可以设置策略来阻止合并。

ReviewNinja 由 SAP 开发并开源。 它有一个 托管版本 ,但我们可以将它部署在我们自己的服务器上并将其用于我们的私有 GitHub 存储库。

在本指南中,您将使用 DockerCoreOS 在 DigitalOcean 上部署 ReviewNinja 实例。 一个生产 ReviewNinja 实例有一些移动部分,所以我们将使用 docker-machine 创建和控制远程 Docker 主机,并使用 docker-compose 来描述、构建和部署我们的堆栈。 我们将 CoreOS 用于 Docker 主机,这是为云部署量身定制的最小 Linux 发行版。 全新安装的 CoreOS 仅运行 systemd 和 Docker 守护程序,因此我们有更多资源可用于我们的应用程序。

先决条件

要完成本教程,您需要:

  • Docker、docker-machinedocker-compose 安装在您的本地计算机上,因此您可以构建我们将部署的应用程序映像。 您可以按照 Docker 官方安装文档 来配置这些工具。 docker-machinedocker-compose 都在 OSX 和 Windows 上使用 Docker 应用程序自动安装,或者您可以使用以下链接手动安装它们:
  • Git 安装在您的本地计算机上,因此您可以克隆 ReviewNinja 存储库以创建容器。 如果您需要满足此先决条件,请遵循 官方 Git 安装文档
  • 具有读取和写入访问权限的 DigitalOcean 访问令牌,您可以通过访问应用程序和 API 页。 复制此令牌,因为您需要将它与 docker-machine 一起使用来创建主机。
  • 一个 1GB CoreOS Droplet,我们将在本教程中使用 docker-machine 进行配置。
  • 一个 GitHub 帐户。

第 1 步 - 创建和激活基于 CoreOS 的 Docker 主机

让我们为我们的部署设置基础设施。 docker-machine 工具允许您将远程机器配置为 Docker 主机并从本地机器控制它们。 它为许多流行的云提供商提供驱动程序,包括 DigitalOcean。 我们将使用 docker-machine 为我们的 Docker 主机创建一个 CoreOS Droplet。

切换到您的终端,并使用您的 DigitalOcean 访问令牌发出以下命令:

docker-machine create --driver=digitalocean \
--digitalocean-access-token=DIGITAL_OCEAN_ACCESS_TOKEN \
--digitalocean-image=coreos-stable \
--digitalocean-region=nyc3 \
--digitalocean-size=1GB \
--digitalocean-ssh-user=core \
reviewninja

我们告诉 docker-machineNYC3 数据中心使用 coreos-stable 图像和 1GB 内存创建一个名为 reviewninja 的 Droplet。 请注意,我们指定 --ssh-user=core 是因为 CoreOS 安装的默认用户是 core

运行此命令时,您将看到以下输出:

OutputRunning pre-create checks...
Creating machine...
(reviewninja) Creating SSH key...
(reviewninja) Creating Digital Ocean droplet...
(reviewninja) Waiting for IP address to be assigned to the Droplet...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with coreOS...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env reviewninja

让我们看看这个新的Droplet是否被docker-machine识别。 运行命令:

docker-machine ls

您将看到以下输出,表明 Docker 主机 reviewminja 正在使用 digitalocean 驱动程序在远程 IP 地址上运行:

OutputNAME          ACTIVE   DRIVER         STATE     URL                          SWARM   DOCKER    ERRORS
reviewninja            digitalocean   Running   tcp://your_ip_address:2376            v1.10.3

当我们创建 Docker 主机时,输出的最后一行告诉我们接下来要做什么。 它说:

OutputTo see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env reviewninja

因此,让我们运行该命令:

docker-machine env reviewninja

您将看到此消息:

Outputexport DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://your_server_ip:2376"
export DOCKER_CERT_PATH="/home/kevin/.docker/machine/machines/reviewninja"
export DOCKER_MACHINE_NAME="reviewninja"
# Run this command to configure your shell:
# eval $(docker-machine env reviewninja)

那么这里发生了什么? Docker 架构使用客户端-服务器模型。 Docker 客户端可以通过 Unix 套接字或 TCP 进行通信。 通常,我们的 Docker 客户端通过 Unix 套接字与本地安装的 Docker 引擎通信。 但是,您可以设置一些环境变量来告诉 Docker 客户端通过 TCP 与 Docker 主机通信。 您看到的输出是一系列用于设置执行此操作的环境变量的 shell 命令。

最后一部分说:

Output# Run this command to configure your shell:
# eval $(docker-machine env reviewninja)

当您运行该命令时,您告诉 shell 执行这些命令,这些命令设置将用于后续 docker 命令的环境变量。

所以继续在你的shell中执行那个命令:

eval $(docker-machine env reviewninja)

现在,如果您执行 docker info,您将看到有关远程 Docker 守护程序的信息,而不是本地 Docker 守护程序:

docker info

该命令的输出将如下所示:

OutputContainers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 1.10.3
 [...]
Labels:
 provider=digitalocean

注意:运行docker命令时可能会出现如下错误:

Error response from daemon: client is newer than server (client API version: 1.24, server API version: 1.22)

这意味着您使用的 Docker 客户端版本与服务器版本不兼容。 要解决此问题,请将环境变量 DOCKER_API_VERSION 设置为与服务器相同的版本。 例如,如果服务器想要 1.22 版本,则执行以下命令:

export DOCKER_API_VERSION=1.22

然后尝试再次运行 Docker 命令。


我们的远程 Docker 主机现在已通过 Docker 进行配置和访问。 在创建 ReviewNinja 容器之前,我们需要在 GitHub 上做一些工作。

第 2 步 — 注册 GitHub OAuth 应用程序

ReviewNinja 需要使用 GitHub 的 API 来访问您的存储库,因此我们会将 ReviewNinja 安装注册为 GitHub OAuth 应用程序。

首先,我们需要找出我们服务器的 IP 地址。 我们可以使用 docker-machine 命令来做到这一点:

docker-machine ip reviewninja

记录此命令显示的 IP 地址。 然后登录你的 GitHub 账号,进入 设置 -> OAuth 应用程序 -> 开发者应用程序 并按下注册新应用程序 按钮。

收到新申请表后,请输入以下信息:

  1. 名称 设置为 review-ninja
  2. 主页 URL 设置为 http://your_ip_address
  3. 授权回调 URL 设置为 http://your_ip_address/auth/GitHub/callback

然后按注册应用程序按钮保存更改并创建应用程序。 这将在屏幕上显示新创建的应用程序。

Client IDClient Secret 的值保存在安全的地方; 您很快就会将它们添加到 ReviewNinja 应用程序配置中。

现在您有了密钥,让我们开始构建我们的 ReviewNinja 实例。

第 3 步 — 创建 ReviewNinja Docker 容器

ReviewNinja 是一个 Node.js 应用程序,它依赖于 MongoDB 支持的存储层。 由于我们将其置于生产环境中,因此我们会将 Node.js 应用程序放在代理服务器后面,这样应用程序服务器就不会直接暴露在 Internet 上。 为此,我们将使用 Nginx。 要配置的东西很多,所以我们将使用 docker-compose 以声明的方式部署多个相关容器。 我们定义我们想要的配置,然后使用 docker-compose 工具来创建具有指定所有运行时环境的容器。

首先,我们需要获取 ReviewNinja 源代码。 使用 Git 在本地机器上克隆源代码:

git clone https://github.com/reviewninja/review.ninja.git

然后导航到项目的文件夹:

cd review.ninja

此存储库包含一个 Dockerfile,它告诉 Docker 如何构建 ReviewNinja 应用程序映像。 如果您在您喜欢的文本编辑器中打开此文件,您将看到以下内容:

Dockerfile

FROM node:0.12.2

COPY . /app

RUN npm install -g bower
RUN cd /app; npm install; bower install --allow-root;

WORKDIR /app

VOLUME ["/certs"]

EXPOSE 5000

CMD ["node", "/app/app.js"]

此文件指定此应用将使用的 Node.js 版本。 然后它将所有文件从当前文件夹复制到 app 文件夹并安装所有应用程序依赖项。 然后它公开端口 5000 并启动应用程序。 更详细的 Dockerfiles 介绍见【X53X】本教程【X70X】。

Dockerfile 描述了 ReviewNinja 应用程序容器,但我们可以使用一个名为 docker-compose.yml 的文件来描述我们的堆栈组件,包括 MongoDB 和 Nginx 代理,这是一个 YAML 文件,一个配置文件的流行格式。

您克隆的存储库有一个名为 docker-compose-example.yml 的文件,但我们将从头开始编写我们自己的文件,因为示例不符合我们的需要。

首先,让我们定义堆栈的存储。 创建文件 docker-compose.yml 并输入以下配置:

码头工人-compose.yml

version: "2"
services:
    db:
        image: mongo
        volumes:
            - /data:/data/db

db 服务使用 Docker Hub 上的官方 MongoDB 镜像,Docker Hub 是 Docker 镜像的中央存储库。 按照设计,Docker 容器在停止和删除时会丢失其运行时状态。 这对于 web 服务来说很好,因为它是无状态的。 对于我们的 db 服务,我们需要将数据持久化到磁盘上,以便在停止或重新启动服务时不会丢失所有代码审查数据。 这就是 volumes 的用武之地。 在运行时,Docker 守护进程可以运行一个容器,将容器中的卷映射到主机上的目录。

在我们的配置中,我们指定了以下内容:

码头工人-compose.yml

        volumes:
            - /data:/data/db

这会将主机的 /data 文件夹映射到容器中的 /data/db ,这恰好是 MongoDB 配置为在容器内写入的文件夹。 通过创建此映射,应用程序所做的更改将保留在主机上的 /data 文件夹中,而不是容器中。

接下来我们定义 ReviewNinja 应用程序容器。 在现有配置之后,将其添加到 docker-compose.yml 文件中:

码头工人-compose.yml

services:
    db:
    [...]

    web:
        build: .
        working_dir: /app/
        links:
            - db
        environment:
            MONGODB: mongodb://db/reviewninja
            GITHUB_CLIENT: YOUR_GITHUB_APP_ID
            GITHUB_SECRET: YOUR_GITHUB_APP_SECRET

注意:确保 web 与您之前定义的 db 服务定义垂直对齐,因为 YAML 文件对缩进很挑剔。


我们使用 build . 告诉 docker-compose 图像应该从我们刚刚在当前文件夹中探索的 Dockerfile 构建。 然后我们声明一个指向 db 图像的链接,因此在 web 容器内,名称 db 将解析为 db 容器的 IP 地址。 这提供了一个基本的服务发现机制; 我们不必提前知道 db 容器的 IP 地址并对其进行硬编码或通过环境变量传递它。 然后我们使用该链接定义 MONGODB 环境变量,使用 mongodb://db/reviewninja 作为值。

使用您创建的 GitHub 应用程序的客户端 ID 和密码填写 GITHUB_CLIENTGITHUB_SECRET。 ReviewNinja 应用程序将在运行时读取这些环境变量。

最后,让我们定义负载平衡服务,它将请求从端口 80 转发到我们的 Node.js 应用程序使用的端口。 将此配置添加到文件中,将其与您刚刚创建的 web 服务声明垂直排列:

码头工人-compose.yml

services:
    web:
    [...]
    nginx:
        image: nginx
        ports:
            - "80:80"
        volumes:
            - ./reviewninja.conf:/etc/nginx/conf.d/default
        command: /bin/bash -c "echo -e 'upstream backend { server web:5000; }\nserver { listen 80; location / { proxy_pass http://backend; }}' > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
        links:
            - web

我们使用Docker Hub的official Nginx image,并声明了80:80的端口映射,将主机上的端口80绑定到端口80容器。 然后,我们创建一个卷映射,将 Nginx 配置文件存储在容器之外,并声明到 app 容器的容器链接,以便我们可以通过名称和代理请求对其进行定位。

command 声明很长,所以让我们分解一下。 它实际上在一行上运行两个命令。 第一个命令是 echo -e ... > /etc/nginx/conf.d/default.conf,它为 ReviewNinja 创建 Nginx 配置文件,如下所示:

默认.conf

upstream backend {
    server web:5000;
}

server {
    listen       80;

    location / {
        proxy_pass http://backend;
    }
}

这定义了上游的 backend 并将其指向 web:5000。 值 web 来自 links 部分中的 docker-compose.yml 文件,端口 5000 是 Node.js 服务器在 web 容器。 然后我们声明我们的 Nginx 服务器将在容器中的端口 80 上运行,并且应该将所有请求代理到我们的应用服务器 backend

命令的第二部分 nginx -g 'daemon off' 是在容器中运行 Nginx 服务器进程的命令。 我们需要指定 daemon off 因为 Nginx 默认以守护模式运行,将自身与正在运行的进程分离。 Docker 将任何与容器入口点分离的程序视为“已退出”并终止容器,从而获得所有进程。 根据经验,在 Docker 容器内运行的任何进程都必须在前台运行。

这是整个 docker-compose.yml 文件,以防万一您想在继续之前仔细检查您的配置:

码头工人-compose.yml

version: "2"
services:
    db:
        image: mongo
        volumes:
            - /data:/data/db
    web:
        build: .
        working_dir: /app/
        links:
            - db
        environment:
            MONGODB: mongodb://db/reviewninja
            GITHUB_CLIENT: YOUR_GITHUB_APP_ID
            GITHUB_SECRET: YOUR_GITHUB_APP_SECRET
    nginx:
        image: nginx
        ports:
            - "80:80"
        volumes:
            - ./reviewninja.conf:/etc/nginx/conf.d/default
        command: /bin/bash -c "echo -e 'upstream backend { server web:5000; }\nserver { listen 80; location / { proxy_pass http://backend; }}' > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
        links:
            - web

如果您想了解有关 docker-compose.yml 的语法和选项的更多信息,请查看 docker-compose 文档

这负责我们对此应用程序的配置。 保存docker-compose.yml文件; 是时候部署这个应用程序了。

第 4 步 — 构建和部署容器

我们已经配置了 docker-compose 来部署我们的 ReviewNinja 应用程序、一个 MongoDB 实例来保存数据和一个 Nginx 代理。 在我们部署这些容器之前,让我们验证 reviewninja Docker 机器是否仍然处于活动状态:

docker-machine active

你应该看到:

Outputreviewninja

如果您没有看到该输出,请务必运行

eval $(docker-machine env reviewninja)

再次确保您的环境设置正确。 然后再试一次。

一旦你确定你有一台活跃的机器,使用 docker-compose 来构建你的堆栈:

docker-compose build

此过程可能需要很长时间,因为它会在 Docker 主机上下载和配置 ReviewNinja 应用程序的所有依赖项。 您将看到以下输出:

Outputdb uses an image, skipping
Building web
Step 1 : FROM node:0.12.2
0.12.2: Pulling from library/node
[...]
Successfully built 106a1992d538

构建过程完成后,验证您是否拥有成功的映像:

docker images

您将看到以下输出,表明图像 reviewninja_web 已成功创建:

OutputREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
reviewninja_web     latest              106a1992d538        3 minutes ago       946.6 MB

现在我们可以使用一个命令在远程服务器上启动我们的数据库、ReviewNinja 应用程序和 Nginx 代理:

docker-compose up -d

这会调出我们在 docker-compose 文件中定义的所有容器。 我们使用 -d(表示“分离”),因此所有容器都在后台运行,并且我们可以控制终端。

OutputCreating network "reviewninja_default" with the default driver
Pulling db (mongo:latest)...
latest: Pulling from library/mongo
[...]
Digest: sha256:d3f19457c816bb91c5639e3b1b50f67370e3b3a58b812d73446d7b966469c65e
Status: Downloaded newer image for mongo:latest
Creating reviewninja_db_1
Creating reviewninja_web_1
Creating reviewninja_nginx_1

让我们验证容器是否已启动并正在运行。 执行以下命令:

docker ps

您将看到如下所示的输出:

OutputCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
29f8e6f770d3        nginx               "nginx -g 'daemon off"   43 seconds ago      Up 41 seconds       0.0.0.0:80->80/tcp, 443/tcp   reviewninja_nginx_1
164564dd450a        reviewninja_web     "node /app/app.js"       45 seconds ago      Up 43 seconds       5000/tcp                      reviewninja_web_1
7cd9d03eb3b9        mongo               "/entrypoint.sh mongo"   46 seconds ago      Up 44 seconds       27017/tcp                     reviewninja_db_1

我们还想确保服务正常运行。 为此,我们使用 docker logs 命令查看容器的输出。 让我们查看 ReviewNinja Web 应用程序的日志。 我们可以通过其 ID(列在前面输出的 CONTAINER ID 列中)或通过其名称来引用容器。 在我们的例子中,名称是 reviewninja_web_1,所以让我们查看该容器的日志:

docker logs reviewninja_web_1

您将看到 ReviewNinja 应用程序的输出,表明它正在侦听连接:

OutputIn server/app.js
checking configs
✓ configs seem ok
Host:        http://localhost:5000
GitHub:      https://GitHub.com
GitHub-Api:  https://api.GitHub.com
bootstrap certificates
bootstrap static files
apply migrations
[...]
bootstrap mongoose
[...]
bootstrap passport
[...]
bootstrap controller
[...]
bootstrap api
[...]
bootstrap webhooks
[...]
bootstrap monkey patch

✓ bootstrapped, app listening on localhost:5000

输出表明 ReviewNinja 正在侦听端口 5000

要从 Web 访问它,我们需要使用 Docker 主机的 IP,这是我们的 CoreOS 服务器。 如果您忘记了服务器的 IP 地址,请使用 docker-machine 查找。

docker-machine ip reviewninja

将您的浏览器指向 http://your_server_ip,您将受到忍者的欢迎:

最后,我们准备好将应用程序与我们自己的代码一起使用。

第 5 步 — 将 ReviewNinja 与存储库一起使用

让我们在测试存储库上试用我们的新 ReviewNinja 实例。 我们将提供有关拉取请求的反馈、解决问题、接受更改并合并拉取请求。

首先,我们需要允许 ReviewNinja 应用访问我们的 GitHub 帐户。 点击【X9X】登录【X20X】,跳转到GitHub登录。 系统将询问您是否允许 ReviewNinja 访问您的 GitHub 帐户:

授权应用程序后,您将被带到 ReviewNinja 的主界面。 如果您有想要 ReviewNinja 使用的私有存储库,您可以单击 Enable private repos 链接:

然后,您将被重定向到 GitHub 以修改您对 ReviewNinja 应用程序的授权,以包括对您的私人存储库的访问:

授予 ReviewNinja 所需的访问权限后,您可以添加一个存储库,以便您可以将 ReviewNinja 用于您的拉取请求工作流程。 当您第一次使用 ReviewNinja 时,您有机会添加一个示例 ReviewNinja-Welcome 存储库:

创建该示例存储库,以便我们了解一些基本的 ReviewNinja 功能。 这会在您的帐户下在 Github 上创建存储库并将其添加到 ReviewNinja。

示例存储库包含一个 ReadMe.md 文件,该文件应该概述 ReviewNinja 代码审查流程的一些功能。 ReviewNinja-Welcome 存储库已经从分支 your_github_username-patch-1 打开了一个拉取请求,该分支具有 ReadMe.md 文件的更新副本。 分支的名称将根据您的用户名而有所不同。

单击该分支,您将看到主要的代码审查界面,您可以在其中浏览差异并添加注释。 您还将看到拉取请求状态框,其中概述了拉取请求的状态和未解决的问题。

Merge pull request 按钮现在是琥珀色的,因为 pull request 的状态是“pending”。 状态将根据您可以通过单击齿轮按钮进行调整的条件而改变。 默认情况下,按钮变为绿色至少需要 1 颗忍者星。

稍后我们将看到这一点,但现在,让我们添加一行注释。 单击显示的代码行

+ convenience we also have a dropdown menu to add these comments

这里稍微迂腐了,建议把dropdown这个词改成drop-down。 使用屏幕右侧的评论框添加评论,并通过在您的评论中添加 !fix 将其标记为阻塞问题,如下图所示:

标记的评论将被视为拉取请求的“问题”,拉取请求的作者需要在 ReviewNinja 允许合并之前解决该问题。

刷新页面,您现在将看到 Merge pull request 按钮上方列出的新问题:

让我们解决这个问题。 在本地机器上,使用 Git 克隆存储库:

git clone git@GitHub.com:your_github_username/ReviewNinja-Welcome.git
cd ReviewNinja-Welcome

然后检查需要工作的分支:

git checkout your_github_username-patch-1

在您喜欢的文本编辑器中打开 ReadMe.md 并将行更改为 drop-down 而不是 dropdown

label ReadMe.md
To add a flag simply leave a comment with your desired flag. For
convenience we also have a drop-down menu to add these comments
automatically.

将文件保存在编辑器中,然后添加并提交您的更改:

git add ReadMe.md
git commit -m "Address code review feedback"

接下来,将分支的更改推送到 Github:

git push origin your_github_username-patch-1

现在,在浏览器中刷新 ReviewNinja 界面。 会看到代码更新了,如果再次点击该行,可以用!fixed或者!resolved回复已有的评论,如下图:

最后,既然我们对拉取请求感到满意,让我们给它一个忍者星作为正式的签字。 点击【X10X】添加忍者星【X28X】按钮:

然后刷新浏览器,观察拉取请求状态更新为“成功”,合并拉取请求按钮为绿色:

您可以通过单击齿轮按钮自定义拉取请求的成功条件:

继续并单击“合并拉取请求”。 页面重新加载后(您可能需要手动刷新),您会看到拉取请求的状态更改为“已合并”。

需要记住的一件事:ReviewNinja 拉取请求 GitHub 拉取请求,反之亦然。 在 ReviewNinja 上发表的评论将自动反映在 GitHub 拉取请求页面上,反之亦然。 通过 ReviewNinja 合并的拉取请求也将反映在 GitHub 上:

对于希望逐渐迁移到 ReviewNinja 进行代码审查的团队来说,这种双向同步将非常方便。

结论

在本教程中,您使用 Docker、docker-machinedocker-compose 来部署 ReviewNinja,这是一个多层 Web 应用程序。 您学习了如何从现有应用程序创建 Docker 映像,以及如何在舒适的本地终端中定义和部署整个基础架构。

您还了解了 ReviewNinja 的一些强大功能,以及如何使用这些功能向 GitHub 拉取请求流程添加一些工作流控制。

快乐的代码审查!