In this tutorial, I will guide you through deploying GitLab on your local macOS machine using Docker. GitLab is a powerful DevOps platform that provides source code management, CI/CD, and other essential tools for software development. By setting up GitLab locally, you’ll be able to manage your projects and pipelines in a self-contained environment for development and testing purposes.

Prerequisites

Please note: If you already have Docker Desktop installed on your machine, you can skip this entire prerequisites section.

Docker cli: Ensure that the Docker CLI is installed on your machine.

Colima - Docker Desktop Alternative: Colima is an alternative to Docker Desktop for macOS. Follow this tutorial to learn more about colima and how to install it on macOS.

Starting colima: Start Colima with the following configuration:

1
colima start --cpu 4 --memory 8 --mount-type 9p --vm-type=qemu

Colima configuration options:

  • --cpu 4: Specifies 4 CPUs to allocate to the virtual machine, ensuring sufficient processing power.
  • --memory 8: Allocates 8 GB of RAM to the virtual machine, which is optimal for running GitLab and associated services.
  • --mount-type 9p: Uses the 9p mount type for file sharing between macOS and the virtual machine, providing efficient and seamless access to files.
  • --vm-type=qemu: Uses the QEMU hypervisor for virtualization, which is compatible with Colima and macOS environments. These configuration options are chosen to optimize performance and compatibility for running GitLab on your local macOS machine.

GitLab installation steps

Step 1 - Create docker network

1
docker network create gitlab-network

Docker network is created to facilitate communication between the GitLab container and the GitLab Runner container, enabling seamless integration and execution of CI/CD pipelines.

Step 2 - Create docker volumes

1
2
3
docker volume create gitlab-config
docker volume create gitlab-logs
docker volume create gitlab-data

Docker volumes are chosen to manage data and configuration persistently within the GitLab container, overcoming any permission-related challenges associated with bind volumes on macOS.

Step 3 - Start the GitLab container with the following command:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
docker run  \
  --detach \
  --restart always \
  --name gitlab-ce \
  --publish 22:22 \
  --publish 80:80 \
  --publish 443:443 \
  --publish 5005:5005 \
  --hostname gitlab.example.com \
  --network gitlab-network \
  --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com'" \
  --volume gitlab-config:/etc/gitlab \
  --volume gitlab-logs:/var/log/gitlab \
  --volume gitlab-data:/var/opt/gitlab \
  --shm-size 2gb yrzr/gitlab-ce-arm64v8:latest

Docker Run command configuration options

  • --detach: Runs the container in the background (detached mode), allowing you to continue using the terminal.
  • --restart always: Ensures that the container restarts automatically if it stops or the Docker service restarts.
  • --name gitlab-ce: Assigns the name “gitlab-ce” to the container for easy identification and management.
  • --publish 22:22: Maps port 22 of the container to port 22 on the host. This is typically used for SSH access.
  • --publish 80:80: Maps port 80 of the container to port 80 on the host, which is used for HTTP access to GitLab.
  • --publish 443:443: Maps port 443 of the container to port 443 on the host, which is used for HTTPS access to GitLab.
  • --publish 5005:5005: Maps port 5005 of the container to port 5005 on the host, which is used for container registry in Gitlab.
  • --hostname gitlab.example.com: Sets the container’s hostname to “gitlab.example.com” for network identification.
  • --network gitlab-network: Connects the container to the “gitlab-network” Docker network, enabling communication with other containers in the same network.
  • --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com'": Sets an environment variable to configure GitLab with an external URL.
  • --volume gitlab-config:/etc/gitlab, --volume gitlab-logs:/var/log/gitlab, --volume gitlab-data:/var/opt/gitlab: Mounts Docker volumes (gitlab-config, gitlab-logs, gitlab-data) to persist configuration files, logs, and data within the container.
  • --shm-size 2gb: Sets the shared memory size limit to 2GB for the container.
  • yrzr/gitlab-ce-arm64v8:latest: I chose the yrzr/gitlab-ce-arm64v8:latest image because it is specifically built to support macOS ARM architecture, ensuring compatibility and optimal performance for running GitLab on Mac machines with ARM processors.

Step 4 - Access GitLab:

  • Create an entry in macOS’s /etc/hosts file to access gitlab with custom host name.
1
127.0.0.1 gitlab.example.com
  • Open your web browser and go to http://localhost (or http://gitlab.example.com if you set a custom hostname).
  • You should see the GitLab login page. sign in with the username root and the password from the following command:
