Publish docker images to Nexus using Jenkins

Publish docker images to Nexus using Jenkins

GitHub -> Jenkins -> Nexus Docker Repository

·

10 min read

Introduction

In the previous blog, we learned how to push images to a docker hub repository using a Jenkins pipeline.

In this blog, we'll learn how to push images to a docker-hosted repository on Nexus.

Prerequisites

Read my previous blog.

Code

Here's the code link.

There's a slight change in the script.groovy file. We have to specify the IP address of the docker-hosted repo when we build an image and when we push this image to the hosted repository. Take a look at the code snippet:

sh "docker build -t 64.227.128.95:8082/sanskriti-portfolio:${BUILD_NUMBER} ." 
sh "docker push 64.227.128.95:8082/sanskriti-portfolio:${BUILD_NUMBER}"

Desired Result

The CI pipeline should build a docker image and publish the image on a docker repository hosted on Nexus. Screenshot from 2022-12-02 23-05-24.png

A brief on Nexus

Nexus by Sonatype is a repository manager that organizes, stores, and distributes artifacts needed for development.

With Nexus, developers can completely control access to, and deployment of, every artifact in an organization from a single location, making it easier to distribute software.

It is most commonly used for hosting Apache Maven. Currently, it supports Maven/Java, npm, NuGet, RubyGems, Docker, P2, OBR, APT and YUM, and more.

You can think of it as a Docker hub repository, but unlike Docker hub, it can store docker images, jar files, war files, and other artifacts.

Setting up Nexus

We will run the Nexus application as a container on a cloud server.

  1. Create a droplet

    Start a droplet named nexus-server on Digital Ocean. I am calling it nexus-server just for the sake of simplicity. We will not directly install the nexus application on the server. Instead, we will run nexus as a container.

    • Create a droplet Screenshot from 2022-12-02 16-42-38.png
    • Choose a Region and an Ubuntu version: You can choose any version. Screenshot from 2022-12-02 16-44-01.png
    • Select CPU options Screenshot from 2022-12-02 16-44-37.png
    • Choose the Authentication Method: Create one if you don't have it already. Screenshot from 2022-12-02 16-44-59.png
    • Change the hostname: Change the hostname to nexus-server and then click on Create Droplet. Screenshot from 2022-12-02 16-45-44.png nexus-server droplet created successfully. Screenshot from 2022-12-02 16-46-43.png
  2. Create a firewall

    A firewall keeps your machine protected from unwanted access. Using it, you can control which devices can access which port.

    • Create a firewall Screenshot from 2022-12-02 16-47-51.png
    • Inbound Rules: Name it nexus-firewall and configure inbound rules. At port 22, on which you can access the nexus-server terminal, I suggest you allow access only to your device. Remove All IPv4 and IPv5 from the sources and add the IP address of your device. Screenshot from 2022-12-02 16-48-44.png
    • Apply to Droplets: Add the nexus-server droplet to this list. Now, the nexus-firewall rules are applied to this droplet. Screenshot from 2022-12-02 16-49-47.png
  3. SSH into the server

    Copy the IP address of the nexus-server droplet and ssh into it from your local terminal.

    ssh root@64.227.128.95
    

    Screenshot from 2022-12-02 16-51-58.png We can run commands on the server. I like to beautify the prompt, so I'll execute the following command:

    echo 'PS1="\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ "' >> .bashrc && source .bashrc
    
  4. Install docker

    Update the apt package manager and install docker on your server.

    apt update -y && apt install docker.io -y
    

    Screenshot from 2022-12-02 16-53-16.png Docker installed! Screenshot from 2022-12-02 16-53-45.png

  5. Run Nexus

    Run the nexus application following the official documentation. We are creating a persistent volume and attaching it to the container. This way, our data won't be lost even if the container fails.

    docker volume create --name nexus-data docker run -d -p 8081:8081 --name nexus -v nexus-data:/nexus-data sonatype/nexus3
    

    As you can see, the nexus application is running on port 8081. Screenshot from 2022-12-02 18-46-58.png

  6. Configure the nexus-firewall

    Configure the nexus-firewall. To access the nexus application on port 8081 of the nexus-server droplet, you have to configure the firewall. Permit to access the application running on port 8081. dasf.jpg

  7. Access Nexus application

    Go to your browser and enter ipAddressofNexusServer:8081. As you can see below, the nexus application is up and running. Screenshot from 2022-12-02 21-40-58.png

  8. Quick Nexus Setup

    This screen will load up. Screenshot from 2022-12-02 21-44-32.png

    • Get the password from /nexus-data/admin.password files that is present in the nexus container.
      docker exec -it nexus bash cat /nexus-data/admin.password
      
      Screenshot from 2022-12-02 21-42-27.png
    • Enter the username and password. Click on Sign-In button. Now, create a new password for the admin user. Screenshot from 2022-12-02 21-45-25.png
    • Disable anonymous access Screenshot from 2022-12-02 21-45-43.png
    • In the below image, you can observe two green boxes. The first box is cube-shaped, and the second is like a setting. They provide a user view and an admin view, respectively. We'll use the admin view to make changes in the Nexus repository manager. Screenshot from 2022-12-02 21-51-52.png

      Nexus is all set up. Now, let's move on to the next step.

