agross / docker-teamcity

JetBrains TeamCity Server as a docker container

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

docker-teamcity

This Dockerfile allows you to build images to deploy your own TeamCity instance. It has been tested on Fedora 23 and CentOS 7.

Please remember to back up your data directories often, especially before upgrading to a newer version.

Test it

  1. Install docker.
  2. Run the container. (Stop with CTRL-C.)
docker run -it --publish 8111:8111 agross/teamcity
  1. Open your browser and navigate to http://localhost:8111.

Run it as service on systemd

  1. Decide where to put TeamCity data and logs. Set domain name/server name and the public port.
TEAMCITY_DATA="/var/data/teamcity"
TEAMCITY_LOGS="/var/log/teamcity"

DOMAIN=example.com
PORT=8011
  1. Create directories to store data and logs outside of the container.
mkdir --parents "$TEAMCITY_DATA" \
                "$TEAMCITY_LOGS"
  1. Set permissions.

The Dockerfile creates a teamcity user and group. This user has a UID and GID of 3000. Make sure to add a user to your host system with this UID and GID and allow this user to read and write to $TEAMCITY_DATA and $TEAMCITY_LOGS. The name of the host user and group in not important.

# Create teamcity group and user in docker host, e.g.:
groupadd --gid 3000 \
        --system teamcity
useradd --uid 3000 \
        --gid 3000 \
        --system \
        --shell /sbin/nologin \
        --comment "JetBrains TeamCity" \
        teamcity

# 3000 is the ID of the teamcity user and group created by the Dockerfile.
chown -R 3000:3000 "$TEAMCITY_DATA" "$TEAMCITY_LOGS"
  1. Create your container.

Note: The :z option on the volume mounts makes sure the SELinux context of the directories are set appropriately.

Use --env to specify JVM and TeamCity server options, e.g. for memory.

/etc/localtime needs to be bind-mounted to use the same time zone as your docker host.

docker create -it --env TEAMCITY_SERVER_MEM_OPTS='-Xms1g -Xmx3g' \
                  --publish $PORT:8111 \
                  --volume /etc/localtime:/etc/localtime:ro \
                  --volume "$TEAMCITY_DATA:/teamcity/.BuildServer:z" \
                  --volume "$TEAMCITY_LOGS:/teamcity/logs:z" \
                  --name teamcity \
                  agross/teamcity
  1. Create systemd unit, e.g. /etc/systemd/system/teamcity.service.
cat <<EOF > "/etc/systemd/system/teamcity.service"
[Unit]
Description=JetBrains TeamCity Server
Requires=docker.service
After=docker.service

[Service]
Restart=always
# When docker stop is executed, the docker-entrypoint.sh trap + wait combination
# will generate an exit status of 143 = 128 + 15 (SIGTERM).
# More information: http://veithen.github.io/2014/11/16/sigterm-propagation.html
SuccessExitStatus=143
PrivateTmp=true
ExecStart=/usr/bin/docker start --attach=true teamcity
ExecStop=/usr/bin/docker stop --time=60 teamcity

[Install]
WantedBy=multi-user.target
EOF

systemctl enable teamcity.service
systemctl start teamcity.service
  1. Setup logrotate, e.g. /etc/logrotate.d/teamcity.
