Docker:它是什么以及如何与 Python 一起使用它
更快的开发流程
便捷的应用程序封装
本地机器/开发/暂存/生产服务器上的行为相同
轻松清晰的监控
易于扩展
支持的平台
安装
术语
创建图像的最佳实践
阿尔卑斯山图像
Docker 方式
这是一篇关于 Docker 容器的入门教程。读完本文后,您将了解如何在本地机器上使用 Docker。除了 Python 之外,我们还将运行 Nginx 和 Redis 容器。这些示例假设您熟悉这些技术的基本概念。文中会包含大量的 Shell 示例,所以请直接打开终端运行。
什么是 Docker?
Docker是一个开源工具,它可以自动在软件容器内部署应用程序。理解 Docker 背后理念最简单的方法就是将它与……标准的集装箱进行比较。
过去,运输公司面临以下挑战:
- 如何并排运输不同(不兼容)类型的货物(如食品和化学品,或玻璃和砖块)。
- 如何使用同一辆车处理各种尺寸的包裹。
集装箱出现后,砖块可以放在玻璃上,化学品可以存放在食品旁边。各种尺寸的货物都可以装入标准化集装箱,并由同一车辆装卸。
让我们回到软件开发中的容器。
当您开发应用程序时,您需要提供您的代码以及所有可能的依赖项,如库、Web 服务器、数据库等。您最终可能会遇到这样的情况:应用程序在您的计算机上运行,但甚至无法在登台服务器、开发人员或 QA 的机器上启动。
可以通过隔离应用程序使其独立于系统来解决这一挑战。
这与虚拟化有何不同?
传统上,人们使用虚拟机来避免这种意外行为。虚拟机的主要问题是,在主机操作系统之上添加一个“额外的操作系统”会给项目增加数GB的空间。大多数情况下,您的服务器会托管多个虚拟机,这会占用更多空间。顺便说一句,目前大多数云服务器提供商都会对这些额外的空间收费。虚拟机的另一个显著缺点是启动速度慢。
Docker 通过在所有作为主机操作系统的单独进程运行的容器之间共享操作系统内核来消除上述所有问题。
请记住,Docker 并非第一个,也不是唯一的容器化平台。然而,目前 Docker 是市场上规模最大、实力最强的参与者。
为什么我们需要 Docker?
其优点包括:
- 更快的开发流程
- 便捷的应用程序封装
- 本地机器 / dev / staging / production 服务器上的行为相同
- 轻松清晰的监控
- 易于扩展
更快的开发流程
无需在系统上安装 PostgreSQL、Redis、Elasticsearch 等第三方应用程序——您可以在容器中运行它。Docker 还允许您同时运行同一应用程序的不同版本。例如,假设您需要手动将数据从旧版本的 Postgres 迁移到新版本。在微服务架构中,当您想使用新版本的第三方软件创建新的微服务时,就可能会遇到这种情况。
在同一个主机操作系统上维护同一个应用程序的两个不同版本可能非常复杂。在这种情况下,Docker 容器可能是一个完美的解决方案——您可以为您的应用程序和第三方提供隔离的环境。
便捷的应用程序封装
您可以完整地交付您的应用程序。大多数编程语言、框架和所有操作系统都有自己的打包管理器。即使您的应用程序可以使用其原生的包管理器打包,为其他系统创建移植版本也可能很困难。
Docker 为您提供统一的镜像格式,以便跨不同的主机系统和云服务分发应用程序。您可以以单一方式交付应用程序,并准备好运行所有必需的依赖项(包含在镜像中)。
本地机器/开发/暂存/生产服务器上的行为相同
Docker 无法保证 100% 的开发/预发布/生产一致性,因为人为因素总是存在的。但它可以将因操作系统版本差异、系统依赖性等因素导致错误的概率降低到几乎为零。
通过正确的方法构建 Docker 镜像,您的应用程序将使用具有相同操作系统版本和所需依赖项的相同基础镜像。
轻松清晰的监控
开箱即用,您可以通过统一的方式从所有正在运行的容器中读取日志文件。您无需记住应用及其依赖项存储日志文件的所有具体路径,也无需编写自定义钩子来处理这些操作。
您可以集成外部日志驱动程序,并在一个地方监控应用日志文件。
易于扩展
正确包装的应用程序将涵盖十二要素中的大部分内容。Docker 的设计强制您遵循其核心原则,例如通过环境变量进行配置、通过 TCP/UDP 端口进行通信等。如果您正确构建了应用程序,它不仅可以在 Docker 中进行扩展。
支持的平台
Docker 的原生平台是 Linux,因为它基于 Linux 内核提供的功能。不过,你仍然可以在 macOS 和 Windows 上运行它。唯一的区别在于,在 macOS 和 Windows 上,Docker 被封装在一个微型虚拟机中。目前,macOS 和 Windows 版 Docker 的可用性已达到相当高的水平,使用体验更像是一个原生应用。
安装
您可以在此处查看 Docker 的安装说明。
如果您在 Linux 上运行 Docker,则需要以 root 身份运行以下所有命令,或者将您的用户添加到 docker 组并重新登录:
sudo usermod -aG docker $(whoami)`
术语
- 容器– 一个封装了所需软件的运行实例。容器通常由镜像创建。容器可以暴露端口和卷,以便与其他容器或/以及外部世界进行交互。容器可以在很短的时间内轻松终止/移除并重新创建。容器不保存状态。
- 镜像– 每个容器的基本元素。创建镜像时,每个步骤都会被缓存并可重复使用(即写即复制模型)。根据镜像的不同,构建可能需要一些时间。而容器则可以立即从镜像启动。
- 端口– 其原始含义为 TCP/UDP 端口。为了简单起见,我们假设端口可以暴露给外部世界(可从主机操作系统访问)或连接到其他容器——即只能从这些容器访问,而对外部世界不可见。
- 卷– 可以理解为一个共享文件夹。卷在容器创建时初始化。卷旨在独立于容器的生命周期来持久保存数据。
- Registry——存储 Docker 镜像的服务器。它可以与 Github 进行比较——你可以从 Registry 中提取镜像并将其部署到本地,也可以将本地构建的镜像推送到 Registry。
- Docker Hub – Docker 公司提供的带有 Web 界面的镜像仓库。它存储了大量不同软件的 Docker 镜像。Docker Hub 是 Docker 团队或与原始软件制造商合作制作的“官方”Docker 镜像的来源(但这并不意味着这些“原始”镜像一定来自官方软件制造商)。官方镜像列出了它们的潜在漏洞。任何登录用户都可以查看这些信息。Docker Hub 提供免费和付费账户。每个账户可以拥有一个私有镜像,以及无限数量的免费公共镜像。Docker Store – 一项与 Docker Hub 非常相似的服务。它是一个提供评级、评论等服务的市场。我个人认为这只是一种营销手段。我对 Docker Hub 非常满意。
示例 1:Hello World
现在是时候运行你的第一个容器了:
docker run ubuntu /bin/echo 'Hello world'
控制台输出:
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6b98dfc16071: Pull complete
4001a1209541: Pull complete
6319fc68c576: Pull complete
b24603670dc3: Pull complete
97f170c87c6f: Pull complete
Digest:sha256:5f4bdc3467537cbbe563e80db2c3ec95d548a9145d64453b06939c4592d67b6d
Status: Downloaded newer image for ubuntu:latest
Hello world
- docker run是运行容器的命令。
- ubuntu是您运行的镜像。例如,Ubuntu 操作系统镜像。当您指定镜像时,Docker 首先在您的 Docker 主机上查找该镜像。如果本地不存在该镜像,则会从公共镜像仓库 Docker Hub 中拉取该镜像。
- /bin/echo 'Hello world'是将在新容器中运行的命令。该容器仅打印“Hello world”并停止执行。
让我们尝试在 Docker 容器内创建一个交互式 shell:
docker run -i -t --rm ubuntu /bin/bash
- -t标志在新容器内分配一个伪tty或终端。
- -i标志允许您通过抓取容器的标准输入(STDIN)来建立交互式连接。
- --rm标志会在进程退出时自动删除容器。默认情况下,容器不会被删除。该容器会一直存在,直到我们保持 shell 会话,并在我们退出会话时终止(类似于与远程服务器的 SSH 会话)。
如果希望在会话结束后保持容器继续运行,则需要将其守护进程化:
docker run --name daemon -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
- --name daemon为新容器指定守护进程名称。如果没有明确指定名称,Docker 将自动生成并分配。
- -d标志在后台运行容器(即,将其守护进程化)。
让我们看看目前有哪些容器:
docker ps -a
控制台输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 32 seconds ago Up 30 seconds daemon
c006f1a02edf ubuntu "/bin/echo 'Hello ..." About a minute ago Exited (0) About a minute ago gifted_nobel
- docker ps是一个列出容器的命令。
- -a显示所有容器(没有 -a 标志 ps 将仅显示正在运行的容器)。
ps 显示我们有两个容器:
- gifted_nobel(此容器的名称是自动生成的——在你的机器上会有所不同)。这是我们创建的第一个容器,它打印了一次“Hello world”。
- 守护进程——我们创建的第三个容器,作为守护进程运行。
注意:由于我们设置了--rm 选项,所以没有第二个容器(带有交互式shell的容器)。因此,该容器在执行后会立即自动删除。
让我们检查日志并查看守护进程容器现在正在做什么:
docker logs -f daemon
控制台输出:
...
hello world
hello world
hello world
- docker logs获取容器的日志。
- -f标志跟踪日志输出(实际上像tail -f一样工作)。
现在让我们停止守护进程容器:
docker stop daemon
确保容器已经停止。
docker ps -a
控制台输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Exited (137) 5 seconds ago daemon
c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 6 minutes ago gifted_nobel
容器已停止。我们可以重新启动它:
docker start daemon
让我们确保它正在运行:
docker ps -a
控制台输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Up 3 seconds daemon
c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 7 minutes ago gifted_nobel
现在,再次停止它并手动删除所有容器:
docker stop daemon
docker rm <your first container name>
docker rm daemon
要删除所有容器,我们可以使用以下命令:
docker rm -f $(docker ps -aq)
- docker rm是删除容器的命令。
- -f标志(用于 rm)如果容器正在运行则停止它(即强制删除)。
- -q标志(用于 ps)仅打印容器 ID。
示例 2:环境变量和卷
从本示例开始,您需要一些额外的文件,可以在我的GitHub 仓库中找到。您可以克隆我的仓库,或者直接使用以下链接下载示例文件。
现在是时候创建并运行更有意义的容器了,比如Nginx。
将目录更改为examples/nginx:
docker run -d --name "test-nginx" -p 8080:80 -v $(pwd):/usr/share/nginx/html:ro nginx:latest
警告:此命令看起来相当繁琐,但它只是一个解释卷和环境变量的示例。在 99% 的实际情况下,您不会手动启动 Docker 容器——您将使用编排服务(我们将在示例 #4中介绍docker-compose)或编写自定义脚本来执行此操作。
控制台输出:
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
683abbb4ea60: Pull complete
a470862432e2: Pull complete
977375e58a31: Pull complete
Digest: sha256:a65beb8c90a08b22a9ff6a219c2f363e16c477b6d610da28fe9cba37c2c3a2ac
Status: Downloaded newer image for nginx:latest
afa095a8b81960241ee92ecb9aa689f78d201cff2469895674cec2c2acdcc61c
- -p是映射主机端口:容器端口的端口。
- -v是卷挂载主机目录:容器目录。
重要提示:run 命令仅接受绝对路径。在我们的示例中,我们使用了$(pwd)来设置当前目录的绝对路径。
现在,请在您的 Web 浏览器中检查此URL。
我们可以尝试更改/example/nginx/index.html(作为卷挂载到容器内的/usr/share/nginx/html目录)并刷新页面。
让我们获取有关test-nginx容器的信息:
docker inspect test-nginx
此命令显示有关 Docker 安装的系统范围信息。此信息包括内核版本、容器和镜像数量、公开的端口、已挂载的卷等。
示例 3:编写你的第一个 Dockerfile
要构建 Docker 镜像,您需要创建一个 Dockerfile。它是一个包含指令和参数的纯文本文件。以下是我们将在下一个示例中使用的指令的描述:
- FROM——设置基础镜像
- RUN——在容器中执行命令
- ENV——设置环境变量
- WORKDIR——设置工作目录
- VOLUME——为卷创建挂载点
- CMD——为容器设置可执行文件
您可以查看Dockerfile 参考以了解更多详细信息。
让我们创建一个镜像,它将使用curl获取网站内容并将其存储到文本文件中。我们需要通过环境变量SITE_URL传递网站 URL 。生成的文件将被放置在一个目录中,并以卷的形式挂载。
将文件名Dockerfile放在examples/curl目录中,内容如下:
FROM ubuntu:latest
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y curl \
&& rm -rf /var/lib/apt/lists/*
ENV SITE_URL http://example.com/
WORKDIR /data
VOLUME /data
CMD sh -c "curl -Lk $SITE_URL > /data/results"
Dockerfile 已准备好。现在该构建实际镜像了。
进入examples/curl目录,执行以下命令构建镜像:
docker build . -t test-curl
控制台输出:
Sending build context to Docker daemon 3.584kB
Step 1/6 : FROM ubuntu:latest
---> 113a43faa138
Step 2/6 : RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl && rm -rf /var/lib/apt/lists/*
---> Running in ccc047efe3c7
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]
...
Removing intermediate container ccc047efe3c7
---> 8d10d8dd4e2d
Step 3/6 : ENV SITE_URL http://example.com/
---> Running in 7688364ef33f
Removing intermediate container 7688364ef33f
---> c71f04bdf39d
Step 4/6 : WORKDIR /data
Removing intermediate container 96b1b6817779
---> 1ee38cca19a5
Step 5/6 : VOLUME /data
---> Running in ce2c3f68dbbb
Removing intermediate container ce2c3f68dbbb
---> f499e78756be
Step 6/6 : CMD sh -c "curl -Lk $SITE_URL > /data/results"
---> Running in 834589c1ac03
Removing intermediate container 834589c1ac03
---> 4b79e12b5c1d
Successfully built 4b79e12b5c1d
Successfully tagged test-curl:latest
- docker build命令在本地构建一个新的镜像。
- -t标志将名称标签设置为图像。
现在我们有了新的图像,我们可以在现有图像列表中看到它:
docker images
控制台输出:
REPOSITORY TAG IMAGE ID CREATED SIZE
test-curl latest 5ebb2a65d771 37 minutes ago 180 MB
nginx latest 6b914bbcb89e 7 days ago 182 MB
ubuntu latest 0ef2e08ed3fa 8 days ago 130 MB
我们可以从镜像创建并运行容器。让我们使用默认参数尝试一下:
docker run --rm -v $(pwd)/vol:/data/:rw test-curl
要查看保存到文件的结果,请运行:
cat ./vol/results
让我们尝试使用 Facebook.com:
docker run --rm -e SITE_URL=https://facebook.com/ -v $(pwd)/vol:/data/:rw test-curl
要查看保存到文件的结果,请运行:
cat ./vol/results
创建图像的最佳实践
- 仅包含必要的上下文- 使用.dockerignore文件(如 git 中的 .gitignore)
- 避免安装不必要的软件包——它会消耗额外的磁盘空间。
- 使用缓存。在 Dockerfile 末尾添加经常变化的上下文(例如,项目的源代码)——这将有效地利用 Docker 缓存。
- 谨慎使用卷。您应该记住卷中存储的数据。由于卷是持久性的,不会随着容器的消失而消失,因此下一个容器将使用前一个容器创建的卷中的数据。
- 使用环境变量(在 RUN、EXPOSE、VOLUME 中)。这将使你的 Dockerfile 更加灵活。
阿尔卑斯山图像
许多 Docker 镜像(镜像的版本)都是在Alpine Linux上创建的——这是一个轻量级发行版,可让您减少 Docker 镜像的整体大小。
我建议您对第三方服务(例如 Redis、Postgres 等)使用基于 Alpine 的图像。对于您的应用程序图像,请使用基于buildpack的图像- 它将很容易在容器内进行调试,并且您将拥有许多预安装的系统范围的要求。
只有您可以决定使用哪个基础图像,但通过对所有图像使用一个基本图像可以获得最大的好处,因为在这种情况下缓存将得到更有效的利用。
示例4:容器之间的连接
Docker-compose是一个用于连接容器的 CLI 实用程序。
您可以通过 pip安装 docker-compose :
sudo pip install docker-compose
在这个例子中,我将连接 Python 和 Redis 容器。
version: '3.6'
services:
app:
build:
context: ./app
depends_on:
- redis
environment:
- REDIS_HOST=redis
ports:
- "5000:5000"
redis:
image: redis:3.2-alpine
volumes:
- redis_data:/data
volumes:
redis_data:
转到examples/compose并执行以下命令:
docker-compose up
控制台输出:
Building app
Step 1/9 : FROM python:3.6.3
3.6.3: Pulling from library/python
f49cf87b52c1: Pull complete
7b491c575b06: Pull complete
b313b08bab3b: Pull complete
51d6678c3f0e: Pull complete
09f35bd58db2: Pull complete
1bda3d37eead: Pull complete
9f47966d4de2: Pull complete
9fd775bfe531: Pull complete
Digest: sha256:cdef88d8625cf50ca705b7abfe99e8eb33b889652a9389b017eb46a6d2f1aaf3
Status: Downloaded newer image for python:3.6.3
---> a8f7167de312
Step 2/9 : ENV BIND_PORT 5000
---> Running in 3b6fe5ca226d
Removing intermediate container 3b6fe5ca226d
---> 0b84340fa920
Step 3/9 : ENV REDIS_HOST localhost
---> Running in a4f9a1d6f541
Removing intermediate container a4f9a1d6f541
---> ebe63bf5959e
Step 4/9 : ENV REDIS_PORT 6379
---> Running in fd06aa65fd33
Removing intermediate container fd06aa65fd33
---> 2a581c31ff4f
Step 5/9 : COPY ./requirements.txt /requirements.txt
---> 671093a12829
Step 6/9 : RUN pip install -r /requirements.txt
---> Running in b8ea53bc6ba6
Collecting flask==1.0.2 (from -r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting redis==2.10.6 (from -r /requirements.txt (line 2))
Downloading https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl (64kB)
Collecting click>=5.1 (from flask==1.0.2->-r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r /requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
Running setup.py bdist_wheel for itsdangerous: started
Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
Running setup.py bdist_wheel for MarkupSafe: started
Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe
Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, flask, redis
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24 redis-2.10.6
You are using pip version 9.0.1, however version 10.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Removing intermediate container b8ea53bc6ba6
---> 3117d3927951
Step 7/9 : COPY ./app.py /app.py
---> 84a82fa91773
Step 8/9 : EXPOSE $BIND_PORT
---> Running in 8e259617b7b5
Removing intermediate container 8e259617b7b5
---> 55f447f498dd
Step 9/9 : CMD [ "python", "/app.py" ]
---> Running in 2ade293ecb25
Removing intermediate container 2ade293ecb25
---> b85b4246e9f8
Successfully built b85b4246e9f8
Successfully tagged compose_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating compose_redis_1 ... done
Creating compose_app_1 ... done
Attaching to compose_redis_1, compose_app_1
redis_1 | 1:C 08 Jul 18:12:21.851 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis_1 | _._
redis_1 | _.-``__ ''-._
redis_1 | _.-`` `. `_. ''-._ Redis 3.2.12 (00000000/0) 64 bit
redis_1 | .-`` .-```
.
```\/ _.,_ ''-._
redis_1 | ( ' , .-` | `, ) Running in standalone mode
redis_1 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
redis_1 | | `-._ `._ / _.-' | PID: 1
redis_1 | `-._ `-._ `-./ _.-' _.-'
redis_1 | |`-._`-._ `-.__.-' _.-'_.-'|
redis_1 | | `-._`-._ _.-'_.-' | http://redis.io
redis_1 | `-._ `-._`-.__.-'_.-' _.-'
redis_1 | |`-._`-._ `-.__.-' _.-'_.-'|
redis_1 | | `-._`-._ _.-'_.-' |
redis_1 | `-._ `-._`-.__.-'_.-' _.-'
redis_1 | `-._ `-.__.-' _.-'
redis_1 | `-._ _.-'
redis_1 | `-.__.-'
redis_1 |
redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1 | 1:M 08 Jul 18:12:21.852 # Server started, Redis version 3.2.12
redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
redis_1 | 1:M 08 Jul 18:12:21.852 * The server is now ready to accept connections on port 6379
app_1 | * Serving Flask app "app" (lazy loading)
app_1 | * Environment: production
app_1 | WARNING: Do not use the development server in a production environment.
app_1 | Use a production WSGI server instead.
app_1 | * Debug mode: on
app_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
app_1 | * Restarting with stat
app_1 | * Debugger is active!
app_1 | * Debugger PIN: 170-528-240
当前示例将增加 Redis 中的查看计数器。在 Web 浏览器中打开以下 URL并进行检查。
如何使用 docker-compose 是另一个教程的主题。首先,你可以使用 Docker Hub 中的一些镜像。如果你想创建自己的镜像,请遵循上面列出的最佳实践。关于使用 docker-compose,我唯一要补充的是,你应该始终在 docker-compose.yml 文件中为卷指定明确的名称(如果镜像包含卷)。这条简单的规则将避免你将来检查卷时出现问题。
version: '3.6'
services:
...
redis:
image: redis:3.2-alpine
volumes:
- redis_data:/data
volumes:
redis_data:
在这种情况下,redis_data将是 docker-compose.yml 文件中的名称;对于真实的卷名称,它将以项目名称前缀添加。
要查看卷,请运行:
docker volume ls
控制台输出:
DRIVER VOLUME NAME
local apptest_redis_data
如果没有明确的卷名,则会有一个 UUID。以下是我本地机器上的一个示例:
DRIVER VOLUME NAME
local ec1a5ac0a2106963c2129151b27cb032ea5bb7c4bd6fe94d9dd22d3e72b2a41b
local f3a664ce353ba24dd43d8f104871594de6024ed847054422bbdd362c5033fc4c
local f81a397776458e62022610f38a1bfe50dd388628e2badc3d3a2553bb08a5467f
local f84228acbf9c5c06da7be2197db37f2e3da34b7e8277942b10900f77f78c9e64
local f9958475a011982b4dc8d8d8209899474ea4ec2c27f68d1a430c94bcc1eb0227
local ff14e0e20d70aa57e62db0b813db08577703ff1405b2a90ec88f48eb4cdc7c19
local polls_pg_data
local polls_public_files
local polls_redis_data
local projectdev_pg_data
local projectdev_redis_data
Docker 方式
Docker 有一些限制和要求,具体取决于您的系统架构(您打包到容器中的应用程序)。您可以忽略这些要求或寻找一些解决方法,但在这种情况下,您将无法获得使用 Docker 的所有好处。我强烈建议您遵循以下建议:
- 1 个应用程序 = 1 个容器。
- 在前台运行该进程(不要使用 systemd、upstart 或任何其他类似工具)。
- 将数据保留在容器之外——使用卷。
- 不要使用 SSH(如果需要进入容器,可以使用 docker exec 命令)。
- 避免在容器内进行手动配置(或操作)。
结论
总结一下,Docker 已经和 IDE 以及 Git 一起成为了开发者必备的工具。它是一款生产就绪的工具,拥有丰富而成熟的基础架构。
Docker 适用于所有类型的项目,无论其规模和复杂程度如何。一开始,你可以从Compose和Swarm开始。随着项目规模的扩大,你可以迁移到Amazon Container Services或Kubernetes等云服务。
就像货物运输中使用的标准集装箱一样,将代码包装在 Docker 容器中将帮助您构建更快、更高效的 CI/CD 流程。这不仅仅是一群极客推动的又一技术潮流,而是一种新的范式,已经在PayPal、Visa、瑞士电信、通用电气、Splink等大型公司的架构中使用。
本文由 Alex Ryabtsev 撰写。本Docker 教程最初发布于 Django Stars 博客。
我们随时欢迎您提出问题并分享您想阅读的主题!
文章来源:https://dev.to/django_stars/what-is-docker-and-how-to-use-it-with-python-tutorial-87a