Self Hosted Java Cloud

Posted by

When starting a new project from scratch it is convenient to have an environment where future project will be deployed at. I don’t pretend on giving the standard solution for desired purposes, but approaches described in this article seemed very useful to me.

Actually, this article is about how to setup all the tools helping for developing modern web applications.

Organizing Self hosted Java Cloud

Building the solution described in this article I was aimed to achieve the requirements:

  1. Each application used must run in separated Docker container for security reasons and for having straightforward deployment flow
  2. There must be a dashboard in order to observe the applications developed and (optionally) manage them
  3. There must be one point to observe all application logs

Note that approaches described below will be helpful not only for Java based solutions.

The tooling stack I selected for achieving the goals is:

  1. Gitea as a git server with GUI
  2. Jenkins as a CI/CD manager / controller
  3. Docker as a container manager
  4. Docker-compose as a container building tool
  5. Spring, Spring Cloud: Eureka
  6. SpringBoot Admin Panel

In a justification of tooling stack selected in this article I can say following:

  1. Gitea is a lightweight, simple solution providing a developer with all options needed to manage the development in small or medium teams
  2. Jenkins has very low level of entrance and has powerful script-based engine to perform CI/CD steps
  3. Docker is a standard of contenerization industry
  4. Docker-compose helps to simplify building containers
  5. No Kubernetes because of it requires the team to have the DevOps position or to spent much time on the very beginning of the development
  6. Spring is a powerful web engine written in Java
  7. Spring Cloud Eureka is a Service Registry where all applications to be registered in order to give the possibility to the dashboard to observe them
  8. SpringBoot Admin Panel is a lightweight dashboard panel. It can show metrics, logs and managing applications using JMX

Docker

Nowadays, if you want to get a high appreciated job position you need to be familiar with Docker.

Docker brings you an opportunity to manage applications easier than having them installed on clear Linux machine. I haven’t said “or on Windows” because of Windows is not my preferred OS and Java applications are preferably be deployed on Linux servers.

So, if you have Ubuntu installed (Ubuntu is mostly used on VPS), just follow the official documentations:

Prepare Ubuntu’s repositories

1. Update the apt package index and install packages to allow apt to use a repository over HTTPS

 $ sudo apt-get update
 $ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-releaseCode language: Bash (bash)

2. Add Docker’s official GPG key

$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpgCode language: Bash (bash)

3. Use the following command to set up the repository

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullCode language: Bash (bash)

4. Update the apt package index

$ sudo apt-get updateCode language: Bash (bash)

Install Docker

1. Installation

$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-pluginCode language: Bash (bash)

2. Verify installed version

$ sudo docker run hello-worldCode language: Bash (bash)

Add your user into linux group named ‘docker’

$ sudo usermod -a -G docker <USER_NAME>Code language: Bash (bash)

I’d advice you to have Docker-Compose be installed as well. I will use Dockerfile and docker-compose.yaml both further.

Install docker-compose

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-composeCode language: JavaScript (javascript)

Git

To me, having own stage environment is having self hosted git server. There are only few options to achieve this:

  1. Git server without UI
  2. Gitea
  3. GitLab self managed

I will not provide you with all the steps to get them installed, please refer to official documentation.

I just want to say that my option of choice is Gitiea.

Preliminary settings for docker images to be communicating

You must decide how your components will run. Will you have only one server for running all components or will you have Gitea running separately from Java applications. My setup is showing how to run all applications on a single Linux machine in a single Docker host server.

Networks

Gitea will run in a network named gitea, and components will run in separated network.

$ docker network create gitea
$ docker network create jenkins
$ docker network create <your-solution-name>Code language: Bash (bash)

Gitea in Docker

In order to run Gitea in Docker you need to setup the database image and the very Gitea image.

Here is docker-compose.yaml for instantiating Gitea in Docker.

version: "3.7"

networks:
  gitea:
    external: true

services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      # - "8080:3000"
      - "2221:22"
    expose:
      - 3000
    depends_on:
      - db
  db:
     image: postgres:13
     restart: always
     environment:
       - POSTGRES_USER=gitea
       - POSTGRES_PASSWORD=gitea
       - POSTGRES_DB=gitea
     networks:
       - gitea
     volumes:
       - ./postgres:/var/lib/postgresql/dataCode language: PHP (php)

Jenkins

Why Jenkins? Jenkins gives you a button to run tests, a button to build and deploy every single application and even will perform testing your code right after you pushing it to git as well.

Here is the official documentations of how to install Jenkins in Docker.

I provide you with my solution of how to use Jenkins in Docker.

the principal schema of how to deploy Jenkins in Docker

The picture above depicts a scheme of how Jenkins may run in Docker:

  1. Jenkins Controller runs in Docker container itself to be isolated and packed in bundle
  2. Jenkins Agents run in Docker in separated containers
  3. They all runs in a single Docker network
  4. Jenkins runs application testing on Jenkins agents
  5. Jenkins deploys applications services on Host machine because of they must be started into Docker containers as well
  6. Due to 5. Host machine is connected to Jenkins Controller as SSH Jenkins agent
  7. Every new deployment stage must be connected to Jenkins Controller as SSH Jenkins agent
connecting production stage to Jenkins Controller

Actually, you can run another instance of Jenkins on each another Linux Machine as an environment stage.

