Before we can start we need the following things
- A server running linux
- A Gitlab-account
- A domain name to direct to your server
- Basic knowledge of Docker, Dockerfiles, and Docker-compose-files
This DigitalOcean post provides a overview of things you can do to secure your new server. We will create a non-root user, add that user to the sudoers-group and disable ssh for the root user.
The first thing we going to do is to add a new user
adduser lovelaceAdding user 'lovelace' ...
Adding new group 'lovelace' (1002) ...
Adding new user 'lovelace' (1001) with group 'lovelace' ...
Creating home directory '/home/lovelace' ...
Copying files from '/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for lovelace
Enter the new value, or press ENTER for the default
Full Name []: Ada Lovelace
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]usermod -aG sudo lovelaceYou can now ssh into your server with the new user
To disable ssh on root we need to edit /etc/ssh/sshd_config and change PermitRootLogin yes or PermitRootLogin without-password to PermitRootLogin no. Bellow is the code you need to run.
ssh username@ipaddress
sudo nano /etc/ssh/sshd_config
sudo service ssh restartSo now that the boring part is done, we can focus on why we’re actually here
Install docker:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ceCheck if the installation is successful sudo docker ps -a
Install docker-compose:
sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
docker-compose version 1.18.0, build 8dd22a9Manage docker as a non-root user:
It’s a nightmare to run everything that has to do with docker with
sudo
sudo groupadd docker
sudo usermod -aG docker $USERLog out and run
docker ps -a, you’ll notice it now works without sudo
Install loadbalancer:
Connect our docker containers to port 80/443 and automatically get SSL/HTTPS with Let’s Encrypt
mkdir /srv/docker
sudo chown -R lovelace:docker /srv/docker/
mkdir /srv/docker/lb
mkdir /srv/docker/lb/data
touch /srv/docker/lb/docker-compose.yml
nano /srv/docker/lb/docker-compose.ymlPaste the following into
docker-compose.yml
version: '3'
services:
traefik:
image: traefik:1.7.3 #check for the latest version https://github.com/containous/traefik/releases
restart: always
command: --api --docker # Enables the web UI and tells Traefik to listen to docker
ports:
- 80:80 #normal traffic
- 443:443 #ssl traffic
- 8080:8080 # The Web UI (enabled by --api)
networks:
- web
volumes: #host:container
- /var/run/docker.sock:/var/run/docker.sock #connect to the docker instance
- ./data/traefik.toml:/traefik.toml
- ./data/acme.json:/acme.json #let's encrypt settings
container_name: traefik
networks:
web:
external: trueA loadbalancer and your application need to be on the same network, otherwise the traffic can’t be routed to the proper container
docker network create webWe need to add some settings for the loadbalancer, and run the following commands
touch /srv/docker/lb/data/acme.json && chmod 600 /srv/docker/lb/data/acme.json
touch /srv/docker/lb/data/traefik.toml
nano /srv/docker/lb/data/traefik.tomlPaste the following into
traefik.toml, don’t forget to change the[acme]email field
debug = false
logLevel = "ERROR"
defaultEntryPoints = ["https","http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "my-awesome-app.org"
watch = true
exposedByDefault = false
[acme]
email = "your-email-here@my-awesome-app.org"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"Run
docker-compose up -dinside the/srv/docker/lbdirectory, visit your Traefik Admin ui viaipaddress:8080
We’re going to create a special deploy user on our server that takes care of the deployment and has minimal rights on our server
sudo adduser deploysudo usermod -aG docker deployssh-keygen -f /Users/lovelace/Desktop/deploy/id_rsaWe get two files, copy content of
id_rsa.pub
su deploy
Password:
mkdir ~/.ssh
nano ~/.ssh/authorized_keysPaste the content of
id_rsa.pub
The next step is to prepare the
docker-compose.ymlfile for our project
mkdir /srv/docker/project-v1
touch /srv/docker/project-v1/.env
sudo chown deploy:docker /srv/docker/project-v1/.env
nano /srv/docker/project-v1/docker-compose.ymlPaste the following into
docker-compose.yml
version: '3'
services:
project-v1:
restart: always
image: "${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}:${IMAGE_TAG}"
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:v1.my-awesome-app.org"
- "traefik.port=80"
networks:
- web
networks:
web:
external:
name: webYou need to edit the service name
project-v1and replacev1.my-awesome-app.orgwith the webaddress you want to use
We need to place a
.gitlab-ci.ymlfile into our gitlab project
Create
Arecord forv1.my-awesome-app.org
The deployment flow looks as following
- We have a project on gitlab, we can push and pull
- When we push our commits to our project, gitlab starts to look for the
.gitlab-ci.ymlfile inside the repository, and will run it if the branches match (master) - In the
.gitlab-ci.ymlfile create two steps: build and deploy step - In the build step we use the included
Dockerfileto create a docker image, and push that image to the gitlab image registry - In the deploy step, log in to our server via SSH (we created the deploy user for this), and pull our image from the gitlab image registry
- Then we run
docker-compose up -don our createddocker-composefile to run the image we created
image: docker:git
services:
- docker:dind
variables:
IMAGE_TAG: $CI_COMMIT_SHA
stages:
- build
- deploy
build:
stage: build
script:
- docker build --build-arg NODE_ENV=prod -t $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/$CI_COMMIT_REF_NAME:$IMAGE_TAG .
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
- docker push $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/$CI_COMMIT_REF_NAME:$IMAGE_TAG
only:
- master
deploy:
stage: deploy
before_script:
- mkdir -p ~/.ssh
- echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/id_rsa
- ssh-keyscan -H $DEPLOYMENT_SERVER >> ~/.ssh/known_hosts
script:
- echo -e "IMAGE_TAG=${IMAGE_TAG}\n CI_REGISTRY=${CI_REGISTRY}\n CI_PROJECT_NAMESPACE=${CI_PROJECT_NAMESPACE}\n CI_PROJECT_NAME=${CI_PROJECT_NAME}\n CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME}" > .env
- scp ./.env $DEPLOYMENT_USER@$DEPLOYMENT_SERVER:$$DEPLOYMENT_LOCATION/.env
- ssh $DEPLOYMENT_USER@$DEPLOYMENT_SERVER "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"
- ssh $DEPLOYMENT_USER@$DEPLOYMENT_SERVER "cd $$DEPLOYMENT_LOCATION && docker-compose stop"
- ssh $DEPLOYMENT_USER@$DEPLOYMENT_SERVER "cd $$DEPLOYMENT_LOCATION && docker-compose up -d"
only:
- masterA
.envfile is generated on our server and some variables are pushed into the file. Those variables are read by ourdocker-composecommand whendocker-compose up -dis ran
FROM exiasr/alpine-yarn-nginx:8.9.4
WORKDIR /usr/share/nginx/www
ADD ./ /usr/share/nginx/www
RUN yarn install
RUN yarn global add gulp
RUN gulp sass
RUN mv nginx/default.conf /etc/nginx/conf.d
EXPOSE 80You can’t just copy paste this
Dockerfilefor your project, but I wanted to share how simple aDockerfileis You don’t have to expose port 443 for HTTPS traffic, theloadbalancertakes care of SSL termination, and routes the traffic to port 80 of the container
In your gitlab project go to
Settings > CI / CDand expand theVariablestab, and add
DEPLOYMENT_SERVERYour server
ipaddress
PRIVATEKEYThe contents of
idrsafile we created in the previous step
DEPLOYMENT_USERThe name of our server user we want to deploy, so in our case
deploy
DEPLOYMENT_LOCATIONThe directory in which we created our
docker-compose.ymlfile in the previous step. So in our case/srv/docker/project-v1
All we need to do, is push our files in the
masterbranch to gitlab If every is setup correctly, gitlab will start processing yourgitlab-ci.ymlfile