惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
V
V2EX
G
Google Developers Blog
F
Full Disclosure
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
H
Hacker News: Front Page
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
A
About on SuperTechFans
The Cloudflare Blog
C
Cisco Blogs
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
Help Net Security
Help Net Security
Recorded Future
Recorded Future
PCI Perspectives
PCI Perspectives
S
Schneier on Security
AI
AI
N
News | PayPal Newsroom
雷峰网
雷峰网
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
L
LINUX DO - 最新话题
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Schneier on Security
Schneier on Security
S
Securelist
云风的 BLOG
云风的 BLOG
Stack Overflow Blog
Stack Overflow Blog
博客园_首页
AWS News Blog
AWS News Blog
TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 三生石上(FineUI控件)
C
CXSECURITY Database RSS Feed - CXSecurity.com
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
Check Point Blog
S
Security Affairs

pseudoyu

周报 #106 - OpenClaw 实践、搬家与生活的能量 周报 #105 - 打破无聊的社会 2025 年末回顾 - Anything different is good 周报 #104 - 被遗忘的生活一隅 周报 #103 - 工作随想与被吞噬的生活 周报 #102 - 我是如何使用 AI 的 周报 #101 - 消失了三个月,我的生活发生了什么变化 周报 #100 - 过去这十年 周报 #99 - 离开 RSS3,迷茫与重启人生 周报 #98 - 生活之书与 Agentic Coding 周报 #97 - 日本旅行小记 28 岁的自白:去找寻自己 周报 #96 - 基于 Roo Code/Cursor 的 iOS 开发工作流 周报 #95 - All AI 与 No AI 周报 #94 - 生活的界限与真实感 周报 #93 - 婚礼纪录片与失落的电影院 周报 #92 - 数码产品 Porn 与断舍离 周报 #91 - 一席、消失的时代与自由意志 周报 #90 - OVH 服务器、Coolify 与对 AI Coding 的思考 周报 #89 - 博客改版与 TheWan App 发布 周报 #88 - 方大同、《回留》与人生清单 周报 #87 - 我们主持了自己的宝可梦婚礼,还为它办了一个展 周报 #86 - 用影像打败时间 周报 #85 - 京华烟云 Moment in Peking 周报 #84 - 想起他们 Echos of Reverie 周报 #83 - 年初收纳(房间、设备、服务、软件) 周报 #82 - Brand New Year 与写日记 2024 年末回顾 - 不完美的日子 周报 #81 - Engaged -> Married 与时间的尺度 周报 #80 - 关于求婚、爱情与婚姻 求婚誓言 | 爱的本质是被看见 周报 #79 - 清迈旅居、曼谷之行与 Follow RSSHub 开发实践 #01:Telegram 频道 RSS 订阅实现与部署方案 Follow —— 信息获取的另一种形态 周报 #78 - NAS、Chromebook 与 Zeabur 折腾小记 周报 #77 - 一间只属于自己的房间(对亲密关系的思考) 周报 #76 - 关于 Adulting 的思考 周报 #75 - 回答 Dayu 关于独立博客的 9 个问题 周报 #74 - 清迈异闻录 周报 #73 - 《社交网络》、Ego 与中秋的现充生活 周报 #71 - Indie App 体验计划 周报 #70 - 消失的附近,Burnout 与 Boreout 周报 #69 - AI Coding 体验与学习的源动力 周报 #68 - 关于写博客这件事与我生活的色彩 使用 GoatCounter 与 Zeabur 搭建网站数据统计系统 周报 #67 - 使用 follow 重塑我的信息输入系统 周报 #66 - 10x 工程师、技术热情与个人工具箱 从零开始搭建你的免费博客评论系统(Remark42 + fly.io) 周报 #65 - Adventure X 体验、Apple Notes 笔记实践与 EpubKit 周报 #64 - 生活在此处(《斯通纳》与《Normal People》) 使用 WebP Cloud 与 Cloudflare WAF 为你的图床添加隐私和版权保护 当云服务器崩溃时,我是如何救援重要数据的 周报 #63 - 不愉快的订花经历、商家和消费者与日渐 AI 化的人 从零开始搭建你的免费图床系统(Cloudflare R2 + WebP Cloud) 2024 年了,我的博客有了什么变化 周报 #62 - 香港之行、5am club 计划与 Rust 学习 周报 #61 - 好朋友的婚礼与对婚姻的思考 27 岁的自白:成长是一件很扫兴的事 周报 #60 - 虫子旁、教育理念与 EpubKit 周报 #59 - 沙漠之行、家庭与家人 周报 #58 - 远程工作这一年 周报 #57 - 生活在别处(巴厘岛之行) 周报 #56 - 面基 Randy 与考科目二 周报 #55 - 油画体验、博客系统升级与对 self-hosting 的思考 周报 #54 - 漂流计划、钱包被盗与 Home Server 周报 #53 - 空白的三个月、生活漂流与春节 周报 #52 - 自谦型人格与感情的另一个视角 周报 #51 - 追星小鹿 Lawrence 与生病 周报 #50 - 天坛漫步、重拾阅读和费曼学习法 周报 #49 - 烧焦的键盘、庞贝神话展、健康调整与神奇的梦 周报 #48 - 内蒙之行、清迈游民、重启的周报与生活 周报 #47 - 采访、拳击与工作状态 周报 #46 - 告别长发、周报初心与合约开发 个人信息获取与知识管理系统(Heptabase + Logseq + Readwise) 周报 #45 - 读书笔记、知识管理与双城生活 周报 #44 - 静寂工人、xLog 与日常 周报 #43 - In the Mood for Love 周报 #42 - 感情观、生活状态与自我 周报 #41 - 生日的仪式感、时间管理与北京之行 26 岁的自白:推石头的人 周报 #40 - 开源之夏、代码之外与流逝的睡眠 周报 #39 - 初患新冠、过去的心绪与失而复得的分享欲 周报 #38 - Foundry、Logseq 与 Surge Ponte 周报 #37 - 浮生半月闲( 武汉 | 香港 | 五月天) 周报 #36 - 肠胃炎、捏捏近况与新的旅途 周报 #35 - 登报、搬家和生病的捏捏 周报 #34 - Don't Stop the Clocks 周报 #33 - 北京 x 故事 x 离别 周报 #32 - 武汉、久别重逢与流动的心绪 周报 #31 - 开源、前端开发与 ChatGPT 实践 周报 #30 - 开源预算、写作初心与对技术的谦卑 Cosmos 区块链架构与 Tendermint 共识机制 周报 #29 - 日语学习经验、Steam Deck 与居酒屋 周报 #28 - Mastodon、故乡与捉迷藏的猫 周报 #27 - 何以为家 周报 #26 - 博客、客制化键盘和新服务器 周报 #25 - 基于 Crossbell 的个人信息输出与同步系统(重构) 摄影集 #02 - Moment In Peking (The Autumn) 摄影集 #01 - Cat In The Box 2022 年末回顾 - 迷茫、低谷与改变
Docker 基础与实践
pseudoyu · 2022-09-06 · via pseudoyu

