85 Commits

Author SHA1 Message Date
yeasy
d6fff9a3f7 Fix broken AliCloud screenshot link 2026-03-09 21:43:51 -07:00
yeasy
fc276d3b4f Expand Compose introduction and value proposition 2026-03-09 21:43:48 -07:00
yeasy
40ded62baa Clean gitignore 2026-03-09 20:04:22 -07:00
yeasy
89b0dc4425 Add details for cloud images 2026-03-09 20:04:22 -07:00
yeasy
330e084e00 Improve READMEs and add contribution prompts 2026-03-09 20:04:21 -07:00
yeasy
e62b203f1a Expand Tencent Cloud and Alibaba Cloud docs 2026-03-09 20:04:20 -07:00
yeasy
cb6bf74a2e Add pitfall notes to COPY and networking docs 2026-03-09 20:04:19 -07:00
yeasy
a980e34276 Fix cgroups, VOLUME, and tmpfs descriptions 2026-03-09 20:04:19 -07:00
yeasy
3d33e00802 Bump Compose to v5.1.0 and drop deprecated version field 2026-03-09 20:04:18 -07:00
yeasy
1a820c8c8b Bump Flannel to v0.28.1 2026-03-09 20:04:17 -07:00
yeasy
585b364574 Add etcd 3.4 end-of-life warning 2026-03-09 20:04:17 -07:00
yeasy
a0a5de7f11 Upgrade EFK stack and cAdvisor versions 2026-03-09 20:04:16 -07:00
yeasy
83ba6b7b47 Upgrade GitHub Actions and Cosign in security docs 2026-03-09 20:04:16 -07:00
yeasy
53f20dede7 Minor fix 2026-03-09 20:04:15 -07:00
baohua
48c8b50cf7 Fix typography 2026-03-09 20:04:13 -07:00
baohua
70ef2cba58 Fix heading hierarchy 2026-03-05 19:24:01 -08:00
baohua
f5cfa4140a Update containerd architecture 2026-03-05 20:34:40 -08:00
baohua
d174cf327c Update table of contents 2026-03-05 23:48:13 -08:00
baohua
6a9ce44c5a Add learning roadmap 2026-03-05 19:14:37 -08:00
baohua
c09f66da55 Add practical examples 2026-03-05 22:23:06 -08:00
baohua
635e05ad34 Add performance optimization 2026-03-05 21:20:06 -08:00
baohua
6f22d0f5f0 Add image security 2026-03-05 22:19:18 -08:00
baohua
17517e26b7 Add advanced networking 2026-03-05 19:39:42 -08:00
Baohua Yang
9fcaff6673 Fix typo 2026-03-04 17:35:19 -08:00
baohua
a096947382 Optimize content and fix issues 2026-03-03 19:30:21 -08:00
Baohua Yang
be09a95d0d Refine words 2026-03-02 22:16:51 -08:00
Baohua Yang
3af007b176 Use more accurate words 2026-03-02 21:58:39 -08:00
Baohua Yang
551dcfd2cb Refine content 2026-03-02 21:57:32 -08:00
Baohua Yang
0d2654fbf2 Update content 2026-03-02 21:56:46 -08:00
Baohua Yang
3894ba56bc Fix format 2026-03-02 21:55:31 -08:00
Baohua Yang
0dd0d036a2 Update and fix format 2026-03-02 21:54:37 -08:00
Baohua Yang
a9c4189c0c Remove duplicated content 2026-03-01 22:48:51 -08:00
Baohua Yang
56deeaee0f Use docker engine v29 2026-03-01 22:47:56 -08:00
Baohua Yang
0ff67cc893 Add security note 2026-02-28 14:28:38 -08:00
Baohua Yang
70cab9e92d release v1.6.0 2026-02-27 22:29:50 -08:00
github-actions[bot]
67547c0c3e Merge pull request #559 from yeasy/dependabot/npm_and_yarn/dependencies-8b11ed37b5
chore(deps): bump the dependencies group with 2 updates
2026-02-28 05:57:27 +00:00
dependabot[bot]
0d012c2d27 chore(deps): bump the dependencies group with 2 updates
Bumps the dependencies group with 2 updates: [chalk](https://github.com/chalk/chalk) and [vuepress](https://github.com/vuejs/vuepress/tree/HEAD/packages/vuepress).


Updates `chalk` from 4.1.2 to 5.6.2
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/compare/v4.1.2...v5.6.2)

Updates `vuepress` from 1.8.2 to 1.9.10
- [Release notes](https://github.com/vuejs/vuepress/releases)
- [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/vuepress)

---
updated-dependencies:
- dependency-name: chalk
  dependency-version: 5.6.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dependencies
- dependency-name: vuepress
  dependency-version: 1.9.10
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 05:57:17 +00:00
Baohua Yang
1eddef954e Enable auto bump 2026-02-27 21:55:34 -08:00
Baohua Yang
6baf2c68f2 Minor fix words 2026-02-27 20:23:35 -08:00
github-actions[bot]
fd28d966ca Merge pull request #556 from yeasy/dependabot/npm_and_yarn/commander-14.0.3
chore(deps): bump commander from 7.2.0 to 14.0.3
2026-02-28 03:32:10 +00:00
dependabot[bot]
ba0c388384 chore(deps): bump commander from 7.2.0 to 14.0.3
Bumps [commander](https://github.com/tj/commander.js) from 7.2.0 to 14.0.3.
- [Release notes](https://github.com/tj/commander.js/releases)
- [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tj/commander.js/compare/v7.2.0...v14.0.3)

---
updated-dependencies:
- dependency-name: commander
  dependency-version: 14.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 03:31:56 +00:00
github-actions[bot]
fc4f1ce2d0 Merge pull request #555 from yeasy/dependabot/npm_and_yarn/honkit-6.1.6
chore(deps): bump honkit from 5.1.5 to 6.1.6
2026-02-28 03:31:55 +00:00
dependabot[bot]
276eb356a2 chore(deps): bump honkit from 5.1.5 to 6.1.6
Bumps [honkit](https://github.com/honkit/honkit) from 5.1.5 to 6.1.6.
- [Release notes](https://github.com/honkit/honkit/releases)
- [Changelog](https://github.com/honkit/honkit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/honkit/honkit/compare/v5.1.5...v6.1.6)

---
updated-dependencies:
- dependency-name: honkit
  dependency-version: 6.1.6
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 03:31:43 +00:00
github-actions[bot]
246fc35c8e Merge pull request #554 from yeasy/dependabot/github_actions/actions/checkout-6.0.2
chore(deps): bump actions/checkout from 4.1.1 to 6.0.2
2026-02-28 03:31:04 +00:00
dependabot[bot]
745b7c812a chore(deps): bump actions/checkout from 4.1.1 to 6.0.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.1.1...v6.0.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 03:30:50 +00:00
Baohua Yang
ad3b86d727 Allow auto bump dependencies 2026-02-27 19:30:13 -08:00
Baohua Yang
16e884da51 Add more terms 2026-02-27 19:24:38 -08:00
Baohua Yang
5bd2c37c54 Add more tools 2026-02-27 19:23:50 -08:00
Baohua Yang
19aaad591c Reduce image size 2026-02-27 19:23:28 -08:00
Baohua Yang
bbfc9d34ff chore: remove version lock from plugins list 2026-02-27 13:52:22 -08:00
Baohua Yang
ecab788013 Add more content and fix format 2026-02-25 21:06:21 -08:00
Baohua Yang
dd449bc84f Minor fix level 2026-02-25 08:40:01 -08:00
Baohua Yang
d6e0838807 Simplify words and fix format issues 2026-02-24 22:01:02 -08:00
Baohua Yang
c44cbfcd62 Fix all missed links 2026-02-24 21:06:40 -08:00
Baohua Yang
c6e2ab8a4a Fix missing links 2026-02-24 13:01:16 -08:00
Baohua Yang
669da56662 Use list for simple content 2026-02-23 09:06:27 -08:00
Baohua Yang
4941ef171c Update figure 2026-02-23 08:56:56 -08:00
Baohua Yang
572266b2f4 Fix wrong links 2026-02-22 16:04:41 -08:00
Baohua Yang
4ca47b0ea1 Split chapter 20 2026-02-22 15:33:20 -08:00
Baohua Yang
24d26c0797 Fix double quotes 2026-02-22 14:54:52 -08:00
Baohua Yang
e57704271d Fix missing links 2026-02-22 13:40:20 -08:00
Baohua Yang
92ea9623b2 Fix naming of the chapter dir 2026-02-22 12:42:15 -08:00
Baohua Yang
b9ac198f19 Add number to section names 2026-02-22 08:37:51 -08:00
Baohua Yang
185da0cf88 Fix period mark 2026-02-21 22:44:57 -08:00
Baohua Yang
8965f084eb Update content 2026-02-21 22:42:46 -08:00
Baohua Yang
175aaae48a style(docs): standardize heading formats and numbering
- Enforce Level 1-3 structural numbering based on SUMMARY.md hierarchy
- Remove structural numbering from Level 4+ headings
- Eliminate single child headings by converting to bold text
- Auto-inject summary text for headings with multiple children missing intro text
- Exclude Appendix chapters from structural numbering
- Avoid modifying code block contents
- Add script to detect non-standard English usage in headers
2026-02-21 22:40:33 -08:00
Baohua Yang
33af380be1 Restruct and reorder chapters 2026-02-21 22:22:17 -08:00
Baohua Yang
2ab40eacc0 Fix space with bold markdown 2026-02-21 17:39:37 -08:00
Baohua Yang
6aa7a51fef chore: remove unused historic image files 2026-02-21 16:43:31 -08:00
Baohua Yang
79ac9c639a style: apply global formatting fixes (struct, spacing, zhlint) 2026-02-21 11:08:52 -08:00
Baohua Yang
ad68b2d973 Fix ascii figures 2026-02-21 10:59:17 -08:00
Baohua Yang
5ae315c9dd Continue to fix format issues 2026-02-21 10:47:37 -08:00
Baohua Yang
47cfc173a6 Fix format issue 2026-02-21 10:19:28 -08:00
Baohua Yang
063c3f1381 Fix grammar 2026-02-17 22:17:02 -08:00
Baohua Yang
f68795f34c Fix errors 2026-02-16 18:45:40 -08:00
Baohua Yang
acc74f17bb release: v1.5.4 2026-02-15 15:14:03 -08:00
Baohua Yang
4fa2fd8543 fix: ensure directories exist in CI before copying 2026-02-15 15:12:17 -08:00
Baohua Yang
95572d3d47 chore: remove combine.py and ignore it 2026-02-15 09:13:06 -08:00
Baohua Yang
e58bd39de5 chore: remove combine.py and ignore it 2026-02-15 09:12:40 -08:00
Baohua Yang
aa589406b9 release v1.5.3: fix CI image paths and update config 2026-02-15 09:09:02 -08:00
Baohua Yang
0835f8467a Fix and improve 2026-02-12 16:51:50 -08:00
Baohua Yang
bae82e993a Move more dockerfile content to chapter 7 2026-02-09 13:13:33 -08:00
Baohua Yang
b44c9acd6c Restruct 2026-02-09 12:56:12 -08:00
baohua
63377d0431 Fix and update 2026-02-09 11:34:35 -08:00
Baohua Yang
e669ee0fe8 Use a better structure 2026-02-09 09:32:05 -08:00
391 changed files with 15312 additions and 8913 deletions

2
.gitattributes vendored
View File

@@ -1,4 +1,4 @@
* text=auto
* text=auto eol=lf
*.sh text eol=lf

42
.github/CODEOWNERS vendored
View File

@@ -2,28 +2,28 @@
/.github/* @khs1994
/.travis/* @khs1994
/.vuepress/* @khs1994
/advanced_network/* @yeasy @khs1994
/01_introduction/* @yeasy @khs1994
/02_basic_concept/* @yeasy @khs1994
/03_install/* @khs1994
/04_image/* @yeasy @khs1994
/05_container/* @yeasy @khs1994
/06_repository/* @khs1994
/07_dockerfile/* @yeasy @khs1994
/08_data/* @yeasy @khs1994
/09_network/* @yeasy @khs1994
/10_buildx/* @khs1994
/11_compose/* @yeasy @khs1994
/12_implementation/* @yeasy @khs1994
/13_kubernetes_concepts/* @yeasy @khs1994
/14_kubernetes_setup/* @yeasy @khs1994
/15_etcd/* @yeasy @khs1994
/16_cloud/* @khs1994
/17_ecosystem/* @khs1994
/18_security/* @yeasy @khs1994
/19_observability/* @yeasy @khs1994
/20_cases_os/* @yeasy @khs1994
/21_case_devops/* @yeasy @khs1994
/appendix/* @yeasy @khs1994
/archive/* @khs1994
/basic_concept/* @yeasy @khs1994
/buildx/* @khs1994
/cases/* @yeasy @khs1994
/cloud/* @khs1994
/compose/* @yeasy @khs1994
/container/* @yeasy @khs1994
/coreos/* @khs1994
/data_management/* @khs1994
/etcd/* @khs1994
/IDE/* @khs1994
/image/* @yeasy @khs1994
/install/* @khs1994
/introduction/* @yeasy @khs1994
/kubernetes/* @yeasy @khs1994
/network/* @yeasy @khs1994
/opensource/* @khs1994
/repository/* @khs1994
/security/* @yeasy @khs1994
/underly/* @yeasy @khs1994
/.drone.yml @khs1994
/.editorconfig/ @khs1994
/.gitattributes @khs1994

View File

@@ -7,6 +7,7 @@ about: Create a report to help us improve
* [ ] Have u googled the problem? If no, pls do that first!
### Environment
<!--请提供环境信息包括操作系统版本等保留你的操作系统其他选项删除-->
<!--Provides env info like OS version-->
@@ -21,6 +22,7 @@ about: Create a report to help us improve
* [x] Others (Pls describe below)
### Docker Version
<!--如果你的 Docker 版本低于 20.10 请尽可能升级到该版本保留你的 Docker 版本其他选项删除-->
<!--if Docker version under 20.10, please upgrade Docker to 20.10-->
@@ -29,13 +31,9 @@ about: Create a report to help us improve
* [x] 1.13.0 or Before
### Problem Description
<!--描述你的问题请贴出操作步骤终端报错截图或文字信息-->
<!--describe problem with detailed steps and logs-->
<!--提交问题之前请点击预览标签符合要求之后再提交问题-->

View File

@@ -7,6 +7,7 @@ about: Create a issue about Docker
* [ ] Have u googled the problem? If no, pls do that first!
### Environment
<!--请提供环境信息包括操作系统版本等保留你的操作系统其他选项删除-->
<!--Provides env info like OS version-->
@@ -21,6 +22,7 @@ about: Create a issue about Docker
* [x] Others (Pls describe below)
### Docker Version
<!--如果你的 Docker 版本低于 20.10 请尽可能升级到该版本保留你的 Docker 版本其他选项删除-->
<!--if Docker version under 20.10, please upgrade Docker to 20.10-->
@@ -29,13 +31,9 @@ about: Create a issue about Docker
* [x] 1.13.0 or Before
### Problem Description
<!--描述你的问题请贴出操作步骤终端报错截图或文字信息-->
<!--describe problem with detailed steps and logs-->
<!--提交问题之前请点击预览标签符合要求之后再提交问题-->

View File

@@ -1,6 +1,6 @@
<!--
Thanks for your contribution.
See [CONTRIBUTING](CONTRIBUTING.md) for contribution guidelines.
See [CONTRIBUTING](../CONTRIBUTING.md) for contribution guidelines.
-->
**Proposed changes (Mandatory)**

27
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "chore(deps)"
labels:
- "dependencies"
groups:
dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "chore(deps)"
labels:
- "dependencies"
groups:
dependencies:
patterns:
- "*"

View File

@@ -8,7 +8,7 @@ jobs:
name: check-link
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- uses: actions/checkout@v6.0.2
# search Issues :-(
- run: |
docker run -i --rm \

View File

@@ -14,7 +14,7 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- uses: actions/checkout@v6.0.2
- name: Build Gitbook
uses: docker://yeasy/docker_practice
with:
@@ -81,17 +81,16 @@ jobs:
echo "vuepress.mirror.docker-practice.com" > .vuepress/dist/CNAME
cp -r _images .vuepress/dist
mkdir -p .vuepress/dist/appendix
cp -r appendix/_images .vuepress/dist/appendix
cp -r cases/ci/drone/_images .vuepress/dist/cases/ci/drone
cp -r cases/os/_images .vuepress/dist/cases/os
cp -r cloud/_images .vuepress/dist/cloud
cp -r data_management/_images .vuepress/dist/data_management
cp -r etcd/_images .vuepress/dist/etcd
cp -r image/_images .vuepress/dist/image
cp -r install/_images .vuepress/dist/install
cp -r introduction/_images .vuepress/dist/introduction
cp -r kubernetes/_images .vuepress/dist/kubernetes
cp -r underly/_images .vuepress/dist/underly
mkdir -p .vuepress/dist/cloud
cp -r 16_cloud/_images .vuepress/dist/cloud
mkdir -p .vuepress/dist/data_management
cp -r 08_data/_images .vuepress/dist/data_management
mkdir -p .vuepress/dist/etcd
cp -r 15_etcd/_images .vuepress/dist/etcd
mkdir -p .vuepress/dist/kubernetes
cp -r 13_kubernetes_concepts/_images .vuepress/dist/kubernetes
echo "include: [_images]" > .vuepress/dist/_config.yml
- name: Upload Vuepress dist

View File

@@ -0,0 +1,27 @@
name: Dependabot auto-merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}

10
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*.tmp
.idea/
_book/
format_report.txt
*.swp
*.edx
.DS_Store
@@ -15,3 +16,12 @@ docker-compose.override.yml
# Editor configs
.obsidian/
.vscode/
.agent/
__pycache__/
# Check scripts
check*.py
find*.py
fix*.py
format*.py

View File

@@ -106,7 +106,7 @@ module.exports = config({
},
{
text: '安装 Docker',
link: '/install/',
link: '/03_install/',
},
{
text: 'Docker 入门',
@@ -114,23 +114,23 @@ module.exports = config({
},
{
text: 'Docker 实战',
link: '/cases/os/'
link: '/15_cases/os/'
},
{
text: 'CI/CD',
link: '/cases/ci/'
link: '/15_cases/ci/'
},
{
text: 'Compose',
link: '/compose/',
link: '/10_compose/',
},
{
text: 'Kubernetes',
link: '/kubernetes/',
link: '/12_orchestration/kubernetes/',
},
{
text: "云计算",
link: "/cloud/",
link: "/13_ecosystem/cloud/",
},
// {
// text: 'GitHub',
@@ -152,7 +152,8 @@ module.exports = config({
// }]
// }
],
sidebar: {
sidebar: "auto",
legacySidebar: {
'/cloud/': [
'intro',
'tencentCloud',
@@ -295,8 +296,8 @@ module.exports = config({
'image/dockerfile/shell',
'image/dockerfile/onbuild',
'image/dockerfile/references',
'image/multistage-builds/',
'image/multistage-builds/laravel',
'image/dockerfile/7.17_multistage_builds.md',
'image/dockerfile/7.18_multistage_builds_laravel.md',
'image/manifest',
]
}, {

3
.zhlintignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules/
.vuepress/
.git/

View File

@@ -1,8 +1,8 @@
# 快速上手 (5分钟)
## 1.1 快速上手
本节将通过一个简单的 Web 应用例子带你快速体验 Docker 的核心流程构建镜像运行容器
## 1. 准备代码
### 1.1.1 准备代码
创建一个名为 `hello-docker` 的文件夹并在其中创建一个 `index.html` 文件
@@ -10,7 +10,7 @@
<h1>Hello, Docker!</h1>
```
## 2. 编写 Dockerfile
### 1.1.2 编写 Dockerfile
在同级目录下创建一个名为 `Dockerfile` (无后缀) 的文件
@@ -19,7 +19,7 @@ FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
```
## 3. 构建镜像
### 1.1.3 构建镜像
打开终端进入该目录执行构建命令
@@ -27,11 +27,11 @@ COPY index.html /usr/share/nginx/html/index.html
$ docker build -t my-hello-world .
```
* `docker build`: 构建命令
* `-t my-hello-world`: 给镜像起个名字标签
* `.`: 指定上下文路径为当前目录
* `docker build`构建命令
* `-t my-hello-world`给镜像起个名字 (标签)
* `.`指定上下文路径为当前目录
## 4. 运行容器
### 1.1.4 运行容器
使用刚才构建的镜像启动一个容器
@@ -39,27 +39,30 @@ $ docker build -t my-hello-world .
$ docker run -d -p 8080:80 my-hello-world
```
* `docker run`: 运行命令
* `-d`: 后台运行
* `-p 8080:80`: 将宿主机的 8080 端口映射到容器的 80 端口
* `docker run`运行命令
* `-d`后台运行
* `-p 8080:80`将宿主机的 8080 端口映射到容器的 80 端口
## 5. 访问测试
### 1.1.5 访问测试
打开浏览器访问 [http://localhost:8080](http://localhost:8080),你应该能看到 "Hello, Docker!"
打开浏览器访问 [http://localhost:8080](http://localhost:8080),你应该能看到 Hello, Docker!
## 6. 清理
### 1.1.6 清理
停止并删除容器
```bash
# 查看正在运行的容器 ID
## 查看正在运行的容器 ID
$ docker ps
# 停止容器
## 停止容器
$ docker stop <CONTAINER_ID>
# 删除容器
## 删除容器
$ docker rm <CONTAINER_ID>
```
恭喜你已经完成了第一次 Docker 实战接下来请阅读 [Docker 核心概念](../02_basic_concept/README.md) 做深入了解
恭喜你已经完成了第一次 Docker 实战接下来请阅读 [Docker 核心概念](../02_basic_concept/README.md)做深入了解

120
01_introduction/1.2_what.md Normal file
View File

@@ -0,0 +1,120 @@
## 1.2 什么是 Docker
Docker 是彻底改变了软件开发和交付方式的革命性技术本节将从核心概念与传统虚拟机的对比技术基础以及历史生态等多个维度带你深入理解什么是 Docker
### 1.2.1 一句话理解 Docker
> **Docker 是一种轻量级的虚拟化技术它让应用程序及其依赖环境可以被打包成一个标准化的单元在任何地方都能一致地运行** 如果用一个生活中的类比**Docker 之于软件就像集装箱之于货物**
在集装箱发明之前货物的运输是一件麻烦的事情不同的货物需要不同的包装不同的装卸方式换一种运输工具就要重新装卸集装箱的出现改变了这一切无论里面装的是什么集装箱的外形是标准的可以用同样的方式装卸堆放和运输
Docker 做的事情类似无论你的应用是用 PythonJavaNode.js 还是其他语言写的无论它需要什么样的依赖库和环境一旦被打包成 Docker 镜像就可以用同样的方式在任何支持 Docker 的机器上运行
### 1.2.2 Docker 的核心价值
笔者认为Docker 解决的是软件开发中最古老的问题之一**在我机器上明明能跑啊**
```mermaid
flowchart LR
subgraph Dev ["开发环境"]
direction TB
A["Python 3.14<br/>Ubuntu 24.04<br/>特定版本的库"] --> B["运行正常"]
end
subgraph Prod ["生产环境"]
direction TB
C["Python 3.11<br/>Ubuntu 22.04<br/>不同版本的库"] --> D["运行失败!"]
end
A -. "≠" .-> C
```
有了 Docker
```mermaid
flowchart LR
subgraph Dev ["开发环境"]
direction TB
A["Docker 镜像<br/>(包含所有依赖)"] --> B["运行正常"]
end
subgraph Prod ["生产环境"]
direction TB
C["同一个镜像<br/>(完全一致)"] --> D["运行正常!"]
end
A === "=" === C
```
### 1.2.3 Docker vs 虚拟机
很多人第一次接触 Docker 时会问**这不就是虚拟机吗** 答案是**不是而且差别很大**
#### 传统虚拟机
传统虚拟机技术是虚拟出一套完整的硬件在其上运行一个完整的操作系统再在该系统上运行应用
![传统虚拟化](../_images/virtualization.png)
#### Docker 容器
Docker 容器内的应用直接运行于宿主的内核容器内没有自己的内核也没有进行硬件虚拟
![Docker](../_images/docker.png)
#### 关键区别
| 特性 | Docker 容器 | 传统虚拟机 |
|------|-------------|------------|
| **启动速度** | 秒级 | 分钟级 |
| **资源占用** | MB 级别 | GB 级别 |
| **性能** | 接近原生 | 有明显损耗 |
| **隔离级别** | 进程级隔离 | 完全隔离 |
| **单机数量** | 可运行上千个 | 通常几十个 |
> 笔者经常用这个类比来解释虚拟机像是每个应用都住在一栋独立的房子里 (有自己的地基水电系统)而容器像是大家住在同一栋公寓楼里的不同房间 (共享地基和水电系统但各自独立)
### 1.2.4 Docker 的技术基础
Docker 使用 [Go 语言](https://golang.google.cn/)开发,基于 Linux 内核的以下技术:
- **[Namespace](https://en.wikipedia.org/wiki/Linux_namespaces)**:实现资源隔离 (进程、网络、文件系统等)
- **[Cgroups](https://zh.wikipedia.org/wiki/Cgroups)**:实现资源限制 (CPU、内存、I/O 等)
- **[Union FS](https://en.wikipedia.org/wiki/Union_mount)**:实现分层存储 (如 OverlayFS)
> 如果你对这些底层技术感兴趣可以阅读本书的[底层实现](../12_implementation/README.md)章节
#### Docker 架构演进
Docker 的底层实现经历了多次演进
```mermaid
flowchart LR
subgraph Timeline
direction LR
LXC["LXC<br/>(2013)"] --> libcontainer["libcontainer<br/>(2014)"]
libcontainer --> runC["runC<br/>(2015)"]
runC --> containerd["containerd + runC<br/>(现在)"]
runC --> OCI["OCI<br/>标准化"]
end
```
- **LXC** (2013)Docker 最初基于 Linux Containers
- **libcontainer** (2014v0.7)Docker 自研的容器运行时
- **runC** (2015v1.11)捐献给 OCI 的标准容器运行时
- **containerd**高级容器运行时管理容器生命周期
![Docker 架构](../_images/docker-on-linux.png)
> `runc` 是一个 Linux 命令行工具用于根据 [OCI 容器运行时规范](https://github.com/opencontainers/runtime-spec)创建和运行容器。
> `containerd` 是一个守护程序它管理容器生命周期提供了在一个节点上执行容器和管理镜像的最小功能集
### 1.2.5 Docker 的历史与生态
**Docker** 最初是 `dotCloud` 公司创始人 [Solomon Hykes](https://github.com/shykes) 在法国期间发起的一个公司内部项目,于 [2013 年 3 月以 Apache 2.0 授权协议开源](https://en.wikipedia.org/wiki/Docker_(software))。
Docker 的发展历程
- **2013 3 **开源发布
- **2013 年底**dotCloud 公司改名为 DockerInc
- **2015 **成立[开放容器联盟 (OCI)](https://opencontainers.org/),推动容器标准化
- **至今**[GitHub 项目](https://github.com/moby/moby)超过 7 万星标
Docker 的成功推动了整个容器生态的发展催生了 KubernetesPodman 等众多相关项目笔者认为Docker 最大的贡献不仅是技术本身更是它 **让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**

206
01_introduction/1.3_why.md Normal file
View File

@@ -0,0 +1,206 @@
## 1.3 为什么要用 Docker
在回答 为什么用 Docker 之前笔者想先问一个问题**你有没有经历过这些场景**
### 1.3.1 没有 Docker 的世界
Docker 出现之前软件开发和运维面临着诸多棘手的问题我们先来看看以下三个典型的痛点场景
#### 场景一在我电脑上明明能跑
```bash
周五下午 5:00
├── 开发者:代码写完了,本地测试通过,提交!🎉
├── 周一早上 9:00
│ └── 测试:"这个功能在测试环境跑不起来"
└── 开发者:" 不可能,在我电脑上明明能跑啊……"
```
笔者统计过这个问题通常由以下原因导致
- Python/Node/Java 版本不一致
- 依赖库版本不一致
- 操作系统配置不一致
- 某些环境变量没有设置
- 忘了说我本地装了个 XXX
#### 场景二环境配置的噩梦
```bash
新同事入职
├── Day 1领电脑配环境
├── Day 2继续配环境遇到问题
├── Day 3换种方法配环境
├── Day 4问老同事怎么配的他也忘了
└── Day 5终于能跑起来了但不知道为什么……
```
#### 场景三服务器迁移的恐惧
```bash
运维:"我们需要把服务迁移到新服务器"
开发:"旧服务器上的配置文档在哪?"
运维:"当时是一个已经离职的同事配的……"
所有人:😱
```
### 1.3.2 Docker 如何解决这些问题
Docker 的出现为上述问题提供了完美的解决方案它通过 一次构建到处运行 的核心理念从根本上改变了软件交付的方式
#### 核心理念一次构建到处运行
```mermaid
flowchart LR
dev["开发团队"] -->|创建| img["Docker 镜像"]
img -->|测试团队验证| test["测试团队"]
test -- "有问题<br/>反馈修改和更新" --> dev
test -- "没问题<br/>发布" --> prod["生产环境"]
```
### 1.3.3 Docker 的核心优势
除了解决上述痛点Docker 还拥有诸多显著的技术优势包括环境一致性秒级启动高效的资源利用等
#### 1. 环境一致性
Docker 镜像包含了应用运行所需的 **一切**代码运行时系统工具配置这意味着
- 开发环境和生产环境完全一致
- 不会再有 在我机器上能跑 的问题
- 新人入职一条命令就能启动开发环境
```bash
## 新同事入职第一天
$ git clone https://github.com/company/project.git
$ docker compose up
## 完整的开发环境就准备好了
...
```
#### 2. 秒级启动
传统虚拟机启动需要几分钟 (引导操作系统) Docker 容器启动通常只需要 **几秒甚至几百毫秒**
笔者实测数据
| 启动内容 | 虚拟机 | Docker 容器 |
|---------|--------|-------------|
| 空系统 | ~60 | ~0.5 |
| MySQL | ~90 | ~3 |
| 完整 Web 应用 | ~120 | ~5 |
这个差异对以下场景尤为重要
- **CI/CD 流水线**每次构建节省几分钟一天累积下来就是几小时
- **弹性扩容**流量高峰时能快速启动更多实例
- **开发体验**快速重启服务进行调试
#### 3. 资源效率
Docker 容器共享宿主机内核无需为每个应用运行完整的操作系统以一台 64GB 内存的物理服务器为例
- **传统虚拟机方案**每个虚拟机都需要运行完整的操作系统每个额外占用如 2GB 内存产生大量资源开销实际可用于应用的内存可能只有约 18GB
- **Docker 方案**容器直接共享宿主机系统只需付出很少的基础开销OS 及引擎约 4GB即可将约 60GB 的内存全部用于实际应用
```mermaid
flowchart TD
subgraph VM ["传统虚拟机方案 ❌"]
direction TB
Server1["物理服务器 (64GB 内存)"]
subgraph VMs ["可用应用内存: 约 18GB"]
direction LR
VM1["VM 1: 应用 1<br/>(含 2GB OS)"]
VM2["VM 2: 应用 2<br/>(含 2GB OS)"]
VM3["VM 3: 应用 3<br/>(含 2GB OS)"]
end
Server1 --- VMs
end
subgraph Docker ["Docker 方案 ✅"]
direction TB
Server2["物理服务器 (64GB 内存)<br/>含约 4GB OS及引擎配置"]
subgraph Containers ["可用应用内存: 约 60GB"]
direction LR
C1["容器 1: 应用 1<br/>(按需分配)"]
C2["容器 2: 应用 2<br/>(按需分配)"]
C3["容器 3: 应用 3<br/>(按需分配)"]
end
Server2 --- Containers
end
```
#### 4. 持续交付和部署
Docker 完美契合 DevOps 的工作流程
```mermaid
flowchart LR
A["代码提交<br/>(Git push)"] --> B["自动构建镜像<br/>(docker build)"]
B --> C["自动测试<br/>(容器内运行测试)"]
C --> D["自动部署<br/>(容器滚动更新)"]
```
使用 [Dockerfile](../04_image/4.5_build.md) 定义镜像构建过程使得
- 构建过程 **可重复可追溯**
- 任何人都能从代码重建完全相同的镜像
- 配合 [GitHub Actions](../21_case_devops/21.2_github_actions.md) CI 系统实现自动化
#### 5. 轻松迁移
Docker 可以在几乎任何平台上运行
- 本地开发机 (macOSWindowsLinux)
- 公有云 (AWSAzureGCP阿里云腾讯云)
- 私有云和自建数据中心
- 边缘设备和 IoT
**同一个镜像在任何地方运行结果都一致** 这让应用迁移变得前所未有的简单
#### 6. 微服务架构的基石
现代微服务架构几乎都依赖容器技术Docker 让你可以
- **隔离服务**每个服务运行在独立容器中互不干扰
- **独立扩展**哪个服务负载高就单独扩展哪个
- **独立部署**更新一个服务不影响其他服务
- **技术多样**不同服务可以用不同语言和框架
```mermaid
flowchart TD
subgraph Microservices ["微服务架构示例"]
direction TB
subgraph AppLayer ["应用层"]
direction LR
Frontend["前端容器<br/>(Node.js)"]
API["API 容器<br/>(Python)"]
Worker["Worker 容器<br/>(Go)"]
end
Redis["Redis 容器"]
DB["PostgreSQL 容器"]
Frontend --> API
API --> Redis
API --> DB
Worker --> Redis
Worker --> DB
end
```
### 1.3.4 Docker 不适合的场景
笔者认为技术选型要客观Docker 并非银弹以下场景可能不太适合
- **需要完全隔离的场景**容器共享宿主机内核隔离性不如虚拟机如果需要运行不受信任的代码虚拟机可能更安全
- **需要特殊内核的场景**容器使用宿主机内核如果应用需要特定版本的内核或内核模块可能需要虚拟机
- **Windows 原生应用**虽然 Docker 支持 Windows 容器但生态不如 Linux 容器成熟传统 Windows 应用的容器化仍有挑战
- **桌面应用**Docker 主要面向服务端应用桌面 GUI 应用的容器化虽然可行但通常得不偿失
### 1.3.5 与传统虚拟机的对比总结
关于容器与虚拟机的详细特性对比请参阅 [1.2.3 Docker vs 虚拟机](1.2_what.md) 中的对比表总结来说
- **性能差异**虚拟机通常有 5-20% 的性能损耗而容器接近原生性能
- **最佳场景**Docker 容器适合微服务CI/CD开发环境虚拟机适合多租户高安全需求场景

View File

@@ -1,17 +1,23 @@
# 简介
# 第一章 Docker 简介
本章将带领你进入 **Docker** 的世界
> **版本提示**本书内容及示例基于 **Docker Engine v29.x** 及以上版本值得注意的是 Docker Engine v29 官方在全新安装场景下 **默认启用了 `containerd image store` 作为镜像存储后端**取代了传统的经典存储引擎如 overlay2 graph driver这项底层革新极大增强了 Docker 对多架构镜像Multi-platform以及软件供应链安全元数据Attestations, SBOM, Provenance的本地支持原生性
## 本章内容
* [什么是 Docker](what.md)
* 介绍 Docker 的起源发展历程以及其背后的核心技术Cgroups, Namespaces, UnionFS
* [快速上手](1.1_quickstart.md)
* 通过一个简单的 Web 应用例子带你快速体验 Docker 的核心流程构建镜像运行容器
* [什么是 Docker](1.2_what.md)
* 介绍 Docker 的起源发展历程以及其背后的核心技术 (CgroupsNamespacesUnionFS以及 `containerd` 引擎的演进)
* 了解 Docker 是如何改变软件交付方式的
* [为什么要用 Docker](why.md)
* [为什么要用 Docker](1.3_why.md)
* 对比传统虚拟机技术阐述 Docker 在启动速度资源利用率交付效率等方面的巨大优势
* 探讨 Docker DevOps微服务架构中的关键作用
## 学习目标
通过本章的学习你将能够

View File

@@ -0,0 +1,11 @@
## 本章小结
- Docker 是一种轻量级虚拟化技术核心价值是 **环境一致性**
- 与虚拟机相比Docker 更轻量更快速资源利用率更高
- Docker 基于 Linux 内核的 NamespaceCgroups Union FS 技术
- Docker 推动了容器技术的标准化 (OCI) 和生态发展
Docker 的核心价值可以用一句话概括**让应用的开发测试部署保持一致同时极大提高资源利用效率** 笔者认为对于现代软件开发者来说Docker 已经不是 要不要学 的问题而是 **必备技能**无论你是前端后端运维还是全栈开发者掌握 Docker 都能让你的工作更高效
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

View File

@@ -1,124 +0,0 @@
# 什么是 Docker
## 一句话理解 Docker
> **Docker 是一种轻量级的虚拟化技术它让应用程序及其依赖环境可以被打包成一个标准化的单元在任何地方都能一致地运行**
如果用一个生活中的类比**Docker 之于软件就像集装箱之于货物**
在集装箱发明之前货物的运输是一件麻烦的事情不同的货物需要不同的包装不同的装卸方式换一种运输工具就要重新装卸集装箱的出现改变了这一切无论里面装的是什么集装箱的外形是标准的可以用同样的方式装卸堆放和运输
Docker 做的事情类似无论你的应用是用 PythonJavaNode.js 还是其他语言写的无论它需要什么样的依赖库和环境一旦被打包成 Docker 镜像就可以用同样的方式在任何支持 Docker 的机器上运行
## Docker 的核心价值
笔者认为Docker 解决的是软件开发中最古老的问题之一**"在我机器上明明能跑啊!"**
```
开发环境 生产环境
┌─────────────────┐ ┌─────────────────┐
│ Python 3.14 │ ≠ │ Python 3.11 │
│ Ubuntu 24.04 │ │ Ubuntu 22.04 │
│ 特定版本的库 │ │ 不同版本的库 │
└─────────────────┘ └─────────────────┘
↓ ↓
运行正常 运行失败!
```
有了 Docker
```
开发环境 生产环境
┌─────────────────┐ ┌─────────────────┐
│ Docker 镜像 │ = │ 同一个镜像 │
│ (包含所有依赖) │ │ (完全一致) │
└─────────────────┘ └─────────────────┘
↓ ↓
运行正常 运行正常!
```
## Docker vs 虚拟机
很多人第一次接触 Docker 时会问**"这不就是虚拟机吗?"**
答案是**不是而且差别很大**
### 传统虚拟机
传统虚拟机技术是虚拟出一套完整的硬件在其上运行一个完整的操作系统再在该系统上运行应用
![传统虚拟化](../_images/virtualization.png)
### Docker 容器
Docker 容器内的应用直接运行于宿主的内核容器内没有自己的内核也没有进行硬件虚拟
![Docker](../_images/docker.png)
### 关键区别
| 特性 | Docker 容器 | 传统虚拟机 |
|------|-------------|------------|
| **启动速度** | 秒级 | 分钟级 |
| **资源占用** | MB 级别 | GB 级别 |
| **性能** | 接近原生 | 有明显损耗 |
| **隔离级别** | 进程级隔离 | 完全隔离 |
| **单机数量** | 可运行上千个 | 通常几十个 |
> 笔者经常用这个类比来解释虚拟机像是每个应用都住在一栋独立的房子里有自己的地基水电系统而容器像是大家住在同一栋公寓楼里的不同房间共享地基和水电系统但各自独立
## Docker 的技术基础
Docker 使用 [Go 语言](https://golang.google.cn/) 开发,基于 Linux 内核的以下技术:
- **[Namespace](https://en.wikipedia.org/wiki/Linux_namespaces)**:实现资源隔离(进程、网络、文件系统等)
- **[Cgroups](https://zh.wikipedia.org/wiki/Cgroups)**实现资源限制CPU、内存、I/O 等)
- **[Union FS](https://en.wikipedia.org/wiki/Union_mount)**:实现分层存储(如 OverlayFS
> 如果你对这些底层技术感兴趣可以阅读本书的[底层实现](../13_implementation/README.md)章节
### Docker 架构演进
Docker 的底层实现经历了多次演进
```
2013 2014 2015 现在
│ │ │ │
▼ ▼ ▼ ▼
LXC ──→ libcontainer ──→ runC ──→ containerd + runC
└── OCI 标准化
```
- **LXC**2013Docker 最初基于 Linux Containers
- **libcontainer**2014v0.7Docker 自研的容器运行时
- **runC**2015v1.11捐献给 OCI 的标准容器运行时
- **containerd**高级容器运行时管理容器生命周期
![Docker 架构](../_images/docker-on-linux.png)
> `runc` 是一个 Linux 命令行工具用于根据 [OCI 容器运行时规范](https://github.com/opencontainers/runtime-spec) 创建和运行容器。
> `containerd` 是一个守护程序它管理容器生命周期提供了在一个节点上执行容器和管理镜像的最小功能集
## Docker 的历史与生态
**Docker** 最初是 `dotCloud` 公司创始人 [Solomon Hykes](https://github.com/shykes) 在法国期间发起的一个公司内部项目,于 [2013 年 3 月以 Apache 2.0 授权协议开源](https://en.wikipedia.org/wiki/Docker_(software))。
Docker 的发展历程
- **2013 3 **开源发布
- **2013 年底**dotCloud 公司改名为 Docker, Inc.
- **2015 **成立 [开放容器联盟OCI](https://opencontainers.org/),推动容器标准化
- **至今**[GitHub 项目](https://github.com/moby/moby) 超过 7 万星标
Docker 的成功推动了整个容器生态的发展催生了 KubernetesPodman 等众多相关项目笔者认为Docker 最大的贡献不仅是技术本身更是它**让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**
## 本章小结
- Docker 是一种轻量级虚拟化技术核心价值是**环境一致性**
- 与虚拟机相比Docker 更轻量更快速资源利用率更高
- Docker 基于 Linux 内核的 NamespaceCgroups Union FS 技术
- Docker 推动了容器技术的标准化OCI和生态发展
接下来让我们了解[为什么要使用 Docker](why.md)

View File

@@ -1,208 +0,0 @@
# 为什么要使用 Docker
在回答"为什么用 Docker"之前笔者想先问一个问题**你有没有经历过这些场景**
## 没有 Docker 的世界
### 场景一"在我电脑上明明能跑"
```
周五下午 5:00
├── 开发者:代码写完了,本地测试通过,提交!🎉
├── 周一早上 9:00
│ └── 测试:"这个功能在测试环境跑不起来"
└── 开发者:" 不可能,在我电脑上明明能跑啊……"
```
笔者统计过这个问题通常由以下原因导致
- Python/Node/Java 版本不一致
- 依赖库版本不一致
- 操作系统配置不一致
- 某些环境变量没有设置
- "哦,忘了说我本地装了个 XXX"
### 场景二环境配置的噩梦
```
新同事入职
├── Day 1领电脑配环境
├── Day 2继续配环境遇到问题
├── Day 3换种方法配环境
├── Day 4问老同事怎么配的他也忘了
└── Day 5终于能跑起来了但不知道为什么……
```
### 场景三服务器迁移的恐惧
```
运维:"我们需要把服务迁移到新服务器"
开发:"旧服务器上的配置文档在哪?"
运维:"当时是一个已经离职的同事配的……"
所有人:😱
```
## Docker 如何解决这些问题
### 核心理念一次构建到处运行
```
开发环境 测试环境 生产环境
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Docker │ = │ Docker │ = │ Docker │
│ 镜像 │ │ 镜像 │ │ 镜像 │
└─────────┘ └─────────┘ └─────────┘
↓ ↓ ↓
完全一致 完全一致 完全一致
```
## Docker 的核心优势
### 1. 环境一致性
Docker 镜像包含了应用运行所需的**一切**代码运行时系统工具配置这意味着
- 开发环境和生产环境完全一致
- 不会再有"在我机器上能跑"的问题
- 新人入职一条命令就能启动开发环境
```bash
# 新同事入职第一天
$ git clone https://github.com/company/project.git
$ docker compose up
# 完整的开发环境就准备好了
```
### 2. 秒级启动
传统虚拟机启动需要几分钟引导操作系统 Docker 容器启动通常只需要**几秒甚至几百毫秒**
笔者实测数据
| 启动内容 | 虚拟机 | Docker 容器 |
|---------|--------|-------------|
| 空系统 | ~60 | ~0.5 |
| MySQL | ~90 | ~3 |
| 完整 Web 应用 | ~120 | ~5 |
这个差异对以下场景尤为重要
- **CI/CD 流水线**每次构建节省几分钟一天累积下来就是几小时
- **弹性扩容**流量高峰时能快速启动更多实例
- **开发体验**快速重启服务进行调试
### 3. 资源效率
Docker 容器共享宿主机内核无需为每个应用运行完整的操作系统
```
传统虚拟机方案:
┌────────────────────────────────────────────────┐
│ 物理服务器 (64GB 内存) │
├──────────────┬───────────────┬─────────────────┤
│ VM1 │ VM2 │ VM3 │
│ 8GB 内存 │ 8GB 内存 │ 8GB 内存 │
│ (含 OS 2GB) │ (含 OS 2GB) │ (含 OS 2GB) │
│ 应用 1 │ 应用 2 │ 应用 3 │
└──────────────┴───────────────┴─────────────────┘
实际可用于应用3 × 6GB = 18GB ❌
Docker 方案:
┌────────────────────────────────────────────────┐
│ 物理服务器 (64GB 内存) │
│ 宿主机 OS + Docker (约 4GB) │
├──────────────┬───────────────┬─────────────────┤
│ 容器 1 │ 容器 2 │ 容器 3 │
│ 应用 1 │ 应用 2 │ 应用 3 │
│ (按需分配) │ (按需分配) │ (按需分配) │
└──────────────┴───────────────┴─────────────────┘
实际可用于应用:约 60GB ✅
```
### 4. 持续交付和部署
Docker 完美契合 DevOps 的工作流程
```
代码提交 ──→ 自动构建镜像 ──→ 自动测试 ──→ 自动部署
│ │ │ │
▼ ▼ ▼ ▼
Git docker 容器内 容器滚动
push build 运行测试 更新
```
使用 [Dockerfile](../04_image/build.md) 定义镜像构建过程使得
- 构建过程**可重复可追溯**
- 任何人都能从代码重建完全相同的镜像
- 配合 [GitHub Actions](../14_cases/ci/actions/README.md) CI 系统实现自动化
### 5. 轻松迁移
Docker 可以在几乎任何平台上运行
- 本地开发机macOSWindowsLinux
- 公有云AWSAzureGCP阿里云腾讯云
- 私有云和自建数据中心
- 边缘设备和 IoT
**同一个镜像在任何地方运行结果都一致** 这让应用迁移变得前所未有的简单
### 6. 微服务架构的基石
现代微服务架构几乎都依赖容器技术Docker 让你可以
- **隔离服务**每个服务运行在独立容器中互不干扰
- **独立扩展**哪个服务负载高就单独扩展哪个
- **独立部署**更新一个服务不影响其他服务
- **技术多样**不同服务可以用不同语言和框架
```
┌───────────────────────────────────────────────────┐
│ 微服务架构示例 │
├─────────────┬─────────────┬───────────────────────┤
│ 前端容器 │ API 容器 │ Worker 容器 │
│ (Node.js) │ (Python) │ (Go) │
├─────────────┴─────────────┴───────────────────────┤
│ Redis 容器 │
├───────────────────────────────────────────────────┤
│ PostgreSQL 容器 │
└───────────────────────────────────────────────────┘
```
## Docker 不适合的场景
笔者认为技术选型要客观Docker 并非银弹以下场景可能不太适合
### 1. 需要完全隔离的场景
容器共享宿主机内核隔离性不如虚拟机如果你需要运行不受信任的代码虚拟机可能更安全
### 2. 需要特殊内核的场景
容器使用宿主机内核如果应用需要特定版本的内核或内核模块可能需要虚拟机
### 3. Windows 原生应用
虽然 Docker 支持 Windows 容器但生态不如 Linux 容器成熟传统 Windows 应用的容器化仍有挑战
### 4. 桌面应用
Docker 主要面向服务端应用桌面 GUI 应用的容器化虽然可行但通常得不偿失
## 与传统虚拟机的对比总结
| 特性 | Docker 容器 | 传统虚拟机 |
|:------|:-----------|:-----------|
| 启动速度 | 秒级 | 分钟级 |
| 磁盘占用 | MB 级别 | GB 级别 |
| 性能 | 接近原生 | 5-20% 损耗 |
| 单机支持量 | 上千个容器 | 几十个虚拟机 |
| 隔离性 | 进程级别 | 完全隔离 |
| 最佳场景 | 微服务CI/CD开发环境 | 多租户高安全需求 |
## 本章小结
Docker 的核心价值可以用一句话概括**让应用的开发测试部署保持一致同时极大提高资源利用效率**
笔者认为对于现代软件开发者来说Docker 已经不是"要不要学"的问题而是**必备技能**无论你是前端后端运维还是全栈开发者掌握 Docker 都能让你的工作更高效
接下来让我们学习 Docker [基本概念](../02_basic_concept/README.md)

View File

@@ -0,0 +1,213 @@
## 2.1 镜像
Docker 镜像作为容器运行的基石其设计理念和实现机制至关重要本节将深入探讨镜像的本质与操作系统的关系内容构成以及核心的分层存储机制
### 2.1.1 一句话理解镜像
> **Docker 镜像是一个只读的模板包含了运行应用所需的一切代码运行时环境变量和配置文件** 如果用一个类比**镜像就像是一张光盘或 ISO 文件**你可以用同一张光盘在不同电脑上安装系统而光盘本身不会被修改同样一个镜像可以创建多个容器而镜像本身保持不变
### 2.1.2 镜像与操作系统的关系
我们都知道操作系统分为 **内核** **用户空间**
```mermaid
flowchart TD
subgraph UserSpace ["用户空间"]
direction TB
App["应用程序、工具、库、配置文件...<br/>(这部分被打包成 Docker 镜像)"]
end
subgraph KernelSpace ["Linux 内核"]
direction TB
Kernel["容器共享宿主机的内核"]
end
UserSpace --- KernelSpace
```
对于 Linux 而言内核启动后会挂载 `root` 文件系统来提供用户空间支持**Docker 镜像** 本质上就是一个 `root` 文件系统
例如官方镜像 `ubuntu:24.04` 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统 **不包含 Linux 内核** (因为容器共享宿主机的内核)
### 2.1.3 镜像包含什么
Docker 镜像是一个特殊的文件系统包含
| 内容类型 | 示例 |
|---------|------|
| **程序文件** | 应用二进制文件Python/Node 解释器 |
| **库文件** | libcOpenSSL各种依赖库 |
| **配置文件** | nginx.confmy.cnf |
| **环境变量** | PATHLANG 等预设值 |
| **元数据** | 启动命令暴露端口数据卷定义 |
**关键特性**
- 镜像是 **只读**
- 镜像 **不包含** 动态数据
- 镜像构建后 **内容不会改变**
### 2.1.4 分层存储镜像的核心设计
镜像的分层存储机制是 Docker 最具创新性的特性之一通过 Union FS 技术Docker 能够高效地构建和管理镜像
#### 为什么需要分层
笔者认为分层存储是 Docker 最巧妙的设计之一
假设你有三个应用都基于 Ubuntu 运行
```mermaid
flowchart TD
subgraph Traditional ["传统方式(不分层)总计: 1.5GB ❌"]
direction LR
AppA_Trad["App A<br/>Ubuntu 500MB"]
AppB_Trad["App B<br/>Ubuntu 500MB"]
AppC_Trad["App C<br/>Ubuntu 500MB"]
end
subgraph DockerLayered ["Docker 分层方式 总计: 620MB ✅"]
direction TB
subgraph Apps ["应用层"]
direction LR
AppA["App A 50MB"]
AppB["App B 30MB"]
AppC["App C 40MB"]
end
Ubuntu["Ubuntu<br/>共享500MB"]
AppA --> Ubuntu
AppB --> Ubuntu
AppC --> Ubuntu
end
```
#### 分层是如何工作的
笔者用一个实际的 Dockerfile 来解释分层
```docker
FROM ubuntu:24.04 # 第 1 层:基础系统(约 78MB
RUN apt-get update # 第 2 层:更新包索引
RUN apt-get install nginx # 第 3 层:安装 nginx
COPY app.conf /etc/nginx/ # 第 4 层:复制配置文件
```
构建后的镜像结构
```mermaid
flowchart TD
Layer4["第 4 层: COPY app.conf (只读)<br/>← 最新添加的层"]
Layer3["第 3 层: nginx 安装文件 (只读)"]
Layer2["第 2 层: apt 缓存更新 (只读)"]
Layer1["第 1 层: Ubuntu 基础系统 (只读)<br/>← 基础镜像层"]
Layer4 --> Layer3 --> Layer2 --> Layer1
```
每一层的特点
- **只读**构建完成后不可修改
- **可共享**多个镜像可以共享相同的层
- **有缓存**未变化的层不会重新构建
#### 分层存储的 陷阱
> **笔者特别提醒**理解这一点可以帮你避免构建出臃肿的镜像**关键原理**每一层的文件变化会被记录 **删除操作只是标记不会真正减小镜像体积**
```docker
## 错误示范 ❌
FROM ubuntu:24.04
RUN apt-get update
RUN apt-get install -y build-essential # 安装编译工具(约 200MB
RUN make && make install # 编译应用
RUN apt-get remove build-essential # 试图删除编译工具
## 结果:镜像仍然包含 200MB 的编译工具!
```
```docker
## 正确做法 ✅
FROM ubuntu:24.04
RUN apt-get update && \
apt-get install -y build-essential && \
make && make install && \
apt-get remove -y build-essential && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
## 在同一层完成安装、使用、清理
```
#### 查看镜像的分层
```bash
## 查看镜像的历史(每层的构建记录)
$ docker history nginx:latest
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
<missing> 2 weeks ago STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago EXPOSE map[80/tcp:{}] 0B
<missing> 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
<missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB
...
```
### 2.1.5 镜像的标识
Docker 镜像有多种标识方式
#### 1. 镜像名称和标签
格式`[仓库地址/]仓库名[:标签]`
```bash
## 完整格式
registry.example.com/myproject/myapp:v1.2.3
## 简写(使用 Docker Hub
nginx:1.25
ubuntu:24.04
## 省略标签(默认使用 latest
nginx # 等同于 nginx:latest
```
#### 2. 镜像 ID (Content-Addressable)
每个镜像有一个基于内容计算的唯一 ID
```bash
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 weeks ago 187MB
ubuntu 24.04 ca2b0f26964c 3 weeks ago 78.1MB
```
#### 3. 镜像摘要
更精确的标识基于镜像内容的 SHA256 哈希
```bash
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:6db391d1c0cfb30588ba0bf72ea999404f2764184d8b8d10d89e8a9c6... a6bd71f48f68
```
> 💡 笔者建议在生产环境使用镜像摘要而非标签因为标签可以被覆盖但摘要是不可变的
### 2.1.6 镜像的来源
Docker 镜像可以通过以下方式获取
| 方式 | 说明 | 示例 |
|------|------|------|
| ** Registry 拉取** | 最常用的方式 | `docker pull nginx` |
| ** Dockerfile 构建** | 自定义镜像 | `docker build -t myapp .` |
| **从容器提交** | 保存容器状态 (不推荐)| `docker commit` |
| **从文件导入** | 离线传输 | `docker load < image.tar` |

View File

@@ -0,0 +1,239 @@
## 2.2 容器
容器是 Docker 技术的核心是应用实际运行的载体本节将从容器的本质与虚拟机的区别存储层机制以及生命周期管理等方面全面解析 Docker 容器
### 2.2.1 一句话理解容器
> **容器是镜像的运行实例如果把镜像比作程序那么容器就是进程** 用面向对象编程的术语来说**镜像是类 (Class)容器是对象 (Instance)**
- 一个镜像可以创建多个容器
- 每个容器相互独立互不影响
- 容器可以被创建启动停止删除暂停
### 2.2.2 容器的本质
> 💡 笔者认为理解这一点是理解 Docker 的关键**容器的本质是一个特殊的进程**
```mermaid
flowchart TD
subgraph NormalProcess ["普通进程"]
direction TB
N1["• 共享系统资源<br/>• 共享网络<br/>• 共享文件系统"]
end
subgraph ContainerProcess ["容器进程 (运行在宿主机内核上)"]
direction TB
C1["• 独立进程空间<br/>• 独立网络环境<br/>• 独立文件系统<br/>• 独立用户空间"]
end
```
这种隔离是通过 Linux 内核的 **Namespace** 技术实现的具体表现为
- **进程空间**容器看不到宿主机上的其他进程
- **网络**容器拥有独立的 IP端口等网络资源
- **文件系统**容器拥有独立的 root 目录
- **用户**容器内的 root 用户不等于宿主机的 root 用户
### 2.2.3 容器 vs 虚拟机核心区别
很多初学者会混淆容器和虚拟机笔者用一张图来说明
```mermaid
flowchart TD
subgraph VM ["虚拟机 (每个 VM 运行完整 OS)"]
direction TB
subgraph VMApp ["应用层"]
VA[App A] & VB[App B]
end
subgraph VMGuest ["Guest OS (完整系统)"]
G1[Guest OS] & G2[Guest OS]
end
V[Hypervisor]
VMH[Host OS]
VMHW[Hardware]
VMApp --> VMGuest --> V --> VMH --> VMHW
end
subgraph Container ["容器 (所有容器共享宿主机内核)"]
direction TB
subgraph CApp ["应用层"]
CA[App A] & CB[App B]
end
subgraph CContainer ["隔离层"]
CC1[Container 仅应用] & CC2[Container 仅应用]
end
CE[Docker Engine]
CH[Host OS]
CHW[Hardware]
CApp --> CContainer --> CE --> CH --> CHW
end
```
| 特性 | 容器 | 虚拟机 |
|------|------|--------|
| **隔离级别** | 进程级 (Namespace)| 硬件级 (Hypervisor)|
| **启动时间** | 秒级 (甚至毫秒)| 分钟级 |
| **资源占用** | MB 级别 | GB 级别 |
| **性能损耗** | 几乎为零 | 5-20% |
| **内核** | 共享宿主机内核 | 各自独立内核 |
### 2.2.4 容器的存储层
理解容器的存储层机制对于数据的持久化和镜像的优化至关重要本节将介绍容器的可写层以及 Copy-on-Write 机制
#### 镜像层 + 容器层
当容器运行时Docker 会在镜像的只读层之上创建一个 **可写层** (容器存储层)
```mermaid
flowchart TD
ContainerLayer["容器存储层(可读写)<br/>容器运行时创建,记录文件变化"]
ImageLayerN["镜像第 N 层(只读)"]
ImageLayerN1["镜像第 N-1 层(只读)"]
Dots["..."]
ImageLayer1["镜像第 1 层(只读)<br/>基础镜像层"]
ContainerLayer --> ImageLayerN --> ImageLayerN1 --> Dots --> ImageLayer1
```
#### Copy-on-Write (写时复制)
当容器需要修改镜像层中的文件时
1. Docker 将该文件 **复制** 到容器存储层
2. 在容器层中进行修改
3. 原始镜像层保持不变
```bash
读取文件:直接从镜像层读取(共享,高效)
修改文件:复制到容器层,然后修改(只有这个容器能看到修改)
```
#### 容器存储层的生命周期
> **笔者特别强调**这是新手最容易踩的坑**容器存储层与容器生命周期绑定容器删除数据就没了**
```bash
## 创建容器,写入数据
$ docker run -it ubuntu bash
root@abc123:/# echo "important data" > /data.txt
root@abc123:/# exit
## 删除容器
$ docker rm abc123
## 数据丢了!没有任何办法恢复!
```
#### 正确的数据持久化方式
按照 Docker 最佳实践容器存储层应该保持 **无状态**需要持久化的数据应该使用
| 方式 | 说明 | 适用场景 |
|------|------|---------|
| **[数据卷 (Volume) ](../08_data/8.1_volume.md)** | Docker 管理的存储 | 数据库应用数据 |
| **[绑定挂载 (Bind Mount) ](../08_data/8.2_bind-mounts.md)** | 挂载宿主机目录 | 开发时共享代码 |
```bash
## 使用数据卷(推荐)
$ docker run -v mydata:/var/lib/mysql mysql
## 使用绑定挂载
$ docker run -v /host/path:/container/path nginx
```
这些位置的读写 **会跳过容器存储层**直接写入宿主机性能更好也不会随容器删除而丢失
### 2.2.5 容器的生命周期
掌握容器的生命周期对于管理和调试 Docker 应用非常重要如图 2-1 所示容器会经历从创建到删除的完整状态流转
```mermaid
stateDiagram-v2
direction TB
[*] --> Created : docker create
Created --> Running : docker start
Running --> Stopped : docker stop
Running --> Paused : docker pause
Paused --> Running : docker unpause
Created --> Deleted : docker rm
Stopped --> Deleted : docker rm
Paused --> Deleted : docker rm
Deleted --> [*]
```
2-1 容器生命周期状态流转图
#### 常用生命周期命令
```bash
## 创建并启动容器(最常用)
$ docker run nginx
## 分步操作
$ docker create nginx # 创建容器(不启动)
$ docker start abc123 # 启动容器
## 停止容器
$ docker stop abc123 # 优雅停止(发送 SIGTERM等待后发送 SIGKILL
$ docker kill abc123 # 强制停止(直接发送 SIGKILL
## 暂停/恢复(不常用,但有时有用)
$ docker pause abc123 # 暂停容器内所有进程
$ docker unpause abc123 # 恢复
## 删除容器
$ docker rm abc123 # 删除已停止的容器
$ docker rm -f abc123 # 强制删除运行中的容器
```
### 2.2.6 容器与进程的关系
> **核心概念**容器的生命周期 = 主进程 (PID 1) 的生命周期
```bash
## 主进程运行,容器运行
## 主进程退出,容器停止
```
这就是为什么
```bash
## 这个容器会立即退出bash 没有输入就退出了)
$ docker run ubuntu
## 这个容器会持续运行nginx 作为守护进程持续运行)
$ docker run nginx
```
详细解释请参考[后台运行](../05_container/5.2_daemon.md)章节
### 2.2.7 容器的隔离性
Docker 容器通过以下 Namespace 实现隔离
| Namespace | 隔离内容 | 效果 |
|-----------|---------|------|
| **PID** | 进程 ID | 容器内 PID 1 是应用进程看不到宿主机其他进程 |
| **NET** | 网络 | 独立的网络栈IP 地址端口 |
| **MNT** | 文件系统 | 独立的根目录和挂载点 |
| **UTS** | 主机名 | 独立的主机名和域名 |
| **IPC** | 进程间通信 | 独立的信号量消息队列 |
| **USER** | 用户 | 独立的用户和组 ID |
> 想深入了解请阅读[底层实现 - 命名空间](../12_implementation/12.2_namespace.md)

View File

@@ -0,0 +1,288 @@
## 2.3 仓库
Docker Registry 是镜像分发和管理的核心组件本节将介绍 Registry 的基本概念公共和私有服务的选择以及镜像的安全管理
### 2.3.1 一句话理解 Registry
> **Docker Registry 是存储和分发 Docker 镜像的服务类似于代码的 GitHub 或包管理的 npm**
镜像构建完成后可以在当前机器上运行但如果需要在其他服务器上使用这个镜像就需要一个集中的存储和分发服务这就是 Docker Registry
### 2.3.2 核心概念
要熟练使用 Docker Registry首先需要理清它与仓库 (Repository)标签 (Tag) 之间的关系
#### Registry仓库标签的关系
Docker Registry 中可以包含多个 Repository每个 Repository 可以包含多个 Tag如图 2-2 所示它们之间具有清晰的层级关系
```mermaid
flowchart TB
subgraph Registry ["Docker Registry如 Docker Hub"]
direction TB
subgraph RepoNginx ["Repository仓库: nginx"]
direction LR
N1(":latest (tag)")
N2(":1.25 (tag)")
N3(":1.24 (tag)")
N4(":alpine (tag)")
N5("...")
N1 ~~~ N2 ~~~ N3 ~~~ N4 ~~~ N5
end
subgraph RepoMysql ["Repository仓库: mysql"]
direction LR
M1(":latest")
M2(":8.0")
M3(":5.7")
M4("...")
M1 ~~~ M2 ~~~ M3 ~~~ M4
end
RepoNginx ~~~ RepoMysql
end
```
2-2 RegistryRepository Tag 的层级关系
相关基本概念具体如下
| 概念 | 说明 | 示例 |
|------|------|------|
| **Registry** | 存储镜像的服务 | Docker Hubghcr.io |
| **Repository (仓库)** | 同一软件的镜像集合 | `nginx``mysql``mycompany/myapp` |
| **Tag (标签)** | 仓库内的版本标识 | `latest``1.25``alpine` |
#### 镜像的完整名称
一个完整的 Docker 镜像名称由 Registry 地址用户名/组织名仓库名和标签组成了解其结构有助于我们更准确地定位镜像基本格式如下
```bash
[registry 地址/][用户名/]仓库名[:标签]
```
示例
```bash
## 完整格式
registry.example.com/mycompany/myapp:v1.2.3
│ │ │ │
│ │ │ └── 标签
│ │ └── 仓库名
│ └── 用户名/组织名
└── Registry 地址
## Docker Hub 官方镜像(省略 registry 和用户名)
nginx:1.25
ubuntu:24.04
## Docker Hub 用户镜像
jwilder/nginx-proxy:latest
## 其他 Registry
ghcr.io/username/myapp:v1.0
gcr.io/google-containers/pause:3.6
```
> 💡 **笔者提示**如果不指定 Registry 地址默认使用 Docker Hub如果不指定标签默认使用 `latest`
### 2.3.3 公共 Registry 服务
公共 Registry 服务为开发者提供了便捷的镜像获取途径其中最著名的是 Docker Hub
#### 默认的 Docker Hub
[Docker Hub](https://hub.docker.com/) 是最大的公共 Registry也是 Docker 的默认 Registry。
**特点**
- 拥有大量[官方镜像](https://hub.docker.com/search?q=&type=image&image_filter=official) (nginx、mysql、redis 等)
- 免费账户可以创建公开仓库
- 付费账户支持私有仓库
```bash
## 从 Docker Hub 拉取镜像
$ docker pull nginx # 官方镜像
$ docker pull bitnami/redis # 第三方镜像
## 推送镜像到 Docker Hub
$ docker login
$ docker push username/myapp:v1.0
```
#### 其他公共 Registry
除了 Docker Hub还有以下几个常见的公共 Registry
| Registry | 地址 | 说明 |
|----------|------|------|
| **GitHub Container Registry** | ghcr.io | GitHub 提供 GitHub Actions 集成好 |
| **Google Container Registry** | gcr.io | Google Cloud 提供Kubernetes 镜像常用 |
| **Quay.io** | quay.io | Red Hat 提供 |
| **阿里云容器镜像服务** | registry.cn-*.aliyuncs.com | 国内访问快 |
| **腾讯云容器镜像服务** | ccr.ccs.tencentyun.com | 国内访问快 |
### 2.3.4 镜像加速器
由于网络原因在国内直接访问 Docker Hub 可能会很慢可以配置 **镜像加速器** (Registry Mirror) 来加速下载配置示例如下
```json
// /etc/docker/daemon.json
{
"registry-mirrors": [
"https://your-accelerator-url"
]
}
```
详细配置方法请参考[镜像加速器](../03_install/3.9_mirror.md)章节
> **笔者提醒**镜像加速器的可用性经常变化使用前建议先测试是否可用
### 2.3.5 私有 Registry
出于安全和隐私的考虑企业往往需要搭建自己的私有 Registry以下是几种常见的搭建方案
#### 官方 Registry 镜像
Docker 官方提供了 [registry](https://hub.docker.com/_/registry/) 镜像,可以快速搭建私有 Registry
```bash
## 启动一个本地 Registry
$ docker run -d -p 5000:5000 --name registry registry:2
## 推送镜像到本地 Registry
$ docker tag myapp:v1.0 localhost:5000/myapp:v1.0
$ docker push localhost:5000/myapp:v1.0
## 从本地 Registry 拉取
$ docker pull localhost:5000/myapp:v1.0
```
#### 企业级解决方案
官方 Registry 功能较为基础企业环境常用以下方案
| 方案 | 特点 |
|------|------|
| **[Harbor](https://goharbor.io/)** | CNCF 项目,功能全面 (用户管理、漏洞扫描、镜像签名)|
| **[Nexus Repository](../06_repository/6.4_nexus3_registry.md)** | 支持多种制品类型 (DockerMavennpm )|
| **云厂商服务** | 阿里云 ACR腾讯云 TCRAWS ECR |
笔者建议
- 小团队可以先用官方 Registry够用即可
- 中大型团队推荐 Harbor功能完善且开源免费
- 已使用云服务直接用云厂商的 Registry 服务更省心
### 2.3.6 镜像的推送和拉取
掌握镜像的推送 (Push) 和拉取 (Pull) 是使用 Docker Registry 的基本功
#### 完整工作流程
如图 2-3 所示镜像从开发环境构建后推送到 Registry再由生产环境拉取并运行
```bash
开发者机器 Registry 生产服务器
│ │ │
│ docker build │ │
│ 构建镜像 │ │
│ │ │
│ docker push ─────────────▶ │
│ 推送镜像 │ 存储镜像 │
│ │ │
│ │ ◀───────────── docker pull │
│ │ 拉取镜像 │
│ │ │
│ │ docker run │
│ │ 运行容器 │
```
2-3 镜像构建推送与拉取流程
#### 常用命令
```bash
## 登录 Registry
$ docker login # 登录 Docker Hub
$ docker login registry.example.com # 登录其他 Registry
## 拉取镜像
$ docker pull nginx:1.25
## 标记镜像(准备推送)
$ docker tag myapp:latest registry.example.com/myteam/myapp:v1.0
## 推送镜像
$ docker push registry.example.com/myteam/myapp:v1.0
## 登出
$ docker logout
```
### 2.3.7 镜像的安全性
在使用公共镜像或维护私有镜像时安全性是不容忽视的重要环节
#### 使用官方镜像
Docker Hub [官方镜像](https://hub.docker.com/search?q=&type=image&image_filter=official) (标有 “Official Image” 标识) 经过 Docker 团队审核,相对更安全。示例如下:
```bash
## 官方镜像示例
nginx # ✅ 官方
mysql # ✅ 官方
redis # ✅ 官方
## 第三方镜像(需要自行评估可信度)
bitnami/redis # ⚠️ 需要评估
someuser/myapp # ⚠️ 需要评估
```
#### 镜像签名
当前更推荐使用 Sigstore / Notation 体系进行镜像签名与验证`Docker Content Trust (DCT)` 已进入退场阶段不建议作为新项目主方案
> 注意Cosign 默认会把签名写回镜像所在仓库请使用你有推送权限的镜像地址
```bash
## 准备一个你有写权限的镜像地址
$ export IMAGE=<你的仓库名>/nginx:1.27
$ docker pull nginx:1.27
$ docker tag nginx:1.27 $IMAGE
$ docker push $IMAGE
## 生成签名密钥(会生成 cosign.key / cosign.pub
$ cosign generate-key-pair
## 使用 Cosign 签名与验证
$ cosign sign --key cosign.key $IMAGE
$ cosign verify --key cosign.pub $IMAGE
```
#### 漏洞扫描
```bash
## 使用 Docker Scout 扫描镜像漏洞
$ docker scout cves nginx:latest
## 使用 Trivy开源工具
$ trivy image nginx:latest
```

View File

@@ -1,9 +1,15 @@
# 基本概念
# 第二章 基本概念
**Docker** 包括三个基本概念
* **镜像**`Image`Docker 镜像是一个特殊的文件系统除了提供容器运行时所需的程序资源配置等文件外还包含了一些为运行时准备的一些配置参数如匿名卷环境变量用户等镜像不包含任何动态数据其内容在构建之后也不会被改变
* **容器**`Container`镜像`Image`和容器`Container`的关系就像是面向对象程序设计中的 `` `实例` 一样镜像是静态的定义容器是镜像运行时的实体容器可以被创建启动停止删除暂停等
* **仓库**`Repository`镜像构建完成后可以很容易的在当前宿主机上运行但是如果需要在其它服务器上使用这个镜像我们就需要一个集中的存储分发镜像的服务Docker Registry 就是这样的服务
* **镜像** (`Image`)Docker 镜像是一个特殊的文件系统除了提供容器运行时所需的程序资源配置等文件外还包含了一些为运行时准备的一些配置参数 (如匿名卷环境变量用户等)镜像不包含任何动态数据其内容在构建之后也不会被改变
* **容器** (`Container`)镜像 (`Image`) 和容器 (`Container`) 的关系就像是面向对象程序设计中的 `` `实例` 一样镜像是静态的定义容器是镜像运行时的实体容器可以被创建启动停止删除暂停等
* **仓库** (`Repository`)镜像构建完成后可以很容易的在当前宿主机上运行但是如果需要在其它服务器上使用这个镜像我们就需要一个集中的存储分发镜像的服务Docker Registry 就是这样的服务
理解了这三个概念就理解了 **Docker** 的整个生命周期
## 本章内容
* [Docker 镜像](2.1_image.md)
* [Docker 容器](2.2_container.md)
* [Docker 仓库](2.3_repository.md)

View File

@@ -1,246 +0,0 @@
# Docker 容器
## 一句话理解容器
> **容器是镜像的运行实例如果把镜像比作程序那么容器就是进程**
用面向对象编程的术语来说**镜像是类Class容器是对象Instance**
- 一个镜像可以创建多个容器
- 每个容器相互独立互不影响
- 容器可以被创建启动停止删除暂停
## 容器的本质
> 💡 **笔者认为理解这一点是理解 Docker 的关键**
**容器的本质是一个特殊的进程**
```
┌─────────────────────────────────────────────────────────────┐
│ 普通进程 │
│ • 与其他进程共享系统资源 │
│ • 可以看到其他进程 │
│ • 共享网络和文件系统 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 容器进程 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ • 有自己的进程空间(看不到宿主机上的其他进程) │ │
│ │ • 有自己的网络(独立 IP、端口 │ │
│ │ • 有自己的文件系统(独立的 root 目录) │ │
│ │ • 有自己的用户(容器内的 root ≠ 宿主机的 root │ │
│ └───────────────────────────────────────────────────────┘ │
│ 但仍然运行在宿主机的内核上 │
└─────────────────────────────────────────────────────────────┘
```
这种隔离是通过 Linux 内核的 **Namespace** 技术实现的
## 容器 vs 虚拟机核心区别
很多初学者会混淆容器和虚拟机笔者用一张图来说明
```
虚拟机 容器
┌───────────────────────┐ ┌───────────────────────┐
│ App A │ App B │ │ App A │ App B │
├────────────┼──────────┤ ├────────────┼──────────┤
│ Guest OS │ Guest OS │ │ Container │ Container│
│ (完整系统) │ (完整系统)│ │ (仅应用) │ (仅应用) │
├────────────┴──────────┤ └────────────┴──────────┤
│ Hypervisor │ │ Docker Engine │
├───────────────────────┤ ├───────────────────────┤
│ Host OS │ │ Host OS │
├───────────────────────┤ ├───────────────────────┤
│ Hardware │ │ Hardware │
└───────────────────────┘ └───────────────────────┘
每个 VM 运行完整 OS 所有容器共享宿主机内核
```
| 特性 | 容器 | 虚拟机 |
|------|------|--------|
| **隔离级别** | 进程级Namespace | 硬件级Hypervisor |
| **启动时间** | 秒级甚至毫秒 | 分钟级 |
| **资源占用** | MB 级别 | GB 级别 |
| **性能损耗** | 几乎为零 | 5-20% |
| **内核** | 共享宿主机内核 | 各自独立内核 |
## 容器的存储层
### 镜像层 + 容器层
当容器运行时Docker 会在镜像的只读层之上创建一个**可写层**容器存储层
```
┌─────────────────────────────────────────────┐
│ 容器存储层(可读写) │ ← 容器运行时创建
│ 运行时产生的文件变化记录在这里 │
├─────────────────────────────────────────────┤
│ 镜像第 N 层(只读) │
├─────────────────────────────────────────────┤
│ 镜像第 N-1 层(只读) │
├─────────────────────────────────────────────┤
│ ... │
├─────────────────────────────────────────────┤
│ 镜像第 1 层(只读) │ ← 基础镜像层
└─────────────────────────────────────────────┘
```
### Copy-on-Write写时复制
当容器需要修改镜像层中的文件时
1. Docker 将该文件**复制**到容器存储层
2. 在容器层中进行修改
3. 原始镜像层保持不变
```
读取文件:直接从镜像层读取(共享,高效)
修改文件:复制到容器层,然后修改(只有这个容器能看到修改)
```
### 容器存储层的生命周期
> **笔者特别强调**这是新手最容易踩的坑
**容器存储层与容器生命周期绑定容器删除数据就没了**
```bash
# 创建容器,写入数据
$ docker run -it ubuntu bash
root@abc123:/# echo "important data" > /data.txt
root@abc123:/# exit
# 删除容器
$ docker rm abc123
# 数据丢了!没有任何办法恢复!
```
### 正确的数据持久化方式
按照 Docker 最佳实践容器存储层应该保持**无状态**需要持久化的数据应该使用
| 方式 | 说明 | 适用场景 |
|------|------|---------|
| **[数据卷Volume](../07_data_network/data/volume.md)** | Docker 管理的存储 | 数据库应用数据 |
| **[绑定挂载Bind Mount](../07_data_network/data/bind-mounts.md)** | 挂载宿主机目录 | 开发时共享代码 |
```bash
# 使用数据卷(推荐)
$ docker run -v mydata:/var/lib/mysql mysql
# 使用绑定挂载
$ docker run -v /host/path:/container/path nginx
```
这些位置的读写**会跳过容器存储层**直接写入宿主机性能更好也不会随容器删除而丢失
## 容器的生命周期
```
┌──────────────────────────────────────────────────┐
│ 容器生命周期 │
└──────────────────────────────────────────────────┘
docker create docker start docker stop
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Created │───────────▶│ Running │───────────▶│ Stopped │
└─────────┘ └─────────┘ └─────────┘
│ │ │
│ │ docker pause │
│ ▼ │
│ ┌─────────┐ │
│ │ Paused │ │
│ └─────────┘ │
│ │ │
│ docker rm │ docker rm │
└───────────────────────┴──────────────────────┘
┌──────────┐
│ Deleted │
└──────────┘
```
### 常用生命周期命令
```bash
# 创建并启动容器(最常用)
$ docker run nginx
# 分步操作
$ docker create nginx # 创建容器(不启动)
$ docker start abc123 # 启动容器
# 停止容器
$ docker stop abc123 # 优雅停止(发送 SIGTERM等待后发送 SIGKILL
$ docker kill abc123 # 强制停止(直接发送 SIGKILL
# 暂停/恢复(不常用,但有时有用)
$ docker pause abc123 # 暂停容器内所有进程
$ docker unpause abc123 # 恢复
# 删除容器
$ docker rm abc123 # 删除已停止的容器
$ docker rm -f abc123 # 强制删除运行中的容器
```
## 容器与进程的关系
> **核心概念**容器的生命周期 = 主进程PID 1的生命周期
```bash
# 主进程运行,容器运行
# 主进程退出,容器停止
```
这就是为什么
```bash
# 这个容器会立即退出bash 没有输入就退出了)
$ docker run ubuntu
# 这个容器会持续运行nginx 作为守护进程持续运行)
$ docker run nginx
```
详细解释请参考[后台运行](../05_container/daemon.md)章节
## 容器的隔离性
Docker 容器通过以下 Namespace 实现隔离
| Namespace | 隔离内容 | 效果 |
|-----------|---------|------|
| **PID** | 进程 ID | 容器内 PID 1 是应用进程看不到宿主机其他进程 |
| **NET** | 网络 | 独立的网络栈IP 地址端口 |
| **MNT** | 文件系统 | 独立的根目录和挂载点 |
| **UTS** | 主机名 | 独立的主机名和域名 |
| **IPC** | 进程间通信 | 独立的信号量消息队列 |
| **USER** | 用户 | 独立的用户和组 ID |
> 想深入了解请阅读[底层实现 - 命名空间](../13_implementation/namespace.md)
## 本章小结
| 概念 | 要点 |
|------|------|
| **容器是什么** | 镜像的运行实例本质是隔离的进程 |
| **容器 vs 虚拟机** | 共享内核更轻量但隔离性较弱 |
| **存储层** | 可写层随容器删除而消失 |
| **数据持久化** | 使用 Volume Bind Mount |
| **生命周期** | 与主进程PID 1绑定 |
理解了镜像和容器接下来让我们学习[仓库](repository.md)存储和分发镜像的服务
## 延伸阅读
- [启动容器](../05_container/run.md)详细的容器启动选项
- [后台运行](../05_container/daemon.md)理解容器为什么会"立即退出"
- [进入容器](../05_container/attach_exec.md)如何操作运行中的容器
- [数据管理](../07_data_network/README.md)Volume 和数据持久化详解

View File

@@ -1,222 +0,0 @@
# Docker 镜像
## 一句话理解镜像
> **Docker 镜像是一个只读的模板包含了运行应用所需的一切代码运行时环境变量和配置文件**
如果用一个类比**镜像就像是一张光盘或 ISO 文件**你可以用同一张光盘在不同电脑上安装系统而光盘本身不会被修改同样一个镜像可以创建多个容器而镜像本身保持不变
## 镜像与操作系统的关系
我们都知道操作系统分为**内核****用户空间**
```
┌─────────────────────────────────────────────────────────────┐
│ 用户空间 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 应用程序、工具、库、配置文件... │ │
│ │ (这部分被打包成 Docker 镜像) │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Linux 内核 │
│ (容器共享宿主机的内核) │
└─────────────────────────────────────────────────────────────┘
```
对于 Linux 而言内核启动后会挂载 `root` 文件系统来提供用户空间支持**Docker 镜像**本质上就是一个 `root` 文件系统
例如官方镜像 `ubuntu:24.04` 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统**不包含 Linux 内核**因为容器共享宿主机的内核
## 镜像包含什么
Docker 镜像是一个特殊的文件系统包含
| 内容类型 | 示例 |
|---------|------|
| **程序文件** | 应用二进制文件Python/Node 解释器 |
| **库文件** | libcOpenSSL各种依赖库 |
| **配置文件** | nginx.confmy.cnf |
| **环境变量** | PATHLANG 等预设值 |
| **元数据** | 启动命令暴露端口数据卷定义 |
**关键特性**
- 镜像是**只读**
- 镜像**不包含**动态数据
- 镜像构建后**内容不会改变**
## 分层存储镜像的核心设计
### 为什么需要分层
笔者认为分层存储是 Docker 最巧妙的设计之一
假设你有三个应用都基于 Ubuntu 运行
```
传统方式(不分层):
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App A │ │ App B │ │ App C │
│ Ubuntu │ │ Ubuntu │ │ Ubuntu │
│ 500MB │ │ 500MB │ │ 500MB │
└─────────────┘ └─────────────┘ └─────────────┘
总计1.5GB ❌
Docker 分层方式:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App A │ │ App B │ │ App C │
│ 50MB │ │ 30MB │ │ 40MB │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────────────┼────────────────┘
┌─────────────────┐
│ Ubuntu │
共享500MB │
└─────────────────┘
总计620MB ✅
```
### 分层是如何工作的
笔者用一个实际的 Dockerfile 来解释分层
```docker
FROM ubuntu:24.04 # 第 1 层:基础系统(约 78MB
RUN apt-get update # 第 2 层:更新包索引
RUN apt-get install nginx # 第 3 层:安装 nginx
COPY app.conf /etc/nginx/ # 第 4 层:复制配置文件
```
构建后的镜像结构
```
┌─────────────────────────────────────┐
│ 第 4 层: COPY app.conf (只读) │ ← 最新添加的层
├─────────────────────────────────────┤
│ 第 3 层: nginx 安装文件 (只读) │
├─────────────────────────────────────┤
│ 第 2 层: apt 缓存更新 (只读) │
├─────────────────────────────────────┤
│ 第 1 层: Ubuntu 基础系统 (只读) │ ← 基础镜像层
└─────────────────────────────────────┘
```
每一层的特点
- **只读**构建完成后不可修改
- **可共享**多个镜像可以共享相同的层
- **有缓存**未变化的层不会重新构建
### 分层存储的"陷阱"
> **笔者特别提醒**理解这一点可以帮你避免构建出臃肿的镜像
**关键原理**每一层的文件变化会被记录**删除操作只是标记不会真正减小镜像体积**
```docker
# 错误示范 ❌
FROM ubuntu:24.04
RUN apt-get update
RUN apt-get install -y build-essential # 安装编译工具(约 200MB
RUN make && make install # 编译应用
RUN apt-get remove build-essential # 试图删除编译工具
# 结果:镜像仍然包含 200MB 的编译工具!
```
```docker
# 正确做法 ✅
FROM ubuntu:24.04
RUN apt-get update && \
apt-get install -y build-essential && \
make && make install && \
apt-get remove -y build-essential && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
# 在同一层完成安装、使用、清理
```
### 查看镜像的分层
```bash
# 查看镜像的历史(每层的构建记录)
$ docker history nginx:latest
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
<missing> 2 weeks ago STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago EXPOSE map[80/tcp:{}] 0B
<missing> 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
<missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB
...
```
## 镜像的标识
Docker 镜像有多种标识方式
### 1. 镜像名称和标签
格式`[仓库地址/]仓库名[:标签]`
```bash
# 完整格式
registry.example.com/myproject/myapp:v1.2.3
# 简写(使用 Docker Hub
nginx:1.25
ubuntu:24.04
# 省略标签(默认使用 latest
nginx # 等同于 nginx:latest
```
### 2. 镜像 IDContent-Addressable
每个镜像有一个基于内容计算的唯一 ID
```bash
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 weeks ago 187MB
ubuntu 24.04 ca2b0f26964c 3 weeks ago 78.1MB
```
### 3. 镜像摘要Digest
更精确的标识基于镜像内容的 SHA256 哈希
```bash
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:6db391d1c0cfb30588ba0bf72ea999404f2764184d8b8d10d89e8a9c6... a6bd71f48f68
```
> 💡 笔者建议在生产环境使用镜像摘要而非标签因为标签可以被覆盖但摘要是不可变的
## 镜像的来源
Docker 镜像可以通过以下方式获取
| 方式 | 说明 | 示例 |
|------|------|------|
| ** Registry 拉取** | 最常用的方式 | `docker pull nginx` |
| ** Dockerfile 构建** | 自定义镜像 | `docker build -t myapp .` |
| **从容器提交** | 保存容器状态不推荐 | `docker commit` |
| **从文件导入** | 离线传输 | `docker load < image.tar` |
## 本章小结
| 概念 | 要点 |
|------|------|
| **镜像是什么** | 只读的应用模板包含运行所需的一切 |
| **分层存储** | 多层叠加共享基础层节省空间 |
| **只读特性** | 构建后不可修改保证一致性 |
| **层的陷阱** | 删除操作只是标记不减小体积 |
理解了镜像接下来让我们学习[容器](container.md)镜像的运行实例
## 延伸阅读
- [获取镜像](../04_image/pull.md) Registry 下载镜像
- [使用 Dockerfile 定制镜像](../04_image/build.md)创建自己的镜像
- [Dockerfile 最佳实践](../15_appendix/best_practices.md)构建高质量镜像的技巧
- [底层实现 - 联合文件系统](../13_implementation/ufs.md)深入理解分层存储的技术原理

View File

@@ -1,250 +0,0 @@
# Docker Registry
## 一句话理解 Registry
> **Docker Registry 是存储和分发 Docker 镜像的服务类似于代码的 GitHub 或包管理的 npm**
镜像构建完成后可以在当前机器上运行但如果需要在其他服务器上使用这个镜像就需要一个集中的存储和分发服务这就是 Docker Registry
## 核心概念
### Registry仓库标签的关系
```
┌─────────────────────────────────────────────────────────────────────┐
│ Docker Registry │
│ (如 Docker Hub
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Repository仓库: nginx │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ :latest │ │ :1.25 │ │ :1.24 │ │ :alpine │ ... │ │
│ │ │ (tag) │ │ (tag) │ │ (tag) │ │ (tag) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Repository仓库: mysql │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ :latest │ │ :8.0 │ │ :5.7 │ ... │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
| 概念 | 说明 | 示例 |
|------|------|------|
| **Registry** | 存储镜像的服务 | Docker Hubghcr.io |
| **Repository仓库** | 同一软件的镜像集合 | `nginx``mysql``mycompany/myapp` |
| **Tag标签** | 仓库内的版本标识 | `latest``1.25``alpine` |
### 镜像的完整名称
```
[registry地址/][用户名/]仓库名[:标签]
```
示例
```bash
# 完整格式
registry.example.com/mycompany/myapp:v1.2.3
│ │ │ │
│ │ │ └── 标签
│ │ └── 仓库名
│ └── 用户名/组织名
└── Registry 地址
# Docker Hub 官方镜像(省略 registry 和用户名)
nginx:1.25
ubuntu:24.04
# Docker Hub 用户镜像
jwilder/nginx-proxy:latest
# 其他 Registry
ghcr.io/username/myapp:v1.0
gcr.io/google-containers/pause:3.6
```
> 💡 **笔者提示**如果不指定 Registry 地址默认使用 Docker Hub如果不指定标签默认使用 `latest`
## 公共 Registry 服务
### Docker Hub默认
[Docker Hub](https://hub.docker.com/) 是最大的公共 Registry也是 Docker 的默认 Registry。
**特点**
- 拥有大量[官方镜像](https://hub.docker.com/search?q=&type=image&image_filter=official)nginx、mysql、redis 等)
- 免费账户可以创建公开仓库
- 付费账户支持私有仓库
```bash
# 从 Docker Hub 拉取镜像
$ docker pull nginx # 官方镜像
$ docker pull bitnami/redis # 第三方镜像
# 推送镜像到 Docker Hub
$ docker login
$ docker push username/myapp:v1.0
```
### 其他公共 Registry
| Registry | 地址 | 说明 |
|----------|------|------|
| **GitHub Container Registry** | ghcr.io | GitHub 提供 GitHub Actions 集成好 |
| **Google Container Registry** | gcr.io | Google Cloud 提供Kubernetes 镜像常用 |
| **Quay.io** | quay.io | Red Hat 提供 |
| **阿里云容器镜像服务** | registry.cn-*.aliyuncs.com | 国内访问快 |
| **腾讯云容器镜像服务** | ccr.ccs.tencentyun.com | 国内访问快 |
## 镜像加速器
由于网络原因在国内直接访问 Docker Hub 可能会很慢可以配置**镜像加速器**Registry Mirror来加速下载
```json
// /etc/docker/daemon.json
{
"registry-mirrors": [
"https://your-accelerator-url"
]
}
```
详细配置方法请参考[镜像加速器](../install/mirror.md)章节
> **笔者提醒**镜像加速器的可用性经常变化使用前建议先测试是否可用
## 私有 Registry
对于企业用户通常需要搭建私有 Registry 来存储内部镜像
### 官方 Registry 镜像
Docker 官方提供了 [registry](https://hub.docker.com/_/registry/) 镜像,可以快速搭建私有 Registry
```bash
# 启动一个本地 Registry
$ docker run -d -p 5000:5000 --name registry registry:2
# 推送镜像到本地 Registry
$ docker tag myapp:v1.0 localhost:5000/myapp:v1.0
$ docker push localhost:5000/myapp:v1.0
# 从本地 Registry 拉取
$ docker pull localhost:5000/myapp:v1.0
```
### 企业级解决方案
官方 Registry 功能较为基础企业环境常用以下方案
| 方案 | 特点 |
|------|------|
| **[Harbor](https://goharbor.io/)** | CNCF 项目,功能全面(用户管理、漏洞扫描、镜像签名) |
| **[Nexus Repository](../repository/nexus3_registry.md)** | 支持多种制品类型DockerMavennpm |
| **云厂商服务** | 阿里云 ACR腾讯云 TCRAWS ECR |
笔者建议
- 小团队可以先用官方 Registry够用即可
- 中大型团队推荐 Harbor功能完善且开源免费
- 已使用云服务直接用云厂商的 Registry 服务更省心
## 镜像的推送和拉取
### 完整工作流程
```
开发者机器 Registry 生产服务器
│ │ │
│ docker build │ │
│ 构建镜像 │ │
│ │ │
│ docker push ─────────────▶ │
│ 推送镜像 │ 存储镜像 │
│ │ │
│ │ ◀───────────── docker pull │
│ │ 拉取镜像 │
│ │ │
│ │ docker run │
│ │ 运行容器 │
```
### 常用命令
```bash
# 登录 Registry
$ docker login # 登录 Docker Hub
$ docker login registry.example.com # 登录其他 Registry
# 拉取镜像
$ docker pull nginx:1.25
# 标记镜像(准备推送)
$ docker tag myapp:latest registry.example.com/myteam/myapp:v1.0
# 推送镜像
$ docker push registry.example.com/myteam/myapp:v1.0
# 登出
$ docker logout
```
## 镜像的安全性
### 使用官方镜像
Docker Hub [官方镜像](https://hub.docker.com/search?q=&type=image&image_filter=official)(标有 "Official Image" 标识)经过 Docker 团队审核,相对更安全。
```bash
# 官方镜像示例
nginx # ✅ 官方
mysql # ✅ 官方
redis # ✅ 官方
# 第三方镜像(需要自行评估可信度)
bitnami/redis # ⚠️ 需要评估
someuser/myapp # ⚠️ 需要评估
```
### 镜像签名
使用 Docker Content Trust (DCT) 验证镜像来源
```bash
# 启用镜像签名验证
$ export DOCKER_CONTENT_TRUST=1
# 此后的 pull/push 会验证签名
$ docker pull nginx:latest
```
### 漏洞扫描
```bash
# 使用 Docker Scout 扫描镜像漏洞
$ docker scout cves nginx:latest
# 使用 Trivy开源工具
$ trivy image nginx:latest
```
## 本章小结
| 概念 | 要点 |
|------|------|
| **Registry** | 存储和分发镜像的服务 |
| **仓库Repository** | 同一软件的镜像集合 |
| **标签Tag** | 版本标识默认为 latest |
| **Docker Hub** | 默认的公共 Registry |
| **私有 Registry** | 企业内部使用推荐 Harbor |
现在你已经了解了 Docker 的三个核心概念[镜像](image.md)[容器](container.md)和仓库接下来让我们开始[安装 Docker](../install/README.md)动手实践
## 延伸阅读
- [Docker Hub](../repository/dockerhub.md)Docker Hub 的详细使用
- [私有仓库](../repository/registry.md)搭建私有 Registry
- [私有仓库高级配置](../repository/registry_auth.md)认证TLS 配置
- [镜像加速器](../install/mirror.md)配置镜像加速

View File

@@ -0,0 +1,40 @@
## 本章小结
本章介绍了 Docker 的三个核心概念镜像容器和仓库
| 概念 | 要点 |
|------|------|
| **镜像是什么** | 只读的应用模板包含运行所需的一切 |
| **分层存储** | 多层叠加共享基础层节省空间 |
| **只读特性** | 构建后不可修改保证一致性 |
| **层的陷阱** | 删除操作只是标记不减小体积 |
| **容器是什么** | 镜像的运行实例本质是隔离的进程 |
| **容器 vs 虚拟机** | 共享内核更轻量但隔离性较弱 |
| **存储层** | 可写层随容器删除而消失 |
| **数据持久化** | 使用 Volume Bind Mount |
| **生命周期** | 与主进程 (PID 1) 绑定 |
| **Registry** | 存储和分发镜像的服务 |
| **仓库 (Repository)** | 同一软件的镜像集合 |
| **标签 (Tag)** | 版本标识默认为 latest |
| **Docker Hub** | 默认的公共 Registry |
| **私有 Registry** | 企业内部使用推荐 Harbor |
现在你已经了解了 Docker 的三个核心概念[镜像](2.1_image.md)[容器](2.2_container.md)和仓库接下来让我们开始[安装 Docker](../03_install/README.md)动手实践
### 延伸阅读
- [获取镜像](../04_image/4.1_pull.md) Registry 下载镜像
- [使用 Dockerfile 定制镜像](../04_image/4.5_build.md)创建自己的镜像
- [Dockerfile 最佳实践](../appendix/best_practices.md)构建高质量镜像的技巧
- [底层实现 - 联合文件系统](../12_implementation/12.4_ufs.md)深入理解分层存储的技术原理
- [启动容器](../05_container/5.1_run.md)详细的容器启动选项
- [后台运行](../05_container/5.2_daemon.md)理解容器为什么会立即退出
- [进入容器](../05_container/5.4_attach_exec.md)如何操作运行中的容器
- [数据管理](../08_data/README.md)Volume 和数据持久化详解
- [Docker Hub](../06_repository/6.1_dockerhub.md)Docker Hub 的详细使用
- [私有仓库](../06_repository/6.2_registry.md)搭建私有 Registry
- [私有仓库高级配置](../06_repository/6.3_registry_auth.md)认证TLS 配置
- [镜像加速器](../03_install/3.9_mirror.md)配置镜像加速
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

View File

@@ -1,12 +1,14 @@
# 开启实验特性
## 3.10 开启实验特性
一些 docker 命令或功能仅当 **实验特性** 开启时才能使用请按照以下方法进行设置
## Docker CLI 的实验特性
### 3.10.1 Docker CLI 的实验特性
CLI 的实验特性通常包含仍在开发中的新功能幸运的是在较新版本中这些特性已经更加易用
`v20.10` 版本开始Docker CLI 所有实验特性的命令均默认开启无需再进行配置或设置系统环境变量
## 开启 dockerd 的实验特性
### 3.10.2 开启 dockerd 的实验特性
编辑 `/etc/docker/daemon.json`新增如下条目

View File

@@ -1,22 +1,26 @@
# Ubuntu 安装 Docker
## 3.1 Ubuntu
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.
Ubuntu Docker 最常用的运行环境之一本节将介绍如何在 Ubuntu 系统上安装 Docker并配置国内镜像加速
## 准备工作
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker
### 系统要求
### 3.1.1 准备工作
Docker 支持诸多版本的 [Ubuntu](https://ubuntu.com/server) 操作系统。但是较旧的版本上将不会有 Docker 新版本的持续更新,以截至 2026 年初的几个 Ubuntu LTSLong Term Support长期支持版本为例
在开始安装之前我们需要确认系统版本是否满足要求并清理可能存在的旧版本
#### 系统要求
Docker 支持诸多版本的 [Ubuntu](https://ubuntu.com/server) 操作系统。但是较旧的版本上将不会有 Docker 新版本的持续更新,以截至 2026 年初的几个 Ubuntu LTS (Long Term Support长期支持) 版本为例:
* Ubuntu Noble 24.04 (LTS)Docker v30.x
* Ubuntu Jammy 22.04 (LTS), Docker v30.x
* Ubuntu Noble 24.04 (LTS)Docker v29.x
* Ubuntu Jammy 22.04 (LTS), Docker v29.x
> **注意**Ubuntu 20.04 LTS 已于 2025 年结束标准支持不再推荐用于新部署
Ubuntu LTS 版本上目前 Docker 支持 amd64arm64armhfppc64els390x 5 个平台而非 LTS 版本支持的平台通常较少同时LTS 版本会获得 5 年的升级维护支持这样的系统会获得更长期的安全保障因此在生产环境中推荐使用 LTS 版本
### 卸载旧版本
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`使用以下命令卸载旧版本
@@ -33,7 +37,7 @@ do
done
```
## 使用 APT 安装
### 3.1.2 使用 APT 安装
由于 `apt` 源使用 HTTPS 以确保软件下载过程中不被篡改因此我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书
@@ -56,8 +60,10 @@ $ sudo apt install \
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 官方源
# $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
## 官方源
## $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
然后我们需要向 `sources.list` 中添加 Docker 软件源
@@ -68,15 +74,19 @@ $ echo \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 官方源
# $ echo \
# "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
# $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
## 官方源
## $ echo \
## "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
## $ stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
>以上命令会添加稳定版本的 Docker APT 镜像源如果需要测试版本的 Docker 请将 stable 改为 test
### 安装 Docker
#### 安装 Docker
更新 apt 软件包缓存并安装 `docker-ce`
@@ -86,31 +96,38 @@ $ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io
```
## 使用脚本自动安装
### 3.1.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本Ubuntu 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
> 若你想安装测试版的 Docker请从 test.docker.com 获取脚本
```bash
# $ curl -fsSL test.docker.com -o get-docker.sh
## $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
## $ sudo sh get-docker.sh --mirror AzureChinaCloud
```
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定(stable)版本安装在系统中
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定 (stable) 版本安装在系统中
## 启动 Docker
### 3.1.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start docker
```
## 建立 docker 用户组
### 3.1.5 建立 docker 用户组
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
> **安全警告`docker` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐但这也意味着该用户可以轻易获取主机的最高 root 权限例如通过挂载根目录运行容器
> 如果你在一个多用户共享的生产系统上配置切勿随意将普通用户加入此组此时更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker`
@@ -126,7 +143,7 @@ $ sudo usermod -aG docker $USER
退出当前终端并重新登录进行如下测试
## 测试 Docker 是否安装正确
### 3.1.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
@@ -161,10 +178,10 @@ For more examples and ideas, visit:
若能正常输出以上信息则说明安装成功
## 镜像加速
### 3.1.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
## 参考文档
### 3.1.8 参考文档
* [Docker 官方 Ubuntu 安装文档](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
* [Docker 官方 Ubuntu 安装文档](https://docs.docker.com/engine/install/ubuntu/)

View File

@@ -1,10 +1,14 @@
# Debian 安装 Docker
## 3.2 Debian
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.
Debian 以其稳定性著称 Docker 的理想宿主系统本节将指导你在 Debian 上完成 Docker 的安装
## 准备工作
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker
### 系统要求
### 3.2.1 准备工作
安装前请仔细检查 Debian 版本支持情况并卸载旧版本以避免冲突
#### 系统要求
Docker 支持以下版本的 [Debian](https://www.debian.org/intro/about) 操作系统:
@@ -12,7 +16,7 @@ Docker 支持以下版本的 [Debian](https://www.debian.org/intro/about) 操作
* Debian Bookworm 12 (oldstable)
* Debian Bullseye 11 (LTS)
### 卸载旧版本
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`使用以下命令卸载旧版本
@@ -22,7 +26,7 @@ $ sudo apt-get remove docker \
docker.io
```
## 使用 APT 安装
### 3.2.2 使用 APT 安装
由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改因此我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书
@@ -45,13 +49,15 @@ $ sudo apt-get install \
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 官方源
# $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
## 官方源
## $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
然后我们需要向 `sources.list` 中添加 Docker 软件源
> 在一些基于 Debian Linux 发行版中 `$(lsb_release -cs)` 可能不会返回 Debian 的版本代号例如 [Kail Linux](https://www.kali.org/docs/policy/kali-linux-relationship-with-debian/)、 [BunsenLabs Linux](https://www.bunsenlabs.org/)。在这些发行版中我们需要将下面命令中的 `$(lsb_release -cs)` 替换为 https://mirrors.aliyun.com/docker-ce/linux/debian/dists/ 中支持的 Debian 版本代号,例如 `buster`。
> 在一些基于 Debian Linux 发行版中 `$(lsb_release -cs)` 可能不会返回 Debian 的版本代号例如 [Kali Linux](https://www.kali.org/docs/policy/kali-linux-relationship-with-debian/)、[BunsenLabs Linux](https://www.bunsenlabs.org/)。在这些发行版中我们需要将下面命令中的 `$(lsb_release -cs)` 替换为 https://mirrors.aliyun.com/docker-ce/linux/debian/dists/ 中支持的 Debian 版本代号,例如 `buster`。
```bash
$ echo \
@@ -59,16 +65,19 @@ $ echo \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 官方源
# $ echo \
# "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
# $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
## 官方源
## $ echo \
## "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
## $ stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
>以上命令会添加稳定版本的 Docker APT 如果需要测试版本的 Docker 请将 stable 改为 test Debian11可能不使用`/etc/apt/keyrings/` gpg错误可以考虑更换为`/etc/apt/trusted.gpg.d` [issue 15727](https://github.com/docker/docs/issues/15727)。
>以上命令会添加稳定版本的 Docker APT 如果需要测试版本的 Docker 请将 stable 改为 testDebian11 可能不使用 `/etc/apt/keyrings/` gpg 错误可以考虑更换为 `/etc/apt/trusted.gpg.d` [issue 15727](https://github.com/docker/docs/issues/15727)。
### 安装 Docker
#### 安装 Docker
更新 apt 软件包缓存并安装 `docker-ce`
@@ -78,31 +87,38 @@ $ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
```
## 使用脚本自动安装
### 3.2.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本Debian 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
> 若你想安装测试版的 Docker请从 test.docker.com 获取脚本
```bash
# $ curl -fsSL test.docker.com -o get-docker.sh
## $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
## $ sudo sh get-docker.sh --mirror AzureChinaCloud
```
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定(stable)版本安装在系统中
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定 (stable) 版本安装在系统中
## 启动 Docker
### 3.2.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start docker
```
## 建立 docker 用户组
### 3.2.5 建立 docker 用户组
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
> **安全警告`docker` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐但这也意味着该用户可以轻易获取主机的最高 root 权限例如通过挂载根目录运行容器
> 如果你在一个多用户共享的生产系统上配置切勿随意将普通用户加入此组此时更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker`
@@ -118,7 +134,7 @@ $ sudo usermod -aG docker $USER
退出当前终端并重新登录进行如下测试
## 测试 Docker 是否安装正确
### 3.2.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
@@ -153,10 +169,10 @@ For more examples and ideas, visit:
若能正常输出以上信息则说明安装成功
## 镜像加速
### 3.2.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
## 参考文档
### 3.2.8 参考文档
* [Docker 官方 Debian 安装文档](https://docs.docker.com/install/linux/docker-ce/debian/)
* [Docker 官方 Debian 安装文档](https://docs.docker.com/engine/install/debian/)

View File

@@ -1,10 +1,14 @@
# Fedora 安装 Docker
## 3.3 Fedora
>警告切勿在没有配置 Docker dnf 源的情况下直接使用 dnf 命令安装 Docker.
Fedora 作为技术前沿的 Linux 发行版 Docker 有着良好的支持本节介绍在 Fedora 上的安装步骤
## 准备工作
>警告切勿在没有配置 Docker dnf 源的情况下直接使用 dnf 命令安装 Docker
### 系统要求
### 3.3.1 准备工作
确保你的 Fedora 版本在支持列表中并清理旧版本
#### 系统要求
Docker 支持以下版本的 [Fedora](https://getfedora.org/) 操作系统:
@@ -12,7 +16,7 @@ Docker 支持以下版本的 [Fedora](https://getfedora.org/) 操作系统:
* 42
* 43
### 卸载旧版本
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`使用以下命令卸载旧版本
@@ -29,7 +33,9 @@ $ sudo dnf remove docker \
docker-engine
```
## 使用 dnf 安装
### 3.3.2 使用 dnf 安装
使用 dnf 包管理器安装是推荐的方式便于后续的更行和管理
执行以下命令安装依赖包
@@ -48,10 +54,14 @@ $ sudo dnf config-manager \
$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
# 官方源
# $ sudo dnf config-manager \
# --add-repo \
# https://download.docker.com/linux/fedora/docker-ce.repo
## 官方源
## $ sudo dnf config-manager \
## --add-repo \
## https://download.docker.com/linux/fedora/docker-ce.repo
```
如果需要测试版本的 Docker 请使用以下命令
@@ -66,7 +76,7 @@ $ sudo dnf config-manager --set-enabled docker-ce-test
$ sudo dnf config-manager --set-disabled docker-ce-test
```
### 安装 Docker
#### 安装 Docker
更新 `dnf` 软件源缓存并安装 `docker-ce`
@@ -85,31 +95,38 @@ docker-ce.x86_64 18.06.1.ce-3.fc28 docker-ce-stable
$ sudo dnf -y install docker-ce-18.06.1.ce
```
## 使用脚本自动安装
### 3.3.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本Debian 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本Fedora 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
> 若你想安装测试版的 Docker请从 test.docker.com 获取脚本
```bash
# $ curl -fsSL test.docker.com -o get-docker.sh
## $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
## $ sudo sh get-docker.sh --mirror AzureChinaCloud
```
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 最新稳定(stable)版本安装在系统中
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 最新稳定 (stable) 版本安装在系统中
## 启动 Docker
### 3.3.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start docker
```
## 建立 docker 用户组
### 3.3.5 建立 docker 用户组
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
> **安全警告`docker` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐但这也意味着该用户可以轻易获取主机的最高 root 权限例如通过挂载根目录运行容器
> 如果你在一个多用户共享的生产系统上配置切勿随意将普通用户加入此组此时更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker`
@@ -125,7 +142,7 @@ $ sudo usermod -aG docker $USER
退出当前终端并重新登录进行如下测试
## 测试 Docker 是否安装正确
### 3.3.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
@@ -160,10 +177,10 @@ For more examples and ideas, visit:
若能正常输出以上信息则说明安装成功
## 镜像加速
### 3.3.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
## 参考文档
### 3.3.8 参考文档
* [Docker 官方 Fedora 安装文档](https://docs.docker.com/install/linux/docker-ce/fedora)。
* [Docker 官方 Fedora 安装文档](https://docs.docker.com/engine/install/fedora/)。

View File

@@ -1,10 +1,14 @@
# CentOS 安装 Docker
## 3.4 CentOS
>警告切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker.
CentOS (及其替代品 Rocky LinuxAlmaLinux) 是企业级服务器常用的操作系统本节介绍在这些系统上安装 Docker 的步骤
## 准备工作
>警告切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker
### 系统要求
### 3.4.1 准备工作
安装前请确认系统版本和内核版本满足 Docker 的运行要求
#### 系统要求
> **重要提示**CentOS 8 已于 2021 12 31 日停止维护CentOS 7 已于 2024 6 30 日结束支持建议新项目使用 **Rocky Linux** **AlmaLinux** 作为替代
@@ -12,7 +16,7 @@ Docker 支持 64 位版本 CentOS Stream 9、Rocky Linux 8/9、AlmaLinux 8/9
对于 Rocky LinuxAlmaLinux CentOS Stream推荐使用 `dnf` 包管理器
### 卸载旧版本
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`使用以下命令卸载旧版本
@@ -31,7 +35,9 @@ $ sudo yum remove docker \
containerd.io
```
## 使用 yum 安装
### 3.4.2 使用 yum 安装
使用 yum/dnf 安装是管理 Docker 生命周期的标准方式
执行以下命令安装依赖包
@@ -48,21 +54,25 @@ $ sudo dnf config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/dnf.repos.d/docker-ce.repo
$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
## 官方源
## $ sudo dnf config-manager \
## --add-repo \
## https://download.docker.com/linux/centos/docker-ce.repo
# 官方源
# $ sudo dnf config-manager \
# --add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo
```
如果需要测试版本的 Docker 请执行以下命令
```bash
$ sudo dnf config-manager --enable docker-ce-test
$ sudo dnf config-manager --set-enabled docker-ce-test
```
### 安装 Docker
#### 安装 Docker
更新 `dnf` 软件源缓存并安装 `docker-ce`
@@ -70,14 +80,15 @@ $ sudo dnf config-manager --enable docker-ce-test
$ sudo dnf install docker-ce docker-ce-cli containerd.io
```
## CentOS8 额外设置
### 3.4.3 CentOS8 额外设置
由于 CentOS8 防火墙使用 `nftables` Docker 尚未支持 `nftables` 我们可以使用如下设置使用 `iptables`
CentOS 8/Stream 默认使用 `nftables`Docker 在新版本中已提供 `nftables` 实验支持但在一些环境下仍可能遇到兼容性问题若你遇到容器网络异常可以先切换回 `iptables` 后端
更改 `/etc/firewalld/firewalld.conf`
```bash
# FirewallBackend=nftables
## FirewallBackend=nftables
FirewallBackend=iptables
```
@@ -89,31 +100,38 @@ $ firewall-cmd --permanent --zone=trusted --add-interface=docker0
$ firewall-cmd --reload
```
## 使用脚本自动安装
### 3.4.4 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本CentOS 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
> 若你想安装测试版的 Docker请从 test.docker.com 获取脚本
```bash
# $ curl -fsSL test.docker.com -o get-docker.sh
## $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
## $ sudo sh get-docker.sh --mirror AzureChinaCloud
```
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定(stable)版本安装在系统中
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定 (stable) 版本安装在系统中
## 启动 Docker
### 3.4.5 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start docker
```
## 建立 docker 用户组
### 3.4.6 建立 docker 用户组
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
> **安全警告`docker` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐但这也意味着该用户可以轻易获取主机的最高 root 权限例如通过挂载根目录运行容器
> 如果你在一个多用户共享的生产系统上配置切勿随意将普通用户加入此组此时更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker`
@@ -129,7 +147,7 @@ $ sudo usermod -aG docker $USER
退出当前终端并重新登录进行如下测试
## 测试 Docker 是否安装正确
### 3.4.7 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
@@ -164,11 +182,11 @@ For more examples and ideas, visit:
若能正常输出以上信息则说明安装成功
## 镜像加速
### 3.4.8 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
## 添加内核参数
### 3.4.9 添加内核参数
如果在 CentOS 使用 Docker 看到下面的这些警告信息
@@ -192,8 +210,8 @@ EOF
$ sudo sysctl -p
```
## 参考文档
### 3.4.10 参考文档
* [Docker 官方 CentOS 安装文档](https://docs.docker.com/install/linux/docker-ce/centos/)。
* [Docker 官方 CentOS 安装文档](https://docs.docker.com/engine/install/centos/)。
* https://firewalld.org/2018/07/nftables-backend
* https://github.com/moby/libnetwork/issues/2496

View File

@@ -1,8 +1,12 @@
# 树莓派卡片电脑安装 Docker
## 3.5 Raspberry Pi
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.
树莓派等 ARM 架构设备在物联网和边缘计算领域应用广泛本节介绍如何在树莓派上安装 Docker
## 系统要求
>警告切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker
### 3.5.1 系统要求
Docker ARM 架构有着良好的支持
Docker 不仅支持 `x86_64` 架构的计算机同时也支持 `ARM` 架构的计算机本小节内容以树莓派单片电脑为例讲解 `ARM` 架构安装 Docker
@@ -12,9 +16,11 @@ Docker 支持以下版本的 [Raspberry Pi OS](https://www.raspberrypi.org/softw
* Raspberry Pi OS Bookworm
* Raspberry Pi OS Bullseye
** `Raspberry Pi OS` 由树莓派的开发与维护机构 [树莓派基金会](https://www.raspberrypi.org/) 官方支持,并推荐用作树莓派的首选系统,其基于 `Debian`。
**`Raspberry Pi OS` 由树莓派的开发与维护机构[树莓派基金会](https://www.raspberrypi.org/)官方支持,并推荐用作树莓派的首选系统,其基于 `Debian`。
## 使用 APT 安装
### 3.5.2 使用 APT 安装
推荐使用 APT 包管理器进行安装以确保版本的稳定性和安全性
由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改因此我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书
@@ -25,9 +31,8 @@ $ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
lsb-release \
software-properties-common
gnupg \
lsb-release
```
鉴于国内网络问题强烈建议使用国内源官方源请在注释中查看
@@ -35,34 +40,44 @@ $ sudo apt-get install \
为了确认所下载软件包的合法性需要添加软件源的 GPG 密钥
```bash
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/raspbian/gpg | sudo apt-key add -
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/raspbian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 官方源
# $ curl -fsSL https://download.docker.com/linux/raspbian/gpg | sudo apt-key add -
## 官方源
## $ sudo install -m 0755 -d /etc/apt/keyrings
## $ curl -fsSL https://download.docker.com/linux/raspbian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
## $ sudo chmod a+r /etc/apt/keyrings/docker.gpg
```
然后我们需要向 `sources.list` 中添加 Docker 软件源
```bash
$ sudo add-apt-repository \
"deb [arch=armhf] https://mirrors.aliyun.com/docker-ce/linux/raspbian \
$(lsb_release -cs) \
stable"
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/raspbian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 官方源
# $ sudo add-apt-repository \
# "deb [arch=armhf] https://download.docker.com/linux/raspbian \
# $(lsb_release -cs) \
# stable"
## 官方源
## $ echo \
## "deb [arch=$ signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian \
## $ stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
>以上命令会添加稳定版本的 Docker APT 如果需要测试版本的 Docker 请将 stable 改为 test
#### 报错解决办法
`Raspberry Pi OS Bullseye/Bookworm` 添加 Docker 软件源的步骤可能会出现如下报错:
`Raspberry Pi OS Bullseye/Bookworm` 如果你使用 `add-apt-repository` 添加源可能会出现如下报错 (推荐改用上面的 `tee` 方式来写入 `/etc/apt/sources.list.d/docker.list`可避免此问题)
```bash
Traceback (most recent call last):
@@ -77,17 +92,19 @@ Traceback (most recent call last):
aptsources.distro.NoDistroTemplateException: Error: could not find a distribution template for Raspbian/bullseye
```
通过以下命令手动添加镜像源到 `/etc/apt/sources.list` 文件中即可解决:
通过以下命令手动添加镜像源到 `/etc/apt/sources.list` 文件中即可解决
```bash
$ sudo echo "deb [arch=armhf] https://mirrors.aliyun.com/docker-ce/linux/raspbian $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/raspbian $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list
# 官方源
# $ sudo echo "deb [arch=armhf] https://download.docker.com/linux/raspbian $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list
## 官方源
## $ echo "deb [arch=$ signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian $ stable" | sudo tee -a /etc/apt/sources.list
```
### 安装 Docker
#### 安装 Docker
更新 apt 软件包缓存并安装 `docker-ce`
@@ -97,31 +114,38 @@ $ sudo apt-get update
$ sudo apt-get install docker-ce
```
## 使用脚本自动安装
### 3.5.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程提供了一套便捷的安装脚本Raspberry Pi OS 系统上可以使用这套脚本安装另外可以通过 `--mirror` 选项使用国内源进行安装
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
> 若你想安装测试版的 Docker请从 test.docker.com 获取脚本
```bash
# $ curl -fsSL test.docker.com -o get-docker.sh
## $ curl -fsSL test.docker.com -o get-docker.sh
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
## $ sudo sh get-docker.sh --mirror AzureChinaCloud
```
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定(stable)版本安装在系统中
执行这个命令后脚本就会自动的将一切准备工作做好并且把 Docker 的稳定 (stable) 版本安装在系统中
## 启动 Docker
### 3.5.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start docker
```
## 建立 docker 用户组
### 3.5.5 建立 docker 用户组
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
默认情况下`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑一般 Linux 系统上不会直接使用 `root` 用户。因此,更好做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
> **安全警告`docker` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐但这也意味着该用户可以轻易获取主机的最高 root 权限例如通过挂载根目录运行容器
> 如果你在一个多用户共享的生产系统上配置切勿随意将普通用户加入此组此时更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker`
@@ -137,7 +161,7 @@ $ sudo usermod -aG docker $USER
退出当前终端并重新登录进行如下测试
## 测试 Docker 是否安装正确
### 3.5.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
@@ -172,8 +196,8 @@ For more examples and ideas, visit:
若能正常输出以上信息则说明安装成功
*注意* ARM 平台不能使用 `x86` 镜像查看 Raspberry Pi OS 可使用镜像请访问 [arm32v7](https://hub.docker.com/u/arm32v7/) 或者 [arm64v8](https://hub.docker.com/u/arm64v8/)。
*注意*ARM 平台不能使用 `x86` 镜像查看 Raspberry Pi OS 可使用镜像请访问 [arm32v7](https://hub.docker.com/u/arm32v7/) 或者 [arm64v8](https://hub.docker.com/u/arm64v8/)。
## 镜像加速
### 3.5.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)

View File

@@ -1,22 +1,22 @@
# Linux 离线安装
## 3.6 Linux 离线安装
\[TOC]
生产环境中一般都是没有公网资源的本文介绍如何在生产服务器上离线部署`Docker`
生产环境中一般都是没有公网资源的本文介绍如何在生产服务器上离线部署 `Docker`
括号内的字母表示该操作需要在哪些服务器上执行
![Docker-offile-install-top](../_images/image-20200412202617411.png)
## CentOS/Rocky/AlmaLinux 离线安装Docker
### 3.6.1 CentOS/Rocky/AlmaLinux 离线安装 Docker
在无法连接外网的安全环境中离线安装是唯一的选择本节介绍如何在 RHEL 系发行版中进行离线安装
> 注意以下命令以 CentOS 7 为例对于 CentOS Stream 9Rocky Linux 9 AlmaLinux 9请将 `yum` 替换为 `dnf`并将软件包后缀 `el7` 替换为 `el9`
### YUM本地文件安装推荐
#### YUM 本地文件安装 (推荐)
推荐这种方式是因为在生产环境一般会选定某个指定的文档软件版本使用
推荐这种方式是因为在生产环境一般会选定某个指定的文档软件版本使用
#### 查询可用的软件版本(A)
##### 查询可用的软件版本
```bash
#下载清华的镜像源文件
@@ -43,7 +43,7 @@ docker-ce.x86_64 3:19.03.1-3.el7 docker-ce-stable
....
```
#### 下载到指定文件夹(A)
##### 下载到指定文件夹
```bash
sudo yum install --downloadonly --downloaddir=/tmp/docker24_offline_install/ docker-ce-24.0.4-1.el7 docker-ce-cli-24.0.4-1.el7
@@ -78,15 +78,15 @@ Total
exiting because "Download Only" specified
```
#### 复制到目标服务器之后进入文件夹安装(C-N)
##### 复制到目标服务器之后进入文件夹安装 (C-N)
* 离线安装时必须使用rpm命令不检查依赖的方式安装
* 离线安装时必须使用 rpm 命令不检查依赖的方式安装
```bash
rpm -Uvh *.rpm --nodeps --force
```
#### 锁定软件版本(C-N)
##### 锁定软件版本 (C-N)
**下载锁定版本软件**
@@ -143,102 +143,117 @@ versionlock deleted: 1
sudo yum versionlock delete all
```
### YUM 本地源服务器搭建安装Docker
#### YUM 本地源服务器搭建安装 Docker
#### 挂载 ISO 镜像搭建本地 File AB
##### 挂载 ISO 镜像搭建本地 File
```bash
# 删除其他网络源
rm -f /etc/yum.repo.d/*
# 挂载光盘或者iso镜像
## 删除其他网络源
rm -f /etc/yum.repos.d/*
## 挂载光盘或者iso镜像
mount /dev/cdrom /mnt
```
```bash
# 添加本地源
## 添加本地源
cat >/etc/yum.repos.d/local_files.repo<< EOF
[Local_Files]
name=Local_Files
baseurl=file:///mnt
enable=1
enabled=1
gpgcheck=0
gpgkey=file:///mnt/RPM-GPG-KEY-CentOS-7
EOF
```
```bash
# 测试刚才的本地源,安装createrepo软件
## 测试刚才的本地源,安装createrepo软件
yum clean all
yum install createrepo -y
```
#### 根据本地文件搭建BASE网络源B
##### 根据本地文件搭建 BASE 网络源
```bash
# 安装apache 服务器
## 安装apache 服务器
yum install httpd -y
# 挂载光盘
## 挂载光盘
mount /dev/cdrom /mnt
# 新建centos目录
## 新建centos目录
mkdir /var/www/html/base
# 复制光盘内的文件到刚才新建的目录
## 复制光盘内的文件到刚才新建的目录
cp -R /mnt/Packages/* /var/www/html/base/
createrepo /var/www/html/centos/
createrepo /var/www/html/base/
systemctl enable httpd
systemctl start httpd
```
#### 下载Docker-CE 镜像仓库A
##### 下载 Docker-CE 镜像仓库
在有网络的服务器上下载Docker-ce镜像
在有网络的服务器上下载 Docker-ce 镜像
```bash
# 下载清华的镜像源文件
## 下载清华的镜像源文件
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
sudo sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
```
```bash
# 新建 docker-ce目录
## 新建 docker-ce目录
mkdir /tmp/docker-ce/
# 把镜像源同步到镜像文件中
## 把镜像源同步到镜像文件中
reposync -r docker-ce-stable -p /tmp/docker-ce/
```
#### 创建仓库索引B
##### 创建仓库索引
把下载的 docker-ce 文件夹复制到离线的服务器
```bash
# 把docker-ce 文件夹复制到/var/www/html/docker-ce
# 重建索引
## 把docker-ce 文件夹复制到/var/www/html/docker-ce
## 重建索引
createrepo /var/www/html/docker-ce/
```
#### YUM 客户端设置C...N
##### YUM 客户端设置 (C...N)
```bash
rm -f /etc/yum.repo.d/*
rm -f /etc/yum.repos.d/*
cat >/etc/yum.repos.d/local_files.repo<< EOF
[local_base]
name=local_base
# 改成B服务器地址
## 改成B服务器地址
baseurl=http://x.x.x.x/base
enable=1
enabled=1
gpgcheck=0
proxy=_none_
[docker_ce]
name=docker_ce
# 改成B服务器地址
baseurl=http://x.x.x.x/base
enable=1
## 改成B服务器地址
baseurl=http://x.x.x.x/docker-ce
enabled=1
gpgcheck=0
proxy=_none_
EOF
```
#### Docker 安装C...N
##### Docker 安装 (C...N)
```bash
sudo yum makecache fast

76
03_install/3.7_mac.md Normal file
View File

@@ -0,0 +1,76 @@
## 3.7 macOS
### 3.7.1 系统要求
[Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/) 要求系统最低为 macOS Sonoma 14.0 或更高版本,建议升级到最新版本的 macOS。
### 3.7.2 安装
> [!WARNING]
> **商业许可限制** 2021 年起Docker Desktop 对微型企业少于 250 名员工且年收入少于 1000 万美元个人使用教育和非商业开源项目仍然免费对于其他商业用途需要付费订阅企业用户请注意合规风险或考虑使用开源替代方案
Docker Desktop Mac 用户提供了无缝的 Docker 体验你可以选择使用 Homebrew 或手动下载安装包进行安装
#### 使用 Homebrew 安装
[Homebrew](https://brew.sh/) 的 [Cask](https://github.com/Homebrew/homebrew-cask) 已经支持 Docker Desktop for Mac因此可以很方便的使用 Homebrew Cask 来进行安装:
```bash
$ brew install --cask docker
```
#### 手动下载安装
如果需要手动下载请点击以下[链接](https://desktop.docker.com/mac/main/amd64/Docker.dmg)下载 Docker Desktop for Mac。
> 如果你的电脑搭载的是 Apple Silicon 芯片 (`arm64` 架构)请点击以下[链接](https://desktop.docker.com/mac/main/arm64/Docker.dmg)下载 Docker Desktop for Mac。你可以在[官方文档](https://docs.docker.com/docker-for-mac/apple-silicon/)查阅已知的问题。
如同 macOS 其它软件一样安装也非常简单双击下载的 `.dmg` 文件然后将那只叫 [Moby](https://www.docker.com/blog/call-me-moby-dock/) 的鲸鱼图标拖拽到 `Application` 文件夹即可 (其间需要输入用户密码)。
![](../_images/install-mac-dmg.jpg)
### 3.7.3 运行
从应用中找到 Docker 图标并点击运行
![](../_images/install-mac-apps.jpg)
运行之后会在右上角菜单栏看到多了一个鲸鱼图标这个图标表明了 Docker 的运行状态
![](../_images/install-mac-menubar.png)
每次点击鲸鱼图标会弹出操作菜单
![](../_images/install-mac-menu.png)
之后你可以在终端通过命令检查安装后的 Docker 版本
```bash
$ docker --version
Docker version 27.0.3, build 7d4bcd8
```
如果 `docker version``docker info` 都正常的话可以尝试运行一个 [Nginx 服务器](https://hub.docker.com/_/nginx/)
```bash
$ docker run -d -p 80:80 --name webserver nginx
```
服务运行后可以访问 [http://localhost](http://localhost),如果看到了 “Welcome to nginx就说明 Docker Desktop for Mac 安装成功了。
![](../_images/install-mac-example-nginx.png)
要停止 Nginx 服务器并删除执行下面的命令
```bash
$ docker stop webserver
$ docker rm webserver
```
### 3.7.4 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
### 3.7.5 参考链接
* [官方文档](https://docs.docker.com/desktop/setup/install/mac-install/)

51
03_install/3.8_windows.md Normal file
View File

@@ -0,0 +1,51 @@
## 3.8 Windows 10/11
Windows 平台上Docker Desktop 提供了完整的 Docker 开发环境本节介绍在 Windows 10/11 上的安装和配置
### 3.8.1 系统要求
[Docker Desktop for Windows](https://docs.docker.com/desktop/setup/install/windows-install/) 支持 64 位版本的 Windows 11 或 Windows 10 (需开启 Hyper-V),推荐使用 Windows 11。
### 3.8.2 安装
> [!WARNING]
> **商业许可限制** 2021 年起Docker Desktop 对微型企业少于 250 名员工且年收入少于 1000 万美元个人使用教育和非商业开源项目仍然免费对于其他商业用途需要付费订阅企业用户请注意合规风险或考虑使用开源替代方案
**手动下载安装**
点击以下[链接](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe)下载 Docker Desktop for Windows。
下载好之后双击 `Docker Desktop Installer.exe` 开始安装
**使用**[**winget**](https://docs.microsoft.com/zh-cn/windows/package-manager/)**安装**
```powershell
$ winget install Docker.DockerDesktop
```
### 3.8.3 WSL2 运行 Docker
若你的 Windows 版本为 Windows 10 专业版或家庭版 v1903 及以上版本可以使用 WSL2 运行 Docker具体请查看 [Docker Desktop WSL 2 backend](https://docs.docker.com/docker-for-windows/wsl/)。
### 3.8.4 运行
Windows 搜索栏输入 **Docker** 点击 **Docker Desktop** 开始运行
![](../_images/install-win-docker-app-search.png)
Docker 启动之后会在 Windows 任务栏出现鲸鱼图标
![](../_images/install-win-taskbar-circle.png)
等待片刻当鲸鱼图标静止时说明 Docker 启动成功之后你可以打开 PowerShell 使用 Docker
> 推荐使用 [Windows Terminal](https://docs.microsoft.com/zh-cn/windows/terminal/get-started) 在终端使用 Docker。
### 3.8.5 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](3.9_mirror.md)
### 3.8.6 参考链接
* [官方文档](https://docs.docker.com/desktop/setup/install/windows-install/)
* [WSL 2 Support is coming to Windows 10 Versions 1903 and 1909](https://devblogs.microsoft.com/commandline/wsl-2-support-is-coming-to-windows-10-versions-1903-and-1909/)

View File

@@ -1,5 +1,18 @@
# 安装 Docker
# 第三章 安装 Docker
Docker 分为 `stable` `test` `nightly` 三个更新频道
官方网站上有各种环境下的 [安装指南](https://docs.docker.com/get-docker/),这里主要介绍 Docker 在 `Linux` 、`Windows 10` 和 `macOS` 上的安装。
官方网站上有各种环境下的[安装指南](https://docs.docker.com/get-docker/),这里主要介绍 Docker 在 `Linux`、`Windows 10` 和 `macOS` 上的安装。
## 详细安装指南
* [Ubuntu](3.1_ubuntu.md)
* [Debian](3.2_debian.md)
* [Fedora](3.3_fedora.md)
* [CentOS](3.4_centos.md)
* [Raspberry Pi](3.5_raspberry-pi.md)
* [Linux 离线安装](3.6_offline.md)
* [macOS](3.7_mac.md)
* [Windows 10/11](3.8_windows.md)
* [镜像加速器](3.9_mirror.md)
* [开启实验特性](3.10_experimental.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,71 +0,0 @@
# macOS
## 系统要求
[Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/) 要求系统最低为 macOS Sonora 14.0 或更高版本,建议升级到最新版本的 macOS。
## 安装
### 使用 Homebrew 安装
[Homebrew](https://brew.sh/) 的 [Cask](https://github.com/Homebrew/homebrew-cask) 已经支持 Docker Desktop for Mac因此可以很方便的使用 Homebrew Cask 来进行安装:
```bash
$ brew install --cask docker
```
### 手动下载安装
如果需要手动下载请点击以下 [链接](https://desktop.docker.com/mac/main/amd64/Docker.dmg) 下载 Docker Desktop for Mac。
> 如果你的电脑搭载的是 Apple Silicon 芯片`arm64` 架构请点击以下 [链接](https://desktop.docker.com/mac/main/arm64/Docker.dmg) 下载 Docker Desktop for Mac。你可以在 [官方文档](https://docs.docker.com/docker-for-mac/apple-silicon/) 查阅已知的问题。
如同 macOS 其它软件一样安装也非常简单双击下载的 `.dmg` 文件然后将那只叫 [Moby](https://www.docker.com/blog/call-me-moby-dock/) 的鲸鱼图标拖拽到 `Application` 文件夹即可(其间需要输入用户密码)。
![](../_images/install-mac-dmg.png)
## 运行
从应用中找到 Docker 图标并点击运行
![](../_images/install-mac-apps.png)
运行之后会在右上角菜单栏看到多了一个鲸鱼图标这个图标表明了 Docker 的运行状态
![](../_images/install-mac-menubar.png)
每次点击鲸鱼图标会弹出操作菜单
![](../_images/install-mac-menu.png)
之后你可以在终端通过命令检查安装后的 Docker 版本
```bash
$ docker --version
Docker version 26.1.1, build 4cf5afa
```
如果 `docker version``docker info` 都正常的话可以尝试运行一个 [Nginx 服务器](https://hub.docker.com/_/nginx/)
```bash
$ docker run -d -p 80:80 --name webserver nginx
```
服务运行后可以访问 [http://localhost](http://localhost),如果看到了 "Welcome to nginx!",就说明 Docker Desktop for Mac 安装成功了。
![](../_images/install-mac-example-nginx.png)
要停止 Nginx 服务器并删除执行下面的命令
```bash
$ docker stop webserver
$ docker rm webserver
```
## 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
## 参考链接
* [官方文档](https://docs.docker.com/docker-for-mac/install/)

30
03_install/summary.md Normal file
View File

@@ -0,0 +1,30 @@
## 本章小结
Docker 支持在多种平台上安装和使用选择合适的安装方式是顺利使用 Docker 的第一步
| 平台 | 推荐方式 | 说明 |
|------|---------|------|
| **Ubuntu/Debian** | 官方 APT 仓库 | 最完善的支持推荐首选 |
| **CentOS/Fedora** | 官方 YUM/DNF 仓库 | 注意关闭 SELinux 或配置策略 |
| **macOS** | Docker Desktop | 图形化安装包含 Compose Kubernetes |
| **Windows 10/11** | Docker Desktop (WSL 2) | 需启用 WSL 2 后端 |
| **Raspberry Pi** | 官方安装脚本 | 支持 ARM 架构 |
| **离线环境** | 二进制包安装 | 适用于无法联网的服务器 |
### 安装后验证
安装完成后运行以下命令验证 Docker 是否正常工作
```bash
$ docker version
$ docker run --rm hello-world
```
### 延伸阅读
- [镜像加速器](3.9_mirror.md)解决国内拉取镜像慢的问题
- [开启实验特性](3.10_experimental.md)使用最新功能
- [Docker Hub](../06_repository/6.1_dockerhub.md)官方镜像仓库
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

View File

@@ -1,46 +0,0 @@
# Windows 10/11
## 系统要求
[Docker Desktop for Windows](https://docs.docker.com/docker-for-windows/install/) 支持 64 位版本的 Windows 11 或 Windows 10需开启 Hyper-V推荐使用 Windows 11。
## 安装
**手动下载安装**
点击以下 [链接](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe) 下载 Docker Desktop for Windows。
下载好之后双击 `Docker Desktop Installer.exe` 开始安装
**使用** [**winget**](https://docs.microsoft.com/zh-cn/windows/package-manager/) **安装**
```powershell
$ winget install Docker.DockerDesktop
```
## WSL2 运行 Docker
若你的 Windows 版本为 Windows 10 专业版或家庭版 v1903 及以上版本可以使用 WSL2 运行 Docker具体请查看 [Docker Desktop WSL 2 backend](https://docs.docker.com/docker-for-windows/wsl/)。
## 运行
Windows 搜索栏输入 **Docker** 点击 **Docker Desktop** 开始运行
![](../_images/install-win-docker-app-search.png)
Docker 启动之后会在 Windows 任务栏出现鲸鱼图标
![](../_images/install-win-taskbar-circle.png)
等待片刻当鲸鱼图标静止时说明 Docker 启动成功之后你可以打开 PowerShell 使用 Docker
> 推荐使用 [Windows Terminal](https://docs.microsoft.com/zh-cn/windows/terminal/get-started) 在终端使用 Docker。
## 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢可以配置 Docker [国内镜像加速](mirror.md)
## 参考链接
* [官方文档](https://docs.docker.com/docker-for-windows/install/)
* [WSL 2 Support is coming to Windows 10 Versions 1903 and 1909](https://devblogs.microsoft.com/commandline/wsl-2-support-is-coming-to-windows-10-versions-1903-and-1909/)

View File

@@ -1,6 +1,8 @@
# 获取镜像
## 4.1 获取镜像
## docker pull 命令
Docker 镜像仓库获取镜像可谓是 Docker 运作的第一步本节将介绍如何使用 `docker pull` 命令下载镜像以及如何理解下载过程
### 4.1.1 docker pull 命令
从镜像仓库获取镜像的命令是 `docker pull`
@@ -8,9 +10,11 @@
docker pull [选项] [Registry地址/]仓库名[:标签]
```
### 镜像名称格式
#### 镜像名称格式
```
Docker 镜像名称由 Registry 地址用户名仓库名和标签组成其标准格式如下
```bash
docker.io / library / ubuntu : 24.04
@@ -20,36 +24,44 @@ Registry地址 用户名 仓库名 标签
| 组成部分 | 说明 | 默认值 |
|---------|------|--------|
| Registry 地址 | 镜像仓库地址 | `docker.io`Docker Hub |
| 用户名 | 镜像所属用户/组织 | `library`官方镜像 |
| Registry 地址 | 镜像仓库地址 | `docker.io` (Docker Hub)|
| 用户名 | 镜像所属用户/组织 | `library` (官方镜像)|
| 仓库名 | 镜像名称 | 必须指定 |
| 标签 | 版本标识 | `latest` |
### 示例
#### 示例
```bash
# 完整格式
## 完整格式
$ docker pull docker.io/library/ubuntu:24.04
# 省略 Registry默认 Docker Hub
## 省略 Registry默认 Docker Hub
$ docker pull library/ubuntu:24.04
# 省略 library官方镜像
## 省略 library官方镜像
$ docker pull ubuntu:24.04
# 省略标签默认 latest
## 省略标签默认 latest
$ docker pull ubuntu
# 拉取第三方镜像
## 拉取第三方镜像
$ docker pull bitnami/redis:latest
# 从其他 Registry 拉取
## 从其他 Registry 拉取
$ docker pull ghcr.io/username/myapp:v1.0
```
---
## 下载过程解析
### 4.1.2 下载过程解析
当我们执行 `docker pull` 命令时Docker 会输出详细的下载进度让我们以 `ubuntu:24.04` 为例来解析这些信息
```bash
$ docker pull ubuntu:24.04
@@ -62,36 +74,37 @@ Status: Downloaded newer image for ubuntu:24.04
docker.io/library/ubuntu:24.04
```
### 输出解读
#### 输出解读
| 输出内容 | 说明 |
|---------|------|
| `Pulling from library/ubuntu` | 正在从官方 ubuntu 仓库拉取 |
| `92dc2a97ff99: Pull complete` | 各层的下载状态显示层 ID 12 |
| `92dc2a97ff99: Pull complete` | 各层的下载状态 (显示层 ID 12 )|
| `Digest: sha256:...` | 镜像内容的唯一摘要 |
| `docker.io/library/ubuntu:24.04` | 镜像的完整名称 |
### 分层下载
#### 分层下载
从输出可以看到镜像是**分层下载**
从输出可以看到镜像是 **分层下载**
```
ubuntu:24.04 镜像
第3层 c8299583700a 已存在跳过下载
第2层 be13a9d27eb8 下载中... 完成
第1层 92dc2a97ff99 下载中... 完成
```mermaid
flowchart TD
subgraph Image ["ubuntu:24.04 镜像"]
direction TB
L3["第3层 c8299583700a<br/>(已存在,跳过下载)"]
L2["第2层 be13a9d27eb8<br/>(下载中... 完成)"]
L1["第1层 92dc2a97ff99<br/>(下载中... 完成)"]
L3 --- L2 --- L1
end
```
如果本地已有相同的层Docker 会跳过下载节省带宽和时间
---
## 常用选项
### 4.1.3 常用选项
`docker pull` 命令支持多种选项来满足不同的下载需求例如下载所有标签指定平台架构等
| 选项 | 说明 | 示例 |
|------|------|------|
@@ -99,7 +112,7 @@ docker.io/library/ubuntu:24.04
| `--platform` | 指定平台架构 | `docker pull --platform linux/arm64 nginx` |
| `--quiet, -q` | 静默模式 | `docker pull -q nginx` |
### 指定平台
#### 指定平台
Apple Silicon Mac 上拉取 x86 镜像
@@ -109,15 +122,17 @@ $ docker pull --platform linux/amd64 nginx
---
## 拉取后运行
### 4.1.4 拉取后运行
拉取镜像后可以基于它启动容器
```bash
# 拉取镜像
## 拉取镜像
$ docker pull ubuntu:24.04
# 运行容器
## 运行容器
$ docker run -it --rm ubuntu:24.04 bash
root@e7009c6ce357:/# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04 LTS"
@@ -137,7 +152,7 @@ root@e7009c6ce357:/# exit
---
## 镜像加速
### 4.1.5 镜像加速
Docker Hub 下载可能较慢可以配置镜像加速器
@@ -155,16 +170,18 @@ root@e7009c6ce357:/# exit
```bash
$ sudo systemctl restart docker # Linux
# 或在 Docker Desktop 中重启
## 或在 Docker Desktop 中重启
```
详见 [镜像加速器](../install/mirror.md) 章节
详见[镜像加速器](../03_install/3.9_mirror.md)章节
---
## 验证镜像完整性
### 4.1.6 验证镜像完整性
### 查看镜像摘要
为了确保下载的镜像没有被篡改且内容一致我们可以校验镜像摘要 (Digest)
#### 查看镜像摘要
```bash
$ docker images --digests ubuntu
@@ -172,7 +189,7 @@ REPOSITORY TAG DIGEST
ubuntu 24.04 sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26 ca2b0f26964c
```
### 使用摘要拉取
#### 使用摘要拉取
用摘要拉取可确保获取完全相同的镜像
@@ -184,49 +201,38 @@ $ docker pull ubuntu@sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9
---
## 常见问题
### 4.1.7 常见问题
### Q: 下载速度
在使用 `docker pull` 过程中可能会遇到下载速度慢镜像不存在或磁盘空间不足等问题以下是一些常见问题的排查思路
#### Q下载速度很慢
1. 配置镜像加速器
2. 检查网络连接
3. 尝试拉取更小的镜像版本 `alpine` 变体
3. 尝试拉取更小的镜像版本 ( `alpine` 变体)
### Q: 提示镜像不存在
#### Q提示镜像不存在
```bash
Error: pull access denied, repository does not exist
```
可能原因
- 镜像名拼写错误
- 私有镜像未登录需要 `docker login`
- 私有镜像未登录 (需要 `docker login`)
- 镜像确实不存在
### Q: 磁盘空间不足
#### Q磁盘空间不足
```bash
# 清理未使用的镜像
## 清理未使用的镜像
$ docker image prune
# 清理所有未使用资源
## 清理所有未使用资源
$ docker system prune
```
---
## 本章小结
| 操作 | 命令 |
|------|------|
| 拉取镜像 | `docker pull 镜像名:标签` |
| 拉取所有标签 | `docker pull -a 镜像名` |
| 指定平台 | `docker pull --platform linux/amd64 镜像名` |
| 用摘要拉取 | `docker pull 镜像名@sha256:...` |
## 延伸阅读
- [列出镜像](list.md)查看本地镜像
- [删除镜像](rm.md)清理本地镜像
- [镜像加速器](../install/mirror.md)加速镜像下载
- [Docker Hub](../repository/dockerhub.md)官方镜像仓库

View File

@@ -1,6 +1,8 @@
# 列出镜像
## 4.2 列出镜像
## 基本用法
在下载了镜像后我们可以使用 `docker image ls` 命令列出本地主机上的镜像
### 4.2.1 基本用法
查看本地已下载的镜像
@@ -17,36 +19,40 @@ ubuntu noble 329ed837d508 3 days ago 78MB
---
## 输出字段说明
### 4.2.2 输出字段说明
`docker image ls` 命令默认输出的列表包含仓库名标签镜像 ID创建时间和占用空间等信息
| 字段 | 说明 |
|------|------|
| **REPOSITORY** | 仓库名 |
| **TAG** | 标签版本 |
| **IMAGE ID** | 镜像唯一标识 ID 12 |
| **TAG** | 标签 (版本)|
| **IMAGE ID** | 镜像唯一标识 ( ID 12 )|
| **CREATED** | 创建时间 |
| **SIZE** | 本地占用空间 |
### 同一镜像多个标签
#### 同一镜像多个标签
注意上面的 `ubuntu:24.04` `ubuntu:noble` 拥有相同的 IMAGE ID它们是同一个镜像的不同标签只占用一份存储空间
---
## 理解镜像大小
### 4.2.3 理解镜像大小
### 本地大小 vs Hub 显示大小
Docker 镜像的大小可能与我们通常理解的文件大小有所不同这涉及到分层存储的概念
#### 本地大小 vs Hub 显示大小
| 位置 | 显示大小 | 说明 |
|------|---------|------|
| Docker Hub | 29MB | 压缩后的网络传输大小 |
| docker image ls | 78MB | 本地解压后的实际大小 |
### 实际磁盘占用
#### 实际磁盘占用
由于镜像是分层存储不同镜像可能共享相同的层
```
```bash
ubuntu:24.04 nginx:latest redis:latest
@@ -56,7 +62,7 @@ ubuntu:24.04 nginx:latest redis:latest
因此`docker image ls` 中各镜像大小之和 > 实际磁盘占用
### 查看实际空间占用
#### 查看实际空间占用
```bash
$ docker system df
@@ -69,12 +75,15 @@ Build Cache 0 0 0B 0B
---
## 过滤镜像
### 4.2.4 过滤镜像
### 按仓库名过滤
随着本地镜像数量的增加我们需要更有效的方式来查找特定的镜像Docker 提供了多种过滤方式
#### 按仓库名过滤
```bash
# 列出所有 ubuntu 镜像
## 列出所有 ubuntu 镜像
$ docker images ubuntu
REPOSITORY TAG IMAGE ID SIZE
ubuntu 24.04 329ed837d508 78MB
@@ -82,7 +91,7 @@ ubuntu noble 329ed837d508 78MB
ubuntu 22.04 a1b2c3d4e5f6 72MB
```
### 按仓库名和标签过滤
#### 按仓库名和标签过滤
```bash
$ docker images ubuntu:24.04
@@ -90,7 +99,7 @@ REPOSITORY TAG IMAGE ID SIZE
ubuntu 24.04 329ed837d508 78MB
```
### 使用过滤器 --filter
#### 使用过滤器 --filter
| 过滤条件 | 说明 | 示例 |
|---------|------|------|
@@ -101,21 +110,26 @@ ubuntu 24.04 329ed837d508 78MB
| `reference=pattern` | 按名称模式 | `-f reference='*:latest'` |
```bash
# 列出 nginx 之后创建的镜像
## 列出 nginx 之后创建的镜像
$ docker images -f since=nginx:latest
# 列出所有带 latest 标签的镜像
## 列出所有带 latest 标签的镜像
$ docker images -f reference='*:latest'
# 列出带特定 LABEL 的镜像
## 列出带特定 LABEL 的镜像
$ docker images -f label=maintainer=example@email.com
```
---
## 虚悬镜像Dangling Images
### 4.2.5 虚悬镜像
### 什么是虚悬镜像
在镜像列表里你可能会看到一些仓库名和标签都为 `<none>` 的镜像这类镜像被称为虚悬镜像
#### 什么是虚悬镜像
仓库名和标签都显示为 `<none>` 的镜像
@@ -125,26 +139,30 @@ REPOSITORY TAG IMAGE ID SIZE
<none> <none> 00285df0df87 342MB
```
### 产生原因
#### 产生原因
1. **镜像重新构建**新镜像使用了旧镜像的标签旧镜像标签被移除
2. **docker pull 更新**拉取更新版本时旧版本失去标签
### 处理虚悬镜像
#### 处理虚悬镜像
```bash
# 列出虚悬镜像
## 列出虚悬镜像
$ docker images -f dangling=true
# 删除虚悬镜像
## 删除虚悬镜像
$ docker image prune
```
---
## 中间层镜像
### 4.2.6 中间层镜像
### 查看所有镜像包含中间层
除了虚悬镜像`docker image ls` 默认列出的只是顶层镜像还有一种镜像是为了加速镜像构建重复利用资源而存在的中间层镜像
#### 查看所有镜像 (包含中间层)
```bash
$ docker images -a
@@ -156,9 +174,11 @@ $ docker images -a
---
## 格式化输出
### 4.2.7 格式化输出
### 只输出 ID
为了配合脚本使用或展示更关注的信息我们可以使用 `--format` 参数来自定义输出格式
#### 只输出 ID
```bash
$ docker images -q
@@ -170,20 +190,22 @@ $ docker images -q
常用于配合其他命令
```bash
# 删除所有镜像
## 删除所有镜像
$ docker rmi $(docker images -q)
# 删除所有 redis 镜像
## 删除所有 redis 镜像
$ docker rmi $(docker images -q redis)
```
### 显示完整 ID
#### 显示完整 ID
```bash
$ docker images --no-trunc
```
### 显示摘要
#### 显示摘要
```bash
$ docker images --digests
@@ -191,18 +213,20 @@ REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:b4f0e0bdeb5... e43d811ce2f4
```
### 自定义格式
#### 自定义格式
使用 Go 模板语法自定义输出
```bash
# 只显示 ID 和仓库名
## 只显示 ID 和仓库名
$ docker images --format "{{.ID}}: {{.Repository}}"
5f515359c7f8: redis
05a60462f8ba: nginx
329ed837d508: ubuntu
# 表格形式带标题
## 表格形式带标题
$ docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY TAG SIZE
redis latest 183MB
@@ -210,7 +234,7 @@ nginx latest 181MB
ubuntu 24.04 78MB
```
### 可用模板字段
#### 可用模板字段
| 字段 | 说明 |
|------|------|
@@ -224,35 +248,20 @@ ubuntu 24.04 78MB
---
## 常用命令组合
### 4.2.8 常用命令组合
```bash
# 列出所有镜像及其大小按大小排序需要系统 sort 命令
## 列出所有镜像及其大小按大小排序需要系统 sort 命令
$ docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -h
# 查找大于 500MB 的镜像
## 查找大于 500MB 的镜像
$ docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | grep -E "^[0-9]+GB|^[5-9][0-9]{2}MB"
# 导出镜像列表
## 导出镜像列表
$ docker images --format "{{.Repository}}:{{.Tag}}" > images.txt
```
---
## 本章小结
| 操作 | 命令 |
|------|------|
| 列出所有镜像 | `docker images` |
| 按仓库名过滤 | `docker images nginx` |
| 列出虚悬镜像 | `docker images -f dangling=true` |
| 只输出 ID | `docker images -q` |
| 显示摘要 | `docker images --digests` |
| 自定义格式 | `docker images --format "..."` |
| 查看空间占用 | `docker system df` |
## 延伸阅读
- [获取镜像](pull.md) Registry 拉取镜像
- [删除镜像](rm.md)清理本地镜像
- [镜像](../02_basic_concept/image.md)理解镜像概念

View File

@@ -1,6 +1,8 @@
# 删除本地镜像
## 4.3 删除本地镜像
## 基本用法
当不再需要某个镜像时我们可以将其删除以释放存储空间本节介绍删除镜像的常用方法
### 4.3.1 基本用法
使用 `docker image rm` 删除本地镜像
@@ -12,18 +14,18 @@ $ docker image rm [选项] <镜像1> [<镜像2> ...]
---
## 镜像标识方式
### 4.3.2 镜像标识方式
删除镜像时可以使用多种方式指定镜像
| 方式 | 说明 | 示例 |
|------|------|------|
| ** ID** | ID 的前几位通常 3-4 | `docker rmi 501` |
| ** ID** | ID 的前几位 (通常 3-4 )| `docker rmi 501` |
| ** ID** | 完整的镜像 ID | `docker rmi 501ad78535f0...` |
| **镜像名:标签** | 仓库名和标签 | `docker rmi redis:alpine` |
| **镜像摘要** | 精确的内容摘要 | `docker rmi nginx@sha256:...` |
### 使用短 ID 删除
#### 使用短 ID 删除
```bash
$ docker image ls
@@ -31,13 +33,14 @@ REPOSITORY TAG IMAGE ID SIZE
redis alpine 501ad78535f0 30MB
nginx latest e43d811ce2f4 142MB
# 只需输入足够区分的前几位
## 只需输入足够区分的前几位
$ docker rmi 501
Untagged: redis:alpine
Deleted: sha256:501ad78535f0...
```
### 使用镜像名删除
#### 使用镜像名删除
```bash
$ docker rmi redis:alpine
@@ -45,23 +48,27 @@ Untagged: redis:alpine
Deleted: sha256:501ad78535f0...
```
### 使用摘要删除
#### 使用摘要删除
摘要删除最精确适用于 CI/CD 场景
```bash
# 查看镜像摘要
## 查看镜像摘要
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:b4f0e0bdeb5... e43d811ce2f4
# 使用摘要删除
## 使用摘要删除
$ docker rmi nginx@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
```
---
## 理解输出信息
### 4.3.3 理解输出信息
执行删除命令后Docker 会输出一系列的操作记录理解这些信息有助于我们掌握镜像删除的机制
删除镜像时会看到两类信息**Untagged** **Deleted**
@@ -74,83 +81,98 @@ Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
```
### Untagged vs Deleted
#### Untagged vs Deleted
| 操作 | 含义 |
|------|------|
| **Untagged** | 移除镜像的标签 |
| **Deleted** | 删除镜像的存储层 |
### 删除流程
#### 删除流程
```
docker rmi redis:alpine
1. Untag移除 redis:alpine 标签
2. 检查是否还有其他标签指向这个镜像
Untag不删除
3. 检查是否有容器依赖
报错无法删除
4. 从上到下逐层删除检查每层是否被其他镜像使用
被使用 保留
未使用 Deleted
Docker 会检测镜像是否有容器依赖或其他标签指向只有在确认为无用资源时才会真正删除存储层
```mermaid
flowchart TD
Start(["docker rmi redis:alpine"]) --> Step1
subgraph Process ["删除流程"]
direction TB
Step1["1. Untag移除 redis:alpine 标签"] --> Step2
Step2{"2. 检查是否还有其他标签指向此镜像"}
Step2 -- "有" --> Keep1["只 Untag不删除"]
Step2 -- "无" --> Step3
Step3{"3. 检查是否有容器依赖"}
Step3 -- "有" --> Error["报错,无法删除"]
Step3 -- "无" --> Step4
Step4{"4. 从上到下逐层删除,检查每层是否被其他镜像使用"}
Step4 -- "被使用" --> Keep2["保留该层"]
Step4 -- "未使用" --> Delete["Deleted (删除该层)"]
end
```
---
## 批量删除
### 4.3.4 批量删除
### 删除所有虚悬镜像
手动一个一个删除镜像非常繁琐Docker 提供了 `image prune` 命令和 shell 组合命令来实现批量清理
虚悬镜像dangling没有标签的镜像通常是旧版本被新版本覆盖后产生的
#### 删除所有虚悬镜像
虚悬镜像 (dangling)没有标签的镜像通常是旧版本被新版本覆盖后产生的
```bash
# 查看虚悬镜像
## 查看虚悬镜像
$ docker images -f dangling=true
# 删除虚悬镜像
## 删除虚悬镜像
$ docker image prune
# 不提示确认
## 不提示确认
$ docker image prune -f
```
### 删除所有未使用的镜像
#### 删除所有未使用的镜像
```bash
# 删除所有没有被容器使用的镜像
## 删除所有没有被容器使用的镜像
$ docker image prune -a
# 保留最近 24 小时的
## 保留最近 24 小时的
$ docker image prune -a --filter "until=24h"
```
### 按条件删除
#### 按条件删除
```bash
# 删除所有 redis 镜像
## 删除所有 redis 镜像
$ docker rmi $(docker images -q redis)
# 删除 mongo:8.0 之前的所有镜像
## 删除 mongo:8.0 之前的所有镜像
$ docker rmi $(docker images -q -f before=mongo:8.0)
# 删除某个时间之前的镜像
## 删除某个时间之前的镜像
$ docker image prune -a --filter "until=168h" # 7天前
```
---
## 删除失败的常见原因
### 4.3.5 删除失败的常见原因
### 原因一有容器依赖
在删除镜像时Docker 可能会提示错误并拒绝执行这通常是为了防止误删正在使用的资源
#### 原因一有容器依赖
```bash
$ docker rmi nginx
@@ -161,15 +183,17 @@ Error: conflict: unable to remove repository reference "nginx"
**解决方案**
```bash
# 方案1先删除依赖的容器
## 方案1先删除依赖的容器
$ docker rm abc123
$ docker rmi nginx
# 方案2强制删除镜像容器仍可运行但无法再创建新容器
## 方案2强制删除镜像容器仍可运行但无法再创建新容器
$ docker rmi -f nginx
```
### 原因二多个标签指向同一镜像
#### 原因二多个标签指向同一镜像
```bash
$ docker images
@@ -179,10 +203,12 @@ ubuntu latest ca2b0f26964c # 同一个镜像
$ docker rmi ubuntu:24.04
Untagged: ubuntu:24.04
# 只是移除标签镜像仍存在因为还有 ubuntu:latest 指向它
## 只是移除标签镜像仍存在因为还有 ubuntu:latest 指向它
```
### 原因三被其他镜像依赖中间层
当同一个镜像有多个标签时`docker rmi` 只是删除指定的标签不会删除镜像本身
#### 原因三被其他镜像依赖 (中间层)
```bash
$ docker rmi some_base_image
@@ -193,7 +219,7 @@ Error: image has dependent child images
---
## 常用过滤条件
### 4.3.6 常用过滤条件
| 过滤条件 | 说明 | 示例 |
|---------|------|------|
@@ -205,26 +231,31 @@ Error: image has dependent child images
---
## 清理策略
### 4.3.7 清理策略
### 开发环境
针对不同的环境 (开发环境 vs 生产环境)我们应该采用不同的镜像清理策略
#### 开发环境
```bash
# 定期清理虚悬镜像
## 定期清理虚悬镜像
$ docker image prune -f
# 一键清理所有未使用资源
## 一键清理所有未使用资源
$ docker system prune -a
```
### CI/CD 环境
#### CI/CD 环境
```bash
# 只保留最近使用的镜像
## 只保留最近使用的镜像
$ docker image prune -a --filter "until=72h" -f
```
### 查看空间占用
#### 查看空间占用
```bash
$ docker system df
@@ -236,20 +267,3 @@ Build Cache 0 0 0B 0B
```
---
## 本章小结
| 操作 | 命令 |
|------|------|
| 删除指定镜像 | `docker rmi 镜像名:标签` |
| 强制删除 | `docker rmi -f 镜像名` |
| 删除虚悬镜像 | `docker image prune` |
| 删除未使用镜像 | `docker image prune -a` |
| 批量删除 | `docker rmi $(docker images -q -f ...)` |
| 查看空间占用 | `docker system df` |
## 延伸阅读
- [列出镜像](list.md)查看和过滤镜像
- [删除容器](../05_container/rm.md)清理容器
- [数据卷](../07_data_network/data/volume.md)清理数据卷

View File

@@ -1,8 +1,8 @@
# 利用 commit 理解镜像构成
## 4.4 利用 commit 理解镜像构成
> 注意如果您是初学者您可以暂时跳过后面的内容直接学习 [容器](../05_container/) 一节
> 注意如果您是初学者您可以暂时跳过后面的内容直接学习[容器](../05_container/)一节
注意 `docker commit` 命令除了学习之外还有一些特殊的应用场合比如被入侵后保存现场等但是不要使用 `docker commit` 定制镜像定制镜像应该使用 `Dockerfile` 来完成如果你想要定制镜像请查看下一小节
注意`docker commit` 命令除了学习之外还有一些特殊的应用场合比如被入侵后保存现场等但是不要使用 `docker commit` 定制镜像定制镜像应该使用 `Dockerfile` 来完成如果你想要定制镜像请查看下一小节
镜像是容器的基础每次执行 `docker run` 的时候都会指定哪个镜像作为容器运行的基础在之前的例子中我们所使用的都是来自于 Docker Hub 的镜像直接使用这些镜像是可以满足一定的需求而当这些镜像无法直接满足需求时我们就需要定制这些镜像接下来的几节就将讲解如何定制镜像
@@ -16,11 +16,11 @@ $ docker run --name webserver -d -p 80:80 nginx
这条命令会用 `nginx` 镜像启动一个容器命名为 `webserver`并且映射了 80 端口这样我们可以用浏览器去访问这个 `nginx` 服务器
如果是在本机运行的 Docker那么可以直接访问`http://localhost` 如果是在虚拟机云服务器上安装的 Docker则需要将 `localhost` 换为虚拟机地址或者实际云服务器地址
如果是在本机运行的 Docker那么可以直接访问`http://localhost`如果是在虚拟机云服务器上安装的 Docker则需要将 `localhost` 换为虚拟机地址或者实际云服务器地址
直接用浏览器访问的话我们会看到默认的 Nginx 欢迎页面
![](../_images/images-mac-example-nginx.png)
![](../_images/images-mac-example-nginx.png)
现在假设我们非常不喜欢这个欢迎页面我们希望改成欢迎 Docker 的文字我们可以使用 `docker exec` 命令进入容器修改其内容
@@ -37,7 +37,7 @@ exit
现在我们再刷新浏览器的话会发现内容被改变了
![](../_images/images-create-nginx-docker.png)
![](../_images/images-create-nginx-docker.png)
我们修改了容器的文件也就是改动了容器的存储层我们可以通过 `docker diff` 命令看到具体的改动
@@ -63,7 +63,7 @@ A /var/cache/nginx/uwsgi_temp
现在我们定制好了变化我们希望能将其保存下来形成镜像
要知道当我们运行一个容器的时候如果不使用卷的话我们做的任何文件修改都会被记录于容器存储层里 Docker 提供了一个 `docker commit` 命令可以将容器的存储层保存下来成为镜像换句话说就是在原有镜像的基础上再叠加上容器的存储层并构成新的镜像以后我们运行这个新镜像的时候就会拥有原有容器最后的文件变化
要知道当我们运行一个容器的时候 (如果不使用卷的话)我们做的任何文件修改都会被记录于容器存储层里 Docker 提供了一个 `docker commit` 命令可以将容器的存储层保存下来成为镜像换句话说就是在原有镜像的基础上再叠加上容器的存储层并构成新的镜像以后我们运行这个新镜像的时候就会拥有原有容器最后的文件变化
`docker commit` 的语法格式为
@@ -120,7 +120,7 @@ docker run --name web2 -d -p 81:80 nginx:v2
至此我们第一次完成了定制镜像使用的是 `docker commit` 命令手动操作给旧的镜像添加了新的一层形成新的镜像对镜像多层存储应该有了更直观的感觉
## 慎用 `docker commit`
### 4.4.1 慎用 `docker commit`
使用 `docker commit` 命令虽然可以比较直观的帮助理解镜像分层存储的概念但是实际环境中并不会这样使用

View File

@@ -1,10 +1,10 @@
# 使用 Dockerfile 定制镜像
## 4.5 使用 Dockerfile 定制镜像
从刚才的 `docker commit` 的学习中我们可以了解到镜像的定制实际上就是定制每一层所添加的配置文件如果我们可以把每一层修改安装构建操作的命令都写入一个脚本用这个脚本来构建定制镜像那么之前提及的无法重复的问题镜像构建透明性的问题体积的问题就都会解决这个脚本就是 Dockerfile
Dockerfile 是一个文本文件其内包含了一条条的 **指令(Instruction)**每一条指令构建一层因此每一条指令的内容就是描述该层应当如何构建
Dockerfile 是一个文本文件其内包含了一条条的 **指令 (Instruction)**每一条指令构建一层因此每一条指令的内容就是描述该层应当如何构建
## 使用 docker init 快速创建推荐
### 4.5.1 使用 docker init 快速创建 (推荐)
Docker 提供了 `docker init` 命令可以根据项目类型自动生成 Dockerfile.dockerignore compose.yaml 文件
@@ -12,9 +12,9 @@ Docker 提供了 `docker init` 命令,可以根据项目类型自动生成 Doc
$ docker init
```
该命令会交互式地询问项目类型 GoNode.jsPythonRust 并生成符合最佳实践的配置文件对于新项目这是推荐的起步方式
该命令会交互式地询问项目类型 ( GoNode.jsPythonRust )并生成符合最佳实践的配置文件对于新项目这是推荐的起步方式
## 手动创建 Dockerfile
### 4.5.2 手动创建 Dockerfile
还以之前定制 `nginx` 镜像为例这次我们使用 Dockerfile 来定制
@@ -35,7 +35,7 @@ RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
这个 Dockerfile 很简单一共就两行涉及到了两条指令`FROM` `RUN`
## FROM 指定基础镜像
### 4.5.3 FROM 指定基础镜像
所谓定制镜像那一定是以一个镜像为基础在其上进行定制就像我们之前运行了一个 `nginx` 镜像的容器再进行修改一样基础镜像是必须指定的 `FROM` 就是指定 **基础镜像**因此一个 `Dockerfile` `FROM` 是必备的指令并且必须是第一条指令
@@ -52,9 +52,9 @@ FROM scratch
如果你以 `scratch` 为基础镜像的话意味着你不以任何镜像为基础接下来所写的指令将作为镜像第一层开始存在
不以任何系统为基础直接将可执行文件复制进镜像的做法并不罕见对于 Linux 下静态编译的程序来说并不需要有操作系统提供运行时支持所需的一切库都已经在可执行文件里了因此直接 `FROM scratch` 会让镜像体积更加小巧使用 [Go 语言](https://golang.google.cn/) 开发的应用很多会使用这种方式来制作镜像,这也是有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
不以任何系统为基础直接将可执行文件复制进镜像的做法并不罕见对于 Linux 下静态编译的程序来说并不需要有操作系统提供运行时支持所需的一切库都已经在可执行文件里了因此直接 `FROM scratch` 会让镜像体积更加小巧使用 [Go 语言](https://golang.google.cn/)开发的应用很多会使用这种方式来制作镜像,这也是有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
## RUN 执行命令
### 4.5.4 RUN 执行命令
`RUN` 指令是用来执行命令行命令的由于命令行的强大能力`RUN` 指令在定制镜像时是最常用的指令之一其格式有两种
@@ -64,57 +64,19 @@ FROM scratch
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
```
* *exec* 格式`RUN ["可执行文件", "参数1", "参数2"]`这更像是函数调用中的格式
* *exec* 格式`RUN [可执行文件, 参数1, 参数2]`这更像是函数调用中的格式
既然 `RUN` 就像 Shell 脚本一样可以执行命令那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 比如这样
Dockerfile 中每一个指令都会建立一层`RUN` 也不例外每一个 `RUN` 的行为就和刚才我们手工建立镜像的过程一样新建立一层在其上执行这些命令执行结束后`commit` 这一层的修改构成新的镜像
```docker
FROM debian:bookworm
> **注意**
>
> 每一个 `RUN` 指令都会产生一个新的镜像层为了减少镜像体积和层数我们通常会将多个命令合并到一个 `RUN` 指令中执行
>
> 更多关于 `RUN` 指令的详细用法最佳实践 (如清理缓存使用 pipefail ) `Union FS` 的层数限制等内容请参阅 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中的 **[RUN 指令](../07_dockerfile/7.1_run.md)** 小节
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-7.2.4.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
```
要想编写优秀的 `Dockerfile`必须了解每一条指令的作用和副作用 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 我们将对 `COPY``ADD``CMD``ENTRYPOINT` 等指令进行详细讲解
之前说过Dockerfile 中每一个指令都会建立一层`RUN` 也不例外每一个 `RUN` 的行为就和刚才我们手工建立镜像的过程一样新建立一层在其上执行这些命令执行结束后`commit` 这一层的修改构成新的镜像
而上面的这种写法创建了 7 层镜像这是完全没有意义的而且很多运行时不需要的东西都被装进了镜像里比如编译环境更新的软件包等等结果就是产生非常臃肿非常多层的镜像不仅仅增加了构建部署的时间也很容易出错
这是很多初学 Docker 的人常犯的一个错误
*Union FS 是有最大层数限制的比如 AUFS曾经是最大不得超过 42 现在是不得超过 127 *
上面的 `Dockerfile` 正确的写法应该是这样
```docker
FROM debian:bookworm
RUN set -x; buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-7.2.4.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
```
首先之前所有的命令只有一个目的就是编译安装 redis 可执行文件因此没有必要建立很多层这只是一层的事情因此这里没有使用很多个 `RUN` 一一对应不同的命令而是仅仅使用一个 `RUN` 指令并使用 `&&` 将各个所需命令串联起来将之前的 7 简化为了 1 在撰写 Dockerfile 的时候要经常提醒自己这并不是在写 Shell 脚本而是在定义每一层该如何构建
并且这里为了格式化还进行了换行Dockerfile 支持 Shell 类的行尾添加 `\` 的命令换行方式以及行首 `#` 进行注释的格式良好的格式比如换行缩进注释等会让维护排障更为容易这是一个比较好的习惯
此外还可以看到这一组命令的最后添加了清理工作的命令删除了为了编译构建所需要的软件清理了所有下载展开的文件并且还清理了 `apt` 缓存文件这是很重要的一步我们之前说过镜像是多层存储每一层的东西并不会在下一层被删除会一直跟随着镜像因此镜像构建时一定要确保每一层只添加真正需要添加的东西任何无关的东西都应该清理掉
很多人初学 Docker 制作出了很臃肿的镜像的原因之一就是忘记了每一层构建的最后一定要清理掉无关文件
## 构建镜像
### 4.5.5 构建镜像
好了让我们再回到之前定制的 nginx 镜像的 Dockerfile 现在我们明白了这个 Dockerfile 的内容那么让我们来构建这个镜像吧
@@ -142,11 +104,11 @@ docker build [选项] <上下文路径/URL/->
在这里我们指定了最终镜像的名称 `-t nginx:v3`构建成功后我们可以像之前运行 `nginx:v2` 那样来运行这个镜像其结果会和 `nginx:v2` 一样
## 镜像构建上下文Context
### 4.5.6 镜像构建上下文
如果注意会看到 `docker build` 命令最后有一个 `.``.` 表示当前目录 `Dockerfile` 就在当前目录因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径这么理解其实是不准确的如果对应上面的命令格式你可能会发现这是在指定 **上下文路径**那么什么是上下文呢
首先我们要理解 `docker build` 的工作原理Docker 在运行时分为 Docker 引擎也就是服务端守护进程和客户端工具Docker 的引擎提供了一组 REST API被称为 [Docker Remote API](https://docs.docker.com/develop/sdk/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端Docker 引擎完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
首先我们要理解 `docker build` 的工作原理Docker 在运行时分为 Docker 引擎 (也就是服务端守护进程) 和客户端工具Docker 的引擎提供了一组 REST API被称为 [Docker Remote API](https://docs.docker.com/develop/sdk/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端 (Docker 引擎) 完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候并非所有定制都会通过 `RUN` 指令完成经常会需要将一些本地文件复制进镜像比如通过 `COPY` 指令`ADD` 指令等 `docker build` 命令构建镜像其实并非在本地构建而是在服务端也就是 Docker 引擎中构建的那么在这种客户端/服务端的架构中如何才能让服务端获得本地文件呢
@@ -158,7 +120,7 @@ docker build [选项] <上下文路径/URL/->
COPY ./package.json /app/
```
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`也不是复制 `Dockerfile` 所在目录下的 `package.json`而是复制 **上下文context** 目录下的 `package.json`
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`也不是复制 `Dockerfile` 所在目录下的 `package.json`而是复制 **上下文 (context)** 目录下的 `package.json`
因此`COPY` 这类指令中的源文件的路径都是*相对路径*这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因因为这些路径已经超出了上下文的范围Docker 引擎无法获得这些位置的文件如果真的需要那些文件应该将它们复制到上下文目录中去
@@ -182,15 +144,16 @@ Sending build context to Docker daemon 2.048 kB
当然一般大家习惯性的会使用默认的文件名 `Dockerfile`以及会将其置于镜像构建上下文目录中
## 其它 `docker build` 的用法
### 4.5.7 其它 `docker build` 的用法
### 直接用 Git repo 进行构建
#### 直接用 Git repo 进行构建
或许你已经注意到了`docker build` 还支持从 URL 构建比如可以直接从 Git repo 中构建
```bash
# $env:DOCKER_BUILDKIT=0
# export DOCKER_BUILDKIT=0
## $env:DOCKER_BUILDKIT=0
## export DOCKER_BUILDKIT=0
$ docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world
@@ -207,7 +170,7 @@ Successfully built 038ad4142d2b
这行命令指定了构建所需的 Git repo并且指定分支为 `master`构建目录为 `/amd64/hello-world/`然后 Docker 就会自己去 `git clone` 这个项目切换到指定分支并进入到指定目录后开始构建
### 用给定的 tar 压缩包构建
#### 用给定的 tar 压缩包构建
```bash
$ docker build http://server/context.tar.gz
@@ -215,7 +178,7 @@ $ docker build http://server/context.tar.gz
如果所给出的 URL 不是个 Git repo而是个 `tar` 压缩包那么 Docker 引擎会下载这个包并自动解压缩以其作为上下文开始构建
### 从标准输入中读取 Dockerfile 进行构建
#### 从标准输入中读取 Dockerfile 进行构建
```bash
docker build - < Dockerfile
@@ -229,7 +192,7 @@ cat Dockerfile | docker build -
如果标准输入传入的是文本文件则将其视为 `Dockerfile`并开始构建这种形式由于直接从标准输入中读取 Dockerfile 的内容它没有上下文因此不可以像其他方法那样可以将本地文件 `COPY` 进镜像之类的事情
### 从标准输入中读取上下文压缩包进行构建
#### 从标准输入中读取上下文压缩包进行构建
```bash
$ docker build - < context.tar.gz

View File

@@ -1,8 +1,8 @@
# 其它制作镜像的方式
## 4.6 其它制作镜像的方式
除了标准的使用 `Dockerfile` 生成镜像的方法外由于各种特殊需求和历史原因还提供了一些其它方法用以生成镜像
## rootfs 压缩包导入
### 4.6.1 rootfs 压缩包导入
格式`docker import [选项] <文件>|<URL>|- [<仓库名>[:<标签>]]`
@@ -37,11 +37,11 @@ IMAGE CREATED CREATED BY SIZE
f477a6e18e98 About a minute ago 214.9 MB Imported from http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz
```
## Docker 镜像的导入和导出 `docker save` `docker load`
### 4.6.2 Docker 镜像的导入和导出 `docker save` `docker load`
Docker 还提供了 `docker save` `docker load` 命令用以将镜像保存为一个文件然后传输到另一个位置上再加载进来这是在没有 Docker Registry 时的做法现在已经不推荐镜像迁移应该直接使用 Docker Registry无论是直接使用 Docker Hub 还是使用内网私有 Registry 都可以
### 保存镜像
#### 保存镜像
使用 `docker save` 命令可以将镜像保存为归档文件
@@ -63,7 +63,7 @@ filename: POSIX tar archive
这里的 filename 可以为任意名称甚至任意后缀名但文件的本质都是归档文件
**注意如果同名则会覆盖没有警告**
**注意如果同名则会覆盖 (没有警告)**
若使用 `gzip` 压缩

65
04_image/4.7_internal.md Normal file
View File

@@ -0,0 +1,65 @@
## 4.7 实现原理
Docker 镜像是怎么实现增量的修改和维护的为什么容器启动如此之快这一切都归功于 Docker 的镜像分层存储设计
### 4.7.1 镜像与分层存储
在之前的章节中我们一直强调镜像包含操作系统完整的 `root` 文件系统其体积往往是庞大的因此在 Docker 设计时就充分利用 **Union FS** 的技术将其设计为分层存储的架构
Docker 镜像并不是一个单纯的文件而是由一组文件系统叠加构成的
最底层的镜像称为 **基础镜像 (Base Image)**通常是各种 Linux 发行版的 root 文件系统 UbuntuDebianCentOS
当我们在基础镜像之上构建新的镜像时 (例如安装了 Nginx)Docker 并不是复制一份基础镜像而是在基础镜像之上**新建一个层 (Layer)**并在该层中仅记录为了安装 Nginx 而发生的文件变更 (添加修改删除)
这种分层存储结构使得镜像的复用分发变得非常高效
* **复用**如果多个镜像都基于同一个基础镜像 (例如都基于 `ubuntu:24.04`)那么宿主机只需要下载一份 `ubuntu:24.04`所有镜像都可以共享它
* **轻量**镜像仅仅记录了与基础镜像的差异因此体积非常小
### 4.7.2 容器层与读写
我们要理解的一个关键概念是**镜像的每一层都是只读的 (Read-only)**
那么既然镜像只读容器为什么能写文件呢
当容器启动时Docker 会在镜像的最上层添加一个新的 **可写层 (Writable Layer)**通常被称为 **容器层**
```mermaid
flowchart TD
subgraph Container ["运行中的容器"]
direction TB
L4["容器层 (可写, Writable Container Layer)"]
L3["镜像层 (只读, Read-only Image Layer)"]
L2["镜像层 (只读, Read-only Image Layer)"]
L1["基础镜像层 (只读, Base Image Layer)"]
L4 --- L3 --- L2 --- L1
end
Note["所有的写操作都在容器层这里"] -.-> L4
```
* **读取文件**当容器需要读取文件时Docker 会从最上层 (容器层) 开始向下层 (镜像层) 寻找直到找到该文件为止
* **修改文件**当容器需要修改某个文件时Docker 会从下层镜像中将该文件复制到上层的容器层然后对副本进行修改这被称为 **写时复制 (Copy-on-WriteCoW)** 策略
* **删除文件**当容器删除某个文件时Docker 并不是真的去下层删除它 (因为下层是只读的)而是在容器层创建一个特殊的 白障 (Whiteout) 文件用来标记该文件已被删除从而在容器视图中隐藏它
这就是为什么
1. **容器删除后数据会丢失**因为所有的数据修改都保存在最上层的容器层中容器销毁时这个层也就随之销毁了(除非使用了数据卷详见[数据管理](../08_data/README.md))
2. **镜像不可变**无论我们在容器里删除了多少文件基础镜像的体积并不会减小因为它们依然存在于底层的只读层中
### 4.7.3 内容寻址与镜像 ID
Docker 镜像的每一层都有一个唯一的 ID这个 ID 是根据该层的内容计算出来的哈希值 (SHA256)这意味着
* **内容即 ID**只要层的内容有一丁点变化ID 就会变
* **安全性**确保了镜像内容的完整性下载过程中如果数据损坏ID 校验就会失败
* **去重**如果两个不同的镜像 (甚至是不同来源的镜像) 包含相同的层 (ID 相同)Docker 引擎在本地只会存储一份绝不重复下载
### 4.7.4 联合文件系统
Docker 使用联合文件系统 (Union FS) 来实现这种分层挂载常见的驱动包括 `overlay2` (目前推荐)`aufs` (早期使用)`btrfs``zfs`
虽然实现细节不同但它们都遵循上述的 **分层 + CoW** 模型
> 想要深入了解 Overlay2 等文件系统的具体实现原理包括 WorkDirUpperDirLowerDir 等底层细节请阅读 **[第十二章 底层实现](../12_implementation/README.md)** 中的 **[联合文件系统](../12_implementation/12.4_ufs.md)** 章节

View File

@@ -1,13 +1,22 @@
# 使用 Docker 镜像
# 第四章 使用镜像
在之前的介绍中我们知道镜像是 Docker 的三大组件之一
Docker 运行容器前需要本地存在对应的镜像如果本地不存在该镜像Docker 会从镜像仓库下载该镜像
## 本章内容
本章将介绍更多关于镜像的内容包括
* 从仓库获取镜像
* [从仓库获取镜像](4.1_pull.md)
* [列出镜像](4.2_list.md)
* [删除本地镜像](4.3_rm.md)
* [利用 commit 理解镜像构成](4.4_commit.md)
* [使用 Dockerfile 定制镜像](4.5_build.md)
* [其它制作镜像的方式](4.6_other.md)
* [镜像的实现原理](4.7_internal.md)
* 管理本地主机上的镜像
* 介绍镜像实现的基本原理
> **版本提示镜像存储后端的变迁**
>
> Docker Engine v29 及后续版本中Docker 全新安装默认启用了 **containerd image store**替代了传统的 classic store这一底层架构级别的变迁意味着 Docker 解锁了对 OCI Image Index Attestations 例如原生的 provenance 来源证明与 SBOM 软件物料清单的全量本地支持
> 读者在执行类似 `docker buildx build --provenance=mode=min --sbom=true` 甚至使用后续审查工具 `docker buildx imagetools inspect`其元数据能够与镜像数据一并完好地管理于本地存储系统中为供应链安全验证补齐了最后一块拼图

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -1,3 +0,0 @@
# Dockerfile 指令详解
我们已经介绍了 `FROM``RUN`还提及了 `COPY`, `ADD`其实 `Dockerfile` 功能很强大它提供了十多个指令下面我们继续讲解其他的指令

View File

@@ -1,219 +0,0 @@
# EXPOSE 声明端口
## 基本语法
```docker
EXPOSE <端口> [<端口>/<协议>...]
```
`EXPOSE` 声明容器运行时提供服务的端口这是一个**文档性质的声明**告诉使用者容器会监听哪些端口
---
## 基本用法
```docker
# 声明单个端口
EXPOSE 80
# 声明多个端口
EXPOSE 80 443
# 声明 TCP 和 UDP 端口
EXPOSE 80/tcp
EXPOSE 53/udp
```
---
## EXPOSE 的作用
### 1. 文档说明
告诉镜像使用者容器将在哪些端口提供服务
```docker
# 使用者一看就知道这是 web 应用
EXPOSE 80 443
```
```bash
# 查看镜像暴露的端口
$ docker inspect nginx --format '{{.Config.ExposedPorts}}'
map[80/tcp:{}]
```
### 2. 配合 -P 使用
使用 `docker run -P` Docker 会自动映射 EXPOSE 的端口到宿主机随机端口
```docker
# Dockerfile
EXPOSE 80
```
```bash
$ docker run -P nginx
$ docker port $(docker ps -q)
80/tcp -> 0.0.0.0:32768
```
---
## EXPOSE vs -p
| 特性 | EXPOSE | -p |
|------|--------|-----|
| **位置** | Dockerfile | docker run 命令 |
| **作用** | 声明/文档 | 实际端口映射 |
| **是否必需** | | 外部访问时 |
| **映射发生时** | 不发生 | 运行时发生 |
```
┌────────────────────────────────────────────────────────────┐
│ EXPOSE 80 │
│ ↓ │
│ 仅声明意图 │
│ └───────────────────────────────────────┘ │
│ │
│ docker run -p │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ 实际端口映射 │ │
│ │ 宿主机 ←→ 容器 │ │
│ └─────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```
### 没有 EXPOSE 也能 -p
```docker
# 即使没有 EXPOSE也可以使用 -p
FROM nginx
# 没有 EXPOSE
```
```bash
# 仍然可以映射端口
$ docker run -p 8080:80 mynginx
```
---
## 常见误解
### 误解EXPOSE 会打开端口
```docker
# ❌ 错误理解:这不会让容器可从外部访问
EXPOSE 80
```
EXPOSE 不会
- 自动进行端口映射
- 让服务可从外部访问
- 在容器启动时开启端口监听
EXPOSE 只是元数据声明容器是否实际监听该端口取决于容器内的应用
### 正确理解
```docker
# Dockerfile
FROM nginx
EXPOSE 80 # 1. 声明:这个容器会在 80 端口提供服务
```
```bash
# 运行:需要 -p 才能从外部访问
$ docker run -p 8080:80 nginx # 2. 映射:宿主机 8080 → 容器 80
```
---
## 最佳实践
### 1. 总是声明应用使用的端口
```docker
# Web 服务
FROM nginx
EXPOSE 80 443
# 数据库
FROM postgres
EXPOSE 5432
# Redis
FROM redis
EXPOSE 6379
```
### 2. 使用明确的协议
```docker
# 默认是 TCP
EXPOSE 80
# 明确指定 UDP
EXPOSE 53/udp
# 同时支持 TCP 和 UDP
EXPOSE 53/tcp 53/udp
```
### 3. 与应用实际端口保持一致
```docker
# ✅ 好EXPOSE 与应用端口一致
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]
# ❌ 差EXPOSE 与应用端口不一致(误导)
EXPOSE 80
CMD ["node", "server.js"] # 实际监听 3000
```
---
## 使用环境变量
```docker
ARG PORT=80
EXPOSE $PORT
```
---
## Compose
```yaml
services:
web:
build: .
ports:
- "8080:80" # 映射端口(类似 -p
expose:
- "80" # 仅声明(类似 EXPOSE
```
`expose` Compose 中仅用于容器间通信的文档说明不进行端口映射
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 声明容器提供服务的端口文档 |
| **不会** | 自动映射端口或开放外部访问 |
| **配合** | `docker run -P` 自动映射 |
| **外部访问** | 需要 `-p 宿主机端口:容器端口` |
| **语法** | `EXPOSE 80` `EXPOSE 80/tcp` |
## 延伸阅读
- [网络配置](../../network/README.md)Docker 网络详解
- [端口映射](../../network/port_bindingbindingbinding.md)-p 参数详解
- [Compose 端口](../../compose/compose_file.md)Compose 中的端口配置

View File

@@ -1,151 +0,0 @@
# ONBUILD 为他人做嫁衣裳
## 基本语法
```docker
ONBUILD <其它指令>
```
`ONBUILD` 是一个特殊的指令它后面跟的是其它指令 `RUN`, `COPY` 这些指令**在当前镜像构建时不会执行**只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行
---
## 为什么需要 ONBUILD
`ONBUILD` 主要用于制作**语言栈基础镜像****框架基础镜像**
### 场景维护 Node.js 项目
假设你有多个 Node.js 项目它们的构建流程都一样
1. 创建目录
2. 复制 `package.json`
3. 执行 `npm install`
4. 复制源码
5. 启动应用
如果不使用 `ONBUILD`每个项目的 Dockerfile 都要重复这些步骤且通过 `COPY` 复制文件时基础镜像无法预知子项目的文件名
### 使用 ONBUILD 的解决方案
**基础镜像 (my-node-base)**
```docker
FROM node:20-alpine
WORKDIR /app
# 这些指令将在子镜像构建时执行
ONBUILD COPY package*.json ./
ONBUILD RUN npm install
ONBUILD COPY . .
CMD ["npm", "start"]
```
**子项目 Dockerfile**
```docker
FROM my-node-base
# 只需要一行!
# 构建时会自动执行 COPY 和 RUN
```
---
## 执行机制
```
基础镜像构建:
Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触发器)
(指令未执行)
子镜像构建:
FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行触发器指令 ──> 继续执行子 Dockerfile
```
---
## 常见使用场景
### 1. 自动处理依赖安装
```docker
# Python 基础镜像
ONBUILD COPY requirements.txt ./
ONBUILD RUN pip install -r requirements.txt
```
### 2. 自动编译代码
```docker
# Go 基础镜像
ONBUILD COPY . .
ONBUILD RUN go build -o app main.go
```
### 3. 处理静态资源
```docker
# Nginx 静态网站基础镜像
ONBUILD COPY dist/ /usr/share/nginx/html/
```
---
## 注意事项
### 1. 继承性限制
`ONBUILD` 指令**只会继承一次**
- 镜像 A ( ONBUILD)
- 镜像 B (FROM A) -> 触发 ONBUILD
- 镜像 C (FROM B) -> **不会**再次触发 ONBUILD
### 2. 构建上下文
子镜像构建时`ONBUILD COPY . .` 中的 `.` 指的是**子项目**的构建上下文而不是基础镜像的上下文
### 3. 不允许级联
`ONBUILD ONBUILD` 是非法的你不能写 `ONBUILD ONBUILD COPY ...`
### 4. 可能会导致构建失败
由于 `ONBUILD` 实际上是在子镜像中执行指令如果子项目的上下文不满足要求例如缺少 `package.json`会导致子镜像构建失败且错误信息可能比较隐晦
---
## 最佳实践
### 1. 命名规范
建议在镜像标签中添加 `-onbuild` 后缀明确告知使用者该镜像包含触发器
```
node:20-onbuild
python:3.12-onbuild
```
### 2. 避免执行耗时操作
尽量不要在 `ONBUILD` 中执行过于耗时或不确定的操作如更新系统软件这会让子镜像构建变得缓慢且不可控
### 3. 清理工作
如果 `ONBUILD` 指令产生了临时文件最好在同一个指令链中清理或者提供机制让子镜像清理
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 定义在子镜像构建时执行的指令 |
| **语法** | `ONBUILD INSTRUCTION` |
| **适用** | 基础架构镜像Node, Python, Go |
| **限制** | 只继承一次不可级联 |
| **规范** | 建议使用 `-onbuild` 标签后缀 |
## 延伸阅读
- [COPY 指令](copy.md)文件复制
- [Dockerfile 最佳实践](../../15_appendix/best_practices.md)基础镜像设计

View File

@@ -1,7 +0,0 @@
# 参考文档
* `Dockerfile` 官方文档https://docs.docker.com/engine/reference/builder/
* `Dockerfile` 最佳实践文档https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
* `Docker` 官方镜像 `Dockerfile`https://github.com/docker-library/docs

View File

@@ -1,273 +0,0 @@
# USER 指定当前用户
## 基本语法
```docker
USER <用户名>[:<用户组>]
USER <UID>[:<GID>]
```
`USER` 指令切换后续指令RUNCMDENTRYPOINT的执行用户
---
## 为什么要使用 USER
> 笔者强调以非 root 用户运行容器是最重要的安全实践之一
```
root 用户运行的风险:
┌────────────────────────────────────────────────────────┐
│ 容器内 root ←─ 可能逃逸 ─→ 宿主机 root │
│ │ │ │
│ └── 漏洞利用 ───────────────→ 完全控制宿主机 │
└────────────────────────────────────────────────────────┘
非 root 用户运行:
┌────────────────────────────────────────────────────────┐
│ 容器内普通用户 ──逃逸后──→ 宿主机普通用户 │
│ │ │ │
│ └── 权限受限,危害降低 ─────→ 无法控制系统 │
└────────────────────────────────────────────────────────┘
```
---
## 基本用法
### 创建并切换用户
```docker
FROM node:20-alpine
# 1. 创建用户和组
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
# 2. 设置目录权限
WORKDIR /app
COPY --chown=appuser:appgroup . .
# 3. 切换用户
USER appuser
# 4. 后续命令以 appuser 身份运行
CMD ["node", "server.js"]
```
### 使用 UID/GID
```docker
# 也可以使用数字
USER 1001:1001
```
---
## 用户必须已存在
`USER` 指令只能切换到**已存在**的用户
```docker
# ❌ 错误:用户不存在
USER nonexistent
# Error: unable to find user nonexistent
# ✅ 正确:先创建用户
RUN useradd -r -s /bin/false appuser
USER appuser
```
### 创建用户的方式
**Debian/Ubuntu**
```docker
RUN groupadd -r appgroup && \
useradd -r -g appgroup appuser
```
**Alpine**
```docker
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S -G appgroup appuser
```
| 选项 | 说明 |
|------|------|
| `-r` (useradd) / `-S` (adduser) | 创建系统用户 |
| `-g` | 指定主组 |
| `-G` | 指定附加组 |
| `-u` | 指定 UID |
| `-s /bin/false` | 禁用登录 shell |
---
## 运行时切换用户
### 使用 gosu推荐
ENTRYPOINT 脚本中切换用户时不要使用 `su` `sudo`应使用 [gosu](https://github.com/tianon/gosu)
```docker
FROM debian:bookworm
# 创建用户
RUN groupadd -r redis && useradd -r -g redis redis
# 安装 gosu
RUN apt-get update && apt-get install -y gosu && rm -rf /var/lib/apt/lists/*
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["redis-server"]
```
**docker-entrypoint.sh**
```bash
#!/bin/bash
set -e
# 以 root 执行初始化
chown -R redis:redis /data
# 用 gosu 切换到 redis 用户运行服务
exec gosu redis "$@"
```
### 为什么不用 su/sudo
| 问题 | su/sudo | gosu |
|------|---------|------|
| TTY 要求 | 需要 | 不需要 |
| 信号传递 | 不正确 | 正确 |
| 子进程 | | exec 替换 |
| 容器中使用 | | |
---
## 运行时覆盖用户
使用 `-u` `--user` 参数
```bash
# 以指定用户运行
$ docker run -u 1001:1001 myimage
# 以 root 运行(调试时)
$ docker run -u root myimage
```
---
## 文件权限处理
切换用户后确保应用有权访问文件
```docker
FROM node:20-alpine
# 创建用户
RUN adduser -D -u 1001 appuser
WORKDIR /app
# 方式1使用 --chown
COPY --chown=appuser:appuser . .
# 方式2手动 chown减少层数
# COPY . .
# RUN chown -R appuser:appuser /app
USER appuser
CMD ["node", "server.js"]
```
---
## 最佳实践
### 1. 始终使用非 root 用户
```docker
# ✅ 推荐
RUN adduser -D appuser
USER appuser
CMD ["myapp"]
# ❌ 避免
CMD ["myapp"] # 以 root 运行
```
### 2. 使用固定 UID/GID
便于在宿主机和容器间共享文件
```docker
# 使用常见的非 root UID
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S -G appgroup appuser
USER 1000:1000
```
### 3. 多阶段构建中的 USER
```docker
# 构建阶段可以用 root
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
# 生产阶段用非 root
FROM node:20-alpine
RUN adduser -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/dist .
USER appuser
CMD ["node", "server.js"]
```
---
## 常见问题
### Q: 权限被拒绝
```bash
permission denied: '/app/data.log'
```
**解决**确保目录权限正确
```docker
RUN mkdir -p /app/data && chown appuser:appuser /app/data
```
### Q: 无法绑定低于 1024 的端口
root 用户无法绑定 80443 等端口
**解决**
1. 使用高端口 8080
2. 在运行时映射端口`docker run -p 80:8080`
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 切换后续指令的执行用户 |
| **语法** | `USER username` `USER UID:GID` |
| **前提** | 用户必须已存在 |
| **运行时覆盖** | `docker run -u` |
| **切换工具** | 使用 gosu不用 su/sudo |
## 延伸阅读
- [安全](../../security/README.md)容器安全实践
- [ENTRYPOINT](entrypoint.md)入口脚本中的用户切换
- [最佳实践](../../15_appendix/best_practices.md)Dockerfile 安全

View File

@@ -1,247 +0,0 @@
# VOLUME 定义匿名卷
## 基本语法
```docker
VOLUME ["/路径1", "/路径2"]
VOLUME /路径
```
`VOLUME` 指令创建挂载点并标记为外部挂载的卷
---
## 为什么使用 VOLUME
> **核心原则**容器存储层应该保持无状态任何运行时数据都应该存储在卷中
```
没有 VOLUME 使用 VOLUME
┌─────────────────────┐ ┌─────────────────────┐
│ 容器存储层 │ │ 容器存储层 │
│ ┌─────────────┐ │ │ (只读/无状态) │
│ │ 数据库文件 │←─问题 │ │
│ │ 日志文件 │ │ └──────────┬──────────┘
│ │ 上传文件 │ │ │
│ └─────────────┘ │ ┌──────────▼──────────┐
└─────────────────────┘ │ 数据卷 │
容器删除 = 数据丢失 │ ┌─────────────┐ │
│ │ 持久化数据 │←─安全
│ └─────────────┘ │
└─────────────────────┘
容器删除,数据保留
```
---
## 基本用法
### 定义单个卷
```docker
FROM mysql:8.0
VOLUME /var/lib/mysql
```
### 定义多个卷
```docker
FROM myapp
VOLUME ["/data", "/logs", "/config"]
```
---
## VOLUME 的行为
### 1. 自动创建匿名卷
如果运行时未指定挂载Docker 会自动创建匿名卷
```bash
$ docker run mysql:8.0
$ docker volume ls
DRIVER VOLUME NAME
local a1b2c3d4e5f6... # 自动创建的匿名卷
```
### 2. 可被命名卷覆盖
```bash
# 使用命名卷替代匿名卷
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
```
### 3. 可被 Bind Mount 覆盖
```bash
# 使用宿主机目录替代
$ docker run -v /my/data:/var/lib/mysql mysql:8.0
```
---
## VOLUME 在构建时的特殊行为
> **重要**VOLUME 之后对该目录的修改会被丢弃
```docker
FROM ubuntu
VOLUME /data
# ❌ 这个文件不会出现在镜像中!
RUN echo "hello" > /data/test.txt
```
**原因**VOLUME 指令之后Docker 将该目录视为外部挂载点不再记录对它的修改
### 正确做法
```docker
FROM ubuntu
# ✅ 先写入文件
RUN mkdir -p /data && echo "hello" > /data/test.txt
# 再声明 VOLUME
VOLUME /data
```
---
## 常见使用场景
### 数据库持久化
```docker
FROM postgres:15
VOLUME /var/lib/postgresql/data
```
### 日志目录
```docker
FROM nginx
VOLUME /var/log/nginx
```
### 上传文件目录
```docker
FROM myapp
VOLUME /app/uploads
```
---
## 查看 VOLUME 定义
```bash
# 查看镜像定义的 VOLUME
$ docker inspect mysql:8.0 --format '{{json .Config.Volumes}}' | jq
{
"/var/lib/mysql": {}
}
# 查看容器挂载的卷
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
```
---
## VOLUME vs docker run -v
| 特性 | Dockerfile VOLUME | docker run -v |
|------|-------------------|---------------|
| **定义时机** | 镜像构建时 | 容器运行时 |
| **默认行为** | 创建匿名卷 | 可指定命名卷或路径 |
| **灵活性** | 固定路径 | 可任意指定 |
| **适用场景** | 定义必须持久化的路径 | 灵活的数据管理 |
---
## Compose
```yaml
services:
db:
image: postgres:15
volumes:
# 命名卷(推荐)
- postgres_data:/var/lib/postgresql/data
# Bind Mount
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
postgres_data: # 声明命名卷
```
---
## 安全注意事项
### 匿名卷可能导致数据丢失
```bash
# 使用 --rm 运行的容器,匿名卷会在容器删除时一起删除
$ docker run --rm mysql:8.0
# 容器停止后,数据丢失!
```
**解决**始终使用命名卷
```bash
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
```
---
## 最佳实践
### 1. 定义必须持久化的路径
```docker
# 数据库必须使用卷
FROM postgres:15
VOLUME /var/lib/postgresql/data
```
### 2. 不要在 VOLUME 后修改目录
```docker
# ❌ 避免
VOLUME /app/data
RUN cp init-data.json /app/data/
# ✅ 正确
RUN mkdir -p /app/data && cp init-data.json /app/data/
VOLUME /app/data
```
### 3. 文档中说明 VOLUME 用途
```docker
# 持久化用户上传的文件
VOLUME /app/uploads
# 持久化数据库数据
VOLUME /var/lib/mysql
```
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 创建挂载点标记为外部卷 |
| **语法** | `VOLUME /path` |
| **默认行为** | 自动创建匿名卷 |
| **覆盖方式** | `docker run -v name:/path` |
| **注意** | VOLUME 之后的修改会丢失 |
## 延伸阅读
- [数据卷](../../07_data_network/data/volume.md)卷的管理和使用
- [挂载主机目录](../../07_data_network/data/bind-mounts.md)Bind Mount
- [Compose 数据管理](../../compose/compose_file.md)Compose 中的卷配置

View File

@@ -1,9 +0,0 @@
# 镜像的实现原理
Docker 镜像是怎么实现增量的修改和维护的
每个镜像都由很多层次构成Docker 使用 [Union FS](https://en.wikipedia.org/wiki/UnionFS) 将这些不同的层结合到一个镜像中去。
通常 Union FS 有两个用途, 一方面可以实现不借助 LVMRAID 将多个 disk 挂到同一个目录下,另一个更常用的就是将一个只读的分支和一个可写的分支联合在一起Live CD 正是基于此方法可以允许在镜像不变的基础上允许用户在其上进行一些写操作
Docker OverlayFS 上构建的容器也是利用了类似的原理

36
04_image/summary.md Normal file
View File

@@ -0,0 +1,36 @@
## 本章小结
本章介绍了 Docker 镜像的获取列出删除以及构建方式
| 操作 | 命令 |
|------|------|
| 拉取镜像 | `docker pull 镜像名:标签` |
| 拉取所有标签 | `docker pull -a 镜像名` |
| 指定平台 | `docker pull --platform linux/amd64 镜像名` |
| 用摘要拉取 | `docker pull 镜像名@sha256:...` |
| 列出所有镜像 | `docker images` |
| 按仓库名过滤 | `docker images nginx` |
| 列出虚悬镜像 | `docker images -f dangling=true` |
| 只输出 ID | `docker images -q` |
| 显示摘要 | `docker images --digests` |
| 自定义格式 | `docker images --format "..."` |
| 查看空间占用 | `docker system df` |
| 删除指定镜像 | `docker rmi 镜像名:标签` |
| 强制删除 | `docker rmi -f 镜像名` |
| 删除虚悬镜像 | `docker image prune` |
| 删除未使用镜像 | `docker image prune -a` |
| 批量删除 | `docker rmi $(docker images -q -f ...)` |
### 延伸阅读
- [获取镜像](4.1_pull.md) Registry 拉取镜像
- [列出镜像](4.2_list.md)查看和过滤镜像
- [删除镜像](4.3_rm.md)清理本地镜像
- [镜像加速器](../03_install/3.9_mirror.md)加速镜像下载
- [Docker Hub](../06_repository/6.1_dockerhub.md)官方镜像仓库
- [镜像](../02_basic_concept/2.1_image.md)理解镜像概念
- [删除容器](../05_container/5.6_rm.md)清理容器
- [数据卷](../08_data/8.1_volume.md)清理数据卷
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

225
05_container/5.1_run.md Normal file
View File

@@ -0,0 +1,225 @@
## 5.1 启动
本节将详细介绍 Docker 容器的启动方式包括新建启动和重新启动已停止的容器
### 5.1.1 启动方式概述
启动容器有两种方式
- **新建并启动**基于镜像创建新容器
- **重新启动**将已终止的容器重新运行
由于 Docker 容器非常轻量实际使用中常常是随时删除和新建容器而不是反复重启同一个容器
### 5.1.2 新建并启动
#### 基本语法
```bash
docker run [选项] 镜像 [命令] [参数...]
```
#### 最简单的例子
输出 Hello World 后容器自动终止
```bash
$ docker run ubuntu:24.04 /bin/echo 'Hello world'
Hello world
```
这与直接执行 `/bin/echo 'Hello world'` 几乎没有区别但实际上已经启动了一个完整的 Ubuntu 容器来执行这条命令
#### 交互式容器
启动一个可以交互的 bash 终端
```bash
$ docker run -it ubuntu:24.04 /bin/bash
root@af8bae53bdd3:/#
```
**参数说明**
| 参数 | 作用 |
|------|------|
| `-i` | 保持标准输入 (stdin) 打开允许输入 |
| `-t` | 分配伪终端 (pseudo-TTY)提供终端界面 |
| `-it` | 两者组合使用获得交互式终端 |
在交互模式下可以执行命令
```bash
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@af8bae53bdd3:/# exit # 退出容器
```
### 5.1.3 docker run 的完整流程
执行 `docker run` Docker 在后台完成以下操作
```mermaid
flowchart TD
Cmd["docker run ubuntu:24.04 /bin/echo 'Hello'"] --> Step1
Step1{"1. 检查本地是否有 ubuntu:24.04 镜像"}
Step1 -- 有 --> Step1_Yes["使用本地镜像"]
Step1 -- 无 --> Step1_No["从 Registry 下载"]
Step1_Yes --> Step2
Step1_No --> Step2
Step2["2. 创建容器<br/>• 基于镜像的只读层<br/>• 添加一层可读写层(容器存储层)"] --> Step3
Step3["3. 配置网络<br/>• 创建虚拟网卡<br/>• 分配 IP 地址<br/>• 连接到 Docker 网桥"] --> Step4
Step4["4. 启动容器,执行指定命令"] --> Step5
Step5["5. 命令执行完毕,容器停止"]
```
### 5.1.4 常用启动选项
#### 基础选项
| 选项 | 说明 | 示例 |
|------|------|------|
| `-d` | 后台运行 (detach)| `docker run -d nginx` |
| `-it` | 交互式终端 | `docker run -it ubuntu bash` |
| `--name` | 指定容器名称 | `docker run --name myapp nginx` |
| `--rm` | 退出后自动删除容器 | `docker run --rm ubuntu echo hi` |
#### 端口映射
```bash
## 将容器的 80 端口映射到宿主机的 8080 端口
$ docker run -d -p 8080:80 nginx
## 随机映射端口
$ docker run -d -P nginx
## 只绑定到 localhost
$ docker run -d -p 127.0.0.1:8080:80 nginx
```
#### 数据卷挂载
```bash
## 挂载命名卷
$ docker run -v mydata:/data nginx
## 挂载宿主机目录
$ docker run -v /host/path:/container/path nginx
## 只读挂载
$ docker run -v /host/path:/container/path:ro nginx
```
#### 环境变量
```bash
## 设置单个环境变量
$ docker run -e MYSQL_ROOT_PASSWORD=secret mysql
## 从文件加载环境变量
$ docker run --env-file .env myapp
```
#### 资源限制
```bash
## 限制内存
$ docker run -m 512m nginx
## 限制 CPU
$ docker run --cpus=1.5 nginx
```
### 5.1.5 启动已终止容器
使用 `docker start` 重新启动已停止的容器
```bash
## 查看所有容器(包括已停止的)
$ docker ps -a
CONTAINER ID IMAGE STATUS NAMES
af8bae53bdd3 ubuntu Exited (0) 2 minutes ago myubuntu
## 重新启动
$ docker start myubuntu
## 启动并附加终端
$ docker start -ai myubuntu
```
### 5.1.6 容器内进程的特点
容器内只运行指定的应用程序及其必需资源
```bash
root@ba267838cc1b:/# ps
PID TTY TIME CMD
1 ? 00:00:00 bash
11 ? 00:00:00 ps
```
可见容器中仅运行了 `bash` 进程这种特点使得 Docker 对资源的利用率极高
> 💡 笔者提示容器内的 PID 1 进程很重要它是容器的主进程该进程退出则容器停止详见[后台运行](5.2_daemon.md)章节
### 5.1.7 常见问题
#### Q容器启动后立即退出
**原因**主进程执行完毕或无法保持运行
```bash
## 这个容器会立即退出echo 执行完就结束了)
$ docker run ubuntu echo "hello"
## 解决:使用能持续运行的命令
$ docker run -d nginx # nginx 是持续运行的服务
```
详细解释见[后台运行](5.2_daemon.md)
#### Q无法连接容器内的服务
**原因**未正确映射端口
```bash
## 错误:没有 -p 参数,外部无法访问
$ docker run -d nginx
## 正确:映射端口
$ docker run -d -p 80:80 nginx
```
#### Q容器内修改的文件丢失
**原因**未使用数据卷数据保存在容器存储层
```bash
## 使用数据卷持久化
$ docker run -v mydata:/app/data myapp
```
详见[数据管理](../08_data/README.md)

View File

@@ -1,19 +1,19 @@
# 后台运行
## 5.2 守护态运行
在生产环境中我们通常需要容器持续运行不受终端关闭的影响本节将深入讲解如何让容器在后台运行以及理解容器生命周期的核心概念
## 核心概念前台 vs 后台
### 5.2.1 核心概念前台 vs 后台
当你在终端运行一个程序时有两种模式
- **前台运行**程序占用当前终端输出直接显示关闭终端程序就停止
- **后台运行**程序在后台执行不占用终端终端关闭也不影响程序
Docker 容器默认是**前台运行**使用 `-d`detach参数可以让容器在后台运行
Docker 容器默认是 **前台运行** 使用 `-d` (detach) 参数可以让容器在后台运行
## 基本使用
### 5.2.2 基本使用
### 前台运行默认
#### 前台运行 (默认)
```bash
$ docker run ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
@@ -23,12 +23,13 @@ hello world
hello world
```
容器会把输出的结果STDOUT打印到宿主机上面此时
容器会把输出的结果 (STDOUT) 打印到宿主机上面此时
- 终端被占用无法执行其他命令
- `Ctrl+C` 会终止容器
- 关闭终端窗口容器也会停止
### 后台运行使用 -d 参数
#### 后台运行 (使用 -d 参数)
```bash
$ docker run -d ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
@@ -36,12 +37,13 @@ $ docker run -d ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep
```
使用 `-d` 参数后
- 容器在后台运行
- 返回容器的完整 ID
- 终端立即释放可以继续执行其他命令
- 输出不会直接显示需要用 `docker logs` 查看
- 输出不会直接显示 (需要用 `docker logs` 查看)
## 深入理解容器为什么会"立即退出"
### 5.2.3 深入理解容器为什么会 立即退出
> **这是初学者最常遇到的困惑** 理解这个问题你就理解了 Docker 的核心设计理念
@@ -53,29 +55,31 @@ $ docker run -d ubuntu:24.04
然后用 `docker ps` 查看发现容器根本不在运行这是为什么
### 核心原理容器的生命周期与主进程绑定
#### 核心原理容器的生命周期与主进程绑定
```
Docker 容器的生命周期 = 容器内 PID 1 进程的生命周期
主进程启动 容器运行
主进程退出 容器停止
```mermaid
flowchart TD
subgraph Lifecycle ["Docker 容器的生命周期 = 容器内 PID 1 进程的生命周期"]
direction LR
Start["主进程启动"] --> Run["容器运行"]
Exit["主进程退出"] --> Stop["容器停止"]
end
```
当你运行 `docker run -d ubuntu:24.04`
1. 容器启动
2. 没有指定命令默认执行 `/bin/bash`
3. 但没有交互式终端没有 `-it` 参数bash 发现没有输入源
3. 但没有交互式终端 (没有 `-it` 参数)bash 发现没有输入源
4. bash 立即退出
5. 主进程退出容器停止
**关键理解**
- `-d` 参数**不是**让容器"一直运行"
- `-d` 参数是让容器"在后台运行"能运行多久取决于主进程
### 常见的"立即退出"场景
- `-d` 参数 **不是** 让容器 一直运行
- `-d` 参数是让容器 在后台运行能运行多久取决于主进程
#### 常见的 立即退出 场景
| 场景 | 原因 | 解决方案 |
|------|------|---------|
@@ -83,9 +87,9 @@ $ docker run -d ubuntu:24.04
| `docker run -d nginx` 后改了配置 | 配置错误导致 nginx 启动失败 | 查看 `docker logs` |
| 自定义镜像容器启动即退 | Dockerfile CMD 执行完毕 | 确保 CMD 是前台进程 |
## 查看后台容器
### 5.2.4 查看后台容器
### 查看运行中的容器
#### 查看运行中的容器
```bash
$ docker container ls
@@ -93,7 +97,7 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS PO
77b2dc01fe0f ubuntu:24.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
```
### 查看容器输出日志
#### 查看容器输出日志
```bash
$ docker container logs 77b2dc01fe0f
@@ -103,78 +107,89 @@ hello world
...
```
**实时查看日志**类似 `tail -f`
**实时查看日志** (类似 `tail -f`)
```bash
$ docker container logs -f 77b2dc01fe0f
```
### 查看已停止的容器
#### 查看已停止的容器
```bash
$ docker container ls -a
```
加上 `-a` 参数可以看到所有容器包括已停止的这对于调试"容器启动即退出"的问题非常有用
加上 `-a` 参数可以看到所有容器包括已停止的这对于调试 容器启动即退出 的问题非常有用
## 最佳实践
### 5.2.5 最佳实践
### 1. 长期运行的服务使用 -d
#### 1. 长期运行的服务使用 -d
```bash
# Web 服务器
## Web 服务器
$ docker run -d -p 80:80 nginx
# 数据库
## 数据库
$ docker run -d -p 3306:3306 mysql:8
# 缓存服务
## 缓存服务
$ docker run -d -p 6379:6379 redis
```
### 2. 调试时先用前台模式
#### 2. 调试时先用前台模式
当容器启动有问题时**去掉 `-d` 参数**可以直接看到输出和错误
当容器启动有问题时**去掉 `-d` 参数** 可以直接看到输出和错误
```bash
# 有问题的容器先前台运行看看发生了什么
## 有问题的容器先前台运行看看发生了什么
$ docker run myimage:latest
```
### 3. 使用 --rm 自动清理
#### 3. 使用 --rm 自动清理
对于一次性任务使用 `--rm` 参数让容器退出后自动删除
```bash
$ docker run --rm ubuntu:24.04 echo "Hello, World!"
Hello, World!
# 容器执行完后自动删除
## 容器执行完后自动删除
...
```
### 4. 配合日志查看
#### 4. 配合日志查看
```bash
# 后台启动
## 后台启动
$ docker run -d --name myapp myimage:latest
# 查看最近 100 行日志
## 查看最近 100 行日志
$ docker logs --tail 100 myapp
# 实时跟踪日志
## 实时跟踪日志
$ docker logs -f myapp
# 查看带时间戳的日志
## 查看带时间戳的日志
$ docker logs -t myapp
```
## 常见问题排查
### 5.2.6 常见问题排查
### Q: 容器启动后立即退出
#### Q容器启动后立即退出
1. **查看退出状态码**
```bash
$ docker ps -a --filter "name=mycontainer"
# 查看 STATUS "Exited (1)" 表示异常退出
# 查看 STATUS Exited (1) 表示异常退出
```
2. **查看容器日志**
@@ -186,9 +201,10 @@ $ docker logs -t myapp
```bash
$ docker run -it myimage:latest /bin/sh
# 进入容器手动执行命令查找问题
```
### Q: 容器在后台运行但无法访问服务
#### Q容器在后台运行但无法访问服务
1. **检查端口映射**
```bash
@@ -200,7 +216,7 @@ $ docker logs -t myapp
$ docker exec mycontainer ps aux
```
### Q: 如何让已经在后台运行的容器回到前台
#### Q如何让已经在后台运行的容器回到前台
使用 `docker attach`
@@ -210,9 +226,9 @@ $ docker attach mycontainer
> **注意**`attach` 会连接到容器的主进程如果主进程不是交互式的你可能只能看到输出使用 `Ctrl+P` `Ctrl+Q` 可以安全退出而不停止容器
## 延伸阅读
### 5.2.7 延伸阅读
- [进入容器](attach_exec.md)如何进入正在运行的容器执行命令
- [容器日志](../15_appendix/best_practices.md)生产环境的日志管理最佳实践
- [HEALTHCHECK 健康检查](../04_image/dockerfile/healthcheck.md)自动检测容器内服务是否正常
- [Docker Compose](../compose/README.md)管理多个后台容器的更好方式
- [进入容器](5.4_attach_exec.md)如何进入正在运行的容器执行命令
- [容器日志](../appendix/best_practices.md)生产环境的日志管理最佳实践
- [HEALTHCHECK 健康检查](../07_dockerfile/7.12_healthcheck.md)自动检测容器内服务是否正常
- [Docker Compose](../11_compose/README.md)管理多个后台容器的更好方式

233
05_container/5.3_stop.md Normal file
View File

@@ -0,0 +1,233 @@
## 5.3 终止
本节将介绍如何终止一个运行中的容器以及几种不同的终止方式及其区别
### 5.3.1 终止方式概述
终止容器有三种方式
| 方式 | 命令 | 说明 |
|------|------|------|
| **优雅停止** | `docker stop` | 先发 SIGTERM超时后发 SIGKILL |
| **强制停止** | `docker kill` | 直接发 SIGKILL |
| **自动终止** | - | 容器主进程退出时自动停止 |
---
### 5.3.2 docker stop (推荐)
#### docker stop 基本用法
```bash
$ docker stop 容器名或ID
```
#### 工作原理
```mermaid
flowchart TD
cmd["docker stop mycontainer"] --> A["1. 发送 SIGTERM 信号给容器主进程 (PID 1)"]
A --> B["2. 等待容器优雅退出 (默认 10 秒)"]
B --> C["3. 如果超时仍未退出,发送 SIGKILL 强制终止"]
```
#### 自定义超时时间
```bash
## 等待 30 秒后强制终止
$ docker stop -t 30 mycontainer
## 立即发送 SIGKILL相当于 docker kill
$ docker stop -t 0 mycontainer
```
#### 停止多个容器
```bash
## 停止多个指定容器
$ docker stop container1 container2 container3
## 停止所有运行中的容器
$ docker stop $(docker ps -q)
```
---
### 5.3.3 docker kill
#### 基本用法
```bash
$ docker kill 容器名或ID
```
#### stop 的区别
| 命令 | 信号 | 使用场景 |
|------|------|---------|
| `docker stop` | SIGTERM SIGKILL | 正常停止让应用优雅退出 |
| `docker kill` | SIGKILL | 应用无响应强制终止 |
#### 发送自定义信号
```bash
## 发送 SIGHUP让进程重新加载配置
$ docker kill -s HUP mycontainer
## 发送 SIGTERM
$ docker kill -s TERM mycontainer
```
---
### 5.3.4 容器自动终止
容器的生命周期与主进程绑定主进程退出时容器自动停止
```bash
## 主进程是交互式 bash
$ docker run -it ubuntu bash
root@abc123:/# exit # 退出 bash → 容器停止
## 主进程执行完毕
$ docker run ubuntu echo "Hello" # echo 执行完 → 容器停止
```
---
### 5.3.5 查看已停止的容器
```bash
$ docker ps -a
CONTAINER ID IMAGE COMMAND STATUS NAMES
ba267838cc1b ubuntu "/bin/bash" Exited (0) 2 minutes ago myubuntu
c5d3a5e8f7b2 nginx "nginx" Up 5 minutes mynginx
```
**STATUS 字段说明**
| 状态 | 说明 |
|------|------|
| `Up X minutes` | 运行中 |
| `Exited (0)` | 正常退出 (退出码 0)|
| `Exited (1)` | 异常退出 (非零退出码)|
| `Exited (137)` | SIGKILL 终止 (128 + 9)|
| `Exited (143)` | SIGTERM 终止 (128 + 15)|
---
### 5.3.6 重新启动容器
#### 启动已停止的容器
```bash
$ docker start 容器名或ID
## 启动并附加终端
$ docker start -ai 容器名
```
#### 重启运行中的容器
```bash
## 先停止再启动
$ docker restart 容器名
## 自定义停止超时
$ docker restart -t 30 容器名
```
---
### 5.3.7 生命周期状态图
```mermaid
stateDiagram-v2
direction TB
[*] --> Created : docker create
Created --> Running : docker start
Running --> Stopped : docker stop
Running --> Paused : docker pause
Paused --> Running : docker unpause
Created --> Deleted : docker rm
Stopped --> Deleted : docker rm
Paused --> Deleted : docker rm
Deleted --> [*]
```
---
### 5.3.8 批量操作
#### 停止所有容器
```bash
$ docker stop $(docker ps -q)
```
#### 删除所有已停止的容器
```bash
$ docker container prune
```
#### 停止并删除所有容器
```bash
$ docker stop $(docker ps -q) && docker container prune -f
```
---
### 5.3.9 常见问题
#### Q容器停止很慢
原因应用没有正确处理 SIGTERM 信号需要等待超时后强制终止
解决方案
1. 在应用中正确处理 SIGTERM
2. 使用 `docker stop -t 0` 立即终止
3. 检查 Dockerfile 中的 `STOPSIGNAL` 配置
#### Q如何让容器优雅退出
确保容器主进程正确处理信号
```dockerfile
## Dockerfile 示例
FROM node:22
...
## 使用 exec 形式确保信号能传递给 node 进程
CMD ["node", "server.js"]
```
#### Q容器无法停止
```bash
## 强制终止
$ docker kill 容器名
## 如果仍无法停止,检查系统资源
$ docker inspect 容器名
```
---

View File

@@ -0,0 +1,282 @@
## 5.4 进入容器
### 5.4.1 为什么需要进入容器
使用 `-d` 参数启动容器后容器在后台运行以下场景需要进入容器内部操作
| 场景 | 示例 |
|------|------|
| **调试问题** | 查看日志检查配置排查错误 |
| **临时操作** | 执行数据库迁移清理缓存 |
| **检查状态** | 查看进程网络连接文件系统 |
| **开发测试** | 交互式测试命令验证环境 |
### 5.4.2 两种进入方式
Docker 提供两种进入容器的命令
| 命令 | 推荐程度 | 特点 |
|------|---------|------|
| `docker exec` | **推荐** | 启动新进程退出不影响容器 |
| `docker attach` | 谨慎使用 | 附加到主进程退出可能停止容器 |
---
### 5.4.3 docker exec (推荐)
#### docker exec 基本用法
```bash
## 进入容器并启动交互式 shell
$ docker exec -it 容器名 /bin/bash
## 或使用 sh适用于 Alpine 等精简镜像)
$ docker exec -it 容器名 /bin/sh
```
#### 参数说明
| 参数 | 作用 |
|------|------|
| `-i` | 保持标准输入打开 (interactive)|
| `-t` | 分配伪终端 (TTY)|
| `-it` | 两者组合获得完整交互体验 |
| `-u` | 指定用户 ( `-u root`)|
| `-w` | 指定工作目录 |
| `-e` | 设置环境变量 |
#### docker exec 示例
```bash
## 启动一个后台容器
$ docker run -dit --name myubuntu ubuntu
69d137adef7a...
## 进入容器(交互式 shell
$ docker exec -it myubuntu bash
root@69d137adef7a:/# ls
bin boot dev etc home lib ...
root@69d137adef7a:/# exit
## 容器仍在运行!
$ docker ps
CONTAINER ID IMAGE STATUS NAMES
69d137adef7a ubuntu Up 2 minutes myubuntu
```
#### 执行单条命令
不进入交互模式直接执行命令
```bash
## 查看容器内进程
$ docker exec myubuntu ps aux
## 查看配置文件
$ docker exec myubuntu cat /etc/nginx/nginx.conf
## 以 root 用户执行
$ docker exec -u root myubuntu apt update
```
#### 只用 -i 不用 -t 的区别
```bash
## 只用 -i可以执行命令但没有提示符
$ docker exec -i myubuntu bash
ls # 输入命令
bin # 输出结果
boot
dev
...
## 用 -it有完整的终端体验
$ docker exec -it myubuntu bash
root@69d137adef7a:/# # 有提示符
```
> 💡 通常使用 `-it` 组合只有在脚本中需要通过管道传入命令时才只用 `-i`
---
### 5.4.4 docker attach (谨慎使用)
#### docker attach 基本用法
```bash
$ docker attach 容器名
```
#### 工作原理
`attach` 会附加到容器的 **主进程** (PID 1) 的标准输入输出
```mermaid
flowchart LR
subgraph Container ["容器"]
direction TB
subgraph Process ["主进程"]
P1["PID 1: /bin/bash<br>(你的输入直接发送到主进程)"]
end
end
Attach["docker attach"] -->|"附加到这里"| P1
```
#### docker attach 示例
```bash
## 启动容器
$ docker run -dit --name myubuntu ubuntu
243c32535da7...
## 附加到容器
$ docker attach myubuntu
root@243c32535da7:/#
```
#### 重要警告
** attach 会话中输入 `exit` 或按 `Ctrl+D` 会导致容器停止**
```bash
$ docker attach myubuntu
root@243c32535da7:/# exit # 这会停止容器!
$ docker ps
CONTAINER ID IMAGE STATUS NAMES
243c32535da7 ubuntu Exited (0) 2 seconds ago myubuntu
```
**原因**attach 附加到主进程退出主进程就等于退出容器
#### 安全退出 attach
使用 `Ctrl+P` 然后 `Ctrl+Q` 可以从 attach 会话中 **分离**而不停止容器
```bash
$ docker attach myubuntu
root@243c32535da7:/#
## 按 Ctrl+P 然后 Ctrl+Q
read escape sequence
$ docker ps # 容器仍在运行
CONTAINER ID IMAGE STATUS NAMES
243c32535da7 ubuntu Up 5 minutes myubuntu
```
---
### 5.4.5 exec vs attach 对比
| 特性 | docker exec | docker attach |
|------|-------------|---------------|
| **工作方式** | 在容器内启动新进程 | 附加到主进程 |
| **退出影响** | 不影响容器 | 可能停止容器 |
| **多终端** | 可以开多个 | 共享同一个会话 |
| **适用场景** | 调试临时操作 | 查看主进程输出 |
| **推荐程度** | 推荐 | 特殊场景使用 |
```mermaid
flowchart LR
subgraph Exec ["docker exec"]
direction TB
subgraph Container1 ["容器"]
E_PID1["PID 1: nginx"]
E_PID50["PID 50: bash"]
end
NewProc["新进程"] -- 附加到 --> E_PID50
end
subgraph Attach ["docker attach"]
direction TB
subgraph Container2 ["容器"]
A_PID1["PID 1: bash"]
end
MainProc["附加到主进程"] --> A_PID1
end
note1["退出 bash 不影响 nginx"]
note2["退出 bash 容器停止"]
Container1 -.-> note1
Container2 -.-> note2
```
---
### 5.4.6 最佳实践
#### 1. 首选 docker exec
```bash
## 进入容器调试
$ docker exec -it myapp bash
## 查看日志
$ docker exec myapp tail -f /var/log/app.log
## 执行数据库迁移
$ docker exec myapp python manage.py migrate
```
#### 2. 生产环境避免进入容器
笔者建议生产环境应尽量避免进入容器直接操作而是通过
- 日志系统查看日志 ( `docker logs` 或集中式日志)
- 监控系统查看状态
- 重新部署而非手动修改
#### 3. shell 镜像的处理
某些精简镜像 (如基于 `scratch` `distroless`) 没有 shell
```bash
## 这会失败
$ docker exec -it myapp bash
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found
## 解决方案使用调试容器Docker Desktop 或 Kubernetes debug
$ docker debug myapp
```
---
### 5.4.7 常见问题
#### Qexec 进入后看不到其他终端的操作
这是正常的exec 启动的是独立进程多个 exec 会话互不影响
#### Q容器没有 bash
尝试使用 sh
```bash
$ docker exec -it myapp /bin/sh
```
#### Q需要 root 权限
```bash
$ docker exec -u root -it myapp bash
```
---

View File

@@ -1,6 +1,8 @@
# 导出和导入容器
## 5.5 导出和导入
## 导出容器
当我们需要迁移容器或者备份容器时可以使用 Docker 的导入和导出功能本节将介绍这两个命令的使用方法
### 5.5.1 导出容器
如果要导出本地某个容器可以使用 `docker export` 命令
```bash
@@ -12,7 +14,7 @@ $ docker export 7691a814370e > ubuntu.tar
这样将导出容器快照到本地文件
## 导入容器快照
### 5.5.2 导入容器快照
可以使用 `docker import` 从容器快照文件中再导入为镜像例如
@@ -29,4 +31,4 @@ test/ubuntu v1.0 9d37a6082e97 About a minute ago
$ docker import http://example.com/exampleimage.tgz example/imagerepo
```
*用户既可以使用 `docker load` 来导入镜像存储文件到本地镜像库也可以使用 `docker import` 来导入一个容器快照到本地镜像库这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息即仅保存容器当时的快照状态而镜像存储文件将保存完整记录体积也要大此外从容器快照文件导入时可以重新指定标签等元数据信息*
*用户既可以使用 `docker load` 来导入镜像存储文件到本地镜像库也可以使用 `docker import` 来导入一个容器快照到本地镜像库这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息 (即仅保存容器当时的快照状态)而镜像存储文件将保存完整记录体积也要大此外从容器快照文件导入时可以重新指定标签等元数据信息*

View File

@@ -1,6 +1,8 @@
# 删除容器
## 5.6 删除
## 基本用法
随着容器的创建和停止系统中会积累大量的容器本节将介绍如何删除不再需要的容器以及如何清理所有停止的容器
### 5.6.1 基本用法
使用 `docker rm` 删除已停止的容器
@@ -12,7 +14,7 @@ $ docker rm 容器名或ID
---
## 删除选项
### 5.6.2 删除选项
| 选项 | 说明 | 示例 |
|------|------|------|
@@ -20,31 +22,34 @@ $ docker rm 容器名或ID
| `-f` | 强制删除运行中的容器 | `docker rm -f mycontainer` |
| `-v` | 同时删除关联的匿名卷 | `docker rm -v mycontainer` |
### 删除已停止的容器
#### 删除已停止的容器
```bash
$ docker rm mycontainer
mycontainer
```
### 强制删除运行中的容器
#### 强制删除运行中的容器
```bash
# 不加 -f 会报错
## 不加 -f 会报错
$ docker rm running_container
Error: cannot remove running container
# -f 强制删除
## -f 强制删除
$ docker rm -f running_container
running_container
```
> 强制删除会向容器发送 SIGKILL 信号可能导致数据丢失建议先 `docker stop` 优雅停止
### 删除容器及其数据卷
#### 删除容器及其数据卷
```bash
# 删除容器时同时删除其匿名卷
## 删除容器时同时删除其匿名卷
$ docker rm -v mycontainer
```
@@ -52,12 +57,13 @@ $ docker rm -v mycontainer
---
## 批量删除
### 5.6.3 批量删除
### 删除所有已停止的容器
#### 删除所有已停止的容器
```bash
# 方式一使用 prune 命令推荐
## 方式一使用 prune 命令推荐
$ docker container prune
WARNING! This will remove all stopped containers.
@@ -67,37 +73,43 @@ abc123...
def456...
Total reclaimed space: 150MB
# 方式二不提示确认
## 方式二不提示确认
$ docker container prune -f
```
### 删除所有容器包括运行中的
#### 删除所有容器 (包括运行中的)
```bash
# 先停止所有容器再删除
## 先停止所有容器再删除
$ docker stop $(docker ps -q)
$ docker rm $(docker ps -aq)
# 或者直接强制删除
## 或者直接强制删除
$ docker rm -f $(docker ps -aq)
```
### 按条件删除
#### 按条件删除
```bash
# 删除所有已退出的容器
## 删除所有已退出的容器
$ docker rm $(docker ps -aq -f status=exited)
# 删除名称包含 "test" 的容器
## 删除名称包含 "test" 的容器
$ docker rm $(docker ps -aq -f name=test)
# 删除 24 小时前创建的容器
## 删除 24 小时前创建的容器
$ docker container prune --filter "until=24h"
```
---
## 常用过滤条件
### 5.6.4 常用过滤条件
`docker ps` 的过滤条件可以配合 `rm` 使用
@@ -110,62 +122,70 @@ $ docker container prune --filter "until=24h"
| `before=xxx` | 在某容器之前创建 | `-f before=mycontainer` |
| `since=xxx` | 在某容器之后创建 | `-f since=mycontainer` |
### 示例
#### 示例
```bash
# 删除所有基于 nginx 镜像的容器
## 删除所有基于 nginx 镜像的容器
$ docker rm $(docker ps -aq -f ancestor=nginx)
# 删除所有创建后未启动的容器
## 删除所有创建后未启动的容器
$ docker rm $(docker ps -aq -f status=created)
```
---
## 容器与镜像的依赖关系
### 5.6.5 容器与镜像的依赖关系
> 有容器依赖的镜像无法删除
```bash
# 尝试删除有容器依赖的镜像
## 尝试删除有容器依赖的镜像
$ docker image rm nginx
Error: image is being used by stopped container abc123
# 需要先删除依赖该镜像的容器
## 需要先删除依赖该镜像的容器
$ docker rm abc123
$ docker image rm nginx
```
---
## 清理策略建议
### 5.6.6 清理策略建议
### 开发环境
#### 开发环境
```bash
# 定期清理已停止的容器
## 定期清理已停止的容器
$ docker container prune -f
# 一键清理所有未使用资源
## 一键清理所有未使用资源
$ docker system prune -f
```
### 生产环境
#### 生产环境
```bash
# 使用 --rm 参数运行临时容器
$ docker run --rm ubuntu echo "Hello"
# 容器退出后自动删除
## 使用 --rm 参数运行临时容器
$ docker run --rm ubuntu echo "Hello"
## 容器退出后自动删除
## 定期清理设置保留时间
# 定期清理设置保留时间
$ docker container prune --filter "until=168h" # 保留 7 天内的
```
### 完整清理脚本
#### 完整清理脚本
```bash
#!/bin/bash
# cleanup.sh - Docker 资源清理脚本
## cleanup.sh - Docker 资源清理脚本
echo "清理已停止的容器..."
docker container prune -f
@@ -185,9 +205,9 @@ docker system df
---
## 常见问题
### 5.6.7 常见问题
### Q: 容器无法删除
#### Q容器无法删除
```bash
Error: container is running
@@ -198,41 +218,29 @@ Error: container is running
```bash
$ docker stop mycontainer
$ docker rm mycontainer
#
##
$ docker rm -f mycontainer
```
### Q: 删除后磁盘空间没释放
#### Q删除后磁盘空间没释放
可能原因
1. 容器的数据卷未删除使用 `-v` 参数
1. 容器的数据卷未删除 (使用 `-v` 参数)
2. 镜像未删除
3. 构建缓存未清理
解决
```bash
# 查看空间占用
## 查看空间占用
$ docker system df
# 完整清理
## 完整清理
$ docker system prune -a --volumes
```
---
## 本章小结
| 操作 | 命令 |
|------|------|
| 删除已停止容器 | `docker rm 容器名` |
| 强制删除运行中容器 | `docker rm -f 容器名` |
| 删除容器及匿名卷 | `docker rm -v 容器名` |
| 清理所有已停止容器 | `docker container prune` |
| 删除所有容器 | `docker rm -f $(docker ps -aq)` |
## 延伸阅读
- [终止容器](stop.md)优雅停止容器
- [删除镜像](../04_image/rm.md)清理镜像
- [数据卷](../07_data_network/data/volume.md)数据卷管理

View File

@@ -1,7 +1,14 @@
# 操作 Docker 容器
# 第五章 操作容器
容器是 Docker 又一核心概念
简单的说容器是独立运行的一个或一组应用以及它们的运行态环境对应的虚拟机可以理解为模拟运行的一整套操作系统提供了运行态环境和其他系统环境和跑在上面的应用
简单的说容器是独立运行的一个或一组应用以及它们的运行态环境对应的虚拟机可以理解为模拟运行的一整套操作系统 (提供了运行态环境和其他系统环境) 和跑在上面的应用
本章将具体介绍如何来管理一个容器包括创建启动和停止等
* [启动容器](5.1_run.md)
* [守护态运行](5.2_daemon.md)
* [终止容器](5.3_stop.md)
* [进入容器](5.4_attach_exec.md)
* [导出和导入容器](5.5_import_export.md)
* [删除容器](5.6_rm.md)

View File

@@ -1,264 +0,0 @@
# 进入容器
## 为什么需要进入容器
使用 `-d` 参数启动容器后容器在后台运行以下场景需要进入容器内部操作
| 场景 | 示例 |
|------|------|
| **调试问题** | 查看日志检查配置排查错误 |
| **临时操作** | 执行数据库迁移清理缓存 |
| **检查状态** | 查看进程网络连接文件系统 |
| **开发测试** | 交互式测试命令验证环境 |
## 两种进入方式
Docker 提供两种进入容器的命令
| 命令 | 推荐程度 | 特点 |
|------|---------|------|
| `docker exec` | **推荐** | 启动新进程退出不影响容器 |
| `docker attach` | 谨慎使用 | 附加到主进程退出可能停止容器 |
---
## docker exec推荐
### 基本用法
```bash
# 进入容器并启动交互式 shell
$ docker exec -it 容器名 /bin/bash
# 或使用 sh适用于 Alpine 等精简镜像)
$ docker exec -it 容器名 /bin/sh
```
### 参数说明
| 参数 | 作用 |
|------|------|
| `-i` | 保持标准输入打开interactive |
| `-t` | 分配伪终端TTY |
| `-it` | 两者组合获得完整交互体验 |
| `-u` | 指定用户 `-u root` |
| `-w` | 指定工作目录 |
| `-e` | 设置环境变量 |
### 示例
```bash
# 启动一个后台容器
$ docker run -dit --name myubuntu ubuntu
69d137adef7a...
# 进入容器(交互式 shell
$ docker exec -it myubuntu bash
root@69d137adef7a:/# ls
bin boot dev etc home lib ...
root@69d137adef7a:/# exit
# 容器仍在运行!
$ docker ps
CONTAINER ID IMAGE STATUS NAMES
69d137adef7a ubuntu Up 2 minutes myubuntu
```
### 执行单条命令
不进入交互模式直接执行命令
```bash
# 查看容器内进程
$ docker exec myubuntu ps aux
# 查看配置文件
$ docker exec myubuntu cat /etc/nginx/nginx.conf
# 以 root 用户执行
$ docker exec -u root myubuntu apt update
```
### 只用 -i 不用 -t 的区别
```bash
# 只用 -i可以执行命令但没有提示符
$ docker exec -i myubuntu bash
ls # 输入命令
bin # 输出结果
boot
dev
...
# 用 -it有完整的终端体验
$ docker exec -it myubuntu bash
root@69d137adef7a:/# # 有提示符
```
> 💡 通常使用 `-it` 组合只有在脚本中需要通过管道传入命令时才只用 `-i`
---
## docker attach谨慎使用
### 基本用法
```bash
$ docker attach 容器名
```
### 工作原理
`attach` 会附加到容器的**主进程**PID 1的标准输入输出
```
┌─────────────────────────────────────────┐
│ 容器 │
│ ┌─────────────────────────────────┐ │
│ │ PID 1: /bin/bash (主进程) │◄───┼─── docker attach 附加到这里
│ │ └─ 你的输入直接发送到主进程 │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
### 示例
```bash
# 启动容器
$ docker run -dit --name myubuntu ubuntu
243c32535da7...
# 附加到容器
$ docker attach myubuntu
root@243c32535da7:/#
```
### 重要警告
** attach 会话中输入 `exit` 或按 `Ctrl+D` 会导致容器停止**
```bash
$ docker attach myubuntu
root@243c32535da7:/# exit # 这会停止容器!
$ docker ps
CONTAINER ID IMAGE STATUS NAMES
243c32535da7 ubuntu Exited (0) 2 seconds ago myubuntu
```
**原因**attach 附加到主进程退出主进程就等于退出容器
### 安全退出 attach
使用 `Ctrl+P` 然后 `Ctrl+Q` 可以从 attach 会话中**分离**而不停止容器
```bash
$ docker attach myubuntu
root@243c32535da7:/#
# 按 Ctrl+P 然后 Ctrl+Q
read escape sequence
$ docker ps # 容器仍在运行
CONTAINER ID IMAGE STATUS NAMES
243c32535da7 ubuntu Up 5 minutes myubuntu
```
---
## exec vs attach 对比
| 特性 | docker exec | docker attach |
|------|-------------|---------------|
| **工作方式** | 在容器内启动新进程 | 附加到主进程 |
| **退出影响** | 不影响容器 | 可能停止容器 |
| **多终端** | 可以开多个 | 共享同一个会话 |
| **适用场景** | 调试临时操作 | 查看主进程输出 |
| **推荐程度** | 推荐 | 特殊场景使用 |
```
docker exec docker attach
┌─────────────────────┐ ┌─────────────────────┐
│ 容器 │ │ 容器 │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ PID 1: nginx │ │ │ │ PID 1: bash │◄─┼── 附加到主进程
│ ├───────────────┤ │ │ └───────────────┘ │
│ │ PID 50: bash │◄─┼── 新进程 │ │
│ └───────────────┘ │ │ │
└─────────────────────┘ └─────────────────────┘
退出 bash 不影响 nginx 退出 bash 容器停止
```
---
## 最佳实践
### 1. 首选 docker exec
```bash
# 进入容器调试
$ docker exec -it myapp bash
# 查看日志
$ docker exec myapp tail -f /var/log/app.log
# 执行数据库迁移
$ docker exec myapp python manage.py migrate
```
### 2. 生产环境避免进入容器
笔者建议生产环境应尽量避免进入容器直接操作而是通过
- 日志系统查看日志 `docker logs` 或集中式日志
- 监控系统查看状态
- 重新部署而非手动修改
### 3. shell 镜像的处理
某些精简镜像如基于 `scratch` `distroless`没有 shell
```bash
# 这会失败
$ docker exec -it myapp bash
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found
# 解决方案使用调试容器Docker Desktop 或 Kubernetes debug
$ docker debug myapp
```
---
## 常见问题
### Q: exec 进入后看不到其他终端的操作
这是正常的exec 启动的是独立进程多个 exec 会话互不影响
### Q: 容器没有 bash
尝试使用 sh
```bash
$ docker exec -it myapp /bin/sh
```
### Q: 需要 root 权限
```bash
$ docker exec -u root -it myapp bash
```
---
## 本章小结
| 需求 | 推荐命令 |
|------|---------|
| 进入容器调试 | `docker exec -it 容器名 bash` |
| 执行单条命令 | `docker exec 容器名 命令` |
| 查看主进程输出 | `docker attach 容器名`慎用 |
## 延伸阅读
- [后台运行](daemon.md)理解容器主进程
- [查看容器](ls.md)列出和过滤容器
- [容器日志](logs.md)查看容器输出

View File

@@ -1,229 +0,0 @@
# 启动容器
## 启动方式概述
启动容器有两种方式
- **新建并启动**基于镜像创建新容器
- **重新启动**将已终止的容器重新运行
由于 Docker 容器非常轻量实际使用中常常是随时删除和新建容器而不是反复重启同一个容器
## 新建并启动
### 基本语法
```bash
docker run [选项] 镜像 [命令] [参数...]
```
### 最简单的例子
输出 "Hello World" 后容器自动终止
```bash
$ docker run ubuntu:24.04 /bin/echo 'Hello world'
Hello world
```
这与直接执行 `/bin/echo 'Hello world'` 几乎没有区别但实际上已经启动了一个完整的 Ubuntu 容器来执行这条命令
### 交互式容器
启动一个可以交互的 bash 终端
```bash
$ docker run -it ubuntu:24.04 /bin/bash
root@af8bae53bdd3:/#
```
**参数说明**
| 参数 | 作用 |
|------|------|
| `-i` | 保持标准输入stdin打开允许输入 |
| `-t` | 分配伪终端pseudo-TTY提供终端界面 |
| `-it` | 两者组合使用获得交互式终端 |
在交互模式下可以执行命令
```bash
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@af8bae53bdd3:/# exit # 退出容器
```
## docker run 的完整流程
执行 `docker run` Docker 在后台完成以下操作
```
┌─────────────────────────────────────────────────────────────────────┐
│ docker run ubuntu:24.04 /bin/echo "Hello" │
└───────────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 1. 检查本地是否有 ubuntu:24.04 镜像 │
│ ├── 有 → 使用本地镜像 │
│ └── 无 → 从 Registry 下载 │
├─────────────────────────────────────────────────────────────────────┤
│ 2. 创建容器 │
│ • 基于镜像的只读层 │
│ • 添加一层可读写层(容器存储层) │
├─────────────────────────────────────────────────────────────────────┤
│ 3. 配置网络 │
│ • 创建虚拟网卡 │
│ • 分配 IP 地址 │
│ • 连接到 Docker 网桥 │
├─────────────────────────────────────────────────────────────────────┤
│ 4. 启动容器,执行指定命令 │
├─────────────────────────────────────────────────────────────────────┤
│ 5. 命令执行完毕,容器停止 │
└─────────────────────────────────────────────────────────────────────┘
```
## 常用启动选项
### 基础选项
| 选项 | 说明 | 示例 |
|------|------|------|
| `-d` | 后台运行detach | `docker run -d nginx` |
| `-it` | 交互式终端 | `docker run -it ubuntu bash` |
| `--name` | 指定容器名称 | `docker run --name myapp nginx` |
| `--rm` | 退出后自动删除容器 | `docker run --rm ubuntu echo hi` |
### 端口映射
```bash
# 将容器的 80 端口映射到宿主机的 8080 端口
$ docker run -d -p 8080:80 nginx
# 随机映射端口
$ docker run -d -P nginx
# 只绑定到 localhost
$ docker run -d -p 127.0.0.1:8080:80 nginx
```
### 数据卷挂载
```bash
# 挂载命名卷
$ docker run -v mydata:/data nginx
# 挂载宿主机目录
$ docker run -v /host/path:/container/path nginx
# 只读挂载
$ docker run -v /host/path:/container/path:ro nginx
```
### 环境变量
```bash
# 设置单个环境变量
$ docker run -e MYSQL_ROOT_PASSWORD=secret mysql
# 从文件加载环境变量
$ docker run --env-file .env myapp
```
### 资源限制
```bash
# 限制内存
$ docker run -m 512m nginx
# 限制 CPU
$ docker run --cpus=1.5 nginx
```
## 启动已终止容器
使用 `docker start` 重新启动已停止的容器
```bash
# 查看所有容器(包括已停止的)
$ docker ps -a
CONTAINER ID IMAGE STATUS NAMES
af8bae53bdd3 ubuntu Exited (0) 2 minutes ago myubuntu
# 重新启动
$ docker start myubuntu
# 启动并附加终端
$ docker start -ai myubuntu
```
## 容器内进程的特点
容器内只运行指定的应用程序及其必需资源
```bash
root@ba267838cc1b:/# ps
PID TTY TIME CMD
1 ? 00:00:00 bash
11 ? 00:00:00 ps
```
可见容器中仅运行了 `bash` 进程这种特点使得 Docker 对资源的利用率极高
> 💡 笔者提示容器内的 PID 1 进程很重要它是容器的主进程该进程退出则容器停止详见[后台运行](daemon.md)章节
## 常见问题
### Q: 容器启动后立即退出
**原因**主进程执行完毕或无法保持运行
```bash
# 这个容器会立即退出echo 执行完就结束了)
$ docker run ubuntu echo "hello"
# 解决:使用能持续运行的命令
$ docker run -d nginx # nginx 是持续运行的服务
```
详细解释见[后台运行](daemon.md)
### Q: 无法连接容器内的服务
**原因**未正确映射端口
```bash
# 错误:没有 -p 参数,外部无法访问
$ docker run -d nginx
# 正确:映射端口
$ docker run -d -p 80:80 nginx
```
### Q: 容器内修改的文件丢失
**原因**未使用数据卷数据保存在容器存储层
```bash
# 使用数据卷持久化
$ docker run -v mydata:/app/data myapp
```
详见[数据管理](../07_data_network/README.md)
## 本章小结
| 操作 | 命令 | 说明 |
|------|------|------|
| 新建并运行 | `docker run` | 最常用的启动方式 |
| 交互式启动 | `docker run -it` | 用于调试或临时操作 |
| 后台运行 | `docker run -d` | 用于服务类应用 |
| 启动已停止的容器 | `docker start` | 重用已有容器 |
## 延伸阅读
- [后台运行](daemon.md)理解 `-d` 参数和容器生命周期
- [进入容器](attach_exec.md)操作运行中的容器
- [网络配置](../network/README.md)理解端口映射的原理
- [数据管理](../07_data_network/README.md)数据持久化方案

View File

@@ -1,256 +0,0 @@
# 终止容器
## 终止方式概述
终止容器有三种方式
| 方式 | 命令 | 说明 |
|------|------|------|
| **优雅停止** | `docker stop` | 先发 SIGTERM超时后发 SIGKILL |
| **强制停止** | `docker kill` | 直接发 SIGKILL |
| **自动终止** | - | 容器主进程退出时自动停止 |
---
## docker stop推荐
### 基本用法
```bash
$ docker stop 容器名或ID
```
### 工作原理
```
docker stop mycontainer
┌─────────────────────────────────────────────────────────────────┐
│ 1. 发送 SIGTERM 信号给容器主进程PID 1
│ ↓ │
│ 2. 等待容器优雅退出(默认 10 秒) │
│ ↓ │
│ 3. 如果超时仍未退出,发送 SIGKILL 强制终止 │
└─────────────────────────────────────────────────────────────────┘
```
### 自定义超时时间
```bash
# 等待 30 秒后强制终止
$ docker stop -t 30 mycontainer
# 立即发送 SIGKILL相当于 docker kill
$ docker stop -t 0 mycontainer
```
### 停止多个容器
```bash
# 停止多个指定容器
$ docker stop container1 container2 container3
# 停止所有运行中的容器
$ docker stop $(docker ps -q)
```
---
## docker kill
### 基本用法
```bash
$ docker kill 容器名或ID
```
### stop 的区别
| 命令 | 信号 | 使用场景 |
|------|------|---------|
| `docker stop` | SIGTERM SIGKILL | 正常停止让应用优雅退出 |
| `docker kill` | SIGKILL | 应用无响应强制终止 |
### 发送自定义信号
```bash
# 发送 SIGHUP让进程重新加载配置
$ docker kill -s HUP mycontainer
# 发送 SIGTERM
$ docker kill -s TERM mycontainer
```
---
## 容器自动终止
容器的生命周期与主进程绑定主进程退出时容器自动停止
```bash
# 主进程是交互式 bash
$ docker run -it ubuntu bash
root@abc123:/# exit # 退出 bash → 容器停止
# 主进程执行完毕
$ docker run ubuntu echo "Hello" # echo 执行完 → 容器停止
```
---
## 查看已停止的容器
```bash
$ docker ps -a
CONTAINER ID IMAGE COMMAND STATUS NAMES
ba267838cc1b ubuntu "/bin/bash" Exited (0) 2 minutes ago myubuntu
c5d3a5e8f7b2 nginx "nginx" Up 5 minutes mynginx
```
**STATUS 字段说明**
| 状态 | 说明 |
|------|------|
| `Up X minutes` | 运行中 |
| `Exited (0)` | 正常退出退出码 0 |
| `Exited (1)` | 异常退出非零退出码 |
| `Exited (137)` | SIGKILL 终止128 + 9 |
| `Exited (143)` | SIGTERM 终止128 + 15 |
---
## 重新启动容器
### 启动已停止的容器
```bash
$ docker start 容器名或ID
# 启动并附加终端
$ docker start -ai 容器名
```
### 重启运行中的容器
```bash
# 先停止再启动
$ docker restart 容器名
# 自定义停止超时
$ docker restart -t 30 容器名
```
---
## 生命周期状态图
```
docker create
┌──────────┐
┌────────│ Created │────────┐
│ └──────────┘ │
│ │ │
│ │ docker start│
│ ▼ │
│ ┌──────────┐ │
│ ┌────│ Running │────┐ │
│ │ └──────────┘ │ │
│ │ │ │ │
│ │ docker │ docker │ │
│ │ pause │ stop │ │
│ ▼ │ │ │
│ ┌──────┐ │ │ │
│ │Paused│ │ │ │
│ └──────┘ │ │ │
│ │ │ │ │
│ │ docker │ │ │
│ │ unpause │ │ │
│ ▼ ▼ │ │
│ └──────►┌──────────┐◄┘ │
│ │ Stopped │ │
│ └──────────┘ │
│ │ │
│ │ docker rm │
│ ▼ │
│ ┌──────────┐ │
└──────────►│ Deleted │◄────┘
└──────────┘
```
---
## 批量操作
### 停止所有容器
```bash
$ docker stop $(docker ps -q)
```
### 删除所有已停止的容器
```bash
$ docker container prune
```
### 停止并删除所有容器
```bash
$ docker stop $(docker ps -q) && docker container prune -f
```
---
## 常见问题
### Q: 容器停止很慢
原因应用没有正确处理 SIGTERM 信号需要等待超时后强制终止
解决方案
1. 在应用中正确处理 SIGTERM
2. 使用 `docker stop -t 0` 立即终止
3. 检查 Dockerfile 中的 `STOPSIGNAL` 配置
### Q: 如何让容器优雅退出
确保容器主进程正确处理信号
```dockerfile
# Dockerfile 示例
FROM node:22
...
# 使用 exec 形式确保信号能传递给 node 进程
CMD ["node", "server.js"]
```
### Q: 容器无法停止
```bash
# 强制终止
$ docker kill 容器名
# 如果仍无法停止,检查系统资源
$ docker inspect 容器名
```
---
## 本章小结
| 操作 | 命令 | 说明 |
|------|------|------|
| 优雅停止 | `docker stop` | SIGTERM超时后 SIGKILL |
| 强制停止 | `docker kill` | 直接 SIGKILL |
| 重新启动 | `docker start` | 启动已停止的容器 |
| 重启 | `docker restart` | 停止后立即启动 |
| 停止全部 | `docker stop $(docker ps -q)` | 停止所有运行中容器 |
## 延伸阅读
- [启动容器](run.md)容器启动详解
- [删除容器](rm.md)清理容器
- [容器日志](logs.md)排查停止原因

33
05_container/summary.md Normal file
View File

@@ -0,0 +1,33 @@
## 本章小结
本章介绍了 Docker 容器的启动停止进入和删除等核心操作
| 操作 | 命令 | 说明 |
|------|------|------|
| 新建并运行 | `docker run` | 最常用的启动方式 |
| 交互式启动 | `docker run -it` | 用于调试或临时操作 |
| 后台运行 | `docker run -d` | 用于服务类应用 |
| 启动已停止的容器 | `docker start` | 重用已有容器 |
| 优雅停止 | `docker stop` | SIGTERM超时后 SIGKILL |
| 强制停止 | `docker kill` | 直接 SIGKILL |
| 重启 | `docker restart` | 停止后立即启动 |
| 停止全部 | `docker stop $(docker ps -q)` | 停止所有运行中容器 |
| 进入容器调试 | `docker exec -it 容器名 bash` | 推荐方式 |
| 执行单条命令 | `docker exec 容器名 命令` | 不进入交互模式 |
| 查看主进程输出 | `docker attach 容器名` | 慎用退出可能停止容器 |
| 删除已停止容器 | `docker rm 容器名` | 需先停止 |
| 强制删除运行中容器 | `docker rm -f 容器名` | 直接删除 |
| 删除容器及匿名卷 | `docker rm -v 容器名` | 同时清理匿名卷 |
| 清理所有已停止容器 | `docker container prune` | 批量清理 |
### 延伸阅读
- [后台运行](5.2_daemon.md)理解 `-d` 参数和容器生命周期
- [进入容器](5.4_attach_exec.md)操作运行中的容器
- [网络配置](../09_network/README.md)理解端口映射的原理
- [数据管理](../08_data/README.md)数据持久化方案
- [删除镜像](../04_image/4.3_rm.md)清理镜像
- [数据卷](../08_data/8.1_volume.md)数据卷管理
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

View File

@@ -0,0 +1,136 @@
## 6.1 Docker Hub
### 6.1.1 什么是 Docker Hub
Docker Hub Docker 的中央镜像仓库通过它您可以轻松地分享和获取 Docker 镜像
[Docker Hub](https://hub.docker.com/) 是 Docker 官方维护的公共镜像仓库,也是全球最大的容器镜像库。
它提供了
- **官方镜像** Docker 官方和软件厂商 ( NginxMySQLNode.js) 维护的高质量镜像
- **个人/组织仓库**用户可以上传自己的镜像
- **自动构建** GitHub/Bitbucket 集成 (需付费)
- **Webhooks**镜像更新时触发回调
---
### 6.1.2 核心功能
#### 1. 搜索镜像
我们可以通过 `docker search` 命令来查找官方仓库中的镜像并利用 `docker pull` 命令来将它下载到本地
除了网页搜索也可以使用命令行
```bash
$ docker search centos
NAME DESCRIPTION STARS OFFICIAL
centos The official build of CentOS. 7000+ [OK]
```
> **技巧**始终优先使用 `OFFICIAL` 标记为 `[OK]` 的镜像安全性更有保障
#### 2. 拉取镜像
```bash
$ docker pull nginx:alpine
```
#### 3. 推送镜像
需要先登录
```bash
$ docker login
## 默认情况下,不带其它参数进行 docker login 会自动走 Device Code Web Flow (浏览器认证)
## 若在非交互 CI 环境中,推荐结合 --username 与 --password-stdin 参数使用
...
```
打标签并推送
```bash
## 1. 标记镜像
$ docker tag myapp:v1 username/myapp:v1
## 2. 推送
$ docker push username/myapp:v1
```
---
### 6.1.3 限制与配额
#### 镜像拉取限制
2020 11 月起Docker Hub 对匿名和免费用户实施了拉取速率限制
| 用户类型 | 限制 |
|---------|------|
| **匿名用户** (未登录) | 6 小时 100 次请求 |
| **免费账户** (已登录) | 6 小时 200 次请求 |
| **Pro/Team 账户** | 无限制 |
#### 滥用限流
除了上述针对特定账号拉取镜像数量的 Pull Rate Limit 之外Docker Hub 对所有用户包含已认证及付费用户还实施了 **滥用保护限流 (Abuse Rate Limiting)**它是根据网络出口 IP (IPv4 IPv6 /64 子网) 计算整体请求频率阈值动态触发通常为每分钟数千级别请求
**两类的差异与排查方法**
- **Pull Rate Limit**针对拉取量达到上限报错返回 `429 Too Many Requests`并且 HTTP 返回体/CLI 错误提示中会带有明确的 `toomanyrequests: You have reached your pull rate limit` 提示常附有账户升级链接
- **Abuse Rate Limit**防范接口频率打击报错仅返回简化的 `429 Too Many Requests`这一限流不分付费与否常发生在多终端共享出口 IP的企业局域网或者第三方云 CI 服务 GitHub Actions 即使你已正常配置 `docker login` 也依旧可能触发
> **提示**如果在 CI/CD 等环境遇到 429 错误建议
> 1. 先甄别具体是哪类限流普通的 pull rate limit 只要在 CI 中配置 `docker login` (并使用有效账号) 就能解除匿名限制
> 2. 如果是 Abuse 频控导致应考虑搭建私有仓库作为拉取缓存代理 (Registry pull-through cache)避免频繁直接请求官方 Hub
> 3. 使用国内镜像加速器
---
### 6.1.4 安全最佳实践
#### 1. 启用 2FA (双因素认证)
为了保护您的 Docker Hub 账号安全我们建议采取以下措施
Account Settings -> Security 中启用 2FA保护账号安全启用后CLI 登录需要使用 **Access Token** 而非密码
#### 2. 使用 Access Token
> ** 警告**绝不要在脚本或 CI/CD 系统中直接使用 `-p` 参数传递密码或 Token (类似 `docker login -p xxx`)这会导致凭证直接暴露在系统的命令历史进程列表和终端输出中
1. Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token (PAT)
2. Token 通过标准输入 (stdin) 安全传递给 Docker:
```bash
$ echo "dckr_pat_xxxxxxx" | docker login --username username --password-stdin
```
#### 3. 关注镜像漏洞
Docker Hub 会对官方镜像和付费用户的镜像进行安全扫描在镜像标签页可以看到漏洞扫描结果
---
### 6.1.5 Webhooks
当镜像被推送时可以自动触发 HTTP 回调 (例如通知 CI 系统部署)
**配置方法**
仓库页面 -> Webhooks -> Create Webhook
---
### 6.1.6 自动构建
> 目前仅限付费用户 (Pro/Team) 使用
链接 GitHub/Bitbucket 仓库后当代码有提交或打标签时Docker Hub 会自动运行构建这保证了镜像总是与代码同步且由可信的官方环境构建
---

View File

@@ -1,10 +1,10 @@
# 私有仓库高级配置
## 6.3 私有仓库高级配置
上一节我们搭建了一个具有基础功能的私有仓库本小节我们来使用 `Docker Compose` 搭建一个拥有权限认证TLS 的私有仓库
新建一个文件夹以下步骤均在该文件夹中进行
## 准备站点证书
### 6.3.1 准备站点证书
如果你拥有一个域名国内各大云服务商均提供免费的站点证书你也可以使用 `openssl` 自行签发证书
@@ -82,7 +82,7 @@ $ openssl x509 -req -days 750 -in "site.csr" -sha256 \
新建 `ssl` 文件夹并将 `docker.domain.com.key` `docker.domain.com.crt` `root-ca.crt` 这三个文件移入删除其他文件
## 配置私有仓库
### 6.3.2 配置私有仓库
私有仓库默认的配置文件位于 `/etc/docker/registry/config.yml`我们先在本地编辑 `config.yml`之后挂载到容器中
@@ -123,7 +123,7 @@ health:
threshold: 3
```
## 生成 http 认证文件
### 6.3.3 生成 http 认证文件
```bash
$ mkdir auth
@@ -136,11 +136,11 @@ $ docker run --rm \
> 将上面的 `username` `password` 替换为你自己的用户名和密码
## 编辑 `docker-compose.yml`
### 6.3.4 编辑 Docker Compose 配置
编辑 `compose.yaml` ( `docker-compose.yml`) 配置如下
```yaml
services:
registry:
image: registry
@@ -154,7 +154,7 @@ volumes:
registry-data:
```
## 修改 hosts
### 6.3.5 修改 Hosts 文件
编辑 `/etc/hosts`
@@ -162,7 +162,7 @@ volumes:
127.0.0.1 docker.domain.com
```
## 启动
### 6.3.6 启动
```bash
$ docker compose up -d
@@ -170,7 +170,7 @@ $ docker compose up -d
这样我们就搭建好了一个具有权限认证TLS 的私有仓库接下来我们测试其功能是否正常
## 测试私有仓库功能
### 6.3.7 测试私有仓库功能
由于自行签发的 CA 根证书不被系统信任所以我们需要将 CA 根证书 `ssl/root-ca.crt` 移入 `/etc/docker/certs.d/docker.domain.com` 文件夹中
@@ -212,6 +212,6 @@ no basic auth credentials
发现会提示没有登录不能将镜像推送到私有仓库中
## 注意事项
### 6.3.8 注意事项
如果你本机占用了 `443` 端口你可以配置 [Nginx 代理](https://docs.docker.com/registry/recipes/nginx/),这里不再赘述。

View File

@@ -1,7 +1,14 @@
# 访问仓库
# 第六章 访问仓库
仓库`Repository`是集中存放镜像的地方
仓库 (`Repository`) 是集中存放镜像的地方
一个容易混淆的概念是注册服务器`Registry`实际上注册服务器是管理仓库的具体服务器每个服务器上可以有多个仓库而每个仓库下面有多个镜像从这方面来说仓库可以被认为是一个具体的项目或目录例如对于仓库地址 `docker.io/ubuntu` 来说`docker.io` 是注册服务器地址`ubuntu` 是仓库名
一个容易混淆的概念是注册服务器 (`Registry`)实际上注册服务器是管理仓库的具体服务器每个服务器上可以有多个仓库而每个仓库下面有多个镜像从这方面来说仓库可以被认为是一个具体的项目或目录例如对于仓库地址 `docker.io/ubuntu` 来说`docker.io` 是注册服务器地址`ubuntu` 是仓库名
大部分时候并不需要严格区分这两者的概念
## 本章内容
* [Docker Hub](6.1_dockerhub.md)
* [私有仓库](6.2_registry.md)
* [私有仓库高级配置](6.3_registry_auth.md)
* [Nexus 3](6.4_nexus3_registry.md)

View File

@@ -1,126 +0,0 @@
# Docker Hub
## 什么是 Docker Hub
[Docker Hub](https://hub.docker.com/) 是 Docker 官方维护的公共镜像仓库,也是全球最大的容器镜像库。
它提供了
- **官方镜像** Docker 官方和软件厂商 Nginx, MySQL, Node.js维护的高质量镜像
- **个人/组织仓库**用户可以上传自己的镜像
- **自动构建** GitHub/Bitbucket 集成需付费
- **Webhooks**镜像更新时触发回调
---
## 核心功能
### 1. 搜索镜像
除了网页搜索也可以使用命令行
```bash
$ docker search centos
NAME DESCRIPTION STARS OFFICIAL
centos The official build of CentOS. 7000+ [OK]
```
> **技巧**始终优先使用 `OFFICIAL` 标记为 `[OK]` 的镜像安全性更有保障
### 2. 拉取镜像
```bash
$ docker pull nginx:alpine
```
### 3. 推送镜像
需要先登录
```bash
$ docker login
# 输入用户名和密码
```
打标签并推送
```bash
# 1. 标记镜像
$ docker tag myapp:v1 username/myapp:v1
# 2. 推送
$ docker push username/myapp:v1
```
---
## 限制与配额重要
### 镜像拉取限制 (Rate Limiting)
2020 11 月起Docker Hub 对匿名和免费用户实施了拉取速率限制
| 用户类型 | 限制 |
|---------|------|
| **匿名用户** (未登录) | 6 小时 100 次请求 |
| **免费账户** (已登录) | 6 小时 200 次请求 |
| **Pro/Team 账户** | 无限制 |
> **提示**如果在 CI/CD 环境中遇到 `toomanyrequests` 错误建议
> 1. CI 中配置 `docker login`
> 2. 使用国内镜像加速器
> 3. 搭建私有仓库代理
---
## 安全最佳实践
### 1. 启用 2FA (双因素认证)
Account Settings -> Security 中启用 2FA保护账号安全启用后CLI 登录需要使用 **Access Token** 而非密码
### 2. 使用 Access Token
不要在脚本或 CI/CD 中直接使用登录密码
1. Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token
2. 使用 Token 作为密码登录
```bash
$ docker login -u username -p dckr_pat_xxxxxxx
```
### 3. 关注镜像漏洞
Docker Hub 会对官方镜像和付费用户的镜像进行安全扫描在镜像标签页可以看到漏洞扫描结果
---
## Webhooks
当镜像被推送时可以自动触发 HTTP 回调例如通知 CI 系统部署
**配置方法**
仓库页面 -> Webhooks -> Create Webhook
---
## 自动构建 (Automated Builds)
> 目前仅限付费用户 (Pro/Team) 使用
链接 GitHub/Bitbucket 仓库后当代码有提交或打标签时Docker Hub 会自动运行构建这保证了镜像总是与代码同步且由可信的官方环境构建
---
## 本章小结
| 功能 | 说明 |
|------|------|
| **官方镜像** | 优先使用的基础镜像 |
| **拉取限制** | 匿名 100/6h登录 200/6h |
| **安全** | 推荐开启 2FA 并使用 Access Token |
| **自动化** | 支持 Webhooks 和自动构建 |
## 延伸阅读
- [私有仓库](registry.md)搭建自己的 Registry
- [镜像加速器](../install/mirror.md)加速下载

18
06_repository/summary.md Normal file
View File

@@ -0,0 +1,18 @@
## 本章小结
本章介绍了 Docker Hub 的使用私有仓库的搭建以及 Nexus 3 等企业级方案
| 功能 | 说明 |
|------|------|
| **官方镜像** | 优先使用的基础镜像 |
| **拉取限制** | 匿名 100 /6h登录 200 /6h |
| **安全** | 推荐开启 2FA 并使用 Access Token |
| **自动化** | 支持 Webhooks 和自动构建 |
### 延伸阅读
- [私有仓库](6.2_registry.md)搭建自己的 Registry
- [镜像加速器](../03_install/3.9_mirror.md)加速下载
---
> 📝 **发现错误或有改进建议** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。

View File

@@ -1,6 +0,0 @@
# 数据与网络管理
本章将介绍 Docker 中的数据管理与网络配置
* [数据管理](data/README.md)
* [网络配置](network/README.md)

View File

@@ -1,8 +0,0 @@
# 数据管理
![](../_images/types-of-mounts.png)
这一章介绍如何在 Docker 内部以及容器之间管理数据在容器中管理数据主要有两种方式
* 数据卷Volumes
* 挂载主机目录 (Bind mounts)

View File

@@ -1,284 +0,0 @@
# 挂载主机目录Bind Mounts
## 什么是 Bind Mount
Bind Mount绑定挂载**宿主机的目录或文件**直接挂载到容器中容器可以读写宿主机的文件系统
```
宿主机 容器
┌─────────────────────┐ ┌─────────────────────┐
│ /home/user/code/ │ │ │
│ ├── index.html │◄───────►│ /usr/share/nginx/ │
│ ├── style.css │ Bind │ html/ │
│ └── app.js │ Mount │ (同一份文件) │
└─────────────────────┘ └─────────────────────┘
```
---
## Bind Mount vs Volume
| 特性 | Bind Mount | Volume |
|------|------------|--------|
| **数据位置** | 宿主机任意路径 | Docker 管理的目录 |
| **路径指定** | 必须是绝对路径 | 卷名 |
| **可移植性** | 依赖宿主机路径 | 更好Docker 管理 |
| **性能** | 依赖宿主机文件系统 | 优化的存储驱动 |
| **适用场景** | 开发环境配置文件 | 生产数据持久化 |
| **备份** | 直接访问文件 | 需要通过 Docker |
### 选择建议
```
需求 推荐方案
─────────────────────────────────────────
开发时同步代码 → Bind Mount
持久化数据库数据 → Volume
共享配置文件 → Bind Mount
容器间共享数据 → Volume
备份方便 → Bind Mount直接访问
生产环境 → Volume
```
---
## 基本语法
### 使用 --mount推荐
```bash
$ docker run -d \
--mount type=bind,source=/宿主机路径,target=/容器路径 \
nginx
```
### 使用 -v简写
```bash
$ docker run -d \
-v /宿主机路径:/容器路径 \
nginx
```
### 两种语法对比
| 特性 | --mount | -v |
|------|---------|-----|
| 语法 | 键值对更清晰 | 冒号分隔更简洁 |
| 路径不存在时 | 报错 | 自动创建目录 |
| 推荐程度 | 推荐 | 常用 |
---
## 使用场景
### 场景一开发环境代码同步
```bash
# 将本地代码目录挂载到容器
$ docker run -d \
-p 8080:80 \
--mount type=bind,source=$(pwd)/src,target=/usr/share/nginx/html \
nginx
# 修改本地文件,容器内立即生效(热更新)
$ echo "Hello" > src/index.html
# 浏览器刷新即可看到变化
```
### 场景二配置文件挂载
```bash
# 挂载自定义 nginx 配置
$ docker run -d \
--mount type=bind,source=/path/to/nginx.conf,target=/etc/nginx/nginx.conf,readonly \
nginx
```
### 场景三日志收集
```bash
# 将容器日志输出到宿主机目录
$ docker run -d \
--mount type=bind,source=/var/log/myapp,target=/app/logs \
myapp
```
### 场景四共享 SSH 密钥
```bash
# 挂载 SSH 密钥(只读)
$ docker run --rm -it \
--mount type=bind,source=$HOME/.ssh,target=/root/.ssh,readonly \
alpine ssh user@remote
```
---
## 只读挂载
防止容器修改宿主机文件
```bash
# --mount 语法
$ docker run -d \
--mount type=bind,source=/config,target=/app/config,readonly \
myapp
# -v 语法
$ docker run -d \
-v /config:/app/config:ro \
myapp
```
容器内尝试写入会报错
```bash
$ touch /app/config/new.txt
touch: /app/config/new.txt: Read-only file system
```
---
## 挂载单个文件
```bash
# 挂载 bash 历史记录
$ docker run --rm -it \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu bash
# 挂载自定义配置文件
$ docker run -d \
--mount type=bind,source=/path/to/my.cnf,target=/etc/mysql/my.cnf \
mysql
```
> **注意**挂载单个文件时如果宿主机上的文件被编辑器替换而非原地修改容器内仍是旧文件的 inode建议重启容器或挂载目录
---
## 查看挂载信息
```bash
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
```
输出
```json
[
{
"Type": "bind",
"Source": "/home/user/code",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
```
| 字段 | 说明 |
|------|------|
| `Type` | 挂载类型bind |
| `Source` | 宿主机路径 |
| `Destination` | 容器内路径 |
| `RW` | 是否可读写 |
| `Propagation` | 挂载传播模式 |
---
## 常见问题
### Q: 路径不存在报错
```bash
$ docker run --mount type=bind,source=/not/exist,target=/app nginx
docker: Error response from daemon: invalid mount config for type "bind":
bind source path does not exist: /not/exist
```
**解决**确保源路径存在或改用 `-v`会自动创建
### Q: 权限问题
容器内用户可能无权访问挂载的文件
```bash
# 方法1确保宿主机文件权限允许容器用户访问
$ chmod -R 755 /path/to/data
# 方法2以 root 运行容器
$ docker run -u root ...
# 方法3使用相同的 UID
$ docker run -u $(id -u):$(id -g) ...
```
### Q: macOS/Windows 性能问题
Docker Desktop Bind Mount 性能较差需要跨文件系统同步
```bash
# 使用 :cached 或 :delegated 提高性能macOS
$ docker run -v /host/path:/container/path:cached myapp
```
| 选项 | 说明 |
|------|------|
| `:cached` | 宿主机权威容器读取可能延迟 |
| `:delegated` | 容器权威宿主机读取可能延迟 |
| `:consistent` | 默认完全一致最慢 |
---
## 最佳实践
### 1. 开发环境使用 Bind Mount
```bash
# 代码热更新
$ docker run -v $(pwd):/app -p 3000:3000 node npm run dev
```
### 2. 生产环境使用 Volume
```bash
# 数据持久化
$ docker run -v mysql_data:/var/lib/mysql mysql
```
### 3. 配置文件使用只读挂载
```bash
$ docker run -v /config/nginx.conf:/etc/nginx/nginx.conf:ro nginx
```
### 4. 注意路径安全
```bash
# ❌ 危险:挂载根目录或敏感目录
$ docker run -v /:/host ...
# ✅ 只挂载必要的目录
$ docker run -v /app/data:/data ...
```
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 将宿主机目录挂载到容器 |
| **语法** | `-v /宿主机:/容器` `--mount type=bind,...` |
| **只读** | 添加 `readonly` `:ro` |
| **适用场景** | 开发环境配置文件日志 |
| **vs Volume** | Bind 更灵活Volume 更适合生产 |
## 延伸阅读
- [数据卷](volume.md)Docker 管理的持久化存储
- [tmpfs 挂载](tmpfs.md)内存临时存储
- [Compose 数据管理](../compose/compose_file.md)Compose 中的挂载配置

View File

@@ -1,291 +0,0 @@
# 网络配置
## Docker 网络概述
Docker 容器需要网络来
- 与外部世界通信访问互联网被外部访问
- 容器之间相互通信
- 与宿主机通信
Docker 在安装时会自动配置网络基础设施大多数情况下开箱即用
## 默认网络架构
Docker 启动时自动创建以下网络组件
```mermaid
graph TD
subgraph Host [宿主机]
eth0[物理网卡 eth0<br>192.168.1.100]
docker0[docker0 网桥<br>172.17.0.1]
subgraph Containers
subgraph ContainerA [容器 A]
eth0_A[eth0<br>172.17.0.2]
end
subgraph ContainerB [容器 B]
eth0_B[eth0<br>172.17.0.3]
end
end
eth0 <--> docker0
docker0 <--> eth0_A
docker0 <--> eth0_B
end
Internet((互联网)) <--> eth0
```
### 核心组件
| 组件 | 说明 |
|------|------|
| **docker0** | 虚拟网桥充当交换机角色 |
| **veth pair** | 虚拟网卡对一端在容器内一端连接网桥 |
| **容器 eth0** | 容器内的网卡 |
| **IP 地址** | 自动从 172.17.0.0/16 网段分配 |
### 数据流向
```
容器 A (172.17.0.2) → docker0 → 容器 B (172.17.0.3) (容器间通信)
容器 A (172.17.0.2) → docker0 → eth0 → 互联网 (访问外网)
外部请求 → eth0 → docker0 → 容器 A (被外部访问,需端口映射)
```
---
## Docker 网络类型
查看默认网络
```bash
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
abc123... bridge bridge local
def456... host host local
ghi789... none null local
```
| 网络类型 | 说明 | 适用场景 |
|---------|------|---------|
| **bridge** | 默认类型容器连接到虚拟网桥 | 大多数单机场景 |
| **host** | 容器直接使用宿主机网络栈 | 需要最高网络性能时 |
| **none** | 禁用网络 | 完全隔离的容器 |
| **overlay** | 跨主机网络 | Docker Swarm 集群 |
| **macvlan** | 容器拥有独立 MAC 地址 | 需要直接接入物理网络 |
---
## 用户自定义网络推荐
### 为什么要用自定义网络
默认 bridge 网络的局限
| 问题 | 自定义网络的优势 |
|------|-----------------|
| 只能用 IP 通信 | 支持容器名 DNS 解析 |
| 所有容器在同一网络 | 更好的隔离性 |
| 需要 --link已废弃 | 原生支持服务发现 |
### 创建自定义网络
```bash
# 创建网络
$ docker network create mynet
# 查看网络详情
$ docker network inspect mynet
```
### 使用自定义网络
```bash
# 启动容器并连接到自定义网络
$ docker run -d --name web --network mynet nginx
$ docker run -d --name db --network mynet postgres
# 在 web 容器中可以直接用容器名访问 db
$ docker exec web ping db
PING db (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.083 ms
```
### 容器名 DNS 解析
自定义网络自动提供 DNS 服务
```
┌─────────────────────────────────────────────────────────┐
│ mynet 网络 │
│ │
│ ┌─────────┐ DNS ┌─────────┐ │
│ │ web │ ──── "db" → 172.18.0.3 ───► │ db │ │
│ │172.18.0.2│ │172.18.0.3│ │
│ └─────────┘ └─────────┘ │
│ │
│ web 容器可以用 "db" 作为主机名访问 db 容器 │
└─────────────────────────────────────────────────────────┘
```
---
## 容器互联
### 同一网络内的容器
同一自定义网络内的容器可以直接通信
```bash
# 创建网络
$ docker network create app-net
# 启动应用和数据库
$ docker run -d --name redis --network app-net redis
$ docker run -d --name app --network app-net myapp
# app 容器中可以用 redis:6379 连接 Redis
```
### 连接到多个网络
一个容器可以连接到多个网络
```bash
# 启动容器
$ docker run -d --name multi-net-container --network frontend nginx
# 再连接到另一个网络
$ docker network connect backend multi-net-container
# 查看容器的网络
$ docker inspect multi-net-container --format '{{json .NetworkSettings.Networks}}'
```
### --link 已废弃
```bash
# 旧方式(不推荐)
$ docker run --link db:database myapp
# 新方式(推荐)
$ docker network create mynet
$ docker run --network mynet --name db postgres
$ docker run --network mynet --name app myapp
```
---
## 端口映射
容器默认只能在 Docker 网络内访问要从外部访问容器需要端口映射
### 基本语法
```bash
# -p 宿主机端口:容器端口
$ docker run -d -p 8080:80 nginx
```
### 映射方式
| 参数 | 说明 | 示例 |
|------|------|------|
| `-p 8080:80` | 指定端口映射 | 宿主机 8080 容器 80 |
| `-p 80` | 随机宿主机端口 | 随机端口 容器 80 |
| `-P` | 自动映射所有暴露端口 | 随机端口 所有 EXPOSE 端口 |
| `-p 127.0.0.1:8080:80` | 只绑定本地 | 仅本机可访问 |
| `-p 8080:80/udp` | UDP 端口 | UDP 协议 |
### 查看端口映射
```bash
$ docker port mycontainer
80/tcp -> 0.0.0.0:8080
```
### 端口映射示意图
```
外部请求 http://宿主机IP:8080
┌─────────────┐
│ 宿主机:8080 │ ─── iptables NAT ───┐
└─────────────┘ │
┌───────────────┐
│ 容器 nginx:80 │
└───────────────┘
```
---
## 网络隔离
不同网络之间默认隔离
```bash
# 创建两个网络
$ docker network create frontend
$ docker network create backend
# 容器 A 在 frontend
$ docker run -d --name web --network frontend nginx
# 容器 B 在 backend
$ docker run -d --name db --network backend postgres
# web 无法直接访问 db不同网络
$ docker exec web ping db
ping: db: Name or service not known
```
这种隔离有助于安全前端容器无法直接访问数据库网络
---
## 常用命令
```bash
# 列出网络
$ docker network ls
# 创建网络
$ docker network create mynet
# 查看网络详情
$ docker network inspect mynet
# 连接容器到网络
$ docker network connect mynet mycontainer
# 断开网络连接
$ docker network disconnect mynet mycontainer
# 删除网络
$ docker network rm mynet
# 清理未使用的网络
$ docker network prune
```
---
## 本章小结
| 概念 | 要点 |
|------|------|
| **默认网络** | docker0 网桥172.17.0.0/16 网段 |
| **自定义网络** | 推荐使用支持容器名 DNS 解析 |
| **端口映射** | `-p 宿主机端口:容器端口` 暴露服务 |
| **网络隔离** | 不同网络默认隔离增强安全性 |
| **--link** | 已废弃使用自定义网络替代 |
## 延伸阅读
- [高级网络配置](linking.md)容器互联详解
- [配置 DNS](dns.md)自定义 DNS 设置
- [端口映射](port_bindbindbindport.md)高级端口配置
- [Compose 网络](../compose/compose_file.md)Compose 中的网络配置

View File

@@ -1,114 +0,0 @@
# 配置 DNS
## 容器的 DNS 机制
Docker 容器的 DNS 配置有两种情况
1. **默认 Bridge 网络**继承宿主机的 DNS 配置`/etc/resolv.conf`
2. **自定义网络**推荐使用 Docker 嵌入式 DNS 服务器 (Embedded DNS)支持通过**容器名**进行服务发现
---
## 嵌入式 DNS (Embedded DNS)
这是 Docker 网络最强大的功能之一在自定义网络中容器可以通过"名字"找到彼此而不需要知道对方的 IP因为 IP 可能会变
```bash
# 1. 创建自定义网络
$ docker network create mynet
# 2. 启动容器 web 并加入网络
$ docker run -d --name web --network mynet nginx
# 3. 启动容器 client 并尝试 ping web
$ docker run -it --rm --network mynet alpine ping web
PING web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms
```
**原理**
Docker 守护进程在 `127.0.0.11` 运行了一个 DNS 服务器容器内的 DNS 请求会被转发到这里如果是容器名解析为容器 IP如果是外部域名 google.com转发给上游 DNS
---
## 配置 DNS 参数
如果你需要手动配置容器的 DNS例如使用内网 DNS 服务器可以在 `docker run` 中使用以下参数
### 1. --dns
指定 DNS 服务器 IP
```bash
$ docker run -it --dns=114.114.114.114 ubuntu cat /etc/resolv.conf
nameserver 114.114.114.114
```
### 2. --dns-search
指定 DNS 搜索域例如设置为 `example.com` `ping host` 会尝试解析 `host.example.com`
```bash
$ docker run --dns-search=example.com myapp
```
### 3. --hostname (-h)
设置容器的主机名
```bash
$ docker run -h myweb nginx
```
---
## 全局 DNS 配置
如果希望所有容器都使用特定的 DNS 服务器而不是继承宿主机可以修改 `/etc/docker/daemon.json`
```json
{
"dns": [
"114.114.114.114",
"8.8.8.8"
]
}
```
修改后需要重启 Docker 服务`systemctl restart docker`
---
## 常见问题
### Q: 容器无法解析域名
**现象**`ping www.baidu.com` 失败 `ping 8.8.8.8` 成功
**解决**
1. 宿主机的 `/etc/resolv.conf` 可能有问题例如使用了本地回环地址 127.0.0.53特别是 Ubuntu 系统Docker 可能会尝试修复但有时会失败
2. 尝试手动指定 DNS`docker run --dns 8.8.8.8 ...`
3. 检查防火墙是否拦截了 UDP 53 端口
### Q: 无法通过容器名通信
**现象**`ping db` 提示 `bad address 'db'`
**原因**
- 你可能在使用**默认的 bridge 网络**默认 bridge 网络**不支持**通过容器名进行 DNS 解析这是一个历史遗留设计
- **解决**使用自定义网络 (`docker network create ...`)
---
## 本章小结
| 场景 | DNS 行为 | 备注 |
|------|----------|------|
| **默认网络** | 继承宿主机 | 不支持容器名解析 |
| **自定义网络** | Docker 嵌入式 DNS | 支持容器名解析 |
| **手动指定** | 使用 `--dns` | 覆盖默认配置 |
## 延伸阅读
- [网络模式](README.md)Docker 网络概览
- [端口映射](port_mapping.md)外部访问

View File

@@ -1,149 +0,0 @@
# 外部访问容器
## 为什么要映射端口
容器运行在自己的隔离网络环境中通常是 Bridge 模式这意味着
- **容器之间**可以通过 IP 或容器名自定义网络互通
- **宿主机访问容器**可以通过容器 IP 访问
- **外部网络访问容器** 默认无法直接访问
为了让外部如你的浏览器其他局域网机器访问容器内的服务我们需要将容器的端口**映射**到宿主机的端口
```
外部用户 (Browser)
宿主机 (localhost:8080)
┌────┴────┐ 端口映射
│ Docker │ (8080 -> 80)
│ Proxy │
└────┬────┘
容器 (Class B: 80)
```
---
## 端口映射方式
### 1. 指定映射 (-p)
使用 `-p <宿主机端口>:<容器端口>` 格式
```bash
# 将宿主机的 8080 端口映射到容器的 80 端口
$ docker run -d -p 8080:80 nginx
```
此时访问 `http://localhost:8080` 即可看到 Nginx 页面
**多种格式**
| 格式 | 含义 | 示例 |
|------|------|------|
| `ip:hostPort:containerPort` | 绑定指定 IP 的特定端口 | `-p 127.0.0.1:8080:80` (仅本机访问) |
| `ip::containerPort` | 绑定指定 IP 的随机端口 | `-p 127.0.0.1::80` |
| `hostPort:containerPort` | 绑定所有 IP (0.0.0.0) 的特定端口 | `-p 8080:80` (默认) |
| `containerPort` | 绑定所有 IP 的随机端口 | `-p 80` |
### 2. 随机映射 (-P)
使用 `-P` (大写) 参数Docker 会随机映射 Dockerfile `EXPOSE` 指令暴露的所有端口到宿主机的高端口49000-49900
```bash
$ docker run -d -P nginx
```
查看映射结果
```bash
$ docker ps
CONTAINER ID PORTS
abc123456 0.0.0.0:49153->80/tcp
```
此时 Nginx 被映射到了宿主机的 49153 端口
---
## 查看端口映射
### docker port
```bash
$ docker port mycontainer
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
```
### docker ps
```bash
$ docker ps
CONTAINER ID IMAGE PORTS NAMES
abc123456 nginx 0.0.0.0:8080->80/tcp web
```
---
## 最佳实践与安全
### 1. 限制监听 IP
默认情况下`-p 8080:80` 会监听 `0.0.0.0:8080`这意味着任何人只要能连接你的宿主机 IP就能访问该服务
如果不希望对外暴露例如数据库服务应绑定到 `127.0.0.1`
```bash
# 仅允许本机访问
$ docker run -d -p 127.0.0.1:3306:3306 mysql
```
### 2. 避免端口冲突
如果宿主机 8080 已经被占用了容器将无法启动
**解决**
- 更换宿主机端口`-p 8081:80`
- Docker 自动分配`-p 80`
### 3. UDP 映射
默认是 TCP 协议如果要映射 UDP 服务 DNS, Syslog
```bash
$ docker run -d -p 53:53/udp dns-server
```
---
## 实现原理
Docker 使用 `docker-proxy` 进程用户态 `iptables` DNAT 规则内核态来实现端口转发
当流量到达宿主机端口时iptables 规则将其目标地址修改为容器 IP 并转发
```bash
# 简化的 iptables 逻辑
iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
```
这也是为什么你在容器内部看到的访问来源 IP 通常是网关 IP 172.17.0.1而不是真实的外部 Client IP除非使用 host 网络模式
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **-p** | 指定端口映射常用 `8080:80` |
| **-P** | 随机映射所有 EXPOSE 的端口 |
| **安全性** | 默认监听所有 IP敏感服务应绑定 `127.0.0.1` |
| **查看** | 使用 `docker port` `docker ps` |
## 延伸阅读
- [EXPOSE 指令](../04_image/dockerfile/expose.md) Dockerfile 中声明端口
- [网络模式](README.md)Host 模式不需要端口映射

View File

@@ -1,6 +1,6 @@
# WORKDIR 指定工作目录
## 7.10 WORKDIR 指定工作目录
## 基本语法
### 7.10.1 基本语法
```docker
WORKDIR <工作目录路径>
@@ -10,7 +10,7 @@ WORKDIR <工作目录路径>
---
## 基本用法
### 7.10.2 基本用法
```docker
WORKDIR /app
@@ -22,19 +22,20 @@ COPY . . # 复制到 /app/
---
## 为什么需要 WORKDIR
### 7.10.3 为什么需要 WORKDIR
### 常见错误
#### 常见错误
```docker
# 错误cd 在下一个 RUN 中无效
## 错误cd 在下一个 RUN 中无效
RUN cd /app
RUN echo "hello" > world.txt # 文件在根目录
```
### 原因分析
#### 原因分析
```
```dockerfile
RUN cd /app
启动容器 cd /app仅内存变化 提交镜像层 容器销毁
@@ -45,19 +46,20 @@ RUN echo "hello" > world.txt
启动新容器工作目录在 / 创建 /world.txt
```
每个 RUN 都在新容器中执行**前一个 RUN 的内存状态包括工作目录不会保留**
每个 RUN 都在新容器中执行**前一个 RUN 的内存状态 (包括工作目录) 不会保留**
### 正确做法
#### 正确做法
```docker
# 正确使用 WORKDIR
## 正确使用 WORKDIR
WORKDIR /app
RUN echo "hello" > world.txt # 创建 /app/world.txt
```
---
## 相对路径
### 7.10.4 相对路径
WORKDIR 支持相对路径基于上一个 WORKDIR
@@ -71,7 +73,7 @@ RUN pwd # 输出 /a/b/c
---
## 使用环境变量
### 7.10.5 使用环境变量
```docker
ENV APP_HOME=/app
@@ -82,10 +84,11 @@ RUN pwd # 输出 /app
---
## 多阶段构建中的 WORKDIR
### 7.10.6 多阶段构建中的 WORKDIR
```docker
# 构建阶段
## 构建阶段
FROM node:20 AS builder
WORKDIR /build
COPY package*.json ./
@@ -93,7 +96,8 @@ RUN npm install
COPY . .
RUN npm run build
# 生产阶段
## 生产阶段
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=builder /build/dist .
@@ -101,9 +105,9 @@ COPY --from=builder /build/dist .
---
## 最佳实践
### 7.10.7 最佳实践
### 1. 尽早设置 WORKDIR
#### 1. 尽早设置 WORKDIR
```docker
FROM node:20
@@ -115,40 +119,46 @@ COPY . .
CMD ["node", "server.js"]
```
### 2. 使用绝对路径
#### 2. 使用绝对路径
```docker
# 推荐绝对路径意图明确
## 推荐绝对路径意图明确
WORKDIR /app
# 避免相对路径可能造成混淆
## 避免相对路径可能造成混淆
WORKDIR app
```
### 3. 不要用 RUN cd
#### 3. 不要用 RUN cd
```docker
# 避免
## 避免
RUN cd /app && echo "hello" > world.txt
# 推荐
## 推荐
WORKDIR /app
RUN echo "hello" > world.txt
```
### 4. 适时重置 WORKDIR
#### 4. 适时重置 WORKDIR
```docker
WORKDIR /app
# ... 应用相关操作 ...
## ... 应用相关操作 ...
WORKDIR /data
# ... 数据相关操作 ...
## ... 数据相关操作 ...
...
```
---
## 与其他指令的关系
### 7.10.8 与其他指令的关系
| 指令 | WORKDIR 的影响 |
|------|---------------|
@@ -168,7 +178,7 @@ CMD ["./start.sh"] # /app/start.sh
---
## 运行时覆盖
### 7.10.9 运行时覆盖
使用 `-w` 参数覆盖工作目录
@@ -178,19 +188,3 @@ $ docker run -w /tmp myimage pwd
```
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 设置后续指令的工作目录 |
| **语法** | `WORKDIR /path` |
| **自动创建** | 目录不存在会自动创建 |
| **持久性** | 影响后续所有指令直到下次 WORKDIR |
| **不要用** | `RUN cd /path`无效 |
## 延伸阅读
- [COPY 复制文件](copy.md)文件复制
- [RUN 执行命令](../../04_image/build.md)执行构建命令
- [最佳实践](../../15_appendix/best_practices.md)Dockerfile 编写指南

279
07_dockerfile/7.11_user.md Normal file
View File

@@ -0,0 +1,279 @@
## 7.11 USER 指定当前用户
### 7.11.1 基本语法
```docker
USER <用户名>[:<用户组>]
USER <UID>[:<GID>]
```
`USER` 指令切换后续指令 (RUNCMDENTRYPOINT) 的执行用户
---
### 7.11.2 为什么要使用 USER
> 笔者强调以非 root 用户运行容器是最重要的安全实践之一
```mermaid
flowchart LR
subgraph Root ["root 用户运行的风险:"]
direction TB
R_C["容器内 root"] -- 可能逃逸 --> R_H["宿主机 root"]
R_C -- 漏洞利用 --> R_Control["完全控制宿主机"]
end
subgraph NonRoot ["非 root 用户运行:"]
direction TB
NR_C["容器内普通用户"] -- 逃逸后 --> NR_H["宿主机普通用户"]
NR_C -- 权限受限,危害降低 --> NR_Safe["无法控制系统"]
end
```
---
### 7.11.3 基本用法
#### 创建并切换用户
```docker
FROM node:20-alpine
## 1. 创建用户和组
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
## 2. 设置目录权限
WORKDIR /app
COPY --chown=appuser:appgroup . .
## 3. 切换用户
USER appuser
## 4. 后续命令以 appuser 身份运行
CMD ["node", "server.js"]
```
#### 使用 UID/GID
```docker
## 也可以使用数字
USER 1001:1001
```
---
### 7.11.4 用户必须已存在
`USER` 指令只能切换到 **已存在** 的用户
```docker
## ❌ 错误:用户不存在
USER nonexistent
## Error: unable to find user nonexistent
## ✅ 正确:先创建用户
RUN useradd -r -s /bin/false appuser
USER appuser
```
#### 创建用户的方式
**Debian/Ubuntu**
```docker
RUN groupadd -r appgroup && \
useradd -r -g appgroup appuser
```
**Alpine**
```docker
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S -G appgroup appuser
```
| 选项 | 说明 |
|------|------|
| `-r` (useradd) / `-S` (adduser) | 创建系统用户 |
| `-g` | 指定主组 |
| `-G` | 指定附加组 |
| `-u` | 指定 UID |
| `-s /bin/false` | 禁用登录 shell |
---
### 7.11.5 运行时切换用户
#### 使用 gosu (推荐)
ENTRYPOINT 脚本中切换用户时不要使用 `su` `sudo`应使用 [gosu](https://github.com/tianon/gosu)
```docker
FROM debian:bookworm
## 创建用户
RUN groupadd -r redis && useradd -r -g redis redis
## 安装 gosu
RUN apt-get update && apt-get install -y gosu && rm -rf /var/lib/apt/lists/*
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["redis-server"]
```
**docker-entrypoint.sh**
```bash
#!/bin/bash
set -e
## 以 root 执行初始化
chown -R redis:redis /data
## 用 gosu 切换到 redis 用户运行服务
exec gosu redis "$@"
```
#### 为什么不用 su/sudo
| 问题 | su/sudo | gosu |
|------|---------|------|
| TTY 要求 | 需要 | 不需要 |
| 信号传递 | 不正确 | 正确 |
| 子进程 | | exec 替换 |
| 容器中使用 | | |
---
### 7.11.6 运行时覆盖用户
使用 `-u` `--user` 参数
```bash
## 以指定用户运行
$ docker run -u 1001:1001 myimage
## 以 root 运行(调试时)
$ docker run -u root myimage
```
---
### 7.11.7 文件权限处理
切换用户后确保应用有权访问文件
```docker
FROM node:20-alpine
## 创建用户
RUN adduser -D -u 1001 appuser
WORKDIR /app
## 方式1使用 --chown
COPY --chown=appuser:appuser . .
## 方式2手动 chown减少层数
## COPY . .
## RUN chown -R appuser:appuser /app
USER appuser
CMD ["node", "server.js"]
```
---
### 7.11.8 最佳实践
#### 1. 始终使用非 root 用户
```docker
## ✅ 推荐
RUN adduser -D appuser
USER appuser
CMD ["myapp"]
## ❌ 避免
CMD ["myapp"] # 以 root 运行
```
#### 2. 使用固定 UID/GID
便于在宿主机和容器间共享文件
```docker
## 使用常见的非 root UID
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S -G appgroup appuser
USER 1000:1000
```
#### 3. 多阶段构建中的 USER
```docker
## 构建阶段可以用 root
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
## 生产阶段用非 root
FROM node:20-alpine
RUN adduser -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/dist .
USER appuser
CMD ["node", "server.js"]
```
---
### 7.11.9 常见问题
#### Q权限被拒绝
```bash
permission denied: '/app/data.log'
```
**解决**确保目录权限正确
```docker
RUN mkdir -p /app/data && chown appuser:appuser /app/data
```
#### Q无法绑定低于 1024 的端口
root 用户无法绑定 80443 等端口
**解决**
1. 使用高端口 ( 8080)
2. 在运行时映射端口`docker run -p 80:8080`
---

View File

@@ -1,6 +1,6 @@
# HEALTHCHECK 健康检查
## 7.12 HEALTHCHECK 健康检查
## 基本语法
### 7.12.1 基本语法
```docker
HEALTHCHECK [选项] CMD <命令>
@@ -11,19 +11,18 @@ HEALTHCHECK NONE
---
## 为什么需要 HEALTHCHECK
### 7.12.2 为什么需要 HEALTHCHECK
在没有 HEALTHCHECK 之前Docker 只能通过**进程退出码**来判断容器状态
在没有 HEALTHCHECK 之前Docker 只能通过 **进程退出码** 来判断容器状态**问题场景**
**问题场景**
- Web 服务死锁无法响应请求但进程仍在运行
- 数据库正在启动中尚未准备好接受连接
- 应用陷入死循环CPU 爆满但进程存活
**引入 HEALTHCHECK **
Docker 定期执行指定的检查命令根据返回值判断容器是否"健康"
Docker 定期执行指定的检查命令根据返回值判断容器是否 健康
```
```bash
容器状态转换
Starting 成功> Healthy 失败N次> Unhealthy
@@ -32,9 +31,9 @@ Starting ──成功──> Healthy ──失败N次──> Unhealthy
---
## 基本用法
### 7.12.3 基本用法
### Web 服务检查
#### Web 服务检查
```docker
FROM nginx
@@ -44,24 +43,24 @@ HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -fs http://localhost/ || exit 1
```
### 命令返回值
#### 命令返回值
- `0`: 成功 (healthy)
- `1`: 失败 (unhealthy)
- `2`: 保留值 (不使用)
- `0`成功 (healthy)
- `1`失败 (unhealthy)
- `2`保留值 (不使用)
### 常用选项
#### 常用选项
| 选项 | 说明 | 默认值 |
|------|------|--------|
| `--interval` | 两次检查的间隔 | 30s |
| `--timeout` | 检查命令的超时时间 | 30s |
| `--start-period` | 启动缓冲期期间失败不计入次数 | 0s |
| `--start-period` | 启动缓冲期 (期间失败不计入次数)| 0s |
| `--retries` | 连续失败多少次标记为 unhealthy | 3 |
---
## 屏蔽健康检查
### 7.12.4 屏蔽健康检查
如果基础镜像定义了 HEALTHCHECK但你不想使用它
@@ -72,31 +71,35 @@ HEALTHCHECK NONE
---
## 常见检查脚本
### 7.12.5 常见检查脚本
### HTTP 服务
#### HTTP 服务
使用 `curl` `wget`
```docker
# 使用 curl
## 使用 curl
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
# 使用 wget (Alpine 默认包含)
## 使用 wgetAlpine 默认包含
HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1
```
### 数据库
#### 数据库
```docker
# MySQL
## MySQL
HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
# Redis
## Redis
HEALTHCHECK CMD redis-cli ping || exit 1
```
### 自定义脚本
#### 自定义脚本
```docker
COPY healthcheck.sh /usr/local/bin/
@@ -105,9 +108,9 @@ HEALTHCHECK CMD ["healthcheck.sh"]
---
## Compose 中使用
### 7.12.6 Compose 中使用
可以在 `docker-compose.yml` 中覆盖或定义健康检查
可以在 `compose.yaml` ( `docker-compose.yml`) 中覆盖或定义健康检查
```yaml
services:
@@ -137,16 +140,18 @@ services:
---
## 查看健康状态
### 7.12.7 查看健康状态
```bash
# 查看容器状态包含健康信息
## 查看容器状态包含健康信息
$ docker ps
CONTAINER ID STATUS
abc123 Up 1 minute (healthy)
def456 Up 2 minutes (unhealthy)
# 查看详细健康日志
## 查看详细健康日志
$ docker inspect --format '{{json .State.Health}}' mycontainer | jq
{
"Status": "healthy",
@@ -164,43 +169,28 @@ $ docker inspect --format '{{json .State.Health}}' mycontainer | jq
---
## 最佳实践
### 7.12.8 最佳实践
### 1. 避免副作用
#### 1. 避免副作用
健康检查会被频繁执行不要在检查脚本中进行写操作或消耗大量资源的操作
### 2. 使用轻量级工具
#### 2. 使用轻量级工具
优先使用镜像中已有的工具 `wget`避免为了健康检查安装庞大的依赖 `curl`
优先使用镜像中已有的工具 ( `wget`)避免为了健康检查安装庞大的依赖 ( `curl`)
### 3. 设置合理的 Start Period
#### 3. 设置合理的 Start Period
应用启动可能需要时间 Java 应用设置 `--start-period` 可以防止在启动阶段因检查失败而误判
应用启动可能需要时间 ( Java 应用)设置 `--start-period` 可以防止在启动阶段因检查失败而误判
```docker
# 给应用 1 分钟启动时间
## 给应用 1 分钟启动时间
HEALTHCHECK --start-period=60s CMD curl -f http://localhost/ || exit 1
```
### 4. 只检查核心依赖
#### 4. 只检查核心依赖
健康检查应主要关注**当前服务**是否可用而不是检查其下游依赖数据库等下游依赖的检查应由应用逻辑处理
健康检查应主要关注 **当前服务** 是否可用而不是检查其下游依赖 (数据库等)下游依赖的检查应由应用逻辑处理
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 检测容器应用是否真实可用 |
| **命令** | `HEALTHCHECK [选项] CMD command` |
| **状态** | starting, healthy, unhealthy |
| **Compose** | 支持 `condition: service_healthy` 依赖 |
| **注意** | 避免副作用节省资源 |
## 延伸阅读
- [CMD 容器启动命令](cmd.md)启动主进程
- [Compose 模板文件](../../compose/compose_file.md)Compose 中的健康检查
- [Docker 调试](../../15_appendix/debug.md)容器排障

View File

@@ -0,0 +1,145 @@
## 7.13 ONBUILD 为他人作嫁衣裳
### 7.13.1 基本语法
```docker
ONBUILD <其它指令>
```
`ONBUILD` 是一个特殊的指令它后面跟的是其它指令 ( `RUN``COPY` )这些指令 **在当前镜像构建时不会执行**只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行
---
### 7.13.2 为什么需要 ONBUILD
`ONBUILD` 主要用于制作 **语言栈基础镜像** **框架基础镜像**
#### 场景维护 Node.js 项目
假设你有多个 Node.js 项目它们的构建流程都一样
1. 创建目录
2. 复制 `package.json`
3. 执行 `npm install`
4. 复制源码
5. 启动应用
如果不使用 `ONBUILD`每个项目的 Dockerfile 都要重复这些步骤且通过 `COPY` 复制文件时基础镜像无法预知子项目的文件名
#### 使用 ONBUILD 的解决方案
**基础镜像 (my-node-base)**
```docker
FROM node:20-alpine
WORKDIR /app
## 这些指令将在子镜像构建时执行
ONBUILD COPY package*.json ./
ONBUILD RUN npm install
ONBUILD COPY . .
CMD ["npm", "start"]
```
**子项目 Dockerfile**
```docker
FROM my-node-base
## 只需要一行!
## 构建时会自动执行 COPY 和 RUN
...
```
---
### 7.13.3 执行机制
```bash
基础镜像构建:
Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触发器)
(指令未执行)
子镜像构建:
FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行触发器指令 ──> 继续执行子 Dockerfile
```
---
### 7.13.4 常见使用场景
#### 1. 自动处理依赖安装
```docker
## Python 基础镜像
ONBUILD COPY requirements.txt ./
ONBUILD RUN pip install -r requirements.txt
```
#### 2. 自动编译代码
```docker
## Go 基础镜像
ONBUILD COPY . .
ONBUILD RUN go build -o app main.go
```
#### 3. 处理静态资源
```docker
## Nginx 静态网站基础镜像
ONBUILD COPY dist/ /usr/share/nginx/html/
```
---
### 7.13.5 注意事项
#### 1. 继承性限制
`ONBUILD` 指令 **只会继承一次**
- 镜像 A ( ONBUILD)
- 镜像 B (FROM A) -> 触发 ONBUILD
- 镜像 C (FROM B) -> **不会** 再次触发 ONBUILD
#### 2. 构建上下文
子镜像构建时`ONBUILD COPY . .` 中的 `.` 指的是 **子项目** 的构建上下文而不是基础镜像的上下文
#### 3. 不允许级联
`ONBUILD ONBUILD` 是非法的你不能写 `ONBUILD ONBUILD COPY ...`
#### 4. 可能会导致构建失败
由于 `ONBUILD` 实际上是在子镜像中执行指令如果子项目的上下文不满足要求 (例如缺少 `package.json`)会导致子镜像构建失败且错误信息可能比较隐晦
---
### 7.13.6 最佳实践
#### 1. 命名规范
建议在镜像标签中添加 `-onbuild` 后缀明确告知使用者该镜像包含触发器
```bash
node:20-onbuild
python:3.12-onbuild
```
#### 2. 避免执行耗时操作
尽量不要在 `ONBUILD` 中执行过于耗时或不确定的操作 (如更新系统软件)这会让子镜像构建变得缓慢且不可控
#### 3. 清理工作
如果 `ONBUILD` 指令产生了临时文件最好在同一个指令链中清理或者提供机制让子镜像清理
---

View File

@@ -1,6 +1,6 @@
# LABEL 为镜像添加元数据
## 7.14 LABEL 为镜像添加元数据
## 基本语法
### 7.14.1 基本语法
```docker
LABEL <key>=<value> <key>=<value> ...
@@ -10,25 +10,25 @@ LABEL <key>=<value> <key>=<value> ...
---
## 为什么需要 LABEL
### 7.14.2 为什么需要 LABEL
1. **版本管理**记录版本号构建时间Git Commit ID
2. **联系信息**维护者邮箱文档地址支持渠道
3. **自动化工具** CI/CD 工具可以读取标签触发操作
3. **自动化工具**CI/CD 工具可以读取标签触发操作
4. **许可证信息**声明开源协议
---
## 基本用法
### 7.14.3 基本用法
### 定义单个标签
#### 定义单个标签
```docker
LABEL version="1.0"
LABEL description="这是一个 Web 应用服务器"
```
### 定义多个标签推荐
#### 定义多个标签 (推荐)
```docker
LABEL maintainer="user@example.com" \
@@ -41,7 +41,7 @@ LABEL maintainer="user@example.com" \
---
## 常用标签规范 (OCI Annotations)
### 7.14.4 常用标签规范
为了标准和互操作性推荐使用 [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys) 定义的标准标签:
@@ -57,7 +57,7 @@ LABEL maintainer="user@example.com" \
| `org.opencontainers.image.title` | 镜像标题 | `My App` |
| `org.opencontainers.image.description` | 描述 | `Production ready web server` |
### 示例
#### 示例
```docker
LABEL org.opencontainers.image.authors="yeasy" \
@@ -68,29 +68,32 @@ LABEL org.opencontainers.image.authors="yeasy" \
---
## MAINTAINER 指令已废弃
### 7.14.5 MAINTAINER 指令 (已废弃)
旧版本的 Dockerfile 中常看到 `MAINTAINER` 指令
```docker
# 已弃用
## 已弃用
MAINTAINER user@example.com
```
现在推荐使用 `LABEL`
```docker
# 推荐
## 推荐
LABEL maintainer="user@example.com"
#
##
LABEL org.opencontainers.image.authors="user@example.com"
```
---
## 动态标签
### 7.14.6 动态标签
配合 `ARG` 使用可以在构建时动态注入标签
配合 `ARG` 使用可以在构建时动态注入标签
```docker
ARG BUILD_DATE
@@ -111,9 +114,9 @@ $ docker build \
---
## 查看标签
### 7.14.7 查看标签
### docker inspect
#### docker inspect
查看镜像的标签信息
@@ -124,31 +127,18 @@ $ docker inspect nginx --format '{{json .Config.Labels}}' | jq
}
```
### 过滤器
#### 过滤器
可以使用标签过滤镜像
```bash
# 列出作者是 yeasy 的所有镜像
## 列出作者是 yeasy 的所有镜像
$ docker images --filter "label=org.opencontainers.image.authors=yeasy"
# 删除所有带有特定标签的镜像
## 删除所有带有特定标签的镜像
$ docker rmi $(docker images -q --filter "label=stage=builder")
```
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 添加 key-value 元数据 |
| **语法** | `LABEL k=v k=v ...` |
| **规范** | 推荐使用 OCI 标准标签 |
| **弃用** | 不要再使用 `MAINTAINER` |
| **查看** | `docker inspect` |
## 延伸阅读
- [OCI 标签规范](https://github.com/opencontainers/image-spec/blob/main/annotations.md)
- [Dockerfile 最佳实践](../../15_appendix/best_practices.md)

View File

@@ -1,96 +1,108 @@
# SHELL 指令
## 7.15 SHELL 指令
## 基本语法
### 7.15.1 基本语法
```docker
SHELL ["executable", "parameters"]
```
`SHELL` 指令允许覆盖 Docker 默认的 shell
- **Linux 默认**`["/bin/sh", "-c"]`
- **Windows 默认**`["cmd", "/S", "/C"]`
该指令会影响后续的 `RUN`, `CMD`, `ENTRYPOINT` 指令当它们使用 shell 格式时
该指令会影响后续的 `RUN``CMD``ENTRYPOINT` 指令 (当它们使用 shell 格式时)
---
## 为什么要用 SHELL 指令
### 7.15.2 为什么要用 SHELL 指令
### 1. 使用 bash 特性
#### 1. 使用 bash 特性
默认的 `/bin/sh`通常是 dash alpine ash功能有限如果你需要使用 bash 的特有功能如数组`{}` 扩展`pipefail` 可以切换 shell
默认的 `/bin/sh` (通常是 dash alpine ash) 功能有限如果你需要使用 bash 的特有功能 (如数组`{}` 扩展`pipefail` )可以切换 shell
```docker
FROM ubuntu:24.04
# 切换到 bash
## 切换到 bash
SHELL ["/bin/bash", "-c"]
# 现在可以使用 bash 特性了
## 现在可以使用 bash 特性了
RUN echo {a..z}
```
### 2. 增强错误处理 (pipefail)
#### 2. 增强错误处理
默认情况下管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功整个指令就视为成功这可能掩盖构建错误
```docker
# 这里的 wget 失败了但构建继续因为 tar 成功了
## 这里的 wget 失败了但构建继续因为 tar 成功了
RUN wget -O - https://invalid-url | tar xz
```
使用 `SHELL` 启用 `pipefail`
```docker
# 启用 pipefail
## 启用 pipefail
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# 如果 wget 失败整个 RUN 就会失败
## 如果 wget 失败整个 RUN 就会失败
RUN wget -O - https://invalid-url | tar xz
```
### 3. Windows 环境
#### 3. Windows 环境
Windows 容器中经常需要在 `cmd` `powershell` 之间切换
```docker
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# 默认是 cmd
## 默认是 cmd
RUN echo Default shell is cmd
# 切换到 powershell
## 切换到 powershell
SHELL ["powershell", "-command"]
RUN Write-Host "Hello from PowerShell"
# 切回 cmd
## 切回 cmd
SHELL ["cmd", "/S", "/C"]
```
---
## 作用范围
### 7.15.3 作用范围
`SHELL` 指令可以出现多次每次只影响其后的指令
```docker
FROM ubuntu:24.04
# 使用默认 sh
## 使用默认 sh
RUN echo "Using sh"
SHELL ["/bin/bash", "-c"]
# 使用 bash
## 使用 bash
RUN echo "Using bash"
SHELL ["/bin/sh", "-c"]
# 回到 sh
## 回到 sh
RUN echo "Using sh again"
```
---
## 对其他指令的影响
### 7.15.4 对其他指令的影响
`SHELL` 影响的是所有使用 **shell 格式** 的指令
@@ -105,9 +117,9 @@ RUN echo "Using sh again"
---
## 最佳实践
### 7.15.5 最佳实践
### 1. 推荐开启 pipefail
#### 1. 推荐开启 pipefail
对于使用 bash 的镜像强烈建议开启 `pipefail`以确保构建过程中的错误能被及时捕获
@@ -115,27 +127,12 @@ RUN echo "Using sh again"
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
```
### 2. 明确意图
#### 2. 明确意图
如果由于脚本需求必须更改 shell最好在 Dockerfile 中显式声明而不是依赖默认行为
### 3. 尽量保持一致
#### 3. 尽量保持一致
避免在 Dockerfile 中频繁切换 SHELL这会使构建过程难以理解和调试尽量在头部定义一次即可
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 更改 RUN/CMD/ENTRYPOINT 的默认 shell |
| **Linux 默认** | `["/bin/sh", "-c"]` |
| **Windows 默认** | `["cmd", "/S", "/C"]` |
| **推荐用法** | `SHELL ["/bin/bash", "-o", "pipefail", "-c"]` |
| **影响范围** | 后续所有使用 shell 格式的指令 |
## 延伸阅读
- [RUN 指令](../../04_image/build.md)执行命令
- [Dockerfile 最佳实践](../../15_appendix/best_practices.md)错误处理与调试

View File

@@ -0,0 +1,36 @@
## 7.16 参考文档
### 官方文档
* `Dockerfile` 官方参考手册https://docs.docker.com/engine/reference/builder/
* `Dockerfile` 最佳实践指南https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
* `Docker` 官方镜像 `Dockerfile` https://github.com/docker-library/docs
### 常用指令总结
Dockerfile 中的常用指令包括
- **FROM**: 指定基础镜像必须是第一条指令
- **RUN**: 在镜像中执行命令用于安装软件包等
- **WORKDIR**: 设置工作目录
- **COPY/ADD**: 复制文件到镜像中
- **EXPOSE**: 声明容器监听的端口
- **ENV**: 设置环境变量
- **ENTRYPOINT**: 容器启动时的入口点
- **CMD**: 容器默认执行的命令
### 最佳实践建议
1. 使用具体的基础镜像版本标签而非 latest
2. 最小化镜像层数合并 RUN 指令
3. 使用 .dockerignore 文件排除不必要的文件
4. 安装必要的软件包后清理缓存
5. 使用多阶段构建减小最终镜像体积
6. 避免以 root 身份运行容器应用
### 相关资源
- Docker 官方镜像库https://hub.docker.com/
- Docker 镜像构建最佳实践https://docs.docker.com/build/building/best-practices/

View File

@@ -1,12 +1,10 @@
# 多阶段构建
## 之前的做法
## 7.17 多阶段构建
Docker 17.05 版本之前我们构建 Docker 镜像时通常会采用两种方式
### 全部放入一个 Dockerfile
### 7.17.1 全部放入一个 Dockerfile
一种方式是将所有的构建过程包含在一个 `Dockerfile` 包括项目及其依赖库的编译测试打包等流程这里可能会带来的一些问题
一种方式是将所有的构建过程包含在一个 `Dockerfile` 包括项目及其依赖库的编译测试打包等流程这里可能会带来的一些问题
* 镜像层次多镜像体积较大部署时间变长
@@ -51,7 +49,7 @@ CMD ["./app"]
$ docker build -t go/helloworld:1 -f Dockerfile.one .
```
### 分散到多个 Dockerfile
### 7.17.2 分散到多个 Dockerfile
另一种方式就是我们事先在一个 `Dockerfile` 将项目及其依赖库编译测试打包好后再将其拷贝到运行环境中这种方式需要我们编写两个 `Dockerfile` 和一些编译脚本才能将其两个阶段自动整合起来这种方式虽然可以很好地规避第一种方式存在的风险但明显部署过程较复杂
@@ -120,7 +118,7 @@ go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
```
## 使用多阶段构建
### 7.17.3 使用多阶段构建
为解决以上问题Docker v17.05 开始支持多阶段构建 (`multistage builds`)使用多阶段构建我们就可以很容易解决前面提到的问题并且只需要编写一个 `Dockerfile`
@@ -169,7 +167,7 @@ go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
很明显使用多阶段构建的镜像体积小同时也完美解决了上边提到的问题
### 只构建某一阶段的镜像
### 7.17.4 只构建某一阶段的镜像
我们可以使用 `as` 来为某一阶段命名例如
@@ -183,7 +181,7 @@ FROM golang:alpine as builder
$ docker build --target builder -t username/imagename:tag .
```
### 构建时从其他镜像复制文件
### 7.17.5 构建时从其他镜像复制文件
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件我们也可以复制任意镜像中的文件

View File

@@ -1,8 +1,8 @@
# 实战多阶段构建 Laravel 镜像
## 7.18 实战多阶段构建 Laravel 镜像
> 本节适用于 PHP 开发者阅读`Laravel` 基于 8.x 版本各个版本的文件结构可能会有差异请根据实际自行修改
## 准备
### 7.18.1 准备
新建一个 `Laravel` 项目或在已有的 `Laravel` 项目根目录下新建 `Dockerfile` `.dockerignore` `laravel.conf` 文件
@@ -25,7 +25,9 @@ yarn-error.log
bootstrap/cache/*
storage/
# 自行添加其他需要排除的文件例如 .env.* 文件
## 自行添加其他需要排除的文件例如 .env.* 文件
...
```
`laravel.conf` 文件中写入 nginx 配置
@@ -45,13 +47,16 @@ server {
include fastcgi.conf;
# fastcgi_connect_timeout 300;
# fastcgi_send_timeout 300;
# fastcgi_read_timeout 300;
}
}
```
## 前端构建
### 7.18.2 前端构建
第一阶段进行前端构建
@@ -72,7 +77,7 @@ RUN set -x ; cd /app \
&& npm run production
```
## 安装 Composer 依赖
### 7.18.3 安装 Composer 依赖
第二阶段安装 Composer 依赖
@@ -92,7 +97,7 @@ RUN set -x ; cd /app \
--prefer-dist
```
## 整合以上阶段所生成的文件
### 7.18.4 整合以上阶段所生成的文件
第三阶段对以上阶段生成的文件进行整合
@@ -118,7 +123,7 @@ RUN set -x ; cd ${LARAVEL_PATH} \
&& php artisan package:discover
```
## 最后一个阶段构建 NGINX 镜像
### 7.18.5 最后一个阶段构建 NGINX 镜像
```docker
FROM nginx:alpine as nginx
@@ -129,7 +134,7 @@ COPY laravel.conf /etc/nginx/conf.d/
COPY --from=laravel ${LARAVEL_PATH}/public ${LARAVEL_PATH}/public
```
## 构建 Laravel Nginx 镜像
### 7.18.6 构建 Laravel Nginx 镜像
使用 `docker build` 命令构建镜像
@@ -139,7 +144,7 @@ $ docker build -t my/laravel --target=laravel .
$ docker build -t my/nginx --target=nginx .
```
## 启动容器并测试
### 7.18.7 启动容器并测试
新建 Docker 网络
@@ -147,7 +152,7 @@ $ docker build -t my/nginx --target=nginx .
$ docker network create laravel
```
启动 laravel 容器 `--name=laravel` 参数设定的名字必须与 `nginx` 配置文件中的 `fastcgi_pass laravel:9000;` 一致
启动 laravel 容器`--name=laravel` 参数设定的名字必须与 `nginx` 配置文件中的 `fastcgi_pass laravel:9000;` 一致
```bash
$ docker run -dit --rm --name=laravel --network=laravel my/laravel
@@ -163,13 +168,13 @@ $ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
> 也许 Laravel 项目依赖其他外部服务例如 redisMySQL请自行启动这些服务之后再进行测试本小节不再赘述
## 生产环境优化
### 7.18.8 生产环境优化
本小节内容为了方便测试将配置文件直接放到了镜像中实际在使用时 **建议** 将配置文件作为 `config` `secret` 挂载到容器中请读者自行学习 `Kubernetes` 的相关内容
由于篇幅所限本小节只是简单列出更多内容可以参考 https://github.com/khs1994-docker/laravel-demo 项目。
## 附录
### 7.18.9 附录
完整的 `Dockerfile` 文件如下

Some files were not shown because too many files have changed in this diff Show More