パフォーマンス無関心で作ったサイトのパフォーマンスチューニング④ 画像のアップロード圧縮

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

静的サイトで何が一番パフォーマンスに影響がでるかは、この記事を書いてきた経験上画像という認識です。
作成しているサイトにはアップロードされた画像を読み込む画面も存在しているため、圧縮が必要と考えました。

今回の内容

画像の圧縮方法を記録していきたいと思います!

使用した技術

Category     Technology Stack
Frontend Nextjs,React,Typescript
Backend Typescript,Node,CloudFunction
Database Firestore
Design   XD  

前回の内容

前回記事

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング③ FirebaseでNextjsをhosting(ImageタグとSSG併用難しい) - zare926のブログ

NextjsのImageを使用してFirebaseへデプロイする方法でした。
バージョンが低い状態でしか成功していないので、いつかAppRouterについても挑戦してみようと思っています。

browser-image-compressionとsharpの紹介

browser-image-compressionはクライアントサイドで圧縮できるライブラリです。
sharpはnodejsでサーバーサイドで圧縮するライブラリです。

browser-image-compression ライブラリ

まずはインストールしましょう

$ npm install browser-image-compression
# または
$ yarn add browser-image-compression

使いたい場所でimport

import imageCompression from 'browser-image-compression';`

で実際圧縮したい場合の例

const options = {
  maxSizeMB: 1, // この場合1Mです
  maxWidthOrHeight: 1920, //最大の高さが1920
  useWebWorker: true, // Webワーカーを有効にしている
};


  const inputImage = async (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target && e.target.files) {
      const file = e.target.files[0]
      const reader = new FileReader()
      const compressFile = await imageCompression(file, options)
      await imageUpload(compressFile)
      reader.readAsDataURL(compressFile)
    }
  }

optionに渡せる設定はドキュメント参照してください。

browser-image-compression - npm

とても簡単に圧縮できます!
クライアントサイドで特に重い処理などがなければ問題なく使えるライブラリだと思います。

sharp ライブラリ

前段としてNextのImageコンポーネントの画像圧縮に使われているライブラリです。
クライアントサイド側で圧縮することが似合わない場合はサーバーサイドで圧縮しましょう。
こちらもインストールから

npm install sharp
# または
yarn add sharp

モジュールじゃなくてrequire

const sharp = require('sharp');

圧縮の一例

// 入力画像のパス
const inputImagePath = 'input.jpg';

// 出力画像のパス
const outputImagePath = 'output.jpg';

// 圧縮設定
const compressionOptions = {
  quality: 70, // 画質の品質を指定 (0から100の範囲)
  progressive: true, // プログレッシブJPEGを有効にする
  optimizePalette: true, // PNGの場合、パレット最適化を有効にする
  fit: 'inside', // 画像のフィット方法を指定
  background: 'white', // 透明な領域の背景色を指定
};

sharp(inputImagePath)
  .resize(800, 600) // 画像のリサイズ
  .toFile(outputImagePath, (err, info) => {
    if (err) {
      console.error(err);
    } else {
      console.log(info);
    }
  });

Webpなども変換できるようですね。
以下記事を参考にしました。

Node.jsのライブラリsharpの出力形式について | Simple is Beautiful.

FirebaseであればCloudFuntions側圧縮するということができそうですね。

const functions = require('firebase-functions');
const { Storage } = require('@google-cloud/storage');
const os = require('os');
const path = require('path');
const sharp = require('sharp');

const gcs = new Storage();

exports.compressImage = functions.storage.object().onFinalize(async (object) => {
  const bucket = gcs.bucket(object.bucket);
  const filePath = object.name;
  const fileName = path.basename(filePath);

  if (!filePath.startsWith('compressed/')) {
    // This check prevents infinite recursion
    const tempFilePath = path.join(os.tmpdir(), fileName);

    // Download the image from Firebase Storage
    await bucket.file(filePath).download({ destination: tempFilePath });

    // Compress the image
    await sharp(tempFilePath).resize(800).toFile(tempFilePath);

    // Upload the compressed image back to Storage
    await bucket.upload(tempFilePath, {
      destination: `compressed/${fileName}`,
    });

    // Clean up the temporary file
    return fs.unlinkSync(tempFilePath);
  }

  return null;
});

(このコードはGPTに作ってもらったので、古いと思います。すいません。。。)

とは言ってもCloudFunctionsのv1は10MB、v2は32Mまでが受け取り可能なデータ量なので、なかなかないと思いますが、それ以上のデータを圧縮する場合は別の方法が必要ですね。

最後に

全然気にもしてなかったんですが、パフォーマンスの記事を書くようになってから画像に対する処理に敏感になりましたね。
いいこと!

Firebase v8 から v9 への Firestore クエリのアップデート

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

Firebaseを使用した案件に携わってますが、ちょうどv8しかなく、初めてすぐにv9がリリースされました。
いつかいつかv9に移行せねばということで、今回重い腰を上げた次第です。
参考になる記事があまりなかったので、メモ程度に記事にします!

今回の内容

とりあえず、Queryからまとめます。
Firestoreからデータを取得する際につかうcollectionとdocのv8→v9の書き方の違いについてです。

初期設定

v8

import firebase from 'firebase/app';
import 'firebase/firestore';

const config = {
  ...YOUR_CONFIG
};

firebase.initializeApp(config);
const db = firebase.firestore();


v9 モジュールベースの初期化ですね。

import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const config = {
  ...YOUR_CONFIG
};

const app = initializeApp(config);
const db = getFirestore(app);

Firestoreクエリと実行(collection)

v8

const query = db.collection('users').where('age', '>=', 18);
query.get().then((querySnapshot) => {
  querySnapshot.forEach((doc) => {
    console.log(doc.id, ' => ', doc.data());
  });
});


v9 whereなどimportがいります。

import { collection, query, where } from 'firebase/firestore';
import { getDocs } from 'firebase/firestore';

const q = query(collection(db, 'users'), where('age', '>=', 18));
getDocs(q).then((querySnapshot) => {
  querySnapshot.forEach((doc) => {
    console.log(doc.id, ' => ', doc.data());
  });
});

query側をgetDocsの引数にしてますね。

Firestoreクエリと実行(doc)

v8

const docRef = db.collection('users').doc('user_id');
docRef.get().then((doc) => {
  if (doc.exists) {
    console.log('Document data:', doc.data());
  } else {
    console.log('No such document!');
  }
});

v9 getDocなので注意!collectionはgetDocsです。

import { getDoc, doc } from 'firebase/firestore';

const userDocRef = doc(db, 'users', 'user_id');
const docSnap = await getDoc(userDocRef);

if (docSnap.exists()) {
  console.log('Document data:', docSnap.data());
} else {
  console.log('No such document!');
}

補足ですが、自分の開発環境だとなぜかdocの第一引数の型が合いませんでした。

    function doc(firestore: types.FirebaseFirestore, path: string, ...pathSegments: string[]): DocumentReference<DocumentData>;
    function doc<T>(reference: types.CollectionReference<T>, path?: string, ...pathSegments: string[]): DocumentReference<T>;
    function doc(reference: types.DocumentReference<unknown>, path: string, ...pathSegments: string[]): DocumentReference<DocumentData>;

docの受け取れる型で、一番上が該当するはずなのになんでやんねん!と思ってたら、vscode再起動で治ってました。
どっちにしろなんでやねん!
だいぶ困惑してましたね、でもよかった。

まとめ

印象としては結構変わってました。
モジュールベースのアプローチに切り替わっていて、今風?なのかなという印象です。
いつかCreate,Update,Deleteもまとめたいと思います。(ほぼ自分のため)
以上ありがとうございました!!!

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング③ FirebaseでNextjsをhosting(ImageタグとSSG併用難しい)

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

作成した静的サイトのパフォーマンスをチューニングします。
経緯ですが、話すと長いので箇条書きでまとめます。

  • 案件をいただき静的サイトを作成した
  • 一時的にデプロイし公開することが急務だった
  • 静的サイトに紐づくプロジェクトがそろそろアクティブになる

やばい!チューニングしないと!
ということで徐々にパフォーマンスが良くなるよう修正していきます。

使用した技術

Category     Technology Stack
Frontend Nextjs,React,Typescript
Backend Typescript,Node,CloudFunction
Database Firestore
Design   XD  

前回の内容

前回記事

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング② NextjsのImageタグが便利 - zare926のブログ

要点をまとめると、HTMLのimgタグでsrcsetなどをセッティングするより、Nextjsを使っているならImageコンポーネントに任せちゃおうって話でした。

今回の内容

色々と経緯はあるんですが、お題としてはFirebaseのHostingを使用してNextjsをSSGでデプロイする方法です。
今回の話はあくまで経験した限りの情報なので、ご留意を!
そんなに多くは無いですが、DBからデータをfetchを取得して表示する項目も存在するので、SSGにしてパフォーマンスを良くしようとしました。

最初に注意点

Nextjsのバージョンは"next": "12.2.5"でSSG(getStaticProps)とImageコンポーネントを使用しFirebase Hostingを行なっていました。
Nextjs13でImageコンポーネントの仕様が少し変わったので、12→13にバージョンをアップデートしました。
そして調整を終え、いつも通りデプロイしてみると...
画像が表示されなくなりました!!!
調べてみると、元々SSGとnextjsのImageコンポーネントは併用ができないそうです。
ですが、Firebaseのドキュメントにはサポートされていると書いてあるし、そして今までできていた。

ドキュメント

Next.js を統合する  |  Firebase Hosting

何通りか試しては見たものの、なかなかうまくいかず、調べてみた記事やライブラリについての情報だけ載せておきます。

next/imageとSSG | エビスコム - EBISUCOM

next/imageを使ってビルド時に画像を最適化する方法

NextのImageコンポーネントはオンデマンドで画像を最適化するので、それ用のサーバーを立てる必要があるようですね。

現状少なくとも"next": "12.2.5"はこれから説明するデプロイ方法でSSGとImageコンポーネントを併用できそうです。

デプロイ手順

まずはFirebaseCLIをインストールしてください

$ npm install -g firebase-tools

すでにインストールされていますが、Nextのホスティングに対応しているバージョンが、ドキュメントだと11.14.2 以降なので、updateしてください。

$ npm i -g firebase-tools to update

続いてFirebaseCLIのフレームワークプレビューを有効にします。

$ firebase experiments:enable webframeworks

次にホスティングの設定をします。

$ firebase init hosting

この後は対話形式です。
ドキュメントに一連が載ってますのでご参考にしてください。

Next.js を統合する  |  Firebase Hosting

基本的にこの設定が有効になっていれば、NextjsのソースコードからgetStaticPropsやgetStaticPathsを読み取ってくれて、必要なCloudFuntionsを自動でデプロイしてくれます。

最後に

やっぱりデプロイとか開発環境と本番環境の差異って出ますよね。
いつもびびってます。
この件でNextのImageコンポーネントについて少しは知れたので良かったとします。
ここまで見てくださった方、ありがとうございました!

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング② NextjsのImageタグが便利

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

作成した静的サイトのパフォーマンスをチューニングします。
経緯ですが、話すと長いので箇条書きでまとめます。

  • 案件をいただき静的サイトを作成した
  • 一時的にデプロイし公開することが急務だった
  • 静的サイトに紐づくプロジェクトがそろそろアクティブになる

やばい!チューニングしないと!
ということで徐々にパフォーマンスが良くなるよう修正していきます。

使用した技術

Category     Technology Stack
Frontend Nextjs,React,Typescript
Backend Typescript,Node,CloudFunction
Database Firestore
Design   XD  

前回の内容

前回記事

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング① - zare926のブログ

要点をまとめると前回記事はLighthouseのスコアを上げるために、画像のサイズを適切なものに変換して、スコアを上げてみました。
画像圧縮しただけで簡単にスコアが上がりました。

今回の内容

画像を圧縮したものの、PC、タブレットスマホの画面サイズが多数存在するので、画像も1枚用意をするのではなく、画面に合わせて用意が必要だとLighthouseから指摘がありました。
実際に用意して実装してみました。
以下は例のコード

<img src="keyboard-800w.jpg"
    alt="A beautiful pink keyboard."
    width="400"
    height="400"
    srcset="keyboard-400w.jpg 400w,
            keyboard-800w.jpg 800w"
    sizes="(max-width: 640px) 400px,
            800px">

srcset属性

srcset 属性は、ブラウザーに対して複数の画像バリアント(異なるサイズや解像度の画像)を提供し、ブラウザーが適切な画像を選択するのを助けます。各画像バリアントは、画像ファイルとその幅の情報で構成されます。
ブラウザーは利用可能な表示領域に最適な画像を選択します。
簡単に言うと、使用する端末がどの程度Widthかを判断し、最適な画像を選んでくれます。

sizes属性

sizes 属性は、srcset 属性で指定された画像バリアントをどのように表示するかを示す指針を提供します。
これは、メディアクエリと画像の幅を組み合わせたものです。
ブラウザーは sizes 属性を使用して、利用可能な表示領域に基づいてどの画像を選択するかを決定します。
簡単に言うと、画像を表示をする際に、端末の画面サイズに合わせて表示方法を設定できます。

この2つを使用するということでした。
最初はちまちま画像を用意し、この二つの属性をせっせこ設定していましたが、あることに気がつきました。
NextjsにはImageコンポーネントがあるじゃないか!!

Imageコンポーネント

公式ドキュメント

Components: <Image> | Next.js

兎にも角にも、画像の最適化、srcsetの設定を自動的におこなってくれます!!!
sizesはレスポンシブなどもあるため、自分で決める必要があります。
生成されるコードを見るとsrcset属性が自動で付与されています。
設定によりますが、widthやheightも自動で確保してくれるので、場所取りも万全です。
この点はたくさん他の記事でも紹介されているので、以下にまとめておきます。

使い方まとめの参考サイト

Next.js の Image コンポーネントの使い方をまとめてみた | DevelopersIO

これでわかるNext.jsの画像最適化Imageコンポーネント | アールエフェクト

Next.js 13のImageコンポーネントの簡単な使い方とメリット

nextjsのversion12と13では使い方が変わっているのでその点も注意です。

Next.js v13のnext/imageの主要な変更点

Next.js 13 next/imageã§ãµã¤ãºæå®ããã«ã¢ã¹ãã¯ãæ¯ãä¿ã£ã¦è¡¨ç¤ºãã | Tech Q Lab - æ ªå¼ä¼ç¤¾ã­ã¥ã¼ã®ããã¯ãã­ã°

本題のスコアは?

今回別のページで変化を見てみました。
Before

After

最後に

画像の圧縮もしてくれるので、便利で簡単です!
ImageコンポーネントのStyleは理解しておくと怖いものなしですね!(主観)
ここまで見てくださった方、ありがとうございました!

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング①

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

作成した静的サイトのパフォーマンスをチューニングします。
経緯ですが、話すと長いので箇条書きでまとめます。

  • 案件をいただき静的サイトを作成した
  • 一時的にデプロイし公開することが急務だった
  • 静的サイトに紐づくプロジェクトがそろそろアクティブになる

やばい!チューニングしないと!
とかはどうでもいいですよね。進みましょう。

使用するもの

  • 作成したサイト
  • Lighthouse

作成したサイト

使用した技術

Category Technology Stack
Frontend Nextjs,React,Typescript
Backend Typescript,Node,CloudFunction
Database Firestore
Design XD

Lighthouse

Lighthouse - Chrome ウェブストア

ご存知かもしれませんが、Google Chrome拡張機能です。
1分ぐらいでDesktopとMobileのGoogle独自の基準でサイトのパフォーマンスを図ってくれます。
こいつの使い方をマスターして、なおかつスコアを上げることが今回の目的です。

気になるスコアは?

早速スコアを測ってみましょう。

驚愕しました。
何から手をつけていいのか・・・

まぁ赤く訴えてるところを1個づつ直そうと思い、Properly size images君に決めた!!
画像のサイズが重すぎるぞと、携帯電話のデータを考えてやれよと指摘が書かれていました。
また、Nextjsを使用していたので、Imageコンポーネントも進めてくれました。優秀。でも確かFirebaseでできなかったような・・・
最終的にSSGでデプロイしたいのでNextを駆使してFirebaseでデプロイするのは後々調べます。

改善させましょう

確かに、もらった画像のデータなどはそのままのクオリティで使用していた。
トップ画面横いっぱいに画像が表示されますが、その画像がなんと14MB
あかんて、スマホが熱くなるし、ガラケーだったら破裂します。

とりあえず、画像を圧縮するために見つけたImageOptimという圧縮ツールを使用しました。 ImageOptim — better Save for Web

良いと思った点は、設定ポイントがおそらく多い、あと古いデータは自動でゴミ箱に入るのでデータがすり替わってます。
ただし最低のクオリティへ変換しても1MBまでしか下がらなかった。
WebPとかにしたほうがいいのかな・・・?
とりあえず1MBで決行。
もう一枚3MBの画像があったので、そちらも圧縮し1MBになりました。
圧縮率にツッコミたかったですが、いいでしょう何か事情があるんでしょうね、解決しなくてはならないので、いずれ調べます。
ついでに意味があるかわかりませんが、react-lazyloadライブラリを使って、遅延読み込みも設定しました。

import LazyLoad from 'react-lazyload';

export const LazyLoadComponent = () => {
  return (
    <LazyLoad>
      <img src"imagePath" alt="img" />
    </LazyLoad>
  );
};

いざ決戦

雑いですが、何も知らないまま言われた通り使用している画像を圧縮し、意味があるかわからない遅延読み込みも伏兵で仕込んでおいた。
気になるスコアは!? 上がりすぎでしょ、ゲームの初期でもこんな上がりませんがな
わかったことは、画像のサイズには敏感になったほうがいいですね。
ちなみにこの結果はDesktopです。
Mobileだとまだまだ画像は重いみたいです。

きっと今はスマホで見ることが多いと思うので、改善してから世に出さないとですね・・・

最後に

最後じゃないんですが、こんな感じで何をしたらスコアが何をしてどう変化したか記録を残しつつパフォーマンスを改善していきたいと思います。

ここまで見てくださった方、ありがとうございました!

Docker Hubへのpushとpull(メモ)

Docker Hubへのpushとpull

push,pullとは

push・・・Docker Hubへ作成したイメージをアップロードすること。
pull・・・Docker Hubからイメージをダウンロードすること。

Docker Hubの登録

DockerHub
ここでアカウント作成
- DockerID - Email - Password 認証メールが届くので、認証して登録完了

Create a Repository

f:id:zare926:20220115073432p:plain ※2022/1/14時点のリポジトリを作成してない場合の画面
Create a Repositoryをクリック
f:id:zare926:20220115073428p:plain プルダウンのメニューはアカウントを選びます。
Nameはリポジトリの名前
Descriptionは説明書き
PublicとPrivateの選択ができますが、アカウント作成時に無料プランを選んでいる場合Privateは1つまでです。
作成後Privateのリポジトリを削除すれば別Privateリポジトリを作れます。

レジストリサーバーへのログイン

次にDocker Hubへログインをして、作成したイメージをDocker Hubへpushのやり方。

$ docker login

上記がログインコマンド

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username:登録したユーザー名を入力
Password:パスワードを入力 // 入力中は見えませんが、そのまま入力してください

Login Succeeded //これが出ればOK!

ログイン完了

Docker Hubにおけるタグ付ルール

<Docker ID>/<イメージ名>:<タグ名>
// タグ名は省略可能

上記の形式でタグ付けする必要あり
タグ名を省略すると自動でlatestになる。

まずはタグ付け
今回はdocker-whaleというイメージをpushするので、docker-whaleにタグ付けします。

$ docker tag docker-whale ユーザー名/docker-whale:ver1

Push!

次にPushコマンド

$ docker push <Docker ID>/<イメージ名>:<タグ名>

今回だとこのようなコマンドになる

$ docker push ユーザー名/docker-whale:ver1

The push refers to repository [docker.io/zare926/docker-whale]
f5b6ab1593d7: Pushed
5f70bf18a086: Mounted from docker/whalesay
d061ee1340ec: Mounted from docker/whalesay
d511ed9e12e1: Mounted from docker/whalesay
091abc5148e4: Mounted from docker/whalesay
b26122d57afa: Mounted from docker/whalesay
37ee47034d9b: Mounted from docker/whalesay
528c8710fd95: Mounted from docker/whalesay
1154ba695078: Mounted from docker/whalesay
ver1: digest: sha256:3a7d145a0d7c829585da330c031169048c246e9864585be6d6093a0411c2255f size: 2614

Pushされてました。 f:id:zare926:20220115073600p:plain

Pull!

一度ローカルから先程pushしたイメージを削除して、Docker Hubからpullします。

$ docker rmi -f イメージのID

あとはpushコマンドのpushをpullに変えるだけ

$ docker pull ユーザー名/docker-whale:ver1

ver1: Pulling from zare926/docker-whale
e190868d63f8: Already exists
909cd34c6fd7: Already exists
0b9bfabab7c1: Already exists
a3ed95caeb02: Already exists
00bf65475aba: Already exists
c57b6bcc83e3: Already exists
8978f6879e2f: Already exists
8eed3712d2cf: Already exists
24b8cff02913: Already exists
Digest: sha256:3a7d145a0d7c829585da330c031169048c246e9864585be6d6093a0411c2255f
Status: Downloaded newer image for zare926/docker-whale:ver1
docker.io/zare926/docker-whale:ver1

これで権限さえあればどのPCでも同じ環境を用意できるという状態にできるということ!

Docker ビルドについて(メモ)

Dockerイメージをビルドする

Dockerfile

Dockerイメージを作成するための指示書となる設定ファイル。
デフォルトでDockerfileという名前のファイルがイメージのビルドに使用される。
別名のDockerfileを使用することも可能。
Dockerビルドをした場合、Dockerfileに定義された内容を上から順位処理していき、最終的な状態の環境がDockerイメージとして保存される。

Dockerfileの作成

ターミナル

$ mkdir imagebuild && cd imagebuild && touch Dockerfile

作成したDockerfile

FROM docker/whalesay:latest

RUN apt-get -y update && apt-get install -y fortunes

CMD /usr/games/fortune | cowsay
  • FROM命令・・・イメージを作成する際に元となるイメージを指定する命令
    元のレイヤーの上に新しいレイヤーを重ねてカスタマイズしていくことが可能
  • RUN命令・・・イメージビルドの際に実行を命令するコマンド
    今回は引数にapt-get -y update && apt-get install -y fortunesをとり、fortunesをインストールするように命令している。
  • CMD命令・・・コンテナ起動時に行う命令
    今回は英文の格言をwhalesayにしゃべらせるというコマンド命令

Dockerfileからイメージをビルドする

$ docker build -t docker-whale .
  • build・・・イメージをビルドするサブコマンド
  • -t docker-whale・・・タグ名を指定している
    今回はdocker-whaleと名付けている
  • .・・・ビルドコンテキストの指定
    ビルドコンテキストとは
    イメージをビルドする際にDockerデーモンに送信をするOS側のディレクトリのことをビルドコンテキストという
$ docker build -t docker-whale .
[+] Building 30.7s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                   0.1s
 => => transferring dockerfile: 156B                                   0.0s
 => [internal] load .dockerignore                                      0.0s
 => => transferring context: 2B                                        0.0s
 => [internal] load metadata for docker.io/docker/whalesay:latest      0.0s
 => [1/2] FROM docker.io/docker/whalesay:latest                        0.3s
 => [2/2] RUN apt-get -y update && apt-get install -y fortunes        30.0s
 => exporting to image                                                 0.3s
 => => exporting layers                                                0.2s
 => => writing image sha256:3f2ef4559f8a54019fca50dd8b2904ba33bf26b87  0.0s
 => => naming to docker.io/library/docker-whale                        0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

ビルドキャッシュ

Dockerイメージをビルドした際に、イメージのレイヤーごとにビルド結果をキャッシュしている。
Dockerfileの内容が変更されていない場合はキャッシュを利用して、初回よりも早くビルドが完了する。
パッケージのインストールをする際に、update文などを入れて、ビルドの度最新のパッケージを取得する場合も、Dockerfileに変更がない場合はスルーされてしまう。
キャッシュを利用しない場合は、ビルドの際に--no-cacheを付け加えてビルドする。

$ docker build --no-cache -t docker-whale .

これでキャッシュを利用せずにビルドされます。

run!

$ docker run docker-whale
 _________________________________________
/ <Knghtbrd> It's a trackball for one     \
| <wichert> so it's not a rodent          |
| <wichert> it's a turd with a ball       |
| sticking out <wichert> which you fondle |
\ constantly                              /
 -----------------------------------------
    \
     \
      \
                    ##        .
              ## ## ##       ==
           ## ## ## ##      ===
       /""""""""""""""""___/ ===
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
       \______ o          __/
        \    \        __/
          \____\______/

fortunesの格言って結構ふざけてる