什么是Docker?
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。 一个完整的Docker有以下几个部分组成:
- DockerClient客户端
- Docker Daemon守护进程
- Docker Image镜像
- DockerContainer容器
什么是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
删除所有的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为xenial
的ubuntu: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>