Dockerfile を使用したイメージのカスタマイズ イメージのカスタマイズとは、実際には各レイヤーに追加される構成とファイルをカスタマイズすることを意味します。変更、インストール、構築、操作の各レイヤーのコマンドをスクリプトに記述し、このスクリプトを使用してイメージを構築およびカスタマイズすることができれば、再現性の欠如、イメージ構築の透明性、ボリュームの問題はすべて解決されます。このスクリプトは Dockerfile です。 Dockerfile は、命令を含むテキスト ファイルです。各命令はレイヤーを構築するため、各命令の内容はレイヤーの構築方法を記述します。 ここでは、nginx イメージのカスタマイズを例に挙げ、Dockerfile を使用してカスタマイズします。 空のディレクトリにテキスト ファイルを作成し、Dockerfile という名前を付けます。 $ mkdir mynginx $ cd mynginx $ Dockerfile をタッチする その内容は次のとおりです。 nginxから RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html この Dockerfile は非常にシンプルで、たった 2 行です。関係する命令は FROM と RUN の 2 つです。 Dockerfile の手順の説明 FROMはベースイメージを指定します いわゆるカスタマイズされたイメージは、イメージに基づいてカスタマイズされる必要があります。 FROM はベースイメージを指定するため、Dockerfile では FROM は必須の命令であり、最初の命令である必要があります。 Docker Store には、nginx、redis、mongo、mysql など、そのまま使えるサービスイメージをはじめ、高品質な公式イメージが多数用意されています。また、node、openjdk、python など、さまざまな言語でアプリケーションを開発、構築、実行するのに便利なイメージも用意されています。最終的な目標に最も適した画像を見つけて、それをベース画像としてカスタマイズできます。 対応するサービスのイメージが見つからない場合は、公式イメージで、Ubuntu、Debian、CentOS などのより基本的なオペレーティング システム イメージも提供されています。これらのオペレーティング システムのソフトウェア ライブラリにより、より広い拡張スペースが提供されます。 既存のイメージをベースイメージとして選択することに加えて、Docker にはスクラッチと呼ばれる特別なイメージもあります。この画像は仮想概念であり、実際には存在しません。空白の画像を表します。 ゼロから ... スクラッチをベースイメージとして使用する場合、何らかのイメージをベースにしていないことを意味し、次に記述する命令はイメージの最初のレイヤーとして存在することになります。 swarm や coreos/etcd などのシステムに基づかずに、実行可能ファイルをイメージに直接コピーすることは珍しくありません。 Linux で静的にコンパイルされたプログラムの場合、オペレーティング システムがランタイム サポートを提供する必要はありません。必要なライブラリはすべて実行可能ファイルに既に含まれているため、最初から直接コンパイルするとイメージ サイズが小さくなります。 Go 言語を使用して開発された多くのアプリケーションは、この方法を使用してイメージを作成します。これが、Go がコンテナ マイクロサービス アーキテクチャに特に適した言語であると考える人がいる理由の 1 つです。 RUN コマンドを実行する RUN 命令は、コマンド ライン命令を実行するために使用されます。コマンドラインの強力な機能により、RUN 命令はイメージをカスタマイズするときに最もよく使用される命令の 1 つです。形式は 2 つあります。 シェル形式: RUN <コマンド> (コマンド ラインに直接入力したコマンドと同じです)。今書いた Dockerfile 内の RUN 命令はこの形式です。 RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html exec 形式: RUN ["実行可能ファイル", "パラメータ 1", "パラメータ 2"]。これは関数呼び出しの形式に似ています。 debian:jessie より apt-get updateを実行する apt-get install -y gcc libc6-dev makeを実行します。 wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" を実行します。 mkdir -p /usr/src/redis を実行します。 tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 を実行します。 make -C /usr/src/redis を実行します。 make -C /usr/src/redis install を実行します。 前述したように、Dockerfile 内の各命令はレイヤーを作成しますが、RUN も例外ではありません。各 RUN の動作は、手動でイメージを作成するプロセスと同じです。新しいレイヤーが作成され、そのレイヤー上でこれらのコマンドが実行され、実行が完了すると、このレイヤーへの変更がコミットされて新しいイメージが形成されます。 上記の書き込み方法では、7 層の画像が作成されます。これは全く意味がなく、コンパイル環境や更新されたソフトウェア パッケージなど、実行時に必要のないものが多数イメージにインストールされます。その結果、非常に肥大化した多層イメージが生成され、ビルドとデプロイメントにかかる時間が長くなるだけでなく、エラーも発生しやすくなります。 これは、多くの Docker 初心者が犯す一般的な間違いです (私も自分自身を許せませんε=(´ο`*))) 残念です)。 Union FS には最大レイヤー数があります。たとえば、AUFS の最大レイヤー数は 42 でしたが、現在は最大 127 です。 上記の Dockerfile を正しく記述する方法は次のとおりです。 debian:jessie より 実行 buildDeps='gcc libc6-dev make' \ && apt-get アップデート \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis をインストール \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps まず、これまでのコマンドの目的は、redis 実行可能ファイルをコンパイルしてインストールすることだけです。したがって、多くのレイヤーを構築する必要はなく、1 つのレイヤーだけで済みます。したがって、さまざまなコマンドに 1 つずつ対応する多数の RUN コマンドを使用する代わりに、1 つの RUN 命令のみが使用され、&& を使用して必要なコマンドが連結されます。以前の 7 つのレイヤーが 1 つのレイヤーに簡素化されました。 Dockerfile を書くときは、シェル スクリプトを書いているのではなく、各レイヤーの構築方法を定義していることを常に思い出してください。 また、ここでは書式設定のために改行が行われます。 Dockerfile は、行末に \ を追加する Shell のようなコマンドライン改行方法と、行先頭に # を追加するコメント形式をサポートしています。改行、インデント、コメントなどの適切な書式設定により、メンテナンスとトラブルシューティングが容易になるため、これは良い習慣です。 さらに、このコマンド セットの最後にクリーンアップ コマンドが追加されていることがわかります。このコマンドは、コンパイルとビルドに必要なソフトウェアを削除し、ダウンロードして展開したすべてのファイルをクリーンアップし、apt キャッシュ ファイルもクリーンアップします。これは非常に重要なステップです。前述のように、画像は複数のレイヤーに保存されます。各レイヤーの内容は次のレイヤーでは削除されず、常に画像に従います。したがって、イメージを構築するときは、各レイヤーに本当に必要なものだけが追加され、無関係なものはすべてクリーンアップされるようにする必要があります。 Docker を初めて使用する人の多くが非常に肥大化したイメージを作成する理由の 1 つは、ビルドの各レイヤーの最後に無関係なファイルをクリーンアップするのを忘れることです。 イメージを構築する さて、カスタマイズされた nginx イメージの Dockerfile に戻りましょう。 Dockerfile の内容がわかったので、このイメージをビルドしてみましょう。 Dockerfile ファイルがあるディレクトリで実行します。 nginx をビルドします。 ビルド コンテキストを Docker デーモンに送信しています 2.048 kB ステップ1: nginxから ---> e43d811ce2f4 ステップ 2: echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html を実行します。 ---> 9cdc27646c7b で実行中 ---> 44aa4490ce2c 中間コンテナ 9cdc27646c7b を削除しています 44aa4490ce2c の構築に成功しました コマンドの出力から、イメージ構築プロセスを明確に確認できます。ステップ 2 では、前述したように、RUN 命令がコンテナー 9cdc27646c7b を起動し、必要なコマンドを実行し、最後にこのレイヤー 44aa4490ce2c を送信し、使用済みのコンテナー 9cdc27646c7b を削除します。 ここでは、docker build コマンドを使用してイメージをビルドします。形式は次のとおりです。 docker build [オプション] <コンテキストパス/URL/-> ここでは、最終イメージの名前 -t nginx:v3 を指定します。ビルドが成功したら、このイメージを直接実行でき、その結果、ホームページが Hello, Docker! に変更されます。 イメージビルドコンテキスト よく見ると、docker build コマンドの最後に . があることがわかります。 . は現在のディレクトリを示しており、Dockerfile は現在のディレクトリ内にあるため、多くの初心者はこのパスが Dockerfile が配置されているパスを指定していると考えていますが、これは実際には正確ではありません。上記のコマンド形式に該当する場合、これがコンテキスト パスを指定していることがわかります。ではコンテキストとは何でしょうか? まず、docker build がどのように機能するかを理解する必要があります。 Docker は実行時に Docker エンジン (つまりサーバーデーモン) とクライアントツールに分かれています。 Docker エンジンは、DockerRemote API と呼ばれる一連の REST API を提供し、docker コマンドなどのクライアント ツールは、この一連の API を介して Docker エンジンと対話し、さまざまな機能を実行します。そのため、表面的にはローカルマシン上でさまざまな Docker 機能を実行しているように見えますが、実際にはすべてがリモート呼び出しを使用してサーバー側 (Docker エンジン) で実行されます。この C/S 設計により、リモート サーバーの Docker エンジンを簡単に操作できます。 イメージをビルドするときに、すべてのカスタマイズが RUN 命令によって実行されるわけではありません。多くの場合、COPY 命令、ADD 命令などを使用して、いくつかのローカル ファイルをイメージにコピーする必要があります。 docker build コマンドはイメージをビルドしますが、実際にはローカルではなく、サーバー側、つまり Docker エンジンでビルドされます。では、このクライアント/サーバー アーキテクチャでは、サーバーはどのようにしてローカル ファイルを取得できるのでしょうか? ここでコンテキストの概念が導入されます。ビルド時に、ユーザーはイメージ コンテキストをビルドするためのパスを指定します。docker build コマンドはこのパスを認識すると、パスの下にあるすべてのコンテンツをパッケージ化し、Docker エンジンにアップロードします。このように、Docker エンジンはこのコンテキスト パッケージを受け取った後、それを展開して、イメージの構築に必要なすべてのファイルを取得します。 Dockerfile に次のように記述します。 ./package.json /app/ をコピーします。 これは、docker build コマンドが実行されるディレクトリに package.json をコピーするという意味ではなく、Dockerfile が配置されているディレクトリに package.json をコピーするという意味ではなく、コンテキスト ディレクトリに package.json をコピーするという意味です。 したがって、COPY などの命令内のソース ファイル パスはすべて相対パスになります。初心者が COPY ../package.json /app または COPY /opt/xxxx /app が機能しない理由を尋ねるのはこのためです。これらのパスはコンテキストの範囲外であり、Docker エンジンはこれらの場所にあるファイルを取得できないためです。これらのファイルが本当に必要な場合は、コンテキスト ディレクトリにコピーする必要があります。 これで、コマンド docker build -t nginx:v3 . の . の意味が理解できました。これは実際にはコンテキスト ディレクトリを指定します。docker build コマンドは、ディレクトリの内容をパッケージ化し、イメージの構築を支援するために Docker エンジンに渡します。 docker build の出力を観察すると、実際にコンテキストを送信するこのプロセスを確認できます。 nginx をビルドします。 ビルド コンテキストを Docker デーモンに送信しています 2.048 kB ... イメージ構築で間違いを避けるためには、ビルド コンテキストを理解することが重要です。例えば、初心者の中には、COPY /opt/xxxx /app が機能しないことに気付いたため、Dockerfile をハードディスクのルートディレクトリに置いてビルドした人もいました。その結果、docker build を実行した後、数十 GB のファイルが送信され、非常に遅くなり、ビルドに失敗しやすくなりました。これは、このアプローチでは docker build にハードドライブ全体をパックするように要求しているためであり、明らかに使用上の誤りです。 一般的に、Dockerfile は空のディレクトリまたはプロジェクトのルート ディレクトリに配置する必要があります。このディレクトリに必要なファイルが存在しない場合は、コピーする必要があります。ビルド中に Docker エンジンに渡したくないものがディレクトリ内に存在する場合は、.gitignore と同じ構文を使用して .dockerignore ファイルを作成できます。このファイルは、コンテキストとして Docker エンジンに渡す必要のないものを削除するために使用されます。 では、なぜ一部の人は、 . が Dockerfile が配置されているディレクトリを指定すると誤解するのでしょうか?これは、デフォルトでは、Dockerfile を指定しない場合、コンテキスト ディレクトリ内の Dockerfile という名前のファイルが Dockerfile として使用されるためです。 これは単なるデフォルトの動作です。実際、Dockerfile ファイル名は Dockerfile である必要はなく、コンテキスト ディレクトリに配置する必要もありません。たとえば、-f ../Dockerfile.php パラメータを使用して、ファイルを Dockerfile として指定できます。 もちろん、通常はデフォルトのファイル名 Dockerfile を使用して、イメージビルドコンテキスト ディレクトリに配置します。 その他の docker ビルドの使用法 Gitリポジトリから直接ビルドする お気づきかもしれませんが、docker build は URL からのビルドもサポートしています。たとえば、Git リポジトリから直接ビルドできます。 $ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14 docker ビルド https://github.com/twang2218/gitlab-ce-zh.git\#:8.14 ビルド コンテキストを Docker デーモンに送信しています 2.048 kB ステップ 1: gitlab/gitlab-ce:8.14.0-ce.0 から 8.14.0-ce.0: gitlab/gitlab-ce からプル aed15891ba52: すでに存在します 773ae8583d14: すでに存在します ... このコマンド ラインは、ビルドに必要な Git リポジトリを指定し、デフォルトのマスター ブランチとビルド ディレクトリを /8.14/ として指定します。次に、Docker はプロジェクトを git clone し、指定されたブランチに切り替えて、指定されたディレクトリに入り、ビルドを開始します。 指定されたtarballでビルドする $ docker build http://server/context.tar.gz 指定された URL が Git リポジトリではなく tarball である場合、Docker エンジンは tarball をダウンロードし、自動的に解凍して、ビルドを開始するためのコンテキストとして使用します。 標準入力からDockerfileを読み込んでビルドする docker ビルド - < Dockerfile または cat Dockerfile | docker ビルド - テキストファイルが標準入力として渡されると、Dockerfile として扱われ、ビルドが開始されます。この形式では、Dockerfile の内容を標準入力から直接読み取るため、コンテキストがないため、他の方法のようにローカル ファイルをイメージに COPY することはできません。 ビルドのために標準入力からコンテキストtarballを読み込む $ docker build -<context.tar.gz 標準入力ファイル形式が gzip、bzip2、または xz であることが判明した場合、コンテキスト圧縮パッケージとして扱われ、直接展開されてビルドを開始するコンテキストとして扱われます。 COPY コピーファイル<br /> フォーマット:
RUN 命令と同様に、コマンド ラインに似た形式と関数呼び出しに似た形式の 2 つがあります。 COPY 命令は、ビルド コンテキスト ディレクトリの <ソース パス> にあるファイル/ディレクトリを、イメージの新しいレイヤーの <宛先パス> の場所にコピーします。例えば: package.json を /usr/src/app/ にコピーします。 <ソース パス> は複数指定することも、ワイルドカードを使用することもできます。ワイルドカードのルールは、次のような Go の filepath.Match ルールを満たす必要があります。 コピー hom* /mydir/ hom?.txt /mydir/ をコピー <ターゲット パス> は、コンテナー内の絶対パスまたは作業ディレクトリへの相対パスにすることができます (作業ディレクトリは WORKDIR 命令を使用して指定できます)。ターゲットパスは事前に作成する必要はありません。ディレクトリが存在しない場合は、ファイルをコピーする前に作成されます。 また、COPY コマンドを使用する場合、ソース ファイルのさまざまなメタデータが保持されることにも注意が必要です。たとえば、読み取り、書き込み、実行権限、ファイルの変更時間など。この機能は画像のカスタマイズに役立ちます。特にビルド関連のファイルが Git を使用して管理されている場合。 より高度なファイルコピーを追加 ADD 命令と COPY 命令の形式とプロパティは基本的に同じです。ただし、COPY に基づいていくつかの機能が追加されます。たとえば、<source path> は URL になります。この場合、Docker エンジンはこのリンクのファイルをダウンロードし、<destination path> に配置しようとします。ダウンロード後のファイル権限は自動的に 600 に設定されます。これが希望する権限でない場合は、権限を調整するために RUN の追加レイヤーを追加する必要があります。また、ダウンロードしたファイルが圧縮パッケージであり、解凍する必要がある場合は、解凍するために RUN 命令の追加レイヤーも必要です。したがって、RUN コマンドを直接使用し、次に wget または curl ツールを使用してダウンロードし、権限を処理し、解凍し、不要なファイルをクリーンアップする方が合理的です。したがって、この機能は実用的ではなく、推奨されません。 <ソース パス> が tar 圧縮ファイルであり、圧縮形式が gzip、bzip2、または xz の場合、ADD コマンドは圧縮ファイルを <宛先パス> に自動的に解凍します。 場合によっては、この自動解凍機能は非常に便利です。たとえば、Ubuntu の公式イメージの場合などです。 ゼロから ubuntu-xenial-core-cloudimg-amd64-root.tar.gz を追加します / ... しかし、場合によっては、圧縮されたファイルを解凍せずにコピーしたい場合、ADD コマンドを使用できません。 Docker の公式 Dockerfile ベスト プラクティス ドキュメントでは、可能な限り COPY を使用することが義務付けられています。これは、COPY のセマンティクスが非常に明確で、単にファイルをコピーするだけであるのに対し、ADD にはより複雑な機能が含まれており、その動作があまり明確でない可能性があるためです。 ADD を使用するのに最も適した状況は、自動解凍が必要な場合です。 また、ADD 命令はイメージ ビルド キャッシュを無効にするため、イメージ ビルドが遅くなる可能性があることに注意してください。 したがって、COPY 命令と ADD 命令のどちらかを選択するときは、すべてのファイル コピーに COPY 命令を使用し、自動解凍が必要な場合にのみ ADD を使用するという原則に従うことができます。 CMD コンテナ起動コマンド CMD 命令の形式は RUN の形式と似ており、次の 2 つの形式があります。
以前コンテナを紹介したとき、Docker は仮想マシンではなく、コンテナはプロセスであると述べました。プロセスなので、コンテナを起動する際には実行するプログラムとパラメータを指定する必要があります。 CMD 命令は、デフォルトのコンテナ メイン プロセスの起動コマンドを指定するために使用されます。 実行時に、イメージ設定のデフォルト コマンドを置き換える新しいコマンドを指定できます。たとえば、ubuntu イメージのデフォルトの CMD は /bin/bash です。docker run -it ubuntu を直接実行すると、bash が直接入力されます。 docker run -it ubuntu cat /etc/os-release など、実行時に実行する他のコマンドを指定することもできます。これは、デフォルトの /bin/bash コマンドを、システムのバージョン情報を出力する cat /etc/os-release コマンドに置き換えます。 命令形式に関しては、通常は exec 形式を使用することをお勧めします。この形式は解析時に JSON 配列として解析されるため、一重引用符ではなく二重引用符 " を使用するようにしてください。 シェル形式を使用する場合、実際のコマンドは sh -c のパラメータとしてパッケージ化され、実行されます。例えば: CMDエコー$HOME 実際の実装では、次のように変更されます。 CMD [ "sh", "-c", "echo $HOME" ] 環境変数を使用できるのは、これらの環境変数がシェルによって解析されるためです。 CMD について話すとき、コンテナ内のアプリケーションのフォアグラウンドおよびバックグラウンド実行の問題について言及する必要があります。これは初心者によくある混乱です。 Docker は仮想マシンではありません。コンテナ内のアプリケーションは、仮想マシンや物理マシンのように upstart/systemd を使用してバックグラウンド サービスを開始するのではなく、フォアグラウンドで実行する必要があります。コンテナにはバックグラウンド サービスの概念はありません。 初心者は通常、CMD を次のように記述します。 CMD サービス nginx 開始 すると、コンテナは実行後すぐに終了することがわかりました。コンテナ内でsystemctlコマンドを使用しても、全く実行できないことが判明しました。これは、フォアグラウンドとバックグラウンドの概念を理解しておらず、コンテナと仮想マシンの違いを区別しておらず、従来の仮想マシンの観点からコンテナを理解しているためです。 コンテナの場合、その起動プログラムはコンテナ アプリケーション プロセスです。コンテナはメイン プロセスのために存在します。メイン プロセスが終了すると、コンテナは存在する意味を失い、終了します。その他の補助プロセスは、コンテナが気にする必要はありません。 service nginx start コマンドを使用する場合、systemd は nginx サービスをバックグラウンド デーモン プロセスとして起動します。前述のように、CMD service nginx start は CMD ["sh", "-c", "service nginxstart"] として理解されるため、メイン プロセスは実際には sh です。そして、service nginx start コマンドが終了すると、sh も終了します。メインプロセスである sh が終了すると、コンテナも自然に終了します。 正しい方法は、nginx 実行可能ファイルを直接実行し、フォアグラウンドで実行するように要求することです。例えば: CMD ["nginx", "-g", "デーモンオフ;"] エントリーポイント ENTRYPOINT の形式は RUN 命令の形式と同じで、exec 形式とシェル形式に分かれています。 ENTRYPOINT の目的は CMD と同じで、コンテナの起動プログラムとパラメータを指定することです。 ENTRYPOINT も実行時に置き換えることができますが、CMD よりも少し複雑で、docker run パラメータ –entrypoint を通じて指定する必要があります。 ENTRYPOINT を指定すると、CMD の意味が変わります。コマンドを直接実行するのではなく、CMD の内容を ENTRYPOINT 命令のパラメータとして渡します。つまり、実際に実行すると次のようになります。 <ENTRYPOINT> "<CMD>" では、CMD があるのに、なぜ ENTRYPOINT が必要なのでしょうか?この <ENTRYPOINT> "<CMD>" には何か利点がありますか?いくつかのシナリオを見てみましょう。 シナリオ1: 画像をコマンドのように使う 現在のパブリック IP を知るためにミラーが必要であると仮定すると、まず CMD を使用してそれを実現します。 ubuntu:16.04より apt-get update を実行する\ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* コマンド [ "curl", "-s", "http://ip.cn" ] docker build -t myip . を使用してイメージをビルドする場合、現在のパブリック IP を照会する必要がある場合は、次のコマンドを実行するだけで済みます。 $ docker 実行 myip 現在の IP: 61.148.226.66 提供元: 北京聯通 そうですね、イメージを直接コマンドとして使用できるようですが、コマンドには常にパラメータがあります。パラメータを追加したい場合はどうすればよいでしょうか?たとえば、上記の CMD から、実際のコマンドは curl であることがわかります。HTTP ヘッダー情報を表示する場合は、-i パラメータを追加する必要があります。では、docker run myip に -i パラメータを追加するだけでいいのでしょうか? $ docker run myip -i docker: デーモンからのエラー応答: 無効なヘッダーフィールド値 "oci ランタイムエラー: con tainer_linux.go:247: コンテナ プロセスの開始により \"exec: \\\"-i\\\": 実行可能になりました $PATH\"\n" にファイルが見つかりません。 実行可能ファイルが見つからない、実行可能ファイルが見つからないというエラー メッセージが表示されます。前に述べたように、コマンドはイメージ名の後に続き、実行時に CMD のデフォルト値を置き換えます。したがって、ここでの -i は、元の curl -s http://ip.cn の後に追加されるのではなく、元の CMD を置き換えます。また、-i はコマンドではないため、見つかりません。 -i パラメータを追加する場合は、コマンド全体を再入力する必要があります。 $ docker run myip curl -s http://ip.cn -i これは明らかに良い解決策ではありませんが、ENTRYPOINT を使用するとこの問題を解決できます。ここで、このイメージを実装するために ENTRYPOINT を再度使用します。 ubuntu:16.04より apt-get update を実行する\ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* エントリポイント [ "curl", "-s", "http://ip.cn" ] 今回は、docker run myip -i を直接使用してみます。 $ docker 実行 myip 現在の IP: 61.148.226.66 提供元: 北京聯通 $ docker run myip -i HTTP/1.1 200 OK サーバー: nginx/1.8.0 日付: 2016 年 11 月 22 日 (火) 05:12:40 GMT コンテンツタイプ: text/html; 文字セット=UTF-8 変化: Accept-Encoding X 搭載: PHP/5.6.24-1~dotdeb+7.1 X-Cache: キャッシュ 2 からの MISS X-Cache-Lookup: キャッシュからの MISS - 2:80 X-Cache: proxy-2_6 からの MISS 転送エンコーディング: チャンク 経由: 1.1 cache-2:80、1.1 proxy-2_6:8006 接続: キープアライブ 現在の IP: 61.148.226.66 提供元: 北京聯通 ご覧の通り、今回は成功しました。これは、ENTRYPOINT が存在する場合、CMD の内容が ENTRYPOINT にパラメータとして渡され、ここで -i が新しい CMD であるため、curl にパラメータとして渡され、目的の効果が得られるためです。 シナリオ 2: アプリケーションを実行する前の準備 コンテナを起動するとメインプロセスが開始されますが、メインプロセスを開始する前に何らかの準備が必要になる場合があります。たとえば、MySQL のようなデータベースでは、最終的な MySQL サーバーを実行する前に完了する必要があるデータベース構成と初期化の作業が必要になる場合があります。 また、セキュリティを向上させるために、サービスの開始に root ユーザーを使用しないようにすることもできます。サービスを開始する前に、root ユーザーとして必要な準備をいくつか実行し、最後にサービス ユーザーに切り替えてサービスを開始する必要があります。あるいは、サービスに加えて、デバッグなどを容易にするために、ルート ID を使用して他のコマンドを実行することもできます。 これらの準備はコンテナの CMD とは関係ありません。CMD が何であっても、事前に前処理タスクを実行する必要があります。この場合、スクリプトを記述して ENTRYPOINT に配置して実行することができます。スクリプトは、パラメータ (つまり、) をコマンドとして受け取り、スクリプトの最後にそれらを実行します。たとえば、公式の redis イメージでは次のように実行されます。 アルパインから:3.4 ... 実行 addgroup -S redis && adduser -S -G redis redis ... エントリポイント ["docker-entrypoint.sh"] エクスポーズ6379 CMD [ "redis-server" ] redis サービス用に redis ユーザーが作成され、最後に dockerentrypoint.sh スクリプトとして ENTRYPOINT が指定されていることがわかります。 #!/bin/sh ... # コンテナを `--user` で起動できるようにする [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]の場合; chown -R redis 。 exec su-exec redis "$0" "$@" フィ 実行 "$@" スクリプトの内容は、CMD の内容に基づいて決定されます。redis-server の場合は、redis ユーザー ID に切り替えてサーバーを起動し、それ以外の場合は、引き続き root ID を使用して実行します。例えば: $ docker run -it redis id uid=0(ルート) gid=0(ルート) グループ=0(ルート) ENVは環境変数を設定します 形式は 2 つあります。
この命令は非常にシンプルで、環境変数を設定するだけです。RUN などの後続の命令でも、実行時のアプリケーションでも、ここで定義された環境変数を直接使用できます。 環境変数 VERSION=1.0 DEBUG=オン \ NAME="ハッピー フィート" この例では、行を折り返し、スペースを含む値を二重引用符で囲む方法を示しています。これは、シェルでの動作と一致しています。 環境変数が定義されると、後続の命令で使用できるようになります。たとえば、公式のノード イメージの Dockerfile には、次のようなコードがあります。 環境変数 NODE_VERSION 7.2.0 curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.ta を実行します。 r.xz" \ && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \ && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs ここでは、最初に環境変数 NODE_VERSION が定義され、次に RUN レイヤーで $NODE_VERSION が複数回使用され、操作がカスタマイズされます。ご覧のとおり、将来イメージビルドバージョンをアップグレードする場合は、7.2.0 にアップデートするだけで済み、Dockerfile ビルドのメンテナンスが容易になります。 次のディレクティブは環境変数の拡張をサポートします。 このコマンドリストから、環境変数は多くの場所で使用でき、非常に強力であることが実感できます。環境変数を使用すると、1 つの Dockerfile を使用して、異なる環境変数を使用してより多くのイメージを作成できます。 ARGビルドパラメータ 形式: ARG <パラメータ名>[=<デフォルト値>] ビルド パラメーターは ENV と同じ効果があり、どちらも環境変数を設定します。違いは、ARG によって設定されたビルド環境の環境変数は、コンテナが将来実行されるときには存在しないことです。ただし、docker history では引き続きすべての値が表示されるため、パスワードなどの情報を保存する場合は ARG を使用しないでください。 Dockerfile 内の ARG 命令は、パラメータ名とそのデフォルト値を定義します。このデフォルト値は、ビルド コマンド docker build で --build-arg <parameter name>=<value> を使用して上書きできます。 1.13 より前のバージョンでは、--build-arg のパラメータ名は、ARG を使用して Dockerfile で定義されている必要があります。つまり、--build-arg で指定されたパラメータは、Dockerfile で使用する必要があります。対応するパラメータが使用されない場合は、エラーが報告され、ビルドは終了します。 1.13 以降では、この厳しい制限が緩和されました。エラーで終了する代わりに、警告メッセージが表示され、ビルドが続行されます。これは、CI システムを使用して同じビルド プロセスで異なる Dockerfile をビルドする場合に役立ち、各 Dockerfile の内容に基づいてビルド コマンドを変更する必要がなくなります。 VOLUMEは匿名ボリュームを定義します 形式は次のとおりです。
前に述べたように、コンテナの実行中は、コンテナ ストレージ レイヤーへの書き込み操作を可能な限り行わないようにする必要があります。動的なデータを保存する必要があるデータベース アプリケーションの場合、データベース ファイルはボリュームに保存する必要があります。実行時に動的ファイルが格納されているディレクトリをボリュームとしてマウントするのを忘れないように、Dockerfile で特定のディレクトリを匿名ボリュームとしてマウントするように事前に指定することができます。これにより、実行時にユーザーがマウントを指定しなくても、コンテナのストレージ層に大量のデータを書き込むことなく、アプリケーションを正常に実行することもできます。 ボリューム/データ ここでの /data ディレクトリは実行時に匿名ボリュームとして自動的にマウントされ、/data に書き込まれた情報はコンテナ ストレージ レイヤーに記録されないため、コンテナ ストレージ レイヤーのステートレス性が保証されます。もちろん、このマウント設定は実行時に上書きできます。例えば: docker run -d -v mydata:/data xxxx このコマンド ラインでは、名前付きボリューム mydata が /data の場所にマウントされ、Dockerfile で定義された匿名ボリュームのマウント構成が置き換えられます。 EXPOSEはポートを宣言する 形式は EXPOSE <port1> [<port2>...] です。 EXPOSE 命令は、ランタイム コンテナーによって提供されるサービス ポートを宣言します。これは単なる宣言であり、この宣言により、アプリケーションは実行時にこのポートのサービスを開きません。 Dockerfile にこのようなステートメントを記述することには、2 つの利点があります。1 つは、イメージ ユーザーがイメージ サービスのデーモン ポートを理解し、構成マッピングを容易にできるようにすることです。もう 1 つの利点は、実行時にランダム ポート マッピングを使用する場合、つまり docker run -P を使用する場合、EXPOSE ポートが自動的にランダムにマッピングされることです。 さらに、初期の Docker バージョンでは特別な使用法があります。以前は、すべてのコンテナがデフォルトのブリッジ ネットワークで実行されていたため、すべてのコンテナが互いに直接アクセスでき、特定のセキュリティ上の問題が発生していました。そのため、Docker エンジン パラメータ --icc=false があります。このパラメータが指定されると、コンテナが --links パラメータを使用して相互に通信しない限り、コンテナはデフォルトで相互にアクセスできなくなり、イメージ内の EXPOSE によって宣言されたポートのみにアクセスできるようになります。 --icc=false の使用は、docker ネットワークの導入後は基本的に使用されなくなりました。カスタム ネットワークを使用すると、コンテナ間の相互接続と分離を簡単に実現できます。 露出を-p <ホストポート>:<コンテナポート>を使用することと区別するには、実行時に。 -pホストポートとコンテナポートをマップします。 WorkDir作業ディレクトリを指定します 形式はworkdir <workingディレクトリパス>です。 WorkDirコマンドを使用して、各レベルの現在のディレクトリを指定します。 前述のように、一部の初心者が発した間違いは、この誤解が次のエラーにつながるかのようにDockerFileを書くことです。 cd /app を実行 Echo "Hello"> world.txtを実行します このdockerfileを使用して画像を作成して実行すると、 /app/world.txtファイルが見つからないか、そのコンテンツが邪魔されないことがわかります。理由は実際には非常に単純です。2つの連続したラインは同じプロセス実行環境であるため、次のコマンドに直接影響します。これは、DockerFileの階層化されたストレージの概念を理解しないことが原因となるエラーです。 前述のように、各実行はコンテナを起動し、コマンドを実行し、ストレージレイヤーファイルの変更をコミットします。最初のレベルのRUNCD /APPの実行は、現在のプロセスの作業ディレクトリのみ、メモリの変更のみを変更し、ファイルの変更にはなりません。 2番目のレイヤーに関しては、新しい容器が起動します。これは、最初の層のコンテナとは関係ありません。 したがって、各レベルで作業ディレクトリの場所を変更する必要がある場合は、WorkDir命令を使用する必要があります。 ユーザーは現在のユーザーを指定します フォーマット:user <UserName> ユーザー指令は、環境の状態を変更し、後続のレイヤーに影響を与えるという点で、WorkDirに似ています。 WorkDirはワーキングディレクトリを変更し、ユーザーは実行、CMD、エントリポイントなどのコマンドを実行する後続のレイヤーのIDを変更します。もちろん、WorkDirと同様に、ユーザーは指定されたユーザーに切り替えるのに役立ちます。そうしないと、スイッチを作成できません。 実行 groupadd -r redis && useradd -r -g redis redis ユーザーredis run ["redis-server"] たとえば、実行中にルートとして実行されたスクリプトのIDを変更する場合、たとえば、確立されたユーザーとしてサービスプロセスを実行する場合は、より複雑な構成を必要とし、TTYのない環境で故障するSUまたはSUDOを使用しないでください。 Gosuを使用することをお勧めします。 #Redisユーザーを作成し、GOSUを使用して別のユーザーに変更してコマンドRun GroupAdd -R Redis && useradd -r -g Redis Redisを実行します #ゴスをダウンロードしてください wget -o/usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/ gosu-amd64 "\ && chmod +x/usr/local/bin/gosu \ && gosu誰も本当です #cmdを設定して、cmdを別のユーザーとして実行します["exec"、 "gosu"、 "redis"、 "redis-server"]] ヘルスチェック 形式:
HealthCheckディレクティブは、DockerのステータスがDocker 1.12で導入された新しい指令であるかどうかをDockerに説明します。 HealthCheck命令が利用できない前に、Dockerエンジンは、コンテナの主要なプロセスが出るかどうかによって、コンテナが異常であるかどうかのみを判断できます。多くの場合、これは問題ありませんが、プログラムがデッドロック状態またはデッドループ状態に入る場合、申請プロセスは終了しませんが、コンテナはもはやサービスを提供できません。 1.12より前に、Dockerはこのコンテナの状態を検出せず、したがってスケジュールを変更せず、サービスを提供できなくなったがユーザーリクエストを受け入れていたコンテナが再スケジュールされました。 1.12以来、DockerはHealthCheck命令を提供します。これは、このコマンドを介したコマンドの行を指定し、このコマンドラインを使用して、コンテナのメインプロセスのサービスステータスがまだ正常かどうかを判断し、それによってコンテナの実際のステータスを比較します。 HealthCheckの指示が鏡で指定されている場合、HealthCheckの命令が正常にチェックされた後、開始されます。 HealthCheckは次のオプションをサポートしています。
CMDやEntryPointと同様に、HealthCheckは1回しか表示されません。 HealthCheck [Options] CMDの後のコマンドは、EntryPointと同様に、シェル形式とExec形式に分割されます。コマンドの返品は、ヘルスチェックが成功したかどうかを決定します。 最もシンプルなWebサービスであるミラーがあります。 nginxから apt -get update && apt -get install -y curl && rm -rf/var/lib/apt/lists/*を実行する HealthCheck - interval = 5s -timeout = 3s \ cmd curl -fs http:// localhost/|| ここでは、5秒ごとにチェックを設定します(ここでの間隔は、実際には比較的長くなるはずです)。 Dockerビルドを使用して、この画像を構築します。 $ docker build -t myweb:v1。 構築後、コンテナを開始します。 $ docker run -d -name web -p 80:80 myweb:v1 画像を実行した後、DockerコンテナLSを介して(Health:Starting)としての初期ステータスを確認できます。 $ dockerコンテナls コンテナID イメージ コマンド 作成ステータス ポート名 03e28eb00bd0 myweb:v1 "nginx -g '' 'daemon off" 3秒前に2秒(健康:開始)80/TCP、443/TCP Web 数秒間待った後、DockerコンテナLSを再びLSを使用すると、健康状態が(健康)に変化することがわかります。 $ dockerコンテナls コンテナID イメージ コマンド 作成ステータス ポート名 03e28eb00bd0 myweb:v1 "nginx -g '' 'daemon off" 18秒前(健康:健康)80/TCP、443/TCP Web 健康チェックが再試行回数を超えて継続的に失敗した場合、ステータスは(不健康)になります。 トラブルシューティングを支援するために、ヘルスチェックコマンド(STDOUTおよびSTDERRを含む)の出力は健康状態に保存され、Docker Inspectを使用して表示できます。 $ docker Inspect - format '{json .state.health}}' upbeat_allen | { 「FailingsStreak」:0、 「ログ」:[ { 「終了」: "2018-06-14T04:55:37.477730277-04:00"、 「exitcode」:0 "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", 「Start」: "2018-06-14T04:55:37.408045977-04:00" }, { 「終了」:「2018-06-14T04:55:42.553816257-04:00」、 「exitcode」:0 "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", 「Start」: "2018-06-14T04:55:42.480940888-04:00" }, { 「終了」: "2018-06-14T04:55:47.631694051-04:00"、 「exitcode」:0 "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", 「Start」: "2018-06-14T04:55:47.557214953-04:00" }, { "end": "2018-06-14T04:55:52.708195002-04:00"、 「exitcode」:0 "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", 「Start」: "2018-06-14T04:55:52.63499573-04:00" }, { "end": "2018-06-14T04:55:57.795117794-04:00"、 「exitcode」:0 "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", 「Start」: "2018-06-14T04:55:57.714289056-04:00" } ]、 「ステータス」:「健康」 } onbuildは他の人のためにウェディングドレスを作ります 形式:onbuild <その他の指示>。 OnBuildは特別な指示であり、その後に実行、コピーなどの他の指示が続き、これらの指示は現在の画像構造中に実行されません。画像が現在の画像に基づいており、次のレベルの画像が構築されている場合にのみ実行されます。 DockerFileのその他の指示は、現在の画像をカスタマイズするために準備されていますが、他の人が自分自身をカスタマイズするのを支援するためにonbuildのみが用意されています。 node.jsによって書かれたアプリケーションのミラーを作成すると仮定します。 node.jsはパッケージ管理にNPMを使用しており、すべての依存関係、構成、起動情報などがpackage.jsonファイルに配置されることを知っています。プログラムコードを取得した後、最初にNPMインストールを実行して、必要なすべての依存関係を取得する必要があります。次に、NPMスタートからアプリケーションを開始できます。したがって、一般的に言えば、dockerfileは次のように書かれています。 ノードから:スリム MKDIR /アプリを実行します ワークディレクトリ /app コピー./package.json /app 実行["npm"、 "install"] コピー ./app/ CMD [ "npm", "start" ] このdockerfileをnode.jsプロジェクトのルートディレクトリに配置し、画像を構築した後、コンテナの実行を開始するために直接使用できます。しかし、2番目のnode.jsプロジェクトがある場合、それはほぼ同じですか? OK、このDockerFileを2番目のプロジェクトにコピーします。 3番目のプロジェクトがある場合はどうなりますか?もう一度コピーしますか?ファイルのコピーが多いほど、そのようなシナリオのメンテナンスの問題を引き続き調べてみましょう。 開発プロセス中に最初のnode.jsプロジェクトが発見された場合、このDockerFileにはタイプミスや追加のパッケージなどの問題があることがわかります。最初のプロジェクトは問題ありませんが、2番目のプロジェクトはどうですか? Dockerfileは元々最初のプロジェクトからコピーされ、貼り付けられていましたが、最初のプロジェクトが最初のプロジェクトであり、2番目のプロジェクトのDockerfileが自動的に修復されるため、DockerFileは修復されません。 それでは、基本的な画像を作成して、各プロジェクトにこの基本的な画像を使用できますか?このようにして、基本的な画像が更新され、各プロジェクトはDockerFileの変更を同期する必要がありません。 OK、OK、結果が何であるか見てみましょう。その後、上記のdockerfileは次のようになります。 ノードから:スリム MKDIR /アプリを実行します ワークディレクトリ /app CMD [ "npm", "start" ] ここでは、プロジェクト関連の建設指示を取り出して、それらをサブプロジェクトに入れます。この基本的な画像の名前がmyNodeであると仮定すると、各プロジェクトのDockerFileは次のようになります。 my-nodeから コピー./package.json /app 実行["npm"、 "install"] コピー ./app/ 基本的な画像が変更された後、各プロジェクトはこのDockerFileを使用して画像を再構築し、基本画像の更新を継承します。 それで、問題は解決されましたか?いいえ。正確には、半分しか解決しませんでした。このDockerFileに調整する必要があるものがある場合はどうなりますか?たとえば、NPMインストールにはいくつかのパラメーターを追加する必要があるため、どうすればよいですか?現在のプロジェクトの./package.jsonが含まれるため、このラインを基本的な画像に導くことは不可能です。したがって、この方法で基本的な画像を作成すると、元のDockerFileの最初の4つの命令の変更の問題が解決するだけで、次の3つの命令の変更は完全に対処することができません。 OnBuildはこの問題を解決できます。 onbuildを使用して、基本的な画像のdockerfileを書き直しましょう。 ノードから:スリム MKDIR /アプリを実行します ワークディレクトリ /app onbuild copy ./package.json /app onbuild run ["npm"、 "install"] onbuildコピー。 CMD [ "npm", "start" ] 今回は元のDockerFileに戻りますが、今回はプロジェクト関連の手順をOnBuildに追加して、基本的な画像を構築するときにこれらの3つの行が実行されないようにします。その後、各プロジェクトのDockerFileは単純になります。 my-nodeから はい、1行しかありません。この1行のDockerFileを使用して各プロジェクトディレクトリに画像を作成すると、以前の基本画像の3行のOnBuildが実行され、現在のプロジェクトのコードを画像にコピーし、このプロジェクトのNPMインストールを実行してアプリケーションイメージを生成します。 参照:https://github.com/yeasy/docker_practice 以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: SSMは、mysqlデータベースアカウントのパスワード暗号文ログイン機能を実装します。
>>: Node.js で簡単なクローラーケースを作成するチュートリアル
具体的な方法は以下の通りです。 CSSコードコードをコピーコードは次のとおりです。 .wap_bot...
コードをコピーコードは次のとおりです。 <div スタイル="幅:630px;高さ:...
この記事では、シンプルなカルーセル効果を実現するためのjsの具体的なコードを参考までに紹介します。具...
環境: MacOS_Cetalina_10.15.1、Mysql8.0.18、Docker_2.0....
目次説明する:要約する補充するDOM を直接変更して操作する js や jQuery とは異なり、V...
最近、プロジェクトをアップグレードするために Docker を使用しました。これまで使用したことがな...
目次1. 順番に紹介する2. ユーザーを作成する3. ユーザーアカウントを削除する4. アクセス権5...
基本概念絶対配置: 絶対配置に設定された要素ボックスはドキュメント フローから完全に削除され、その包...
画像をダウンロードMySQLイメージの選択 docker 検索 mysql MySQL 5.7 イメ...
目次1. ブロックスコープとは何ですか? 2. ブロックスコープが必要なのはなぜですか? 3. 関数...
私のコンピューターのグラフィック カードは Nvidia グラフィック カードです。再起動後、画面に...
多くの友人は、フロントエンドを学習するときに、ボックス モデルがデフォルトで正方形であることに気付き...
1. 事件の背景:仕事上、Ubuntu への vscode リモート接続を使用する必要があります。 ...
導入Binlog ログ、つまりバイナリ ログ ファイルは、データベースに対するユーザー操作の SQL...
目次導入複数の異なるハッシュを区別するハッシュチャンクハッシュコンテンツハッシュjs キャッシュの実...