Installing GitLab CE into Containers.

Brian KingBrian King
9 min read

TL;DR.

This post provides a step-by-step guide to installing GitLab CE using Docker within an LXC (LinuX Container). I start by explaining the basics of LXD, LXC, and Docker, then move on to the installation processes for each utility. This guide also explores how I set up LXC and Docker, how I download GitLab CE from Docker Hub, and how I resolve common issues. The end result is GitLab CE running in a Docker container within an LXC, offering a more effective, and streamlined, development workflow.

Attributions:

https://ubuntu.com/tutorials/how-to-run-docker-inside-lxd-containers↗,

https://docs.gitlab.com/ee/install/docker.html↗,

https://docs.docker.com/compose/↗.

An Introduction.

Containers are important to a modular development and deployment workflows.

The purpose of this post is to show two container technologies working together.

The Big Picture.

The LXD and LXCs (LinuX Containers) use one type of container technology. Docker uses another type of container technology. Although LXCs and Docker both use containers, they each solve different problems and address their own, specific use cases. LXCs run processes that do not affect other containers, or the host system, and are easily run. Docker encloses, and runs, apps in containers that are easily shared.

This post uses the GitLab CE installation as a way to demonstrate LXC and Docker technologies working together. (Yes, I know this post is a little over the top 😁.)

Prerequisites.

  • A Debian-based Linux distro (I use Ubuntu).

Updating my Base System.

  • In a terminal, I update my base system:
sudo apt clean && \
sudo apt update && \
sudo apt dist-upgrade -y && \
sudo apt --fix-broken install && \
sudo apt autoclean && \
sudo apt autoremove -y

What is LXD and LXC?

The LXD (LinuX Daemon) is the container manager that is used to create, and manage, LXCs (LinuX Containers). It is a background service that can automatically start LXCs when the host system boots, or stop any container from starting at all.

An LXC (LinuX Container) is an isolated, OS-level virtualization which, for efficiency, uses the Linux kernel of the host system. An LXC is a virtual environment where system processes within the LXC container can not affect other containers, or the host system, without specifically running certain commands.

https://ubuntu.com/server/docs/containers-lxd↗,

https://ubuntu.com/server/docs/containers-lxc↗, and

https://solodev.app/installing-lxd-and-using-lxcs.

Setting Up an LXC.

  • I list the existing containers:
lxc ls

NOTE: I need to install LXD if this command fails.

  • I launch a new container called GitLab:
lxc launch ubuntu:22.04 GitLab
  • I bash into the container:
lxc exec GitLab -- bash
  • I update and upgrade the container:
sudo apt clean && \
sudo apt update && \
sudo apt dist-upgrade -y && \
sudo apt --fix-broken install && \
sudo apt autoclean && \
sudo apt autoremove -y

Adding a User Account to the LXC.

  • From within the container, I add a new user:
adduser yt
  • I add the new user to the sudo group:
usermod -aG sudo yt

NOTE: usermod let's me (-a)ppend the sudo (-G)roup to the yt account.

  • I exit the container:
exit

NOTE: I exit the root account to use the yt account.

Setting the LXC Home Directory.

  • From the terminal, I log in to the container with the yt account:
lxc exec GitLab -- su yt

NOTE: At the moment, the home directory is /root. This section will address the issue by changing the home directory to ~.

  • I use the Nano text editor to open the .bashrc file:
sudo nano ~/.bashrc
  • I copy the following, add it (CTRL + SHIFT + V) to the bottom of the .bashrc file, save (CTRL +S) the changes, and exit (CTRL + X) Nano:
cd ~

Hardening the Container.

  • From within the container, I use the Nano text editor to open the sshd_config file:
sudo nano /etc/ssh/sshd_config
  • I copy the following, add it (CTRL + SHIFT + V) to the bottom of the sshd_config file, save (CTRL +S) the changes, and exit (CTRL + X) Nano:
PasswordAuthentication no
PermitRootLogin no
Protocol 2
Port 22

NOTE: Port 22 is the default, so I'll choose my own, random, available port number.

  • I restart the "ssh" service:
sudo systemctl restart ssh.service
  • I reboot the container:
sudo reboot

NOTE: Within the container, I can also install (or enable) UFW, Fail2Ban, and CrowdSec.

What is Docker?

Docker is a tool for easily deploying, and running my applications on any platform. I can package an application with all its code, libraries, dependencies, and tools, which allows me to deploy that app as a single bundle. Docker guarantees that my application will run on any computer that also runs Docker.

https://www.docker.com/↗.

Updating the Container for Docker.

  • From the terminal, I create a btrfs storage pool called docker:
lxc storage create docker btrfs
  • In the docker pool, I create a new storage volume called GitLabVol:
lxc storage volume create docker GitLabVol
  • I attach the GitLab container to the GitLabVol volume:
lxc config device add GitLab docker disk pool=docker source=GitLabVol path=/var/lib/docker
  • I change the nested containers setting to true:
lxc config set GitLab security.nesting=true
  • I change the intercept system calls setting to true:
lxc config set GitLab security.syscalls.intercept.mknod=true
  • I change the emulate system calls setting to true:
lxc config set GitLab security.syscalls.intercept.setxattr=true
  • I restart the container:
lxc restart GitLab

Installing Docker.

  • From the terminal, I log in to the container with the yt account:
lxc exec GitLab -- su yt

NOTE: The home directory (~) for my account was set in the .bashrc file earlier in this post.

  • I install the following requirements:
sudo apt install -y ca-certificates curl gnupg lsb-release
  • I make a keyrings directory:
