Docker のインストールと開始について 1 つの記事で学びましょう

Docker を 1 つの記事で詳しく説明

ドッカーとは何ですか?

Docker は、アプリケーションのデプロイ、実行、管理のプロセスを簡素化するように設計されたオープンソースのコンテナ化プラットフォームです。これはコンテナ化テクノロジーに基づいており、アプリケーションとその依存関係を独立したポータブルなコンテナーにパッケージ化できるため、アプリケーションをさまざまな環境で一貫して実行できるようになります。

コンテナーは、アプリケーションと、その実行に必要なすべてのソフトウェア、ライブラリ、環境変数、構成ファイルを含む軽量の自己完結型の実行単位です。Docker を使用すると、アプリケーションとその依存関係をコンテナー イメージにパッケージ化し、基盤となる環境の違いを気にすることなく、Docker をサポートする任意の環境にこのコンテナー イメージをデプロイして実行できます。

Docker は、コンテナーを作成、管理、実行するための一連のコマンドライン ツールと API を提供します。Linux Containers (LXC) などのオペレーティング システム層の仮想化テクノロジ、またはコンテナ エンジン (containerd) やコンテナ ランタイム (コンテナ ランタイム) などの最近のオープン ソース テクノロジを使用して、効率的なコンテナ化を実現します。

Dockerを利用することでアプリケーションを迅速に導入・拡張できるため、開発や運用・保守の効率が向上します。これにより、分離性、移植性、再利用性が提供され、アプリケーションのパッケージ化、配信、管理が容易になります。さらに、Docker はコンテナ間のネットワーク通信とデータ ボリュームのマウントもサポートしているため、コンテナ間の相互接続とデータ共有が容易になります。

要約すると、Docker は、アプリケーションのデプロイと管理を簡素化するツールと環境を提供するコンテナ化プラットフォームであり、アプリケーションをさまざまな環境間で一貫した方法で実行できるようにします

Docker の適用シナリオは何ですか?

Docker には幅広いアプリケーション シナリオがあり、一般的なアプリケーション シナリオをいくつか示します。

  1. アプリケーションのパッケージ化と配信: Docker は、アプリケーションとそのすべての依存関係をコンテナー イメージにパッケージ化して、アプリケーションがさまざまな環境で一貫して実行されることを保証します。これにより、アプリケーションの展開と配信がより簡単かつ信頼性の高いものになります。
  2. アプリケーションのデプロイとスケーリング: Docker を使用すると、アプリケーションを迅速にデプロイし、スケーリングできます。アプリケーションをコンテナー イメージにパッケージ化すると、Docker が有効な環境でアプリケーションを簡単にデプロイして実行できます。同時に、増大する需要に対応するために、必要に応じてアプリケーションを水平方向に迅速に拡張できます。
  3. マイクロサービス アーキテクチャ: Docker は、マイクロサービス アーキテクチャの構築と管理に最適です。各マイクロサービスを独立したコンテナーにパッケージ化して、サービス間の分離と独立したデプロイメントを実現できます。Docker を使用すると、マイクロサービスの管理と拡張が容易になるだけでなく、マイクロサービス間の通信や共同作業も容易になります。
  4. 継続的インテグレーションと継続的デプロイメント (CI/CD) : Docker を継続的インテグレーションと継続的デプロイメントのプロセスと組み合わせて、自動化されたビルド、テスト、デプロイメントを実現できます。Docker コンテナを使用すると、一貫したビルドおよびテスト環境を作成し、さまざまな段階でアプリケーションの一貫性を確保できます。
  5. 開発環境の分離と一貫性: Docker は、開発者が軽量で分離された開発環境をローカルに作成するのに役立ちます。開発者はコンテナを使用して、ホスト システムに影響を与えることなくアプリケーションの実行環境を構成および管理できます。これにより、開発環境のセットアップと構成がより簡単になり、再現可能になります。
  6. クロスプラットフォームおよびハイブリッド クラウド デプロイ: Docker のクロスプラットフォームの性質により、コンテナ イメージをさまざまなオペレーティング システムやクラウド プラットフォームにデプロイできます。これにより、アプリケーションの移行や拡張がより柔軟になり、さまざまな環境間での移行や拡張が可能になると同時に、ハイブリッド クラウドの展開やクロスクラウド プラットフォームの管理も容易になります。

上記のアプリケーション シナリオに加えて、Docker はテスト環境の作成、迅速な環境構築と共有、リソースの分離とセキュリティなどにも使用できます。Docker はその柔軟性と移植性により、最新のアプリケーションの開発と展開で広く使用されています。

Docker の基本概念

