iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📝

Distributing Cloud Functions Using GitHub Packages Container Registry

に公開

As the long title suggests, this is an experimental record of various things I wanted to try out.

Background

I have a Cloud Function (HTTP-triggered) that is called from a certain service. Since I was calling the Cloud Function on GCP even during local execution and testing, I wondered if I could containerize the Cloud Function and start it locally for access. That was the starting point.

Environment

  • macOS Catalina (10.15.7)
  • Docker Engine (20.10.5)
  • Nodejs (14.x)

Building a Cloud Functions Function for Local Startup

First, I had to make the Cloud Functions function requestable via HTTP. Initially, I thought about wrapping it with Express.js to turn it into a web server, but the official documentation mentioned that Cloud Functions actually uses an OSS library called Functions Frameworks.

Furthermore, a tool called pack is provided to build functions into containers that can be run locally. By using this, it automatically creates a container that wraps the function with Functions Frameworks. How convenient!

By the way, I also learned while researching this that the containers for Cloud Functions are placed in GCP's Container Registry and are accessible to users. I was also able to pull and run these locally.

1. Create a function

The function to be created this time is triggered by HTTP. I will use the sample from the official documentation for the function implementation.

First, initialize npm and install the module.

npm init
npm install escape-html --save

Place the sample implementation.

index.js
const escapeHtml = require('escape-html');

/**
 * HTTP Cloud Function.
 *
 * @param {Object} req Cloud Function request context.
 *                     More info: https://expressjs.com/en/api.html#req
 * @param {Object} res Cloud Function response context.
 *                     More info: https://expressjs.com/en/api.html#res
 */
exports.helloHttp = (req, res) => {
  res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};

2. Build the container with pack

Refer to the pack installation instructions and install it using brew. I actually wanted to run it in a container, but I couldn't due to a docker.sock permission error (it didn't work even with sudo, so I gave up for now).

brew install buildpacks/tap/pack

Following the procedure in the official documentation, build with pack.

pack build \
  --builder gcr.io/buildpacks/builder:v1 \
  --env GOOGLE_RUNTIME=nodejs \
  --env GOOGLE_FUNCTION_SIGNATURE_TYPE=http \
  --env GOOGLE_FUNCTION_TARGET=helloHttp \
  cloud-functions-sample

3. Run the container

Start the built container. Bind port 8080 to the container's port.

docker run --rm -p 8080:8080 cloud-functions-sample

You can see that the response from the function is returned by accessing localhost:8080!

$ curl http://localhost:8080
Hello World!

I will push the sample function created so far to GitHub.

Build with GitHub Actions and register in GitHub Packages Container Registry

Next, create the GitHub Actions definition.

.github/workflows/build.yml
# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the master branch
  push:
    branches: [master]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    env:
      IMAGE_NAME: cloud-functions-sample

    strategy:
      matrix:
        node-version: [14.x]

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: Run npm install
        run: npm install

      - name: Install pack-cli
        run: |
          sudo add-apt-repository ppa:cncf-buildpacks/pack-cli \
            && sudo apt-get update \
            && sudo apt-get install pack-cli

      - name: Run pack build
        run: |
          pack build \
            --builder gcr.io/buildpacks/builder:v1 \
            --env GOOGLE_RUNTIME=nodejs \
            --env GOOGLE_FUNCTION_SIGNATURE_TYPE=http \
            --env GOOGLE_FUNCTION_TARGET=helloHttp \
            $IMAGE_NAME

      - name: Log in to GitHub Docker Registry
        uses: docker/login-action@v1
        with:
          registry: docker.pkg.github.com
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Push a container image to GitHub Container Registory
        run: |
          docker tag $IMAGE_NAME docker.pkg.github.com/$GITHUB_REPOSITORY/$IMAGE_NAME:latest \
            && docker push docker.pkg.github.com/$GITHUB_REPOSITORY/$IMAGE_NAME:latest

The installation steps for pack also included instructions for Ubuntu, so I was able to install it by simply copying and pasting.

The steps to push to the Container Registry are similar to other registries. For authentication, you use either a GITHUB_TOKEN or a Personal Access Token. In the case of GitHub Actions, a GITHUB_TOKEN is automatically issued, so it's very convenient as you don't need to manage credentials!

Registration of the container to the Container Registry is now complete.

Pull and run from GitHub Packages Container Registry

Finally, let's try to pull and run the registered container locally.

$ docker pull docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample:latest
Error response from daemon: Head https://docker.pkg.github.com/v2/bisque33/github-container-integration/cloud-functions-sample/manifests/latest: no basic auth credentials

It seems that authentication is required even though it's a public repository.

For authentication, generate a Personal access token from the Developer settings in your personal settings. The permission only needs to be read:packages.

I placed the issued token in ~/.github/pat-only-read-packages.txt. Using that, I'll perform a docker login and try docker pull again.

$ cat ~/.github/pat-only-read-packages.txt | docker login https://docker.pkg.github.com -u bisque33 --password-stdin
Login Succeeded

$ docker pull docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample:latest
WARNING: ⚠️ Failed to pull manifest by the resolved digest. This registry does not
	appear to conform to the distribution registry specification; falling back to
	pull by tag.  This fallback is DEPRECATED, and will be removed in a future
	release.  Please contact admins of https://docker.pkg.github.com. ⚠️

latest: Pulling from bisque33/github-container-integration/cloud-functions-sample
c81a311ac26a: Already exists
05c49db37ee9: Already exists
3c2cba919283: Already exists
91af061cbbcd: Already exists
45d7126bebce: Already exists
cf6b7f7a08d9: Pull complete
09b4558c7e27: Pull complete
8f0d61e10f8d: Pull complete
785ed11a713f: Pull complete
476a9b260057: Pull complete
769b7fb5f768: Pull complete
9e1b79ed05fb: Pull complete
Digest: sha256:1e6d4e80cd55be14300f5cf97955d6749712c2d0feae9284917c27b68ae95981
Status: Downloaded newer image for docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample:latest
docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample:latest

$ docker images | grep cloud-functions-sample
docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample          latest                    dea60fbcdd4b   41 years ago    224MB

A WARNING was displayed, but putting that aside for now, I confirmed it works correctly upon startup!

docker run --rm -p 8080:8080 docker.pkg.github.com/bisque33/github-container-integration/cloud-functions-sample:latest
$ curl http://localhost:8080
Hello World!

Conclusion

The sample repository I created is available here.

By linking GitHub Actions and GitHub Packages Container Registry, you can easily register images to the registry, making it much easier to share private images!

GitHubで編集を提案

Discussion