前言

这是工作实践系列容器部分的第一篇,主要介绍 Docker 的基础知识与实践。

作为一个后端开发,我刚开始工作的时候其实主要都是在本地调试的,并没有怎么了解过 Docker 的相关使用。直到后来开始接触较为复杂的底层链开发,因为链或其相关工具的依赖关系比较复杂,也涉及很多版本冲突问题,在本机或服务器上每次需要配置复杂的环境,且每次重启后很多服务与配置都需要重新部署,繁琐且容易出现一些莫名的跨平台错误。

因此逐渐开始采用编写项目特定 Dockerfile 并编译镜像的方式进行后续的开发调试,部署的机器仅需安装 Docker 环境(以及 Docker Compose),而不需要本地安装各种依赖,很便捷。后续也和 Leader 一起基于 Docker 镜像、GitLab CI 与 k8s 环境配置了项目的 CI/CD 流程,极大提升了开发调试效率。

本文将基于这些经验对 Docker 相关的概念与实践进行总结,希望能有所帮助。

Docker 简介

我们所开发的服务往往以二进制的方式运行在操作系统中,而 Docker 是一种容器技术,将我们的应用程序及相关依赖打包在一个容器中,容器往往是基于一个较为轻量级的 Linux 镜像,是多层镜像的堆叠,我们的应用往往在最上层,这些依赖关系在 Dockerfile 中进行指定。