Docker の基本概念を理解する前に、理解する必要のある中心的な用語がいくつかあります。

  1. イメージ: Docker イメージは、コンテナーの実行に必要なすべてのファイル システム、ソフトウェア環境、アプリケーション、依存関係を含む読み取り専用のテンプレートです。イメージはコンテナー作成の基礎であり、Docker Hub またはプライベート リポジトリから既存のイメージを取得したり、Dockerfile を通じてカスタム イメージを構築したりできます。
  2. コンテナ: Docker コンテナはイメージの実行可能なインスタンスであり、独立した分離された実行環境です。コンテナには、アプリケーションの実行に必要なすべてのファイル、環境変数、ライブラリ、および構成が含まれています。コンテナを起動することでアプリケーションを実行したり、コンテナ上で起動、停止、一時停止、削除などの操作を行うことができます。
    1. イメージ (Image) とコンテナ (Container) の関係は、オブジェクト指向プログラミングにおけるクラスとインスタンスのようなもので、イメージは静的な定義であり、コンテナはイメージを実行するときの実体です。
  3. ウェアハウス (Docker レジストリ) : Docker レジストリは、Docker イメージを保存および配布するためのアクセス可能な一元的なリポジトリを提供し、ユーザーがイメージを簡単に共有、ダウンロード、管理できるようにします。
    1. イメージを構築した後は、現在のホストで簡単に実行できますが、このイメージを他のサーバーで使用する必要がある場合は、イメージを保存および配布するための集中サービス (Docker Registry など) が必要です。
    2. Docker レジストリには複数のリポジトリ (リポジトリ) を含めることができ、各リポジトリには複数のタグを含めることができ、各タグはイメージに対応します。
    3. 通常、ウェアハウスには同じソフトウェアの異なるバージョンのイメージが含まれており、ソフトウェアの各バージョンに対応するためにタグがよく使用されます
      このソフトウェアのどのバージョンのイメージが <ウェアハウス名>:<タグ> の形式で指定できます。
    4. Docker は、Docker Hub と呼ばれるパブリック レジストリを公式に提供しています。Docker Hub では、ユーザーは直接使用したり、独自のイメージを構築するためのベース イメージとして使用したりできる多数のパブリック イメージを見つけることができます。
    5. Docker Hub に加えて、ユーザーはプライベート Docker レジストリを構築して独自のイメージを保存および管理し、より高いセキュリティと制御性を実現することもできます。
  4. Dockerfile: Dockerfile は、Docker イメージの構築方法を定義するテキスト ファイルです。Dockerfile には、ベース イメージの選択、ソフトウェアのインストール、ファイルのコピー、環境変数の構成など、ビルド プロセス中の Docker エンジンの操作をガイドするための一連の命令と構成が含まれています。Dockerfile を通じて、自動イメージ構築プロセスを実現できます。
  5. Docker Compose: Docker Compose は、複数の Docker コンテナーを定義して実行するためのツールです。YAML ファイルを使用して複数のコンテナ間の関係と依存関係を構成し、単一のコマンドでコンテナ アプリケーション全体を起動、停止、管理できます。

これらの基本概念は Docker の中核を形成しており、これらを理解することで、Docker コンテナ化環境をより適切に使用および管理できるようになります。

Docker のインストール

ここでは、関連する外部情報を引用して、CenOS7 環境での Docker のインストールをリストします。

  1. Vmvare の詳細なインストール チュートリアル: https://blog.csdn.net/SoulNone/article/details/126681722
  2. Vmvare 仮想マシンのアクティベーション キー: https://www.bilibili.com/read/cv21151094
  3. CenterOS7 イメージのダウンロード: https://blog.csdn.net/a350904150/article/details/129833998
  4. VmvareインストールCenterOS7:https://blog.csdn.net/StupidlyGrass/article/details/128646335
  5. CenterOS7 環境に Docker をインストールする: https://blog.csdn.net/Siebert_Angers/article/details/127315542

Dockerイメージ

Docker は、コンテナーを実行する前に、対応するイメージがローカルに存在する必要があります。イメージがローカルに存在しない場合、Docker はイメージ ウェアハウスからイメージをダウンロードします。

画像を取得

Docker イメージ ウェアハウスからイメージを取得するコマンドは docker pull です。そのコマンド形式は次のとおりです。

$ docker pull [选项] [Docker Registry 地址[:端口号]] 仓库名[:标签]
  • Docker イメージ ウェアハウスのアドレス: アドレスの形式は通常、<ドメイン名/IP>[:ポート番号]です。デフォルトのアドレスは DockerHub です
  • ウェアハウス名: 前述したように、ここでのウェアハウス名は 2 つの部分からなる名前、つまり <ユーザー名>/<ソフトウェア名> です。DockerHub の場合、ユーザー名が指定されていない場合、デフォルトはライブラリ (公式イメージ) です。
[root@localhost snow]# docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete 
b51569e7c507: Pull complete 
da8ef40b9eca: Pull complete 
fb15d46c38dc: Pull complete 
Digest: sha256:1f1a2d56de1d604801a9671f301190704c25d604a416f59e03c04f5c6ffee0d6
Status: Downloaded newer image for ubuntu:16.04
docker.io/library/ubuntu:16.04
[root@localhost snow]# 

上記の例では、Docker イメージ ウェアハウスのアドレスが指定されていないため、イメージは Docker Hub から取得されます。イメージ名は ubuntu:16.04 なので、公式イメージ ライブラリ/ubuntu ウェアハウスにある 16.04 というラベルの付いたイメージが取得されます。

ダウンロード プロセスから、前述した階層型ストレージの概念がわかり、イメージは複数のストレージ層で構成されています。ダウンロードも単一のファイルではなくレイヤーごとに行われます。

走る

イメージを取得したら、このイメージに基づいてコンテナを起動して実行できます。上記の ubuntu:16.04 を例として、内部で bash を起動して対話型操作を実行する場合:

root@localhost snow]# docker run -it --rm ubuntu:16.04 bash
root@3f44186a6166:/# ls -la
total 4
drwxr-xr-x.   1 root root    6 May 27 18:29 .
drwxr-xr-x.   1 root root    6 May 27 18:29 ..
-rwxr-xr-x.   1 root root    0 May 27 18:29 .dockerenv
drwxr-xr-x.   2 root root 4096 Aug  4  2021 bin
drwxr-xr-x.   2 root root    6 Apr 12  2016 boot

docker run はコンテナを実行するコマンドで、詳細なコマンド形式はコンテナの項で説明します。

画像一覧

ダウンロードしたイメージを一覧表示するには、docker image ls コマンドを使用します。

