Docker入门

108 阅读6分钟

什么是Docker?

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。 一个完整的Docker有以下几个部分组成:

  1. DockerClient客户端
  2. Docker Daemon守护进程
  3. Docker Image镜像
  4. DockerContainer容器

Docker菜鸟教程

什么是Docker映像?

Docker映像是一个文件。该映像文件本身是由一个名为“Dockerfile”的说明文件构建的。映像永不改变;不能编辑现有文件,但可以为其创建一个新的层。

什么是Docker容器?

容器仅包含执行应用程序所需的内容;可以开始,停止和与他们互动。它们是主机中的隔离环境,能够通过定义的方法(TCP/UDP)与彼此和主机本身进行交互。

图片与容器

容器是映像的实例。

烹饪比喻

Dockerfile是购物清单(或配方)。映像是原材料,而容器是美味佳肴。 要获取映像,必须使用Dockerfile构建它。然后,运行映像以创建一个容器。 因此,也许更合适的隐喻是映像是冷冻的,预先煮好的饭。

基本Docker命令

要列出当前下载的图像,运行:

docker images

要列出正在运行的容器,运行:

docker container ls

列出所有容器:

docker container ls -a

当我们有很多不同的容器时,可以使用grep(或其他类似的实用程序)来过滤列表:

$ docker container ls -a | grep hello-world

删除容器:

docker rm name/ID

在没有重复的情况下,该命令还可以使用ID的前几个字符。例如,如果容器的ID为3d4bab29dd67,则可以使用以下命令删除容器:

docker rm 3d

还可以使用多个参数:

docker rm id1 id2 id3

如果有数百个已停止的容器,并且希望将其全部删除,则应使用:

docker container prune

关于prune命令

删除所有的hello-world容器后,运行docker rmi hello-world来删除该映像。

可以用docker images用来确认未列出的映像。

还可以使用pull命令下载映像而不运行它们:

docker pull hello-world

-d参数

让我们尝试启动一个新的容器:

$ docker run nginx

注意,在pull并启动容器后,命令行不动了。这是因为Nginx现在正在当前终端中运行,从而阻止了输入。通过按退出control + c并再次尝试使用该-d参数。

$ docker run -d nginx
  c7749cf989f61353c1d433466d9ed6c45458291106e8131391af972c287fb0e5

-d参数意味着启动一个分离的容器,这意味着它在后台运行。 需要先停止容器docker stop <container id or name>,再用rm删除。 强制删除也是一种可能,docker rm --force <container id or name>在这种情况下我们可以安全使用。

最常用的命令

docker container ls -a	列出所有容器
docker images	列出所有图片
docker pull <image>	从名为docker hub的docker注册表中提取图像
docker rm <container-id>	删除容器,您可以使用容器名称或ID
docker rmi <image-id>	删除图像,您可以使用图像名称或ID
docker stop <container-id>	停止容器,可以使用容器名称或ID
docker run <image>	运行创建容器的图像,可以使用图像名称或ID
docker exec <container-id>	在容器内执行命令 

详细查看映像

可以标记图像以保存同一图像的不同版本。您可以通过:<tag>在图像名称后添加来定义图像的标签。

Ubuntu的Docker Hub页面显示了一个名为16.04的标签,该标签向我们保证该映像基于Ubuntu 16.04。

$ docker pull ubuntu:16.04 

为了方便起见,我们还可以在本地标记图像,例如,docker tag ubuntu:16.04 ubuntu:xenial创建了一个tag为xenialubuntu:16.04版本映像。 现在,我们可以创建一个新的Ubuntu容器,并uptime通过运行以下命令来执行该命令docker run fav_distro:xenial uptime

运行和停止容器

让我们在后台运行一个容器:

docker run -d --name looper ubuntu:16.04 sh -c 'while true; do date; sleep 1; done'

将其取名为looper 查看log输出:

$ docker logs -f looper
Thu Oct 29 20:41:20 UTC 2020
Thu Oct 29 20:41:21 UTC 2020
Thu Oct 29 20:41:22 UTC 2020

