Jetbrains released docker images for Teamcity (although not “official” as per the docker hub). I need to document the process internally, so I might as well turn it into an article and share it with everyone.
Using separate data container gives you much more flexibility in how you upgrade, test, or backup the services. You then have a pure service that you can kill restart throw away and upgrade without worrying about the configuration. The configuration will be provided by the long-live data container instead. you don’t need to backup the service. only the data container associated to it, etc…
We’ll have 4 pieces :
- Teamcity server : official image
- Teamcity data
- Postgres instance : official image
- Postgres data
Let’s start with the data containers
We’ll use an empty image to create empty data containers :
(If you want to know more, I prefer this reasoning, using an empty image to this one that uses the same base image as the app, but maybe I’m wrong, let me know :))
docker create -v /teamcity --name teamcity-data tianon/true echo 'teamcity data' docker create -v /postgres --name postgres-data tianon/true echo 'postgres data'
That gives you a container with nothing but 1 folder named
you can take a peak at it by doing the following :
C:\> docker run -it --rm --volumes-from teamcity-data ubuntu ls bin core etc lib media opt root sbin sys tmp var boot dev home lib64 mnt proc run srv *teamcity* usr
We map the volumes from our newly created teamcity-data container into a new ubuntu container and we run ls. we can see the
teamcity folder, mounted from our teamcity-data container.
Postgres is quite straight forward. We will start the postgres container, and take the volume from our postgres-data container :
docker run --volumes-from postgres-data \ --name tc-postgres \ -e POSTGRES_PASSWORD=<password_here> \ -e PGDATA=/postgres/pgdata postgres
The postgres image uses some environment variables during setup to define the password and the data folder for our db. The user is postgres by default. That’s what is provided after the -e parameter.
And that’s about it. Now we get :
C:\> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7f43642f2c2e postgres "/docker-entrypoint.s" 2 hours ago Up 2 hours 5432/tcp tc-postgres
I took a backup of our existing teamcity, from the teamcity UI, and unzipped it on my local machine :
C:\teamcityupgrade> ls Directory: C:\teamcityupgrade Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2016-07-22 01:05 PM teamcity -a---- 2016-07-22 01:01 PM 124890616 TeamCity_Backup.zip C:\teamcityupgrade> ls .\teamcity\ Directory: C:\teamcityupgrade\teamcity Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2016-07-22 01:01 PM config d----- 2016-07-22 01:01 PM lib d----- 2016-07-22 01:01 PM metadata d----- 2016-07-22 01:01 PM plugins d----- 2016-07-22 01:01 PM system -a---- 2016-07-22 10:56 AM 6 charset -a---- 2016-07-22 10:56 AM 681 export.report -a---- 2016-07-22 10:56 AM 87 version.txt
We’ll want to restore the database, and you can do that with the maintainDB.sh script from Teamcity. That’s in the TC container. Let’s see what we need :
- Use the volumes from our teamcity-data container
- Tell teamcity to use our volume folder (env variable)
- link it to postgres
- restore backup
- run teamcity
We’ll start with the db restore. We will bash into a teamcity container to get access to the maintainDB.sh script. we call the container
The script needs the
database.properties file, an empty config folder, the zipped backup file and a connection to postgres :
docker run -it --name restore-tc --rm \ --link tc-postgres \ # gives us a connection to postgres --volumes-from teamcity-data \ # gives us the config folder jetbrains/teamcity-server /bin/bash
Then we will copy the backup and the
database.properties and the driver libraries into the container. We’ll use docker cp to do that :
So from another console, we run :
docker cp C:\teamcityupgrade\TeamCity_Backup.zip restore-tc:/backup.zip
We edit the database.properties to connect to our postgres, and copy it too :
connectionProperties.user=postgres connectionProperties.password=<password_here> connectionUrl=jdbc\:postgresql\://tc-postgres\:5432/
Note the url uses the postgres container name. This works thanks to the link parameter in the previous command. Then we copy it over :
docker cp C:\teamcityupgrade\teamcity\config\database.properties restore-tc:/restore-database.properties
And finally the jdbc drivers for postgres:
docker cp C:\teamcityupgrade\teamcity\lib\jdbc\ restore-tc:/teamcity/jdbc/
Now, to restore the database, let’s go back to the bash console inside our
restore-tc container and run the
maintainDB.sh script (documentation):
:/# /opt/teamcity/bin/maintainDB.sh restore \ -A /teamcity/ \ -F /backup.zip \ -T /restore-database.properties
Voilà, backup is restored!
We could keep that container and run it from there, but we can also kill it and start a new one with about the same parameter, and without the /bin/bash.
all the config data is in the
teamcity-data data container now.
so we exit the
restore-tc container (we started it with –rm so it will be stopped and removed).
What we want :
- data from the teamcity-data (where we restored the config)
- tell teamcity to use the /teamcity folder as data folder
- use db from postgres-tc (we also just restored it)
- port exposed
- run as daemon
To run it, the full command will be :
docker run -d --name teamcity \ -p80:8111 \ -e TEAMCITY_DATA_PATH=/teamcity \ --link tc-postgres \ --volumes-from teamcity-data \ jetbrains/teamcity-server
Aaand… it works (I mapped it to port 8111 in my test) :)