Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66946df756 | |||
| 556fac3002 | |||
| 1ad7df2df3 | |||
| a3d010c5f0 |
@@ -1,11 +1,12 @@
|
||||
[](https://github.com/zrepl/zrepl/blob/master/LICENSE)
|
||||
[](https://golang.org/)
|
||||
[](https://golang.org/)
|
||||
[](https://zrepl.github.io)
|
||||
[](https://patreon.com/zrepl)
|
||||
[](https://patreon.com/zrepl)
|
||||
[](https://github.com/sponsors/problame)
|
||||
[](https://liberapay.com/zrepl/donate)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R5QSXJVYHGX96)
|
||||
[](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fzrepl%2Fzrepl)
|
||||
[](https://matrix.to/#/#zrepl:matrix.org)
|
||||
|
||||
# zrepl
|
||||
zrepl is a one-stop ZFS backup & replication solution.
|
||||
|
||||
@@ -9,7 +9,4 @@ jobs:
|
||||
key: "/etc/zrepl/backups.key"
|
||||
client_cns:
|
||||
- "prod"
|
||||
recv:
|
||||
placeholder:
|
||||
encryption: inherit # use 'off' if sender uses send.encrypted
|
||||
root_fs: "storage/zrepl/sink"
|
||||
|
||||
@@ -9,8 +9,8 @@ jobs:
|
||||
key: /etc/zrepl/prod.key
|
||||
server_cn: "backups"
|
||||
filesystems: {
|
||||
"zroot/var/db": true,
|
||||
"zroot/usr/home<": true,
|
||||
"zroot<": true,
|
||||
"zroot/var/tmp<": false,
|
||||
"zroot/usr/home/paranoid": false
|
||||
}
|
||||
snapshotting:
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
jobs:
|
||||
# Separate job for snapshots and pruning
|
||||
- name: snapshots
|
||||
type: snap
|
||||
filesystems:
|
||||
'tank<': true # all filesystems
|
||||
snapshotting:
|
||||
type: periodic
|
||||
prefix: zrepl_
|
||||
interval: 10m
|
||||
pruning:
|
||||
keep:
|
||||
# Keep non-zrepl snapshots
|
||||
- type: regex
|
||||
negate: true
|
||||
regex: '^zrepl_'
|
||||
# Time-based snapshot retention
|
||||
- type: grid
|
||||
grid: 1x1h(keep=all) | 24x1h | 30x1d | 12x30d
|
||||
regex: '^zrepl_'
|
||||
|
||||
# Source job for target B
|
||||
- name: target_b
|
||||
type: source
|
||||
serve:
|
||||
type: tls
|
||||
listen: :8888
|
||||
ca: /etc/zrepl/b.example.com.crt
|
||||
cert: /etc/zrepl/a.example.com.crt
|
||||
key: /etc/zrepl/a.example.com.key
|
||||
client_cns:
|
||||
- b.example.com
|
||||
filesystems:
|
||||
'tank<': true # all filesystems
|
||||
# Snapshots are handled by the separate snap job
|
||||
snapshotting:
|
||||
type: manual
|
||||
|
||||
# Source job for target C
|
||||
- name: target_c
|
||||
type: source
|
||||
serve:
|
||||
type: tls
|
||||
listen: :8889
|
||||
ca: /etc/zrepl/c.example.com.crt
|
||||
cert: /etc/zrepl/a.example.com.crt
|
||||
key: /etc/zrepl/a.example.com.key
|
||||
client_cns:
|
||||
- c.example.com
|
||||
filesystems:
|
||||
'tank<': true # all filesystems
|
||||
# Snapshots are handled by the separate snap job
|
||||
snapshotting:
|
||||
type: manual
|
||||
|
||||
# Source jobs for remaining targets. Each one should listen on a different port
|
||||
# and reference the correct certificate and client CN.
|
||||
# - name: target_c
|
||||
# ...
|
||||
@@ -0,0 +1,30 @@
|
||||
jobs:
|
||||
# Pull from source server A
|
||||
- name: source_a
|
||||
type: pull
|
||||
connect:
|
||||
type: tls
|
||||
# Use the correct port for this specific client (eg. B is 8888, C is 8889, etc.)
|
||||
address: a.example.com:8888
|
||||
ca: /etc/zrepl/a.example.com.crt
|
||||
# Use the correct key pair for this specific client
|
||||
cert: /etc/zrepl/b.example.com.crt
|
||||
key: /etc/zrepl/b.example.com.key
|
||||
server_cn: a.example.com
|
||||
root_fs: pool0/backup
|
||||
interval: 10m
|
||||
pruning:
|
||||
keep_sender:
|
||||
# Source does the pruning in its snap job
|
||||
- type: regex
|
||||
regex: '.*'
|
||||
# Receiver-side pruning can be configured as desired on each target server
|
||||
keep_receiver:
|
||||
# Keep non-zrepl snapshots
|
||||
- type: regex
|
||||
negate: true
|
||||
regex: '^zrepl_'
|
||||
# Time-based snapshot retention
|
||||
- type: grid
|
||||
grid: 1x1h(keep=all) | 24x1h | 30x1d | 12x30d
|
||||
regex: '^zrepl_'
|
||||
@@ -248,7 +248,7 @@ Limitations
|
||||
Multiple Jobs & More than 2 Machines
|
||||
------------------------------------
|
||||
|
||||
The quick-start guides focus on simple setups with a single sender and a single receiver.
|
||||
Most users are served well with a single sender and a single receiver job.
|
||||
This section documents considerations for more complex setups.
|
||||
|
||||
.. ATTENTION::
|
||||
@@ -288,11 +288,46 @@ This section might be relevant to users who wish to *fan-in* (N machines replica
|
||||
|
||||
**Working setups**:
|
||||
|
||||
* N ``push`` identities, 1 ``sink`` (as long as the different push jobs have a different :ref:`client identity <overview-passive-side--client-identity>`)
|
||||
* **Fan-in: N servers replicated to one receiver, disjoint dataset trees.**
|
||||
|
||||
* ``sink`` constrains each client to a disjoint sub-tree of the sink-side dataset hierarchy ``${root_fs}/${client_identity}``.
|
||||
* This is the common use case of a centralized backup server.
|
||||
|
||||
* Implementation:
|
||||
|
||||
* N ``push`` jobs (one per sender server), 1 ``sink`` (as long as the different push jobs have a different :ref:`client identity <overview-passive-side--client-identity>`)
|
||||
* N ``source`` jobs (one per sender server), N ``pull`` on the receiver server (unique names, disjoing ``root_fs``)
|
||||
|
||||
* The ``sink`` job automatically constrains each client to a disjoint sub-tree of the sink-side dataset hierarchy ``${root_fs}/${client_identity}``.
|
||||
Therefore, the different clients cannot interfere.
|
||||
|
||||
* The ``pull`` job only pulls from one host, so it's up to the zrepl user to ensure that the different ``pull`` jobs don't interfere.
|
||||
|
||||
.. _fan-out-replication:
|
||||
|
||||
* **Fan-out: 1 server replicated to N receivers**
|
||||
|
||||
* Can be implemented either in a pull or push fashion.
|
||||
|
||||
* **pull setup**: 1 ``pull`` job on each receiver server, each with a corresponding **unique** ``source`` job on the sender server.
|
||||
* **push setup**: 1 ``sink`` job on each receiver server, each with a corresponding **unique** ``push`` job on the sender server.
|
||||
|
||||
* It is critical that we have one sending-side job (``source``, ``push``) per receiver.
|
||||
The reason is that :ref:`zrepl's ZFS abstractions <zrepl-zfs-abstractions>` (``zrepl zfs-abstraction list``) include the name of the ``source``/``push`` job, but not the receive-side job name or client identity (see :issue:`380`).
|
||||
As a counter-example, suppose we used multiple ``pull`` jobs with only one ``source`` job.
|
||||
All ``pull`` jobs would share the same :ref:`replication cursor bookmark <replication-cursor-and-last-received-hold>` and trip over each other, breaking incremental replication guarantees quickly.
|
||||
The anlogous problem exists for 1 ``push`` to N ``sink`` jobs.
|
||||
|
||||
* The ``filesystems`` matched by the sending side jobs (``source``, ``push``) need not necessarily be disjoint.
|
||||
For this to work, we need to avoid interference between snapshotting and pruning of the different sending jobs.
|
||||
The solution is to centralize sender-side snapshot management in a separate ``snap`` job.
|
||||
Snapshotting in the ``source``/``push`` job should then be disabled (``type: manual``).
|
||||
And sender-side pruning (``keep_sender``) needs to be disabled in the active side (``pull`` / ``push``), since that'll be done by the ``snap job``.
|
||||
|
||||
* **Restore limitations**: when restoring from one of the ``pull`` targets (e.g., using ``zfs send -R``), the replication cursor bookmarks don't exist on the restored system.
|
||||
This can break incremental replication to all other receive-sides after restore.
|
||||
|
||||
* See :ref:`the fan-out replication quick-start guide <quickstart-fan-out-replication>` for an example of this setup.
|
||||
|
||||
|
||||
**Setups that do not work**:
|
||||
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@
|
||||
|
||||
.. |GitHub license| image:: https://img.shields.io/github/license/zrepl/zrepl.svg
|
||||
:target: https://github.com/zrepl/zrepl/blob/master/LICENSE
|
||||
.. |Language: Go| image:: https://img.shields.io/badge/language-Go-6ad7e5.svg
|
||||
.. |Language: Go| image:: https://img.shields.io/badge/lang-Go-6ad7e5.svg
|
||||
:target: https://golang.org/
|
||||
.. |User Docs| image:: https://img.shields.io/badge/docs-web-blue.svg
|
||||
:target: https://zrepl.github.io
|
||||
@@ -13,13 +13,14 @@
|
||||
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R5QSXJVYHGX96
|
||||
.. |Donate via Liberapay| image:: https://img.shields.io/liberapay/patrons/zrepl.svg?logo=liberapay
|
||||
:target: https://liberapay.com/zrepl/donate
|
||||
.. |Donate via Patreon| image:: https://img.shields.io/badge/dynamic/json?color=yellow&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F3095079
|
||||
.. |Donate via Patreon| image:: https://img.shields.io/badge/dynamic/json?color=yellow&label=Patreon&query=data.attributes.patron_count&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F3095079
|
||||
:target: https://www.patreon.com/zrepl
|
||||
.. |Donate via GitHub Sponsors| image:: https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=yellow
|
||||
:target: https://github.com/sponsors/problame
|
||||
.. |Twitter| image:: https://img.shields.io/twitter/url/https/github.com/zrepl/zrepl.svg?style=social
|
||||
:target: https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fzrepl%2Fzrepl
|
||||
|
||||
.. |Matrix| image:: https://img.shields.io/badge/chat-matrix-blue.svg
|
||||
:target: https://matrix.to/#/#zrepl:matrix.org
|
||||
|
||||
.. |serve-transport| replace:: :ref:`serve specification<transport>`
|
||||
.. |connect-transport| replace:: :ref:`connect specification<transport>`
|
||||
|
||||
+2
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
.. include:: global.rst.inc
|
||||
|
||||
|GitHub license| |Language: Go| |Twitter| |Donate via Patreon| |Donate via GitHub Sponsors| |Donate via Liberapay| |Donate via PayPal|
|
||||
|GitHub license| |Language: Go| |Twitter| |Donate via Patreon| |Donate via GitHub Sponsors| |Donate via Liberapay| |Donate via PayPal| |Matrix|
|
||||
|
||||
|
||||
zrepl - ZFS replication
|
||||
@@ -137,5 +137,6 @@ Table of Contents
|
||||
pr
|
||||
changelog
|
||||
GitHub Repository & Issue Tracker <https://github.com/zrepl/zrepl>
|
||||
Chat: Matrix <https://matrix.to/#/#zrepl:matrix.org>
|
||||
supporters
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ Keep the :ref:`full config documentation <configuration_toc>` handy if a config
|
||||
|
||||
quickstart/continuous_server_backup
|
||||
quickstart/backup_to_external_disk
|
||||
quickstart/fan_out_replication
|
||||
|
||||
Use ``zrepl configcheck`` to validate your configuration.
|
||||
No output indicates that everything is fine.
|
||||
|
||||
@@ -9,13 +9,13 @@ This config example shows how we can backup our ZFS-based server to another mach
|
||||
|
||||
* Production server ``prod`` with filesystems to back up:
|
||||
|
||||
* ``zroot/var/db``
|
||||
* ``zroot/usr/home`` and all its child filesystems
|
||||
* **except** ``zroot/usr/home/paranoid`` belonging to a user doing backups themselves
|
||||
* The entire pool ``zroot``
|
||||
* except ``zroot/var/tmp`` and all child datasets of it
|
||||
* and except ``zroot/usr/home/paranoid`` which belongs to a user doing backups themselves.
|
||||
|
||||
* Backup server ``backups`` with
|
||||
* Backup server ``backups`` with a dataset sub-tree for use by zrepl:
|
||||
|
||||
* Filesystem ``storage/zrepl/sink/prod`` + children dedicated to backups of ``prod``
|
||||
* In our example, that will be ``storage/zrepl/sink/prod``.
|
||||
|
||||
Our backup solution should fulfill the following requirements:
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
.. include:: ../global.rst.inc
|
||||
|
||||
.. _quickstart-fan-out-replication:
|
||||
|
||||
Fan-out replication
|
||||
===================
|
||||
|
||||
This quick-start example demonstrates how to implement a fan-out replication setup where datasets on a server (A) are replicated to multiple targets (B, C, etc.).
|
||||
|
||||
This example uses multiple ``source`` jobs on server A and ``pull`` jobs on the target servers.
|
||||
|
||||
.. WARNING::
|
||||
|
||||
Before implementing this setup, please see the caveats listed in the :ref:`fan-out replication configuration overview <fan-out-replication>`.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
On the source server (A), there should be:
|
||||
|
||||
* A ``snap`` job
|
||||
|
||||
* Creates the snapshots
|
||||
* Handles the pruning of snapshots
|
||||
|
||||
* A ``source`` job for target B
|
||||
|
||||
* Accepts connections from server B and B only
|
||||
|
||||
* Further ``source`` jobs for each additional target (C, D, etc.)
|
||||
|
||||
* Listens on a unique port
|
||||
* Only accepts connections from the specific target
|
||||
|
||||
On each target server, there should be:
|
||||
|
||||
* A ``pull`` job that connects to the corresponding ``source`` job on A
|
||||
|
||||
* ``prune_sender`` should keep all snapshots since A's ``snap`` job handles the pruning
|
||||
* ``prune_receiver`` can be configured as appropriate on each target server
|
||||
|
||||
Generate TLS Certificates
|
||||
-------------------------
|
||||
|
||||
Mutual TLS via the :ref:`TLS client authentication transport <transport-tcp+tlsclientauth>` can be used to secure the connections between the servers. In this example, a self-signed certificate is created for each server without setting up a CA.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
source=a.example.com
|
||||
targets=(
|
||||
b.example.com
|
||||
c.example.com
|
||||
# ...
|
||||
)
|
||||
|
||||
for server in "${source}" "${targets[@]}"; do
|
||||
openssl req -x509 -sha256 -nodes \
|
||||
-newkey rsa:4096 \
|
||||
-days 365 \
|
||||
-keyout "${server}.key" \
|
||||
-out "${server}.crt" \
|
||||
-addext "subjectAltName = DNS:${server}" \
|
||||
-subj "/CN=${server}"
|
||||
done
|
||||
|
||||
# Distribute each host's keypair
|
||||
for server in "${source}" "${targets[@]}"; do
|
||||
ssh root@"${server}" mkdir /etc/zrepl
|
||||
scp "${server}".{crt,key} root@"${server}":/etc/zrepl/
|
||||
done
|
||||
|
||||
# Distribute target certificates to the source
|
||||
scp "${targets[@]/%/.crt}" root@"${source}":/etc/zrepl/
|
||||
|
||||
# Distribute source certificate to the targets
|
||||
for server in "${targets[@]}"; do
|
||||
scp "${source}.crt" root@"${server}":/etc/zrepl/
|
||||
done
|
||||
|
||||
Configure source server A
|
||||
-------------------------
|
||||
|
||||
.. literalinclude:: ../../config/samples/quickstart_fan_out_replication_source.yml
|
||||
|
||||
Configure each target server
|
||||
----------------------------
|
||||
|
||||
.. literalinclude:: ../../config/samples/quickstart_fan_out_replication_target.yml
|
||||
|
||||
Go Back To Quickstart Guide
|
||||
---------------------------
|
||||
|
||||
:ref:`Click here <quickstart-apply-config>` to go back to the quickstart guide.
|
||||
@@ -9,9 +9,11 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zrepl/zrepl/util/chainlock"
|
||||
"github.com/zrepl/zrepl/util/zreplcircleci"
|
||||
)
|
||||
|
||||
func TestContextWithOptionalDeadline(t *testing.T) {
|
||||
zreplcircleci.SkipOnCircleCI(t, "test relies on predictably low scheduler latency")
|
||||
|
||||
ctx := context.Background()
|
||||
cctx, enforceDeadline := ContextWithOptionalDeadline(ctx)
|
||||
@@ -72,6 +74,7 @@ func TestContextWithOptionalDeadlineNegativeDeadline(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContextWithOptionalDeadlineParentCancellation(t *testing.T) {
|
||||
zreplcircleci.SkipOnCircleCI(t, "test relies on predictably low scheduler latency")
|
||||
|
||||
pctx, cancel := context.WithCancel(context.Background())
|
||||
cctx, enforceDeadline := ContextWithOptionalDeadline(pctx)
|
||||
|
||||
Reference in New Issue
Block a user