root@3f44186a6166:/# exit
exit
[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    9c7a54a9a43c   3 weeks ago     13.3kB
ubuntu        16.04     b6f507652425   21 months ago   135MB

ローカルイメージを削除する

ローカル イメージを削除する場合は、 docker image rm コマンドを使用できます。その形式は次のとおりです。

$ docker image rm [选项] <镜像1> [<镜像2> ...]

このうち、<image> には、画像の短い ID、画像の長い ID、画像の名前、または画像の概要を指定できます。

デフォルトで docker image ls によってリストされる短い ID は、他のイメージと区別するのに十分である限り、通常は最初の 3 文字以上です。

[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
redis         latest    0ec8ab59a35f   4 days ago      117MB
hello-world   latest    9c7a54a9a43c   3 weeks ago     13.3kB
ubuntu        16.04     b6f507652425   21 months ago   135MB
[root@localhost snow]# docker image rm 0ec
Untagged: redis:latest
Untagged: redis@sha256:f9724694a0b97288d2255ff2b69642dfba7f34c8e41aaf0a59d33d10d8a42687
Deleted: sha256:0ec8ab59a35faa3aaee416630128e11949d44ac82d15d43053f8af5d61182a5d
Deleted: sha256:f89720eafb298773509177d4b8b76a32adda4da1015ca28f52daa03fc6090499
Deleted: sha256:c3b5385e13627e14d553f09a098275d1f1ada0b6228cc30c92e095d669df799c
Deleted: sha256:b830b2806be6a052c6d857f927f72ef18a2539e69fdb6d51cf95d76d7e06c8f1
Deleted: sha256:8de1c0863fac10debb4e19de0cc27639ae97c1111eca0920649b21d97bc8dded
Deleted: sha256:80940c1d5550f3f56f5ab008aa79e899e4d3c9b6b41b9f76077f31dcfb2c482c
Deleted: sha256:8cbe4b54fa88d8fc0198ea0cc3a5432aea41573e6a0ee26eca8c79f9fbfa40e3
[root@localhost snow]# 

上記のコマンドの出力情報を観察すると、削除動作が 2 つのカテゴリに分けられ、1 つはタグなし、もう 1 つは削除であることがわかります。前に紹介したように、画像の一意の識別子はその ID と概要であり、画像には複数のタグを付けることができます。

上記のコマンドを使用してイメージを削除するとき、実際には特定のタグのイメージを削除するよう求めています。したがって、最初に行う必要があるのは、要件を満たすすべてのイメージ タグをキャンセルすることです。これが、タグなしの情報です。画像のタグがすべて解除されると、その画像は存在意味を失う可能性が高いため、削除動作がトリガーされます。

commit 画像の構成を理解する

注: docker commit コマンドには、学習に加えて、侵入後のシーンの保存など、いくつかの特別なアプリケーションもあります。ただし、イメージのカスタマイズには docker commit を使用しないでください。イメージのカスタマイズは Dockerfile を使用して行う必要があります画像をカスタマイズしたい場合は、次のセクションを参照してください。

次に、Web サーバーのカスタマイズを例として、イメージがどのように構築されるかを説明します。

[root@localhost snow]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
f03b40093957: Pull complete 
eed12bbd6494: Pull complete 
fa7eb8c8eee8: Pull complete 
7ff3b2b12318: Pull complete 
0f67c7de5f2c: Pull complete 
831f51541d38: Pull complete 
Digest: sha256:af296b188c7b7df99ba960ca614439c99cb7cf252ed7bbc23e90cfda59092305
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
[root@localhost snow]# docker run --name web1.0 -d -p 80:80 nginx
054d1cdcf5cf2de43116697f2a96e0c50a5138cd753580f55f1966364df155f1
[root@localhost snow]# 

このコマンドは、web1.0 という名前の nginx イメージを含むコンテナーを起動し、ポート 80 をマップします。これにより、ブラウザーを使用して nginx サーバーにアクセスできるようになります。

Linux 上で Docker を実行している場合は、http://localhost に直接アクセスできます。仮想マシンまたはクラウド サーバーにインストールされた Docker を使用している場合は、localhost を仮想マシンのアドレスまたは実際のクラウド サーバーのアドレスに置き換える必要があります。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが存在する可能性があります。画像を保存して直接アップロードすることをお勧めします (img-Licj8a72-1686898089872) (C:\Users\lixuewen\AppData\Roaming\Typora\) typora-user-images\ image-20230527191015639.png)]

このウェルカム ページが気に入らないので、他のテキストに変更したい場合は、 docker exec コマンドを使用してコンテナに入り、そのコンテンツを変更できます。

[root@localhost snow]# docker exec -it web1.0 bash
root@054d1cdcf5cf:/# echo '<h1>Hello,World!</h1>' > /usr/share/nginx/html/index.html
root@054d1cdcf5cf:/# exit
exit
[root@localhost snow]# 

対話型ターミナル モードで Web サーバー コンテナに入り、bash コマンドを実行しました。つまり、操作可能なシェルを取得しました。その後、次を使用しました。

こんにちは世界!

/usr/share/nginx/html/index.html のコンテンツをカバーします。

ブラウザを更新すると、ページが次のように表示されます。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが存在する可能性があります。画像を保存して直接アップロードすることをお勧めします (img-N2yya1zd-1686898089873) (C:\Users\lixuewen\AppData\Roaming\Typora\) typora-user-images\ image-20230527193042587.png)]

コンテナーのファイルを変更しました。つまり、コンテナーのストレージ層を変更しました。docker diff コマンドを通じて特定の変更を確認できます。

[root@localhost snow]# docker diff web1.0
C /run
A /run/nginx.pid
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /root
A /root/.bash_history
C /etc
C /etc/nginx
C /etc/nginx/conf.d
C /etc/nginx/conf.d/default.conf
C /var
C /var/cache

Docker は、コンテナーのストレージ層をイメージに保存できる docker commit コマンドを提供します。言い換えれば、元の画像に基づいて、コンテナの記憶層を重ね合わせて新しい画像を形成します今後この新しいイメージを実行すると、元のコンテナーの最後のファイル変更が反映されます。