cat <<EOF > "/etc/logrotate.d/teamcity"
$TEAMCITY_LOGS/*.log
{
  rotate 7
  daily
  dateext
  missingok
  notifempty
  sharedscripts
  copytruncate
  compress
}
EOF
  1. Add nginx configuration, e.g. /etc/nginx/conf.d/teamcity.conf.
cat <<EOF > "/etc/nginx/conf.d/teamcity.conf"
upstream teamcity {
  server localhost:$PORT;
}

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      '';
}

server {
  listen           80;
  listen      [::]:80;

  server_name $DOMAIN;

  access_log  /var/log/nginx/$DOMAIN.access.log;
  error_log   /var/log/nginx/$DOMAIN.error.log;

  # Do not limit upload.
  client_max_body_size 0;

  proxy_read_timeout     1200;
  proxy_connect_timeout  240;

  # Required to avoid HTTP 411: see issue #1486 (https://gitteamcity.com/dotcloud/docker/issues/1486)
  chunked_transfer_encoding on;

  location / {
    proxy_pass http://teamcity;

    proxy_set_header Host \$host;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-Host \$http_host;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto \$scheme;
    proxy_http_version 1.1;

    # Support WebSockets.
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }
}
EOF

nginx -s reload

Make sure SELinux policy allows nginx to access port $PORT (the first part of --publish $PORT:8080 of step 3).

EXPR="^http_port_t\s+tcp\s.*\b$PORT\b"
if ! semanage port --list | grep --quiet --perl-regexp "$EXPR"; then
  if semanage port --add --type http_port_t --proto tcp $PORT; then
    echo Added port $PORT as a valid port for nginx:
    semanage port --list | grep --perl-regexp "$EXPR"
  else
    >&2 echo Could not add port $PORT as a valid port for nginx. \
             Please add it yourself. More information: \
             http://axilleas.me/en/blog/2013/selinux-policy-for-nginx-and-gitlab-unix-socket-in-fedora-19/
  fi
else
  echo Port $PORT is already a valid port for nginx:
  semanage port --list | grep --perl-regexp "$EXPR"
fi
  1. Configure TeamCity.

Follow the steps of the installation instructions for JetBrains TeamCity using paths inside the docker container located under /teamcity/.BuildServer.

  1. Update to a newer version.
docker pull agross/teamcity

systemctl stop teamcity.service

# Back up $TEAMCITY_DATA.
tar -zcvf "teamcity-data-$(date +%F-%H-%M-%S).tar.gz" "$TEAMCITY_DATA"

docker rm teamcity

# Repeat step 4 and create a new image.
docker create ...

systemctl start teamcity.service
  1. Trust self-signed SSL certificates.

If you need to connect to e.g. an LDAP server that uses a self-signed certificate or use the NuGet trigger with a NuGet feed served with a self-signed certificate you need to add those certificates to the JVM trust store inside the container.

With the docker container running, execute:

HOST=ldap.example.com:636

docker exec -it -u root teamcity_teamcity_1 bash -c "
  set -o pipefail
  echo -n |
    openssl s_client -connect "$HOST" |
    sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > "/tmp/$HOST" &&
    "\$JAVA_HOME/bin/keytool" -import \
                              -alias "$HOST" \
                              -file "/tmp/$HOST" \
                              -keystore "\$JAVA_HOME/jre/lib/security/cacerts" \
                              -noprompt \
                              -storepass changeit &&
    rm /tmp/$HOST"

Building and testing the Dockerfile

  1. Build the Dockerfile.
docker build --tag agross/teamcity:testing .

docker images
# Should contain:
# REPOSITORY                        TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
# agross/teamcity                   testing             0dcb8bf6093f        49 seconds ago      405.4 MB
  1. Prepare directories for testing.
TEST_DIR="/tmp/teamcity-testing"

mkdir --parents "$TEST_DIR/data" \
                "$TEST_DIR/logs"
chown -R 3000:3000 "$TEST_DIR"
  1. Run the container built in step 1.

Note: The :z option on the volume mounts makes sure the SELinux context of the directories are set appropriately.

docker run -it --rm \
               --name teamcity-testing \
               --publish 8111:8111 \
               --volume "$TEST_DIR/data:/teamcity/.BuildServer:z" \
               --volume "$TEST_DIR/logs:/teamcity/logs:z" \
               agross/teamcity:testing
  1. Open a shell to your running container.
docker exec -it teamcity-testing bash
  1. Run bash instead of starting TeamCity.

Note: The :z option on the volume mounts makes sure the SELinux context of the directories are set appropriately.

docker run -it --rm \
               --name teamcity-testing \
               --publish 8111:8111 \
               --volume "$TEST_DIR/data:/teamcity/.BuildServer:z" \
               --volume "$TEST_DIR/logs:/teamcity/logs:z" \
               agross/teamcity:testing \
               bash

About

JetBrains TeamCity Server as a docker container


Languages

Language:Shell 51.3%Language:Dockerfile 48.7%