3

装在笔记本里的私有云环境:持续集成(上)

 2 years ago
source link: https://segmentfault.com/a/1190000041215560
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本篇是系列中的第五篇内容,我们继续聊聊如何把一个简化过的私有云环境部署在笔记本里,以满足低成本、低功耗、低延时的实验环境。如果你有闲置的轻量云服务器,也可以动手试试。

作为“持续集成”章节的第一篇内容,我们先来聊聊在单机服务器上的 CI 的使用。

关于基础的搭建,之前的文章中已经多次提到,所以我就不再赘述,本文将着重介绍过程中的一些细节,如果你对 Gitea 和 Drone 或者 GitLab 感兴趣,可以阅读之前的内容:

为了更低的维护成本,以及后续多机扩展使用,本文所有程序的使用均在容器环境下。

单机 CI 设计

在展开实践细节之前,我们得先来聊聊“设计”。

CI 过程中的参与者主要有下面这几类(本篇暂不聊软件仓库部分):用户、Git服务、CI 服务、CI 执行器。

简单针对上面的参与者进行定义:“用户”可以是有血有肉的人,也可以是自动化的脚本或者 BOT,各种数据的创造者;“Git 服务”,用于存储代码数据,提供基础的权限功能和界面管理的程序;“CI 服务”,提供持续集成的任务的调度和管理的程序;“CI 执行器”,用于执行具体的 CI 任务的程序。

考虑到单机服务器上除了 Git 服务和 CI 服务之外,还会运行我们需要更新和部署的程序,为了让资源使用效率更好、维护成本更低、避免我们为每一个 Web 程序配置 HTTPS 证书,我们可以添加一个支持服务发现的应用网关。

即使是单机服务器,我们依旧需要注意 SSH 的使用安全,在多机环境下,我们会使用跳板机和云服务器安全策略来进行集中的安全管理,在单机场景下,我使用 SSH 服务开关来完成简单的安全防护(不用的时候,直接关闭,也为互联网上的嗅探机器人省点电)。

如果将上面的“参与者”用图例来表示,一个最基础的单机 CI 使用模式会类似下面这样:

CI 的基础使用模式

我将图中不同角色的数据交互进行的数字序号标注,简单解释一下这些序号代表的具体内容:

  • “1” 表示了用户使用具体的域名来访问我们的 Git 服务和 CI 服务,来进行仓库管理或者配置 CI 任务。这类交互使用的是 HTTP 的方式,比如在浏览器中访问 https://gitea.lab.comhttps://gitlab.lab.comhttps://drone.lab.com
  • “2” 表示了用户或者客户端使用 SSH 的方式访问 Git 仓库,需要搭配 RSA Key 使用。
  • “3” 和 “4” 表示了 Traefik 使用服务发现的方式,聚合 Git 服务和 CI 服务,为用户提供域名形式的访问方式,这里使用的代理模式同样也是 HTTP。
  • “5” 表示了 SSH 开关和 Git SSH 服务之间的数据交互,交互形式为 TCP。
  • “6” 和 “7” 表示了 CI 服务 分别和Git 服务、CI 执行器之间的数据交互,从 Git 获取仓库变动,然后创建 CI 任务,接着将 CI 任务执行状态不断推送至 Git 服务中,交互形式不限,可以使用 HTTP API,也可以使用各种基于 TCP 的 RPC 的方式。
  • “8” 则表示了 CI 执行器如何从 Git 服务器的代码仓库中获取代码,或者将一些数据更新回 Git 服务器中,一般情况下是使用 HTTP 的方式,我更推荐使用 Git Over SSH 进行交互。

在单机全容器模式下,我们一般会用两种方式可以完成部署。

一类是基于文件挂载的方式,比如在 CI 过程中将 CI JOB 容器中的文件系统和宿主机打通,然后将构建产物同步到宿主机中、类似的变体还有使用各种网络文件协议进行文件系统挂载;另外一类,则是使用 SSH 或者 SCP 、Rsync 等方式,在容器中访问宿主机完成数据交换或者服务初始化或启停操作。

单机模式下基于Docker 的 CI 部署模式

除此之外,如果我们借助软件仓库、容器仓库,还能够完成纯容器交付,让交互更纯粹和“干净”。这个话题,我们会在后续文章中展开。

单机 CI 配置实践

接下来,我们以上文中的 “SSH 开关”这个应用,在 Gitea 和 Drone 环境中进行持续集成和部署实践为例,来聊聊如何在单机模式下使用 CI。

配置好 CI 的示例仓库

因为这个项目类型是一个不支持热加载的、需要持续运行的网络程序,程序的更新需要重启服务。所以我们恰好可以使用“部署模式”中的挂载文件的方式更新文件,以及使用 SSH 的方式来进行服务的停止和重新启动。(如果是静态资源类的项目部署,则只需要完成资源替换更新即可)

定义 CI 配置文件

首先将需要集成 CI 的项目放置上传到 Gitea 中的某个仓库中,这里以上文中提到的 Git SSH 开关为例。在项目中创建一个名为 .drone.yml 的 CI 配置文件。

一个相对通用的 CI 配置可以用下面的形式来表达:

---
kind: pipeline
name: default

steps:

- name: clone

- name: stop-previous-services
  depends_on: [ clone ]

- name: update-services
  depends_on: [ stop-previous-services ]

- name: start-new-services
  depends_on: [ update-services ]

上面的配置包含了:下载仓库代码、停止原先的服务、更新服务程序代码、重新启动服务四个过程。在实际生产中,根据业务类型,我们的执行顺序可能会有变化,甚至不再是上面的“串行”方式执行。