docker commit の構文形式は次のとおりです。

$ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
[root@localhost snow]# docker commit --author "snow" --message "edit index.html" web1.0 nginx:2.0
sha256:c06e3839c9eb09fee7e4409290e2d51ddeb214fe87a73d9806cd9479ddf2c9ca
[root@localhost snow]# docker image ls nginx
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
nginx        2.0       c06e3839c9eb   28 seconds ago   143MB
nginx        latest    f9c14fe76d50   2 days ago       143MB
[root@localhost snow]# 

–author は変更の作成者を指定し、--message はこの変更の内容を記録します。これは git バージョン管理に似ており、この新しいカスタマイズされたイメージは docker image ls で確認できます。

新しいイメージをカスタマイズしたら、イメージを実行できます。

ここでは、新しいサービスに web2.0 という名前を付け、ポート 81 にマップします。再度 http://ip:81 に直接アクセスして結果を確認すると、以前に修正した web1.0 と同じ内容になっているはずです。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが存在する可能性があります。画像を保存して直接アップロードすることをお勧めします (img-UdbJXDID-1686898089874) (C:\Users\lixuewen\AppData\Roaming\Typora\) typora-user-images\ image-20230527194357988.png)]

ここまでで、初めてカスタマイズされたイメージが完成しました。docker commit コマンドを使用して、古いイメージに新しいレイヤーを手動で追加して、新しいイメージを形成しました。

docker commit は注意して使用してください

docker commit コマンドを使用すると、イメージ階層化ストレージの概念を直感的に理解できますが、実際の環境ではこのように使用されません。

docker commit を使用することは、イメージに対するすべての操作がブラック ボックス操作であることを意味します。生成されたイメージはブラック ボックス イメージとも呼ばれます。つまり、イメージを作成した人以外は、どのようなコマンドが実行され、どのようにイメージを生成したかを知りません。他の人はそれを知る方法がありません。

Dockerfile カスタム イメージ

docker commit の勉強から、イメージのカスタマイズは実際には各レイヤーに追加される構成とファイルのカスタマイズであることがわかりました。各レイヤーを変更、インストール、構築、操作するためのコマンドをスクリプトに記述し、このスクリプトを使用してイメージを構築およびカスタマイズできれば、前述の問題は繰り返されず、イメージ構築の透明性が向上します。のボリュームで問題はすべて解決されます。このスクリプトは Dockerfile です。

前回の nginx イメージのカスタマイズを例に、今回は Dockerfile を使用してカスタマイズします。

空のディレクトリにテキスト ファイルを作成し、Dockerfile という名前を付けます。

[root@localhost snow]# mkdir nginxtest
[root@localhost snow]# cd nginxtest
[root@localhost nginxtest]# touch Dockerfile

Vim コマンドを使用してそのコンテンツを追加します。

FROM nginx
RUN echo '<h1>Hello, EveryDay!</h1>' > /usr/share/nginx/html/index.html
  • FROM はベースイメージを指定します

    いわゆるカスタマイズされたイメージは、イメージをベースにし、その上でカスタマイズする必要があります。以前に nginx イメージを使用してコンテナーを実行し、それを変更したのと同じように
    、ベース イメージを指定する必要があります。FROM はベース イメージを指定する
    ため、FROM は Dockerfile 内で必要な命令であり、最初の命令である必要があります

  • RUN実行コマンド

    RUN 命令は、コマンド ライン コマンドを実行するために使用されます。コマンド ラインの強力な機能により、RUN コマンドはイメージをカスタマイズするときに
    最もよく使用されるコマンドの 1 つです。次の 2 つの形式があります。

    • シェル形式: RUN <コマンド>、コマンドラインに直接入力されたコマンドと同様
    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
    
    • exec 形式: RUN ["実行可能ファイル", "パラメータ 1", "パラメータ 2"]、これは関数呼び出しの形式に似ています

RUN はシェルスクリプトと同じようにコマンドを実行できるので、
シェルスクリプトのように各コマンドを RUN にマッピングできますか? 例えば:

FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

上記の書き方で7層の画像が作成されます。これはまったく意味がありません。次のように記述するとよいでしょう。

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& 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 install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

多くの RUN ペアを使用して異なるコマンドに 1 つずつ対応するのではなく、RUN 命令を 1 つだけ使用し、&& を使用して必要なコマンドを連結します。以前の 7 つのレイヤーを 1 つのレイヤーに簡略化しました。Dockerfile を作成するときは、シェル スクリプトを作成しているのではなく、各
レイヤーの構築方法を定義していることを常に思い出してください。

ビルドイメージ

この Dockerfile の内容を理解したので、イメージをビルドしましょう。Dockerfile ファイルが配置されているディレクトリで実行します

[root@localhost nginxtest]# docker build -t nginx:3.0 .
[+] Building 0.9s (6/6) FINISHED                                                             
 => [internal] load build definition from Dockerfile                                         
 => => transferring dockerfile: 179B                                                         
 => [internal] load .dockerignore                                                           
 => => transferring context: 2B                                                             
 => [internal] load metadata for docker.io/library/nginx:latest                             
 => [1/2] FROM docker.io/library/nginx                                                       
 => [2/2] RUN echo '<h1>Hello, EveryDay!</h1>' > /usr/share/nginx/html/index.html           
 => exporting to image                                                                       
 => => exporting layers                                                                     
 => => writing image sha256:e899e0dd12df606efc99e3bb62b57d99935db376c9b1760b05ed7352a004c37a 
 => => naming to docker.io/library/nginx:3.0                                                            

(注:上記のビルド コマンドの最後に必ずスペースとドットを追加してください。追加しないとエラーが報告されます)