mkdir -m 0755 -p /etc/apt/keyrings
  • I add Docker’s official GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  • I change the mode of the docker.gpg file:
sudo chmod a+r /etc/apt/keyrings/docker.gpg
  • I install the Docker repository:
echo "deb [arch="$(dpkg --print-architecture)" \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  • I update the homelab repo list:
sudo apt update
  • I install Docker and its requirements:
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Checking the Docker Installation.

  • I check if Docker is active:
systemctl is-active docker
  • I check the status of Docker:
service docker status
  • I check the Docker version:
sudo docker version
  • I list the information for Docker:
sudo docker info
  • I create the Docker group, if required:
sudo groupadd docker

Setting the Docker Privileges.

  • I create the docker group, if required:
sudo groupadd docker
  • I add my account to the docker group:
sudo usermod -aG docker $USER
  • I log in to the new docker group (or reboot the container):
newgrp docker
  • I verify the Docker installation by running this command without a sudo prefix:
docker run hello-world

NOTE: This command displays 'Hello from Docker!' if Docker is properly installed.

What is GitLab CE?

GitLab CE (Community Edition) is an open source, end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more.
I can self-host GitLab CE on my own servers, in a container, or on a cloud provider.

Setting the GITLAB_HOME Env.

  • I make the GitLab directory using the -parents flag:
sudo mkdir -p /srv/gitlab
  • I define the GITLAB_HOME environment variable:
export GITLAB_HOME=/srv/gitlab
  • I reboot the container:
sudo reboot

Downloading GitLab with Docker Compose.

  • From the terminal, I log in to the container with the yt account:
lxc exec GitLab -- su yt
  • I use Nano to create a Docker Compose file called docker-compose.yml:
sudo nano ./docker-compose.yml
  • I copy the following, add it (CTRL + SHIFT + V) to the file, save (CTRL + S) the changes, and exit (CTRL + X) Nano:
version: '3.6'
services:
  gitlab:
    image: gitlab/gitlab-ce:16.7.5-ce.0
    container_name: gitlab
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url 'https://gitlab.example.com'
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80'
      - '443:443'
      - '22:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

NOTE: Compose V2 ignores the version: '3.6' entry on the first line, which is included for Compose V1 compatibility. Since 2023, V1 is no longer updated.

  • I run Docker Compose:
docker compose up -d

NOTE 1: Docker Compose is -detached from the terminal and runs as a daemon. (A daemon is a service that runs in the background.)

NOTE 2: It will take up to ~5 minutes to start the GitLab service.

  • I can stop the Docker container (if required) with the down command:
docker compose down
  • I check the logs in real time:
docker compose logs -f -t
  • I stop the logs with CTRL + C.

Setting Up the Root Password.

  • I setup the root password:
docker exec -it gitlab gitlab-rake "gitlab:password:reset[root]"

NOTE: Be patient. It can take up to a minute for this command to respond.

  • I exit the container:
exit

Testing GitLab CE.

  • From the terminal, I find the IP address for the host interface called eth0:
lxc ls

NOTE: eth0 was set when the LXD was initialised earlier in this post.

  • I add the IP address to a browser.

  • I login to the root account.

NOTE: It is NEVER a good idea to use the root account of ANY program for running day-to-day operations. This includes local distros and self-hosted services. I ALWAYS create a user account for handling my daily activities.

The Results.

In this post, I walked through the process of installing GitLab CE using Docker in an LXC. I began by discussing the basics of LXD, LXC, and Docker, then moved on to the installation processes for each. I also explored how to set up an LXC and Docker, and how to download GitLab CE from Docker Hub. By following the steps outlined in this guide, I was able to run GitLab CE in a Docker container within an LXC, thereby leveraging the benefits of both containerization technologies for my coding workflow.

In Conclusion.

LXD (LinuX Daemon) is a container manager, while LXCs (LinuX Containers) are isolated, OS-level virtualizations. Docker is a tool that helps with bundling applications into container packages that can run anywhere. GitLab CE is an open-source, end-to-end software development platform and repository. I can self-host everything, or I can push it all to a cloud provider.

So, how did I bring them all together? First, I installed LXD, and then I created an LXC. Next, I updated the LXC to handle Docker and finally, I pulled the latest GitLab CE image from Docker Hub. Voila! I now have GitLab CE running in a Docker container within an LXC.

By leveraging these technologies, my development workflows have become more efficient. And remember: I always keep my systems updated and upgraded for optimal security and performance.

Do you have any thoughts on combining these technologies for your development workflows? Have you tried it before, or are you considering it now? Let's discuss in the comments below!

Until next time: Be safe, be kind, be awesome.

#GitLabCE #Docker #LXC #LXD #DockerInLXC #LinuxContainers #Containerization #DevOps #SoftwareDevelopment #TechGuide #DevelopmentWorkflow

0
Subscribe to my newsletter

Read articles from Brian King directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Brian King
Brian King

Thank you for reading this post. My name is Brian and I'm a developer from New Zealand. I've been interested in computers since the early 1990s. My first language was QBASIC. (Things have changed since the days of MS-DOS.) I am the managing director of a one-man startup called Digital Core (NZ) Limited. I have accepted the "12 Startups in 12 Months" challenge so that DigitalCore will have income-generating products by April 2024. This blog will follow the "12 Startups" project during its design, development, and deployment, cover the Agile principles and the DevOps philosophy that is used by the "12 Startups" project, and delve into the world of AI, machine learning, deep learning, prompt engineering, and large language models. I hope you enjoyed this post and, if you did, I encourage you to explore some others I've written. And remember: The best technologies bring people together.