在另一个终端运行

docker pause looper

docker停止运行,日志停止输出; 让docker重新运行,日志重新开始输出:

docker unpause looper

使日志保持打开状态,并使用“ attach”从第二个终端附加到正在运行的容器:

$ docker attach looper 
Thu Oct 29 20:45:44 UTC 2020
Thu Oct 29 20:45:45 UTC 2020
Thu Oct 29 20:45:46 UTC 2020

在附加的窗口中按Control + c。容器已停止,因为该进程不再运行。

如果要连接到容器,同时确保不从其他终端关闭容器,则可以禁用信号代理。让我们从启动已停止的容器,docker start looper并通过附加到它--sig-proxy=false

然后尝试按Control + C。

$ docker attach --sig-proxy=false looper 
Thu Oct 29 20:44:32 UTC 2020
Thu Oct 29 20:44:33 UTC 2020
Thu Oct 29 20:44:34 UTC 2020

容器将继续运行。Control + c现在仅让输出断开连接。

运行:

$ docker exec -it looper bash 
root@427145c7ac1b:/# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.0   4504  1648 ?        Ss   20:55   0:00 sh -c while true; do date; sleep 1; done
root        12  0.1  0.1  18308  3316 pts/0    Ss   20:55   0:00 bash
root       107  0.0  0.0   4376   668 ?        S    20:56   0:00 sleep 1
root       108  0.0  0.1  34424  2796 pts/0    R+   20:56   0:00 ps aux

参数-it-i-t的缩写,-it表示可以通过命令行和容器进行交互。 我们可以通过使用终止进程来终止容器kill 1,或者使用exit然后终止或停止容器。

$ docker kill looper 
$ docker rm looper 

运行前两个命令基本上等同于运行 docker rm --force looper

$ docker run -d --rm -it --name looper-it ubuntu:16.04 sh -c 'while true; do date; sleep 1; done'

--rm参数表示停止容器后自动将其删除,所以再使用docker start也不能用来启动容器。

进入容器

docker exec -it <docker name or ID> bash
$ docker exec -it d58d9519933e bash
root@d58d9519933e:/usr/app# tail -f ./logs.txt
"Docker is easy"
Thu, 29 Oct 2020 22:15:20 GMT
Thu, 29 Oct 2020 22:15:23 GMT
Thu, 29 Oct 2020 22:15:26 GMT
Thu, 29 Oct 2020 22:15:29 GMT
Secret message is:
"Docker is easy"
Thu, 29 Oct 2020 22:15:35 GMT
Thu, 29 Oct 2020 22:15:38 GMT
Thu, 29 Oct 2020 22:15:41 GMT
Thu, 29 Oct 2020 22:15:44 GMT

##Creating your very own dockerized project

FROM ubuntu:16.04 

WORKDIR /mydir 
RUN apt-get update && apt-get install -y wget 

RUN touch hello.txt 
COPY local.txt . 
RUN wget http://example.com/index.html 
  • WORKDIR将在此指令之后创建并将当前工作目录设置为/mydir
  • RUN将执行带有/bin/sh -c前缀的命令-因此,WORKDIR该命令本质上与RUN touch /mydir/hello.txt
  • COPY将现有的本地文件复制到第二个参数(在我们的示例中,它将复制到映像当前目录/ mydir)。最好是在添加文件时使用,COPY而不要使用它ADD(ADD附加了各种魔术行为)
  • CMD是使用时将执行的命令docker run。任何Dockerfile都应该只有一条CMD指令,反之,如果有多于1条CMD指令,则只有最后一条有效,而docker守护程序将忽略其他一条。CMD在docs.docker.com上了解更多信息。

然后,我们通过运行带有context参数的build命令来构建它.,这意味着我们必须位于同一目录中(我们可以从另一个目录运行此构建,然后在此处提供路径)

$ docker build . 