イメージビルドコンテキスト (Context)

注意してみると、docker build コマンドの最後に . があることがわかります。. は現在のディレクトリを表し、Dockerfile は現在のディレクトリにあります。上記のコマンド形式は実際にはコンテキスト パスを指定します。では、コンテキストとは何でしょうか?

まず、docker build がどのように機能するかを理解する必要があります。Docker は、 Docker エンジン(つまり、サーバー側のデーモン) と実行時のクライアント側のツールに分かれています。Docker エンジンは、DockerRemote API と呼ばれる REST API のセットを提供し、docker コマンドなどのクライアント ツールは、この API セットを通じて Docker エンジンと対話し、さまざまな機能を実行します。したがって、表面的にはさまざまな docker 関数をローカルで実行しているように見えますが、実際にはすべてリモート呼び出しを使用してサーバー側 (Docker エンジン) で行われます。

docker build コマンドを使用してイメージをビルドする場合、多くの場合、いくつかのローカル ファイルをイメージにコピーする必要がありますが、これはローカルではなくサーバー側、つまり Docker エンジンでビルドされます。では、このクライアント/サーバー アーキテクチャでは、サーバーはどのようにしてローカル ファイルを取得できるのでしょうか?

これにより、コンテキストの概念が導入されます。ビルド時に、ユーザーはイメージ コンテキストをビルドするためのパスを指定します。dockerbuild コマンドはこのパスを学習すると、そのパスの下にあるすべてのコンテンツをパッケージ化し、Docker エンジンにアップロードします。このようにして、Docker エンジンはコンテキスト パッケージを受け取った後、イメージを構築するために必要なすべてのファイルを展開して取得します。

イメージ構築において不必要な間違いを避けるためには、ビルド コンテキストを理解することが非常に重要です。たとえば、一部の初心者は、 COPY /opt/xxxx /app が機能しないことに気づき、単に Dockerfile をハード ディスクのルート ディレクトリに置いてビルドしましたが、docker build の実行後、数十 GB のファイルが送信されることに気付きました。作業は非常に遅くて簡単でしたが、ビルドは失敗しました。これは、このアプローチでは docker build にハードディスク全体をパッケージ化するよう要求しているためであり、これは明らかに使用上のエラーです。

一般に、Dockerfile は空のディレクトリまたはプロジェクトのルート ディレクトリに配置する必要があります。必要なファイルがこのディレクトリに見つからない場合は、必要なファイルをコピーする必要があります。ビルド中に本当に Docker エンジンに渡したくないものがディレクトリ内にある場合は、 .gitignoreと同じ構文で .dockerignore を作成できます。このファイルは、必要のないものを排除するために使用されます。コンテキストとして Docker エンジンに渡されます。