Blob Store

We need storage for the repository that we'll create. The blob store is used to store repository contents. We can also store contents in a default blob store. But for now, let's create one.

  1. Navigate to Administration -> Repository -> Blob Stores. Screenshot from 2022-12-02 21-52-34.png

  2. Click on Create Blob Store Screenshot from 2022-12-02 21-53-06.png

  3. You'll see two option: - File - Store blobs in file system-based storage. - S3 - Store blobs in AWS S3 cloud storage. I'll choose File type for this project. Screenshot from 2022-12-02 21-53-45.png

  4. Name it docker-blob and click on the Save button. Screenshot from 2022-12-02 21-55-06.png

  5. docker-blob storage created. Screenshot from 2022-12-02 21-55-22.png

Create a docker repository

We have to create a docker repository on Nexus to publish docker images. Follow the below steps:

  1. Navigate to Repositories -> Create repository.
    Screenshot from 2022-12-02 21-56-08.png

  2. Select docker (hosted). Screenshot from 2022-12-02 21-56-32.png

  3. Name it dockerhosted-repo. We'll create an HTTP connector at port 8082 (any port other than port 8081, where the nexus application is running). Port 8082 is where the docker repository by nexus will be hosted. Screenshot from 2022-12-02 21-59-01.png

  4. Choose docker-blob to store repository contents. Screenshot from 2022-12-02 21-58-31.png

  5. Click on Create repository. The dokcerhosted-repo is created. Screenshot from 2022-12-02 21-59-38.png

Create a Role

Roles aggregate privileges into a related context and can, in turn, be grouped to create more complex roles.

The repository manager ships with a predefined admin as well as an anonymous role.

First, we'll create a role, give access privileges for the dockerhosted-repo repository, and then assign this role to a user. Using this user, we will log in to the dockherhosted-repo running at port 8082 and push docker images there.

To create a new role, follow these steps:

  1. Select Roles from the sidebar menu. Click on Create Role Screenshot from 2022-12-02 22-07-37.png

  2. Select Nexus-role as the role type. Screenshot from 2022-12-02 22-13-16.png

  3. Add Role ID and Role Name. I am naming both as nx-docker. Screenshot from 2022-12-02 22-14-11.png

  4. Add nexus-repository-view-docker-dockerhosted-repo-* privilege to this role. Screenshot from 2022-12-02 22-14-51.png Screenshot from 2022-12-02 22-15-41.png

  5. Role created! Screenshot from 2022-12-02 22-15-55.png

Creating a role is like having a position in your company. For example, you have a CEO position in your company. CEO has some privileges that only this role can exercise. So, that is about the role. CEO is the role name, and making decisions is the privilege assigned to this role. Now, If I say that Indra Nooyi is the CEO of the company, then Indra is the user here who is assigned a CEO role. I hope you get the idea.

Read more about Role in the official documentation.

Create a User

Now it's time to create a user and assign the nexus-docker role to it.

To create a user:

  1. Select Users from the sidebar menu. Click on + Create local user.
    Screenshot from 2022-12-02 22-16-30.png

  2. I am naming this user jenkins and assigning it the nexus-docker role. We'll use this user's username and password to create a nexus credentials ID in the Jenkins application. This nexus ID will then be used to log in to the dockerhosted-repo and push images. Screenshot from 2022-12-02 22-18-05.png

    In the script.groovy file I have mentioned this user. image.png

  3. jenkins user created! Screenshot from 2022-12-02 22-18-38.png

