IntrotoDockerTHM
基本Docker
Docker一开始看起来很强大。然而,这些命令非常直观,只要稍加练习,你很快就会成为Docker向导。
Docker的语法可以分为四大类:
- 运行容器
- 管理和检查容器
- 管理Docker镜像
- Docker守护进程统计数据和信息
我们将在本任务中分解这些类别。
管理Docker镜像
Docker Pull
在运行Docker容器之前,我们首先需要一个镜像。回忆起“Intro to Containerisation集装箱化简介” 房间,图像是容器应该执行的指令。运行一个什么都不做的容器是没有用的!
器中运行Web服务器。在下载映像之前,让我们分解下载映像所需的命令和语法。可以使用 docker pull
命令并提供图像的名称。
比如说, docker pull nginx
. Docker必须知道从哪里获取这个镜像(比如从一个我们将在后面的任务中提到的存储库)。
继续我们上面的例子,让我们下载这个Nginx镜像!
显示下载“Nginx”图像的终端
cmnatic@thm:~$ docker pull nginx |
通过运行此命令,我们正在下载名为“nginx”的最新版本的图像。图像具有这些标签,称为 tags. 这些 tags 用于表示图像的变化。例如,图像可以具有相同的名称,但不同的标记表示不同的版本。我提供了一个如何在下表中使用标签的示例:
Docker镜像 | TAG | 命令示例 | 解释 |
---|---|---|---|
Ubuntu | 最新 | docker pull ubuntu**- IS THE SAME AS -docker pull ubuntu:latest docker pull ubuntu-是什么-**docker pull ubuntu:latest | 此命令将拉取最新版本的“ubuntu”镜像。如果没有指定标签,Docker会假设你想要的是“最新”版本。值得记住的是,你并不总是想要“最新的”。这个图像是相当字面上的“最新”的意义上,它将有最新的变化。这可能会修复或损坏您的容器。 |
ubuntu | 22.04 | docker pull ubuntu:22.04docker pull ubuntu:22.04 | 此命令将拉取“ubuntu”映像的“22.04(Jammy)”版本。 |
ubuntu | 20.04 | docker pull ubuntu:20.04Docker pull ubuntu:20.04 | 此命令将拉取“ubuntu”镜像的“20.04(Focal)”版本。 |
ubuntu | 18.04 | docker pull ubuntu:18.04 | 此命令将拉取“ubuntu”映像的“18.04(Bionic)”版本。 |
指定标记时,必须包含冒号 :
在图像名称和标签之间,例如, ubuntu:22.04
不要忘记标签-我们将在未来的任务中返回到这些!
Docker Image x/y/z
docker image
命令,以及适当的选项,允许我们管理本地系统上的映像。要列出可用的选项,我们可以简单地做 docker image
看看我们能做些什么我在下面的终端中为您做了这个:
一个终端,显示我们可以提供的各种参数“docker image”
cmnatic@thm:~$ docker image |
- 在这个房间里,我们只会介绍Docker镜像的以下选项:
- pull (we have done this above!)pull(我们已经在上面做了!)
- ls (list images)ls(列表图像)
- rm (remove an image)
- build (we will come onto this in the “Building your First Container” task)
Docker镜像ls
此命令允许我们列出本地系统上存储的所有图像。我们可以使用此命令来验证图像是否已正确下载,并查看有关它的更多信息(例如标记,图像创建时间和图像大小)。
列出存储在主机操作系统上的Docker镜像的终端
cmnatic@thm:~$ docker image ls |
例如,在上面的终端中,我们可以看到系统上两个图像的一些信息:
Repository | Tag | Image ID | Created | Size |
---|---|---|---|---|
ubuntu | 22.04 | 2dc39ba059dc | 10 days ago | 77.8MB |
nginx | latest | 2b7d6430f78d | 2 weeks ago | 142MB |
Docker镜像rm
如果我们想从系统中删除一个图像,我们可以使用 docker image rm
沿着名称(或映像ID)。在下面的示例中,我将删除“ubuntu“ image with the tag ““带标签的图像“22.04“. “.我的命令是 docker image rm ubuntu:22.04
:
重要的是要记住, tag 图像名称。
一种显示对图像的取消标记的终端,
cmnatic@thm:~$ docker image rm ubuntu:22.04 |
如果我们要运行一个 docker image ls
, 我们将看到该图像不再列出:
确认我们的Docker镜像已被删除的终端
cmnatic@thm:~$ docker image ls |
运行您的第一个容器
Docker run命令从镜像创建运行容器。这是运行来自Dockerfile的命令(以及我们自己在运行时的输入)的地方。正因为如此,它必须是你学习的第一个语法。
该命令的工作方式如下:docker run [OPTIONS] IMAGE_NAME [COMMAND] [ARGUMENTS...]
括号中的选项不是容器运行所必需的。
Docker容器可以使用各种选项运行-这取决于我们如何使用容器。本任务将解释您可能要使用的一些最常见的选项。
首先,简单地运行一个容器
让我们回想一下运行Docker容器所需的语法: docker run [OPTIONS] IMAGE_NAME [COMMAND] [ARGUMENTS...]
。在这个例子中,我将配置 容器运行:
- 一个名为“helloworld”的图像
- 通过在[OPTIONS]命令中提供
-it
开关实现“交互式”。这将允许我们直接与容器交互。 - 我将通过提供
/bin/bash
作为[COMMAND]部分在容器中生成一个shell。这个参数是你放置你想要在容器中运行的命令的地方(比如文件,应用程序或者shell!)
因此,为了实现上述目标,我的命令将如下所示:docker run -it helloworld /bin/bash
一个终端显示一个集装箱在“交互”模式下被启动
cmnatic@thm-intro-to-docker:~$ docker run -it helloworld /bin/bash |
我们可以验证是否成功启动了shell,因为我们的提示符将更改为另一个用户帐户和主机名。容器的主机名是容器ID(可以使用docker ps
找到)。例如,在上面的终端中,我们的用户名和主机名是root@30eff5ed7492
正在运行容器…继续
如前所述,Docker容器可以使用各种选项运行。容器的用途和Dockerfile中的指令集(我们将在后面的任务中讨论)决定了我们需要使用什么选项来运行容器。首先,我将一些运行Docker容器所需的最常见选项放入下表。
[OPTION] | Explanation | 相关Dockerfile说明 | Example |
---|---|---|---|
-d- | 这个参数告诉容器以“分离”模式启动。这意味着容器将在后台运行。 | N/A | docker run -d helloworld |
-it | 这个参数有两个部分。“i”表示交互运行,“t”告诉Docker在容器中运行shell。如果我们希望在容器运行后直接与它交互,我们将使用此选项。 | N/A | docker run -it helloworld |
-v | 这个参数是“Volume”的缩写,它告诉Docker将一个目录或文件从主机操作系统挂载到容器中的某个位置。这些文件的存储位置在Dockerfile中定义 | VOLUME | docker run -v /host/os/directory:/container/directory helloworld |
-p | 这个参数告诉Docker将主机操作系统上的端口绑定到容器中公开的端口。如果您正在容器中运行应用程序或服务(如Web服务器),并希望通过导航到IP地址来访问应用程序/服务,则可以使用此说明。 | EXPOSE | docker run -p 80:80 webserver |
–rm | 这个参数告诉Docker一旦容器完成运行它被指示做的任何事情,就删除容器。 | N/A | docker run --rm helloworld |
–name | 这个参数让我们为容器给予一个友好的、好记的名字。当一个容器在没有这个选项的情况下运行时,名称是两个随机的单词。我们可以使用这个open来命名一个容器,以该容器正在运行的应用程序命名。 | N/A | docker run --name helloworld |
这些只是我们在运行容器时可以提供的一些参数。同样,我们需要运行的大多数参数将由容器的构建方式决定。但是,像--rm
和--name
这样的参数将指示Docker如何运行容器。其他参数包括(但不限于!):
- 告诉Docker容器应该使用哪个网络适配器
- 容器应该访问哪些功能。这是在TryHackMe上的“Docker牛仔竞技表演“室中介绍的。
- 将值存储到环境变量中
如果你想探索更多的这些论点,我强烈建议你阅读Docker run文档。
列出正在运行的容器
要列出正在运行的容器,我们可以使用docker ps命令。此命令将列出当前正在运行的容器-如下所示:
一种显示运行中的容器及其信息的终端
cmnatic@thm:~/intro-to-docker$ docker ps |
此命令还将显示有关容器的信息,包括:
- 容器的ID
- 容器正在运行什么命令
- 容器是什么时候创建的
- 容器运行多久了
- 映射哪些端口
- 容器的名称
提示:要列出所有容器(即使是停止的),您可以使用docker ps -a
:
显示所有集装箱及其信息列表的终端
cmnatic@thm:~/intro-to-docker$ docker ps -a |
Dockerfiles简介
Dockerfiles在Docker中扮演着重要的角色。Dockerfiles是一个格式化的文本文件,本质上是容器应该做什么的指导手册,并最终组装Docker镜像。
您使用Dockerfiles来包含容器在构建时应该执行的命令。要开始使用Dockerfiles,我们需要了解一些基本的语法和说明。Dockerfiles的格式如下:
INSTRUCTION argument |
首先,让我们介绍一些基本的说明:
Instruction 指令 | Description 描述 | Example 例如 |
---|---|---|
FROM | 此指令为容器设置构建阶段以及设置基础映像(操作系统)。所有Dockerfiles都必须以这个开头。 | FROM ubuntu |
RUN | 此指令将在新层的容器中执行命令。 | RUN whoami |
COPY | 此指令将文件从本地系统复制到容器中的工作目录(语法类似于cp 命令)。 |
COPY /home/cmnatic/myfolder/app/ |
WORKDIR | 此指令设置容器的工作目录。(类似于在Linux上使用cd )。 |
WORKDIR / (sets to the root of the filesystem in the container) |
CMD | 此指令确定容器启动时运行的命令(您可以使用此命令启动服务或应用程序)。 | CMD /bin/sh -c script.sh |
EXPOSE | .此指令用于告诉运行容器的人在运行容器时应该发布什么端口。 | EXPOSE 80(告诉运行容器的人发布到端口80,即docker run -p 80:80 ) |
现在我们理解了组成Dockerfile的核心指令,让我们看一个Dockerfile的工作示例。但首先,我将解释我想要容器做什么:
- 使用“Ubuntu”(版本22.04)操作系统作为基础。
- 将工作目录设置为容器的根目录。
- 创建文本文件“helloworld.txt”。
# THIS IS A COMMENT |
请记住,您可以通过RUN
指令运行的命令将取决于您在FROM
指令中使用的操作系统。(In在这个例子中,我选择了Ubuntu。重要的是要记住,容器中使用的操作系统通常非常少。也就是说,don’t expect a command to be there from the start (even commands like 可能需要安装)。
构建您的第一个容器
一旦我们有了Dockerfile,我们就可以使用docker build
命令创建一个镜像。此命令需要几条信息:
- 无论您是否想自己命名图像(我们将使用
-t
(标记)参数)。 - 您要为图像给予的名称。
- 您希望使用的Dockerfile的位置。
我将提供场景,然后解释相关的命令。假设我们想要构建一个图像-让我们填写上面列出的两条必需信息:
- 我们将自己命名它,所以我们将使用
-t
参数。 - 我们想给这个图像命名。
- Dockerfile位于我们当前的工作目录(
.
)。
我们要构建的Dockerfile如下:
# Use Ubuntu 22.04 as the base operating system of the container |
命令看起来像这样: docker build -t helloworld .
正在使用点来告诉Docker在我们的工作目录中查找)。如果我们正确填写了命令,我们将看到Docker开始构建镜像:
展示“helloworld”图像构建过程的终端
cmnatic@thm:~$ docker build -t helloworld . |
好极了!看上去很成功。让我们使用docker image ls
来看看这个镜像是否已经构建好了:
使用“docker image ls”命令确认我们的镜像是否已经成功构建
cmnatic@thm:~$ docker image ls |
Dockerfile中的指令也将被下载。这就是为什么我们可以看到两个图像:
- helloworld (our image)
- ubuntu(我们图像中使用的基本操作系统)。
您现在可以在容器中使用此映像。请参阅“运行您的第一个容器”任务以提醒您如何启动容器。
升级我们的Dockerfile
让我们升级我们的Dockerfile。到目前为止,我们的容器只会创建一个文件-这不是很有用!在下面的Dockerfile中,我将:
- 使用Ubuntu 22.04作为容器的基本操作系统。
- 安装“apache2”Web服务器。
- 增加一些网络。由于这是一个Web服务器,我们需要能够通过网络连接到容器。我将通过使用
EXPOSE
指令并告诉容器暴露端口80来实现这一点。 - 告诉容器在启动时启动“apache 2”服务。容器没有像
systemd
这样的服务管理器(这是设计的-在同一个容器中运行多个应用程序是不好的做法。例如,此容器用于apache 2 web服务器-并且仅用于apache 2 web服务器)。
# THIS IS A COMMENT |
作为参考,构建此文件的命令是docker build -t webserver .
(假设Dockerfile与您运行命令的目录相同)。一旦使用适当的选项(docker run -d --name webserver -p 80:80 webserver
)启动容器,我们就可以在浏览器中导航到本地机器的IP地址!
WEB服务器工作!目前,Apache 2正在提供默认文件,因为我们还没有将自己的文件添加到容器中。
优化我们的Dockerfile
Docker当然是一门艺术-而且它不会停止于Dockerfiles!首先,我们需要问自己为什么优化我们的Dockerfile是必要的?臃肿的Dockerfiles很难阅读和维护,并且经常使用大量不必要的存储空间!例如,你可以减少Docker镜像的大小(并减少构建时间!)使用几种方法:
- 只安装必要的软件包。容器的好处在于,它们从一开始就几乎是空的–我们有完全的自由来决定我们想要什么。
- 删除缓存文件(如APT缓存或随工具安装的文档)。容器中的代码只执行一次(在构建时!),所以我们不需要储存任何东西以备后用。
- 在我们的
FROM
指令中使用最小的基本操作系统。尽管像Ubuntu这样的容器操作系统已经非常苗条了,但还是考虑使用更精简的版本(即ubuntu:22.04-minimal
)。或者,例如,使用Alpine(可以小到5.59MB!)。 - 最小化层数-我将在下面进一步解释。
每个指令(即FROM
、RUN
等)在自己的层中运行。层增加构建时间!目标是尽可能少的层。例如,尝试将来自RUN
的命令链接在一起,如下所示:
Before:
FROM ubuntu:latest |
显示正在构建的Dockerfile的五个层的终端
cmnatic@thm:~$ docker build -t before . |
After:
FROM ubuntu:latest |
显示正在构建的Dockerfile的两层的终端
cmnatic@thm:~$ docker build -t after . |
请注意,现在只有两个构建步骤(这将是两个层,使构建更快)。这只是Dockerfile的一个小例子,所以构建时间不会那么激烈,但是在更大的Dockerfile中-减少层的数量将在构建过程中获得惊人的性能提升。
Docker Compose介绍
让我们首先了解Docker Compose是什么以及为什么它值得理解。到目前为止,我们只与单独的容器进行了交互。总之,Docker Compose允许多个容器(或应用程序)在需要时相互交互,同时彼此隔离运行。
到目前为止,你可能已经注意到Docker的一个问题。通常情况下,应用程序需要额外的服务才能运行,而我们无法在单个容器中完成这些工作。例如,现代动态网站使用诸如数据库和Web服务器之类的服务。为了完成这项任务,我们将把每个应用程序都视为一个“微服务”。
虽然我们可以单独启动多个容器或“微服务”并将它们连接起来,但一个接一个地这样做既麻烦又低效。Docker Compose允许我们将这些“微服务”创建为一个单一的“服务”。
此插图显示了如何使用Docker Compose与Docker一起部署容器:
在演示Docker Compose之前,让我们先介绍一下使用Docker Compose的基础知识。
- Docker Compose(默认情况下Docker不附带)。安装它超出了本会议室的范围,因为它会根据您的操作系统和其他因素而变化。您可以查看安装文档 here这里.
- 我们需要一个有效的docker-compose.yml文件-我们很快就会处理这个问题。
- 基本了解使用Docker Compose构建和管理容器。
我将一些基本的Docker Compose命令放入下表:
Command | Explanation | Example |
---|---|---|
up | 此命令将(重新)创建/构建并启动组合文件中指定的容器。 | docker-compose up docker run --rm helloworld |
start | 此命令将启动(但需要已构建的容器)组合文件中指定的容器。 | docker-compose start |
down | 此命令将停止并删除在编写文件中指定的容器。 | docker-compose down |
stop | 此命令将停止(而不是删除)组合文件中指定的容器。 | docker-compose stop |
build | 此命令将构建(但不会启动)组合文件中指定的容器。 | docker-compose build |
注意:这些只是一些可能的命令。查看撰写文档了解所有可能的选项。*
Docker Compose展示
说到这里,让我们来看看如何使用Docker Compose。在这种情况下,我将假设以下要求:
- 在Apache上运行的电子商务网站
- 本电子商务网站将客户信息存储在MySQL数据库中
现在,我们可以通过以下方式手动运行两个容器:
- 在两个容器之间创建网络:
docker network create ecommerce
- 运行Apache2 Web服务器容器:
docker run -p 80:80 --name webserver --net ecommerce webserver
- 运行MySQL数据库服务器:
docker run --name database --net ecommerce webserver
下图显示了两个容器彼此独立运行,并且无法相互通信。
但我们每次都要这样吗或者,如果我们决定扩大规模,让许多Web服务器参与进来呢?我们是否要对每个容器每次都这样做?我当然不知道
相反,我们可以通过docker-compose up
使用Docker Compose来一起运行这些容器,这给我们带来了以下优势:
- 一个简单的命令来运行它们
- 这两个容器联网在一起,所以我们不需要配置网络。
- 非常便携。我们可以与其他人共享docker-compose.yml文件,他们可以让设置工作完全相同,而无需了解容器如何单独工作。
- 易于维护和更换。我们不必担心特定的容器使用(可能是过时的)图像。
显示作为组合服务部署的两个容器的插图。这两个容器可以相互通信。
Docker-compose.yml files 101
一个文件来管理所有文件。docker-compose.yml文件的格式与Dockerfile的格式不同。需要注意的是,YAML需要缩进(一个好的做法是两个空格必须一致!)。首先,在我们开始创建docker-compose.yml文件之前,我
Instruction | Explanation | Example |
---|---|---|
version | 它位于文件的顶部,用于标识docker-compose.yml是为哪个版本的Compose编写的。 | ‘3.3’ |
services | 此指令标记要管理的容器的开始。 | services: |
name (replace value) | 此指令是定义容器及其配置的地方。“name”需要替换为您要定义的容器的实际名称,即“webserver”或“database”。 | webserver |
build | 此指令定义包含此容器/服务的Dockerfile的目录。(you将需要使用此或图像)。 | ./webserver |
ports | 此指令将端口发布到暴露的端口(这取决于image/Dockerfile)。 | ‘80:80’ |
volumes | 此指令列出了应该从主机操作系统装入容器的目录。 | ‘./home/cmnatic/webserver/:/var/www/html’’ |
environment | 此指令用于传递环境变量(不安全),即密码,用户名,时区配置等。 | MYSQL_ROOT_PASSWORD=helloworld |
image | .这个指令定义了容器应该使用什么镜像来构建(你需要使用this或者build)。 | mysql:latest |
networks | 此指令定义容器将成为哪些网络的一部分。容器可以是多个网络的一部分(即Web服务器只能连接一个数据库,但数据库可以连接多个Web服务器)。 | ecommerce |
注意:这些只是一些可能的指令。查看compose文件文档以获取所有可能的说明。*
说到这里,让我们看看我们的第一个docker-compose.yml文件。这个docker-compose.yml文件假设如下:
- 我们将从前面提到的场景中运行一个Web服务器(名为Web)。
- 我们将从前面提到的场景中运行一个数据库服务器(命名为database)。
- Web服务器将使用其Dockerfile构建,但我们将使用已经构建的数据库服务器(MySQL)映像
- 集装箱将联网以相互通信(该网络称为电子商务)。
- 我们的目录列表如下所示:
- docker-compose.yml
- web/DockerfileWeb/Dockerfile
下面是我们的docker-compose.yml文件的样子(提醒一下,必须注意缩进):
version: '3.3' |
Docker Socket简介
本任务将解释Docker如何在操作系统和容器之间进行交互。当你安装Docker时,会安装两个程序:
- The Docker Client
- The Docker Server
Docker采用客户端/服务器模式。具体来说,这两个程序相互通信,形成了我们所熟悉和喜爱的Docker。Docker使用一种叫做socket的东西来实现这种通信。套接字是操作系统的一个基本特征,它允许数据进行通信。
例如,当使用聊天程序时,可能有两个套接字:
- 用于存储您要发送的消息的套接字
- 用于存储某人发送给您的消息的套接字。
程序将与这两个套接字交互以存储或检索其中的数据!套接字可以是网络连接或表示为文件的内容。关于套接字,重要的是要知道它们允许进程间通信(IPC)。这仅仅意味着操作系统上的进程可以相互通信!
在Docker的上下文中,Docker Server实际上只是一个API。Docker Server使用此API来侦听请求,而Docker Client使用API来发送请求。
例如,让我们使用这个命令:docker run helloworld
。Docker客户端将请求Docker服务器运行一个使用镜像“helloworld”的容器。现在,虽然这个解释是相当基本的,但它是Docker如何工作的基本前提。
让我们看看下面的图表来展示这个过程的实际情况:
有趣的是,正因为如此,我们可以使用curl
这样的命令或API开发人员工具(如Postman)与Docker Server交互。现在,使用这个超出了这个房间的范围,但我将演示使用Postman与Docker Server通信,以列出存储在操作系统上的所有映像:
最后,重要的是要注意,正因为如此,运行Docker的主机可以配置为处理从另一个设备发送的命令。如果没有正确配置,这是一个非常危险的漏洞,因为这意味着有人可以远程停止,启动和访问Docker容器。尽管如此,在一些用例中,Docker的这个功能非常有用!我们将在稍后的会议室中进一步详细介绍这一点!