Use NFS filesystem to host git repos on HA setup
Introduce an NFS server which is hosting the git repos.
This is done since it is closer to a real installation,
and allowed us to find or reproduce issues specific to
NFS. Also adapt the docker-compose file accordingly.
Change-Id: I60c34e458cd3adb1ad2d71bc0db4eb450de67630
diff --git a/src/test/README.md b/src/test/README.md
index 1b8e4a2..fd59f64 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -1,19 +1,31 @@
# Gerrit high-availability docker setup example
The Docker Compose project in the docker directory contains a simple test
-environment of two Gerrit masters in HA configuration.
+environment of two Gerrit masters in HA configuration, with their git repos
+hosted on NFS filesystem.
## How to build
-The project can be built using docker-compose.
+The project can be built using docker-compose (make sure you set the
+`platform` attribute in the docker-compose.yaml file if you're not
+in an amd64 arch).
To build the Docker VMs:
-```
- $ docker-compose build
+```bash
+ # first, remove the buildx if it exists and its not running
+ $ docker buildx inspect docker-ha | grep Status
+ $ docker buildx rm docker-ha
+ # create the docker-ha buildx node, provide your architecture and start it up
+ docker buildx create --name docker-ha --platform "linux/amd64" --driver docker-container --use \
+ && docker buildx inspect --bootstrap \
+ && docker-compose build
```
### Building the Docker VMs using a non-default user id
+First, update the user id in the [NFS Dockerfile](./docker/nfs/Dockerfile).
+This is done simply by modifying the file setting the non-default user id.
+Then, run the following:
```
$ export GERRIT_UID=$(id -u)
$ docker-compose build --build-arg GERRIT_UID
@@ -24,6 +36,9 @@
host is not owned by you. For example, some corporate environments use a
restricted 1000 user (id). In that case, the containerized application
may fail to write towards the host (through volumes).
+**Important:** The user id in gerrit must be the same as the uid in the
+NFS server, otherwise you will encounter file ownership problems on any
+filesystem operation.
That UID will be the one set for the containerized gerrit user. Latter's
group will remain as default (1000). This is because groups known from
@@ -36,9 +51,41 @@
Use the 'up' target to startup the Docker Compose VMs.
```
- $ docker-compose up
+ $ docker-compose up -d
```
+## Background on using an NFS server
+We are using the `erichough/nfs-server` image mainly because it's easy to use
+& we had success with it. The work has been inspired by
+[this blog post](https://nothing2say.co.uk/running-a-linux-based-nfs-server-in-docker-on-windows-b64445d5ada2).
+
+The containers start with the `privileged` flag set, which is a security risk
+but necessary to work around permission issues.
+
+It is worth noting that we are exposing the `/var/gerrit/git` directory as the
+nfs-share. This is because more often than not it's the git directory that's
+shared over the network. You can change this in the nfs server and gerrit
+docker files, and in the `exports.txt` file.
+
+The NFS server is using a static IP. The Docker Compose YAML file defines a
+bridge network with the subnet `192.168.1.0/24` (this is what allows us to
+give the NFS Server a known, static IP).
+
+The `addr=192.168.1.5` option (in the `nfs-client-volume` volume) is the
+reason we need a static IP for the server (and hence a configured subnet
+for the network). Note that using a name (ie. addr=nfs-server) we weren't
+able to get the DNS resolution to work properly.
+
+Also in the Docker Compose file we can see that the `nfs-server` container
+uses a `healthcheck`, this is necessary to control when the `gerrit`
+services will start up (they need to start after the nfs server is fully
+up-and-running).
+
+Finally, we are providing an `exports.txt` file, which again utilises the
+subnet we provided during the bridge network creation. This file is baked
+into the image sacrificing a bit of flexibility, but we feel this is
+a small price to pay to have everything automated.
+
# Gerrit high-availability local setup example
1. Init gerrit instances with high-availability plugin installed:
diff --git a/src/test/docker/docker-compose.yaml b/src/test/docker/docker-compose.yaml
index 42c12c9..59bec58 100644
--- a/src/test/docker/docker-compose.yaml
+++ b/src/test/docker/docker-compose.yaml
@@ -2,16 +2,40 @@
services:
+ nfs-server:
+ build: nfs
+# platform: linux/arm64/v8 # uncomment for Apple Silicon arch
+ privileged: true
+ container_name: nfs-server
+ environment:
+ NFS_LOG_LEVEL: DEBUG
+ hostname: nfs-server
+ healthcheck:
+ test: ["CMD-SHELL", "sleep 10"] # required, otherwise the gerrit service will fail to start with a "connection refused" error
+ interval: 1s
+ timeout: 1m
+ retries: 10
+ ports:
+ - 2049:2049
+ networks:
+ nfs-server-bridge:
+ ipv4_address: 192.168.1.5
+ volumes:
+ - nfs-server-volume:/var/gerrit/git
gerrit-01:
build: gerrit
+ privileged: true
+ depends_on:
+ nfs-server:
+ condition: service_healthy
ports:
- "8081:8080"
- "29411:29418"
networks:
- - gerrit-net
+ nfs-server-bridge: null
volumes:
- /dev/urandom:/dev/random
- - gitvolume:/var/gerrit/git
+ - git-volume:/var/gerrit/git
- shareddir:/var/gerrit/shareddir
- ./etc/gerrit.config:/var/gerrit/etc/gerrit.config.orig
- ./etc/high-availability.gerrit-01.config:/var/gerrit/etc/high-availability.config.orig
@@ -20,16 +44,20 @@
gerrit-02:
build: gerrit
+ privileged: true
ports:
- "8082:8080"
- "29412:29418"
networks:
- - gerrit-net
+ nfs-server-bridge: null
depends_on:
- - gerrit-01
+ gerrit-01:
+ condition: service_started
+ nfs-server:
+ condition: service_healthy
volumes:
- /dev/urandom:/dev/random
- - gitvolume:/var/gerrit/git
+ - git-volume:/var/gerrit/git
- shareddir:/var/gerrit/shareddir
- ./etc/gerrit.config:/var/gerrit/etc/gerrit.config.orig
- ./etc/high-availability.gerrit-02.config:/var/gerrit/etc/high-availability.config.orig
@@ -43,9 +71,9 @@
- "80:80"
- "29418:29418"
networks:
- - gerrit-net
+ nfs-server-bridge: null
volumes:
- - syslog-sidecar
+ - syslog-sidecar:/syslog-sidecar
depends_on:
- syslog-sidecar
- gerrit-01
@@ -54,13 +82,22 @@
syslog-sidecar:
build: docker-syslog-ng-stdout
networks:
- - gerrit-net
+ nfs-server-bridge: null
networks:
- gerrit-net:
- driver: bridge
+ nfs-server-bridge:
+ ipam:
+ driver: default
+ config:
+ - subnet: 192.168.1.0/24
volumes:
syslog-sidecar:
shareddir:
- gitvolume:
+ nfs-server-volume:
+ git-volume:
+ driver: "local"
+ driver_opts:
+ type: nfs
+ o: "addr=192.168.1.5,rw"
+ device: ":/var/gerrit/git"
diff --git a/src/test/docker/gerrit/Dockerfile b/src/test/docker/gerrit/Dockerfile
index 820be4c..30912e9 100644
--- a/src/test/docker/gerrit/Dockerfile
+++ b/src/test/docker/gerrit/Dockerfile
@@ -1,27 +1,52 @@
-FROM gerritcodereview/gerrit:3.5.0-rc2
+FROM almalinux:8.5
-ENV GERRIT_BRANCH=stable-3.5
+# Install dependencies
+RUN yum -y install \
+ git \
+ java-11-openjdk \
+ procps \
+ sudo \
+ passwd \
+ gettext \
+ && yum -y clean all
-ENV GERRIT_CI_URL=https://gerrit-ci.gerritforge.com/job
+ENV GERRIT_VERSION=3.6
+ENV JAVA_HOME /usr/lib/jvm/jre-11-openjdk
-USER root
+# Add gerrit user
+RUN adduser -p -m --uid 1000 gerrit --home-dir /home/gerrit
+RUN echo "gerrit:gerrit" | chpasswd
+RUN echo "gerrit ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/gerrit && \
+ chmod 0440 /etc/sudoers.d/gerrit
-RUN yum install -y iputils nmap curl lsof gettext net-tools sudo
+# Create gerrit installation directory
+RUN mkdir -p /var/gerrit && chown -R gerrit /var/gerrit
+ADD --chown=gerrit \
+ "https://gerrit-ci.gerritforge.com/job/Gerrit-bazel-stable-$GERRIT_VERSION/lastSuccessfulBuild/artifact/gerrit/bazel-bin/release.war" \
+ /tmp/gerrit.war
+
+ADD --chown=gerrit \
+"https://gerrit-ci.gerritforge.com/job/plugin-javamelody-bazel-master-stable-$GERRIT_VERSION/lastSuccessfulBuild/artifact/bazel-bin/plugins/javamelody/javamelody.jar" \
+ /var/gerrit/plugins/javamelody.jar
+ADD --chown=gerrit \
+ "https://gerrit-ci.gerritforge.com/job/plugin-high-availability-bazel-stable-$GERRIT_VERSION/lastSuccessfulBuild/artifact/bazel-bin/plugins/high-availability/high-availability.jar" \
+ /var/gerrit/plugins/high-availability.jar
+
+ADD --chown=gerrit \
+ "https://repo1.maven.org/maven2/com/gerritforge/global-refdb/$GERRIT_VERSION.3.4/global-refdb-$GERRIT_VERSION.3.4.jar" \
+ /tmp
+
+ADD --chown=gerrit:gerrit ./wait-for-it.sh /bin
+
+# Change user
USER gerrit
-ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-javamelody-bazel-master-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/javamelody/javamelody.jar /var/gerrit/plugins/javamelody.jar
-ADD --chown=gerrit:gerrit $GERRIT_CI_URL/plugin-high-availability-bazel-master-$GERRIT_BRANCH/lastSuccessfulBuild/artifact/bazel-bin/plugins/high-availability/high-availability.jar /var/gerrit/plugins/high-availability.jar
+# Expose ssh and http ports
+EXPOSE 29418 8080
+
+COPY --chown=gerrit ./entrypoint.sh /bin
USER root
-ADD start.sh /bin/
-ADD wait-for-it.sh /bin/
-
-RUN rm -Rf /var/gerrit/{git,index,cache}/*
-
-ARG GERRIT_UID=1000
-RUN usermod -u ${GERRIT_UID} gerrit &> /dev/null
-
-ENTRYPOINT ["/usr/bin/env"]
-CMD /bin/start.sh
+ENTRYPOINT /bin/entrypoint.sh
\ No newline at end of file
diff --git a/src/test/docker/gerrit/entrypoint.sh b/src/test/docker/gerrit/entrypoint.sh
new file mode 100755
index 0000000..b382a3b
--- /dev/null
+++ b/src/test/docker/gerrit/entrypoint.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+if [[ ! -z "$WAIT_FOR" ]]
+then
+ wait-for-it.sh $WAIT_FOR -t 600 -- echo "$WAIT_FOR is up"
+fi
+
+chown -R gerrit /var/gerrit/etc
+sudo -u gerrit cp /var/gerrit/etc/gerrit.config.orig /var/gerrit/etc/gerrit.config
+sudo -u gerrit cp /var/gerrit/etc/high-availability.config.orig /var/gerrit/etc/high-availability.config
+
+
+echo "Init gerrit..."
+sudo -u gerrit java -jar /tmp/gerrit.war init -d /var/gerrit --batch --install-all-plugins
+chown -R gerrit: /var/gerrit/shareddir
+
+# required until regression is fixed, see https://groups.google.com/g/repo-discuss/c/DH-ftHMiCyE/m/qF88c6KMAAAJ
+echo "Copying global ref db jar into lib"
+sudo -u gerrit cp /tmp/global-refdb-*.jar /var/gerrit/lib
+
+echo "Reindexing Gerrit..."
+cd /var/gerrit && sudo -u gerrit java -jar /var/gerrit/bin/gerrit.war reindex -d /var/gerrit
+sudo -u gerrit git config -f /var/gerrit/etc/gerrit.config gerrit.canonicalWebUrl http://$HOSTNAME/
+sudo -u gerrit touch /var/gerrit/logs/{gc_log,error_log,httpd_log,sshd_log,replication_log} && tail -f /var/gerrit/logs/* | grep --line-buffered -v 'HEAD /' &
+
+echo "Running Gerrit ..."
+sudo -u gerrit /var/gerrit/bin/gerrit.sh run
diff --git a/src/test/docker/gerrit/start.sh b/src/test/docker/gerrit/start.sh
deleted file mode 100755
index d906628..0000000
--- a/src/test/docker/gerrit/start.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash -e
-
-if [[ ! -z "$WAIT_FOR" ]]
-then
- wait-for-it.sh $WAIT_FOR -t 600 -- echo "$WAIT_FOR is up"
-fi
-
-sudo -u gerrit cp /var/gerrit/etc/gerrit.config.orig /var/gerrit/etc/gerrit.config
-sudo -u gerrit cp /var/gerrit/etc/high-availability.config.orig /var/gerrit/etc/high-availability.config
-
-if [[ ! -f /var/gerrit/etc/ssh_host_ed25519_key ]]
-then
- echo "Initializing Gerrit site ..."
- sudo -u gerrit java -jar /var/gerrit/bin/gerrit.war init -d /var/gerrit --batch
- chown -R gerrit: /var/gerrit/shareddir
-fi
-
-echo "Reindexing Gerrit ..."
-sudo -u gerrit java -jar /var/gerrit/bin/gerrit.war reindex -d /var/gerrit
-sudo -u gerrit git config -f /var/gerrit/etc/gerrit.config gerrit.canonicalWebUrl http://$HOSTNAME/
-
-sudo -u gerrit touch /var/gerrit/logs/{gc_log,error_log,httpd_log,sshd_log,replication_log} && tail -f /var/gerrit/logs/* | grep --line-buffered -v 'HEAD /' &
-
-echo "Running Gerrit ..."
-sudo -u gerrit /var/gerrit/bin/gerrit.sh run
diff --git a/src/test/docker/nfs/Dockerfile b/src/test/docker/nfs/Dockerfile
new file mode 100644
index 0000000..19744f3
--- /dev/null
+++ b/src/test/docker/nfs/Dockerfile
@@ -0,0 +1,13 @@
+FROM erichough/nfs-server
+
+COPY exports.txt /etc/exports
+
+# To avoid ownership issues, the user must be the same betweeen the
+# server and the client, hence we are creating it explicitly in both.
+RUN adduser --disabled-password --gecos "" --uid 1000 gerrit
+
+# /var/gerrit/git is the shared directory
+RUN mkdir --parents /var/gerrit/git
+
+RUN chown gerrit:gerrit /var/lib/nfs
+RUN chown gerrit:gerrit /var/gerrit/git
\ No newline at end of file
diff --git a/src/test/docker/nfs/exports.txt b/src/test/docker/nfs/exports.txt
new file mode 100644
index 0000000..c31d586
--- /dev/null
+++ b/src/test/docker/nfs/exports.txt
@@ -0,0 +1 @@
+/var/gerrit/git 192.168.1.0/24(rw,no_subtree_check,insecure)
\ No newline at end of file