使用容器进行部署比起在本机或远程服务器有很多明显的优势。

  1. 无需在操作系统上安装各类环境和依赖(除了 Docker 自身)。如果采用原有的服务启动模式,开发流程会变得十分繁琐,需要开发与运维不断沟通,配合完成环境配置与部署,并且如果一台机器上部署了多个服务,也极易造成依赖/版本冲突问题。
  2. 可以拥有独立的部署环境。我们通过为不同的项目编写 Dockerfile 来构建镜像,将应用所需环境与依赖打包在镜像中,可以很方便地运行同个应用的不同版本,或为 MySQL 这样的通用服务运行多个实例,并且可以通过 Docker 命令或 Docker Compose 命令进行管理,一键启动/暂停。
  3. Docker 并不强依赖于操作系统本身的版本,同一个 Docker 镜像可以在不同的操作系统(Windows、macOS、不同发行版的 Linux)上运行,易于服务的分享、迁移与跨平台部署等。
  4. 与虚拟机相比,Docker 容器没有内核而只包含应用层,体积更小,启动速度更快,更加轻量级。

当然,Docker 容器的兼容性相比操作系统与虚拟机相对更差一些,如 VM 能够运行任意其他操作系统,能满足更特定的一些需求。

Docker 基础操作

安装 Docker

Docker 的安装很简单,在官网下载自己操作系统对应的安装包并按照指引进行安装即可。

macOS

我个人的 macOS 系统起初是安装了 Docker Desktop,可以通过图形化界面对镜像、容器进行管理,很方便但是占用较高,比较耗电。

后来尝试了 Colima,一个较为轻量级的容器运行环境,在 macOS 系统上本机调试十分方便,推荐使用,根据项目官方文档安装并配置环境即可。我直接通过 brew 包管理工具来进行安装:

brew install colima

安装完成后运行 colima start 即可启动容器,运行 colima stop 停止容器,更多命令可以通过 colima --help 查看。

我通过了如下命令启动了自己的常用开发环境,大家可以根据自己的需求自行配置:

colima start -c 8 -m 16 -a x86_64 -p docker-amd

CentOS

比起本机开发,Docker 更常用的应用场景是在服务器上部署应用,我常用的操作系统是 CentOS 7,可以通过 yum 包管理工具安装:

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager  --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce

安装完成后,启动 Docker 服务并配置其开机自启:

systemctl enable docker
systemctl start docker

Docker 镜像

Docker 主要有镜像和容器两个概念,可以认为镜像是通过 Dockerfile 编译出来的容器的一个模板,而容器是镜像的一个实例。

Dockerfile

我们通过 Dockerfile 来指定应用所需环境与依赖,其基本格式如下:

FROM <image>

ENV USERNAME=admin \
    PASSWORD=123456

RUN mkdir -p <app-directory>

COPY . /<app-directory>

CMD ["<command>", "<entrypoint file>"]

完成 Dockerfile 编写后,我们可以在同级目录(或指定 Dockerfile)下通过 docker build 命令来构建镜像:

# 镜像构建
docker build -t <image:tag> .

存储、加载镜像

我们可以把本地编译好的镜像存储为 tar 包来进行分享:

