How to Build a Container Registry
Tags: pulumi, container, kubernetes, serverless
Published: 2021-03-08 19:15:30

Whether you are working with Kubernetes or serverless, your application uses containers. If you use the Docker desktop client, images are pushed to Docker Hub by default. Pulling images from Docker Hub is convenient, but there are many reasons to store images in your own registry. For example, Docker Hub doesn’t guarantee to produce the same image on repeated pulls, i.e., your base image might have changed. It’s also possible to inadvertently expose secrets in an intermediate image used to build the image stored on Docker Hub. There is also the possibility of vulnerabilities in even official images. This article shows how to create a repository and how to build and push images to that repository

##Container registry offerings
AWS provides the [Elastic Container Registry (ECR)](https://aws.amazon.com/ecr/), Azure has [Container Registry](https://azure.microsoft.com/en-us/services/container-registry/), and Google has it’s [Container Registry](https://cloud.google.com/container-registry). Each provider has associated services unique to their offering, but all support Docker or OCI compliant images.

##Build it
Let’s examine how to create a registry with the provider of your choice. In these examples, we create a registry, build a Docker image, and push the image to the registry. The [application](https://github.com/pulumi/examples/tree/master/aws-ts-containers/app) used for the image is NGINX.

Choose your cloud provider to learn how to build a registry. We'll use AWS in this example, but you can find examples for Azure and GCP [right here](https://www.pulumi.com/blog/how-to-registries/?utm_source=dev.to&utm_medium=page-post&utm_campaign=content-syndication):

In this example, we create an ECR repository configured to scan an image’s Operating System components. Scanning for vulnerabilities in an application is currently [out of scope](https://aws.amazon.com/blogs/containers/amazon-ecr-native-container-image-scanning/). We also set a policy for the repository that controls the actions allowed and a lifecycle policy that expires an image after a set time.

With Pulumi, it’s possible to build an image locally using Docker and push it to your repository. To push the image, we obtain the credentials required to push from the registry. Finally, we export the credentials and the URL for the registry. Read more about ECR in the [API Reference](https://www.pulumi.com/docs/reference/pkg/aws/ecr/).

```
import * as docker from "@pulumi/docker";
import * as aws from "@pulumi/aws";

// Create a repository and configure to scan the image on push
const repo = new aws.ecr.Repository("myrepository", {
    imageScanningConfiguration: {
        scanOnPush: true
    },
    imageTagMutability: "MUTABLE",
});

// Set a use policy for the repository
const repositoryPolicy = new aws.ecr.RepositoryPolicy("myrepositorypolicy", {
    repository: repo.id,
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Sid: "new policy",
            Effect: "Allow",
            Principal: "*",
            Action: [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:DescribeRepositories",
                "ecr:GetRepositoryPolicy",
                "ecr:ListImages",
                "ecr:DeleteRepository",
                "ecr:BatchDeleteImage",
                "ecr:SetRepositoryPolicy",
                "ecr:DeleteRepositoryPolicy"
            ]
        }]
    })
});

// Set a policy to control the lifecycle of an image
const lifecyclePolicy = new aws.ecr.LifecyclePolicy("mylifecyclepolicy", {
    repository: repo.id,
    policy: JSON.stringify({
        rules: [{
            rulePriority: 1,
            description: "Expire images older than 14 days",
            selection: {
                tagStatus: "untagged",
                countType: "sinceImagePushed",
                countUnit: "days",
                countNumber: 14
            },
            action: {
                type: "expire"
            }
        }]
    })
});

// Get the repository credentials we use to push to the repository
const repoCreds = repo.registryId.apply(async (registryId) => {
    const credentials = await aws.ecr.getCredentials({
        registryId: registryId,
    });
    const decodedCredentials = Buffer.from(credentials.authorizationToken, "base64").toString();
    const [username, password] = decodedCredentials.split(":");
    return { server: credentials.proxyEndpoint, username, password };
});

// Create a new image and push to the repository
const image = new docker.Image("myapp", {
    imageName: repo.repositoryUrl,
    build: "./app",
    registry: repoCreds,
})

// Export credentials and URL to the repository
export const credentials = repoCreds;
export const repoEndpoint = repo.repositoryUrl;
```

##Learn more
Container registries are just one of the many resources used for deploying modern applications. Implementations among cloud service providers differ by the functionality they offer and how they are deployed. The commonality among them is that they provide a secure place to store and retrieve Docker or OCI compliant container images. Explore how to create and manage resources for the cloud service provider of your choice with Pulumi. Great places to start are:

[Tutorials](https://www.pulumi.com/docs/tutorials/?utm_source=dev.to&utm_medium=page-post&utm_campaign=content-syndication)
[Examples](https://github.com/pulumi/examples?utm_source=dev.to&utm_medium=page-post&utm_campaign=content-syndication)
[API Reference](https://www.pulumi.com/docs/reference/pkg/?utm_source=dev.to&utm_medium=page-post&utm_campaign=content-syndication)