Git repository cluster setup

01_git-clusterIf you have gitosis and a number of repositories installed on a server and need to move them to another server, or you want to build a cluster to have more then one server serving the repositories, this post will describe how I solved this for my gitosis repositories.

For this article I assume that you already have a working gitosis installation as described in Setup gitosis to manage git repositories. Our existing gitosis server will be called the master-server.

The Concept

The cluster setup described in this article is not necessarily based on gitosis. You can deploy this setup as well for any other git repositories you have. To cluster a git repository, only standard git functionality is used. Having gitosis or a similar repository management tool just makes it easier.

The following setup will describe an active-active cluster for git repositories. As there is no master-slave relationship involved, it is possible to push or pull to or from any of the git servers in the cluster.

Prepare the new Server

To prepare the mirror-server follow the Setup gitosis to manage git repositories post, but do not configure the repositories on the mirror-server.

Instead of configuring each server individually, you can just add the mirror-servers as additional remote repositories to the gitosis-admin repository you already have cloned to your local machine. To do so, change to the directory of your already cloned gitosis-admin repository and add the mirror server to it as a remote repository.

[Desktop]$ git remote add mirror-server ssh://git@mirror.example.com:1234/gitosis-admin.git

As the remote repository is different from the local one a normal push will fail. The first push needs to be forced to override the remote repository on the mirror-server with the local one.

[Desktop]$ git push -f mirror-server master

When you check the remote repositories configured in this cloned repository you should now see two remote repositories configured.

[Desktop]$ git remote -v
mirror-server	ssh://git@mirror.example.com:1234/gitosis-admin.git (fetch)
mirror-server	ssh://git@mirror.example.com:1234/gitosis-admin.git (push)
origin	ssh://git@master.example.com:1234/gitosis-admin.git (fetch)
origin	ssh://git@master.example.com:1234/gitosis-admin.git (push)

Whenever you change the configuration of the git repositories, you need to push the changes to both repositories. For convenience I created a small script inside the gitosis-admin repository, I called “git-push-all”, that pushes my changes automatically to all remote repositories.

#!/bin/bash

REMOTE_LIST=`git remote`
for REMOTE in $REMOTE_LIST
do
	BRANCH_LIST=`git branch | sed "s/^\** *//"`
	for BRANCH in $BRANCH_LIST
	do
		echo "*** Perform: git push $REMOTE $BRANCH"
		git push $REMOTE $BRANCH
	done
done

I also created a script inside the gitosis-admin repository, called “git-pull-all”, to automatically pull the latest changes from all the remote repositories.

#!/bin/bash

REMOTE_LIST=`git remote`
for REMOTE in $REMOTE_LIST
do
	BRANCH_LIST=`git branch | sed "s/^\** *//"`
	for BRANCH in $BRANCH_LIST
	do
		echo "*** Perform: git pull $REMOTE $BRANCH"
		git pull $REMOTE $BRANCH
	done
done

As we need the repository servers to each push their changes to the other one, we need to setup the access between them. With gitosis adding access rights is quite easy. First we need to create an ssh-key for the “git” user gitosis is running as for both servers.

During the setup gitosis to manage git repositories the created user did not get a password to login with. That means to create the key, we need to login to the server as root and access the git user from there with “su – git”, as root change user does not require a password. From there you can create the ssh-key with the following command.

[Master]$ ssh-keygen -t rsa -b 4096

The created ssh-keys are stored in /home/git/.ssh/id_rsa.pub (public key) and /home/git/.ssh/id_rsa (private key) where they can be accessed by the user “git”. Copy the /home/git/.ssh/id_rsa.pub to your local computer and rename it to a meaningful name. I choose a name with the hostname and the user-name in it, so that I would know what they are used for. For example: git_master.example.com.pub

This needs to be done on both servers. When this is done you have 2 files which might have names like this.

git_master.example.com.pub
git_mirror.example.com.pub

Copy these keys into the “keys/” directory of the gitosis-admin repository. To more easliy configure access easier to the repositories you may want to create a group in the gitosis.conf inside the gitosis-admin repository. Open it with you favourite editor and add the group with both keys.

