从“Docker?略懂一二!”到对容器的自信:一本简洁实用的指南,助你在日常开发中更好地理解和使用Docker
多年来,我一直成功避开了Docker。作为一名后端开发人员,我对任何与Docker相关的任务的标准回应是搞笑的回应:“Docker?跟她完全不熟!”然后我就找各种办法在本地运行所有东西。说服自己容器是多余的复杂性,只是另一个会很快过时的技术流行语。
这听起来熟悉吗?
非Docker用户的进化之路
我在会议上点头附和,每当提到容器编排时,我就会在桌子底下偷偷搜索相关术语。除了涉及Docker的任务,我都会主动承担其他任务。就这样,我竟然还能够在职业生涯中建立起一定的声誉,而对待容器就像对待那个你在家庭聚会中尽力避开的远房亲戚。
直到再也做不下去为止。
一份新工作,一个复杂的微服务架构系统,突然间我避免使用Docker的策略就此崩溃了。接下来是一个周末疯狂的学习和咒骂,最后,令人惊讶的是,一些真正顿悟的时刻来临了。等等,这就是Docker的功能吗?为什么之前没有人这么解释呢?
其实,Docker 并不难懂。大多数时候,它只是被复杂的术语和假设的知识包围,解释得不够清楚透彻。这就是我希望当时有人为我写的指南:一个关于 Docker 是什么,为什么重要,以及如何从今天开始使用 Docker 的直截了当的解释。
不谈容器传道。没有 DevOps 宣传。只为让生活更简单,让普通开发人员停止回避 Docker 并开始利用 Docker 提供实用知识。
不为什么你无法摆脱Docker(其实也不想摆脱)Docker 已悄无声息地渗入了现代开发的每个角落。你从 GitHub Actions 收到测试通过的通知了吗?那条酷炫的 CI/CD 管道正在部署你的代码吧?从头到尾都是容器。甚至你上周写的那个“无服务器”函数,很可能也在某个容器里运行。
Docker冰山现象
而且有一个很好的原因来解释它的无处不在:Docker 解决了软件开发中最让人头疼的问题之一,即“在我的机器上可以运行,但在别人的机器上不行”的烦恼。
我见过整个开发团队因为追踪特定环境下的 bug 而浪费了好几天。这些 bug 本可以通过使用容器避免。我曾经花了三天时间追踪一个痛苦的 bug,而这个 bug 只在生产环境中出现,而在任何开发者的机器上都无法复现。罪魁祸首是我们应用程序依赖的一个系统库的版本略有不同。使用 Docker,就不会出现那样的噩梦。
Docker 在日常开发中大放异彩的地方就是这里:
- 新团队成员的入职: 还记得你入职第一天的情景吗?“首先安装这17个依赖项,配置这些环境变量,然后在满月之夜献祭一只小老鼠。”有了Docker,只需“克隆代码库并运行docker-compose up。”
- 跨环境测试: 需要验证你的应用是否支持PostgreSQL 12和13吗?没有容器的话,你可能会陷入依赖地狱。使用Docker,只需在配置文件中更改一个版本号。
- 本地开发隔离: 同时进行多个项目的工作,这些项目需要不同版本的相同工具?Docker会将所有内容保持得井井有条,这样你的Node 14项目就不会与Node 16项目发生冲突。
- 在本地重现生产问题: 当生产环境中出现问题时,能够在笔记本电脑上快速搭建一个相同的环境,这几乎是无价之宝。
我认识的大多数最初抵制 Docker 的开发人员最终在其中一个场景对他们个人产生影响时都有了一个“aha”时刻。对我来说,这是一次新开发人员在一个需要两天才能完成入职流程的项目上仅用15分钟就开始工作的情景。
# 一个你可能已经用过的 GitHub Actions 工作流:
name: 测试运行
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: , 运行容器中的测试
run: 运行:docker run --rm -v $PWD:/app my-test-image npm test
即使你从未使用过Docker,你也可能在背后受益于它。这些干净且一致的环境可不是随便就能得到的。
容器与虚拟机:那个“啊哈!”时刻
让我们来谈谈房间里的大象: 大多数开发者最初都认为容器只是轻量级的虚拟环境。我也是这么认为的。好几个月来,我告诉他们Docker容器就像是"更快启动的小VM"。我错了。
这个误解非常普遍,澄清它通常会让人们对于 Docker 有顿悟的时刻。所以让我们澄清事实。Docker 终于让人感觉“点击”了。
虚拟机、容器解决的是类似的问题,但从根本上来说方式不同。
虚拟机对比Docker
但这些差异在你的日常工作里有什么影响呢? 让我解释,当我本地运行一个典型的Web应用时,
作为虚拟机:
- 启动大约需要2-3分钟
- 在空闲状态下至少占用1GB内存
- 需要配置网络
- 即使关机也能继续运行
作为Docker容器:
- 可在几秒内启动
- 仅使用应用程序所需的资源
- 网络配置自动完成
- 在不需要时会自动停止
架构上的差异至关重要。虚拟机模拟整个计算机硬件,并在其模拟之上运行完整的操作系统。每个虚拟机都需要自己的操作系统、内核以及所有相关组件。
相比之下,容器与主机共享操作系统内核,但运行在隔离的用户区中。它们仅包含应用程序及其依赖。可以将虚拟机想象成独立的屋子(每个屋子有自己的地基、管道和电气系统),而容器更像是建筑物中的公寓(共享核心设施,但各自拥有独立空间)。
我也是通过艰难的方式才了解到这个区别。我曾经尝试在一个需要硬件级GPU隔离以确保安全的项目中使用容器。几天后,我意识到虚拟机实际上是适合那个场景的正确工具。容器对我们的一些微服务来说简直是完美的选择,但有些任务确实需要使用虚拟机。
不过,容器其实不像是在模拟整个计算机。它们提供隔离的环境以运行进程。这种差异解释了为什么容器几乎是瞬间启动,而且占用的资源少得多。
对于大多数开发工作流程而言,容器找到了虚拟机未能达到的平衡点:轻量到可以在笔记本电脑上运行数十个,但足够隔离以避免“在我的机器上可以工作”的问题。它们不是更好的虚拟机,而是完全不同的工具。
Docker三大组件:了解组件间的配合如果你看到这里,你可能在想,“好吧,我已经理解了Docker的重要性,但我现在具体在处理什么?”让我来解释一下Docker的三个核心组成部分,也就是我所说的 Docker三元素 。
旅程开始了
可以把 Docker 想象成建造和装修房子的一个房间。每个组件都承担特定的功能:
Dockerfile 的蓝图Dockerfile 是一个普通的文本文件,包含了构建镜像的指令。你可以把它想象成你房子的设计图。
FROM node:14
# 从这个基础开始
WORKDIR /app
# 创建一个专属的空间放我们的东西
COPY package*.json ./
# 首先带入项目清单
RUN npm install
# 安装所有依赖
COPY . .
# 现在带入其他东西
CMD ["npm", "start"]
# 使用这种方式打招呼
Dockerfile中的每一行都是一个指令,会创建镜像中的一个层。你经常会看到的最常见指令包括,比如:
FROM
:我们是从哪个现有的镜像开始构建的?WORKDIR
:我们把文件放在容器内部的哪个位置?COPY
/ADD
:从电脑里复制文件到镜像中RUN
:在构建过程中运行哪些命令?ENV
:设置环境变量是什么意思?EXPOSE
:说明容器将使用哪些端口CMD
/ENTRYPOINT
:容器启动时要运行什么命令?
让我困惑的是,理解到每个指令都会生成自己的层,而这些层会被缓存。这就是为什么顺序这么重要。
看图像:照片一个镜像是基于 Dockerfile 构建的,这是一个只读模板,包含了运行你的应用程序所需的所有内容。就像我们之前的房屋类比,镜像就如同一个被真空密封的完整房屋,无法再被修改。镜像有一些关键特性,比如:
- 不可变:一旦构建完成,镜像就不会改变。如果要更新内容,只需构建一个新的镜像。
- 分层:每个 Dockerfile 中的指令都会在镜像中创建一个层。这些层会被缓存并重用。
- 标签化:镜像通过标签(如
node:14
或myapp:1.2.3
)来区分。
我发现开发者常犯的一个错误是直接去修改图片。他们可不能这样做。他们得修改 Dockerfile 并重新构建一下。
# 从当前目录的Dockerfile构建一个镜像
docker build -t myapp:1.0 .
# 这会创建一个名为myapp:1.0的镜像
# 列出你的镜像列表
docker images
# 这会显示所有你拥有的镜像
# 同一个镜像可以被打上多个标签
docker tag myapp:1.0 myapp:latest
# 这会将myapp:1.0的标签更新为latest
容器:正在运行的实例
最后,容器就是镜像的实际运行实例。如果镜像是一栋封闭的房子,那么容器就像是有人搬进去住的状态。容器具有以下特点:
- 可运行:它们是实际的执行环境,能够执行代码。
- 隔离:它们有独立的文件系统、网络和进程树。
- 短暂存在:默认情况下,容器停止后,其文件系统中的任何更改都会丢失。
- 基于镜像:每个容器都从一个镜像启动。
# 运行一个容器
docker run myapp:1.0 # 运行容器 myapp:1.0
# 查看正在运行的容器
docker ps # 查看运行中的容器
# 停止容器
docker stop container_id # 停止容器(用容器ID)
汇总所有部分
这里是如何在典型工作流程中,这三个组件相互配合的。
- 你写一个Dockerfile来描述如何构建你的应用程序
- 你运行
docker build
命令来从Dockerfile构建一个镜像 - 你运行
docker run
命令来从该镜像启动一个容器 - 你的应用程序就在这个容器里运行
让我分享一个我早期使用Docker时的“陷阱”:我花了好几个小时试图弄清楚为什么我的应用程序更改没有在容器中显示。问题在于我在修改代码后没有重新构建镜像就启动容器。记住这一流程:
# 编辑你的代码
vim app.js
# 重新构建图像
docker build -t myapp:latest .
# 用更新后的版本启动新的容器
docker run myapp:latest
理解这三个概念——从 Dockerfile 到 Image 再到 Container——是我使用 Docker 的真正“啊哈!”时刻。一旦明白了这些关系,一切都开始变得清晰明了。Docker 的酷的地方在于,这个简单的模式可以从简单的“hello world”示例扩展到复杂的微服务架构。无论你是运行单个容器还是管理数千个容器,基本原则都是一样的。
你的第一个Docker容器:亲身体验,无忧无虑好了,理论讲得够多了,让我们动手做起来。我记得我第一次接触教程时构建一个复杂的多层应用,这让我感到困惑,并复制粘贴了一些我不懂的命令。我们不会那么做。相反,我们会构建一个简单但完整的应用,来展示整个工作流程。
我们将创建一个容器,用来展示随机生成的ASCII艺术图案。没错,它确实有些傻气,但它会教你一些基础的知识,而不会让你陷入特定应用的细节。
第一步:设置你的项目:下面列出步骤。
首先,我们为项目创建一个文件夹。
创建目录 docker-hello
进入 docker-hello 目录
这里没什么特别的,只是一个用来整理我们东西的地方。Docker 对你给目录起什么名字无所谓,但有一个专门的空间让后续清理更方便。我们的东西放在那里,可以保持井井有条。Docker 不管你给目录起什么名字,有一个专门的空间让后续清理更方便。
第2步:编写消息脚本我们需要一个脚本文件,容器会运行这个脚本文件。我们来创建一个简单的 bash 脚本,用 ASCII 艺术显示一条随机消息。
cat > print_message.sh << 'EOF'
#!/bin/bash
# 消息数组定义
MESSAGES=("Hello Docker!" "Containers are awesome" "You did it!")
# 将随机消息赋值给RANDOM_MESSAGE
RANDOM_MESSAGE=${MESSAGES[$RANDOM % ${#MESSAGES[@]}]}
# 使用figlet命令将消息格式化输出
figlet "$RANDOM_MESSAGE"
# 结束标记
EOF
这里在做什么?我们正在创建一个名为“print_message.sh”的文件,该脚本内容如下:这是一个bash脚本。
- 定义一系列可能的消息
- 从中随机挑选一条
- 然后用叫
figlet
的程序来展示这条消息(是的,figlet 确实是一个程序,确实很有趣)
如果你不熟悉 bash 脚本也无需担心,因为实现细节并不重要,所以不必担心。重要的是我们有一个脚本,执行时能做出一些看得见的效果。
步骤 3:创建 Dockerfile
现在来到最关键的部分,Dockerfile,它告诉Docker如何构建我们的镜像,这一步很关键。
cat > Dockerfile << 'EOF'
# 创建Dockerfile以构建包含figlet和print_message.sh的Ubuntu镜像
FROM ubuntu:latest
# 更新软件包列表并安装figlet
RUN apt-get update && apt-get install -y figlet
# 复制本地的print_message.sh到容器的/app目录
COPY ./print_message.sh /app/print_message.sh
# 使print_message.sh可执行
RUN chmod +x /app/print_message.sh
# 设置容器启动时运行print_message.sh脚本
CMD ["/app/print_message.sh"]
EOF
我们来逐行分析一下,
或者我们一起来看看每一行的内容:
FROM ubuntu:latest
: 基于最新的 Ubuntu 镜像RUN apt-get update && apt-get install -y figlet
: 更新包列表并安装 figletCOPY ./print_message.sh /app/print_message.sh
: 将脚本复制到应用目录下RUN chmod +x /app/print_message.sh
: 使脚本具有可执行权限(我第一次忘了这一步,为此困惑了好久)CMD ["/app/print_message.sh"]
: 将此脚本设定为容器启动时自动运行的程序
这个 Dockerfile 代表了你最常使用的模式之一:从一个基础镜像开始,安装依赖项,复制你的代码文件,然后指定如何运行它。
第 4 步:构建 Docker 镜像现在让我们生成这个图像
使用docker构建一个名为ascii-art的镜像。
-t ascii-art
这一部分给我们的图片命名(或称为“标签名”)。.
指示 Docker 在当前文件夹中寻找 Dockerfile。这会产生大量输出,因为 Docker 执行 Dockerfile 中的每条指令时会生成大量信息:
正在将构建上下文(3.072kB)发送到Docker守护进程
Step 1/5 : FROM ubuntu:latest
latest: 正在拉取来自library/ubuntu
...等
成功构建 8d3f41923b39
成功标记为 ascii-art 最新版本
在幕后,Docker 实际上是:
- 为每个指令创建一个临时容器来存放它
- 执行该指令
- 根据更改创建一个新的层
- 转到下一个指令
如果你修改了 Dockerfile 并重新构建,Docker 会重用缓存的层直到第一个被修改的指令,从而使重新构建更快。
步骤 5: 运行你的容器来了那个神奇的时刻:
docker run ascii-art
这里运行的命令是启动一个生成ASCII艺术的Docker容器。如果你不熟悉Docker和ASCII艺术,简单来说,就是在使用Docker运行一个可以生成文本艺术的应用程序。
你应该看到类似下面这样的信息:
| | | | | | | | _ \ | | | |
| |_| | ___| | | ___ | | | |___ ____| | _____ _ __| |
| _ |/ _ \ | |/ _ \ | | | / _ \ / _ | |/ / _ \ '__| |
| | | | __/ | | (_) | | |/ / (_) | (_| | < __/ | |_|
\_| |_/\___|_|_|\___/ |___/ \___/ \__,_|_|\_\___|_| (_)
Since the source text is ASCII art representing a stylized face or abstract design and not actual text, the translation should maintain the visual style and pattern using Chinese characters or pinyin. However, the source text does not provide a text or specific meaning that can be translated. Therefore, the same structure is preserved to maintain the visual representation.
恭喜你!你刚刚:
- 创建了一个Dockerfile
- 构建了一个Docker镜像文件
- 运行了一个该镜像的容器
酷的是,你不必在自己的电脑上安装figlet,它只是在容器里安装。这是一个简单的例子,不过想象一下,如果你的应用程序需要特定版本的Node.js或Python,或者与系统中其他组件冲突的数据库。使用Docker,这根本不是问题了!
第六步:改了再建让我们看看更新是怎么运作的,然后修改你的消息文本。
cat > print_message.sh << 'EOF'
#!/bin/bash
MESSAGES=("Docker 真好玩!" "容器真棒!" "你现在就是 Docker 专家了!")
RANDOM_MESSAGE=${MESSAGES[$RANDOM % ${#MESSAGES[@]}]}
figlet -f slant "$RANDOM_MESSAGE"
EOF
我已经修改了消息内容,并添加了 -f slant
以使用不同的字体。现在重新编译:-f slant
docker build -t ascii-art:v2 .
这会构建一个新的 Docker 镜像,标签为 ascii-art:v2。
注意,我们把它标记为 v2
。现在来运行一下。
运行版本2的ascii-art容器
docker run ascii-art:v2
你应该看到你的新消息是以斜体显示的!新手常犯的一个常见问题是:如果你不重建镜像,你的更改就不会在容器中显示。我第一次用 Docker 的时候,我不断修改代码,却总是搞不懂为什么容器没有反映我的更新。你得重建镜像并启动一个新的容器才能看到更新。
我们已经做了些什么这个简单的案例展示了整个Docker工作流。
- 创建一个描述你环境的Dockerfile文件
- 从该Dockerfile构建一个镜像文件
- 运行基于该镜像的容器实例
- 更新你的代码并重新构建镜像文件
虽然我们的ASCII艺术生成器不会对你的业务产生革命性的影响,但你刚刚遵循的流程和你用于实际应用的流程是一样的。唯一的区别在于实际应用的复杂性和依赖关系。
接下来,我们将看看如何在实际项目中使用 Docker 多个容器和持久化数据的卷,以及其他实用要点。
超越'Hello World':在实际项目中使用Docker技术所以你已经创建了自己的第一个容器,恭喜你!但正如我们所知,生成随机消息的程序相比来说和部署生产API或管理复杂开发环境还是不一样的。让我们来谈谈在实际工作中使用Docker是什么样子。
这是我希望能有人在我进入编程深入阶段前,告诉我这些事情:在度过编程入门阶段后,但在开始尝试将所有程序打包到容器中之前(这个阶段每个人都经历过)。
管理你的图片,不抓狂当我开始认真使用Docker时,我的笔记本电脑很快变成了未使用镜像和容器的坟场。我运行docker images
命令后,会看到几十个带有晦涩名字或麻烦的<none>
标签的条目,让我感到烦恼。
这里是如何避免那样的结局的:
# 不要只使用 'latest' - 使用语义化版本号。
docker build -t myapp:1.0.0 .
# 如果是当前版本,也标记为 latest
docker tag myapp:1.0.0 myapp:latest
# 列出镜像以查看你有什么
docker images
# 清理未使用的镜像(这曾经为我节省了15GB的空间!)
docker image prune
# 对于更激进的清理(小心使用此命令)
docker system prune -a
就我个人而言,我认为在处理任何实际项目时,明确的版本标签绝对必不可少。在生产环境中使用 latest
,就像是在部署时玩俄罗斯轮盘赌一样,你永远不知道实际在运行的是什么版本。
当你需要的应用不仅仅是一个服务(比如API和数据库)时,Docker Compose 就成了你最好的朋友。我花了好几个星期手动连接容器,直到有人向我展示了这个改变游戏规则的工具。
下面举一个简单的例子,比如:
services:
app:
build: .
ports:
- "3000:3000" # 应用程序端口映射
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb # 数据库URL
depends_on:
- db # 应用程序依赖于数据库服务
db:
image: postgres:13 # 使用Postgres 13版本
volumes:
- postgres_data:/var/lib/postgresql/data # 数据卷定义
environment:
- POSTGRES_USER=user # 设置Postgres用户名
- POSTGRES_PASSWORD=pass # 设置Postgres用户密码
- POSTGRES_DB=mydb # 设置Postgres数据库名称
volumes:
postgres_data: # 数据卷存储位置
使用此文件,一个 docker-compose up
命令即可启动两个服务,设置它们之间的网络,并挂载所需的文件卷。这与用单独的 Docker 命令来一个个管理相比,简直就像变魔术一样。
在第一个月使用 Docker 时,我为每个容器都开了一个单独的终端窗口。别学我这样。
卷:因为数据应该比容器更持久我对容器的体积概念感到困惑。“如果容器是临时的,我的数据会跑到哪里去?”我记得在不小心停止了一个包含一天工作数据的容器后惊慌地问道。
卷是 Docker 提供的一种持久数据存储方式。你可以把它们想象成可以插入容器的 USB 闪存盘。
# 创建一个命名卷
docker volume create my_data
# 运行挂载了卷的容器
docker run -v my_data:/app/data myapp
# 使用 Docker Compose 更简单
# (如前面的例子所示)
有三种坐骑类型,弄清楚何时使用每一种坐骑花了我一点时间:
- Volumes(如上所述):由 Docker 管理,最适合持久性数据
- Bind mounts:将容器路径连接到主机路径,非常适合开发
- tmpfs mounts:仅保存在内存中,适合存储敏感信息
在本地开发时,我几乎每次都使用绑定挂载这种技术将我的代码目录与容器同步,这样做:
# 我的常用开发设置
docker run -v $(pwd):/app myapp
这样做,我在本地所做的任何更改会立即在容器中体现出来,而且无需重新构建容器。这彻底改变了我的工作方式。
团队中的 Docker:做个好容器公民在团队中使用Docker会带来一些独特的挑战。在我们当前的项目中,我们有使用Windows、Mac和Linux系统的开发者,但多亏了Docker,我们的开发环境保持了一致性。
这里有一些让我们少了很多麻烦的团队实践。
- 将所有环境变量记录在
.env.example
文件中 - 使用
docker-compose.override.yml
进行本地定制 - 确定标签策略(我们使用 git hash 进行部署)
- 在 compose 文件中设置资源上限以避免容器独占资源
请一定要,出于对所有容器化的热爱,不要在Dockerfile或docker-compose.yml中存储秘密。我见过这种事太多次了。使用环境变量,更好的办法是使用密钥管理方案。
团队的最大好处是新开发者入职。过去这需要几天时间(“按这个顺序安装这17个依赖项”),现在只需几分钟时间(“运行docker-compose up命令”)。上个月有一位新成员加入我们团队时,她在一个小时内就可以在本地运行整个栈。
Docker 学习的难度如果你感到不知所措,你不是唯一一个这样感觉的人。对我来说,最难的部分不是理解基本概念,而是弄清楚所有这些最佳做法和文档中没有明确说明的模式。
从一个小的地方开始。一次只容器化一个服务就好。不要急于成为Docker高手,就像我那样,一冲动就把整个系统都迁移了。那个周末至今仍让我做噩梦。
要记住,Docker只是一个工具,并不是一种信仰。仍然有正当的理由直接在你的机器上运行一些程序。当使用 Docker 增加不必要的复杂性时,我还是会直接开发一些项目,而不会使用 Docker。
但对于那些复杂的项目,尤其是那些有多项服务或依赖关系的项目,Docker 完全改变了我工作的方式,再也不想回到“在我的机器上能运行”的日子了。
从 Docker 初学者到容器大师回想我当初避免使用Docker的日子,我不禁笑自己当时浪费了那么多时间处理那些容器几分钟内就能解决的环境问题。曾经常用的那个“在我的机器上可以运行”的借口已经被淘汰了,取而代之的是一种自信感,那就是只要我能实现容器化,它就能在任何环境中运行。
Docker:轻松之旅
最让我惊讶的是,并不是 Docker 的技术能力(虽然这些功能确实很强大),而是它成为我工作流程中不可或缺的一部分这么快。一开始看起来有点让人摸不着头脑的功能,比如这些层、卷、网络等,现在感觉就像使用 git 一样自然。
你看完一篇文章不会成为Docker专家,但是你现在应该理解了那些曾经让我困惑了好几个月的核心概念:
- Dockerfile、镜像和容器间的关系
- 容器与虚拟机的根本区别
- 如何构建和运行自己的容器
- 在实际项目中管理Docker的基本知识
这些基础知识足够让你开始将Docker融入你的日常工作。每当你使用它时,这些概念会逐渐变得更牢固,直到它们变得像本能一样。
接下来该去哪儿如果你准备好了,想进一步提升你的 Docker 技能,这里有几个合理的下一步。
- 将你现有的一个项目容器化。 从小项目开始——选择一个简单且适合容器化的项目。
- 深入学习 Docker Compose。 我们之前简短的介绍远远不够,远远不能涵盖多容器应用的可能性。
- 探索容器编排。 当你熟悉 Docker 的基础知识后,可以看看像 Kubernetes 这样的工具是如何在此基础上构建生产部署的。
- 加入容器社区。 Docker 社区对新人非常友好。你可以去 Docker 官方论坛、Stack Overflow 或 Reddit 的 r/docker 寻找帮助。
记住 Docker 只是你工具箱里的一个工具。有时候它正是你所需要的;其他时候则可能过于复杂。知道何时使用它和如何使用它一样重要。
我现在最欣赏 Docker 的是它让我可以专注于编写代码而不是环境配置。不需要给新团队成员发送一份长达12页的配置指南,我只需要说一句“docker-compose up”,看着他们兴奋的眼神。
所以你会用你新学到的 Docker 知识创建什么呢?无论是做什么,我可以肯定你会有很多“aha!”的瞬间,这时你会突然有一种“aha!”的感觉,容器的概念会以新的、令人满意的方式变得清晰。
Docker 已经从我主动避开的东西变成了我积极推荐的东西。希望这篇指南能帮助你从避开 Docker 到成为容器达人,用比我少得多的时间完成同样的转变。
祝你容器化愉快!
您的Docker达人
NoManNayeem - 简介全栈工程师 Python/GO/Node | 技术项目经理 | 技术传播者 | 数据科学爱好者及从业者 | 讲师…github.com共同學習,寫下你的評論
評論加載中...
作者其他優質文章