Realms

In order to access docker anonymously, you also need to enable the Docker Bearer Token Realm as generally outlined in Realms. This realm is inactive by default. Activate it. Screenshot from 2022-12-02 23-06-15.png Screenshot from 2022-12-02 23-06-39.png

For further reading, visit the documentation page.

Add credentials

Before building the pipeline we have to add credentials in the Jenkins application. I have already created credentials for github in my previous blog.

To access dockerhub-repo we have to create credentials. We will name the ID as nexus. As we have already set up a jenkins user that can access the docker repo on Nexus, we'll use this user's credentials as a name and password for the nexus ID. Let's go! Screenshot from 2022-12-02 22-30-12.png

nexus credential created! Screenshot from 2022-12-02 22-30-38.png

Create the pipeline

Let's create a Jenkins CI pipeline.

  1. Create a pipeline named sanskriti-portfolio. Screenshot from 2022-12-02 22-31-38.png

  2. We will create a pipeline using SCM. I am using the code from my GitHub repository. Use github credential.

Done! Let's build the pipeline, shall we?

Build Jenkins Pipeline

And Voila! We got an error! TT Screenshot from 2022-12-02 22-33-41.png Let's see what the error is. error.jpg

Error:

Error response from daemon: Get "64.227.128.95:8082/v2" : http: server gave HTTP response to HTTPS client

Cause of Error

The dockerhosted-repo is insecure, it doesn't use HTTPS protocol and docker daemon is not letting us access this insecure repository because it might be harmful for our system.

Resolve the error http: server gave HTTP response to HTTPS client

We have to configure docker so that it will let us use this dockehosted-repo. We have to add the IP address of this repository to the list of insecure registries.

docker info command

This command displays system-wide information regarding the Docker installation. One of the information listed is the list of insecure-registries. Screenshot from 2022-12-02 22-43-58.png

We know that the dockerhosted-repo is an insecure docker repository. We have to add this repository to the list of insecure registries so that the jenkins application running on the jenkins-server droplet can access this repo.

Adding Insecure registries

An insecure registry is a registry without a valid TLS certificate or one which only supports HTTP connections.

To add the dockerhosted-repo to the list of insecure registries, follow the below steps.

  1. Edit the daemon.json file in the jenkins application, whose default location is /etc/docker/daemon.json on the server where you have hosted the jenkins application. And in our case, the server is the jenkins-server droplet.

To be clear, we are editing the daemon.json file in the jenkins-server. And that is because the jenkins application will run the docker commands to push an image to an insecure registry.

If the daemon.json file does not exist, create it.

vi /etc/docker/daemon.json
  1. Add the below lines to this file. Remember, 64.227.128.95:8082 is the IP address where the repository dockerhosted-repo is being hosted and not where the nexus container is running.
    { 
    "insecure-registries": ["64.227.128.95:8082"] 
    }
    
    Screenshot from 2022-12-02 22-52-04.png
  2. Restart Docker for the changes to take effect.
    systemctl daemon-reload systemctl docker restart docker
    
  3. Run the following command to see whether the dockerhosted-repo was added to the list of insecure registries or not.
    docker info
    
    Screenshot from 2022-12-02 22-54-59.png

As you can see, the dockerhosted-repo repository was added to the registry successfully.

Build the pipeline again

This time the pipeline built successfully! Congratulations! Screenshot from 2022-12-02 23-04-05.png

PS - I made some silly mistakes so I had to delete two builds. Therefore, it shows build no. 4 just after the first build. You don't have to worry about it.

Docker hosted Repository on Nexus

Now, let's check whether the image was published on the nexus repository or not.

Change to user view(by clicking on cube-shaped green box), then Browse -> dockerhosted-repo -> v2 -> sanskriti-portfolio. Screenshot from 2022-12-02 23-05-00.png Screenshot from 2022-12-02 23-05-24.png

The image was published successfully! Yesss!

What's next

You can also create Cleanup Policies. Cleanup policies allow you to delete old and unused artifacts (like Docker images). I am not going to cover this topic. Visit and read about it here and build cleanup policies for your repositories.

Conclusion

We learned how to build a pipeline that pushes an image to a docker repository hosted by Nexus using Jenkins. You also learned how to configure Jenkins to push images to an insecure repository.

With this basic understanding, you can now build a CI pipeline for an application and push the docker image to a repo other than the Docker Hub.