1
 docker exec -it gitlab-ce grep 'Password:' /etc/gitlab/initial_root_password

GitLab runner installation steps

Configuring a GitLab Runner involves installing the runner, registering it with your GitLab instance, and setting it up to run your CI/CD jobs. Here’s a step-by-step guide on how to do it:

Step 1 - Create docker volumes for gitlab-runner

1
docker volume create gitlab-runner

Step 2 - Run the GitLab Runner container and register it:

1
docker run --rm -t -i -v gitlab-runner:/etc/gitlab-runner --network gitlab-network gitlab/gitlab-runner:latest register --docker-network-mode gitlab-network

Follow the prompts to register the runner:

  • Enter the GitLab instance URL: e.g. http://gitlab.example.com
  • Enter the registration token: This can be found in your GitLab project under Settings > CI/CD > Runners.
  • Enter a description for the runner: This is just a label to identify your runner.
  • Enter tags for the runner: Tags are used to limit which jobs can be picked by this runner.
  • Enter the executor: Choose docker as the executor.
  • Enter the default Docker image: Choose a Docker image that will be used to run your jobs, e.g., alpine:latest.

Step 3 - Run the GitLab Runner

1
docker run -d --name gitlab-runner --restart always  --network gitlab-network  -v gitlab-runner:/etc/gitlab-runner  -v /var/run/docker.sock:/var/run/docker.sock  gitlab/gitlab-runner:latest

Step 4 - Verify the runner

  1. Go to your GitLab project.
  2. Navigate to Settings > CI/CD > Runners.
  3. Verify that your runner is listed as active.

By following these steps, you will have a GitLab Runner configured and ready to run your CI/CD jobs.

Test a Simple CI/CD Pipeline

Create a .gitlab-ci.yml File:

  • In your project repository, create a file named .gitlab-ci.yml with the following content:
1
2
3
4
5
6
7
8
9
  stages:
    - test
  test_job:
    stage: test
    tags: 
      - gitlab-runner
    script:
      - echo "Running tests..."
      - echo "This is a test job."

Commit and Push:

  • Commit the .gitlab-ci.yml file to your repository and push it to GitLab.
1
2
3
  git add .gitlab-ci.yml
  git commit -m "Add CI pipeline configuration"
  git push origin main

Check Pipeline Status:

  1. In your GitLab project, go to CI/CD > Pipelines.
  2. You should see a new pipeline triggered by the commit.
  3. Click on the pipeline to see the jobs and their status.

Enable GitLab container registry (optional)

Create self-signed certificate

First, you’ll need to generate a private key and a self-signed certificate using OpenSSL.

  1. Open a terminal on your Mac.
  2. Create a directory to store the SSL certificate and key:
1
2
mkdir -p ~/certificates/gitlab-registry
cd ~/certificates/gitlab-registry
  1. Generate a private key:
1
openssl genrsa -out registry.gitlab.example.com.key 2048
  1. Create an OpenSSL configuration file: You will need a configuration file that specifies the SAN. Here’s an example of a simple configuration file (openssl-san.cnf)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[ req ]
default_bits        = 2048
prompt              = no
default_md          = sha256
distinguished_name  = dn
req_extensions      = req_ext
x509_extensions     = v3_ca  # The extentions to add to the self signed cert

[ dn ]
C  = India
ST = Rajasthan
L  = Jodhpur
O  = PiyushKashyap
OU = DevOps
CN = registry.gitlab.example.com

[ req_ext ]
subjectAltName = @alt_names

