如何使用 Ansible 同步 GitHub 和 GitLab
GitHub GitLab

> 
> 通过使用 Ansible 镜像 Git 存储库，保护对重要项目的访问。
> 
> 
> 


![](/data/attachment/album/202001/01/104040fbdu68wosg8o99wg.jpg)


开源无处不在。它在家里的计算机上、在工作场所的计算机上、在互联网上，并且很多都由 [Git](https://opensource.com/resources/what-is-git) 管理。由于 Git 是分布式的，因此许多人也将其视为一种众包的备份解决方案。从理论上讲，每当有人将 Git 存储库克隆到其本地计算机时，他们就创建了该项目源代码的备份。如果有 100 个人这样做，则存储库就有 100 个备份副本。


从理论上讲，这可以缓解“灾难”的影响，例如当项目维护者[突然决定删除存储库](https://github.com/AntiMicro/antimicro/issues/3)或[莫名其妙地阻止所有流量](https://opensource.com/article/19/10/how-community-saved-artwork-creative-commons)，导致开发人员们无头苍蝇般地寻找谁拥有主分支的最新版本。类似的，整个代码托管站点也会消失。没有人会想到 Google Code、Microsoft CodePlex 或 Gitorious 会在鼎盛时期将被关闭。


简而言之，如果在过去的几十年中互联网教给了我们一些东西，那就是依靠互联网神奇地创建备份并不是冗余的最可靠途径。


此外，对于许多人来说，很多开源项目都托管在 GitHub 上是个问题 —— GitHub 并不是开放平台。许多开发人员和用户都希望支持诸如 GitLab 之类的堆栈并与之交互，它具有开源社区版本。


### 使用 Ansible 管理 Git


Git 的去中心方式对于解决这个问题很有用。使用纯 Git，你可以使用一个 `push` 命令轻松地将其推到两个或多个存储库。但是，为了使其在发生意外故障时有用，你必须经常与 Git 存储库进行交互（特别是推送）。此外，即使你可能永远不会自己推送或拉出代码，也可能有一些要备份的存储库。


但是，使用 Ansible，你可以自动执行项目主分支（或其他任何分支）的 Git 拉取，然后自动进行存储库到“异地”镜像的 Git 推送。换句话说，你可以让你的计算机定期从 GitHub 拉取并推送到 GitLab 或 [Gitolite](https://opensource.com/article/19/4/server-administration-git) 或 Gitea（或你喜欢的任何 Git 托管主机）。


### Ansible 模块


如果不是因其出色的模块集合，那么 Ansible 就没那么出色。像 Python 的第三方库或 Linux 的应用程序一样，这个技术引擎的一个有用而令人惊讶的简单技巧是，Ansible 以其他人贡献的组件而闻名。因为本文正在研究如何有效和可靠地备份 Git 存储库，所以这里使用的模块是 [Git 模块](https://docs.ansible.com/ansible/latest/modules/git_module.html)和 [ini\_file](https://docs.ansible.com/ansible/latest/modules/ini_file_module.html) 模块。


首先，创建一个名为 `mirror.yaml` 的文件作为<ruby> 剧本 <rt>  playbook </rt></ruby>。你可以像通常使用 Ansible 一样，从 `name` 和 `task` 条目开始。本示例将 `localhost` 添加到 `hosts` 列表中，以便在控制器计算机（你现在坐在前面的计算机）上运行<ruby> 动作 <rt>  play </rt></ruby>，但是在现实生活中，你可能会在特定的主机或一组网络上的主机上运行它。



```
---
- name: "Mirror a Git repo with Ansible"
  hosts: localhost
  tasks:
```

### Git 拉取和克隆


如果要进行备份，则需要最新代码的副本。明显，在 Git 仓库中实现这一目标的方法是执行 `git pull`。 但是，`pull` 会假定克隆已经存在，而写得很好的 Ansible 动作（Ansible 脚本）则尽可能少的假定。最好告诉 Ansible 先克隆存储库。


将你的第一个任务添加到剧本：



```
---
- name: "Mirror a Git repo with Ansible"
  hosts: localhost
  vars:
    git_dir: /tmp/soso.git
  tasks:

  - name: "Clone the git repo"
    git:
       repo: 'https://github.com/ozkl/soso.git'
       dest: '{{ git_dir }}'
       clone: yes
       update: yes
```

这个例子使用了开源的、类似于 Unix 的操作系统 soso 作为我要镜像的存储库。这是一个完全任意的选择，绝不意味着我对该存储库的未来缺乏信心。它还使用变量来引用目标文件夹 `/tmp/soso.git`，这很方便，并且如果以后你希望将它扩展为一个通用的镜像脚本也会受益。在现实生活中，你的工作机上可能会比 `/tmp` 具有更永久的位置，例如 `/home/gitmirrors/soso.git` 或 `/opt/gitmirrors/soso.git`。


运行你的剧本：



```
$ ansible-playbook mirror.yaml
```

首次运行该剧本时，Ansible 会正确检测到 Git 存储库在本地尚不存在，因此将其克隆。



```
PLAY [Ansible Git mirror] ********

TASK [Gathering Facts] ***********
ok: [localhost]

TASK [Clone git repo] ************
changed: [localhost]

PLAY RECAP ***********************
localhost: ok=2 changed=1 failed=0 [...]
```

如果你再次运行该剧本，Ansible 会正确检测到自上次运行以来没有任何更改，并且会报告未执行任何操作：



```
localhost: ok=2 changed=0 failed=0 [...]
```

接下来，必须指示 Ansible 将存储库推送到另一个 Git 服务器。


### Git 推送


Ansible 中的 Git 模块不提供 `push` 功能，因此该过程的一部分是手动的。但是，在将存储库推送到备用镜像之前，你必须具有一个镜像，并且必须将镜像配置为备用<ruby> 远程服务器 <rt>  remote </rt></ruby>。


首先，必须将备用的远程服务器添加到 Git 配置。因为 Git 配置文件是 INI 样式的配置，所以你可以使用 `ini_file` Ansible 模块轻松地添加所需的信息。将此添加到你的剧本：



```
 - name: "Add alternate remote"
    ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
    tags: configuration
```

为此，你必须在目标服务器上有一个空的存储库（在本例中为 [GitLab.com](http://GitLab.com)）。如果需要在剧本中创建目标存储库，可以按照 Steve Ovens 的出色文章《[如何使用 Ansible 通过 SSH 设置 Git 服务器](https://opensource.com/article/17/8/ansible-environment-management)》来完成。


最后，直接使用 Git 将 HEAD 推送到备用远程服务器：



```
 - name: "Push the repo to alternate remote"
    shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
```

像往常一样运行该剧本，然后使该过程自动化，这样你就不必再次直接运行它了。你可以使用变量和特定的 Git 命令来调整脚本以适应你的需求，但是通过常规的拉取和推送操作，可以确保驻留在一台服务器上的重要项目可以安全地镜像到另一台服务器上。


这是完整的剧本，供参考：



```
---
- name: "Mirror a Git repository with Ansible"
  hosts: localhost
  vars:
    git_dir: /tmp/soso.git

  tasks:

  - name: "Clone the Git repo"
    git:
       repo: 'https://github.com/ozkl/soso.git'
       dest: '{{ git_dir }}'
       clone: yes
       update: yes

  - name: "Add alternate remote"
    ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
    tags: configuration
 
  - name: "Push the repo to alternate remote"
    shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
```



---


via: <https://opensource.com/article/19/11/how-host-github-gitlab-ansible>


作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
Open source is everywhere. It's in your computer at home, it's in your computer at work, it's on the internet, and a lot of it is managed with [Git](https://opensource.com/resources/what-is-git). Because Git is decentralized, many people also think of it as a kind of crowdsourced backup solution. The theory is that each time someone clones a Git repository to their local computer, they are creating a backup of the project's source code. If 100 people do that, then there are 100 backup copies of a repository.

This, in theory, mitigates "disasters" such as a project maintainer [suddenly deciding to remove a repository](https://github.com/AntiMicro/antimicro/issues/3) or [inexplicably blocking all traffic](https://opensource.com/article/19/10/how-community-saved-artwork-creative-commons) and leaving developers scrambling to figure out who has the latest version of the master branch. Similarly, entire code-hosting sites have disappeared in the past. Nobody anticipated the closure of Google Code, Microsoft CodePlex, or Gitorious when they were at their peak.

In short, if the internet has taught us anything over the past few decades, it's that relying on the internet to magically create backups isn't the most reliable road to redundancy.

Besides, it's a problem for a lot of people that many open source projects are hosted on GitHub, which is not an open platform. Many developers and users would prefer to support and interact with a stack such as GitLab, which has an open source community edition.

## Using Ansible for Git

Git's decentralization is useful in solving this problem. Using pure Git, you can easily push to two or more repositories with a single **push** command. However, for that to be useful against unexpected failure, you must be interacting (and pushing, specifically) with a Git repository frequently. Furthermore, there may be repositories out there that you want to back up, even though you may never push or pull the code yourself.


But using Ansible, you can automate Git pulls of a project's master branch (or any other branch, for that matter) and then automate Git pushes of the repository to an "offsite" mirror. In other words, you can have your computer regularly pull from GitHub and push to GitLab or [Gitolite](https://opensource.com/article/19/4/server-administration-git) or Gitea (or whatever Git host you prefer).

## Ansible modules

There wouldn't be much to Ansible if it weren't for its excellent collection of modules. Like third-party libraries for Python or applications for Linux, the technical *driver* of the useful and surprisingly easy tricks Ansible is famous for are the parts that other people have already figured out for you. Because this article is tackling how to effectively and reliably backup a Git repository, the modules used here are the [Git module](https://docs.ansible.com/ansible/latest/modules/git_module.html) and the [ini_file](https://docs.ansible.com/ansible/latest/modules/ini_file_module.html) module.

To begin, create a file called **mirror.yaml** to serve as the playbook. You can start mostly as you usually do with Ansible, with **name** and **task** entries. This example adds **localhost** to the **hosts** list so that the play runs on the controller machine (the computer you're sitting at right now), but in real life, you would probably run this on a specific host or group of hosts on your network.

```
---
- name: "Mirror a Git repo with Ansible"
hosts: localhost
tasks:
```


## Git pull and clone

If you're going to make a backup, then you need a copy of the latest code. The obvious way to make that happen with a Git repository is to perform a **git pull**. However, **pull** assumes that a clone already exists, and a well-written Ansible *play* (an Ansible script) assumes as little as possible. It's better to tell Ansible to **clone** a repository first.

Add your first task to your playbook:

```
---
- name: "Mirror a Git repo with Ansible"
hosts: localhost
vars:
git_dir: /tmp/soso.git
tasks:
- name: "Clone the git repo"
git:
repo: 'https://github.com/ozkl/soso.git'
dest: '{{ git_dir }}'
clone: yes
update: yes
```


This example uses the open source, Unix-like operating system **soso** as the repository I want to mirror. This is a completely arbitrary choice and in no way implies a lack of confidence in this repository's future. It also uses a variable to refer to the destination folder, **/tmp/soso.git**, which is convenient now and also beneficial later should you want to scale this out to be a generic mirroring script. In real life, you would probably have a more permanent location than **/tmp**, such as **/home/gitmirrors/soso.git** or **/opt/gitmirrors/soso.git**, on your worker machine.

Run your playbook:

`$ ansible-playbook mirror.yaml`


The first time you run the playbook, Ansible correctly detects that the Git repository does not yet exist locally, so it clones it.

```
PLAY [Ansible Git mirror] ********
TASK [Gathering Facts] ***********
ok: [localhost]
TASK [Clone git repo] ************
changed: [localhost]
PLAY RECAP ***********************
localhost: ok=2 changed=1 failed=0 [...]
```


Should you run the playbook again, Ansible correctly detects that there have been no changes since the last time it was run and it reports that no actions were performed:

`localhost: ok=2 changed=0 failed=0 [...]`


Next, Ansible must be instructed to push the repository to another Git server.

## Git push

The Git module in Ansible doesn't provide a **push** function, so that part of the process is manual. However, before you can push the repo to an alternate mirror, you have to have a mirror, and you have to configure the mirror as an alternate remote.

First, you must add an alternate remote to your Git configuration. Because the Git config file is an INI-style configuration, you can use the **ini_file** Ansible module to append the required information easily. Add this to your playbook:

```
- name: "Add alternate remote"
ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
tags: configuration
```


For this to work, you must have an empty repository on your destination server (in this case, [GitLab.com](http://GitLab.com)). If you need to create destination repositories in your playbook, you can do that by following Steve Ovens' excellent article "[How to use Ansible to set up a Git server over SSH](https://opensource.com/article/17/8/ansible-environment-management)."

Finally, use Git directly to push HEAD to your alternate remote:

```
- name: "Push the repo to alternate remote"
shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
```


Run the playbook as usual, and then automate the process so that you never have to run it directly again. You can adjust the script with variables and specific Git commands to suit your needs, but with regular pulls and pushes, you can be sure that an important project that lives on one server is safely mirrored on another.

Here is the full playbook for reference:

```
---
- name: "Mirror a Git repository with Ansible"
hosts: localhost
vars:
git_dir: /tmp/soso.git
tasks:
- name: "Clone the Git repo"
git:
repo: 'https://github.com/ozkl/soso.git'
dest: '{{ git_dir }}'
clone: yes
update: yes
- name: "Add alternate remote"
ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
tags: configuration
- name: "Push the repo to alternate remote"
shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
```


## 5 Comments