mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 04:20:55 +01:00
Add bin/repository clusterize
and document setup and migration for clusters
Summary: Ref T4292. This provides at least some sort of hint about how to set up cluster repositories. Test Plan: - Read documentation. - Ran `bin/repository clusterize` to add + remove clusters. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4292 Differential Revision: https://secure.phabricator.com/D15798
This commit is contained in:
parent
8d9bc401e4
commit
dc3a13c5e8
4 changed files with 286 additions and 24 deletions
|
@ -3174,6 +3174,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryGraphCache' => 'applications/repository/graphcache/PhabricatorRepositoryGraphCache.php',
|
||||
'PhabricatorRepositoryGraphStream' => 'applications/repository/daemon/PhabricatorRepositoryGraphStream.php',
|
||||
'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php',
|
||||
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php',
|
||||
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php',
|
||||
'PhabricatorRepositoryManagementEditWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php',
|
||||
'PhabricatorRepositoryManagementImportingWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php',
|
||||
|
@ -7832,6 +7833,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryGraphCache' => 'Phobject',
|
||||
'PhabricatorRepositoryGraphStream' => 'Phobject',
|
||||
'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementEditWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementImportingWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorRepositoryManagementClusterizeWorkflow
|
||||
extends PhabricatorRepositoryManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('clusterize')
|
||||
->setExamples('**clusterize** [options] __repository__ ...')
|
||||
->setSynopsis(
|
||||
pht('Convert existing repositories into cluster repositories.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'service',
|
||||
'param' => 'service',
|
||||
'help' => pht(
|
||||
'Cluster repository service in Almanac to move repositories '.
|
||||
'into.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'remove-service',
|
||||
'help' => pht('Take repositories out of a cluster.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'repositories',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$repositories = $this->loadRepositories($args, 'repositories');
|
||||
if (!$repositories) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify one or more repositories to clusterize.'));
|
||||
}
|
||||
|
||||
$service_name = $args->getArg('service');
|
||||
$remove_service = $args->getArg('remove-service');
|
||||
|
||||
if ($remove_service && $service_name) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify --service or --remove-service, but not both.'));
|
||||
}
|
||||
|
||||
if (!$service_name && !$remove_service) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify --service or --remove-service.'));
|
||||
}
|
||||
|
||||
if ($remove_service) {
|
||||
$service = null;
|
||||
} else {
|
||||
$service = id(new AlmanacServiceQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($service_name))
|
||||
->withServiceTypes(
|
||||
array(
|
||||
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$service) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'No repository service "%s" exists.',
|
||||
$service_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($service) {
|
||||
$service_phid = $service->getPHID();
|
||||
} else {
|
||||
$service_phid = null;
|
||||
}
|
||||
|
||||
$content_source = $this->newContentSource();
|
||||
$diffusion_phid = id(new PhabricatorDiffusionApplication())->getPHID();
|
||||
|
||||
foreach ($repositories as $repository) {
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new PhabricatorRepositoryTransaction())
|
||||
->setTransactionType(PhabricatorRepositoryTransaction::TYPE_SERVICE)
|
||||
->setNewValue($service_phid);
|
||||
|
||||
id(new PhabricatorRepositoryEditor())
|
||||
->setActor($viewer)
|
||||
->setActingAsPHID($diffusion_phid)
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true)
|
||||
->applyTransactions($repository, $xactions);
|
||||
|
||||
if ($service) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Moved repository "%s" to cluster service "%s".',
|
||||
$repository->getDisplayName(),
|
||||
$service->getName()));
|
||||
} else {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Removed repository "%s" from cluster service.',
|
||||
$repository->getDisplayName()));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -93,16 +93,16 @@ application, see @{article:Almanac User Guide}.
|
|||
|
||||
Add **interfaces** to each device record so Phabricator can tell how to
|
||||
connect to these hosts. Normally, you'll add one HTTP interface (usually on
|
||||
port 80) and one SSH interface (often on port 22) to each device:
|
||||
port 80) and one SSH interface (by default, on port 2222) to each device:
|
||||
|
||||
For example, if you are building a two-host repository cluster, you may end
|
||||
up with records that look like these:
|
||||
|
||||
- Device: `repo001.mycompany.net`
|
||||
- Interface: `123.0.0.1:22`
|
||||
- Interface: `123.0.0.1:2222`
|
||||
- Interface: `123.0.0.1:80`
|
||||
- Device: `repo002.mycopmany.net`
|
||||
- Interface: `123.0.0.2:22`
|
||||
- Interface: `123.0.0.2:2222`
|
||||
- Interface: `123.0.0.2:80`
|
||||
|
||||
Note that these hosts will normally run two `sshd` ports: the standard `sshd`
|
||||
|
@ -230,6 +230,11 @@ sure the process is completed correctly.
|
|||
Note that a copy of the active private key is stored in the `conf/keys/`
|
||||
directory permanently.
|
||||
|
||||
When converting a host into a cluster host, you may need to revisit
|
||||
@{article:Diffusion User Guide: Repository Hosting} and double check the `sudo`
|
||||
permission for the host. In particular, cluster hosts need to be able to run
|
||||
`ssh` via `sudo` so they can read the device private key.
|
||||
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
|
|
@ -9,9 +9,9 @@ Overview
|
|||
WARNING: This feature is a very early prototype; the features this document
|
||||
describes are mostly speculative fantasy.
|
||||
|
||||
If you use Git or Mercurial, you can deploy Phabricator with multiple
|
||||
repository hosts, configured so that each host is readable and writable. The
|
||||
advantages of doing this are:
|
||||
If you use Git, you can deploy Phabricator with multiple repository hosts,
|
||||
configured so that each host is readable and writable. The advantages of doing
|
||||
this are:
|
||||
|
||||
- you can completely survive the loss of repository hosts;
|
||||
- reads and writes can scale across multiple machines; and
|
||||
|
@ -22,24 +22,6 @@ This configuration is complex, and many installs do not need to pursue it.
|
|||
This configuration is not currently supported with Subversion or Mercurial.
|
||||
|
||||
|
||||
Repository Hosts
|
||||
================
|
||||
|
||||
Repository hosts must run a complete, fully configured copy of Phabricator,
|
||||
including a webserver. They must also run a properly configured `sshd`.
|
||||
|
||||
Generally, these hosts will run the same set of services and configuration that
|
||||
web hosts run. If you prefer, you can overlay these services and put web and
|
||||
repository services on the same hosts. See @{article:Clustering Introduction}
|
||||
for some guidance on overlaying services.
|
||||
|
||||
When a user requests information about a repository that can only be satisfied
|
||||
by examining a repository working copy, the webserver receiving the request
|
||||
will make an HTTP service call to a repository server which hosts the
|
||||
repository to retrieve the data it needs. It will use the result of this query
|
||||
to respond to the user.
|
||||
|
||||
|
||||
How Reads and Writes Work
|
||||
=========================
|
||||
|
||||
|
@ -95,6 +77,162 @@ Other mitigations are possible, but securing a network against the NSA and
|
|||
similar agents of other rogue nations is beyond the scope of this document.
|
||||
|
||||
|
||||
Repository Hosts
|
||||
================
|
||||
|
||||
Repository hosts must run a complete, fully configured copy of Phabricator,
|
||||
including a webserver. They must also run a properly configured `sshd`.
|
||||
|
||||
If you are converting existing hosts into cluster hosts, you may need to
|
||||
revisit @{article:Diffusion User Guide: Repository Hosting} and make sure
|
||||
the system user accounts have all the necessary `sudo` permissions. In
|
||||
particular, cluster devices need `sudo` access to `ssh` so they can read
|
||||
device keys.
|
||||
|
||||
Generally, these hosts will run the same set of services and configuration that
|
||||
web hosts run. If you prefer, you can overlay these services and put web and
|
||||
repository services on the same hosts. See @{article:Clustering Introduction}
|
||||
for some guidance on overlaying services.
|
||||
|
||||
When a user requests information about a repository that can only be satisfied
|
||||
by examining a repository working copy, the webserver receiving the request
|
||||
will make an HTTP service call to a repository server which hosts the
|
||||
repository to retrieve the data it needs. It will use the result of this query
|
||||
to respond to the user.
|
||||
|
||||
|
||||
Setting up a Cluster Services
|
||||
=============================
|
||||
|
||||
To set up clustering, first register the devices that you want to use as part
|
||||
of the cluster with Almanac. For details, see @{article:Cluster: Devices}.
|
||||
|
||||
NOTE: Once you create a service, new repositories will immediately allocate
|
||||
on it. You may want to disable repository creation during initial setup.
|
||||
|
||||
Once the hosts are registered as devices, you can create a new service in
|
||||
Almanac:
|
||||
|
||||
- First, register at least one device according to the device clustering
|
||||
instructions.
|
||||
- Create a new service of type **Phabricator Cluster: Repository** in
|
||||
Almanac.
|
||||
- Bind this service to all the interfaces on the device or devices.
|
||||
- For each binding, add a `protocol` key with one of these values:
|
||||
`ssh`, `http`, `https`.
|
||||
|
||||
For example, a service might look like this:
|
||||
|
||||
- Service: `repos001.mycompany.net`
|
||||
- Binding: `repo001.mycompany.net:80`, `protocol=http`
|
||||
- Binding: `repo001.mycompany.net:2222`, `protocol=ssh`
|
||||
|
||||
The service itself has a `closed` property. You can set this to `true` to
|
||||
disable new repository allocations on this service (for example, if it is
|
||||
reaching capacity).
|
||||
|
||||
|
||||
Migrating to Clustered Services
|
||||
===============================
|
||||
|
||||
To convert existing repositories on an install into cluster repositories, you
|
||||
will generally perform these steps:
|
||||
|
||||
- Register the existing host as a cluster device.
|
||||
- Configure a single host repository service using //only// that host.
|
||||
|
||||
This puts you in a transitional state where repositories on the host can work
|
||||
as either on-host repositories or cluster repositories. You can move forward
|
||||
from here slowly and make sure services still work, with a quick path back to
|
||||
safety if you run into trouble.
|
||||
|
||||
To move forward, migrate one repository to the service and make sure things
|
||||
work correctly. If you run into issues, you can back out by migrating the
|
||||
repository off the service.
|
||||
|
||||
To migrate a repository onto a cluster service, use this command:
|
||||
|
||||
```
|
||||
$ ./bin/repository clusterize <repository> --service <service>
|
||||
```
|
||||
|
||||
To migrate a repository back off a service, use this command:
|
||||
|
||||
```
|
||||
$ ./bin/repoistory clusterize <repository> --remove-service
|
||||
```
|
||||
|
||||
This command only changes how Phabricator connects to the repository; it does
|
||||
not move any data or make any complex structural changes.
|
||||
|
||||
When Phabricator needs information about a non-clustered repository, it just
|
||||
runs a command like `git log` directly on disk. When Phabricator needs
|
||||
information about a clustered repository, it instead makes a service call to
|
||||
another server, asking that server to run `git log` instead.
|
||||
|
||||
In a single-host cluster the server will make this service call to itself, so
|
||||
nothing will really change. But this //is// an effective test for most
|
||||
possible configuration mistakes.
|
||||
|
||||
If your canary repository works well, you can migrate the rest of your
|
||||
repositories when ready (you can use `bin/repository list` to quickly get a
|
||||
list of all repository monograms).
|
||||
|
||||
Once all repositories are migrated, you've reached a stable state and can
|
||||
remain here as long as you want. This state is sufficient to convert daemons,
|
||||
SSH, and web services into clustered versions and spread them across multiple
|
||||
machines if those goals are more interesting.
|
||||
|
||||
Obviously, your single-device "cluster" will not be able to survive the loss of
|
||||
the single repository host, but you can take as long as you want to expand the
|
||||
cluster and add redundancy.
|
||||
|
||||
After creating a service, you do not need to `clusterize` new repositories:
|
||||
they will automatically allocate onto an open service.
|
||||
|
||||
When you're ready to expand the cluster, continue below.
|
||||
|
||||
|
||||
Expanding a Cluster
|
||||
===================
|
||||
|
||||
To expand an existing cluster, follow these general steps:
|
||||
|
||||
- Register new devices in Almanac.
|
||||
- Add bindings to the new devices to the repository service, also in Almanac.
|
||||
- Start the daemons on the new devices.
|
||||
|
||||
For instructions on configuring and registering devices, see
|
||||
@{article:Cluster: Devices}.
|
||||
|
||||
As soon as you add active bindings to a service, Phabricator will begin
|
||||
synchronizing repositories and sending traffic to the new device. You do not
|
||||
need to copy any repository data to the device: Phabricator will automatically
|
||||
synchronize it.
|
||||
|
||||
If you have a large amount of repository data, you may want to help this
|
||||
process along by copying the repository directory from an existing cluster
|
||||
device before bringing the new host online. This is optional, but can reduce
|
||||
the amount of time required to fully synchronize the cluster.
|
||||
|
||||
You do not need to synchronize the most up-to-date data or stop writes during
|
||||
this process. For example, loading the most recent backup snapshot onto the new
|
||||
device will substantially reduce the amount of data that needs to be
|
||||
synchronized.
|
||||
|
||||
|
||||
Contracting a Cluster
|
||||
=====================
|
||||
|
||||
To reduce the size of an existing cluster, follow these general steps:
|
||||
|
||||
- Disable the bindings from the service to the dead device in Almanac.
|
||||
|
||||
If you are removing a device because it failed abruptly (or removing several
|
||||
devices at once) it is possible that some repositories will have lost all their
|
||||
leaders. See "Loss of Leaders" below to understand and resolve this.
|
||||
|
||||
|
||||
Monitoring Services
|
||||
===================
|
||||
|
||||
|
|
Loading…
Reference in a new issue