Dockerfile命令の詳しい説明

  1. COPYファイルをコピーします: COPY <コピー元パス>… <コピー先パス>

    1. COPY 命令は、ビルド コンテキスト ディレクトリ内の <ソース パス> にあるファイル/ディレクトリを、新しいレイヤーのイメージ内の<ターゲット
      パス> の場所にコピーします。例えば:

      COPY package.json /usr/src/app/
      
    2. <source path> は複数にすることも、ワイルドカードにすることもできます

    3. <ターゲット パス> には、コンテナ内の絶対パス、または作業ディレクトリを基準とした相対パスを指定できます (作業ディレクトリは
      WORKDIR ディレクティブを使用して指定できます)。

    4. COPY命令を使用すると、ソースファイルの各種メタデータが保持されます。読み取り、書き込み、実行権限、ファイル変更時間など。この機能は画像のカスタマイズに役立ちます。特にビルド関連のファイルが Git を使用して管理されている場合はそうです。

  2. ADDはより高度なコピー ファイルです。コマンドと COPY の形式とプロパティは基本的に同じです。ただし、COPYをベースにいくつかの機能が追加されています

    1. たとえば、<ソース パス> に URL を指定すると、Docker エンジンはリンクされたファイルをダウンロードして、<ターゲット パス> に配置しようとします。

    2. <source path> が tar 圧縮ファイルで、圧縮形式が gzip、bzip2、および xz の場合、ADD コマンドは圧縮ファイルを <destination path> に自動的に解凍します。

    3. したがって、COPY 命令と ADD 命令のどちらを選択する場合も、すべてのファイル コピーには COPY 命令を使用し、自動解凍が必要な場合にのみ ADD 命令を使用するという原則に従うことができます。

  3. CMDコンテナ起動コマンド:

    シェル形式: CMD <コマンド>;

    exec形式: CMD ["実行ファイル", "パラメータ1", "パラメータ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 コマンドに置き換え、システムのバージョン情報を出力することを意味します。

  4. ENTRYPOINTエントリーポイント

    ENTRYPOINT の目的は CMD と同じで、コンテナーの起動プログラムとパラメーターを指定することです。ENTRYPOINT は実行時に置き換えることもできますが、CMD よりも少し面倒で、docker run のパラメーター --entrypoint を使用して指定する必要があります。

    ENTRYPOINTを指定するとCMDの意味が変わり、コマンドを直接実行するのではなく、CMDの内容がパラメータとしてENTRYPOINT命令に渡されます。つまり、実際に実行すると「」となります。

    CMD を取得した後、なぜ ENTRYPOINT が必要なのでしょうか?

    1. 画像をコマンドのように使えるようにする

    2. アプリケーションを実行する前の準備

  5. ENV は環境変数を設定します。2 つの形式は次のとおりです。

    ENV
    ENV = =…

    このコマンドは環境変数を設定するためのものであり、RUN などの後続の命令でも、実行時アプリケーションでも、ここで定義した環境変数を直接使用できます。

    環境変数を定義すると、この環境変数は後続の命令で使用できるようになります。

  6. ARGビルド パラメーター: 形式: ARG <パラメーター名>[=<デフォルト値>]

    ビルド パラメーターには ENV と同じ効果があり、環境変数を設定します。違いは、ARG によって設定されたビルド環境の環境変数が、将来コンテナーを実行するときに存在しないことです。

    Dockerfile の ARG ディレクティブはパラメーター名を定義し、そのデフォルト値を定義します。このデフォルト値は、ビルド コマンド docker build の --build-arg <引数名>=<値> でオーバーライドできます。

  7. VOLUME は匿名ボリュームを定義します

    形式は次のとおりです。
    VOLUME [“<path1>”, “<path2>”…]
    VOLUME <path>

    コンテナが実行されているときは、コンテナ ストレージ層が書き込み操作を行わないようにする必要があると前に述べました。動的データを保存する必要があるデータベース アプリケーションの場合、データベース ファイルはボリュームに保存する必要があります。さらに、Docker ボリュームを で紹介します。次の章の概念。

    ユーザーが実行時に動的ファイルがボリュームとして保存されるディレクトリをマウントし忘れることを防ぐために、Dockerfile で匿名ボリュームとしてマウントする特定のディレクトリを事前に指定できます。これにより、ユーザーが実行時にマウントを指定しない場合でも、の場合、アプリケーションは正常に動作し、コンテナ ストレージ レイヤーに大量のデータを書き込むことなく実行できます。

    VOLUME /data
    

    ここの /data ディレクトリは実行時に匿名ボリュームとして自動的にマウントされ、/data に書き込まれる情報はコンテナ ストレージ層に記録されないため、コンテナ ストレージ層のステートレス性が保証されます。

  8. EXPOSEポート宣言

    形式は次のとおりです: EXPOSE <ポート 1> [<ポート 2>…]

    EXPOSE ディレクティブは、ランタイム コンテナがサービス ポートを提供することを宣言します。これは単なるステートメントです。このステートメントにより、アプリケーションは実行時にこのポートのサービスを開きません。

    このような宣言を Dockerfile に記述することには、次の 2 つの利点があります。

    1. 1 つは、イメージ ユーザーがこのイメージ サービスのガード ポートを理解し、構成マッピングを容易にすることです。

    2. もう 1 つの用途は、実行時にランダム ポート マッピングを使用する場合、つまり docker run -P を実行すると、EXPOSE ポートが自動的かつランダムにマッピングされます。

  9. WORKDIR は作業ディレクトリを指定します

    形式は次のとおりです: WORKDIR <作業ディレクトリのパス>

    WORKDIR コマンドを使用して、作業ディレクトリ (または現在のディレクトリ) を指定できます。今後、各層の現在のディレクトリは、指定したディレクトリに変更されます。ディレクトリが存在しない場合は、WORKDIR を使用してディレクトリを作成します。

    前に述べたように、一部の初心者が犯すよくある間違いは、Dockerfile をシェル スクリプトとして作成することです。この誤った理解は、
    次のエラーを引き起こす可能性もあります。

    RUN cd /app
    RUN echo "hello" > world.txt
    

    この Dockerfile を実行してイメージを構築すると、/app/world.txt ファイルが見つからないか、その内容が hello ではないことがわかります。その理由は実は非常に単純で、シェルでは連続する 2 行が同じプロセス実行環境を表すため、前のコマンドで変更されたメモリ状態が後続のコマンドに直接影響しますが、Dockerfile では RUN の 2 行の実行環境が影響を受けます。コマンドは根本的に異なります。 、 は 2 つの完全に異なるコンテナですこれは、Dockerfile を使用して階層ストレージを構築するという概念を理解していないために発生する間違いです。

    前述したように、各 RUN はコンテナーを開始し、コマンドを実行して、ストレージ レイヤー ファイルの変更をコミットします。

Dockerコンテナの操作

簡単に言えば、コンテナとは、独立して実行されるアプリケーションまたはアプリケーションのグループ、およびその実行環境です。これに応じて、仮想マシンは、実行 (実行環境およびその他のシステム環境を提供) とその上で実行されるアプリケーションをシミュレートするオペレーティング システムの完全なセットとして理解できます。

コンテナの起動

コンテナの起動方法には、イメージを基に新規コンテナを作成して起動する方法と、終了状態(停止状態)のコンテナを再起動する方法があります。

新規作成して開始

必要なコマンドは主にdocker runです。

たとえば、次のコマンドは「Hello Docker」を出力し、コンテナを終了します。

[root@localhost snow]# docker run ubuntu:16.04 /bin/echo 'Hello Docker'
Hello Docker

次のコマンドは bash ターミナルを起動し、ユーザーとの対話を可能にします。

[root@localhost snow]# docker run -t -i ubuntu:16.04 /bin/bash
root@d80afe7ea29f:/# ps
   PID TTY          TIME CMD
     1 pts/0    00:00:00 bash
    10 pts/0    00:00:00 ps
root@d80afe7ea29f:/# exit
exit

このうち -t オプションを使用すると、Docker が疑似 tty (擬似 tty) を割り当ててコンテナの標準入力にバインドできるようになり、-i オプションを使用するとコンテナの標準入力を開いたままにできます。

docker run を使用してコンテナーを作成する場合、Docker がバックグラウンドで実行する標準操作には次のものがあります。

  1. 指定したイメージがローカルに存在するか確認し、存在しない場合は公開ウェアハウスからダウンロードします。
  2. イメージを使用してコンテナを作成して起動する
  3. ファイル システムを割り当て、読み取り専用イメージ レイヤーの外側に読み取り/書き込みレイヤーをマウントします。
  4. ホスト上で構成されたブリッジインターフェースからコンテナーに仮想インターフェースをブリッジします。
  5. アドレスプールからコンテナへのIPアドレスの構成
  6. ユーザー指定のアプリケーションを実行する
  7. 実行後、コンテナは終了します
終了したコンテナを起動する

docker container startコマンドを使用して、終了したコンテナを直接起動できます。

コンテナの中核は実行されるアプリケーションであり、必要なリソースはすべてアプリケーションの実行に必要です。これ以外にリソースはありません。疑似端末で ps または top を使用すると、プロセス情報を表示できます。

バックグラウンドプロセス

多くの場合、コマンドの実行結果を現在のホストに直接出力するのではなく、バックグラウンドで Docker を実行させる必要があります。
この時点で、これは -d パラメータを追加することで実現できます。

-d パラメーターを使用して開始すると、一意の ID が返され、dockercontainerls コマンドを使用してコンテナー情報を表示することもできます。

[root@localhost snow]# docker container ls
CONTAINER ID   IMAGE       COMMAND                  CREATED      STATUS      PORTS                               NAMES
e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0

コンテナーの出力情報を取得するには、docker container logsコマンドを使用できます。

コンテナを終了する

docker container stop を使用して、実行中のコンテナを終了できます。

また、Dockerコンテナ内の指定したアプリケーションが終了すると、コンテナも自動的に終了しますたとえば、前の章で 1 つのターミナルのみを起動したコンテナの場合、ユーザーが exit コマンドまたは Ctrl+d を使用してターミナルを終了すると、作成されたコンテナはすぐに終了します。

終了したコンテナーは、 docker container ls -a コマンドを使用して確認できます。例えば

[root@localhost snow]# docker container ls -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                        PORTS                               NAMES
d80afe7ea29f   ubuntu:16.04   "/bin/bash"              9 minutes ago    Exited (0) 9 minutes ago                                          bold_saha
626c2066188c   ubuntu:16.04   "/bin/bash"              10 minutes ago   Exited (127) 10 minutes ago                                       jolly_shockley
791d1f4d5eca   ubuntu:16.04   "/bin/bash"              10 minutes ago   Exited (127) 10 minutes ago                                       boring_margulis
1280e87e1cae   ubuntu:16.04   "/bin/echo 'Hello Do…"   11 minutes ago   Exited (0) 11 minutes ago                                         wonderful_saha

終了状態のコンテナは、docker container start コマンドを使用して再起動できます。
さらに、 docker container restart コマンドは、実行中のコンテナを終了してから再起動します。

コンテナに入る

-d パラメーターを使用すると、コンテナーは開始後にバックグラウンドに入ります。

dockerattach コマンドや docker exec コマンドを使用するなど、操作のためにコンテナに入る必要がある場合がありますが、以下で説明する理由により docker exec コマンドを使用することをお勧めします。

  1. アタッチコマンド

    dockerattach は Docker に付属するコマンドです。このコマンドの使用例を次に示します。

    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                               NAMES
    5f6884f826e4   ubuntu      "/bin/bash"              23 seconds ago   Up 22 seconds                                       practical_payne
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago       Up 6 days       0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago       Up 6 days       0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    [root@localhost snow]# docker attach 5f6
    root@5f6884f826e4:/# exit
    exit
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED      STATUS      PORTS                               NAMES
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    

    注: この標準入力を終了すると、コンテナが停止します。

  2. 実行コマンド

    Docker exec の後には複数のパラメータを指定できますが、ここでは主に -i -t パラメータについて説明します。

    -i パラメータのみを使用した場合、疑似ターミナルが割り当てられないため、インターフェイスには使い慣れた Linux コマンド プロンプトはありませんが、コマンドの実行
    結果は返されます。

    -i -t パラメーターを一緒に使用すると、使い慣れた Linux コマンド プロンプトが表示されます。

    [root@localhost snow]# docker run -dit ubuntu
    3a7731e1a8a290b59263e9c73695f0a52e57e009505b4652be57c3fbe88a147e
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                               NAMES
    3a7731e1a8a2   ubuntu      "/bin/bash"              3 seconds ago   Up 2 seconds                                       hardcore_ptolemy
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago      Up 6 days      0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago      Up 6 days      0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    [root@localhost snow]# docker exec -it 3a77 bash
    root@3a7731e1a8a2:/# exit
    exit
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED              STATUS              PORTS                               NAMES
    3a7731e1a8a2   ubuntu      "/bin/bash"              About a minute ago   Up About a minute                                       hardcore_ptolemy
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago           Up 6 days           0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago           Up 6 days           0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    

    この標準入力を終了してもコンテナは停止しません。これが、誰もが docker exec を使用することをお勧めする理由です。

    パラメーターの詳細については、docker exec --help を使用して表示してください。

コンテナのエクスポートとインポート

エクスポートコンテナ:

ローカル コンテナーをエクスポートする場合は、docker export コマンドを使用できます。(これにより、コンテナーのスナップショットがローカル ファイルにエクスポートされます)

[root@localhost snow]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                        PORTS                               NAMES
3a7731e1a8a2   ubuntu         "/bin/bash"              4 minutes ago    Up 4 minutes                                                      hardcore_ptolemy
[root@localhost snow]# docker export 3a77 > ubuntuBash.tar

インポートコンテナ:

docker import を使用して、コンテナー スナップショット ファイルからイメージにインポートできます。

[root@localhost snow]# cat ubuntuBash.tar | docker import - test/ubuntu:test2.0
sha256:1f2b949f67cade289f240a22612630363380d95b43d63bc60e032bbff69bc373
[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
test/ubuntu   test2.0   1f2b949f67ca   12 seconds ago   77.8MB
nginx         3.0       e899e0dd12df   3 hours ago      143MB

また、URLやディレクトリを指定してインポートすることもできます。

注: ユーザーは、docker load を使用してイメージ ストレージ ファイルをローカル イメージ ライブラリにインポートすることも、dockerimport を使用してコンテナ スナップショットをローカル イメージ ライブラリにインポートすることもできます。2 つの違いは、コンテナ スナップショット ファイルはすべての履歴レコードとメタデータ情報を破棄する(つまり、その時点でのコンテナのスナップショット状態のみを保存する) のに対し、イメージ ストレージ ファイルは完全なレコードを保存し、サイズが大きいことです。

また、コンテナスナップショットファイルからインポートする際に、タグなどのメタデータ情報を再指定することができます。

コンテナの削除

docker container rm を使用して、終了状態にあるコンテナを削除できます。例えば

[root@localhost snow]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED             STATUS                           PORTS                               NAMES
3a7731e1a8a2   ubuntu         "/bin/bash"              15 minutes ago      Up 15 minutes                                                        hardcore_ptolemy
5f6884f826e4   ubuntu         "/bin/bash"              19 minutes ago      Exited (0) 18 minutes ago                                            practical_payne
d80afe7ea29f   ubuntu:16.04   "/bin/bash"              About an hour ago   Exited (0) About an hour ago                                         bold_saha
[root@localhost snow]# docker container rm 5f68
5f68

実行中のコンテナを削除する場合は、-f パラメータを追加できます。Docker は SIGKILL シグナルをコンテナーに送信します。

終了したすべてのコンテナをクリーンアップする

docker container ls -a コマンドを使用して、終了状態を含む作成済みのコンテナをすべて表示します。数が多すぎると、1 つずつ削除するのは面倒です。次のコマンドを使用して、終了状態のすべてのコンテナをクリーンアップします。

$ docker container prune

倉庫を訪問する

リポジトリは、イメージが集中的に保存される場所です。

混同されやすい概念は、登録サーバー (レジストリ) です。実際、登録サーバーはウェアハウスを管理する特定のサーバーであり、各サーバーは複数のウェアハウスを持つことができ、各ウェアハウスには複数のミラーがあります。この点で、リポジトリは特定のプロジェクトまたはディレクトリと考えることができます。たとえば、ウェアハウス アドレス dl.dockerpool.com/ubuntu の場合、dl.dockerpool.com は登録サーバー アドレス、ubuntu はウェアハウス名です。

ドッカーハブ

Docker はパブリック リポジトリである Docker Hub を公式に管理しており、そこにはすでに 15,000 を超えるイメージが含まれています。ほとんどの要件は、Docker Hub からイメージを直接ダウンロードすることで実現できます。

  1. 登録: https://cloud.docker.com で無料の Docker アカウントを登録します。

  2. ログイン: docker login を実行してログインし、docker logout を実行してログアウトします。

  3. イメージをプルする: docker search コマンドを使用して公式ウェアハウスでイメージを検索し、docker pull コマンドを使用して
    ローカルにダウンロードします。

    ミラーリソースは、公式に提供されているかどうかに応じて 2 つのカテゴリに分類できます。

    1. 1 つは、ベースイメージまたはルートイメージと呼ばれる、centos のような、公式に提供されているイメージです。
    2. tianon/centos イメージなどの別のタイプは、Docker ユーザーによって作成および保守され、多くの場合、
      ユーザー名がプレフィックスとして付けられます。
  4. イメージをプッシュする: docker Push コマンドを使用して、独自のイメージを Docker Hub にプッシュします。

  5. 自動作成: イメージ内のプログラムを頻繁にアップグレードする必要がある場合に非常に便利です

    ユーザーが Docker Hub を介してターゲット Web サイト (現在 GitHub または BitBucket をサポートしている) で追跡プロジェクトを指定できるようにします。プロジェクトで新しい送信が発生するか、新しいタグが作成されると、Docker Hub は自動的にイメージを構築し、それを Docker Hub にプッシュします。

私設倉庫

アドレス dl.dockerpool.com/ubuntu の場合、dl.dockerpool.com は登録サーバーのアドレス、ubuntu はウェアハウス名です。

ドッカーハブ

Docker はパブリック リポジトリである Docker Hub を公式に管理しており、そこにはすでに 15,000 を超えるイメージが含まれています。ほとんどの要件は、Docker Hub からイメージを直接ダウンロードすることで実現できます。

  1. 登録: https://cloud.docker.com で無料の Docker アカウントを登録します。

  2. ログイン: docker login を実行してログインし、docker logout を実行してログアウトします。

  3. イメージをプルする: docker search コマンドを使用して公式ウェアハウスでイメージを検索し、docker pull コマンドを使用して
    ローカルにダウンロードします。

    ミラーリソースは、公式に提供されているかどうかに応じて 2 つのカテゴリに分類できます。

    1. 1 つは、ベースイメージまたはルートイメージと呼ばれる、centos のような、公式に提供されているイメージです。
    2. tianon/centos イメージなどの別のタイプは、Docker ユーザーによって作成および保守され、多くの場合、
      ユーザー名がプレフィックスとして付けられます。
  4. イメージをプッシュする: docker Push コマンドを使用して、独自のイメージを Docker Hub にプッシュします。

  5. 自動作成: イメージ内のプログラムを頻繁にアップグレードする必要がある場合に非常に便利です

    ユーザーが Docker Hub を介してターゲット Web サイト (現在 GitHub または BitBucket をサポートしている) で追跡プロジェクトを指定できるようにします。プロジェクトで新しい送信が発生するか、新しいタグが作成されると、Docker Hub は自動的にイメージを構築し、それを Docker Hub にプッシュします。

私設倉庫

おすすめ

転載: blog.csdn.net/weixin_40709965/article/details/131246908