Niniejsza pozycja omawia przejście z serwera CI CD TeamCity w wersji 9.1.7 do TeamCity w wersji 2017.2.3 umieszczonego w kontenerze dockerowym pod kontrolą Docker Swarm. Bazą danych, którą wykorzystano w przykładzie jest MySQL.
Pierwszym krokiem, który musimy jest wykonanie kopii zapasowej TeamCity:
- prawy górny róg i "Administration",
- belka po lewej i "Backup",
- z listy rozwijanej "Backup scope" określamy co ma zawierać kopia, a wybranie opcji "Custom" pozwala nam na indywidualne określenie zakresu,
- możemy określić nazwę pliku w sekcji "Backup file"
- pole "add timestamp suffix" doda nam w nazwie pliku kopii zapasowej wzorzec daty i czasu,
- naciskamy "Start Backup".
Kopię można również wykonać za pomocą dedykowanego narzędzia znajdującego się w katalogu z instalacją TC. Dla przykładu w moim wypadku będzie to wywołanie "/home/teamcity/teamcity/bin/maintainDB.sh -C -D -L -P".
Oczekujemy na zakończenie wykonywania się kopii zapasowej (będzie miała rozszerzenie "zip"). W podsumowaniu znajduje się ścieżka do pliku.
W związku z tym, że kopia nie obejmuje artefaktów to musimy je skopiować sami jeżeli chcemy je również mieć w nowej instancji TC.
Udajemy się do katalogu z artefaktami, który jest zależy od tego gdzie jest zainstalowane TC. W moim przypadku jest to "/home/teamcity/.BuildServer/system/artifacts" i wydajemy tam komendę
tar -czvf artifacts.tar.gz .
Przenosimy archiwa z kopią zapasową i artefaktami na serwer docelowy gdzie będzie stało nasze TeamCity w kontenerze.
Teraz przyszła pora na wdrożenie naszego nowego TC na docelowej maszynie. Ja zrobię to przy pomocy Ansible'a uruchamiają plik Docker Compose'a stworzony z szablonu Ansible, który ma następującą postać:
version: '3.5'
services:
mysql:
image: mysql:5.7
ports:
- 3306:3306
networks:
- teamcity
environment:
MYSQL_ROOT_PASSWORD: {{ vault_mysql_root_password }}
MYSQL_DATABASE: "{{ mysql_teamcity_database }}"
MYSQL_USER: "{{ mysql_teamcity_user }}"
MYSQL_PASSWORD: "{{ vault_mysql_teamcity_user_password }}"
volumes:
- mysql_data_directory:/var/lib/mysql
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
teamcity:
image: jetbrains/teamcity-server:2017.2.3
ports:
- 8111:8111
networks:
- teamcity
- traefik
volumes:
- teamcity_data_directory:/data/teamcity_server/datadir
- teamcity_logs_directory:/opt/teamcity/logs
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
labels:
traefik.frontend.entryPoints: "http,https"
traefik.frontend.rule: "Host:${HOST}"
traefik.http.port: 8111
traefik.backend:
volumes:
mysql_data_directory:
teamcity_data_directory:
teamcity_logs_directory:
networks:
teamcity:
driver: overlay
attachable: true
traefik:
external:
name: traefik
A więc na docelowym serwerze mamy świeże i niezainicjalizowane TC w kontenerze dockerowym w Swarmie. Kolejnym krokiem jest uruchomienie dodatkowego kontenera z TeamCity współdzielącego wolumen, w którym dokonamy przywrócenia danych:
bkorpala@hercules:~$ sudo docker run -it --name restore-tc --rm --network=teamcity_teamcity --volumes-from=teamcity_teamcity.1.41rl6y4npfcb7pj7odvs4pfvo jetbrains/teamcity-server /bin/bash
Welcome to TeamCity Server Docker container
* Installation directory: /opt/teamcity
* Logs directory: /opt/teamcity/logs
* Data directory: /data/teamcity_server/datadir
root@73b7919e46dc:/#
Jako argument opcji "--network" podajemy sieć która została utworzona dla TC przez Swarma ("sudo docker network ls"), a dla opcji "--volumes-from" podajemy nazwę kontenera TC ("sudo docker ps").
Jeżeli chcemy wyjść z kontenera bez zabicia go to wciskamy najpierw klawisz Ctrl + P, a potem Ctrl + Q. Aby wejść znów do kontenera wpisujemy "sudo docker exec -it restore-tc /bin/bash".
Z pierwotnej instancji TC na serwer docelowy kopiujemy informację na temat połączenia z bazą danych. W moim przypadku jest to "/home/teamcity/.BuildServer/config/database.properties".
Otwieramy plik i modyfikujemy go wpisując użytkownika, hasło oraz nazwę bazy, które użyjemy w nowej instancji. Mój plik np. wygląda następująco:
# Database: HSQLDB (HyperSonic) version 2.x
connectionUrl=jdbc:mysql://mysql:3306/teamcity?useUnicode=yes&characterEncoding=UTF-8
connectionProperties.user=teamcity
connectionProperties.password=MGjHHVgm7ABSvnbP
Dzięki temu, że jesteśmy w zadeklarowanej przez nas sieci dockerowej to dla serwera MySQL możemy użyć nazwy domenowej, która odpowiada nazwie usługi w Docker Compose.
Do kontenera z dodatkowym TC kopiujemy plik z informacją na temat połączenia z bazą danych:
sudo docker cp database.properties restore-tc:/restore-database.properties
Kopiujemy tam również kopię bazy danych:
sudo docker cp TeamCity_Backup_Before_Update_20180601_200257.zip restore-tc:/backup.zip
Następnie musimy pobrać sterownik dla połączeń z bazą danych MySQL ("https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.11.tar.gz") na serwer docelowy i go rozpakować. W kontenerze z dodatkowym TC tworzymy strukturę katalogów "/data/teamcity_server/datadir/lib/jdbc" i kopiujemy do niego nasz sterownik:
sudo docker cp mysql-connector-java-8.0.11/mysql-connector-java-8.0.11.jar restore-tc:/data/teamcity_server/datadir/lib/jdbc/
W kontenerze z dodatkowym TC wydajemy komendę, która zacznie proces odzyskiwania:
/opt/teamcity/bin/maintainDB.sh restore -A /data/teamcity_server/datadir/ -F /backup.zip -T /restore-database.properties
Po wgraniu kopii musimy jeszcze wgrać artefakty. Dla przykładu w moim wypadku (artefakty są umieszczone w folderach odpowiadających nazwom projektów) znajdują się one w katalogu "artifacts" po wypakowaniu archiwum, które wcześniej utworzyliśmy:
sudo docker cp artifacts/. restore-tc:/data/teamcity_server/datadir/system/artifacts/
Uruchamiamy ponownie kontener z naszym nowym TC. W moim przypadku będzie to restart usługi Docker Swarma:
bkorpala@hercules:~$ sudo docker service scale teamcity_teamcity=0
teamcity_teamcity scaled to 0
overall progress: 0 out of 0 tasks
verify: Service converged
bkorpala@hercules:~$ sudo docker service scale teamcity_teamcity=1
teamcity_teamcity scaled to 1
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
Wchodzimy na stronę z nowym TC i widzimy informację "TeamCity server requires technical maintenance. Please let the server administrator know this." i wtedy klikamy na "I'm a server administrator, show me the details". Musimy podać kod autoryzacyjny, który uzyskamy wchodząc do kontenera i odczytując logi jak np.:
bkorpala@hercules:~$ sudo docker exec -it teamcity_teamcity.1.foie75s0t5bk8grqz6jqm5xjw tail /opt/teamcity/logs/teamcity-server.log
[2018-06-03 04:04:46,377] INFO - jetbrains.buildServer.STARTUP - The database properties file "/data/teamcity_server/datadir/config/database.properties" doesn't exist
[2018-06-03 04:04:46,377] INFO - jetbrains.buildServer.STARTUP - The internal database data file "/data/teamcity_server/datadir/system/buildserver.data" doesn't exist
[2018-06-03 04:04:46,393] INFO - jetbrains.buildServer.STARTUP - Data Directory version: 727
[2018-06-03 04:04:46,393] INFO - jetbrains.buildServer.STARTUP - Current stage: Looking for the database configuration
[2018-06-03 04:04:46,393] INFO - jetbrains.buildServer.STARTUP - Database properties file "/data/teamcity_server/datadir/config/database.properties" doesn't exist
[2018-06-03 04:04:46,393] INFO - jetbrains.buildServer.STARTUP - Internal HSQL database file (/data/teamcity_server/datadir/system/buildserver.data) doesn't exist
[2018-06-03 04:04:46,393] INFO - jetbrains.buildServer.STARTUP - Neither database properties file nor internal database found.
[2018-06-03 04:04:46,393] ERROR - jetbrains.buildServer.STARTUP - Data parts are inconsistent: the Data Directory exists (from another version of TeamCity) but the database does not.
[2018-06-03 04:04:46,394] INFO - jetbrains.buildServer.STARTUP - Current stage: TeamCity server startup error
[2018-06-03 04:04:46,394] INFO - jetbrains.buildServer.STARTUP - Administrator can login from web UI using authentication token: 8288277720538082065
Widzimy, że brakuje nam plik z informacją na temat połączenia do bazy "/data/teamcity_server/datadir/config/database.properties" więc musimy go skopiować do kontenera i jeszcze raz zrestartować usługę w Docker Swarmie.
Kolejnym krokiem jest kliknięcie na "Confirm", a następnie "Upgrade". Teraz oczekujemy aż aktualizacja się zakończy.