剧透:这篇文章是一个手把手的入门教程,所以准备好一杯茶和一张舒服的板凳,让我们开始创建一些容器吧。今天我们将学习Docker的基础知识,学习如何构建、运行和删除容器。
还记得那句名言吗:这能跑在我的机器上吗?相信每个开发人员都有 被环境的问题折磨得头疼的可怕经历吧?
有了Docker,现在你可以忘记这句话了。Docker允许您在本地运行将在生产环境中使用的相同(或几乎相同)的环境。
我们先理解一下Docker是什么和不是什么。
Docker是什么?
Linux容器技术很早就有了,比较有名的是Linux内核中的LXC项目。
Docker是一个基于LXC技术构建的容器引擎,使用Go语言开发,是一种执行操作系统级虚拟化(也称为“容器化”)的计算机程序。
不仅如此,Docker是一种流行的工具,可简化使用容器构建,部署和运行应用程序的过程。容器使我们可以打包应用程序需要的所有东西,例如库和其他依赖项,并将它们作为单个包打包。这样,我们的应用程序可以在任何机器上运行并且具有相同的行为。
Docker不是什么
Docker不是虚拟机(VM)。
与虚拟机不同,Docker容器不需要或包含单独的操作系统。相反,它依靠内核的功能,并对CPU和内存使用资源隔离,并使用单独的命名空间隔离应用程序对操作系统的视图。
根据上面的简短描述,下图显示了虚拟机与Docker容器之间的比较。
如我们所见,Docker容器比虚拟机更简单,使用它我们可以避免启动和维护VM的开销。由于这是一个动手教程,因此我不会深入探讨Docker的工作原理。
基本概念
镜像
Docker
的镜像概念类似于虚拟机里的镜像,是一个只读的模板,也是一个独立的文件系统,包括运行容器所需的代码和数据,用来创建新的容器。
镜像一般使用 Dockerfile
来构建, Dockerfile
是一个描述文件,里面包含若干条命令,每条命令都会在基础镜像的文件系统之上添加新的层级。
除了通过Dockerfile构建镜像,也可以直接从 DockerHub
上下载镜像别人已经创建好的镜像。
容器
Docker
容器是由 Docker
镜像创建的运行实例。Docker
容器类似虚拟机,每个容器间是相互隔离的,但隔离的效果比不上虚拟机。容器中会运行某个应用,包含应用代码及其依赖(操作系统、第三方库等)。
在 Docker
容器中,容器之间的隔离是通过 Linux
的 CGroups
和 Namespaces
技术来实现的。其中 CGroups
实现了对 CPU
,内存,磁盘等资源访问进行限制, Namespaces
实现了命名空间上的隔离。
仓库
Docker
仓库需要托管在某个 Registry,Registry 不止一个,可以是公共的,也可以是私有的。最大的公共 Registry 是 Docker
官方提供的 Docker Hub。还可以使用 Docker
官方提供的 registry
镜像来搭建一个自己的 Registry。
仓库支持的操作类似 git
,创建了新的镜像后,可以 push
推送镜像到仓库,也可以 pull
拉取镜像到本地。
动手实践
现在我们已经了解了什么是Docker,让我们开始动手实践。
安装
由于安装取决于您的操作系统,因此我们不会在本教程中介绍它。要在您的操作系统上安装Docker,请遵循官方文档
Install Docker for Mac
Install Docker for Mac
Get Docker CE for Ubuntu
这里我用的是阿里的源
代码语言:javascript复制$ curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
另外Docker官方镜像仓库国内访问比较慢,这里也改用阿里云提供的镜像服务。首先,我们需要编辑 /etc/docker/daemon.json
文件(如果没有则新建),加入如下内容:
{
"registry-mirrors": ["https://n6syp70m.mirror.aliyuncs.com"]
}
修改之后,需要重启 docker
服务来让修改生效:
$ sudo service docker restart
Hello World
正确安装并运行Docker后,让我们开始创建容器。Docker中的 HelloWorld
很简单:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.
...
如你所见,它显示以下消息:
无法在本地找到映像
hello-world:latest
这意味着您在本地没有名为 hello-world的映像,因此它将自动从Docker仓库中提取。拉出hello-world
映像后,它将运行容器并显示带有来自其他信息的HellofromDocker!
消息。
恭喜,您已经在Docker中运行了"Hello World”!
创建和运行容器
既然我们已经创建了“ Hello World”示例,并且基本上了解了什么是Docker仓库,那么让我们来创建一些更复杂的东西。让我们在Python中创建一个简单的Flask应用程序。在这一点上,有一点Python和Flask的经验是最好的,但是不用担心,我们只需要知道它是Flask中的“ Hello World” Web应用程序即可。它将从容器内部在localhost上运行。首先,我们需要创建一个名为 my_web_app
的目录,并将以下代码保存在名为 app.py
的文件中。
from flask import Flask
app = Flask(__name__)@app.route("/")
def hello():
return "Hello World!"if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
这段Python代码将导入Flask软件包,创建Flask app
,定义路由并在本地主机上以调试模式运行该应用程序。因此,让我们开始创建Dockerfile。为此,只需在Python文件的同一目录中创建一个名为 Dockerfile
的文件,并将以下命令放入其中:
# Inherit from the Python Docker image
FROM python:3.7-slim
# Install the Flask package via pip
RUN pip install flask==1.0.2
# Copy the source code to app folder
COPY ./app.py /app/
# Change the working directory
WORKDIR /app/
# Set "python" as the entry point
ENTRYPOINT ["python"]
# Set the command as the script name
CMD ["app.py"]
正如我们从注释中看到的那样,它将继承自Python Docker image,安装Flask软件包,将应用程序的源代码复制到app文件夹,将工作目录更改为 app
文件夹,并设置 ENTRYPOINT
和 CMD。
现在,使用 -t
标志(即 tag
)从构建镜像,并设置名称( flask_app
)和标签( 0.1
):
$ docker build -t flask_app:0.1 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flask_app 0.1 1aa01015fcfd 11 minutes ago 188MB
python 3.7-slim 84de2ffd919d 7 days ago 179MB
通过输入 docker image
,我们可以看到该 Image
已成功创建。然后,运行容器,指定要映射的端口(-p),并使用 -d
标志(表示 detached
),以免终端卡住。我们还必须将 Image
的名称和标签作为参数( flask_app:0.1
)传递。
打开浏览器,转到 localhost:5000
地址,然后打开voilà,我们正在访问在容器内运行的Web应用程序。
停止和移除容器
由于我们的容器仍在运行,因此可以在删除之前将其停止。为此,我们只需要运行docker stop即可,将容器名称作为参数传递,例如:
代码语言:javascript复制$ docker stop interesting_nash
我们还可以通过使用容器ID来停止容器,例如:
代码语言:javascript复制$ docker stop 4a865da83995
如果我们现在运行 docker ps-a
,我们将看到该容器处于该列表中,状态为Exited。让我们删除容器,然后删除镜像。类似于 docker stop
命令,我们可以使用 docker rm
命令传递容器名称或ID作为参数,例如:
$ docker rm interesting_nash
# or
$ docker rm 4a865da83995
如果我们运行 docker ps-a
现在我们将看到我们的容器从列表中消失了。如果我们运行“ docker images”命令,我们将在列表中看到带有标签 0.1
的 flask_app
。要删除我们的 flask_app
镜像,我们可以使用 docker rmi
命令,例如:
$ docker rmi flask_app:0.1
如果再次运行 docker images
,我们将看到 flask_app
镜像已被删除。
常见命令
在本节中,你可以找到一些基本的Docker命令,例如build,run,和exec。
代码语言:javascript复制# 构建一个Docker镜像
$ docker build -t [image_name]:[tag] .
# 运行一个指定名称的Docker容器
$ docker run --name [container_name] [image_name]:[tag]
# 提取容器的日志
$ docker logs -f [container_id_or_name]
# 在正在运行的容器中运行命令
$ docker exec -it [container_id_or_name] bash
# 显示正在运行的容器
$ docker ps
# 显示所有容器
$ docker ps -a
# 显示所有的Docker镜像
$ docker images
# 停止一个Docker容器
$ docker stop [container_id_or_name]
# 删除一个Docker容器
$ docker rm [container_id_or_name]
# 删除一个Docker镜像
$ docker rmi [image_id_or_name]