[ v3_ca ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = registry.gitlab.example.com
  • [req]: Configuration for the certificate request process.
  • default_bits: Specifies the key size (2048 bits).
  • prompt: Disables prompting for DN fields during the certificate generation.
  • default_md: Sets SHA256 as the default message digest algorithm.
  • distinguished_name: Points to the [dn] section for the certificate’s DN fields.
  • req_extensions: Refers to [req_ext] for request-specific extensions.
  • x509_extensions: Refers to [v3_ca] for additional x509 extensions (used for CA certificates).
  • [dn]: Defines the distinguished name fields: C, ST, L, O, OU, CN: Specifies the certificate’s country, state, locality, organization, organizational unit, and common name (here, the domain registry.gitlab.example.com).
  • [req_ext]: Defines extensions for the certificate request.
  • subjectAltName: Points to the [alt_names] section for defining alternative DNS names.
  • [v3_ca]: Specifies x509 CA extensions.
  • subjectAltName: Same as req_ext, referring to [alt_names] for SAN entries.
  • [alt_names]: Lists alternative DNS names for the certificate.
  • DNS.1: Sets the first alternative DNS name to registry.gitlab.example.com.
  1. Generate a self-signed certificate:
1
openssl req -new -x509 -key registry.gitlab.example.com.key -out registry.gitlab.example.com.crt -days 365 -config openssl-san.cnf
  1. Verify the certificate: After creating the certificate, verify if the SAN is included:
1
openssl x509 -in registry.gitlab.example.com.crt -noout -text | grep -A1 "Subject Alternative Name"

This command should show the SANs like this:

1
2
X509v3 Subject Alternative Name:
    DNS:registry.gitlab.example.com

Configure GitLab to use the self-signed certificate

Once you have your certificate and key, you need to configure GitLab to use them for the registry.

  1. Create /etc/gitlab/ssl/ directory inside your GitLab container
1
docker exec -it gitlab-ce mkdir -p /etc/gitlab/ssl
  1. Copy the certificate and key to the GitLab container
1
2
docker cp registry.gitlab.example.com.crt gitlab-ce:/etc/gitlab/ssl/
docker cp registry.gitlab.example.com.key gitlab-ce:/etc/gitlab/ssl/
  1. Edit the GitLab Configuration to use the SSL certificate and key for the registry
1
docker exec -it gitlab-ce vi /etc/gitlab/gitlab.rb

Find below lines in the gitlab.rb file, These lines are commented out indicated by the # at the beginning of each line. To enable the Container Registry, you’ll need to uncomment these lines and modify them as below.

1
2
3
4
5
registry_external_url 'https://registry.gitlab.example.com:5005'
gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "registry.gitlab.example.com"
gitlab_rails['registry_port'] = "5005"
gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
  1. Reconfigure and Restart GitLab: Apply the changes:
1
2
docker exec -it gitlab-ce gitlab-ctl reconfigure
docker exec -it gitlab-ce gitlab-ctl restart

Trust certificate

We will add the certificate to the trusted certificate store within the Colima environment. This certificate needs to be applied to Docker within Colima so that it can pull images from the GitLab private registry without the “unknown authority” error.

1
colima ssh -- sudo sh -c "openssl s_client -showcerts -connect registry.gitlab.example.com:5005 </dev/null 2>/dev/null|openssl x509 -outform PEM > /usr/local/share/ca-certificates/internal.crt && update-ca-certificates && cat /var/run/docker.pid | xargs kill"

This command will download the certificate, install in the certificate storage in the colima virtual machine in one go and restart the docker process.

Test registry

Create an entry in macOS’s /etc/hosts file to access registry with custom host name.

1
127.0.0.1 registry.gitlab.example.com

In GitLab, go to your project and click on Container Registry from the left sidebar under the Deploy section. You should see the Container Registry page with Docker CLI commands similar to what is shown in the image below.

Gitlab Container Registry

Run these docker commands from your local machine to connect, build and push image to container registry.

Troubleshooting

Reset GitLab user password

  1. Access the GitLab Container:
    • First, get into your running GitLab container:
    1
    
    docker exec -it gitlab-ce /bin/bash
    
  2. Reset the Password:
    • Once inside the container, run the following command to reset the user password:
    1
    
    gitlab-rails console -e production
    
    • In the Rails console, execute the following commands to reset the user password:
    1
    2
    3
    4
    
    user = User.find_by(username: 'piyush')
    user.password = 'new_password'
    user.password_confirmation = 'new_password'
    user.save!
    
  3. Exit the Container:
    • Type exit to leave the Rails console and then exit again to leave the container.

Check Container Logs

By reviewing logs for both GitLab and the GitLab Runner containers you can gather crucial error messages and diagnostic information to resolve operational challenges effectively.

1
2
docker logs gitlab-ce
docker logs gitlab-runner

Reconfigure GitLab

If there have been changes to the GitLab configuration, reconfigure and restart the GitLab services.

1
2
docker exec -it gitlab-ce gitlab-ctl reconfigure
docker exec -it gitlab-ce gitlab-ctl restart

Show gitlab-runner config

1
docker exec -it gitlab-runner cat /etc/gitlab-runner/config.toml

Check DNS Resolution within Runner

Ensure that the runner can resolve gitlab hostname gitlab.example.com correctly.

1
docker run --rm -it --network gitlab-network alpine nslookup gitlab.example.com

Feel free to reach out with any questions or feedback. Happy GitLabbing!