Creating OCI DevOps CI/CD pipeline and deploy code to OCI Functions

This blog post will describe how to create CI/CD pipeline with Oracle Cloud Infrastructure. The deployment environment for this is OCI Functions, which is a serverless application.

The scenario we build is as follows;

  • Developer commit code to local repository
  • Code push to github repository
  • Code get synced to OCI code repository
  • Run OCI Continuous Integration (CI)
  • Container image will be uploaded to container registry
  • Automatically trigger OCI Continuous Delivery (CD) and deploy to OCI Functions
  • Use OCI Gateway to access the function from public Internet

Architecture

Figure 1 shows the complete architecture of the scenario.

Figure 1: Architecture

Activities 

The following are steps to achieve our setup and will explain in detail in the next section.

  1. Create DevOps Dynamic group
  2. Create Policies for DevOps Project
  3. Create Container Registry Repository
  4. Verify code and github
  5. Create DevOps Project
  6. Create External Connection to github
  7. Create Build Pipeline and test
  8. Create Function and deploy application
  9. Create API Gateway
  10. Create Deployment Pipeline
  11. Test the CI/CD pipeline

Step-by-Step Guide 

This section will explain the tasks along with the necessary screenshots of each activity.

1. Create DevOps Dynamic group

The dynamic group facilitates the grouping of multiple resources, so it’s convenient to write policies. We need to create a dynamic group for DevOps project at the start.

Figure 2: Locate Dynamic Group

Note down the OCID of the compartment to include it in the dynamic group rule.

Figure 3: Create Dynamic group

This rule requires it to be included in the dynamic group. It covers all resource types needed for our project.

“All {resource.compartment.id = ‘ocid1.compartment.oc1..xxxxxx’, Any {resource.type = ‘devopsdeploypipeline’, resource.type = ‘devopsbuildpipeline’, resource.type = ‘devopsrepository’, resource.type = ‘devopsconnection’, resource.type = ‘devopstrigger’}}”

2. Create Policies for DevOps Project

The policies are required to control the access to the OCI resources. This can be designed as per the security requirement of the organization. Below is just an example of rights.

Figure 4: Locate Policies

In order to create the policies we need to know the compartment name that our DevOps project is going to create and the dynamic group name that we created in step 1.

Figure 5: Create Policy

Below are a sample set of policies that you can organize your security. You may not require all the policies and may include additional as required.

Allow dynamic-group <dynamic group name> to manage devops-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage devops-repository in compartment <compartment name>

Allow dynamic-group <dynamic group name> to read secret-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use ons-topics in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use adm-knowledge-bases in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage devops-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage adm-vulnerability-audits in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use subnets in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use vnics in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use network-security-groups in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use cabundles in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use vaults in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use keys in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage secret-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage generic-artifacts in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage repos in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage generic-artifacts in compartment <compartment name>

Allow dynamic-group <dynamic group name> to manage devops-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to use ons-topics in compartment <compartment name>

Allow dynamic-group <dynamic group name> to read secret-family in compartment <compartment name>

Allow dynamic-group <dynamic group name> to read devops-family in compartment <compartment name>

3. Create Container Registry Repository

We are going to dockerize our code at the CI (continuous Integration) and store it in the container repository, so that the CD (Continuous Delivery) stage can get it from the  repository. 

Therefore, we need to create a repository first that can be found at ‘Developer Services’.

Figure 6: Create Repository

4. Verify Code and github repository

In this section, we’ll explore the files required for the deployment. The target is to deploy a simple web page. 

The figure 7 shows the build specification file will use for OCI DevOps (CI). If you need more information about building build_spec.yaml please refer to the documentation here.

Make sure to adjust the DOCKER_TAG and REGISTRY with the correct path. The namespace can be found on the container repository created in step 3.

Figure 7: build_spec.yaml

Figure 8 is the ‘Dockerfile’ that will dockerize the web file.

Figure 8: Dockerfile

Figure 9, is the PHP file to deploy. This will have a background colour of blue and version. In the next deployment we can change them and see the execution of CI/CD.

Figure 9: hello.php

Figure 10 is a configuration file for the API gateway. Please update the ‘functionId’ with the OCID of the function after creating the same.

Figure 10: spec.json

Figure 11, is representation of our code in the github code repository.

Figure 11: github

5. Create DevOps Project

