GitHub Actionsを使ったGitHub PackagesでのNode.jsのDockerイメージの管理

オープンソースの開発者に関わらず、githubを利用して開発をされているかたは多いと思います。

Repositoryという言葉は長いので、英語でもよく repoと書いたり、会話でも repoと言ったりします。



今回は、こちらからの翻訳です。

https://snyk.io/blog/managing-node-js-docker-images-in-github-packages-using-github-actions/

githubでの安全なNode.jsのDockerイメージの管理についてステップバイステップの説明となっています。

# GitHub Actionsを使用して、GitHub Packagesで Node.jsのDockerイメージの管理する



Liran Tal
July 13, 2021
現在、あなたがオープンソース開発をしているなら、GitHubコミュニティ内でアクティブに活動し、オープンソースプロジェクトやそのリポジトリに参加されていると思います。最近GitHubのエコシステムに加わったのが[GitHub Packages](https://github.com/features/packages)で、2019年に発表され、現在はGitHub Packagesコンテナーレジストリの一般提供により、さらにアップデートされています。つまり、Dockerベースのイメージや、その他のOCIに準拠したフォーマットを、**すべてGitHubのエコシステム内**で公開したり、プルしたりできるようになったのです。
 
この記事では、GitHub Actions のワークフローをひとつひとつ説明していきます。 [Node.js プロジェクトを Docker イメージとして公開](https://snyk.io/blog/10-best-practices-to-containerize-nodejs-web-applications-with-docker/)/[参考翻訳](https://qiita.com/bricolageart/items/a509594a5b4c349e90b7)し、それを GitHub Packages のコンテナレジストリにプッシュする方法を学びます。
まず、その前に、[GitHub Support Community](https://github.community/) という GitHub ユーザー向けのサポートフォーラムがあることをご存知でしょうか。何か困ったことがあったら、一度訪れてみるとよいでしょう。
この記事のNode.jsプロジェクトは [dockly](https://github.com/lirantal/dockly)で、Dockerコンテナとサービスを管理するための没入型端末インターフェースを提供する、オープンソースのNode.jsコマンドラインツールです。
<img src="https://snyk.io/wp-content/uploads/blog-nodejs-docker-gh-dockly.png">

 
### GitHub Actionsのワークフローを作成する 

GitHub上のオープンソースリポジトリを参照し、**Actions**タブ、**New Workflow**の順にクリックし、以下のようなワークフローを自分で設定してください。
<img src="https://snyk.io/wp-content/uploads/blog-nodejs-docker-gh-workflow.png">

まず、ワークフローのファイル名を定義し、**docker-publish.yml** など、お好きな命名規則で設定してください。
GitHubでは、ワークフローのコードがあらかじめ入力されている場合があります。その場合はすべて削除して、次のコードを貼り付けます。

``` docker 
name: Docker

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

on:
  push:
    branches: [ main ]
    tags: [ 'v*.*.*' ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
```

上記のワークフローでは、次のような規約が設定されています。
- このワークフローは、メインブランチへのコミット、あるいはメインブランチへのプルリクエストに対してのみ実行されます。
注意: もしあなたのリポジトリが以前のコンベンションのmaster ブランチを使用している場合は、ここと残りのコードスニペット全体でブランチ名を変更する必要があります。また、`<v*.*.*>`,というsemverフォーマットでタグをリポジトリにプッシュしたときにも実行されます。これは、各バージョンに対してDockerイメージをパブリッシュできるので便利です。
- これにより、新しいDockerイメージが公開されたときに、GitHubリポジトリのタグも作成されるようになります。
- GitHub Container Registry (ghcr.io) を指す残りのワークフロージョブのグローバル環境変数を設定し、 Docker イメージ名を`lirantal/nodejs-app`といった、Docker Hub の標準的な規約にそって、 `<user>/<repo>` にしたがって設定します。
- 

次に、Dockerイメージの構築から公開までのプロセスを確立する、ジョブの定義を行います。

### GitHub Actionsのワークフローに含まれるDockerイメージのビルド

Docker イメージを GitHub Packages コンテナ・レジストリに (あるいは単に GitHub に) 公開できるようにするためには、まず有効なアカウントで認証を行う必要があります。そして、ビルドと公開のジョブの最初のステップは、ログインすることです。
前回のコード貼り付けの続きとして、以下のコードをコピーしてください。

```docker
jobs:
  build_and_publish:

    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Log into registry ${{ env.REGISTRY }}
        if: github.event_name != 'pull_request'
        uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
```

ここで、ジョブが行う動作の概要を確認しておきましょう。

1. まず、リポジトリのソースコードをチェックアウトすることから始めます。
2. 続いて、GitHub Packages のコンテナレジストリへの認証を行います。先ほど定義した`REGISTRY`  環境変数を再利用して、GitHub 独自のコンテナイメージのレジストリである `ghcr.io`, を指定しています。
認証するユーザー名は、ワークフローを開始したユーザーのもので、リポジトリの所有者であるあなたのユーザーと一致するはずです。パスワードは GitHub Actions のワークフローで自動的に利用可能になる `GITHUB_TOKEN` を利用しており、リポジトリのシークレット情報や環境変数に手動で追加する必要はありません。これがプルリクエストの場合はレジストリログインは発生しないことにお気づきかと思いますが、それは不要ではるのですが、プルリクエストCIやフォークに関する機密情報が漏洩する可能性があります。
3. 上記のコードスニペットの最後のステップは、イメージからメタデータを抽出し、Dockerイメージ構築プロセス（このワークフローの次で最後のステップ！）で利用できるようにするためのものです。メタデータは、Dockerビルドアクションで利用可能になるタグやラベルに関する情報から構成されています。具体的には、渡された`images` の入力は、タグのベース名として使用するDockerイメージを定義します。

### Dockerイメージを構築し、GitHubのPackagesコンテナレジストリに公開する

最終的には、Dockerイメージをビルドして公開することになります。これは、それをサポートするアクションを使用する統一されたステップで行われます。しかし、ワークフローのプッシュ入力に条件を設定し、プルリクエストでイベントがトリガーされなかった場合にのみ、レジストリへのイメージの公開を試みることに気づかれたことでしょう。

``` docker
     - name: Build and push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
```
 
これだけです。
ワークフローを保存し (インデントも適切に!)、このワークフローをメインブランチにマージして起動すると、ビルドされて GitHub Packages コンテナレジストリに画像が公開されるはずです。
 
うまくいけば、私のように待望の緑色のチェックマークが表示されるはずです。
<img src="https://snyk.io/wp-content/uploads/blog-nodejs-docker-gh-success.png">
 
##### Docker イメージを GitHub Packages のコンテナ・レジストリにプッシュするにはどうしたらいいですか？
GitHub Actions ワークフローファイルで、公式の Docker GitHub Action `docker/build-push-action` を使用し、`REGISTRY` に`ghcr.io` が設定されていることを確認してください。
 
### GitHub PackagesのコンテナレジストリからDockerイメージを取得する
さて、我々のプロジェクトは公開レジストリにDockerイメージを公開しているので、ローカルの開発環境からそれをプルしてテストしてみましょう。

```docker
$ docker pull ghcr.io/lirantal/my-nodejs-app
Using default tag: latest
latest: Pulling from lirantal/my-nodejs-app
b4d181a07f80: Pulling fs layer
de8ecf497b75: Pulling fs layer
69b92f9e5e70: Pulling fs layer
1f2b8e2c8ad8: Waiting
d0f4259cb643: Waiting
9ae47f3f99ba: Waiting
87270829eb60: Waiting
905fc634546c: Waiting
```

#### PullするDockerのバージョンイメージはどのように指定するのですか？
docker pullコマンドは、`<registry>/<user>/<repo>:<image tag>`のスキームを持つ追加引数を受け取ります。例えば、GitHub Packagesのコンテナレジストリから`my-nodejs-app image`イメージのDockerイメージタグ「latest」を取得するには、次のコマンドを使用します：
`docker pull ghcr.io/lirantal/my-nodejs-app`
成功しましたね！
でも、GitHub Packages にプッシュした Docker イメージを確認するにあたっては、これが最善の方法といえるでしょう。
 
### GitHubのパッケージからDockerイメージを探す 

GitHub のプロフィールページ (たとえば私の場合は https://github.com/lirantal?tab=packages) で、公開されているすべての Docker イメージが表示されています。
このチュートリアルに沿ってDockerイメージをビルドした方、見つかりましたか？GitHub Actions CIというリポジトリの一部としてGitHub PackagesにプッシュしたDockerイメージのパッケージのページはこちらです。

<img src="https://snyk.io/wp-content/uploads/blog-nodejs-docker-gh-pushed.jpg">

#### 公開されたDockerイメージにlatestタグがない？
 
使用している**main**ブランチや**master**ブランチのDockerイメージタグを指定せずに、その名前でDockerイメージを引き出そうとした場合、よく使われるDockerイメージタグの**latest**が利用できないことがあったかと思います。これはsemver（セマンティックバージョン管理）タグがプッシュされていない場合に起こる現象なので、おそらく自分のリポジトリではプッシュしているので利用可能になるのでしょう。
公開されたDockerイメージにDockerイメージタグの最新のエイリアスを強制したい場合は、次のようにメタデータアクションを更新します（追加の複数行のフレーバーキーに注目してください）。

```docker
     - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: |
            latest=true
            prefix=
            suffix=
```
 
ただし、この場合、メインブランチがCIジョブをトリガーする際に公開されるDockerイメージのエイリアスとして、常に最新のイメージタグが設定されることに注意してください。

 
#### Dockerイメージに最新のタグを付けるには？
以前ビルドしたイメージからローカルにタグを付けるには、 Docker tag imageコマンド `<local image name> <new tag>`を使用します。
例：`docker tag my-nodejs-app lirantal/my-nodejs-app:latest`

#### Dockerイメージにタグを付けて実行するにはどうすればよいですか？
イメージの実行時に特定のDockerイメージタグを使用するには、`docker run`コマンドに完全修飾イメージ名とタグを指定するだけです。
例: `docker run --rm -p 27017:27017 mongo:latest`
 
#### GitHub Actions MarketplaceにおけるDockerとコンテナ関連の統合機能
[GitHub Action Market Place](https://github.com/marketplace?type=actions)では　Github Action Wrokflows を探したり、作成したりできます。コード品質、依存関係管理、セキュリティなど、9000を超えるワークフローが用意されています。 [Snyk GitHub Actions](https://github.com/marketplace/actions/snyk)は、オープンソースライブラリにおけるサードパーティの脆弱性からプロジェクトを保護し、チームが [セキュアなコーディングプラクティス](https://snyk.io/learn/secure-coding-practices/)に従っていることを確認するためのものです。
 
この記事の焦点である、Dockerイメージの構築、テスト、レジストリへの公開については、マーケットプレイスを閲覧するよりも簡単に始める方法があります。すでにリポジトリに **Dockerfile** が存在する場合は、GitHub がそれを自動的に検出して GitHub Actions で使用するワークフローを提案してくれます。**Actions** タブに移動するだけです。
<img src="https://snyk.io/wp-content/uploads/blog-nodejs-docker-gh-actions.png">
 
最初に提案されたワークフロー**Publish Docker Container**は、すでにレビューしたものと同様のDockerイメージ公開とDockerイメージ構築のワークフローをセットアップしてくれるでしょう。

#####まとめとフォローアップ
セキュアな開発手法に少しは興味を持ってもらえたら幸いです。
下記の記事もオススメです。
 
 
1. サプライチェーンのセキュリティは重要です。関連記事はこちら。 [10 GitHub Security Best Practices}(https://snyk.io/blog/ten-git-hub-security-best-practices/)
 
2. Node.jsとnpmパッケージのビルドに興味がある方に是非。 
[GitHub Actions to securely publish npm packages](https://snyk.io/blog/github-actions-to-securely-publish-npm-packages/)

3. GitHubエコシステム内で多くの時間を費やすなら、ワークフロー内でより統合された体験を提供するSnykのコードスキャンをチェックしてみてください。[GitHub Security Code Scanning: Secure your open source dependencies](https://snyk.io/blog/github-security-code-scanning/)


[無料でセキュアなSDLC](https://app.snyk.io/login?cta=sign-up&sect=body&page=managing-node-js-docker-images-in-github-packages-using-github-actions)


######最後まで、読んでいただきありがとうございました！！！

---
Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global