Generating SSH keys for Jenkins

Here is a script helping to generate SSH key pair. It will be used every time when such pair is needed.

$ cat generate-key.sh
#/bin/bash
ssh-keygen -m PEM -t rsa -b 4096 -f $(pwd)/$1.rsaCode language: Bash (bash)

Installing Jenkins in Docker

I’d prefer to build Jenkins Controller and Agents with Dockerfiles.

I have following structure on my disk:

-build
-build/agent
-build/agent/Dockerfile
-build/controller
-build/controller/Dockerfile
-jenkins_home
-docker-compose.yaml
-rebuild.sh

Jenkins Agent Dockerfile is:

FROM jenkins/ssh-agent:latest-jdk17

ARG DOCKER_COMPOSE_VERSION=2.15.0

USER root
RUN apt-get update && \
   apt-get upgrade -y && \
   apt-get -y install apt-transport-https \
      ca-certificates \
      curl \
      gnupg2 \
      git \
      software-properties-common && \
   curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
   add-apt-repository \
      "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
      $(lsb_release -cs) \
      stable" && \
   apt-get update && \
   apt-get -y install docker-ce && \
   apt-get clean autoclean && apt-get autoremove && rm -rf /var/lib/{apt,dpkg,cache,log}/

RUN curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose 

#RUN usermod -aG docker jenkins && gpasswd -a jenkins docker

USER jenkins

ENTRYPOINT ["setup-sshd"]Code language: PHP (php)

Jenkins Controller Dockerfile:

FROM jenkins/jenkins:lts

ARG DOCKER_COMPOSE_VERSION=2.15.0

USER root

# Install the latest Docker CE binaries and add user `jenkins` to the docker group
RUN apt-get update
RUN apt-get -y --no-install-recommends install apt-transport-https \
      apt-utils ca-certificates curl gnupg2 software-properties-common && \
    curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo     "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
    add-apt-repository \
      "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
      $(lsb_release -cs) \
      stable"

RUN apt-get update && apt-get install -y docker-ce-cli docker-ce && \
   apt-get clean && \
   usermod -aG docker jenkins

RUN curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
   -o /usr/local/bin/docker-compose && \
   chmod u+x /usr/local/bin/docker-compose

USER jenkins
RUN jenkins-plugin-cli --plugins "docker-workflow:1.29 ansicolor"Code language: PHP (php)

Here is docker-compose.yaml for running Jenkins Controller with two Jenkins Agents in Docker containers. Count of agents is limited by VPS (or server) CPU and memory limits.

version: '3.8'

networks:
  jenkins:
    external: true

services:

  jenkins:
    #image: jenkins/jenkins:lts
    build: ./build/controller/
    networks:
      - jenkins
    privileged: true
    user: root
    ports:
      - 9000:8080
      - 50000:50000
    container_name: jenkins
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - DOCKER_HOST=host-gateway
    restart: always

  jenkins-agent-1:
    image: jenkins/ssh-agent:latest-jdk17
    container_name: jenkins-agent-1
    restart: always
    networks:
      - jenkins
    environment:
      - JENKINS_AGENT_SSH_PUBKEY=ssh-rsa AAAA...== bvn13@bvn13-workbook # paste public key here


  jenkins-agent-2:
    image: jenkins/ssh-agent:latest-jdk17
    container_name: jenkins-agent-2
    restart: always
    networks:
      - jenkins
    environment:
      - JENKINS_AGENT_SSH_PUBKEY=ssh-rsa AAAA...== bvn13@bvn13-workbook # paste public key here

Code language: PHP (php)

rebuild.sh content:

#!/bin/env bash
docker-compose down
docker-compose up -d --build --force-recreateCode language: JavaScript (javascript)

Jenkins setup steps

1. Please refer the post-installation setup wizard on Jenkins official documentation page to setup Jenkins properly.

2. Next step is to add agents into controller.

Hints:

  1. Create credentials for accessing to Agents by adding private key as a pair to the very public key specified in docker-compose.yaml for agents few steps earlier
  2. Agent’s host name is docker container name specified into docker-compose.yaml few steps earlier

3. Next – add Host as Jenkins agent

Hints:

  1. Remote root path shall be specified as a directory under your user’s home directory otherwise Jenkins cannot access it: /home/{USER}/jenkins-agent
  2. Connection to Host shall be ssh
  3. Host IP is 172.17.0.1 – Docker HOST IP
  4. Provide the credentials to Host system – please generate another key pair for this step. Public key shall be specified as approved one in ~/.ssh/authorized_keys file. Private key shall be added in Jenkins credentials
adjusting connection to Host from Jenkins Controller Docker image

Spring Cloud Eureka

Spring Cloud Eureka is an application that must run between services to coordinate their communications.

Spring Admin Panel

Spring boot admin provides an admin interface for Spring Boot web applications which expose actuator endpoints. It provides the possibility to look after services (health), their metrics, logs and others actuator endpoints.

spring boot admin panel

Spring Cloud Config Server

Spring Cloud Config Server is intended for externalizing configuration. I take the main feature Spring Cloud Config Server provides to be the possibility to update properties of a service without the need to rebuild or even reboot service.

Leave a Reply

Your email address will not be published. Required fields are marked *