docker save -o <image-name>.tar <image-name>

当需要使用镜像时则可以通过 docker load 命令来加载 tar 包:

docker load -i <image-name>.tar

上传、拉取镜像

当然,通过镜像 tar 包的方式来进行分享并不那么便捷,有的镜像可能会很大,传输也不方便。因此,我们可以通过 docker push 命令来将镜像推送至官方镜像仓库或企业/个人的私有库(像我所在的项目就是通过 Harbor 来管理镜像),并通过 docker pull 命令来进行拉取。

# 拉取官方镜像(简写)
docker pull <image:tag>

# 拉取官方镜像(完整命令)
docker pull docker.io/library/<image:tag>

# 推送镜像至官方镜像仓库 Docker Hub
docker push <image:tag>

# 推送镜像至私有库(需要配置鉴权)
docker tag <image:tag> <private-repo-path>/<image:tag>
docker push <private-repo-path>/<image:tag>

Docker 镜像操作

针对 Docker 镜像,我常用到的操作就是查看、删除与重命名 tag,更多命令可以通过 docker image --help 或官网查看。

# 查看所有镜像
docker images

# 删除镜像
docker rmi <image:tag>

# 重命名镜像
docker tag <old-image:tag> <new-image:tag>

容器操作

查看容器

当我们通过 Docker 或 Docker Compose 命令启动镜像后,可以通过以下命令查看服务状态:

# 查看运行中容器
docker ps

# 查看所有容器
docker ps -a

通过镜像启动/停止实例

当我们通过 Dockerfile 编译好了所需镜像后,可以通过 docker run 命令启动镜像实例,并在命令中加入一些配置来满足我们的服务需求,我的常用操作如下:

# 运行容器
docker run <image:tag>

# 运行容器并指定名称
docker run --name <server-name> <image:tag>

# 以 detached 模式运行容器
docker run -d <image:tag>

# 端口映射
docker run -p6000:6379 <image:tag>

# 配置环境变量
docker run -e USERNAME=admin -e PASSWORD=123456 <image:tag>

启动/停止容器服务

当我们通过镜像创建实例后,可以通过如下命令来启动/停止容器服务:

# 启动/重启容器
docker start <container-id>

# 暂停容器
docker stop <container-id>

查看日志

当我们的通过 Docker 启动服务后,还常常需要查看其运行日志以便于调试,可以通过 docker logs 进行查看,具体命令如下:

# 查看日志
docker logs <container-id>

# 滚动查看日志
docker logs -f <container-id>

进入容器

有时我们还需要进入 Docker 容器服务内部进行服务查看与调试,可以通过 docker exec 命令进入容器,具体命令如下:

# 根据 id 进入特定容器
docker exec -it <container-id> <command>

Docker 网络

Docker 容器实例运行于网络中,我们上文的各个命令未指定网络,所以服务会运行在默认网络下,我们可以通过以下命令来查看网络:

# 查看所有网络
docker network ls

如果不想运行在默认网络中,我们可以通过如下命令创建自定义网络:

# 创建自定义网络
docker network create <network-name>

创建了我们的自定义网络后,在创建容器实例时我们可以通过 --network 参数来指定网络:

docker run --network <network-name> <image:tag>

Docker 数据持久化

使用 Docker 实例运行服务后,我们的数据会保存在容器中,当容器被删除后,数据也会被删除,对于一些需要长期运行的服务来说会造成数据丢失。因此,我们需要进行数据的持久化,我常用 host 挂载与 container 挂载两种方式。

我们可以通过将宿主机的某个具体的目录挂载映射至容器内的目录来实现持久化:

# 通过宿主机目录挂载容器内目录
docker run -v <host-file-path>:<container-file-path> <image:tag>

也可以通过 container 挂载的方式,使用 volume 来实现持久化:

# 可以通过名字来引用 volume
# Docker 会自动生成一个路径
# Windows: C:\ProgramData\docker\volumes
# Linux: /var/lib/docker/volumes
# macOS: /var/lib/docker/volumes
docker run -v <volume-name>:<container-file-path> <image:tag>

如果只是需要挂载,不需要对文件进行具体的管理查看等,我们也可以通过 container 匿名挂载的方式,不指定 volume 名称,而使用其自动生成的目录:

# Docker 会自动生成一个路径
# Windows: C:\ProgramData\docker\volumes
# Linux: /var/lib/docker/volumes
# macOS: /var/lib/docker/volumes
docker run -v <container-file-path> <image:tag>

Docker Compose

Docker 提供了丰富的命令供我们使用,但是使用命令行操作不易于记忆,且如果应用依赖多个环境/服务,则需要分别运行与管理多个容器,造成不便。因此,我们可以通过 Docker Compose 工具来进行管理。

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具,其通过 .yaml 文件来进行配置管理。我在日常工作中使用最高频率的也是 Docker Compose,只有一些很简单的应用才会使用 docker run 命令来启动,也便于统一管理和后续的配置调整。

安装

macOS 系统如果安装了 Docker Desktop 则已经自带了 Docker Compose,可以直接使用。如果是 Linux 系统则需要单独安装,我这里同样以 CentOS 7 为例:

curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

完成安装后就可以使用 docker-compose 命令了。

配置管理

Docker Compose 的配置文件是一个 yaml 文件,其基本格式如下:

version: '3'
services:
  contrainer-1:
    image: <image-name>
    ports:
      - <host>:<container>
    volumes:
      - <host-file-path>:<container-file-path>
    environment:
      <ENV-KEY>=<ENV-VALUE>
  contrainer-2:
    image: <image-name>
    ports:
      - <host>:<container>
    volumes:
      - <volume-name-1>:<container-file-path>
    environment:
      <ENV-KEY>=<ENV-VALUE>
volumes:
  volume-name-1:
    driver: local

其大部分配置都很直观,如服务名称、镜像名称、端口映射、文件挂载、环境变量等。

其中,version 表示配置文件的版本,services 表示服务列表,volumes 表示挂载的卷列表。

在具体的 services 中,image 表示镜像名称,ports 表示端口映射,volumes 表示文件挂载,environment 表示环境变量,更多配置可以根据项目需要进行查看。

常用命令

启动/停止服务

docker run 命令类似,Docker Compose 也提供了 updown 命令来启动和停止服务。

# 启动服务
docker-compose -f <name>.yaml up

# 以 detached 模式启动服务
docker-compose -f <name>.yaml up -d

# 停止服务
docker-compose -f <name>.yaml down

查看日志

我们可以通过 logs 命令来查看服务的日志。

# 查看日志
docker-compose logs <container-id>

# 滚动查看日志
docker-compose logs -f <container-id>

实用操作命令

除了以上基础命令外,我常用的还有以下几个常用命令。

清除无用容器

当我们因配置或程序运行时调用出错而导致容器实例退出时,其依然会保留,可以通过 docker ps -a 命令来查看,我们可以通过以下组合命令进行清理:

docker rm `docker ps -a | grep Exited | awk '{print $1}'`

批量导入本地镜像

当我们需要将大量本地镜像导入机器时,如果一个个导入会非常麻烦,我们可以将镜像放入同一个目录并通过以下命令进行批量导入:

for i in `ls`; do docker load < $i ; done

总结

以上就是我对 Docker 容器技术的基础知识与实用操作的讲解,希望对你有所帮助。其实 Docker 的内容还有很多,如在上一个项目中尝试用到 Docker 的 Buildkit 特性,极大减小了最终构建镜像的大小,以及使用到 buildx 来实现跨平台兼容等等,本文旨在讲解基础知识与实践中常用的命令,这些拓展部分如果大家感兴趣的话后续再进行更新。

参考资料

  1. Docker 官网
  2. Docker Tutorial for Beginners