我们图像的随机名称也不是理想的,因为现在我们需要分别docker tag 66b527252f32 myfirst为其指定一个合理的名称,因此让我们再次构建它以对其进行标记:

$ docker build -t myfirst . 

现在运行我们的图像。

$ docker run -it myfirst 
  root@accf99660aeb:/mydir# ls 
  hello.txt  index.html  local.txt 

我们的WORKDIR上一个设置为,/mydir因此继承的bash命令在该目录中启动。还要注意我们的主机名如何accf99660aeb等于容器ID。在退出容器之前,让我们创建一个文件(除了由创建的文件Dockerfile)

$ touch manually.txt 
$ exit 

现在,我们可以使用diff比较图像myfirst和容器之间的更改:

$ docker diff accf

  C /mydir 
  A /mydir/manually.txt 
  C /root 
  A /root/.bash_history 

文件名前面的字符表示容器文件系统中更改的类型:A =已添加,D =已删除,C =已更改。

我们发现,除了我们的manually.txt文件外,还bash“秘密”创建了一个历史文件。我们可以通过以下更改创建新图像(myfirst+ changes = newimage)

$ docker commit accf99660aeb myfirst-pluschanges 

Volumes: bind mount

通过将主机(我们的机器)文件夹绑定到容器,我们可以将文件直接发送到我们的机器。用-v选项开始另一个运行,它需要一个绝对路径。我们在容器中挂载当前文件夹为/mydir,覆盖我们在 Dockerfile 中放置在该文件夹中的所有内容。

$ docker run -v "$(pwd):/mydir" youtube-dl https://imgur.com/JY5tHqr

因此,volume只是在主机和容器之间共享的文件夹(或文件)。如果volume中的文件被容器内运行的程序修改,则当容器关闭时,由于文件存在于主机上,更改也不会被破坏。这是volume的主要用途,否则在重新启动容器时将无法访问所有文件。volume还可用于在容器之间共享文件并运行能够加载更改文件的程序。

在我们的 youtube-dl 中,我们想挂载整个目录,因为文件的命名相当随机。如果我们希望创建一个只有一个文件的卷,我们也可以通过指向它来实现。例如,-v $(pwd)/material.md:/mydir/material.md我们可以通过这种方式在本地编辑 material.md 并在容器中更改它(反之亦然)。另请注意,-v如果文件不存在,则会创建一个目录。

从外界打开到Docker容器的连接需要两个步骤:

  • Exposing port
  • Publishing port

Exposing port 意味着您告诉Docker容器侦听了某个端口。

Publishing port意味着Docker将容器端口映射到主机(您的机器)端口。

To expose a port, add line EXPOSE <port> in your Dockerfile

To publish a port, run the container with

 -p <host-port>:<container-port>

例如:某个应用程序使用端口4567接受udp连接。

EXPOSE 4567在Dockerfile中,将允许从映像创建的容器接受连接。可以说图片名称是“ app-in-port”

$ docker container run -p 1234:4567 app-in-port

现在,您可以连接到主机端口1234(例如http:// localhost:1234),它将被映射到应用程序端口。

如果您忽略了主机端口,仅指定了容器端口,则docker将自动选择一个空闲端口作为主机端口:

$ docker container run -p 4567 app-in-port

在Docker Hub中发布项目

转到hub.docker.com/创建一个帐户。您可以配… hub为您构建映像,但也可以使用push。

让我们发布youtube-dl image。登录并导航到您的仪表板,然后按创建存储库。命名空间可以是您的个人帐户或组织帐户。现在,让我们坚持个人帐户,并在存储库名称中写一些诸如youtube-dl之类的描述性内容。我们需要在第2部分中记住它。

接下来,我们需要重命名image:

$ docker image tag youtube-dl <username>/<repositoryname>

最后,我们需要通过登录来验证我们的推送:

$ docker login

现在推送应该可以正常工作:

$ docker image push <username>/<repositoryname>

用Docker运行react app:

Dockerizing a React App