Software development infrastructure. Part 0x02. Setting things up. Gitlab
Time to set up some containers. This is also where we make use of docker-compose.
What Will I Learn?
This is the next part of series of tutorials aimed at setting up a development infrastructure. Today we cover:
- setting up of Gitlab in Docker
- using Docker
- using Docker Compose
- automating the setup with bash scripts and a Makefile
Requirements
- You need a running instance of Docker available
- You need an installed Docker Compose
Both of the above have been done in Part 0x01, be sure to check that out (no one submitted a homework, so the reward is still available, just saying).
Difficulty
It's all trivial.
Jokes aside, I'd say Intermediate. Some things will be super simple, some rather challenging. You are not expected to understand everything as I am not a perfect teacher. If you don't understand, don't feed your imposter syndrome, just ask. I will do my best to explain.
Tutorial Contents
Running docker compose
Just remember, there may be some differences in naming (for instance if you install docker through snap, it will be docker.compose
, not docker-compose
).
To start a set of containers with docker-compose, you will need to run
docker-compose up
This way you will see the logs and Ctrl+C will stop it (second Ctrl+C will kill it).
To clean up after that, run
docker-compose down
It will cleanup the network and stuff, but will leave volume folders intact. I need to sort this out.
If you start docker with
docker-compose up -d
it will be in detached state and you will be able to use the same terminal or close the window. You will not see the logs, however. If you want to see them now, you need to run
docker-compose logs
or add -f
to keep them flowing.
All of the above commands assume that your docker-compose.yml
file is in the current directory. If not, you need to add -f parameter followed by the location of you docker-compose.yml
. For following the logs it makes the command line look silly:
docker-compose -f /path/t/docker-compose.yml logs -f
...but it works. Docker Compose takes the parameters located between the name and command, the command takes parameters located after it.
Apart from up
and down
, there are also start
and stop
. The first two set up and tear down the container composition, the second two just start and stop the containers. They are useful when you’re not rebuilding the containers composition over and over again.
At all times, a very useful command it docker-compose help
which lists the commands, or details of a single command if you type it after help
.
Ssh keys
I’m assuming below that you have your ssh key pair generated. It will be used for authenticating in git commands. If you don’t have it, execute
ssh-keygen -t rsa -b 4096
and follow the instructions.
Your key pair can be used to authenticate without a password when using ssh. I do recommend having such a setup. You will have two keys generated, a public and a private one. The public one will be added to the Gitlab to authenticate when using repositories.
Gitlab
Some of you may know Gitlab as employers to world class database removal specialists ( and they give support to others in such difficult situation ). We were really impressed by how they dealt with their problems then.
We host a community edition of Gitlab at work, so I would like to have fun with it. They provide some documentation for their docker images.
I started with a simple docker-compose.yml that was a spot on in terms of not working:
version: '2'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab'
# Add any other gitlab.rb configuration here, each on its own line
ports:
- '80'
volumes:
- './volumes/gitlab/config:/etc/gitlab'
- './volumes/gitlab/logs:/var/log/gitlab'
- './volumes/gitlab/data:/var/opt/gitlab'
networks:
le_net:
ipv4_address: 10.5.0.10
networks:
le_net:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
To get the volumes to work, I created a following tree structure:
$ tree -d -L 2 volumes/
volumes/
└── gitlab
├── config
├── data
└── logs
We will need to automate this somehow below. Note that this part of config is not needed and I could have left volumes as not accessible from my file system, but I prefer it this way. I can get to the files and browse them without getting into the container
You can see I have set up a network 10.5.0.0/16. Having a static network is quite handy, I went to /etc/hosts and added:
10.5.0.10 gitlab
Frankly speaking I wasn’t expecting it all to work on first try, and so it didn't. I removed the GITLAB_OMNIBUS_CONFIG that came from the sample, opened the rest of ports and there we are, http://gitlab shows:
This is the docker-compose.yml that worked:
version: '2'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab'
ports:
- '80'
- '443'
- '22'
volumes:
- './volumes/gitlab/config:/etc/gitlab'
- './volumes/gitlab/logs:/var/log/gitlab'
- './volumes/gitlab/data:/var/opt/gitlab'
networks:
le_net:
ipv4_address: 10.5.0.10
networks:
le_net:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
Just note that it takes a long time to boot up at first, as it needs to create all the data and stuff. Also, you can note that files created are owned by root. If you want to access them, you’ll need to sudo.
I chose some password (the IEEE Standard Secure Password is 12345678, if you want).
I added some
group and a project project
in it. I used nice big shiny buttons on the front page.
I have also created a user user
. I went to settings and just clicked it in. I could not set up a password, so after creating the user, I went into edit and then set up a password.
I could have an LDAP config to simplify this stuff, but don’t know how to do it yet. I could see some docker images for openldap. I guess I’ll sort this out some other time.
I think that there should be a way to preconfigure all of the above. I would like you to be able to clone my repo and run some command to get to a state I landed in. We’ll think about it later.
Anyway, I added user
to group some
in group settings, I logged in as user
(had to change password, but it accepted the same one), added my ssh key and ran the following on my computer:
$ git clone git@gitlab:some/project.git
Cloning into 'project'...
The authenticity of host 'gitlab (10.5.0.10)' can't be established.
ECDSA key fingerprint is SHA256:Ralo1gYTTGl8xzEnmYC0AO2IWS5+7ROb04lg6H+rYYY.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitlab,10.5.0.10' (ECDSA) to the list of known hosts.
warning: You appear to have cloned an empty repository.
And there we are.
Automating the steps
The first idea that comes to my mind is to use a Makefile that would set things up.
What I need (would like to) automate so far:
- create folders before start
- set up /etc/hosts (I prefer to just provide the values, you can add them by yourself)
- set up admin account
- set up group
- set up project
- set up user account
- add some cleanup after finishing
I started with the admin. It turns out there is a GITLAB_ROOT_PASSWORD
env var that can automate the setup. Let’s try.
version: '2'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab'
ports:
- '80'
- '443'
- '22'
volumes:
- './volumes/gitlab/config:/etc/gitlab'
- './volumes/gitlab/logs:/var/log/gitlab'
- './volumes/gitlab/data:/var/opt/gitlab'
environment:
- GITLAB_ROOT_PASSWORD=12345678
networks:
le_net:
ipv4_address: 10.5.0.10
networks:
le_net:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
Ran this and landed on the login page. Username root
and password 12345678
works. Brilliant. Now the rest of the setup. Let’s use the api:
curl -XPOST --data "grant_type=password&username=root&password=12345678" http://gitlab/oauth/token
...returns:
{"access_token":"96925c73c77536a5370eda14d9a098e1566c3556e3702f33434434d88a721d3a","token_type":"bearer","refresh_token":"dac13077506ac16864ceae2329dfeea3ffa0ac9e60f3b45c745d179607f9d00d","scope":"api","created_at":1515684086}
Let’s do something to extract the value.
TOKEN=$(curl -XPOST --data "grant_type=password&username=root&password=12345678" http://gitlab/oauth/token | python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])")
I’ve already assigned the value to TOKEN to use it in future requests, like this one for example:
$ curl --header "Authorization: Bearer $TOKEN" http://gitlab/api/v4/user
{"id":1,"name":"Administrator","username":"root","state":"active","avatar_url":"http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon","web_url":"http://gitlab/root","created_at":"2018-01-11T14:12:10.267Z","bio":null,"location":null,"skype":"","linkedin":"","twitter":"","website_url":"","organization":null,"last_sign_in_at":null,"confirmed_at":"2018-01-11T14:12:10.185Z","last_activity_on":null,"email":"[email protected]","theme_id":1,"color_scheme_id":1,"projects_limit":100000,"current_sign_in_at":null,"identities":[],"can_create_group":true,"can_create_project":true,"two_factor_enabled":false,"external":false,"is_admin":true}
I won’t get into the whole API here as it’s quite big, but a couple things we need to run would look like this (available in the repo in scripts/gitlab/after-start):
echo fetch token
TOKEN=$(curl -XPOST --data "grant_type=password&username=root&password=12345678" http://gitlab/oauth/token | python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])")
echo create some group
curl -X POST --header "Authorization: Bearer $TOKEN" --data "name=some&path=some&visibility=internal" http://gitlab/api/v4/groups
echo create user
curl -X POST --header "Authorization: Bearer $TOKEN" --data "username=user&name=user&password=12345678&email=user@email&skip_confirmation=true" http://gitlab/api/v4/users
echo add user to some group
# shortcut:, user is a second user, so user_id is 2. I could have extracted like in token.
curl -X POST --header "Authorization: Bearer $TOKEN" --data "user_id=2&access_level=40" http://gitlab/api/v4/groups/some/members
echo add ssh key to user
# shortcut: user is a second user, so user_id is 2. I could have extracted like in token.
# shortcut: I’m assuming you have a key pair in ~/.ssh, only one pair, and public key file ends with .pub
# note that I’m passing the data differently; the key needs to be urlencoded so that pluses aren’t read as space characters
curl \
-X POST \
--header "Authorization: Bearer $TOKEN" \
--data-urlencode "title=key" \
--data-urlencode "key=$(cat ~/.ssh/*.pub)" \
http://gitlab/api/v4/users/2/keys
echo create project
# shortcut: group_id is 2, I don’t know what namespace_id 1 is and I don’t really care
curl -X POST --header "Authorization: Bearer $TOKEN" --data "name=project&merge_requests_enabled=yes&namespace_id=2" http://gitlab/api/v4/projects
Now this script works. Let’s add some simplifications for regularly used commands. I’ve added a Makefile:
clean: stop
./scripts/gitlab/reset
./scripts/clean
before-start:
./scripts/gitlab/before-start
after-start:
./scripts/gitlab/after-start
start: before-start
docker-compose up -d
stop:
docker-compose down
clean
removesvolumes
and cleans up the self-generated certificates fromknown_hosts
, after making sure the container is downbefore-start
createsvolumes
after-start
creates groups and repos in Gitlab (provided above); it will succeed once Gitlab is upstart
starts the containers (if you’re using snaps, change command to docker.compose)stop
stops the containers (if you’re using snaps, change command to docker.compose)
To start and initialize everything, simply run:
make start
# now wait for it to be up, check http://gitlab/
make after-start
Of course, you can simply follow the whole tutorial.
Summary
Gitlab is up and running, next we’ll set up Jenkins.
This part was a real pain in the lower back. I got everything working on first try, then I could not make my certificate work when automating. After a lot of tries I ended up regenerating my key and then it started working. After a retry, it stopped working again. It turned out whatever cleanup I was making, I wasn’t making folders clean enough (probably hidden folders remained), so I changed approach, removed reset and only left an updated clean script which worked as expected.
This is how it rolls in software engineering – a dumb mistake escalates and you keep looking in all the wrong places for far too long. If you’re lucky enough, it’s at least not your error.
Remember: THE DUMBEST QUESTIONS ARE THOSE NEVER ASKED
The final code version for this part is under tag 0x02.
Homework
Improve make’s goal after-start not to fail after start. Make it wait for Gitlab to be up. Again, after a week, I will select one submitter and reward him with 1SBD. If no one comes, I will wait for the first one to submit a working homework. Let's say I'll wait for a month.
Curriculum
Posts so far:
- Introductory post
- Part 0x01 Docker
- Part 0x02 Gitlab (this post)
I have set up a git repository to host whatever files I may need in this tutorial. The readme also contains links to articles.
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Hey @jestemkioskiem, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!
Thank you!
Your contribution cannot be approved because it does not follow the Utopian Rules.
You can contact us on Discord.
[utopian-moderator]
Hey @creon, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!
Hi @creon,
Thank you for your time to review this post. Im not sure what you mean by "wrong repository" - breadcentric/dev-infrastructure contains the code for this tutorial precisely.
With regards to the clarity, I will add am introduction to clearly state the goal of the whole series, as described in two previous parts of it. I will do it in an hour or so.
Do I need to ping you precisely when I'm done or can it be any moderator?
Thanks for sharing the knowledge :) tip! 0.3
Hi @breadcentric! You have received 0.3 SBD tip + 0.03 SBD @tipU from @cardboard :)
@cardboard wrote lately about: Just Testing. Feel free to follow @cardboard if you like it :)
@breadcentric, Contribution to open source project, I like you and upvote.
Thank you @steemitstats.
Hey @breadcentric I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x
Cool!
Didn't realise you were into dockers as well. I will have to post something that might be helpful for people in that regard as well.
Just a bit, we're not using it in production. I treat it as a handy tool to start something in a clean way and then tidy up.