[group grp_mirror]
members = git_master.example.com.pub git_mirror.example.com.pub

This group can now be added to the repository or repositories you are planning to mirror. As it is a group, you don’t have to add every key manually to every repository, and if you add another mirror, you just have to change one line by adding the additional key to the “grp_mirror” group. The repository configuration might look similar to this after you added the “grp_mirror” group.

[group repository-name]
writable = repository-name
members = @grp_users user1_key.pub @grp_mirror

The changes can now be staged and committed to the local repository. You can use the created script “git-push-all” afterwards to easily push the changes to all remote repository locations.

[Desktop]$ git add .
[Desktop]$ git commit -m "Add mirror keys and configured permission for mirroring"
[Desktop]$ ./git-push-all

Mirror the repositories

The new mirror-server does not yet contain any repositories other then the gitosis-admin repository. With the next step we will mirror the repository from the master server to the mirror server.

As the ssh-keys are setup, connect to the mirror-server, change to the git user and execute the following command to mirror the repository. The first command will get you into the repository directory of the git user.

[Mirror]$ cd /home/git/repositories
[Mirror]$ git clone --mirror git@master.example.com/repository-name.git

This will clone the bare repository to the mirror-server. Now you have a copy of the repository from the master-server.

infoThis way of mirroring will mirror the repository content. It will not mirror the complete repository with all of its hooks. If you need a mirroring that includes all aspects of the repository, you should consider a file system based replication like rsync or unison, but those replication services are not a good idea for multi-master repositories.

The mirror server now has a copy of the repository as well as the remote address stored in the mirrored repository. You can check the remote URL with “git remote -v”.

On the other hand the master-server does not yet know about its mirror. To be able to push the masters changes to the mirror, the master needs to know the remote URL of the mirror-server. To configure it, connect to the master-server and change to the user “git”. Inside the repository directory run the following command to configure the remote URL. The third command will then test the URL to confirm that the authentication and the URL is correct.

[Master]$ cd /home/git/repositories/repository-name.git/
[Master]$ git remote add mirror-server git@mirror.example.com/repository-name.git
[Master]$ git push --mirror mirror-server

To keep the repositories up to date, hooks need to be created to push the changes to the remote repository. This hook needs to be configured in the same way on both servers. They will read out the remote repository locations configured and push the changes.

Create a script named “post-receive” with the following content. This script will be the hook that is triggered on every push to the repository.

#!/bin/bash
REMOTE_LIST=`git remote`
for REMOTE in $REMOTE_LIST
do
	echo "git push $REMOTE"
	git push $REMOTE
done

The post-receive hook will be triggered every time content is pushed to the repository. This script should be copied to the “hooks” directory of the repository you want to mirror. This should be done on both the master-server’s and the mirror-server’s repositories to be able to mirror both ways.

To be executed, this script file needs to be executable by the git user when new updates are pushed to the repository. With the following commands, the files are marked executable and the owner is changed to the git user.

[Master]$ cd /home/git/repositories/repository-name.git/hooks/
[Master]$ chown git:gitosis post-receive
[Master]$ chmod a+x post-receive

With the hook in place, the push output will show additional lines like those in the example below. As you can see the repository is mirrored from the master-server to the mirror-server. The mirror-server tries to push the changes back to the master server but this is up to date and therefore no update is performed. Without performing an update, the hook does not get called again on the master-server, so it does not create a loop.

Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 306 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: git push mirror-server
remote: remote: git push master-server        
remote: remote: Everything up-to-date        
remote: To ssh://git@mirror.example.com/repository-name.git
remote:    709387f..e2375b5  master -> master
To ssh://git@master.example.com/repository-name.git
   709387f..e2375b5  master -> master

Now whenever something is pushed to one of the servers (master or mirror), the hook gets triggered and will push the changes on to the other server. The procedure of the mirroring to the other server can be seen as well in the sample output above.


Read more of my posts on my blog at http://blog.tinned-software.net/.

This entry was posted in Linux Administration, Version control system and tagged , , , , . Bookmark the permalink.