We need to create a top and a subscription as prerequisites to create the DevOps project. This is to inform the progress/ status of DevOps project actions.

Figure 12: Create Notification

The OCI DevOps project can be found at ‘Developer Services’.

Figure 13: Locate DevOps project

We can provide a preferred name for the project and choose the previously created topic for the communication.

Figure 14: Create DevOps Project

The Devops project will look like figure 15 after creation. We can enable logging as the first activity.

Figure 15: DevOps Project

6. Create External Connection to github

Since our objective is to sync our code in github with our code repository in the DevOps project we need to create an ‘external connection’. This will make sure to sync the committed code from github to DevOps project in OCI in the setup time interval.

In order to do that, we first need to generate a token at github and store it in OCI securely. OCI DevOps project can then create connections using the credentials. 

Access github – > Settings -> Developer Settings -> Fine-grained tokens

Figure 16: Access token

Provide a name for the token and generate one.There you can decide the permission levels as you preferred.

Figure 17: Token Name

Then we need to copy the token generated.

Figure 18: Copy Token

If you don’t have a OCI vault already, you need to create one to store the token.

Figure 19: Create OCI Vault

And then, create a master encryption key as in figure 20.

Figure 20: Create Vault Key

Then it is time to store the password (github token) as a secret.

Figure 21: Create Secret

We can now create an external connection at the OCI DevOps project. Choose the type as ‘github’ and previously created secret from the menu.

Figure 22: Create External Connection

We can then validate the connection by clicking ‘Validate connection’.

Figure 22: Validate Connection

Next step is to bring our code in github and have a repository in DevOps project. We need to navigate to ‘Code Repositories’ and click on ‘Mirror repository’.

Figure 23: DevOps Code Repositories

As in figure 24, we can provide connection name and schedule time for the connection. With the default setting, it automatically syncs with the github repository every 15 minutes. We can adjust it to lower or higher.

Figure 24: Create Mirror Repository

7. Create Build Pipeline and Test

We can create a build pipeline which is the continuous integration (CI) part now. This is available under the resources section of the DevOps project.

Figure 25: Create Build Pipeline

Once you click on the ‘create’ you can see a graphical user interface like in figure 26. From here, we can add stages to the pipeline.

Figure 26: Build Pipeline Add stage

In the build pipeline, ‘Managed Build’ needs to be the first stage. This will dockerize the application for the rest of stages.

Figure 27: Add first stage

After providing a name for the stage, we need to add the repository that contains the code. In our case it’s from ‘OCI Code Repository’. Actually we could use github directly as the code repository as  well. This is just to show the options we have. We need to provide ‘build_spec.yaml’ file path if it doesn’t include in the root path.

Figure 28: Managed Build Stage

Once the managed build stage dockerize the application, we need to upload it to the container registry repository. This can be achieved via the ‘Deliver artifacts’ stage.

Figure 29: Add Deliver artifacts stage

Since we haven’t created a ‘artifact’ with container registry path, we can create one here. The syntax is as follows;

<region-key>.ocir.io/<tenancy-namespace>/<repo-name>:<tag>

Figure 30: Add Deliver artifacts details

At the configuration step, we need to provide ‘Build config/result artifact name’. This needs to match with ‘outputArtifacts’ name in the ‘build_spec.yaml’ file.

Figure 31: associate artifacts

Figure 32 shows the snippet of ‘outputArtifacts’ from the ‘build_spec.yaml’ file.

Figure 32: outputArtifacts snippet

Now, the build pipeline is ready for dockerize and uploading the image to the container repository. We can test the execution by clicking the ‘Start manual run’ button.

Figure 33: Build pipeline first two steps

As in figure 34, we can see the successful completion in a few minutes time.  We can see the granular level steps in middle section and logs on the right hand side.

Figure 34: Build pipeline run completes

The image now appears in the container registry as expected. The deployment pipeline we are setting up next will get this image for the deployment.

Figure 35: Image Upload

8. Create Function and deploy application

Since the image is now uploaded, we can create an OCI function using that. OCI Functions is located under ‘Developer Services’. It’s required to have a VCN and subnet prior to create the function. 

Figure 36: Create Function-step 1

As in figure 37, choose ‘Create from existing image’ option and provide the path to the image in container registry repository.

Figure 37: Create Function-step 2

The OCI function is our deployment environment. Therefore we can go back to DevOps project and update this information.

Figure 38: Create Environment

We can provide the details of the function and click ‘Create environment’ to complete the task.

Figure 39: Environment Details

Now we can update the ‘spec.json’ with the correct OCID of the OCI function we created.

9. Create API Gateway

Now we can create an API gateway which allows users to access the application over the Internet.

Firstly we need to create dynamic group as in figure 40.

Figure 40: Dynamic group – API GW

The rule of the dynamic group is as follows. Make sure to update the compartment OCID correctly.

All {resource.type = ‘ApiGateway’, resource.compartment.id = ‘ocid1.compartment.oc1..xxxx’}

Then we need to create a policy (or update previous policy) with the following two. First policy will require the dynamic group name we just created and the second one requires the user group of yours. 

allow dynamic-group <API GW dynamic group> to use functions-family in compartment <compartment Name>

allow group <user group> to use fn-invocation in compartment <compartment Name>

The API Gateway is again available under ‘Developer Services’ and can be created under a public subnet.

Figure 41: Create API Gateway

Under the resources section of the API gateway, we can find the ‘Deployments’. Here we can provide a name and path (in this case ‘/v1’).

Figure 42: Create deployment -step1

As in figure 43, we can provide authentication (or no authentication in this case), route path (/hello) and backend type (Oracle functions) and create the deployment.

Figure 43: Create deployment -step2

10. Create Deployment Pipeline

Now we can come back to the DevOps project to complete the CI/CD pipeline. We can now start build the ‘Deployment Pipeline’ which takes care of deploying the image to the OCI functions.

Figure 44: Create Deployment Pipeline – step 1

Choose the ‘Function’ as the deployment type as in figure 45.

Figure 45: Create Deployment Pipeline – step 2

Upon selecting the environment we created earlier, you can see the details get auto populated.

Figure 46: Create Deployment Pipeline – step 3

This concludes the deployment pipeline of our DevOps project. Now we need to integrate the ‘Build pipeline’ so that it will flow continuously. 

In order to do that, revisit the deployment pipeline and add a stage. This time choose ‘Trigger Deployment’.

Figure 47: Add Trigger Deployment – step1

In the configure page, choose the deployment pipeline we created earlier. This will auto-poulate the details.

Figure 48: Add Trigger Deployment – step2

Figure 49 shows the both build and deployment pipeline steps we created. The steps selected are personal choice for the flow and you can mix and match many combinations.

Figure 49: Build and Deployment pipelines

We can now go to the build pipeline and run it by clicking the ‘Start manual run’.

Figure 50: Run Build pipeline

As in figure 51, we can now see both the build pipeline and deployment pipelines ran successfully.

Figure 51: CI/CD pipeline completes

11. Test the CI/CD pipeline

Let’s test the entire cycle and see whether the CI/CD pipeline is working fine. As you may remember, we added an API gateway to expose our application publicly over the Internet. The API gateway has an ‘endpoint’ which we can find in the API gateway page.

Figure 52: Locate Endpoint

We configured the path as (/hello) while creating the setup. Therefore, we need to copy the API endpoint on the web browser and add /hello at the end.

Now we should see the output as in figure 53. Note that version 1 appears in the blue background. This means CI/CD pipeline successfully deployed on the OCI functions.

Figure 53: First Run

Now, let’s change the code in hello.php and run the CI/CD pipeline to see whether the entire cycle is working. 

We’ll change the version to 2 and the background colour to ‘green’.

Figure 54: Change code – hello.php

After saving the file, let’s commit the changes. Figure 55 shows using the git desktop tool but it can be generic ‘git commit’ as well.

Figure 55: Commit changes

Then we can push the changes to the git repository.

Figure 56: Push Changes

When we create the OCI repository in OCI Devops project, we choose the default sync timing which is 15 minutes. If we want we can sync it manually as well.

Figure 57: Sync with OCI Repository

Now, go to the build pipeline and run it again.

Figure 58: Run build pipeline

We can observe the progress of the build pipeline and deploy the pipeline while it’s running. Once it’s completed we can check the browser again for the change. As we can see now the version 2 is appeared correctly along with the new background colour ‘green’.

Figure 59: New code deploy

This concludes the step-by-step guideline of building CI/CD using OCI DevOps and deploy code on OCI Functions.