Gitea 仓库配置好 CI 之后

按照上面的配置将 CI 配置好之后,当我们推送代码到代码仓库触发 CI 任务后。在图形界面中,我们将看到类似上图的结果。

使用 SSH 协议下载代码

不论是使用哪一种 CI 工具,我都推荐你使用 Git Over SSH 的方式来获取代码,而非使用 Git Token 或者账号密码的方式来进行交互。这样可以让你的程序对于某一种 CI 或者 Git 仓库的依赖更低,更容易在合适的时间点、以低成本切换到更合适的工具。

在 Drone CI 中,如果想使用 SSH 方式来下载代码,可以使用下面的配置:(在 GitLab Runner 中同理)

---
kind: pipeline
name: default

clone:
  disable: true

steps:

- name: clone
  image: alpine/git
  pull: if-not-exists
  environment:
    KEY:
      from_secret: ssh_key
  commands:
    - GIT_HOST=$(echo $DRONE_GIT_SSH_URL | sed 's/git@/\1/' | sed 's/:.*/\1/') && mkdir "$HOME/.ssh" && echo "$KEY" > "$HOME/.ssh/id_rsa" && chmod 600 $HOME/.ssh/id_rsa && eval `ssh-agent -s` && ssh-add $HOME/.ssh/id_rsa && ssh-keyscan $GIT_HOST > ~/.ssh/known_hosts && chmod 400 "$HOME/.ssh/known_hosts";
    - git clone $DRONE_GIT_SSH_URL .
    - git -c advice.detachedHead=false checkout $DRONE_COMMIT

上面的代码中,为了使用 SSH 方式下载程序代码,CI 程序会做两件事:

  1. 从 CI 软件中读取我们预先配置好的 ssh_key 环境变量,然后将变量输出成程序可以直接使用的 rsa_key ,并设置好权限,使用 ssh-agent 加载程序。
  2. 将仓库使用默认的 HTTP 协议替换为 Git 协议,以备程序使用。

当然,想要使用 SSH 方式下载代码,我们需要在 Git 软件的账号或者仓库中配置 SSH Key

使用 SSH 方式操作服务启停

这个应用中,我们在 docker-compose.yml 定义了容器的启动方式,所以服务的启动和关闭可以使用我们熟悉的命令 docker-compose up -ddocker-compose down 来完成。

因为 CI 在容器中执行,我们不能直接操作宿主机,所以需要借助 SSH 或者 dind 模式的 docker.sock 来完成服务状态的改变。

本文先聊聊如何使用 SSH 来解决基础的部署操作:

- name: stop-or-start-services
  image: deploy-tool
  depends_on: [ clone ]
  pull: if-not-exists
  environment:
    KEY:
      from_secret: ssh_key
    # 环境变量,除了私密的定义在 CI 软件的环境变量中,也可显式声明在 CI 配置中
    TARGET_HOST: user@host
    TARGET_PORT: 22
  commands:
    - mkdir "$HOME/.ssh" && echo "$KEY" > "$HOME/.ssh/id_rsa" && chmod 600 $HOME/.ssh/id_rsa && eval `ssh-agent -s` && ssh-add $HOME/.ssh/id_rsa";
    # 关闭服务
    - ssh -i "$HOME/.ssh/id_rsa" -p $TARGET_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $TARGET_HOST "bash -c \"cd /app-path/ && docker-compose down\""
    # 启动服务
    - ssh -i "$HOME/.ssh/id_rsa" -p $TARGET_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $TARGET_HOST "bash -c \"cd /app-path/ && docker-compose up -d\""

和下载代码类似,我们从环境变量中初始化 rsa key,然后在 ssh-agent 中加载私钥。然后使用 ssh 客户端连接宿主机,切换工作目录,执行命令操作服务的启动和关闭即可。

同样的,想要使用 SSH 操作服务器,我们需要在服务器对应用户的 ~/.ssh/authorized_keys 中配置对应的公钥。

使用文件挂载的方式更新代码

更新代码有两种方式,一种是使用上文中提到的 SSH 的方式,远程执行 scprsync 等命令同步数据,另外一种则是使用文件挂载的方式。因为我们的部署在同一台机器上,所以文件挂载不失为一个高效的方式。

以 Drone CI 配置为例,演示如何挂载宿主机目录到容器内:

- name: update-services
  image: deploy-tool
  depends_on: [ stop-previous-services ]
  pull: if-not-exists
  commands:
    - rm -rf /deploy/*
    - cp -r /drone/src/* /deploy/
    - cp -r /drone/src/.env /deploy/
  volumes:
    - name: host-dir
      path: /deploy

volumes:
  - name: host-dir
    host:
      path: /app-path

在接下来的“持续集成”相关文章中,我将展开聊聊 CI 在多机和相对复杂场景下的使用,以及其他场景类型的部署实战细节。

--EOF


我们有一个小小的折腾群,里面聚集了几百位喜欢折腾的小伙伴。

在不发广告的情况下,我们在里面会一起聊聊软硬件、HomeLab、编程上的一些问题,也会在群里不定期的分享一些技术沙龙的资料。

喜欢折腾的小伙伴欢迎扫码添加好友。(添加好友,请备注实名,注明来源和目的,否则不会通过审核)

关于折腾群入群的那些事


如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2022年01月02日
统计字数: 5438字
阅读时间: 11分钟阅读
本文链接: https://soulteary.com/2022/01...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK