OCI #10 – Docker Container Registry in Oracle Cloud

This recipe shows how to work with Oracle Cloud Infrastructure Registry (Docker Image Registry in Oracle Cloud).

Before you start, you should install and configure Oracle Cloud Infrastructure CLI. See: OCI Setup recipe, if needed.


Everything around production-grade Linux containers at mass scale has really begun with this short demo back in 2013. Linux container engines make use of Linux kernel features (cgroups, namespaces) to isolate applications executed inside logical entities called containers, which are built from multi-layered templates called images. You can watch this comprehensive introduction video. Docker is the most popular Linux container engine, however, there are also a bit less mature alternatives like CNCF-incubating rkt or containerd.

Container Image Registry

In order to leverage containers in production you need to store somewhere the Docker images for your applications. Oracle Cloud Infrastructure comes with Oracle Cloud Infrastructure Registry (OCIR), an Oracle-managed registry, where you can store your Docker images as private and public repositories. A single Registry repository can be thought of as a set of related, tagged Docker image versions for a single (micro)service. Tags are used to denote particular versions of a (micro)service. In other words you keep a separate Docker Registry repository for all versions of each individual (micro)service. An orchestration engine (for example Kubernetes) would then pull images from the registry and run new containers on various worker nodes.

Access policies

A user can interact with OCIR, if there is a correct IAM policy defined for the IAM group the user belongs to. This policy allows a group of users to create and delete repositories, push and pull images and change the visibility of a repository (between public and private):

allow group groupName to manage repos in tenancy where target.repo.name = /projectName-*/

The where clause, at the end of the policy statement, says that the policy refers only to these repositories whose names start with projectName-. Why would we need it? Each Cloud Account comes with a single Docker image registry that, by default, can store up to 500 repositories. In a multi-project environment, you usually need to control user access to selected repositories depending on the project that owns given images. In this case, you can use IAM policies that rely on project prefixes. When naming images, you can prefix each image. When you push an image into registry, it is resembled as repository with the same name. As a result, you can limit access rights for different IAM groups using IAM policies with repository name filters.


You can reuse an OCI CLI script I’ve prepared. First clone the Git repository:

git clone https://github.com/mtjakobczyk/blog-code.git
cd blog-code/oci-10

You can simply customize and execute this script as OCI tenancy or compartment administrator:

# oci/create-ocir-policy.sh 1/1

# Add a new policy statement to let group member manage OCIR repositories with a specific naming pattern
OCI_POLICY_MANAGE="allow group $OCI_GROUP to manage repos in tenancy where target.repo.name = /$OCI_PROJECT_PREFIX-*/"
oci iam policy create -c $OCI_POLICY_COMPARTMENT_OCID --name $OCI_POLICY_NAME --description "$OCI_POLICY_DESC" --statements "[ \"$OCI_POLICY_MANAGE\" ]"

You can find the entire script here.

There is one thing, I need to mention at this stage. The policy we’ve just added let’s the group members push and pull images, which is their core task. However, they won’t be able to list the repositories neither in OCI Console nor using API.  Actually, if you carefully think about the automation you would most probably involve, you may not need such capability. Yet, if you still prefer to allow the users to browse through the list of all OCIR repositories that are available in the tenancy’s OCIR, please add the following statement:

allow group groupName to inspect repos in tenancy where request.operation='ListDockerRepositories'

Generate Authentication Token

OCI IAM users can interact with OCIR using Docker v2 API and Docker client. The authentication is token-based. Before a user performs docker login against OCIR, he has to generate an authentication token for the corresponding IAM user. Let me show you two ways how to do it:

I. Done by the user in OCI Console – ad-hoc self-service approach:

  1. Sign in to OCI Console as the IAM user you would like to generate an authentication token for
  2. Go to User Settings
  3. You should see user details for this user. Go to Resources ➟ Auth Tokens and click Generate Token
  4. Note down the newly generated token, because you will never be able to display it again in OCI Console.

II. CLI-driven – automating authentication token creation (especially for another user):

  1. Identify the OCID of the IAM user for which you would like to generate an authentication token
  2. Execute the following CLI command:
    oci iam auth-token create --user-id $IAM_USER_OCID --description Token-1 | grep token
        "token": "XIaTLD9uP_(rX9W3]r#w",
  3. Note down the newly generated token, because you will see it only once.

Please note a single IAM user can have up to two authentication tokens at any given time.

Pushing a Docker image into OCIR

In the previous two sections we added a required policy and generated an authentication token for an IAM user. Now, we are ready to push a Docker image to OCIR.

In this example, we will build on our local machine a Docker image that encapsulates a simple Node.js application and push it into OCIR. If you cloned the Git repository I linked earlier in this post, make sure you are in this folder first:

cd blog-code/oci-10

Now, execute the following commands:

cd app
docker build -t $IMAGE_NAME:$IMAGE_TAG .
docker login -u $OCI_TENANCY/$OCI_USER $OCIR_REGION.ocir.io
### You will be prompted for the auth token here

You can find the entire script here.

The code above will:

  • build a Docker image with simple Node.js application
  • tag the new image using the format required by OCID
  • login to a remote registry (OCIR)
  • push the image to OCIR
Sending build context to Docker daemon 3.072 kB
Step 1/4 : FROM node:10.13-alpine
Trying to pull repository docker.io/library/node ... 
10.13-alpine: Pulling from docker.io/library/node
4fe2ade4980c: Pull complete 
e4ac5f64a73e: Pull complete 
f62e97d98767: Pull complete 
Digest: sha256:5ba2248f02abf173f0fb2bd6f84fca7f3616496c50c9333cdd362314ec2970d9
Status: Downloaded newer image for docker.io/node:10.13-alpine
 ---> e35872f034fd
Step 2/4 : ADD app.js .
 ---> e603137d5ad2
Removing intermediate container c6eac4b1d54b
Step 3/4 : EXPOSE 8080
 ---> Running in 5626417a29fc
 ---> 6d7cd9fbe57b
Removing intermediate container 5626417a29fc
Step 4/4 : CMD node app.js
 ---> Running in aa8c48327a61
 ---> 106ddc2d93a6
Removing intermediate container aa8c48327a61
Successfully built 106ddc2d93a6
Login Succeeded
The push refers to a repository [fra.ocir.io/▋▋▋▋▋▋/p-oraas-simple-api]
e8f3411efc34: Pushed 
f87e587403a6: Pushed 
819d7ee3ee24: Pushed 
df64d3292fd6: Pushed 
1.0: digest: sha256:1525924e4e79f5cc5d7a67a755343cac0568ce40f773badc8981d08d555f9b1f size: 1158

You should be able to see the newly created repository in OCIR. Just log in as an administrator or any other user that belongs to the group to which you’ve granted ListDockerRepositories operation access.


You can find the code I presented in this chapter here.

OCI #9 – Custom Image for Compute

This recipe shows how to create a new Custom Image for Compute instances.

Before you start, you must install and configure Oracle Cloud Infrastructure CLI ( see: CLI Setup recipe, if needed) and Terraform ( see: Terraform Setup recipe, if needed).

An Image is a template that specifies the instance’s preinstalled software including the Operating System. It is used to initiate a boot volume that gets associated with an instance during provisioning. Oracle Cloud Infrastructure provides default images with various Operating Systems. You can create your own custom images to install additional software on top of an existing image.

You create a new Custom Image from an existing compute instance. This explains the recommended steps you should take, namely:

  1. provision a new compute instance (I like to call it an image builder instance)
  2. install software you would like your new custom image to include
  3. create a new custom image from the instance
  4. terminate the compute instance

If you use scripts, the entire process can be automated and parametrized.

Create Custom Image

First, let’s launch a new compute instance to use it as a base for our image. If you would like your new custom image to include an installation of Node.js on top of CentOS 7.5, you have to provision the instance using Oracle-provided CentOS 7.5 image, install Node.js and use the instance to create a new custom image.

You can use a simple stack I’ve prepared. It will launch a single virtual machine with CentOS 7.5 in a newly created VCN. To do so:

git clone https://github.com/mtjakobczyk/blog-code.git
cd blog-code/oci-09/
# Create a SSH Keypar for the image builder instance
mkdir keys
ssh-keygen -t rsa -b 2048 -C "yourName@example.com" -f keys/oci_id_rsa

Now you should see this:

tree git/blog-code/oci-09
├── build_image.sh
├── infrastructure
│   ├── basevm.tf
│   └── provider.tf
├── keys
│   ├── oci_id_rsa ⇽ 
│   └── oci_id_rsa.pub ⇽ 
└── scripts
    ├── image_building_simple.sh
    └── wait_for_ssh_on_vm.sh

Terraform will need to know the compartment OCID. You can pass it using environment variable. Replace the placeholder and execute the code snippet below:

# oci-09/build_image.sh 1/5
export TF_VAR_compartment_ocid={put-here-your-compartment}

Now you can provision the instance by executing the code snippet. Please use the same console tab as before to reuse the shell variables:

# oci-09/build_image.sh 2/5
export TF_VAR_custom_image_shape="$custom_image_shape"
export TF_VAR_custom_image_base="$custom_image_base"
cd infrastructure
terraform init
echo "IB: Provisioning image building infrastructure"
terraform apply -auto-approve 2>&1 | tee ../terraform.out
vm_ip=`cat ../terraform.out | grep image_builder_vm_public_ip | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+'`
vm_ocid=`cat ../terraform.out | grep image_builder_vm_ocid | awk '{print $3}'`
echo "IB ip: $vm_ip ocid: $vm_ocid"
cd ..
echo "IB: Waiting for SSH on VM"

Next, install the software you would like your custom image to include. As you can see I am using an external shell script that is executed on the instance to perform the installation of Node.js. If you would like to install any other software, this is the script you should customise (scripts/image_building_simple.sh). The installation script is executed synchronously. In this way we can be sure that the image would never get created too early. Please use the same console tab as before to reuse the shell variables:

# oci-09/build_image.sh 3/5
echo "IB: Executing image building script"
ssh -i keys/oci_id_rsa -o StrictHostKeyChecking=no -l opc $vm_ip "bash -s" < scripts/image_building_simple.sh
echo "IB: Ready"

We use OCI CLI to create the custom image:

# oci-09/build_image.sh 4/5
oci compute image create --display-name $custom_image_name --instance-id "$vm_ocid" --wait-for-state AVAILABLE

Finally, we use Terraform to destroy the image builder instance

# oci-09/build_image.sh 5/5
cd infrastructure
terraform destroy -auto-approve
unset TF_VAR_custom_image_shape
unset TF_VAR_custom_image_base
unset TF_VAR_compartment_ocid

You can find the entire script here.

Now you should be able to see the new custom image:

oci compute image list --operating-system CentOS --operating-system-version 7 --output table --query "data [*].{Image:\"display-name\"}"
| Image                                 |
| CentOS-7-2018.11.16-0                 |
| CentOS-7-2018.10.12-0                 |
| CentOS-7-2018.09.19-0                 |
| CentOS-7-2018.10.12-0-Node.js-v10.0.0 |